// $Id: wingcc.c 12965 2014-01-28 20:23:52Z arjenmarkus $
//
//      PLplot WIN32 under GCC device driver.
//
// Copyright (C) 2004  Andrew Roach
//
// This file is part of PLplot.
//
// PLplot is free software; you can redistribute it and/or modify
// it under the terms of the GNU Library General Public License as published
// by the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// PLplot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with PLplot; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
//
#include "plDevs.h"

#ifdef PLD_wingcc

#include <string.h>
#include <windows.h>
#if !defined ( __CYGWIN__ )
#include <tchar.h>
#else
#include <winnt.h>
#define _T( a )    __TEXT( a )
#endif
#ifdef _WIN64
#define GWL_USERDATA    GWLP_USERDATA
#define GCL_HCURSOR     GCLP_HCURSOR
#endif

#include "plplotP.h"
#include "drivers.h"
#include "plevent.h"

#ifdef PL_HAVE_FREETYPE

//
//  Freetype support has been added to the wingcc driver using the
//  plfreetype.c module, and implemented as a driver-specific optional extra
//  invoked via the -drvopt command line toggle. It uses the
//  "PLESC_HAS_TEXT" command for rendering within the driver.
//
//  Freetype support is turned on/off at compile time by defining
//  "PL_HAVE_FREETYPE".
//
//  To give the user some level of control over the fonts that are used,
//  environmental variables can be set to over-ride the definitions used by
//  the five default plplot fonts.
//
//  Freetype rendering is used with the command line "-drvopt text".
//  Anti-aliased fonts can be used by issuing "-drvopt text,smooth"
//

#include "plfreetype.h"

#ifndef max_number_of_grey_levels_used_in_text_smoothing
#define max_number_of_grey_levels_used_in_text_smoothing    64
#endif

#endif


// Device info

PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wingcc = "wingcc:Win32 (GCC):1:wingcc:9:wingcc\n";

// Struct to hold device-specific info.

typedef struct
{
    PLFLT scale;                     // scaling factor to "blow up" to the "virtual" page in removing hidden lines
    PLINT width;                     // Window width (which can change)
    PLINT height;                    // Window Height

    PLFLT PRNT_scale;
    PLINT PRNT_width;
    PLINT PRNT_height;

    char  FT_smooth_text;
//
// WIN32 API variables
//

    COLORREF          colour;                       // Current Colour
    COLORREF          oldcolour;                    // Used for high-speed background erasing
    MSG               msg;                          // A Win32 message structure.
    WNDCLASSEX        wndclass;                     // An extended window class structure.
    HWND              hwnd;                         // Handle for the main window.
    HPEN              pen;                          // Windows pen used for drawing
    HDC               hdc;                          // Driver Context
    HDC               hdc2;                         // Driver Context II - used for Blitting
    HDC               SCRN_hdc;                     // The screen's context
    HDC               PRNT_hdc;                     // used for printing
    PAINTSTRUCT       ps;                           // used to paint the client area of a window owned by that application
    RECT              rect;                         // defines the coordinates of the upper-left and lower-right corners of a rectangle
    RECT              oldrect;                      // used for re-sizing comparisons
    RECT              paintrect;                    // used for painting etc...
    HBRUSH            fillbrush;                    // brush used for fills
    HCURSOR           cursor;                       // Current windows cursor for this window
    HBITMAP           bitmap;                       // Bitmap of current display; used for fast redraws via blitting
    HGDIOBJ           oldobject;                    // Used for tracking objects probably not really needed but
    HMENU             PopupMenu;

    PLINT             draw_mode;
    char              truecolour;      // Flag to indicate 24 bit mode
    char              waiting;         // Flag to indicate drawing is done, and it is waiting;
                                       // we only do a windows redraw if plplot is plotting
    char              enterresize;     // Used to keep track of reszing messages from windows
    char              already_erased;  // Used to track first and only first backgroudn erases

    struct wingcc_Dev *push;           // A copy of the entire structure used when printing
                                       // We push and pop it off a virtual stack
} wingcc_Dev;


void plD_dispatch_init_wingcc( PLDispatchTable *pdt );

void plD_init_wingcc( PLStream * );
void plD_line_wingcc( PLStream *, short, short, short, short );
void plD_polyline_wingcc( PLStream *, short *, short *, PLINT );
void plD_eop_wingcc( PLStream * );
void plD_bop_wingcc( PLStream * );
void plD_tidy_wingcc( PLStream * );
void plD_state_wingcc( PLStream *, PLINT );
void plD_esc_wingcc( PLStream *, PLINT, void * );

#ifdef PL_HAVE_FREETYPE

static void plD_pixel_wingcc( PLStream *pls, short x, short y );
static void plD_pixelV_wingcc( PLStream *pls, short x, short y );
static PLINT plD_read_pixel_wingcc( PLStream *pls, short x, short y );
static void plD_set_pixel_wingcc( PLStream *pls, short x, short y, PLINT colour );
static void plD_set_pixelV_wingcc( PLStream *pls, short x, short y, PLINT colour );
static void init_freetype_lv1( PLStream *pls );
static void init_freetype_lv2( PLStream *pls );

#endif


//--------------------------------------------------------------------------
//  Local Function definitions and function-like defines
//--------------------------------------------------------------------------

static int GetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int size );
static int SetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int dwType, int size );
static void Resize( PLStream *pls );
static void plD_fill_polygon_wingcc( PLStream *pls );
static void CopySCRtoBMP( PLStream *pls );
static void PrintPage( PLStream *pls );
static void UpdatePageMetrics( PLStream *pls, char flag );

#define SetRegStringValue( a, b, c )       SetRegValue( a, b, c, REG_SZ, strlen( c ) + 1 )
#define SetRegBinaryValue( a, b, c, d )    SetRegValue( a, b, (char *) c, REG_BINARY, d )
#define SetRegIntValue( a, b, c )          SetRegValue( a, b, (char *) c, REG_DWORD, 4 )
#define GetRegStringValue( a, b, c, d )    GetRegValue( a, b, c, d )
#define GetRegIntValue( a, b, c )          GetRegValue( a, b, (char *) c, 4 )
#define GetRegBinaryValue( a, b, c, d )    GetRegValue( a, b, (char *) c, d )

//--------------------------------------------------------------------------
//  Some debugging macros
//--------------------------------------------------------------------------

#if defined ( _MSC_VER )
  #define Debug( a )           do { if ( pls->debug ) { fprintf( stderr, ( a ) ); } } while ( 0 )
  #define Debug2( a, b )       do { if ( pls->debug ) { fprintf( stderr, ( a ), ( b ) ); } } while ( 0 )
  #define Debug3( a, b, c )    do { if ( pls->debug ) { fprintf( stderr, ( a ), ( b ), ( c ) ); } } while ( 0 )
#elif defined ( __BORLANDC__ )
  #define Debug     if ( pls->debug ) printf
  #define Debug2    if ( pls->debug ) printf
  #define Debug3    if ( pls->debug ) printf
#else
  #define Verbose( ... )     do { if ( pls->verbose ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 )
  #define Debug( ... )       do { if ( pls->debug ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 )
  #define Debug2( ... )      do { if ( pls->debug ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 )
  #define Debug3( ... )      do { if ( pls->debug ) { fprintf( stderr, __VA_ARGS__ ); } } while ( 0 )
#endif

#define ReportWinError()     do {                                                       \
        LPVOID lpMsgBuf;                                                                \
        FormatMessage(                                                                  \
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,                \
            NULL, GetLastError(),                                                       \
            MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPTSTR) &lpMsgBuf, 0, NULL ); \
        MessageBox( NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION );       \
        LocalFree( lpMsgBuf ); } while ( 0 )

#define CrossHairCursor()    do {                                   \
        dev->cursor = LoadCursor( NULL, IDC_CROSS );                \
        SetClassLong( dev->hwnd, GCL_HCURSOR, (long) dev->cursor ); \
        SetCursor( dev->cursor ); } while ( 0 )

#define NormalCursor()       do {                                          \
        dev->cursor = LoadCursor( NULL, IDC_ARROW );                       \
        SetClassLongPtr( dev->hwnd, GCL_HCURSOR, (LONG_PTR) dev->cursor ); \
        SetCursor( dev->cursor ); } while ( 0 )

#define BusyCursor()         do {                                          \
        dev->cursor = LoadCursor( NULL, IDC_WAIT );                        \
        SetClassLongPtr( dev->hwnd, GCL_HCURSOR, (LONG_PTR) dev->cursor ); \
        SetCursor( dev->cursor ); } while ( 0 )

#define PopupPrint       0x08A1
#define PopupNextPage    0x08A2
#define PopupQuit        0x08A3


void plD_dispatch_init_wingcc( PLDispatchTable *pdt )
{
#ifndef ENABLE_DYNDRIVERS
    pdt->pl_MenuStr = "Win32 GCC device";
    pdt->pl_DevName = "wingcc";
#endif
    pdt->pl_type     = plDevType_Interactive;
    pdt->pl_seq      = 9;
    pdt->pl_init     = (plD_init_fp) plD_init_wingcc;
    pdt->pl_line     = (plD_line_fp) plD_line_wingcc;
    pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wingcc;
    pdt->pl_eop      = (plD_eop_fp) plD_eop_wingcc;
    pdt->pl_bop      = (plD_bop_fp) plD_bop_wingcc;
    pdt->pl_tidy     = (plD_tidy_fp) plD_tidy_wingcc;
    pdt->pl_state    = (plD_state_fp) plD_state_wingcc;
    pdt->pl_esc      = (plD_esc_fp) plD_esc_wingcc;
}

static TCHAR* szWndClass = _T( "PlplotWin" );


//--------------------------------------------------------------------------
// This is the window function for the plot window. Whenever a message is
// dispatched using DispatchMessage (or sent with SendMessage) this function
// gets called with the contents of the message.
//--------------------------------------------------------------------------

LRESULT CALLBACK PlplotWndProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
{
    PLStream   *pls = NULL;
    wingcc_Dev *dev = NULL;

//
// The window carries a 32bit user defined pointer which points to the
// plplot stream (pls). This is used for tracking the window.
// Unfortunately, this is "attached" to the window AFTER it is created
// so we can not initialise PLStream or wingcc_Dev "blindly" because
// they may not yet have been initialised.
// WM_CREATE is called before we get to initialise those variables, so
// we wont try to set them.
//

    if ( nMsg == WM_CREATE )
    {
        return ( 0 );
    }
    else
    {
#ifndef _WIN64
#undef GetWindowLongPtr
#define GetWindowLongPtr    GetWindowLong
#endif
        pls = (PLStream *) GetWindowLongPtr( hwnd, GWL_USERDATA ); // Try to get the address to pls for this window
        if ( pls )                                                 // If we got it, then we will initialise this windows plplot private data area
        {
            dev = (wingcc_Dev *) pls->dev;
        }
    }

//
// Process the windows messages
//
// Everything except WM_CREATE is done here and it is generally hoped that
// pls and dev are defined already by this stage.
// That will be true MOST of the time. Some times WM_PAINT will be called
// before we get to initialise the user data area of the window with the
// pointer to the windows plplot stream
//

    switch ( nMsg )
    {
    case WM_DESTROY:
        if ( dev )
            Debug( "WM_DESTROY\t" );
        PostQuitMessage( 0 );
        return ( 0 );
        break;

    case WM_PAINT:
        if ( dev )
        {
            Debug( "WM_PAINT\t" );
            if ( GetUpdateRect( dev->hwnd, &dev->paintrect, TRUE ) )
            {
                BusyCursor();
                BeginPaint( dev->hwnd, &dev->ps );

                if ( ( dev->waiting == 1 ) && ( dev->already_erased == 1 ) )
                {
                    Debug( "Remaking\t" );

                    if ( dev->ps.fErase )
                    {
                        dev->oldcolour = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
                        ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &dev->rect, _T( "" ), 0, 0 );
                        SetBkColor( dev->hdc, dev->oldcolour );
                    }

                    plRemakePlot( pls );
                    CopySCRtoBMP( pls );
                    dev->already_erased++;
                }
                else if ( ( dev->waiting == 1 ) && ( dev->already_erased == 2 ) )
                {
                    dev->oldobject = SelectObject( dev->hdc2, dev->bitmap );
                    BitBlt( dev->hdc, dev->paintrect.left, dev->paintrect.top,
                        dev->paintrect.right, dev->paintrect.bottom,
                        dev->hdc2, dev->paintrect.left, dev->paintrect.top, SRCCOPY );
                    SelectObject( dev->hdc2, dev->oldobject );
                }

                EndPaint( dev->hwnd, &dev->ps );
                NormalCursor();
                return ( 0 );
            }
        }
        return ( 1 );
        break;

    case WM_SIZE:
        if ( dev )
        {
            Debug( "WM_SIZE\t" );

            if ( dev->enterresize == 0 )
                Resize( pls );
        }
        return ( 0 );
        break;

    case WM_ENTERSIZEMOVE:
        if ( dev )
        {
            Debug( "WM_ENTERSIZEMOVE\t" );
            dev->enterresize = 1;
        }
        return ( 0 );
        break;


    case WM_EXITSIZEMOVE:
        if ( dev )
        {
            Debug( "WM_EXITSIZEMOVE\t" );
            Resize( pls );
            dev->enterresize = 0;     // Reset the variables that track sizing ops
        }
        return ( 0 );
        break;

    case WM_ERASEBKGND:

        if ( dev )
        {
            if ( dev->already_erased == 0 )
            {
                Debug( "WM_ERASEBKGND\t" );

                //
                //    This is a new "High Speed" way of filling in the background.
                //    supposidely this executes faster than creating a brush and
                //    filling a rectangle - go figure ?
                //

                dev->oldcolour = SetBkColor( dev->hdc, RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
                ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &dev->rect, _T( "" ), 0, 0 );
                SetBkColor( dev->hdc, dev->oldcolour );

                dev->already_erased = 1;
                return ( 1 );
            }
        }
        return ( 0 );
        break;

    case WM_COMMAND:
        if ( dev )
            Debug( "WM_COMMAND\t" );
        return ( 0 );
        break;
    }

    // If we don't handle a message completely we hand it to the system
    // provided default window function.
    return DefWindowProc( hwnd, nMsg, wParam, lParam );
}


//--------------------------------------------------------------------------
// plD_init_wingcc()
//
// Initialize device (terminal).
//--------------------------------------------------------------------------

void
plD_init_wingcc( PLStream *pls )
{
    wingcc_Dev *dev;

#ifdef PL_HAVE_FREETYPE
    static int freetype    = 0;
    static int smooth_text = 0;
    static int save_reg    = 0;
    FT_Data    *FT;

//
//  Variables used for reading the registary keys
//  might eventually add a user defined pallette here, but for now it just does freetype
//
    TCHAR key_name[]       = _T( "Software\\PLplot\\wingcc" );
    TCHAR Keyword_text[]   = _T( "freetype" );
    TCHAR Keyword_smooth[] = _T( "smooth" );
#endif

    DrvOpt wingcc_options[] = {
#ifdef PL_HAVE_FREETYPE
        { "text",   DRV_INT, &freetype,    "Use driver text (FreeType)"            },
        { "smooth", DRV_INT, &smooth_text, "Turn text smoothing on (1) or off (0)" },
        { "save",   DRV_INT, &save_reg,    "Save defaults to registary"            },

#endif
        { NULL,     DRV_INT, NULL,         NULL                                    }
    };

//
// Variable for storing the program name
//
    TCHAR *program;
    int   programlength;

// Allocate and initialize device-specific data

    if ( pls->dev != NULL )
        free( (void *) pls->dev );

    pls->dev = calloc( 1, (size_t) sizeof ( wingcc_Dev ) );
    if ( pls->dev == NULL )
        plexit( "plD_init_wingcc_Dev: Out of memory." );

    dev = (wingcc_Dev *) pls->dev;

    pls->icol0 = 1;                   // Set a fall back pen colour in case user doesn't

    pls->termin      = 1;             // interactive device
    pls->graphx      = GRAPHICS_MODE; //  No text mode for this driver (at least for now, might add a console window if I ever figure it out and have the inclination)
    pls->dev_fill0   = 1;             // driver can do solid area fills
    pls->dev_xor     = 1;             // driver supports xor mode
    pls->dev_clear   = 0;             // driver does not support clear - what is the proper API?
    pls->dev_dash    = 0;             // driver can not do dashed lines (yet)
    pls->plbuf_write = 1;             // driver uses the buffer for redraws

    if ( !pls->colorset )
        pls->color = 1;


#ifdef PL_HAVE_FREETYPE

//
//  Read registry to see if the user has set up default values
//  for text and smoothing. These will be overriden by anything that
//  might be given on the command line, so we will load the
//  values right into the same memory slots we pass to plParseDrvOpts
//

    GetRegIntValue( key_name, Keyword_text, &freetype );
    GetRegIntValue( key_name, Keyword_smooth, &smooth_text );

#endif

// Check for and set up driver options

    plParseDrvOpts( wingcc_options );

#ifdef PL_HAVE_FREETYPE

//
//  We will now save the settings to the registary if the user wants
//

    if ( save_reg == 1 )
    {
        SetRegIntValue( key_name, Keyword_text, &freetype );
        SetRegIntValue( key_name, Keyword_smooth, &smooth_text );
    }

#endif

// Set up device parameters

    if ( pls->xlength <= 0 || pls->ylength <= 0 )
    {
        // use default width, height of 800x600 if not specifed by -geometry option
        // or plspage
        plspage( 0., 0., 800, 600, 0, 0 );
    }

    dev->width  = pls->xlength - 1;     // should I use -1 or not???
    dev->height = pls->ylength - 1;

//
// Begin initialising the window
//

    // Initialize the entire structure to zero.
    memset( &dev->wndclass, 0, sizeof ( WNDCLASSEX ) );

    // This class is called WinTestWin
    dev->wndclass.lpszClassName = szWndClass;

    // cbSize gives the size of the structure for extensibility.
    dev->wndclass.cbSize = sizeof ( WNDCLASSEX );

    // All windows of this class redraw when resized.
    dev->wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC | CS_PARENTDC;

    // All windows of this class use the WndProc window function.
    dev->wndclass.lpfnWndProc = PlplotWndProc;

    // This class is used with the current program instance.

    dev->wndclass.hInstance = GetModuleHandle( NULL );

    // Use standard application icon and arrow cursor provided by the OS
    dev->wndclass.hIcon   = LoadIcon( NULL, IDI_APPLICATION );
    dev->wndclass.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
    dev->wndclass.hCursor = LoadCursor( NULL, IDC_ARROW );
    // Color the background white
    dev->wndclass.hbrBackground = NULL;

    dev->wndclass.cbWndExtra = sizeof ( pls );


    //
    // Now register the window class for use.
    //

    RegisterClassEx( &dev->wndclass );

    //
    //convert the program name to wide char if needed
    //

#ifdef UNICODE
    printf( pls->program );
    programlength = strlen( pls->program ) + 1;
    program       = malloc( programlength * sizeof ( TCHAR ) );
    MultiByteToWideChar( CP_UTF8, 0, pls->program, programlength, program, programlength );
#else
    program = pls->program;
#endif
    //
    // Create our main window using that window class.
    //
    dev->hwnd = CreateWindowEx( WS_EX_WINDOWEDGE + WS_EX_LEFT,
        szWndClass,                                         // Class name
        program,                                            // Caption
        WS_OVERLAPPEDWINDOW,                                // Style
        pls->xoffset,                                       // Initial x (use default)
        pls->yoffset,                                       // Initial y (use default)
        pls->xlength,                                       // Initial x size (use default)
        pls->ylength,                                       // Initial y size (use default)
        NULL,                                               // No parent window
        NULL,                                               // No menu
        dev->wndclass.hInstance,                            // This program instance
        NULL                                                // Creation parameters
        );

#ifdef UNICODE
    free( program );
#endif

//
// Attach a pointer to the stream to the window's user area
// this pointer will be used by the windows call back for
// process this window
//

#ifdef _WIN64
    SetWindowLongPtr( dev->hwnd, GWL_USERDATA, (LONG_PTR) pls );
#else
    SetWindowLong( dev->hwnd, GWL_USERDATA, (LONG) pls );
#endif

    dev->SCRN_hdc = dev->hdc = GetDC( dev->hwnd );

//
//  Setup the popup menu
//

    dev->PopupMenu = CreatePopupMenu();
    AppendMenu( dev->PopupMenu, MF_STRING, PopupPrint, _T( "Print" ) );
    AppendMenu( dev->PopupMenu, MF_STRING, PopupNextPage, _T( "Next Page" ) );
    AppendMenu( dev->PopupMenu, MF_STRING, PopupQuit, _T( "Quit" ) );

#ifdef PL_HAVE_FREETYPE

    if ( freetype )
    {
        pls->dev_text    = 1; // want to draw text
        pls->dev_unicode = 1; // want unicode
        init_freetype_lv1( pls );
        FT = (FT_Data *) pls->FT;
        FT->want_smooth_text = smooth_text;
    }

#endif



    plD_state_wingcc( pls, PLSTATE_COLOR0 );
    //
    // Display the window which we just created (using the nShow
    // passed by the OS, which allows for start minimized and that
    // sort of thing).
    //
    ShowWindow( dev->hwnd, SW_SHOWDEFAULT );
    SetForegroundWindow( dev->hwnd );

    //
    // Set up the DPI etc...
    //


    if ( pls->xdpi <= 0 ) // Get DPI from windows
    {
        plspage( GetDeviceCaps( dev->hdc, HORZRES ) / GetDeviceCaps( dev->hdc, HORZSIZE ) * 25.4,
            GetDeviceCaps( dev->hdc, VERTRES ) / GetDeviceCaps( dev->hdc, VERTSIZE ) * 25.4, 0, 0, 0, 0 );
    }
    else
    {
        pls->ydpi = pls->xdpi;        // Set X and Y dpi's to the same value
    }


//
//  Now we have to find out, from windows, just how big our drawing area is
//  when we specified the page size earlier on, that includes the borders,
//  title bar etc... so now that windows has done all its initialisations,
//  we will ask how big the drawing area is, and tell plplot
//

    GetClientRect( dev->hwnd, &dev->rect );
    dev->width  = dev->rect.right;
    dev->height = dev->rect.bottom;

    if ( dev->width > dev->height )           // Work out the scaling factor for the
    {                                         // "virtual" (oversized) page
        dev->scale = (PLFLT) ( PIXELS_X - 1 ) / dev->width;
    }
    else
    {
        dev->scale = (PLFLT) PIXELS_Y / dev->height;
    }

    Debug2( "Scale = %f (FLT)\n", dev->scale );

    plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 );
    plP_setphy( 0, (PLINT) ( dev->scale * dev->width ), 0, (PLINT) ( dev->scale * dev->height ) );

    // Set fill rule.
    if ( pls->dev_eofill )
        SetPolyFillMode( dev->hdc, ALTERNATE );
    else
        SetPolyFillMode( dev->hdc, WINDING );

#ifdef PL_HAVE_FREETYPE
    if ( pls->dev_text )
    {
        init_freetype_lv2( pls );
    }
#endif
}

//--------------------------------------------------------------------------
// plD_line_wingcc()
//
// Draw a line in the current color from (x1,y1) to (x2,y2).
//--------------------------------------------------------------------------

void
plD_line_wingcc( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
    POINT      points[2];


    points[0].x = (LONG) ( x1a / dev->scale );
    points[1].x = (LONG) ( x2a / dev->scale );
    points[0].y = (LONG) ( dev->height - ( y1a / dev->scale ) );
    points[1].y = (LONG) ( dev->height - ( y2a / dev->scale ) );

    dev->oldobject = SelectObject( dev->hdc, dev->pen );

    if ( points[0].x != points[1].x || points[0].y != points[1].y )
    {
        Polyline( dev->hdc, points, 2 );
    }
    else
    {
        SetPixel( dev->hdc, points[0].x, points[0].y, dev->colour );
    }
    SelectObject( dev->hdc, dev->oldobject );
}


//--------------------------------------------------------------------------
// plD_polyline_wingcc()
//
// Draw a polyline in the current color.
//--------------------------------------------------------------------------

void
plD_polyline_wingcc( PLStream *pls, short *xa, short *ya, PLINT npts )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
    int        i;
    POINT      *points = NULL;

    if ( npts > 0 )
    {
        points = GlobalAlloc( GMEM_ZEROINIT | GMEM_FIXED, (size_t) npts * sizeof ( POINT ) );
        if ( points != NULL )
        {
            for ( i = 0; i < npts; i++ )
            {
                points[i].x = (LONG) ( xa[i] / dev->scale );
                points[i].y = (LONG) ( dev->height - ( ya[i] / dev->scale ) );
            }
            dev->oldobject = SelectObject( dev->hdc, dev->pen );
            Polyline( dev->hdc, points, npts );
            SelectObject( dev->hdc, dev->oldobject );
            GlobalFree( points );
        }
        else
        {
            plexit( "Could not allocate memory to \"plD_polyline_wingcc\"\n" );
        }
    }
}

//--------------------------------------------------------------------------
// plD_fill_polygon_wingcc()
//
// Fill polygon described in points pls->dev_x[] and pls->dev_y[].
//--------------------------------------------------------------------------

static void
plD_fill_polygon_wingcc( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
    int        i;
    POINT      *points = NULL;
    HPEN       hpen, hpenOld;

    if ( pls->dev_npts > 0 )
    {
        points = GlobalAlloc( GMEM_ZEROINIT, (size_t) pls->dev_npts * sizeof ( POINT ) );

        if ( points == NULL )
            plexit( "Could not allocate memory to \"plD_fill_polygon_wingcc\"\n" );

        for ( i = 0; i < pls->dev_npts; i++ )
        {
            points[i].x = (PLINT) ( pls->dev_x[i] / dev->scale );
            points[i].y = (PLINT) ( dev->height - ( pls->dev_y[i] / dev->scale ) );
        }

        dev->fillbrush = CreateSolidBrush( dev->colour );
        hpen           = CreatePen( PS_SOLID, 1, dev->colour );
        dev->oldobject = SelectObject( dev->hdc, dev->fillbrush );
        hpenOld        = SelectObject( dev->hdc, hpen );
        Polygon( dev->hdc, points, pls->dev_npts );
        SelectObject( dev->hdc, dev->oldobject );
        DeleteObject( dev->fillbrush );
        SelectObject( dev->hdc, hpenOld );
        DeleteObject( hpen );
        GlobalFree( points );
    }
}

//--------------------------------------------------------------------------
//    static void CopySCRtoBMP(PLStream *pls)
//       Function copies the screen contents into a bitmap which is
//       later used for fast redraws of the screen (when it gets corrupted)
//       rather than remaking the plot from the plot buffer.
//--------------------------------------------------------------------------

static void CopySCRtoBMP( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    //
    //   Clean up the old bitmap and DC
    //

    if ( dev->hdc2 != NULL )
        DeleteDC( dev->hdc2 );
    if ( dev->bitmap != NULL )
        DeleteObject( dev->bitmap );

    dev->hdc2 = CreateCompatibleDC( dev->hdc );
    GetClientRect( dev->hwnd, &dev->rect );
    dev->bitmap    = CreateCompatibleBitmap( dev->hdc, dev->rect.right, dev->rect.bottom );
    dev->oldobject = SelectObject( dev->hdc2, dev->bitmap );
    BitBlt( dev->hdc2, 0, 0, dev->rect.right, dev->rect.bottom, dev->hdc, 0, 0, SRCCOPY );
    SelectObject( dev->hdc2, dev->oldobject );
}



void
plD_eop_wingcc( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    Debug( "End of the page\n" );
    CopySCRtoBMP( pls );
    dev->already_erased = 2;

    NormalCursor();

    if ( !pls->nopause )
    {
        dev->waiting = 1;
        while ( GetMessage( &dev->msg, NULL, 0, 0 ) )
        {
            TranslateMessage( &dev->msg );
            switch ( (int) dev->msg.message )
            {
            case WM_CONTEXTMENU:
            case WM_RBUTTONDOWN:
                TrackPopupMenu( dev->PopupMenu, TPM_CENTERALIGN | TPM_RIGHTBUTTON, LOWORD( dev->msg.lParam ),
                    HIWORD( dev->msg.lParam ), 0, dev->hwnd, NULL );
                break;

            case WM_CHAR:
                if ( ( (TCHAR) ( dev->msg.wParam ) == 32 ) ||
                     ( (TCHAR) ( dev->msg.wParam ) == 13 ) )
                {
                    dev->waiting = 0;
                }
                else if ( ( (TCHAR) ( dev->msg.wParam ) == 27 ) ||
                          ( (TCHAR) ( dev->msg.wParam ) == 'q' ) ||
                          ( (TCHAR) ( dev->msg.wParam ) == 'Q' ) )
                {
                    dev->waiting = 0;
                    PostQuitMessage( 0 );
                }
                break;

            case WM_LBUTTONDBLCLK:
                Debug( "WM_LBUTTONDBLCLK\t" );
                dev->waiting = 0;
                break;

            case WM_COMMAND:
                switch ( LOWORD( dev->msg.wParam ) )
                {
                case PopupPrint:
                    Debug( "PopupPrint" );
                    PrintPage( pls );
                    break;
                case PopupNextPage:
                    Debug( "PopupNextPage" );
                    dev->waiting = 0;
                    break;
                case PopupQuit:
                    Debug( "PopupQuit" );
                    dev->waiting = 0;
                    PostQuitMessage( 0 );
                    break;
                }
                break;

            default:
                DispatchMessage( &dev->msg );
                break;
            }
            if ( dev->waiting == 0 )
                break;
        }
    }
}

//--------------------------------------------------------------------------
//  Beginning of the new page
//--------------------------------------------------------------------------
void
plD_bop_wingcc( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
#ifdef PL_HAVE_FREETYPE
    FT_Data    *FT = (FT_Data *) pls->FT;
#endif
    Debug( "Start of Page\t" );

//
//  Turn the cursor to a busy sign, clear the page by "invalidating" it
//  then reset the colours and pen width
//

    BusyCursor();
    dev->already_erased = 0;
    RedrawWindow( dev->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );

    plD_state_wingcc( pls, PLSTATE_COLOR0 );
}

void
plD_tidy_wingcc( PLStream *pls )
{
    wingcc_Dev *dev = NULL;

#ifdef PL_HAVE_FREETYPE
    if ( pls->dev_text )
    {
        FT_Data *FT = (FT_Data *) pls->FT;
        plscmap0n( FT->ncol0_org );
        plD_FreeType_Destroy( pls );
    }
#endif
    Debug( "plD_tidy_wingcc" );

    if ( pls->dev != NULL )
    {
        dev = (wingcc_Dev *) pls->dev;

        DeleteMenu( dev->PopupMenu, PopupPrint, 0 );
        DeleteMenu( dev->PopupMenu, PopupNextPage, 0 );
        DeleteMenu( dev->PopupMenu, PopupQuit, 0 );
        DestroyMenu( dev->PopupMenu );

        if ( dev->hdc2 != NULL )
            DeleteDC( dev->hdc2 );
        if ( dev->hdc != NULL )
            ReleaseDC( dev->hwnd, dev->hdc );
        if ( dev->bitmap != NULL )
            DeleteObject( dev->bitmap );

        free_mem( pls->dev );
    }
}




//--------------------------------------------------------------------------
// plD_state_png()
//
// Handle change in PLStream state (color, pen width, fill attribute, etc).
//--------------------------------------------------------------------------

void
plD_state_wingcc( PLStream *pls, PLINT op )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    switch ( op )
    {
    case PLSTATE_COLOR0:
    case PLSTATE_COLOR1:
        dev->colour = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
        break;

    case PLSTATE_CMAP0:
    case PLSTATE_CMAP1:
        dev->colour = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
        break;
    }

    if ( dev->pen != NULL )
        DeleteObject( dev->pen );
    dev->pen = CreatePen( PS_SOLID, pls->width, dev->colour );
}

void
plD_esc_wingcc( PLStream *pls, PLINT op, void *ptr )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    switch ( op )
    {
    case PLESC_GETC:
        break;

    case PLESC_FILL:
        plD_fill_polygon_wingcc( pls );
        break;

    case PLESC_DOUBLEBUFFERING:
        break;

    case PLESC_XORMOD:
        if ( *(PLINT *) ( ptr ) == 0 )
            SetROP2( dev->hdc, R2_COPYPEN );
        else
            SetROP2( dev->hdc, R2_XORPEN );
        break;

#ifdef PL_HAVE_FREETYPE
    case PLESC_HAS_TEXT:
        plD_render_freetype_text( pls, (EscText *) ptr );
        break;

//     case PLESC_LIKES_UNICODE:
//      plD_render_freetype_sym(pls, (EscText *)ptr);
//      break;

#endif
    }
}

//--------------------------------------------------------------------------
// static void Resize( PLStream *pls )
//
// This function calculates how to resize a window after a message has been
// received from windows telling us the window has been changed.
// It tries to recalculate the scale of the window so everything works out
// just right.
// The window is only resized if plplot has finished all of its plotting.
// That means that if you resize while a picture is being plotted,
// unpredictable results may result. The reason I do this is because the
// resize function calls redraw window, which replays the whole plot.
//--------------------------------------------------------------------------

static void Resize( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
#ifdef PL_HAVE_FREETYPE
    FT_Data    *FT = (FT_Data *) pls->FT;
#endif
    Debug( "Resizing" );

    if ( dev->waiting == 1 )     // Only resize the window IF plplot has finished with it
    {
        memcpy( &dev->oldrect, &dev->rect, sizeof ( RECT ) );
        GetClientRect( dev->hwnd, &dev->rect );
        Debug3( "[%d %d]", dev->rect.right, dev->rect.bottom );

        if ( ( dev->rect.right > 0 ) && ( dev->rect.bottom > 0 ) )            // Check to make sure it isn't just minimised (i.e. zero size)
        {
            if ( memcmp( &dev->rect, &dev->oldrect, sizeof ( RECT ) ) != 0 )  // See if the window's changed size or not
            {
                dev->already_erased = 0;
                dev->width          = dev->rect.right;
                dev->height         = dev->rect.bottom;
                if ( dev->width > dev->height )     // Work out the scaling factor for the
                {                                   // "virtual" (oversized) page
                    dev->scale = (PLFLT) ( PIXELS_X - 1 ) / dev->width;
                }
                else
                {
                    dev->scale = (PLFLT) PIXELS_Y / dev->height;
                }

#ifdef PL_HAVE_FREETYPE
                if ( FT )
                {
                    FT->scale = dev->scale;
                    FT->ymax  = dev->height;
                }
#endif
            }
            RedrawWindow( dev->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
        }
        else
        {
            memcpy( &dev->rect, &dev->oldrect, sizeof ( RECT ) ); // restore the old size to current size since the window is minimised
        }
    }
}


//--------------------------------------------------------------------------
// int SetRegValue(char *key_name, char *key_word, char *buffer,int dwType, int size)
//
//  Function set the registry; if registary entry does not exist, it is
//  created. Actually, the key is created before it is set just to make sure
//  that is is there !
//--------------------------------------------------------------------------

static int SetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int dwType, int size )
{
    int   j = 0;

    DWORD lpdwDisposition;
    HKEY  hKey;

    j = RegCreateKeyEx(
        HKEY_CURRENT_USER,
        key_name,
        0,                                         // reserved
        NULL,                                      // address of class string
        REG_OPTION_NON_VOLATILE,                   // special options flag
        KEY_WRITE,                                 // desired security access
        NULL,                                      // address of key security structure
        &hKey,                                     // address of buffer for opened handle
        &lpdwDisposition                           // address of disposition value buffer
        );

    if ( j == ERROR_SUCCESS )
    {
        RegSetValueEx( hKey, key_word, 0, dwType, buffer, size );
        RegCloseKey( hKey );
    }
    return ( j );
}

//--------------------------------------------------------------------------
// int GetRegValue(char *key_name, char *key_word, char *buffer, int size)
//
//  Function reads the registry and gets a string value from it
//  buffer must be allocated by the caller, and the size is given in the size
//  paramater.
//  Return code is 1 for success, and 0 for failure.
//--------------------------------------------------------------------------

static int GetRegValue( TCHAR *key_name, TCHAR *key_word, char *buffer, int size )
{
    int  ret = 0;
    HKEY hKey;
    int  dwType;
    int  dwSize = size;

    if ( RegOpenKeyEx( HKEY_CURRENT_USER, key_name, 0, KEY_READ, &hKey ) == ERROR_SUCCESS )
    {
        if ( RegQueryValueEx( hKey, key_word, 0, (LPDWORD) &dwType, buffer, (LPDWORD) &dwSize ) == ERROR_SUCCESS )
        {
            ret = 1;
        }
        RegCloseKey( hKey );
    }
    return ( ret );
}

#ifdef PL_HAVE_FREETYPE

//--------------------------------------------------------------------------
//  void plD_pixel_wingcc (PLStream *pls, short x, short y)
//
//  callback function, of type "plD_pixel_fp", which specifies how a single
//  pixel is set in the current colour.
//--------------------------------------------------------------------------

static void plD_pixel_wingcc( PLStream *pls, short x, short y )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    SetPixel( dev->hdc, x, y, dev->colour );
}

static void plD_pixelV_wingcc( PLStream *pls, short x, short y )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    SetPixelV( dev->hdc, x, y, dev->colour );
}

//--------------------------------------------------------------------------
//  void plD_set_pixelV_wingcc (PLStream *pls, short x, short y,PLINT colour)
//
//  callback function, of type "plD_set_pixel_fp", which specifies how a
//  single pixel is set in the s[ecified colour. This colour
//  by-passes plplot's internal table, and directly 'hits the hardware'.
//--------------------------------------------------------------------------

static void plD_set_pixel_wingcc( PLStream *pls, short x, short y, PLINT colour )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    SetPixel( dev->hdc, x, y, colour );
}

static void plD_set_pixelV_wingcc( PLStream *pls, short x, short y, PLINT colour )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    SetPixelV( dev->hdc, x, y, colour );
}


//--------------------------------------------------------------------------
//  void plD_read_pixel_wingcc (PLStream *pls, short x, short y)
//
//  callback function, of type "plD_pixel_fp", which specifies how a single
//  pixel is read.
//--------------------------------------------------------------------------
static PLINT plD_read_pixel_wingcc( PLStream *pls, short x, short y )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    return ( GetPixel( dev->hdc, x, y ) );
}


//--------------------------------------------------------------------------
//  void init_freetype_lv1 (PLStream *pls)
//
//  "level 1" initialisation of the freetype library.
//  "Level 1" initialisation calls plD_FreeType_init(pls) which allocates
//  memory to the pls->FT structure, then sets up the pixel callback
//  function.
//--------------------------------------------------------------------------

static void init_freetype_lv1( PLStream *pls )
{
    FT_Data    *FT;
    int        x;
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;

    plD_FreeType_init( pls );

    FT = (FT_Data *) pls->FT;



//
//  Work out if our device support "fast" pixel setting
//  and if so, use that instead of "slow" pixel setting
//

    x = GetDeviceCaps( dev->hdc, RASTERCAPS );

    if ( x & RC_BITBLT )
        FT->pixel = (plD_pixel_fp) plD_pixelV_wingcc;
    else
        FT->pixel = (plD_pixel_fp) plD_pixel_wingcc;

//
//  See if we have a 24 bit device (or better), in which case
//  we can use the better antialaaising.
//

    if ( GetDeviceCaps( dev->hdc, BITSPIXEL ) > 24 )
    {
        FT->BLENDED_ANTIALIASING = 1;
        FT->read_pixel           = (plD_read_pixel_fp) plD_read_pixel_wingcc;

        if ( x & RC_BITBLT )
            FT->set_pixel = (plD_set_pixel_fp) plD_set_pixelV_wingcc;
        else
            FT->set_pixel = (plD_set_pixel_fp) plD_set_pixel_wingcc;
    }
}

//--------------------------------------------------------------------------
//  void init_freetype_lv2 (PLStream *pls)
//
//  "Level 2" initialisation of the freetype library.
//  "Level 2" fills in a few setting that aren't public until after the
//  graphics sub-system has been initialised.
//  The "level 2" initialisation fills in a few things that are defined
//  later in the initialisation process for the GD driver.
//
//  FT->scale is a scaling factor to convert co-ordinates. This is used by
//  the GD and other drivers to scale back a larger virtual page and this
//  eliminate the "hidden line removal bug". Set it to 1 if your device
//  doesn't have scaling.
//
//  Some coordinate systems have zero on the bottom, others have zero on
//  the top. Freetype does it one way, and most everything else does it the
//  other. To make sure everything is working ok, we have to "flip" the
//  coordinates, and to do this we need to know how big in the Y dimension
//  the page is, and whether we have to invert the page or leave it alone.
//
//  FT->ymax specifies the size of the page FT->invert_y=1 tells us to
//  invert the y-coordinates, FT->invert_y=0 will not invert the
//  coordinates.
//--------------------------------------------------------------------------

static void init_freetype_lv2( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
    FT_Data    *FT  = (FT_Data *) pls->FT;

    FT->scale    = dev->scale;
    FT->ymax     = dev->height;
    FT->invert_y = 1;

    if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) )           // do we want to at least *try* for smoothing ?
    {
        FT->ncol0_org   = pls->ncol0;                                                   // save a copy of the original size of ncol0
        FT->ncol0_xtra  = 16777216 - ( pls->ncol1 + pls->ncol0 );                       // work out how many free slots we have
        FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing;             // find out how many different shades of anti-aliasing we can do
        FT->ncol0_width = max_number_of_grey_levels_used_in_text_smoothing;             // set a maximum number of shades
        plscmap0n( FT->ncol0_org + ( FT->ncol0_width * pls->ncol0 ) );                  // redefine the size of cmap0
// the level manipulations are to turn off the plP_state(PLSTATE_CMAP0)
// call in plscmap0 which (a) leads to segfaults since the GD image is
// not defined at this point and (b) would be inefficient in any case since
// setcmap is always called later (see plD_bop_png) to update the driver
// color palette to be consistent with cmap0.
        {
            PLINT level_save;
            level_save = pls->level;
            pls->level = 0;
            pl_set_extended_cmap0( pls, FT->ncol0_width, FT->ncol0_org ); // call the function to add the extra cmap0 entries and calculate stuff
            pls->level = level_save;
        }
        FT->smooth_text = 1;                                                       // Yippee ! We had success setting up the extended cmap0
    }
    else if ( ( FT->want_smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 1 ) ) // If we have a truecolour device, we wont even bother trying to change the palette
    {
        FT->smooth_text = 1;
    }
}
#endif

//--------------------------------------------------------------------------
//  static void UpdatePageMetrics ( PLStream *pls, char flag )
//
//      UpdatePageMetrics is a simple function which simply gets new vales for
//      a changed DC, be it swapping from printer to screen or vice-versa.
//      The flag variable is used to tell the function if it is updating
//      from the printer (1) or screen (0).
//--------------------------------------------------------------------------

static void UpdatePageMetrics( PLStream *pls, char flag )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
  #ifdef PL_HAVE_FREETYPE
    FT_Data    *FT = (FT_Data *) pls->FT;
  #endif

    if ( flag == 1 )
    {
        dev->width  = GetDeviceCaps( dev->hdc, HORZRES ); // Get the page size from the printer
        dev->height = GetDeviceCaps( dev->hdc, VERTRES );
    }
    else
    {
        GetClientRect( dev->hwnd, &dev->rect );
        dev->width  = dev->rect.right;
        dev->height = dev->rect.bottom;
    }

    if ( dev->width > dev->height )     // Work out the scaling factor for the
    {                                   // "virtual" (oversized) page
        dev->scale = (PLFLT) ( PIXELS_X - 1 ) / dev->width;
    }
    else
    {
        dev->scale = (PLFLT) PIXELS_Y / dev->height;
    }

  #ifdef PL_HAVE_FREETYPE
    if ( FT )           // If we are using freetype, then set it up next
    {
        FT->scale = dev->scale;
        FT->ymax  = dev->height;
        if ( GetDeviceCaps( dev->hdc, RASTERCAPS ) & RC_BITBLT )
            FT->pixel = (plD_pixel_fp) plD_pixelV_wingcc;
        else
            FT->pixel = (plD_pixel_fp) plD_pixel_wingcc;
    }
  #endif

    pls->xdpi = GetDeviceCaps( dev->hdc, HORZRES ) / GetDeviceCaps( dev->hdc, HORZSIZE ) * 25.4;
    pls->ydpi = GetDeviceCaps( dev->hdc, VERTRES ) / GetDeviceCaps( dev->hdc, VERTSIZE ) * 25.4;
    plP_setpxl( dev->scale * pls->xdpi / 25.4, dev->scale * pls->ydpi / 25.4 );
    plP_setphy( 0, (PLINT) ( dev->scale * dev->width ), 0, (PLINT) ( dev->scale * dev->height ) );
}

//--------------------------------------------------------------------------
//  static void PrintPage ( PLStream *pls )
//
//     Function brings up a standard printer dialog and, after the user
//     has selected a printer, replots the current page to the windows
//     printer.
//--------------------------------------------------------------------------

static void PrintPage( PLStream *pls )
{
    wingcc_Dev *dev = (wingcc_Dev *) pls->dev;
  #ifdef PL_HAVE_FREETYPE
    FT_Data    *FT = (FT_Data *) pls->FT;
  #endif
    PRINTDLG   Printer;
    DOCINFO    docinfo;

    //
    //    Reset the docinfo structure to 0 and set it's fields up
    //    This structure is used to supply a name to the print queue
    //

    ZeroMemory( &docinfo, sizeof ( docinfo ) );
    docinfo.cbSize      = sizeof ( docinfo );
    docinfo.lpszDocName = _T( "Plplot Page" );

    //
    //   Reset out printer structure to zero and initialise it
    //

    ZeroMemory( &Printer, sizeof ( PRINTDLG ) );
    Printer.lStructSize = sizeof ( PRINTDLG );
    Printer.hwndOwner   = dev->hwnd;
    Printer.Flags       = PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
    Printer.nCopies     = 1;

    //
    //   Call the printer dialog function.
    //   If the user has clicked on "Print", then we will continue
    //   processing and print out the page.
    //

    if ( PrintDlg( &Printer ) != 0 )
    {
        //
        //  Before doing anything, we will take some backup copies
        //  of the existing values for page size and the like, because
        //  all we are going to do is a quick and dirty modification
        //  of plplot's internals to match the new page size and hope
        //  it all works out ok. After that, we will manip the values,
        //  and when all is done, restore them.
        //

        if ( ( dev->push = GlobalAlloc( GMEM_ZEROINIT, sizeof ( wingcc_Dev ) ) ) != NULL )
        {
            BusyCursor();
            memcpy( dev->push, dev, sizeof ( wingcc_Dev ) );

            dev->hdc = dev->PRNT_hdc = Printer.hDC; // Copy the printer HDC

            UpdatePageMetrics( pls, 1 );

          #ifdef PL_HAVE_FREETYPE
            if ( FT )                                  // If we are using freetype, then set it up next
            {
                dev->FT_smooth_text = FT->smooth_text; // When printing, we don't want smoothing
                FT->smooth_text     = 0;
            }
          #endif

            //
            //   Now the stuff that actually does the printing !!
            //

            StartDoc( dev->hdc, &docinfo );
            plRemakePlot( pls );
            EndDoc( dev->hdc );

            //
            //  Now to undo everything back to what it was for the screen
            //

            dev->hdc = dev->SCRN_hdc;  // Reset the screen HDC to the default
            UpdatePageMetrics( pls, 0 );

          #ifdef PL_HAVE_FREETYPE
            if ( FT )           // If we are using freetype, then set it up next
            {
                FT->smooth_text = dev->FT_smooth_text;
            }
          #endif
            memcpy( dev, dev->push, sizeof ( wingcc_Dev ) ); // POP our "stack" now to restore the values

            GlobalFree( dev->push );
            NormalCursor();
            RedrawWindow( dev->hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
        }
    }
}



#else
int
pldummy_wingcc()
{
    return ( 0 );
}

#endif                          // PLD_wingccdev
