/////////////////////////////////////////////////////////////////////////////////////////
// Miranda NG: the free IM client for Microsoft* Windows*
//
// Copyright (C) 2012-19 Miranda NG team,
// Copyright (c) 2000-09 Miranda ICQ/IM project,
// all portions of this codebase are copyrighted to the people
// listed in contributors.txt.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// you should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// part of tabSRMM messaging plugin for Miranda.
//
// (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
//
// group chat options and generic font handling

#include "stdafx.h"

#define FONTF_BOLD   1
#define FONTF_ITALIC 2

struct FontOptionsList
{
	wchar_t *szDescr;
	COLORREF defColour;
	wchar_t *szDefFace;
	BYTE     defCharset, defStyle;
	char     defSize;
	COLORREF colour;
	wchar_t  szFace[LF_FACESIZE];
	BYTE     charset, style;
	char     size;
};

struct ColorOptionsList
{
	int		order;
	wchar_t *tszGroup;
	wchar_t *tszName;
	char    *szSetting;
	COLORREF def;
};

/*
* note: bits 24-31 in default color indicates that color is a system color index
* (GetSysColor(default_color & 0x00ffffff)), not a rgb value.
*/
static ColorOptionsList _clrs[] = {
	{ 0, LPGENW("Message sessions"), LPGENW("Input area background"), "inputbg", SRMSGDEFSET_BKGCOLOUR },
	{ 1, LPGENW("Message sessions"), LPGENW("Log background"), SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR },
	{ 0, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), LPGENW("Outgoing background"), "outbg", SRMSGDEFSET_BKGOUTCOLOUR },
	{ 1, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), LPGENW("Incoming background"), "inbg", SRMSGDEFSET_BKGINCOLOUR },
	{ 2, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), LPGENW("Status background"), "statbg", SRMSGDEFSET_BKGCOLOUR },
	{ 3, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), LPGENW("Incoming background(old)"), "oldinbg", SRMSGDEFSET_BKGINCOLOUR },
	{ 4, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), LPGENW("Outgoing background(old)"), "oldoutbg", SRMSGDEFSET_BKGOUTCOLOUR },
	{ 5, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), LPGENW("Horizontal Grid Lines"), "hgrid", RGB(224, 224, 224) },
	{ 0, LPGENW("Message sessions") L"/" LPGENW("Info Panel"), LPGENW("Panel background low"), "ipfieldsbg", 0x62caff },
	{ 1, LPGENW("Message sessions") L"/" LPGENW("Info Panel"), LPGENW("Panel background high"), "ipfieldsbgHigh", 0xf0f0f0 },
	{ 0, LPGENW("Message sessions") L"/" LPGENW("Common colors"), LPGENW("Toolbar background high"), "tbBgHigh", 0 },
	{ 1, LPGENW("Message sessions") L"/" LPGENW("Common colors"), LPGENW("Toolbar background low"), "tbBgLow", 0 },
	{ 2, LPGENW("Message sessions") L"/" LPGENW("Common colors"), LPGENW("Window fill color"), "fillColor", 0 },
	{ 3, LPGENW("Message sessions") L"/" LPGENW("Common colors"), LPGENW("Text area borders"), "cRichBorders", 0 },
	{ 4, LPGENW("Message sessions") L"/" LPGENW("Common colors"), LPGENW("Aero glow effect"), "aeroGlow", RGB(40, 40, 255) },
	{ 4, LPGENW("Message sessions") L"/" LPGENW("Common colors"), LPGENW("Generic text color (only when fill color is set)"), "genericTxtClr", 0xff000000 | COLOR_BTNTEXT },
};

static ColorOptionsList _tabclrs[] = {
	{ 0, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Normal text"), "tab_txt_normal", 0xff000000 | COLOR_BTNTEXT },
	{ 1, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Active text"), "tab_txt_active", 0xff000000 | COLOR_BTNTEXT },
	{ 2, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Hovered text"), "tab_txt_hottrack", 0xff000000 | COLOR_HOTLIGHT },
	{ 3, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Unread text"), "tab_txt_unread", 0xff000000 | COLOR_HOTLIGHT },

	{ 4, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Normal background"), "tab_bg_normal", 0xff000000 | COLOR_3DFACE },
	{ 5, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Active background"), "tab_bg_active", 0xff000000 | COLOR_3DFACE },
	{ 6, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Hovered background"), "tab_bg_hottrack", 0xff000000 | COLOR_3DFACE },
	{ 7, LPGENW("Message sessions") L"/" LPGENW("Tabs"), LPGENW("Unread background"), "tab_bg_unread", 0xff000000 | COLOR_3DFACE }
};

extern LOGFONT lfDefault;

static FontOptionsList IM_fontOptionsList[] =
{
	{ LPGENW(">> Outgoing messages"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW(">> Outgoing misc events"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("<< Incoming messages"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("<< Incoming misc events"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW(">> Outgoing name"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW(">> Outgoing timestamp"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW("<< Incoming name"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW("<< Incoming timestamp"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW(">> Outgoing messages (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW(">> Outgoing misc events (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("<< Incoming messages (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("<< Incoming misc events (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW(">> Outgoing name (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW(">> Outgoing timestamp (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW("<< Incoming name (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW("<< Incoming timestamp (old)"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, FONTF_BOLD, -12 },
	{ LPGENW("* Message Input Area"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("* Status changes"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("* Dividers"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("* Error and warning messages"), RGB(50, 50, 50), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("* Symbols (incoming)"), RGB(50, 50, 50), L"Webdings", SYMBOL_CHARSET, 0, -12 },
	{ LPGENW("* Symbols (outgoing)"), RGB(50, 50, 50), L"Webdings", SYMBOL_CHARSET, 0, -12 },
};

static FontOptionsList IP_fontOptionsList[] =
{
	{ LPGENW("Nickname"), RGB(0, 0, 0), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("UIN"), RGB(0, 0, 0), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("Status"), RGB(0, 0, 0), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("Protocol"), RGB(0, 0, 0), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("Contacts local time"), RGB(0, 0, 0), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
	{ LPGENW("Window caption (skinned mode)"), RGB(255, 255, 255), lfDefault.lfFaceName, DEFAULT_CHARSET, 0, -12 },
};

struct branch_t
{
	wchar_t*  szDescr;
	char*     szDBName;
	int       iMode;
	BYTE      bDefault;
	HTREEITEM hItem;
};
static branch_t branch1[] = {
	{ LPGENW("Open new chat rooms in the default container"), "DefaultContainer", 0, 1, nullptr },
	{ LPGENW("Flash window when someone speaks"), "FlashWindow", 0, 0, nullptr },
	{ LPGENW("Flash window when a word is highlighted"), "FlashWindowHighlight", 0, 1, nullptr },
	{ LPGENW("Create tabs or windows for highlight events"), "CreateWindowOnHighlight", 0, 0, nullptr },
	{ LPGENW("Activate chat window on highlight"), "AnnoyingHighlight", 0, 0, nullptr },
	{ LPGENW("Show list of users in the chat room"), "ShowNicklist", 0, 1, nullptr },
	{ LPGENW("Colorize nicknames in member list"), "ColorizeNicks", 0, 1, nullptr },
	{ LPGENW("Show button menus when right clicking the buttons"), "RightClickFilter", 0, 1, nullptr },
	{ LPGENW("Show topic as status message on the contact list"), "TopicOnClist", 0, 1, nullptr },
	{ LPGENW("Do not pop up the window when joining a chat room"), "PopupOnJoin", 0, 0, nullptr },
	{ LPGENW("Sync splitter position with standard IM sessions"), "SyncSplitter", 0, 0, nullptr },
	{ LPGENW("Show contact's status modes if supported by the protocol"), "ShowContactStatus", 0, 1, nullptr },
	{ LPGENW("Display contact's status icon before user role icon"), "ContactStatusFirst", 0, 0, nullptr },
	{ LPGENW("Use IRC style status indicators in the nick list"), "ClassicIndicators", 0, 0, nullptr },
	{ LPGENW("Use alternative sorting method in member list"), "AlternativeSorting", 0, 1, nullptr }
};

static branch_t branch2[] = {
	{ LPGENW("Prefix all events with a timestamp"), "ShowTimeStamp", 0, 1, nullptr },
	{ LPGENW("Timestamp only when event time differs"), "ShowTimeStampIfChanged", 0, 0, nullptr },
	{ LPGENW("Timestamp has same color as the event"), "TimeStampEventColour", 0, 0, nullptr },
	{ LPGENW("Indent the second line of a message"), "LogIndentEnabled", 0, 1, nullptr },
	{ LPGENW("Limit user names in the message log to 20 characters"), "LogLimitNames", 0, 1, nullptr },
	{ LPGENW("Add a colon (:) to auto-completed user names"), "AddColonToAutoComplete", 0, 1, nullptr },
	{ LPGENW("Add a comma instead of a colon to auto-completed user names"), "UseCommaAsColon", 0, 0, nullptr },
	{ LPGENW("Start private conversation on double click in nick list (insert nick if unchecked)"), "DoubleClick4Privat", 0, 0, nullptr },
	{ LPGENW("Strip colors from messages in the log"), "StripFormatting", 0, 0, nullptr },
	{ LPGENW("Enable the 'event filter' for new rooms"), "FilterEnabled", 0, 0, nullptr },
	{ LPGENW("Use IRC style status indicators in the log"), "LogClassicIndicators", 0, 0, nullptr },
	{ LPGENW("Allow clickable user names in the message log"), "ClickableNicks", 0, 1, nullptr },
	{ LPGENW("Add new line after names"), "NewlineAfterNames", 0, 0, nullptr },
	{ LPGENW("Colorize user names in message log"), "ColorizeNicksInLog", 0, 1, nullptr },
	{ LPGENW("Scale down icons to 10x10 pixels in the chat log"), "ScaleIcons", 0, 1, nullptr }
};

void LoadMsgDlgFont(int section, int i, LOGFONT *lf, COLORREF* colour, char *szMod)
{
	char str[32];
	int db_idx = (section == FONTSECTION_IM) ? i : i + 100;

	FontOptionsList *fol;
	switch (section) {
	case FONTSECTION_IM: fol = IM_fontOptionsList; break;
	case FONTSECTION_IP: fol = IP_fontOptionsList; break;
	default: 
		return;
	}

	if (colour) {
		mir_snprintf(str, "Font%dCol", db_idx);
		*colour = db_get_dw(0, szMod, str, fol[i].defColour);
	}

	if (lf) {
		mir_snprintf(str, "Font%dSize", db_idx);
		lf->lfHeight = (char)db_get_b(0, szMod, str, fol[i].defSize);
		lf->lfWidth = 0;
		lf->lfEscapement = 0;
		lf->lfOrientation = 0;
		mir_snprintf(str, "Font%dSty", db_idx);
		int style = db_get_b(0, szMod, str, fol[i].defStyle);
		if (i == MSGFONTID_MESSAGEAREA && section == FONTSECTION_IM && M.GetByte("inputFontFix", 1) == 1) {
			lf->lfWeight = FW_NORMAL;
			lf->lfItalic = 0;
			lf->lfUnderline = 0;
			lf->lfStrikeOut = 0;
		}
		else {
			lf->lfWeight = style & FONTF_BOLD ? FW_BOLD : FW_NORMAL;
			lf->lfItalic = style & FONTF_ITALIC ? 1 : 0;
			lf->lfUnderline = style & FONTF_UNDERLINE ? 1 : 0;
			lf->lfStrikeOut = style & FONTF_STRIKEOUT ? 1 : 0;
		}
		mir_snprintf(str, "Font%dSet", db_idx);
		lf->lfCharSet = db_get_b(0, szMod, str, fol[i].defCharset);
		lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
		lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
		lf->lfQuality = DEFAULT_QUALITY;
		lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
		mir_snprintf(str, "Font%d", db_idx);
		if ((i == 17 && !mir_strcmp(szMod, CHATFONT_MODULE)) || ((i == 20 || i == 21) && !mir_strcmp(szMod, FONTMODULE))) {
			lf->lfCharSet = SYMBOL_CHARSET;
			wcsncpy_s(lf->lfFaceName, L"Webdings", _TRUNCATE);
		}
		else wcsncpy_s(lf->lfFaceName, ptrW(db_get_wsa(0, szMod, str, fol[i].szDefFace)), _TRUNCATE);
	}
}

static IconItem _icons[] =
{
	{ LPGEN("Window Icon"), "chat_window", IDI_CHANMGR },
	{ LPGEN("Icon overlay"), "chat_overlay", IDI_OVERLAY },

	{ LPGEN("Status 1 (10x10)"), "chat_status0", IDI_STATUS0 },
	{ LPGEN("Status 2 (10x10)"), "chat_status1", IDI_STATUS1 },
	{ LPGEN("Status 3 (10x10)"), "chat_status2", IDI_STATUS2 },
	{ LPGEN("Status 4 (10x10)"), "chat_status3", IDI_STATUS3 },
	{ LPGEN("Status 5 (10x10)"), "chat_status4", IDI_STATUS4 },
	{ LPGEN("Status 6 (10x10)"), "chat_status5", IDI_STATUS5 }
};

static IconItem _logicons[] =
{
	{ LPGEN("Message in (10x10)"), "chat_log_message_in", IDI_MESSAGE },
	{ LPGEN("Message out (10x10)"), "chat_log_message_out", IDI_MESSAGEOUT },
	{ LPGEN("Action (10x10)"), "chat_log_action", IDI_ACTION },
	{ LPGEN("Add Status (10x10)"), "chat_log_addstatus", IDI_ADDSTATUS },
	{ LPGEN("Remove Status (10x10)"), "chat_log_removestatus", IDI_REMSTATUS },
	{ LPGEN("Join (10x10)"), "chat_log_join", IDI_JOIN },
	{ LPGEN("Leave (10x10)"), "chat_log_part", IDI_PART },
	{ LPGEN("Quit (10x10)"), "chat_log_quit", IDI_QUIT },
	{ LPGEN("Kick (10x10)"), "chat_log_kick", IDI_KICK },
	{ LPGEN("Notice (10x10)"), "chat_log_notice", IDI_NOTICE },
	{ LPGEN("Nickchange (10x10)"), "chat_log_nick", IDI_NICK },
	{ LPGEN("Topic (10x10)"), "chat_log_topic", IDI_TOPIC },
	{ LPGEN("Highlight (10x10)"), "chat_log_highlight", IDI_HIGHLIGHT },
	{ LPGEN("Information (10x10)"), "chat_log_info", IDI_INFO }
};

// add icons to the skinning module
void Chat_AddIcons(void)
{
	Icon_Register(g_hIconDLL, LPGEN("Message Sessions") "/" LPGEN("Group chat windows"), _icons, _countof(_icons), 0, &g_plugin);
	Icon_Register(g_hIconDLL, LPGEN("Message Sessions") "/" LPGEN("Group chat log"), _logicons, _countof(_logicons), 0, &g_plugin);
	g_chatApi.MM_IconsChanged();
}

// get icon by name from the core icon library service
HICON LoadIconEx(char *pszIcoLibName)
{
	char szTemp[256];
	mir_snprintf(szTemp, "chat_%s", pszIcoLibName);
	return IcoLib_GetIcon(szTemp);
}

static wchar_t *chatcolorsnames[] =
{
	LPGENW("Voiced"),
	LPGENW("Half operators"),
	LPGENW("Channel operators"),
	LPGENW("Extended mode 1"),
	LPGENW("Extended mode 2"),
	LPGENW("Selection background"),
	LPGENW("Selected text"),
	LPGENW("Incremental search highlight")
};

void RegisterFontServiceFonts()
{
	LOGFONT lf;
	FontIDW fid = {};
	ColourIDW cid = {};

	strncpy(fid.dbSettingsGroup, FONTMODULE, _countof(fid.dbSettingsGroup));

	int i = 0; 
	for (auto &it : IM_fontOptionsList) {
		fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS;
		LoadMsgDlgFont(FONTSECTION_IM, i, &lf, &it.colour, FONTMODULE);
		mir_snprintf(fid.setting, "Font%d", i++);
		fid.order = i;
		wcsncpy(fid.name, it.szDescr, _countof(fid.name));
		fid.deffontsettings.colour = it.colour;
		fid.deffontsettings.size = (char)lf.lfHeight;
		fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? FONTF_BOLD : 0) | (lf.lfItalic ? FONTF_ITALIC : 0);
		fid.deffontsettings.charset = lf.lfCharSet;
		fid.flags = fid.flags & ~FIDF_CLASSMASK | (fid.deffontsettings.style&FONTF_BOLD ? FIDF_CLASSHEADER : FIDF_CLASSGENERAL);
		wcsncpy(fid.deffontsettings.szFace, lf.lfFaceName, LF_FACESIZE);
		wcsncpy(fid.backgroundGroup, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), _countof(fid.backgroundGroup));
		wcsncpy(fid.group, LPGENW("Message sessions") L"/" LPGENW("Single Messaging"), _countof(fid.group));
		switch (i) {
		case MSGFONTID_MYMSG:
		case 1:
		case MSGFONTID_MYNAME:
		case MSGFONTID_MYTIME:
		case 21:
			wcsncpy(fid.backgroundName, LPGENW("Outgoing background"), _countof(fid.backgroundName));
			break;
		case 8:
		case 9:
		case 12:
		case 13:
			wcsncpy(fid.backgroundName, LPGENW("Outgoing background(old)"), _countof(fid.backgroundName));
			break;
		case 10:
		case 11:
		case 14:
		case 15:
			wcsncpy(fid.backgroundName, LPGENW("Incoming background(old)"), _countof(fid.backgroundName));
			break;
		case MSGFONTID_MESSAGEAREA:
			wcsncpy(fid.group, LPGENW("Message sessions"), _countof(fid.group));
			wcsncpy(fid.backgroundGroup, LPGENW("Message sessions"), _countof(fid.backgroundGroup));
			wcsncpy(fid.backgroundName, LPGENW("Input area background"), _countof(fid.backgroundName));
			break;
		case 17:
			fid.flags |= FIDF_DISABLESTYLES;
			fid.flags &= ~FIDF_ALLOWEFFECTS;
			wcsncpy(fid.backgroundName, LPGENW("Status background"), _countof(fid.backgroundName));
			break;
		case 18:
			wcsncpy(fid.backgroundGroup, LPGENW("Message sessions"), _countof(fid.backgroundGroup));
			wcsncpy(fid.backgroundName, LPGENW("Log background"), _countof(fid.backgroundName));
			break;
		case 19:
			wcsncpy(fid.backgroundName, L"", _countof(fid.backgroundName));
			break;
		default:
			wcsncpy(fid.backgroundName, LPGENW("Incoming background"), _countof(fid.backgroundName));
			break;
		}
		g_plugin.addFont(&fid);
	}

	i = 0;
	fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS;
	wcsncpy(fid.group, LPGENW("Message sessions") L"/" LPGENW("Info Panel"), _countof(fid.group));
	wcsncpy(fid.backgroundGroup, LPGENW("Message sessions") L"/" LPGENW("Info Panel"), _countof(fid.backgroundGroup));
	wcsncpy(fid.backgroundName, LPGENW("Fields background"), _countof(fid.backgroundName));
	for (auto &it : IP_fontOptionsList) {
		LoadMsgDlgFont(FONTSECTION_IP, i, &lf, &it.colour, FONTMODULE);
		mir_snprintf(fid.setting, "Font%d", i+100);
		fid.order = i+100;
		wcsncpy(fid.name, it.szDescr, _countof(fid.name));
		fid.deffontsettings.colour = it.colour;
		fid.deffontsettings.size = (char)lf.lfHeight;
		fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? FONTF_BOLD : 0) | (lf.lfItalic ? FONTF_ITALIC : 0);
		fid.deffontsettings.charset = lf.lfCharSet;
		fid.flags = fid.flags & ~FIDF_CLASSMASK | (fid.deffontsettings.style&FONTF_BOLD ? FIDF_CLASSHEADER : FIDF_CLASSGENERAL);
		fid.deffontsettings.charset = lf.lfCharSet;
		wcsncpy(fid.deffontsettings.szFace, lf.lfFaceName, LF_FACESIZE);
		if (i == IPFONTCOUNT - 1) {
			wcsncpy(fid.backgroundGroup, L"", _countof(fid.backgroundGroup));
			wcsncpy(fid.backgroundName, L"", _countof(fid.backgroundName));
			wcsncpy(fid.group, LPGENW("Message sessions"), _countof(fid.group));
		}
		g_plugin.addFont(&fid);
		i++;
	}

	wcsncpy(cid.group, LPGENW("Message sessions") L"/" LPGENW("Group chats"), _countof(cid.group));
	strncpy(cid.dbSettingsGroup, CHAT_MODULE, _countof(cid.dbSettingsGroup));
	for (i = 0; i <= 7; i++) {
		mir_snprintf(cid.setting, "NickColor%d", i);
		wcsncpy(cid.name, chatcolorsnames[i], _countof(cid.name));
		cid.order = i + 1;
		switch (i) {
		case 5:
			cid.defcolour = GetSysColor(COLOR_HIGHLIGHT);
			break;
		case 6:
			cid.defcolour = GetSysColor(COLOR_HIGHLIGHTTEXT);
			break;
		default:
			cid.defcolour = RGB(0, 0, 0);
			break;
		}
		g_plugin.addColor(&cid);
	}
	cid.order++;
	wcsncpy_s(cid.name, LPGENW("Nick list background"), _TRUNCATE);
	strncpy_s(cid.setting, "ColorNicklistBG", _TRUNCATE);
	cid.defcolour = SRMSGDEFSET_BKGCOLOUR;
	g_plugin.addColor(&cid);

	cid.order++;
	wcsncpy_s(cid.name, LPGENW("Group chat log background"), _TRUNCATE);
	strncpy_s(cid.setting, "ColorLogBG", _TRUNCATE);
	g_plugin.addColor(&cid);

	// static colors (info panel, tool bar background etc...)
	strncpy(fid.dbSettingsGroup, FONTMODULE, _countof(fid.dbSettingsGroup));
	strncpy(cid.dbSettingsGroup, FONTMODULE, _countof(fid.dbSettingsGroup));

	for (auto &it : _clrs) {
		cid.order = it.order;
		wcsncpy(cid.group, it.tszGroup, _countof(fid.group));
		wcsncpy(cid.name, it.tszName, _countof(cid.name));
		strncpy(cid.setting, it.szSetting, _countof(cid.setting));
		if (it.def & 0xff000000)
			cid.defcolour = GetSysColor(it.def & 0x000000ff);
		else
			cid.defcolour = it.def;
		g_plugin.addColor(&cid);
	}

	strncpy(cid.dbSettingsGroup, SRMSGMOD_T, _countof(fid.dbSettingsGroup));

	// text and background colors for tabs
	for (auto &it : _tabclrs) {
		cid.order = it.order;
		wcsncpy(cid.group, it.tszGroup, _countof(fid.group));
		wcsncpy(cid.name, it.tszName, _countof(cid.name));
		strncpy(cid.setting, it.szSetting, _countof(cid.setting));
		if (it.def & 0xff000000)
			cid.defcolour = GetSysColor(it.def & 0x000000ff);
		else
			cid.defcolour = it.def;

		g_plugin.addColor(&cid);
	}
}

int FontServiceFontsChanged(WPARAM, LPARAM)
{
	PluginConfig.reloadSettings();
	CSkin::initAeroEffect();
	CacheMsgLogIcons();
	CacheLogFonts();
	FreeTabConfig();
	ReloadTabConfig();
	Skin->setupAeroSkins();
	Srmm_Broadcast(DM_OPTIONSAPPLIED, 1, 0);
	return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

static INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM)
{
	char szDir[MAX_PATH];
	switch (uMsg) {
	case BFFM_INITIALIZED:
		SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)M.getUserDir());
		break;

	case BFFM_SELCHANGED:
		if (SHGetPathFromIDListA((LPITEMIDLIST)lp, szDir))
			SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);
		break;
	}
	return 0;
}

static HWND CreateToolTip(HWND hwndParent, LPCWSTR ptszText, LPCTSTR ptszTitle)
{
	HWND hwndTT = CreateWindowEx(WS_EX_TOPMOST,
		TOOLTIPS_CLASS, nullptr,
		WS_POPUP | TTS_NOPREFIX,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		hwndParent, nullptr, g_plugin.getInst(), nullptr);

	SetWindowPos(hwndTT, HWND_TOPMOST, 0, 0, 0, 0,
		SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

	TOOLINFO ti = { 0 };
	ti.cbSize = sizeof(TOOLINFO);
	ti.uFlags = TTF_SUBCLASS | TTF_CENTERTIP;
	ti.hwnd = hwndParent;
	ti.hinst = g_plugin.getInst();
	ti.lpszText = (LPWSTR)ptszText;
	GetClientRect(hwndParent, &ti.rect);
	ti.rect.left = -65;

	SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti);
	SendMessage(hwndTT, TTM_SETTITLE, 1, (LPARAM)ptszTitle);
	SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, (LPARAM)640);
	return hwndTT;
}

/////////////////////////////////////////////////////////////////////////////////////////
// base class for chat options

class CChatBaseOptionDlg : public CDlgBase
{

protected:
	CChatBaseOptionDlg(int iDlg) :
		CDlgBase(g_plugin, iDlg)
	{
		m_OnFinishWizard = Callback(this, &CChatBaseOptionDlg::onFinalOptions);
	}

	void onFinalOptions(void*)
	{
		g_chatApi.ReloadSettings();
		g_chatApi.MM_IconsChanged();
		g_chatApi.MM_FontsChanged();
		Chat_UpdateOptions();
		SM_ReconfigureFilters();

		PluginConfig.reloadSettings();
		CacheMsgLogIcons();
		CacheLogFonts();
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// Group chat - Settings

class CChatSettingsDlg : public CChatBaseOptionDlg
{
	HTREEITEM hListHeading1 = nullptr;
	HTREEITEM hListHeading2 = nullptr;

	CCtrlTreeView treeCheck;
	CCtrlEdit edtGroup;

	HTREEITEM InsertBranch(wchar_t* pszDescr, BOOL bExpanded)
	{
		TVINSERTSTRUCT tvis;
		tvis.hParent = nullptr;
		tvis.hInsertAfter = TVI_LAST;
		tvis.item.mask = TVIF_TEXT | TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
		tvis.item.pszText = TranslateW(pszDescr);
		tvis.item.stateMask = TVIS_EXPANDED | TVIS_BOLD;
		tvis.item.state = (bExpanded ? TVIS_EXPANDED : 0) | TVIS_BOLD;
		tvis.item.iImage = tvis.item.iSelectedImage = (bExpanded ? IMG_GRPOPEN : IMG_GRPCLOSED);
		return treeCheck.InsertItem(&tvis);
	}

	void FillBranch(HTREEITEM hParent, branch_t *branch, size_t nValues, DWORD defaultval)
	{
		if (hParent == nullptr)
			return;

		TVINSERTSTRUCT tvis = {};

		for (int i = 0; i < nValues; i++) {
			tvis.hParent = hParent;
			tvis.hInsertAfter = TVI_LAST;
			tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
			tvis.item.pszText = TranslateW(branch[i].szDescr);
			if (branch[i].iMode)
				tvis.item.iImage = tvis.item.iSelectedImage = ((((db_get_dw(0, CHAT_MODULE, branch[i].szDBName, defaultval) & branch[i].iMode) & branch[i].iMode) != 0) ? IMG_CHECK : IMG_NOCHECK);
			else
				tvis.item.iImage = tvis.item.iSelectedImage = ((db_get_dw(0, CHAT_MODULE, branch[i].szDBName, branch[i].bDefault) != 0) ? IMG_CHECK : IMG_NOCHECK);
			branch[i].hItem = treeCheck.InsertItem(&tvis);
		}
	}

	void SaveBranch(branch_t *branch, int nValues)
	{
		TVITEMEX tvi = { 0 };
		BYTE bChecked;
		DWORD iState = 0;

		for (int i = 0; i < nValues; i++) {
			tvi.mask = TVIF_HANDLE | TVIF_IMAGE;
			tvi.hItem = branch[i].hItem;
			treeCheck.GetItem(&tvi);
			bChecked = ((tvi.iImage == IMG_CHECK) ? 1 : 0);
			if (branch[i].iMode) {
				if (bChecked)
					iState |= branch[i].iMode;
				if (iState & GC_EVENT_ADDSTATUS)
					iState |= GC_EVENT_REMOVESTATUS;
				db_set_dw(0, CHAT_MODULE, branch[i].szDBName, iState);
			}
			else db_set_b(0, CHAT_MODULE, branch[i].szDBName, bChecked);
		}
	}

public:
	CChatSettingsDlg() :
		CChatBaseOptionDlg(IDD_OPTIONS1),
		edtGroup(this, IDC_GROUP),
		treeCheck(this, IDC_CHECKBOXES)
	{}

	bool OnInitDialog() override
	{
		SetWindowLongPtr(treeCheck.GetHwnd(), GWL_STYLE, GetWindowLongPtr(treeCheck.GetHwnd(), GWL_STYLE) | (TVS_NOHSCROLL));
		
		// Replace image list
		treeCheck.SetImageList(CreateStateImageList(), TVSIL_NORMAL);

		hListHeading1 = InsertBranch(TranslateT("Appearance and functionality of chat room windows"), TRUE);
		hListHeading2 = InsertBranch(TranslateT("Appearance of the message log"), TRUE);

		FillBranch(hListHeading1, branch1, _countof(branch1), 0x0000);
		FillBranch(hListHeading2, branch2, _countof(branch2), 0x0000);

		edtGroup.SetText(ptrW(Chat_GetGroup()));
		return true;
	}

	bool OnApply() override
	{
		Chat_SetGroup(ptrW(edtGroup.GetText()));

		SaveBranch(branch1, _countof(branch1));
		SaveBranch(branch2, _countof(branch2));
		return true;
	}

	void OnDestroy() override
	{
		BYTE b = treeCheck.GetItemState(hListHeading1, TVIS_EXPANDED) & TVIS_EXPANDED ? 1 : 0;
		db_set_b(0, CHAT_MODULE, "Branch1Exp", b);

		b = treeCheck.GetItemState(hListHeading2, TVIS_EXPANDED) & TVIS_EXPANDED ? 1 : 0;
		db_set_b(0, CHAT_MODULE, "Branch2Exp", b);

		TreeViewDestroy(treeCheck.GetHwnd());
	}

	INT_PTR DlgProc(UINT msg, WPARAM wParam, LPARAM lParam) override
	{
		if (msg == WM_NOTIFY && ((LPNMHDR)lParam)->idFrom == IDC_CHECKBOXES)
			return TreeViewHandleClick(m_hwnd, ((LPNMHDR)lParam)->hwndFrom, wParam, lParam);

		return CDlgBase::DlgProc(msg, wParam, lParam);
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// Group chat - Log

class CChatLogOptionDlg : public CChatBaseOptionDlg
{
	CTimer m_timer;
	CCtrlButton btnBaseDir, btnFontChoose;
	CCtrlCheck chkLogging;
	HWND hPathTip = nullptr;

public:
	CChatLogOptionDlg() :
		CChatBaseOptionDlg(IDD_OPTIONS2),
		m_timer(this, 1),
		btnBaseDir(this, IDC_MUC_OPENLOGBASEDIR),
		chkLogging(this, IDC_LOGGING),
		btnFontChoose(this, IDC_FONTCHOOSE)
	{
		btnBaseDir.OnClick = Callback(this, &CChatLogOptionDlg::onClick_BaseDir);
		btnFontChoose.OnClick = Callback(this, &CChatLogOptionDlg::onClick_FontChoose);

		chkLogging.OnChange = Callback(this, &CChatLogOptionDlg::onChange_Logging);

		m_timer.OnEvent = Callback(this, &CChatLogOptionDlg::onTimer);
	}

	bool OnInitDialog() override
	{
		SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN2, UDM_SETRANGE, 0, MAKELONG(5000, 0));
		SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN2, UDM_SETPOS, 0, MAKELONG(db_get_w(0, CHAT_MODULE, "LogLimit", 100), 0));
		SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN3, UDM_SETRANGE, 0, MAKELONG(255, 10));
		SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN3, UDM_SETPOS, 0, MAKELONG(db_get_b(0, CHAT_MODULE, "NicklistRowDist", 12), 0));
		SetDlgItemText(m_hwnd, IDC_LOGTIMESTAMP, g_Settings.pszTimeStampLog);
		SetDlgItemText(m_hwnd, IDC_TIMESTAMP, g_Settings.pszTimeStamp);
		SetDlgItemText(m_hwnd, IDC_OUTSTAMP, g_Settings.pszOutgoingNick);
		SetDlgItemText(m_hwnd, IDC_INSTAMP, g_Settings.pszIncomingNick);
		CheckDlgButton(m_hwnd, IDC_LOGGING, g_Settings.bLoggingEnabled ? BST_CHECKED : BST_UNCHECKED);
		SetDlgItemText(m_hwnd, IDC_LOGDIRECTORY, g_Settings.pszLogDir);
		Utils::enableDlgControl(m_hwnd, IDC_LOGDIRECTORY, g_Settings.bLoggingEnabled);
		Utils::enableDlgControl(m_hwnd, IDC_FONTCHOOSE, g_Settings.bLoggingEnabled);
		SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN4, UDM_SETRANGE, 0, MAKELONG(10000, 0));
		SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN4, UDM_SETPOS, 0, MAKELONG(db_get_w(0, CHAT_MODULE, "LoggingLimit", 100), 0));
		Utils::enableDlgControl(m_hwnd, IDC_LIMIT, g_Settings.bLoggingEnabled);

		CMStringW tszTooltipText(FORMAT,
			L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n\n"
			L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n\n"
			L"%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s\n%s - %s",
			// contact vars
			L"%nick%", TranslateT("nick of current contact (if defined)"),
			L"%proto%", TranslateT("protocol name of current contact (if defined). Account name is used when protocol supports multiple accounts"),
			L"%accountname%", TranslateT("user-defined account name of current contact (if defined)."),
			L"%userid%", TranslateT("user ID of current contact (if defined). It is like UIN for ICQ, JID for Jabber, etc."),
			// global vars
			L"%miranda_path%", TranslateT("path to Miranda root folder"),
			L"%miranda_profilesdir%", TranslateT("path to folder containing Miranda profiles"),
			L"%miranda_profilename%", TranslateT("name of current Miranda profile (filename, without extension)"),
			L"%miranda_userdata%", TranslateT("will return parsed string %miranda_profilesdir%\\%miranda_profilename%"),
			L"%miranda_logpath%", TranslateT("will return parsed string %miranda_userdata%\\Logs"),
			L"%appdata%", TranslateT("same as environment variable %APPDATA% for currently logged-on Windows user"),
			L"%username%", TranslateT("username for currently logged-on Windows user"),
			L"%mydocuments%", TranslateT("\"My Documents\" folder for currently logged-on Windows user"),
			L"%desktop%", TranslateT("\"Desktop\" folder for currently logged-on Windows user"),
			L"%xxxxxxx%", TranslateT("any environment variable defined in current Windows session (like %systemroot%, %allusersprofile%, etc.)"),
			// date/time vars
			L"%d%", TranslateT("day of month, 1-31"),
			L"%dd%", TranslateT("day of month, 01-31"),
			L"%m%", TranslateT("month number, 1-12"),
			L"%mm%", TranslateT("month number, 01-12"),
			L"%mon%", TranslateT("abbreviated month name"),
			L"%month%", TranslateT("full month name"),
			L"%yy%", TranslateT("year without century, 01-99"),
			L"%yyyy%", TranslateT("year with century, 1901-9999"),
			L"%wday%", TranslateT("abbreviated weekday name"),
			L"%weekday%", TranslateT("full weekday name"));

		hPathTip = CreateToolTip(GetDlgItem(m_hwnd, IDC_LOGDIRECTORY), tszTooltipText, TranslateT("Variables"));
		if (hPathTip)
			m_timer.Start(3000);
		return true;
	}

	bool OnApply() override
	{
		char *pszText = nullptr;

		int iLen = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_LOGDIRECTORY));
		if (iLen > 0) {
			wchar_t *pszText1 = (wchar_t*)mir_alloc(iLen * sizeof(wchar_t) + 2);
			GetDlgItemText(m_hwnd, IDC_LOGDIRECTORY, pszText1, iLen + 1);
			db_set_ws(0, CHAT_MODULE, "LogDirectory", pszText1);
			mir_free(pszText1);
			g_Settings.bLoggingEnabled = IsDlgButtonChecked(m_hwnd, IDC_LOGGING) == BST_CHECKED;
			db_set_b(0, CHAT_MODULE, "LoggingEnabled", g_Settings.bLoggingEnabled);
		}
		else {
			db_unset(0, CHAT_MODULE, "LogDirectory");
			db_set_b(0, CHAT_MODULE, "LoggingEnabled", 0);
		}
		g_chatApi.SM_InvalidateLogDirectories();

		iLen = SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN4, UDM_GETPOS, 0, 0);
		db_set_w(0, CHAT_MODULE, "LoggingLimit", (WORD)iLen);

		iLen = SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN3, UDM_GETPOS, 0, 0);
		if (iLen > 0)
			db_set_b(0, CHAT_MODULE, "NicklistRowDist", (BYTE)iLen);
		else
			db_unset(0, CHAT_MODULE, "NicklistRowDist");

		iLen = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_LOGTIMESTAMP));
		if (iLen > 0) {
			pszText = (char *)mir_realloc(pszText, iLen + 1);
			GetDlgItemTextA(m_hwnd, IDC_LOGTIMESTAMP, pszText, iLen + 1);
			db_set_s(0, CHAT_MODULE, "LogTimestamp", pszText);
		}
		else db_unset(0, CHAT_MODULE, "LogTimestamp");

		iLen = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_TIMESTAMP));
		if (iLen > 0) {
			pszText = (char *)mir_realloc(pszText, iLen + 1);
			GetDlgItemTextA(m_hwnd, IDC_TIMESTAMP, pszText, iLen + 1);
			db_set_s(0, CHAT_MODULE, "HeaderTime", pszText);
		}
		else db_unset(0, CHAT_MODULE, "HeaderTime");

		iLen = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_INSTAMP));
		if (iLen > 0) {
			pszText = (char *)mir_realloc(pszText, iLen + 1);
			GetDlgItemTextA(m_hwnd, IDC_INSTAMP, pszText, iLen + 1);
			db_set_s(0, CHAT_MODULE, "HeaderIncoming", pszText);
		}
		else db_unset(0, CHAT_MODULE, "HeaderIncoming");

		iLen = GetWindowTextLength(GetDlgItem(m_hwnd, IDC_OUTSTAMP));
		if (iLen > 0) {
			pszText = (char *)mir_realloc(pszText, iLen + 1);
			GetDlgItemTextA(m_hwnd, IDC_OUTSTAMP, pszText, iLen + 1);
			db_set_s(0, CHAT_MODULE, "HeaderOutgoing", pszText);
		}
		else db_unset(0, CHAT_MODULE, "HeaderOutgoing");

		iLen = SendDlgItemMessage(m_hwnd, IDC_CHAT_SPIN2, UDM_GETPOS, 0, 0);
		db_set_w(0, CHAT_MODULE, "LogLimit", (WORD)iLen);
		mir_free(pszText);

		if (g_chatApi.hListBkgBrush)
			DeleteObject(g_chatApi.hListBkgBrush);
		g_chatApi.hListBkgBrush = CreateSolidBrush(db_get_dw(0, CHAT_MODULE, "ColorNicklistBG", SRMSGDEFSET_BKGCOLOUR));
		return true;
	}

	void OnDestroy() override
	{
		if (hPathTip) {
			KillTimer(m_hwnd, 0);
			DestroyWindow(hPathTip);
			hPathTip = nullptr;
		}
	}

	// open the base directory for MUC logs, using a standard file selector
	// dialog. Simply allows the user to view what log files are there
	// and possibly delete archived logs.

	void onClick_BaseDir(CCtrlButton*)
	{
		wchar_t	tszTemp[MAX_PATH + 20];
		wcsncpy_s(tszTemp, g_Settings.pszLogDir, _TRUNCATE);

		wchar_t *p = tszTemp;
		while (*p && (*p == '\\' || *p == '.'))
			p++;

		if (*p)
			if (wchar_t *p1 = wcschr(p, '\\'))
				*p1 = 0;

		wchar_t tszInitialDir[_MAX_DRIVE + _MAX_PATH + 10];
		mir_snwprintf(tszInitialDir, L"%s%s", M.getChatLogPath(), p);
		if (!PathFileExists(tszInitialDir))
			wcsncpy_s(tszInitialDir, M.getChatLogPath(), _TRUNCATE);

		wchar_t	tszReturnName[MAX_PATH]; tszReturnName[0] = 0;
		mir_snwprintf(tszTemp, L"%s%c*.*%c%c", TranslateT("All files"), 0, 0, 0);

		OPENFILENAME ofn = { 0 };
		ofn.lpstrInitialDir = tszInitialDir;
		ofn.lpstrFilter = tszTemp;
		ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
		ofn.lpstrFile = tszReturnName;
		ofn.nMaxFile = MAX_PATH;
		ofn.nMaxFileTitle = MAX_PATH;
		ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
		ofn.lpstrDefExt = L"log";
		GetOpenFileName(&ofn);
	}

	void onClick_FontChoose(CCtrlButton*)
	{
		wchar_t tszDirectory[MAX_PATH];

		LPMALLOC psMalloc;
		if (FAILED(CoGetMalloc(1, &psMalloc)))
			return;

		BROWSEINFO bi = {};
		bi.hwndOwner = m_hwnd;
		bi.pszDisplayName = tszDirectory;
		bi.lpszTitle = TranslateT("Select folder");
		bi.ulFlags = BIF_NEWDIALOGSTYLE | BIF_EDITBOX | BIF_RETURNONLYFSDIRS;
		bi.lpfn = BrowseCallbackProc;
		bi.lParam = (LPARAM)tszDirectory;

		LPITEMIDLIST idList = SHBrowseForFolder(&bi);
		if (idList) {
			const wchar_t *szUserDir = M.getUserDir();
			SHGetPathFromIDList(idList, tszDirectory);
			mir_wstrcat(tszDirectory, L"\\");

			wchar_t tszTemp[MAX_PATH];
			PathToRelativeW(tszDirectory, tszTemp, szUserDir);
			SetDlgItemText(m_hwnd, IDC_LOGDIRECTORY, mir_wstrlen(tszTemp) > 1 ? tszTemp : DEFLOGFILENAME);
		}
		psMalloc->Free(idList);
		psMalloc->Release();
	}

	void onChange_Logging(CCtrlCheck*)
	{
		bool bChecked = chkLogging.GetState();
		Utils::enableDlgControl(m_hwnd, IDC_LOGDIRECTORY, bChecked);
		Utils::enableDlgControl(m_hwnd, IDC_FONTCHOOSE, bChecked);
		Utils::enableDlgControl(m_hwnd, IDC_LIMIT, bChecked);
	}

	void onTimer(CTimer*)
	{
		if (IsWindow(hPathTip))
			KillTimer(hPathTip, 4); // It will prevent tooltip autoclosing
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// Group chat - Events

#define NR_GC_EVENTS 12

static UINT _eventorder[] =
{
	GC_EVENT_ACTION,
	GC_EVENT_MESSAGE,
	GC_EVENT_NICK,
	GC_EVENT_JOIN,
	GC_EVENT_PART,
	GC_EVENT_TOPIC,
	GC_EVENT_ADDSTATUS,
	GC_EVENT_INFORMATION,
	GC_EVENT_QUIT,
	GC_EVENT_KICK,
	GC_EVENT_NOTICE,
	GC_EVENT_HIGHLIGHT
};

class CChatEventOptionDlg : public CChatBaseOptionDlg
{

public:
	CChatEventOptionDlg() :
		CChatBaseOptionDlg(IDD_OPTIONS3)
	{}

	bool OnInitDialog() override
	{
		DWORD dwFilterFlags = db_get_dw(0, CHAT_MODULE, "FilterFlags", GC_EVENT_ALL);
		DWORD dwTrayFlags = db_get_dw(0, CHAT_MODULE, "TrayIconFlags", GC_EVENT_HIGHLIGHT);
		DWORD dwPopupFlags = db_get_dw(0, CHAT_MODULE, "PopupFlags", GC_EVENT_HIGHLIGHT);
		DWORD dwLogFlags = db_get_dw(0, CHAT_MODULE, "DiskLogFlags", GC_EVENT_ALL);

		for (int i = 0; i < _countof(_eventorder); i++) {
			if (_eventorder[i] != GC_EVENT_HIGHLIGHT) {
				CheckDlgButton(m_hwnd, IDC_1 + i, dwFilterFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
				CheckDlgButton(m_hwnd, IDC_L1 + i, dwLogFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
			}
			CheckDlgButton(m_hwnd, IDC_P1 + i, dwPopupFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
			CheckDlgButton(m_hwnd, IDC_T1 + i, dwTrayFlags & _eventorder[i] ? BST_CHECKED : BST_UNCHECKED);
		}

		SendDlgItemMessage(m_hwnd, IDC_LOGICONTYPE, CB_ADDSTRING, -1, (LPARAM)TranslateT("No markers"));
		SendDlgItemMessage(m_hwnd, IDC_LOGICONTYPE, CB_ADDSTRING, -1, (LPARAM)TranslateT("Show as icons"));
		SendDlgItemMessage(m_hwnd, IDC_LOGICONTYPE, CB_ADDSTRING, -1, (LPARAM)TranslateT("Show as text symbols"));

		SendDlgItemMessage(m_hwnd, IDC_LOGICONTYPE, CB_SETCURSEL, (g_Settings.bLogSymbols ? 2 : (g_Settings.dwIconFlags ? 1 : 0)), 0);

		CheckDlgButton(m_hwnd, IDC_TRAYONLYFORINACTIVE, db_get_b(0, CHAT_MODULE, "TrayIconInactiveOnly", 0) ? BST_CHECKED : BST_UNCHECKED);
		return true;
	}

	bool OnApply() override
	{
		DWORD dwFilterFlags = 0, dwTrayFlags = 0,
			dwPopupFlags = 0, dwLogFlags = 0;

		for (int i = 0; i < _countof(_eventorder); i++) {
			if (_eventorder[i] != GC_EVENT_HIGHLIGHT) {
				dwFilterFlags |= (IsDlgButtonChecked(m_hwnd, IDC_1 + i) ? _eventorder[i] : 0);
				dwLogFlags |= (IsDlgButtonChecked(m_hwnd, IDC_L1 + i) ? _eventorder[i] : 0);
			}
			dwPopupFlags |= (IsDlgButtonChecked(m_hwnd, IDC_P1 + i) ? _eventorder[i] : 0);
			dwTrayFlags |= (IsDlgButtonChecked(m_hwnd, IDC_T1 + i) ? _eventorder[i] : 0);
		}
		db_set_dw(0, CHAT_MODULE, "FilterFlags", dwFilterFlags);
		db_set_dw(0, CHAT_MODULE, "PopupFlags", dwPopupFlags);
		db_set_dw(0, CHAT_MODULE, "TrayIconFlags", dwTrayFlags);
		db_set_dw(0, CHAT_MODULE, "DiskLogFlags", dwLogFlags);

		LRESULT lr = SendDlgItemMessage(m_hwnd, IDC_LOGICONTYPE, CB_GETCURSEL, 0, 0);

		db_set_dw(0, CHAT_MODULE, "IconFlags", lr == 1 ? 1 : 0);
		db_set_b(0, CHAT_MODULE, "LogSymbols", lr == 2 ? 1 : 0);

		db_set_b(0, CHAT_MODULE, "TrayIconInactiveOnly", IsDlgButtonChecked(m_hwnd, IDC_TRAYONLYFORINACTIVE) ? 1 : 0);
		return true;
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// Group chat - Highlight

class CHighlighOptionDlg : public CChatBaseOptionDlg
{
	CCtrlEdit edtNick, edtText;
	CCtrlCheck chkNick, chkText;

public:
	CHighlighOptionDlg() :
		CChatBaseOptionDlg(IDD_OPTIONS4),
		edtNick(this, IDC_HIGHLIGHTNICKPATTERN),
		edtText(this, IDC_HIGHLIGHTTEXTPATTERN),
		chkNick(this, IDC_HIGHLIGHTNICKENABLE),
		chkText(this, IDC_HIGHLIGHTTEXTENABLE)
	{
		chkNick.OnChange = Callback(this, &CHighlighOptionDlg::onChange_Nick);
		chkText.OnChange = Callback(this, &CHighlighOptionDlg::onChange_Text);
	}

	bool OnInitDialog() override
	{
		ptrW wszText(db_get_wsa(0, CHAT_MODULE, "HighlightWords"));
		if (wszText)
			edtText.SetText(wszText);

		wszText = db_get_wsa(0, CHAT_MODULE, "HighlightNames");
		if (wszText)
			edtNick.SetText(wszText);

		DWORD dwFlags = db_get_b(0, CHAT_MODULE, "HighlightEnabled", CMUCHighlight::MATCH_TEXT);

		chkNick.SetState(dwFlags & CMUCHighlight::MATCH_NICKNAME); onChange_Nick(0);
		chkText.SetState(dwFlags & CMUCHighlight::MATCH_TEXT); onChange_Text(0);
		
		::CheckDlgButton(m_hwnd, IDC_HIGHLIGHTNICKUID, dwFlags & CMUCHighlight::MATCH_UIN ? BST_CHECKED : BST_UNCHECKED);
		::CheckDlgButton(m_hwnd, IDC_HIGHLIGHTME, db_get_b(0, CHAT_MODULE, "HighlightMe", 1) ? BST_CHECKED : BST_UNCHECKED);
		return true;
	}

	void onChange_Text(CCtrlCheck*) 
	{
		bool bChecked = chkText.GetState();
		edtText.Enable(bChecked);
		Utils::enableDlgControl(m_hwnd, IDC_HIGHLIGHTME, bChecked);
	}

	void onChange_Nick(CCtrlCheck*)
	{
		bool bChecked = chkNick.GetState();
		edtNick.Enable(bChecked);
		Utils::enableDlgControl(m_hwnd, IDC_HIGHLIGHTNICKUID, chkNick.GetState());
	}

	bool OnApply() override
	{
		db_set_ws(0, CHAT_MODULE, "HighlightNames", ptrW(edtNick.GetText()));
		db_set_ws(0, CHAT_MODULE, "HighlightWords", ptrW(edtText.GetText()));

		BYTE dwFlags = (chkNick.GetState() ? CMUCHighlight::MATCH_NICKNAME : 0) | (chkText.GetState() ? CMUCHighlight::MATCH_TEXT : 0);
		if (dwFlags & CMUCHighlight::MATCH_NICKNAME)
			dwFlags |= (::IsDlgButtonChecked(m_hwnd, IDC_HIGHLIGHTNICKUID) ? CMUCHighlight::MATCH_UIN : 0);

		db_set_b(0, CHAT_MODULE, "HighlightEnabled", dwFlags);
		db_set_b(0, CHAT_MODULE, "HighlightMe", ::IsDlgButtonChecked(m_hwnd, IDC_HIGHLIGHTME) ? 1 : 0);
		g_Settings.Highlight->init();
		return true;
	}
};

/////////////////////////////////////////////////////////////////////////////////////////

void Chat_Options(WPARAM wParam)
{
	OPTIONSDIALOGPAGE odp = {};
	odp.flags = ODPF_BOLDGROUPS;
	odp.position = 910000000;
	odp.szGroup.a = LPGEN("Message sessions");
	odp.szTitle.a = LPGEN("Group chats");
	
	odp.szTab.a = LPGEN("Settings");
	odp.pDialog = new CChatSettingsDlg();
	g_plugin.addOptions(wParam, &odp);

	odp.szTab.a = LPGEN("Log formatting");
	odp.pDialog = new CChatLogOptionDlg();
	g_plugin.addOptions(wParam, &odp);

	odp.szTab.a = LPGEN("Events and filters");
	odp.pDialog = new CChatEventOptionDlg();
	g_plugin.addOptions(wParam, &odp);

	odp.szTab.a = LPGEN("Highlighting");
	odp.pDialog = new CHighlighOptionDlg();
	g_plugin.addOptions(wParam, &odp);
}