2014-08-16 13:02:00 -05:00
// 16 august 2014
# include "winapi_windows.h"
# include "_cgo_export.h"
2014-08-17 18:28:32 -05:00
// TODO top left pixel of checkbox state 0 not drawn?
2014-08-17 12:34:06 -05:00
2014-08-16 13:02:00 -05:00
HBITMAP unscaledBitmap ( void * i , intptr_t dx , intptr_t dy )
{
BITMAPINFO bi ;
VOID * ppvBits ;
HBITMAP bitmap ;
ZeroMemory ( & bi , sizeof ( BITMAPINFO ) ) ;
bi . bmiHeader . biSize = sizeof ( BITMAPINFOHEADER ) ;
bi . bmiHeader . biWidth = ( LONG ) dx ;
bi . bmiHeader . biHeight = - ( ( LONG ) dy ) ; // negative height to force top-down drawing;
bi . bmiHeader . biPlanes = 1 ;
bi . bmiHeader . biBitCount = 32 ;
bi . bmiHeader . biCompression = BI_RGB ;
bi . bmiHeader . biSizeImage = ( DWORD ) ( dx * dy * 4 ) ;
bitmap = CreateDIBSection ( NULL , & bi , DIB_RGB_COLORS , & ppvBits , 0 , 0 ) ;
if ( bitmap = = NULL )
xpanic ( " error creating HBITMAP for unscaled ImageList image copy " , GetLastError ( ) ) ;
dotoARGB ( i , ( void * ) ppvBits ) ;
return bitmap ;
}
HIMAGELIST newImageList ( int width , int height )
{
HIMAGELIST il ;
2014-08-29 19:46:39 -05:00
// this handles alpha properly; see https://web.archive.org/web/20100512144953/http://msdn.microsoft.com/en-us/library/ms997646.aspx#xptheming_topic13 and http://stackoverflow.com/a/2640897/3408572
// TODO alpha-premultiplied? http://stackoverflow.com/a/641836/3408572 implies it is, http://trac.wxwidgets.org/ticket/9050#comment:1 implies it isn't
2014-08-16 13:02:00 -05:00
il = ( * fv_ImageList_Create ) ( width , height , ILC_COLOR32 , 20 , 20 ) ; // should be reasonable
if ( il = = NULL )
xpanic ( " error creating image list " , GetLastError ( ) ) ;
return il ;
}
2014-08-16 16:37:21 -05:00
void addImage ( HIMAGELIST il , HWND hwnd , HBITMAP bitmap , int origwid , int oright , int width , int height )
2014-08-16 13:02:00 -05:00
{
BOOL wasScaled = FALSE ;
2014-08-16 16:37:21 -05:00
HDC winDC , scaledDC , origDC ;
2014-08-16 13:02:00 -05:00
HBITMAP scaled ;
HBITMAP prevscaled , prevorig ;
// first we need to scale the bitmap
if ( origwid = = width & & oright = = height ) {
scaled = bitmap ;
goto noscale ;
}
wasScaled = TRUE ;
2014-08-16 16:37:21 -05:00
winDC = GetDC ( hwnd ) ;
if ( winDC = = NULL )
xpanic ( " error getting DC for window " , GetLastError ( ) ) ;
origDC = CreateCompatibleDC ( winDC ) ;
2014-08-17 12:34:06 -05:00
if ( origDC = = NULL )
2014-08-16 16:37:21 -05:00
xpanic ( " error getting DC for original ImageList bitmap " , GetLastError ( ) ) ;
prevorig = SelectObject ( origDC , bitmap ) ;
if ( prevorig = = NULL )
xpanic ( " error selecting original ImageList bitmap into DC " , GetLastError ( ) ) ;
scaledDC = CreateCompatibleDC ( origDC ) ;
2014-08-16 13:02:00 -05:00
if ( scaledDC = = NULL )
2014-08-16 16:37:21 -05:00
xpanic ( " error getting DC for scaled ImageList bitmap " , GetLastError ( ) ) ;
scaled = CreateCompatibleBitmap ( origDC , width , height ) ;
2014-08-16 13:02:00 -05:00
if ( scaled = = NULL )
xpanic ( " error creating scaled ImageList bitmap " , GetLastError ( ) ) ;
prevscaled = SelectObject ( scaledDC , scaled ) ;
if ( prevscaled = = NULL )
2014-08-16 16:37:21 -05:00
xpanic ( " error selecting scaled ImageList bitmap into DC " , GetLastError ( ) ) ;
2014-08-16 16:43:43 -05:00
if ( SetStretchBltMode ( scaledDC , COLORONCOLOR ) = = 0 )
xpanic ( " error setting scaling mode " , GetLastError ( ) ) ;
2014-08-16 13:02:00 -05:00
if ( StretchBlt ( scaledDC , 0 , 0 , width , height ,
origDC , 0 , 0 , origwid , oright ,
SRCCOPY ) = = 0 )
xpanic ( " error scaling ImageList bitmap down " , GetLastError ( ) ) ;
if ( SelectObject ( origDC , prevorig ) ! = bitmap )
2014-08-16 16:37:21 -05:00
xpanic ( " error selecting previous bitmap into original image's DC " , GetLastError ( ) ) ;
2014-08-16 13:02:00 -05:00
if ( DeleteDC ( origDC ) = = 0 )
2014-08-16 16:37:21 -05:00
xpanic ( " error deleting original image's DC " , GetLastError ( ) ) ;
2014-08-16 13:02:00 -05:00
if ( SelectObject ( scaledDC , prevscaled ) ! = scaled )
2014-08-16 16:37:21 -05:00
xpanic ( " error selecting previous bitmap into scaled image's DC " , GetLastError ( ) ) ;
2014-08-16 13:02:00 -05:00
if ( DeleteDC ( scaledDC ) = = 0 )
2014-08-16 16:37:21 -05:00
xpanic ( " error deleting scaled image's DC " , GetLastError ( ) ) ;
2014-08-17 12:34:06 -05:00
if ( ReleaseDC ( hwnd , winDC ) = = 0 )
2014-08-16 16:37:21 -05:00
xpanic ( " error deleting window DC " , GetLastError ( ) ) ;
2014-08-16 13:02:00 -05:00
noscale :
if ( ( * fv_ImageList_Add ) ( il , scaled , NULL ) = = - 1 )
xpanic ( " error adding ImageList image to image list " , GetLastError ( ) ) ;
if ( wasScaled ) // clean up
if ( DeleteObject ( scaled ) = = 0 )
xpanic ( " error deleting scaled bitmap " , GetLastError ( ) ) ;
}
2014-08-16 16:37:21 -05:00
2014-08-25 15:53:47 -05:00
void applyImageList ( HWND hwnd , UINT uMsg , WPARAM wParam , HIMAGELIST il , HIMAGELIST old )
2014-08-16 16:37:21 -05:00
{
2014-08-25 15:53:47 -05:00
if ( SendMessageW ( hwnd , uMsg , wParam , ( LPARAM ) il ) ! = ( LRESULT ) old )
xpanic ( " error setting image list " , GetLastError ( ) ) ;
if ( old ! = NULL & & ( * fv_ImageList_Destroy ) ( old ) = = 0 )
xpanic ( " error freeing old checkbox image list " , GetLastError ( ) ) ;
2014-08-16 16:37:21 -05:00
}
2014-08-17 12:34:06 -05:00
static UINT dfcState ( int cbstate )
{
UINT ret ;
ret = DFCS_BUTTONCHECK ;
if ( ( cbstate & checkboxStateChecked ) ! = 0 )
ret | = DFCS_CHECKED ;
if ( ( cbstate & checkboxStateHot ) ! = 0 )
ret | = DFCS_HOT ;
if ( ( cbstate & checkboxStatePushed ) ! = 0 )
ret | = DFCS_PUSHED ;
return ret ;
}
2014-08-25 13:04:20 -05:00
static void dfcImage ( HDC dc , RECT * r , int cbState , HTHEME theme )
2014-08-17 12:34:06 -05:00
{
2014-08-25 01:22:07 -05:00
if ( DrawFrameControl ( dc , r , DFC_BUTTON , dfcState ( cbState ) ) = = 0 )
2014-08-17 12:34:06 -05:00
xpanic ( " error drawing checkbox image " , GetLastError ( ) ) ;
}
2014-08-25 13:04:20 -05:00
static void dfcSize ( HDC dc , int * width , int * height , HTHEME theme )
2014-08-17 12:34:06 -05:00
{
// there's no real metric around
// let's use SM_CX/YSMICON and hope for the best
2014-08-25 01:22:07 -05:00
* width = GetSystemMetrics ( SM_CXSMICON ) ;
* height = GetSystemMetrics ( SM_CYSMICON ) ;
2014-08-17 12:34:06 -05:00
}
static int themestates [ checkboxnStates ] = {
CBS_UNCHECKEDNORMAL , // 0
CBS_CHECKEDNORMAL , // checked
CBS_UNCHECKEDHOT , // hot
CBS_CHECKEDHOT , // checked | hot
CBS_UNCHECKEDPRESSED , // pushed
CBS_CHECKEDPRESSED , // checked | pushed
CBS_UNCHECKEDPRESSED , // hot | pushed
CBS_CHECKEDPRESSED , // checked | hot | pushed
} ;
2014-08-25 13:04:20 -05:00
static SIZE getStateSize ( HDC dc , int cbState , HTHEME theme )
2014-08-17 12:34:06 -05:00
{
SIZE s ;
2014-08-25 00:49:11 -05:00
HRESULT res ;
2014-08-17 12:34:06 -05:00
2014-08-25 00:49:11 -05:00
res = GetThemePartSize ( theme , dc , BP_CHECKBOX , themestates [ cbState ] , NULL , TS_DRAW , & s ) ;
if ( res ! = S_OK )
xpanichresult ( " error getting theme part size " , res ) ;
2014-08-17 12:34:06 -05:00
return s ;
}
2014-08-25 13:04:20 -05:00
static void themeImage ( HDC dc , RECT * r , int cbState , HTHEME theme )
2014-08-25 01:22:07 -05:00
{
HRESULT res ;
res = DrawThemeBackground ( theme , dc , BP_CHECKBOX , themestates [ cbState ] , r , NULL ) ;
if ( res ! = S_OK )
xpanichresult ( " error drawing checkbox image " , res ) ;
}
2014-08-25 13:04:20 -05:00
static void themeSize ( HDC dc , int * width , int * height , HTHEME theme )
2014-08-25 01:22:07 -05:00
{
SIZE size ;
int cbState ;
2014-08-25 13:04:20 -05:00
size = getStateSize ( dc , 0 , theme ) ;
2014-08-25 01:22:07 -05:00
for ( cbState = 1 ; cbState < checkboxnStates ; cbState + + ) {
SIZE against ;
2014-08-25 13:04:20 -05:00
against = getStateSize ( dc , cbState , theme ) ;
2014-08-25 01:22:07 -05:00
if ( size . cx ! = against . cx | | size . cy ! = against . cy )
xpanic ( " size mismatch in checkbox states " , GetLastError ( ) ) ;
}
* width = ( int ) size . cx ;
* height = ( int ) size . cy ;
}
2014-08-25 13:04:20 -05:00
static HBITMAP makeCheckboxImageListEntry ( HDC dc , int width , int height , int cbState , void ( * drawfunc ) ( HDC , RECT * , int , HTHEME ) , HTHEME theme )
2014-08-17 12:34:06 -05:00
{
BITMAPINFO bi ;
VOID * ppvBits ;
HBITMAP bitmap ;
RECT r ;
HDC drawDC ;
HBITMAP prevbitmap ;
r . left = 0 ;
r . top = 0 ;
r . right = width ;
r . bottom = height ;
ZeroMemory ( & bi , sizeof ( BITMAPINFO ) ) ;
bi . bmiHeader . biSize = sizeof ( BITMAPINFOHEADER ) ;
bi . bmiHeader . biWidth = ( LONG ) width ;
bi . bmiHeader . biHeight = - ( ( LONG ) height ) ; // negative height to force top-down drawing;
bi . bmiHeader . biPlanes = 1 ;
bi . bmiHeader . biBitCount = 32 ;
bi . bmiHeader . biCompression = BI_RGB ;
bi . bmiHeader . biSizeImage = ( DWORD ) ( width * height * 4 ) ;
bitmap = CreateDIBSection ( NULL , & bi , DIB_RGB_COLORS , & ppvBits , 0 , 0 ) ;
if ( bitmap = = NULL )
xpanic ( " error creating HBITMAP for unscaled ImageList image copy " , GetLastError ( ) ) ;
drawDC = CreateCompatibleDC ( dc ) ;
if ( drawDC = = NULL )
xpanic ( " error getting DC for checkbox image list bitmap " , GetLastError ( ) ) ;
prevbitmap = SelectObject ( drawDC , bitmap ) ;
if ( prevbitmap = = NULL )
xpanic ( " error selecting checkbox image list bitmap into DC " , GetLastError ( ) ) ;
2014-08-25 13:04:20 -05:00
( * drawfunc ) ( drawDC , & r , cbState , theme ) ;
2014-08-17 12:34:06 -05:00
if ( SelectObject ( drawDC , prevbitmap ) ! = bitmap )
xpanic ( " error selecting previous bitmap into checkbox image's DC " , GetLastError ( ) ) ;
if ( DeleteDC ( drawDC ) = = 0 )
xpanic ( " error deleting checkbox image's DC " , GetLastError ( ) ) ;
return bitmap ;
}
2014-08-25 13:04:20 -05:00
static HIMAGELIST newCheckboxImageList ( HWND hwnddc , void ( * sizefunc ) ( HDC , int * , int * , HTHEME ) , void ( * drawfunc ) ( HDC , RECT * , int , HTHEME ) , HTHEME theme )
2014-08-17 12:34:06 -05:00
{
int width , height ;
int cbState ;
HDC dc ;
2014-08-25 01:22:07 -05:00
HIMAGELIST il ;
2014-08-17 12:34:06 -05:00
dc = GetDC ( hwnddc ) ;
if ( dc = = NULL )
xpanic ( " error getting DC for making the checkbox image list " , GetLastError ( ) ) ;
2014-08-25 13:04:20 -05:00
( * sizefunc ) ( dc , & width , & height , theme ) ;
2014-08-25 01:22:07 -05:00
il = ( * fv_ImageList_Create ) ( width , height , ILC_COLOR32 , 20 , 20 ) ; // should be reasonable
if ( il = = NULL )
2014-08-17 12:34:06 -05:00
xpanic ( " error creating checkbox image list " , GetLastError ( ) ) ;
for ( cbState = 0 ; cbState < checkboxnStates ; cbState + + ) {
HBITMAP bitmap ;
2014-08-25 13:04:20 -05:00
bitmap = makeCheckboxImageListEntry ( dc , width , height , cbState , drawfunc , theme ) ;
2014-08-25 01:22:07 -05:00
if ( ( * fv_ImageList_Add ) ( il , bitmap , NULL ) = = - 1 )
2014-08-17 12:34:06 -05:00
xpanic ( " error adding checkbox image to image list " , GetLastError ( ) ) ;
if ( DeleteObject ( bitmap ) = = 0 )
xpanic ( " error deleting checkbox bitmap " , GetLastError ( ) ) ;
}
if ( ReleaseDC ( hwnddc , dc ) = = 0 )
xpanic ( " error deleting checkbox image list DC " , GetLastError ( ) ) ;
2014-08-25 01:22:07 -05:00
return il ;
2014-08-17 12:34:06 -05:00
}
2014-08-25 13:04:20 -05:00
HIMAGELIST makeCheckboxImageList ( HWND hwnddc , HTHEME * theme )
2014-08-17 12:34:06 -05:00
{
2014-08-25 13:04:20 -05:00
if ( * theme ! = NULL ) {
HRESULT res ;
res = CloseThemeData ( * theme ) ;
if ( res ! = S_OK )
xpanichresult ( " error closing theme " , res ) ;
* theme = NULL ;
2014-08-17 12:34:06 -05:00
}
2014-08-25 13:04:20 -05:00
// ignore error; if it can't be done, we can fall back to DrawFrameControl()
if ( * theme = = NULL ) // try to open the theme
* theme = OpenThemeData ( hwnddc , L " button " ) ;
if ( * theme ! = NULL ) // use the theme
return newCheckboxImageList ( hwnddc , themeSize , themeImage , * theme ) ;
2014-08-17 12:34:06 -05:00
// couldn't open; fall back
2014-08-25 13:04:20 -05:00
return newCheckboxImageList ( hwnddc , dfcSize , dfcImage , * theme ) ;
2014-08-17 12:34:06 -05:00
}