2016-04-14 20:35:50 -05:00
// 14 april 2016
2016-04-23 15:49:46 -05:00
# include "uipriv_windows.hpp"
2016-04-14 20:35:50 -05:00
2016-04-17 18:37:03 -05:00
// TODOs
2016-04-17 21:09:18 -05:00
// - quote the Choose Font sample here for reference
2016-04-17 18:37:03 -05:00
// - the Choose Font sample defaults to Regular/Italic/Bold/Bold Italic in some case (no styles?); do we? find out what the case is
2016-04-17 21:09:18 -05:00
// - do we set initial family and style topmost as well?
2016-04-18 12:32:48 -05:00
// - this should probably just handle IDWriteFonts
2016-04-17 18:37:03 -05:00
2016-04-14 20:35:50 -05:00
struct fontDialog {
HWND hwnd ;
HWND familyCombobox ;
HWND styleCombobox ;
HWND sizeCombobox ;
2016-04-17 00:34:05 -05:00
2016-04-18 12:32:48 -05:00
struct fontDialogParams * params ;
2016-04-17 00:34:05 -05:00
2016-04-14 20:35:50 -05:00
fontCollection * fc ;
2016-04-17 00:34:05 -05:00
2016-04-16 16:58:45 -05:00
RECT sampleRect ;
2016-04-17 12:06:45 -05:00
HWND sampleBox ;
2016-04-17 00:34:05 -05:00
// we store the current selections in case an invalid string is typed in (partial or nonexistent or invalid number)
// on OK, these are what are read
LRESULT curFamily ;
LRESULT curStyle ;
2016-04-17 18:03:34 -05:00
double curSize ;
2016-04-17 18:22:37 -05:00
2016-04-17 18:37:03 -05:00
// these are finding the style that's closest to the previous one (these fields) when changing a font
2016-04-17 18:22:37 -05:00
DWRITE_FONT_WEIGHT weight ;
DWRITE_FONT_STYLE style ;
DWRITE_FONT_STRETCH stretch ;
2016-04-14 20:35:50 -05:00
} ;
2016-04-17 19:10:14 -05:00
static LRESULT cbAddString ( HWND cb , const WCHAR * str )
2016-04-14 21:25:32 -05:00
{
LRESULT lr ;
lr = SendMessageW ( cb , CB_ADDSTRING , 0 , ( LPARAM ) str ) ;
if ( lr = = ( LRESULT ) CB_ERR | | lr = = ( LRESULT ) CB_ERRSPACE )
2016-04-23 21:15:33 -05:00
logLastError ( L " error adding item to combobox " ) ;
2016-04-14 21:25:32 -05:00
return lr ;
}
2016-04-17 19:10:14 -05:00
static LRESULT cbInsertString ( HWND cb , const WCHAR * str , WPARAM pos )
2016-04-14 21:25:32 -05:00
{
LRESULT lr ;
2016-04-17 19:10:14 -05:00
lr = SendMessageW ( cb , CB_INSERTSTRING , pos , ( LPARAM ) str ) ;
if ( lr ! = ( LRESULT ) pos )
2016-04-23 21:15:33 -05:00
logLastError ( L " error inserting item to combobox " ) ;
2016-04-14 21:25:32 -05:00
return lr ;
}
2016-04-17 00:34:05 -05:00
static LRESULT cbGetItemData ( HWND cb , WPARAM item )
2016-04-15 12:18:06 -05:00
{
2016-04-17 00:34:05 -05:00
LRESULT data ;
data = SendMessageW ( cb , CB_GETITEMDATA , item , 0 ) ;
if ( data = = ( LRESULT ) CB_ERR )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting combobox item data for font dialog " ) ;
2016-04-17 00:34:05 -05:00
return data ;
}
static void cbSetItemData ( HWND cb , WPARAM item , LPARAM data )
{
if ( SendMessageW ( cb , CB_SETITEMDATA , item , data ) = = ( LRESULT ) CB_ERR )
2016-04-23 21:15:33 -05:00
logLastError ( L " error setting combobox item data " ) ;
2016-04-17 00:34:05 -05:00
}
static BOOL cbGetCurSel ( HWND cb , LRESULT * sel )
{
LRESULT n ;
n = SendMessageW ( cb , CB_GETCURSEL , 0 , 0 ) ;
if ( n = = ( LRESULT ) CB_ERR )
return FALSE ;
if ( sel ! = NULL )
* sel = n ;
return TRUE ;
}
static void cbSetCurSel ( HWND cb , WPARAM item )
{
2016-04-17 18:03:34 -05:00
if ( SendMessageW ( cb , CB_SETCURSEL , item , 0 ) ! = ( LRESULT ) item )
2016-04-23 21:15:33 -05:00
logLastError ( L " error selecting combobox item " ) ;
2016-04-17 00:34:05 -05:00
}
2016-04-15 12:18:06 -05:00
2016-04-17 00:34:05 -05:00
static LRESULT cbGetCount ( HWND cb )
{
LRESULT n ;
n = SendMessageW ( cb , CB_GETCOUNT , 0 , 0 ) ;
2016-04-15 12:18:06 -05:00
if ( n = = ( LRESULT ) CB_ERR )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting combobox item count " ) ;
2016-04-17 00:34:05 -05:00
return n ;
}
static void cbWipeAndReleaseData ( HWND cb )
{
IUnknown * obj ;
LRESULT i , n ;
n = cbGetCount ( cb ) ;
2016-04-15 12:18:06 -05:00
for ( i = 0 ; i < n ; i + + ) {
2016-04-17 00:34:05 -05:00
obj = ( IUnknown * ) cbGetItemData ( cb , ( WPARAM ) i ) ;
obj - > Release ( ) ;
2016-04-15 12:18:06 -05:00
}
2016-04-17 00:34:05 -05:00
SendMessageW ( cb , CB_RESETCONTENT , 0 , 0 ) ;
}
2016-04-17 18:03:34 -05:00
static WCHAR * cbGetItemText ( HWND cb , WPARAM item )
{
LRESULT len ;
WCHAR * text ;
// note: neither message includes the terminating L'\0'
len = SendMessageW ( cb , CB_GETLBTEXTLEN , item , 0 ) ;
if ( len = = ( LRESULT ) CB_ERR )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting item text length from combobox " ) ;
2016-04-17 18:03:34 -05:00
text = ( WCHAR * ) uiAlloc ( ( len + 1 ) * sizeof ( WCHAR ) , " WCHAR[] " ) ;
if ( SendMessageW ( cb , CB_GETLBTEXT , item , ( LPARAM ) text ) ! = len )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting item text from combobox " ) ;
2016-04-17 19:10:14 -05:00
return text ;
2016-04-17 18:03:34 -05:00
}
static BOOL cbTypeToSelect ( HWND cb , LRESULT * posOut , BOOL restoreAfter )
{
WCHAR * text ;
LRESULT pos ;
DWORD selStart , selEnd ;
// start by saving the current selection as setting the item will change the selection
SendMessageW ( cb , CB_GETEDITSEL , ( WPARAM ) ( & selStart ) , ( LPARAM ) ( & selEnd ) ) ;
text = windowText ( cb ) ;
pos = SendMessageW ( cb , CB_FINDSTRINGEXACT , ( WPARAM ) ( - 1 ) , ( LPARAM ) text ) ;
if ( pos = = ( LRESULT ) CB_ERR ) {
uiFree ( text ) ;
return FALSE ;
}
cbSetCurSel ( cb , ( WPARAM ) pos ) ;
if ( posOut ! = NULL )
* posOut = pos ;
if ( restoreAfter )
if ( SendMessageW ( cb , WM_SETTEXT , 0 , ( LPARAM ) text ) ! = ( LRESULT ) TRUE )
2016-04-23 21:15:33 -05:00
logLastError ( L " error restoring old combobox text " ) ;
2016-04-17 18:03:34 -05:00
uiFree ( text ) ;
// and restore the selection like above
// TODO isn't there a 32-bit version of this
if ( SendMessageW ( cb , CB_SETEDITSEL , 0 , MAKELPARAM ( selStart , selEnd ) ) ! = ( LRESULT ) TRUE )
2016-04-23 21:15:33 -05:00
logLastError ( L " error restoring combobox edit selection " ) ;
2016-04-17 18:03:34 -05:00
return TRUE ;
}
2016-04-17 00:34:05 -05:00
static void wipeStylesBox ( struct fontDialog * f )
{
cbWipeAndReleaseData ( f - > styleCombobox ) ;
2016-04-15 12:18:06 -05:00
}
2016-04-18 12:32:48 -05:00
static WCHAR * fontStyleName ( struct fontCollection * fc , IDWriteFont * font )
2016-04-15 12:18:06 -05:00
{
IDWriteLocalizedStrings * str ;
WCHAR * wstr ;
HRESULT hr ;
2016-04-17 00:34:05 -05:00
hr = font - > GetFaceNames ( & str ) ;
2016-04-15 12:18:06 -05:00
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error getting font style name for font dialog " , hr ) ;
2016-04-18 12:32:48 -05:00
wstr = fontCollectionCorrectString ( fc , str ) ;
2016-04-15 12:18:06 -05:00
str - > Release ( ) ;
return wstr ;
}
2016-04-17 18:22:37 -05:00
static void queueRedrawSampleText ( struct fontDialog * f )
{
// TODO TRUE?
if ( InvalidateRect ( f - > sampleBox , NULL , TRUE ) = = 0 )
2016-04-23 21:15:33 -05:00
logLastError ( L " error queueing a redraw of the font dialog's sample text " ) ;
2016-04-17 18:22:37 -05:00
}
static void styleChanged ( struct fontDialog * f )
{
LRESULT pos ;
BOOL selected ;
IDWriteFont * font ;
selected = cbGetCurSel ( f - > styleCombobox , & pos ) ;
if ( ! selected ) // on deselect, do nothing
return ;
f - > curStyle = pos ;
font = ( IDWriteFont * ) cbGetItemData ( f - > styleCombobox , ( WPARAM ) ( f - > curStyle ) ) ;
// these are for the nearest match when changing the family; see below
f - > weight = font - > GetWeight ( ) ;
f - > style = font - > GetStyle ( ) ;
f - > stretch = font - > GetStretch ( ) ;
queueRedrawSampleText ( f ) ;
}
static void styleEdited ( struct fontDialog * f )
{
if ( cbTypeToSelect ( f - > styleCombobox , & ( f - > curStyle ) , FALSE ) )
styleChanged ( f ) ;
}
2016-04-15 12:18:06 -05:00
static void familyChanged ( struct fontDialog * f )
{
2016-04-17 00:34:05 -05:00
LRESULT pos ;
BOOL selected ;
2016-04-16 21:46:39 -05:00
IDWriteFontFamily * family ;
2016-04-17 18:37:03 -05:00
IDWriteFont * font , * matchFont ;
DWRITE_FONT_WEIGHT weight ;
DWRITE_FONT_STYLE style ;
DWRITE_FONT_STRETCH stretch ;
2016-04-16 21:46:39 -05:00
UINT32 i , n ;
2016-04-17 18:37:03 -05:00
UINT32 matching ;
2016-04-15 12:18:06 -05:00
WCHAR * label ;
HRESULT hr ;
2016-04-17 00:34:05 -05:00
selected = cbGetCurSel ( f - > familyCombobox , & pos ) ;
if ( ! selected ) // on deselect, do nothing
return ;
f - > curFamily = pos ;
2016-04-15 12:18:06 -05:00
2016-04-17 00:34:05 -05:00
family = ( IDWriteFontFamily * ) cbGetItemData ( f - > familyCombobox , ( WPARAM ) ( f - > curFamily ) ) ;
2016-04-15 12:18:06 -05:00
2016-04-17 18:37:03 -05:00
// for the nearest style match
// when we select a new family, we want the nearest style to the previously selected one to be chosen
// this is how the Choose Font sample does it
hr = family - > GetFirstMatchingFont (
f - > weight ,
f - > stretch ,
f - > style ,
& matchFont ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error finding first matching font to previous style in font dialog " , hr ) ;
2016-04-17 18:37:03 -05:00
// we can't just compare pointers; a "newly created" object comes out
// the Choose Font sample appears to do this instead
weight = matchFont - > GetWeight ( ) ;
style = matchFont - > GetStyle ( ) ;
stretch = matchFont - > GetStretch ( ) ;
matchFont - > Release ( ) ;
2016-04-16 21:46:39 -05:00
// TODO test mutliple streteches; all the fonts I have have only one stretch value?
2016-04-17 00:34:05 -05:00
wipeStylesBox ( f ) ;
2016-04-16 21:46:39 -05:00
n = family - > GetFontCount ( ) ;
2016-04-17 18:37:03 -05:00
matching = 0 ; // a safe/suitable default just in case
2016-04-16 21:46:39 -05:00
for ( i = 0 ; i < n ; i + + ) {
2016-04-17 00:34:05 -05:00
hr = family - > GetFont ( i , & font ) ;
2016-04-15 12:18:06 -05:00
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error getting font for filling styles box " , hr ) ;
2016-04-18 12:32:48 -05:00
label = fontStyleName ( f - > fc , font ) ;
2016-04-15 12:18:06 -05:00
pos = cbAddString ( f - > styleCombobox , label ) ;
uiFree ( label ) ;
2016-04-17 00:34:05 -05:00
cbSetItemData ( f - > styleCombobox , ( WPARAM ) pos , ( LPARAM ) font ) ;
2016-04-17 18:37:03 -05:00
if ( font - > GetWeight ( ) = = weight & &
font - > GetStyle ( ) = = style & &
font - > GetStretch ( ) = = stretch )
matching = i ;
2016-04-15 12:18:06 -05:00
}
2016-04-17 18:37:03 -05:00
// and now, load the match
cbSetCurSel ( f - > styleCombobox , ( WPARAM ) matching ) ;
2016-04-17 18:22:37 -05:00
styleChanged ( f ) ;
2016-04-17 12:06:45 -05:00
}
2016-04-17 18:03:34 -05:00
// TODO search language variants like the sample does
static void familyEdited ( struct fontDialog * f )
{
if ( cbTypeToSelect ( f - > familyCombobox , & ( f - > curFamily ) , FALSE ) )
familyChanged ( f ) ;
}
2016-04-17 12:06:45 -05:00
2016-04-17 19:10:14 -05:00
static const struct {
const WCHAR * text ;
double value ;
} defaultSizes [ ] = {
{ L " 8 " , 8 } ,
{ L " 9 " , 9 } ,
{ L " 10 " , 10 } ,
{ L " 11 " , 11 } ,
{ L " 12 " , 12 } ,
{ L " 14 " , 14 } ,
{ L " 16 " , 16 } ,
{ L " 18 " , 18 } ,
{ L " 20 " , 20 } ,
{ L " 22 " , 22 } ,
{ L " 24 " , 24 } ,
{ L " 26 " , 26 } ,
{ L " 28 " , 28 } ,
{ L " 36 " , 36 } ,
{ L " 48 " , 48 } ,
{ L " 72 " , 72 } ,
{ NULL , 0 } ,
} ;
static void sizeChanged ( struct fontDialog * f )
{
LRESULT pos ;
BOOL selected ;
selected = cbGetCurSel ( f - > sizeCombobox , & pos ) ;
if ( ! selected ) // on deselect, do nothing
return ;
f - > curSize = defaultSizes [ pos ] . value ;
queueRedrawSampleText ( f ) ;
}
static void sizeEdited ( struct fontDialog * f )
{
WCHAR * wsize ;
double size ;
// handle type-to-selection
if ( cbTypeToSelect ( f - > sizeCombobox , NULL , FALSE ) ) {
sizeChanged ( f ) ;
return ;
}
// selection not chosen, try to parse the typing
wsize = windowText ( f - > sizeCombobox ) ;
// this is what the Choose Font dialog does; it swallows errors while the real ChooseFont() is not lenient (and only checks on OK)
size = wcstod ( wsize , NULL ) ;
if ( size < = 0 ) // don't change on invalid size
return ;
f - > curSize = size ;
queueRedrawSampleText ( f ) ;
}
2016-04-17 12:06:45 -05:00
static void fontDialogDrawSampleText ( struct fontDialog * f , ID2D1RenderTarget * rt )
{
D2D1_COLOR_F color ;
D2D1_BRUSH_PROPERTIES props ;
ID2D1SolidColorBrush * black ;
IDWriteFont * font ;
IDWriteLocalizedStrings * sampleStrings ;
BOOL exists ;
WCHAR * sample ;
2016-04-17 19:10:14 -05:00
WCHAR * family ;
2016-04-17 12:06:45 -05:00
IDWriteTextFormat * format ;
D2D1_RECT_F rect ;
HRESULT hr ;
color . r = 0.0 ;
color . g = 0.0 ;
color . b = 0.0 ;
color . a = 1.0 ;
ZeroMemory ( & props , sizeof ( D2D1_BRUSH_PROPERTIES ) ) ;
props . opacity = 1.0 ;
// identity matrix
props . transform . _11 = 1 ;
props . transform . _22 = 1 ;
hr = rt - > CreateSolidColorBrush (
& color ,
& props ,
& black ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error creating solid brush " , hr ) ;
2016-04-17 12:06:45 -05:00
font = ( IDWriteFont * ) cbGetItemData ( f - > styleCombobox , ( WPARAM ) f - > curStyle ) ;
hr = font - > GetInformationalStrings ( DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT , & sampleStrings , & exists ) ;
if ( hr ! = S_OK )
exists = FALSE ;
if ( exists ) {
sample = fontCollectionCorrectString ( f - > fc , sampleStrings ) ;
sampleStrings - > Release ( ) ;
} else
2016-04-17 15:49:50 -05:00
sample = L " The quick brown fox jumps over the lazy dog. " ;
2016-04-17 12:06:45 -05:00
2016-04-17 19:10:14 -05:00
// DirectWrite doesn't allow creating a text format from a font; we need to get this ourselves
family = cbGetItemText ( f - > familyCombobox , f - > curFamily ) ;
2016-04-17 12:06:45 -05:00
hr = dwfactory - > CreateTextFormat ( family ,
NULL ,
font - > GetWeight ( ) ,
font - > GetStyle ( ) ,
font - > GetStretch ( ) ,
// typographic points are 1/72 inch; this parameter is 1/96 inch
// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx
2016-04-17 19:10:14 -05:00
f - > curSize * ( 96.0 / 72.0 ) ,
2016-04-17 12:06:45 -05:00
// see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx
// TODO use the current locale again?
L " " ,
& format ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error creating IDWriteTextFormat " , hr ) ;
2016-04-17 12:06:45 -05:00
uiFree ( family ) ;
rect . left = 0 ;
rect . top = 0 ;
rect . right = rt - > GetSize ( ) . width ;
rect . bottom = rt - > GetSize ( ) . height ;
rt - > DrawText ( sample , wcslen ( sample ) ,
format ,
& rect ,
black ,
// TODO really?
D2D1_DRAW_TEXT_OPTIONS_NONE ,
DWRITE_MEASURING_MODE_NATURAL ) ;
format - > Release ( ) ;
if ( exists )
uiFree ( sample ) ;
black - > Release ( ) ;
}
static LRESULT CALLBACK fontDialogSampleSubProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam , UINT_PTR uIdSubclass , DWORD_PTR dwRefData )
{
ID2D1RenderTarget * rt ;
struct fontDialog * f ;
switch ( uMsg ) {
case msgD2DScratchPaint :
rt = ( ID2D1RenderTarget * ) lParam ;
f = ( struct fontDialog * ) dwRefData ;
fontDialogDrawSampleText ( f , rt ) ;
return 0 ;
case WM_NCDESTROY :
if ( RemoveWindowSubclass ( hwnd , fontDialogSampleSubProc , uIdSubclass ) = = FALSE )
2016-04-23 21:15:33 -05:00
logLastError ( L " error removing font dialog sample text subclass " ) ;
2016-04-17 12:06:45 -05:00
break ;
}
return DefSubclassProc ( hwnd , uMsg , wParam , lParam ) ;
2016-04-15 12:18:06 -05:00
}
2016-04-17 23:56:13 -05:00
static void setupInitialFontDialogState ( struct fontDialog * f )
{
WCHAR wsize [ 512 ] ; // this should be way more than enough
LRESULT pos ;
// first let's load the size
// the real font dialog:
// - if the chosen font size is in the list, it selects that item AND makes it topmost
// - if the chosen font size is not in the list, don't bother
// we'll simulate it by setting the text to a %f representation, then pretending as if it was entered
2016-04-18 16:14:33 -05:00
// TODO is 512 the correct number to pass to _snwprintf()?
// TODO will this revert to scientific notation?
_snwprintf ( wsize , 512 , L " %g " , f - > params - > size ) ;
2016-04-17 23:56:13 -05:00
// TODO make this a setWindowText()
if ( SendMessageW ( f - > sizeCombobox , WM_SETTEXT , 0 , ( LPARAM ) wsize ) ! = ( LRESULT ) TRUE )
2016-04-23 21:15:33 -05:00
logLastError ( L " error setting size combobox to initial font size " ) ;
2016-04-17 23:56:13 -05:00
sizeEdited ( f ) ;
if ( cbGetCurSel ( f - > sizeCombobox , & pos ) )
if ( SendMessageW ( f - > sizeCombobox , CB_SETTOPINDEX , ( WPARAM ) pos , 0 ) ! = 0 )
2016-04-23 21:15:33 -05:00
logLastError ( L " error making chosen size topmost in the size combobox " ) ;
2016-04-17 23:56:13 -05:00
// now we set the family and style
// we do this by first setting the previous style attributes, then simulating a font entered
2016-04-18 16:14:33 -05:00
f - > weight = f - > params - > font - > GetWeight ( ) ;
f - > style = f - > params - > font - > GetStyle ( ) ;
f - > stretch = f - > params - > font - > GetStretch ( ) ;
if ( SendMessageW ( f - > familyCombobox , WM_SETTEXT , 0 , ( LPARAM ) ( f - > params - > familyName ) ) ! = ( LRESULT ) TRUE )
2016-04-23 21:15:33 -05:00
logLastError ( L " error setting family combobox to initial font family " ) ;
2016-04-17 23:56:13 -05:00
familyEdited ( f ) ;
}
2016-04-14 20:35:50 -05:00
static struct fontDialog * beginFontDialog ( HWND hwnd , LPARAM lParam )
{
struct fontDialog * f ;
2016-04-17 00:34:05 -05:00
UINT32 i , nFamilies ;
IDWriteFontFamily * family ;
2016-04-14 21:25:32 -05:00
WCHAR * wname ;
2016-04-17 19:10:14 -05:00
LRESULT pos ;
2016-04-16 16:58:45 -05:00
HWND samplePlacement ;
2016-04-14 21:25:32 -05:00
HRESULT hr ;
2016-04-14 20:35:50 -05:00
f = uiNew ( struct fontDialog ) ;
f - > hwnd = hwnd ;
2016-04-18 12:32:48 -05:00
f - > params = ( struct fontDialogParams * ) lParam ;
2016-04-14 20:35:50 -05:00
f - > familyCombobox = GetDlgItem ( f - > hwnd , rcFontFamilyCombobox ) ;
if ( f - > familyCombobox = = NULL )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting font family combobox handle " ) ;
2016-04-14 20:35:50 -05:00
f - > styleCombobox = GetDlgItem ( f - > hwnd , rcFontStyleCombobox ) ;
if ( f - > styleCombobox = = NULL )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting font style combobox handle " ) ;
2016-04-14 20:35:50 -05:00
f - > sizeCombobox = GetDlgItem ( f - > hwnd , rcFontSizeCombobox ) ;
if ( f - > sizeCombobox = = NULL )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting font size combobox handle " ) ;
2016-04-14 20:35:50 -05:00
2016-04-14 21:25:32 -05:00
f - > fc = loadFontCollection ( ) ;
2016-04-17 00:34:05 -05:00
nFamilies = f - > fc - > fonts - > GetFontFamilyCount ( ) ;
for ( i = 0 ; i < nFamilies ; i + + ) {
hr = f - > fc - > fonts - > GetFontFamily ( i , & family ) ;
2016-04-14 21:25:32 -05:00
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error getting font family " , hr ) ;
2016-04-17 00:34:05 -05:00
wname = fontCollectionFamilyName ( f - > fc , family ) ;
pos = cbAddString ( f - > familyCombobox , wname ) ;
2016-04-14 21:25:32 -05:00
uiFree ( wname ) ;
2016-04-17 00:34:05 -05:00
cbSetItemData ( f - > familyCombobox , ( WPARAM ) pos , ( LPARAM ) family ) ;
2016-04-14 21:25:32 -05:00
}
2016-04-17 19:10:14 -05:00
for ( i = 0 ; defaultSizes [ i ] . text ! = NULL ; i + + )
cbInsertString ( f - > sizeCombobox , defaultSizes [ i ] . text , ( WPARAM ) i ) ;
2016-04-16 16:58:45 -05:00
samplePlacement = GetDlgItem ( f - > hwnd , rcFontSamplePlacement ) ;
if ( samplePlacement = = NULL )
2016-04-23 21:15:33 -05:00
logLastError ( L " error getting sample placement static control handle " ) ;
2016-04-29 16:08:31 -05:00
uiWindowsEnsureGetWindowRect ( samplePlacement , & ( f - > sampleRect ) ) ;
2016-04-16 16:58:45 -05:00
mapWindowRect ( NULL , f - > hwnd , & ( f - > sampleRect ) ) ;
2016-04-23 21:15:33 -05:00
uiWindowsEnsureDestroyWindow ( samplePlacement ) ;
2016-04-17 12:06:45 -05:00
f - > sampleBox = newD2DScratch ( f - > hwnd , & ( f - > sampleRect ) , ( HMENU ) rcFontSamplePlacement , fontDialogSampleSubProc , ( DWORD_PTR ) f ) ;
2016-04-16 16:58:45 -05:00
2016-04-17 23:56:13 -05:00
setupInitialFontDialogState ( f ) ;
2016-04-14 20:35:50 -05:00
return f ;
}
static void endFontDialog ( struct fontDialog * f , INT_PTR code )
{
2016-04-15 12:18:06 -05:00
wipeStylesBox ( f ) ;
2016-04-17 00:34:05 -05:00
cbWipeAndReleaseData ( f - > familyCombobox ) ;
2016-04-14 21:25:32 -05:00
fontCollectionFree ( f - > fc ) ;
2016-04-14 20:35:50 -05:00
if ( EndDialog ( f - > hwnd , code ) = = 0 )
2016-04-23 21:15:33 -05:00
logLastError ( L " error ending font dialog " ) ;
2016-04-14 20:35:50 -05:00
uiFree ( f ) ;
}
static INT_PTR tryFinishDialog ( struct fontDialog * f , WPARAM wParam )
{
2016-04-18 16:14:33 -05:00
IDWriteFontFamily * family ;
2016-04-18 00:12:25 -05:00
2016-04-14 20:35:50 -05:00
// cancelling
if ( LOWORD ( wParam ) ! = IDOK ) {
endFontDialog ( f , 1 ) ;
return TRUE ;
}
2016-04-17 21:09:18 -05:00
// OK
2016-04-18 16:14:33 -05:00
destroyFontDialogParams ( f - > params ) ;
f - > params - > font = ( IDWriteFont * ) cbGetItemData ( f - > styleCombobox , f - > curStyle ) ;
// we need to save font from being destroyed with the combobox
f - > params - > font - > AddRef ( ) ;
f - > params - > size = f - > curSize ;
family = ( IDWriteFontFamily * ) cbGetItemData ( f - > familyCombobox , f - > curFamily ) ;
f - > params - > familyName = fontCollectionFamilyName ( f - > fc , family ) ;
f - > params - > styleName = fontStyleName ( f - > fc , f - > params - > font ) ;
2016-04-14 20:35:50 -05:00
endFontDialog ( f , 2 ) ;
return TRUE ;
}
static INT_PTR CALLBACK fontDialogDlgProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
struct fontDialog * f ;
f = ( struct fontDialog * ) GetWindowLongPtrW ( hwnd , DWLP_USER ) ;
if ( f = = NULL ) {
if ( uMsg = = WM_INITDIALOG ) {
f = beginFontDialog ( hwnd , lParam ) ;
SetWindowLongPtrW ( hwnd , DWLP_USER , ( LONG_PTR ) f ) ;
return TRUE ;
}
return FALSE ;
}
switch ( uMsg ) {
case WM_COMMAND :
2016-04-16 16:58:45 -05:00
SetWindowLongPtrW ( f - > hwnd , DWLP_MSGRESULT , 0 ) ; // just in case
2016-04-15 12:18:06 -05:00
switch ( LOWORD ( wParam ) ) {
case IDOK :
case IDCANCEL :
if ( HIWORD ( wParam ) ! = BN_CLICKED )
return FALSE ;
return tryFinishDialog ( f , wParam ) ;
case rcFontFamilyCombobox :
2016-04-17 18:03:34 -05:00
if ( HIWORD ( wParam ) = = CBN_SELCHANGE ) {
familyChanged ( f ) ;
return TRUE ;
}
if ( HIWORD ( wParam ) = = CBN_EDITCHANGE ) {
familyEdited ( f ) ;
return TRUE ;
}
return FALSE ;
2016-04-16 21:15:19 -05:00
case rcFontStyleCombobox :
2016-04-17 18:22:37 -05:00
if ( HIWORD ( wParam ) = = CBN_SELCHANGE ) {
styleChanged ( f ) ;
return TRUE ;
}
if ( HIWORD ( wParam ) = = CBN_EDITCHANGE ) {
styleEdited ( f ) ;
return TRUE ;
}
return FALSE ;
2016-04-16 21:15:19 -05:00
case rcFontSizeCombobox :
2016-04-17 19:10:14 -05:00
if ( HIWORD ( wParam ) = = CBN_SELCHANGE ) {
sizeChanged ( f ) ;
return TRUE ;
}
if ( HIWORD ( wParam ) = = CBN_EDITCHANGE ) {
sizeEdited ( f ) ;
return TRUE ;
}
return FALSE ;
2016-04-15 12:18:06 -05:00
}
return FALSE ;
2016-04-14 20:35:50 -05:00
}
return FALSE ;
}
2016-04-18 12:32:48 -05:00
BOOL showFontDialog ( HWND parent , struct fontDialogParams * params )
2016-04-14 20:35:50 -05:00
{
2016-04-18 12:32:48 -05:00
switch ( DialogBoxParamW ( hInstance , MAKEINTRESOURCE ( rcFontDialog ) , parent , fontDialogDlgProc , ( LPARAM ) params ) ) {
2016-04-17 21:09:18 -05:00
case 1 : // cancel
return FALSE ;
case 2 : // ok
// make the compiler happy by putting the return after the switch
2016-04-14 20:35:50 -05:00
break ;
default :
2016-04-23 21:15:33 -05:00
logLastError ( L " error running font dialog " ) ;
2016-04-14 20:35:50 -05:00
}
2016-04-17 21:09:18 -05:00
return TRUE ;
2016-04-14 20:35:50 -05:00
}
2016-04-18 12:32:48 -05:00
static IDWriteFontFamily * tryFindFamily ( IDWriteFontCollection * fc , const WCHAR * name )
{
UINT32 index ;
BOOL exists ;
IDWriteFontFamily * family ;
HRESULT hr ;
hr = fc - > FindFamilyName ( name , & index , & exists ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error finding font family for font dialog " , hr ) ;
2016-04-18 12:32:48 -05:00
if ( ! exists )
return NULL ;
hr = fc - > GetFontFamily ( index , & family ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error extracting found font family for font dialog " , hr ) ;
2016-04-18 12:32:48 -05:00
return family ;
}
void loadInitialFontDialogParams ( struct fontDialogParams * params )
{
struct fontCollection * fc ;
IDWriteFontFamily * family ;
IDWriteFont * font ;
HRESULT hr ;
// Our preferred font is Arial 10 Regular.
// 10 comes from the official font dialog.
// Arial Regular is a reasonable, if arbitrary, default; it's similar to the defaults on other systems.
// If Arial isn't found, we'll use Helvetica and then MS Sans Serif as fallbacks, and if not, we'll just grab the first font family in the collection.
// We need the correct localized name for Regular (and possibly Arial too? let's say yes to be safe), so let's grab the strings from DirectWrite instead of hardcoding them.
fc = loadFontCollection ( ) ;
family = tryFindFamily ( fc - > fonts , L " Arial " ) ;
if ( family = = NULL ) {
family = tryFindFamily ( fc - > fonts , L " Helvetica " ) ;
if ( family = = NULL ) {
family = tryFindFamily ( fc - > fonts , L " MS Sans Serif " ) ;
if ( family = = NULL ) {
hr = fc - > fonts - > GetFontFamily ( 0 , & family ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error getting first font out of font collection (worst case scenario) " , hr ) ;
2016-04-18 12:32:48 -05:00
}
}
}
// next part is simple: just get the closest match to regular
hr = family - > GetFirstMatchingFont (
DWRITE_FONT_WEIGHT_NORMAL ,
DWRITE_FONT_STRETCH_NORMAL ,
DWRITE_FONT_STYLE_NORMAL ,
& font ) ;
if ( hr ! = S_OK )
2016-04-23 21:15:33 -05:00
logHRESULT ( L " error getting Regular font from Arial " , hr ) ;
2016-04-18 12:32:48 -05:00
2016-04-18 16:14:33 -05:00
params - > font = font ;
params - > size = 10 ;
params - > familyName = fontCollectionFamilyName ( fc , family ) ;
params - > styleName = fontStyleName ( fc , font ) ;
// don't release font; we still need it
2016-04-18 12:32:48 -05:00
family - > Release ( ) ;
fontCollectionFree ( fc ) ;
}
2016-04-18 16:14:33 -05:00
void destroyFontDialogParams ( struct fontDialogParams * params )
{
params - > font - > Release ( ) ;
uiFree ( params - > familyName ) ;
uiFree ( params - > styleName ) ;
}
WCHAR * fontDialogParamsToString ( struct fontDialogParams * params )
{
WCHAR * text ;
// TODO dynamically allocate
text = ( WCHAR * ) uiAlloc ( 512 * sizeof ( WCHAR ) , " WCHAR[] " ) ;
_snwprintf ( text , 512 , L " %s %s %g " ,
params - > familyName ,
params - > styleName ,
params - > size ) ;
return text ;
}