6702 lines
225 KiB
C++
6702 lines
225 KiB
C++
//-------------------------------------------------------------------------//
|
|
// 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 );
|
|
BOOL _ClientRectToScreen( HWND, LPRECT prcClient );
|
|
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 );
|
|
HRESULT _CreateBackgroundBitmap( IN OPTIONAL HDC, IN HTHEME, IN int iPartId, IN int iStateId, IN OUT LPSIZE, OUT HBITMAP*);
|
|
HRESULT _CreateMdiMenuItemBitmap( IN HDC hdc, IN HTHEME hTheme, IN OUT SIZE* pSize, IN OUT MENUITEMINFO* pmii );
|
|
void _ComputeElementOffset( HTHEME, int iPartId, int iStateId, LPCRECT prcBase, POINT *pptOffset);
|
|
int _GetRawClassicCaptionHeight( DWORD dwStyle, DWORD dwExStyle );
|
|
int _GetSumClassicCaptionHeight( DWORD dwStyle, DWORD dwExStyle );
|
|
void _ComputeNcWindowStatus( IN HWND, IN DWORD dwStatus, IN OUT NCWNDMET* pncwm );
|
|
BOOL _NeedsWindowEdgeStyle(DWORD dwStyle, DWORD dwExStyle );
|
|
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();
|
|
|
|
//-------------------------------------------------------------------------//
|
|
#ifdef THEMED_NCBTNMETRICS
|
|
BOOL _GetClassicNcBtnMetrics( IN OPTIONAL NCWNDMET* pncwm, IN OPTIONAL HICON hAppIcon,
|
|
IN OPTIONAL BOOL fCanClose, IN OPTIONAL BOOL fRefresh = FALSE );
|
|
#else THEMED_NCBTNMETRICS
|
|
BOOL _GetNcBtnMetrics( IN OUT NCWNDMET*, IN const NCTHEMEMET*, IN HICON, IN OPTIONAL BOOL );
|
|
#endif THEMED_NCBTNMETRICS
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// 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
|
|
//-------------------------------------------------------------------------//
|
|
|
|
//-------------------------------------------------------------------------//
|
|
BOOL _ClientRectToScreen( HWND hwnd, LPRECT prcClient )
|
|
{
|
|
if( prcClient && GetClientRect( hwnd, prcClient ) )
|
|
{
|
|
POINT* pp = (POINT*)prcClient;
|
|
|
|
//---- use MapWindowPoints() to account for mirrored windows ----
|
|
MapWindowPoints(hwnd, HWND_DESKTOP, pp, 2);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
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);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// Determines whether WS_EX_WINDOWEDGE should be assumed.
|
|
// Ripped from USER sources (rtl\winmgr.c)
|
|
BOOL _NeedsWindowEdgeStyle(DWORD dwStyle, DWORD dwExStyle )
|
|
{
|
|
BOOL fGetsWindowEdge = FALSE;
|
|
|
|
if (dwExStyle & WS_EX_DLGMODALFRAME)
|
|
fGetsWindowEdge = TRUE;
|
|
else if (dwExStyle & WS_EX_STATICEDGE)
|
|
fGetsWindowEdge = FALSE;
|
|
else if (dwStyle & WS_THICKFRAME)
|
|
fGetsWindowEdge = TRUE;
|
|
else switch (dwStyle & WS_CAPTION)
|
|
{
|
|
case WS_DLGFRAME:
|
|
fGetsWindowEdge = TRUE;
|
|
break;
|
|
case WS_CAPTION:
|
|
fGetsWindowEdge = TRUE; // PORTPORT: SHIMSHIM should be: = (RtlGetExpWinVer(hMod) > VER40)
|
|
// we will assume a new app; old apps are denied.
|
|
break;
|
|
}
|
|
|
|
return(fGetsWindowEdge);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// _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 );
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// Creates a replacement menu item bitmap for MDI frame window menubar
|
|
// buttons for maximized MDI child.
|
|
HRESULT _CreateMdiMenuItemBitmap(
|
|
IN HDC hdc,
|
|
IN HTHEME hTheme,
|
|
IN OUT SIZE* pSize,
|
|
IN OUT MENUITEMINFO* pmii )
|
|
{
|
|
WINDOWPARTS iPartId;
|
|
CLOSEBUTTONSTATES iStateId = CBS_NORMAL;
|
|
|
|
switch( (UINT)pmii->wID )
|
|
{
|
|
case SC_CLOSE:
|
|
iPartId = WP_MDICLOSEBUTTON;
|
|
break;
|
|
|
|
case SC_MINIMIZE:
|
|
iPartId = WP_MDIMINBUTTON;
|
|
break;
|
|
|
|
case SC_RESTORE:
|
|
iPartId = WP_MDIRESTOREBUTTON;
|
|
break;
|
|
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
iStateId = TESTFLAG(pmii->fState, MFS_DISABLED) ? CBS_DISABLED : CBS_NORMAL;
|
|
|
|
if( NULL == pSize )
|
|
{
|
|
SIZE size;
|
|
size.cx = NcGetSystemMetrics(SM_CXMENUSIZE);
|
|
size.cy = NcGetSystemMetrics(SM_CYMENUSIZE);
|
|
pSize = &size;
|
|
}
|
|
|
|
return _CreateBackgroundBitmap( hdc, hTheme, iPartId, iStateId, pSize, &pmii->hbmpItem );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
HRESULT _CreateBackgroundBitmap(
|
|
IN OPTIONAL HDC hdcCompatible,
|
|
IN HTHEME hTheme,
|
|
IN int iPartId,
|
|
IN int iStateId,
|
|
IN OUT LPSIZE pSize, // in: if cx <= 0 || cx <=0, assume truesize. out: background size
|
|
OUT HBITMAP* phbmOut )
|
|
{
|
|
ASSERT(hdcCompatible);
|
|
ASSERT(hTheme);
|
|
ASSERT(pSize);
|
|
ASSERT(phbmOut);
|
|
|
|
HRESULT hr = E_FAIL;
|
|
SIZE size;
|
|
|
|
*phbmOut = NULL;
|
|
size = *pSize;
|
|
pSize->cx = pSize->cy = 0;
|
|
|
|
// Create working DC.
|
|
HDC hdcMem = CreateCompatibleDC(hdcCompatible);
|
|
|
|
if( hdcMem != NULL )
|
|
{
|
|
// determine output size;
|
|
hr = (size.cx <= 0 || size.cy <= 0) ?
|
|
GetThemePartSize( hTheme, hdcCompatible, iPartId, iStateId, NULL, TS_TRUE, &size ) : S_OK;
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
HBITMAP hbmOut = CreateCompatibleBitmap( hdcCompatible, size.cx, size.cy );
|
|
if( hbmOut )
|
|
{
|
|
HBITMAP hbm0 = (HBITMAP)SelectObject(hdcMem, hbmOut);
|
|
RECT rcBkgnd;
|
|
SetRect( &rcBkgnd, 0, 0, size.cx, size.cy );
|
|
hr = NcDrawThemeBackground( hTheme, hdcMem, iPartId, iStateId, &rcBkgnd, 0 );
|
|
SelectObject( hdcMem, hbm0 );
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
*phbmOut = hbmOut;
|
|
pSize->cx = size.cx;
|
|
pSize->cy = size.cy;
|
|
}
|
|
else
|
|
{
|
|
SAFE_DELETE_GDIOBJ(hbmOut);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
DeleteDC(hdcMem);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// _ComputeElementOffset() - calculates specified offset for caption or button
|
|
// This could be generalized in the themeapi, yes? [scotthan]
|
|
void _ComputeElementOffset(
|
|
HTHEME hTheme,
|
|
int iPartId,
|
|
int iStateId,
|
|
LPCRECT prcBase,
|
|
POINT *pptOffset)
|
|
{
|
|
OFFSETTYPE eOffsetType;
|
|
if (FAILED(GetThemeEnumValue(hTheme, iPartId, iStateId, TMT_OFFSETTYPE, (int *)&eOffsetType)))
|
|
eOffsetType = OT_TOPLEFT; // default value
|
|
|
|
POINT ptOffset;
|
|
if (FAILED(GetThemePosition(hTheme, iPartId, iStateId, TMT_OFFSET, &ptOffset)))
|
|
{
|
|
ptOffset.x = 0;
|
|
ptOffset.y = 0;
|
|
}
|
|
|
|
RECT rcBase = *prcBase;
|
|
|
|
switch (eOffsetType)
|
|
{
|
|
case OT_TOPLEFT:
|
|
ptOffset.x += rcBase.left;
|
|
ptOffset.y += rcBase.top;
|
|
break;
|
|
|
|
case OT_TOPRIGHT:
|
|
ptOffset.x += rcBase.right;
|
|
ptOffset.y += rcBase.top;
|
|
break;
|
|
|
|
case OT_TOPMIDDLE:
|
|
ptOffset.x += (rcBase.left + rcBase.right)/2;
|
|
ptOffset.y += rcBase.top;
|
|
break;
|
|
|
|
case OT_BOTTOMLEFT:
|
|
ptOffset.x += rcBase.left;
|
|
ptOffset.y += rcBase.bottom;
|
|
break;
|
|
|
|
case OT_BOTTOMRIGHT:
|
|
ptOffset.x += rcBase.right;
|
|
ptOffset.y += rcBase.bottom;
|
|
break;
|
|
|
|
case OT_BOTTOMMIDDLE:
|
|
ptOffset.x += (rcBase.left + rcBase.right)/2;
|
|
ptOffset.y += rcBase.bottom;
|
|
break;
|
|
|
|
// Todo: handle the remaining cases:
|
|
case OT_LEFTOFCAPTION:
|
|
case OT_RIGHTOFCAPTION:
|
|
case OT_LEFTOFLASTBUTTON:
|
|
case OT_RIGHTOFLASTBUTTON:
|
|
case OT_ABOVELASTBUTTON:
|
|
case OT_BELOWLASTBUTTON:
|
|
ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
*pptOffset = ptOffset;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// _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 );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
HTHEME GetCurrentNcThemeHandle()
|
|
{
|
|
return _nctmCurrent.hTheme;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
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 = 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 ) )
|
|
{
|
|
#ifdef THEMED_NCBTNMETRICS
|
|
_GetClassicNcBtnMetrics( NULL, NULL, FALSE, TRUE );
|
|
#endif THEMED_NCBTNMETRICS
|
|
|
|
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; // serializes access to _rgtwNcPaint
|
|
|
|
//-------------------------------------------------------------------------//
|
|
void NcPaintWindow_Add( HWND hwnd )
|
|
{
|
|
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()
|
|
{
|
|
if( _cNcPaintWnd )
|
|
{
|
|
DWORD dwThread = GetCurrentThreadId();
|
|
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;
|
|
|
|
if( _cNcPaintWnd )
|
|
{
|
|
DWORD dwThread = GetCurrentThreadId();
|
|
|
|
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),
|
|
_fCritSectsInit(FALSE),
|
|
_dwRevokeFlags(0),
|
|
_cLockRedraw(0),
|
|
_cNcPaint(0),
|
|
_cNcThemePaint(0),
|
|
_htHot(HTERROR),
|
|
_fProcessedEraseBk(0),
|
|
#ifdef LAME_BUTTON
|
|
_hFontLame(NULL),
|
|
#endif // LAME_BUTTON
|
|
#ifdef DEBUG_THEMEWND_DESTRUCTOR
|
|
_fDestructed(FALSE),
|
|
_fDeleteCritsec(FALSE),
|
|
#endif DEBUG_THEMEWND_DESTRUCTOR
|
|
_cRef(1)
|
|
{
|
|
InterlockedIncrement( &_cObj );
|
|
|
|
// set object validation signature tags
|
|
strcpy(_szHead, SIG_CTHEMEWND_HEAD);
|
|
strcpy(_szTail, SIG_CTHEMEWND_TAIL);
|
|
|
|
// cached subregion arrays
|
|
ZeroMemory( _rghrgnParts, sizeof(_rghrgnParts) );
|
|
ZeroMemory( _rghrgnSizingTemplates, sizeof(_rghrgnSizingTemplates) );
|
|
|
|
// initialize add'l structures.
|
|
InitWindowMetrics();
|
|
FillMemory(&_sizeRgn, sizeof(_sizeRgn), 0xFF);
|
|
|
|
#ifdef DEBUG
|
|
*_szCaption = *_szWndClass = 0;
|
|
#endif DEBUG
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
CThemeWnd::~CThemeWnd()
|
|
{
|
|
_CloseTheme();
|
|
_FreeRegionHandles();
|
|
UnloadMdiBtns();
|
|
ClearLameResources();
|
|
|
|
if( _fCritSectsInit )
|
|
{
|
|
DeleteCriticalSection( &_cswm );
|
|
#ifdef DEBUG_THEMEWND_DESTRUCTOR
|
|
_fDeleteCritsec = TRUE;
|
|
#endif DEBUG_THEMEWND_DESTRUCTOR
|
|
}
|
|
InterlockedDecrement( &_cObj );
|
|
|
|
#ifdef DEBUG_THEMEWND_DESTRUCTOR
|
|
_fDestructed = TRUE; // destructor has been called.
|
|
#endif DEBUG_THEMEWND_DESTRUCTOR
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
void CThemeWnd::_CloseTheme()
|
|
{
|
|
if( _hTheme )
|
|
{
|
|
CloseThemeData(_hTheme);
|
|
_hTheme = NULL;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
LONG CThemeWnd::AddRef()
|
|
{
|
|
return InterlockedIncrement( &_cRef );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
LONG CThemeWnd::Release()
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
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( _fCritSectsInit || NT_SUCCESS(RtlInitializeCriticalSection( &_cswm )) )
|
|
{
|
|
Log(LOG_NCATTACH, L"_AttachInstance: Nonclient attached to hwnd=0x%x", hwnd);
|
|
|
|
_fCritSectsInit = TRUE;
|
|
_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(_fCritSectsInit);
|
|
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);
|
|
|
|
#ifdef THEMED_NCBTNMETRICS
|
|
_GetClassicNcBtnMetrics( &_ncwm, _hAppIcon, _MNCanClose(_hwnd), FALSE );
|
|
#else THEMED_NCBTNMETRICS
|
|
_GetNcBtnMetrics( &_ncwm, &nctm, _hAppIcon, _MNCanClose(_hwnd) );
|
|
#endif THEMED_NCBTNMETRICS
|
|
|
|
// 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;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
void CThemeWnd::ReleaseNcWindowMetrics( IN NCWNDMET* pncwm )
|
|
{
|
|
LeaveCriticalSection( &_cswm );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
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;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
void _CopyInflateRect( LPRECT prcDst, LPCRECT prcSrc, int cx, int cy)
|
|
{
|
|
prcDst->left = prcSrc->left - cx;
|
|
prcDst->right = prcSrc->right + cx;
|
|
prcDst->top = prcSrc->top - cy;
|
|
prcDst->bottom = prcSrc->bottom + cy;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// 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 _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;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// 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;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------//
|
|
// 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.
|
|
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;
|
|
|
|
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 )
|
|
{
|
|
EnterCriticalSection( &_csNcSysMet );
|
|
|
|
HFONT 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) )
|
|
{
|
|
lstrcpy(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];
|
|
wvsprintf(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];
|
|
|
|
wsprintf( szInfo, TEXT("%08lX -'%s' ('%s') cf: %08lX"), _hwnd, _szCaption, _szWndClass, _fClassFlags );
|
|
wsprintf( 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];
|
|
|
|
wsprintfW( 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];
|
|
|
|
wsprintfW( 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
|