// ========================================================================== // 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 #include "OXCaptionPainter.h" #include "UTBStrOp.h" #include "UTB64Bit.h" #include "WTypes.h" #ifndef __OXMFCIMPL_H__ #if _MFC_VER >= 0x0700 #if _MFC_VER >= 1400 #include #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 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 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; nIndexIsHooked()); 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); }