Index: /trunk/src/gui/kernel/qwindowdefs_pm.h
===================================================================
--- /trunk/src/gui/kernel/qwindowdefs_pm.h	(revision 112)
+++ /trunk/src/gui/kernel/qwindowdefs_pm.h	(revision 113)
@@ -60,8 +60,11 @@
 typedef LHANDLE HPS;
 typedef LHANDLE HPOINTER;
+typedef LHANDLE HRGN;
 typedef struct _QMSG QMSG;
 typedef void *MRESULT;
 
 typedef HWND WId;
+
+#define NULLHANDLE ((LHANDLE)0)
 
 Q_GUI_EXPORT HPS qt_display_ps();
Index: /trunk/src/gui/painting/qregion.h
===================================================================
--- /trunk/src/gui/painting/qregion.h	(revision 112)
+++ /trunk/src/gui/painting/qregion.h	(revision 113)
@@ -144,4 +144,10 @@
 #if defined(Q_WS_WIN)
     inline HRGN    handle() const { ensureHandle(); return d->rgn; }
+#elif defined(Q_WS_PM)
+    inline HRGN handle(int targetHeight = 0) const {
+        if (d->rgn == NULLHANDLE || d->height != targetHeight)
+            updateHandle(targetHeight);
+        return d->rgn;
+    }
 #elif defined(Q_WS_X11)
     inline Region handle() const { if(!d->rgn) updateX11Region(); return d->rgn; }
@@ -175,4 +181,7 @@
     void ensureHandle() const;
     QRegion winCombine(const QRegion &r, int num) const;
+#elif defined(Q_WS_PM)
+    QRegion pmCombine(const QRegion &r, int op) const;
+    void updateHandle(int targetHeight) const;
 #elif defined(Q_WS_X11)
     void updateX11Region() const;
@@ -194,4 +203,7 @@
 #if defined(Q_WS_WIN)
         HRGN   rgn;
+#elif defined(Q_WS_PM)
+        HRGN rgn;
+        int height;
 #elif defined(Q_WS_X11)
         Region rgn;
Index: /trunk/src/gui/painting/qregion_pm.cpp
===================================================================
--- /trunk/src/gui/painting/qregion_pm.cpp	(revision 112)
+++ /trunk/src/gui/painting/qregion_pm.cpp	(revision 113)
@@ -48,7 +48,41 @@
 #include "qregion.h"
 
+#include "qt_os2.h"
+
 QT_BEGIN_NAMESPACE
 
-QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1) }; // @todo, 0 };
+//  To compensate the difference between Qt (where y axis goes downwards) and
+//  GPI (where y axis goes upwards) coordinate spaces when dealing with regions
+//  we use the following technique: when a GPI resource is allocated for a Qt
+//  region, we simply change the sign of all y coordinates to quickly flip it
+//  top to bottom in a manner that doesn't depend on the target device height.
+//  All we have to do to apply the created GPI region to a particular GPI device
+//  is to align its y axis to the top of the device (i.e. offset the region
+//  up by the height of the device), and unalign it afterwards to bring it back
+//  to the coordinate space of other device-independent (unaligned) regions.
+//  To optimize this, we remember (in data->hgt) the last height value used to
+//  align the region, and align it again only if the target device height
+//  changes. Zero height indicates a device-independent target (such as other
+//  unaligned Qt region).
+//
+//  The handle() function, used for external access to the region, takes an
+//  argument that must be always set to the height of the target device to
+//  guarantee the correct coordinate space alignment.
+
+#if defined(__GNUC__) && defined(__INNOTEK_LIBC__)
+
+// Innotek GCC lacks some API functions in its version of OS/2 Toolkit headers
+
+extern "C" HRGN APIENTRY GpiCreateEllipticRegion(HPS hps,
+                                                 PRECTL prclRect);
+
+extern "C" HRGN APIENTRY GpiCreatePolygonRegion(HPS hps,
+                                                ULONG ulCount,
+                                                PPOLYGON paplgn,
+                                                ULONG flOptions);
+#endif
+
+QRegion::QRegionData QRegion::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1),
+                                               NULLHANDLE, 0 };
 
 QRegion::QRegion()
@@ -60,14 +94,59 @@
 QRegion::QRegion(const QRect &r, RegionType t)
 {
-    d = new QRegionData;
-    d->ref = 1;
-    // @todo implement
+    if (r.isEmpty()) {
+        d = &shared_empty;
+        d->ref.ref();
+    } else {
+        d = new QRegionData;
+        d->ref = 1;
+        d->height = 0;
+        HPS hps = qt_display_ps();
+        if (t == Rectangle) {
+            RECTL rcl = { r.left(), -(r.bottom()+1), r.right()+1, -r.top() };
+            d->rgn = GpiCreateRegion(hps, 1, &rcl);
+        } else if (t == Ellipse) {
+            // if the width or height of the ellipse is odd, GPI always
+            // converts it to a nearest even value, which is obviously stupid
+            // So, we don't use GpiCreateEllipticRegion(), but create an array
+            // of points to call GpiCreatePolygonRegion() instead.
+            QPainterPath p(QPointF(r.x(), r.y()));
+            p.arcTo(r.x(), r.y(), r.width(), r.height(), 0, 360);
+            QPolygon a = p.toFillPolygon().toPolygon();
+            for (int i = 0; i < a.size(); ++ i)
+                a[i].ry() = -(a[i].y() + 1);
+            // GpiCreatePolygonRegion() is bogus and always starts a poligon from
+            // the current position. Make the last point the current one and reduce
+            // the number of points by one.
+            GpiMove(hps, reinterpret_cast<PPOINTL>(&a[a.size() - 1]));
+            POLYGON poly = { a.size() - 1, reinterpret_cast<PPOINTL>(a.data()) };
+            d->rgn = GpiCreatePolygonRegion(hps, 1, &poly, POLYGON_ALTERNATE);
+        }
+    }
 }
 
 QRegion::QRegion(const QPolygon &a, Qt::FillRule fillRule)
 {
-    d = new QRegionData;
-    d->ref = 1;
-    // @todo implement
+    if (a.size() < 3) {
+        d = &shared_empty;
+        d->ref.ref();
+    } else {
+        d = new QRegionData;
+        d->ref = 1;
+        d->height = 0;
+        HPS hps = qt_display_ps();
+        POINTL *pts = new POINTL[a.size()];
+        for (int i = 0; i < a.size(); ++ i) {
+            pts[i].x = a[i].x();
+            pts[i].y = - (a[i].y() + 1);
+        }
+        // GpiCreatePolygonRegion() is bogus and always starts a poligon from
+        // the current position. Make the last point the current one and reduce
+        // the number of points by one.
+        GpiMove(hps, &pts[a.size() - 1]);
+        POLYGON poly = { a.size() - 1, pts };
+        ULONG opts = Qt::OddEvenFill ? POLYGON_ALTERNATE : POLYGON_WINDING;
+        d->rgn = GpiCreatePolygonRegion(hps, 1, &poly, opts);
+        delete[] pts;
+    }
 }
 
@@ -78,15 +157,100 @@
 }
 
+static HRGN bitmapToRegion(const QBitmap& bitmap)
+{
+    HRGN region = 0;
+    QImage image = bitmap.convertToImage();
+    const int maxrect = 256;
+    RECTL rects[maxrect];
+    HPS hps = qt_display_ps();
+
+#define FlushSpans \
+    { \
+        HRGN r = GpiCreateRegion(hps, n, rects); \
+	    if (region) { \
+	        GpiCombineRegion(hps, region, region, r, CRGN_OR); \
+	        GpiDestroyRegion(hps, r); \
+    	} else { \
+    	    region = r; \
+    	} \
+    }
+
+#define AddSpan \
+    { \
+        rects[n].xLeft = prev1; \
+        rects[n].yBottom = -(y+1); \
+        rects[n].xRight = x-1+1; \
+        rects[n].yTop = -y; \
+        n++; \
+        if (n == maxrect) { \
+            FlushSpans \
+            n = 0; \
+        } \
+    }
+
+    int n = 0;
+    int zero = 0x00;
+
+    int x, y;
+    for (y = 0; y < image.height(); y++) {
+    	uchar *line = image.scanLine(y);
+    	int w = image.width();
+    	uchar all = zero;
+    	int prev1 = -1;
+    	for (x = 0; x < w;) {
+    	    uchar byte = line[x/8];
+    	    if (x > w-8 || byte != all) {
+    		for (int b = 8; b > 0 && x < w; b--) {
+    		    if (!(byte & 0x80) == !all) {
+                    // More of the same
+    		    } else {
+        			// A change.
+        			if (all != zero) {
+        			    AddSpan;
+        			    all = zero;
+        			} else {
+        			    prev1 = x;
+        			    all = ~zero;
+        			}
+    		    }
+    		    byte <<= 1;
+    		    x++;
+    		}
+    	    } else {
+                x += 8;
+    	    }
+    	}
+    	if (all != zero) {
+    	    AddSpan;
+    	}
+    }
+    if (n) {
+        FlushSpans;
+    }
+
+    if (!region)
+        region = GpiCreateRegion(hps, 0, NULL);
+
+    return region;
+}
+
 QRegion::QRegion(const QBitmap &bm)
 {
-    d = new QRegionData;
-    d->ref = 1;
-    // @todo implement
+    if (bm.isNull()) {
+        d = &shared_empty;
+        d->ref.ref();
+    } else {
+        d = new QRegionData;
+        d->ref = 1;
+        d->height = 0;
+        d->rgn = bitmapToRegion(bm);
+    }
 }
 
 void QRegion::cleanUp(QRegion::QRegionData *x)
 {
+    if (x->rgn != NULLHANDLE)
+        GpiDestroyRegion(qt_display_ps(), x->rgn);
     delete x;
-    // @todo implement
 }
 
@@ -109,6 +273,20 @@
 QRegion QRegion::copy() const
 {
-    // @todo implement
-    return QRegion();
+    QRegion r;
+    QRegionData *x = new QRegionData;
+    x->ref = 1;
+    if (d->rgn != NULLHANDLE) {
+        x->height = d->height;
+        HPS hps = qt_display_ps();
+        x->rgn = GpiCreateRegion(hps, 0, NULL);
+        GpiCombineRegion(hps, x->rgn, d->rgn, NULL, CRGN_COPY);
+    } else {
+        x->height = 0;
+        x->rgn = NULLHANDLE;
+    }
+    if (!r.d->ref.deref())
+        cleanUp(r.d);
+    r.d = x;
+    return r;
 }
 
@@ -118,27 +296,101 @@
 }
 
-
 bool QRegion::contains(const QPoint &p) const
 {
-    // @todo implement
-    return false;
+    LONG rc = PRGN_OUTSIDE;
+    if (d->rgn != NULLHANDLE) {
+        POINTL ptl = { p.x(), d->height - (p.y() + 1) };
+        rc = GpiPtInRegion(qt_display_ps(), d->rgn, &ptl);
+    }
+    return rc == PRGN_INSIDE;
 }
 
 bool QRegion::contains(const QRect &r) const
 {
-    // @todo implement
-    return false;
-}
-
+    LONG rc = PRGN_OUTSIDE;
+    if (d->rgn != NULLHANDLE) {
+        RECTL rcl = { r.left(), d->height - (r.bottom() + 1),
+                      r.right() + 1, d->height - r.top() };
+        rc = GpiRectInRegion(qt_display_ps(), d->rgn, &rcl);
+    }
+    return rc == RRGN_INSIDE || rc == RRGN_PARTIAL;
+}
 
 void QRegion::translate(int dx, int dy)
 {
-    // @todo implement
+    if (d->rgn == NULLHANDLE || (dx == 0 && dy == 0))
+        return;
+    detach();
+    POINTL ptl = { dx, -dy };
+    GpiOffsetRegion(qt_display_ps(), d->rgn, &ptl);
+}
+
+#define CRGN_NOP -1
+
+// Duplicates of those in qregion.cpp
+#define QRGN_OR               6
+#define QRGN_AND              7
+#define QRGN_SUB              8
+#define QRGN_XOR              9
+
+/*
+  Performs the actual OR, AND, SUB and XOR operation between regions.
+  Sets the resulting region handle to 0 to indicate an empty region.
+*/
+
+QRegion QRegion::pmCombine(const QRegion &r, int op) const
+{
+    LONG both = CRGN_NOP, left = CRGN_NOP, right = CRGN_NOP;
+    switch (op) {
+    case QRGN_OR:
+        both = CRGN_OR;
+        left = right = CRGN_COPY;
+        break;
+    case QRGN_AND:
+        both = CRGN_AND;
+        break;
+    case QRGN_SUB:
+        both = CRGN_DIFF;
+        left = CRGN_COPY;
+        break;
+    case QRGN_XOR:
+        both = CRGN_XOR;
+        left = right = CRGN_COPY;
+        break;
+    default:
+        qWarning( "QRegion: Internal error in pmCombine" );
+    }
+
+    QRegion result;
+    if (d->rgn == NULLHANDLE && r.d->rgn == NULLHANDLE)
+        return result;
+    HPS hps = qt_display_ps();
+    result.detach();
+    result.d->rgn = GpiCreateRegion(hps, 0, NULL);
+    LONG rc = RGN_NULL;
+    if (d->rgn != NULLHANDLE && r.d->rgn != NULLHANDLE) {
+        updateHandle(r.d->height); // bring to the same coordinate space
+        rc = GpiCombineRegion(hps, result.d->rgn, d->rgn, r.d->rgn, both);
+        result.d->height = r.d->height;
+    } else if (d->rgn && left != CRGN_NOP) {
+        rc = GpiCombineRegion(hps, result.d->rgn, d->rgn, 0, left);
+        result.d->height = d->height;
+    } else if (r.d->rgn != NULLHANDLE && right != CRGN_NOP) {
+        rc = GpiCombineRegion(hps, result.d->rgn, r.d->rgn, 0, right);
+        result.d->height = r.d->height;
+    }
+    if (rc == RGN_NULL || rc == RGN_ERROR) {
+        result = QRegion(); // shared_empty
+    }
+    return result;
 }
 
 QRegion QRegion::unite(const QRegion &r) const
 {
-    // @todo implement
-    return QRegion();
+    if (d->rgn == NULLHANDLE)
+        return r;
+    if (r.d->rgn == NULLHANDLE)
+        return *this;
+    return pmCombine(r, QRGN_OR);
 }
 
@@ -150,32 +402,73 @@
 QRegion QRegion::intersect(const QRegion &r) const
 {
-    // @todo implement
-    return QRegion();
+    if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
+        return QRegion();
+     return pmCombine(r, QRGN_AND);
 }
 
 QRegion QRegion::subtract(const QRegion &r) const
 {
-    // @todo implement
-    return QRegion();
+    if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
+        return *this;
+    return pmCombine(r, QRGN_SUB);
 }
 
 QRegion QRegion::eor(const QRegion &r) const
 {
-    // @todo implement
-    return QRegion();
-}
-
+    if (d->rgn == NULLHANDLE)
+        return r;
+    if (r.d->rgn == NULLHANDLE)
+        return *this;
+    return pmCombine(r, QRGN_XOR);
+}
 
 QRect QRegion::boundingRect() const
 {
-    // @todo implement
-    return QRect();
+    if (!d->rgn)
+        return QRect();
+
+    RECTL rcl;
+    LONG rc = RGN_NULL;
+    if (d->rgn != NULLHANDLE)
+        rc = GpiQueryRegionBox(qt_display_ps(), d->rgn, &rcl);
+    if (rc == RGN_NULL || rc == RGN_ERROR)
+        return QRect();
+    else
+        return QRect(rcl.xLeft, d->height - rcl.yTop,
+                     rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom);
 }
 
 QVector<QRect> QRegion::rects() const
 {
-    // @todo implement
-    return QVector<QRect>();
-}
+    QVector<QRect> a;
+
+    if (d->rgn == NULLHANDLE)
+        return a;
+
+    HPS hps = qt_display_ps();
+    RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
+    if (!GpiQueryRegionRects(hps, d->rgn, NULL, &ctl, NULL))
+        return a;
+
+    ctl.crc = ctl.crcReturned;
+    PRECTL rcls = new RECTL[ctl.crcReturned];
+    if (rcls == 0)
+        return a;
+    if (!GpiQueryRegionRects(hps, d->rgn, NULL, &ctl, rcls)) {
+        delete [] rcls;
+        return a;
+    }
+
+    a = QVector<QRect>(ctl.crcReturned);
+    PRECTL r = rcls;
+    for (int i = 0; i < a.size(); ++i) {
+        a[i].setRect(r->xLeft, d->height - r->yTop,
+                     r->xRight - r->xLeft, r->yTop - r->yBottom);
+        ++r;
+    }
+
+    delete [] rcls;
+    return a;
+ }
 
 void QRegion::setRects(const QRect *rects, int num)
@@ -190,17 +483,38 @@
 int QRegion::numRects() const
 {
-    // @todo implement
-    return 0;
+    if (d->rgn == NULLHANDLE)
+        return 0;
+
+    RGNRECT ctl = {1, 0, 0, RECTDIR_LFRT_TOPBOT};
+    if (!GpiQueryRegionRects(qt_display_ps(), d->rgn, NULL, &ctl, NULL))
+        return 0;
+
+    return ctl.crcReturned;
 }
 
 bool QRegion::operator==(const QRegion &r) const
 {
-    // @todo implement
-    return false;
+    if (d == r.d)
+        return true;
+    if ((d->rgn == NULLHANDLE) ^ (r.d->rgn == NULLHANDLE)) // one is empty, not both
+        return false;
+    if (d->rgn == NULLHANDLE) // both empty
+        return true;
+    updateHandle(r.d->height); // bring to the same coordinate space
+    return // both not empty
+        GpiEqualRegion(qt_display_ps(), d->rgn, r.d->rgn) == EQRGN_EQUAL;
 }
 
 QRegion& QRegion::operator+=(const QRegion &r)
 {
-    // @todo implement
+    if (r.d->rgn == NULLHANDLE)
+        return *this;
+
+    if (d->rgn == NULLHANDLE) {
+        *this = r;
+        return *this;
+    }
+
+    *this = unite(r);
     return *this;
 }
@@ -208,5 +522,8 @@
 QRegion& QRegion::operator-=(const QRegion &r)
 {
-    // @todo implement
+    if (r.d->rgn == NULLHANDLE || d->rgn == NULLHANDLE)
+        return *this;
+
+    *this = subtract(r);
     return *this;
 }
@@ -214,5 +531,13 @@
 QRegion& QRegion::operator&=(const QRegion &r)
 {
-    // @todo implement
+    if (d->rgn == NULLHANDLE)
+        return *this;
+
+    if (r.d->rgn == NULLHANDLE) {
+        *this = QRegion();
+        return *this;
+    }
+
+    *this = intersect(r);
     return *this;
 }
@@ -225,3 +550,26 @@
 }
 
+/*!
+    \internal
+
+    Updates the region handle so that it is suitable for selection to
+    a device with the given \a height.
+ */
+void QRegion::updateHandle(int targetHeight) const
+{
+    QRegion *that = const_cast<QRegion*>(this); // we're const here
+    if (d->rgn == NULLHANDLE) {
+        // a handle of a null region is requested, allocate an empty region
+        that->detach();
+        that->d->rgn = GpiCreateRegion(qt_display_ps(), 0, NULL);
+        that->d->height = targetHeight;
+    } else if (d->height != targetHeight) {
+        // align region y axis to the top of the device
+        that->detach();
+        POINTL ptl = { 0, targetHeight - d->height };
+        GpiOffsetRegion(qt_display_ps(), d->rgn, &ptl);
+        that->d->height = targetHeight;
+    }
+}
+
 QT_END_NAMESPACE
