From f12d6a4000cfce521392200635d1cadc63dba934 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Fri, 14 Apr 2023 17:59:03 +0300 Subject: SRMM code unification --- src/core/stdmsg/src/chat_window.cpp | 104 ----- src/core/stdmsg/src/msgdialog.cpp | 2 +- src/core/stdmsg/src/msglog.cpp | 857 ++++++++++++++++++++---------------- src/core/stdmsg/src/msgs.h | 17 - src/mir_app/src/db_events.cpp | 10 +- src/mir_app/src/mir_app.def | 2 + src/mir_app/src/mir_app64.def | 2 + src/mir_app/src/srmm_log_rtf.cpp | 25 ++ 8 files changed, 518 insertions(+), 501 deletions(-) (limited to 'src') diff --git a/src/core/stdmsg/src/chat_window.cpp b/src/core/stdmsg/src/chat_window.cpp index a8806f0cbc..c78e3d1d0e 100644 --- a/src/core/stdmsg/src/chat_window.cpp +++ b/src/core/stdmsg/src/chat_window.cpp @@ -107,110 +107,6 @@ void CMsgDialog::UpdateStatusBar() ///////////////////////////////////////////////////////////////////////////////////////// -void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) -{ - auto *si = m_pDlg.m_si; - if (lin == nullptr || si == nullptr) - return; - - if (!bRedraw && si->iType == GCW_CHATROOM && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0) - return; - - LOGSTREAMDATA streamData; - memset(&streamData, 0, sizeof(streamData)); - streamData.hwnd = m_rtf.GetHwnd(); - streamData.si = si; - streamData.lin = lin; - streamData.bStripFormat = FALSE; - - bool bFlag = false; - - EDITSTREAM stream = {}; - stream.pfnCallback = Srmm_LogStreamCallback; - stream.dwCookie = (DWORD_PTR)& streamData; - - SCROLLINFO scroll; - scroll.cbSize = sizeof(SCROLLINFO); - scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; - GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll); - - POINT point = {}; - m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point); - - // do not scroll to bottom if there is a selection - CHARRANGE oldsel, sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel); - if (oldsel.cpMax != oldsel.cpMin) - m_rtf.SetDraw(false); - - //set the insertion point at the bottom - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - - // fix for the indent... must be a M$ bug - if (sel.cpMax == 0) - bRedraw = TRUE; - - // should the event(s) be appended to the current log - WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF; - - //get the number of pixels per logical inch - if (bRedraw) { - HDC hdc = GetDC(nullptr); - g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); - g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); - ReleaseDC(nullptr, hdc); - m_rtf.SetDraw(false); - bFlag = true; - } - - // stream in the event(s) - streamData.lin = lin; - streamData.bRedraw = bRedraw; - m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream); - - // do smileys - if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) { - CHARRANGE newsel; - newsel.cpMax = -1; - newsel.cpMin = sel.cpMin; - if (newsel.cpMin < 0) - newsel.cpMin = 0; - - SMADD_RICHEDIT3 sm = {}; - sm.cbSize = sizeof(sm); - sm.hwndRichEditControl = m_rtf.GetHwnd(); - sm.Protocolname = si->pszModule; - sm.rangeToReplace = bRedraw ? nullptr : &newsel; - sm.disableRedraw = TRUE; - sm.hContact = si->hContact; - CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm); - } - - // scroll log to bottom if the log was previously scrolled to bottom, else restore old position - if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50) - ScrollToBottom(); - else - m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point); - - // do we need to restore the selection - if (oldsel.cpMax != oldsel.cpMin) { - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } - - // need to invalidate the window - if (bFlag) { - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - m_rtf.SetDraw(true); - InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { int iFlags; diff --git a/src/core/stdmsg/src/msgdialog.cpp b/src/core/stdmsg/src/msgdialog.cpp index 08f0036782..cfeca501ef 100644 --- a/src/core/stdmsg/src/msgdialog.cpp +++ b/src/core/stdmsg/src/msgdialog.cpp @@ -922,7 +922,7 @@ LRESULT CMsgDialog::WndProc_Message(UINT msg, WPARAM wParam, LPARAM lParam) } if (wParam == VK_NEXT || wParam == VK_PRIOR) { - ((CLogWindow *)m_pLog)->WndProc(msg, wParam, lParam); + ((CRtfLogWindow *)m_pLog)->WndProc(msg, wParam, lParam); return TRUE; } } diff --git a/src/core/stdmsg/src/msglog.cpp b/src/core/stdmsg/src/msglog.cpp index 318e246ae3..d9c7c5f69b 100644 --- a/src/core/stdmsg/src/msglog.cpp +++ b/src/core/stdmsg/src/msglog.cpp @@ -42,7 +42,7 @@ struct LogStreamData CMStringA buf; int eventsToInsert; bool isEmpty; - CMsgDialog *dlgDat; + class CLogWindow *pLog; }; static int logPixelSY; @@ -51,6 +51,8 @@ static char szSep2[40], szSep2_RTL[50]; static const wchar_t *bbcodes[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[/b]", L"[/i]", L"[/u]", L"[/s]" }; static const char *bbcodefmt[] = { "\\b ", "\\i ", "\\ul ", "\\strike ", "\\b0 ", "\\i0 ", "\\ul0 ", "\\strike0 " }; +static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb); + static void AppendPlainUnicode(CMStringA &buf, const wchar_t *str) { for (; *str; str++) { @@ -198,214 +200,6 @@ bool DbEventIsShown(const DBEVENTINFO *dbei) return dbei->eventType == EVENTTYPE_MESSAGE || dbei->eventType == EVENTTYPE_FILE || DbEventIsForMsgWindow(dbei); } -static bool CreateRTFFromDbEvent(LogStreamData *dat) -{ - DB::EventInfo dbei(dat->hDbEvent); - if (!dbei) - return false; - - if (!DbEventIsShown(&dbei)) - return false; - - if (!(dbei.flags & DBEF_SENT) && (dbei.eventType == EVENTTYPE_MESSAGE || DbEventIsForMsgWindow(&dbei))) { - db_event_markRead(dat->hContact, dat->hDbEvent); - g_clistApi.pfnRemoveEvent(dat->hContact, dat->hDbEvent); - } - else if (dbei.eventType == EVENTTYPE_JABBER_CHATSTATES || dbei.eventType == EVENTTYPE_JABBER_PRESENCE) { - db_event_markRead(dat->hContact, dat->hDbEvent); - } - - CMStringA &buf = dat->buf; - bool bIsRtl = dat->dlgDat->m_bIsAutoRTL; - if (!bIsRtl && !dat->isEmpty) - buf.Append("\\par"); - - if (dbei.flags & DBEF_RTL) { - buf.Append("\\rtlpar"); - bIsRtl = true; - } - else buf.Append("\\ltrpar"); - - dat->isEmpty = false; - - if (bIsRtl) { - if (dbei.flags & DBEF_RTL) - buf.Append("\\ltrch\\rtlch"); - else - buf.Append("\\rtlch\\ltrch"); - } - - if (g_plugin.bShowIcons) { - int i = ((dbei.eventType == EVENTTYPE_MESSAGE) ? ((dbei.flags & DBEF_SENT) ? LOGICON_MSG_OUT : LOGICON_MSG_IN): LOGICON_MSG_NOTICE); - - buf.Append("\\f0\\fs14"); - buf.Append(pLogIconBmpBits[i]); - } - - int showColon = 0; - if (g_plugin.bShowTime) { - const wchar_t* szFormat; - wchar_t str[64]; - - if (g_plugin.bShowSecs) - szFormat = g_plugin.bShowDate ? L"d s" : L"s"; - else - szFormat = g_plugin.bShowDate ? L"d t" : L"t"; - - TimeZone_PrintTimeStamp(nullptr, dbei.timestamp, szFormat, str, _countof(str), 0); - - SetToStyle((dbei.flags & DBEF_SENT) ? MSGFONTID_MYTIME : MSGFONTID_YOURTIME, buf); - AppendToBufferWithRTF(buf, str); - showColon = 1; - } - - if (g_plugin.bShowNames && dbei.eventType != EVENTTYPE_JABBER_CHATSTATES && dbei.eventType != EVENTTYPE_JABBER_PRESENCE) { - wchar_t *szName; - - if (dbei.flags & DBEF_SENT) { - if (wchar_t *p = Contact::GetInfo(CNF_DISPLAY, 0, dbei.szModule)) - szName = NEWWSTR_ALLOCA(p); - else - szName = TranslateT("Me"); - } - else szName = Clist_GetContactDisplayName(dat->hContact); - - SetToStyle((dbei.flags & DBEF_SENT) ? MSGFONTID_MYNAME : MSGFONTID_YOURNAME, buf); - AppendToBufferWithRTF(buf, szName); - showColon = 1; - } - - if (showColon) - SetToStyle((dbei.flags & DBEF_SENT) ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON, buf); - - wchar_t *msg, *szName; - switch (dbei.eventType) { - case EVENTTYPE_JABBER_CHATSTATES: - case EVENTTYPE_JABBER_PRESENCE: - if (dbei.flags & DBEF_SENT) { - if (wchar_t *p = Contact::GetInfo(CNF_DISPLAY, 0, dbei.szModule)) { - szName = NEWWSTR_ALLOCA(p); - mir_free(p); - } - else szName = L""; - } - else szName = Clist_GetContactDisplayName(dat->hContact); - - SetToStyle(MSGFONTID_NOTICE, buf); - AppendToBufferWithRTF(buf, szName); - AppendToBufferWithRTF(buf, L" "); - - msg = DbEvent_GetTextW(&dbei, CP_ACP); - if (msg) { - AppendToBufferWithRTF(buf, msg); - mir_free(msg); - } - break; - - case EVENTTYPE_FILE: - SetToStyle(MSGFONTID_NOTICE, buf); - { - DB::FILE_BLOB blob(dbei); - if (blob.isOffline()) { - AppendToBufferWithRTF(buf, TranslateT("Offline file")); - buf.Append(" {\\field{\\*\\fldinst HYPERLINK \""); - buf.AppendFormat("ofile:%ul", dat->hDbEvent); - buf.Append("\"}{\\fldrslt{\\ul "); - AppendToBufferWithRTF(buf, blob.getName()); - buf.AppendFormat("}}} | %uKB", blob.getSize() / 1024); - - CMStringA szHost; - if (const char *b = strstr(blob.getUrl(), "://")) - for (b = b + 3; *b != 0 && *b != '/' && *b != ':'; b++) - szHost.AppendChar(*b); - - if (!szHost.IsEmpty()) - buf.AppendFormat(" on %s", szHost.c_str()); - - if (blob.getSize() > 0 && blob.getSize() == blob.getTransferred()) { - buf.AppendChar(' '); - AppendToBufferWithRTF(buf, TranslateT("Completed")); - } - } - else { - AppendToBufferWithRTF(buf, (dbei.flags & DBEF_SENT) ? TranslateT("File sent") : TranslateT("File received")); - buf.Append(": "); - AppendToBufferWithRTF(buf, blob.getName()); - - if (mir_wstrlen(blob.getDescr())) { - buf.Append(" ("); - AppendToBufferWithRTF(buf, blob.getDescr()); - buf.Append(")"); - } - } - } - break; - - case EVENTTYPE_MESSAGE: - default: - msg = DbEvent_GetTextW(&dbei, CP_ACP); - SetToStyle((dbei.eventType == EVENTTYPE_MESSAGE) ? ((dbei.flags & DBEF_SENT) ? MSGFONTID_MYMSG : MSGFONTID_YOURMSG) : MSGFONTID_NOTICE, buf); - AppendToBufferWithRTF(buf, msg); - mir_free(msg); - } - - if (bIsRtl) - buf.Append("\\par"); - - return true; -} - -static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - LogStreamData *dat = (LogStreamData*)dwCookie; - - if (dat->buf.IsEmpty()) { - switch (dat->stage) { - case STREAMSTAGE_HEADER: - CreateRTFHeader(dat->buf); - dat->stage = STREAMSTAGE_EVENTS; - break; - - case STREAMSTAGE_EVENTS: - if (dat->eventsToInsert) { - bool bOk; - do { - bOk = CreateRTFFromDbEvent(dat); - if (bOk) - dat->hDbEventLast = dat->hDbEvent; - dat->hDbEvent = db_event_next(dat->hContact, dat->hDbEvent); - if (--dat->eventsToInsert == 0) - break; - } while (!bOk && dat->hDbEvent); - - if (bOk) { - dat->isEmpty = false; - break; - } - } - dat->stage = STREAMSTAGE_TAIL; - __fallthrough; - - case STREAMSTAGE_TAIL: - CreateRTFTail(dat->buf); - dat->stage = STREAMSTAGE_STOP; - break; - case STREAMSTAGE_STOP: - *pcb = 0; - return 0; - } - } - - *pcb = min(cb, dat->buf.GetLength()); - memcpy(pbBuff, dat->buf.GetBuffer(), *pcb); - if (dat->buf.GetLength() == *pcb) - dat->buf.Empty(); - else - dat->buf.Delete(0, *pcb); - - return 0; -} - #define RTFPICTHEADERMAXSIZE 78 void LoadMsgLogIcons(void) { @@ -461,204 +255,519 @@ void FreeMsgLogIcons(void) } ///////////////////////////////////////////////////////////////////////////////////////// +// Log window class -void CLogWindow::Attach() +class CLogWindow : public CRtfLogWindow { - CSuper::Attach(); + typedef CRtfLogWindow CSuper; - // get around a lame bug in the Windows template resource code where richedits are limited to 0x7FFF - m_rtf.SendMsg(EM_LIMITTEXT, sizeof(wchar_t) * 0x7FFFFFFF, 0); - m_rtf.SendMsg(EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_LINK | ENM_SCROLL); - m_rtf.SendMsg(EM_HIDESELECTION, TRUE, 0); - m_rtf.SendMsg(EM_AUTOURLDETECT, TRUE, 0); -} +public: + CLogWindow(CMsgDialog &pDlg) : + CSuper(pDlg) + {} -void CLogWindow::LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) -{ - CHARRANGE oldSel, sel; - BOOL bottomScroll = TRUE; - POINT scrollPos; - - m_rtf.SetDraw(false); - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldSel); - - LogStreamData streamData = {}; - streamData.hContact = m_pDlg.m_hContact; - streamData.hDbEvent = hDbEventFirst; - streamData.dlgDat = &m_pDlg; - streamData.eventsToInsert = count; - streamData.isEmpty = !bAppend || GetWindowTextLength(m_rtf.GetHwnd()) == 0; - - EDITSTREAM stream = {}; - stream.pfnCallback = LogStreamInEvents; - stream.dwCookie = (DWORD_PTR)&streamData; - - if (!streamData.isEmpty) { - bottomScroll = GetFocus() != m_rtf.GetHwnd() && AtBottom(); - if (!bottomScroll) - m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&scrollPos); + void AppendUnicodeString(CMStringA &str, const wchar_t *pwszBuf) + { + AppendToBufferWithRTF(str, pwszBuf); } - FINDTEXTEXA fi; - if (bAppend) { - sel.cpMin = sel.cpMax = -1; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - fi.chrg.cpMin = 0; - } - else { - GETTEXTLENGTHEX gtxl = { 0 }; - gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMCHARS; - gtxl.codepage = 1200; - fi.chrg.cpMin = m_rtf.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); + void Attach() override + { + CSuper::Attach(); - sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + // get around a lame bug in the Windows template resource code where richedits are limited to 0x7FFF + m_rtf.SendMsg(EM_LIMITTEXT, sizeof(wchar_t) * 0x7FFFFFFF, 0); + m_rtf.SendMsg(EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_LINK | ENM_SCROLL); + m_rtf.SendMsg(EM_HIDESELECTION, TRUE, 0); + m_rtf.SendMsg(EM_AUTOURLDETECT, TRUE, 0); } - mir_strcpy(szSep2, bAppend ? "\\par\\sl0" : "\\sl1000"); - mir_strcpy(szSep2_RTL, bAppend ? "\\rtlpar\\rtlmark\\par\\sl1000" : "\\sl1000"); + bool CreateRTFFromDbEvent(LogStreamData *dat) + { + DB::EventInfo dbei(dat->hDbEvent); + if (!dbei) + return false; - m_rtf.SendMsg(EM_STREAMIN, bAppend ? SFF_SELECTION | SF_RTF : SF_RTF, (LPARAM)&stream); - if (bottomScroll) { - sel.cpMin = sel.cpMax = -1; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - } - else { - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldSel); - m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&scrollPos); + if (!DbEventIsShown(&dbei)) + return false; + + if (!(dbei.flags & DBEF_SENT) && (dbei.eventType == EVENTTYPE_MESSAGE || DbEventIsForMsgWindow(&dbei))) { + db_event_markRead(dat->hContact, dat->hDbEvent); + g_clistApi.pfnRemoveEvent(dat->hContact, dat->hDbEvent); + } + else if (dbei.eventType == EVENTTYPE_JABBER_CHATSTATES || dbei.eventType == EVENTTYPE_JABBER_PRESENCE) { + db_event_markRead(dat->hContact, dat->hDbEvent); + } + + CMStringA &buf = dat->buf; + bool bIsRtl = m_pDlg.m_bIsAutoRTL; + if (!bIsRtl && !dat->isEmpty) + buf.Append("\\par"); + + if (dbei.flags & DBEF_RTL) { + buf.Append("\\rtlpar"); + bIsRtl = true; + } + else buf.Append("\\ltrpar"); + + dat->isEmpty = false; + + if (bIsRtl) { + if (dbei.flags & DBEF_RTL) + buf.Append("\\ltrch\\rtlch"); + else + buf.Append("\\rtlch\\ltrch"); + } + + if (g_plugin.bShowIcons) { + int i = ((dbei.eventType == EVENTTYPE_MESSAGE) ? ((dbei.flags & DBEF_SENT) ? LOGICON_MSG_OUT : LOGICON_MSG_IN) : LOGICON_MSG_NOTICE); + + buf.Append("\\f0\\fs14"); + buf.Append(pLogIconBmpBits[i]); + } + + int showColon = 0; + if (g_plugin.bShowTime) { + const wchar_t *szFormat; + wchar_t str[64]; + + if (g_plugin.bShowSecs) + szFormat = g_plugin.bShowDate ? L"d s" : L"s"; + else + szFormat = g_plugin.bShowDate ? L"d t" : L"t"; + + TimeZone_PrintTimeStamp(nullptr, dbei.timestamp, szFormat, str, _countof(str), 0); + + SetToStyle((dbei.flags & DBEF_SENT) ? MSGFONTID_MYTIME : MSGFONTID_YOURTIME, buf); + AppendToBufferWithRTF(buf, str); + showColon = 1; + } + + if (g_plugin.bShowNames && dbei.eventType != EVENTTYPE_JABBER_CHATSTATES && dbei.eventType != EVENTTYPE_JABBER_PRESENCE) { + wchar_t *szName; + + if (dbei.flags & DBEF_SENT) { + if (wchar_t *p = Contact::GetInfo(CNF_DISPLAY, 0, dbei.szModule)) + szName = NEWWSTR_ALLOCA(p); + else + szName = TranslateT("Me"); + } + else szName = Clist_GetContactDisplayName(dat->hContact); + + SetToStyle((dbei.flags & DBEF_SENT) ? MSGFONTID_MYNAME : MSGFONTID_YOURNAME, buf); + AppendToBufferWithRTF(buf, szName); + showColon = 1; + } + + if (showColon) + SetToStyle((dbei.flags & DBEF_SENT) ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON, buf); + + wchar_t *msg, *szName; + switch (dbei.eventType) { + case EVENTTYPE_JABBER_CHATSTATES: + case EVENTTYPE_JABBER_PRESENCE: + if (dbei.flags & DBEF_SENT) { + if (wchar_t *p = Contact::GetInfo(CNF_DISPLAY, 0, dbei.szModule)) { + szName = NEWWSTR_ALLOCA(p); + mir_free(p); + } + else szName = L""; + } + else szName = Clist_GetContactDisplayName(dat->hContact); + + SetToStyle(MSGFONTID_NOTICE, buf); + AppendToBufferWithRTF(buf, szName); + AppendToBufferWithRTF(buf, L" "); + + msg = DbEvent_GetTextW(&dbei, CP_ACP); + if (msg) { + AppendToBufferWithRTF(buf, msg); + mir_free(msg); + } + break; + + case EVENTTYPE_FILE: + SetToStyle(MSGFONTID_NOTICE, buf); + { + DB::FILE_BLOB blob(dbei); + if (blob.isOffline()) { + InsertFileLink(buf, dat->hDbEvent, blob); + } + else { + AppendToBufferWithRTF(buf, (dbei.flags & DBEF_SENT) ? TranslateT("File sent") : TranslateT("File received")); + buf.Append(": "); + AppendToBufferWithRTF(buf, blob.getName()); + + if (mir_wstrlen(blob.getDescr())) { + buf.Append(" ("); + AppendToBufferWithRTF(buf, blob.getDescr()); + buf.Append(")"); + } + } + } + break; + + case EVENTTYPE_MESSAGE: + default: + msg = DbEvent_GetTextW(&dbei, CP_ACP); + SetToStyle((dbei.eventType == EVENTTYPE_MESSAGE) ? ((dbei.flags & DBEF_SENT) ? MSGFONTID_MYMSG : MSGFONTID_YOURMSG) : MSGFONTID_NOTICE, buf); + AppendToBufferWithRTF(buf, msg); + mir_free(msg); + } + + if (bIsRtl) + buf.Append("\\par"); + + return true; } - if (g_plugin.bSmileyInstalled) { - SMADD_RICHEDIT3 smre; - smre.cbSize = sizeof(SMADD_RICHEDIT3); - smre.hwndRichEditControl = m_rtf.GetHwnd(); + ///////////////////////////////////////////////////////////////////////////////////////// - MCONTACT hContact = db_mc_getSrmmSub(m_pDlg.m_hContact); - smre.Protocolname = (hContact != 0) ? Proto_GetBaseAccountName(hContact) : m_pDlg.m_szProto; + void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override + { + CHARRANGE oldSel, sel; + BOOL bottomScroll = TRUE; + POINT scrollPos; - if (fi.chrg.cpMin > 0) { - sel.cpMin = fi.chrg.cpMin; - sel.cpMax = -1; - smre.rangeToReplace = &sel; + m_rtf.SetDraw(false); + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldSel); + + LogStreamData streamData = {}; + streamData.hContact = m_pDlg.m_hContact; + streamData.hDbEvent = hDbEventFirst; + streamData.pLog = this; + streamData.eventsToInsert = count; + streamData.isEmpty = !bAppend || GetWindowTextLength(m_rtf.GetHwnd()) == 0; + + EDITSTREAM stream = {}; + stream.pfnCallback = LogStreamInEvents; + stream.dwCookie = (DWORD_PTR)&streamData; + + if (!streamData.isEmpty) { + bottomScroll = GetFocus() != m_rtf.GetHwnd() && AtBottom(); + if (!bottomScroll) + m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&scrollPos); } - else smre.rangeToReplace = nullptr; - smre.disableRedraw = TRUE; - smre.hContact = m_pDlg.m_hContact; - smre.flags = 0; - CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&smre); + FINDTEXTEXA fi; + if (bAppend) { + sel.cpMin = sel.cpMax = -1; + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + fi.chrg.cpMin = 0; + } + else { + GETTEXTLENGTHEX gtxl = { 0 }; + gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMCHARS; + gtxl.codepage = 1200; + fi.chrg.cpMin = m_rtf.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)>xl, 0); + + sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + } + + mir_strcpy(szSep2, bAppend ? "\\par\\sl0" : "\\sl1000"); + mir_strcpy(szSep2_RTL, bAppend ? "\\rtlpar\\rtlmark\\par\\sl1000" : "\\sl1000"); + + m_rtf.SendMsg(EM_STREAMIN, bAppend ? SFF_SELECTION | SF_RTF : SF_RTF, (LPARAM)&stream); + if (bottomScroll) { + sel.cpMin = sel.cpMax = -1; + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + } + else { + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldSel); + m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&scrollPos); + } + + if (g_plugin.bSmileyInstalled) { + SMADD_RICHEDIT3 smre; + smre.cbSize = sizeof(SMADD_RICHEDIT3); + smre.hwndRichEditControl = m_rtf.GetHwnd(); + + MCONTACT hContact = db_mc_getSrmmSub(m_pDlg.m_hContact); + smre.Protocolname = (hContact != 0) ? Proto_GetBaseAccountName(hContact) : m_pDlg.m_szProto; + + if (fi.chrg.cpMin > 0) { + sel.cpMin = fi.chrg.cpMin; + sel.cpMax = -1; + smre.rangeToReplace = &sel; + } + else smre.rangeToReplace = nullptr; + + smre.disableRedraw = TRUE; + smre.hContact = m_pDlg.m_hContact; + smre.flags = 0; + CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&smre); + } + + m_rtf.SetDraw(true); + if (bottomScroll || AtBottom()) { + ScrollToBottom(); + RedrawWindow(m_rtf.GetHwnd(), nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); + } + + m_pDlg.m_hDbEventLast = streamData.hDbEventLast; } - m_rtf.SetDraw(true); - if (bottomScroll || AtBottom()) { - ScrollToBottom(); - RedrawWindow(m_rtf.GetHwnd(), nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); + ///////////////////////////////////////////////////////////////////////////////////////// + + void LogEvents(struct LOGINFO *lin, bool bRedraw) override + { + auto *si = m_pDlg.m_si; + if (lin == nullptr || si == nullptr) + return; + + if (!bRedraw && si->iType == GCW_CHATROOM && (m_pDlg.m_iLogFilterFlags & lin->iType) == 0) + return; + + LOGSTREAMDATA streamData; + memset(&streamData, 0, sizeof(streamData)); + streamData.hwnd = m_rtf.GetHwnd(); + streamData.si = si; + streamData.lin = lin; + streamData.bStripFormat = FALSE; + + bool bFlag = false; + + EDITSTREAM stream = {}; + stream.pfnCallback = Srmm_LogStreamCallback; + stream.dwCookie = (DWORD_PTR)&streamData; + + SCROLLINFO scroll; + scroll.cbSize = sizeof(SCROLLINFO); + scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + GetScrollInfo(m_rtf.GetHwnd(), SB_VERT, &scroll); + + POINT point = {}; + m_rtf.SendMsg(EM_GETSCROLLPOS, 0, (LPARAM)&point); + + // do not scroll to bottom if there is a selection + CHARRANGE oldsel, sel; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldsel); + if (oldsel.cpMax != oldsel.cpMin) + m_rtf.SetDraw(false); + + //set the insertion point at the bottom + sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + + // fix for the indent... must be a M$ bug + if (sel.cpMax == 0) + bRedraw = TRUE; + + // should the event(s) be appended to the current log + WPARAM wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF; + + //get the number of pixels per logical inch + if (bRedraw) { + HDC hdc = GetDC(nullptr); + g_chatApi.logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); + g_chatApi.logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(nullptr, hdc); + m_rtf.SetDraw(false); + bFlag = true; + } + + // stream in the event(s) + streamData.lin = lin; + streamData.bRedraw = bRedraw; + m_rtf.SendMsg(EM_STREAMIN, wp, (LPARAM)&stream); + + // do smileys + if (g_plugin.bSmileyInstalled && (bRedraw || (lin->ptszText && lin->iType != GC_EVENT_JOIN && lin->iType != GC_EVENT_NICK && lin->iType != GC_EVENT_ADDSTATUS && lin->iType != GC_EVENT_REMOVESTATUS))) { + CHARRANGE newsel; + newsel.cpMax = -1; + newsel.cpMin = sel.cpMin; + if (newsel.cpMin < 0) + newsel.cpMin = 0; + + SMADD_RICHEDIT3 sm = {}; + sm.cbSize = sizeof(sm); + sm.hwndRichEditControl = m_rtf.GetHwnd(); + sm.Protocolname = si->pszModule; + sm.rangeToReplace = bRedraw ? nullptr : &newsel; + sm.disableRedraw = TRUE; + sm.hContact = si->hContact; + CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm); + } + + // scroll log to bottom if the log was previously scrolled to bottom, else restore old position + if (bRedraw || (UINT)scroll.nPos >= (UINT)scroll.nMax - scroll.nPage - 5 || scroll.nMax - scroll.nMin - scroll.nPage < 50) + ScrollToBottom(); + else + m_rtf.SendMsg(EM_SETSCROLLPOS, 0, (LPARAM)&point); + + // do we need to restore the selection + if (oldsel.cpMax != oldsel.cpMin) { + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&oldsel); + m_rtf.SetDraw(true); + InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); + } + + // need to invalidate the window + if (bFlag) { + sel.cpMin = sel.cpMax = m_rtf.GetRichTextLength(); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + m_rtf.SetDraw(true); + InvalidateRect(m_rtf.GetHwnd(), nullptr, TRUE); + } } - m_pDlg.m_hDbEventLast = streamData.hDbEventLast; -} + ///////////////////////////////////////////////////////////////////////////////////////// -void CLogWindow::UpdateOptions() -{ - if (m_pDlg.isChat()) - m_rtf.SendMsg(EM_SETBKGNDCOLOR, 0, g_Settings.crLogBackground); - else { - // configure message history for proper RTL formatting - PARAFORMAT2 pf2; - memset(&pf2, 0, sizeof(pf2)); - pf2.cbSize = sizeof(pf2); - - pf2.wEffects = PFE_RTLPARA; - pf2.dwMask = PFM_RTLPARA; - m_rtf.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2); - - pf2.wEffects = 0; - m_rtf.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2); - - m_rtf.SendMsg(EM_SETLANGOPTIONS, 0, m_rtf.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD); - m_rtf.SendMsg(EM_SETBKGNDCOLOR, 0, g_plugin.getDword(SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR)); + void UpdateOptions() override + { + if (m_pDlg.isChat()) + m_rtf.SendMsg(EM_SETBKGNDCOLOR, 0, g_Settings.crLogBackground); + else { + // configure message history for proper RTL formatting + PARAFORMAT2 pf2; + memset(&pf2, 0, sizeof(pf2)); + pf2.cbSize = sizeof(pf2); + + pf2.wEffects = PFE_RTLPARA; + pf2.dwMask = PFM_RTLPARA; + m_rtf.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2); + + pf2.wEffects = 0; + m_rtf.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2); + + m_rtf.SendMsg(EM_SETLANGOPTIONS, 0, m_rtf.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD); + m_rtf.SendMsg(EM_SETBKGNDCOLOR, 0, g_plugin.getDword(SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR)); + } } -} -///////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////// -static const CHARRANGE rangeAll = { 0, -1 }; + INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam) override + { + CHARRANGE sel; -INT_PTR CLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - CHARRANGE sel; - - switch (msg) { - case WM_CONTEXTMENU: - // we display context menu here only for private chats, group chats are processed by the core - if (!m_pDlg.isChat()) { - POINT pt; - GetCursorPos(&pt); - - SetFocus(m_rtf.GetHwnd()); - - HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); - HMENU hSubMenu = GetSubMenu(hMenu, 0); - TranslateMenu(hSubMenu); - - CHARRANGE all = { 0, -1 }; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin == sel.cpMax) - EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); - - switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) { - case IDM_COPY: - m_rtf.SendMsg(WM_COPY, 0, 0); - break; - case IDM_COPYALL: - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); - m_rtf.SendMsg(WM_COPY, 0, 0); - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); - break; - case IDM_SELECTALL: - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); - break; - case IDM_CLEAR: - Clear(); - m_pDlg.m_hDbEventFirst = 0; - break; + switch (msg) { + case WM_CONTEXTMENU: + // we display context menu here only for private chats, group chats are processed by the core + if (!m_pDlg.isChat()) { + POINT pt; + GetCursorPos(&pt); + + SetFocus(m_rtf.GetHwnd()); + + HMENU hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT)); + HMENU hSubMenu = GetSubMenu(hMenu, 0); + TranslateMenu(hSubMenu); + + CHARRANGE all = { 0, -1 }; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin == sel.cpMax) + EnableMenuItem(hSubMenu, IDM_COPY, MF_BYCOMMAND | MF_GRAYED); + + switch (TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_pDlg.m_hwnd, nullptr)) { + case IDM_COPY: + m_rtf.SendMsg(WM_COPY, 0, 0); + break; + case IDM_COPYALL: + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); + m_rtf.SendMsg(WM_COPY, 0, 0); + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + break; + case IDM_SELECTALL: + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&all); + break; + case IDM_CLEAR: + Clear(); + m_pDlg.m_hDbEventFirst = 0; + break; + } + DestroyMenu(hSubMenu); + DestroyMenu(hMenu); + return TRUE; } - DestroyMenu(hSubMenu); - DestroyMenu(hMenu); - return TRUE; - } - break; - - case WM_LBUTTONUP: - if (g_plugin.bAutoCopy) { - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) { - m_rtf.SendMsg(WM_COPY, 0, 0); - sel.cpMin = sel.cpMax; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + break; + + case WM_LBUTTONUP: + if (g_plugin.bAutoCopy) { + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) { + m_rtf.SendMsg(WM_COPY, 0, 0); + sel.cpMin = sel.cpMax; + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); + } + SetFocus(m_pDlg.m_message.GetHwnd()); } - SetFocus(m_pDlg.m_message.GetHwnd()); - } - break; - - case WM_KEYDOWN: - bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - bool isCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; - bool isAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; - - if (wParam == 0x57 && isCtrl && !isAlt) { // ctrl-w (close window) - m_pDlg.CloseTab(); - return TRUE; + break; + + case WM_KEYDOWN: + bool isShift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; + bool isCtrl = (GetKeyState(VK_CONTROL) & 0x8000) != 0; + bool isAlt = (GetKeyState(VK_MENU) & 0x8000) != 0; + + if (wParam == 0x57 && isCtrl && !isAlt) { // ctrl-w (close window) + m_pDlg.CloseTab(); + return TRUE; + } + + if (m_pDlg.ProcessHotkeys(wParam, isShift, isCtrl, isAlt)) + return FALSE; } - if (m_pDlg.ProcessHotkeys(wParam, isShift, isCtrl, isAlt)) - return FALSE; + return CSuper::WndProc(msg, wParam, lParam); } +}; - return CSuper::WndProc(msg, wParam, lParam); +static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + LogStreamData *dat = (LogStreamData *)dwCookie; + + if (dat->buf.IsEmpty()) { + switch (dat->stage) { + case STREAMSTAGE_HEADER: + CreateRTFHeader(dat->buf); + dat->stage = STREAMSTAGE_EVENTS; + break; + + case STREAMSTAGE_EVENTS: + if (dat->eventsToInsert) { + bool bOk; + do { + bOk = dat->pLog->CreateRTFFromDbEvent(dat); + if (bOk) + dat->hDbEventLast = dat->hDbEvent; + dat->hDbEvent = db_event_next(dat->hContact, dat->hDbEvent); + if (--dat->eventsToInsert == 0) + break; + } while (!bOk && dat->hDbEvent); + + if (bOk) { + dat->isEmpty = false; + break; + } + } + dat->stage = STREAMSTAGE_TAIL; + __fallthrough; + + case STREAMSTAGE_TAIL: + CreateRTFTail(dat->buf); + dat->stage = STREAMSTAGE_STOP; + break; + case STREAMSTAGE_STOP: + *pcb = 0; + return 0; + } + } + + *pcb = min(cb, dat->buf.GetLength()); + memcpy(pbBuff, dat->buf.GetBuffer(), *pcb); + if (dat->buf.GetLength() == *pcb) + dat->buf.Empty(); + else + dat->buf.Delete(0, *pcb); + + return 0; } -CSrmmLogWindow *logBuilder(CMsgDialog &pDlg) +///////////////////////////////////////////////////////////////////////////////////////// +// Module entry point + +CSrmmLogWindow* logBuilder(CMsgDialog &pDlg) { return new CLogWindow(pDlg); } diff --git a/src/core/stdmsg/src/msgs.h b/src/core/stdmsg/src/msgs.h index 59458e220a..2dc98a0e66 100644 --- a/src/core/stdmsg/src/msgs.h +++ b/src/core/stdmsg/src/msgs.h @@ -31,23 +31,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define EVENTTYPE_JABBER_CHATSTATES 2000 #define EVENTTYPE_JABBER_PRESENCE 2001 -class CLogWindow : public CRtfLogWindow -{ - typedef CRtfLogWindow CSuper; - -public: - CLogWindow(CMsgDialog &pDlg) : - CSuper(pDlg) - {} - - void Attach() override; - void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override; - void LogEvents(struct LOGINFO *, bool) override; - void UpdateOptions() override; - - INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam) override; -}; - class CMsgDialog : public CSrmmBaseDialog { friend class CLogWindow; diff --git a/src/mir_app/src/db_events.cpp b/src/mir_app/src/db_events.cpp index c3135bfa76..29436c5f51 100644 --- a/src/mir_app/src/db_events.cpp +++ b/src/mir_app/src/db_events.cpp @@ -91,14 +91,14 @@ MIR_APP_DLL(DBEVENTTYPEDESCR*) DbEvent_GetType(const char *szModule, int eventTy ///////////////////////////////////////////////////////////////////////////////////////// -static wchar_t* getEventString(DBEVENTINFO *dbei, LPSTR &buf) +static wchar_t* getEventString(const DB::EventInfo *dbei, LPSTR &buf) { LPSTR in = buf; buf += mir_strlen(buf) + 1; - return (dbei->flags & DBEF_UTF) ? mir_utf8decodeW(in) : mir_a2u(in); + return dbei->getString(in); } -static INT_PTR DbEventGetTextWorker(DB::EventInfo *dbei, int codepage, int datatype) +static INT_PTR DbEventGetTextWorker(const DB::EventInfo *dbei, int codepage, int datatype) { if (dbei == nullptr || dbei->szModule == nullptr) return 0; @@ -205,12 +205,12 @@ static INT_PTR DbEventGetTextWorker(DB::EventInfo *dbei, int codepage, int datat return 0; } -MIR_APP_DLL(char*) DbEvent_GetTextA(DBEVENTINFO *dbei, int codepage) +MIR_APP_DLL(char*) DbEvent_GetTextA(const DBEVENTINFO *dbei, int codepage) { return (char*)DbEventGetTextWorker((DB::EventInfo *)dbei, codepage, DBVT_ASCIIZ); } -MIR_APP_DLL(wchar_t*) DbEvent_GetTextW(DBEVENTINFO *dbei, int codepage) +MIR_APP_DLL(wchar_t*) DbEvent_GetTextW(const DBEVENTINFO *dbei, int codepage) { return (wchar_t*)DbEventGetTextWorker((DB::EventInfo *)dbei, codepage, DBVT_WCHAR); } diff --git a/src/mir_app/src/mir_app.def b/src/mir_app/src/mir_app.def index d586c688c3..021694711d 100644 --- a/src/mir_app/src/mir_app.def +++ b/src/mir_app/src/mir_app.def @@ -849,3 +849,5 @@ Chat_IsMuted @941 NONAME ?getSize@FILE_BLOB@DB@@QBEIXZ @965 NONAME ?getTransferred@FILE_BLOB@DB@@QBEIXZ @966 NONAME ?getString@EventInfo@DB@@QBEPA_WPBD@Z @967 NONAME +?InsertFileLink@CRtfLogWindow@@IAEXAAV?$CMStringT@DV?$ChTraitsCRT@D@@@@IABVFILE_BLOB@DB@@@Z @968 NONAME +?GetDialog@CSrmmLogWindow@@QBEAAVCMsgDialog@@XZ @969 NONAME diff --git a/src/mir_app/src/mir_app64.def b/src/mir_app/src/mir_app64.def index 0d86eeb01a..b888eb1a3b 100644 --- a/src/mir_app/src/mir_app64.def +++ b/src/mir_app/src/mir_app64.def @@ -849,3 +849,5 @@ Chat_IsMuted @941 NONAME ?getSize@FILE_BLOB@DB@@QEBAIXZ @965 NONAME ?getTransferred@FILE_BLOB@DB@@QEBAIXZ @966 NONAME ?getString@EventInfo@DB@@QEBAPEA_WPEBD@Z @967 NONAME +?InsertFileLink@CRtfLogWindow@@IEAAXAEAV?$CMStringT@DV?$ChTraitsCRT@D@@@@IAEBVFILE_BLOB@DB@@@Z @968 NONAME +?GetDialog@CSrmmLogWindow@@QEBAAEAVCMsgDialog@@XZ @969 NONAME diff --git a/src/mir_app/src/srmm_log_rtf.cpp b/src/mir_app/src/srmm_log_rtf.cpp index dec151b43d..eae329a4f0 100644 --- a/src/mir_app/src/srmm_log_rtf.cpp +++ b/src/mir_app/src/srmm_log_rtf.cpp @@ -124,6 +124,31 @@ wchar_t* CRtfLogWindow::GetSelection() ///////////////////////////////////////////////////////////////////////////////////////// +void CRtfLogWindow::InsertFileLink(CMStringA &buf, MEVENT hEvent, const DB::FILE_BLOB &blob) +{ + AppendUnicodeString(buf, TranslateT("Offline file")); + buf.Append(" {\\field{\\*\\fldinst HYPERLINK \""); + buf.AppendFormat("ofile:%ul", hEvent); + buf.Append("\"}{\\fldrslt{\\ul "); + AppendUnicodeString(buf, blob.getName()); + buf.AppendFormat("}}} | %uKB", blob.getSize() / 1024); + + CMStringA szHost; + if (const char *b = strstr(blob.getUrl(), "://")) + for (b = b + 3; *b != 0 && *b != '/' && *b != ':'; b++) + szHost.AppendChar(*b); + + if (!szHost.IsEmpty()) + buf.AppendFormat(" on %s", szHost.c_str()); + + if (blob.getSize() > 0 && blob.getSize() == blob.getTransferred()) { + buf.AppendChar(' '); + AppendUnicodeString(buf, TranslateT("Completed")); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + INT_PTR CRtfLogWindow::Notify(WPARAM, LPARAM lParam) { LPNMHDR hdr = (LPNMHDR)lParam; -- cgit v1.2.3