X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/bbbbe360a81bab2ce9bc83aed4719765fd46d297..e83c4d401206ff9d5568f03ade6d9713c57bdd51:/src/gtk/dcclient.cpp diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 2dec4c304d..680ab2c6cd 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -1,23 +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 +#include "wx/fontutil.h" + +#include "wx/gtk/private.h" +#include "wx/gtk/private/object.h" + +//----------------------------------------------------------------------------- +// local defines +//----------------------------------------------------------------------------- + +#define XLOG2DEV(x) LogicalToDeviceX(x) +#define XLOG2DEVREL(x) LogicalToDeviceXRel(x) +#define YLOG2DEV(y) LogicalToDeviceY(y) +#define YLOG2DEVREL(y) LogicalToDeviceYRel(y) -#include "gdk/gdk.h" -#include "gtk/gtk.h" +#define USE_PAINT_REGION 1 //----------------------------------------------------------------------------- // local data @@ -29,97 +47,277 @@ #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 //----------------------------------------------------------------------------- -#define RAD2DEG 57.2957795131 +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) + +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 + +static int wxGCPoolSize = 0; + +static wxGC *wxGCPool = NULL; + +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; - - gc_private = (GdkGCPrivate*) gc; + } - if (width == -1) width = src_private->width; - if (height == -1) height = src_private->height; + // Zero initial pool. + memset(wxGCPool, 0, wxGCPoolSize * sizeof(wxGC)); +} - XCopyPlane( drawable_private->xdisplay, - src_private->xwindow, - drawable_private->xwindow, - gc_private->xgc, - xsrc, ysrc, - width, height, - xdest, ydest, - 1 ); +static void wxCleanUpGCPool() +{ + 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; +} + +static GdkGC* wxGetPoolGC( GdkWindow *window, wxPoolGCType type ) +{ + 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; - - if (!window) return; + 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; - if (!widget) return; - m_window = widget->window; - if (!m_window) 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_isMemDC = FALSE; - + m_gdkwindow = window->GTKGetDrawingWindow(); + + // 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); + } + + m_context = window->GTKGetPangoDefaultContext(); + m_layout = pango_layout_new( m_context ); + m_fontdesc = pango_font_description_copy( widget->style->font_desc ); + + // Window not realized ? + if (!m_gdkwindow) + { + // Don't report problems as per MSW. + m_ok = true; + + return; + } + + m_cmap = gtk_widget_get_colormap(widget); + SetUpDC(); /* this must be done after SetUpDC, bacause SetUpDC calls the @@ -128,274 +326,485 @@ wxWindowDC::wxWindowDC( wxWindow *window ) and this might not be desired as the standard dc background 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 wxWindowDCImpl::SetUpDC( bool isMemDC ) +{ + 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 ); } -void wxWindowDC::FloodFill( long WXUNUSED(x), long WXUNUSED(y), - const wxColour &WXUNUSED(col), int WXUNUSED(style) ) +void wxWindowDCImpl::DoGetSize( int* width, int* height ) const { - wxFAIL_MSG( "wxWindowDC::FloodFill not implemented" ); + wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); + + m_window->GetSize(width, height); } -bool wxWindowDC::GetPixel( long WXUNUSED(x1), long WXUNUSED(y1), wxColour *WXUNUSED(col) ) const +bool wxWindowDCImpl::DoFloodFill(wxCoord x, wxCoord y, + const wxColour& col, wxFloodFillStyle style) { - wxFAIL_MSG( "wxWindowDC::GetPixel not implemented" ); - return FALSE; +#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 } -void wxWindowDC::DrawLine( long x1, long y1, long x2, long y2 ) +bool wxWindowDCImpl::DoGetPixel( wxCoord x1, wxCoord y1, wxColour *col ) const { - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (m_pen.GetStyle() != wxTRANSPARENT) + GdkImage* image = NULL; + if (m_gdkwindow) { - 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::CrossHair( long x, long y ) +void wxWindowDCImpl::DoCrossHair( wxCoord x, wxCoord y ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (m_pen.GetStyle() != wxTRANSPARENT) + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + if ( m_pen.IsNonTransparent() ) { int w = 0; int h = 0; - GetSize( &w, &h ); - long xx = XLOG2DEV(x); - long yy = YLOG2DEV(y); - gdk_draw_line( m_window, m_penGC, 0, yy, XLOG2DEVREL(w), yy ); - gdk_draw_line( m_window, m_penGC, xx, 0, xx, YLOG2DEVREL(h) ); + GetOwner()->GetSize( &w, &h ); + wxCoord xx = XLOG2DEV(x); + wxCoord yy = YLOG2DEV(y); + if (m_gdkwindow) + { + 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::DrawArc( long x1, long y1, long x2, long y2, double xc, double yc ) +void wxWindowDCImpl::DrawingSetup(GdkGC*& gc, bool& originChanged) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - long xx1 = XLOG2DEV(x1); - long yy1 = YLOG2DEV(y1); - long xx2 = XLOG2DEV(x2); - long yy2 = YLOG2DEV(y2); - long xxc = XLOG2DEV((long)xc); - long yyc = YLOG2DEV((long)yc); - double dx = xx1 - xxc; + 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( IsOk(), wxT("invalid window dc") ); + + wxCoord xx1 = XLOG2DEV(x1); + wxCoord yy1 = YLOG2DEV(y1); + wxCoord xx2 = XLOG2DEV(x2); + wxCoord yy2 = YLOG2DEV(y2); + wxCoord xxc = XLOG2DEV(xc); + wxCoord yyc = YLOG2DEV(yc); + double dx = xx1 - xxc; double dy = yy1 - yyc; - double radius = sqrt(dx*dx+dy*dy); - long r = (long)radius; + double radius = sqrt((double)(dx*dx+dy*dy)); + wxCoord r = (wxCoord)radius; double radius1, radius2; - if (xx1 == xx2 && yy1 == yy2) + if (xx1 == xx2 && yy1 == yy2) { radius1 = 0.0; radius2 = 360.0; - } - else - if (radius == 0.0) + } + else if ( wxIsNullDouble(radius) ) { - radius1 = radius2 = 0.0; - } - else + radius1 = + radius2 = 0.0; + } + else { radius1 = (xx1 - xxc == 0) ? - (yy1 - yyc < 0) ? 90.0 : -90.0 : - -atan2(double(yy1-yyc), double(xx1-xxc)) * RAD2DEG; + (yy1 - yyc < 0) ? 90.0 : -90.0 : + -atan2(double(yy1-yyc), double(xx1-xxc)) * RAD2DEG; radius2 = (xx2 - xxc == 0) ? - (yy2 - yyc < 0) ? 90.0 : -90.0 : - -atan2(double(yy2-yyc), double(xx2-xxc)) * RAD2DEG; + (yy2 - yyc < 0) ? 90.0 : -90.0 : + -atan2(double(yy2-yyc), double(xx2-xxc)) * RAD2DEG; } - long alpha1 = long(radius1 * 64.0); - long alpha2 = long((radius2 - radius1) * 64.0); + wxCoord alpha1 = wxCoord(radius1 * 64.0); + wxCoord alpha2 = wxCoord((radius2 - radius1) * 64.0); while (alpha2 <= 0) alpha2 += 360*64; while (alpha1 > 360*64) alpha1 -= 360*64; - 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_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_penGC, FALSE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 ); - + if (m_gdkwindow) + { + 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.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::DrawEllipticArc( long x, long y, long width, long height, double sa, double ea ) +void wxWindowDCImpl::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double sa, double ea ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - long xx = XLOG2DEV(x); - long yy = YLOG2DEV(y); - long ww = m_signX * XLOG2DEVREL(width); - long hh = m_signY * YLOG2DEVREL(height); - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + wxCoord xx = XLOG2DEV(x); + wxCoord yy = YLOG2DEV(y); + wxCoord ww = m_signX * XLOG2DEVREL(width); + wxCoord hh = m_signY * YLOG2DEVREL(height); + // CMB: handle -ve width and/or height if (ww < 0) { ww = -ww; xx = xx - ww; } if (hh < 0) { hh = -hh; yy = yy - hh; } - - long start = long(sa * 64.0); - long end = long(ea * 64.0); - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_brushGC, 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 (m_gdkwindow) + { + wxCoord start = wxCoord(sa * 64.0); + wxCoord end = wxCoord((ea-sa) * 64.0); + + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); + + gdk_draw_arc(m_gdkwindow, gc, true, 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::DrawPoint( long x, long y ) +void wxWindowDCImpl::DoDrawPoint( wxCoord x, wxCoord y ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_point( m_window, m_penGC, XLOG2DEV(x), YLOG2DEV(y) ); - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + if ( m_pen.IsNonTransparent() && m_gdkwindow ) + gdk_draw_point( m_gdkwindow, m_penGC, XLOG2DEV(x), YLOG2DEV(y) ); + CalcBoundingBox (x, y); } -void wxWindowDC::DrawLines( int n, wxPoint points[], long xoffset, long yoffset ) +void wxWindowDCImpl::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (m_pen.GetStyle() == wxTRANSPARENT) return; - if (n <= 0) return; - - CalcBoundingBox( points[0].x + xoffset, points[0].y + yoffset ); - - for (int i = 0; i < n-1; i++) - { - long x1 = XLOG2DEV(points[i].x + xoffset); - long x2 = XLOG2DEV(points[i+1].x + xoffset); - long y1 = YLOG2DEV(points[i].y + yoffset); // oh, what a waste - long y2 = YLOG2DEV(points[i+1].y + yoffset); - gdk_draw_line( m_window, m_penGC, x1, y1, x2, y2 ); - - CalcBoundingBox( points[i+1].x + xoffset, points[i+1].y + yoffset ); - } -} - -void wxWindowDC::DrawLines( wxList *points, long xoffset, long yoffset ) -{ - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (m_pen.GetStyle() == wxTRANSPARENT) return; - - wxNode *node = points->First(); - if (!node) return; - - wxPoint *pt = (wxPoint*)node->Data(); - CalcBoundingBox( pt->x + xoffset, pt->y + yoffset ); - - while (node->Next()) - { - wxPoint *point = (wxPoint*)node->Data(); - wxPoint *npoint = (wxPoint*)node->Next()->Data(); - long x1 = XLOG2DEV(point->x + xoffset); - long x2 = XLOG2DEV(npoint->x + xoffset); - long y1 = YLOG2DEV(point->y + yoffset); // and a waste again... - long y2 = YLOG2DEV(npoint->y + yoffset); - gdk_draw_line( m_window, m_penGC, x1, y1, x2, y2 ); - node = node->Next(); - - CalcBoundingBox( npoint->x + xoffset, npoint->y + yoffset ); - } -} - -void wxWindowDC::DrawPolygon( int n, wxPoint points[], long xoffset, long yoffset, int WXUNUSED(fillStyle) ) -{ - wxCHECK_RET( Ok(), "invalid window dc" ); - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + if (n <= 0) return; - - GdkPoint *gdkpoints = new GdkPoint[n+1]; - int i; - for (i = 0 ; i < n ; i++) + + 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(points); + + if (doScale) + gpts = new GdkPoint[n]; + + for (int 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 (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_polygon (m_window, m_brushGC, TRUE, gdkpoints, n); - - // To do: Fillstyle - - if (m_pen.GetStyle() != wxTRANSPARENT) - for (i = 0 ; i < n ; i++) - { - 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); + if (doScale) + { + gpts[i].x = XLOG2DEV(points[i].x + xoffset); + gpts[i].y = YLOG2DEV(points[i].y + yoffset); } - - delete[] gdkpoints; + 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::DrawPolygon( wxList *lines, long xoffset, long yoffset, int WXUNUSED(fillStyle)) +void wxWindowDCImpl::DoDrawPolygon( int n, wxPoint points[], + wxCoord xoffset, wxCoord yoffset, + wxPolygonFillMode WXUNUSED(fillStyle) ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - int n = lines->Number(); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + if (n <= 0) return; - - GdkPoint *gdkpoints = new GdkPoint[n]; - wxNode *node = lines->First(); - int cnt = 0; - while (node) - { - wxPoint *p = (wxPoint *) node->Data(); - gdkpoints[cnt].x = XLOG2DEV(p->x + xoffset); - gdkpoints[cnt].y = YLOG2DEV(p->y + yoffset); - node = node->Next(); - cnt++; - - CalcBoundingBox( p->x + xoffset, p->y + yoffset ); - } - - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_polygon (m_window, m_brushGC, TRUE, gdkpoints, n); - - // To do: Fillstyle - - if (m_pen.GetStyle() != wxTRANSPARENT) - { - int i; - for (i = 0 ; i < n ; i++) - { - 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 ); + + //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++) + { + 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_gdkwindow) + { + if ( m_brush.IsNonTransparent() ) + { + GdkGC* gc; + bool originChanged; + DrawingSetup(gc, originChanged); + + gdk_draw_polygon(m_gdkwindow, gc, true, gdkpoints, n); + + if (originChanged) + gdk_gc_set_ts_origin(gc, 0, 0); + } + + if ( m_pen.IsNonTransparent() ) + { +/* + 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::DrawRectangle( long x, long y, long width, long height ) +void wxWindowDCImpl::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) { - wxCHECK_RET( Ok(), "invalid window dc" ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + wxCoord xx = XLOG2DEV(x); + wxCoord yy = YLOG2DEV(y); + wxCoord ww = m_signX * XLOG2DEVREL(width); + wxCoord hh = m_signY * YLOG2DEVREL(height); - long xx = XLOG2DEV(x); - long yy = YLOG2DEV(y); - long ww = m_signX * XLOG2DEVREL(width); - long hh = m_signY * YLOG2DEVREL(height); - // CMB: draw nothing if transformed w or h is 0 if (ww == 0 || hh == 0) return; @@ -403,27 +812,66 @@ void wxWindowDC::DrawRectangle( long x, long y, long width, long height ) if (ww < 0) { ww = -ww; xx = xx - ww; } if (hh < 0) { hh = -hh; yy = yy - hh; } - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh ); - - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy, ww-1, hh-1 ); - + if (m_gdkwindow) + { + 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 ); + } + + // 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::DrawRoundedRectangle( long x, long y, long width, long height, double radius ) +void wxWindowDCImpl::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + if (radius < 0.0) radius = - radius * ((width < height) ? width : height); - - long xx = XLOG2DEV(x); - long yy = YLOG2DEV(y); - long ww = m_signX * XLOG2DEVREL(width); - long hh = m_signY * YLOG2DEVREL(height); - long rr = XLOG2DEVREL((long)radius); + + wxCoord xx = XLOG2DEV(x); + wxCoord yy = YLOG2DEV(y); + wxCoord ww = m_signX * XLOG2DEVREL(width); + wxCoord hh = m_signY * YLOG2DEVREL(height); + wxCoord rr = XLOG2DEVREL((wxCoord)radius); // CMB: handle -ve width and/or height if (ww < 0) { ww = -ww; xx = xx - ww; } @@ -433,7 +881,7 @@ void wxWindowDC::DrawRoundedRectangle( long x, long y, long width, long height, // X drawing errors with small radii if (rr == 0) { - DrawRectangle( x, y, width, height ); + DoDrawRectangle( x, y, width, height ); return; } @@ -442,932 +890,1381 @@ void wxWindowDC::DrawRoundedRectangle( long x, long y, long width, long height, // 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--; } - // CMB: ensure dd is not larger than rectangle otherwise we - // get an hour glass shape - long dd = 2 * rr; - if (dd > ww) dd = ww; - if (dd > hh) dd = hh; - rr = dd / 2; + if (m_gdkwindow) + { + // CMB: ensure dd is not larger than rectangle otherwise we + // get an hour glass shape + wxCoord dd = 2 * rr; + if (dd > ww) dd = ww; + if (dd > hh) dd = hh; + rr = dd / 2; + + if ( m_brush.IsNonTransparent() ) + { + 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.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 ); + 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 ); + } + } + + // this ignores the radius + CalcBoundingBox( x, y ); + CalcBoundingBox( x + width, y + height ); +} + +void wxWindowDCImpl::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) +{ + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + wxCoord xx = XLOG2DEV(x); + wxCoord yy = YLOG2DEV(y); + wxCoord ww = m_signX * XLOG2DEVREL(width); + wxCoord hh = m_signY * YLOG2DEVREL(height); + + // CMB: handle -ve width and/or height + if (ww < 0) { ww = -ww; xx = xx - ww; } + if (hh < 0) { hh = -hh; yy = yy - hh; } + + if (m_gdkwindow) + { + 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.IsNonTransparent() ) + gdk_draw_arc( m_gdkwindow, m_penGC, false, xx, yy, ww, hh, 0, 360*64 ); + } + + CalcBoundingBox( x, y ); + CalcBoundingBox( x + width, y + height ); +} + +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, true ); +} + +// 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) +{ + 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; +} + +// 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; +} + +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") ); + + if (!m_gdkwindow) return; + + const int w = bitmap.GetWidth(); + const int h = bitmap.GetHeight(); + + // 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); + + // device coords + int xx = LogicalToDeviceX(x); + const int yy = LogicalToDeviceY(y); + const int ww = LogicalToDeviceXRel(w); + const int hh = LogicalToDeviceYRel(h); + + if (m_window && m_window->GetLayoutDirection() == wxLayout_RightToLeft) + xx -= ww; + + 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; + } + + const bool isScaled = ww != w || hh != h; + const bool hasAlpha = bitmap.HasAlpha(); + GdkGC* const use_gc = m_penGC; + + GdkPixmap* mask = NULL; + // mask does not work when drawing a pixbuf with alpha + if (useMask && !hasAlpha) + { + 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); + } + + // 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 + { + g_object_ref(pixmap); + } + + if (isScaled) + { + 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); + } + + 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 (mask) + { + g_object_unref(mask); + gdk_gc_set_clip_region(use_gc, clipRegion); + } +} + +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 ) +{ + wxCHECK_MSG( IsOk(), false, wxT("invalid window dc") ); + wxCHECK_MSG( source, false, wxT("invalid source dc") ); + + if (!m_gdkwindow) return false; + + 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; + } + + 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; + } + } + + // 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); + + 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; + } + + const bool isScaled = src_w != dst_w || src_h != dst_h; + double scale_x = 0; + double scale_y = 0; + if (isScaled) + { + // 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); + } + + GdkGC* const use_gc = m_penGC; + + if (mask) + { + g_object_ref(mask); + int srcMask_x = src_x; + int srcMask_y = src_y; + if (xsrcMask != -1 || ysrcMask != -1) + { + srcMask_x = source->LogicalToDeviceX(xsrcMask); + srcMask_y = source->LogicalToDeviceY(ysrcMask); + } + if (isScaled) + { + 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; + } + 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, + 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); + } + + 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; + } + + const wxRasterOperationMode logical_func_save = m_logicalFunction; + SetLogicalFunction(logical_func); + if (memDC == NULL) + gdk_gc_set_subwindow(use_gc, GDK_INCLUDE_INFERIORS); + + if (isScaled) + { + 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); + } + + SetLogicalFunction(logical_func_save); + if (memDC == NULL) + gdk_gc_set_subwindow(use_gc, GDK_CLIP_BY_CHILDREN); + + if (pixmap) + g_object_unref(pixmap); + if (mask) + { + g_object_unref(mask); + gdk_gc_set_clip_region(use_gc, clipRegion); + } + return true; +} + +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 + + wxCharBuffer data = wxGTK_CONV(text); + if ( !data ) + return; + + pango_layout_set_text(m_layout, data, data.length()); + const bool setAttrs = m_font.GTKSetPangoAttrs(m_layout); + + 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. + + // 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); + + // Draw layout. + int x_rtl = x; + 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 (m_brush.GetStyle() != wxTRANSPARENT) + if (isScaled) { - 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 ); + // reset unscaled size + pango_font_description_set_size( m_fontdesc, oldSize ); + + // actually apply unscaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); } - - if (m_pen.GetStyle() != wxTRANSPARENT) + if (setAttrs) { - 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 ); + // undo underline attributes setting: + pango_layout_set_attributes(m_layout, NULL); } - - // this ignores the radius - CalcBoundingBox( x, y ); - CalcBoundingBox( x + width, y + height ); -} - -void wxWindowDC::DrawEllipse( long x, long y, long width, long height ) -{ - wxCHECK_RET( Ok(), "invalid window dc" ); - - long xx = XLOG2DEV(x); - long yy = YLOG2DEV(y); - long ww = m_signX * XLOG2DEVREL(width); - long hh = m_signY * YLOG2DEVREL(height); - // CMB: handle -ve width and/or height - if (ww < 0) { ww = -ww; xx = xx - ww; } - if (hh < 0) { hh = -hh; yy = yy - hh; } - - if (m_brush.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 ); - - if (m_pen.GetStyle() != wxTRANSPARENT) - gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, 0, 360*64 ); - - CalcBoundingBox( x - width, y - height ); - CalcBoundingBox( x + width, y + height ); + CalcBoundingBox(xLogical + int(w / m_scaleX), yLogical + int(h / m_scaleY)); + CalcBoundingBox(xLogical, yLogical); } -bool wxWindowDC::CanDrawBitmap() const +// 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 ) { - return TRUE; -} + if (!m_gdkwindow || text.empty()) + return; -void wxWindowDC::DrawIcon( const wxIcon &icon, long x, long y ) -{ - DrawBitmap( icon, x, y, TRUE ); -} + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); -void wxWindowDC::DrawBitmap( const wxBitmap &bitmap, long x, long y, bool useMask ) -{ - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (!bitmap.Ok()) return; - - /* scale/translate size and position */ - - int xx = XLOG2DEV(x); - int yy = YLOG2DEV(y); - - int w = bitmap.GetWidth(); - int h = bitmap.GetHeight(); - - int ww = XLOG2DEVREL(w); - int hh = YLOG2DEVREL(h); - - /* scale bitmap if required */ - - wxBitmap use_bitmap; - - if ((w != ww) || (h != hh)) - { - wxImage image( bitmap ); - image = image.Scale( ww, hh ); - - use_bitmap = image.ConvertToBitmap(); - } - else + x = XLOG2DEV(x); + y = YLOG2DEV(y); + + 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) { - use_bitmap = bitmap; + //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 ); } - - /* apply mask if any */ - - GdkBitmap *mask = (GdkBitmap *) NULL; - if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap(); - - if (useMask && mask) + + 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) { - gdk_gc_set_clip_mask( m_penGC, mask ); - gdk_gc_set_clip_origin( m_penGC, xx, yy ); + // reset unscaled size + pango_font_description_set_size( m_fontdesc, oldSize ); + + // actually apply unscaled font + pango_layout_set_font_description( m_layout, m_fontdesc ); } - - /* draw XPixmap or XBitmap, depending on what the wxBitmap contains */ - - GdkPixmap *pm = use_bitmap.GetPixmap(); - if (pm) + + CalcBoundingBox(x+minX, y+minY); + CalcBoundingBox(x+maxX, y+maxY); +} + +void wxWindowDCImpl::DoGetTextExtent(const wxString &string, + wxCoord *width, wxCoord *height, + 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 + if ( !theFont || !theFont->IsOk() ) + theFont = &m_font; + + // and use it if it's valid + if ( theFont->IsOk() ) { - gdk_draw_pixmap( m_window, m_penGC, pm, 0, 0, xx, yy, -1, -1 ); + pango_layout_set_font_description + ( + m_layout, + theFont->GetNativeFontInfo()->description + ); } - else + + // Set layout's text + const wxCharBuffer dataUTF8 = wxGTK_CONV_FONT(string, *theFont); + if ( !dataUTF8 ) { - GdkBitmap *bm = use_bitmap.GetBitmap(); - if (bm) - { - gdk_draw_bitmap( m_window, m_penGC, bm, 0, 0, xx, yy, -1, -1 ); - } + // hardly ideal, but what else can we do if conversion failed? + return; } - - /* remove mask again if any */ - - if (useMask && mask) + + pango_layout_set_text(m_layout, dataUTF8, -1); + + int h; + pango_layout_get_pixel_size(m_layout, width, &h); + if (descent) { - gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL ); - gdk_gc_set_clip_origin( m_penGC, 0, 0 ); - } - - CalcBoundingBox( x, y ); - CalcBoundingBox( x + w, y + h ); -} - -bool wxWindowDC::Blit( long xdest, long ydest, long width, long height, - wxDC *source, long xsrc, long ysrc, int logical_func, bool useMask ) -{ - /* 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( Ok(), FALSE, "invalid window dc" ); - - wxCHECK_MSG( source, FALSE, "invalid source dc" ); - - wxClientDC *srcDC = (wxClientDC*)source; - wxMemoryDC *memDC = (wxMemoryDC*)source; - - bool use_bitmap_method = FALSE; - - if (srcDC->m_isMemDC) - { - if (!memDC->m_selected.Ok()) return FALSE; - - /* 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. - we HAVE TO use the direct way for memory dcs - that have mask since the XCopyArea doesn't know - about masks and 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 = ( (useMask && (memDC->m_selected.GetMask())) || - ((xsrc == 0) && (ysrc == 0) && - (width == memDC->m_selected.GetWidth()) && - (height == memDC->m_selected.GetHeight()) ) - ); - } - - CalcBoundingBox( xdest, ydest ); - CalcBoundingBox( xdest + width, ydest + height ); - - int old_logical_func = m_logicalFunction; - SetLogicalFunction( logical_func ); - - if (use_bitmap_method) - { - /* scale/translate bitmap size */ - - long bm_width = memDC->m_selected.GetWidth(); - long bm_height = memDC->m_selected.GetHeight(); - - long bm_ww = XLOG2DEVREL( bm_width ); - long bm_hh = YLOG2DEVREL( bm_height ); - - /* scale bitmap if required */ - - wxBitmap use_bitmap; - - if ((bm_width != bm_ww) || (bm_height != bm_hh)) - { - wxImage image( memDC->m_selected ); - image = image.Scale( bm_ww, bm_hh ); - - use_bitmap = image.ConvertToBitmap(); - } - else - { - use_bitmap = memDC->m_selected; - } - - /* scale/translate size and position */ - - long xx = XLOG2DEV(xdest); - long yy = YLOG2DEV(ydest); - - long ww = XLOG2DEVREL(width); - long hh = YLOG2DEVREL(height); - - /* apply mask if any */ - - GdkBitmap *mask = (GdkBitmap *) NULL; - if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap(); - - if (useMask && mask) - { - gdk_gc_set_clip_mask( m_penGC, mask ); - gdk_gc_set_clip_origin( m_penGC, xx, yy ); - } - - /* draw XPixmap or XBitmap, depending on what the wxBitmap contains */ - - GdkPixmap *pm = use_bitmap.GetPixmap(); - if (pm) - { - gdk_draw_pixmap( m_window, m_penGC, pm, 0, 0, xx, yy, ww, hh ); - } - else - { - GdkBitmap *bm = use_bitmap.GetBitmap(); - if (bm) - { - gdk_draw_bitmap( m_window, m_penGC, bm, 0, 0, xx, yy, ww, hh ); - } - } - - /* remove mask again if any */ - - if (useMask && mask) - { - gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL ); - gdk_gc_set_clip_origin( m_penGC, 0, 0 ); - } + 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); } - else /* use_bitmap_method */ - { - /* scale/translate size and position */ - - long xx = XLOG2DEV(xdest); - long yy = YLOG2DEV(ydest); - - long ww = XLOG2DEVREL(width); - long hh = YLOG2DEVREL(height); - - 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. */ - - wxBitmap bitmap( width, height ); - gdk_window_copy_area( bitmap.GetPixmap(), m_penGC, 0, 0, - srcDC->GetWindow(), - xsrc, ysrc, width, height ); - - /* scale image */ - - wxImage image( bitmap ); - image = image.Scale( ww, hh ); - - /* convert to bitmap */ - - bitmap = image.ConvertToBitmap(); - - /* draw scaled bitmap */ - - gdk_draw_pixmap( m_window, m_penGC, bitmap.GetPixmap(), 0, 0, xx, yy, -1, -1 ); - - } - else - { - /* no scaling and not a memory dc with a mask either */ - - gdk_window_copy_area( m_window, m_penGC, xx, yy, - srcDC->GetWindow(), - xsrc, ysrc, width, height ); - } - } - - SetLogicalFunction( old_logical_func ); - return TRUE; -} - -void wxWindowDC::DrawText( const wxString &text, long x, long y, bool WXUNUSED(use16) ) -{ - wxCHECK_RET( Ok(), "invalid window dc" ); - - GdkFont *font = m_font.GetInternalFont( m_scaleY ); + if (height) + *height = h; + + // Reset old font description + if (theFont->IsOk()) + pango_layout_set_font_description( m_layout, m_fontdesc ); +} - x = XLOG2DEV(x); - y = YLOG2DEV(y); - /* CMB 21/5/98: draw text background if mode is wxSOLID */ - if (m_backgroundMode == wxSOLID) +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 ) { - long width = gdk_string_width( font, text ); - long 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() ); + // hardly ideal, but what else can we do if conversion failed? + wxLogLastError(wxT("DoGetPartialTextExtents")); + return false; } - gdk_draw_string( m_window, font, m_textGC, x, y + font->ascent, text ); - /* 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()) + 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)) { - long width = gdk_string_width( font, text ); - 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); + pango_layout_iter_get_cluster_extents(iter, NULL, &pos); + int position = PANGO_PIXELS(pos.x); + widths[i++] = position; } - - long w, h; - GetTextExtent (text, &w, &h); - CalcBoundingBox (x + w, y + h); - CalcBoundingBox (x, y); -} + while (i < len) + widths[i++] = PANGO_PIXELS(pos.x + pos.width); + pango_layout_iter_free(iter); -bool wxWindowDC::CanGetTextExtent() const -{ - return TRUE; + return true; } -void wxWindowDC::GetTextExtent( const wxString &string, long *width, long *height, - long *descent, long *externalLeading, - wxFont *theFont, bool WXUNUSED(use16) ) + +wxCoord wxWindowDCImpl::GetCharWidth() const { - wxCHECK_RET( Ok(), "invalid window dc" ); - - wxFont fontToUse = m_font; - if (theFont) fontToUse = *theFont; - - GdkFont *font = fontToUse.GetInternalFont( m_scaleY ); - if (width) (*width) = long(gdk_string_width( font, string ) / m_scaleX); - if (height) (*height) = long((font->ascent + font->descent) / m_scaleY); - if (descent) (*descent) = long(font->descent / m_scaleY); - if (externalLeading) (*externalLeading) = 0; // ?? + pango_layout_set_text( m_layout, "H", 1 ); + int w; + pango_layout_get_pixel_size( m_layout, &w, NULL ); + return w; } -long wxWindowDC::GetCharWidth() +wxCoord wxWindowDCImpl::GetCharHeight() const { - wxCHECK_MSG( Ok(), 0, "invalid window dc" ); - - GdkFont *font = m_font.GetInternalFont( m_scaleY ); - return long(gdk_string_width( font, "H" ) / m_scaleX); + 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") ); + + wxCoord h = PANGO_PIXELS (pango_font_metrics_get_descent (metrics) + + pango_font_metrics_get_ascent (metrics)); + pango_font_metrics_unref (metrics); + return h; } -long wxWindowDC::GetCharHeight() +void wxWindowDCImpl::Clear() { - wxCHECK_MSG( Ok(), 0, "invalid window dc" ); - - GdkFont *font = m_font.GetInternalFont( m_scaleY ); - return long((font->ascent + font->descent) / m_scaleY); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + 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(), "invalid window dc" ); - - if (!m_isMemDC) - { - gdk_window_clear( m_window ); - } - else + m_font = font; + + if (m_font.IsOk()) { - int width,height; - GetSize( &width, &height ); - gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height ); + if (m_fontdesc) + pango_font_description_free( m_fontdesc ); + + m_fontdesc = pango_font_description_copy( m_font.GetNativeFontInfo()->description ); + + + if (m_window) + { + PangoContext *oldContext = m_context; + + 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::SetFont( const wxFont &font ) +void wxWindowDCImpl::SetPen( const wxPen &pen ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - m_font = font; -} + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); -void wxWindowDC::SetPen( const wxPen &pen ) -{ - wxCHECK_RET( Ok(), "invalid window dc" ); - if (m_pen == pen) return; - + m_pen = pen; - - if (!m_pen.Ok()) return; - + + if (!m_pen.IsOk()) return; + + if (!m_gdkwindow) return; + gint width = m_pen.GetWidth(); - // CMB: if width is non-zero scale it with the dc if (width <= 0) { + // CMB: if width is non-zero scale it with the dc width = 1; } else { // X doesn't allow different width in x and y and so we take // the average - double w = 0.5 + (abs(XLOG2DEVREL(width)) + abs(YLOG2DEVREL(width))) / 2.0; + double w = 0.5 + + ( 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; + } } - - GdkLineStyle lineStyle = GDK_LINE_SOLID; + + 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 wxGTKDash *req_dash; + + GdkLineStyle lineStyle = GDK_LINE_ON_OFF_DASH; switch (m_pen.GetStyle()) { - case wxSOLID: { lineStyle = GDK_LINE_SOLID; break; } - case wxDOT: { lineStyle = GDK_LINE_ON_OFF_DASH; break; } - case wxLONG_DASH: { lineStyle = GDK_LINE_ON_OFF_DASH; break; } - case wxSHORT_DASH: { lineStyle = GDK_LINE_ON_OFF_DASH; break; } - case wxDOT_DASH: { lineStyle = GDK_LINE_DOUBLE_DASH; break; } + case wxPENSTYLE_USER_DASH: + req_nb_dash = m_pen.GetDashCount(); + req_dash = (wxGTKDash*)m_pen.GetDash(); + break; + case wxPENSTYLE_DOT: + req_nb_dash = 2; + req_dash = dotted; + break; + case wxPENSTYLE_LONG_DASH: + req_nb_dash = 2; + req_dash = wxCoord_dashed; + break; + case wxPENSTYLE_SHORT_DASH: + req_nb_dash = 2; + req_dash = short_dashed; + break; + case wxPENSTYLE_DOT_DASH: + req_nb_dash = 4; + req_dash = dotted_dashed; + break; + + case wxPENSTYLE_TRANSPARENT: + case wxPENSTYLE_STIPPLE_MASK_OPAQUE: + case wxPENSTYLE_STIPPLE: + case wxPENSTYLE_SOLID: + default: + lineStyle = GDK_LINE_SOLID; + req_dash = NULL; + req_nb_dash = 0; + break; + } + + if (req_dash && 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++) + real_req_dash[i] = req_dash[i] * width; + gdk_gc_set_dashes( m_penGC, 0, real_req_dash, req_nb_dash ); + delete[] real_req_dash; + } + else + { + // No Memory. We use non-scaled dash pattern... + gdk_gc_set_dashes( m_penGC, 0, (wxGTKDash*)req_dash, req_nb_dash ); + } } - + 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 ); - + m_pen.GetColour().CalcPixel( m_cmap ); 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(), "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_gdkwindow) return; + m_brush.GetColour().CalcPixel( m_cmap ); gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() ); - - GdkFill fillStyle = GDK_SOLID; - switch (m_brush.GetStyle()) + + gdk_gc_set_fill( m_brushGC, GDK_SOLID ); + + if ((m_brush.GetStyle() == wxBRUSHSTYLE_STIPPLE) && (m_brush.GetStipple()->IsOk())) { - case wxSOLID: - case wxTRANSPARENT: - break; - default: - fillStyle = GDK_STIPPLED; + if (m_brush.GetStipple()->GetDepth() != 1) + { + gdk_gc_set_fill( m_brushGC, GDK_TILED ); + gdk_gc_set_tile( m_brushGC, m_brush.GetStipple()->GetPixmap() ); + } + else + { + gdk_gc_set_fill( m_brushGC, GDK_STIPPLED ); + gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetPixmap() ); + } } - - gdk_gc_set_fill( m_brushGC, fillStyle ); - - if (m_brush.GetStyle() == wxSTIPPLE) + + if ((m_brush.GetStyle() == wxBRUSHSTYLE_STIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask())) { - gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetPixmap() ); + gdk_gc_set_fill( m_textGC, GDK_OPAQUE_STIPPLED); + gdk_gc_set_stipple( m_textGC, m_brush.GetStipple()->GetMask()->GetBitmap() ); } - - if (IS_HATCH(m_brush.GetStyle())) + + if (m_brush.IsHatch()) { - int num = m_brush.GetStyle() - wxBDIAGONAL_HATCH; - gdk_gc_set_stipple( m_brushGC, hatches[num] ); + gdk_gc_set_fill( m_brushGC, GDK_STIPPLED ); + 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(), "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_owner) - { - m_owner->SetBackgroundColour( m_backgroundBrush.GetColour() ); - } - - 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() ); - - GdkFill fillStyle = GDK_SOLID; - switch (m_backgroundBrush.GetStyle()) - { - case wxSOLID: - case wxTRANSPARENT: - break; - default: - fillStyle = GDK_STIPPLED; - } - - gdk_gc_set_fill( m_bgGC, fillStyle ); - - if (m_backgroundBrush.GetStyle() == wxSTIPPLE) + + if (!m_backgroundBrush.IsOk()) 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); + + + gdk_gc_set_fill( m_bgGC, GDK_SOLID ); + + if (m_backgroundBrush.GetStyle() == wxBRUSHSTYLE_STIPPLE) { - gdk_gc_set_stipple( m_bgGC, m_backgroundBrush.GetStipple()->GetPixmap() ); + const wxBitmap* stipple = m_backgroundBrush.GetStipple(); + if (stipple->IsOk()) + { + 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()) { - int num = m_backgroundBrush.GetStyle() - wxBDIAGONAL_HATCH; - gdk_gc_set_stipple( m_bgGC, hatches[num] ); + gdk_gc_set_fill( m_bgGC, GDK_STIPPLED ); + gdk_gc_set_stipple(m_bgGC, GetHatch(m_backgroundBrush.GetStyle())); } } -void wxWindowDC::SetLogicalFunction( int function ) +void wxWindowDCImpl::SetLogicalFunction( wxRasterOperationMode function ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - if (m_logicalFunction == function) return; - - GdkFunction mode = GDK_COPY; + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + if (m_logicalFunction == function) + return; + + // VZ: shouldn't this be a CHECK? + if (!m_gdkwindow) + return; + + GdkFunction mode; switch (function) { - case wxXOR: mode = GDK_INVERT; break; - case wxINVERT: mode = GDK_INVERT; break; - default: break; + case wxXOR: mode = GDK_XOR; break; + case wxINVERT: mode = GDK_INVERT; break; + 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 wxAND: mode = GDK_AND; break; + case wxOR: mode = GDK_OR; break; + case wxEQUIV: mode = GDK_EQUIV; break; + case wxNAND: mode = GDK_NAND; break; + case wxAND_INVERT: mode = GDK_AND_INVERT; break; + case wxCOPY: mode = GDK_COPY; break; + case wxNO_OP: mode = GDK_NOOP; break; + case wxSRC_INVERT: mode = GDK_COPY_INVERT; break; + case wxNOR: mode = GDK_NOR; break; + default: + wxFAIL_MSG("unknown mode"); + return; } - + m_logicalFunction = function; + 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(), "invalid window dc" ); - - if (m_textForegroundColour == col) return; - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + // 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; - - 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(), "invalid window dc" ); - - if (m_textBackgroundColour == col) return; - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + // same as above + if ( !col.IsOk() || (m_textBackgroundColour == col) ) + return; + m_textBackgroundColour = col; - if (!m_textBackgroundColour.Ok()) 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(), "invalid window dc" ); - - m_backgroundMode = mode; + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); - // 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); - } + m_backgroundMode = mode; } -void wxWindowDC::SetPalette( const wxPalette& WXUNUSED(palette) ) +void wxWindowDCImpl::SetPalette( const wxPalette& WXUNUSED(palette) ) { - wxFAIL_MSG( "wxWindowDC::SetPalette not implemented" ); + wxFAIL_MSG( wxT("wxWindowDCImpl::SetPalette not implemented") ); } -void wxWindowDC::SetClippingRegion( long x, long y, long width, long height ) +void wxWindowDCImpl::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - - wxDC::SetClippingRegion( x, y, width, height ); - - GdkRectangle rect; + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + if (!m_gdkwindow) return; + + 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::SetClippingRegion( const wxRegion ®ion ) +void wxWindowDCImpl::DoSetDeviceClippingRegion( const wxRegion ®ion ) { - wxCHECK_RET( Ok(), "invalid window dc" ); - + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + if (region.Empty()) { DestroyClippingRegion(); 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; + + if (!m_currentClippingRegion.IsNull()) + m_currentClippingRegion.Intersect( region ); + else + m_currentClippingRegion.Union( region ); + +#if USE_PAINT_REGION + if (!m_paintClippingRegion.IsNull()) + m_currentClippingRegion.Intersect( m_paintClippingRegion ); +#endif + + wxCoord xx, yy, ww, hh; + m_currentClippingRegion.GetBox( xx, yy, ww, hh ); + wxGTKDCImpl::DoSetClippingRegion( xx, yy, ww, hh ); + + 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::DestroyClippingRegion() +void wxWindowDCImpl::DestroyClippingRegion() { - wxCHECK_RET( Ok(), "invalid window dc" ); - - wxDC::DestroyClippingRegion(); - - 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 ); + wxCHECK_RET( IsOk(), wxT("invalid window dc") ); + + wxDCImpl::DestroyClippingRegion(); + + m_currentClippingRegion.Clear(); + +#if USE_PAINT_REGION + if (!m_paintClippingRegion.IsEmpty()) + m_currentClippingRegion.Union( m_paintClippingRegion ); +#endif + + if (!m_gdkwindow) return; + + GdkRegion* gdkRegion = NULL; + if (!m_currentClippingRegion.IsEmpty()) + 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::Destroy() { - 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 ); - - tmp_brush = m_backgroundBrush; - m_backgroundBrush = wxNullBrush; - SetBackground( tmp_brush ); - - 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 ); - } -} - -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; -} - -GdkWindow *wxWindowDC::GetWindow() -{ - return m_window; -} - -// ----------------------------------- 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; - -#define half(z1, z2) ((z1+z2)/2.0) -#define THRESHOLD 5 - -/* iterative version */ - -void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4, - double b4) -{ - register double xmid, ymid; - double x1, y1, x2, y2, x3, y3, x4, y4; - - wx_clear_stack(); - wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4); - - 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); - } - } -} - -/* 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() -{ - wx_stack_top = wx_spline_stack; - wx_stack_count = 0; + 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; } -void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) +void wxWindowDCImpl::SetDeviceOrigin( wxCoord x, wxCoord y ) { - 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++; + m_deviceOriginX = x; + m_deviceOriginY = y; + + ComputeScaleAndOrigin(); } -int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, - double *x3, double *y3, double *x4, double *y4) +void wxWindowDCImpl::SetAxisOrientation( bool xLeftRight, bool yBottomUp ) { - 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); + m_signX = (xLeftRight ? 1 : -1); + m_signY = (yBottomUp ? -1 : 1); + + if (m_window && m_window->m_wxwindow && + (m_window->GetLayoutDirection() == wxLayout_RightToLeft)) + m_signX = -m_signX; + + ComputeScaleAndOrigin(); } -static bool wx_spline_add_point(double x, double y) +void wxWindowDCImpl::ComputeScaleAndOrigin() { - wxPoint *point = new wxPoint ; - point->x = (int) x; - point->y = (int) y; - wx_spline_point_list.Append((wxObject*)point); - return TRUE; + const wxRealPoint origScale(m_scaleX, m_scaleY); + + wxDCImpl::ComputeScaleAndOrigin(); + + // 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 ); + } } -static void wx_spline_draw_point_array(wxDC *dc) +// Resolution in pixels per logical inch +wxSize wxWindowDCImpl::GetPPI() const { - 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(); - } + return wxSize( (int) (m_mm_to_pix_x * 25.4 + 0.5), (int) (m_mm_to_pix_y * 25.4 + 0.5)); } -void wxWindowDC::DrawSpline( wxList *points ) +int wxWindowDCImpl::GetDepth() const { - wxCHECK_RET( Ok(), "invalid window dc" ); - - wxPoint *p; - double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4; - double x1, y1, x2, y2; - - wxNode *node = points->First(); - p = (wxPoint *)node->Data(); - - x1 = p->x; - y1 = p->y; - - node = node->Next(); - p = (wxPoint *)node->Data(); + return gdk_drawable_get_depth(m_gdkwindow); +} - 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); +//----------------------------------------------------------------------------- +// wxClientDCImpl +//----------------------------------------------------------------------------- - wx_spline_add_point(x1, y1); +IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl) - while ((node = node->Next()) != NULL) - { - 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; +wxClientDCImpl::wxClientDCImpl( wxDC *owner ) + : wxWindowDCImpl( owner ) +{ +} - wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4); +wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *win ) + : wxWindowDCImpl( owner, win ) +{ + wxCHECK_RET( win, wxT("NULL window in wxClientDCImpl::wxClientDC") ); - cx1 = cx4; - cy1 = cy4; - cx2 = (double)(cx1 + x2) / 2; - cy2 = (double)(cy1 + y2) / 2; - } +#ifdef __WXUNIVERSAL__ + wxPoint ptOrigin = win->GetClientAreaOrigin(); + SetDeviceOrigin(ptOrigin.x, ptOrigin.y); + wxSize size = win->GetClientSize(); + DoSetClippingRegion(0, 0, size.x, size.y); +#endif + // __WXUNIVERSAL__ +} - wx_spline_add_point( cx1, cy1 ); - wx_spline_add_point( x2, y2 ); +void wxClientDCImpl::DoGetSize(int *width, int *height) const +{ + wxCHECK_RET( m_window, wxT("GetSize() doesn't work without window") ); - wx_spline_draw_point_array( this ); + m_window->GetClientSize( width, height ); } - //----------------------------------------------------------------------------- -// wxPaintDC +// wxPaintDCImpl //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxPaintDC,wxWindowDC) +IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxClientDCImpl) -wxPaintDC::wxPaintDC() - : wxWindowDC() +// 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) + { + 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); + } } -wxPaintDC::wxPaintDC( wxWindow *win ) - : wxWindowDC( win ) +wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) + : wxClientDCImpl( owner ) { } -//----------------------------------------------------------------------------- -// wxClientDC -//----------------------------------------------------------------------------- +wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *win ) + : wxClientDCImpl( owner, win ) +{ +#if USE_PAINT_REGION + if (!win->m_clipPaintRegion) + return; -IMPLEMENT_DYNAMIC_CLASS(wxClientDC,wxWindowDC) + wxSize sz = win->GetSize(); + m_paintClippingRegion = win->m_nativeUpdateRegion; + wxLimitRegionToSize(m_paintClippingRegion, sz); -wxClientDC::wxClientDC() - : wxWindowDC() -{ + GdkRegion *region = m_paintClippingRegion.GetRegion(); + if ( region ) + { + m_currentClippingRegion.Union( m_paintClippingRegion ); + wxLimitRegionToSize(m_currentClippingRegion, sz); + + 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 } -wxClientDC::wxClientDC( wxWindow *win ) - : wxWindowDC( win ) +// ---------------------------------------------------------------------------- +// wxDCModule +// ---------------------------------------------------------------------------- + +class wxDCModule : public wxModule +{ +public: + bool OnInit(); + void OnExit(); + +private: + DECLARE_DYNAMIC_CLASS(wxDCModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule) + +bool wxDCModule::OnInit() { + wxInitGCPool(); + return true; } +void wxDCModule::OnExit() +{ + wxCleanUpGCPool(); + + for (int i = wxBRUSHSTYLE_LAST_HATCH - wxBRUSHSTYLE_FIRST_HATCH; i--; ) + { + if (hatches[i]) + g_object_unref(hatches[i]); + } +}