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