2025-11-27 16:46:48 +09:00

2417 lines
57 KiB
C++

// ==========================================================================
// Class Implementation :
// COXCaptionInfo & COXCaptionPainter
// ==========================================================================
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// //////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include <shlwapi.h>
#include "OXCaptionPainter.h"
#include "UTBStrOp.h"
#include "UTB64Bit.h"
#include "WTypes.h"
#ifndef __OXMFCIMPL_H__
#if _MFC_VER >= 0x0700
#if _MFC_VER >= 1400
#include <afxtempl.h>
#endif
#include <..\src\mfc\afximpl.h>
#else
#include <..\src\afximpl.h>
#endif
#define __OXMFCIMPL_H__
#endif
#ifndef OXCP_NO_SAVESTATE
#include "OXRegistryValFile.h"
#endif // OXCP_NO_SAVESTATE
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define IDT_UPDATECAPTION 421
#define ID_UPDATECAPTION_DELAY 300
static void PaintRect(CDC* pDC, int x, int y, int w, int h, COLORREF color);
static BOOL IsSmallFont(BOOL& bIsSmall);
UINT COXCaptionPainter::m_nSetCaptionPainter=
RegisterWindowMessage(_T("_SET_CAPTION_PAINTER_"));
UINT COXCaptionPainter::m_nGetCaptionPainter=
RegisterWindowMessage(_T("_GET_CAPTION_PAINTER_"));
//////////////////////////////////////////////////////////
IMPLEMENT_SERIAL(COXCaptionInfo, CObject, 1)
// Constructor
COXCaptionInfo::COXCaptionInfo()
{
Reset();
}
// Sets COXCaptionInfo properties to their default value
void COXCaptionInfo::Reset()
{
m_bGradient=TRUE;
m_nGradientAlignment=ID_GRADIENT_LEFT;
m_nGradientAlgorithm=ID_GRADIENT_SQUARE;
m_nTextFormat=DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
m_nNumberShade=64;
m_clrBackground=ID_OXCP_COLOR_NONE;
m_clrText=ID_OXCP_COLOR_NONE;
if((HFONT)m_font!=NULL)
m_font.DeleteObject();
if((HFONT)m_fontSm!=NULL)
m_fontSm.DeleteObject();
}
// Returns color used to fill caption. If an user preffered not to set any
// custom color then function returns standard color.
COLORREF COXCaptionInfo::GetBackgroundColor(BOOL bActive) const
{
if(m_clrBackground==ID_OXCP_COLOR_NONE)
{
if(bActive)
{
return GetSysColor(COLOR_ACTIVECAPTION);
// return RGB(255,0,0);
}
else
{
return GetSysColor(COLOR_INACTIVECAPTION);
// return RGB(0,255,0);
}
}
return m_clrBackground;
}
// Returns color used to draw text in caption. If an user preffered not to set any
// custom color then function returns standard color.
COLORREF COXCaptionInfo::GetTextColor(BOOL bActive) const
{
if(m_clrText==ID_OXCP_COLOR_NONE)
{
if(bActive)
return ::GetSysColor(COLOR_CAPTIONTEXT);
else
return ::GetSysColor(COLOR_INACTIVECAPTIONTEXT);
}
return m_clrText;
}
// returns TRUE if succeeds and sets plf to the LOGINFO of the font used to draw text,
// otherwise returns FALSE and plf is undefined
BOOL COXCaptionInfo::GetCaptionLogFont(LOGFONT* plf) const
{
if((HFONT)m_font==NULL)
{
BOOL bIsSmall;
IsSmallFont(bIsSmall);
NONCLIENTMETRICS ncm;
ncm.cbSize=sizeof(NONCLIENTMETRICS);
if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,0,&ncm,0))
{
CFont font;
if(font.CreateFontIndirect(&ncm.lfCaptionFont))
return font.GetObject(sizeof(*plf),plf);
}
return FALSE;
}
return m_font.GetObject(sizeof(*plf),plf);
}
// returns TRUE if succeeds and sets plf to the LOGINFO of the font used to
// draw text (Small caption), otherwise returns FALSE and plf is undefined
BOOL COXCaptionInfo::GetSmCaptionLogFont(LOGFONT* plf) const
{
if((HFONT)m_fontSm==NULL)
{
NONCLIENTMETRICS ncm;
ncm.cbSize=sizeof(NONCLIENTMETRICS);
if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS,0,&ncm,0))
{
CFont font;
if(font.CreateFontIndirect(&ncm.lfSmCaptionFont))
return font.GetObject(sizeof(*plf),plf);
}
return FALSE;
}
return m_fontSm.GetObject(sizeof(*plf),plf);
}
// returns TRUE if succeeds and creates font used to draw text from plf,
// otherwise returns FALSE. If plf is NULL then any custom font previosly
// set by SetCaptionLogFont function will be deleted (i.e. standard font
// will be used to draw text in caption)
BOOL COXCaptionInfo::SetCaptionLogFont(LOGFONT* plf)
{
if ((HFONT)m_font!=NULL)
m_font.DeleteObject();
if(plf!=NULL)
return m_font.CreateFontIndirect(plf);
return TRUE;
}
// returns TRUE if succeeds and creates font used to draw text from plf,
// otherwise returns FALSE. If plf is NULL then any custom font previosly
// set by SetCaptionLogFont function will be deleted (i.e. standard font
// will be used to draw text in caption)
BOOL COXCaptionInfo::SetSmCaptionLogFont(LOGFONT* plf)
{
if ((HFONT)m_fontSm!=NULL)
m_fontSm.DeleteObject();
if(plf!=NULL)
return m_fontSm.CreateFontIndirect(plf);
return TRUE;
}
//////////////////////////////////////////////
// copy constructor.
COXCaptionInfo& COXCaptionInfo::operator=(const COXCaptionInfo& ci)
{
ASSERT_VALID(this);
ASSERT_VALID(&ci);
if(this==&ci)
return *this;
m_clrBackground=ci.m_clrBackground;
m_clrText=ci.m_clrText;
m_bGradient=ci.m_bGradient;
m_nGradientAlignment=ci.m_nGradientAlignment;
m_nGradientAlgorithm=ci.m_nGradientAlgorithm;
m_nNumberShade=ci.m_nNumberShade;
LOGFONT lf;
if ((HFONT)m_font!=NULL)
m_font.DeleteObject();
if(ci.GetCaptionLogFont(&lf))
SetCaptionLogFont(&lf);
if ((HFONT)m_fontSm!=NULL)
m_fontSm.DeleteObject();
if(ci.GetSmCaptionLogFont(&lf))
SetSmCaptionLogFont(&lf);
m_nTextFormat=ci.m_nTextFormat;
return *this;
}
/////////////////////////////////////////////////////////////////////////////
// serialization
void COXCaptionInfo::Serialize(CArchive& ar)
{
// Only CObject-derived objects and six data-type
// primitives are serializable. However, you
// can cast any data type to a serializable data type,
// and then you can serialize your data. The serializable
// data types are
// BYTE: 8 bits unsigned
// WORD: 16 bits unsigned
// LONG: 32 bits unsigned
// DWORD: 32 bits unsigned
// float 32 bits
// double 64 bits, IEEE standard
if (ar.IsStoring())
{
ar << m_clrBackground;
ar << m_bGradient;
ar << m_nGradientAlignment;
ar << m_nGradientAlgorithm;
ar << m_nTextFormat;
ar << m_clrText;
ar << m_nNumberShade;
}
else
{
ar >> m_clrBackground;
ar >> m_bGradient;
ar >> m_nGradientAlignment;
ar >> m_nGradientAlgorithm;
ar >> m_nTextFormat;
ar >> m_clrText;
ar >> m_nNumberShade;
}
SerializeFont(ar,&m_font);
SerializeFont(ar,&m_fontSm);
}
// helper function to serialize any font to opened archive
void COXCaptionInfo::SerializeFont(CArchive& ar, CFont* pFont)
{
// Only CObject-derived objects and six data-type
// primitives are serializable. However, you
// can cast any data type to a serializable data type,
// and then you can serialize your data. The serializable
// data types are
// BYTE: 8 bits unsigned
// WORD: 16 bits unsigned
// LONG: 32 bits unsigned
// DWORD: 32 bits unsigned
// float 32 bits
// double 64 bits, IEEE standard
LOGFONT lf;
if (ar.IsStoring())
{
if((HFONT)*pFont==NULL)
{
ar << FALSE;
}
else
{
ar << TRUE;
if(!pFont->GetLogFont(&lf))
{
// Throw the same exception as before on older compilers, for compatibility
#if _MSC_VER >= 1400
AfxThrowArchiveException(CArchiveException::badClass);
#else
AfxThrowArchiveException(CArchiveException::generic);
#endif
}
ar << lf.lfHeight;
ar << lf.lfWidth;
ar << lf.lfEscapement;
ar << lf.lfOrientation;
ar << lf.lfWeight;
ar << lf.lfItalic;
ar << lf.lfUnderline;
ar << lf.lfStrikeOut;
ar << lf.lfCharSet;
ar << lf.lfOutPrecision;
ar << lf.lfClipPrecision;
ar << lf.lfQuality;
ar << lf.lfPitchAndFamily;
CString string=lf.lfFaceName;
ar << string;
}
}
else
{
if ((HFONT)*pFont!=NULL)
{
pFont->DeleteObject();
}
BOOL bFontWasSaved;
ar >> bFontWasSaved;
if(bFontWasSaved)
{
ar >> lf.lfHeight;
ar >> lf.lfWidth;
ar >> lf.lfEscapement;
ar >> lf.lfOrientation;
ar >> lf.lfWeight;
ar >> lf.lfItalic;
ar >> lf.lfUnderline;
ar >> lf.lfStrikeOut;
ar >> lf.lfCharSet;
ar >> lf.lfOutPrecision;
ar >> lf.lfClipPrecision;
ar >> lf.lfQuality;
ar >> lf.lfPitchAndFamily;
CString string;
ar >> string;
try
{
UTBStr::tcscpy(lf.lfFaceName, string.GetLength() + 1, string.GetBuffer(LF_FACESIZE));
}
catch(CMemoryException* e)
{
UNREFERENCED_PARAMETER(e);
// Throw the same exception as before on older compilers, for compatibility
#if _MSC_VER >= 1400
AfxThrowArchiveException(CArchiveException::badClass);
#else
AfxThrowArchiveException(CArchiveException::generic);
#endif
}
if(!pFont->CreateFontIndirect(&lf))
{
// Throw the same exception as before on older compilers, for compatibility
#if _MSC_VER >= 1400
AfxThrowArchiveException(CArchiveException::badClass);
#else
AfxThrowArchiveException(CArchiveException::generic);
#endif
}
}
}
}
//////////////////////////////////////
IMPLEMENT_DYNAMIC(COXCaptionPainter, COXHookWnd);
// Constructor
COXCaptionPainter::COXCaptionPainter()
{
Reset();
m_bHackStyleSet=FALSE;
m_bHackStyleExSet=FALSE;
m_bAnimation=FALSE;
m_bActive=TRUE;
m_bUpdate=FALSE;
m_sWindowText.Empty();
m_nIdleFlags=0;
}
// Destructor
COXCaptionPainter::~COXCaptionPainter()
{
}
#ifndef PACKVERSION
#define PACKVERSION(major,minor) MAKELONG(minor,major)
#endif
DWORD COXCaptionPainter::GetDllVersion(LPCTSTR lpszDllName)
{
HINSTANCE hinstDll;
DWORD dwVersion = 0;
hinstDll = LoadLibrary(lpszDllName);
if(hinstDll)
{
DLLGETVERSIONPROC pDllGetVersion;
pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hinstDll, "DllGetVersion");
/*Because some DLLs may not implement this function, you
*must test for it explicitly. Depending on the particular
*DLL, the lack of a DllGetVersion function may
*be a useful indicator of the version.
*/
if(pDllGetVersion)
{
DLLVERSIONINFO dvi;
HRESULT hr;
ZeroMemory(&dvi, sizeof(dvi));
dvi.cbSize = sizeof(dvi);
hr = (*pDllGetVersion)(&dvi);
if(SUCCEEDED(hr))
{
dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
}
}
FreeLibrary(hinstDll);
}
return dwVersion;
}
BOOL COXCaptionPainter::IsAppSkinned()
{
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
::GetVersionEx(&osvi);
if (osvi.dwMajorVersion >= 5 && osvi.dwMinorVersion >= 1)
{
// We have XP or higher
// Call ::IsAppThemed()
HMODULE hLib = ::LoadLibrary(_T("Uxtheme.dll"));
typedef int (WINAPI* LPISAPPTHEMED) (void);
LPISAPPTHEMED pIsAppThemed = (LPISAPPTHEMED) GetProcAddress(hLib, "IsAppThemed");
BOOL bThemed = pIsAppThemed();
::FreeLibrary(hLib);
if (bThemed)
{
// Check is we are using common controls v.6.0
if(GetDllVersion(_T("comctl32.dll")) >= PACKVERSION(6,00))
return TRUE; // we have the XP skins on
else
return FALSE;
}
else
return FALSE;
}
else
return FALSE;
}
// Attach caption handler.
BOOL COXCaptionPainter::Attach(CWnd* pWnd)
{
if (IsAppSkinned())
return FALSE; // we have Windows XP or higher and the skins are turned on,
// making the caption painter obsolete
ASSERT_VALID(pWnd);
ASSERT(::IsWindow(pWnd->m_hWnd));
DWORD dwStyle=pWnd->GetStyle();
if((dwStyle&WS_CAPTION)==WS_CAPTION)
{
HookWindow(pWnd);
m_nColorBits=GetNumColorBits();
if(pWnd->SetTimer(IDT_UPDATECAPTION,ID_UPDATECAPTION_DELAY,NULL)!=
IDT_UPDATECAPTION)
{
UnhookWindow();
return FALSE;
}
return TRUE;
}
return FALSE;
}
// Detach caption handler. Called by default when hooked window is about
// to be destroyed.
void COXCaptionPainter::Detach()
{
UnhookWindow();
}
// sets caption properties
void COXCaptionPainter::SetCaptionInfo(COXCaptionInfo* pCI, BOOL bActive)
{
ASSERT_VALID(pCI);
if(bActive)
m_ciActive=*pCI;
else
m_ciInactive=*pCI;
}
// retrievs any COXCaptionPainter object associated with any CWnd
BOOL COXCaptionPainter::GetCaptionPainter(CWnd* pWnd, COXCaptionPainter* pCP)
{
ASSERT_VALID(pWnd);
ASSERT(::IsWindow(pWnd->m_hWnd));
return (BOOL)pWnd->SendMessage(m_nGetCaptionPainter,0,(LPARAM)pCP);
}
// sets properties of existed COXCaptionPainter object to the COXCaptionPainter
// associated with any window
BOOL COXCaptionPainter::SetCaptionPainter(CWnd* pWnd, COXCaptionPainter* pCP)
{
ASSERT_VALID(pWnd);
ASSERT(::IsWindow(pWnd->m_hWnd));
return (BOOL)pWnd->SendMessage(m_nSetCaptionPainter,0,(LPARAM)pCP);
}
// Sets/removes hooked window's style
void COXCaptionPainter::SetHackStyle(LONG dwStyle, UINT nAction)
{
ASSERT(nAction==ID_OXHACK_ADDSTYLE || nAction==ID_OXHACK_REMOVESTYLE);
ASSERT(::IsWindow(m_hWndHooked));
ASSERT_VALID(this);
if(!m_bHackStyleSet)
{
ASSERT(GetHookedWnd()!=NULL);
// save original size
m_dwSavedStyle=GetHookedWnd()->GetStyle();
switch(nAction)
{
case ID_OXHACK_ADDSTYLE:
{
::SetWindowLongPtr(m_hWndHooked,GWL_STYLE,m_dwSavedStyle|dwStyle);
break;
}
case ID_OXHACK_REMOVESTYLE:
{
::SetWindowLongPtr(m_hWndHooked,GWL_STYLE,(m_dwSavedStyle & ~dwStyle));
break;
}
}
m_bHackStyleSet=TRUE;
}
}
// Sets hooked window's original style
void COXCaptionPainter::RemoveHackStyle()
{
ASSERT(::IsWindow(m_hWndHooked));
ASSERT_VALID(this);
if(m_bHackStyleSet)
{
ASSERT(m_hWndHooked!=NULL);
// have to put it here
m_bHackStyleSet=FALSE;
::SetWindowLongPtr(m_hWndHooked, GWL_STYLE, m_dwSavedStyle);
}
}
// Sets/removes hooked window's extended style
void COXCaptionPainter::SetHackStyleEx(LONG dwStyleEx, UINT nAction)
{
ASSERT(nAction==ID_OXHACK_ADDSTYLE || nAction==ID_OXHACK_REMOVESTYLE);
ASSERT(::IsWindow(m_hWndHooked));
ASSERT_VALID(this);
if(!m_bHackStyleExSet)
{
ASSERT(m_hWndHooked!=NULL);
// save original extended size
m_dwSavedStyleEx=GetHookedWnd()->GetExStyle();
switch(nAction)
{
case ID_OXHACK_ADDSTYLE:
{
::SetWindowLongPtr(m_hWndHooked, GWL_EXSTYLE,
m_dwSavedStyleEx|dwStyleEx);
break;
}
case ID_OXHACK_REMOVESTYLE:
{
::SetWindowLongPtr(m_hWndHooked, GWL_EXSTYLE,
m_dwSavedStyleEx & ~ dwStyleEx);
break;
}
}
m_bHackStyleExSet=TRUE;
}
}
// Sets hooked window's original extended style
void COXCaptionPainter::RemoveHackStyleEx()
{
ASSERT(::IsWindow(m_hWndHooked));
ASSERT_VALID(this);
if(m_bHackStyleExSet)
{
ASSERT(m_hWndHooked!=NULL);
// have to put it here
m_bHackStyleExSet=FALSE;
::SetWindowLongPtr(m_hWndHooked, GWL_EXSTYLE, m_dwSavedStyleEx);
}
}
// Handle animation effects
LRESULT COXCaptionPainter::HackAnimation(UINT msg, WPARAM wp, LPARAM lp)
{
ASSERT(::IsWindow(m_hWndHooked));
LRESULT result;
if(!m_bAnimation)
{
// animation structure
ANIMATIONINFO aniInfo;
aniInfo.cbSize=sizeof(ANIMATIONINFO);
SystemParametersInfo(SPI_GETANIMATION,sizeof(ANIMATIONINFO),&aniInfo,0);
// save original state
BOOL bAnimated=aniInfo.iMinAnimate==0 ? FALSE : TRUE;
if(bAnimated)
{
aniInfo.iMinAnimate=0;
SystemParametersInfo(SPI_SETANIMATION,sizeof(ANIMATIONINFO),
&aniInfo,0);
}
result=COXHookWnd::WindowProc(msg,wp,lp);
// animation
if(bAnimated)
{
// load original state
aniInfo.iMinAnimate=1;
SystemParametersInfo(SPI_SETANIMATION,sizeof(ANIMATIONINFO),&aniInfo,0);
}
}
else
{
result=COXHookWnd::WindowProc(msg,wp,lp);
}
return result;
}
////////////////////////////////////////////////////////////////////////////
// Message handler handles caption-related and other messages
//
LRESULT COXCaptionPainter::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
ASSERT(::IsWindow(m_hWndHooked));
// The purpose of SetHackStyle() and SetHackStyleEx() functions is to
// force Windows to handle some messages in a special way. As soon as
// the message is handled we have to restore the original settings calling
// RemoveHackStyle() and RemoveHackStyleEx(). The best place for that is
// when next message for hooked window will be handled. But when SetHackStyle()
// or SetHackStyleEx() are called then WM_STYLECHANGING and WM_STYLECHANGED
// messages are sent. So we have to treat them a little bit differently.
if(msg!=WM_STYLECHANGING && msg!=WM_STYLECHANGED)
{
UINT oldMsg=msg;
RemoveHackStyle();
RemoveHackStyleEx();
msg=oldMsg;
}
// first of all handle or registered messages
if(msg==m_nGetCaptionPainter)
{
ASSERT_VALID((COXCaptionPainter*)lp);
((COXCaptionPainter*)lp)->Reset();
((COXCaptionPainter*)lp)->SetCaptionInfo(GetCaptionInfo(TRUE),TRUE);
((COXCaptionPainter*)lp)->SetCaptionInfo(GetCaptionInfo(FALSE),FALSE);
return TRUE;
}
else if(msg==m_nSetCaptionPainter)
{
ASSERT_VALID((COXCaptionPainter*)lp);
Reset();
SetCaptionInfo(((COXCaptionPainter*)lp)->GetCaptionInfo(TRUE),TRUE);
SetCaptionInfo(((COXCaptionPainter*)lp)->GetCaptionInfo(FALSE),FALSE);
return TRUE;
}
// Handle all messages which standard handlers could draw in caption rect.
// I am sure you'll be amased to know that there are so many places where
// Windows draws different element of caption. It is mostly due to problem
// with MDI window. Just look through the code and you will figure out
// how Windows inconsistent internally.
switch (msg)
{
case WM_HELP:
case WM_MENUSELECT:
case WM_INITMENUPOPUP:
case WM_CANCELMODE:
{
LRESULT result=COXHookWnd::WindowProc(msg,wp,lp);
DrawCaption();
return result;
}
case WM_SETCURSOR:
{
if(LOWORD(lp)==HTBOTTOM || LOWORD(lp)==HTBOTTOMLEFT ||
LOWORD(lp)==HTBOTTOMRIGHT || LOWORD(lp)==HTLEFT ||
LOWORD(lp)==HTRIGHT || LOWORD(lp)==HTTOP ||
LOWORD(lp)==HTTOPLEFT || LOWORD(lp)==HTTOPRIGHT)
{
SetHackStyle(WS_CAPTION);
LRESULT result=COXHookWnd::WindowProc(msg,wp,lp);
RemoveHackStyle();
return result;
}
break;
}
case WM_NCLBUTTONDOWN:
{
return OnNCLButtonDown(wp,lp);
}
case WM_NCLBUTTONDBLCLK:
{
if(wp==HTCAPTION && (GetHookedWnd()->GetStyle()&WS_MAXIMIZEBOX)!=0)
{
LRESULT result=::SendMessage(m_hWndHooked,WM_SYSCOMMAND,
(GetHookedWnd()->IsZoomed() ? SC_RESTORE : SC_MAXIMIZE),0);
DrawCaption();
return result;
}
break;
}
case WM_SYSCOMMAND:
{
if(wp==SC_MINIMIZE || wp==SC_MAXIMIZE ||
wp==SC_RESTORE || wp==SC_CLOSE)
{
DrawCaption();
if(wp!=SC_CLOSE)
{
return HackAnimation(msg,wp,lp);
}
}
break;
}
case WM_COMMAND:
{
if(LOWORD(wp)==ID_WINDOW_TILE_HORZ ||
LOWORD(wp)==ID_WINDOW_TILE_VERT ||
LOWORD(wp)==ID_WINDOW_CASCADE ||
LOWORD(wp)==ID_WINDOW_ARRANGE)
{
return HackAnimation(msg,wp,lp);
}
break;
}
case WM_SIZE:
{
if(GetHookedWnd()->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)))
{
if(wp==SIZE_MAXIMIZED || wp==SIZE_MINIMIZED || wp==SIZE_RESTORED)
{
LRESULT result=COXHookWnd::WindowProc(msg,wp,lp);
DrawCaption();
return result;
}
}
break;
}
case WM_NCPAINT:
{
OnNcPaint(HRGN(wp));
return 0;
}
case WM_NCACTIVATE:
{
return OnNcActivate((BOOL)wp);
}
case WM_SETTEXT:
{
OnSetText((LPCTSTR)lp);
return TRUE;
}
case WM_SYSCOLORCHANGE:
case WM_SETTINGCHANGE:
{
m_nColorBits=GetNumColorBits();
Reset();
DrawCaption();
return 0;
}
case WM_STYLECHANGED:
{
LRESULT result=COXHookWnd::WindowProc(msg,wp,lp);
Reset();
DrawCaption();
return result;
}
case WM_TIMER:
{
if(wp==IDT_UPDATECAPTION)
{
// force new bitmap
CString sText;
GetHookedWnd()->GetWindowText(sText);
if(sText!=m_sWindowText)
{
Reset();
DrawCaption();
}
else
{
// redraw the toolbar if necessary
if(m_nIdleFlags & oxidleRedrawCaption)
{
if(GetHookedWnd()->IsWindowVisible())
{
Reset();
DrawCaption();
}
}
}
return 0L;
}
}
case WM_DESTROY:
{
GetHookedWnd()->KillTimer(IDT_UPDATECAPTION);
break;
}
}
// I don't handle it: pass along
return COXHookWnd::WindowProc(msg,wp,lp);
}
/////////////////
// Handle WM_NCPAINT for main window
//
void COXCaptionPainter::OnNcPaint(HRGN hRgn)
{
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd=*GetHookedWnd();
// caption rectangle in window coordinates
CRect rect;
GetCaptionRect(rect);
// window rect
CRect rectWindow;
// .. get window rect
wnd.GetWindowRect(&rectWindow);
// convert caption rect to screen coords
rect+=rectWindow.TopLeft();
// Don't bother painting if the caption doesn't lie within the region.
//
if ((WORD)hRgn>1 && !::RectInRegion(hRgn,&rect))
{
// just do default thing
Default();
return;
}
// Exclude caption from update region
//
HRGN hRgnCaption=::CreateRectRgnIndirect(&rect);
HRGN hRgnNew=::CreateRectRgnIndirect(&rect);
if((WORD)hRgn>1)
{
// wParam is a valid region: subtract caption from it
::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
}
else
{
// wParam is not a valid region: create one that's the whole
// window minus the caption bar
//
HRGN hRgnAll = ::CreateRectRgnIndirect(&rectWindow);
CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
DeleteObject(hRgnAll);
}
// Call Windows to do WM_NCPAINT with altered update region
//
MSG& msg=AfxGetThreadState()->m_lastSentMsg;
// save original wParam
WPARAM oldWP=msg.wParam;
// set new region for DefWindowProc
msg.wParam=(WPARAM)hRgnNew;
Default();
// clean up
DeleteObject(hRgnCaption);
DeleteObject(hRgnNew);
// restore original wParam
msg.wParam=oldWP;
// Now paint the special caption
DrawCaption();
}
//////////////////
// Handle WM_NCACTIVATE for main window
//
BOOL COXCaptionPainter::OnNcActivate(BOOL bActive)
{
ASSERT(::IsWindow(m_hWndHooked));
// Handle it differently for MDIFrame window
if(GetHookedWnd()->IsKindOf(RUNTIME_CLASS(CFrameWnd)))
{
CFrameWnd& frame=*((CFrameWnd*)GetHookedWnd());
ASSERT_KINDOF(CFrameWnd, &frame);
// Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
if (frame.m_nFlags & WF_STAYACTIVE)
{
bActive = TRUE;
}
if (!frame.IsWindowEnabled())
{
// but not if disabled
bActive = FALSE;
}
if (bActive==m_bActive && bActive==FALSE)
{
// nothing to do
DrawCaption();
return TRUE;
}
// In case this is a MDI app, manually activate/paint active MDI child
// window, because Windows won't do it if parent frame is invisible.
// Must do this BEFORE calling Default, or it will not work.
//
if (!m_bUpdate)
{
m_bUpdate=TRUE;
CFrameWnd* pActiveFrame = frame.GetActiveFrame();
if (pActiveFrame)
{
pActiveFrame->SendMessage(WM_NCACTIVATE,TRUE);
pActiveFrame->SendMessage(WM_NCPAINT, 1);
}
CWnd* pWnd=frame.GetNextWindow();
CWnd* pStartWnd=pWnd;
while (pWnd)
{
CFrameWnd* pFrame=DYNAMIC_DOWNCAST(CFrameWnd, pWnd);
if (pFrame && pFrame!=pActiveFrame)
{
pFrame->SendMessage(WM_NCACTIVATE,FALSE);
}
pWnd=pWnd->GetNextWindow();
}
pWnd=pStartWnd;
if (pWnd)
pWnd=pWnd->GetNextWindow(GW_HWNDPREV);
while (pWnd)
{
CFrameWnd* pFrame=DYNAMIC_DOWNCAST(CFrameWnd, pWnd);
if (pFrame /*&& pFrame!=pActiveFrame*/)
{
pFrame->SendMessage(WM_NCACTIVATE,FALSE);
}
pWnd=pWnd->GetNextWindow(GW_HWNDPREV);
}
m_bUpdate=FALSE;
}
// Turn WS_CAPTION off before calling DefWindowProc,
// so DefWindowProc won't paint and thereby cause flicker.
//
MSG& msg=AfxGetThreadState()->m_lastSentMsg;
msg.wParam=bActive;
Default();
// RemoveHackStyle();
// At this point, nothing has happened (since WS_CAPTION was off).
// Now it's time to paint.
//
// update state
m_bActive = bActive;
// paint non-client area (frame too)
frame.SendMessage(WM_NCPAINT);
return TRUE;
}
else
{
CWnd& wnd = *GetHookedWnd();
if (bActive==m_bActive)
{
// nothing to do
return TRUE;
}
// SetHackStyle(WS_CAPTION);
MSG& msg=AfxGetThreadState()->m_lastSentMsg;
msg.wParam=bActive;
Default();
// RemoveHackStyle();
// Now it's time to paint.
//
// update state
m_bActive = bActive;
// paint non-client area (frame too)
wnd.SendMessage(WM_NCPAINT);
return TRUE;
}
}
//////////////////
// Handle WM_SETTEXT for main window
//
void COXCaptionPainter::OnSetText(LPCTSTR lpText)
{
UNREFERENCED_PARAMETER(lpText);
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd=*GetHookedWnd();
// Turn WS_CAPTION style off before calling Windows to set the text,
// then turn it back on again after.
//
// When MDIChild window is maximized then it draws the caption of
// its parent, MDIFrame window. if this is a case then we have to
// apply hack to parent window.
CMDIFrameWnd* pFrameWnd=NULL;
BOOL bMDIFrameChanged=FALSE;
BOOL bMaximizedMDIChild=FALSE;
BOOL bIsMDIChild=wnd.IsKindOf(RUNTIME_CLASS(CMDIChildWnd));
BOOL bIsMDIFrame=wnd.IsKindOf(RUNTIME_CLASS(CMDIFrameWnd));
ASSERT(!(bIsMDIChild&bIsMDIFrame));
if(bIsMDIChild)
{
pFrameWnd=((CMDIChildWnd*)&wnd)->GetMDIFrame();
if(pFrameWnd!=NULL)
{
BOOL bMaximize=FALSE;
CWnd* pActiveWnd=pFrameWnd->MDIGetActive(&bMaximize);
if(pActiveWnd->GetSafeHwnd()==wnd.GetSafeHwnd() && bMaximize)
{
pFrameWnd->ModifyStyle(WS_VISIBLE,0);
bMDIFrameChanged=TRUE;
}
}
}
else if(bIsMDIFrame)
{
pFrameWnd=(CMDIFrameWnd*)GetHookedWnd();
BOOL bMaximize=FALSE;
CWnd* pActiveWnd=pFrameWnd->MDIGetActive(&bMaximize);
if(pActiveWnd!=NULL && bMaximize)
{
SetHackStyle(WS_VISIBLE);
bMaximizedMDIChild=TRUE;
}
}
if(!bMDIFrameChanged && !bMaximizedMDIChild)
{
SetHackStyle(WS_CAPTION);
}
Default();
if(bMDIFrameChanged)
{
pFrameWnd->ModifyStyle(0,WS_VISIBLE);
}
else
{
RemoveHackStyle();
}
// force new bitmap
Reset();
DrawCaption();
}
//////////////////
// Handle WM_NCLBUTTONDOWN for main window
//
LRESULT COXCaptionPainter::OnNCLButtonDown(WPARAM wp, LPARAM lp)
{
// Treat caption area in a special way
if(wp==HTCAPTION)
{
if(!m_bActive)
{
if(!GetHookedWnd()->IsKindOf(RUNTIME_CLASS(CMDIChildWnd)))
{
GetHookedWnd()->SendMessage(WM_ACTIVATE,TRUE);
DrawCaption();
}
else
{
CWnd* pWnd=GetHookedWnd()->GetParent();
if(pWnd!=NULL)
{
pWnd->SendMessage(WM_ACTIVATE,TRUE);
DrawCaption();
}
}
}
// It sounds crazy but we have to handle it this way.
if(m_nColorBits<=8 &&
!GetHookedWnd()->IsKindOf(RUNTIME_CLASS(CMiniDockFrameWnd)))
{
return GetHookedWnd()->SendMessage(WM_SYSCOMMAND,SC_MOVE);
}
}
// area of caption's buttons is treated differently
if(wp!=HTCLOSE && wp!=HTHELP && wp!=HTMAXBUTTON &&
wp!=HTMINBUTTON && wp!=HTREDUCE && wp!=HTZOOM)
{
SetHackStyle(WS_CAPTION);
}
LRESULT result=COXHookWnd::WindowProc(WM_NCLBUTTONDOWN, wp, lp);
// in result of operation the window might have been destroyed
if(!IsHooked())
return result;
if(wp!=HTCLOSE && wp!=HTHELP && wp!=HTMAXBUTTON &&
wp!=HTMINBUTTON && wp!=HTREDUCE && wp!=HTZOOM)
{
RemoveHackStyle();
}
if(wp==HTHELP)
{
DrawCaption();
}
return result;
}
//////////////////
// Paint custom caption. Flag tells whether active or not. Just blast the
// bitmap to the title bar, but not if minimized (iconic).
//
void COXCaptionPainter::DrawCaption()
{
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd=*GetHookedWnd();
m_nIdleFlags&=~oxidleRedrawCaption;
// Get caption DC and rectangle
//
// window DC
CWindowDC dc(&wnd);
// memory DC
CDC dcCompatible;
dcCompatible.CreateCompatibleDC(&dc);
CRect rect;
// get caption rectangle
GetCaptionRect(rect);
// if the saved size of caption doesn't match the real one then mark
// bitmaps for both active/inactive state as "dirty"
if(m_sizeCaption!=rect.Size())
{
m_sizeCaption=rect.Size();
// invalidate bitmaps
m_bmActive.DeleteObject();
m_bmInactive.DeleteObject();
}
// Get active/inactive bitmap & determine if needs to be regenerated
CBitmap &bm=m_bActive ? m_bmActive : m_bmInactive;
COXCaptionInfo &ci=m_bActive ? m_ciActive : m_ciInactive;
BOOL bPaint=FALSE;
if(bm.GetSafeHandle()==NULL)
{
// no bitmap:
bm.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
bPaint=TRUE;
}
// select bitmap into memory DC
CBitmap* pOldBitmap=dcCompatible.SelectObject(&bm);
// If bitmap needs painting then do it.
if (bPaint)
{
// first of all draw background
DrawCaptionBackground(&dcCompatible,&ci,&rect);
CSize sizeConstraints(0,0);
CSize sizeCaption(rect.Width(),rect.Height());
// then draw icon
sizeConstraints.cx=DrawCaptionIcon(&dcCompatible,&ci,&sizeCaption);
sizeConstraints.cy=DrawCaptionButtons(&dcCompatible,&ci,&sizeCaption);
DrawCaptionText(&dcCompatible,&ci,&sizeCaption,&sizeConstraints);
}
// blast bits to screen
dc.BitBlt(rect.left,rect.top,rect.Width(),rect.Height(),
&dcCompatible,0,0,SRCCOPY);
dcCompatible.SelectObject(pOldBitmap); // restore DC
}
////////////////
// Draw caption background.
//
// Compute new color brush for each band from x to x + xDelta.
// Excel uses a linear algorithm from black to normal, i.e.
//
// color = CaptionColor * r
//
// where r is the ratio x/w, which ranges from 0 (x=0, left)
// to 1 (x=w, right). This results in a mostly black title bar,
// since we humans don't distinguish dark colors as well as light
// ones. So instead, I use the formula
//
// color = CaptionColor * [1-(1-r)^2]
//
// which still equals black when r=0 and CaptionColor when r=1,
// but spends more time near CaptionColor. For example, when r=.5,
// the multiplier is [1-(1-.5)^2] = .75, closer to 1 than .5.
// I leave the algebra to the reader to verify that the above formula
// is equivalent to
//
// color = CaptionColor - (CaptionColor*(w-x)*(w-x))/(w*w)
//
// The computation looks horrendous, but it's only done once each
// time the caption changes size; thereafter BitBlt'ed to the screen.
//
void COXCaptionPainter::DrawCaptionBackground(CDC* pDC, const COXCaptionInfo* pCI,
const CRect* pCaptionRect)
{
ASSERT(::IsWindow(m_hWndHooked));
ASSERT_VALID(pCI);
ASSERT(pDC);
int cxCap=pCaptionRect->Width();
int cyCap=pCaptionRect->Height();
// red, green and blue color vals
COLORREF clrBackground=pCI->GetBackgroundColor(m_bActive);
int red=GetRValue(clrBackground);
int green=GetGValue(clrBackground);
int blue=GetBValue(clrBackground);
if(!pCI->GetGradient())
{
PaintRect(pDC,0,0,cxCap,cyCap,clrBackground);
}
else
{
int nCurBlock;
// width of area to shade and width squared
int nWidth, nWidth_x_2;
// width of one shade band
int nDelta;
UINT nAlignment=pCI->GetGradientAlignment();
UINT nAlgorithm=pCI->GetGradientAlgorithm();
UINT nNumberShade=pCI->GetNumberShade();
switch(nAlignment)
{
case ID_GRADIENT_LEFT:
{
nCurBlock=cxCap;
nWidth=cxCap;
nWidth_x_2=cxCap*cxCap;
nDelta=__max(nWidth/nNumberShade,1);
while(nCurBlock>0)
{
switch(nAlgorithm)
{
case ID_GRADIENT_LINEAR:
{
// paint bands right to left
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB((red*nCurBlock)/nWidth,
(green*nCurBlock)/nWidth,
(blue*nCurBlock)/nWidth));
break;
}
case ID_GRADIENT_SQUARE:
{
// paint bands right to left
int nRest_x_2=(nWidth-nCurBlock)*(nWidth-nCurBlock);
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
break;
}
}
// next band
nCurBlock-=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,0,0,nCurBlock+nDelta,cyCap,RGB(0,0,0));
break;
}
case ID_GRADIENT_CENTER:
{
nCurBlock=cxCap/2;
nWidth=cxCap/2;
nWidth_x_2=cxCap*cxCap/4;
nDelta=__max(nWidth/(2*nNumberShade),1);
while(nCurBlock>0)
{
switch(nAlgorithm)
{
case ID_GRADIENT_LINEAR:
{
// paint bands right to left
PaintRect(pDC, nWidth+nCurBlock, 0, nDelta, cyCap,
RGB((red*nCurBlock)/nWidth,
(green*nCurBlock)/nWidth,
(blue*nCurBlock)/nWidth));
break;
}
case ID_GRADIENT_SQUARE:
{
// paint bands right to left
int nRest_x_2=(nWidth-nCurBlock)*(nWidth-nCurBlock);
PaintRect(pDC, nWidth+nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
break;
}
}
// next band
nCurBlock-=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,nWidth,0,nCurBlock+nDelta,cyCap,RGB(0,0,0));
nCurBlock=0;
while(nCurBlock<=nWidth)
{
switch(nAlgorithm)
{
case ID_GRADIENT_LINEAR:
{
// paint bands left to right
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nCurBlock)/nWidth,
green-(green*nCurBlock)/nWidth,
blue-(blue*nCurBlock)/nWidth));
break;
}
case ID_GRADIENT_SQUARE:
{
// paint bands left to right
int nRest_x_2=nCurBlock*nCurBlock;
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
break;
}
}
// next band
nCurBlock+=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,nCurBlock-nDelta,0,nWidth-nCurBlock+nDelta,
cyCap,RGB(0,0,0));
break;
}
case ID_GRADIENT_RIGHT:
{
nCurBlock=0;
nWidth=cxCap;
nWidth_x_2=cxCap*cxCap;
nDelta=__max(nWidth/nNumberShade,1);
while(nCurBlock<nWidth)
{
switch(nAlgorithm)
{
case ID_GRADIENT_LINEAR:
{
// paint bands left to right
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nCurBlock)/nWidth,
green-(green*nCurBlock)/nWidth,
blue-(blue*nCurBlock)/nWidth));
break;
}
case ID_GRADIENT_SQUARE:
{
// paint bands left to right
int nRest_x_2=nCurBlock*nCurBlock;
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
break;
}
}
// next band
nCurBlock+=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,nCurBlock-nDelta,0,nWidth-nCurBlock+nDelta-1,
cyCap,RGB(0,0,0));
break;
}
}
}
}
////////////////
// Draw caption icon
//
int COXCaptionPainter::DrawCaptionIcon(CDC* pDC, const COXCaptionInfo* pCI,
const CSize* pCaptionSize)
{
UNREFERENCED_PARAMETER(pCI);
UNREFERENCED_PARAMETER(pCaptionSize);
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd = *GetHookedWnd();
ASSERT_VALID(pCI);
ASSERT(pDC);
if((wnd.GetStyle() & WS_SYSMENU)!=0)
{
HICON hIcon = (HICON)(UINT_PTR)GetClassLongPtr(wnd.m_hWnd, GCL_HICONSM);
if(hIcon==NULL)
hIcon=wnd.GetIcon(FALSE);
if(hIcon)
{
// Within the basic button rectangle, Windows 95 uses a 1 or 2 pixel border
// Icon has 2 pixel border on left, 1 pixel on top/bottom, 0 right
//
int cxIcon=GetSystemMetrics(SM_CXSIZE);
CRect rect(0, 0, cxIcon, GetSystemMetrics(SM_CYSIZE));
rect.DeflateRect(2,1,0,1);
DrawIconEx(pDC->m_hDC, rect.left, rect.top, hIcon,
rect.Width(), rect.Height(), 0, NULL, DI_NORMAL);
return cxIcon+2;
}
}
return 2;
}
////////////////
// Draw min, max/restore, close buttons.
//
int COXCaptionPainter::DrawCaptionButtons(CDC* pDC, const COXCaptionInfo* pCI,
const CSize* pCaptionSize)
{
UNREFERENCED_PARAMETER(pCI);
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd=*GetHookedWnd();
ASSERT_VALID(pCI);
ASSERT(pDC);
int nButtonsWidth=0;
DWORD dwStyle=wnd.GetStyle();
int cxIcon;
int cyIcon;
if((GetHookedWnd()->GetExStyle() & WS_EX_TOOLWINDOW)==WS_EX_TOOLWINDOW)
{
cxIcon=GetSystemMetrics(SM_CXSMSIZE);
cyIcon=GetSystemMetrics(SM_CYSMSIZE);
}
else
{
cxIcon=GetSystemMetrics(SM_CXSIZE);
cyIcon=GetSystemMetrics(SM_CYSIZE);
}
BOOL bCloseBox=(dwStyle & WS_SYSMENU)!=0;
BOOL bMaxBox=bCloseBox &
((dwStyle&WS_MAXIMIZEBOX)!=0 || (dwStyle&WS_MINIMIZEBOX)!=0);
BOOL bMinBox=bCloseBox &
((dwStyle&WS_MINIMIZEBOX)!=0 || (dwStyle&WS_MAXIMIZEBOX)!=0);
BOOL bHelpBox=bCloseBox & !bMaxBox & !bMinBox &
((wnd.GetExStyle()&WS_EX_CONTEXTHELP)!=0);
//changed 11/17/99
BOOL bCloseBoxGrayed=FALSE;
BOOL bMaxBoxGrayed=FALSE;
BOOL bMinBoxGrayed=FALSE;
BOOL bHelpBoxGrayed=FALSE;
// Draw caption buttons. These are all drawn inside a rectangle
// of dimensions SM_CXSIZE by SM_CYSIZE
CRect rect(pCaptionSize->cx-cxIcon, 0, pCaptionSize->cx, cyIcon);
if(bCloseBox)
{
// Close box has a 2 pixel border on all sides but left, which is zero
rect.DeflateRect(0,2,2,2);
UINT nState=DFCS_CAPTIONCLOSE;
nState|=(bCloseBoxGrayed?DFCS_INACTIVE:NULL);
pDC->DrawFrameControl(&rect, DFC_CAPTION, nState);
nButtonsWidth+=cxIcon;
}
// Max/restore button is like close box; just shift rectangle left
if(bMaxBox)
{
ASSERT(bCloseBox);
rect-=CPoint(cxIcon,0);
UINT nState=wnd.IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX;
if((dwStyle&WS_MAXIMIZEBOX)==0)
{
ASSERT(bMinBox);
nState|=DFCS_INACTIVE;
}
nState|=(bMaxBoxGrayed?DFCS_INACTIVE:NULL);
pDC->DrawFrameControl(&rect, DFC_CAPTION, nState);
nButtonsWidth+=cxIcon;
}
// Minimize button has 2 pixel border on all sides but right.
if(bMinBox)
{
ASSERT(bCloseBox);
rect-=CPoint(cxIcon-2,0);
UINT nState=(wnd.IsIconic() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN);
if((dwStyle&WS_MINIMIZEBOX)==0)
{
ASSERT(bMaxBox);
nState|=DFCS_INACTIVE;
}
nState|=(bMinBoxGrayed?DFCS_INACTIVE:NULL);
pDC->DrawFrameControl(&rect, DFC_CAPTION, nState);
nButtonsWidth+=cxIcon;
}
// help button, if any.
if(bHelpBox)
{
ASSERT(bCloseBox);
rect-=CPoint(cxIcon, 0);
UINT nState=DFCS_CAPTIONHELP;
nState|=(bHelpBoxGrayed?DFCS_INACTIVE:NULL);
pDC->DrawFrameControl(&rect, DFC_CAPTION, nState);
nButtonsWidth+=cxIcon;
}
return nButtonsWidth;
}
////////////////
// Draw caption background.
//
void COXCaptionPainter::DrawCaptionText(CDC* pDC, const COXCaptionInfo* pCI,
const CSize* pCaptionSize,
const CSize* pIndentsSize)
{
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd=*GetHookedWnd();
ASSERT_VALID(pCI);
ASSERT(pDC);
int cxCap=pCaptionSize->cx-(pIndentsSize->cx+pIndentsSize->cy);
int cyCap=pCaptionSize->cy;
wnd.GetWindowText(m_sWindowText);
if(m_sWindowText.IsEmpty() || cxCap<=0)
return;
CFont font;
CFont* pOldFont=NULL;
BOOL bUseFont=FALSE;
LOGFONT lf;
BOOL bResult=FALSE;
if((GetHookedWnd()->GetExStyle() & WS_EX_TOOLWINDOW)==WS_EX_TOOLWINDOW)
{
bResult=pCI->GetSmCaptionLogFont(&lf);
}
else
{
bResult=pCI->GetCaptionLogFont(&lf);
}
if(bResult && font.CreateFontIndirect(&lf))
{
pOldFont=pDC->SelectObject(&font);
bUseFont=TRUE;
}
COLORREF clr=pCI->GetTextColor(m_bActive);
pDC->SetTextColor(clr);
CRect rect(pIndentsSize->cx,0,pIndentsSize->cx+cxCap,cyCap);
// draw on top of our shading
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(m_sWindowText, &rect, pCI->GetTextFormat());
if(bUseFont)
{
ASSERT(pOldFont!=NULL);
// Restore DC
pDC->SelectObject(pOldFont);
}
}
void COXCaptionPainter::GetCaptionRect(CRect& rect)
{
ASSERT(::IsWindow(m_hWndHooked));
CWnd& wnd=*GetHookedWnd();
DWORD dwStyle=wnd.GetStyle();
// Get size of frame around window
CSize szFrame=(dwStyle & WS_THICKFRAME) ?
CSize(GetSystemMetrics(SM_CXSIZEFRAME), GetSystemMetrics(SM_CYSIZEFRAME)) :
CSize(GetSystemMetrics(SM_CXFIXEDFRAME), GetSystemMetrics(SM_CYFIXEDFRAME));
// Compute rectangle
//
// window rect in screen coords
wnd.GetWindowRect(rect);
// shift origin to (0,0)
rect-=CPoint(rect.left,rect.top);
// frame
rect.left+=szFrame.cx;
// frame
rect.right-=szFrame.cx;
// top = end of frame
rect.top+=szFrame.cy;
// height of caption minus gray shadow border
rect.bottom=rect.top+
(((GetHookedWnd()->GetExStyle() & WS_EX_TOOLWINDOW)==WS_EX_TOOLWINDOW) ?
GetSystemMetrics(SM_CYSMCAPTION) : GetSystemMetrics(SM_CYCAPTION))-
GetSystemMetrics(SM_CYBORDER);
if(wnd.IsIconic())
{
rect.InflateRect(1,1,1,-1);
}
}
UINT COXCaptionPainter::GetNumColorBits()
{
ASSERT(::IsWindow(m_hWndHooked));
// get the number of bits in current system pallete
CWindowDC dc(GetHookedWnd());
return dc.GetDeviceCaps(BITSPIXEL);
}
//////////////////////////////////////
#ifndef OXCP_NO_SAVESTATE
//////////////////////////////////////
BOOL COXCaptionPainter::SaveState(LPCTSTR lpszProfileName)
{
#ifndef _MAC
CWinApp* pApp=AfxGetApp();
// make sure you called CWinApp::SetRegistryKey() functions before
if(pApp->m_pszRegistryKey==NULL || pApp->m_pszProfileName==NULL)
{
TRACE(_T("COXCaptionPainter::SaveState: haven't called SetRegistryKey()\n"));
return FALSE;
}
// we use default registry key assigned to your application by MFC
HKEY hSecKey=pApp->GetSectionKey(_T(""));
if (hSecKey==NULL)
{
TRACE(_T("COXCaptionPainter::SaveState: unable to get section key\n"));
return FALSE;
}
BOOL bResult=TRUE;
try
{
COXRegistryValFile regActive(hSecKey, lpszProfileName, _T("Active"));
CArchive arActive(&regActive, CArchive::store);
m_ciActive.Serialize(arActive);
arActive.Close();
COXRegistryValFile regInactive(hSecKey, lpszProfileName, _T("Inactive"));
CArchive arInactive(&regInactive, CArchive::store);
m_ciInactive.Serialize(arInactive);
arInactive.Close();
}
catch(CException* e)
{
UNREFERENCED_PARAMETER(e);
bResult=FALSE;
}
::RegCloseKey(hSecKey);
return bResult;
#else
return FALSE;
#endif
}
BOOL COXCaptionPainter::LoadState(LPCTSTR lpszProfileName, BOOL bApply/*=TRUE*/)
{
#ifndef _MAC
CWinApp* pApp=AfxGetApp();
// make sure you called CWinApp::SetRegistryKey() functions before
if(pApp->m_pszRegistryKey==NULL || pApp->m_pszProfileName==NULL)
{
TRACE(_T("COXCaptionPainter::SaveState: haven't called SetRegistryKey()\n"));
return FALSE;
}
// we use default registry key assigned to your application by MFC
HKEY hSecKey=pApp->GetSectionKey(_T(""));
if (hSecKey==NULL)
{
TRACE(_T("COXCaptionPainter::SaveState: unable to get section key\n"));
return FALSE;
}
BOOL bResult=TRUE;
try
{
COXRegistryValFile regActive(hSecKey, lpszProfileName, _T("Active"));
if(regActive.GetLength()>0)
{
CArchive arActive(&regActive, CArchive::load);
m_ciActive.Serialize(arActive);
arActive.Close();
}
COXRegistryValFile regInactive(hSecKey, lpszProfileName, _T("Inactive"));
if(regInactive.GetLength()>0)
{
CArchive arInactive(&regInactive, CArchive::load);
m_ciInactive.Serialize(arInactive);
arInactive.Close();
}
}
catch(CException* e)
{
UNREFERENCED_PARAMETER(e);
bResult=FALSE;
}
::RegCloseKey(hSecKey);
if(bResult && bApply)
{
Reset();
CWnd* pWnd = GetHookedWnd();
if (pWnd != NULL)
pWnd->SendMessage(WM_NCPAINT);
}
return bResult;
#else
return FALSE;
#endif
}
//////////////////////////////////////
#endif // OXCP_NO_SAVESTATE
//////////////////////////////////////
//////////////////////////////////////////////////////////////////
//////////////////
// Helper to paint rectangle with a color.
//
static void PaintRect(CDC* pDC, int x, int y, int w, int h, COLORREF color)
{
CBrush brush(color);
CBrush* pOldBrush = pDC->SelectObject(&brush);
pDC->PatBlt(x, y, w, h, PATCOPY);
pDC->SelectObject(pOldBrush);
}
BOOL IsSmallFont(BOOL& bIsSmall)
{
CWnd* pWnd=CWnd::GetDesktopWindow();
if(pWnd)
{
CDC* pDC=pWnd->GetWindowDC();
if(pDC)
{
TEXTMETRIC tm;
if(pDC->GetTextMetrics(&tm))
{
if(tm.tmHeight>16)
{
bIsSmall=FALSE;
}
else
{
bIsSmall=TRUE;
}
pWnd->ReleaseDC(pDC);
return TRUE;
}
pWnd->ReleaseDC(pDC);
}
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// static variables
CMap<DWORD,DWORD,COXCaptionPainterOrganizer*,COXCaptionPainterOrganizer*>
COXCaptionPainterOrganizer::m_arrThreadOrganizers;
HHOOK COXCaptionPainterOrganizer::m_pfnOriginalCBTHookProc=NULL;
HHOOK COXCaptionPainterOrganizer::m_pfnOriginalGetMessageHookProc=NULL;
/////////////////
COXCaptionPainterOrganizer::~COXCaptionPainterOrganizer()
{
if(IsAttachedAllInThread())
{
DetachAllInThread();
}
else
{
Detach(NULL);
}
int nCount= (int)m_arrUsedPainters.GetSize();
for(int nIndex=0; nIndex<nCount; nIndex++)
{
COXCaptionPainter* pPainter=m_arrUsedPainters[nIndex];
ASSERT(pPainter!=NULL);
ASSERT(!pPainter->IsHooked());
delete pPainter;
}
m_arrUsedPainters.RemoveAll();
ASSERT(m_pfnOriginalCBTHookProc==NULL);
ASSERT(m_pfnOldCBTHookProc==NULL);
ASSERT(m_pfnOriginalGetMessageHookProc==NULL);
}
COXCaptionPainter* COXCaptionPainterOrganizer::Attach(CWnd* pWnd)
{
ASSERT(pWnd!=NULL);
if(pWnd==NULL)
return NULL;
HWND hWndAttached=pWnd->GetSafeHwnd();
ASSERT(::IsWindow(hWndAttached));
COXCaptionPainter* pPainter=NULL;
if(m_arrAttachedWnd.Lookup(hWndAttached,pPainter))
{
ASSERT(pPainter!=NULL);
TRACE(_T("COXCaptionPainterOrganizer::Attach: specified window already attached to a caption painter object\n"));
return pPainter;
}
if(m_arrUsedPainters.GetSize()>0)
{
pPainter=m_arrUsedPainters[0];
ASSERT(pPainter!=NULL);
ASSERT(!pPainter->IsHooked());
m_arrUsedPainters.RemoveAt(0);
}
else
{
pPainter=new COXCaptionPainter;
}
if(pPainter->Attach(pWnd))
{
m_arrAttachedWnd.SetAt(hWndAttached,pPainter);
return pPainter;
}
else
{
// save the object in the array of COXCaptionPainter objects
// that can be used later
m_arrUsedPainters.Add(pPainter);
return NULL;
}
}
BOOL COXCaptionPainterOrganizer::Detach(const CWnd* pWnd/*=NULL*/,
BOOL bRedraw/*=TRUE*/)
{
if(pWnd==NULL)
{
POSITION pos=m_arrAttachedWnd.GetStartPosition();
HWND hAttachedWnd=NULL;
COXCaptionPainter* pPainter=NULL;
while(pos!=NULL)
{
m_arrAttachedWnd.GetNextAssoc(pos,hAttachedWnd,pPainter);
ASSERT(::IsWindow(hAttachedWnd));
ASSERT(pPainter!=NULL);
if(bRedraw)
{
WINDOWPOS wPos;
::ZeroMemory(&wPos, sizeof(wPos));
CRect rect;
::GetWindowRect(hAttachedWnd,&rect);
wPos.cx=rect.Width();
wPos.cy=rect.Height();
wPos.x=rect.left;
wPos.y=rect.top;
wPos.hwnd=hAttachedWnd;
wPos.flags=SWP_DRAWFRAME | SWP_FRAMECHANGED;
::SendMessage(hAttachedWnd,WM_WINDOWPOSCHANGED,
NULL, (LPARAM) &wPos);
HDC hDC=::GetWindowDC(hAttachedWnd);
::SendMessage(hAttachedWnd,WM_ERASEBKGND,(WPARAM) hDC, NULL);
pPainter->Reset();
::ReleaseDC(hAttachedWnd,hDC);
}
if(pPainter!=NULL)
{
if(pPainter->IsHooked())
pPainter->Detach();
// save the object in the array of COXCaptionPainter objects
// that can be used later
m_arrUsedPainters.Add(pPainter);
}
}
m_arrAttachedWnd.RemoveAll();
}
else
{
COXCaptionPainter* pPainter=NULL;
CWnd* pAttachedWnd=(CWnd*)pWnd;
if(!m_arrAttachedWnd.Lookup(pAttachedWnd->GetSafeHwnd(),pPainter))
return FALSE;
ASSERT(pPainter!=NULL);
m_arrAttachedWnd.RemoveKey(pAttachedWnd->GetSafeHwnd());
if(bRedraw && ::IsWindow(pAttachedWnd->GetSafeHwnd()))
{
pPainter->Reset();
pAttachedWnd->SendMessage(WM_NCPAINT);
}
if(pPainter!=NULL)
{
if(pPainter->IsHooked())
pPainter->Detach();
// save the object in the array of COXCaptionPainter objects
// that can be used later
m_arrUsedPainters.Add(pPainter);
}
}
return TRUE;
}
BOOL COXCaptionPainterOrganizer::SetCaptionInfo(const CWnd* pWnd,
COXCaptionInfo* pCI,
BOOL bActive,
BOOL bRedraw/*=TRUE*/) const
{
if(pWnd==NULL)
{
POSITION pos=m_arrAttachedWnd.GetStartPosition();
HWND hAttachedWnd=NULL;
COXCaptionPainter* pPainter=NULL;
while(pos!=NULL)
{
m_arrAttachedWnd.GetNextAssoc(pos,hAttachedWnd,pPainter);
ASSERT(::IsWindow(hAttachedWnd));
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
pPainter->SetCaptionInfo(pCI,bActive);
if(bRedraw)
{
pPainter->Reset();
::SendMessage(hAttachedWnd,WM_NCPAINT,NULL,NULL);
}
}
}
else
{
COXCaptionPainter* pPainter=NULL;
CWnd* pAttachedWnd=(CWnd*)pWnd;
if(!m_arrAttachedWnd.Lookup(pAttachedWnd->GetSafeHwnd(),pPainter))
return FALSE;
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
pPainter->SetCaptionInfo(pCI,bActive);
if(bRedraw && ::IsWindow(pAttachedWnd->GetSafeHwnd()))
{
pPainter->Reset();
pAttachedWnd->SendMessage(WM_NCPAINT);
}
}
return TRUE;
}
BOOL COXCaptionPainterOrganizer::Reset(CWnd* pWnd, BOOL bRedraw/*=TRUE*/) const
{
if(pWnd==NULL)
{
POSITION pos=m_arrAttachedWnd.GetStartPosition();
HWND hAttachedWnd=NULL;
COXCaptionPainter* pPainter=NULL;
while(pos!=NULL)
{
m_arrAttachedWnd.GetNextAssoc(pos,hAttachedWnd,pPainter);
ASSERT(::IsWindow(hAttachedWnd));
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
pPainter->Reset();
if(bRedraw)
{
::SendMessage(hAttachedWnd,WM_NCPAINT,NULL,NULL);
}
}
}
else
{
COXCaptionPainter* pPainter=NULL;
CWnd* pAttachedWnd=(CWnd*)pWnd;
if(!m_arrAttachedWnd.Lookup(pAttachedWnd->GetSafeHwnd(),pPainter))
return FALSE;
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
pPainter->Reset();
if(bRedraw && ::IsWindow(pAttachedWnd->GetSafeHwnd()))
{
pAttachedWnd->SendMessage(WM_NCPAINT);
}
}
return TRUE;
}
#ifndef OXCP_NO_SAVESTATE
BOOL COXCaptionPainterOrganizer::LoadState(const CWnd* pWnd, LPCTSTR lpszProfileName,
BOOL bApply/*=TRUE*/)
{
if(pWnd==NULL)
{
POSITION pos=m_arrAttachedWnd.GetStartPosition();
HWND hAttachedWnd=NULL;
COXCaptionPainter* pPainter=NULL;
while(pos!=NULL)
{
m_arrAttachedWnd.GetNextAssoc(pos,hAttachedWnd,pPainter);
ASSERT(::IsWindow(hAttachedWnd));
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
pPainter->LoadState(lpszProfileName,bApply);
}
}
else
{
COXCaptionPainter* pPainter=NULL;
CWnd* pAttachedWnd=(CWnd*)pWnd;
if(!m_arrAttachedWnd.Lookup(pAttachedWnd->GetSafeHwnd(),pPainter))
return FALSE;
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
pPainter->LoadState(lpszProfileName,bApply);
}
return TRUE;
}
#endif // OXCP_NO_SAVESTATE
COXCaptionPainter* COXCaptionPainterOrganizer::GetPainter(const CWnd* pWnd) const
{
ASSERT(pWnd!=NULL);
if(pWnd==NULL)
return NULL;
COXCaptionPainter* pPainter=NULL;
CWnd* pAttachedWnd=(CWnd*)pWnd;
if(!m_arrAttachedWnd.Lookup(pAttachedWnd->GetSafeHwnd(),pPainter))
pPainter=NULL;
return pPainter;
}
BOOL COXCaptionPainterOrganizer::IsAttached(const CWnd* pWnd) const
{
ASSERT(pWnd!=NULL);
if(pWnd==NULL)
return FALSE;
COXCaptionPainter* pPainter=NULL;
CWnd* pAttachedWnd=(CWnd*)pWnd;
if(m_arrAttachedWnd.Lookup(pAttachedWnd->GetSafeHwnd(),pPainter))
{
ASSERT(pPainter!=NULL);
if(pPainter!=NULL)
return TRUE;
}
return FALSE;
}
BOOL COXCaptionPainterOrganizer::
AttachAllInThread(DWORD dwThreadID/*=::GetCurrentThreadId()*/)
{
if(IsAttachedAllInThread())
{
TRACE(_T("COXCaptionPainterOrganizer::AttachAllInThread: this object already attached to a thread\n"));
return FALSE;
}
COXCaptionPainterOrganizer* pOrganizer=NULL;
if(m_arrThreadOrganizers.Lookup(dwThreadID,pOrganizer))
{
ASSERT(pOrganizer!=NULL);
TRACE(_T("COXCaptionPainterOrganizer::AttachAllInThread: specified thread already attached to a COXCaptionPainterOrganizer object\n"));
return FALSE;
}
m_arrThreadOrganizers.SetAt(dwThreadID,this);
m_dwThreadID=dwThreadID;
// go through all windows and attach them
::EnumWindows(&EnumThreadWindows,(LPARAM)this);
// setup hooks for Computer Based Training
if(m_pfnOriginalCBTHookProc==NULL)
{
m_pfnOriginalCBTHookProc=
::SetWindowsHookEx(WH_CBT,CaptionPainterCBTHookProc,NULL,dwThreadID);
m_pfnOldCBTHookProc=m_pfnOriginalCBTHookProc;
}
else
{
m_pfnOldCBTHookProc=
::SetWindowsHookEx(WH_CBT,CaptionPainterCBTHookProc,NULL,dwThreadID);
}
// setup hooks for GetMessage
if(m_pfnOriginalGetMessageHookProc==NULL)
{
m_pfnOriginalGetMessageHookProc=::SetWindowsHookEx(WH_GETMESSAGE,
CaptionPainterGetMessageHookProc,NULL,dwThreadID);
m_pfnOldGetMessageHookProc=m_pfnOriginalGetMessageHookProc;
}
else
{
m_pfnOldGetMessageHookProc=::SetWindowsHookEx(WH_GETMESSAGE,
CaptionPainterGetMessageHookProc,NULL,dwThreadID);
}
return TRUE;
}
void COXCaptionPainterOrganizer::AttachAllWindows(HWND hWndStartFrom)
{
ASSERT(hWndStartFrom!=NULL);
HWND hWnd=hWndStartFrom;
while(hWnd!=NULL)
{
CWnd* pWnd=CWnd::FromHandlePermanent(hWnd);
if(pWnd!=NULL && !IsAttached(pWnd))
{
Attach(pWnd);
}
// loop through children
HWND hWndChild=::GetWindow(hWnd,GW_CHILD);
if(hWndChild!=NULL)
AttachAllWindows(hWndChild);
// loop through windows
hWnd=::GetWindow(hWnd,GW_HWNDNEXT);
}
}
LRESULT CALLBACK COXCaptionPainterOrganizer::
CaptionPainterCBTHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
if(nCode>=0 && ::IsWindow((HWND)wParam))
{
DWORD dwThreadID=::GetWindowThreadProcessId((HWND)wParam,NULL);
COXCaptionPainterOrganizer* pOrganizer=NULL;
if(COXCaptionPainterOrganizer::m_arrThreadOrganizers.
Lookup(dwThreadID,pOrganizer))
{
ASSERT(pOrganizer!=NULL);
ASSERT(pOrganizer->IsAttachedAllInThread());
if(nCode==HCBT_DESTROYWND)
{
// check if the window that is about to be destroyed
// had been added to caption organizer list
CWnd* pWnd=CWnd::FromHandlePermanent((HWND)wParam);
if(pWnd!=NULL && pOrganizer->IsAttached(pWnd))
pOrganizer->Detach(pWnd,FALSE);
}
else
{
// check if new window is created and attach it.
CWnd* pWnd=CWnd::FromHandlePermanent((HWND)wParam);
if(pWnd!=NULL && !pOrganizer->IsAttached(pWnd))
{
POSITION pos=NULL;
COXCaptionPainter* pPainter=pOrganizer->GetFirstPainter(pos);
if(pOrganizer->Attach(pWnd)!=NULL && pPainter!=NULL)
{
COXCaptionPainter::SetCaptionPainter(pWnd,pPainter);
}
}
}
return ::CallNextHookEx(pOrganizer->GetSavedCBTHookProc(),
nCode,wParam,lParam);
}
}
return ::CallNextHookEx(COXCaptionPainterOrganizer::GetOriginalCBTHookProc(),
nCode,wParam,lParam);
}
LRESULT CALLBACK COXCaptionPainterOrganizer::
CaptionPainterGetMessageHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
if(nCode>=0 && ::IsWindow(((MSG*)lParam)->hwnd))
{
DWORD dwThreadID=::GetWindowThreadProcessId(((MSG*)lParam)->hwnd,NULL);
COXCaptionPainterOrganizer* pOrganizer=NULL;
if(COXCaptionPainterOrganizer::
m_arrThreadOrganizers.Lookup(dwThreadID,pOrganizer))
{
ASSERT(pOrganizer!=NULL);
ASSERT(pOrganizer->IsAttachedAllInThread());
// check if new window is created and attach it.
CWnd* pWnd=CWnd::FromHandlePermanent(((MSG*)lParam)->hwnd);
if(pWnd!=NULL && !pOrganizer->IsAttached(pWnd))
{
POSITION pos=NULL;
COXCaptionPainter* pPainter=pOrganizer->GetFirstPainter(pos);
if(pOrganizer->Attach(pWnd)!=NULL && pPainter!=NULL)
{
COXCaptionPainter::SetCaptionPainter(pWnd,pPainter);
}
}
return ::CallNextHookEx(pOrganizer->GetSavedGetMessageHookProc(),
nCode,wParam,lParam);
}
}
return ::CallNextHookEx(COXCaptionPainterOrganizer::GetOriginalGetMessageHookProc(),
nCode,wParam,lParam);
}
BOOL CALLBACK COXCaptionPainterOrganizer::EnumThreadWindows(HWND hWnd, LPARAM lParam)
{
#if defined (_WINDLL)
#if defined (_AFXDLL)
AFX_MANAGE_STATE(AfxGetAppModuleState());
#else
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
#endif
ASSERT(lParam!=NULL);
ASSERT(::IsWindow(hWnd));
COXCaptionPainterOrganizer* pOrganizer=(COXCaptionPainterOrganizer*)lParam;
ASSERT(pOrganizer->IsAttachedAllInThread());
DWORD dwThreadID=::GetWindowThreadProcessId(hWnd,NULL);
if(dwThreadID==pOrganizer->GetAttachedThread())
{
pOrganizer->AttachAllWindows(hWnd);
return FALSE;
}
return TRUE;
}
BOOL COXCaptionPainterOrganizer::DetachAllInThread(BOOL bRedraw/*=TRUE*/)
{
if(!IsAttachedAllInThread())
return FALSE;
ASSERT(m_dwThreadID!=NULL);
ASSERT(m_pfnOldCBTHookProc!=NULL);
ASSERT(m_pfnOriginalCBTHookProc!=NULL);
ASSERT(m_pfnOldGetMessageHookProc!=NULL);
ASSERT(m_pfnOriginalGetMessageHookProc!=NULL);
// unhook CBT
if(m_pfnOldCBTHookProc!=NULL)
{
VERIFY(::UnhookWindowsHookEx(m_pfnOldCBTHookProc));
m_pfnOldCBTHookProc=NULL;
m_pfnOriginalCBTHookProc=NULL;
}
// unhook GetMessage
if(m_pfnOldGetMessageHookProc!=NULL)
{
VERIFY(::UnhookWindowsHookEx(m_pfnOldGetMessageHookProc));
m_pfnOldGetMessageHookProc=NULL;
m_pfnOriginalGetMessageHookProc=NULL;
}
m_arrThreadOrganizers.RemoveKey(m_dwThreadID);
m_dwThreadID=NULL;
return Detach(NULL,bRedraw);
}