|
|
//-------------------------------------------------------------------------//
// NCTheme.cpp
//-------------------------------------------------------------------------//
// bug: resizable dialog (themesel) doesn't repaint client when needed
// (for test case, resize "themesel" using "bussolid" theme.
//-------------------------------------------------------------------------//
#include "stdafx.h"
#include "nctheme.h"
#include "sethook.h"
#include "info.h"
#include "rgn.h" // AddToCompositeRgn()
#include "scroll.h" // DrawSizeBox, DrawScrollBar, HandleScrollCmd
#include "resource.h"
#include "tmreg.h"
#include "wrapper.h"
#include "appinfo.h"
//-------------------------------------------------------------------------//
/// local macros, consts, vars
//-------------------------------------------------------------------------//
const RECT rcNil = {-1,-1,-1,-1}; const WINDOWPARTS BOGUS_WINDOWPART = (WINDOWPARTS)0; #define VALID_WINDOWPART(part) ((part)!=BOGUS_WINDOWPART)
#define WS_MINMAX (WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
#define HAS_CAPTIONBAR( dwStyle ) (WS_CAPTION == ((dwStyle) & WS_CAPTION))
#define DLGWNDCLASSNAME TEXT("#32770")
#define DLGWNDCLASSNAMEW L"#32770"
#define NUMBTNSTATES 4 /*number of defined states*/
#define MAKE_BTNSTATE(framestate, state) ((((framestate)-1) * NUMBTNSTATES) + (state))
#define MDIBTNINDEX(ncrc) ((ncrc)-NCMDIBTNFIRST)
#ifdef MAKEPOINT
#undef MAKEPOINT
#endif MAKEPOINT
#define MAKEPOINT(pt,lParam) POINTSTOPOINT(pt, MAKEPOINTS(lParam))
#define IsHTFrameButton(htCode) \
(((htCode) == HTMINBUTTON) || \ ((htCode) == HTMAXBUTTON) || \ ((htCode) == HTCLOSE) || \ ((htCode) == HTHELP))
#define IsTopLevelWindow(hwnd) (IsWindow(hwnd) && NULL==GetParent(hwnd))
#define IsHTScrollBar(htCode) (((htCode) == HTVSCROLL) || ((htCode) == HTHSCROLL))
#define SIG_CTHEMEWND_HEAD "themewnd"
#define SIG_CTHEMEWND_TAIL "end"
//-------------------------------------------------------------------------//
HWND _hwndFirstTop = NULL; // first themed window in process
TCHAR _szWindowMetrics[128] = {0}; // WM_SETTINGCHANGE string param.
//-------------------------------------------------------------------------//
// debug painting switch.
#define DEBUG_NCPAINT
//-------------------------------------------------------------------------//
// internal helper forwards
//-------------------------------------------------------------------------//
HDC _GetNonclientDC( IN HWND hwnd, IN OPTIONAL HRGN hrgnUpdate ); void _ScreenToParent( HWND, LPRECT prcWnd ); BOOL _GetWindowMonitorRect( HWND hwnd, LPRECT prcMonitor ); BOOL _GetMaximizedContainer( IN HWND hwnd, OUT LPRECT prcContainer ); BOOL _IsFullMaximized( IN OPTIONAL HWND hwnd, IN LPCRECT prcWnd ); BOOL _IsMessageWindow( HWND ); void _MDIUpdate( HWND hwndMDIChildOrClient, UINT uSwpFlags ); BOOL _MDIClientUpdateChildren( HWND hwndMDIClient ); void _MDIChildUpdateParent( HWND hwndMDIChild, BOOL fSetMenu = FALSE ); HWND _MDIGetActive( HWND, OUT OPTIONAL BOOL* pfMaximized = NULL ); HWND _MDIGetParent( HWND hwnd, OUT OPTIONAL CThemeWnd** ppMdiFrame = NULL, OUT OPTIONAL HWND *phwndMDIClient = NULL ); int _GetRawClassicCaptionHeight( DWORD dwStyle, DWORD dwExStyle ); int _GetSumClassicCaptionHeight( DWORD dwStyle, DWORD dwExStyle ); void _ComputeNcWindowStatus( IN HWND, IN DWORD dwStatus, IN OUT NCWNDMET* pncwm ); BOOL _MNCanClose(HWND); int _GetWindowBorders(LONG lStyle, DWORD dwExStyle ); BOOL _GetWindowMetrics( HWND, IN OPTIONAL HWND hwndMDIActive, OUT NCWNDMET* pncwm ); BOOL _IsNcPartTransparent( WINDOWPARTS part, const NCTHEMEMET& nctm ); BOOL _GetNcFrameMetrics( HWND, HTHEME hTheme, const NCTHEMEMET&, IN OUT NCWNDMET& ); BOOL _GetNcCaptionMargins( HTHEME hTheme, IN const NCTHEMEMET& nctm, IN OUT NCWNDMET& ncwm ); LPWSTR _AllocWindowText( IN HWND hwnd ); BOOL _GetNcCaptionTextSize( IN HTHEME hTheme, IN HWND hwnd, IN HFONT hf, OUT SIZE* psizeCaption ); BOOL _GetNcCaptionTextRect( IN OUT NCWNDMET* pncwm ); COLORREF _GetNcCaptionTextColor( FRAMESTATES iStateId ); void _GetNcBtnHitTestRect( IN const NCWNDMET* pncwm, IN UINT uHitcode, BOOL fWindowRelative, OUT LPRECT prcHit ); void _GetBrushesForPart(HTHEME hTheme, int iPart, HBITMAP* phbm, HBRUSH* phbr); BOOL _ShouldAssignFrameRgn( IN const NCWNDMET* pncwm, IN const NCTHEMEMET& nctm ); BOOL _IsNcPartTransparent( WINDOWPARTS part, const NCTHEMEMET& nctm ); BOOL _ComputeNcPartTransparency( HTHEME, IN OUT NCTHEMEMET* pnctm ); HRESULT _LoadNcThemeMetrics( HWND, IN OUT OPTIONAL NCTHEMEMET* pnctm ); HRESULT _LoadNcThemeSysMetrics( HWND hwnd, IN OUT OPTIONAL NCTHEMEMET* pnctm ); void _NcSetPreviewMetrics( BOOL fPreview ); BOOL _NcUsingPreviewMetrics(); BOOL _GetNcBtnMetrics( IN OUT NCWNDMET*, IN const NCTHEMEMET*, IN HICON, IN OPTIONAL BOOL );
//-------------------------------------------------------------------------//
// Debug painting.
#if defined(DEBUG)
ULONG _NcTraceFlags = 0; # if defined(DEBUG_NCPAINT)
# define BEGIN_DEBUG_NCPAINT() int cgbl = 0; if(TESTFLAG(_NcTraceFlags, NCTF_NCPAINT)) {GdiSetBatchLimit(1);}
# define END_DEBUG_NCPAINT() if(TESTFLAG(_NcTraceFlags, NCTF_NCPAINT)) {GdiSetBatchLimit(cgbl);}
HRESULT _DebugDrawThemeBackground(HTHEME, HDC, int, int, const RECT*, OPTIONAL const RECT*); HRESULT _DebugDrawThemeBackgroundEx(HTHEME, HDC, int, int, const RECT *prc, OPTIONAL const DTBGOPTS*); void NcDebugClipRgn( HDC hdc, COLORREF rgbPaint ); # define NcDrawThemeBackground _DebugDrawThemeBackground
# define NcDrawThemeBackgroundEx _DebugDrawThemeBackgroundEx
# else //defined(DEBUG_NCPAINT)
# define BEGIN_DEBUG_NCPAINT()
# define END_DEBUG_NCPAINT()
# define NcDrawThemeBackground DrawThemeBackground
# define NcDrawThemeBackgroundEx DrawThemeBackgroundEx
# define NcDebugClipRgn(hdc,rgbPaint)
# endif //defined(DEBUG_NCPAINT)
#else
# define BEGIN_DEBUG_NCPAINT()
# define END_DEBUG_NCPAINT()
# define NcDrawThemeBackground DrawThemeBackground
# define NcDrawThemeBackgroundEx DrawThemeBackgroundEx
# define NcDebugClipRgn(hdc,rgbPaint)
#endif //defined(DEBUG)
#define RGBDEBUGBKGND RGB(0xFF,0x00,0xFF) // debug background indicator fill color
//-------------------------------------------------------------------------//
// process-global metrics
static NCTHEMEMET _nctmCurrent = {0}; CRITICAL_SECTION _csNcSysMet = {0}; // protects access to _incmCurrent
CRITICAL_SECTION _csThemeMet = {0}; // protects access to _nctmCurrent
//-------------------------------------------------------------------------//
// process NONCLIENTMETRICS cache.
struct CInternalNonclientMetrics //-------------------------------------------------------------------------//
{ const NONCLIENTMETRICS& GetNcm() { Acquire(FALSE); return _ncm; }
HFONT GetFont( BOOL fSmallCaption ) { if( _fSet) { return fSmallCaption ? _hfSmCaption : _hfCaption; } return NULL; }
void operator =( const NONCLIENTMETRICS& ncmSrc ) { _ncm = ncmSrc;
SAFE_DELETE_GDIOBJ(_hfCaption); _hfCaption = CreateFontIndirect( &_ncm.lfCaptionFont );
SAFE_DELETE_GDIOBJ(_hfSmCaption); _hfSmCaption = CreateFontIndirect( &_ncm.lfSmCaptionFont );
_fSet = TRUE; }
BOOL Acquire( BOOL fRefresh ) { //---- quick check for outdated metrics ----
if (!_fPreview) { int iNewHeight = GetSystemMetrics(SM_CYSIZE);
if (iNewHeight != _iCaptionButtonHeight) // out of date
{ fRefresh = TRUE; // force the issue
_iCaptionButtonHeight = iNewHeight; } }
// normal metrics
if( !_fSet || fRefresh ) { // save logfont checksum
LOGFONT lfCaption = _ncm.lfCaptionFont; LOGFONT lfSmCaption = _ncm.lfSmCaptionFont;
Log(LOG_TMLOAD, L"Acquire: calling ClassicSystemParmetersInfo");
_ncm.cbSize = sizeof(_ncm); _fSet = ClassicSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &_ncm, FALSE );
if( _fSet ) { // if old, new logfont checksums don't match, recycle our fonts
if( CompareLogfont( &lfCaption, &_ncm.lfCaptionFont) ) { SAFE_DELETE_GDIOBJ(_hfCaption); _hfCaption = CreateFontIndirect(&_ncm.lfCaptionFont); }
if( CompareLogfont( &lfSmCaption, &_ncm.lfSmCaptionFont) ) { SAFE_DELETE_GDIOBJ(_hfSmCaption); _hfSmCaption = CreateFontIndirect(&_ncm.lfSmCaptionFont); } } } return _fSet; }
void Clear() { SAFE_DELETE_GDIOBJ(_hfCaption); SAFE_DELETE_GDIOBJ(_hfSmCaption); ZeroMemory( &_ncm, sizeof(_ncm) ); _fSet = FALSE; }
static int CompareLogfont( const LOGFONT* plf1, const LOGFONT* plf2 ) { int n = memcmp( plf1, plf2, sizeof(LOGFONT) - sizeof(plf1->lfFaceName) ); if( !n ) { n = lstrcmp( plf1->lfFaceName, plf2->lfFaceName ); } return n; }
NONCLIENTMETRICS _ncm; int _iCaptionButtonHeight; BOOL _fSet; HFONT _hfCaption; HFONT _hfSmCaption; BOOL _fPreview;
} _incmCurrent = {0}, _incmPreview = {0};
//-------------------------------------------------------------------------//
// MDI sys button group abstraction
class CMdiBtns //-------------------------------------------------------------------------//
{ public: CMdiBtns(); ~CMdiBtns() { Unload(); }
BOOL Load( IN HTHEME hTheme, IN OPTIONAL HDC hdc = NULL, IN OPTIONAL UINT uSysCmd = 0 ); BOOL ThemeItem( HMENU hMenu, int iPos, MENUITEMINFO* pmii, BOOL fTheme ); void Unload( IN OPTIONAL UINT uSysCmd = 0 ); BOOL Measure( IN HTHEME hTheme, IN OUT MEASUREITEMSTRUCT* pmis ); BOOL Draw( IN HTHEME hTheme, IN DRAWITEMSTRUCT* pdis );
private: #define MDIBTNCOUNT 3 // 1=min, 2=restore, 3=close
//------------------------------------//
// MDI sys button descriptor element
struct MDIBTN { UINT wID; WINDOWPARTS iPartId; SIZINGTYPE sizingType; SIZE size; UINT fTypePrev; HBITMAP hbmPrev; HBITMAP hbmTheme;
} _rgBtns[MDIBTNCOUNT];
private: MDIBTN* _FindBtn( IN UINT wID ); static CLOSEBUTTONSTATES _CalcState( IN ULONG ulodAction, IN ULONG ulodState ); };
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// utility impl
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
void _ScreenToParent( HWND hwnd, LPRECT prcWnd ) { //if we have a parent, we need to convert to those coords
HWND hwndParent = GetAncestor(hwnd, GA_PARENT); POINT* pp = (POINT*)prcWnd; //---- use MapWindowPoints() to account for mirrored windows ----
MapWindowPoints(HWND_DESKTOP, hwndParent, pp, 2); }
//-------------------------------------------------------------------------//
inline BOOL _StrictPtInRect( LPCRECT prc, const POINT& pt ) { // Win32 PtInRect will test positive for an empty rectangle...
return !IsRectEmpty(prc) && PtInRect( prc, pt ); }
//-------------------------------------------------------------------------//
inline BOOL _RectInRect( LPCRECT prcTest, LPCRECT prc ) { if ( prc->left < prcTest->left && prc->right > prcTest->right && prc->top < prcTest->top && prc->bottom > prcTest->bottom ) { return TRUE; } else { return FALSE; } }
//-------------------------------------------------------------------------//
inline HDC _GetNonclientDC( IN HWND hwnd, IN OPTIONAL HRGN hrgnUpdate ) { // private GetDCEx #defines from user
#define DCX_USESTYLE 0x00010000L
#define DCX_NODELETERGN 0x00040000L
DWORD dwDCX = DCX_USESTYLE|DCX_WINDOW|DCX_LOCKWINDOWUPDATE;
if( hrgnUpdate != NULL ) dwDCX |= (DCX_INTERSECTRGN|DCX_NODELETERGN); return GetDCEx( hwnd, hrgnUpdate, dwDCX ); }
//-------------------------------------------------------------------------//
HWND _MDIGetActive( HWND hwndMDIClient, OUT OPTIONAL BOOL* pfMaximized ) { BOOL fMaximized = FALSE; HWND hwndActive = NULL;
if( IsWindow( hwndMDIClient ) ) hwndActive = (HWND)SendMessage( hwndMDIClient, WM_MDIGETACTIVE, 0, (LPARAM)&fMaximized );
if( pfMaximized ) *pfMaximized = fMaximized; return hwndActive; }
//-------------------------------------------------------------------------////
// computes rectangle of window's default monitor
BOOL _GetWindowMonitorRect( HWND hwnd, LPRECT prcMonitor ) { if( IsWindow(hwnd) ) { // default to primary monitor
SetRect( prcMonitor, 0, 0, NcGetSystemMetrics(SM_CXSCREEN), NcGetSystemMetrics(SM_CYSCREEN));
// try determining window's real monitor
HMONITOR hMon = MonitorFromWindow( hwnd, MONITOR_DEFAULTTONULL ); if( hMon ) { MONITORINFO mi; mi.cbSize = sizeof(mi); if( GetMonitorInfo( hMon, &mi ) ) { *prcMonitor = mi.rcWork; } } return TRUE; } return FALSE; }
//-------------------------------------------------------------------------////
// determines whether the indicate window is as large or larger than
// the target monitor
BOOL _GetMaximizedContainer( IN HWND hwnd, OUT LPRECT prcContainer ) { ASSERT(IsWindow(hwnd));
HWND hwndParent = GetParent(hwnd); if( hwndParent ) { return GetWindowRect( hwndParent, prcContainer ); }
// top-level window: container is primary monitor
return _GetWindowMonitorRect( hwnd, prcContainer ); }
//-------------------------------------------------------------------------////
// determines whether the indicate window is as large or larger than
// the target monitor
BOOL _IsFullMaximized( IN OPTIONAL HWND hwnd, IN LPCRECT prcWnd ) { if( !IsWindow(hwnd) ) return TRUE; // assume full-screen maximized window
if( IsZoomed(hwnd) ) { RECT rcContainer = {0}; if( !_GetMaximizedContainer( hwnd, &rcContainer ) ) return TRUE;
// determine whether the rect is contained in the screen rect
return _RectInRect( &rcContainer, prcWnd ); } return FALSE; }
//-------------------------------------------------------------------------//
//
// _GetRawClassicCaptionHeight() -
//
// Using system metrics, computes the total height of the caption bar
// including edge and borders
//
inline int _GetRawClassicCaptionHeight( DWORD dwStyle, DWORD dwExStyle ) { ASSERT(HAS_CAPTIONBAR(dwStyle)); // shouldn't be here without WS_CAPTION
return NcGetSystemMetrics( TESTFLAG(dwExStyle, WS_EX_TOOLWINDOW ) ? SM_CYSMCAPTION : SM_CYCAPTION ); }
//-------------------------------------------------------------------------//
//
// _GetSumClassicCaptionHeight() -
//
// Using system metrics, computes the total height of the caption bar
// including edge and borders
//
inline int _GetSumClassicCaptionHeight( DWORD dwStyle, DWORD dwExStyle ) { ASSERT(HAS_CAPTIONBAR(dwStyle)); // shouldn't be here without WS_CAPTION
// Factor in window border width.
return _GetWindowBorders( dwStyle, dwExStyle) + _GetRawClassicCaptionHeight( dwStyle, dwExStyle ); }
//-------------------------------------------------------------------------//
//
// GetWindowBorders() - port from win32k, rtl\winmgr.c
//
// Computes window border dimensions based on style bits.
//
int _GetWindowBorders(LONG lStyle, DWORD dwExStyle ) { int cBorders = 0;
//
// Is there a 3D border around the window?
//
if( TESTFLAG(dwExStyle, WS_EX_WINDOWEDGE) ) cBorders += 2; else if ( TESTFLAG(dwExStyle, WS_EX_STATICEDGE) ) ++cBorders;
//
// Is there a single flat border around the window? This is true for
// WS_BORDER, WS_DLGFRAME, and WS_EX_DLGMODALFRAME windows.
//
if( TESTFLAG(lStyle, WS_CAPTION) || TESTFLAG(dwExStyle, WS_EX_DLGMODALFRAME) ) ++cBorders;
//
// Is there a sizing flat border around the window?
//
if( TESTFLAG(lStyle, WS_THICKFRAME) && !TESTFLAG(lStyle, WS_MINIMIZE) ) { NONCLIENTMETRICS ncm; cBorders += (NcGetNonclientMetrics( &ncm, FALSE ) ? ncm.iBorderWidth : NcGetSystemMetrics( SM_CXBORDER )); }
return(cBorders); }
//-------------------------------------------------------------------------//
// _MNCanClose
//
// returns TRUE only if USER32 determines that the window can be closed
// (by checking its system menu items and their disabled state)
//
BOOL _MNCanClose(HWND hwnd) { LogEntryNC(L"_MNCanClose");
BOOL fRetVal = FALSE; TITLEBARINFO tbi = {sizeof(tbi)};
//---- don't use GetSystemMenu() - has user handle leak issues ----
if (GetTitleBarInfo(hwnd, &tbi)) { //---- mask out the good bits ----
DWORD dwVal = (tbi.rgstate[5] & (~(STATE_SYSTEM_PRESSED | STATE_SYSTEM_FOCUSABLE))); fRetVal = (dwVal == 0); // only if no bad bits are left
}
if ( !fRetVal && TESTFLAG(GetWindowLong(hwnd, GWL_EXSTYLE), WS_EX_MDICHILD) ) { HMENU hMenu = GetSystemMenu(hwnd, FALSE); MENUITEMINFO menuInfo;
menuInfo.cbSize = sizeof(MENUITEMINFO); menuInfo.fMask = MIIM_STATE; if ( GetMenuItemInfo(hMenu, SC_CLOSE, FALSE, &menuInfo) ) { fRetVal = !(menuInfo.fState & MFS_GRAYED) ? TRUE : FALSE; } } LogExitNC(L"_MNCanClose"); return fRetVal; }
//-------------------------------------------------------------------------//
void CThemeWnd::UpdateMDIFrameStuff( HWND hwndMDIClient, BOOL fSetMenu ) { HWND hwndMDIActive = _MDIGetActive( hwndMDIClient, NULL );
// cache MDIClient, maximized M window handle
_hwndMDIClient = IsWindow(hwndMDIActive) ? hwndMDIClient : NULL; }
//-------------------------------------------------------------------------//
BOOL CALLBACK _FreshenThemeMetricsCB( HWND hwnd, LPARAM lParam ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd( hwnd ); if( VALID_THEMEWND(pwnd) ) { pwnd->AddRef(); pwnd->GetNcWindowMetrics( NULL, NULL, NULL, NCWMF_RECOMPUTE ); pwnd->Release(); } return TRUE; }
//-------------------------------------------------------------------------//
BOOL _IsMessageWindow( HWND hwnd ) { // A window parented by HWND_MESSAGE has no UI and should not be themed.
static ATOM _atomMsgWnd = 0;
HWND hwndParent = (HWND)GetWindowLongPtr( hwnd, GWLP_HWNDPARENT ); if( hwndParent ) { ATOM atomParent = (ATOM)GetClassLong( hwndParent, GCW_ATOM ); // have we seen the message window wndclass before?
if( _atomMsgWnd ) return (atomParent == _atomMsgWnd); // compare class atoms
// haven't seen a message window come through in this process,
// so compare class names.
WCHAR szClass[128]; if( GetClassNameW( hwndParent, szClass, ARRAYSIZE(szClass) ) ) { if( 0 == AsciiStrCmpI( szClass, L"Message" ) ) { _atomMsgWnd = atomParent; return TRUE; } } } return FALSE; }
//-------------------------------------------------------------------------//
// Retrieves MDI frame and/or MDICLIENT window for an MDI child window
HWND _MDIGetParent( HWND hwnd, OUT OPTIONAL CThemeWnd** ppMdiFrame, OUT OPTIONAL HWND* phwndMDIClient ) { if( ppMdiFrame ) *ppMdiFrame = NULL; if( phwndMDIClient ) *phwndMDIClient = NULL;
if( TESTFLAG(GetWindowLong( hwnd, GWL_EXSTYLE ), WS_EX_MDICHILD) ) { HWND hwndMDIClient = GetParent(hwnd); if( IsWindow(hwndMDIClient) ) { HWND hwndFrame = GetParent(hwndMDIClient); if( IsWindow(hwndFrame) ) { if( phwndMDIClient ) *phwndMDIClient = hwndMDIClient; if( ppMdiFrame ) *ppMdiFrame = CThemeWnd::FromHwnd(hwndFrame);
return hwndFrame; } } } return NULL; }
//-------------------------------------------------------------------------//
HWND _FindMDIClient( HWND hwndFrame ) { for( HWND hwndChild = GetWindow(hwndFrame, GW_CHILD); hwndChild != NULL; hwndChild = GetNextWindow(hwndChild, GW_HWNDNEXT)) { TCHAR szClass[48]; if( GetClassName(hwndChild, szClass, ARRAYSIZE(szClass)) ) { if( 0 == lstrcmpi(szClass, TEXT("MDIClient")) ) { return hwndChild; } } } return NULL; }
//-------------------------------------------------------------------------//
// Handle MDI relative updating on WM_WINDOWPOSCHANGED
void _MDIUpdate( HWND hwnd, UINT uSwpFlags) { // Notify MDI frame if we became maximized, etc.
BOOL bIsClient = FALSE;
// Could be the MDI client, could be a MDI child
if (!(TESTFLAG(uSwpFlags, SWP_NOMOVE) && TESTFLAG(uSwpFlags, SWP_NOSIZE))) { bIsClient = _MDIClientUpdateChildren( hwnd ); } if (!bIsClient) { _MDIChildUpdateParent( hwnd, FALSE ); } }
//-------------------------------------------------------------------------//
// Post-WM_WINDOWPOSCHANGED processing for MDI client or children.
// We need to recompute each child when the MDI client moves.
BOOL _MDIClientUpdateChildren( HWND hwndMDIChildOrClient ) { // Find if it's the MDI client window
HWND hWndChild = GetWindow(hwndMDIChildOrClient, GW_CHILD); if (IsWindow(hWndChild) && TESTFLAG(GetWindowLong(hWndChild, GWL_EXSTYLE), WS_EX_MDICHILD)) { // Yes it's the MDI client, refresh each MDI child's metrics
do { _FreshenThemeMetricsCB(hWndChild, NULL); } while (NULL != (hWndChild = GetWindow(hWndChild, GW_HWNDNEXT))); return TRUE; }
return FALSE; }
//-------------------------------------------------------------------------//
// Informs MDI frame that a child window may
void _MDIChildUpdateParent( HWND hwndMDIChild, BOOL fSetMenu ) { CThemeWnd* pwndParent; HWND hwndMDIClient;
if( _MDIGetParent( hwndMDIChild, &pwndParent, &hwndMDIClient ) && VALID_THEMEWND(pwndParent) ) { pwndParent->UpdateMDIFrameStuff( hwndMDIClient, fSetMenu ); } }
//-------------------------------------------------------------------------//
// _ComputeNcWindowStatus
//
// Assigns and translates window status bits to/in NCWNDMET block.
//
void _ComputeNcWindowStatus( IN HWND hwnd, IN DWORD dwStatus, IN OUT NCWNDMET* pncwm ) { BOOL fActive = TESTFLAG( dwStatus, WS_ACTIVECAPTION );
if (fActive || !HAS_CAPTIONBAR(pncwm->dwStyle) ) { pncwm->framestate = FS_ACTIVE; } else { pncwm->framestate = FS_INACTIVE; }
if( HAS_CAPTIONBAR(pncwm->dwStyle) ) { pncwm->rgbCaption = _GetNcCaptionTextColor( pncwm->framestate ); } }
//-------------------------------------------------------------------------////
BOOL _GetWindowMetrics( HWND hwnd, IN OPTIONAL HWND hwndMDIActive, OUT NCWNDMET* pncwm ) { WINDOWINFO wi; wi.cbSize = sizeof(wi); if( GetWindowInfo( hwnd, &wi ) ) { pncwm->dwStyle = wi.dwStyle; pncwm->dwExStyle = wi.dwExStyle; pncwm->rcS0[NCRC_WINDOW] = wi.rcWindow; pncwm->rcS0[NCRC_CLIENT] = wi.rcClient;
pncwm->fMin = IsIconic(hwnd); pncwm->fMaxed = IsZoomed(hwnd); pncwm->fFullMaxed = pncwm->fMaxed ? _IsFullMaximized(hwnd, &wi.rcWindow) : FALSE;
pncwm->dwWindowStatus = wi.dwWindowStatus; // if this window is the active MDI child and is owned by the foreground window
// (which may not be the case if a popup, for example, is foremost), then
// fix up the status bit.
if( hwnd == hwndMDIActive ) { HWND hwndFore = GetForegroundWindow(); if( IsChild(hwndFore, hwndMDIActive) ) { pncwm->dwWindowStatus = WS_ACTIVECAPTION; } }
return TRUE; } return FALSE; }
//-------------------------------------------------------------------------//
BOOL _ShouldAssignFrameRgn( IN const NCWNDMET* pncwm, IN const NCTHEMEMET& nctm ) { if( TESTFLAG( CThemeWnd::EvaluateStyle(pncwm->dwStyle, pncwm->dwExStyle), TWCF_FRAME|TWCF_TOOLFRAME) ) { // always need window region for maximized windows.
if( pncwm->fFullMaxed ) return TRUE;
// otherwise, need region only if the background is transparent
for( int i = 0; i < ARRAYSIZE( pncwm->rgframeparts ); i++ ) { if( _IsNcPartTransparent( pncwm->rgframeparts[i], nctm ) ) return TRUE; } } return FALSE; }
//-------------------------------------------------------------------------//
BOOL _IsNcPartTransparent( WINDOWPARTS part, const NCTHEMEMET& nctm ) { #define GET_NCTRANSPARENCY(part,field) \
case part: return nctm.nct.##field switch(part) { GET_NCTRANSPARENCY(WP_CAPTION, fCaption); GET_NCTRANSPARENCY(WP_SMALLCAPTION, fCaption); GET_NCTRANSPARENCY(WP_MINCAPTION, fMinCaption); GET_NCTRANSPARENCY(WP_SMALLMINCAPTION, fSmallMinCaption); GET_NCTRANSPARENCY(WP_MAXCAPTION, fMaxCaption); GET_NCTRANSPARENCY(WP_SMALLMAXCAPTION, fSmallMaxCaption); GET_NCTRANSPARENCY(WP_FRAMELEFT, fFrameLeft); GET_NCTRANSPARENCY(WP_FRAMERIGHT, fFrameRight); GET_NCTRANSPARENCY(WP_FRAMEBOTTOM, fFrameBottom); GET_NCTRANSPARENCY(WP_SMALLFRAMELEFT, fSmFrameLeft); GET_NCTRANSPARENCY(WP_SMALLFRAMERIGHT, fSmFrameRight); GET_NCTRANSPARENCY(WP_SMALLFRAMEBOTTOM, fSmFrameBottom); } return FALSE; }
//-------------------------------------------------------------------------//
BOOL _ComputeNcPartTransparency( HTHEME hTheme, IN OUT NCTHEMEMET* pnctm ) { #define TEST_NCTRANSPARENCY(part) IsThemePartDefined(hTheme,part,0) ? \
IsThemeBackgroundPartiallyTransparent(hTheme,part,FS_ACTIVE) : FALSE; pnctm->nct.fCaption = TEST_NCTRANSPARENCY(WP_CAPTION); pnctm->nct.fSmallCaption = TEST_NCTRANSPARENCY(WP_SMALLCAPTION); pnctm->nct.fMinCaption = TEST_NCTRANSPARENCY(WP_MINCAPTION); pnctm->nct.fSmallMinCaption = TEST_NCTRANSPARENCY(WP_SMALLMINCAPTION); pnctm->nct.fMaxCaption = TEST_NCTRANSPARENCY(WP_MAXCAPTION); pnctm->nct.fSmallMaxCaption = TEST_NCTRANSPARENCY(WP_SMALLMAXCAPTION);
pnctm->nct.fFrameLeft = TEST_NCTRANSPARENCY(WP_FRAMELEFT); pnctm->nct.fFrameRight = TEST_NCTRANSPARENCY(WP_FRAMERIGHT); pnctm->nct.fFrameBottom = TEST_NCTRANSPARENCY(WP_FRAMEBOTTOM); pnctm->nct.fSmFrameLeft = TEST_NCTRANSPARENCY(WP_SMALLFRAMELEFT); pnctm->nct.fSmFrameRight = TEST_NCTRANSPARENCY(WP_SMALLFRAMERIGHT); pnctm->nct.fSmFrameBottom = TEST_NCTRANSPARENCY(WP_SMALLFRAMEBOTTOM);
return TRUE; }
//-------------------------------------------------------------------------//
// NCTHEMEMET implementation
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
BOOL GetCurrentNcThemeMetrics( OUT NCTHEMEMET* pnctm ) { *pnctm = _nctmCurrent; return IsValidNcThemeMetrics( pnctm ); }
//-------------------------------------------------------------------------//
void InitNcThemeMetrics( NCTHEMEMET* pnctm ) { if( !pnctm ) pnctm = &_nctmCurrent;
ZeroMemory( pnctm, sizeof(*pnctm) ); }
//---------------------------------------------------------------------------
void ClearNcThemeMetrics( NCTHEMEMET* pnctm ) { if( !pnctm ) pnctm = &_nctmCurrent;
//---- minimize THREAD-UNSAFE access to _nctmCurrent by ----
//---- NULL-ing out the hTheme type members as soon as ----
//---- they are closed ----
if( pnctm->hTheme ) { CloseThemeData( pnctm->hTheme ); pnctm->hTheme = NULL; }
if( pnctm->hThemeTab ) { CloseThemeData( pnctm->hThemeTab ); pnctm->hThemeTab = NULL; }
SAFE_DELETE_GDIOBJ( pnctm->hbmTabDialog ); SAFE_DELETE_GDIOBJ( pnctm->hbrTabDialog );
InitNcThemeMetrics( pnctm ); }
//-------------------------------------------------------------------------//
// Computes process-global, per-theme metrics for the nonclient area theme.
HRESULT AcquireNcThemeMetrics() { HRESULT hr = E_FAIL;
if( VALID_CRITICALSECTION(&_csThemeMet) ) { hr = S_OK;
EnterCriticalSection( &_csThemeMet );
ClearNcThemeMetrics( &_nctmCurrent ); NcGetNonclientMetrics( NULL, FALSE ); hr = _LoadNcThemeMetrics(NULL, &_nctmCurrent);
LeaveCriticalSection( &_csThemeMet );
Log(LOG_TMCHANGE, L"AcquireNcThemeMetrics: got hTheme=0x%x", _nctmCurrent.hTheme); }
return hr; }
//-------------------------------------------------------------------------//
// Computes and/or loads per-theme (as opposed to per-window)
// system metrics and resources not managed by the theme manager.
//
// Called by _LoadNcThemeMetrics
HRESULT _LoadNcThemeSysMetrics( HWND hwnd, IN OUT NCTHEMEMET* pnctm ) { HRESULT hr = E_FAIL; ASSERT(pnctm);
// grab system metrics for nonclient area.
NONCLIENTMETRICS ncm = {0}; ncm.cbSize = sizeof(ncm); if( NcGetNonclientMetrics( &ncm, FALSE ) ) { hr = S_OK;
// Establish minimized window size
if( 0 >= pnctm->sizeMinimized.cx ) pnctm->sizeMinimized.cx = NcGetSystemMetrics( SM_CXMINIMIZED ); if( 0 >= pnctm->sizeMinimized.cy ) pnctm->sizeMinimized.cy = NcGetSystemMetrics( SM_CYMINIMIZED ); } else { hr = HRESULT_FROM_WIN32(GetLastError()); if( SUCCEEDED(hr) ) hr = E_FAIL; }
// Maximized caption height or width
pnctm->cyMaxCaption = _GetRawClassicCaptionHeight( WS_CAPTION|WS_OVERLAPPED, 0 );
return hr; }
//-------------------------------------------------------------------------//
// Computes and/or loads per-theme (as opposed to per-window)
// metrics and resources not managed by the theme manager
HRESULT _LoadNcThemeMetrics( HWND hwnd, NCTHEMEMET* pnctm ) { HRESULT hr = E_FAIL;
// Initialize incoming NCTHEMEMET:
if( pnctm ) { InitNcThemeMetrics( pnctm );
HTHEME hTheme = ::OpenNcThemeData( hwnd, L"Window" ); if( hTheme ) { pnctm->hTheme = hTheme;
// determine transparency for each frame part.
_ComputeNcPartTransparency(hTheme, pnctm);
// menubar pixels not accounted for by CalcMenuBar or PaintMenuBar
pnctm->dyMenuBar = NcGetSystemMetrics(SM_CYMENU) - NcGetSystemMetrics(SM_CYMENUSIZE);
// normal caption margins
if( FAILED( GetThemeMargins( hTheme, NULL, WP_CAPTION, CS_ACTIVE, TMT_CAPTIONMARGINS, NULL, &pnctm->marCaptionText )) ) { FillMemory( &pnctm->marCaptionText, sizeof(pnctm->marCaptionText), 0 ); }
// maximized caption margins
if( FAILED( GetThemeMargins( hTheme, NULL, WP_MAXCAPTION, CS_ACTIVE, TMT_CAPTIONMARGINS, NULL, &pnctm->marMaxCaptionText )) ) { FillMemory( &pnctm->marMaxCaptionText, sizeof(pnctm->marMaxCaptionText), 0 ); }
// minimized caption margins
if( FAILED( GetThemeMargins( hTheme, NULL, WP_MINCAPTION, CS_ACTIVE, TMT_CAPTIONMARGINS, NULL, &pnctm->marMinCaptionText )) ) { FillMemory( &pnctm->marMinCaptionText, sizeof(pnctm->marMinCaptionText), 0 ); }
// dynamically resizing small (toolframe) caption margins
if( FAILED( GetThemeMargins( hTheme, NULL, WP_SMALLCAPTION, CS_ACTIVE, TMT_CAPTIONMARGINS, NULL, &pnctm->marSmCaptionText )) ) { FillMemory( &pnctm->marSmCaptionText, sizeof(pnctm->marSmCaptionText), 0 ); }
// caption and frame resizing border hittest template parts
pnctm->fCapSizingTemplate = IsThemePartDefined( hTheme, WP_CAPTIONSIZINGTEMPLATE, 0); pnctm->fLeftSizingTemplate = IsThemePartDefined( hTheme, WP_FRAMELEFTSIZINGTEMPLATE, 0); pnctm->fRightSizingTemplate = IsThemePartDefined( hTheme, WP_FRAMERIGHTSIZINGTEMPLATE, 0); pnctm->fBottomSizingTemplate = IsThemePartDefined( hTheme, WP_FRAMEBOTTOMSIZINGTEMPLATE, 0);
// toolwindow caption and frame resizing border hittest template parts
pnctm->fSmCapSizingTemplate = IsThemePartDefined( hTheme, WP_SMALLCAPTIONSIZINGTEMPLATE, 0); pnctm->fSmLeftSizingTemplate = IsThemePartDefined( hTheme, WP_SMALLFRAMELEFTSIZINGTEMPLATE, 0); pnctm->fSmRightSizingTemplate = IsThemePartDefined( hTheme, WP_SMALLFRAMERIGHTSIZINGTEMPLATE, 0); pnctm->fSmBottomSizingTemplate = IsThemePartDefined( hTheme, WP_SMALLFRAMEBOTTOMSIZINGTEMPLATE, 0);
// Minimized window size.
// If this is a truesize image, honor its dimensions; otherwise use
// width, height properties. Fall back on system metrics.
SIZINGTYPE st = ST_TRUESIZE; hr = GetThemeInt( hTheme, WP_MINCAPTION, FS_ACTIVE, TMT_SIZINGTYPE, (int*)&st );
if( ST_TRUESIZE == st ) { hr = GetThemePartSize( hTheme, NULL, WP_MINCAPTION, FS_ACTIVE, NULL, TS_TRUE, &pnctm->sizeMinimized );
if( FAILED(hr) ) { GetThemeMetric( hTheme, NULL, WP_MINCAPTION, FS_ACTIVE, TMT_WIDTH, (int*)&pnctm->sizeMinimized.cx ); GetThemeMetric( hTheme, NULL, WP_MINCAPTION, FS_ACTIVE, TMT_HEIGHT, (int*)&pnctm->sizeMinimized.cy ); } }
// -- normal nonclient button size.
int cy = NcGetSystemMetrics( SM_CYSIZE ); hr = GetThemePartSize( pnctm->hTheme, NULL, WP_CLOSEBUTTON, 0, NULL, TS_TRUE, &pnctm->sizeBtn ); if( SUCCEEDED(hr) ) { pnctm->theme_sysmets.cxBtn = MulDiv( cy, pnctm->sizeBtn.cx, pnctm->sizeBtn.cy ); } else { pnctm->theme_sysmets.cxBtn = pnctm->sizeBtn.cx = NcGetSystemMetrics( SM_CXSIZE ); pnctm->sizeBtn.cy = cy; } // -- toolframe nonclient button size.
cy = NcGetSystemMetrics( SM_CYSMSIZE ); hr = GetThemePartSize( pnctm->hTheme, NULL, WP_SMALLCLOSEBUTTON, 0, NULL, TS_TRUE, &pnctm->sizeSmBtn ); if( SUCCEEDED(hr) ) { pnctm->theme_sysmets.cxSmBtn = MulDiv( cy, pnctm->sizeSmBtn.cx, pnctm->sizeSmBtn.cy ); } else { pnctm->theme_sysmets.cxSmBtn = pnctm->sizeSmBtn.cx = NcGetSystemMetrics( SM_CXSMSIZE );
pnctm->sizeSmBtn.cy = cy; } // -- validate sysmet hook values
pnctm->theme_sysmets.fValid = TRUE;
// dialog background for dialogs parented by PROPSHEETs or
// specifically stamped via EnableThemeDialogTexture to match the tab control background.
//
// We need to open the tab control's theme so that we can get the background of tab dialogs
// We can't dynamically load this because of how this cache is set up: It's all or nothing.
pnctm->hThemeTab = ::OpenThemeData(hwnd, L"Tab"); _GetBrushesForPart(pnctm->hThemeTab, TABP_BODY, &pnctm->hbmTabDialog, &pnctm->hbrTabDialog);
hr = _LoadNcThemeSysMetrics( hwnd, pnctm ); } }
return hr; }
//-------------------------------------------------------------------------//
BOOL IsValidNcThemeMetrics( NCTHEMEMET* pnctm ) { return pnctm->hTheme != NULL; }
//-------------------------------------------------------------------------//
// THREADWINDOW implementation
//-------------------------------------------------------------------------//
//
// Note: this is a fixed length array of threads-window mappings.
// We'll use this to keep track of the threads processing a certain message
//
// Thread local storage would be better suited to the task, but we
// learned early on that the unique load/unload situation of uxtheme
// causes us to miss DLL_THREAD_DETACH in some scenarios, which would mean
// leaking the TLS.
//
typedef struct _THREADWINDOW { DWORD dwThread; HWND hwnd;
} THREADWINDOW;
//-------------------------------------------------------------------------//
// WM_NCPAINT tracking:
THREADWINDOW _rgtwNcPaint[16] = {0}; // threads processing NCPAINT in this process
int _cNcPaintWnd = 0; // count of threads processing NCPAINT in this process
CRITICAL_SECTION _csNcPaint = {0}; // serializes access to _rgtwNcPaint
//-------------------------------------------------------------------------//
void NcPaintWindow_Add( HWND hwnd ) { // add entry to our list of threads handling WM_NCPAINT.
if( VALID_CRITICALSECTION(&_csNcPaint) ) { EnterCriticalSection( &_csNcPaint ); for( int i = 0; i < ARRAYSIZE(_rgtwNcPaint); i++ ) { if( 0 == _rgtwNcPaint[i].dwThread ) { _rgtwNcPaint[i].dwThread = GetCurrentThreadId(); _rgtwNcPaint[i].hwnd = hwnd; _cNcPaintWnd++; } } LeaveCriticalSection( &_csNcPaint ); } }
//-------------------------------------------------------------------------//
void NcPaintWindow_Remove() { // remove entry from our list of threads handling WM_NCPAINT.
if( _cNcPaintWnd ) { DWORD dwThread = GetCurrentThreadId();
if( VALID_CRITICALSECTION(&_csNcPaint) ) { EnterCriticalSection( &_csNcPaint ); for( int i = 0; i < ARRAYSIZE(_rgtwNcPaint); i++ ) { if( dwThread == _rgtwNcPaint[i].dwThread ) { _rgtwNcPaint[i].dwThread = 0; _rgtwNcPaint[i].hwnd = 0; _cNcPaintWnd--; break; } } LeaveCriticalSection( &_csNcPaint ); } } }
//-------------------------------------------------------------------------//
HWND NcPaintWindow_Find() { HWND hwnd = NULL;
// Search entries in our list of threads handling WM_NCPAINT.
if( _cNcPaintWnd ) { DWORD dwThread = GetCurrentThreadId();
if( VALID_CRITICALSECTION(&_csNcPaint) ) { EnterCriticalSection( &_csNcPaint ); for( int i = 0; i < ARRAYSIZE(_rgtwNcPaint); i++ ) { if( dwThread == _rgtwNcPaint[i].dwThread ) { hwnd = _rgtwNcPaint[i].hwnd; break; } } LeaveCriticalSection( &_csNcPaint ); } } return hwnd; }
//-------------------------------------------------------------------------//
// CThemeWnd implementation
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
LONG CThemeWnd::_cObj = 0;
//-------------------------------------------------------------------------//
CThemeWnd::CThemeWnd() : _hwnd(NULL), _hTheme(NULL), _dwRenderedNcParts(0), _hwndMDIClient(NULL), _hAppIcon(NULL), _hrgnWnd(NULL), _fClassFlags(0), _fDirtyFrameRgn(0), _fFrameThemed(FALSE), _fThemedMDIBtns(FALSE), _pMdiBtns(NULL), _fAssigningFrameRgn(FALSE), _fAssignedFrameRgn(FALSE), _fSuppressStyleMsgs(FALSE), _fInThemeSettingChange(FALSE), _fDetached(FALSE), _dwRevokeFlags(0), _cLockRedraw(0), _cNcPaint(0), _cNcThemePaint(0), _htHot(HTERROR), _fProcessedEraseBk(0), #ifdef LAME_BUTTON
_hFontLame(NULL), #endif // LAME_BUTTON
_cRef(1) { InterlockedIncrement( &_cObj );
// set object validation signature tags
StringCchCopyA(_szHead, ARRAYSIZE(_szHead), SIG_CTHEMEWND_HEAD); StringCchCopyA(_szTail, ARRAYSIZE(_szTail), SIG_CTHEMEWND_TAIL);
// cached subregion arrays
ZeroMemory( _rghrgnParts, sizeof(_rghrgnParts) ); ZeroMemory( _rghrgnSizingTemplates, sizeof(_rghrgnSizingTemplates) ); // initialize add'l structures.
InitWindowMetrics(); ZeroMemory(&_cswm, sizeof(_cswm)); FillMemory(&_sizeRgn, sizeof(_sizeRgn), 0xFF);
#ifdef DEBUG
*_szCaption = *_szWndClass = 0; #endif DEBUG
}
//-------------------------------------------------------------------------//
CThemeWnd::~CThemeWnd() { _CloseTheme(); _FreeRegionHandles(); UnloadMdiBtns(); ClearLameResources();
SAFE_DELETECRITICALSECTION(&_cswm); ASSERT( 0 != _cObj ); InterlockedDecrement( &_cObj ); }
//-------------------------------------------------------------------------//
void CThemeWnd::_CloseTheme() { if( _hTheme ) { CloseThemeData(_hTheme); _hTheme = NULL; } }
//-------------------------------------------------------------------------//
LONG CThemeWnd::AddRef() { return InterlockedIncrement( &_cRef ); }
//-------------------------------------------------------------------------//
LONG CThemeWnd::Release() { ASSERT( 0 != _cRef ); LONG cRef = InterlockedDecrement( &_cRef );
if( 0 == cRef ) { if (_hwnd) { //---- check if last window of app ----
ShutDownCheck(_hwnd); }
//Log(LOG_RFBUG, L"DELETING CThemeWnd=0x%08x", this);
delete this; } return cRef; }
//-------------------------------------------------------------------------//
ULONG CThemeWnd::EvaluateWindowStyle( HWND hwnd ) { ULONG dwStyle = GetWindowLong( hwnd, GWL_STYLE ); ULONG dwExStyle = GetWindowLong( hwnd, GWL_EXSTYLE );
return EvaluateStyle( dwStyle, dwExStyle ); }
//-------------------------------------------------------------------------//
// CThemeWnd::EvaluateStyle() - determines appropriate theming flags for the
// specified window style bits.
ULONG CThemeWnd::EvaluateStyle( DWORD dwStyle, DWORD dwExStyle ) { ULONG fClassFlags = 0;
//--- frame check ---
if( HAS_CAPTIONBAR(dwStyle) ) { fClassFlags |= (TESTFLAG(dwExStyle, WS_EX_TOOLWINDOW) ? TWCF_TOOLFRAME : TWCF_FRAME ); }
//--- client edge check ---
if( TESTFLAG(dwExStyle, WS_EX_CLIENTEDGE) ) fClassFlags |= TWCF_CLIENTEDGE;
//--- scrollbar check ---
if( TESTFLAG(dwStyle, WS_HSCROLL|WS_VSCROLL) ) fClassFlags |= TWCF_SCROLLBARS;
return fClassFlags; }
//-------------------------------------------------------------------------//
// CThemeWnd::_EvaluateExclusions() - determines special-case per-window exclusions
ULONG CThemeWnd::_EvaluateExclusions( HWND hwnd, NCEVALUATE* pnce ) { // Windows parented by HWND_MESSAGE should not be themed..
if( _IsMessageWindow(hwnd) ) { pnce->fExile = TRUE; return 0L; }
TCHAR szWndClass[128]; *szWndClass = 0;
if( TESTFLAG(pnce->fClassFlags, (TWCF_FRAME|TWCF_TOOLFRAME)) ) { do { if( !pnce->fIgnoreWndRgn ) { //--- Complex region check on frame
RECT rcRgn = {0}; int nRgn = GetWindowRgnBox( hwnd, &rcRgn ); if( COMPLEXREGION == nRgn || SIMPLEREGION == nRgn ) { pnce->fClassFlags &= ~TWCF_FRAME; break; } }
// SHIMSHIM [scotthan]:
#ifndef __NO_APPHACKS__
// Check for excluded window classes.
static LPCWSTR _rgExcludedClassesW[] = { L"MsoCommandBar", // Outlook's custom combobox control.
// (122225) In OnOwpPostCreate we call SetWindowPos which causes
// a WM_WINDOWPOSCHANGING to be sent to the control. However
// the controls isn't ready to begin accepting messages and
// the following error message is display:
//
// Runtime Error!
// Program: Outlook.exe
// R6025 - pure virtual function call
L"Exceed", // 150248: Hummingbird Exceed 6.xx
// The application's main window class name, a hidden window
// whose only purpose is to appear in the task bar in order to handle
// his context menu. The ExceedWndProc AVs when themed due to the
// additional messages generated in OnOwpPostCreate.
//---- winlogoon hidden windows ----
L"NDDEAgnt", // on private desktop
L"MM Notify Callback", // on private desktop
L"SAS window class", // on private desktop
};
if( GetClassNameW( hwnd, szWndClass, ARRAYSIZE(szWndClass) ) && AsciiScanStringList( szWndClass, _rgExcludedClassesW, ARRAYSIZE(_rgExcludedClassesW), TRUE ) ) { pnce->fClassFlags &= ~TWCF_FRAME; pnce->fExile = TRUE; break; } #endif __NO_APPHACKS__
} while(0); }
// Some applications (MsDev) create scrollbar controls and incorrectly include
// WS_[V|H]SCROLL style bits causing us to think they are non-client scrolls.
// See #204191.
if( TESTFLAG(pnce->fClassFlags, TWCF_SCROLLBARS) ) { if( !*szWndClass && GetClassName( hwnd, szWndClass, ARRAYSIZE(szWndClass) ) ) { if( 0 == AsciiStrCmpI(szWndClass, L"scrollbar") ) pnce->fClassFlags &= ~TWCF_SCROLLBARS; } }
// Fixed in longhorn [phellyar]:
#ifndef __NO_APPHACKS__
if( *szWndClass || GetClassName( hwnd, szWndClass, ARRAYSIZE(szWndClass) ) ) { // 453888: VB Grid control - setting the redraw property on this control
// causes it to zero and reset the scrollbar range for every scroll event.
// This causes the scrollbar to function incorrecly when themed.
if( 0 == AsciiStrCmpI(szWndClass, L"MSFlexGridWndClass") ) { pnce->fClassFlags &= ~TWCF_SCROLLBARS; pnce->fExile = TRUE; } } #endif __NO_APPHACKS__
return pnce->fClassFlags;
}
//-------------------------------------------------------------------------//
// CThemeWnd::_Evaluate() - determines appropriate theming flags for the
// specified window.
ULONG CThemeWnd::_Evaluate( HWND hwnd, NCEVALUATE* pnce ) { pnce->fClassFlags = 0; pnce->dwStyle = GetWindowLong( hwnd, GWL_STYLE ); pnce->dwExStyle = GetWindowLong( hwnd, GWL_EXSTYLE );
if( GetClassLong( hwnd, GCW_ATOM ) == (DWORD)(DWORD_PTR)WC_DIALOG ) { pnce->fClassFlags |= TWCF_DIALOG; }
#ifdef DEBUG
//--- dialog check ---
if( TESTFLAG( pnce->fClassFlags, TWCF_DIALOG ) ) { TCHAR szWndClass[96]; if( !GetClassNameW( hwnd, szWndClass, ARRAYSIZE(szWndClass) ) ) return 0; ASSERT(0 == lstrcmpW(szWndClass, DLGWNDCLASSNAMEW)); } #endif DEBUG
pnce->fClassFlags |= EvaluateStyle( pnce->dwStyle, pnce->dwExStyle );
if( pnce->fClassFlags ) { pnce->fClassFlags = _EvaluateExclusions( hwnd, pnce ); }
return pnce->fClassFlags; }
//-------------------------------------------------------------------------//
// Retrieves the address of the CThemeWnd object instance from the
// indicated window.
CThemeWnd* CThemeWnd::FromHwnd( HWND hwnd ) { CThemeWnd *pwnd = NULL;
if( IsWindow(hwnd) ) { if( g_dwProcessId ) { DWORD dwPid = 0; GetWindowThreadProcessId( hwnd, &dwPid ); if( dwPid == g_dwProcessId ) { pwnd = (CThemeWnd*)GetProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)) );
if ( VALID_THEMEWND(pwnd) ) { // verify this is a valid CThemeWnd object pointer
if ( IsBadReadPtr(pwnd, sizeof(CThemeWnd)) || (memcmp(pwnd->_szHead, SIG_CTHEMEWND_HEAD, ARRAYSIZE(pwnd->_szHead)) != 0) || (memcmp(pwnd->_szTail, SIG_CTHEMEWND_TAIL, ARRAYSIZE(pwnd->_szTail)) != 0) ) { pwnd = THEMEWND_REJECT; } } } } }
return pwnd; }
//-------------------------------------------------------------------------//
// retrieves CThemeWnd instance from window or ancestors.
CThemeWnd* CThemeWnd::FromHdc( HDC hdc, int cAncestors ) { HWND hwnd = NULL;
for( hwnd = WindowFromDC(hdc); cAncestors >=0 && IsWindow(hwnd); cAncestors--, hwnd = GetParent(hwnd) ) { CThemeWnd* pwnd = FromHwnd(hwnd); if( VALID_THEMEWND(pwnd) ) { return pwnd; } }
return NULL; }
//-------------------------------------------------------------------------//
// Static wrapper: attaches a CThemeWnd instance to the specified window.
CThemeWnd* CThemeWnd::Attach( HWND hwnd, IN OUT OPTIONAL NCEVALUATE* pnce ) { LogEntryNC(L"Attach");
#ifdef LOGGING
//---- remember first window (app window) hooked for ShutDownCheck() ----
//---- this is only for BoundsChecker (tm) runs for finding leaks ----
if (! g_hwndFirstHooked) { if ((GetMenu(hwnd)) && (! GetParent(hwnd))) g_hwndFirstHooked = hwnd; } #endif
CThemeWnd* pwnd = NULL;
// Note: Important not to do anything here that causes
// a window message to be posted or sent to the window: could
// mean tying ourselves up in a recursive knot (see _ThemeDefWindowProc).
pwnd = FromHwnd( hwnd );
if( NULL == pwnd ) { HTHEME hTheme = NULL; NCEVALUATE nce;
// copy any IN params from NCEVALUATE struct
if( !pnce ) { ZeroMemory(&nce, sizeof(nce)); pnce = &nce; }
ULONG ulTargetFlags = _Evaluate( hwnd, pnce );
// Anything worth theming?
if( TESTFLAG(ulTargetFlags, TWCF_NCTHEMETARGETMASK) ) { hTheme = _AcquireThemeHandle( hwnd, &ulTargetFlags ); if( NULL == hTheme ) { Fail(hwnd); } } else { // reject windows with untargeted a
Reject(hwnd, pnce->fExile); }
if( NULL != hTheme ) { // Yes, create a real nctheme object for the window
if( (pwnd = new CThemeWnd) != NULL ) { if( !pwnd->_AttachInstance( hwnd, hTheme, ulTargetFlags, pnce->pvWndCompat ) ) { pwnd->Release(); pwnd = NULL; } } else // cleanup hTheme if CThemeWnd creation failed
{ CloseThemeData(hTheme); } } }
LogExitNC(L"Attach"); return pwnd; }
//-------------------------------------------------------------------------//
// Instance method: attaches the CThemeWnd object to the specified window.
BOOL CThemeWnd::_AttachInstance( HWND hwnd, HTHEME hTheme, ULONG ulTargetFlags, PVOID pvWndCompat ) { if( VALID_CRITICALSECTION(&_cswm) || NT_SUCCESS(RtlInitializeCriticalSection( &_cswm )) ) { Log(LOG_NCATTACH, L"_AttachInstance: Nonclient attached to hwnd=0x%x", hwnd);
_hwnd = hwnd; _hTheme = hTheme; _fClassFlags = ulTargetFlags;
_fFrameThemed = TESTFLAG( ulTargetFlags, TWCF_FRAME|TWCF_TOOLFRAME ); return SetProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)), this ); }
return FALSE; }
//-------------------------------------------------------------------------//
void CThemeWnd::RemoveWindowProperties(HWND hwnd, BOOL fDestroying) { //---- remove properties that require theme or hooks ----
RemoveProp(hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_HTHEME)));
if (fDestroying) { // Help apps by cleaning up the dialog texture.
RemoveProp(hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_DLGTEXTURING)));
//---- remove all remaining theme properties ----
ApplyStringProp(hwnd, NULL, GetThemeAtom(THEMEATOM_SUBIDLIST)); ApplyStringProp(hwnd, NULL, GetThemeAtom(THEMEATOM_SUBAPPNAME));
//---- notify appinfo (foreign tracking, preview) ----
g_pAppInfo->OnWindowDestroyed(hwnd); } else { //---- only do this if hwnd is not being destroyed ----
ClearExStyleBits(hwnd); } } //-------------------------------------------------------------------------//
// Static wrapper: detaches and destroys the CThemeWnd instance attached to the indicated
// window
void CThemeWnd::Detach( HWND hwnd, DWORD dwDisposition ) { LogEntryNC(L"Detach");
// DO NOT GENERATE ANY WINDOW MESSAGES FROM THIS FUNCTION!!!
// (unless cleaning up frame).
// Prevent message threads from detaching when unhook thread (DetachAll) is executing...
if( !UNHOOKING() || TESTFLAG(dwDisposition, HMD_BULKDETACH) ) { CThemeWnd* pwnd = FromHwnd( hwnd );
if( pwnd ) // nonclient tagged
{ if( VALID_THEMEWND(pwnd) ) { // only one thread flips the _fDetached bit and proceeds through
// instance detatch and object free. Otherwise, object can be freed
// simultaneously on two different threads,
// e.g. (1) message thread and (2) UIAH_UNHOOK thread (ouch! scotthan).
if( !InterlockedCompareExchange( (LONG*)&pwnd->_fDetached, TRUE, FALSE ) ) { pwnd->_DetachInstance( dwDisposition ); pwnd->Release(); } } else { RemoveProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)) ); } }
if (hwnd) { RemoveWindowProperties( hwnd, ((dwDisposition & HMD_WINDOWDESTROY) != 0) ); } }
LogExitNC(L"Detach"); }
//-------------------------------------------------------------------------//
// Instance method: detaches the CThemeWnd object from the specified window.
BOOL CThemeWnd::_DetachInstance( DWORD dwDisposition ) { HWND hwnd = _hwnd;
// untheme maxed MDI child sysbuttons.
ThemeMDIMenuButtons(FALSE, FALSE);
// Here's our last chance to ensure frame theme is withdrawn cleanly.
if( (IsFrameThemed() || IsRevoked(RF_REGION)) && AssignedFrameRgn() && !TESTFLAG(dwDisposition, HMD_PROCESSDETACH|HMD_WINDOWDESTROY)) { RemoveFrameTheme( FTF_REDRAW ); }
//SPEW_THEMEWND( pwnd, 0, TEXT("UxTheme - Detaching and deleting themewnd: %s\n") );
DetachScrollBars( hwnd );
_hwnd = _hwndMDIClient = NULL;
UnloadMdiBtns();
_CloseTheme();
Log(LOG_NCATTACH, L"_DetachInstance: Nonclient detached to hwnd=0x%x", hwnd);
RemoveProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)) ); return TRUE; }
//-------------------------------------------------------------------------//
// Ensures that the specified window will not be themed during its lifetime
BOOL CThemeWnd::Reject( HWND hwnd, BOOL fExile ) { // set a 'nil' tag on the window
return hwnd ? SetProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)), fExile ? THEMEWND_EXILE : THEMEWND_REJECT ) : FALSE; }
//-------------------------------------------------------------------------//
// Ensures that the specified window will not be themed during its lifetime
BOOL CThemeWnd::Fail( HWND hwnd ) { // set a failure tag on the window
return hwnd ? SetProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)), THEMEWND_FAILURE ) : FALSE; }
//-------------------------------------------------------------------------//
// Revokes theming on a themed window
BOOL CThemeWnd::Revoke() { // Warning Will Robinson: After we detach, the CThemeWnd::_hwnd
// and related members will be reset, so save this on the stack.
BOOL fRet = TRUE; HWND hwnd = _hwnd;
if( !IsRevoked(RF_INREVOKE) ) { EnterRevoke(); _dwRevokeFlags &= ~RF_DEFER; Detach( hwnd, HMD_REVOKE ); fRet = Reject( hwnd, TRUE ); LeaveRevoke(); } return fRet; }
//-------------------------------------------------------------------------//
// cookie passed to EnumChildWindows callback for CThemeWnd::DetachAll
typedef struct { DWORD dwProcessId; DWORD dwDisposition; }DETACHALL;
//-------------------------------------------------------------------------//
// EnumChildWindows callback for CThemeWnd::DetachAll
BOOL CThemeWnd::_DetachDesktopWindowsCB( HWND hwnd, LPARAM lParam ) { DETACHALL* pda = (DETACHALL*)lParam;
// detach this window
if( IsWindowProcess( hwnd, pda->dwProcessId ) ) { //---- clear the nonclient theme ----
CThemeWnd::Detach(hwnd, HMD_THEMEDETACH|pda->dwDisposition);
if( !TESTFLAG(pda->dwDisposition, HMD_PROCESSDETACH) ) { //---- clear the client theme now, so that we can invalidate ----
//---- all old theme handles after this. ----
SafeSendMessage(hwnd, WM_THEMECHANGED, (WPARAM)-1, 0);
Log(LOG_TMHANDLE, L"Did SEND of WM_THEMECHANGED to client hwnd=0x%x", hwnd); } }
return TRUE; }
//-------------------------------------------------------------------------//
// Detaches all themed windows managed by this process.
void CThemeWnd::DetachAll( DWORD dwDisposition ) { DETACHALL da; da.dwProcessId = GetCurrentProcessId(); da.dwDisposition = dwDisposition; da.dwDisposition |= HMD_BULKDETACH;
//---- this will enum all windows for this process (all desktops, all child levels) ----
EnumProcessWindows( _DetachDesktopWindowsCB, (LPARAM)&da ); }
//-------------------------------------------------------------------------//
HTHEME CThemeWnd::_AcquireThemeHandle( HWND hwnd, ULONG* pfClassFlags ) { HTHEME hTheme = ::OpenNcThemeData( hwnd, L"Window" );
if( NULL == hTheme ) { if( pfClassFlags ) { if( TESTFLAG(*pfClassFlags, TWCF_ANY) ) (*pfClassFlags) &= ~TWCF_ALL; else *pfClassFlags = 0; } }
//---- Did OpenNcThemeData() discover a new theme ----
if (g_pAppInfo->HasThemeChanged()) { //---- IMPORTANT: we must refresh our theme metrics now, ----
//---- BEFORE we do our nonclient layout calcs & build a region window ----
AcquireNcThemeMetrics(); }
return hTheme; }
//-------------------------------------------------------------------------//
// CThemeWnd::SetFrameTheme
//
// Initiates theming of the frame.
void CThemeWnd::SetFrameTheme( IN ULONG dwFlags, IN OPTIONAL WINDOWINFO* pwi ) { LogEntryNC(L"SetFrameTheme");
ASSERT(TestCF( TWCF_FRAME|TWCF_TOOLFRAME )); InitLameResources();
DWORD fSwp = SWP_NOZORDER|SWP_NOACTIVATE; RECT rcWnd = {0}; BOOL bSwp = FALSE;
if( !TESTFLAG( dwFlags, FTF_NOMODIFYPLACEMENT ) ) { GetWindowRect( _hwnd, &rcWnd ); fSwp |= (SWP_NOSIZE|SWP_NOMOVE/*|SWP_FRAMECHANGED 341700: this flag causes some apps to crash on WINDOWPOSCHANGED*/); bSwp = TRUE; }
// Generate a WM_WINDOWPOSCHANGING message to
// force a SetWindowRgn + frame repaint.
if( TESTFLAG(dwFlags, FTF_REDRAW) ) { fSwp |= SWP_DRAWFRAME; } else { fSwp |= SWP_NOSENDCHANGING; }
// theme MDI menubar buttons
_hwndMDIClient = _FindMDIClient(_hwnd); if( _hwndMDIClient ) { ThemeMDIMenuButtons(TRUE, FALSE); }
// Kick frame region update.
_fFrameThemed = TRUE; // we invoked SetFrameTheme. Must be set BEFORE SetWindowPos.so we handle NCCALCSIZE properly.
SetDirtyFrameRgn(TRUE, TRUE); // ensure region assembly on non-resizing windows and dlgs.
if( !TESTFLAG( dwFlags, FTF_NOMODIFYPLACEMENT ) && bSwp ) { _ScreenToParent( _hwnd, &rcWnd ); SetWindowPos( _hwnd, NULL, rcWnd.left, rcWnd.top, RECTWIDTH(&rcWnd), RECTHEIGHT(&rcWnd), fSwp ); }
LogExitNC(L"SetFrameTheme"); }
//-------------------------------------------------------------------------//
void CThemeWnd::_FreeRegionHandles() { #ifdef DEBUG
if( _hrgnWnd ) { SPEW_RGNRECT(NCTF_RGNWND, TEXT("_FreeRegionHandles() - deleting window region"), _hrgnWnd, -1 ); } #endif DEBUG
SAFE_DELETE_GDIOBJ(_hrgnWnd);
for( int i = 0; i < cFRAMEPARTS; i++ ) { #ifdef DEBUG
if( _rghrgnParts[i] ) { SPEW_RGNRECT(NCTF_RGNWND, TEXT("_FreeRegionHandles() - deleting component region"), _rghrgnParts[i], _ncwm.rgframeparts[i] ); }
if( _rghrgnSizingTemplates[i] ) { SPEW_RGNRECT(NCTF_RGNWND, TEXT("_FreeRegionHandles() - deleting template region"), _rghrgnSizingTemplates[i], _ncwm.rgframeparts[i] ); } #endif DEBUG
SAFE_DELETE_GDIOBJ(_rghrgnParts[i]); SAFE_DELETE_GDIOBJ(_rghrgnSizingTemplates[i]); } }
//-------------------------------------------------------------------------//
// CThemeWnd::RemoveFrameTheme
//
// Initiates theming of the frame. This method will not free the
// theme handle nor update the theme index.
void CThemeWnd::RemoveFrameTheme( ULONG dwFlags ) { LogEntryNC(L"RemoveFrameTheme");
ASSERT(TestCF( TWCF_FRAME|TWCF_TOOLFRAME ));
_fFrameThemed = FALSE; // we're reverting SetFrameTheme
ClearRenderedNcPart(RNCF_ALL);
// Remove region
if( AssignedFrameRgn() && !TESTFLAG(dwFlags, FTF_NOMODIFYRGN) ) { _fAssignedFrameRgn = FALSE; _AssignRgn( NULL, dwFlags ); _FreeRegionHandles(); }
// Force redraw
if( TESTFLAG(dwFlags, FTF_REDRAW) ) InvalidateRect( _hwnd, NULL, TRUE );
ClearLameResources();
LogExitNC(L"RemoveFrameTheme"); }
//-------------------------------------------------------------------------//
BOOL CThemeWnd::IsNcThemed() { if( _hTheme != NULL && (IsRevoked(RF_DEFER) || !IsRevoked(RF_INREVOKE|RF_TYPEMASK)) && TestCF(TWCF_ANY & TWCF_NCTHEMETARGETMASK) ) { if( TestCF(TWCF_FRAME|TWCF_TOOLFRAME) ) { // if we're a frame window, we should be properly initialized
// w/ SetFrameTheme()
return _fFrameThemed; }
return TRUE; } return FALSE; }
//-------------------------------------------------------------------------//
BOOL CThemeWnd::IsFrameThemed() { return IsNcThemed() && _fFrameThemed && (AssignedFrameRgn() ? TRUE : TestCF( TWCF_FRAME|TWCF_TOOLFRAME )); }
//-------------------------------------------------------------------------//
void CThemeWnd::SetDirtyFrameRgn( BOOL fDirty, BOOL fFrameChanged ) { _fDirtyFrameRgn = fDirty;
Log(LOG_NCATTACH, L"SetDirtyFrameRgn: fDirty=%d, fFrameChanged=%d", fDirty, fFrameChanged); if( fFrameChanged ) // assure a region update despite no size change.
{ _sizeRgn.cx = _sizeRgn.cy = -1; } }
//-------------------------------------------------------------------------//
// CThemeWnd::CreateCompositeRgn() - assembles a composite region from
// non-client segment regions sized to fill the specified window rectangle.
//
HRGN CThemeWnd::CreateCompositeRgn( IN const NCWNDMET* pncwm, OUT HRGN rghrgnParts[], OUT HRGN rghrgnTemplates[] ) { ASSERT( pncwm->fFrame == TRUE ); // shouldn't be here unless we're a frame window
HRGN hrgnWnd = NULL, hrgnContent = NULL; HRGN rghrgn[cFRAMEPARTS] = {0}; int i;
if( pncwm->fFullMaxed ) { // All full-screen maximized windows get a region, which is used to clip
// the window to the current monitor. The window region for a maximized
// window consists of the maxcaption region combined with a rect region
// corresponding to the content area.
RECT rcFullCaption = pncwm->rcW0[NCRC_CAPTION]; rcFullCaption.top += pncwm->cnBorders; rcFullCaption.left += pncwm->cnBorders; rcFullCaption.right -= pncwm->cnBorders; if( SUCCEEDED(GetThemeBackgroundRegion(_hTheme, NULL, pncwm->rgframeparts[iCAPTION], pncwm->framestate, &rcFullCaption, &rghrgn[iCAPTION])) ) { SPEW_RGNRECT(NCTF_RGNWND, TEXT("CreateCompositeRgn() maximized caption rgn"), rghrgn[iCAPTION], pncwm->rgframeparts[iCAPTION] ); AddToCompositeRgn(&hrgnWnd, rghrgn[iCAPTION], 0, 0); if( !IsRectEmpty( &pncwm->rcW0[NCRC_CONTENT] ) ) { // remainder of full-maxed frame region is the content area (client+menubar+scrollbars),
// and is always rectangular
hrgnContent = CreateRectRgnIndirect( &pncwm->rcW0[NCRC_CONTENT] ); SPEW_RGNRECT(NCTF_RGNWND, TEXT("CreateCompositeRgn() maximized frame content rgn"), hrgnContent, 0 );
AddToCompositeRgn(&hrgnWnd, hrgnContent, 0, 0); SAFE_DELETE_GDIOBJ(hrgnContent); } } } else { // Normal windows consist of either a stand-alone frame part, or a frame
// part plus a caption part. In the first case, the window region is
// the frame region. In the second case, the window region is a composite
// of the frame and caption rects.
for( i = 0; i < ARRAYSIZE(pncwm->rgframeparts); i++ ) { if( (iCAPTION == i || !pncwm->fMin) && !IsRectEmpty( &pncwm->rcW0[NCRC_FRAMEFIRST + i] ) ) { if( _IsNcPartTransparent(pncwm->rgframeparts[i], _nctmCurrent) ) { if( FAILED(GetThemeBackgroundRegion( _hTheme, NULL, pncwm->rgframeparts[i], pncwm->framestate, &pncwm->rcW0[NCRC_FRAMEFIRST+i], &rghrgn[i] )) ) { rghrgn[i] = NULL; } } else { rghrgn[i] = CreateRectRgnIndirect( &pncwm->rcW0[NCRC_FRAMEFIRST + i] ); } }
if( rghrgn[i] != NULL ) { SPEW_RGNRECT(NCTF_RGNWND, TEXT("CreateCompositeRgn() frame subrgn"), rghrgn[i], pncwm->rgframeparts[i] ); AddToCompositeRgn(&hrgnWnd, rghrgn[i], 0, 0); } }
// don't forget window content area (client+menubar+scrollbars), which is always rectangular
if( !pncwm->fMin && !IsRectEmpty( &pncwm->rcW0[NCRC_CONTENT] ) ) { hrgnContent = CreateRectRgnIndirect( &pncwm->rcW0[NCRC_CONTENT] ); SPEW_RGNRECT(NCTF_RGNWND, TEXT("CreateCompositeRgn() normal frame content rgn"), hrgnContent, 0 );
AddToCompositeRgn(&hrgnWnd, hrgnContent, 0, 0); SAFE_DELETE_GDIOBJ(hrgnContent); } }
// copy subregions back to caller
CopyMemory( rghrgnParts, rghrgn, sizeof(rghrgn) );
// extract frame resizing templates
ZeroMemory( rghrgn, sizeof(rghrgn) ); // reuse region array
for( i = 0; i < cFRAMEPARTS; i++ ) { const RECT* prc = &pncwm->rcW0[NCRC_FRAMEFIRST + i];
if( VALID_WINDOWPART(pncwm->rgsizehitparts[i]) && !IsRectEmpty( prc ) ) { if( SUCCEEDED(GetThemeBackgroundRegion( _hTheme, NULL, pncwm->rgsizehitparts[i], pncwm->framestate, prc, &rghrgn[i])) ) {
SPEW_RGNRECT(NCTF_RGNWND, TEXT("CreateCompositeRgn() sizing template"), rghrgn[i], pncwm->rgframeparts[i] ); } } } CopyMemory( rghrgnTemplates, rghrgn, sizeof(rghrgn) );
return hrgnWnd; }
//-------------------------------------------------------------------------//
void CThemeWnd::AssignFrameRgn( BOOL fAssign, DWORD dwFlags ) { if( fAssign ) { NCWNDMET* pncwm = NULL; NCTHEMEMET nctm = {0}; if( GetNcWindowMetrics( NULL, &pncwm, &nctm, 0 ) ) { // should we set up a window region on this frame?
if( pncwm->fFrame ) { if( _ShouldAssignFrameRgn( pncwm, nctm ) ) { if( (_sizeRgn.cx != RECTWIDTH(&pncwm->rcW0[NCRC_WINDOW]) || _sizeRgn.cy != RECTHEIGHT(&pncwm->rcW0[NCRC_WINDOW])) ) { HRGN hrgnWnd = NULL; HRGN rghrgnParts[cFRAMEPARTS] = {0}; HRGN rghrgnTemplates[cFRAMEPARTS] = {0}; if( (hrgnWnd = CreateCompositeRgn( pncwm, rghrgnParts, rghrgnTemplates )) != NULL ) { _sizeRgn.cx = RECTWIDTH(&pncwm->rcW0[NCRC_WINDOW]); _sizeRgn.cy = RECTHEIGHT(&pncwm->rcW0[NCRC_WINDOW]);
// cache all of our regions for fast hit-testing.
_FreeRegionHandles(); _hrgnWnd = _DupRgn( hrgnWnd ); // dup this one cuz after _AssignRgn, we don't own it.
CopyMemory( _rghrgnParts, rghrgnParts, sizeof(_rghrgnParts) ); CopyMemory( _rghrgnSizingTemplates, rghrgnTemplates, sizeof(_rghrgnSizingTemplates) );
// assign the region
_AssignRgn( hrgnWnd, dwFlags ); } } } // otherwise, if we've assigned a region, make sure we remove it.
else if( AssignedFrameRgn() ) { fAssign = FALSE; } } } }
if( !fAssign ) { _AssignRgn( NULL, dwFlags ); FillMemory(&_sizeRgn, sizeof(_sizeRgn), 0xFF); _FreeRegionHandles(); } SetDirtyFrameRgn(FALSE); // make sure we reset this in case we didn't hit _AssignRgn.
}
//-------------------------------------------------------------------------//
// CThemeWnd::_AssignRgn() - assigns the specified region
// to the window, prevents recursion (SetWindowRgn w/ bRedraw == TRUE
// generates WM_WINDOWPOSCHANGING, WM_NCCALCSIZE, && WM_NCPAINT).
//
void CThemeWnd::_AssignRgn( HRGN hrgn, DWORD dwFlags ) { if( TESTFLAG(dwFlags, FTF_NOMODIFYRGN) ) { _fAssignedFrameRgn = FALSE; } else if( !IsWindowInDestroy(_hwnd) ) { // Assign the new region.
_fAssigningFrameRgn = TRUE; SPEW_RGNRECT(NCTF_RGNWND, TEXT("_AssignRgn() rect"), hrgn, -1 ); _fAssignedFrameRgn = SetWindowRgn( _hwnd, hrgn, TESTFLAG(dwFlags, FTF_REDRAW) ) != 0; _fAssigningFrameRgn = FALSE;
} SetDirtyFrameRgn(FALSE); }
//-------------------------------------------------------------------------//
// CThemeWnd::GetNcWindowMetrics
//
// Computes internal per-window theme metrics.
//
BOOL CThemeWnd::GetNcWindowMetrics( IN OPTIONAL LPCRECT prcWnd, OUT OPTIONAL NCWNDMET** ppncwm, OUT OPTIONAL NCTHEMEMET* pnctm, IN DWORD dwOptions ) { LogEntryNC(L"GetNcWindowMetrics");
NCTHEMEMET nctm; BOOL bRet = FALSE; BOOL fMenuBar = _ncwm.cyMenu != 0; WINDOWPARTS rgframeparts[cFRAMEPARTS];
CopyMemory( rgframeparts, _ncwm.rgframeparts, sizeof(rgframeparts) ); // fetch per-theme metrics; we're going to need theme throughout
if (TESTFLAG(dwOptions, NCWMF_PREVIEW)) { _LoadNcThemeMetrics(_hwnd, &nctm); } else if( !GetCurrentNcThemeMetrics( &nctm ) ) { goto exit; } if( pnctm ) *pnctm = nctm;
if( !_ncwm.fValid || prcWnd != NULL ) dwOptions |= NCWMF_RECOMPUTE;
if( TESTFLAG(dwOptions, NCWMF_RECOMPUTE) ) { // get caption text size before entering critsec (sends WM_GETTEXTLENGTH, WM_GETTEXT).
SIZE sizeCaptionText = {0}; HFONT hfCaption = NULL; HWND hwndMDIActive = NULL;
// Do rough determination of whether or not we're a frame window and need to compute text metrics.
// We'll finalize this later
BOOL fFrame, fSmallFrame;
if( _ncwm.fValid ) { fFrame = TESTFLAG( CThemeWnd::EvaluateStyle(_ncwm.dwStyle, _ncwm.dwExStyle), TWCF_FRAME|TWCF_TOOLFRAME ); fSmallFrame = TESTFLAG( CThemeWnd::EvaluateStyle(_ncwm.dwStyle, _ncwm.dwExStyle), TWCF_TOOLFRAME ); } else { fFrame = TestCF(TWCF_FRAME|TWCF_TOOLFRAME); fSmallFrame = TestCF(TWCF_TOOLFRAME); }
// Compute text metrics outside of critical section (sends WM_GETTEXT);
if( fFrame && _fFrameThemed ) { hfCaption = NcGetCaptionFont( fSmallFrame ); _GetNcCaptionTextSize( _hTheme, _hwnd, hfCaption, &sizeCaptionText ); } // Retrieve active MDI sibling outside of critical section (sends WM_MDIGETACTIVE);
if( TESTFLAG(GetWindowLong(_hwnd, GWL_EXSTYLE), WS_EX_MDICHILD) ) { hwndMDIActive = _MDIGetActive( GetParent(_hwnd) ); } ASSERT(VALID_CRITICALSECTION(&_cswm));
if( VALID_CRITICALSECTION(&_cswm) ) { EnterCriticalSection( &_cswm ); ZeroMemory( &_ncwm, sizeof(_ncwm) );
if( (bRet = _GetWindowMetrics( _hwnd, hwndMDIActive, &_ncwm )) != FALSE ) { _ComputeNcWindowStatus( _hwnd, _ncwm.dwWindowStatus, &_ncwm );
// if window RECT is provided by the caller, stuff it now.
if( prcWnd ) { _ncwm.rcS0[NCRC_WINDOW] = *prcWnd; SetRectEmpty( &_ncwm.rcS0[NCRC_CLIENT] ); }
// stuff caption text size
_ncwm.sizeCaptionText = sizeCaptionText; _ncwm.hfCaption = hfCaption;
// retrieve frame metrics.
if( _GetNcFrameMetrics( _hwnd, _hTheme, nctm, _ncwm ) ) { if( _ncwm.fFrame ) { // user32!SetMenu has been called, or the caption or frame part has changed
// So ensure frame region update.
if( (_ncwm.cyMenu == 0 && fMenuBar) || (_ncwm.cyMenu > 0 && !fMenuBar) || memcmp( rgframeparts, _ncwm.rgframeparts, sizeof(rgframeparts) ) ) { SetDirtyFrameRgn(TRUE, TRUE); }
// Compute NC button placement
AcquireFrameIcon(_ncwm.dwStyle, _ncwm.dwExStyle, FALSE); _GetNcBtnMetrics( &_ncwm, &nctm, _hAppIcon, _MNCanClose(_hwnd) );
// Determine the caption margin for lame button metrics.
_GetNcCaptionMargins( _hTheme, nctm, _ncwm ); _GetNcCaptionTextRect( &_ncwm );
if( _ncwm.fFrame ) { GetLameButtonMetrics( &_ncwm, &sizeCaptionText ); } }
// Compute window-relative metrics
//
// If passed a window rect, base offsets on current window rect.
// This is done to ensure preview window's (_hwnd) fake child windows are rendered correctly.
RECT rcWnd = _ncwm.rcS0[NCRC_WINDOW];
if( prcWnd ) { if( _hwnd ) GetWindowRect( _hwnd, &rcWnd );
// for an incoming window rect, assign the computed client rect.
_ncwm.rcS0[NCRC_CLIENT] = _ncwm.rcS0[NCRC_UXCLIENT];
}
for( int i = NCRC_FIRST; i < NCRC_COUNT; i++ ) { _ncwm.rcW0[i] = _ncwm.rcS0[i]; OffsetRect( &_ncwm.rcW0[i], -rcWnd.left, -rcWnd.top ); }
// All base computations are done; mark valid.
_ncwm.fValid = TRUE; } } LeaveCriticalSection( &_cswm ); } }
if( ppncwm ) { *ppncwm = &_ncwm; }
bRet = TRUE;
exit: LogExitNC(L"GetNcWindowMetrics"); return bRet; }
//-------------------------------------------------------------------------//
inline COLORREF _GetNcCaptionTextColor( FRAMESTATES iStateId ) { return GetSysColor( FS_ACTIVE == iStateId ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT ); }
//-------------------------------------------------------------------------//
// Get CTLCOLOR brush for solid fills
void _GetBrushesForPart(HTHEME hTheme, int iPart, HBITMAP* phbm, HBRUSH* phbr) { int nBgType;
*phbm = NULL; *phbr = NULL;
// Get CTLCOLOR brush for solid fills
HRESULT hr = GetThemeEnumValue( hTheme, iPart, 0, TMT_BGTYPE, &nBgType ); if( SUCCEEDED( hr )) { if (BT_BORDERFILL == nBgType) { int nFillType; hr = GetThemeEnumValue( hTheme, iPart, 0, TMT_FILLTYPE, &nFillType ); if (SUCCEEDED( hr ) && FT_SOLID == nFillType) { COLORREF cr; hr = GetThemeColor( hTheme, iPart, 0, TMT_FILLCOLOR, &cr);
*phbr = CreateSolidBrush(cr); } else { ASSERTMSG(FALSE, "Themes: The theme file specified an invalid fill type for dialog boxes"); } } else if (BT_IMAGEFILE == nBgType) { HDC hdc = GetWindowDC(NULL); if ( hdc ) { hr = GetThemeBitmap(hTheme, hdc, iPart, 0, NULL, phbm); if (SUCCEEDED(hr)) { *phbr = CreatePatternBrush(*phbm); } ReleaseDC(NULL, hdc); } } } }
//-------------------------------------------------------------------------//
//
// Chooses appropriate hit testing parts for the various Nc area
//
void _GetNcSizingTemplates( IN const NCTHEMEMET& nctm, IN OUT NCWNDMET& ncwm ) // window metric block. dwStyle, dwExStyle, rcS0[NCRC_WINDOW] members are required.
{ FillMemory( ncwm.rgsizehitparts, sizeof(ncwm.rgsizehitparts), BOGUS_WINDOWPART );
// No need on windows without frames
if( !ncwm.fFrame ) return;
// minimized or full-screen maximized window
if( ncwm.fMin || ncwm.fFullMaxed ) return;
// No need on windows that aren't sizable
if( !TESTFLAG(ncwm.dwStyle, WS_THICKFRAME) ) return;
if( ncwm.fSmallFrame) { if (nctm.fSmCapSizingTemplate) ncwm.rgsizehitparts[iCAPTION] = WP_SMALLCAPTIONSIZINGTEMPLATE;
if (nctm.fSmLeftSizingTemplate) ncwm.rgsizehitparts[iFRAMELEFT] = WP_SMALLFRAMELEFTSIZINGTEMPLATE;
if (nctm.fSmRightSizingTemplate) ncwm.rgsizehitparts[iFRAMERIGHT] = WP_SMALLFRAMERIGHTSIZINGTEMPLATE;
if (nctm.fSmBottomSizingTemplate) ncwm.rgsizehitparts[iFRAMEBOTTOM] = WP_SMALLFRAMEBOTTOMSIZINGTEMPLATE; } else { if (nctm.fCapSizingTemplate) ncwm.rgsizehitparts[iCAPTION] = WP_CAPTIONSIZINGTEMPLATE;
if (nctm.fLeftSizingTemplate) ncwm.rgsizehitparts[iFRAMELEFT] = WP_FRAMELEFTSIZINGTEMPLATE;
if (nctm.fRightSizingTemplate) ncwm.rgsizehitparts[iFRAMERIGHT] = WP_FRAMERIGHTSIZINGTEMPLATE;
if (nctm.fBottomSizingTemplate) ncwm.rgsizehitparts[iFRAMEBOTTOM] = WP_FRAMEBOTTOMSIZINGTEMPLATE; } }
//-------------------------------------------------------------------------//
//
// Computes theme metrics for frame window.
//
BOOL _GetNcFrameMetrics( IN OPTIONAL HWND hwnd, // window handle (required for multiline menubar calcs).
IN HTHEME hTheme, // theme handle (required)
IN const NCTHEMEMET& nctm, // theme metric block
IN OUT NCWNDMET& ncwm ) // window metric block. dwStyle, dwExStyle, rcS0[NCRC_WINDOW] members are required.
{ LogEntryNC(L"_GetNcFrameMetrics"); ASSERT(hTheme); // recompute style class
ncwm.dwStyleClass = CThemeWnd::EvaluateStyle( ncwm.dwStyle, ncwm.dwExStyle ); ncwm.cnBorders = _GetWindowBorders( ncwm.dwStyle, ncwm.dwExStyle );
// compute frame attributes, state
ncwm.fFrame = TESTFLAG( ncwm.dwStyleClass, (TWCF_FRAME|TWCF_TOOLFRAME) ); ncwm.fSmallFrame = TESTFLAG( ncwm.dwStyleClass, TWCF_TOOLFRAME );
// compute frame and caption parts
if( ncwm.fFrame ) { ncwm.rgframeparts[iFRAMEBOTTOM] = ncwm.rgframeparts[iFRAMELEFT] = ncwm.rgframeparts[iFRAMERIGHT] = ncwm.rgframeparts[iCAPTION] = BOGUS_WINDOWPART;
if( ncwm.fMin ) // minimized window
{ ncwm.rgframeparts[iCAPTION] = WP_MINCAPTION; } else if( ncwm.fFullMaxed ) // full-screen maximized window
{ ncwm.rgframeparts[iCAPTION] = WP_MAXCAPTION; } else // normal or partial-screen maximized window with thick border
{ if( ncwm.fSmallFrame ) { ncwm.rgframeparts[iCAPTION] = WP_SMALLCAPTION; ncwm.rgframeparts[iFRAMELEFT] = WP_SMALLFRAMELEFT; ncwm.rgframeparts[iFRAMERIGHT] = WP_SMALLFRAMERIGHT; ncwm.rgframeparts[iFRAMEBOTTOM] = WP_SMALLFRAMEBOTTOM; } else { ncwm.rgframeparts[iCAPTION] = ncwm.fMaxed ? WP_MAXCAPTION : WP_CAPTION; ncwm.rgframeparts[iFRAMELEFT] = WP_FRAMELEFT; ncwm.rgframeparts[iFRAMERIGHT] = WP_FRAMERIGHT; ncwm.rgframeparts[iFRAMEBOTTOM] = WP_FRAMEBOTTOM; } }
// stash caption text color.
ncwm.rgbCaption = _GetNcCaptionTextColor( ncwm.framestate ); // retrieve sizing templates.
_GetNcSizingTemplates( nctm, ncwm ); }
//-----------------------------------------------------------//
// Frame metrics
//
// Frame area includes 'skin' boundaries,
// menu, integrated caption and client edge.
//
// Independent of the frame is the separate caption seg,
// scrollbars, and sizebox
//-----------------------------------------------------------//
if( ncwm.fFrame ) // frame windows only
{ // Initialize positions of main frame components...
// Content rect: area bounded by frame theme.
// Client rect: area contained in content rect that excludes all nonclient
// elements (viz, scrollbars, menubar, inside edges).
// Caption rect: pertains to minimized and maximized windows,
// and normal windows if the theme defines a caption part
ncwm.rcS0[NCRC_CAPTION] = ncwm.rcS0[NCRC_CONTENT] = ncwm.rcS0[NCRC_WINDOW]; SetRectEmpty( &ncwm.rcS0[NCRC_UXCLIENT] );
if( ncwm.fMin ) /* minimized frame */ { // zero out content, client rectangles.
ncwm.rcS0[NCRC_CONTENT].right = ncwm.rcS0[NCRC_CONTENT].left; ncwm.rcS0[NCRC_CONTENT].bottom = ncwm.rcS0[NCRC_CONTENT].top;
ncwm.rcS0[NCRC_CLIENT] = ncwm.rcS0[NCRC_UXCLIENT] = ncwm.rcS0[NCRC_CONTENT]; } else { NONCLIENTMETRICS ncm; if( NcGetNonclientMetrics( &ncm, FALSE ) ) { ncwm.rcS0[NCRC_FRAMEBOTTOM] = ncwm.rcS0[NCRC_FRAMELEFT] = ncwm.rcS0[NCRC_FRAMERIGHT] = ncwm.rcS0[NCRC_WINDOW];
// themed caption rect spans left, top, right bordersS
// and 1 pixel edge below caption
ncwm.rcS0[NCRC_CAPTION].bottom = ncwm.rcS0[NCRC_CAPTION].top + ncwm.cnBorders + (ncwm.fSmallFrame ? ncm.iSmCaptionHeight : ncm.iCaptionHeight) + 1 /* 1 pixel below caption */;
// update the content and rects while we're here:
InflateRect( &ncwm.rcS0[NCRC_CONTENT], -ncwm.cnBorders, -ncwm.cnBorders ); ncwm.rcS0[NCRC_CONTENT].top = ncwm.rcS0[NCRC_CAPTION].bottom; if( ncwm.rcS0[NCRC_CONTENT].bottom < ncwm.rcS0[NCRC_CONTENT].top ) ncwm.rcS0[NCRC_CONTENT].bottom = ncwm.rcS0[NCRC_CONTENT].top;
// at this point the client rect is identical to the content rect (haven't computed menubar, scrollbars).
ncwm.rcS0[NCRC_UXCLIENT] = ncwm.rcS0[NCRC_CONTENT];
// bottom border segment.
ncwm.rcS0[NCRC_FRAMEBOTTOM].top = ncwm.rcS0[NCRC_FRAMEBOTTOM].bottom - ncwm.cnBorders;
// side border segments
ncwm.rcS0[NCRC_FRAMELEFT].top = ncwm.rcS0[NCRC_FRAMERIGHT].top = ncwm.rcS0[NCRC_CAPTION].bottom; ncwm.rcS0[NCRC_FRAMELEFT].bottom = ncwm.rcS0[NCRC_FRAMERIGHT].bottom = ncwm.rcS0[NCRC_FRAMEBOTTOM].top;
ncwm.rcS0[NCRC_FRAMELEFT].right = ncwm.rcS0[NCRC_FRAMELEFT].left + ncwm.cnBorders; ncwm.rcS0[NCRC_FRAMERIGHT].left = ncwm.rcS0[NCRC_FRAMERIGHT].right - ncwm.cnBorders; } } } else // frameless windows with scrollbars and/or client-edge:
{ // Non-frame windows
ncwm.rcS0[NCRC_UXCLIENT] = ncwm.rcS0[NCRC_WINDOW]; InflateRect( &ncwm.rcS0[NCRC_UXCLIENT], -ncwm.cnBorders, -ncwm.cnBorders ); ncwm.rcS0[NCRC_CONTENT] = ncwm.rcS0[NCRC_UXCLIENT]; }
// Menubar
if( !(ncwm.fMin || TESTFLAG( ncwm.dwStyle, WS_CHILD )) ) // child windows don't have menubars
{ // Menubar offsets (for painting)
ncwm.cnMenuOffsetTop = ncwm.rcS0[NCRC_CONTENT].top - ncwm.rcS0[NCRC_WINDOW].top; ncwm.cnMenuOffsetLeft = ncwm.rcS0[NCRC_CONTENT].left - ncwm.rcS0[NCRC_WINDOW].left; ncwm.cnMenuOffsetRight = ncwm.rcS0[NCRC_WINDOW].right - ncwm.rcS0[NCRC_CONTENT].right;
if( hwnd ) { // calc menubar does the right thing for multiline menubars
ncwm.cyMenu = CalcMenuBar( hwnd, ncwm.cnMenuOffsetLeft, ncwm.cnMenuOffsetRight, ncwm.cnMenuOffsetTop, &ncwm.rcS0[NCRC_WINDOW] ); } else { // no window (e.g. preview) == no menu, meaning don't call CalcMenuBar.
// we emulate computations best we can:
ncwm.cyMenu = NcGetSystemMetrics( SM_CYMENUSIZE ); }
// CalcMenuBar and SM_CYMENUSIZE are 1 pixel short of reality.
if( ncwm.cyMenu ) ncwm.cyMenu += nctm.dyMenuBar;
// Menubar rect (for hit-testing and clipping)
SetRect( &ncwm.rcS0[NCRC_MENUBAR], ncwm.rcS0[NCRC_CONTENT].left, ncwm.rcS0[NCRC_CONTENT].top, ncwm.rcS0[NCRC_CONTENT].right, min(ncwm.rcS0[NCRC_CONTENT].bottom, ncwm.rcS0[NCRC_CONTENT].top + ncwm.cyMenu) );
ncwm.rcS0[NCRC_UXCLIENT].top = ncwm.rcS0[NCRC_MENUBAR].bottom; }
// Client Edge.
if( !ncwm.fMin && TESTFLAG(ncwm.dwExStyle, WS_EX_CLIENTEDGE) ) { CopyRect( &ncwm.rcS0[NCRC_CLIENTEDGE], &ncwm.rcS0[NCRC_UXCLIENT] ); InflateRect( &ncwm.rcS0[NCRC_UXCLIENT], -NcGetSystemMetrics( SM_CXEDGE ), -NcGetSystemMetrics( SM_CYEDGE )); }
//-----------------------------------------------------------//
// Scrollbars and sizebox/gripper
//-----------------------------------------------------------//
if( !ncwm.fMin ) { // horizontal scroll bar.
if( TESTFLAG(ncwm.dwStyle, WS_HSCROLL) ) { ncwm.rcS0[NCRC_HSCROLL] = ncwm.rcS0[NCRC_UXCLIENT]; ncwm.rcS0[NCRC_HSCROLL].top = ncwm.rcS0[NCRC_UXCLIENT].bottom = ncwm.rcS0[NCRC_HSCROLL].bottom - NcGetSystemMetrics( SM_CYHSCROLL );
if( IsRectEmpty( &ncwm.rcS0[NCRC_CLIENT] ) /* this happens in preview */ ) { ncwm.rcS0[NCRC_HSCROLL].left = ncwm.rcS0[NCRC_UXCLIENT].left; ncwm.rcS0[NCRC_HSCROLL].right = ncwm.rcS0[NCRC_UXCLIENT].right; } else { ncwm.rcS0[NCRC_HSCROLL].left = ncwm.rcS0[NCRC_CLIENT].left; ncwm.rcS0[NCRC_HSCROLL].right = ncwm.rcS0[NCRC_CLIENT].right; } }
// vertical scroll bar
if( TESTFLAG(ncwm.dwStyle, WS_VSCROLL) ) { ncwm.rcS0[NCRC_VSCROLL] = ncwm.rcS0[NCRC_UXCLIENT];
if( TESTFLAG(ncwm.dwExStyle, WS_EX_LAYOUTRTL) ^ TESTFLAG(ncwm.dwExStyle, WS_EX_LEFTSCROLLBAR) ) { ncwm.rcS0[NCRC_VSCROLL].right = ncwm.rcS0[NCRC_UXCLIENT].left = ncwm.rcS0[NCRC_VSCROLL].left + NcGetSystemMetrics( SM_CXVSCROLL );
// Adjust for horz scroll, gripper
if( TESTFLAG(ncwm.dwStyle, WS_HSCROLL) ) { ncwm.rcS0[NCRC_SIZEBOX]= ncwm.rcS0[NCRC_HSCROLL]; ncwm.rcS0[NCRC_SIZEBOX].right = ncwm.rcS0[NCRC_HSCROLL].left = ncwm.rcS0[NCRC_UXCLIENT].left; } } else { ncwm.rcS0[NCRC_VSCROLL].left = ncwm.rcS0[NCRC_UXCLIENT].right = ncwm.rcS0[NCRC_VSCROLL].right - NcGetSystemMetrics( SM_CXVSCROLL );
// Adjust for horz scroll, gripper
if( TESTFLAG(ncwm.dwStyle, WS_HSCROLL) ) { ncwm.rcS0[NCRC_SIZEBOX]= ncwm.rcS0[NCRC_HSCROLL]; ncwm.rcS0[NCRC_SIZEBOX].left = ncwm.rcS0[NCRC_HSCROLL].right = ncwm.rcS0[NCRC_UXCLIENT].right; } }
if( IsRectEmpty( &ncwm.rcS0[NCRC_CLIENT] ) /* this happens in preview */ ) { ncwm.rcS0[NCRC_VSCROLL].top = ncwm.rcS0[NCRC_UXCLIENT].top; ncwm.rcS0[NCRC_VSCROLL].bottom = ncwm.rcS0[NCRC_UXCLIENT].bottom; } else { ncwm.rcS0[NCRC_VSCROLL].top = ncwm.rcS0[NCRC_CLIENT].top; ncwm.rcS0[NCRC_VSCROLL].bottom = ncwm.rcS0[NCRC_CLIENT].bottom; } } }
LogExitNC(L"_GetNcFrameMetrics"); return TRUE; }
#define EXT_TRACK_VERT 0x01
#define EXT_TRACK_HORZ 0x02
//-------------------------------------------------------------------------//
void _GetNcBtnHitTestRect( IN const NCWNDMET* pncwm, IN UINT uHitcode, BOOL fWindowRelative, OUT LPRECT prcHit ) { const RECT* prcBtn = NULL; int dxLeft = 0; // button's left side delta
int dxRight = 0; // button's right side delta
// adjust hitrect to classic-look caption bar strip:
RECT rcHit = fWindowRelative ? pncwm->rcW0[NCRC_CAPTION] : pncwm->rcS0[NCRC_CAPTION]; rcHit.top += pncwm->cnBorders; rcHit.left += pncwm->cnBorders; rcHit.right -= pncwm->cnBorders; rcHit.bottom -= 1;
// determine which button we're working with, how to extend the left, right sides.
switch( uHitcode ) { case HTMINBUTTON: prcBtn = fWindowRelative ? &pncwm->rcW0[NCRC_MINBTN] : &pncwm->rcS0[NCRC_MINBTN]; dxLeft = -1; break;
case HTMAXBUTTON: prcBtn = fWindowRelative ? &pncwm->rcW0[NCRC_MAXBTN] : &pncwm->rcS0[NCRC_MAXBTN]; dxRight = 1; break;
case HTHELP: prcBtn = fWindowRelative ? &pncwm->rcW0[NCRC_HELPBTN] : &pncwm->rcS0[NCRC_HELPBTN]; dxLeft = -1; dxRight = 1; break;
case HTCLOSE: prcBtn = fWindowRelative ? &pncwm->rcW0[NCRC_CLOSEBTN] : &pncwm->rcS0[NCRC_CLOSEBTN]; dxLeft = -1; dxRight = rcHit.right - prcBtn->right; break;
case HTSYSMENU: prcBtn = fWindowRelative ? &pncwm->rcW0[NCRC_SYSBTN] : &pncwm->rcS0[NCRC_SYSBTN]; dxLeft = rcHit.left - prcBtn->left; dxRight = 1; break; }
if( prcBtn ) { *prcHit = *prcBtn; if( !IsRectEmpty( prcBtn ) ) { rcHit.left = prcBtn->left + dxLeft; rcHit.right = prcBtn->right + dxRight; *prcHit = rcHit; } } else { SetRectEmpty( prcHit ); } }
//-------------------------------------------------------------------------//
// wraps alloc, retrieval of window text
LPWSTR _AllocWindowText( IN HWND hwnd ) { LPWSTR pszRet = NULL;
if (hwnd && IsWindow(hwnd)) { if( (pszRet = new WCHAR[MAX_PATH]) != NULL ) { int cch; if( (cch = InternalGetWindowText(hwnd, pszRet, MAX_PATH)) <= 0 ) { __try // some wndprocs can't handle an early WM_GETTEXT (eg.310700).
{ cch = GetWindowText(hwnd, pszRet, MAX_PATH); } __except(EXCEPTION_EXECUTE_HANDLER) { cch = 0; } } if( !cch ) { SAFE_DELETE_ARRAY(pszRet); // delete and zero pointer
} } }
return pszRet; }
//-------------------------------------------------------------------------//
// _GetNcCaptionMargins() - computes a margin from the window ULC based on the
// offsets in the theme and the location of enabled caption buttons. The left
// margin is to the right of the last left-aligned button, and the right margin
// is to the left of the first right-aligned button.
//
BOOL _GetNcCaptionMargins( IN HTHEME hTheme, IN const NCTHEMEMET& nctm, IN OUT NCWNDMET& ncwm ) { ZeroMemory( &ncwm.CaptionMargins, sizeof(ncwm.CaptionMargins) ); if( ncwm.fFrame ) { // assign per-window CaptinMargins, hfCaption values
if( ncwm.fSmallFrame ) { ncwm.CaptionMargins = nctm.marSmCaptionText; } else { if( ncwm.fMaxed ) { ncwm.CaptionMargins = nctm.marMaxCaptionText; } else if( ncwm.fMin ) { ncwm.CaptionMargins = nctm.marMinCaptionText; } else { ncwm.CaptionMargins = nctm.marCaptionText; } } ncwm.hfCaption = NcGetCaptionFont(ncwm.fSmallFrame);
RECT rcContainer = ncwm.rcS0[NCRC_CAPTION]; RECT *prcBtn = &ncwm.rcS0[NCBTNFIRST]; rcContainer.left += ncwm.cnBorders; rcContainer.right -= ncwm.cnBorders;
// sysmenu icon, if present, is the leftmost limit
if( !IsRectEmpty( &ncwm.rcS0[NCRC_SYSBTN] ) ) { rcContainer.left = ncwm.rcS0[NCRC_SYSBTN].right; }
// Compute our rightmost limit
for( UINT cRects = NCBTNRECTS; cRects; --cRects, ++prcBtn ) { if (!IsRectEmpty(prcBtn)) { if( prcBtn->left < rcContainer.right ) { rcContainer.right = prcBtn->left; } } }
if( rcContainer.right < rcContainer.left ) { rcContainer.right = rcContainer.left; }
// final captions margins are adjusted to accommodate buttons.
ncwm.CaptionMargins.cxLeftWidth += (rcContainer.left - ncwm.rcS0[NCRC_CAPTION].left); ncwm.CaptionMargins.cxRightWidth += (ncwm.rcS0[NCRC_CAPTION].right - rcContainer.right);
return TRUE; } return FALSE; }
//-------------------------------------------------------------------------//
BOOL _GetNcCaptionTextSize( HTHEME hTheme, HWND hwnd, HFONT hf, OUT SIZE* psizeCaption ) { BOOL fRet = FALSE; LPWSTR pszCaption = _AllocWindowText( hwnd );
psizeCaption->cx = psizeCaption->cy = 0;
if( pszCaption ) { HDC hdc = GetWindowDC(hwnd); if( hdc ) { //---- select font ----
HFONT hf0 = (HFONT)SelectObject(hdc, hf);
//---- let theme mgr do the calculation ----
RECT rcExtent; HRESULT hr = GetThemeTextExtent( hTheme, hdc, WP_CAPTION, 0, pszCaption, lstrlen(pszCaption), 0, NULL, &rcExtent );
//---- store result in "psizeCaption ----
if (SUCCEEDED(hr)) { psizeCaption->cx = WIDTH(rcExtent); psizeCaption->cy = HEIGHT(rcExtent); }
//---- clean up ----
SelectObject(hdc, hf0); ReleaseDC(hwnd, hdc); }
SAFE_DELETE_ARRAY(pszCaption); } return fRet; }
//-------------------------------------------------------------------------//
// Retrieves position of available area for caption text, in window-relative
// coordinates
BOOL _GetNcCaptionTextRect( IN OUT NCWNDMET* pncwm ) { pncwm->rcS0[NCRC_CAPTIONTEXT] = pncwm->rcS0[NCRC_CAPTION];
// accommodate classic top sizing border:
pncwm->rcS0[NCRC_CAPTIONTEXT].top += pncwm->cnBorders;
// Assign left, right based on resp. caption margins
pncwm->rcS0[NCRC_CAPTIONTEXT].left += pncwm->CaptionMargins.cxLeftWidth; pncwm->rcS0[NCRC_CAPTIONTEXT].right -= pncwm->CaptionMargins.cxRightWidth;
// vertically center the text between margins
int cyPadding = (RECTHEIGHT(&pncwm->rcS0[NCRC_CAPTIONTEXT]) - pncwm->sizeCaptionText.cy)/2; pncwm->rcS0[NCRC_CAPTIONTEXT].top += cyPadding; pncwm->rcS0[NCRC_CAPTIONTEXT].bottom -= cyPadding;
return TRUE; }
//-------------------------------------------------------------------------//
// retrieve the window icon
HICON CThemeWnd::AcquireFrameIcon( DWORD dwStyle, DWORD dwExStyle, BOOL fWinIniChange ) { if( _hAppIcon != NULL ) { if( fWinIniChange ) { _hAppIcon = NULL; } }
if( !TESTFLAG(dwStyle, WS_SYSMENU) || TESTFLAG(dwExStyle, WS_EX_TOOLWINDOW) ) { // return nil value without throwing away cached icon handle;
// this may be a transient style change.
return NULL; }
NONCLIENTMETRICS ncm = {0}; NcGetNonclientMetrics( &ncm, FALSE ); BOOL fPerferLargeIcon = ((30 < ncm.iCaptionHeight) ? TRUE : FALSE); if( NULL == _hAppIcon && NULL == (_hAppIcon = _GetWindowIcon(_hwnd, fPerferLargeIcon)) ) { if ( HAS_CAPTIONBAR(dwStyle) && ((dwStyle & (WS_BORDER|WS_DLGFRAME)) != WS_DLGFRAME) && !TESTFLAG(dwExStyle, WS_EX_DLGMODALFRAME) ) { // If we still can't get an icon and the window has
// SYSMENU set, then they get the default winlogo icon
_hAppIcon = LoadIcon(NULL, IDI_WINLOGO); } }
return _hAppIcon; }
//-------------------------------------------------------------------------//
// CThemeWnd::ScreenToWindow() - transforms points from screen coords to
// window coords.
//
void CThemeWnd::ScreenToWindow( LPPOINT prgPts, UINT cPts ) { RECT rcWnd; if( GetWindowRect( _hwnd, &rcWnd ) ) { for( UINT i = 0; i < cPts; i++ ) { prgPts[i].x -= rcWnd.left; prgPts[i].y -= rcWnd.top; } } }
//-------------------------------------------------------------------------//
// CThemeWnd::ScreenToWindow() - transforms non-empty rectangles from
// screen coords to window coords.
//
void CThemeWnd::ScreenToWindowRect( LPRECT prc ) { if( !IsRectEmpty(prc) ) ScreenToWindow( (LPPOINT)prc, 2 ); }
//-------------------------------------------------------------------------//
// CThemeWnd::InitWindowMetrics()
//
// initializes theme resources
void CThemeWnd::InitWindowMetrics() { ZeroMemory( &_ncwm, sizeof(_ncwm) ); }
//-------------------------------------------------------------------------//
BOOL _fClassicNcBtnMetricsReset = TRUE;
//-------------------------------------------------------------------------//
// computes classic button position
BOOL _GetNcBtnMetrics( IN OUT NCWNDMET* pncwm, IN const NCTHEMEMET* pnctm, IN HICON hAppIcon, IN OPTIONAL BOOL fCanClose ) { BOOL fRet = TRUE; if( pncwm && pncwm->fFrame && TESTFLAG(pncwm->dwStyle, WS_SYSMENU) ) { NONCLIENTMETRICS ncm; fRet = NcGetNonclientMetrics( &ncm, FALSE ); if( fRet ) { // (1) compute baseline rectangles
int cxEdge = NcGetSystemMetrics( SM_CXEDGE ); int cyEdge = NcGetSystemMetrics( SM_CYEDGE );
int cyBtn = NcGetSystemMetrics( SM_CYSIZE ); int cxBtn = MulDiv( cyBtn, pnctm->sizeBtn.cx, pnctm->sizeBtn.cy );
int cySmBtn = NcGetSystemMetrics( SM_CYSMSIZE ); int cxSmBtn = MulDiv( cySmBtn, pnctm->sizeSmBtn.cx, pnctm->sizeSmBtn.cy );
// remove padding from x,y
cyBtn -= (cyEdge * 2); cxBtn -= (cyEdge * 2); cySmBtn -= (cyEdge * 2); cxSmBtn -= (cyEdge * 2);
RECT rcClose, rcMin, rcMax, rcHelp, rcSys, rcSmClose;
// common top, w/ zero v-offset
rcClose.top = rcMin.top = rcMax.top = rcHelp.top = rcSys.top = rcSmClose.top = 0;
// common bottom, w/ zero v-offset
rcClose.bottom = rcMin.bottom = rcMax.bottom = rcHelp.bottom = max( rcClose.top, rcClose.top + cyBtn );
rcSmClose.bottom = max( rcSmClose.top, cySmBtn );
// sysmenu icon bottom
rcSys.bottom = rcSys.top + NcGetSystemMetrics(SM_CYSMICON);
// close, min, max left, right (as offsets from container's right boundary)
rcClose.right = -cxEdge; rcClose.left = rcClose.right - cxBtn;
rcMax.right = rcClose.left - cxEdge; rcMax.left = rcMax.right - cxBtn; rcHelp = rcMax;
rcMin.right = rcMax.left - cxEdge; rcMin.left = rcMin.right - cxBtn;
// appicon left, right (as offsets from container's left boundary)
rcSys.left = cxEdge; rcSys.right = rcSys.left + NcGetSystemMetrics(SM_CXSMICON); // toolwindow close, left, right
rcSmClose.right = -cxEdge; rcSmClose.left = rcSmClose.right - cxSmBtn;
const RECT* prcBox = &pncwm->rcS0[NCRC_CAPTION]; int cnLOffset = prcBox->left + pncwm->cnBorders; int cnROffset = prcBox->right - pncwm->cnBorders; int cnCtrOffset = pncwm->cnBorders + prcBox->top + (pncwm->fSmallFrame ? (ncm.iCaptionHeight - RECTHEIGHT(&rcClose))/2 : (ncm.iSmCaptionHeight - RECTHEIGHT(&rcSmClose))/2);
// (2) assign outbound rectangles.
// vertically center w/ respect to classic caption area,
// horizontally position w/ respect to respective container boundary.
// close button
pncwm->rcS0[NCRC_CLOSEBTN] = pncwm->fSmallFrame ? rcSmClose : rcClose; OffsetRect( &pncwm->rcS0[NCRC_CLOSEBTN], cnROffset, cnCtrOffset ); pncwm->rawCloseBtnState = fCanClose ? CBS_NORMAL : CBS_DISABLED;
// (1) min/max/help/appicons not displayed for toolwindows
// (2) min/max btns mutually exclusive w/ context help btn
if( !TESTFLAG(pncwm->dwExStyle, WS_EX_TOOLWINDOW) ) { // min, max
if( TESTFLAG(pncwm->dwStyle, WS_MINIMIZEBOX|WS_MAXIMIZEBOX) ) { pncwm->rcS0[NCRC_MINBTN] = rcMin; OffsetRect( &pncwm->rcS0[NCRC_MINBTN], cnROffset, cnCtrOffset );
pncwm->rcS0[NCRC_MAXBTN] = rcMax; OffsetRect( &pncwm->rcS0[NCRC_MAXBTN], cnROffset, cnCtrOffset ); pncwm->iMaxButtonPart = pncwm->fMaxed ? WP_RESTOREBUTTON : WP_MAXBUTTON; pncwm->iMinButtonPart = pncwm->fMin ? WP_RESTOREBUTTON : WP_MINBUTTON;
pncwm->rawMaxBtnState = TESTFLAG(pncwm->dwStyle, WS_MAXIMIZEBOX) ? CBS_NORMAL : CBS_DISABLED; pncwm->rawMinBtnState = TESTFLAG(pncwm->dwStyle, WS_MINIMIZEBOX) ? CBS_NORMAL : CBS_DISABLED; } // help btn
else if( TESTFLAG(pncwm->dwExStyle, WS_EX_CONTEXTHELP) ) { pncwm->rcS0[NCRC_HELPBTN] = rcHelp; OffsetRect( &pncwm->rcS0[NCRC_HELPBTN], cnROffset, cnCtrOffset ); }
if( hAppIcon ) { // sysmenu icon
pncwm->rcS0[NCRC_SYSBTN] = rcSys; OffsetRect( &pncwm->rcS0[NCRC_SYSBTN], cnLOffset, pncwm->cnBorders + prcBox->top + (ncm.iCaptionHeight - RECTHEIGHT(&rcSys))/2 ); } } } } return fRet; }
//-------------------------------------------------------------------------//
// computes classic nonclient button position
#if 0 // (keeping around for documentation purposes - scotthan)
BOOL _GetClassicNcBtnMetrics( IN OPTIONAL NCWNDMET* pncwm, IN HICON hAppIcon, IN OPTIONAL BOOL fCanClose, BOOL fRefresh ) { static int cxEdge, cyEdge; static int cxBtn, cyBtn, cxSmBtn, cySmBtn; static RECT rcClose, rcMin, rcMax, rcHelp, rcSys; static RECT rcSmClose; static BOOL fInit = FALSE;
if( _fClassicNcBtnMetricsReset || fRefresh ) { cxBtn = NcGetSystemMetrics( SM_CXSIZE ); cyBtn = NcGetSystemMetrics( SM_CYSIZE ); cxSmBtn = NcGetSystemMetrics( SM_CXSMSIZE ); cySmBtn = NcGetSystemMetrics( SM_CYSMSIZE ); cxEdge = NcGetSystemMetrics( SM_CXEDGE ); cyEdge = NcGetSystemMetrics( SM_CYEDGE );
// common top, w/ zero v-offset
rcClose.top = rcMin.top = rcMax.top = rcHelp.top = rcSys.top = rcSmClose.top = 0;
// common bottom, w/ zero v-offset
rcClose.bottom = rcMin.bottom = rcMax.bottom = rcHelp.bottom = rcClose.top + (cyBtn - (cyEdge * 2)); rcSmClose.bottom= (cySmBtn - (cyEdge * 2));
// sysmenu icon bottom
rcSys.bottom = rcSys.top + NcGetSystemMetrics(SM_CYSMICON);
// close, min, max left, right (as offsets from container's right boundary)
rcClose.right = -cxEdge; rcClose.left = rcClose.right - (cxBtn - cxEdge);
rcMax.right = rcClose.left - cxEdge; rcMax.left = rcMax.right - (cxBtn - cxEdge); rcHelp = rcMax;
rcMin.right = rcMax.left; rcMin.left = rcMin.right - (cxBtn - cxEdge);
// appicon left, right (as offsets from container's left boundary)
rcSys.left = cxEdge; rcSys.right = rcSys.left + NcGetSystemMetrics(SM_CXSMICON); // toolwindow close, left, right
rcSmClose.right = -cxEdge; rcSmClose.left = rcSmClose.right - (cxSmBtn - cxEdge); _fClassicNcBtnMetricsReset = FALSE; }
if( !_fClassicNcBtnMetricsReset && pncwm && pncwm->fFrame && TESTFLAG(pncwm->dwStyle, WS_SYSMENU) ) { NONCLIENTMETRICS ncm;
if( NcGetNonclientMetrics( &ncm, FALSE ) ) { const RECT* prcBox = &pncwm->rcS0[NCRC_CAPTION]; int cnLOffset = prcBox->left + pncwm->cnBorders; int cnROffset = prcBox->right - pncwm->cnBorders; int cnCtrOffset = pncwm->cnBorders + prcBox->top + (pncwm->fSmallFrame ? (ncm.iCaptionHeight - RECTHEIGHT(&rcClose))/2 : (ncm.iSmCaptionHeight - RECTHEIGHT(&rcSmClose))/2);
// assign outbound rectangles.
// vertically center w/ respect to classic caption area,
// horizontally position w/ respect to respective container boundary.
// close button
pncwm->rcS0[NCRC_CLOSEBTN] = pncwm->fSmallFrame ? rcSmClose : rcClose; OffsetRect( &pncwm->rcS0[NCRC_CLOSEBTN], cnROffset, cnCtrOffset ); pncwm->rawCloseBtnState = fCanClose ? CBS_NORMAL : CBS_DISABLED;
// (1) min/max/help/appicons not displayed for toolwindows
// (2) min/max btns mutually exclusive w/ context help btn
if( !TESTFLAG(pncwm->dwExStyle, WS_EX_TOOLWINDOW) ) { // min, max
if( TESTFLAG(pncwm->dwStyle, WS_MINIMIZEBOX|WS_MAXIMIZEBOX) ) { pncwm->rcS0[NCRC_MINBTN] = rcMin; OffsetRect( &pncwm->rcS0[NCRC_MINBTN], cnROffset, cnCtrOffset );
pncwm->rcS0[NCRC_MAXBTN] = rcMax; OffsetRect( &pncwm->rcS0[NCRC_MAXBTN], cnROffset, cnCtrOffset ); pncwm->iMaxButtonPart = pncwm->fMaxed ? WP_RESTOREBUTTON : WP_MAXBUTTON; pncwm->iMinButtonPart = pncwm->fMin ? WP_RESTOREBUTTON : WP_MINBUTTON;
pncwm->rawMaxBtnState = TESTFLAG(pncwm->dwStyle, WS_MAXIMIZEBOX) ? CBS_NORMAL : CBS_DISABLED; pncwm->rawMinBtnState = TESTFLAG(pncwm->dwStyle, WS_MINIMIZEBOX) ? CBS_NORMAL : CBS_DISABLED; } // help btn
else if( TESTFLAG(pncwm->dwExStyle, WS_EX_CONTEXTHELP) ) { pncwm->rcS0[NCRC_HELPBTN] = rcHelp; OffsetRect( &pncwm->rcS0[NCRC_HELPBTN], cnROffset, cnCtrOffset ); }
if( hAppIcon ) { // sysmenu icon
pncwm->rcS0[NCRC_SYSBTN] = rcSys; OffsetRect( &pncwm->rcS0[NCRC_SYSBTN], cnLOffset, pncwm->cnBorders + prcBox->top + (ncm.iCaptionHeight - RECTHEIGHT(&rcSys))/2 ); } } return TRUE; } return FALSE; } return fInit; } #endif 0 // (keeping around for documentation purposes - scotthan)
//-------------------------------------------------------------------------//
// CThemeWnd::NcBackgroundHitTest() - hit test the Caption or Frame part
//
WORD CThemeWnd::NcBackgroundHitTest( POINT ptHit, LPCRECT prcWnd, DWORD dwStyle, DWORD dwExStyle, FRAMESTATES fs, const WINDOWPARTS rgiParts[], const WINDOWPARTS rgiTemplates[], const RECT rgrcParts[] ) { WORD hitcode = HTNOWHERE; HRESULT hr = E_FAIL; eFRAMEPARTS iPartHit = (eFRAMEPARTS)-1;
// do a standard rect hit test:
for( int i = 0; i < cFRAMEPARTS; i++ ) { if( _StrictPtInRect(&rgrcParts[i], ptHit) ) { iPartHit = (eFRAMEPARTS)i; break; } } if( iPartHit >= 0 ) { BOOL fResizing = TESTFLAG(dwStyle, WS_THICKFRAME); DWORD dwHTFlags = fResizing ? HTTB_RESIZINGBORDER : HTTB_FIXEDBORDER;
RECT rcHit = rgrcParts[iPartHit];
switch( iPartHit ) { case iCAPTION: // Ensure caption rect and test point are zero-relative to
// the correct origin (if we have a window region,
// this would be window origin, otherwise, it's the part origin.)
if( _hrgnWnd != NULL ) rcHit = *prcWnd; if( fResizing ) dwHTFlags &= ~HTTB_RESIZINGBORDER_BOTTOM; break;
case iFRAMEBOTTOM: if( fResizing ) dwHTFlags &= ~HTTB_RESIZINGBORDER_TOP; break;
case iFRAMELEFT: if( fResizing ) dwHTFlags = HTTB_RESIZINGBORDER_LEFT; break;
case iFRAMERIGHT: if( fResizing ) dwHTFlags = HTTB_RESIZINGBORDER_RIGHT; break; }
ptHit.x -= prcWnd->left; ptHit.y -= prcWnd->top; OffsetRect( &rcHit, -prcWnd->left, -prcWnd->top );
// Here our assumption is that the hit testing for the template
// is "as good" or "better" than the rectangles checking applied
// to the caption part. So we do one or the other. There are
// situations where you would need to do both (if the template
// were outside the window region and you were able to get USER to
// send you NcHitTest messages for it). For those situations
// you would need to call both so that you can distinguish between
// a mouse hit over the caption "client" area vs. over the
// outside-transparent area.
if( VALID_WINDOWPART(rgiTemplates[iPartHit]) ) { hr = HitTestThemeBackground( _hTheme, NULL, rgiTemplates[iPartHit], fs, dwHTFlags | (fResizing ? HTTB_SIZINGTEMPLATE : 0), &rcHit, _rghrgnSizingTemplates[iPartHit], ptHit, &hitcode ); } else { hr = HitTestThemeBackground( _hTheme, NULL, rgiParts[iPartHit], fs, dwHTFlags | (fResizing ? HTTB_SYSTEMSIZINGMARGINS : 0), &rcHit, _hrgnWnd, ptHit, &hitcode ); }
if( SUCCEEDED(hr) ) { if( iCAPTION == iPartHit && (HTCLIENT == hitcode || HTBORDER == hitcode) ) hitcode = HTCAPTION; } }
if ( FAILED(hr) ) { hitcode = HTNOWHERE; }
return hitcode; }
//-------------------------------------------------------------------------//
// CThemeWnd::TrackFrameButton() - track the mouse over the caption buttons,
// pressing/releasing as appropriate. Return back SC_* command to report or 0
// if the mouse was released off of the button.
//
BOOL CThemeWnd::TrackFrameButton( HWND hwnd, INT iHitCode, OUT OPTIONAL WPARAM* puSysCmd, BOOL fHottrack ) { int iStateId, iNewStateId; int iPartId = -1; UINT cmd = 0; MSG msg = {0}; LPRECT prcBtnPaint = NULL; RECT rcBtnTrack; HDC hdc;
if (puSysCmd) { *puSysCmd = 0; }
// map iHitCode to the correct part number
switch (iHitCode) { case HTHELP: cmd = SC_CONTEXTHELP; iPartId = WP_HELPBUTTON; prcBtnPaint = &_ncwm.rcW0[NCRC_HELPBTN]; break;
case HTCLOSE: cmd = SC_CLOSE; iPartId = _ncwm.fSmallFrame ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON; prcBtnPaint = &_ncwm.rcW0[NCRC_CLOSEBTN]; break;
case HTMINBUTTON: cmd = _ncwm.fMin ? SC_RESTORE : SC_MINIMIZE; iPartId = _ncwm.iMinButtonPart; prcBtnPaint = &_ncwm.rcW0[NCRC_MINBTN]; break;
case HTMAXBUTTON: cmd = _ncwm.fMaxed ? SC_RESTORE : SC_MAXIMIZE; iPartId = _ncwm.iMaxButtonPart; prcBtnPaint = &_ncwm.rcW0[NCRC_MAXBTN]; break;
case HTSYSMENU: if (puSysCmd) { *puSysCmd = SC_MOUSEMENU | iHitCode; } return TRUE; }
// If we didn't recognize the hit code there's nothing to track
if (iPartId >= 0 ) { // Get the window DC, in window coords
hdc = _GetNonclientDC(_hwnd, NULL); if ( hdc ) { // Don't paint in the window's content area, clip to the content area
ExcludeClipRect( hdc, _ncwm.rcW0[NCRC_CONTENT].left, _ncwm.rcW0[NCRC_CONTENT].top, _ncwm.rcW0[NCRC_CONTENT].right, _ncwm.rcW0[NCRC_CONTENT].bottom );
// Calculate the tracking rect. We track a larger button rect when maximized
// but paint into the normal sized rect.
rcBtnTrack = *prcBtnPaint; _GetNcBtnHitTestRect( &_ncwm, iHitCode, TRUE, &rcBtnTrack );
// when tracking MDI child window frame buttons, clip to their
// parent rect.
if ( TESTFLAG(GetWindowLong(hwnd, GWL_EXSTYLE), WS_EX_MDICHILD) ) { RECT rcMDIClient;
GetWindowRect(GetParent(hwnd), &rcMDIClient); ScreenToWindowRect(&rcMDIClient); InflateRect(&rcMDIClient, -NcGetSystemMetrics(SM_CXEDGE), -NcGetSystemMetrics(SM_CYEDGE)); IntersectClipRect(hdc, rcMDIClient.left, rcMDIClient.top, rcMDIClient.right, rcMDIClient.bottom); }
if (fHottrack) { // draw the button hot if the mouse is over it
iStateId = (iHitCode == _htHot) ? SBS_HOT : CBS_NORMAL; } else { // draw the button depressed
iStateId = SBS_PUSHED; }
iStateId = MAKE_BTNSTATE(_ncwm.framestate, iStateId); NcDrawThemeBackground(_hTheme, hdc, iPartId, iStateId, prcBtnPaint, 0);
// TODO NotifyWinEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_TITLEBAR, iButton, 0);
if (!fHottrack) { BOOL fTrack, fMouseUp = FALSE; SetCapture(hwnd); // take mouse capture
do // mouse button tracking loop
{ fTrack = FALSE;
// Let's go to sleep, to be awakened only on a mouse message placed in our
// thread's queue.
switch (MsgWaitForMultipleObjectsEx(0, NULL, INFINITE /*why consume CPU processing a timeout when we don't have to?*/, QS_MOUSE, MWMO_INPUTAVAILABLE)) { case WAIT_OBJECT_0: // a mouse message or important system event has been queued
if (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)) {
// PeekMessage returns a point in screen relative coords. Mirror the
// point it this is a RTL window. Translate the point to window coords.
if ( TESTFLAG(_ncwm.dwExStyle, WS_EX_LAYOUTRTL) ) { // mirror the point to hittest correctly
MIRROR_POINT(_ncwm.rcS0[NCRC_WINDOW], msg.pt); } ScreenToWindow( &msg.pt, 1 );
if (msg.message == WM_LBUTTONUP) { ReleaseCapture(); fMouseUp = TRUE; } else if ((msg.message == WM_MOUSEMOVE) && cmd) { iNewStateId = MAKE_BTNSTATE(_ncwm.framestate, PtInRect(&rcBtnTrack, msg.pt) ? SBS_PUSHED : SBS_NORMAL);
if (iStateId != iNewStateId) { iStateId = iNewStateId; NcDrawThemeBackground(_hTheme, hdc, iPartId, iStateId, prcBtnPaint, 0); // TODO NotifyWinEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_TITLEBAR, iButton, 0);
} fTrack = TRUE; } } else { // Check loss of capture. This can happen if we loose activation
// via alt-tab and may not have received a WM_CAPTURECHANGED message
if (GetCapture() != hwnd) { break; } }
// Dequeue CAPTURECHANGED
if (PeekMessage(&msg, NULL, WM_CAPTURECHANGED, WM_CAPTURECHANGED, PM_REMOVE) || fMouseUp) { break; } fTrack = TRUE; // go back to sleep until the next mouse event
break;
default: break; }
} while (fTrack);
// draw button in normal state if it is not in that state already
iNewStateId = MAKE_BTNSTATE(_ncwm.framestate, CBS_NORMAL); if (iStateId != iNewStateId) { NcDrawThemeBackground(_hTheme, hdc, iPartId, iNewStateId, prcBtnPaint, 0); }
// if we did not end up on a button return 0
if( puSysCmd && (*puSysCmd = cmd) != 0 ) { // TODO NotifyWinEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_TITLEBAR, iButton, 0);
// If mouse wasn't released over the button, cancel the command.
if( !(fMouseUp && PtInRect(&rcBtnTrack, msg.pt)) ) *puSysCmd = 0; }
}
// Done with DC now
ReleaseDC(_hwnd, hdc); } }
return TRUE; }
//-------------------------------------------------------------------------//
DWORD GetTextAlignFlags(HTHEME hTheme, IN NCWNDMET* pncwm, BOOL fReverse) { CONTENTALIGNMENT contentAlignment = CA_LEFT; DWORD dwAlignFlags = 0;
//---- compute text alignment ----
GetThemeInt(hTheme, pncwm->rgframeparts[iCAPTION], pncwm->framestate, TMT_CONTENTALIGNMENT, (int *)&contentAlignment);
if (fReverse) { //---- reverse alignment ----
switch(contentAlignment) { default: case CA_LEFT: dwAlignFlags |= DT_RIGHT; break; case CA_CENTER: dwAlignFlags |= DT_CENTER; break; case CA_RIGHT: dwAlignFlags |= DT_LEFT; break; } } else { //---- normal alignment ----
switch(contentAlignment) { default: case CA_LEFT: dwAlignFlags |= DT_LEFT; break; case CA_CENTER: dwAlignFlags |= DT_CENTER; break; case CA_RIGHT: dwAlignFlags |= DT_RIGHT; break; } }
return dwAlignFlags; }
//-------------------------------------------------------------------------//
void _BorderRect( HDC hdc, COLORREF rgb, LPCRECT prc, int cxBorder, int cyBorder ) { COLORREF rgbSave = SetBkColor( hdc, rgb ); RECT rc = *prc;
// bottom border
rc = *prc; rc.top = prc->bottom - cyBorder; ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, NULL );
// right border
rc = *prc; rc.left = prc->right - cxBorder; ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, NULL );
// left border
rc = *prc; rc.right = prc->left + cxBorder; ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, NULL );
// top border
rc = *prc; rc.bottom = prc->top + cyBorder; ExtTextOut( hdc, rc.left, rc.top, ETO_OPAQUE, &rc, NULL, 0, NULL );
SetBkColor( hdc, rgbSave ); }
//-------------------------------------------------------------------------//
void _DrawWindowEdges( HDC hdc, NCWNDMET* pncwm, BOOL fIsFrame ) { // non-frame window edge & border
if( !fIsFrame ) { RECT rcWnd = pncwm->rcW0[NCRC_WINDOW];
int cxBorder = NcGetSystemMetrics(SM_CXBORDER), cyBorder = NcGetSystemMetrics(SM_CYBORDER);
// static, window edge
if( TESTFLAG(pncwm->dwExStyle, WS_EX_WINDOWEDGE) ) { RECT rcClip = rcWnd;
InflateRect( &rcClip, -pncwm->cnBorders, -pncwm->cnBorders ); ExcludeClipRect( hdc, rcClip.left, rcClip.top, rcClip.right, rcClip.bottom ); DrawEdge( hdc, &rcWnd, EDGE_RAISED, BF_RECT | BF_ADJUST | BF_MIDDLE); SelectClipRgn( hdc, NULL ); } else if( TESTFLAG(pncwm->dwExStyle, WS_EX_STATICEDGE) ) { DrawEdge( hdc, &rcWnd, BDR_SUNKENOUTER, BF_RECT | BF_ADJUST ); } // Normal border
else if( TESTFLAG(pncwm->dwStyle, WS_BORDER) ) { _BorderRect( hdc, GetSysColor( COLOR_WINDOWFRAME), &rcWnd, cxBorder, cyBorder ); } }
// client edge
if( TESTFLAG(pncwm->dwExStyle, WS_EX_CLIENTEDGE) ) { #ifdef _TEST_CLIENTEDGE_
HBRUSH hbr = CreateSolidBrush( RGB(255,0,255) ); FillRect(hdc, &ncwm.rcW0[NCRC_CLIENTEDGE], hbr); DeleteObject(hbr);
#else _TEST_CLIENTEDGE_
DrawEdge( hdc, &pncwm->rcW0[NCRC_CLIENTEDGE], EDGE_SUNKEN, BF_RECT | BF_ADJUST);
#endif _TEST_CLIENTEDGE_
} }
//-------------------------------------------------------------------------//
void CThemeWnd::NcPaintCaption( IN HDC hdcOut, IN NCWNDMET* pncwm, IN BOOL fBuffered, IN DWORD dwCaptionFlags, // draw caption flags (DC_xxx, winuser.h)
IN DTBGOPTS* pdtbopts ) { ASSERT(hdcOut); ASSERT(pncwm); ASSERT(pncwm->fFrame); ASSERT(HAS_CAPTIONBAR(pncwm->dwStyle));
DWORD dwOldAlign = 0;
// caption text implies caption background
if( TESTFLAG( dwCaptionFlags, DC_TEXT|DC_ICON ) || 0 == dwCaptionFlags ) { dwCaptionFlags = DC_ENTIRECAPTION; }
if( dwCaptionFlags != DC_ENTIRECAPTION #if defined(DEBUG) && defined(DEBUG_NCPAINT)
|| TESTFLAG( _NcTraceFlags, NCTF_NCPAINT ) #endif DEBUG
) { fBuffered = FALSE; }
// create caption double buffer
HBITMAP hbmBuf = fBuffered ? CreateCompatibleBitmap(hdcOut, RECTWIDTH(&pncwm->rcW0[NCRC_CAPTION]), RECTHEIGHT(&pncwm->rcW0[NCRC_CAPTION])) : NULL; if( !fBuffered || hbmBuf ) { HDC hdc = fBuffered ? CreateCompatibleDC(hdcOut) : hdcOut; if( hdc ) { //--- DO NOT EXIT FROM WITHIN THIS CONDITIONAL ---//
EnterNcThemePaint();
HBITMAP hbm0 = fBuffered ? (HBITMAP)SelectObject(hdc, hbmBuf) : NULL;
if( TESTFLAG( dwCaptionFlags, DC_BACKGROUND ) ) { // Draw caption background
RECT rcBkgnd = pncwm->rcW0[NCRC_CAPTION]; if( pncwm->fFullMaxed ) { rcBkgnd.top += pncwm->cnBorders; rcBkgnd.left += pncwm->cnBorders; rcBkgnd.right -= pncwm->cnBorders; } NcDrawThemeBackgroundEx( _hTheme, hdc, pncwm->rgframeparts[iCAPTION], pncwm->framestate, &rcBkgnd, pdtbopts ); }
if( TESTFLAG( dwCaptionFlags, DC_BUTTONS ) ) { // Draw standard caption buttons
if (!IsRectEmpty(&pncwm->rcW0[NCRC_CLOSEBTN])) { NcDrawThemeBackground( _hTheme, hdc, pncwm->fSmallFrame ? WP_SMALLCLOSEBUTTON : WP_CLOSEBUTTON, MAKE_BTNSTATE(pncwm->framestate, pncwm->rawCloseBtnState), &pncwm->rcW0[NCRC_CLOSEBTN], 0); }
if (!IsRectEmpty(&pncwm->rcW0[NCRC_MAXBTN])) { NcDrawThemeBackground(_hTheme, hdc, pncwm->iMaxButtonPart, MAKE_BTNSTATE(pncwm->framestate, pncwm->rawMaxBtnState), &pncwm->rcW0[NCRC_MAXBTN], 0); }
if (!IsRectEmpty(&pncwm->rcW0[NCRC_MINBTN])) { NcDrawThemeBackground( _hTheme, hdc, pncwm->iMinButtonPart, MAKE_BTNSTATE(pncwm->framestate, pncwm->rawMinBtnState), &pncwm->rcW0[NCRC_MINBTN], 0); }
if (!IsRectEmpty(&pncwm->rcW0[NCRC_HELPBTN])) NcDrawThemeBackground(_hTheme, hdc, WP_HELPBUTTON, MAKE_BTNSTATE(pncwm->framestate, CBS_NORMAL), &pncwm->rcW0[NCRC_HELPBTN], 0); }
// Draw sysmenu icon
if( TESTFLAG( dwCaptionFlags, DC_ICON ) ) { if (!IsRectEmpty(&pncwm->rcW0[NCRC_SYSBTN]) && _hAppIcon) { #define MAX_APPICON_RETRIES 1
int cRetries = 0;
DWORD dwLayout = GetLayout(hdc); if( GDI_ERROR != dwLayout && TESTFLAG(dwLayout, LAYOUT_RTL) ) { SetLayout(hdc, dwLayout|LAYOUT_BITMAPORIENTATIONPRESERVED); }
do { // note: we don't draw sysmenu icon mirrored
if( DrawIconEx(hdc, pncwm->rcW0[NCRC_SYSBTN].left, pncwm->rcW0[NCRC_SYSBTN].top, _hAppIcon, RECTWIDTH(&pncwm->rcW0[NCRC_SYSBTN]), RECTHEIGHT(&pncwm->rcW0[NCRC_SYSBTN]), 0, NULL, DI_NORMAL)) { break; // success; done
}
// failure; try recycling the handle
if( _hAppIcon && GetLastError() == ERROR_INVALID_CURSOR_HANDLE ) { _hAppIcon = NULL; AcquireFrameIcon( pncwm->dwStyle, pncwm->dwExStyle, FALSE );
if( (++cRetries) > MAX_APPICON_RETRIES ) { _hAppIcon = NULL; // failed to retrieve a new icon handle; bail for good.
} }
} while( _hAppIcon && cRetries <= MAX_APPICON_RETRIES );
if( GDI_ERROR != dwLayout ) { SetLayout(hdc, dwLayout); }
} }
if( TESTFLAG( dwCaptionFlags, DC_TEXT ) ) { // Draw caption text
HFONT hf0 = NULL; DWORD dwDTFlags = DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS; BOOL fSelFont = FALSE; LPWSTR pszText = _AllocWindowText(_hwnd);
if( pszText && *pszText ) { // Compute frame text rect
if( pncwm->hfCaption ) { hf0 = (HFONT)SelectObject( hdc, pncwm->hfCaption ); fSelFont = TRUE; }
//---- compute text alignment ----
BOOL fReverse = TESTFLAG(_ncwm.dwExStyle, WS_EX_RIGHT);
dwDTFlags |= GetTextAlignFlags(_hTheme, pncwm, fReverse); }
//---- adjust text align for WS_EX_RTLREADING ----
if (TESTFLAG(_ncwm.dwExStyle, WS_EX_RTLREADING)) dwOldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
if( pszText && *pszText ) { //---- set options for DrawThemeText() ----
DTTOPTS DttOpts = {sizeof(DttOpts)}; DttOpts.dwFlags = DTT_TEXTCOLOR; DttOpts.crText = pncwm->rgbCaption;
Log(LOG_RFBUG, L"Drawing Caption Text: left=%d, state=%d, text=%s", pncwm->rcW0[NCRC_CAPTIONTEXT].left, pncwm->framestate, pszText);
//---- draw the caption text ----
DrawThemeTextEx(_hTheme, hdc, pncwm->rgframeparts[iCAPTION], pncwm->framestate, pszText, -1, dwDTFlags, &pncwm->rcW0[NCRC_CAPTIONTEXT], &DttOpts); }
//---- free the text, if temp. allocated ----
SAFE_DELETE_ARRAY(pszText)
//---- draw the "Comments?" text ----
SetBkMode( hdc, TRANSPARENT ); SetTextColor( hdc, pncwm->rgbCaption ); DrawLameButton(hdc, pncwm);
//---- restore the text align ----
if (TESTFLAG(_ncwm.dwExStyle, WS_EX_RTLREADING)) SetTextAlign(hdc, dwOldAlign); if( fSelFont ) { SelectObject(hdc, hf0); } } if( hdc != hdcOut ) { // Slap the bits on the output DC.
BitBlt( hdcOut, pncwm->rcW0[NCRC_CAPTION].left, pncwm->rcW0[NCRC_CAPTION].top, WIDTH(pncwm->rcW0[NCRC_CAPTION]), HEIGHT(pncwm->rcW0[NCRC_CAPTION]), hdc, 0, 0, SRCCOPY ); SelectObject(hdc, hbm0); DeleteDC(hdc); }
LeaveNcThemePaint(); } DeleteObject( hbmBuf ); }
if( IsWindowVisible(_hwnd) ) { SetRenderedNcPart(RNCF_CAPTION); } }
//-------------------------------------------------------------------------//
// CThemeWnd::NcPaint() - NC painting worker
//
void CThemeWnd::NcPaint( IN OPTIONAL HDC hdcIn, IN ULONG dwFlags, IN OPTIONAL HRGN hrgnUpdate, IN OPTIONAL NCPAINTOVERIDE* pncpo) { NCTHEMEMET nctm; NCWNDMET* pncwm = NULL; HDC hdc = NULL;
if( _cLockRedraw > 0 ) return;
// Compute all metrics before painting:
if (pncpo) // preview override
{ ASSERT(hdcIn); hdc = hdcIn; pncwm = pncpo->pncwm; nctm = pncpo->nctm; } else // live window
{ if( !GetNcWindowMetrics( NULL, &pncwm, &nctm, NCWMF_RECOMPUTE ) ) return;
// Ensure status bits reflect caller's intention for frame state
if( dwFlags != NCPF_DEFAULT ) { _ComputeNcWindowStatus( _hwnd, TESTFLAG(dwFlags, NCPF_ACTIVEFRAME) ? WS_ACTIVECAPTION : 0, pncwm ); }
hdc = hdcIn ? hdcIn : _GetNonclientDC( _hwnd, hrgnUpdate );
if (! hdc) { //---- don't assert here since stress (out of memory) could cause a legit failure ----
Log(LOG_ALWAYS, L"call to GetDCEx() for nonclient painting failed"); } }
if( hdc != NULL ) { //--- DO NOT EXIT FROM WITHIN THIS CONDITIONAL ---//
BEGIN_DEBUG_NCPAINT(); EnterNcThemePaint();
// Clip to content rect (alleviates flicker in menubar and scrollbars as we paint background)
RECT rcClip; rcClip = pncwm->rcW0[NCRC_CONTENT]; if( TESTFLAG(pncwm->dwExStyle, WS_EX_LAYOUTRTL) ) { // mirror the clip rect relative to the window rect
// and apply that as the clipping region for the dc
MIRROR_RECT(pncwm->rcW0[NCRC_WINDOW], rcClip); }
ExcludeClipRect( hdc, rcClip.left, rcClip.top, rcClip.right, rcClip.bottom );
if( pncwm->fFrame ) { //---- DrawThemeBackgroundEx() options ----
DTBGOPTS dtbopts = {sizeof(dtbopts)}; DTBGOPTS *pdtbopts = NULL;
// if not drawing preview, set "draw solid" option
if(!pncpo) { // Because of the interleaving of NCPAINT and SetWindowRgn, drawing solid results
// in some flicker and transparency bleed. Commenting this out for now [scotthan]
//dtbopts.dwFlags |= DTBG_DRAWSOLID;
pdtbopts = &dtbopts; }
// Frame Background
if( pncwm->fMin ) { NcDrawThemeBackgroundEx( _hTheme, hdc, WP_MINCAPTION, pncwm->framestate, &pncwm->rcW0[NCRC_CAPTION], pdtbopts ) ; } else if( !pncwm->fFullMaxed ) { NcDrawThemeBackgroundEx( _hTheme, hdc, pncwm->rgframeparts[iFRAMELEFT], pncwm->framestate, &pncwm->rcW0[NCRC_FRAMELEFT], pdtbopts ); NcDrawThemeBackgroundEx( _hTheme, hdc, pncwm->rgframeparts[iFRAMERIGHT], pncwm->framestate, &pncwm->rcW0[NCRC_FRAMERIGHT], pdtbopts ); NcDrawThemeBackgroundEx( _hTheme, hdc, pncwm->rgframeparts[iFRAMEBOTTOM], pncwm->framestate, &pncwm->rcW0[NCRC_FRAMEBOTTOM], pdtbopts ); }
SetRenderedNcPart(RNCF_FRAME);
// Caption
NcPaintCaption( hdc, pncwm, !(pncwm->fMin || pncwm->fFullMaxed || pncpo), DC_ENTIRECAPTION, pdtbopts ); }
// Clip to client rect
SelectClipRgn( hdc, NULL );
// Menubar
if( !(pncwm->fMin || TESTFLAG(pncwm->dwStyle, WS_CHILD)) && !IsRectEmpty(&pncwm->rcW0[NCRC_MENUBAR]) ) { RECT rcMenuBar = pncwm->rcW0[NCRC_MENUBAR]; BOOL fClip = RECTHEIGHT(&rcMenuBar) < pncwm->cyMenu; if( fClip ) { IntersectClipRect( hdc, rcMenuBar.left, rcMenuBar.top, rcMenuBar.right, rcMenuBar.bottom ); }
PaintMenuBar( _hwnd, hdc, pncwm->cnMenuOffsetLeft, pncwm->cnMenuOffsetRight, pncwm->cnMenuOffsetTop, TESTFLAG(pncwm->framestate, FS_ACTIVE) ? PMB_ACTIVE : 0 ); // deal with unpainted menubar pixels:
if( nctm.dyMenuBar > 0 && RECTHEIGHT(&pncwm->rcW0[NCRC_MENUBAR]) >= pncwm->cyMenu ) { rcMenuBar.top = rcMenuBar.bottom - nctm.dyMenuBar; COLORREF rgbBk = SetBkColor( hdc, GetSysColor(COLOR_MENU) ); ExtTextOut(hdc, rcMenuBar.left, rcMenuBar.top, ETO_OPAQUE, &rcMenuBar, NULL, 0, NULL ); SetBkColor( hdc, rgbBk ); }
if( fClip ) SelectClipRgn( hdc, NULL ); }
// Scrollbars
if( !pncwm->fMin ) { // Draw static, window, client edges.
_DrawWindowEdges( hdc, pncwm, pncwm->fFrame );
RECT rcVScroll = pncwm->rcW0[NCRC_VSCROLL]; if ( TESTFLAG(pncwm->dwExStyle, WS_EX_LAYOUTRTL) ) { MIRROR_RECT(pncwm->rcW0[NCRC_WINDOW], rcVScroll); }
if( TESTFLAG(pncwm->dwStyle, WS_VSCROLL) && ( HasRenderedNcPart(RNCF_SCROLLBAR) || RectVisible(hdc, &rcVScroll)) ) { if( TESTFLAG(pncwm->dwStyle, WS_HSCROLL) ) {
// Draw sizebox.
RECT rcSizeBox = pncwm->rcW0[NCRC_SIZEBOX];
if ( TESTFLAG(pncwm->dwExStyle, WS_EX_LAYOUTRTL) ) { MIRROR_RECT(pncwm->rcW0[NCRC_WINDOW], rcSizeBox); }
DrawSizeBox( _hwnd, hdc, rcSizeBox.left, rcSizeBox.top ); }
DrawScrollBar( _hwnd, hdc, pncpo ? &pncwm->rcW0[NCRC_VSCROLL]: NULL, TRUE /*vertical*/ ); SetRenderedNcPart( RNCF_SCROLLBAR ); }
if( TESTFLAG(pncwm->dwStyle, WS_HSCROLL) && ( HasRenderedNcPart(RNCF_SCROLLBAR) || RectVisible(hdc, &pncwm->rcW0[NCRC_HSCROLL])) ) { DrawScrollBar( _hwnd, hdc, pncpo ? &pncwm->rcW0[NCRC_HSCROLL] : NULL, FALSE /*vertical*/ ); SetRenderedNcPart( RNCF_SCROLLBAR ); } }
if (pncpo || hdcIn) { SelectClipRgn( hdc, NULL ); } else { ReleaseDC( _hwnd, hdc ); }
LeaveNcThemePaint(); END_DEBUG_NCPAINT(); } }
//-------------------------------------------------------------------------//
// WM_STYLECHANGED themewnd instance handler
void CThemeWnd::StyleChanged( UINT iGWL, DWORD dwOld, DWORD dwNew ) { DWORD dwStyleOld, dwStyleNew, dwExStyleOld, dwExStyleNew;
switch( iGWL ) { case GWL_STYLE: dwStyleOld = dwOld; dwStyleNew = dwNew; dwExStyleOld = dwExStyleNew = GetWindowLong(_hwnd, GWL_EXSTYLE); break;
case GWL_EXSTYLE: dwExStyleOld = dwOld; dwExStyleNew = dwNew; dwStyleOld = dwStyleNew = GetWindowLong(_hwnd, GWL_STYLE); break;
default: return; }
DWORD fClassFlagsOld = CThemeWnd::EvaluateStyle( dwStyleOld, dwExStyleOld); DWORD fClassFlagsNew = CThemeWnd::EvaluateStyle( dwStyleNew, dwExStyleNew);
// Update theme class flags.
// Always keep the scrollbar class flag if the window had it initially. User
// flips scroll styles on and off without corresponding style change notification.
_fClassFlags = fClassFlagsNew | (_fClassFlags & TWCF_SCROLLBARS); _fFrameThemed = TESTFLAG( _fClassFlags, TWCF_FRAME|TWCF_TOOLFRAME );
// Are we losing the frame?
if( TESTFLAG( fClassFlagsOld, TWCF_FRAME|TWCF_TOOLFRAME ) && !TESTFLAG( fClassFlagsNew, TWCF_FRAME|TWCF_TOOLFRAME ) ) { ThemeMDIMenuButtons(FALSE, FALSE);
if( AssignedFrameRgn() ) { AssignFrameRgn(FALSE /*strip off frame rgn*/, FTF_REDRAW); } } // Are we gaining a frame?
else if( TESTFLAG( fClassFlagsNew, TWCF_FRAME|TWCF_TOOLFRAME ) && !TESTFLAG( fClassFlagsOld, TWCF_FRAME|TWCF_TOOLFRAME ) ) { SetFrameTheme(0, NULL); }
// Freshen window metrics
GetNcWindowMetrics( NULL, NULL, NULL, NCWMF_RECOMPUTE ); }
//-------------------------------------------------------------------------//
// ThemeDefWindowProc message handlers
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// WM_THEMECHANGED post-wndproc msg handler
LRESULT CALLBACK OnOwpPostThemeChanged( CThemeWnd* pwnd, THEME_MSG *ptm ) { if (IS_THEME_CHANGE_TARGET(ptm->lParam)) { //---- avoid redundant retheming (except for SetWindowTheme() calls)
if ((HTHEME(*pwnd) == _nctmCurrent.hTheme) && (! (ptm->lParam & WTC_CUSTOMTHEME))) { Log(LOG_NCATTACH, L"OnOwpPostThemeChanged, just kicking the frame");
//---- window got correct theme thru _XXXWindowProc() from sethook ----
//---- we just need to redraw the frame for all to be right ----
if (pwnd->IsFrameThemed()) { //---- attach the region to the window now ----
pwnd->SetFrameTheme(FTF_REDRAW, NULL); } } else { Log(LOG_NCATTACH, L"OnOwpPostThemeChanged, calling Full ::ChangeTheme()");
//---- its a real, app/system theme change ----
pwnd->ChangeTheme( ptm ); } }
MsgHandled( ptm ); return 1L; }
//-------------------------------------------------------------------------//
void CThemeWnd::ChangeTheme( THEME_MSG* ptm ) { if( _hTheme ) // hwnd attached for previous theme
{ // do a lightweight detach from current theme
_DetachInstance( HMD_CHANGETHEME ); }
if( IsAppThemed() ) // new theme is active
{ // retrieve window client rect, style bits.
WINDOWINFO wi; wi.cbSize = sizeof(wi); GetWindowInfo( ptm->hwnd, &wi ); ULONG ulTargetFlags = EvaluateStyle( wi.dwStyle, wi.dwExStyle ); // If the window is themable
if( TESTFLAG(ulTargetFlags, TWCF_NCTHEMETARGETMASK) ) { // Open the new theme
HTHEME hTheme = ::OpenNcThemeData( ptm->hwnd, L"Window" );
if( hTheme ) { // do a lightweight attach
if( _AttachInstance( ptm->hwnd, hTheme, ulTargetFlags, NULL ) ) { // reattach scrollbars
if( TESTFLAG( ulTargetFlags, TWCF_SCROLLBARS ) ) { AttachScrollBars(ptm->hwnd); }
if (IsFrameThemed()) { //---- attach the region to the window now ----
SetFrameTheme(FTF_REDRAW, NULL); } } else { CloseThemeData( hTheme ); } } } }
if (! _hTheme) // if an hwnd is no longer attached
{ // Left without a theme handle: This means either we failed to open a new theme handle or
// failed to evaulate as a target, no new theme, etc.
RemoveWindowProperties(ptm->hwnd, FALSE);
//---- release our CThemeWnd obj so it doesn't leak (addref-protected by caller) ----
Release(); }
} //-------------------------------------------------------------------------//
BOOL IsPropertySheetChild(HWND hDlg) { while(hDlg) { ULONG ulFlags = HandleToUlong(GetProp(hDlg, MAKEINTATOM(GetThemeAtom(THEMEATOM_DLGTEXTURING))));
if( ETDT_ENABLETAB == (ulFlags & ETDT_ENABLETAB) /* all bits in this mask are required */ ) { return TRUE; } hDlg = GetAncestor(hDlg, GA_PARENT); }
return FALSE; } //---------------------------------------------------------------------------
void PrintClientNotHandled(HWND hwnd) { ATOM aIsPrinting = GetThemeAtom(THEMEATOM_PRINTING); DWORD dw = PtrToUlong(GetProp(hwnd, (PCTSTR)aIsPrinting)); if (dw == PRINTING_ASKING) SetProp(hwnd, (PCTSTR)aIsPrinting, (HANDLE)PRINTING_WINDOWDIDNOTHANDLE); }
//---------------------------------------------------------------------------
HBRUSH GetDialogColor(HWND hwnd, NCTHEMEMET &nctm) { HBRUSH hbr = NULL;
// if this is a PROPSHEET child or the app called
// EnableThemeDialogTexture() on this hwnd, we'll use the tab background.
if (IsPropertySheetChild(hwnd)) { hbr = nctm.hbrTabDialog; }
if( NULL == hbr ) { hbr = GetSysColorBrush(COLOR_3DFACE); }
return hbr; } //---------------------------------------------------------------------------
LRESULT CALLBACK OnDdpPrint(CThemeWnd* pwnd, THEME_MSG* ptm) { LRESULT lRet = 0L; if (!ptm->lRet) { if (pwnd->HasProcessedEraseBk()) { RECT rc; HDC hdc = (HDC)ptm->wParam; NCTHEMEMET nctm; if( GetCurrentNcThemeMetrics( &nctm )) { HBRUSH hbr = GetDialogColor(*pwnd, nctm); if (hbr) { POINT pt;
if (GetClipBox(hdc, &rc) == NULLREGION) GetClientRect(*pwnd, &rc);
SetBrushOrgEx(hdc, -rc.left, -rc.top, &pt); FillRect(hdc, &rc, hbr); SetBrushOrgEx(hdc, pt.x, pt.y, NULL);
lRet = (LRESULT)1; MsgHandled( ptm ); } } } else { PrintClientNotHandled(*pwnd); } }
return lRet; }
//---------------------------------------------------------------------------
LRESULT CALLBACK OnDdpCtlColor(CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L; if (!ptm->lRet && pwnd->HasProcessedEraseBk()) { NCTHEMEMET nctm; if( GetCurrentNcThemeMetrics( &nctm )) { HBRUSH hbr = GetDialogColor(*pwnd, nctm); if (hbr) { RECT rc; HDC hdc = (HDC)ptm->wParam;
GetWindowRect(((HWND)ptm->lParam), &rc); MapWindowPoints(NULL, *pwnd, (POINT*)&rc, 2); SetBkMode(hdc, TRANSPARENT); SetBrushOrgEx(hdc, -rc.left, -rc.top, NULL);
// the hdc's default background color needs to be set
// for for those controls that insist on using OPAQUE
SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
lRet = (LRESULT)hbr; MsgHandled( ptm ); } } } return lRet; }
//-------------------------------------------------------------------------//
// WM_CTLCOLORxxx defwindowproc override handler
LRESULT CALLBACK OnDdpPostCtlColor( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L; if (!ptm->lRet) { // This is sent to the parent in the case of WM_CTLCOLORMSGBOX, but to the
// dialog itself in the case of a WM_CTLCOLORDLG. This gets both.
CThemeWnd* pwndDlg = CThemeWnd::FromHwnd((HWND)ptm->lParam);
// WM_CTLCOLORMSGBOX is sent for Both the dialog AND the static
// control inside. So we need to sniff: Are we talking to a dialog or a
// control. the pwnd is associated with the dialog, but not the control
if (pwndDlg && VALID_THEMEWND(pwndDlg)) { if (IsPropertySheetChild(*pwnd)) { NCTHEMEMET nctm; if( GetCurrentNcThemeMetrics( &nctm )) { HBRUSH hbr = GetDialogColor(*pwndDlg, nctm); if (hbr) { lRet = (LRESULT) hbr; pwndDlg->ProcessedEraseBk(TRUE); MsgHandled(ptm); } } } } else { // If we're talking to a control, forward to the control handler
// because we have to offset the brush
lRet = OnDdpCtlColor(pwnd, ptm );
} } return lRet; }
//-------------------------------------------------------------------------//
LRESULT CALLBACK OnDwpPrintClient( CThemeWnd* pwnd, THEME_MSG *ptm ) { PrintClientNotHandled(*pwnd);
return 0; }
//---- Non-Client ----
//-------------------------------------------------------------------------//
// WM_NCPAINT pre-wndmproc msg handler
LRESULT CALLBACK OnOwpPreNcPaint( CThemeWnd* pwnd, THEME_MSG *ptm ) { NcPaintWindow_Add(*pwnd);
if( !pwnd->InNcPaint() ) { pwnd->ClearRenderedNcPart(RNCF_ALL); } pwnd->EnterNcPaint(); return 0L; }
//-------------------------------------------------------------------------//
// WM_NCPAINT DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcPaint( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L; if( !pwnd->IsNcThemed() ) return lRet;
if( IsWindowVisible(*pwnd) ) { pwnd->NcPaint( NULL, NCPF_DEFAULT, 1 == ptm->wParam ? NULL : (HRGN)ptm->wParam, NULL ); }
MsgHandled( ptm ); return lRet; }
//-------------------------------------------------------------------------//
// WM_NCPAINT post-wndmproc msg handler
LRESULT CALLBACK OnOwpPostNcPaint( CThemeWnd* pwnd, THEME_MSG *ptm ) { pwnd->LeaveNcPaint(); NcPaintWindow_Remove(); return 0L; }
//-------------------------------------------------------------------------//
// WM_PRINT DefWindowProc msg handler
LRESULT CALLBACK OnDwpPrint( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = DoMsgDefault(ptm); if( !pwnd->IsNcThemed() ) return lRet;
if( ptm->lParam & PRF_NONCLIENT ) { int iLayoutSave = GDI_ERROR; HDC hdc = (HDC)ptm->wParam;
if (TESTFLAG(GetWindowLong(*pwnd, GWL_EXSTYLE), WS_EX_LAYOUTRTL)) { // AnimateWindow sends WM_PRINT with an unmirrored memory hdc
iLayoutSave = SetLayout(hdc, LAYOUT_RTL); }
pwnd->NcPaint( (HDC)ptm->wParam, NCPF_DEFAULT, NULL, NULL );
if (iLayoutSave != GDI_ERROR) { SetLayout(hdc, iLayoutSave); } }
return lRet; }
//-------------------------------------------------------------------------//
// WM_NCUAHDRAWCAPTION DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcThemeDrawCaption( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L; if( !pwnd->IsNcThemed() || !pwnd->HasRenderedNcPart(RNCF_CAPTION) ) return lRet;
NCWNDMET* pncwm; if( pwnd->GetNcWindowMetrics( NULL, &pncwm, NULL, NCWMF_RECOMPUTE ) ) { HDC hdc = _GetNonclientDC( *pwnd, NULL ); if( hdc ) { DTBGOPTS dtbo; dtbo.dwSize = sizeof(dtbo); dtbo.dwFlags = DTBG_DRAWSOLID; pwnd->NcPaintCaption( hdc, pncwm, TRUE, (DWORD)ptm->wParam, &dtbo ); ReleaseDC( *pwnd, hdc ); MsgHandled( ptm ); } }
return lRet; }
//-------------------------------------------------------------------------//
// WM_NCUAHDRAWFRAME DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcThemeDrawFrame( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L; if( !pwnd->IsNcThemed() || !pwnd->HasRenderedNcPart(RNCF_FRAME) ) return lRet;
pwnd->NcPaint( (HDC)ptm->wParam, ptm->lParam & DF_ACTIVE ? NCPF_ACTIVEFRAME : NCPF_INACTIVEFRAME, NULL, NULL );
MsgHandled( ptm ); return lRet; }
//-------------------------------------------------------------------------//
CMdiBtns* CThemeWnd::LoadMdiBtns( IN OPTIONAL HDC hdc, IN OPTIONAL UINT uSysCmd ) { if( NULL == _pMdiBtns && NULL == (_pMdiBtns = new CMdiBtns) ) { return NULL; }
return _pMdiBtns->Load( _hTheme, hdc, uSysCmd ) ? _pMdiBtns : NULL; }
//-------------------------------------------------------------------------//
void CThemeWnd::UnloadMdiBtns( IN OPTIONAL UINT uSysCmd ) { SAFE_DELETE(_pMdiBtns); }
//-------------------------------------------------------------------------//
// WM_MEASUREITEM pre-wndproc msg handler
LRESULT CALLBACK OnOwpPreMeasureItem( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( pwnd->IsNcThemed() && IsWindow(pwnd->GetMDIClient()) ) { MEASUREITEMSTRUCT* pmis = (MEASUREITEMSTRUCT*)ptm->lParam;
CMdiBtns* pBtns = pwnd->LoadMdiBtns( NULL, pmis->itemID ); if( pBtns ) { if( pBtns->Measure( *pwnd, pmis ) ) { MsgHandled(ptm); return TRUE; } } } return FALSE; }
//-------------------------------------------------------------------------//
// WM_DRAWITEM pre-wndproc msg handler
LRESULT CALLBACK OnOwpPreDrawItem( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( pwnd->IsNcThemed() && IsWindow(pwnd->GetMDIClient()) ) { DRAWITEMSTRUCT* pdis = (DRAWITEMSTRUCT*)ptm->lParam;
CMdiBtns* pBtns = pwnd->LoadMdiBtns( NULL, pdis->itemID ); if( pBtns ) { if( pBtns->Draw( *pwnd, pdis ) ) { MsgHandled(ptm); return TRUE; } } } return FALSE; }
//-------------------------------------------------------------------------//
// WM_MENUCHAR pre-wndproc msg handler
LRESULT CALLBACK OnOwpPreMenuChar( CThemeWnd* pwnd, THEME_MSG *ptm ) { // Route MENUCHAR messages relating to themed MDI buttons to
// DefWindowProc (some apps assume all owner-drawn menuitems
// belong to themselves).
HWND hwndMDIClient = pwnd->GetMDIClient();
if( pwnd->IsNcThemed() && IsWindow(hwndMDIClient)) { if( LOWORD(ptm->wParam) == TEXT('-') ) { BOOL fMaxedChild; HWND hwndActive = _MDIGetActive(hwndMDIClient, &fMaxedChild ); if( hwndActive && fMaxedChild ) { MsgHandled(ptm); return DefFrameProc(ptm->hwnd, hwndMDIClient, ptm->uMsg, ptm->wParam, ptm->lParam); } } } return 0L; }
//-------------------------------------------------------------------------//
// WM_NCHITTEST DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcHitTest( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( !pwnd->IsNcThemed() ) return DoMsgDefault( ptm );
NCTHEMEMET nctm; NCWNDMET* pncwm; POINT pt; MAKEPOINT( pt, ptm->lParam ); MsgHandled( ptm );
if( pwnd->GetNcWindowMetrics( NULL, &pncwm, &nctm, 0 ) ) { if( _StrictPtInRect( &pncwm->rcS0[NCRC_CLIENT], pt ) ) return HTCLIENT;
if( _StrictPtInRect( &pncwm->rcS0[NCRC_HSCROLL], pt ) ) return HTHSCROLL;
if( _StrictPtInRect( &pncwm->rcS0[NCRC_SIZEBOX], pt ) ) { if (SizeBoxHwnd(*pwnd) && !TESTFLAG(pncwm->dwExStyle, WS_EX_LEFTSCROLLBAR))
{ return TESTFLAG(pncwm->dwExStyle, WS_EX_LAYOUTRTL) ? HTBOTTOMLEFT : HTBOTTOMRIGHT; } else { return HTGROWBOX; } }
if ( TESTFLAG(pncwm->dwExStyle, WS_EX_LAYOUTRTL) ) { // mirror the point to hittest correctly
MIRROR_POINT(pncwm->rcS0[NCRC_WINDOW], pt); }
if( _StrictPtInRect( &pncwm->rcS0[NCRC_VSCROLL], pt ) ) return HTVSCROLL;
if( _StrictPtInRect( &pncwm->rcS0[NCRC_MENUBAR], pt ) ) return HTMENU;
if( pncwm->fFrame ) { RECT rcButton; // ---- close button ----
_GetNcBtnHitTestRect( pncwm, HTCLOSE, FALSE, &rcButton );
if ( _StrictPtInRect( &rcButton, pt ) ) { return HTCLOSE; }
// ---- minimize button ----
_GetNcBtnHitTestRect( pncwm, HTMINBUTTON, FALSE, &rcButton );
if ( _StrictPtInRect( &rcButton, pt ) ) { return HTMINBUTTON; }
// ---- maximize button ----
_GetNcBtnHitTestRect( pncwm, HTMAXBUTTON, FALSE, &rcButton );
if ( _StrictPtInRect( &rcButton, pt ) ) { return HTMAXBUTTON; }
// ---- sys menu ----
_GetNcBtnHitTestRect( pncwm, HTSYSMENU, FALSE, &rcButton );
if ( _StrictPtInRect( &rcButton, pt ) ) { return HTSYSMENU; }
// ---- help button ----
_GetNcBtnHitTestRect( pncwm, HTHELP, FALSE, &rcButton );
if ( _StrictPtInRect( &rcButton, pt ) ) { return HTHELP; } #ifdef LAME_BUTTON
if ( TESTFLAG(pncwm->dwExStyle, WS_EX_LAMEBUTTON) ) { if ( _StrictPtInRect( &pncwm->rcS0[NCRC_LAMEBTN], pt ) ) return HTLAMEBUTTON; } #endif // LAME_BUTTON
// don't need a mirrored point for the remaining hittests
MAKEPOINT( pt, ptm->lParam );
if( !_StrictPtInRect( &pncwm->rcS0[NCRC_CONTENT], pt ) ) { if( pncwm->fMin || pncwm->fMaxed ) { if( _StrictPtInRect( &pncwm->rcS0[NCRC_CAPTION], pt ) ) return HTCAPTION; }
//---- combined caption/frame case ----
return pwnd->NcBackgroundHitTest( pt, &pncwm->rcS0[NCRC_WINDOW], pncwm->dwStyle, pncwm->dwExStyle, pncwm->framestate, pncwm->rgframeparts, pncwm->rgsizehitparts, pncwm->rcS0 + NCRC_FRAMEFIRST ); } } }
return DoMsgDefault( ptm ); }
//-------------------------------------------------------------------------//
// WM_WINDOWPOSCHANGING pre-wndproc override handler
LRESULT CALLBACK OnOwpPreWindowPosChanging( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( pwnd->IsFrameThemed() ) { // Suppress WM_WINDOWPOSCHANGING from being sent to wndproc if it
// was generated by us calling SetWindowRgn.
// Many apps (e.g. Adobe Acrobat Reader, Photoshop dialogs, etc) that handle
// WM_NCCALCSIZE, WM_WINDOWPOSCHANGING and/or WM_WINDOWPOSCHANGED are not
// reentrant on their handlers for these messages, and therefore botch the
// recursion induced by our SetWindowRgn call when we post-process
// WM_WINDOWPOSCHANGED.
// There is no reason that a theme-complient wndproc should ever know that
// it's window(s) host a region managed by the system.
if( pwnd->AssigningFrameRgn() ) { MsgHandled(ptm); return DefWindowProc(ptm->hwnd, ptm->uMsg, ptm->wParam, ptm->lParam); } } return 0L; }
//-------------------------------------------------------------------------//
// WM_WINDOWPOSCHANGED pre-wndproc override handler
LRESULT CALLBACK OnOwpPreWindowPosChanged( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( pwnd->IsFrameThemed() ) { // Suppress WM_WINDOWPOSCHANGING from being sent to wndproc if it
// was generated by us calling SetWindowRgn.
// Many apps (e.g. Adobe Acrobat Reader, Photoshop dialogs, etc) that handle
// WM_NCCALCSIZE, WM_WINDOWPOSCHANGING and/or WM_WINDOWPOSCHANGED are not
// reentrant on their handlers for these messages, and therefore botch the
// recursion induced by our SetWindowRgn call when we post-process
// WM_WINDOWPOSCHANGED.
// There is no reason that a theme-complient wndproc should ever know that
// it's window(s) host a region managed by the system.
if( pwnd->AssigningFrameRgn() ) { MsgHandled(ptm); return DefWindowProc(ptm->hwnd, ptm->uMsg, ptm->wParam, ptm->lParam); } } return 0L; }
//-------------------------------------------------------------------------//
// WM_WINDOWPOSCHANGED message handler
inline LRESULT WindowPosChangedWorker( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( pwnd->IsRevoked(RF_DEFER) ) { if( !pwnd->IsRevoked(RF_INREVOKE) ) { pwnd->Revoke(); // don't touch PWND after this!
} } else if( pwnd->IsNcThemed() && !IsWindowInDestroy(*pwnd) ) { // If were not resizing, update the window region.
if( pwnd->IsFrameThemed() ) { NCWNDMET* pncwm = NULL; NCTHEMEMET nctm = {0};
// Freshen per-window metrics
if( !pwnd->AssigningFrameRgn() ) { WINDOWPOS *pWndPos = (WINDOWPOS*) ptm->lParam;
// Freshen this window's per-window metrics
pwnd->GetNcWindowMetrics( NULL, &pncwm, &nctm, NCWMF_RECOMPUTE );
// Freshen window metrics for nc-themed children (e.g., MDI child frames)
EnumChildWindows( *pwnd, _FreshenThemeMetricsCB, NULL );
if( !TESTFLAG(pWndPos->flags, SWP_NOSIZE) || pwnd->DirtyFrameRgn() || TESTFLAG(pWndPos->flags, SWP_FRAMECHANGED) ) { if( pWndPos->cx > 0 && pWndPos->cy > 0 ) { pwnd->AssignFrameRgn( TRUE, FTF_REDRAW ); } } } }
_MDIUpdate( *pwnd, ((WINDOWPOS*) ptm->lParam)->flags); } return 0L; }
//-------------------------------------------------------------------------//
// WM_WINDOWPOSCHANGED post-wndproc override handler
//
// Note: we'll handle this post-wndproc for normal, client-side wndprocs
LRESULT CALLBACK OnOwpPostWindowPosChanged( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( !IsServerSideWindow(ptm->hwnd) ) { return WindowPosChangedWorker( pwnd, ptm ); } return 0L; }
//-------------------------------------------------------------------------//
// WM_WINDOWPOSCHANGED DefWindowProc override handler.
//
// Note: we'll handle this in DefWindowProc only for windows with win32k-based
// wndprocs, which are deprived of OWP callbacks.
LRESULT CALLBACK OnDwpWindowPosChanged( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( IsServerSideWindow(ptm->hwnd) ) { WindowPosChangedWorker( pwnd, ptm ); } return 0L; }
//-------------------------------------------------------------------------//
// WM_NACTIVATE DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcActivate( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 1L;
if( pwnd->IsNcThemed() ) { // We need to forward on. The DWP remembers the state
// and MFC apps (for one) need this as well
// but we don't want to actually paint, so lock the window
ptm->lParam = (LPARAM)-1; lRet = DoMsgDefault(ptm);
pwnd->NcPaint( NULL, ptm->wParam ? NCPF_ACTIVEFRAME : NCPF_INACTIVEFRAME, NULL, NULL ); MsgHandled(ptm); }
return lRet; }
//-------------------------------------------------------------------------//
BOOL CThemeWnd::ShouldTrackFrameButton( UINT uHitcode ) { switch(uHitcode) { case HTHELP: return TESTFLAG(_ncwm.dwExStyle, WS_EX_CONTEXTHELP);
case HTMAXBUTTON: if( !TESTFLAG(_ncwm.dwStyle, WS_MAXIMIZEBOX) || (CBS_DISABLED == _ncwm.rawMaxBtnState && FS_ACTIVE == _ncwm.framestate) ) { break; } return TRUE;
case HTMINBUTTON: if( !TESTFLAG(_ncwm.dwStyle, WS_MINIMIZEBOX) || (CBS_DISABLED == _ncwm.rawMinBtnState && FS_ACTIVE == _ncwm.framestate) ) { break; } return TRUE;
case HTCLOSE: if( !_MNCanClose(_hwnd) || (CBS_DISABLED == _ncwm.rawCloseBtnState && FS_ACTIVE == _ncwm.framestate) ) { break; } return TRUE;
case HTSYSMENU: return TESTFLAG(_ncwm.dwStyle, WS_SYSMENU); } return FALSE; }
//-------------------------------------------------------------------------//
// WM_NCLBUTTONDOWN DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcLButtonDown( CThemeWnd* pwnd, THEME_MSG *ptm ) { WPARAM uSysCmd = 0; MsgHandled( ptm );
switch( ptm->wParam /* hittest code */ ) { case HTHELP: case HTMAXBUTTON: case HTMINBUTTON: case HTCLOSE: case HTSYSMENU: if( pwnd->ShouldTrackFrameButton(ptm->wParam) ) { if( pwnd->HasRenderedNcPart(RNCF_CAPTION) ) { POINT pt; MAKEPOINT( pt, ptm->lParam ); if( !pwnd->TrackFrameButton( *pwnd, (int)ptm->wParam, &uSysCmd ) ) { return DoMsgDefault( ptm ); } } else { return DoMsgDefault( ptm ); } } break;
case HTHSCROLL: case HTVSCROLL: if( pwnd->HasRenderedNcPart(RNCF_SCROLLBAR) ) { uSysCmd = ptm->wParam | ((ptm->wParam == HTVSCROLL) ? SC_HSCROLL:SC_VSCROLL);
break; }
// fall thru
default: return DoMsgDefault( ptm ); }
// TODO USER will ignore system command if it is disabled on system menu here,
// don't know why. Imitating the code caused standard min/max/close buttons to
// render so be careful.
if( uSysCmd != 0 ) { SendMessage( *pwnd, WM_SYSCOMMAND, uSysCmd, ptm->lParam ); }
return 0L; }
//-------------------------------------------------------------------------//
// WM_NCMOUSEMOVE DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcMouseMove(CThemeWnd* pwnd, THEME_MSG *ptm) { LRESULT lRet = DoMsgDefault(ptm);
int htHotLast = pwnd->GetNcHotItem(); int htHot;
//
// If the mouse has just entered the NC area, request
// that we be notified when it leaves.
//
if (htHotLast == HTERROR) { TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE | TME_NONCLIENT; tme.hwndTrack = *pwnd; tme.dwHoverTime = 0;
TrackMouseEvent(&tme); }
//
// Filter out the NC elements we don't care about hottracking. And only
// track the element if we've previously rendered it. Some apps handle
// painting non-client elements by handling ncpaint. They may not expect
// that we now hottrack.
//
if ( (IsHTFrameButton(ptm->wParam) && pwnd->HasRenderedNcPart(RNCF_CAPTION) && pwnd->ShouldTrackFrameButton(ptm->wParam)) ||
(IsHTScrollBar(ptm->wParam) && pwnd->HasRenderedNcPart(RNCF_SCROLLBAR)) ) { htHot = (int)ptm->wParam; } else { htHot = HTNOWHERE; }
//
// anything to do?
//
if ((htHot != htHotLast) || IsHTScrollBar(htHot) || IsHTScrollBar(htHotLast)) { POINT pt;
MAKEPOINT( pt, ptm->lParam );
//
// save the hittest code of the NC element the mouse is
// currently over
//
pwnd->SetNcHotItem(htHot);
//
// Determine what should be repainted because the mouse
// is no longer over it
//
if ( IsHTFrameButton(htHotLast) && pwnd->HasRenderedNcPart(RNCF_CAPTION) ) { pwnd->TrackFrameButton(*pwnd, htHotLast, NULL, TRUE); } else if ( IsHTScrollBar(htHotLast) && pwnd->HasRenderedNcPart(RNCF_SCROLLBAR) ) { ScrollBar_MouseMove(*pwnd, (htHot == htHotLast) ? &pt : NULL, (htHotLast == HTVSCROLL) ? TRUE : FALSE); }
//
// Determine what should be repainted because the mouse
// is now over it
//
if ( IsHTFrameButton(htHot) && pwnd->HasRenderedNcPart(RNCF_CAPTION) ) { pwnd->TrackFrameButton(*pwnd, htHot, NULL, TRUE); } else if ( IsHTScrollBar(htHot) && pwnd->HasRenderedNcPart(RNCF_SCROLLBAR) ) { ScrollBar_MouseMove(*pwnd, &pt, (htHot == HTVSCROLL) ? TRUE : FALSE); }
}
return lRet; }
//-------------------------------------------------------------------------//
// WM_NCMOUSELEAVE DefWindowProc msg handler
LRESULT CALLBACK OnDwpNcMouseLeave(CThemeWnd* pwnd, THEME_MSG *ptm) { LRESULT lRet = DoMsgDefault(ptm);
int htHot = pwnd->GetNcHotItem();
//
// the mouse has left NC area, nothing should be drawn in the
// hot state anymore
//
pwnd->SetNcHotItem(HTERROR);
if ( IsHTFrameButton(htHot) && pwnd->ShouldTrackFrameButton(htHot) && pwnd->HasRenderedNcPart(RNCF_CAPTION) ) { pwnd->TrackFrameButton(*pwnd, htHot, NULL, TRUE); } else if ( IsHTScrollBar(htHot) && pwnd->HasRenderedNcPart(RNCF_SCROLLBAR) ) { ScrollBar_MouseMove(*pwnd, NULL, (htHot == HTVSCROLL) ? TRUE : FALSE); }
return lRet; }
//-------------------------------------------------------------------------//
// WM_CONTEXTMENU DefWindowProc msg handler
LRESULT CALLBACK OnDwpContextMenu(CThemeWnd* pwnd, THEME_MSG *ptm) { NCWNDMET* pncwm; POINT pt; MAKEPOINT( pt, ptm->lParam ); MsgHandled( ptm );
if( pwnd->GetNcWindowMetrics( NULL, &pncwm, NULL, 0 ) ) { if ( TESTFLAG(pncwm->dwExStyle, WS_EX_LAYOUTRTL) ) { // mirror the point to hittest correctly
MIRROR_POINT(pncwm->rcS0[NCRC_WINDOW], pt); }
if( _StrictPtInRect( &pncwm->rcS0[NCRC_HSCROLL], pt ) ) { ScrollBar_Menu(*pwnd, *pwnd, ptm->lParam, FALSE); return 0; }
if( _StrictPtInRect( &pncwm->rcS0[NCRC_VSCROLL], pt ) ) { ScrollBar_Menu(*pwnd, *pwnd, ptm->lParam, TRUE); return 0; } }
return DoMsgDefault( ptm ); }
//-------------------------------------------------------------------------//
// WM_SYSCOMMAND DefWindowProc msg handler
LRESULT CALLBACK OnDwpSysCommand( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L;
switch( ptm->wParam & ~0x0F ) { // Handle scroll commands
case SC_VSCROLL: case SC_HSCROLL: HandleScrollCmd( *pwnd, ptm->wParam, ptm->lParam ); MsgHandled( ptm ); return lRet; } return DoMsgDefault( ptm ); }
//-------------------------------------------------------------------------//
// MDI menubar button theme/untheme wrapper
void CThemeWnd::ThemeMDIMenuButtons( BOOL fTheme, BOOL fRedraw ) { // Verify we're an MDI frame with a maximized mdi child
if( _hwndMDIClient && !IsWindowInDestroy(_hwndMDIClient) ) { BOOL fMaxed = FALSE; HWND hwndActive = _MDIGetActive( _hwndMDIClient, &fMaxed ); if( hwndActive && fMaxed ) { ModifyMDIMenubar(fTheme, fRedraw ); } } }
//-------------------------------------------------------------------------//
// MDI menubar button theme/untheme worker
void CThemeWnd::ModifyMDIMenubar( BOOL fTheme, BOOL fRedraw ) { _fThemedMDIBtns = FALSE;
if( IsFrameThemed() || !fTheme ) { MENUBARINFO mbi; mbi.cbSize = sizeof(mbi);
if( GetMenuBarInfo( _hwnd, OBJID_MENU, 0, &mbi ) ) { _NcTraceMsg( NCTF_MDIBUTTONS, TEXT("ModifyMDIMenubar: GetMenuBarInfo() returns hMenu: %08lX, hwndMenu: %08lX"), mbi.hMenu, mbi.hwndMenu );
int cItems = GetMenuItemCount( mbi.hMenu ); int cThemedItems = 0; int cRedraw = 0;
_NcTraceMsg( NCTF_MDIBUTTONS, TEXT("ModifyMDIMenubar: on entry, GetMenuItemCount(hMenu = %08lX) returns %d"), mbi.hMenu, cItems );
if( cItems > 0 ) { for( int i = cItems - 1; i >= 0 && cThemedItems < MDIBTNCOUNT; i-- ) { MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_ID|MIIM_STATE|MIIM_FTYPE|MIIM_BITMAP;
if( GetMenuItemInfo( mbi.hMenu, i, TRUE, &mii ) ) { _NcTraceMsg( NCTF_MDIBUTTONS, TEXT("GetMenuItemInfo by pos (%d) returns ID %04lX"), i, mii.wID );
switch( mii.wID ) { case SC_RESTORE: case SC_MINIMIZE: case SC_CLOSE: { BOOL fThemed = TESTFLAG(mii.fType, MFT_OWNERDRAW); if( (fThemed && fTheme) || (fThemed == fTheme) ) { cThemedItems = MDIBTNCOUNT; // one item is already done, assume all to be.
} else { CMdiBtns* pBtns = LoadMdiBtns( NULL, mii.wID ); if( pBtns ) { if( pBtns->ThemeItem( mbi.hMenu, i, &mii, fTheme ) ) { cThemedItems++; cRedraw++; _NcTraceMsg( NCTF_MDIBUTTONS, TEXT("ModifyMDIMenubar: on entry, GetMenuItemCount(hMenu = %08lX) returns %d"), mbi.hMenu, GetMenuItemCount(mbi.hMenu) ); } } } break; } } } } }
if( cThemedItems ) { _fThemedMDIBtns = fTheme;
if( fRedraw && cRedraw ) { DrawMenuBar( _hwnd ); } }
_NcTraceMsg( NCTF_MDIBUTTONS, TEXT("ModifyMDIMenubar: Modified %d menu items, exiting"), cThemedItems ); } } }
//-------------------------------------------------------------------------//
BOOL CThemeWnd::_PreDefWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plRet ) { if (uMsg == WM_PRINTCLIENT) { PrintClientNotHandled(hwnd); }
return FALSE; }
//-------------------------------------------------------------------------//
BOOL CThemeWnd::_PostDlgProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plRet ) { switch( uMsg ) { case WM_PRINTCLIENT: { PrintClientNotHandled(hwnd); } break; }
return FALSE; }
//-------------------------------------------------------------------------//
// Handles Defwindowproc post-processing for unthemed windows.
BOOL CThemeWnd::_PostWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plRet ) { switch( uMsg ) { // Special-case WM_SYSCOMMAND for MDI frame window updates
case WM_WINDOWPOSCHANGED: if( lParam /* don't trust this */) { _MDIUpdate( hwnd, ((WINDOWPOS*) lParam)->flags); } break;
case WM_MDISETMENU: { HWND hwndActive = _MDIGetActive(hwnd); if( hwndActive ) _MDIChildUpdateParent( hwndActive, TRUE ); break; } } return FALSE; }
//-------------------------------------------------------------------------//
// WM_CREATE post-wndproc msg handler
LRESULT CALLBACK OnOwpPostCreate( CThemeWnd* pwnd, THEME_MSG *ptm ) { if( -1 != ptm->lRet ) { if( pwnd->TestCF( TWCF_FRAME|TWCF_TOOLFRAME )) { DWORD dwFTFlags = FTF_CREATE; CREATESTRUCT* pcs = (CREATESTRUCT*)ptm->lParam;
if( pcs ) { // don't resize dialogs until post-WM_INITDIALOG
if( pwnd->TestCF(TWCF_DIALOG) ) { dwFTFlags |= FTF_NOMODIFYPLACEMENT; }
pwnd->SetFrameTheme( dwFTFlags, NULL ); MsgHandled(ptm); } } } return 0L; }
//---------------------------------------------------------------------------
// WM_INITDIALOG post defdialogproc handler
LRESULT CALLBACK OnDdpPostInitDialog(CThemeWnd* pwnd, THEME_MSG* ptm) { LRESULT lRet = ptm->lRet;
// Do this sequence for dialogs only
if( pwnd->TestCF( TWCF_DIALOG ) && pwnd->TestCF( TWCF_FRAME|TWCF_TOOLFRAME ) ) { DWORD dwFTFlags = FTF_CREATE; pwnd->SetFrameTheme( dwFTFlags, NULL ); MsgHandled(ptm); } return lRet; }
//-------------------------------------------------------------------------//
// WM_STYLECHANGING/WM_SYTLECHANGED Pre DefWindowProc msg handler
LRESULT CALLBACK OnOwpPreStyleChange( CThemeWnd* pwnd, THEME_MSG *ptm ) { // Allow this message to arrive at detination WndProc?
if ( pwnd->SuppressingStyleMsgs() ) { MsgHandled(ptm); return DefWindowProc(ptm->hwnd, ptm->uMsg, ptm->wParam, ptm->lParam); } return 0L; }
//-------------------------------------------------------------------------//
// WM_SYTLECHANGED DefWindowProc msg handler
LRESULT CALLBACK OnDwpStyleChanged( CThemeWnd* pwnd, THEME_MSG *ptm ) { pwnd->StyleChanged((UINT)ptm->wParam, ((STYLESTRUCT*)ptm->lParam)->styleOld, ((STYLESTRUCT*)ptm->lParam)->styleNew ); return 0L; }
//-------------------------------------------------------------------------//
// WM_SETTINGCHANGE post-wndproc handler
LRESULT CALLBACK OnOwpPostSettingChange( CThemeWnd* pwnd, THEME_MSG *ptm ) { /*ignore theme setting change process refresh*/
if( SPI_SETNONCLIENTMETRICS == ptm->wParam && !pwnd->InThemeSettingChange() ) { // recompute per-theme metrics.
if( VALID_CRITICALSECTION(&_csThemeMet) ) { EnterCriticalSection( &_csThemeMet ); // force refresh of NONCLIENTMETRICS cache.
NcGetNonclientMetrics( NULL, TRUE );
LeaveCriticalSection( &_csThemeMet ); }
pwnd->UnloadMdiBtns();
// recycle frame icon handle; current one is no longer valid.
pwnd->AcquireFrameIcon( GetWindowLong(*pwnd, GWL_STYLE), GetWindowLong(*pwnd, GWL_EXSTYLE), TRUE );
// frame windows should be invalidated.
if( pwnd->IsFrameThemed() ) { SetWindowPos( *pwnd, NULL, 0,0,0,0, SWP_DRAWFRAME| SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE ); } } return 0L; }
//-------------------------------------------------------------------------//
// WM_SETTEXT DefWindowProc msg handler
LRESULT CALLBACK OnDwpSetText( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L; if( pwnd->IsFrameThemed() ) { // prevent ourselves from painting as we call on RealDefWindowProc()
// to cache the new window text.
pwnd->LockRedraw( TRUE ); lRet = DoMsgDefault(ptm); pwnd->LockRedraw( FALSE ); } return lRet; }
//-------------------------------------------------------------------------//
// WM_SETICON DefWindowProc msg handler
LRESULT CALLBACK OnDwpSetIcon( CThemeWnd* pwnd, THEME_MSG *ptm ) { LRESULT lRet = 0L;
// invalidate our app icon handle, force re-acquire.
pwnd->SetFrameIcon(NULL);
// call on RealDefWindowProc to cache the icon
lRet = DoMsgDefault( ptm );
// RealDefWindowProc won't call send a WM_NCUAHDRAWCAPTION for large icons
if( ICON_BIG == ptm->wParam && pwnd->IsFrameThemed() ) { NCWNDMET* pncwm; if( pwnd->GetNcWindowMetrics( NULL, &pncwm, NULL, NCWMF_RECOMPUTE ) ) { HDC hdc = _GetNonclientDC( *pwnd, NULL ); if( hdc ) { DTBGOPTS dtbo; dtbo.dwSize = sizeof(dtbo); dtbo.dwFlags = DTBG_DRAWSOLID; pwnd->NcPaintCaption( hdc, pncwm, TRUE, (DWORD)DC_ICON, &dtbo ); ReleaseDC( *pwnd, hdc ); } } } return lRet; }
//-------------------------------------------------------------------------//
#define NCPREV_CLASS TEXT("NCPreviewFakeWindow")
//-------------------------------------------------------------------------//
BOOL _fPreviewSysMetrics = FALSE;
//-------------------------------------------------------------------------//
void _NcSetPreviewMetrics( BOOL fPreview ) { BOOL fPrev = _fPreviewSysMetrics; _fPreviewSysMetrics = fPreview; if( fPreview != fPrev ) { // make sure we reset button metrics if something has changed
_fClassicNcBtnMetricsReset = TRUE; } }
//-------------------------------------------------------------------------//
inline BOOL _NcUsingPreviewMetrics() { return _fPreviewSysMetrics; }
//-------------------------------------------------------------------------//
BOOL NcGetNonclientMetrics( OUT OPTIONAL NONCLIENTMETRICS* pncm, BOOL fRefresh ) { BOOL fRet = FALSE; CInternalNonclientMetrics *pincm = NULL;
if( VALID_CRITICALSECTION(&_csNcSysMet) ) { EnterCriticalSection( &_csNcSysMet );
// Feed off a static instance of NONCLIENTMETRICS to reduce call overhead.
if( _NcUsingPreviewMetrics() ) { // hand off preview metrics and get out.
pincm = &_incmPreview; } else { if( _incmCurrent.Acquire( fRefresh ) ) { pincm = &_incmCurrent; } }
if( pincm ) { if( pncm ) { *pncm = pincm->GetNcm(); } fRet = TRUE; }
LeaveCriticalSection( &_csNcSysMet ); }
return fRet; }
//-------------------------------------------------------------------------//
HFONT NcGetCaptionFont( BOOL fSmallCaption ) { HFONT hf = NULL; if( VALID_CRITICALSECTION(&_csNcSysMet) ) { EnterCriticalSection( &_csNcSysMet );
hf = _NcUsingPreviewMetrics() ? _incmPreview.GetFont( fSmallCaption ) : _incmCurrent.GetFont( fSmallCaption ); LeaveCriticalSection( &_csNcSysMet ); } return hf; }
//-------------------------------------------------------------------------//
void NcClearNonclientMetrics() { _incmCurrent.Clear(); }
//-------------------------------------------------------------------------//
int NcGetSystemMetrics(int nIndex) { if( _NcUsingPreviewMetrics() ) { int iValue; const NONCLIENTMETRICS& ncmPreview = _incmPreview.GetNcm();
switch (nIndex) { case SM_CXHSCROLL: // fall through
case SM_CXVSCROLL: iValue = ncmPreview.iScrollWidth; break; case SM_CYHSCROLL: // fall through
case SM_CYVSCROLL: iValue = ncmPreview.iScrollHeight; break;
case SM_CXSIZE: iValue = ncmPreview.iCaptionWidth; break; case SM_CYSIZE: iValue = ncmPreview.iCaptionHeight; break; case SM_CYCAPTION: iValue = ncmPreview.iCaptionHeight + 1; break; case SM_CXSMSIZE: iValue = ncmPreview.iSmCaptionWidth; break; case SM_CYSMSIZE: iValue = ncmPreview.iSmCaptionHeight; break; case SM_CXMENUSIZE: iValue = ncmPreview.iMenuWidth; break; case SM_CYMENUSIZE: iValue = ncmPreview.iMenuHeight; break; default: iValue = ClassicGetSystemMetrics(nIndex); break; } return iValue; } else { return ClassicGetSystemMetrics(nIndex); } }
//-------------------------------------------------------------------------//
// _InternalGetSystemMetrics() - Themed implementation of GetSystemMetrics().
//
int _InternalGetSystemMetrics( int iMetric, BOOL& fHandled ) { int iRet = 0; int* plSysMet = NULL; NCTHEMEMET nctm;
switch( iMetric ) { case SM_CXSIZE: plSysMet = &nctm.theme_sysmets.cxBtn; break;
case SM_CXSMSIZE: plSysMet = &nctm.theme_sysmets.cxSmBtn; break; }
if( plSysMet && GetCurrentNcThemeMetrics( &nctm ) && nctm.hTheme != NULL && nctm.theme_sysmets.fValid ) { iRet = *plSysMet; fHandled = TRUE; /*was missing (doh! - 408190)*/ }
return iRet; }
//-------------------------------------------------------------------------//
// _InternalSystemParametersInfo() - Themed implementation of SystemParametersInfo().
//
// return value of FALSE interpreted by caller as not handled.
BOOL _InternalSystemParametersInfo( IN UINT uiAction, IN UINT uiParam, IN OUT PVOID pvParam, IN UINT fWinIni, IN BOOL fUnicode, BOOL& fHandled ) { SYSTEMPARAMETERSINFO pfnDefault = fUnicode ? ClassicSystemParametersInfoW : ClassicSystemParametersInfoA;\
BOOL fRet = pfnDefault( uiAction, uiParam, pvParam, fWinIni ); fHandled = TRUE;
if( SPI_GETNONCLIENTMETRICS == uiAction && fRet ) { NCTHEMEMET nctm; if( GetCurrentNcThemeMetrics( &nctm ) && nctm.hTheme != NULL && nctm.theme_sysmets.fValid ) { NONCLIENTMETRICS* pncm = (NONCLIENTMETRICS*)pvParam; pncm->iCaptionWidth = nctm.theme_sysmets.cxBtn; } } return fRet; }
//-------------------------------------------------------------------------//
THEMEAPI DrawNCWindow(CThemeWnd* pThemeWnd, HWND hwndFake, HDC hdc, DWORD dwFlags, LPRECT prc, NONCLIENTMETRICS* pncm, COLORREF* prgb) { // Build up Overide structure
NCPAINTOVERIDE ncpo; pThemeWnd->GetNcWindowMetrics( prc, &ncpo.pncwm, &ncpo.nctm, NCWMF_RECOMPUTE|NCWMF_PREVIEW );
// Force window to look active
if (dwFlags & NCPREV_ACTIVEWINDOW) { ncpo.pncwm->framestate = FS_ACTIVE; ncpo.pncwm->rawCloseBtnState = ncpo.pncwm->rawMaxBtnState = ncpo.pncwm->rawMinBtnState = CBS_NORMAL; } ncpo.pncwm->rgbCaption = prgb[FS_ACTIVE == ncpo.pncwm->framestate ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT]; ncpo.pncwm->dwStyle &= ~WS_SIZEBOX; // Paint the beautiful visual styled window
pThemeWnd->NcPaint(hdc, NCPF_DEFAULT, NULL, &ncpo);
COLORREF rgbBk = prgb[(dwFlags & NCPREV_MESSAGEBOX) ? COLOR_3DFACE : COLOR_WINDOW]; HBRUSH hbrBack = CreateSolidBrush(rgbBk); FillRect(hdc, &ncpo.pncwm->rcW0[NCRC_CLIENT], hbrBack); DeleteObject(hbrBack);
WCHAR szText[MAX_PATH]; // Draw client area
HFONT hfont = CreateFont(-MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72), 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, L"MS Shell Dlg"); if (hfont) { if (dwFlags & NCPREV_MESSAGEBOX) { HTHEME htheme = OpenThemeData( hwndFake, L"Button" ); int offsetX = ((ncpo.pncwm->rcW0[NCRC_CLIENT].right + ncpo.pncwm->rcW0[NCRC_CLIENT].left) / 2) - 40; int offsetY = ((ncpo.pncwm->rcW0[NCRC_CLIENT].bottom + ncpo.pncwm->rcW0[NCRC_CLIENT].top) / 2) - 15; RECT rcButton = { offsetX, offsetY, offsetX + 80, offsetY + 30 }; NcDrawThemeBackground(htheme, hdc, BP_PUSHBUTTON, PBS_DEFAULTED, &rcButton, 0); RECT rcContent; GetThemeBackgroundContentRect(htheme, hdc, BP_PUSHBUTTON, PBS_DEFAULTED, &rcButton, &rcContent); LoadString(g_hInst, IDS_OKBUTTON, szText, ARRAYSIZE(szText)); if (szText[0]) { HFONT hfontOld = (HFONT)SelectObject(hdc, hfont); DrawThemeText(htheme, hdc, BP_PUSHBUTTON, PBS_DEFAULTED, szText, lstrlen(szText), DT_CENTER | DT_VCENTER | DT_SINGLELINE, 0, &rcContent); SelectObject(hdc, hfontOld); } CloseThemeData(htheme); } else if (dwFlags & NCPREV_ACTIVEWINDOW) { HTHEME htheme = OpenThemeData( hwndFake, L"Button" ); RECT rcButton = ncpo.pncwm->rcW0[NCRC_CLIENT]; LoadString(g_hInst, IDS_WINDOWTEXT, szText, ARRAYSIZE(szText)); if (szText[0]) { HFONT hfontOld = (HFONT)SelectObject(hdc, hfont); DTTOPTS DttOpts = {sizeof(DttOpts)}; DttOpts.dwFlags = DTT_TEXTCOLOR; DttOpts.crText = prgb[COLOR_WINDOWTEXT];
DrawThemeTextEx(htheme, hdc, BP_PUSHBUTTON, PBS_DEFAULTED, szText, lstrlen(szText), DT_SINGLELINE, &rcButton, &DttOpts); SelectObject(hdc, hfontOld); } CloseThemeData(htheme); } } DeleteObject(hfont);
ClearNcThemeMetrics(&ncpo.nctm);
return S_OK; }
//-------------------------------------------------------------------------//
THEMEAPI DrawNCPreview(HDC hdc, DWORD dwFlags, LPRECT prc, LPCWSTR pszVSPath, LPCWSTR pszVSColor, LPCWSTR pszVSSize, NONCLIENTMETRICS* pncm, COLORREF* prgb) { WNDCLASS wc;
// Create a fake Window and attach NC themeing to it
if (!GetClassInfo(g_hInst, NCPREV_CLASS, &wc)) { wc.style = 0; wc.lpfnWndProc = DefWindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = g_hInst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1); wc.lpszMenuName = NULL; wc.lpszClassName = NCPREV_CLASS;
if (!RegisterClass(&wc)) return FALSE; }
_incmPreview = *pncm; _incmPreview._fPreview = TRUE; _NcSetPreviewMetrics( TRUE ); DWORD dwExStyle = WS_EX_DLGMODALFRAME | ((dwFlags & NCPREV_RTL) ? WS_EX_RTLREADING : 0); HWND hwndFake = CreateWindowEx(dwExStyle, NCPREV_CLASS, L"", 0, 0, 0, RECTWIDTH(prc), RECTHEIGHT(prc), NULL, NULL, g_hInst, NULL);
if (hwndFake) { HTHEMEFILE htFile = NULL;
WCHAR szCurVSPath[MAX_PATH]; WCHAR szCurVSColor[MAX_PATH]; WCHAR szCurVSSize[MAX_PATH];
GetCurrentThemeName(szCurVSPath, ARRAYSIZE(szCurVSPath), szCurVSColor, ARRAYSIZE(szCurVSColor), szCurVSSize, ARRAYSIZE(szCurVSSize));
if ((lstrcmp(szCurVSPath, pszVSPath) != 0) || (lstrcmp(szCurVSColor, pszVSColor) != 0) || (lstrcmp(szCurVSSize, pszVSSize) != 0)) { HRESULT hr = OpenThemeFile(pszVSPath, pszVSColor, pszVSSize, &htFile, FALSE); if (SUCCEEDED(hr)) { //---- first, detach from the normal theme ----
CThemeWnd::Detach(hwndFake, FALSE);
//---- apply the preview theme ----
hr = ApplyTheme(htFile, 0, hwndFake); } }
//---- attach to the preview theme ----
CThemeWnd* pThemeWnd = CThemeWnd::Attach(hwndFake);
if (VALID_THEMEWND(pThemeWnd)) { struct { DWORD dwNcPrev; UINT uIDStr; DWORD dwFlags; RECT rc; } fakeWindow[]= { {NCPREV_INACTIVEWINDOW, IDS_INACTIVEWINDOW, 0, { prc->left, prc->top, prc->right - 17, prc->bottom - 20 }}, {NCPREV_ACTIVEWINDOW, IDS_ACTIVEWINDOW, NCPREV_ACTIVEWINDOW, { prc->left + 10, prc->top + 22, prc->right, prc->bottom }}, {NCPREV_MESSAGEBOX, IDS_MESSAGEBOX, NCPREV_ACTIVEWINDOW | NCPREV_MESSAGEBOX, { prc->left + (RECTWIDTH(prc)/2) - 75, prc->top + (RECTHEIGHT(prc)/2) - 50 + 22, prc->left + (RECTWIDTH(prc)/2) + 75, prc->top + (RECTHEIGHT(prc)/2) + 50 + 22}}};
WCHAR szWindowName[MAX_PATH]; for (int i = 0; i < ARRAYSIZE(fakeWindow); i++) { if (dwFlags & fakeWindow[i].dwNcPrev) { LoadString(g_hInst, fakeWindow[i].uIDStr, szWindowName, ARRAYSIZE(szWindowName)); SetWindowText(hwndFake, szWindowName); if (fakeWindow[i].dwNcPrev & NCPREV_MESSAGEBOX) { SetWindowLongPtr(hwndFake, GWL_STYLE, WS_TILED | WS_CAPTION | WS_SYSMENU); } else { SetWindowLongPtr(hwndFake, GWL_STYLE, WS_TILEDWINDOW | WS_VSCROLL); }
DrawNCWindow(pThemeWnd, hwndFake, hdc, fakeWindow[i].dwFlags, &fakeWindow[i].rc, pncm, prgb); } }
// Clean Up
CThemeWnd::Detach(hwndFake, 0); }
if (htFile) { CloseThemeFile(htFile); //---- clear the preview hold on the theme file ----
ApplyTheme(NULL, 0, hwndFake); }
DestroyWindow(hwndFake); }
_NcSetPreviewMetrics( FALSE ); _incmPreview.Clear(); return S_OK; }
//-------------------------------------------------------------------------//
// CMdiBtns impl
//-------------------------------------------------------------------------//
//-------------------------------------------------------------------------//
// ctor
CMdiBtns::CMdiBtns() { ZeroMemory( _rgBtns, sizeof(_rgBtns) ); _rgBtns[0].wID = SC_CLOSE; _rgBtns[1].wID = SC_RESTORE; _rgBtns[2].wID = SC_MINIMIZE; }
//-------------------------------------------------------------------------//
// Helper: button lookup based on syscmd ID.
CMdiBtns::MDIBTN* CMdiBtns::_FindBtn( UINT wID ) { for( int i = 0; i < ARRAYSIZE(_rgBtns); i++ ) { if( wID == _rgBtns[i].wID ) { return (_rgBtns + i); } } return NULL; }
//-------------------------------------------------------------------------//
// Acquires MDI button resources,.computes metrics
BOOL CMdiBtns::Load( HTHEME hTheme, IN OPTIONAL HDC hdc, UINT uSysCmd ) { // if caller wants all buttons loaded, call recursively.
if( 0 == uSysCmd ) { return Load( hTheme, hdc, SC_CLOSE ) && Load( hTheme, hdc, SC_RESTORE ) && Load( hTheme, hdc, SC_MINIMIZE ); } MDIBTN* pBtn = _FindBtn( uSysCmd ); if( pBtn && !VALID_WINDOWPART(pBtn->iPartId) /*only if necessary*/ ) { // select appropriate window part
WINDOWPARTS iPartId = BOGUS_WINDOWPART; switch( uSysCmd ) { case SC_CLOSE: iPartId = WP_MDICLOSEBUTTON; break; case SC_RESTORE: iPartId = WP_MDIRESTOREBUTTON; break; case SC_MINIMIZE: iPartId = WP_MDIMINBUTTON; break; } if( VALID_WINDOWPART(iPartId) ) { if( IsThemePartDefined( hTheme, iPartId, 0) ) { // Retrieve sizing type, defaulting to 'stretch'.
if( FAILED( GetThemeInt( hTheme, iPartId, 0, TMT_SIZINGTYPE, (int*)&pBtn->sizingType ) ) ) { pBtn->sizingType = ST_STRETCH; } // if 'truesize', retrieve the size.
if( ST_TRUESIZE == pBtn->sizingType ) { // If no DC provided, base size on screen DC of default monitor.
HDC hdcSize = hdc; if( NULL == hdcSize ) { hdcSize = GetDC(NULL); }
if( FAILED( GetThemePartSize( hTheme, hdc, iPartId, 0, NULL, TS_TRUE, &pBtn->size ) ) ) { pBtn->sizingType = ST_STRETCH; }
if( hdcSize != hdc ) { ReleaseDC(NULL, hdcSize); } }
// not 'truesize'; use system metrics for MDI buttons
if( pBtn->sizingType != ST_TRUESIZE ) { pBtn->size.cx = NcGetSystemMetrics( SM_CXMENUSIZE ); pBtn->size.cy = NcGetSystemMetrics( SM_CYMENUSIZE ); } // assign button attributes
pBtn->iPartId = iPartId; } } } return pBtn != NULL && VALID_WINDOWPART(pBtn->iPartId); }
//-------------------------------------------------------------------------//
// Releases MDI button resources,.resets metrics
void CMdiBtns::Unload( IN OPTIONAL UINT uSysCmd ) { // if caller wants all buttons unloaded, call recursively.
if( 0 == uSysCmd ) { Unload( SC_CLOSE ); Unload( SC_RESTORE ); Unload( SC_MINIMIZE ); return; }
MDIBTN* pBtn = _FindBtn( uSysCmd );
if( pBtn ) { SAFE_DELETE_GDIOBJ(pBtn->hbmTheme); ZeroMemory(pBtn, sizeof(*pBtn)); // restore our zeroed syscommand value
pBtn->wID = uSysCmd; } }
//-------------------------------------------------------------------------//
// Theme/untheme MDI frame menubar's Minimize, Restore, Close menu items.
BOOL CMdiBtns::ThemeItem( HMENU hMenu, int iPos, MENUITEMINFO* pmii, BOOL fTheme ) { // To theme, we simply make the item owner draw. To untheme,
// we restore it to system-drawn.
BOOL fRet = FALSE; MDIBTN* pBtn = _FindBtn( pmii->wID );
if( pBtn && pmii && hMenu ) { if( fTheme ) { // save off previous menuitem type, bitmap
pBtn->fTypePrev = pmii->fType; pBtn->hbmPrev = pmii->hbmpItem;
pmii->fType &= ~MFT_BITMAP; pmii->fType |= MFT_OWNERDRAW|MFT_RIGHTJUSTIFY; pmii->hbmpItem = NULL; } else { // restore menuitem type, bitmap
pmii->fType = pBtn->fTypePrev|MFT_RIGHTJUSTIFY /*409042 - force right-justify on the way out*/; pmii->hbmpItem = pBtn->hbmPrev; } pmii->fMask = MIIM_FTYPE;
fRet = SetMenuItemInfo( hMenu, iPos, TRUE, pmii );
if( !fRet || !fTheme ) { pBtn->fTypePrev = 0; pBtn->hbmPrev = NULL; } } return fRet; }
//-------------------------------------------------------------------------//
// Computes button state identifier from Win32 owner draw state
CLOSEBUTTONSTATES CMdiBtns::_CalcState( ULONG ulOwnerDrawAction, ULONG ulOwnerDrawState ) { CLOSEBUTTONSTATES iStateId = CBS_NORMAL;
if( TESTFLAG(ulOwnerDrawState, ODS_DISABLED|ODS_GRAYED|ODS_INACTIVE) ) { iStateId = CBS_DISABLED; } else if( TESTFLAG(ulOwnerDrawState, ODS_SELECTED) ) { iStateId = CBS_PUSHED; } else if( TESTFLAG(ulOwnerDrawState, ODS_HOTLIGHT) ) { iStateId = CBS_HOT; } return iStateId; }
//-------------------------------------------------------------------------//
// MDI sys button WM_DRAWITEM handler
BOOL CMdiBtns::Measure( HTHEME hTheme, MEASUREITEMSTRUCT* pmis ) { MDIBTN* pBtn = _FindBtn( pmis->itemID );
if( pBtn && VALID_WINDOWPART(pBtn->iPartId) ) { pmis->itemWidth = pBtn->size.cx; pmis->itemHeight = pBtn->size.cy; return TRUE; }
return FALSE; }
//-------------------------------------------------------------------------//
// MDI sys button WM_DRAWITEM handler
BOOL CMdiBtns::Draw( HTHEME hTheme, DRAWITEMSTRUCT* pdis ) { MDIBTN* pBtn = _FindBtn( pdis->itemID );
if( pBtn && VALID_WINDOWPART(pBtn->iPartId) ) { return SUCCEEDED(NcDrawThemeBackground( hTheme, pdis->hDC, pBtn->iPartId, _CalcState( pdis->itemAction, pdis->itemState ), &pdis->rcItem, 0 )); } return FALSE; }
//-------------------------------------------------------------------------////
// "Comments?" link in caption bar, known as the PHellyar (lame) button
//-------------------------------------------------------------------------//
#ifdef LAME_BUTTON
//-------------------------------------------------------------------------//
WCHAR g_szLameText[50] = {0};
//-------------------------------------------------------------------------//
#define SZ_LAMETEXT_SUBKEY TEXT("Control Panel\\Desktop")
#define SZ_LAMETEXT_VALUE TEXT("LameButtonText")
#define SZ_LAMETEXT_DEFAULT TEXT("Comments?")
#define CLR_LAMETEXT RGB(91, 171, 245)
//-------------------------------------------------------------------------//
void InitLameText() { CCurrentUser hkeyCurrentUser(KEY_READ); HKEY hkLame; HRESULT hr = E_FAIL;
if ( RegOpenKeyEx(hkeyCurrentUser, SZ_LAMETEXT_SUBKEY, 0, KEY_QUERY_VALUE, &hkLame) == ERROR_SUCCESS ) { hr = RegistryStrRead(hkLame, SZ_LAMETEXT_VALUE, g_szLameText, ARRAYSIZE(g_szLameText)); RegCloseKey(hkLame); }
if ( FAILED(hr) ) { StringCchCopyW(g_szLameText, ARRAYSIZE(g_szLameText), SZ_LAMETEXT_DEFAULT); } }
//-------------------------------------------------------------------------//
VOID CThemeWnd::InitLameResources() { //
// Using GetWindowInfo here bc GetWindowLong masks
// out the WS_EX_LAMEBUTTON bit.
//
SAFE_DELETE_GDIOBJ(_hFontLame);
WINDOWINFO wi = {0};
wi.cbSize = sizeof(wi); if ( GetWindowInfo(_hwnd, &wi) && TESTFLAG(wi.dwExStyle, WS_EX_LAMEBUTTON) ) { SIZE sizeLame; HFONT hfCaption = NcGetCaptionFont(TESTFLAG(wi.dwExStyle, WS_EX_TOOLWINDOW));
if( hfCaption != NULL ) { LOGFONT lfLame; if( GetObject( hfCaption, sizeof(lfLame), &lfLame ) ) { lfLame.lfHeight -= (lfLame.lfHeight > 0) ? 2 : -2; lfLame.lfUnderline = TRUE; lfLame.lfWeight = FW_THIN;
HFONT hFontLame = CreateFontIndirect(&lfLame); if ( hFontLame != NULL ) { HDC hdc = GetWindowDC(_hwnd);
if ( hdc != NULL ) { SelectObject(hdc, hFontLame);
if (GetTextExtentPoint32(hdc, g_szLameText, lstrlen(g_szLameText), &sizeLame)) { _hFontLame = hFontLame; hFontLame = NULL; // don't free at end of this function
_sizeLame = sizeLame; }
ReleaseDC(_hwnd, hdc); } }
if (hFontLame) // didn't assign this font
DeleteObject(hFontLame); } } }
}
//-------------------------------------------------------------------------//
VOID CThemeWnd::ClearLameResources() { SAFE_DELETE_GDIOBJ(_hFontLame); ZeroMemory( &_sizeLame, sizeof(_sizeLame) ); }
//-------------------------------------------------------------------------//
inline VOID CThemeWnd::DrawLameButton(HDC hdc, IN const NCWNDMET* pncwm) { if ( TESTFLAG(pncwm->dwExStyle, WS_EX_LAMEBUTTON) && _hFontLame ) { Log(LOG_RFBUG, L"DrawLameButton; _hFontLame=0x%x", _hFontLame);
HFONT hFontSave = (HFONT)SelectObject(hdc, _hFontLame); COLORREF clrSave = SetTextColor(hdc, CLR_LAMETEXT);
DrawText(hdc, g_szLameText, lstrlen(g_szLameText), (LPRECT)&pncwm->rcW0[NCRC_LAMEBTN], DT_LEFT | DT_SINGLELINE);
SetTextColor(hdc, clrSave); SelectObject(hdc, hFontSave); } }
//-------------------------------------------------------------------------//
VOID CThemeWnd::GetLameButtonMetrics( NCWNDMET* pncwm, const SIZE* psizeCaption ) { if( TESTFLAG(pncwm->dwExStyle, WS_EX_LAMEBUTTON) && _hFontLame ) { BOOL fLameOn; RECT rcCaptionText = pncwm->rcS0[NCRC_CAPTIONTEXT]; RECT* prcButton = &pncwm->rcS0[NCRC_LAMEBTN]; int cxPad = NcGetSystemMetrics(SM_CXEDGE) * 2; // Enough room to draw the lame button link?
fLameOn = RECTWIDTH(&rcCaptionText) > psizeCaption->cx + cxPad + // between caption, lame text
_sizeLame.cx + cxPad; // between lame text, nearest button;
//---- compute lame button alignment ----
BOOL fReverse = TRUE; // normally, lame goes on right side
//---- WS_EX_RIGHT wants the opposite ----
if (TESTFLAG(_ncwm.dwExStyle, WS_EX_RIGHT)) fReverse = FALSE;
DWORD dwFlags = GetTextAlignFlags(_hTheme, &_ncwm, fReverse);
//---- turn off lame button for center captions ----
if (dwFlags & DT_CENTER) fLameOn = FALSE;
if ( fLameOn ) { CopyRect(prcButton, &rcCaptionText);
//---- note: pMargins already includes the theme specified ----
//---- CaptionMargins (which scale with DPI) and the ----
//---- icon and buttons widths ----
if(dwFlags & DT_RIGHT) // put lame on right
{ prcButton->left = (prcButton->right - _sizeLame.cx) - cxPad ;
//---- adjust margins to remove lame area ----
pncwm->CaptionMargins.cxRightWidth -= _sizeLame.cx; } else // put lame on left
{ prcButton->right = (prcButton->left + _sizeLame.cx) + cxPad;
//---- adjust margins to remove lame area ----
pncwm->CaptionMargins.cxLeftWidth += _sizeLame.cx; }
// vertically center the text between margins
prcButton->top += (RECTHEIGHT(&rcCaptionText) - _sizeLame.cy)/2; prcButton->bottom = prcButton->top + _sizeLame.cy; } } }
#endif // LAME_BUTTON
#ifdef DEBUG
//-------------------------------------------------------------------------//
void CDECL _NcTraceMsg( ULONG uFlags, LPCTSTR pszFmt, ...) { if( TESTFLAG(_NcTraceFlags, uFlags) || NCTF_ALWAYS == uFlags ) { va_list args; va_start(args, pszFmt);
TCHAR szSpew[2048]; StringCchPrintf(szSpew, ARRAYSIZE(szSpew), pszFmt, args); OutputDebugString(szSpew); OutputDebugString(TEXT("\n"));
va_end(args); } }
//-------------------------------------------------------------------------//
void INIT_THEMEWND_DBG( CThemeWnd* pwnd ) { if( IsWindow( *pwnd ) ) { GetWindowText( *pwnd, pwnd->_szCaption, ARRAYSIZE(pwnd->_szCaption) ); GetClassName( *pwnd, pwnd->_szWndClass, ARRAYSIZE(pwnd->_szWndClass) ); } }
//-------------------------------------------------------------------------//
void CThemeWnd::Spew( DWORD dwSpewFlags, LPCTSTR pszFmt, LPCTSTR pszClassList ) { if( pszClassList && *pszClassList ) { if( !_tcsstr( pszClassList, _szWndClass ) ) return; }
TCHAR szInfo[MAX_PATH*2]; TCHAR szMsg[MAX_PATH*2];
StringCchPrintf( szInfo, ARRAYSIZE(szInfo), TEXT("%08lX -'%s' ('%s') cf: %08lX"), _hwnd, _szCaption, _szWndClass, _fClassFlags ); StringCchPrintf( szMsg, ARRAYSIZE(szMsg), pszFmt, szInfo ); Log(LOG_NCATTACH, szMsg ); }
typedef struct { DWORD dwProcessId; DWORD dwThreadId; DWORD dwSpewFlags; LPCTSTR pszFmt; LPCTSTR pszClassList; } SPEW_ALL;
//-------------------------------------------------------------------------//
BOOL _SpewAllEnumCB( HWND hwnd, LPARAM lParam ) { SPEW_ALL* psa = (SPEW_ALL*)lParam;
if( IsWindowProcess( hwnd, psa->dwProcessId ) ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd( hwnd ); if( VALID_THEMEWND(pwnd) ) pwnd->Spew( psa->dwSpewFlags, psa->pszFmt ); }
return TRUE; }
//-------------------------------------------------------------------------//
void CThemeWnd::SpewAll( DWORD dwSpewFlags, LPCTSTR pszFmt, LPCTSTR pszClassList ) { SPEW_ALL sa; sa.dwThreadId = GetCurrentThreadId(); sa.dwProcessId = GetCurrentProcessId(); sa.dwSpewFlags = dwSpewFlags; sa.pszFmt = pszFmt; sa.pszClassList = pszClassList;
//---- this will enum all windows for this process (all desktops, all child levels) ----
EnumProcessWindows( _SpewAllEnumCB, (LPARAM)&sa ); }
//-------------------------------------------------------------------------//
void CThemeWnd::SpewLeaks() { if( _cObj > 0 ) { Log(LOG_NCATTACH, L"LEAK WARNING: %d CThemeWnd instances outstanding.", _cObj ); } }
//-------------------------------------------------------------------------//
void SPEW_RECT( ULONG ulTrace, LPCTSTR pszMsg, LPCRECT prc ) { LPCTSTR pszFmt = TEXT("%s: {L:%d,T:%d,R:%d,B:%d}, (%d x %d)"); WCHAR szMsg[1024];
StringCchPrintfW( szMsg, ARRAYSIZE(szMsg), pszFmt, pszMsg, prc->left, prc->top, prc->right, prc->bottom, RECTWIDTH(prc), RECTHEIGHT(prc) ); _NcTraceMsg(ulTrace, szMsg); }
//-------------------------------------------------------------------------//
void SPEW_MARGINS( ULONG ulTrace, LPCTSTR pszMsg, LPCRECT prcParent, LPCRECT prcChild ) { LPCTSTR pszFmt = TEXT("%s: {L:%d,T:%d,R:%d,B:%d}"); WCHAR szMsg[1024];
StringCchPrintfW( szMsg, ARRAYSIZE(szMsg), pszFmt, pszMsg, prcChild->left - prcParent->left, prcChild->top - prcParent->top, prcParent->right - prcChild->right, prcParent->bottom - prcChild->bottom ); _NcTraceMsg(ulTrace, szMsg); }
//-------------------------------------------------------------------------//
void SPEW_RGNRECT( ULONG ulTrace, LPCTSTR pszMsg, HRGN hrgn, int iPartID ) { RECT rc; if( NULLREGION == GetRgnBox( hrgn, &rc ) ) FillMemory( &rc, sizeof(rc), static_cast<UCHAR>(-1) ); _NcTraceMsg( ulTrace, TEXT("Region %08lX for partID[%d]:\n\t"), hrgn, iPartID ); SPEW_RECT( ulTrace, pszMsg, &rc ); }
//-------------------------------------------------------------------------//
void SPEW_WINDOWINFO( ULONG ulTrace, WINDOWINFO* pwi ) { SPEW_RECT(ulTrace, TEXT("->wi.rcWindow"), &pwi->rcWindow ); SPEW_RECT(ulTrace, TEXT("->wi.rcClient"), &pwi->rcClient ); _NcTraceMsg(ulTrace, TEXT("->wi.dwStyle: %08lX"), pwi->dwStyle ); _NcTraceMsg(ulTrace, TEXT("->wi.dwExStyle: %08lX"), pwi->dwExStyle ); _NcTraceMsg(ulTrace, TEXT("->wi.dwWindowStatus: %08lX"), pwi->dwWindowStatus ); _NcTraceMsg(ulTrace, TEXT("->wi.cxWindowBorders: %d"), pwi->cxWindowBorders ); _NcTraceMsg(ulTrace, TEXT("->wi.cyWindowBorders: %d"), pwi->cyWindowBorders ); }
//-------------------------------------------------------------------------//
void SPEW_NCWNDMET( ULONG ulTrace, LPCTSTR pszMsg, NCWNDMET* pncwm ) { _NcTraceMsg(ulTrace, TEXT("\n%s - Spewing NCWNDMET @ %08lx..."), pszMsg, pncwm );
_NcTraceMsg(ulTrace, TEXT("->fValid: %d"), pncwm->fValid ); _NcTraceMsg(ulTrace, TEXT("->dwStyle: %08lX"), pncwm->dwStyle ); _NcTraceMsg(ulTrace, TEXT("->dwExStyle: %08lX"), pncwm->dwExStyle ); _NcTraceMsg(ulTrace, TEXT("->dwWindowStatus: %08lX"), pncwm->dwWindowStatus ); _NcTraceMsg(ulTrace, TEXT("->fFrame: %d"), pncwm->fFrame ); _NcTraceMsg(ulTrace, TEXT("->fSmallFrame: %d"), pncwm->fSmallFrame ); _NcTraceMsg(ulTrace, TEXT("->iFRAMEBOTTOM %d"), pncwm->rgframeparts[iFRAMEBOTTOM] ); _NcTraceMsg(ulTrace, TEXT("->iFRAMELEFT: %d"), pncwm->rgframeparts[iFRAMELEFT] ); _NcTraceMsg(ulTrace, TEXT("->iFRAMERIGHT: %d"), pncwm->rgframeparts[iFRAMERIGHT] ); _NcTraceMsg(ulTrace, TEXT("->framestate: %d"), pncwm->framestate ); _NcTraceMsg(ulTrace, TEXT("->iMinButtonPart: %d"), pncwm->iMinButtonPart); _NcTraceMsg(ulTrace, TEXT("->iMaxButtonPart: %d"), pncwm->iMaxButtonPart); _NcTraceMsg(ulTrace, TEXT("->rawCloseBtnState: %d"), pncwm->rawCloseBtnState); _NcTraceMsg(ulTrace, TEXT("->rawMinBtnState: %d"), pncwm->rawMinBtnState); _NcTraceMsg(ulTrace, TEXT("->rawMaxBtnState: %d"), pncwm->rawMaxBtnState); _NcTraceMsg(ulTrace, TEXT("->cyMenu: %d"), pncwm->cyMenu ); _NcTraceMsg(ulTrace, TEXT("->cnMenuOffsetLeft: %d"), pncwm->cnMenuOffsetLeft ); _NcTraceMsg(ulTrace, TEXT("->cnMenuOffsetRight: %d"), pncwm->cnMenuOffsetRight ); _NcTraceMsg(ulTrace, TEXT("->cnMenuOffsetTop: %d"), pncwm->cnMenuOffsetTop ); _NcTraceMsg(ulTrace, TEXT("->cnBorders: %d"), pncwm->cnBorders ); _NcTraceMsg(ulTrace, TEXT("->CaptionMargins: (%d,%d,%d,%d)"), pncwm->CaptionMargins );
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_WINDOW] "), &pncwm->rcS0[NCRC_WINDOW] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_CLIENT] "), &pncwm->rcS0[NCRC_CLIENT] ); SPEW_MARGINS(ulTrace, TEXT("Window-Client margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_CLIENT] );
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_CONTENT] "), &pncwm->rcS0[NCRC_CONTENT] ); SPEW_MARGINS(ulTrace, TEXT("Window-Content margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_CONTENT]);
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_MENUBAR] "), &pncwm->rcS0[NCRC_MENUBAR] ); SPEW_MARGINS(ulTrace, TEXT("Window-Menubar margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_MENUBAR]);
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_CAPTION] "), &pncwm->rcS0[NCRC_CAPTION] ); SPEW_MARGINS(ulTrace, TEXT("Window-Caption margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_CAPTION]);
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_FRAMELEFT] "), &pncwm->rcS0[NCRC_FRAMELEFT] ); SPEW_MARGINS(ulTrace, TEXT("Window-FrameLeft margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_FRAMELEFT]);
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_FRAMERIGHT]"), &pncwm->rcS0[NCRC_FRAMERIGHT] ); SPEW_MARGINS(ulTrace, TEXT("Window-FrameRight margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_FRAMERIGHT]);
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_FRAMEBOTTOM]"), &pncwm->rcS0[NCRC_FRAMEBOTTOM] ); SPEW_MARGINS(ulTrace, TEXT("Window-FrameBottom margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_FRAMEBOTTOM]);
SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_CLIENTEDGE]"), &pncwm->rcS0[NCRC_CLIENTEDGE] ); SPEW_MARGINS(ulTrace, TEXT("Window-ClientEdge margins"), &pncwm->rcS0[NCRC_WINDOW], &pncwm->rcS0[NCRC_CLIENTEDGE]); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_HSCROLL] "), &pncwm->rcS0[NCRC_HSCROLL] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_VSCROLL] "), &pncwm->rcS0[NCRC_VSCROLL] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_SIZEBOX] "), &pncwm->rcS0[NCRC_SIZEBOX] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_CLOSEBTN] "), &pncwm->rcS0[NCRC_CLOSEBTN] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_MINBTN] "), &pncwm->rcS0[NCRC_MINBTN] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_MAXBTN] "), &pncwm->rcS0[NCRC_MAXBTN] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_SYSBTN] "), &pncwm->rcS0[NCRC_SYSBTN] ); SPEW_RECT(ulTrace, TEXT("->rcS0[NCRC_HELPBTN] "), &pncwm->rcS0[NCRC_HELPBTN] ); #ifdef LAME_BUTTON
SPEW_RECT(ulTrace, TEXT("rcLame"), &pncwm->rcS0[NCRC_LAMEBTN] ); #endif // LAME_BUTTON
}
//-------------------------------------------------------------------------//
void SPEW_THEMEMSG( ULONG ulTrace, LPCTSTR pszMsg, THEME_MSG* ptm ) { _NcTraceMsg(ulTrace, TEXT("%s hwnd: %08lX, uMsg: %04lX, handled?: %d"), pszMsg, (ptm)->hwnd, (ptm)->uMsg, (ptm)->fHandled ); }
//-------------------------------------------------------------------------//
void SPEW_SCROLLINFO( LPCTSTR pszMsg, HWND hwnd, LPCSCROLLINFO psi ) { #ifdef _ENABLE_SCROLL_SPEW_
_NcTraceMsg(ulTrace, L"%s for HWND %08lX...\ncbSize: %d\nfMask: %08lX\nnMin: %d\nnMax: %d\nnPage: %d\nnPos: %d", pszMsg, hwnd, psi->cbSize, psi->fMask, psi->nMin, psi->nMax, psi->nPage, psi->nPos ); #endif _ENABLE_SCROLL_SPEW_
}
#if defined(DEBUG_NCPAINT)
static int _cPaintSleep = 10;
void _DebugBackground( HDC hdc, COLORREF rgb, const RECT *prc ) { // paint some indicator stuff
COLORREF rgb0 = SetBkColor( hdc, rgb ); SPEW_RECT( NCTF_ALWAYS, TEXT("\tprc"), prc ); ExtTextOut( hdc, prc->left, prc->top, ETO_OPAQUE, prc, NULL, 0, NULL ); Sleep(_cPaintSleep); SetBkColor( hdc, rgb0 ); }
//-------------------------------------------------------------------------//
HRESULT _DebugDrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *prc, OPTIONAL const RECT* prcClip ) { if( TESTFLAG( _NcTraceFlags, NCTF_NCPAINT ) ) { _NcTraceMsg( NCTF_ALWAYS, TEXT("DrawThemeBackground( hTheme = %08lX, hdc = %08lX, iPartId = %d, iStateId = %d"), hTheme, hdc, iPartId, iStateId ); _DebugBackground( hdc, RGBDEBUGBKGND, prc ); }
// paint the real background.
HRESULT hr = DrawThemeBackground( hTheme, hdc, iPartId, iStateId, prc, prcClip );
if( TESTFLAG( _NcTraceFlags, NCTF_NCPAINT ) ) { Sleep(_cPaintSleep); }
return hr; }
//-------------------------------------------------------------------------//
HRESULT _DebugDrawThemeBackgroundEx( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *prc, OPTIONAL const DTBGOPTS *pOptions ) { if( TESTFLAG( _NcTraceFlags, NCTF_NCPAINT ) ) { _NcTraceMsg( NCTF_ALWAYS, TEXT("DrawThemeBackground( hTheme = %08lX, hdc = %08lX, iPartId = %d, iStateId = %d"), hTheme, hdc, iPartId, iStateId ); _DebugBackground( hdc, RGBDEBUGBKGND, prc ); }
// paint the real background.
HRESULT hr = DrawThemeBackgroundEx( hTheme, hdc, iPartId, iStateId, prc, pOptions );
if( TESTFLAG( _NcTraceFlags, NCTF_NCPAINT ) ) { Sleep(_cPaintSleep); }
return hr; }
//-------------------------------------------------------------------------//
void NcDebugClipRgn( HDC hdc, COLORREF rgbPaint ) { if( TESTFLAG( _NcTraceFlags, NCTF_NCPAINT ) ) { HRGN hrgn = CreateRectRgn(0,0,1,1);
if( hrgn ) { if( GetClipRgn( hdc, hrgn ) > 0 ) { HBRUSH hbr = CreateSolidBrush(rgbPaint); FillRgn( hdc, hrgn, hbr ); DeleteObject(hbr); Sleep(_cPaintSleep); } DeleteObject(hrgn); } } }
#endif //defined(DEBUG_NCPAINT)
#endif DEBUG
|