diff options
Diffstat (limited to 'plugins/TabSRMM/chat/log.cpp')
| -rw-r--r-- | plugins/TabSRMM/chat/log.cpp | 1352 | 
1 files changed, 1352 insertions, 0 deletions
diff --git a/plugins/TabSRMM/chat/log.cpp b/plugins/TabSRMM/chat/log.cpp new file mode 100644 index 0000000000..7c167441d0 --- /dev/null +++ b/plugins/TabSRMM/chat/log.cpp @@ -0,0 +1,1352 @@ +/*
 + * astyle --force-indent=tab=4 --brackets=linux --indent-switches
 + *		  --pad=oper --one-line=keep-blocks  --unpad=paren
 + *
 + * Miranda IM: the free IM client for Microsoft* Windows*
 + *
 + * Copyright 2000-2009 Miranda ICQ/IM project,
 + * all portions of this codebase are copyrighted to the people
 + * listed in contributors.txt.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * you should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 + *
 + * part of tabSRMM messaging plugin for Miranda.
 + *
 + * This code is based on and still contains large parts of the the
 + * original chat module for Miranda IM, written and copyrighted
 + * by Joergen Persson in 2005.
 + *
 + * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
 + *
 + * $Id: log.cpp 13614 2011-04-22 13:16:21Z borkra $
 + *
 + * Implements the richedit-based message history display for the group
 + * chat window.
 + *
 + */
 +
 +#include "../src/commonheaders.h"
 +#include <math.h>
 +#include <mbstring.h>
 +#include <shlwapi.h>
 +
 +/*
 + * The code for streaming the text is to a large extent copied from
 + * the srmm module and then modified to fit the chat module.
 + */
 +
 +extern FONTINFO	aFonts[OPTIONS_FONTCOUNT];
 +extern HICON	hIcons[30];
 +
 +static PBYTE	pLogIconBmpBits[14];
 +static int		logIconBmpSize[ SIZEOF(pLogIconBmpBits)];
 +
 +static int		logPixelSY = 0;
 +static int		logPixelSX = 0;
 +static char		*szDivider = "\\strike----------------------------------------------------------------------------\\strike0";
 +static char		CHAT_rtfFontsGlobal[OPTIONS_FONTCOUNT + 2][RTFCACHELINESIZE];
 +static char		*CHAT_rtffonts = 0;
 +
 +/*
 + * ieview MUC support - mostly from scriver
 + */
 +
 +/*
 +static char* u2a( const wchar_t* src, int codepage ) {
 +	int cbLen = WideCharToMultiByte( codepage, 0, src, -1, NULL, 0, NULL, NULL );
 +	char* result = ( char* )mir_alloc( cbLen+1 );
 +	if ( result == NULL )
 +		return NULL;
 +
 +	WideCharToMultiByte( codepage, 0, src, -1, result, cbLen, NULL, NULL );
 +	result[ cbLen ] = 0;
 +	return result;
 +}
 +
 +static char* t2acp( const TCHAR* src, int codepage ) {
 +	#if defined( _UNICODE )
 +		return u2a( src, codepage );
 +	#else
 +		return mir_strdup( src );
 +	#endif
 +}
 +
 +static TCHAR *a2tcp(const char *text, int cp) {
 +	if ( text != NULL ) {
 +	#if defined ( _UNICODE )
 +		int cbLen = MultiByteToWideChar( cp, 0, text, -1, NULL, 0 );
 +		TCHAR* result = ( TCHAR* )mir_alloc( sizeof(TCHAR)*( cbLen+1 ));
 +		if ( result == NULL )
 +			return NULL;
 +		MultiByteToWideChar(cp, 0, text, -1, result, cbLen);
 +		return result;
 +	#else
 +		return mir_strdup(text);
 +	#endif
 +	}
 +	return NULL;
 +}
 +
 +static int Log_AppendIEView(LOGSTREAMDATA* streamData, BOOL simpleMode, TCHAR **buffer, int *cbBufferEnd, int *cbBufferAlloced, const TCHAR *fmt, ...)
 +{
 +	va_list va;
 +	int lineLen, textCharsCount=0;
 +	TCHAR* line = (TCHAR*)alloca( 8001 * sizeof(TCHAR));
 +	TCHAR* d;
 +	MODULEINFO *mi = MM_FindModule(streamData->si->pszModule);
 +
 +	va_start(va, fmt);
 +	lineLen = _vsntprintf( line, 8000, fmt, va);
 +	if (lineLen < 0)
 +		return 0;
 +	line[lineLen] = 0;
 +	va_end(va);
 +	lineLen = lineLen*9 + 8;
 +	if (*cbBufferEnd + lineLen > *cbBufferAlloced) {
 +		cbBufferAlloced[0] += (lineLen + 1024 - lineLen % 1024);
 +		*buffer = (TCHAR *) mir_realloc(*buffer, *cbBufferAlloced * sizeof(TCHAR));
 +	}
 +
 +	d = *buffer + *cbBufferEnd;
 +
 +	for (; *line; line++, textCharsCount++) {
 +		if (*line == '%' && !simpleMode ) {
 +			TCHAR szTemp[200];
 +
 +			szTemp[0] = '\0';
 +			switch ( *++line ) {
 +			case '\0':
 +			case '%':
 +				*d++ = '%';
 +				break;
 +
 +			case 'c':
 +			case 'f':
 +				if (!g_Settings.StripFormat && !streamData->bStripFormat) {
 +					if ( line[1] != '\0' && line[2] != '\0') {
 +						TCHAR szTemp3[3], c = *line;
 +						int col;
 +						szTemp3[0] = line[1];
 +						szTemp3[1] = line[2];
 +						szTemp3[2] = '\0';
 +						col = _ttoi(szTemp3);
 +						mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%%%c#%02X%02X%02X"), c, GetRValue(mi->crColors[col]), GetGValue(mi->crColors[col]), GetBValue(mi->crColors[col]));
 +					}
 +				}
 +				line += 2;
 +				break;
 +			case 'C':
 +			case 'F':
 +				if ( !g_Settings.StripFormat && !streamData->bStripFormat) {
 +					mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%%%c"), *line );
 +				}
 +				break;
 +			case 'b':
 +			case 'u':
 +			case 'i':
 +			case 'B':
 +			case 'U':
 +			case 'I':
 +			case 'r':
 +				if ( !streamData->bStripFormat ) {
 +					mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%%%c"), *line );
 +				}
 +				break;
 +			}
 +
 +			if ( szTemp[0] ) {
 +				size_t iLen = lstrlen(szTemp);
 +				memcpy( d, szTemp, iLen * sizeof(TCHAR));
 +				d += iLen;
 +			}
 +		}
 +		else if (*line == '%') {
 +			*d++ = '%';
 +			*d++ = (char) *line;
 +		}
 +		else {
 +			*d++ = (TCHAR) *line;
 +		}
 +	}
 +	*d = '\0';
 +	*cbBufferEnd = (int) (d - *buffer);
 +	return textCharsCount;
 +}
 +
 +static void AddEventTextToBufferIEView(TCHAR **buffer, int *bufferEnd, int *bufferAlloced, LOGSTREAMDATA *streamData)
 +{
 +	if (streamData->lin->ptszText)
 +		Log_AppendIEView(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText);
 +}
 +
 +static void AddEventToBufferIEView(TCHAR **buffer, int *bufferEnd, int *bufferAlloced, LOGSTREAMDATA *streamData, TCHAR *pszNick)
 +{
 +
 + 	if ( streamData && streamData->lin ) {
 +		switch ( streamData->lin->iType ) {
 +		case GC_EVENT_MESSAGE:
 +			if ( streamData->lin->ptszText ) {
 +				TCHAR *ptszTemp = NULL;
 +				TCHAR *ptszText = streamData->lin->ptszText;
 +		#if defined( _UNICODE )
 +				if (streamData->dat->codePage != CP_ACP) {
 +					char *aText = t2acp(streamData->lin->ptszText, CP_ACP);
 +					ptszText = ptszTemp = a2tcp(aText, streamData->dat->codePage);
 +					mir_free(aText);
 +				}
 +		#endif
 +				Log_AppendIEView( streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), ptszText );
 +				mir_free(ptszTemp);
 +			}
 +			break;
 +		case GC_EVENT_ACTION:
 +			if ( pszNick && streamData->lin->ptszText) {
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, _T("%s "), streamData->lin->ptszNick);
 +				Log_AppendIEView(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText);
 +			}
 +			break;
 +		case GC_EVENT_JOIN:
 +			if (pszNick) {
 +				if (!streamData->lin->bIsMe)
 +				 	Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s has joined"), pszNick);
 +				else
 +					Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("You have joined %s"), streamData->si->ptszName);
 +			}
 +			break;
 +		case GC_EVENT_PART:
 +			if (pszNick)
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s has left"), pszNick);
 +			AddEventTextToBufferIEView(buffer, bufferEnd, bufferAlloced, streamData);
 +			break;
 +		case GC_EVENT_QUIT:
 +			if (pszNick)
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s has disconnected"), pszNick);
 +			AddEventTextToBufferIEView(buffer, bufferEnd, bufferAlloced, streamData);
 +			break;
 +		case GC_EVENT_NICK:
 +			if (pszNick && streamData->lin->ptszText) {
 +				if (!streamData->lin->bIsMe)
 +					Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s is now known as %s"), pszNick, streamData->lin->ptszText);
 +				else
 +					Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("You are now known as %s"), streamData->lin->ptszText);
 +			}
 +			break;
 +		case GC_EVENT_KICK:
 +			if (pszNick && streamData->lin->ptszStatus)
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s kicked %s"), streamData->lin->ptszStatus, streamData->lin->ptszNick);
 +			AddEventTextToBufferIEView(buffer, bufferEnd, bufferAlloced, streamData);
 +			break;
 +		case GC_EVENT_NOTICE:
 +			if (pszNick && streamData->lin->ptszText) {
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("Notice from %s"), pszNick );
 +				AddEventTextToBufferIEView(buffer, bufferEnd, bufferAlloced, streamData);
 +			}
 +			break;
 +		case GC_EVENT_TOPIC:
 +			if (streamData->lin->ptszText)
 +				Log_AppendIEView(streamData, FALSE, buffer, bufferEnd, bufferAlloced, TranslateT("The topic is \'%s%s\'"), streamData->lin->ptszText, _T("%r"));
 +			if (pszNick)
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced,
 +					streamData->lin->ptszUserInfo ? TranslateT(" (set by %s on %s)"): TranslateT(" (set by %s)"),
 +					pszNick, streamData->lin->ptszUserInfo);
 +			break;
 +		case GC_EVENT_INFORMATION:
 +			if (streamData->lin->ptszText)
 +				Log_AppendIEView(streamData, FALSE, buffer, bufferEnd, bufferAlloced, (streamData->lin->bIsMe) ? _T("--> %s") : _T("%s"), streamData->lin->ptszText);
 +			break;
 +		case GC_EVENT_ADDSTATUS:
 +			if (pszNick && streamData->lin->ptszText && streamData->lin->ptszStatus)
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s enables \'%s\' status for %s"), streamData->lin->ptszText, streamData->lin->ptszStatus, streamData->lin->ptszNick);
 +			break;
 +		case GC_EVENT_REMOVESTATUS:
 +			if (pszNick && streamData->lin->ptszText && streamData->lin->ptszStatus)
 +				Log_AppendIEView(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s disables \'%s\' status for %s"), streamData->lin->ptszText , streamData->lin->ptszStatus, streamData->lin->ptszNick);
 +			break;
 +		}
 +	}
 +}
 +
 +static void LogEventIEView(LOGSTREAMDATA *streamData, TCHAR *ptszNick)
 +{
 +	TCHAR *buffer = NULL;
 +	int bufferEnd = 0;
 +	int bufferAlloced = 0;
 +	IEVIEWEVENTDATA ied;
 +	IEVIEWEVENT event;
 +	ZeroMemory(&event, sizeof(event));
 +	event.cbSize = sizeof(event);
 +	event.dwFlags = 0;
 +	event.hwnd = streamData->dat->hwndIEView ? streamData->dat->hwndIEView : streamData->dat->hwndHPP;
 +	event.hContact = streamData->dat->hContact;
 +	event.codepage = streamData->dat->codePage;
 +	event.pszProto = streamData->si->pszModule;
 +	event.iType = IEE_LOG_MEM_EVENTS;
 +	event.eventData = &ied;
 +	event.count = 1;
 +
 +	ZeroMemory(&ied, sizeof(ied));
 +	AddEventToBufferIEView(&buffer, &bufferEnd, &bufferAlloced, streamData, ptszNick);
 +	ied.ptszNick = ptszNick;
 +	ied.ptszText = buffer;
 +	ied.time = streamData->lin->time;
 +	ied.bIsMe = streamData->lin->bIsMe;
 +
 +	switch ( streamData->lin->iType ) {
 +		case GC_EVENT_MESSAGE:
 +			ied.iType = IEED_GC_EVENT_MESSAGE;
 +			ied.dwData = IEEDD_GC_SHOW_NICK;
 +			break;
 +		case GC_EVENT_ACTION:
 +			ied.iType = IEED_GC_EVENT_ACTION;
 +			break;
 +		case GC_EVENT_JOIN:
 +			ied.iType = IEED_GC_EVENT_JOIN;
 +			break;
 +		case GC_EVENT_PART:
 +			ied.iType = IEED_GC_EVENT_PART;
 +			break;
 +		case GC_EVENT_QUIT:
 +			ied.iType = IEED_GC_EVENT_QUIT;
 +			break;
 +		case GC_EVENT_NICK:
 +			ied.iType = IEED_GC_EVENT_NICK;
 +			break;
 +		case GC_EVENT_KICK:
 +			ied.iType = IEED_GC_EVENT_KICK;
 +			break;
 +		case GC_EVENT_NOTICE:
 +			ied.iType = IEED_GC_EVENT_NOTICE;
 +			break;
 +		case GC_EVENT_TOPIC:
 +			ied.iType = IEED_GC_EVENT_TOPIC;
 +			break;
 +		case GC_EVENT_INFORMATION:
 +			ied.iType = IEED_GC_EVENT_INFORMATION;
 +			break;
 +		case GC_EVENT_ADDSTATUS:
 +			ied.iType = IEED_GC_EVENT_ADDSTATUS;
 +			break;
 +		case GC_EVENT_REMOVESTATUS:
 +			ied.iType = IEED_GC_EVENT_REMOVESTATUS;
 +			break;
 +	}
 +	ied.dwData |= g_Settings.ShowTime ? IEEDD_GC_SHOW_TIME : 0;
 +	ied.dwData |= IEEDD_GC_SHOW_ICON;
 +#if defined( _UNICODE )
 +	ied.dwFlags = IEEDF_UNICODE_TEXT | IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT2;
 +#endif
 +	ied.next = NULL;
 +	CallService(streamData->dat->hwndIEView ? MS_IEVIEW_EVENT : MS_HPP_EG_EVENT, 0, (LPARAM)&event);
 +	mir_free(buffer);
 +}
 +
 +*/
 +
 +static int EventToIndex(LOGINFO * lin)
 +{
 +	switch (lin->iType) {
 +		case GC_EVENT_MESSAGE:
 +			if (lin->bIsMe)
 +				return 10;
 +			else
 +				return 9;
 +
 +		case GC_EVENT_JOIN:
 +			return 3;
 +		case GC_EVENT_PART:
 +			return 4;
 +		case GC_EVENT_QUIT:
 +			return 5;
 +		case GC_EVENT_NICK:
 +			return 7;
 +		case GC_EVENT_KICK:
 +			return 6;
 +		case GC_EVENT_NOTICE:
 +			return 8;
 +		case GC_EVENT_TOPIC:
 +			return 11;
 +		case GC_EVENT_INFORMATION:
 +			return 12;
 +		case GC_EVENT_ADDSTATUS:
 +			return 13;
 +		case GC_EVENT_REMOVESTATUS:
 +			return 14;
 +		case GC_EVENT_ACTION:
 +			return 15;
 +	}
 +	return 0;
 +}
 +
 +static BYTE EventToSymbol(LOGINFO *lin)
 +{
 +	switch (lin->iType) {
 +		case GC_EVENT_MESSAGE:
 +			return (lin->bIsMe) ? 0x37 : 0x38;
 +		case GC_EVENT_JOIN:
 +			return 0x34;
 +		case GC_EVENT_PART:
 +			return 0x33;
 +		case GC_EVENT_QUIT:
 +			return 0x39;
 +		case GC_EVENT_NICK:
 +			return 0x71;
 +		case GC_EVENT_KICK:
 +			return 0x72;
 +		case GC_EVENT_NOTICE:
 +			return 0x28;
 +		case GC_EVENT_INFORMATION:
 +			return 0x69;
 +		case GC_EVENT_ADDSTATUS:
 +			return 0x35;
 +		case GC_EVENT_REMOVESTATUS:
 +			return 0x36;
 +		case GC_EVENT_ACTION:
 +			return 0x60;
 +	}
 +	return 0x73;
 +}
 +
 +static int EventToIcon(LOGINFO * lin)
 +{
 +	switch (lin->iType) {
 +		case GC_EVENT_MESSAGE:
 +			if (lin->bIsMe)
 +				return ICON_MESSAGEOUT;
 +			else
 +				return ICON_MESSAGE;
 +
 +		case GC_EVENT_JOIN:
 +			return ICON_JOIN;
 +		case GC_EVENT_PART:
 +			return ICON_PART;
 +		case GC_EVENT_QUIT:
 +			return ICON_QUIT;
 +		case GC_EVENT_NICK:
 +			return ICON_NICK;
 +		case GC_EVENT_KICK:
 +			return ICON_KICK;
 +		case GC_EVENT_NOTICE:
 +			return ICON_NOTICE;
 +		case GC_EVENT_TOPIC:
 +			return ICON_TOPIC;
 +		case GC_EVENT_INFORMATION:
 +			return ICON_INFO;
 +		case GC_EVENT_ADDSTATUS:
 +			return ICON_ADDSTATUS;
 +		case GC_EVENT_REMOVESTATUS:
 +			return ICON_REMSTATUS;
 +		case GC_EVENT_ACTION:
 +			return ICON_ACTION;
 +	}
 +	return 0;
 +}
 +
 +/* replace pattern `ptrn' with the string `rplc' in string `src' points to */
 +static TCHAR * _tcsrplc(TCHAR **src, const TCHAR *ptrn, const TCHAR *rplc)
 +{
 +	size_t lSrc, lPtrn, lRplc;
 +	TCHAR *tszFound, *tszTail;
 +
 +	lSrc = lstrlen(*src);
 +	lPtrn = lstrlen(ptrn);
 +	lRplc = lstrlen(rplc);
 +	if (lPtrn && lSrc && lSrc >= lPtrn && (tszFound = _tcsstr(*src, ptrn)) != NULL) {
 +		if (lRplc > lPtrn)
 +			*src = (TCHAR *) realloc((void *) * src,
 +									 sizeof(TCHAR) * (lSrc + lRplc - lPtrn + 1));
 +		if (tszTail = (TCHAR *) malloc(sizeof(TCHAR) *
 +									   (lSrc - (tszFound - *src) - lPtrn + 1))) {
 +			/* save tail */
 +			_tcscpy(tszTail, tszFound + lPtrn);
 +			/* write replacement string */
 +			_tcscpy(tszFound, rplc);
 +			/* write tail */
 +			_tcscpy(tszFound + lRplc, tszTail);
 +			free((void *) tszTail);
 +		}
 +	}
 +	return *src;
 +}
 +
 +/*
 + * replace pattern `ptrn' with the string `rplc' in string `src',
 + * `src' is supposed to be `n' character long (or no checking is done if n < 0).
 + * This function is useful for statically allocated buffers
 + */
 +static TCHAR * _tcsnrplc(TCHAR *src, size_t n, const TCHAR *ptrn, const TCHAR *rplc)
 +{
 +	size_t lSrc, lPtrn, lRplc;
 +	TCHAR *tszFound, *tszTail;
 +
 +	lSrc = lstrlen(src);
 +	lPtrn = lstrlen(ptrn);
 +	lRplc = lstrlen(rplc);
 +	if (lPtrn && lSrc && lSrc >= lPtrn && /* lengths are ok */
 +			(tszFound = _tcsstr(src, ptrn)) != NULL && /* pattern was found in string */
 +			(n < 0 || lSrc - lPtrn + lRplc < n) && /* there is enough room in the string */
 +			(tszTail = (TCHAR *) malloc(sizeof(TCHAR) *
 +										(lSrc - (tszFound - src) - lPtrn + 1))) != NULL) {
 +		/* save tail */
 +		_tcscpy(tszTail, tszFound + lPtrn);
 +		/* write replacement string */
 +		_tcscpy(tszFound, rplc);
 +		/* write tail */
 +		_tcscpy(tszFound + lRplc, tszTail);
 +		free((void *) tszTail);
 +	}
 +	return src;
 +}
 +
 +static char *Log_SetStyle(int style, int fontindex)
 +{
 +	if (style < OPTIONS_FONTCOUNT)
 +		return CHAT_rtffonts + (style * RTFCACHELINESIZE);
 +
 +	return "";
 +}
 +
 +static void Log_Append(char **buffer, int *cbBufferEnd, int *cbBufferAlloced, const char *fmt, ...)
 +{
 +	va_list va;
 +	int charsDone = 0;
 +
 +	va_start(va, fmt);
 +	for (;;) {
 +		charsDone = mir_vsnprintf(*buffer + *cbBufferEnd, *cbBufferAlloced - *cbBufferEnd, fmt, va);
 +		if (charsDone >= 0)
 +			break;
 +		*cbBufferAlloced += 4096;
 +		*buffer = (char *) mir_realloc(*buffer, *cbBufferAlloced);
 +	}
 +	va_end(va);
 +	*cbBufferEnd += charsDone;
 +}
 +
 +static int Log_AppendRTF(LOGSTREAMDATA* streamData, BOOL simpleMode, char **buffer, int *cbBufferEnd, int *cbBufferAlloced, const TCHAR *fmt, ...)
 +{
 +	va_list va;
 +	int lineLen, textCharsCount = 0;
 +	TCHAR* line = (TCHAR*)alloca(8001 * sizeof(TCHAR));
 +	char* d;
 +
 +	va_start(va, fmt);
 +	lineLen = _vsntprintf(line, 8000, fmt, va);
 +	if (lineLen < 0) lineLen = 8000;
 +	line[lineLen] = 0;
 +	va_end(va);
 +
 +	lineLen = lineLen * 20 + 8;
 +	if (*cbBufferEnd + lineLen > *cbBufferAlloced) {
 +		cbBufferAlloced[0] += (lineLen + 1024 - lineLen % 1024);
 +		if ((d = (char *) mir_realloc(*buffer, *cbBufferAlloced)) == 0)
 +			return 0;
 +		*buffer = d;
 +	}
 +
 +	d = *buffer + *cbBufferEnd;
 +
 +	for (; *line; line++, textCharsCount++) {
 +		if (*line == '\r' && line[1] == '\n') {
 +			CopyMemory(d, "\\par ", 5);
 +			line++;
 +			d += 5;
 +		} else if (*line == '\n') {
 +			CopyMemory(d, "\\line ", 6);
 +			d += 6;
 +		} else if (*line == '%' && !simpleMode) {
 +			char szTemp[200];
 +
 +			szTemp[0] = '\0';
 +			switch (*++line) {
 +				case '\0':
 +				case '%':
 +					*d++ = '%';
 +					break;
 +
 +				case 'c':
 +				case 'f':
 +					if (g_Settings.StripFormat || streamData->bStripFormat)
 +						line += 2;
 +
 +					else if (line[1] != '\0' && line[2] != '\0') {
 +						TCHAR szTemp3[3], c = *line;
 +						int col;
 +						szTemp3[0] = line[1];
 +						szTemp3[1] = line[2];
 +						szTemp3[2] = '\0';
 +						line += 2;
 +
 +						col = _ttoi(szTemp3);
 +						col += (OPTIONS_FONTCOUNT + 1);
 +						mir_snprintf(szTemp, SIZEOF(szTemp), (c == 'c') ? "\\cf%u " : "\\highlight%u ", col);
 +					}
 +					break;
 +				case 'C':
 +				case 'F':
 +					if (!g_Settings.StripFormat && !streamData->bStripFormat) {
 +						int j = streamData->lin->bIsHighlighted ? 16 : EventToIndex(streamData->lin);
 +						if (*line == 'C')
 +							mir_snprintf(szTemp, SIZEOF(szTemp), "\\cf%u ", j + 1);
 +						else
 +							mir_snprintf(szTemp, SIZEOF(szTemp), "\\highlight0 ");
 +					}
 +					break;
 +				case 'b':
 +				case 'u':
 +				case 'i':
 +					if (!streamData->bStripFormat)
 +						mir_snprintf(szTemp, SIZEOF(szTemp), (*line == 'u') ? "\\%cl " : "\\%c ", *line);
 +					break;
 +
 +				case 'B':
 +				case 'U':
 +				case 'I':
 +					if (!streamData->bStripFormat) {
 +						mir_snprintf(szTemp, SIZEOF(szTemp), (*line == 'U') ? "\\%cl0 " : "\\%c0 ", *line);
 +						CharLowerA(szTemp);
 +					}
 +					break;
 +
 +				case 'r':
 +					if (!streamData->bStripFormat) {
 +						int index = EventToIndex(streamData->lin);
 +						mir_snprintf(szTemp, SIZEOF(szTemp), "%s ", Log_SetStyle(index, index));
 +					}
 +					break;
 +			}
 +
 +			if (szTemp[0]) {
 +				int iLen = lstrlenA(szTemp);
 +				memcpy(d, szTemp, iLen);
 +				d += iLen;
 +			}
 +		} else if (*line == '\t' && !streamData->bStripFormat) {
 +			CopyMemory(d, "\\tab ", 5);
 +			d += 5;
 +		} else if ((*line == '\\' || *line == '{' || *line == '}') && !streamData->bStripFormat) {
 +			*d++ = '\\';
 +			*d++ = (char) * line;
 +		} else if (*line > 0 && *line < 128) {
 +			*d++ = (char) * line;
 +		}
 +		else d += sprintf(d, "\\u%u ?", (WORD) * line);
 +	}
 +
 +	*cbBufferEnd = (int)(d - *buffer);
 +	return textCharsCount;
 +}
 +
 +static void AddEventToBuffer(char **buffer, int *bufferEnd, int *bufferAlloced, LOGSTREAMDATA *streamData)
 +{
 +	TCHAR szTemp[512], szTemp2[512];
 +	TCHAR* pszNick = NULL;
 +
 +	if (streamData == NULL)
 +		return;
 +
 +	if (streamData->lin == NULL)
 +		return;
 +
 +	if (streamData->lin->ptszNick) {
 +		if (g_Settings.LogLimitNames && lstrlen(streamData->lin->ptszNick) > 20) {
 +			lstrcpyn(szTemp, streamData->lin->ptszNick, 20);
 +			lstrcpyn(szTemp + 20, _T("..."), 4);
 +		} else lstrcpyn(szTemp, streamData->lin->ptszNick, 511);
 +
 +		if (g_Settings.ClickableNicks)
 +			mir_sntprintf(szTemp2, SIZEOF(szTemp2), _T("~~++#%s#++~~"), szTemp);
 +		else
 +			_tcscpy(szTemp2, szTemp);
 +
 +		if (streamData->lin->ptszUserInfo && streamData->lin->iType != GC_EVENT_TOPIC)
 +			mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%s (%s)"), szTemp2, streamData->lin->ptszUserInfo);
 +		else
 +			mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%s"), szTemp2);
 +		pszNick = szTemp;
 +	}
 +
 +	switch (streamData->lin->iType) {
 +		case GC_EVENT_MESSAGE:
 +			if (streamData->lin->ptszText)
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText);
 +			break;
 +		case GC_EVENT_ACTION:
 +			if (streamData->lin->ptszNick && streamData->lin->ptszText) {
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, _T("%s "), streamData->lin->ptszNick);
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText);
 +			}
 +			break;
 +		case GC_EVENT_JOIN:
 +			if (pszNick) {
 +				if (!streamData->lin->bIsMe)
 +					/* replace nick of a newcomer with a link */
 +					Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_JOINED), pszNick);
 +				else
 +					Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_ME_JOINED), streamData->si->ptszName);
 +			}
 +			break;
 +		case GC_EVENT_PART:
 +			if (pszNick)
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_LEFT), pszNick);
 +			if (streamData->lin->ptszText)
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText);
 +			break;
 +		case GC_EVENT_QUIT:
 +			if (pszNick)
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_DISC), pszNick);
 +			if (streamData->lin->ptszText)
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText);
 +			break;
 +		case GC_EVENT_NICK:
 +			if (pszNick && streamData->lin->ptszText) {
 +				if (!streamData->lin->bIsMe)
 +					Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_NICKCHANGE), pszNick, streamData->lin->ptszText);
 +				else
 +					Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_ME_NICKCHANGE), streamData->lin->ptszText);
 +			}
 +			break;
 +		case GC_EVENT_KICK:
 +			if (pszNick && streamData->lin->ptszStatus)
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced,
 +							  CTranslator::get(CTranslator::MUC_LOG_KICK), streamData->lin->ptszStatus, pszNick);
 +
 +			if (streamData->lin->ptszText)
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText);
 +			break;
 +		case GC_EVENT_NOTICE:
 +			if (pszNick && streamData->lin->ptszText) {
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_NOTICE), pszNick);
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText);
 +			}
 +			break;
 +		case GC_EVENT_TOPIC:
 +			if (streamData->lin->ptszText)
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, CTranslator::get(CTranslator::MUC_LOG_TOPICIS), streamData->lin->ptszText, _T("%r"));
 +			if (pszNick)
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced,
 +							  (streamData->lin->ptszUserInfo) ? CTranslator::get(CTranslator::MUC_LOG_TOPICSETBYON) :
 +							  CTranslator::get(CTranslator::MUC_LOG_TOPICSETBY),
 +							  pszNick, streamData->lin->ptszUserInfo);
 +			break;
 +		case GC_EVENT_INFORMATION:
 +			if (streamData->lin->ptszText)
 +				Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, (streamData->lin->bIsMe) ? _T("--> %s") : _T("%s"), streamData->lin->ptszText);
 +			break;
 +		case GC_EVENT_ADDSTATUS:
 +			if (pszNick && streamData->lin->ptszText && streamData->lin->ptszStatus)
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced,
 +							  CTranslator::get(CTranslator::MUC_LOG_STATUSENABLE),
 +							  streamData->lin->ptszText, streamData->lin->ptszStatus, pszNick);
 +			break;
 +		case GC_EVENT_REMOVESTATUS:
 +			if (pszNick && streamData->lin->ptszText && streamData->lin->ptszStatus) {
 +				Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced,
 +							  CTranslator::get(CTranslator::MUC_LOG_STATUSDISABLE),
 +							  streamData->lin->ptszText , streamData->lin->ptszStatus, pszNick);
 +			}
 +			break;
 +	}
 +}
 +
 +TCHAR* MakeTimeStamp(TCHAR* pszStamp, time_t time)
 +{
 +	static TCHAR szTime[30];
 +	_tcsftime(szTime, 29, pszStamp, localtime(&time));
 +	return szTime;
 +}
 +
 +static char* Log_CreateRTF(LOGSTREAMDATA *streamData)
 +{
 +	char *buffer, *header;
 +	int bufferAlloced, bufferEnd, i, me = 0;
 +	LOGINFO * lin = streamData->lin;
 +	MODULEINFO *mi = MM_FindModule(streamData->si->pszModule);
 +
 +	// guesstimate amount of memory for the RTF
 +	bufferEnd = 0;
 +	bufferAlloced = streamData->bRedraw ? 2048 * (streamData->si->iEventCount + 2) : 2048;
 +	buffer = (char *) mir_alloc(bufferAlloced);
 +	buffer[0] = '\0';
 +
 +	// ### RTF HEADER
 +
 +	if(0 == mi->pszHeader)
 +		mi->pszHeader = Log_CreateRtfHeader(mi);
 +
 +	header = mi->pszHeader;
 +	streamData->crCount = mi->nColorCount;
 +
 +	if (header)
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, header);
 +
 +
 +	// ### RTF BODY (one iteration per event that should be streamed in)
 +	while (lin) {
 +		// filter
 +		if (streamData->si->iType != GCW_CHATROOM || !streamData->si->bFilterEnabled || (streamData->si->iLogFilterFlags&lin->iType) != 0) {
 +			if (lin->next != NULL)
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\par ");
 +
 +			if (streamData->dat->dwFlags & MWF_DIVIDERWANTED || lin->dwFlags & MWF_DIVIDERWANTED) {
 +				static char szStyle_div[128] = "\0";
 +				if (szStyle_div[0] == 0)
 +					mir_snprintf(szStyle_div, 128, "\\f%u\\cf%u\\ul0\\b%d\\i%d\\fs%u", 17, 18, 0, 0, 5);
 +
 +				lin->dwFlags |= MWF_DIVIDERWANTED;
 +				if (lin->prev || !streamData->bRedraw)
 +					Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\qc\\sl-1\\highlight%d %s ---------------------------------------------------------------------------------------\\par ", 18, szStyle_div);
 +				streamData->dat->dwFlags &= ~MWF_DIVIDERWANTED;
 +			}
 +			// create new line, and set font and color
 +			Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\ql\\sl0%s ", Log_SetStyle(0, 0));
 +			Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\v~-+%d+-~\\v0 ", lin);
 +
 +			// Insert icon
 +			if (g_Settings.LogSymbols)                // use symbols
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s %c", Log_SetStyle(17, 17), EventToSymbol(lin));
 +			else if (g_Settings.dwIconFlags) {
 +				int iIndex = lin->bIsHighlighted ? ICON_HIGHLIGHT : EventToIcon(lin);
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\f0\\fs14");
 +				while (bufferAlloced - bufferEnd < (logIconBmpSize[0] + 20))
 +					bufferAlloced += 4096;
 +				buffer = (char *) mir_realloc(buffer, bufferAlloced);
 +				CopyMemory(buffer + bufferEnd, pLogIconBmpBits[iIndex], logIconBmpSize[iIndex]);
 +				bufferEnd += logIconBmpSize[iIndex];
 +			}
 +
 +			if (g_Settings.TimeStampEventColour) {
 +				// colored timestamps
 +				static char szStyle[256];
 +				int iii;
 +				if (lin->ptszNick && lin->iType == GC_EVENT_MESSAGE) {
 +					iii = lin->bIsHighlighted ? 16 : (lin->bIsMe ? 2 : 1);
 +					mir_snprintf(szStyle, SIZEOF(szStyle), "\\f0\\cf%u\\ul0\\highlight0\\b%d\\i%d\\ul%d\\fs%u", iii + 1, aFonts[0].lf.lfWeight >= FW_BOLD ? 1 : 0,aFonts[0].lf.lfItalic,aFonts[0].lf.lfUnderline, 2 * abs(aFonts[0].lf.lfHeight) * 74 / logPixelSY);
 +					Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", szStyle);
 +				} else {
 +					iii = lin->bIsHighlighted ? 16 : EventToIndex(lin);
 +					mir_snprintf(szStyle, SIZEOF(szStyle), "\\f0\\cf%u\\ul0\\highlight0\\b%d\\i%d\\ul%d\\fs%u", iii + 1, aFonts[0].lf.lfWeight >= FW_BOLD ? 1 : 0, aFonts[0].lf.lfItalic,aFonts[0].lf.lfUnderline ,2 * abs(aFonts[0].lf.lfHeight) * 74 / logPixelSY);
 +					Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", szStyle);
 +				}
 +			} else
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", Log_SetStyle(0, 0));
 +			// insert a TAB if necessary to put the timestamp in the right position
 +			if (g_Settings.dwIconFlags)
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tab ");
 +
 +			//insert timestamp
 +			if (g_Settings.ShowTime) {
 +				TCHAR szTimeStamp[30], szOldTimeStamp[30];
 +
 +				lstrcpyn(szTimeStamp, MakeTimeStamp(g_Settings.pszTimeStamp, lin->time), 30);
 +				lstrcpyn(szOldTimeStamp, MakeTimeStamp(g_Settings.pszTimeStamp, streamData->si->LastTime), 30);
 +				if (!g_Settings.ShowTimeIfChanged || streamData->si->LastTime == 0 || lstrcmp(szTimeStamp, szOldTimeStamp)) {
 +					streamData->si->LastTime = lin->time;
 +					Log_AppendRTF(streamData, TRUE, &buffer, &bufferEnd, &bufferAlloced, _T("%s"), szTimeStamp);
 +				}
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tab ");
 +			}
 +
 +			// Insert the nick
 +			if (lin->ptszNick && lin->iType == GC_EVENT_MESSAGE) {
 +				TCHAR pszTemp[300], *p1;
 +				STATUSINFO *ti;
 +				char pszIndicator[3] = "\0\0";
 +				int  crNickIndex = 0;
 +													//mad
 +				if (g_Settings.LogClassicIndicators/*g_Settings.ClassicIndicators */||g_Settings.ColorizeNicksInLog) {
 +					USERINFO *ui = streamData->si->pUsers;
 +					while (ui) {
 +						if (!lstrcmp(ui->pszNick, lin->ptszNick)) {
 +							ti = TM_FindStatus(streamData->si->pStatuses, TM_WordToString(streamData->si->pStatuses, ui->Status));
 +							if (ti && (int)ti->hIcon < streamData->si->iStatusCount) {
 +								int id = streamData->si->iStatusCount - (int)ti->hIcon - 1;
 +								switch (id) {
 +									case 1:
 +										pszIndicator[0] = '+';
 +										crNickIndex = 2;
 +										break;
 +									case 2:
 +										pszIndicator[0] = '%';
 +										crNickIndex = 1;
 +										break;
 +									case 3:
 +										pszIndicator[0] = '@';
 +										crNickIndex = 0;
 +										break;
 +									case 4:
 +										pszIndicator[0] = '!';
 +										crNickIndex = 3;
 +										break;
 +									case 5:
 +										pszIndicator[0] = '*';
 +										crNickIndex = 4;
 +										break;
 +									default:
 +										pszIndicator[0] = 0;
 +								}
 +							}
 +							break;
 +						}
 +						ui = ui->next;
 +					}
 +				}
 +
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", Log_SetStyle(lin->bIsMe ? 2 : 1, lin->bIsMe ? 2 : 1));
 +													//MAD
 +				if (g_Settings.LogClassicIndicators /*g_Settings.ClassicIndicators*/)
 +					Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s", pszIndicator);
 +
 +				lstrcpyn(pszTemp, lin->bIsMe ? g_Settings.pszOutgoingNick : g_Settings.pszIncomingNick, 299);
 +				p1 = _tcsstr(pszTemp, _T("%n"));
 +				if (p1)
 +					p1[1] = 's';
 +
 +				if (!lin->bIsMe) {
 +					if (g_Settings.ClickableNicks) {
 +						_tcsnrplc(pszTemp, 300, _T("%s"), _T("~~++#%s#++~~"));
 +						pszTemp[299] = 0;
 +					}
 +					//Log_Append(&buffer, &bufferEnd, &bufferAlloced, "~~++#");
 +					if (g_Settings.ColorizeNicksInLog && pszIndicator[0])
 +						Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\cf%u ", OPTIONS_FONTCOUNT + streamData->crCount + crNickIndex + 1);
 +				}
 +
 +				Log_AppendRTF(streamData, TRUE, &buffer, &bufferEnd, &bufferAlloced, pszTemp, lin->ptszNick);
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, " ");
 +			}
 +
 +			// Insert the message
 +			{
 +				i = lin->bIsHighlighted ? 16 : EventToIndex(lin);
 +				Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", Log_SetStyle(i, i));
 +				streamData->lin = lin;
 +				AddEventToBuffer(&buffer, &bufferEnd, &bufferAlloced, streamData);
 +			}
 +		}
 +		lin = lin->prev;
 +	}
 +
 +	// ### RTF END
 +	if (streamData->bRedraw)
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\par}");
 +	else
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "}");
 +	return buffer;
 +}
 +
 +static DWORD CALLBACK Log_StreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb)
 +{
 +	LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA *) dwCookie;
 +
 +	if (lstrdat) {
 +		// create the RTF
 +		if (lstrdat->buffer == NULL) {
 +			lstrdat->bufferOffset = 0;
 +			lstrdat->buffer = Log_CreateRTF(lstrdat);
 +			lstrdat->bufferLen = lstrlenA(lstrdat->buffer);
 +		}
 +
 +		// give the RTF to the RE control
 +		*pcb = min(cb, lstrdat->bufferLen - lstrdat->bufferOffset);
 +		CopyMemory(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb);
 +		lstrdat->bufferOffset += *pcb;
 +
 +		// free stuff if the streaming operation is complete
 +		if (lstrdat->bufferOffset == lstrdat->bufferLen) {
 +			mir_free(lstrdat->buffer);
 +			lstrdat->buffer = NULL;
 +		}
 +	}
 +
 +	return 0;
 +}
 +
 +void Log_StreamInEvent(HWND hwndDlg,  LOGINFO* lin, SESSION_INFO* si, BOOL bRedraw, BOOL bPhaseTwo)
 +{
 +	EDITSTREAM stream;
 +	LOGSTREAMDATA streamData;
 +	CHARRANGE oldsel, sel, newsel;
 +	POINT point = {0};
 +	SCROLLINFO scroll;
 +	WPARAM wp;
 +	HWND hwndRich;
 +	TWindowData *dat = (struct TWindowData *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
 +
 +	if (hwndDlg == 0 || lin == 0 || si == 0 || dat == 0)
 +		return;
 +
 +	hwndRich = GetDlgItem(hwndDlg, IDC_CHAT_LOG);
 +	ZeroMemory(&streamData, sizeof(LOGSTREAMDATA));
 +	streamData.hwnd = hwndRich;
 +	streamData.si = si;
 +	streamData.lin = lin;
 +	streamData.bStripFormat = FALSE;
 +	streamData.dat = dat;
 +
 +	//	bPhaseTwo = bRedraw && bPhaseTwo;
 +
 +	if (bRedraw || si->iType != GCW_CHATROOM || !si->bFilterEnabled || (si->iLogFilterFlags&lin->iType) != 0) {
 +		BOOL bFlag = FALSE, fDoReplace;
 +
 +		ZeroMemory(&stream, sizeof(stream));
 +		stream.pfnCallback = Log_StreamCallback;
 +		stream.dwCookie = (DWORD_PTR) & streamData;
 +		scroll.cbSize = sizeof(SCROLLINFO);
 +		scroll.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
 +		GetScrollInfo(GetDlgItem(hwndDlg, IDC_CHAT_LOG), SB_VERT, &scroll);
 +		SendMessage(hwndRich, EM_GETSCROLLPOS, 0, (LPARAM) &point);
 +
 +		// do not scroll to bottom if there is a selection
 +		SendMessage(hwndRich, EM_EXGETSEL, 0, (LPARAM) &oldsel);
 +		if (oldsel.cpMax != oldsel.cpMin)
 +			SendMessage(hwndRich, WM_SETREDRAW, FALSE, 0);
 +
 +		//set the insertion point at the bottom
 +		sel.cpMin = sel.cpMax = GetRichTextLength(hwndRich);
 +		SendMessage(hwndRich, 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
 +		wp = bRedraw ? SF_RTF : SFF_SELECTION | SF_RTF;
 +
 +		//get the number of pixels per logical inch
 +		if (bRedraw) {
 +			HDC hdc;
 +			hdc = GetDC(NULL);
 +			logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY);
 +			logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX);
 +			ReleaseDC(NULL, hdc);
 +			SendMessage(hwndRich, WM_SETREDRAW, FALSE, 0);
 +			bFlag = TRUE;
 +			//			SetCursor(LoadCursor(NULL, IDC_ARROW));
 +		}
 +
 +		// stream in the event(s)
 +		streamData.lin = lin;
 +		streamData.bRedraw = bRedraw;
 +		SendMessage(hwndRich, EM_STREAMIN, wp, (LPARAM) & stream);
 +
 +
 +		//SendMessage(hwndRich, EM_EXGETSEL, (WPARAM)0, (LPARAM)&newsel);
 +		/*
 +		* for new added events, only replace in message or action events.
 +		* no need to replace smileys or math formulas elsewhere
 +		*/
 +
 +		fDoReplace = (bRedraw || (lin->ptszText
 +								  && (lin->iType == GC_EVENT_MESSAGE || lin->iType == GC_EVENT_ACTION)));
 +
 +
 +		/*
 +		 * use mathmod to replace formulas
 +		 */
 +		if (g_Settings.MathMod && fDoReplace) {
 +			TMathRicheditInfo mathReplaceInfo;
 +			CHARRANGE mathNewSel;
 +			mathNewSel.cpMin = sel.cpMin;
 +
 +			if (mathNewSel.cpMin < 0)
 +				mathNewSel.cpMin = 0;
 +
 +			mathNewSel.cpMax = -1;
 +
 +			mathReplaceInfo.hwndRichEditControl = hwndRich;
 +
 +			if (!bRedraw)
 +				mathReplaceInfo.sel = &mathNewSel;
 +			else
 +				mathReplaceInfo.sel = 0;
 +
 +			mathReplaceInfo.disableredraw = TRUE;
 +			CallService(MATH_RTF_REPLACE_FORMULAE, 0, (LPARAM)&mathReplaceInfo);
 +			bFlag = TRUE;
 +		}
 +
 +		/*
 +		 * replace marked nicknames with hyperlinks to make the nicks
 +		 * clickable
 +		 */
 +
 +		if (g_Settings.ClickableNicks) {
 +			CHARFORMAT2 cf2;
 +			FINDTEXTEX fi, fi2;
 +
 +			ZeroMemory(&cf2, sizeof(CHARFORMAT2));
 +			fi2.lpstrText = _T("#++~~");
 +			fi.chrg.cpMin = bRedraw ? 0 : sel.cpMin;
 +			fi.chrg.cpMax = -1;
 +			fi.lpstrText = _T("~~++#");
 +			cf2.cbSize = sizeof(cf2);
 +
 +			while (SendMessage(hwndRich, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) > -1) {
 +				fi2.chrg.cpMin = fi.chrgText.cpMin;
 +				fi2.chrg.cpMax = -1;
 +
 +				if (SendMessage(hwndRich, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi2) > -1) {
 +
 +					SendMessage(hwndRich, EM_EXSETSEL, 0, (LPARAM)&fi.chrgText);
 +					SendMessage(hwndRich, EM_REPLACESEL, TRUE, (LPARAM)_T(""));
 +					fi2.chrgText.cpMin -= fi.chrgText.cpMax - fi.chrgText.cpMin;
 +					fi2.chrgText.cpMax -= fi.chrgText.cpMax - fi.chrgText.cpMin;
 +					SendMessage(hwndRich, EM_EXSETSEL, 0, (LPARAM)&fi2.chrgText);
 +					SendMessage(hwndRich, EM_REPLACESEL, TRUE, (LPARAM)_T(""));
 +					fi2.chrgText.cpMax = fi2.chrgText.cpMin;
 +
 +					fi2.chrgText.cpMin = fi.chrgText.cpMin;
 +					SendMessage(hwndRich, EM_EXSETSEL, 0, (LPARAM)&fi2.chrgText);
 +					cf2.dwMask = CFM_PROTECTED;
 +					cf2.dwEffects = CFE_PROTECTED;
 +					SendMessage(hwndRich, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
 +				}
 +				fi.chrg.cpMin = fi.chrgText.cpMax;
 +			}
 +			SendMessage(hwndRich, EM_SETSEL, -1, -1);
 +		}
 +
 +
 +		/*
 +		* run smileyadd
 +		*/
 +		if (PluginConfig.g_SmileyAddAvail && fDoReplace) {
 +			SMADD_RICHEDIT3 sm = {0};
 +
 +			newsel.cpMax = -1;
 +			newsel.cpMin = sel.cpMin;
 +			if (newsel.cpMin < 0)
 +				newsel.cpMin = 0;
 +			ZeroMemory(&sm, sizeof(sm));
 +			sm.cbSize = sizeof(sm);
 +			sm.hwndRichEditControl = hwndRich;
 +			sm.Protocolname = si->pszModule;
 +			sm.rangeToReplace = bRedraw ? NULL : &newsel;
 +			sm.disableRedraw = TRUE;
 +			sm.hContact = si->hContact;
 +			CallService(MS_SMILEYADD_REPLACESMILEYS, 0, (LPARAM)&sm);
 +			}
 +
 +		/*
 +		* trim the message log to the number of most recent events
 +		* this uses hidden marks in the rich text to find the events which should be deleted
 +		*/
 +
 +
 +		if (si->wasTrimmed) {
 +			TCHAR szPattern[50];
 +			FINDTEXTEX fi;
 +
 +			mir_sntprintf(szPattern, SIZEOF(szPattern), _T("~-+%d+-~"), si->pLogEnd);
 +			fi.lpstrText = szPattern;
 +			fi.chrg.cpMin = 0;
 +			fi.chrg.cpMax = -1;
 +			if (SendMessage(hwndRich, EM_FINDTEXTEX, FR_DOWN, (LPARAM)&fi) != 0) {
 +				CHARRANGE sel;
 +				sel.cpMin = 0;
 +				sel.cpMax = 20;
 +				SendMessage(hwndRich, EM_SETSEL, 0, fi.chrgText.cpMax + 1);
 +				SendMessage(hwndRich, EM_REPLACESEL, TRUE, (LPARAM)_T(""));
 +			}
 +			si->wasTrimmed = FALSE;
 +		}
 +
 +		// 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))
 +			SendMessage(GetParent(hwndRich), GC_SCROLLTOBOTTOM, 0, 0);
 +		else
 +			SendMessage(hwndRich, EM_SETSCROLLPOS, 0, (LPARAM) &point);
 +
 +		// do we need to restore the selection
 +		if (oldsel.cpMax != oldsel.cpMin) {
 +			SendMessage(hwndRich, EM_EXSETSEL, 0, (LPARAM) & oldsel);
 +			SendMessage(hwndRich, WM_SETREDRAW, TRUE, 0);
 +			InvalidateRect(hwndRich, NULL, TRUE);
 +		}
 +
 +		// need to invalidate the window
 +		if (bFlag) {
 +			sel.cpMin = sel.cpMax = GetRichTextLength(hwndRich);
 +			SendMessage(hwndRich, EM_EXSETSEL, 0, (LPARAM) & sel);
 +			SendMessage(hwndRich, WM_SETREDRAW, TRUE, 0);
 +			InvalidateRect(hwndRich, NULL, TRUE);
 +		}
 +	}
 +}
 +
 +char * Log_CreateRtfHeader(MODULEINFO * mi)
 +{
 +	char *buffer;
 +	int bufferAlloced, bufferEnd, i = 0;
 +
 +	// guesstimate amount of memory for the RTF header
 +	bufferEnd = 0;
 +	bufferAlloced = 4096;
 +	buffer = (char *) mir_realloc(mi->pszHeader, bufferAlloced);
 +	buffer[0] = '\0';
 +
 +
 +	//get the number of pixels per logical inch
 +	if (logPixelSY == 0) {
 +		HDC hdc;
 +		hdc = GetDC(NULL);
 +		logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY);
 +		logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX);
 +		ReleaseDC(NULL, hdc);
 +	}
 +
 +	// ### RTF HEADER
 +
 +	// font table
 +	Log_Append(&buffer, &bufferEnd, &bufferAlloced, "{\\rtf1\\ansi\\deff0{\\fonttbl");
 +	for (i = 0; i < OPTIONS_FONTCOUNT ; i++)
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "{\\f%u\\fnil\\fcharset%u" TCHAR_STR_PARAM ";}", i, aFonts[i].lf.lfCharSet, aFonts[i].lf.lfFaceName);
 +
 +	// colour table
 +	Log_Append(&buffer, &bufferEnd, &bufferAlloced, "}{\\colortbl ;");
 +
 +	for (i = 0; i < OPTIONS_FONTCOUNT; i++)
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(aFonts[i].color), GetGValue(aFonts[i].color), GetBValue(aFonts[i].color));
 +
 +	for (i = 0; i < mi->nColorCount; i++)
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(mi->crColors[i]), GetGValue(mi->crColors[i]), GetBValue(mi->crColors[i]));
 +
 +	for (i = 0; i < 5; i++)
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(g_Settings.nickColors[i]), GetGValue(g_Settings.nickColors[i]), GetBValue(g_Settings.nickColors[i]));
 +
 +	// new paragraph
 +	Log_Append(&buffer, &bufferEnd, &bufferAlloced, "}\\pard\\sl%d", 1000);
 +
 +	// set tabs and indents
 +	{
 +		int iIndent = 0;
 +
 +		if (g_Settings.LogSymbols) {
 +			TCHAR szString[2];
 +			LOGFONT lf;
 +			HFONT hFont;
 +			int iText;
 +
 +			szString[1] = 0;
 +			szString[0] = 0x28;
 +			LoadMsgDlgFont(FONTSECTION_CHAT, 17, &lf, NULL, CHAT_FONTMODULE);
 +			hFont = CreateFontIndirect(&lf);
 +			iText = GetTextPixelSize(szString, hFont, TRUE) + 3;
 +			DeleteObject(hFont);
 +			iIndent += (iText * 1440) / logPixelSX;
 +			Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tx%u", iIndent);
 +		} else if (g_Settings.dwIconFlags) {
 +			iIndent += ((g_Settings.ScaleIcons ? 14 : 20) * 1440) / logPixelSX;
 +			Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tx%u", iIndent);
 +		}
 +		if (g_Settings.ShowTime) {
 +			int iSize = (g_Settings.LogTextIndent * 1440) / logPixelSX;
 +			Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tx%u", iIndent + iSize);
 +			if (g_Settings.LogIndentEnabled)
 +				iIndent += iSize;
 +		}
 +		Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\fi-%u\\li%u", iIndent, iIndent);
 +	}
 +	return buffer;
 +}
 +
 +#define RTFPICTHEADERMAXSIZE   78
 +void LoadMsgLogBitmaps(void)
 +{
 +	HICON hIcon;
 +	HBITMAP hBmp, hoBmp;
 +	HDC hdc, hdcMem;
 +	BITMAPINFOHEADER bih = { 0 };
 +	int widthBytes, i;
 +	RECT rc;
 +	HBRUSH hBkgBrush;
 +	int rtfHeaderSize;
 +	PBYTE pBmpBits;
 +	int iIconSize;
 +	int sizeX = 0, sizeY = 0;
 +
 +	if (hIcons[0])
 +		Utils::getIconSize(hIcons[0], sizeX, sizeY);
 +	else
 +		sizeX = 16;
 +
 +	if (sizeX >= 12)
 +		iIconSize = g_Settings.ScaleIcons ? 12 : 16;
 +	else
 +		iIconSize = sizeX;
 +
 +	hBkgBrush = CreateSolidBrush(M->GetDword(FONTMODULE, SRMSGSET_BKGCOLOUR_MUC, SRMSGDEFSET_BKGCOLOUR));
 +	bih.biSize = sizeof(bih);
 +	bih.biBitCount = 24;
 +	bih.biCompression = BI_RGB;
 +	bih.biHeight = iIconSize;
 +	bih.biPlanes = 1;
 +	bih.biWidth = iIconSize;
 +	widthBytes = ((bih.biWidth * bih.biBitCount + 31) >> 5) * 4;
 +	rc.top = rc.left = 0;
 +	rc.right = iIconSize;
 +	rc.bottom = iIconSize;
 +	hdc = GetDC(NULL);
 +	hBmp = CreateCompatibleBitmap(hdc, bih.biWidth, bih.biHeight);
 +	hdcMem = CreateCompatibleDC(hdc);
 +	pBmpBits = (PBYTE) mir_alloc(widthBytes * bih.biHeight);
 +	for (i = 0; i < SIZEOF(pLogIconBmpBits); i++) {
 +		hIcon = hIcons[i];
 +		pLogIconBmpBits[i] = (PBYTE) mir_alloc(RTFPICTHEADERMAXSIZE + (bih.biSize + widthBytes * bih.biHeight) * 2);
 +		rtfHeaderSize = sprintf((char *)pLogIconBmpBits[i], "{\\pict\\dibitmap0\\wbmbitspixel%u\\wbmplanes1\\wbmwidthbytes%u\\picw%u\\pich%u ", bih.biBitCount, widthBytes, bih.biWidth, bih.biHeight);
 +		hoBmp = (HBITMAP) SelectObject(hdcMem, hBmp);
 +		FillRect(hdcMem, &rc, hBkgBrush);
 +		DrawIconEx(hdcMem, 0, 1, hIcon, iIconSize, iIconSize, 0, NULL, DI_NORMAL);
 +
 +		SelectObject(hdcMem, hoBmp);
 +		GetDIBits(hdc, hBmp, 0, bih.biHeight, pBmpBits, (BITMAPINFO *) & bih, DIB_RGB_COLORS);
 +		{
 +			int n;
 +			for (n = 0; n < sizeof(BITMAPINFOHEADER); n++)
 +				sprintf((char *)pLogIconBmpBits[i] + rtfHeaderSize + n * 2, "%02X", ((PBYTE) & bih)[n]);
 +			for (n = 0; n < widthBytes * bih.biHeight; n += 4)
 +				sprintf((char *)pLogIconBmpBits[i] + rtfHeaderSize + (bih.biSize + n) * 2, "%02X%02X%02X%02X", pBmpBits[n], pBmpBits[n + 1], pBmpBits[n + 2], pBmpBits[n + 3]);
 +		}
 +		logIconBmpSize[i] = rtfHeaderSize + (bih.biSize + widthBytes * bih.biHeight) * 2 + 1;
 +		pLogIconBmpBits[i][logIconBmpSize[i] - 1] = '}';
 +	}
 +	mir_free(pBmpBits);
 +	DeleteDC(hdcMem);
 +	DeleteObject(hBmp);
 +	ReleaseDC(NULL, hdc);
 +	DeleteObject(hBkgBrush);
 +
 +	/* cache RTF font headers */
 +
 +	//get the number of pixels per logical inch
 +	if (logPixelSY == 0) {
 +		HDC hdc;
 +		hdc = GetDC(NULL);
 +		logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY);
 +		logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX);
 +		ReleaseDC(NULL, hdc);
 +	}
 +
 +	for (i = 0; i < OPTIONS_FONTCOUNT; i++)
 +		mir_snprintf(CHAT_rtfFontsGlobal[i], RTFCACHELINESIZE, "\\f%u\\cf%u\\ul0\\highlight0\\b%d\\i%d\\ul%d\\fs%u", i, i + 1, aFonts[i].lf.lfWeight >= FW_BOLD ? 1 : 0, aFonts[i].lf.lfItalic,aFonts[i].lf.lfUnderline ,2 * abs(aFonts[i].lf.lfHeight) * 74 / logPixelSY);
 +	CHAT_rtffonts = &(CHAT_rtfFontsGlobal[0][0]);
 +}
 +
 +void FreeMsgLogBitmaps(void)
 +{
 +	int i;
 +	for (i = 0; i < SIZEOF(pLogIconBmpBits); i++)
 +		mir_free(pLogIconBmpBits[i]);
 +}
  | 
