/************************************************************************

  File: font.c

  Purpose:

    Contains all functions pertinent to CDTEST's Font dialog.

  Functions:

     DoFontDlg()          -- Creates CDTEST's font dialog.

     FontProc()           -- Callback for CDTEST's font dialog.

     InitFontStruct()     -- Fills a CHOOSEFONT structure with some defaults.

     FillFontDlg()        -- Fills CDTEST's font dialog with the contents
                             of a CHOOSEFONT structure.

     GetFontDlg()         -- Retrieves the contents of CDTEST's font dialog
                             and fills a CHOOSEFONT structure with them.

     GetHdc()             -- Creates and returns an HDC

     LogFontProc()        -- Callback function for the logfont dlg.

     InitLogFontStruct()  -- Fills a LOGFONT structure with some defaults.

     FillLogFontDlg()     -- Fills the LOGFONT dlg with the values in
                             a LOGFONT structure.

     GetLogFontDlg()      -- Retrieves the user's edits in the LogFont dlg
                             and puts them in a LOGFONT structure.

     FontHookProc()       -- Callback function for CHOOSEFONT->lpfnHook

     GetFontDlgHandle()   -- Creates a handle to the custom template and
                             returns a handle to it.

     EnumFontsProc()      -- Callback function for EnumFontFamilies()

     FillFontsBox()       -- Fills CDTEST's font list box with requested fonts

     ResetCheckBoxes()    -- handles the font choices check box manipulations

     HandleFontCheckBox() -- Handles the WM_COMMAND messages from the font
                             choices check boxes.

     DoChooseFontStuff()  -- Calls the ChooseFont() function.

     FontThreadProc1()    -- Starting address for the first thread.

     FontThreadProc2()    -- Starting address for the second thread.

     MultiThreadFontDlg() -- Creates two threads which each create a
                             ChooseFont() dialog.

     FontEnableButtons()  -- Enables or disables CDTEST's font dialog buttons.
                             Necessary for multithreading part of this app.

************************************************************************/


#include <windows.h>
#include <commdlg.h>
#include <stdlib.h>
#include <winnls.h>
#include "cdtest.h"
#include "font.h"
#include "logfont.h"



/* some definitions that will help us tell the difference between
   screen and printer fonts */

#define FONT_TYPE_WYSIWYG 1
BOOL bScreen ;



/* function prototypes and general variables */

void InitFontStruct(HWND, LPCHOOSEFONT) ;
void FillFontDlg(HWND, LPCHOOSEFONT) ;
void GetFontDlg(HWND, LPCHOOSEFONT) ;
HDC  GetHdc(HWND, int) ;
void InitLogFontStruct(HWND, LPLOGFONT) ;
void FillLogFontDlg(HWND, LPLOGFONT) ;
void GetLogFontDlg(HWND, LPLOGFONT) ;
BOOL APIENTRY LogFontProc(HWND, UINT, UINT, LONG) ;
UINT APIENTRY FontHookProc(HWND, UINT, UINT, LONG) ;
int  CALLBACK EnumFontsProc(LPLOGFONT, LPTEXTMETRIC, DWORD, LONG) ;
void FillFontsBox(HWND, DWORD) ;
void ResetCheckBoxes(HWND) ;
void HandleFontCheckBox(HWND, int) ;
void DoChooseFontStuff(HWND, LPCHOOSEFONT) ;



/* Global variables and some external variables and functions */

DWORD dwFontFlag ;
PRINTDLG pfd ;
BOOL bLogFontParam ;
LOGFONT lfWM_CF_LF ;                 //for the WM_CHOOSEFONT_GETLOGFONT message
extern UINT uMode ;
extern LONG MyAtol(LPTSTR, BOOL, LPBOOL) ;
HANDLE hResFont, hDialogFont ;
HANDLE GetFontDlgHandle(void) ;
CHOOSEFONT cf ;
LOGFONT lf ;
HDC hdc ;
TCHAR szFaceName[50] ;
TCHAR szFontTemplate[50] ;
TCHAR szFontStyle[50] ;
#define HDCSCREEN 1
#define HDCPRINTER 2
#define HDCNULL 3
#define HDCINVALID 4
INT nHdcType ;



/* Multithreading stuff */

HANDLE hFontThread1 ;
HANDLE hFontThread2 ;
DWORD  dwFontThreadID1 ;
DWORD  dwFontThreadID2 ;
DWORD  dwFontThreadParm1 ;
DWORD  dwFontThreadParm2 ;
DWORD  FontThreadProc1(LPDWORD) ;
DWORD  FontThreadProc2(LPDWORD) ;
HANDLE hwndMainFont ;
int    nOpenFontDialogCount ;
CHOOSEFONT cfThread1 ;
CHOOSEFONT cfThread2 ;
void MultiThreadFontDlg(void) ;
void EnableFontButtons(HWND, BOOL) ;




/************************************************************************

  Function: DoFontDlg(HWND)

  Purpose:

    To create the CDTEST's font dialog

  Returns: Nothing.

  Comments:


************************************************************************/

void DoFontDialog(HWND hwnd)
{


  DialogBox(hInst, MAKEINTRESOURCE(ID_FONTDIALOG),
            hwnd, FontProc) ;

}







/************************************************************************

  Function: FontProc(HWND, UINT, UINT, LONG)

  Purpose:

    Callback function for CDTEST's font dialog.

  Returns: TRUE or FALSE depending on the situation.

  Comments:


************************************************************************/


BOOL APIENTRY FontProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  BOOL b ;
  TCHAR szNum[30] ;

  switch (msg)
  {
    case WM_INITDIALOG:

        InitLogFontStruct(hwnd, &lf) ;   //fill (struct lf) so the choosefont
        InitFontStruct(hwnd, &cf) ;      //struct can use it
        FillFontDlg(hwnd, &cf) ;

        CheckRadioButton(hwnd, ID_HDCSCREEN, ID_HDCPRINTER, ID_HDCSCREEN) ;
        nHdcType = HDCSCREEN ;

        dwFontFlag = cf.Flags ;

        FillFontsBox(hwnd, dwFontFlag) ;

        ResetCheckBoxes(hwnd) ;

        *(&cfThread1) = *(&cfThread2) = *(&cf) ;

        hwndMainFont = hwnd ;
        nOpenFontDialogCount = 0 ;

        SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEF)) ;

        break ;


    case UMSG_DECREMENTDLGCOUNT:  // user defined message.  This is send by
                                  // the functions that are executing when
                                  // a new thread is created.  When these
                                  // Thread functions end, they should send
                                  // this message.

      nOpenFontDialogCount-- ;

      if (nOpenFontDialogCount == 0)
        EnableFontButtons(hwnd, TRUE) ;

      break ;


    case WM_CF_LF:

       /* If this message comes in, we know that the user clicked the
          button that tells the ChooseFont() dialog to tell the parent
          to send the WM_CHOOSEFONT_GETLOGFONT message.  So what we will
          do is send the message and then show the user what was in the
          logfont by calling creating the Logfont dialog box */


       SendMessage((HWND) lParam, WM_CHOOSEFONT_GETLOGFONT,
                   0, (LPARAM) &lfWM_CF_LF) ;

       bLogFontParam = TRUE ; //tells us if we are doing normal logfont
                              //processing or answering the WM_CF_LF message.

       DialogBoxParam(hInst, MAKEINTRESOURCE(ID_LOGFONTDIALOG),
                      (HWND) lParam, LogFontProc, (LPARAM) &lf) ;

       bLogFontParam = FALSE ;

       break ;


    case WM_COMMAND:
    {
        switch (LOWORD(wParam))
        {
          case IDOK:
            DoChooseFontStuff(hwnd, &cf) ;  //create the dialog.
            break ;

          case IDCANCEL:
            EndDialog(hwnd, FALSE) ;
            break ;

          case ID_RESETFONT:

            InitLogFontStruct(hwnd, &lf) ;  //reset all the structures.
            InitFontStruct(hwnd, &cf) ;
            FillFontDlg(hwnd, &cf) ;        //refill the dialog.

            CheckRadioButton(hwnd, ID_HDCSCREEN, ID_HDCPRINTER, ID_HDCSCREEN) ;
            nHdcType = HDCSCREEN ;

            SendDlgItemMessage(hwnd, ID_NULLSTRUCTFONT, BM_SETCHECK, (WPARAM) 0, (LPARAM)0) ;
            SendDlgItemMessage(hwnd, ID_PRELOADEDFONT,  BM_SETCHECK, (WPARAM) 0, (LPARAM)0) ;

            dwFontFlag = cf.Flags ;
            FillFontsBox(hwnd, dwFontFlag) ;
            ResetCheckBoxes(hwnd) ;

            SetFocus(GetDlgItem(hwnd, ID_STRUCTSIZEF)) ;

            break ;


          case ID_HDCSCREEN:
            nHdcType = HDCSCREEN ;
            break ;

          case ID_HDCPRINTER:
            nHdcType = HDCPRINTER ;
            break ;

          case ID_HDCNULL:
            nHdcType = HDCNULL ;
            break ;

          case ID_HDCINVALID:
            nHdcType = HDCINVALID ;
            break ;

          case ID_EDITLOGFONT:

            /* Get the address of the logfont and then show the user what
               is in it with the Logfont dialog box */

            GetDlgItemText(hwnd, ID_LOGFONTF, szNum, 30) ;
            cf.lpLogFont = (LPLOGFONT) MyAtol(szNum, uMode==IDM_HEXMODE, &b) ;

            DialogBox(hInst, MAKEINTRESOURCE(ID_LOGFONTDIALOG),
                      hwnd, LogFontProc) ;
            break ;


          case F_TTONLY:
          case F_ANSIONLY:
          case F_PRINTERFONTS:
          case F_SCREENFONTS:
          case F_FIXEDPITCHONLY:
          case F_NOOEMFONTS:
          case F_NOVECTORFONTS:
          case F_SCALABLEONLY:
          case F_WYSIWYG:
            HandleFontCheckBox(hwnd, LOWORD(wParam)) ;
            break ;


          case ID_MULTITHREADFONT:

            /* Set the dialog count to 2, disable the buttons in CDTEST's font
               dialog so the user can't do anything until last dialog has
               been terminated */

            nOpenFontDialogCount = 2 ;

            EnableFontButtons(hwndMainFont, FALSE) ;

            /* And then do the multithreading */

            MultiThreadFontDlg() ;

            break ;


          default: break ;
        }
    }

    default:

      /*
         If the help button is pressed in the ChooseFont()
         dialog, it will send a message Registered with RegisterWindowMessage()
         to the parent window.  The message nHelpMessage was registered
         at application startup

         It must be detected with an IF statement because the value
         returned by RegisterWindowMessage() is not a constant
      */

      if (msg == nHelpMessage)
        MessageBox(GetForegroundWindow(), TEXT("Hello from the help button"),
                   TEXT("Font Help Button"), MB_OK | MB_APPLMODAL) ;
      break ;
  }

  return FALSE ;
}








/************************************************************************

  Function: InitFontStruct(HWND, LPCHOOSEFONT)

  Purpose:

    Fill the CHOOSEFONT structure passed as the second parameter with
    some default values.

  Returns: Nothing.

  Comments:


************************************************************************/

void InitFontStruct(HWND hwnd, LPCHOOSEFONT pcf)
{
  pcf->lStructSize = sizeof(CHOOSEFONT) ;
  pcf->hwndOwner = hwnd ;
  pcf->hDC = (HDC) 0 ;
  pcf->lpLogFont = &lf ;
  pcf->iPointSize = 24 ;
  pcf->Flags = CF_EFFECTS | CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_SHOWHELP | CF_APPLY ;
  pcf->rgbColors = RGB(0, 255, 0) ;
  pcf->lCustData = 0L ;
  pcf->lpfnHook = FontHookProc ;

  lstrcpy(szFontTemplate, TEXT("fonttemp")) ;
  pcf->lpTemplateName = szFontTemplate ;

  pcf->hInstance = (HANDLE) hInst ;

  lstrcpy(szFontStyle, TEXT("Bold")) ;
  pcf->lpszStyle = szFontStyle ;

  pcf->nFontType = SCREEN_FONTTYPE ;

  pcf->nSizeMin = 8 ;
  pcf->nSizeMax = 50 ;
}










/************************************************************************

  Function: FillFontDlg(HWND, LPCHOOSEFONT)

  Purpose:

    Fill CDTEST's font dialog with the values in the CHOOSEFONT structure
    passed via the second parameter.

  Returns: Nothing.

  Comments:


************************************************************************/

void FillFontDlg(HWND hwnd, LPCHOOSEFONT pcf)
{
  wsprintf(szTemp, szLongFilter, pcf->lStructSize) ;
  SetDlgItemText(hwnd, ID_STRUCTSIZEF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hwndOwner) ;
  SetDlgItemText(hwnd, ID_HWNDOWNERF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hDC) ;
  SetDlgItemText(hwnd, ID_HDCF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hDC) ;
  SetDlgItemText(hwnd, ID_HDCF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->lpLogFont) ;
  SetDlgItemText(hwnd, ID_LOGFONTF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->iPointSize) ;
  SetDlgItemText(hwnd, ID_POINTSIZEF, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcf->Flags) ;
  SetDlgItemText(hwnd, ID_FLAGSF, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcf->rgbColors) ;
  SetDlgItemText(hwnd, ID_RGBCOLORSF, szTemp) ;

  wsprintf(szTemp, szLongFilter, pcf->lCustData) ;
  SetDlgItemText(hwnd, ID_CUSTDATAF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->lpfnHook) ;
  SetDlgItemText(hwnd, ID_HOOKF, szTemp) ;

  SetDlgItemText(hwnd, ID_TEMPLATEF, pcf->lpTemplateName) ;

  wsprintf(szTemp, szLongFilter, (DWORD) pcf->hInstance) ;
  SetDlgItemText(hwnd, ID_HINSTANCEF, szTemp) ;

  SetDlgItemText(hwnd, ID_STYLEF, pcf->lpszStyle) ;

  wsprintf(szTemp, szLongFilter, (int) pcf->nFontType) ;
  SetDlgItemText(hwnd, ID_FONTTYPEF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (int) pcf->nSizeMin) ;
  SetDlgItemText(hwnd, ID_SIZEMINF, szTemp) ;

  wsprintf(szTemp, szLongFilter, (int) pcf->nSizeMax) ;
  SetDlgItemText(hwnd, ID_SIZEMAXF, szTemp) ;

}







/************************************************************************

  Function: GetFontDlg(HWND, LPCHOOSEFONT)

  Purpose:

    Retrieve the users edits from CDTEST's font dialog and put them
    in the CHOOSEFONT structure passed in as the second parameter

  Returns: Nothing.

  Comments:


************************************************************************/

void GetFontDlg(HWND hwnd, LPCHOOSEFONT pcf)
{
  BOOL b ;
  TCHAR szNum[30] ;

#define WSIZEFO 30

  GetDlgItemText(hwnd, ID_STRUCTSIZEF, szNum, WSIZEFO) ;
  pcf->lStructSize = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HWNDOWNERF, szNum, WSIZEFO) ;
  pcf->hwndOwner = (HWND) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LOGFONTF, szNum, WSIZEFO) ;
  pcf->lpLogFont = (LPLOGFONT) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_POINTSIZEF, szNum, WSIZEFO) ;
  pcf->iPointSize = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_FLAGSF, szNum, WSIZEFO) ;
  pcf->Flags = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_RGBCOLORSF, szNum, WSIZEFO) ;
  pcf->rgbColors = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_CUSTDATAF, szNum, WSIZEFO) ;
  pcf->lCustData = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_HOOKF, szNum, WSIZEFO) ;
  pcf->lpfnHook = (LPCFHOOKPROC) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_TEMPLATEF, szFontTemplate, 50) ;

  GetDlgItemText(hwnd, ID_HINSTANCEF, szNum, WSIZEFO) ;
  pcf->hInstance = (HANDLE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_STYLEF, szFontStyle, 50) ;

  GetDlgItemText(hwnd, ID_FONTTYPEF, szNum, WSIZEFO) ;
  pcf->nFontType = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_SIZEMINF, szNum, WSIZEFO) ;
  pcf->nSizeMin = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_SIZEMAXF, szNum, WSIZEFO) ;
  pcf->nSizeMax = (int) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;
}







/************************************************************************

  Function: GetHdc(HWND, int)

  Purpose:

    Creates and returns an HDC of the type specified.


  Returns: The HDC that it creates.


  Comments:

    The NULL and INVALID HDCs are only useful to test if the common dialogs
    handle an HDC that is not useful, but this function is also necessary
    in that the HDC must be a printer HDC if ChooseFont() is called
    with the CF_PRINTERFONTS flag bit set.

    To tell ChooseFont() to list only printer fonts:

    1.  Set the HDC in CDTEST's font dialog to "Printer"

    2.  Set the "Flags" edit box CF_PRINTERFONTS


************************************************************************/



HDC GetHdc(HWND hwnd, int nType)
{
  switch (nType)
  {
    case HDCSCREEN:
        return GetDC(hwnd) ;
        break ;

    case HDCNULL:
        return (HDC) 0 ;
        break ;

    case HDCINVALID:
        return (HDC) 999 ;
        break ;

    case HDCPRINTER:

        /* To get the HDC of the current printer, fill out a PRINTDLG
           structure, and set it's flags to (PD_RETURNDC | PD_RETURNDEFAULT).
           This will tell PrintDlg() to create you an HDC but not show
           the Print dialog box. */

        pfd.lStructSize = sizeof(PRINTDLG) ;
        pfd.hwndOwner = hwnd ;
        pfd.hDevMode = (HANDLE) 0 ;
        pfd.hDevNames = (HANDLE) 0 ;
        pfd.Flags = PD_RETURNDC | PD_RETURNDEFAULT ;   //just get default printer
        pfd.nFromPage = 0 ;
        pfd.nToPage = 0 ;
        pfd.nMinPage = 0 ;
        pfd.nMaxPage = 0 ;
        pfd.nCopies = 0 ;
        pfd.hInstance = (HANDLE) hInst ;

        if (PrintDlg(&pfd) == 0)
            MessageBox(hwnd, TEXT("Error: Could not create a printer DC!"),
                       (LPTSTR) NULL, MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;
        else
           return pfd.hDC ;

        break ;

    default:
        break ;
  }
  return (HDC) 0 ;
}









/************************************************************************

  Function: LogFontProc(HWND, int)

  Purpose:

    The callback proc for the LogFont dialog box


  Returns: BOOL -- depending on the situation.


  Comments:

************************************************************************/


BOOL APIENTRY LogFontProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  switch (msg)
  {
    case WM_INITDIALOG:
        FillLogFontDlg(hwnd, cf.lpLogFont) ;  //start off with the one that
                                              //is returned from ChooseFont()
        break ;

    case WM_COMMAND:
        switch(LOWORD(wParam))
        {
          case IDOK:

            GetLogFontDlg(hwnd, &lf) ;

            cf.lpLogFont = &lf ;

            EndDialog(hwnd, TRUE) ;

            break ;



          case ID_RESETLF:

            SetFocus(GetDlgItem(hwnd, ID_LFHEIGHT)) ;

            InitLogFontStruct(hwnd, &lf) ;

            FillLogFontDlg(hwnd, &lf) ;

            break ;


          case IDCANCEL:

            EndDialog(hwnd, FALSE) ;

            break ;


          default:
            break ;
        }

    default: break ;
  }


  return FALSE ;
}







/************************************************************************

  Function: InitLogFontStruct(HWND, LPLOGFONT)

  Purpose:

    Fills the LOGFONT structure passed in as the second parameter with
    some default values.

  Returns: Nothing.

  Comments:

************************************************************************/

void InitLogFontStruct(HWND hwnd, LPLOGFONT plf)
{
  plf->lfHeight = 24 ;
  plf->lfWidth  = 20 ;
  plf->lfEscapement = 0 ;
  plf->lfOrientation = 10 ;
  plf->lfWeight = 400 ;
  plf->lfItalic = FALSE ;
  plf->lfUnderline = FALSE ;
  plf->lfStrikeOut = FALSE ;
  plf->lfCharSet = ANSI_CHARSET ;
  plf->lfOutPrecision = OUT_DEFAULT_PRECIS ;
  plf->lfClipPrecision = CLIP_DEFAULT_PRECIS ;
  plf->lfQuality = DEFAULT_QUALITY ;
  plf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE ;

  szFaceName[0] = (TCHAR) 0 ;
  lstrcpy(plf->lfFaceName, szFaceName) ;
}






/************************************************************************

  Function: FillLogFontDlg(HWND, LPLOGFONT)

  Purpose:

    Fills CDTEST's logfont dialog with the values in the logfont that
    is passed in as the second parameter.

  Returns: Nothing.

  Comments:

************************************************************************/

void FillLogFontDlg(HWND hwnd, LPLOGFONT plf)
{
  TCHAR szTemp[50] ;

  LPLOGFONT pl = plf ;

  if (bLogFontParam)    //Are creating this dialog in response to the
    pl = &lfWM_CF_LF ;  //WM_CHOOSEFONT_GETLOGFONT message ?

  if (!pl)              //Avert a ghastly exception error
    return ;

  wsprintf(szTemp, szLongFilter, pl->lfHeight) ;
  SetDlgItemText(hwnd, ID_LFHEIGHT, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfWidth) ;
  SetDlgItemText(hwnd, ID_LFWIDTH, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfEscapement) ;
  SetDlgItemText(hwnd, ID_LFESCAPEMENT, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfOrientation) ;
  SetDlgItemText(hwnd, ID_LFORIENTATION, szTemp) ;

  wsprintf(szTemp, szLongFilter, pl->lfWeight) ;
  SetDlgItemText(hwnd, ID_LFWEIGHT, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfItalic) ;
  SetDlgItemText(hwnd, ID_LFITALIC, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfUnderline) ;
  SetDlgItemText(hwnd, ID_LFUNDERLINE, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfStrikeOut) ;
  SetDlgItemText(hwnd, ID_LFSTRIKEOUT, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfCharSet) ;
  SetDlgItemText(hwnd, ID_LFCHARSET, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfOutPrecision) ;
  SetDlgItemText(hwnd, ID_LFOUTP, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfClipPrecision) ;
  SetDlgItemText(hwnd, ID_LFCLIPP, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfQuality) ;
  SetDlgItemText(hwnd, ID_LFQUALITY, szTemp) ;

  wsprintf(szTemp, szShortFilter, pl->lfPitchAndFamily) ;
  SetDlgItemText(hwnd, ID_LFPITCHANDFAM, szTemp) ;

  SetDlgItemText(hwnd, ID_LFFACENAME, (LPTSTR) pl->lfFaceName) ;
}






/************************************************************************

  Function: GetLogFontDlg(HWND, LPLOGFONT)

  Purpose:

    Retrieves the users edits in CDTEST's Logfont dialog and puts them
    into the LOGFONT struct that is passed as the second parameter.

  Returns: Nothing.

  Comments:

************************************************************************/

void GetLogFontDlg(HWND hwnd, LPLOGFONT plf)
{
  BOOL b ;
  TCHAR szNum[30] ;

  if (bLogFontParam)  //if we are just viewing the logfont because we sent a
    return ;          //WM_CHOOSEFONT_GETLOGFONT message, don't change the Logfont Struct
                      //that ChooseFont() gives us...

  #define LFSIZE 30

  GetDlgItemText(hwnd, ID_LFHEIGHT, szNum, LFSIZE) ;
  plf->lfHeight = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFWIDTH, szNum, LFSIZE) ;
  plf->lfWidth = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFESCAPEMENT, szNum, LFSIZE) ;
  plf->lfEscapement = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFORIENTATION, szNum, LFSIZE) ;
  plf->lfOrientation = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFWEIGHT, szNum, LFSIZE) ;
  plf->lfWeight = MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFITALIC, szNum, LFSIZE) ;
  plf->lfItalic = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFUNDERLINE, szNum, LFSIZE) ;
  plf->lfUnderline = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFSTRIKEOUT, szNum, LFSIZE) ;
  plf->lfStrikeOut = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFCHARSET, szNum, LFSIZE) ;
  plf->lfCharSet = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFOUTP, szNum, LFSIZE) ;
  plf->lfOutPrecision = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFCLIPP, szNum, LFSIZE) ;
  plf->lfClipPrecision = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFQUALITY, szNum, LFSIZE) ;
  plf->lfQuality = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFPITCHANDFAM, szNum, LFSIZE) ;
  plf->lfPitchAndFamily = (BYTE) MyAtol(szNum, uMode == IDM_HEXMODE, &b) ;

  GetDlgItemText(hwnd, ID_LFFACENAME, (LPTSTR) plf->lfFaceName, 32) ;
}








/************************************************************************

  Function: FontHookProc(HWND, UINT, UINT, LONG)

  Purpose: The callback function that acts as the hook proc for
           the ChooseFont() dialog.


  Returns: TRUE to discard the message.  FALSE to send the message on
           to the normal dialog processing done by the ChooseFont()
           function.

  Comments:

************************************************************************/

UINT APIENTRY FontHookProc(HWND hwnd, UINT msg, UINT wParam, LONG lParam)
{
  LPCHOOSEFONT pCf ;
  TCHAR szMsg[50] ;

  switch(msg)
  {
    case WM_INITDIALOG:

      pCf = (LPCHOOSEFONT) lParam ;

      if (pCf->lCustData != 0L)
      {
        wsprintf(szMsg, TEXT("CHOOSEFONT->lCustData is: %ld"), pCf->lCustData) ;
        MessageBox(hwnd, szMsg, TEXT("lCustData Sent!"), MB_OK) ;
      }

      SetWindowText(hwnd, TEXT("Font Hook Proc Dialog")) ;

      break ;

    case WM_COMMAND:

        /* NOTE: This button will only be available if the user creates the
           ChooseFont() dialog with the CF_ENABLETEMPLATE or CF_ENABLETEMPLATEHANDLE
           flag.

           This button will only work if the ChooseFont() dialog is created
           with the CF_ENABLEHOOK flag.  So, to get full functionality from
           this button:

           1.  Enter (CF_ENABLEHOOK | CF_ENABLETEMPLATE | CF_SCREENFONTS)
               in the "Flags" edit box of CDTEST'S Font dialog.

           2.  Click OK
        */



        /* The parent will send the WM_CHOOSEFONT_GETLOGFONT message and
           display it's findings in a LogFont dialog if it gets this message */

        if (LOWORD(wParam) == ID_SEND_WM_CF_LF_MSG)
          SendMessage(GetParent(hwnd), WM_CF_LF, 0, (LPARAM) hwnd) ;

        break ;


    default:
      break ;
  }

  return FALSE ;   //send msg to the common dialog code
}









/************************************************************************

  Function: GetFontDlgHandle(void)

  Purpose:  Finds and loads the custom template resource and returns a
            handle to it.


  Returns: A handle to the custom template resource.

  Comments:

************************************************************************/

HANDLE GetFontDlgHandle(void)
{

  hResFont = FindResource(hInst, TEXT("fonttemp"), RT_DIALOG) ;

  hDialogFont = LoadResource(hInst, hResFont) ;


  return hDialogFont ;
}








/************************************************************************

  Function: EnumFontsProc(LPLOGFONT, LPTEXTMETRIC, DWORD, LONG)

  Purpose: Acts as a callback function for the EnumFontFamilies()
           function.

  Returns: 0 if the font enumeration should stop.  1 to ask for the
           next font.

  Comments:

    EnumFontFamilies will find the fonts on the system and call this
    function each time it finds a font.  Pointers to the LOGFONT and
    TEXTMETRIC structures the describe the font are passed as the first
    and second arguments, so we can determine if the fonts we get
    meet the specs we want.  If they do, we can add them to the font
    list box in CDTEST's Font dialog.

    NOTE:

    None of this affects the performance of ChooseFont().  This serves
    only to illustrate how ChooseFont() finds the fonts you request based
    on the flags you pass to the ChooseFont() function.

    The fonts found with this function should be the same as the ones
    found by ChooseFont() for any flag combination.


************************************************************************/


int CALLBACK EnumFontsProc(LPLOGFONT lplf, LPTEXTMETRIC lptm,
                           DWORD dwStyle, LONG lParam)
{
  UINT i ;

  /* check to see if the font is already there right off the bat
     and just continue the enumeration if it is...        */


  i = SendDlgItemMessage((HWND) lParam, ID_FONTLIST, LB_FINDSTRINGEXACT, (WPARAM) 0,
                         (LONG) (LPTSTR) lplf->lfFaceName) ;

  if (i != LB_ERR)
  {
    if (bScreen)     //if we are adding it for the first time
      return 1 ;

    //else, we are adding printer fonts, so if we find one that is already there
    //it must be available for both the printer and the screen...

    else
    {
      SendDlgItemMessage((HWND) lParam, ID_FONTLIST, LB_SETITEMDATA, i,
                          FONT_TYPE_WYSIWYG) ;
      return 1 ;
    }
  }


  //Get rid of any fonts that we don't want...

  if ((dwFontFlag & CF_TTONLY) && (!(dwStyle & TRUETYPE_FONTTYPE)))
    return 1 ;

  if ((dwFontFlag & CF_SCALABLEONLY) && (dwStyle & RASTER_FONTTYPE))
    return 1 ;

  if ((dwFontFlag & CF_ANSIONLY) && (lplf->lfCharSet != ANSI_CHARSET))
    return 1 ;

  if ((dwFontFlag & CF_FIXEDPITCHONLY) && (lplf->lfPitchAndFamily & VARIABLE_PITCH))
    return 1 ;

  if ((dwFontFlag & CF_NOVECTORFONTS) && (lplf->lfCharSet == OEM_CHARSET))
    return 1 ;


  //if there is a font to be added, add it.

  if (*(lplf->lfFaceName))
    SendDlgItemMessage((HWND) lParam, ID_FONTLIST, LB_ADDSTRING,
                     (WPARAM) 0, (LONG) (LPTSTR) lplf->lfFaceName) ;

  return 1 ;
}








/************************************************************************

  Function: FillFontsBox(HWND, DWORD)

  Purpose: Enumerates all fonts on the system and sends the ones that
           meet the criteria of the dwFlags parameter (CF_SCREENFONTS,
           CF_SCALABLEONLY, etc).

  Returns: Nothing.

  Comments:


************************************************************************/

void FillFontsBox(HWND hwnd, DWORD dwFlags)
{
  HDC hdc ;
  HWND hwndControl ;
  DWORD dwData ;
  int nItemCount ;


  /* Empty the list box, and turn of the redraw because we may need
     to remove some fonts after they are added if the user calls this
     function with the CF_WYSIWYG flag bit set. */

  SendDlgItemMessage(hwnd, ID_FONTLIST, LB_RESETCONTENT, (WPARAM)0, (LPARAM) 0) ;

  hwndControl = GetDlgItem(hwnd, ID_FONTLIST) ;

  SendMessage(hwndControl, WM_SETREDRAW, FALSE, 0L) ;


  /* Now call EnumFontFamilies() for each type of HDC requested */

  if (dwFlags & CF_SCREENFONTS)
  {
    bScreen = TRUE ;
    hdc = GetHdc(hwnd, HDCSCREEN) ;
    EnumFontFamilies(hdc, (LPTSTR) NULL, (FONTENUMPROC) EnumFontsProc, (LONG) hwnd) ;
    ReleaseDC(hwnd, hdc) ;
  }

  if (dwFlags & CF_PRINTERFONTS)
  {
    bScreen = FALSE ;
    hdc = GetHdc(hwnd, HDCPRINTER) ;
    EnumFontFamilies(hdc, (LPTSTR) NULL, (FONTENUMPROC) EnumFontsProc, (LONG) hwnd) ;
    ReleaseDC(hwnd, hdc) ;
  }


  /* Special case:  If the CF_WYSIWYG flag is used, we have to enumerate
     the fonts in both HDCs and then remove all the ones that are not
     both printer fonts and screen fonts. */

  if (dwFontFlag & CF_WYSIWYG)
  {
    nItemCount = SendDlgItemMessage(hwnd, ID_FONTLIST, LB_GETCOUNT, 0, 0L) ;

    nItemCount-- ;   //the list is zero based

    while (nItemCount >= 0)
    {
      dwData = SendDlgItemMessage(hwnd, ID_FONTLIST, LB_GETITEMDATA, nItemCount, 0L) ;

      if (dwData != FONT_TYPE_WYSIWYG)
      {
        SendDlgItemMessage(hwnd, ID_FONTLIST, LB_DELETESTRING, nItemCount, 0L) ;
      }

      nItemCount-- ;
    }
  }


  /* Now redraw the font list */

  SendMessage(hwndControl, WM_SETREDRAW, TRUE, 0L) ;
  InvalidateRect(hwndControl, NULL, FALSE) ;

  return ;
}






/************************************************************************

  Function: ResetCheckBoxes(HWND)

  Purpose: Checks the checkboxes that control what fonts are listed in
           CDTEST's font list box.

  Returns: Nothing.

  Comments:


************************************************************************/

void ResetCheckBoxes(HWND hwnd)
{

  SendDlgItemMessage(hwnd, F_TTONLY,          BM_SETCHECK, dwFontFlag & CF_TTONLY         ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_ANSIONLY,        BM_SETCHECK, dwFontFlag & CF_ANSIONLY       ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_SCREENFONTS,     BM_SETCHECK, dwFontFlag & CF_SCREENFONTS    ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_PRINTERFONTS,    BM_SETCHECK, dwFontFlag & CF_PRINTERFONTS   ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_NOOEMFONTS,      BM_SETCHECK, dwFontFlag & CF_NOOEMFONTS     ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_NOVECTORFONTS,   BM_SETCHECK, dwFontFlag & CF_NOVECTORFONTS  ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_SCALABLEONLY,    BM_SETCHECK, dwFontFlag & CF_SCALABLEONLY   ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_WYSIWYG,         BM_SETCHECK, dwFontFlag & CF_WYSIWYG        ? 1 : 0, (LPARAM)0) ;
  SendDlgItemMessage(hwnd, F_FIXEDPITCHONLY,  BM_SETCHECK, dwFontFlag & CF_FIXEDPITCHONLY ? 1 : 0, (LPARAM)0) ;

}






/************************************************************************

  Function: HandleFontCheckBox(HWND)

  Purpose: If the user clicks one of the fonts in the CDTEST's list of
           valid font types, this function will check the correct boxes
           and set the correct values in the flag which keeps track of
           which fonts are to be enumerated and added to CDTEST's font
           list box.


  Returns: Nothing.

  Comments:

    CF_NOOEMFONTS and CF_NOVECTORFONTS are identical.


************************************************************************/

void HandleFontCheckBox(HWND hwnd, int nID)
{
  DWORD dwTemp ;

  dwTemp = dwFontFlag ;

  switch (nID)
  {
    case F_TTONLY:

      /* TTONLY is a special case.  If we only want Truetype fonts,
         turn off irrelevant flags */

      if (!(dwFontFlag & CF_TTONLY)) //if we're turning this flag on...
      {
        dwFontFlag = 0L ;  //turn everyone off but the following:

        dwFontFlag |= CF_TTONLY ;

        if (dwTemp & CF_ANSIONLY)
          dwFontFlag |= CF_ANSIONLY ;

        if (dwTemp & CF_FIXEDPITCHONLY)
          dwFontFlag |= CF_FIXEDPITCHONLY ;

        if (dwTemp & CF_SCREENFONTS)
          dwFontFlag |= CF_SCREENFONTS ;

        if (dwTemp & CF_PRINTERFONTS)
          dwFontFlag |= CF_PRINTERFONTS ;
      }
      else
      {
        dwFontFlag ^= CF_TTONLY ;
      }


      break ;

    case F_ANSIONLY:
      dwFontFlag ^= CF_ANSIONLY ;        //otherwise toggle the bit.
      dwFontFlag &= ~CF_WYSIWYG ;

      break ;

    case F_PRINTERFONTS:
      dwFontFlag ^= CF_PRINTERFONTS ;

      break ;

    case F_SCREENFONTS:
      dwFontFlag ^= CF_SCREENFONTS ;

      break ;

    case F_FIXEDPITCHONLY:
      dwFontFlag ^= CF_FIXEDPITCHONLY ;

      break ;

    case F_NOOEMFONTS:
      dwFontFlag ^= CF_NOOEMFONTS ;

      break ;

    case F_NOVECTORFONTS:
      dwFontFlag ^= CF_NOVECTORFONTS ;

      break ;

    case F_SCALABLEONLY:
      dwFontFlag ^= CF_SCALABLEONLY ;

      break ;

    case F_WYSIWYG:                        //We want fonts for both the
                                           //screen and the printer...
      dwFontFlag ^= CF_WYSIWYG ;

      if (dwFontFlag & CF_WYSIWYG)
        dwFontFlag |= (CF_SCREENFONTS | CF_PRINTERFONTS | CF_SCALABLEONLY) ;

      break ;

    default: break ;
  }

  ResetCheckBoxes(hwnd) ;
  FillFontsBox(hwnd, dwFontFlag) ;
}







/************************************************************************

  Function: DoChooseFontStuff(HWND, LPCHOOSEFONT)

  Purpose:

    Calls the ChooseFont() function.

  Returns: Nothing.

  Comments:


************************************************************************/


void DoChooseFontStuff(HWND hwnd, LPCHOOSEFONT pcf)
{
  BOOL bRet = FALSE ;

  if (IsDlgButtonChecked(hwnd, ID_PRELOADEDFONT) == 1)
  {
    pcf->hInstance = GetFontDlgHandle() ;
    wsprintf(szTemp, szLongFilter, pcf->hInstance) ;
    SetDlgItemText(hwnd, ID_HINSTANCEF, szTemp) ;
  }

  pcf->hDC = GetHdc(hwnd, nHdcType) ;

  wsprintf(szTemp, szLongFilter, pcf->hDC) ;
  SetDlgItemText(hwnd, ID_HDCF, szTemp) ;


  GetFontDlg(hwnd, pcf) ;


  if (IsDlgButtonChecked(hwnd, ID_NULLSTRUCTFONT) == 1)
    bRet = ChooseFont((LPCHOOSEFONT)NULL) ;
  else
    bRet = ChooseFont(pcf) ;

  wsprintf(szTemp, szLongFilter, CommDlgExtendedError()) ;
  SetDlgItemText(hwnd, ID_ERRORF, szTemp) ;

  if (pcf->hDC)
  {
    ReleaseDC(hwnd, pcf->hDC) ;   //free the HDC that we used if we called
    pcf->hDC = (HDC) 0 ;          //the function with a valid HDC.
  }

  if (hDialogFont)
  {
    FreeResource(hDialogFont) ;
    hDialogFont = (HANDLE) 0 ;
    hResFont = (HANDLE) 0 ;
  }

  wsprintf(szTemp, szShortFilter, bRet) ;     //fill results into the Font dlg
  SetDlgItemText(hwnd, ID_RETURNF, szTemp) ;

  FillFontDlg(hwnd, pcf) ;
}






/************************************************************************

  Function: FontThreadProc1(LPDWORD)

  Purpose:

    Acts as the starting address for thread 1

  Returns: Any DWORD value.

  Comments:


************************************************************************/

DWORD FontThreadProc1(LPDWORD pdw)
{
  DoChooseFontStuff(hwndMainFont, &cfThread1) ;

  PostMessage(hwndMainFont, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}







/************************************************************************

  Function: FontThreadProc2(LPDWORD)

  Purpose:

    Acts as the starting address for thread 2

  Returns: Any DWORD value.

  Comments:


************************************************************************/


DWORD FontThreadProc2(LPDWORD pdw)
{
  DoChooseFontStuff(hwndMainFont, &cfThread2) ;

  PostMessage(hwndMainFont, UMSG_DECREMENTDLGCOUNT, 0, 0L ) ;

  return 0L ;
}







/************************************************************************

  Function: MultiThreadFontDlg(void)

  Purpose:

    Create the two threads that will in turn create two ChooseFont()
    dialogs.

  Returns: Nothing.

  Comments:

    Multithreading note:

    This function will return before the common dialog functions return.
    Therefore, do not pass any parameters to this function that will be
    referenced by the common dialogs because as soon as this function
    ends those parameters will be gone.

************************************************************************/

void MultiThreadFontDlg(void)
{

  dwFontThreadParm1 = dwFontThreadParm2 = 0L ;

  if (!(hFontThread1 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) FontThreadProc1,
                                     &dwFontThreadParm1, CREATE_SUSPENDED, &dwFontThreadID1)))

  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 1"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenFontDialogCount = 0 ;

    EnableFontButtons(hwndMainFont, TRUE) ;

    return ;
  }


  if (!(hFontThread2 = CreateThread((LPSECURITY_ATTRIBUTES) NULL, 0,
                                     (LPTHREAD_START_ROUTINE) FontThreadProc2,
                                     &dwFontThreadParm2, CREATE_SUSPENDED, &dwFontThreadID2)))
  {
    MessageBox(GetForegroundWindow(), TEXT("Error creating thread 2"), NULL,
               MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL) ;

    nOpenFontDialogCount = 0 ;

    EnableFontButtons(hwndMainFont, TRUE) ;

    return ;
  }

  ResumeThread(hFontThread1) ;
  ResumeThread(hFontThread2) ;

  return ;
}






/************************************************************************

  Function: EnableFontButtons(HWND, BOOL)

  Purpose:

    Enables or disables CDTEST's font dialogs buttons based on the
    status of the second parameter.


  Returns: Nothing.

  Comments:

    This is necessary when multithreading in the case of this application.

************************************************************************/

void EnableFontButtons(HWND hwnd, BOOL bEnable)
{
  EnableWindow(GetDlgItem(hwnd, IDOK), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, IDCANCEL), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_RESETFONT), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_MULTITHREADFONT), bEnable) ;
  EnableWindow(GetDlgItem(hwnd, ID_EDITLOGFONT), bEnable) ;
}
