#include "windows.h"
#include "fontangl.h"

/*  Sample Angled Font Manipulation Module
 */

extern long FAR PASCAL lstrlen();
extern long FAR PASCAL lstrcpy();
extern long FAR PASCAL lstrcat();

int yCoord;         /* the y coordinate of the starting text */
int nFirstIndex;    /* the font currently displayed at the top */
int nLastIndex;     /* the font currently displayed at the bottom */
int nTopClipped;    /* the clipped portion of the top line of text */
int nBottomClipped; /* the clipped portion of the bottom line of text */
int nScrollBarTop;  /* the nLastIndex at which the scrollbar is at the top */
int nFontCount;     /* number of fonts enumerated */
HFONT hFontANSIFIXED;

HBRUSH hbrWhite;
HBRUSH hbrBlack;
HBRUSH hbrGray;

char    szName[ LF_FACESIZE + 10 ];

/* Save away all enumerated LogFonts and TextMetrics */
LOGFONT LF[ MAXFONTS ];
TEXTMETRIC TM[ MAXFONTS ];

/* the two callback routines */
static FARPROC eafproc;
static FARPROC cbproc;

typedef struct {
    HDC     hDC;
    FARPROC lpCallbackFunc;
} EAFDataType;


MyCopyStruct( dst, src, size )
PSTR dst;
LPSTR src;
int size;
{
    while (size--)
        *dst++ = *src++;
}


BOOL MyCompStruct( pS1, pS2, size )
BYTE *pS1;
BYTE *pS2;
int size;
{
    while (size--)
        if (*pS1++ != *pS2++)
            return FALSE;
    return TRUE;
}


EnumAllFonts( hWnd, lpCallbackFunc )
HWND hWnd;
FARPROC lpCallbackFunc;
{
    HDC hDC;
    EAFDataType EAFData;

    nFontCount = 0;
    hDC = GetDC( hWnd );

    (EAFData.hDC)            = hDC;
    (EAFData.lpCallbackFunc) = lpCallbackFunc;

    /* Enumerates one font from each facename */

    EnumFonts( hDC, (LPSTR)NULL, eafproc, (LPSTR)&EAFData );

    ReleaseDC( hWnd, hDC );
}


short int FAR PASCAL EAFCallback( lpLogFont, lpTextMetrics, FontType, lpEAFData )
LOGFONT FAR *lpLogFont;
TEXTMETRIC FAR *lpTextMetrics;
WORD FontType;
EAFDataType FAR *lpEAFData;
{
    /* Enumerates all fonts with the same facename */

    return( EnumFonts( lpEAFData->hDC,
                       (LPSTR)lpLogFont->lfFaceName,
                       lpEAFData->lpCallbackFunc,
                       (LPSTR)NULL ) );
}


short int FAR PASCAL cb( lpLogfont, lpTextMetrics, nFontType, lpData )
LPLOGFONT lpLogfont;
LPTEXTMETRIC lpTextMetrics;
WORD nFontType;
LONG lpData;
{

    /* Skip over any raster fonts. We only want vector fonts. */
    /* ################################################################### */
    if ( nFontType & RASTER_FONTTYPE ) return TRUE;

    MyCopyStruct( (PSTR)&LF[nFontCount], (LPSTR)lpLogfont, sizeof(LOGFONT) );
    MyCopyStruct( (PSTR)&TM[nFontCount], (LPSTR)lpTextMetrics, sizeof(TEXTMETRIC) );

    /* Stop callback when we can't hold anymore */

    if (++nFontCount == MAXFONTS)
        return FALSE;
    else
        return TRUE;
}


/* ######################################################################### */
DWORD PlaceFont( hDC, x, y, pLogfont )
HDC hDC;
short x, y;
LOGFONT *pLogfont;
{
    HFONT hLocalFont;
    DWORD   Extent;

    hLocalFont = CreateFontIndirect( (LPLOGFONT)pLogfont );
    SelectObject( hDC, (HANDLE)hLocalFont );

    TextOut( hDC,
             x, y,
             (LPSTR)szName,
             lstrlen((LPSTR)szName) );

    Extent = GetTextExtent(hDC,
                           (LPSTR)szName,
                           lstrlen((LPSTR)szName) );

    /* Blow away the memory allocated for the font */

    SelectObject( hDC, hFontANSIFIXED );
    DeleteObject( hLocalFont );

    return ( Extent );
}


#define OPB() \
    TextOut( hDC, CurX, CurY, (LPSTR)Buffer, Chars ); \
    CurY += TextMetric.tmHeight + TextMetric.tmExternalLeading;


ShowFont( hDC, i, y, WindowWidth )
HDC hDC;
int i;          /* index into LF and TM */
int y;
int WindowWidth;
{
    LOGFONT *pLogfont;
    short   CurX, CurY;
    DWORD   Extent;
    short   OldBkMode;
    short   Angle;
    char    Buffer[256];
    short   Chars;
    TEXTMETRIC  TextMetric;

    OldBkMode = SetBkMode( hDC, TRANSPARENT );

    pLogfont = &(LF[i]);

    GetTextMetrics( hDC, &TextMetric );

    CurX = TextMetric.tmAveCharWidth;
    CurY = y;

    Chars = sprintf(Buffer, "lfCharSet       = %d",
                   pLogfont->lfCharSet      ); OPB();
    Chars = sprintf(Buffer, "lfOutPrecision  = %d",
                   pLogfont->lfOutPrecision ); OPB();
    Chars = sprintf(Buffer, "lfClipPrecision = %d",
                   pLogfont->lfClipPrecision); OPB();

    CurX = ( TextMetric.tmAveCharWidth + WindowWidth ) / 2;
    CurY = y;

    Chars = sprintf(Buffer, "lfQuality             = %d",
                   pLogfont->lfQuality             ); OPB();
    Chars = sprintf(Buffer, "lfPitchAndFamily&0x03 = %d",
                   pLogfont->lfPitchAndFamily&0x03 ); OPB();
    Chars = sprintf(Buffer, "lfPitchAndFamily&0xF0 = %d",
                   pLogfont->lfPitchAndFamily&0xF0 ); OPB();

    lstrcpy( (LPSTR)szName, (LPSTR)"  " );
    lstrcat( (LPSTR)szName, (LPSTR)pLogfont->lfFaceName );

    CurX = TM[i].tmAveCharWidth;
    CurY = y;

    pLogfont->lfEscapement  = 3600-900;
    pLogfont->lfOrientation = 0;
    Extent = PlaceFont( hDC, CurX, CurY, pLogfont );

    CurX += ( LOWORD(Extent) + WindowWidth ) / 2;
    CurY += (HIWORD(Extent) - CHARHEIGHT( i ));

    for (Angle = 0; Angle <= 1800; Angle += 225) {
        pLogfont->lfEscapement  = pLogfont->lfOrientation = Angle;
        PlaceFont( hDC, CurX, CurY, pLogfont );
    }

    pLogfont->lfEscapement  = 0;
    pLogfont->lfOrientation = 0;
    SetBkMode( hDC, OldBkMode );
}
/* ######################################################################### */


LONG FontAnglPaint( hDC, nRepaintBegin, nRepaintEnd, WindowWidth )
HDC hDC;
int nRepaintBegin, nRepaintEnd;
int WindowWidth;
{
    int i, y, h;

    /* Convert the nRepaintBegin line to a font index */

    i = nFirstIndex;
    y = -nTopClipped;
    h = 0;
    do {
        y += h;
        h = STRINGHEIGHT(i);
        i++;
    } while ( (i < nFontCount) && ( (y + h) < nRepaintBegin ));

    /* Backtrack since we passed it */

    i--;

    /* Repaint until we pass the nRepaintEnd line */

    for (; (i < nFontCount) && (y < nRepaintEnd); i++) {
        ShowFont( hDC, i, y, WindowWidth );
        y += STRINGHEIGHT(i);
        }

    /* Return the last displayed font index and its clipped amount */

    h = y - nRepaintEnd;
    return( MAKELONG( i-1, (h<0 ? 0 : h) ) );
}


int FontAnglScroll( hWnd, cmd )
HWND hWnd;
int cmd;
{
    int samount, sm, h;

    switch( cmd )
    {
    case SB_LINEDOWN:
        if ((nLastIndex < nFontCount-1) || nBottomClipped) {

            /* Figure out how much to scroll up:
             *   Reveal the bottom row if some part of it was hidded,
             *   otherwise bring in the next row.
             */

            if (nBottomClipped)
                sm = nBottomClipped;
            else {
                nLastIndex++;
                sm = STRINGHEIGHT(nLastIndex);
                }
            samount = sm;
            nBottomClipped = 0;

            /* Move up enough rows to reveal the scroll amount */

            h = STRINGHEIGHT(nFirstIndex) - nTopClipped;
            while ( (sm-=h) >= 0 ) {
                nFirstIndex++;
                h = STRINGHEIGHT(nFirstIndex);
                }

            /* Clip the top row with the remaining pixels */

            if (sm)
                nTopClipped = STRINGHEIGHT(nFirstIndex) + sm;
            else
                nTopClipped = 0;

            /* Scroll up */

            ScrollWindow( hWnd,
                          0,
                          -samount,
                          (LPRECT)NULL,
                          (LPRECT)NULL );
            SetScrollPos( hWnd, SB_VERT, nLastIndex-nScrollBarTop, TRUE );
            }
        break;

    case SB_LINEUP:
        if (nFirstIndex || nTopClipped) {

            /* Figure out how much to scroll down */

            if (nTopClipped)
                sm = nTopClipped;
            else {
                nFirstIndex--;
                sm = STRINGHEIGHT(nFirstIndex);
                }
            samount = sm;
            nTopClipped = 0;

            /* Move down enough rows to make top row completely visible */

            h = STRINGHEIGHT(nLastIndex) - nBottomClipped;
            while ( (sm-=h) >= 0 ) {
                nLastIndex--;
                h = STRINGHEIGHT(nLastIndex);
                }

            /* Figure out clipped amount as result of scrolling */

            if (sm)
                nBottomClipped = STRINGHEIGHT(nLastIndex) + sm;
            else
                nBottomClipped = 0;

            /* Scroll down */

            ScrollWindow( hWnd,
                          0,
                          samount,
                          (LPRECT)NULL,
                          (LPRECT)NULL );
            SetScrollPos( hWnd, SB_VERT, nLastIndex-nScrollBarTop, TRUE );
            }
        break;
    }
}


int FontAnglSize( hWnd, width, length )
HWND hWnd;
int width, length;
{
    static int oldlength = 0;

    /* Redraw completely if vertical position changed:  avoid the
     *                                 -----    -----
     * case where tiling changes from  |---| to | | | and the last
     *                                 -----    -----
     * font is displayed at the bottom.
     */

    if (length != oldlength) {
        nFirstIndex = 0;
        nTopClipped = 0;
        oldlength = length;
        }
}


int FontAnglCommand( hWnd, cmd )
HWND hWnd;
WORD cmd;
{
    if (cmd == START_SESSION) {
        EnumAllFonts( hWnd, cbproc );
        nFirstIndex = 0;
        nTopClipped = 0;
        InvalidateRect( hWnd, (LPRECT)NULL, TRUE );
        UpdateWindow( hWnd );
        }
}


long FAR PASCAL FontAnglWndProc( hWnd, message, wParam, lParam )
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
    PAINTSTRUCT ps;
    RECT rcEntire;
    int i;
    LONG l;

    switch (message)
    {
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;

    case WM_COMMAND:
        FontAnglCommand( hWnd, wParam );
        break;

    case WM_PAINT:
        GetClientRect( hWnd, (LPRECT)&rcEntire );
        BeginPaint( hWnd, (LPPAINTSTRUCT)&ps );
        l = FontAnglPaint( ps.hdc,
                           ps.rcPaint.top,
                           ps.rcPaint.bottom,
                           rcEntire.right - rcEntire.left );

        /* If the whole client rectangle is repainted, remember the index
         * of the last font displayed, and if it was clipped at the bottom.
         * This info helps in scrolling.
         */

        if (nFontCount &&
            MyCompStruct( (BYTE *)&(ps.rcPaint), (BYTE *)&rcEntire, sizeof(RECT)))
            {
            nLastIndex = LOWORD( l );
            nBottomClipped = HIWORD( l );

            /* Redefine scrollbar if redrew from the top */

            if (nFirstIndex==0) {
                nScrollBarTop = nLastIndex;
                i = (nFontCount-1)-nScrollBarTop;
                if (i<=0) {
                    if (nBottomClipped)
                        SetScrollRange( hWnd, SB_VERT, 0, 1, FALSE );
                    else
                        /* No need to scroll */
                        SetScrollRange( hWnd, SB_VERT, 0, 32767, FALSE );
                    }
                else
                    SetScrollRange( hWnd, SB_VERT, 0, i, FALSE );
                SetScrollPos( hWnd, SB_VERT, 0, TRUE );
                }
            }
        EndPaint( hWnd, (LPPAINTSTRUCT)&ps );
        break;

    case WM_SIZE:
        FontAnglSize( hWnd, LOWORD(lParam), HIWORD(lParam) );
        break;

    case WM_VSCROLL:
        FontAnglScroll( hWnd, wParam );
        break;

    default :
        return (DefWindowProc( hWnd, message, wParam, lParam));
        break;
    }
    return(0L);
}


FontAnglInit( hInstance )
HANDLE hInstance;
{
    PWNDCLASS   pFontAnglClass;

    /* Set up some default brushes */

    hbrWhite = GetStockObject( WHITE_BRUSH );
    hbrBlack = GetStockObject( BLACK_BRUSH );
    hbrGray  = GetStockObject( GRAY_BRUSH );
    hFontANSIFIXED = GetStockObject ( ANSI_FIXED_FONT );

    /* Allocate class structure in local heap */

    pFontAnglClass = (PWNDCLASS)LocalAlloc( LPTR, sizeof(WNDCLASS) );
    pFontAnglClass->hIcon         = LoadIcon( hInstance,(LPSTR)"fontangl" );
    pFontAnglClass->hCursor       = LoadCursor( NULL, IDC_ARROW );
    pFontAnglClass->lpszMenuName  = (LPSTR)"fontAngl";
    pFontAnglClass->lpszClassName = (LPSTR)"FontAngl";
    pFontAnglClass->hbrBackground = (HBRUSH)hbrWhite;
    pFontAnglClass->style         = CS_HREDRAW | CS_VREDRAW;
    pFontAnglClass->lpfnWndProc   = FontAnglWndProc;
    pFontAnglClass->hInstance     = hInstance;

    if (!RegisterClass( (LPWNDCLASS)pFontAnglClass ) )
        return FALSE;

    LocalFree( (HANDLE)pFontAnglClass );
    return TRUE;    /* Initialization succeeded */
}


int PASCAL WinMain( hInstance, hPrev, lpszCmdLine, cmdShow )
HANDLE hInstance, hPrev;
LPSTR lpszCmdLine;
int cmdShow;
{
    MSG msg;
    HWND hWnd;

    if (!hPrev) {
        if ( !FontAnglInit(hInstance) )
            return( FALSE );
        }
    else {
        GetInstanceData( hPrev, (PSTR)&hFontANSIFIXED, sizeof( HFONT ) );
        GetInstanceData( hPrev, (PSTR)&hbrWhite, sizeof( hbrWhite ) );
        GetInstanceData( hPrev, (PSTR)&hbrBlack, sizeof( hbrBlack ) );
        GetInstanceData( hPrev, (PSTR)&hbrGray,  sizeof( hbrGray ) );
        }

    hWnd = (HWND)CreateWindow(
        (LPSTR) "FontAngl",
        (LPSTR) "Angled Fonts",
        WS_TILEDWINDOW | WS_VSCROLL,
        0, 0, 0, 100,
        (HANDLE)NULL,
        (HANDLE)NULL,
        (HANDLE)hInstance,
        (HANDLE)NULL
        );

    eafproc = MakeProcInstance( (FARPROC)EAFCallback, hInstance );
    cbproc  = MakeProcInstance( (FARPROC)cb, hInstance );

    ShowWindow(hWnd, cmdShow);
    UpdateWindow( hWnd );

    while (GetMessage((LPMSG)&msg, NULL, 0, 0)) {
        TranslateMessage((LPMSG)&msg);
        DispatchMessage((LPMSG)&msg);
        }

    return msg.wParam;
}
