X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/95724b1aa2f7248091cdec506d4678b4fbcea20d..e83c4d401206ff9d5568f03ade6d9713c57bdd51:/src/gtk/dcclient.cpp diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 01e03030ab..680ab2c6cd 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -1,24 +1,41 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: dcclient.cpp -// Purpose: +// Name: src/gtk/dcclient.cpp +// Purpose: wxWindowDCImpl implementation // Author: Robert Roebling // RCS-ID: $Id$ -// Copyright: (c) 1998 Robert Roebling, Markus Holzem, Chris Breeze -// Licence: wxWindows licence +// Copyright: (c) 1998 Robert Roebling, Chris Breeze +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "dcclient.h" +// For compilers that support precompilation, includes "wx.h". +#include "wx/wxprec.h" + +#include "wx/gtk/dcclient.h" + +#ifndef WX_PRECOMP + #include "wx/window.h" + #include "wx/log.h" + #include "wx/dcmemory.h" + #include "wx/math.h" + #include "wx/image.h" + #include "wx/module.h" #endif -#include "wx/dcclient.h" -#include "wx/dcmemory.h" -#include "wx/image.h" -#include "wx/gtk/win_gtk.h" +#include "wx/fontutil.h" + +#include "wx/gtk/private.h" +#include "wx/gtk/private/object.h" + +//----------------------------------------------------------------------------- +// local defines +//----------------------------------------------------------------------------- -#include // for floating-point functions -#include -#include +#define XLOG2DEV(x) LogicalToDeviceX(x) +#define XLOG2DEVREL(x) LogicalToDeviceXRel(x) +#define YLOG2DEV(y) LogicalToDeviceY(y) +#define YLOG2DEVREL(y) LogicalToDeviceYRel(y) + +#define USE_PAINT_REGION 1 //----------------------------------------------------------------------------- // local data @@ -30,130 +47,276 @@ #include "horiz.xbm" #include "verti.xbm" #include "cross.xbm" -#define num_hatches 6 -static GdkPixmap *hatches[num_hatches]; -static GdkPixmap **hatch_bitmap = (GdkPixmap **) NULL; +static GdkPixmap* hatches[wxBRUSHSTYLE_LAST_HATCH - wxBRUSHSTYLE_FIRST_HATCH + 1]; //----------------------------------------------------------------------------- // constants //----------------------------------------------------------------------------- -const double RAD2DEG = 180.0 / M_PI; +static const double RAD2DEG = 180.0 / M_PI; + +// ---------------------------------------------------------------------------- +// private functions +// ---------------------------------------------------------------------------- + +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(name ## _bits), \ + name ## _width, \ + name ## _height \ + ) + + switch (style) + { + case wxBRUSHSTYLE_BDIAGONAL_HATCH: + hatches[i] = CREATE_FROM_XBM_DATA(bdiag); + break; + case wxBRUSHSTYLE_CROSSDIAG_HATCH: + hatches[i] = CREATE_FROM_XBM_DATA(cdiag); + break; + case wxBRUSHSTYLE_CROSS_HATCH: + hatches[i] = CREATE_FROM_XBM_DATA(cross); + break; + case wxBRUSHSTYLE_FDIAGONAL_HATCH: + hatches[i] = CREATE_FROM_XBM_DATA(fdiag); + break; + case wxBRUSHSTYLE_HORIZONTAL_HATCH: + hatches[i] = CREATE_FROM_XBM_DATA(horiz); + break; + case wxBRUSHSTYLE_VERTICAL_HATCH: + hatches[i] = CREATE_FROM_XBM_DATA(verti); + break; + } + +#undef CREATE_FROM_XBM_DATA + } + return hatches[i]; +} //----------------------------------------------------------------------------- -// temporary implementation of the missing GDK function +// Implement Pool of Graphic contexts. Creating them takes too much time. //----------------------------------------------------------------------------- -#include "gdk/gdkprivate.h" - -void gdk_draw_bitmap (GdkDrawable *drawable, - GdkGC *gc, - GdkDrawable *src, - gint xsrc, - gint ysrc, - gint xdest, - gint ydest, - gint width, - gint height) -{ - GdkWindowPrivate *drawable_private; - GdkWindowPrivate *src_private; - GdkGCPrivate *gc_private; - - g_return_if_fail (drawable != NULL); - g_return_if_fail (src != NULL); - g_return_if_fail (gc != NULL); - - drawable_private = (GdkWindowPrivate*) drawable; - src_private = (GdkWindowPrivate*) src; - if (drawable_private->destroyed || src_private->destroyed) - return; +enum wxPoolGCType +{ + wxGC_ERROR = 0, + wxTEXT_MONO, + wxBG_MONO, + wxPEN_MONO, + wxBRUSH_MONO, + wxTEXT_COLOUR, + wxBG_COLOUR, + wxPEN_COLOUR, + wxBRUSH_COLOUR, + wxTEXT_SCREEN, + wxBG_SCREEN, + wxPEN_SCREEN, + wxBRUSH_SCREEN, + wxTEXT_COLOUR_ALPHA, + wxBG_COLOUR_ALPHA, + wxPEN_COLOUR_ALPHA, + wxBRUSH_COLOUR_ALPHA +}; + +struct wxGC +{ + GdkGC *m_gc; + wxPoolGCType m_type; + bool m_used; +}; + +#define GC_POOL_ALLOC_SIZE 100 - gc_private = (GdkGCPrivate*) gc; +static int wxGCPoolSize = 0; - if (width == -1) width = src_private->width; - if (height == -1) height = src_private->height; +static wxGC *wxGCPool = NULL; - XCopyPlane( drawable_private->xdisplay, - src_private->xwindow, - drawable_private->xwindow, - gc_private->xgc, - xsrc, ysrc, - width, height, - xdest, ydest, - 1 ); +static void wxInitGCPool() +{ + // This really could wait until the first call to + // wxGetPoolGC, but we will make the first allocation + // now when other initialization is being performed. + + // Set initial pool size. + wxGCPoolSize = GC_POOL_ALLOC_SIZE; + + // Allocate initial pool. + wxGCPool = (wxGC *)malloc(wxGCPoolSize * sizeof(wxGC)); + if (wxGCPool == NULL) + { + // If we cannot malloc, then fail with error + // when debug is enabled. If debug is not enabled, + // the problem will eventually get caught + // in wxGetPoolGC. + wxFAIL_MSG( wxT("Cannot allocate GC pool") ); + return; + } + + // Zero initial pool. + memset(wxGCPool, 0, wxGCPoolSize * sizeof(wxGC)); } -/* - * compare two doubles and return the larger rounded - * to the nearest int - */ -static int roundmax(double a, double b) +static void wxCleanUpGCPool() { - return (int)((a > b ? a : b) + 0.5); + for (int i = 0; i < wxGCPoolSize; i++) + { + if (wxGCPool[i].m_gc) + g_object_unref (wxGCPool[i].m_gc); + } + + free(wxGCPool); + wxGCPool = NULL; + wxGCPoolSize = 0; } -/* - * compare two doubles and return the smaller rounded - * to the nearest int - */ -static int roundmin(double a, double b) +static GdkGC* wxGetPoolGC( GdkWindow *window, wxPoolGCType type ) { - return (int)((a < b ? a : b) - 0.5); + wxGC *pptr; + + // Look for an available GC. + for (int i = 0; i < wxGCPoolSize; i++) + { + if (!wxGCPool[i].m_gc) + { + wxGCPool[i].m_gc = gdk_gc_new( window ); + gdk_gc_set_exposures( wxGCPool[i].m_gc, FALSE ); + wxGCPool[i].m_type = type; + wxGCPool[i].m_used = false; + } + if ((!wxGCPool[i].m_used) && (wxGCPool[i].m_type == type)) + { + wxGCPool[i].m_used = true; + return wxGCPool[i].m_gc; + } + } + + // We did not find an available GC. + // We need to grow the GC pool. + pptr = (wxGC *)realloc(wxGCPool, + (wxGCPoolSize + GC_POOL_ALLOC_SIZE)*sizeof(wxGC)); + if (pptr != NULL) + { + // Initialize newly allocated pool. + wxGCPool = pptr; + memset(&wxGCPool[wxGCPoolSize], 0, + GC_POOL_ALLOC_SIZE*sizeof(wxGC)); + + // Initialize entry we will return. + wxGCPool[wxGCPoolSize].m_gc = gdk_gc_new( window ); + gdk_gc_set_exposures( wxGCPool[wxGCPoolSize].m_gc, FALSE ); + wxGCPool[wxGCPoolSize].m_type = type; + wxGCPool[wxGCPoolSize].m_used = true; + + // Set new value of pool size. + wxGCPoolSize += GC_POOL_ALLOC_SIZE; + + // Return newly allocated entry. + return wxGCPool[wxGCPoolSize-GC_POOL_ALLOC_SIZE].m_gc; + } + + // The realloc failed. Fall through to error. + wxFAIL_MSG( wxT("No GC available") ); + + return NULL; } +static void wxFreePoolGC( GdkGC *gc ) +{ + for (int i = 0; i < wxGCPoolSize; i++) + { + if (wxGCPool[i].m_gc == gc) + { + wxGCPool[i].m_used = false; + return; + } + } + + wxFAIL_MSG( wxT("Wrong GC") ); +} //----------------------------------------------------------------------------- // wxWindowDC //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxWindowDC, wxDC) +IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxGTKDCImpl) -wxWindowDC::wxWindowDC() +wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) : + wxGTKDCImpl( owner ) { - m_penGC = (GdkGC *) NULL; - m_brushGC = (GdkGC *) NULL; - m_textGC = (GdkGC *) NULL; - m_bgGC = (GdkGC *) NULL; - m_cmap = (GdkColormap *) NULL; - m_isMemDC = FALSE; - m_owner = (wxWindow *)NULL; + m_gdkwindow = NULL; + m_penGC = NULL; + m_brushGC = NULL; + m_textGC = NULL; + m_bgGC = NULL; + m_cmap = NULL; + m_isScreenDC = false; + m_context = NULL; + m_layout = NULL; + m_fontdesc = NULL; } -wxWindowDC::wxWindowDC( wxWindow *window ) +wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) : + wxGTKDCImpl( owner ) { - m_penGC = (GdkGC *) NULL; - m_brushGC = (GdkGC *) NULL; - m_textGC = (GdkGC *) NULL; - m_bgGC = (GdkGC *) NULL; - m_cmap = (GdkColormap *) NULL; - m_owner = (wxWindow *)NULL; - m_isMemDC = FALSE; - m_font = window->GetFont(); - wxASSERT_MSG( window, wxT("DC needs a window") ); + m_gdkwindow = NULL; + m_penGC = NULL; + m_brushGC = NULL; + m_textGC = NULL; + m_bgGC = NULL; + m_cmap = NULL; + m_isScreenDC = false; + m_font = window->GetFont(); + GtkWidget *widget = window->m_wxwindow; + m_gdkwindow = window->GTKGetDrawingWindow(); - wxASSERT_MSG( widget, wxT("DC needs a widget") ); + // Some controls don't have m_wxwindow - like wxStaticBox, but the user + // code should still be able to create wxClientDCs for them + if ( !widget ) + { + widget = window->m_widget; + + wxCHECK_RET(widget, "DC needs a widget"); + + m_gdkwindow = widget->window; + if (!gtk_widget_get_has_window(widget)) + SetDeviceLocalOrigin(widget->allocation.x, widget->allocation.y); + } - GtkPizza *pizza = GTK_PIZZA( widget ); - m_window = pizza->bin_window; + m_context = window->GTKGetPangoDefaultContext(); + m_layout = pango_layout_new( m_context ); + m_fontdesc = pango_font_description_copy( widget->style->font_desc ); - /* not realized ? */ - if (!m_window) + // Window not realized ? + if (!m_gdkwindow) { - /* don't report problems */ - m_ok = TRUE; + // Don't report problems as per MSW. + m_ok = true; return; } - if (window->m_wxwindow) - m_cmap = gtk_widget_get_colormap( window->m_wxwindow ); - else - m_cmap = gtk_widget_get_colormap( window->m_widget ); + m_cmap = gtk_widget_get_colormap(widget); SetUpDC(); @@ -164,63 +327,260 @@ wxWindowDC::wxWindowDC( wxWindow *window ) is white whereas a window might assume gray to be the standard (as e.g. wxStatusBar) */ - m_owner = window; + m_window = window; + + if (m_window && m_window->m_wxwindow && + (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) + { + // reverse sense + m_signX = -1; + + // origin in the upper right corner + m_deviceOriginX = m_window->GetClientSize().x; + } } -wxWindowDC::~wxWindowDC() +wxWindowDCImpl::~wxWindowDCImpl() { Destroy(); + + if (m_layout) + g_object_unref (m_layout); + if (m_fontdesc) + pango_font_description_free( m_fontdesc ); } -void wxWindowDC::DoFloodFill( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y), - const wxColour &WXUNUSED(col), int WXUNUSED(style) ) +void wxWindowDCImpl::SetUpDC( bool isMemDC ) { - wxFAIL_MSG( wxT("wxWindowDC::DoFloodFill not implemented") ); + m_ok = true; + + wxASSERT_MSG( !m_penGC, wxT("GCs already created") ); + + bool done = false; + + if ((isMemDC) && (GetSelectedBitmap().IsOk())) + { + if (GetSelectedBitmap().GetDepth() == 1) + { + m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_MONO ); + m_brushGC = wxGetPoolGC( m_gdkwindow, wxBRUSH_MONO ); + m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_MONO ); + m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_MONO ); + done = true; + } + } + + if (!done) + { + if (m_isScreenDC) + { + m_penGC = wxGetPoolGC( m_gdkwindow, wxPEN_SCREEN ); + m_brushGC = wxGetPoolGC( m_gdkwindow, wxBRUSH_SCREEN ); + 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 ); + m_brushGC = wxGetPoolGC( m_gdkwindow, wxBRUSH_COLOUR ); + m_textGC = wxGetPoolGC( m_gdkwindow, wxTEXT_COLOUR ); + m_bgGC = wxGetPoolGC( m_gdkwindow, wxBG_COLOUR ); + } + } + + /* background colour */ + m_backgroundBrush = *wxWHITE_BRUSH; + m_backgroundBrush.GetColour().CalcPixel( m_cmap ); + const GdkColor *bg_col = m_backgroundBrush.GetColour().GetColor(); + + /* m_textGC */ + m_textForegroundColour.CalcPixel( m_cmap ); + gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() ); + + m_textBackgroundColour.CalcPixel( m_cmap ); + gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() ); + + gdk_gc_set_fill( m_textGC, GDK_SOLID ); + + gdk_gc_set_colormap( m_textGC, m_cmap ); + + /* m_penGC */ + m_pen.GetColour().CalcPixel( m_cmap ); + gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() ); + gdk_gc_set_background( m_penGC, bg_col ); + + gdk_gc_set_line_attributes( m_penGC, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_ROUND ); + + /* m_brushGC */ + m_brush.GetColour().CalcPixel( m_cmap ); + gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() ); + gdk_gc_set_background( m_brushGC, bg_col ); + + gdk_gc_set_fill( m_brushGC, GDK_SOLID ); + + /* m_bgGC */ + gdk_gc_set_background( m_bgGC, bg_col ); + gdk_gc_set_foreground( m_bgGC, bg_col ); + + gdk_gc_set_fill( m_bgGC, GDK_SOLID ); + + /* ROPs */ + gdk_gc_set_function( m_textGC, GDK_COPY ); + gdk_gc_set_function( m_brushGC, GDK_COPY ); + gdk_gc_set_function( m_penGC, GDK_COPY ); + + /* clipping */ + gdk_gc_set_clip_rectangle( m_penGC, NULL ); + gdk_gc_set_clip_rectangle( m_brushGC, NULL ); + gdk_gc_set_clip_rectangle( m_textGC, NULL ); + gdk_gc_set_clip_rectangle( m_bgGC, NULL ); } -bool wxWindowDC::DoGetPixel( wxCoord WXUNUSED(x1), wxCoord WXUNUSED(y1), wxColour *WXUNUSED(col) ) const +void wxWindowDCImpl::DoGetSize( int* width, int* height ) const { - wxFAIL_MSG( wxT("wxWindowDC::DoGetPixel not implemented") ); - return FALSE; + wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); + + m_window->GetSize(width, height); } -void wxWindowDC::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 ) +bool wxWindowDCImpl::DoFloodFill(wxCoord x, wxCoord y, + const wxColour& col, wxFloodFillStyle style) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); +#if wxUSE_IMAGE + extern bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y, + const wxColour & col, wxFloodFillStyle style); + + return wxDoFloodFill( GetOwner(), x, y, col, style); +#else + wxUnusedVar(x); + wxUnusedVar(y); + wxUnusedVar(col); + wxUnusedVar(style); + + return false; +#endif +} - if (m_pen.GetStyle() != wxTRANSPARENT) +bool wxWindowDCImpl::DoGetPixel( wxCoord x1, wxCoord y1, wxColour *col ) const +{ + GdkImage* image = NULL; + if (m_gdkwindow) { - if (m_window) - gdk_draw_line( m_window, m_penGC, XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2) ); + const int x = LogicalToDeviceX(x1); + const int y = LogicalToDeviceY(y1); + wxRect rect; + gdk_drawable_get_size(m_gdkwindow, &rect.width, &rect.height); + if (rect.Contains(x, y)) + image = gdk_drawable_get_image(m_gdkwindow, x, y, 1, 1); + } + if (image == NULL) + { + *col = wxColour(); + return false; + } + GdkColormap* colormap = gdk_image_get_colormap(image); + const unsigned pixel = gdk_image_get_pixel(image, 0, 0); + if (colormap == NULL) + *col = pixel ? m_textForegroundColour : m_textBackgroundColour; + else + { + GdkColor c; + gdk_colormap_query_color(colormap, pixel, &c); + col->Set(c.red >> 8, c.green >> 8, c.blue >> 8); + } + g_object_unref(image); + return true; +} + +void wxWindowDCImpl::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 ) +{ + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + if ( m_pen.IsNonTransparent() ) + { + if (m_gdkwindow) + gdk_draw_line( m_gdkwindow, m_penGC, XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2) ); CalcBoundingBox(x1, y1); CalcBoundingBox(x2, y2); } } -void wxWindowDC::DoCrossHair( wxCoord x, wxCoord y ) +void wxWindowDCImpl::DoCrossHair( wxCoord x, wxCoord y ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (m_pen.GetStyle() != wxTRANSPARENT) + if ( m_pen.IsNonTransparent() ) { int w = 0; int h = 0; - GetSize( &w, &h ); + GetOwner()->GetSize( &w, &h ); wxCoord xx = XLOG2DEV(x); wxCoord yy = YLOG2DEV(y); - if (m_window) + if (m_gdkwindow) { - gdk_draw_line( m_window, m_penGC, 0, yy, XLOG2DEVREL(w), yy ); - gdk_draw_line( m_window, m_penGC, xx, 0, xx, YLOG2DEVREL(h) ); + gdk_draw_line( m_gdkwindow, m_penGC, 0, yy, XLOG2DEVREL(w), yy ); + gdk_draw_line( m_gdkwindow, m_penGC, xx, 0, xx, YLOG2DEVREL(h) ); } } } -void wxWindowDC::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, +void wxWindowDCImpl::DrawingSetup(GdkGC*& gc, bool& originChanged) +{ + gc = m_brushGC; + GdkPixmap* pixmap = NULL; + const int style = m_brush.GetStyle(); + + if (style == wxBRUSHSTYLE_STIPPLE || style == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE) + { + const wxBitmap* stipple = m_brush.GetStipple(); + if (stipple->IsOk()) + { + if (style == wxBRUSHSTYLE_STIPPLE) + pixmap = stipple->GetPixmap(); + else if (stipple->GetMask()) + { + pixmap = stipple->GetPixmap(); + gc = m_textGC; + } + } + } + else if (m_brush.IsHatch()) + { + pixmap = GetHatch(style); + } + + int origin_x = 0; + int origin_y = 0; + if (pixmap) + { + int w, h; + gdk_drawable_get_size(pixmap, &w, &h); + origin_x = m_deviceOriginX % w; + origin_y = m_deviceOriginY % h; + } + + originChanged = origin_x || origin_y; + if (originChanged) + gdk_gc_set_ts_origin(gc, origin_x, origin_y); +} + +void wxWindowDCImpl::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord xc, wxCoord yc ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); wxCoord xx1 = XLOG2DEV(x1); wxCoord yy1 = YLOG2DEV(y1); @@ -239,10 +599,10 @@ void wxWindowDC::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, radius1 = 0.0; radius2 = 360.0; } - else - if (radius == 0.0) + else if ( wxIsNullDouble(radius) ) { - radius1 = radius2 = 0.0; + radius1 = + radius2 = 0.0; } else { @@ -258,22 +618,39 @@ void wxWindowDC::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, while (alpha2 <= 0) alpha2 += 360*64; while (alpha1 > 360*64) alpha1 -= 360*64; - if (m_window) + if (m_gdkwindow) { - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_brushGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 ); + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); + + gdk_draw_arc(m_gdkwindow, gc, true, xxc-r, yyc-r, 2*r, 2*r, alpha1, alpha2); + + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); + } - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_penGC, FALSE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 ); + 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.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 ); + } + } } CalcBoundingBox (x1, y1); CalcBoundingBox (x2, y2); } -void wxWindowDC::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double sa, double ea ) +void wxWindowDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double sa, double ea ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); wxCoord xx = XLOG2DEV(x); wxCoord yy = YLOG2DEV(y); @@ -284,94 +661,144 @@ void wxWindowDC::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxCoord if (ww < 0) { ww = -ww; xx = xx - ww; } if (hh < 0) { hh = -hh; yy = yy - hh; } - if (m_window) + if (m_gdkwindow) { wxCoord start = wxCoord(sa * 64.0); - wxCoord end = wxCoord(ea * 64.0); + wxCoord end = wxCoord((ea-sa) * 64.0); + + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end ); + gdk_draw_arc(m_gdkwindow, gc, true, xx, yy, ww, hh, start, end); - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, start, end ); + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); + } + + if ( m_pen.IsNonTransparent() ) + gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx, yy, ww, hh, start, end ); } CalcBoundingBox (x, y); CalcBoundingBox (x + width, y + height); } -void wxWindowDC::DoDrawPoint( wxCoord x, wxCoord y ) +void wxWindowDCImpl::DoDrawPoint( wxCoord x, wxCoord y ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if ((m_pen.GetStyle() != wxTRANSPARENT) && m_window) - gdk_draw_point( m_window, m_penGC, XLOG2DEV(x), YLOG2DEV(y) ); + if ( m_pen.IsNonTransparent() && m_gdkwindow ) + gdk_draw_point( m_gdkwindow, m_penGC, XLOG2DEV(x), YLOG2DEV(y) ); CalcBoundingBox (x, y); } -void wxWindowDC::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset ) +void wxWindowDCImpl::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (m_pen.GetStyle() == wxTRANSPARENT) return; if (n <= 0) return; - CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset ); + if ( m_pen.IsTransparent() ) + return; - for (int i = 0; i < n-1; i++) - { - wxCoord x1 = XLOG2DEV(points[i].x + xoffset); - wxCoord x2 = XLOG2DEV(points[i+1].x + xoffset); - wxCoord y1 = YLOG2DEV(points[i].y + yoffset); // oh, what a waste - wxCoord y2 = YLOG2DEV(points[i+1].y + yoffset); + //Check, if scaling is necessary + const bool doScale = + xoffset != 0 || yoffset != 0 || XLOG2DEV(10) != 10 || YLOG2DEV(10) != 10; - if (m_window) - gdk_draw_line( m_window, m_penGC, x1, y1, x2, y2 ); + // GdkPoint and wxPoint have the same memory layout, so we can cast one to the other + GdkPoint* gpts = reinterpret_cast(points); + + if (doScale) + gpts = new GdkPoint[n]; - CalcBoundingBox( points[i+1].x + xoffset, points[i+1].y + yoffset ); + 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); + } + CalcBoundingBox(points[i].x + xoffset, points[i].y + yoffset); } + + if (m_gdkwindow) + gdk_draw_lines( m_gdkwindow, m_penGC, gpts, n); + + if (doScale) + delete[] gpts; } -void wxWindowDC::DoDrawPolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, int WXUNUSED(fillStyle) ) +void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[], + wxCoord xoffset, wxCoord yoffset, + wxPolygonFillMode WXUNUSED(fillStyle) ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); if (n <= 0) return; - GdkPoint *gdkpoints = new GdkPoint[n+1]; + //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* gdkpoints = reinterpret_cast(points); + + if (doScale) + gdkpoints = new GdkPoint[n]; + int i; for (i = 0 ; i < n ; i++) { - gdkpoints[i].x = XLOG2DEV(points[i].x + xoffset); - gdkpoints[i].y = YLOG2DEV(points[i].y + yoffset); - - CalcBoundingBox( points[i].x + xoffset, points[i].y + yoffset ); + if (doScale) + { + gdkpoints[i].x = XLOG2DEV(points[i].x + xoffset); + gdkpoints[i].y = YLOG2DEV(points[i].y + yoffset); + } + CalcBoundingBox(points[i].x + xoffset, points[i].y + yoffset); } - if ((m_brush.GetStyle() != wxTRANSPARENT) && m_window) - gdk_draw_polygon (m_window, m_brushGC, TRUE, gdkpoints, n); + if (m_gdkwindow) + { + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); - // To do: Fillstyle + gdk_draw_polygon(m_gdkwindow, gc, true, gdkpoints, n); - if ((m_pen.GetStyle() != wxTRANSPARENT) && m_window) - { - for (i = 0 ; i < n ; i++) + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); + } + + if ( m_pen.IsNonTransparent() ) { - gdk_draw_line( m_window, m_penGC, - gdkpoints[i%n].x, - gdkpoints[i%n].y, - gdkpoints[(i+1)%n].x, - gdkpoints[(i+1)%n].y); +/* + for (i = 0 ; i < n ; i++) + { + gdk_draw_line( m_gdkwindow, m_penGC, + gdkpoints[i%n].x, + gdkpoints[i%n].y, + gdkpoints[(i+1)%n].x, + gdkpoints[(i+1)%n].y); + } +*/ + gdk_draw_polygon( m_gdkwindow, m_penGC, FALSE, gdkpoints, n ); + } } - delete[] gdkpoints; + if (doScale) + delete[] gdkpoints; } -void wxWindowDC::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) +void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); wxCoord xx = XLOG2DEV(x); wxCoord yy = YLOG2DEV(y); @@ -385,22 +812,58 @@ void wxWindowDC::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord h if (ww < 0) { ww = -ww; xx = xx - ww; } if (hh < 0) { hh = -hh; yy = yy - hh; } - if (m_window) + if (m_gdkwindow) { - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh ); + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); + + gdk_draw_rectangle(m_gdkwindow, gc, true, xx, yy, ww, hh); + + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); + } + + if ( m_pen.IsNonTransparent() ) + { + if ((m_pen.GetWidth() == 2) && (m_pen.GetCap() == wxCAP_ROUND) && + (m_pen.GetJoin() == wxJOIN_ROUND) && (m_pen.GetStyle() == wxPENSTYLE_SOLID)) + { + // Use 2 1-line rects instead + gdk_gc_set_line_attributes( m_penGC, 1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + + if (m_signX == -1) + { + // Different for RTL + gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx+1, yy, ww-2, hh-2 ); + gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx, yy-1, ww, hh ); + } + else + { + gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx, yy, ww-2, hh-2 ); + gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx-1, yy-1, ww, hh ); + } - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy, ww-1, hh-1 ); + // reset + gdk_gc_set_line_attributes( m_penGC, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + } + else + { + // Just use X11 for other cases + gdk_draw_rectangle( m_gdkwindow, m_penGC, FALSE, xx, yy, ww-1, hh-1 ); + } + } } CalcBoundingBox( x, y ); CalcBoundingBox( x + width, y + height ); } -void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius ) +void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); if (radius < 0.0) radius = - radius * ((width < height) ? width : height); @@ -418,7 +881,7 @@ void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wx // X drawing errors with small radii if (rr == 0) { - DrawRectangle( x, y, width, height ); + DoDrawRectangle( x, y, width, height ); return; } @@ -427,13 +890,13 @@ void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wx // CMB: adjust size if outline is drawn otherwise the result is // 1 pixel too wide and high - if (m_pen.GetStyle() != wxTRANSPARENT) + if ( m_pen.IsNonTransparent() ) { ww--; hh--; } - if (m_window) + if (m_gdkwindow) { // CMB: ensure dd is not larger than rectangle otherwise we // get an hour glass shape @@ -442,26 +905,33 @@ void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wx if (dd > hh) dd = hh; rr = dd / 2; - if (m_brush.GetStyle() != wxTRANSPARENT) + if ( m_brush.IsNonTransparent() ) { - gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx+rr, yy, ww-dd+1, hh ); - gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy+rr, ww, hh-dd+1 ); - gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 ); - gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 ); - gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 ); - gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 ); + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); + + gdk_draw_rectangle(m_gdkwindow, gc, true, xx+rr, yy, ww-dd+1, hh); + gdk_draw_rectangle(m_gdkwindow, gc, true, xx, yy+rr, ww, hh-dd+1); + gdk_draw_arc(m_gdkwindow, gc, true, xx, yy, dd, dd, 90*64, 90*64); + gdk_draw_arc(m_gdkwindow, gc, true, xx+ww-dd, yy, dd, dd, 0, 90*64); + gdk_draw_arc(m_gdkwindow, gc, true, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64); + gdk_draw_arc(m_gdkwindow, gc, true, xx, yy+hh-dd, dd, dd, 180*64, 90*64); + + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); } - if (m_pen.GetStyle() != wxTRANSPARENT) + if ( m_pen.IsNonTransparent() ) { - gdk_draw_line( m_window, m_penGC, xx+rr, yy, xx+ww-rr, yy ); - gdk_draw_line( m_window, m_penGC, xx+rr, yy+hh, xx+ww-rr, yy+hh ); - gdk_draw_line( m_window, m_penGC, xx, yy+rr, xx, yy+hh-rr ); - gdk_draw_line( m_window, m_penGC, xx+ww, yy+rr, xx+ww, yy+hh-rr ); - gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, dd, dd, 90*64, 90*64 ); - gdk_draw_arc( m_window, m_penGC, FALSE, xx+ww-dd, yy, dd, dd, 0, 90*64 ); - gdk_draw_arc( m_window, m_penGC, FALSE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 ); - gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 ); + 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 ); + gdk_draw_line( m_gdkwindow, m_penGC, xx, yy+rr+1, xx, yy+hh-rr ); + gdk_draw_line( m_gdkwindow, m_penGC, xx+ww, yy+rr+1, xx+ww, yy+hh-rr ); + gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx, yy, dd, dd, 90*64, 90*64 ); + gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx+ww-dd, yy, dd, dd, 0, 90*64 ); + gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 ); + gdk_draw_arc( m_gdkwindow, m_penGC, FALSE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 ); } } @@ -470,9 +940,9 @@ void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wx CalcBoundingBox( x + width, y + height ); } -void wxWindowDC::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) +void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); wxCoord xx = XLOG2DEV(x); wxCoord yy = YLOG2DEV(y); @@ -483,493 +953,757 @@ void wxWindowDC::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord hei if (ww < 0) { ww = -ww; xx = xx - ww; } if (hh < 0) { hh = -hh; yy = yy - hh; } - if (m_window) + if (m_gdkwindow) { - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 ); + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); + + // If the pen is transparent pen we increase the size + // for better compatibility with other platforms. + if (m_pen.IsTransparent()) + { + ++ww; + ++hh; + } + + gdk_draw_arc(m_gdkwindow, gc, true, xx, yy, ww, hh, 0, 360*64); + + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); + } - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, 0, 360*64 ); + if ( m_pen.IsNonTransparent() ) + gdk_draw_arc( m_gdkwindow, m_penGC, false, xx, yy, ww, hh, 0, 360*64 ); } - CalcBoundingBox( x - width, y - height ); + CalcBoundingBox( x, y ); CalcBoundingBox( x + width, y + height ); } -void wxWindowDC::DoDrawIcon( const wxIcon &icon, wxCoord x, wxCoord y ) +void wxWindowDCImpl::DoDrawIcon( const wxIcon &icon, wxCoord x, wxCoord y ) { // VZ: egcs 1.0.3 refuses to compile this without cast, no idea why - DoDrawBitmap( (const wxBitmap&)icon, x, y, (bool)TRUE ); + DoDrawBitmap( (const wxBitmap&)icon, x, y, true ); } -void wxWindowDC::DoDrawBitmap( const wxBitmap &bitmap, - wxCoord x, wxCoord y, - bool useMask ) +// scale a pixbuf, return new pixbuf, unref old one +static GdkPixbuf* +Scale(GdkPixbuf* pixbuf, int dst_w, int dst_h, double sx, double sy) +{ + GdkPixbuf* pixbuf_scaled = gdk_pixbuf_new( + 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 +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); +} + +// scale part of a mask pixmap, return new mask, unref old one +static GdkPixmap* +ScaleMask(GdkPixmap* mask, int x, int y, int w, int h, int dst_w, int dst_h, double sx, double sy) +{ + GdkPixbuf* pixbuf = Scale(mask, x, y, w, h, dst_w, dst_h, sx, sy); + + // convert black and white pixbuf back to a mono pixmap + const unsigned out_rowstride = (dst_w + 7) / 8; + const size_t data_size = out_rowstride * size_t(dst_h); + char* data = new char[data_size]; + char* out = data; + const guchar* row = gdk_pixbuf_get_pixels(pixbuf); + const int rowstride = gdk_pixbuf_get_rowstride(pixbuf); + memset(data, 0, data_size); + for (int j = 0; j < dst_h; j++, row += rowstride, out += out_rowstride) + { + const guchar* in = row; + for (int i = 0; i < dst_w; i++, in += 3) + if (*in) + out[i >> 3] |= 1 << (i & 7); + } + 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) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + GdkGCValues gcValues; + gcValues.foreground.pixel = 0; + GdkGC* gc = gdk_gc_new_with_values(mask, &gcValues, GDK_GC_FOREGROUND); + GdkPixmap* pixmap = gdk_pixmap_new(mask, w, h, 1); + // clear new mask, so clipped areas will be masked + gdk_draw_rectangle(pixmap, gc, true, 0, 0, w, h); + gdk_gc_set_clip_region(gc, clipRegion); + gdk_gc_set_clip_origin(gc, -dst_x, -dst_y); + // 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; +} - wxCHECK_RET( bitmap.Ok(), wxT("invalid bitmap") ); +// make a color pixmap from part of a mono one, using text fg/bg colors +GdkPixmap* +wxWindowDCImpl::MonoToColor(GdkPixmap* monoPixmap, int x, int y, int w, int h) const +{ + GdkPixmap* pixmap = gdk_pixmap_new(m_gdkwindow, w, h, -1); + GdkGCValues gcValues; + gcValues.foreground.pixel = m_textForegroundColour.GetColor()->pixel; + gcValues.background.pixel = m_textBackgroundColour.GetColor()->pixel; + gcValues.stipple = monoPixmap; + gcValues.fill = GDK_OPAQUE_STIPPLED; + gcValues.ts_x_origin = -x; + gcValues.ts_y_origin = -y; + GdkGC* gc = gdk_gc_new_with_values(pixmap, &gcValues, GdkGCValuesMask( + GDK_GC_FOREGROUND | GDK_GC_BACKGROUND | GDK_GC_STIPPLE | GDK_GC_FILL | + GDK_GC_TS_X_ORIGIN | GDK_GC_TS_Y_ORIGIN)); + gdk_draw_rectangle(pixmap, gc, true, 0, 0, w, h); + g_object_unref(gc); + return pixmap; +} - /* scale/translate size and position */ +void wxWindowDCImpl::DoDrawBitmap( const wxBitmap &bitmap, + wxCoord x, wxCoord y, + bool useMask ) +{ + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + wxCHECK_RET( bitmap.IsOk(), wxT("invalid bitmap") ); - int xx = XLOG2DEV(x); - int yy = YLOG2DEV(y); + if (!m_gdkwindow) return; - int w = bitmap.GetWidth(); - int h = bitmap.GetHeight(); + const int w = bitmap.GetWidth(); + const int h = bitmap.GetHeight(); - CalcBoundingBox( x, y ); - CalcBoundingBox( x + w, y + h ); + // notice that as the bitmap is not drawn upside down (or right to left) + // even if the corresponding axis direction is inversed, we need to take it + // into account when calculating its bounding box + CalcBoundingBox(x, y); + CalcBoundingBox(x + m_signX*w, y + m_signY*h); - if (!m_window) return; + // device coords + int xx = LogicalToDeviceX(x); + const int yy = LogicalToDeviceY(y); + const int ww = LogicalToDeviceXRel(w); + const int hh = LogicalToDeviceYRel(h); - int ww = XLOG2DEVREL(w); - int hh = YLOG2DEVREL(h); + if (m_window && m_window->GetLayoutDirection() == wxLayout_RightToLeft) + xx -= ww; - /* scale bitmap if required */ + GdkRegion* const clipRegion = m_currentClippingRegion.GetRegion(); + // determine clip region overlap + int overlap = wxInRegion; + if (clipRegion) + { + overlap = m_currentClippingRegion.Contains(xx, yy, ww, hh); + if (overlap == wxOutRegion) + return; + } - wxBitmap use_bitmap; + const bool isScaled = ww != w || hh != h; + const bool hasAlpha = bitmap.HasAlpha(); + GdkGC* const use_gc = m_penGC; - if ((w != ww) || (h != hh)) + GdkPixmap* mask = NULL; + // mask does not work when drawing a pixbuf with alpha + if (useMask && !hasAlpha) { - wxImage image( bitmap ); - image = image.Scale( ww, hh ); + wxMask* m = bitmap.GetMask(); + if (m) + mask = m->GetBitmap(); + } + if (mask) + { + g_object_ref(mask); + if (isScaled) + mask = ScaleMask(mask, 0, 0, w, h, ww, hh, m_scaleX, m_scaleY); + 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); + } + gdk_gc_set_clip_mask(use_gc, mask); + gdk_gc_set_clip_origin(use_gc, xx, yy); + } - use_bitmap = image.ConvertToBitmap(); + // determine whether to use pixmap or pixbuf + GdkPixmap* pixmap = NULL; + GdkPixbuf* pixbuf = 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); + } + else if (hasAlpha || pixmap == NULL) + { + pixmap = NULL; + pixbuf = bitmap.GetPixbuf(); + g_object_ref(pixbuf); } else { - use_bitmap = bitmap; + g_object_ref(pixmap); } - /* apply mask if any */ - - GdkBitmap *mask = (GdkBitmap *) NULL; - if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap(); - - if (useMask && mask) + if (isScaled) { - gdk_gc_set_clip_mask( m_penGC, mask ); - gdk_gc_set_clip_origin( m_penGC, xx, yy ); + if (pixbuf) + pixbuf = Scale(pixbuf, ww, hh, m_scaleX, m_scaleY); + else + pixbuf = Scale(pixmap, 0, 0, w, h, ww, hh, m_scaleX, m_scaleY); } - /* draw XPixmap or XBitmap, depending on what the wxBitmap contains */ - - GdkPixmap *pm = use_bitmap.GetPixmap(); - if (pm) + if (pixbuf) { - gdk_draw_pixmap( m_window, m_penGC, pm, 0, 0, xx, yy, -1, -1 ); + 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 { - GdkBitmap *bm = use_bitmap.GetBitmap(); - if (bm) - { - gdk_draw_bitmap( m_window, m_penGC, bm, 0, 0, xx, yy, -1, -1 ); - } + gdk_draw_drawable(m_gdkwindow, use_gc, pixmap, 0, 0, xx, yy, ww, hh); } - /* remove mask again if any */ - - if (useMask && mask) + if (pixmap) + g_object_unref(pixmap); + if (mask) { - gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL ); - gdk_gc_set_clip_origin( m_penGC, 0, 0 ); + g_object_unref(mask); + gdk_gc_set_clip_region(use_gc, clipRegion); } } -bool wxWindowDC::DoBlit( wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height, - wxDC *source, wxCoord xsrc, wxCoord ysrc, - int logical_func, bool useMask ) +bool wxWindowDCImpl::DoBlit( wxCoord xdest, wxCoord ydest, + wxCoord width, wxCoord height, + wxDC *source, + wxCoord xsrc, wxCoord ysrc, + wxRasterOperationMode logical_func, + bool useMask, + wxCoord xsrcMask, wxCoord ysrcMask ) { - /* this is the nth try to get this utterly useless function to - work. it now completely ignores the scaling or translation - of the source dc, but scales correctly on the target dc and - knows about possible mask information in a memory dc. */ + wxCHECK_MSG( IsOk(), false, wxT("invalid window dc") ); + wxCHECK_MSG( source, false, wxT("invalid source dc") ); - wxCHECK_MSG( Ok(), FALSE, wxT("invalid window dc") ); + if (!m_gdkwindow) return false; - wxCHECK_MSG( source, FALSE, wxT("invalid source dc") ); + GdkDrawable* srcDrawable = NULL; + GdkPixmap* mask = NULL; + wxMemoryDC* memDC = wxDynamicCast(source, wxMemoryDC); + if (memDC) + { + const wxBitmap& bitmap = memDC->GetSelectedBitmap(); + if (!bitmap.IsOk()) + return false; + srcDrawable = bitmap.GetPixmap(); + if (useMask) + { + wxMask* m = bitmap.GetMask(); + if (m) + mask = m->GetBitmap(); + } + } + else + { + wxDCImpl* impl = source->GetImpl(); + wxWindowDCImpl* gtk_impl = wxDynamicCast(impl, wxWindowDCImpl); + if (gtk_impl) + srcDrawable = gtk_impl->GetGDKWindow(); + if (srcDrawable == NULL) + return false; + } - if (!m_window) return FALSE; + CalcBoundingBox(xdest, ydest); + CalcBoundingBox(xdest + width, ydest + height); + + // source device coords + int src_x = source->LogicalToDeviceX(xsrc); + int src_y = source->LogicalToDeviceY(ysrc); + int src_w = source->LogicalToDeviceXRel(width); + int src_h = source->LogicalToDeviceYRel(height); + + // Clip source rect to source dc. + // Only necessary when scaling, to avoid GDK errors when + // converting to pixbuf, but no harm in always doing it. + // If source rect changes, it also changes the dest rect. + wxRect clip; + gdk_drawable_get_size(srcDrawable, &clip.width, &clip.height); + clip.Intersect(wxRect(src_x, src_y, src_w, src_h)); + if (src_w != clip.width || src_h != clip.height) + { + if (clip.width == 0) + return true; + + src_w = clip.width; + src_h = clip.height; + width = source->DeviceToLogicalXRel(src_w); + height = source->DeviceToLogicalYRel(src_h); + if (src_x != clip.x || src_y != clip.y) + { + xdest += source->DeviceToLogicalXRel(clip.x - src_x); + ydest += source->DeviceToLogicalYRel(clip.y - src_y); + src_x = clip.x; + src_y = clip.y; + } + } - wxClientDC *srcDC = (wxClientDC*)source; - wxMemoryDC *memDC = (wxMemoryDC*)source; + // destination device coords + const int dst_x = LogicalToDeviceX(xdest); + const int dst_y = LogicalToDeviceY(ydest); + const int dst_w = LogicalToDeviceXRel(width); + const int dst_h = LogicalToDeviceYRel(height); - bool use_bitmap_method = FALSE; + GdkRegion* const clipRegion = m_currentClippingRegion.GetRegion(); + // determine dest clip region overlap + int overlap = wxInRegion; + if (clipRegion) + { + overlap = m_currentClippingRegion.Contains(dst_x, dst_y, dst_w, dst_h); + if (overlap == wxOutRegion) + return true; + } - if (srcDC->m_isMemDC) + const bool isScaled = src_w != dst_w || src_h != dst_h; + double scale_x = 0; + double scale_y = 0; + if (isScaled) { - if (!memDC->m_selected.Ok()) return FALSE; + // get source to dest scale + double usx, usy, lsx, lsy; + source->GetUserScale(&usx, &usy); + source->GetLogicalScale(&lsx, &lsy); + scale_x = m_scaleX / (usx * lsx); + scale_y = m_scaleY / (usy * lsy); + } - /* we use the "XCopyArea" way to copy a memory dc into - y different window if the memory dc BOTH - a) doesn't have any mask or its mask isn't used - b) it is clipped - c) is not 1-bit */ + GdkGC* const use_gc = m_penGC; - if (useMask && (memDC->m_selected.GetMask())) - { - /* we HAVE TO use the direct way for memory dcs - that have mask since the XCopyArea doesn't know - about masks */ - use_bitmap_method = TRUE; - } - else if (memDC->m_selected.GetDepth() == 1) + if (mask) + { + g_object_ref(mask); + int srcMask_x = src_x; + int srcMask_y = src_y; + if (xsrcMask != -1 || ysrcMask != -1) { - /* we HAVE TO use the direct way for memory dcs - that are bitmaps because XCopyArea doesn't cope - with different bit depths */ - use_bitmap_method = TRUE; + srcMask_x = source->LogicalToDeviceX(xsrcMask); + srcMask_y = source->LogicalToDeviceY(ysrcMask); } - else if ((xsrc == 0) && (ysrc == 0) && - (width == memDC->m_selected.GetWidth()) && - (height == memDC->m_selected.GetHeight())) + if (isScaled) { - /* we SHOULD use the direct way if all of the bitmap - in the memory dc is copied in which case XCopyArea - wouldn't be able able to boost performace by reducing - the area to be scaled */ - use_bitmap_method = TRUE; + mask = ScaleMask(mask, srcMask_x, srcMask_y, + src_w, src_h, dst_w, dst_h, scale_x, scale_y); + srcMask_x = 0; + srcMask_y = 0; } - else + if (overlap == wxPartRegion) { - use_bitmap_method = FALSE; + // 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, + srcMask_x, srcMask_y, dst_x, dst_y, dst_w, dst_h); + 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); } - CalcBoundingBox( xdest, ydest ); - CalcBoundingBox( xdest + width, ydest + height ); + GdkPixmap* pixmap = NULL; + if (gdk_drawable_get_depth(srcDrawable) == 1) + { + // Convert mono pixmap to color using text fg/bg colors. + // Scaling/drawing is simpler if this is done first. + pixmap = MonoToColor(srcDrawable, src_x, src_y, src_w, src_h); + srcDrawable = pixmap; + src_x = 0; + src_y = 0; + } - int old_logical_func = m_logicalFunction; - SetLogicalFunction( logical_func ); + const wxRasterOperationMode logical_func_save = m_logicalFunction; + SetLogicalFunction(logical_func); + if (memDC == NULL) + gdk_gc_set_subwindow(use_gc, GDK_INCLUDE_INFERIORS); - if (use_bitmap_method) + if (isScaled) { - /* scale/translate bitmap size */ + GdkPixbuf* pixbuf = Scale(srcDrawable, + src_x, src_y, src_w, src_h, dst_w, dst_h, scale_x, scale_y); + gdk_draw_pixbuf(m_gdkwindow, use_gc, pixbuf, + 0, 0, dst_x, dst_y, dst_w, dst_h, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref(pixbuf); + } + else + { + gdk_draw_drawable(m_gdkwindow, use_gc, srcDrawable, + src_x, src_y, dst_x, dst_y, dst_w, dst_h); + } - wxCoord bm_width = memDC->m_selected.GetWidth(); - wxCoord bm_height = memDC->m_selected.GetHeight(); + SetLogicalFunction(logical_func_save); + if (memDC == NULL) + gdk_gc_set_subwindow(use_gc, GDK_CLIP_BY_CHILDREN); - wxCoord bm_ww = XLOG2DEVREL( bm_width ); - wxCoord bm_hh = YLOG2DEVREL( bm_height ); + if (pixmap) + g_object_unref(pixmap); + if (mask) + { + g_object_unref(mask); + gdk_gc_set_clip_region(use_gc, clipRegion); + } + return true; +} - /* scale bitmap if required */ +void wxWindowDCImpl::DoDrawText(const wxString& text, + wxCoord xLogical, + wxCoord yLogical) +{ + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - wxBitmap use_bitmap; + if (!m_gdkwindow) return; - if ((bm_width != bm_ww) || (bm_height != bm_hh)) - { - wxImage image( memDC->m_selected ); - image = image.Scale( bm_ww, bm_hh ); + if (text.empty()) return; - use_bitmap = image.ConvertToBitmap(); - } - else - { - use_bitmap = memDC->m_selected; - } + wxCoord x = XLOG2DEV(xLogical), + y = YLOG2DEV(yLogical); - /* scale/translate size and position */ + 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") ); - wxCoord xx = XLOG2DEV(xdest); - wxCoord yy = YLOG2DEV(ydest); + gdk_pango_context_set_colormap( m_context, m_cmap ); // not needed in gtk+ >= 2.6 - wxCoord ww = XLOG2DEVREL(width); - wxCoord hh = YLOG2DEVREL(height); + wxCharBuffer data = wxGTK_CONV(text); + if ( !data ) + return; - /* apply mask if any */ + pango_layout_set_text(m_layout, data, data.length()); + const bool setAttrs = m_font.GTKSetPangoAttrs(m_layout); - GdkBitmap *mask = (GdkBitmap *) NULL; - if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap(); + int oldSize = 0; + const bool isScaled = fabs(m_scaleY - 1.0) > 0.00001; + if (isScaled) + { + // If there is a user or actually any scale applied to + // the device context, scale the font. - if (useMask && mask) - { - gdk_gc_set_clip_mask( m_penGC, mask ); - gdk_gc_set_clip_origin( m_penGC, xx, yy ); - } + // scale font description + oldSize = pango_font_description_get_size(m_fontdesc); + pango_font_description_set_size(m_fontdesc, int(oldSize * m_scaleY)); - /* draw XPixmap or XBitmap, depending on what the wxBitmap contains */ + // actually apply scaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); + } - GdkPixmap *pm = use_bitmap.GetPixmap(); - if (pm) - { - gdk_draw_pixmap( m_window, m_penGC, pm, xsrc, ysrc, xx, yy, ww, hh ); - } - else - { - GdkBitmap *bm = use_bitmap.GetBitmap(); - if (bm) - { - /* we use the textGC here because blitting a bitmap is done - using the current text colour */ - gdk_draw_bitmap( m_window, m_textGC, bm, xsrc, ysrc, xx, yy, ww, hh ); - } - } + int w, h; + pango_layout_get_pixel_size(m_layout, &w, &h); - /* remove mask again if any */ + // Draw layout. + int x_rtl = x; + if (m_window && m_window->GetLayoutDirection() == wxLayout_RightToLeft) + x_rtl -= w; - if (useMask && mask) - { - gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL ); - gdk_gc_set_clip_origin( m_penGC, 0, 0 ); - } - } - else /* use_bitmap_method */ - { - /* scale/translate size and position */ + const GdkColor* bg_col = NULL; + if (m_backgroundMode == wxBRUSHSTYLE_SOLID) + bg_col = m_textBackgroundColour.GetColor(); - wxCoord xx = XLOG2DEV(xdest); - wxCoord yy = YLOG2DEV(ydest); + gdk_draw_layout_with_colors(m_gdkwindow, m_textGC, x_rtl, y, m_layout, NULL, bg_col); - wxCoord ww = XLOG2DEVREL(width); - wxCoord hh = YLOG2DEVREL(height); + if (isScaled) + { + // reset unscaled size + pango_font_description_set_size( m_fontdesc, oldSize ); - if ((width != ww) || (height != hh)) - { - /* draw source window into a bitmap as we cannot scale - a window in contrast to a bitmap. this would actually - work with memory dcs as well, but we'd lose the mask - information and waste one step in this process since - a memory already has a bitmap. all this is slightly - inefficient as we could take an XImage directly from - an X window, but we'd then also have to care that - the window is not outside the screen (in which case - we'd get a BadMatch or what not). - Is a double XGetImage and combined XGetPixel and - XPutPixel really faster? I'm not sure. look at wxXt - for a different implementation of the same problem. */ + // actually apply unscaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); + } + if (setAttrs) + { + // undo underline attributes setting: + pango_layout_set_attributes(m_layout, NULL); + } - wxBitmap bitmap( width, height ); - gdk_window_copy_area( bitmap.GetPixmap(), m_penGC, 0, 0, - srcDC->GetWindow(), - xsrc, ysrc, width, height ); + CalcBoundingBox(xLogical + int(w / m_scaleX), yLogical + int(h / m_scaleY)); + CalcBoundingBox(xLogical, yLogical); +} - /* scale image */ +// 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; - wxImage image( bitmap ); - image = image.Scale( ww, hh ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - /* convert to bitmap */ + x = XLOG2DEV(x); + y = YLOG2DEV(y); - bitmap = image.ConvertToBitmap(); + pango_layout_set_text(m_layout, wxGTK_CONV(text), -1); + const bool setAttrs = m_font.GTKSetPangoAttrs(m_layout); + 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. - /* draw scaled bitmap */ + // scale font description + oldSize = pango_font_description_get_size(m_fontdesc); + pango_font_description_set_size(m_fontdesc, int(oldSize * m_scaleY)); - gdk_draw_pixmap( m_window, m_penGC, bitmap.GetPixmap(), 0, 0, xx, yy, -1, -1 ); + // actually apply scaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); + } - } - else - { - /* no scaling and not a memory dc with a mask either */ + 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 x3 = x4 + x2; + double y3 = y4 + y2; + // Then we calculate max and min of the rotated rectangle. + wxCoord maxX = (wxCoord)(dmax(dmax(0, x2), dmax(x3, x4)) + 0.5), + 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 (setAttrs) + 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 ); - gdk_window_copy_area( m_window, m_penGC, xx, yy, - srcDC->GetWindow(), - xsrc, ysrc, width, height ); - } + // actually apply unscaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); } - SetLogicalFunction( old_logical_func ); - return TRUE; + CalcBoundingBox(x+minX, y+minY); + CalcBoundingBox(x+maxX, y+maxY); } -void wxWindowDC::DoDrawText( const wxString &text, wxCoord x, wxCoord y ) +void wxWindowDCImpl::DoGetTextExtent(const wxString &string, + wxCoord *width, wxCoord *height, + wxCoord *descent, wxCoord *externalLeading, + const wxFont *theFont) const { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); - - if (!m_window) return; - - GdkFont *font = m_font.GetInternalFont( m_scaleY ); - - wxCHECK_RET( font, wxT("invalid font") ); + if ( width ) + *width = 0; + if ( height ) + *height = 0; + if ( descent ) + *descent = 0; + if ( externalLeading ) + *externalLeading = 0; + + if (string.empty()) + return; - x = XLOG2DEV(x); - y = YLOG2DEV(y); + // ensure that theFont is always non-NULL + if ( !theFont || !theFont->IsOk() ) + theFont = &m_font; - /* CMB 21/5/98: draw text background if mode is wxSOLID */ - if (m_backgroundMode == wxSOLID) + // and use it if it's valid + if ( theFont->IsOk() ) { - wxCoord width = gdk_string_width( font, text.mbc_str() ); - wxCoord height = font->ascent + font->descent; - gdk_gc_set_foreground( m_textGC, m_textBackgroundColour.GetColor() ); - gdk_draw_rectangle( m_window, m_textGC, TRUE, x, y, width, height ); - gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() ); + pango_layout_set_font_description + ( + m_layout, + theFont->GetNativeFontInfo()->description + ); } - gdk_draw_string( m_window, font, m_textGC, x, y + font->ascent, text.mbc_str() ); - /* CMB 17/7/98: simple underline: ignores scaling and underlying - X font's XA_UNDERLINE_POSITION and XA_UNDERLINE_THICKNESS - properties (see wxXt implementation) */ - if (m_font.GetUnderlined()) + // Set layout's text + const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(string, *theFont); + if ( !dataUTF8 ) { - long width = gdk_string_width( font, text.mbc_str() ); - long ul_y = y + font->ascent; - if (font->descent > 0) ul_y++; - gdk_draw_line( m_window, m_textGC, x, ul_y, x + width, ul_y); + // hardly ideal, but what else can we do if conversion failed? + return; } - long w, h; - GetTextExtent (text, &w, &h); - CalcBoundingBox (x + w, y + h); - CalcBoundingBox (x, y); -} + pango_layout_set_text(m_layout, dataUTF8, -1); -void wxWindowDC::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord y, double angle ) -{ - if (angle == 0.0) + int h; + pango_layout_get_pixel_size(m_layout, width, &h); + if (descent) { - DrawText(text, x, y); - return; + 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( Ok(), wxT("invalid window dc") ); + // Reset old font description + if (theFont->IsOk()) + pango_layout_set_font_description( m_layout, m_fontdesc ); +} - if (!m_window) return; - GdkFont *font = m_font.GetInternalFont( m_scaleY ); +bool wxWindowDCImpl::DoGetPartialTextExtents(const wxString& text, + wxArrayInt& widths) const +{ + const size_t len = text.length(); + widths.Empty(); + widths.Add(0, len); - wxCHECK_RET( font, wxT("invalid font") ); + if (text.empty()) + return true; - x = XLOG2DEV(x); - y = YLOG2DEV(y); + // 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; + } - int cx = gdk_string_width( font, text.mbc_str() ); - int cy = font->ascent + font->descent; - - wxBitmap src(cx, cy); - wxMemoryDC dc; - dc.SelectObject(src); - dc.SetFont(GetFont()); - dc.SetBackground(*wxWHITE_BRUSH); - dc.SetBrush(*wxBLACK_BRUSH); - dc.Clear(); - dc.DrawText(text, 0, 0); - dc.SetFont(wxNullFont); - - // Calculate the size of the rotated bounding box. - double dx = cos(angle / 180.0 * M_PI); - double dy = sin(angle / 180.0 * M_PI); - double x4 = -cy * dy; - double y4 = cy * dx; - double x3 = cx * dx; - double y3 = cx * dy; - double x2 = x3 + x4; - double y2 = y3 + y4; - double x1 = x; - double y1 = y; - - // Create image from the source bitmap after writing the text into it. - wxImage image(src); - - int minx = roundmin(0, roundmin(x4, roundmin(x2, x3))); - int miny = roundmin(0, roundmin(y4, roundmin(y2, y3))); - int maxx = roundmax(0, roundmax(x4, roundmax(x2, x3))); - int maxy = roundmax(0, roundmax(y4, roundmax(y2, y3))); - - // This rotates counterclockwise around the top left corner. - for (int rx = minx; rx < maxx; rx++) - { - for (int ry = miny; ry < maxy; ry++) - { - // transform dest coords to source coords - int sx = (int) (rx * dx + ry * dy + 0.5); - int sy = (int) (ry * dx - rx * dy + 0.5); - if (sx >= 0 && sx < cx && sy >= 0 && sy < cy) - { - // draw black pixels, ignore white ones (i.e. transparent b/g) - if (image.GetRed(sx, sy) == 0) - { - DrawPoint(x1 + maxx - rx, cy + y1 - ry); - } - else - { - // Background - //DrawPoint(x1 + maxx - rx, cy + y1 + maxy - ry); - } - } - } + pango_layout_set_text(m_layout, dataUTF8, -1); + + // 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); - // TODO call CalcBoundingBox() + return true; } -void wxWindowDC::DoGetTextExtent(const wxString &string, - wxCoord *width, wxCoord *height, - wxCoord *descent, wxCoord *externalLeading, - wxFont *theFont) const -{ - wxFont fontToUse = m_font; - if (theFont) fontToUse = *theFont; - GdkFont *font = fontToUse.GetInternalFont( m_scaleY ); - if (width) (*width) = wxCoord(gdk_string_width( font, string.mbc_str() ) / m_scaleX); - if (height) (*height) = wxCoord((font->ascent + font->descent) / m_scaleY); - if (descent) (*descent) = wxCoord(font->descent / m_scaleY); - if (externalLeading) (*externalLeading) = 0; // ?? +wxCoord wxWindowDCImpl::GetCharWidth() const +{ + pango_layout_set_text( m_layout, "H", 1 ); + int w; + pango_layout_get_pixel_size( m_layout, &w, NULL ); + return w; } -wxCoord wxWindowDC::GetCharWidth() const +wxCoord wxWindowDCImpl::GetCharHeight() const { - GdkFont *font = m_font.GetInternalFont( m_scaleY ); - wxCHECK_MSG( font, -1, wxT("invalid font") ); + PangoFontMetrics *metrics = pango_context_get_metrics (m_context, m_fontdesc, pango_context_get_language(m_context)); + wxCHECK_MSG( metrics, -1, wxT("failed to get pango font metrics") ); - return wxCoord(gdk_string_width( font, "H" ) / m_scaleX); + wxCoord h = PANGO_PIXELS (pango_font_metrics_get_descent (metrics) + + pango_font_metrics_get_ascent (metrics)); + pango_font_metrics_unref (metrics); + return h; } -wxCoord wxWindowDC::GetCharHeight() const +void wxWindowDCImpl::Clear() { - GdkFont *font = m_font.GetInternalFont( m_scaleY ); - wxCHECK_MSG( font, -1, wxT("invalid font") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - return wxCoord((font->ascent + font->descent) / m_scaleY); + if (!m_gdkwindow) return; + + int width,height; + DoGetSize( &width, &height ); + gdk_draw_rectangle( m_gdkwindow, m_bgGC, TRUE, 0, 0, width, height ); } -void wxWindowDC::Clear() +void wxWindowDCImpl::SetFont( const wxFont &font ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + m_font = font; - if (!m_window) return; + if (m_font.IsOk()) + { + if (m_fontdesc) + pango_font_description_free( m_fontdesc ); - /* - we either are a memory dc or have a window as the - owner. anything else shouldn't happen. - - we don't use gdk_window_clear() as we don't set - the window's background colour anymore. it is too - much pain to keep the DC's and the window's back- - ground colour in synch. */ + m_fontdesc = pango_font_description_copy( m_font.GetNativeFontInfo()->description ); - if (m_owner) - { - int width,height; - m_owner->GetSize( &width, &height ); - gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height ); - return; - } - if (m_isMemDC) - { - int width,height; - GetSize( &width, &height ); - gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height ); - return; - } -} + if (m_window) + { + PangoContext *oldContext = m_context; -void wxWindowDC::SetFont( const wxFont &font ) -{ - m_font = font; + m_context = m_window->GTKGetPangoDefaultContext(); + + // If we switch back/forth between different contexts + // we also have to create a new layout. I think so, + // at least, and it doesn't hurt to do it. + if (oldContext != m_context) + { + if (m_layout) + g_object_unref (m_layout); + + m_layout = pango_layout_new( m_context ); + } + } + + pango_layout_set_font_description( m_layout, m_fontdesc ); + } } -void wxWindowDC::SetPen( const wxPen &pen ) +void wxWindowDCImpl::SetPen( const wxPen &pen ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); if (m_pen == pen) return; m_pen = pen; - if (!m_pen.Ok()) return; + if (!m_pen.IsOk()) return; - if (!m_window) return; + if (!m_gdkwindow) return; gint width = m_pen.GetWidth(); if (width <= 0) @@ -985,74 +1719,62 @@ void wxWindowDC::SetPen( const wxPen &pen ) ( fabs((double) XLOG2DEVREL(width)) + fabs((double) YLOG2DEVREL(width)) ) / 2.0; width = (int)w; + if ( !width ) + { + // width can't be 0 or an internal GTK error occurs inside + // gdk_gc_set_dashes() below + width = 1; + } } - static const char dotted[] = {1, 1}; - static const char short_dashed[] = {2, 2}; - static const char wxCoord_dashed[] = {2, 4}; - static const char dotted_dashed[] = {3, 3, 1, 3}; + static const wxGTKDash dotted[] = {1, 1}; + static const wxGTKDash short_dashed[] = {2, 2}; + static const wxGTKDash wxCoord_dashed[] = {2, 4}; + static const wxGTKDash dotted_dashed[] = {3, 3, 1, 3}; // We express dash pattern in pen width unit, so we are // independent of zoom factor and so on... int req_nb_dash; - const char *req_dash; + const wxGTKDash *req_dash; - GdkLineStyle lineStyle = GDK_LINE_SOLID; + GdkLineStyle lineStyle = GDK_LINE_ON_OFF_DASH; switch (m_pen.GetStyle()) { - case wxUSER_DASH: - { - lineStyle = GDK_LINE_ON_OFF_DASH; + case wxPENSTYLE_USER_DASH: req_nb_dash = m_pen.GetDashCount(); - req_dash = m_pen.GetDash(); + req_dash = (wxGTKDash*)m_pen.GetDash(); break; - } - case wxDOT: - { - lineStyle = GDK_LINE_ON_OFF_DASH; + case wxPENSTYLE_DOT: req_nb_dash = 2; req_dash = dotted; break; - } - case wxLONG_DASH: - { - lineStyle = GDK_LINE_ON_OFF_DASH; + case wxPENSTYLE_LONG_DASH: req_nb_dash = 2; req_dash = wxCoord_dashed; break; - } - case wxSHORT_DASH: - { - lineStyle = GDK_LINE_ON_OFF_DASH; + case wxPENSTYLE_SHORT_DASH: req_nb_dash = 2; req_dash = short_dashed; break; - } - case wxDOT_DASH: - { -// lineStyle = GDK_LINE_DOUBLE_DASH; - lineStyle = GDK_LINE_ON_OFF_DASH; + case wxPENSTYLE_DOT_DASH: req_nb_dash = 4; req_dash = dotted_dashed; break; - } - case wxTRANSPARENT: - case wxSTIPPLE: - case wxSOLID: + case wxPENSTYLE_TRANSPARENT: + case wxPENSTYLE_STIPPLE_MASK_OPAQUE: + case wxPENSTYLE_STIPPLE: + case wxPENSTYLE_SOLID: default: - { lineStyle = GDK_LINE_SOLID; - req_dash = (wxDash*)NULL; + req_dash = NULL; req_nb_dash = 0; break; - } } -#if (GTK_MINOR_VERSION > 0) if (req_dash && req_nb_dash) { - char *real_req_dash = new char[req_nb_dash]; + wxGTKDash *real_req_dash = new wxGTKDash[req_nb_dash]; if (real_req_dash) { for (int i = 0; i < req_nb_dash; i++) @@ -1063,25 +1785,32 @@ void wxWindowDC::SetPen( const wxPen &pen ) else { // No Memory. We use non-scaled dash pattern... - gdk_gc_set_dashes( m_penGC, 0, (char*)req_dash, req_nb_dash ); + gdk_gc_set_dashes( m_penGC, 0, (wxGTKDash*)req_dash, req_nb_dash ); } } -#endif GdkCapStyle capStyle = GDK_CAP_ROUND; switch (m_pen.GetCap()) { - case wxCAP_ROUND: { capStyle = (width <= 1) ? GDK_CAP_NOT_LAST : GDK_CAP_ROUND; break; } case wxCAP_PROJECTING: { capStyle = GDK_CAP_PROJECTING; break; } case wxCAP_BUTT: { capStyle = GDK_CAP_BUTT; break; } + case wxCAP_ROUND: + default: + if (width <= 1) + { + width = 0; + capStyle = GDK_CAP_NOT_LAST; + } + break; } GdkJoinStyle joinStyle = GDK_JOIN_ROUND; switch (m_pen.GetJoin()) { case wxJOIN_BEVEL: { joinStyle = GDK_JOIN_BEVEL; break; } - case wxJOIN_ROUND: { joinStyle = GDK_JOIN_ROUND; break; } case wxJOIN_MITER: { joinStyle = GDK_JOIN_MITER; break; } + case wxJOIN_ROUND: + default: { joinStyle = GDK_JOIN_ROUND; break; } } gdk_gc_set_line_attributes( m_penGC, width, lineStyle, capStyle, joinStyle ); @@ -1090,26 +1819,26 @@ void wxWindowDC::SetPen( const wxPen &pen ) gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() ); } -void wxWindowDC::SetBrush( const wxBrush &brush ) +void wxWindowDCImpl::SetBrush( const wxBrush &brush ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); if (m_brush == brush) return; m_brush = brush; - if (!m_brush.Ok()) return; + if (!m_brush.IsOk()) return; - if (!m_window) return; + if (!m_gdkwindow) return; m_brush.GetColour().CalcPixel( m_cmap ); gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() ); gdk_gc_set_fill( m_brushGC, GDK_SOLID ); - if ((m_brush.GetStyle() == wxSTIPPLE) && (m_brush.GetStipple()->Ok())) + if ((m_brush.GetStyle() == wxBRUSHSTYLE_STIPPLE) && (m_brush.GetStipple()->IsOk())) { - if (m_brush.GetStipple()->GetPixmap()) + if (m_brush.GetStipple()->GetDepth() != 1) { gdk_gc_set_fill( m_brushGC, GDK_TILED ); gdk_gc_set_tile( m_brushGC, m_brush.GetStipple()->GetPixmap() ); @@ -1117,83 +1846,95 @@ void wxWindowDC::SetBrush( const wxBrush &brush ) else { gdk_gc_set_fill( m_brushGC, GDK_STIPPLED ); - gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetBitmap() ); + gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetPixmap() ); } } - if (IS_HATCH(m_brush.GetStyle())) + 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() ); + } + + if (m_brush.IsHatch()) { gdk_gc_set_fill( m_brushGC, GDK_STIPPLED ); - int num = m_brush.GetStyle() - wxBDIAGONAL_HATCH; - gdk_gc_set_stipple( m_brushGC, hatches[num] ); + gdk_gc_set_stipple(m_brushGC, GetHatch(m_brush.GetStyle())); } } -void wxWindowDC::SetBackground( const wxBrush &brush ) +void wxWindowDCImpl::SetBackground( const wxBrush &brush ) { /* CMB 21/7/98: Added SetBackground. Sets background brush * for Clear() and bg colour for shapes filled with cross-hatch brush */ - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); if (m_backgroundBrush == brush) return; m_backgroundBrush = brush; - if (!m_backgroundBrush.Ok()) return; + if (!m_backgroundBrush.IsOk()) return; - if (!m_window) return; + if (!m_gdkwindow) return; + + wxColor color = m_backgroundBrush.GetColour(); + color.CalcPixel(m_cmap); + const GdkColor* gdkColor = color.GetColor(); + gdk_gc_set_background(m_brushGC, gdkColor); + gdk_gc_set_background(m_penGC, gdkColor); + gdk_gc_set_background(m_bgGC, gdkColor); + gdk_gc_set_foreground(m_bgGC, gdkColor); - m_backgroundBrush.GetColour().CalcPixel( m_cmap ); - gdk_gc_set_background( m_brushGC, m_backgroundBrush.GetColour().GetColor() ); - gdk_gc_set_background( m_penGC, m_backgroundBrush.GetColour().GetColor() ); - gdk_gc_set_background( m_bgGC, m_backgroundBrush.GetColour().GetColor() ); - gdk_gc_set_foreground( m_bgGC, m_backgroundBrush.GetColour().GetColor() ); gdk_gc_set_fill( m_bgGC, GDK_SOLID ); - if ((m_backgroundBrush.GetStyle() == wxSTIPPLE) && (m_backgroundBrush.GetStipple()->Ok())) + if (m_backgroundBrush.GetStyle() == wxBRUSHSTYLE_STIPPLE) { - if (m_backgroundBrush.GetStipple()->GetPixmap()) + const wxBitmap* stipple = m_backgroundBrush.GetStipple(); + if (stipple->IsOk()) { - gdk_gc_set_fill( m_bgGC, GDK_TILED ); - gdk_gc_set_tile( m_bgGC, m_backgroundBrush.GetStipple()->GetPixmap() ); - } - else - { - gdk_gc_set_fill( m_bgGC, GDK_STIPPLED ); - gdk_gc_set_stipple( m_bgGC, m_backgroundBrush.GetStipple()->GetBitmap() ); + if (stipple->GetDepth() != 1) + { + gdk_gc_set_fill(m_bgGC, GDK_TILED); + gdk_gc_set_tile(m_bgGC, stipple->GetPixmap()); + } + else + { + gdk_gc_set_fill(m_bgGC, GDK_STIPPLED); + gdk_gc_set_stipple(m_bgGC, stipple->GetPixmap()); + } } } - - if (IS_HATCH(m_backgroundBrush.GetStyle())) + else if (m_backgroundBrush.IsHatch()) { gdk_gc_set_fill( m_bgGC, GDK_STIPPLED ); - int num = m_backgroundBrush.GetStyle() - wxBDIAGONAL_HATCH; - gdk_gc_set_stipple( m_bgGC, hatches[num] ); + gdk_gc_set_stipple(m_bgGC, GetHatch(m_backgroundBrush.GetStyle())); } } -void wxWindowDC::SetLogicalFunction( int function ) +void wxWindowDCImpl::SetLogicalFunction( wxRasterOperationMode function ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + if (m_logicalFunction == function) + return; - if (m_logicalFunction == function) return; + // VZ: shouldn't this be a CHECK? + if (!m_gdkwindow) + return; - GdkFunction mode = GDK_COPY; + GdkFunction mode; switch (function) { case wxXOR: mode = GDK_XOR; break; case wxINVERT: mode = GDK_INVERT; break; -#if (GTK_MINOR_VERSION > 0) case wxOR_REVERSE: mode = GDK_OR_REVERSE; break; case wxAND_REVERSE: mode = GDK_AND_REVERSE; break; case wxCLEAR: mode = GDK_CLEAR; break; case wxSET: mode = GDK_SET; break; case wxOR_INVERT: mode = GDK_OR_INVERT; break; - case wxSRC_AND: case wxAND: mode = GDK_AND; break; - case wxSRC_OR: case wxOR: mode = GDK_OR; break; case wxEQUIV: mode = GDK_EQUIV; break; case wxNAND: mode = GDK_NAND; break; @@ -1201,98 +1942,95 @@ void wxWindowDC::SetLogicalFunction( int function ) case wxCOPY: mode = GDK_COPY; break; case wxNO_OP: mode = GDK_NOOP; break; case wxSRC_INVERT: mode = GDK_COPY_INVERT; break; -#endif + case wxNOR: mode = GDK_NOR; break; default: - { - wxFAIL_MSG( wxT("unsupported logical function") ); - break; - } + wxFAIL_MSG("unknown mode"); + return; } m_logicalFunction = function; - if (!m_window) return; - gdk_gc_set_function( m_penGC, mode ); gdk_gc_set_function( m_brushGC, mode ); + + // to stay compatible with wxMSW, we don't apply ROPs to the text + // operations (i.e. DrawText/DrawRotatedText). + // True, but mono-bitmaps use the m_textGC and they use ROPs as well. gdk_gc_set_function( m_textGC, mode ); } -void wxWindowDC::SetTextForeground( const wxColour &col ) +void wxWindowDCImpl::SetTextForeground( const wxColour &col ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (m_textForegroundColour == col) return; + // don't set m_textForegroundColour to an invalid colour as we'd crash + // later then (we use m_textForegroundColour.GetColor() without checking + // in a few places) + if ( !col.IsOk() || (m_textForegroundColour == col) ) + return; m_textForegroundColour = col; - if (!m_textForegroundColour.Ok()) return; - if (!m_window) return; - - m_textForegroundColour.CalcPixel( m_cmap ); - gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() ); + if ( m_gdkwindow ) + { + m_textForegroundColour.CalcPixel( m_cmap ); + gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() ); + } } -void wxWindowDC::SetTextBackground( const wxColour &col ) +void wxWindowDCImpl::SetTextBackground( const wxColour &col ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (m_textBackgroundColour == col) return; + // same as above + if ( !col.IsOk() || (m_textBackgroundColour == col) ) + return; m_textBackgroundColour = col; - if (!m_textBackgroundColour.Ok()) return; - - if (!m_window) return; - m_textBackgroundColour.CalcPixel( m_cmap ); - gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() ); + if ( m_gdkwindow ) + { + m_textBackgroundColour.CalcPixel( m_cmap ); + gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() ); + } } -void wxWindowDC::SetBackgroundMode( int mode ) +void wxWindowDCImpl::SetBackgroundMode( int mode ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); m_backgroundMode = mode; - - if (!m_window) return; - - // CMB 21/7/98: fill style of cross-hatch brushes is affected by - // transparent/solid background mode - - if (m_brush.GetStyle() != wxSOLID && m_brush.GetStyle() != wxTRANSPARENT) - { - gdk_gc_set_fill( m_brushGC, - (m_backgroundMode == wxTRANSPARENT) ? GDK_STIPPLED : GDK_OPAQUE_STIPPLED); - } } -void wxWindowDC::SetPalette( const wxPalette& WXUNUSED(palette) ) +void wxWindowDCImpl::SetPalette( const wxPalette& WXUNUSED(palette) ) { - wxFAIL_MSG( wxT("wxWindowDC::SetPalette not implemented") ); + wxFAIL_MSG( wxT("wxWindowDCImpl::SetPalette not implemented") ); } -void wxWindowDC::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) +void wxWindowDCImpl::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); - - wxDC::DoSetClippingRegion( x, y, width, height ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - if (!m_window) return; + if (!m_gdkwindow) return; - GdkRectangle rect; + wxRect rect; rect.x = XLOG2DEV(x); rect.y = YLOG2DEV(y); rect.width = XLOG2DEVREL(width); rect.height = YLOG2DEVREL(height); - gdk_gc_set_clip_rectangle( m_penGC, &rect ); - gdk_gc_set_clip_rectangle( m_brushGC, &rect ); - gdk_gc_set_clip_rectangle( m_textGC, &rect ); - gdk_gc_set_clip_rectangle( m_bgGC, &rect ); + + if (m_window && m_window->m_wxwindow && + (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) + { + rect.x -= rect.width; + } + + DoSetDeviceClippingRegion(wxRegion(rect)); } -void wxWindowDC::DoSetClippingRegionAsRegion( const wxRegion ®ion ) +void wxWindowDCImpl::DoSetDeviceClippingRegion( const wxRegion ®ion ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); if (region.Empty()) { @@ -1300,323 +2038,233 @@ void wxWindowDC::DoSetClippingRegionAsRegion( const wxRegion ®ion ) return; } - if (!m_window) return; - - gdk_gc_set_clip_region( m_penGC, region.GetRegion() ); - gdk_gc_set_clip_region( m_brushGC, region.GetRegion() ); - gdk_gc_set_clip_region( m_textGC, region.GetRegion() ); - gdk_gc_set_clip_region( m_bgGC, region.GetRegion() ); -} + if (!m_gdkwindow) return; -void wxWindowDC::DestroyClippingRegion() -{ - wxCHECK_RET( Ok(), wxT("invalid window dc") ); + if (!m_currentClippingRegion.IsNull()) + m_currentClippingRegion.Intersect( region ); + else + m_currentClippingRegion.Union( region ); - wxDC::DestroyClippingRegion(); +#if USE_PAINT_REGION + if (!m_paintClippingRegion.IsNull()) + m_currentClippingRegion.Intersect( m_paintClippingRegion ); +#endif - if (!m_window) return; + wxCoord xx, yy, ww, hh; + m_currentClippingRegion.GetBox( xx, yy, ww, hh ); + wxGTKDCImpl::DoSetClippingRegion( xx, yy, ww, hh ); - gdk_gc_set_clip_rectangle( m_penGC, (GdkRectangle *) NULL ); - gdk_gc_set_clip_rectangle( m_brushGC, (GdkRectangle *) NULL ); - gdk_gc_set_clip_rectangle( m_textGC, (GdkRectangle *) NULL ); - gdk_gc_set_clip_rectangle( m_bgGC, (GdkRectangle *) NULL ); + GdkRegion* gdkRegion = m_currentClippingRegion.GetRegion(); + gdk_gc_set_clip_region(m_penGC, gdkRegion); + gdk_gc_set_clip_region(m_brushGC, gdkRegion); + gdk_gc_set_clip_region(m_textGC, gdkRegion); + gdk_gc_set_clip_region(m_bgGC, gdkRegion); } -void wxWindowDC::SetUpDC() +void wxWindowDCImpl::DestroyClippingRegion() { - Destroy(); - m_ok = TRUE; - m_logicalFunction = wxCOPY; - m_penGC = gdk_gc_new( m_window ); - m_brushGC = gdk_gc_new( m_window ); - m_textGC = gdk_gc_new( m_window ); - m_bgGC = gdk_gc_new( m_window ); - - wxColour tmp_col( m_textForegroundColour ); - m_textForegroundColour = wxNullColour; - SetTextForeground( tmp_col ); - tmp_col = m_textBackgroundColour; - m_textBackgroundColour = wxNullColour; - SetTextBackground( tmp_col ); - - wxPen tmp_pen( m_pen ); - m_pen = wxNullPen; - SetPen( tmp_pen ); - - wxFont tmp_font( m_font ); - m_font = wxNullFont; - SetFont( tmp_font ); - - wxBrush tmp_brush( m_brush ); - m_brush = wxNullBrush; - SetBrush( tmp_brush ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); -/* - tmp_brush = m_backgroundBrush; - m_backgroundBrush = wxNullBrush; - SetBackground( tmp_brush ); -*/ - tmp_brush = m_backgroundBrush; - m_backgroundBrush = wxNullBrush; - SetBackground( *wxWHITE_BRUSH ); - m_backgroundBrush = tmp_brush; + wxDCImpl::DestroyClippingRegion(); - if (!hatch_bitmap) - { - hatch_bitmap = hatches; - hatch_bitmap[0] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, bdiag_bits, bdiag_width, bdiag_height ); - hatch_bitmap[1] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, cdiag_bits, cdiag_width, cdiag_height ); - hatch_bitmap[2] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, fdiag_bits, fdiag_width, fdiag_height ); - hatch_bitmap[3] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, cross_bits, cross_width, cross_height ); - hatch_bitmap[4] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, horiz_bits, horiz_width, horiz_height ); - hatch_bitmap[5] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, verti_bits, verti_width, verti_height ); - } -} + m_currentClippingRegion.Clear(); -void wxWindowDC::Destroy() -{ - if (m_penGC) gdk_gc_unref( m_penGC ); - m_penGC = (GdkGC*) NULL; - if (m_brushGC) gdk_gc_unref( m_brushGC ); - m_brushGC = (GdkGC*) NULL; - if (m_textGC) gdk_gc_unref( m_textGC ); - m_textGC = (GdkGC*) NULL; - if (m_bgGC) gdk_gc_unref( m_bgGC ); - m_bgGC = (GdkGC*) NULL; -} +#if USE_PAINT_REGION + if (!m_paintClippingRegion.IsEmpty()) + m_currentClippingRegion.Union( m_paintClippingRegion ); +#endif -void wxWindowDC::ComputeScaleAndOrigin() -{ - /* CMB: copy scale to see if it changes */ - double origScaleX = m_scaleX; - double origScaleY = m_scaleY; + if (!m_gdkwindow) return; - wxDC::ComputeScaleAndOrigin(); + GdkRegion* gdkRegion = NULL; + if (!m_currentClippingRegion.IsEmpty()) + gdkRegion = m_currentClippingRegion.GetRegion(); - /* CMB: if scale has changed call SetPen to recalulate the line width */ - if ((m_scaleX != origScaleX || m_scaleY != origScaleY) && - (m_pen.Ok())) - { - /* this is a bit artificial, but we need to force wxDC to think - the pen has changed */ - wxPen pen = m_pen; - m_pen = wxNullPen; - SetPen( pen ); - } + gdk_gc_set_clip_region(m_penGC, gdkRegion); + gdk_gc_set_clip_region(m_brushGC, gdkRegion); + gdk_gc_set_clip_region(m_textGC, gdkRegion); + gdk_gc_set_clip_region(m_bgGC, gdkRegion); } -// Resolution in pixels per logical inch -wxSize wxWindowDC::GetPPI() const +void wxWindowDCImpl::Destroy() { - return wxSize(100, 100); + if (m_penGC) wxFreePoolGC( m_penGC ); + m_penGC = NULL; + if (m_brushGC) wxFreePoolGC( m_brushGC ); + m_brushGC = NULL; + if (m_textGC) wxFreePoolGC( m_textGC ); + m_textGC = NULL; + if (m_bgGC) wxFreePoolGC( m_bgGC ); + m_bgGC = NULL; } -int wxWindowDC::GetDepth() const +void wxWindowDCImpl::SetDeviceOrigin( wxCoord x, wxCoord y ) { - wxFAIL_MSG(wxT("not implemented")); + m_deviceOriginX = x; + m_deviceOriginY = y; - return -1; + ComputeScaleAndOrigin(); } -#if wxUSE_SPLINES -// ----------------------------------- spline code ---------------------------------------- - -void wx_quadratic_spline(double a1, double b1, double a2, double b2, - double a3, double b3, double a4, double b4); -void wx_clear_stack(); -int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3, - double *y3, double *x4, double *y4); -void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, - double x4, double y4); -static bool wx_spline_add_point(double x, double y); -static void wx_spline_draw_point_array(wxDC *dc); - -wxList wx_spline_point_list; +void wxWindowDCImpl::SetAxisOrientation( bool xLeftRight, bool yBottomUp ) +{ + m_signX = (xLeftRight ? 1 : -1); + m_signY = (yBottomUp ? -1 : 1); -#define half(z1, z2) ((z1+z2)/2.0) -#define THRESHOLD 5 + if (m_window && m_window->m_wxwindow && + (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) + m_signX = -m_signX; -/* iterative version */ + ComputeScaleAndOrigin(); +} -void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4, - double b4) +void wxWindowDCImpl::ComputeScaleAndOrigin() { - register double xmid, ymid; - double x1, y1, x2, y2, x3, y3, x4, y4; + const wxRealPoint origScale(m_scaleX, m_scaleY); - wx_clear_stack(); - wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4); + wxDCImpl::ComputeScaleAndOrigin(); - while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) { - xmid = (double)half(x2, x3); - ymid = (double)half(y2, y3); - if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD && - fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) { - wx_spline_add_point( x1, y1 ); - wx_spline_add_point( xmid, ymid ); - } else { - wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3), - (double)half(x3, x4), (double)half(y3, y4), x4, y4); - wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2), - (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid); - } + // if scale has changed call SetPen to recalulate the line width + if ( wxRealPoint(m_scaleX, m_scaleY) != origScale && m_pen.IsOk() ) + { + // this is a bit artificial, but we need to force wxDC to think the pen + // has changed + wxPen pen = m_pen; + m_pen = wxNullPen; + SetPen( pen ); } } -/* utilities used by spline drawing routines */ - -typedef struct wx_spline_stack_struct { - double x1, y1, x2, y2, x3, y3, x4, y4; -} Stack; - -#define SPLINE_STACK_DEPTH 20 -static Stack wx_spline_stack[SPLINE_STACK_DEPTH]; -static Stack *wx_stack_top; -static int wx_stack_count; - -void wx_clear_stack() +// Resolution in pixels per logical inch +wxSize wxWindowDCImpl::GetPPI() const { - wx_stack_top = wx_spline_stack; - wx_stack_count = 0; + return wxSize( (int) (m_mm_to_pix_x * 25.4 + 0.5), (int) (m_mm_to_pix_y * 25.4 + 0.5)); } -void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) +int wxWindowDCImpl::GetDepth() const { - wx_stack_top->x1 = x1; - wx_stack_top->y1 = y1; - wx_stack_top->x2 = x2; - wx_stack_top->y2 = y2; - wx_stack_top->x3 = x3; - wx_stack_top->y3 = y3; - wx_stack_top->x4 = x4; - wx_stack_top->y4 = y4; - wx_stack_top++; - wx_stack_count++; + return gdk_drawable_get_depth(m_gdkwindow); } -int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, - double *x3, double *y3, double *x4, double *y4) -{ - if (wx_stack_count == 0) - return (0); - wx_stack_top--; - wx_stack_count--; - *x1 = wx_stack_top->x1; - *y1 = wx_stack_top->y1; - *x2 = wx_stack_top->x2; - *y2 = wx_stack_top->y2; - *x3 = wx_stack_top->x3; - *y3 = wx_stack_top->y3; - *x4 = wx_stack_top->x4; - *y4 = wx_stack_top->y4; - return (1); -} +//----------------------------------------------------------------------------- +// wxClientDCImpl +//----------------------------------------------------------------------------- -static bool wx_spline_add_point(double x, double y) -{ - wxPoint *point = new wxPoint ; - point->x = (int) x; - point->y = (int) y; - wx_spline_point_list.Append((wxObject*)point); - return TRUE; -} +IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl) -static void wx_spline_draw_point_array(wxDC *dc) +wxClientDCImpl::wxClientDCImpl( wxDC *owner ) + : wxWindowDCImpl( owner ) { - dc->DrawLines(&wx_spline_point_list, 0, 0 ); - wxNode *node = wx_spline_point_list.First(); - while (node) - { - wxPoint *point = (wxPoint *)node->Data(); - delete point; - delete node; - node = wx_spline_point_list.First(); - } } -void wxWindowDC::DoDrawSpline( wxList *points ) +wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *win ) + : wxWindowDCImpl( owner, win ) { - wxCHECK_RET( Ok(), wxT("invalid window dc") ); - - wxPoint *p; - double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; - double x1, y1, x2, y2; + wxCHECK_RET( win, wxT("NULL window in wxClientDCImpl::wxClientDC") ); - wxNode *node = points->First(); - p = (wxPoint *)node->Data(); +#ifdef __WXUNIVERSAL__ + wxPoint ptOrigin = win->GetClientAreaOrigin(); + SetDeviceOrigin(ptOrigin.x, ptOrigin.y); + wxSize size = win->GetClientSize(); + DoSetClippingRegion(0, 0, size.x, size.y); +#endif + // __WXUNIVERSAL__ +} - x1 = p->x; - y1 = p->y; +void wxClientDCImpl::DoGetSize(int *width, int *height) const +{ + wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); - node = node->Next(); - p = (wxPoint *)node->Data(); + m_window->GetClientSize( width, height ); +} - x2 = p->x; - y2 = p->y; - cx1 = (double)((x1 + x2) / 2); - cy1 = (double)((y1 + y2) / 2); - cx2 = (double)((cx1 + x2) / 2); - cy2 = (double)((cy1 + y2) / 2); +//----------------------------------------------------------------------------- +// wxPaintDCImpl +//----------------------------------------------------------------------------- - wx_spline_add_point(x1, y1); +IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxClientDCImpl) - while ((node = node->Next()) != NULL) +// Limit the paint region to the window size. Sometimes +// the paint region is too big, and this risks X11 errors +static void wxLimitRegionToSize(wxRegion& region, const wxSize& sz) +{ + wxRect originalRect = region.GetBox(); + wxRect rect(originalRect); + if (rect.width + rect.x > sz.x) + rect.width = sz.x - rect.x; + if (rect.height + rect.y > sz.y) + rect.height = sz.y - rect.y; + if (rect != originalRect) { - p = (wxPoint *)node->Data(); - x1 = x2; - y1 = y2; - x2 = p->x; - y2 = p->y; - cx4 = (double)(x1 + x2) / 2; - cy4 = (double)(y1 + y2) / 2; - cx3 = (double)(x1 + cx4) / 2; - cy3 = (double)(y1 + cy4) / 2; - - wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4); - - cx1 = cx4; - cy1 = cy4; - cx2 = (double)(cx1 + x2) / 2; - cy2 = (double)(cy1 + y2) / 2; + region = wxRegion(rect); + wxLogTrace(wxT("painting"), wxT("Limiting region from %d, %d, %d, %d to %d, %d, %d, %d\n"), + originalRect.x, originalRect.y, originalRect.width, originalRect.height, + rect.x, rect.y, rect.width, rect.height); } +} - wx_spline_add_point( cx1, cy1 ); - wx_spline_add_point( x2, y2 ); - - wx_spline_draw_point_array( this ); +wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) + : wxClientDCImpl( owner ) +{ } -#endif // wxUSE_SPLINE +wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *win ) + : wxClientDCImpl( owner, win ) +{ +#if USE_PAINT_REGION + if (!win->m_clipPaintRegion) + return; -//----------------------------------------------------------------------------- -// wxPaintDC -//----------------------------------------------------------------------------- + wxSize sz = win->GetSize(); + m_paintClippingRegion = win->m_nativeUpdateRegion; + wxLimitRegionToSize(m_paintClippingRegion, sz); -IMPLEMENT_DYNAMIC_CLASS(wxPaintDC,wxWindowDC) + GdkRegion *region = m_paintClippingRegion.GetRegion(); + if ( region ) + { + m_currentClippingRegion.Union( m_paintClippingRegion ); + wxLimitRegionToSize(m_currentClippingRegion, sz); -wxPaintDC::wxPaintDC() - : wxWindowDC() -{ + if (sz.x <= 0 || sz.y <= 0) + return ; + + gdk_gc_set_clip_region( m_penGC, region ); + gdk_gc_set_clip_region( m_brushGC, region ); + gdk_gc_set_clip_region( m_textGC, region ); + gdk_gc_set_clip_region( m_bgGC, region ); + } +#endif } -wxPaintDC::wxPaintDC( wxWindow *win ) - : wxWindowDC( win ) +// ---------------------------------------------------------------------------- +// wxDCModule +// ---------------------------------------------------------------------------- + +class wxDCModule : public wxModule { -} +public: + bool OnInit(); + void OnExit(); -//----------------------------------------------------------------------------- -// wxClientDC -//----------------------------------------------------------------------------- +private: + DECLARE_DYNAMIC_CLASS(wxDCModule) +}; -IMPLEMENT_DYNAMIC_CLASS(wxClientDC,wxWindowDC) +IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule) -wxClientDC::wxClientDC() - : wxWindowDC() +bool wxDCModule::OnInit() { + wxInitGCPool(); + return true; } -wxClientDC::wxClientDC( wxWindow *win ) - : wxWindowDC( win ) +void wxDCModule::OnExit() { -} + wxCleanUpGCPool(); + for (int i = wxBRUSHSTYLE_LAST_HATCH - wxBRUSHSTYLE_FIRST_HATCH; i--; ) + { + if (hatches[i]) + g_object_unref(hatches[i]); + } +}