1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk1/dcclient.cpp
3// Purpose:
4// Author:      Robert Roebling
5// RCS-ID:      $Id: dcclient.cpp 48989 2007-09-29 23:10:35Z VZ $
6// Copyright:   (c) 1998 Robert Roebling, Chris Breeze
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#ifdef __VMS
14#define XCopyPlane XCOPYPLANE
15#endif
16
17#include "wx/dcclient.h"
18
19#ifndef WX_PRECOMP
20    #include "wx/log.h"
21    #include "wx/dcmemory.h"
22    #include "wx/math.h" // for floating-point functions
23    #include "wx/image.h"
24    #include "wx/module.h"
25#endif
26
27#include "wx/fontutil.h"
28
29#include "wx/gtk1/win_gtk.h"
30
31#include <gdk/gdk.h>
32#include <gdk/gdkx.h>
33#include <gdk/gdkprivate.h>
34#include <gtk/gtk.h>
35
36//-----------------------------------------------------------------------------
37// local defines
38//-----------------------------------------------------------------------------
39
40#define USE_PAINT_REGION 1
41
42//-----------------------------------------------------------------------------
43// local data
44//-----------------------------------------------------------------------------
45
46#include "bdiag.xbm"
47#include "fdiag.xbm"
48#include "cdiag.xbm"
49#include "horiz.xbm"
50#include "verti.xbm"
51#include "cross.xbm"
52#define  num_hatches 6
53
54#define IS_15_PIX_HATCH(s) ((s)==wxCROSSDIAG_HATCH || (s)==wxHORIZONTAL_HATCH || (s)==wxVERTICAL_HATCH)
55#define IS_16_PIX_HATCH(s) ((s)!=wxCROSSDIAG_HATCH && (s)!=wxHORIZONTAL_HATCH && (s)!=wxVERTICAL_HATCH)
56
57
58static GdkPixmap  *hatches[num_hatches];
59static GdkPixmap **hatch_bitmap = (GdkPixmap **) NULL;
60
61extern GtkWidget *wxGetRootWindow();
62
63//-----------------------------------------------------------------------------
64// constants
65//-----------------------------------------------------------------------------
66
67const double RAD2DEG  = 180.0 / M_PI;
68
69// ----------------------------------------------------------------------------
70// private functions
71// ----------------------------------------------------------------------------
72
73static inline double dmax(double a, double b) { return a > b ? a : b; }
74static inline double dmin(double a, double b) { return a < b ? a : b; }
75
76static inline double DegToRad(double deg) { return (deg * M_PI) / 180.0; }
77
78//-----------------------------------------------------------------------------
79// temporary implementation of the missing GDK function
80//-----------------------------------------------------------------------------
81
82#include "gdk/gdkprivate.h"
83
84void gdk_wx_draw_bitmap(GdkDrawable  *drawable,
85                        GdkGC        *gc,
86                        GdkDrawable  *src,
87                        gint         xsrc,
88                        gint         ysrc,
89                        gint         xdest,
90                        gint         ydest,
91                        gint         width,
92                        gint         height)
93{
94    wxCHECK_RET( drawable, _T("NULL drawable in gdk_wx_draw_bitmap") );
95    wxCHECK_RET( src, _T("NULL src in gdk_wx_draw_bitmap") );
96    wxCHECK_RET( gc, _T("NULL gc in gdk_wx_draw_bitmap") );
97
98    GdkWindowPrivate *drawable_private;
99    GdkWindowPrivate *src_private;
100    GdkGCPrivate *gc_private;
101
102    drawable_private = (GdkWindowPrivate*) drawable;
103    src_private = (GdkWindowPrivate*) src;
104    if (drawable_private->destroyed || src_private->destroyed)
105        return;
106
107    gint src_width = src_private->width;
108    gint src_height = src_private->height;
109
110    gc_private = (GdkGCPrivate*) gc;
111
112    if (width == -1) width = src_width;
113    if (height == -1) height = src_height;
114
115    XCopyPlane( drawable_private->xdisplay,
116                src_private->xwindow,
117                drawable_private->xwindow,
118                gc_private->xgc,
119                xsrc, ysrc,
120                width, height,
121                xdest, ydest,
122                1 );
123}
124
125//-----------------------------------------------------------------------------
126// Implement Pool of Graphic contexts. Creating them takes too much time.
127//-----------------------------------------------------------------------------
128
129enum wxPoolGCType
130{
131   wxGC_ERROR = 0,
132   wxTEXT_MONO,
133   wxBG_MONO,
134   wxPEN_MONO,
135   wxBRUSH_MONO,
136   wxTEXT_COLOUR,
137   wxBG_COLOUR,
138   wxPEN_COLOUR,
139   wxBRUSH_COLOUR,
140   wxTEXT_SCREEN,
141   wxBG_SCREEN,
142   wxPEN_SCREEN,
143   wxBRUSH_SCREEN
144};
145
146struct wxGC
147{
148    GdkGC        *m_gc;
149    wxPoolGCType  m_type;
150    bool          m_used;
151};
152
153#define GC_POOL_ALLOC_SIZE 100
154
155static int wxGCPoolSize = 0;
156
157static wxGC *wxGCPool = NULL;
158
159static void wxInitGCPool()
160{
161    // This really could wait until the first call to
162    // wxGetPoolGC, but we will make the first allocation
163    // now when other initialization is being performed.
164
165    // Set initial pool size.
166    wxGCPoolSize = GC_POOL_ALLOC_SIZE;
167
168    // Allocate initial pool.
169    wxGCPool = (wxGC *)malloc(wxGCPoolSize * sizeof(wxGC));
170    if (wxGCPool == NULL)
171    {
172        // If we cannot malloc, then fail with error
173        // when debug is enabled.  If debug is not enabled,
174        // the problem will eventually get caught
175        // in wxGetPoolGC.
176        wxFAIL_MSG( wxT("Cannot allocate GC pool") );
177        return;
178    }
179
180    // Zero initial pool.
181    memset(wxGCPool, 0, wxGCPoolSize * sizeof(wxGC));
182}
183
184static void wxCleanUpGCPool()
185{
186    for (int i = 0; i < wxGCPoolSize; i++)
187    {
188        if (wxGCPool[i].m_gc)
189            gdk_gc_unref( wxGCPool[i].m_gc );
190    }
191
192    free(wxGCPool);
193    wxGCPool = NULL;
194    wxGCPoolSize = 0;
195}
196
197static GdkGC* wxGetPoolGC( GdkWindow *window, wxPoolGCType type )
198{
199    wxGC *pptr;
200
201    // Look for an available GC.
202    for (int i = 0; i < wxGCPoolSize; i++)
203    {
204        if (!wxGCPool[i].m_gc)
205        {
206            wxGCPool[i].m_gc = gdk_gc_new( window );
207            gdk_gc_set_exposures( wxGCPool[i].m_gc, FALSE );
208            wxGCPool[i].m_type = type;
209            wxGCPool[i].m_used = false;
210        }
211        if ((!wxGCPool[i].m_used) && (wxGCPool[i].m_type == type))
212        {
213            wxGCPool[i].m_used = true;
214            return wxGCPool[i].m_gc;
215        }
216    }
217
218    // We did not find an available GC.
219    // We need to grow the GC pool.
220    pptr = (wxGC *)realloc(wxGCPool,
221        (wxGCPoolSize + GC_POOL_ALLOC_SIZE)*sizeof(wxGC));
222    if (pptr != NULL)
223    {
224        // Initialize newly allocated pool.
225        wxGCPool = pptr;
226        memset(&wxGCPool[wxGCPoolSize], 0,
227            GC_POOL_ALLOC_SIZE*sizeof(wxGC));
228
229        // Initialize entry we will return.
230        wxGCPool[wxGCPoolSize].m_gc = gdk_gc_new( window );
231        gdk_gc_set_exposures( wxGCPool[wxGCPoolSize].m_gc, FALSE );
232        wxGCPool[wxGCPoolSize].m_type = type;
233        wxGCPool[wxGCPoolSize].m_used = true;
234
235        // Set new value of pool size.
236        wxGCPoolSize += GC_POOL_ALLOC_SIZE;
237
238        // Return newly allocated entry.
239        return wxGCPool[wxGCPoolSize-GC_POOL_ALLOC_SIZE].m_gc;
240    }
241
242    // The realloc failed.  Fall through to error.
243    wxFAIL_MSG( wxT("No GC available") );
244
245    return (GdkGC*) NULL;
246}
247
248static void wxFreePoolGC( GdkGC *gc )
249{
250    for (int i = 0; i < wxGCPoolSize; i++)
251    {
252        if (wxGCPool[i].m_gc == gc)
253        {
254            wxGCPool[i].m_used = false;
255            return;
256        }
257    }
258
259    wxFAIL_MSG( wxT("Wrong GC") );
260}
261
262//-----------------------------------------------------------------------------
263// wxWindowDC
264//-----------------------------------------------------------------------------
265
266IMPLEMENT_DYNAMIC_CLASS(wxWindowDC, wxDC)
267
268wxWindowDC::wxWindowDC()
269{
270    m_penGC = (GdkGC *) NULL;
271    m_brushGC = (GdkGC *) NULL;
272    m_textGC = (GdkGC *) NULL;
273    m_bgGC = (GdkGC *) NULL;
274    m_cmap = (GdkColormap *) NULL;
275    m_isMemDC = false;
276    m_isScreenDC = false;
277    m_owner = (wxWindow *)NULL;
278}
279
280wxWindowDC::wxWindowDC( wxWindow *window )
281{
282    wxASSERT_MSG( window, wxT("DC needs a window") );
283
284    m_penGC = (GdkGC *) NULL;
285    m_brushGC = (GdkGC *) NULL;
286    m_textGC = (GdkGC *) NULL;
287    m_bgGC = (GdkGC *) NULL;
288    m_cmap = (GdkColormap *) NULL;
289    m_owner = (wxWindow *)NULL;
290    m_isMemDC = false;
291    m_isScreenDC = false;
292    m_font = window->GetFont();
293
294    GtkWidget *widget = window->m_wxwindow;
295
296    // Some controls don't have m_wxwindow - like wxStaticBox, but the user
297    // code should still be able to create wxClientDCs for them, so we will
298    // use the parent window here then.
299    if ( !widget )
300    {
301        window = window->GetParent();
302        widget = window->m_wxwindow;
303    }
304
305    wxASSERT_MSG( widget, wxT("DC needs a widget") );
306
307    GtkPizza *pizza = GTK_PIZZA( widget );
308    m_window = pizza->bin_window;
309
310    // Window not realized ?
311    if (!m_window)
312    {
313         // Don't report problems as per MSW.
314         m_ok = true;
315
316         return;
317    }
318
319    m_cmap = gtk_widget_get_colormap( widget ? widget : window->m_widget );
320
321    SetUpDC();
322
323    /* this must be done after SetUpDC, bacause SetUpDC calls the
324       repective SetBrush, SetPen, SetBackground etc functions
325       to set up the DC. SetBackground call m_owner->SetBackground
326       and this might not be desired as the standard dc background
327       is white whereas a window might assume gray to be the
328       standard (as e.g. wxStatusBar) */
329
330    m_owner = window;
331}
332
333wxWindowDC::~wxWindowDC()
334{
335    Destroy();
336}
337
338void wxWindowDC::SetUpDC()
339{
340    m_ok = true;
341
342    wxASSERT_MSG( !m_penGC, wxT("GCs already created") );
343
344    if (m_isScreenDC)
345    {
346        m_penGC = wxGetPoolGC( m_window, wxPEN_SCREEN );
347        m_brushGC = wxGetPoolGC( m_window, wxBRUSH_SCREEN );
348        m_textGC = wxGetPoolGC( m_window, wxTEXT_SCREEN );
349        m_bgGC = wxGetPoolGC( m_window, wxBG_SCREEN );
350    }
351    else
352    if (m_isMemDC && (((wxMemoryDC*)this)->m_selected.GetDepth() == 1))
353    {
354        m_penGC = wxGetPoolGC( m_window, wxPEN_MONO );
355        m_brushGC = wxGetPoolGC( m_window, wxBRUSH_MONO );
356        m_textGC = wxGetPoolGC( m_window, wxTEXT_MONO );
357        m_bgGC = wxGetPoolGC( m_window, wxBG_MONO );
358    }
359    else
360    {
361        m_penGC = wxGetPoolGC( m_window, wxPEN_COLOUR );
362        m_brushGC = wxGetPoolGC( m_window, wxBRUSH_COLOUR );
363        m_textGC = wxGetPoolGC( m_window, wxTEXT_COLOUR );
364        m_bgGC = wxGetPoolGC( m_window, wxBG_COLOUR );
365    }
366
367    /* background colour */
368    m_backgroundBrush = *wxWHITE_BRUSH;
369    m_backgroundBrush.GetColour().CalcPixel( m_cmap );
370    GdkColor *bg_col = m_backgroundBrush.GetColour().GetColor();
371
372    /* m_textGC */
373    m_textForegroundColour.CalcPixel( m_cmap );
374    gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
375
376    m_textBackgroundColour.CalcPixel( m_cmap );
377    gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() );
378
379    gdk_gc_set_fill( m_textGC, GDK_SOLID );
380
381    /* m_penGC */
382    m_pen.GetColour().CalcPixel( m_cmap );
383    gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() );
384    gdk_gc_set_background( m_penGC, bg_col );
385
386    gdk_gc_set_line_attributes( m_penGC, 0, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_ROUND );
387
388    /* m_brushGC */
389    m_brush.GetColour().CalcPixel( m_cmap );
390    gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() );
391    gdk_gc_set_background( m_brushGC, bg_col );
392
393    gdk_gc_set_fill( m_brushGC, GDK_SOLID );
394
395    /* m_bgGC */
396    gdk_gc_set_background( m_bgGC, bg_col );
397    gdk_gc_set_foreground( m_bgGC, bg_col );
398
399    gdk_gc_set_fill( m_bgGC, GDK_SOLID );
400
401    /* ROPs */
402    gdk_gc_set_function( m_textGC, GDK_COPY );
403    gdk_gc_set_function( m_brushGC, GDK_COPY );
404    gdk_gc_set_function( m_penGC, GDK_COPY );
405
406    /* clipping */
407    gdk_gc_set_clip_rectangle( m_penGC, (GdkRectangle *) NULL );
408    gdk_gc_set_clip_rectangle( m_brushGC, (GdkRectangle *) NULL );
409    gdk_gc_set_clip_rectangle( m_textGC, (GdkRectangle *) NULL );
410    gdk_gc_set_clip_rectangle( m_bgGC, (GdkRectangle *) NULL );
411
412    if (!hatch_bitmap)
413    {
414        hatch_bitmap    = hatches;
415        hatch_bitmap[0] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, bdiag_bits, bdiag_width, bdiag_height );
416        hatch_bitmap[1] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, cdiag_bits, cdiag_width, cdiag_height );
417        hatch_bitmap[2] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, fdiag_bits, fdiag_width, fdiag_height );
418        hatch_bitmap[3] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, cross_bits, cross_width, cross_height );
419        hatch_bitmap[4] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, horiz_bits, horiz_width, horiz_height );
420        hatch_bitmap[5] = gdk_bitmap_create_from_data( (GdkWindow *) NULL, verti_bits, verti_width, verti_height );
421    }
422}
423
424void wxWindowDC::DoGetSize( int* width, int* height ) const
425{
426    wxCHECK_RET( m_owner, _T("GetSize() doesn't work without window") );
427
428    m_owner->GetSize(width, height);
429}
430
431extern bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y,
432                          const wxColour & col, int style);
433
434bool wxWindowDC::DoFloodFill(wxCoord x, wxCoord y,
435                             const wxColour& col, int style)
436{
437    return wxDoFloodFill(this, x, y, col, style);
438}
439
440bool wxWindowDC::DoGetPixel( wxCoord x1, wxCoord y1, wxColour *col ) const
441{
442    // Generic (and therefore rather inefficient) method.
443    // Could be improved.
444    wxMemoryDC memdc;
445    wxBitmap bitmap(1, 1);
446    memdc.SelectObject(bitmap);
447    memdc.Blit(0, 0, 1, 1, (wxDC*) this, x1, y1);
448    memdc.SelectObject(wxNullBitmap);
449
450    wxImage image = bitmap.ConvertToImage();
451    col->Set(image.GetRed(0, 0), image.GetGreen(0, 0), image.GetBlue(0, 0));
452    return true;
453}
454
455void wxWindowDC::DoDrawLine( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2 )
456{
457    wxCHECK_RET( Ok(), wxT("invalid window dc") );
458
459    if (m_pen.GetStyle() != wxTRANSPARENT)
460    {
461        if (m_window)
462            gdk_draw_line( m_window, m_penGC, XLOG2DEV(x1), YLOG2DEV(y1), XLOG2DEV(x2), YLOG2DEV(y2) );
463
464        CalcBoundingBox(x1, y1);
465        CalcBoundingBox(x2, y2);
466    }
467}
468
469void wxWindowDC::DoCrossHair( wxCoord x, wxCoord y )
470{
471    wxCHECK_RET( Ok(), wxT("invalid window dc") );
472
473    if (m_pen.GetStyle() != wxTRANSPARENT)
474    {
475        int w = 0;
476        int h = 0;
477        GetSize( &w, &h );
478        wxCoord xx = XLOG2DEV(x);
479        wxCoord yy = YLOG2DEV(y);
480        if (m_window)
481        {
482            gdk_draw_line( m_window, m_penGC, 0, yy, XLOG2DEVREL(w), yy );
483            gdk_draw_line( m_window, m_penGC, xx, 0, xx, YLOG2DEVREL(h) );
484        }
485    }
486}
487
488void wxWindowDC::DoDrawArc( wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2,
489                            wxCoord xc, wxCoord yc )
490{
491    wxCHECK_RET( Ok(), wxT("invalid window dc") );
492
493    wxCoord xx1 = XLOG2DEV(x1);
494    wxCoord yy1 = YLOG2DEV(y1);
495    wxCoord xx2 = XLOG2DEV(x2);
496    wxCoord yy2 = YLOG2DEV(y2);
497    wxCoord xxc = XLOG2DEV(xc);
498    wxCoord yyc = YLOG2DEV(yc);
499    double dx = xx1 - xxc;
500    double dy = yy1 - yyc;
501    double radius = sqrt((double)(dx*dx+dy*dy));
502    wxCoord   r      = (wxCoord)radius;
503    double radius1, radius2;
504
505    if (xx1 == xx2 && yy1 == yy2)
506    {
507        radius1 = 0.0;
508        radius2 = 360.0;
509    }
510    else if ( wxIsNullDouble(radius) )
511    {
512        radius1 =
513        radius2 = 0.0;
514    }
515    else
516    {
517        radius1 = (xx1 - xxc == 0) ?
518            (yy1 - yyc < 0) ? 90.0 : -90.0 :
519            -atan2(double(yy1-yyc), double(xx1-xxc)) * RAD2DEG;
520        radius2 = (xx2 - xxc == 0) ?
521            (yy2 - yyc < 0) ? 90.0 : -90.0 :
522            -atan2(double(yy2-yyc), double(xx2-xxc)) * RAD2DEG;
523    }
524    wxCoord alpha1 = wxCoord(radius1 * 64.0);
525    wxCoord alpha2 = wxCoord((radius2 - radius1) * 64.0);
526    while (alpha2 <= 0) alpha2 += 360*64;
527    while (alpha1 > 360*64) alpha1 -= 360*64;
528
529    if (m_window)
530    {
531        if (m_brush.GetStyle() != wxTRANSPARENT)
532        {
533            if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
534            {
535                gdk_gc_set_ts_origin( m_textGC,
536                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
537                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
538                gdk_draw_arc( m_window, m_textGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
539                gdk_gc_set_ts_origin( m_textGC, 0, 0 );
540            } else
541            if (IS_15_PIX_HATCH(m_brush.GetStyle()))
542            {
543                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
544                gdk_draw_arc( m_window, m_brushGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
545                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
546            } else
547            if (IS_16_PIX_HATCH(m_brush.GetStyle()))
548            {
549                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
550                gdk_draw_arc( m_window, m_brushGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
551                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
552            } else
553            if (m_brush.GetStyle() == wxSTIPPLE)
554            {
555                gdk_gc_set_ts_origin( m_brushGC,
556                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
557                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
558                gdk_draw_arc( m_window, m_brushGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
559                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
560            }
561            else
562            {
563                gdk_draw_arc( m_window, m_brushGC, TRUE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
564            }
565        }
566
567        if (m_pen.GetStyle() != wxTRANSPARENT)
568        {
569            gdk_draw_arc( m_window, m_penGC, FALSE, xxc-r, yyc-r, 2*r,2*r, alpha1, alpha2 );
570
571            gdk_draw_line( m_window, m_penGC, xx1, yy1, xxc, yyc );
572            gdk_draw_line( m_window, m_penGC, xxc, yyc, xx2, yy2 );
573        }
574    }
575
576    CalcBoundingBox (x1, y1);
577    CalcBoundingBox (x2, y2);
578}
579
580void wxWindowDC::DoDrawEllipticArc( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double sa, double ea )
581{
582    wxCHECK_RET( Ok(), wxT("invalid window dc") );
583
584    wxCoord xx = XLOG2DEV(x);
585    wxCoord yy = YLOG2DEV(y);
586    wxCoord ww = m_signX * XLOG2DEVREL(width);
587    wxCoord hh = m_signY * YLOG2DEVREL(height);
588
589    // CMB: handle -ve width and/or height
590    if (ww < 0) { ww = -ww; xx = xx - ww; }
591    if (hh < 0) { hh = -hh; yy = yy - hh; }
592
593    if (m_window)
594    {
595        wxCoord start = wxCoord(sa * 64.0);
596        wxCoord end = wxCoord((ea-sa) * 64.0);
597
598        if (m_brush.GetStyle() != wxTRANSPARENT)
599        {
600            if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
601            {
602                gdk_gc_set_ts_origin( m_textGC,
603                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
604                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
605                gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy, ww, hh, start, end );
606                gdk_gc_set_ts_origin( m_textGC, 0, 0 );
607            } else
608            if (IS_15_PIX_HATCH(m_brush.GetStyle()))
609            {
610                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
611                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
612                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
613            } else
614            if (IS_16_PIX_HATCH(m_brush.GetStyle()))
615            {
616                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
617                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
618                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
619            } else
620            if (m_brush.GetStyle() == wxSTIPPLE)
621            {
622                gdk_gc_set_ts_origin( m_brushGC,
623                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
624                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
625                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
626                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
627            }
628            else
629            {
630                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, start, end );
631            }
632        }
633
634        if (m_pen.GetStyle() != wxTRANSPARENT)
635            gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, start, end );
636    }
637
638    CalcBoundingBox (x, y);
639    CalcBoundingBox (x + width, y + height);
640}
641
642void wxWindowDC::DoDrawPoint( wxCoord x, wxCoord y )
643{
644    wxCHECK_RET( Ok(), wxT("invalid window dc") );
645
646    if ((m_pen.GetStyle() != wxTRANSPARENT) && m_window)
647        gdk_draw_point( m_window, m_penGC, XLOG2DEV(x), YLOG2DEV(y) );
648
649    CalcBoundingBox (x, y);
650}
651
652void wxWindowDC::DoDrawLines( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset )
653{
654    wxCHECK_RET( Ok(), wxT("invalid window dc") );
655
656    if (m_pen.GetStyle() == wxTRANSPARENT) return;
657    if (n <= 0) return;
658
659
660    GdkPoint * const gpts = new GdkPoint[n];
661    if ( !gpts )
662    {
663        wxFAIL_MSG( wxT("Cannot allocate PolyLine") );
664        return;
665    }
666
667    for (int i = 0; i < n; i++)
668    {
669        wxCoord x = points[i].x + xoffset,
670                y = points[i].y + yoffset;
671
672        CalcBoundingBox(x + xoffset, y + yoffset);
673
674        gpts[i].x = XLOG2DEV(x);
675        gpts[i].y = YLOG2DEV(y);
676    }
677
678    gdk_draw_lines( m_window, m_penGC, gpts, n);
679
680    delete[] gpts;
681}
682
683void wxWindowDC::DoDrawPolygon( int n, wxPoint points[], wxCoord xoffset, wxCoord yoffset, int WXUNUSED(fillStyle) )
684{
685    wxCHECK_RET( Ok(), wxT("invalid window dc") );
686
687    if (n <= 0) return;
688
689    GdkPoint * const gpts = new GdkPoint[n];
690
691    for (int i = 0 ; i < n ; i++)
692    {
693        wxCoord x = points[i].x + xoffset,
694                y = points[i].y + yoffset;
695
696        CalcBoundingBox(x + xoffset, y + yoffset);
697
698        gpts[i].x = XLOG2DEV(x);
699        gpts[i].y = YLOG2DEV(y);
700    }
701
702    if (m_brush.GetStyle() == wxSOLID)
703    {
704        gdk_draw_polygon( m_window, m_brushGC, TRUE, gpts, n );
705    }
706    else if (m_brush.GetStyle() != wxTRANSPARENT)
707    {
708        if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
709        {
710            gdk_gc_set_ts_origin( m_textGC,
711                                  m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
712                                  m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
713            gdk_draw_polygon( m_window, m_textGC, TRUE, gpts, n );
714            gdk_gc_set_ts_origin( m_textGC, 0, 0 );
715        } else
716        if (IS_15_PIX_HATCH(m_brush.GetStyle()))
717        {
718            gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
719            gdk_draw_polygon( m_window, m_brushGC, TRUE, gpts, n );
720            gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
721        } else
722        if (IS_16_PIX_HATCH(m_brush.GetStyle()))
723        {
724            gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
725            gdk_draw_polygon( m_window, m_brushGC, TRUE, gpts, n );
726            gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
727        } else
728        if (m_brush.GetStyle() == wxSTIPPLE)
729        {
730            gdk_gc_set_ts_origin( m_brushGC,
731                                  m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
732                                  m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
733            gdk_draw_polygon( m_window, m_brushGC, TRUE, gpts, n );
734            gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
735        }
736        else
737        {
738            gdk_draw_polygon( m_window, m_brushGC, TRUE, gpts, n );
739        }
740    }
741
742    if (m_pen.GetStyle() != wxTRANSPARENT)
743    {
744        gdk_draw_polygon( m_window, m_penGC, FALSE, gpts, n );
745
746    }
747
748    delete[] gpts;
749}
750
751void wxWindowDC::DoDrawRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
752{
753    wxCHECK_RET( Ok(), wxT("invalid window dc") );
754
755    wxCoord xx = XLOG2DEV(x);
756    wxCoord yy = YLOG2DEV(y);
757    wxCoord ww = m_signX * XLOG2DEVREL(width);
758    wxCoord hh = m_signY * YLOG2DEVREL(height);
759
760    // CMB: draw nothing if transformed w or h is 0
761    if (ww == 0 || hh == 0) return;
762
763    // CMB: handle -ve width and/or height
764    if (ww < 0) { ww = -ww; xx = xx - ww; }
765    if (hh < 0) { hh = -hh; yy = yy - hh; }
766
767    if (m_window)
768    {
769        if (m_brush.GetStyle() != wxTRANSPARENT)
770        {
771            if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
772            {
773                gdk_gc_set_ts_origin( m_textGC,
774                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
775                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
776                gdk_draw_rectangle( m_window, m_textGC, TRUE, xx, yy, ww, hh );
777                gdk_gc_set_ts_origin( m_textGC, 0, 0 );
778            } else
779            if (IS_15_PIX_HATCH(m_brush.GetStyle()))
780            {
781                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
782                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
783                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
784            } else
785            if (IS_16_PIX_HATCH(m_brush.GetStyle()))
786            {
787                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
788                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
789                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
790            } else
791            if (m_brush.GetStyle() == wxSTIPPLE)
792            {
793                gdk_gc_set_ts_origin( m_brushGC,
794                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
795                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
796                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
797                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
798            }
799            else
800            {
801                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy, ww, hh );
802            }
803        }
804
805        if (m_pen.GetStyle() != wxTRANSPARENT)
806            gdk_draw_rectangle( m_window, m_penGC, FALSE, xx, yy, ww-1, hh-1 );
807    }
808
809    CalcBoundingBox( x, y );
810    CalcBoundingBox( x + width, y + height );
811}
812
813void wxWindowDC::DoDrawRoundedRectangle( wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius )
814{
815    wxCHECK_RET( Ok(), wxT("invalid window dc") );
816
817    if (radius < 0.0) radius = - radius * ((width < height) ? width : height);
818
819    wxCoord xx = XLOG2DEV(x);
820    wxCoord yy = YLOG2DEV(y);
821    wxCoord ww = m_signX * XLOG2DEVREL(width);
822    wxCoord hh = m_signY * YLOG2DEVREL(height);
823    wxCoord rr = XLOG2DEVREL((wxCoord)radius);
824
825    // CMB: handle -ve width and/or height
826    if (ww < 0) { ww = -ww; xx = xx - ww; }
827    if (hh < 0) { hh = -hh; yy = yy - hh; }
828
829    // CMB: if radius is zero use DrawRectangle() instead to avoid
830    // X drawing errors with small radii
831    if (rr == 0)
832    {
833        DrawRectangle( x, y, width, height );
834        return;
835    }
836
837    // CMB: draw nothing if transformed w or h is 0
838    if (ww == 0 || hh == 0) return;
839
840    // CMB: adjust size if outline is drawn otherwise the result is
841    // 1 pixel too wide and high
842    if (m_pen.GetStyle() != wxTRANSPARENT)
843    {
844        ww--;
845        hh--;
846    }
847
848    if (m_window)
849    {
850        // CMB: ensure dd is not larger than rectangle otherwise we
851        // get an hour glass shape
852        wxCoord dd = 2 * rr;
853        if (dd > ww) dd = ww;
854        if (dd > hh) dd = hh;
855        rr = dd / 2;
856
857        if (m_brush.GetStyle() != wxTRANSPARENT)
858        {
859            if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
860            {
861                gdk_gc_set_ts_origin( m_textGC,
862                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
863                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
864                gdk_draw_rectangle( m_window, m_textGC, TRUE, xx+rr, yy, ww-dd+1, hh );
865                gdk_draw_rectangle( m_window, m_textGC, TRUE, xx, yy+rr, ww, hh-dd+1 );
866                gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 );
867                gdk_draw_arc( m_window, m_textGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
868                gdk_draw_arc( m_window, m_textGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
869                gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
870                gdk_gc_set_ts_origin( m_textGC, 0, 0 );
871            } else
872            if (IS_15_PIX_HATCH(m_brush.GetStyle()))
873            {
874                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
875                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx+rr, yy, ww-dd+1, hh );
876                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy+rr, ww, hh-dd+1 );
877                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 );
878                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
879                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
880                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
881                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
882            } else
883            if (IS_16_PIX_HATCH(m_brush.GetStyle()))
884            {
885                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
886                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx+rr, yy, ww-dd+1, hh );
887                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy+rr, ww, hh-dd+1 );
888                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 );
889                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
890                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
891                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
892                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
893            } else
894            if (m_brush.GetStyle() == wxSTIPPLE)
895            {
896                gdk_gc_set_ts_origin( m_brushGC,
897                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
898                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
899                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx+rr, yy, ww-dd+1, hh );
900                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy+rr, ww, hh-dd+1 );
901                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 );
902                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
903                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
904                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
905                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
906            }
907            else
908            {
909                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx+rr, yy, ww-dd+1, hh );
910                gdk_draw_rectangle( m_window, m_brushGC, TRUE, xx, yy+rr, ww, hh-dd+1 );
911                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, dd, dd, 90*64, 90*64 );
912                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
913                gdk_draw_arc( m_window, m_brushGC, TRUE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
914                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
915            }
916        }
917
918        if (m_pen.GetStyle() != wxTRANSPARENT)
919        {
920            gdk_draw_line( m_window, m_penGC, xx+rr+1, yy, xx+ww-rr, yy );
921            gdk_draw_line( m_window, m_penGC, xx+rr+1, yy+hh, xx+ww-rr, yy+hh );
922            gdk_draw_line( m_window, m_penGC, xx, yy+rr+1, xx, yy+hh-rr );
923            gdk_draw_line( m_window, m_penGC, xx+ww, yy+rr+1, xx+ww, yy+hh-rr );
924            gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, dd, dd, 90*64, 90*64 );
925            gdk_draw_arc( m_window, m_penGC, FALSE, xx+ww-dd, yy, dd, dd, 0, 90*64 );
926            gdk_draw_arc( m_window, m_penGC, FALSE, xx+ww-dd, yy+hh-dd, dd, dd, 270*64, 90*64 );
927            gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy+hh-dd, dd, dd, 180*64, 90*64 );
928        }
929    }
930
931    // this ignores the radius
932    CalcBoundingBox( x, y );
933    CalcBoundingBox( x + width, y + height );
934}
935
936void wxWindowDC::DoDrawEllipse( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
937{
938    wxCHECK_RET( Ok(), wxT("invalid window dc") );
939
940    wxCoord xx = XLOG2DEV(x);
941    wxCoord yy = YLOG2DEV(y);
942    wxCoord ww = m_signX * XLOG2DEVREL(width);
943    wxCoord hh = m_signY * YLOG2DEVREL(height);
944
945    // CMB: handle -ve width and/or height
946    if (ww < 0) { ww = -ww; xx = xx - ww; }
947    if (hh < 0) { hh = -hh; yy = yy - hh; }
948
949    if (m_window)
950    {
951        if (m_brush.GetStyle() != wxTRANSPARENT)
952        {
953            if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
954            {
955                gdk_gc_set_ts_origin( m_textGC,
956                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
957                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
958                gdk_draw_arc( m_window, m_textGC, TRUE, xx, yy, ww, hh, 0, 360*64 );
959                gdk_gc_set_ts_origin( m_textGC, 0, 0 );
960            } else
961            if (IS_15_PIX_HATCH(m_brush.GetStyle()))
962            {
963                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 15, m_deviceOriginY % 15 );
964                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 );
965                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
966            } else
967            if (IS_16_PIX_HATCH(m_brush.GetStyle()))
968            {
969                gdk_gc_set_ts_origin( m_brushGC, m_deviceOriginX % 16, m_deviceOriginY % 16 );
970                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 );
971                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
972            } else
973            if (m_brush.GetStyle() == wxSTIPPLE)
974            {
975                gdk_gc_set_ts_origin( m_brushGC,
976                                      m_deviceOriginX % m_brush.GetStipple()->GetWidth(),
977                                      m_deviceOriginY % m_brush.GetStipple()->GetHeight() );
978                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 );
979                gdk_gc_set_ts_origin( m_brushGC, 0, 0 );
980            }
981            else
982            {
983                gdk_draw_arc( m_window, m_brushGC, TRUE, xx, yy, ww, hh, 0, 360*64 );
984            }
985        }
986
987        if (m_pen.GetStyle() != wxTRANSPARENT)
988            gdk_draw_arc( m_window, m_penGC, FALSE, xx, yy, ww, hh, 0, 360*64 );
989    }
990
991    CalcBoundingBox( x, y );
992    CalcBoundingBox( x + width, y + height );
993}
994
995void wxWindowDC::DoDrawIcon( const wxIcon &icon, wxCoord x, wxCoord y )
996{
997    // VZ: egcs 1.0.3 refuses to compile this without cast, no idea why
998    DoDrawBitmap( (const wxBitmap&)icon, x, y, true );
999}
1000
1001void wxWindowDC::DoDrawBitmap( const wxBitmap &bitmap,
1002                               wxCoord x, wxCoord y,
1003                               bool useMask )
1004{
1005    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1006
1007    wxCHECK_RET( bitmap.Ok(), wxT("invalid bitmap") );
1008
1009    bool is_mono = (bitmap.GetBitmap() != NULL);
1010
1011    // scale/translate size and position
1012    int xx = XLOG2DEV(x);
1013    int yy = YLOG2DEV(y);
1014
1015    int w = bitmap.GetWidth();
1016    int h = bitmap.GetHeight();
1017
1018    CalcBoundingBox( x, y );
1019    CalcBoundingBox( x + w, y + h );
1020
1021    if (!m_window) return;
1022
1023    int ww = XLOG2DEVREL(w);
1024    int hh = YLOG2DEVREL(h);
1025
1026    // compare to current clipping region
1027    if (!m_currentClippingRegion.IsNull())
1028    {
1029        wxRegion tmp( xx,yy,ww,hh );
1030        tmp.Intersect( m_currentClippingRegion );
1031        if (tmp.IsEmpty())
1032            return;
1033    }
1034
1035    // scale bitmap if required
1036    wxBitmap use_bitmap = bitmap;
1037    if ((w != ww) || (h != hh))
1038        use_bitmap = use_bitmap.Rescale( 0, 0, ww, hh, ww, hh );
1039
1040    // NB: We can't render pixbufs with GTK+ < 2.2, we need to use pixmaps code.
1041    //     Pixbufs-based bitmaps with alpha channel don't have a mask, so we
1042    //     have to call GetPixmap() here -- it converts the pixbuf into pixmap
1043    //     and also creates the mask as a side-effect:
1044    use_bitmap.GetPixmap();
1045
1046    // apply mask if any
1047    GdkBitmap *mask = (GdkBitmap *) NULL;
1048    if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap();
1049
1050    GdkBitmap *new_mask = (GdkBitmap*) NULL;
1051
1052    if (useMask && mask)
1053    {
1054        if (!m_currentClippingRegion.IsNull())
1055        {
1056            GdkColor col;
1057            new_mask = gdk_pixmap_new( wxGetRootWindow()->window, ww, hh, 1 );
1058            GdkGC *gc = gdk_gc_new( new_mask );
1059            col.pixel = 0;
1060            gdk_gc_set_foreground( gc, &col );
1061            gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, ww, hh );
1062            col.pixel = 0;
1063            gdk_gc_set_background( gc, &col );
1064            col.pixel = 1;
1065            gdk_gc_set_foreground( gc, &col );
1066            gdk_gc_set_clip_region( gc, m_currentClippingRegion.GetRegion() );
1067            gdk_gc_set_clip_origin( gc, -xx, -yy );
1068            gdk_gc_set_fill( gc, GDK_OPAQUE_STIPPLED );
1069            gdk_gc_set_stipple( gc, mask );
1070            gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, ww, hh );
1071            gdk_gc_unref( gc );
1072        }
1073
1074        if (is_mono)
1075        {
1076            if (new_mask)
1077                gdk_gc_set_clip_mask( m_textGC, new_mask );
1078            else
1079                gdk_gc_set_clip_mask( m_textGC, mask );
1080            gdk_gc_set_clip_origin( m_textGC, xx, yy );
1081        }
1082        else
1083        {
1084            if (new_mask)
1085                gdk_gc_set_clip_mask( m_penGC, new_mask );
1086            else
1087                gdk_gc_set_clip_mask( m_penGC, mask );
1088            gdk_gc_set_clip_origin( m_penGC, xx, yy );
1089        }
1090    }
1091
1092    // Draw XPixmap or XBitmap, depending on what the wxBitmap contains. For
1093    // drawing a mono-bitmap (XBitmap) we use the current text GC
1094    if (is_mono)
1095    {
1096        gdk_wx_draw_bitmap( m_window, m_textGC, use_bitmap.GetBitmap(), 0, 0, xx, yy, -1, -1 );
1097    }
1098    else
1099    {
1100        gdk_draw_pixmap(m_window, m_penGC,
1101                        use_bitmap.GetPixmap(),
1102                        0, 0, xx, yy, -1, -1);
1103    }
1104
1105    // remove mask again if any
1106    if (useMask && mask)
1107    {
1108        if (is_mono)
1109        {
1110            gdk_gc_set_clip_mask( m_textGC, (GdkBitmap *) NULL );
1111            gdk_gc_set_clip_origin( m_textGC, 0, 0 );
1112            if (!m_currentClippingRegion.IsNull())
1113                gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
1114        }
1115        else
1116        {
1117            gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL );
1118            gdk_gc_set_clip_origin( m_penGC, 0, 0 );
1119            if (!m_currentClippingRegion.IsNull())
1120                gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
1121        }
1122    }
1123
1124    if (new_mask)
1125        gdk_bitmap_unref( new_mask );
1126}
1127
1128bool wxWindowDC::DoBlit( wxCoord xdest, wxCoord ydest,
1129                         wxCoord width, wxCoord height,
1130                         wxDC *source,
1131                         wxCoord xsrc, wxCoord ysrc,
1132                         int logical_func,
1133                         bool useMask,
1134                         wxCoord xsrcMask, wxCoord ysrcMask )
1135{
1136    wxCHECK_MSG( Ok(), false, wxT("invalid window dc") );
1137
1138    wxCHECK_MSG( source, false, wxT("invalid source dc") );
1139
1140    if (!m_window) return false;
1141
1142    // transform the source DC coords to the device ones
1143    xsrc = source->XLOG2DEV(xsrc);
1144    ysrc = source->YLOG2DEV(ysrc);
1145
1146    wxClientDC *srcDC = (wxClientDC*)source;
1147    wxMemoryDC *memDC = (wxMemoryDC*)source;
1148
1149    bool use_bitmap_method = false;
1150    bool is_mono = false;
1151
1152    if (xsrcMask == -1 && ysrcMask == -1)
1153    {
1154        xsrcMask = xsrc;
1155        ysrcMask = ysrc;
1156    }
1157
1158    if (srcDC->m_isMemDC)
1159    {
1160        if (!memDC->m_selected.Ok()) return false;
1161
1162        is_mono = (memDC->m_selected.GetDepth() == 1);
1163
1164        // we use the "XCopyArea" way to copy a memory dc into
1165        // a different window if the memory dc BOTH
1166        // a) doesn't have any mask or its mask isn't used
1167        // b) it is clipped
1168        // c) is not 1-bit
1169
1170        if (useMask && (memDC->m_selected.GetMask()))
1171        {
1172            // we HAVE TO use the direct way for memory dcs
1173            // that have mask since the XCopyArea doesn't know
1174            // about masks
1175            use_bitmap_method = true;
1176        }
1177        else if (is_mono)
1178        {
1179            // we HAVE TO use the direct way for memory dcs
1180            // that are bitmaps because XCopyArea doesn't cope
1181            // with different bit depths
1182            use_bitmap_method = true;
1183        }
1184        else if ((xsrc == 0) && (ysrc == 0) &&
1185                 (width == memDC->m_selected.GetWidth()) &&
1186                 (height == memDC->m_selected.GetHeight()))
1187        {
1188            // we SHOULD use the direct way if all of the bitmap
1189            // in the memory dc is copied in which case XCopyArea
1190            // wouldn't be able able to boost performace by reducing
1191            // the area to be scaled
1192            use_bitmap_method = true;
1193        }
1194        else
1195        {
1196            use_bitmap_method = false;
1197        }
1198    }
1199
1200    CalcBoundingBox( xdest, ydest );
1201    CalcBoundingBox( xdest + width, ydest + height );
1202
1203    // scale/translate size and position
1204    wxCoord xx = XLOG2DEV(xdest);
1205    wxCoord yy = YLOG2DEV(ydest);
1206
1207    wxCoord ww = XLOG2DEVREL(width);
1208    wxCoord hh = YLOG2DEVREL(height);
1209
1210    // compare to current clipping region
1211    if (!m_currentClippingRegion.IsNull())
1212    {
1213        wxRegion tmp( xx,yy,ww,hh );
1214        tmp.Intersect( m_currentClippingRegion );
1215        if (tmp.IsEmpty())
1216            return true;
1217    }
1218
1219    int old_logical_func = m_logicalFunction;
1220    SetLogicalFunction( logical_func );
1221
1222    if (use_bitmap_method)
1223    {
1224        // scale/translate bitmap size
1225        wxCoord bm_width = memDC->m_selected.GetWidth();
1226        wxCoord bm_height = memDC->m_selected.GetHeight();
1227
1228        // Get clip coords for the bitmap. If we don't
1229        // use wxBitmap::Rescale(), which can clip the
1230        // bitmap, these are the same as the original
1231        // coordinates
1232        wxCoord cx = xx;
1233        wxCoord cy = yy;
1234        wxCoord cw = ww;
1235        wxCoord ch = hh;
1236
1237        // interpret userscale of src too
1238        double xsc,ysc;
1239        memDC->GetUserScale(&xsc,&ysc);
1240        bm_width = (int) (bm_width / xsc);
1241        bm_height = (int) (bm_height / ysc);
1242
1243        wxCoord bm_ww = XLOG2DEVREL( bm_width );
1244        wxCoord bm_hh = YLOG2DEVREL( bm_height );
1245
1246        // Scale bitmap if required
1247        wxBitmap use_bitmap;
1248        if ((memDC->m_selected.GetWidth()!= bm_ww) || ( memDC->m_selected.GetHeight()!= bm_hh))
1249        {
1250            // This indicates that the blitting code below will get
1251            // a clipped bitmap and therefore needs to move the origin
1252            // accordingly
1253            wxRegion tmp( xx,yy,ww,hh );
1254            tmp.Intersect( m_currentClippingRegion );
1255            tmp.GetBox(cx,cy,cw,ch);
1256
1257            // Scale and clipped bitmap
1258            use_bitmap = memDC->m_selected.Rescale(cx-xx,cy-yy,cw,ch,bm_ww,bm_hh);
1259        }
1260        else
1261        {
1262            // Don't scale bitmap
1263            use_bitmap = memDC->m_selected;
1264        }
1265
1266        // apply mask if any
1267        GdkBitmap *mask = (GdkBitmap *) NULL;
1268        if (use_bitmap.GetMask()) mask = use_bitmap.GetMask()->GetBitmap();
1269
1270        GdkBitmap *new_mask = (GdkBitmap*) NULL;
1271
1272        if (useMask && mask)
1273        {
1274            if (!m_currentClippingRegion.IsNull())
1275            {
1276                GdkColor col;
1277                new_mask = gdk_pixmap_new( wxGetRootWindow()->window, bm_ww, bm_hh, 1 );
1278                GdkGC *gc = gdk_gc_new( new_mask );
1279                col.pixel = 0;
1280                gdk_gc_set_foreground( gc, &col );
1281                gdk_gc_set_ts_origin( gc, -xsrcMask, -ysrcMask);
1282                gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, bm_ww, bm_hh );
1283                col.pixel = 0;
1284                gdk_gc_set_background( gc, &col );
1285                col.pixel = 1;
1286                gdk_gc_set_foreground( gc, &col );
1287                gdk_gc_set_clip_region( gc, m_currentClippingRegion.GetRegion() );
1288                // was: gdk_gc_set_clip_origin( gc, -xx, -yy );
1289                gdk_gc_set_clip_origin( gc, -cx, -cy );
1290                gdk_gc_set_fill( gc, GDK_OPAQUE_STIPPLED );
1291                gdk_gc_set_stipple( gc, mask );
1292                gdk_draw_rectangle( new_mask, gc, TRUE, 0, 0, bm_ww, bm_hh );
1293                gdk_gc_unref( gc );
1294            }
1295
1296            if (is_mono)
1297            {
1298                if (new_mask)
1299                {
1300                    gdk_gc_set_clip_mask( m_textGC, new_mask );
1301                    gdk_gc_set_clip_origin( m_textGC, cx, cy );
1302                }
1303                else
1304                {
1305                    gdk_gc_set_clip_mask( m_textGC, mask );
1306                    gdk_gc_set_clip_origin( m_textGC, cx-xsrcMask, cy-ysrcMask );
1307                }
1308            }
1309            else
1310            {
1311                if (new_mask)
1312                {
1313                    gdk_gc_set_clip_mask( m_penGC, new_mask );
1314                    gdk_gc_set_clip_origin( m_penGC, cx, cy );
1315                }
1316                else
1317                {
1318                    gdk_gc_set_clip_mask( m_penGC, mask );
1319                    gdk_gc_set_clip_origin( m_penGC, cx-xsrcMask, cy-ysrcMask );
1320                }
1321            }
1322        }
1323
1324        // Draw XPixmap or XBitmap, depending on what the wxBitmap contains. For
1325        // drawing a mono-bitmap (XBitmap) we use the current text GC
1326
1327        if (is_mono)
1328        {
1329            // was: gdk_wx_draw_bitmap( m_window, m_textGC, use_bitmap.GetBitmap(), xsrc, ysrc, xx, yy, ww, hh );
1330            gdk_wx_draw_bitmap( m_window, m_textGC, use_bitmap.GetBitmap(), xsrc, ysrc, cx, cy, cw, ch );
1331        }
1332        else
1333        {
1334            // was: gdk_draw_pixmap( m_window, m_penGC, use_bitmap.GetPixmap(), xsrc, ysrc, xx, yy, ww, hh );
1335            gdk_draw_pixmap( m_window, m_penGC, use_bitmap.GetPixmap(), xsrc, ysrc, cx, cy, cw, ch );
1336        }
1337
1338        // remove mask again if any
1339        if (useMask && mask)
1340        {
1341            if (is_mono)
1342            {
1343                gdk_gc_set_clip_mask( m_textGC, (GdkBitmap *) NULL );
1344                gdk_gc_set_clip_origin( m_textGC, 0, 0 );
1345                if (!m_currentClippingRegion.IsNull())
1346                    gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
1347            }
1348            else
1349            {
1350                gdk_gc_set_clip_mask( m_penGC, (GdkBitmap *) NULL );
1351                gdk_gc_set_clip_origin( m_penGC, 0, 0 );
1352                if (!m_currentClippingRegion.IsNull())
1353                    gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
1354            }
1355        }
1356
1357        if (new_mask)
1358            gdk_bitmap_unref( new_mask );
1359    }
1360    else // use_bitmap_method
1361    {
1362        if ((width != ww) || (height != hh))
1363        {
1364            // get clip coords
1365            wxRegion tmp( xx,yy,ww,hh );
1366            tmp.Intersect( m_currentClippingRegion );
1367            wxCoord cx,cy,cw,ch;
1368            tmp.GetBox(cx,cy,cw,ch);
1369
1370            // rescale bitmap
1371            wxBitmap bitmap = memDC->m_selected.Rescale( cx-xx, cy-yy, cw, ch, ww, hh );
1372
1373            // draw scaled bitmap
1374            // was: gdk_draw_pixmap( m_window, m_penGC, bitmap.GetPixmap(), 0, 0, xx, yy, -1, -1 );
1375            gdk_draw_pixmap( m_window, m_penGC, bitmap.GetPixmap(), 0, 0, cx, cy, -1, -1 );
1376        }
1377        else
1378        {
1379            // No scaling and not a memory dc with a mask either
1380
1381            // copy including child window contents
1382            gdk_gc_set_subwindow( m_penGC, GDK_INCLUDE_INFERIORS );
1383            gdk_window_copy_area( m_window, m_penGC, xx, yy,
1384                                  srcDC->GetWindow(),
1385                                  xsrc, ysrc, width, height );
1386            gdk_gc_set_subwindow( m_penGC, GDK_CLIP_BY_CHILDREN );
1387        }
1388    }
1389
1390    SetLogicalFunction( old_logical_func );
1391
1392    return true;
1393}
1394
1395void wxWindowDC::DoDrawText( const wxString &text, wxCoord x, wxCoord y )
1396{
1397    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1398
1399    if (!m_window) return;
1400
1401    if (text.empty()) return;
1402
1403    GdkFont *font = m_font.GetInternalFont( m_scaleY );
1404
1405    wxCHECK_RET( font, wxT("invalid font") );
1406
1407    x = XLOG2DEV(x);
1408    y = YLOG2DEV(y);
1409
1410    wxCoord width = gdk_string_width( font, text.mbc_str() );
1411    wxCoord height = font->ascent + font->descent;
1412
1413    if ( m_backgroundMode == wxSOLID )
1414    {
1415        gdk_gc_set_foreground( m_textGC, m_textBackgroundColour.GetColor() );
1416        gdk_draw_rectangle( m_window, m_textGC, TRUE, x, y, width, height );
1417        gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
1418    }
1419    gdk_draw_string( m_window, font, m_textGC, x, y + font->ascent, text.mbc_str() );
1420
1421    /* CMB 17/7/98: simple underline: ignores scaling and underlying
1422       X font's XA_UNDERLINE_POSITION and XA_UNDERLINE_THICKNESS
1423       properties (see wxXt implementation) */
1424    if (m_font.GetUnderlined())
1425    {
1426        wxCoord ul_y = y + font->ascent;
1427        if (font->descent > 0) ul_y++;
1428        gdk_draw_line( m_window, m_textGC, x, ul_y, x + width, ul_y);
1429    }
1430
1431    width = wxCoord(width / m_scaleX);
1432    height = wxCoord(height / m_scaleY);
1433    CalcBoundingBox (x + width, y + height);
1434    CalcBoundingBox (x, y);
1435}
1436
1437
1438// TODO: There is an example of rotating text with GTK2 that would probably be
1439// a better approach here:
1440//           http://www.daa.com.au/pipermail/pygtk/2003-April/005052.html
1441
1442void wxWindowDC::DoDrawRotatedText( const wxString &text, wxCoord x, wxCoord y, double angle )
1443{
1444    if ( wxIsNullDouble(angle) )
1445    {
1446        DrawText(text, x, y);
1447        return;
1448    }
1449
1450    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1451
1452    if (!m_window) return;
1453
1454    wxCoord w;
1455    wxCoord h;
1456
1457    GdkFont *font = m_font.GetInternalFont( m_scaleY );
1458
1459    wxCHECK_RET( font, wxT("invalid font") );
1460
1461    // the size of the text
1462    w = gdk_string_width( font, text.mbc_str() );
1463    h = font->ascent + font->descent;
1464
1465    // draw the string normally
1466    wxBitmap src(w, h);
1467    wxMemoryDC dc;
1468    dc.SelectObject(src);
1469    dc.SetFont(GetFont());
1470    dc.SetBackground(*wxBLACK_BRUSH);
1471    dc.SetBrush(*wxBLACK_BRUSH);
1472    dc.Clear();
1473    dc.SetTextForeground( *wxWHITE );
1474    dc.DrawText(text, 0, 0);
1475    dc.SelectObject(wxNullBitmap);
1476
1477    // Calculate the size of the rotated bounding box.
1478    double rad = DegToRad(angle);
1479    double dx = cos(rad),
1480           dy = sin(rad);
1481
1482    // the rectngle vertices are counted clockwise with the first one being at
1483    // (0, 0) (or, rather, at (x, y))
1484    double x2 = w*dx,
1485           y2 = -w*dy;      // y axis points to the bottom, hence minus
1486    double x4 = h*dy,
1487           y4 = h*dx;
1488    double x3 = x4 + x2,
1489           y3 = y4 + y2;
1490
1491    // calc max and min
1492    wxCoord maxX = (wxCoord)(dmax(x2, dmax(x3, x4)) + 0.5),
1493            maxY = (wxCoord)(dmax(y2, dmax(y3, y4)) + 0.5),
1494            minX = (wxCoord)(dmin(x2, dmin(x3, x4)) - 0.5),
1495            minY = (wxCoord)(dmin(y2, dmin(y3, y4)) - 0.5);
1496
1497
1498    wxImage image = src.ConvertToImage();
1499
1500    image.ConvertColourToAlpha( m_textForegroundColour.Red(),
1501                                m_textForegroundColour.Green(),
1502                                m_textForegroundColour.Blue() );
1503    image = image.Rotate( rad, wxPoint(0,0) );
1504
1505    int i_angle = (int) angle;
1506    i_angle = i_angle % 360;
1507    if (i_angle < 0)
1508        i_angle += 360;
1509    int xoffset = 0;
1510    if ((i_angle >= 90.0) && (i_angle < 270.0))
1511        xoffset = image.GetWidth();
1512    int yoffset = 0;
1513    if ((i_angle >= 0.0) && (i_angle < 180.0))
1514        yoffset = image.GetHeight();
1515
1516    if ((i_angle >= 0) && (i_angle < 90))
1517        yoffset -= (int)( cos(rad)*h );
1518    if ((i_angle >= 90) && (i_angle < 180))
1519        xoffset -= (int)( sin(rad)*h );
1520    if ((i_angle >= 180) && (i_angle < 270))
1521        yoffset -= (int)( cos(rad)*h );
1522    if ((i_angle >= 270) && (i_angle < 360))
1523        xoffset -= (int)( sin(rad)*h );
1524
1525    int i_x = x - xoffset;
1526    int i_y = y - yoffset;
1527
1528    src = image;
1529    DoDrawBitmap( src, i_x, i_y, true );
1530
1531
1532    // it would be better to draw with non underlined font and draw the line
1533    // manually here (it would be more straight...)
1534#if 0
1535    if ( m_font.GetUnderlined() )
1536    {
1537        gdk_draw_line( m_window, m_textGC,
1538                       XLOG2DEV(x + x4), YLOG2DEV(y + y4 + font->descent),
1539                       XLOG2DEV(x + x3), YLOG2DEV(y + y3 + font->descent));
1540    }
1541#endif // 0
1542
1543    // update the bounding box
1544    CalcBoundingBox(x + minX, y + minY);
1545    CalcBoundingBox(x + maxX, y + maxY);
1546}
1547
1548void wxWindowDC::DoGetTextExtent(const wxString &string,
1549                                 wxCoord *width, wxCoord *height,
1550                                 wxCoord *descent, wxCoord *externalLeading,
1551                                 wxFont *theFont) const
1552{
1553    if ( width )
1554        *width = 0;
1555    if ( height )
1556        *height = 0;
1557    if ( descent )
1558        *descent = 0;
1559    if ( externalLeading )
1560        *externalLeading = 0;
1561
1562    if (string.empty())
1563    {
1564        return;
1565    }
1566
1567    wxFont fontToUse = m_font;
1568    if (theFont)
1569        fontToUse = *theFont;
1570
1571    GdkFont *font = fontToUse.GetInternalFont( m_scaleY );
1572    if ( !font )
1573        return;
1574
1575    if (width)
1576        *width = wxCoord(gdk_string_width( font, string.mbc_str() ) / m_scaleX);
1577    if (height)
1578        *height = wxCoord((font->ascent + font->descent) / m_scaleY);
1579    if (descent)
1580        *descent = wxCoord(font->descent / m_scaleY);
1581}
1582
1583wxCoord wxWindowDC::GetCharWidth() const
1584{
1585    GdkFont *font = m_font.GetInternalFont( m_scaleY );
1586    wxCHECK_MSG( font, -1, wxT("invalid font") );
1587
1588    return wxCoord(gdk_string_width( font, "H" ) / m_scaleX);
1589}
1590
1591wxCoord wxWindowDC::GetCharHeight() const
1592{
1593    GdkFont *font = m_font.GetInternalFont( m_scaleY );
1594    wxCHECK_MSG( font, -1, wxT("invalid font") );
1595
1596    return wxCoord((font->ascent + font->descent) / m_scaleY);
1597}
1598
1599void wxWindowDC::Clear()
1600{
1601    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1602
1603    if (!m_window) return;
1604
1605    // VZ: the code below results in infinite recursion and crashes when
1606    //     dc.Clear() is done from OnPaint() so I disable it for now.
1607    //     I don't know what the correct fix is but Clear() surely should not
1608    //     reenter OnPaint()!
1609#if 0
1610    /* - we either are a memory dc or have a window as the
1611       owner. anything else shouldn't happen.
1612       - we don't use gdk_window_clear() as we don't set
1613       the window's background colour anymore. it is too
1614       much pain to keep the DC's and the window's back-
1615       ground colour in synch. */
1616
1617    if (m_owner)
1618    {
1619        m_owner->Clear();
1620        return;
1621    }
1622
1623    if (m_isMemDC)
1624    {
1625        int width,height;
1626        GetSize( &width, &height );
1627        gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height );
1628        return;
1629    }
1630#else // 1
1631    int width,height;
1632    GetSize( &width, &height );
1633    gdk_draw_rectangle( m_window, m_bgGC, TRUE, 0, 0, width, height );
1634#endif // 0/1
1635}
1636
1637void wxWindowDC::SetFont( const wxFont &font )
1638{
1639    m_font = font;
1640}
1641
1642void wxWindowDC::SetPen( const wxPen &pen )
1643{
1644    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1645
1646    if (m_pen == pen) return;
1647
1648    m_pen = pen;
1649
1650    if (!m_pen.Ok()) return;
1651
1652    if (!m_window) return;
1653
1654    gint width = m_pen.GetWidth();
1655    if (width <= 0)
1656    {
1657        // CMB: if width is non-zero scale it with the dc
1658        width = 1;
1659    }
1660    else
1661    {
1662        // X doesn't allow different width in x and y and so we take
1663        // the average
1664        double w = 0.5 +
1665                   ( fabs((double) XLOG2DEVREL(width)) +
1666                     fabs((double) YLOG2DEVREL(width)) ) / 2.0;
1667        width = (int)w;
1668        if ( !width )
1669        {
1670            // width can't be 0 or an internal GTK error occurs inside
1671            // gdk_gc_set_dashes() below
1672            width = 1;
1673        }
1674    }
1675
1676    static const wxGTKDash dotted[] = {1, 1};
1677    static const wxGTKDash short_dashed[] = {2, 2};
1678    static const wxGTKDash wxCoord_dashed[] = {2, 4};
1679    static const wxGTKDash dotted_dashed[] = {3, 3, 1, 3};
1680
1681    // We express dash pattern in pen width unit, so we are
1682    // independent of zoom factor and so on...
1683    int req_nb_dash;
1684    const wxGTKDash *req_dash;
1685
1686    GdkLineStyle lineStyle = GDK_LINE_SOLID;
1687    switch (m_pen.GetStyle())
1688    {
1689        case wxUSER_DASH:
1690        {
1691            lineStyle = GDK_LINE_ON_OFF_DASH;
1692            req_nb_dash = m_pen.GetDashCount();
1693            req_dash = (wxGTKDash*)m_pen.GetDash();
1694            break;
1695        }
1696        case wxDOT:
1697        {
1698            lineStyle = GDK_LINE_ON_OFF_DASH;
1699            req_nb_dash = 2;
1700            req_dash = dotted;
1701            break;
1702        }
1703        case wxLONG_DASH:
1704        {
1705            lineStyle = GDK_LINE_ON_OFF_DASH;
1706            req_nb_dash = 2;
1707            req_dash = wxCoord_dashed;
1708            break;
1709        }
1710        case wxSHORT_DASH:
1711        {
1712            lineStyle = GDK_LINE_ON_OFF_DASH;
1713            req_nb_dash = 2;
1714            req_dash = short_dashed;
1715            break;
1716        }
1717        case wxDOT_DASH:
1718        {
1719//            lineStyle = GDK_LINE_DOUBLE_DASH;
1720            lineStyle = GDK_LINE_ON_OFF_DASH;
1721            req_nb_dash = 4;
1722            req_dash = dotted_dashed;
1723            break;
1724        }
1725
1726        case wxTRANSPARENT:
1727        case wxSTIPPLE_MASK_OPAQUE:
1728        case wxSTIPPLE:
1729        case wxSOLID:
1730        default:
1731        {
1732            lineStyle = GDK_LINE_SOLID;
1733            req_dash = (wxGTKDash*)NULL;
1734            req_nb_dash = 0;
1735            break;
1736        }
1737    }
1738
1739    if (req_dash && req_nb_dash)
1740    {
1741        wxGTKDash *real_req_dash = new wxGTKDash[req_nb_dash];
1742        if (real_req_dash)
1743        {
1744            for (int i = 0; i < req_nb_dash; i++)
1745                real_req_dash[i] = req_dash[i] * width;
1746            gdk_gc_set_dashes( m_penGC, 0, real_req_dash, req_nb_dash );
1747            delete[] real_req_dash;
1748        }
1749        else
1750        {
1751            // No Memory. We use non-scaled dash pattern...
1752            gdk_gc_set_dashes( m_penGC, 0, (wxGTKDash*)req_dash, req_nb_dash );
1753        }
1754    }
1755
1756    GdkCapStyle capStyle = GDK_CAP_ROUND;
1757    switch (m_pen.GetCap())
1758    {
1759        case wxCAP_PROJECTING: { capStyle = GDK_CAP_PROJECTING; break; }
1760        case wxCAP_BUTT:       { capStyle = GDK_CAP_BUTT;       break; }
1761        case wxCAP_ROUND:
1762        default:
1763        {
1764            if (width <= 1)
1765            {
1766                width = 0;
1767                capStyle = GDK_CAP_NOT_LAST;
1768            }
1769            else
1770            {
1771                capStyle = GDK_CAP_ROUND;
1772            }
1773            break;
1774        }
1775    }
1776
1777    GdkJoinStyle joinStyle = GDK_JOIN_ROUND;
1778    switch (m_pen.GetJoin())
1779    {
1780        case wxJOIN_BEVEL: { joinStyle = GDK_JOIN_BEVEL; break; }
1781        case wxJOIN_MITER: { joinStyle = GDK_JOIN_MITER; break; }
1782        case wxJOIN_ROUND:
1783        default:           { joinStyle = GDK_JOIN_ROUND; break; }
1784    }
1785
1786    gdk_gc_set_line_attributes( m_penGC, width, lineStyle, capStyle, joinStyle );
1787
1788    m_pen.GetColour().CalcPixel( m_cmap );
1789    gdk_gc_set_foreground( m_penGC, m_pen.GetColour().GetColor() );
1790}
1791
1792void wxWindowDC::SetBrush( const wxBrush &brush )
1793{
1794    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1795
1796    if (m_brush == brush) return;
1797
1798    m_brush = brush;
1799
1800    if (!m_brush.Ok()) return;
1801
1802    if (!m_window) return;
1803
1804    m_brush.GetColour().CalcPixel( m_cmap );
1805    gdk_gc_set_foreground( m_brushGC, m_brush.GetColour().GetColor() );
1806
1807    gdk_gc_set_fill( m_brushGC, GDK_SOLID );
1808
1809    if ((m_brush.GetStyle() == wxSTIPPLE) && (m_brush.GetStipple()->Ok()))
1810    {
1811        if (m_brush.GetStipple()->GetPixmap())
1812        {
1813            gdk_gc_set_fill( m_brushGC, GDK_TILED );
1814            gdk_gc_set_tile( m_brushGC, m_brush.GetStipple()->GetPixmap() );
1815        }
1816        else
1817        {
1818            gdk_gc_set_fill( m_brushGC, GDK_STIPPLED );
1819            gdk_gc_set_stipple( m_brushGC, m_brush.GetStipple()->GetBitmap() );
1820        }
1821    }
1822
1823    if ((m_brush.GetStyle() == wxSTIPPLE_MASK_OPAQUE) && (m_brush.GetStipple()->GetMask()))
1824    {
1825        gdk_gc_set_fill( m_textGC, GDK_OPAQUE_STIPPLED);
1826        gdk_gc_set_stipple( m_textGC, m_brush.GetStipple()->GetMask()->GetBitmap() );
1827    }
1828
1829    if (m_brush.IsHatch())
1830    {
1831        gdk_gc_set_fill( m_brushGC, GDK_STIPPLED );
1832        int num = m_brush.GetStyle() - wxBDIAGONAL_HATCH;
1833        gdk_gc_set_stipple( m_brushGC, hatches[num] );
1834    }
1835}
1836
1837void wxWindowDC::SetBackground( const wxBrush &brush )
1838{
1839   /* CMB 21/7/98: Added SetBackground. Sets background brush
1840    * for Clear() and bg colour for shapes filled with cross-hatch brush */
1841
1842    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1843
1844    if (m_backgroundBrush == brush) return;
1845
1846    m_backgroundBrush = brush;
1847
1848    if (!m_backgroundBrush.Ok()) return;
1849
1850    if (!m_window) return;
1851
1852    m_backgroundBrush.GetColour().CalcPixel( m_cmap );
1853    gdk_gc_set_background( m_brushGC, m_backgroundBrush.GetColour().GetColor() );
1854    gdk_gc_set_background( m_penGC, m_backgroundBrush.GetColour().GetColor() );
1855    gdk_gc_set_background( m_bgGC, m_backgroundBrush.GetColour().GetColor() );
1856    gdk_gc_set_foreground( m_bgGC, m_backgroundBrush.GetColour().GetColor() );
1857
1858    gdk_gc_set_fill( m_bgGC, GDK_SOLID );
1859
1860    if ((m_backgroundBrush.GetStyle() == wxSTIPPLE) && (m_backgroundBrush.GetStipple()->Ok()))
1861    {
1862        if (m_backgroundBrush.GetStipple()->GetPixmap())
1863        {
1864            gdk_gc_set_fill( m_bgGC, GDK_TILED );
1865            gdk_gc_set_tile( m_bgGC, m_backgroundBrush.GetStipple()->GetPixmap() );
1866        }
1867        else
1868        {
1869            gdk_gc_set_fill( m_bgGC, GDK_STIPPLED );
1870            gdk_gc_set_stipple( m_bgGC, m_backgroundBrush.GetStipple()->GetBitmap() );
1871        }
1872    }
1873
1874    if (m_backgroundBrush.IsHatch())
1875    {
1876        gdk_gc_set_fill( m_bgGC, GDK_STIPPLED );
1877        int num = m_backgroundBrush.GetStyle() - wxBDIAGONAL_HATCH;
1878        gdk_gc_set_stipple( m_bgGC, hatches[num] );
1879    }
1880}
1881
1882void wxWindowDC::SetLogicalFunction( int function )
1883{
1884    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1885
1886    if (m_logicalFunction == function)
1887        return;
1888
1889    // VZ: shouldn't this be a CHECK?
1890    if (!m_window)
1891        return;
1892
1893    GdkFunction mode;
1894    switch (function)
1895    {
1896        case wxXOR:          mode = GDK_XOR;           break;
1897        case wxINVERT:       mode = GDK_INVERT;        break;
1898        case wxOR_REVERSE:   mode = GDK_OR_REVERSE;    break;
1899        case wxAND_REVERSE:  mode = GDK_AND_REVERSE;   break;
1900        case wxCLEAR:        mode = GDK_CLEAR;         break;
1901        case wxSET:          mode = GDK_SET;           break;
1902        case wxOR_INVERT:    mode = GDK_OR_INVERT;     break;
1903        case wxAND:          mode = GDK_AND;           break;
1904        case wxOR:           mode = GDK_OR;            break;
1905        case wxEQUIV:        mode = GDK_EQUIV;         break;
1906        case wxNAND:         mode = GDK_NAND;          break;
1907        case wxAND_INVERT:   mode = GDK_AND_INVERT;    break;
1908        case wxCOPY:         mode = GDK_COPY;          break;
1909        case wxNO_OP:        mode = GDK_NOOP;          break;
1910        case wxSRC_INVERT:   mode = GDK_COPY_INVERT;   break;
1911
1912        // unsupported by GTK
1913        case wxNOR:          mode = GDK_COPY;          break;
1914        default:
1915           wxFAIL_MSG( wxT("unsupported logical function") );
1916           mode = GDK_COPY;
1917    }
1918
1919    m_logicalFunction = function;
1920
1921    gdk_gc_set_function( m_penGC, mode );
1922    gdk_gc_set_function( m_brushGC, mode );
1923
1924    // to stay compatible with wxMSW, we don't apply ROPs to the text
1925    // operations (i.e. DrawText/DrawRotatedText).
1926    // True, but mono-bitmaps use the m_textGC and they use ROPs as well.
1927    gdk_gc_set_function( m_textGC, mode );
1928}
1929
1930void wxWindowDC::SetTextForeground( const wxColour &col )
1931{
1932    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1933
1934    // don't set m_textForegroundColour to an invalid colour as we'd crash
1935    // later then (we use m_textForegroundColour.GetColor() without checking
1936    // in a few places)
1937    if ( !col.Ok() || (m_textForegroundColour == col) )
1938        return;
1939
1940    m_textForegroundColour = col;
1941
1942    if ( m_window )
1943    {
1944        m_textForegroundColour.CalcPixel( m_cmap );
1945        gdk_gc_set_foreground( m_textGC, m_textForegroundColour.GetColor() );
1946    }
1947}
1948
1949void wxWindowDC::SetTextBackground( const wxColour &col )
1950{
1951    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1952
1953    // same as above
1954    if ( !col.Ok() || (m_textBackgroundColour == col) )
1955        return;
1956
1957    m_textBackgroundColour = col;
1958
1959    if ( m_window )
1960    {
1961        m_textBackgroundColour.CalcPixel( m_cmap );
1962        gdk_gc_set_background( m_textGC, m_textBackgroundColour.GetColor() );
1963    }
1964}
1965
1966void wxWindowDC::SetBackgroundMode( int mode )
1967{
1968    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1969
1970    m_backgroundMode = mode;
1971
1972    if (!m_window) return;
1973
1974    // CMB 21/7/98: fill style of cross-hatch brushes is affected by
1975    // transparent/solid background mode
1976
1977    if (m_brush.GetStyle() != wxSOLID && m_brush.GetStyle() != wxTRANSPARENT)
1978    {
1979        gdk_gc_set_fill( m_brushGC,
1980          (m_backgroundMode == wxTRANSPARENT) ? GDK_STIPPLED : GDK_OPAQUE_STIPPLED);
1981    }
1982}
1983
1984void wxWindowDC::SetPalette( const wxPalette& WXUNUSED(palette) )
1985{
1986    wxFAIL_MSG( wxT("wxWindowDC::SetPalette not implemented") );
1987}
1988
1989void wxWindowDC::DoSetClippingRegion( wxCoord x, wxCoord y, wxCoord width, wxCoord height )
1990{
1991    wxCHECK_RET( Ok(), wxT("invalid window dc") );
1992
1993    if (!m_window) return;
1994
1995    wxRect rect;
1996    rect.x = XLOG2DEV(x);
1997    rect.y = YLOG2DEV(y);
1998    rect.width = XLOG2DEVREL(width);
1999    rect.height = YLOG2DEVREL(height);
2000
2001    if (!m_currentClippingRegion.IsNull())
2002        m_currentClippingRegion.Intersect( rect );
2003    else
2004        m_currentClippingRegion.Union( rect );
2005
2006#if USE_PAINT_REGION
2007    if (!m_paintClippingRegion.IsNull())
2008        m_currentClippingRegion.Intersect( m_paintClippingRegion );
2009#endif
2010
2011    wxCoord xx, yy, ww, hh;
2012    m_currentClippingRegion.GetBox( xx, yy, ww, hh );
2013    wxDC::DoSetClippingRegion( xx, yy, ww, hh );
2014
2015    gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
2016    gdk_gc_set_clip_region( m_brushGC, m_currentClippingRegion.GetRegion() );
2017    gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
2018    gdk_gc_set_clip_region( m_bgGC, m_currentClippingRegion.GetRegion() );
2019}
2020
2021void wxWindowDC::DoSetClippingRegionAsRegion( const wxRegion &region  )
2022{
2023    wxCHECK_RET( Ok(), wxT("invalid window dc") );
2024
2025    if (region.Empty())
2026    {
2027        DestroyClippingRegion();
2028        return;
2029    }
2030
2031    if (!m_window) return;
2032
2033    if (!m_currentClippingRegion.IsNull())
2034        m_currentClippingRegion.Intersect( region );
2035    else
2036        m_currentClippingRegion.Union( region );
2037
2038#if USE_PAINT_REGION
2039    if (!m_paintClippingRegion.IsNull())
2040        m_currentClippingRegion.Intersect( m_paintClippingRegion );
2041#endif
2042
2043    wxCoord xx, yy, ww, hh;
2044    m_currentClippingRegion.GetBox( xx, yy, ww, hh );
2045    wxDC::DoSetClippingRegion( xx, yy, ww, hh );
2046
2047    gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
2048    gdk_gc_set_clip_region( m_brushGC, m_currentClippingRegion.GetRegion() );
2049    gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
2050    gdk_gc_set_clip_region( m_bgGC, m_currentClippingRegion.GetRegion() );
2051}
2052
2053void wxWindowDC::DestroyClippingRegion()
2054{
2055    wxCHECK_RET( Ok(), wxT("invalid window dc") );
2056
2057    wxDC::DestroyClippingRegion();
2058
2059    m_currentClippingRegion.Clear();
2060
2061#if USE_PAINT_REGION
2062    if (!m_paintClippingRegion.IsEmpty())
2063        m_currentClippingRegion.Union( m_paintClippingRegion );
2064#endif
2065
2066    if (!m_window) return;
2067
2068    if (m_currentClippingRegion.IsEmpty())
2069    {
2070        gdk_gc_set_clip_rectangle( m_penGC, (GdkRectangle *) NULL );
2071        gdk_gc_set_clip_rectangle( m_brushGC, (GdkRectangle *) NULL );
2072        gdk_gc_set_clip_rectangle( m_textGC, (GdkRectangle *) NULL );
2073        gdk_gc_set_clip_rectangle( m_bgGC, (GdkRectangle *) NULL );
2074    }
2075    else
2076    {
2077        gdk_gc_set_clip_region( m_penGC, m_currentClippingRegion.GetRegion() );
2078        gdk_gc_set_clip_region( m_brushGC, m_currentClippingRegion.GetRegion() );
2079        gdk_gc_set_clip_region( m_textGC, m_currentClippingRegion.GetRegion() );
2080        gdk_gc_set_clip_region( m_bgGC, m_currentClippingRegion.GetRegion() );
2081    }
2082}
2083
2084void wxWindowDC::Destroy()
2085{
2086    if (m_penGC) wxFreePoolGC( m_penGC );
2087    m_penGC = (GdkGC*) NULL;
2088    if (m_brushGC) wxFreePoolGC( m_brushGC );
2089    m_brushGC = (GdkGC*) NULL;
2090    if (m_textGC) wxFreePoolGC( m_textGC );
2091    m_textGC = (GdkGC*) NULL;
2092    if (m_bgGC) wxFreePoolGC( m_bgGC );
2093    m_bgGC = (GdkGC*) NULL;
2094}
2095
2096void wxWindowDC::ComputeScaleAndOrigin()
2097{
2098    const wxRealPoint origScale(m_scaleX, m_scaleY);
2099
2100    wxDC::ComputeScaleAndOrigin();
2101
2102    // if scale has changed call SetPen to recalulate the line width
2103    if ( wxRealPoint(m_scaleX, m_scaleY) != origScale && m_pen.Ok() )
2104    {
2105        // this is a bit artificial, but we need to force wxDC to think the pen
2106        // has changed
2107        wxPen pen = m_pen;
2108        m_pen = wxNullPen;
2109        SetPen( pen );
2110    }
2111}
2112
2113// Resolution in pixels per logical inch
2114wxSize wxWindowDC::GetPPI() const
2115{
2116    return wxSize( (int) (m_mm_to_pix_x * 25.4 + 0.5), (int) (m_mm_to_pix_y * 25.4 + 0.5));
2117}
2118
2119int wxWindowDC::GetDepth() const
2120{
2121    wxFAIL_MSG(wxT("not implemented"));
2122
2123    return -1;
2124}
2125
2126
2127//-----------------------------------------------------------------------------
2128// wxPaintDC
2129//-----------------------------------------------------------------------------
2130
2131IMPLEMENT_DYNAMIC_CLASS(wxPaintDC, wxClientDC)
2132
2133// Limit the paint region to the window size. Sometimes
2134// the paint region is too big, and this risks X11 errors
2135static void wxLimitRegionToSize(wxRegion& region, const wxSize& sz)
2136{
2137    wxRect originalRect = region.GetBox();
2138    wxRect rect(originalRect);
2139    if (rect.width + rect.x > sz.x)
2140        rect.width = sz.x - rect.x;
2141    if (rect.height + rect.y > sz.y)
2142        rect.height = sz.y - rect.y;
2143    if (rect != originalRect)
2144    {
2145        region = wxRegion(rect);
2146        wxLogTrace(wxT("painting"), wxT("Limiting region from %d, %d, %d, %d to %d, %d, %d, %d\n"),
2147                   originalRect.x, originalRect.y, originalRect.width, originalRect.height,
2148                   rect.x, rect.y, rect.width, rect.height);
2149    }
2150}
2151
2152wxPaintDC::wxPaintDC( wxWindow *win )
2153         : wxClientDC( win )
2154{
2155#if USE_PAINT_REGION
2156    if (!win->m_clipPaintRegion)
2157        return;
2158
2159    wxSize sz = win->GetSize();
2160    m_paintClippingRegion = win->GetUpdateRegion();
2161    wxLimitRegionToSize(m_paintClippingRegion, sz);
2162
2163    GdkRegion *region = m_paintClippingRegion.GetRegion();
2164    if ( region )
2165    {
2166        m_currentClippingRegion.Union( m_paintClippingRegion );
2167        wxLimitRegionToSize(m_currentClippingRegion, sz);
2168
2169        if (sz.x <= 0 || sz.y <= 0)
2170            return ;
2171
2172        gdk_gc_set_clip_region( m_penGC, region );
2173        gdk_gc_set_clip_region( m_brushGC, region );
2174        gdk_gc_set_clip_region( m_textGC, region );
2175        gdk_gc_set_clip_region( m_bgGC, region );
2176    }
2177#endif // USE_PAINT_REGION
2178}
2179
2180//-----------------------------------------------------------------------------
2181// wxClientDC
2182//-----------------------------------------------------------------------------
2183
2184IMPLEMENT_DYNAMIC_CLASS(wxClientDC, wxWindowDC)
2185
2186wxClientDC::wxClientDC( wxWindow *win )
2187          : wxWindowDC( win )
2188{
2189    wxCHECK_RET( win, _T("NULL window in wxClientDC::wxClientDC") );
2190
2191#ifdef __WXUNIVERSAL__
2192    wxPoint ptOrigin = win->GetClientAreaOrigin();
2193    SetDeviceOrigin(ptOrigin.x, ptOrigin.y);
2194    wxSize size = win->GetClientSize();
2195    SetClippingRegion(wxPoint(0, 0), size);
2196#endif // __WXUNIVERSAL__
2197}
2198
2199void wxClientDC::DoGetSize(int *width, int *height) const
2200{
2201    wxCHECK_RET( m_owner, _T("GetSize() doesn't work without window") );
2202
2203    m_owner->GetClientSize( width, height );
2204}
2205
2206// ----------------------------------------------------------------------------
2207// wxDCModule
2208// ----------------------------------------------------------------------------
2209
2210class wxDCModule : public wxModule
2211{
2212public:
2213    bool OnInit();
2214    void OnExit();
2215
2216private:
2217    DECLARE_DYNAMIC_CLASS(wxDCModule)
2218};
2219
2220IMPLEMENT_DYNAMIC_CLASS(wxDCModule, wxModule)
2221
2222bool wxDCModule::OnInit()
2223{
2224    wxInitGCPool();
2225    return true;
2226}
2227
2228void wxDCModule::OnExit()
2229{
2230    wxCleanUpGCPool();
2231}
2232