/*
======================================================================
text.c

Write antialiased text on a Displayable.

Ernie Wright  18 Apr 05
MSVC 4.0
====================================================================== */

#include "qv.h"
#include <commctrl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*
======================================================================
update_undo()

Before writing a line of text, compositeText() calls update_undo() to
copy the area of the image that will be overwritten, so that the text
can be erased by calling undoText().  We take some care to calculate
a rectangle that's entirely within the image, and save only that.
====================================================================== */

static int update_undo( QVTextImage *timage, QVText *text, Displayable *disp )
{
   int x, y, w, h;

   x = text->lx;
   if ( text->dx < 0 ) x += text->dx;

   y = text->y - timage->ascent;
   if ( text->dy < 0 ) y += text->dy;

   w = timage->width + abs( text->dx );
   h = timage->height + abs( text->dy );

   if ( x >= disp->image->width ) return 0;
   if ( y >= disp->image->height ) return 0;
   if ( x < 0 ) { w += x; x = 0; }
   if ( y < 0 ) { h += y; y = 0; }

   if ( x + w > disp->image->width )  w -= x + w - disp->image->width;
   if ( y + h > disp->image->height ) h -= y + h - disp->image->height;
   if ( w <= 0 || h <= 0 ) return 0;

   freeImage( disp->undo.image );
   disp->undo.image = copyImageRect( disp->image, x, y, w, h );
   if ( !disp->undo.image ) return 0;
   disp->undo.x = x;
   disp->undo.y = y;

   return 1;
}


/*
======================================================================
undoText()

Remove the most recently written text from a Displayable, or restore
it (redo).
====================================================================== */

void undoText( Displayable *disp )
{
   Image *image;

   if ( !disp->undo.image ) return;

   image = copyImageRect( disp->image, disp->undo.x, disp->undo.y,
      disp->undo.image->width, disp->undo.image->height );
   pasteImageRect( disp->undo.image, disp->image, disp->undo.x, disp->undo.y );
   realizeDisplayableRect( disp, disp->undo.x, disp->undo.y,
      disp->undo.image->width, disp->undo.image->height );
   freeImage( disp->undo.image );
   disp->undo.image = image;
}


/*
======================================================================
write_aachar()

Draw the antialiased image of a character into an 8-bit buffer.
Returns the width of the character cell in pixels, which can be used
to advance the x-coordinate for the next write_aachar() call.  Called
by make_textimage().
====================================================================== */

static int write_aachar( HDC hdc, int c, QVTextImage *image, int x, int y )
{
   GLYPHMETRICS gm;
   MAT2 mat = {{ 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 }};
   DWORD result;
   unsigned char *buf;
   int size, i, j, p, q, span, v, w;


   /* get the buffer size */

   size = GetGlyphOutline( hdc, c, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat );
   if ( size <= 0 ) return 0;

   span = size / gm.gmBlackBoxY;
   w = ( int ) gm.gmBlackBoxX < span ? ( int ) gm.gmBlackBoxX : span;

   /* allocate the bitmap */

   buf = malloc( size );
   if ( !buf ) return 0;

   /* write the character into the bitmap */

   result = GetGlyphOutline( hdc, c, GGO_GRAY8_BITMAP, &gm, size, buf, &mat );
   if ( result <= 0 ) {
      free( buf );
      return 0;
   }

   /* draw the character in the QVTextImage buffer */

   for ( j = 0; j < ( int ) gm.gmBlackBoxY; j++ ) {
      p = j * span;
      q = ( y + j - gm.gmptGlyphOrigin.y ) * image->rowbytes
         + x + gm.gmptGlyphOrigin.x;
      for ( i = 0; i < w; /*gm.gmBlackBoxX;*/ i++, p++, q++ ) {
         v = image->bits[ q ] + ( buf[ p ] << 2 );
         if ( v > 255 ) v = 255;
         image->bits[ q ] = v;
      }
   }

   free( buf );
   return gm.gmCellIncX;
}


/*
======================================================================
make_textimage()

Create the antialiased image of a single line of text.  The image is
written in a QVTextImage, along with its pixel dimensions and the
ascent y-coordinate.  Called by compositeText().
====================================================================== */

int make_textimage( LOGFONT *lf, char *text, QVTextImage *dst )
{
   HDC hdc;
   HFONT hfont;
   SIZE size;
   TEXTMETRIC tm;
   int x, i, len;

   hdc = CreateCompatibleDC( NULL );
   hfont = SelectObject( hdc, CreateFontIndirect( lf ));

   len = strlen( text );
   GetTextExtentPoint32( hdc, text, len, &size );
   GetTextMetrics( hdc, &tm );
   if ( tm.tmItalic ) size.cx += 2;

   dst->width    = size.cx;
   dst->height   = size.cy;
   dst->depth    = 8;
   dst->rowbytes = (( dst->width + 3 ) >> 2 ) << 2;
   dst->ascent   = tm.tmAscent;
   dst->bits     = calloc( dst->rowbytes, dst->height );
   if ( !dst->bits ) return 0;

   GetTextExtentPoint32( hdc, " ", 1, &size );
   for ( i = 0, x = 0; i < len; i++ )
      if ( text[ i ] == ' ' )
         x += size.cx;
      else
         x += write_aachar( hdc, text[ i ], dst, x, dst->ascent );

   hfont = SelectObject( hdc, hfont );
   DeleteObject( hfont );
   DeleteDC( hdc );

   return 1;
}


/*
======================================================================
comp()

Composite (combine) the pixel value of an 8-bit source and a float
destination.  The level acts like an alpha value, determining the
proportional contributions of the source and destination.  Called by
compositeText().
====================================================================== */

static float comp( int src, float dst, float level )
{
   return ( src * level + ( 255 - src ) * dst ) / 255.0f;
}


/*
======================================================================
compositeText()

Draw a line of text into a Displayable.  The color, font, position,
and contents of the text are described by the QVText.  Before drawing,
the area of the image that will be altered is stored in an undo buffer
that can be used later to erase the text.
====================================================================== */

int compositeText( QVText *text, Displayable *disp )
{
   QVTextImage timage;
   float color[ 3 ];
   int x, y, row, col, offset;
   unsigned char *src;
   float *r, *g, *b, *a;


   if ( !text->text ) return 0;
   if ( !make_textimage( &text->lf, text->text, &timage )) return 0;

   switch ( text->lcr ) {
      case LCR_LEFT:    text->lx = text->x;                     break;
      case LCR_CENTER:  text->lx = text->x - timage.width / 2;  break;
      case LCR_RIGHT:   text->lx = text->x - timage.width;      break;
   }

   if ( !update_undo( &timage, text, disp )) {
      free( timage.bits );
      return 0;
   }

   color[ 0 ] = GetRValue( text->color ) / 255.0f;
   color[ 1 ] = GetGValue( text->color ) / 255.0f;
   color[ 2 ] = GetBValue( text->color ) / 255.0f;

   for ( y = 0; y < timage.height; y++ )
   {
      if ( text->dropshadow )
      {
         row = y + text->y + text->dy - timage.ascent;
         if ( row >= 0 && row < disp->image->height ) {
            offset = row * disp->image->width + text->lx + text->dx;
            r = disp->image->r + offset;
            g = disp->image->g + offset;
            b = disp->image->b + offset;
            a = disp->image->a + offset;
            src = timage.bits + y * timage.rowbytes;

            for ( x = 0; x < timage.width; x++ ) {
               col = x + text->lx + text->dx;
               if ( col >= 0 && col < disp->image->width ) {
                  r[ x ] = comp( src[ x ], r[ x ], 0.0f );
                  g[ x ] = comp( src[ x ], g[ x ], 0.0f );
                  b[ x ] = comp( src[ x ], b[ x ], 0.0f );
                  a[ x ] = comp( src[ x ], a[ x ], 0.0f );
               }
            }
         }
      }

      row = y + text->y - timage.ascent;
      if ( row >= 0 && row < disp->image->height ) {
         offset = row * disp->image->width + text->lx;
         r = disp->image->r + offset;
         g = disp->image->g + offset;
         b = disp->image->b + offset;
         a = disp->image->a + offset;
         src = timage.bits + timage.rowbytes * y;

         for ( x = 0; x < timage.width; x++ ) {
            col = x + text->lx;
            if ( col >= 0 && col < disp->image->width ) {
               r[ x ] = comp( src[ x ], r[ x ], color[ 0 ] );
               g[ x ] = comp( src[ x ], g[ x ], color[ 1 ] );
               b[ x ] = comp( src[ x ], b[ x ], color[ 2 ] );
               a[ x ] = comp( src[ x ], a[ x ], 1.0f );
            }
         }
      }
   }

   free( timage.bits );
   realizeDisplayableRect( disp, disp->undo.x, disp->undo.y,
      disp->undo.image->width, disp->undo.image->height );
   return 1;
}


/*
----------------------------------------------------------------------
Text Dialog

Display a modal dialog that allows the user to set the string to draw
and the font, color, and size, whether to make it bold and/or italic,
the coordinates of the insertion point and whether the text is left or
right justified or centered on that point, whether a drop shadow is
drawn and what offset it should be drawn at.

The QVText is a temporary copy of QV's text specification.  The HFONT
handles are used to render the B and I in the bold and italic buttons.
---------------------------------------------------------------------- */

static QVText qvtext;            // temp copy of QV's text specification
static HFONT hbold, hitalic;     // for the B and I in the style buttons

#define NCOLORS 7
#define CUSTINDEX (NCOLORS - 1)

static struct st_ColorList {     // a color pool shared by the color
   COLORREF rgb;                 //  combo box and the chooser dialog
   char *name;
} colors[ NCOLORS ] =
{
   RGB( 255, 255, 255 ), "White",
   0,                    "Black",
   RGB( 255, 255,   0 ), "Yellow",
   RGB( 255,   0,   0 ), "Red",
   RGB(   0, 255,   0 ), "Green",
   RGB(   0,   0, 255 ), "Blue",
   RGB( 128, 128, 128 ), "Custom"
};


/*
======================================================================
draw_fontname()

Called in response to a WM_DRAWITEM for the font combo box.  The font
is used to draw its own name in the dropdown list.  For fonts like
Symbol and Wingdings, the name is drawn in the system font.
====================================================================== */

static void draw_fontname( LPDRAWITEMSTRUCT pdis )
{
   char fontname[ LF_FACESIZE ];
   HFONT hfont;
   LOGFONT lf = { 0 }, *plf;

   SetBkMode( pdis->hDC, TRANSPARENT );

   /* erase the background and set the text color */

   if ( pdis->itemState & ODS_SELECTED ) {
      FillRect( pdis->hDC, &pdis->rcItem, GetSysColorBrush( COLOR_HIGHLIGHT ));
      SetTextColor( pdis->hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ));
   }
   else {
      FillRect( pdis->hDC, &pdis->rcItem, GetSysColorBrush( COLOR_WINDOW ));
      SetTextColor( pdis->hDC, GetSysColor( COLOR_WINDOWTEXT ));
   }

   /* (( UINT ) -1 ) identifies the static edit control.  Windows
      apparently doesn't send WM_DRAWITEM for this, but it does send
      WM_MEASUREITEM. */

   if (( pdis->itemID & 0x80000000 ) == 0 ) {
      SendMessage( pdis->hwndItem, CB_GETLBTEXT, ( WPARAM ) pdis->itemID,
         ( LPARAM ) fontname );
      plf = ( LOGFONT * ) pdis->itemData;

      /* use system font if the font is non-ANSI */

      if ( plf->lfCharSet == ANSI_CHARSET ) {
         lf = *plf;
         lf.lfHeight = -16;
         lf.lfWidth = 0;
      }
      else {
         lf.lfHeight = -10;
         lf.lfWeight = FW_NORMAL;
         strcpy( lf.lfFaceName, "MS Sans Serif" );
      }

      /* select the font into the item's DC and draw the font name */

      hfont = CreateFontIndirect( &lf );
      hfont = SelectObject( pdis->hDC, hfont );

      DrawText( pdis->hDC, fontname, strlen( fontname ), &pdis->rcItem,
         DT_LEFT | DT_VCENTER | DT_SINGLELINE );

      DeleteObject( SelectObject( pdis->hDC, hfont ));
   }
}


/*
======================================================================
draw_color()

Called in response to a WM_DRAWITEM for the color combo box.  The
item's color is drawn in a rectangle to the left of the preset name
for the color.
====================================================================== */

static void draw_color( LPDRAWITEMSTRUCT pdis )
{
   RECT rect;
   HBRUSH hbrush;

   SetBkMode( pdis->hDC, TRANSPARENT );
   if ( pdis->itemState & ODS_SELECTED ) {
      FillRect( pdis->hDC, &pdis->rcItem, GetSysColorBrush( COLOR_HIGHLIGHT ));
      SetTextColor( pdis->hDC, GetSysColor( COLOR_HIGHLIGHTTEXT ));
   }
   else {
      FillRect( pdis->hDC, &pdis->rcItem, GetSysColorBrush( COLOR_WINDOW ));
      SetTextColor( pdis->hDC, GetSysColor( COLOR_WINDOWTEXT ));
   }

   if (( pdis->itemID & 0x80000000 ) == 0 ) {
      rect = pdis->rcItem;
      rect.left += 32;
      DrawText( pdis->hDC, colors[ pdis->itemID ].name,
         strlen( colors[ pdis->itemID ].name ), &rect,
         DT_LEFT | DT_VCENTER | DT_SINGLELINE );

      rect.left  = 4;
      rect.right = 24;
      InflateRect( &rect, 0, -2 );
      FrameRect( pdis->hDC, &rect, GetStockObject( BLACK_BRUSH ));

      InflateRect( &rect, -1, -1 );
      hbrush = CreateSolidBrush( colors[ pdis->itemID ].rgb );
      FillRect( pdis->hDC, &rect, hbrush );
      DeleteObject( hbrush );
   }
}


/*
======================================================================
select_color()

Called in response to a WM_COMMAND when the user selects "Custom" in
the font color combo box.  The color picker is initialized with the
current custom color, which receives the user's color choice if the
user dismisses the dialog with the OK button.
====================================================================== */

static int select_color( HWND hwnd )
{
   static COLORREF dlgcolors[ 16 ];
   static CHOOSECOLOR cc = {
      sizeof( CHOOSECOLOR ), 0, 0, 0, dlgcolors, CC_RGBINIT | CC_FULLOPEN };
   int i, result;

   for ( i = 0; i < NCOLORS; i++ )
      dlgcolors[ i ] = colors[ i ].rgb;
   cc.hwndOwner = hwnd;
   cc.rgbResult = colors[ CUSTINDEX ].rgb;

   result = ChooseColor( &cc );
   if ( result ) colors[ CUSTINDEX ].rgb = cc.rgbResult;

   return result;
}


/*
======================================================================
enum_font()

Callback set by init_fontlist() when it calls EnumFonts().  We add the
font name to the font combo box as an item string, and add a copy of
the corresponding LOGFONT as item data.  These are used when the font
name is later drawn by draw_fontname().
====================================================================== */

static BOOL CALLBACK enum_font( LOGFONT *lf, TEXTMETRIC *tm, int type,
   LPARAM lparam )
{
   HWND hwndcb;
   LPLOGFONT cblf;
   LRESULT i;

   if ( type == TRUETYPE_FONTTYPE ) {
      hwndcb = ( HWND ) lparam;
      cblf = calloc( 1, sizeof( LOGFONT ));
      *cblf = *lf;
      i = SendMessage( hwndcb, CB_ADDSTRING, 0, ( LPARAM ) lf->lfFaceName );
      SendMessage( hwndcb, CB_SETITEMDATA, i, ( LPARAM ) cblf );
   }
   return TRUE;
}


/*
======================================================================
init_fontlist()

Called in response to WM_INITDIALOG to initialize the font combo box.
Fonts are added to the list by calling EnumFonts().  Once the list is
built, the CB_SELECTSTRING message causes the list to be searched for
a font name corresponding to the current font in the QVText, and if
that's found, it's made the current selection in the combo box.
====================================================================== */

static void init_fontlist( HWND hwnd )
{
   HWND hwndcb = GetDlgItem( hwnd, TIDC_FONT );
   HDC hdc = CreateCompatibleDC( NULL );

   EnumFonts( hdc, NULL, ( FONTENUMPROC ) enum_font, ( LPARAM ) hwndcb );
   DeleteDC( hdc );
   SendMessage( hwndcb, CB_SELECTSTRING, ( WPARAM ) -1,
      ( LPARAM ) qvtext.lf.lfFaceName );
}


/*
======================================================================
free_fontlist()

Windows doesn't try to free the LOGFONTs we set as item data in the
font combo box, so we need to free those in response to WM_DESTROY.
====================================================================== */

static void free_fontlist( HWND hwnd )
{
   HWND hwndcb = GetDlgItem( hwnd, TIDC_FONT );
   LPLOGFONT lf;
   int i;
	 LRESULT n;

   n = SendMessage( hwndcb, CB_GETCOUNT, 0, 0 );
   for ( i = 0; i < n; i++ ) {
      lf = ( LPLOGFONT ) SendMessage( hwndcb, CB_GETITEMDATA, i, 0 );
      free( lf );
   }
}


/*
======================================================================
init_colorlist()

Called in response to WM_INITDIALOG to initialize the font color combo
box.  The preset names in colors[] are added as item strings.  One of
the presets is made the current selection if it matches the color in
the QVText, otherwise the Custom color is set to QVText.color and made
the current selection.
====================================================================== */

static void init_colorlist( HWND hwnd )
{
   int i, sel;
   HWND hwndcb = GetDlgItem( hwnd, TIDC_COLOR );

   for ( i = 0; i < NCOLORS; i++ )
      SendMessage( hwndcb, CB_ADDSTRING, 0, ( LPARAM ) colors[ i ].name );

   for ( i = 0, sel = -1; i < NCOLORS; i++ )
      if ( colors[ i ].rgb == qvtext.color ) {
         sel = i;
         break;
      }
   if ( sel == -1 ) {
      colors[ CUSTINDEX ].rgb = qvtext.color;
      sel = CUSTINDEX;
   }
   SendMessage( hwndcb, CB_SETCURSEL, ( WPARAM ) sel, 0 );
}


/*
======================================================================
init_sizelist()

Called in response to WM_INITDIALOG to initialize the font size combo
box.  The font size is stored as a negative number in the LOGFONT of
the QVText, to denote a pixel (rather than a point) size.  We do this
because pixel sizes are absolute, while point sizes are relative to
the resolution of the screen.
====================================================================== */

static void init_sizelist( HWND hwnd )
{
   int sizes[] = { 6, 7, 8, 9, 10, 11, 12, 14, 16,
      18, 20, 24, 28, 32, 40, 48, 60, 72, 0 };
   int i;
   char s[ 4 ];
   HWND hwndcb = GetDlgItem( hwnd, TIDC_SIZE );

   for ( i = 0; sizes[ i ]; i++ )
      SendMessage( hwndcb, CB_ADDSTRING, 0,
         ( LPARAM ) _itoa( sizes[ i ], s, 10 ));

   for ( i = 0; sizes[ i ]; i++ )
      if ( sizes[ i ] >= -qvtext.lf.lfHeight ) {
         SendMessage( hwndcb, CB_SETCURSEL, ( WPARAM ) i, 0 );
         break;
      }
   SetDlgItemInt( hwnd, TIDC_SIZE, -qvtext.lf.lfHeight, TRUE );
}


/*
======================================================================
measure_item()

Called in response to WM_MEASUREITEM, which is sent for owner-drawn
combobox controls.
====================================================================== */

static BOOL measure_item( HWND hwnd, LPMEASUREITEMSTRUCT mis )
{
   switch ( mis->CtlID ) {
      case TIDC_COLOR:
         mis->itemHeight = 15;
         return TRUE;
      case TIDC_FONT:
         mis->itemHeight += 4;
         return TRUE;
      default:
         break;
   }
   return FALSE;
}


/*
======================================================================
init_dlg()

Called in response to WM_INITDIALOG to initialize the dialog box and
its controls.
====================================================================== */

static void init_dlg( HWND hwnd )
{
   extern HICON hiconsm;
   LOGFONT lf = { 0 };

   SendMessage( hwnd, WM_SETICON, 0, ( LPARAM ) hiconsm );

   SetDlgItemText( hwnd, TIDC_TEXT, qvtext.text );
   SetDlgItemInt( hwnd, TIDC_X, qvtext.x, TRUE );
   SetDlgItemInt( hwnd, TIDC_Y, qvtext.y, TRUE );
   SetDlgItemInt( hwnd, TIDC_DX, qvtext.dx, TRUE );
   SetDlgItemInt( hwnd, TIDC_DY, qvtext.dy, TRUE );
   CheckDlgButton( hwnd, TIDC_DROP, qvtext.dropshadow );
   CheckRadioButton( hwnd, TIDC_LEFT, TIDC_RIGHT, TIDC_LEFT + qvtext.lcr );
   init_fontlist( hwnd );
   init_colorlist( hwnd );
   init_sizelist( hwnd );

   lf.lfHeight = -14;
   lf.lfWeight = FW_BOLD;
   strcpy( lf.lfFaceName, "Times New Roman" );
   hbold = CreateFontIndirect( &lf );
   SendMessage( GetDlgItem( hwnd, TIDC_BOLD ), WM_SETFONT,
      ( WPARAM ) hbold, 0 );
   CheckDlgButton( hwnd, TIDC_BOLD, qvtext.lf.lfWeight == FW_BOLD );

   lf.lfWeight = FW_NORMAL;
   lf.lfItalic = TRUE;
   hitalic = CreateFontIndirect( &lf );
   SendMessage( GetDlgItem( hwnd, TIDC_ITALIC ), WM_SETFONT,
      ( LPARAM ) hitalic, 0 );
   CheckDlgButton( hwnd, TIDC_ITALIC, qvtext.lf.lfItalic );

   SendMessage( GetDlgItem( hwnd, TIDC_SPINDX ), UDM_SETRANGE, 0,
      ( WPARAM ) MAKELONG( 200, -200 ));
   SendMessage( GetDlgItem( hwnd, TIDC_SPINDY ), UDM_SETRANGE, 0,
      ( WPARAM ) MAKELONG( 200, -200 ));
}


/*
======================================================================
get_dlg()

Retrieve the values of the dialog's controls.  Called in response to a
WM_COMMAND set by the IDOK button.
====================================================================== */

static int get_dlg( HWND hwnd )
{
   LRESULT i;
   BOOL ok;
   HWND hctl;
   LPLOGFONT lf;

   GetDlgItemText( hwnd, TIDC_TEXT, qvtext.text, sizeof( qvtext.text ));

   hctl = GetDlgItem( hwnd, TIDC_FONT );
   i = SendMessage( hctl, CB_GETCURSEL, 0, 0 );
   if ( i >= 0 ) {
      lf = ( LPLOGFONT ) SendMessage( hctl, CB_GETITEMDATA,
         ( WPARAM ) i, 0 );
      qvtext.lf = *lf;
      qvtext.lf.lfWidth = 0;
   }
   qvtext.lf.lfHeight = -(( int ) GetDlgItemInt( hwnd, TIDC_SIZE, &ok, FALSE ));
   qvtext.lf.lfItalic = IsDlgButtonChecked( hwnd, TIDC_ITALIC );
   qvtext.lf.lfWeight = IsDlgButtonChecked( hwnd, TIDC_BOLD ) ?
      FW_BOLD : FW_NORMAL;

   hctl = GetDlgItem( hwnd, TIDC_COLOR );
   i = SendMessage( hctl, CB_GETCURSEL, 0, 0 );
   qvtext.color = colors[ i ].rgb;

   qvtext.x  = GetDlgItemInt( hwnd, TIDC_X,  &ok, TRUE );
   qvtext.y  = GetDlgItemInt( hwnd, TIDC_Y,  &ok, TRUE );
   qvtext.dx = GetDlgItemInt( hwnd, TIDC_DX, &ok, TRUE );
   qvtext.dy = GetDlgItemInt( hwnd, TIDC_DY, &ok, TRUE );
   qvtext.dropshadow = IsDlgButtonChecked( hwnd, TIDC_DROP );
   for ( i = 0; i < 3; i++ )
      if ( IsDlgButtonChecked( hwnd, (int)TIDC_LEFT + i ))
         break;
   qvtext.lcr = (int)i;

   return TRUE;
}


/*
======================================================================
TextDlgProc()

The dialog's window procedure.
====================================================================== */

static INT_PTR  WINAPI
TextDlgProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
   int id = LOWORD( wparam );

   switch ( msg )
   {
      case WM_INITDIALOG:
         init_dlg( hwnd );
         return TRUE;

      case WM_MEASUREITEM:
         return measure_item( hwnd, ( LPMEASUREITEMSTRUCT ) lparam );

      case WM_DRAWITEM:
         switch( id )
         {
            case TIDC_FONT:
               draw_fontname(( LPDRAWITEMSTRUCT ) lparam );
               return TRUE;

            case TIDC_COLOR:
               draw_color(( LPDRAWITEMSTRUCT ) lparam );
               return TRUE;
         }
         break;

      case WM_COMMAND:
         switch ( id )
         {
            case TIDC_LEFT:
            case TIDC_CENTER:
            case TIDC_RIGHT:
               CheckRadioButton( hwnd, TIDC_LEFT, TIDC_RIGHT, id );
               return TRUE;

            case TIDC_COLOR:
               if ( HIWORD( wparam ) == CBN_SELCHANGE )
                  if ( CUSTINDEX == SendMessage(( HWND ) lparam, CB_GETCURSEL, 0, 0 )) {
                     select_color( hwnd );
                     return TRUE;
                  }
               break;

            case IDOK:
               if ( get_dlg( hwnd ))
                  EndDialog( hwnd, TRUE );
               return TRUE;

            case IDCANCEL:
               EndDialog( hwnd, FALSE );
               return TRUE;
         }
         break;

      case WM_DESTROY:
         free_fontlist( hwnd );
         DeleteObject( hbold );
         DeleteObject( hitalic );
         break;

      default:
         break;
   }

   return FALSE;
}


/*
======================================================================
textDialog()

Clients call this to display the text dialog.  If the user presses OK,
the QVText pointed to by the second argument is updated with the
user's settings.
====================================================================== */

INT_PTR textDialog( HWND hwnd, QVText *t )
{
   HINSTANCE hinst;
   INT_PTR result;

   qvtext = *t;
   hinst = ( HINSTANCE ) GetWindowLongPtr( hwnd, GWLP_HINSTANCE );
   result = DialogBox( hinst, "qvtext", hwnd, TextDlgProc );
   if ( result ) *t = qvtext;

   return result;
}
