// Append the 'chat state' to the cursor void WChatLog::ChatLog_ChatStateTextAppend(INOUT OCursor & oTextCursor) { TContact ** ppContactStop; TContact ** ppContact = m_arraypContactsComposing.PrgpGetContactsStop(OUT &ppContactStop); if (ppContact == ppContactStop) { oTextCursor.removeSelectedText(); } else { if (g_poImageComposing == NULL) g_poImageComposing = new QImage(":/ico/Pencil"); while (TRUE) { oTextCursor.insertImage(*g_poImageComposing); TContact * pContact = *ppContact++; Assert(pContact != NULL); Assert(pContact->EGetRuntimeClass() == RTI(TContact)); g_strScratchBufferStatusBar.Format(d_szu_nbsp " <b>^s</b> is typing...", pContact->ChatLog_PszGetNickname()); oTextCursor.insertHtml(g_strScratchBufferStatusBar); if (ppContact == ppContactStop) break; } // while } if (m_pContactOrGroup->EGetRuntimeClass() == RTI(TContact)) ((TContact *)m_pContactOrGroup)->ChatLogContact_AppendExtraTextToChatState(INOUT oTextCursor); Widget_ScrollToEnd(INOUT this); } // ChatLog_ChatStateTextAppend()
void WLayoutChatLog::ChatLog_ScrollToDisplayLastMessage() { #ifdef COMPILE_WITH_CHATLOG_HTML m_pwChatLog_NZ->_ScrollToDisplayLastEvent(); #else Widget_ScrollToEnd(m_pwChatLog_NZ); #endif }
void CMessageLog::AppendTextU(QRGBX coxTextColor, PSZUC pszuTextAppend, PSZAC pszTextExtra) { if (g_fMessageLogsPaused) return; #ifdef CONFIG_MESSAGE_LOG_WIN32 if (g_hwndMainWindow == NULL) { // The main window has not been created yet, so this message is very early (typically an assertion failure within a constructor or something of that sort) //Assert(g_pwMainWindow == NULL); // This pointer should also be NULL //Assert(m_hwndMessageLog == NULL); ::OutputDebugStringA((PSZAC)pszuTextAppend); ::OutputDebugStringA((PSZAC)pszTextExtra); return; } if (m_hwndMessageLog == NULL) CreateLogWindow(); if (!IsWindow(m_hwndRichEdit)) { ::OutputDebugStringA("MessageLog: Rich Edit is unavailable\n"); if (this != &g_oErrorLog) ErrorLog_AddNewMessage("MessageLog: Rich Edit is unavailable\n", pszuTextAppend); /* if (g_pwMainWindow != NULL) g_pwMainWindow->setWindowTitle(QString::fromUtf8((const char *)pszuTextAppend)); // Try to display something to the user using the caption as the "Message Log" */ return; } RichEdit_SelectionMoveToEnd(m_hwndRichEdit); int ichSel = 0; // Index of the last selected character. ::SendMessage(m_hwndRichEdit, EM_GETSEL, OUT (WPARAM)&ichSel, OUT (LPARAM)&ichSel); // The start and end of the selection should be the same, since we just called RichEdit_SelectionMoveToEnd(). if (ichSel >= d_cchLogMax) { // The log is full, so flush some of the data ::SendMessage(m_hwndRichEdit, EM_SETSEL, 0, d_cchLogMax / 4); // Remove one fourth of the log ::SendMessage(m_hwndRichEdit, EM_REPLACESEL, FALSE /* fCanUndo */, (LPARAM)L"[...]"); RichEdit_SelectionMoveToEnd(m_hwndRichEdit); } // Set the character format to the insertion point CHARFORMAT cf; InitToGarbage(OUT &cf, sizeof(cf)); cf.cbSize = sizeof(cf); cf.dwMask = CFM_COLOR | CFM_BOLD; cf.crTextColor = COX_GetRGB(coxTextColor); cf.dwEffects = COX_IsBold(coxTextColor) ? CFE_BOLD : 0; Verify( ::SendMessage(m_hwndRichEdit, EM_SETCHARFORMAT, SCF_SELECTION, IN (LPARAM)&cf) ); // If the EM_SETCHARFORMAT fails, it may be because the method EditW_EnableColoring() was not called. SETTEXTEX st = { ST_SELECTION, CP_UTF8 }; ::SendMessage(m_hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)pszuTextAppend); if (pszTextExtra != NULL) ::SendMessage(m_hwndRichEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)pszTextExtra); ::SendMessage(m_hwndRichEdit, WM_VSCROLL, SB_BOTTOM, d_zNA); // Ensure the inserted text is visible by scrolling to the bottom ::UpdateWindow(m_hwndRichEdit); // Force the window to redraw immediately #else Report(m_pwChatDebugger != NULL && "The widget should have been created!"); if (m_pwChatDebugger == NULL) CreateLogWindow(); Assert(m_pwChatDebugger != NULL); Assert(m_pwTextBrowser != NULL); if (m_pwTextBrowser != NULL) { QTextCursor oTextCursor = m_pwTextBrowser->textCursor(); oTextCursor.movePosition(QTextCursor::End); m_pwTextBrowser->setTextCursor(oTextCursor); m_pwTextBrowser->setFontWeight(COX_IsBold(coxTextColor) ? QFont::Bold : QFont::Normal); m_pwTextBrowser->setTextColor(COX_GetQRGB(coxTextColor)); m_pwTextBrowser->insertPlainText(QString::fromUtf8((char *)pszuTextAppend)); if (pszTextExtra != NULL) m_pwTextBrowser->insertPlainText(QString::fromUtf8((char *)pszTextExtra)); Widget_ScrollToEnd(m_pwTextBrowser); } #endif } // AppendTextU()
void WChatLog::ChatLog_EventsDisplay(const CArrayPtrEvents & arraypEvents, int iEventStart) { Assert(iEventStart >= 0); Assert(m_oTextBlockComposing.isValid()); // CSocketXmpp * pSocket = m_pContactOrGroup->Xmpp_PGetSocketOnlyIfReady(); OCursorSelectBlock oCursor(m_oTextBlockComposing); QTextBlock oTextBlockEvent; // Text block for each event to insert IEvent ** ppEventStop; IEvent ** ppEventFirst = arraypEvents.PrgpGetEventsStop(OUT &ppEventStop) + iEventStart; IEvent ** ppEvent = ppEventFirst; if (!m_fDisplayAllMessages) { // For performance, limit the display to the last 100 events if (ppEventStop != NULL) { IEvent ** ppEventStart = ppEventStop - 100; if (ppEventStart > ppEvent) { ppEvent = ppEventStart; QTextBlockFormat oFormat; oFormat.setAlignment(Qt::AlignHCenter); oFormat.setBackground(c_brushSilver); // Use a silver color oCursor.setBlockFormat(oFormat); g_strScratchBufferStatusBar.Format("<a href='" d_SzMakeCambrianAction(d_szCambrianAction_DisplayAllHistory) "'>Display complete history ($I messages)</a>", arraypEvents.GetSize()); oCursor.insertHtml(g_strScratchBufferStatusBar); oCursor.AppendBlockBlank(); } } } while (ppEvent < ppEventStop) { IEvent * pEvent = *ppEvent++; AssertValidEvent(pEvent); if (pEvent->m_tsEventID >= m_tsMidnightNext) { QDateTime dtl = QDateTime::fromMSecsSinceEpoch(pEvent->m_tsEventID).toLocalTime(); QDate date = dtl.date(); // Strip the time of the day m_tsMidnightNext = QDateTime(date).toMSecsSinceEpoch() + d_ts_cDays; // I am sure there is a more elegant way to strip the time from a date, however at the moment I don't have time to investigate a better solution (and this code works) QTextBlockFormat oFormatBlock; oFormatBlock.setAlignment(Qt::AlignHCenter); oFormatBlock.setBackground(c_brushSilver); oCursor.setBlockFormat(oFormatBlock); QTextCharFormat oFormatChar; // = oCursor.charFormat(); oFormatChar.setFontWeight(QFont::Bold); //oFormatChar.setFontItalic(true); //oCursor.setCharFormat(oFormatChar); oCursor.insertText(date.toString("dddd MMMM d, yyyy"), oFormatChar); oCursor.AppendBlockBlank(); } oTextBlockEvent = oCursor.block(); // Get the current block under the cursor Endorse(oTextBlockEvent.userData() == NULL); if (pEvent->m_uFlagsEvent & IEvent::FE_kfReplacing) { MessageLog_AppendTextFormatSev(eSeverityComment, "Attempting to replace Event ID $t\n", pEvent->m_tsEventID); QTextBlock oTextBlockUpdate; QTextBlock oTextBlockTemp = document()->lastBlock(); IEvent * pEventOld = pEvent; const EEventClass eEventClassUpdater = pEvent->Event_FIsEventTypeSent() ? CEventUpdaterSent::c_eEventClass : CEventUpdaterReceived::c_eEventClass; // Which updater to search for // The event is replacing an older event. This code is a bit complex because the Chat Log may not display all events and therefore we need to find the most recent block displaying the most recent event. IEvent ** ppEventStop; IEvent ** ppEventFirst = pEvent->m_pVaultParent_NZ->m_arraypaEvents.PrgpGetEventsStop(OUT &ppEventStop); IEvent ** ppEventCompare = ppEventStop; // Search the array from the end, as the event to search is likely to be a recent one while (ppEventFirst < ppEventCompare) { // Find the updater which should be right before the replacing event IEvent * pEventTemp = *--ppEventCompare; TryAgain: if (pEventTemp == pEventOld && ppEventFirst < ppEventCompare) { CEventUpdaterSent * pEventUpdater = (CEventUpdaterSent *)*--ppEventCompare; // Get the updater which is just before the event if (pEventUpdater->EGetEventClass() != eEventClassUpdater) { MessageLog_AppendTextFormatSev(eSeverityErrorWarning, "\t Missing Updater for Event ID $t ({tL}); instead found class '$U' with Event ID $t.\n", pEventTemp->m_tsEventID, pEventTemp->m_tsEventID, pEventUpdater->EGetEventClass(), pEventUpdater->m_tsEventID); pEvent->m_uFlagsEvent &= ~IEvent::FE_kfReplacing; // Remove the bit to avoid displaying the error again and again pEvent->m_pVaultParent_NZ->SetModified(); // Save the change continue; } const TIMESTAMP tsEventIdOld = pEventUpdater->m_tsEventIdOld; MessageLog_AppendTextFormatSev(eSeverityNoise, "\t [$i] Found updater: $t -> $t\n", ppEventCompare - ppEventFirst, pEventUpdater->m_tsEventIdNew, tsEventIdOld); // Now, search for the block containing the replacement event while (oTextBlockTemp.isValid()) { OTextBlockUserDataEvent * pUserData = (OTextBlockUserDataEvent *)oTextBlockTemp.userData(); if (pUserData != NULL) { TIMESTAMP_DELTA dtsEvent = (pUserData->m_pEvent->m_tsEventID - tsEventIdOld); MessageLog_AppendTextFormatCo(d_coPurple, "Comparing block Event ID $t with $t: dtsEvent = $T\n", pUserData->m_pEvent->m_tsEventID, tsEventIdOld, dtsEvent); if (dtsEvent <= 0) { if (dtsEvent == 0) { MessageLog_AppendTextFormatSev(eSeverityNoise, "\t Found matching textblock for replacement: Event ID $t -> $t\n", pEventOld->m_tsEventID, tsEventIdOld); oTextBlockUpdate = oTextBlockTemp; } break; } } oTextBlockTemp = oTextBlockTemp.previous(); } // while // Keep searching in case there are chained updated events while (ppEventFirst < ppEventCompare) { pEventTemp = *--ppEventCompare; if (pEventTemp->m_tsEventID == tsEventIdOld) { MessageLog_AppendTextFormatSev(eSeverityNoise, "\t [$i] Found chained replacement: Event ID $t -> $t\n", ppEventCompare - ppEventFirst, pEvent->m_tsEventID, tsEventIdOld); pEventTemp->m_uFlagsEvent |= IEvent::FE_kfReplaced; pEventOld = pEventTemp; goto TryAgain; } } // while } // if } // while if (oTextBlockUpdate.isValid()) { MessageLog_AppendTextFormatSev(eSeverityNoise, "\t Event ID $t is updating its old Event ID $t\n", pEvent->m_tsEventID, pEventOld->m_tsEventID); OCursorSelectBlock oCursorEventOld(oTextBlockUpdate); pEvent->ChatLogUpdateTextBlock(INOUT &oCursorEventOld); continue; } MessageLog_AppendTextFormatSev(eSeverityErrorWarning, "Event ID $t is replacing another event which cannot be found\n", pEvent->m_tsEventID); } // if (replacing) oTextBlockEvent.setUserData(PA_CHILD new OTextBlockUserDataEvent(pEvent)); // Assign an event for each text block pEvent->ChatLogUpdateTextBlock(INOUT &oCursor); if ((pEvent->m_uFlagsEvent & IEvent::FE_kfEventHidden) == 0) oCursor.AppendBlockBlank(); // If the event is visible, then add a new text block (otherwise it will reuse the same old block) else oTextBlockEvent.setUserData(NULL); // Since we are reusing the same block, delete its userdata so we may assing another OTextBlockUserDataEvent } // while m_oTextBlockComposing = oCursor.block(); ChatLog_ChatStateTextAppend(INOUT oCursor); Widget_ScrollToEnd(INOUT this); } // ChatLog_EventsDisplay()