/*
 * PROJECT:         ReactOS Console Configuration DLL
 * LICENSE:         GPL - See COPYING in the top level directory
 * FILE:            dll/cpl/console/font.c
 * PURPOSE:         Font dialog
 * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
 *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
 *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
 */

#include "console.h"

#define NDEBUG
#include <debug.h>


/*
 * Current active font, corresponding to the active console font,
 * and used for painting the text samples.
 */
FONT_PREVIEW FontPreview = {NULL, 0, 0};


/*
 * Standard font pixel/point heights for TrueType fonts
 */
static const SHORT TrueTypePoints[] =
{
    5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72
};

typedef struct _FONTSIZE_LIST_CTL
{
    LIST_CTL RasterSizeList;    // ListBox for Raster font sizes; needs to handle bisection.
    HWND hWndTTSizeList;        // ComboBox for TrueType font sizes.
    BOOL bIsTTSizeDirty;        // TRUE or FALSE depending on whether we have edited the edit zone.
    BOOL UseRasterOrTTList;     // TRUE: Use the Raster size list; FALSE: Use the TrueType size list.
    BOOL TTSizePixelUnit;       // TRUE: Size in pixels (default); FALSE: Size in points.
    LONG CurrentRasterSize;
    LONG CurrentTTSize;         // In whatever unit (pixels or points) currently selected.
} FONTSIZE_LIST_CTL, *PFONTSIZE_LIST_CTL;

/* Used by FontTypeChange() only */
static INT   CurrentSelFont  = LB_ERR;
static DWORD CurrentFontType = (DWORD)-1;   // Invalid font type

/* Detect whether the current code page has changed */
static UINT CurrentCodePage = INVALID_CP;


VOID
RefreshFontPreview(
    IN FONT_PREVIEW* Preview,
    IN PCONSOLE_STATE_INFO pConInfo)
{
    HFONT hFont = CreateConsoleFont(pConInfo);
    if (!hFont)
    {
        DPRINT1("RefreshFontPreview: CreateConsoleFont() failed\n");
        return;
    }

    if (Preview->hFont) DeleteObject(Preview->hFont);
    Preview->hFont = hFont;
    GetFontCellSize(NULL, hFont, &Preview->CharHeight, &Preview->CharWidth);
}

VOID
UpdateFontPreview(
    IN FONT_PREVIEW* Preview,
    IN HFONT hFont,
    IN UINT  CharWidth,
    IN UINT  CharHeight)
{
    if (Preview->hFont) DeleteObject(Preview->hFont);
    Preview->hFont = hFont;
    Preview->CharWidth  = CharWidth;
    Preview->CharHeight = CharHeight;
}

// PLIST_GETCOUNT
static INT
RasterSizeList_GetCount(
    IN PLIST_CTL ListCtl)
{
    return (INT)SendMessageW(ListCtl->hWndList, LB_GETCOUNT, 0, 0);
}

// PLIST_GETDATA
static ULONG_PTR
RasterSizeList_GetData(
    IN PLIST_CTL ListCtl,
    IN INT Index)
{
    return (ULONG_PTR)SendMessageW(ListCtl->hWndList, LB_GETITEMDATA, (WPARAM)Index, 0);
}


INT
LogicalSizeToPointSize(
    IN HDC hDC OPTIONAL,
    IN UINT LogicalSize)
{
    INT PointSize;
    HDC hOrgDC = hDC;

    if (!hDC)
        hDC = GetDC(NULL);

    // LogicalSize = tm.tmHeight - tm.tmInternalLeading;
    PointSize = MulDiv(LogicalSize, 72, GetDeviceCaps(hDC, LOGPIXELSY));

    if (!hOrgDC)
        ReleaseDC(NULL, hDC);

    return PointSize;
}

INT
PointSizeToLogicalSize(
    IN HDC hDC OPTIONAL,
    IN INT PointSize)
{
    INT LogicalSize;
    HDC hOrgDC = hDC;

    if (!hDC)
        hDC = GetDC(NULL);

    LogicalSize = MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);

    if (!hOrgDC)
        ReleaseDC(NULL, hDC);

    return LogicalSize;
}


static VOID
FontSizeList_SelectFontSize(
    IN PFONTSIZE_LIST_CTL SizeList,
    IN ULONG FontSize)
{
    INT nSel;
    WCHAR szFontSize[100];

    //
    // FIXME: Check whether FontSize == 0
    // (or in the case of raster font maybe, whether HIWORD(FontSize) == Height == 0) ??
    //

    /* Find and select the best font size in the list corresponding to the current size */
    if (SizeList->UseRasterOrTTList)
    {
        INT idx;

        /* Raster font size (in pixels) */
        SizeList->CurrentRasterSize = FontSize;

        nSel = BisectListSortedByValue(&SizeList->RasterSizeList, FontSize, NULL, FALSE);
        idx  = (INT)SendMessageW(SizeList->RasterSizeList.hWndList, LB_GETCOUNT, 0, 0);
        if (nSel == LB_ERR)
        {
            /* Not found, select the first element of the list */
            nSel = 0;
        }
        else if (nSel >= idx)
        {
            /*
             * We got an index beyond the end of the list (as per Bisect* functionality),
             * so instead, select the last element of the list.
             */
            nSel = idx - 1;
        }
        SendMessageW(SizeList->RasterSizeList.hWndList, LB_SETCURSEL, (WPARAM)nSel, 0);
    }
    else
    {
        /* TrueType font size (in pixels or points) */
        SizeList->CurrentTTSize = FontSize;

        // _ultow(szFontSize, FontSize, 10);
        StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", FontSize);

        /* Find the font size in the list, or add it both in the ComboBox list, sorted by size value (string), and its edit box */
        nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_FINDSTRINGEXACT, 0, (LPARAM)szFontSize);
        if (nSel == CB_ERR)
        {
            nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_ADDSTRING, -1, (LPARAM)szFontSize);
            // ComboBox_SetText(...)
            SetWindowTextW(SizeList->hWndTTSizeList, szFontSize);
            SizeList->bIsTTSizeDirty = TRUE;
        }
        SendMessageW(SizeList->hWndTTSizeList, CB_SETCURSEL, (WPARAM)nSel, 0);
    }
}

static LONG
FontSizeList_GetSelectedFontSize(
    IN PFONTSIZE_LIST_CTL SizeList)
{
    INT nSel;
    LONG FontSize;
    PWCHAR pszNext = NULL;
    WCHAR szFontSize[100];

    if (SizeList->UseRasterOrTTList)
    {
        /* Raster font size (in pixels) */

        nSel = (INT)SendMessageW(SizeList->RasterSizeList.hWndList, LB_GETCURSEL, 0, 0);
        if (nSel == LB_ERR) return 0;

        FontSize = (LONG)SizeList->RasterSizeList.GetData(&SizeList->RasterSizeList, nSel);
        if (FontSize == LB_ERR) return 0;

        SizeList->CurrentRasterSize = FontSize;
    }
    else
    {
        /* TrueType font size (in pixels or points) */

        if (!SizeList->bIsTTSizeDirty)
        {
            /*
             * The user just selected an existing size, read the ComboBox selection.
             *
             * See: https://support.microsoft.com/en-us/help/66365/how-to-process-a-cbn-selchange-notification-message
             * for more details.
             */
            INT Length;

            nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_GETCURSEL, 0, 0);
            if (nSel == CB_ERR) return 0;

            Length = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_GETLBTEXTLEN, nSel, 0);
            ASSERT((Length != LB_ERR) && (Length < ARRAYSIZE(szFontSize)));

            Length = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_GETLBTEXT, nSel, (LPARAM)szFontSize);
            ASSERT((Length != LB_ERR) && (Length < ARRAYSIZE(szFontSize)));
            szFontSize[Length] = L'\0';

            /* Validate the font size */
            FontSize = wcstoul(szFontSize, &pszNext, 10);
            if ((FontSize == 0) || (*pszNext))
                return 0;
        }
        else
        {
            /* Read the ComboBox edit string, as the user has entered a custom size */
            // ComboBox_GetText(...)
            GetWindowTextW(SizeList->hWndTTSizeList, szFontSize, ARRAYSIZE(szFontSize));

            /* Validate the font size */
            FontSize = wcstoul(szFontSize, &pszNext, 10);
            if ((FontSize == 0) || (*pszNext))
                return 0;

            /* Find if the font size already exists in the list; if not, add it */
            nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_FINDSTRINGEXACT, 0, (LPARAM)szFontSize);
            if (nSel == CB_ERR)
            {
                nSel = (INT)SendMessageW(SizeList->hWndTTSizeList, CB_ADDSTRING, -1, (LPARAM)szFontSize);
                //// ComboBox_SetText(...)
                //SetWindowTextW(SizeList->hWndTTSizeList, szFontSize);
                //SizeList->bIsTTSizeDirty = TRUE;
            }
            SendMessageW(SizeList->hWndTTSizeList, CB_SETCURSEL, (WPARAM)nSel, 0);
        }

        SizeList->bIsTTSizeDirty = FALSE;

        SizeList->CurrentTTSize = FontSize;

        /*
         * If the font size is given in points, instead of pixels,
         * convert it into logical size.
         */
        if (!SizeList->TTSizePixelUnit)
            FontSize = -PointSizeToLogicalSize(NULL, FontSize);
    }

    return FontSize;
}


static VOID
AddFontToList(
    IN HWND hWndList,
    IN LPCWSTR pszFaceName,
    IN DWORD FontType)
{
    INT iItem;

    /* Make sure the font doesn't already exist in the list */
    if (SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)pszFaceName) != LB_ERR)
        return;

    /* Add the font */
    iItem = (INT)SendMessageW(hWndList, LB_ADDSTRING, 0, (LPARAM)pszFaceName);
    if (iItem == LB_ERR)
    {
        DPRINT1("Failed to add font '%S'\n", pszFaceName);
        return;
    }

    DPRINT1("Add font '%S'\n", pszFaceName);

    /* Store this information in the list-item's userdata area */
    // SendMessageW(hWndList, LB_SETITEMDATA, idx, MAKELPARAM(fFixed, fTrueType));
    SendMessageW(hWndList, LB_SETITEMDATA, iItem, (LPARAM)FontType);
}

typedef struct _FACE_NAMES_PROC_PARAM
{
    HWND hWndList;
    UINT CodePage;
} FACE_NAMES_PROC_PARAM, *PFACE_NAMES_PROC_PARAM;

static BOOL CALLBACK
EnumFaceNamesProc(
    IN PLOGFONTW lplf,
    IN PNEWTEXTMETRICW lpntm,
    IN DWORD  FontType,
    IN LPARAM lParam)
{
    PFACE_NAMES_PROC_PARAM Param = (PFACE_NAMES_PROC_PARAM)lParam;

    if (IsValidConsoleFont2(lplf, lpntm, FontType, Param->CodePage))
    {
        /* Add the face name to the list */
        AddFontToList(Param->hWndList, lplf->lfFaceName, FontType);
    }

    /* Continue enumerating the faces */
    return TRUE;
}

static BOOL CALLBACK
EnumFontSizesProc(
    IN PLOGFONTW lplf,
    IN PNEWTEXTMETRICW lpntm,
    IN DWORD  FontType,
    IN LPARAM lParam)
{
    PFONTSIZE_LIST_CTL SizeList = (PFONTSIZE_LIST_CTL)lParam;
    UINT iItem, iDupItem;
    WCHAR szFontSize[100];

    if (FontType != TRUETYPE_FONTTYPE)
    {
        WPARAM FontSize;

        /*
         * Format:
         * Width  = FontSize.X = LOWORD(FontSize);
         * Height = FontSize.Y = HIWORD(FontSize);
         */

        StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d x %d", lplf->lfWidth, lplf->lfHeight);
        FontSize = MAKEWPARAM(lplf->lfWidth, lplf->lfHeight);

        /* Add the font size into the list, sorted by size value. Avoid any duplicates. */
        /* Store this information in the list-item's userdata area */
        iDupItem = LB_ERR;
        iItem = BisectListSortedByValue(&SizeList->RasterSizeList, FontSize, &iDupItem, TRUE);
        if (iItem == LB_ERR)
            iItem = 0;
        if (iDupItem == LB_ERR)
        {
            iItem = (UINT)SendMessageW(SizeList->RasterSizeList.hWndList, LB_INSERTSTRING, iItem, (LPARAM)szFontSize);
            if (iItem != LB_ERR && iItem != LB_ERRSPACE)
                iItem = SendMessageW(SizeList->RasterSizeList.hWndList, LB_SETITEMDATA, iItem, FontSize);
        }

        return TRUE;
    }
    else
    {
        /* TrueType or vectored font: list all the hardcoded font points */
        ULONG i;
        for (i = 0; i < ARRAYSIZE(TrueTypePoints); ++i)
        {
            // _ultow(szFontSize, TrueTypePoints[i], 10);
            StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", TrueTypePoints[i]);

            /* Add the font size into the list, sorted by size value (string). Avoid any duplicates. */
            if (SendMessageW(SizeList->hWndTTSizeList, CB_FINDSTRINGEXACT, 0, (LPARAM)szFontSize) == CB_ERR)
                iItem = (UINT)SendMessageW(SizeList->hWndTTSizeList, CB_INSERTSTRING, -1, (LPARAM)szFontSize);
        }

        /* Stop the enumeration now */
        return FALSE;
    }
}

static VOID
FaceNameList_Initialize(
    IN HWND hWndList,
    IN UINT CodePage)
{
    FACE_NAMES_PROC_PARAM Param;
    HDC hDC;
    LOGFONTW lf;
    INT idx;

    /* Reset the face names list */
    SendMessageW(hWndList, LB_RESETCONTENT, 0, 0);

    Param.hWndList = hWndList;
    Param.CodePage = CodePage;

    ZeroMemory(&lf, sizeof(lf));
    lf.lfCharSet = DEFAULT_CHARSET; // CodePageToCharSet(CodePage);
    // lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;

    hDC = GetDC(NULL);
    EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFaceNamesProc, (LPARAM)&Param, 0);
    ReleaseDC(NULL, hDC);

    idx = (INT)SendMessageW(hWndList, LB_GETCOUNT, 0, 0);
    if (idx != LB_ERR && idx != 0)
    {
        /* We have found some faces and filled the list, we are fine! */
        return;
    }

    /* No fonts were found. Manually add default ones into the list. */
    DPRINT1("The ideal console fonts were not found; manually add default ones.\n");

    AddFontToList(hWndList, L"Terminal", RASTER_FONTTYPE);
#if 0
    // TODO: insert only the *single* default TT font, that should
    // be found in the TT font cache with the codepage number 0.
    AddFontToList(hWndList, L"Lucida Console", TRUETYPE_FONTTYPE);
#endif
}

static VOID
FaceNameList_SelectFont(
    IN HWND hDlg,
    IN HWND hWndList,
    IN PFONTSIZE_LIST_CTL SizeList,
    IN LPCWSTR FaceName,
    IN ULONG FontFamily,
    IN ULONG FontWeight,
    IN COORD FontSize)
{
    INT iItem;

    iItem = (INT)SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)FaceName);
    if (iItem == LB_ERR)
        iItem = (INT)SendMessageW(hWndList, LB_FINDSTRINGEXACT, 0, (LPARAM)L"Terminal");
    if (iItem == LB_ERR)
        iItem = 0;
    SendMessageW(hWndList, LB_SETCURSEL, (WPARAM)iItem, 0);

    if (FontWeight >= FW_BOLD)
        CheckDlgButton(hDlg, IDC_CHECK_BOLD_FONTS, BST_CHECKED);
    else
        CheckDlgButton(hDlg, IDC_CHECK_BOLD_FONTS, BST_UNCHECKED);

    /* Select the current font size */
    /*
     * Format:
     * Width  = FontSize.X = LOWORD(FontSize);
     * Height = FontSize.Y = HIWORD(FontSize);
     */
    SizeList->CurrentRasterSize = MAKELONG(FontSize.X, FontSize.Y);
    SizeList->CurrentTTSize = FontSize.Y;
    // FontSizeList_SelectFontSize(SizeList, SizeList->CurrentRasterSize);

    // return iItem;
}

static VOID
UpdateFontSizeList(
    IN HWND hDlg,
    IN PFONTSIZE_LIST_CTL SizeList)
{
    HWND hDlgItem;

    if (SizeList->UseRasterOrTTList)
    {
        /*
         * Raster font: show the Raster size list, and
         * hide the TrueType size list and the units.
         */

        // EnableDlgItem(hDlg, IDC_CHECK_BOLD_FONTS, FALSE);

        hDlgItem = GetDlgItem(hDlg, IDC_RADIO_PIXEL_UNIT);
        ShowWindow(hDlgItem, SW_HIDE);
        EnableWindow(hDlgItem, FALSE);

        hDlgItem = GetDlgItem(hDlg, IDC_RADIO_POINT_UNIT);
        ShowWindow(hDlgItem, SW_HIDE);
        EnableWindow(hDlgItem, FALSE);

        hDlgItem = SizeList->hWndTTSizeList;
        ShowWindow(hDlgItem, SW_HIDE);
        EnableWindow(hDlgItem, FALSE);

        hDlgItem = SizeList->RasterSizeList.hWndList;
        EnableWindow(hDlgItem, TRUE);
        ShowWindow(hDlgItem, SW_SHOW);
    }
    else
    {
        /*
         * TrueType font: show the TrueType size list
         * and the units, and hide the Raster size list.
         */

        // EnableDlgItem(hDlg, IDC_CHECK_BOLD_FONTS, TRUE);

        hDlgItem = SizeList->RasterSizeList.hWndList;
        ShowWindow(hDlgItem, SW_HIDE);
        EnableWindow(hDlgItem, FALSE);

        hDlgItem = SizeList->hWndTTSizeList;
        EnableWindow(hDlgItem, TRUE);
        ShowWindow(hDlgItem, SW_SHOW);

        hDlgItem = GetDlgItem(hDlg, IDC_RADIO_PIXEL_UNIT);
        EnableWindow(hDlgItem, TRUE);
        ShowWindow(hDlgItem, SW_SHOW);

        hDlgItem = GetDlgItem(hDlg, IDC_RADIO_POINT_UNIT);
        EnableWindow(hDlgItem, TRUE);
        ShowWindow(hDlgItem, SW_SHOW);
    }
}

static BOOL
FontSizeChange(
    IN HWND hDlg,
    IN PFONTSIZE_LIST_CTL SizeList,
    IN OUT PCONSOLE_STATE_INFO pConInfo);

static BOOL
FontTypeChange(
    IN HWND hDlg,
    IN PFONTSIZE_LIST_CTL SizeList,
    IN OUT PCONSOLE_STATE_INFO pConInfo)
{
    HWND hFontList = GetDlgItem(hDlg, IDC_LBOX_FONTTYPE);
    INT Length, nSel;
    LPWSTR FaceName;
    DWORD FontType;
    LPCWSTR FontGrpBoxLabelTpl = NULL;
    WCHAR FontGrpBoxLabel[260];

    nSel = (INT)SendMessageW(hFontList, LB_GETCURSEL, 0, 0);
    if (nSel == LB_ERR) return FALSE;

    /*
     * This is disabled, because there can be external parameters
     * that may have changed (e.g. ConInfo->FontWeight, code page, ...)
     * and that we don't control here, and that need a font refresh.
     */
#if 0
    /* Check whether the selection has changed */
    if (nSel == CurrentSelFont)
        return FALSE;
#endif

    Length = (INT)SendMessageW(hFontList, LB_GETTEXTLEN, nSel, 0);
    if (Length == LB_ERR) return FALSE;

    FaceName = HeapAlloc(GetProcessHeap(),
                         HEAP_ZERO_MEMORY,
                         (Length + 1) * sizeof(WCHAR));
    if (FaceName == NULL) return FALSE;

    Length = (INT)SendMessageW(hFontList, LB_GETTEXT, nSel, (LPARAM)FaceName);
    ASSERT(Length != LB_ERR);
    FaceName[Length] = L'\0';

    StringCchCopyW(pConInfo->FaceName, ARRAYSIZE(pConInfo->FaceName), FaceName);
    DPRINT("pConInfo->FaceName = '%S'\n", pConInfo->FaceName);

    /*
     * Retrieve the read-only font group box label string template,
     * and set the group box label to the name of the selected font.
     */
    Length = LoadStringW(hApplet, IDS_GROUPBOX_FONT_NAME, (LPWSTR)&FontGrpBoxLabelTpl, 0);
    if (FontGrpBoxLabelTpl && Length > 0)
    {
        StringCchCopyNW(FontGrpBoxLabel, ARRAYSIZE(FontGrpBoxLabel), FontGrpBoxLabelTpl, Length);
        StringCchCatW(FontGrpBoxLabel, ARRAYSIZE(FontGrpBoxLabel), FaceName);
        SetDlgItemTextW(hDlg, IDC_GROUPBOX_FONT_NAME, FontGrpBoxLabel);
    }

    HeapFree(GetProcessHeap(), 0, FaceName);

    /*
     * Reset the font size list, only:
     * - if we have changed the type of font, or
     * - if the font type is the same and is RASTER but the font has changed.
     * Otherwise, if the font type is not RASTER and has not changed,
     * we always display the TrueType default sizes and we don't need to
     * recreate the list when we change between different TrueType fonts.
     */
    FontType = (DWORD)SendMessageW(hFontList, LB_GETITEMDATA, nSel, 0);
    if (FontType != LB_ERR)
    {
        SizeList->UseRasterOrTTList = (FontType == RASTER_FONTTYPE);

        /* Display the correct font size list (if needed) */
        if (CurrentFontType != FontType)
            UpdateFontSizeList(hDlg, SizeList);

        /* Enumerate the available sizes for the selected font */
        if ((CurrentFontType != FontType) ||
            (FontType == RASTER_FONTTYPE && CurrentSelFont != nSel))
        {
            LOGFONTW lf;
            HDC hDC;

            if (SizeList->UseRasterOrTTList)
                SendMessageW(SizeList->RasterSizeList.hWndList, LB_RESETCONTENT, 0, 0);
            else
                SendMessageW(SizeList->hWndTTSizeList, CB_RESETCONTENT, 0, 0);

            ZeroMemory(&lf, sizeof(lf));
            lf.lfCharSet = DEFAULT_CHARSET; // CodePageToCharSet(pConInfo->CodePage);
            // lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
            StringCchCopyW(lf.lfFaceName, ARRAYSIZE(lf.lfFaceName), pConInfo->FaceName);

            hDC = GetDC(NULL);
            EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontSizesProc, (LPARAM)SizeList, 0);
            ReleaseDC(NULL, hDC);

            /* Re-select the current font size */
            if (SizeList->UseRasterOrTTList)
                FontSizeList_SelectFontSize(SizeList, SizeList->CurrentRasterSize);
            else
                FontSizeList_SelectFontSize(SizeList, SizeList->CurrentTTSize);
        }
    }
    else
    {
        /* We failed, display the raster fonts size list */
        SizeList->UseRasterOrTTList = TRUE;
        UpdateFontSizeList(hDlg, SizeList);
    }
    CurrentFontType = FontType;
    CurrentSelFont  = nSel;

    FontSizeChange(hDlg, SizeList, pConInfo);
    return TRUE;
}

static BOOL
FontSizeChange(
    IN HWND hDlg,
    IN PFONTSIZE_LIST_CTL SizeList,
    IN OUT PCONSOLE_STATE_INFO pConInfo)
{
    LONG FontSize;
    UINT CharWidth, CharHeight;
    HFONT hFont;
    WCHAR szFontSize[100];

    /*
     * Retrieve the current selected font size.
     * - If SizeList->UseRasterOrTTList is TRUE, or if it is FALSE but
     *   if SizeList->TTSizePixelUnit is TRUE, then the font size is in pixels;
     * - If SizeList->TTSizePixelUnit is FALSE, then the font size is in points.
     */
    FontSize = FontSizeList_GetSelectedFontSize(SizeList);
    if (FontSize == 0)
        return FALSE; // We have got an invalid font size...

    /*
     * For TrueType fonts we set the requested width to zero
     * so as to obtain a default aspect-ratio width.
     */
    CharHeight = (UINT)(SizeList->UseRasterOrTTList ? HIWORD(FontSize) : FontSize);
    CharWidth  = (UINT)(SizeList->UseRasterOrTTList ? LOWORD(FontSize) : 0);

    hFont = CreateConsoleFont2((LONG)CharHeight, (LONG)CharWidth, pConInfo);
    if (!hFont)
    {
        DPRINT1("FontSizeChange: CreateConsoleFont2() failed\n");
        return FALSE;
    }

    /* Retrieve the real character size in pixels */
    GetFontCellSize(NULL, hFont, &CharHeight, &CharWidth);

    /*
     * Update the font preview as well, and store the font handle. It will be
     * freed at later update or when the font preview is refreshed or reset.
     * For TrueType fonts, the preview will show the actual character width.
     */
    UpdateFontPreview(&FontPreview, hFont, CharWidth, CharHeight);

    /*
     * Format:
     * Width  = FontSize.X = LOWORD(FontSize);
     * Height = FontSize.Y = HIWORD(FontSize);
     */
    pConInfo->FontSize.X = (SHORT)(SizeList->UseRasterOrTTList ? CharWidth : 0);
    pConInfo->FontSize.Y = (SHORT)CharHeight;

    DPRINT("pConInfo->FontSize = (%d x %d) ; (CharWidth x CharHeight) = (%d x %d)\n",
           pConInfo->FontSize.X, pConInfo->FontSize.Y, CharWidth, CharHeight);

    InvalidateRect(GetDlgItem(hDlg, IDC_STATIC_FONT_WINDOW_PREVIEW), NULL, TRUE);
    InvalidateRect(GetDlgItem(hDlg, IDC_STATIC_SELECT_FONT_PREVIEW), NULL, TRUE);

    StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", CharWidth);
    SetDlgItemText(hDlg, IDC_FONT_SIZE_X, szFontSize);
    StringCchPrintfW(szFontSize, ARRAYSIZE(szFontSize), L"%d", CharHeight);
    SetDlgItemText(hDlg, IDC_FONT_SIZE_Y, szFontSize);

    return TRUE;
}


INT_PTR
CALLBACK
FontProc(HWND hDlg,
         UINT uMsg,
         WPARAM wParam,
         LPARAM lParam)
{
    PFONTSIZE_LIST_CTL SizeList;

    SizeList = (PFONTSIZE_LIST_CTL)GetWindowLongPtrW(hDlg, DWLP_USER);

    switch (uMsg)
    {
        case WM_INITDIALOG:
        {
            SizeList = (PFONTSIZE_LIST_CTL)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*SizeList));
            if (!SizeList)
            {
                EndDialog(hDlg, 0);
                return (INT_PTR)TRUE;
            }
            SizeList->RasterSizeList.hWndList = GetDlgItem(hDlg, IDC_LBOX_FONTSIZE);
            SizeList->RasterSizeList.GetCount = RasterSizeList_GetCount;
            SizeList->RasterSizeList.GetData  = RasterSizeList_GetData;
            SizeList->hWndTTSizeList = GetDlgItem(hDlg, IDC_CBOX_FONTSIZE);
            SizeList->bIsTTSizeDirty = FALSE;
            SetWindowLongPtrW(hDlg, DWLP_USER, (LONG_PTR)SizeList);

            /* By default show the raster font size list */
            SizeList->UseRasterOrTTList = TRUE;

            /* By default show the font sizes in pixel units */
            CheckRadioButton(hDlg, IDC_RADIO_PIXEL_UNIT, IDC_RADIO_POINT_UNIT, IDC_RADIO_PIXEL_UNIT);
            SizeList->TTSizePixelUnit = TRUE;

            UpdateFontSizeList(hDlg, SizeList);

            DPRINT1("ConInfo->FaceName = '%S'\n", ConInfo->FaceName);

            /* Face names list and current font selection will be done during PSN_SETACTIVE notification */
            // CurrentCodePage = INVALID_CP;

            return TRUE;
        }

        case WM_DESTROY:
        {
            if (SizeList)
                HeapFree(GetProcessHeap(), 0, SizeList);
            return (INT_PTR)TRUE;
        }

        case WM_DRAWITEM:
        {
            LPDRAWITEMSTRUCT drawItem = (LPDRAWITEMSTRUCT)lParam;

            if (drawItem->CtlID == IDC_STATIC_SELECT_FONT_PREVIEW)
                PaintText(drawItem, ConInfo, Screen);

            return TRUE;
        }

        case WM_DISPLAYCHANGE:
        {
            /* Retransmit to the preview window */
            SendDlgItemMessageW(hDlg, IDC_STATIC_FONT_WINDOW_PREVIEW,
                                WM_DISPLAYCHANGE, wParam, lParam);
            break;
        }

        case WM_FONTCHANGE:
        {
            /* The pool of font resources has changed, re-enumerate the fonts */
            HWND hFontList = GetDlgItem(hDlg, IDC_LBOX_FONTTYPE);

            /* Initialize the font list */
            FaceNameList_Initialize(hFontList, ConInfo->CodePage);

            /* Select the current font */
            FaceNameList_SelectFont(hDlg, hFontList,
                                    SizeList,
                                    ConInfo->FaceName,
                                    ConInfo->FontFamily,
                                    ConInfo->FontWeight,
                                    ConInfo->FontSize);

            /* Refresh everything */
            FontTypeChange(hDlg, SizeList, ConInfo);
            break;
        }

        case WM_NOTIFY:
        {
            switch (((LPNMHDR)lParam)->code)
            {
                case PSN_APPLY:
                {
                    ApplyConsoleInfo(hDlg);
                    return TRUE;
                }

                case PSN_SETACTIVE:
                {
                    /* Check whether the current code page has changed.
                     * If so, re-enumerate the fonts. */
                    if (CurrentCodePage != ConInfo->CodePage)
                    {
                        HWND hFontList;

                        /* Save the new code page */
                        CurrentCodePage = ConInfo->CodePage;

                        hFontList = GetDlgItem(hDlg, IDC_LBOX_FONTTYPE);

                        /* Initialize the font list */
                        FaceNameList_Initialize(hFontList, ConInfo->CodePage);

                        /* Select the current font */
                        FaceNameList_SelectFont(hDlg, hFontList,
                                                SizeList,
                                                ConInfo->FaceName,
                                                ConInfo->FontFamily,
                                                ConInfo->FontWeight,
                                                ConInfo->FontSize);

                        /* Refresh everything */
                        FontTypeChange(hDlg, SizeList, ConInfo);
                    }

                    /* Fall back to default behaviour */
                    break;
                }
            }

            break;
        }

        case WM_COMMAND:
        {
            if (HIWORD(wParam) == LBN_SELCHANGE /* || CBN_SELCHANGE */)
            {
                switch (LOWORD(wParam))
                {
                    case IDC_LBOX_FONTTYPE:
                    {
                        /* Change the property sheet state only if the font has really changed */
                        if (FontTypeChange(hDlg, SizeList, ConInfo))
                            PropSheet_Changed(GetParent(hDlg), hDlg);
                        break;
                    }

                    case IDC_LBOX_FONTSIZE:
                    case IDC_CBOX_FONTSIZE:
                    {
                        /* Change the property sheet state only if the font has really changed */
                        if (FontSizeChange(hDlg, SizeList, ConInfo))
                            PropSheet_Changed(GetParent(hDlg), hDlg);
                        break;
                    }
                }
            }
            /* NOTE: CBN_EDITUPDATE is sent first, and is followed by CBN_EDITCHANGE */
            else if (HIWORD(wParam) == CBN_EDITUPDATE && LOWORD(wParam) == IDC_CBOX_FONTSIZE)
            {
                ULONG FontSize;
                PWCHAR pszNext = NULL;
                WCHAR szFontSize[100];
                WCHAR szMessage[260];

                /* Read the ComboBox edit string, as the user has entered a custom size */
                GetWindowTextW(SizeList->hWndTTSizeList, szFontSize, ARRAYSIZE(szFontSize));

                /* Validate the font size */
                FontSize = wcstoul(szFontSize, &pszNext, 10);
                if ((FontSize == 0) || (*pszNext))
                {
                    // FIXME: Localize!
                    StringCchPrintfW(szMessage, ARRAYSIZE(szMessage), L"\"%s\" is not a valid font size.", szFontSize);
                    MessageBoxW(hDlg, szMessage, L"Error", MB_ICONINFORMATION | MB_OK);
                }
                /**/SizeList->bIsTTSizeDirty = TRUE;/**/
            }
            else if (HIWORD(wParam) == CBN_KILLFOCUS && LOWORD(wParam) == IDC_CBOX_FONTSIZE)
            {
                /* Change the property sheet state only if the font has really changed */
                if (FontSizeChange(hDlg, SizeList, ConInfo))
                    PropSheet_Changed(GetParent(hDlg), hDlg);
            }
            else
            if (HIWORD(wParam) == BN_CLICKED)
            {
                switch (LOWORD(wParam))
                {
                case IDC_CHECK_BOLD_FONTS:
                {
                    if (IsDlgButtonChecked(hDlg, IDC_CHECK_BOLD_FONTS) == BST_CHECKED)
                        ConInfo->FontWeight = FW_BOLD;
                    else
                        ConInfo->FontWeight = FW_NORMAL;

                    FontTypeChange(hDlg, SizeList, ConInfo);
                    PropSheet_Changed(GetParent(hDlg), hDlg);
                    break;
                }

                case IDC_RADIO_PIXEL_UNIT:
                case IDC_RADIO_POINT_UNIT:
                {
                    SizeList->TTSizePixelUnit = (LOWORD(wParam) == IDC_RADIO_PIXEL_UNIT);

                    /* The call is valid only for TrueType fonts */
                    if (CurrentFontType != TRUETYPE_FONTTYPE)
                        break;

                    /* Change the property sheet state only if the font has really changed */
                    if (FontSizeChange(hDlg, SizeList, ConInfo))
                        PropSheet_Changed(GetParent(hDlg), hDlg);
                    break;
                }
                }
            }

            break;
        }

        default:
            break;
    }

    return FALSE;
}
