/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    fontdlg.dlg

Abstract:

    This module contains the code for console font dialog

Author:

    Therese Stowell (thereses) Feb-3-1992 (swiped from Win3.1)

Revision History:

--*/

#include "precomp.h"
#pragma hdrstop
#include "fontdlg.h"


/* ----- Prototypes ----- */

int FontListCreate(
    HWND hDlg,
    LPTSTR ptszTTFace,
    BOOL bNewFaceList
    );

BOOL PreviewUpdate(
    HWND hDlg,
    BOOL bLB
    );

int SelectCurrentSize(
    HWND hDlg,
    BOOL bLB,
    int FontIndex);

BOOL PreviewInit(
    HWND hDlg);

VOID DrawItemFontList(
    const LPDRAWITEMSTRUCT lpdis);

/* ----- Globals ----- */

const TCHAR g_szPreviewText[] = \
    TEXT("C:\\WINDOWS> dir                       \n") \
    TEXT("SYSTEM       <DIR>     10-01-99   5:00a\n") \
    TEXT("SYSTEM32     <DIR>     10-01-99   5:00a\n") \
    TEXT("README   TXT     26926 10-01-99   5:00a\n") \
    TEXT("WINDOWS  BMP     46080 10-01-99   5:00a\n") \
    TEXT("NOTEPAD  EXE    337232 10-01-99   5:00a\n") \
    TEXT("CLOCK    AVI     39594 10-01-99   5:00p\n") \
    TEXT("WIN      INI      7005 10-01-99   5:00a\n");

HBITMAP hbmTT = NULL; // handle of TT logo bitmap
BITMAP bmTT;          // attributes of TT source bitmap
int dyFacelistItem;   // height of Item in Facelist listbox

BOOL gbPointSizeError = FALSE;
BOOL gbBold = FALSE;
#if defined(FE_SB)
BOOL fChangeCodePage = FALSE;


BOOL
SelectCurrentFont(
    HWND hDlg,
    int FontIndex
    );
#endif

// Globals strings loaded from resource
TCHAR tszSelectedFont[CCH_SELECTEDFONT+1];
TCHAR tszRasterFonts[CCH_RASTERFONTS+1];


INT_PTR
APIENTRY
FontDlgProc(
    HWND hDlg,
    UINT wMsg,
    WPARAM wParam,
    LPARAM lParam
    )

/*++

    Dialog proc for the font selection dialog box.
    Returns the near offset into the far table of LOGFONT structures.

--*/

{
    HWND hWndFocus;
    HWND hWndList;
    int FontIndex;
    BOOL bLB;
    TEXTMETRIC tm;
    HDC hDC;

    switch (wMsg) {
    case WM_INITDIALOG:
        /*
         * Load the font description strings
         */
        LoadString(ghInstance, IDS_RASTERFONT,
                   tszRasterFonts, NELEM(tszRasterFonts));
        DBGPRINT(("tszRasterFonts = \"%ls\"\n", tszRasterFonts));
        ASSERT(_tcslen(tszRasterFonts) < CCH_RASTERFONTS);

        LoadString(ghInstance, IDS_SELECTEDFONT,
                   tszSelectedFont, NELEM(tszSelectedFont));
        DBGPRINT(("tszSelectedFont = \"%ls\"\n", tszSelectedFont));
        ASSERT(_tcslen(tszSelectedFont) < CCH_SELECTEDFONT);

        /* Save current font size as dialog window's user data */
#if defined(FE_SB)
        ASSERT(OEMCP != 0);
        if (gfFESystem) {
            SetWindowLongPtr(hDlg, GWLP_USERDATA,
                          MAKELONG(FontInfo[CurrentFontIndex].tmCharSet,
                                   FontInfo[CurrentFontIndex].Size.Y));
        } else {
#endif
            SetWindowLongPtr(hDlg, GWLP_USERDATA,
                          MAKELONG(FontInfo[CurrentFontIndex].Size.X,
                                   FontInfo[CurrentFontIndex].Size.Y));
#if defined(FE_SB)
        }
#endif

        /* Create the list of suitable fonts */
        gbEnumerateFaces = TRUE;
        bLB = !TM_IS_TT_FONT(gpStateInfo->FontFamily);
        gbBold = IS_BOLD(gpStateInfo->FontWeight);
        CheckDlgButton(hDlg, IDD_BOLDFONT, gbBold);
        FontListCreate(hDlg, bLB ? NULL : gpStateInfo->FaceName, TRUE);

        /* Initialize the preview window - selects current face & size too */
        bLB = PreviewInit(hDlg);
        PreviewUpdate(hDlg, bLB);

        /* Make sure the list box has the focus */
        hWndList = GetDlgItem(hDlg, bLB ? IDD_PIXELSLIST : IDD_POINTSLIST);
        SetFocus(hWndList);
        break;

    case WM_FONTCHANGE:
        gbEnumerateFaces = TRUE;
        bLB = !TM_IS_TT_FONT(gpStateInfo->FontFamily);
        FontListCreate(hDlg, NULL, TRUE);
        FontIndex = FindCreateFont(gpStateInfo->FontFamily,
                                   gpStateInfo->FaceName,
                                   gpStateInfo->FontSize,
                                   gpStateInfo->FontWeight,
                                   gpStateInfo->CodePage);
        SelectCurrentSize(hDlg, bLB, FontIndex);
        return TRUE;

#if defined(FE_SB)
    case WM_PAINT:
        if (fChangeCodePage)
        {
            fChangeCodePage = FALSE;

            /* Create the list of suitable fonts */
            bLB = !TM_IS_TT_FONT(gpStateInfo->FontFamily);
            FontIndex = FontListCreate(hDlg, !bLB ? NULL : gpStateInfo->FaceName, TRUE);
            FontIndex = FontListCreate(hDlg, bLB ? NULL : gpStateInfo->FaceName, TRUE);
            CurrentFontIndex = FontIndex;

            FontIndex = SelectCurrentSize(hDlg, bLB, FontIndex);
            SelectCurrentFont(hDlg, FontIndex);

            PreviewUpdate(hDlg, bLB);
        }
        break;
#endif

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDD_BOLDFONT:
            DBGPRINT(("WM_COMMAND to Bold Font checkbox %x\n", HIWORD(wParam)));
            gbBold = IsDlgButtonChecked(hDlg, IDD_BOLDFONT);
            goto RedoFontListAndPreview;

        case IDD_FACENAME:
            switch (HIWORD(wParam))
            {
            case LBN_SELCHANGE:
RedoFontListAndPreview:
                {
                    TCHAR atchNewFace[LF_FACESIZE];
                    LONG l;

                    DBGFONTS(("LBN_SELCHANGE from FACENAME\n"));
                    l = (LONG)SendDlgItemMessage(hDlg, IDD_FACENAME, LB_GETCURSEL, 0, 0L);
                    bLB = (BOOL)SendDlgItemMessage(hDlg, IDD_FACENAME, LB_GETITEMDATA, l, 0L);
                    if (!bLB) {
                        SendDlgItemMessage(hDlg, IDD_FACENAME, LB_GETTEXT, l, (LPARAM)atchNewFace);
                        DBGFONTS(("LBN_EDITUPDATE, got TT face \"%ls\"\n", atchNewFace));
                    }
                    FontIndex = FontListCreate(hDlg, bLB ? NULL : atchNewFace, FALSE);
                    FontIndex = SelectCurrentSize(hDlg, bLB, FontIndex);
                    PreviewUpdate(hDlg, bLB);
                    return TRUE;
                }
            }
            break;

        case IDD_POINTSLIST:
            switch (HIWORD(wParam)) {
            case CBN_SELCHANGE:
                DBGFONTS(("CBN_SELCHANGE from POINTSLIST\n"));
                PreviewUpdate(hDlg, FALSE);
                return TRUE;

            case CBN_KILLFOCUS:
                DBGFONTS(("CBN_KILLFOCUS from POINTSLIST\n"));
                if (!gbPointSizeError) {
                    hWndFocus = GetFocus();
                    if (hWndFocus != NULL && IsChild(hDlg, hWndFocus) &&
                        hWndFocus != GetDlgItem(hDlg, IDCANCEL)) {
                        PreviewUpdate(hDlg, FALSE);
                    }
                }
                return TRUE;

            default:
                DBGFONTS(("unhandled CBN_%x from POINTSLIST\n",HIWORD(wParam)));
                break;
            }
            break;

        case IDD_PIXELSLIST:
            switch (HIWORD(wParam)) {
            case LBN_SELCHANGE:
                DBGFONTS(("LBN_SELCHANGE from PIXELSLIST\n"));
                PreviewUpdate(hDlg, TRUE);
                return TRUE;

            default:
                break;
            }
            break;

        default:
            break;
        }
        break;

    case WM_NOTIFY:
        switch (((LPNMHDR)lParam)->code) {
        case PSN_KILLACTIVE:
            //
            // If the TT combo box is visible, update selection
            //
            hWndList = GetDlgItem(hDlg, IDD_POINTSLIST);
            if (hWndList != NULL && IsWindowVisible(hWndList)) {
                if (!PreviewUpdate(hDlg, FALSE)) {
                    SetDlgMsgResult(hDlg, PSN_KILLACTIVE, TRUE);
                    return TRUE;
                }
                SetDlgMsgResult(hDlg, PSN_KILLACTIVE, FALSE);
            }

            FontIndex = CurrentFontIndex;

            if (FontInfo[FontIndex].SizeWant.Y == 0) {
                // Raster Font, so save actual size
                gpStateInfo->FontSize = FontInfo[FontIndex].Size;
            } else {
                // TT Font, so save desired size
                gpStateInfo->FontSize = FontInfo[FontIndex].SizeWant;
            }

            gpStateInfo->FontWeight = FontInfo[FontIndex].Weight;
            gpStateInfo->FontFamily = FontInfo[FontIndex].Family;
            wcscpy(gpStateInfo->FaceName, FontInfo[FontIndex].FaceName);

            return TRUE;

        case PSN_APPLY:
            /*
             * Write out the state values and exit.
             */
            EndDlgPage(hDlg);
            return TRUE;
        }
        break;

    /*
     *  For WM_MEASUREITEM and WM_DRAWITEM, since there is only one
     *  owner-draw item (combobox) in the entire dialog box, we don't have
     *  to do a GetDlgItem to figure out who he is.
     */
    case WM_MEASUREITEM:
        /*
         * Load the TrueType logo bitmap
         */
        if (hbmTT == NULL) {
            hbmTT = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_TRUETYPE));
            GetObject(hbmTT, sizeof(BITMAP), &bmTT);
        }

        /*
         * Compute the height of face name listbox entries
         */
        if (dyFacelistItem == 0) {
            HFONT hFont;
            hDC = GetDC(hDlg);
            hFont = GetWindowFont(hDlg);
            if (hFont) {
                hFont = SelectObject(hDC, hFont);
            }
            GetTextMetrics(hDC, &tm);
            if (hFont) {
                SelectObject(hDC, hFont);
            }
            ReleaseDC(hDlg, hDC);
            dyFacelistItem = max(tm.tmHeight, bmTT.bmHeight);
        }
        ((LPMEASUREITEMSTRUCT)lParam)->itemHeight = dyFacelistItem;
        return TRUE;

    case WM_DRAWITEM:
        DrawItemFontList((LPDRAWITEMSTRUCT)lParam);
        return TRUE;

    case WM_DESTROY:
        /*
         * Delete the TrueType logo bitmap
         */
        if (hbmTT != NULL) {
            DeleteObject(hbmTT);
            hbmTT = NULL;
        }
        return TRUE;

    default:
        break;
    }
    return CommonDlgProc(hDlg, wMsg, wParam, lParam);
}


int
FontListCreate(
    HWND hDlg,
    LPTSTR ptszTTFace,
    BOOL bNewFaceList
    )

/*++

    Initializes the font list by enumerating all fonts and picking the
    proper ones for our list.

    Returns
        FontIndex of selected font (LB_ERR if none)
--*/

{
    TCHAR tszText[80];
    LONG lListIndex;
    ULONG i;
    HWND hWndShow;      // List or Combo box
    HWND hWndHide;    // Combo or List box
    HWND hWndFaceCombo;
    HANDLE hStockFont;
    BOOL bLB;
    int LastShowX = 0;
    int LastShowY = 0;
    int nSameSize = 0;
    UINT  CodePage = gpStateInfo->CodePage;
    BOOL  fDbcsCharSet = IS_ANY_DBCS_CHARSET( CodePageToCharSet( CodePage ) );
    BOOL  fFindTTFont = FALSE;
    LPTSTR ptszAltTTFace = NULL;
    LONG_PTR dwExStyle = 0L;

    ASSERT(OEMCP != 0); // must be initialized

    bLB = ((ptszTTFace == NULL) || (ptszTTFace[0] == TEXT('\0')));
    if (! bLB) {
        if (IsAvailableTTFont(ptszTTFace)) {
            ptszAltTTFace = GetAltFaceName(ptszTTFace);
        }
        else {
            ptszAltTTFace = ptszTTFace;
        }
    }
    DBGFONTS(("FontListCreate %lx, %s, %s new FaceList\n", hDlg,
            bLB ? "Raster" : "TrueType",
            bNewFaceList ? "Make" : "No" ));

    /*
     * This only enumerates face names if necessary, and
     * it only enumerates font sizes if necessary
     */

    EnumerateFonts(bLB ? EF_OEMFONT : EF_TTFONT);

    /* init the TTFaceNames */

    DBGFONTS(("  Create %s fonts\n", bLB ? "Raster" : "TrueType"));

    if (bNewFaceList) {
        PFACENODE panFace;
        hWndFaceCombo = GetDlgItem(hDlg, IDD_FACENAME);

        SendMessage(hWndFaceCombo, LB_RESETCONTENT, 0, 0);

        lListIndex = (LONG)SendMessage(hWndFaceCombo, LB_ADDSTRING, 0, (LPARAM)tszRasterFonts);
        SendMessage(hWndFaceCombo, LB_SETITEMDATA, lListIndex, TRUE);
        DBGFONTS(("Added \"%ls\", set Item Data %d = TRUE\n", tszRasterFonts, lListIndex));
        for (panFace = gpFaceNames; panFace; panFace = panFace->pNext) {
            if ((panFace->dwFlag & (EF_TTFONT|EF_NEW)) != (EF_TTFONT|EF_NEW)) {
                continue;
            }
            if (!fDbcsCharSet && (panFace->dwFlag & EF_DBCSFONT)) {
                continue;
            }

            if ((fDbcsCharSet && IsAvailableTTFontCP(panFace->atch, CodePage)) ||
                (!fDbcsCharSet && IsAvailableTTFontCP(panFace->atch, 0)))
            {

                if ( !bLB &&
                     (_tcscmp(ptszTTFace, panFace->atch) == 0 ||
                      _tcscmp(ptszAltTTFace, panFace->atch) == 0)
                   ) {
                    fFindTTFont = TRUE;
                }
                lListIndex = (LONG)SendMessage(hWndFaceCombo, LB_ADDSTRING, 0,
                                         (LPARAM)panFace->atch);
                SendMessage(hWndFaceCombo, LB_SETITEMDATA, lListIndex, FALSE);
                DBGFONTS(("Added \"%ls\", set Item Data %d = FALSE\n",
                          panFace->atch, lListIndex));
            }
        }
        if (!bLB && !fFindTTFont) {

            for (panFace = gpFaceNames; panFace; panFace = panFace->pNext) {

                if ((panFace->dwFlag & (EF_TTFONT|EF_NEW)) != (EF_TTFONT|EF_NEW)) {
                    continue;
                }
                if (!fDbcsCharSet && (panFace->dwFlag & EF_DBCSFONT)) {
                    continue;
                }

                if (( fDbcsCharSet && IsAvailableTTFontCP(panFace->atch, CodePage)) ||
                    (!fDbcsCharSet && IsAvailableTTFontCP(panFace->atch, 0)))
                {

                    if (_tcscmp(ptszTTFace, panFace->atch) != 0) {
                        _tcscpy(ptszTTFace, panFace->atch);
                        break;
                    }
                }
            }
        }
    } // if(bNewFaceList)

    hWndShow = GetDlgItem(hDlg, IDD_BOLDFONT);
#if defined(FE_SB)
    /*
     * For JAPAN, We uses "MS Gothic" TT font.
     * So, Bold of this font is not 1:2 width between SBCS:DBCS.
     */
    if (fDbcsCharSet && IsDisableBoldTTFont(ptszTTFace)) {
        EnableWindow(hWndShow, FALSE);
        gbBold = FALSE;
        CheckDlgButton(hDlg, IDD_BOLDFONT, FALSE);
    } else {
#endif
        CheckDlgButton(hDlg, IDD_BOLDFONT, (bLB || !gbBold) ? FALSE : TRUE);
        EnableWindow(hWndShow, bLB ? FALSE : TRUE);
#if defined(FE_SB)
    }
#endif

    hWndHide = GetDlgItem(hDlg, bLB ? IDD_POINTSLIST : IDD_PIXELSLIST);
    ShowWindow(hWndHide, SW_HIDE);
    EnableWindow(hWndHide, FALSE);

    hWndShow = GetDlgItem(hDlg, bLB ? IDD_PIXELSLIST : IDD_POINTSLIST);
//    hStockFont = GetStockObject(SYSTEM_FIXED_FONT);
//    SendMessage(hWndShow, WM_SETFONT, (DWORD)hStockFont, FALSE);
    ShowWindow(hWndShow, SW_SHOW);
    EnableWindow(hWndShow, TRUE);

#if defined(FE_SB)
    if (bNewFaceList) {
        lcbRESETCONTENT(hWndShow, bLB);
    }
#endif

    dwExStyle = GetWindowLongPtr(hWndShow, GWL_EXSTYLE);
    if((dwExStyle & WS_EX_LAYOUTRTL) && !(dwExStyle & WS_EX_RTLREADING))
    {
        // if mirrored RTL Reading means LTR !!
        SetWindowLongPtr(hWndShow, GWL_EXSTYLE, dwExStyle | WS_EX_RTLREADING);
    }

    /* Initialize hWndShow list/combo box */

    for (i=0;i<NumberOfFonts;i++) {
        int ShowX, ShowY;

        if (!bLB == !TM_IS_TT_FONT(FontInfo[i].Family)) {
            DBGFONTS(("  Font %x not right type\n", i));
            continue;
        }
#if defined(FE_SB)
        if (fDbcsCharSet) {
            if (!IS_ANY_DBCS_CHARSET(FontInfo[i].tmCharSet)) {
                DBGFONTS(("  Font %x not right type for DBCS character set\n", i));
                continue;
            }
        }
        else {
            if (IS_ANY_DBCS_CHARSET(FontInfo[i].tmCharSet)) {
                DBGFONTS(("  Font %x not right type for SBCS character set\n", i));
                continue;
            }
        }
#endif

        if (!bLB) {
            if (_tcscmp(FontInfo[i].FaceName, ptszTTFace) != 0 &&
                _tcscmp(FontInfo[i].FaceName, ptszAltTTFace) != 0
               ) {
                /*
                 * A TrueType font, but not the one we're interested in,
                 * so don't add it to the list of point sizes.
                 */
                DBGFONTS(("  Font %x is TT, but not %ls\n", i, ptszTTFace));
                continue;
            }
            if (gbBold != IS_BOLD(FontInfo[i].Weight)) {
                DBGFONTS(("  Font %x has weight %d, but we wanted %sbold\n",
                        i, FontInfo[i].Weight, gbBold ? "" : "not "));
                continue;
            }
        }

        if (FontInfo[i].SizeWant.X > 0) {
            ShowX = FontInfo[i].SizeWant.X;
        } else {
            ShowX = FontInfo[i].Size.X;
        }
        if (FontInfo[i].SizeWant.Y > 0) {
            ShowY = FontInfo[i].SizeWant.Y;
        } else {
            ShowY = FontInfo[i].Size.Y;
        }
        /*
         * Add the size description string to the end of the right list
         */
        if (TM_IS_TT_FONT(FontInfo[i].Family)) {
            // point size
            wsprintf(tszText, TEXT("%2d"), FontInfo[i].SizeWant.Y);
        } else {
            // pixel size
            if ((LastShowX == ShowX) && (LastShowY == ShowY)) {
                nSameSize++;
            } else {
                LastShowX = ShowX;
                LastShowY = ShowY;
                nSameSize = 0;
            }
            /*
             * The number nSameSize is appended to the string to distinguish
             * between Raster fonts of the same size.  It is not intended to
             * be visible and exists off the edge of the list
             */
            if(((dwExStyle & WS_EX_RIGHT) && !(dwExStyle & WS_EX_LAYOUTRTL))
                || (!(dwExStyle & WS_EX_RIGHT) && (dwExStyle & WS_EX_LAYOUTRTL))) {
                // flip  it so that the hidden part be at the far left
                wsprintf(tszText, TEXT("#%d                %2d x %2d"),
                         nSameSize, ShowX, ShowY);
            } else {
                wsprintf(tszText, TEXT("%2d x %2d                #%d"),
                         ShowX, ShowY, nSameSize);
            }
        }
        lListIndex = lcbFINDSTRINGEXACT(hWndShow, bLB, tszText);
        if (lListIndex == LB_ERR) {
            lListIndex = lcbADDSTRING(hWndShow, bLB, tszText);
        }
        DBGFONTS(("  added %ls to %sSLIST(%lx) index %lx\n",
                tszText,
                bLB ? "PIXEL" : "POINT",
                hWndShow, lListIndex));
        lcbSETITEMDATA(hWndShow, bLB, (DWORD)lListIndex, i);
    }

    /*
     * Get the FontIndex from the currently selected item.
     * (i will be LB_ERR if no currently selected item).
     */
    lListIndex = lcbGETCURSEL(hWndShow, bLB);
    i = lcbGETITEMDATA(hWndShow, bLB, lListIndex);

    DBGFONTS(("FontListCreate returns 0x%x\n", i));
    return i;
}


/** DrawItemFontList
 *
 *  Answer the WM_DRAWITEM message sent from the font list box or
 *  facename list box.
 *
 *  Entry:
 *      lpdis     -> DRAWITEMSTRUCT describing object to be drawn
 *
 *  Returns:
 *      None.
 *
 *      The object is drawn.
 */
VOID WINAPI
DrawItemFontList(const LPDRAWITEMSTRUCT lpdis)
{
    HDC     hDC, hdcMem;
    DWORD   rgbBack, rgbText, rgbFill;
    TCHAR   tszFace[LF_FACESIZE];
    HBITMAP hOld;
    int     dy;
    HBRUSH  hbrFill;
    HWND    hWndItem;
    BOOL    bLB;
    int     dxttbmp;

    if ((int)lpdis->itemID < 0)
        return;

    hDC = lpdis->hDC;

    if (lpdis->itemAction & ODA_FOCUS) {
        if (lpdis->itemState & ODS_SELECTED) {
            DrawFocusRect(hDC, &lpdis->rcItem);
        }
    } else {
        if (lpdis->itemState & ODS_SELECTED) {
            rgbText = SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
            rgbBack = SetBkColor(hDC, rgbFill = GetSysColor(COLOR_HIGHLIGHT));
        } else {
            rgbText = SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
            rgbBack = SetBkColor(hDC, rgbFill = GetSysColor(COLOR_WINDOW));
        }
        // draw selection background
        hbrFill = CreateSolidBrush(rgbFill);
        if (hbrFill) {
            FillRect(hDC, &lpdis->rcItem, hbrFill);
            DeleteObject(hbrFill);
        }

        // get the string
        if (IsWindow(hWndItem = lpdis->hwndItem) == FALSE) {
            return;
        }
        SendMessage(hWndItem, LB_GETTEXT, lpdis->itemID, (LPARAM)tszFace);
        bLB = (BOOL)SendMessage(hWndItem, LB_GETITEMDATA, lpdis->itemID, 0L);
        dxttbmp = bLB ? 0 : bmTT.bmWidth;

        DBGFONTS(("DrawItemFontList must redraw \"%ls\" %s\n", tszFace,
                bLB ? "Raster" : "TrueType"));

        // draw the text
        TabbedTextOut(hDC, lpdis->rcItem.left + dxttbmp,
                      lpdis->rcItem.top, tszFace,
                      _tcslen(tszFace), 0, NULL, dxttbmp);

        // and the TT bitmap if needed
        if (!bLB) {
            hdcMem = CreateCompatibleDC(hDC);
            if (hdcMem) {
                hOld = SelectObject(hdcMem, hbmTT);

                dy = ((lpdis->rcItem.bottom - lpdis->rcItem.top) - bmTT.bmHeight) / 2;

                BitBlt(hDC, lpdis->rcItem.left, lpdis->rcItem.top + dy,
                       dxttbmp, dyFacelistItem, hdcMem,
                       0, 0, SRCINVERT);

                if (hOld)
                    SelectObject(hdcMem, hOld);
                DeleteDC(hdcMem);
            }
        }

        SetTextColor(hDC, rgbText);
        SetBkColor(hDC, rgbBack);

        if (lpdis->itemState & ODS_FOCUS) {
            DrawFocusRect(hDC, &lpdis->rcItem);
        }
    }
}


UINT
GetPointSizeInRange(
   HWND hDlg,
   INT Min,
   INT Max)
/*++

Routine Description:

   Get a size from the Point Size ComboBox edit field

Return Value:

   Point Size - of the edit field limited by Min/Max size
   0 - if the field is empty or invalid

--*/

{
    TCHAR szBuf[90];
    int nTmp = 0;
    BOOL bOK;

    if (GetDlgItemText(hDlg, IDD_POINTSLIST, szBuf, NELEM(szBuf))) {
        nTmp = GetDlgItemInt(hDlg, IDD_POINTSLIST, &bOK, TRUE);
        if (bOK && nTmp >= Min && nTmp <= Max) {
            return nTmp;
        }
    }

    return 0;
}


/* ----- Preview routines ----- */

LRESULT
FontPreviewWndProc(
    HWND hWnd,
    UINT wMessage,
    WPARAM wParam,
    LPARAM lParam
    )

/*  FontPreviewWndProc
 *      Handles the font preview window
 */

{
    PAINTSTRUCT ps;
    RECT rect;
    HFONT hfontOld;
    HBRUSH hbrNew;
    HBRUSH hbrOld;
    COLORREF rgbText;
    COLORREF rgbBk;

    switch (wMessage) {
    case WM_ERASEBKGND:
        break;

    case WM_PAINT:
        BeginPaint(hWnd, &ps);

        /* Draw the font sample */
        if (GetWindowLong(hWnd, GWL_ID) == IDD_COLOR_POPUP_COLORS) {
            rgbText = GetNearestColor(ps.hdc, PopupTextColor(gpStateInfo));
            rgbBk = GetNearestColor(ps.hdc, PopupBkColor(gpStateInfo));
        } else {
            rgbText = GetNearestColor(ps.hdc, ScreenTextColor(gpStateInfo));
            rgbBk = GetNearestColor(ps.hdc, ScreenBkColor(gpStateInfo));
        }
        SetTextColor(ps.hdc, rgbText);
        SetBkColor(ps.hdc, rgbBk);
        GetClientRect(hWnd, &rect);
        hfontOld = SelectObject(ps.hdc, FontInfo[CurrentFontIndex].hFont);
        hbrNew = CreateSolidBrush(rgbBk);
        hbrOld = SelectObject(ps.hdc, hbrNew);
        PatBlt(ps.hdc, rect.left, rect.top,
                rect.right - rect.left, rect.bottom - rect.top,
                PATCOPY);
        InflateRect(&rect, -2, -2);
        DrawText(ps.hdc, g_szPreviewText, -1, &rect, 0);
        SelectObject(ps.hdc, hbrOld);
        DeleteObject(hbrNew);
        SelectObject(ps.hdc, hfontOld);

        EndPaint(hWnd, &ps);
        break;

    default:
        return DefWindowProc(hWnd, wMessage, wParam, lParam);
    }
    return 0L;
}


/*
 * Get the font index for a new font
 * If necessary, attempt to create the font.
 * Always return a valid FontIndex (even if not correct)
 * Family:   Find/Create a font with of this Family
 *           0    - don't care
 * ptszFace: Find/Create a font with this face name.
 *           NULL or TEXT("")  - use DefaultFaceName
 * Size:     Must match SizeWant or actual Size.
 */
int
FindCreateFont(
    DWORD Family,
    LPTSTR ptszFace,
    COORD Size,
    LONG Weight,
    UINT CodePage)
{
#define NOT_CREATED_NOR_FOUND -1
#define CREATED_BUT_NOT_FOUND -2

    int i;
    int FontIndex = NOT_CREATED_NOR_FOUND;
    BOOL bFontOK;
    TCHAR AltFaceName[LF_FACESIZE];
    COORD AltFontSize;
    BYTE  AltFontFamily;
    ULONG AltFontIndex = 0;
    LPTSTR ptszAltFace = NULL;

    BYTE CharSet = CodePageToCharSet(CodePage);

    ASSERT(OEMCP != 0);

    DBGFONTS(("FindCreateFont Family=%x %ls (%d,%d) %d %d %x\n",
            Family, ptszFace, Size.X, Size.Y, Weight, CodePage, CharSet));

    if (gfFESystem) {
        if (IS_ANY_DBCS_CHARSET(CharSet)) {
            if (ptszFace == NULL || *ptszFace == TEXT('\0')) {
                ptszFace = DefaultFaceName;
            }
            if (Size.Y == 0) {
                Size = DefaultFontSize;
            }
        }
        else {
            MakeAltRasterFont(CodePage, &AltFontSize, &AltFontFamily, &AltFontIndex, AltFaceName);

            if (ptszFace == NULL || *ptszFace == TEXT('\0')) {
                ptszFace = AltFaceName;
            }
            if (Size.Y == 0) {
                Size.X = AltFontSize.X;
                Size.Y = AltFontSize.Y;
            }
        }
    }
    else {
        if (ptszFace == NULL || *ptszFace == TEXT('\0')) {
            ptszFace = DefaultFaceName;
        }
        if (Size.Y == 0) {
            Size = DefaultFontSize;
        }
    }


    if (IsAvailableTTFont(ptszFace)) {
        ptszAltFace = GetAltFaceName(ptszFace);
    }
    else {
        ptszAltFace = ptszFace;
    }

    /*
     * Try to find the exact font
     */
TryFindExactFont:
    for (i=0; i < (int)NumberOfFonts; i++) {
        /*
         * If looking for a particular Family, skip non-matches
         */
        if ((Family != 0) &&
                ((BYTE)Family != FontInfo[i].Family)) {
            continue;
        }

        /*
         * Skip non-matching sizes
         */
        if ((!SIZE_EQUAL(FontInfo[i].SizeWant, Size) &&
             !SIZE_EQUAL(FontInfo[i].Size, Size))) {
            continue;
        }

        /*
         * Skip non-matching weights
         */
        if ((Weight != 0) && (Weight != FontInfo[i].Weight)) {
            continue;
        }

#if defined(FE_SB)
        if (!TM_IS_TT_FONT(FontInfo[i].Family) &&
                FontInfo[i].tmCharSet != CharSet) {
            continue;
        }
#endif

        /*
         * Size (and maybe Family) match.
         *  If we don't care about the name, or if it matches, use this font.
         *  Else if name doesn't match and it is a raster font, consider it.
         */
        if ((ptszFace == NULL) || (ptszFace[0] == TEXT('\0')) ||
                (_tcscmp(FontInfo[i].FaceName, ptszFace) == 0) ||
                (_tcscmp(FontInfo[i].FaceName, ptszAltFace) == 0)
           ) {
            FontIndex = i;
            goto FoundFont;
        } else if (!TM_IS_TT_FONT(FontInfo[i].Family)) {
            FontIndex = i;
        }
    }

    if (FontIndex == NOT_CREATED_NOR_FOUND) {
        /*
         * Didn't find the exact font, so try to create it
         */
        ULONG ulOldEnumFilter;
        ulOldEnumFilter = SetFontEnumeration(0);
        SetFontEnumeration(ulOldEnumFilter & ~FE_FILTER_TRUETYPE);
        if (Size.Y < 0) {
            Size.Y = -Size.Y;
        }
        bFontOK = DoFontEnum(NULL, ptszFace, &Size.Y, 1);
        SetFontEnumeration(ulOldEnumFilter);
        if (bFontOK) {
            DBGFONTS(("FindCreateFont created font!\n"));
            FontIndex = CREATED_BUT_NOT_FOUND;
            goto TryFindExactFont;
        } else {
            DBGFONTS(("FindCreateFont failed to create font!\n"));
        }
    } else if (FontIndex >= 0) {
        // a close Raster Font fit - only the name doesn't match.
        goto FoundFont;
    }

    /*
     * Failed to find exact match, even after enumeration, so now try
     * to find a font of same family and same size or bigger
     */
    for (i=0; i < (int)NumberOfFonts; i++) {
#if defined(FE_SB)
        if (gfFESystem) {
            if ((Family != 0) &&
                    ((BYTE)Family != FontInfo[i].Family)) {
                continue;
            }

            if (!TM_IS_TT_FONT(FontInfo[i].Family) &&
                FontInfo[i].tmCharSet != CharSet) {
                continue;
            }
        }
        else {
#endif
        if ((BYTE)Family != FontInfo[i].Family) {
            continue;
        }
#if defined(FE_SB)
        }
#endif

        if (FontInfo[i].Size.Y >= Size.Y &&
                FontInfo[i].Size.X >= Size.X) {
            // Same family, size >= desired.
            FontIndex = i;
            break;
        }
    }

    if (FontIndex < 0) {
        DBGFONTS(("FindCreateFont defaults!\n"));
#if defined(FE_SB)
        if (gfFESystem) {
            if (CodePage == OEMCP) {
                FontIndex = DefaultFontIndex;
            }
            else {
                FontIndex = AltFontIndex;
            }
        }
        else {
#endif
        FontIndex = DefaultFontIndex;
#if defined(FE_SB)
        }
#endif
    }

FoundFont:
    DBGFONTS(("FindCreateFont returns %x : %ls (%d,%d)\n", FontIndex,
            FontInfo[FontIndex].FaceName,
            FontInfo[FontIndex].Size.X, FontInfo[FontIndex].Size.Y));
    return FontIndex;

#undef NOT_CREATED_NOR_FOUND
#undef CREATED_BUT_NOT_FOUND
}


/*
 * SelectCurrentSize - Select the right line of the Size listbox/combobox.
 *   bLB       : Size controls is a listbox (TRUE for RasterFonts)
 *   FontIndex : Index into FontInfo[] cache
 *               If < 0 then choose a good font.
 * Returns
 *   FontIndex : Index into FontInfo[] cache
 */
int
SelectCurrentSize(HWND hDlg, BOOL bLB, int FontIndex)
{
    int iCB;
    HWND hWndList;

    DBGFONTS(("SelectCurrentSize %lx %s %x\n",
            hDlg, bLB ? "Raster" : "TrueType", FontIndex));

    hWndList = GetDlgItem(hDlg, bLB ? IDD_PIXELSLIST : IDD_POINTSLIST);
    iCB = lcbGETCOUNT(hWndList, bLB);
    DBGFONTS(("  Count of items in %lx = %lx\n", hWndList, iCB));

    if (FontIndex >= 0) {
        /*
         * look for FontIndex
         */
        while (iCB > 0) {
            iCB--;
            if (lcbGETITEMDATA(hWndList, bLB, iCB) == FontIndex) {
                lcbSETCURSEL(hWndList, bLB, iCB);
                break;
            }
        }
    } else {
        /*
         * look for a reasonable default size: looking backwards, find
         * the first one same height or smaller.
         */
        DWORD Size;
        Size = GetWindowLong(hDlg, GWLP_USERDATA);
#if defined(FE_SB)
        ASSERT(OEMCP != 0);
        if (gfFESystem && bLB
            && (FontInfo[CurrentFontIndex].tmCharSet != LOBYTE(LOWORD(Size)))
           )
        {
            TCHAR AltFaceName[LF_FACESIZE];
            COORD AltFontSize;
            BYTE  AltFontFamily;
            ULONG AltFontIndex = 0;

            MakeAltRasterFont(gpStateInfo->CodePage, &AltFontSize, &AltFontFamily, &AltFontIndex, AltFaceName);

            while (iCB > 0) {
                iCB--;
                if (lcbGETITEMDATA(hWndList, bLB, iCB) == (int)AltFontIndex) {
                    lcbSETCURSEL(hWndList, bLB, iCB);
                    break;
                }
            }
        }
        else
#endif
        while (iCB > 0) {
            iCB--;
            FontIndex = lcbGETITEMDATA(hWndList, bLB, iCB);
            if (FontInfo[FontIndex].Size.Y <= HIWORD(Size)) {
                lcbSETCURSEL(hWndList, bLB, iCB);
                break;
            }
        }
    }
    DBGFONTS(("SelectCurrentSize returns %x\n", FontIndex));
    return FontIndex;
}


BOOL
SelectCurrentFont(HWND hDlg, int FontIndex)
{
    BOOL bLB;

    DBGFONTS(("SelectCurrentFont hDlg=%lx, FontIndex=%x\n", hDlg, FontIndex));

    bLB = !TM_IS_TT_FONT(FontInfo[FontIndex].Family);

    SendDlgItemMessage(hDlg, IDD_FACENAME, LB_SELECTSTRING, (WPARAM)-1,
            bLB ? (LPARAM)tszRasterFonts : (LPARAM)FontInfo[FontIndex].FaceName);

    SelectCurrentSize(hDlg, bLB, FontIndex);
    return bLB;
}


BOOL
PreviewInit(
    HWND hDlg
    )

/*  PreviewInit
 *      Prepares the preview code, sizing the window and the dialog to
 *      make an attractive preview.
 *  Returns TRUE if Raster Fonts, FALSE if TT Font
 */

{
    int nFont;

    DBGFONTS(("PreviewInit hDlg=%lx\n", hDlg));

    /*
     * Set the current font
     */
    nFont = FindCreateFont(gpStateInfo->FontFamily,
                           gpStateInfo->FaceName,
                           gpStateInfo->FontSize,
                           gpStateInfo->FontWeight,
                           gpStateInfo->CodePage);

    DBGPRINT(("Changing Font Number from %d to %d\n",
              CurrentFontIndex, nFont));
    CurrentFontIndex = nFont;

    return SelectCurrentFont(hDlg, nFont);
}


BOOL
PreviewUpdate(
    HWND hDlg,
    BOOL bLB
    )

/*++

    Does the preview of the selected font.

--*/

{
    PFONT_INFO lpFont;
    int FontIndex;
    LONG lIndex;
    HWND hWnd;
    TCHAR tszText[60];
    TCHAR tszFace[LF_FACESIZE + CCH_SELECTEDFONT];
    HWND hWndList;

    DBGFONTS(("PreviewUpdate hDlg=%lx, %s\n", hDlg,
            bLB ? "Raster" : "TrueType"));

    hWndList = GetDlgItem(hDlg, bLB ? IDD_PIXELSLIST : IDD_POINTSLIST);

    /* When we select a font, we do the font preview by setting it into
     *  the appropriate list box
     */
    lIndex = lcbGETCURSEL(hWndList, bLB);
    DBGFONTS(("PreviewUpdate GETCURSEL gets %x\n", lIndex));
    if ((lIndex < 0) && !bLB) {
        COORD NewSize;

        lIndex = (LONG)SendDlgItemMessage(hDlg, IDD_FACENAME, LB_GETCURSEL, 0, 0L);
        SendDlgItemMessage(hDlg, IDD_FACENAME, LB_GETTEXT, lIndex, (LPARAM)tszFace);
        NewSize.X = 0;
        NewSize.Y = (SHORT)GetPointSizeInRange(hDlg, MIN_PIXEL_HEIGHT, MAX_PIXEL_HEIGHT);

        if (NewSize.Y == 0) {
            TCHAR tszBuf[60];
            /*
             * Use tszText, tszBuf to put up an error msg for bad point size
             */
            gbPointSizeError = TRUE;
            LoadString(ghInstance, IDS_FONTSIZE, tszBuf, NELEM(tszBuf));
            wsprintf(tszText, tszBuf, MIN_PIXEL_HEIGHT, MAX_PIXEL_HEIGHT);

            GetWindowText(hDlg, tszBuf, NELEM(tszBuf));
            MessageBoxEx(hDlg, tszText, tszBuf, MB_OK|MB_ICONINFORMATION, 0L);
            SetFocus(hWndList);
            gbPointSizeError = FALSE;
            return FALSE;
        }
        FontIndex = FindCreateFont(FF_MODERN|TMPF_VECTOR|TMPF_TRUETYPE,
                                   tszFace, NewSize, 0,
                                   gpStateInfo->CodePage);
    } else {
        FontIndex = lcbGETITEMDATA(hWndList, bLB, lIndex);
    }

    if (FontIndex < 0) {
        FontIndex = DefaultFontIndex;
    }

    /*
     * If we've selected a new font, tell the property sheet we've changed
     */
    if (CurrentFontIndex != (ULONG)FontIndex) {
        CurrentFontIndex = FontIndex;
    }

    lpFont = &FontInfo[FontIndex];

    /* Display the new font */

    _tcscpy(tszFace, tszSelectedFont);
    _tcscat(tszFace, lpFont->FaceName);
    SetDlgItemText(hDlg, IDD_GROUP, tszFace);

    /* Put the font size in the static boxes */
    wsprintf(tszText, TEXT("%u"), lpFont->Size.X);
    hWnd = GetDlgItem(hDlg, IDD_FONTWIDTH);
    SetWindowText(hWnd, tszText);
    InvalidateRect(hWnd, NULL, TRUE);
    wsprintf(tszText, TEXT("%u"), lpFont->Size.Y);
    hWnd = GetDlgItem(hDlg, IDD_FONTHEIGHT);
    SetWindowText(hWnd, tszText);
    InvalidateRect(hWnd, NULL, TRUE);

    /* Force the preview windows to repaint */
    hWnd = GetDlgItem(hDlg, IDD_PREVIEWWINDOW);
    SendMessage(hWnd, CM_PREVIEW_UPDATE, 0, 0);
    hWnd = GetDlgItem(hDlg, IDD_FONTWINDOW);
    InvalidateRect(hWnd, NULL, TRUE);

    DBGFONTS(("Font %x, (%d,%d) %ls\n", FontIndex,
            FontInfo[FontIndex].Size.X,
            FontInfo[FontIndex].Size.Y,
            FontInfo[FontIndex].FaceName));

    return TRUE;
}
