2018-10-14 22:56:00 -05:00
// 9 october 2018
# define UNICODE
# define _UNICODE
# define STRICT
# define STRICT_TYPED_ITEMIDS
# define WINVER 0x0600 /* from Microsoft's winnls.h */
# define _WIN32_WINNT 0x0600
# define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */
# define _WIN32_IE 0x0700
# define NTDDI_VERSION 0x06000000
# include <windows.h>
# include <commctrl.h>
# include <uxtheme.h>
# include <vsstyle.h>
# include <vssym32.h>
# include <windowsx.h>
# include <stdio.h>
# include <stdlib.h>
2019-09-15 09:52:15 -05:00
// cl winbuttonexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib _res\windows.res
2018-10-14 22:56:00 -05:00
void diele ( const char * func )
{
DWORD le ;
le = GetLastError ( ) ;
fprintf ( stderr , " %s: %I32u \n " , func , le ) ;
exit ( EXIT_FAILURE ) ;
}
void diehr ( const char * func , HRESULT hr )
{
fprintf ( stderr , " %s: 0x%08I32X \n " , func , hr ) ;
exit ( EXIT_FAILURE ) ;
}
2018-10-29 20:11:16 -05:00
// TODO if we merge this into libui proper, this will need to be deduplicated
static inline HRESULT lastErrorToHRESULT ( DWORD lastError )
{
if ( lastError = = 0 )
return E_FAIL ;
return HRESULT_FROM_WIN32 ( lastError ) ;
}
2018-10-14 22:56:00 -05:00
HINSTANCE hInstance ;
HWND leftButtons [ 5 ] ;
HWND rightButtons [ 3 ] ;
2018-10-29 20:11:16 -05:00
class commandModuleStyleParams {
public :
virtual int partID_CMOD_MODULEBACKGROUND ( void ) const = 0 ;
virtual int partID_CMOD_TASKBUTTON ( void ) const = 0 ;
virtual int partID_CMOD_SPLITBUTTONLEFT ( void ) const = 0 ;
virtual int partID_CMOD_SPLITBUTTONRIGHT ( void ) const = 0 ;
virtual int partID_CMOD_MENUGLYPH ( void ) const = 0 ;
virtual int partID_CMOD_OVERFLOWGLYPH ( void ) const = 0 ;
virtual int stateID_CMODS_NORMAL ( void ) const = 0 ;
virtual int stateID_CMODS_HOT ( void ) const = 0 ;
virtual int stateID_CMODS_PRESSED ( void ) const = 0 ;
virtual int stateID_CMODS_KEYFOCUSED ( void ) const = 0 ;
virtual int stateID_CMODS_NEARHOT ( void ) const = 0 ;
virtual HRESULT backgroundGradientColors ( HTHEME theme , COLORREF * colors ) const = 0 ;
2018-11-01 09:50:10 -05:00
virtual HRESULT buttonTextColor ( HTHEME theme , UINT uItemState , COLORREF * color ) const = 0 ;
virtual BOOL buttonTextShadowed ( UINT uItemState ) const = 0 ;
2018-10-30 22:34:06 -05:00
2018-11-04 21:19:44 -06:00
virtual int folderBarMarginsLeftDIP ( void ) const = 0 ;
virtual int folderBarMarginsTopDIP ( void ) const = 0 ;
virtual int folderBarMarginsRightDIP ( void ) const = 0 ;
virtual int folderBarMarginsBottomDIP ( void ) const = 0 ;
2018-10-30 22:34:06 -05:00
virtual int buttonMarginsXDIP ( void ) const = 0 ;
virtual int buttonMarginsYDIP ( void ) const = 0 ;
virtual int buttonTextArrowSeparationXDIP ( void ) const = 0 ;
2018-10-29 20:11:16 -05:00
} ;
2018-10-28 17:17:37 -05:00
2018-10-29 20:11:16 -05:00
class commandModuleStyleParamsVista : public commandModuleStyleParams {
public :
virtual int partID_CMOD_MODULEBACKGROUND ( void ) const { return 1 ; }
virtual int partID_CMOD_TASKBUTTON ( void ) const { return 2 ; }
virtual int partID_CMOD_SPLITBUTTONLEFT ( void ) const { return 3 ; }
virtual int partID_CMOD_SPLITBUTTONRIGHT ( void ) const { return 4 ; }
virtual int partID_CMOD_MENUGLYPH ( void ) const { return 5 ; }
virtual int partID_CMOD_OVERFLOWGLYPH ( void ) const { return 6 ; }
virtual int stateID_CMODS_NORMAL ( void ) const { return 1 ; }
virtual int stateID_CMODS_HOT ( void ) const { return 2 ; }
virtual int stateID_CMODS_PRESSED ( void ) const { return 3 ; }
virtual int stateID_CMODS_KEYFOCUSED ( void ) const { return 4 ; }
virtual int stateID_CMODS_NEARHOT ( void ) const { return 5 ; }
virtual HRESULT backgroundGradientColors ( HTHEME theme , COLORREF * colors ) const
{
if ( colors = = NULL )
return E_POINTER ;
# define argb(a, r, g, b) ((((COLORREF) ((BYTE) (a))) << 24) | RGB(r, g, b))
colors [ 0 ] = argb ( 255 , 4 , 80 , 130 ) ;
colors [ 1 ] = argb ( 255 , 17 , 101 , 132 ) ;
colors [ 2 ] = argb ( 255 , 29 , 121 , 134 ) ;
2018-11-01 09:50:10 -05:00
return S_OK ;
}
virtual HRESULT buttonTextColor ( HTHEME theme , UINT uItemState , COLORREF * color ) const
{
if ( color = = NULL )
return E_POINTER ;
* color = GetSysColor ( COLOR_WINDOW ) ;
if ( ( uItemState & CDIS_DISABLED ) ! = 0 )
* color = argb ( 255 , 161 , 204 , 210 ) ;
2018-10-29 20:11:16 -05:00
# undef argb
return S_OK ;
2018-10-28 21:03:15 -05:00
}
2018-10-30 22:34:06 -05:00
2018-11-01 09:50:10 -05:00
virtual BOOL buttonTextShadowed ( UINT uItemState ) const
{
return ( uItemState & CDIS_DISABLED ) = = 0 ;
}
2018-11-04 21:19:44 -06:00
virtual int folderBarMarginsLeftDIP ( void ) const { return 3 ; }
virtual int folderBarMarginsTopDIP ( void ) const { return 2 ; }
virtual int folderBarMarginsRightDIP ( void ) const { return 3 ; }
virtual int folderBarMarginsBottomDIP ( void ) const { return 3 ; }
2018-10-30 22:34:06 -05:00
virtual int buttonMarginsXDIP ( void ) const { return 6 ; }
virtual int buttonMarginsYDIP ( void ) const { return 5 ; }
virtual int buttonTextArrowSeparationXDIP ( void ) const { return 3 ; }
2018-10-29 20:11:16 -05:00
} ;
2018-10-28 17:17:37 -05:00
2018-10-29 20:11:16 -05:00
class commandModuleStyleParams7 : public commandModuleStyleParams {
virtual int partID_CMOD_MODULEBACKGROUND ( void ) const { return 1 ; }
virtual int partID_CMOD_TASKBUTTON ( void ) const { return 3 ; }
virtual int partID_CMOD_SPLITBUTTONLEFT ( void ) const { return 4 ; }
virtual int partID_CMOD_SPLITBUTTONRIGHT ( void ) const { return 5 ; }
virtual int partID_CMOD_MENUGLYPH ( void ) const { return 6 ; }
virtual int partID_CMOD_OVERFLOWGLYPH ( void ) const { return 7 ; }
virtual int stateID_CMODS_NORMAL ( void ) const { return 1 ; }
virtual int stateID_CMODS_HOT ( void ) const { return 2 ; }
virtual int stateID_CMODS_PRESSED ( void ) const { return 3 ; }
virtual int stateID_CMODS_KEYFOCUSED ( void ) const { return 4 ; }
/*TODO verify this*/ virtual int stateID_CMODS_NEARHOT ( void ) const { return 5 ; }
virtual HRESULT backgroundGradientColors ( HTHEME theme , COLORREF * colors ) const
{
HRESULT hr ;
if ( colors = = NULL )
return E_POINTER ;
hr = GetThemeColor ( theme ,
2 , 0 ,
2018-11-02 22:23:35 -05:00
TMT_GRADIENTCOLOR1 , colors + 0 ) ;
2018-10-29 20:11:16 -05:00
if ( hr ! = S_OK )
return hr ;
hr = GetThemeColor ( theme ,
2 , 0 ,
2018-11-02 22:23:35 -05:00
TMT_GRADIENTCOLOR2 , colors + 1 ) ;
2018-10-29 20:11:16 -05:00
if ( hr ! = S_OK )
return hr ;
return GetThemeColor ( theme ,
2 , 0 ,
2018-11-02 22:23:35 -05:00
TMT_GRADIENTCOLOR3 , colors + 2 ) ;
2018-10-28 21:03:15 -05:00
}
2018-10-30 22:34:06 -05:00
2018-11-01 09:50:10 -05:00
virtual HRESULT buttonTextColor ( HTHEME theme , UINT uItemState , COLORREF * color ) const
{
int state ;
if ( color = = NULL )
return E_POINTER ;
state = 1 ;
if ( ( uItemState & CDIS_DISABLED ) ! = 0 )
state = 6 ;
// TODO check if 3 is the correct part ID for all button types
return GetThemeColor ( theme ,
3 , state ,
TMT_TEXTCOLOR , color ) ;
}
virtual BOOL buttonTextShadowed ( UINT uItemState ) const
{
return FALSE ;
}
2018-11-04 21:19:44 -06:00
virtual int folderBarMarginsLeftDIP ( void ) const { return 3 ; }
virtual int folderBarMarginsTopDIP ( void ) const { return 2 ; }
virtual int folderBarMarginsRightDIP ( void ) const { return 9 ; }
virtual int folderBarMarginsBottomDIP ( void ) const { return 3 ; }
2018-10-30 22:34:06 -05:00
virtual int buttonMarginsXDIP ( void ) const { return 13 ; }
virtual int buttonMarginsYDIP ( void ) const { return 5 ; }
virtual int buttonTextArrowSeparationXDIP ( void ) const { return 1 ; }
2018-10-29 20:11:16 -05:00
} ;
2018-10-26 22:23:08 -05:00
// all coordinates are in client space
struct buttonMetrics {
SIZE fittingSize ;
int baseX ;
int baseY ;
int dpiX ;
int dpiY ;
BOOL hasText ;
SIZE textSize ;
BOOL hasArrow ;
SIZE arrowSize ;
} ;
2018-11-01 09:50:10 -05:00
struct buttonRects {
RECT clientRect ;
RECT textRect ;
RECT arrowRect ;
} ;
2018-10-29 20:11:16 -05:00
class commandModuleStyle {
public :
virtual HRESULT drawFolderBar ( commandModuleStyleParams * p , HDC dc , RECT * rcWindow , RECT * rcPaint ) const = 0 ;
virtual HRESULT buttonMetrics ( commandModuleStyleParams * p , HWND button , HDC dc , struct buttonMetrics * m ) const = 0 ;
2018-11-01 09:50:10 -05:00
virtual HRESULT buttonRects ( commandModuleStyleParams * p , HWND button , struct buttonMetrics * m , struct buttonRects * r ) const = 0 ;
2018-11-02 22:23:35 -05:00
virtual HRESULT drawButton ( commandModuleStyleParams * p , HWND button , HDC dc , UINT uItemState , RECT * rcPaint ) const = 0 ;
2018-10-29 20:11:16 -05:00
} ;
class commandModuleStyleThemed : public commandModuleStyle {
HTHEME theme ;
2018-10-30 22:34:06 -05:00
HTHEME textstyleTheme ;
2018-10-29 20:11:16 -05:00
public :
2018-10-30 22:34:06 -05:00
commandModuleStyleThemed ( HTHEME theme , HTHEME textstyleTheme )
{
this - > theme = theme ;
this - > textstyleTheme = textstyleTheme ;
}
2018-10-29 20:11:16 -05:00
virtual HRESULT drawFolderBar ( commandModuleStyleParams * p , HDC dc , RECT * rcWindow , RECT * rcPaint ) const
{
COLORREF colors [ 3 ] ;
TRIVERTEX vertices [ 4 ] ;
static GRADIENT_RECT gr [ 2 ] = {
{ 0 , 1 } ,
{ 2 , 3 } ,
} ;
HRESULT hr ;
hr = p - > backgroundGradientColors ( this - > theme , colors ) ;
if ( hr ! = S_OK )
return hr ;
vertices [ 0 ] . x = rcWindow - > left ;
vertices [ 0 ] . y = rcWindow - > top ;
vertices [ 0 ] . Red = ( ( COLOR16 ) GetRValue ( colors [ 0 ] ) ) < < 8 ;
vertices [ 0 ] . Green = ( ( COLOR16 ) GetGValue ( colors [ 0 ] ) ) < < 8 ;
vertices [ 0 ] . Blue = ( ( COLOR16 ) GetBValue ( colors [ 0 ] ) ) < < 8 ;
vertices [ 0 ] . Alpha = ( ( COLOR16 ) LOBYTE ( colors [ 0 ] > > 24 ) ) < < 8 ;
vertices [ 1 ] . x = ( rcWindow - > right - rcWindow - > left ) / 2 ;
vertices [ 1 ] . y = rcWindow - > bottom ;
vertices [ 1 ] . Red = ( ( COLOR16 ) GetRValue ( colors [ 1 ] ) ) < < 8 ;
vertices [ 1 ] . Green = ( ( COLOR16 ) GetGValue ( colors [ 1 ] ) ) < < 8 ;
vertices [ 1 ] . Blue = ( ( COLOR16 ) GetBValue ( colors [ 1 ] ) ) < < 8 ;
vertices [ 1 ] . Alpha = ( ( COLOR16 ) LOBYTE ( colors [ 1 ] > > 24 ) ) < < 8 ;
vertices [ 2 ] = vertices [ 1 ] ;
vertices [ 2 ] . y = rcWindow - > top ;
vertices [ 3 ] . x = rcWindow - > right ;
vertices [ 3 ] . y = rcWindow - > bottom ;
vertices [ 3 ] . Red = ( ( COLOR16 ) GetRValue ( colors [ 2 ] ) ) < < 8 ;
vertices [ 3 ] . Green = ( ( COLOR16 ) GetGValue ( colors [ 2 ] ) ) < < 8 ;
vertices [ 3 ] . Blue = ( ( COLOR16 ) GetBValue ( colors [ 2 ] ) ) < < 8 ;
vertices [ 3 ] . Alpha = ( ( COLOR16 ) LOBYTE ( colors [ 2 ] > > 24 ) ) < < 8 ;
if ( GradientFill ( dc , vertices , 4 , ( PVOID ) gr , 2 , GRADIENT_FILL_RECT_H ) = = FALSE )
return lastErrorToHRESULT ( GetLastError ( ) ) ;
return DrawThemeBackground ( this - > theme , dc ,
p - > partID_CMOD_MODULEBACKGROUND ( ) , 0 ,
rcWindow , rcPaint ) ;
}
# define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2])
2018-10-26 22:23:08 -05:00
# define dlgUnitsToX(dlg, baseX) MulDiv((dlg), (baseX), 4)
# define dlgUnitsToY(dlg, baseY) MulDiv((dlg), (baseY), 8)
// TODO verify the parameter order
# define dipsToX(dip, dpiX) MulDiv((dip), (dpiX), 96)
# define dipsToY(dip, dpiY) MulDiv((dip), (dpiY), 96)
2018-10-29 20:11:16 -05:00
// TODO for win7: the sizes are correct (according to UI Automation) but they don't visually match?
virtual HRESULT buttonMetrics ( commandModuleStyleParams * p , HWND button , HDC dc , struct buttonMetrics * m ) const
{
BOOL releaseDC ;
TEXTMETRICW tm ;
RECT r ;
int minStdButtonHeight ;
HRESULT hr ;
2018-11-01 09:50:10 -05:00
if ( m = = NULL )
return E_POINTER ;
2018-10-29 20:11:16 -05:00
releaseDC = FALSE ;
if ( dc = = NULL ) {
dc = GetDC ( button ) ;
if ( dc = = NULL )
return lastErrorToHRESULT ( GetLastError ( ) ) ;
releaseDC = TRUE ;
}
2018-10-26 22:23:08 -05:00
2018-10-29 20:11:16 -05:00
ZeroMemory ( & tm , sizeof ( TEXTMETRICW ) ) ;
2018-10-30 22:34:06 -05:00
hr = GetThemeTextMetrics ( this - > textstyleTheme , dc ,
TEXT_BODYTEXT , 0 ,
2018-10-29 20:11:16 -05:00
& tm ) ;
2018-10-30 22:34:06 -05:00
if ( hr ! = S_OK )
goto fail ;
hr = GetThemeTextExtent ( this - > textstyleTheme , dc ,
TEXT_BODYTEXT , 0 ,
2018-10-29 20:11:16 -05:00
L " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz " , 52 , 0 ,
2018-10-26 22:23:08 -05:00
NULL , & r ) ;
2018-10-30 22:34:06 -05:00
if ( hr ! = S_OK )
goto fail ;
2018-10-29 20:11:16 -05:00
m - > baseX = ( int ) ( ( ( r . right - r . left ) / 26 + 1 ) / 2 ) ;
m - > baseY = ( int ) tm . tmHeight ;
m - > dpiX = GetDeviceCaps ( dc , LOGPIXELSX ) ;
m - > dpiY = GetDeviceCaps ( dc , LOGPIXELSY ) ;
2018-10-26 22:23:08 -05:00
2018-10-29 20:11:16 -05:00
m - > fittingSize . cx = 0 ;
m - > fittingSize . cy = 0 ;
2018-10-26 22:23:08 -05:00
2018-10-29 20:11:16 -05:00
m - > hasText = TRUE ;
if ( m - > hasText ) {
LRESULT n ;
WCHAR * buf ;
LOGFONTW lf ;
n = SendMessageW ( button , WM_GETTEXTLENGTH , 0 , 0 ) ;
buf = new WCHAR [ n + 1 ] ;
GetWindowTextW ( button , buf , n + 1 ) ;
2018-10-30 22:34:06 -05:00
hr = GetThemeTextExtent ( this - > textstyleTheme , dc ,
TEXT_BODYTEXT , 0 ,
2018-10-29 20:11:16 -05:00
buf , n , DT_CENTER ,
NULL , & r ) ;
delete [ ] buf ;
if ( hr ! = S_OK )
goto fail ;
m - > textSize . cx = r . right - r . left ;
m - > textSize . cy = r . bottom - r . top ;
m - > fittingSize . cx + = m - > textSize . cx ;
m - > fittingSize . cy + = m - > textSize . cy ;
// dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for...
// and yes, it seems to be using the raw lfHeight value here :/
// TODO find the right TMT constant
2018-10-30 22:34:06 -05:00
hr = GetThemeFont ( this - > textstyleTheme , dc ,
2018-10-29 20:11:16 -05:00
4 , 0 ,
TMT_FONT , & lf ) ;
if ( hr ! = S_OK )
goto fail ;
m - > fittingSize . cx + = 2 * ( abs ( lf . lfHeight ) / 6 ) ;
}
2018-10-26 22:23:08 -05:00
2018-10-29 20:11:16 -05:00
m - > hasArrow = hasNonsplitArrow ( button ) ;
if ( m - > hasArrow ) {
// TS_MIN returns 1x1 and TS_DRAW returns 0x0, so...
hr = GetThemePartSize ( theme , dc ,
2018-10-30 22:34:06 -05:00
p - > partID_CMOD_MENUGLYPH ( ) , 0 ,
2018-10-29 20:11:16 -05:00
NULL , TS_TRUE , & ( m - > arrowSize ) ) ;
if ( hr ! = S_OK )
goto fail ;
m - > fittingSize . cx + = m - > arrowSize . cx ;
// TODO I don't think dui70.dll takes this into consideration...
if ( m - > fittingSize . cy < m - > arrowSize . cy )
m - > fittingSize . cy = m - > arrowSize . cy ;
2018-10-30 22:34:06 -05:00
m - > fittingSize . cx + = dipsToX ( p - > buttonTextArrowSeparationXDIP ( ) , m - > dpiX ) ;
2018-10-29 20:11:16 -05:00
}
2018-10-26 22:23:08 -05:00
2018-10-30 22:34:06 -05:00
m - > fittingSize . cx + = dipsToX ( p - > buttonMarginsXDIP ( ) , m - > dpiX ) * 2 ;
m - > fittingSize . cy + = dipsToY ( p - > buttonMarginsYDIP ( ) , m - > dpiY ) * 2 ;
2018-10-29 20:11:16 -05:00
// and dui70.dll seems to do a variant of this but for text buttons only...
minStdButtonHeight = dlgUnitsToY ( 14 , m - > baseY ) ;
if ( m - > fittingSize . cy < minStdButtonHeight )
m - > fittingSize . cy = minStdButtonHeight ;
hr = S_OK ;
fail :
if ( releaseDC )
// TODO when migrating this to libui, this will need to be renamed to indicate we are intentionally ignoring errors
ReleaseDC ( button , dc ) ;
return hr ;
} ;
2018-10-26 22:23:08 -05:00
2018-11-01 09:50:10 -05:00
// TODO check errors
virtual HRESULT buttonRects ( commandModuleStyleParams * p , HWND button , struct buttonMetrics * m , struct buttonRects * r ) const
{
if ( r = = NULL )
return E_POINTER ;
2018-10-26 22:23:08 -05:00
2018-11-01 09:50:10 -05:00
GetClientRect ( button , & ( r - > clientRect ) ) ;
2018-10-26 22:23:08 -05:00
2018-11-01 09:50:10 -05:00
if ( m - > hasText )
r - > textRect = r - > clientRect ;
2018-10-26 22:23:08 -05:00
2018-11-01 09:50:10 -05:00
if ( m - > hasArrow ) {
r - > arrowRect = r - > clientRect ;
r - > arrowRect . left = r - > arrowRect . right ;
r - > arrowRect . left - = dipsToX ( p - > buttonMarginsXDIP ( ) , m - > dpiX ) ;
r - > arrowRect . right = r - > arrowRect . left ;
r - > arrowRect . left - = m - > arrowSize . cx ;
r - > arrowRect . top + = ( ( r - > arrowRect . bottom - r - > arrowRect . top ) - m - > arrowSize . cy ) / 2 ;
r - > arrowRect . bottom = r - > arrowRect . top + m - > arrowSize . cy ;
if ( m - > hasText ) {
r - > textRect . right = r - > arrowRect . left - dipsToX ( p - > buttonTextArrowSeparationXDIP ( ) , m - > dpiX ) ;
r - > textRect . right + = dipsToX ( p - > buttonMarginsXDIP ( ) , m - > dpiX ) ;
}
2018-10-26 22:23:08 -05:00
}
2018-11-01 09:50:10 -05:00
return S_OK ;
2018-10-26 22:23:08 -05:00
}
2018-11-02 22:23:35 -05:00
// TODO check errors fully
virtual HRESULT drawButton ( commandModuleStyleParams * p , HWND button , HDC dc , UINT uItemState , RECT * rcPaint ) const
{
struct buttonMetrics m ;
struct buttonRects r ;
int part , state ;
HRESULT hr ;
hr = this - > buttonMetrics ( p , button , dc , & m ) ;
if ( hr ! = S_OK )
return hr ;
hr = this - > buttonRects ( p , button , & m , & r ) ;
if ( hr ! = S_OK )
return hr ;
part = p - > partID_CMOD_TASKBUTTON ( ) ;
state = p - > stateID_CMODS_NORMAL ( ) ;
if ( ( uItemState & CDIS_FOCUS ) ! = 0 )
state = p - > stateID_CMODS_KEYFOCUSED ( ) ;
if ( ( uItemState & CDIS_HOT ) ! = 0 )
state = p - > stateID_CMODS_HOT ( ) ;
if ( ( uItemState & CDIS_SELECTED ) ! = 0 )
state = p - > stateID_CMODS_PRESSED ( ) ;
hr = DrawThemeParentBackground ( button , dc , rcPaint ) ;
if ( hr ! = S_OK )
return hr ;
hr = DrawThemeBackground ( this - > theme , dc ,
part , state ,
& ( r . clientRect ) , rcPaint ) ;
if ( hr ! = S_OK )
return hr ;
if ( m . hasText ) {
int textState ;
COLORREF textColor ;
LRESULT n ;
WCHAR * buf ;
DTTOPTS dttopts ;
hr = p - > buttonTextColor ( this - > theme , uItemState , & textColor ) ;
if ( hr ! = S_OK )
return hr ;
n = SendMessageW ( button , WM_GETTEXTLENGTH , 0 , 0 ) ;
buf = new WCHAR [ n + 1 ] ;
GetWindowTextW ( button , buf , n + 1 ) ;
ZeroMemory ( & dttopts , sizeof ( DTTOPTS ) ) ;
dttopts . dwSize = sizeof ( DTTOPTS ) ;
dttopts . dwFlags = DTT_TEXTCOLOR ;
dttopts . crText = textColor ;
hr = DrawThemeTextEx ( this - > textstyleTheme , dc ,
TEXT_BODYTEXT , 0 ,
buf , n , DT_CENTER | DT_VCENTER | DT_SINGLELINE ,
& ( r . textRect ) , & dttopts ) ;
delete [ ] buf ;
if ( hr ! = S_OK )
return hr ;
}
if ( m . hasArrow ) {
// TODO verify the state ID on all platforms
hr = DrawThemeBackground ( this - > theme , dc ,
p - > partID_CMOD_MENUGLYPH ( ) , 0 ,
& ( r . arrowRect ) , rcPaint ) ;
if ( hr ! = S_OK )
return hr ;
}
return S_OK ;
}
2018-11-01 09:50:10 -05:00
} ;
2018-10-26 22:23:08 -05:00
2018-11-01 09:50:10 -05:00
#if 0
2018-10-14 22:56:00 -05:00
// TODO check errors
void drawExplorerChevron ( HTHEME theme , HDC dc , HWND rebar , WPARAM band , RECT * rcPaint )
{
REBARBANDINFOW rbi ;
RECT r ;
int state ;
ZeroMemory ( & rbi , sizeof ( REBARBANDINFOW ) ) ;
rbi . cbSize = sizeof ( REBARBANDINFOW ) ;
rbi . fMask = RBBIM_CHILD | RBBIM_CHEVRONLOCATION | RBBIM_CHEVRONSTATE ;
SendMessageW ( rebar , RB_GETBANDINFOW , band , ( LPARAM ) ( & rbi ) ) ;
if ( ( rbi . uChevronState & STATE_SYSTEM_INVISIBLE ) ! = 0 )
return ;
state = 1 ;
// TODO check if this is correct
if ( ( rbi . uChevronState & STATE_SYSTEM_FOCUSED ) ! = 0 )
state = 4 ;
if ( ( rbi . uChevronState & STATE_SYSTEM_HOTTRACKED ) ! = 0 )
state = 2 ;
if ( ( rbi . uChevronState & STATE_SYSTEM_PRESSED ) ! = 0 )
state = 3 ;
r = rbi . rcChevronLocation ;
// TODO commctrl.h says this should be correct for the chevron rect, but it's not?
// MapWindowRect(rbi.hwndChild, rebar, &r);
DrawThemeBackground ( theme , dc ,
3 , state ,
& r , rcPaint ) ;
DrawThemeBackground ( theme , dc ,
7 , 1 ,
& r , rcPaint ) ;
}
2018-11-01 09:50:10 -05:00
# endif
2018-10-14 22:56:00 -05:00
2018-11-02 22:23:35 -05:00
HICON shieldIcon ;
HICON applicationIcon ;
HICON helpIcon ;
HIMAGELIST rightList ;
HTHEME theme = NULL ;
HTHEME textstyleTheme = NULL ;
const char * which = " 7 " ;
commandModuleStyle * cms = NULL ;
commandModuleStyleParams * cmsp = NULL ;
HIMAGELIST dropdownArrowList = NULL ;
static struct {
const WCHAR * text ;
BOOL dropdown ;
} leftbarButtons [ ] = {
{ L " Organize " , TRUE } ,
{ L " Include in library " , TRUE } ,
{ L " Share with " , TRUE } ,
{ L " Burn " , FALSE } ,
{ L " New folder " , FALSE } ,
} ;
2018-10-14 22:56:00 -05:00
// TODO check errors
2018-10-15 08:30:22 -05:00
LRESULT drawExplorerButton ( NMCUSTOMDRAW * nm )
2018-10-14 22:56:00 -05:00
{
2018-11-02 22:23:35 -05:00
HRESULT hr ;
2018-10-14 22:56:00 -05:00
2018-10-16 22:19:23 -05:00
if ( nm - > dwDrawStage ! = CDDS_PREPAINT )
return CDRF_DODEFAULT ;
2018-11-02 22:23:35 -05:00
hr = cms - > drawButton ( cmsp , nm - > hdr . hwndFrom ,
nm - > hdc , nm - > uItemState , & ( nm - > rc ) ) ;
if ( hr ! = S_OK )
return CDRF_DODEFAULT ;
2018-10-16 22:19:23 -05:00
return CDRF_SKIPDEFAULT ;
2018-10-14 22:56:00 -05:00
}
void onWM_CREATE ( HWND hwnd )
{
int buttonx , buttony ;
int i ;
buttonx = 5 ;
buttony = 5 ;
for ( i = 0 ; i < 5 ; i + + ) {
2018-10-15 00:54:15 -05:00
// TODO split buttons don't support arrow navigation?
2018-10-14 22:56:00 -05:00
leftButtons [ i ] = CreateWindowExW ( 0 ,
L " BUTTON " , leftbarButtons [ i ] . text ,
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON ,
buttonx , buttony ,
150 , 30 ,
hwnd , ( HMENU ) ( 100 + i ) , hInstance , NULL ) ;
if ( leftButtons [ i ] = = NULL )
diele ( " CreateWindowExW(L \" BUTTON \" ) " ) ;
buttonx + = 150 ;
}
}
// TODO check errors
void updateTheme ( HWND hwnd )
{
2018-11-02 22:23:35 -05:00
if ( cmsp ! = NULL ) {
delete cmsp ;
cmsp = NULL ;
2018-10-14 22:56:00 -05:00
}
2018-11-02 22:23:35 -05:00
if ( cms ! = NULL ) {
delete cms ;
cms = NULL ;
2018-10-14 22:56:00 -05:00
}
2018-10-16 10:25:22 -05:00
if ( textstyleTheme ! = NULL ) {
CloseThemeData ( textstyleTheme ) ;
textstyleTheme = NULL ;
}
2018-10-14 22:56:00 -05:00
if ( theme ! = NULL ) {
CloseThemeData ( theme ) ;
theme = NULL ;
}
theme = OpenThemeData ( hwnd , L " CommandModule " ) ;
2018-10-16 10:25:22 -05:00
textstyleTheme = OpenThemeData ( hwnd , L " TEXTSTYLE " ) ;
2018-11-02 22:23:35 -05:00
cms = new commandModuleStyleThemed ( theme , textstyleTheme ) ;
if ( strcmp ( which , " vista " ) = = 0 )
cmsp = new commandModuleStyleParamsVista ;
else
cmsp = new commandModuleStyleParams7 ;
2018-10-14 22:56:00 -05:00
}
void repositionButtons ( HWND hwnd )
{
HDWP dwp ;
int buttonx , buttony ;
2018-11-05 20:09:50 -06:00
HDC dc ;
int dpiX ;
int dpiY ;
2018-10-26 22:23:08 -05:00
struct buttonMetrics m ;
2018-10-14 22:56:00 -05:00
int i ;
2018-11-05 20:09:50 -06:00
dc = GetDC ( hwnd ) ;
if ( dc = = NULL )
diele ( " GetDC() " ) ;
dpiX = GetDeviceCaps ( dc , LOGPIXELSX ) ;
dpiY = GetDeviceCaps ( dc , LOGPIXELSY ) ;
// TODO check error
ReleaseDC ( hwnd , dc ) ;
2018-10-14 22:56:00 -05:00
dwp = BeginDeferWindowPos ( 5 ) ;
if ( dwp = = NULL )
diele ( " BeginDeferWindowPos() " ) ;
2018-11-05 20:09:50 -06:00
buttonx = dipsToX ( cmsp - > folderBarMarginsLeftDIP ( ) , dpiX ) ;
buttony = dipsToY ( cmsp - > folderBarMarginsTopDIP ( ) , dpiY ) ;
2018-10-14 22:56:00 -05:00
for ( i = 0 ; i < 5 ; i + + ) {
2018-11-02 22:23:35 -05:00
cms - > buttonMetrics ( cmsp , leftButtons [ i ] , NULL , & m ) ;
2018-10-14 22:56:00 -05:00
dwp = DeferWindowPos ( dwp , leftButtons [ i ] , NULL ,
2018-10-26 22:23:08 -05:00
buttonx , buttony , m . fittingSize . cx , m . fittingSize . cy ,
2018-10-14 22:56:00 -05:00
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER ) ;
if ( dwp = = NULL )
diele ( " DeferWindowPos() " ) ;
2018-10-26 22:23:08 -05:00
buttonx + = m . fittingSize . cx ;
2018-10-14 22:56:00 -05:00
}
if ( EndDeferWindowPos ( dwp ) = = 0 )
diele ( " EndDeferWindowPos() " ) ;
}
2018-11-05 20:09:50 -06:00
// TODO check errors
void folderBarRect ( HWND hwnd , HDC dc , RECT * r )
{
int dpiX ;
int dpiY ;
RECT child ;
int i ;
dpiX = GetDeviceCaps ( dc , LOGPIXELSX ) ;
dpiY = GetDeviceCaps ( dc , LOGPIXELSY ) ;
GetClientRect ( hwnd , r ) ;
r - > right - = r - > left ;
r - > left = 0 ;
r - > top = 0 ;
r - > bottom = 0 ;
for ( i = 0 ; i < 5 ; i + + ) {
GetWindowRect ( leftButtons [ i ] , & child ) ;
if ( r - > bottom < ( child . bottom - child . top ) )
r - > bottom = ( child . bottom - child . top ) ;
}
r - > bottom + = dipsToY ( cmsp - > folderBarMarginsTopDIP ( ) , dpiY ) ;
r - > bottom + = dipsToY ( cmsp - > folderBarMarginsBottomDIP ( ) , dpiY ) ;
}
2018-10-14 22:56:00 -05:00
#if 0
// TODO check errors
void handleEvents ( HWND hwnd , WPARAM wParam )
{
LRESULT n ;
const WCHAR * selRebar = NULL , * selToolbar = NULL ;
WCHAR * bufRebar = NULL , * bufToolbar = NULL ;
BOOL changeRebar = FALSE , changeToolbar = FALSE ;
BOOL invalidate = FALSE ;
WPARAM check ;
switch ( wParam ) {
case MAKEWPARAM ( 300 , CBN_SELCHANGE ) :
n = SendMessageW ( rebarCombo , CB_GETCURSEL , 0 , 0 ) ;
selRebar = rebarThemes [ n ] ;
changeRebar = TRUE ;
break ;
case MAKEWPARAM ( 301 , CBN_SELCHANGE ) :
n = SendMessageW ( toolbarCombo , CB_GETCURSEL , 0 , 0 ) ;
selToolbar = toolbarThemes [ n ] ;
changeToolbar = TRUE ;
break ;
case MAKEWPARAM ( 200 , BN_CLICKED ) :
drawmode = 0 ;
n = SendMessageW ( rebarCombo , WM_GETTEXTLENGTH , 0 , 0 ) ;
bufRebar = new WCHAR [ n + 1 ] ;
GetWindowTextW ( rebarCombo , bufRebar , n + 1 ) ;
n = SendMessageW ( toolbarCombo , WM_GETTEXTLENGTH , 0 , 0 ) ;
bufToolbar = new WCHAR [ n + 1 ] ;
GetWindowTextW ( toolbarCombo , bufToolbar , n + 1 ) ;
selRebar = bufRebar ;
selToolbar = bufToolbar ;
changeRebar = TRUE ;
changeToolbar = TRUE ;
break ;
case MAKEWPARAM ( 201 , BN_CLICKED ) :
drawmode = 1 ;
invalidate = TRUE ;
break ;
case MAKEWPARAM ( 302 , BN_CLICKED ) :
ShowWindow ( leftbar , SW_HIDE ) ;
check = BST_CHECKED ;
if ( SendMessage ( toolbarTransparentCheckbox , BM_GETCHECK , 0 , 0 ) = = BST_CHECKED )
check = BST_UNCHECKED ;
SendMessage ( toolbarTransparentCheckbox , BM_SETCHECK , check , 0 ) ;
if ( check = = BST_CHECKED )
SendMessageW ( leftbar , TB_SETSTYLE , 0 , toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT ) ;
else
SendMessageW ( leftbar , TB_SETSTYLE , 0 , toolbarStyles | TBSTYLE_LIST ) ;
ShowWindow ( leftbar , SW_SHOW ) ;
break ;
case MAKEWPARAM ( 202 , BN_CLICKED ) :
SetFocus ( leftbar ) ;
break ;
}
if ( changeRebar ) {
if ( selRebar ! = NULL & & wcscmp ( selRebar , L " NULL " ) = = 0 )
selRebar = NULL ;
if ( selRebar ! = NULL & & * selRebar ! = L ' \0 ' )
SendMessageW ( rebar , RB_SETWINDOWTHEME , 0 , ( LPARAM ) selRebar ) ;
else
SetWindowTheme ( rebar , selRebar , selRebar ) ;
invalidate = TRUE ;
}
if ( changeToolbar ) {
if ( selToolbar ! = NULL & & wcscmp ( selToolbar , L " NULL " ) = = 0 )
selToolbar = NULL ;
if ( selToolbar ! = NULL & & * selToolbar ! = L ' \0 ' ) {
SendMessageW ( leftbar , TB_SETWINDOWTHEME , 0 , ( LPARAM ) selToolbar ) ;
SendMessageW ( rightbar , TB_SETWINDOWTHEME , 0 , ( LPARAM ) selToolbar ) ;
} else {
SetWindowTheme ( leftbar , selToolbar , selToolbar ) ;
SetWindowTheme ( rightbar , selToolbar , selToolbar ) ;
}
invalidate = TRUE ;
}
if ( invalidate )
InvalidateRect ( hwnd , NULL , TRUE ) ;
if ( bufRebar ! = NULL )
delete [ ] bufRebar ;
if ( bufToolbar ! = NULL )
delete [ ] bufToolbar ;
}
# endif
LRESULT CALLBACK wndproc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
2018-10-28 17:17:37 -05:00
HDC dc ;
PAINTSTRUCT ps ;
2018-10-14 22:56:00 -05:00
NMHDR * nm = ( NMHDR * ) lParam ;
2018-10-15 08:30:22 -05:00
int i ;
2018-10-14 22:56:00 -05:00
switch ( uMsg ) {
case WM_CREATE :
onWM_CREATE ( hwnd ) ;
updateTheme ( hwnd ) ;
repositionButtons ( hwnd ) ;
break ;
case WM_CLOSE :
PostQuitMessage ( 0 ) ;
break ;
case WM_SIZE :
repositionButtons ( hwnd ) ;
2018-10-28 17:17:37 -05:00
// TODO check errors
InvalidateRect ( hwnd , NULL , TRUE ) ;
2018-10-14 22:56:00 -05:00
break ;
case WM_THEMECHANGED :
updateTheme ( hwnd ) ;
repositionButtons ( hwnd ) ;
break ;
2018-10-28 17:17:37 -05:00
case WM_PAINT :
// TODO check errors
dc = BeginPaint ( hwnd , & ps ) ;
{ RECT w ;
2018-11-05 20:09:50 -06:00
folderBarRect ( hwnd , dc , & w ) ;
2018-11-02 22:23:35 -05:00
cms - > drawFolderBar ( cmsp , dc , & w , & ( ps . rcPaint ) ) ; }
2018-10-28 17:17:37 -05:00
EndPaint ( hwnd , & ps ) ;
return 0 ;
case WM_PRINTCLIENT :
2018-11-05 20:09:50 -06:00
{ RECT w , paint ;
folderBarRect ( hwnd , ( HDC ) wParam , & w ) ;
GetClientRect ( hwnd , & paint ) ;
2018-11-02 22:23:35 -05:00
cms - > drawFolderBar ( cmsp , ( HDC ) wParam , & w , & w ) ; }
2018-10-28 17:17:37 -05:00
return 0 ;
2018-10-14 22:56:00 -05:00
#if 0
case WM_COMMAND :
handleEvents ( hwnd , wParam ) ;
break ;
# endif
case WM_NOTIFY :
switch ( nm - > code ) {
case NM_CUSTOMDRAW :
2018-10-15 08:30:22 -05:00
for ( i = 0 ; i < 5 ; i + + )
if ( nm - > hwndFrom = = leftButtons [ i ] )
return drawExplorerButton ( ( NMCUSTOMDRAW * ) nm ) ;
2018-10-14 22:56:00 -05:00
break ;
}
break ;
}
return DefWindowProcW ( hwnd , uMsg , wParam , lParam ) ;
}
EXTERN_C IMAGE_DOS_HEADER __ImageBase ;
2018-11-02 22:23:35 -05:00
int main ( int argc , char * argv [ ] )
2018-10-14 22:56:00 -05:00
{
STARTUPINFOW si ;
int nCmdShow ;
INITCOMMONCONTROLSEX icc ;
HICON hDefaultIcon ;
HCURSOR hDefaultCursor ;
WNDCLASSW wc ;
HWND mainwin ;
MSG msg ;
HRESULT hr ;
2018-11-02 22:23:35 -05:00
if ( argc > 1 )
which = argv [ 1 ] ;
2018-10-14 22:56:00 -05:00
hInstance = ( HINSTANCE ) ( & __ImageBase ) ;
nCmdShow = SW_SHOWDEFAULT ;
GetStartupInfoW ( & si ) ;
if ( ( si . dwFlags & STARTF_USESHOWWINDOW ) ! = 0 )
nCmdShow = si . wShowWindow ;
ZeroMemory ( & icc , sizeof ( INITCOMMONCONTROLSEX ) ) ;
icc . dwSize = sizeof ( INITCOMMONCONTROLSEX ) ;
icc . dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES ;
if ( InitCommonControlsEx ( & icc ) = = 0 )
diele ( " InitCommonControlsEx() " ) ;
hDefaultIcon = LoadIconW ( NULL , IDI_APPLICATION ) ;
if ( hDefaultIcon = = NULL )
diele ( " LoadIconW(IDI_APPLICATION) " ) ;
hDefaultCursor = LoadCursorW ( NULL , IDC_ARROW ) ;
if ( hDefaultCursor = = NULL )
diele ( " LoadCursorW(IDC_ARROW) " ) ;
hr = LoadIconMetric ( NULL , IDI_SHIELD , LIM_SMALL , & shieldIcon ) ;
if ( hr ! = S_OK )
diehr ( " LoadIconMetric(IDI_SHIELD) " , hr ) ;
hr = LoadIconMetric ( NULL , IDI_APPLICATION , LIM_SMALL , & applicationIcon ) ;
if ( hr ! = S_OK )
diehr ( " LoadIconMetric(IDI_APPLICATION) " , hr ) ;
hr = LoadIconMetric ( NULL , IDI_QUESTION , LIM_SMALL , & helpIcon ) ;
if ( hr ! = S_OK )
diehr ( " LoadIconMetric(IDI_QUESTION) " , hr ) ;
rightList = ImageList_Create ( GetSystemMetrics ( SM_CXSMICON ) , GetSystemMetrics ( SM_CYSMICON ) ,
ILC_COLOR32 , 0 , 3 ) ;
if ( rightList = = NULL )
diele ( " ImageList_Create() " ) ;
if ( ImageList_ReplaceIcon ( rightList , - 1 , shieldIcon ) = = - 1 )
diele ( " ImageList_ReplaceIcon(IDI_SHIELD) " ) ;
if ( ImageList_ReplaceIcon ( rightList , - 1 , applicationIcon ) = = - 1 )
diele ( " ImageList_ReplaceIcon(IDI_APPLICATION) " ) ;
if ( ImageList_ReplaceIcon ( rightList , - 1 , helpIcon ) = = - 1 )
diele ( " ImageList_ReplaceIcon(IDI_QUESTION) " ) ;
ZeroMemory ( & wc , sizeof ( WNDCLASSW ) ) ;
wc . lpszClassName = L " mainwin " ;
wc . lpfnWndProc = wndproc ;
wc . hInstance = hInstance ;
wc . hIcon = hDefaultIcon ;
wc . hCursor = hDefaultCursor ;
wc . hbrBackground = ( HBRUSH ) ( COLOR_BTNFACE + 1 ) ;
if ( RegisterClassW ( & wc ) = = 0 )
diele ( " RegisterClassW() " ) ;
mainwin = CreateWindowExW ( 0 ,
L " mainwin " , L " Main Window " ,
WS_OVERLAPPEDWINDOW ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL ) ;
if ( mainwin = = NULL )
diele ( " CreateWindowExW(L \" mainwin \" ) " ) ;
ShowWindow ( mainwin , nCmdShow ) ;
if ( UpdateWindow ( mainwin ) = = 0 )
diele ( " UpdateWindow() " ) ;
for ( ; ; ) {
int res ;
res = GetMessageW ( & msg , NULL , 0 , 0 ) ;
if ( res < 0 )
diele ( " GetMessageW() " ) ;
if ( res = = 0 )
break ;
if ( IsDialogMessageW ( mainwin , & msg ) = = 0 ) {
TranslateMessage ( & msg ) ;
DispatchMessageW ( & msg ) ;
}
}
return 0 ;
}