]> git.saurik.com Git - wxWidgets.git/blobdiff - src/gtk/dcclient.cpp
Make wxWindow::HasScrollbar() respect wxScrolled::ShowScrollbars().
[wxWidgets.git] / src / gtk / dcclient.cpp
index 0ac7173e3df2163732fb25595e0815f5d9390ea5..a11615391e6d7b77e6107050452af4740b202503 100644 (file)
@@ -2,7 +2,6 @@
 // Name:        src/gtk/dcclient.cpp
 // Purpose:     wxWindowDCImpl implementation
 // Author:      Robert Roebling
-// RCS-ID:      $Id$
 // Copyright:   (c) 1998 Robert Roebling, Chris Breeze
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
@@ -25,6 +24,7 @@
 
 #include "wx/gtk/private.h"
 #include "wx/gtk/private/object.h"
+#include "wx/private/textmeasure.h"
 
 //-----------------------------------------------------------------------------
 // local defines
@@ -63,35 +63,48 @@ static const double RAD2DEG  = 180.0 / M_PI;
 static inline double dmax(double a, double b) { return a > b ? a : b; }
 static inline double dmin(double a, double b) { return a < b ? a : b; }
 
-static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
-
 static GdkPixmap* GetHatch(int style)
 {
     wxASSERT(style >= wxBRUSHSTYLE_FIRST_HATCH && style <= wxBRUSHSTYLE_LAST_HATCH);
     const int i = style - wxBRUSHSTYLE_FIRST_HATCH;
     if (hatches[i] == NULL)
     {
+        // This macro creates a bitmap from an XBM file included above. Notice
+        // the need for the cast because gdk_bitmap_create_from_data() doesn't
+        // accept unsigned data but the arrays in XBM need to be unsigned to
+        // avoid warnings (and even errors in C+0x mode) from g++.
+#define CREATE_FROM_XBM_DATA(name) \
+        gdk_bitmap_create_from_data \
+        ( \
+            NULL, \
+            reinterpret_cast<gchar *>(name ## _bits), \
+            name ## _width, \
+            name ## _height \
+        )
+
         switch (style)
         {
         case wxBRUSHSTYLE_BDIAGONAL_HATCH:
-            hatches[i] = gdk_bitmap_create_from_data(NULL, bdiag_bits, bdiag_width, bdiag_height);
+            hatches[i] = CREATE_FROM_XBM_DATA(bdiag);
             break;
         case wxBRUSHSTYLE_CROSSDIAG_HATCH:
-            hatches[i] = gdk_bitmap_create_from_data(NULL, cdiag_bits, cdiag_width, cdiag_height);
+            hatches[i] = CREATE_FROM_XBM_DATA(cdiag);
             break;
         case wxBRUSHSTYLE_CROSS_HATCH:
-            hatches[i] = gdk_bitmap_create_from_data(NULL, cross_bits, cross_width, cross_height);
+            hatches[i] = CREATE_FROM_XBM_DATA(cross);
             break;
         case wxBRUSHSTYLE_FDIAGONAL_HATCH:
-            hatches[i] = gdk_bitmap_create_from_data(NULL, fdiag_bits, fdiag_width, fdiag_height);
+            hatches[i] = CREATE_FROM_XBM_DATA(fdiag);
             break;
         case wxBRUSHSTYLE_HORIZONTAL_HATCH:
-            hatches[i] = gdk_bitmap_create_from_data(NULL, horiz_bits, horiz_width, horiz_height);
+            hatches[i] = CREATE_FROM_XBM_DATA(horiz);
             break;
         case wxBRUSHSTYLE_VERTICAL_HATCH:
-            hatches[i] = gdk_bitmap_create_from_data(NULL, verti_bits, verti_width, verti_height);
+            hatches[i] = CREATE_FROM_XBM_DATA(verti);
             break;
         }
+
+#undef CREATE_FROM_XBM_DATA
     }
     return hatches[i];
 }
@@ -114,7 +127,11 @@ enum wxPoolGCType
    wxTEXT_SCREEN,
    wxBG_SCREEN,
    wxPEN_SCREEN,
-   wxBRUSH_SCREEN
+   wxBRUSH_SCREEN,
+   wxTEXT_COLOUR_ALPHA,
+   wxBG_COLOUR_ALPHA,
+   wxPEN_COLOUR_ALPHA,
+   wxBRUSH_COLOUR_ALPHA
 };
 
 struct wxGC
@@ -280,7 +297,7 @@ wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) :
         wxCHECK_RET(widget, "DC needs a widget");
 
         m_gdkwindow = widget->window;
-        if (GTK_WIDGET_NO_WINDOW(widget))
+        if (!gtk_widget_get_has_window(widget))
             SetDeviceLocalOrigin(widget->allocation.x, widget->allocation.y);
     }
 
@@ -360,6 +377,18 @@ void wxWindowDCImpl::SetUpDC( bool isMemDC )
             m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_SCREEN );
             m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_SCREEN );
         }
+#if GTK_CHECK_VERSION(2,12,0)
+        // gdk_screen_get_rgba_colormap was added in 2.8, but this code is for
+        // compositing which requires 2.12
+        else if (gtk_check_version(2,12,0) == NULL &&
+            m_cmap == gdk_screen_get_rgba_colormap(gdk_colormap_get_screen(m_cmap)))
+        {
+            m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR_ALPHA );
+            m_brushGC = wxGetPoolGC( m_gdkwindow, wxBRUSH_COLOUR_ALPHA );
+            m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_COLOUR_ALPHA );
+            m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_COLOUR_ALPHA );
+        }
+#endif
         else
         {
             m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_COLOUR );
@@ -477,7 +506,7 @@ void wxWindowDCImpl::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2
 {
     wxCHECK_RET( IsOk(), wxT("invalid window dc") );
 
-    if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+    if ( m_pen.IsNonTransparent() )
     {
         if (m_gdkwindow)
             gdk_draw_line( m_gdkwindow, m_penGC, XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2) );
@@ -491,7 +520,7 @@ void wxWindowDCImpl::DoCrossHair( wxCoord x, wxCoord y )
 {
     wxCHECK_RET( IsOk(), wxT("invalid window dc") );
 
-    if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+    if ( m_pen.IsNonTransparent() )
     {
         int w = 0;
         int h = 0;
@@ -589,7 +618,7 @@ void wxWindowDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2,
 
     if (m_gdkwindow)
     {
-        if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
+        if ( m_brush.IsNonTransparent() )
         {
             GdkGC* gc;
             bool originChanged;
@@ -601,11 +630,11 @@ void wxWindowDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2,
                 gdk_gc_set_ts_origin(gc, 0, 0);
         }
 
-        if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+        if ( m_pen.IsNonTransparent() )
         {
             gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
 
-            if ((m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT) && (alpha2 - alpha1 != 360*64))
+            if ( m_brush.IsNonTransparent() && (alpha2 - alpha1 != 360*64) )
             {
                 gdk_draw_line( m_gdkwindow, m_penGC, xx1, yy1, xxc, yyc );
                 gdk_draw_line( m_gdkwindow, m_penGC, xxc, yyc, xx2, yy2 );
@@ -635,7 +664,7 @@ void wxWindowDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxC
         wxCoord start = wxCoord(sa * 64.0);
         wxCoord end = wxCoord((ea-sa) * 64.0);
 
-        if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
+        if ( m_brush.IsNonTransparent() )
         {
             GdkGC* gc;
             bool originChanged;
@@ -647,7 +676,7 @@ void wxWindowDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxC
                 gdk_gc_set_ts_origin(gc, 0, 0);
         }
 
-        if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+        if ( m_pen.IsNonTransparent() )
             gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx, yy, ww, hh, start, end );
     }
 
@@ -659,47 +688,52 @@ void wxWindowDCImpl::DoDrawPoint( wxCoord x, wxCoord y )
 {
     wxCHECK_RET( IsOk(), wxT("invalid window dc") );
 
-    if ((m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT) && m_gdkwindow)
+    if ( m_pen.IsNonTransparent() && m_gdkwindow )
         gdk_draw_point( m_gdkwindow, m_penGC, XLOG2DEV(x), YLOG2DEV(y) );
 
     CalcBoundingBox (x, y);
 }
 
-void wxWindowDCImpl::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset )
+void wxWindowDCImpl::DoDrawLines( int n, const wxPoint points[], wxCoord xoffset, wxCoord yoffset )
 {
     wxCHECK_RET( IsOk(), wxT("invalid window dc") );
 
-    if (m_pen.GetStyle() == wxPENSTYLE_TRANSPARENT) return;
     if (n <= 0) return;
 
+    if ( m_pen.IsTransparent() )
+        return;
+
     //Check, if scaling is necessary
     const bool doScale =
         xoffset != 0 || yoffset != 0 || XLOG2DEV(10) != 10 || YLOG2DEV(10) != 10;
 
     // GdkPoint and wxPoint have the same memory layout, so we can cast one to the other
-    GdkPoint* gpts = reinterpret_cast<GdkPoint*>(points);
+    const GdkPoint* gpts = reinterpret_cast<const GdkPoint*>(points);
+    GdkPoint* gpts_alloc = NULL;
 
     if (doScale)
-        gpts = new GdkPoint[n];
+    {
+        gpts_alloc = new GdkPoint[n];
+        gpts = gpts_alloc;
+    }
 
     for (int i = 0; i < n; i++)
     {
         if (doScale)
         {
-            gpts[i].x = XLOG2DEV(points[i].x + xoffset);
-            gpts[i].y = YLOG2DEV(points[i].y + yoffset);
+            gpts_alloc[i].x = XLOG2DEV(points[i].x + xoffset);
+            gpts_alloc[i].y = YLOG2DEV(points[i].y + yoffset);
         }
         CalcBoundingBox(points[i].x + xoffset, points[i].y + yoffset);
     }
 
     if (m_gdkwindow)
-        gdk_draw_lines( m_gdkwindow, m_penGC, gpts, n);
+        gdk_draw_lines( m_gdkwindow, m_penGC, (GdkPoint*) gpts, n);
 
-    if (doScale)
-        delete[] gpts;
+    delete[] gpts_alloc;
 }
 
-void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[],
+void wxWindowDCImpl::DoDrawPolygon( int n, const wxPoint points[],
                                     wxCoord xoffset, wxCoord yoffset,
                                     wxPolygonFillMode WXUNUSED(fillStyle) )
 {
@@ -712,37 +746,41 @@ void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[],
         xoffset != 0 || yoffset != 0 || XLOG2DEV(10) != 10 || YLOG2DEV(10) != 10;
 
     // GdkPoint and wxPoint have the same memory layout, so we can cast one to the other
-    GdkPoint* gdkpoints = reinterpret_cast<GdkPoint*>(points);
+    const GdkPoint* gdkpoints = reinterpret_cast<const GdkPoint*>(points);
+    GdkPoint* gdkpoints_alloc = NULL;
 
     if (doScale)
-        gdkpoints = new GdkPoint[n];
+    {
+        gdkpoints_alloc = new GdkPoint[n];
+        gdkpoints = gdkpoints_alloc;
+    }
 
     int i;
     for (i = 0 ; i < n ; i++)
     {
         if (doScale)
         {
-            gdkpoints[i].x = XLOG2DEV(points[i].x + xoffset);
-            gdkpoints[i].y = YLOG2DEV(points[i].y + yoffset);
+            gdkpoints_alloc[i].x = XLOG2DEV(points[i].x + xoffset);
+            gdkpoints_alloc[i].y = YLOG2DEV(points[i].y + yoffset);
         }
         CalcBoundingBox(points[i].x + xoffset, points[i].y + yoffset);
     }
 
     if (m_gdkwindow)
     {
-        if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
+        if ( m_brush.IsNonTransparent() )
         {
             GdkGC* gc;
             bool originChanged;
             DrawingSetup(gc, originChanged);
 
-            gdk_draw_polygon(m_gdkwindow, gc, true, gdkpoints, n);
+            gdk_draw_polygon(m_gdkwindow, gc, true, (GdkPoint*) gdkpoints, n);
 
             if (originChanged)
                 gdk_gc_set_ts_origin(gc, 0, 0);
         }
 
-        if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+        if ( m_pen.IsNonTransparent() )
         {
 /*
             for (i = 0 ; i < n ; i++)
@@ -754,13 +792,12 @@ void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[],
                                gdkpoints[(i+1)%n].y);
             }
 */
-            gdk_draw_polygon( m_gdkwindow, m_penGC, FALSE, gdkpoints, n );
+            gdk_draw_polygon( m_gdkwindow, m_penGC, FALSE, (GdkPoint*) gdkpoints, n );
 
         }
     }
 
-    if (doScale)
-        delete[] gdkpoints;
+    delete[] gdkpoints_alloc;
 }
 
 void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
@@ -781,7 +818,7 @@ void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoo
 
     if (m_gdkwindow)
     {
-        if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
+        if ( m_brush.IsNonTransparent() )
         {
             GdkGC* gc;
             bool originChanged;
@@ -793,9 +830,8 @@ void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoo
                 gdk_gc_set_ts_origin(gc, 0, 0);
         }
 
-        if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+        if ( m_pen.IsNonTransparent() )
         {
-#if 1
             if ((m_pen.GetWidth() == 2) && (m_pen.GetCap() == wxCAP_ROUND) &&
                 (m_pen.GetJoin() == wxJOIN_ROUND) && (m_pen.GetStyle() == wxPENSTYLE_SOLID))
             {
@@ -818,7 +854,6 @@ void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoo
                 gdk_gc_set_line_attributes( m_penGC, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND );
             }
             else
-#endif
             {
                 // Just use X11 for other cases
                 gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx, yy, ww-1, hh-1 );
@@ -859,7 +894,7 @@ void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width
 
     // CMB: adjust size if outline is drawn otherwise the result is
     // 1 pixel too wide and high
-    if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+    if ( m_pen.IsNonTransparent() )
     {
         ww--;
         hh--;
@@ -874,7 +909,7 @@ void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width
         if (dd > hh) dd = hh;
         rr = dd / 2;
 
-        if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
+        if ( m_brush.IsNonTransparent() )
         {
             GdkGC* gc;
             bool originChanged;
@@ -891,7 +926,7 @@ void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width
                 gdk_gc_set_ts_origin(gc, 0, 0);
         }
 
-        if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+        if ( m_pen.IsNonTransparent() )
         {
             gdk_draw_line( m_gdkwindow, m_penGC, xx+rr+1, yy, xx+ww-rr, yy );
             gdk_draw_line( m_gdkwindow, m_penGC, xx+rr+1, yy+hh, xx+ww-rr, yy+hh );
@@ -924,7 +959,7 @@ void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord
 
     if (m_gdkwindow)
     {
-        if (m_brush.GetStyle() != wxBRUSHSTYLE_TRANSPARENT)
+        if ( m_brush.IsNonTransparent() )
         {
             GdkGC* gc;
             bool originChanged;
@@ -932,7 +967,7 @@ void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord
 
             // If the pen is transparent pen we increase the size
             // for better compatibility with other platforms.
-            if (m_pen.GetStyle() == wxPENSTYLE_TRANSPARENT)
+            if (m_pen.IsTransparent())
             {
                 ++ww;
                 ++hh;
@@ -944,7 +979,7 @@ void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord
                 gdk_gc_set_ts_origin(gc, 0, 0);
         }
 
-        if (m_pen.GetStyle() != wxPENSTYLE_TRANSPARENT)
+        if ( m_pen.IsNonTransparent() )
             gdk_draw_arc( m_gdkwindow, m_penGC, false, xx, yy, ww, hh, 0, 360*64 );
     }
 
@@ -958,7 +993,7 @@ void wxWindowDCImpl::DoDrawIcon( const wxIcon &icon, wxCoord x, wxCoord y )
     DoDrawBitmap( (const wxBitmap&)icon, x, y, true );
 }
 
-// scale a pixbuf, return new pixbuf, unref old one
+// scale a pixbuf
 static GdkPixbuf*
 Scale(GdkPixbuf* pixbuf, int dst_w, int dst_h, double sx, double sy)
 {
@@ -966,20 +1001,21 @@ Scale(GdkPixbuf* pixbuf, int dst_w, int dst_h, double sx, double sy)
         GDK_COLORSPACE_RGB, gdk_pixbuf_get_has_alpha(pixbuf), 8, dst_w, dst_h);
     gdk_pixbuf_scale(pixbuf, pixbuf_scaled,
         0, 0, dst_w, dst_h, 0, 0, sx, sy, GDK_INTERP_NEAREST);
-    g_object_unref(pixbuf);
     return pixbuf_scaled;
 }
 
-// scale part of a pixmap using pixbuf scaling, return pixbuf
+// scale part of a pixmap using pixbuf scaling
 static GdkPixbuf*
 Scale(GdkPixmap* pixmap, int x, int y, int w, int h, int dst_w, int dst_h, double sx, double sy)
 {
     GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(
         NULL, pixmap, NULL, x, y, 0, 0, w, h);
-    return Scale(pixbuf, dst_w, dst_h, sx, sy);
+    GdkPixbuf* pixbuf2 = Scale(pixbuf, dst_w, dst_h, sx, sy);
+    g_object_unref(pixbuf);
+    return pixbuf2;
 }
 
-// scale part of a mask pixmap, return new mask, unref old one
+// scale part of a mask pixmap
 static GdkPixmap*
 ScaleMask(GdkPixmap* mask, int x, int y, int w, int h, int dst_w, int dst_h, double sx, double sy)
 {
@@ -1003,12 +1039,10 @@ ScaleMask(GdkPixmap* mask, int x, int y, int w, int h, int dst_w, int dst_h, dou
     g_object_unref(pixbuf);
     GdkPixmap* pixmap = gdk_bitmap_create_from_data(mask, data, dst_w, dst_h);
     delete[] data;
-    g_object_unref(mask);
     return pixmap;
 }
 
 // Make a new mask from part of a mask and a clip region.
-// Return new mask, unref old one.
 static GdkPixmap*
 ClipMask(GdkPixmap* mask, GdkRegion* clipRegion, int x, int y, int dst_x, int dst_y, int w, int h)
 {
@@ -1023,7 +1057,6 @@ ClipMask(GdkPixmap* mask, GdkRegion* clipRegion, int x, int y, int dst_x, int ds
     // draw old mask onto new one, with clip
     gdk_draw_drawable(pixmap, gc, mask, x, y, 0, 0, w, h);
     g_object_unref(gc);
-    g_object_unref(mask);
     return pixmap;
 }
 
@@ -1094,18 +1127,25 @@ void wxWindowDCImpl::DoDrawBitmap( const wxBitmap &bitmap,
     {
         wxMask* m = bitmap.GetMask();
         if (m)
-            mask = m->GetBitmap();
+            mask = *m;
     }
+
+    GdkPixmap* mask_new = NULL;
     if (mask)
     {
-        g_object_ref(mask);
         if (isScaled)
+        {
             mask = ScaleMask(mask, 0, 0, w, h, ww, hh, m_scaleX, m_scaleY);
+            mask_new = mask;
+        }
         if (overlap == wxPartRegion)
         {
             // need a new mask that also masks the clipped area,
             // because gc can't have both a mask and a clip region
             mask = ClipMask(mask, clipRegion, 0, 0, xx, yy, ww, hh);
+            if (mask_new)
+                g_object_unref(mask_new);
+            mask_new = mask;
         }
         gdk_gc_set_clip_mask(use_gc, mask);
         gdk_gc_set_clip_origin(use_gc, xx, yy);
@@ -1113,24 +1153,22 @@ void wxWindowDCImpl::DoDrawBitmap( const wxBitmap &bitmap,
 
     // determine whether to use pixmap or pixbuf
     GdkPixmap* pixmap = NULL;
+    GdkPixmap* pixmap_new = NULL;
     GdkPixbuf* pixbuf = NULL;
+    GdkPixbuf* pixbuf_new = NULL;
     if (bitmap.HasPixmap())
         pixmap = bitmap.GetPixmap();
     if (pixmap && gdk_drawable_get_depth(pixmap) == 1)
     {
-        // convert mono pixmap to color using text fg/bg colors
-        pixmap = MonoToColor(pixmap, 0, 0, w, h);
+        if (gdk_drawable_get_depth(m_gdkwindow) != 1)
+        {
+            // convert mono pixmap to color using text fg/bg colors
+            pixmap = MonoToColor(pixmap, 0, 0, w, h);
+            pixmap_new = pixmap;
+        }
     }
     else if (hasAlpha || pixmap == NULL)
-    {
-        pixmap = NULL;
         pixbuf = bitmap.GetPixbuf();
-        g_object_ref(pixbuf);
-    }
-    else
-    {
-        g_object_ref(pixmap);
-    }
 
     if (isScaled)
     {
@@ -1138,25 +1176,34 @@ void wxWindowDCImpl::DoDrawBitmap( const wxBitmap &bitmap,
             pixbuf = Scale(pixbuf, ww, hh, m_scaleX, m_scaleY);
         else
             pixbuf = Scale(pixmap, 0, 0, w, h, ww, hh, m_scaleX, m_scaleY);
+
+        pixbuf_new = pixbuf;
     }
 
     if (pixbuf)
     {
         gdk_draw_pixbuf(m_gdkwindow, use_gc, pixbuf,
             0, 0, xx, yy, ww, hh, GDK_RGB_DITHER_NORMAL, 0, 0);
-        g_object_unref(pixbuf);
     }
     else
     {
         gdk_draw_drawable(m_gdkwindow, use_gc, pixmap, 0, 0, xx, yy, ww, hh);
     }
 
-    if (pixmap)
-        g_object_unref(pixmap);
+    if (pixbuf_new)
+        g_object_unref(pixbuf_new);
+    if (pixmap_new)
+        g_object_unref(pixmap_new);
     if (mask)
     {
-        g_object_unref(mask);
         gdk_gc_set_clip_region(use_gc, clipRegion);
+
+        // Notice that we can only release the mask now, we can't do it before
+        // the calls to gdk_draw_xxx() above as they crash with X error with
+        // GTK+ up to 2.20.1 (i.e. it works with 2.20 but is known to not work
+        // with 2.16.1 and below).
+        if (mask_new)
+            g_object_unref(mask_new);
     }
 }
 
@@ -1186,7 +1233,7 @@ bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest,
         {
             wxMask* m = bitmap.GetMask();
             if (m)
-                mask = m->GetBitmap();
+                mask = *m;
         }
     }
     else
@@ -1266,7 +1313,6 @@ bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest,
 
     if (mask)
     {
-        g_object_ref(mask);
         int srcMask_x = src_x;
         int srcMask_y = src_y;
         if (xsrcMask != -1 || ysrcMask != -1)
@@ -1274,10 +1320,12 @@ bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest,
             srcMask_x = source->LogicalToDeviceX(xsrcMask);
             srcMask_y = source->LogicalToDeviceY(ysrcMask);
         }
+        GdkPixmap* mask_new = NULL;
         if (isScaled)
         {
             mask = ScaleMask(mask, srcMask_x, srcMask_y,
                 src_w, src_h, dst_w, dst_h, scale_x, scale_y);
+            mask_new = mask;
             srcMask_x = 0;
             srcMask_y = 0;
         }
@@ -1287,15 +1335,21 @@ bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest,
             // because gc can't have both a mask and a clip region
             mask = ClipMask(mask, clipRegion,
                 srcMask_x, srcMask_y, dst_x, dst_y, dst_w, dst_h);
+            if (mask_new)
+                g_object_unref(mask_new);
+            mask_new = mask;
             srcMask_x = 0;
             srcMask_y = 0;
         }
         gdk_gc_set_clip_mask(use_gc, mask);
         gdk_gc_set_clip_origin(use_gc, dst_x - srcMask_x, dst_y - srcMask_y);
+        if (mask_new)
+            g_object_unref(mask_new);
     }
 
     GdkPixmap* pixmap = NULL;
-    if (gdk_drawable_get_depth(srcDrawable) == 1)
+    if (gdk_drawable_get_depth(srcDrawable) == 1 &&
+        (gdk_drawable_get_depth(m_gdkwindow) != 1 || isScaled))
     {
         // Convert mono pixmap to color using text fg/bg colors.
         // Scaling/drawing is simpler if this is done first.
@@ -1331,10 +1385,8 @@ bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest,
     if (pixmap)
         g_object_unref(pixmap);
     if (mask)
-    {
-        g_object_unref(mask);
         gdk_gc_set_clip_region(use_gc, clipRegion);
-    }
+
     return true;
 }
 
@@ -1342,199 +1394,57 @@ void wxWindowDCImpl::DoDrawText(const wxString& text,
                                 wxCoord xLogical,
                                 wxCoord yLogical)
 {
-    wxCHECK_RET( IsOk(), wxT("invalid window dc") );
-
-    if (!m_gdkwindow) return;
-
-    if (text.empty()) return;
-
-    wxCoord x = XLOG2DEV(xLogical),
-            y = YLOG2DEV(yLogical);
-
-    wxCHECK_RET( m_context, wxT("no Pango context") );
-    wxCHECK_RET( m_layout, wxT("no Pango layout") );
-    wxCHECK_RET( m_fontdesc, wxT("no Pango font description") );
-
-    gdk_pango_context_set_colormap( m_context, m_cmap );  // not needed in gtk+ >= 2.6
-
-    bool underlined = m_font.IsOk() && m_font.GetUnderlined();
+    DoDrawRotatedText(text, xLogical, yLogical, 0);
+}
 
-    wxCharBuffer data = wxGTK_CONV(text);
-    if ( !data )
+void wxWindowDCImpl::DoDrawRotatedText(const wxString& text, int xLogical, int yLogical, double angle)
+{
+    if (!m_gdkwindow || text.empty())
         return;
-    size_t datalen = strlen(data);
-
-    // in Pango >= 1.16 the "underline of leading/trailing spaces" bug
-    // has been fixed and thus the hack implemented below should never be used
-    static bool pangoOk = !wx_pango_version_check(1, 16, 0);
 
-    bool needshack = underlined && !pangoOk;
-
-    if (needshack)
-    {
-        // a PangoLayout which has leading/trailing spaces with underlined font
-        // is not correctly drawn by this pango version: Pango won't underline the spaces.
-        // This can be a problem; e.g. wxHTML rendering of underlined text relies on
-        // this behaviour. To workaround this problem, we use a special hack here
-        // suggested by pango maintainer Behdad Esfahbod: we prepend and append two
-        // empty space characters and give them a dummy colour attribute.
-        // This will force Pango to underline the leading/trailing spaces, too.
-
-        wxCharBuffer data_tmp(datalen + 6);
-        // copy the leading U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format
-        memcpy(data_tmp.data(), "\342\200\214", 3);
-        // copy the user string
-        memcpy(data_tmp.data() + 3, data, datalen);
-        // copy the trailing U+200C ZERO WIDTH NON-JOINER encoded in UTF8 format
-        memcpy(data_tmp.data() + 3 + datalen, "\342\200\214", 3);
-
-        data = data_tmp;
-        datalen += 6;
-    }
-
-    pango_layout_set_text(m_layout, data, datalen);
+    wxCHECK_RET( IsOk(), wxT("invalid window dc") );
 
-    if (underlined)
-    {
-        PangoAttrList *attrs = pango_attr_list_new();
-        PangoAttribute *a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-        a->start_index = 0;
-        a->end_index = datalen;
-        pango_attr_list_insert(attrs, a);
+    pango_layout_set_text(m_layout, wxGTK_CONV(text), -1);
+    const bool setAttrs = m_font.GTKSetPangoAttrs(m_layout);
 
-        if (needshack)
-        {
-            // dummy colour for the leading space
-            a = pango_attr_foreground_new (0x0057, 0x52A9, 0xD614);
-            a->start_index = 0;
-            a->end_index = 1;
-            pango_attr_list_insert(attrs, a);
-
-            // dummy colour for the trailing space
-            a = pango_attr_foreground_new (0x0057, 0x52A9, 0xD614);
-            a->start_index = datalen - 1;
-            a->end_index = datalen;
-            pango_attr_list_insert(attrs, a);
-        }
-
-        pango_layout_set_attributes(m_layout, attrs);
-        pango_attr_list_unref(attrs);
-    }
+    const GdkColor* bg_col = NULL;
+    if (m_backgroundMode == wxBRUSHSTYLE_SOLID)
+        bg_col = m_textBackgroundColour.GetColor();
 
-    int oldSize = 0;
-    const bool isScaled = fabs(m_scaleY - 1.0) > 0.00001;
-    if (isScaled)
+    PangoMatrix matrix = PANGO_MATRIX_INIT;
+    if (!wxIsSameDouble(m_scaleX, 1) || !wxIsSameDouble(m_scaleY, 1) || !wxIsNullDouble(angle))
     {
-        // If there is a user or actually any scale applied to
-        // the device context, scale the font.
-
-        // scale font description
-        oldSize = pango_font_description_get_size(m_fontdesc);
-        pango_font_description_set_size(m_fontdesc, int(oldSize * m_scaleY));
-
-        // actually apply scaled font
-        pango_layout_set_font_description( m_layout, m_fontdesc );
+        pango_matrix_scale(&matrix, m_scaleX, m_scaleY);
+        pango_matrix_rotate(&matrix, angle);
+        pango_context_set_matrix(m_context, &matrix);
+        pango_layout_context_changed(m_layout);
     }
 
     int w, h;
     pango_layout_get_pixel_size(m_layout, &w, &h);
 
-    // Draw layout.
-    int x_rtl = x;
+    int x = LogicalToDeviceX(xLogical);
+    int y = LogicalToDeviceY(yLogical);
     if (m_window && m_window->GetLayoutDirection() == wxLayout_RightToLeft)
-        x_rtl -= w;
-
-    const GdkColor* bg_col = NULL;
-    if (m_backgroundMode == wxBRUSHSTYLE_SOLID)
-        bg_col = m_textBackgroundColour.GetColor();
-
-    gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x_rtl, y, m_layout, NULL, bg_col);
-
-    if (isScaled)
-    {
-         // reset unscaled size
-         pango_font_description_set_size( m_fontdesc, oldSize );
+        x -= LogicalToDeviceXRel(w);
 
-         // actually apply unscaled font
-         pango_layout_set_font_description( m_layout, m_fontdesc );
-    }
-    if (underlined)
+    if (wxIsNullDouble(angle))
     {
-        // undo underline attributes setting:
-        pango_layout_set_attributes(m_layout, NULL);
+        CalcBoundingBox(xLogical, yLogical);
+        CalcBoundingBox(xLogical + w, yLogical + h);
     }
-
-    CalcBoundingBox(xLogical + int(w / m_scaleX), yLogical + int(h / m_scaleY));
-    CalcBoundingBox(xLogical, yLogical);
-}
-
-// TODO: When GTK2.6 is required, merge DoDrawText and DoDrawRotatedText to
-// avoid code duplication
-void wxWindowDCImpl::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord y, double angle )
-{
-    if (!m_gdkwindow || text.empty())
-        return;
-
-    wxCHECK_RET( IsOk(), wxT("invalid window dc") );
-
-#if __WXGTK26__
-    if (!gtk_check_version(2,6,0))
+    else
     {
-        x = XLOG2DEV(x);
-        y = YLOG2DEV(y);
-
-        pango_layout_set_text(m_layout, wxGTK_CONV(text), -1);
-
-        if (m_font.GetUnderlined())
-        {
-            PangoAttrList *attrs = pango_attr_list_new();
-            PangoAttribute *a = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
-            pango_attr_list_insert(attrs, a);
-            pango_layout_set_attributes(m_layout, attrs);
-            pango_attr_list_unref(attrs);
-        }
-
-        int oldSize = 0;
-        const bool isScaled = fabs(m_scaleY - 1.0) > 0.00001;
-        if (isScaled)
-        {
-            //TODO: when Pango >= 1.6 is required, use pango_matrix_scale()
-             // If there is a user or actually any scale applied to
-             // the device context, scale the font.
-
-             // scale font description
-            oldSize = pango_font_description_get_size(m_fontdesc);
-            pango_font_description_set_size(m_fontdesc, int(oldSize * m_scaleY));
-
-             // actually apply scaled font
-             pango_layout_set_font_description( m_layout, m_fontdesc );
-        }
-
-        int w, h;
-        pango_layout_get_pixel_size(m_layout, &w, &h);
-
-        const GdkColor* bg_col = NULL;
-        if (m_backgroundMode == wxBRUSHSTYLE_SOLID)
-            bg_col = m_textBackgroundColour.GetColor();
-
-        // rotate the text
-        PangoMatrix matrix = PANGO_MATRIX_INIT;
-        pango_matrix_rotate (&matrix, angle);
-        pango_context_set_matrix (m_context, &matrix);
-        pango_layout_context_changed (m_layout);
-
         // To be compatible with MSW, the rotation axis must be in the old
         // top-left corner.
         // Calculate the vertices of the rotated rectangle containing the text,
         // relative to the old top-left vertex.
-        // We could use the matrix for this, but it's simpler with trignonometry.
-        double rad = DegToRad(angle);
         // the rectangle vertices are counted clockwise with the first one
         // being at (0, 0)
-        double x2 = w * cos(rad);
-        double y2 = -w * sin(rad);   // y axis points to the bottom, hence minus
-        double x4 = h * sin(rad);
-        double y4 = h * cos(rad);
+        double x2 = w * matrix.xx;
+        double y2 = w * matrix.yx;
+        double x4 = h * matrix.xy;
+        double y4 = h * matrix.yy;
         double x3 = x4 + x2;
         double y3 = y4 + y2;
         // Then we calculate max and min of the rotated rectangle.
@@ -1542,132 +1452,17 @@ void wxWindowDCImpl::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord
                 maxY = (wxCoord)(dmax(dmax(0, y2), dmax(y3, y4)) + 0.5),
                 minX = (wxCoord)(dmin(dmin(0, x2), dmin(x3, x4)) - 0.5),
                 minY = (wxCoord)(dmin(dmin(0, y2), dmin(y3, y4)) - 0.5);
-
-        gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x+minX, y+minY,
-                                    m_layout, NULL, bg_col);
-
-        if (m_font.GetUnderlined())
-            pango_layout_set_attributes(m_layout, NULL);
-
-        // clean up the transformation matrix
-        pango_context_set_matrix(m_context, NULL);
-
-        if (isScaled)
-        {
-             // reset unscaled size
-             pango_font_description_set_size( m_fontdesc, oldSize );
-
-             // actually apply unscaled font
-             pango_layout_set_font_description( m_layout, m_fontdesc );
-        }
-
-        CalcBoundingBox(x+minX, y+minY);
-        CalcBoundingBox(x+maxX, y+maxY);
-    }
-    else
-#endif //__WXGTK26__
-    {
-#if wxUSE_IMAGE
-    if ( wxIsNullDouble(angle) )
-    {
-        DoDrawText(text, x, y);
-        return;
+        x += minX;
+        y += minY;
+        CalcBoundingBox(DeviceToLogicalX(x), DeviceToLogicalY(y));
+        CalcBoundingBox(DeviceToLogicalX(x + maxX - minX), DeviceToLogicalY(y + maxY - minY));
     }
 
-    wxCoord w;
-    wxCoord h;
-
-    // TODO: implement later without GdkFont for GTK 2.0
-    DoGetTextExtent(text, &w, &h, NULL,NULL, &m_font);
-
-    // draw the string normally
-    wxBitmap src(w, h);
-    wxMemoryDC dc;
-    dc.SelectObject(src);
-    dc.SetFont(GetFont());
-    dc.SetBackground(*wxBLACK_BRUSH);
-    dc.SetBrush(*wxBLACK_BRUSH);
-    dc.Clear();
-    dc.SetTextForeground( *wxWHITE );
-    dc.DrawText(text, 0, 0);
-    dc.SelectObject(wxNullBitmap);
-
-    // Calculate the size of the rotated bounding box.
-    double rad = DegToRad(angle);
-    double dx = cos(rad),
-           dy = sin(rad);
-
-    // the rectngle vertices are counted clockwise with the first one being at
-    // (0, 0) (or, rather, at (x, y))
-    double x2 = w*dx,
-           y2 = -w*dy;      // y axis points to the bottom, hence minus
-    double x4 = h*dy,
-           y4 = h*dx;
-    double x3 = x4 + x2,
-           y3 = y4 + y2;
-
-    // calc max and min
-    wxCoord maxX = (wxCoord)(dmax(x2, dmax(x3, x4)) + 0.5),
-            maxY = (wxCoord)(dmax(y2, dmax(y3, y4)) + 0.5),
-            minX = (wxCoord)(dmin(x2, dmin(x3, x4)) - 0.5),
-            minY = (wxCoord)(dmin(y2, dmin(y3, y4)) - 0.5);
-
-
-    wxImage image = src.ConvertToImage();
-
-    image.ConvertColourToAlpha( m_textForegroundColour.Red(),
-                                m_textForegroundColour.Green(),
-                                m_textForegroundColour.Blue() );
-    image = image.Rotate( rad, wxPoint(0,0) );
-
-    int i_angle = (int) angle;
-    i_angle = i_angle % 360;
-    if (i_angle < 0)
-        i_angle += 360;
-    int xoffset = 0;
-    if ((i_angle >= 90.0) && (i_angle < 270.0))
-        xoffset = image.GetWidth();
-    int yoffset = 0;
-    if ((i_angle >= 0.0) && (i_angle < 180.0))
-        yoffset = image.GetHeight();
-
-    if ((i_angle >= 0) && (i_angle < 90))
-        yoffset -= (int)( cos(rad)*h );
-    if ((i_angle >= 90) && (i_angle < 180))
-        xoffset -= (int)( sin(rad)*h );
-    if ((i_angle >= 180) && (i_angle < 270))
-        yoffset -= (int)( cos(rad)*h );
-    if ((i_angle >= 270) && (i_angle < 360))
-        xoffset -= (int)( sin(rad)*h );
-
-    int i_x = x - xoffset;
-    int i_y = y - yoffset;
-
-    src = image;
-    DoDrawBitmap( src, i_x, i_y, true );
-
-
-    // it would be better to draw with non underlined font and draw the line
-    // manually here (it would be more straight...)
-#if 0
-    if ( m_font.GetUnderlined() )
-    {
-        gdk_draw_line( m_gdkwindow, m_textGC,
-                       XLOG2DEV(x + x4), YLOG2DEV(y + y4 + font->descent),
-                       XLOG2DEV(x + x3), YLOG2DEV(y + y3 + font->descent));
-    }
-#endif // 0
-
-    // update the bounding box
-    CalcBoundingBox(x + minX, y + minY);
-    CalcBoundingBox(x + maxX, y + maxY);
-#else // !wxUSE_IMAGE
-    wxUnusedVar(text);
-    wxUnusedVar(x);
-    wxUnusedVar(y);
-    wxUnusedVar(angle);
-#endif // wxUSE_IMAGE/!wxUSE_IMAGE
-    }
+    gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x, y, m_layout, NULL, bg_col);
+
+    pango_context_set_matrix(m_context, NULL);
+    if (setAttrs)
+        pango_layout_set_attributes(m_layout, NULL);
 }
 
 void wxWindowDCImpl::DoGetTextExtent(const wxString &string,
@@ -1675,100 +1470,27 @@ void wxWindowDCImpl::DoGetTextExtent(const wxString &string,
                                  wxCoord *descent, wxCoord *externalLeading,
                                  const wxFont *theFont) const
 {
-    if ( width )
-        *width = 0;
-    if ( height )
-        *height = 0;
-    if ( descent )
-        *descent = 0;
-    if ( externalLeading )
-        *externalLeading = 0;
-
-    if (string.empty())
-        return;
-
-    // ensure that theFont is always non-NULL
+    // ensure we work with a valid font
+    const wxFont *fontToUse;
     if ( !theFont || !theFont->IsOk() )
-        theFont = &m_font;
-
-    // and use it if it's valid
-    if ( theFont->IsOk() )
-    {
-        pango_layout_set_font_description
-        (
-            m_layout,
-            theFont->GetNativeFontInfo()->description
-        );
-    }
-
-    // Set layout's text
-    const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(string, *theFont);
-    if ( !dataUTF8 )
-    {
-        // hardly ideal, but what else can we do if conversion failed?
-        return;
-    }
-
-    pango_layout_set_text(m_layout, dataUTF8, -1);
+        fontToUse = &m_font;
+    else
+        fontToUse = theFont;
 
-    int h;
-    pango_layout_get_pixel_size(m_layout, width, &h);
-    if (descent)
-    {
-        PangoLayoutIter *iter = pango_layout_get_iter(m_layout);
-        int baseline = pango_layout_iter_get_baseline(iter);
-        pango_layout_iter_free(iter);
-        *descent = h - PANGO_PIXELS(baseline);
-    }
-    if (height)
-        *height = h;
+    wxCHECK_RET( fontToUse->IsOk(), wxT("invalid font") );
 
-    // Reset old font description
-    if (theFont->IsOk())
-        pango_layout_set_font_description( m_layout, m_fontdesc );
+    wxTextMeasure txm(GetOwner(), fontToUse);
+    txm.GetTextExtent(string, width, height, descent, externalLeading);
 }
 
 
 bool wxWindowDCImpl::DoGetPartialTextExtents(const wxString& text,
                                          wxArrayInt& widths) const
 {
-    const size_t len = text.length();
-    widths.Empty();
-    widths.Add(0, len);
-
-    if (text.empty())
-        return true;
-
-    // Set layout's text
-    const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(text, m_font);
-    if ( !dataUTF8 )
-    {
-        // hardly ideal, but what else can we do if conversion failed?
-        wxLogLastError(wxT("DoGetPartialTextExtents"));
-        return false;
-    }
-
-    pango_layout_set_text(m_layout, dataUTF8, -1);
+    wxCHECK_MSG( m_font.IsOk(), false, wxT("Invalid font") );
 
-    // Calculate the position of each character based on the widths of
-    // the previous characters
-
-    // Code borrowed from Scintilla's PlatGTK
-    PangoLayoutIter *iter = pango_layout_get_iter(m_layout);
-    PangoRectangle pos;
-    pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
-    size_t i = 0;
-    while (pango_layout_iter_next_cluster(iter))
-    {
-        pango_layout_iter_get_cluster_extents(iter, NULL, &pos);
-        int position = PANGO_PIXELS(pos.x);
-        widths[i++] = position;
-    }
-    while (i < len)
-        widths[i++] = PANGO_PIXELS(pos.x + pos.width);
-    pango_layout_iter_free(iter);
-
-    return true;
+    wxTextMeasure txm(GetOwner(), &m_font);
+    return txm.GetPartialTextExtents(text, widths, m_scaleX);
 }
 
 
@@ -1996,7 +1718,7 @@ void wxWindowDCImpl::SetBrush( const wxBrush &brush )
     if ((m_brush.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
     {
         gdk_gc_set_fill( m_textGC, GDK_OPAQUE_STIPPLED);
-        gdk_gc_set_stipple( m_textGC, m_brush.GetStipple()->GetMask()->GetBitmap() );
+        gdk_gc_set_stipple( m_textGC, *m_brush.GetStipple()->GetMask() );
     }
 
     if (m_brush.IsHatch())
@@ -2289,7 +2011,6 @@ int wxWindowDCImpl::GetDepth() const
     return gdk_drawable_get_depth(m_gdkwindow);
 }
 
-
 //-----------------------------------------------------------------------------
 // wxClientDCImpl
 //-----------------------------------------------------------------------------
@@ -2319,7 +2040,7 @@ void wxClientDCImpl::DoGetSize(int *width, int *height) const
 {
     wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") );
 
-    m_window->GetClientSize( width, height );
+    m_window->GetClientSize(width, height);
 }
 
 //-----------------------------------------------------------------------------