From f3d44bc057201407373012b7f682881bda7b3d98 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Wed, 23 May 2012 07:44:30 +0000 Subject: some includes restored, mir_full.sln updated, some renaming of folders and projects git-svn-id: http://svn.miranda-ng.org/main/trunk@140 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/TabSRMM/src/taskbar.cpp | 956 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 956 insertions(+) create mode 100644 plugins/TabSRMM/src/taskbar.cpp (limited to 'plugins/TabSRMM/src/taskbar.cpp') diff --git a/plugins/TabSRMM/src/taskbar.cpp b/plugins/TabSRMM/src/taskbar.cpp new file mode 100644 index 0000000000..dfe645bd4a --- /dev/null +++ b/plugins/TabSRMM/src/taskbar.cpp @@ -0,0 +1,956 @@ +/* + * 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 13178 2010-12-05 21:29:17Z 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(::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(::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(&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(::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(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(&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(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(::GetWindowLongPtr(hWnd, GWLP_USERDATA)); + + if(pWnd) + return(pWnd->wndProc(hWnd, msg, wParam, lParam)); + + switch(msg) { + case WM_NCCREATE: { + CREATESTRUCT *cs = reinterpret_cast(lParam); + CProxyWindow *pWnd = reinterpret_cast(cs->lpCreateParams); + ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(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(::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(::SelectObject(m_hdc, m_hbmThumb)); + + SetBkMode(m_hdc, TRANSPARENT); + + m_hOldFont = reinterpret_cast(::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(LoadSkinnedProtoIconBig(m_dat->cache->getActiveProto(), m_dat->cache->getActiveStatus())); + if(0 == hIcon || reinterpret_cast(CALLSERVICE_NOTFOUND) == hIcon) { + hIcon = reinterpret_cast(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(::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(::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); +} -- cgit v1.2.3