Sending copy to an instance infers ownership by the sender so we must
[wxWidgets.git] / src / cocoa / dc.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/cocoa/dc.mm
3 // Purpose:     wxDC
4 // Author:      David Elliott
5 // Modified by:
6 // Created:     2003/04/01
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2003 David Elliott
9 // Licence:     wxWidgets licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifndef WX_PRECOMP
14     #include "wx/log.h"
15     #include "wx/dc.h"
16 #endif //WX_PRECOMP
17
18 #include "wx/cocoa/autorelease.h"
19 #include "wx/cocoa/string.h"
20
21 #import <AppKit/NSBezierPath.h>
22 #import <AppKit/NSTextStorage.h>
23 #import <AppKit/NSLayoutManager.h>
24 #import <AppKit/NSTextContainer.h>
25 #import <AppKit/NSGraphicsContext.h>
26 #import <AppKit/NSAffineTransform.h>
27 #import <AppKit/NSColor.h>
28 #import <AppKit/NSTypesetter.h>
29 #import <AppKit/NSImage.h>
30
31 #include "wx/listimpl.cpp"
32 WX_DEFINE_LIST(wxCocoaDCStack);
33
34 IMPLEMENT_ABSTRACT_CLASS(wxDC, wxObject)
35 WX_NSTextStorage wxDC::sm_cocoaNSTextStorage = nil;
36 WX_NSLayoutManager wxDC::sm_cocoaNSLayoutManager = nil;
37 WX_NSTextContainer wxDC::sm_cocoaNSTextContainer = nil;
38 wxCocoaDCStack wxDC::sm_cocoaDCStack;
39
40 inline void CocoaSetPenForNSBezierPath(wxPen &pen, NSBezierPath *bezpath)
41 {
42     [pen.GetNSColor() set];
43     const float *pattern;
44     [bezpath setLineDash:pattern count:pen.GetCocoaLineDash(&pattern) phase:0.0];
45     [bezpath setLineWidth:pen.GetWidth()];
46     switch(pen.GetJoin())
47     {
48     case wxJOIN_BEVEL:
49         [bezpath setLineJoinStyle:NSBevelLineJoinStyle];
50         break;
51     case wxJOIN_ROUND:
52         [bezpath setLineJoinStyle:NSRoundLineJoinStyle];
53         break;
54     case wxJOIN_MITER:
55         [bezpath setLineJoinStyle:NSMiterLineJoinStyle];
56         break;
57     }
58     switch(pen.GetCap())
59     {
60     case wxCAP_ROUND:
61         [bezpath setLineCapStyle:NSRoundLineCapStyle];
62         break;
63     case wxCAP_PROJECTING:
64         [bezpath setLineCapStyle:NSSquareLineCapStyle];
65         break;
66     case wxCAP_BUTT:
67         [bezpath setLineCapStyle:NSButtLineCapStyle];
68         break;
69     }
70 }
71
72 void wxDC::CocoaInitializeTextSystem()
73 {
74     wxASSERT_MSG(!sm_cocoaNSTextStorage && !sm_cocoaNSLayoutManager && !sm_cocoaNSTextContainer,wxT("Text system already initalized!  BAD PROGRAMMER!"));
75
76     sm_cocoaNSTextStorage = [[NSTextStorage alloc] init];
77
78     sm_cocoaNSLayoutManager = [[NSLayoutManager alloc] init];
79     [sm_cocoaNSTextStorage addLayoutManager:sm_cocoaNSLayoutManager];
80     // NSTextStorage retains NSLayoutManager, but so do we
81     // [sm_cocoaNSLayoutManager release]; [sm_cocoaNSLayoutManager retain];
82
83     // NOTE:  initWithContainerSize is the designated initializer, but the
84     // Apple CircleView sample gets away with just calling init, which
85     // is all we really need for our purposes.
86     sm_cocoaNSTextContainer = [[NSTextContainer alloc] init];
87     [sm_cocoaNSLayoutManager addTextContainer:sm_cocoaNSTextContainer];
88     // NSLayoutManager retains NSTextContainer, but so do we
89     // [sm_cocoaNSTextContainer release]; [sm_cocoaNSTextContainer retain];
90 }
91
92 void wxDC::CocoaShutdownTextSystem()
93 {
94     [sm_cocoaNSTextContainer release]; sm_cocoaNSTextContainer = nil;
95     [sm_cocoaNSLayoutManager release]; sm_cocoaNSLayoutManager = nil;
96     [sm_cocoaNSTextStorage release]; sm_cocoaNSTextStorage = nil;
97 }
98
99 void wxDC::CocoaUnwindStackAndLoseFocus()
100 {
101     wxCocoaDCStack::compatibility_iterator ourNode=sm_cocoaDCStack.Find(this);
102     if(ourNode)
103     {
104         wxCocoaDCStack::compatibility_iterator node=sm_cocoaDCStack.GetFirst();
105         for(;node!=ourNode; node=sm_cocoaDCStack.GetFirst())
106         {
107             wxDC *dc = node->GetData();
108             wxASSERT(dc);
109             wxASSERT(dc!=this);
110             if(!dc->CocoaUnlockFocus())
111             {
112                 wxFAIL_MSG(wxT("Unable to unlock focus on higher-level DC!"));
113             }
114             sm_cocoaDCStack.Erase(node);
115         }
116         wxASSERT(node==ourNode);
117         wxASSERT(ourNode->GetData() == this);
118         ourNode->GetData()->CocoaUnlockFocus();
119         sm_cocoaDCStack.Erase(ourNode);
120     }
121 }
122
123 bool wxDC::CocoaUnwindStackAndTakeFocus()
124 {
125     wxCocoaDCStack::compatibility_iterator node=sm_cocoaDCStack.GetFirst();
126     for(;node;node = sm_cocoaDCStack.GetFirst())
127     {
128         wxDC *dc = node->GetData();
129         wxASSERT(dc);
130         // If we're on the stack, then it's unwound enough and we have focus
131         if(dc==this)
132             return true;
133         // If unable to unlockFocus (e.g. wxPaintDC) stop here
134         if(!dc->CocoaUnlockFocus())
135             break;
136         sm_cocoaDCStack.Erase(node);
137     }
138     return CocoaLockFocus();
139 }
140
141 wxDC::wxDC(void)
142 {
143     m_cocoaWxToBoundsTransform = nil;
144     m_pen = *wxBLACK_PEN;
145 }
146
147 wxDC::~wxDC(void)
148 {
149 }
150
151 bool wxDC::CocoaLockFocus()
152 {
153     return false;
154 }
155
156 bool wxDC::CocoaUnlockFocus()
157 {
158     return false;
159 }
160
161 /*static*/ WX_NSAffineTransform wxDC::CocoaGetWxToBoundsTransform(bool isFlipped, float height)
162 {
163     NSAffineTransform *transform = nil;
164     // This transform flips the graphics since wxDC uses top-left origin
165     if(!isFlipped)
166     {
167         // The transform is auto released
168         transform = [NSAffineTransform transform];
169         /*  x' = 1x + 0y + 0
170             y' = 0x + -1y + window's height
171         */
172         NSAffineTransformStruct matrix = {
173             1,  0
174         ,   0, -1
175         ,   0, height
176         };
177         [transform setTransformStruct: matrix];
178     }
179     return transform;
180 }
181
182 void wxDC::CocoaApplyTransformations()
183 {
184     [m_cocoaWxToBoundsTransform concat];
185     // TODO: Apply device/logical/user position/scaling transformations
186 }
187
188 void wxDC::CocoaUnapplyTransformations()
189 {
190     // NOTE: You *must* call this with focus held.
191     // Undo all transforms so we're back in true Cocoa coords with
192     // no scaling or flipping.
193     NSAffineTransform *invertTransform;
194     invertTransform = [m_cocoaWxToBoundsTransform copy];
195     [invertTransform invert];
196     [invertTransform concat];
197     [invertTransform release];
198 }
199
200 bool wxDC::CocoaGetBounds(void *rectData)
201 {
202     // We don't know what we are so we can't return anything.
203     return false;
204 }
205
206 void wxDC::DoDrawRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
207 {
208     wxAutoNSAutoreleasePool pool;
209     if(!CocoaTakeFocus()) return;
210     NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:NSMakeRect(x,y,width,height)];
211     CocoaSetPenForNSBezierPath(m_pen,bezpath);
212     [bezpath stroke];
213     [m_brush.GetNSColor() set];
214     [bezpath fill];
215 }
216
217 void wxDC::DoDrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
218 {
219     wxAutoNSAutoreleasePool pool;
220     if(!CocoaTakeFocus()) return;
221     NSBezierPath *bezpath = [NSBezierPath bezierPath];
222     [bezpath moveToPoint:NSMakePoint(x1,y1)];
223     [bezpath lineToPoint:NSMakePoint(x2,y2)];
224
225     CocoaSetPenForNSBezierPath(m_pen,bezpath);
226     [bezpath stroke];
227 }
228
229 void wxDC::DoGetTextExtent(const wxString& text, wxCoord *x, wxCoord *y, wxCoord *descent, wxCoord *externalLeading, wxFont *theFont) const
230 {
231     wxAutoNSAutoreleasePool pool;
232 // FIXME: Cache this so it can be used for DoDrawText
233     wxASSERT_MSG(sm_cocoaNSTextStorage && sm_cocoaNSLayoutManager && sm_cocoaNSTextContainer, wxT("Text system has not been initialized.  BAD PROGRAMMER!"));
234     NSAttributedString *attributedString = [[NSAttributedString alloc]
235             initWithString:wxNSStringWithWxString(text.c_str())];
236     [sm_cocoaNSTextStorage setAttributedString:attributedString];
237     [attributedString release];
238
239     NSRange glyphRange = [sm_cocoaNSLayoutManager glyphRangeForTextContainer:sm_cocoaNSTextContainer];
240     NSRect usedRect = [sm_cocoaNSLayoutManager usedRectForTextContainer:sm_cocoaNSTextContainer];
241     if(x)
242         *x=(int)usedRect.size.width;
243     if(y)
244         *y=(int)usedRect.size.height;
245     if(descent)
246         *descent=0;
247     if(externalLeading)
248         *externalLeading=0;
249 }
250
251 void wxDC::DoDrawText(const wxString& text, wxCoord x, wxCoord y)
252 {
253     wxAutoNSAutoreleasePool pool;
254     if(!CocoaTakeFocus()) return;
255     wxASSERT_MSG(sm_cocoaNSTextStorage && sm_cocoaNSLayoutManager && sm_cocoaNSTextContainer, wxT("Text system has not been initialized.  BAD PROGRAMMER!"));
256     NSAttributedString *attributedString = [[NSAttributedString alloc]
257             initWithString:wxNSStringWithWxString(text.c_str())];
258     [sm_cocoaNSTextStorage setAttributedString:attributedString];
259     [attributedString release];
260
261     // Set the color (and later font) attributes
262     NSColor *fgColor = m_textForegroundColour.GetNSColor();
263     NSColor *bgColor = m_textBackgroundColour.GetNSColor();
264     if(!fgColor)
265         fgColor = [NSColor clearColor];
266     if(!bgColor)
267         bgColor = [NSColor clearColor];
268     NSDictionary *attrDict = [[NSDictionary alloc] initWithObjectsAndKeys:
269             fgColor, NSForegroundColorAttributeName,
270             bgColor, NSBackgroundColorAttributeName,
271             nil];
272     [sm_cocoaNSTextStorage addAttributes: attrDict range:NSMakeRange(0,[sm_cocoaNSTextStorage length])];
273     [attrDict release];
274
275     NSRange glyphRange = [sm_cocoaNSLayoutManager glyphRangeForTextContainer:sm_cocoaNSTextContainer];
276     NSRect usedRect = [sm_cocoaNSLayoutManager usedRectForTextContainer:sm_cocoaNSTextContainer];
277     // NOTE: We'll crash trying to get the location of glyphAtIndex:0 if
278     // there is no length or we don't start at zero
279     if(!glyphRange.length)
280         return;
281     wxASSERT_MSG(glyphRange.location==0,wxT("glyphRange must begin at zero"));
282
283     NSAffineTransform *transform = [NSAffineTransform transform];
284     [transform translateXBy:x yBy:y];
285
286     NSAffineTransform *flipTransform = [NSAffineTransform transform];
287     /*  x' = 1x + 0y + 0
288         y' = 0x + -1y + window's height
289     */
290     NSAffineTransformStruct matrix = {
291         1,  0
292     ,   0, -1
293     ,   0, usedRect.size.height
294     };
295     [flipTransform setTransformStruct: matrix];
296
297     NSGraphicsContext *context = [NSGraphicsContext currentContext];
298     [context saveGraphicsState];
299     [transform concat];
300     [flipTransform concat];
301     #if 0
302     // Draw+fill a rectangle so we can see where the shit is supposed to be.
303     wxLogTrace(wxTRACE_COCOA,wxT("(%f,%f) (%fx%f)"),usedRect.origin.x,usedRect.origin.y,usedRect.size.width,usedRect.size.height);
304     NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:NSMakeRect(0,0,usedRect.size.width,usedRect.size.height)];
305     [[NSColor blackColor] set];
306     [bezpath stroke];
307     [[NSColor blueColor] set];
308     [bezpath fill];
309     #endif
310
311     NSPoint layoutLocation = [sm_cocoaNSLayoutManager locationForGlyphAtIndex:0];
312     layoutLocation.x = 0.0;
313     layoutLocation.y *= -1.0;
314     layoutLocation.y += [[sm_cocoaNSLayoutManager typesetter] baselineOffsetInLayoutManager:sm_cocoaNSLayoutManager glyphIndex:0];
315     if(m_backgroundMode==wxSOLID)
316         [sm_cocoaNSLayoutManager drawBackgroundForGlyphRange:glyphRange  atPoint:NSZeroPoint];
317     [sm_cocoaNSLayoutManager drawGlyphsForGlyphRange:glyphRange  atPoint:layoutLocation];
318
319     [context restoreGraphicsState];
320 }
321
322 // wxDCBase functions
323 int wxDCBase::DeviceToLogicalX(int x) const
324 {
325     return x;
326 }
327
328 int wxDCBase::DeviceToLogicalY(int y) const
329 {
330     return y;
331 }
332
333 int wxDCBase::DeviceToLogicalXRel(int x) const
334 {
335     return x;
336 }
337
338 int wxDCBase::DeviceToLogicalYRel(int y) const
339 {
340     return y;
341 }
342
343 int wxDCBase::LogicalToDeviceX(int x) const
344 {
345     return x;
346 }
347
348 int wxDCBase::LogicalToDeviceY(int y) const
349 {
350     return y;
351 }
352
353 int wxDCBase::LogicalToDeviceXRel(int x) const
354 {
355     return x;
356 }
357
358 int wxDCBase::LogicalToDeviceYRel(int y) const
359 {
360     return y;
361 }
362
363 ///////////////////////////////////////////////////////////////////////////
364 // cut here, the rest is stubs
365 ///////////////////////////////////////////////////////////////////////////
366
367 //-----------------------------------------------------------------------------
368 // constants
369 //-----------------------------------------------------------------------------
370
371 #define mm2inches               0.0393700787402
372 #define inches2mm               25.4
373 #define mm2twips                56.6929133859
374 #define twips2mm                0.0176388888889
375 #define mm2pt                   2.83464566929
376 #define pt2mm                   0.352777777778
377
378 //-----------------------------------------------------------------------------
379 // wxDC
380 //-----------------------------------------------------------------------------
381
382 void wxDC::DoDrawIcon( const wxIcon &WXUNUSED(icon), int WXUNUSED(x), int WXUNUSED(y) )
383 {
384 };
385
386 void wxDC::DoDrawPoint( int x, int y ) 
387
388 };
389
390 void wxDC::DoDrawPolygon( int, wxPoint *, int, int, int)
391 {
392 };
393
394 void wxDC::DoDrawLines( int, wxPoint *, int, int )
395 {
396 }
397
398 int wxDC::GetDepth() const
399 {
400     return 0;
401 }
402
403 wxSize wxDC::GetPPI() const
404 {
405     return wxSize(0,0);
406 }
407
408 bool wxDC::CanGetTextExtent() const
409 {
410     return false;
411 }
412
413 wxCoord wxDC::GetCharHeight() const
414 {
415     return 0;
416 }
417
418 wxCoord wxDC::GetCharWidth() const
419 {
420     return 0;
421 }
422
423 bool wxDC::CanDrawBitmap() const
424 {
425     return true;
426 }
427
428 bool wxDC::DoGetPixel(wxCoord x, wxCoord y, wxColour *col) const
429 {
430     return false;
431 }
432
433 void wxDC::DoDrawArc(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord xc, wxCoord yc)
434 {
435 }
436     
437 void wxDC::SetPen(const wxPen& pen)
438 {
439     m_pen = pen;
440 }
441
442 void wxDC::SetBrush(const wxBrush& brush)
443 {
444     m_brush = brush;
445 }
446
447 void wxDC::DoSetClippingRegionAsRegion(const wxRegion& region)
448 {
449 }
450
451 void wxDC::DoSetClippingRegion(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
452 {
453 }
454
455 void wxDC::DestroyClippingRegion()
456 {
457 }
458
459 void wxDC::DoDrawRoundedRectangle(wxCoord x, wxCoord y, wxCoord width, wxCoord height, double radius)
460 {
461 }
462
463 void wxDC::DoDrawRotatedText(const wxString& text, wxCoord x, wxCoord y, double angle)
464 {
465 }
466
467 void wxDC::DoDrawEllipticArc(wxCoord x, wxCoord y, wxCoord w, wxCoord h, double sa, double ea)
468 {
469 }
470
471 void wxDC::DoDrawEllipse(wxCoord x, wxCoord y, wxCoord width, wxCoord height)
472 {
473 }
474
475 void wxDC::DoDrawBitmap(const wxBitmap &bmp, wxCoord x, wxCoord y, bool useMask)
476 {
477     wxAutoNSAutoreleasePool pool;
478     if(!CocoaTakeFocus()) return;
479     if(!bmp.Ok())
480         return;
481
482 #if 0
483     // Draw a rect so we can see where it's supposed to be
484     wxLogTrace(wxTRACE_COCOA,wxT("image at (%d,%d) size %dx%d"),x,y,bmp.GetWidth(),bmp.GetHeight());
485     NSBezierPath *bezpath = [NSBezierPath bezierPathWithRect:NSMakeRect(x,y,bmp.GetWidth(),bmp.GetHeight())];
486     [[NSColor blackColor] set];
487     [bezpath stroke];
488     [[NSColor blueColor] set];
489     [bezpath fill];
490 #endif // 0
491
492     NSAffineTransform *transform = [NSAffineTransform transform];
493     [transform translateXBy:x yBy:y];
494
495     NSAffineTransform *flipTransform = [NSAffineTransform transform];
496     /*  x' = 1x + 0y + 0
497         y' = 0x + -1y + window's height
498     */
499     NSAffineTransformStruct matrix = {
500         1,  0
501     ,   0, -1
502     ,   0, bmp.GetHeight()
503     };
504     [flipTransform setTransformStruct: matrix];
505
506     NSGraphicsContext *context = [NSGraphicsContext currentContext];
507     [context saveGraphicsState];
508     [transform concat];
509     [flipTransform concat];
510
511     NSImage *nsimage = [bmp.GetNSImage(useMask) retain];
512
513     [nsimage drawAtPoint: NSMakePoint(0,0)
514         fromRect: NSMakeRect(0.0,0.0,bmp.GetWidth(),bmp.GetHeight())
515         operation: NSCompositeSourceOver
516         fraction: 1.0];
517         
518     [nsimage release];
519     [context restoreGraphicsState];
520 }
521
522 bool wxDC::DoFloodFill(wxCoord x, wxCoord y, const wxColour& col, int style)
523 {
524     return false;
525 }
526
527 void wxDC::DoCrossHair(wxCoord x, wxCoord y)
528 {
529 }
530
531
532 bool wxDC::DoBlit(wxCoord xdest, wxCoord ydest, wxCoord width, wxCoord height, wxDC *source, wxCoord xsrc, wxCoord ysrc, int rop, bool useMask , wxCoord xsrcMask, wxCoord ysrcMask)
533 {
534     if(!CocoaTakeFocus()) return false;
535     if(!source) return false;
536     return source->CocoaDoBlitOnFocusedDC(xdest,ydest,width,height,
537         xsrc, ysrc, rop, useMask, xsrcMask, ysrcMask);
538 }
539
540 bool wxDC::CocoaDoBlitOnFocusedDC(wxCoord xdest, wxCoord ydest,
541     wxCoord width, wxCoord height, wxCoord xsrc, wxCoord ysrc,
542     int logicalFunc, bool useMask, wxCoord xsrcMask, wxCoord ysrcMask)
543 {
544     return false;
545 }
546
547 void wxDC::DoGetSize( int* width, int* height ) const
548 {
549   *width = m_maxX-m_minX;
550   *height = m_maxY-m_minY;
551 };
552
553 void wxDC::DoGetSizeMM( int* width, int* height ) const
554 {
555   int w = 0;
556   int h = 0;
557   GetSize( &w, &h );
558 };
559
560 void wxDC::SetTextForeground( const wxColour &col )
561 {
562   if (!Ok()) return;
563   m_textForegroundColour = col;
564 };
565
566 void wxDC::SetTextBackground( const wxColour &col )
567 {
568   if (!Ok()) return;
569   m_textBackgroundColour = col;
570 };
571
572 void wxDC::Clear()
573 {
574     if(!CocoaTakeFocus()) return;
575
576     NSRect boundsRect;
577     if(!CocoaGetBounds(&boundsRect)) return;
578
579     NSGraphicsContext *context = [NSGraphicsContext currentContext];
580     [context saveGraphicsState];
581
582     // Undo all transforms so when we draw our bounds rect we
583     // really overwrite our bounds rect.
584     CocoaUnapplyTransformations();
585
586     [m_backgroundBrush.GetNSColor() set];
587     [NSBezierPath fillRect:boundsRect];
588
589     [context restoreGraphicsState];
590 }
591
592 void wxDC::SetBackground(const wxBrush& brush)
593 {
594     m_backgroundBrush = brush;
595 }
596
597 void wxDC::SetPalette(const wxPalette&)
598 {
599 }
600
601 void wxDC::SetLogicalFunction(int)
602 {
603 }
604
605
606 void wxDC::SetMapMode( int mode )
607 {
608   switch (mode) 
609   {
610     case wxMM_TWIPS:
611       break;
612     case wxMM_POINTS:
613       break;
614     case wxMM_METRIC:
615       break;
616     case wxMM_LOMETRIC:
617       break;
618     default:
619     case wxMM_TEXT:
620       SetLogicalScale( 1.0, 1.0 );
621       break;
622   };
623   if (mode != wxMM_TEXT)
624   {
625   };
626 };
627
628 void wxDC::SetUserScale( double x, double y )
629 {
630   // allow negative ? -> no
631   m_userScaleX = x;
632   m_userScaleY = y;
633   ComputeScaleAndOrigin();
634 };
635
636 void wxDC::SetLogicalScale( double x, double y )
637 {
638   // allow negative ?
639   m_logicalScaleX = x;
640   m_logicalScaleY = y;
641   ComputeScaleAndOrigin();
642 };
643
644 void wxDC::SetLogicalOrigin( wxCoord x, wxCoord y )
645 {
646   m_logicalOriginX = x * m_signX;   // is this still correct ?
647   m_logicalOriginY = y * m_signY;
648   ComputeScaleAndOrigin();
649 };
650
651 void wxDC::SetDeviceOrigin( wxCoord x, wxCoord y )
652 {
653   ComputeScaleAndOrigin();
654 };
655
656 void wxDC::SetAxisOrientation( bool xLeftRight, bool yBottomUp )
657 {
658   m_signX = (xLeftRight ?  1 : -1);
659   m_signY = (yBottomUp  ? -1 :  1);
660   ComputeScaleAndOrigin();
661 };
662
663 void wxDC::ComputeScaleAndOrigin(void)
664 {
665   // CMB: copy scale to see if it changes
666   double origScaleX = m_scaleX;
667   double origScaleY = m_scaleY;
668
669   m_scaleX = m_logicalScaleX * m_userScaleX;
670   m_scaleY = m_logicalScaleY * m_userScaleY;
671
672   // CMB: if scale has changed call SetPen to recalulate the line width 
673   if (m_scaleX != origScaleX || m_scaleY != origScaleY)
674   {
675     // this is a bit artificial, but we need to force wxDC to think
676     // the pen has changed
677     const wxPen* pen = & GetPen();
678     wxPen tempPen;
679     m_pen = tempPen;
680     SetPen(* pen);
681   }
682 };
683