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