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