diff options
Diffstat (limited to 'src/libui_sdl/libui/windows/fontdialog.cpp')
-rw-r--r-- | src/libui_sdl/libui/windows/fontdialog.cpp | 686 |
1 files changed, 0 insertions, 686 deletions
diff --git a/src/libui_sdl/libui/windows/fontdialog.cpp b/src/libui_sdl/libui/windows/fontdialog.cpp deleted file mode 100644 index 603a17d..0000000 --- a/src/libui_sdl/libui/windows/fontdialog.cpp +++ /dev/null @@ -1,686 +0,0 @@ -// 14 april 2016 -#include "uipriv_windows.hpp" - -// TODOs -// - quote the Choose Font sample here for reference -// - the Choose Font sample defaults to Regular/Italic/Bold/Bold Italic in some case (no styles?); do we? find out what the case is -// - do we set initial family and style topmost as well? -// - this should probably just handle IDWriteFonts - -struct fontDialog { - HWND hwnd; - HWND familyCombobox; - HWND styleCombobox; - HWND sizeCombobox; - - struct fontDialogParams *params; - - fontCollection *fc; - - RECT sampleRect; - HWND sampleBox; - - // 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; - double curSize; - - // these are finding the style that's closest to the previous one (these fields) when changing a font - DWRITE_FONT_WEIGHT weight; - DWRITE_FONT_STYLE style; - DWRITE_FONT_STRETCH stretch; -}; - -static LRESULT cbAddString(HWND cb, const WCHAR *str) -{ - LRESULT lr; - - lr = SendMessageW(cb, CB_ADDSTRING, 0, (LPARAM) str); - if (lr == (LRESULT) CB_ERR || lr == (LRESULT) CB_ERRSPACE) - logLastError(L"error adding item to combobox"); - return lr; -} - -static LRESULT cbInsertString(HWND cb, const WCHAR *str, WPARAM pos) -{ - LRESULT lr; - - lr = SendMessageW(cb, CB_INSERTSTRING, pos, (LPARAM) str); - if (lr != (LRESULT) pos) - logLastError(L"error inserting item to combobox"); - return lr; -} - -static LRESULT cbGetItemData(HWND cb, WPARAM item) -{ - LRESULT data; - - data = SendMessageW(cb, CB_GETITEMDATA, item, 0); - if (data == (LRESULT) CB_ERR) - logLastError(L"error getting combobox item data for font dialog"); - return data; -} - -static void cbSetItemData(HWND cb, WPARAM item, LPARAM data) -{ - if (SendMessageW(cb, CB_SETITEMDATA, item, data) == (LRESULT) CB_ERR) - logLastError(L"error setting combobox item data"); -} - -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) -{ - if (SendMessageW(cb, CB_SETCURSEL, item, 0) != (LRESULT) item) - logLastError(L"error selecting combobox item"); -} - -static LRESULT cbGetCount(HWND cb) -{ - LRESULT n; - - n = SendMessageW(cb, CB_GETCOUNT, 0, 0); - if (n == (LRESULT) CB_ERR) - logLastError(L"error getting combobox item count"); - return n; -} - -static void cbWipeAndReleaseData(HWND cb) -{ - IUnknown *obj; - LRESULT i, n; - - n = cbGetCount(cb); - for (i = 0; i < n; i++) { - obj = (IUnknown *) cbGetItemData(cb, (WPARAM) i); - obj->Release(); - } - SendMessageW(cb, CB_RESETCONTENT, 0, 0); -} - -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) - logLastError(L"error getting item text length from combobox"); - text = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); - if (SendMessageW(cb, CB_GETLBTEXT, item, (LPARAM) text) != len) - logLastError(L"error getting item text from combobox"); - return text; -} - -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) - logLastError(L"error restoring old combobox text"); - 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) - logLastError(L"error restoring combobox edit selection"); - return TRUE; -} - -static void wipeStylesBox(struct fontDialog *f) -{ - cbWipeAndReleaseData(f->styleCombobox); -} - -static WCHAR *fontStyleName(struct fontCollection *fc, IDWriteFont *font) -{ - IDWriteLocalizedStrings *str; - WCHAR *wstr; - HRESULT hr; - - hr = font->GetFaceNames(&str); - if (hr != S_OK) - logHRESULT(L"error getting font style name for font dialog", hr); - wstr = fontCollectionCorrectString(fc, str); - str->Release(); - return wstr; -} - -static void queueRedrawSampleText(struct fontDialog *f) -{ - // TODO TRUE? - invalidateRect(f->sampleBox, NULL, TRUE); -} - -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); -} - -static void familyChanged(struct fontDialog *f) -{ - LRESULT pos; - BOOL selected; - IDWriteFontFamily *family; - IDWriteFont *font, *matchFont; - DWRITE_FONT_WEIGHT weight; - DWRITE_FONT_STYLE style; - DWRITE_FONT_STRETCH stretch; - UINT32 i, n; - UINT32 matching; - WCHAR *label; - HRESULT hr; - - selected = cbGetCurSel(f->familyCombobox, &pos); - if (!selected) // on deselect, do nothing - return; - f->curFamily = pos; - - family = (IDWriteFontFamily *) cbGetItemData(f->familyCombobox, (WPARAM) (f->curFamily)); - - // 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) - logHRESULT(L"error finding first matching font to previous style in font dialog", hr); - // 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(); - - // TODO test mutliple streteches; all the fonts I have have only one stretch value? - wipeStylesBox(f); - n = family->GetFontCount(); - matching = 0; // a safe/suitable default just in case - for (i = 0; i < n; i++) { - hr = family->GetFont(i, &font); - if (hr != S_OK) - logHRESULT(L"error getting font for filling styles box", hr); - label = fontStyleName(f->fc, font); - pos = cbAddString(f->styleCombobox, label); - uiFree(label); - cbSetItemData(f->styleCombobox, (WPARAM) pos, (LPARAM) font); - if (font->GetWeight() == weight && - font->GetStyle() == style && - font->GetStretch() == stretch) - matching = i; - } - - // and now, load the match - cbSetCurSel(f->styleCombobox, (WPARAM) matching); - styleChanged(f); -} - -// TODO search language variants like the sample does -static void familyEdited(struct fontDialog *f) -{ - if (cbTypeToSelect(f->familyCombobox, &(f->curFamily), FALSE)) - familyChanged(f); -} - -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); -} - -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; - WCHAR *family; - 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) - logHRESULT(L"error creating solid brush", hr); - - 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 - sample = L"The quick brown fox jumps over the lazy dog."; - - // DirectWrite doesn't allow creating a text format from a font; we need to get this ourselves - family = cbGetItemText(f->familyCombobox, f->curFamily); - 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 - f->curSize * (96.0 / 72.0), - // 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) - logHRESULT(L"error creating IDWriteTextFormat", hr); - uiFree(family); - - rect.left = 0; - rect.top = 0; - rect.right = realGetSize(rt).width; - rect.bottom = realGetSize(rt).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) - logLastError(L"error removing font dialog sample text subclass"); - break; - } - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} - -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 - // 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); - // TODO make this a setWindowText() - if (SendMessageW(f->sizeCombobox, WM_SETTEXT, 0, (LPARAM) wsize) != (LRESULT) TRUE) - logLastError(L"error setting size combobox to initial font size"); - sizeEdited(f); - if (cbGetCurSel(f->sizeCombobox, &pos)) - if (SendMessageW(f->sizeCombobox, CB_SETTOPINDEX, (WPARAM) pos, 0) != 0) - logLastError(L"error making chosen size topmost in the size combobox"); - - // now we set the family and style - // we do this by first setting the previous style attributes, then simulating a font entered - 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) - logLastError(L"error setting family combobox to initial font family"); - familyEdited(f); -} - -static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) -{ - struct fontDialog *f; - UINT32 i, nFamilies; - IDWriteFontFamily *family; - WCHAR *wname; - LRESULT pos; - HWND samplePlacement; - HRESULT hr; - - f = uiNew(struct fontDialog); - f->hwnd = hwnd; - f->params = (struct fontDialogParams *) lParam; - - f->familyCombobox = getDlgItem(f->hwnd, rcFontFamilyCombobox); - f->styleCombobox = getDlgItem(f->hwnd, rcFontStyleCombobox); - f->sizeCombobox = getDlgItem(f->hwnd, rcFontSizeCombobox); - - f->fc = loadFontCollection(); - nFamilies = f->fc->fonts->GetFontFamilyCount(); - for (i = 0; i < nFamilies; i++) { - hr = f->fc->fonts->GetFontFamily(i, &family); - if (hr != S_OK) - logHRESULT(L"error getting font family", hr); - wname = fontCollectionFamilyName(f->fc, family); - pos = cbAddString(f->familyCombobox, wname); - uiFree(wname); - cbSetItemData(f->familyCombobox, (WPARAM) pos, (LPARAM) family); - } - - for (i = 0; defaultSizes[i].text != NULL; i++) - cbInsertString(f->sizeCombobox, defaultSizes[i].text, (WPARAM) i); - - samplePlacement = getDlgItem(f->hwnd, rcFontSamplePlacement); - uiWindowsEnsureGetWindowRect(samplePlacement, &(f->sampleRect)); - mapWindowRect(NULL, f->hwnd, &(f->sampleRect)); - uiWindowsEnsureDestroyWindow(samplePlacement); - f->sampleBox = newD2DScratch(f->hwnd, &(f->sampleRect), (HMENU) rcFontSamplePlacement, fontDialogSampleSubProc, (DWORD_PTR) f); - - setupInitialFontDialogState(f); - return f; -} - -static void endFontDialog(struct fontDialog *f, INT_PTR code) -{ - wipeStylesBox(f); - cbWipeAndReleaseData(f->familyCombobox); - fontCollectionFree(f->fc); - if (EndDialog(f->hwnd, code) == 0) - logLastError(L"error ending font dialog"); - uiFree(f); -} - -static INT_PTR tryFinishDialog(struct fontDialog *f, WPARAM wParam) -{ - IDWriteFontFamily *family; - - // cancelling - if (LOWORD(wParam) != IDOK) { - endFontDialog(f, 1); - return TRUE; - } - - // OK - 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); - 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: - SetWindowLongPtrW(f->hwnd, DWLP_MSGRESULT, 0); // just in case - switch (LOWORD(wParam)) { - case IDOK: - case IDCANCEL: - if (HIWORD(wParam) != BN_CLICKED) - return FALSE; - return tryFinishDialog(f, wParam); - case rcFontFamilyCombobox: - if (HIWORD(wParam) == CBN_SELCHANGE) { - familyChanged(f); - return TRUE; - } - if (HIWORD(wParam) == CBN_EDITCHANGE) { - familyEdited(f); - return TRUE; - } - return FALSE; - case rcFontStyleCombobox: - if (HIWORD(wParam) == CBN_SELCHANGE) { - styleChanged(f); - return TRUE; - } - if (HIWORD(wParam) == CBN_EDITCHANGE) { - styleEdited(f); - return TRUE; - } - return FALSE; - case rcFontSizeCombobox: - if (HIWORD(wParam) == CBN_SELCHANGE) { - sizeChanged(f); - return TRUE; - } - if (HIWORD(wParam) == CBN_EDITCHANGE) { - sizeEdited(f); - return TRUE; - } - return FALSE; - } - return FALSE; - } - return FALSE; -} - -BOOL showFontDialog(HWND parent, struct fontDialogParams *params) -{ - switch (DialogBoxParamW(hInstance, MAKEINTRESOURCE(rcFontDialog), parent, fontDialogDlgProc, (LPARAM) params)) { - case 1: // cancel - return FALSE; - case 2: // ok - // make the compiler happy by putting the return after the switch - break; - default: - logLastError(L"error running font dialog"); - } - return TRUE; -} - -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) - logHRESULT(L"error finding font family for font dialog", hr); - if (!exists) - return NULL; - hr = fc->GetFontFamily(index, &family); - if (hr != S_OK) - logHRESULT(L"error extracting found font family for font dialog", hr); - 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) - logHRESULT(L"error getting first font out of font collection (worst case scenario)", hr); - } - } - } - - // 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) - logHRESULT(L"error getting Regular font from Arial", hr); - - params->font = font; - params->size = 10; - params->familyName = fontCollectionFamilyName(fc, family); - params->styleName = fontStyleName(fc, font); - - // don't release font; we still need it - family->Release(); - fontCollectionFree(fc); -} - -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; -} |