/*

Miranda NG: the free IM client for Microsoft* Windows*

Copyright (c) 2012-18 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-03 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.
*/

#include "stdafx.h"
#include <m_findadd.h>
#include "cluiframes.h"
#include "coolscroll.h"

#define TM_AUTOALPHA  1
#define TIMERID_AUTOSIZE 100
#define MENU_MIRANDAMENU 0xFFFF1234

int g_fading_active = 0;

static RECT g_PreSizeRect, g_SizingRect;
static int g_sizingmethod;
static LONG g_CLUI_x_off, g_CLUI_y_off, g_CLUI_y1_off, g_CLUI_x1_off;
static RECT rcWPC;

static int transparentFocus = 1;
static byte oldhideoffline;
static int disableautoupd = 1;
HANDLE hFrameContactTree;
extern RECT old_window_rect, new_window_rect;

extern BOOL g_trayTooltipActive;
extern POINT tray_hover_pos;
extern HWND g_hwndViewModeFrame, g_hwndEventArea, g_hwndToolbarFrame;

extern ImageItem *g_CLUIImageItem;
extern HBRUSH g_CLUISkinnedBkColor;
extern HWND g_hwndSFL;
extern ButtonItem *g_ButtonItems;
extern COLORREF g_CLUISkinnedBkColorRGB;
extern FRAMEWND *wndFrameCLC;
extern HPEN g_hPenCLUIFrames;

static BYTE old_cliststate, show_on_first_autosize = FALSE;

RECT cluiPos;

wchar_t *statusNames[12];

extern LRESULT CALLBACK EventAreaWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
extern HANDLE hNotifyFrame;

int LoadCluiServices(void);
void MF_InitCheck(void);
void InitGroupMenus();
void FS_RegisterFonts();
void LoadExtraIconModule();
void RemoveFromTaskBar(HWND hWnd);

extern LONG g_cxsmIcon, g_cysmIcon;

SIZE g_oldSize = { 0 };
POINT g_oldPos = { 0 };
int during_sizing = 0;
extern int dock_prevent_moving;

static HDC hdcLockedPoint = nullptr;
static HBITMAP hbmLockedPoint = nullptr, hbmOldLockedPoint = nullptr;

HICON overlayicons[10];

static IconItem myIcons[] = {
	{ LPGEN("Toggle show online/offline"), "CLN_online", IDI_HIDEOFFLINE },
	{ LPGEN("Toggle groups"), "CLN_groups", IDI_HIDEGROUPS },
	{ LPGEN("Find contacts"), "CLN_findadd", IDI_FINDANDADD },
	{ LPGEN("Open preferences"), "CLN_options", IDI_TBOPTIONS },
	{ LPGEN("Toggle sounds"), "CLN_sound", IDI_SOUNDSON },
	{ LPGEN("Minimize contact list"), "CLN_minimize", IDI_MINIMIZE },
	{ LPGEN("Show TabSRMM session list"), "CLN_slist", IDI_TABSRMMSESSIONLIST },
	{ LPGEN("Show TabSRMM menu"), "CLN_menu", IDI_TABSRMMMENU },
	{ LPGEN("Sounds are off"), "CLN_soundsoff", IDI_SOUNDSOFF },
	{ LPGEN("Select view mode"), "CLN_CLVM_select", IDI_CLVM_SELECT },
	{ LPGEN("Reset view mode"), "CLN_CLVM_reset", IDI_DELETE },
	{ LPGEN("Configure view modes"), "CLN_CLVM_options", IDI_CLVM_OPTIONS },
	{ LPGEN("Show menu"), "CLN_topmenu", IDI_TBTOPMENU },
	{ LPGEN("Setup accounts"), "CLN_accounts", IDI_TBACCOUNTS }
};

HWND hTbMenu, hTbGlobalStatus;

static void Tweak_It(COLORREF clr)
{
	SetWindowLongPtr(pcli->hwndContactList, GWL_EXSTYLE, GetWindowLongPtr(pcli->hwndContactList, GWL_EXSTYLE) | WS_EX_LAYERED);
	SetLayeredWindowAttributes(pcli->hwndContactList, clr, 0, LWA_COLORKEY);
	cfg::dat.colorkey = clr;
}

static void LayoutButtons(HWND hwnd, RECT *rc)
{
	RECT rect;
	BYTE left_offset = cfg::dat.bCLeft - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
	BYTE right_offset = cfg::dat.bCRight - (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN ? 3 : 0);
	BYTE delta = left_offset + right_offset;
	ButtonItem *btnItems = g_ButtonItems;

	if (rc == nullptr)
		GetClientRect(hwnd, &rect);
	else
		rect = *rc;

	rect.bottom -= cfg::dat.bCBottom;

	if (g_ButtonItems) {
		while (btnItems) {
			LONG x = (btnItems->xOff >= 0) ? rect.left + btnItems->xOff : rect.right - abs(btnItems->xOff);
			LONG y = (btnItems->yOff >= 0) ? rect.top + btnItems->yOff : rect.bottom - cfg::dat.statusBarHeight;

			SetWindowPos(btnItems->hWnd, nullptr, x, y, btnItems->width, btnItems->height, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);
			btnItems = btnItems->nextItem;
		}
	}

	SetWindowPos(hTbMenu, nullptr, 2 + left_offset, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
		21 * 3, 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);

	SetWindowPos(hTbGlobalStatus, nullptr, left_offset + (3 * 21) + 3, rect.bottom - cfg::dat.statusBarHeight - 21 - 1,
		rect.right - delta - (3 * 21 + 5), 21 + 1, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW);

}

static int FS_FontsChanged(WPARAM, LPARAM)
{
	COLORREF clr_cluiframes = db_get_dw(NULL, "CLUI", "clr_frameborder", RGB(40, 40, 40));

	if (g_hPenCLUIFrames)
		DeleteObject(g_hPenCLUIFrames);
	g_hPenCLUIFrames = CreatePen(PS_SOLID, 1, clr_cluiframes);

	pcli->pfnClcOptionsChanged();
	RedrawWindow(pcli->hwndContactList, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
	return 0;
}

// create the CLC control, but not yet the frame. The frame containing the CLC should be created as the
// last frame of all.
static HWND PreCreateCLC(HWND parent)
{
	pcli->hwndContactTree = CreateWindow(CLISTCONTROL_CLASSW, L"",
		WS_CHILD | CLS_CONTACTLIST
		| (db_get_b(NULL, "CList", "UseGroups", SETTING_USEGROUPS_DEFAULT) ? CLS_USEGROUPS : 0)
		| (db_get_b(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT) ? CLS_HIDEOFFLINE : 0)
		| (db_get_b(NULL, "CList", "HideEmptyGroups", SETTING_HIDEEMPTYGROUPS_DEFAULT) ? CLS_HIDEEMPTYGROUPS : 0)
		| CLS_MULTICOLUMN,
		0, 0, 0, 0, parent, nullptr, g_hInst, (LPVOID)0xff00ff00);

	cfg::clcdat = (struct ClcData *)GetWindowLongPtr(pcli->hwndContactTree, 0);
	return pcli->hwndContactTree;
}

// create internal frames, including the last frame (actual CLC control)
static int CreateCLC()
{
	ExtraIcon_Reload();
	pcli->pfnSetHideOffline(oldhideoffline);
	disableautoupd = 0;
	{
		CLISTFrame frame = { 0 };
		frame.cbSize = sizeof(frame);
		frame.tname = L"EventArea";
		frame.TBtname = TranslateT("Event area");
		frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
		frame.height = 20;
		frame.Flags = F_VISIBLE | F_SHOWTBTIP | F_NOBORDER | F_UNICODE;
		frame.align = alBottom;
		frame.hWnd = CreateWindowExA(0, "EventAreaClass", "evt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, pcli->hwndContactList, (HMENU)nullptr, g_hInst, nullptr);
		g_hwndEventArea = frame.hWnd;
		hNotifyFrame = (HWND)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)&frame, 0);
		CallService(MS_CLIST_FRAMES_UPDATEFRAME, (WPARAM)hNotifyFrame, FU_FMPOS);
		HideShowNotifyFrame();
		CreateViewModeFrame();
	}
	{
		CLISTFrame Frame = { 0 };
		Frame.cbSize = sizeof(CLISTFrame);
		Frame.hWnd = pcli->hwndContactTree;
		Frame.align = alClient;
		Frame.hIcon = Skin_LoadIcon(SKINICON_OTHER_FRAME);
		Frame.Flags = F_VISIBLE | F_SHOWTB | F_SHOWTBTIP | F_NOBORDER | F_UNICODE;
		Frame.tname = L"My contacts";
		Frame.TBtname = TranslateT("My contacts");
		Frame.height = 200;
		hFrameContactTree = (HWND)CallService(MS_CLIST_FRAMES_ADDFRAME, (WPARAM)&Frame, 0);
		CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_TBTIPNAME | FO_UNICODETEXT, hFrameContactTree), (LPARAM)TranslateT("My contacts"));

		// ugly, but working hack. Prevent that annoying little scroll bar from appearing in the "My Contacts" title bar
		DWORD flags = (DWORD)CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), 0);
		flags |= F_VISIBLE;
		CallService(MS_CLIST_FRAMES_SETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hFrameContactTree), flags);
	}

	SetButtonToSkinned();
	return 0;
}

static int CluiModulesLoaded(WPARAM, LPARAM)
{
	FS_RegisterFonts();
	HookEvent(ME_FONT_RELOAD, FS_FontsChanged);
	return 0;
}

static HICON hIconSaved = nullptr;

void ClearIcons(int mode)
{
	for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_OUTTOLUNCH; i++) {
		if (overlayicons[i - IDI_OVL_OFFLINE] != nullptr) {
			if (mode)
				DestroyIcon(overlayicons[i - IDI_OVL_OFFLINE]);
			overlayicons[i - IDI_OVL_OFFLINE] = nullptr;
		}
	}
}

static void CacheClientIcons()
{
	ClearIcons(0);

	for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_OUTTOLUNCH; i++) {
		char szBuffer[128];
		mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
		overlayicons[i - IDI_OVL_OFFLINE] = IcoLib_GetIcon(szBuffer);
	}
}

static void InitIcoLib()
{
	Icon_Register(g_hInst, LPGEN("Contact list") "/" LPGEN("Default"), myIcons, _countof(myIcons));

	for (int i = IDI_OVL_OFFLINE; i <= IDI_OVL_OUTTOLUNCH; i++) {
		char szBuffer[128];
		mir_snprintf(szBuffer, "cln_ovl_%d", ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE));
		IconItemT icon = { pcli->pfnGetStatusModeDescription(ID_STATUS_OFFLINE + (i - IDI_OVL_OFFLINE), 0), szBuffer, i };
		Icon_RegisterT(g_hInst, LPGENW("Contact list") L"/" LPGENW("Overlay icons"), &icon, 1);
	}

	for (auto &pa : Accounts()) {
		if (!pa->IsEnabled() || CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_2, 0) == 0)
			continue;

		wchar_t szDescr[128];
		mir_snwprintf(szDescr, TranslateT("%s connecting"), pa->tszAccountName);
		IconItemT icon = { szDescr, "conn", IDI_PROTOCONNECTING };
		Icon_RegisterT(g_hInst, LPGENW("Contact list") L"/" LPGENW("Connecting icons"), &icon, 1, pa->szModuleName);
	}
}

static int IcoLibChanged(WPARAM, LPARAM)
{
	IcoLibReloadIcons();
	return 0;
}

void CreateButtonBar(HWND hWnd)
{
	hTbMenu = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBMENU, g_hInst, nullptr);
	CustomizeButton(hTbMenu, false, false, false);
	SetWindowText(hTbMenu, TranslateT("Menu"));
	SendMessage(hTbMenu, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_OTHER_MAINMENU));
	SendMessage(hTbMenu, BUTTONSETSENDONDOWN, TRUE, 0);
	SendMessage(hTbMenu, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Open main menu"), 0);

	hTbGlobalStatus = CreateWindowEx(0, MIRANDABUTTONCLASS, L"", BS_PUSHBUTTON | WS_CHILD | WS_TABSTOP, 0, 0, 20, 20, hWnd, (HMENU)IDC_TBGLOBALSTATUS, g_hInst, nullptr);
	CustomizeButton(hTbGlobalStatus, false, false, false);
	SetWindowText(hTbGlobalStatus, TranslateT("Offline"));
	SendMessage(hTbGlobalStatus, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_LoadIcon(SKINICON_STATUS_OFFLINE));
	SendMessage(hTbGlobalStatus, BUTTONSETSENDONDOWN, TRUE, 0);
	SendMessage(hTbGlobalStatus, BUTTONADDTOOLTIP, (WPARAM)LPGEN("Set status modes"), 0);
}

// if mode != 0 we do first time init, otherwise only reload the extra icon stuff
void CLN_LoadAllIcons(BOOL mode)
{
	if (mode) {
		InitIcoLib();
		HookEvent(ME_SKIN2_ICONSCHANGED, IcoLibChanged);
	}
	CacheClientIcons();
}

void ConfigureEventArea()
{
	int iCount = GetMenuItemCount(cfg::dat.hMenuNotify);
	DWORD dwFlags = cfg::dat.dwFlags;
	int oldstate = cfg::dat.notifyActive;
	int dwVisible = CallService(MS_CLIST_FRAMES_GETFRAMEOPTIONS, MAKEWPARAM(FO_FLAGS, hNotifyFrame), 0) & F_VISIBLE;

	if (dwVisible) {
		if (dwFlags & CLUI_FRAME_AUTOHIDENOTIFY)
			cfg::dat.notifyActive = iCount > 0 ? 1 : 0;
		else
			cfg::dat.notifyActive = 1;
	}
	else
		cfg::dat.notifyActive = 0;

	if (oldstate != cfg::dat.notifyActive)
		HideShowNotifyFrame();
}

void ConfigureFrame()
{
	int show = cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? SW_SHOW : SW_HIDE;
	ShowWindow(hTbMenu, show);
	ShowWindow(hTbGlobalStatus, show);
}

void IcoLibReloadIcons()
{
	CacheClientIcons();
	ExtraIcon_Reload();
	ExtraIcon_SetAll();

	Clist_Broadcast(CLM_AUTOREBUILD, 0, 0);
	SendMessage(g_hwndViewModeFrame, WM_USER + 100, 0, 0);
}

void ConfigureCLUIGeometry(int mode)
{
	RECT rcStatus;
	DWORD clmargins = db_get_dw(NULL, "CLUI", "clmargins", 0);

	cfg::dat.bCLeft = LOBYTE(LOWORD(clmargins));
	cfg::dat.bCRight = HIBYTE(LOWORD(clmargins));
	cfg::dat.bCTop = LOBYTE(HIWORD(clmargins));
	cfg::dat.bCBottom = HIBYTE(HIWORD(clmargins));

	if (mode) {
		if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
			SendMessage(pcli->hwndStatus, WM_SIZE, 0, 0);
			GetWindowRect(pcli->hwndStatus, &rcStatus);
			cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
		}
		else cfg::dat.statusBarHeight = 0;
	}

	cfg::dat.topOffset = cfg::dat.bCTop;
	cfg::dat.bottomOffset = (cfg::dat.dwFlags & CLUI_FRAME_SHOWBOTTOMBUTTONS ? 2 + 21 : 0) + cfg::dat.bCBottom;

	if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
		cfg::dat.topOffset += 2;
		cfg::dat.bottomOffset += 2;
		cfg::dat.bCLeft += 3;
		cfg::dat.bCRight += 3;
	}
}

// set the states of defined database action buttons (only if button is a toggle)
void SetDBButtonStates(MCONTACT hPassedContact)
{
	ButtonItem *buttonItem = g_ButtonItems;
	MCONTACT hContact = 0, hFinalContact = 0;
	char *szModule, *szSetting;
	ClcContact *contact = nullptr;

	if (cfg::clcdat && hPassedContact == 0) {
		pcli->pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
		if (contact && contact->type == CLCIT_CONTACT) {
			hContact = contact->hContact;
		}
	}

	while (buttonItem) {
		BOOL result = FALSE;

		if (!(buttonItem->dwFlags & BUTTON_ISTOGGLE && buttonItem->dwFlags & BUTTON_ISDBACTION)) {
			buttonItem = buttonItem->nextItem;
			continue;
		}
		szModule = buttonItem->szModule;
		szSetting = buttonItem->szSetting;
		if (buttonItem->dwFlags & BUTTON_DBACTIONONCONTACT || buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION) {
			if (hContact == 0) {
				SendMessage(buttonItem->hWnd, BM_SETCHECK, BST_UNCHECKED, 0);
				buttonItem = buttonItem->nextItem;
				continue;
			}
			if (buttonItem->dwFlags & BUTTON_ISCONTACTDBACTION)
				szModule = GetContactProto(hContact);
			hFinalContact = hContact;
		}
		else
			hFinalContact = 0;

		if (buttonItem->type == DBVT_ASCIIZ) {
			DBVARIANT dbv = { 0 };

			if (!db_get_s(hFinalContact, szModule, szSetting, &dbv)) {
				result = !mir_strcmp((char *)buttonItem->bValuePush, dbv.pszVal);
				db_free(&dbv);
			}
		}
		else {
			switch (buttonItem->type) {
			case DBVT_BYTE: {
					BYTE val = db_get_b(hFinalContact, szModule, szSetting, 0);
					result = (val == buttonItem->bValuePush[0]);
					break;
				}
			case DBVT_WORD: {
					WORD val = db_get_w(hFinalContact, szModule, szSetting, 0);
					result = (val == *((WORD *)&buttonItem->bValuePush));
					break;
				}
			case DBVT_DWORD:
				DWORD val = db_get_dw(hFinalContact, szModule, szSetting, 0);
				result = (val == *((DWORD *)&buttonItem->bValuePush));
				break;
			}
		}
		SendMessage(buttonItem->hWnd, BM_SETCHECK, (WPARAM)result, 0);
		buttonItem = buttonItem->nextItem;
	}
}

// set states of standard buttons (pressed/unpressed)
void SetButtonStates()
{
	ButtonItem *buttonItem = g_ButtonItems;

	if (g_ButtonItems) {
		while (buttonItem) {
			if (buttonItem->dwFlags & BUTTON_ISINTERNAL) {
				switch (buttonItem->uId) {
				case IDC_STBSOUND:
					SendMessage(buttonItem->hWnd, BM_SETCHECK, cfg::dat.soundsOff ? BST_CHECKED : BST_UNCHECKED, 0);
					break;
				case IDC_STBHIDEOFFLINE:
					SendMessage(buttonItem->hWnd, BM_SETCHECK, db_get_b(NULL, "CList", "HideOffline", 0) ? BST_CHECKED : BST_UNCHECKED, 0);
					break;
				case IDC_STBHIDEGROUPS:
					SendMessage(buttonItem->hWnd, BM_SETCHECK, db_get_b(NULL, "CList", "UseGroups", 0) ? BST_CHECKED : BST_UNCHECKED, 0);
					break;
				}
			}
			buttonItem = buttonItem->nextItem;
		}
	}
}

void BlitWallpaper(HDC hdc, RECT *rc, struct ClcData *dat)
{
	int x, y;
	int bitx, bity;
	int maxx, maxy;
	int destw, desth, height, width;
	BITMAP *bmp = &cfg::dat.bminfoBg;
	LONG clip = cfg::dat.bClipBorder;

	if (dat == nullptr)
		return;

	SetStretchBltMode(hdc, HALFTONE);

	y = rc->top;

	rc->left = max(rc->left, clip);
	rc->right = min(rc->right - clip, rc->right);
	rc->top = max(rc->top, clip);
	rc->bottom = min(rc->bottom - clip, rc->bottom);

	width = rc->right - rc->left;
	height = rc->bottom - rc->top;
	HRGN my_rgn = CreateRectRgn(rc->left, rc->top, rc->right, rc->bottom);
	SelectClipRgn(hdc, my_rgn);
	maxx = dat->backgroundBmpUse & CLBF_TILEH ? rc->right : rc->left + 1;
	maxy = dat->backgroundBmpUse & CLBF_TILEV ? rc->bottom : y + 1;
	switch (dat->backgroundBmpUse & CLBM_TYPE) {
	case CLB_STRETCH:
		if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
			if (width * bmp->bmHeight < height * bmp->bmWidth) {
				desth = height;
				destw = desth * bmp->bmWidth / bmp->bmHeight;
			}
			else {
				destw = width;
				desth = destw * bmp->bmHeight / bmp->bmWidth;
			}
		}
		else {
			destw = width;
			desth = height;
		}
		break;
	case CLB_STRETCHH:
		if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
			destw = width;
			desth = destw * bmp->bmHeight / bmp->bmWidth;
		}
		else {
			destw = width;
			desth = bmp->bmHeight;
		}
		break;

	case CLB_STRETCHV:
		if (dat->backgroundBmpUse & CLBF_PROPORTIONAL) {
			desth = height;
			destw = desth * bmp->bmWidth / bmp->bmHeight;
		}
		else {
			destw = bmp->bmWidth;
			desth = height;
		}
		break;

	default:
		//clb_topleft
		destw = bmp->bmWidth;
		desth = bmp->bmHeight;
		break;
	}

	bitx = 0;
	bity = 0;
	for (; y < maxy; y += desth) {
		for (x = rc->left; x < maxx; x += destw)
			StretchBlt(hdc, x, y, destw, desth, cfg::dat.hdcPic, bitx, bity, bmp->bmWidth, bmp->bmHeight, SRCCOPY);
	}
	SelectClipRgn(hdc, nullptr);
	DeleteObject(my_rgn);
}

void ReloadThemedOptions()
{
	cfg::dat.bSkinnedStatusBar = db_get_b(NULL, "CLUI", "sb_skinned", 0);
	cfg::dat.bUsePerProto = db_get_b(NULL, "CLCExt", "useperproto", 0);
	cfg::dat.bOverridePerStatusColors = db_get_b(NULL, "CLCExt", "override_status", 0);
	cfg::dat.bRowSpacing = db_get_b(NULL, "CLC", "RowGap", 0);
	cfg::dat.bApplyIndentToBg = db_get_b(NULL, "CLCExt", "applyindentbg", 0);
	cfg::dat.bWallpaperMode = db_get_b(NULL, "CLUI", "UseBkSkin", 1);
	cfg::dat.bClipBorder = db_get_b(NULL, "CLUI", "clipborder", 0);
	cfg::dat.cornerRadius = db_get_b(NULL, "CLCExt", "CornerRad", 6);
	cfg::dat.gapBetweenFrames = (BYTE)db_get_dw(NULL, "CLUIFrames", "GapBetweenFrames", 1);
	cfg::dat.bUseDCMirroring = db_get_b(NULL, "CLC", "MirrorDC", 0);
	cfg::dat.bGroupAlign = db_get_b(NULL, "CLC", "GroupAlign", 0);
	if (cfg::dat.hBrushColorKey)
		DeleteObject(cfg::dat.hBrushColorKey);
	cfg::dat.hBrushColorKey = CreateSolidBrush(RGB(255, 0, 255));
	cfg::dat.bWantFastGradients = db_get_b(NULL, "CLCExt", "FastGradients", 0);
	cfg::dat.titleBarHeight = db_get_b(NULL, "CLCExt", "frame_height", DEFAULT_TITLEBAR_HEIGHT);
	cfg::dat.group_padding = db_get_dw(NULL, "CLCExt", "grp_padding", 0);
}

static RECT rcWindow = { 0 };

static void sttProcessResize(HWND hwnd, NMCLISTCONTROL *nmc)
{
	RECT rcTree, rcWorkArea, rcOld;
	int maxHeight, newHeight;
	int winstyle, skinHeight = 0;

	if (disableautoupd)
		return;

	if (!db_get_b(NULL, "CLUI", "AutoSize", 0))
		return;

	if (Docking_IsDocked(0, 0))
		return;
	if (hFrameContactTree == nullptr)
		return;

	maxHeight = db_get_b(NULL, "CLUI", "MaxSizeHeight", 75);
	rcOld = rcWindow;

	GetWindowRect(hwnd, &rcWindow);
	GetWindowRect(pcli->hwndContactTree, &rcTree);
	winstyle = GetWindowLongPtr(pcli->hwndContactTree, GWL_STYLE);

	SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
	HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
	MONITORINFO mi;
	mi.cbSize = sizeof(mi);
	if (GetMonitorInfo(hMon, &mi))
		rcWorkArea = mi.rcWork;

	if (nmc->pt.y > (rcWorkArea.bottom - rcWorkArea.top)) {
		nmc->pt.y = (rcWorkArea.bottom - rcWorkArea.top);
	}

	if (winstyle & CLS_SKINNEDFRAME) {
		BOOL hasTitleBar = wndFrameCLC ? wndFrameCLC->TitleBar.ShowTitleBar : 0;
		StatusItems_t *item = arStatusItems[(hasTitleBar ? ID_EXTBKOWNEDFRAMEBORDERTB : ID_EXTBKOWNEDFRAMEBORDER) - ID_STATUS_OFFLINE];
		skinHeight = item->IGNORED ? 0 : item->MARGIN_BOTTOM + item->MARGIN_TOP;
	}

	newHeight = max(nmc->pt.y, 3) + 1 + ((winstyle & WS_BORDER) ? 2 : 0) + skinHeight + (rcWindow.bottom - rcWindow.top) - (rcTree.bottom - rcTree.top);
	if (newHeight == (rcWindow.bottom - rcWindow.top) && show_on_first_autosize == FALSE)
		return;

	if (newHeight > (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100)
		newHeight = (rcWorkArea.bottom - rcWorkArea.top) * maxHeight / 100;
	if (db_get_b(NULL, "CLUI", "AutoSizeUpward", 0)) {
		rcWindow.top = rcWindow.bottom - newHeight;
		if (rcWindow.top < rcWorkArea.top) rcWindow.top = rcWorkArea.top;
	}
	else {
		rcWindow.bottom = rcWindow.top + newHeight;
		if (rcWindow.bottom > rcWorkArea.bottom) rcWindow.bottom = rcWorkArea.bottom;
	}
	if (cfg::dat.szOldCTreeSize.cx != rcTree.right - rcTree.left) {
		cfg::dat.szOldCTreeSize.cx = rcTree.right - rcTree.left;
		return;
	}
	KillTimer(hwnd, TIMERID_AUTOSIZE);
	SetTimer(hwnd, TIMERID_AUTOSIZE, 100, nullptr);
}

int CustomDrawScrollBars(NMCSBCUSTOMDRAW *nmcsbcd)
{
	switch (nmcsbcd->hdr.code) {
	case NM_COOLSB_CUSTOMDRAW:
		static HDC hdcScroll = nullptr;
		static HBITMAP hbmScroll, hbmScrollOld;
		static LONG scrollLeft, scrollRight, scrollHeight, scrollYmin, scrollYmax;

		switch (nmcsbcd->dwDrawStage) {
		case CDDS_PREPAINT:
			if (cfg::dat.bSkinnedScrollbar) // XXX fix (verify skin items to be complete, otherwise don't draw
				return CDRF_SKIPDEFAULT;
			return CDRF_DODEFAULT;

		case CDDS_POSTPAINT:
			return 0;

		case CDDS_ITEMPREPAINT:
			HDC hdc = nmcsbcd->hdc;
			StatusItems_t *item = nullptr, *arrowItem = nullptr;
			UINT uItemID = ID_EXTBKSCROLLBACK;
			HRGN rgn = nullptr;

			RECT rc;
			GetWindowRect(pcli->hwndContactTree, &rc);

			POINT pt;
			pt.x = rc.left;
			pt.y = rc.top;
			ScreenToClient(pcli->hwndContactList, &pt);
			hdcScroll = hdc;
			BitBlt(hdcScroll, nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right - nmcsbcd->rect.left,
				nmcsbcd->rect.bottom - nmcsbcd->rect.top, cfg::dat.hdcBg, pt.x + nmcsbcd->rect.left, pt.y + nmcsbcd->rect.top, SRCCOPY);

			switch (nmcsbcd->uItem) {
			case HTSCROLL_UP:
			case HTSCROLL_DOWN:
				uItemID = (nmcsbcd->uState == CDIS_DEFAULT || nmcsbcd->uState == CDIS_DISABLED) ? ID_EXTBKSCROLLBUTTON :
					(nmcsbcd->uState == CDIS_HOT ? ID_EXTBKSCROLLBUTTONHOVER : ID_EXTBKSCROLLBUTTONPRESSED);
				break;
			case HTSCROLL_PAGEGDOWN:
			case HTSCROLL_PAGEGUP:
				uItemID = nmcsbcd->uItem == HTSCROLL_PAGEGUP ? ID_EXTBKSCROLLBACK : ID_EXTBKSCROLLBACKLOWER;
				rgn = CreateRectRgn(nmcsbcd->rect.left, nmcsbcd->rect.top, nmcsbcd->rect.right, nmcsbcd->rect.bottom);
				SelectClipRgn(hdcScroll, rgn);
				break;
			case HTSCROLL_THUMB:
				uItemID = nmcsbcd->uState == CDIS_SELECTED ? ID_EXTBKSCROLLTHUMBPRESSED : ID_EXTBKSCROLLTHUMB;
				break;
			default:
				break;
			}

			uItemID -= ID_STATUS_OFFLINE;
			item = arStatusItems[uItemID];
			if (!item->IGNORED) {
				int alpha = nmcsbcd->uState == CDIS_DISABLED ? item->ALPHA - 50 : item->ALPHA;
				DrawAlpha(hdcScroll, &nmcsbcd->rect, item->COLOR, alpha, item->COLOR2, item->COLOR2_TRANSPARENT,
					item->GRADIENT, item->CORNER, item->BORDERSTYLE, item->imageItem);
			}
			DWORD dfcFlags = DFCS_FLAT | (nmcsbcd->uState == CDIS_DISABLED ? DFCS_INACTIVE :
				(nmcsbcd->uState == CDIS_HOT ? DFCS_HOT : (nmcsbcd->uState == CDIS_SELECTED ? DFCS_PUSHED : 0)));

			if (nmcsbcd->uItem == HTSCROLL_UP)
				arrowItem = arStatusItems[ID_EXTBKSCROLLARROWUP - ID_STATUS_OFFLINE];
			if (nmcsbcd->uItem == HTSCROLL_DOWN)
				arrowItem = arStatusItems[ID_EXTBKSCROLLARROWDOWN - ID_STATUS_OFFLINE];
			if (arrowItem && !arrowItem->IGNORED)
				DrawAlpha(hdcScroll, &nmcsbcd->rect, arrowItem->COLOR, arrowItem->ALPHA, arrowItem->COLOR2, arrowItem->COLOR2_TRANSPARENT,
					arrowItem->GRADIENT, arrowItem->CORNER, arrowItem->BORDERSTYLE, arrowItem->imageItem);
			else if (arrowItem)
				DrawFrameControl(hdcScroll, &nmcsbcd->rect, DFC_SCROLL, (nmcsbcd->uItem == HTSCROLL_UP ? DFCS_SCROLLUP : DFCS_SCROLLDOWN) | dfcFlags);

			if (rgn) {
				SelectClipRgn(hdcScroll, nullptr);
				DeleteObject(rgn);
			}
		}
	}
	return 0;
}

static int ServiceParamsOK(ButtonItem *item, WPARAM *wParam, LPARAM *lParam, MCONTACT hContact)
{
	if (item->dwFlags & BUTTON_PASSHCONTACTW || item->dwFlags & BUTTON_PASSHCONTACTL || item->dwFlags & BUTTON_ISCONTACTDBACTION) {
		if (hContact == 0)
			return 0;

		if (item->dwFlags & BUTTON_PASSHCONTACTW)
			*wParam = hContact;
		else if (item->dwFlags & BUTTON_PASSHCONTACTL)
			*lParam = hContact;
	}
	return 1;
}

static void ShowCLUI(HWND hwnd)
{
	int state = old_cliststate;
	int onTop = db_get_b(NULL, "CList", "OnTop", SETTING_ONTOP_DEFAULT);

	SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
	if (!db_get_b(NULL, "CLUI", "ShowMainMenu", SETTING_SHOWMAINMENU_DEFAULT))
		SetMenu(pcli->hwndContactList, nullptr);
	if (state == SETTING_STATE_NORMAL) {
		SendMessage(pcli->hwndContactList, WM_SIZE, 0, 0);
		ShowWindow(pcli->hwndContactList, SW_SHOWNORMAL);
		SendMessage(pcli->hwndContactList, CLUIINTM_REDRAW, 0, 0);
	}
	else if (state == SETTING_STATE_MINIMIZED) {
		cfg::dat.forceResize = TRUE;
		ShowWindow(pcli->hwndContactList, SW_HIDE);
	}
	else if (state == SETTING_STATE_HIDDEN) {
		cfg::dat.forceResize = TRUE;
		ShowWindow(pcli->hwndContactList, SW_HIDE);
	}
	SetWindowPos(pcli->hwndContactList, onTop ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
	DrawMenuBar(hwnd);
	if (cfg::dat.autosize) {
		SendMessage(pcli->hwndContactList, WM_SIZE, 0, 0);
		SendMessage(pcli->hwndContactTree, WM_SIZE, 0, 0);
	}
}

static void GetButtonRect(HWND hwnd, RECT *rc)
{
	if (hwnd)
		GetWindowRect(hwnd, rc);
	else {
		POINT pt;
		GetCursorPos(&pt);
		rc->bottom = rc->top = pt.y;
		rc->left = rc->right = pt.x;
	}
}

LRESULT CALLBACK ContactListWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	RECT rc;

	switch (msg) {
	case WM_CREATE:
		{
			int flags = WS_CHILD | CCS_BOTTOM;
			flags |= db_get_b(NULL, "CLUI", "ShowSBar", 1) ? WS_VISIBLE : 0;
			flags |= db_get_b(NULL, "CLUI", "ShowGrip", 1) ? SBARS_SIZEGRIP : 0;
			pcli->hwndStatus = CreateWindow(STATUSCLASSNAME, nullptr, flags, 0, 0, 0, 0, hwnd, nullptr, g_hInst, nullptr);
			if (flags & WS_VISIBLE) {
				ShowWindow(pcli->hwndStatus, SW_SHOW);
				SendMessage(pcli->hwndStatus, WM_SIZE, 0, 0);
			}
			mir_subclassWindow(pcli->hwndStatus, NewStatusBarWndProc);
			SetClassLong(pcli->hwndStatus, GCL_STYLE, GetClassLong(pcli->hwndStatus, GCL_STYLE) & ~(CS_VREDRAW | CS_HREDRAW));
		}
		g_oldSize.cx = g_oldSize.cy = 0;
		old_cliststate = db_get_b(NULL, "CList", "State", SETTING_STATE_NORMAL);
		db_set_b(NULL, "CList", "State", SETTING_STATE_HIDDEN);
		SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_VISIBLE);
		SetWindowLongPtr(hwnd, GWL_STYLE, GetWindowLongPtr(hwnd, GWL_STYLE) | WS_CLIPCHILDREN);
		if (!cfg::dat.bFirstRun)
			ConfigureEventArea();
		ConfigureCLUIGeometry(0);
		CluiProtocolStatusChanged(0, nullptr);

		for (int i = ID_STATUS_OFFLINE; i <= ID_STATUS_OUTTOLUNCH; i++)
			statusNames[i - ID_STATUS_OFFLINE] = pcli->pfnGetStatusModeDescription(i, 0);

		//delay creation of CLC so that it can get the status icons right the first time (needs protocol modules loaded)
		if (cfg::dat.bLayeredHack) {
			SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | (WS_EX_LAYERED));
			SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
		}

		if (cfg::dat.isTransparent) {
			SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
			SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
		}
		transparentFocus = 1;

		TranslateMenu(GetMenu(hwnd));
		PostMessage(hwnd, M_CREATECLC, 0, 0);
		return FALSE;

	case WM_NCCREATE:
		{
			LPCREATESTRUCT p = (LPCREATESTRUCT)lParam;
			p->style &= ~(CS_HREDRAW | CS_VREDRAW);
		}
		break;

	case M_CREATECLC: {
			if (db_get_b(NULL, "CLUI", "useskin", 0))
				IMG_LoadItems();
			CreateButtonBar(hwnd);
			//FYR: to be checked: otherwise it raises double xStatus items
			//NotifyEventHooks(pcli->hPreBuildStatusMenuEvent, 0, 0);
			SendMessage(hwnd, WM_SETREDRAW, FALSE, FALSE);
			{
				LONG style;
				BYTE windowStyle = db_get_b(NULL, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_TOOLWINDOW);
				ShowWindow(pcli->hwndContactList, SW_HIDE);
				style = GetWindowLongPtr(pcli->hwndContactList, GWL_EXSTYLE);
				if (windowStyle != SETTING_WINDOWSTYLE_DEFAULT) {
					style |= WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
					style &= ~WS_EX_APPWINDOW;
				}
				else {
					style &= ~(WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE);
					if (db_get_b(NULL, "CList", "AlwaysHideOnTB", 1))
						style &= ~WS_EX_APPWINDOW;
					else
						style |= WS_EX_APPWINDOW;
				}

				SetWindowLongPtr(pcli->hwndContactList, GWL_EXSTYLE, style);
				ApplyCLUIBorderStyle();

				SetWindowPos(pcli->hwndContactList, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE);
			}

			if (cfg::dat.bSkinnedButtonMode)
				SetButtonToSkinned();
			ConfigureFrame();
			SetButtonStates();

			CreateCLC();
			cfg::clcdat = (struct ClcData *)GetWindowLongPtr(pcli->hwndContactTree, 0);

			if (cfg::dat.bFullTransparent) {
				if (g_CLUISkinnedBkColorRGB)
					Tweak_It(g_CLUISkinnedBkColorRGB);
				else if (cfg::dat.bClipBorder || (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME))
					Tweak_It(RGB(255, 0, 255));
				else
					Tweak_It(cfg::clcdat->bkColour);
			}

			db_set_b(NULL, "CList", "State", old_cliststate);

			if (db_get_b(NULL, "CList", "AutoApplyLastViewMode", 0)) {
				DBVARIANT dbv = { 0 };
				if (!db_get(NULL, "CList", "LastViewMode", &dbv)) {
					if (mir_strlen(dbv.pszVal) > 2) {
						if (db_get_dw(NULL, CLVM_MODULE, dbv.pszVal, -1) != 0xffffffff)
							ApplyViewMode((char *)dbv.pszVal);
					}
					db_free(&dbv);
				}
			}
			if (!cfg::dat.autosize)
				ShowCLUI(hwnd);
			else {
				show_on_first_autosize = TRUE;
				RecalcScrollBar(pcli->hwndContactTree, cfg::clcdat);
			}
			return 0;
		}
	case WM_ERASEBKGND:
		return TRUE;
		/*
		if (cfg::dat.bSkinnedButtonMode)
		return TRUE;
		return DefWindowProc(hwnd, msg, wParam, lParam);
		*/

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			RECT rcFrame, rcClient;
			HDC hdc;
			HRGN rgn = nullptr;
			HDC hdcReal = BeginPaint(hwnd, &ps);

			if (during_sizing)
				rcClient = rcWPC;
			else
				GetClientRect(hwnd, &rcClient);
			CopyRect(&rc, &rcClient);

			if (!cfg::dat.hdcBg || rc.right > cfg::dat.dcSize.cx || rc.bottom + cfg::dat.statusBarHeight > cfg::dat.dcSize.cy) {
				RECT rcWorkArea;

				SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, FALSE);
				HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
				MONITORINFO mi;
				mi.cbSize = sizeof(mi);
				if (GetMonitorInfo(hMon, &mi))
					rcWorkArea = mi.rcWork;

				cfg::dat.dcSize.cy = max(rc.bottom + cfg::dat.statusBarHeight, rcWorkArea.bottom - rcWorkArea.top);
				cfg::dat.dcSize.cx = max(rc.right, (rcWorkArea.right - rcWorkArea.left) / 2);

				if (cfg::dat.hdcBg) {
					SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
					DeleteObject(cfg::dat.hbmBg);
					DeleteDC(cfg::dat.hdcBg);
				}
				cfg::dat.hdcBg = CreateCompatibleDC(hdcReal);
				cfg::dat.hbmBg = CreateCompatibleBitmap(hdcReal, cfg::dat.dcSize.cx, cfg::dat.dcSize.cy);
				cfg::dat.hbmBgOld = reinterpret_cast<HBITMAP>(SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBg));
			}

			if (cfg::shutDown) {
				EndPaint(hwnd, &ps);
				return 0;
			}

			hdc = cfg::dat.hdcBg;

			CopyRect(&rcFrame, &rcClient);
			if (g_CLUISkinnedBkColor) {
				if (cfg::dat.fOnDesktop) {
					HDC dc = GetDC(nullptr);
					RECT rcWin;

					GetWindowRect(hwnd, &rcWin);
					BitBlt(hdc, 0, 0, rcClient.right, rcClient.bottom, dc, rcWin.left, rcWin.top, SRCCOPY);
				}
				else
					FillRect(hdc, &rcClient, g_CLUISkinnedBkColor);
			}

			if (cfg::dat.bClipBorder != 0 || cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME) {
				int docked = Clist_IsDocked();
				int clip = cfg::dat.bClipBorder;

				if (!g_CLUISkinnedBkColor)
					FillRect(hdc, &rcClient, cfg::dat.hBrushColorKey);
				if (cfg::dat.dwFlags & CLUI_FRAME_ROUNDEDFRAME)
					rgn = CreateRoundRectRgn(clip, docked ? 0 : clip, rcClient.right - clip + 1, rcClient.bottom - (docked ? 0 : clip - 1), 8 + clip, 8 + clip);
				else
					rgn = CreateRectRgn(clip, docked ? 0 : clip, rcClient.right - clip, rcClient.bottom - (docked ? 0 : clip));
				SelectClipRgn(hdc, rgn);
			}

			if (g_CLUIImageItem) {
				IMG_RenderImageItem(hdc, g_CLUIImageItem, &rcFrame);
				cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
				ClientToScreen(hwnd, &cfg::dat.ptW);
				goto skipbg;
			}

			if (cfg::dat.bWallpaperMode)
				FillRect(hdc, &rcClient, cfg::dat.hBrushCLCBk);
			else
				FillRect(hdc, &rcClient, GetSysColorBrush(COLOR_3DFACE));

			rcFrame.left += (cfg::dat.bCLeft - 1);
			rcFrame.right -= (cfg::dat.bCRight - 1);
			rcFrame.bottom++;
			rcFrame.bottom -= cfg::dat.statusBarHeight;
			rcFrame.top += (cfg::dat.topOffset - 1);

			if (cfg::dat.dwFlags & CLUI_FRAME_CLISTSUNKEN) {
				if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
					InflateRect(&rcFrame, -1, -1);
					if (cfg::dat.bmpBackground)
						BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
					cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
					ClientToScreen(hwnd, &cfg::dat.ptW);
				}
				InflateRect(&rcFrame, 1, 1);
				if (cfg::dat.bSkinnedButtonMode)
					rcFrame.bottom -= (cfg::dat.bottomOffset);
				DrawEdge(hdc, &rcFrame, BDR_SUNKENOUTER, BF_RECT);
			}
			else if (cfg::dat.bWallpaperMode && cfg::clcdat != nullptr) {
				if (cfg::dat.bmpBackground)
					BlitWallpaper(hdc, &rcFrame, cfg::clcdat);
				cfg::dat.ptW.x = cfg::dat.ptW.y = 0;
				ClientToScreen(hwnd, &cfg::dat.ptW);
			}
skipbg:
			BitBlt(hdcReal, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdc, 0, 0, SRCCOPY);
			if (rgn) {
				SelectClipRgn(hdc, nullptr);
				DeleteObject(rgn);
			}
			EndPaint(hwnd, &ps);
		}
		return 0;

	case WM_ENTERSIZEMOVE:
		{
			POINT pt = { 0 };

			GetWindowRect(hwnd, &g_PreSizeRect);
			GetClientRect(hwnd, &rc);
			ClientToScreen(hwnd, &pt);
			g_CLUI_x_off = pt.x - g_PreSizeRect.left;
			g_CLUI_y_off = pt.y - g_PreSizeRect.top;
			pt.x = rc.right;
			ClientToScreen(hwnd, &pt);
			g_CLUI_x1_off = g_PreSizeRect.right - pt.x;
			pt.x = 0;
			pt.y = rc.bottom;
			ClientToScreen(hwnd, &pt);
			g_CLUI_y1_off = g_PreSizeRect.bottom - pt.y;
		}
		break;

	case WM_EXITSIZEMOVE:
		PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
		break;

	case WM_SIZING:
		break;

	case WM_WINDOWPOSCHANGED:
		if (Docking_IsDocked(0, 0))
			break;

	case WM_WINDOWPOSCHANGING:
		if (pcli->hwndContactList != nullptr) {
			WINDOWPOS *wp = (WINDOWPOS *)lParam;
			if (!wp || (wp->flags & SWP_NOSIZE))
				return FALSE;

			RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
			during_sizing = true;

			new_window_rect.left = 0;
			new_window_rect.right = wp->cx - (g_CLUI_x_off + g_CLUI_x1_off);
			new_window_rect.top = 0;
			new_window_rect.bottom = wp->cy - g_CLUI_y_off - g_CLUI_y1_off;

			if (cfg::dat.dwFlags & CLUI_FRAME_SBARSHOW) {
				RECT rcStatus;
				SetWindowPos(pcli->hwndStatus, nullptr, 0, new_window_rect.bottom - 20, new_window_rect.right, 20, SWP_NOZORDER);
				GetWindowRect(pcli->hwndStatus, &rcStatus);
				cfg::dat.statusBarHeight = (rcStatus.bottom - rcStatus.top);
				if (wp->cx != g_oldSize.cx)
					SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
				RedrawWindow(pcli->hwndStatus, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
			}
			else
				cfg::dat.statusBarHeight = 0;

			SizeFramesByWindowRect(&new_window_rect);
			dock_prevent_moving = 0;
			LayoutButtons(hwnd, &new_window_rect);
			dock_prevent_moving = 1;
			g_oldPos.x = wp->x;
			g_oldPos.y = wp->y;
			g_oldSize.cx = wp->cx;
			g_oldSize.cy = wp->cy;
			rcWPC = new_window_rect;
		}
		during_sizing = false;
		return 0;

	case WM_SIZE:
		if ((wParam == 0 && lParam == 0) || Docking_IsDocked(0, 0)) {

			if (IsZoomed(hwnd))
				ShowWindow(hwnd, SW_SHOWNORMAL);

			if (pcli->hwndContactList != nullptr) {
				SendMessage(hwnd, WM_ENTERSIZEMOVE, 0, 0);
				GetWindowRect(hwnd, &rc);
				WINDOWPOS wp = {};
				wp.cx = rc.right - rc.left;
				wp.cy = rc.bottom - rc.top;
				wp.x = rc.left;
				wp.y = rc.top;
				wp.flags = 0;
				SendMessage(hwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
				SendMessage(hwnd, WM_EXITSIZEMOVE, 0, 0);
			}
		}

	case WM_MOVE:
		if (!IsIconic(hwnd)) {
			GetWindowRect(hwnd, &rc);

			if (!Docking_IsDocked(0, 0)) {
				cluiPos.bottom = (DWORD)(rc.bottom - rc.top);
				cluiPos.left = rc.left;
				cluiPos.top = rc.top;
			}
			cluiPos.right = rc.right - rc.left;
			if (cfg::dat.realTimeSaving) {
				GetWindowRect(hwnd, &rc);

				// if docked, dont remember pos (except for width)
				if (!Clist_IsDocked()) {
					db_set_dw(NULL, "CList", "Height", (DWORD)(rc.bottom - rc.top));
					db_set_dw(NULL, "CList", "x", (DWORD)rc.left);
					db_set_dw(NULL, "CList", "y", (DWORD)rc.top);
				}
				db_set_dw(NULL, "CList", "Width", (DWORD)(rc.right - rc.left));
			}
		}
		return TRUE;

	case WM_SETFOCUS:
		SetFocus(pcli->hwndContactTree);
		return 0;

	case CLUIINTM_REMOVEFROMTASKBAR: {
			BYTE windowStyle = db_get_b(NULL, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
			if (windowStyle == SETTING_WINDOWSTYLE_DEFAULT && db_get_b(NULL, "CList", "AlwaysHideOnTB", 0))
				RemoveFromTaskBar(hwnd);
			return 0;
		}
	case WM_ACTIVATE:
		if (g_fading_active) {
			if (wParam != WA_INACTIVE && cfg::dat.isTransparent)
				transparentFocus = 1;
			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
		if (wParam == WA_INACTIVE) {
			if ((HWND)wParam != hwnd)
				if (cfg::dat.isTransparent)
					if (transparentFocus)
						SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
		}
		else {
			if (cfg::dat.isTransparent) {
				KillTimer(hwnd, TM_AUTOALPHA);
				SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
				transparentFocus = 1;
			}
			SetWindowPos(pcli->hwndContactList, db_get_b(NULL, "CList", "OnTop", SETTING_ONTOP_DEFAULT) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING);
		}
		PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);
		return DefWindowProc(hwnd, msg, wParam, lParam);

	case WM_SETCURSOR:
		if (cfg::dat.isTransparent) {
			if (!transparentFocus && GetForegroundWindow() != hwnd) {
				SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
				transparentFocus = 1;
				SetTimer(hwnd, TM_AUTOALPHA, 250, nullptr);
			}
		}
		return DefWindowProc(hwnd, msg, wParam, lParam);

	case WM_NCHITTEST: {
			LRESULT result;
			RECT r;
			POINT pt;
			int clip = cfg::dat.bClipBorder;

			GetWindowRect(hwnd, &r);
			GetCursorPos(&pt);
			if (pt.y <= r.bottom && pt.y >= r.bottom - clip - 6 && !db_get_b(NULL, "CLUI", "AutoSize", 0)) {
				if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
					return HTBOTTOM;
				if (pt.x < r.left + clip + 10)
					return HTBOTTOMLEFT;
				if (pt.x > r.right - clip - 10)
					return HTBOTTOMRIGHT;
			}
			else if (pt.y >= r.top && pt.y <= r.top + 3 && !db_get_b(NULL, "CLUI", "AutoSize", 0)) {
				if (pt.x > r.left + clip + 10 && pt.x < r.right - clip - 10)
					return HTTOP;
				if (pt.x < r.left + clip + 10)
					return HTTOPLEFT;
				if (pt.x > r.right - clip - 10)
					return HTTOPRIGHT;
			}
			else if (pt.x >= r.left && pt.x <= r.left + clip + 6)
				return HTLEFT;
			else if (pt.x >= r.right - clip - 6 && pt.x <= r.right)
				return HTRIGHT;

			result = DefWindowProc(hwnd, WM_NCHITTEST, wParam, lParam);
			if (result == HTSIZE || result == HTTOP || result == HTTOPLEFT || result == HTTOPRIGHT || result == HTBOTTOM || result == HTBOTTOMRIGHT || result == HTBOTTOMLEFT)
				if (cfg::dat.autosize)
					return HTCLIENT;
			return result;
		}

	case WM_TIMER:
		if (wParam == TM_AUTOALPHA) {
			int inwnd;

			if (GetForegroundWindow() == hwnd) {
				KillTimer(hwnd, TM_AUTOALPHA);
				inwnd = 1;
			}
			else {
				POINT pt;
				HWND hwndPt;
				pt.x = (short)LOWORD(GetMessagePos());
				pt.y = (short)HIWORD(GetMessagePos());
				hwndPt = WindowFromPoint(pt);
				inwnd = (hwndPt == hwnd || GetParent(hwndPt) == hwnd);
			}
			if (inwnd != transparentFocus) {
				//change
				transparentFocus = inwnd;
				if (transparentFocus)
					SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.alpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
				else
					SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), cfg::dat.autoalpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
			}
			if (!transparentFocus)
				KillTimer(hwnd, TM_AUTOALPHA);
		}
		else if (wParam == TIMERID_AUTOSIZE) {
			KillTimer(hwnd, wParam);
			SetWindowPos(hwnd, nullptr, rcWindow.left, rcWindow.top, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
			PostMessage(hwnd, WM_SIZE, 0, 0);
			PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
		}
		return TRUE;

	case WM_SHOWWINDOW:
		{
			static int noRecurse = 0;
			DWORD thisTick, startTick;
			int sourceAlpha, destAlpha;

			if (cfg::dat.forceResize && wParam != SW_HIDE) {
				cfg::dat.forceResize = FALSE;
				SendMessage(hwnd, WM_SIZE, 0, 0);
				PostMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
			}
			PostMessage(hwnd, CLUIINTM_REMOVEFROMTASKBAR, 0, 0);

			if (lParam)
				return DefWindowProc(hwnd, msg, wParam, lParam);
			if (noRecurse)
				return DefWindowProc(hwnd, msg, wParam, lParam);
			if (!cfg::dat.fadeinout)
				return DefWindowProc(hwnd, msg, wParam, lParam);

			g_fading_active = 1;

			if (wParam) {
				sourceAlpha = 0;
				destAlpha = cfg::dat.isTransparent ? cfg::dat.alpha : 255;
				SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? (COLORREF)cfg::dat.colorkey : RGB(0, 0, 0), (BYTE)sourceAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
				noRecurse = 1;
				ShowWindow(hwnd, SW_SHOW);
				RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
				noRecurse = 0;
			}
			else {
				sourceAlpha = cfg::dat.isTransparent ? (transparentFocus ? cfg::dat.alpha : cfg::dat.autoalpha) : 255;
				destAlpha = 0;
			}
			for (startTick = GetTickCount();;) {
				thisTick = GetTickCount();
				if (thisTick >= startTick + 200) {
					SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (BYTE)destAlpha, LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
					g_fading_active = 0;
					return DefWindowProc(hwnd, msg, wParam, lParam);
				}
				SetLayeredWindowAttributes(hwnd, cfg::dat.bFullTransparent ? cfg::dat.colorkey : RGB(0, 0, 0), (BYTE)(sourceAlpha + (destAlpha - sourceAlpha) * (int)(thisTick - startTick) / 200), LWA_ALPHA | (cfg::dat.bFullTransparent ? LWA_COLORKEY : 0));
			}
		}

	case WM_SYSCOMMAND:
		{
			BYTE bWindowStyle = db_get_b(NULL, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT);
			if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle) {
				if (wParam == SC_RESTORE) {
					CallWindowProc(DefWindowProc, hwnd, msg, wParam, lParam);
					SendMessage(hwnd, WM_SIZE, 0, 0);
					SendMessage(hwnd, CLUIINTM_REDRAW, 0, 0);
					SendMessage(hwnd, CLUIINTM_STATUSBARUPDATE, 0, 0);
					db_set_b(NULL, "CList", "State", SETTING_STATE_NORMAL);
					break;
				}
			}

			if (wParam == SC_MAXIMIZE)
				return 0;

			if (wParam == SC_MINIMIZE) {
				if (SETTING_WINDOWSTYLE_DEFAULT == bWindowStyle && !db_get_b(NULL, "CList", "AlwaysHideOnTB", 0)) {
					db_set_b(NULL, "CList", "State", SETTING_STATE_MINIMIZED);
					break;
				}
				pcli->pfnShowHide();
				return 0;
			}
			if (wParam == SC_RESTORE) {
				pcli->pfnShowHide();
				return 0;
			}
		}
		return DefWindowProc(hwnd, msg, wParam, lParam);

	case WM_COMMAND:
		{
			DWORD dwOldFlags = cfg::dat.dwFlags;
			if (HIWORD(wParam) == BN_CLICKED && lParam != 0) {
				if (LOWORD(wParam) == IDC_TBFIRSTUID - 1)
					break;

				else if (LOWORD(wParam) >= IDC_TBFIRSTUID) { // skinnable buttons handling
					ButtonItem *item = g_ButtonItems;
					WPARAM wwParam = 0;
					LPARAM llParam = 0;
					MCONTACT hContact = 0;
					ClcContact *contact = nullptr;
					int serviceFailure = FALSE;

					if (cfg::clcdat) {
						pcli->pfnGetRowByIndex(cfg::clcdat, cfg::clcdat->selection, &contact, nullptr);
						if (contact && contact->type == CLCIT_CONTACT)
							hContact = contact->hContact;
					}
					while (item) {
						if (item->uId == (DWORD)LOWORD(wParam)) {
							int contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);

							if (item->dwFlags & BUTTON_ISSERVICE) {
								if (ServiceExists(item->szService) && contactOK)
									CallService(item->szService, wwParam, llParam);
								else if (contactOK)
									serviceFailure = TRUE;
							}
							else if (item->dwFlags & BUTTON_ISPROTOSERVICE && cfg::clcdat) {
								if (contactOK) {
									char *szProto = GetContactProto(hContact);
									if (ProtoServiceExists(szProto, item->szService))
										CallProtoService(szProto, item->szService, wwParam, llParam);
									else
										serviceFailure = TRUE;
								}
							}
							else if (item->dwFlags & BUTTON_ISDBACTION) {
								BYTE *pValue;
								char *szModule = item->szModule;
								char *szSetting = item->szSetting;
								MCONTACT finalhContact = 0;

								if (item->dwFlags & BUTTON_ISCONTACTDBACTION || item->dwFlags & BUTTON_DBACTIONONCONTACT) {
									contactOK = ServiceParamsOK(item, &wwParam, &llParam, hContact);
									if (contactOK && item->dwFlags & BUTTON_ISCONTACTDBACTION)
										szModule = GetContactProto(hContact);
									finalhContact = hContact;
								}
								else
									contactOK = 1;

								if (contactOK) {
									BOOL fDelete = FALSE;

									if (item->dwFlags & BUTTON_ISTOGGLE) {
										BOOL fChecked = (SendMessage(item->hWnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED);

										pValue = fChecked ? item->bValueRelease : item->bValuePush;
										if (fChecked && pValue[0] == 0)
											fDelete = TRUE;
									}
									else
										pValue = item->bValuePush;

									if (fDelete)
										db_unset(finalhContact, szModule, szSetting);
									else {
										switch (item->type) {
										case DBVT_BYTE:
											db_set_b(finalhContact, szModule, szSetting, pValue[0]);
											break;
										case DBVT_WORD:
											db_set_w(finalhContact, szModule, szSetting, *((WORD *)&pValue[0]));
											break;
										case DBVT_DWORD:
											db_set_dw(finalhContact, szModule, szSetting, *((DWORD *)&pValue[0]));
											break;
										case DBVT_ASCIIZ:
											db_set_s(finalhContact, szModule, szSetting, (char *)pValue);
											break;
										}
									}
								}
								else if (item->dwFlags & BUTTON_ISTOGGLE)
									SendMessage(item->hWnd, BM_SETCHECK, 0, 0);
							}
							if (!contactOK)
								MessageBox(nullptr, TranslateT("The requested action requires a valid contact selection. Please select a contact from the contact list and repeat."), TranslateT("Parameter mismatch"), MB_OK);
							if (serviceFailure) {
								wchar_t szError[512];
								mir_snwprintf(szError, TranslateT("The service %S specified by the %S button definition was not found. You may need to install additional plugins."), item->szService, item->szName);
								MessageBox(nullptr, szError, TranslateT("Service failure"), MB_OK);
							}
							break;
						}
						item = item->nextItem;
					}
					goto buttons_done;
				}

				switch (LOWORD(wParam)) {
				case IDC_TBMENU:
				case IDC_TBTOPMENU:
				case IDC_STBTOPMENU:
					GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
					TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBMENU ? rc.top : rc.bottom, 0, hwnd, nullptr);
					return 0;

				case IDC_TBTOPSTATUS:
				case IDC_STBTOPSTATUS:
				case IDC_TBGLOBALSTATUS:
					GetButtonRect(GetDlgItem(hwnd, LOWORD(wParam)), &rc);
					TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, rc.left, LOWORD(wParam) == IDC_TBGLOBALSTATUS ? rc.top : rc.bottom, 0, hwnd, nullptr);
					return 0;

				case IDC_TBSOUND:
				case IDC_STBSOUND:
					cfg::dat.soundsOff = !cfg::dat.soundsOff;
					db_set_b(NULL, "CLUI", "NoSounds", (BYTE)cfg::dat.soundsOff);
					db_set_b(NULL, "Skin", "UseSound", (BYTE)(cfg::dat.soundsOff ? 0 : 1));
					return 0;

				case IDC_TBSELECTVIEWMODE:
				case IDC_STBSELECTVIEWMODE:
					SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_SELECTMODE, lParam);
					break;
				case IDC_TBCLEARVIEWMODE:
				case IDC_STBCLEARVIEWMODE:
					SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_RESETMODES, lParam);
					break;
				case IDC_TBCONFIGUREVIEWMODE:
				case IDC_STBCONFIGUREVIEWMODE:
					SendMessage(g_hwndViewModeFrame, WM_COMMAND, IDC_CONFIGUREMODES, lParam);
					break;
				case IDC_TBFINDANDADD:
				case IDC_STBFINDANDADD:
					CallService(MS_FINDADD_FINDADD, 0, 0);
					return 0;
				case IDC_TBACCOUNTS:
				case IDC_STBACCOUNTS:
					CallService(MS_PROTO_SHOWACCMGR, 0, 0);
					break;
				case IDC_TBOPTIONS:
				case IDC_STBOPTIONS:
					CallService("Options/OptionsCommand", 0, 0);
					return 0;
				}
			}
			else if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_MAINMENU, NULL))
				return 0;

buttons_done:
			switch (LOWORD(wParam)) {
			case ID_TRAY_EXIT:
			case ID_ICQ_EXIT:
				cfg::shutDown = 1;
				if (Miranda_OkToExit())
					DestroyWindow(hwnd);
				break;
			case ID_TRAY_HIDE:
			case IDC_TBMINIMIZE:
			case IDC_STBMINIMIZE:
				pcli->pfnShowHide();
				break;
			case POPUP_NEWGROUP:
				SendMessage(pcli->hwndContactTree, CLM_SETHIDEEMPTYGROUPS, 0, 0);
				SendMessage(pcli->hwndContactTree, CLM_SETUSEGROUPS, 1, 0);
				Clist_GroupCreate(NULL, nullptr);
				break;
			case POPUP_HIDEOFFLINE:
			case IDC_TBHIDEOFFLINE:
			case IDC_STBHIDEOFFLINE:
				pcli->pfnSetHideOffline(-1);
				break;
			case POPUP_HIDEOFFLINEROOT:
				CallService(MS_CLIST_TOGGLEHIDEOFFLINEROOT, 0, 0);
				break;
			case POPUP_HIDEEMPTYGROUPS:
				CallService(MS_CLIST_TOGGLEEMPTYGROUPS, 0, 0);
				break;
			case IDC_TBHIDEGROUPS:
			case IDC_STBHIDEGROUPS:
			case POPUP_DISABLEGROUPS:
				ClcSetButtonState(IDC_TBHIDEGROUPS, CallService(MS_CLIST_TOGGLEGROUPS, 0, 0));
				SetButtonStates();
				break;
			case POPUP_HIDEMIRANDA:
				pcli->pfnShowHide();
				break;
			case POPUP_SHOWMETAICONS:
				cfg::dat.dwFlags ^= CLUI_USEMETAICONS;
				Clist_InitAutoRebuild(pcli->hwndContactTree);
				break;
			case POPUP_FRAME:
				cfg::dat.dwFlags ^= CLUI_FRAME_CLISTSUNKEN;
				break;
			case POPUP_BUTTONS:
				cfg::dat.dwFlags ^= CLUI_FRAME_SHOWBOTTOMBUTTONS;
				break;
			case POPUP_SHOWSTATUSICONS:
				cfg::dat.dwFlags ^= CLUI_FRAME_STATUSICONS;
				break;
			}
			if (dwOldFlags != cfg::dat.dwFlags) {
				InvalidateRect(pcli->hwndContactTree, nullptr, FALSE);
				db_set_dw(NULL, "CLUI", "Frameflags", cfg::dat.dwFlags);
				if ((dwOldFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN)) != (cfg::dat.dwFlags & (CLUI_FRAME_SHOWBOTTOMBUTTONS | CLUI_FRAME_CLISTSUNKEN))) {
					ConfigureFrame();
					ConfigureCLUIGeometry(1);
				}
				ConfigureEventArea();
				PostMessage(pcli->hwndContactList, WM_SIZE, 0, 0);
				PostMessage(pcli->hwndContactList, CLUIINTM_REDRAW, 0, 0);
			}
		}
		return FALSE;

	case WM_LBUTTONDOWN:
		if (g_ButtonItems) {
			POINT pt;
			GetCursorPos(&pt);
			return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
		}
		break;

	case WM_DISPLAYCHANGE:
		SendMessage(pcli->hwndContactTree, WM_SIZE, 0, 0); //forces it to send a cln_listsizechanged
		break;

	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->hwndFrom == pcli->hwndContactTree) {
			switch (((LPNMHDR)lParam)->code) {
			case CLN_LISTSIZECHANGE:
				sttProcessResize(hwnd, (NMCLISTCONTROL *)lParam);
				return FALSE;

			case NM_CLICK:
				{
					NMCLISTCONTROL *nm = (NMCLISTCONTROL *)lParam;
					DWORD hitFlags;
					SendMessage(pcli->hwndContactTree, CLM_HITTEST, (WPARAM)&hitFlags, MAKELPARAM(nm->pt.x, nm->pt.y));
					if ((hitFlags & (CLCHT_NOWHERE | CLCHT_INLEFTMARGIN | CLCHT_BELOWITEMS)) == 0)
						break;
					
					if (db_get_b(NULL, "CLUI", "ClientAreaDrag", SETTING_CLIENTDRAG_DEFAULT)) {
						POINT pt;
						pt = nm->pt;
						ClientToScreen(pcli->hwndContactTree, &pt);
						return SendMessage(hwnd, WM_SYSCOMMAND, SC_MOVE | HTCAPTION, MAKELPARAM(pt.x, pt.y));
					}
				}
				return FALSE;
			}
		}
		break;

	case WM_CONTEXTMENU:
		GetWindowRect(pcli->hwndContactTree, &rc);
		{
			// x/y might be -1 if it was generated by a kb click
			POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
			if (pt.x == -1 && pt.y == -1) {
				// all this is done in screen-coords!
				GetCursorPos(&pt);
				// the mouse isnt near the window, so put it in the middle of the window
				if (!PtInRect(&rc, pt)) {
					pt.x = rc.left + (rc.right - rc.left) / 2;
					pt.y = rc.top + (rc.bottom - rc.top) / 2;
				}
			}
			if (PtInRect(&rc, pt)) {
				HMENU hMenu = Menu_BuildGroupMenu();
				TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
				Menu_DestroyNestedMenu(hMenu);
				return 0;
			}
			GetWindowRect(pcli->hwndStatus, &rc);
			if (PtInRect(&rc, pt)) {
				HMENU hMenu;
				if (db_get_b(NULL, "CLUI", "SBarRightClk", 0))
					hMenu = Menu_GetMainMenu();
				else
					hMenu = Menu_GetStatusMenu();
				TrackPopupMenu(hMenu, TPM_TOPALIGN | TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hwnd, nullptr);
				return 0;
			}
		}
		break;

	case WM_MEASUREITEM:
		if (((LPMEASUREITEMSTRUCT)lParam)->itemData == MENU_MIRANDAMENU) {
			((LPMEASUREITEMSTRUCT)lParam)->itemWidth = g_cxsmIcon * 4 / 3;
			((LPMEASUREITEMSTRUCT)lParam)->itemHeight = 0;
			return TRUE;
		}
		return Menu_MeasureItem(lParam);

	case WM_DRAWITEM:
		{
			LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;

			if (hbmLockedPoint == nullptr) {
				hdcLockedPoint = CreateCompatibleDC(dis->hDC);
				hbmLockedPoint = CreateCompatibleBitmap(dis->hDC, 5, 5);
				hbmOldLockedPoint = reinterpret_cast<HBITMAP>(SelectObject(hdcLockedPoint, hbmLockedPoint));
			}
			if (dis->hwndItem == pcli->hwndStatus) {
				ProtocolData *pd = (ProtocolData *)dis->itemData;
				if (IsBadCodePtr((FARPROC)pd))
					return TRUE;
				if (cfg::shutDown)
					return TRUE;

				int nParts = SendMessage(pcli->hwndStatus, SB_GETPARTS, 0, 0);
				SIZE textSize;
				BYTE showOpts = db_get_b(NULL, "CLUI", "SBarShow", 1);
				char *szProto = pd->RealName;
				int status = CallProtoService(szProto, PS_GETSTATUS, 0, 0);
				SetBkMode(dis->hDC, TRANSPARENT);
				int x = dis->rcItem.left;

				if (showOpts & 1) {
					HICON hIcon;

					if (status >= ID_STATUS_CONNECTING && status < ID_STATUS_OFFLINE) {
						char szBuffer[128];
						mir_snprintf(szBuffer, "%s_conn", pd->RealName);
						hIcon = IcoLib_GetIcon(szBuffer);
					}
					else if (cfg::dat.bShowXStatusOnSbar && status > ID_STATUS_OFFLINE) {
						int xStatus;
						CUSTOM_STATUS cst = { sizeof(cst) };
						cst.flags = CSSF_MASK_STATUS;
						cst.status = &xStatus;
						if (ProtoServiceExists(pd->RealName, PS_GETCUSTOMSTATUSEX) && !CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cst) && xStatus > 0)
							hIcon = (HICON)CallProtoService(pd->RealName, PS_GETCUSTOMSTATUSICON, 0, LR_SHARED); // get OWN xStatus icon (if set)
						else
							hIcon = Skin_LoadProtoIcon(szProto, status);
					}
					else
						hIcon = Skin_LoadProtoIcon(szProto, status);

					if (!(showOpts & 6) && cfg::dat.bEqualSections)
						x = (dis->rcItem.left + dis->rcItem.right - 16) >> 1;
					if (pd->protopos == 0)
						x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
					else if (pd->protopos == nParts - 1)
						x -= (cfg::dat.bCRight / 2);
					DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
					IcoLib_ReleaseIcon(hIcon);

					if (db_get_b(NULL, "CLUI", "sbar_showlocked", 1)) {
						if (db_get_b(NULL, szProto, "LockMainStatus", 0)) {
							hIcon = Skin_LoadIcon(SKINICON_OTHER_STATUS_LOCKED);
							if (hIcon != nullptr) {
								DrawIconEx(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - 16) >> 1, hIcon, 16, 16, 0, nullptr, DI_NORMAL);
								IcoLib_ReleaseIcon(hIcon);
							}
						}
					}
					x += 18;
				}
				else {
					x += 2;
					if (pd->protopos == 0)
						x += (cfg::dat.bEqualSections ? (cfg::dat.bCLeft / 2) : cfg::dat.bCLeft);
					else if (pd->protopos == nParts - 1)
						x -= (cfg::dat.bCRight / 2);
				}

				if (showOpts & 2) {
					wchar_t szName[64];
					PROTOACCOUNT *pa = Proto_GetAccount(szProto);
					if (pa) {
						mir_wstrncpy(szName, pa->tszAccountName, _countof(szName));
						szName[_countof(szName) - 1] = 0;
					}
					else
						szName[0] = 0;

					if (mir_wstrlen(szName) < sizeof(szName) - 1)
						mir_wstrcat(szName, L" ");
					GetTextExtentPoint32(dis->hDC, szName, (int)mir_wstrlen(szName), &textSize);
					TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szName, (int)mir_wstrlen(szName));
					x += textSize.cx;
				}
				if (showOpts & 4) {
					wchar_t *szStatus = pcli->pfnGetStatusModeDescription(status, 0);
					GetTextExtentPoint32(dis->hDC, szStatus, (int)mir_wstrlen(szStatus), &textSize);
					TextOut(dis->hDC, x, (dis->rcItem.top + dis->rcItem.bottom - textSize.cy) >> 1, szStatus, (int)mir_wstrlen(szStatus));
				}
			}
			else if (dis->CtlType == ODT_MENU) {
				if (dis->itemData == MENU_MIRANDAMENU)
					break;
				return Menu_DrawItem(lParam);
			}
		}
		return 0;

	case WM_CLOSE:
		if (SETTING_WINDOWSTYLE_DEFAULT == db_get_b(NULL, "CLUI", "WindowStyle", SETTING_WINDOWSTYLE_DEFAULT) && !db_get_b(NULL, "CList", "AlwaysHideOnTB", 0)) {
			PostMessage(hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0);
			return 0;
		}
		pcli->pfnShowHide();
		return 0;

	case CLUIINTM_REDRAW:
		if (show_on_first_autosize) {
			show_on_first_autosize = FALSE;
			ShowCLUI(hwnd);
		}
		RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);
		return 0;

	case CLUIINTM_STATUSBARUPDATE:
		CluiProtocolStatusChanged(0, nullptr);
		return 0;

	case WM_THEMECHANGED:
		API::updateState();
		break;

	case WM_DESTROY:
		if (cfg::dat.hdcBg) {
			SelectObject(cfg::dat.hdcBg, cfg::dat.hbmBgOld);
			DeleteObject(cfg::dat.hbmBg);
			DeleteDC(cfg::dat.hdcBg);
			cfg::dat.hdcBg = nullptr;
		}
		if (cfg::dat.bmpBackground) {
			SelectObject(cfg::dat.hdcPic, cfg::dat.hbmPicOld);
			DeleteDC(cfg::dat.hdcPic);
			DeleteObject(cfg::dat.bmpBackground);
			cfg::dat.bmpBackground = nullptr;
		}
		FreeProtocolData();
		if (hdcLockedPoint) {
			SelectObject(hdcLockedPoint, hbmOldLockedPoint);
			DeleteObject(hbmLockedPoint);
			DeleteDC(hdcLockedPoint);
		}
		// if this has not yet been set, do it now.
		// indicates that clist is shutting down and prevents various things
		// from happening at shutdown.
		if (!cfg::shutDown)
			cfg::shutDown = 1;
		CallService(MS_CLIST_FRAMES_REMOVEFRAME, (WPARAM)hFrameContactTree, 0);
		break;
	}

	return coreCli.pfnContactListWndProc(hwnd, msg, wParam, lParam);
}

#ifndef CS_DROPSHADOW
#define CS_DROPSHADOW 0x00020000
#endif

static int MetaChanged(WPARAM wParam, LPARAM lParam)
{
	Clist_Broadcast(INTM_METACHANGEDEVENT, wParam, lParam);
	return 0;
}

static INT_PTR CLN_ShowMainMenu(WPARAM, LPARAM)
{
	POINT pt;
	GetCursorPos(&pt);
	TrackPopupMenu(Menu_GetMainMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, pcli->hwndContactList, nullptr);
	return 0;
}

static INT_PTR CLN_ShowStatusMenu(WPARAM, LPARAM)
{
	POINT pt;
	GetCursorPos(&pt);
	TrackPopupMenu(Menu_GetStatusMenu(), TPM_TOPALIGN | TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, pcli->hwndContactList, nullptr);
	return 0;
}

#define MS_CLUI_SHOWMAINMENU	"CList/ShowMainMenu"
#define MS_CLUI_SHOWSTATUSMENU	"CList/ShowStatusMenu"

void LoadCLUIModule(void)
{
	HookEvent(ME_SYSTEM_MODULESLOADED, CluiModulesLoaded);

	WNDCLASS wndclass;
	wndclass.style = 0;
	wndclass.lpfnWndProc = EventAreaWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = g_hInst;
	wndclass.hIcon = nullptr;
	wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)COLOR_3DFACE;
	wndclass.lpszMenuName = nullptr;
	wndclass.lpszClassName = L"EventAreaClass";
	RegisterClass(&wndclass);

	oldhideoffline = db_get_b(NULL, "CList", "HideOffline", SETTING_HIDEOFFLINE_DEFAULT);
	cluiPos.left = db_get_dw(NULL, "CList", "x", 600);
	cluiPos.top = db_get_dw(NULL, "CList", "y", 200);
	cluiPos.right = db_get_dw(NULL, "CList", "Width", 150);
	cluiPos.bottom = db_get_dw(NULL, "CList", "Height", 350);

	LoadExtraIconModule();
	LoadCLUIFramesModule();

	CreateServiceFunction(MS_CLUI_SHOWMAINMENU, CLN_ShowMainMenu);
	CreateServiceFunction(MS_CLUI_SHOWSTATUSMENU, CLN_ShowStatusMenu);

	if (db_get_b(NULL, "CLUI", "FloaterMode", 0)) {
		MessageBox(nullptr,
			TranslateT("You need the FloatingContacts plugin, cause the embedded floating contacts were removed."),
			TranslateT("Warning"), MB_OK | MB_ICONWARNING);
		db_unset(NULL, "CLUI", "FloaterMode");
	}
	
	MF_InitCheck();
}

void OnCreateClc()
{
	HookEvent(ME_MC_DEFAULTTCHANGED, MetaChanged);
	HookEvent(ME_MC_SUBCONTACTSCHANGED, MetaChanged);

	InitGroupMenus();
	LoadExtBkSettingsFromDB();
	PreCreateCLC(pcli->hwndContactList);
}

struct
{
	const wchar_t *tszName;
	int iMask;
}
static clistFontDescr[] =
{
	{ LPGENW("Standard contacts"), FIDF_CLASSGENERAL },
	{ LPGENW("Online contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
	{ LPGENW("Offline contacts"), FIDF_CLASSGENERAL },
	{ LPGENW("Contacts which are 'not on list'"), FIDF_CLASSGENERAL },
	{ LPGENW("Groups"), FIDF_CLASSHEADER },
	{ LPGENW("Group member counts"), FIDF_CLASSHEADER },
	{ LPGENW("Dividers"), FIDF_CLASSSMALL },
	{ LPGENW("Offline contacts to whom you have a different visibility"), FIDF_CLASSGENERAL },
	{ LPGENW("Status mode"), FIDF_CLASSGENERAL },
	{ LPGENW("Frame titles"), FIDF_CLASSGENERAL },
	{ LPGENW("Event area"), FIDF_CLASSGENERAL },
	{ LPGENW("Contact list local time"), FIDF_CLASSGENERAL }
};

void FS_RegisterFonts()
{
	FontIDW fid = { sizeof(fid) };
	wcsncpy(fid.group, LPGENW("Contact list"), _countof(fid.group));
	strncpy(fid.dbSettingsGroup, "CLC", 5);
	fid.flags = FIDF_DEFAULTVALID | FIDF_ALLOWEFFECTS | FIDF_APPENDNAME | FIDF_SAVEPOINTSIZE;

	HDC hdc = GetDC(nullptr);
	for (int i = 0; i < _countof(clistFontDescr); i++) {
		LOGFONT lf;
		pcli->pfnGetFontSetting(i, &lf, &fid.deffontsettings.colour);
		lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72);

		wcsncpy_s(fid.deffontsettings.szFace, lf.lfFaceName, _TRUNCATE);
		fid.deffontsettings.charset = lf.lfCharSet;
		fid.deffontsettings.size = (char)lf.lfHeight;
		fid.deffontsettings.style = (lf.lfWeight >= FW_BOLD ? DBFONTF_BOLD : 0) | (lf.lfItalic ? DBFONTF_ITALIC : 0);

		fid.flags &= ~FIDF_CLASSMASK;
		fid.flags |= clistFontDescr[i].iMask;

		wcsncpy(fid.name, clistFontDescr[i].tszName, _countof(fid.name));

		char idstr[10];
		mir_snprintf(idstr, "Font%d", i);
		strncpy(fid.prefix, idstr, _countof(fid.prefix));
		fid.order = i;
		Font_RegisterW(&fid);
	}
	ReleaseDC(nullptr, hdc);

	// and colours
	ColourIDW colourid = { 0 };
	colourid.cbSize = sizeof(colourid);
	colourid.order = 0;
	strncpy(colourid.dbSettingsGroup, "CLC", sizeof(colourid.dbSettingsGroup));

	strncpy(colourid.setting, "BkColour", sizeof(colourid.setting));
	wcsncpy(colourid.name, LPGENW("Background"), _countof(colourid.name));
	wcsncpy(colourid.group, LPGENW("Contact list"), _countof(colourid.group));
	colourid.defcolour = CLCDEFAULT_BKCOLOUR;
	Colour_RegisterW(&colourid);

	strncpy(colourid.setting, "SelTextColour", sizeof(colourid.setting));
	wcsncpy(colourid.name, LPGENW("Selected text"), _countof(colourid.name));
	colourid.order = 1;
	colourid.defcolour = CLCDEFAULT_SELTEXTCOLOUR;
	Colour_RegisterW(&colourid);

	strncpy(colourid.setting, "HotTextColour", sizeof(colourid.setting));
	wcsncpy(colourid.name, LPGENW("Hottrack text"), _countof(colourid.name));
	colourid.order = 1;
	colourid.defcolour = CLCDEFAULT_HOTTEXTCOLOUR;
	Colour_RegisterW(&colourid);

	strncpy(colourid.setting, "QuickSearchColour", sizeof(colourid.setting));
	wcsncpy(colourid.name, LPGENW("Quicksearch text"), _countof(colourid.name));
	colourid.order = 1;
	colourid.defcolour = CLCDEFAULT_QUICKSEARCHCOLOUR;
	Colour_RegisterW(&colourid);

	strncpy(colourid.dbSettingsGroup, "CLUI", sizeof(colourid.dbSettingsGroup));
	strncpy(colourid.setting, "clr_frameborder", sizeof(colourid.setting));
	wcsncpy(colourid.name, LPGENW("Embedded frames border"), _countof(colourid.name));
	colourid.order = 1;
	colourid.defcolour = RGB(40, 40, 40);
	Colour_RegisterW(&colourid);
}