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 --- include/m_database.h | 4 +- include/m_srmm_int.h | 7 + libs/win32/mir_app.lib | Bin 245744 -> 246684 bytes libs/win64/mir_app.lib | Bin 242852 -> 243818 bytes plugins/Scriver/src/msgdialog.cpp | 9 +- plugins/Scriver/src/msglog.cpp | 718 ++++++++++++++---------------- plugins/Scriver/src/msgs.cpp | 4 +- plugins/Scriver/src/msgs.h | 27 +- plugins/Scriver/src/msgutils.cpp | 5 +- plugins/TabSRMM/src/msgdlgutils.cpp | 3 + plugins/TabSRMM/src/msglog.cpp | 464 +++++++++---------- plugins/TabSRMM/src/msgs.h | 74 ++-- 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 ++ 20 files changed, 1153 insertions(+), 1181 deletions(-) diff --git a/include/m_database.h b/include/m_database.h index a3e7a09af9..28e0d78c17 100644 --- a/include/m_database.h +++ b/include/m_database.h @@ -517,8 +517,8 @@ __forceinline MCONTACT DbGetAuthEventContact(DBEVENTINFO *dbei) // Function returns a pointer to a string in the required format. // This string should be freed by a call of mir_free -EXTERN_C MIR_APP_DLL(char*) DbEvent_GetTextA(DBEVENTINFO *dbei, int codepage); -EXTERN_C MIR_APP_DLL(wchar_t*) DbEvent_GetTextW(DBEVENTINFO *dbei, int codepage); +EXTERN_C MIR_APP_DLL(char*) DbEvent_GetTextA(const DBEVENTINFO *dbei, int codepage); +EXTERN_C MIR_APP_DLL(wchar_t*) DbEvent_GetTextW(const DBEVENTINFO *dbei, int codepage); ///////////////////////////////////////////////////////////////////////////////////////// // Retrieves the event's icon diff --git a/include/m_srmm_int.h b/include/m_srmm_int.h index 27dc5d0ebf..9e3b8e0c7a 100644 --- a/include/m_srmm_int.h +++ b/include/m_srmm_int.h @@ -134,6 +134,10 @@ public: virtual void UpdateOptions() {}; virtual INT_PTR Notify(WPARAM, LPARAM) { return 0; } + + __inline CMsgDialog& GetDialog() const + { return m_pDlg; + } }; typedef CSrmmLogWindow *(MIR_CDECL *pfnSrmmLogCreator)(CMsgDialog &pDlg); @@ -149,10 +153,13 @@ class MIR_APP_EXPORT CRtfLogWindow : public CSrmmLogWindow protected: CCtrlRichEdit &m_rtf; + void InsertFileLink(CMStringA &buf, MEVENT hEvent, const DB::FILE_BLOB &blob); + public: CRtfLogWindow(CMsgDialog &pDlg); ~CRtfLogWindow() override; + virtual void AppendUnicodeString(CMStringA &str, const wchar_t *pwszBuf) = 0; virtual INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam); //////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/win32/mir_app.lib b/libs/win32/mir_app.lib index 2b977a54b5..89d18b715f 100644 Binary files a/libs/win32/mir_app.lib and b/libs/win32/mir_app.lib differ diff --git a/libs/win64/mir_app.lib b/libs/win64/mir_app.lib index 65574852bc..67a4e37d53 100644 Binary files a/libs/win64/mir_app.lib and b/libs/win64/mir_app.lib differ diff --git a/plugins/Scriver/src/msgdialog.cpp b/plugins/Scriver/src/msgdialog.cpp index 4c6c1f9af0..4112c792de 100644 --- a/plugins/Scriver/src/msgdialog.cpp +++ b/plugins/Scriver/src/msgdialog.cpp @@ -171,10 +171,6 @@ bool CMsgDialog::OnInitDialog() m_nTypeMode = PROTOTYPE_SELFTYPING_OFF; timerType.Start(1000); - m_lastEventType = -1; - m_lastEventTime = time(0); - m_startTime = time(0); - m_bUseRtl = g_plugin.getByte(m_hContact, "UseRTL", 0) != 0; PARAFORMAT2 pf2; @@ -434,7 +430,7 @@ void CMsgDialog::onClick_Quote(CCtrlButton*) if (!dbei) return; - if (DbEventIsMessageOrCustom(&dbei)) { + if (DbEventIsMessageOrCustom(dbei)) { buffer = DbEvent_GetTextW(&dbei, CP_ACP); if (buffer != nullptr) { CMStringW quotedBuffer(GetQuotedTextW(buffer)); @@ -1077,7 +1073,7 @@ INT_PTR CMsgDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) while (hDbEvent != 0) { DBEVENTINFO dbei = {}; db_event_get(hDbEvent, &dbei); - if (!(dbei.flags & DBEF_SENT) && DbEventIsMessageOrCustom(&dbei)) + if (!(dbei.flags & DBEF_SENT) && DbEventIsMessageOrCustom(dbei)) g_clistApi.pfnRemoveEvent(m_hContact, hDbEvent); hDbEvent = db_event_next(m_hContact, hDbEvent); } @@ -1140,7 +1136,6 @@ INT_PTR CMsgDialog::DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) return TRUE; case DM_REMAKELOG: - m_lastEventType = -1; if (wParam == 0 || wParam == m_hContact) m_pLog->LogEvents(m_hDbEventFirst, -1, 0); diff --git a/plugins/Scriver/src/msglog.cpp b/plugins/Scriver/src/msglog.cpp index 5bd6e406f7..0359c1cdf7 100644 --- a/plugins/Scriver/src/msglog.cpp +++ b/plugins/Scriver/src/msglog.cpp @@ -45,20 +45,22 @@ struct LogStreamData size_t bufferOffset, bufferLen; int eventsToInsert; int isFirst; - CMsgDialog *dlgDat; + class CLogWindow *pLog; GlobalMessageData *gdat; - EventData *events; + DB::EventInfo *dbei; }; -bool DbEventIsCustomForMsgWindow(const DBEVENTINFO *dbei) +static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb); + +bool DbEventIsCustomForMsgWindow(const DBEVENTINFO &dbei) { - DBEVENTTYPEDESCR *et = DbEvent_GetType(dbei->szModule, dbei->eventType); + DBEVENTTYPEDESCR *et = DbEvent_GetType(dbei.szModule, dbei.eventType); return et && (et->flags & DETF_MSGWINDOW); } -bool DbEventIsMessageOrCustom(const DBEVENTINFO* dbei) +bool DbEventIsMessageOrCustom(const DBEVENTINFO &dbei) { - return dbei->eventType == EVENTTYPE_MESSAGE || DbEventIsCustomForMsgWindow(dbei); + return dbei.eventType == EVENTTYPE_MESSAGE || dbei.eventType == EVENTTYPE_FILE || DbEventIsCustomForMsgWindow(dbei); } bool DbEventIsShown(const DBEVENTINFO &dbei) @@ -73,107 +75,15 @@ bool DbEventIsShown(const DBEVENTINFO &dbei) return 0; } - return DbEventIsCustomForMsgWindow(&dbei); -} - -EventData* CMsgDialog::GetEventFromDB(MCONTACT hContact, MEVENT hDbEvent) -{ - DB::EventInfo dbei(hDbEvent); - if (!dbei) - return nullptr; - - if (!DbEventIsShown(dbei)) - return nullptr; - - EventData *evt = (EventData*)mir_calloc(sizeof(EventData)); - evt->custom = DbEventIsCustomForMsgWindow(&dbei); - if (!(dbei.flags & DBEF_SENT) && (dbei.eventType == EVENTTYPE_MESSAGE || evt->custom)) { - db_event_markRead(hContact, hDbEvent); - g_clistApi.pfnRemoveEvent(hContact, hDbEvent); - } - else if (dbei.eventType == EVENTTYPE_JABBER_CHATSTATES || dbei.eventType == EVENTTYPE_JABBER_PRESENCE) - db_event_markRead(hContact, hDbEvent); - - evt->eventType = dbei.eventType; - evt->dwFlags = (dbei.flags & DBEF_READ ? IEEDF_READ : 0) | (dbei.flags & DBEF_SENT ? IEEDF_SENT : 0) | (dbei.flags & DBEF_RTL ? IEEDF_RTL : 0); - evt->dwFlags |= IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK; - - if (m_bUseRtl) - evt->dwFlags |= IEEDF_RTL; - - evt->time = dbei.timestamp; - evt->szNick.w = nullptr; - if (evt->dwFlags & IEEDF_SENT) - evt->szNick.w = Contact::GetInfo(CNF_DISPLAY, 0, m_szProto); - else - evt->szNick.w = mir_wstrdup(Clist_GetContactDisplayName(hContact)); - - evt->szText.w = DbEvent_GetTextW(&dbei, CP_UTF8); - - if (!m_bUseRtl && Utils_IsRtl(evt->szText.w)) - evt->dwFlags |= IEEDF_RTL; - - return evt; -} - -static EventData* GetTestEvent(uint32_t flags) -{ - EventData *evt = (EventData *)mir_calloc(sizeof(EventData)); - evt->eventType = EVENTTYPE_MESSAGE; - evt->dwFlags = IEEDF_READ | flags; - evt->dwFlags |= IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK; - evt->time = time(0); - return evt; + return DbEventIsCustomForMsgWindow(dbei); } -static EventData* GetTestEvents() +static void AppendUnicodeToBuffer(CMStringA &buf, const wchar_t *line) { - EventData *evt, *firstEvent, *prevEvent; - firstEvent = prevEvent = evt = GetTestEvent(IEEDF_SENT); - evt->szNick.w = mir_wstrdup(TranslateT("Me")); - evt->szText.w = mir_wstrdup(TranslateT("O Lord, bless this Thy hand grenade that with it Thou mayest blow Thine enemies")); - - evt = GetTestEvent(IEEDF_SENT); - evt->szNick.w = mir_wstrdup(TranslateT("Me")); - evt->szText.w = mir_wstrdup(TranslateT("to tiny bits, in Thy mercy")); - prevEvent->next = evt; - prevEvent = evt; - - evt = GetTestEvent(0); - evt->szNick.w = mir_wstrdup(TranslateT("My contact")); - evt->szText.w = mir_wstrdup(TranslateT("Lorem ipsum dolor sit amet,")); - prevEvent->next = evt; - prevEvent = evt; - - evt = GetTestEvent(0); - evt->szNick.w = mir_wstrdup(TranslateT("My contact")); - evt->szText.w = mir_wstrdup(TranslateT("consectetur adipisicing elit")); - prevEvent->next = evt; - prevEvent = evt; - return firstEvent; -} + buf.Append("{\\uc1 "); -static void freeEvent(EventData *evt) -{ - mir_free(evt->szNick.w); - mir_free(evt->szText.w); - mir_free(evt); -} - -static int AppendUnicodeOrAnsiiToBufferL(CMStringA &buf, const wchar_t *line, size_t maxLen, BOOL isAnsii) -{ - if (maxLen == -1) - maxLen = mir_wstrlen(line); - - const wchar_t *maxLine = line + maxLen; - - if (isAnsii) - buf.Append("{"); - else - buf.Append("{\\uc1 "); - - int wasEOL = 0, textCharsCount = 0; - for (; line < maxLine; line++, textCharsCount++) { + int wasEOL = 0; + for (; *line; line++) { wasEOL = 0; if (*line == '\r' && line[1] == '\n') { buf.Append("\\line "); @@ -194,9 +104,6 @@ static int AppendUnicodeOrAnsiiToBufferL(CMStringA &buf, const wchar_t *line, si else if (*line < 128) { buf.AppendChar((char)*line); } - else if (isAnsii) { - buf.AppendFormat("\\'%02x", (*line) & 0xFF); - } else { buf.AppendFormat("\\u%d ?", *line); } @@ -205,18 +112,6 @@ static int AppendUnicodeOrAnsiiToBufferL(CMStringA &buf, const wchar_t *line, si buf.AppendChar(' '); buf.AppendChar('}'); - - return textCharsCount; -} - -static int AppendAnsiToBuffer(CMStringA &buf, const char *line) -{ - return AppendUnicodeOrAnsiiToBufferL(buf, _A2T(line), -1, true); -} - -static int AppendUnicodeToBuffer(CMStringA &buf, const wchar_t *line) -{ - return AppendUnicodeOrAnsiiToBufferL(buf, line, -1, false); } // mir_free() the return value @@ -333,276 +228,22 @@ int isSameDate(time_t time1, time_t time2) return 0; } -static void AppendWithCustomLinks(EventData *evt, int style, CMStringA &buf) +static void AppendWithCustomLinks(DBEVENTINFO &dbei, int style, CMStringA &buf) { - if (evt->szText.w == nullptr) + if (dbei.pBlob == nullptr) return; - BOOL isAnsii = (evt->dwFlags & IEEDF_UNICODE_TEXT) == 0; wchar_t *wText; - size_t len; - if (isAnsii) { - len = mir_strlen(evt->szText.a); - wText = mir_a2u(evt->szText.a); - } - else { - wText = evt->szText.w; - len = (int)mir_wstrlen(evt->szText.w); - } - - if (len > 0) { - buf.AppendFormat("%s ", SetToStyle(style)); - AppendUnicodeOrAnsiiToBufferL(buf, wText, len, isAnsii); - } - - if (isAnsii) - mir_free(wText); -} - -// mir_free() the return value -char* CMsgDialog::CreateRTFFromEvent(EventData *evt, GlobalMessageData *gdat, LogStreamData *streamData) -{ - int style, showColon = 0; - int isGroupBreak = TRUE; - int highlight = 0; - - if ((gdat->flags.bGroupMessages) && evt->dwFlags == LOWORD(m_lastEventType) && - evt->eventType == EVENTTYPE_MESSAGE && HIWORD(m_lastEventType) == EVENTTYPE_MESSAGE && - (isSameDate(evt->time, m_lastEventTime)) && ((((int)evt->time < m_startTime) == (m_lastEventTime < m_startTime)) || !(evt->dwFlags & IEEDF_READ))) { - isGroupBreak = FALSE; - } - - CMStringA buf; - if (!streamData->isFirst && !m_isMixed) { - if (isGroupBreak || gdat->flags.bMarkFollowups) - buf.Append("\\par"); - else - buf.Append("\\line"); - } - - if (evt->dwFlags & IEEDF_RTL) - m_isMixed = 1; - - if (!streamData->isFirst && isGroupBreak && (gdat->flags.bDrawLines)) - buf.AppendFormat("\\sl-1\\slmult0\\highlight%d\\cf%d\\fs1 \\par\\sl0", fontOptionsListSize + 4, fontOptionsListSize + 4); - - buf.Append((evt->dwFlags & IEEDF_RTL) ? "\\rtlpar" : "\\ltrpar"); - - if (evt->eventType == EVENTTYPE_MESSAGE) - highlight = fontOptionsListSize + 2 + ((evt->dwFlags & IEEDF_SENT) ? 1 : 0); + if ((dbei.flags & DBEF_UTF) == 0) + wText = mir_a2u((char*)dbei.pBlob); else - highlight = fontOptionsListSize + 1; - - buf.AppendFormat("\\highlight%d\\cf%d", highlight, highlight); - if (!streamData->isFirst && m_isMixed) { - if (isGroupBreak) - buf.Append("\\sl-1 \\par\\sl0"); - else - buf.Append("\\sl-1 \\line\\sl0"); - } - streamData->isFirst = FALSE; - if (m_isMixed) { - if (evt->dwFlags & IEEDF_RTL) - buf.Append("\\ltrch\\rtlch"); - else - buf.Append("\\rtlch\\ltrch"); - } - if ((gdat->flags.bShowIcons) && isGroupBreak) { - int i = LOGICON_MSG_NOTICE; - - switch (evt->eventType) { - case EVENTTYPE_MESSAGE: - if (evt->dwFlags & IEEDF_SENT) - i = LOGICON_MSG_OUT; - else - i = LOGICON_MSG_IN; - break; - - default: - i = LOGICON_MSG_NOTICE; - break; - } - - buf.Append("\\fs1 "); - buf.Append(pLogIconBmpBits[i]); - buf.AppendChar(' '); - } - - if (gdat->flags.bShowTime && (evt->eventType != EVENTTYPE_MESSAGE || - (gdat->flags.bMarkFollowups || isGroupBreak || !(gdat->flags.bGroupMessages)))) { - wchar_t *timestampString = nullptr; - if (gdat->flags.bGroupMessages && evt->eventType == EVENTTYPE_MESSAGE) { - if (isGroupBreak) { - if (!gdat->flags.bMarkFollowups) - timestampString = TimestampToString(gdat->flags, evt->time, 0); - else if (gdat->flags.bShowDate) - timestampString = TimestampToString(gdat->flags, evt->time, 1); - } - else if (gdat->flags.bMarkFollowups) - timestampString = TimestampToString(gdat->flags, evt->time, 2); - } - else timestampString = TimestampToString(gdat->flags, evt->time, 0); - - if (timestampString != nullptr) { - buf.AppendFormat("%s ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYTIME : MSGFONTID_YOURTIME)); - AppendUnicodeToBuffer(buf, timestampString); - } - if (evt->eventType != EVENTTYPE_MESSAGE) - buf.AppendFormat("%s: ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON)); - showColon = 1; - } - if ((!(gdat->flags.bHideNames) && evt->eventType == EVENTTYPE_MESSAGE && isGroupBreak) || evt->eventType == EVENTTYPE_JABBER_CHATSTATES || evt->eventType == EVENTTYPE_JABBER_PRESENCE) { - if (evt->eventType == EVENTTYPE_MESSAGE) { - if (showColon) - buf.AppendFormat(" %s ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYNAME : MSGFONTID_YOURNAME)); - else - buf.AppendFormat("%s ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYNAME : MSGFONTID_YOURNAME)); - } - else buf.AppendFormat("%s ", SetToStyle(MSGFONTID_NOTICE)); - - if (evt->dwFlags & IEEDF_UNICODE_NICK) - AppendUnicodeToBuffer(buf, evt->szNick.w); - else - AppendAnsiToBuffer(buf, evt->szNick.a); - - showColon = 1; - if (evt->eventType == EVENTTYPE_MESSAGE && gdat->flags.bGroupMessages) { - if (gdat->flags.bMarkFollowups) - buf.Append("\\par"); - else - buf.Append("\\line"); - showColon = 0; - } - } + wText = mir_utf8decodeW((char *)dbei.pBlob); - if (gdat->flags.bShowTime && gdat->flags.bGroupMessages && gdat->flags.bMarkFollowups && evt->eventType == EVENTTYPE_MESSAGE && isGroupBreak) { - buf.AppendFormat(" %s ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYTIME : MSGFONTID_YOURTIME)); - AppendUnicodeToBuffer(buf, TimestampToString(gdat->flags, evt->time, 2)); - showColon = 1; - } - if (showColon && evt->eventType == EVENTTYPE_MESSAGE) { - if (evt->dwFlags & IEEDF_RTL) - buf.AppendFormat("\\~%s: ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON)); - else - buf.AppendFormat("%s: ", SetToStyle(evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON)); - } - switch (evt->eventType) { - case EVENTTYPE_JABBER_CHATSTATES: - case EVENTTYPE_JABBER_PRESENCE: - case EVENTTYPE_FILE: - style = MSGFONTID_NOTICE; + if (wText) { buf.AppendFormat("%s ", SetToStyle(style)); - if (evt->eventType == EVENTTYPE_FILE) { - if (evt->dwFlags & IEEDF_SENT) - AppendUnicodeToBuffer(buf, TranslateT("File sent")); - else - AppendUnicodeToBuffer(buf, TranslateT("File received")); - AppendUnicodeToBuffer(buf, L":"); - } - AppendUnicodeToBuffer(buf, L" "); - - if (evt->szText.w != nullptr) { - if (evt->dwFlags & IEEDF_UNICODE_TEXT) - AppendUnicodeToBuffer(buf, evt->szText.w); - else - AppendAnsiToBuffer(buf, evt->szText.a); - } - break; - - default: - if (gdat->flags.bMsgOnNewline && showColon) - buf.Append("\\line"); - - style = evt->dwFlags & IEEDF_SENT ? MSGFONTID_MYMSG : MSGFONTID_YOURMSG; - AppendWithCustomLinks(evt, style, buf); - break; - } - - if (m_isMixed) - buf.Append("\\par"); - - m_lastEventTime = evt->time; - m_lastEventType = MAKELONG(evt->dwFlags, evt->eventType); - return buf.Detach(); -} - -static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - LogStreamData *dat = (LogStreamData*)dwCookie; - - if (dat->buffer == nullptr) { - dat->bufferOffset = 0; - switch (dat->stage) { - case STREAMSTAGE_HEADER: - dat->buffer = CreateRTFHeader(); - dat->stage = STREAMSTAGE_EVENTS; - break; - case STREAMSTAGE_EVENTS: - if (dat->events != nullptr) { - EventData *evt = dat->events; - dat->buffer = nullptr; - dat->buffer = dat->dlgDat->CreateRTFFromEvent(evt, dat->gdat, dat); - dat->events = evt->next; - freeEvent(evt); - } - else if (dat->eventsToInsert) { - do { - EventData *evt = dat->dlgDat->GetEventFromDB(dat->hContact, dat->hDbEvent); - dat->buffer = nullptr; - if (evt != nullptr) { - dat->buffer = dat->dlgDat->CreateRTFFromEvent(evt, dat->gdat, dat); - freeEvent(evt); - } - if (dat->buffer) - dat->hDbEventLast = dat->hDbEvent; - dat->hDbEvent = db_event_next(dat->hContact, dat->hDbEvent); - if (--dat->eventsToInsert == 0) - break; - } while (dat->buffer == nullptr && dat->hDbEvent); - } - if (dat->buffer) - break; - - dat->stage = STREAMSTAGE_TAIL; - __fallthrough; - - case STREAMSTAGE_TAIL: - dat->buffer = CreateRTFTail(); - dat->stage = STREAMSTAGE_STOP; - break; - case STREAMSTAGE_STOP: - *pcb = 0; - return 0; - } - dat->bufferLen = mir_strlen(dat->buffer); - } - *pcb = min(cb, LONG(dat->bufferLen - dat->bufferOffset)); - memcpy(pbBuff, dat->buffer + dat->bufferOffset, *pcb); - dat->bufferOffset += *pcb; - if (dat->bufferOffset == dat->bufferLen) { - mir_free(dat->buffer); - dat->buffer = nullptr; + AppendUnicodeToBuffer(buf, wText); + mir_free(wText); } - return 0; -} - -void StreamInTestEvents(HWND hEditWnd, GlobalMessageData *gdat) -{ - CMsgDialog *dat = new CMsgDialog(0, false); - - LogStreamData streamData = { 0 }; - streamData.isFirst = TRUE; - streamData.events = GetTestEvents(); - streamData.dlgDat = dat; - streamData.gdat = gdat; - - EDITSTREAM stream = { 0 }; - stream.pfnCallback = LogStreamInEvents; - stream.dwCookie = (DWORD_PTR)&streamData; - SendMessage(hEditWnd, EM_STREAMIN, SF_RTF, (LPARAM)&stream); - SendMessage(hEditWnd, EM_HIDESELECTION, FALSE, 0); - - delete dat; } #define RTFPICTHEADERMAXSIZE 78 @@ -691,10 +332,21 @@ class CLogWindow : public CRtfLogWindow { typedef CRtfLogWindow CSuper; + int m_isMixed = 0; + int m_lastEventType = -1; + time_t m_startTime, m_lastEventTime; + public: CLogWindow(CMsgDialog &pDlg) : CSuper(pDlg) - {} + { + m_lastEventTime = m_startTime = time(0); + } + + void AppendUnicodeString(CMStringA &str, const wchar_t *pwszBuf) override + { + AppendUnicodeToBuffer(str, pwszBuf); + } void Attach() override { @@ -726,8 +378,191 @@ public: m_rtf.SendMsg(EM_AUTOURLDETECT, TRUE, 0); } + char* CreateRTFFromEvent(DB::EventInfo &dbei, LogStreamData *streamData) + { + int style, showColon = 0; + int isGroupBreak = TRUE; + int highlight = 0; + auto *gdat = streamData->gdat; + + if ((gdat->flags.bGroupMessages) && dbei.flags == LOWORD(m_lastEventType) && + dbei.eventType == EVENTTYPE_MESSAGE && HIWORD(m_lastEventType) == EVENTTYPE_MESSAGE && + (isSameDate(dbei.timestamp, m_lastEventTime)) && ((((int)dbei.timestamp < m_startTime) == (m_lastEventTime < m_startTime)) || !(dbei.flags & DBEF_READ))) { + isGroupBreak = FALSE; + } + + ptrW wszText(DbEvent_GetTextW(&dbei, CP_UTF8)), wszNick; + + // test contact + if (m_pDlg.m_hContact != 0) { + if (dbei.flags & DBEF_SENT) + wszNick = Contact::GetInfo(CNF_DISPLAY, 0, m_pDlg.m_szProto); + else + wszNick = mir_wstrdup(Clist_GetContactDisplayName(m_pDlg.m_hContact)); + } + else wszNick = mir_wstrdup((dbei.flags & DBEF_SENT) ? TranslateT("Me") : TranslateT("My contact")); + + if (!m_pDlg.m_bUseRtl && Utils_IsRtl(wszText)) + dbei.flags |= DBEF_RTL; + + CMStringA buf; + if (!streamData->isFirst && !m_isMixed) { + if (isGroupBreak || gdat->flags.bMarkFollowups) + buf.Append("\\par"); + else + buf.Append("\\line"); + } + + if (dbei.flags & DBEF_RTL) + m_isMixed = 1; + + if (!streamData->isFirst && isGroupBreak && (gdat->flags.bDrawLines)) + buf.AppendFormat("\\sl-1\\slmult0\\highlight%d\\cf%d\\fs1 \\par\\sl0", fontOptionsListSize + 4, fontOptionsListSize + 4); + + buf.Append((dbei.flags & DBEF_RTL) ? "\\rtlpar" : "\\ltrpar"); + + if (dbei.eventType == EVENTTYPE_MESSAGE) + highlight = fontOptionsListSize + 2 + ((dbei.flags & DBEF_SENT) ? 1 : 0); + else + highlight = fontOptionsListSize + 1; + + buf.AppendFormat("\\highlight%d\\cf%d", highlight, highlight); + if (!streamData->isFirst && m_isMixed) { + if (isGroupBreak) + buf.Append("\\sl-1 \\par\\sl0"); + else + buf.Append("\\sl-1 \\line\\sl0"); + } + streamData->isFirst = FALSE; + if (m_isMixed) { + if (dbei.flags & DBEF_RTL) + buf.Append("\\ltrch\\rtlch"); + else + buf.Append("\\rtlch\\ltrch"); + } + if ((gdat->flags.bShowIcons) && isGroupBreak) { + int i = LOGICON_MSG_NOTICE; + + switch (dbei.eventType) { + case EVENTTYPE_MESSAGE: + if (dbei.flags & DBEF_SENT) + i = LOGICON_MSG_OUT; + else + i = LOGICON_MSG_IN; + break; + + default: + i = LOGICON_MSG_NOTICE; + break; + } + + buf.Append("\\fs1 "); + buf.Append(pLogIconBmpBits[i]); + buf.AppendChar(' '); + } + + if (gdat->flags.bShowTime && (dbei.eventType != EVENTTYPE_MESSAGE || + (gdat->flags.bMarkFollowups || isGroupBreak || !(gdat->flags.bGroupMessages)))) { + wchar_t *timestampString = nullptr; + if (gdat->flags.bGroupMessages && dbei.eventType == EVENTTYPE_MESSAGE) { + if (isGroupBreak) { + if (!gdat->flags.bMarkFollowups) + timestampString = TimestampToString(gdat->flags, dbei.timestamp, 0); + else if (gdat->flags.bShowDate) + timestampString = TimestampToString(gdat->flags, dbei.timestamp, 1); + } + else if (gdat->flags.bMarkFollowups) + timestampString = TimestampToString(gdat->flags, dbei.timestamp, 2); + } + else timestampString = TimestampToString(gdat->flags, dbei.timestamp, 0); + + if (timestampString != nullptr) { + buf.AppendFormat("%s ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYTIME : MSGFONTID_YOURTIME)); + AppendUnicodeToBuffer(buf, timestampString); + } + if (dbei.eventType != EVENTTYPE_MESSAGE) + buf.AppendFormat("%s: ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON)); + showColon = 1; + } + if ((!(gdat->flags.bHideNames) && dbei.eventType == EVENTTYPE_MESSAGE && isGroupBreak) || dbei.eventType == EVENTTYPE_JABBER_CHATSTATES || dbei.eventType == EVENTTYPE_JABBER_PRESENCE) { + if (dbei.eventType == EVENTTYPE_MESSAGE) { + if (showColon) + buf.AppendFormat(" %s ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYNAME : MSGFONTID_YOURNAME)); + else + buf.AppendFormat("%s ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYNAME : MSGFONTID_YOURNAME)); + } + else buf.AppendFormat("%s ", SetToStyle(MSGFONTID_NOTICE)); + + AppendUnicodeToBuffer(buf, wszNick); + + showColon = 1; + if (dbei.eventType == EVENTTYPE_MESSAGE && gdat->flags.bGroupMessages) { + if (gdat->flags.bMarkFollowups) + buf.Append("\\par"); + else + buf.Append("\\line"); + showColon = 0; + } + } + + if (gdat->flags.bShowTime && gdat->flags.bGroupMessages && gdat->flags.bMarkFollowups && dbei.eventType == EVENTTYPE_MESSAGE && isGroupBreak) { + buf.AppendFormat(" %s ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYTIME : MSGFONTID_YOURTIME)); + AppendUnicodeToBuffer(buf, TimestampToString(gdat->flags, dbei.timestamp, 2)); + showColon = 1; + } + if (showColon && dbei.eventType == EVENTTYPE_MESSAGE) { + if (dbei.flags & DBEF_RTL) + buf.AppendFormat("\\~%s: ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON)); + else + buf.AppendFormat("%s: ", SetToStyle(dbei.flags & DBEF_SENT ? MSGFONTID_MYCOLON : MSGFONTID_YOURCOLON)); + } + switch (dbei.eventType) { + case EVENTTYPE_JABBER_CHATSTATES: + case EVENTTYPE_JABBER_PRESENCE: + case EVENTTYPE_FILE: + style = MSGFONTID_NOTICE; + buf.AppendFormat("%s ", SetToStyle(style)); + if (dbei.eventType == EVENTTYPE_FILE) { + DB::FILE_BLOB blob(dbei); + if (blob.isOffline()) { + InsertFileLink(buf, streamData->hDbEvent, blob); + break; + } + + if (dbei.flags & DBEF_SENT) + AppendUnicodeToBuffer(buf, TranslateT("File sent")); + else + AppendUnicodeToBuffer(buf, TranslateT("File received")); + AppendUnicodeToBuffer(buf, L":"); + } + AppendUnicodeToBuffer(buf, L" "); + + if (wszText != nullptr) + AppendUnicodeToBuffer(buf, wszText); + break; + + default: + if (gdat->flags.bMsgOnNewline && showColon) + buf.Append("\\line"); + + style = dbei.flags & DBEF_SENT ? MSGFONTID_MYMSG : MSGFONTID_YOURMSG; + AppendWithCustomLinks(dbei, style, buf); + break; + } + + if (m_isMixed) + buf.Append("\\par"); + + m_lastEventTime = dbei.timestamp; + m_lastEventType = MAKELONG(dbei.flags, dbei.eventType); + return buf.Detach(); + } + void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override { + if (!bAppend) + m_lastEventType = -1; + CHARRANGE oldSel, sel; m_rtf.SetDraw(false); m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&oldSel); @@ -736,7 +571,7 @@ public: streamData.hContact = m_pDlg.m_hContact; streamData.hDbEvent = hDbEventFirst; streamData.hDbEventLast = m_pDlg.m_hDbEventLast; - streamData.dlgDat = &m_pDlg; + streamData.pLog = this; streamData.eventsToInsert = count; streamData.isFirst = bAppend ? m_rtf.GetRichTextLength() == 0 : 1; streamData.gdat = &g_dat; @@ -774,7 +609,7 @@ public: sel.cpMax = m_rtf.GetRichTextLength(); m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&sel); fi.chrg.cpMin = 0; - m_pDlg.m_isMixed = 0; + m_isMixed = 0; } m_rtf.SendMsg(EM_STREAMIN, bAppend ? SFF_SELECTION | SF_RTF : SFF_SELECTION | SF_RTF, (LPARAM)&stream); @@ -817,6 +652,8 @@ public: m_pDlg.m_hDbEventLast = streamData.hDbEventLast; } + //////////////////////////////////////////////////////////////////////////////////////// + void LogEvents(struct LOGINFO *lin, bool bRedraw) override { auto *si = m_pDlg.m_si; @@ -982,6 +819,121 @@ public: } }; +///////////////////////////////////////////////////////////////////////////////////////// + +const char *szBuiltinEvents[] = { + "O Lord, bless this Thy hand grenade that with it Thou mayest blow Thine enemies", + "to tiny bits, in Thy mercy", + "Lorem ipsum dolor sit amet,", + "consectetur adipisicing elit", +}; + +static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + LogStreamData *dat = (LogStreamData *)dwCookie; + + if (dat->buffer == nullptr) { + dat->bufferOffset = 0; + switch (dat->stage) { + case STREAMSTAGE_HEADER: + dat->buffer = CreateRTFHeader(); + dat->stage = STREAMSTAGE_EVENTS; + break; + + case STREAMSTAGE_EVENTS: + // predefined text event + if (dat->dbei != nullptr) { + dat->dbei->flags = DBEF_UTF | ((dat->eventsToInsert < 2) ? DBEF_SENT : 0); + dat->dbei->pBlob = (uint8_t *)TranslateU(szBuiltinEvents[dat->eventsToInsert++]); + dat->dbei->cbBlob = (int)mir_strlen((char *)dat->dbei->pBlob); + dat->buffer = dat->pLog->CreateRTFFromEvent(*dat->dbei, dat); + if (dat->eventsToInsert == _countof(szBuiltinEvents)) { + dat->dbei->pBlob = nullptr; + dat->dbei = nullptr; + } + } + // usual database event + else if (dat->eventsToInsert) { + do { + dat->buffer = nullptr; + + DB::EventInfo dbei(dat->hDbEvent); + if (dbei && DbEventIsShown(dbei)) { + dat->buffer = dat->pLog->CreateRTFFromEvent(dbei, dat); + + if (!(dbei.flags & DBEF_SENT) && (dbei.eventType == EVENTTYPE_MESSAGE || DbEventIsCustomForMsgWindow(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); + } + + if (dat->buffer) + dat->hDbEventLast = dat->hDbEvent; + dat->hDbEvent = db_event_next(dat->hContact, dat->hDbEvent); + if (--dat->eventsToInsert == 0) + break; + } while (dat->buffer == nullptr && dat->hDbEvent); + } + if (dat->buffer) + break; + + dat->stage = STREAMSTAGE_TAIL; + __fallthrough; + + case STREAMSTAGE_TAIL: + dat->buffer = CreateRTFTail(); + dat->stage = STREAMSTAGE_STOP; + break; + + case STREAMSTAGE_STOP: + *pcb = 0; + return 0; + } + dat->bufferLen = mir_strlen(dat->buffer); + } + *pcb = min(cb, LONG(dat->bufferLen - dat->bufferOffset)); + memcpy(pbBuff, dat->buffer + dat->bufferOffset, *pcb); + dat->bufferOffset += *pcb; + if (dat->bufferOffset == dat->bufferLen) { + mir_free(dat->buffer); + dat->buffer = nullptr; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void StreamInTestEvents(HWND hEditWnd, GlobalMessageData *gdat) +{ + DB::EventInfo dbei; + dbei.flags = DBEF_UTF; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.timestamp = time(0); + dbei.szModule = SRMM_MODULE; + + CMsgDialog *dat = new CMsgDialog(0, false); + + LogStreamData streamData = {}; + streamData.isFirst = TRUE; + streamData.dbei = &dbei; + streamData.pLog = new CLogWindow(*dat); + streamData.gdat = gdat; + + EDITSTREAM stream = { 0 }; + stream.pfnCallback = LogStreamInEvents; + stream.dwCookie = (DWORD_PTR)&streamData; + SendMessage(hEditWnd, EM_STREAMIN, SF_RTF, (LPARAM)&stream); + SendMessage(hEditWnd, EM_HIDESELECTION, FALSE, 0); + + delete streamData.pLog; + delete dat; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Module entry point + CSrmmLogWindow* logBuilder(CMsgDialog &pDlg) { return new CLogWindow(pDlg); diff --git a/plugins/Scriver/src/msgs.cpp b/plugins/Scriver/src/msgs.cpp index 498dfe43e3..b3e0026f77 100644 --- a/plugins/Scriver/src/msgs.cpp +++ b/plugins/Scriver/src/msgs.cpp @@ -91,7 +91,7 @@ static int MessageEventAdded(WPARAM hContact, LPARAM hDbEvent) if (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ)) return 0; - if (dbei.flags & DBEF_SENT || !DbEventIsMessageOrCustom(&dbei)) + if (dbei.flags & DBEF_SENT || !DbEventIsMessageOrCustom(dbei)) return 0; /* does a window for the contact exist? */ @@ -239,7 +239,7 @@ static void RestoreUnreadMessageAlerts(void) DBEVENTINFO dbei = {}; if (db_event_get(hDbEvent, &dbei)) continue; - if (dbei.markedRead() || !DbEventIsMessageOrCustom(&dbei) || !Proto_GetBaseAccountName(hContact)) + if (dbei.markedRead() || !DbEventIsMessageOrCustom(dbei) || !Proto_GetBaseAccountName(hContact)) continue; int windowAlreadyExists = Srmm_FindWindow(hContact) != nullptr; diff --git a/plugins/Scriver/src/msgs.h b/plugins/Scriver/src/msgs.h index 3daa951eea..ce0e5c082c 100644 --- a/plugins/Scriver/src/msgs.h +++ b/plugins/Scriver/src/msgs.h @@ -29,23 +29,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define MSGERROR_RETRY 1 #define MSGERROR_DONE 2 -struct EventData -{ - int cbSize; - int iType; - uint32_t dwFlags; - const char *fontName; - int fontSize; - int fontStyle; - COLORREF color; - MAllStrings szNick; // Nick, usage depends on type of event - MAllStrings szText; // Text, usage depends on type of event - uint32_t time; - uint32_t eventType; - BOOL custom; - EventData *next; -}; - struct ToolbarButton { wchar_t *name; @@ -97,9 +80,6 @@ class CMsgDialog : public CSrmmBaseDialog HICON m_hStatusIcon, m_hStatusIconBig, m_hStatusIconOverlay; char *m_szProto; - time_t m_startTime, m_lastEventTime; - int m_lastEventType; - int m_isMixed; bool m_bUseRtl; HBITMAP m_hbmpAvatarPic; @@ -192,9 +172,6 @@ public: wchar_t *m_wszInitialText; - char* CreateRTFFromEvent(EventData *evt, GlobalMessageData *gdat, struct LogStreamData *streamData); - EventData *GetEventFromDB(MCONTACT hContact, MEVENT hDbEvent); - void Reattach(HWND hwndContainer); }; @@ -216,8 +193,8 @@ public: #define EVENTTYPE_JABBER_PRESENCE 2001 bool DbEventIsShown(const DBEVENTINFO &dbei); -bool DbEventIsCustomForMsgWindow(const DBEVENTINFO *dbei); -bool DbEventIsMessageOrCustom(const DBEVENTINFO *dbei); +bool DbEventIsCustomForMsgWindow(const DBEVENTINFO &dbei); +bool DbEventIsMessageOrCustom(const DBEVENTINFO &dbei); void LoadMsgLogIcons(void); void FreeMsgLogIcons(void); int IsAutoPopup(MCONTACT hContact); diff --git a/plugins/Scriver/src/msgutils.cpp b/plugins/Scriver/src/msgutils.cpp index dff0e00e10..b2abbe2a55 100644 --- a/plugins/Scriver/src/msgutils.cpp +++ b/plugins/Scriver/src/msgutils.cpp @@ -28,7 +28,6 @@ void CMsgDialog::ClearLog() CSuper::ClearLog(); m_hDbEventFirst = 0; - m_lastEventType = -1; } void CMsgDialog::CloseTab() @@ -102,7 +101,7 @@ void CMsgDialog::EventAdded(MEVENT hDbEvent, const DBEVENTINFO &dbei) else SendMessage(m_hwnd, DM_REMAKELOG, 0, 0); - if (!(dbei.flags & DBEF_SENT) && !DbEventIsCustomForMsgWindow(&dbei)) { + if (!(dbei.flags & DBEF_SENT) && !DbEventIsCustomForMsgWindow(dbei)) { if (!bIsActive) { m_iShowUnread = 1; UpdateIcon(); @@ -124,7 +123,7 @@ bool CMsgDialog::GetFirstEvent() if (m_hDbEventFirst != 0) { DBEVENTINFO dbei = {}; db_event_get(m_hDbEventFirst, &dbei); - if (DbEventIsMessageOrCustom(&dbei) && !(dbei.flags & DBEF_READ) && !(dbei.flags & DBEF_SENT)) + if (DbEventIsMessageOrCustom(dbei) && !(dbei.flags & DBEF_READ) && !(dbei.flags & DBEF_SENT)) notifyUnread = true; } diff --git a/plugins/TabSRMM/src/msgdlgutils.cpp b/plugins/TabSRMM/src/msgdlgutils.cpp index eff4705074..f3ed376965 100644 --- a/plugins/TabSRMM/src/msgdlgutils.cpp +++ b/plugins/TabSRMM/src/msgdlgutils.cpp @@ -320,6 +320,9 @@ bool IsStringValidLink(wchar_t *pszText) if (pszText[0] == '\\' && pszText[1] == '\\') return true; + if (!mir_wstrncmp(pszText, L"ofile:", 6)) + return true; + if (mir_wstrlen(pszText) < 5 || wcschr(pszText, '"')) return false; diff --git a/plugins/TabSRMM/src/msglog.cpp b/plugins/TabSRMM/src/msglog.cpp index 3499e97cc3..36c76a92ad 100644 --- a/plugins/TabSRMM/src/msglog.cpp +++ b/plugins/TabSRMM/src/msglog.cpp @@ -94,7 +94,7 @@ struct LogStreamData int eventsToInsert; int isEmpty; int isAppend; - CMsgDialog *dlgDat; + class CLogWindow *pLog; DBEVENTINFO *dbei; }; @@ -234,12 +234,11 @@ static int TSAPI GetColorIndex(char *rtffont) return 0; } -static int AppendUnicodeToBuffer(CMStringA &str, const wchar_t *line, int mode) +static void AppendUnicodeToBuffer(CMStringA &str, const wchar_t *line, int mode) { str.Append("{\\uc1 "); - int textCharsCount = 0; - for (; *line; line++, textCharsCount++) { + for (; *line; line++) { if (*line == 127 && line[1] != 0) { wchar_t code = line[2]; if (((code == '0' || code == '1') && line[3] == ' ') || (line[1] == 'c' && code == 'x')) { @@ -302,17 +301,19 @@ static int AppendUnicodeToBuffer(CMStringA &str, const wchar_t *line, int mode) } str.AppendChar('}'); - return textCharsCount; } ///////////////////////////////////////////////////////////////////////////////////////// +// mir_free() the return value -static void Build_RTF_Header(CMStringA &str, CMsgDialog *dat) +static char* CreateRTFHeader(CLogWindow *pLog) { int i; - LOGFONTW *logFonts = dat->m_pContainer->m_theme.logFonts; - COLORREF *fontColors = dat->m_pContainer->m_theme.fontColors; - TLogTheme *theme = &dat->m_pContainer->m_theme; + CMStringA str; + auto &dat = pLog->GetDialog(); + TLogTheme *theme = &dat.m_pContainer->m_theme; + LOGFONTW *logFonts = theme->logFonts; + COLORREF *fontColors = theme->fontColors; str.Append("{\\rtf1\\ansi\\deff0{\\fonttbl"); @@ -359,15 +360,8 @@ static void Build_RTF_Header(CMStringA &str, CMsgDialog *dat) str.AppendFormat("}"); // indent - if (!(dat->m_dwFlags & MWF_LOG_INDENT)) + if (!(dat.m_dwFlags & MWF_LOG_INDENT)) str.AppendFormat("\\li%u\\ri%u\\fi%u\\tx%u", 2 * 15, 2 * 15, 0, 70 * 15); -} - -// mir_free() the return value -static char* CreateRTFHeader(CMsgDialog *dat) -{ - CMStringA str; - Build_RTF_Header(str, dat); return str.Detach(); } @@ -425,7 +419,212 @@ bool DbEventIsForMsgWindow(const DBEVENTINFO *dbei) return et && (et->flags & DETF_MSGWINDOW); } -static char* Template_CreateRTFFromDbEvent(CMsgDialog *dat, MCONTACT hContact, MEVENT hDbEvent, LogStreamData *streamData) +static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) +{ + LogStreamData *dat = (LogStreamData*)dwCookie; + + if (dat->buffer == nullptr) { + dat->bufferOffset = 0; + switch (dat->stage) { + case STREAMSTAGE_HEADER: + mir_free(dat->buffer); + dat->buffer = CreateRTFHeader(dat->pLog); + dat->stage = STREAMSTAGE_EVENTS; + break; + + case STREAMSTAGE_EVENTS: + if (dat->eventsToInsert) { + do { + mir_free(dat->buffer); + dat->buffer = dat->pLog->CreateRTFFromDbEvent(dat); + if (dat->buffer) + dat->hDbEventLast = dat->hDbEvent; + dat->hDbEvent = db_event_next(dat->hContact, dat->hDbEvent); + if (--dat->eventsToInsert == 0) + break; + } while (dat->buffer == nullptr && dat->hDbEvent); + + if (dat->buffer) + break; + } + dat->stage = STREAMSTAGE_TAIL; + __fallthrough; + + case STREAMSTAGE_TAIL: + mir_free(dat->buffer); + dat->buffer = CreateRTFTail(); + dat->stage = STREAMSTAGE_STOP; + break; + + case STREAMSTAGE_STOP: + *pcb = 0; + return 0; + } + dat->bufferLen = (int)mir_strlen(dat->buffer); + } + *pcb = min(cb, dat->bufferLen - dat->bufferOffset); + memcpy(pbBuff, dat->buffer + dat->bufferOffset, *pcb); + dat->bufferOffset += *pcb; + if (dat->bufferOffset == dat->bufferLen) + replaceStr(dat->buffer, nullptr); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +INT_PTR CLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) +{ + bool isCtrl, isShift, isAlt; + + switch (msg) { + case WM_KILLFOCUS: + if (wParam != (WPARAM)m_rtf.GetHwnd() && 0 != wParam) { + CHARRANGE cr; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&cr); + if (cr.cpMax != cr.cpMin) { + cr.cpMin = cr.cpMax; + m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&cr); + } + } + break; + + case WM_CHAR: + m_pDlg.KbdState(isShift, isCtrl, isAlt); + if (wParam == 0x03 && isCtrl) // Ctrl+C + return m_pDlg.WMCopyHandler(msg, wParam, lParam); + if (wParam == 0x11 && isCtrl) // Ctrl+Q + m_pDlg.m_btnQuote.Click(); + break; + + case WM_LBUTTONUP: + if (m_pDlg.isChat() && g_Settings.bClickableNicks) { + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + CheckCustomLink(m_rtf.GetHwnd(), &pt, msg, wParam, lParam, TRUE); + } + + if (g_plugin.bAutoCopy) { + CHARRANGE sel; + SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel); + if (sel.cpMin != sel.cpMax) { + SendMessage(m_rtf.GetHwnd(), WM_COPY, 0, 0); + sel.cpMin = sel.cpMax; + SendMessage(m_rtf.GetHwnd(), EM_EXSETSEL, 0, (LPARAM)&sel); + SetFocus(m_pDlg.m_message.GetHwnd()); + } + } + break; + + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONUP: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + if (m_pDlg.isChat() && g_Settings.bClickableNicks) { + POINT pt = { LOWORD(lParam), HIWORD(lParam) }; + CheckCustomLink(m_rtf.GetHwnd(), &pt, msg, wParam, lParam, TRUE); + } + break; + + case WM_SETCURSOR: + if (g_Settings.bClickableNicks && m_pDlg.isChat() && (LOWORD(lParam) == HTCLIENT)) { + POINT pt; + GetCursorPos(&pt); + ScreenToClient(m_rtf.GetHwnd(), &pt); + if (CheckCustomLink(m_rtf.GetHwnd(), &pt, msg, wParam, lParam, FALSE)) + return TRUE; + } + break; + + case WM_SYSKEYUP: + if (wParam == VK_MENU) { + m_pDlg.ProcessHotkeysByMsgFilter(m_rtf, msg, wParam, lParam); + return 0; + } + break; + + case WM_SYSKEYDOWN: + m_pDlg.m_bkeyProcessed = false; + if (m_pDlg.ProcessHotkeysByMsgFilter(m_rtf, msg, wParam, lParam)) { + m_pDlg.m_bkeyProcessed = true; + return 0; + } + break; + + case WM_SYSCHAR: + if (m_pDlg.m_bkeyProcessed) { + m_pDlg.m_bkeyProcessed = false; + return 0; + } + break; + + case WM_KEYDOWN: + m_pDlg.KbdState(isShift, isCtrl, isAlt); + if (wParam == VK_INSERT && isCtrl) + return m_pDlg.WMCopyHandler(msg, wParam, lParam); + + if (wParam == 0x57 && isCtrl) { // ctrl-w (close window) + PostMessage(m_pDlg.m_hwnd, WM_CLOSE, 0, 1); + return TRUE; + } + + break; + + case WM_COPY: + return m_pDlg.WMCopyHandler(msg, wParam, lParam); + + case WM_NCCALCSIZE: + return CSkin::NcCalcRichEditFrame(m_rtf.GetHwnd(), &m_pDlg, ID_EXTBKHISTORY, msg, wParam, lParam, stubLogProc); + + case WM_NCPAINT: + return CSkin::DrawRichEditFrame(m_rtf.GetHwnd(), &m_pDlg, ID_EXTBKHISTORY, msg, wParam, lParam, stubLogProc); + + case WM_CONTEXTMENU: + if (!m_pDlg.isChat()) { + POINT pt; + if (lParam == 0xFFFFFFFF) { + CHARRANGE sel; + m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); + m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); + ClientToScreen(m_rtf.GetHwnd(), &pt); + } + else { + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + } + + m_pDlg.ShowPopupMenu(m_rtf, pt); + return TRUE; + } + } + + return CSuper::WndProc(msg, wParam, lParam); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CLogWindow::AppendUnicodeString(CMStringA &str, const wchar_t *pwszBuf) +{ + AppendUnicodeToBuffer(str, pwszBuf, 0); +} + +void CLogWindow::Attach() +{ + CSuper::Attach(); + + m_rtf.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0)); + m_rtf.SendMsg(EM_AUTOURLDETECT, TRUE, 0); + m_rtf.SendMsg(EM_EXLIMITTEXT, 0, 0x7FFFFFFF); + m_rtf.SendMsg(EM_SETUNDOLIMIT, 0, 0); + m_rtf.SendMsg(EM_HIDESELECTION, TRUE, 0); + m_rtf.SendMsg(EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_LINK); + m_rtf.SendMsg(EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); + m_rtf.SendMsg(EM_SETLANGOPTIONS, 0, m_rtf.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOFONTSIZEADJUST); + m_rtf.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +char* CLogWindow::CreateRTFFromDbEvent(LogStreamData *streamData) { HANDLE hTimeZone = nullptr; struct tm event_time = { 0 }; @@ -437,19 +636,20 @@ static char* Template_CreateRTFFromDbEvent(CMsgDialog *dat, MCONTACT hContact, M memcpy(&dbei, streamData->dbei, sizeof(DBEVENTINFO)); else { dbei.cbBlob = -1; - db_event_get(hDbEvent, &dbei); + db_event_get(streamData->hDbEvent, &dbei); if (!DbEventIsShown(&dbei)) return nullptr; } + auto *dat = &m_pDlg; if (dbei.eventType == EVENTTYPE_MESSAGE && !dbei.markedRead()) dat->m_cache->updateStats(TSessionStats::SET_LAST_RCV, mir_strlen((char *)dbei.pBlob)); BOOL isSent = (dbei.flags & DBEF_SENT); BOOL bIsStatusChangeEvent = IsStatusEvent(dbei.eventType); if (!isSent && (bIsStatusChangeEvent || dbei.eventType == EVENTTYPE_MESSAGE || DbEventIsForMsgWindow(&dbei))) { - db_event_markRead(hContact, hDbEvent); - g_clistApi.pfnRemoveEvent(hContact, hDbEvent); + db_event_markRead(streamData->hContact, streamData->hDbEvent); + g_clistApi.pfnRemoveEvent(streamData->hContact, streamData->hDbEvent); } CMStringW msg(ptrW(DbEvent_GetTextW(&dbei, CP_UTF8))); @@ -549,7 +749,7 @@ static char* Template_CreateRTFFromDbEvent(CMsgDialog *dat, MCONTACT hContact, M memmove(dat->m_hHistoryEvents, &dat->m_hHistoryEvents[1], sizeof(HANDLE) * (dat->m_maxHistory - 1)); dat->m_curHistory--; } - dat->m_hHistoryEvents[dat->m_curHistory++] = hDbEvent; + dat->m_hHistoryEvents[dat->m_curHistory++] = streamData->hDbEvent; } str.Append("\\ul0\\b0\\i0\\v0 "); @@ -851,8 +1051,13 @@ static char* Template_CreateRTFFromDbEvent(CMsgDialog *dat, MCONTACT hContact, M str.Append(GetRTFFont(iFontIDOffset + (isSent ? MSGFONTID_MYMISC : MSGFONTID_YOURMISC))); str.AppendChar(' '); } - - AppendUnicodeToBuffer(str, ptrW(DbEvent_GetTextW(&dbei, CP_ACP)), 0); + { + DB::FILE_BLOB blob(dbei); + if (blob.isOffline()) + InsertFileLink(str, streamData->hDbEvent, blob); + else + AppendUnicodeToBuffer(str, ptrW(DbEvent_GetTextW(&dbei, CP_ACP)), 0); + } break; default: @@ -981,7 +1186,7 @@ skip: } if (dat->m_hHistoryEvents) - str.AppendFormat(dat->m_szMicroLf, MSGDLGFONTCOUNT + 1 + ((isSent) ? 1 : 0), hDbEvent); + str.AppendFormat(dat->m_szMicroLf, MSGDLGFONTCOUNT + 1 + ((isSent) ? 1 : 0), streamData->hDbEvent); str.Append("\\par"); @@ -993,204 +1198,8 @@ skip: return str.Detach(); } -static DWORD CALLBACK LogStreamInEvents(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) -{ - LogStreamData *dat = (LogStreamData*)dwCookie; - - if (dat->buffer == nullptr) { - dat->bufferOffset = 0; - switch (dat->stage) { - case STREAMSTAGE_HEADER: - mir_free(dat->buffer); - dat->buffer = CreateRTFHeader(dat->dlgDat); - dat->stage = STREAMSTAGE_EVENTS; - break; - - case STREAMSTAGE_EVENTS: - if (dat->eventsToInsert) { - do { - mir_free(dat->buffer); - dat->buffer = Template_CreateRTFFromDbEvent(dat->dlgDat, dat->hContact, dat->hDbEvent, dat); - if (dat->buffer) - dat->hDbEventLast = dat->hDbEvent; - dat->hDbEvent = db_event_next(dat->hContact, dat->hDbEvent); - if (--dat->eventsToInsert == 0) - break; - } while (dat->buffer == nullptr && dat->hDbEvent); - - if (dat->buffer) - break; - } - dat->stage = STREAMSTAGE_TAIL; - __fallthrough; - - case STREAMSTAGE_TAIL: - mir_free(dat->buffer); - dat->buffer = CreateRTFTail(); - dat->stage = STREAMSTAGE_STOP; - break; - - case STREAMSTAGE_STOP: - *pcb = 0; - return 0; - } - dat->bufferLen = (int)mir_strlen(dat->buffer); - } - *pcb = min(cb, dat->bufferLen - dat->bufferOffset); - memcpy(pbBuff, dat->buffer + dat->bufferOffset, *pcb); - dat->bufferOffset += *pcb; - if (dat->bufferOffset == dat->bufferLen) - replaceStr(dat->buffer, nullptr); - return 0; -} - ///////////////////////////////////////////////////////////////////////////////////////// -INT_PTR CLogWindow::WndProc(UINT msg, WPARAM wParam, LPARAM lParam) -{ - bool isCtrl, isShift, isAlt; - - switch (msg) { - case WM_KILLFOCUS: - if (wParam != (WPARAM)m_rtf.GetHwnd() && 0 != wParam) { - CHARRANGE cr; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&cr); - if (cr.cpMax != cr.cpMin) { - cr.cpMin = cr.cpMax; - m_rtf.SendMsg(EM_EXSETSEL, 0, (LPARAM)&cr); - } - } - break; - - case WM_CHAR: - m_pDlg.KbdState(isShift, isCtrl, isAlt); - if (wParam == 0x03 && isCtrl) // Ctrl+C - return m_pDlg.WMCopyHandler(msg, wParam, lParam); - if (wParam == 0x11 && isCtrl) // Ctrl+Q - m_pDlg.m_btnQuote.Click(); - break; - - case WM_LBUTTONUP: - if (m_pDlg.isChat() && g_Settings.bClickableNicks) { - POINT pt = { LOWORD(lParam), HIWORD(lParam) }; - CheckCustomLink(m_rtf.GetHwnd(), &pt, msg, wParam, lParam, TRUE); - } - - if (g_plugin.bAutoCopy) { - CHARRANGE sel; - SendMessage(m_rtf.GetHwnd(), EM_EXGETSEL, 0, (LPARAM)&sel); - if (sel.cpMin != sel.cpMax) { - SendMessage(m_rtf.GetHwnd(), WM_COPY, 0, 0); - sel.cpMin = sel.cpMax; - SendMessage(m_rtf.GetHwnd(), EM_EXSETSEL, 0, (LPARAM)&sel); - SetFocus(m_pDlg.m_message.GetHwnd()); - } - } - break; - - case WM_LBUTTONDOWN: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONDBLCLK: - if (m_pDlg.isChat() && g_Settings.bClickableNicks) { - POINT pt = { LOWORD(lParam), HIWORD(lParam) }; - CheckCustomLink(m_rtf.GetHwnd(), &pt, msg, wParam, lParam, TRUE); - } - break; - - case WM_SETCURSOR: - if (g_Settings.bClickableNicks && m_pDlg.isChat() && (LOWORD(lParam) == HTCLIENT)) { - POINT pt; - GetCursorPos(&pt); - ScreenToClient(m_rtf.GetHwnd(), &pt); - if (CheckCustomLink(m_rtf.GetHwnd(), &pt, msg, wParam, lParam, FALSE)) - return TRUE; - } - break; - - case WM_SYSKEYUP: - if (wParam == VK_MENU) { - m_pDlg.ProcessHotkeysByMsgFilter(m_rtf, msg, wParam, lParam); - return 0; - } - break; - - case WM_SYSKEYDOWN: - m_pDlg.m_bkeyProcessed = false; - if (m_pDlg.ProcessHotkeysByMsgFilter(m_rtf, msg, wParam, lParam)) { - m_pDlg.m_bkeyProcessed = true; - return 0; - } - break; - - case WM_SYSCHAR: - if (m_pDlg.m_bkeyProcessed) { - m_pDlg.m_bkeyProcessed = false; - return 0; - } - break; - - case WM_KEYDOWN: - m_pDlg.KbdState(isShift, isCtrl, isAlt); - if (wParam == VK_INSERT && isCtrl) - return m_pDlg.WMCopyHandler(msg, wParam, lParam); - - if (wParam == 0x57 && isCtrl) { // ctrl-w (close window) - PostMessage(m_pDlg.m_hwnd, WM_CLOSE, 0, 1); - return TRUE; - } - - break; - - case WM_COPY: - return m_pDlg.WMCopyHandler(msg, wParam, lParam); - - case WM_NCCALCSIZE: - return CSkin::NcCalcRichEditFrame(m_rtf.GetHwnd(), &m_pDlg, ID_EXTBKHISTORY, msg, wParam, lParam, stubLogProc); - - case WM_NCPAINT: - return CSkin::DrawRichEditFrame(m_rtf.GetHwnd(), &m_pDlg, ID_EXTBKHISTORY, msg, wParam, lParam, stubLogProc); - - case WM_CONTEXTMENU: - if (!m_pDlg.isChat()) { - POINT pt; - if (lParam == 0xFFFFFFFF) { - CHARRANGE sel; - m_rtf.SendMsg(EM_EXGETSEL, 0, (LPARAM)&sel); - m_rtf.SendMsg(EM_POSFROMCHAR, (WPARAM)&pt, (LPARAM)sel.cpMax); - ClientToScreen(m_rtf.GetHwnd(), &pt); - } - else { - pt.x = GET_X_LPARAM(lParam); - pt.y = GET_Y_LPARAM(lParam); - } - - m_pDlg.ShowPopupMenu(m_rtf, pt); - return TRUE; - } - } - - return CSuper::WndProc(msg, wParam, lParam); -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void CLogWindow::Attach() -{ - CSuper::Attach(); - - m_rtf.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0)); - m_rtf.SendMsg(EM_AUTOURLDETECT, TRUE, 0); - m_rtf.SendMsg(EM_EXLIMITTEXT, 0, 0x7FFFFFFF); - m_rtf.SendMsg(EM_SETUNDOLIMIT, 0, 0); - m_rtf.SendMsg(EM_HIDESELECTION, TRUE, 0); - m_rtf.SendMsg(EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_LINK); - m_rtf.SendMsg(EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR, SES_EXTENDBACKCOLOR); - m_rtf.SendMsg(EM_SETLANGOPTIONS, 0, m_rtf.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOFONTSIZEADJUST); - m_rtf.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3)); -} - void CLogWindow::LogEvents(MEVENT hDbEventFirst, int count, bool fAppend) { LogEvents(hDbEventFirst, count, fAppend, nullptr); @@ -1228,7 +1237,7 @@ void CLogWindow::LogEvents(MEVENT hDbEventFirst, int count, bool fAppend, DBEVEN LogStreamData streamData = { 0 }; streamData.hContact = m_pDlg.m_hContact; streamData.hDbEvent = hDbEventFirst; - streamData.dlgDat = &m_pDlg; + streamData.pLog = this; streamData.eventsToInsert = count; streamData.isEmpty = fAppend ? GetWindowTextLength(m_rtf.GetHwnd()) == 0 : 1; streamData.dbei = dbei_s; @@ -1306,6 +1315,8 @@ void CLogWindow::LogEvents(MEVENT hDbEventFirst, int count, bool fAppend, DBEVEN mir_free(streamData.buffer); } +///////////////////////////////////////////////////////////////////////////////////////// + void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) { auto *si = m_pDlg.m_si; @@ -1460,6 +1471,8 @@ void CLogWindow::LogEvents(LOGINFO *lin, bool bRedraw) } } +///////////////////////////////////////////////////////////////////////////////////////// + void CLogWindow::ReplaceIcons(LONG startAt, int fAppend, BOOL isSent) { wchar_t trbuffer[40]; @@ -1582,6 +1595,8 @@ void CLogWindow::ReplaceIcons(LONG startAt, int fAppend, BOOL isSent) } } +///////////////////////////////////////////////////////////////////////////////////////// + void CLogWindow::ScrollToBottom() { ScrollToBottom(false, false); @@ -1606,6 +1621,8 @@ void CLogWindow::ScrollToBottom(bool bImmediate, bool bRedraw) InvalidateRect(m_rtf.GetHwnd(), nullptr, FALSE); } +///////////////////////////////////////////////////////////////////////////////////////// + void CLogWindow::UpdateOptions() { COLORREF colour = m_pDlg.isChat() ? g_Settings.crLogBackground : db_get_dw(0, FONTMODULE, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR); @@ -1633,6 +1650,9 @@ void CLogWindow::UpdateOptions() } } +///////////////////////////////////////////////////////////////////////////////////////// +// Module entry point + CSrmmLogWindow *logBuilder(CMsgDialog &pDlg) { return new CLogWindow(pDlg); diff --git a/plugins/TabSRMM/src/msgs.h b/plugins/TabSRMM/src/msgs.h index 6d05b1a25f..d3bdec4c3d 100644 --- a/plugins/TabSRMM/src/msgs.h +++ b/plugins/TabSRMM/src/msgs.h @@ -370,42 +370,6 @@ struct TContainerData : public MZeroedObject } }; -///////////////////////////////////////////////////////////////////////////////////////// -// CLogWindow - built-in log window - -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 ScrollToBottom() override; - void UpdateOptions() override; - - void DisableStaticEdge() - { - SetWindowLongPtr(m_rtf.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_rtf.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE); - } - - char* GetRichTextRtf(bool bText, bool bSelection) - { - return m_rtf.GetRichTextRtf(bText, bSelection); - } - - void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend, DBEVENTINFO *dbei); - void ReplaceIcons(LONG startAt, int fAppend, BOOL isSent); - void ScrollToBottom(bool, bool); - - INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam) override; -}; - ///////////////////////////////////////////////////////////////////////////////////////// // CMsgDialog - SRMM window class @@ -711,6 +675,44 @@ public: extern LIST g_arUnreadWindows; +///////////////////////////////////////////////////////////////////////////////////////// +// CLogWindow - built-in log window + +class CLogWindow : public CRtfLogWindow +{ + typedef CRtfLogWindow CSuper; + +public: + CLogWindow(CMsgDialog &pDlg) : + CSuper(pDlg) + {} + + char *CreateRTFFromDbEvent(struct LogStreamData *streamData); + + void AppendUnicodeString(CMStringA &str, const wchar_t *pwszBuf) override; + void Attach() override; + void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend) override; + void LogEvents(struct LOGINFO *, bool) override; + void ScrollToBottom() override; + void UpdateOptions() override; + + void DisableStaticEdge() + { + SetWindowLongPtr(m_rtf.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_rtf.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE); + } + + char *GetRichTextRtf(bool bText, bool bSelection) + { + return m_rtf.GetRichTextRtf(bText, bSelection); + } + + void LogEvents(MEVENT hDbEventFirst, int count, bool bAppend, DBEVENTINFO *dbei); + void ReplaceIcons(LONG startAt, int fAppend, BOOL isSent); + void ScrollToBottom(bool, bool); + + INT_PTR WndProc(UINT msg, WPARAM wParam, LPARAM lParam) override; +}; + #define MESSAGE_WINDOW_DATA_SIZE offsetof(_MessageWindowData, hdbEventFirst); /* 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