2417 lines
57 KiB
C++
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(®Active, CArchive::store);
|
|
m_ciActive.Serialize(arActive);
|
|
arActive.Close();
|
|
|
|
COXRegistryValFile regInactive(hSecKey, lpszProfileName, _T("Inactive"));
|
|
CArchive arInactive(®Inactive, 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(®Active, CArchive::load);
|
|
m_ciActive.Serialize(arActive);
|
|
arActive.Close();
|
|
}
|
|
|
|
COXRegistryValFile regInactive(hSecKey, lpszProfileName, _T("Inactive"));
|
|
if(regInactive.GetLength()>0)
|
|
{
|
|
CArchive arInactive(®Inactive, 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);
|
|
}
|
|
|