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