From 1979fd80424d16b2e489f9b57d01d9c7811d25a2 Mon Sep 17 00:00:00 2001
From: dartraiden <wowemuh@gmail.com>
Date: Mon, 2 Jan 2023 21:10:29 +0300
Subject: Update copyrights

---
 plugins/TabSRMM/src/ImageDataObject.cpp     |    2 +-
 plugins/TabSRMM/src/TSButton.cpp            |    2 +-
 plugins/TabSRMM/src/chat.h                  |    2 +-
 plugins/TabSRMM/src/chat_log.cpp            |    2 +-
 plugins/TabSRMM/src/chat_main.cpp           |    2 +-
 plugins/TabSRMM/src/chat_manager.cpp        |    2 +-
 plugins/TabSRMM/src/chat_options.cpp        |    2 +-
 plugins/TabSRMM/src/chat_tools.cpp          |    2 +-
 plugins/TabSRMM/src/contactcache.cpp        |  956 ++---
 plugins/TabSRMM/src/contactcache.h          |    2 +-
 plugins/TabSRMM/src/container.cpp           |    2 +-
 plugins/TabSRMM/src/containeroptions.cpp    |    2 +-
 plugins/TabSRMM/src/controls.cpp            |    2 +-
 plugins/TabSRMM/src/controls.h              |    2 +-
 plugins/TabSRMM/src/eventpopups.cpp         |    2 +-
 plugins/TabSRMM/src/functions.h             |    2 +-
 plugins/TabSRMM/src/generic_msghandlers.cpp | 2716 ++++++-------
 plugins/TabSRMM/src/globals.cpp             |    2 +-
 plugins/TabSRMM/src/globals.h               |    2 +-
 plugins/TabSRMM/src/hotkeyhandler.cpp       |    2 +-
 plugins/TabSRMM/src/infopanel.cpp           |    2 +-
 plugins/TabSRMM/src/infopanel.h             |    2 +-
 plugins/TabSRMM/src/mim.cpp                 |  976 ++---
 plugins/TabSRMM/src/mim.h                   |    2 +-
 plugins/TabSRMM/src/modplus.cpp             |    2 +-
 plugins/TabSRMM/src/msgdialog.cpp           |    2 +-
 plugins/TabSRMM/src/msgdlgother.cpp         | 5716 +++++++++++++--------------
 plugins/TabSRMM/src/msgdlgutils.cpp         |    2 +-
 plugins/TabSRMM/src/msgdlgutils.h           |    2 +-
 plugins/TabSRMM/src/msglog.cpp              |    2 +-
 plugins/TabSRMM/src/msgoptions.cpp          |    2 +-
 plugins/TabSRMM/src/msgs.cpp                |    2 +-
 plugins/TabSRMM/src/msgs.h                  |    2 +-
 plugins/TabSRMM/src/muchighlight.cpp        |    2 +-
 plugins/TabSRMM/src/muchighlight.h          |    2 +-
 plugins/TabSRMM/src/nen.h                   |    2 +-
 plugins/TabSRMM/src/selectcontainer.cpp     |    2 +-
 plugins/TabSRMM/src/sendlater.cpp           |    2 +-
 plugins/TabSRMM/src/sendlater.h             |    2 +-
 plugins/TabSRMM/src/sendqueue.cpp           |    2 +-
 plugins/TabSRMM/src/sendqueue.h             |    2 +-
 plugins/TabSRMM/src/sidebar.cpp             |    2 +-
 plugins/TabSRMM/src/sidebar.h               |    2 +-
 plugins/TabSRMM/src/srmm.cpp                |  288 +-
 plugins/TabSRMM/src/stdafx.cxx              |    2 +-
 plugins/TabSRMM/src/stdafx.h                |    2 +-
 plugins/TabSRMM/src/tabctrl.cpp             |    2 +-
 plugins/TabSRMM/src/taskbar.cpp             |    2 +-
 plugins/TabSRMM/src/taskbar.h               |    2 +-
 plugins/TabSRMM/src/templates.cpp           |  248 +-
 plugins/TabSRMM/src/themeio.cpp             |    2 +-
 plugins/TabSRMM/src/themes.cpp              |    2 +-
 plugins/TabSRMM/src/themes.h                |    2 +-
 plugins/TabSRMM/src/userprefs.cpp           |    2 +-
 plugins/TabSRMM/src/utils.cpp               |    2 +-
 plugins/TabSRMM/src/utils.h                 |    2 +-
 plugins/TabSRMM/src/version.h               |    2 +-
 plugins/TabSRMM/src/warning.cpp             |  656 +--
 58 files changed, 5829 insertions(+), 5829 deletions(-)

(limited to 'plugins/TabSRMM/src')

diff --git a/plugins/TabSRMM/src/ImageDataObject.cpp b/plugins/TabSRMM/src/ImageDataObject.cpp
index 82c7756cef..6da7a5fda6 100644
--- a/plugins/TabSRMM/src/ImageDataObject.cpp
+++ b/plugins/TabSRMM/src/ImageDataObject.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/TSButton.cpp b/plugins/TabSRMM/src/TSButton.cpp
index 7ab8576319..444787e681 100644
--- a/plugins/TabSRMM/src/TSButton.cpp
+++ b/plugins/TabSRMM/src/TSButton.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat.h b/plugins/TabSRMM/src/chat.h
index 42df2909a2..75ceea90cf 100644
--- a/plugins/TabSRMM/src/chat.h
+++ b/plugins/TabSRMM/src/chat.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_log.cpp b/plugins/TabSRMM/src/chat_log.cpp
index e25c14e6fa..73884d4e84 100644
--- a/plugins/TabSRMM/src/chat_log.cpp
+++ b/plugins/TabSRMM/src/chat_log.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_main.cpp b/plugins/TabSRMM/src/chat_main.cpp
index c9662c5dbc..b398955f21 100644
--- a/plugins/TabSRMM/src/chat_main.cpp
+++ b/plugins/TabSRMM/src/chat_main.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_manager.cpp b/plugins/TabSRMM/src/chat_manager.cpp
index dab25a0ddf..bc540bed44 100644
--- a/plugins/TabSRMM/src/chat_manager.cpp
+++ b/plugins/TabSRMM/src/chat_manager.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_options.cpp b/plugins/TabSRMM/src/chat_options.cpp
index f8c1c99341..710a46a0af 100644
--- a/plugins/TabSRMM/src/chat_options.cpp
+++ b/plugins/TabSRMM/src/chat_options.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/chat_tools.cpp b/plugins/TabSRMM/src/chat_tools.cpp
index d2d7fdc31f..78a316ea6f 100644
--- a/plugins/TabSRMM/src/chat_tools.cpp
+++ b/plugins/TabSRMM/src/chat_tools.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/contactcache.cpp b/plugins/TabSRMM/src/contactcache.cpp
index 88b06217ca..2071555d94 100644
--- a/plugins/TabSRMM/src/contactcache.cpp
+++ b/plugins/TabSRMM/src/contactcache.cpp
@@ -1,478 +1,478 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/IM project,
-// all portions of this codebase are copyrighted to the people
-// listed in contributors.txt.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// you should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// contact cache implementation
-//
-// the contact cache provides various services to the message window(s)
-// it also abstracts meta contacts.
-
-#include "stdafx.h"
-
-static OBJLIST<CContactCache> arContacts(50, NumericKeySortT);
-
-static DBCachedContact ccInvalid;
-
-CContactCache::CContactCache(MCONTACT hContact) :
-	m_hContact(hContact),
-	m_history(10)
-{
-	if (hContact) {
-		if ((cc = db_get_contact(hContact)) != nullptr) {
-			initPhaseTwo();
-			return;
-		}
-	}
-
-	cc = &ccInvalid;
-	m_szAccount = C_INVALID_ACCOUNT;
-	m_isMeta = false;
-	m_isValid = false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// 2nd part of the object initialization that must be callable during the
-// object's lifetime (not only on construction).
-
-void CContactCache::initPhaseTwo()
-{
-	m_szAccount = nullptr;
-	if (cc->szProto) {
-		PROTOACCOUNT *acc = Proto_GetAccount(cc->szProto);
-		if (acc && acc->tszAccountName)
-			m_szAccount = acc->tszAccountName;
-	}
-
-	m_isValid = (cc->szProto != nullptr && m_szAccount != nullptr) ? true : false;
-	if (m_isValid) {
-		m_iStatus = db_get_w(m_hContact, cc->szProto, "Status", ID_STATUS_OFFLINE);
-		m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here
-		if (m_isMeta)
-			updateMeta();
-		updateNick();
-	}
-	else {
-		m_szAccount = C_INVALID_ACCOUNT;
-		m_isMeta = false;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// reset meta contact information.Used when meta contacts are disabled
-// on user's request.
-
-void CContactCache::resetMeta()
-{
-	m_isMeta = false;
-	m_szMetaProto = nullptr;
-	m_iMetaStatus = ID_STATUS_OFFLINE;
-	initPhaseTwo();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// if the contact has an open message window, close it.
-// window procedure will use setWindowData() to reset m_hwnd to 0.
-
-void CContactCache::closeWindow()
-{
-	if (m_dat) {
-		m_dat->m_bForcedClose = true;
-		m_dat->Close();
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update private copy of the nick name.Use contact list name cache
-// 
-// @return bool: true if nick has changed.
-
-bool CContactCache::updateNick()
-{
-	bool fChanged = false;
-	if (m_isValid) {
-		wchar_t *tszNick = Clist_GetContactDisplayName(getActiveContact());
-		if (tszNick && mir_wstrcmp(m_szNick, tszNick))
-			fChanged = true;
-		wcsncpy_s(m_szNick, (tszNick ? tszNick : L"<undef>"), _TRUNCATE);
-	}
-	return fChanged;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update meta(subcontact and - protocol) status.This runs when the
-// MC protocol fires one of its events OR when a relevant database value changes
-// in the master contact.
-
-void CContactCache::updateMeta()
-{
-	if (m_isValid) {
-		MCONTACT hOldSub = m_hSub;
-		m_hSub = db_mc_getSrmmSub(cc->contactID);
-		m_szMetaProto = Proto_GetBaseAccountName(m_hSub);
-		m_iMetaStatus = (uint16_t)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE);
-		PROTOACCOUNT *pa = Proto_GetAccount(m_szMetaProto);
-		if (pa)
-			m_szAccount = pa->tszAccountName;
-
-		if (hOldSub != m_hSub) {
-			updateNick();
-			updateUIN();
-		}
-	}
-	else {
-		m_hSub = 0;
-		m_szMetaProto = nullptr;
-		m_iMetaStatus = ID_STATUS_OFFLINE;
-		m_xStatus = 0;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// obtain the UIN.This is only maintained for open message windows
-// it also run when the subcontact for a MC changes.
-
-bool CContactCache::updateUIN()
-{
-	m_szUIN[0] = 0;
-
-	if (m_isValid) {
-		ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, getActiveContact(), getActiveProto()));
-		if (uid != nullptr)
-			wcsncpy_s(m_szUIN, uid, _TRUNCATE);
-	}
-
-	return false;
-}
-
-void CContactCache::updateStats(int iType, size_t value)
-{
-	if (m_stats == nullptr)
-		m_stats = new TSessionStats();
-
-	switch (iType) {
-	case TSessionStats::UPDATE_WITH_LAST_RCV:
-		if (!m_stats->lastReceivedChars)
-			break;
-		m_stats->iReceived++;
-		m_stats->messageCount++;
-		m_stats->iReceivedBytes += m_stats->lastReceivedChars;
-		m_stats->lastReceivedChars = 0;
-		break;
-	case TSessionStats::INIT_TIMER:
-		m_stats->started = time(0);
-		break;
-	case TSessionStats::SET_LAST_RCV:
-		m_stats->lastReceivedChars = (unsigned int)value;
-		break;
-	case TSessionStats::BYTES_SENT:
-		m_stats->iSent++;
-		m_stats->messageCount++;
-		m_stats->iSentBytes += (unsigned int)value;
-		break;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-//set the window data for this contact.The window procedure of the message
-// dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache
-// that a message window is open for this contact.
-//
-// @param dat: CMsgDialog* - window data structure
-
-void CContactCache::setWindowData(CMsgDialog *dat)
-{
-	m_dat = dat;
-	
-	if (dat) {
-		updateStatusMsg();
-	}
-	else {
-		// release memory - not needed when window isn't open
-		replaceStrW(m_szStatusMsg, nullptr);
-		replaceStrW(m_ListeningInfo, nullptr);
-		replaceStrW(m_xStatusMsg, nullptr);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// saves message to the input history.
-// it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history.
-
-void CContactCache::saveHistory()
-{
-	if (m_dat == nullptr)
-		return;
-
-	CCtrlRichEdit &pEntry = m_dat->GetEntry();
-	ptrA szFromStream(pEntry.GetRichTextRtf());
-	if (szFromStream != nullptr) {
-		m_iHistoryCurrent = -1;
-		m_history.insert(szFromStream.detach());
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// handle the input history scrolling for the message input area
-// @param wParam: VK_ keyboard code (VK_UP or VK_DOWN)
-
-void CContactCache::inputHistoryEvent(WPARAM wParam)
-{
-	if (m_dat == nullptr)
-		return;
-
-	CCtrlRichEdit &pEntry = m_dat->GetEntry();
-	if (m_history.getCount() > 0) {
-		char *pszText;
-		if (wParam == VK_UP) {
-			if (m_iHistoryCurrent == 0)
-				return;
-
-			if (m_iHistoryCurrent < 0)
-				m_iHistoryCurrent = m_history.getCount()-1;
-			else
-				m_iHistoryCurrent--;
-			pszText = m_history[m_iHistoryCurrent];
-		}
-		else {
-			if (m_iHistoryCurrent == -1)
-				return;
-
-			if (m_iHistoryCurrent == m_history.getCount() - 1) {
-				m_iHistoryCurrent = -1;
-				pszText = "";
-			}
-			else {
-				m_iHistoryCurrent++;
-				pszText = m_history[m_iHistoryCurrent];
-			}
-		}
-
-		SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
-		pEntry.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pszText);
-		pEntry.SendMsg(EM_SETSEL, -1, -1);
-	}
-
-	pEntry.OnChange(&pEntry);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// release additional memory resources
-
-void CContactCache::releaseAlloced()
-{
-	if (m_stats) {
-		delete m_stats;
-		m_stats = nullptr;
-	}
-
-	for (auto &it : m_history)
-		mir_free(it);
-	m_history.destroy();
-
-	mir_free(m_szStatusMsg);
-	m_szStatusMsg = nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// when a contact is deleted, mark it as invalid in the cache and release
-// all memory it has allocated.
-
-void CContactCache::deletedHandler()
-{
-	cc = &ccInvalid;
-	m_isValid = false;
-	if (m_dat) {
-		m_dat->m_bForcedClose = true;
-
-		// this message must be sent async to allow a contact to rest in peace before window gets closed
-		::PostMessage(m_dat->GetHwnd(), WM_CLOSE, 1, 2);
-	}
-
-	releaseAlloced();
-	m_hContact = INVALID_CONTACT_ID;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update all or only the given status message information from the database
-//
-// @param szKey: char* database key name or 0 to reload all messages
-
-void CContactCache::updateStatusMsg(const char *szKey)
-{
-	if (!m_isValid)
-		return;
-
-	MCONTACT hContact = getActiveContact();
-
-	if (szKey == nullptr || (szKey && !mir_strcmp("StatusMsg", szKey))) {
-		if (m_szStatusMsg)
-			mir_free(m_szStatusMsg);
-		m_szStatusMsg = nullptr;
-		ptrW szStatus(db_get_wsa(hContact, "CList", "StatusMsg"));
-		if (szStatus != 0)
-			m_szStatusMsg = (mir_wstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : nullptr);
-	}
-	if (szKey == nullptr || (szKey && !mir_strcmp("ListeningTo", szKey))) {
-		if (m_ListeningInfo)
-			mir_free(m_ListeningInfo);
-		m_ListeningInfo = nullptr;
-		ptrW szListeningTo(db_get_wsa(hContact, cc->szProto, "ListeningTo"));
-		if (szListeningTo != 0 && *szListeningTo)
-			m_ListeningInfo = szListeningTo.detach();
-	}
-	if (szKey == nullptr || (szKey && !mir_strcmp("XStatusMsg", szKey))) {
-		if (m_xStatusMsg)
-			mir_free(m_xStatusMsg);
-		m_xStatusMsg = nullptr;
-		ptrW szXStatusMsg(db_get_wsa(hContact, cc->szProto, "XStatusMsg"));
-		if (szXStatusMsg != 0 && *szXStatusMsg)
-			m_xStatusMsg = szXStatusMsg.detach();
-	}
-	m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve contact cache entry for the given contact.It _never_ returns zero, for a hContact
-// 0, it retrieves a dummy object.
-// Non-existing cache entries are created on demand.
-//
-// @param 	hContact:			contact handle
-// @return	CContactCache*		pointer to the cache entry for this contact
-
-CContactCache* CContactCache::getContactCache(MCONTACT hContact)
-{
-	CContactCache *cc = arContacts.find((CContactCache*)&hContact);
-	if (cc == nullptr) {
-		cc = new CContactCache(hContact);
-		arContacts.insert(cc);
-	}
-	return cc;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// when the state of the meta contacts protocol changes from enabled to disabled
-// (or vice versa), this updates the contact cache
-//
-// it is ONLY called from the DBSettingChanged() event handler when the relevant
-// database value is touched.
-
-int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM)
-{
-	for (auto &c : arContacts) {
-		if (c->isMeta() && !bMetaEnabled) {
-			c->closeWindow();
-			c->resetMeta();
-		}
-
-		// meta contacts are enabled, but current contact is a subcontact - > close window
-		if (bMetaEnabled && c->isSubContact())
-			c->closeWindow();
-
-		// reset meta contact information, if metacontacts protocol became avail
-		if (bMetaEnabled && !c->cc->IsMeta())
-			c->resetMeta();
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// normalize the status message with proper cr / lf sequences.
-// @param src wchar_t*:		original status message
-// @param fStripAll bool:	strip all cr/lf sequences and replace them with spaces (use for title bar)
-// @return wchar_t*:			converted status message. CALLER is responsible to mir_free it, MUST use mir_free()
-
-wchar_t* CContactCache::getNormalizedStatusMsg(const wchar_t *src, bool fStripAll)
-{
-	if (src == nullptr || mir_wstrlen(src) < 2)
-		return nullptr;
-
-	CMStringW dest;
-
-	for (int i = 0; src[i] != 0; i++) {
-		if (src[i] == 0x0d || src[i] == '\t')
-			continue;
-		if (i && src[i] == (wchar_t)0x0a) {
-			if (fStripAll) {
-				dest.AppendChar(' ');
-				continue;
-			}
-			dest.AppendChar('\n');
-			continue;
-		}
-		dest.AppendChar(src[i]);
-	}
-
-	return mir_wstrndup(dest, dest.GetLength());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve the tab / title icon for the corresponding session.
-
-HICON CContactCache::getIcon(int &iSize) const
-{
-	if (!m_dat)
-		return Skin_LoadProtoIcon(cc->szProto, getStatus());
-
-	if (m_dat->m_bErrorState)
-		return PluginConfig.g_iconErr;
-	if (m_dat->m_bCanFlashTab)
-		return m_dat->m_iFlashIcon;
-
-	if (m_dat->isChat() && m_dat->m_iFlashIcon) {
-		int sizeX, sizeY;
-		Utils::getIconSize(m_dat->m_iFlashIcon, sizeX, sizeY);
-		iSize = sizeX;
-		return m_dat->m_iFlashIcon;
-	}
-	if (m_dat->m_hTabIcon == m_dat->m_hTabStatusIcon && m_dat->m_hXStatusIcon)
-		return m_dat->m_hXStatusIcon;
-	return m_dat->m_hTabIcon;
-}
-
-size_t CContactCache::getMaxMessageLength()
-{
-	if (m_nMax == 0) {
-		MCONTACT hContact = getActiveContact();
-		LPCSTR szProto = getActiveProto();
-		if (szProto) {
-			m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact);
-			if (m_nMax) {
-				if (M.GetByte("autosplit", 0))
-					m_nMax = 20000;
-			}
-			else m_nMax = 20000;
-
-			m_dat->LimitMessageText(m_nMax);
-		}
-	}
-	return m_nMax;
-}
-
-bool CContactCache::updateStatus(int iStatus)
-{
-	m_iOldStatus = m_iStatus;
-	m_iStatus = iStatus;
-	return m_iOldStatus != iStatus;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// contact cache implementation
+//
+// the contact cache provides various services to the message window(s)
+// it also abstracts meta contacts.
+
+#include "stdafx.h"
+
+static OBJLIST<CContactCache> arContacts(50, NumericKeySortT);
+
+static DBCachedContact ccInvalid;
+
+CContactCache::CContactCache(MCONTACT hContact) :
+	m_hContact(hContact),
+	m_history(10)
+{
+	if (hContact) {
+		if ((cc = db_get_contact(hContact)) != nullptr) {
+			initPhaseTwo();
+			return;
+		}
+	}
+
+	cc = &ccInvalid;
+	m_szAccount = C_INVALID_ACCOUNT;
+	m_isMeta = false;
+	m_isValid = false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// 2nd part of the object initialization that must be callable during the
+// object's lifetime (not only on construction).
+
+void CContactCache::initPhaseTwo()
+{
+	m_szAccount = nullptr;
+	if (cc->szProto) {
+		PROTOACCOUNT *acc = Proto_GetAccount(cc->szProto);
+		if (acc && acc->tszAccountName)
+			m_szAccount = acc->tszAccountName;
+	}
+
+	m_isValid = (cc->szProto != nullptr && m_szAccount != nullptr) ? true : false;
+	if (m_isValid) {
+		m_iStatus = db_get_w(m_hContact, cc->szProto, "Status", ID_STATUS_OFFLINE);
+		m_isMeta = db_mc_isMeta(cc->contactID) != 0; // don't use cc->IsMeta() here
+		if (m_isMeta)
+			updateMeta();
+		updateNick();
+	}
+	else {
+		m_szAccount = C_INVALID_ACCOUNT;
+		m_isMeta = false;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reset meta contact information.Used when meta contacts are disabled
+// on user's request.
+
+void CContactCache::resetMeta()
+{
+	m_isMeta = false;
+	m_szMetaProto = nullptr;
+	m_iMetaStatus = ID_STATUS_OFFLINE;
+	initPhaseTwo();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// if the contact has an open message window, close it.
+// window procedure will use setWindowData() to reset m_hwnd to 0.
+
+void CContactCache::closeWindow()
+{
+	if (m_dat) {
+		m_dat->m_bForcedClose = true;
+		m_dat->Close();
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update private copy of the nick name.Use contact list name cache
+// 
+// @return bool: true if nick has changed.
+
+bool CContactCache::updateNick()
+{
+	bool fChanged = false;
+	if (m_isValid) {
+		wchar_t *tszNick = Clist_GetContactDisplayName(getActiveContact());
+		if (tszNick && mir_wstrcmp(m_szNick, tszNick))
+			fChanged = true;
+		wcsncpy_s(m_szNick, (tszNick ? tszNick : L"<undef>"), _TRUNCATE);
+	}
+	return fChanged;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update meta(subcontact and - protocol) status.This runs when the
+// MC protocol fires one of its events OR when a relevant database value changes
+// in the master contact.
+
+void CContactCache::updateMeta()
+{
+	if (m_isValid) {
+		MCONTACT hOldSub = m_hSub;
+		m_hSub = db_mc_getSrmmSub(cc->contactID);
+		m_szMetaProto = Proto_GetBaseAccountName(m_hSub);
+		m_iMetaStatus = (uint16_t)db_get_w(m_hSub, m_szMetaProto, "Status", ID_STATUS_OFFLINE);
+		PROTOACCOUNT *pa = Proto_GetAccount(m_szMetaProto);
+		if (pa)
+			m_szAccount = pa->tszAccountName;
+
+		if (hOldSub != m_hSub) {
+			updateNick();
+			updateUIN();
+		}
+	}
+	else {
+		m_hSub = 0;
+		m_szMetaProto = nullptr;
+		m_iMetaStatus = ID_STATUS_OFFLINE;
+		m_xStatus = 0;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// obtain the UIN.This is only maintained for open message windows
+// it also run when the subcontact for a MC changes.
+
+bool CContactCache::updateUIN()
+{
+	m_szUIN[0] = 0;
+
+	if (m_isValid) {
+		ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, getActiveContact(), getActiveProto()));
+		if (uid != nullptr)
+			wcsncpy_s(m_szUIN, uid, _TRUNCATE);
+	}
+
+	return false;
+}
+
+void CContactCache::updateStats(int iType, size_t value)
+{
+	if (m_stats == nullptr)
+		m_stats = new TSessionStats();
+
+	switch (iType) {
+	case TSessionStats::UPDATE_WITH_LAST_RCV:
+		if (!m_stats->lastReceivedChars)
+			break;
+		m_stats->iReceived++;
+		m_stats->messageCount++;
+		m_stats->iReceivedBytes += m_stats->lastReceivedChars;
+		m_stats->lastReceivedChars = 0;
+		break;
+	case TSessionStats::INIT_TIMER:
+		m_stats->started = time(0);
+		break;
+	case TSessionStats::SET_LAST_RCV:
+		m_stats->lastReceivedChars = (unsigned int)value;
+		break;
+	case TSessionStats::BYTES_SENT:
+		m_stats->iSent++;
+		m_stats->messageCount++;
+		m_stats->iSentBytes += (unsigned int)value;
+		break;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+//set the window data for this contact.The window procedure of the message
+// dialog will use this in WM_INITDIALOG and WM_DESTROY to tell the cache
+// that a message window is open for this contact.
+//
+// @param dat: CMsgDialog* - window data structure
+
+void CContactCache::setWindowData(CMsgDialog *dat)
+{
+	m_dat = dat;
+	
+	if (dat) {
+		updateStatusMsg();
+	}
+	else {
+		// release memory - not needed when window isn't open
+		replaceStrW(m_szStatusMsg, nullptr);
+		replaceStrW(m_ListeningInfo, nullptr);
+		replaceStrW(m_xStatusMsg, nullptr);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// saves message to the input history.
+// it's using streamout in UTF8 format - no unicode "issues" and all RTF formatting is saved to the history.
+
+void CContactCache::saveHistory()
+{
+	if (m_dat == nullptr)
+		return;
+
+	CCtrlRichEdit &pEntry = m_dat->GetEntry();
+	ptrA szFromStream(pEntry.GetRichTextRtf());
+	if (szFromStream != nullptr) {
+		m_iHistoryCurrent = -1;
+		m_history.insert(szFromStream.detach());
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// handle the input history scrolling for the message input area
+// @param wParam: VK_ keyboard code (VK_UP or VK_DOWN)
+
+void CContactCache::inputHistoryEvent(WPARAM wParam)
+{
+	if (m_dat == nullptr)
+		return;
+
+	CCtrlRichEdit &pEntry = m_dat->GetEntry();
+	if (m_history.getCount() > 0) {
+		char *pszText;
+		if (wParam == VK_UP) {
+			if (m_iHistoryCurrent == 0)
+				return;
+
+			if (m_iHistoryCurrent < 0)
+				m_iHistoryCurrent = m_history.getCount()-1;
+			else
+				m_iHistoryCurrent--;
+			pszText = m_history[m_iHistoryCurrent];
+		}
+		else {
+			if (m_iHistoryCurrent == -1)
+				return;
+
+			if (m_iHistoryCurrent == m_history.getCount() - 1) {
+				m_iHistoryCurrent = -1;
+				pszText = "";
+			}
+			else {
+				m_iHistoryCurrent++;
+				pszText = m_history[m_iHistoryCurrent];
+			}
+		}
+
+		SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
+		pEntry.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)pszText);
+		pEntry.SendMsg(EM_SETSEL, -1, -1);
+	}
+
+	pEntry.OnChange(&pEntry);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// release additional memory resources
+
+void CContactCache::releaseAlloced()
+{
+	if (m_stats) {
+		delete m_stats;
+		m_stats = nullptr;
+	}
+
+	for (auto &it : m_history)
+		mir_free(it);
+	m_history.destroy();
+
+	mir_free(m_szStatusMsg);
+	m_szStatusMsg = nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// when a contact is deleted, mark it as invalid in the cache and release
+// all memory it has allocated.
+
+void CContactCache::deletedHandler()
+{
+	cc = &ccInvalid;
+	m_isValid = false;
+	if (m_dat) {
+		m_dat->m_bForcedClose = true;
+
+		// this message must be sent async to allow a contact to rest in peace before window gets closed
+		::PostMessage(m_dat->GetHwnd(), WM_CLOSE, 1, 2);
+	}
+
+	releaseAlloced();
+	m_hContact = INVALID_CONTACT_ID;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update all or only the given status message information from the database
+//
+// @param szKey: char* database key name or 0 to reload all messages
+
+void CContactCache::updateStatusMsg(const char *szKey)
+{
+	if (!m_isValid)
+		return;
+
+	MCONTACT hContact = getActiveContact();
+
+	if (szKey == nullptr || (szKey && !mir_strcmp("StatusMsg", szKey))) {
+		if (m_szStatusMsg)
+			mir_free(m_szStatusMsg);
+		m_szStatusMsg = nullptr;
+		ptrW szStatus(db_get_wsa(hContact, "CList", "StatusMsg"));
+		if (szStatus != 0)
+			m_szStatusMsg = (mir_wstrlen(szStatus) > 0 ? getNormalizedStatusMsg(szStatus) : nullptr);
+	}
+	if (szKey == nullptr || (szKey && !mir_strcmp("ListeningTo", szKey))) {
+		if (m_ListeningInfo)
+			mir_free(m_ListeningInfo);
+		m_ListeningInfo = nullptr;
+		ptrW szListeningTo(db_get_wsa(hContact, cc->szProto, "ListeningTo"));
+		if (szListeningTo != 0 && *szListeningTo)
+			m_ListeningInfo = szListeningTo.detach();
+	}
+	if (szKey == nullptr || (szKey && !mir_strcmp("XStatusMsg", szKey))) {
+		if (m_xStatusMsg)
+			mir_free(m_xStatusMsg);
+		m_xStatusMsg = nullptr;
+		ptrW szXStatusMsg(db_get_wsa(hContact, cc->szProto, "XStatusMsg"));
+		if (szXStatusMsg != 0 && *szXStatusMsg)
+			m_xStatusMsg = szXStatusMsg.detach();
+	}
+	m_xStatus = db_get_b(hContact, cc->szProto, "XStatusId", 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve contact cache entry for the given contact.It _never_ returns zero, for a hContact
+// 0, it retrieves a dummy object.
+// Non-existing cache entries are created on demand.
+//
+// @param 	hContact:			contact handle
+// @return	CContactCache*		pointer to the cache entry for this contact
+
+CContactCache* CContactCache::getContactCache(MCONTACT hContact)
+{
+	CContactCache *cc = arContacts.find((CContactCache*)&hContact);
+	if (cc == nullptr) {
+		cc = new CContactCache(hContact);
+		arContacts.insert(cc);
+	}
+	return cc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// when the state of the meta contacts protocol changes from enabled to disabled
+// (or vice versa), this updates the contact cache
+//
+// it is ONLY called from the DBSettingChanged() event handler when the relevant
+// database value is touched.
+
+int CContactCache::cacheUpdateMetaChanged(WPARAM bMetaEnabled, LPARAM)
+{
+	for (auto &c : arContacts) {
+		if (c->isMeta() && !bMetaEnabled) {
+			c->closeWindow();
+			c->resetMeta();
+		}
+
+		// meta contacts are enabled, but current contact is a subcontact - > close window
+		if (bMetaEnabled && c->isSubContact())
+			c->closeWindow();
+
+		// reset meta contact information, if metacontacts protocol became avail
+		if (bMetaEnabled && !c->cc->IsMeta())
+			c->resetMeta();
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// normalize the status message with proper cr / lf sequences.
+// @param src wchar_t*:		original status message
+// @param fStripAll bool:	strip all cr/lf sequences and replace them with spaces (use for title bar)
+// @return wchar_t*:			converted status message. CALLER is responsible to mir_free it, MUST use mir_free()
+
+wchar_t* CContactCache::getNormalizedStatusMsg(const wchar_t *src, bool fStripAll)
+{
+	if (src == nullptr || mir_wstrlen(src) < 2)
+		return nullptr;
+
+	CMStringW dest;
+
+	for (int i = 0; src[i] != 0; i++) {
+		if (src[i] == 0x0d || src[i] == '\t')
+			continue;
+		if (i && src[i] == (wchar_t)0x0a) {
+			if (fStripAll) {
+				dest.AppendChar(' ');
+				continue;
+			}
+			dest.AppendChar('\n');
+			continue;
+		}
+		dest.AppendChar(src[i]);
+	}
+
+	return mir_wstrndup(dest, dest.GetLength());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve the tab / title icon for the corresponding session.
+
+HICON CContactCache::getIcon(int &iSize) const
+{
+	if (!m_dat)
+		return Skin_LoadProtoIcon(cc->szProto, getStatus());
+
+	if (m_dat->m_bErrorState)
+		return PluginConfig.g_iconErr;
+	if (m_dat->m_bCanFlashTab)
+		return m_dat->m_iFlashIcon;
+
+	if (m_dat->isChat() && m_dat->m_iFlashIcon) {
+		int sizeX, sizeY;
+		Utils::getIconSize(m_dat->m_iFlashIcon, sizeX, sizeY);
+		iSize = sizeX;
+		return m_dat->m_iFlashIcon;
+	}
+	if (m_dat->m_hTabIcon == m_dat->m_hTabStatusIcon && m_dat->m_hXStatusIcon)
+		return m_dat->m_hXStatusIcon;
+	return m_dat->m_hTabIcon;
+}
+
+size_t CContactCache::getMaxMessageLength()
+{
+	if (m_nMax == 0) {
+		MCONTACT hContact = getActiveContact();
+		LPCSTR szProto = getActiveProto();
+		if (szProto) {
+			m_nMax = CallProtoService(szProto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, hContact);
+			if (m_nMax) {
+				if (M.GetByte("autosplit", 0))
+					m_nMax = 20000;
+			}
+			else m_nMax = 20000;
+
+			m_dat->LimitMessageText(m_nMax);
+		}
+	}
+	return m_nMax;
+}
+
+bool CContactCache::updateStatus(int iStatus)
+{
+	m_iOldStatus = m_iStatus;
+	m_iStatus = iStatus;
+	return m_iOldStatus != iStatus;
+}
diff --git a/plugins/TabSRMM/src/contactcache.h b/plugins/TabSRMM/src/contactcache.h
index 80a7ff18b2..1168b60258 100644
--- a/plugins/TabSRMM/src/contactcache.h
+++ b/plugins/TabSRMM/src/contactcache.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/container.cpp b/plugins/TabSRMM/src/container.cpp
index c26ddddb23..2c6c218631 100644
--- a/plugins/TabSRMM/src/container.cpp
+++ b/plugins/TabSRMM/src/container.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/containeroptions.cpp b/plugins/TabSRMM/src/containeroptions.cpp
index f956233387..96eaf7480d 100644
--- a/plugins/TabSRMM/src/containeroptions.cpp
+++ b/plugins/TabSRMM/src/containeroptions.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/controls.cpp b/plugins/TabSRMM/src/controls.cpp
index 4e72e2c8e2..1ac916f6cc 100644
--- a/plugins/TabSRMM/src/controls.cpp
+++ b/plugins/TabSRMM/src/controls.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/controls.h b/plugins/TabSRMM/src/controls.h
index c3ff022fd1..b878f347d4 100644
--- a/plugins/TabSRMM/src/controls.h
+++ b/plugins/TabSRMM/src/controls.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/eventpopups.cpp b/plugins/TabSRMM/src/eventpopups.cpp
index 37731f2f44..3b35900a1a 100644
--- a/plugins/TabSRMM/src/eventpopups.cpp
+++ b/plugins/TabSRMM/src/eventpopups.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/functions.h b/plugins/TabSRMM/src/functions.h
index e8c57f4476..dc0bb47c5f 100644
--- a/plugins/TabSRMM/src/functions.h
+++ b/plugins/TabSRMM/src/functions.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/generic_msghandlers.cpp b/plugins/TabSRMM/src/generic_msghandlers.cpp
index 9399fdc313..f704717da6 100644
--- a/plugins/TabSRMM/src/generic_msghandlers.cpp
+++ b/plugins/TabSRMM/src/generic_msghandlers.cpp
@@ -1,1358 +1,1358 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/IM project,
-// all portions of this codebase are copyrighted to the people
-// listed in contributors.txt.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// you should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// these are generic message handlers which are used by the message dialog window procedure.
-// calling them directly instead of using SendMessage() is faster.
-// also contains various callback functions for custom buttons
-
-#include "stdafx.h"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Save message log for given session as RTF document
-
-/*
-void CMsgDialog::DM_SaveLogAsRTF() const
-{
-	if (m_hwndIEView != nullptr) {
-		IEVIEWEVENT event = { sizeof(event) };
-		event.hwnd = m_hwndIEView;
-		event.hContact = m_hContact;
-		event.iType = IEE_SAVE_DOCUMENT;
-		CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
-	}
-	else {
-		wchar_t szFilter[MAX_PATH], szFilename[MAX_PATH];
-		mir_snwprintf(szFilter, L"%s%c*.rtf%c%c", TranslateT("Rich Edit file"), 0, 0, 0);
-		mir_snwprintf(szFilename, L"%s.rtf", m_cache->getNick());
-
-		Utils::sanitizeFilename(szFilename);
-
-		wchar_t szInitialDir[MAX_PATH + 2];
-		mir_snwprintf(szInitialDir, L"%s%s\\", M.getDataPath(), L"\\Saved message logs");
-		CreateDirectoryTreeW(szInitialDir);
-
-		OPENFILENAME ofn = { 0 };
-		ofn.lStructSize = sizeof(ofn);
-		ofn.hwndOwner = m_hwnd;
-		ofn.lpstrFile = szFilename;
-		ofn.lpstrFilter = szFilter;
-		ofn.lpstrInitialDir = szInitialDir;
-		ofn.nMaxFile = MAX_PATH;
-		ofn.Flags = OFN_HIDEREADONLY;
-		ofn.lpstrDefExt = L"rtf";
-		if (GetSaveFileName(&ofn)) {
-			EDITSTREAM stream = { 0 };
-			stream.dwCookie = (DWORD_PTR)szFilename;
-			stream.dwError = 0;
-			stream.pfnCallback = Utils::StreamOut;
-			m_rtf.SendMsg(EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM)&stream);
-		}
-	}
-}
-*/
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// checks if the balloon tooltip can be dismissed (usually called by WM_MOUSEMOVE events)
-
-void CMsgDialog::DM_DismissTip(const POINT& pt)
-{
-	if (!IsWindowVisible(m_hwndTip))
-		return;
-
-	RECT rc;
-	GetWindowRect(m_hwndTip, &rc);
-	if (PtInRect(&rc, pt))
-		return;
-
-	if (abs(pt.x - m_ptTipActivation.x) > 5 || abs(pt.y - m_ptTipActivation.y) > 5) {
-		SendMessage(m_hwndTip, TTM_TRACKACTIVATE, FALSE, 0);
-		m_ptTipActivation.x = m_ptTipActivation.y = 0;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// initialize the balloon tooltip for message window notifications
-
-void CMsgDialog::DM_InitTip()
-{
-	m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
-		CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, nullptr, g_plugin.getInst(), (LPVOID)nullptr);
-
-	memset(&ti, 0, sizeof(ti));
-	ti.cbSize = sizeof(ti);
-	ti.lpszText = TranslateT("No status message");
-	ti.hinst = g_plugin.getInst();
-	ti.hwnd = m_hwnd;
-	ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT;
-	ti.uId = (UINT_PTR)m_hwnd;
-	SendMessage(m_hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
-
-	SetWindowPos(m_hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// checks generic hotkeys valid for both IM and MUC sessions
-//
-// returns 1 for handled hotkeys, 0 otherwise.
-
-bool CMsgDialog::DM_GenericHotkeysCheck(MSG *message)
-{
-	LRESULT mim_hotkey_check = Hotkey_Check(message, TABSRMM_HK_SECTION_GENERIC);
-
-	switch (mim_hotkey_check) {
-	case TABSRMM_HK_PASTEANDSEND:
-		HandlePasteAndSend();
-		return true;
-
-	case TABSRMM_HK_HISTORY:
-		m_btnHistory.Click();
-		return true;
-
-	case TABSRMM_HK_CONTAINEROPTIONS:
-		m_pContainer->OptionsDialog();
-		return true;
-
-	case TABSRMM_HK_TOGGLEINFOPANEL:
-		m_pPanel.setActive(!m_pPanel.isActive());
-		m_pPanel.showHide();
-		return true;
-
-	case TABSRMM_HK_TOGGLETOOLBAR:
-		SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLETOOLBAR, 0);
-		return true;
-
-	case TABSRMM_HK_CLEARLOG:
-		tabClearLog();
-		return true;
-
-	case TABSRMM_HK_TOGGLESIDEBAR:
-		if (m_pContainer->m_pSideBar->isActive())
-			SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
-		return true;
-
-	case TABSRMM_HK_CLOSE_OTHER:
-		CloseOtherTabs(m_pContainer->m_hwndTabs, *this);
-		return true;
-	}
-	return false;
-}
-
-LRESULT CMsgDialog::DM_MsgWindowCmdHandler(UINT cmd, WPARAM wParam, LPARAM lParam)
-{
-	RECT  rc;
-	int   iSelection;
-	HMENU submenu;
-
-	switch (cmd) {
-	case IDC_SRMM_BOLD:
-	case IDC_SRMM_ITALICS:
-	case IDC_SRMM_UNDERLINE:
-	case IDC_FONTSTRIKEOUT:
-		if (m_SendFormat != 0) { // dont use formatting if disabled
-			CHARFORMAT2 cf, cfOld;
-			memset(&cf, 0, sizeof(CHARFORMAT2));
-			memset(&cfOld, 0, sizeof(CHARFORMAT2));
-			cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2);
-			cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
-			m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld);
-			BOOL isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD);
-			BOOL isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC);
-			BOOL isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE);
-			BOOL isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT);
-
-			int ctrlId = LOWORD(wParam);
-			if (ctrlId == IDC_SRMM_BOLD && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_BOLD)))
-				break;
-			if (ctrlId == IDC_SRMM_ITALICS && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_ITALICS)))
-				break;
-			if (ctrlId == IDC_SRMM_UNDERLINE && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_UNDERLINE)))
-				break;
-			if (ctrlId == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_FONTSTRIKEOUT)))
-				break;
-			if (ctrlId == IDC_SRMM_BOLD) {
-				cf.dwEffects = isBold ? 0 : CFE_BOLD;
-				cf.dwMask = CFM_BOLD;
-				CheckDlgButton(m_hwnd, IDC_SRMM_BOLD, !isBold ? BST_CHECKED : BST_UNCHECKED);
-			}
-			else if (ctrlId == IDC_SRMM_ITALICS) {
-				cf.dwEffects = isItalic ? 0 : CFE_ITALIC;
-				cf.dwMask = CFM_ITALIC;
-				CheckDlgButton(m_hwnd, IDC_SRMM_ITALICS, !isItalic ? BST_CHECKED : BST_UNCHECKED);
-			}
-			else if (ctrlId == IDC_SRMM_UNDERLINE) {
-				cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE;
-				cf.dwMask = CFM_UNDERLINE;
-				CheckDlgButton(m_hwnd, IDC_SRMM_UNDERLINE, !isUnderline ? BST_CHECKED : BST_UNCHECKED);
-			}
-			else if (ctrlId == IDC_FONTSTRIKEOUT) {
-				cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT;
-				cf.dwMask = CFM_STRIKEOUT;
-				CheckDlgButton(m_hwnd, IDC_FONTSTRIKEOUT, !isStrikeout ? BST_CHECKED : BST_UNCHECKED);
-			}
-			m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
-		}
-		break;
-
-	case IDCANCEL:
-		ShowWindow(m_pContainer->m_hwnd, SW_MINIMIZE);
-		return FALSE;
-
-	case IDC_CLOSE:
-		PostMessage(m_hwnd, WM_CLOSE, 1, 0);
-		break;
-
-	case IDC_NAME:
-		if (GetKeyState(VK_SHIFT) & 0x8000)   // copy UIN
-			Utils_ClipboardCopy(m_cache->getUIN());
-		else
-			CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(m_cache->getActiveContact()), 0);
-		break;
-
-	case IDC_TIME:
-		submenu = GetSubMenu(PluginConfig.g_hMenuContext, 2);
-		MsgWindowUpdateMenu(submenu, MENU_LOGMENU);
-
-		GetWindowRect(GetDlgItem(m_hwnd, IDC_TIME), &rc);
-
-		iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
-		return MsgWindowMenuHandler(iSelection, MENU_LOGMENU);
-
-	case IDC_PROTOMENU:
-		submenu = GetSubMenu(PluginConfig.g_hMenuContext, 4);
-		{
-			bool iOldGlobalSendFormat = g_plugin.bSendFormat;
-			int iLocalFormat = M.GetDword(m_hContact, "sendformat", 0);
-			int iNewLocalFormat = iLocalFormat;
-
-			GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
-
-			CheckMenuItem(submenu, ID_MODE_GLOBAL, !m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
-			CheckMenuItem(submenu, ID_MODE_PRIVATE, m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
-
-			// formatting menu..
-			CheckMenuItem(submenu, ID_GLOBAL_BBCODE, (g_plugin.bSendFormat) ? MF_CHECKED : MF_UNCHECKED);
-			CheckMenuItem(submenu, ID_GLOBAL_OFF, (g_plugin.bSendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
-
-			CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, (iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
-			CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, (iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED);
-			CheckMenuItem(submenu, ID_THISCONTACT_OFF, (iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED);
-
-			iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
-			switch (iSelection) {
-			case ID_MODE_GLOBAL:
-				m_bSplitterOverride = false;
-				db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 0);
-				LoadSplitter();
-				AdjustBottomAvatarDisplay();
-				DM_RecalcPictureSize();
-				Resize();
-				break;
-
-			case ID_MODE_PRIVATE:
-				m_bSplitterOverride = true;
-				db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 1);
-				LoadSplitter();
-				AdjustBottomAvatarDisplay();
-				DM_RecalcPictureSize();
-				Resize();
-				break;
-
-			case ID_GLOBAL_BBCODE:
-				g_plugin.bSendFormat = SENDFORMAT_BBCODE;
-				break;
-
-			case ID_GLOBAL_OFF:
-				g_plugin.bSendFormat = SENDFORMAT_NONE;
-				break;
-
-			case ID_THISCONTACT_GLOBALSETTING:
-				iNewLocalFormat = 0;
-				break;
-
-			case ID_THISCONTACT_BBCODE:
-				iNewLocalFormat = SENDFORMAT_BBCODE;
-				break;
-
-			case ID_THISCONTACT_OFF:
-				iNewLocalFormat = -1;
-				break;
-			}
-
-			if (iNewLocalFormat == 0)
-				db_unset(m_hContact, SRMSGMOD_T, "sendformat");
-			else if (iNewLocalFormat != iLocalFormat)
-				db_set_dw(m_hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat);
-
-			if (iNewLocalFormat != iLocalFormat || g_plugin.bSendFormat != iOldGlobalSendFormat) {
-				m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
-				if (m_SendFormat == -1)          // per contact override to disable it..
-					m_SendFormat = 0;
-				Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
-			}
-		}
-		break;
-
-	case IDC_TOGGLETOOLBAR:
-		if (lParam == 1)
-			m_pContainer->cfg.flags.m_bNoMenuBar = !m_pContainer->cfg.flags.m_bNoMenuBar;
-		else
-			m_pContainer->cfg.flags.m_bHideToolbar = !m_pContainer->cfg.flags.m_bHideToolbar;
-		m_pContainer->ApplySetting(true);
-		break;
-
-	case IDC_SENDMENU:
-		submenu = GetSubMenu(PluginConfig.g_hMenuContext, 3);
-
-		GetWindowRect(GetDlgItem(m_hwnd, IDOK), &rc);
-		CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, (m_sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, m_sendMode == 0 ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, (m_sendMode & SMODE_CONTAINER) ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, (m_sendMode & SMODE_SENDLATER) ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, (m_sendMode & SMODE_NOACK) ? MF_CHECKED : MF_UNCHECKED);
-
-		if (lParam)
-			iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
-		else
-			iSelection = HIWORD(wParam);
-
-		switch (iSelection) {
-		case ID_SENDMENU_SENDTOMULTIPLEUSERS:
-			m_sendMode ^= SMODE_MULTIPLE;
-			if (m_sendMode & SMODE_MULTIPLE)
-				DM_CreateClist();
-			else if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
-				DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
-			break;
-		case ID_SENDMENU_SENDDEFAULT:
-			m_sendMode = 0;
-			break;
-		case ID_SENDMENU_SENDTOCONTAINER:
-			m_sendMode ^= SMODE_CONTAINER;
-			RedrawWindow(m_hwnd, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW);
-			break;
-		case ID_SENDMENU_SENDLATER:
-			if (SendLater::Avail)
-				m_sendMode ^= SMODE_SENDLATER;
-			else
-				CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
-			break;
-		case ID_SENDMENU_SENDWITHOUTTIMEOUTS:
-			m_sendMode ^= SMODE_NOACK;
-			if (m_sendMode & SMODE_NOACK)
-				db_set_b(m_hContact, SRMSGMOD_T, "no_ack", 1);
-			else
-				db_unset(m_hContact, SRMSGMOD_T, "no_ack");
-			break;
-		}
-		db_set_b(m_hContact, SRMSGMOD_T, "no_ack", (uint8_t)(m_sendMode & SMODE_NOACK ? 1 : 0));
-		SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
-		if (m_sendMode & SMODE_MULTIPLE || m_sendMode & SMODE_CONTAINER) {
-			SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
-				SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
-			RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
-		}
-		else {
-			if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
-				DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
-			SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
-				SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
-			RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
-		}
-		m_pContainer->QueryClientArea(rc);
-		Resize();
-		DM_ScrollToBottom(1, 1);
-		Utils::showDlgControl(m_hwnd, IDC_MULTISPLITTER, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
-		Utils::showDlgControl(m_hwnd, IDC_CLIST, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
-		break;
-
-	case IDC_TOGGLESIDEBAR:
-		SendMessage(m_pContainer->m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
-		break;
-
-	case IDC_PIC:
-		GetClientRect(m_hwnd, &rc);
-
-		m_bEditNotesActive = !m_bEditNotesActive;
-		if (m_bEditNotesActive) {
-			int iLen = GetWindowTextLength(m_message.GetHwnd());
-			if (iLen != 0) {
-				ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("You cannot edit user notes when there are unsent messages"));
-				m_bEditNotesActive = false;
-				break;
-			}
-
-			if (!m_bIsAutosizingInput) {
-				m_iSplitterSaved = m_iSplitterY;
-				m_iSplitterY = rc.bottom / 2;
-				SendMessage(m_hwnd, WM_SIZE, 1, 1);
-			}
-
-			ptrW wszText(db_get_wsa(m_hContact, "UserInfo", "MyNotes"));
-			if (wszText != nullptr)
-				m_message.SetText(wszText);
-		}
-		else {
-			ptrW buf(m_message.GetText());
-			db_set_ws(m_hContact, "UserInfo", "MyNotes", buf);
-			m_message.SetText(L"");
-
-			if (!m_bIsAutosizingInput) {
-				m_iSplitterY = m_iSplitterSaved;
-				Resize();
-				DM_ScrollToBottom(0, 1);
-			}
-		}
-		SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
-			SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
-		RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
-
-		if (m_bEditNotesActive)
-			CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK | MB_ICONINFORMATION);
-		break;
-
-	case IDM_CLEAR:
-		tabClearLog();
-		break;
-
-	case IDC_PROTOCOL:
-		submenu = Menu_BuildContactMenu(m_hContact);
-		if (lParam == 0)
-			GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
-		else
-			GetWindowRect((HWND)lParam, &rc);
-
-		iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
-		if (iSelection)
-			Clist_MenuProcessCommand(LOWORD(iSelection), MPCF_CONTACTMENU, m_hContact);
-
-		DestroyMenu(submenu);
-		break;
-
-	// error control
-	case IDC_CANCELSEND:
-		DM_ErrorDetected(MSGERROR_CANCEL, 0);
-		break;
-
-	case IDC_RETRY:
-		DM_ErrorDetected(MSGERROR_RETRY, 0);
-		break;
-
-	case IDC_MSGSENDLATER:
-		DM_ErrorDetected(MSGERROR_SENDLATER, 0);
-		break;
-
-	case IDC_SELFTYPING:
-		if (AllowTyping()) {
-			int iCurrentTypingMode = g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew);
-			if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) {
-				DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
-				m_nTypeMode = PROTOTYPE_SELFTYPING_OFF;
-			}
-			g_plugin.setByte(m_hContact, SRMSGSET_TYPING, (uint8_t)!iCurrentTypingMode);
-		}
-		break;
-
-	default:
-		return 0;
-	}
-	return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// initialize rich edit control (log and edit control) for both MUC and
-// standard IM session windows.
-
-void CMsgDialog::DM_InitRichEdit()
-{
-	char *szStreamOut = nullptr;
-	if (!isChat() && GetWindowTextLength(m_message.GetHwnd()) > 0)
-		szStreamOut = m_message.GetRichTextRtf();
-	SetWindowText(m_message.GetHwnd(), L"");
-
-	m_pLog->UpdateOptions();
-
-	m_message.SendMsg(EM_SETBKGNDCOLOR, 0, m_pContainer->m_theme.inputbg);
-
-	CHARFORMAT2 cf2 = {};
-	cf2.cbSize = sizeof(cf2);
-
-	if (isChat()) {
-		LOGFONTW lf;
-		COLORREF inputcharcolor;
-		LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor);
-
-		cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR;
-		cf2.crTextColor = inputcharcolor;
-		cf2.bCharSet = lf.lfCharSet;
-		cf2.crBackColor = m_pContainer->m_theme.inputbg;
-		wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
-		cf2.dwEffects = 0;
-		cf2.wWeight = (uint16_t)lf.lfWeight;
-		cf2.bPitchAndFamily = lf.lfPitchAndFamily;
-		cf2.yHeight = abs(lf.lfHeight) * 15;
-	}
-	else {
-		LOGFONTW lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
-		COLORREF inputcharcolor = m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA];
-
-		for (auto &it : Utils::rtf_clrs)
-			if (it->clr == inputcharcolor)
-				inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1);
-
-		cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC;
-		cf2.crTextColor = inputcharcolor;
-		cf2.bCharSet = lf.lfCharSet;
-		wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
-		cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0);
-		cf2.wWeight = (uint16_t)lf.lfWeight;
-		cf2.bPitchAndFamily = lf.lfPitchAndFamily;
-		cf2.yHeight = abs(lf.lfHeight) * 15;
-	}
-	m_message.SendMsg(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
-	m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); /* WINE: fix send colour text. */
-	m_message.SendMsg(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2); /* WINE: fix send colour text. */
-
-	// setup the rich edit control(s)
-	// LOG is always set to RTL, because this is needed for proper bidirectional operation later.
-	// The real text direction is then enforced by the streaming code which adds appropiate paragraph
-	// and textflow formatting commands to the
-	PARAFORMAT2 pf2;
-	memset(&pf2, 0, sizeof(PARAFORMAT2));
-	pf2.cbSize = sizeof(pf2);
-	pf2.wEffects = PFE_RTLPARA;
-	pf2.dwMask = PFM_RTLPARA;
-	if (FindRTLLocale())
-		m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
-	if (!(m_dwFlags & MWF_LOG_RTL)) {
-		pf2.wEffects = 0;
-		m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
-	}
-	m_message.SendMsg(EM_SETLANGOPTIONS, 0, (LPARAM)m_message.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
-
-	if (m_dwFlags & MWF_LOG_RTL)
-		SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
-	else
-		SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
-
-	if (szStreamOut != nullptr) {
-		SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
-		m_message.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut);
-		mir_free(szStreamOut);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// set the states of defined database action buttons(only if button is a toggle)
-
-void CMsgDialog::DM_SetDBButtonStates()
-{
-	ButtonItem *buttonItem = m_pContainer->m_buttonItems;
-	MCONTACT hFinalContact = 0;
-	HWND hwndContainer = m_pContainer->m_hwnd;
-
-	while (buttonItem) {
-		HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId);
-
-		if (buttonItem->pfnCallback)
-			buttonItem->pfnCallback(buttonItem, m_hwnd, this, hWnd);
-
-		if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
-			buttonItem = buttonItem->nextItem;
-			continue;
-		}
-
-		BOOL result = FALSE;
-		char *szModule = buttonItem->szModule;
-		char *szSetting = buttonItem->szSetting;
-		if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
-			if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
-				szModule = Proto_GetBaseAccountName(m_hContact);
-			hFinalContact = m_hContact;
-		}
-		else hFinalContact = 0;
-
-		switch (buttonItem->type) {
-		case DBVT_BYTE:
-			result = (db_get_b(hFinalContact, szModule, szSetting, 0) == buttonItem->bValuePush[0]);
-			break;
-		case DBVT_WORD:
-			result = (db_get_w(hFinalContact, szModule, szSetting, 0) == *((uint16_t *)&buttonItem->bValuePush));
-			break;
-		case DBVT_DWORD:
-			result = (db_get_dw(hFinalContact, szModule, szSetting, 0) == *((uint32_t *)&buttonItem->bValuePush));
-			break;
-		case DBVT_ASCIIZ:
-			ptrA szValue(db_get_sa(hFinalContact, szModule, szSetting));
-			if (szValue)
-				result = !mir_strcmp((char*)buttonItem->bValuePush, szValue);
-			break;
-		}
-		SendMessage(hWnd, BM_SETCHECK, result, 0);
-		buttonItem = buttonItem->nextItem;
-	}
-}
-
-void CMsgDialog::DM_ScrollToBottom(WPARAM wParam, LPARAM lParam)
-{
-	if (m_bScrollingDisabled)
-		return;
-
-	if (IsIconic(m_pContainer->m_hwnd))
-		m_bDeferredScroll = true;
-
-	if (m_iLogMode == WANT_BUILTIN_LOG)
-		((CLogWindow *)m_pLog)->ScrollToBottom(wParam != 0, lParam != 0);
-	else
-		m_pLog->ScrollToBottom();
-}
-
-void CMsgDialog::DM_RecalcPictureSize()
-{
-	HBITMAP hbm = ((m_pPanel.isActive()) && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
-	if (hbm) {
-		BITMAP bminfo;
-		GetObject(hbm, sizeof(bminfo), &bminfo);
-		CalcDynamicAvatarSize(&bminfo);
-		Resize();
-	}
-	else m_pic.cy = m_pic.cx = 60;
-}
-
-void CMsgDialog::DM_UpdateLastMessage() const
-{
-	if (m_pContainer->m_hwndStatus == nullptr || m_pContainer->m_hwndActive != m_hwnd)
-		return;
-
-	wchar_t szBuf[100];
-	if (m_bShowTyping) {
-		SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
-		mir_snwprintf(szBuf, TranslateT("%s is typing a message..."), m_cache->getNick());
-	}
-	else if (m_bStatusSet) {
-		SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
-		SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
-		return;
-	}
-	else {
-		SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
-
-		if (m_pContainer->cfg.flags.m_bUinStatusBar)
-			mir_snwprintf(szBuf, L"UID: %s", m_cache->getUIN());
-		else if (m_lastMessage) {
-			wchar_t date[64], time[64];
-			TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"d", date, _countof(date), 0);
-			if (m_pContainer->cfg.flags.m_bUinStatusBar && mir_wstrlen(date) > 6)
-				date[mir_wstrlen(date) - 5] = 0;
-			TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"t", time, _countof(time), 0);
-			mir_snwprintf(szBuf, TranslateT("Last received: %s at %s"), date, time);
-		}
-		else szBuf[0] = 0;
-	}
-
-	SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szBuf);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// create embedded contact list control
-
-HWND CMsgDialog::DM_CreateClist()
-{
-	if (!SendLater::Avail) {
-		CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
-		m_sendMode &= ~SMODE_MULTIPLE;
-		return nullptr;
-	}
-
-	HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248, 184, 0, 30, 30, m_hwnd, (HMENU)IDC_CLIST, g_plugin.getInst(), nullptr);
-	SendMessage(hwndClist, WM_TIMER, 14, 0);
-	HANDLE hItem = (HANDLE)SendMessage(hwndClist, CLM_FINDCONTACT, m_hContact, 0);
-
-	SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT);
-	SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL));
-
-	if (!g_plugin.bAllowOfflineMultisend)
-		SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE);
-
-	if (hItem)
-		SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM)hItem, 1);
-
-	SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, Clist::HideEmptyGroups, 0);
-	SendMessage(hwndClist, CLM_SETUSEGROUPS, Clist::UseGroups, 0);
-	SendMessage(hwndClist, CLM_FIRST + 106, 0, 1);
-	SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0);
-	if (hwndClist)
-		RedrawWindow(hwndClist, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
-	return hwndClist;
-}
-
-LRESULT CMsgDialog::DM_MouseWheelHandler(WPARAM wParam, LPARAM lParam)
-{
-	POINT pt;
-	GetCursorPos(&pt);
-
-	RECT rc;
-	GetWindowRect(m_message.GetHwnd(), &rc);
-	if (PtInRect(&rc, pt))
-		return 1;
-
-	if (isChat()) {					// scroll nick list by just hovering it
-		RECT rcNicklist;
-		GetWindowRect(m_nickList.GetHwnd(), &rcNicklist);
-		if (PtInRect(&rcNicklist, pt)) {
-			m_nickList.SendMsg(WM_MOUSEWHEEL, wParam, lParam);
-			return 0;
-		}
-	}
-
-	GetWindowRect(m_pLog->GetHwnd(), &rc);
-	if (PtInRect(&rc, pt)) {
-		short wDirection = (short)HIWORD(wParam);
-
-		if (LOWORD(wParam) & MK_SHIFT || M.GetByte("fastscroll", 0)) {
-			if (wDirection < 0)
-				SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
-			else if (wDirection > 0)
-				SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
-		}
-		else SendMessage(m_pLog->GetHwnd(), WM_MOUSEWHEEL, wParam, lParam);
-		return 0;
-	}
-
-	if (GetTabItemFromMouse(m_pContainer->m_hwndTabs, &pt) != -1) {
-		SendMessage(m_pContainer->m_hwndTabs, WM_MOUSEWHEEL, wParam, -1);
-		return 0;
-	}
-	return 1;
-}
-
-void CMsgDialog::DM_FreeTheme()
-{
-	if (m_hTheme) {
-		CloseThemeData(m_hTheme);
-		m_hTheme = nullptr;
-	}
-	if (m_hThemeIP) {
-		CloseThemeData(m_hThemeIP);
-		m_hThemeIP = nullptr;
-	}
-	if (m_hThemeToolbar) {
-		CloseThemeData(m_hThemeToolbar);
-		m_hThemeToolbar = nullptr;
-	}
-}
-
-void CMsgDialog::DM_ThemeChanged()
-{
-	CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY];
-	CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA];
-
-	m_hTheme = OpenThemeData(m_hwnd, L"EDIT");
-
-	if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_log->IGNORED)) {
-		if (m_iLogMode == WANT_BUILTIN_LOG)
-			LOG()->DisableStaticEdge();
-
-		if (isChat())
-			SetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
-	}
-	
-	if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_msg->IGNORED))
-		SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
-
-	m_hThemeIP = M.isAero() ? OpenThemeData(m_hwnd, L"ButtonStyle") : nullptr;
-	m_hThemeToolbar = (M.isAero() || (!CSkin::m_skinEnabled && M.isVSThemed())) ? OpenThemeData(m_hwnd, L"REBAR") : nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// send out message typing notifications (MTN) when the
-// user is typing/editing text in the message input area.
-
-void CMsgDialog::DM_NotifyTyping(int mode)
-{
-	const char *szProto = m_cache->getActiveProto();
-	MCONTACT hContact = m_cache->getActiveContact();
-
-	// editing user notes or preparing a message for queued delivery -> don't send MTN
-	if (m_bEditNotesActive || (m_sendMode & SMODE_SENDLATER))
-		return;
-
-	// allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle)
-	if (!g_plugin.getByte(hContact, SRMSGSET_TYPING, g_plugin.bTypingNew))
-		return;
-
-	if (szProto == nullptr) // should not, but who knows...
-		return;
-
-	// check status and capabilities of the protocol
-	uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
-	if (!(typeCaps & PF4_SUPPORTTYPING))
-		return;
-
-	if (isChat()) {
-		m_nTypeMode = mode;
-		Chat_DoEventHook(m_si, GC_USER_TYPNOTIFY, 0, 0, m_nTypeMode);
-	}
-	else {
-		uint32_t protoStatus = Proto_GetStatus(szProto);
-		if (protoStatus < ID_STATUS_ONLINE)
-			return;
-
-		// check visibility/invisibility lists to not "accidentially" send MTN to contacts who
-		// should not see them (privacy issue)
-		uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
-		if (protoCaps & PF1_VISLIST && db_get_w(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
-			return;
-
-		if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && db_get_w(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
-			return;
-
-		// don't send to contacts which are not permanently added to the contact list,
-		// unless the option to ignore added status is set.
-		if (!Contact::OnList(m_hContact) && !g_plugin.bTypingUnknown)
-			return;
-
-		// End user check
-		m_nTypeMode = mode;
-		CallService(MS_PROTO_SELFISTYPING, hContact, m_nTypeMode);
-	}
-}
-
-void CMsgDialog::DM_OptionsApplied(bool bRemakeLog)
-{
-	m_szMicroLf[0] = 0;
-	if (!m_pContainer->m_theme.isPrivate) {
-		m_pContainer->LoadThemeDefaults();
-		m_dwFlags = m_pContainer->m_theme.dwFlags;
-	}
-
-	LoadLocalFlags();
-	m_hTimeZone = TimeZone_CreateByContact(m_hContact, nullptr, TZF_KNOWNONLY);
-
-	m_bShowUIElements = (m_pContainer->cfg.flags.m_bHideToolbar) == 0;
-	m_bSplitterOverride = M.GetByte(m_hContact, "splitoverride", 0) != 0;
-	m_pPanel.getVisibility();
-
-	// small inner margins (padding) for the text areas
-	m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
-
-	GetSendFormat();
-	SetDialogToType();
-	SendMessage(m_hwnd, DM_CONFIGURETOOLBAR, 0, 0);
-
-	DM_InitRichEdit();
-	if (m_hwnd == m_pContainer->m_hwndActive)
-		SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
-	InvalidateRect(m_message.GetHwnd(), nullptr, FALSE);
-	if (bRemakeLog) {
-		if (IsIconic(m_pContainer->m_hwnd))
-			m_bDeferredRemakeLog = true;
-		else if (isChat())
-			RedrawLog();
-		else
-			RemakeLog();
-	}
-
-	ShowWindow(m_hwndPanelPicParent, SW_SHOW);
-	EnableWindow(m_hwndPanelPicParent, TRUE);
-
-	UpdateWindowIcon();
-}
-	
-void CMsgDialog::DM_Typing(bool fForceOff)
-{
-	HWND hwndContainer = m_pContainer->m_hwnd;
-	HWND hwndStatus = m_pContainer->m_hwndStatus;
-
-	if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - m_nLastTyping > TIMEOUT_TYPEOFF)
-		DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
-
-	if (m_bShowTyping == 1) {
-		if (m_nTypeSecs > 0) {
-			m_nTypeSecs--;
-			if (GetForegroundWindow() == hwndContainer)
-				UpdateWindowIcon();
-		}
-		else {
-			if (!fForceOff) {
-				m_bShowTyping = 2;
-				m_nTypeSecs = 86400;
-
-				if (!isChat())
-					mir_snwprintf(m_wszStatusBar, TranslateT("%s has entered text."), m_cache->getNick());
-
-				if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd)
-					SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
-			}
-			UpdateWindowIcon();
-			HandleIconFeedback(this, (HICON)-1);
-			CMsgDialog *dat_active = (CMsgDialog*)GetWindowLongPtr(m_pContainer->m_hwndActive, GWLP_USERDATA);
-			if (dat_active && !dat_active->isChat())
-				m_pContainer->UpdateTitle(0);
-			else
-				m_pContainer->UpdateTitle(0, dat_active);
-			if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
-				m_pContainer->ReflashContainer();
-		}
-	}
-	else if (m_bShowTyping == 2) {
-		if (m_nTypeSecs > 0)
-			m_nTypeSecs--;
-		else {
-			m_wszStatusBar[0] = 0;
-			m_bShowTyping = 0;
-		}
-		tabUpdateStatusBar();
-	}
-	else if (m_nTypeSecs > 0) {
-		mir_snwprintf(m_wszStatusBar, TranslateT("%s is typing a message"), 
-			(m_pUserTyping) ? m_pUserTyping->pszNick : m_cache->getNick());
-
-		m_nTypeSecs--;
-		if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
-			SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
-			SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
-		}
-		if (IsIconic(hwndContainer) || !IsActive()) {
-			SetWindowText(hwndContainer, m_wszStatusBar);
-			m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
-			if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
-				m_pContainer->ReflashContainer();
-		}
-
-		if (m_pContainer->m_hwndActive != m_hwnd) {
-			if (m_bCanFlashTab)
-				m_iFlashIcon = PluginConfig.g_IconTypingEvent;
-			HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
-		}
-		else { // active tab may show icon if status bar is disabled
-			if (!hwndStatus) {
-				if (TabCtrl_GetItemCount(m_hwndParent) > 1 || !m_pContainer->cfg.flags.m_bHideTabs)
-					HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
-			}
-		}
-		if ((GetForegroundWindow() != hwndContainer) || (m_pContainer->m_hwndStatus == nullptr) || (m_pContainer->m_hwndActive != m_hwnd))
-			m_pContainer->SetIcon(this, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
-
-		m_bShowTyping = 1;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// sync splitter position for all open sessions.
-// This cares about private / per container / MUC <> IM splitter syncing and everything.
-// called from IM and MUC windows via DM_SPLITTERGLOBALEVENT
-
-int CMsgDialog::DM_SplitterGlobalEvent(WPARAM wParam, LPARAM lParam)
-{
-	CMsgDialog *srcDat = PluginConfig.lastSPlitterPos.pSrcDat;
-	TContainerData *srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer;
-	bool fCntGlobal = (!m_pContainer->cfg.fPrivate ? true : false);
-
-	if (m_bIsAutosizingInput)
-		return 0;
-
-	RECT rcWin;
-	GetWindowRect(m_hwnd, &rcWin);
-
-	LONG newPos;
-	if (wParam == 0 && lParam == 0) {
-		if (m_bSplitterOverride && this != srcDat)
-			return 0;
-
-		if (srcDat->isChat() == isChat())
-			newPos = PluginConfig.lastSPlitterPos.pos;
-		else if (!srcDat->isChat() && isChat())
-			newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
-		else if (srcDat->isChat() && !isChat())
-			newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
-		else
-			newPos = 0;
-
-		if (this == srcDat) {
-			m_pContainer->cfg.iSplitterY = m_iSplitterY;
-			if (fCntGlobal)
-				SaveSplitter();
-			return 0;
-		}
-
-		if (!fCntGlobal && m_pContainer != srcCnt)
-			return 0;
-		if (srcCnt->cfg.fPrivate && m_pContainer != srcCnt)
-			return 0;
-
-		// for inactive sessions, delay the splitter repositioning until they become
-		// active (faster, avoid redraw/resize problems for minimized windows)
-		if (IsIconic(m_pContainer->m_hwnd) || m_pContainer->m_hwndActive != m_hwnd) {
-			m_bDelayedSplitter = true;
-			m_wParam = newPos;
-			m_lParam = PluginConfig.lastSPlitterPos.lParam;
-			return 0;
-		}
-	}
-	else newPos = wParam;
-
-	LoadSplitter();
-	AdjustBottomAvatarDisplay();
-	DM_RecalcPictureSize();
-	Resize();
-	DM_ScrollToBottom(1, 1);
-	if (this != srcDat)
-		UpdateToolbarBG();
-	return 0;
-}
-
-void CMsgDialog::DM_AddDivider()
-{
-	if (!m_bDividerSet && g_plugin.bUseDividers)
-		if (GetWindowTextLength(m_pLog->GetHwnd()) > 0)
-			m_bDividerSet = m_bDividerWanted = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// incoming event handler
-
-void CMsgDialog::DM_EventAdded(WPARAM, LPARAM lParam)
-{
-	MEVENT hDbEvent = (MEVENT)lParam;
-
-	DBEVENTINFO dbei = {};
-	db_event_get(hDbEvent, &dbei);
-	if (m_hDbEventFirst == 0)
-		m_hDbEventFirst = hDbEvent;
-
-	bool bIsStatusChangeEvent = IsStatusEvent(dbei.eventType);
-	bool bDisableNotify = (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ));
-
-	if (!DbEventIsShown(&dbei))
-		return;
-
-	if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
-		m_lastMessage = dbei.timestamp;
-		m_wszStatusBar[0] = 0;
-		if (m_bShowTyping) {
-			m_nTypeSecs = 0;
-			DM_Typing(true);
-			m_bShowTyping = 0;
-		}
-		HandleIconFeedback(this, (HICON)-1);
-		if (m_pContainer->m_hwndStatus)
-			PostMessage(m_hwnd, DM_UPDATELASTMESSAGE, 0, 0);
-	}
-
-	// set the message log divider to mark new (maybe unseen) messages, if the container has
-	// been minimized or in the background.
-	if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
-		if (g_plugin.bDividersUsePopupConfig && g_plugin.bUseDividers) {
-			if (!MessageWindowOpened(m_hContact, nullptr))
-				DM_AddDivider();
-		}
-		else if (g_plugin.bUseDividers) {
-			if (!m_pContainer->IsActive())
-				DM_AddDivider();
-			else if (m_pContainer->m_hwndActive != m_hwnd)
-				DM_AddDivider();
-		}
-
-		if (IsWindowVisible(m_pContainer->m_hwnd))
-			m_pContainer->m_bHidden = false;
-	}
-	m_cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0);
-
-	if (hDbEvent != m_hDbEventFirst || isChat())
-		StreamEvents(hDbEvent, 1, 1);
-	else
-		RemakeLog();
-
-	// handle tab flashing
-	if (!bDisableNotify && !bIsStatusChangeEvent)
-		if ((TabCtrl_GetCurSel(m_hwndParent) != m_iTabID) && !(dbei.flags & DBEF_SENT)) {
-			switch (dbei.eventType) {
-			case EVENTTYPE_MESSAGE:
-				m_iFlashIcon = PluginConfig.g_IconMsgEvent;
-				break;
-			case EVENTTYPE_FILE:
-				m_iFlashIcon = PluginConfig.g_IconFileEvent;
-				break;
-			default:
-				m_iFlashIcon = PluginConfig.g_IconMsgEvent;
-				break;
-			}
-			timerFlash.Start(TIMEOUT_FLASHWND);
-			m_bCanFlashTab = true;
-		}
-
-	// try to flash the contact list...
-	if (!bDisableNotify)
-		FlashOnClist(hDbEvent, &dbei);
-
-	// autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch)
-	// never switch for status changes...
-	if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
-		if (g_plugin.bAutoSwitchTabs && m_pContainer->m_hwndActive != m_hwnd) {
-			if ((IsIconic(m_pContainer->m_hwnd) && !IsZoomed(m_pContainer->m_hwnd)) || (g_plugin.bHideOnClose && !IsWindowVisible(m_pContainer->m_hwnd))) {
-				int iItem = GetTabIndexFromHWND(GetParent(m_hwnd), m_hwnd);
-				if (iItem >= 0) {
-					TabCtrl_SetCurSel(m_hwndParent, iItem);
-					ShowWindow(m_pContainer->m_hwndActive, SW_HIDE);
-					m_pContainer->m_hwndActive = m_hwnd;
-					m_pContainer->UpdateTitle(m_hContact);
-					m_pContainer->cfg.flags.m_bDeferredTabSelect = true;
-				}
-			}
-		}
-	}
-
-	// flash window if it is not focused
-	if (!bDisableNotify && !bIsStatusChangeEvent)
-		if (!IsActive() && !(dbei.flags & DBEF_SENT)) {
-			if (!m_pContainer->cfg.flags.m_bNoFlash && !m_pContainer->IsActive())
-				m_pContainer->FlashContainer(1, 0);
-			m_pContainer->SetIcon(this, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
-			m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
-		}
-
-	// play a sound
-	if (!bDisableNotify && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT)))
-		PlayIncomingSound();
-	
-	if (m_pWnd)
-		m_pWnd->Invalidate();
-}
-
-void CMsgDialog::DM_HandleAutoSizeRequest(REQRESIZE* rr)
-{
-	if (rr == nullptr || GetForegroundWindow() != m_pContainer->m_hwnd)
-		return;
-
-	if (!m_bIsAutosizingInput || m_iInputAreaHeight == -1)
-		return;
-
-	LONG heightLimit = M.GetDword("autoSplitMinLimit", 0);
-	LONG iNewHeight = rr->rc.bottom - rr->rc.top;
-
-	if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
-		iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2);
-
-	if (heightLimit && iNewHeight < heightLimit)
-		iNewHeight = heightLimit;
-
-	if (iNewHeight == m_iInputAreaHeight)
-		return;
-
-	RECT rc;
-	GetClientRect(m_hwnd, &rc);
-	LONG cy = rc.bottom - rc.top;
-	LONG panelHeight = (m_pPanel.isActive() ? m_pPanel.getHeight() : 0);
-
-	if (iNewHeight > (cy - panelHeight) / 2)
-		iNewHeight = (cy - panelHeight) / 2;
-
-	m_dynaSplitter = iNewHeight - DPISCALEY_S(2);
-	if (m_pContainer->cfg.flags.m_bBottomToolbar)
-		m_dynaSplitter += DPISCALEY_S(22);
-	m_iSplitterY = m_dynaSplitter + DPISCALEY_S(34);
-	DM_RecalcPictureSize();
-
-	m_iInputAreaHeight = iNewHeight;
-	UpdateToolbarBG();
-	DM_ScrollToBottom(1, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// status icon stuff (by sje, used for indicating encryption status in the status bar
-// this is now part of the message window api
-
-
-static int OnSrmmIconChanged(WPARAM hContact, LPARAM)
-{
-	if (hContact == 0)
-		Srmm_Broadcast(DM_STATUSICONCHANGE, 0, 0);
-	else {
-		HWND hwnd = Srmm_FindWindow(hContact);
-		if (hwnd)
-			PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
-	}
-	return 0;
-}
-
-void CMsgDialog::DrawStatusIcons(HDC hDC, const RECT &rc, int gap)
-{
-	int x = rc.left;
-	int y = (rc.top + rc.bottom - PluginConfig.m_smcxicon) >> 1;
-
-	SetBkMode(hDC, TRANSPARENT);
-
-	int nIcon = 0;
-	while (StatusIconData *sid = Srmm_GetNthIcon(m_hContact, nIcon++)) {
-		if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
-			if (sid->dwId == MSG_ICON_SOUND) {
-				DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS],
-					PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
-
-				DrawIconEx(hDC, x, y, m_pContainer->cfg.flags.m_bNoSound ?
-					PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled,
-					PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
-			}
-			else if (sid->dwId == MSG_ICON_UTN) {
-				if (AllowTyping()) {
-					DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
-
-					DrawIconEx(hDC, x, y, g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew) ?
-						PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
-				}
-				else CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50);
-			}
-		}
-		else {
-			HICON hIcon;
-			if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled)
-				hIcon = sid->hIconDisabled;
-			else
-				hIcon = sid->hIcon;
-
-			if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled == nullptr)
-				CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIcon, 50);
-			else
-				DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
-		}
-
-		x += PluginConfig.m_smcxicon + gap;
-	}
-}
-
-void CMsgDialog::CheckStatusIconClick(POINT pt, const RECT &rc, int gap, int code)
-{
-	if (code == NM_CLICK || code == NM_RCLICK) {
-		POINT	ptScreen;
-		GetCursorPos(&ptScreen);
-		if (!PtInRect(&rcLastStatusBarClick, ptScreen))
-			return;
-	}
-
-	UINT iconNum = (pt.x - (rc.left + 0)) / (PluginConfig.m_smcxicon + gap);
-	
-	StatusIconData *sid = Srmm_GetNthIcon(m_hContact, iconNum);
-	if (sid == nullptr)
-		return;
-
-	if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
-		if (sid->dwId == MSG_ICON_SOUND && code != NM_RCLICK) {
-			if (GetKeyState(VK_SHIFT) & 0x8000) {
-				for (TContainerData *p = pFirstContainer; p; p = p->pNext) {
-					p->cfg.flags.m_bNoSound = m_pContainer->cfg.flags.m_bNoSound;
-					InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
-				}
-			}
-			else {
-				m_pContainer->cfg.flags.m_bNoSound = !m_pContainer->cfg.flags.m_bNoSound;
-				InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
-			}
-		}
-		else if (sid->dwId == MSG_ICON_UTN && code != NM_RCLICK && AllowTyping()) {
-			SendMessage(m_pContainer->m_hwndActive, WM_COMMAND, IDC_SELFTYPING, 0);
-			InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
-		}
-	}
-	else {
-		StatusIconClickData sicd = { sizeof(sicd) };
-		GetCursorPos(&sicd.clickLocation);
-		sicd.dwId = sid->dwId;
-		sicd.szModule = sid->szModule;
-		sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
-		Srmm_ClickStatusIcon(m_hContact, &sicd);
-		InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
-	}
-}
-
-void CMsgDialog::DM_ErrorDetected(int type, int flag)
-{
-	switch (type) {
-	case MSGERROR_CANCEL:
-	case MSGERROR_SENDLATER:
-		if (m_bErrorState) {
-			m_cache->saveHistory();
-			if (type == MSGERROR_SENDLATER)
-				sendQueue->doSendLater(m_iCurrentQueueError, this); // to be implemented at a later time
-			m_iOpenJobs--;
-			sendQueue->dec();
-			if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS)
-				sendQueue->clearJob(m_iCurrentQueueError);
-			m_iCurrentQueueError = -1;
-			sendQueue->showErrorControls(this, FALSE);
-			if (type != MSGERROR_CANCEL || flag == 0)
-				m_message.SetText(L"");
-			sendQueue->checkQueue(this);
-			int iNextFailed = sendQueue->findNextFailed(this);
-			if (iNextFailed >= 0)
-				sendQueue->handleError(this, iNextFailed);
-		}
-		break;
-
-	case MSGERROR_RETRY:
-		if (m_bErrorState) {
-			int resent = 0;
-
-			m_cache->saveHistory();
-			if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) {
-				SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
-				if (job->iSendId == 0 && job->hContact == 0)
-					break;
-
-				job->iSendId = ProtoChainSend(job->hContact, PSS_MESSAGE, job->dwFlags, (LPARAM)job->szSendBuffer);
-				resent++;
-			}
-
-			if (resent) {
-				SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
-
-				SetTimer(m_hwnd, TIMERID_MSGSEND + m_iCurrentQueueError, PluginConfig.m_MsgTimeout, nullptr);
-				job->iStatus = SendQueue::SQ_INPROGRESS;
-				m_iCurrentQueueError = -1;
-				sendQueue->showErrorControls(this, FALSE);
-				m_message.SetText(L"");
-				sendQueue->checkQueue(this);
-
-				int iNextFailed = sendQueue->findNextFailed(this);
-				if (iNextFailed >= 0)
-					sendQueue->handleError(this, iNextFailed);
-			}
-		}
-	}
-}
-
-int SI_InitStatusIcons()
-{
-	StatusIconData sid = {};
-	sid.szModule = MSG_ICON_MODULE;
-	sid.dwId = MSG_ICON_SOUND; // Sounds
-	Srmm_AddIcon(&sid, &g_plugin);
-
-	sid.dwId = MSG_ICON_UTN;
-	Srmm_AddIcon(&sid, &g_plugin);
-
-	HookEvent(ME_MSG_ICONSCHANGED, OnSrmmIconChanged);
-	return 0;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// these are generic message handlers which are used by the message dialog window procedure.
+// calling them directly instead of using SendMessage() is faster.
+// also contains various callback functions for custom buttons
+
+#include "stdafx.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Save message log for given session as RTF document
+
+/*
+void CMsgDialog::DM_SaveLogAsRTF() const
+{
+	if (m_hwndIEView != nullptr) {
+		IEVIEWEVENT event = { sizeof(event) };
+		event.hwnd = m_hwndIEView;
+		event.hContact = m_hContact;
+		event.iType = IEE_SAVE_DOCUMENT;
+		CallService(MS_IEVIEW_EVENT, 0, (LPARAM)&event);
+	}
+	else {
+		wchar_t szFilter[MAX_PATH], szFilename[MAX_PATH];
+		mir_snwprintf(szFilter, L"%s%c*.rtf%c%c", TranslateT("Rich Edit file"), 0, 0, 0);
+		mir_snwprintf(szFilename, L"%s.rtf", m_cache->getNick());
+
+		Utils::sanitizeFilename(szFilename);
+
+		wchar_t szInitialDir[MAX_PATH + 2];
+		mir_snwprintf(szInitialDir, L"%s%s\\", M.getDataPath(), L"\\Saved message logs");
+		CreateDirectoryTreeW(szInitialDir);
+
+		OPENFILENAME ofn = { 0 };
+		ofn.lStructSize = sizeof(ofn);
+		ofn.hwndOwner = m_hwnd;
+		ofn.lpstrFile = szFilename;
+		ofn.lpstrFilter = szFilter;
+		ofn.lpstrInitialDir = szInitialDir;
+		ofn.nMaxFile = MAX_PATH;
+		ofn.Flags = OFN_HIDEREADONLY;
+		ofn.lpstrDefExt = L"rtf";
+		if (GetSaveFileName(&ofn)) {
+			EDITSTREAM stream = { 0 };
+			stream.dwCookie = (DWORD_PTR)szFilename;
+			stream.dwError = 0;
+			stream.pfnCallback = Utils::StreamOut;
+			m_rtf.SendMsg(EM_STREAMOUT, SF_RTF | SF_USECODEPAGE, (LPARAM)&stream);
+		}
+	}
+}
+*/
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// checks if the balloon tooltip can be dismissed (usually called by WM_MOUSEMOVE events)
+
+void CMsgDialog::DM_DismissTip(const POINT& pt)
+{
+	if (!IsWindowVisible(m_hwndTip))
+		return;
+
+	RECT rc;
+	GetWindowRect(m_hwndTip, &rc);
+	if (PtInRect(&rc, pt))
+		return;
+
+	if (abs(pt.x - m_ptTipActivation.x) > 5 || abs(pt.y - m_ptTipActivation.y) > 5) {
+		SendMessage(m_hwndTip, TTM_TRACKACTIVATE, FALSE, 0);
+		m_ptTipActivation.x = m_ptTipActivation.y = 0;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// initialize the balloon tooltip for message window notifications
+
+void CMsgDialog::DM_InitTip()
+{
+	m_hwndTip = CreateWindowEx(0, TOOLTIPS_CLASS, nullptr, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT, CW_USEDEFAULT,
+		CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, nullptr, g_plugin.getInst(), (LPVOID)nullptr);
+
+	memset(&ti, 0, sizeof(ti));
+	ti.cbSize = sizeof(ti);
+	ti.lpszText = TranslateT("No status message");
+	ti.hinst = g_plugin.getInst();
+	ti.hwnd = m_hwnd;
+	ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_TRANSPARENT;
+	ti.uId = (UINT_PTR)m_hwnd;
+	SendMessage(m_hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+	SetWindowPos(m_hwndTip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// checks generic hotkeys valid for both IM and MUC sessions
+//
+// returns 1 for handled hotkeys, 0 otherwise.
+
+bool CMsgDialog::DM_GenericHotkeysCheck(MSG *message)
+{
+	LRESULT mim_hotkey_check = Hotkey_Check(message, TABSRMM_HK_SECTION_GENERIC);
+
+	switch (mim_hotkey_check) {
+	case TABSRMM_HK_PASTEANDSEND:
+		HandlePasteAndSend();
+		return true;
+
+	case TABSRMM_HK_HISTORY:
+		m_btnHistory.Click();
+		return true;
+
+	case TABSRMM_HK_CONTAINEROPTIONS:
+		m_pContainer->OptionsDialog();
+		return true;
+
+	case TABSRMM_HK_TOGGLEINFOPANEL:
+		m_pPanel.setActive(!m_pPanel.isActive());
+		m_pPanel.showHide();
+		return true;
+
+	case TABSRMM_HK_TOGGLETOOLBAR:
+		SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLETOOLBAR, 0);
+		return true;
+
+	case TABSRMM_HK_CLEARLOG:
+		tabClearLog();
+		return true;
+
+	case TABSRMM_HK_TOGGLESIDEBAR:
+		if (m_pContainer->m_pSideBar->isActive())
+			SendMessage(m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+		return true;
+
+	case TABSRMM_HK_CLOSE_OTHER:
+		CloseOtherTabs(m_pContainer->m_hwndTabs, *this);
+		return true;
+	}
+	return false;
+}
+
+LRESULT CMsgDialog::DM_MsgWindowCmdHandler(UINT cmd, WPARAM wParam, LPARAM lParam)
+{
+	RECT  rc;
+	int   iSelection;
+	HMENU submenu;
+
+	switch (cmd) {
+	case IDC_SRMM_BOLD:
+	case IDC_SRMM_ITALICS:
+	case IDC_SRMM_UNDERLINE:
+	case IDC_FONTSTRIKEOUT:
+		if (m_SendFormat != 0) { // dont use formatting if disabled
+			CHARFORMAT2 cf, cfOld;
+			memset(&cf, 0, sizeof(CHARFORMAT2));
+			memset(&cfOld, 0, sizeof(CHARFORMAT2));
+			cfOld.cbSize = cf.cbSize = sizeof(CHARFORMAT2);
+			cfOld.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT;
+			m_message.SendMsg(EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfOld);
+			BOOL isBold = (cfOld.dwEffects & CFE_BOLD) && (cfOld.dwMask & CFM_BOLD);
+			BOOL isItalic = (cfOld.dwEffects & CFE_ITALIC) && (cfOld.dwMask & CFM_ITALIC);
+			BOOL isUnderline = (cfOld.dwEffects & CFE_UNDERLINE) && (cfOld.dwMask & CFM_UNDERLINE);
+			BOOL isStrikeout = (cfOld.dwEffects & CFM_STRIKEOUT) && (cfOld.dwMask & CFM_STRIKEOUT);
+
+			int ctrlId = LOWORD(wParam);
+			if (ctrlId == IDC_SRMM_BOLD && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_BOLD)))
+				break;
+			if (ctrlId == IDC_SRMM_ITALICS && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_ITALICS)))
+				break;
+			if (ctrlId == IDC_SRMM_UNDERLINE && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_SRMM_UNDERLINE)))
+				break;
+			if (ctrlId == IDC_FONTSTRIKEOUT && !IsWindowEnabled(GetDlgItem(m_hwnd, IDC_FONTSTRIKEOUT)))
+				break;
+			if (ctrlId == IDC_SRMM_BOLD) {
+				cf.dwEffects = isBold ? 0 : CFE_BOLD;
+				cf.dwMask = CFM_BOLD;
+				CheckDlgButton(m_hwnd, IDC_SRMM_BOLD, !isBold ? BST_CHECKED : BST_UNCHECKED);
+			}
+			else if (ctrlId == IDC_SRMM_ITALICS) {
+				cf.dwEffects = isItalic ? 0 : CFE_ITALIC;
+				cf.dwMask = CFM_ITALIC;
+				CheckDlgButton(m_hwnd, IDC_SRMM_ITALICS, !isItalic ? BST_CHECKED : BST_UNCHECKED);
+			}
+			else if (ctrlId == IDC_SRMM_UNDERLINE) {
+				cf.dwEffects = isUnderline ? 0 : CFE_UNDERLINE;
+				cf.dwMask = CFM_UNDERLINE;
+				CheckDlgButton(m_hwnd, IDC_SRMM_UNDERLINE, !isUnderline ? BST_CHECKED : BST_UNCHECKED);
+			}
+			else if (ctrlId == IDC_FONTSTRIKEOUT) {
+				cf.dwEffects = isStrikeout ? 0 : CFM_STRIKEOUT;
+				cf.dwMask = CFM_STRIKEOUT;
+				CheckDlgButton(m_hwnd, IDC_FONTSTRIKEOUT, !isStrikeout ? BST_CHECKED : BST_UNCHECKED);
+			}
+			m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf);
+		}
+		break;
+
+	case IDCANCEL:
+		ShowWindow(m_pContainer->m_hwnd, SW_MINIMIZE);
+		return FALSE;
+
+	case IDC_CLOSE:
+		PostMessage(m_hwnd, WM_CLOSE, 1, 0);
+		break;
+
+	case IDC_NAME:
+		if (GetKeyState(VK_SHIFT) & 0x8000)   // copy UIN
+			Utils_ClipboardCopy(m_cache->getUIN());
+		else
+			CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(m_cache->getActiveContact()), 0);
+		break;
+
+	case IDC_TIME:
+		submenu = GetSubMenu(PluginConfig.g_hMenuContext, 2);
+		MsgWindowUpdateMenu(submenu, MENU_LOGMENU);
+
+		GetWindowRect(GetDlgItem(m_hwnd, IDC_TIME), &rc);
+
+		iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+		return MsgWindowMenuHandler(iSelection, MENU_LOGMENU);
+
+	case IDC_PROTOMENU:
+		submenu = GetSubMenu(PluginConfig.g_hMenuContext, 4);
+		{
+			bool iOldGlobalSendFormat = g_plugin.bSendFormat;
+			int iLocalFormat = M.GetDword(m_hContact, "sendformat", 0);
+			int iNewLocalFormat = iLocalFormat;
+
+			GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
+
+			CheckMenuItem(submenu, ID_MODE_GLOBAL, !m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
+			CheckMenuItem(submenu, ID_MODE_PRIVATE, m_bSplitterOverride ? MF_CHECKED : MF_UNCHECKED);
+
+			// formatting menu..
+			CheckMenuItem(submenu, ID_GLOBAL_BBCODE, (g_plugin.bSendFormat) ? MF_CHECKED : MF_UNCHECKED);
+			CheckMenuItem(submenu, ID_GLOBAL_OFF, (g_plugin.bSendFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
+
+			CheckMenuItem(submenu, ID_THISCONTACT_GLOBALSETTING, (iLocalFormat == SENDFORMAT_NONE) ? MF_CHECKED : MF_UNCHECKED);
+			CheckMenuItem(submenu, ID_THISCONTACT_BBCODE, (iLocalFormat > 0) ? MF_CHECKED : MF_UNCHECKED);
+			CheckMenuItem(submenu, ID_THISCONTACT_OFF, (iLocalFormat == -1) ? MF_CHECKED : MF_UNCHECKED);
+
+			iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+			switch (iSelection) {
+			case ID_MODE_GLOBAL:
+				m_bSplitterOverride = false;
+				db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 0);
+				LoadSplitter();
+				AdjustBottomAvatarDisplay();
+				DM_RecalcPictureSize();
+				Resize();
+				break;
+
+			case ID_MODE_PRIVATE:
+				m_bSplitterOverride = true;
+				db_set_b(m_hContact, SRMSGMOD_T, "splitoverride", 1);
+				LoadSplitter();
+				AdjustBottomAvatarDisplay();
+				DM_RecalcPictureSize();
+				Resize();
+				break;
+
+			case ID_GLOBAL_BBCODE:
+				g_plugin.bSendFormat = SENDFORMAT_BBCODE;
+				break;
+
+			case ID_GLOBAL_OFF:
+				g_plugin.bSendFormat = SENDFORMAT_NONE;
+				break;
+
+			case ID_THISCONTACT_GLOBALSETTING:
+				iNewLocalFormat = 0;
+				break;
+
+			case ID_THISCONTACT_BBCODE:
+				iNewLocalFormat = SENDFORMAT_BBCODE;
+				break;
+
+			case ID_THISCONTACT_OFF:
+				iNewLocalFormat = -1;
+				break;
+			}
+
+			if (iNewLocalFormat == 0)
+				db_unset(m_hContact, SRMSGMOD_T, "sendformat");
+			else if (iNewLocalFormat != iLocalFormat)
+				db_set_dw(m_hContact, SRMSGMOD_T, "sendformat", iNewLocalFormat);
+
+			if (iNewLocalFormat != iLocalFormat || g_plugin.bSendFormat != iOldGlobalSendFormat) {
+				m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
+				if (m_SendFormat == -1)          // per contact override to disable it..
+					m_SendFormat = 0;
+				Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
+			}
+		}
+		break;
+
+	case IDC_TOGGLETOOLBAR:
+		if (lParam == 1)
+			m_pContainer->cfg.flags.m_bNoMenuBar = !m_pContainer->cfg.flags.m_bNoMenuBar;
+		else
+			m_pContainer->cfg.flags.m_bHideToolbar = !m_pContainer->cfg.flags.m_bHideToolbar;
+		m_pContainer->ApplySetting(true);
+		break;
+
+	case IDC_SENDMENU:
+		submenu = GetSubMenu(PluginConfig.g_hMenuContext, 3);
+
+		GetWindowRect(GetDlgItem(m_hwnd, IDOK), &rc);
+		CheckMenuItem(submenu, ID_SENDMENU_SENDTOMULTIPLEUSERS, (m_sendMode & SMODE_MULTIPLE) ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(submenu, ID_SENDMENU_SENDDEFAULT, m_sendMode == 0 ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(submenu, ID_SENDMENU_SENDTOCONTAINER, (m_sendMode & SMODE_CONTAINER) ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(submenu, ID_SENDMENU_SENDLATER, (m_sendMode & SMODE_SENDLATER) ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(submenu, ID_SENDMENU_SENDWITHOUTTIMEOUTS, (m_sendMode & SMODE_NOACK) ? MF_CHECKED : MF_UNCHECKED);
+
+		if (lParam)
+			iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+		else
+			iSelection = HIWORD(wParam);
+
+		switch (iSelection) {
+		case ID_SENDMENU_SENDTOMULTIPLEUSERS:
+			m_sendMode ^= SMODE_MULTIPLE;
+			if (m_sendMode & SMODE_MULTIPLE)
+				DM_CreateClist();
+			else if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
+				DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
+			break;
+		case ID_SENDMENU_SENDDEFAULT:
+			m_sendMode = 0;
+			break;
+		case ID_SENDMENU_SENDTOCONTAINER:
+			m_sendMode ^= SMODE_CONTAINER;
+			RedrawWindow(m_hwnd, nullptr, nullptr, RDW_ERASENOW | RDW_UPDATENOW);
+			break;
+		case ID_SENDMENU_SENDLATER:
+			if (SendLater::Avail)
+				m_sendMode ^= SMODE_SENDLATER;
+			else
+				CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
+			break;
+		case ID_SENDMENU_SENDWITHOUTTIMEOUTS:
+			m_sendMode ^= SMODE_NOACK;
+			if (m_sendMode & SMODE_NOACK)
+				db_set_b(m_hContact, SRMSGMOD_T, "no_ack", 1);
+			else
+				db_unset(m_hContact, SRMSGMOD_T, "no_ack");
+			break;
+		}
+		db_set_b(m_hContact, SRMSGMOD_T, "no_ack", (uint8_t)(m_sendMode & SMODE_NOACK ? 1 : 0));
+		SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
+		if (m_sendMode & SMODE_MULTIPLE || m_sendMode & SMODE_CONTAINER) {
+			SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+				SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+			RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+		}
+		else {
+			if (IsWindow(GetDlgItem(m_hwnd, IDC_CLIST)))
+				DestroyWindow(GetDlgItem(m_hwnd, IDC_CLIST));
+			SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+				SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+			RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+		}
+		m_pContainer->QueryClientArea(rc);
+		Resize();
+		DM_ScrollToBottom(1, 1);
+		Utils::showDlgControl(m_hwnd, IDC_MULTISPLITTER, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
+		Utils::showDlgControl(m_hwnd, IDC_CLIST, (m_sendMode & SMODE_MULTIPLE) ? SW_SHOW : SW_HIDE);
+		break;
+
+	case IDC_TOGGLESIDEBAR:
+		SendMessage(m_pContainer->m_hwnd, WM_COMMAND, IDC_TOGGLESIDEBAR, 0);
+		break;
+
+	case IDC_PIC:
+		GetClientRect(m_hwnd, &rc);
+
+		m_bEditNotesActive = !m_bEditNotesActive;
+		if (m_bEditNotesActive) {
+			int iLen = GetWindowTextLength(m_message.GetHwnd());
+			if (iLen != 0) {
+				ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("You cannot edit user notes when there are unsent messages"));
+				m_bEditNotesActive = false;
+				break;
+			}
+
+			if (!m_bIsAutosizingInput) {
+				m_iSplitterSaved = m_iSplitterY;
+				m_iSplitterY = rc.bottom / 2;
+				SendMessage(m_hwnd, WM_SIZE, 1, 1);
+			}
+
+			ptrW wszText(db_get_wsa(m_hContact, "UserInfo", "MyNotes"));
+			if (wszText != nullptr)
+				m_message.SetText(wszText);
+		}
+		else {
+			ptrW buf(m_message.GetText());
+			db_set_ws(m_hContact, "UserInfo", "MyNotes", buf);
+			m_message.SetText(L"");
+
+			if (!m_bIsAutosizingInput) {
+				m_iSplitterY = m_iSplitterSaved;
+				Resize();
+				DM_ScrollToBottom(0, 1);
+			}
+		}
+		SetWindowPos(m_message.GetHwnd(), nullptr, 0, 0, 0, 0, SWP_DRAWFRAME | SWP_FRAMECHANGED | SWP_NOZORDER |
+			SWP_NOMOVE | SWP_NOSIZE | SWP_NOCOPYBITS);
+		RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
+
+		if (m_bEditNotesActive)
+			CWarning::show(CWarning::WARN_EDITUSERNOTES, MB_OK | MB_ICONINFORMATION);
+		break;
+
+	case IDM_CLEAR:
+		tabClearLog();
+		break;
+
+	case IDC_PROTOCOL:
+		submenu = Menu_BuildContactMenu(m_hContact);
+		if (lParam == 0)
+			GetWindowRect(GetDlgItem(m_hwnd, IDC_PROTOCOL), &rc);
+		else
+			GetWindowRect((HWND)lParam, &rc);
+
+		iSelection = TrackPopupMenu(submenu, TPM_RETURNCMD, rc.left, rc.bottom, 0, m_hwnd, nullptr);
+		if (iSelection)
+			Clist_MenuProcessCommand(LOWORD(iSelection), MPCF_CONTACTMENU, m_hContact);
+
+		DestroyMenu(submenu);
+		break;
+
+	// error control
+	case IDC_CANCELSEND:
+		DM_ErrorDetected(MSGERROR_CANCEL, 0);
+		break;
+
+	case IDC_RETRY:
+		DM_ErrorDetected(MSGERROR_RETRY, 0);
+		break;
+
+	case IDC_MSGSENDLATER:
+		DM_ErrorDetected(MSGERROR_SENDLATER, 0);
+		break;
+
+	case IDC_SELFTYPING:
+		if (AllowTyping()) {
+			int iCurrentTypingMode = g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew);
+			if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && iCurrentTypingMode) {
+				DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
+				m_nTypeMode = PROTOTYPE_SELFTYPING_OFF;
+			}
+			g_plugin.setByte(m_hContact, SRMSGSET_TYPING, (uint8_t)!iCurrentTypingMode);
+		}
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// initialize rich edit control (log and edit control) for both MUC and
+// standard IM session windows.
+
+void CMsgDialog::DM_InitRichEdit()
+{
+	char *szStreamOut = nullptr;
+	if (!isChat() && GetWindowTextLength(m_message.GetHwnd()) > 0)
+		szStreamOut = m_message.GetRichTextRtf();
+	SetWindowText(m_message.GetHwnd(), L"");
+
+	m_pLog->UpdateOptions();
+
+	m_message.SendMsg(EM_SETBKGNDCOLOR, 0, m_pContainer->m_theme.inputbg);
+
+	CHARFORMAT2 cf2 = {};
+	cf2.cbSize = sizeof(cf2);
+
+	if (isChat()) {
+		LOGFONTW lf;
+		COLORREF inputcharcolor;
+		LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, &lf, &inputcharcolor);
+
+		cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_ITALIC | CFM_BACKCOLOR;
+		cf2.crTextColor = inputcharcolor;
+		cf2.bCharSet = lf.lfCharSet;
+		cf2.crBackColor = m_pContainer->m_theme.inputbg;
+		wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
+		cf2.dwEffects = 0;
+		cf2.wWeight = (uint16_t)lf.lfWeight;
+		cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+		cf2.yHeight = abs(lf.lfHeight) * 15;
+	}
+	else {
+		LOGFONTW lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
+		COLORREF inputcharcolor = m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA];
+
+		for (auto &it : Utils::rtf_clrs)
+			if (it->clr == inputcharcolor)
+				inputcharcolor = RGB(GetRValue(inputcharcolor), GetGValue(inputcharcolor), GetBValue(inputcharcolor) == 0 ? GetBValue(inputcharcolor) + 1 : GetBValue(inputcharcolor) - 1);
+
+		cf2.dwMask = CFM_COLOR | CFM_FACE | CFM_CHARSET | CFM_SIZE | CFM_WEIGHT | CFM_BOLD | CFM_ITALIC;
+		cf2.crTextColor = inputcharcolor;
+		cf2.bCharSet = lf.lfCharSet;
+		wcsncpy_s(cf2.szFaceName, lf.lfFaceName, _TRUNCATE);
+		cf2.dwEffects = ((lf.lfWeight >= FW_BOLD) ? CFE_BOLD : 0) | (lf.lfItalic ? CFE_ITALIC : 0) | (lf.lfUnderline ? CFE_UNDERLINE : 0) | (lf.lfStrikeOut ? CFE_STRIKEOUT : 0);
+		cf2.wWeight = (uint16_t)lf.lfWeight;
+		cf2.bPitchAndFamily = lf.lfPitchAndFamily;
+		cf2.yHeight = abs(lf.lfHeight) * 15;
+	}
+	m_message.SendMsg(EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cf2);
+	m_message.SendMsg(EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2); /* WINE: fix send colour text. */
+	m_message.SendMsg(EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf2); /* WINE: fix send colour text. */
+
+	// setup the rich edit control(s)
+	// LOG is always set to RTL, because this is needed for proper bidirectional operation later.
+	// The real text direction is then enforced by the streaming code which adds appropiate paragraph
+	// and textflow formatting commands to the
+	PARAFORMAT2 pf2;
+	memset(&pf2, 0, sizeof(PARAFORMAT2));
+	pf2.cbSize = sizeof(pf2);
+	pf2.wEffects = PFE_RTLPARA;
+	pf2.dwMask = PFM_RTLPARA;
+	if (FindRTLLocale())
+		m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+	if (!(m_dwFlags & MWF_LOG_RTL)) {
+		pf2.wEffects = 0;
+		m_message.SendMsg(EM_SETPARAFORMAT, 0, (LPARAM)&pf2);
+	}
+	m_message.SendMsg(EM_SETLANGOPTIONS, 0, (LPARAM)m_message.SendMsg(EM_GETLANGOPTIONS, 0, 0) & ~IMF_AUTOKEYBOARD);
+
+	if (m_dwFlags & MWF_LOG_RTL)
+		SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) | WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR);
+	else
+		SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR));
+
+	if (szStreamOut != nullptr) {
+		SETTEXTEX stx = { ST_DEFAULT, CP_UTF8 };
+		m_message.SendMsg(EM_SETTEXTEX, (WPARAM)&stx, (LPARAM)szStreamOut);
+		mir_free(szStreamOut);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// set the states of defined database action buttons(only if button is a toggle)
+
+void CMsgDialog::DM_SetDBButtonStates()
+{
+	ButtonItem *buttonItem = m_pContainer->m_buttonItems;
+	MCONTACT hFinalContact = 0;
+	HWND hwndContainer = m_pContainer->m_hwnd;
+
+	while (buttonItem) {
+		HWND hWnd = GetDlgItem(hwndContainer, buttonItem->uId);
+
+		if (buttonItem->pfnCallback)
+			buttonItem->pfnCallback(buttonItem, m_hwnd, this, hWnd);
+
+		if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
+			buttonItem = buttonItem->nextItem;
+			continue;
+		}
+
+		BOOL result = FALSE;
+		char *szModule = buttonItem->szModule;
+		char *szSetting = buttonItem->szSetting;
+		if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
+			if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
+				szModule = Proto_GetBaseAccountName(m_hContact);
+			hFinalContact = m_hContact;
+		}
+		else hFinalContact = 0;
+
+		switch (buttonItem->type) {
+		case DBVT_BYTE:
+			result = (db_get_b(hFinalContact, szModule, szSetting, 0) == buttonItem->bValuePush[0]);
+			break;
+		case DBVT_WORD:
+			result = (db_get_w(hFinalContact, szModule, szSetting, 0) == *((uint16_t *)&buttonItem->bValuePush));
+			break;
+		case DBVT_DWORD:
+			result = (db_get_dw(hFinalContact, szModule, szSetting, 0) == *((uint32_t *)&buttonItem->bValuePush));
+			break;
+		case DBVT_ASCIIZ:
+			ptrA szValue(db_get_sa(hFinalContact, szModule, szSetting));
+			if (szValue)
+				result = !mir_strcmp((char*)buttonItem->bValuePush, szValue);
+			break;
+		}
+		SendMessage(hWnd, BM_SETCHECK, result, 0);
+		buttonItem = buttonItem->nextItem;
+	}
+}
+
+void CMsgDialog::DM_ScrollToBottom(WPARAM wParam, LPARAM lParam)
+{
+	if (m_bScrollingDisabled)
+		return;
+
+	if (IsIconic(m_pContainer->m_hwnd))
+		m_bDeferredScroll = true;
+
+	if (m_iLogMode == WANT_BUILTIN_LOG)
+		((CLogWindow *)m_pLog)->ScrollToBottom(wParam != 0, lParam != 0);
+	else
+		m_pLog->ScrollToBottom();
+}
+
+void CMsgDialog::DM_RecalcPictureSize()
+{
+	HBITMAP hbm = ((m_pPanel.isActive()) && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
+	if (hbm) {
+		BITMAP bminfo;
+		GetObject(hbm, sizeof(bminfo), &bminfo);
+		CalcDynamicAvatarSize(&bminfo);
+		Resize();
+	}
+	else m_pic.cy = m_pic.cx = 60;
+}
+
+void CMsgDialog::DM_UpdateLastMessage() const
+{
+	if (m_pContainer->m_hwndStatus == nullptr || m_pContainer->m_hwndActive != m_hwnd)
+		return;
+
+	wchar_t szBuf[100];
+	if (m_bShowTyping) {
+		SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+		mir_snwprintf(szBuf, TranslateT("%s is typing a message..."), m_cache->getNick());
+	}
+	else if (m_bStatusSet) {
+		SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+		SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+		return;
+	}
+	else {
+		SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+
+		if (m_pContainer->cfg.flags.m_bUinStatusBar)
+			mir_snwprintf(szBuf, L"UID: %s", m_cache->getUIN());
+		else if (m_lastMessage) {
+			wchar_t date[64], time[64];
+			TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"d", date, _countof(date), 0);
+			if (m_pContainer->cfg.flags.m_bUinStatusBar && mir_wstrlen(date) > 6)
+				date[mir_wstrlen(date) - 5] = 0;
+			TimeZone_PrintTimeStamp(nullptr, m_lastMessage, L"t", time, _countof(time), 0);
+			mir_snwprintf(szBuf, TranslateT("Last received: %s at %s"), date, time);
+		}
+		else szBuf[0] = 0;
+	}
+
+	SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szBuf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// create embedded contact list control
+
+HWND CMsgDialog::DM_CreateClist()
+{
+	if (!SendLater::Avail) {
+		CWarning::show(CWarning::WARN_NO_SENDLATER, MB_OK | MB_ICONINFORMATION);
+		m_sendMode &= ~SMODE_MULTIPLE;
+		return nullptr;
+	}
+
+	HWND hwndClist = CreateWindowExA(0, "CListControl", "", WS_TABSTOP | WS_VISIBLE | WS_CHILD | 0x248, 184, 0, 30, 30, m_hwnd, (HMENU)IDC_CLIST, g_plugin.getInst(), nullptr);
+	SendMessage(hwndClist, WM_TIMER, 14, 0);
+	HANDLE hItem = (HANDLE)SendMessage(hwndClist, CLM_FINDCONTACT, m_hContact, 0);
+
+	SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) & ~CLS_EX_TRACKSELECT);
+	SetWindowLongPtr(hwndClist, GWL_EXSTYLE, GetWindowLongPtr(hwndClist, GWL_EXSTYLE) | (CLS_EX_NOSMOOTHSCROLLING | CLS_EX_NOTRANSLUCENTSEL));
+
+	if (!g_plugin.bAllowOfflineMultisend)
+		SetWindowLongPtr(hwndClist, GWL_STYLE, GetWindowLongPtr(hwndClist, GWL_STYLE) | CLS_HIDEOFFLINE);
+
+	if (hItem)
+		SendMessage(hwndClist, CLM_SETCHECKMARK, (WPARAM)hItem, 1);
+
+	SendMessage(hwndClist, CLM_SETHIDEEMPTYGROUPS, Clist::HideEmptyGroups, 0);
+	SendMessage(hwndClist, CLM_SETUSEGROUPS, Clist::UseGroups, 0);
+	SendMessage(hwndClist, CLM_FIRST + 106, 0, 1);
+	SendMessage(hwndClist, CLM_AUTOREBUILD, 0, 0);
+	if (hwndClist)
+		RedrawWindow(hwndClist, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
+	return hwndClist;
+}
+
+LRESULT CMsgDialog::DM_MouseWheelHandler(WPARAM wParam, LPARAM lParam)
+{
+	POINT pt;
+	GetCursorPos(&pt);
+
+	RECT rc;
+	GetWindowRect(m_message.GetHwnd(), &rc);
+	if (PtInRect(&rc, pt))
+		return 1;
+
+	if (isChat()) {					// scroll nick list by just hovering it
+		RECT rcNicklist;
+		GetWindowRect(m_nickList.GetHwnd(), &rcNicklist);
+		if (PtInRect(&rcNicklist, pt)) {
+			m_nickList.SendMsg(WM_MOUSEWHEEL, wParam, lParam);
+			return 0;
+		}
+	}
+
+	GetWindowRect(m_pLog->GetHwnd(), &rc);
+	if (PtInRect(&rc, pt)) {
+		short wDirection = (short)HIWORD(wParam);
+
+		if (LOWORD(wParam) & MK_SHIFT || M.GetByte("fastscroll", 0)) {
+			if (wDirection < 0)
+				SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEDOWN, 0), 0);
+			else if (wDirection > 0)
+				SendMessage(m_pLog->GetHwnd(), WM_VSCROLL, MAKEWPARAM(SB_PAGEUP, 0), 0);
+		}
+		else SendMessage(m_pLog->GetHwnd(), WM_MOUSEWHEEL, wParam, lParam);
+		return 0;
+	}
+
+	if (GetTabItemFromMouse(m_pContainer->m_hwndTabs, &pt) != -1) {
+		SendMessage(m_pContainer->m_hwndTabs, WM_MOUSEWHEEL, wParam, -1);
+		return 0;
+	}
+	return 1;
+}
+
+void CMsgDialog::DM_FreeTheme()
+{
+	if (m_hTheme) {
+		CloseThemeData(m_hTheme);
+		m_hTheme = nullptr;
+	}
+	if (m_hThemeIP) {
+		CloseThemeData(m_hThemeIP);
+		m_hThemeIP = nullptr;
+	}
+	if (m_hThemeToolbar) {
+		CloseThemeData(m_hThemeToolbar);
+		m_hThemeToolbar = nullptr;
+	}
+}
+
+void CMsgDialog::DM_ThemeChanged()
+{
+	CSkinItem *item_log = &SkinItems[ID_EXTBKHISTORY];
+	CSkinItem *item_msg = &SkinItems[ID_EXTBKINPUTAREA];
+
+	m_hTheme = OpenThemeData(m_hwnd, L"EDIT");
+
+	if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_log->IGNORED)) {
+		if (m_iLogMode == WANT_BUILTIN_LOG)
+			LOG()->DisableStaticEdge();
+
+		if (isChat())
+			SetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_nickList.GetHwnd(), GWL_EXSTYLE) & ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
+	}
+	
+	if (m_hTheme != nullptr || (CSkin::m_skinEnabled && !item_msg->IGNORED))
+		SetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE, GetWindowLongPtr(m_message.GetHwnd(), GWL_EXSTYLE) & ~WS_EX_STATICEDGE);
+
+	m_hThemeIP = M.isAero() ? OpenThemeData(m_hwnd, L"ButtonStyle") : nullptr;
+	m_hThemeToolbar = (M.isAero() || (!CSkin::m_skinEnabled && M.isVSThemed())) ? OpenThemeData(m_hwnd, L"REBAR") : nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send out message typing notifications (MTN) when the
+// user is typing/editing text in the message input area.
+
+void CMsgDialog::DM_NotifyTyping(int mode)
+{
+	const char *szProto = m_cache->getActiveProto();
+	MCONTACT hContact = m_cache->getActiveContact();
+
+	// editing user notes or preparing a message for queued delivery -> don't send MTN
+	if (m_bEditNotesActive || (m_sendMode & SMODE_SENDLATER))
+		return;
+
+	// allow supression of sending out TN for the contact (NOTE: for metacontacts, do NOT use the subcontact handle)
+	if (!g_plugin.getByte(hContact, SRMSGSET_TYPING, g_plugin.bTypingNew))
+		return;
+
+	if (szProto == nullptr) // should not, but who knows...
+		return;
+
+	// check status and capabilities of the protocol
+	uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+	if (!(typeCaps & PF4_SUPPORTTYPING))
+		return;
+
+	if (isChat()) {
+		m_nTypeMode = mode;
+		Chat_DoEventHook(m_si, GC_USER_TYPNOTIFY, 0, 0, m_nTypeMode);
+	}
+	else {
+		uint32_t protoStatus = Proto_GetStatus(szProto);
+		if (protoStatus < ID_STATUS_ONLINE)
+			return;
+
+		// check visibility/invisibility lists to not "accidentially" send MTN to contacts who
+		// should not see them (privacy issue)
+		uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+		if (protoCaps & PF1_VISLIST && db_get_w(hContact, szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+			return;
+
+		if (protoCaps & PF1_INVISLIST && protoStatus == ID_STATUS_INVISIBLE && db_get_w(hContact, szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+			return;
+
+		// don't send to contacts which are not permanently added to the contact list,
+		// unless the option to ignore added status is set.
+		if (!Contact::OnList(m_hContact) && !g_plugin.bTypingUnknown)
+			return;
+
+		// End user check
+		m_nTypeMode = mode;
+		CallService(MS_PROTO_SELFISTYPING, hContact, m_nTypeMode);
+	}
+}
+
+void CMsgDialog::DM_OptionsApplied(bool bRemakeLog)
+{
+	m_szMicroLf[0] = 0;
+	if (!m_pContainer->m_theme.isPrivate) {
+		m_pContainer->LoadThemeDefaults();
+		m_dwFlags = m_pContainer->m_theme.dwFlags;
+	}
+
+	LoadLocalFlags();
+	m_hTimeZone = TimeZone_CreateByContact(m_hContact, nullptr, TZF_KNOWNONLY);
+
+	m_bShowUIElements = (m_pContainer->cfg.flags.m_bHideToolbar) == 0;
+	m_bSplitterOverride = M.GetByte(m_hContact, "splitoverride", 0) != 0;
+	m_pPanel.getVisibility();
+
+	// small inner margins (padding) for the text areas
+	m_message.SendMsg(EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(3, 3));
+
+	GetSendFormat();
+	SetDialogToType();
+	SendMessage(m_hwnd, DM_CONFIGURETOOLBAR, 0, 0);
+
+	DM_InitRichEdit();
+	if (m_hwnd == m_pContainer->m_hwndActive)
+		SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
+	InvalidateRect(m_message.GetHwnd(), nullptr, FALSE);
+	if (bRemakeLog) {
+		if (IsIconic(m_pContainer->m_hwnd))
+			m_bDeferredRemakeLog = true;
+		else if (isChat())
+			RedrawLog();
+		else
+			RemakeLog();
+	}
+
+	ShowWindow(m_hwndPanelPicParent, SW_SHOW);
+	EnableWindow(m_hwndPanelPicParent, TRUE);
+
+	UpdateWindowIcon();
+}
+	
+void CMsgDialog::DM_Typing(bool fForceOff)
+{
+	HWND hwndContainer = m_pContainer->m_hwnd;
+	HWND hwndStatus = m_pContainer->m_hwndStatus;
+
+	if (m_nTypeMode == PROTOTYPE_SELFTYPING_ON && GetTickCount() - m_nLastTyping > TIMEOUT_TYPEOFF)
+		DM_NotifyTyping(PROTOTYPE_SELFTYPING_OFF);
+
+	if (m_bShowTyping == 1) {
+		if (m_nTypeSecs > 0) {
+			m_nTypeSecs--;
+			if (GetForegroundWindow() == hwndContainer)
+				UpdateWindowIcon();
+		}
+		else {
+			if (!fForceOff) {
+				m_bShowTyping = 2;
+				m_nTypeSecs = 86400;
+
+				if (!isChat())
+					mir_snwprintf(m_wszStatusBar, TranslateT("%s has entered text."), m_cache->getNick());
+
+				if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd)
+					SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+			}
+			UpdateWindowIcon();
+			HandleIconFeedback(this, (HICON)-1);
+			CMsgDialog *dat_active = (CMsgDialog*)GetWindowLongPtr(m_pContainer->m_hwndActive, GWLP_USERDATA);
+			if (dat_active && !dat_active->isChat())
+				m_pContainer->UpdateTitle(0);
+			else
+				m_pContainer->UpdateTitle(0, dat_active);
+			if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
+				m_pContainer->ReflashContainer();
+		}
+	}
+	else if (m_bShowTyping == 2) {
+		if (m_nTypeSecs > 0)
+			m_nTypeSecs--;
+		else {
+			m_wszStatusBar[0] = 0;
+			m_bShowTyping = 0;
+		}
+		tabUpdateStatusBar();
+	}
+	else if (m_nTypeSecs > 0) {
+		mir_snwprintf(m_wszStatusBar, TranslateT("%s is typing a message"), 
+			(m_pUserTyping) ? m_pUserTyping->pszNick : m_cache->getNick());
+
+		m_nTypeSecs--;
+		if (hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
+			SendMessage(hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+			SendMessage(hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+		}
+		if (IsIconic(hwndContainer) || !IsActive()) {
+			SetWindowText(hwndContainer, m_wszStatusBar);
+			m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
+			if (!m_pContainer->cfg.flags.m_bNoFlash && PluginConfig.m_FlashOnMTN)
+				m_pContainer->ReflashContainer();
+		}
+
+		if (m_pContainer->m_hwndActive != m_hwnd) {
+			if (m_bCanFlashTab)
+				m_iFlashIcon = PluginConfig.g_IconTypingEvent;
+			HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
+		}
+		else { // active tab may show icon if status bar is disabled
+			if (!hwndStatus) {
+				if (TabCtrl_GetItemCount(m_hwndParent) > 1 || !m_pContainer->cfg.flags.m_bHideTabs)
+					HandleIconFeedback(this, PluginConfig.g_IconTypingEvent);
+			}
+		}
+		if ((GetForegroundWindow() != hwndContainer) || (m_pContainer->m_hwndStatus == nullptr) || (m_pContainer->m_hwndActive != m_hwnd))
+			m_pContainer->SetIcon(this, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+
+		m_bShowTyping = 1;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sync splitter position for all open sessions.
+// This cares about private / per container / MUC <> IM splitter syncing and everything.
+// called from IM and MUC windows via DM_SPLITTERGLOBALEVENT
+
+int CMsgDialog::DM_SplitterGlobalEvent(WPARAM wParam, LPARAM lParam)
+{
+	CMsgDialog *srcDat = PluginConfig.lastSPlitterPos.pSrcDat;
+	TContainerData *srcCnt = PluginConfig.lastSPlitterPos.pSrcContainer;
+	bool fCntGlobal = (!m_pContainer->cfg.fPrivate ? true : false);
+
+	if (m_bIsAutosizingInput)
+		return 0;
+
+	RECT rcWin;
+	GetWindowRect(m_hwnd, &rcWin);
+
+	LONG newPos;
+	if (wParam == 0 && lParam == 0) {
+		if (m_bSplitterOverride && this != srcDat)
+			return 0;
+
+		if (srcDat->isChat() == isChat())
+			newPos = PluginConfig.lastSPlitterPos.pos;
+		else if (!srcDat->isChat() && isChat())
+			newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+		else if (srcDat->isChat() && !isChat())
+			newPos = PluginConfig.lastSPlitterPos.pos + PluginConfig.lastSPlitterPos.off_im;
+		else
+			newPos = 0;
+
+		if (this == srcDat) {
+			m_pContainer->cfg.iSplitterY = m_iSplitterY;
+			if (fCntGlobal)
+				SaveSplitter();
+			return 0;
+		}
+
+		if (!fCntGlobal && m_pContainer != srcCnt)
+			return 0;
+		if (srcCnt->cfg.fPrivate && m_pContainer != srcCnt)
+			return 0;
+
+		// for inactive sessions, delay the splitter repositioning until they become
+		// active (faster, avoid redraw/resize problems for minimized windows)
+		if (IsIconic(m_pContainer->m_hwnd) || m_pContainer->m_hwndActive != m_hwnd) {
+			m_bDelayedSplitter = true;
+			m_wParam = newPos;
+			m_lParam = PluginConfig.lastSPlitterPos.lParam;
+			return 0;
+		}
+	}
+	else newPos = wParam;
+
+	LoadSplitter();
+	AdjustBottomAvatarDisplay();
+	DM_RecalcPictureSize();
+	Resize();
+	DM_ScrollToBottom(1, 1);
+	if (this != srcDat)
+		UpdateToolbarBG();
+	return 0;
+}
+
+void CMsgDialog::DM_AddDivider()
+{
+	if (!m_bDividerSet && g_plugin.bUseDividers)
+		if (GetWindowTextLength(m_pLog->GetHwnd()) > 0)
+			m_bDividerSet = m_bDividerWanted = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// incoming event handler
+
+void CMsgDialog::DM_EventAdded(WPARAM, LPARAM lParam)
+{
+	MEVENT hDbEvent = (MEVENT)lParam;
+
+	DBEVENTINFO dbei = {};
+	db_event_get(hDbEvent, &dbei);
+	if (m_hDbEventFirst == 0)
+		m_hDbEventFirst = hDbEvent;
+
+	bool bIsStatusChangeEvent = IsStatusEvent(dbei.eventType);
+	bool bDisableNotify = (dbei.eventType == EVENTTYPE_MESSAGE && (dbei.flags & DBEF_READ));
+
+	if (!DbEventIsShown(&dbei))
+		return;
+
+	if (dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT))) {
+		m_lastMessage = dbei.timestamp;
+		m_wszStatusBar[0] = 0;
+		if (m_bShowTyping) {
+			m_nTypeSecs = 0;
+			DM_Typing(true);
+			m_bShowTyping = 0;
+		}
+		HandleIconFeedback(this, (HICON)-1);
+		if (m_pContainer->m_hwndStatus)
+			PostMessage(m_hwnd, DM_UPDATELASTMESSAGE, 0, 0);
+	}
+
+	// set the message log divider to mark new (maybe unseen) messages, if the container has
+	// been minimized or in the background.
+	if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
+		if (g_plugin.bDividersUsePopupConfig && g_plugin.bUseDividers) {
+			if (!MessageWindowOpened(m_hContact, nullptr))
+				DM_AddDivider();
+		}
+		else if (g_plugin.bUseDividers) {
+			if (!m_pContainer->IsActive())
+				DM_AddDivider();
+			else if (m_pContainer->m_hwndActive != m_hwnd)
+				DM_AddDivider();
+		}
+
+		if (IsWindowVisible(m_pContainer->m_hwnd))
+			m_pContainer->m_bHidden = false;
+	}
+	m_cache->updateStats(TSessionStats::UPDATE_WITH_LAST_RCV, 0);
+
+	if (hDbEvent != m_hDbEventFirst || isChat())
+		StreamEvents(hDbEvent, 1, 1);
+	else
+		RemakeLog();
+
+	// handle tab flashing
+	if (!bDisableNotify && !bIsStatusChangeEvent)
+		if ((TabCtrl_GetCurSel(m_hwndParent) != m_iTabID) && !(dbei.flags & DBEF_SENT)) {
+			switch (dbei.eventType) {
+			case EVENTTYPE_MESSAGE:
+				m_iFlashIcon = PluginConfig.g_IconMsgEvent;
+				break;
+			case EVENTTYPE_FILE:
+				m_iFlashIcon = PluginConfig.g_IconFileEvent;
+				break;
+			default:
+				m_iFlashIcon = PluginConfig.g_IconMsgEvent;
+				break;
+			}
+			timerFlash.Start(TIMEOUT_FLASHWND);
+			m_bCanFlashTab = true;
+		}
+
+	// try to flash the contact list...
+	if (!bDisableNotify)
+		FlashOnClist(hDbEvent, &dbei);
+
+	// autoswitch tab if option is set AND container is minimized (otherwise, we never autoswitch)
+	// never switch for status changes...
+	if (!(dbei.flags & DBEF_SENT) && !bIsStatusChangeEvent) {
+		if (g_plugin.bAutoSwitchTabs && m_pContainer->m_hwndActive != m_hwnd) {
+			if ((IsIconic(m_pContainer->m_hwnd) && !IsZoomed(m_pContainer->m_hwnd)) || (g_plugin.bHideOnClose && !IsWindowVisible(m_pContainer->m_hwnd))) {
+				int iItem = GetTabIndexFromHWND(GetParent(m_hwnd), m_hwnd);
+				if (iItem >= 0) {
+					TabCtrl_SetCurSel(m_hwndParent, iItem);
+					ShowWindow(m_pContainer->m_hwndActive, SW_HIDE);
+					m_pContainer->m_hwndActive = m_hwnd;
+					m_pContainer->UpdateTitle(m_hContact);
+					m_pContainer->cfg.flags.m_bDeferredTabSelect = true;
+				}
+			}
+		}
+	}
+
+	// flash window if it is not focused
+	if (!bDisableNotify && !bIsStatusChangeEvent)
+		if (!IsActive() && !(dbei.flags & DBEF_SENT)) {
+			if (!m_pContainer->cfg.flags.m_bNoFlash && !m_pContainer->IsActive())
+				m_pContainer->FlashContainer(1, 0);
+			m_pContainer->SetIcon(this, Skin_LoadIcon(SKINICON_EVENT_MESSAGE));
+			m_pContainer->cfg.flags.m_bNeedsUpdateTitle = true;
+		}
+
+	// play a sound
+	if (!bDisableNotify && dbei.eventType == EVENTTYPE_MESSAGE && !(dbei.flags & (DBEF_SENT)))
+		PlayIncomingSound();
+	
+	if (m_pWnd)
+		m_pWnd->Invalidate();
+}
+
+void CMsgDialog::DM_HandleAutoSizeRequest(REQRESIZE* rr)
+{
+	if (rr == nullptr || GetForegroundWindow() != m_pContainer->m_hwnd)
+		return;
+
+	if (!m_bIsAutosizingInput || m_iInputAreaHeight == -1)
+		return;
+
+	LONG heightLimit = M.GetDword("autoSplitMinLimit", 0);
+	LONG iNewHeight = rr->rc.bottom - rr->rc.top;
+
+	if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+		iNewHeight += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP + SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM - 2);
+
+	if (heightLimit && iNewHeight < heightLimit)
+		iNewHeight = heightLimit;
+
+	if (iNewHeight == m_iInputAreaHeight)
+		return;
+
+	RECT rc;
+	GetClientRect(m_hwnd, &rc);
+	LONG cy = rc.bottom - rc.top;
+	LONG panelHeight = (m_pPanel.isActive() ? m_pPanel.getHeight() : 0);
+
+	if (iNewHeight > (cy - panelHeight) / 2)
+		iNewHeight = (cy - panelHeight) / 2;
+
+	m_dynaSplitter = iNewHeight - DPISCALEY_S(2);
+	if (m_pContainer->cfg.flags.m_bBottomToolbar)
+		m_dynaSplitter += DPISCALEY_S(22);
+	m_iSplitterY = m_dynaSplitter + DPISCALEY_S(34);
+	DM_RecalcPictureSize();
+
+	m_iInputAreaHeight = iNewHeight;
+	UpdateToolbarBG();
+	DM_ScrollToBottom(1, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// status icon stuff (by sje, used for indicating encryption status in the status bar
+// this is now part of the message window api
+
+
+static int OnSrmmIconChanged(WPARAM hContact, LPARAM)
+{
+	if (hContact == 0)
+		Srmm_Broadcast(DM_STATUSICONCHANGE, 0, 0);
+	else {
+		HWND hwnd = Srmm_FindWindow(hContact);
+		if (hwnd)
+			PostMessage(hwnd, DM_STATUSICONCHANGE, 0, 0);
+	}
+	return 0;
+}
+
+void CMsgDialog::DrawStatusIcons(HDC hDC, const RECT &rc, int gap)
+{
+	int x = rc.left;
+	int y = (rc.top + rc.bottom - PluginConfig.m_smcxicon) >> 1;
+
+	SetBkMode(hDC, TRANSPARENT);
+
+	int nIcon = 0;
+	while (StatusIconData *sid = Srmm_GetNthIcon(m_hContact, nIcon++)) {
+		if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
+			if (sid->dwId == MSG_ICON_SOUND) {
+				DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_SOUNDS],
+					PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+
+				DrawIconEx(hDC, x, y, m_pContainer->cfg.flags.m_bNoSound ?
+					PluginConfig.g_iconOverlayDisabled : PluginConfig.g_iconOverlayEnabled,
+					PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+			}
+			else if (sid->dwId == MSG_ICON_UTN) {
+				if (AllowTyping()) {
+					DrawIconEx(hDC, x, y, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+
+					DrawIconEx(hDC, x, y, g_plugin.getByte(m_hContact, SRMSGSET_TYPING, g_plugin.bTypingNew) ?
+						PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, 0, nullptr, DI_NORMAL);
+				}
+				else CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING], 50);
+			}
+		}
+		else {
+			HICON hIcon;
+			if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled)
+				hIcon = sid->hIconDisabled;
+			else
+				hIcon = sid->hIcon;
+
+			if ((sid->flags & MBF_DISABLED) && sid->hIconDisabled == nullptr)
+				CSkin::DrawDimmedIcon(hDC, x, y, PluginConfig.m_smcxicon, PluginConfig.m_smcyicon, hIcon, 50);
+			else
+				DrawIconEx(hDC, x, y, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
+		}
+
+		x += PluginConfig.m_smcxicon + gap;
+	}
+}
+
+void CMsgDialog::CheckStatusIconClick(POINT pt, const RECT &rc, int gap, int code)
+{
+	if (code == NM_CLICK || code == NM_RCLICK) {
+		POINT	ptScreen;
+		GetCursorPos(&ptScreen);
+		if (!PtInRect(&rcLastStatusBarClick, ptScreen))
+			return;
+	}
+
+	UINT iconNum = (pt.x - (rc.left + 0)) / (PluginConfig.m_smcxicon + gap);
+	
+	StatusIconData *sid = Srmm_GetNthIcon(m_hContact, iconNum);
+	if (sid == nullptr)
+		return;
+
+	if (!mir_strcmp(sid->szModule, MSG_ICON_MODULE)) {
+		if (sid->dwId == MSG_ICON_SOUND && code != NM_RCLICK) {
+			if (GetKeyState(VK_SHIFT) & 0x8000) {
+				for (TContainerData *p = pFirstContainer; p; p = p->pNext) {
+					p->cfg.flags.m_bNoSound = m_pContainer->cfg.flags.m_bNoSound;
+					InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+				}
+			}
+			else {
+				m_pContainer->cfg.flags.m_bNoSound = !m_pContainer->cfg.flags.m_bNoSound;
+				InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+			}
+		}
+		else if (sid->dwId == MSG_ICON_UTN && code != NM_RCLICK && AllowTyping()) {
+			SendMessage(m_pContainer->m_hwndActive, WM_COMMAND, IDC_SELFTYPING, 0);
+			InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+		}
+	}
+	else {
+		StatusIconClickData sicd = { sizeof(sicd) };
+		GetCursorPos(&sicd.clickLocation);
+		sicd.dwId = sid->dwId;
+		sicd.szModule = sid->szModule;
+		sicd.flags = (code == NM_RCLICK ? MBCF_RIGHTBUTTON : 0);
+		Srmm_ClickStatusIcon(m_hContact, &sicd);
+		InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+	}
+}
+
+void CMsgDialog::DM_ErrorDetected(int type, int flag)
+{
+	switch (type) {
+	case MSGERROR_CANCEL:
+	case MSGERROR_SENDLATER:
+		if (m_bErrorState) {
+			m_cache->saveHistory();
+			if (type == MSGERROR_SENDLATER)
+				sendQueue->doSendLater(m_iCurrentQueueError, this); // to be implemented at a later time
+			m_iOpenJobs--;
+			sendQueue->dec();
+			if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS)
+				sendQueue->clearJob(m_iCurrentQueueError);
+			m_iCurrentQueueError = -1;
+			sendQueue->showErrorControls(this, FALSE);
+			if (type != MSGERROR_CANCEL || flag == 0)
+				m_message.SetText(L"");
+			sendQueue->checkQueue(this);
+			int iNextFailed = sendQueue->findNextFailed(this);
+			if (iNextFailed >= 0)
+				sendQueue->handleError(this, iNextFailed);
+		}
+		break;
+
+	case MSGERROR_RETRY:
+		if (m_bErrorState) {
+			int resent = 0;
+
+			m_cache->saveHistory();
+			if (m_iCurrentQueueError >= 0 && m_iCurrentQueueError < SendQueue::NR_SENDJOBS) {
+				SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
+				if (job->iSendId == 0 && job->hContact == 0)
+					break;
+
+				job->iSendId = ProtoChainSend(job->hContact, PSS_MESSAGE, job->dwFlags, (LPARAM)job->szSendBuffer);
+				resent++;
+			}
+
+			if (resent) {
+				SendJob *job = sendQueue->getJobByIndex(m_iCurrentQueueError);
+
+				SetTimer(m_hwnd, TIMERID_MSGSEND + m_iCurrentQueueError, PluginConfig.m_MsgTimeout, nullptr);
+				job->iStatus = SendQueue::SQ_INPROGRESS;
+				m_iCurrentQueueError = -1;
+				sendQueue->showErrorControls(this, FALSE);
+				m_message.SetText(L"");
+				sendQueue->checkQueue(this);
+
+				int iNextFailed = sendQueue->findNextFailed(this);
+				if (iNextFailed >= 0)
+					sendQueue->handleError(this, iNextFailed);
+			}
+		}
+	}
+}
+
+int SI_InitStatusIcons()
+{
+	StatusIconData sid = {};
+	sid.szModule = MSG_ICON_MODULE;
+	sid.dwId = MSG_ICON_SOUND; // Sounds
+	Srmm_AddIcon(&sid, &g_plugin);
+
+	sid.dwId = MSG_ICON_UTN;
+	Srmm_AddIcon(&sid, &g_plugin);
+
+	HookEvent(ME_MSG_ICONSCHANGED, OnSrmmIconChanged);
+	return 0;
+}
diff --git a/plugins/TabSRMM/src/globals.cpp b/plugins/TabSRMM/src/globals.cpp
index 953996a5cd..a80ff3f2b2 100644
--- a/plugins/TabSRMM/src/globals.cpp
+++ b/plugins/TabSRMM/src/globals.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/globals.h b/plugins/TabSRMM/src/globals.h
index fb52d01df2..db6229428e 100644
--- a/plugins/TabSRMM/src/globals.h
+++ b/plugins/TabSRMM/src/globals.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/hotkeyhandler.cpp b/plugins/TabSRMM/src/hotkeyhandler.cpp
index ebbd290ec5..5deb191552 100644
--- a/plugins/TabSRMM/src/hotkeyhandler.cpp
+++ b/plugins/TabSRMM/src/hotkeyhandler.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/infopanel.cpp b/plugins/TabSRMM/src/infopanel.cpp
index 69b1400fcb..37d433cd7f 100644
--- a/plugins/TabSRMM/src/infopanel.cpp
+++ b/plugins/TabSRMM/src/infopanel.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/infopanel.h b/plugins/TabSRMM/src/infopanel.h
index ead415ea29..ffa8c884a1 100644
--- a/plugins/TabSRMM/src/infopanel.h
+++ b/plugins/TabSRMM/src/infopanel.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/mim.cpp b/plugins/TabSRMM/src/mim.cpp
index 736c9c5e86..ca1e9c3272 100644
--- a/plugins/TabSRMM/src/mim.cpp
+++ b/plugins/TabSRMM/src/mim.cpp
@@ -1,488 +1,488 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/IM project,
-// all portions of this codebase are copyrighted to the people
-// listed in contributors.txt.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// you should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// wraps some parts of Miranda API
-// Also, OS dependent stuff (visual styles api etc.)
-
-#include "stdafx.h"
-
-PDTTE  CMimAPI::m_pfnDrawThemeTextEx = nullptr;
-DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = nullptr;
-DICE   CMimAPI::m_pfnDwmIsCompositionEnabled = nullptr;
-DRT    CMimAPI::m_pfnDwmRegisterThumbnail = nullptr;
-BPI    CMimAPI::m_pfnBufferedPaintInit = nullptr;
-BPU    CMimAPI::m_pfnBufferedPaintUninit = nullptr;
-BBP    CMimAPI::m_pfnBeginBufferedPaint = nullptr;
-EBP    CMimAPI::m_pfnEndBufferedPaint = nullptr;
-BBW    CMimAPI::m_pfnDwmBlurBehindWindow = nullptr;
-DGC    CMimAPI::m_pfnDwmGetColorizationColor = nullptr;
-BPSA   CMimAPI::m_pfnBufferedPaintSetAlpha = nullptr;
-DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = nullptr;
-DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = nullptr;
-DWMUT  CMimAPI::m_pfnDwmUpdateThumbnailProperties = nullptr;
-DURT   CMimAPI::m_pfnDwmUnregisterThumbnail = nullptr;
-DSIT   CMimAPI::m_pfnDwmSetIconicThumbnail = nullptr;
-DSILP  CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = nullptr;
-bool   CMimAPI::m_shutDown = 0;
-wchar_t  CMimAPI::m_userDir[] = L"\0";
-
-bool   CMimAPI::m_haveBufferedPaint = false;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMimAPI::FoldersPathChanged(WPARAM, LPARAM)
-{
-	return M.foldersPathChanged();
-}
-
-void CMimAPI::configureCustomFolders()
-{
-	m_hDataPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Data path"), const_cast<wchar_t *>(getDataPath()));
-	m_hSkinsPath = FoldersRegisterCustomPathW(LPGEN("Skins"), LPGEN("TabSRMM"), const_cast<wchar_t *>(getSkinPath()));
-	m_hAvatarsPath = FoldersRegisterCustomPathW(LPGEN("Avatars"), LPGEN("Saved TabSRMM avatars"), const_cast<wchar_t *>(getSavedAvatarPath()));
-	m_hChatLogsPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Group chat logs root"), const_cast<wchar_t *>(getChatLogPath()));
-
-	if (m_hDataPath)
-		HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged);
-
-	foldersPathChanged();
-}
-
-INT_PTR CMimAPI::foldersPathChanged()
-{
-	wchar_t szTemp[MAX_PATH + 2];
-
-	if (m_hDataPath) {
-		szTemp[0] = 0;
-		FoldersGetCustomPathW(m_hDataPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getDataPath()));
-		wcsncpy_s(m_szProfilePath, szTemp, _TRUNCATE);
-
-		szTemp[0] = 0;
-		FoldersGetCustomPathW(m_hSkinsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSkinPath()));
-		wcsncpy_s(m_szSkinsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
-		Utils::ensureTralingBackslash(m_szSkinsPath);
-
-		szTemp[0] = 0;
-		FoldersGetCustomPathW(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSavedAvatarPath()));
-		wcsncpy_s(m_szSavedAvatarsPath, szTemp, _TRUNCATE);
-
-		szTemp[0] = 0;
-		FoldersGetCustomPathW(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getChatLogPath()));
-		wcsncpy_s(m_szChatLogsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
-		Utils::ensureTralingBackslash(m_szChatLogsPath);
-	}
-
-	CreateDirectoryTreeW(m_szProfilePath);
-	CreateDirectoryTreeW(m_szSkinsPath);
-	CreateDirectoryTreeW(m_szSavedAvatarsPath);
-
-	Skin->extractSkinsAndLogo(true);
-	Skin->setupAeroSkins();
-	return 0;
-}
-
-const wchar_t* CMimAPI::getUserDir()
-{
-	if (m_userDir[0] == 0) {
-		if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
-			wcsncpy_s(m_userDir, L"%miranda_userdata%", _TRUNCATE);
-		else
-			wcsncpy_s(m_userDir, VARSW(L"%miranda_userdata%"), _TRUNCATE);
-
-		Utils::ensureTralingBackslash(m_userDir);
-	}
-	return m_userDir;
-}
-
-void CMimAPI::InitPaths()
-{
-	const wchar_t *szUserdataDir = getUserDir();
-
-	mir_snwprintf(m_szProfilePath, L"%stabSRMM", szUserdataDir);
-	if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) {
-		wcsncpy_s(m_szChatLogsPath, L"%miranda_logpath%", _TRUNCATE);
-		wcsncpy_s(m_szSkinsPath, L"%miranda_path%\\Skins\\TabSRMM", _TRUNCATE);
-	}
-	else {
-		wcsncpy_s(m_szChatLogsPath, VARSW(L"%miranda_logpath%"), _TRUNCATE);
-		wcsncpy_s(m_szSkinsPath, VARSW(L"%miranda_path%\\Skins\\TabSRMM"), _TRUNCATE);
-	}
-
-	Utils::ensureTralingBackslash(m_szChatLogsPath);
-	replaceStrW(g_Settings.pszLogDir, m_szChatLogsPath);
-
-	Utils::ensureTralingBackslash(m_szSkinsPath);
-
-	mir_snwprintf(m_szSavedAvatarsPath, L"%s\\Saved Contact Pictures", m_szProfilePath);
-}
-
-bool CMimAPI::getAeroState()
-{
-	m_isAero = m_DwmActive = false;
-	if (IsWinVerVistaPlus()) {
-		BOOL result = FALSE;
-		m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result);
-		m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive;
-
-	}
-	m_isVsThemed = IsThemeActive() != 0;
-	return m_isAero;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Initialize various Win32 API functions which are not common to all versions of Windows.
-// We have to work with functions pointers here.
-
-void CMimAPI::InitAPI()
-{
-	m_hUxTheme = nullptr;
-	m_hDwmApi = nullptr;
-
-	// vista+ DWM API
-	if (IsWinVerVistaPlus()) {
-		m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll");
-		if (m_hDwmApi) {
-			m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi, "DwmExtendFrameIntoClientArea");
-			m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi, "DwmIsCompositionEnabled");
-			m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail");
-			m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow");
-			m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor");
-			m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps");
-			m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute");
-			m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties");
-			m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail");
-			m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail");
-			m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap");
-		}
-
-		// additional uxtheme APIs (Vista+)
-		m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll");
-		if (m_hUxTheme) {
-			m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx");
-			m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint");
-			m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint");
-			m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit");
-			m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit");
-			m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha");
-			m_haveBufferedPaint = (m_pfnBeginBufferedPaint != nullptr && m_pfnEndBufferedPaint != nullptr) ? true : false;
-			if (m_haveBufferedPaint)
-				m_pfnBufferedPaintInit();
-		}
-	}
-	else m_haveBufferedPaint = false;
-
-	switch (GetByte("default_ieview", -1)) {
-	case 1:
-		db_set_s(0, "SRMM", "Logger", "ieview");
-		__fallthrough;
-
-	case 0:
-		db_unset(0, SRMSGMOD_T, "default_ieview");
-	}
-
-	switch (GetByte("default_hpp", -1)) {
-	case 1:
-		db_set_s(0, "SRMM", "Logger", "hpp");
-		__fallthrough;
-
-	case 0:
-		db_unset(0, SRMSGMOD_T, "default_hpp");
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// hook subscriber function for incoming message typing events
-
-int CMimAPI::TypingMessage(WPARAM hContact, LPARAM nSecs)
-{
-	int foundWin = 0, preTyping = 0;
-	BOOL fShowOnClist = TRUE;
-
-	auto *pDlg = Srmm_FindDialog(hContact);
-	MCONTACT hMeta = db_mc_getMeta(hContact);
-	if (hMeta) {
-		if (!pDlg)
-			pDlg = Srmm_FindDialog(hMeta);
-		hContact = hMeta;
-	}
-
-	if (pDlg && g_plugin.getByte(SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING))
-		preTyping = pDlg->Typing(nSecs);
-
-	if (pDlg && IsWindowVisible(pDlg->GetHwnd()))
-		foundWin = MessageWindowOpened(0, pDlg);
-	else
-		foundWin = 0;
-
-	TContainerData *pContainer = nullptr;
-	if (pDlg) {
-		pContainer = pDlg->m_pContainer;
-		if (pContainer == nullptr) // should never happen
-			return 0;
-	}
-
-	if (g_plugin.getByte(SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) {
-		if (!pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGNOWINOPEN, 1))
-			fShowOnClist = false;
-		if (pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGWINOPEN, 1))
-			fShowOnClist = false;
-	}
-	else fShowOnClist = false;
-
-	if ((!foundWin || !pContainer->cfg.flags.m_bNoSound) && preTyping != (nSecs != 0))
-		Skin_PlaySound(nSecs ? "TNStart" : "TNStop");
-
-	if (g_plugin.bPopups) {
-		BOOL fShow = false;
-		int  iMode = M.GetByte("MTN_PopupMode", 0);
-
-		switch (iMode) {
-		case 0:
-			fShow = true;
-			break;
-		case 1:
-			if (!foundWin || !(pContainer && pContainer->m_hwndActive == pDlg->GetHwnd() && GetForegroundWindow() == pContainer->m_hwnd))
-				fShow = true;
-			break;
-		case 2:
-			if (pDlg == nullptr)
-				fShow = true;
-			else {
-				if (g_plugin.bHideOnClose) {
-					TContainerData *pCont = pDlg->m_pContainer;
-					if (pCont && pCont->m_bHidden)
-						fShow = true;
-				}
-			}
-			break;
-		}
-		if (fShow)
-			TN_TypingMessage(hContact, nSecs);
-	}
-
-	if (nSecs) {
-		wchar_t szTip[256];
-		mir_snwprintf(szTip, TranslateT("%s is typing a message"), Clist_GetContactDisplayName(hContact));
-		if (fShowOnClist && g_plugin.getByte("ShowTypingBalloon", 0))
-			Clist_TrayNotifyW(nullptr, TranslateT("Typing notification"), szTip, NIIF_INFO, 1000 * 4);
-
-		if (fShowOnClist) {
-			g_clistApi.pfnRemoveEvent(hContact, 1);
-
-			CLISTEVENT cle = {};
-			cle.hContact = hContact;
-			cle.hDbEvent = 1;
-			cle.flags = CLEF_ONLYAFEW | CLEF_UNICODE;
-			cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
-			cle.pszService = MS_MSG_TYPINGMESSAGE;
-			cle.szTooltip.w = szTip;
-			g_clistApi.pfnAddEvent(&cle);
-		}
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// this is the global ack dispatcher.It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events
-// for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches
-// it to the owners window
-//
-// ACKTYPE_AVATAR no longer handled here, because we have avs services now.
-
-int CMimAPI::ProtoAck(WPARAM, LPARAM lParam)
-{
-	ACKDATA *pAck = (ACKDATA*)lParam;
-
-	if ((pAck != nullptr) && (pAck->type == ACKTYPE_MESSAGE)) {
-		int i = 0, iFound = SendQueue::NR_SENDJOBS;
-		SendJob *jobs = sendQueue->getJobByIndex(0);
-		MCONTACT hMeta = db_mc_getMeta(pAck->hContact);
-		for (int j = 0; j < SendQueue::NR_SENDJOBS; j++) {
-			SendJob &p = jobs[j];
-			if (pAck->hProcess == (HANDLE)p.iSendId && pAck->hContact == p.hContact) {
-				CMsgDialog *dat = p.hOwnerWnd ? (CMsgDialog*)GetWindowLongPtr(p.hOwnerWnd, GWLP_USERDATA) : nullptr;
-				if (dat == nullptr) {
-					sendQueue->ackMessage(nullptr, (WPARAM)MAKELONG(j, i), lParam);
-					return 0;
-				}
-				if (dat->m_hContact == p.hContact || dat->m_hContact == hMeta) {
-					iFound = j;
-					break;
-				}
-			}
-		}
-		if (iFound == SendQueue::NR_SENDJOBS)     // no matching send info found in the queue
-			SendLater::processAck(pAck);
-		else                                      // try to find the process handle in the list of open send later jobs
-			SendMessage(jobs[iFound].hOwnerWnd, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam);
-	}
-	return 0;
-}
-
-int CMimAPI::PrebuildContactMenu(WPARAM hContact, LPARAM)
-{
-	if (hContact == 0)
-		return 0;
-
-	bool bEnabled = false;
-	char *szProto = Proto_GetBaseAccountName(hContact);
-	if (szProto) {
-		// leave this menu item hidden for chats
-		if (!Contact::IsGroupChat(hContact, szProto))
-			if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
-				bEnabled = true;
-	}
-
-	Menu_ShowItem(PluginConfig.m_hMenuItem, bEnabled);
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// this handler is called first in the message window chain - it will handle events for which a message window
-// is already open. if not, it will do nothing and the 2nd handler(MessageEventAdded) will perform all
-// the needed actions.
-//
-// this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will
-// improve the overall responsiveness when receiving messages.
-
-int CMimAPI::DispatchNewEvent(WPARAM hContact, LPARAM hDbEvent)
-{
-	if (hContact) {
-		Utils::sendContactMessage(hContact, HM_DBEVENTADDED, hContact, hDbEvent);
-
-		// we're in meta and an event belongs to a sub
-		MCONTACT hReal = db_event_getContact(hDbEvent);
-		if (hReal != hContact)
-			Utils::sendContactMessage(hReal, HM_DBEVENTADDED, hContact, hDbEvent);
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Message event added is called when a new message is added to the database
-// if no session is open for the contact, this function will determine if and how a new message
-// session(tab) must be created.
-//
-// if a session is already created, it just does nothing and DispatchNewEvent() will take care.
-
-int CMimAPI::MessageEventAdded(WPARAM hContact, LPARAM hDbEvent)
-{
-	if (hContact == 0)
-		return 0;
-
-	DBEVENTINFO dbei = {};
-	db_event_get(hDbEvent, &dbei);
-
-	auto *pDlg = Srmm_FindDialog(hContact);
-	if (pDlg == nullptr)
-		pDlg = Srmm_FindDialog(db_event_getContact(hDbEvent));
-
-	BOOL isCustomEvent = IsCustomEvent(dbei.eventType);
-	BOOL isShownCustomEvent = DbEventIsForMsgWindow(&dbei);
-	if (dbei.markedRead() || (isCustomEvent && !isShownCustomEvent))
-		return 0;
-
-	g_clistApi.pfnRemoveEvent(hContact, 1);
-
-	bool bAutoPopup = g_plugin.bAutoPopup;
-	bool bAutoCreate = g_plugin.bAutoTabs;
-	bool bAutoContainer = g_plugin.bAutoContainer;
-
-	if (pDlg) {
-		TContainerData *pTargetContainer = pDlg->m_pContainer;
-		if (pTargetContainer == nullptr || !g_plugin.bHideOnClose || IsWindowVisible(pTargetContainer->m_hwnd))
-			return 0;
-
-		WINDOWPLACEMENT wp = { 0 };
-		wp.length = sizeof(wp);
-		GetWindowPlacement(pTargetContainer->m_hwnd, &wp);
-
-		wchar_t szName[CONTAINER_NAMELEN + 1];
-		GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN);
-
-		if (bAutoPopup || bAutoCreate) {
-			if (bAutoPopup) {
-				if (wp.showCmd == SW_SHOWMAXIMIZED)
-					ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMAXIMIZED);
-				else
-					ShowWindow(pTargetContainer->m_hwnd, SW_SHOWNOACTIVATE);
-				return 0;
-			}
-
-			TContainerData *pContainer = FindContainerByName(szName);
-			if (pContainer != nullptr) {
-				if (bAutoContainer) {
-					ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
-					return 0;
-				}
-				goto nowindowcreate;
-			}
-			else if (bAutoContainer) {
-				ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
-				return 0;
-			}
-		}
-	}
-	else {
-		switch (dbei.eventType) {
-		case EVENTTYPE_AUTHREQUEST:
-		case EVENTTYPE_ADDED:
-		case EVENTTYPE_FILE:
-			return 0;
-		}
-	}
-
-	if (!NEN::bNoSounds)
-		Skin_PlaySound("AlertMsg");
-
-	if (NEN::bNoAutoPopup)
-		goto nowindowcreate;
-
-	PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_CREATECONTAINER, hContact, hDbEvent);
-	return 0;
-
-nowindowcreate:
-	// for tray support, we add the event to the tray menu. otherwise we send it back to
-	// the contact list for flashing
-	if (!(dbei.flags & DBEF_READ)) {
-		AddUnreadContact(hContact);
-
-		wchar_t toolTip[256];
-		mir_snwprintf(toolTip, TranslateT("Message from %s"), Clist_GetContactDisplayName(hContact));
-
-		CLISTEVENT cle = {};
-		cle.hContact = hContact;
-		cle.hDbEvent = hDbEvent;
-		cle.flags = CLEF_UNICODE;
-		cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
-		cle.pszService = MS_MSG_READMESSAGE;
-		cle.szTooltip.w = toolTip;
-		g_clistApi.pfnAddEvent(&cle);
-	}
-	return 0;
-}
-
-CMimAPI M;
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// wraps some parts of Miranda API
+// Also, OS dependent stuff (visual styles api etc.)
+
+#include "stdafx.h"
+
+PDTTE  CMimAPI::m_pfnDrawThemeTextEx = nullptr;
+DEFICA CMimAPI::m_pfnDwmExtendFrameIntoClientArea = nullptr;
+DICE   CMimAPI::m_pfnDwmIsCompositionEnabled = nullptr;
+DRT    CMimAPI::m_pfnDwmRegisterThumbnail = nullptr;
+BPI    CMimAPI::m_pfnBufferedPaintInit = nullptr;
+BPU    CMimAPI::m_pfnBufferedPaintUninit = nullptr;
+BBP    CMimAPI::m_pfnBeginBufferedPaint = nullptr;
+EBP    CMimAPI::m_pfnEndBufferedPaint = nullptr;
+BBW    CMimAPI::m_pfnDwmBlurBehindWindow = nullptr;
+DGC    CMimAPI::m_pfnDwmGetColorizationColor = nullptr;
+BPSA   CMimAPI::m_pfnBufferedPaintSetAlpha = nullptr;
+DWMIIB CMimAPI::m_pfnDwmInvalidateIconicBitmaps = nullptr;
+DWMSWA CMimAPI::m_pfnDwmSetWindowAttribute = nullptr;
+DWMUT  CMimAPI::m_pfnDwmUpdateThumbnailProperties = nullptr;
+DURT   CMimAPI::m_pfnDwmUnregisterThumbnail = nullptr;
+DSIT   CMimAPI::m_pfnDwmSetIconicThumbnail = nullptr;
+DSILP  CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap = nullptr;
+bool   CMimAPI::m_shutDown = 0;
+wchar_t  CMimAPI::m_userDir[] = L"\0";
+
+bool   CMimAPI::m_haveBufferedPaint = false;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMimAPI::FoldersPathChanged(WPARAM, LPARAM)
+{
+	return M.foldersPathChanged();
+}
+
+void CMimAPI::configureCustomFolders()
+{
+	m_hDataPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Data path"), const_cast<wchar_t *>(getDataPath()));
+	m_hSkinsPath = FoldersRegisterCustomPathW(LPGEN("Skins"), LPGEN("TabSRMM"), const_cast<wchar_t *>(getSkinPath()));
+	m_hAvatarsPath = FoldersRegisterCustomPathW(LPGEN("Avatars"), LPGEN("Saved TabSRMM avatars"), const_cast<wchar_t *>(getSavedAvatarPath()));
+	m_hChatLogsPath = FoldersRegisterCustomPathW(LPGEN("TabSRMM"), LPGEN("Group chat logs root"), const_cast<wchar_t *>(getChatLogPath()));
+
+	if (m_hDataPath)
+		HookEvent(ME_FOLDERS_PATH_CHANGED, CMimAPI::FoldersPathChanged);
+
+	foldersPathChanged();
+}
+
+INT_PTR CMimAPI::foldersPathChanged()
+{
+	wchar_t szTemp[MAX_PATH + 2];
+
+	if (m_hDataPath) {
+		szTemp[0] = 0;
+		FoldersGetCustomPathW(m_hDataPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getDataPath()));
+		wcsncpy_s(m_szProfilePath, szTemp, _TRUNCATE);
+
+		szTemp[0] = 0;
+		FoldersGetCustomPathW(m_hSkinsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSkinPath()));
+		wcsncpy_s(m_szSkinsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
+		Utils::ensureTralingBackslash(m_szSkinsPath);
+
+		szTemp[0] = 0;
+		FoldersGetCustomPathW(m_hAvatarsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getSavedAvatarPath()));
+		wcsncpy_s(m_szSavedAvatarsPath, szTemp, _TRUNCATE);
+
+		szTemp[0] = 0;
+		FoldersGetCustomPathW(m_hChatLogsPath, szTemp, MAX_PATH, const_cast<wchar_t *>(getChatLogPath()));
+		wcsncpy_s(m_szChatLogsPath, (MAX_PATH - 1), szTemp, _TRUNCATE);
+		Utils::ensureTralingBackslash(m_szChatLogsPath);
+	}
+
+	CreateDirectoryTreeW(m_szProfilePath);
+	CreateDirectoryTreeW(m_szSkinsPath);
+	CreateDirectoryTreeW(m_szSavedAvatarsPath);
+
+	Skin->extractSkinsAndLogo(true);
+	Skin->setupAeroSkins();
+	return 0;
+}
+
+const wchar_t* CMimAPI::getUserDir()
+{
+	if (m_userDir[0] == 0) {
+		if (ServiceExists(MS_FOLDERS_REGISTER_PATH))
+			wcsncpy_s(m_userDir, L"%miranda_userdata%", _TRUNCATE);
+		else
+			wcsncpy_s(m_userDir, VARSW(L"%miranda_userdata%"), _TRUNCATE);
+
+		Utils::ensureTralingBackslash(m_userDir);
+	}
+	return m_userDir;
+}
+
+void CMimAPI::InitPaths()
+{
+	const wchar_t *szUserdataDir = getUserDir();
+
+	mir_snwprintf(m_szProfilePath, L"%stabSRMM", szUserdataDir);
+	if (ServiceExists(MS_FOLDERS_REGISTER_PATH)) {
+		wcsncpy_s(m_szChatLogsPath, L"%miranda_logpath%", _TRUNCATE);
+		wcsncpy_s(m_szSkinsPath, L"%miranda_path%\\Skins\\TabSRMM", _TRUNCATE);
+	}
+	else {
+		wcsncpy_s(m_szChatLogsPath, VARSW(L"%miranda_logpath%"), _TRUNCATE);
+		wcsncpy_s(m_szSkinsPath, VARSW(L"%miranda_path%\\Skins\\TabSRMM"), _TRUNCATE);
+	}
+
+	Utils::ensureTralingBackslash(m_szChatLogsPath);
+	replaceStrW(g_Settings.pszLogDir, m_szChatLogsPath);
+
+	Utils::ensureTralingBackslash(m_szSkinsPath);
+
+	mir_snwprintf(m_szSavedAvatarsPath, L"%s\\Saved Contact Pictures", m_szProfilePath);
+}
+
+bool CMimAPI::getAeroState()
+{
+	m_isAero = m_DwmActive = false;
+	if (IsWinVerVistaPlus()) {
+		BOOL result = FALSE;
+		m_DwmActive = (m_pfnDwmIsCompositionEnabled && (m_pfnDwmIsCompositionEnabled(&result) == S_OK) && result);
+		m_isAero = (CSkin::m_skinEnabled == false) && GetByte("useAero", 1) && CSkin::m_fAeroSkinsValid && m_DwmActive;
+
+	}
+	m_isVsThemed = IsThemeActive() != 0;
+	return m_isAero;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Initialize various Win32 API functions which are not common to all versions of Windows.
+// We have to work with functions pointers here.
+
+void CMimAPI::InitAPI()
+{
+	m_hUxTheme = nullptr;
+	m_hDwmApi = nullptr;
+
+	// vista+ DWM API
+	if (IsWinVerVistaPlus()) {
+		m_hDwmApi = Utils::loadSystemLibrary(L"\\dwmapi.dll");
+		if (m_hDwmApi) {
+			m_pfnDwmExtendFrameIntoClientArea = (DEFICA)GetProcAddress(m_hDwmApi, "DwmExtendFrameIntoClientArea");
+			m_pfnDwmIsCompositionEnabled = (DICE)GetProcAddress(m_hDwmApi, "DwmIsCompositionEnabled");
+			m_pfnDwmRegisterThumbnail = (DRT)GetProcAddress(m_hDwmApi, "DwmRegisterThumbnail");
+			m_pfnDwmBlurBehindWindow = (BBW)GetProcAddress(m_hDwmApi, "DwmEnableBlurBehindWindow");
+			m_pfnDwmGetColorizationColor = (DGC)GetProcAddress(m_hDwmApi, "DwmGetColorizationColor");
+			m_pfnDwmInvalidateIconicBitmaps = (DWMIIB)GetProcAddress(m_hDwmApi, "DwmInvalidateIconicBitmaps");
+			m_pfnDwmSetWindowAttribute = (DWMSWA)GetProcAddress(m_hDwmApi, "DwmSetWindowAttribute");
+			m_pfnDwmUpdateThumbnailProperties = (DWMUT)GetProcAddress(m_hDwmApi, "DwmUpdateThumbnailProperties");
+			m_pfnDwmUnregisterThumbnail = (DURT)GetProcAddress(m_hDwmApi, "DwmUnregisterThumbnail");
+			m_pfnDwmSetIconicThumbnail = (DSIT)GetProcAddress(m_hDwmApi, "DwmSetIconicThumbnail");
+			m_pfnDwmSetIconicLivePreviewBitmap = (DSILP)GetProcAddress(m_hDwmApi, "DwmSetIconicLivePreviewBitmap");
+		}
+
+		// additional uxtheme APIs (Vista+)
+		m_hUxTheme = Utils::loadSystemLibrary(L"\\uxtheme.dll");
+		if (m_hUxTheme) {
+			m_pfnDrawThemeTextEx = (PDTTE)GetProcAddress(m_hUxTheme, "DrawThemeTextEx");
+			m_pfnBeginBufferedPaint = (BBP)GetProcAddress(m_hUxTheme, "BeginBufferedPaint");
+			m_pfnEndBufferedPaint = (EBP)GetProcAddress(m_hUxTheme, "EndBufferedPaint");
+			m_pfnBufferedPaintInit = (BPI)GetProcAddress(m_hUxTheme, "BufferedPaintInit");
+			m_pfnBufferedPaintUninit = (BPU)GetProcAddress(m_hUxTheme, "BufferedPaintUnInit");
+			m_pfnBufferedPaintSetAlpha = (BPSA)GetProcAddress(m_hUxTheme, "BufferedPaintSetAlpha");
+			m_haveBufferedPaint = (m_pfnBeginBufferedPaint != nullptr && m_pfnEndBufferedPaint != nullptr) ? true : false;
+			if (m_haveBufferedPaint)
+				m_pfnBufferedPaintInit();
+		}
+	}
+	else m_haveBufferedPaint = false;
+
+	switch (GetByte("default_ieview", -1)) {
+	case 1:
+		db_set_s(0, "SRMM", "Logger", "ieview");
+		__fallthrough;
+
+	case 0:
+		db_unset(0, SRMSGMOD_T, "default_ieview");
+	}
+
+	switch (GetByte("default_hpp", -1)) {
+	case 1:
+		db_set_s(0, "SRMM", "Logger", "hpp");
+		__fallthrough;
+
+	case 0:
+		db_unset(0, SRMSGMOD_T, "default_hpp");
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// hook subscriber function for incoming message typing events
+
+int CMimAPI::TypingMessage(WPARAM hContact, LPARAM nSecs)
+{
+	int foundWin = 0, preTyping = 0;
+	BOOL fShowOnClist = TRUE;
+
+	auto *pDlg = Srmm_FindDialog(hContact);
+	MCONTACT hMeta = db_mc_getMeta(hContact);
+	if (hMeta) {
+		if (!pDlg)
+			pDlg = Srmm_FindDialog(hMeta);
+		hContact = hMeta;
+	}
+
+	if (pDlg && g_plugin.getByte(SRMSGSET_SHOWTYPING, SRMSGDEFSET_SHOWTYPING))
+		preTyping = pDlg->Typing(nSecs);
+
+	if (pDlg && IsWindowVisible(pDlg->GetHwnd()))
+		foundWin = MessageWindowOpened(0, pDlg);
+	else
+		foundWin = 0;
+
+	TContainerData *pContainer = nullptr;
+	if (pDlg) {
+		pContainer = pDlg->m_pContainer;
+		if (pContainer == nullptr) // should never happen
+			return 0;
+	}
+
+	if (g_plugin.getByte(SRMSGSET_SHOWTYPINGCLIST, SRMSGDEFSET_SHOWTYPINGCLIST)) {
+		if (!pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGNOWINOPEN, 1))
+			fShowOnClist = false;
+		if (pDlg && !g_plugin.getByte(SRMSGSET_SHOWTYPINGWINOPEN, 1))
+			fShowOnClist = false;
+	}
+	else fShowOnClist = false;
+
+	if ((!foundWin || !pContainer->cfg.flags.m_bNoSound) && preTyping != (nSecs != 0))
+		Skin_PlaySound(nSecs ? "TNStart" : "TNStop");
+
+	if (g_plugin.bPopups) {
+		BOOL fShow = false;
+		int  iMode = M.GetByte("MTN_PopupMode", 0);
+
+		switch (iMode) {
+		case 0:
+			fShow = true;
+			break;
+		case 1:
+			if (!foundWin || !(pContainer && pContainer->m_hwndActive == pDlg->GetHwnd() && GetForegroundWindow() == pContainer->m_hwnd))
+				fShow = true;
+			break;
+		case 2:
+			if (pDlg == nullptr)
+				fShow = true;
+			else {
+				if (g_plugin.bHideOnClose) {
+					TContainerData *pCont = pDlg->m_pContainer;
+					if (pCont && pCont->m_bHidden)
+						fShow = true;
+				}
+			}
+			break;
+		}
+		if (fShow)
+			TN_TypingMessage(hContact, nSecs);
+	}
+
+	if (nSecs) {
+		wchar_t szTip[256];
+		mir_snwprintf(szTip, TranslateT("%s is typing a message"), Clist_GetContactDisplayName(hContact));
+		if (fShowOnClist && g_plugin.getByte("ShowTypingBalloon", 0))
+			Clist_TrayNotifyW(nullptr, TranslateT("Typing notification"), szTip, NIIF_INFO, 1000 * 4);
+
+		if (fShowOnClist) {
+			g_clistApi.pfnRemoveEvent(hContact, 1);
+
+			CLISTEVENT cle = {};
+			cle.hContact = hContact;
+			cle.hDbEvent = 1;
+			cle.flags = CLEF_ONLYAFEW | CLEF_UNICODE;
+			cle.hIcon = PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING];
+			cle.pszService = MS_MSG_TYPINGMESSAGE;
+			cle.szTooltip.w = szTip;
+			g_clistApi.pfnAddEvent(&cle);
+		}
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this is the global ack dispatcher.It handles both ACKTYPE_MESSAGE and ACKTYPE_AVATAR events
+// for ACKTYPE_MESSAGE it searches the corresponding send job in the queue and, if found, dispatches
+// it to the owners window
+//
+// ACKTYPE_AVATAR no longer handled here, because we have avs services now.
+
+int CMimAPI::ProtoAck(WPARAM, LPARAM lParam)
+{
+	ACKDATA *pAck = (ACKDATA*)lParam;
+
+	if ((pAck != nullptr) && (pAck->type == ACKTYPE_MESSAGE)) {
+		int i = 0, iFound = SendQueue::NR_SENDJOBS;
+		SendJob *jobs = sendQueue->getJobByIndex(0);
+		MCONTACT hMeta = db_mc_getMeta(pAck->hContact);
+		for (int j = 0; j < SendQueue::NR_SENDJOBS; j++) {
+			SendJob &p = jobs[j];
+			if (pAck->hProcess == (HANDLE)p.iSendId && pAck->hContact == p.hContact) {
+				CMsgDialog *dat = p.hOwnerWnd ? (CMsgDialog*)GetWindowLongPtr(p.hOwnerWnd, GWLP_USERDATA) : nullptr;
+				if (dat == nullptr) {
+					sendQueue->ackMessage(nullptr, (WPARAM)MAKELONG(j, i), lParam);
+					return 0;
+				}
+				if (dat->m_hContact == p.hContact || dat->m_hContact == hMeta) {
+					iFound = j;
+					break;
+				}
+			}
+		}
+		if (iFound == SendQueue::NR_SENDJOBS)     // no matching send info found in the queue
+			SendLater::processAck(pAck);
+		else                                      // try to find the process handle in the list of open send later jobs
+			SendMessage(jobs[iFound].hOwnerWnd, HM_EVENTSENT, (WPARAM)MAKELONG(iFound, i), lParam);
+	}
+	return 0;
+}
+
+int CMimAPI::PrebuildContactMenu(WPARAM hContact, LPARAM)
+{
+	if (hContact == 0)
+		return 0;
+
+	bool bEnabled = false;
+	char *szProto = Proto_GetBaseAccountName(hContact);
+	if (szProto) {
+		// leave this menu item hidden for chats
+		if (!Contact::IsGroupChat(hContact, szProto))
+			if (CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IMSEND)
+				bEnabled = true;
+	}
+
+	Menu_ShowItem(PluginConfig.m_hMenuItem, bEnabled);
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this handler is called first in the message window chain - it will handle events for which a message window
+// is already open. if not, it will do nothing and the 2nd handler(MessageEventAdded) will perform all
+// the needed actions.
+//
+// this handler POSTs the event to the message window procedure - so it is fast and can exit quickly which will
+// improve the overall responsiveness when receiving messages.
+
+int CMimAPI::DispatchNewEvent(WPARAM hContact, LPARAM hDbEvent)
+{
+	if (hContact) {
+		Utils::sendContactMessage(hContact, HM_DBEVENTADDED, hContact, hDbEvent);
+
+		// we're in meta and an event belongs to a sub
+		MCONTACT hReal = db_event_getContact(hDbEvent);
+		if (hReal != hContact)
+			Utils::sendContactMessage(hReal, HM_DBEVENTADDED, hContact, hDbEvent);
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Message event added is called when a new message is added to the database
+// if no session is open for the contact, this function will determine if and how a new message
+// session(tab) must be created.
+//
+// if a session is already created, it just does nothing and DispatchNewEvent() will take care.
+
+int CMimAPI::MessageEventAdded(WPARAM hContact, LPARAM hDbEvent)
+{
+	if (hContact == 0)
+		return 0;
+
+	DBEVENTINFO dbei = {};
+	db_event_get(hDbEvent, &dbei);
+
+	auto *pDlg = Srmm_FindDialog(hContact);
+	if (pDlg == nullptr)
+		pDlg = Srmm_FindDialog(db_event_getContact(hDbEvent));
+
+	BOOL isCustomEvent = IsCustomEvent(dbei.eventType);
+	BOOL isShownCustomEvent = DbEventIsForMsgWindow(&dbei);
+	if (dbei.markedRead() || (isCustomEvent && !isShownCustomEvent))
+		return 0;
+
+	g_clistApi.pfnRemoveEvent(hContact, 1);
+
+	bool bAutoPopup = g_plugin.bAutoPopup;
+	bool bAutoCreate = g_plugin.bAutoTabs;
+	bool bAutoContainer = g_plugin.bAutoContainer;
+
+	if (pDlg) {
+		TContainerData *pTargetContainer = pDlg->m_pContainer;
+		if (pTargetContainer == nullptr || !g_plugin.bHideOnClose || IsWindowVisible(pTargetContainer->m_hwnd))
+			return 0;
+
+		WINDOWPLACEMENT wp = { 0 };
+		wp.length = sizeof(wp);
+		GetWindowPlacement(pTargetContainer->m_hwnd, &wp);
+
+		wchar_t szName[CONTAINER_NAMELEN + 1];
+		GetContainerNameForContact(hContact, szName, CONTAINER_NAMELEN);
+
+		if (bAutoPopup || bAutoCreate) {
+			if (bAutoPopup) {
+				if (wp.showCmd == SW_SHOWMAXIMIZED)
+					ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMAXIMIZED);
+				else
+					ShowWindow(pTargetContainer->m_hwnd, SW_SHOWNOACTIVATE);
+				return 0;
+			}
+
+			TContainerData *pContainer = FindContainerByName(szName);
+			if (pContainer != nullptr) {
+				if (bAutoContainer) {
+					ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
+					return 0;
+				}
+				goto nowindowcreate;
+			}
+			else if (bAutoContainer) {
+				ShowWindow(pTargetContainer->m_hwnd, SW_SHOWMINNOACTIVE);
+				return 0;
+			}
+		}
+	}
+	else {
+		switch (dbei.eventType) {
+		case EVENTTYPE_AUTHREQUEST:
+		case EVENTTYPE_ADDED:
+		case EVENTTYPE_FILE:
+			return 0;
+		}
+	}
+
+	if (!NEN::bNoSounds)
+		Skin_PlaySound("AlertMsg");
+
+	if (NEN::bNoAutoPopup)
+		goto nowindowcreate;
+
+	PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_CREATECONTAINER, hContact, hDbEvent);
+	return 0;
+
+nowindowcreate:
+	// for tray support, we add the event to the tray menu. otherwise we send it back to
+	// the contact list for flashing
+	if (!(dbei.flags & DBEF_READ)) {
+		AddUnreadContact(hContact);
+
+		wchar_t toolTip[256];
+		mir_snwprintf(toolTip, TranslateT("Message from %s"), Clist_GetContactDisplayName(hContact));
+
+		CLISTEVENT cle = {};
+		cle.hContact = hContact;
+		cle.hDbEvent = hDbEvent;
+		cle.flags = CLEF_UNICODE;
+		cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
+		cle.pszService = MS_MSG_READMESSAGE;
+		cle.szTooltip.w = toolTip;
+		g_clistApi.pfnAddEvent(&cle);
+	}
+	return 0;
+}
+
+CMimAPI M;
diff --git a/plugins/TabSRMM/src/mim.h b/plugins/TabSRMM/src/mim.h
index e3081a18d5..b9d094868a 100644
--- a/plugins/TabSRMM/src/mim.h
+++ b/plugins/TabSRMM/src/mim.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/modplus.cpp b/plugins/TabSRMM/src/modplus.cpp
index 7fbcb934dc..a8daa352ef 100644
--- a/plugins/TabSRMM/src/modplus.cpp
+++ b/plugins/TabSRMM/src/modplus.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdialog.cpp b/plugins/TabSRMM/src/msgdialog.cpp
index dbf57a6c4e..9c8aa11318 100644
--- a/plugins/TabSRMM/src/msgdialog.cpp
+++ b/plugins/TabSRMM/src/msgdialog.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdlgother.cpp b/plugins/TabSRMM/src/msgdlgother.cpp
index edaa0bacbc..db968ba08e 100644
--- a/plugins/TabSRMM/src/msgdlgother.cpp
+++ b/plugins/TabSRMM/src/msgdlgother.cpp
@@ -1,2858 +1,2858 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/IM project,
-// all portions of this codebase are copyrighted to the people
-// listed in contributors.txt.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// you should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// Helper functions for the message dialog.
-
-#include "stdafx.h"
-
-UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM, LPARAM lParam);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// show the balloon tooltip control.
-
-void CMsgDialog::ActivateTooltip(int iCtrlId, const wchar_t *pwszMessage)
-{
-	if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd)
-		m_pPanel.showTip(iCtrlId, pwszMessage);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::AddLog()
-{
-	if (g_plugin.bUseDividers) {
-		if (g_plugin.bDividersUsePopupConfig) {
-			if (!MessageWindowOpened(0, this))
-				DM_AddDivider();
-		}
-		else {
-			if (!IsActive())
-				DM_AddDivider();
-			else if (m_pContainer->m_hwndActive != m_hwnd)
-				DM_AddDivider();
-		}
-	}
-
-	CSrmmBaseDialog::AddLog();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::AdjustBottomAvatarDisplay()
-{
-	GetAvatarVisibility();
-
-	bool bInfoPanel = m_pPanel.isActive();
-	HBITMAP hbm = (bInfoPanel && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
-	if (hbm) {
-		if (m_dynaSplitter == 0 || m_iSplitterY == 0)
-			LoadSplitter();
-		m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
-		DM_RecalcPictureSize();
-		Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
-		InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
-	}
-	else {
-		Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
-		m_pic.cy = m_pic.cx = DPISCALEY_S(60);
-		InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// calculates avatar layouting, based on splitter position to find the optimal size
-// for the avatar w/o disturbing the toolbar too much.
-
-void CMsgDialog::CalcDynamicAvatarSize(BITMAP *bminfo)
-{
-	if (m_bWasBackgroundCreate || m_pContainer->cfg.flags.m_bDeferredConfigure || m_pContainer->cfg.flags.m_bCreateMinimized || IsIconic(m_pContainer->m_hwnd))
-		return;  // at this stage, the layout is not yet ready...
-
-	RECT rc;
-	GetClientRect(m_hwnd, &rc);
-
-	BOOL bBottomToolBar = m_pContainer->cfg.flags.m_bBottomToolbar;
-	BOOL bToolBar = m_pContainer->cfg.flags.m_bHideToolbar ? 0 : 1;
-	int  iSplitOffset = m_bIsAutosizingInput ? 1 : 0;
-
-	double picAspect = (bminfo->bmWidth == 0 || bminfo->bmHeight == 0) ? 1.0 : (double)(bminfo->bmWidth / (double)bminfo->bmHeight);
-	double picProjectedWidth = (double)((m_dynaSplitter - ((bBottomToolBar && bToolBar) ? DPISCALEX_S(24) : 0) + ((m_bShowUIElements) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect;
-
-	if ((rc.right - (int)picProjectedWidth) > (m_iButtonBarReallyNeeds) && !PluginConfig.m_bAlwaysFullToolbarWidth && bToolBar)
-		m_iRealAvatarHeight = m_dynaSplitter + 3 + (m_bShowUIElements ? DPISCALEY_S(28) : DPISCALEY_S(2));
-	else
-		m_iRealAvatarHeight = m_dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset);
-
-	m_iRealAvatarHeight -= ((bBottomToolBar && bToolBar) ? DPISCALEY_S(22) : 0);
-
-	if (PluginConfig.m_LimitStaticAvatarHeight > 0)
-		m_iRealAvatarHeight = min(m_iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight);
-
-	if (M.GetByte(m_hContact, "dontscaleavatars", M.GetByte("dontscaleavatars", 0)))
-		m_iRealAvatarHeight = min(bminfo->bmHeight, m_iRealAvatarHeight);
-
-	double aspect = (bminfo->bmHeight != 0) ? (double)m_iRealAvatarHeight / (double)bminfo->bmHeight : 1.0;
-	double newWidth = (double)bminfo->bmWidth * aspect;
-	if (newWidth > (double)(rc.right) * 0.8)
-		newWidth = (double)(rc.right) * 0.8;
-	m_pic.cy = m_iRealAvatarHeight + 2;
-	m_pic.cx = (int)newWidth + 2;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::CloseTab()
-{
-	int iTabs = TabCtrl_GetItemCount(m_hwndParent);
-	if (iTabs == 1) {
-		SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 1);
-		return;
-	}
-
-	m_pContainer->m_iChilds--;
-	int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd);
-
-	// after closing a tab, we need to activate the tab to the left side of
-	// the previously open tab.
-	// normally, this tab has the same index after the deletion of the formerly active tab
-	// unless, of course, we closed the last (rightmost) tab.
-	if (!m_pContainer->m_bDontSmartClose && iTabs > 1) {
-		if (i == iTabs - 1)
-			i--;
-		else
-			i++;
-		TabCtrl_SetCurSel(m_hwndParent, i);
-
-		m_pContainer->m_hwndActive = GetTabWindow(m_hwndParent, i);
-
-		RECT rc;
-		m_pContainer->QueryClientArea(rc);
-		SetWindowPos(m_pContainer->m_hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW);
-		ShowWindow(m_pContainer->m_hwndActive, SW_SHOW);
-		SetForegroundWindow(m_pContainer->m_hwndActive);
-		SetFocus(m_pContainer->m_hwndActive);
-	}
-
-	SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
-	DestroyWindow(m_hwnd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// calculate the minimum required client height for the given message
-// window layout
-//
-// the container will use this in its WM_GETMINMAXINFO handler to set
-// minimum tracking height.
-
-void CMsgDialog::DetermineMinHeight()
-{
-	RECT rc;
-	LONG height = (m_pPanel.isActive() ? m_pPanel.getHeight() + 2 : 0);
-	if (!m_pContainer->cfg.flags.m_bHideToolbar)
-		height += DPISCALEY_S(24); // toolbar
-	GetClientRect(m_message.GetHwnd(), &rc);
-	height += rc.bottom; // input area
-	height += 40; // min space for log area and some padding
-
-	m_pContainer->m_uChildMinHeight = height;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting
-// tags and return plain text
-
-static wchar_t tszRtfBreaks[] = L" \\\n\r";
-
-static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst)
-{
-	const wchar_t *pszText = Text;
-	int iIndex = 1;
-
-	static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];";
-	wchar_t szRed[10], szGreen[10], szBlue[10];
-
-	const wchar_t *p1 = wcsstr(pszText, L"\\colortbl");
-	if (!p1)
-		return;
-
-	const wchar_t *pEnd = wcschr(p1, '}');
-
-	const wchar_t *p2 = wcsstr(p1, L"\\red");
-
-	for (int i = 0; i < iCount; i++)
-		pDst[i] = -1;
-
-	while (p2 && p2 < pEnd) {
-		if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) {
-			for (int i = 0; i < iCount; i++) {
-				if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue)))
-					pDst[i] = iIndex;
-			}
-		}
-		iIndex++;
-		p1 = p2;
-		p1++;
-
-		p2 = wcsstr(p1, L"\\red");
-	}
-}
-
-static int RtfColorToIndex(int iNumColors, int *pIndex, int iCol)
-{
-	for (int i = 0; i < iNumColors; i++)
-		if (pIndex[i] == iCol)
-			return i;
-
-	return -1;
-}
-
-BOOL CMsgDialog::DoRtfToTags(CMStringW &pszText) const
-{
-	if (pszText.IsEmpty())
-		return FALSE;
-
-	// used to filter out attributes which are already set for the default message input area font
-	auto &lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
-
-	// create an index of colors in the module and map them to
-	// corresponding colors in the RTF color table
-	int iNumColors = Utils::rtf_clrs.getCount();
-	int *pIndex = (int *)_alloca(iNumColors * sizeof(int));
-	COLORREF *pColors = (COLORREF *)_alloca(iNumColors * sizeof(COLORREF));
-	for (int i = 0; i < iNumColors; i++)
-		pColors[i] = Utils::rtf_clrs[i].clr;
-	CreateColorMap(pszText, iNumColors, pColors, pIndex);
-
-	// scan the file for rtf commands and remove or parse them
-	int idx = pszText.Find(L"\\pard");
-	if (idx == -1) {
-		if ((idx = pszText.Find(L"\\ltrpar")) == -1)
-			return FALSE;
-		idx += 7;
-	}
-	else idx += 5;
-
-	MODULEINFO *mi = (isChat()) ? m_si->pMI : nullptr;
-
-	bool bInsideColor = false, bInsideUl = false;
-	CMStringW res;
-
-	// iterate through all characters, if rtf control character found then take action
-	for (const wchar_t *p = pszText.GetString() + idx; *p;) {
-		switch (*p) {
-		case '\\':
-			if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters
-				res.AppendChar(p[1]);
-				p += 2; break;
-			}
-			if (p[1] == '~') { // non-breaking space
-				res.AppendChar(0xA0);
-				p += 2; break;
-			}
-
-			if (!wcsncmp(p, L"\\cf", 3)) { // foreground color
-				int iCol = _wtoi(p + 3);
-				int iInd = RtfColorToIndex(iNumColors, pIndex, iCol);
-
-				if (iCol > 0) {
-					if (isChat()) {
-						if (mi && mi->bColor) {
-							if (iInd >= 0) {
-								if (!(res.IsEmpty() && m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA] == pColors[iInd]))
-									res.AppendFormat(L"%%c%u", iInd);
-							}
-							else if (!res.IsEmpty())
-								res.Append(L"%%C");
-						}
-					}
-					else res.AppendFormat((iInd >= 0) ? (bInsideColor ? L"[/color][color=%s]" : L"[color=%s]") : (bInsideColor ? L"[/color]" : L""), Utils::rtf_clrs[iInd].szName);
-				}
-
-				bInsideColor = iInd >= 0;
-			}
-			else if (!wcsncmp(p, L"\\highlight", 10)) { // background color
-				if (isChat()) {
-					if (mi && mi->bBkgColor) {
-						int iInd = RtfColorToIndex(iNumColors, pIndex, _wtoi(p + 10));
-						if (iInd >= 0) {
-							// if the entry field is empty & the color passed is the back color, skip it
-							if (!(res.IsEmpty() && m_pContainer->m_theme.inputbg == pColors[iInd]))
-								res.AppendFormat(L"%%f%u", iInd);
-						}
-						else if (!res.IsEmpty())
-							res.AppendFormat(L"%%F");
-					}
-				}
-			}
-			else if (!wcsncmp(p, L"\\line", 5)) { // soft line break;
-				res.AppendChar('\n');
-			}
-			else if (!wcsncmp(p, L"\\endash", 7)) {
-				res.AppendChar(0x2013);
-			}
-			else if (!wcsncmp(p, L"\\emdash", 7)) {
-				res.AppendChar(0x2014);
-			}
-			else if (!wcsncmp(p, L"\\bullet", 7)) {
-				res.AppendChar(0x2022);
-			}
-			else if (!wcsncmp(p, L"\\ldblquote", 10)) {
-				res.AppendChar(0x201C);
-			}
-			else if (!wcsncmp(p, L"\\rdblquote", 10)) {
-				res.AppendChar(0x201D);
-			}
-			else if (!wcsncmp(p, L"\\lquote", 7)) {
-				res.AppendChar(0x2018);
-			}
-			else if (!wcsncmp(p, L"\\rquote", 7)) {
-				res.AppendChar(0x2019);
-			}
-			else if (!wcsncmp(p, L"\\b", 2)) { //bold
-				if (isChat()) {
-					if (mi && mi->bBold)
-						res.Append((p[2] != '0') ? L"%b" : L"%B");
-				}
-				else {
-					if (!(lf.lfWeight == FW_BOLD)) // only allow bold if the font itself isn't a bold one, otherwise just strip it..
-						if (m_SendFormat)
-							res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
-				}
-			}
-			else if (!wcsncmp(p, L"\\i", 2)) { // italics
-				if (isChat()) {
-					if (mi && mi->bItalics)
-						res.Append((p[2] != '0') ? L"%i" : L"%I");
-				}
-				else {
-					if (!lf.lfItalic && m_SendFormat)
-						res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
-				}
-			}
-			else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
-				if (!lf.lfStrikeOut && m_SendFormat)
-					res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
-			}
-			else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
-				if (isChat()) {
-					if (mi && mi->bUnderline)
-						res.Append((p[3] != '0') ? L"%u" : L"%U");
-				}
-				else {
-					if (!lf.lfUnderline && m_SendFormat) {
-						if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) {
-							res.Append(L"[u]");
-							bInsideUl = true;
-						}
-						else if (!wcsncmp(p + 3, L"none", 4)) {
-							if (bInsideUl)
-								res.Append(L"[/u]");
-							bInsideUl = false;
-						}
-					}
-				}
-			}
-			else if (!wcsncmp(p, L"\\tab", 4)) { // tab
-				res.AppendChar('\t');
-			}
-			else if (p[1] == '\'') { // special character
-				if (p[2] != ' ' && p[2] != '\\') {
-					wchar_t tmp[10];
-
-					if (p[3] != ' ' && p[3] != '\\') {
-						wcsncpy(tmp, p + 2, 3);
-						tmp[3] = 0;
-					}
-					else {
-						wcsncpy(tmp, p + 2, 2);
-						tmp[2] = 0;
-					}
-
-					// convert string containing char in hex format to int.
-					wchar_t *stoppedHere;
-					res.AppendChar(wcstol(tmp, &stoppedHere, 16));
-				}
-			}
-
-			p++; // skip initial slash
-			p += wcscspn(p, tszRtfBreaks);
-			if (*p == ' ')
-				p++;
-			break;
-
-		case '{': // other RTF control characters
-		case '}':
-			p++;
-			break;
-
-		case '%': // double % for stupid chat engine
-			if (isChat())
-				res.Append(L"%%");
-			else
-				res.AppendChar(*p);
-			p++;
-			break;
-
-		default: // other text that should not be touched
-			res.AppendChar(*p++);
-			break;
-		}
-	}
-
-	if (bInsideColor && !isChat())
-		res.Append(L"[/color]");
-	if (bInsideUl)
-		res.Append(L"[/u]");
-
-	pszText = res;
-	return TRUE;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::EnableSendButton(bool bMode) const
-{
-	SendDlgItemMessage(m_hwnd, IDOK, BUTTONSETASNORMAL, bMode, 0);
-	SendDlgItemMessage(m_hwnd, IDC_PIC, BUTTONSETASNORMAL, m_bEditNotesActive ? TRUE : (!bMode && m_iOpenJobs == 0) ? TRUE : FALSE, 0);
-
-	HWND hwndOK = GetDlgItem(GetParent(GetParent(m_hwnd)), IDOK);
-	if (IsWindow(hwndOK))
-		SendMessage(hwndOK, BUTTONSETASNORMAL, bMode, 0);
-}
-
-void CMsgDialog::EnableSending(bool bMode) const
-{
-	m_message.SendMsg(EM_SETREADONLY, !bMode, 0);
-	Utils::enableDlgControl(m_hwnd, IDC_CLIST, bMode);
-	EnableSendButton(bMode);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::FindFirstEvent()
-{
-	int historyMode = g_plugin.getByte(m_hContact, SRMSGSET_LOADHISTORY, -1);
-	if (historyMode == -1)
-		historyMode = (int)g_plugin.getByte(SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY);
-
-	m_hDbEventFirst = db_event_firstUnread(m_hContact);
-
-	if (m_bActualHistory)
-		historyMode = LOADHISTORY_COUNT;
-
-	DBEVENTINFO dbei = {};
-	DB::ECPTR pCursor(DB::EventsRev(m_hContact, m_hDbEventFirst));
-
-	switch (historyMode) {
-	case LOADHISTORY_COUNT:
-		int i;
-
-		// ability to load only current session's history
-		if (m_bActualHistory)
-			i = m_cache->getSessionMsgCount();
-		else
-			i = g_plugin.getWord(SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT);
-
-		for (; i > 0; i--) {
-			MEVENT hPrevEvent = pCursor.FetchNext();
-			if (hPrevEvent == 0)
-				break;
-
-			dbei.cbBlob = 0;
-			m_hDbEventFirst = hPrevEvent;
-			db_event_get(m_hDbEventFirst, &dbei);
-			if (!DbEventIsShown(&dbei))
-				i++;
-		}
-		break;
-
-	case LOADHISTORY_TIME:
-		if (m_hDbEventFirst == 0)
-			dbei.timestamp = time(0);
-		else
-			db_event_get(m_hDbEventFirst, &dbei);
-
-		uint32_t firstTime = dbei.timestamp - 60 * g_plugin.getWord(SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME);
-
-		while (MEVENT hPrevEvent = pCursor.FetchNext()) {
-			dbei.cbBlob = 0;
-			db_event_get(hPrevEvent, &dbei);
-			if (dbei.timestamp < firstTime)
-				break;
-			m_hDbEventFirst = hPrevEvent;
-		}
-		break;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// returns != 0 when one of the installed keyboard layouts belongs to an rtl language
-// used to find out whether we need to configure the message input box for bidirectional mode
-
-int CMsgDialog::FindRTLLocale()
-{
-	int result = 0;
-
-	if (m_iHaveRTLLang == 0) {
-		HKL layouts[20];
-		memset(layouts, 0, sizeof(layouts));
-		GetKeyboardLayoutList(20, layouts);
-		for (int i = 0; i < 20 && layouts[i]; i++) {
-			uint16_t wCtype2[5];
-			LCID lcid = MAKELCID(LOWORD(layouts[i]), 0);
-			GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2);
-			if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT)
-				result = 1;
-		}
-		m_iHaveRTLLang = (result ? 1 : -1);
-	}
-	else result = m_iHaveRTLLang == 1 ? 1 : 0;
-
-	return result;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::FlashOnClist(MEVENT hEvent, DBEVENTINFO *dbei)
-{
-	m_dwTickLastEvent = GetTickCount();
-
-	if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) {
-		m_dwUnread++;
-		AddUnreadContact(m_hContact);
-	}
-
-	if (hEvent == 0)
-		return;
-
-	if (!g_plugin.bFlashOnClist)
-		return;
-
-	if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !m_bFlashClist) {
-		CLISTEVENT cle = {};
-		cle.hContact = m_hContact;
-		cle.hDbEvent = hEvent;
-		cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
-		cle.pszService = MS_MSG_READMESSAGE;
-		g_clistApi.pfnAddEvent(&cle);
-
-		m_bFlashClist = true;
-		m_hFlashingEvent = hEvent;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// flash a tab icon if mode = true, otherwise restore default icon
-// store flashing state into bState
-
-void CMsgDialog::FlashTab(bool bInvertMode)
-{
-	if (bInvertMode)
-		m_bTabFlash = !m_bTabFlash;
-
-	TCITEM item = {};
-	item.mask = TCIF_IMAGE;
-	TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
-	if (m_pContainer->cfg.flags.m_bSideBar)
-		m_pContainer->m_pSideBar->updateSession(this);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// this translates formatting tags into rtf sequences...
-// flags: loword = words only for simple  * /_ formatting
-//        hiword = bbcode support (strip bbcodes if 0)
-
-static wchar_t *w_bbcodes_begin[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[color=" };
-static wchar_t *w_bbcodes_end[] = { L"[/b]", L"[/i]", L"[/u]", L"[/s]", L"[/color]" };
-
-static wchar_t *formatting_strings_begin[] = { L"b1 ", L"i1 ", L"u1 ", L"s1 ", L"c1 " };
-static wchar_t *formatting_strings_end[] = { L"b0 ", L"i0 ", L"u0 ", L"s0 ", L"c0 " };
-
-void CMsgDialog::FormatRaw(CMStringW &msg, int flags, bool isSent)
-{
-	bool clr_was_added = false;
-	int beginmark = 0, endmark = 0, tempmark = 0;
-	int i, endindex;
-
-	if (m_dwFlags & MWF_LOG_BBCODE) {
-		beginmark = 0;
-		while (true) {
-			for (i = 0; i < _countof(w_bbcodes_begin); i++)
-				if ((tempmark = msg.Find(w_bbcodes_begin[i], 0)) != -1)
-					break;
-
-			if (i >= _countof(w_bbcodes_begin))
-				break;
-
-			beginmark = tempmark;
-			endindex = i;
-			endmark = msg.Find(w_bbcodes_end[i], beginmark);
-			if (endindex == 4) { // color
-				int closing = msg.Find(L"]", beginmark);
-				bool was_added = false;
-
-				if (closing == -1) {                      // must be an invalid [color=] tag w/o closing bracket
-					msg.SetAt(beginmark, ' ');
-					continue;
-				}
-
-				CMStringW colorname = msg.Mid(beginmark + 7, closing - beginmark - 7);
-search_again:
-				bool clr_found = false;
-				for (int ii = 0; ii < Utils::rtf_clrs.getCount(); ii++) {
-					auto &rtfc = Utils::rtf_clrs[ii];
-					if (!wcsicmp(colorname, rtfc.szName)) {
-						closing = beginmark + 7 + (int)mir_wstrlen(rtfc.szName);
-						if (endmark != -1) {
-							msg.Delete(endmark, 8);
-							msg.Insert(endmark, L"c0 ");
-						}
-						msg.Delete(beginmark, closing - beginmark + 1);
-
-						wchar_t szTemp[5];
-						msg.Insert(beginmark, L"cxxx ");
-						mir_snwprintf(szTemp, L"%02d", MSGDLGFONTCOUNT + 13 + ii);
-						msg.SetAt(beginmark + 3, szTemp[0]);
-						msg.SetAt(beginmark + 4, szTemp[1]);
-						clr_found = true;
-						if (was_added) {
-							wchar_t wszTemp[100];
-							mir_snwprintf(wszTemp, L"##col##%06u:%04u", endmark - closing, ii);
-							wszTemp[99] = 0;
-							msg.Insert(beginmark, wszTemp);
-						}
-						break;
-					}
-				}
-				if (!clr_found) {
-					int c_closing = colorname.Find(L"]");
-					if (c_closing == -1)
-						c_closing = colorname.GetLength();
-					const wchar_t *wszColname = colorname.c_str();
-					if (endmark != -1 && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing - 1])) {
-						Utils::RTF_ColorAdd(wszColname);
-						if (!was_added) {
-							clr_was_added = was_added = true;
-							goto search_again;
-						}
-						else goto invalid_code;
-					}
-					else {
-invalid_code:
-						if (endmark != -1)
-							msg.Delete(endmark, 8);
-						if (closing != -1 && closing < endmark)
-							msg.Delete(beginmark, (closing - beginmark) + 1);
-						else
-							msg.SetAt(beginmark, ' ');
-					}
-				}
-				continue;
-			}
-
-			if (endmark != -1) {
-				msg.Delete(endmark, 4);
-				msg.Insert(endmark, formatting_strings_end[i]);
-			}
-			msg.Delete(beginmark, 3);
-			msg.Insert(beginmark, L" ");
-			msg.Insert(beginmark, formatting_strings_begin[i]);
-		}
-	}
-
-	if ((m_dwFlags & MWF_LOG_TEXTFORMAT) && msg.Find(L"://") == -1) {
-		while ((beginmark = msg.Find(L"*/_", beginmark)) != -1) {
-			wchar_t endmarker = msg[beginmark];
-			if (LOWORD(flags)) {
-				if (beginmark > 0 && !iswspace(msg[beginmark - 1]) && !iswpunct(msg[beginmark - 1])) {
-					beginmark++;
-					continue;
-				}
-
-				// search a corresponding endmarker which fulfills the criteria
-				INT_PTR mark = beginmark + 1;
-				while ((endmark = msg.Find(endmarker, mark)) != -1) {
-					if (iswpunct(msg[endmark + 1]) || iswspace(msg[endmark + 1]) || msg[endmark + 1] == 0 || wcschr(L"*/_", msg[endmark + 1]) != nullptr)
-						goto ok;
-					mark = endmark + 1;
-				}
-				break;
-			}
-			else {
-				if ((endmark = msg.Find(endmarker, beginmark + 1)) == -1)
-					break;
-			}
-ok:
-			if ((endmark - beginmark) < 2) {
-				beginmark++;
-				continue;
-			}
-			
-			int index = 0;
-			switch (endmarker) {
-			case '*':
-				index = 0;
-				break;
-			case '/':
-				index = 1;
-				break;
-			case '_':
-				index = 2;
-				break;
-			}
-
-			// check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if
-			// it really is one.
-			if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) {
-				CMStringW smcode = msg.Mid(beginmark, (endmark - beginmark) + 1);
-
-				SMADD_BATCHPARSE2 smbp = {};
-				smbp.cbSize = sizeof(smbp);
-				smbp.Protocolname = m_cache->getActiveProto();
-				smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0);
-				smbp.str = (wchar_t*)smcode.c_str();
-				smbp.hContact = m_hContact;
-
-				SMADD_BATCHPARSERES *smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&smbp);
-				if (smbpr) {
-					CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr);
-					beginmark = endmark + 1;
-					continue;
-				}
-			}
-			msg.Delete(endmark, 1);
-			msg.Insert(endmark, formatting_strings_end[index]);
-			msg.Delete(beginmark, 1);
-			msg.Insert(beginmark, formatting_strings_begin[index]);
-		}
-	}
-
-	m_bClrAdded = clr_was_added;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// format the title bar string for IM chat sessions using placeholders.
-// the caller must mir_free() the returned string
-
-static wchar_t* Trunc500(wchar_t *str)
-{
-	if (mir_wstrlen(str) > 500)
-		str[500] = 0;
-	return str;
-}
-
-bool CMsgDialog::FormatTitleBar(const wchar_t *szFormat, CMStringW &dest)
-{
-	for (const wchar_t *src = szFormat; *src; src++) {
-		if (*src != '%') {
-			dest.AppendChar(*src);
-			continue;
-		}
-
-		switch (*++src) {
-		case 'n':
-			dest.Append(m_cache->getNick());
-			break;
-
-		case 'p':
-		case 'a':
-			dest.Append(m_cache->getRealAccount());
-			break;
-
-		case 's':
-			dest.Append(m_wszStatus);
-			break;
-
-		case 'u':
-			dest.Append(m_cache->getUIN());
-			break;
-
-		case 'c':
-			dest.Append(!mir_wstrcmp(m_pContainer->m_wszName, L"default") ? TranslateT("Default container") : m_pContainer->m_wszName);
-			break;
-
-		case 'o':
-			{
-				const char *szProto = m_cache->getActiveProto();
-				if (szProto)
-					dest.Append(_A2T(szProto));
-			}
-			break;
-
-		case 'x':
-			{
-				uint8_t xStatus = m_cache->getXStatusId();
-				if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
-					ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
-					dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
-				}
-			}
-			break;
-
-		case 'm':
-			{
-				uint8_t xStatus = m_cache->getXStatusId();
-				if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
-					ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
-					dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
-				}
-				else dest.Append(m_wszStatus[0] ? m_wszStatus : L"(undef)");
-			}
-			break;
-
-			// status message (%T will skip the "No status message" for empty messages)
-		case 't':
-		case 'T':
-			{
-				ptrW tszStatus(m_cache->getNormalizedStatusMsg(m_cache->getStatusMsg(), true));
-				if (tszStatus)
-					dest.Append(tszStatus);
-				else if (*src == 't')
-					dest.Append(TranslateT("No status message"));
-			}
-			break;
-
-		case 'g':
-			{
-				ptrW tszGroup(Clist_GetGroup(m_hContact));
-				if (tszGroup != nullptr)
-					dest.Append(tszGroup);
-			}
-			break;
-
-		case 0: // wrongly formed format string
-			return true;
-		}
-	}
-
-	return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve the visiblity of the avatar window, depending on the global setting
-// and local mode
-
-bool CMsgDialog::GetAvatarVisibility()
-{
-	uint8_t bAvatarMode = m_pContainer->cfg.avatarMode;
-	uint8_t bOwnAvatarMode = m_pContainer->cfg.ownAvatarMode;
-	char hideOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
-
-	// infopanel visible, consider own avatar display
-	m_bShowAvatar = false;
-	if (m_si)
-		bAvatarMode = 1;
-
-	if (m_pPanel.isActive() && bAvatarMode != 3) {
-		if (!bOwnAvatarMode) {
-			m_bShowAvatar = (m_hOwnPic && m_hOwnPic != PluginConfig.g_hbmUnknown);
-			if (!m_hwndContactPic)
-				m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
-		}
-
-		switch (bAvatarMode) {
-		case 2:
-			m_bShowInfoAvatar = false;
-			break;
-		case 0:
-			m_bShowInfoAvatar = true;
-		case 1:
-			HBITMAP hbm = ((m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr);
-			if (hbm == nullptr && !bAvatarMode) {
-				m_bShowInfoAvatar = false;
-				break;
-			}
-
-			if (!m_hwndPanelPic) {
-				m_hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, m_hwndPanelPicParent, (HMENU)7000, nullptr, nullptr);
-				if (m_hwndPanelPic)
-					SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE);
-			}
-
-			if (bAvatarMode != 0)
-				m_bShowInfoAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
-			break;
-		}
-
-		if (m_bShowInfoAvatar)
-			m_bShowInfoAvatar = hideOverride == 0 ? false : m_bShowInfoAvatar;
-		else
-			m_bShowInfoAvatar = hideOverride == 1 ? true : m_bShowInfoAvatar;
-
-		Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
-		SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
-	}
-	else {
-		m_bShowInfoAvatar = false;
-
-		switch (bAvatarMode) {
-		case 0: // globally on
-			m_bShowAvatar = true;
-		LBL_Check:
-			if (!m_hwndContactPic)
-				m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
-			break;
-		case 2: // globally OFF
-			m_bShowAvatar = false;
-			break;
-		case 3: // on, if present
-		case 1:
-			HBITMAP hbm = (m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr;
-			m_bShowAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
-			goto LBL_Check;
-		}
-
-		if (m_bShowAvatar)
-			m_bShowAvatar = hideOverride == 0 ? 0 : m_bShowAvatar;
-		else
-			m_bShowAvatar = hideOverride == 1 ? 1 : m_bShowAvatar;
-
-		// reloads avatars
-		if (m_hwndPanelPic) { // shows contact or user picture, depending on panel visibility
-			SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
-			Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
-		}
-		else Utils::setAvatarContact(m_hwndContactPic, m_hContact);
-	}
-	return m_bShowAvatar;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::GetClientIcon()
-{
-	if (m_hClientIcon)
-		DestroyIcon(m_hClientIcon);
-
-	m_hClientIcon = nullptr;
-	if (ServiceExists(MS_FP_GETCLIENTICONT)) {
-		ptrW tszMirver(db_get_wsa(m_cache->getActiveContact(), m_cache->getActiveProto(), "MirVer"));
-		if (tszMirver)
-			m_hClientIcon = Finger_GetClientIcon(tszMirver, 1);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-HICON CMsgDialog::GetMyContactIcon(const CMOption<bool> *opt)
-{
-	int bUseMeta = (opt == nullptr) ? false : M.GetByte(opt->GetDBSettingName(), mir_strcmp(opt->GetDBSettingName(), "MetaiconTab") == 0);
-	if (bUseMeta)
-		return Skin_LoadProtoIcon(m_cache->getProto(), m_cache->getStatus());
-	return Skin_LoadProtoIcon(m_cache->getActiveProto(), m_cache->getActiveStatus());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::GetMyNick()
-{
-	ptrW tszNick(Contact::GetInfo(CNF_CUSTOMNICK, 0, m_cache->getActiveProto()));
-	if (tszNick == nullptr)
-		tszNick = Contact::GetInfo(CNF_NICK, 0, m_cache->getActiveProto());
-	if (tszNick != nullptr) {
-		if (mir_wstrlen(tszNick) == 0 || !mir_wstrcmp(tszNick, TranslateT("'(Unknown contact)'")))
-			wcsncpy_s(m_wszMyNickname, (m_myUin[0] ? m_myUin : TranslateT("'(Unknown contact)'")), _TRUNCATE);
-		else
-			wcsncpy_s(m_wszMyNickname, tszNick, _TRUNCATE);
-	}
-	else wcsncpy_s(m_wszMyNickname, L"<undef>", _TRUNCATE); // same here
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// retrieve both buddys and my own UIN for a message session and store them in the message 
-// window *dat respects metacontacts and uses the current protocol if the contact is a MC
-
-void CMsgDialog::GetMYUIN()
-{
-	ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, 0, m_cache->getActiveProto()));
-	if (uid != nullptr)
-		wcsncpy_s(m_myUin, uid, _TRUNCATE);
-	else
-		m_myUin[0] = 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// returns the status of Send button
-
-LRESULT CMsgDialog::GetSendButtonState()
-{
-	return m_btnOk.SendMsg(BUTTONGETSTATEID, TRUE, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// reads send format and configures the toolbar buttons
-// if mode == 0, int only configures the buttons and does not change send format
-
-void CMsgDialog::GetSendFormat()
-{
-	m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
-	if (m_SendFormat == -1)          // per contact override to disable it..
-		m_SendFormat = 0;
-	else if (m_SendFormat == 0)
-		m_SendFormat = g_plugin.bSendFormat;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-HICON CMsgDialog::GetXStatusIcon() const
-{
-	uint8_t xStatus = m_cache->getXStatusId();
-	if (xStatus == 0)
-		return nullptr;
-
-	if (!ProtoServiceExists(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON))
-		return nullptr;
-
-	return (HICON)(CallProtoService(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON, xStatus, 0));
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// paste contents of the clipboard into the message input area and send it immediately
-
-void CMsgDialog::HandlePasteAndSend()
-{
-	// is feature disabled?
-	if (!g_plugin.bPasteAndSend) {
-		ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending messages' section"));
-		return;
-	}
-
-	m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0);
-	if (GetWindowTextLength(m_message.GetHwnd()) > 0)
-		SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// convert the avatar bitmap to icon format so that it can be used on the task bar
-// tries to keep correct aspect ratio of the avatar image
-//
-// @param dat: _MessageWindowData* pointer to the window data
-// @return HICON: the icon handle
-
-HICON CMsgDialog::IconFromAvatar() const
-{
-	if (!ServiceExists(MS_AV_GETAVATARBITMAP))
-		return nullptr;
-
-	AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, m_hContact, 0);
-	if (ace == nullptr || ace->hbmPic == nullptr)
-		return nullptr;
-
-	LONG lIconSize = Win7Taskbar->getIconSize();
-	double dNewWidth, dNewHeight;
-	Utils::scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize);
-
-	// resize picture to fit it on the task bar, use an image list for converting it to
-	// 32bpp icon format. hTaskbarIcon will cache it until avatar is changed
-	HBITMAP hbmResized = ::Image_Resize(ace->hbmPic, RESIZEBITMAP_STRETCH, dNewWidth, dNewHeight);
-	HIMAGELIST hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0);
-
-	RECT rc = { 0, 0, lIconSize, lIconSize };
-
-	HDC hdc = ::GetDC(m_pContainer->m_hwnd);
-	HDC dc = ::CreateCompatibleDC(hdc);
-	HDC dcResized = ::CreateCompatibleDC(hdc);
-
-	ReleaseDC(m_pContainer->m_hwnd, hdc);
-
-	HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc);
-	HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew));
-	HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized));
-
-	LONG ix = (lIconSize - (LONG)dNewWidth) / 2;
-	LONG iy = (lIconSize - (LONG)dNewHeight) / 2;
-	CSkin::m_default_bf.SourceConstantAlpha = M.GetByte("taskBarIconAlpha", 255);
-	GdiAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
-
-	CSkin::m_default_bf.SourceConstantAlpha = 255;
-	::SelectObject(dc, hbmOld);
-	::ImageList_Add(hIml_c, hbmNew, nullptr);
-	::DeleteObject(hbmNew);
-	::DeleteDC(dc);
-
-	::SelectObject(dcResized, hbmOldResized);
-	if (hbmResized != ace->hbmPic)
-		::DeleteObject(hbmResized);
-	::DeleteDC(dcResized);
-	HICON hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL);
-	::ImageList_RemoveAll(hIml_c);
-	::ImageList_Destroy(hIml_c);
-	return hIcon;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// is window active or not?
-
-bool CMsgDialog::IsActive() const
-{
-	return m_pContainer->IsActive() && m_pContainer->m_hwndActive == m_hwnd;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// read keyboard state and return the state of the modifier keys
-
-void CMsgDialog::KbdState(bool &isShift, bool &isControl, bool &isAlt)
-{
-	GetKeyboardState(kstate);
-	isShift = (kstate[VK_SHIFT] & 0x80) != 0;
-	isControl = (kstate[VK_CONTROL] & 0x80) != 0;
-	isAlt = (kstate[VK_MENU] & 0x80) != 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LimitMessageText(int iLen)
-{
-	if (this != nullptr)
-		m_message.SendMsg(EM_EXLIMITTEXT, 0, iLen);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadContactAvatar()
-{
-	m_ace = Utils::loadAvatarFromAVS(m_bIsMeta ? db_mc_getSrmmSub(m_hContact) : m_hContact);
-
-	BITMAP bm;
-	if (m_ace && m_ace->hbmPic)
-		GetObject(m_ace->hbmPic, sizeof(bm), &bm);
-	else if (m_ace == nullptr)
-		GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm);
-	else
-		return;
-
-	AdjustBottomAvatarDisplay();
-	CalcDynamicAvatarSize(&bm);
-
-	if (!m_pPanel.isActive() || m_pContainer->cfg.avatarMode == 3) {
-		m_iRealAvatarHeight = 0;
-		PostMessage(m_hwnd, WM_SIZE, 0, 0);
-	}
-	else if (m_pPanel.isActive())
-		GetAvatarVisibility();
-
-	if (m_pWnd != nullptr)
-		m_pWnd->verifyDwmState();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadOwnAvatar()
-{
-	if (ServiceExists(MS_AV_GETMYAVATAR))
-		m_ownAce = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(m_cache->getActiveProto()));
-	else
-		m_ownAce = nullptr;
-
-	if (m_ownAce)
-		m_hOwnPic = m_ownAce->hbmPic;
-	else
-		m_hOwnPic = PluginConfig.g_hbmUnknown;
-
-	if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
-		BITMAP bm;
-
-		m_iRealAvatarHeight = 0;
-		AdjustBottomAvatarDisplay();
-		GetObject(m_hOwnPic, sizeof(bm), &bm);
-		CalcDynamicAvatarSize(&bm);
-		Resize();
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadSettings()
-{
-	m_clrInputBG = m_pContainer->m_theme.inputbg;
-	LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LoadSplitter()
-{
-	if (m_bIsAutosizingInput) {
-		m_iSplitterY = (m_pContainer->cfg.flags.m_bBottomToolbar) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46);
-
-		if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
-			m_iSplitterY += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2);
-		return;
-	}
-
-	if (!m_bSplitterOverride) {
-		if (!m_pContainer->cfg.fPrivate)
-			m_iSplitterY = (int)M.GetDword("splitsplity", 60);
-		else
-			m_iSplitterY = m_pContainer->cfg.iSplitterY;
-	}
-	else m_iSplitterY = (int)M.GetDword(m_hContact, "splitsplity", M.GetDword("splitsplity", 60));
-
-	if (m_iSplitterY < MINSPLITTERY)
-		m_iSplitterY = 150;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::LogEvent(DBEVENTINFO &dbei)
-{
-	if (m_iLogMode != WANT_BUILTIN_LOG) {
-		dbei.flags |= DBEF_TEMPORARY;
-
-		MEVENT hDbEvent = db_event_add(m_hContact, &dbei);
-		if (hDbEvent) {
-			m_pLog->LogEvents(hDbEvent, 1, true);
-			db_event_delete(hDbEvent);
-		}
-	}
-	else LOG()->LogEvents(0, 1, true, &dbei);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// draw various elements of the message window, like avatar(s), info panel fields
-// and the color formatting menu
-
-int CMsgDialog::MsgWindowDrawHandler(DRAWITEMSTRUCT *dis)
-{
-	if ((dis->hwndItem == GetDlgItem(m_hwnd, IDC_CONTACTPIC) && m_bShowAvatar) || (dis->hwndItem == m_hwnd && m_pPanel.isActive())) {
-		HBITMAP hbmAvatar = m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown;
-		if (hbmAvatar == nullptr)
-			return TRUE;
-
-		int top, cx, cy;
-		RECT rcClient, rcFrame;
-		bool bPanelPic = (dis->hwndItem == m_hwnd);
-		if (bPanelPic && !m_bShowInfoAvatar)
-			return TRUE;
-
-		RECT rc;
-		GetClientRect(m_hwnd, &rc);
-		if (bPanelPic) {
-			rcClient = dis->rcItem;
-			cx = (rcClient.right - rcClient.left);
-			cy = (rcClient.bottom - rcClient.top) + 1;
-		}
-		else {
-			GetClientRect(dis->hwndItem, &rcClient);
-			cx = rcClient.right;
-			cy = rcClient.bottom;
-		}
-
-		if (cx < 5 || cy < 5)
-			return TRUE;
-
-		HDC hdcDraw = CreateCompatibleDC(dis->hDC);
-		HBITMAP hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy);
-		HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw);
-
-		bool bAero = M.isAero();
-
-		HRGN clipRgn = nullptr;
-		HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcDraw, bAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE));
-		rcFrame = rcClient;
-
-		if (!bPanelPic) {
-			top = (cy - m_pic.cy) / 2;
-			RECT rcEdge = { 0, top, m_pic.cx, top + m_pic.cy };
-			if (CSkin::m_skinEnabled)
-				CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, hdcDraw);
-			else if (PluginConfig.m_fillColor) {
-				HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor);
-				FillRect(hdcDraw, &rcFrame, br);
-				DeleteObject(br);
-			}
-			else if (bAero && CSkin::m_pCurrentAeroEffect) {
-				COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh :
-					(CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0);
-
-				HBRUSH br = CreateSolidBrush(clr);
-				FillRect(hdcDraw, &rcFrame, br);
-				DeleteObject(br);
-			}
-			else FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE));
-
-			HPEN hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr);
-			HPEN hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder);
-
-			if (CSkin::m_bAvatarBorderType == 1)
-				Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom);
-			else if (CSkin::m_bAvatarBorderType == 2) {
-				clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, 6, 6);
-				SelectClipRgn(hdcDraw, clipRgn);
-
-				HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
-				FrameRgn(hdcDraw, clipRgn, hbr, 1, 1);
-				DeleteObject(hbr);
-				DeleteObject(clipRgn);
-			}
-
-			SelectObject(hdcDraw, hPenOld);
-			DeleteObject(hPenBorder);
-		}
-
-		if (bPanelPic) {
-			bool bBorder = (CSkin::m_bAvatarBorderType ? true : false);
-
-			int border_off = bBorder ? 1 : 0;
-			int iMaxHeight = m_iPanelAvatarY - (bBorder ? 2 : 0);
-			int iMaxWidth = m_iPanelAvatarX - (bBorder ? 2 : 0);
-
-			rcFrame.left = rcFrame.top = 0;
-			rcFrame.right = (rcClient.right - rcClient.left);
-			rcFrame.bottom = (rcClient.bottom - rcClient.top);
-
-			rcFrame.left = rcFrame.right - (LONG)m_iPanelAvatarX;
-			rcFrame.bottom = (LONG)m_iPanelAvatarY;
-
-			int height_off = (cy - iMaxHeight - (bBorder ? 2 : 0)) / 2;
-			rcFrame.top += height_off;
-			rcFrame.bottom += height_off;
-
-			SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, bAero ? TRUE : FALSE);
-			SetWindowPos(m_hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off,
-				iMaxWidth, iMaxHeight, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING);
-		}
-
-		SelectObject(hdcDraw, hOldBrush);
-		if (!bPanelPic)
-			BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY);
-		SelectObject(hdcDraw, hbmOld);
-		DeleteObject(hbmDraw);
-		DeleteDC(hdcDraw);
-		return TRUE;
-	}
-
-	if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(m_hwnd, IDC_LOGFROZENTEXT)) {
-		wchar_t szWindowText[256];
-		if (CSkin::m_skinEnabled) {
-			SetTextColor(dis->hDC, CSkin::m_DefaultFontColor);
-			CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
-		}
-		else {
-			SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
-			CSkin::FillBack(dis->hDC, &dis->rcItem);
-		}
-		GetWindowText(dis->hwndItem, szWindowText, _countof(szWindowText));
-		szWindowText[255] = 0;
-		SetBkMode(dis->hDC, TRANSPARENT);
-		DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS);
-		return TRUE;
-	}
-
-	if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICERRORICON)) {
-		if (CSkin::m_skinEnabled)
-			CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
-		else
-			CSkin::FillBack(dis->hDC, &dis->rcItem);
-		DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8,
-			PluginConfig.g_iconErr, 16, 16, 0, nullptr, DI_NORMAL);
-		return TRUE;
-	}
-
-	if (dis->CtlType == ODT_MENU && m_pPanel.isHovered()) {
-		DrawMenuItem(dis, (HICON)dis->itemData, 0);
-		return TRUE;
-	}
-
-	return Menu_DrawItem((LPARAM)dis);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMsgDialog::MsgWindowUpdateMenu(HMENU submenu, int menuID)
-{
-	bool bInfoPanel = m_pPanel.isActive();
-
-	if (menuID == MENU_TABCONTEXT) {
-		EnableMenuItem(submenu, ID_TABMENU_LEAVECHATROOM, (isChat() && ProtoServiceExists(m_szProto, PS_LEAVECHAT)) ? MF_ENABLED : MF_GRAYED);
-		EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) ? MF_GRAYED : MF_ENABLED);
-		EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M.GetDword(m_hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED);
-	}
-	else if (menuID == MENU_PICMENU) {
-		wchar_t *szText = nullptr;
-		char  avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
-		HMENU visMenu = GetSubMenu(submenu, 0);
-		BOOL picValid = bInfoPanel ? (m_hOwnPic != nullptr) : (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown);
-
-		MENUITEMINFO mii = { 0 };
-		mii.cbSize = sizeof(mii);
-		mii.fMask = MIIM_STRING;
-
-		EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, picValid ? MF_ENABLED : MF_GRAYED);
-
-		CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
-
-		CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, PluginConfig.m_bAlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED);
-		if (!bInfoPanel) {
-			EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
-			szText = TranslateT("Contact picture settings...");
-			EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED);
-		}
-		else {
-			EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED);
-			EnableMenuItem(submenu, ID_PICMENU_SETTINGS, (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED);
-			szText = TranslateT("Set your avatar...");
-		}
-		mii.dwTypeData = szText;
-		mii.cch = (int)mir_wstrlen(szText) + 1;
-		SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii);
-	}
-	else if (menuID == MENU_PANELPICMENU) {
-		HMENU visMenu = GetSubMenu(submenu, 0);
-		char  avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
-
-		CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
-		CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
-
-		EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
-		EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED);
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update state of the container - this is called whenever a tab becomes active, no matter how and
-// deals with various things like updating the title bar, removing flashing icons, updating the
-// session list, switching the keyboard layout (autolocale active)  and the general container status.
-//
-// it protects itself from being called more than once per session activation and is valid for
-// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c)
-
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMsgDialog::MsgWindowMenuHandler(int selection, int menuId)
-{
-	if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) {
-		switch (selection) {
-		case ID_TABMENU_ATTACHTOCONTAINER:
-			SelectContainer();
-			return 1;
-		case ID_TABMENU_CONTAINEROPTIONS:
-			m_pContainer->OptionsDialog();
-			return 1;
-		case ID_TABMENU_CLOSECONTAINER:
-			SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 0);
-			return 1;
-		case ID_TABMENU_CLOSETAB:
-			PostMessage(m_hwnd, WM_CLOSE, 1, 0);
-			return 1;
-		case ID_TABMENU_SAVETABPOSITION:
-			db_set_dw(m_hContact, SRMSGMOD_T, "tabindex", m_iTabID * 100);
-			break;
-		case ID_TABMENU_CLEARSAVEDTABPOSITION:
-			db_unset(m_hContact, SRMSGMOD_T, "tabindex");
-			break;
-		case ID_TABMENU_LEAVECHATROOM:
-			if (isChat()) {
-				char *szProto = Proto_GetBaseAccountName(m_hContact);
-				if (szProto)
-					CallProtoService(szProto, PS_LEAVECHAT, m_hContact, 0);
-			}
-			return 1;
-
-		case ID_VISIBILITY_DEFAULT:
-		case ID_VISIBILITY_HIDDENFORTHISCONTACT:
-		case ID_VISIBILITY_VISIBLEFORTHISCONTACT:
-			{
-				uint8_t avOverrideMode;
-				if (selection == ID_VISIBILITY_DEFAULT)
-					avOverrideMode = -1;
-				else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT)
-					avOverrideMode = 1;
-				else
-					avOverrideMode = 0;
-				db_set_b(m_hContact, SRMSGMOD_T, "hideavatar", avOverrideMode);
-			}
-
-			ShowPicture(false);
-			Resize();
-			DM_ScrollToBottom(0, 1);
-			return 1;
-
-		case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH:
-			PluginConfig.m_bAlwaysFullToolbarWidth = !PluginConfig.m_bAlwaysFullToolbarWidth;
-			db_set_b(0, SRMSGMOD_T, "alwaysfulltoolbar", (uint8_t)PluginConfig.m_bAlwaysFullToolbarWidth);
-			Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
-			break;
-
-		case ID_PICMENU_SAVETHISPICTUREAS:
-			if (m_pPanel.isActive())
-				SaveAvatarToFile(m_hOwnPic, 1);
-			else if (m_ace)
-				SaveAvatarToFile(m_ace->hbmPic, 0);
-			break;
-
-		case ID_PANELPICMENU_SAVETHISPICTUREAS:
-			if (m_ace)
-				SaveAvatarToFile(m_ace->hbmPic, 0);
-			break;
-
-		case ID_PICMENU_SETTINGS:
-			if (menuId == MENU_PICMENU) {
-				if (m_pPanel.isActive()) {
-					if (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0))
-						CallService(MS_AV_SETMYAVATARW, (WPARAM)(m_cache->getActiveProto()), 0);
-					return TRUE;
-				}
-			}
-			CallService(MS_AV_CONTACTOPTIONS, m_hContact, (LPARAM)m_hwnd);
-			return 1;
-		}
-	}
-	else if (menuId == MENU_LOGMENU) {
-		switch (selection) {
-		case ID_MESSAGELOGSETTINGS_GLOBAL:
-			g_plugin.openOptions(nullptr, L"Message sessions", L"Message log");
-			return 1;
-
-		case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
-			CallService(MS_TABMSG_SETUSERPREFS, m_hContact, 0);
-			return 1;
-		}
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::NotifyDeliveryFailure() const
-{
-	if (!g_plugin.bErrorPopup || !Popup_Enabled())
-		return;
-
-	POPUPDATAW ppd = {};
-	ppd.lchContact = m_hContact;
-	ppd.PluginWindowProc = Utils::PopupDlgProcError;
-	ppd.lchIcon = PluginConfig.g_iconErr;
-	ppd.iSeconds = NEN::iDelayErr;
-	if (!NEN::bColDefaultErr) {
-		ppd.colorText = NEN::colTextErr;
-		ppd.colorBack = NEN::colBackErr;
-	}
-	wcsncpy_s(ppd.lpwzContactName, m_cache->getNick(), _TRUNCATE);
-	wcsncpy_s(ppd.lpwzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE);
-	PUAddPopupW(&ppd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::PlayIncomingSound() const
-{
-	int iPlay = m_pContainer->MustPlaySound(this);
-	if (iPlay) {
-		if (GetForegroundWindow() == m_pContainer->m_hwnd && m_pContainer->m_hwndActive == m_hwnd)
-			Skin_PlaySound("RecvMsgActive");
-		else
-			Skin_PlaySound("RecvMsgInactive");
-	}
-}
-
-void CMsgDialog::RemakeLog()
-{
-	m_szMicroLf[0] = 0;
-	m_lastEventTime = 0;
-	m_iLastEventType = -1;
-	StreamEvents(m_hDbEventFirst, -1, 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// saves a contact picture to disk
-// takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar)
-// requires AVS service (Miranda 0.7+)
-
-void CMsgDialog::SaveAvatarToFile(HBITMAP hbm, int isOwnPic)
-{
-	wchar_t szFinalFilename[MAX_PATH];
-	time_t t = time(0);
-	struct tm *lt = localtime(&t);
-	uint32_t setView = 1;
-
-	wchar_t szTimestamp[100];
-	mir_snwprintf(szTimestamp, L"%04u %02u %02u_%02u%02u", lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min);
-
-	wchar_t *szProto = mir_a2u(m_cache->getActiveProto());
-
-	wchar_t szFinalPath[MAX_PATH];
-	mir_snwprintf(szFinalPath, L"%s\\%s", M.getSavedAvatarPath(), szProto);
-	mir_free(szProto);
-
-	if (CreateDirectory(szFinalPath, nullptr) == 0) {
-		if (GetLastError() != ERROR_ALREADY_EXISTS) {
-			MessageBox(nullptr, TranslateT("Error creating destination directory"),
-				TranslateT("Save contact picture"), MB_OK | MB_ICONSTOP);
-			return;
-		}
-	}
-
-	wchar_t szBaseName[MAX_PATH];
-	if (isOwnPic)
-		mir_snwprintf(szBaseName, L"My Avatar_%s", szTimestamp);
-	else
-		mir_snwprintf(szBaseName, L"%s_%s", m_cache->getNick(), szTimestamp);
-
-	mir_snwprintf(szFinalFilename, L"%s.png", szBaseName);
-
-	// do not allow / or \ or % in the filename
-	Utils::sanitizeFilename(szFinalFilename);
-
-	wchar_t filter[MAX_PATH];
-	mir_snwprintf(filter, L"%s%c*.bmp;*.png;*.jpg;*.gif%c%c", TranslateT("Image files"), 0, 0, 0);
-
-	OPENFILENAME ofn = { 0 };
-	ofn.lpstrDefExt = L"png";
-	ofn.lpstrFilter = filter;
-	ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
-	ofn.lpfnHook = OpenFileSubclass;
-	ofn.lStructSize = sizeof(ofn);
-	ofn.lpstrFile = szFinalFilename;
-	ofn.lpstrInitialDir = szFinalPath;
-	ofn.nMaxFile = MAX_PATH;
-	ofn.nMaxFileTitle = MAX_PATH;
-	ofn.lCustData = (LPARAM)& setView;
-	if (GetSaveFileName(&ofn)) {
-		if (PathFileExists(szFinalFilename))
-			if (MessageBox(nullptr, TranslateT("The file exists. Do you want to overwrite it?"), TranslateT("Save contact picture"), MB_YESNO | MB_ICONQUESTION) == IDNO)
-				return;
-
-		IMGSRVC_INFO ii;
-		ii.cbSize = sizeof(ii);
-		ii.pwszName = szFinalFilename;
-		ii.hbm = hbm;
-		ii.dwMask = IMGI_HBITMAP;
-		ii.fif = FIF_UNKNOWN;			// get the format from the filename extension. png is default.
-		Image_Save(&ii);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::SaveSplitter()
-{
-	if (m_bIsAutosizingInput)
-		return;
-
-	if (m_iSplitterY < DPISCALEY_S(MINSPLITTERY) || m_iSplitterY < 0)
-		m_iSplitterY = DPISCALEY_S(MINSPLITTERY);
-
-	if (m_bSplitterOverride)
-		db_set_dw(m_hContact, SRMSGMOD_T, "splitsplity", m_iSplitterY);
-	else {
-		if (m_pContainer->cfg.fPrivate)
-			m_pContainer->cfg.iSplitterY = m_iSplitterY;
-		else
-			db_set_dw(0, SRMSGMOD_T, "splitsplity", m_iSplitterY);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// send a pasted bitmap by file transfer.
-
-static LIST<wchar_t> vTempFilenames(5);
-
-void CMsgDialog::SendHBitmapAsFile(HBITMAP hbmp) const
-{
-	const wchar_t *mirandatempdir = L"Miranda";
-	const wchar_t *filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg";
-	wchar_t filename[MAX_PATH];
-	size_t tempdirlen = GetTempPath(MAX_PATH, filename);
-	bool fSend = true;
-
-	const char *szProto = m_cache->getActiveProto();
-	int wMyStatus = Proto_GetStatus(szProto);
-
-	uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
-	uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
-
-	// check protocol capabilities, status modes and visibility lists (privacy)
-	// to determine whether the file can be sent. Throw a warning if any of
-	// these checks fails.
-	if (!(protoCaps & PF1_FILESEND))
-		fSend = false;
-
-	if ((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == m_cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES)))
-		fSend = false;
-
-	if (protoCaps & PF1_VISLIST && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
-		fSend = false;
-
-	if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
-		fSend = false;
-
-	if (!fSend) {
-		CWarning::show(CWarning::WARN_SENDFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
-		return;
-	}
-
-	if (tempdirlen <= 0 || tempdirlen >= MAX_PATH - mir_wstrlen(mirandatempdir) - mir_wstrlen(filenametemplate) - 2) // -2 is because %Y takes 4 symbols
-		filename[0] = 0;					// prompt for a new name
-	else {
-		mir_wstrcpy(filename + tempdirlen, mirandatempdir);
-		if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, nullptr) == 0)
-			filename[0] = 0;
-		else {
-			tempdirlen = mir_wstrlen(filename);
-
-			time_t rawtime;
-			time(&rawtime);
-			const tm *timeinfo;
-			timeinfo = _localtime32((__time32_t *)& rawtime);
-			wcsftime(filename + tempdirlen, MAX_PATH - tempdirlen, filenametemplate, timeinfo);
-			size_t firstnumberpos = tempdirlen + 14;
-			size_t lastnumberpos = tempdirlen + 20;
-			while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) {	// while it exists
-				for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--)
-					if (filename[pos]++ != '9')
-						break;
-					else
-						if (pos == firstnumberpos)
-							filename[0] = 0;	// all filenames exist => prompt for a new name
-						else
-							filename[pos] = '0';
-			}
-		}
-	}
-
-	if (filename[0] == 0) {	// prompting to save
-		wchar_t filter[MAX_PATH];
-		mir_snwprintf(filter, L"%s%c*.jpg%c%c", TranslateT("JPEG-compressed images"), 0, 0, 0);
-
-		OPENFILENAME dlg;
-		dlg.lStructSize = sizeof(dlg);
-		dlg.lpstrFilter = filter;
-		dlg.nFilterIndex = 1;
-		dlg.lpstrFile = filename;
-		dlg.nMaxFile = MAX_PATH;
-		dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
-		dlg.lpstrDefExt = L"jpg";
-		if (!GetSaveFileName(&dlg))
-			return;
-	}
-
-	IMGSRVC_INFO ii;
-	ii.cbSize = sizeof(ii);
-	ii.hbm = hbmp;
-	ii.pwszName = filename;
-	ii.dwMask = IMGI_HBITMAP;
-	ii.fif = FIF_JPEG;
-	if (!Image_Save(&ii)) {
-		CWarning::show(CWarning::WARN_SAVEFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
-		return;
-	}
-
-	vTempFilenames.insert(mir_wstrdup(filename));
-
-	wchar_t *ppFiles[2] = { filename, nullptr };
-	CallService(MS_FILE_SENDSPECIFICFILEST, m_cache->getActiveContact(), (LPARAM)&ppFiles);
-}
-
-// remove all temporary files created by the "send clipboard as file" feature.
-void TSAPI CleanTempFiles()
-{
-	for (auto &it : vTempFilenames) {
-		DeleteFileW(it);
-		mir_free(it);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// Sets a status bar text for a contact
-
-void CMsgDialog::SetStatusText(const wchar_t *wszText, HICON hIcon)
-{
-	if (wszText != nullptr) {
-		m_bStatusSet = true;
-		m_szStatusText = wszText;
-		m_szStatusIcon = hIcon;
-	}
-	else {
-		m_bStatusSet = false;
-		m_szStatusText.Empty();
-		m_szStatusIcon = nullptr;
-	}
-
-	tabUpdateStatusBar();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// subclassing for the message filter dialog (set and configure event filters for the 
-// current session
-
-static UINT _eventorder[] =
-{
-	GC_EVENT_ACTION,
-	GC_EVENT_MESSAGE,
-	GC_EVENT_NICK,
-	GC_EVENT_JOIN,
-	GC_EVENT_PART,
-	GC_EVENT_TOPIC,
-	GC_EVENT_ADDSTATUS,
-	GC_EVENT_INFORMATION,
-	GC_EVENT_QUIT,
-	GC_EVENT_KICK,
-	GC_EVENT_NOTICE
-};
-
-INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
-	CMsgDialog *pDlg = (CMsgDialog *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
-	switch (uMsg) {
-	case WM_INITDIALOG:
-		pDlg = (CMsgDialog *)lParam;
-		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
-		{
-			uint32_t dwMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", 0);
-			uint32_t dwFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", 0);
-
-			uint32_t dwPopupMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", 0);
-			uint32_t dwPopupFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", 0);
-
-			uint32_t dwTrayMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", 0);
-			uint32_t dwTrayFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", 0);
-
-			for (int i = 0; i < _countof(_eventorder); i++) {
-				CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
-				CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
-				CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
-			}
-		}
-		return FALSE;
-
-	case WM_CTLCOLOREDIT:
-	case WM_CTLCOLORSTATIC:
-		SetTextColor((HDC)wParam, RGB(60, 60, 150));
-		SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
-		return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
-
-	case WM_CLOSE:
-		if (wParam == 1 && lParam == 1 && pDlg) {
-			int iFlags = 0;
-			uint32_t dwMask = 0;
-
-			for (int i = 0; i < _countof(_eventorder); i++) {
-				int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i);
-				dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
-				iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
-			}
-
-			if (iFlags & GC_EVENT_ADDSTATUS)
-				iFlags |= GC_EVENT_REMOVESTATUS;
-
-			if (dwMask == 0) {
-				db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterFlags");
-				db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterMask");
-			}
-			else {
-				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", iFlags);
-				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", dwMask);
-			}
-
-			dwMask = iFlags = 0;
-
-			for (int i = 0; i < _countof(_eventorder); i++) {
-				int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i);
-				dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
-				iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
-			}
-
-			if (iFlags & GC_EVENT_ADDSTATUS)
-				iFlags |= GC_EVENT_REMOVESTATUS;
-
-			if (dwMask == 0) {
-				db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupFlags");
-				db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupMask");
-			}
-			else {
-				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", iFlags);
-				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", dwMask);
-			}
-
-			dwMask = iFlags = 0;
-
-			for (int i = 0; i < _countof(_eventorder); i++) {
-				int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i);
-				dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
-				iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
-			}
-			if (iFlags & GC_EVENT_ADDSTATUS)
-				iFlags |= GC_EVENT_REMOVESTATUS;
-
-			if (dwMask == 0) {
-				db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags");
-				db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask");
-			}
-			else {
-				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", iFlags);
-				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", dwMask);
-			}
-			Chat_SetFilters(pDlg->getChat());
-
-			if (pDlg->m_bFilterEnabled) {
-				if (pDlg->m_iLogFilterFlags == 0)
-					pDlg->m_btnFilter.Click();
-				pDlg->RedrawLog();
-				db_set_b(pDlg->m_hContact, CHAT_MODULE, "FilterEnabled", pDlg->m_bFilterEnabled);
-			}
-		}
-		DestroyWindow(hwndDlg);
-		break;
-
-	case WM_DESTROY:
-		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
-		break;
-	}
-	return FALSE;
-}
-
-void CMsgDialog::ShowFilterMenu()
-{
-	m_hwndFilter = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_pContainer->m_hwnd, FilterWndProc, (LPARAM)this);
-	TranslateDialogDefault(m_hwndFilter);
-
-	RECT rcFilter, rcLog;
-	GetClientRect(m_hwndFilter, &rcFilter);
-	GetWindowRect(m_pLog->GetHwnd(), &rcLog);
-
-	POINT pt;
-	pt.x = rcLog.right; pt.y = rcLog.bottom;
-	ScreenToClient(m_pContainer->m_hwnd, &pt);
-
-	SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::ShowPicture(bool showNewPic)
-{
-	if (!m_pPanel.isActive())
-		m_pic.cy = m_pic.cx = DPISCALEY_S(60);
-
-	if (showNewPic) {
-		if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
-			if (!m_hwndPanelPic) {
-				InvalidateRect(m_hwnd, nullptr, TRUE);
-				UpdateWindow(m_hwnd);
-				Resize();
-			}
-			return;
-		}
-		AdjustBottomAvatarDisplay();
-	}
-	else {
-		m_bShowAvatar = !m_bShowAvatar;
-		db_set_b(m_hContact, SRMSGMOD_T, "MOD_ShowPic", m_bShowAvatar);
-	}
-
-	RECT rc;
-	GetWindowRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), &rc);
-	if (m_minEditBoxSize.cy + DPISCALEY_S(3) > m_iSplitterY)
-		SplitterMoved(rc.bottom - m_minEditBoxSize.cy, GetDlgItem(m_hwnd, IDC_SPLITTERY));
-	if (!showNewPic)
-		SetDialogToType();
-	else
-		Resize();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// show a modified context menu for the richedit control(s)
-
-void CMsgDialog::ShowPopupMenu(const CCtrlBase &pCtrl, POINT pt)
-{
-	CHARRANGE sel, all = { 0, -1 };
-
-	HMENU hSubMenu, hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
-	if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
-		hSubMenu = GetSubMenu(hMenu, 0);
-	else {
-		hSubMenu = GetSubMenu(hMenu, 2);
-		EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, m_SendFormat != 0 ? MF_ENABLED : MF_GRAYED);
-		EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, g_plugin.bPasteAndSend ? MF_ENABLED : MF_GRAYED);
-		CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED);
-		EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, m_pContainer->m_hwndStatus ? MF_ENABLED : MF_GRAYED);
-	}
-	TranslateMenu(hSubMenu);
-	pCtrl.SendMsg(EM_EXGETSEL, 0, (LPARAM)& sel);
-	if (sel.cpMin == sel.cpMax) {
-		EnableMenuItem(hSubMenu, IDM_COPY, MF_GRAYED);
-		EnableMenuItem(hSubMenu, IDM_QUOTE, MF_GRAYED);
-		if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
-			EnableMenuItem(hSubMenu, IDM_CUT, MF_GRAYED);
-	}
-
-	if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) {
-		InsertMenuA(hSubMenu, 6, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
-		CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, m_bScrollingDisabled ? MF_CHECKED : MF_UNCHECKED);
-	}
-
-	MessageWindowPopupData mwpd;
-	// First notification
-	mwpd.uType = MSG_WINDOWPOPUP_SHOWING;
-	mwpd.uFlags = (pCtrl.GetCtrlId() == IDC_SRMM_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT);
-	mwpd.hContact = m_hContact;
-	mwpd.hwnd = pCtrl.GetHwnd();
-	mwpd.hMenu = hSubMenu;
-	mwpd.selection = 0;
-	mwpd.pt = pt;
-	NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
-
-	int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
-
-	// Second notification
-	mwpd.selection = iSelection;
-	mwpd.uType = MSG_WINDOWPOPUP_SELECTED;
-	NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
-
-	switch (iSelection) {
-	case IDM_COPY:
-		pCtrl.SendMsg(WM_COPY, 0, 0);
-		break;
-	case IDM_CUT:
-		pCtrl.SendMsg(WM_CUT, 0, 0);
-		break;
-	case IDM_PASTE:
-	case IDM_PASTEFORMATTED:
-		if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
-			pCtrl.SendMsg(EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0);
-		break;
-	case IDM_COPYALL:
-		pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
-		pCtrl.SendMsg(WM_COPY, 0, 0);
-		pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& sel);
-		break;
-	case IDM_QUOTE:
-		SendMessage(m_hwnd, WM_COMMAND, IDC_QUOTE, 0);
-		break;
-	case IDM_SELECTALL:
-		pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
-		break;
-	case IDM_CLEAR:
-		tabClearLog();
-		break;
-	case ID_LOG_FREEZELOG:
-		SendDlgItemMessage(m_hwnd, IDC_SRMM_LOG, WM_KEYDOWN, VK_F12, 0);
-		break;
-	case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR:
-		PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator;
-		db_set_b(0, SRMSGMOD_T, "msgsizebar", (uint8_t)PluginConfig.m_visualMessageSizeIndicator);
-		Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 0);
-		Resize();
-		if (m_pContainer->m_hwndStatus)
-			RedrawWindow(m_pContainer->m_hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
-		break;
-	case ID_EDITOR_PASTEANDSENDIMMEDIATELY:
-		HandlePasteAndSend();
-		break;
-	}
-
-	if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
-		RemoveMenu(hSubMenu, 7, MF_BYPOSITION);
-	DestroyMenu(hMenu);
-}
-
-void CMsgDialog::SplitterMoved(int coord, HWND hwnd)
-{
-	POINT pt;
-	RECT rc;
-
-	switch (GetDlgCtrlID(hwnd)) {
-	case IDC_MULTISPLITTER:
-		GetClientRect(m_hwnd, &rc);
-		pt.x = coord;
-		pt.y = 0;
-		ScreenToClient(m_hwnd, &pt);
-		{
-			int oldSplitterX = m_iMultiSplit;
-			m_iMultiSplit = rc.right - pt.x;
-			if (m_iMultiSplit < 25)
-				m_iMultiSplit = 25;
-
-			if (m_iMultiSplit > ((rc.right - rc.left) - 80))
-				m_iMultiSplit = oldSplitterX;
-		}
-		Resize();
-		break;
-
-	case IDC_SPLITTERX:
-		GetClientRect(m_hwnd, &rc);
-		pt.x = coord, pt.y = 0;
-		ScreenToClient(m_hwnd, &pt);
-		{
-			int iSplitterX = rc.right - pt.x + 1;
-			if (iSplitterX < 35)
-				iSplitterX = 35;
-			if (iSplitterX > rc.right - rc.left - 35)
-				iSplitterX = rc.right - rc.left - 35;
-			m_pContainer->cfg.iSplitterX = iSplitterX;
-		}
-		Resize();
-		break;
-
-	case IDC_SPLITTERY:
-		GetClientRect(m_hwnd, &rc);
-		rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30);
-		pt.x = 0;
-		pt.y = coord;
-		ScreenToClient(m_hwnd, &pt);
-		{
-			int oldSplitterY = m_iSplitterY;
-			int oldDynaSplitter = m_dynaSplitter;
-
-			m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(23);
-
-			// attempt to fix splitter troubles..
-			// hardcoded limits... better solution is possible, but this works for now
-			int bottomtoolbarH = 0;
-			if (m_pContainer->cfg.flags.m_bBottomToolbar)
-				bottomtoolbarH = 22;
-
-			if (m_iSplitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) {	// min splitter size
-				m_iSplitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH);
-				m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
-				DM_RecalcPictureSize();
-			}
-			else if (m_iSplitterY > (rc.bottom - rc.top)) {
-				m_iSplitterY = oldSplitterY;
-				m_dynaSplitter = oldDynaSplitter;
-				DM_RecalcPictureSize();
-			}
-			else {
-				m_dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11);
-				DM_RecalcPictureSize();
-			}
-		}
-		UpdateToolbarBG();
-		Resize();
-		break;
-
-	case IDC_PANELSPLITTER:
-		GetClientRect(m_pLog->GetHwnd(), &rc);
-
-		POINT	pnt = { 0, coord };
-		ScreenToClient(m_hwnd, &pnt);
-		if ((pnt.y + 2 >= MIN_PANELHEIGHT + 2) && (pnt.y + 2 < 100) && (pnt.y + 2 < rc.bottom - 30))
-			m_pPanel.setHeight(pnt.y + 2, true);
-
-		RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
-		if (M.isAero())
-			InvalidateRect(GetParent(m_hwnd), nullptr, FALSE);
-		break;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::StreamEvents(MEVENT hDbEventFirst, int count, bool bAppend)
-{
-	m_pLog->LogEvents(hDbEventFirst, count, bAppend);
-
-	DM_ScrollToBottom(0, 0);
-	if (bAppend && hDbEventFirst)
-		m_hDbEventLast = hDbEventFirst;
-	else
-		m_hDbEventLast = db_event_last(m_hContact);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// sent by the select container dialog box when a container was selected...
-
-void CMsgDialog::SwitchToContainer(const wchar_t *szNewName)
-{
-	if (!mir_wstrcmp(szNewName, TranslateT("Default container")))
-		szNewName = CGlobals::m_default_container_name;
-
-	int iOldItems = TabCtrl_GetItemCount(m_hwndParent);
-	if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN))
-		return;
-
-	TContainerData *pNewContainer = FindContainerByName(szNewName);
-	if (pNewContainer == nullptr)
-		if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr)
-			return;
-
-	db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName);
-	PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact);
-	if (iOldItems > 1)                // there were more than 1 tab, container is still valid
-		SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0);
-	SetForegroundWindow(pNewContainer->m_hwnd);
-	SetActiveWindow(pNewContainer->m_hwnd);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-bool CMsgDialog::TabAutoComplete()
-{
-	LRESULT lResult = m_message.SendMsg(EM_GETSEL, 0, 0);
-	int start = LOWORD(lResult), end = HIWORD(lResult);
-	int origStart = start, origEnd = end;
-	m_message.SendMsg(EM_SETSEL, end, end);
-
-	GETTEXTEX gt = { 0 };
-	gt.codepage = 1200;
-	gt.flags = GTL_DEFAULT | GTL_PRECISE;
-	int iLen = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)& gt, 0);
-	if (iLen <= 0)
-		return false;
-
-	bool isTopic = false, isRoom = false;
-	wchar_t *pszText = (wchar_t *)mir_calloc((iLen + 10) * sizeof(wchar_t));
-
-	gt.flags = GT_DEFAULT;
-	gt.cb = (iLen + 9) * sizeof(wchar_t);
-	m_message.SendMsg(EM_GETTEXTEX, (WPARAM)& gt, (LPARAM)pszText);
-
-	if (m_wszSearchResult != nullptr) {
-		int cbResult = (int)mir_wstrlen(m_wszSearchResult);
-		if (start >= cbResult && !wcsnicmp(m_wszSearchResult, pszText + start - cbResult, cbResult)) {
-			start -= cbResult;
-			goto LBL_SkipEnd;
-		}
-	}
-
-	while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB)
-		start--;
-
-LBL_SkipEnd:
-	while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB)
-		end++;
-
-	if (pszText[start] == '#')
-		isRoom = true;
-	else {
-		int topicStart = start;
-		while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB))
-			topicStart--;
-		if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6])
-			isTopic = true;
-	}
-	
-	if (m_wszSearchQuery == nullptr) {
-		m_wszSearchQuery = mir_wstrndup(pszText + start, end - start);
-		m_wszSearchResult = mir_wstrdup(m_wszSearchQuery);
-		m_pLastSession = nullptr;
-	}
-
-	const wchar_t *pszName = nullptr;
-	if (isTopic)
-		pszName = m_si->ptszTopic;
-	else if (isRoom) {
-		m_pLastSession = SM_FindSessionAutoComplete(m_si->pszModule, m_si, m_pLastSession, m_wszSearchQuery, m_wszSearchResult);
-		if (m_pLastSession != nullptr)
-			pszName = m_pLastSession->ptszName;
-	}
-	else pszName = g_chatApi.UM_FindUserAutoComplete(m_si, m_wszSearchQuery, m_wszSearchResult);
-
-	replaceStrW(m_wszSearchResult, nullptr);
-
-	if (pszName != nullptr) {
-		if (end != start) {
-			CMStringW szReplace;
-			if (!isRoom && !isTopic && start == 0) {
-				szReplace = pszName;
-				if (mir_wstrlen(g_Settings.pwszAutoText))
-					szReplace.Append(g_Settings.pwszAutoText);
-				szReplace.AppendChar(' ');
-				m_wszSearchResult = szReplace.Detach();
-				pszName = m_wszSearchResult;
-			}
-			else m_wszSearchResult = mir_wstrdup(pszName);
-
-			m_message.SendMsg(EM_SETSEL, start, end);
-			m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pszName);
-		}
-		else m_wszSearchResult = mir_wstrdup(pszName);
-
-		return true;
-	}
-
-	if (end != start) {
-		m_message.SendMsg(EM_SETSEL, start, end);
-		m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)m_wszSearchQuery);
-	}
-	m_message.SendMsg(EM_SETSEL, origStart, origEnd);
-	replaceStrW(m_wszSearchQuery, nullptr);
-	return false;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::tabClearLog()
-{
-	if (isChat()) {
-		g_chatApi.LM_RemoveAll(&m_si->pLog, &m_si->pLogEnd);
-		m_si->iEventCount = 0;
-		m_si->LastTime = 0;
-		PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
-	}
-
-	m_pLog->Clear();
-	m_hDbEventFirst = 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-CThumbBase *CMsgDialog::tabCreateThumb(CProxyWindow *pProxy) const
-{
-	if (isChat())
-		return new CThumbMUC(pProxy, m_si);
-
-	return new CThumbIM(pProxy);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update all status bar fields and force a redraw of the status bar.
-
-void CMsgDialog::tabUpdateStatusBar() const
-{
-	if (m_pContainer->m_hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
-		if (!isChat()) {
-			if (m_wszStatusBar[0]) {
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
-			}
-			else if (m_bStatusSet) {
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
-			}
-			else {
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
-				DM_UpdateLastMessage();
-			}
-		}
-		else {
-			if (m_bStatusSet) {
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
-				SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
-			}
-			else SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
-		}
-		UpdateReadChars();
-		InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
-		SendMessage(m_pContainer->m_hwndStatus, WM_USER + 101, 0, (LPARAM)this);
-	}
-}
-
-int CMsgDialog::Typing(int secs)
-{
-	if (!AllowTyping())
-		return 0;
-
-	int preTyping = m_nTypeSecs != 0;
-
-	setTyping(m_nTypeSecs = (secs > 0) ? secs : 0);
-	if (m_nTypeSecs)
-		m_bShowTyping = 0;
-
-	return preTyping;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateNickList()
-{
-	int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
-	m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0);
-	m_nickList.SendMsg(LB_SETTOPINDEX, i, 0);
-	UpdateTitle();
-	m_hTabIcon = m_hTabStatusIcon;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateOptions()
-{
-	GetSendFormat();
-
-	DM_InitRichEdit();
-	m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0);
-
-	m_nickList.SetItemHeight(0, g_Settings.iNickListFontHeight);
-	InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
-
-	m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(m_bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0);
-
-	CSuper::UpdateOptions();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// update the status bar field which displays the number of characters in the input area
-// and various indicators (caps lock, num lock, insert mode).
-
-void CMsgDialog::UpdateReadChars() const
-{
-	if (!m_pContainer->m_hwndStatus || m_pContainer->m_hwndActive != m_hwnd)
-		return;
-
-	int len;
-	if (isChat())
-		len = GetWindowTextLength(m_message.GetHwnd());
-	else {
-		// retrieve text length in UTF8 bytes, because this is the relevant length for most protocols
-		GETTEXTLENGTHEX gtxl = { 0 };
-		gtxl.codepage = CP_UTF8;
-		gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
-
-		len = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
-	}
-
-	BOOL fCaps = (GetKeyState(VK_CAPITAL) & 1);
-	BOOL fNum = (GetKeyState(VK_NUMLOCK) & 1);
-
-	wchar_t szBuf[20]; szBuf[0] = 0;
-	if (m_bInsertMode)
-		mir_wstrcat(szBuf, L"O");
-	if (fCaps)
-		mir_wstrcat(szBuf, L"C");
-	if (fNum)
-		mir_wstrcat(szBuf, L"N");
-	if (m_bInsertMode || fCaps || fNum)
-		mir_wstrcat(szBuf, L" | ");
-
-	wchar_t buf[128];
-	mir_snwprintf(buf, L"%s%s %d/%d", szBuf, m_lcID, m_iOpenJobs, len);
-	SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)buf);
-	if (PluginConfig.m_visualMessageSizeIndicator)
-		InvalidateRect(m_pContainer->m_hwndStatus, nullptr, FALSE);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateSaveAndSendButton()
-{
-	GETTEXTLENGTHEX gtxl = { 0 };
-	gtxl.codepage = CP_UTF8;
-	gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
-
-	int len = SendDlgItemMessage(m_hwnd, IDC_SRMM_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
-	if (len && GetSendButtonState() == PBS_DISABLED)
-		EnableSendButton(true);
-	else if (len == 0 && GetSendButtonState() != PBS_DISABLED)
-		EnableSendButton(false);
-
-	if (len) {          // looks complex but avoids flickering on the button while typing.
-		if (!m_bSaveBtn) {
-			SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]);
-			SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save and close session"), BATF_UNICODE);
-			m_bSaveBtn = true;
-		}
-	}
-	else {
-		SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]);
-		SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Close session"), BATF_UNICODE);
-		m_bSaveBtn = false;
-	}
-	m_textLen = len;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateStatusBar()
-{
-	if (m_pContainer->m_hwndActive != m_hwnd || m_pContainer->m_hwndStatus == nullptr || CMimAPI::m_shutDown || m_wszStatusBar[0])
-		return;
-
-	if (m_si->pszModule == nullptr)
-		return;
-
-	//Mad: strange rare crash here...
-	MODULEINFO *mi = m_si->pMI;
-	if (!mi->ptszModDispName)
-		return;
-
-	int x = 12;
-	x += Chat_GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->m_hwndStatus, WM_GETFONT, 0, 0), true);
-	x += GetSystemMetrics(SM_CXSMICON);
-
-	wchar_t szFinalStatusBarText[512];
-	if (m_pPanel.isActive()) {
-		time_t now = time(0);
-		uint32_t diff = (now - mi->idleTimeStamp) / 60;
-		if (diff >= 1) {
-			if (diff > 59) {
-				uint32_t hours = diff / 60;
-				uint32_t minutes = diff % 60;
-				mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"),
-					hours, hours > 1 ? TranslateT("hours") : TranslateT("hour"),
-					minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute"));
-			}
-			else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute"));
-		}
-		else mi->tszIdleMsg[0] = 0;
-
-		mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg);
-	}
-	else {
-		if (m_si->ptszStatusbarText)
-			mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, m_si->ptszStatusbarText);
-		else
-			wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE);
-	}
-	SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText);
-	tabUpdateStatusBar();
-	m_pPanel.Invalidate();
-	if (m_pWnd)
-		m_pWnd->Invalidate();
-}
-
-void CMsgDialog::UpdateTitle()
-{
-	if (isChat()) {
-		m_wStatus = m_si->wStatus;
-
-		const wchar_t *szNick = m_cache->getNick();
-		if (mir_wstrlen(szNick) > 0) {
-			if (M.GetByte("cuttitle", 0))
-				CutContactName(szNick, m_wszTitle, _countof(m_wszTitle));
-			else
-				wcsncpy_s(m_wszTitle, szNick, _TRUNCATE);
-		}
-
-		CMStringW wszTitle;
-		HICON hIcon = nullptr;
-		int nUsers = m_si->getUserList().getCount();
-
-		switch (m_si->iType) {
-		case GCW_CHATROOM:
-			hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
-			wszTitle.Format((nUsers == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"),
-				szNick, nUsers, m_bFilterEnabled ? TranslateT(", event filter active") : L"");
-			break;
-
-		case GCW_PRIVMESS:
-			hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
-			if (nUsers == 1)
-				wszTitle.Format(TranslateT("%s: message session"), szNick);
-			else
-				wszTitle.Format(TranslateT("%s: message session (%u users)"), szNick, nUsers);
-			break;
-
-		case GCW_SERVER:
-			wszTitle.Format(L"%s: Server", szNick);
-			hIcon = LoadIconEx("window");
-			break;
-
-		default:
-			return;
-		}
-
-		if (m_pWnd) {
-			m_pWnd->updateTitle(m_wszTitle);
-			m_pWnd->updateIcon(hIcon);
-		}
-		m_hTabStatusIcon = hIcon;
-
-		if (m_cache->getStatus() != m_cache->getOldStatus()) {
-			wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_wStatus, 0), _TRUNCATE);
-
-			TCITEM item = {};
-			item.mask = TCIF_TEXT;
-			item.pszText = m_wszTitle;
-			TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
-		}
-		SetWindowText(m_hwnd, wszTitle);
-		if (m_pContainer->m_hwndActive == m_hwnd) {
-			m_pContainer->UpdateTitle(0, this);
-			UpdateStatusBar();
-		}
-	}
-	else {
-		uint32_t dwOldIdle = m_idle;
-		const char *szActProto = nullptr;
-
-		m_wszStatus[0] = 0;
-
-		if (m_iTabID == -1)
-			return;
-
-		TCITEM item = {};
-
-		bool bChanged = false;
-		wchar_t newtitle[128];
-		if (m_szProto) {
-			szActProto = m_cache->getProto();
-
-			bool bHasName = (m_cache->getUIN()[0] != 0);
-			m_idle = m_cache->getIdleTS();
-			m_bIsIdle = m_idle != 0;
-
-			m_wStatus = m_cache->getStatus();
-			wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_szProto == nullptr ? ID_STATUS_OFFLINE : m_wStatus, 0), _TRUNCATE);
-
-			wchar_t newcontactname[128]; newcontactname[0] = 0;
-			if (PluginConfig.m_bCutContactNameOnTabs)
-				CutContactName(m_cache->getNick(), newcontactname, _countof(newcontactname));
-			else
-				wcsncpy_s(newcontactname, m_cache->getNick(), _TRUNCATE);
-
-			Utils::DoubleAmpersands(newcontactname, _countof(newcontactname));
-
-			if (newcontactname[0] != 0) {
-				if (g_plugin.bStatusOnTabs)
-					mir_snwprintf(newtitle, L"%s (%s)", newcontactname, m_wszStatus);
-				else
-					wcsncpy_s(newtitle, newcontactname, _TRUNCATE);
-			}
-			else wcsncpy_s(newtitle, L"Forward", _TRUNCATE);
-
-			if (mir_wstrcmp(newtitle, m_wszTitle))
-				bChanged = true;
-			else if (m_wStatus != m_wOldStatus)
-				bChanged = true;
-
-			UpdateWindowIcon();
-
-			wchar_t fulluin[256];
-			if (m_bIsMeta)
-				mir_snwprintf(fulluin,
-				TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nRight click for metacontact control\nClick dropdown to add or remove user from your favorites."),
-				bHasName ? m_cache->getUIN() : TranslateT("No UID"));
-			else
-				mir_snwprintf(fulluin,
-				TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nClick dropdown to change this contact's favorite status."),
-				bHasName ? m_cache->getUIN() : TranslateT("No UID"));
-
-			SendDlgItemMessage(m_hwnd, IDC_NAME, BUTTONADDTOOLTIP, (WPARAM)fulluin, BATF_UNICODE);
-		}
-		else wcsncpy_s(newtitle, L"Message Session", _TRUNCATE);
-
-		m_wOldStatus = m_wStatus;
-		if (m_idle != dwOldIdle || bChanged) {
-			if (bChanged) {
-				item.mask |= TCIF_TEXT;
-				item.pszText = m_wszTitle;
-				wcsncpy_s(m_wszTitle, newtitle, _TRUNCATE);
-				if (m_pWnd)
-					m_pWnd->updateTitle(m_cache->getNick());
-			}
-			if (m_iTabID >= 0) {
-				TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
-				if (m_pContainer->cfg.flags.m_bSideBar)
-					m_pContainer->m_pSideBar->updateSession(this);
-			}
-			if (m_pContainer->m_hwndActive == m_hwnd && bChanged)
-				m_pContainer->UpdateTitle(m_hContact);
-
-			m_pPanel.Invalidate();
-			if (m_pWnd)
-				m_pWnd->Invalidate();
-		}
-
-		// care about MetaContacts and update the statusbar icon with the currently "most online" contact...
-		if (m_bIsMeta) {
-			PostMessage(m_hwnd, DM_OWNNICKCHANGED, 0, 0);
-			if (m_pContainer->cfg.flags.m_bUinStatusBar)
-				DM_UpdateLastMessage();
-		}
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void CMsgDialog::UpdateWindowIcon()
-{
-	if (m_hXStatusIcon) {
-		DestroyIcon(m_hXStatusIcon);
-		m_hXStatusIcon = nullptr;
-	}
-
-	if (LPCSTR szProto = m_cache->getProto()) {
-		m_hTabIcon = m_hTabStatusIcon = GetMyContactIcon(&g_plugin.bMetaTab);
-		if (g_plugin.bUseXStatus)
-			m_hXStatusIcon = GetXStatusIcon();
-
-		SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BUTTONSETASDIMMED, m_bIsIdle, 0);
-		SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(m_hXStatusIcon ? m_hXStatusIcon : GetMyContactIcon(&g_plugin.bMetaBar)));
-
-		if (m_pContainer->m_hwndActive == m_hwnd)
-			m_pContainer->SetIcon(this, m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
-
-		if (m_pWnd)
-			m_pWnd->updateIcon(m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// called whenever a group chat tab becomes active(either by switching tabs or activating a
-// container window
-
-void CMsgDialog::UpdateWindowState(UINT msg)
-{
-	if (m_iTabID < 0)
-		return;
-
-	if (msg == WM_ACTIVATE) {
-		if (m_pContainer->cfg.flags.m_bTransparent) {
-			uint32_t trans = LOWORD(m_pContainer->cfg.dwTransparency);
-			SetLayeredWindowAttributes(m_pContainer->m_hwnd, CSkin::m_ContainerColorKey, (uint8_t)trans, (m_pContainer->cfg.flags.m_bTransparent ? LWA_ALPHA : 0));
-		}
-	}
-
-	if (m_hwndFilter) {
-		POINT pt;
-		GetCursorPos(&pt);
-
-		RECT rcFilter;
-		GetWindowRect(m_hwndFilter, &rcFilter);
-		if (!PtInRect(&rcFilter, pt)) {
-			SendMessage(m_hwndFilter, WM_CLOSE, 1, 1);
-			m_hwndFilter = nullptr;
-		}
-	}
-
-	if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) {
-		m_iInputAreaHeight = 0;
-		m_message.SendMsg(EM_REQUESTRESIZE, 0, 0);
-	}
-
-	m_pPanel.dismissConfig();
-	m_dwUnread = 0;
-	if (m_pWnd) {
-		m_pWnd->activateTab();
-		m_pWnd->setOverlayIcon(nullptr, true);
-	}
-
-	if (m_pContainer->m_hwndSaved == m_hwnd)
-		return;
-
-	m_pContainer->m_hwndSaved = m_hwnd;
-	m_dwTickLastEvent = 0;
-	m_bDividerSet = false;
-
-	if (m_pContainer->m_dwFlashingStarted != 0) {
-		m_pContainer->FlashContainer(0, 0);
-		m_pContainer->m_dwFlashingStarted = 0;
-	}
-
-	if (m_si) {
-		g_chatApi.SetActiveSession(m_si);
-		m_hTabIcon = m_hTabStatusIcon;
-
-		if (db_get_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0) != 0)
-			db_set_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0);
-		if (g_clistApi.pfnGetEvent(m_si->hContact, 0))
-			g_clistApi.pfnRemoveEvent(m_si->hContact, GC_FAKE_EVENT);
-
-		UpdateTitle();
-		m_hTabIcon = m_hTabStatusIcon;
-		if (timerFlash.Stop() || m_iFlashIcon) {
-			FlashTab(false);
-			m_bCanFlashTab = FALSE;
-			m_iFlashIcon = nullptr;
-		}
-
-		m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
-
-		if (m_bNeedCheckSize)
-			PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
-
-		SetFocus(m_message.GetHwnd());
-		m_dwLastActivity = GetTickCount();
-		m_pContainer->m_dwLastActivity = m_dwLastActivity;
-		m_pContainer->m_pMenuBar->configureMenu();
-	}
-	else {
-		if (timerFlash.Stop()) {
-			FlashTab(false);
-			m_bCanFlashTab = false;
-		}
-
-		if (m_bFlashClist) {
-			m_bFlashClist = false;
-			if (m_hFlashingEvent != 0)
-				g_clistApi.pfnRemoveEvent(m_hContact, m_hFlashingEvent);
-			m_hFlashingEvent = 0;
-		}
-		m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
-
-		if (m_bDeferredRemakeLog && !IsIconic(m_pContainer->m_hwnd)) {
-			RemakeLog();
-			m_bDeferredRemakeLog = false;
-		}
-
-		if (m_bNeedCheckSize)
-			PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
-
-		m_pContainer->m_hIconTaskbarOverlay = nullptr;
-		m_pContainer->UpdateTitle(m_hContact);
-
-		tabUpdateStatusBar();
-		m_dwLastActivity = GetTickCount();
-		m_pContainer->m_dwLastActivity = m_dwLastActivity;
-
-		m_pContainer->m_pMenuBar->configureMenu();
-		g_arUnreadWindows.remove(HANDLE(m_hContact));
-
-		m_pPanel.Invalidate();
-
-		if (m_bDeferredScroll) {
-			m_bDeferredScroll = false;
-			DM_ScrollToBottom(0, 1);
-		}
-	}
-
-	DM_SetDBButtonStates();
-
-	if (m_bDelayedSplitter) {
-		m_bDelayedSplitter = false;
-		ShowWindow(m_pContainer->m_hwnd, SW_RESTORE);
-		PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam);
-		PostMessage(m_hwnd, WM_SIZE, 0, 0);
-		m_wParam = m_lParam = 0;
-	}
-
-	BB_SetButtonsPos();
-	if (M.isAero())
-		InvalidateRect(m_hwndParent, nullptr, FALSE);
-
-	if (m_pContainer->cfg.flags.m_bSideBar)
-		m_pContainer->m_pSideBar->setActiveItem(this, msg == WM_ACTIVATE);
-
-	if (m_pWnd)
-		m_pWnd->Invalidate();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// generic handler for the WM_COPY message in message log/chat history richedit control(s).
-// it filters out the invisible event boundary markers from the text copied to the clipboard.
-// WINE Fix: overwrite clippboad data from original control data
-
-LRESULT CMsgDialog::WMCopyHandler(UINT msg, WPARAM wParam, LPARAM lParam)
-{
-	LRESULT result = mir_callNextSubclass(m_pLog->GetHwnd(), stubLogProc, msg, wParam, lParam);
-
-	ptrA szFromStream(LOG()->GetRichTextRtf(true, true));
-	if (szFromStream != nullptr) {
-		ptrW converted(mir_utf8decodeW(szFromStream));
-		if (converted != nullptr) {
-			Utils::FilterEventMarkers(converted);
-			Utils_ClipboardCopy(converted);
-		}
-	}
-
-	return result;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// Helper functions for the message dialog.
+
+#include "stdafx.h"
+
+UINT_PTR CALLBACK OpenFileSubclass(HWND hwnd, UINT msg, WPARAM, LPARAM lParam);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show the balloon tooltip control.
+
+void CMsgDialog::ActivateTooltip(int iCtrlId, const wchar_t *pwszMessage)
+{
+	if (!IsIconic(m_pContainer->m_hwnd) && m_pContainer->m_hwndActive == m_hwnd)
+		m_pPanel.showTip(iCtrlId, pwszMessage);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::AddLog()
+{
+	if (g_plugin.bUseDividers) {
+		if (g_plugin.bDividersUsePopupConfig) {
+			if (!MessageWindowOpened(0, this))
+				DM_AddDivider();
+		}
+		else {
+			if (!IsActive())
+				DM_AddDivider();
+			else if (m_pContainer->m_hwndActive != m_hwnd)
+				DM_AddDivider();
+		}
+	}
+
+	CSrmmBaseDialog::AddLog();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::AdjustBottomAvatarDisplay()
+{
+	GetAvatarVisibility();
+
+	bool bInfoPanel = m_pPanel.isActive();
+	HBITMAP hbm = (bInfoPanel && m_pContainer->cfg.avatarMode != 3) ? m_hOwnPic : (m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown);
+	if (hbm) {
+		if (m_dynaSplitter == 0 || m_iSplitterY == 0)
+			LoadSplitter();
+		m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
+		DM_RecalcPictureSize();
+		Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
+		InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
+	}
+	else {
+		Utils::showDlgControl(m_hwnd, IDC_CONTACTPIC, m_bShowAvatar ? SW_SHOW : SW_HIDE);
+		m_pic.cy = m_pic.cx = DPISCALEY_S(60);
+		InvalidateRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), nullptr, TRUE);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// calculates avatar layouting, based on splitter position to find the optimal size
+// for the avatar w/o disturbing the toolbar too much.
+
+void CMsgDialog::CalcDynamicAvatarSize(BITMAP *bminfo)
+{
+	if (m_bWasBackgroundCreate || m_pContainer->cfg.flags.m_bDeferredConfigure || m_pContainer->cfg.flags.m_bCreateMinimized || IsIconic(m_pContainer->m_hwnd))
+		return;  // at this stage, the layout is not yet ready...
+
+	RECT rc;
+	GetClientRect(m_hwnd, &rc);
+
+	BOOL bBottomToolBar = m_pContainer->cfg.flags.m_bBottomToolbar;
+	BOOL bToolBar = m_pContainer->cfg.flags.m_bHideToolbar ? 0 : 1;
+	int  iSplitOffset = m_bIsAutosizingInput ? 1 : 0;
+
+	double picAspect = (bminfo->bmWidth == 0 || bminfo->bmHeight == 0) ? 1.0 : (double)(bminfo->bmWidth / (double)bminfo->bmHeight);
+	double picProjectedWidth = (double)((m_dynaSplitter - ((bBottomToolBar && bToolBar) ? DPISCALEX_S(24) : 0) + ((m_bShowUIElements) ? DPISCALEX_S(28) : DPISCALEX_S(2)))) * picAspect;
+
+	if ((rc.right - (int)picProjectedWidth) > (m_iButtonBarReallyNeeds) && !PluginConfig.m_bAlwaysFullToolbarWidth && bToolBar)
+		m_iRealAvatarHeight = m_dynaSplitter + 3 + (m_bShowUIElements ? DPISCALEY_S(28) : DPISCALEY_S(2));
+	else
+		m_iRealAvatarHeight = m_dynaSplitter + DPISCALEY_S(6) + DPISCALEY_S(iSplitOffset);
+
+	m_iRealAvatarHeight -= ((bBottomToolBar && bToolBar) ? DPISCALEY_S(22) : 0);
+
+	if (PluginConfig.m_LimitStaticAvatarHeight > 0)
+		m_iRealAvatarHeight = min(m_iRealAvatarHeight, PluginConfig.m_LimitStaticAvatarHeight);
+
+	if (M.GetByte(m_hContact, "dontscaleavatars", M.GetByte("dontscaleavatars", 0)))
+		m_iRealAvatarHeight = min(bminfo->bmHeight, m_iRealAvatarHeight);
+
+	double aspect = (bminfo->bmHeight != 0) ? (double)m_iRealAvatarHeight / (double)bminfo->bmHeight : 1.0;
+	double newWidth = (double)bminfo->bmWidth * aspect;
+	if (newWidth > (double)(rc.right) * 0.8)
+		newWidth = (double)(rc.right) * 0.8;
+	m_pic.cy = m_iRealAvatarHeight + 2;
+	m_pic.cx = (int)newWidth + 2;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::CloseTab()
+{
+	int iTabs = TabCtrl_GetItemCount(m_hwndParent);
+	if (iTabs == 1) {
+		SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 1);
+		return;
+	}
+
+	m_pContainer->m_iChilds--;
+	int i = GetTabIndexFromHWND(m_hwndParent, m_hwnd);
+
+	// after closing a tab, we need to activate the tab to the left side of
+	// the previously open tab.
+	// normally, this tab has the same index after the deletion of the formerly active tab
+	// unless, of course, we closed the last (rightmost) tab.
+	if (!m_pContainer->m_bDontSmartClose && iTabs > 1) {
+		if (i == iTabs - 1)
+			i--;
+		else
+			i++;
+		TabCtrl_SetCurSel(m_hwndParent, i);
+
+		m_pContainer->m_hwndActive = GetTabWindow(m_hwndParent, i);
+
+		RECT rc;
+		m_pContainer->QueryClientArea(rc);
+		SetWindowPos(m_pContainer->m_hwndActive, HWND_TOP, rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top), SWP_SHOWWINDOW);
+		ShowWindow(m_pContainer->m_hwndActive, SW_SHOW);
+		SetForegroundWindow(m_pContainer->m_hwndActive);
+		SetFocus(m_pContainer->m_hwndActive);
+	}
+
+	SendMessage(m_pContainer->m_hwnd, WM_SIZE, 0, 0);
+	DestroyWindow(m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// calculate the minimum required client height for the given message
+// window layout
+//
+// the container will use this in its WM_GETMINMAXINFO handler to set
+// minimum tracking height.
+
+void CMsgDialog::DetermineMinHeight()
+{
+	RECT rc;
+	LONG height = (m_pPanel.isActive() ? m_pPanel.getHeight() + 2 : 0);
+	if (!m_pContainer->cfg.flags.m_bHideToolbar)
+		height += DPISCALEY_S(24); // toolbar
+	GetClientRect(m_message.GetHwnd(), &rc);
+	height += rc.bottom; // input area
+	height += 40; // min space for log area and some padding
+
+	m_pContainer->m_uChildMinHeight = height;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert rich edit code to bbcode (if wanted). Otherwise, strip all RTF formatting
+// tags and return plain text
+
+static wchar_t tszRtfBreaks[] = L" \\\n\r";
+
+static void CreateColorMap(CMStringW &Text, int iCount, COLORREF *pSrc, int *pDst)
+{
+	const wchar_t *pszText = Text;
+	int iIndex = 1;
+
+	static const wchar_t *lpszFmt = L"\\red%[^ \x5b\\]\\green%[^ \x5b\\]\\blue%[^ \x5b;];";
+	wchar_t szRed[10], szGreen[10], szBlue[10];
+
+	const wchar_t *p1 = wcsstr(pszText, L"\\colortbl");
+	if (!p1)
+		return;
+
+	const wchar_t *pEnd = wcschr(p1, '}');
+
+	const wchar_t *p2 = wcsstr(p1, L"\\red");
+
+	for (int i = 0; i < iCount; i++)
+		pDst[i] = -1;
+
+	while (p2 && p2 < pEnd) {
+		if (swscanf(p2, lpszFmt, &szRed, &szGreen, &szBlue) > 0) {
+			for (int i = 0; i < iCount; i++) {
+				if (pSrc[i] == RGB(_wtoi(szRed), _wtoi(szGreen), _wtoi(szBlue)))
+					pDst[i] = iIndex;
+			}
+		}
+		iIndex++;
+		p1 = p2;
+		p1++;
+
+		p2 = wcsstr(p1, L"\\red");
+	}
+}
+
+static int RtfColorToIndex(int iNumColors, int *pIndex, int iCol)
+{
+	for (int i = 0; i < iNumColors; i++)
+		if (pIndex[i] == iCol)
+			return i;
+
+	return -1;
+}
+
+BOOL CMsgDialog::DoRtfToTags(CMStringW &pszText) const
+{
+	if (pszText.IsEmpty())
+		return FALSE;
+
+	// used to filter out attributes which are already set for the default message input area font
+	auto &lf = m_pContainer->m_theme.logFonts[MSGFONTID_MESSAGEAREA];
+
+	// create an index of colors in the module and map them to
+	// corresponding colors in the RTF color table
+	int iNumColors = Utils::rtf_clrs.getCount();
+	int *pIndex = (int *)_alloca(iNumColors * sizeof(int));
+	COLORREF *pColors = (COLORREF *)_alloca(iNumColors * sizeof(COLORREF));
+	for (int i = 0; i < iNumColors; i++)
+		pColors[i] = Utils::rtf_clrs[i].clr;
+	CreateColorMap(pszText, iNumColors, pColors, pIndex);
+
+	// scan the file for rtf commands and remove or parse them
+	int idx = pszText.Find(L"\\pard");
+	if (idx == -1) {
+		if ((idx = pszText.Find(L"\\ltrpar")) == -1)
+			return FALSE;
+		idx += 7;
+	}
+	else idx += 5;
+
+	MODULEINFO *mi = (isChat()) ? m_si->pMI : nullptr;
+
+	bool bInsideColor = false, bInsideUl = false;
+	CMStringW res;
+
+	// iterate through all characters, if rtf control character found then take action
+	for (const wchar_t *p = pszText.GetString() + idx; *p;) {
+		switch (*p) {
+		case '\\':
+			if (p[1] == '\\' || p[1] == '{' || p[1] == '}') { // escaped characters
+				res.AppendChar(p[1]);
+				p += 2; break;
+			}
+			if (p[1] == '~') { // non-breaking space
+				res.AppendChar(0xA0);
+				p += 2; break;
+			}
+
+			if (!wcsncmp(p, L"\\cf", 3)) { // foreground color
+				int iCol = _wtoi(p + 3);
+				int iInd = RtfColorToIndex(iNumColors, pIndex, iCol);
+
+				if (iCol > 0) {
+					if (isChat()) {
+						if (mi && mi->bColor) {
+							if (iInd >= 0) {
+								if (!(res.IsEmpty() && m_pContainer->m_theme.fontColors[MSGFONTID_MESSAGEAREA] == pColors[iInd]))
+									res.AppendFormat(L"%%c%u", iInd);
+							}
+							else if (!res.IsEmpty())
+								res.Append(L"%%C");
+						}
+					}
+					else res.AppendFormat((iInd >= 0) ? (bInsideColor ? L"[/color][color=%s]" : L"[color=%s]") : (bInsideColor ? L"[/color]" : L""), Utils::rtf_clrs[iInd].szName);
+				}
+
+				bInsideColor = iInd >= 0;
+			}
+			else if (!wcsncmp(p, L"\\highlight", 10)) { // background color
+				if (isChat()) {
+					if (mi && mi->bBkgColor) {
+						int iInd = RtfColorToIndex(iNumColors, pIndex, _wtoi(p + 10));
+						if (iInd >= 0) {
+							// if the entry field is empty & the color passed is the back color, skip it
+							if (!(res.IsEmpty() && m_pContainer->m_theme.inputbg == pColors[iInd]))
+								res.AppendFormat(L"%%f%u", iInd);
+						}
+						else if (!res.IsEmpty())
+							res.AppendFormat(L"%%F");
+					}
+				}
+			}
+			else if (!wcsncmp(p, L"\\line", 5)) { // soft line break;
+				res.AppendChar('\n');
+			}
+			else if (!wcsncmp(p, L"\\endash", 7)) {
+				res.AppendChar(0x2013);
+			}
+			else if (!wcsncmp(p, L"\\emdash", 7)) {
+				res.AppendChar(0x2014);
+			}
+			else if (!wcsncmp(p, L"\\bullet", 7)) {
+				res.AppendChar(0x2022);
+			}
+			else if (!wcsncmp(p, L"\\ldblquote", 10)) {
+				res.AppendChar(0x201C);
+			}
+			else if (!wcsncmp(p, L"\\rdblquote", 10)) {
+				res.AppendChar(0x201D);
+			}
+			else if (!wcsncmp(p, L"\\lquote", 7)) {
+				res.AppendChar(0x2018);
+			}
+			else if (!wcsncmp(p, L"\\rquote", 7)) {
+				res.AppendChar(0x2019);
+			}
+			else if (!wcsncmp(p, L"\\b", 2)) { //bold
+				if (isChat()) {
+					if (mi && mi->bBold)
+						res.Append((p[2] != '0') ? L"%b" : L"%B");
+				}
+				else {
+					if (!(lf.lfWeight == FW_BOLD)) // only allow bold if the font itself isn't a bold one, otherwise just strip it..
+						if (m_SendFormat)
+							res.Append((p[2] != '0') ? L"[b]" : L"[/b]");
+				}
+			}
+			else if (!wcsncmp(p, L"\\i", 2)) { // italics
+				if (isChat()) {
+					if (mi && mi->bItalics)
+						res.Append((p[2] != '0') ? L"%i" : L"%I");
+				}
+				else {
+					if (!lf.lfItalic && m_SendFormat)
+						res.Append((p[2] != '0') ? L"[i]" : L"[/i]");
+				}
+			}
+			else if (!wcsncmp(p, L"\\strike", 7)) { // strike-out
+				if (!lf.lfStrikeOut && m_SendFormat)
+					res.Append((p[7] != '0') ? L"[s]" : L"[/s]");
+			}
+			else if (!wcsncmp(p, L"\\ul", 3)) { // underlined
+				if (isChat()) {
+					if (mi && mi->bUnderline)
+						res.Append((p[3] != '0') ? L"%u" : L"%U");
+				}
+				else {
+					if (!lf.lfUnderline && m_SendFormat) {
+						if (p[3] == 0 || wcschr(tszRtfBreaks, p[3])) {
+							res.Append(L"[u]");
+							bInsideUl = true;
+						}
+						else if (!wcsncmp(p + 3, L"none", 4)) {
+							if (bInsideUl)
+								res.Append(L"[/u]");
+							bInsideUl = false;
+						}
+					}
+				}
+			}
+			else if (!wcsncmp(p, L"\\tab", 4)) { // tab
+				res.AppendChar('\t');
+			}
+			else if (p[1] == '\'') { // special character
+				if (p[2] != ' ' && p[2] != '\\') {
+					wchar_t tmp[10];
+
+					if (p[3] != ' ' && p[3] != '\\') {
+						wcsncpy(tmp, p + 2, 3);
+						tmp[3] = 0;
+					}
+					else {
+						wcsncpy(tmp, p + 2, 2);
+						tmp[2] = 0;
+					}
+
+					// convert string containing char in hex format to int.
+					wchar_t *stoppedHere;
+					res.AppendChar(wcstol(tmp, &stoppedHere, 16));
+				}
+			}
+
+			p++; // skip initial slash
+			p += wcscspn(p, tszRtfBreaks);
+			if (*p == ' ')
+				p++;
+			break;
+
+		case '{': // other RTF control characters
+		case '}':
+			p++;
+			break;
+
+		case '%': // double % for stupid chat engine
+			if (isChat())
+				res.Append(L"%%");
+			else
+				res.AppendChar(*p);
+			p++;
+			break;
+
+		default: // other text that should not be touched
+			res.AppendChar(*p++);
+			break;
+		}
+	}
+
+	if (bInsideColor && !isChat())
+		res.Append(L"[/color]");
+	if (bInsideUl)
+		res.Append(L"[/u]");
+
+	pszText = res;
+	return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::EnableSendButton(bool bMode) const
+{
+	SendDlgItemMessage(m_hwnd, IDOK, BUTTONSETASNORMAL, bMode, 0);
+	SendDlgItemMessage(m_hwnd, IDC_PIC, BUTTONSETASNORMAL, m_bEditNotesActive ? TRUE : (!bMode && m_iOpenJobs == 0) ? TRUE : FALSE, 0);
+
+	HWND hwndOK = GetDlgItem(GetParent(GetParent(m_hwnd)), IDOK);
+	if (IsWindow(hwndOK))
+		SendMessage(hwndOK, BUTTONSETASNORMAL, bMode, 0);
+}
+
+void CMsgDialog::EnableSending(bool bMode) const
+{
+	m_message.SendMsg(EM_SETREADONLY, !bMode, 0);
+	Utils::enableDlgControl(m_hwnd, IDC_CLIST, bMode);
+	EnableSendButton(bMode);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::FindFirstEvent()
+{
+	int historyMode = g_plugin.getByte(m_hContact, SRMSGSET_LOADHISTORY, -1);
+	if (historyMode == -1)
+		historyMode = (int)g_plugin.getByte(SRMSGSET_LOADHISTORY, SRMSGDEFSET_LOADHISTORY);
+
+	m_hDbEventFirst = db_event_firstUnread(m_hContact);
+
+	if (m_bActualHistory)
+		historyMode = LOADHISTORY_COUNT;
+
+	DBEVENTINFO dbei = {};
+	DB::ECPTR pCursor(DB::EventsRev(m_hContact, m_hDbEventFirst));
+
+	switch (historyMode) {
+	case LOADHISTORY_COUNT:
+		int i;
+
+		// ability to load only current session's history
+		if (m_bActualHistory)
+			i = m_cache->getSessionMsgCount();
+		else
+			i = g_plugin.getWord(SRMSGSET_LOADCOUNT, SRMSGDEFSET_LOADCOUNT);
+
+		for (; i > 0; i--) {
+			MEVENT hPrevEvent = pCursor.FetchNext();
+			if (hPrevEvent == 0)
+				break;
+
+			dbei.cbBlob = 0;
+			m_hDbEventFirst = hPrevEvent;
+			db_event_get(m_hDbEventFirst, &dbei);
+			if (!DbEventIsShown(&dbei))
+				i++;
+		}
+		break;
+
+	case LOADHISTORY_TIME:
+		if (m_hDbEventFirst == 0)
+			dbei.timestamp = time(0);
+		else
+			db_event_get(m_hDbEventFirst, &dbei);
+
+		uint32_t firstTime = dbei.timestamp - 60 * g_plugin.getWord(SRMSGSET_LOADTIME, SRMSGDEFSET_LOADTIME);
+
+		while (MEVENT hPrevEvent = pCursor.FetchNext()) {
+			dbei.cbBlob = 0;
+			db_event_get(hPrevEvent, &dbei);
+			if (dbei.timestamp < firstTime)
+				break;
+			m_hDbEventFirst = hPrevEvent;
+		}
+		break;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns != 0 when one of the installed keyboard layouts belongs to an rtl language
+// used to find out whether we need to configure the message input box for bidirectional mode
+
+int CMsgDialog::FindRTLLocale()
+{
+	int result = 0;
+
+	if (m_iHaveRTLLang == 0) {
+		HKL layouts[20];
+		memset(layouts, 0, sizeof(layouts));
+		GetKeyboardLayoutList(20, layouts);
+		for (int i = 0; i < 20 && layouts[i]; i++) {
+			uint16_t wCtype2[5];
+			LCID lcid = MAKELCID(LOWORD(layouts[i]), 0);
+			GetStringTypeA(lcid, CT_CTYPE2, "���", 3, wCtype2);
+			if (wCtype2[0] == C2_RIGHTTOLEFT || wCtype2[1] == C2_RIGHTTOLEFT || wCtype2[2] == C2_RIGHTTOLEFT)
+				result = 1;
+		}
+		m_iHaveRTLLang = (result ? 1 : -1);
+	}
+	else result = m_iHaveRTLLang == 1 ? 1 : 0;
+
+	return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::FlashOnClist(MEVENT hEvent, DBEVENTINFO *dbei)
+{
+	m_dwTickLastEvent = GetTickCount();
+
+	if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE) {
+		m_dwUnread++;
+		AddUnreadContact(m_hContact);
+	}
+
+	if (hEvent == 0)
+		return;
+
+	if (!g_plugin.bFlashOnClist)
+		return;
+
+	if ((GetForegroundWindow() != m_pContainer->m_hwnd || m_pContainer->m_hwndActive != m_hwnd) && !(dbei->flags & DBEF_SENT) && dbei->eventType == EVENTTYPE_MESSAGE && !m_bFlashClist) {
+		CLISTEVENT cle = {};
+		cle.hContact = m_hContact;
+		cle.hDbEvent = hEvent;
+		cle.hIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE);
+		cle.pszService = MS_MSG_READMESSAGE;
+		g_clistApi.pfnAddEvent(&cle);
+
+		m_bFlashClist = true;
+		m_hFlashingEvent = hEvent;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// flash a tab icon if mode = true, otherwise restore default icon
+// store flashing state into bState
+
+void CMsgDialog::FlashTab(bool bInvertMode)
+{
+	if (bInvertMode)
+		m_bTabFlash = !m_bTabFlash;
+
+	TCITEM item = {};
+	item.mask = TCIF_IMAGE;
+	TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+	if (m_pContainer->cfg.flags.m_bSideBar)
+		m_pContainer->m_pSideBar->updateSession(this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// this translates formatting tags into rtf sequences...
+// flags: loword = words only for simple  * /_ formatting
+//        hiword = bbcode support (strip bbcodes if 0)
+
+static wchar_t *w_bbcodes_begin[] = { L"[b]", L"[i]", L"[u]", L"[s]", L"[color=" };
+static wchar_t *w_bbcodes_end[] = { L"[/b]", L"[/i]", L"[/u]", L"[/s]", L"[/color]" };
+
+static wchar_t *formatting_strings_begin[] = { L"b1 ", L"i1 ", L"u1 ", L"s1 ", L"c1 " };
+static wchar_t *formatting_strings_end[] = { L"b0 ", L"i0 ", L"u0 ", L"s0 ", L"c0 " };
+
+void CMsgDialog::FormatRaw(CMStringW &msg, int flags, bool isSent)
+{
+	bool clr_was_added = false;
+	int beginmark = 0, endmark = 0, tempmark = 0;
+	int i, endindex;
+
+	if (m_dwFlags & MWF_LOG_BBCODE) {
+		beginmark = 0;
+		while (true) {
+			for (i = 0; i < _countof(w_bbcodes_begin); i++)
+				if ((tempmark = msg.Find(w_bbcodes_begin[i], 0)) != -1)
+					break;
+
+			if (i >= _countof(w_bbcodes_begin))
+				break;
+
+			beginmark = tempmark;
+			endindex = i;
+			endmark = msg.Find(w_bbcodes_end[i], beginmark);
+			if (endindex == 4) { // color
+				int closing = msg.Find(L"]", beginmark);
+				bool was_added = false;
+
+				if (closing == -1) {                      // must be an invalid [color=] tag w/o closing bracket
+					msg.SetAt(beginmark, ' ');
+					continue;
+				}
+
+				CMStringW colorname = msg.Mid(beginmark + 7, closing - beginmark - 7);
+search_again:
+				bool clr_found = false;
+				for (int ii = 0; ii < Utils::rtf_clrs.getCount(); ii++) {
+					auto &rtfc = Utils::rtf_clrs[ii];
+					if (!wcsicmp(colorname, rtfc.szName)) {
+						closing = beginmark + 7 + (int)mir_wstrlen(rtfc.szName);
+						if (endmark != -1) {
+							msg.Delete(endmark, 8);
+							msg.Insert(endmark, L"c0 ");
+						}
+						msg.Delete(beginmark, closing - beginmark + 1);
+
+						wchar_t szTemp[5];
+						msg.Insert(beginmark, L"cxxx ");
+						mir_snwprintf(szTemp, L"%02d", MSGDLGFONTCOUNT + 13 + ii);
+						msg.SetAt(beginmark + 3, szTemp[0]);
+						msg.SetAt(beginmark + 4, szTemp[1]);
+						clr_found = true;
+						if (was_added) {
+							wchar_t wszTemp[100];
+							mir_snwprintf(wszTemp, L"##col##%06u:%04u", endmark - closing, ii);
+							wszTemp[99] = 0;
+							msg.Insert(beginmark, wszTemp);
+						}
+						break;
+					}
+				}
+				if (!clr_found) {
+					int c_closing = colorname.Find(L"]");
+					if (c_closing == -1)
+						c_closing = colorname.GetLength();
+					const wchar_t *wszColname = colorname.c_str();
+					if (endmark != -1 && c_closing > 2 && c_closing <= 6 && iswalnum(colorname[0]) && iswalnum(colorname[c_closing - 1])) {
+						Utils::RTF_ColorAdd(wszColname);
+						if (!was_added) {
+							clr_was_added = was_added = true;
+							goto search_again;
+						}
+						else goto invalid_code;
+					}
+					else {
+invalid_code:
+						if (endmark != -1)
+							msg.Delete(endmark, 8);
+						if (closing != -1 && closing < endmark)
+							msg.Delete(beginmark, (closing - beginmark) + 1);
+						else
+							msg.SetAt(beginmark, ' ');
+					}
+				}
+				continue;
+			}
+
+			if (endmark != -1) {
+				msg.Delete(endmark, 4);
+				msg.Insert(endmark, formatting_strings_end[i]);
+			}
+			msg.Delete(beginmark, 3);
+			msg.Insert(beginmark, L" ");
+			msg.Insert(beginmark, formatting_strings_begin[i]);
+		}
+	}
+
+	if ((m_dwFlags & MWF_LOG_TEXTFORMAT) && msg.Find(L"://") == -1) {
+		while ((beginmark = msg.Find(L"*/_", beginmark)) != -1) {
+			wchar_t endmarker = msg[beginmark];
+			if (LOWORD(flags)) {
+				if (beginmark > 0 && !iswspace(msg[beginmark - 1]) && !iswpunct(msg[beginmark - 1])) {
+					beginmark++;
+					continue;
+				}
+
+				// search a corresponding endmarker which fulfills the criteria
+				INT_PTR mark = beginmark + 1;
+				while ((endmark = msg.Find(endmarker, mark)) != -1) {
+					if (iswpunct(msg[endmark + 1]) || iswspace(msg[endmark + 1]) || msg[endmark + 1] == 0 || wcschr(L"*/_", msg[endmark + 1]) != nullptr)
+						goto ok;
+					mark = endmark + 1;
+				}
+				break;
+			}
+			else {
+				if ((endmark = msg.Find(endmarker, beginmark + 1)) == -1)
+					break;
+			}
+ok:
+			if ((endmark - beginmark) < 2) {
+				beginmark++;
+				continue;
+			}
+			
+			int index = 0;
+			switch (endmarker) {
+			case '*':
+				index = 0;
+				break;
+			case '/':
+				index = 1;
+				break;
+			case '_':
+				index = 2;
+				break;
+			}
+
+			// check if the code enclosed by simple formatting tags is a valid smiley code and skip formatting if
+			// it really is one.
+			if (PluginConfig.g_SmileyAddAvail && (endmark > (beginmark + 1))) {
+				CMStringW smcode = msg.Mid(beginmark, (endmark - beginmark) + 1);
+
+				SMADD_BATCHPARSE2 smbp = {};
+				smbp.cbSize = sizeof(smbp);
+				smbp.Protocolname = m_cache->getActiveProto();
+				smbp.flag = SAFL_TCHAR | SAFL_PATH | (isSent ? SAFL_OUTGOING : 0);
+				smbp.str = (wchar_t*)smcode.c_str();
+				smbp.hContact = m_hContact;
+
+				SMADD_BATCHPARSERES *smbpr = (SMADD_BATCHPARSERES *)CallService(MS_SMILEYADD_BATCHPARSE, 0, (LPARAM)&smbp);
+				if (smbpr) {
+					CallService(MS_SMILEYADD_BATCHFREE, 0, (LPARAM)smbpr);
+					beginmark = endmark + 1;
+					continue;
+				}
+			}
+			msg.Delete(endmark, 1);
+			msg.Insert(endmark, formatting_strings_end[index]);
+			msg.Delete(beginmark, 1);
+			msg.Insert(beginmark, formatting_strings_begin[index]);
+		}
+	}
+
+	m_bClrAdded = clr_was_added;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// format the title bar string for IM chat sessions using placeholders.
+// the caller must mir_free() the returned string
+
+static wchar_t* Trunc500(wchar_t *str)
+{
+	if (mir_wstrlen(str) > 500)
+		str[500] = 0;
+	return str;
+}
+
+bool CMsgDialog::FormatTitleBar(const wchar_t *szFormat, CMStringW &dest)
+{
+	for (const wchar_t *src = szFormat; *src; src++) {
+		if (*src != '%') {
+			dest.AppendChar(*src);
+			continue;
+		}
+
+		switch (*++src) {
+		case 'n':
+			dest.Append(m_cache->getNick());
+			break;
+
+		case 'p':
+		case 'a':
+			dest.Append(m_cache->getRealAccount());
+			break;
+
+		case 's':
+			dest.Append(m_wszStatus);
+			break;
+
+		case 'u':
+			dest.Append(m_cache->getUIN());
+			break;
+
+		case 'c':
+			dest.Append(!mir_wstrcmp(m_pContainer->m_wszName, L"default") ? TranslateT("Default container") : m_pContainer->m_wszName);
+			break;
+
+		case 'o':
+			{
+				const char *szProto = m_cache->getActiveProto();
+				if (szProto)
+					dest.Append(_A2T(szProto));
+			}
+			break;
+
+		case 'x':
+			{
+				uint8_t xStatus = m_cache->getXStatusId();
+				if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+					ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
+					dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
+				}
+			}
+			break;
+
+		case 'm':
+			{
+				uint8_t xStatus = m_cache->getXStatusId();
+				if (m_wStatus != ID_STATUS_OFFLINE && xStatus > 0 && xStatus <= 31) {
+					ptrW szXStatus(db_get_wsa(m_hContact, m_szProto, "XStatusName"));
+					dest.Append((szXStatus != nullptr) ? Trunc500(szXStatus) : xStatusDescr[xStatus - 1]);
+				}
+				else dest.Append(m_wszStatus[0] ? m_wszStatus : L"(undef)");
+			}
+			break;
+
+			// status message (%T will skip the "No status message" for empty messages)
+		case 't':
+		case 'T':
+			{
+				ptrW tszStatus(m_cache->getNormalizedStatusMsg(m_cache->getStatusMsg(), true));
+				if (tszStatus)
+					dest.Append(tszStatus);
+				else if (*src == 't')
+					dest.Append(TranslateT("No status message"));
+			}
+			break;
+
+		case 'g':
+			{
+				ptrW tszGroup(Clist_GetGroup(m_hContact));
+				if (tszGroup != nullptr)
+					dest.Append(tszGroup);
+			}
+			break;
+
+		case 0: // wrongly formed format string
+			return true;
+		}
+	}
+
+	return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve the visiblity of the avatar window, depending on the global setting
+// and local mode
+
+bool CMsgDialog::GetAvatarVisibility()
+{
+	uint8_t bAvatarMode = m_pContainer->cfg.avatarMode;
+	uint8_t bOwnAvatarMode = m_pContainer->cfg.ownAvatarMode;
+	char hideOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+
+	// infopanel visible, consider own avatar display
+	m_bShowAvatar = false;
+	if (m_si)
+		bAvatarMode = 1;
+
+	if (m_pPanel.isActive() && bAvatarMode != 3) {
+		if (!bOwnAvatarMode) {
+			m_bShowAvatar = (m_hOwnPic && m_hOwnPic != PluginConfig.g_hbmUnknown);
+			if (!m_hwndContactPic)
+				m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
+		}
+
+		switch (bAvatarMode) {
+		case 2:
+			m_bShowInfoAvatar = false;
+			break;
+		case 0:
+			m_bShowInfoAvatar = true;
+		case 1:
+			HBITMAP hbm = ((m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr);
+			if (hbm == nullptr && !bAvatarMode) {
+				m_bShowInfoAvatar = false;
+				break;
+			}
+
+			if (!m_hwndPanelPic) {
+				m_hwndPanelPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, m_hwndPanelPicParent, (HMENU)7000, nullptr, nullptr);
+				if (m_hwndPanelPic)
+					SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, TRUE);
+			}
+
+			if (bAvatarMode != 0)
+				m_bShowInfoAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
+			break;
+		}
+
+		if (m_bShowInfoAvatar)
+			m_bShowInfoAvatar = hideOverride == 0 ? false : m_bShowInfoAvatar;
+		else
+			m_bShowInfoAvatar = hideOverride == 1 ? true : m_bShowInfoAvatar;
+
+		Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
+		SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
+	}
+	else {
+		m_bShowInfoAvatar = false;
+
+		switch (bAvatarMode) {
+		case 0: // globally on
+			m_bShowAvatar = true;
+		LBL_Check:
+			if (!m_hwndContactPic)
+				m_hwndContactPic = CreateWindowEx(WS_EX_TOPMOST, AVATAR_CONTROL_CLASS, L"", WS_VISIBLE | WS_CHILD, 1, 1, 1, 1, GetDlgItem(m_hwnd, IDC_CONTACTPIC), (HMENU)nullptr, nullptr, nullptr);
+			break;
+		case 2: // globally OFF
+			m_bShowAvatar = false;
+			break;
+		case 3: // on, if present
+		case 1:
+			HBITMAP hbm = (m_ace && !(m_ace->dwFlags & AVS_HIDEONCLIST)) ? m_ace->hbmPic : nullptr;
+			m_bShowAvatar = (hbm && hbm != PluginConfig.g_hbmUnknown);
+			goto LBL_Check;
+		}
+
+		if (m_bShowAvatar)
+			m_bShowAvatar = hideOverride == 0 ? 0 : m_bShowAvatar;
+		else
+			m_bShowAvatar = hideOverride == 1 ? 1 : m_bShowAvatar;
+
+		// reloads avatars
+		if (m_hwndPanelPic) { // shows contact or user picture, depending on panel visibility
+			SendMessage(m_hwndContactPic, AVATAR_SETPROTOCOL, 0, (LPARAM)m_cache->getActiveProto());
+			Utils::setAvatarContact(m_hwndPanelPic, m_hContact);
+		}
+		else Utils::setAvatarContact(m_hwndContactPic, m_hContact);
+	}
+	return m_bShowAvatar;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::GetClientIcon()
+{
+	if (m_hClientIcon)
+		DestroyIcon(m_hClientIcon);
+
+	m_hClientIcon = nullptr;
+	if (ServiceExists(MS_FP_GETCLIENTICONT)) {
+		ptrW tszMirver(db_get_wsa(m_cache->getActiveContact(), m_cache->getActiveProto(), "MirVer"));
+		if (tszMirver)
+			m_hClientIcon = Finger_GetClientIcon(tszMirver, 1);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON CMsgDialog::GetMyContactIcon(const CMOption<bool> *opt)
+{
+	int bUseMeta = (opt == nullptr) ? false : M.GetByte(opt->GetDBSettingName(), mir_strcmp(opt->GetDBSettingName(), "MetaiconTab") == 0);
+	if (bUseMeta)
+		return Skin_LoadProtoIcon(m_cache->getProto(), m_cache->getStatus());
+	return Skin_LoadProtoIcon(m_cache->getActiveProto(), m_cache->getActiveStatus());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::GetMyNick()
+{
+	ptrW tszNick(Contact::GetInfo(CNF_CUSTOMNICK, 0, m_cache->getActiveProto()));
+	if (tszNick == nullptr)
+		tszNick = Contact::GetInfo(CNF_NICK, 0, m_cache->getActiveProto());
+	if (tszNick != nullptr) {
+		if (mir_wstrlen(tszNick) == 0 || !mir_wstrcmp(tszNick, TranslateT("'(Unknown contact)'")))
+			wcsncpy_s(m_wszMyNickname, (m_myUin[0] ? m_myUin : TranslateT("'(Unknown contact)'")), _TRUNCATE);
+		else
+			wcsncpy_s(m_wszMyNickname, tszNick, _TRUNCATE);
+	}
+	else wcsncpy_s(m_wszMyNickname, L"<undef>", _TRUNCATE); // same here
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// retrieve both buddys and my own UIN for a message session and store them in the message 
+// window *dat respects metacontacts and uses the current protocol if the contact is a MC
+
+void CMsgDialog::GetMYUIN()
+{
+	ptrW uid(Contact::GetInfo(CNF_DISPLAYUID, 0, m_cache->getActiveProto()));
+	if (uid != nullptr)
+		wcsncpy_s(m_myUin, uid, _TRUNCATE);
+	else
+		m_myUin[0] = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// returns the status of Send button
+
+LRESULT CMsgDialog::GetSendButtonState()
+{
+	return m_btnOk.SendMsg(BUTTONGETSTATEID, TRUE, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// reads send format and configures the toolbar buttons
+// if mode == 0, int only configures the buttons and does not change send format
+
+void CMsgDialog::GetSendFormat()
+{
+	m_SendFormat = M.GetDword(m_hContact, "sendformat", g_plugin.bSendFormat);
+	if (m_SendFormat == -1)          // per contact override to disable it..
+		m_SendFormat = 0;
+	else if (m_SendFormat == 0)
+		m_SendFormat = g_plugin.bSendFormat;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+HICON CMsgDialog::GetXStatusIcon() const
+{
+	uint8_t xStatus = m_cache->getXStatusId();
+	if (xStatus == 0)
+		return nullptr;
+
+	if (!ProtoServiceExists(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON))
+		return nullptr;
+
+	return (HICON)(CallProtoService(m_cache->getActiveProto(), PS_GETCUSTOMSTATUSICON, xStatus, 0));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// paste contents of the clipboard into the message input area and send it immediately
+
+void CMsgDialog::HandlePasteAndSend()
+{
+	// is feature disabled?
+	if (!g_plugin.bPasteAndSend) {
+		ActivateTooltip(IDC_SRMM_MESSAGE, TranslateT("The 'paste and send' feature is disabled. You can enable it on the 'General' options page in the 'Sending messages' section"));
+		return;
+	}
+
+	m_message.SendMsg(EM_PASTESPECIAL, CF_UNICODETEXT, 0);
+	if (GetWindowTextLength(m_message.GetHwnd()) > 0)
+		SendMessage(m_hwnd, WM_COMMAND, IDOK, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// convert the avatar bitmap to icon format so that it can be used on the task bar
+// tries to keep correct aspect ratio of the avatar image
+//
+// @param dat: _MessageWindowData* pointer to the window data
+// @return HICON: the icon handle
+
+HICON CMsgDialog::IconFromAvatar() const
+{
+	if (!ServiceExists(MS_AV_GETAVATARBITMAP))
+		return nullptr;
+
+	AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, m_hContact, 0);
+	if (ace == nullptr || ace->hbmPic == nullptr)
+		return nullptr;
+
+	LONG lIconSize = Win7Taskbar->getIconSize();
+	double dNewWidth, dNewHeight;
+	Utils::scaleAvatarHeightLimited(ace->hbmPic, dNewWidth, dNewHeight, lIconSize);
+
+	// resize picture to fit it on the task bar, use an image list for converting it to
+	// 32bpp icon format. hTaskbarIcon will cache it until avatar is changed
+	HBITMAP hbmResized = ::Image_Resize(ace->hbmPic, RESIZEBITMAP_STRETCH, dNewWidth, dNewHeight);
+	HIMAGELIST hIml_c = ::ImageList_Create(lIconSize, lIconSize, ILC_COLOR32 | ILC_MASK, 1, 0);
+
+	RECT rc = { 0, 0, lIconSize, lIconSize };
+
+	HDC hdc = ::GetDC(m_pContainer->m_hwnd);
+	HDC dc = ::CreateCompatibleDC(hdc);
+	HDC dcResized = ::CreateCompatibleDC(hdc);
+
+	ReleaseDC(m_pContainer->m_hwnd, hdc);
+
+	HBITMAP hbmNew = CSkin::CreateAeroCompatibleBitmap(rc, dc);
+	HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmNew));
+	HBITMAP hbmOldResized = reinterpret_cast<HBITMAP>(::SelectObject(dcResized, hbmResized));
+
+	LONG ix = (lIconSize - (LONG)dNewWidth) / 2;
+	LONG iy = (lIconSize - (LONG)dNewHeight) / 2;
+	CSkin::m_default_bf.SourceConstantAlpha = M.GetByte("taskBarIconAlpha", 255);
+	GdiAlphaBlend(dc, ix, iy, (LONG)dNewWidth, (LONG)dNewHeight, dcResized, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
+
+	CSkin::m_default_bf.SourceConstantAlpha = 255;
+	::SelectObject(dc, hbmOld);
+	::ImageList_Add(hIml_c, hbmNew, nullptr);
+	::DeleteObject(hbmNew);
+	::DeleteDC(dc);
+
+	::SelectObject(dcResized, hbmOldResized);
+	if (hbmResized != ace->hbmPic)
+		::DeleteObject(hbmResized);
+	::DeleteDC(dcResized);
+	HICON hIcon = ::ImageList_GetIcon(hIml_c, 0, ILD_NORMAL);
+	::ImageList_RemoveAll(hIml_c);
+	::ImageList_Destroy(hIml_c);
+	return hIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// is window active or not?
+
+bool CMsgDialog::IsActive() const
+{
+	return m_pContainer->IsActive() && m_pContainer->m_hwndActive == m_hwnd;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// read keyboard state and return the state of the modifier keys
+
+void CMsgDialog::KbdState(bool &isShift, bool &isControl, bool &isAlt)
+{
+	GetKeyboardState(kstate);
+	isShift = (kstate[VK_SHIFT] & 0x80) != 0;
+	isControl = (kstate[VK_CONTROL] & 0x80) != 0;
+	isAlt = (kstate[VK_MENU] & 0x80) != 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LimitMessageText(int iLen)
+{
+	if (this != nullptr)
+		m_message.SendMsg(EM_EXLIMITTEXT, 0, iLen);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadContactAvatar()
+{
+	m_ace = Utils::loadAvatarFromAVS(m_bIsMeta ? db_mc_getSrmmSub(m_hContact) : m_hContact);
+
+	BITMAP bm;
+	if (m_ace && m_ace->hbmPic)
+		GetObject(m_ace->hbmPic, sizeof(bm), &bm);
+	else if (m_ace == nullptr)
+		GetObject(PluginConfig.g_hbmUnknown, sizeof(bm), &bm);
+	else
+		return;
+
+	AdjustBottomAvatarDisplay();
+	CalcDynamicAvatarSize(&bm);
+
+	if (!m_pPanel.isActive() || m_pContainer->cfg.avatarMode == 3) {
+		m_iRealAvatarHeight = 0;
+		PostMessage(m_hwnd, WM_SIZE, 0, 0);
+	}
+	else if (m_pPanel.isActive())
+		GetAvatarVisibility();
+
+	if (m_pWnd != nullptr)
+		m_pWnd->verifyDwmState();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadOwnAvatar()
+{
+	if (ServiceExists(MS_AV_GETMYAVATAR))
+		m_ownAce = (AVATARCACHEENTRY *)CallService(MS_AV_GETMYAVATAR, 0, (LPARAM)(m_cache->getActiveProto()));
+	else
+		m_ownAce = nullptr;
+
+	if (m_ownAce)
+		m_hOwnPic = m_ownAce->hbmPic;
+	else
+		m_hOwnPic = PluginConfig.g_hbmUnknown;
+
+	if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
+		BITMAP bm;
+
+		m_iRealAvatarHeight = 0;
+		AdjustBottomAvatarDisplay();
+		GetObject(m_hOwnPic, sizeof(bm), &bm);
+		CalcDynamicAvatarSize(&bm);
+		Resize();
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSettings()
+{
+	m_clrInputBG = m_pContainer->m_theme.inputbg;
+	LoadMsgDlgFont(FONTSECTION_IM, MSGFONTID_MESSAGEAREA, nullptr, &m_clrInputFG);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LoadSplitter()
+{
+	if (m_bIsAutosizingInput) {
+		m_iSplitterY = (m_pContainer->cfg.flags.m_bBottomToolbar) ? DPISCALEY_S(46 + 22) : DPISCALEY_S(46);
+
+		if (CSkin::m_skinEnabled && !SkinItems[ID_EXTBKINPUTAREA].IGNORED)
+			m_iSplitterY += (SkinItems[ID_EXTBKINPUTAREA].MARGIN_BOTTOM + SkinItems[ID_EXTBKINPUTAREA].MARGIN_TOP - 2);
+		return;
+	}
+
+	if (!m_bSplitterOverride) {
+		if (!m_pContainer->cfg.fPrivate)
+			m_iSplitterY = (int)M.GetDword("splitsplity", 60);
+		else
+			m_iSplitterY = m_pContainer->cfg.iSplitterY;
+	}
+	else m_iSplitterY = (int)M.GetDword(m_hContact, "splitsplity", M.GetDword("splitsplity", 60));
+
+	if (m_iSplitterY < MINSPLITTERY)
+		m_iSplitterY = 150;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::LogEvent(DBEVENTINFO &dbei)
+{
+	if (m_iLogMode != WANT_BUILTIN_LOG) {
+		dbei.flags |= DBEF_TEMPORARY;
+
+		MEVENT hDbEvent = db_event_add(m_hContact, &dbei);
+		if (hDbEvent) {
+			m_pLog->LogEvents(hDbEvent, 1, true);
+			db_event_delete(hDbEvent);
+		}
+	}
+	else LOG()->LogEvents(0, 1, true, &dbei);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// draw various elements of the message window, like avatar(s), info panel fields
+// and the color formatting menu
+
+int CMsgDialog::MsgWindowDrawHandler(DRAWITEMSTRUCT *dis)
+{
+	if ((dis->hwndItem == GetDlgItem(m_hwnd, IDC_CONTACTPIC) && m_bShowAvatar) || (dis->hwndItem == m_hwnd && m_pPanel.isActive())) {
+		HBITMAP hbmAvatar = m_ace ? m_ace->hbmPic : PluginConfig.g_hbmUnknown;
+		if (hbmAvatar == nullptr)
+			return TRUE;
+
+		int top, cx, cy;
+		RECT rcClient, rcFrame;
+		bool bPanelPic = (dis->hwndItem == m_hwnd);
+		if (bPanelPic && !m_bShowInfoAvatar)
+			return TRUE;
+
+		RECT rc;
+		GetClientRect(m_hwnd, &rc);
+		if (bPanelPic) {
+			rcClient = dis->rcItem;
+			cx = (rcClient.right - rcClient.left);
+			cy = (rcClient.bottom - rcClient.top) + 1;
+		}
+		else {
+			GetClientRect(dis->hwndItem, &rcClient);
+			cx = rcClient.right;
+			cy = rcClient.bottom;
+		}
+
+		if (cx < 5 || cy < 5)
+			return TRUE;
+
+		HDC hdcDraw = CreateCompatibleDC(dis->hDC);
+		HBITMAP hbmDraw = CreateCompatibleBitmap(dis->hDC, cx, cy);
+		HBITMAP hbmOld = (HBITMAP)SelectObject(hdcDraw, hbmDraw);
+
+		bool bAero = M.isAero();
+
+		HRGN clipRgn = nullptr;
+		HBRUSH hOldBrush = (HBRUSH)SelectObject(hdcDraw, bAero ? (HBRUSH)GetStockObject(HOLLOW_BRUSH) : GetSysColorBrush(COLOR_3DFACE));
+		rcFrame = rcClient;
+
+		if (!bPanelPic) {
+			top = (cy - m_pic.cy) / 2;
+			RECT rcEdge = { 0, top, m_pic.cx, top + m_pic.cy };
+			if (CSkin::m_skinEnabled)
+				CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, hdcDraw);
+			else if (PluginConfig.m_fillColor) {
+				HBRUSH br = CreateSolidBrush(PluginConfig.m_fillColor);
+				FillRect(hdcDraw, &rcFrame, br);
+				DeleteObject(br);
+			}
+			else if (bAero && CSkin::m_pCurrentAeroEffect) {
+				COLORREF clr = PluginConfig.m_tbBackgroundHigh ? PluginConfig.m_tbBackgroundHigh :
+					(CSkin::m_pCurrentAeroEffect ? CSkin::m_pCurrentAeroEffect->m_clrToolbar : 0xf0f0f0);
+
+				HBRUSH br = CreateSolidBrush(clr);
+				FillRect(hdcDraw, &rcFrame, br);
+				DeleteObject(br);
+			}
+			else FillRect(hdcDraw, &rcFrame, GetSysColorBrush(COLOR_3DFACE));
+
+			HPEN hPenBorder = CreatePen(PS_SOLID, 1, CSkin::m_avatarBorderClr);
+			HPEN hPenOld = (HPEN)SelectObject(hdcDraw, hPenBorder);
+
+			if (CSkin::m_bAvatarBorderType == 1)
+				Rectangle(hdcDraw, rcEdge.left, rcEdge.top, rcEdge.right, rcEdge.bottom);
+			else if (CSkin::m_bAvatarBorderType == 2) {
+				clipRgn = CreateRoundRectRgn(rcEdge.left, rcEdge.top, rcEdge.right + 1, rcEdge.bottom + 1, 6, 6);
+				SelectClipRgn(hdcDraw, clipRgn);
+
+				HBRUSH hbr = CreateSolidBrush(CSkin::m_avatarBorderClr);
+				FrameRgn(hdcDraw, clipRgn, hbr, 1, 1);
+				DeleteObject(hbr);
+				DeleteObject(clipRgn);
+			}
+
+			SelectObject(hdcDraw, hPenOld);
+			DeleteObject(hPenBorder);
+		}
+
+		if (bPanelPic) {
+			bool bBorder = (CSkin::m_bAvatarBorderType ? true : false);
+
+			int border_off = bBorder ? 1 : 0;
+			int iMaxHeight = m_iPanelAvatarY - (bBorder ? 2 : 0);
+			int iMaxWidth = m_iPanelAvatarX - (bBorder ? 2 : 0);
+
+			rcFrame.left = rcFrame.top = 0;
+			rcFrame.right = (rcClient.right - rcClient.left);
+			rcFrame.bottom = (rcClient.bottom - rcClient.top);
+
+			rcFrame.left = rcFrame.right - (LONG)m_iPanelAvatarX;
+			rcFrame.bottom = (LONG)m_iPanelAvatarY;
+
+			int height_off = (cy - iMaxHeight - (bBorder ? 2 : 0)) / 2;
+			rcFrame.top += height_off;
+			rcFrame.bottom += height_off;
+
+			SendMessage(m_hwndPanelPic, AVATAR_SETAEROCOMPATDRAWING, 0, bAero ? TRUE : FALSE);
+			SetWindowPos(m_hwndPanelPic, HWND_TOP, rcFrame.left + border_off, rcFrame.top + border_off,
+				iMaxWidth, iMaxHeight, SWP_SHOWWINDOW | SWP_ASYNCWINDOWPOS | SWP_DEFERERASE | SWP_NOSENDCHANGING);
+		}
+
+		SelectObject(hdcDraw, hOldBrush);
+		if (!bPanelPic)
+			BitBlt(dis->hDC, 0, 0, cx, cy, hdcDraw, 0, 0, SRCCOPY);
+		SelectObject(hdcDraw, hbmOld);
+		DeleteObject(hbmDraw);
+		DeleteDC(hdcDraw);
+		return TRUE;
+	}
+
+	if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICTEXT) || dis->hwndItem == GetDlgItem(m_hwnd, IDC_LOGFROZENTEXT)) {
+		wchar_t szWindowText[256];
+		if (CSkin::m_skinEnabled) {
+			SetTextColor(dis->hDC, CSkin::m_DefaultFontColor);
+			CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
+		}
+		else {
+			SetTextColor(dis->hDC, GetSysColor(COLOR_BTNTEXT));
+			CSkin::FillBack(dis->hDC, &dis->rcItem);
+		}
+		GetWindowText(dis->hwndItem, szWindowText, _countof(szWindowText));
+		szWindowText[255] = 0;
+		SetBkMode(dis->hDC, TRANSPARENT);
+		DrawText(dis->hDC, szWindowText, -1, &dis->rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOCLIP | DT_END_ELLIPSIS);
+		return TRUE;
+	}
+
+	if (dis->hwndItem == GetDlgItem(m_hwnd, IDC_STATICERRORICON)) {
+		if (CSkin::m_skinEnabled)
+			CSkin::SkinDrawBG(dis->hwndItem, m_pContainer->m_hwnd, m_pContainer, &dis->rcItem, dis->hDC);
+		else
+			CSkin::FillBack(dis->hDC, &dis->rcItem);
+		DrawIconEx(dis->hDC, (dis->rcItem.right - dis->rcItem.left) / 2 - 8, (dis->rcItem.bottom - dis->rcItem.top) / 2 - 8,
+			PluginConfig.g_iconErr, 16, 16, 0, nullptr, DI_NORMAL);
+		return TRUE;
+	}
+
+	if (dis->CtlType == ODT_MENU && m_pPanel.isHovered()) {
+		DrawMenuItem(dis, (HICON)dis->itemData, 0);
+		return TRUE;
+	}
+
+	return Menu_DrawItem((LPARAM)dis);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMsgDialog::MsgWindowUpdateMenu(HMENU submenu, int menuID)
+{
+	bool bInfoPanel = m_pPanel.isActive();
+
+	if (menuID == MENU_TABCONTEXT) {
+		EnableMenuItem(submenu, ID_TABMENU_LEAVECHATROOM, (isChat() && ProtoServiceExists(m_szProto, PS_LEAVECHAT)) ? MF_ENABLED : MF_GRAYED);
+		EnableMenuItem(submenu, ID_TABMENU_ATTACHTOCONTAINER, (M.GetByte("useclistgroups", 0) || M.GetByte("singlewinmode", 0)) ? MF_GRAYED : MF_ENABLED);
+		EnableMenuItem(submenu, ID_TABMENU_CLEARSAVEDTABPOSITION, (M.GetDword(m_hContact, "tabindex", -1) != -1) ? MF_ENABLED : MF_GRAYED);
+	}
+	else if (menuID == MENU_PICMENU) {
+		wchar_t *szText = nullptr;
+		char  avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+		HMENU visMenu = GetSubMenu(submenu, 0);
+		BOOL picValid = bInfoPanel ? (m_hOwnPic != nullptr) : (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown);
+
+		MENUITEMINFO mii = { 0 };
+		mii.cbSize = sizeof(mii);
+		mii.fMask = MIIM_STRING;
+
+		EnableMenuItem(submenu, ID_PICMENU_SAVETHISPICTUREAS, picValid ? MF_ENABLED : MF_GRAYED);
+
+		CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
+
+		CheckMenuItem(submenu, ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH, PluginConfig.m_bAlwaysFullToolbarWidth ? MF_CHECKED : MF_UNCHECKED);
+		if (!bInfoPanel) {
+			EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
+			szText = TranslateT("Contact picture settings...");
+			EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_ENABLED);
+		}
+		else {
+			EnableMenuItem(submenu, 0, MF_BYPOSITION | MF_GRAYED);
+			EnableMenuItem(submenu, ID_PICMENU_SETTINGS, (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0)) ? MF_ENABLED : MF_GRAYED);
+			szText = TranslateT("Set your avatar...");
+		}
+		mii.dwTypeData = szText;
+		mii.cch = (int)mir_wstrlen(szText) + 1;
+		SetMenuItemInfo(submenu, ID_PICMENU_SETTINGS, FALSE, &mii);
+	}
+	else if (menuID == MENU_PANELPICMENU) {
+		HMENU visMenu = GetSubMenu(submenu, 0);
+		char  avOverride = (char)M.GetByte(m_hContact, "hideavatar", -1);
+
+		CheckMenuItem(visMenu, ID_VISIBILITY_DEFAULT, avOverride == -1 ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(visMenu, ID_VISIBILITY_HIDDENFORTHISCONTACT, avOverride == 0 ? MF_CHECKED : MF_UNCHECKED);
+		CheckMenuItem(visMenu, ID_VISIBILITY_VISIBLEFORTHISCONTACT, avOverride == 1 ? MF_CHECKED : MF_UNCHECKED);
+
+		EnableMenuItem(submenu, ID_PICMENU_SETTINGS, ServiceExists(MS_AV_GETAVATARBITMAP) ? MF_ENABLED : MF_GRAYED);
+		EnableMenuItem(submenu, ID_PANELPICMENU_SAVETHISPICTUREAS, (m_ace && m_ace->hbmPic && m_ace->hbmPic != PluginConfig.g_hbmUnknown) ? MF_ENABLED : MF_GRAYED);
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update state of the container - this is called whenever a tab becomes active, no matter how and
+// deals with various things like updating the title bar, removing flashing icons, updating the
+// session list, switching the keyboard layout (autolocale active)  and the general container status.
+//
+// it protects itself from being called more than once per session activation and is valid for
+// normal IM sessions *only*. Group chat sessions have their own activation handler (see chat/window.c)
+
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMsgDialog::MsgWindowMenuHandler(int selection, int menuId)
+{
+	if (menuId == MENU_PICMENU || menuId == MENU_PANELPICMENU || menuId == MENU_TABCONTEXT) {
+		switch (selection) {
+		case ID_TABMENU_ATTACHTOCONTAINER:
+			SelectContainer();
+			return 1;
+		case ID_TABMENU_CONTAINEROPTIONS:
+			m_pContainer->OptionsDialog();
+			return 1;
+		case ID_TABMENU_CLOSECONTAINER:
+			SendMessage(m_pContainer->m_hwnd, WM_CLOSE, 0, 0);
+			return 1;
+		case ID_TABMENU_CLOSETAB:
+			PostMessage(m_hwnd, WM_CLOSE, 1, 0);
+			return 1;
+		case ID_TABMENU_SAVETABPOSITION:
+			db_set_dw(m_hContact, SRMSGMOD_T, "tabindex", m_iTabID * 100);
+			break;
+		case ID_TABMENU_CLEARSAVEDTABPOSITION:
+			db_unset(m_hContact, SRMSGMOD_T, "tabindex");
+			break;
+		case ID_TABMENU_LEAVECHATROOM:
+			if (isChat()) {
+				char *szProto = Proto_GetBaseAccountName(m_hContact);
+				if (szProto)
+					CallProtoService(szProto, PS_LEAVECHAT, m_hContact, 0);
+			}
+			return 1;
+
+		case ID_VISIBILITY_DEFAULT:
+		case ID_VISIBILITY_HIDDENFORTHISCONTACT:
+		case ID_VISIBILITY_VISIBLEFORTHISCONTACT:
+			{
+				uint8_t avOverrideMode;
+				if (selection == ID_VISIBILITY_DEFAULT)
+					avOverrideMode = -1;
+				else if (selection == ID_VISIBILITY_VISIBLEFORTHISCONTACT)
+					avOverrideMode = 1;
+				else
+					avOverrideMode = 0;
+				db_set_b(m_hContact, SRMSGMOD_T, "hideavatar", avOverrideMode);
+			}
+
+			ShowPicture(false);
+			Resize();
+			DM_ScrollToBottom(0, 1);
+			return 1;
+
+		case ID_PICMENU_ALWAYSKEEPTHEBUTTONBARATFULLWIDTH:
+			PluginConfig.m_bAlwaysFullToolbarWidth = !PluginConfig.m_bAlwaysFullToolbarWidth;
+			db_set_b(0, SRMSGMOD_T, "alwaysfulltoolbar", (uint8_t)PluginConfig.m_bAlwaysFullToolbarWidth);
+			Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 1);
+			break;
+
+		case ID_PICMENU_SAVETHISPICTUREAS:
+			if (m_pPanel.isActive())
+				SaveAvatarToFile(m_hOwnPic, 1);
+			else if (m_ace)
+				SaveAvatarToFile(m_ace->hbmPic, 0);
+			break;
+
+		case ID_PANELPICMENU_SAVETHISPICTUREAS:
+			if (m_ace)
+				SaveAvatarToFile(m_ace->hbmPic, 0);
+			break;
+
+		case ID_PICMENU_SETTINGS:
+			if (menuId == MENU_PICMENU) {
+				if (m_pPanel.isActive()) {
+					if (ServiceExists(MS_AV_SETMYAVATARW) && CallService(MS_AV_CANSETMYAVATAR, (WPARAM)(m_cache->getActiveProto()), 0))
+						CallService(MS_AV_SETMYAVATARW, (WPARAM)(m_cache->getActiveProto()), 0);
+					return TRUE;
+				}
+			}
+			CallService(MS_AV_CONTACTOPTIONS, m_hContact, (LPARAM)m_hwnd);
+			return 1;
+		}
+	}
+	else if (menuId == MENU_LOGMENU) {
+		switch (selection) {
+		case ID_MESSAGELOGSETTINGS_GLOBAL:
+			g_plugin.openOptions(nullptr, L"Message sessions", L"Message log");
+			return 1;
+
+		case ID_MESSAGELOGSETTINGS_FORTHISCONTACT:
+			CallService(MS_TABMSG_SETUSERPREFS, m_hContact, 0);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::NotifyDeliveryFailure() const
+{
+	if (!g_plugin.bErrorPopup || !Popup_Enabled())
+		return;
+
+	POPUPDATAW ppd = {};
+	ppd.lchContact = m_hContact;
+	ppd.PluginWindowProc = Utils::PopupDlgProcError;
+	ppd.lchIcon = PluginConfig.g_iconErr;
+	ppd.iSeconds = NEN::iDelayErr;
+	if (!NEN::bColDefaultErr) {
+		ppd.colorText = NEN::colTextErr;
+		ppd.colorBack = NEN::colBackErr;
+	}
+	wcsncpy_s(ppd.lpwzContactName, m_cache->getNick(), _TRUNCATE);
+	wcsncpy_s(ppd.lpwzText, TranslateT("A message delivery has failed.\nClick to open the message window."), _TRUNCATE);
+	PUAddPopupW(&ppd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::PlayIncomingSound() const
+{
+	int iPlay = m_pContainer->MustPlaySound(this);
+	if (iPlay) {
+		if (GetForegroundWindow() == m_pContainer->m_hwnd && m_pContainer->m_hwndActive == m_hwnd)
+			Skin_PlaySound("RecvMsgActive");
+		else
+			Skin_PlaySound("RecvMsgInactive");
+	}
+}
+
+void CMsgDialog::RemakeLog()
+{
+	m_szMicroLf[0] = 0;
+	m_lastEventTime = 0;
+	m_iLastEventType = -1;
+	StreamEvents(m_hDbEventFirst, -1, 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// saves a contact picture to disk
+// takes hbm (bitmap handle) and bool isOwnPic (1 == save the picture as your own avatar)
+// requires AVS service (Miranda 0.7+)
+
+void CMsgDialog::SaveAvatarToFile(HBITMAP hbm, int isOwnPic)
+{
+	wchar_t szFinalFilename[MAX_PATH];
+	time_t t = time(0);
+	struct tm *lt = localtime(&t);
+	uint32_t setView = 1;
+
+	wchar_t szTimestamp[100];
+	mir_snwprintf(szTimestamp, L"%04u %02u %02u_%02u%02u", lt->tm_year + 1900, lt->tm_mon, lt->tm_mday, lt->tm_hour, lt->tm_min);
+
+	wchar_t *szProto = mir_a2u(m_cache->getActiveProto());
+
+	wchar_t szFinalPath[MAX_PATH];
+	mir_snwprintf(szFinalPath, L"%s\\%s", M.getSavedAvatarPath(), szProto);
+	mir_free(szProto);
+
+	if (CreateDirectory(szFinalPath, nullptr) == 0) {
+		if (GetLastError() != ERROR_ALREADY_EXISTS) {
+			MessageBox(nullptr, TranslateT("Error creating destination directory"),
+				TranslateT("Save contact picture"), MB_OK | MB_ICONSTOP);
+			return;
+		}
+	}
+
+	wchar_t szBaseName[MAX_PATH];
+	if (isOwnPic)
+		mir_snwprintf(szBaseName, L"My Avatar_%s", szTimestamp);
+	else
+		mir_snwprintf(szBaseName, L"%s_%s", m_cache->getNick(), szTimestamp);
+
+	mir_snwprintf(szFinalFilename, L"%s.png", szBaseName);
+
+	// do not allow / or \ or % in the filename
+	Utils::sanitizeFilename(szFinalFilename);
+
+	wchar_t filter[MAX_PATH];
+	mir_snwprintf(filter, L"%s%c*.bmp;*.png;*.jpg;*.gif%c%c", TranslateT("Image files"), 0, 0, 0);
+
+	OPENFILENAME ofn = { 0 };
+	ofn.lpstrDefExt = L"png";
+	ofn.lpstrFilter = filter;
+	ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK;
+	ofn.lpfnHook = OpenFileSubclass;
+	ofn.lStructSize = sizeof(ofn);
+	ofn.lpstrFile = szFinalFilename;
+	ofn.lpstrInitialDir = szFinalPath;
+	ofn.nMaxFile = MAX_PATH;
+	ofn.nMaxFileTitle = MAX_PATH;
+	ofn.lCustData = (LPARAM)& setView;
+	if (GetSaveFileName(&ofn)) {
+		if (PathFileExists(szFinalFilename))
+			if (MessageBox(nullptr, TranslateT("The file exists. Do you want to overwrite it?"), TranslateT("Save contact picture"), MB_YESNO | MB_ICONQUESTION) == IDNO)
+				return;
+
+		IMGSRVC_INFO ii;
+		ii.cbSize = sizeof(ii);
+		ii.pwszName = szFinalFilename;
+		ii.hbm = hbm;
+		ii.dwMask = IMGI_HBITMAP;
+		ii.fif = FIF_UNKNOWN;			// get the format from the filename extension. png is default.
+		Image_Save(&ii);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::SaveSplitter()
+{
+	if (m_bIsAutosizingInput)
+		return;
+
+	if (m_iSplitterY < DPISCALEY_S(MINSPLITTERY) || m_iSplitterY < 0)
+		m_iSplitterY = DPISCALEY_S(MINSPLITTERY);
+
+	if (m_bSplitterOverride)
+		db_set_dw(m_hContact, SRMSGMOD_T, "splitsplity", m_iSplitterY);
+	else {
+		if (m_pContainer->cfg.fPrivate)
+			m_pContainer->cfg.iSplitterY = m_iSplitterY;
+		else
+			db_set_dw(0, SRMSGMOD_T, "splitsplity", m_iSplitterY);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send a pasted bitmap by file transfer.
+
+static LIST<wchar_t> vTempFilenames(5);
+
+void CMsgDialog::SendHBitmapAsFile(HBITMAP hbmp) const
+{
+	const wchar_t *mirandatempdir = L"Miranda";
+	const wchar_t *filenametemplate = L"\\clp-%Y%m%d-%H%M%S0.jpg";
+	wchar_t filename[MAX_PATH];
+	size_t tempdirlen = GetTempPath(MAX_PATH, filename);
+	bool fSend = true;
+
+	const char *szProto = m_cache->getActiveProto();
+	int wMyStatus = Proto_GetStatus(szProto);
+
+	uint32_t protoCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_1, 0);
+	uint32_t typeCaps = CallProtoService(szProto, PS_GETCAPS, PFLAGNUM_4, 0);
+
+	// check protocol capabilities, status modes and visibility lists (privacy)
+	// to determine whether the file can be sent. Throw a warning if any of
+	// these checks fails.
+	if (!(protoCaps & PF1_FILESEND))
+		fSend = false;
+
+	if ((ID_STATUS_OFFLINE == wMyStatus) || (ID_STATUS_OFFLINE == m_cache->getActiveStatus() && !(typeCaps & PF4_OFFLINEFILES)))
+		fSend = false;
+
+	if (protoCaps & PF1_VISLIST && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) == ID_STATUS_OFFLINE)
+		fSend = false;
+
+	if (protoCaps & PF1_INVISLIST && wMyStatus == ID_STATUS_INVISIBLE && db_get_w(m_cache->getActiveContact(), szProto, "ApparentMode", 0) != ID_STATUS_ONLINE)
+		fSend = false;
+
+	if (!fSend) {
+		CWarning::show(CWarning::WARN_SENDFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
+		return;
+	}
+
+	if (tempdirlen <= 0 || tempdirlen >= MAX_PATH - mir_wstrlen(mirandatempdir) - mir_wstrlen(filenametemplate) - 2) // -2 is because %Y takes 4 symbols
+		filename[0] = 0;					// prompt for a new name
+	else {
+		mir_wstrcpy(filename + tempdirlen, mirandatempdir);
+		if ((GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES || ((GetFileAttributes(filename) & FILE_ATTRIBUTE_DIRECTORY) == 0)) && CreateDirectory(filename, nullptr) == 0)
+			filename[0] = 0;
+		else {
+			tempdirlen = mir_wstrlen(filename);
+
+			time_t rawtime;
+			time(&rawtime);
+			const tm *timeinfo;
+			timeinfo = _localtime32((__time32_t *)& rawtime);
+			wcsftime(filename + tempdirlen, MAX_PATH - tempdirlen, filenametemplate, timeinfo);
+			size_t firstnumberpos = tempdirlen + 14;
+			size_t lastnumberpos = tempdirlen + 20;
+			while (GetFileAttributes(filename) != INVALID_FILE_ATTRIBUTES) {	// while it exists
+				for (size_t pos = lastnumberpos; pos >= firstnumberpos; pos--)
+					if (filename[pos]++ != '9')
+						break;
+					else
+						if (pos == firstnumberpos)
+							filename[0] = 0;	// all filenames exist => prompt for a new name
+						else
+							filename[pos] = '0';
+			}
+		}
+	}
+
+	if (filename[0] == 0) {	// prompting to save
+		wchar_t filter[MAX_PATH];
+		mir_snwprintf(filter, L"%s%c*.jpg%c%c", TranslateT("JPEG-compressed images"), 0, 0, 0);
+
+		OPENFILENAME dlg;
+		dlg.lStructSize = sizeof(dlg);
+		dlg.lpstrFilter = filter;
+		dlg.nFilterIndex = 1;
+		dlg.lpstrFile = filename;
+		dlg.nMaxFile = MAX_PATH;
+		dlg.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+		dlg.lpstrDefExt = L"jpg";
+		if (!GetSaveFileName(&dlg))
+			return;
+	}
+
+	IMGSRVC_INFO ii;
+	ii.cbSize = sizeof(ii);
+	ii.hbm = hbmp;
+	ii.pwszName = filename;
+	ii.dwMask = IMGI_HBITMAP;
+	ii.fif = FIF_JPEG;
+	if (!Image_Save(&ii)) {
+		CWarning::show(CWarning::WARN_SAVEFILE, MB_OK | MB_ICONEXCLAMATION | CWarning::CWF_NOALLOWHIDE);
+		return;
+	}
+
+	vTempFilenames.insert(mir_wstrdup(filename));
+
+	wchar_t *ppFiles[2] = { filename, nullptr };
+	CallService(MS_FILE_SENDSPECIFICFILEST, m_cache->getActiveContact(), (LPARAM)&ppFiles);
+}
+
+// remove all temporary files created by the "send clipboard as file" feature.
+void TSAPI CleanTempFiles()
+{
+	for (auto &it : vTempFilenames) {
+		DeleteFileW(it);
+		mir_free(it);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Sets a status bar text for a contact
+
+void CMsgDialog::SetStatusText(const wchar_t *wszText, HICON hIcon)
+{
+	if (wszText != nullptr) {
+		m_bStatusSet = true;
+		m_szStatusText = wszText;
+		m_szStatusIcon = hIcon;
+	}
+	else {
+		m_bStatusSet = false;
+		m_szStatusText.Empty();
+		m_szStatusIcon = nullptr;
+	}
+
+	tabUpdateStatusBar();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// subclassing for the message filter dialog (set and configure event filters for the 
+// current session
+
+static UINT _eventorder[] =
+{
+	GC_EVENT_ACTION,
+	GC_EVENT_MESSAGE,
+	GC_EVENT_NICK,
+	GC_EVENT_JOIN,
+	GC_EVENT_PART,
+	GC_EVENT_TOPIC,
+	GC_EVENT_ADDSTATUS,
+	GC_EVENT_INFORMATION,
+	GC_EVENT_QUIT,
+	GC_EVENT_KICK,
+	GC_EVENT_NOTICE
+};
+
+INT_PTR CALLBACK CMsgDialog::FilterWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	CMsgDialog *pDlg = (CMsgDialog *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+	switch (uMsg) {
+	case WM_INITDIALOG:
+		pDlg = (CMsgDialog *)lParam;
+		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+		{
+			uint32_t dwMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", 0);
+			uint32_t dwFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", 0);
+
+			uint32_t dwPopupMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", 0);
+			uint32_t dwPopupFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", 0);
+
+			uint32_t dwTrayMask = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", 0);
+			uint32_t dwTrayFlags = db_get_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", 0);
+
+			for (int i = 0; i < _countof(_eventorder); i++) {
+				CheckDlgButton(hwndDlg, IDC_1 + i, dwMask & _eventorder[i] ? (dwFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+				CheckDlgButton(hwndDlg, IDC_P1 + i, dwPopupMask & _eventorder[i] ? (dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+				CheckDlgButton(hwndDlg, IDC_T1 + i, dwTrayMask & _eventorder[i] ? (dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED) : BST_INDETERMINATE);
+			}
+		}
+		return FALSE;
+
+	case WM_CTLCOLOREDIT:
+	case WM_CTLCOLORSTATIC:
+		SetTextColor((HDC)wParam, RGB(60, 60, 150));
+		SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW));
+		return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
+
+	case WM_CLOSE:
+		if (wParam == 1 && lParam == 1 && pDlg) {
+			int iFlags = 0;
+			uint32_t dwMask = 0;
+
+			for (int i = 0; i < _countof(_eventorder); i++) {
+				int result = IsDlgButtonChecked(hwndDlg, IDC_1 + i);
+				dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+				iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+			}
+
+			if (iFlags & GC_EVENT_ADDSTATUS)
+				iFlags |= GC_EVENT_REMOVESTATUS;
+
+			if (dwMask == 0) {
+				db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterFlags");
+				db_unset(pDlg->m_hContact, CHAT_MODULE, "FilterMask");
+			}
+			else {
+				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterFlags", iFlags);
+				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "FilterMask", dwMask);
+			}
+
+			dwMask = iFlags = 0;
+
+			for (int i = 0; i < _countof(_eventorder); i++) {
+				int result = IsDlgButtonChecked(hwndDlg, IDC_P1 + i);
+				dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+				iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+			}
+
+			if (iFlags & GC_EVENT_ADDSTATUS)
+				iFlags |= GC_EVENT_REMOVESTATUS;
+
+			if (dwMask == 0) {
+				db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupFlags");
+				db_unset(pDlg->m_hContact, CHAT_MODULE, "PopupMask");
+			}
+			else {
+				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupFlags", iFlags);
+				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "PopupMask", dwMask);
+			}
+
+			dwMask = iFlags = 0;
+
+			for (int i = 0; i < _countof(_eventorder); i++) {
+				int result = IsDlgButtonChecked(hwndDlg, IDC_T1 + i);
+				dwMask |= (result != BST_INDETERMINATE ? _eventorder[i] : 0);
+				iFlags |= (result == BST_CHECKED ? _eventorder[i] : 0);
+			}
+			if (iFlags & GC_EVENT_ADDSTATUS)
+				iFlags |= GC_EVENT_REMOVESTATUS;
+
+			if (dwMask == 0) {
+				db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags");
+				db_unset(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask");
+			}
+			else {
+				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconFlags", iFlags);
+				db_set_dw(pDlg->m_hContact, CHAT_MODULE, "TrayIconMask", dwMask);
+			}
+			Chat_SetFilters(pDlg->getChat());
+
+			if (pDlg->m_bFilterEnabled) {
+				if (pDlg->m_iLogFilterFlags == 0)
+					pDlg->m_btnFilter.Click();
+				pDlg->RedrawLog();
+				db_set_b(pDlg->m_hContact, CHAT_MODULE, "FilterEnabled", pDlg->m_bFilterEnabled);
+			}
+		}
+		DestroyWindow(hwndDlg);
+		break;
+
+	case WM_DESTROY:
+		SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+		break;
+	}
+	return FALSE;
+}
+
+void CMsgDialog::ShowFilterMenu()
+{
+	m_hwndFilter = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_FILTER), m_pContainer->m_hwnd, FilterWndProc, (LPARAM)this);
+	TranslateDialogDefault(m_hwndFilter);
+
+	RECT rcFilter, rcLog;
+	GetClientRect(m_hwndFilter, &rcFilter);
+	GetWindowRect(m_pLog->GetHwnd(), &rcLog);
+
+	POINT pt;
+	pt.x = rcLog.right; pt.y = rcLog.bottom;
+	ScreenToClient(m_pContainer->m_hwnd, &pt);
+
+	SetWindowPos(m_hwndFilter, HWND_TOP, pt.x - rcFilter.right, pt.y - rcFilter.bottom, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::ShowPicture(bool showNewPic)
+{
+	if (!m_pPanel.isActive())
+		m_pic.cy = m_pic.cx = DPISCALEY_S(60);
+
+	if (showNewPic) {
+		if (m_pPanel.isActive() && m_pContainer->cfg.avatarMode != 3) {
+			if (!m_hwndPanelPic) {
+				InvalidateRect(m_hwnd, nullptr, TRUE);
+				UpdateWindow(m_hwnd);
+				Resize();
+			}
+			return;
+		}
+		AdjustBottomAvatarDisplay();
+	}
+	else {
+		m_bShowAvatar = !m_bShowAvatar;
+		db_set_b(m_hContact, SRMSGMOD_T, "MOD_ShowPic", m_bShowAvatar);
+	}
+
+	RECT rc;
+	GetWindowRect(GetDlgItem(m_hwnd, IDC_CONTACTPIC), &rc);
+	if (m_minEditBoxSize.cy + DPISCALEY_S(3) > m_iSplitterY)
+		SplitterMoved(rc.bottom - m_minEditBoxSize.cy, GetDlgItem(m_hwnd, IDC_SPLITTERY));
+	if (!showNewPic)
+		SetDialogToType();
+	else
+		Resize();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show a modified context menu for the richedit control(s)
+
+void CMsgDialog::ShowPopupMenu(const CCtrlBase &pCtrl, POINT pt)
+{
+	CHARRANGE sel, all = { 0, -1 };
+
+	HMENU hSubMenu, hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_CONTEXT));
+	if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
+		hSubMenu = GetSubMenu(hMenu, 0);
+	else {
+		hSubMenu = GetSubMenu(hMenu, 2);
+		EnableMenuItem(hSubMenu, IDM_PASTEFORMATTED, m_SendFormat != 0 ? MF_ENABLED : MF_GRAYED);
+		EnableMenuItem(hSubMenu, ID_EDITOR_PASTEANDSENDIMMEDIATELY, g_plugin.bPasteAndSend ? MF_ENABLED : MF_GRAYED);
+		CheckMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, PluginConfig.m_visualMessageSizeIndicator ? MF_CHECKED : MF_UNCHECKED);
+		EnableMenuItem(hSubMenu, ID_EDITOR_SHOWMESSAGELENGTHINDICATOR, m_pContainer->m_hwndStatus ? MF_ENABLED : MF_GRAYED);
+	}
+	TranslateMenu(hSubMenu);
+	pCtrl.SendMsg(EM_EXGETSEL, 0, (LPARAM)& sel);
+	if (sel.cpMin == sel.cpMax) {
+		EnableMenuItem(hSubMenu, IDM_COPY, MF_GRAYED);
+		EnableMenuItem(hSubMenu, IDM_QUOTE, MF_GRAYED);
+		if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
+			EnableMenuItem(hSubMenu, IDM_CUT, MF_GRAYED);
+	}
+
+	if (pCtrl.GetCtrlId() == IDC_SRMM_LOG) {
+		InsertMenuA(hSubMenu, 6, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+		CheckMenuItem(hSubMenu, ID_LOG_FREEZELOG, m_bScrollingDisabled ? MF_CHECKED : MF_UNCHECKED);
+	}
+
+	MessageWindowPopupData mwpd;
+	// First notification
+	mwpd.uType = MSG_WINDOWPOPUP_SHOWING;
+	mwpd.uFlags = (pCtrl.GetCtrlId() == IDC_SRMM_LOG ? MSG_WINDOWPOPUP_LOG : MSG_WINDOWPOPUP_INPUT);
+	mwpd.hContact = m_hContact;
+	mwpd.hwnd = pCtrl.GetHwnd();
+	mwpd.hMenu = hSubMenu;
+	mwpd.selection = 0;
+	mwpd.pt = pt;
+	NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
+
+	int iSelection = TrackPopupMenu(hSubMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_hwnd, nullptr);
+
+	// Second notification
+	mwpd.selection = iSelection;
+	mwpd.uType = MSG_WINDOWPOPUP_SELECTED;
+	NotifyEventHooks(g_chatApi.hevWinPopup, 0, (LPARAM)& mwpd);
+
+	switch (iSelection) {
+	case IDM_COPY:
+		pCtrl.SendMsg(WM_COPY, 0, 0);
+		break;
+	case IDM_CUT:
+		pCtrl.SendMsg(WM_CUT, 0, 0);
+		break;
+	case IDM_PASTE:
+	case IDM_PASTEFORMATTED:
+		if (pCtrl.GetCtrlId() == IDC_SRMM_MESSAGE)
+			pCtrl.SendMsg(EM_PASTESPECIAL, (iSelection == IDM_PASTE) ? CF_UNICODETEXT : 0, 0);
+		break;
+	case IDM_COPYALL:
+		pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
+		pCtrl.SendMsg(WM_COPY, 0, 0);
+		pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& sel);
+		break;
+	case IDM_QUOTE:
+		SendMessage(m_hwnd, WM_COMMAND, IDC_QUOTE, 0);
+		break;
+	case IDM_SELECTALL:
+		pCtrl.SendMsg(EM_EXSETSEL, 0, (LPARAM)& all);
+		break;
+	case IDM_CLEAR:
+		tabClearLog();
+		break;
+	case ID_LOG_FREEZELOG:
+		SendDlgItemMessage(m_hwnd, IDC_SRMM_LOG, WM_KEYDOWN, VK_F12, 0);
+		break;
+	case ID_EDITOR_SHOWMESSAGELENGTHINDICATOR:
+		PluginConfig.m_visualMessageSizeIndicator = !PluginConfig.m_visualMessageSizeIndicator;
+		db_set_b(0, SRMSGMOD_T, "msgsizebar", (uint8_t)PluginConfig.m_visualMessageSizeIndicator);
+		Srmm_Broadcast(DM_CONFIGURETOOLBAR, 0, 0);
+		Resize();
+		if (m_pContainer->m_hwndStatus)
+			RedrawWindow(m_pContainer->m_hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+		break;
+	case ID_EDITOR_PASTEANDSENDIMMEDIATELY:
+		HandlePasteAndSend();
+		break;
+	}
+
+	if (pCtrl.GetCtrlId() == IDC_SRMM_LOG)
+		RemoveMenu(hSubMenu, 7, MF_BYPOSITION);
+	DestroyMenu(hMenu);
+}
+
+void CMsgDialog::SplitterMoved(int coord, HWND hwnd)
+{
+	POINT pt;
+	RECT rc;
+
+	switch (GetDlgCtrlID(hwnd)) {
+	case IDC_MULTISPLITTER:
+		GetClientRect(m_hwnd, &rc);
+		pt.x = coord;
+		pt.y = 0;
+		ScreenToClient(m_hwnd, &pt);
+		{
+			int oldSplitterX = m_iMultiSplit;
+			m_iMultiSplit = rc.right - pt.x;
+			if (m_iMultiSplit < 25)
+				m_iMultiSplit = 25;
+
+			if (m_iMultiSplit > ((rc.right - rc.left) - 80))
+				m_iMultiSplit = oldSplitterX;
+		}
+		Resize();
+		break;
+
+	case IDC_SPLITTERX:
+		GetClientRect(m_hwnd, &rc);
+		pt.x = coord, pt.y = 0;
+		ScreenToClient(m_hwnd, &pt);
+		{
+			int iSplitterX = rc.right - pt.x + 1;
+			if (iSplitterX < 35)
+				iSplitterX = 35;
+			if (iSplitterX > rc.right - rc.left - 35)
+				iSplitterX = rc.right - rc.left - 35;
+			m_pContainer->cfg.iSplitterX = iSplitterX;
+		}
+		Resize();
+		break;
+
+	case IDC_SPLITTERY:
+		GetClientRect(m_hwnd, &rc);
+		rc.top += (m_pPanel.isActive() ? m_pPanel.getHeight() + 40 : 30);
+		pt.x = 0;
+		pt.y = coord;
+		ScreenToClient(m_hwnd, &pt);
+		{
+			int oldSplitterY = m_iSplitterY;
+			int oldDynaSplitter = m_dynaSplitter;
+
+			m_iSplitterY = rc.bottom - pt.y + DPISCALEY_S(23);
+
+			// attempt to fix splitter troubles..
+			// hardcoded limits... better solution is possible, but this works for now
+			int bottomtoolbarH = 0;
+			if (m_pContainer->cfg.flags.m_bBottomToolbar)
+				bottomtoolbarH = 22;
+
+			if (m_iSplitterY < (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH)) {	// min splitter size
+				m_iSplitterY = (DPISCALEY_S(MINSPLITTERY) + 5 + bottomtoolbarH);
+				m_dynaSplitter = m_iSplitterY - DPISCALEY_S(34);
+				DM_RecalcPictureSize();
+			}
+			else if (m_iSplitterY > (rc.bottom - rc.top)) {
+				m_iSplitterY = oldSplitterY;
+				m_dynaSplitter = oldDynaSplitter;
+				DM_RecalcPictureSize();
+			}
+			else {
+				m_dynaSplitter = (rc.bottom - pt.y) - DPISCALEY_S(11);
+				DM_RecalcPictureSize();
+			}
+		}
+		UpdateToolbarBG();
+		Resize();
+		break;
+
+	case IDC_PANELSPLITTER:
+		GetClientRect(m_pLog->GetHwnd(), &rc);
+
+		POINT	pnt = { 0, coord };
+		ScreenToClient(m_hwnd, &pnt);
+		if ((pnt.y + 2 >= MIN_PANELHEIGHT + 2) && (pnt.y + 2 < 100) && (pnt.y + 2 < rc.bottom - 30))
+			m_pPanel.setHeight(pnt.y + 2, true);
+
+		RedrawWindow(m_hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE);
+		if (M.isAero())
+			InvalidateRect(GetParent(m_hwnd), nullptr, FALSE);
+		break;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::StreamEvents(MEVENT hDbEventFirst, int count, bool bAppend)
+{
+	m_pLog->LogEvents(hDbEventFirst, count, bAppend);
+
+	DM_ScrollToBottom(0, 0);
+	if (bAppend && hDbEventFirst)
+		m_hDbEventLast = hDbEventFirst;
+	else
+		m_hDbEventLast = db_event_last(m_hContact);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// sent by the select container dialog box when a container was selected...
+
+void CMsgDialog::SwitchToContainer(const wchar_t *szNewName)
+{
+	if (!mir_wstrcmp(szNewName, TranslateT("Default container")))
+		szNewName = CGlobals::m_default_container_name;
+
+	int iOldItems = TabCtrl_GetItemCount(m_hwndParent);
+	if (!wcsncmp(m_pContainer->m_wszName, szNewName, CONTAINER_NAMELEN))
+		return;
+
+	TContainerData *pNewContainer = FindContainerByName(szNewName);
+	if (pNewContainer == nullptr)
+		if ((pNewContainer = CreateContainer(szNewName, FALSE, m_hContact)) == nullptr)
+			return;
+
+	db_set_ws(m_hContact, SRMSGMOD_T, "containerW", szNewName);
+	PostMessage(PluginConfig.g_hwndHotkeyHandler, DM_DOCREATETAB, (WPARAM)pNewContainer, m_hContact);
+	if (iOldItems > 1)                // there were more than 1 tab, container is still valid
+		SendMessage(m_pContainer->m_hwndActive, WM_SIZE, 0, 0);
+	SetForegroundWindow(pNewContainer->m_hwnd);
+	SetActiveWindow(pNewContainer->m_hwnd);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool CMsgDialog::TabAutoComplete()
+{
+	LRESULT lResult = m_message.SendMsg(EM_GETSEL, 0, 0);
+	int start = LOWORD(lResult), end = HIWORD(lResult);
+	int origStart = start, origEnd = end;
+	m_message.SendMsg(EM_SETSEL, end, end);
+
+	GETTEXTEX gt = { 0 };
+	gt.codepage = 1200;
+	gt.flags = GTL_DEFAULT | GTL_PRECISE;
+	int iLen = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)& gt, 0);
+	if (iLen <= 0)
+		return false;
+
+	bool isTopic = false, isRoom = false;
+	wchar_t *pszText = (wchar_t *)mir_calloc((iLen + 10) * sizeof(wchar_t));
+
+	gt.flags = GT_DEFAULT;
+	gt.cb = (iLen + 9) * sizeof(wchar_t);
+	m_message.SendMsg(EM_GETTEXTEX, (WPARAM)& gt, (LPARAM)pszText);
+
+	if (m_wszSearchResult != nullptr) {
+		int cbResult = (int)mir_wstrlen(m_wszSearchResult);
+		if (start >= cbResult && !wcsnicmp(m_wszSearchResult, pszText + start - cbResult, cbResult)) {
+			start -= cbResult;
+			goto LBL_SkipEnd;
+		}
+	}
+
+	while (start > 0 && pszText[start - 1] != ' ' && pszText[start - 1] != 13 && pszText[start - 1] != VK_TAB)
+		start--;
+
+LBL_SkipEnd:
+	while (end < iLen && pszText[end] != ' ' && pszText[end] != 13 && pszText[end - 1] != VK_TAB)
+		end++;
+
+	if (pszText[start] == '#')
+		isRoom = true;
+	else {
+		int topicStart = start;
+		while (topicStart > 0 && (pszText[topicStart - 1] == ' ' || pszText[topicStart - 1] == 13 || pszText[topicStart - 1] == VK_TAB))
+			topicStart--;
+		if (topicStart > 5 && wcsstr(&pszText[topicStart - 6], L"/topic") == &pszText[topicStart - 6])
+			isTopic = true;
+	}
+	
+	if (m_wszSearchQuery == nullptr) {
+		m_wszSearchQuery = mir_wstrndup(pszText + start, end - start);
+		m_wszSearchResult = mir_wstrdup(m_wszSearchQuery);
+		m_pLastSession = nullptr;
+	}
+
+	const wchar_t *pszName = nullptr;
+	if (isTopic)
+		pszName = m_si->ptszTopic;
+	else if (isRoom) {
+		m_pLastSession = SM_FindSessionAutoComplete(m_si->pszModule, m_si, m_pLastSession, m_wszSearchQuery, m_wszSearchResult);
+		if (m_pLastSession != nullptr)
+			pszName = m_pLastSession->ptszName;
+	}
+	else pszName = g_chatApi.UM_FindUserAutoComplete(m_si, m_wszSearchQuery, m_wszSearchResult);
+
+	replaceStrW(m_wszSearchResult, nullptr);
+
+	if (pszName != nullptr) {
+		if (end != start) {
+			CMStringW szReplace;
+			if (!isRoom && !isTopic && start == 0) {
+				szReplace = pszName;
+				if (mir_wstrlen(g_Settings.pwszAutoText))
+					szReplace.Append(g_Settings.pwszAutoText);
+				szReplace.AppendChar(' ');
+				m_wszSearchResult = szReplace.Detach();
+				pszName = m_wszSearchResult;
+			}
+			else m_wszSearchResult = mir_wstrdup(pszName);
+
+			m_message.SendMsg(EM_SETSEL, start, end);
+			m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)pszName);
+		}
+		else m_wszSearchResult = mir_wstrdup(pszName);
+
+		return true;
+	}
+
+	if (end != start) {
+		m_message.SendMsg(EM_SETSEL, start, end);
+		m_message.SendMsg(EM_REPLACESEL, TRUE, (LPARAM)m_wszSearchQuery);
+	}
+	m_message.SendMsg(EM_SETSEL, origStart, origEnd);
+	replaceStrW(m_wszSearchQuery, nullptr);
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::tabClearLog()
+{
+	if (isChat()) {
+		g_chatApi.LM_RemoveAll(&m_si->pLog, &m_si->pLogEnd);
+		m_si->iEventCount = 0;
+		m_si->LastTime = 0;
+		PostMessage(m_hwnd, WM_MOUSEACTIVATE, 0, 0);
+	}
+
+	m_pLog->Clear();
+	m_hDbEventFirst = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+CThumbBase *CMsgDialog::tabCreateThumb(CProxyWindow *pProxy) const
+{
+	if (isChat())
+		return new CThumbMUC(pProxy, m_si);
+
+	return new CThumbIM(pProxy);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update all status bar fields and force a redraw of the status bar.
+
+void CMsgDialog::tabUpdateStatusBar() const
+{
+	if (m_pContainer->m_hwndStatus && m_pContainer->m_hwndActive == m_hwnd) {
+		if (!isChat()) {
+			if (m_wszStatusBar[0]) {
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_DEFAULT_TYPING]);
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_wszStatusBar);
+			}
+			else if (m_bStatusSet) {
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+			}
+			else {
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+				DM_UpdateLastMessage();
+			}
+		}
+		else {
+			if (m_bStatusSet) {
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, (LPARAM)m_szStatusIcon);
+				SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)m_szStatusText.c_str());
+			}
+			else SendMessage(m_pContainer->m_hwndStatus, SB_SETICON, 0, 0);
+		}
+		UpdateReadChars();
+		InvalidateRect(m_pContainer->m_hwndStatus, nullptr, TRUE);
+		SendMessage(m_pContainer->m_hwndStatus, WM_USER + 101, 0, (LPARAM)this);
+	}
+}
+
+int CMsgDialog::Typing(int secs)
+{
+	if (!AllowTyping())
+		return 0;
+
+	int preTyping = m_nTypeSecs != 0;
+
+	setTyping(m_nTypeSecs = (secs > 0) ? secs : 0);
+	if (m_nTypeSecs)
+		m_bShowTyping = 0;
+
+	return preTyping;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateNickList()
+{
+	int i = m_nickList.SendMsg(LB_GETTOPINDEX, 0, 0);
+	m_nickList.SendMsg(LB_SETCOUNT, m_si->getUserList().getCount(), 0);
+	m_nickList.SendMsg(LB_SETTOPINDEX, i, 0);
+	UpdateTitle();
+	m_hTabIcon = m_hTabStatusIcon;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateOptions()
+{
+	GetSendFormat();
+
+	DM_InitRichEdit();
+	m_btnOk.SendMsg(BUTTONSETASNORMAL, TRUE, 0);
+
+	m_nickList.SetItemHeight(0, g_Settings.iNickListFontHeight);
+	InvalidateRect(m_nickList.GetHwnd(), nullptr, TRUE);
+
+	m_btnFilter.SendMsg(BUTTONSETOVERLAYICON, (LPARAM)(m_bFilterEnabled ? PluginConfig.g_iconOverlayEnabled : PluginConfig.g_iconOverlayDisabled), 0);
+
+	CSuper::UpdateOptions();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// update the status bar field which displays the number of characters in the input area
+// and various indicators (caps lock, num lock, insert mode).
+
+void CMsgDialog::UpdateReadChars() const
+{
+	if (!m_pContainer->m_hwndStatus || m_pContainer->m_hwndActive != m_hwnd)
+		return;
+
+	int len;
+	if (isChat())
+		len = GetWindowTextLength(m_message.GetHwnd());
+	else {
+		// retrieve text length in UTF8 bytes, because this is the relevant length for most protocols
+		GETTEXTLENGTHEX gtxl = { 0 };
+		gtxl.codepage = CP_UTF8;
+		gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+		len = m_message.SendMsg(EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
+	}
+
+	BOOL fCaps = (GetKeyState(VK_CAPITAL) & 1);
+	BOOL fNum = (GetKeyState(VK_NUMLOCK) & 1);
+
+	wchar_t szBuf[20]; szBuf[0] = 0;
+	if (m_bInsertMode)
+		mir_wstrcat(szBuf, L"O");
+	if (fCaps)
+		mir_wstrcat(szBuf, L"C");
+	if (fNum)
+		mir_wstrcat(szBuf, L"N");
+	if (m_bInsertMode || fCaps || fNum)
+		mir_wstrcat(szBuf, L" | ");
+
+	wchar_t buf[128];
+	mir_snwprintf(buf, L"%s%s %d/%d", szBuf, m_lcID, m_iOpenJobs, len);
+	SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 1, (LPARAM)buf);
+	if (PluginConfig.m_visualMessageSizeIndicator)
+		InvalidateRect(m_pContainer->m_hwndStatus, nullptr, FALSE);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateSaveAndSendButton()
+{
+	GETTEXTLENGTHEX gtxl = { 0 };
+	gtxl.codepage = CP_UTF8;
+	gtxl.flags = GTL_DEFAULT | GTL_PRECISE | GTL_NUMBYTES;
+
+	int len = SendDlgItemMessage(m_hwnd, IDC_SRMM_MESSAGE, EM_GETTEXTLENGTHEX, (WPARAM)&gtxl, 0);
+	if (len && GetSendButtonState() == PBS_DISABLED)
+		EnableSendButton(true);
+	else if (len == 0 && GetSendButtonState() != PBS_DISABLED)
+		EnableSendButton(false);
+
+	if (len) {          // looks complex but avoids flickering on the button while typing.
+		if (!m_bSaveBtn) {
+			SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_SAVE]);
+			SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save and close session"), BATF_UNICODE);
+			m_bSaveBtn = true;
+		}
+	}
+	else {
+		SendDlgItemMessage(m_hwnd, IDC_CLOSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)PluginConfig.g_buttonBarIcons[ICON_BUTTON_CANCEL]);
+		SendDlgItemMessage(m_hwnd, IDC_CLOSE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Close session"), BATF_UNICODE);
+		m_bSaveBtn = false;
+	}
+	m_textLen = len;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateStatusBar()
+{
+	if (m_pContainer->m_hwndActive != m_hwnd || m_pContainer->m_hwndStatus == nullptr || CMimAPI::m_shutDown || m_wszStatusBar[0])
+		return;
+
+	if (m_si->pszModule == nullptr)
+		return;
+
+	//Mad: strange rare crash here...
+	MODULEINFO *mi = m_si->pMI;
+	if (!mi->ptszModDispName)
+		return;
+
+	int x = 12;
+	x += Chat_GetTextPixelSize(mi->ptszModDispName, (HFONT)SendMessage(m_pContainer->m_hwndStatus, WM_GETFONT, 0, 0), true);
+	x += GetSystemMetrics(SM_CXSMICON);
+
+	wchar_t szFinalStatusBarText[512];
+	if (m_pPanel.isActive()) {
+		time_t now = time(0);
+		uint32_t diff = (now - mi->idleTimeStamp) / 60;
+		if (diff >= 1) {
+			if (diff > 59) {
+				uint32_t hours = diff / 60;
+				uint32_t minutes = diff % 60;
+				mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s, %d %s idle"),
+					hours, hours > 1 ? TranslateT("hours") : TranslateT("hour"),
+					minutes, minutes > 1 ? TranslateT("minutes") : TranslateT("minute"));
+			}
+			else mir_snwprintf(mi->tszIdleMsg, TranslateT(", %d %s idle"), diff, diff > 1 ? TranslateT("minutes") : TranslateT("minute"));
+		}
+		else mi->tszIdleMsg[0] = 0;
+
+		mir_snwprintf(szFinalStatusBarText, TranslateT("%s on %s%s"), m_wszMyNickname, mi->ptszModDispName, mi->tszIdleMsg);
+	}
+	else {
+		if (m_si->ptszStatusbarText)
+			mir_snwprintf(szFinalStatusBarText, L"%s %s", mi->ptszModDispName, m_si->ptszStatusbarText);
+		else
+			wcsncpy_s(szFinalStatusBarText, mi->ptszModDispName, _TRUNCATE);
+	}
+	SendMessage(m_pContainer->m_hwndStatus, SB_SETTEXT, 0, (LPARAM)szFinalStatusBarText);
+	tabUpdateStatusBar();
+	m_pPanel.Invalidate();
+	if (m_pWnd)
+		m_pWnd->Invalidate();
+}
+
+void CMsgDialog::UpdateTitle()
+{
+	if (isChat()) {
+		m_wStatus = m_si->wStatus;
+
+		const wchar_t *szNick = m_cache->getNick();
+		if (mir_wstrlen(szNick) > 0) {
+			if (M.GetByte("cuttitle", 0))
+				CutContactName(szNick, m_wszTitle, _countof(m_wszTitle));
+			else
+				wcsncpy_s(m_wszTitle, szNick, _TRUNCATE);
+		}
+
+		CMStringW wszTitle;
+		HICON hIcon = nullptr;
+		int nUsers = m_si->getUserList().getCount();
+
+		switch (m_si->iType) {
+		case GCW_CHATROOM:
+			hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
+			wszTitle.Format((nUsers == 1) ? TranslateT("%s: chat room (%u user%s)") : TranslateT("%s: chat room (%u users%s)"),
+				szNick, nUsers, m_bFilterEnabled ? TranslateT(", event filter active") : L"");
+			break;
+
+		case GCW_PRIVMESS:
+			hIcon = Skin_LoadProtoIcon(m_si->pszModule, (m_wStatus <= ID_STATUS_OFFLINE) ? ID_STATUS_OFFLINE : m_wStatus);
+			if (nUsers == 1)
+				wszTitle.Format(TranslateT("%s: message session"), szNick);
+			else
+				wszTitle.Format(TranslateT("%s: message session (%u users)"), szNick, nUsers);
+			break;
+
+		case GCW_SERVER:
+			wszTitle.Format(L"%s: Server", szNick);
+			hIcon = LoadIconEx("window");
+			break;
+
+		default:
+			return;
+		}
+
+		if (m_pWnd) {
+			m_pWnd->updateTitle(m_wszTitle);
+			m_pWnd->updateIcon(hIcon);
+		}
+		m_hTabStatusIcon = hIcon;
+
+		if (m_cache->getStatus() != m_cache->getOldStatus()) {
+			wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_wStatus, 0), _TRUNCATE);
+
+			TCITEM item = {};
+			item.mask = TCIF_TEXT;
+			item.pszText = m_wszTitle;
+			TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+		}
+		SetWindowText(m_hwnd, wszTitle);
+		if (m_pContainer->m_hwndActive == m_hwnd) {
+			m_pContainer->UpdateTitle(0, this);
+			UpdateStatusBar();
+		}
+	}
+	else {
+		uint32_t dwOldIdle = m_idle;
+		const char *szActProto = nullptr;
+
+		m_wszStatus[0] = 0;
+
+		if (m_iTabID == -1)
+			return;
+
+		TCITEM item = {};
+
+		bool bChanged = false;
+		wchar_t newtitle[128];
+		if (m_szProto) {
+			szActProto = m_cache->getProto();
+
+			bool bHasName = (m_cache->getUIN()[0] != 0);
+			m_idle = m_cache->getIdleTS();
+			m_bIsIdle = m_idle != 0;
+
+			m_wStatus = m_cache->getStatus();
+			wcsncpy_s(m_wszStatus, Clist_GetStatusModeDescription(m_szProto == nullptr ? ID_STATUS_OFFLINE : m_wStatus, 0), _TRUNCATE);
+
+			wchar_t newcontactname[128]; newcontactname[0] = 0;
+			if (PluginConfig.m_bCutContactNameOnTabs)
+				CutContactName(m_cache->getNick(), newcontactname, _countof(newcontactname));
+			else
+				wcsncpy_s(newcontactname, m_cache->getNick(), _TRUNCATE);
+
+			Utils::DoubleAmpersands(newcontactname, _countof(newcontactname));
+
+			if (newcontactname[0] != 0) {
+				if (g_plugin.bStatusOnTabs)
+					mir_snwprintf(newtitle, L"%s (%s)", newcontactname, m_wszStatus);
+				else
+					wcsncpy_s(newtitle, newcontactname, _TRUNCATE);
+			}
+			else wcsncpy_s(newtitle, L"Forward", _TRUNCATE);
+
+			if (mir_wstrcmp(newtitle, m_wszTitle))
+				bChanged = true;
+			else if (m_wStatus != m_wOldStatus)
+				bChanged = true;
+
+			UpdateWindowIcon();
+
+			wchar_t fulluin[256];
+			if (m_bIsMeta)
+				mir_snwprintf(fulluin,
+				TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nRight click for metacontact control\nClick dropdown to add or remove user from your favorites."),
+				bHasName ? m_cache->getUIN() : TranslateT("No UID"));
+			else
+				mir_snwprintf(fulluin,
+				TranslateT("UID: %s (Shift+click -> copy to clipboard)\nClick for user's details\nClick dropdown to change this contact's favorite status."),
+				bHasName ? m_cache->getUIN() : TranslateT("No UID"));
+
+			SendDlgItemMessage(m_hwnd, IDC_NAME, BUTTONADDTOOLTIP, (WPARAM)fulluin, BATF_UNICODE);
+		}
+		else wcsncpy_s(newtitle, L"Message Session", _TRUNCATE);
+
+		m_wOldStatus = m_wStatus;
+		if (m_idle != dwOldIdle || bChanged) {
+			if (bChanged) {
+				item.mask |= TCIF_TEXT;
+				item.pszText = m_wszTitle;
+				wcsncpy_s(m_wszTitle, newtitle, _TRUNCATE);
+				if (m_pWnd)
+					m_pWnd->updateTitle(m_cache->getNick());
+			}
+			if (m_iTabID >= 0) {
+				TabCtrl_SetItem(m_hwndParent, m_iTabID, &item);
+				if (m_pContainer->cfg.flags.m_bSideBar)
+					m_pContainer->m_pSideBar->updateSession(this);
+			}
+			if (m_pContainer->m_hwndActive == m_hwnd && bChanged)
+				m_pContainer->UpdateTitle(m_hContact);
+
+			m_pPanel.Invalidate();
+			if (m_pWnd)
+				m_pWnd->Invalidate();
+		}
+
+		// care about MetaContacts and update the statusbar icon with the currently "most online" contact...
+		if (m_bIsMeta) {
+			PostMessage(m_hwnd, DM_OWNNICKCHANGED, 0, 0);
+			if (m_pContainer->cfg.flags.m_bUinStatusBar)
+				DM_UpdateLastMessage();
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void CMsgDialog::UpdateWindowIcon()
+{
+	if (m_hXStatusIcon) {
+		DestroyIcon(m_hXStatusIcon);
+		m_hXStatusIcon = nullptr;
+	}
+
+	if (LPCSTR szProto = m_cache->getProto()) {
+		m_hTabIcon = m_hTabStatusIcon = GetMyContactIcon(&g_plugin.bMetaTab);
+		if (g_plugin.bUseXStatus)
+			m_hXStatusIcon = GetXStatusIcon();
+
+		SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BUTTONSETASDIMMED, m_bIsIdle, 0);
+		SendDlgItemMessage(m_hwnd, IDC_PROTOCOL, BM_SETIMAGE, IMAGE_ICON, (LPARAM)(m_hXStatusIcon ? m_hXStatusIcon : GetMyContactIcon(&g_plugin.bMetaBar)));
+
+		if (m_pContainer->m_hwndActive == m_hwnd)
+			m_pContainer->SetIcon(this, m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
+
+		if (m_pWnd)
+			m_pWnd->updateIcon(m_hXStatusIcon ? m_hXStatusIcon : m_hTabIcon);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// called whenever a group chat tab becomes active(either by switching tabs or activating a
+// container window
+
+void CMsgDialog::UpdateWindowState(UINT msg)
+{
+	if (m_iTabID < 0)
+		return;
+
+	if (msg == WM_ACTIVATE) {
+		if (m_pContainer->cfg.flags.m_bTransparent) {
+			uint32_t trans = LOWORD(m_pContainer->cfg.dwTransparency);
+			SetLayeredWindowAttributes(m_pContainer->m_hwnd, CSkin::m_ContainerColorKey, (uint8_t)trans, (m_pContainer->cfg.flags.m_bTransparent ? LWA_ALPHA : 0));
+		}
+	}
+
+	if (m_hwndFilter) {
+		POINT pt;
+		GetCursorPos(&pt);
+
+		RECT rcFilter;
+		GetWindowRect(m_hwndFilter, &rcFilter);
+		if (!PtInRect(&rcFilter, pt)) {
+			SendMessage(m_hwndFilter, WM_CLOSE, 1, 1);
+			m_hwndFilter = nullptr;
+		}
+	}
+
+	if (m_bIsAutosizingInput && m_iInputAreaHeight == -1) {
+		m_iInputAreaHeight = 0;
+		m_message.SendMsg(EM_REQUESTRESIZE, 0, 0);
+	}
+
+	m_pPanel.dismissConfig();
+	m_dwUnread = 0;
+	if (m_pWnd) {
+		m_pWnd->activateTab();
+		m_pWnd->setOverlayIcon(nullptr, true);
+	}
+
+	if (m_pContainer->m_hwndSaved == m_hwnd)
+		return;
+
+	m_pContainer->m_hwndSaved = m_hwnd;
+	m_dwTickLastEvent = 0;
+	m_bDividerSet = false;
+
+	if (m_pContainer->m_dwFlashingStarted != 0) {
+		m_pContainer->FlashContainer(0, 0);
+		m_pContainer->m_dwFlashingStarted = 0;
+	}
+
+	if (m_si) {
+		g_chatApi.SetActiveSession(m_si);
+		m_hTabIcon = m_hTabStatusIcon;
+
+		if (db_get_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0) != 0)
+			db_set_w(m_si->hContact, m_si->pszModule, "ApparentMode", 0);
+		if (g_clistApi.pfnGetEvent(m_si->hContact, 0))
+			g_clistApi.pfnRemoveEvent(m_si->hContact, GC_FAKE_EVENT);
+
+		UpdateTitle();
+		m_hTabIcon = m_hTabStatusIcon;
+		if (timerFlash.Stop() || m_iFlashIcon) {
+			FlashTab(false);
+			m_bCanFlashTab = FALSE;
+			m_iFlashIcon = nullptr;
+		}
+
+		m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
+
+		if (m_bNeedCheckSize)
+			PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
+
+		SetFocus(m_message.GetHwnd());
+		m_dwLastActivity = GetTickCount();
+		m_pContainer->m_dwLastActivity = m_dwLastActivity;
+		m_pContainer->m_pMenuBar->configureMenu();
+	}
+	else {
+		if (timerFlash.Stop()) {
+			FlashTab(false);
+			m_bCanFlashTab = false;
+		}
+
+		if (m_bFlashClist) {
+			m_bFlashClist = false;
+			if (m_hFlashingEvent != 0)
+				g_clistApi.pfnRemoveEvent(m_hContact, m_hFlashingEvent);
+			m_hFlashingEvent = 0;
+		}
+		m_pContainer->cfg.flags.m_bNeedsUpdateTitle = false;
+
+		if (m_bDeferredRemakeLog && !IsIconic(m_pContainer->m_hwnd)) {
+			RemakeLog();
+			m_bDeferredRemakeLog = false;
+		}
+
+		if (m_bNeedCheckSize)
+			PostMessage(m_hwnd, DM_SAVESIZE, 0, 0);
+
+		m_pContainer->m_hIconTaskbarOverlay = nullptr;
+		m_pContainer->UpdateTitle(m_hContact);
+
+		tabUpdateStatusBar();
+		m_dwLastActivity = GetTickCount();
+		m_pContainer->m_dwLastActivity = m_dwLastActivity;
+
+		m_pContainer->m_pMenuBar->configureMenu();
+		g_arUnreadWindows.remove(HANDLE(m_hContact));
+
+		m_pPanel.Invalidate();
+
+		if (m_bDeferredScroll) {
+			m_bDeferredScroll = false;
+			DM_ScrollToBottom(0, 1);
+		}
+	}
+
+	DM_SetDBButtonStates();
+
+	if (m_bDelayedSplitter) {
+		m_bDelayedSplitter = false;
+		ShowWindow(m_pContainer->m_hwnd, SW_RESTORE);
+		PostMessage(m_hwnd, DM_SPLITTERGLOBALEVENT, m_wParam, m_lParam);
+		PostMessage(m_hwnd, WM_SIZE, 0, 0);
+		m_wParam = m_lParam = 0;
+	}
+
+	BB_SetButtonsPos();
+	if (M.isAero())
+		InvalidateRect(m_hwndParent, nullptr, FALSE);
+
+	if (m_pContainer->cfg.flags.m_bSideBar)
+		m_pContainer->m_pSideBar->setActiveItem(this, msg == WM_ACTIVATE);
+
+	if (m_pWnd)
+		m_pWnd->Invalidate();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// generic handler for the WM_COPY message in message log/chat history richedit control(s).
+// it filters out the invisible event boundary markers from the text copied to the clipboard.
+// WINE Fix: overwrite clippboad data from original control data
+
+LRESULT CMsgDialog::WMCopyHandler(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	LRESULT result = mir_callNextSubclass(m_pLog->GetHwnd(), stubLogProc, msg, wParam, lParam);
+
+	ptrA szFromStream(LOG()->GetRichTextRtf(true, true));
+	if (szFromStream != nullptr) {
+		ptrW converted(mir_utf8decodeW(szFromStream));
+		if (converted != nullptr) {
+			Utils::FilterEventMarkers(converted);
+			Utils_ClipboardCopy(converted);
+		}
+	}
+
+	return result;
+}
diff --git a/plugins/TabSRMM/src/msgdlgutils.cpp b/plugins/TabSRMM/src/msgdlgutils.cpp
index daec4e8349..eff4705074 100644
--- a/plugins/TabSRMM/src/msgdlgutils.cpp
+++ b/plugins/TabSRMM/src/msgdlgutils.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgdlgutils.h b/plugins/TabSRMM/src/msgdlgutils.h
index b6666fd6ce..b9cd0fd792 100644
--- a/plugins/TabSRMM/src/msgdlgutils.h
+++ b/plugins/TabSRMM/src/msgdlgutils.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msglog.cpp b/plugins/TabSRMM/src/msglog.cpp
index b4143a69a6..e12507d8e0 100644
--- a/plugins/TabSRMM/src/msglog.cpp
+++ b/plugins/TabSRMM/src/msglog.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgoptions.cpp b/plugins/TabSRMM/src/msgoptions.cpp
index b87a801874..b71a71dbc7 100644
--- a/plugins/TabSRMM/src/msgoptions.cpp
+++ b/plugins/TabSRMM/src/msgoptions.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgs.cpp b/plugins/TabSRMM/src/msgs.cpp
index 7753b418d0..1c4448bff0 100644
--- a/plugins/TabSRMM/src/msgs.cpp
+++ b/plugins/TabSRMM/src/msgs.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/msgs.h b/plugins/TabSRMM/src/msgs.h
index 693f5aa08c..382ad1f54d 100644
--- a/plugins/TabSRMM/src/msgs.h
+++ b/plugins/TabSRMM/src/msgs.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/muchighlight.cpp b/plugins/TabSRMM/src/muchighlight.cpp
index ffa1759fa9..9eb5cd96f5 100644
--- a/plugins/TabSRMM/src/muchighlight.cpp
+++ b/plugins/TabSRMM/src/muchighlight.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/muchighlight.h b/plugins/TabSRMM/src/muchighlight.h
index 197e130859..7338765c40 100644
--- a/plugins/TabSRMM/src/muchighlight.h
+++ b/plugins/TabSRMM/src/muchighlight.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/nen.h b/plugins/TabSRMM/src/nen.h
index bca0b37885..d40d7367b6 100644
--- a/plugins/TabSRMM/src/nen.h
+++ b/plugins/TabSRMM/src/nen.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/selectcontainer.cpp b/plugins/TabSRMM/src/selectcontainer.cpp
index c8189f9622..dc835292ca 100644
--- a/plugins/TabSRMM/src/selectcontainer.cpp
+++ b/plugins/TabSRMM/src/selectcontainer.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendlater.cpp b/plugins/TabSRMM/src/sendlater.cpp
index 1ad1690076..1e8614d1fd 100644
--- a/plugins/TabSRMM/src/sendlater.cpp
+++ b/plugins/TabSRMM/src/sendlater.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendlater.h b/plugins/TabSRMM/src/sendlater.h
index bfe97b5fe6..5dc2a58df3 100644
--- a/plugins/TabSRMM/src/sendlater.h
+++ b/plugins/TabSRMM/src/sendlater.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendqueue.cpp b/plugins/TabSRMM/src/sendqueue.cpp
index 4e8372b7e4..be8566f188 100644
--- a/plugins/TabSRMM/src/sendqueue.cpp
+++ b/plugins/TabSRMM/src/sendqueue.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sendqueue.h b/plugins/TabSRMM/src/sendqueue.h
index 89f32b17f8..7dfb4ff9b1 100644
--- a/plugins/TabSRMM/src/sendqueue.h
+++ b/plugins/TabSRMM/src/sendqueue.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sidebar.cpp b/plugins/TabSRMM/src/sidebar.cpp
index c3014aa210..8f6de0a0f3 100644
--- a/plugins/TabSRMM/src/sidebar.cpp
+++ b/plugins/TabSRMM/src/sidebar.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/sidebar.h b/plugins/TabSRMM/src/sidebar.h
index b02e742d37..2e67eae807 100644
--- a/plugins/TabSRMM/src/sidebar.h
+++ b/plugins/TabSRMM/src/sidebar.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/srmm.cpp b/plugins/TabSRMM/src/srmm.cpp
index 9c9c3c6d2f..388565d140 100644
--- a/plugins/TabSRMM/src/srmm.cpp
+++ b/plugins/TabSRMM/src/srmm.cpp
@@ -1,144 +1,144 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/IM project,
-// all portions of this codebase are copyrighted to the people
-// listed in contributors.txt.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// you should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// plugin loading functions and global exports.
-
-#include "stdafx.h"
-
-LOGFONT lfDefault = { 0 };
-
-/*
- * miranda interfaces
- */
-
-CMPlugin g_plugin;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx = {
-	sizeof(PLUGININFOEX),
-	__PLUGIN_NAME,
-	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
-	__DESCRIPTION,
-	__AUTHOR,
-	__COPYRIGHT,
-	__AUTHORWEB,
-	UNICODE_AWARE,
-	// {6CA5F042-7A7F-47CC-A715-FC8C46FBF434}
-	{ 0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 } }
-};
-
-CMPlugin::CMPlugin() :
-	PLUGIN<CMPlugin>("SRMsg", pluginInfoEx),
-
-	// main settings
-	bAutoMin(SRMSGMOD_T, "AutoMin", false),
-	bAutoCopy(SRMSGMOD_T, "autocopy", true),
-	bAutoTabs(SRMSGMOD_T, "autotabs", true),
-	bAllowTab(SRMSGMOD_T, "tabmode", false),
-	bAutoClose(SRMSGMOD_T, "AutoClose", false),
-	bAutoPopup(SRMSGMOD_T, "AutoPopup", false),
-	bAutoSplit(SRMSGMOD_T, "autosplit", false),
-	bDeleteTemp(SRMSGMOD_T, "deletetemp", false),
-	bUseXStatus(SRMSGMOD_T, "use_xicons", true),
-	bSendFormat(SRMSGMOD_T, "sendformat", false),
-	bHideOnClose(SRMSGMOD_T, "hideonclose", false),
-	bStatusOnTabs(SRMSGMOD_T, "tabstatus", true),
-	bFlashOnClist(SRMSGMOD_T, "flashcl", false),
-	bPasteAndSend(SRMSGMOD_T, "pasteandsend", true),
-	bAutoContainer(SRMSGMOD_T, "autocontainer", true),
-	bAutoSwitchTabs(SRMSGMOD_T, "autoswitchtabs", true),
-	bPopupContainer(SRMSGMOD_T, "cpopup", true),
-	bDetailedTooltips(SRMSGMOD_T, "d_tooltips", false),
-	bUseSameSplitSize(SRMSGMOD_T, "usesamesplitsize", true),
-	bAllowOfflineMultisend(SRMSGMOD_T, "AllowOfflineMultisend", true),
-
-	// advanced options
-	bMetaBar(SRMSGMOD_T, "MetaiconBar", true),
-	bMetaTab(SRMSGMOD_T, "MetaiconTab", true),
-	bShowDesc(SRMSGMOD_T, "ShowClientDescription", false),
-	bCloseSend(SRMSGMOD_T, "adv_AutoClose_2", false),
-	bErrorPopup(SRMSGMOD_T, "adv_ErrorPopups", true),
-	
-	// chat settings
-	bOpenInDefault(CHAT_MODULE, "DefaultContainer", true),
-	bCreateWindowOnHighlight(CHAT_MODULE, "CreateWindowOnHighlight", false),
-	bBBCodeInPopups(CHAT_MODULE, "BBCodeInPopups", false),
-	bClassicIndicators(CHAT_MODULE, "ClassicIndicators", false),
-	bLogClassicIndicators(CHAT_MODULE, "LogClassicIndicators", false),
-	bAlternativeSorting(CHAT_MODULE, "AlternativeSorting", true),
-	bAnnoyingHighlight(CHAT_MODULE, "AnnoyingHighlight", false),
-	bLogSymbols(CHAT_MODULE, "LogSymbols", true),
-	bClickableNicks(CHAT_MODULE, "ClickableNicks", true),
-	bColorizeNicks(CHAT_MODULE, "ColorizeNicks", true),
-	bColorizeNicksInLog(CHAT_MODULE, "ColorizeNicksInLog", true),
-	bScaleIcons(CHAT_MODULE, "ScaleIcons", true),
-	bNewLineAfterNames(CHAT_MODULE, "NewlineAfterNames", false),
-
-	// typing settings
-	bPopups(TypingModule, "TypingPopup", true),
-	bTypingNew(TypingModule, "DefaultTyping", true),
-	bTypingUnknown(TypingModule, "UnknownTyping", false),
-
-	// log options
-	bUseDividers(SRMSGMOD_T, "usedividers", false),
-	bLogStatusChanges(SRMSGMOD_T, "logstatuschanges", false),
-	bDividersUsePopupConfig(SRMSGMOD_T, "div_popupconfig", false)
-{}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_SRMM, MIID_LAST };
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPlugin::Load()
-{
-	SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
-
-	hLogger = RegisterSrmmLog(this, "built-in", LPGENW("tabSRMM internal log"), &logBuilder);
-	
-	Chat_Load();
-
-	return LoadSendRecvMessageModule();
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPlugin::Unload()
-{
-	UnregisterSrmmLog(hLogger);
-	FreeLogFonts();
-	Chat_Unload();
-	int iRet = SplitmsgShutdown();
-	Skin->setupTabCloseBitmap(true);
-	Skin->UnloadAeroTabs();
-	CleanTempFiles();
-	SendLater::shutDown();
-	delete Skin;
-	delete sendQueue;
-	return iRet;
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// plugin loading functions and global exports.
+
+#include "stdafx.h"
+
+LOGFONT lfDefault = { 0 };
+
+/*
+ * miranda interfaces
+ */
+
+CMPlugin g_plugin;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+	sizeof(PLUGININFOEX),
+	__PLUGIN_NAME,
+	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+	__DESCRIPTION,
+	__AUTHOR,
+	__COPYRIGHT,
+	__AUTHORWEB,
+	UNICODE_AWARE,
+	// {6CA5F042-7A7F-47CC-A715-FC8C46FBF434}
+	{ 0x6ca5f042, 0x7a7f, 0x47cc, { 0xa7, 0x15, 0xfc, 0x8c, 0x46, 0xfb, 0xf4, 0x34 } }
+};
+
+CMPlugin::CMPlugin() :
+	PLUGIN<CMPlugin>("SRMsg", pluginInfoEx),
+
+	// main settings
+	bAutoMin(SRMSGMOD_T, "AutoMin", false),
+	bAutoCopy(SRMSGMOD_T, "autocopy", true),
+	bAutoTabs(SRMSGMOD_T, "autotabs", true),
+	bAllowTab(SRMSGMOD_T, "tabmode", false),
+	bAutoClose(SRMSGMOD_T, "AutoClose", false),
+	bAutoPopup(SRMSGMOD_T, "AutoPopup", false),
+	bAutoSplit(SRMSGMOD_T, "autosplit", false),
+	bDeleteTemp(SRMSGMOD_T, "deletetemp", false),
+	bUseXStatus(SRMSGMOD_T, "use_xicons", true),
+	bSendFormat(SRMSGMOD_T, "sendformat", false),
+	bHideOnClose(SRMSGMOD_T, "hideonclose", false),
+	bStatusOnTabs(SRMSGMOD_T, "tabstatus", true),
+	bFlashOnClist(SRMSGMOD_T, "flashcl", false),
+	bPasteAndSend(SRMSGMOD_T, "pasteandsend", true),
+	bAutoContainer(SRMSGMOD_T, "autocontainer", true),
+	bAutoSwitchTabs(SRMSGMOD_T, "autoswitchtabs", true),
+	bPopupContainer(SRMSGMOD_T, "cpopup", true),
+	bDetailedTooltips(SRMSGMOD_T, "d_tooltips", false),
+	bUseSameSplitSize(SRMSGMOD_T, "usesamesplitsize", true),
+	bAllowOfflineMultisend(SRMSGMOD_T, "AllowOfflineMultisend", true),
+
+	// advanced options
+	bMetaBar(SRMSGMOD_T, "MetaiconBar", true),
+	bMetaTab(SRMSGMOD_T, "MetaiconTab", true),
+	bShowDesc(SRMSGMOD_T, "ShowClientDescription", false),
+	bCloseSend(SRMSGMOD_T, "adv_AutoClose_2", false),
+	bErrorPopup(SRMSGMOD_T, "adv_ErrorPopups", true),
+	
+	// chat settings
+	bOpenInDefault(CHAT_MODULE, "DefaultContainer", true),
+	bCreateWindowOnHighlight(CHAT_MODULE, "CreateWindowOnHighlight", false),
+	bBBCodeInPopups(CHAT_MODULE, "BBCodeInPopups", false),
+	bClassicIndicators(CHAT_MODULE, "ClassicIndicators", false),
+	bLogClassicIndicators(CHAT_MODULE, "LogClassicIndicators", false),
+	bAlternativeSorting(CHAT_MODULE, "AlternativeSorting", true),
+	bAnnoyingHighlight(CHAT_MODULE, "AnnoyingHighlight", false),
+	bLogSymbols(CHAT_MODULE, "LogSymbols", true),
+	bClickableNicks(CHAT_MODULE, "ClickableNicks", true),
+	bColorizeNicks(CHAT_MODULE, "ColorizeNicks", true),
+	bColorizeNicksInLog(CHAT_MODULE, "ColorizeNicksInLog", true),
+	bScaleIcons(CHAT_MODULE, "ScaleIcons", true),
+	bNewLineAfterNames(CHAT_MODULE, "NewlineAfterNames", false),
+
+	// typing settings
+	bPopups(TypingModule, "TypingPopup", true),
+	bTypingNew(TypingModule, "DefaultTyping", true),
+	bTypingUnknown(TypingModule, "UnknownTyping", false),
+
+	// log options
+	bUseDividers(SRMSGMOD_T, "usedividers", false),
+	bLogStatusChanges(SRMSGMOD_T, "logstatuschanges", false),
+	bDividersUsePopupConfig(SRMSGMOD_T, "div_popupconfig", false)
+{}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_SRMM, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Load()
+{
+	SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfDefault), &lfDefault, FALSE);
+
+	hLogger = RegisterSrmmLog(this, "built-in", LPGENW("tabSRMM internal log"), &logBuilder);
+	
+	Chat_Load();
+
+	return LoadSendRecvMessageModule();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+	UnregisterSrmmLog(hLogger);
+	FreeLogFonts();
+	Chat_Unload();
+	int iRet = SplitmsgShutdown();
+	Skin->setupTabCloseBitmap(true);
+	Skin->UnloadAeroTabs();
+	CleanTempFiles();
+	SendLater::shutDown();
+	delete Skin;
+	delete sendQueue;
+	return iRet;
+}
diff --git a/plugins/TabSRMM/src/stdafx.cxx b/plugins/TabSRMM/src/stdafx.cxx
index f64d25234b..ebbde0ade1 100644
--- a/plugins/TabSRMM/src/stdafx.cxx
+++ b/plugins/TabSRMM/src/stdafx.cxx
@@ -1,5 +1,5 @@
 /*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
diff --git a/plugins/TabSRMM/src/stdafx.h b/plugins/TabSRMM/src/stdafx.h
index e09f24597b..4c87b4c428 100644
--- a/plugins/TabSRMM/src/stdafx.h
+++ b/plugins/TabSRMM/src/stdafx.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/tabctrl.cpp b/plugins/TabSRMM/src/tabctrl.cpp
index 29d2b0511b..35523e14f7 100644
--- a/plugins/TabSRMM/src/tabctrl.cpp
+++ b/plugins/TabSRMM/src/tabctrl.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/taskbar.cpp b/plugins/TabSRMM/src/taskbar.cpp
index a6eb6b8a2d..d49d937af5 100644
--- a/plugins/TabSRMM/src/taskbar.cpp
+++ b/plugins/TabSRMM/src/taskbar.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/taskbar.h b/plugins/TabSRMM/src/taskbar.h
index 92f8dcf85d..ed407dadbd 100644
--- a/plugins/TabSRMM/src/taskbar.h
+++ b/plugins/TabSRMM/src/taskbar.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/templates.cpp b/plugins/TabSRMM/src/templates.cpp
index 369c5ee4fe..a27a3df00f 100644
--- a/plugins/TabSRMM/src/templates.cpp
+++ b/plugins/TabSRMM/src/templates.cpp
@@ -1,124 +1,124 @@
-/////////////////////////////////////////////////////////////////////////////////////////
-// Miranda NG: the free IM client for Microsoft* Windows*
-//
-// Copyright (C) 2012-22 Miranda NG team,
-// Copyright (c) 2000-09 Miranda ICQ/IM project,
-// all portions of this codebase are copyrighted to the people
-// listed in contributors.txt.
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// you should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-//
-// part of tabSRMM messaging plugin for Miranda.
-//
-// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
-//
-// Simple editor for the message log templates
-
-#include "stdafx.h"
-
-/*
-* hardcoded default set of templates for both LTR and RTL.
-* cannot be changed and may be used at any time to "revert" to a working layout
-*/
-
-char* TemplateNames[TMPL_MAX] =
-{
-	LPGEN("Message In"),
-	LPGEN("Message Out"),
-	LPGEN("Group In (Start)"),
-	LPGEN("Group Out (Start)"),
-	LPGEN("Group In (Inner)"),
-	LPGEN("Group Out (Inner)"),
-	LPGEN("Status change"),
-	LPGEN("Error message")
-};
-
-wchar_t* TemplateNamesW[TMPL_MAX] =
-{
-	LPGENW("Message In"),
-	LPGENW("Message Out"),
-	LPGENW("Group In (Start)"),
-	LPGENW("Group Out (Start)"),
-	LPGENW("Group In (Inner)"),
-	LPGENW("Group Out (Inner)"),
-	LPGENW("Status change"),
-	LPGENW("Error message")
-};
-
-TTemplateSet LTR_Default = 
-{
-	TRUE,
-	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
-	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
-	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
-	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
-	L"%S %T%|%M",
-	L"%S %T%|%M",
-	L"%I %S %&r, %&T, %N %M%! ",
-	L"%I%S %r, %T, %e%l%M",
-	"Default LTR"
-};
-
-TTemplateSet RTL_Default =
-{
-	TRUE,
-	L"%I %N  %r%n%S %T%|%M",
-	L"%I %N  %r%n%S %T%|%M",
-	L"%I %N  %r%n%S %T%|%M",
-	L"%I %N  %r%n%S %T%|%M",
-	L"%S %T%|%M",
-	L"%S %T%|%M",
-	L"%I%S %r, %T, %N %M%! ",
-	L"%I%S %r, %T, %e%l%M",
-	"Default RTL"
-};
-
-TTemplateSet LTR_Active, RTL_Active;
-
-/*
-* loads template set overrides from hContact into the given set of already existing
-* templates
-*/
-
-static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl)
-{
-	for (int i = 0; i < TMPL_MAX; i++) {
-		DBVARIANT dbv = { 0 };
-		if (db_get_ws(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv))
-			continue;
-		if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR)
-			wcsncpy_s(tSet->szTemplates[i], dbv.pwszVal, _TRUNCATE);
-		db_free(&dbv);
-	}
-}
-
-void LoadDefaultTemplates()
-{
-	LTR_Active = LTR_Default;
-	RTL_Active = RTL_Default;
-
-	if (db_get_b(0, RTLTEMPLATES_MODULE, "setup", 0) < 2) {
-		for (int i = 0; i < TMPL_MAX; i++)
-			db_set_ws(0, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]);
-		db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2);
-	}
-	if (db_get_b(0, TEMPLATES_MODULE, "setup", 0) < 2) {
-		for (int i = 0; i < TMPL_MAX; i++)
-			db_set_ws(0, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]);
-		db_set_b(0, TEMPLATES_MODULE, "setup", 2);
-	}
-	LoadTemplatesFrom(&LTR_Active, 0, 0);
-	LoadTemplatesFrom(&RTL_Active, 0, 1);
-}
+/////////////////////////////////////////////////////////////////////////////////////////
+// Miranda NG: the free IM client for Microsoft* Windows*
+//
+// Copyright (C) 2012-23 Miranda NG team,
+// Copyright (c) 2000-09 Miranda ICQ/IM project,
+// all portions of this codebase are copyrighted to the people
+// listed in contributors.txt.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// you should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// part of tabSRMM messaging plugin for Miranda.
+//
+// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
+//
+// Simple editor for the message log templates
+
+#include "stdafx.h"
+
+/*
+* hardcoded default set of templates for both LTR and RTL.
+* cannot be changed and may be used at any time to "revert" to a working layout
+*/
+
+char* TemplateNames[TMPL_MAX] =
+{
+	LPGEN("Message In"),
+	LPGEN("Message Out"),
+	LPGEN("Group In (Start)"),
+	LPGEN("Group Out (Start)"),
+	LPGEN("Group In (Inner)"),
+	LPGEN("Group Out (Inner)"),
+	LPGEN("Status change"),
+	LPGEN("Error message")
+};
+
+wchar_t* TemplateNamesW[TMPL_MAX] =
+{
+	LPGENW("Message In"),
+	LPGENW("Message Out"),
+	LPGENW("Group In (Start)"),
+	LPGENW("Group Out (Start)"),
+	LPGENW("Group In (Inner)"),
+	LPGENW("Group Out (Inner)"),
+	LPGENW("Status change"),
+	LPGENW("Error message")
+};
+
+TTemplateSet LTR_Default = 
+{
+	TRUE,
+	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+	L"%I %N  %?&r%\\&E%\\!, %\\T%\\!: %?n%?S %?T%?|%M",
+	L"%S %T%|%M",
+	L"%S %T%|%M",
+	L"%I %S %&r, %&T, %N %M%! ",
+	L"%I%S %r, %T, %e%l%M",
+	"Default LTR"
+};
+
+TTemplateSet RTL_Default =
+{
+	TRUE,
+	L"%I %N  %r%n%S %T%|%M",
+	L"%I %N  %r%n%S %T%|%M",
+	L"%I %N  %r%n%S %T%|%M",
+	L"%I %N  %r%n%S %T%|%M",
+	L"%S %T%|%M",
+	L"%S %T%|%M",
+	L"%I%S %r, %T, %N %M%! ",
+	L"%I%S %r, %T, %e%l%M",
+	"Default RTL"
+};
+
+TTemplateSet LTR_Active, RTL_Active;
+
+/*
+* loads template set overrides from hContact into the given set of already existing
+* templates
+*/
+
+static void LoadTemplatesFrom(TTemplateSet *tSet, MCONTACT hContact, int rtl)
+{
+	for (int i = 0; i < TMPL_MAX; i++) {
+		DBVARIANT dbv = { 0 };
+		if (db_get_ws(hContact, rtl ? RTLTEMPLATES_MODULE : TEMPLATES_MODULE, TemplateNames[i], &dbv))
+			continue;
+		if (dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_WCHAR)
+			wcsncpy_s(tSet->szTemplates[i], dbv.pwszVal, _TRUNCATE);
+		db_free(&dbv);
+	}
+}
+
+void LoadDefaultTemplates()
+{
+	LTR_Active = LTR_Default;
+	RTL_Active = RTL_Default;
+
+	if (db_get_b(0, RTLTEMPLATES_MODULE, "setup", 0) < 2) {
+		for (int i = 0; i < TMPL_MAX; i++)
+			db_set_ws(0, RTLTEMPLATES_MODULE, TemplateNames[i], RTL_Default.szTemplates[i]);
+		db_set_b(0, RTLTEMPLATES_MODULE, "setup", 2);
+	}
+	if (db_get_b(0, TEMPLATES_MODULE, "setup", 0) < 2) {
+		for (int i = 0; i < TMPL_MAX; i++)
+			db_set_ws(0, TEMPLATES_MODULE, TemplateNames[i], LTR_Default.szTemplates[i]);
+		db_set_b(0, TEMPLATES_MODULE, "setup", 2);
+	}
+	LoadTemplatesFrom(&LTR_Active, 0, 0);
+	LoadTemplatesFrom(&RTL_Active, 0, 1);
+}
diff --git a/plugins/TabSRMM/src/themeio.cpp b/plugins/TabSRMM/src/themeio.cpp
index 7648b8f90b..deaecbfcc6 100644
--- a/plugins/TabSRMM/src/themeio.cpp
+++ b/plugins/TabSRMM/src/themeio.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/themes.cpp b/plugins/TabSRMM/src/themes.cpp
index 14a762cc0a..7c48a65d5e 100644
--- a/plugins/TabSRMM/src/themes.cpp
+++ b/plugins/TabSRMM/src/themes.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/themes.h b/plugins/TabSRMM/src/themes.h
index 471c1a73f5..2e7bd1bf1c 100644
--- a/plugins/TabSRMM/src/themes.h
+++ b/plugins/TabSRMM/src/themes.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/userprefs.cpp b/plugins/TabSRMM/src/userprefs.cpp
index 573f2b883e..880112e041 100644
--- a/plugins/TabSRMM/src/userprefs.cpp
+++ b/plugins/TabSRMM/src/userprefs.cpp
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/utils.cpp b/plugins/TabSRMM/src/utils.cpp
index 74fab10f8a..484b5dca1d 100644
--- a/plugins/TabSRMM/src/utils.cpp
+++ b/plugins/TabSRMM/src/utils.cpp
@@ -1,7 +1,7 @@
 ////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/utils.h b/plugins/TabSRMM/src/utils.h
index f60cebe2e3..66716896ba 100644
--- a/plugins/TabSRMM/src/utils.h
+++ b/plugins/TabSRMM/src/utils.h
@@ -1,7 +1,7 @@
 /////////////////////////////////////////////////////////////////////////////////////////
 // Miranda NG: the free IM client for Microsoft* Windows*
 //
-// Copyright (C) 2012-22 Miranda NG team,
+// Copyright (C) 2012-23 Miranda NG team,
 // Copyright (c) 2000-09 Miranda ICQ/IM project,
 // all portions of this codebase are copyrighted to the people
 // listed in contributors.txt.
diff --git a/plugins/TabSRMM/src/version.h b/plugins/TabSRMM/src/version.h
index 37a59b1f3a..376079948b 100644
--- a/plugins/TabSRMM/src/version.h
+++ b/plugins/TabSRMM/src/version.h
@@ -10,4 +10,4 @@
 #define __DESCRIPTION         "IM and group chat module for Miranda NG."
 #define __AUTHOR              "The Miranda developers team and contributors"
 #define __AUTHORWEB           "https://miranda-ng.org/p/TabSRMM"
-#define __COPYRIGHT           "© 2012-22 Miranda NG team, 2000-2010 Miranda Project and contributors."
+#define __COPYRIGHT           "© 2012-23 Miranda NG team, 2000-2010 Miranda Project and contributors."
diff --git a/plugins/TabSRMM/src/warning.cpp b/plugins/TabSRMM/src/warning.cpp
index c5bf30736f..fe93ea374c 100644
--- a/plugins/TabSRMM/src/warning.cpp
+++ b/plugins/TabSRMM/src/warning.cpp
@@ -1,328 +1,328 @@
-/*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-
-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 version 2
-of the License.
-
-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, see <http://www.gnu.org/licenses/>.
-*/
-
-#include "stdafx.h"
-
-using namespace CWarning;
-
-static MWindowList hWindowList;
-
-class CWarningImpl
-{
-	ptrW     m_szTitle, m_szText;
-	UINT     m_uId;
-	HFONT    m_hFontCaption = nullptr;
-	uint32_t m_dwFlags;
-	HWND     m_hwnd = nullptr;
-	bool     m_fIsModal;
-
-public:
-	CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) :
-		m_szTitle(mir_wstrdup(tszTitle)),
-		m_szText(mir_wstrdup(tszText))
-	{
-		m_uId = uId;
-		m_dwFlags = dwFlags;
-		m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
-	}
-
-	~CWarningImpl()
-	{
-		if (m_hFontCaption)
-			::DeleteObject(m_hFontCaption);
-	}
-
-	// static function to construct and show the dialog, returns the user's choice
-	LRESULT ShowDialog() const
-	{
-		if (!m_fIsModal) {
-			::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
-			return 0;
-		}
-
-		return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
-	}
-
-	/////////////////////////////////////////////////////////////////////////////////////////
-	// stub dlg procedure.Just register the object pointer in WM_INITDIALOG
-
-	static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-	{
-		CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
-		if (w)
-			return(w->dlgProc(hwnd, msg, wParam, lParam));
-
-		switch (msg) {
-		case WM_INITDIALOG:
-			w = reinterpret_cast<CWarningImpl *>(lParam);
-			if (w) {
-				::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
-				return(w->dlgProc(hwnd, msg, wParam, lParam));
-			}
-			break;
-		}
-		return FALSE;
-	}
-
-	/////////////////////////////////////////////////////////////////////////////////////////
-	// dialog procedure for the warning dialog box
-
-	INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-	{
-		switch (msg) {
-		case WM_INITDIALOG:
-			m_hwnd = hwnd;
-
-			::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message"));
-			::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA);
-			::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0);
-			::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
-
-			TranslateDialogDefault(hwnd);
-			{
-				CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15);
-				str.Append(m_szText);
-				str.Append(L"}");
-				str.Replace(L"\n", L"\\line ");
-				SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
-				::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str));
-
-				::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle);
-
-				if (m_dwFlags & CWF_NOALLOWHIDE)
-					Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
-				if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
-					Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
-					::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
-				}
-				else {
-					Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
-					Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
-					Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
-					::SetFocus(::GetDlgItem(hwnd, IDOK));
-				}
-
-				UINT uResId = 0;
-				if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND))
-					uResId = 32513;
-				else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING))
-					uResId = 32515;
-				else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION))
-					uResId = 32516;
-				else if (m_dwFlags & MB_ICONQUESTION)
-					uResId = 32514;
-
-				HICON hIcon;
-				if (uResId)
-					hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
-				else
-					hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true);
-				::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);
-
-				if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
-					::ShowWindow(hwnd, SW_SHOWNORMAL);
-
-				WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd);
-			}
-			return TRUE;
-
-		case WM_CTLCOLORSTATIC:
-			{
-				HWND hwndChild = reinterpret_cast<HWND>(lParam);
-				UINT id = ::GetDlgCtrlID(hwndChild);
-				if (nullptr == m_hFontCaption) {
-					HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
-					LOGFONT lf = {0};
-
-					::GetObject(hFont, sizeof(lf), &lf);
-					lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
-					m_hFontCaption = ::CreateFontIndirect(&lf);
-					::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
-				}
-
-				if (IDC_CAPTION == id) {
-					::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
-					::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
-				}
-
-				if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
-					::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
-					return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
-				}
-			}
-			break;
-
-		case WM_COMMAND:
-			switch (LOWORD(wParam)) {
-			case IDOK:
-			case IDYES:
-			case IDNO:
-				if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
-					uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId);
-					db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal);
-
-					if (LOWORD(wParam) != IDNO) {
-						newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId);
-						db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal);
-					}
-				}
-				__fallthrough;
-
-			case IDCANCEL:
-				if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll()
-					::DestroyWindow(hwnd);
-				else
-					::EndDialog(hwnd, LOWORD(wParam));
-				break;
-			}
-			break;
-
-		case WM_NOTIFY:
-			switch (((NMHDR *)lParam)->code) {
-			case EN_LINK:
-				switch (((ENLINK *)lParam)->msg) {
-				case WM_LBUTTONUP:
-					ENLINK *e = reinterpret_cast<ENLINK *>(lParam);
-
-					const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
-					if (wszUrl) {
-						Utils_OpenUrlW(wszUrl);
-						mir_free(const_cast<wchar_t *>(wszUrl));
-					}
-				}
-			}
-			break;
-
-		case WM_DESTROY:
-			::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
-			delete this;
-
-			WindowList_Remove(hWindowList, hwnd);
-			Window_FreeIcon_IcoLib(hwnd);
-			break;
-		}
-
-		return FALSE;
-	}
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// implementation of the CWarningImpl class
-//
-// IMPORTANT note to translators for translation of the warning dialogs:
-// 
-//  Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
-//  warning title from the actual warning text.
-// 
-//  Also, do NOT insert multiple | characters in the translated string. Not well-formatted
-//  warnings cannot be translated and the plugin will show the untranslated versions.
-// 
-//  strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
-//  will be used for important and critical error messages only.
-// 
-//  some strings are empty, this is intentional and used for error messages that share
-//  the message with other possible error notifications (popups, tool tips etc.)
-// 
-//  Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them.
-
-static wchar_t *Warnings[] = {
-	nullptr,
-	LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE 
-	LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"),  /* WARN_EDITUSERNOTES */
-	LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"),		/* WARN_ICONPACKMISSING */
-	LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."),	/* WARN_AEROPEEKSKIN */
-	LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */
-	LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0  is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
-	LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0  and \\b1 send to multiple contacts\\b0  features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */
-	LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"),		/* WARN_CLOSEWINDOW */
-	LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
-	LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
-};
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// send cancel message to all open warning dialogs so they are destroyed
-// before TabSRMM is unloaded.
-// 
-// called by the OkToExit handler in globals.cpp
-
-void CWarning::destroyAll()
-{
-	if (hWindowList)
-		WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// show a warning dialog using the id value. Check whether the user has chosen to
-// not show this message again. This has room for 64 different warning dialogs, which
-// should be enough in the first place. Extending it should not be too hard though.
-
-LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt)
-{
-	if (hWindowList == nullptr)
-		hWindowList = WindowList_Create();
-
-	// don't open new warnings when shutdown was initiated (modal ones will otherwise
-	// block the shutdown)
-	if (CMimAPI::m_shutDown)
-		return -1;
-
-	wchar_t *_s = nullptr;
-	if (tszTxt)
-		_s = const_cast<wchar_t *>(tszTxt);
-	else {
-		if (uId == -1)
-			return -1;
-
-		if (dwFlags & CWF_UNTRANSLATED)
-			_s = TranslateW(Warnings[uId]);
-		else {
-			// revert to untranslated warning when the translated message
-			// is not well-formatted.
-			_s = TranslateW(Warnings[uId]);
-
-			if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|'))
-				_s = TranslateW(Warnings[uId]);
-		}
-	}
-
-	if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) {
-		if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) {
-			uint32_t val = M.GetDword("cWarningsL", 0);
-			uint32_t mask = ((__int64)1L) << uId;
-			if (mask & val) {
-				bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0;
-				if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
-					return (bResult) ? IDYES : IDNO;
-				return IDOK;
-			}
-		}
-
-		ptrW s(mir_wstrdup(_s));
-		wchar_t *separator_pos = wcschr(s, '|');
-
-		if (separator_pos) {
-			*separator_pos = 0;
-
-			CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags);
-			if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
-				return w->ShowDialog();
-
-			w->ShowDialog();
-		}
-	}
-	return -1;
-}
+/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+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 version 2
+of the License.
+
+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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+using namespace CWarning;
+
+static MWindowList hWindowList;
+
+class CWarningImpl
+{
+	ptrW     m_szTitle, m_szText;
+	UINT     m_uId;
+	HFONT    m_hFontCaption = nullptr;
+	uint32_t m_dwFlags;
+	HWND     m_hwnd = nullptr;
+	bool     m_fIsModal;
+
+public:
+	CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) :
+		m_szTitle(mir_wstrdup(tszTitle)),
+		m_szText(mir_wstrdup(tszText))
+	{
+		m_uId = uId;
+		m_dwFlags = dwFlags;
+		m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
+	}
+
+	~CWarningImpl()
+	{
+		if (m_hFontCaption)
+			::DeleteObject(m_hFontCaption);
+	}
+
+	// static function to construct and show the dialog, returns the user's choice
+	LRESULT ShowDialog() const
+	{
+		if (!m_fIsModal) {
+			::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
+			return 0;
+		}
+
+		return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
+	}
+
+	/////////////////////////////////////////////////////////////////////////////////////////
+	// stub dlg procedure.Just register the object pointer in WM_INITDIALOG
+
+	static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+	{
+		CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
+		if (w)
+			return(w->dlgProc(hwnd, msg, wParam, lParam));
+
+		switch (msg) {
+		case WM_INITDIALOG:
+			w = reinterpret_cast<CWarningImpl *>(lParam);
+			if (w) {
+				::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+				return(w->dlgProc(hwnd, msg, wParam, lParam));
+			}
+			break;
+		}
+		return FALSE;
+	}
+
+	/////////////////////////////////////////////////////////////////////////////////////////
+	// dialog procedure for the warning dialog box
+
+	INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+	{
+		switch (msg) {
+		case WM_INITDIALOG:
+			m_hwnd = hwnd;
+
+			::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message"));
+			::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA);
+			::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0);
+			::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
+
+			TranslateDialogDefault(hwnd);
+			{
+				CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15);
+				str.Append(m_szText);
+				str.Append(L"}");
+				str.Replace(L"\n", L"\\line ");
+				SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
+				::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str));
+
+				::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle);
+
+				if (m_dwFlags & CWF_NOALLOWHIDE)
+					Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
+				if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
+					Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
+					::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
+				}
+				else {
+					Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
+					Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
+					Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
+					::SetFocus(::GetDlgItem(hwnd, IDOK));
+				}
+
+				UINT uResId = 0;
+				if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND))
+					uResId = 32513;
+				else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING))
+					uResId = 32515;
+				else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION))
+					uResId = 32516;
+				else if (m_dwFlags & MB_ICONQUESTION)
+					uResId = 32514;
+
+				HICON hIcon;
+				if (uResId)
+					hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE));
+				else
+					hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true);
+				::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);
+
+				if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
+					::ShowWindow(hwnd, SW_SHOWNORMAL);
+
+				WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd);
+			}
+			return TRUE;
+
+		case WM_CTLCOLORSTATIC:
+			{
+				HWND hwndChild = reinterpret_cast<HWND>(lParam);
+				UINT id = ::GetDlgCtrlID(hwndChild);
+				if (nullptr == m_hFontCaption) {
+					HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
+					LOGFONT lf = {0};
+
+					::GetObject(hFont, sizeof(lf), &lf);
+					lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
+					m_hFontCaption = ::CreateFontIndirect(&lf);
+					::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+				}
+
+				if (IDC_CAPTION == id) {
+					::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
+					::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
+				}
+
+				if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
+					::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
+					return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
+				}
+			}
+			break;
+
+		case WM_COMMAND:
+			switch (LOWORD(wParam)) {
+			case IDOK:
+			case IDYES:
+			case IDNO:
+				if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
+					uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId);
+					db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal);
+
+					if (LOWORD(wParam) != IDNO) {
+						newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId);
+						db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal);
+					}
+				}
+				__fallthrough;
+
+			case IDCANCEL:
+				if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll()
+					::DestroyWindow(hwnd);
+				else
+					::EndDialog(hwnd, LOWORD(wParam));
+				break;
+			}
+			break;
+
+		case WM_NOTIFY:
+			switch (((NMHDR *)lParam)->code) {
+			case EN_LINK:
+				switch (((ENLINK *)lParam)->msg) {
+				case WM_LBUTTONUP:
+					ENLINK *e = reinterpret_cast<ENLINK *>(lParam);
+
+					const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
+					if (wszUrl) {
+						Utils_OpenUrlW(wszUrl);
+						mir_free(const_cast<wchar_t *>(wszUrl));
+					}
+				}
+			}
+			break;
+
+		case WM_DESTROY:
+			::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+			delete this;
+
+			WindowList_Remove(hWindowList, hwnd);
+			Window_FreeIcon_IcoLib(hwnd);
+			break;
+		}
+
+		return FALSE;
+	}
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// implementation of the CWarningImpl class
+//
+// IMPORTANT note to translators for translation of the warning dialogs:
+// 
+//  Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
+//  warning title from the actual warning text.
+// 
+//  Also, do NOT insert multiple | characters in the translated string. Not well-formatted
+//  warnings cannot be translated and the plugin will show the untranslated versions.
+// 
+//  strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
+//  will be used for important and critical error messages only.
+// 
+//  some strings are empty, this is intentional and used for error messages that share
+//  the message with other possible error notifications (popups, tool tips etc.)
+// 
+//  Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them.
+
+static wchar_t *Warnings[] = {
+	nullptr,
+	LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE 
+	LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"),  /* WARN_EDITUSERNOTES */
+	LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"),		/* WARN_ICONPACKMISSING */
+	LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."),	/* WARN_AEROPEEKSKIN */
+	LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */
+	LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0  is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
+	LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0  and \\b1 send to multiple contacts\\b0  features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */
+	LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"),		/* WARN_CLOSEWINDOW */
+	LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
+	LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// send cancel message to all open warning dialogs so they are destroyed
+// before TabSRMM is unloaded.
+// 
+// called by the OkToExit handler in globals.cpp
+
+void CWarning::destroyAll()
+{
+	if (hWindowList)
+		WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// show a warning dialog using the id value. Check whether the user has chosen to
+// not show this message again. This has room for 64 different warning dialogs, which
+// should be enough in the first place. Extending it should not be too hard though.
+
+LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt)
+{
+	if (hWindowList == nullptr)
+		hWindowList = WindowList_Create();
+
+	// don't open new warnings when shutdown was initiated (modal ones will otherwise
+	// block the shutdown)
+	if (CMimAPI::m_shutDown)
+		return -1;
+
+	wchar_t *_s = nullptr;
+	if (tszTxt)
+		_s = const_cast<wchar_t *>(tszTxt);
+	else {
+		if (uId == -1)
+			return -1;
+
+		if (dwFlags & CWF_UNTRANSLATED)
+			_s = TranslateW(Warnings[uId]);
+		else {
+			// revert to untranslated warning when the translated message
+			// is not well-formatted.
+			_s = TranslateW(Warnings[uId]);
+
+			if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|'))
+				_s = TranslateW(Warnings[uId]);
+		}
+	}
+
+	if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) {
+		if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) {
+			uint32_t val = M.GetDword("cWarningsL", 0);
+			uint32_t mask = ((__int64)1L) << uId;
+			if (mask & val) {
+				bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0;
+				if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
+					return (bResult) ? IDYES : IDNO;
+				return IDOK;
+			}
+		}
+
+		ptrW s(mir_wstrdup(_s));
+		wchar_t *separator_pos = wcschr(s, '|');
+
+		if (separator_pos) {
+			*separator_pos = 0;
+
+			CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags);
+			if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
+				return w->ShowDialog();
+
+			w->ShowDialog();
+		}
+	}
+	return -1;
+}
-- 
cgit v1.2.3