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/QuickSearch/src/main.cpp        |  376 +++----
 plugins/QuickSearch/src/options.cpp     | 1038 +++++++++---------
 plugins/QuickSearch/src/stdafx.cxx      |   34 +-
 plugins/QuickSearch/src/utils.cpp       | 1112 ++++++++++----------
 plugins/QuickSearch/src/version.h       |    2 +-
 plugins/QuickSearch/src/window.cpp      | 1752 +++++++++++++++----------------
 plugins/QuickSearch/src/window_misc.cpp | 1750 +++++++++++++++---------------
 plugins/QuickSearch/src/window_row.cpp  |  448 ++++----
 8 files changed, 3256 insertions(+), 3256 deletions(-)

(limited to 'plugins/QuickSearch')

diff --git a/plugins/QuickSearch/src/main.cpp b/plugins/QuickSearch/src/main.cpp
index cf068d7561..632bfb2aff 100644
--- a/plugins/QuickSearch/src/main.cpp
+++ b/plugins/QuickSearch/src/main.cpp
@@ -1,188 +1,188 @@
-/*
-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"
-
-CMPlugin g_plugin;
-
-HANDLE hTTBButton;
-
-bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled;
-
-int OnOptInit(WPARAM, LPARAM);
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-PLUGININFOEX pluginInfoEx = {
-	sizeof(PLUGININFOEX),
-	__PLUGIN_NAME,
-	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
-	__DESCRIPTION,
-	__AUTHOR,
-	__COPYRIGHT,
-	__AUTHORWEB,
-	UNICODE_AWARE,
-	// {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C}
-	{0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}}
-};
-
-CMPlugin::CMPlugin() :
-	PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
-	m_columns(1)
-{
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static int OnTTBLoaded(WPARAM, LPARAM)
-{
-	TTBButton ttb = {};
-	ttb.dwFlags = TTBBF_VISIBLE;
-	ttb.pszService = QS_SHOWSERVICE;
-	ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS);
-	ttb.name = MODULENAME;
-	ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search");
-	hTTBButton = g_plugin.addTTB(&ttb);
-	return 0;
-}
-
-static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM)
-{
-	OpenSrWindow((wchar_t *)wParam);
-	return 0;
-}
-
-static int OnCheckPlugins(WPARAM, LPARAM)
-{
-	g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING);
-	g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW);
-	g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW);
-
-	return 0;
-}
-
-static int OnModulesLoaded(WPARAM, LPARAM)
-{
-	HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded);
-
-	CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow);
-
-	// add menu item
-	CMenuItem mi(&g_plugin);
-	SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0);
-	mi.name.a = LPGEN("Quick Search");
-	mi.position = 500050000;
-	mi.pszService = QS_SHOWSERVICE;
-	mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS);
-	Menu_AddMainMenuItem(&mi);
-
-	// register hotkey
-	HOTKEYDESC hkd = {};
-	hkd.pszName = "QS_Global";
-	hkd.szDescription.a = LPGEN("Open Quick Search window");
-	hkd.szSection.a = LPGEN("Quick Search");
-	hkd.pszService = QS_SHOWSERVICE;
-	hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3);
-	g_plugin.addHotkey(&hkd);
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static IconItem iconList[] =
-{
-	{ LPGEN("Quick Search"),  "QS",      IDI_QS     },
-	{ LPGEN("New Column"),    "New",     IDI_NEW    },
-	{ LPGEN("Column Up"),     "Up",      IDI_UP     },
-	{ LPGEN("Column Down"),   "Down",    IDI_DOWN   },
-	{ LPGEN("Delete Column"), "Delete",  IDI_DELETE },
-	{ LPGEN("Default"),       "Default", IDI_DEFAULT},
-	{ LPGEN("Reload"),        "Reload",  IDI_RELOAD },
-	{ LPGEN("Male"),          "Male",    IDI_MALE   },
-	{ LPGEN("Female"),        "Female",  IDI_FEMALE },
-};
-
-struct
-{
-	COLORREF defValue;
-	const char *szSetting, *szDescr;
-}
-static sttColors[color_max] = {
-	{ 0x00FFFFFF, "back_norm", LPGEN("Normal background")           },
-	{ 0x00000000, "fore_norm", LPGEN("Normal foreground")           },
-	{ 0x00EBE6DE, "back_odd" , LPGEN("Odd background")              },
-	{ 0x00000000, "fore_odd" , LPGEN("Odd foreground")              },
-	{ 0x008080FF, "back_dis" , LPGEN("Disabled account background") },
-	{ 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") },
-	{ 0x008000FF, "back_del" , LPGEN("Deleted account background")  },
-	{ 0x00000000, "fore_del" , LPGEN("Deleted account foreground")  },
-	{ 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background")   },
-	{ 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground")   },
-	{ 0x00BAE699, "back_meta", LPGEN("Metacontact background")      },
-	{ 0x00000000, "fore_meta", LPGEN("Metacontact foreground")      },
-	{ 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background")       },
-	{ 0x00000000, "fore_sub" , LPGEN("Subcontact foreground")       },
-};
-
-static int OnColorReload(WPARAM, LPARAM)
-{
-	for (int i = 0; i < color_max; i++)
-		g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szDescr);
-	return 0;
-}
-
-int CMPlugin::Load()
-{
-	g_plugin.registerIcon(MODULENAME, iconList);
-
-	ColourID colourid = {};
-	strncpy_s(colourid.group, MODULENAME, _TRUNCATE);
-	strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE);
-
-	for (auto &it : sttColors) {
-		strncpy_s(colourid.name, it.szDescr, _TRUNCATE);
-		strncpy_s(colourid.setting, it.szSetting, _TRUNCATE);
-		colourid.defcolour = it.defValue;
-		colourid.order = int(&it - sttColors);
-		g_plugin.addColor(&colourid);
-	}
-	OnColorReload(0, 0);
-	OnCheckPlugins(0, 0);
-
-	HookEvent(ME_COLOUR_RELOAD, OnColorReload);
-	HookEvent(ME_OPT_INITIALISE, OnOptInit);
-	HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
-	HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
-	HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins);
-
-	if (!LoadColumns(m_columns))
-		LoadDefaultColumns(m_columns);
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CMPlugin::Unload()
-{
-	if (hTTBButton) {
-		CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0);
-		hTTBButton = 0;
-	}
-
-	CloseSrWindow();
-	return 0;
-}
+/*
+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"
+
+CMPlugin g_plugin;
+
+HANDLE hTTBButton;
+
+bool g_bVarsInstalled, g_bTipperInstalled, g_bFingerInstalled;
+
+int OnOptInit(WPARAM, LPARAM);
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+PLUGININFOEX pluginInfoEx = {
+	sizeof(PLUGININFOEX),
+	__PLUGIN_NAME,
+	PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+	__DESCRIPTION,
+	__AUTHOR,
+	__COPYRIGHT,
+	__AUTHORWEB,
+	UNICODE_AWARE,
+	// {49BD9F2A-3111-4EB9-87E3-71E69CD97F7C}
+	{0x49bd9f2a, 0x3111, 0x4eb9, {0x87, 0xe3, 0x71, 0xe6, 0x9c, 0xd9, 0x7f, 0x7c}}
+};
+
+CMPlugin::CMPlugin() :
+	PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx),
+	m_columns(1)
+{
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int OnTTBLoaded(WPARAM, LPARAM)
+{
+	TTBButton ttb = {};
+	ttb.dwFlags = TTBBF_VISIBLE;
+	ttb.pszService = QS_SHOWSERVICE;
+	ttb.hIconHandleDn = ttb.hIconHandleUp = g_plugin.getIconHandle(IDI_QS);
+	ttb.name = MODULENAME;
+	ttb.pszTooltipUp = ttb.pszTooltipDn = LPGEN("Quick Search");
+	hTTBButton = g_plugin.addTTB(&ttb);
+	return 0;
+}
+
+static INT_PTR OpenSearchWindow(WPARAM wParam, LPARAM)
+{
+	OpenSrWindow((wchar_t *)wParam);
+	return 0;
+}
+
+static int OnCheckPlugins(WPARAM, LPARAM)
+{
+	g_bVarsInstalled = ServiceExists(MS_VARS_FORMATSTRING);
+	g_bTipperInstalled = ServiceExists(MS_TIPPER_SHOWTIPW);
+	g_bFingerInstalled = ServiceExists(MS_FP_GETCLIENTICONW);
+
+	return 0;
+}
+
+static int OnModulesLoaded(WPARAM, LPARAM)
+{
+	HookEvent(ME_TTB_MODULELOADED, OnTTBLoaded);
+
+	CreateServiceFunction(QS_SHOWSERVICE, OpenSearchWindow);
+
+	// add menu item
+	CMenuItem mi(&g_plugin);
+	SET_UID(mi, 0x98C2A92A, 0xD93D, 0x43E8, 0x91, 0xC3, 0x3B, 0xB6, 0xBE, 0x43, 0x44, 0xF0);
+	mi.name.a = LPGEN("Quick Search");
+	mi.position = 500050000;
+	mi.pszService = QS_SHOWSERVICE;
+	mi.hIcolibItem = g_plugin.getIconHandle(IDI_QS);
+	Menu_AddMainMenuItem(&mi);
+
+	// register hotkey
+	HOTKEYDESC hkd = {};
+	hkd.pszName = "QS_Global";
+	hkd.szDescription.a = LPGEN("Open Quick Search window");
+	hkd.szSection.a = LPGEN("Quick Search");
+	hkd.pszService = QS_SHOWSERVICE;
+	hkd.DefHotKey = HOTKEYCODE(HOTKEYF_ALT, VK_F3);
+	g_plugin.addHotkey(&hkd);
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static IconItem iconList[] =
+{
+	{ LPGEN("Quick Search"),  "QS",      IDI_QS     },
+	{ LPGEN("New Column"),    "New",     IDI_NEW    },
+	{ LPGEN("Column Up"),     "Up",      IDI_UP     },
+	{ LPGEN("Column Down"),   "Down",    IDI_DOWN   },
+	{ LPGEN("Delete Column"), "Delete",  IDI_DELETE },
+	{ LPGEN("Default"),       "Default", IDI_DEFAULT},
+	{ LPGEN("Reload"),        "Reload",  IDI_RELOAD },
+	{ LPGEN("Male"),          "Male",    IDI_MALE   },
+	{ LPGEN("Female"),        "Female",  IDI_FEMALE },
+};
+
+struct
+{
+	COLORREF defValue;
+	const char *szSetting, *szDescr;
+}
+static sttColors[color_max] = {
+	{ 0x00FFFFFF, "back_norm", LPGEN("Normal background")           },
+	{ 0x00000000, "fore_norm", LPGEN("Normal foreground")           },
+	{ 0x00EBE6DE, "back_odd" , LPGEN("Odd background")              },
+	{ 0x00000000, "fore_odd" , LPGEN("Odd foreground")              },
+	{ 0x008080FF, "back_dis" , LPGEN("Disabled account background") },
+	{ 0x00000000, "fore_dis" , LPGEN("Disabled account foreground") },
+	{ 0x008000FF, "back_del" , LPGEN("Deleted account background")  },
+	{ 0x00000000, "fore_del" , LPGEN("Deleted account foreground")  },
+	{ 0x0080FFFF, "back_hid" , LPGEN("Hidden contact background")   },
+	{ 0x00000000, "fore_hid" , LPGEN("Hidden contact foreground")   },
+	{ 0x00BAE699, "back_meta", LPGEN("Metacontact background")      },
+	{ 0x00000000, "fore_meta", LPGEN("Metacontact foreground")      },
+	{ 0x00B3CCC1, "back_sub" , LPGEN("Subcontact background")       },
+	{ 0x00000000, "fore_sub" , LPGEN("Subcontact foreground")       },
+};
+
+static int OnColorReload(WPARAM, LPARAM)
+{
+	for (int i = 0; i < color_max; i++)
+		g_plugin.m_colors[i] = Colour_Get(MODULENAME, sttColors[i].szDescr);
+	return 0;
+}
+
+int CMPlugin::Load()
+{
+	g_plugin.registerIcon(MODULENAME, iconList);
+
+	ColourID colourid = {};
+	strncpy_s(colourid.group, MODULENAME, _TRUNCATE);
+	strncpy_s(colourid.dbSettingsGroup, MODULENAME, _TRUNCATE);
+
+	for (auto &it : sttColors) {
+		strncpy_s(colourid.name, it.szDescr, _TRUNCATE);
+		strncpy_s(colourid.setting, it.szSetting, _TRUNCATE);
+		colourid.defcolour = it.defValue;
+		colourid.order = int(&it - sttColors);
+		g_plugin.addColor(&colourid);
+	}
+	OnColorReload(0, 0);
+	OnCheckPlugins(0, 0);
+
+	HookEvent(ME_COLOUR_RELOAD, OnColorReload);
+	HookEvent(ME_OPT_INITIALISE, OnOptInit);
+	HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded);
+	HookEvent(ME_SYSTEM_MODULELOAD, OnCheckPlugins);
+	HookEvent(ME_SYSTEM_MODULEUNLOAD, OnCheckPlugins);
+
+	if (!LoadColumns(m_columns))
+		LoadDefaultColumns(m_columns);
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CMPlugin::Unload()
+{
+	if (hTTBButton) {
+		CallService(MS_TTB_REMOVEBUTTON, (WPARAM)hTTBButton, 0);
+		hTTBButton = 0;
+	}
+
+	CloseSrWindow();
+	return 0;
+}
diff --git a/plugins/QuickSearch/src/options.cpp b/plugins/QuickSearch/src/options.cpp
index d827416ecc..d74cf745aa 100644
--- a/plugins/QuickSearch/src/options.cpp
+++ b/plugins/QuickSearch/src/options.cpp
@@ -1,519 +1,519 @@
-/*
-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"
-
-class COptionsDlg : public CDlgBase
-{
-	OBJLIST<ColumnItem> m_columns;
-
-	void AddColumn(int idx, ColumnItem *pCol)
-	{
-		LVITEM lvi = {};
-		lvi.mask = LVIF_PARAM;
-		lvi.iItem = idx;
-		lvi.lParam = LPARAM(pCol);
-		m_list.InsertItem(&lvi);
-	}
-
-	void CheckDirection(int iItem)
-	{
-		btnUp.Enable(iItem > 0);
-		btnDown.Enable(iItem < m_list.GetItemCount()-1);
-	}
-
-	void ClearScreen()
-	{
-		// setting
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE);
-
-		// contact info
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE);
-
-		// others
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE);
-	}
-
-	void DisplayCurInfo(const ColumnItem *pCol)
-	{
-		ClearScreen();
-		SetupScreen(pCol->setting_type);
-
-		editTitle.SetText(pCol->title);
-		cmbVarType.SelectData(pCol->setting_type);
-
-		switch (pCol->setting_type) {
-		case QST_SETTING:
-			cmbDataType.SelectData(pCol->datatype);
-			editModule.SetTextA(pCol->module);
-			editSetting.SetTextA(pCol->setting);
-			break;
-
-		case QST_SCRIPT:
-			SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script);
-			break;
-
-		case QST_CONTACTINFO:
-			cmbCnfType.SelectData(pCol->cnftype);
-			break;
-
-		case QST_OTHER:
-			cmbOther.SelectData(pCol->other);
-			break;
-		}
-	}
-
-	void FillTableLine(int item, ColumnItem *pColumn)
-	{
-		m_list.SetItemText(item, 1, pColumn->title);
-
-		switch (pColumn->setting_type) {
-		case QST_SETTING:
-			m_list.SetItemText(item, 2, _A2T(pColumn->module));
-			m_list.SetItemText(item, 3, _A2T(pColumn->setting));
-			break;
-
-		case QST_SCRIPT:
-			m_list.SetItemText(item, 2, TranslateT("Script"));
-			break;
-
-		case QST_SERVICE:
-			m_list.SetItemText(item, 2, TranslateT("Service"));
-			m_list.SetItemText(item, 3, _A2T(pColumn->svc.service));
-			break;
-
-		case QST_CONTACTINFO:
-			m_list.SetItemText(item, 2, TranslateT("Contact info"));
-			m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype));
-			break;
-
-		case QST_OTHER:
-			m_list.SetItemText(item, 2, TranslateT("Other"));
-			if (pColumn->other == QSTO_METACONTACT)
-				m_list.SetItemText(item, 3, TranslateT("Metacontact"));
-			break;
-		}
-	}
-
-	void InitScreen()
-	{
-		// setting
-		cmbDataType.SetCurSel(0);
-		editModule.SetText(L"");
-		editSetting.SetText(L"");
-
-		// contact info
-		cmbCnfType.SetCurSel(0);
-
-		// others
-		cmbOther.SetCurSel(0);
-	}
-
-	void ResizeControl(int id, int width)
-	{
-		HWND hwnd = GetDlgItem(m_hwnd, id);
-		RECT rc;
-		::GetWindowRect(hwnd, &rc);
-		::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
-	}
-
-	void SetupScreen(int type)
-	{
-		if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE)))
-			return;
-
-		// setting
-		int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE;
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd);
-		editModule.Show(cmd == SW_SHOW);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd);
-		editSetting.Show(cmd == SW_SHOW);
-
-		// contact info
-		cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE; 
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd);
-
-		// script
-		cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE; 
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd);
-
-		// others
-		cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE; 
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd);
-	}
-
-	void UpdateList()
-	{
-		m_list.DeleteAllItems();
-
-		int cnt = 0;
-		for (auto &it : m_columns) {
-			AddColumn(cnt, it);
-			FillTableLine(cnt, it);
-			m_list.SetCheckState(cnt, it->bEnabled);
-			cnt++;
-		}
-
-		m_list.SetCurSel(0);
-	}
-
-	CCtrlEdit editTitle, editModule, editSetting;
-	CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons;
-	CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType;
-	CCtrlListView m_list;
-	CCtrlButton btnSave, btnResize;
-	CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload;
-
-public: 
-	COptionsDlg() :
-		CDlgBase(g_plugin, IDD_OPTIONS),
-		m_columns(1),
-		m_list(this, IDC_LIST),
-		btnSave(this, IDC_SETITEM),
-		btnResize(this, IDC_B_RESIZE),
-		btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")),
-		btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")),
-		btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")),
-		btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")),
-		btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")),
-		btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")),
-		editTitle(this, IDC_E_TITLE),
-		editModule(this, IDC_E_MODULE),
-		editSetting(this, IDC_E_SETTING),
-		cmbOther(this, IDC_C_OTHER),
-		cmbCnfType(this, IDC_C_CNFTYPE),
-		cmbVarType(this, IDC_C_VARTYPE),
-		cmbDataType(this, IDC_C_DATATYPE),
-		chkDrawGrid(this, IDC_CH_DRAWGRID),
-		chkAutoClose(this, IDC_CH_AUTOCLOSE),	
-		chkSortStatus(this, IDC_CH_SORTSTATUS),
-		chkSavePattern(this, IDC_CH_SAVEPATTERN),
-		chkClientIcons(this, IDC_CH_CLIENTICONS),
-		chkUseToolstyle(this, IDC_CH_USETOOLSTYLE)
-	{
-		m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List);
-
-		btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up);
-		btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New);
-		btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down);
-		btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save);
-		btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete);
-		btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload);
-		btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize);
-		btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default);
-
-		cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var);
-	}
-
-	bool OnInitDialog() override
-	{
-		editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent();
-		cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent();
-
-		m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
-		m_list.AddColumn(0, L"#", 20);
-		m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1", 95));
-		m_list.AddColumn(2, TranslateT("Module/Info type"), g_plugin.getWord("col2", 105));
-		m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3", 85));
-
-		cmbVarType.AddString(TranslateT("Database setting"), QST_SETTING);
-		cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT);
-		cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO);
-		cmbVarType.AddString(TranslateT("Other"), QST_OTHER);
-		cmbVarType.SetCurSel(0);
-
-		cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE);
-		cmbDataType.AddString(TranslateT("Word"), QSTS_WORD);
-		cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD);
-		cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED);
-		cmbDataType.AddString(TranslateT("Hexadecimal"), QSTS_HEXNUM);
-		cmbDataType.AddString(TranslateT("String"), QSTS_STRING);
-		cmbDataType.AddString(TranslateT("Timestamp"), QSTS_TIMESTAMP);
-		cmbDataType.SetCurSel(0);
-
-		cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN);
-		cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT);
-		cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT);
-		cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT);
-		cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME);
-		cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT);
-		cmbOther.SetCurSel(0);
-
-		for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++)
-			if (auto *pwszText = cnf2str(i))
-				cmbCnfType.AddString(pwszText, i);
-		cmbCnfType.SetCurSel(0);
-
-		chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0);
-		chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0);
-		chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0);
-		chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0);
-		chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0);
-		chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0);
-
-		// make local copy of column descriptions
-		for (auto &it : g_plugin.m_columns)
-			m_columns.insert(new ColumnItem(*it));
-
-		UpdateList();
-		onClick_Resize(0);
-		if (m_columns.getCount())
-			DisplayCurInfo(&m_columns[0]);
-		return true;
-	}
-
-	bool OnApply() override
-	{
-		// checkboxes
-		g_plugin.m_flags &= ~QSO_MAINOPTIONS;
-		if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID;
-		if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE;
-		if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS;
-		if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS;
-		if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN;
-		if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE;
-
-		int tmpbool = CloseSrWindow(false);
-
-		g_plugin.m_columns.destroy();
-		int nCount = m_list.GetItemCount();
-		for (int i = 0; i < nCount; i++) {
-			auto *pCol = (ColumnItem *)m_list.GetItemData(i);
-			pCol->bEnabled = m_list.GetCheckState(i) != 0;
-			g_plugin.m_columns.insert(new ColumnItem(*pCol));
-		}
-
-		g_plugin.SaveOptions();
-
-		if (tmpbool)
-			OpenSrWindow(0);
-		return true;
-	}
-
-	void OnDestroy() override
-	{
-		m_columns.destroy();
-
-		g_plugin.setWord("col1", m_list.GetColumnWidth(1));
-		g_plugin.setWord("col2", m_list.GetColumnWidth(2));
-		g_plugin.setWord("col3", m_list.GetColumnWidth(3));
-	}
-
-	void onClick_New(CCtrlButton *)
-	{
-		int idx = m_list.GetSelectionMark()+1;
-		auto *pNew = new ColumnItem(TranslateT("New column"));
-		m_columns.insert(pNew);
-
-		AddColumn(idx, pNew);
-		m_list.EnsureVisible(idx, FALSE);
-		m_list.SetCurSel(idx);
-		InitScreen();
-		CheckDirection(idx);
-		btnDelete.Enable();
-		NotifyChange();
-	}
-
-	void onClick_Delete(CCtrlButton *)
-	{
-		int idx = m_list.GetSelectionMark();
-		auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
-
-		m_list.DeleteItem(idx);
-		m_columns.remove(pCol);
-
-		int nCount = m_list.GetItemCount();
-		if (nCount == 0) {
-			m_list.Disable();
-			InitScreen();
-		}
-		else {
-			if (nCount == idx)
-				idx--;
-			m_list.SetCurSel(idx);
-		}
-		CheckDirection(idx);
-		NotifyChange();
-	}
-
-	void onClick_Up(CCtrlButton *)
-	{
-		int idx = m_list.GetSelectionMark();
-		if (idx > 0) {
-			CheckDirection(m_list.MoveItem(idx, -1));
-			NotifyChange();
-		}
-	}
-
-	void onClick_Down(CCtrlButton *)
-	{
-		int idx = m_list.GetSelectionMark();
-		if (idx < m_list.GetItemCount() - 1) {
-			CheckDirection(m_list.MoveItem(idx, 1));
-			NotifyChange();
-		}
-	}
-
-	void onClick_Reload(CCtrlButton *)
-	{
-		g_plugin.LoadColumns(m_columns);
-		UpdateList();
-	}
-
-	void onClick_Default(CCtrlButton *)
-	{
-		LoadDefaultColumns(m_columns);
-		UpdateList();
-		NotifyChange();
-	}
-
-	void onClick_Save(CCtrlButton *)
-	{
-		if (m_list.GetItemCount() == 0) {
-			AddColumn(0, new ColumnItem(TranslateT("New column")));
-			m_list.SetCurSel(0);
-			btnDelete.Enable();
-		}
-
-		int idx = m_list.GetSelectionMark();
-		auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
-		pCol->dwFlags = 0;
-		if (m_list.GetCheckState(idx))
-			pCol->bEnabled = pCol->bFilter = true;
-		pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel());
-		replaceStrW(pCol->title, editTitle.GetText());
-
-		switch (pCol->setting_type) {
-		case QST_SETTING:
-			pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel());
-			pCol->module = mir_u2a(ptrW(editModule.GetText()));
-			pCol->setting = mir_u2a(ptrW(editSetting.GetText()));
-			break;
-
-		case QST_CONTACTINFO:
-			pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
-			break;
-
-		case QST_OTHER:
-			pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
-			break;
-		}
-
-		FillTableLine(idx, pCol);
-		NotifyChange();
-	}
-
-	void onClick_Resize(CCtrlButton *)
-	{
-		wchar_t *pcw;
-		int dx, rside;
-
-		RECT rc, rc1;
-		GetClientRect(m_hwnd, &rc);
-		GetWindowRect(btnResize.GetHwnd(), &rc1);
-
-		POINT pt = { rc1.left, 0 };
-		ScreenToClient(m_hwnd, &pt);
-		if (pt.x < (rc.right - 50)) {
-			rside = SW_HIDE;
-			dx = rc.right - (rc1.right - rc1.left) - 4;
-			pcw = L"<";
-		}
-		else {
-			rside = SW_SHOW;
-
-			GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc);
-			pt.x = rc.left;
-			pt.y = 0;
-			ScreenToClient(m_hwnd, &pt);
-			dx = pt.x - (rc1.right - rc1.left) - 4;
-			pcw = L">";
-		}
-		
-		btnResize.SetText(pcw);
-
-		// move separator button
-		SetWindowPos(btnResize.GetHwnd(), 0, dx + 2, 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
-
-		// resize left side controls
-		ResizeControl(IDC_LIST, dx);
-		ResizeControl(IDC_CH_GROUP, dx);
-
-		ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8);
-		ResizeControl(IDC_CH_DRAWGRID, dx - 8);
-		ResizeControl(IDC_CH_SAVEPATTERN, dx - 8);
-		ResizeControl(IDC_CH_AUTOCLOSE, dx - 8);
-		ResizeControl(IDC_CH_SORTSTATUS, dx - 8);
-		ResizeControl(IDC_CH_CLIENTICONS, dx - 8);
-
-		// show/hide setting block (ugly, i know!)
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside);
-		ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside);
-
-		ClearScreen();
-		if (rside == SW_SHOW)
-			SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel()));
-	}
-	
-	void onItemChanged_List(CCtrlListView::TEventInfo *ev)
-	{
-		auto *nmlv = ev->nmlv;
-		// we got new focus
-		if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) {
-			CheckDirection(nmlv->iItem);
-			InitScreen();
-			DisplayCurInfo((ColumnItem*)nmlv->lParam);
-		}
-	}
-
-	void onSelChanged_Var(CCtrlCombo *pCombo)
-	{
-		SetupScreen(pCombo->GetItemData(pCombo->GetCurSel()));
-	}
-};
-
-int OnOptInit(WPARAM wParam, LPARAM)
-{
-	OPTIONSDIALOGPAGE odp = {};
-	odp.flags = ODPF_BOLDGROUPS;
-	odp.szGroup.a = LPGEN("Contacts");
-	odp.szTitle.a = LPGEN("Quick Search");
-	odp.position = 900003000;
-	odp.pDialog = new COptionsDlg();
-	g_plugin.addOptions(wParam, &odp);
-	return 0;
-}
+/*
+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"
+
+class COptionsDlg : public CDlgBase
+{
+	OBJLIST<ColumnItem> m_columns;
+
+	void AddColumn(int idx, ColumnItem *pCol)
+	{
+		LVITEM lvi = {};
+		lvi.mask = LVIF_PARAM;
+		lvi.iItem = idx;
+		lvi.lParam = LPARAM(pCol);
+		m_list.InsertItem(&lvi);
+	}
+
+	void CheckDirection(int iItem)
+	{
+		btnUp.Enable(iItem > 0);
+		btnDown.Enable(iItem < m_list.GetItemCount()-1);
+	}
+
+	void ClearScreen()
+	{
+		// setting
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), SW_HIDE);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), SW_HIDE);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), SW_HIDE);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), SW_HIDE);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), SW_HIDE);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), SW_HIDE);
+
+		// contact info
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), SW_HIDE);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), SW_HIDE);
+
+		// others
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), SW_HIDE);
+	}
+
+	void DisplayCurInfo(const ColumnItem *pCol)
+	{
+		ClearScreen();
+		SetupScreen(pCol->setting_type);
+
+		editTitle.SetText(pCol->title);
+		cmbVarType.SelectData(pCol->setting_type);
+
+		switch (pCol->setting_type) {
+		case QST_SETTING:
+			cmbDataType.SelectData(pCol->datatype);
+			editModule.SetTextA(pCol->module);
+			editSetting.SetTextA(pCol->setting);
+			break;
+
+		case QST_SCRIPT:
+			SetDlgItemTextW(m_hwnd, IDC_E_SCRIPT, pCol->script);
+			break;
+
+		case QST_CONTACTINFO:
+			cmbCnfType.SelectData(pCol->cnftype);
+			break;
+
+		case QST_OTHER:
+			cmbOther.SelectData(pCol->other);
+			break;
+		}
+	}
+
+	void FillTableLine(int item, ColumnItem *pColumn)
+	{
+		m_list.SetItemText(item, 1, pColumn->title);
+
+		switch (pColumn->setting_type) {
+		case QST_SETTING:
+			m_list.SetItemText(item, 2, _A2T(pColumn->module));
+			m_list.SetItemText(item, 3, _A2T(pColumn->setting));
+			break;
+
+		case QST_SCRIPT:
+			m_list.SetItemText(item, 2, TranslateT("Script"));
+			break;
+
+		case QST_SERVICE:
+			m_list.SetItemText(item, 2, TranslateT("Service"));
+			m_list.SetItemText(item, 3, _A2T(pColumn->svc.service));
+			break;
+
+		case QST_CONTACTINFO:
+			m_list.SetItemText(item, 2, TranslateT("Contact info"));
+			m_list.SetItemText(item, 3, cnf2str(pColumn->cnftype));
+			break;
+
+		case QST_OTHER:
+			m_list.SetItemText(item, 2, TranslateT("Other"));
+			if (pColumn->other == QSTO_METACONTACT)
+				m_list.SetItemText(item, 3, TranslateT("Metacontact"));
+			break;
+		}
+	}
+
+	void InitScreen()
+	{
+		// setting
+		cmbDataType.SetCurSel(0);
+		editModule.SetText(L"");
+		editSetting.SetText(L"");
+
+		// contact info
+		cmbCnfType.SetCurSel(0);
+
+		// others
+		cmbOther.SetCurSel(0);
+	}
+
+	void ResizeControl(int id, int width)
+	{
+		HWND hwnd = GetDlgItem(m_hwnd, id);
+		RECT rc;
+		::GetWindowRect(hwnd, &rc);
+		::SetWindowPos(hwnd, 0, 0, 0, width, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW);
+	}
+
+	void SetupScreen(int type)
+	{
+		if (!IsWindowVisible(GetDlgItem(m_hwnd, IDC_E_TITLE)))
+			return;
+
+		// setting
+		int cmd = (type == QST_SETTING) ? SW_SHOW : SW_HIDE;
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_DATATYPE), cmd);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), cmd);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_MODULE), cmd);
+		editModule.Show(cmd == SW_SHOW);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_SETTING), cmd);
+		editSetting.Show(cmd == SW_SHOW);
+
+		// contact info
+		cmd = (type == QST_CONTACTINFO) ? SW_SHOW : SW_HIDE; 
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_CNFTYPE), cmd);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), cmd);
+
+		// script
+		cmd = (type == QST_SCRIPT) ? SW_SHOW : SW_HIDE; 
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), cmd);
+
+		// others
+		cmd = (type == QST_OTHER) ? SW_SHOW : SW_HIDE; 
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), cmd);
+	}
+
+	void UpdateList()
+	{
+		m_list.DeleteAllItems();
+
+		int cnt = 0;
+		for (auto &it : m_columns) {
+			AddColumn(cnt, it);
+			FillTableLine(cnt, it);
+			m_list.SetCheckState(cnt, it->bEnabled);
+			cnt++;
+		}
+
+		m_list.SetCurSel(0);
+	}
+
+	CCtrlEdit editTitle, editModule, editSetting;
+	CCtrlCheck chkSortStatus, chkAutoClose, chkUseToolstyle, chkDrawGrid, chkSavePattern, chkClientIcons;
+	CCtrlCombo cmbVarType, cmbDataType, cmbOther, cmbCnfType;
+	CCtrlListView m_list;
+	CCtrlButton btnSave, btnResize;
+	CCtrlMButton btnNew, btnUp, btnDown, btnDelete, btnDefault, btnReload;
+
+public: 
+	COptionsDlg() :
+		CDlgBase(g_plugin, IDD_OPTIONS),
+		m_columns(1),
+		m_list(this, IDC_LIST),
+		btnSave(this, IDC_SETITEM),
+		btnResize(this, IDC_B_RESIZE),
+		btnUp(this, IDC_UP, g_plugin.getIcon(IDI_UP), LPGEN("Up")),
+		btnNew(this, IDC_NEW, g_plugin.getIcon(IDI_NEW), LPGEN("New")),
+		btnDown(this, IDC_DN, g_plugin.getIcon(IDI_DOWN), LPGEN("Down")),
+		btnDelete(this, IDC_DELETE, g_plugin.getIcon(IDI_DELETE), LPGEN("Delete")),
+		btnReload(this, IDC_RELOAD, g_plugin.getIcon(IDI_RELOAD), LPGEN("Reload")),
+		btnDefault(this, IDC_DEFAULT, g_plugin.getIcon(IDI_DEFAULT), LPGEN("Default")),
+		editTitle(this, IDC_E_TITLE),
+		editModule(this, IDC_E_MODULE),
+		editSetting(this, IDC_E_SETTING),
+		cmbOther(this, IDC_C_OTHER),
+		cmbCnfType(this, IDC_C_CNFTYPE),
+		cmbVarType(this, IDC_C_VARTYPE),
+		cmbDataType(this, IDC_C_DATATYPE),
+		chkDrawGrid(this, IDC_CH_DRAWGRID),
+		chkAutoClose(this, IDC_CH_AUTOCLOSE),	
+		chkSortStatus(this, IDC_CH_SORTSTATUS),
+		chkSavePattern(this, IDC_CH_SAVEPATTERN),
+		chkClientIcons(this, IDC_CH_CLIENTICONS),
+		chkUseToolstyle(this, IDC_CH_USETOOLSTYLE)
+	{
+		m_list.OnItemChanged = Callback(this, &COptionsDlg::onItemChanged_List);
+
+		btnUp.OnClick = Callback(this, &COptionsDlg::onClick_Up);
+		btnNew.OnClick = Callback(this, &COptionsDlg::onClick_New);
+		btnDown.OnClick = Callback(this, &COptionsDlg::onClick_Down);
+		btnSave.OnClick = Callback(this, &COptionsDlg::onClick_Save);
+		btnDelete.OnClick = Callback(this, &COptionsDlg::onClick_Delete);
+		btnReload.OnClick = Callback(this, &COptionsDlg::onClick_Reload);
+		btnResize.OnClick = Callback(this, &COptionsDlg::onClick_Resize);
+		btnDefault.OnClick = Callback(this, &COptionsDlg::onClick_Default);
+
+		cmbVarType.OnSelChanged = Callback(this, &COptionsDlg::onSelChanged_Var);
+	}
+
+	bool OnInitDialog() override
+	{
+		editTitle.SetSilent(); editModule.SetSilent(); editSetting.SetSilent();
+		cmbOther.SetSilent(); cmbCnfType.SetSilent(); cmbVarType.SetSilent(); cmbDataType.SetSilent();
+
+		m_list.SetExtendedListViewStyle(m_list.GetExtendedListViewStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES);
+		m_list.AddColumn(0, L"#", 20);
+		m_list.AddColumn(1, TranslateT("Title"), g_plugin.getWord("col1", 95));
+		m_list.AddColumn(2, TranslateT("Module/Info type"), g_plugin.getWord("col2", 105));
+		m_list.AddColumn(3, TranslateT("Setting"), g_plugin.getWord("col3", 85));
+
+		cmbVarType.AddString(TranslateT("Database setting"), QST_SETTING);
+		cmbVarType.AddString(TranslateT("Script"), QST_SCRIPT);
+		cmbVarType.AddString(TranslateT("Contact info"), QST_CONTACTINFO);
+		cmbVarType.AddString(TranslateT("Other"), QST_OTHER);
+		cmbVarType.SetCurSel(0);
+
+		cmbDataType.AddString(TranslateT("Byte"), QSTS_BYTE);
+		cmbDataType.AddString(TranslateT("Word"), QSTS_WORD);
+		cmbDataType.AddString(TranslateT("Dword"), QSTS_DWORD);
+		cmbDataType.AddString(TranslateT("Signed"), QSTS_SIGNED);
+		cmbDataType.AddString(TranslateT("Hexadecimal"), QSTS_HEXNUM);
+		cmbDataType.AddString(TranslateT("String"), QSTS_STRING);
+		cmbDataType.AddString(TranslateT("Timestamp"), QSTS_TIMESTAMP);
+		cmbDataType.SetCurSel(0);
+
+		cmbOther.AddString(TranslateT("Last seen"), QSTO_LASTSEEN);
+		cmbOther.AddString(TranslateT("Last event"), QSTO_LASTEVENT);
+		cmbOther.AddString(TranslateT("Metacontact"), QSTO_METACONTACT);
+		cmbOther.AddString(TranslateT("Event count"), QSTO_EVENTCOUNT);
+		cmbOther.AddString(TranslateT("Display name"), QSTO_DISPLAYNAME);
+		cmbOther.AddString(TranslateT("Account name"), QSTO_ACCOUNT);
+		cmbOther.SetCurSel(0);
+
+		for (int i = CNF_FIRSTNAME; i < CNF_MAX; i++)
+			if (auto *pwszText = cnf2str(i))
+				cmbCnfType.AddString(pwszText, i);
+		cmbCnfType.SetCurSel(0);
+
+		chkDrawGrid.SetState((g_plugin.m_flags & QSO_DRAWGRID) != 0);
+		chkAutoClose.SetState((g_plugin.m_flags & QSO_AUTOCLOSE) != 0);
+		chkSortStatus.SetState((g_plugin.m_flags & QSO_SORTBYSTATUS) != 0);
+		chkClientIcons.SetState((g_plugin.m_flags & QSO_CLIENTICONS) != 0);
+		chkSavePattern.SetState((g_plugin.m_flags & QSO_SAVEPATTERN) != 0);
+		chkUseToolstyle.SetState((g_plugin.m_flags & QSO_TOOLSTYLE) != 0);
+
+		// make local copy of column descriptions
+		for (auto &it : g_plugin.m_columns)
+			m_columns.insert(new ColumnItem(*it));
+
+		UpdateList();
+		onClick_Resize(0);
+		if (m_columns.getCount())
+			DisplayCurInfo(&m_columns[0]);
+		return true;
+	}
+
+	bool OnApply() override
+	{
+		// checkboxes
+		g_plugin.m_flags &= ~QSO_MAINOPTIONS;
+		if (chkDrawGrid.IsChecked()) g_plugin.m_flags |= QSO_DRAWGRID;
+		if (chkAutoClose.IsChecked()) g_plugin.m_flags |= QSO_AUTOCLOSE;
+		if (chkSortStatus.IsChecked()) g_plugin.m_flags |= QSO_SORTBYSTATUS;
+		if (chkClientIcons.IsChecked()) g_plugin.m_flags |= QSO_CLIENTICONS;
+		if (chkSavePattern.IsChecked()) g_plugin.m_flags |= QSO_SAVEPATTERN;
+		if (chkUseToolstyle.IsChecked()) g_plugin.m_flags |= QSO_TOOLSTYLE;
+
+		int tmpbool = CloseSrWindow(false);
+
+		g_plugin.m_columns.destroy();
+		int nCount = m_list.GetItemCount();
+		for (int i = 0; i < nCount; i++) {
+			auto *pCol = (ColumnItem *)m_list.GetItemData(i);
+			pCol->bEnabled = m_list.GetCheckState(i) != 0;
+			g_plugin.m_columns.insert(new ColumnItem(*pCol));
+		}
+
+		g_plugin.SaveOptions();
+
+		if (tmpbool)
+			OpenSrWindow(0);
+		return true;
+	}
+
+	void OnDestroy() override
+	{
+		m_columns.destroy();
+
+		g_plugin.setWord("col1", m_list.GetColumnWidth(1));
+		g_plugin.setWord("col2", m_list.GetColumnWidth(2));
+		g_plugin.setWord("col3", m_list.GetColumnWidth(3));
+	}
+
+	void onClick_New(CCtrlButton *)
+	{
+		int idx = m_list.GetSelectionMark()+1;
+		auto *pNew = new ColumnItem(TranslateT("New column"));
+		m_columns.insert(pNew);
+
+		AddColumn(idx, pNew);
+		m_list.EnsureVisible(idx, FALSE);
+		m_list.SetCurSel(idx);
+		InitScreen();
+		CheckDirection(idx);
+		btnDelete.Enable();
+		NotifyChange();
+	}
+
+	void onClick_Delete(CCtrlButton *)
+	{
+		int idx = m_list.GetSelectionMark();
+		auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
+
+		m_list.DeleteItem(idx);
+		m_columns.remove(pCol);
+
+		int nCount = m_list.GetItemCount();
+		if (nCount == 0) {
+			m_list.Disable();
+			InitScreen();
+		}
+		else {
+			if (nCount == idx)
+				idx--;
+			m_list.SetCurSel(idx);
+		}
+		CheckDirection(idx);
+		NotifyChange();
+	}
+
+	void onClick_Up(CCtrlButton *)
+	{
+		int idx = m_list.GetSelectionMark();
+		if (idx > 0) {
+			CheckDirection(m_list.MoveItem(idx, -1));
+			NotifyChange();
+		}
+	}
+
+	void onClick_Down(CCtrlButton *)
+	{
+		int idx = m_list.GetSelectionMark();
+		if (idx < m_list.GetItemCount() - 1) {
+			CheckDirection(m_list.MoveItem(idx, 1));
+			NotifyChange();
+		}
+	}
+
+	void onClick_Reload(CCtrlButton *)
+	{
+		g_plugin.LoadColumns(m_columns);
+		UpdateList();
+	}
+
+	void onClick_Default(CCtrlButton *)
+	{
+		LoadDefaultColumns(m_columns);
+		UpdateList();
+		NotifyChange();
+	}
+
+	void onClick_Save(CCtrlButton *)
+	{
+		if (m_list.GetItemCount() == 0) {
+			AddColumn(0, new ColumnItem(TranslateT("New column")));
+			m_list.SetCurSel(0);
+			btnDelete.Enable();
+		}
+
+		int idx = m_list.GetSelectionMark();
+		auto *pCol = (ColumnItem *)m_list.GetItemData(idx);
+		pCol->dwFlags = 0;
+		if (m_list.GetCheckState(idx))
+			pCol->bEnabled = pCol->bFilter = true;
+		pCol->setting_type = cmbVarType.GetItemData(cmbVarType.GetCurSel());
+		replaceStrW(pCol->title, editTitle.GetText());
+
+		switch (pCol->setting_type) {
+		case QST_SETTING:
+			pCol->datatype = cmbDataType.GetItemData(cmbDataType.GetCurSel());
+			pCol->module = mir_u2a(ptrW(editModule.GetText()));
+			pCol->setting = mir_u2a(ptrW(editSetting.GetText()));
+			break;
+
+		case QST_CONTACTINFO:
+			pCol->cnftype = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
+			break;
+
+		case QST_OTHER:
+			pCol->other = cmbCnfType.GetItemData(cmbCnfType.GetCurSel());
+			break;
+		}
+
+		FillTableLine(idx, pCol);
+		NotifyChange();
+	}
+
+	void onClick_Resize(CCtrlButton *)
+	{
+		wchar_t *pcw;
+		int dx, rside;
+
+		RECT rc, rc1;
+		GetClientRect(m_hwnd, &rc);
+		GetWindowRect(btnResize.GetHwnd(), &rc1);
+
+		POINT pt = { rc1.left, 0 };
+		ScreenToClient(m_hwnd, &pt);
+		if (pt.x < (rc.right - 50)) {
+			rside = SW_HIDE;
+			dx = rc.right - (rc1.right - rc1.left) - 4;
+			pcw = L"<";
+		}
+		else {
+			rside = SW_SHOW;
+
+			GetWindowRect(GetDlgItem(m_hwnd, IDC_S_COLSETTING), &rc);
+			pt.x = rc.left;
+			pt.y = 0;
+			ScreenToClient(m_hwnd, &pt);
+			dx = pt.x - (rc1.right - rc1.left) - 4;
+			pcw = L">";
+		}
+		
+		btnResize.SetText(pcw);
+
+		// move separator button
+		SetWindowPos(btnResize.GetHwnd(), 0, dx + 2, 2, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW);
+
+		// resize left side controls
+		ResizeControl(IDC_LIST, dx);
+		ResizeControl(IDC_CH_GROUP, dx);
+
+		ResizeControl(IDC_CH_USETOOLSTYLE, dx - 8);
+		ResizeControl(IDC_CH_DRAWGRID, dx - 8);
+		ResizeControl(IDC_CH_SAVEPATTERN, dx - 8);
+		ResizeControl(IDC_CH_AUTOCLOSE, dx - 8);
+		ResizeControl(IDC_CH_SORTSTATUS, dx - 8);
+		ResizeControl(IDC_CH_CLIENTICONS, dx - 8);
+
+		// show/hide setting block (ugly, i know!)
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_COLSETTING), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_LINE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_TITLE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_TITLE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SCRIPT), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_MODULE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_E_SETTING), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_S_VARTYPE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_VARTYPE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_OTHER), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_CNFTYPE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_C_DATATYPE), rside);
+		ShowWindow(GetDlgItem(m_hwnd, IDC_SETITEM), rside);
+
+		ClearScreen();
+		if (rside == SW_SHOW)
+			SetupScreen(cmbVarType.GetItemData(cmbVarType.GetCurSel()));
+	}
+	
+	void onItemChanged_List(CCtrlListView::TEventInfo *ev)
+	{
+		auto *nmlv = ev->nmlv;
+		// we got new focus
+		if ((nmlv->uOldState & LVNI_FOCUSED) < (nmlv->uNewState & LVNI_FOCUSED)) {
+			CheckDirection(nmlv->iItem);
+			InitScreen();
+			DisplayCurInfo((ColumnItem*)nmlv->lParam);
+		}
+	}
+
+	void onSelChanged_Var(CCtrlCombo *pCombo)
+	{
+		SetupScreen(pCombo->GetItemData(pCombo->GetCurSel()));
+	}
+};
+
+int OnOptInit(WPARAM wParam, LPARAM)
+{
+	OPTIONSDIALOGPAGE odp = {};
+	odp.flags = ODPF_BOLDGROUPS;
+	odp.szGroup.a = LPGEN("Contacts");
+	odp.szTitle.a = LPGEN("Quick Search");
+	odp.position = 900003000;
+	odp.pDialog = new COptionsDlg();
+	g_plugin.addOptions(wParam, &odp);
+	return 0;
+}
diff --git a/plugins/QuickSearch/src/stdafx.cxx b/plugins/QuickSearch/src/stdafx.cxx
index 1ab0efee94..ebbde0ade1 100644
--- a/plugins/QuickSearch/src/stdafx.cxx
+++ b/plugins/QuickSearch/src/stdafx.cxx
@@ -1,18 +1,18 @@
-/*
-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/>.
-*/
-
+/*
+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"
\ No newline at end of file
diff --git a/plugins/QuickSearch/src/utils.cpp b/plugins/QuickSearch/src/utils.cpp
index 9db7133c3b..2cf1cdbc9f 100644
--- a/plugins/QuickSearch/src/utils.cpp
+++ b/plugins/QuickSearch/src/utils.cpp
@@ -1,556 +1,556 @@
-/*
-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"
-
-#define ACF_TYPE_NUMBER  0x00 // Parameter is number
-#define ACF_TYPE_STRING  0x01 // Parameter is ANSI String
-#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string
-#define ACF_TYPE_STRUCT  0x03 // Parameter is (result is in) structure
-#define ACF_TYPE_PARAM   0x08 // Parameter is Call parameter
-#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window
-#define ACF_TYPE_RESULT  0x0A // Parameter is previous action result
-#define ACF_TYPE_MASK    0x0F // parameter/result type mask
-
-ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) :
-	title(mir_wstrdup(pwszTitle)),
-	width(_width),
-	setting_type(_setting_type)
-{
-	bEnabled = true;
-}
-
-ColumnItem::ColumnItem(const ColumnItem &src)
-{
-	memcpy(this, &src, sizeof(ColumnItem));
-
-	title = mir_wstrdup(title);
-
-	switch (setting_type) {
-	case QST_SETTING:
-		module = mir_strdup(module);
-		setting = mir_strdup(setting);
-		break;
-
-	case QST_SCRIPT:
-		script = mir_wstrdup(script);
-		break;
-
-	case QST_SERVICE:
-		svc.service = mir_strdup(svc.service);
-		switch (svc.wFlags) {
-		case ACF_TYPE_NUMBER:
-		case ACF_TYPE_STRING:
-		case ACF_TYPE_UNICODE:
-			svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam);
-			break;
-		case ACF_TYPE_STRUCT:
-			svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam);
-			break;
-		}
-
-		switch (svc.lFlags) {
-		case ACF_TYPE_NUMBER:
-		case ACF_TYPE_STRING:
-		case ACF_TYPE_UNICODE:
-			svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam);
-			break;
-		case ACF_TYPE_STRUCT:
-			svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam);
-			break;
-		}
-		break;
-	}
-}
-
-ColumnItem::~ColumnItem()
-{
-	mir_free(title);
-
-	switch (setting_type) {
-	case QST_SETTING:
-		mir_free(module);
-		mir_free(setting);
-		break;
-
-	case QST_SCRIPT:
-		mir_free(script);
-		break;
-
-	case QST_SERVICE:
-		mir_free(svc.service);
-		switch (svc.wFlags) {
-		case ACF_TYPE_NUMBER:
-		case ACF_TYPE_STRING:
-		case ACF_TYPE_UNICODE:
-			mir_free((wchar_t *)svc.wParam);
-			break;
-		case ACF_TYPE_STRUCT:
-			mir_free((char *)svc.wParam);
-			break;
-		}
-
-		switch (svc.lFlags) {
-		case ACF_TYPE_NUMBER:
-		case ACF_TYPE_STRING:
-		case ACF_TYPE_UNICODE:
-			mir_free((wchar_t *)svc.lParam);
-			break;
-		case ACF_TYPE_STRUCT:
-			mir_free((char *)svc.lParam);
-			break;
-		}
-		break;
-	}
-}
-
-void ColumnItem::SetSpecialColumns()
-{
-    if (setting_type == QST_SETTING) {
-		 if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group"))
-			 isGroup = true;
-
-		 else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW"))
-			 isContainer = true;
-
-		 else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId"))
-			 isXstatus = true;
-
-		 else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled)
-			 isClient = true;
-	 }
-	 else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER)
-		 isGender = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// column functions
-
-int ListViewToColumn(int col)
-{
-	for (auto &it : g_plugin.m_columns) {
-		if (!it->bEnabled)
-			continue;
-
-		if (col-- <= 0)
-			return g_plugin.m_columns.indexOf(&it);
-	}
-	return -1;
-}
-
-int ColumnToListView(int col)
-{
-	int res = -1;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->bEnabled)
-			res++;
-
-		if (col-- <= 0)
-			break;
-	}
-	return res;
-}
-
-void LoadDefaultColumns(OBJLIST<ColumnItem> &dst)
-{
-	dst.destroy();
-
-	auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER);
-	pNew->other = QSTO_ACCOUNT;
-	dst.insert(pNew);
-
-	dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER));
-
-	pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID);
-	pNew->bFilter = true;
-	dst.insert(pNew);
-	
-	pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER);
-	pNew->bFilter = true;
-	pNew->other = QSTO_DISPLAYNAME;
-
-	pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME);
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME);
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING);
-	pNew->datatype = QSTS_STRING;
-	pNew->module = mir_strdup("CList");
-	pNew->setting = mir_strdup("Group");
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING);
-	pNew->datatype = QSTS_STRING;
-	pNew->module = mir_strdup("Tab_SRMsg");
-	pNew->setting = mir_strdup("containerW");
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL);
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING);
-	pNew->datatype = QSTS_STRING;
-	pNew->setting = mir_strdup("MirVer");
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER);
-	pNew->other = QSTO_LASTSEEN;
-	pNew->dwFlags = 0;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER);
-	pNew->other = QSTO_LASTEVENT;
-	pNew->dwFlags = 0;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING);
-	pNew->datatype = QSTS_TIMESTAMP;
-	pNew->setting = mir_strdup("LogonTS");
-	pNew->bFilter = true;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER);
-	pNew->other = QSTO_METACONTACT;
-	pNew->dwFlags = 0;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER);
-	pNew->other = QSTO_EVENTCOUNT;
-	pNew->dwFlags = 0;
-	dst.insert(pNew);
-
-	pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING);
-	pNew->datatype = QSTS_TIMESTAMP;
-	pNew->module = mir_strdup("UserInfo");
-	pNew->setting = mir_strdup("ContactAddTime");
-	dst.insert(pNew);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// window options
-
-void CMPlugin::LoadOptWnd()
-{
-	m_rect.bottom = getDword(so_mbottom);
-	m_rect.right = getDword(so_mright);
-	m_rect.left = getDword(so_mleft);
-	m_rect.top = getDword(so_mtop);
-
-	m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
-	m_sortOrder = getDword(so_columnsort);
-}
-
-void CMPlugin::SaveOptWnd()
-{
-	setDword(so_mbottom, m_rect.bottom);
-	setDword(so_mright, m_rect.right);
-	setDword(so_mleft, m_rect.left);
-	setDword(so_mtop, m_rect.top);
-
-	setDword(so_flags, m_flags);
-	setDword(so_columnsort, m_sortOrder);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// load options
-
-int CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst)
-{
-	m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
-	int numCols = getWord(so_numcolumns);
-
-	for (int i = 0; i < numCols; i++) {
-		auto *pNew = new ColumnItem(nullptr);
-		LoadColumn(i, *pNew);
-		dst.insert(pNew);
-	}
-
-	return numCols;
-}
-
-void CMPlugin::LoadColumn(int n, ColumnItem &col)
-{
-	char buf[127];
-	int offset = mir_snprintf(buf, "%s%d_", so_item, n);
-
-	strcpy(buf + offset, so_title); col.title = getWStringA(buf);
-	strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf);
-	strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf);
-	strcpy(buf + offset, so_width); col.width = getWord(buf);
-
-	switch (col.setting_type) {
-	case QST_SETTING:
-		strcpy(buf + offset, so_datatype); col.datatype = getWord(buf);
-		strcpy(buf + offset, so_module); col.module = getStringA(buf);
-		strcpy(buf + offset, so_setting); col.setting = getStringA(buf);
-		break;
-
-	case QST_SCRIPT:
-		strcpy(buf + offset, so_script); col.script = getWStringA(buf);
-		break;
-
-	case QST_CONTACTINFO:
-		strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf);
-		break;
-
-	case QST_SERVICE: 
-		offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
-		strcpy(buf + offset, so_service); col.svc.service = getStringA(buf);
-		strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf);
-		if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) {
-			col.setting_type = QST_OTHER;
-			col.other = QSTO_ACCOUNT;
-			break;
-		}
-
-		strcpy(buf + offset, so_wparam); 
-		LoadParamValue(buf, col.svc.wFlags, col.svc.wParam);
-
-		strcpy(buf + offset, so_lparam);
-		LoadParamValue(buf, col.svc.lFlags, col.svc.lParam);
-		break;
-
-	case QST_OTHER:
-		strcpy(buf + offset, so_other); col.other = getWord(buf);
-		break;
-	}
-}
-
-void CMPlugin::LoadParamValue(char *buf, uint32_t &dwFlags, LPARAM &dwWalue)
-{
-	char *pEnd = buf + strlen(buf);
-	strcpy(pEnd, "flags"); dwFlags = getDword(buf);
-
-	strcpy(pEnd, "value"); 
-	switch (dwFlags) {
-	case ACF_TYPE_NUMBER:
-	case ACF_TYPE_STRING:
-	case ACF_TYPE_UNICODE:
-		dwWalue = LPARAM(getWStringA(buf));
-		break;
-
-	case ACF_TYPE_STRUCT:
-		dwWalue = LPARAM(getStringA(buf));
-		break;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// save options
-
-static int ListSettings(const char *szSetting, void *param)
-{
-	if (!memcmp(szSetting, so_item, 4)) {
-		auto *pList = (LIST<char>*)param;
-		pList->insert(mir_strdup(szSetting));
-	}
-	return 0;
-}
-
-void CMPlugin::SaveOptions()
-{
-	// remove old settings
-	LIST<char> settings(30);
-	db_enum_settings(0, ListSettings, MODULENAME, &settings);
-
-	for (auto &it : settings) {
-		delSetting(it);
-		mir_free(it);
-	}
-
-	// write new settings
-	setDword(so_flags, m_flags);
-	setWord(so_numcolumns, m_columns.getCount());
-
-	int i = 0;
-	for (auto &it : m_columns)
-		SaveColumn(i++, *it);
-}
-
-void CMPlugin::SaveColumn(int n, const ColumnItem &col)
-{
-	char buf[127];
-	int offset = mir_snprintf(buf, "%s%d_", so_item, n);
-
-	strcpy(buf + offset, so_title); setWString(buf, col.title);
-	strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type);
-	strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags);
-	strcpy(buf + offset, so_width); setWord(buf, col.width);
-
-	switch (col.setting_type) {
-	case QST_SETTING:
-		strcpy(buf + offset, so_datatype); setWord(buf, col.datatype);
-		strcpy(buf + offset, so_module); setString(buf, col.module);
-		strcpy(buf + offset, so_setting); setString(buf, col.setting);
-		break;
-
-	case QST_SCRIPT:
-		strcpy(buf + offset, so_script); setWString(buf, col.script);
-		break;
-
-	case QST_CONTACTINFO:
-		strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype);
-		break;
-
-	case QST_SERVICE: 
-		offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
-		strcpy(buf + offset, so_service); setString(buf, col.svc.service);
-		strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags);
-		
-		strcpy(buf + offset, so_wparam); 
-		SaveParamValue(buf, col.svc.wFlags, col.svc.wParam);
-		
-		strcpy(buf + offset, so_lparam);
-		SaveParamValue(buf, col.svc.lFlags, col.svc.lParam);
-		break;
-
-	case QST_OTHER:
-		strcpy(buf + offset, so_other); setWord(buf, col.other);
-		break;
-	}
-}
-
-void CMPlugin::SaveParamValue(char *buf, uint32_t flags, LPARAM value)
-{
-	char *pEnd = buf + strlen(buf);
-	strcpy(pEnd, "flags"); setDword(buf, flags);
-
-	strcpy(pEnd, "value"); 
-	switch (flags) {
-	case ACF_TYPE_NUMBER:
-	case ACF_TYPE_STRING:
-	case ACF_TYPE_UNICODE:
-		setWString(buf, (wchar_t *)value);
-		break;
-
-	case ACF_TYPE_STRUCT:
-		setString(buf, (char *)value);
-		break;
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-const wchar_t* cnf2str(int cnf)
-{
-	switch (cnf) {
-	case CNF_FIRSTNAME:  return TranslateT("First name");
-	case CNF_LASTNAME:   return TranslateT("Last name");
-	case CNF_NICK:       return TranslateT("Nick");
-	case CNF_CUSTOMNICK: return TranslateT("Custom nick");
-	case CNF_EMAIL:      return TranslateT("Email");
-	case CNF_CITY:       return TranslateT("City");
-	case CNF_STATE:      return TranslateT("State");
-	case CNF_COUNTRY:    return TranslateT("Country");
-	case CNF_PHONE:      return TranslateT("Phone");
-	case CNF_HOMEPAGE:   return TranslateT("Homepage");
-	case CNF_ABOUT:      return TranslateT("About");
-	case CNF_GENDER:     return TranslateT("Gender");
-	case CNF_AGE:        return TranslateT("Age");
-	case CNF_FIRSTLAST:  return TranslateT("First name/Last name");
-	case CNF_UNIQUEID:   return TranslateT("Unique ID");
-	case CNF_FAX:        return TranslateT("Fax");
-	case CNF_CELLULAR:   return TranslateT("Cellular");
-	case CNF_TIMEZONE:   return TranslateT("Time zone");
-	case CNF_MYNOTES:    return TranslateT("My notes");
-	case CNF_BIRTHDAY:   return TranslateT("Birth day");
-	case CNF_BIRTHMONTH: return TranslateT("Birth month");
-	case CNF_BIRTHYEAR:  return TranslateT("Birth year");
-	case CNF_STREET:     return TranslateT("Street");
-	case CNF_ZIP:        return TranslateT("ZIP code");
-	case CNF_LANGUAGE1:  return TranslateT("Language #1");
-	case CNF_LANGUAGE2:  return TranslateT("Language #2");
-	case CNF_LANGUAGE3:  return TranslateT("Language #3");
-	case CNF_CONAME:     return TranslateT("Company name");
-	case CNF_CODEPT:     return TranslateT("Company department");
-	case CNF_COPOSITION: return TranslateT("Company position");
-	case CNF_COSTREET:   return TranslateT("Company street");
-	case CNF_COCITY:     return TranslateT("Company city");
-	case CNF_COSTATE:    return TranslateT("Company state");
-	case CNF_COZIP:      return TranslateT("Company ZIP");
-	case CNF_COCOUNTRY:  return TranslateT("Company country");
-	case CNF_COHOMEPAGE: return TranslateT("Company homepage");
-	case CNF_DISPLAYUID: return TranslateT("Display ID");
-	}
-	return nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// formatters
-
-wchar_t* BuildLastSeenTime(uint32_t ts)
-{
-	int year = ts / (60 * 24 * 31 * 356);
-	if (year == 0)
-		return nullptr;
-
-	year += 1980; ts = ts % (60 * 24 * 31 * 356);
-
-	int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31);
-	int day = ts / (60 * 24); ts = ts % (60 * 24);
-	int hours = ts / 60;
-	int mins = ts % 60;
-
-	return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach();
-}
-
-uint32_t BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule)
-{
-	int year = db_get_w(hContact, szModule, "Year");
-	if (year == 0)
-		return 0;
-
-	int day = db_get_w(hContact, szModule, "Day");
-	int month = db_get_w(hContact, szModule, "Month");
-	int hours = db_get_w(hContact, szModule, "Hours");
-	int minutes = db_get_w(hContact, szModule, "Minutes");
-
-	return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes;
-}
-
-wchar_t* TimeToStrW(uint32_t timestamp)
-{
-	wchar_t buf[63];
-	TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf));
-	return mir_wstrdup(buf);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void SnapToScreen(RECT &rc)
-{
-	int left = GetSystemMetrics(SM_XVIRTUALSCREEN);
-	int top = GetSystemMetrics(SM_YVIRTUALSCREEN);
-	int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left;
-	int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top;
-	if (rc.right > right)
-		rc.right = right;
-	if (rc.bottom > bottom)
-		rc.bottom = bottom;
-	if (rc.left < left)
-		rc.left = left;
-	if (rc.top < top)
-		rc.top = top;
-}
+/*
+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"
+
+#define ACF_TYPE_NUMBER  0x00 // Parameter is number
+#define ACF_TYPE_STRING  0x01 // Parameter is ANSI String
+#define ACF_TYPE_UNICODE 0x02 // Parameter is Unicode string
+#define ACF_TYPE_STRUCT  0x03 // Parameter is (result is in) structure
+#define ACF_TYPE_PARAM   0x08 // Parameter is Call parameter
+#define ACF_TYPE_CURRENT 0x09 // Parameter is ignored, used current user handle from current message window
+#define ACF_TYPE_RESULT  0x0A // Parameter is previous action result
+#define ACF_TYPE_MASK    0x0F // parameter/result type mask
+
+ColumnItem::ColumnItem(const wchar_t *pwszTitle, int _width, int _setting_type) :
+	title(mir_wstrdup(pwszTitle)),
+	width(_width),
+	setting_type(_setting_type)
+{
+	bEnabled = true;
+}
+
+ColumnItem::ColumnItem(const ColumnItem &src)
+{
+	memcpy(this, &src, sizeof(ColumnItem));
+
+	title = mir_wstrdup(title);
+
+	switch (setting_type) {
+	case QST_SETTING:
+		module = mir_strdup(module);
+		setting = mir_strdup(setting);
+		break;
+
+	case QST_SCRIPT:
+		script = mir_wstrdup(script);
+		break;
+
+	case QST_SERVICE:
+		svc.service = mir_strdup(svc.service);
+		switch (svc.wFlags) {
+		case ACF_TYPE_NUMBER:
+		case ACF_TYPE_STRING:
+		case ACF_TYPE_UNICODE:
+			svc.wParam = (WPARAM)mir_wstrdup((wchar_t *)svc.wParam);
+			break;
+		case ACF_TYPE_STRUCT:
+			svc.wParam = (WPARAM)mir_strdup((char *)svc.wParam);
+			break;
+		}
+
+		switch (svc.lFlags) {
+		case ACF_TYPE_NUMBER:
+		case ACF_TYPE_STRING:
+		case ACF_TYPE_UNICODE:
+			svc.lParam = (WPARAM)mir_wstrdup((wchar_t *)svc.lParam);
+			break;
+		case ACF_TYPE_STRUCT:
+			svc.lParam = (WPARAM)mir_strdup((char *)svc.lParam);
+			break;
+		}
+		break;
+	}
+}
+
+ColumnItem::~ColumnItem()
+{
+	mir_free(title);
+
+	switch (setting_type) {
+	case QST_SETTING:
+		mir_free(module);
+		mir_free(setting);
+		break;
+
+	case QST_SCRIPT:
+		mir_free(script);
+		break;
+
+	case QST_SERVICE:
+		mir_free(svc.service);
+		switch (svc.wFlags) {
+		case ACF_TYPE_NUMBER:
+		case ACF_TYPE_STRING:
+		case ACF_TYPE_UNICODE:
+			mir_free((wchar_t *)svc.wParam);
+			break;
+		case ACF_TYPE_STRUCT:
+			mir_free((char *)svc.wParam);
+			break;
+		}
+
+		switch (svc.lFlags) {
+		case ACF_TYPE_NUMBER:
+		case ACF_TYPE_STRING:
+		case ACF_TYPE_UNICODE:
+			mir_free((wchar_t *)svc.lParam);
+			break;
+		case ACF_TYPE_STRUCT:
+			mir_free((char *)svc.lParam);
+			break;
+		}
+		break;
+	}
+}
+
+void ColumnItem::SetSpecialColumns()
+{
+    if (setting_type == QST_SETTING) {
+		 if (datatype == QSTS_STRING && !mir_strcmp(module, "CList") && !mir_strcmp(setting, "Group"))
+			 isGroup = true;
+
+		 else if (datatype == QSTS_STRING && !mir_strcmp(module, "Tab_SRMsg") && !mir_strcmp(setting, "containerW"))
+			 isContainer = true;
+
+		 else if (datatype == QSTS_BYTE && !mir_strcmpi(setting, "XStatusId"))
+			 isXstatus = true;
+
+		 else if (datatype == QSTS_STRING && !mir_strcmp(setting, "MirVer") && g_bFingerInstalled)
+			 isClient = true;
+	 }
+	 else if (setting_type == QST_CONTACTINFO && cnftype == CNF_GENDER)
+		 isGender = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// column functions
+
+int ListViewToColumn(int col)
+{
+	for (auto &it : g_plugin.m_columns) {
+		if (!it->bEnabled)
+			continue;
+
+		if (col-- <= 0)
+			return g_plugin.m_columns.indexOf(&it);
+	}
+	return -1;
+}
+
+int ColumnToListView(int col)
+{
+	int res = -1;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->bEnabled)
+			res++;
+
+		if (col-- <= 0)
+			break;
+	}
+	return res;
+}
+
+void LoadDefaultColumns(OBJLIST<ColumnItem> &dst)
+{
+	dst.destroy();
+
+	auto *pNew = new ColumnItem(TranslateT("Account"), 82, QST_OTHER);
+	pNew->other = QSTO_ACCOUNT;
+	dst.insert(pNew);
+
+	dst.insert(new ContactIntoColumn(TranslateT("Gender"), 20, CNF_GENDER));
+
+	pNew = new ContactIntoColumn(TranslateT("UserID"), 80, CNF_UNIQUEID);
+	pNew->bFilter = true;
+	dst.insert(pNew);
+	
+	pNew = new ContactIntoColumn(TranslateT("Nickname"), 76, QST_OTHER);
+	pNew->bFilter = true;
+	pNew->other = QSTO_DISPLAYNAME;
+
+	pNew = new ContactIntoColumn(TranslateT("First name"), 68, CNF_FIRSTNAME);
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ContactIntoColumn(TranslateT("Last name"), 66, CNF_LASTNAME);
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Group"), 80, QST_SETTING);
+	pNew->datatype = QSTS_STRING;
+	pNew->module = mir_strdup("CList");
+	pNew->setting = mir_strdup("Group");
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Container"), 80, QST_SETTING);
+	pNew->datatype = QSTS_STRING;
+	pNew->module = mir_strdup("Tab_SRMsg");
+	pNew->setting = mir_strdup("containerW");
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ContactIntoColumn(TranslateT("Email"), 116, CNF_EMAIL);
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Client ID"), 60, QST_SETTING);
+	pNew->datatype = QSTS_STRING;
+	pNew->setting = mir_strdup("MirVer");
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Last seen"), 116, QST_OTHER);
+	pNew->other = QSTO_LASTSEEN;
+	pNew->dwFlags = 0;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Last event"), 100, QST_OTHER);
+	pNew->other = QSTO_LASTEVENT;
+	pNew->dwFlags = 0;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Online since"), 100, QST_SETTING);
+	pNew->datatype = QSTS_TIMESTAMP;
+	pNew->setting = mir_strdup("LogonTS");
+	pNew->bFilter = true;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Metacontact"), 50, QST_OTHER);
+	pNew->other = QSTO_METACONTACT;
+	pNew->dwFlags = 0;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Event count"), 50, QST_OTHER);
+	pNew->other = QSTO_EVENTCOUNT;
+	pNew->dwFlags = 0;
+	dst.insert(pNew);
+
+	pNew = new ColumnItem(TranslateT("Contact add time"), 80, QST_SETTING);
+	pNew->datatype = QSTS_TIMESTAMP;
+	pNew->module = mir_strdup("UserInfo");
+	pNew->setting = mir_strdup("ContactAddTime");
+	dst.insert(pNew);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// window options
+
+void CMPlugin::LoadOptWnd()
+{
+	m_rect.bottom = getDword(so_mbottom);
+	m_rect.right = getDword(so_mright);
+	m_rect.left = getDword(so_mleft);
+	m_rect.top = getDword(so_mtop);
+
+	m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
+	m_sortOrder = getDword(so_columnsort);
+}
+
+void CMPlugin::SaveOptWnd()
+{
+	setDword(so_mbottom, m_rect.bottom);
+	setDword(so_mright, m_rect.right);
+	setDword(so_mleft, m_rect.left);
+	setDword(so_mtop, m_rect.top);
+
+	setDword(so_flags, m_flags);
+	setDword(so_columnsort, m_sortOrder);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// load options
+
+int CMPlugin::LoadColumns(OBJLIST<ColumnItem> &dst)
+{
+	m_flags = getDword(so_flags, QSO_SORTBYSTATUS + QSO_DRAWGRID + QSO_CLIENTICONS + QSO_COLORIZE + QSO_SORTASC);
+	int numCols = getWord(so_numcolumns);
+
+	for (int i = 0; i < numCols; i++) {
+		auto *pNew = new ColumnItem(nullptr);
+		LoadColumn(i, *pNew);
+		dst.insert(pNew);
+	}
+
+	return numCols;
+}
+
+void CMPlugin::LoadColumn(int n, ColumnItem &col)
+{
+	char buf[127];
+	int offset = mir_snprintf(buf, "%s%d_", so_item, n);
+
+	strcpy(buf + offset, so_title); col.title = getWStringA(buf);
+	strcpy(buf + offset, so_setting_type); col.setting_type = getWord(buf);
+	strcpy(buf + offset, so_flags); col.dwFlags = getWord(buf);
+	strcpy(buf + offset, so_width); col.width = getWord(buf);
+
+	switch (col.setting_type) {
+	case QST_SETTING:
+		strcpy(buf + offset, so_datatype); col.datatype = getWord(buf);
+		strcpy(buf + offset, so_module); col.module = getStringA(buf);
+		strcpy(buf + offset, so_setting); col.setting = getStringA(buf);
+		break;
+
+	case QST_SCRIPT:
+		strcpy(buf + offset, so_script); col.script = getWStringA(buf);
+		break;
+
+	case QST_CONTACTINFO:
+		strcpy(buf + offset, so_cnftype); col.cnftype = getWord(buf);
+		break;
+
+	case QST_SERVICE: 
+		offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
+		strcpy(buf + offset, so_service); col.svc.service = getStringA(buf);
+		strcpy(buf + offset, so_restype); col.svc.flags = getDword(buf);
+		if (!mir_strcmp(col.svc.service, "Proto/GetContactBaseAccount")) {
+			col.setting_type = QST_OTHER;
+			col.other = QSTO_ACCOUNT;
+			break;
+		}
+
+		strcpy(buf + offset, so_wparam); 
+		LoadParamValue(buf, col.svc.wFlags, col.svc.wParam);
+
+		strcpy(buf + offset, so_lparam);
+		LoadParamValue(buf, col.svc.lFlags, col.svc.lParam);
+		break;
+
+	case QST_OTHER:
+		strcpy(buf + offset, so_other); col.other = getWord(buf);
+		break;
+	}
+}
+
+void CMPlugin::LoadParamValue(char *buf, uint32_t &dwFlags, LPARAM &dwWalue)
+{
+	char *pEnd = buf + strlen(buf);
+	strcpy(pEnd, "flags"); dwFlags = getDword(buf);
+
+	strcpy(pEnd, "value"); 
+	switch (dwFlags) {
+	case ACF_TYPE_NUMBER:
+	case ACF_TYPE_STRING:
+	case ACF_TYPE_UNICODE:
+		dwWalue = LPARAM(getWStringA(buf));
+		break;
+
+	case ACF_TYPE_STRUCT:
+		dwWalue = LPARAM(getStringA(buf));
+		break;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// save options
+
+static int ListSettings(const char *szSetting, void *param)
+{
+	if (!memcmp(szSetting, so_item, 4)) {
+		auto *pList = (LIST<char>*)param;
+		pList->insert(mir_strdup(szSetting));
+	}
+	return 0;
+}
+
+void CMPlugin::SaveOptions()
+{
+	// remove old settings
+	LIST<char> settings(30);
+	db_enum_settings(0, ListSettings, MODULENAME, &settings);
+
+	for (auto &it : settings) {
+		delSetting(it);
+		mir_free(it);
+	}
+
+	// write new settings
+	setDword(so_flags, m_flags);
+	setWord(so_numcolumns, m_columns.getCount());
+
+	int i = 0;
+	for (auto &it : m_columns)
+		SaveColumn(i++, *it);
+}
+
+void CMPlugin::SaveColumn(int n, const ColumnItem &col)
+{
+	char buf[127];
+	int offset = mir_snprintf(buf, "%s%d_", so_item, n);
+
+	strcpy(buf + offset, so_title); setWString(buf, col.title);
+	strcpy(buf + offset, so_setting_type); setWord(buf, col.setting_type);
+	strcpy(buf + offset, so_flags); setWord(buf, col.dwFlags);
+	strcpy(buf + offset, so_width); setWord(buf, col.width);
+
+	switch (col.setting_type) {
+	case QST_SETTING:
+		strcpy(buf + offset, so_datatype); setWord(buf, col.datatype);
+		strcpy(buf + offset, so_module); setString(buf, col.module);
+		strcpy(buf + offset, so_setting); setString(buf, col.setting);
+		break;
+
+	case QST_SCRIPT:
+		strcpy(buf + offset, so_script); setWString(buf, col.script);
+		break;
+
+	case QST_CONTACTINFO:
+		strcpy(buf + offset, so_cnftype); setWord(buf, col.cnftype);
+		break;
+
+	case QST_SERVICE: 
+		offset = mir_snprintf(buf, "%s%d/service/", so_item, n);
+		strcpy(buf + offset, so_service); setString(buf, col.svc.service);
+		strcpy(buf + offset, so_restype); setDword(buf, col.svc.flags);
+		
+		strcpy(buf + offset, so_wparam); 
+		SaveParamValue(buf, col.svc.wFlags, col.svc.wParam);
+		
+		strcpy(buf + offset, so_lparam);
+		SaveParamValue(buf, col.svc.lFlags, col.svc.lParam);
+		break;
+
+	case QST_OTHER:
+		strcpy(buf + offset, so_other); setWord(buf, col.other);
+		break;
+	}
+}
+
+void CMPlugin::SaveParamValue(char *buf, uint32_t flags, LPARAM value)
+{
+	char *pEnd = buf + strlen(buf);
+	strcpy(pEnd, "flags"); setDword(buf, flags);
+
+	strcpy(pEnd, "value"); 
+	switch (flags) {
+	case ACF_TYPE_NUMBER:
+	case ACF_TYPE_STRING:
+	case ACF_TYPE_UNICODE:
+		setWString(buf, (wchar_t *)value);
+		break;
+
+	case ACF_TYPE_STRUCT:
+		setString(buf, (char *)value);
+		break;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+const wchar_t* cnf2str(int cnf)
+{
+	switch (cnf) {
+	case CNF_FIRSTNAME:  return TranslateT("First name");
+	case CNF_LASTNAME:   return TranslateT("Last name");
+	case CNF_NICK:       return TranslateT("Nick");
+	case CNF_CUSTOMNICK: return TranslateT("Custom nick");
+	case CNF_EMAIL:      return TranslateT("Email");
+	case CNF_CITY:       return TranslateT("City");
+	case CNF_STATE:      return TranslateT("State");
+	case CNF_COUNTRY:    return TranslateT("Country");
+	case CNF_PHONE:      return TranslateT("Phone");
+	case CNF_HOMEPAGE:   return TranslateT("Homepage");
+	case CNF_ABOUT:      return TranslateT("About");
+	case CNF_GENDER:     return TranslateT("Gender");
+	case CNF_AGE:        return TranslateT("Age");
+	case CNF_FIRSTLAST:  return TranslateT("First name/Last name");
+	case CNF_UNIQUEID:   return TranslateT("Unique ID");
+	case CNF_FAX:        return TranslateT("Fax");
+	case CNF_CELLULAR:   return TranslateT("Cellular");
+	case CNF_TIMEZONE:   return TranslateT("Time zone");
+	case CNF_MYNOTES:    return TranslateT("My notes");
+	case CNF_BIRTHDAY:   return TranslateT("Birth day");
+	case CNF_BIRTHMONTH: return TranslateT("Birth month");
+	case CNF_BIRTHYEAR:  return TranslateT("Birth year");
+	case CNF_STREET:     return TranslateT("Street");
+	case CNF_ZIP:        return TranslateT("ZIP code");
+	case CNF_LANGUAGE1:  return TranslateT("Language #1");
+	case CNF_LANGUAGE2:  return TranslateT("Language #2");
+	case CNF_LANGUAGE3:  return TranslateT("Language #3");
+	case CNF_CONAME:     return TranslateT("Company name");
+	case CNF_CODEPT:     return TranslateT("Company department");
+	case CNF_COPOSITION: return TranslateT("Company position");
+	case CNF_COSTREET:   return TranslateT("Company street");
+	case CNF_COCITY:     return TranslateT("Company city");
+	case CNF_COSTATE:    return TranslateT("Company state");
+	case CNF_COZIP:      return TranslateT("Company ZIP");
+	case CNF_COCOUNTRY:  return TranslateT("Company country");
+	case CNF_COHOMEPAGE: return TranslateT("Company homepage");
+	case CNF_DISPLAYUID: return TranslateT("Display ID");
+	}
+	return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// formatters
+
+wchar_t* BuildLastSeenTime(uint32_t ts)
+{
+	int year = ts / (60 * 24 * 31 * 356);
+	if (year == 0)
+		return nullptr;
+
+	year += 1980; ts = ts % (60 * 24 * 31 * 356);
+
+	int month = ts / (60 * 24 * 31); ts = ts % (60 * 24 * 31);
+	int day = ts / (60 * 24); ts = ts % (60 * 24);
+	int hours = ts / 60;
+	int mins = ts % 60;
+
+	return CMStringW(FORMAT, L"%02d.%02d.%04d - %02d:%02d", year, month, day, hours, mins).Detach();
+}
+
+uint32_t BuildLastSeenTimeInt(MCONTACT hContact, const char *szModule)
+{
+	int year = db_get_w(hContact, szModule, "Year");
+	if (year == 0)
+		return 0;
+
+	int day = db_get_w(hContact, szModule, "Day");
+	int month = db_get_w(hContact, szModule, "Month");
+	int hours = db_get_w(hContact, szModule, "Hours");
+	int minutes = db_get_w(hContact, szModule, "Minutes");
+
+	return ((((year - 1980) * 356 + month) * 31 + day) * 24 + hours) * 60 + minutes;
+}
+
+wchar_t* TimeToStrW(uint32_t timestamp)
+{
+	wchar_t buf[63];
+	TimeZone_ToStringW(timestamp, L"d - t", buf, _countof(buf));
+	return mir_wstrdup(buf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void SnapToScreen(RECT &rc)
+{
+	int left = GetSystemMetrics(SM_XVIRTUALSCREEN);
+	int top = GetSystemMetrics(SM_YVIRTUALSCREEN);
+	int right = GetSystemMetrics(SM_CXVIRTUALSCREEN) + left;
+	int bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN) + top;
+	if (rc.right > right)
+		rc.right = right;
+	if (rc.bottom > bottom)
+		rc.bottom = bottom;
+	if (rc.left < left)
+		rc.left = left;
+	if (rc.top < top)
+		rc.top = top;
+}
diff --git a/plugins/QuickSearch/src/version.h b/plugins/QuickSearch/src/version.h
index c054045e77..d1f948c4c1 100644
--- a/plugins/QuickSearch/src/version.h
+++ b/plugins/QuickSearch/src/version.h
@@ -10,4 +10,4 @@
 #define __DESCRIPTION             "This plugin allows you to quick search for nickname, firstname, lastname, email, uin in your contact list."
 #define __AUTHOR                  "Bethoven, Awkward"
 #define __AUTHORWEB               "https://miranda-ng.org/p/QuickSearch"
-#define __COPYRIGHT               "© 2004-05 Bethoven; 2006-13 Awkward; 2014-22 Miranda NG team"
+#define __COPYRIGHT               "© 2004-05 Bethoven; 2006-13 Awkward; 2014-23 Miranda NG team"
diff --git a/plugins/QuickSearch/src/window.cpp b/plugins/QuickSearch/src/window.cpp
index 891d39aa92..8624be7d98 100644
--- a/plugins/QuickSearch/src/window.cpp
+++ b/plugins/QuickSearch/src/window.cpp
@@ -1,876 +1,876 @@
-/*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it &/|
-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 | 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"
-
-#define IDM_STAYONTOP      (WM_USER+1)
-
-static QSMainDlg *g_pDlg = 0;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// edit control window procedure
-
-static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-	auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
-	if (pDlg)
-		if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
-			return res;
-
-	return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
-}
-
-INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
-{
-	switch (msg) {
-	case WM_CHAR:
-		if (wParam == 27) // Escape
-			Close();
-		break;
-
-	case WM_KEYUP:
-		if (wParam == VK_RETURN) {
-			if (m_grid.GetSelectedCount() == 1)
-				ShowContactMsgDlg(GetFocusedContact());
-			return 0;
-		}
-		break;
-
-	case WM_KEYDOWN:
-		int count = m_grid.GetItemCount();
-		int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
-		int next = -1;
-		if (count > 0) {
-			switch (wParam) {
-			case VK_UP:
-				if (current > 0)
-					next = current - 1;
-				break;
-
-			case VK_DOWN:
-				if (current < count - 1)
-					next = current + 1;
-				break;
-
-			case VK_F5:
-				onClick_Refresh(0);
-				return 0;
-
-			case VK_NEXT:
-			case VK_PRIOR:
-				int perpage = m_grid.GetCountPerPage();
-				if (wParam == VK_NEXT)
-					next = min(current + perpage, count);
-				else
-					next = max(current - perpage, 0);
-				break;
-			}
-		}
-
-		if (next >= 0) {
-			m_grid.SetItemState(-1, 0, LVIS_SELECTED);
-			m_grid.SetCurSel(next);
-			m_grid.EnsureVisible(next, FALSE);
-		}
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// list header window procedure
-
-static void MakeColumnMenu()
-{
-	HMENU hMenu = CreatePopupMenu();
-
-	for (auto &it : g_plugin.m_columns) {
-		int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
-		AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
-	}
-
-	POINT pt;
-	GetCursorPos(&pt);
-
-	int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
-	if (id >= 100)
-		g_pDlg->ToggleColumn(id - 100);
-
-	DestroyMenu(hMenu);
-}
-
-void QSMainDlg::ToggleColumn(int col)
-{
-	auto &pCol = g_plugin.m_columns[col];
-
-	if (!pCol.bEnabled) { // show column
-		pCol.bEnabled = true;
-
-		if (!pCol.bInit) {
-			for (auto &it : m_rows)
-				it->pValues[col].LoadOneItem(it->hContact, pCol, this);
-			pCol.bInit = true;
-		}
-
-		// screen
-		int lvcol = ColumnToListView(col);
-		AddColumn(lvcol, &pCol);
-		
-		int nCount = m_grid.GetItemCount();
-		for (int i = 0; i < nCount; i++) {
-			auto *pRow = GetRow(i);
-
-			LV_ITEMW li;
-			li.iItem = i;
-			li.iSubItem = lvcol;
-			li.mask = LVIF_TEXT;
-			li.pszText = pRow->pValues[col].text;
-			if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
-				li.mask |= LVIF_IMAGE;
-			m_grid.SetItem(&li);
-		}
-	}
-	else { // hide column
-		int cnt = 0;
-		for (auto &it : g_plugin.m_columns)
-			if (it->bEnabled)
-				cnt++;
-
-		// keep at least one visible column (1 + this)
-		if (cnt > 2) {
-			m_grid.DeleteColumn(col);
-			pCol.bEnabled = false;
-		}
-	}
-}
-
-static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-	switch (msg) {
-	case WM_RBUTTONUP:
-		return 0;
-
-	case WM_RBUTTONDOWN:
-		MakeColumnMenu();
-		break;
-	}
-
-	return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// grid list window procedure
-
-static int OldHSubItem = 0, OldHItem = 0;
-
-static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
-	auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
-	if (pDlg)
-		if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
-			return res;
-
-	return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
-}
-
-INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
-{
-	LV_HITTESTINFO pinfo;
-
-	switch (msg) {
-	case WM_CHAR:
-		switch (wParam) { // ESC
-		case 27:
-			Close();
-			break;
-
-		case 1: // Cltr+A
-			m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
-			break;
-
-		case 3: // Ctrl-C
-			CopyMultiLines();
-			break;
-
-		case 8: // backspace
-			edtFilter.SendMsg(msg, wParam, lParam);
-			break;
-		}
-
-		if (wParam >= 32 && wParam <= 127) // letters
-			edtFilter.SendMsg(msg, wParam, lParam);
-		break;
-
-	case WM_MOUSEMOVE:
-		pinfo.pt.x = LOWORD(lParam);
-		pinfo.pt.y = HIWORD(lParam);
-		pinfo.flags = 0;
-		if (m_grid.SubItemHitTest(&pinfo) == -1)
-			break;
-
-		if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
-			OldHSubItem = pinfo.iSubItem;
-			OldHItem = pinfo.iItem;
-
-			if (g_bTipperInstalled) {
-				if (TTShowed) {
-					TTShowed = false;
-					Tipper_Hide();
-				}
-				m_hover.Stop();
-
-				if (OldHSubItem == 0)
-					m_hover.Start(450);
-			}
-
-			TOOLINFOW ti = {};
-			ti.cbSize = sizeof(ti);
-			ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
-			ti.hwnd = m_hwnd;
-			ti.uId = LPARAM(m_hwnd);
-
-			int num = ListViewToColumn(OldHSubItem);
-			auto &pCol = g_plugin.m_columns[num];
-			if (pCol.isXstatus || pCol.isGender) {
-				auto *pRow = GetRow(OldHItem);
-				if (pCol.isGender) {
-					switch (pRow->pValues[num].data) {
-					case 'M': ti.lpszText = TranslateT("Male"); break;
-					case 'F': ti.lpszText = TranslateT("Female"); break;
-					default: ti.lpszText = TranslateT("Unknown"); break;
-					}
-				}
-				else {
-					wchar_t buf[256];
-					mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
-					int iStatus = _wtoi(buf);
-
-					CUSTOM_STATUS ics = {};
-					ics.cbSize = sizeof(ics);
-					ics.status = &iStatus;
-					ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
-					ics.pwszName = buf;
-					CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
-					ti.lpszText = TranslateW(buf);
-				}
-			}
-
-			SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
-		}
-		break;
-
-	case WM_KEYUP:
-		switch (wParam) {
-		case VK_RETURN:
-			if (m_grid.GetSelectedCount() == 1)
-				ShowContactMsgDlg(GetFocusedContact());
-			break;
-
-		case VK_INSERT:
-			CallService(MS_FINDADD_FINDADD, 0, 0);
-			break;
-
-		case VK_DELETE:
-			lParam = m_grid.GetSelectedCount();
-			if (lParam > 1)
-				DeleteByList();
-			else if (lParam == 1)
-				DeleteOneContact(GetFocusedContact());
-			break;
-
-		case VK_F5:
-			onClick_Refresh(0);
-			break;
-		}
-		break;
-
-	case WM_NOTIFY:
-		if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
-			NMHEADER *pdhr = (NMHEADER *)lParam;
-			if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
-				int i = ListViewToColumn(pdhr->iItem);
-				auto &pCol = g_plugin.m_columns[i];
-
-				if (pdhr->pitem->fmt & HDF_CHECKED) {
-					pCol.bFilter = false;
-					pdhr->pitem->fmt &= ~HDF_CHECKED;
-				}
-				else {
-					pCol.bFilter = true;
-					pdhr->pitem->fmt |= HDF_CHECKED;
-				}
-
-				SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
-				FillGrid();
-				return TRUE;
-			}
-		}
-	}
-	return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// QSMainDlg class implementation
-
-static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
-{
-	if (p1->bAccDel != p2->bAccDel)
-		return (p1->bAccDel) ? 1 : -1;
-
-	if (p1->bAccOff != p2->bAccOff)
-		return (p1->bAccOff) ? 1 : -1;
-
-	return mir_strcmp(p1->szProto, p2->szProto);
-}
-
-QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
-	CDlgBase(g_plugin, IDD_MAIN),
-	m_rows(50),
-	m_patterns(1),
-	m_sbdata(10, CompareSb),
-	m_grid(this, IDC_LIST),
-	m_hover(this, 10),
-	cmbProto(this, IDC_CB_PROTOCOLS),
-	edtFilter(this, IDC_E_SEARCHTEXT),
-	btnRefresh(this, IDC_REFRESH),
-	chkColorize(this, IDC_CH_COLORIZE),
-	chkShowOffline(this, IDC_CH_SHOWOFFLINE)
-{
-	SetMinSize(300, 160);
-
-	if (pwszPattern)
-		m_wszPatternBuf = mir_wstrdup(pwszPattern);
-	else if (g_plugin.m_flags & QSO_SAVEPATTERN)
-		m_wszPatternBuf = g_plugin.getWStringA("pattern");
-
-	m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
-
-	m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
-	m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
-	m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
-	m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
-
-	btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
-
-	cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
-
-	edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
-	chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
-	chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
-}
-
-bool QSMainDlg::OnInitDialog()
-{
-	g_pDlg = this;
-	mnuhandle = 0;
-
-	SetCaption(TranslateT("Quick Search"));
-
-	hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
-
-	HMENU smenu = GetSystemMenu(m_hwnd, false);
-	InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
-	InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
-
-	if (g_plugin.m_flags & QSO_STAYONTOP) {
-		CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
-		SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
-	}
-
-	chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
-
-	szFilterProto = nullptr; // display all protocols
-	if (g_plugin.m_flags & QSO_SHOWOFFLINE)
-		bShowOffline = true;
-
-	chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
-
-	// Window
-	INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
-	if (g_plugin.m_flags & QSO_TOOLSTYLE)
-		tmp |= WS_EX_TOOLWINDOW;
-	else
-		tmp &= ~WS_EX_TOOLWINDOW;
-	SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
-
-	SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
-
-	// ListView
-	m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
-
-	tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
-		LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
-	if (g_plugin.m_flags & QSO_DRAWGRID)
-		tmp |= LVS_EX_GRIDLINES;
-	m_grid.SetExtendedListViewStyle(tmp);
-
-	// ListView header
-	HWND header = m_grid.GetHeader();
-	SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
-
-	mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
-
-	SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
-	mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
-	
-	SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
-	mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
-
-	FillProtoCombo();
-
-	PrepareTable();
-
-	if (m_wszPatternBuf) {
-		edtFilter.SetText(m_wszPatternBuf);
-		MakePattern(m_wszPatternBuf);
-	}
-	FillGrid();
-
-	// Show sorting column
-	HDITEM hdi = {};
-	hdi.mask = HDI_FORMAT;
-	SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-	if (g_plugin.m_flags & QSO_SORTASC)
-		hdi.fmt |= HDF_SORTUP;
-	else
-		hdi.fmt |= HDF_SORTDOWN;
-	SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
-	RECT rc = g_plugin.m_rect;
-	::SnapToScreen(rc);
-	::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
-
-	HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
-
-	TOOLINFOW ti;
-	ti.cbSize = sizeof(ti);
-	ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
-	ti.hwnd = m_hwnd;
-	ti.uId = UINT_PTR(m_grid.GetHwnd());
-	SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
-
-	hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
-	hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
-	hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
-	return true;
-}
-
-void QSMainDlg::OnDestroy()
-{
-	if (mnuhandle)
-		Menu_RemoveItem(mnuhandle);
-
-	UnhookEvent(hAdd);
-	UnhookEvent(hDelete);
-	UnhookEvent(hChange);
-
-	g_pDlg = nullptr;
-	
-	RECT rc;
-	GetWindowRect(m_hwnd, &rc);
-	CopyRect(&g_plugin.m_rect, &rc);
-
-	// save column width/order
-	SaveColumnOrder();
-
-	g_plugin.SaveOptWnd();
-
-	m_grid.SetImageList(0, LVSIL_SMALL);
-
-	if (g_plugin.m_flags & QSO_SAVEPATTERN)
-		g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
-
-	m_rows.destroy();
-	m_patterns.destroy();
-}
-
-int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
-{
-	switch (urc->wId) {
-	case IDC_REFRESH:
-		return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
-
-	case IDC_E_SEARCHTEXT:
-		return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
-
-	case IDC_LIST:
-		return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
-
-	case IDC_STATUSBAR:
-		return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
-	}
-	return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// event handlers
-
-INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
-{
-	auto *pRow = new CRowItem(hContact, this);
-	m_rows.insert(pRow);
-	AddContactToList(hContact, pRow);
-	ProcessLine(pRow);
-	Sort();
-	UpdateSB();
-	return 0;
-}
-
-INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
-{
-	int idx = -1;
-	CRowItem *pRow = 0;
-
-	for (auto &it : m_rows)
-		if (it->hContact == hContact) {
-			pRow = it;
-			idx = m_rows.indexOf(&it);
-			break;
-		}
-
-	if (idx == -1)
-		return 0;
-
-	int iItem = FindItem(pRow);
-	if (iItem != -1)
-		m_grid.DeleteItem(iItem);
-
-	m_rows.remove(idx);
-	UpdateSB();
-	return 0;
-}
-
-INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
-{
-	auto *pRow = FindRow(hContact);
-	if (pRow == nullptr)
-		return 0;
-
-	int oldStatus = pRow->status;
-	int newStatus = Contact::GetStatus(hContact);
-	pRow->status = newStatus;
-
-	if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
-		ChangeStatusPicture(pRow, hContact, lParam);
-	else if (oldStatus != ID_STATUS_OFFLINE) {
-		if (g_plugin.m_flags & QSO_SHOWOFFLINE)
-			ChangeStatusPicture(pRow, hContact, lParam);
-		else
-			ProcessLine(pRow, true);
-	}
-	else if (newStatus != ID_STATUS_OFFLINE) {
-		if (g_plugin.m_flags & QSO_SHOWOFFLINE)
-			ChangeStatusPicture(pRow, hContact, lParam);
-		else {
-			pRow->bActive = false;
-			m_grid.DeleteItem(FindItem(pRow));
-		}
-	}
-
-	if (g_plugin.m_flags & QSO_SORTBYSTATUS)
-		Sort();
-	
-	UpdateSB();
-	return 0;
-}
-
-INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
-{
-	if (wParam == IDM_STAYONTOP) {
-		int h; HWND w;
-		if (g_plugin.m_flags & QSO_STAYONTOP) {
-			h = MF_BYCOMMAND | MF_UNCHECKED;
-			w = HWND_NOTOPMOST;
-		}
-		else {
-			h = MF_BYCOMMAND | MF_CHECKED;
-			w = HWND_TOPMOST;
-		}
-		CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
-		SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
-		g_plugin.m_flags ^= QSO_STAYONTOP;
-	}
-	return 0;
-}
-
-INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
-{
-	if (g_bTipperInstalled) {
-		RECT rc;
-		GetWindowRect(m_grid.GetHwnd(), &rc);
-
-		POINT pt = { LOWORD(lParam), HIWORD(lParam) };
-		ClientToScreen(m_hwnd, &pt);
-		if (!PtInRect(&rc, pt)) {
-			if (TTShowed) {
-				TTShowed = false;
-				CallService(MS_TIPPER_HIDETIP, 0, 0);
-			}
-		}
-
-		m_hover.Stop();
-	}
-	return 0;
-}
-
-INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
-{
-	if (wParam == VK_F5)
-		PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
-	return 0;
-}
-
-void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
-{
-	int w = m_grid.GetSelectedCount();
-	if (w > 1)
-		ShowMultiPopup(w);
-	else
-		ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
-}
-
-void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
-{
-	LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
-	if (lParam == -1 || lParam == 0)
-		szFilterProto = nullptr;
-	else
-		szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
-
-	AdvancedFilter();
-}
-
-void QSMainDlg::onChange_Filter(CCtrlEdit *)
-{
-	if (!m_bInitialized)
-		return;
-
-	MakePattern(ptrW(edtFilter.GetText()));
-	FillGrid();
-}
-
-void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
-{
-	if (chkShowOffline.IsChecked()) {
-		g_plugin.m_flags |= QSO_SHOWOFFLINE;
-		bShowOffline = true;
-	}
-	else {
-		g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
-		bShowOffline = false;
-	}
-	
-	AdvancedFilter();
-}
-
-void QSMainDlg::onChange_Colorize(CCtrlCheck *)
-{
-	if (chkColorize.IsChecked())
-		g_plugin.m_flags |= QSO_COLORIZE;
-	else
-		g_plugin.m_flags &= ~QSO_COLORIZE;
-	RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
-}
-
-void QSMainDlg::onClick_Refresh(CCtrlButton *)
-{
-	m_rows.destroy();
-	PrepareToFill();
-	PrepareTable(true);
-	FillGrid();
-}
-
-void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
-{
-	HWND header = m_grid.GetHeader();
-
-	// clear sort mark
-	HDITEM hdi = {};
-	hdi.mask = HDI_FORMAT;
-	SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-	hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
-	SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
-	if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
-		g_plugin.m_flags |= QSO_SORTASC;
-		g_plugin.m_sortOrder = ev->nmlv->iSubItem;
-	}
-	else g_plugin.m_flags ^= QSO_SORTASC;;
-
-	// set new sort mark
-	SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-	if ((g_plugin.m_flags & QSO_SORTASC) == 0)
-		hdi.fmt |= HDF_SORTDOWN;
-	else
-		hdi.fmt &= ~HDF_SORTUP;
-	SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
-
-	Sort();
-}
-
-void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
-{
-	ShowContactMsgDlg(GetFocusedContact());
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
-{
-	LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
-	HICON h;
-	RECT rc;
-	auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
-
-	int result = CDRF_DODEFAULT;
-	switch (lplvcd->nmcd.dwDrawStage) {
-	case CDDS_PREPAINT:
-		result = CDRF_NOTIFYITEMDRAW;
-		break;
-
-	case CDDS_ITEMPREPAINT:
-		pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
-		result = CDRF_NOTIFYSUBITEMDRAW;
-		break;
-
-	case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
-		pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
-		{
-			int sub = ListViewToColumn(lplvcd->iSubItem);
-			auto *pCol = &g_plugin.m_columns[sub];
-			if (pCol == nullptr)
-				break;
-
-			if (pCol->isGender) {
-				m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
-
-				switch (pRow->pValues[sub].data) {
-				case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
-				case 'M': h = g_plugin.getIcon(IDI_MALE); break;
-				default: h = 0;
-				}
-
-				if (h)
-					DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
-				result = CDRF_SKIPDEFAULT;
-			}
-			else if (pCol->isXstatus) {
-				int j = _wtoi(pRow->pValues[sub].text);
-				if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
-					h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
-					m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
-					DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
-				}
-				result = CDRF_SKIPDEFAULT;
-			}
-			else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
-				result = CDRF_NOTIFYPOSTPAINT;
-		}
-		break;
-
-	case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
-		{
-			int sub = ListViewToColumn(lplvcd->iSubItem);
-			auto *pCol = &g_plugin.m_columns[sub];
-			if (pCol == nullptr)
-				break;
-
-			if (pCol->isClient) {
-				auto *MirVerW = pRow->pValues[sub].text;
-				if (MirVerW && *MirVerW && g_bFingerInstalled) {
-					h = Finger_GetClientIcon(MirVerW, FALSE);
-					m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
-					DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
-					DestroyIcon(h);
-				}
-			}
-			result = CDRF_SKIPDEFAULT;
-		}
-		break;
-	}
-
-	SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
-}
-
-void QSMainDlg::onTimer_Hover(CTimer *pTimer)
-{
-	pTimer->Stop();
-
-	if (GetForegroundWindow() != m_hwnd)
-		return;
-
-	auto *pRow = GetRow(OldHItem);
-	if (pRow == 0)
-		return;
-
-	POINT pt;
-	GetCursorPos(&pt);
-
-	RECT rcItem;
-	m_grid.GetItemRect(OldHItem, &rcItem, 0);
-	ScreenToClient(m_grid.GetHwnd(), &pt);
-	if (!PtInRect(&rcItem, pt))
-		return;
-
-	CLCINFOTIP info = {};
-	info.cbSize = sizeof(info);
-	info.hItem = HANDLE(pRow->hContact);
-	Tipper_ShowTip(0, &info);
-	TTShowed = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-int CloseSrWindow(bool)
-{
-	if (!g_pDlg)
-		return false;
-
-	g_pDlg->Close();
-	return true;
-}
-
-int OpenSrWindow(const wchar_t *pwszPattern)
-{
-	if (g_pDlg) {
-		WINDOWPLACEMENT wp;
-		wp.length = sizeof(wp);
-		GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
-		if (wp.showCmd == SW_SHOWMINIMIZED)
-			g_pDlg->Show(SW_RESTORE);
-		SetForegroundWindow(g_pDlg->GetHwnd());
-		return true;
-	}
-
-	int count = 0;
-	for (auto &it : g_plugin.m_columns)
-		if (it->bEnabled)
-			count++;
-
-	// no even one visible column
-	if (count == 0)
-		return true;
-
-	g_plugin.LoadOptWnd();
-
-	auto *pDlg = new QSMainDlg(pwszPattern);
-	if (pDlg->PrepareToFill())
-		pDlg->Create();
-	else
-		delete pDlg;
-
-	return true;
-}
+/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+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 | 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"
+
+#define IDM_STAYONTOP      (WM_USER+1)
+
+static QSMainDlg *g_pDlg = 0;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// edit control window procedure
+
+static LRESULT CALLBACK sttNewEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+	if (pDlg)
+		if (INT_PTR res = pDlg->NewEditProc(msg, wParam, lParam))
+			return res;
+
+	return mir_callNextSubclass(hwnd, sttNewEditProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewEditProc(UINT msg, WPARAM wParam, LPARAM)
+{
+	switch (msg) {
+	case WM_CHAR:
+		if (wParam == 27) // Escape
+			Close();
+		break;
+
+	case WM_KEYUP:
+		if (wParam == VK_RETURN) {
+			if (m_grid.GetSelectedCount() == 1)
+				ShowContactMsgDlg(GetFocusedContact());
+			return 0;
+		}
+		break;
+
+	case WM_KEYDOWN:
+		int count = m_grid.GetItemCount();
+		int current = m_grid.GetNextItem(-1, LVNI_FOCUSED);
+		int next = -1;
+		if (count > 0) {
+			switch (wParam) {
+			case VK_UP:
+				if (current > 0)
+					next = current - 1;
+				break;
+
+			case VK_DOWN:
+				if (current < count - 1)
+					next = current + 1;
+				break;
+
+			case VK_F5:
+				onClick_Refresh(0);
+				return 0;
+
+			case VK_NEXT:
+			case VK_PRIOR:
+				int perpage = m_grid.GetCountPerPage();
+				if (wParam == VK_NEXT)
+					next = min(current + perpage, count);
+				else
+					next = max(current - perpage, 0);
+				break;
+			}
+		}
+
+		if (next >= 0) {
+			m_grid.SetItemState(-1, 0, LVIS_SELECTED);
+			m_grid.SetCurSel(next);
+			m_grid.EnsureVisible(next, FALSE);
+		}
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// list header window procedure
+
+static void MakeColumnMenu()
+{
+	HMENU hMenu = CreatePopupMenu();
+
+	for (auto &it : g_plugin.m_columns) {
+		int flag = MF_STRING + (it->bEnabled) ? MF_CHECKED : MF_UNCHECKED;
+		AppendMenuW(hMenu, flag, 100 + g_plugin.m_columns.indexOf(&it), TranslateW(it->title));
+	}
+
+	POINT pt;
+	GetCursorPos(&pt);
+
+	int id = TrackPopupMenu(hMenu, TPM_RETURNCMD + TPM_NONOTIFY, pt.x, pt.y, 0, g_pDlg->GetHwnd(), 0);
+	if (id >= 100)
+		g_pDlg->ToggleColumn(id - 100);
+
+	DestroyMenu(hMenu);
+}
+
+void QSMainDlg::ToggleColumn(int col)
+{
+	auto &pCol = g_plugin.m_columns[col];
+
+	if (!pCol.bEnabled) { // show column
+		pCol.bEnabled = true;
+
+		if (!pCol.bInit) {
+			for (auto &it : m_rows)
+				it->pValues[col].LoadOneItem(it->hContact, pCol, this);
+			pCol.bInit = true;
+		}
+
+		// screen
+		int lvcol = ColumnToListView(col);
+		AddColumn(lvcol, &pCol);
+		
+		int nCount = m_grid.GetItemCount();
+		for (int i = 0; i < nCount; i++) {
+			auto *pRow = GetRow(i);
+
+			LV_ITEMW li;
+			li.iItem = i;
+			li.iSubItem = lvcol;
+			li.mask = LVIF_TEXT;
+			li.pszText = pRow->pValues[col].text;
+			if ((pCol.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText) || pCol.isGender || pCol.isXstatus)
+				li.mask |= LVIF_IMAGE;
+			m_grid.SetItem(&li);
+		}
+	}
+	else { // hide column
+		int cnt = 0;
+		for (auto &it : g_plugin.m_columns)
+			if (it->bEnabled)
+				cnt++;
+
+		// keep at least one visible column (1 + this)
+		if (cnt > 2) {
+			m_grid.DeleteColumn(col);
+			pCol.bEnabled = false;
+		}
+	}
+}
+
+static LRESULT CALLBACK sttNewLVHProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	switch (msg) {
+	case WM_RBUTTONUP:
+		return 0;
+
+	case WM_RBUTTONDOWN:
+		MakeColumnMenu();
+		break;
+	}
+
+	return mir_callNextSubclass(hwnd, sttNewLVHProc, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid list window procedure
+
+static int OldHSubItem = 0, OldHItem = 0;
+
+static LRESULT CALLBACK sttNewLVProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	auto *pDlg = (QSMainDlg *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
+	if (pDlg)
+		if (INT_PTR res = pDlg->NewLVProc(msg, wParam, lParam))
+			return res;
+
+	return mir_callNextSubclass(hwnd, sttNewLVProc, msg, wParam, lParam);
+}
+
+INT_PTR QSMainDlg::NewLVProc(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+	LV_HITTESTINFO pinfo;
+
+	switch (msg) {
+	case WM_CHAR:
+		switch (wParam) { // ESC
+		case 27:
+			Close();
+			break;
+
+		case 1: // Cltr+A
+			m_grid.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
+			break;
+
+		case 3: // Ctrl-C
+			CopyMultiLines();
+			break;
+
+		case 8: // backspace
+			edtFilter.SendMsg(msg, wParam, lParam);
+			break;
+		}
+
+		if (wParam >= 32 && wParam <= 127) // letters
+			edtFilter.SendMsg(msg, wParam, lParam);
+		break;
+
+	case WM_MOUSEMOVE:
+		pinfo.pt.x = LOWORD(lParam);
+		pinfo.pt.y = HIWORD(lParam);
+		pinfo.flags = 0;
+		if (m_grid.SubItemHitTest(&pinfo) == -1)
+			break;
+
+		if ((pinfo.flags & LVHT_ONITEM) && (pinfo.iItem != OldHItem || pinfo.iSubItem != OldHSubItem)) {
+			OldHSubItem = pinfo.iSubItem;
+			OldHItem = pinfo.iItem;
+
+			if (g_bTipperInstalled) {
+				if (TTShowed) {
+					TTShowed = false;
+					Tipper_Hide();
+				}
+				m_hover.Stop();
+
+				if (OldHSubItem == 0)
+					m_hover.Start(450);
+			}
+
+			TOOLINFOW ti = {};
+			ti.cbSize = sizeof(ti);
+			ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+			ti.hwnd = m_hwnd;
+			ti.uId = LPARAM(m_hwnd);
+
+			int num = ListViewToColumn(OldHSubItem);
+			auto &pCol = g_plugin.m_columns[num];
+			if (pCol.isXstatus || pCol.isGender) {
+				auto *pRow = GetRow(OldHItem);
+				if (pCol.isGender) {
+					switch (pRow->pValues[num].data) {
+					case 'M': ti.lpszText = TranslateT("Male"); break;
+					case 'F': ti.lpszText = TranslateT("Female"); break;
+					default: ti.lpszText = TranslateT("Unknown"); break;
+					}
+				}
+				else {
+					wchar_t buf[256];
+					mir_wstrncpy(buf, pRow->pValues[num].text, _countof(buf));
+					int iStatus = _wtoi(buf);
+
+					CUSTOM_STATUS ics = {};
+					ics.cbSize = sizeof(ics);
+					ics.status = &iStatus;
+					ics.flags = CSSF_DEFAULT_NAME | CSSF_MASK_NAME | CSSF_UNICODE;
+					ics.pwszName = buf;
+					CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&ics);
+					ti.lpszText = TranslateW(buf);
+				}
+			}
+
+			SendMessageW(HintWnd, TTM_SETTOOLINFOW, 0, LPARAM(&ti));
+		}
+		break;
+
+	case WM_KEYUP:
+		switch (wParam) {
+		case VK_RETURN:
+			if (m_grid.GetSelectedCount() == 1)
+				ShowContactMsgDlg(GetFocusedContact());
+			break;
+
+		case VK_INSERT:
+			CallService(MS_FINDADD_FINDADD, 0, 0);
+			break;
+
+		case VK_DELETE:
+			lParam = m_grid.GetSelectedCount();
+			if (lParam > 1)
+				DeleteByList();
+			else if (lParam == 1)
+				DeleteOneContact(GetFocusedContact());
+			break;
+
+		case VK_F5:
+			onClick_Refresh(0);
+			break;
+		}
+		break;
+
+	case WM_NOTIFY:
+		if (((LPNMHDR)lParam)->code == HDN_ITEMSTATEICONCLICK) {
+			NMHEADER *pdhr = (NMHEADER *)lParam;
+			if ((pdhr->pitem->mask & HDI_FORMAT) && (pdhr->pitem->fmt & HDF_CHECKBOX)) {
+				int i = ListViewToColumn(pdhr->iItem);
+				auto &pCol = g_plugin.m_columns[i];
+
+				if (pdhr->pitem->fmt & HDF_CHECKED) {
+					pCol.bFilter = false;
+					pdhr->pitem->fmt &= ~HDF_CHECKED;
+				}
+				else {
+					pCol.bFilter = true;
+					pdhr->pitem->fmt |= HDF_CHECKED;
+				}
+
+				SendMessage(pdhr->hdr.hwndFrom, HDM_SETITEM, pdhr->iItem, LPARAM(pdhr->pitem));
+				FillGrid();
+				return TRUE;
+			}
+		}
+	}
+	return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// QSMainDlg class implementation
+
+static int CompareSb(const CStatusBarItem *p1, const CStatusBarItem *p2)
+{
+	if (p1->bAccDel != p2->bAccDel)
+		return (p1->bAccDel) ? 1 : -1;
+
+	if (p1->bAccOff != p2->bAccOff)
+		return (p1->bAccOff) ? 1 : -1;
+
+	return mir_strcmp(p1->szProto, p2->szProto);
+}
+
+QSMainDlg::QSMainDlg(const wchar_t *pwszPattern) :
+	CDlgBase(g_plugin, IDD_MAIN),
+	m_rows(50),
+	m_patterns(1),
+	m_sbdata(10, CompareSb),
+	m_grid(this, IDC_LIST),
+	m_hover(this, 10),
+	cmbProto(this, IDC_CB_PROTOCOLS),
+	edtFilter(this, IDC_E_SEARCHTEXT),
+	btnRefresh(this, IDC_REFRESH),
+	chkColorize(this, IDC_CH_COLORIZE),
+	chkShowOffline(this, IDC_CH_SHOWOFFLINE)
+{
+	SetMinSize(300, 160);
+
+	if (pwszPattern)
+		m_wszPatternBuf = mir_wstrdup(pwszPattern);
+	else if (g_plugin.m_flags & QSO_SAVEPATTERN)
+		m_wszPatternBuf = g_plugin.getWStringA("pattern");
+
+	m_hover.OnEvent = Callback(this, &QSMainDlg::onTimer_Hover);
+
+	m_grid.OnBuildMenu = Callback(this, &QSMainDlg::onBuildMenu_Grid);
+	m_grid.OnColumnClick = Callback(this, &QSMainDlg::onColumnClick_Grid);
+	m_grid.OnCustomDraw = Callback(this, &QSMainDlg::onCustomDraw_Grid);
+	m_grid.OnDoubleClick = Callback(this, &QSMainDlg::onDblClick_Grid);
+
+	btnRefresh.OnClick = Callback(this, &QSMainDlg::onClick_Refresh);
+
+	cmbProto.OnSelChanged = Callback(this, &QSMainDlg::onSelChange_Proto);
+
+	edtFilter.OnChange = Callback(this, &QSMainDlg::onChange_Filter);
+	chkColorize.OnChange = Callback(this, &QSMainDlg::onChange_Colorize);
+	chkShowOffline.OnChange = Callback(this, &QSMainDlg::onChange_ShowOffline);
+}
+
+bool QSMainDlg::OnInitDialog()
+{
+	g_pDlg = this;
+	mnuhandle = 0;
+
+	SetCaption(TranslateT("Quick Search"));
+
+	hwndStatusBar = GetDlgItem(m_hwnd, IDC_STATUSBAR);
+
+	HMENU smenu = GetSystemMenu(m_hwnd, false);
+	InsertMenu(smenu, 5, MF_BYPOSITION | MF_SEPARATOR, 0, nullptr);
+	InsertMenuW(smenu, 6, MF_BYPOSITION | MF_STRING, IDM_STAYONTOP, TranslateT("Stay on Top"));
+
+	if (g_plugin.m_flags & QSO_STAYONTOP) {
+		CheckMenuItem(smenu, IDM_STAYONTOP, MF_BYCOMMAND | MF_CHECKED);
+		SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+	}
+
+	chkShowOffline.SetState((g_plugin.m_flags & QSO_SHOWOFFLINE) != 0);
+
+	szFilterProto = nullptr; // display all protocols
+	if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+		bShowOffline = true;
+
+	chkColorize.SetState((g_plugin.m_flags & QSO_COLORIZE) != 0);
+
+	// Window
+	INT_PTR tmp = GetWindowLongPtrW(m_hwnd, GWL_EXSTYLE);
+	if (g_plugin.m_flags & QSO_TOOLSTYLE)
+		tmp |= WS_EX_TOOLWINDOW;
+	else
+		tmp &= ~WS_EX_TOOLWINDOW;
+	SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, tmp);
+
+	SendMessage(m_hwnd, WM_SETICON, ICON_SMALL, (LPARAM)g_plugin.getIcon(IDI_QS));
+
+	// ListView
+	m_grid.SetImageList(Clist_GetImageList(), LVSIL_SMALL);
+
+	tmp = LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES | LVS_EX_HEADERDRAGDROP |
+		LVS_EX_LABELTIP | LVS_EX_DOUBLEBUFFER;
+	if (g_plugin.m_flags & QSO_DRAWGRID)
+		tmp |= LVS_EX_GRIDLINES;
+	m_grid.SetExtendedListViewStyle(tmp);
+
+	// ListView header
+	HWND header = m_grid.GetHeader();
+	SetWindowLongPtrW(header, GWL_STYLE, GetWindowLongPtrW(header, GWL_STYLE) | HDS_CHECKBOXES);
+
+	mir_subclassWindow(edtFilter.GetHwnd(), &sttNewEditProc);
+
+	SetWindowLongPtrW(m_grid.GetHeader(), GWLP_USERDATA, LPARAM(this));
+	mir_subclassWindow(m_grid.GetHeader(), &sttNewLVHProc);
+	
+	SetWindowLongPtrW(m_grid.GetHwnd(), GWLP_USERDATA, LPARAM(this));
+	mir_subclassWindow(m_grid.GetHwnd(), &sttNewLVProc);
+
+	FillProtoCombo();
+
+	PrepareTable();
+
+	if (m_wszPatternBuf) {
+		edtFilter.SetText(m_wszPatternBuf);
+		MakePattern(m_wszPatternBuf);
+	}
+	FillGrid();
+
+	// Show sorting column
+	HDITEM hdi = {};
+	hdi.mask = HDI_FORMAT;
+	SendMessageW(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+	if (g_plugin.m_flags & QSO_SORTASC)
+		hdi.fmt |= HDF_SORTUP;
+	else
+		hdi.fmt |= HDF_SORTDOWN;
+	SendMessageW(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+	RECT rc = g_plugin.m_rect;
+	::SnapToScreen(rc);
+	::MoveWindow(m_hwnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, false);
+
+	HintWnd = CreateWindowExW(0, TOOLTIPS_CLASS, nullptr, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, m_hwnd, 0, g_plugin.getInst(), 0);
+
+	TOOLINFOW ti;
+	ti.cbSize = sizeof(ti);
+	ti.uFlags = TTF_SUBCLASS + TTF_IDISHWND;
+	ti.hwnd = m_hwnd;
+	ti.uId = UINT_PTR(m_grid.GetHwnd());
+	SendMessageW(HintWnd, TTM_ADDTOOLW, 0, LPARAM(&ti));
+
+	hAdd = HookEventMessage(ME_DB_CONTACT_ADDED, m_hwnd, WM_CONTACT_ADDED);
+	hDelete = HookEventMessage(ME_DB_CONTACT_DELETED, m_hwnd, WM_CONTACT_DELETED);
+	hChange = HookEventMessage(ME_CLIST_CONTACTICONCHANGED, m_hwnd, WM_STATUS_CHANGED);
+	return true;
+}
+
+void QSMainDlg::OnDestroy()
+{
+	if (mnuhandle)
+		Menu_RemoveItem(mnuhandle);
+
+	UnhookEvent(hAdd);
+	UnhookEvent(hDelete);
+	UnhookEvent(hChange);
+
+	g_pDlg = nullptr;
+	
+	RECT rc;
+	GetWindowRect(m_hwnd, &rc);
+	CopyRect(&g_plugin.m_rect, &rc);
+
+	// save column width/order
+	SaveColumnOrder();
+
+	g_plugin.SaveOptWnd();
+
+	m_grid.SetImageList(0, LVSIL_SMALL);
+
+	if (g_plugin.m_flags & QSO_SAVEPATTERN)
+		g_plugin.setWString("pattern", ptrW(edtFilter.GetText()));
+
+	m_rows.destroy();
+	m_patterns.destroy();
+}
+
+int QSMainDlg::Resizer(UTILRESIZECONTROL *urc)
+{
+	switch (urc->wId) {
+	case IDC_REFRESH:
+		return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+
+	case IDC_E_SEARCHTEXT:
+		return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP;
+
+	case IDC_LIST:
+		return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+	case IDC_STATUSBAR:
+		return RD_ANCHORX_WIDTH | RD_ANCHORY_BOTTOM;
+	}
+	return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// event handlers
+
+INT_PTR QSMainDlg::OnContactAdded(UINT, WPARAM hContact, LPARAM)
+{
+	auto *pRow = new CRowItem(hContact, this);
+	m_rows.insert(pRow);
+	AddContactToList(hContact, pRow);
+	ProcessLine(pRow);
+	Sort();
+	UpdateSB();
+	return 0;
+}
+
+INT_PTR QSMainDlg::OnContactDeleted(UINT, WPARAM hContact, LPARAM)
+{
+	int idx = -1;
+	CRowItem *pRow = 0;
+
+	for (auto &it : m_rows)
+		if (it->hContact == hContact) {
+			pRow = it;
+			idx = m_rows.indexOf(&it);
+			break;
+		}
+
+	if (idx == -1)
+		return 0;
+
+	int iItem = FindItem(pRow);
+	if (iItem != -1)
+		m_grid.DeleteItem(iItem);
+
+	m_rows.remove(idx);
+	UpdateSB();
+	return 0;
+}
+
+INT_PTR QSMainDlg::OnStatusChanged(UINT, WPARAM hContact, LPARAM lParam)
+{
+	auto *pRow = FindRow(hContact);
+	if (pRow == nullptr)
+		return 0;
+
+	int oldStatus = pRow->status;
+	int newStatus = Contact::GetStatus(hContact);
+	pRow->status = newStatus;
+
+	if (oldStatus != ID_STATUS_OFFLINE && newStatus != ID_STATUS_OFFLINE)
+		ChangeStatusPicture(pRow, hContact, lParam);
+	else if (oldStatus != ID_STATUS_OFFLINE) {
+		if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+			ChangeStatusPicture(pRow, hContact, lParam);
+		else
+			ProcessLine(pRow, true);
+	}
+	else if (newStatus != ID_STATUS_OFFLINE) {
+		if (g_plugin.m_flags & QSO_SHOWOFFLINE)
+			ChangeStatusPicture(pRow, hContact, lParam);
+		else {
+			pRow->bActive = false;
+			m_grid.DeleteItem(FindItem(pRow));
+		}
+	}
+
+	if (g_plugin.m_flags & QSO_SORTBYSTATUS)
+		Sort();
+	
+	UpdateSB();
+	return 0;
+}
+
+INT_PTR QSMainDlg::OnSysCommand(UINT, WPARAM wParam, LPARAM)
+{
+	if (wParam == IDM_STAYONTOP) {
+		int h; HWND w;
+		if (g_plugin.m_flags & QSO_STAYONTOP) {
+			h = MF_BYCOMMAND | MF_UNCHECKED;
+			w = HWND_NOTOPMOST;
+		}
+		else {
+			h = MF_BYCOMMAND | MF_CHECKED;
+			w = HWND_TOPMOST;
+		}
+		CheckMenuItem(GetSystemMenu(m_hwnd, false), IDM_STAYONTOP, h);
+		SetWindowPos(m_hwnd, w, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
+		g_plugin.m_flags ^= QSO_STAYONTOP;
+	}
+	return 0;
+}
+
+INT_PTR QSMainDlg::OnMouseMove(UINT, WPARAM, LPARAM lParam)
+{
+	if (g_bTipperInstalled) {
+		RECT rc;
+		GetWindowRect(m_grid.GetHwnd(), &rc);
+
+		POINT pt = { LOWORD(lParam), HIWORD(lParam) };
+		ClientToScreen(m_hwnd, &pt);
+		if (!PtInRect(&rc, pt)) {
+			if (TTShowed) {
+				TTShowed = false;
+				CallService(MS_TIPPER_HIDETIP, 0, 0);
+			}
+		}
+
+		m_hover.Stop();
+	}
+	return 0;
+}
+
+INT_PTR QSMainDlg::OnKeydown(UINT, WPARAM wParam, LPARAM)
+{
+	if (wParam == VK_F5)
+		PostMessage(m_hwnd, WM_COMMAND, IDC_REFRESH, 0);
+	return 0;
+}
+
+void QSMainDlg::onBuildMenu_Grid(CContextMenuPos *pos)
+{
+	int w = m_grid.GetSelectedCount();
+	if (w > 1)
+		ShowMultiPopup(w);
+	else
+		ShowContactMenu(GetFocusedContact(), GetLVSubItem(pos->pt.x, pos->pt.y));
+}
+
+void QSMainDlg::onSelChange_Proto(CCtrlCombo *)
+{
+	LPARAM lParam = cmbProto.GetItemData(cmbProto.GetCurSel());
+	if (lParam == -1 || lParam == 0)
+		szFilterProto = nullptr;
+	else
+		szFilterProto = ((PROTOACCOUNT *)lParam)->szModuleName;
+
+	AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Filter(CCtrlEdit *)
+{
+	if (!m_bInitialized)
+		return;
+
+	MakePattern(ptrW(edtFilter.GetText()));
+	FillGrid();
+}
+
+void QSMainDlg::onChange_ShowOffline(CCtrlCheck *)
+{
+	if (chkShowOffline.IsChecked()) {
+		g_plugin.m_flags |= QSO_SHOWOFFLINE;
+		bShowOffline = true;
+	}
+	else {
+		g_plugin.m_flags &= ~QSO_SHOWOFFLINE;
+		bShowOffline = false;
+	}
+	
+	AdvancedFilter();
+}
+
+void QSMainDlg::onChange_Colorize(CCtrlCheck *)
+{
+	if (chkColorize.IsChecked())
+		g_plugin.m_flags |= QSO_COLORIZE;
+	else
+		g_plugin.m_flags &= ~QSO_COLORIZE;
+	RedrawWindow(m_grid.GetHwnd(), nullptr, 0, RDW_INVALIDATE);
+}
+
+void QSMainDlg::onClick_Refresh(CCtrlButton *)
+{
+	m_rows.destroy();
+	PrepareToFill();
+	PrepareTable(true);
+	FillGrid();
+}
+
+void QSMainDlg::onColumnClick_Grid(CCtrlListView::TEventInfo *ev)
+{
+	HWND header = m_grid.GetHeader();
+
+	// clear sort mark
+	HDITEM hdi = {};
+	hdi.mask = HDI_FORMAT;
+	SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+	hdi.fmt &= ~(HDF_SORTDOWN | HDF_SORTUP);
+	SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+	if (g_plugin.m_sortOrder != ev->nmlv->iSubItem) {
+		g_plugin.m_flags |= QSO_SORTASC;
+		g_plugin.m_sortOrder = ev->nmlv->iSubItem;
+	}
+	else g_plugin.m_flags ^= QSO_SORTASC;;
+
+	// set new sort mark
+	SendMessage(header, HDM_GETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+	if ((g_plugin.m_flags & QSO_SORTASC) == 0)
+		hdi.fmt |= HDF_SORTDOWN;
+	else
+		hdi.fmt &= ~HDF_SORTUP;
+	SendMessage(header, HDM_SETITEM, g_plugin.m_sortOrder, LPARAM(&hdi));
+
+	Sort();
+}
+
+void QSMainDlg::onDblClick_Grid(CCtrlListView::TEventInfo*)
+{
+	ShowContactMsgDlg(GetFocusedContact());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::onCustomDraw_Grid(CCtrlListView::TEventInfo *ev)
+{
+	LPNMLVCUSTOMDRAW lplvcd = ev->nmcd;
+	HICON h;
+	RECT rc;
+	auto *pRow = (CRowItem *)lplvcd->nmcd.lItemlParam;
+
+	int result = CDRF_DODEFAULT;
+	switch (lplvcd->nmcd.dwDrawStage) {
+	case CDDS_PREPAINT:
+		result = CDRF_NOTIFYITEMDRAW;
+		break;
+
+	case CDDS_ITEMPREPAINT:
+		pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+		result = CDRF_NOTIFYSUBITEMDRAW;
+		break;
+
+	case CDDS_SUBITEM + CDDS_ITEMPREPAINT:
+		pRow->GetCellColor(lplvcd->nmcd.dwItemSpec, lplvcd->clrTextBk, lplvcd->clrText);
+		{
+			int sub = ListViewToColumn(lplvcd->iSubItem);
+			auto *pCol = &g_plugin.m_columns[sub];
+			if (pCol == nullptr)
+				break;
+
+			if (pCol->isGender) {
+				m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+
+				switch (pRow->pValues[sub].data) {
+				case 'F': h = g_plugin.getIcon(IDI_FEMALE); break;
+				case 'M': h = g_plugin.getIcon(IDI_MALE); break;
+				default: h = 0;
+				}
+
+				if (h)
+					DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+				result = CDRF_SKIPDEFAULT;
+			}
+			else if (pCol->isXstatus) {
+				int j = _wtoi(pRow->pValues[sub].text);
+				if (j > 0 && ProtoServiceExists(pRow->szProto, PS_GETCUSTOMSTATUSICON)) {
+					h = (HICON)CallProtoService(pRow->szProto, PS_GETCUSTOMSTATUSICON, j, LR_SHARED);
+					m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+					DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+				}
+				result = CDRF_SKIPDEFAULT;
+			}
+			else if ((g_plugin.m_flags & QSO_CLIENTICONS) && pCol->isClient)
+				result = CDRF_NOTIFYPOSTPAINT;
+		}
+		break;
+
+	case CDDS_SUBITEM + CDDS_ITEMPOSTPAINT:
+		{
+			int sub = ListViewToColumn(lplvcd->iSubItem);
+			auto *pCol = &g_plugin.m_columns[sub];
+			if (pCol == nullptr)
+				break;
+
+			if (pCol->isClient) {
+				auto *MirVerW = pRow->pValues[sub].text;
+				if (MirVerW && *MirVerW && g_bFingerInstalled) {
+					h = Finger_GetClientIcon(MirVerW, FALSE);
+					m_grid.GetSubItemRect(lplvcd->nmcd.dwItemSpec, lplvcd->iSubItem, LVIR_ICON, &rc);
+					DrawIconEx(lplvcd->nmcd.hdc, rc.left + 1, rc.top, h, 16, 16, 0, 0, DI_NORMAL);
+					DestroyIcon(h);
+				}
+			}
+			result = CDRF_SKIPDEFAULT;
+		}
+		break;
+	}
+
+	SetWindowLongPtrW(m_hwnd, DWLP_MSGRESULT, result);
+}
+
+void QSMainDlg::onTimer_Hover(CTimer *pTimer)
+{
+	pTimer->Stop();
+
+	if (GetForegroundWindow() != m_hwnd)
+		return;
+
+	auto *pRow = GetRow(OldHItem);
+	if (pRow == 0)
+		return;
+
+	POINT pt;
+	GetCursorPos(&pt);
+
+	RECT rcItem;
+	m_grid.GetItemRect(OldHItem, &rcItem, 0);
+	ScreenToClient(m_grid.GetHwnd(), &pt);
+	if (!PtInRect(&rcItem, pt))
+		return;
+
+	CLCINFOTIP info = {};
+	info.cbSize = sizeof(info);
+	info.hItem = HANDLE(pRow->hContact);
+	Tipper_ShowTip(0, &info);
+	TTShowed = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int CloseSrWindow(bool)
+{
+	if (!g_pDlg)
+		return false;
+
+	g_pDlg->Close();
+	return true;
+}
+
+int OpenSrWindow(const wchar_t *pwszPattern)
+{
+	if (g_pDlg) {
+		WINDOWPLACEMENT wp;
+		wp.length = sizeof(wp);
+		GetWindowPlacement(g_pDlg->GetHwnd(), &wp);
+		if (wp.showCmd == SW_SHOWMINIMIZED)
+			g_pDlg->Show(SW_RESTORE);
+		SetForegroundWindow(g_pDlg->GetHwnd());
+		return true;
+	}
+
+	int count = 0;
+	for (auto &it : g_plugin.m_columns)
+		if (it->bEnabled)
+			count++;
+
+	// no even one visible column
+	if (count == 0)
+		return true;
+
+	g_plugin.LoadOptWnd();
+
+	auto *pDlg = new QSMainDlg(pwszPattern);
+	if (pDlg->PrepareToFill())
+		pDlg->Create();
+	else
+		delete pDlg;
+
+	return true;
+}
diff --git a/plugins/QuickSearch/src/window_misc.cpp b/plugins/QuickSearch/src/window_misc.cpp
index e6b5e23341..58d96561fc 100644
--- a/plugins/QuickSearch/src/window_misc.cpp
+++ b/plugins/QuickSearch/src/window_misc.cpp
@@ -1,875 +1,875 @@
-/*
-Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)
-
-This program is free software; you can redistribute it &/|
-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 | 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"
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// patterns
-
-bool QSMainDlg::CheckPattern(CRowItem *pRow)
-{
-	if (m_patterns.getCount() == 0)
-		return true;
-	
-	for (auto &p : m_patterns)
-		p->res = 0;
-
-	int i = 0;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->bEnabled && it->bFilter && pRow->pValues[i].text != nullptr) {
-			CMStringW buf(pRow->pValues[i].text);
-			buf.MakeLower();
-
-			for (auto &p : m_patterns)
-				if (!p->res && buf.Find(p->str) != -1)
-					p->res = true;
-		}
-		i++;
-	}
-
-	bool result = true;
-	for (auto &p : m_patterns)
-		result = result && p->res;
-	return result;
-}
-
-void QSMainDlg::MakePattern(const wchar_t *pwszPattern)
-{
-	m_patterns.destroy();
-	if (mir_wstrlen(pwszPattern) == 0)
-		return;
-
-	// m_wszPatternBuf works as a storage for patterns, we store pointers to it in m_patterns
-	m_wszPatternBuf = mir_wstrdup(pwszPattern);
-	CharLowerW(m_wszPatternBuf);
-	
-	for (wchar_t *p = m_wszPatternBuf; *p; ) {
-		auto *pWord = wcspbrk(p, L" \"");
-		if (pWord == nullptr) {
-			m_patterns.insert(new Pattern(p));
-			return;
-		}
-
-		bool isSpace = pWord[0] == ' ';
-
-		// there's some valuable info between p and pWord
-		if (pWord != p) {
-			*pWord = 0;
-			m_patterns.insert(new Pattern(p));
-		}
-
-		if (isSpace) {
-			p = ltrimpw(pWord + 1); // skip all spaces
-		}
-		else {
-			auto *pEnd = wcschr(++pWord, '\"');
-			
-			// treat the rest of line as one pattern
-			if (pEnd == nullptr) {
-				m_patterns.insert(new Pattern(pWord));
-				return;
-			}
-
-			*pEnd = 0;
-			m_patterns.insert(new Pattern(pWord));
-			p = ltrimpw(pEnd + 1);
-		}
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void QSMainDlg::AddColumn(int idx, ColumnItem *pCol)
-{
-	LV_COLUMN lvcol = {};
-	lvcol.mask = LVCF_TEXT | LVCF_WIDTH;
-	lvcol.pszText = TranslateW(pCol->title);
-	lvcol.cx = pCol->width;
-	m_grid.InsertColumn(idx, &lvcol);
-
-	HDITEM hdi;
-	hdi.mask = HDI_FORMAT;
-	if (pCol->bFilter)
-		hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED;
-	else
-		hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX;
-	SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi));
-}
-
-void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow)
-{
-	LV_ITEMW li = {};
-	li.mask = LVIF_IMAGE | LVIF_PARAM;
-	li.iItem = 100000;
-	li.iImage = Clist_GetContactIcon(hContact);
-	li.lParam = LPARAM(pRow);
-
-	li.iItem = m_grid.InsertItem(&li);
-	li.iImage = 0;
-	li.iSubItem = 0;
-
-	for (int i = 0; i < g_plugin.m_columns.getCount(); i++) {
-		auto &col = g_plugin.m_columns[i];
-		if (!col.bEnabled)
-			continue;
-
-		// Client icons preprocess
-		li.pszText = pRow->pValues[i].text;
-		li.mask = LVIF_TEXT;
-		if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender)
-			li.mask |= LVIF_IMAGE;
-		m_grid.SetItem(&li);
-		li.iSubItem++;
-	}
-}
-
-void QSMainDlg::AdvancedFilter()
-{
-	m_grid.SetDraw(false);
-
-	for (auto &it : m_rows) {
-		bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto);
-		if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE)
-			bShow = false;
-
-		if (it->bPattern) {
-			if (bShow) {
-				if (!it->bActive)
-					ProcessLine(it, false);
-			}
-			else {
-				it->bActive = false;
-				m_grid.DeleteItem(FindItem(it));
-			}
-		}
-	}
-
-	m_grid.SetDraw(true);
-	InvalidateRect(m_grid.GetHwnd(), 0, false);
-
-	Sort();
-	UpdateSB();
-}
-
-void QSMainDlg::ChangeStatusPicture(CRowItem *pRow, MCONTACT, LPARAM lParam)
-{
-	int idx = FindItem(pRow);
-	if (idx == -1)
-		return;
-
-	LV_ITEMW li = {};
-	li.iItem = idx;
-	li.mask = LVIF_IMAGE;
-	li.iImage = lParam; //CallService(MS_CLIST_GETCONTACTICON,hContact,0);
-	m_grid.SetItem(&li);
-}
-
-void QSMainDlg::CopyMultiLines()
-{
-	CMStringW buf;
-
-	int i = 0;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->bEnabled) {
-			it->width = m_grid.GetColumnWidth(i);
-			if (it->width >= 10)
-				buf.AppendFormat(L"%s\t", it->title);
-		}
-		i++;
-	}
-	buf.Append(L"\r\n");
-
-	int nRows = m_grid.GetItemCount();
-	int nSelected = m_grid.GetSelectedCount();
-
-	for (int j = 0; j < nRows; j++) {
-		if (nSelected > 1 && !m_grid.GetItemState(j, LVIS_SELECTED))
-			continue;
-
-		auto *pRow = GetRow(j);
-		
-		i = 0;
-		for (auto &it : g_plugin.m_columns) {
-			if (it->bEnabled && it->width >= 10)
-				buf.AppendFormat(L"%s\t", pRow->pValues[i].getText());
-			i++;
-		}
-		buf.Append(L"\r\n");
-	}
-
-	Utils_ClipboardCopy(buf);
-}
-
-void QSMainDlg::DeleteByList()
-{
-	if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts?"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING))
-		return;
-
-	m_grid.SetDraw(false);
-
-	for (int i = m_grid.GetItemCount() - 1; i >= 0; i--)
-		if (m_grid.GetItemState(i, LVIS_SELECTED))
-			db_delete_contact(GetRow(i)->hContact);
-
-	m_grid.SetDraw(true);
-}
-
-void QSMainDlg::DeleteOneContact(MCONTACT hContact)
-{
-	if (ServiceExists(MS_CLIST_DELETECONTACT))
-		CallService(MS_CLIST_DELETECONTACT, hContact, 0);
-	else
-		db_delete_contact(hContact);
-}
-
-wchar_t* QSMainDlg::DoMeta(MCONTACT hContact)
-{
-	for (auto &it : m_rows) {
-		if (it->hContact != hContact)
-			continue;
-
-		if (it->bIsMeta) {
-			if (it->wparam == 0)
-				it->wparam = ++hLastMeta;
-		}
-		else if (it->bIsSub)
-			it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam);
-
-		if (it->wparam > 0) {
-			CMStringW tmp(FORMAT, L"[%d]", int(it->wparam));
-			if (it->lparam > 0)
-				tmp.AppendFormat(L" %d", int(it->lparam));
-			return tmp.Detach();
-		}
-		break;
-	}
-
-	return nullptr;
-}
-
-void QSMainDlg::DrawSB()
-{
-	CStatusBarItem global(0, 0);
-	for (auto &it : m_sbdata) {
-		global.found += it->found;
-		global.liston += it->liston;
-		global.online += it->online;
-		global.total += it->total;
-	}
-
-	CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online);
-	
-	RECT rc;
-	HDC hdc = GetDC(hwndStatusBar);
-	DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT);
-	ReleaseDC(hwndStatusBar, hdc);
-
-	int all = rc.right - rc.left, i = 1;
-
-	mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2)));
-	parts[0] = all;
-	for (auto &it : m_sbdata) {
-		UNREFERENCED_PARAMETER(it);
-		all += 55;
-		parts[i++] = all;
-	}
-	parts[i] = -1;
-	SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get()));
-	SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str()));
-
-	i = 1;
-	for (auto &it : m_sbdata) {
-		HICON hIcon;
-		wchar_t c, *pc;
-		if (it->bAccDel) {
-			c = '!';
-			pc = TranslateT("deleted");
-			hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
-		}
-		else if (it->bAccOff) {
-			c = '?';
-			pc = TranslateT("disabled");
-			hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
-		}
-		else {
-			c = ' ';
-			pc = TranslateT("active");
-			hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE);
-		}
-
-		SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon);
-
-		buf.Format(L"%c %d", c, it->found);
-		SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str()));
-
-		auto *pa = Proto_GetAccount(it->szProto);
-		buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online);
-		SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str()));
-		i++;
-	}
-}
-
-void QSMainDlg::FillGrid()
-{
-	m_grid.SetDraw(false);
-
-	for (auto &it: m_rows)
-		ProcessLine(it);
-
-	m_grid.SetDraw(true);
-	InvalidateRect(m_grid.GetHwnd(), 0, FALSE);
-
-	Sort();
-	UpdateSB();
-	AdvancedFilter();
-
-	m_grid.SetCurSel(0);
-}
-
-void QSMainDlg::FillProtoCombo()
-{
-	cmbProto.ResetContent();
-	cmbProto.AddString(TranslateT("All"));
-
-	for (auto &it : Accounts())
-		cmbProto.AddString(it->tszAccountName, (LPARAM)it);
-
-	cmbProto.SetCurSel(0);
-}
-
-int QSMainDlg::FindItem(CRowItem *pRow)
-{
-	if (pRow == nullptr)
-		return -1;
-
-	LV_FINDINFO fi = {};
-	fi.flags = LVFI_PARAM;
-	fi.lParam = LPARAM(pRow);
-	return m_grid.FindItem(-1, &fi);
-}
-
-int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum)
-{
-	for (auto &it : m_rows) {
-		if (it->hContact != hMeta)
-			continue;
-
-		// new meta
-		if (it->wparam == 0) {
-			it->wparam = ++hLastMeta;
-			it->lparam = 0;
-		}
-		metaNum = it->wparam;
-		it->lparam++;
-		return it->lparam;
-	}
-
-	return 0;
-}
-
-CRowItem* QSMainDlg::FindRow(MCONTACT hContact)
-{
-	for (auto &it : m_rows)
-		if (it->hContact == hContact)
-			return it;
-
-	return nullptr;
-}
-
-MCONTACT QSMainDlg::GetFocusedContact()
-{
-	int idx = m_grid.GetSelectionMark();
-	if (idx == -1)
-		return -1;
-	
-	INT_PTR data = m_grid.GetItemData(idx);
-	return (data == -1) ? -1 : ((CRowItem *)data)->hContact;
-}
-
-int QSMainDlg::GetLVSubItem(int x, int y)
-{
-	LV_HITTESTINFO info = {};
-	info.pt.x = x;
-	info.pt.y = y;
-	ScreenToClient(m_grid.GetHwnd(), &info.pt);
-	if (m_grid.SubItemHitTest(&info) == -1)
-		return -1;
-
-	return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1;
-}
-
-void QSMainDlg::PrepareTable(bool bReset)
-{
-	m_grid.DeleteAllItems();
-
-	HDITEM hdi = {};
-	hdi.mask = HDI_FORMAT;
-
-	int old = tableColumns;
-	tableColumns = 0;
-
-	LV_COLUMN lvc = {};
-	lvc.mask = LVCF_TEXT | LVCF_WIDTH;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->bEnabled)
-			AddColumn(tableColumns++, it);
-			
-		it->SetSpecialColumns();
-	}
-
-	if (bReset)
-		for (int i = old + tableColumns - 1; i >= tableColumns; i--)
-			m_grid.DeleteColumn(i);
-}
-
-bool QSMainDlg::PrepareToFill()
-{
-	if (g_plugin.m_columns.getCount() == 0)
-		return false;
-
-	for (auto &it : g_plugin.m_columns)
-		if (it->bEnabled)
-			it->bInit = true;
-
-	hLastMeta = 0;
-
-	m_rows.destroy();
-	for (auto &hContact : Contacts())
-		m_rows.insert(new CRowItem(hContact, this));
-
-	return m_rows.getCount() != 0;
-}
-
-void QSMainDlg::ProcessLine(CRowItem *pRow, bool test)
-{
-	if (pRow->bDeleted)
-		return;
-
-	if (test)
-		pRow->bPattern = CheckPattern(pRow);
-
-	if (pRow->bPattern) {
-		if (!pRow->bActive) {
-			if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) {
-				// check for proto in combo
-				if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) {
-					pRow->bActive = true;
-					AddContactToList(pRow->hContact, pRow);
-				}
-			}
-		}
-	}
-	else if (pRow->bActive) {
-		pRow->bActive = false;
-		m_grid.DeleteItem(FindItem(pRow));
-	}
-}
-
-void QSMainDlg::SaveColumnOrder()
-{
-	int idx = 0, col = 0;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->bEnabled) {
-			it->width = m_grid.GetColumnWidth(col++);
-			g_plugin.SaveColumn(idx, *it);
-		}
-		idx++;
-	}
-}
-
-void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact)
-{
-	if (hContact) {
-		Clist_ContactDoubleClicked(hContact);
-		if (g_plugin.m_flags & QSO_AUTOCLOSE)
-			Close();
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// contact menu
-
-static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param)
-{
-	((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param);
-	return 0;
-}
-
-void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col)
-{
-	auto &pCol = g_plugin.m_columns[col];
-
-	auto *pRow = FindRow(hContact);
-	if (pRow == nullptr)
-		return;
-
-	const char *szModule = pCol.module;
-	if (szModule == nullptr)
-		szModule = pRow->szProto;
-
-	auto &pVal = pRow->pValues[col];
-	CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title);
-
-	ENTER_STRING es = {};
-	es.szModuleName = MODULENAME;
-	es.caption = TranslateT("Enter new cell value");
-	es.ptszInitVal = pVal.text;
-	if (!EnterString(&es))
-		return;
-
-	replaceStrW(pVal.text, es.ptszResult);
-	if (pCol.datatype != QSTS_STRING)
-		pVal.data = _wtoi(pVal.text);
-
-	switch (pCol.datatype) {
-	case QSTS_BYTE:
-		db_set_b(hContact, szModule, pCol.setting, pVal.data);
-		break;
-	case QSTS_WORD:
-		db_set_w(hContact, szModule, pCol.setting, pVal.data);
-		break;
-	case QSTS_DWORD:
-	case QSTS_SIGNED:
-	case QSTS_HEXNUM:
-		db_set_dw(hContact, szModule, pCol.setting, pVal.data);
-		break;
-
-	case QSTS_STRING:
-		db_set_ws(hContact, szModule, pCol.setting, pVal.text);
-		break;
-	}
-
-	UpdateLVCell(FindItem(pRow), col, pVal.text);
-}
-
-void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col)
-{
-	if (hContact == 0)
-		return;
-
-	HANDLE srvhandle = 0;
-
-	bool bDoit = false;
-	if (col >= 0) {
-		if ((col = ListViewToColumn(col)) == -1)
-			return;
-
-		auto &pCol = g_plugin.m_columns[col];
-		if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) {
-			bDoit = true;
-
-			srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col);
-
-			if (mnuhandle == nullptr) {
-				CMenuItem mi(&g_plugin);
-				SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81);
-				mi.name.a = LPGEN("Change setting through QS");
-				mi.pszService = "QS/Dummy";
-				mnuhandle = Menu_AddContactMenuItem(&mi);
-			}
-			else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0);
-		}
-	}
-
-	POINT pt;
-	GetCursorPos(&pt);
-	HMENU hMenu = Menu_BuildContactMenu(hContact);
-	if (hMenu) {
-		int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0);
-		if (iCmd) {
-			if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) {
-				if (g_plugin.m_flags & QSO_AUTOCLOSE)
-					CloseSrWindow();
-			}
-		}
-
-		::DestroyMenu(hMenu);
-	}
-
-	if (srvhandle)
-		DestroyServiceFunction(srvhandle);
-	if (bDoit)
-		Menu_ShowItem(mnuhandle, false);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// muptiple selection popup menu
-
-static HMENU MakeContainerMenu()
-{
-	HMENU hMenu = CreatePopupMenu();
-
-	for (int i = 0;; i++) {
-		char setting[10];
-		_itoa_s(i, setting, 10);
-		ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting));
-		if (wszName != nullptr)
-			AppendMenuW(hMenu, MF_STRING, 300 + i, wszName);
-		else
-			break;
-	}
-	
-	return hMenu;
-}
-
-void QSMainDlg::ShowMultiPopup(int cnt)
-{
-	HMENU hMenu = CreatePopupMenu();
-	AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt));
-	AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
-	AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete"));
-	AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy"));
-	AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta"));
-
-	HMENU cntmenu = MakeContainerMenu();
-	AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container"));
-
-	if (HMENU grpmenu = Clist_GroupBuildMenu(400))
-		AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group"));
-
-	POINT pt;
-	GetCursorPos(&pt);
-
-	int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0);
-	switch (iRes) {
-	case 101:
-		DeleteByList();
-		break;
-
-	case 102:
-		CopyMultiLines();
-		break;
-
-	case 103:
-		ConvertToMeta();
-		break;
-	}
-	
-	if (iRes >= 300 && iRes <= 399) {
-		wchar_t buf[100];
-		if (iRes == 300) // default container, just delete setting
-			buf[0] = 0;
-		else
-			GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND);
-
-		MoveToContainer(buf);
-	}
-	else if (iRes >= 400 && iRes <= 499)
-		MoveToGroup(Clist_GroupGetName(iRes - 400));
-}
-
-void QSMainDlg::ConvertToMeta()
-{
-	MCONTACT hMeta = 0;
-	
-	int nCount = m_grid.GetItemCount();
-	for (int i = 0; i < nCount; i++) {
-		if (!m_grid.GetItemState(i, LVIS_SELECTED))
-			continue;
-
-		auto *pRow = GetRow(i);
-		if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) {
-			if (hMeta == 0)
-				hMeta = tmp;
-			else if (hMeta != tmp) {
-				MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR);
-				return;
-			}
-		}
-	}
-
-	if (hMeta != 0)
-		if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING))
-			return;
-
-	for (int i = 0; i < nCount; i++) {
-		if (!m_grid.GetItemState(i, LVIS_SELECTED))
-			continue;
-
-		auto *pRow = GetRow(i);
-		if (hMeta)
-			db_mc_addToMeta(pRow->hContact, hMeta);
-		else
-			db_mc_convertToMeta(pRow->hContact);
-	}
-}
-
-void QSMainDlg::MoveToContainer(const wchar_t *pwszName)
-{
-	int grcol = -1;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->isContainer) {
-			if (it->bEnabled)
-				grcol = g_plugin.m_columns.indexOf(&it);
-			else
-				it->bInit = false;
-		}
-	}
-
-	int nCount = m_grid.GetItemCount();
-	for (int i = 0; i < nCount; i++) {
-		if (!m_grid.GetItemState(i, LVIS_SELECTED))
-			continue;
-
-		auto *pRow = GetRow(i);
-		if (*pwszName == 0)
-			db_unset(pRow->hContact, "Tab_SRMsg", "containerW");
-		else
-			db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName);
-
-		if (grcol != -1) {
-			auto &pVal = pRow->pValues[grcol];
-			replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr);
-			UpdateLVCell(i, grcol, pwszName);
-		}
-	}
-}
-
-void QSMainDlg::MoveToGroup(const wchar_t *pwszName)
-{
-	int grcol = -1;
-	for (auto &it : g_plugin.m_columns) {
-		if (it->isGroup) {
-			if (it->bEnabled)
-				grcol = g_plugin.m_columns.indexOf(&it);
-			else
-				it->bInit = false;
-		}
-	}
-
-	int nCount = m_grid.GetItemCount();
-	for (int i = 0; i < nCount; i++) {
-		if (!m_grid.GetItemState(i, LVIS_SELECTED))
-			continue;
-
-		auto *pRow = GetRow(i);
-		Clist_SetGroup(pRow->hContact, pwszName);
-
-		if (grcol != -1) {
-			auto &pVal = pRow->pValues[grcol];
-			replaceStrW(pVal.text, pwszName);
-			UpdateLVCell(i, grcol, pwszName);
-		}
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// grid sorting
-
-static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type)
-{
-	bool typ1, typ2;
-	UINT_PTR i1, i2;
-	int result = 0;
-	CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2;
-
-	if (type == StatusSort) {
-		i1 = r1->status, i2 = r2->status;
-		if (i1 == ID_STATUS_OFFLINE) i1 += 64;
-		if (i2 == ID_STATUS_OFFLINE) i2 += 64;
-		typ1 = typ2 = false;
-	}
-	else {
-		auto &res1 = r1->pValues[type], &res2 = r2->pValues[type];
-		i1 = res1.data, i2 = res2.data;
-		typ1 = i1 == -1; typ2 = i1 == -1;
-
-		if (typ1 && typ2) { // two strings
-			if (res1.text == 0 && res2.text == 0)
-				result = 0;
-			else if (res2.text == 0)
-				result = 1;
-			else if (res1.text == 0)
-				result = -1;
-			else
-				result = lstrcmpiW(res1.text, res2.text);
-		}
-		else if (typ1 || typ2) // string & num
-			result = (typ1) ? 1 : -1;
-	}
-
-	if (!typ1 && !typ2) { // not strings
-		if (i1 > i2)
-			result = 1;
-		else if (i1 < i2)
-			result = -1;
-		else
-			result = 0;
-	}
-
-	if (g_plugin.m_flags & QSO_SORTASC)
-		result = -result;
-	return result;
-}
-
-void QSMainDlg::Sort()
-{
-	if (g_plugin.m_sortOrder >= tableColumns)
-		g_plugin.m_sortOrder = StatusSort;
-	m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder));
-
-	if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS))
-		m_grid.SortItems(&CompareItem, StatusSort);
-}
-
-void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText)
-{
-	auto &pCol = g_plugin.m_columns[column];
-	auto *pRow = GetRow(item);
-
-	if (pwszText == nullptr) {
-		auto &pVal = pRow->pValues[column];
-		replaceStrW(pVal.text, 0);
-		pVal.LoadOneItem(pRow->hContact, pCol, this);
-		pwszText = pVal.text;
-	}
-
-	m_grid.SetItemText(item, ColumnToListView(column), pwszText);
-	
-	if (pCol.bFilter)
-		ProcessLine(pRow, true);
-	if (g_plugin.m_sortOrder == column)
-		Sort();
-}
-
-void QSMainDlg::UpdateSB()
-{
-	m_sbdata.destroy();
-
-	for (auto &it : m_rows) {
-		if (it->szProto == nullptr)
-			continue;
-
-		CStatusBarItem tmp(it->szProto, it->flags);
-		auto *pItem = m_sbdata.find(&tmp);
-		if (pItem == nullptr)
-			m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags));
-
-		pItem->total++;
-
-		if (it->bActive)
-			pItem->found++;
-
-		if (it->status != ID_STATUS_OFFLINE) {
-			pItem->online++;
-			if (it->bActive)
-				pItem->liston++;
-		}
-	}
-
-	DrawSB();
-}
+/*
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it &/|
+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 | 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"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// patterns
+
+bool QSMainDlg::CheckPattern(CRowItem *pRow)
+{
+	if (m_patterns.getCount() == 0)
+		return true;
+	
+	for (auto &p : m_patterns)
+		p->res = 0;
+
+	int i = 0;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->bEnabled && it->bFilter && pRow->pValues[i].text != nullptr) {
+			CMStringW buf(pRow->pValues[i].text);
+			buf.MakeLower();
+
+			for (auto &p : m_patterns)
+				if (!p->res && buf.Find(p->str) != -1)
+					p->res = true;
+		}
+		i++;
+	}
+
+	bool result = true;
+	for (auto &p : m_patterns)
+		result = result && p->res;
+	return result;
+}
+
+void QSMainDlg::MakePattern(const wchar_t *pwszPattern)
+{
+	m_patterns.destroy();
+	if (mir_wstrlen(pwszPattern) == 0)
+		return;
+
+	// m_wszPatternBuf works as a storage for patterns, we store pointers to it in m_patterns
+	m_wszPatternBuf = mir_wstrdup(pwszPattern);
+	CharLowerW(m_wszPatternBuf);
+	
+	for (wchar_t *p = m_wszPatternBuf; *p; ) {
+		auto *pWord = wcspbrk(p, L" \"");
+		if (pWord == nullptr) {
+			m_patterns.insert(new Pattern(p));
+			return;
+		}
+
+		bool isSpace = pWord[0] == ' ';
+
+		// there's some valuable info between p and pWord
+		if (pWord != p) {
+			*pWord = 0;
+			m_patterns.insert(new Pattern(p));
+		}
+
+		if (isSpace) {
+			p = ltrimpw(pWord + 1); // skip all spaces
+		}
+		else {
+			auto *pEnd = wcschr(++pWord, '\"');
+			
+			// treat the rest of line as one pattern
+			if (pEnd == nullptr) {
+				m_patterns.insert(new Pattern(pWord));
+				return;
+			}
+
+			*pEnd = 0;
+			m_patterns.insert(new Pattern(pWord));
+			p = ltrimpw(pEnd + 1);
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void QSMainDlg::AddColumn(int idx, ColumnItem *pCol)
+{
+	LV_COLUMN lvcol = {};
+	lvcol.mask = LVCF_TEXT | LVCF_WIDTH;
+	lvcol.pszText = TranslateW(pCol->title);
+	lvcol.cx = pCol->width;
+	m_grid.InsertColumn(idx, &lvcol);
+
+	HDITEM hdi;
+	hdi.mask = HDI_FORMAT;
+	if (pCol->bFilter)
+		hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX | HDF_CHECKED;
+	else
+		hdi.fmt = HDF_LEFT | HDF_STRING | HDF_CHECKBOX;
+	SendMessage(m_grid.GetHeader(), HDM_SETITEM, idx, LPARAM(&hdi));
+}
+
+void QSMainDlg::AddContactToList(MCONTACT hContact, CRowItem *pRow)
+{
+	LV_ITEMW li = {};
+	li.mask = LVIF_IMAGE | LVIF_PARAM;
+	li.iItem = 100000;
+	li.iImage = Clist_GetContactIcon(hContact);
+	li.lParam = LPARAM(pRow);
+
+	li.iItem = m_grid.InsertItem(&li);
+	li.iImage = 0;
+	li.iSubItem = 0;
+
+	for (int i = 0; i < g_plugin.m_columns.getCount(); i++) {
+		auto &col = g_plugin.m_columns[i];
+		if (!col.bEnabled)
+			continue;
+
+		// Client icons preprocess
+		li.pszText = pRow->pValues[i].text;
+		li.mask = LVIF_TEXT;
+		if ((col.isClient && (g_plugin.m_flags & QSO_CLIENTICONS) && li.pszText != 0) || col.isXstatus || col.isGender)
+			li.mask |= LVIF_IMAGE;
+		m_grid.SetItem(&li);
+		li.iSubItem++;
+	}
+}
+
+void QSMainDlg::AdvancedFilter()
+{
+	m_grid.SetDraw(false);
+
+	for (auto &it : m_rows) {
+		bool bShow = (szFilterProto == nullptr) || !mir_strcmp(szFilterProto, it->szProto);
+		if (bShow && !bShowOffline && it->status == ID_STATUS_OFFLINE)
+			bShow = false;
+
+		if (it->bPattern) {
+			if (bShow) {
+				if (!it->bActive)
+					ProcessLine(it, false);
+			}
+			else {
+				it->bActive = false;
+				m_grid.DeleteItem(FindItem(it));
+			}
+		}
+	}
+
+	m_grid.SetDraw(true);
+	InvalidateRect(m_grid.GetHwnd(), 0, false);
+
+	Sort();
+	UpdateSB();
+}
+
+void QSMainDlg::ChangeStatusPicture(CRowItem *pRow, MCONTACT, LPARAM lParam)
+{
+	int idx = FindItem(pRow);
+	if (idx == -1)
+		return;
+
+	LV_ITEMW li = {};
+	li.iItem = idx;
+	li.mask = LVIF_IMAGE;
+	li.iImage = lParam; //CallService(MS_CLIST_GETCONTACTICON,hContact,0);
+	m_grid.SetItem(&li);
+}
+
+void QSMainDlg::CopyMultiLines()
+{
+	CMStringW buf;
+
+	int i = 0;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->bEnabled) {
+			it->width = m_grid.GetColumnWidth(i);
+			if (it->width >= 10)
+				buf.AppendFormat(L"%s\t", it->title);
+		}
+		i++;
+	}
+	buf.Append(L"\r\n");
+
+	int nRows = m_grid.GetItemCount();
+	int nSelected = m_grid.GetSelectedCount();
+
+	for (int j = 0; j < nRows; j++) {
+		if (nSelected > 1 && !m_grid.GetItemState(j, LVIS_SELECTED))
+			continue;
+
+		auto *pRow = GetRow(j);
+		
+		i = 0;
+		for (auto &it : g_plugin.m_columns) {
+			if (it->bEnabled && it->width >= 10)
+				buf.AppendFormat(L"%s\t", pRow->pValues[i].getText());
+			i++;
+		}
+		buf.Append(L"\r\n");
+	}
+
+	Utils_ClipboardCopy(buf);
+}
+
+void QSMainDlg::DeleteByList()
+{
+	if (IDOK != MessageBoxW(0, TranslateT("Do you really want to delete selected contacts?"), TranslateT("Warning"), MB_OKCANCEL + MB_ICONWARNING))
+		return;
+
+	m_grid.SetDraw(false);
+
+	for (int i = m_grid.GetItemCount() - 1; i >= 0; i--)
+		if (m_grid.GetItemState(i, LVIS_SELECTED))
+			db_delete_contact(GetRow(i)->hContact);
+
+	m_grid.SetDraw(true);
+}
+
+void QSMainDlg::DeleteOneContact(MCONTACT hContact)
+{
+	if (ServiceExists(MS_CLIST_DELETECONTACT))
+		CallService(MS_CLIST_DELETECONTACT, hContact, 0);
+	else
+		db_delete_contact(hContact);
+}
+
+wchar_t* QSMainDlg::DoMeta(MCONTACT hContact)
+{
+	for (auto &it : m_rows) {
+		if (it->hContact != hContact)
+			continue;
+
+		if (it->bIsMeta) {
+			if (it->wparam == 0)
+				it->wparam = ++hLastMeta;
+		}
+		else if (it->bIsSub)
+			it->lparam = FindMeta(db_mc_getMeta(hContact), it->wparam);
+
+		if (it->wparam > 0) {
+			CMStringW tmp(FORMAT, L"[%d]", int(it->wparam));
+			if (it->lparam > 0)
+				tmp.AppendFormat(L" %d", int(it->lparam));
+			return tmp.Detach();
+		}
+		break;
+	}
+
+	return nullptr;
+}
+
+void QSMainDlg::DrawSB()
+{
+	CStatusBarItem global(0, 0);
+	for (auto &it : m_sbdata) {
+		global.found += it->found;
+		global.liston += it->liston;
+		global.online += it->online;
+		global.total += it->total;
+	}
+
+	CMStringW buf(FORMAT, TranslateT("%i users found (%i) Online: %i"), global.found, m_rows.getCount(), global.online);
+	
+	RECT rc;
+	HDC hdc = GetDC(hwndStatusBar);
+	DrawTextW(hdc, buf, buf.GetLength(), &rc, DT_CALCRECT);
+	ReleaseDC(hwndStatusBar, hdc);
+
+	int all = rc.right - rc.left, i = 1;
+
+	mir_ptr<int> parts((int*)mir_alloc(sizeof(int) * (m_sbdata.getCount()+2)));
+	parts[0] = all;
+	for (auto &it : m_sbdata) {
+		UNREFERENCED_PARAMETER(it);
+		all += 55;
+		parts[i++] = all;
+	}
+	parts[i] = -1;
+	SendMessageW(hwndStatusBar, SB_SETPARTS, m_sbdata.getCount() + 2, LPARAM(parts.get()));
+	SendMessageW(hwndStatusBar, SB_SETTEXTW, 0, LPARAM(buf.c_str()));
+
+	i = 1;
+	for (auto &it : m_sbdata) {
+		HICON hIcon;
+		wchar_t c, *pc;
+		if (it->bAccDel) {
+			c = '!';
+			pc = TranslateT("deleted");
+			hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
+		}
+		else if (it->bAccOff) {
+			c = '?';
+			pc = TranslateT("disabled");
+			hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_OFFLINE);
+		}
+		else {
+			c = ' ';
+			pc = TranslateT("active");
+			hIcon = Skin_LoadProtoIcon(it->szProto, ID_STATUS_ONLINE);
+		}
+
+		SendMessageW(hwndStatusBar, SB_SETICON, i, (LPARAM)hIcon);
+
+		buf.Format(L"%c %d", c, it->found);
+		SendMessageW(hwndStatusBar, SB_SETTEXTW, i, LPARAM(buf.c_str()));
+
+		auto *pa = Proto_GetAccount(it->szProto);
+		buf.Format(L"%s (%s): %d (%d); %s %d (%d))", pa->tszAccountName, pc, it->found, it->total, TranslateT("Online"), it->liston, it->online);
+		SendMessageW(hwndStatusBar, SB_SETTIPTEXTW, i, LPARAM(buf.c_str()));
+		i++;
+	}
+}
+
+void QSMainDlg::FillGrid()
+{
+	m_grid.SetDraw(false);
+
+	for (auto &it: m_rows)
+		ProcessLine(it);
+
+	m_grid.SetDraw(true);
+	InvalidateRect(m_grid.GetHwnd(), 0, FALSE);
+
+	Sort();
+	UpdateSB();
+	AdvancedFilter();
+
+	m_grid.SetCurSel(0);
+}
+
+void QSMainDlg::FillProtoCombo()
+{
+	cmbProto.ResetContent();
+	cmbProto.AddString(TranslateT("All"));
+
+	for (auto &it : Accounts())
+		cmbProto.AddString(it->tszAccountName, (LPARAM)it);
+
+	cmbProto.SetCurSel(0);
+}
+
+int QSMainDlg::FindItem(CRowItem *pRow)
+{
+	if (pRow == nullptr)
+		return -1;
+
+	LV_FINDINFO fi = {};
+	fi.flags = LVFI_PARAM;
+	fi.lParam = LPARAM(pRow);
+	return m_grid.FindItem(-1, &fi);
+}
+
+int QSMainDlg::FindMeta(MCONTACT hMeta, WPARAM &metaNum)
+{
+	for (auto &it : m_rows) {
+		if (it->hContact != hMeta)
+			continue;
+
+		// new meta
+		if (it->wparam == 0) {
+			it->wparam = ++hLastMeta;
+			it->lparam = 0;
+		}
+		metaNum = it->wparam;
+		it->lparam++;
+		return it->lparam;
+	}
+
+	return 0;
+}
+
+CRowItem* QSMainDlg::FindRow(MCONTACT hContact)
+{
+	for (auto &it : m_rows)
+		if (it->hContact == hContact)
+			return it;
+
+	return nullptr;
+}
+
+MCONTACT QSMainDlg::GetFocusedContact()
+{
+	int idx = m_grid.GetSelectionMark();
+	if (idx == -1)
+		return -1;
+	
+	INT_PTR data = m_grid.GetItemData(idx);
+	return (data == -1) ? -1 : ((CRowItem *)data)->hContact;
+}
+
+int QSMainDlg::GetLVSubItem(int x, int y)
+{
+	LV_HITTESTINFO info = {};
+	info.pt.x = x;
+	info.pt.y = y;
+	ScreenToClient(m_grid.GetHwnd(), &info.pt);
+	if (m_grid.SubItemHitTest(&info) == -1)
+		return -1;
+
+	return (info.flags & LVHT_ONITEM) ? info.iSubItem : -1;
+}
+
+void QSMainDlg::PrepareTable(bool bReset)
+{
+	m_grid.DeleteAllItems();
+
+	HDITEM hdi = {};
+	hdi.mask = HDI_FORMAT;
+
+	int old = tableColumns;
+	tableColumns = 0;
+
+	LV_COLUMN lvc = {};
+	lvc.mask = LVCF_TEXT | LVCF_WIDTH;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->bEnabled)
+			AddColumn(tableColumns++, it);
+			
+		it->SetSpecialColumns();
+	}
+
+	if (bReset)
+		for (int i = old + tableColumns - 1; i >= tableColumns; i--)
+			m_grid.DeleteColumn(i);
+}
+
+bool QSMainDlg::PrepareToFill()
+{
+	if (g_plugin.m_columns.getCount() == 0)
+		return false;
+
+	for (auto &it : g_plugin.m_columns)
+		if (it->bEnabled)
+			it->bInit = true;
+
+	hLastMeta = 0;
+
+	m_rows.destroy();
+	for (auto &hContact : Contacts())
+		m_rows.insert(new CRowItem(hContact, this));
+
+	return m_rows.getCount() != 0;
+}
+
+void QSMainDlg::ProcessLine(CRowItem *pRow, bool test)
+{
+	if (pRow->bDeleted)
+		return;
+
+	if (test)
+		pRow->bPattern = CheckPattern(pRow);
+
+	if (pRow->bPattern) {
+		if (!pRow->bActive) {
+			if ((g_plugin.m_flags & QSO_SHOWOFFLINE) || pRow->status != ID_STATUS_OFFLINE) {
+				// check for proto in combo
+				if (!szFilterProto || !mir_strcmp(szFilterProto, pRow->szProto)) {
+					pRow->bActive = true;
+					AddContactToList(pRow->hContact, pRow);
+				}
+			}
+		}
+	}
+	else if (pRow->bActive) {
+		pRow->bActive = false;
+		m_grid.DeleteItem(FindItem(pRow));
+	}
+}
+
+void QSMainDlg::SaveColumnOrder()
+{
+	int idx = 0, col = 0;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->bEnabled) {
+			it->width = m_grid.GetColumnWidth(col++);
+			g_plugin.SaveColumn(idx, *it);
+		}
+		idx++;
+	}
+}
+
+void QSMainDlg::ShowContactMsgDlg(MCONTACT hContact)
+{
+	if (hContact) {
+		Clist_ContactDoubleClicked(hContact);
+		if (g_plugin.m_flags & QSO_AUTOCLOSE)
+			Close();
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// contact menu
+
+static INT_PTR ColChangeFunc(void *pThis, WPARAM hContact, LPARAM, LPARAM param)
+{
+	((QSMainDlg *)pThis)->ChangeCellValue(hContact, (int)param);
+	return 0;
+}
+
+void QSMainDlg::ChangeCellValue(MCONTACT hContact, int col)
+{
+	auto &pCol = g_plugin.m_columns[col];
+
+	auto *pRow = FindRow(hContact);
+	if (pRow == nullptr)
+		return;
+
+	const char *szModule = pCol.module;
+	if (szModule == nullptr)
+		szModule = pRow->szProto;
+
+	auto &pVal = pRow->pValues[col];
+	CMStringW wszTitle(FORMAT, TranslateT("Editing of column %s"), pCol.title);
+
+	ENTER_STRING es = {};
+	es.szModuleName = MODULENAME;
+	es.caption = TranslateT("Enter new cell value");
+	es.ptszInitVal = pVal.text;
+	if (!EnterString(&es))
+		return;
+
+	replaceStrW(pVal.text, es.ptszResult);
+	if (pCol.datatype != QSTS_STRING)
+		pVal.data = _wtoi(pVal.text);
+
+	switch (pCol.datatype) {
+	case QSTS_BYTE:
+		db_set_b(hContact, szModule, pCol.setting, pVal.data);
+		break;
+	case QSTS_WORD:
+		db_set_w(hContact, szModule, pCol.setting, pVal.data);
+		break;
+	case QSTS_DWORD:
+	case QSTS_SIGNED:
+	case QSTS_HEXNUM:
+		db_set_dw(hContact, szModule, pCol.setting, pVal.data);
+		break;
+
+	case QSTS_STRING:
+		db_set_ws(hContact, szModule, pCol.setting, pVal.text);
+		break;
+	}
+
+	UpdateLVCell(FindItem(pRow), col, pVal.text);
+}
+
+void QSMainDlg::ShowContactMenu(MCONTACT hContact, int col)
+{
+	if (hContact == 0)
+		return;
+
+	HANDLE srvhandle = 0;
+
+	bool bDoit = false;
+	if (col >= 0) {
+		if ((col = ListViewToColumn(col)) == -1)
+			return;
+
+		auto &pCol = g_plugin.m_columns[col];
+		if (pCol.setting_type == QST_SETTING && pCol.datatype != QSTS_TIMESTAMP) {
+			bDoit = true;
+
+			srvhandle = CreateServiceFunctionObjParam("QS/Dummy", &ColChangeFunc, this, col);
+
+			if (mnuhandle == nullptr) {
+				CMenuItem mi(&g_plugin);
+				SET_UID(mi, 0xD384A798, 0x5D4C, 0x48B4, 0xB3, 0xE2, 0x30, 0x04, 0x6E, 0xD6, 0xF4, 0x81);
+				mi.name.a = LPGEN("Change setting through QS");
+				mi.pszService = "QS/Dummy";
+				mnuhandle = Menu_AddContactMenuItem(&mi);
+			}
+			else Menu_ModifyItem(mnuhandle, 0, INVALID_HANDLE_VALUE, 0);
+		}
+	}
+
+	POINT pt;
+	GetCursorPos(&pt);
+	HMENU hMenu = Menu_BuildContactMenu(hContact);
+	if (hMenu) {
+		int iCmd = ::TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, m_grid.GetHwnd(), 0);
+		if (iCmd) {
+			if (Clist_MenuProcessCommand(iCmd, MPCF_CONTACTMENU, hContact)) {
+				if (g_plugin.m_flags & QSO_AUTOCLOSE)
+					CloseSrWindow();
+			}
+		}
+
+		::DestroyMenu(hMenu);
+	}
+
+	if (srvhandle)
+		DestroyServiceFunction(srvhandle);
+	if (bDoit)
+		Menu_ShowItem(mnuhandle, false);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// muptiple selection popup menu
+
+static HMENU MakeContainerMenu()
+{
+	HMENU hMenu = CreatePopupMenu();
+
+	for (int i = 0;; i++) {
+		char setting[10];
+		_itoa_s(i, setting, 10);
+		ptrW wszName(db_get_wsa(0, "TAB_ContainersW", setting));
+		if (wszName != nullptr)
+			AppendMenuW(hMenu, MF_STRING, 300 + i, wszName);
+		else
+			break;
+	}
+	
+	return hMenu;
+}
+
+void QSMainDlg::ShowMultiPopup(int cnt)
+{
+	HMENU hMenu = CreatePopupMenu();
+	AppendMenuW(hMenu, MF_DISABLED + MF_STRING, 0, CMStringW(FORMAT, TranslateT("Selected %d contacts"), cnt));
+	AppendMenuW(hMenu, MF_SEPARATOR, 0, 0);
+	AppendMenuW(hMenu, MF_STRING, 101, TranslateT("&Delete"));
+	AppendMenuW(hMenu, MF_STRING, 102, TranslateT("&Copy"));
+	AppendMenuW(hMenu, MF_STRING, 103, TranslateT("C&onvert to Meta"));
+
+	HMENU cntmenu = MakeContainerMenu();
+	AppendMenuW(hMenu, MF_POPUP, UINT_PTR(cntmenu), TranslateT("Attach to &Tab container"));
+
+	if (HMENU grpmenu = Clist_GroupBuildMenu(400))
+		AppendMenuW(hMenu, MF_POPUP, UINT_PTR(grpmenu), TranslateT("&Move to Group"));
+
+	POINT pt;
+	GetCursorPos(&pt);
+
+	int iRes = TrackPopupMenu(hMenu, TPM_RETURNCMD+TPM_NONOTIFY, pt.x, pt.y, 0, m_hwnd, 0);
+	switch (iRes) {
+	case 101:
+		DeleteByList();
+		break;
+
+	case 102:
+		CopyMultiLines();
+		break;
+
+	case 103:
+		ConvertToMeta();
+		break;
+	}
+	
+	if (iRes >= 300 && iRes <= 399) {
+		wchar_t buf[100];
+		if (iRes == 300) // default container, just delete setting
+			buf[0] = 0;
+		else
+			GetMenuStringW(cntmenu, iRes, buf, _countof(buf), MF_BYCOMMAND);
+
+		MoveToContainer(buf);
+	}
+	else if (iRes >= 400 && iRes <= 499)
+		MoveToGroup(Clist_GroupGetName(iRes - 400));
+}
+
+void QSMainDlg::ConvertToMeta()
+{
+	MCONTACT hMeta = 0;
+	
+	int nCount = m_grid.GetItemCount();
+	for (int i = 0; i < nCount; i++) {
+		if (!m_grid.GetItemState(i, LVIS_SELECTED))
+			continue;
+
+		auto *pRow = GetRow(i);
+		if (MCONTACT tmp = db_mc_getMeta(pRow->hContact)) {
+			if (hMeta == 0)
+				hMeta = tmp;
+			else if (hMeta != tmp) {
+				MessageBoxW(m_hwnd, TranslateT("Some of selected contacts in different metacontacts already"), L"Quick Search", MB_ICONERROR);
+				return;
+			}
+		}
+	}
+
+	if (hMeta != 0)
+		if (IDYES != MessageBoxW(0, TranslateT("One or more contacts already belong to the same metacontact. Try to convert anyway?"), L"Quick Search", MB_YESNO + MB_ICONWARNING))
+			return;
+
+	for (int i = 0; i < nCount; i++) {
+		if (!m_grid.GetItemState(i, LVIS_SELECTED))
+			continue;
+
+		auto *pRow = GetRow(i);
+		if (hMeta)
+			db_mc_addToMeta(pRow->hContact, hMeta);
+		else
+			db_mc_convertToMeta(pRow->hContact);
+	}
+}
+
+void QSMainDlg::MoveToContainer(const wchar_t *pwszName)
+{
+	int grcol = -1;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->isContainer) {
+			if (it->bEnabled)
+				grcol = g_plugin.m_columns.indexOf(&it);
+			else
+				it->bInit = false;
+		}
+	}
+
+	int nCount = m_grid.GetItemCount();
+	for (int i = 0; i < nCount; i++) {
+		if (!m_grid.GetItemState(i, LVIS_SELECTED))
+			continue;
+
+		auto *pRow = GetRow(i);
+		if (*pwszName == 0)
+			db_unset(pRow->hContact, "Tab_SRMsg", "containerW");
+		else
+			db_set_ws(pRow->hContact, "Tab_SRMsg", "containerW", pwszName);
+
+		if (grcol != -1) {
+			auto &pVal = pRow->pValues[grcol];
+			replaceStrW(pVal.text, (*pwszName) ? pwszName : nullptr);
+			UpdateLVCell(i, grcol, pwszName);
+		}
+	}
+}
+
+void QSMainDlg::MoveToGroup(const wchar_t *pwszName)
+{
+	int grcol = -1;
+	for (auto &it : g_plugin.m_columns) {
+		if (it->isGroup) {
+			if (it->bEnabled)
+				grcol = g_plugin.m_columns.indexOf(&it);
+			else
+				it->bInit = false;
+		}
+	}
+
+	int nCount = m_grid.GetItemCount();
+	for (int i = 0; i < nCount; i++) {
+		if (!m_grid.GetItemState(i, LVIS_SELECTED))
+			continue;
+
+		auto *pRow = GetRow(i);
+		Clist_SetGroup(pRow->hContact, pwszName);
+
+		if (grcol != -1) {
+			auto &pVal = pRow->pValues[grcol];
+			replaceStrW(pVal.text, pwszName);
+			UpdateLVCell(i, grcol, pwszName);
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// grid sorting
+
+static int CALLBACK CompareItem(LPARAM l1, LPARAM l2, LPARAM type)
+{
+	bool typ1, typ2;
+	UINT_PTR i1, i2;
+	int result = 0;
+	CRowItem *r1 = (CRowItem *)l1, *r2 = (CRowItem *)l2;
+
+	if (type == StatusSort) {
+		i1 = r1->status, i2 = r2->status;
+		if (i1 == ID_STATUS_OFFLINE) i1 += 64;
+		if (i2 == ID_STATUS_OFFLINE) i2 += 64;
+		typ1 = typ2 = false;
+	}
+	else {
+		auto &res1 = r1->pValues[type], &res2 = r2->pValues[type];
+		i1 = res1.data, i2 = res2.data;
+		typ1 = i1 == -1; typ2 = i1 == -1;
+
+		if (typ1 && typ2) { // two strings
+			if (res1.text == 0 && res2.text == 0)
+				result = 0;
+			else if (res2.text == 0)
+				result = 1;
+			else if (res1.text == 0)
+				result = -1;
+			else
+				result = lstrcmpiW(res1.text, res2.text);
+		}
+		else if (typ1 || typ2) // string & num
+			result = (typ1) ? 1 : -1;
+	}
+
+	if (!typ1 && !typ2) { // not strings
+		if (i1 > i2)
+			result = 1;
+		else if (i1 < i2)
+			result = -1;
+		else
+			result = 0;
+	}
+
+	if (g_plugin.m_flags & QSO_SORTASC)
+		result = -result;
+	return result;
+}
+
+void QSMainDlg::Sort()
+{
+	if (g_plugin.m_sortOrder >= tableColumns)
+		g_plugin.m_sortOrder = StatusSort;
+	m_grid.SortItems(&CompareItem, ListViewToColumn(g_plugin.m_sortOrder));
+
+	if (g_plugin.m_sortOrder != StatusSort && (g_plugin.m_flags & QSO_SORTBYSTATUS))
+		m_grid.SortItems(&CompareItem, StatusSort);
+}
+
+void QSMainDlg::UpdateLVCell(int item, int column, const wchar_t *pwszText)
+{
+	auto &pCol = g_plugin.m_columns[column];
+	auto *pRow = GetRow(item);
+
+	if (pwszText == nullptr) {
+		auto &pVal = pRow->pValues[column];
+		replaceStrW(pVal.text, 0);
+		pVal.LoadOneItem(pRow->hContact, pCol, this);
+		pwszText = pVal.text;
+	}
+
+	m_grid.SetItemText(item, ColumnToListView(column), pwszText);
+	
+	if (pCol.bFilter)
+		ProcessLine(pRow, true);
+	if (g_plugin.m_sortOrder == column)
+		Sort();
+}
+
+void QSMainDlg::UpdateSB()
+{
+	m_sbdata.destroy();
+
+	for (auto &it : m_rows) {
+		if (it->szProto == nullptr)
+			continue;
+
+		CStatusBarItem tmp(it->szProto, it->flags);
+		auto *pItem = m_sbdata.find(&tmp);
+		if (pItem == nullptr)
+			m_sbdata.insert(pItem = new CStatusBarItem(it->szProto, it->flags));
+
+		pItem->total++;
+
+		if (it->bActive)
+			pItem->found++;
+
+		if (it->status != ID_STATUS_OFFLINE) {
+			pItem->online++;
+			if (it->bActive)
+				pItem->liston++;
+		}
+	}
+
+	DrawSB();
+}
diff --git a/plugins/QuickSearch/src/window_row.cpp b/plugins/QuickSearch/src/window_row.cpp
index d05ddb0c43..991dcafd68 100644
--- a/plugins/QuickSearch/src/window_row.cpp
+++ b/plugins/QuickSearch/src/window_row.cpp
@@ -1,224 +1,224 @@
-/*
-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"
-
-CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) :
-	hContact(_1)
-{
-	auto *pa = Proto_GetContactAccount(hContact);
-	if (pa != nullptr) {
-		szProto = pa->szModuleName;
-		if (!pa->IsEnabled())
-			bAccOff = true;
-
-		if (db_mc_isMeta(hContact))
-			bIsMeta = true;
-		else if (db_mc_isSub(hContact))
-			bIsSub = true;
-	}
-	else {
-		szProto = nullptr;
-		bAccDel = true;
-	}
-
-	if (bAccDel || bAccOff)
-		status = ID_STATUS_OFFLINE;
-	else
-		status = Contact::GetStatus(hContact);
-
-	if (int nCount = g_plugin.m_columns.getCount()) {
-		pValues = new Val[nCount];
-		for (int i = 0; i < nCount; i++)
-			pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg);
-	}
-	else pValues = nullptr;
-}
-
-CRowItem::~CRowItem()
-{
-	delete[] pValues;
-}
-
-void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText)
-{
-	if (g_plugin.m_flags & QSO_COLORIZE) {
-		if (bAccDel) {
-			clrBack = g_plugin.m_colors[bkg_del];
-			clrText = g_plugin.m_colors[fgr_del];
-			return;
-		}
-		if (bAccOff) {
-			clrBack = g_plugin.m_colors[bkg_dis];
-			clrText = g_plugin.m_colors[fgr_dis];
-			return;
-		}
-		if (bIsMeta) {
-			clrBack = g_plugin.m_colors[bkg_meta];
-			clrText = g_plugin.m_colors[fgr_meta];
-			return;
-		}
-		if (bIsSub) {
-			clrBack = g_plugin.m_colors[bkg_sub];
-			clrText = g_plugin.m_colors[fgr_sub];
-			return;
-		}
-		if (bInList) {
-			clrBack = g_plugin.m_colors[bkg_hid];
-			clrText = g_plugin.m_colors[fgr_hid];
-			return;
-		}
-	}
-
-	if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) {
-		clrBack = g_plugin.m_colors[bkg_odd];
-		clrText = g_plugin.m_colors[fgr_odd];
-	}
-	else {
-		clrBack = g_plugin.m_colors[bkg_norm];
-		clrText = g_plugin.m_colors[fgr_norm];
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static wchar_t* int2strw(uint32_t num)
-{
-	wchar_t buf[64];
-	_itow_s(num, buf, 10);
-	return mir_wstrdup(buf);
-}
-
-static wchar_t* hex2strw(uint32_t num)
-{
-	wchar_t buf[64];
-	_itow_s(num, buf, 16);
-	return mir_wstrdup(buf);
-}
-
-void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg)
-{
-	data = UINT_PTR(-1);
-	replaceStrW(text, nullptr);
-
-	switch (pCol.setting_type) {
-	case QST_SCRIPT:
-		{
-			VARSW vars(pCol.script);
-			if (g_bVarsInstalled)
-				text = variables_parse(vars, 0, hContact);
-			else 
-				text = vars.detach();
-		}
-		break;
-
-	case QST_SERVICE:
-		// !!!!!!!!!!!!!!!!!!! not implemented
-		break;
-
-	case QST_CONTACTINFO:
-		text = Contact::GetInfo(pCol.cnftype, hContact);
-		if (text)
-			data = _wtoi(text);
-		break;
-
-	case QST_OTHER:
-		switch (pCol.other) {
-		case QSTO_ACCOUNT:
-			if (auto *pa = Proto_GetContactAccount(hContact))
-				text = mir_wstrdup(pa->tszAccountName);
-			break;
-
-		case QSTO_LASTSEEN:
-			data = BuildLastSeenTimeInt(hContact, "SeenModule");
-			text = BuildLastSeenTime(data);
-			break;
-
-		case QSTO_DISPLAYNAME:
-			text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0));
-			break;
-
-		case QSTO_LASTEVENT:
-			if (MEVENT hDbEvent = db_event_last(hContact)) {
-				DBEVENTINFO dbei = {};
-				db_event_get(hDbEvent, &dbei);
-				data = dbei.timestamp;
-				text = TimeToStrW(data);
-			}
-			else text = 0;			 
-			break;
-
-		case QSTO_METACONTACT:
-			text = pDlg->DoMeta(hContact);
-			break;
-
-		case QSTO_EVENTCOUNT:
-			data = db_event_count(hContact);
-			text = int2strw(data);
-			break;
-		}
-		break;
-
-	case QST_SETTING:
-		auto *szNodule = pCol.module;
-		if (!mir_strlen(szNodule))
-			szNodule = Proto_GetBaseAccountName(hContact);
-
-		switch (pCol.datatype) {
-		case QSTS_STRING:
-			text = db_get_wsa(hContact, szNodule, pCol.setting);
-			break;
-
-		case QSTS_BYTE:
-			data = db_get_b(hContact, szNodule, pCol.setting);
-			text = int2strw(data);
-			break;
-
-		case QSTS_WORD:
-			data = db_get_w(hContact, szNodule, pCol.setting);
-			text = int2strw(data);
-			break;
-
-		case QSTS_DWORD:
-			if (pCol.setting == nullptr) {
-				data = hContact;
-				text = hex2strw(data);
-			}
-			else {
-				data = db_get_dw(hContact, szNodule, pCol.setting);
-				text = int2strw(data);
-			}
-			break;
-
-		case QSTS_SIGNED:
-			data = db_get_dw(hContact, szNodule, pCol.setting);
-			text = int2strw(data);
-			break;
-
-		case QSTS_HEXNUM:
-			data = db_get_dw(hContact, szNodule, pCol.setting);
-			text = hex2strw(data);
-			break;
-
-		case QSTS_TIMESTAMP:
-			data = db_get_dw(hContact, szNodule, pCol.setting);
-			if (data != 0)
-				text = TimeToStrW(data);
-			break;
-		}
-	}
-}
+/*
+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"
+
+CRowItem::CRowItem(MCONTACT _1, QSMainDlg *pDlg) :
+	hContact(_1)
+{
+	auto *pa = Proto_GetContactAccount(hContact);
+	if (pa != nullptr) {
+		szProto = pa->szModuleName;
+		if (!pa->IsEnabled())
+			bAccOff = true;
+
+		if (db_mc_isMeta(hContact))
+			bIsMeta = true;
+		else if (db_mc_isSub(hContact))
+			bIsSub = true;
+	}
+	else {
+		szProto = nullptr;
+		bAccDel = true;
+	}
+
+	if (bAccDel || bAccOff)
+		status = ID_STATUS_OFFLINE;
+	else
+		status = Contact::GetStatus(hContact);
+
+	if (int nCount = g_plugin.m_columns.getCount()) {
+		pValues = new Val[nCount];
+		for (int i = 0; i < nCount; i++)
+			pValues[i].LoadOneItem(hContact, g_plugin.m_columns[i], pDlg);
+	}
+	else pValues = nullptr;
+}
+
+CRowItem::~CRowItem()
+{
+	delete[] pValues;
+}
+
+void CRowItem::GetCellColor(int idx, COLORREF &clrBack, COLORREF &clrText)
+{
+	if (g_plugin.m_flags & QSO_COLORIZE) {
+		if (bAccDel) {
+			clrBack = g_plugin.m_colors[bkg_del];
+			clrText = g_plugin.m_colors[fgr_del];
+			return;
+		}
+		if (bAccOff) {
+			clrBack = g_plugin.m_colors[bkg_dis];
+			clrText = g_plugin.m_colors[fgr_dis];
+			return;
+		}
+		if (bIsMeta) {
+			clrBack = g_plugin.m_colors[bkg_meta];
+			clrText = g_plugin.m_colors[fgr_meta];
+			return;
+		}
+		if (bIsSub) {
+			clrBack = g_plugin.m_colors[bkg_sub];
+			clrText = g_plugin.m_colors[fgr_sub];
+			return;
+		}
+		if (bInList) {
+			clrBack = g_plugin.m_colors[bkg_hid];
+			clrText = g_plugin.m_colors[fgr_hid];
+			return;
+		}
+	}
+
+	if ((g_plugin.m_flags & QSO_DRAWGRID) == 0 && idx % 2 == 1) {
+		clrBack = g_plugin.m_colors[bkg_odd];
+		clrText = g_plugin.m_colors[fgr_odd];
+	}
+	else {
+		clrBack = g_plugin.m_colors[bkg_norm];
+		clrText = g_plugin.m_colors[fgr_norm];
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static wchar_t* int2strw(uint32_t num)
+{
+	wchar_t buf[64];
+	_itow_s(num, buf, 10);
+	return mir_wstrdup(buf);
+}
+
+static wchar_t* hex2strw(uint32_t num)
+{
+	wchar_t buf[64];
+	_itow_s(num, buf, 16);
+	return mir_wstrdup(buf);
+}
+
+void CRowItem::Val::LoadOneItem(MCONTACT hContact, const ColumnItem &pCol, QSMainDlg *pDlg)
+{
+	data = UINT_PTR(-1);
+	replaceStrW(text, nullptr);
+
+	switch (pCol.setting_type) {
+	case QST_SCRIPT:
+		{
+			VARSW vars(pCol.script);
+			if (g_bVarsInstalled)
+				text = variables_parse(vars, 0, hContact);
+			else 
+				text = vars.detach();
+		}
+		break;
+
+	case QST_SERVICE:
+		// !!!!!!!!!!!!!!!!!!! not implemented
+		break;
+
+	case QST_CONTACTINFO:
+		text = Contact::GetInfo(pCol.cnftype, hContact);
+		if (text)
+			data = _wtoi(text);
+		break;
+
+	case QST_OTHER:
+		switch (pCol.other) {
+		case QSTO_ACCOUNT:
+			if (auto *pa = Proto_GetContactAccount(hContact))
+				text = mir_wstrdup(pa->tszAccountName);
+			break;
+
+		case QSTO_LASTSEEN:
+			data = BuildLastSeenTimeInt(hContact, "SeenModule");
+			text = BuildLastSeenTime(data);
+			break;
+
+		case QSTO_DISPLAYNAME:
+			text = mir_wstrdup(Clist_GetContactDisplayName(hContact, 0));
+			break;
+
+		case QSTO_LASTEVENT:
+			if (MEVENT hDbEvent = db_event_last(hContact)) {
+				DBEVENTINFO dbei = {};
+				db_event_get(hDbEvent, &dbei);
+				data = dbei.timestamp;
+				text = TimeToStrW(data);
+			}
+			else text = 0;			 
+			break;
+
+		case QSTO_METACONTACT:
+			text = pDlg->DoMeta(hContact);
+			break;
+
+		case QSTO_EVENTCOUNT:
+			data = db_event_count(hContact);
+			text = int2strw(data);
+			break;
+		}
+		break;
+
+	case QST_SETTING:
+		auto *szNodule = pCol.module;
+		if (!mir_strlen(szNodule))
+			szNodule = Proto_GetBaseAccountName(hContact);
+
+		switch (pCol.datatype) {
+		case QSTS_STRING:
+			text = db_get_wsa(hContact, szNodule, pCol.setting);
+			break;
+
+		case QSTS_BYTE:
+			data = db_get_b(hContact, szNodule, pCol.setting);
+			text = int2strw(data);
+			break;
+
+		case QSTS_WORD:
+			data = db_get_w(hContact, szNodule, pCol.setting);
+			text = int2strw(data);
+			break;
+
+		case QSTS_DWORD:
+			if (pCol.setting == nullptr) {
+				data = hContact;
+				text = hex2strw(data);
+			}
+			else {
+				data = db_get_dw(hContact, szNodule, pCol.setting);
+				text = int2strw(data);
+			}
+			break;
+
+		case QSTS_SIGNED:
+			data = db_get_dw(hContact, szNodule, pCol.setting);
+			text = int2strw(data);
+			break;
+
+		case QSTS_HEXNUM:
+			data = db_get_dw(hContact, szNodule, pCol.setting);
+			text = hex2strw(data);
+			break;
+
+		case QSTS_TIMESTAMP:
+			data = db_get_dw(hContact, szNodule, pCol.setting);
+			if (data != 0)
+				text = TimeToStrW(data);
+			break;
+		}
+	}
+}
-- 
cgit v1.2.3