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