/*
 * astyle --force-indent=tab=4 --brackets=linux --indent-switches
 *		  --pad=oper --one-line=keep-blocks  --unpad=paren
 *
 * Miranda IM: the free IM client for Microsoft* Windows*
 *
 * Copyright 2000-2009 Miranda ICQ/IM project,
 * all portions of this codebase are copyrighted to the people
 * listed in contributors.txt.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * you should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * part of tabSRMM messaging plugin for Miranda.
 *
 * (C) 2005-2010 by silvercircle _at_ gmail _dot_ com and contributors
 *
 * $Id: taskbar.cpp 13176 2010-12-05 21:29:11Z silvercircle $
 *
 * Windows 7 taskbar integration
 *
 * - overlay icons
 * - custom taskbar thumbnails for aero peek in tabbed containers
 * - read Windows 7 task bar configuration from the registry.
 */
/**
 * how it works:
 *
 * Because of the fact, the DWM does not talk to non-toplevel windows
 * we need an invisible "proxy window" for each tab. This window is a very
 * small and hidden toplevel tool window which is used to communicate
 * with the dwm. Each proxy is associated with the client window (the "tab")
 * and registers itself with the message container window via
 * ITaskbarList3::RegisterTab().
 *
 * Instead of automatically created snapshots of the window content, we
 * use custom generated thumbnails for the task bar buttons, including
 * nickname, UID, status message and avatar. This makes the thumbnails
 * easily recognizable.
 *
 * Thumbnails are generated "on request", only when the desktop window
 * manager needs one.
 *
 * Each proxy window has a CThumbIM or CThumbMUC object which represents
 * the actual thumbnail bitmap.
 */

#include "commonheaders.h"

/**
 * maps MUC event types to icon names for retrieving the "big" icons
 * while generating task bar thumbnails.
 * used by getMUCBigICon()
 */

/*
struct TMUCLargeIconsMap { 
	UINT	eventType;
	char*	szIconDesc;
} MUCLargeIconMap[] = {
	{ GC_EVENT_NICK, "chat_nick" },
	{ GC_EVENT_PART, "chat_part" }
};
*/

CTaskbarInteract* Win7Taskbar = 0;

/**
 * set the overlay icon for a task bar button. Used for typing notifications and incoming
 * message indicator.
 *
 * @param 	hwndDlg HWND: 	container window handle
 * @param 	lParam  LPARAM:	icon handle
 * @return	true if icon has been set and taskbar will accept it, false otherwise
 */
bool CTaskbarInteract::setOverlayIcon(HWND hwndDlg, LPARAM lParam) const
{
	if (m_pTaskbarInterface && m_isEnabled && m_fHaveLargeicons) {
		m_pTaskbarInterface->SetOverlayIcon(hwndDlg,(HICON)lParam, NULL);
		return(true);
	}
	return(false);
}

/**
 * check the task bar status for "large icon mode".
 * @return bool: true if large icons are in use, false otherwise
 */
bool CTaskbarInteract::haveLargeIcons()
{
	m_fHaveLargeicons = false;

	if (m_pTaskbarInterface && m_isEnabled) {
		HKEY 	hKey;
		DWORD 	val = 1;
		DWORD	valGrouping = 2;
		DWORD	size = 4;
		DWORD	dwType = REG_DWORD;
		/*
		 * check whether the taskbar is set to show large icons. This is necessary, because the method SetOverlayIcon()
		 * always returns S_OK, but the icon is simply ignored when using small taskbar icons.
		 * also, figure out the button grouping mode.
		 */
		if(::RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hKey) == ERROR_SUCCESS) {
			::RegQueryValueEx(hKey, _T("TaskbarSmallIcons"), 0, &dwType, (LPBYTE)&val, &size);
			size = 4;
			dwType = REG_DWORD;
			/*
			 * this is the "grouping mode" setting for the task bar. 0 = always combine, no labels
			 */
			::RegQueryValueEx(hKey, _T("TaskbarGlomLevel"), 0, &dwType, (LPBYTE)&valGrouping, &size);
			::RegCloseKey(hKey);
		}
		m_fHaveLargeicons = (val ? false : true);			// small icons in use, revert to default icon feedback
		m_fHaveAlwaysGrouping = (valGrouping == 0 ? true : false);
	}
	return(m_fHaveLargeicons);
}

/**
 * removes the overlay icon for the given container window
 * @param hwndDlg HWND: window handle
 */
void CTaskbarInteract::clearOverlayIcon(HWND hwndDlg) const
{
	if (m_pTaskbarInterface && m_isEnabled)
		m_pTaskbarInterface->SetOverlayIcon(hwndDlg, NULL, NULL);
}

LONG CTaskbarInteract::updateMetrics()
{
	m_IconSize = 32;

	return(m_IconSize);
}

/**
 * register a new task bar button ("tab") for the button group hwndContainer
 * (one of the top level message windows)
 * @param hwndTab			proxy window handle
 * @param hwndContainer		a message container window
 */
void CTaskbarInteract::registerTab(const HWND hwndTab, const HWND hwndContainer) const
{
	if(m_isEnabled) {
		m_pTaskbarInterface->RegisterTab(hwndTab, hwndContainer);
		m_pTaskbarInterface->SetTabOrder(hwndTab, 0);
	}
}

/**
 * remove a previously registered proxy window. The destructor of the proxy
 * window class is using this before destroying the proxy window itself.
 * @param hwndTab	proxy window handle
 */
void CTaskbarInteract::unRegisterTab(const HWND hwndTab) const
{
	if(m_isEnabled)
		m_pTaskbarInterface->UnregisterTab(hwndTab);
}

/**
 * set a tab as active. The active thumbnail will appear with a slightly
 * different background and transparency.
 *
 * @param hwndTab			window handle of the PROXY window to activate
 * 							(don't use the real window handle of the message
 * 							tab, because it is not a toplevel window).
 * @param hwndGroup			window group to which it belongs (usually, the
 * 							container window handle).
 */
void CTaskbarInteract::SetTabActive(const HWND hwndTab, const HWND hwndGroup) const
{
	if(m_isEnabled)
		m_pTaskbarInterface->SetTabActive(hwndTab, hwndGroup, 0);
}

/**
 * create a proxy window object for the given session. Do NOT call this more than once
 * per session and not outside of WM_INITDIALOG after most things are initialized.
 * @param dat		session window data
 *
 * static member function. Ignored when OS is not Windows 7 or global option for
 * Windows 7 task bar support is diabled.
 */
void CProxyWindow::add(TWindowData *dat)
{
	if(PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek) // && (!CSkin::m_skinEnabled || M->GetByte("forceAeroPeek", 0)))
		dat->pWnd = new CProxyWindow(dat);
	else
		dat->pWnd = 0;
}

/**
 * This is called from the broadcasted WM_DWMCOMPOSITIONCHANGED event by all messages
 * sessions. It checks and, if needed, destroys or creates a proxy object, based on
 * the status of the DWM
 * @param dat		session window data
 *
 * static member function
 */
void CProxyWindow::verify(TWindowData *dat)
{
	if(PluginConfig.m_bIsWin7 && PluginConfig.m_useAeroPeek) {
		if(0 == dat->pWnd) {
			dat->pWnd = new CProxyWindow(dat);
			if(dat->pWnd) {
				dat->pWnd->updateIcon(dat->hTabStatusIcon);
				dat->pWnd->updateTitle(dat->cache->getNick());
			}
		}
		else
			dat->pWnd->verifyDwmState();
	}
	/*
	 * this should not happens, but who knows...
	 */
	else {
		if(dat->pWnd) {
			delete dat->pWnd;
			dat->pWnd = 0;
		}
	}
}

/**
 * create the proxy (toplevel) window required to show per tab thumbnails
 * and previews for a message session.
 * each tab has one invisible proxy window
 */
CProxyWindow::CProxyWindow(const TWindowData *dat)
{
	m_dat = dat;
	m_hBigIcon = 0;
	m_thumb = 0;

	m_hwndProxy = ::CreateWindowEx(/*WS_EX_TOOLWINDOW | */WS_EX_NOACTIVATE, PROXYCLASSNAME, _T(""),
		WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION, -32000, -32000, 10, 10, NULL, NULL, g_hInst, (LPVOID)this);

#if defined(__LOGDEBUG_)
	_DebugTraceW(_T("create proxy object for: %s"), m_dat->cache->getNick());
#endif
	Win7Taskbar->registerTab(m_hwndProxy, m_dat->pContainer->hwnd);
	if(CMimAPI::m_pfnDwmSetWindowAttribute) {
		BOOL	fIconic = TRUE;
		BOOL	fHasIconicBitmap = TRUE;

		CMimAPI::m_pfnDwmSetWindowAttribute(m_hwndProxy, DWMWA_FORCE_ICONIC_REPRESENTATION, &fIconic,  sizeof(fIconic));
		CMimAPI::m_pfnDwmSetWindowAttribute(m_hwndProxy, DWMWA_HAS_ICONIC_BITMAP, &fHasIconicBitmap, sizeof(fHasIconicBitmap));
	}
}

CProxyWindow::~CProxyWindow()
{
	Win7Taskbar->unRegisterTab(m_hwndProxy);
	::DestroyWindow(m_hwndProxy);

#if defined(__LOGDEBUG_)
	_DebugTraceW(_T("destroy proxy object for: %s"), m_dat->cache->getNick());
#endif
	if(m_thumb) {
		delete m_thumb;
		m_thumb = 0;
	}
}

/**
 * verify status of DWM when system broadcasts a WM_DWMCOMPOSITIONCHANGED message
 * delete thumbnails, if no longer needed
 */
void CProxyWindow::verifyDwmState()
{
	if(!M->isDwmActive()) {
		if(m_thumb) {
			delete m_thumb;
			m_thumb = 0;
		}
	}
	else {
		/*
		 * force thumbnail recreation
		 */
		m_width = 0;
		m_height = 0;
	}
}

/**
 * send a thumbnail to the DWM. If required, refresh it first.
 * called by WM_DWMSENDICONICTHUMBNAIL handler.
 *
 * @param width		thumbnail width as requested by DWM via lParam
 * @param height	thumbnail height as requested by DWM via lParam
 */
void CProxyWindow::sendThumb(LONG width, LONG height)
{
	if(0 == m_thumb) {
		m_width = width;
		m_height = height;
		if(m_dat->bType == SESSIONTYPE_IM)
			m_thumb = new CThumbIM(this);
		else
			m_thumb = new CThumbMUC(this);
	}
	else if(width != m_width || height != m_height || !m_thumb->isValid()) {
		m_width = width;
		m_height = height;
		m_thumb->update();
	}
	if(m_thumb)
		CMimAPI::m_pfnDwmSetIconicThumbnail(m_hwndProxy, m_thumb->getHBM(), DWM_SIT_DISPLAYFRAME);
}

/**
 * send a live preview image of a given message session to the DWM.
 * called by WM_DWMSENDICONICLIVEPREVIEWBITMAP on DWM's request.
 *
 * The bitmap can be deleted after submitting it, because the DWM
 * will cache a copy of it (and re-request it when its own bitmap cache
 * was purged).
 */
void CProxyWindow::sendPreview()
{
	POINT 			pt = {0};
	RECT			rcContainer;
	HDC				hdc, dc;
	FORMATRANGE 	fr = {0};
	int				twips = (int)(15.0f / PluginConfig.g_DPIscaleY);
	RECT			rcTemp;
	RECT			rcRich, rcLog;
	bool			fIsChat = m_dat->bType == SESSIONTYPE_IM ? false : true;
	TWindowData* 	dat_active = reinterpret_cast<TWindowData *>(::GetWindowLongPtr(m_dat->pContainer->hwndActive, GWLP_USERDATA));

	if(m_thumb && dat_active) {
		HWND 	hwndRich = ::GetDlgItem(m_dat->hwnd, fIsChat ? IDC_CHAT_LOG : IDC_LOG);
		LONG 	cx, cy;
		POINT	ptOrigin = {0}, ptBottom;

		if(m_dat->dwFlags & MWF_NEEDCHECKSIZE) {
			RECT	rcClient;

			::SendMessage(m_dat->pContainer->hwnd, DM_QUERYCLIENTAREA, 0, (LPARAM)&rcClient);
			::MoveWindow(m_dat->hwnd, rcClient.left, rcClient.top, (rcClient.right - rcClient.left), (rcClient.bottom - rcClient.top), FALSE);
			::SendMessage(m_dat->hwnd, WM_SIZE, 0, 0);
			::SendMessage(m_dat->hwnd, DM_FORCESCROLL, 0, 0);
		}
		/*
		 * a minimized container has a null rect as client area, so do not use it
		 * use the last known client area size instead.
		 */

		if(!::IsIconic(m_dat->pContainer->hwnd)) {
			::GetWindowRect(m_dat->pContainer->hwndActive, &rcLog);
			::GetClientRect(m_dat->pContainer->hwnd, &rcContainer);
			pt.x = rcLog.left;
			pt.y = rcLog.top;
			::ScreenToClient(m_dat->pContainer->hwnd, &pt);
		}
		else {
			rcLog = m_dat->pContainer->rcLogSaved;
			rcContainer = m_dat->pContainer->rcSaved;
			pt = m_dat->pContainer->ptLogSaved;
		}

		::GetWindowRect(::GetDlgItem(m_dat->pContainer->hwndActive, dat_active->bType == SESSIONTYPE_IM ? IDC_LOG : IDC_CHAT_LOG), &rcTemp);
		ptBottom.x = rcTemp.left;
		ptBottom.y = rcTemp.bottom;
		::ScreenToClient(m_dat->pContainer->hwnd, &ptBottom);

		cx = rcLog.right - rcLog.left;
		cy = rcLog.bottom - rcLog.top;
		rcRich.left = 0;
		rcRich.top = 0;
		rcRich.right = cx;
		rcRich.bottom = ptBottom.y - pt.y;

		dc = ::GetDC(m_dat->hwnd);
		hdc = ::CreateCompatibleDC(dc);
		HBITMAP hbm = CSkin::CreateAeroCompatibleBitmap(rcContainer, hdc);
		HBITMAP hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(hdc, hbm));

		HBRUSH brb = ::CreateSolidBrush(RGB(20, 20, 20));
		::FillRect(hdc, &rcContainer, brb);
		::DeleteObject(brb);
		CImageItem::SetBitmap32Alpha(hbm, 100);

		LRESULT first = ::SendMessage(hwndRich, EM_CHARFROMPOS, 0, reinterpret_cast<LPARAM>(&ptOrigin));

		/*
		 * paint the content of the message log control into a separate bitmap without
		 * transparency
		 */
		HDC hdcRich = ::CreateCompatibleDC(dc);
		HBITMAP hbmRich = CSkin::CreateAeroCompatibleBitmap(rcRich, hdcRich);
		HBITMAP hbmRichOld = reinterpret_cast<HBITMAP>(::SelectObject(hdcRich, hbmRich));

		COLORREF clr = fIsChat ? M->GetDword(FONTMODULE, SRMSGSET_BKGCOLOUR, SRMSGDEFSET_BKGCOLOUR) : m_dat->pContainer->theme.inbg;
		HBRUSH br = ::CreateSolidBrush(clr);
		::FillRect(hdcRich, &rcRich, br);
		::DeleteObject(br);

		if(m_dat->hwndIEView)
			::SendMessage(m_dat->hwndIEView, WM_PRINT, reinterpret_cast<WPARAM>(hdcRich), PRF_CLIENT | PRF_NONCLIENT);
		else if(m_dat->hwndHPP) {
			CSkin::RenderText(hdcRich, m_dat->hTheme, CTranslator::get(CTranslator::GEN_AEROPEEK_NOHPP),
							  &rcRich, DT_VCENTER | DT_CENTER | DT_WORDBREAK, 10, m_dat->pContainer->theme.fontColors[MSGFONTID_MYMSG], false);
		}
		else {
			rcRich.right *= twips;
			rcRich.bottom *= twips;

			fr.hdc = hdcRich;
			fr.hdcTarget = hdcRich;
			fr.rc = rcRich;
			fr.rcPage = rcRich;
			fr.chrg.cpMax = -1;
			fr.chrg.cpMin = first;

			::SendMessage(hwndRich, EM_FORMATRANGE, 1, reinterpret_cast<LPARAM>(&fr));
		}

		::SelectObject(hdcRich, hbmRichOld);
		CImageItem::SetBitmap32Alpha(hbmRich, 255);
		::SelectObject(hdcRich, hbmRich);
		::BitBlt(hdc, pt.x, pt.y, cx, cy, hdcRich, 0, 0, SRCCOPY);
		::SelectObject(hdcRich, hbmRichOld);
		::DeleteObject(hbmRich);
		::DeleteDC(hdcRich);

		::SelectObject(hdc, hbmOld);
		::DeleteDC(hdc);
		if(CSkin::m_skinEnabled && CSkin::m_frameSkins) {
			pt.x = CSkin::m_SkinnedFrame_left;
			pt.y = CSkin::m_SkinnedFrame_caption + CSkin::m_SkinnedFrame_bottom;
		}
		else
			pt.x = pt.y = 0;
		CMimAPI::m_pfnDwmSetIconicLivePreviewBitmap(m_hwndProxy, hbm, &pt, m_dat->pContainer->dwFlags & CNT_CREATE_MINIMIZED ? 0 : DWM_SIT_DISPLAYFRAME);
		::ReleaseDC(m_dat->hwnd, dc);
		::DeleteObject(hbm);
	}
}

/**
 * set the large icon for the thumbnail. This is mostly used by group chats
 * to indicate last active event in the session.
 *
 * hIcon may be 0 to remove a custom big icon. In that case, the renderer
 * will try to figure out a suitable one, based on session data.
 *
 * @param hIcon			icon handle (should be a 32x32 icon)
 * @param fInvalidate	invalidate the thumbnail (default value = true)
 */
void CProxyWindow::setBigIcon(const HICON hIcon, bool fInvalidate)
{
	m_hBigIcon = hIcon;
	if(fInvalidate)
		Invalidate();
}

/**
 * set a overlay icon for the thumbnail. This is mostly used by group chats
 * to indicate last active event in the session.
 *
 * hIcon may be 0 to remove a custom overlay icon.
 *
 * @param hIcon			icon handle (should be a 16x16 icon)
 * @param fInvalidate	invalidate the thumbnail (default value = true)
 */
void CProxyWindow::setOverlayIcon(const HICON hIcon, bool fInvalidate)
{
	m_hOverlayIcon = hIcon;
	if(fInvalidate)
		Invalidate();
}

/**
 * update the (small) thumbnail icon in front of the title string
 * @param hIcon		new icon handle
 */
void CProxyWindow::updateIcon(const HICON hIcon) const
{
	if(m_hwndProxy && hIcon)
		::SendMessage(m_hwndProxy, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon));
}

/**
 * set the task bar button ("tab") active. This activates the proxy
 * window as a sub-window of the (top level) container window.
 * This is called whenever the active message tab or window changes
 */
void CProxyWindow::activateTab() const
{
	Win7Taskbar->SetTabActive(m_hwndProxy, m_dat->pContainer->hwnd);
}
/**
 * invalidate the thumbnail, it will be recreated at the next request
 * by the DWM
 *
 * this is called from several places whenever a relevant information,
 * represented in a thumbnail image, has changed.
 *
 * also tell the DWM that it must request a new thumb.
 */
void CProxyWindow::Invalidate() const
{
	if(m_thumb) {
		m_thumb->setValid(false);
		/*
		 * tell the DWM to request a new thumbnail for the proxy window m_hwnd
		 * when it needs one.
		 */
		CMimAPI::m_pfnDwmInvalidateIconicBitmaps(m_hwndProxy);
	}
}

/**
 * update the thumb title string (usually, the nickname)
 * @param tszTitle: 	new title string
 */
void CProxyWindow::updateTitle(const TCHAR *tszTitle) const
{
	if(m_hwndProxy && tszTitle)
		::SetWindowText(m_hwndProxy, tszTitle);
}

/**
 * stub window procedure for the custom proxy window class
 * just initialize GWLP_USERDATA and call the object's method
 *
 * static member function
 */
LRESULT CALLBACK CProxyWindow::stubWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	CProxyWindow* pWnd = reinterpret_cast<CProxyWindow *>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));

	if(pWnd)
		return(pWnd->wndProc(hWnd, msg, wParam, lParam));

	switch(msg) {
		case WM_NCCREATE: {
			CREATESTRUCT *cs = reinterpret_cast<CREATESTRUCT *>(lParam);
			CProxyWindow *pWnd = reinterpret_cast<CProxyWindow *>(cs->lpCreateParams);
			::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<UINT_PTR>(pWnd));
			return(pWnd->wndProc(hWnd, msg, wParam, lParam));
		}
		default:
			break;
	}
	return(::DefWindowProc(hWnd, msg, wParam, lParam));
}

/**
 * window procedure for the proxy window
 */
LRESULT CALLBACK CProxyWindow::wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg) {

#if defined(__LOGDEBUG_)
		case WM_NCCREATE:
			_DebugTraceW(_T("create proxy WINDOW for: %s"), m_dat->cache->getNick());
			break;
#endif
        case WM_CLOSE: {
        	TContainerData* pC = m_dat->pContainer;

        	if(m_dat->hwnd != pC->hwndActive)
        		SendMessage(m_dat->hwnd, WM_CLOSE, 1, 3);
        	else
        		SendMessage(m_dat->hwnd, WM_CLOSE, 1, 2);
        	if(!IsIconic(pC->hwnd))
        		SetForegroundWindow(pC->hwnd);
        	return(0);
        }

			/*
			 * proxy window was activated by clicking on the thumbnail. Send this
			 * to the real message window.
			 */
		case WM_ACTIVATE:
			if(WA_ACTIVE == wParam) {
				if(IsWindow(m_dat->hwnd))
					::PostMessage(m_dat->hwnd, DM_ACTIVATEME, 0, 0);
				return(0);			// no default processing, avoid flickering.
			}
			break;

		case WM_NCDESTROY:
			::SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
#if defined(__LOGDEBUG_)
			_DebugTraceW(_T("destroy proxy WINDOW for: %s"), m_dat->cache->getNick());
#endif
			break;

        case WM_DWMSENDICONICTHUMBNAIL:
        	sendThumb(HIWORD(lParam), LOWORD(lParam));
        	return(0);

        case WM_DWMSENDICONICLIVEPREVIEWBITMAP:
       		sendPreview();
            return(0);

		default:
			break;
	}
	return(::DefWindowProc(hWnd, msg, wParam, lParam));
}

/**
 * base thumbnail class. Create the background and common parts for a
 * thumbnail
 *
 * @param _p			 owner proxy window object
 * @return
 */
CThumbBase::CThumbBase(const CProxyWindow* _p)
{
	m_pWnd = _p;
	m_hbmThumb = 0;
	renderBase();
}

/**
 * render base for a thumbnail. This creates the background, the large icon
 * and the basic status mode text. It also divides the thumbnail rectangle
 * into a few content rectangles used later by the content renderer.
 */
void CThumbBase::renderBase()
{
	HICON	hIcon = 0;
	HBRUSH	brBack;
	LONG	lIconSize = 32;

	m_width = m_pWnd->getWidth();
	m_height = m_pWnd->getHeight();
	m_dat = m_pWnd->getDat();
	m_dtFlags = 0;
	m_hOldFont = 0;

#if defined(__LOGDEBUG_)
	_DebugTraceW(_T("refresh base (background) with %d, %d"), m_width, m_height);
#endif

	m_rc.right = m_width;
	m_rc.bottom = m_height;
	m_rc.left = m_rc.top = 0;

	if(m_hbmThumb) {
		::DeleteObject(m_hbmThumb);
		m_hbmThumb = 0;
	}

	HDC dc = ::GetDC(m_pWnd->getHwnd());
	m_hdc = ::CreateCompatibleDC(dc);

	m_hbmThumb = CSkin::CreateAeroCompatibleBitmap(m_rc, m_hdc);
	m_hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(m_hdc, m_hbmThumb));
	ReleaseDC(m_pWnd->getHwnd(), dc);

	brBack = ::CreateSolidBrush(m_dat->dwUnread ? RGB(80, 60, 60) : RGB(60, 60, 60));
	::FillRect(m_hdc, &m_rc, brBack);
	::DeleteObject(brBack);

	::SelectObject(m_hdc, m_hbmOld);
	CImageItem::SetBitmap32Alpha(m_hbmThumb, m_dat->dwUnread ? 110 : 60);
	m_hbmOld = reinterpret_cast<HBITMAP>(::SelectObject(m_hdc, m_hbmThumb));

	SetBkMode(m_hdc, TRANSPARENT);

	m_hOldFont = reinterpret_cast<HFONT>(::SelectObject(m_hdc, CInfoPanel::m_ipConfig.hFonts[IPFONTID_STATUS]));
	::GetTextExtentPoint32A(m_hdc, "A", 1, &m_sz);

	InflateRect(&m_rc, -3, -3);

	setupRect();
	hIcon = m_pWnd->getBigIcon();

	if(0 == hIcon) {
		if(m_dat->dwUnread) {
			if(PluginConfig.g_IconMsgEventBig)
				hIcon = PluginConfig.g_IconMsgEventBig;
			else {
				hIcon = PluginConfig.g_IconMsgEvent;
				lIconSize = 16;
			}
		}
		else {
			hIcon = reinterpret_cast<HICON>(LoadSkinnedProtoIconBig(m_dat->cache->getActiveProto(), m_dat->cache->getActiveStatus()));
			if(0 == hIcon || reinterpret_cast<HICON>(CALLSERVICE_NOTFOUND) == hIcon) {
				hIcon = reinterpret_cast<HICON>(LoadSkinnedProtoIcon(m_dat->cache->getActiveProto(), m_dat->cache->getActiveStatus()));
				lIconSize = 16;
			}
		}
	}
	::DrawIconEx(m_hdc, m_rcIcon.right / 2 - lIconSize / 2, m_rcIcon.top, hIcon, lIconSize, lIconSize, 0, 0, DI_NORMAL);
	hIcon = m_pWnd->getOverlayIcon();
	if(hIcon)
		::DrawIconEx(m_hdc, m_rcIcon.right - 16, m_rcIcon.top + 16, hIcon, 16, 16, 0, 0, DI_NORMAL);

	m_rcIcon.top += (lIconSize + 3);
	CSkin::RenderText(m_hdc, m_dat->hTheme, m_dat->szStatus, &m_rcIcon, m_dtFlags | DT_CENTER | DT_WORD_ELLIPSIS, 10, 0, true);
	if(m_dat->dwUnread && SESSIONTYPE_IM == m_dat->bType) {
		wchar_t	tszTemp[30];

		m_rcIcon.top += m_sz.cy;
		mir_sntprintf(tszTemp, 30, CTranslator::get(CTranslator::GEN_TASKBAR_STRING_UNREAD), m_dat->dwUnread);
		CSkin::RenderText(m_hdc, m_dat->hTheme, tszTemp, &m_rcIcon, m_dtFlags | DT_CENTER | DT_WORD_ELLIPSIS, 10, 0, true);
	}
	m_rcIcon= m_rcTop;
	m_rcIcon.top += 2;
	m_rcIcon.left = m_rc.right / 3;
	m_cx = m_rcIcon.right - m_rcIcon.left;
	m_cy = m_rcIcon.bottom - m_rcIcon.top;
}

/**
 * divide space into content rectangles for normal thumbnails
 */
void CThumbBase::setupRect()
{
	if(SESSIONTYPE_IM == m_pWnd->getDat()->bType) {
		m_rcTop = m_rc;
		m_rcBottom = m_rc;
		m_rcBottom.top = m_rc.bottom - ( 2 * (m_rcBottom.bottom / 5)) - 2;
		m_rcTop.bottom = m_rcBottom.top - 2;

		m_rcIcon = m_rcTop;
		m_rcIcon.right = m_rc.right / 3;
	}
	else {
		m_rcTop = m_rc;
		m_rcBottom = m_rc;
		m_rcBottom.top = m_rc.bottom - ( 2 * (m_rcBottom.bottom / 5)) - 2;
		m_rcTop.bottom = m_rcBottom.top - 2;

		m_rcIcon = m_rcTop;
		m_rcIcon.right = m_rc.left + 42;
	}
}

/**
 * destroy the thumbnail object. Just delete the bitmap we cached
 * @return
 */
CThumbBase::~CThumbBase()
{
	if(m_hbmThumb) {
		::DeleteObject(m_hbmThumb);
		m_hbmThumb = 0;
		m_isValid = false;
	}
#if defined(__LOGDEBUG_)
	_DebugTraceW(_T("destroy CThumbBase"));
#endif
}

/**
 * create a IM session thumbnail. base class will create the
 * bitmap and render the background.
 *
 * @param _p	our owner (CProxyWindow object)
 */
CThumbIM::CThumbIM(const CProxyWindow* _p) : CThumbBase(_p)
{
	renderContent();
	setValid(true);
}

/**
 * update the thumbnail, render everything and set it valid
 */
void CThumbIM::update()
{
	renderBase();
	renderContent();
	setValid(true);
}

/**
 * render the content for an IM chat session thumbnail
 * m_hdc etc. must already be initialized (done by the constructor)
 * background had been already rendered
 */
void CThumbIM::renderContent()
{
	HBITMAP			hbmAvatar, hbmOldAv;
	double			dNewWidth = 0.0, dNewHeight = 0.0;
	bool			fFree = false;
	HRGN			hRgn = 0;
	HDC				dc;
	const wchar_t*	tszStatusMsg = 0;

	hbmAvatar = (m_dat->ace && m_dat->ace->hbmPic) ? m_dat->ace->hbmPic : PluginConfig.g_hbmUnknown;
	Utils::scaleAvatarHeightLimited(hbmAvatar, dNewWidth, dNewHeight, m_rcIcon.bottom - m_rcIcon.top);

	HBITMAP hbmResized = CSkin::ResizeBitmap(hbmAvatar, dNewWidth, dNewHeight, fFree);

	dc = CreateCompatibleDC(m_hdc);
	hbmOldAv = reinterpret_cast<HBITMAP>(::SelectObject(dc, hbmResized));

	LONG xOff = m_rcIcon.right - (LONG)dNewWidth - 2;
	LONG yOff = (m_cy - (LONG)dNewHeight) / 2 + m_rcIcon.top;

	hRgn = ::CreateRectRgn(xOff - 1, yOff - 1, xOff + (LONG)dNewWidth + 2, yOff + (LONG)dNewHeight + 2);
	CSkin::m_default_bf.SourceConstantAlpha = 150;
	CMimAPI::m_MyAlphaBlend(m_hdc, xOff, yOff, (LONG)dNewWidth, (LONG)dNewHeight, dc, 0, 0, (LONG)dNewWidth, (LONG)dNewHeight, CSkin::m_default_bf);
	CSkin::m_default_bf.SourceConstantAlpha = 255;
	::FrameRgn(m_hdc, hRgn, reinterpret_cast<HBRUSH>(::GetStockObject(BLACK_BRUSH)), 1, 1);

	::DeleteObject(hRgn);
	::SelectObject(dc, hbmOldAv);

	if(hbmResized != hbmAvatar)
		::DeleteObject(hbmResized);

	::DeleteDC(dc);
	m_rcBottom.bottom -= 16;

	/*
	 * status message and bottom line (either UID or nick name, depending on
	 * task bar grouping mode). For chat rooms, it is the topic.
	 */
	if((m_rcBottom.bottom - m_rcBottom.top) < 2 * m_sz.cy)
		m_dtFlags |= DT_SINGLELINE;

	m_rcBottom.bottom -= ((m_rcBottom.bottom - m_rcBottom.top) % m_sz.cy);		// adjust to a multiple of line height

	if(0 == (tszStatusMsg = m_dat->cache->getStatusMsg()))
		tszStatusMsg = CTranslator::get(CTranslator::GEN_NO_STATUS);

	CSkin::RenderText(m_hdc, m_dat->hTheme, tszStatusMsg, &m_rcBottom, DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | m_dtFlags, 10, 0, true);
	m_rcBottom.bottom = m_rc.bottom;
	m_rcBottom.top = m_rcBottom.bottom - m_sz.cy - 2;
	CSkin::RenderText(m_hdc, m_dat->hTheme, Win7Taskbar->haveAlwaysGroupingMode() ? m_dat->cache->getUIN() : m_dat->cache->getNick(),
					  &m_rcBottom, m_dtFlags | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS, 10, 0, true);

	/*
	 * finalize it
	 * do NOT delete the bitmap, the dwm will need the handle
	 * m_hbm is deleted when a new thumbnail is generated on dwm's request.
	 * this is not a leak!
	 */
	if(m_hOldFont)
		::SelectObject(m_hdc, m_hOldFont);

	::SelectObject(m_hdc, m_hbmOld);
	::DeleteDC(m_hdc);
}

/**
 * create a MUC session thumbnail. base class will create the
 * bitmap and render the background.
 *
 * @param _p	our owner (CProxyWindow object)
 * @return
 */
CThumbMUC::CThumbMUC(const CProxyWindow* _p) : CThumbBase(_p)
{
	renderContent();
	setValid(true);
}

/**
 * update an invalidated thumbnail
 */
void CThumbMUC::update()
{
	renderBase();
	renderContent();
	setValid(true);
}

/**
 * render content area for a MUC thumbnail
 */
void CThumbMUC::renderContent()
{
	if(m_dat->si) {
		const MODULEINFO*	mi = MM_FindModule(m_dat->si->pszModule);
		wchar_t				szTemp[250];
		const wchar_t*		szStatusMsg = 0;

		if(mi) {
			if(m_dat->dwUnread) {
				mir_sntprintf(szTemp, 30, CTranslator::get(CTranslator::GEN_TASKBAR_STRING_UNREAD), m_dat->dwUnread);
				CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
				m_rcIcon.top += m_sz.cy;
			}
			if(m_dat->si->iType != GCW_SERVER) {
				wchar_t* _p = NULL;
				if ( m_dat->si->ptszStatusbarText )
					_p = wcschr(m_dat->si->ptszStatusbarText, ']');
				if( _p ) {
					_p++;
					wchar_t	_t = *_p;
					*_p = 0;
					mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_CHAT_ROOM), m_dat->si->ptszStatusbarText);
					*_p = _t;
				}
				else
					mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_CHAT_ROOM), L"");
				CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
				m_rcIcon.top += m_sz.cy;
				mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_USERS), m_dat->si->nUsersInNicklist);
				CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
			}
			else {
				mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::GEN_TASKBAR_STRING_SERVER_WINDOW));
				CSkin::RenderText(m_hdc, m_dat->hTheme, szTemp, &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
				if(mi->tszIdleMsg[0] && _tcslen(mi->tszIdleMsg) > 2) {
					m_rcIcon.top += m_sz.cy;
					CSkin::RenderText(m_hdc, m_dat->hTheme, &mi->tszIdleMsg[2], &m_rcIcon, m_dtFlags | DT_SINGLELINE | DT_RIGHT, 10, 0, true);
				}
			}
		}

		if((m_rcBottom.bottom - m_rcBottom.top) < 2 * m_sz.cy)
			m_dtFlags |= DT_SINGLELINE;

		m_rcBottom.bottom -= ((m_rcBottom.bottom - m_rcBottom.top) % m_sz.cy);		// adjust to a multiple of line height

		if(m_dat->si->iType != GCW_SERVER) {
			if(0 == (szStatusMsg = m_dat->si->ptszTopic))
				szStatusMsg = CTranslator::get(CTranslator::GEN_MUC_NO_TOPIC);
		}
		else if(mi) {
			mir_sntprintf(szTemp, SIZEOF(szTemp), CTranslator::get(CTranslator::MUC_SBAR_ON_SERVER), m_dat->szMyNickname, mi->ptszModDispName, L"");
			szStatusMsg = szTemp;
		}

		CSkin::RenderText(m_hdc, m_dat->hTheme, szStatusMsg, &m_rcBottom, DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | m_dtFlags, 10, 0, true);
	}
	/*
	 * finalize it
	 * do NOT delete the bitmap, the dwm will need the handle
	 * m_hbm is deleted when a new thumbnail is generated on dwm's request.
	 * this is not a leak!
	 */
	if(m_hOldFont)
		::SelectObject(m_hdc, m_hOldFont);

	::SelectObject(m_hdc, m_hbmOld);
	::DeleteDC(m_hdc);
}
