//===============================================================
// vWinDC - Windows Base DC
//
// Copyright (C) 1995,1996,1997,1998  Bruce E. Wampler
//
// This file is part of the V C++ GUI Framework, and is covered
// under the terms of the GNU Library General Public License,
// Version 2. This library has NO WARRANTY. See the source file
// vapp.cxx for more complete information about license terms.
//===============================================================

#include <v/vos2.h>            // for OS/2 stuff
extern "C"
{
  #include <math.h>
}
#include <v/vwindc.h>
#include <v/vmemdc.h>
#include <v/vapp.h>            // need access to the app
#include <v/vcanvas.h>         // our own canvas widget
#include <v/vicon.h>

//-----------------------------------------------------------------------
//================>>> vWinDC::vWinDC <<<========================
  vWinDC::vWinDC()
  {
    SysDebug(Constructor,"vWinDC::vWinDC() constructor\n")

    _font = theApp->GetDefaultFont();  // Use the default fixed font by default!
    _hdc = 0;

    // set some defaults
    _canvasBG = SETRGB(255,255,255);    // white
    _hpen = _pen.GetHPEN();            // Get the default pen
    _hbrush = _brush.GetHBRUSH();       // Get the default brush
//    _hpen = 0;                          // no pen for now
//    _hbrush = 0;                        // no brush for now
    _isPrinterDC = 0;                  // Assume not printer
  }

//================>>> vWinDC::~vWinDC <<<========================
  vWinDC::~vWinDC()
  {
    SysDebug(Destructor,"vWinDC::~vWinDC() destructor\n")
  }
//================>>> vWinDC::CopyFromMemoryDC <<<=======================
  void vWinDC::CopyFromMemoryDC(vMemoryDC* vmemdc, int destX, int destY,
       int srcX, int srcY, int srcW, int srcH) // V:1.13
  {
    BeginPaint();
    int ch = vmemdc->_physHeight;
    int cw = vmemdc->_physWidth;
    int cx = 0;
    int cy = 0;

    if (srcX > 0)               // Fixed: 10/11/96
      cx = srcX;
    if (srcY > 0)
      cy = srcY;
    if (srcW > 0)
      cw = srcW;
    if (srcH > 0)
      ch = srcH;

    if ((cw + cx) > vmemdc->_physWidth && _physWidth > 0)       // only copy what will fi
      cw = vmemdc->_physWidth - cx;
    if ((ch + cy) > vmemdc->_physHeight && _physHeight > 0)
      ch = vmemdc->_physHeight - cy;

    if ((cw + destX) > _physWidth && _physWidth > 0)
      cw = _physWidth - destX;
    if ((ch + destY) > _physHeight && _physHeight > 0)
      ch = _physHeight - destY;


    if (cw > _physWidth && _physWidth > 0)     // only copy what will fit
      cw = _physWidth;
    if (ch > _physHeight && _physHeight > 0)
      ch = _physHeight;

    POINTL cPoints[3] = {destX, destY,              // lower left targ
                         destX+cw, destY+ch,        // upper right targ
                         cx, cy };                  // lower left src

    HPS memDC = vmemdc->_hdc;
    SysDebug1(Constructor,"vWinDC::CopyFromMemoryDC() memDC = %d\n", memDC)

#define BITBLT
#ifdef BITBLT
    // we need to convert the target coords which are in World Space
    // to Device Space since GpiBitBlt expects device coords
//    GpiConvert(_hdc, CVTC_WORLD, CVTC_DEVICE, 1, &cPoints[0]);
//    GpiConvert(_hdc, CVTC_WORLD, CVTC_DEVICE, 1, &cPoints[1]);
    MapToOS2(&cPoints[0]);
    MapToOS2(&cPoints[1]);
    GpiBitBlt(_hdc, memDC, 3, cPoints, ROP_SRCCOPY, BBO_IGNORE);
#else
    // register COLORREF color, cout;
    register ULONG color, cout;
    POINTL cPoint;
    LINEBUNDLE Pen;
    for (int row = 0 ; row < ch ; ++row)
      for (int col = 0 ; col < cw ; ++col)
      {
        cPoint.x = row;
        cPoint.y = col;
        MapToOS2(&cPoint);

        color = GpiQueryPel (memDC, cPoint);
        cout = GpiQueryAttrs (_hdc, PRIM_LINE, LBB_COLOR, &Pen);
        Pen.lColor = color;
        GpiSetAttrs (_hdc, PRIM_LINE, LBB_COLOR, 0, &Pen);
        GpiSetPel (_hdc, cPoint);
        Pen.lColor = cout;
        GpiSetAttrs (_hdc, PRIM_LINE, LBB_COLOR, 0, &Pen);
      }
#endif
    EndPaint();
   }

//====================>>> vWinDC::DrawIcon <<<==========================
  void vWinDC::DrawIcon(int x, int y, VCONST vIcon& icon)
  {
    BeginPaint();
    int xx = Scale(x+_tx);
    int yy = Scale(y+_ty);
    // Now, draw the icon
    HBITMAP hbm = icon.GetIconHBM(_hdc);    // get the bitmap
    SysDebug1(Build,"vWinDC::DrawIcon() hbm=%u\n", hbm)

    // Now BitBlt the icon BMP into the drawing canvas, leaving
    // background untouched, and the pen color to draw 1 bits
    // (Note: Target is in World Coords, and Source is in Device Coords)
    POINTL cPoints[4] = {xx, yy,                             // lower left targ
			 xx+icon.width-1, yy+icon.height-1,  // upper right targ
			 0, 0,                               // lower left src
			 icon.width, icon.height};           // upper right src

    // Map to OS/2 coord system before calling
    // (only need to map World Coords)
    MapToOS2(&cPoints[0]);
    MapToOS2(&cPoints[1]);

    // set ownership of bitmap to canvas
    HBITMAP hbmr = GpiSetBitmap(_hdc, hbm);
    // now reset the original bitmap if there was one
    GpiSetBitmap(_hdc, hbmr);

    if (icon.iType == Transparent)
    {
      // for transparent icons, we set the canvas background
      // color to the icon transparent color and then set
      // the background mix mode to BM_SRCTRANSPARENT, OS/2
      // takes care of the rest (compare with the windoze code! :-) )
      IMAGEBUNDLE himage;
      himage.lColor = _pen.GetColor().pixel();  // use pen color
      himage.lBackColor = icon.GetTransparentColor();
      GpiSetAttrs(_hdc, PRIM_IMAGE, IBB_BACK_COLOR | IBB_COLOR, 0, &himage);
      GpiSetBackMix(_hdc,BM_SRCTRANSPARENT);

      // blit the image to the canvas
      GpiWCBitBlt(_hdc, hbm, 4, cPoints, ROP_SRCCOPY, BBO_IGNORE);

      // restore defaults
      himage.lColor = _pen.GetColor().pixel();  // use pen color
      himage.lBackColor = _canvasBG;            // use background color
      GpiSetAttrs(_hdc, PRIM_IMAGE, IBB_BACK_COLOR | IBB_COLOR, 0, &himage);
      GpiSetBackMix(_hdc,BM_DEFAULT);
    }
    else
    {
      // for monochrome bitmaps, we can control the
      // target colors using the image bundle
      IMAGEBUNDLE himage;
      himage.lColor = _pen.GetColor().pixel();  // use pen color
      himage.lBackColor = _canvasBG;            // use background color
      GpiSetAttrs(_hdc, PRIM_IMAGE, IBB_COLOR | IBB_BACK_COLOR, 0, &himage);

      // blit the image to the canvas
      GpiWCBitBlt(_hdc, hbm, 4, cPoints, ROP_SRCCOPY, BBO_IGNORE);
    }
    EndPaint();
  }

//#define DRAWARC
#ifdef DRAWARC
//====================>>> vWinDC::DrawArc <<<==========================
// This function is experimental
//  (xx1, yy1) start of arc
//  (xx2, yy2) end of arc
//  (xxc, yyc) centre of arc

  void vWinDC::DrawArc(int xx1, int yy1, int xx2, int yy2,
    int xxc, int yyc)
  {
    double dx = xx1 - xxc;
    double dy = yy1 - yyc;
    double radius = sqrt(dx * dx + dy * dy);
    const double PIx2 = 6.283185;
    double angle1, angle2;    // angles of points wrt center

    BeginPaint();
    if (xx1 == xx2 && yy1 == yy2)
    {
      angle1 = 0.0;
      angle2 = 360.0;
    }
    else if (radius == 0.0)
      angle1 = angle2 = 0.0;
    else
    {
      if (xx1 - xxc == 0)
      {
        if (yy1 - yyc < 0)
          angle1 = 90.0;
        else
          angle1 = -90.0;
      }
      else
        angle1 = -atan2 ((double) (yy1 - yyc), (double) (xx1 - xxc)) * 360.0 / PIx2;

      if (xx2 - xxc == 0)
      {
        if (yy2 - yyc < 0)
          angle2 = 90.0;
        else
          angle2 = -90.0;
      }
      else
        angle2 = -atan2 ((double) (yy2 - yyc), (double) (xx2 - xxc)) * 360.0 / PIx2;
    }

    POINTL posc = {xxc, yyc};
    POINTL pos1 = {xx1, yy1};
    MapToOS2(&posc);
    MapToOS2(&pos1);

    ARCPARAMS arc;
    double ri, Scale;

    ri = floor(radius);   // radius always >= 0
    Scale = radius/ri;
    arc.lP = (LONG) ri;
    arc.lQ = (LONG) ri;
    arc.lR = 0L;
    arc.lS = 0L;
    GpiSetArcParams(_hdc, &arc);

    // compute start and sweep angles (must be >= 0)
    double start = angle1;
    double sweep = fabs(angle2 - angle1);
    while (start < 0)
      start += 360;
    // set pen attributes and draw arc
    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH, 0, _hpen);

    GpiMove(_hdc, &pos1);
    GpiPartialArc(_hdc, &posc, DBLTOFX(Scale), DBLTOFX(start), DBLTOFX(sweep));
    EndPaint();
  }
#endif

//====================>>> vWinDC::DrawEllipse <<<==========================
// (x,y) lower left corner ellipse bounding box
  void vWinDC::DrawEllipse(int x, int y, int width, int height)
  {
    if (height == 0 || width == 0)
      return;

    BeginPaint();
    // coords for GPI functions are inclusive/inclusive
    // right = left + width -1
    // top = bottom + height -1
    int x1 = Scale(x+_tx);
    int y1 = Scale(y+_ty);
    int width1 = Scale(width-1);
    int height1 = Scale(height-1);

    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    _hbrush = _brush.GetHBRUSH();
    GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, _hbrush);

    // coords for GPI functions are inclusive/inclusive
    // right = left + width -1
    // top = bottom + height -1
    // set arc params at 2x scale
    ARCPARAMS arc;
    arc.lP = width1;
    arc.lQ = height1;
    arc.lR = 0;
    arc.lS = 0;
    GpiSetArcParams(_hdc , &arc);

    // compute center of ellipse
    POINTL posc = {x1+(width1/2), y1+(height1/2)};
    MapToOS2(&posc);

    if (_hpen->usType == LINETYPE_SOLID)
    {
      GpiSetCurrentPosition (_hdc, &posc);
      GpiFullArc (_hdc, DRO_FILL, MAKEFIXED(0,0x8000));   // scale by 1/2
      GpiBeginPath(_hdc, 1L);
      GpiFullArc (_hdc, DRO_OUTLINE, MAKEFIXED(0,0x8000));   // scale by 1/2
      GpiEndPath(_hdc);

      AREABUNDLE hlinebrush;
      hlinebrush.lColor = _hpen->lColor;
      hlinebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &hlinebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked
    }
    else
    {
      GpiSetCurrentPosition (_hdc, &posc);    // move to start point
      GpiFullArc (_hdc, DRO_OUTLINEFILL, MAKEFIXED(0,0x8000));   // scale by 1/2
    }
    EndPaint();
  }

//====================>>> vWinDC::DrawLine <<<==========================
  void vWinDC::DrawLine(int x, int y, int xend, int yend)
  {
    BeginPaint();
    // Draw a line from x,y to xend,yend
    // This method has the typical sequence of code for drawing. All other
    // drawing methods are modeled using this approach
    // First, do the required scaling
    int xx = Scale(x+_tx);
    int yy = Scale(y+_ty);
    int xe = Scale(xend+_tx);
    int ye = Scale(yend+_ty);

    //  OS/2 can define a presentation space when the window
    // is created, and we can use it until the window is
    // destroyed.  This saves us from needing to constantly
    // get and destroy the DC (unlike the windows code) while the user program is
    // drawing stuff interactively (usually in response to mouse input).
    // For Redraw, the canvas redraw event will issue a WinBeginPaint
    // and WinEndPaint, which will bracket all drawing.  We feed
    // WinBeginPaint our presentation space for the window so it
    // doesn't create a separate cached micro-PS like it normally would do.
    // This allows us to retain any attributes of the PS for the
    // life of the window.

    _hpen = _pen.GetHPEN();         // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
       0, _hpen);

    // Shapes will do a similar sequence for brushes as well
    // Now the code to actually do the drawing
    POINTL poss = {xx, yy};  // start point
    POINTL pose = {xe, ye};  // end point
    MapToOS2(&poss);
    MapToOS2(&pose);

    GpiBeginPath(_hdc, 1L);
    GpiSetCurrentPosition (_hdc, &poss);
    GpiLine (_hdc , &pose);
    GpiEndPath(_hdc);

    // it turns out that to get a line of variable thickness it must be
    // stroked.  However, such lines do not support linetypes other
    // than type solid. So to get dots and dashed lines we need to
    // use outlines, but then the width is ignored (dohh!).
    // This behavior is also true for windows though,
    // so I'm not going to worry about it.  The major pain though is
    // that the stroked line color is set to the area fill color, which
    // is why I have to do the nonsense with the hlinebrush.

    if (_hpen->usType == LINETYPE_SOLID)
    {
      // for Geometric lines, the fill color is used (what a debacle!)
      AREABUNDLE hlinebrush;
      hlinebrush.lColor = _hpen->lColor;              // for stroked lines
      hlinebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &hlinebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // solid lines can be stroked
    }
    else
      GpiOutlinePath(_hdc, 1L, 0L);  // other lines need to be outlined

    EndPaint();
  }

//====================>>> vWinDC::DrawLines <<<==========================
  void vWinDC::DrawLines(vLine* lineList, int count)
  {
    if (count < 1 || lineList == 0)
      return;

    BeginPaint();
    _hpen = _pen.GetHPEN();         // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    vLine* lp = lineList ;
    POINTL poss;   // start point
    POINTL pose;   // end point

    GpiBeginPath(_hdc, 1L);
    for (int num = 0 ; num < count ; ++lp, ++num)
    {
      poss.x = Scale(lp->x+_tx);
      poss.y = Scale(lp->y+_ty);
      pose.x = Scale(lp->xend+_tx);
      pose.y = Scale(lp->yend+_ty);
      MapToOS2(&poss);
      MapToOS2(&pose);

      GpiSetCurrentPosition (_hdc, &poss);
      GpiLine (_hdc , &pose);
    }
    GpiEndPath(_hdc);
    if (_hpen->usType == LINETYPE_SOLID)
    {
      // for Geometric lines, the fill color is used (what a debacle!)
      AREABUNDLE linebrush;
      linebrush.lColor = _hpen->lColor;               // for stroked lines
      linebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // solid lines can be stroked
    }
    else
      GpiOutlinePath(_hdc, 1L, 0L);  // other lines need to be outlined

    EndPaint();
  }

//==================>>> vWinDC::DrawColorPoints <<<======================
  void vWinDC::DrawColorPoints(int x, int y, int nPoints, vColor* pointList)
  {
    // Draw a bunch of color points.
    if (nPoints < 1 || pointList == 0)
      return;

    BeginPaint();

    int xx = Scale(x+_tx);
    int yy = Scale(y+_ty);
    if (yy < 0)
      return;                         // row not displayed

#define BITBLT
#ifdef BITBLT
    // performance was bad with GpiSetPel so  do this with
    // a line bitmap blitted to the canvas

    // bitmap data structures
    PVBYTE bmbits;
    PBITMAPINFO2 pbmi;

    // build a memory PS compatible with calling PS
    HDC DevCxtComp = GpiQueryDevice(_hdc);
    HDC DevCxtMem = DevOpenDC(theApp->AppHab(), OD_MEMORY, "*", 0L, NULL, DevCxtComp);
    SIZEL size = {nPoints, 1};
    HPS hdcMem = GpiCreatePS (theApp->AppHab(), DevCxtMem, &size,
      PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC );

    // set the PS to RGB mode
    GpiCreateLogColorTable(hdcMem, 0L, LCOLF_RGB, 0L, 0L, NULL);

    // First, create a blank bitmap compatible with the
    // current context that is size of bitmap.
    BITMAPINFOHEADER2 bmih;
    memset(&bmih, 0, sizeof(BITMAPINFOHEADER2));
    bmih.cbFix = sizeof(BITMAPINFOHEADER2);
    bmih.cx = nPoints;
    bmih.cy = 1;
    bmih.ulColorEncoding = BCE_RGB;
    bmih.cPlanes = 1;         // always 1 for OS/2
    bmih.cBitCount = 24;      // bits of color per pixel

    HBITMAP hbm = GpiCreateBitmap(hdcMem, &bmih, 0L, NULL, NULL);
    // now we need to load the bitmap with data by associating
    // with a memory PS and setting each pel
    GpiSetBitmap(hdcMem, hbm);

    // We first need to create the bitmapinfo structure
    // (a color table is not needed for 24 bit bitmaps)
    // allocate space for header
    pbmi=(PBITMAPINFO2) new VBYTE [sizeof(BITMAPINFOHEADER2)];

    // copy the header to the bitmap info structure
    memcpy(pbmi, &bmih, sizeof(BITMAPINFOHEADER2));
    pbmi->cclrUsed = 0 ;         // no color table needed
    pbmi->cbImage = nPoints*3;   // image size (bytes)

    // Now need to copy pixel data over from pointList
    int rowwords = ((nPoints*3)+3) / 4;   // number of words (for OS/2 bitmap data)
    bmbits = new VBYTE[rowwords*4];         // allocate space
    PVBYTE to = bmbits;                    // set to first pixel

    int col;
    for (col = 0 ; col < nPoints ; ++col)   // do each V pixel per row
    {
      // R G B
      *to++ = pointList[col].r();
      *to++ = pointList[col].g();
      *to++ = pointList[col].b();
    }

    // Finished a row, need to pad to a word boundary
    while ( (col*3)<(4*rowwords) )
    {
      *to++ = 0x00;           // pad with 0's
      col++;
    }

    // Now, create the bitmap
    GpiSetBitmapBits(hdcMem, 0L, pbmi->cy, (PBYTE) bmbits, pbmi);
    // free up resources and we are done
    delete [] bmbits;
    delete [] pbmi;

    GpiSetBitmap (hdcMem, NULLHANDLE);
    GpiAssociate (hdcMem, NULLHANDLE);
    GpiDestroyPS (hdcMem);
    DevCloseDC (DevCxtMem);

    POINTL cPoints[4] = {xx, yy,                 // lower left targ
                         xx+nPoints-1, yy,       // upper right targ
                         0, 0,                   // lower left src
                         nPoints, 1};            // upper right src
    MapToOS2(&cPoints[0]);
    MapToOS2(&cPoints[1]);

    // set ownership of bitmap to canvas
    HBITMAP hbmr = GpiSetBitmap(_hdc, hbm);
    // now reset the original bitmap if there was one
    GpiSetBitmap(_hdc, hbmr);
    // blit the image to the canvas
    GpiWCBitBlt(_hdc, hbm, 4, cPoints, ROP_SRCCOPY, BBO_IGNORE);
    // destroy the bitmap
    if (hbm != 0)
      GpiDeleteBitmap(hbm);

#else
    // this is the slow but straightforward way using GpiSetPel
    LINEBUNDLE pen;
    POINTL Point, os2Point;
    Point.x = xx;
    Point.y = yy;

    if (xx < 0)                                // need to check every time
    {
      for (int ix = 0 ; ix < nPoints ; ++ix)
      {
        if (xx+ix < 0)
          continue;                       // this one not displayed
        pen.lColor = pointList[ix].pixel();
        GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR, 0, &pen);
        Point.x = xx+ix;
        os2Point.x = Point.x;
        os2Point.y = Point.y;
        MapToOS2(&os2Point);
        GpiSetPel (_hdc, &os2Point);
      }
    }
    else                               // don't need xx check
    {
      for (int ix = 0 ; ix < nPoints ; ++ix)
      {
        pen.lColor = pointList[ix].pixel();
        GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR, 0, &pen);
        Point.x = xx+ix;
        os2Point.x = Point.x;
        os2Point.y = Point.y;
        MapToOS2(&os2Point);
        GpiSetPel (_hdc, &os2Point);
      }
    }
#endif
    EndPaint();
 }

//==================>>> vWinDC::DrawPoint <<<======================
  void vWinDC::DrawPoint(int x, int y)
  {
    BeginPaint();
    // First, do the required scaling
    int xx = Scale(x+_tx);
    int yy = Scale(y+_ty);

    POINTL Point;
    Point.x = xx;
    Point.y = yy;
    MapToOS2(&Point);


    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR, 0, _hpen);

    // Now the code to actually do the drawing
    GpiSetPel (_hdc, &Point);
    EndPaint();
  }

//==================>>> vWinDC::DrawPoints <<<======================
  void vWinDC::DrawPoints(vPoint* pointList, int count)
  {
    POINTL Point;

    if (count < 1 || pointList == 0)
      return;

    BeginPaint();
    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR, 0, _hpen);

    // Now the code to actually do the drawing
    vPoint* pl = pointList ;
    for (int num = 0 ; num < count ; ++pl, ++num)
    {
      Point.x = Scale(pl->x+_tx);
      Point.y = Scale(pl->y+_ty);
      MapToOS2(&Point);

      GpiSetPel (_hdc, &Point);
    }
    EndPaint();
  }

//==================>>> vWinDC::DrawPolygon <<<======================
  void vWinDC::DrawPolygon(int n, vPoint points[], int fillStyle)
  {
    // draw a complete polygon (starting point specified twice!)
    BeginPaint();
    if (_hasScale || _tx != 0 || _ty != 0)     // If we have to scale, then we need to copy
    {
      for (int i = 0; i < n; i++)
      {
        points[i].x = ((points[i].x+_tx) * _Mult) / _Div;   // scale
        points[i].y = ((points[i].y+_ty) * _Mult) / _Div;
      }
    }

    // we need to be careful here, since we are actually changing the
    // data in the users own structure here.  We will need to put things
    // back the way they were when we are done!
    for (int i = 0; i < n; i++)
    {
      MapToOS2(&points[i]);
    }

    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    _hbrush = _brush.GetHBRUSH();
    GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, _hbrush);

    // fill the outline
    GpiBeginPath(_hdc, 1L);
    GpiSetCurrentPosition (_hdc , &points[0]);   // move to first point
    GpiPolyLine(_hdc, n-1, &points[1]);           // draw the outline polygon
    GpiEndPath(_hdc);

    GpiFillPath(_hdc, 1L, (fillStyle == vAlternate) ? FPATH_ALTERNATE : FPATH_WINDING);

    // draw the outline
    GpiBeginPath(_hdc, 1L);
    GpiSetCurrentPosition (_hdc , &points[0]);   // move to last point
    GpiPolyLine(_hdc, n-1, &points[1]);               // draw the outline polygon
    GpiEndPath(_hdc);

    if (_hpen->usType == LINETYPE_SOLID)
    {
       // for Geometric lines, the fill color is used (what a debacle!)
       AREABUNDLE linebrush;
       linebrush.lColor = _hpen->lColor;               // for stroked lines
       linebrush.usSymbol = PATSYM_SOLID;
       GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
       GpiStrokePath(_hdc, 1L, 0L);   // solid lines can be stroked
    }
    else
      GpiOutlinePath(_hdc, 1L, 0L);  // other lines need to be outlined

    // restore the user's points data back to the original values
    for (i = 0; i < n; i++)
    {
      MapFromOS2(&points[i]);
    }

    EndPaint();
}

//==================>>> vWinDC::DrawRectangle <<<======================
  void vWinDC::DrawRectangle(int x, int y, int width, int height)
  {
    if (height == 0 || width == 0)
      return;

    BeginPaint();
    POINTL poss, pose;
    AREABUNDLE linebrush;

    // coords for GPI functions are inclusive/inclusive
    // right = left + width -1
    // top = bottom + height -1
    poss.x = Scale(x+_tx);
    poss.y = Scale(y+_ty);
    pose.x = Scale(x+width-1+_tx);
    pose.y = Scale(y+height-1+_ty);
    MapToOS2(&poss);
    MapToOS2(&pose);

    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    _hbrush = _brush.GetHBRUSH();
    GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, _hbrush);

    if (_hpen->usType == LINETYPE_SOLID)
    {
      GpiSetCurrentPosition (_hdc, &poss);
      GpiBox(_hdc, DRO_FILL, &pose, 0, 0);
      GpiBeginPath(_hdc, 1L);
      GpiBox(_hdc, DRO_OUTLINE, &pose, 0, 0);
      GpiEndPath(_hdc);

      linebrush.lColor = _hpen->lColor;
      linebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked
    }
    else
    {
      GpiSetCurrentPosition (_hdc, &poss);    // move to start point
      GpiBox(_hdc, DRO_OUTLINEFILL, &pose, 0, 0);
    }
    EndPaint();
  }

//==================>>> vWinDC::DrawRectangles <<<======================
  void vWinDC::DrawRectangles(vRect* rectList, int count)
  {
    if (count < 1 || !rectList)
      return;

    POINTL poss, pose;

    BeginPaint();
    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    _hbrush = _brush.GetHBRUSH();
    GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, _hbrush);

    vRect* rp = rectList ;
    for (int num=0 ; num < count ; ++rp, ++num)
    {
      // coords for GPI functions are inclusive/inclusive
      // right = left + width -1
      // top = bottom + height -1
      poss.x = Scale(rp->x+_tx);
      poss.y = Scale(rp->y+_ty);
      pose.x = Scale(rp->x+rp->w-1+_tx);
      pose.y = Scale(rp->y+rp->h-1+_ty);
      if (rp->h == 0 && rp->w == 0)
        continue;
      MapToOS2(&poss);
      MapToOS2(&pose);

      // if solid line then we can support variable line widths
      if (_hpen->usType == LINETYPE_SOLID)
      {
        GpiSetCurrentPosition (_hdc, &poss);
        GpiBox(_hdc, DRO_FILL, &pose, 0, 0);
        GpiBeginPath(_hdc, 1L);
        GpiBox(_hdc, DRO_OUTLINE, &pose, 0, 0);
        GpiEndPath(_hdc);

        AREABUNDLE linebrush;
        linebrush.lColor = _hpen->lColor;
        linebrush.usSymbol = PATSYM_SOLID;
        GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
        GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked
        // don't forget to reset the brush back to what it was

        GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, _hbrush);
      }
      else
      // otherwise, we only support single pel width lines
      {
        GpiSetCurrentPosition (_hdc, &poss);    // move to start point
        GpiBox(_hdc, DRO_OUTLINEFILL, &pose, 0, 0);
      }
    }
    EndPaint();
  }

//================>>> vWinDC::DrawRoundedRectangle <<<===================
  void vWinDC::DrawRoundedRectangle(int x, int y,
    int width, int height, int radius)
  {
    if (height == 0 || width == 0)
      return;

    BeginPaint();
    // coords for GPI functions are inclusive/inclusive
    // right = left + width -1
    // top = bottom + height -1
    POINTL poss, pose;
    poss.x = Scale(x+_tx);
    poss.y = Scale(y+_ty);
    pose.x = Scale(x+width-1+_tx);
    pose.y = Scale(y+height-1+_ty);
    MapToOS2(&poss);
    MapToOS2(&pose);

    int r;
    if (radius < 0)
    {
      // Negative radius means divide average of height and width
      // by this
      r = (Scale(width+height)/(-2 * radius));
    }
    else
      r = Scale(radius);

    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    _hbrush = _brush.GetHBRUSH();
    GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, _hbrush);

    if (_hpen->usType == LINETYPE_SOLID)
    {
      GpiSetCurrentPosition (_hdc, &poss);
      GpiBox(_hdc, DRO_FILL, &pose, 2*r, 2*r);
      GpiBeginPath(_hdc, 1L);
      GpiBox(_hdc, DRO_OUTLINE, &pose, 2*r, 2*r);
      GpiEndPath(_hdc);

      AREABUNDLE linebrush;
      linebrush.lColor = _hpen->lColor;
      linebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked
    }
    else
    {
      GpiSetCurrentPosition (_hdc, &poss);    // move to start point
      GpiBox(_hdc, DRO_OUTLINEFILL, &pose, 2*r, 2*r);
    }
    EndPaint();
  }

//==================>>> vWinDC::DrawRubberEllipse<<<===================
  void vWinDC::DrawRubberEllipse(int x, int y, int width, int height)
  {
    if (height == 0 || width == 0)
      return;

    BeginPaint();
    // coords for GPI functions are inclusive/inclusive
    // right = left + width -1
    // top = bottom + height -1
    int x1 = Scale(x+_tx);
    int y1 = Scale(y+_ty);
    int width1 = Scale(width-1);
    int height1 = Scale(height-1);

    _hpen = _pen.GetHPEN();             // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH, 0, _hpen);

    // set arc params at 2x scale
    ARCPARAMS arc;
    arc.lP = width1;
    arc.lQ = height1;
    arc.lR = 0;
    arc.lS = 0;
    GpiSetArcParams(_hdc , &arc);

    // compute center of ellipse
    POINTL posc = {x1+(width1/2), y1+(height1/2)};
    MapToOS2(&posc);

    if (_hpen->usType == LINETYPE_SOLID)
    {
      // save the original brush values
      LONG orgColor = _hbrush->lColor;
      USHORT orgMix = _hbrush->usMixMode;

      GpiSetCurrentPosition (_hdc, &posc);
      GpiBeginPath(_hdc, 1L);
      GpiFullArc (_hdc, DRO_OUTLINE, MAKEFIXED(0,0x8000));   // scale by 1/2
      GpiEndPath(_hdc);

      AREABUNDLE linebrush;
      linebrush.lColor = SETRGB(255,255,255);
      linebrush.usMixMode = FM_XOR;           // draw in XOR
      linebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_MIX_MODE | ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked

      // restore the original brush values
      linebrush.lColor = orgColor;
      linebrush.usMixMode = orgMix;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_MIX_MODE | ABB_COLOR, 0, &linebrush);
    }
    else
    {
      // save the original pen values
      LONG orgColor = _hpen->lColor;
      USHORT orgMix = _hpen->usMixMode;

      // change pen for rubber line
      _hpen->lColor = SETRGB(255,255,255);  // set pen color to white
      _hpen->usMixMode = FM_XOR;            // draw in XOR
      GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);

      GpiFullArc (_hdc, DRO_OUTLINE, MAKEFIXED(0,0x8000));   // scale by 1/2
      // restore the original pen values
      _hpen->lColor = orgColor;
      _hpen->usMixMode = orgMix;
      GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);
    EndPaint();
    }
 }

//==================>>> vWinDC::DrawRubberLine <<<======================
  void vWinDC::DrawRubberLine(int x, int y, int xend, int yend)
  {
    BeginPaint();
    // Draw a rubber-band line from x,y to xend,yend. Redrawing
    // over the same with will erase it.
    int xx = Scale(x+_tx);
    int yy = Scale(y+_ty);
    int xe = Scale(xend+_tx);
    int ye = Scale(yend+_ty);

    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH, 0, _hpen);

    // Shapes will do a similar sequence for brushes as well
    // Now the code to actually do the drawing

    POINTL poss = {xx, yy};  // start point
    POINTL pose = {xe, ye};  // end point
    MapToOS2(&poss);
    MapToOS2(&pose);

    if (_hpen->usType == LINETYPE_SOLID)
    {
      // save the original brush values
      LONG orgColor = _hbrush->lColor;
      USHORT orgMix = _hbrush->usMixMode;

      GpiBeginPath(_hdc, 1L);
      GpiSetCurrentPosition (_hdc, &poss);
      GpiLine (_hdc , &pose);
      GpiEndPath(_hdc);

      AREABUNDLE linebrush;
      linebrush.lColor = SETRGB(255,255,255);
      linebrush.usMixMode = FM_XOR;           // draw in XOR
      linebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_MIX_MODE | ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked

      // restore the original brush values
      linebrush.lColor = orgColor;
      linebrush.usMixMode = orgMix;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_MIX_MODE | ABB_COLOR, 0, &linebrush);
    }
    else
    {
      // save the original pen values
      LONG orgColor = _hpen->lColor;
      USHORT orgMix = _hpen->usMixMode;

      // change pen for rubber line
      _hpen->lColor = SETRGB(255,255,255);  // set pen color to white
      _hpen->usMixMode = FM_XOR;            // draw in XOR
      GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);
      GpiSetCurrentPosition (_hdc, &poss);
      GpiLine (_hdc , &pose);

      // restore the original pen values
      _hpen->lColor = orgColor;
      _hpen->usMixMode = orgMix;
      GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);
    }
    EndPaint();
  }

//==================>>> vWinDC::DrawRubberPoint <<<======================
  void vWinDC::DrawRubberPoint(int x, int y)
  {
    BeginPaint();
    // First, do the required scaling
    int xx = Scale(x+_tx);
    int yy = Scale(y+_ty);

    POINTL Point;
    Point.x = xx;
    Point.y = yy;
    MapToOS2(&Point);

    _hpen = _pen.GetHPEN();            // Get the pen
    // save the original pen values
    LONG orgColor = _hpen->lColor;
    USHORT orgMix = _hpen->usMixMode;
    // change pen for rubber line
    _hpen->lColor = SETRGB(255,255,255);  // set pen color to white
    _hpen->usMixMode = FM_XOR;            // draw in XOR
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);
    GpiSetPel (_hdc, &Point);

    // restore the original pen values
    _hpen->lColor = orgColor;
    _hpen->usMixMode = orgMix;
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);
    EndPaint();
 }

//==================>>> vWinDC::DrawRubberRectangle <<<==================
  void vWinDC::DrawRubberRectangle(int x, int y, int width, int height)
  {
    if (height == 0 || width == 0)
      return;

    BeginPaint();
    POINTL poss, pose;
    AREABUNDLE linebrush;
    // coords for GPI functions are inclusive/inclusive
    // right = left + width -1
    // top = bottom + height -1
    poss.x = Scale(x+_tx);
    poss.y = Scale(y+_ty);
    pose.x = Scale(x+width-1+_tx);
    pose.y = Scale(y+height-1+_ty);
    MapToOS2(&poss);
    MapToOS2(&pose);

    _hpen = _pen.GetHPEN();            // Get the pen
    GpiSetAttrs(_hdc, PRIM_LINE, LBB_COLOR | LBB_TYPE | LBB_WIDTH | LBB_GEOM_WIDTH,
      0, _hpen);

    if (_hpen->usType == LINETYPE_SOLID)
    {
      // save the original brush values
      LONG orgColor = _hbrush->lColor;
      USHORT orgMix = _hbrush->usMixMode;

      GpiSetCurrentPosition (_hdc, &poss);
      GpiBeginPath(_hdc, 1L);
      GpiBox(_hdc, DRO_OUTLINE, &pose, 0, 0);
      GpiEndPath(_hdc);

      linebrush.lColor = SETRGB(255,255,255);
      linebrush.usMixMode = FM_XOR;           // draw in XOR
      linebrush.usSymbol = PATSYM_SOLID;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_MIX_MODE | ABB_COLOR | ABB_SYMBOL, 0, &linebrush);
      GpiStrokePath(_hdc, 1L, 0L);   // thick lines need to be stroked

      // restore the original brush values
      linebrush.lColor = orgColor;
      linebrush.usMixMode = orgMix;
      GpiSetAttrs(_hdc, PRIM_AREA, ABB_MIX_MODE | ABB_COLOR, 0, &linebrush);
    }
    else
    {
      // save the original pen values
      LONG orgColor = _hpen->lColor;
      USHORT orgMix = _hpen->usMixMode;
      // change pen for rubber line
      _hpen->lColor = SETRGB(255,255,255);  // set pen color to white
      _hpen->usMixMode = FM_XOR;            // draw in XOR
      GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);

      GpiSetCurrentPosition (_hdc, &poss);    // move to start point
      GpiBox(_hdc, DRO_OUTLINE, &pose, 0, 0);

      // restore the original pen values
      _hpen->lColor = orgColor;
      _hpen->usMixMode = orgMix;
      GpiSetAttrs(_hdc, PRIM_LINE, LBB_MIX_MODE | LBB_COLOR, 0, _hpen);
    }
    EndPaint();
  }

//=====================>>> vWinDC::DrawAttrText <<<==========================
  void vWinDC::DrawAttrText(int x, int y, VCONST char* text, const ChrAttr attr)
  {
    // Draw text with attributes at given x, y.
    static int mapColor[] =
    {
      vC_Black, vC_Blue, vC_Green, vC_Cyan,
      vC_Red, vC_Magenta, vC_Yellow, vC_White,
      vC_DarkGray, vC_DimBlue, vC_DimGreen, vC_DimCyan,
      vC_DimRed, vC_DimMagenta, vC_DimYellow, vC_MedGray
    };

    BeginPaint();
    POINTL poss;
    poss.x = Scale(x+_tx);
    poss.y = Scale(y+_ty);
    MapToOS2(&poss);

    _font.LoadFont(_hdc);              // need to setup font!
    _htext = _font.GetHFONT();
    _htext->usTextAlign = TA_BOTTOM | TA_LEFT;

    // we get the charbundle to control font color
    // we use the current pen to define text colors
    ULONG orgfgc = _pen.GetColor().pixel();
    ULONG orgbgc = _canvasBG;

    if (attr & ChHighlight)
    {
      _htext->usBackMixMode = BM_OVERPAINT;
      _htext->lBackColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_HILITEBACKGROUND, 0L);
      _htext->lColor = WinQuerySysColor(HWND_DESKTOP, SYSCLR_HILITEFOREGROUND, 0L);
      _htext->usTextAlign = TA_BOTTOM | TA_LEFT;
    }
    else if (attr & ChReverse)
    {
      // default backmixmode is BM_LEAVEALONE, the windows version of V
      // overpaints background so we will do accordingly
      _htext->usBackMixMode = BM_OVERPAINT;
      _htext->lBackColor = orgfgc;
      _htext->lColor = orgbgc;
      _htext->usTextAlign = TA_BOTTOM | TA_LEFT;
    }
    else
    {
      _htext->usBackMixMode = BM_OVERPAINT;
//      _htext->usBackMixMode = BM_LEAVEALONE;
      _htext->lBackColor = orgbgc;
      _htext->lColor = orgfgc;
      _htext->usTextAlign = TA_BOTTOM | TA_LEFT;
    }

    if (attr & 0xF0)           // A color overide
    {
      _htext->lColor = vStdColors[ mapColor[((attr & 0xF0) >> 4)] ].pixel();
    }

    GpiSetAttrs(_hdc, PRIM_CHAR, CBB_BACK_COLOR | CBB_BACK_MIX_MODE | CBB_COLOR | CBB_TEXT_ALIGN,
      0, _htext);

    // Now the code to actually do the drawing
    if (_isPrinterDC)
    {
      GpiCharStringAt (_hdc, &poss, strlen(text), text);
    }
    else
    {
      // need to turn off cursor during screen update
      GpiCharStringAt (_hdc, &poss, strlen(text), text);
    }

    if ((attr & ChReverse) || (attr & ChHighlight))
    {
      _htext->usBackMixMode = BM_LEAVEALONE;
      _htext->lBackColor = orgbgc;
      _htext->lColor = orgfgc;
      GpiSetAttrs(_hdc, PRIM_CHAR, CBB_BACK_COLOR | CBB_BACK_MIX_MODE | CBB_COLOR, 0, _htext);
    }
    else
    {
      _htext->usBackMixMode = BM_LEAVEALONE;
      GpiSetAttrs(_hdc, PRIM_CHAR, CBB_BACK_MIX_MODE, 0, _htext);
    }

    EndPaint();
  }

//=====================>>> vWinDC::DrawText <<<==========================
  void vWinDC::DrawText(int x, int y, VCONST char* text)
  {
    // simple draw text at given x, y
    // First, do the required scaling
    if (!text || !*text)
      return;

    BeginPaint();
    POINTL poss;
    poss.x = Scale(x+_tx);
    poss.y = Scale(y+_ty);
    MapToOS2(&poss);

    _font.LoadFont(_hdc);              // need to setup font!
    _htext = _font.GetHFONT();

    _htext->lColor = _pen.GetColor().pixel();
    _htext->lBackColor = _canvasBG;
    _htext->usBackMixMode = BM_OVERPAINT;
    _htext->usTextAlign = TA_BOTTOM | TA_LEFT;
    GpiSetAttrs(_hdc, PRIM_CHAR,
      CBB_BACK_COLOR | CBB_COLOR | CBB_TEXT_ALIGN | CBB_BACK_MIX_MODE, 0, _htext);

    // Now the code to actually do the drawing
    // We fudge the y position to accomodate the
    // model transform that puts the origin at the top left.
    if (_isPrinterDC)
    {
//      poss.y = poss.y + _font.GetPointSize();
      GpiCharStringAt (_hdc, &poss, strlen(text), text);
    }
    else
    {
      // need to turn off cursor during screen update
      GpiCharStringAt (_hdc, &poss, strlen(text), text);
    }

    // restore to deafult mix mode
    _htext->usBackMixMode = BM_LEAVEALONE;
    GpiSetAttrs(_hdc, PRIM_CHAR, CBB_BACK_MIX_MODE, 0, _htext);

    EndPaint();
  }

//=====================>>> vWinDC::SetBrush <<<============================
  void vWinDC::SetBrush(VCONST vBrush& brush)
  {
    _brush = brush;
  }

//=====================>>> vWinDC::SetPen <<<============================
  void vWinDC::SetPen(VCONST vPen& pen)
  {
    _pen = pen;
  }

//====================>>> vWinDC::TextHeight <<<=============================
  int vWinDC::TextHeight(int& asc, int& des) VCONST
  {
    // Return total height of this font. V will use total height, which
    // is most often made up of ascent + descent.  This is too much
    // detail for the kind of apps V will support.
    _font.LoadFont(_hdc);              // need to setup font!
    _htext = _font.GetHFONT();
    _htext->usTextAlign = TA_BOTTOM | TA_LEFT;
    GpiSetAttrs(_hdc, PRIM_CHAR, CBB_BACK_COLOR | CBB_COLOR | CBB_TEXT_ALIGN, 0, _htext);

    FONTMETRICS fm;
    int a = 5, d = 3;                  // something reasonable?
    GpiQueryFontMetrics (_hdc, sizeof(FONTMETRICS), &fm);
    a = fm.lMaxAscender;
    d = fm.lMaxDescender;

    asc = a ; des = d;

    if (_isPrinterDC)  // need to correct for the scaling of the print canvas
    {
      return ((a+d)*65536/_OS2Scale);
    }
    else
    {
      return (a+d);
    }
  }

//========================>>> vvWinDC::TextWidth <<<==========================
  int vWinDC::TextWidth(char* str) VCONST
  {
    _font.LoadFont(_hdc);              // need to setup font!
    _htext = _font.GetHFONT();
    GpiSetAttrs(_hdc, PRIM_CHAR, CBB_BACK_COLOR | CBB_COLOR, 0, _htext);

    POINTL box[TXTBOX_COUNT];
    GpiQueryTextBox (_hdc, strlen(str), str, TXTBOX_COUNT, box);

    int width = box[TXTBOX_TOPRIGHT].x - box[TXTBOX_TOPLEFT].x;

    if (_isPrinterDC)  // need to correct for the scaling of the print canvas
    {
      return (width*65536/_OS2Scale);
    }
    else
    {
      return (width);
    }
  }

//========================>>> vvWinDC::MapToOS2 <<<===========================
  void vWinDC::MapToOS2(PPOINTL in)
  {
    // we use the OS/2 FIXED data type to handle fractional
    // scaling while retaining integer math
    ULONG m, f;

    m = HIUSHORT(_OS2Scale);
    f = LOUSHORT(_OS2Scale);
    in->y = -(1-_OS2Height + in->y);
    in->y = (in->y * m) + (in->y * f/65536);
    in->x = (in->x * m) + (in->x * f/65536);
  }

//========================>>> vvWinDC::MapFromOS2 <<<=========================
  void vWinDC::MapFromOS2(PPOINTL in)
  {
    // we use the OS/2 FIXED data type to handle fractional
    // scaling while retaining integer math
    in->y = -(1-_OS2Height + (in->y * 65536/_OS2Scale) );
    in->x = in->x * 65536/_OS2Scale;
  }


