/////////////////////////////////////////////////////////////////////////////
// Name:        src/os2/dc.cpp
// Purpose:     wxDC class
// Author:      David Webster
// Modified by:
// Created:     10/14/99
// RCS-ID:      $Id: dc.cpp 51287 2008-01-19 14:10:36Z SN $
// Copyright:   (c) David Webster
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"

#ifndef WX_PRECOMP
    #include "wx/window.h"
    #include "wx/dc.h"
    #include "wx/utils.h"
    #include "wx/dialog.h"
    #include "wx/app.h"
    #include "wx/bitmap.h"
    #include "wx/dcmemory.h"
    #include "wx/log.h"
    #include "wx/icon.h"
    #include "wx/msgdlg.h"
    #include "wx/dcprint.h"
    #include "wx/statusbr.h"
    #include "wx/module.h"
#endif

#include <string.h>

#include "wx/os2/private.h"

IMPLEMENT_ABSTRACT_CLASS(wxDC, wxObject)

//
// wxWidgets uses the Microsoft convention that the origin is the UPPER left.
// Native OS/2 however in the GPI and PM define the origin as the LOWER left.
// In order to map OS/2 GPI/PM y coordinates to wxWidgets coordinates we must
// perform the following transformation:
//
// Parent object height:     POBJHEIGHT
// Desried origin:           WXORIGINY
// Object to place's height: OBJHEIGHT
//
// To get the OS2 position from the wxWidgets one:
//
// OS2Y = POBJHEIGHT - (WXORIGINY + OBJHEIGHT)
//
// For OS/2 wxDC's we will always determine m_vRclPaint as the size of the
// OS/2 Presentation Space associated with the device context.  y is the
// desired application's y coordinate of the origin in wxWidgets space.
// objy is the height of the object we are going to draw.
//
#define OS2Y(y, objy) ((m_vRclPaint.yTop - m_vRclPaint.yBottom) - (y + objy))

// ---------------------------------------------------------------------------
// constants
// ---------------------------------------------------------------------------

static const int VIEWPORT_EXTENT = 1000;

static const int MM_POINTS = 9;
static const int MM_METRIC = 10;

// ---------------------------------------------------------------------------
// private functions
// ---------------------------------------------------------------------------

// convert degrees to radians
static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }

int SetTextColor(
  HPS                               hPS
, int                               nForegroundColour
)
{
    CHARBUNDLE                      vCbnd;

    vCbnd.lColor =  nForegroundColour;
    ::GpiSetAttrs( hPS       // presentation-space handle
                  ,PRIM_CHAR // Char primitive.
                  ,CBB_COLOR // sets color.
                  ,0         //
                  ,&vCbnd    // buffer for attributes.
                 );
    return 0;
}

int QueryTextBkColor(
  HPS                               hPS
)
{
    CHARBUNDLE                      vCbnd;

    ::GpiQueryAttrs( hPS            // presentation-space handle
                    ,PRIM_CHAR      // Char primitive.
                    ,CBB_BACK_COLOR // Background color.
                    ,&vCbnd         // buffer for attributes.
                   );
    return vCbnd.lBackColor;
}


int SetTextBkColor(
  HPS                               hPS
, int                               nBackgroundColour
)
{
    CHARBUNDLE                      vCbnd;
    int                             rc;

    rc =  QueryTextBkColor(hPS);

    vCbnd.lBackColor = nBackgroundColour;
    ::GpiSetAttrs(hPS,            // presentation-space handle
                  PRIM_CHAR,      // Char primitive.
                  CBB_BACK_COLOR, // sets color.
                  0,
                  &vCbnd          // buffer for attributes.
                 );
    return rc;
}

int SetBkMode(
  HPS                               hPS
, int                               nBackgroundMode
)
{
    if(nBackgroundMode == wxTRANSPARENT)
        ::GpiSetBackMix( hPS
                        ,BM_LEAVEALONE
                       );
    else
        // the background of the primitive takes  over whatever is underneath.
        ::GpiSetBackMix( hPS
                        ,BM_OVERPAINT
                        );
    return 0;
}

// ===========================================================================
// implementation
// ===========================================================================

#if wxUSE_DC_CACHEING

/*
 * This implementation is a bit ugly and uses the old-fashioned wxList class, so I will
 * improve it in due course, either using arrays, or simply storing pointers to one
 * entry for the bitmap, and two for the DCs. -- JACS
 */

// ---------------------------------------------------------------------------
// wxDCCacheEntry
// ---------------------------------------------------------------------------

wxList wxDC::m_svBitmapCache;
wxList wxDC::m_svDCCache;

wxDCCacheEntry::wxDCCacheEntry(
  WXHBITMAP                         hBitmap
, int                               nWidth
, int                               nHeight
, int                               nDepth
)
{
    m_hBitmap = hBitmap;
    m_hPS     = NULLHANDLE;
    m_nWidth  = nWidth;
    m_nHeight = nHeight;
    m_nDepth  = nDepth;
} // end of wxDCCacheEntry::wxDCCacheEntry

wxDCCacheEntry::wxDCCacheEntry(
  HPS                               hPS
, int                               nDepth
)
{
    m_hBitmap = NULLHANDLE;
    m_hPS     = hPS;
    m_nWidth  = 0;
    m_nHeight = 0;
    m_nDepth  = nDepth;
} // end of wxDCCacheEntry::wxDCCacheEntry

wxDCCacheEntry::~wxDCCacheEntry()
{
    if (m_hBitmap)
        ::GpiDeleteBitmap(m_hBitmap);
    if (m_hPS)
        ::GpiDestroyPS(m_hPS);
} // end of wxDCCacheEntry::~wxDCCacheEntry

wxDCCacheEntry* wxDC::FindBitmapInCache(
  HPS                               hPS
, int                               nWidth
, int                               nHeight
)
{
    int                             nDepth = 24; // we'll fix this later ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
    wxNode*                         pNode = m_svBitmapCache.First();
    BITMAPINFOHEADER2               vBmpHdr;

    while(pNode)
    {
        wxDCCacheEntry*             pEntry = (wxDCCacheEntry*)pNode->Data();

        if (pEntry->m_nDepth == nDepth)
        {
            memset(&vBmpHdr, 0, sizeof(BITMAPINFOHEADER2));

            if (pEntry->m_nWidth < nWidth || pEntry->m_nHeight < nHeight)
            {
                ::GpiDeleteBitmap((HBITMAP)pEntry->m_hBitmap);
                vBmpHdr.cbFix     = sizeof(BITMAPINFOHEADER2);
                vBmpHdr.cx        = nWidth;
                vBmpHdr.cy        = nHeight;
                vBmpHdr.cPlanes   = 1;
                vBmpHdr.cBitCount = (USHORT)nDepth;

                pEntry->m_hBitmap = (WXHBITMAP) ::GpiCreateBitmap( hPS
                                                                  ,&vBmpHdr
                                                                  ,0L, NULL, NULL
                                                                 );
                if (!pEntry->m_hBitmap)
                {
                    wxLogLastError(wxT("CreateCompatibleBitmap"));
                }
                pEntry->m_nWidth  = nWidth;
                pEntry->m_nHeight = nHeight;
                return pEntry;
            }
            return pEntry;
        }
        pNode = pNode->Next();
    }
    memset(&vBmpHdr, 0, sizeof(BITMAPINFOHEADER2));
    vBmpHdr.cbFix     = sizeof(BITMAPINFOHEADER2);
    vBmpHdr.cx        = nWidth;
    vBmpHdr.cy        = nHeight;
    vBmpHdr.cPlanes   = 1;
    vBmpHdr.cBitCount = (USHORT)nDepth;

    WXHBITMAP                       hBitmap = (WXHBITMAP) ::GpiCreateBitmap( hPS
                                                                            ,&vBmpHdr
                                                                            ,0L, NULL, NULL
                                                                           );
    if (!hBitmap)
    {
        wxLogLastError(wxT("CreateCompatibleBitmap"));
    }
    wxDCCacheEntry*                 pEntry = new wxDCCacheEntry( hBitmap
                                                                ,nWidth
                                                                ,nHeight
                                                                ,nDepth
                                                               );
    AddToBitmapCache(pEntry);
    return pEntry;
} // end of FindBitmapInCache

wxDCCacheEntry* wxDC::FindDCInCache(
  wxDCCacheEntry*                   pNotThis
, HPS                               hPS
)
{
    int                             nDepth = 24; // we'll fix this up later ::GetDeviceCaps((HDC) dc, PLANES) * ::GetDeviceCaps((HDC) dc, BITSPIXEL);
    wxNode*                         pNode = m_svDCCache.First();

    while(pNode)
    {
        wxDCCacheEntry*             pEntry = (wxDCCacheEntry*)pNode->Data();

        //
        // Don't return the same one as we already have
        //
        if (!pNotThis || (pNotThis != pEntry))
        {
            if (pEntry->m_nDepth == nDepth)
            {
                return pEntry;
            }
        }
        pNode = pNode->Next();
    }
    wxDCCacheEntry*                 pEntry = new wxDCCacheEntry( hPS
                                                                ,nDepth
                                                               );
    AddToDCCache(pEntry);
    return pEntry;
} // end of wxDC::FindDCInCache

void wxDC::AddToBitmapCache(
  wxDCCacheEntry*                   pEntry
)
{
    m_svBitmapCache.Append(pEntry);
} // end of wxDC::AddToBitmapCache

void wxDC::AddToDCCache(
  wxDCCacheEntry*                   pEntry
)
{
    m_svDCCache.Append(pEntry);
} // end of wxDC::AddToDCCache

void wxDC::ClearCache()
{
    m_svBitmapCache.DeleteContents(true);
    m_svBitmapCache.Clear();
    m_svBitmapCache.DeleteContents(false);
    m_svDCCache.DeleteContents(true);
    m_svDCCache.Clear();
    m_svDCCache.DeleteContents(false);
} // end of wxDC::ClearCache

// Clean up cache at app exit
class wxDCModule : public wxModule
{
public:
    virtual bool OnInit() { return true; }
    virtual void OnExit() { wxDC::ClearCache(); }

private:
    DECLARE_DYNAMIC_CLASS(wxDCModule)
}; // end of CLASS wxDCModule

IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)

#endif // ndef for wxUSE_DC_CACHEING

// ---------------------------------------------------------------------------
// wxDC
// ---------------------------------------------------------------------------

wxDC::wxDC(void)
{
    m_pCanvas      = NULL;

    m_hOldBitmap   = 0;
    m_hOldPen      = 0;
    m_hOldBrush    = 0;
    m_hOldFont     = 0;
    m_hOldPalette  = 0;

    m_bOwnsDC      = false;
    m_hDC          = 0;
    m_hOldPS       = NULL;
    m_hPS          = NULL;
    m_bIsPaintTime = false; // True at Paint Time

    m_pen.SetColour(*wxBLACK);
    m_brush.SetColour(*wxWHITE);

} // end of wxDC::wxDC

wxDC::~wxDC(void)
{
    if ( m_hDC != 0 )
    {
        SelectOldObjects(m_hDC);

        // if we own the HDC, we delete it, otherwise we just release it

        if (m_bOwnsDC)
        {
            if(m_hPS)
            {
                ::GpiAssociate(m_hPS, NULLHANDLE);
                ::GpiDestroyPS(m_hPS);
            }
            m_hPS = NULLHANDLE;
            ::DevCloseDC((HDC)m_hDC);
        }
        else
        {
            //
            // Just Dissacociate, not destroy if we don't own the DC
            //
            if(m_hPS)
            {
                ::GpiAssociate(m_hPS, NULLHANDLE);
            }
        }
    }
} // end of wxDC::~wxDC

// This will select current objects out of the DC,
// which is what you have to do before deleting the
// DC.
void wxDC::SelectOldObjects(
  WXHDC                             hPS
)
{
    if (hPS)
    {
        if (m_hOldBitmap)
        {
            ::GpiSetBitmap(hPS, (HBITMAP) m_hOldBitmap);
            if (m_vSelectedBitmap.Ok())
            {
                m_vSelectedBitmap.SetSelectedInto(NULL);
            }
        }
        m_hOldBitmap = 0;
        //
        // OS/2 has no other native GDI objects to set in a PS/DC like windows
        //
        m_hOldPen = 0;
        m_hOldBrush = 0;
        m_hOldFont = 0;
        m_hOldPalette = 0;
    }

    m_brush           = wxNullBrush;
    m_pen             = wxNullPen;
    m_palette         = wxNullPalette;
    m_font            = wxNullFont;
    m_backgroundBrush = wxNullBrush;
    m_vSelectedBitmap = wxNullBitmap;
} // end of wxDC::SelectOldObjects

// ---------------------------------------------------------------------------
// clipping
// ---------------------------------------------------------------------------

#define DO_SET_CLIPPING_BOX()                    \
{                                                \
    RECTL rect;                                  \
                                                 \
    ::GpiQueryClipBox(m_hPS, &rect);             \
                                                 \
    m_clipX1 = (wxCoord) XDEV2LOG(rect.xLeft);   \
    m_clipY1 = (wxCoord) YDEV2LOG(rect.yTop);    \
    m_clipX2 = (wxCoord) XDEV2LOG(rect.xRight);  \
    m_clipY2 = (wxCoord) YDEV2LOG(rect.yBottom); \
}

void wxDC::DoSetClippingRegion(
  wxCoord                           vX
, wxCoord                           vY
, wxCoord                           vWidth
, wxCoord                           vHeight
)
{
    RECTL                           vRect;

    vY = OS2Y(vY,vHeight);
    m_clipping    = true;
    vRect.xLeft   = vX;
    vRect.yTop    = vY + vHeight;
    vRect.xRight  = vX + vWidth;
    vRect.yBottom = vY;
    ::GpiIntersectClipRectangle(m_hPS, &vRect);
    DO_SET_CLIPPING_BOX()
} // end of wxDC::DoSetClippingRegion

void wxDC::DoSetClippingRegionAsRegion(
  const wxRegion&                   rRegion
)
{
     wxCHECK_RET(rRegion.GetHRGN(), wxT("invalid clipping region"));
     HRGN                           hRgnOld;

     m_clipping = true;
     ::GpiSetClipRegion( m_hPS
                        ,(HRGN)rRegion.GetHRGN()
                        ,&hRgnOld
                       );
    DO_SET_CLIPPING_BOX()
} // end of wxDC::DoSetClippingRegionAsRegion

void wxDC::DestroyClippingRegion(void)
{
    if (m_clipping && m_hPS)
    {
         HRGN                       hRgnOld;
         RECTL                      vRect;

         // TODO: this should restore the previous clipped region
         //       so that OnPaint processing works correctly, and
         //       the update doesn't get destroyed after the first
         //       DestroyClippingRegion
         vRect.xLeft   = XLOG2DEV(0);
         vRect.yTop    = YLOG2DEV(32000);
         vRect.xRight  = XLOG2DEV(32000);
         vRect.yBottom = YLOG2DEV(0);

         HRGN                       hRgn = ::GpiCreateRegion(m_hPS, 1, &vRect);

         ::GpiSetClipRegion(m_hPS, hRgn, &hRgnOld);
     }
    ResetClipping();
} // end of wxDC::DestroyClippingRegion

// ---------------------------------------------------------------------------
// query capabilities
// ---------------------------------------------------------------------------

bool wxDC::CanDrawBitmap() const
{
    return true;
}

bool wxDC::CanGetTextExtent() const
{
    LONG                            lTechnology = 0L;

    ::DevQueryCaps(GetHDC(), CAPS_TECHNOLOGY, 1L, &lTechnology);
    return (lTechnology == CAPS_TECH_RASTER_DISPLAY) || (lTechnology == CAPS_TECH_RASTER_PRINTER);
} // end of wxDC::CanGetTextExtent

int wxDC::GetDepth() const
{
    LONG                            lCapsColorBitcount;
    int                             nBitsPerPixel = 0;

    if(::DevQueryCaps( GetHDC()
                      ,CAPS_COLOR_BITCOUNT
                      ,1L
                      ,&lCapsColorBitcount
                     ))
    {
        nBitsPerPixel = (int)lCapsColorBitcount;
    }
    return nBitsPerPixel;
} // end of wxDC::GetDepth

// ---------------------------------------------------------------------------
// drawing
// ---------------------------------------------------------------------------

void wxDC::Clear()
{
    //
    // If this is a canvas DC then just fill with the background color
    // Otherwise purge the whole thing
    //
    if (m_pCanvas)
    {
        RECTL                       vRect;

        ::GpiQueryClipBox(m_hPS, &vRect);
        ::WinFillRect(m_hPS, &vRect, ::GpiQueryBackColor(m_hPS));
    }
    else
    ::GpiErase(m_hPS);
} // end of wxDC::Clear

bool wxDC::DoFloodFill(
  wxCoord                           vX
, wxCoord                           vY
, const wxColour&                   rCol
, int                               nStyle
)
{
    POINTL                          vPtlPos;
    LONG                            lColor;
    LONG                            lOptions;
    LONG                            lHits;
    bool                            bSuccess = false;

    vPtlPos.x = vX;             // Loads x-coordinate
    vPtlPos.y = OS2Y(vY,0);     // Loads y-coordinate
    ::GpiMove(m_hPS, &vPtlPos); // Sets current position
    lColor = rCol.GetPixel();
    lOptions = FF_BOUNDARY;
    if(wxFLOOD_SURFACE == nStyle)
        lOptions = FF_SURFACE;

    if ((lHits = ::GpiFloodFill(m_hPS, lOptions, lColor)) != GPI_ERROR)
        bSuccess = true;

    return bSuccess;
} // end of wxDC::DoFloodFill

bool wxDC::DoGetPixel(
  wxCoord                           vX
, wxCoord                           vY
, wxColour*                         pCol
) const
{
    POINTL                          vPoint;
    LONG                            lColor;

    vPoint.x = vX;
    vPoint.y = OS2Y(vY,0);
    lColor = ::GpiQueryPel(m_hPS, &vPoint);

    //
    // return the color of the pixel
    //
    if(pCol)
        pCol->Set( GetRValue(lColor)
                  ,GetGValue(lColor)
                  ,GetBValue(lColor)
                 );
    return true;
} // end of wxDC::DoGetPixel

void wxDC::DoCrossHair(
  wxCoord                           vX
, wxCoord                           vY
)
{
    vY = OS2Y(vY,0);

    wxCoord                         vX1 = vX - VIEWPORT_EXTENT;
    wxCoord                         vY1 = vY - VIEWPORT_EXTENT;
    wxCoord                         vX2 = vX + VIEWPORT_EXTENT;
    wxCoord                         vY2 = vY + VIEWPORT_EXTENT;
    POINTL                          vPoint[4];

    vPoint[0].x = vX1;
    vPoint[0].y = vY;

    vPoint[1].x = vX2;
    vPoint[1].y = vY;

    ::GpiMove(m_hPS, &vPoint[0]);
    ::GpiLine(m_hPS, &vPoint[1]);

    vPoint[2].x = vX;
    vPoint[2].y = vY1;

    vPoint[3].x = vX;
    vPoint[3].y = vY2;

    ::GpiMove(m_hPS, &vPoint[2]);
    ::GpiLine(m_hPS, &vPoint[3]);
    CalcBoundingBox(vX1, vY1);
    CalcBoundingBox(vX2, vY2);
} // end of wxDC::DoCrossHair

void wxDC::DoDrawLine(
  wxCoord                           vX1
, wxCoord                           vY1
, wxCoord                           vX2
, wxCoord                           vY2
)
{
    POINTL                          vPoint[2];
    COLORREF                        vColor = 0x00ffffff;

    //
    // Might be a memory DC with no Paint rect.
    //
    if (!(m_vRclPaint.yTop == 0 &&
          m_vRclPaint.yBottom == 0 &&
          m_vRclPaint.xRight == 0 &&
          m_vRclPaint.xLeft == 0))
    {
        vY1 = OS2Y(vY1,0);
        vY2 = OS2Y(vY2,0);
    }
    else
    {
        if (m_vSelectedBitmap.Ok())
        {
            m_vRclPaint.yTop = m_vSelectedBitmap.GetHeight();
            m_vRclPaint.xRight = m_vSelectedBitmap.GetWidth();
            vY1 = OS2Y(vY1,0);
            vY2 = OS2Y(vY2,0);
        }
    }
    vPoint[0].x = vX1;
    vPoint[0].y = vY1;
    vPoint[1].x = vX2;
    vPoint[1].y = vY2;
    if (m_pen.Ok())
    {
        vColor = m_pen.GetColour().GetPixel();
    }
    ::GpiSetColor(m_hPS, vColor);
    ::GpiMove(m_hPS, &vPoint[0]);
    ::GpiLine(m_hPS, &vPoint[1]);
    CalcBoundingBox(vX1, vY1);
    CalcBoundingBox(vX2, vY2);
} // end of wxDC::DoDrawLine

//////////////////////////////////////////////////////////////////////////////
// Draws an arc of a circle, centred on (xc, yc), with starting point (x1, y1)
// and ending at (x2, y2). The current pen is used for the outline and the
// current brush for filling the shape. The arc is drawn in an anticlockwise
// direction from the start point to the end point.
//////////////////////////////////////////////////////////////////////////////
void wxDC::DoDrawArc(
  wxCoord                           vX1
, wxCoord                           vY1
, wxCoord                           vX2
, wxCoord                           vY2
, wxCoord                           vXc
, wxCoord                           vYc
)
{
     POINTL                         vPtlPos;
     POINTL                         vPtlArc[2]; // Structure for current position
     double                         dRadius;
     double                         dAngl1;
     double                         dAngl2;
     double                         dAnglmid;
     wxCoord                        vXm;
     wxCoord                        vYm;
     ARCPARAMS                      vArcp; // Structure for arc parameters

    if((vX1 == vXc && vY1 == vXc) || (vX2 == vXc && vY2 == vXc))
        return; // Draw point ??
    dRadius = 0.5 * ( hypot( (double)(vY1 - vYc)
                            ,(double)(vX1 - vXc)
                           ) +
                      hypot( (double)(vY2 - vYc)
                            ,(double)(vX2 - vXc)
                           )
                     );

    dAngl1 = atan2( (double)(vY1 - vYc)
                   ,(double)(vX1 - vXc)
                  );
    dAngl2 = atan2( (double)(vY2 - vYc)
                   ,(double)(vX2 - vXc)
                  );
    if(dAngl2 < dAngl1)
        dAngl2 += M_PI * 2;

    //
    // GpiPointArc can't draw full arc
    //
     if(dAngl2 == dAngl1 || (vX1 == vX2 && vY1 == vY2) )
     {
        //
        // Medium point
        //
        dAnglmid = (dAngl1 + dAngl2)/2. + M_PI;
        vXm      = (wxCoord)(vXc + dRadius * cos(dAnglmid));
        vYm      = (wxCoord)(vYc + dRadius * sin(dAnglmid));
        DoDrawArc( vX1, vY1
                  ,vXm, vYm
                  ,vXc, vYc
                 );
        DoDrawArc( vXm, vYm
                  ,vX2, vY2
                  ,vXc, vYc
                 );
        return;
    }

    //
    // Medium point
    //
    dAnglmid = (dAngl1 + dAngl2)/2.;
    vXm      = (wxCoord)(vXc + dRadius * cos(dAnglmid));
    vYm      = (wxCoord)(vYc + dRadius * sin(dAnglmid));

    //
    // Ellipse main axis (r,q), (p,s) with center at (0,0) */
    //
    vArcp.lR = 0;
    vArcp.lQ = 1;
    vArcp.lP = 1;
    vArcp.lS = 0;
    ::GpiSetArcParams(m_hPS, &vArcp); // Sets parameters to default

    vPtlPos.x = vX1; // Loads x-coordinate
    vPtlPos.y = vY1; // Loads y-coordinate
    ::GpiMove(m_hPS, &vPtlPos); // Sets current position
    vPtlArc[0].x = vXm;
    vPtlArc[0].y = vYm;
    vPtlArc[1].x = vX2;
    vPtlArc[1].y = vY2;
    ::GpiPointArc(m_hPS, vPtlArc); // Draws the arc
    CalcBoundingBox( (wxCoord)(vXc - dRadius)
                    ,(wxCoord)(vYc - dRadius)
                   );
    CalcBoundingBox( (wxCoord)(vXc + dRadius)
                    ,(wxCoord)(vYc + dRadius)
                   );
} // end of wxDC::DoDrawArc

void wxDC::DoDrawCheckMark(
  wxCoord                           vX1
, wxCoord                           vY1
, wxCoord                           vWidth
, wxCoord                           vHeight
)
{
    POINTL                          vPoint[2];

    vY1 = OS2Y(vY1,vHeight);

    vPoint[0].x = vX1;
    vPoint[0].y = vY1;
    vPoint[1].x = vX1 + vWidth;
    vPoint[1].y = vY1 + vHeight;

    ::GpiMove(m_hPS, &vPoint[0]);
    ::GpiBox( m_hPS       // handle to a presentation space
             ,DRO_OUTLINE // draw the box outline ? or ?
             ,&vPoint[1]  // address of the corner
             ,0L          // horizontal corner radius
             ,0L          // vertical corner radius
            );
    if(vWidth > 4 && vHeight > 4)
    {
        int                         nTmp;

        vPoint[0].x += 2; vPoint[0].y += 2;
        vPoint[1].x -= 2; vPoint[1].y -= 2;
        ::GpiMove(m_hPS, &vPoint[0]);
        ::GpiLine(m_hPS, &vPoint[1]);
        nTmp = vPoint[0].x;
        vPoint[0].x = vPoint[1].x;
        vPoint[1].x = nTmp;
        ::GpiMove(m_hPS, &vPoint[0]);
        ::GpiLine(m_hPS, &vPoint[1]);
    }
    CalcBoundingBox( vX1
                    ,vY1
                   );

    wxCoord                         vX2 = vX1 + vWidth;
    wxCoord                         vY2 = vY1 + vHeight;

    CalcBoundingBox( vX2
                    ,vY2
                   );
} // end of wxDC::DoDrawCheckMark

void wxDC::DoDrawPoint(
  wxCoord                           vX
, wxCoord                           vY
)
{
    POINTL                          vPoint;
    COLORREF                        vColor = 0x00ffffff;

    if (m_pen.Ok())
    {
        vColor = m_pen.GetColour().GetPixel();
    }
    ::GpiSetColor(m_hPS, vColor);
    vPoint.x = vX;
    vPoint.y = OS2Y(vY,0);
    ::GpiSetPel(m_hPS, &vPoint);
    CalcBoundingBox( vX
                    ,vY
                   );
} // end of wxDC::DoDrawPoint

void wxDC::DoDrawPolygon( int n,
                          wxPoint vPoints[],
                          wxCoord vXoffset,
                          wxCoord vYoffset,
                          int nFillStyle )
{
    ULONG     ulCount = 1;    // Number of polygons.
    POLYGON   vPlgn;          // polygon.
    ULONG     flOptions = 0L; // Drawing options.

    //////////////////////////////////////////////////////////////////////////////
    // This contains fields of option bits... to draw boundary lines as well as
    // the area interior.
    //
    // Drawing boundary lines:
    //   POLYGON_NOBOUNDARY              Does not draw boundary lines.
    //   POLYGON_BOUNDARY                Draws boundary lines (the default).
    //
    // Construction of the area interior:
    //   POLYGON_ALTERNATE               Constructs interior in alternate mode
    //                                   (the default).
    //   POLYGON_WINDING                 Constructs interior in winding mode.
    //////////////////////////////////////////////////////////////////////////////

    ULONG     flModel = POLYGON_INCL; // Drawing model.

    //////////////////////////////////////////////////////////////////////////////
    // Drawing model.
    //   POLYGON_INCL  Fill is inclusive of bottom right (the default).
    //   POLYGON_EXCL  Fill is exclusive of bottom right.
    //       This is provided to aid migration from other graphics models.
    //////////////////////////////////////////////////////////////////////////////

    LONG      lHits = 0L; // Correlation/error indicator.
    int       i;
    int       nIsTRANSPARENT = 0;
    LONG      lBorderColor = 0L;
    LONG      lColor = 0L;

    lBorderColor = m_pen.GetColour().GetPixel();
    lColor       = m_brush.GetColour().GetPixel();
    if(m_brush.GetStyle() == wxTRANSPARENT)
        nIsTRANSPARENT = 1;

    vPlgn.ulPoints = n;
    vPlgn.aPointl = (POINTL*) calloc( n + 1
                                     ,sizeof(POINTL)
                                    ); // well, new will call malloc

    for(i = 0; i < n; i++)
    {
        vPlgn.aPointl[i].x = vPoints[i].x+vXoffset;
        vPlgn.aPointl[i].y = OS2Y(vPoints[i].y+vYoffset,0);
    }
    flOptions = POLYGON_BOUNDARY;
    if(nFillStyle == wxWINDING_RULE)
        flOptions |= POLYGON_WINDING;
    else
        flOptions |= POLYGON_ALTERNATE;

    ::GpiSetColor(m_hPS, lBorderColor);
    ::GpiMove(m_hPS, &vPlgn.aPointl[0]);
    lHits = ::GpiPolygons(m_hPS, ulCount, &vPlgn, flOptions, flModel);
    free(vPlgn.aPointl);
} // end of wxDC::DoDrawPolygon

void wxDC::DoDrawLines(
  int                               n
, wxPoint                           vPoints[]
, wxCoord                           vXoffset
, wxCoord                           vYoffset
)
{
    POINTL                          vPoint;

    if (vXoffset != 0L || vXoffset != 0L)
    {
        int                             i;

        vPoint.x = vPoints[0].x + vXoffset;
        vPoint.y = OS2Y(vPoints[0].y + vYoffset,0);
        ::GpiMove(m_hPS, &vPoint);

        LONG                            lBorderColor = m_pen.GetColour().GetPixel();

        ::GpiSetColor(m_hPS, lBorderColor);
        for(i = 1; i < n; i++)
        {
            vPoint.x = vPoints[i].x + vXoffset;
            vPoint.y = OS2Y(vPoints[i].y + vYoffset,0);
            ::GpiLine(m_hPS, &vPoint);
        }
    }
    else
    {
        int                         i;

        CalcBoundingBox( vPoints[0].x
                        ,vPoints[0].y
                       );
        vPoint.x = vPoints[0].x;
        vPoint.y = OS2Y(vPoints[0].y,0);
        ::GpiMove(m_hPS, &vPoint);

        for (i = 0; i < n; i++)
        {
            CalcBoundingBox( vPoints[i].x
                            ,vPoints[i].y
                           );
            vPoint.x = vPoints[i].x;
            vPoint.y = OS2Y(vPoints[i].y,0);
            ::GpiLine(m_hPS, &vPoint);
        }
    }
} // end of wxDC::DoDrawLines

void wxDC::DoDrawRectangle(
  wxCoord                           vX
, wxCoord                           vY
, wxCoord                           vWidth
, wxCoord                           vHeight
)
{
    POINTL                          vPoint[2];
    LONG                            lControl;
    LONG                            lColor;
    LONG                            lBorderColor;
    int                             nIsTRANSPARENT = 0;

    //
    // Might be a memory DC with no Paint rect.
    //
    if (!(m_vRclPaint.yTop == 0 &&
          m_vRclPaint.yBottom == 0 &&
          m_vRclPaint.xRight == 0 &&
          m_vRclPaint.xLeft == 0))
        vY = OS2Y(vY,vHeight);
    else
    {
        if (m_vSelectedBitmap.Ok())
        {
            m_vRclPaint.yTop = m_vSelectedBitmap.GetHeight();
            m_vRclPaint.xRight = m_vSelectedBitmap.GetWidth();
            vY = OS2Y(vY,vHeight);
        }
    }

    wxCoord                         vX2 = vX + vWidth;
    wxCoord                         vY2 = vY + vHeight;

    vPoint[0].x = vX;
    vPoint[0].y = vY;
    vPoint[1].x = vX + vWidth - 1;
    vPoint[1].y = vY + vHeight - 1;
    ::GpiMove(m_hPS, &vPoint[0]);
    lColor       = m_brush.GetColour().GetPixel();
    lBorderColor = m_pen.GetColour().GetPixel();
    if (m_brush.GetStyle() == wxTRANSPARENT)
        nIsTRANSPARENT = 1;
    if(lColor == lBorderColor || nIsTRANSPARENT)
    {
        lControl = DRO_OUTLINEFILL; //DRO_FILL;
        if(m_brush.GetStyle() == wxTRANSPARENT)
            lControl = DRO_OUTLINE;

        ::GpiSetColor(m_hPS, lBorderColor);
        ::GpiBox( m_hPS       // handle to a presentation space
                 ,lControl   // draw the box outline ? or ?
                 ,&vPoint[1]  // address of the corner
                 ,0L          // horizontal corner radius
                 ,0L          // vertical corner radius
                );
    }
    else
    {
        lControl = DRO_OUTLINE;
        ::GpiSetColor( m_hPS
                      ,lBorderColor
                     );
        ::GpiBox( m_hPS
                 ,lControl
                 ,&vPoint[1]
                 ,0L
                 ,0L
                );
        lControl = DRO_FILL;
        ::GpiSetColor( m_hPS
                      ,lColor
                     );
        vPoint[0].x = vX + 1;
        vPoint[0].y = vY + 1;
        vPoint[1].x = vX + vWidth - 2;
        vPoint[1].y = vY + vHeight - 2;
        ::GpiMove(m_hPS, &vPoint[0]);
        ::GpiBox( m_hPS
                 ,lControl
                 ,&vPoint[1]
                 ,0L
                 ,0L
                );
    }
    CalcBoundingBox(vX, vY);
    CalcBoundingBox(vX2, vY2);
} // end of wxDC::DoDrawRectangle

void wxDC::DoDrawRoundedRectangle(
  wxCoord                           vX
, wxCoord                           vY
, wxCoord                           vWidth
, wxCoord                           vHeight
, double                            dRadius
)
{
    POINTL                          vPoint[2];
    LONG                            lControl;
    LONG                            lColor;
    LONG                            lBorderColor;
    int                             nIsTRANSPARENT = 0;

    //
    // Might be a memory DC with no Paint rect.
    //
    if (!(m_vRclPaint.yTop == 0 &&
          m_vRclPaint.yBottom == 0 &&
          m_vRclPaint.xRight == 0 &&
          m_vRclPaint.xLeft == 0))
        vY = OS2Y(vY,vHeight);
    else
    {
        if (m_vSelectedBitmap.Ok())
        {
            m_vRclPaint.yTop = m_vSelectedBitmap.GetHeight();
            m_vRclPaint.xRight = m_vSelectedBitmap.GetWidth();
            vY = OS2Y(vY,vHeight);
        }
    }

    wxCoord                         vX2 = (vX + vWidth);
    wxCoord                         vY2 = (vY + vHeight);

    vPoint[0].x = vX;
    vPoint[0].y = vY;
    vPoint[1].x = vX + vWidth - 1;
    vPoint[1].y = vY + vHeight - 1;
    ::GpiMove(m_hPS, &vPoint[0]);

    lColor       = m_brush.GetColour().GetPixel();
    lBorderColor = m_pen.GetColour().GetPixel();
    lControl = DRO_OUTLINEFILL; //DRO_FILL;
    if (m_brush.GetStyle() == wxTRANSPARENT)
        nIsTRANSPARENT = 1;
    if(lColor == lBorderColor || nIsTRANSPARENT)
    {
        lControl = DRO_OUTLINEFILL; //DRO_FILL;
        if(m_brush.GetStyle() == wxTRANSPARENT)
            lControl = DRO_OUTLINE;

        ::GpiSetColor(m_hPS, lColor);
        ::GpiBox( m_hPS         // handle to a presentation space
                 ,lControl      // draw the box outline ? or ?
                 ,&vPoint[1]    // address of the corner
                 ,(LONG)dRadius // horizontal corner radius
                 ,(LONG)dRadius // vertical corner radius
                );
    }
    else
    {
        lControl = DRO_OUTLINE;
        ::GpiSetColor( m_hPS
                      ,lBorderColor
                     );
        ::GpiBox( m_hPS
                 ,lControl
                 ,&vPoint[1]
                 ,(LONG)dRadius
                 ,(LONG)dRadius
                );
        lControl = DRO_FILL;
        ::GpiSetColor( m_hPS
                      ,lColor
                     );
        vPoint[0].x = vX + 1;
        vPoint[0].y = vY + 1;
        vPoint[1].x = vX + vWidth - 2;
        vPoint[1].y = vY + vHeight - 2;
        ::GpiMove(m_hPS, &vPoint[0]);
        ::GpiBox( m_hPS
                 ,lControl
                 ,&vPoint[1]
                 ,(LONG)dRadius
                 ,(LONG)dRadius
                );
    }

    CalcBoundingBox(vX, vY);
    CalcBoundingBox(vX2, vY2);
} // end of wxDC::DoDrawRoundedRectangle

// Draw Ellipse within box (x,y) - (x+width, y+height)
void wxDC::DoDrawEllipse(
  wxCoord                           vX
, wxCoord                           vY
, wxCoord                           vWidth
, wxCoord                           vHeight
)
{
    POINTL                          vPtlPos; // Structure for current position
    FIXED                           vFxMult; // Multiplier for ellipse
    ARCPARAMS                       vArcp;   // Structure for arc parameters

    vY = OS2Y(vY,vHeight);

    vArcp.lR = 0;
    vArcp.lQ = vHeight/2;
    vArcp.lP = vWidth/2;
    vArcp.lS = 0;
    ::GpiSetArcParams( m_hPS
                      ,&vArcp
                     ); // Sets parameters to default
    vPtlPos.x = vX + vWidth/2;  // Loads x-coordinate
    vPtlPos.y = vY + vHeight/2; // Loads y-coordinate
    ::GpiMove( m_hPS
              ,&vPtlPos
             ); // Sets current position
    vFxMult = MAKEFIXED(1, 0);             /* Sets multiplier            */

    //
    // DRO_FILL, DRO_OTLINEFILL - where to get
    //
    ::GpiFullArc( m_hPS
                 ,DRO_OUTLINE
                 ,vFxMult
                ); // Draws full arc with center at current position

    wxCoord                         vX2 = (vX + vWidth);
    wxCoord                         vY2 = (vY + vHeight);

    CalcBoundingBox(vX, vY);
    CalcBoundingBox(vX2, vY2);
} // end of wxDC::DoDrawEllipse

void wxDC::DoDrawEllipticArc(
  wxCoord                           vX
, wxCoord                           vY
, wxCoord                           vWidth
, wxCoord                           vHeight
, double                            dSa
, double                            dEa
)
{
    POINTL                          vPtlPos; // Structure for current position
    FIXED                           vFxMult; // Multiplier for ellipse
    ARCPARAMS                       vArcp;   // Structure for arc parameters
    FIXED                           vFSa;
    FIXED                           vFSweepa; // Start angle, sweep angle
    double                          dIntPart;
    double                          dFractPart;

    vY = OS2Y(vY,vHeight);

    dFractPart = modf(dSa,&dIntPart);
    vFSa = MAKEFIXED((int)dIntPart, (int)(dFractPart * 0xffff) );
    dFractPart = modf(dEa - dSa, &dIntPart);
    vFSweepa = MAKEFIXED((int)dIntPart, (int)(dFractPart * 0xffff) );

    //
    // Ellipse main axis (r,q), (p,s) with center at (0,0)
    //
    vArcp.lR = 0;
    vArcp.lQ = vHeight/2;
    vArcp.lP = vWidth/2;
    vArcp.lS = 0;
    ::GpiSetArcParams(m_hPS, &vArcp); // Sets parameters to default
    vPtlPos.x = (wxCoord)(vX + vWidth/2  * (1. + cos(DegToRad(dSa)))); // Loads x-coordinate
    vPtlPos.y = (wxCoord)(vY + vHeight/2 * (1. + sin(DegToRad(dSa)))); // Loads y-coordinate
    ::GpiMove(m_hPS, &vPtlPos); // Sets current position

    //
    // May be not to the center ?
    //
    vPtlPos.x = vX + vWidth/2 ; // Loads x-coordinate
    vPtlPos.y = vY + vHeight/2; // Loads y-coordinate
    vFxMult = MAKEFIXED(1, 0);  // Sets multiplier

    //
    // DRO_FILL, DRO_OTLINEFILL - where to get
    //
    ::GpiPartialArc( m_hPS
                    ,&vPtlPos
                    ,vFxMult
                    ,vFSa
                    ,vFSweepa
                   );
    wxCoord                         vX2 = (vX + vWidth);
    wxCoord                         vY2 = (vY + vHeight);

    CalcBoundingBox(vX, vY);
    CalcBoundingBox(vX2, vY2);
} // end of wxDC::DoDrawEllipticArc

void wxDC::DoDrawIcon(
  const wxIcon&                     rIcon
, wxCoord                           vX
, wxCoord                           vY
)
{
    //
    // Need to copy back into a bitmap.  ::WinDrawPointer uses device coords
    // and I don't feel like figuring those out for scrollable windows so
    // just convert to a bitmap then let the DoDrawBitmap routine display it
    //
    if (rIcon.IsXpm())
    {
        DoDrawBitmap(rIcon.GetXpmSrc(), vX, vY, true);
    }
    else
    {
        wxBitmap                        vBitmap(rIcon);

        DoDrawBitmap(vBitmap, vX, vY, false);
    }
    CalcBoundingBox(vX, vY);
    CalcBoundingBox(vX + rIcon.GetWidth(), vY + rIcon.GetHeight());
} // end of wxDC::DoDrawIcon

void wxDC::DoDrawBitmap(
  const wxBitmap&                   rBmp
, wxCoord                           vX
, wxCoord                           vY
, bool                              bUseMask
)
{
#if wxUSE_PRINTING_ARCHITECTURE
    if (!IsKindOf(CLASSINFO(wxPrinterDC)))
#endif
    {
        HBITMAP                         hBitmap =  (HBITMAP)rBmp.GetHBITMAP();
        HBITMAP                         hBitmapOld = NULLHANDLE;
        POINTL                          vPoint[4];

        vY = OS2Y(vY,rBmp.GetHeight());

        vPoint[0].x = vX;
        vPoint[0].y = vY + rBmp.GetHeight();
        vPoint[1].x = vX + rBmp.GetWidth();
        vPoint[1].y = vY;
        vPoint[2].x = 0;
        vPoint[2].y = 0;
        vPoint[3].x = rBmp.GetWidth();
        vPoint[3].y = rBmp.GetHeight();
        if (bUseMask)
        {
            wxMask*                     pMask = rBmp.GetMask();

            if (pMask)
            {
                //
                // Need to imitate ::MaskBlt in windows.
                // 1) Extract the bits from from the bitmap.
                // 2) Extract the bits from the mask
                // 3) Using the mask bits do the following:
                //   A) If the mask byte is 00 leave the bitmap byte alone
                //   B) If the mask byte is FF copy the screen color into
                //      bitmap byte
                // 4) Create a new bitmap and set its bits to the above result
                // 5) Blit this to the screen PS
                //
                HBITMAP                 hMask = (HBITMAP)pMask->GetMaskBitmap();
                HBITMAP                 hOldMask   = NULLHANDLE;
                HBITMAP                 hOldBitmap = NULLHANDLE;
                HBITMAP                 hNewBitmap = NULLHANDLE;
                unsigned char*          pucBits;     // buffer that will contain the bitmap data
                unsigned char*          pucBitsMask; // buffer that will contain the mask data
                unsigned char*          pucData;     // pointer to use to traverse bitmap data
                unsigned char*          pucDataMask; // pointer to use to traverse mask data
                LONG                    lHits;
                ERRORID                 vError;
                wxString                sError;

                //
                // The usual Memory context creation stuff
                //
                DEVOPENSTRUC                    vDop  = {0L, "DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
                SIZEL                           vSize = {0, 0};
                HDC                             hDC   = ::DevOpenDC(vHabmain, OD_MEMORY, "*", 5L, (PDEVOPENDATA)&vDop, NULLHANDLE);
                HPS                             hPS   = ::GpiCreatePS(vHabmain, hDC, &vSize, PU_PELS | GPIA_ASSOC);

                //
                // The usual bitmap header stuff
                //
                BITMAPINFOHEADER2               vHeader;
                BITMAPINFO2                     vInfo;

                memset(&vHeader, '\0', 16);
                vHeader.cbFix           = 16;

                memset(&vInfo, '\0', 16);
                vInfo.cbFix           = 16;
                vInfo.cx              = (ULONG)rBmp.GetWidth();
                vInfo.cy              = (ULONG)rBmp.GetHeight();
                vInfo.cPlanes         = 1;
                vInfo.cBitCount       = 24; // Set to desired count going in

                //
                // Create the buffers for data....all wxBitmaps are 24 bit internally
                //
                int                     nBytesPerLine = rBmp.GetWidth() * 3;
                int                     nSizeDWORD    = sizeof(DWORD);
                int                     nLineBoundary = nBytesPerLine % nSizeDWORD;
                int                     nPadding = 0;
                int                     i;
                int                     j;
                LONG                    lScans = 0L;
                LONG                    lColor = 0L;

                //
                // Need to get a background color for mask blitting
                //
                if (IsKindOf(CLASSINFO(wxWindowDC)))
                {
                    wxWindowDC*             pWindowDC = wxDynamicCast(this, wxWindowDC);

                    lColor = pWindowDC->m_pCanvas->GetBackgroundColour().GetPixel();
                }
                else if (GetBrush().Ok())
                    lColor = GetBrush().GetColour().GetPixel();
                else
                    lColor = m_textBackgroundColour.GetPixel();

                //
                // Bitmap must be in a double-word aligned address so we may
                // have some padding to worry about
                //
                if (nLineBoundary > 0)
                {
                    nPadding     = nSizeDWORD - nLineBoundary;
                    nBytesPerLine += nPadding;
                }
                pucBits = (unsigned char *)malloc(nBytesPerLine * rBmp.GetHeight());
                pucBitsMask = (unsigned char *)malloc(nBytesPerLine * rBmp.GetHeight());
                memset(pucBits, '\0', (nBytesPerLine * rBmp.GetHeight()));
                memset(pucBitsMask, '\0', (nBytesPerLine * rBmp.GetHeight()));

                //
                // Extract the bitmap and mask data
                //
                if ((hOldBitmap = ::GpiSetBitmap(hPS, hBitmap)) == HBM_ERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }
                ::GpiQueryBitmapInfoHeader(hBitmap, &vHeader);
                vInfo.cBitCount = 24;
                if ((lScans = ::GpiQueryBitmapBits( hPS
                                                   ,0L
                                                   ,(LONG)rBmp.GetHeight()
                                                   ,(PBYTE)pucBits
                                                   ,&vInfo
                                                  )) == GPI_ALTERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }
                if ((hOldMask = ::GpiSetBitmap(hPS, hMask)) == HBM_ERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }
                ::GpiQueryBitmapInfoHeader(hMask, &vHeader);
                vInfo.cBitCount = 24;
                if ((lScans = ::GpiQueryBitmapBits( hPS
                                                   ,0L
                                                   ,(LONG)rBmp.GetHeight()
                                                   ,(PBYTE)pucBitsMask
                                                   ,&vInfo
                                                  )) == GPI_ALTERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }
                if (( hMask = ::GpiSetBitmap(hPS, hOldMask)) == HBM_ERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }

                //
                // Now set the bytes(bits) according to the mask values
                // 3 bytes per pel...must handle one at a time
                //
                pucData     = pucBits;
                pucDataMask = pucBitsMask;

                //
                // 16 bit kludge really only kinda works.  The mask gets applied
                // where needed but the original bitmap bits are dorked sometimes
                //
                bool                    bpp16 = (wxDisplayDepth() == 16);

                for (i = 0; i < rBmp.GetHeight(); i++)
                {
                    for (j = 0; j < rBmp.GetWidth(); j++)
                    {
                        // Byte 1
                        if (bpp16 && *pucDataMask == 0xF8) // 16 bit display gobblygook
                            pucData++;
                        else if (*pucDataMask == 0xFF) // leave bitmap byte alone
                            pucData++;
                        else
                        {
                            *pucData = ((unsigned char)(lColor >> 16));
                            pucData++;
                        }
                        // Byte 2
                        if (bpp16 && *(pucDataMask + 1) == 0xFC) // 16 bit display gobblygook
                            pucData++;
                        else if (*(pucDataMask + 1) == 0xFF) // leave bitmap byte alone
                            pucData++;
                        else
                        {
                            *pucData = ((unsigned char)(lColor >> 8));
                            pucData++;
                        }

                        // Byte 3
                        if (bpp16 && *(pucDataMask + 2) == 0xF8) // 16 bit display gobblygook
                            pucData++;
                        else if (*(pucDataMask + 2) == 0xFF) // leave bitmap byte alone
                            pucData++;
                        else
                        {
                            *pucData = ((unsigned char)lColor);
                            pucData++;
                        }
                        pucDataMask += 3;
                    }
                    for (j = 0; j < nPadding; j++)
                    {
                        pucData++;
                        pucDataMask++;
                    }
                }
                //
                // Create a new bitmap
                //
                vHeader.cx              = (ULONG)rBmp.GetWidth();
                vHeader.cy              = (ULONG)rBmp.GetHeight();
                vHeader.cPlanes         = 1L;
                vHeader.cBitCount       = 24;
                if ((hNewBitmap = ::GpiCreateBitmap( hPS
                                                    ,&vHeader
                                                    ,CBM_INIT
                                                    ,(PBYTE)pucBits
                                                    ,&vInfo
                                                   )) == GPI_ERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }

                //
                // Now blit it to the screen PS
                //
                if ((lHits = ::GpiWCBitBlt( (HPS)GetHPS()
                                           ,hNewBitmap
                                           ,4
                                           ,vPoint
                                           ,ROP_SRCCOPY
                                           ,BBO_IGNORE
                                          )) == GPI_ERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                }

                //
                // Clean up
                //
                free(pucBits);
                free(pucBitsMask);
                ::GpiSetBitmap(hPS, NULLHANDLE);
                ::GpiDeleteBitmap(hNewBitmap);
                ::GpiDestroyPS(hPS);
                ::DevCloseDC(hDC);
            }
        }
        else
        {
            ULONG                       lOldForeGround = ::GpiQueryColor((HPS)GetHPS());
            ULONG                       lOldBackGround = ::GpiQueryBackColor((HPS)GetHPS());

            if (m_textForegroundColour.Ok())
            {
                ::GpiSetColor( (HPS)GetHPS()
                               ,m_textForegroundColour.GetPixel()
                              );
            }
            if (m_textBackgroundColour.Ok())
            {
                ::GpiSetBackColor( (HPS)GetHPS()
                                  ,m_textBackgroundColour.GetPixel()
                                 );
            }
            //
            // Need to alter bits in a mono bitmap to match the new
            // background-foreground if it is different.
            //
            if (rBmp.IsMono() &&
               ((m_textForegroundColour.GetPixel() != lOldForeGround) ||
                (m_textBackgroundColour.GetPixel() != lOldBackGround)))
            {
                DEVOPENSTRUC        vDop  = {0L, "DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
                SIZEL               vSize = {0, 0};
                HDC                 hDC   = ::DevOpenDC(vHabmain, OD_MEMORY, "*", 5L, (PDEVOPENDATA)&vDop, NULLHANDLE);
                HPS                 hPS   = ::GpiCreatePS(vHabmain, hDC, &vSize, PU_PELS | GPIA_ASSOC);

                int                 nBytesPerLine = rBmp.GetWidth() * 3;
                int                 i, j;
                LONG                lForeGround = m_textForegroundColour.GetPixel();
                LONG                lBackGround = m_textBackgroundColour.GetPixel();
                LONG                lScans;
                HBITMAP             hOldBitmap = NULLHANDLE;
                BITMAPINFO2         vInfo;
                ERRORID             vError;
                wxString            sError;


                memset(&vInfo, '\0', 16);
                vInfo.cbFix           = 16;
                vInfo.cx              = (ULONG)rBmp.GetWidth();
                vInfo.cy              = (ULONG)rBmp.GetHeight();
                vInfo.cPlanes         = 1;
                vInfo.cBitCount       = 24;

                unsigned char*          pucBits;     // buffer that will contain the bitmap data
                unsigned char*          pucData;     // pointer to use to traverse bitmap data

                pucBits = new unsigned char[nBytesPerLine * rBmp.GetHeight()];
                memset(pucBits, '\0', (nBytesPerLine * rBmp.GetHeight()));

                if ((hOldBitmap = ::GpiSetBitmap(hPS, hBitmap)) == HBM_ERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                    return;
                }
                if ((lScans = ::GpiQueryBitmapBits( hPS
                                                   ,0L
                                                   ,(LONG)rBmp.GetHeight()
                                                   ,(PBYTE)pucBits
                                                   ,&vInfo
                                                  )) == GPI_ALTERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                    return;
                }
                unsigned char           cOldRedFore   = (unsigned char)(lOldForeGround >> 16);
                unsigned char           cOldGreenFore = (unsigned char)(lOldForeGround >> 8);
                unsigned char           cOldBlueFore  = (unsigned char)lOldForeGround;

                unsigned char           cRedFore   = (unsigned char)(lForeGround >> 16);
                unsigned char           cGreenFore = (unsigned char)(lForeGround >> 8);
                unsigned char           cBlueFore  = (unsigned char)lForeGround;

                unsigned char           cRedBack   = (unsigned char)(lBackGround >> 16);
                unsigned char           cGreenBack = (unsigned char)(lBackGround >> 8);
                unsigned char           cBlueBack  = (unsigned char)lBackGround;

                pucData = pucBits;
                for (i = 0; i < rBmp.GetHeight(); i++)
                {
                    for (j = 0; j < rBmp.GetWidth(); j++)
                    {
                        unsigned char    cBmpRed   = *pucData;
                        unsigned char    cBmpGreen = *(pucData + 1);
                        unsigned char    cBmpBlue  = *(pucData + 2);

                        if ((cBmpRed == cOldRedFore) &&
                            (cBmpGreen == cOldGreenFore) &&
                            (cBmpBlue == cOldBlueFore))
                        {
                            *pucData = cBlueFore;
                            pucData++;
                            *pucData = cGreenFore;
                            pucData++;
                            *pucData = cRedFore;
                            pucData++;
                        }
                        else
                        {
                            *pucData = cBlueBack;
                            pucData++;
                            *pucData = cGreenBack;
                            pucData++;
                            *pucData = cRedBack;
                            pucData++;
                        }
                    }
                }
                if ((lScans = ::GpiSetBitmapBits( hPS
                                                 ,0L
                                                 ,(LONG)rBmp.GetHeight()
                                                 ,(PBYTE)pucBits
                                                 ,&vInfo
                                                )) == GPI_ALTERROR)
                {
                    vError = ::WinGetLastError(vHabmain);
                    sError = wxPMErrorToStr(vError);
                    return;
                }
                delete [] pucBits;
                ::GpiSetBitmap(hPS, NULLHANDLE);
                ::GpiDestroyPS(hPS);
                ::DevCloseDC(hDC);
            }
            ::GpiWCBitBlt( (HPS)GetHPS()
                          ,hBitmap
                          ,4
                          ,vPoint
                          ,ROP_SRCCOPY
                          ,BBO_IGNORE
                         );
            ::GpiSetBitmap((HPS)GetHPS(), hBitmapOld);
            ::GpiSetColor((HPS)GetHPS(), lOldForeGround);
            ::GpiSetBackColor((HPS)GetHPS(), lOldBackGround);
        }
    }
} // end of wxDC::DoDrawBitmap

void wxDC::DoDrawText(
  const wxString&                   rsText
, wxCoord                           vX
, wxCoord                           vY
)
{
    wxCoord                         vWidth;
    wxCoord                         vHeight;

    DrawAnyText( rsText
                ,vX
                ,vY
               );

    CalcBoundingBox(vX, vY);
    GetTextExtent(rsText, &vWidth, &vHeight);
    CalcBoundingBox((vX + vWidth), (vY + vHeight));
} // end of wxDC::DoDrawText

void wxDC::DrawAnyText( const wxString& rsText,
                        wxCoord vX,
                        wxCoord vY )
{
    int                             nOldBackground = 0;
    POINTL                          vPtlStart;
    LONG                            lHits;
    wxCoord                         vTextX = 0;
    wxCoord                         vTextY = 0;

    //
    // prepare for drawing the text
    //

    //
    // Set text color attributes
    //
    if (m_textForegroundColour.Ok())
    {
        SetTextColor( m_hPS
                     ,(int)m_textForegroundColour.GetPixel()
                    );
    }

    if (m_textBackgroundColour.Ok())
    {
        nOldBackground = SetTextBkColor( m_hPS
                                        ,(int)m_textBackgroundColour.GetPixel()
                                       );
    }
    SetBkMode( m_hPS
              ,m_backgroundMode
             );
    GetTextExtent( rsText
                  ,&vTextX
                  ,&vTextY
                 );
    vPtlStart.x = vX;
    if (!(m_vRclPaint.yTop == 0 &&
          m_vRclPaint.yBottom == 0 &&
          m_vRclPaint.xRight == 0 &&
          m_vRclPaint.xLeft == 0))
    {
        vPtlStart.y = OS2Y(vY,vTextY);
    }
    else
    {
        if (m_vSelectedBitmap.Ok())
        {
            m_vRclPaint.yTop = m_vSelectedBitmap.GetHeight();
            m_vRclPaint.xRight = m_vSelectedBitmap.GetWidth();
            vPtlStart.y = OS2Y(vY,vTextY);
        }
        else
            vPtlStart.y = vY;
    }

    PCH pzStr = (PCH)rsText.c_str();

    ::GpiMove(m_hPS, &vPtlStart);
    lHits = ::GpiCharString( m_hPS
                            ,rsText.length()
                            ,pzStr
                           );
    if (lHits != GPI_OK)
    {
        wxLogLastError(wxT("TextOut"));
    }

    //
    // Restore the old parameters (text foreground colour may be left because
    // it never is set to anything else, but background should remain
    // transparent even if we just drew an opaque string)
    //
    if (m_textBackgroundColour.Ok())
            SetTextBkColor( m_hPS
                           ,nOldBackground
                          );
    SetBkMode( m_hPS
              ,wxTRANSPARENT
             );
}

void wxDC::DoDrawRotatedText(
  const wxString&                   rsText
, wxCoord                           vX
, wxCoord                           vY
, double                            dAngle
)
{
    if (dAngle == 0.0)
    {
        DoDrawText( rsText
                   ,vX
                   ,vY
                  );
    }

   // TODO:
   /*
    if ( angle == 0.0 )
    {
        DoDrawText(text, x, y);
    }
    else
    {
        LOGFONT lf;
        wxFillLogFont(&lf, &m_font);

        // GDI wants the angle in tenth of degree
        long angle10 = (long)(angle * 10);
        lf.lfEscapement = angle10;
        lf. lfOrientation = angle10;

        HFONT hfont = ::CreateFontIndirect(&lf);
        if ( !hfont )
        {
            wxLogLastError("CreateFont");
        }
        else
        {
            HFONT hfontOld = ::SelectObject(GetHdc(), hfont);

            DrawAnyText(text, x, y);

            (void)::SelectObject(GetHdc(), hfontOld);
        }

        // call the bounding box by adding all four vertices of the rectangle
        // containing the text to it (simpler and probably not slower than
        // determining which of them is really topmost/leftmost/...)
        wxCoord w, h;
        GetTextExtent(text, &w, &h);

        double rad = DegToRad(angle);

        // "upper left" and "upper right"
        CalcBoundingBox(x, y);
        CalcBoundingBox(x + w*cos(rad), y - h*sin(rad));
        CalcBoundingBox(x + h*sin(rad), y + h*cos(rad));

        // "bottom left" and "bottom right"
        x += (wxCoord)(h*sin(rad));
        y += (wxCoord)(h*cos(rad));
        CalcBoundingBox(x, y);
        CalcBoundingBox(x + h*sin(rad), y + h*cos(rad));
    }
*/
}

// ---------------------------------------------------------------------------
// set GDI objects
// ---------------------------------------------------------------------------

void wxDC::DoSelectPalette( bool WXUNUSED(bRealize) )
{
    //
    // Set the old object temporarily, in case the assignment deletes an object
    // that's not yet selected out.
    //
    if (m_hOldPalette)
    {
        m_hOldPalette = 0;
    }

    if (m_palette.Ok())
    {
        HPALETTE                    hOldPal;

        hOldPal = ::GpiSelectPalette((HDC) m_hPS, (HPALETTE) m_palette.GetHPALETTE());
        if (!m_hOldPalette)
            m_hOldPalette = (WXHPALETTE)hOldPal;
    }
} // end of wxDC::DoSelectPalette

void wxDC::InitializePalette()
{
    if (wxDisplayDepth() <= 8 )
    {
        //
        // Look for any window or parent that has a custom palette. If any has
        // one then we need to use it in drawing operations
        //
        wxWindow*                   pWin = m_pCanvas->GetAncestorWithCustomPalette();

        m_hasCustomPalette = pWin && pWin->HasCustomPalette();
        if (m_hasCustomPalette)
        {
            m_palette = pWin->GetPalette();

            //
            // turn on PM translation for this palette
            //
            DoSelectPalette();
        }
    }
} // end of wxDC::InitializePalette

void wxDC::SetPalette(
  const wxPalette&                  rPalette
)
{
    if (m_hOldFont)
    {
        m_hOldFont = 0;
    }
    m_palette = rPalette;
    if (!rPalette.Ok())
    {
        if (m_hOldFont)
        {
            m_hOldFont = 0;
        }
    }
    HPALETTE                    hOldPal = ::GpiSelectPalette((HDC) m_hPS, (HPALETTE) m_palette.GetHPALETTE());
    if (!m_hOldPalette)
        m_hOldPalette = (WXHPALETTE)hOldPal;
} // end of wxDC::SetPalette

void wxDC::SetFont(
  const wxFont&                     rFont
)
{
    //
    // Set the old object temporarily, in case the assignment deletes an object
    // that's not yet selected out.
    //
    if (m_hOldFont)
    {
        m_hOldFont = 0;
    }
    m_font = rFont;
    if (!rFont.Ok())
    {
        m_hOldFont = 0;
    }

    m_font.SetPS(m_hPS); // this will realize the font

    if (m_font.Ok())
    {
        HFONT                       hFont = m_font.GetResourceHandle();
        if (hFont == (HFONT) NULL)
        {
            wxLogDebug(wxT("::SelectObject failed in wxDC::SetFont."));
        }
        if (!m_hOldFont)
            m_hOldFont = (WXHFONT) hFont;
    }
} // end of wxDC::SetFont

void wxDC::SetPen(
  const wxPen&                      rPen
)
{
    wxCHECK_RET( Ok(), wxT("invalid window dc") );

    if (m_pen == rPen)
        return;
    m_pen = rPen;
    if (!m_pen.Ok())
        return;

    if (m_hOldPen)
        m_hOldPen = 0L;
    m_pen = rPen;

    if (!m_pen.Ok())
    {
        if (m_hOldPen)
        {
            m_pen.SetPS((HPS)m_hOldPen);
        }
        m_hOldPen = 0L;
    }

    if (m_pen.Ok())
    {
        if (m_pen.GetResourceHandle())
        {
            m_pen.SetPS(m_hPS);
            if (!m_hOldPen)
                m_hOldPen = m_pen.GetPS();
        }
        ::GpiSetColor(m_hPS, m_pen.GetColour().GetPixel());
    }
}

void wxDC::SetBrush(
  const wxBrush&                    rBrush
)
{
    wxCHECK_RET( Ok(), wxT("invalid window dc") );

    if (m_hOldBrush)
        m_hOldBrush = 0L;
    m_brush = rBrush;
    if (!m_brush.Ok())
    if (m_brush == rBrush)
        return;
    if (!m_brush.Ok())
        if (m_hOldBrush)
            m_hOldBrush = 0L;

    if (!m_brush.Ok())
    {
        if (m_hOldBrush)
        {
            m_brush.SetPS((HPS)m_hOldBrush);
        }
        m_hOldBrush = 0L;
    }

    if (m_brush.Ok())
    {
        if (m_brush.GetResourceHandle())
        {
            m_brush.SetPS(m_hPS);
            if (!m_hOldBrush)
                m_hOldBrush = (WXHWND)m_brush.GetPS();
        }
    }
} // end of wxDC::SetBrush

void wxDC::SetBackground(const wxBrush& rBrush)
{
    m_backgroundBrush = rBrush;

    if (m_backgroundBrush.Ok())
    {
        (void)::GpiSetBackColor((HPS)m_hPS, m_backgroundBrush.GetColour().GetPixel());
    }
} // end of wxDC::SetBackground

void wxDC::SetBackgroundMode(int nMode)
{
    m_backgroundMode = nMode;
} // end of wxDC::SetBackgroundMode

void wxDC::SetLogicalFunction(int nFunction)
{
    m_logicalFunction = nFunction;
    SetRop((WXHDC)m_hDC);
} // wxDC::SetLogicalFunction

void wxDC::SetRop(WXHDC hDC)
{
    if (!hDC || m_logicalFunction < 0)
        return;

    LONG lCRop;
    switch (m_logicalFunction)
    {
        case wxXOR:
            lCRop = FM_XOR;
            break;

        case wxINVERT:
            lCRop = FM_INVERT;
            break;

        case wxOR_REVERSE:
            lCRop = FM_MERGESRCNOT;
            break;

        case wxAND_REVERSE:
            lCRop = FM_NOTMASKSRC;
            break;

        case wxCLEAR:
            lCRop = FM_ONE;
            break;

        case wxSET:
            lCRop = FM_ZERO;
            break;

        case wxSRC_INVERT:
            lCRop = FM_MERGENOTSRC;
            break;

        case wxOR_INVERT:
            lCRop = FM_MERGESRCNOT;
            break;

        case wxAND:
            lCRop = FM_AND;
            break;

        case wxOR:
            lCRop = FM_OR;
            break;

        case wxAND_INVERT:
            lCRop = FM_SUBTRACT;
            break;

        case wxEQUIV:
        case wxNAND:
        case wxCOPY:
        default:
            lCRop = FM_OVERPAINT;
            break;
    }
    ::GpiSetMix((HPS)hDC, lCRop);
} // end of wxDC::SetRop

bool wxDC::StartDoc( const wxString& WXUNUSED(rsMessage) )
{
    // We might be previewing, so return true to let it continue.
    return true;
} // end of wxDC::StartDoc

void wxDC::EndDoc()
{
} // end of wxDC::EndDoc

void wxDC::StartPage()
{
} // end of wxDC::StartPage

void wxDC::EndPage()
{
} // end of wxDC::EndPage

// ---------------------------------------------------------------------------
// text metrics
// ---------------------------------------------------------------------------

wxCoord wxDC::GetCharHeight() const
{
    FONTMETRICS                     vFM; // metrics structure

    ::GpiQueryFontMetrics( m_hPS
                          ,sizeof(FONTMETRICS)
                          ,&vFM
                         );
    return YDEV2LOGREL(vFM.lXHeight);
}

wxCoord wxDC::GetCharWidth() const
{
    FONTMETRICS                     vFM; // metrics structure

    ::GpiQueryFontMetrics( m_hPS
                          ,sizeof(FONTMETRICS)
                          ,&vFM
                         );
    return XDEV2LOGREL(vFM.lAveCharWidth);
}

void wxDC::DoGetTextExtent(
  const wxString&                   rsString
, wxCoord*                          pvX
, wxCoord*                          pvY
, wxCoord*                          pvDescent
, wxCoord*                          pvExternalLeading
, wxFont*                           pTheFont
) const
{
    POINTL                          avPoint[TXTBOX_COUNT];
    POINTL                          vPtMin;
    POINTL                          vPtMax;
    int                             i;
    int                             l;
    FONTMETRICS                     vFM; // metrics structure
    BOOL                            bRc;
    ERRORID                         vErrorCode; // last error id code
    wxFont*                         pFontToUse = (wxFont*)pTheFont;

    wxChar                          zMsg[128]; // DEBUG
    wxString                        sError;

    if (!pFontToUse)
        pFontToUse = (wxFont*)&m_font;
    l = rsString.length();

    //
    // In world coordinates.
    //
    bRc = ::GpiQueryTextBox( m_hPS
                            ,l
                            ,(PCH)rsString.c_str()
                            ,TXTBOX_COUNT // return maximum information
                            ,avPoint      // array of coordinates points
                           );
    if(!bRc)
    {
       vErrorCode = ::WinGetLastError(wxGetInstance());
       sError = wxPMErrorToStr(vErrorCode);
       // DEBUG
       wxSprintf(zMsg, _T("GpiQueryTextBox for %s: failed with Error: %lx - %s"), rsString.c_str(), vErrorCode, sError.c_str());
       (void)wxMessageBox( _T("wxWidgets Menu sample")
                          ,zMsg
                          ,wxICON_INFORMATION
                         );
    }

    vPtMin.x = avPoint[0].x;
    vPtMax.x = avPoint[0].x;
    vPtMin.y = avPoint[0].y;
    vPtMax.y = avPoint[0].y;
    for (i = 1; i < 4; i++)
    {
        if(vPtMin.x > avPoint[i].x) vPtMin.x = avPoint[i].x;
        if(vPtMin.y > avPoint[i].y) vPtMin.y = avPoint[i].y;
        if(vPtMax.x < avPoint[i].x) vPtMax.x = avPoint[i].x;
        if(vPtMax.y < avPoint[i].y) vPtMax.y = avPoint[i].y;
    }
    ::GpiQueryFontMetrics( m_hPS
                          ,sizeof(FONTMETRICS)
                          ,&vFM
                         );

    if (pvX)
        *pvX = (wxCoord)(vPtMax.x - vPtMin.x + 1);
    if (pvY)
        *pvY = (wxCoord)(vPtMax.y - vPtMin.y + 1);
    if (pvDescent)
        *pvDescent = vFM.lMaxDescender;
    if (pvExternalLeading)
        *pvExternalLeading = vFM.lExternalLeading;
}

void wxDC::SetMapMode(
  int                               nMode
)
{
    int                             nPixelWidth = 0;
    int                             nPixelHeight = 0;
    int                             nMmWidth = 1;
    int                             nMmHeight = 1;
    LONG                            lArray[CAPS_VERTICAL_RESOLUTION+1];

    m_mappingMode = nMode;

    if(::DevQueryCaps( m_hDC
                      ,CAPS_FAMILY                  // id of first item
                      ,CAPS_VERTICAL_RESOLUTION+1   // number of items wanted
                      ,lArray
                     ))
    {
        LONG                        lHorzRes;
        LONG                        lVertRes;

        nPixelWidth    = lArray[CAPS_WIDTH];
        nPixelHeight   = lArray[CAPS_HEIGHT];
        lHorzRes  = lArray[CAPS_HORIZONTAL_RESOLUTION]; // returns pel/meter
        lVertRes  = lArray[CAPS_VERTICAL_RESOLUTION];   // returns pel/meter
        nMmWidth  = (lHorzRes/1000) * nPixelWidth;
        nMmHeight = (lVertRes/1000) * nPixelHeight;
    }
    if ((nPixelWidth == 0) || (nPixelHeight == 0) || (nMmWidth == 0) || (nMmHeight == 0))
    {
        return;
    }

    double dMm2pixelsX = nPixelWidth/(double)nMmWidth;
    double dMm2pixelsY = nPixelHeight/(double)nMmHeight;

    switch (nMode)
    {
        case wxMM_TWIPS:
            m_logicalScaleX = (twips2mm * dMm2pixelsX);
            m_logicalScaleY = (twips2mm * dMm2pixelsY);
            break;

        case wxMM_POINTS:
            m_logicalScaleX = (pt2mm * dMm2pixelsX);
            m_logicalScaleY = (pt2mm * dMm2pixelsY);
            break;

        case wxMM_METRIC:
            m_logicalScaleX = dMm2pixelsX;
            m_logicalScaleY = dMm2pixelsY;
            break;

        case wxMM_LOMETRIC:
            m_logicalScaleX = (dMm2pixelsX/10.0);
            m_logicalScaleY = (dMm2pixelsY/10.0);
            break;

        case wxMM_TEXT:
        default:
            m_logicalScaleX = 1.0;
            m_logicalScaleY = 1.0;
            break;
    }

    SIZEL                           vSize;
    ULONG                           ulOptions;

    ulOptions = ::GpiQueryPS(m_hPS, &vSize);
    if (!ulOptions & PU_ARBITRARY)
    {
        ulOptions = PU_ARBITRARY | GPIF_DEFAULT;
        ::GpiSetPS(m_hPS, &vSize, ulOptions);
    }
}; // end of wxDC::SetMapMode

void wxDC::SetUserScale( double dX,
                         double dY )
{
    m_userScaleX = dX;
    m_userScaleY = dY;

    SetMapMode(m_mappingMode);
} // end of wxDC::SetUserScale

void wxDC::SetAxisOrientation( bool bXLeftRight,
                               bool bYBottomUp )
{
    m_signX = bXLeftRight ? 1 : -1;
    m_signY = bYBottomUp ? -1 : 1;

    SetMapMode(m_mappingMode);
} // end of wxDC::SetAxisOrientation

void wxDC::SetSystemScale(
  double                            dX
, double                            dY
)
{
    m_scaleX = dX;
    m_scaleY = dY;

    SetMapMode(m_mappingMode);
} // end of wxDC::SetSystemScale

void wxDC::SetLogicalOrigin(
  wxCoord                           vX
, wxCoord                           vY
)
{
    RECTL                           vRect;

    ::GpiQueryPageViewport( m_hPS
                           ,&vRect
                          );
    vRect.xRight -= vX;
    vRect.yTop += vY;
    vRect.xLeft = vX;
    vRect.yBottom = vY;
    ::GpiSetPageViewport( m_hPS
                         ,&vRect
                        );
}; // end of wxDC::SetLogicalOrigin

void wxDC::SetDeviceOrigin(
  wxCoord                           vX
, wxCoord                           vY
)
{
    RECTL                           vRect;

    m_deviceOriginX = vX;
    m_deviceOriginY = vY;
    ::GpiQueryPageViewport( m_hPS
                           ,&vRect
                          );
    vRect.xLeft += vX;
    vRect.xRight += vX;
    vRect.yBottom -= vY;
    vRect.yTop -= vY;
    ::GpiSetPageViewport( m_hPS
                         ,&vRect
                        );
}; // end of wxDC::SetDeviceOrigin

// ---------------------------------------------------------------------------
// coordinates transformations
// ---------------------------------------------------------------------------

wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
{
    return (wxCoord) (((x) - m_deviceOriginX)/(m_logicalScaleX*m_userScaleX*m_signX*m_scaleX) - m_logicalOriginX);
}

wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
{
    // axis orientation is not taken into account for conversion of a distance
    return (wxCoord) ((x)/(m_logicalScaleX*m_userScaleX*m_scaleX));
}

wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
{
    return (wxCoord) (((y) - m_deviceOriginY)/(m_logicalScaleY*m_userScaleY*m_signY*m_scaleY) - m_logicalOriginY);
}

wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
{
    // axis orientation is not taken into account for conversion of a distance
    return (wxCoord) ((y)/(m_logicalScaleY*m_userScaleY*m_scaleY));
}

wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
{
    return (wxCoord) ((x - m_logicalOriginX)*m_logicalScaleX*m_userScaleX*m_signX*m_scaleX + m_deviceOriginX);
}

wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
{
    // axis orientation is not taken into account for conversion of a distance
    return (wxCoord) (x*m_logicalScaleX*m_userScaleX*m_scaleX);
}

wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
{
    return (wxCoord) ((y - m_logicalOriginY)*m_logicalScaleY*m_userScaleY*m_signY*m_scaleY + m_deviceOriginY);
}

wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
{
    // axis orientation is not taken into account for conversion of a distance
    return (wxCoord) (y*m_logicalScaleY*m_userScaleY*m_scaleY);
}

// ---------------------------------------------------------------------------
// bit blit
// ---------------------------------------------------------------------------

bool wxDC::DoBlit( wxCoord vXdest,
                   wxCoord vYdest,
                   wxCoord vWidth,
                   wxCoord vHeight,
                   wxDC*   pSource,
                   wxCoord vXsrc,
                   wxCoord vYsrc,
                   int     nRop,
                   bool    bUseMask,
                   wxCoord WXUNUSED(vXsrcMask),
                   wxCoord WXUNUSED(vYsrcMask) )
{
    wxMask*                         pMask = NULL;
    CHARBUNDLE                      vCbnd;
    COLORREF                        vOldTextColor;
    COLORREF                        vOldBackground = ::GpiQueryBackColor(m_hPS);

    if (bUseMask)
    {
        const wxBitmap&             rBmp = pSource->m_vSelectedBitmap;

        pMask = rBmp.GetMask();
        if (!(rBmp.Ok() && pMask && pMask->GetMaskBitmap()))
        {
            bUseMask = false;
        }
    }

    ::GpiQueryAttrs( m_hPS
                    ,PRIM_CHAR
                    ,CBB_COLOR
                    ,&vCbnd
                   );
    vOldTextColor = (COLORREF)vCbnd.lColor;

    if (m_textForegroundColour.Ok())
    {
        vCbnd.lColor = (LONG)m_textForegroundColour.GetPixel();
        ::GpiSetAttrs( m_hPS           // presentation-space handle
                      ,PRIM_CHAR       // Char primitive.
                      ,CBB_COLOR       // sets color.
                      ,0
                      ,&vCbnd          // buffer for attributes.
                     );
    }
    if (m_textBackgroundColour.Ok())
    {
        ::GpiSetBackColor(m_hPS, (LONG)m_textBackgroundColour.GetPixel());
    }

    LONG                            lRop = ROP_SRCCOPY;

    switch (nRop)
    {
        case wxXOR:          lRop = ROP_SRCINVERT;        break;
        case wxINVERT:       lRop = ROP_DSTINVERT;        break;
        case wxOR_REVERSE:   lRop = 0x00DD0228;           break;
        case wxAND_REVERSE:  lRop = ROP_SRCERASE;         break;
        case wxCLEAR:        lRop = ROP_ZERO;             break;
        case wxSET:          lRop = ROP_ONE;              break;
        case wxOR_INVERT:    lRop = ROP_MERGEPAINT;       break;
        case wxAND:          lRop = ROP_SRCAND;           break;
        case wxOR:           lRop = ROP_SRCPAINT;         break;
        case wxEQUIV:        lRop = 0x00990066;           break;
        case wxNAND:         lRop = 0x007700E6;           break;
        case wxAND_INVERT:   lRop = 0x00220326;           break;
        case wxCOPY:         lRop = ROP_SRCCOPY;          break;
        case wxNO_OP:        lRop = ROP_NOTSRCERASE;      break;
        case wxSRC_INVERT:   lRop = ROP_SRCINVERT;        break;
        case wxNOR:          lRop = ROP_NOTSRCCOPY;       break;
        default:
           wxFAIL_MSG( wxT("unsupported logical function") );
           return false;
    }

    bool                            bSuccess;

    if (bUseMask)
    {
        //
        // Blit bitmap with mask
        //

        //
        // Create a temp buffer bitmap and DCs/PSs to access it and the mask
        //
        HDC                             hDCMask;
        HDC                             hDCBuffer;
        HPS                             hPSMask;
        HPS                             hPSBuffer;
        DEVOPENSTRUC                    vDOP = {0L, "DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
        BITMAPINFOHEADER2               vBmpHdr;
        HBITMAP                         hBufBitmap;
        SIZEL                           vSize = {0, 0};
        LONG                            rc;

        memset(&vBmpHdr, 0, sizeof(BITMAPINFOHEADER2));
        vBmpHdr.cbFix     = sizeof(BITMAPINFOHEADER2);
        vBmpHdr.cx        = vWidth;
        vBmpHdr.cy        = vHeight;
        vBmpHdr.cPlanes   = 1;
        vBmpHdr.cBitCount = 24;

#if wxUSE_DC_CACHEING
        {
            //
            // create a temp buffer bitmap and DCs to access it and the mask
            //
            wxDCCacheEntry*         pDCCacheEntry1    = FindDCInCache( NULL
                                                                      ,pSource->GetHPS()
                                                                     );
            wxDCCacheEntry*         pDCCacheEntry2    = FindDCInCache( pDCCacheEntry1
                                                                      ,GetHPS()
                                                                     );
            wxDCCacheEntry*         pBitmapCacheEntry = FindBitmapInCache( GetHPS()
                                                                          ,vWidth
                                                                          ,vHeight
                                                                         );

            hPSMask = pDCCacheEntry1->m_hPS;
            hDCBuffer = (HDC)pDCCacheEntry2->m_hPS;
            hBufBitmap = (HBITMAP)pBitmapCacheEntry->m_hBitmap;
            wxUnusedVar(hDCMask);
        }
#else
        {
            hDCMask = ::DevOpenDC(vHabmain, OD_MEMORY, "*", 5L, (PDEVOPENDATA)&vDOP, NULLHANDLE);
            hDCBuffer = ::DevOpenDC(vHabmain, OD_MEMORY, "*", 5L, (PDEVOPENDATA)&vDOP, NULLHANDLE);
            hPSMask = ::GpiCreatePS(vHabmain, hDCMask, &vSize, PU_PELS | GPIT_MICRO | GPIA_ASSOC);
            hPSBuffer = ::GpiCreatePS(vHabmain, hDCBuffer, &vSize, PU_PELS | GPIT_MICRO | GPIA_ASSOC);
            hBufBitmap = ::GpiCreateBitmap(GetHPS(), &vBmpHdr, 0L, NULL, NULL);
        }
#endif

        POINTL                          aPoint1[4] = { {0, 0}
                              ,{vWidth, vHeight}
                              ,{vXdest, vYdest}
                              ,{vXdest + vWidth, vYdest + vHeight}
                                                     };
        POINTL                          aPoint2[4] = { {0, 0}
                              ,{vWidth, vHeight}
                              ,{vXsrc, vYsrc}
                              ,{vXsrc + vWidth, vYsrc + vHeight}
                                                     };
        POINTL                          aPoint3[4] = { {vXdest, vYdest}
                              ,{vXdest + vWidth, vYdest + vHeight}
                              ,{vXsrc, vYsrc}
                              ,{vXsrc + vWidth, vYsrc + vHeight}
                                                     };
        POINTL                          aPoint4[4] = { {vXdest, vYdest}
                              ,{vXdest + vWidth, vYdest + vHeight}
                              ,{0, 0}
                              ,{vWidth, vHeight}
                                                     };
        ::GpiSetBitmap(hPSMask, (HBITMAP) pMask->GetMaskBitmap());
        ::GpiSetBitmap(hPSBuffer, (HBITMAP) hBufBitmap);

        //
        // Copy dest to buffer
        //
        rc = ::GpiBitBlt( hPSBuffer
                         ,GetHPS()
                         ,4L
                         ,aPoint1
                         ,ROP_SRCCOPY
                         ,BBO_IGNORE
                        );
        if (rc == GPI_ERROR)
        {
            wxLogLastError(wxT("BitBlt"));
        }

        //
        // Copy src to buffer using selected raster op
        //
        rc = ::GpiBitBlt( hPSBuffer
                         ,GetHPS()
                         ,4L
                         ,aPoint2
                         ,lRop
                         ,BBO_IGNORE
                        );
        if (rc == GPI_ERROR)
        {
            wxLogLastError(wxT("BitBlt"));
        }

        //
        // Set masked area in buffer to BLACK (pixel value 0)
        //
        COLORREF                        vPrevBkCol = ::GpiQueryBackColor(GetHPS());
        COLORREF                        vPrevCol = ::GpiQueryColor(GetHPS());

        ::GpiSetBackColor(GetHPS(), OS2RGB(255, 255, 255));
        ::GpiSetColor(GetHPS(), OS2RGB(0, 0, 0));

        rc = ::GpiBitBlt( hPSBuffer
                         ,hPSMask
                         ,4L
                         ,aPoint2
                         ,ROP_SRCAND
                         ,BBO_IGNORE
                        );
        if (rc == GPI_ERROR)
        {
            wxLogLastError(wxT("BitBlt"));
        }

        //
        // Set unmasked area in dest to BLACK
        //
        ::GpiSetBackColor(GetHPS(), OS2RGB(0, 0, 0));
        ::GpiSetColor(GetHPS(), OS2RGB(255, 255, 255));
        rc = ::GpiBitBlt( GetHPS()
                         ,hPSMask
                         ,4L
                         ,aPoint3
                         ,ROP_SRCAND
                         ,BBO_IGNORE
                        );
        if (rc == GPI_ERROR)
        {
            wxLogLastError(wxT("BitBlt"));
        }

        //
        // Restore colours to original values
        //
        ::GpiSetBackColor(GetHPS(), vPrevBkCol);
        ::GpiSetColor(GetHPS(), vPrevCol);

        //
        // OR buffer to dest
        //
        rc = ::GpiBitBlt( GetHPS()
                         ,hPSMask
                         ,4L
                         ,aPoint4
                         ,ROP_SRCPAINT
                         ,BBO_IGNORE
                        );
        if (rc == GPI_ERROR)
        {
            bSuccess = false;
            wxLogLastError(wxT("BitBlt"));
        }

        //
        // Tidy up temporary DCs and bitmap
        //
        ::GpiSetBitmap(hPSMask, NULLHANDLE);
        ::GpiSetBitmap(hPSBuffer, NULLHANDLE);
#if !wxUSE_DC_CACHEING
        ::GpiDestroyPS(hPSMask);
        ::GpiDestroyPS(hPSBuffer);
        ::DevCloseDC(hDCMask);
        ::DevCloseDC(hDCBuffer);
        ::GpiDeleteBitmap(hBufBitmap);
#endif
        bSuccess = true;
    }
    else // no mask, just BitBlt() it
    {
      POINTL                          aPoint[4] = { {vXdest, vYdest}
                           ,{vXdest + vWidth, vYdest + vHeight}
                           ,{vXsrc, vYsrc}
                           ,{vXsrc + vWidth, vYsrc + vHeight}
                                                    };

        bSuccess = (::GpiBitBlt( m_hPS
                                ,pSource->GetHPS()
                                ,4L
                                ,aPoint
                                ,lRop
                                ,BBO_IGNORE
                               ) != GPI_ERROR);
        if (!bSuccess )
        {
            wxLogLastError(wxT("BitBlt"));
        }
    }
    vCbnd.lColor = (LONG)vOldTextColor;
    ::GpiSetAttrs( m_hPS           // presentation-space handle
                  ,PRIM_CHAR       // Char primitive.
                  ,CBB_COLOR       // sets color.
                  ,0
                  ,&vCbnd          // buffer for attributes.
                 );
    ::GpiSetBackColor(m_hPS, (LONG)vOldBackground);
    return bSuccess;
}

void wxDC::DoGetSize( int* pnWidth,
                      int* pnHeight ) const
{
    LONG lArray[CAPS_HEIGHT+1];

    if(::DevQueryCaps( m_hDC
                      ,CAPS_FAMILY
                      ,CAPS_HEIGHT+1
                      ,lArray
                     ))
    {
        if (pnWidth)
            *pnWidth  = lArray[CAPS_WIDTH];
        if (pnHeight)
            *pnHeight = lArray[CAPS_HEIGHT];
    }
}; // end of wxDC::DoGetSize(

void wxDC::DoGetSizeMM( int* pnWidth,
                        int* pnHeight ) const
{
    LONG                            lArray[CAPS_VERTICAL_RESOLUTION+1];

    if(::DevQueryCaps( m_hDC
                      ,CAPS_FAMILY
                      ,CAPS_VERTICAL_RESOLUTION+1
                      ,lArray
                     ))
    {
        if(pnWidth)
        {
            int nWidth   = lArray[CAPS_WIDTH];
            int nHorzRes = lArray[CAPS_HORIZONTAL_RESOLUTION]; // returns pel/meter
            // use fp to avoid returning 0 if nHorzRes < 1000
            *pnWidth  = (int)((nHorzRes/1000.0) * nWidth);
        }

        if(pnHeight)
        {
            int nHeight  = lArray[CAPS_HEIGHT];
            int nVertRes = lArray[CAPS_VERTICAL_RESOLUTION];   // returns pel/meter
            // use fp to avoid returning 0 if nVertRes < 1000
            *pnHeight = (int)((nVertRes/1000.0) * nHeight);
        }
    }
}; // end of wxDC::DoGetSizeMM

wxSize wxDC::GetPPI() const
{
    LONG                            lArray[CAPS_VERTICAL_RESOLUTION+1];
    int                             nWidth = 0;
    int                             nHeight = 0;

    if(::DevQueryCaps( m_hDC
                      ,CAPS_FAMILY
                      ,CAPS_VERTICAL_RESOLUTION+1
                      ,lArray
                     ))
    {
        int                         nPelWidth;
        int                         nPelHeight;
        int                         nHorzRes;
        int                         nVertRes;

        nPelWidth  = lArray[CAPS_WIDTH];
        nPelHeight = lArray[CAPS_HEIGHT];
        nHorzRes   = lArray[CAPS_HORIZONTAL_RESOLUTION]; // returns pel/meter
        nVertRes   = lArray[CAPS_VERTICAL_RESOLUTION];   // returns pel/meter
        nWidth   = (int)((nHorzRes/39.3) * nPelWidth);
        nHeight  = (int)((nVertRes/39.3) * nPelHeight);
    }
    wxSize ppisize(nWidth, nHeight);
    return ppisize;
} // end of wxDC::GetPPI

void wxDC::SetLogicalScale( double dX, double dY )
{
    m_logicalScaleX = dX;
    m_logicalScaleY = dY;
}; // end of wxDC::SetLogicalScale
