]> git.saurik.com Git - wxWidgets.git/blob - src/common/dcbase.cpp
added code for optimized handling of UTF-8 locales: some string operations are more...
[wxWidgets.git] / src / common / dcbase.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/dcbase.cpp
3 // Purpose: generic methods of the wxDC Class
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 05/25/99
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWidgets team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #include "wx/dc.h"
28 #include "wx/dcbuffer.h" // for IMPLEMENT_DYNAMIC_CLASS
29
30 #ifndef WX_PRECOMP
31 #include "wx/math.h"
32 #endif
33
34 // bool wxDCBase::sm_cacheing = false;
35
36 IMPLEMENT_ABSTRACT_CLASS(wxDCBase, wxObject)
37
38 // ============================================================================
39 // implementation
40 // ============================================================================
41
42 IMPLEMENT_DYNAMIC_CLASS(wxBufferedDC, wxMemoryDC)
43 IMPLEMENT_ABSTRACT_CLASS(wxBufferedPaintDC, wxBufferedDC)
44
45 wxDCBase::wxDCBase()
46 : m_colour(wxColourDisplay())
47 , m_ok(true)
48 , m_clipping(false)
49 , m_isInteractive(0)
50 , m_isBBoxValid(false)
51 , m_logicalOriginX(0), m_logicalOriginY(0)
52 , m_deviceOriginX(0), m_deviceOriginY(0)
53 , m_deviceLocalOriginX(0), m_deviceLocalOriginY(0)
54 , m_logicalScaleX(1.0), m_logicalScaleY(1.0)
55 , m_userScaleX(1.0), m_userScaleY(1.0)
56 , m_scaleX(1.0), m_scaleY(1.0)
57 , m_signX(1), m_signY(1)
58 , m_minX(0), m_minY(0), m_maxX(0), m_maxY(0)
59 , m_clipX1(0), m_clipY1(0), m_clipX2(0), m_clipY2(0)
60 , m_logicalFunction(wxCOPY)
61 , m_backgroundMode(wxTRANSPARENT)
62 , m_mappingMode(wxMM_TEXT)
63 , m_pen()
64 , m_brush()
65 , m_backgroundBrush(*wxTRANSPARENT_BRUSH)
66 , m_textForegroundColour(*wxBLACK)
67 , m_textBackgroundColour(*wxWHITE)
68 , m_font()
69 #if wxUSE_PALETTE
70 , m_palette()
71 , m_hasCustomPalette(false)
72 #endif // wxUSE_PALETTE
73 {
74 m_mm_to_pix_x = (double)wxGetDisplaySize().GetWidth() /
75 (double)wxGetDisplaySizeMM().GetWidth();
76 m_mm_to_pix_y = (double)wxGetDisplaySize().GetHeight() /
77 (double)wxGetDisplaySizeMM().GetHeight();
78
79 ResetBoundingBox();
80 ResetClipping();
81 }
82
83 wxDCBase::~wxDCBase()
84 {
85 }
86
87 #if WXWIN_COMPATIBILITY_2_6
88 void wxDCBase::BeginDrawing()
89 {
90 }
91
92 void wxDCBase::EndDrawing()
93 {
94 }
95 #endif // WXWIN_COMPATIBILITY_2_6
96
97 // ----------------------------------------------------------------------------
98 // coordinate conversions and transforms
99 // ----------------------------------------------------------------------------
100
101 wxCoord wxDCBase::DeviceToLogicalX(wxCoord x) const
102 {
103 return wxRound((double)(x - m_deviceOriginX - m_deviceLocalOriginX) / m_scaleX) * m_signX + m_logicalOriginX;
104 }
105
106 wxCoord wxDCBase::DeviceToLogicalY(wxCoord y) const
107 {
108 return wxRound((double)(y - m_deviceOriginY - m_deviceLocalOriginY) / m_scaleY) * m_signY + m_logicalOriginY;
109 }
110
111 wxCoord wxDCBase::DeviceToLogicalXRel(wxCoord x) const
112 {
113 return wxRound((double)(x) / m_scaleX);
114 }
115
116 wxCoord wxDCBase::DeviceToLogicalYRel(wxCoord y) const
117 {
118 return wxRound((double)(y) / m_scaleY);
119 }
120
121 wxCoord wxDCBase::LogicalToDeviceX(wxCoord x) const
122 {
123 return wxRound((double)(x - m_logicalOriginX) * m_scaleX) * m_signX + m_deviceOriginX + m_deviceLocalOriginX;
124 }
125
126 wxCoord wxDCBase::LogicalToDeviceY(wxCoord y) const
127 {
128 return wxRound((double)(y - m_logicalOriginY) * m_scaleY) * m_signY + m_deviceOriginY + m_deviceLocalOriginY;
129 }
130
131 wxCoord wxDCBase::LogicalToDeviceXRel(wxCoord x) const
132 {
133 return wxRound((double)(x) * m_scaleX);
134 }
135
136 wxCoord wxDCBase::LogicalToDeviceYRel(wxCoord y) const
137 {
138 return wxRound((double)(y) * m_scaleY);
139 }
140
141 void wxDCBase::ComputeScaleAndOrigin()
142 {
143 m_scaleX = m_logicalScaleX * m_userScaleX;
144 m_scaleY = m_logicalScaleY * m_userScaleY;
145 }
146
147 void wxDCBase::SetMapMode( int mode )
148 {
149 switch (mode)
150 {
151 case wxMM_TWIPS:
152 SetLogicalScale( twips2mm*m_mm_to_pix_x, twips2mm*m_mm_to_pix_y );
153 break;
154 case wxMM_POINTS:
155 SetLogicalScale( pt2mm*m_mm_to_pix_x, pt2mm*m_mm_to_pix_y );
156 break;
157 case wxMM_METRIC:
158 SetLogicalScale( m_mm_to_pix_x, m_mm_to_pix_y );
159 break;
160 case wxMM_LOMETRIC:
161 SetLogicalScale( m_mm_to_pix_x/10.0, m_mm_to_pix_y/10.0 );
162 break;
163 default:
164 case wxMM_TEXT:
165 SetLogicalScale( 1.0, 1.0 );
166 break;
167 }
168 m_mappingMode = mode;
169 }
170
171 void wxDCBase::SetUserScale( double x, double y )
172 {
173 // allow negative ? -> no
174 m_userScaleX = x;
175 m_userScaleY = y;
176 ComputeScaleAndOrigin();
177 }
178
179 void wxDCBase::SetLogicalScale( double x, double y )
180 {
181 // allow negative ?
182 m_logicalScaleX = x;
183 m_logicalScaleY = y;
184 ComputeScaleAndOrigin();
185 }
186
187 void wxDCBase::SetLogicalOrigin( wxCoord x, wxCoord y )
188 {
189 m_logicalOriginX = x * m_signX;
190 m_logicalOriginY = y * m_signY;
191 ComputeScaleAndOrigin();
192 }
193
194 void wxDCBase::SetDeviceOrigin( wxCoord x, wxCoord y )
195 {
196 m_deviceOriginX = x;
197 m_deviceOriginY = y;
198 ComputeScaleAndOrigin();
199 }
200
201 void wxDCBase::SetDeviceLocalOrigin( wxCoord x, wxCoord y )
202 {
203 m_deviceLocalOriginX = x;
204 m_deviceLocalOriginY = y;
205 ComputeScaleAndOrigin();
206 }
207
208 void wxDCBase::SetAxisOrientation( bool xLeftRight, bool yBottomUp )
209 {
210 // only wxPostScripDC has m_signX = -1, we override SetAxisOrientation there
211 m_signX = (xLeftRight ? 1 : -1);
212 m_signY = (yBottomUp ? -1 : 1);
213 ComputeScaleAndOrigin();
214 }
215
216 // ----------------------------------------------------------------------------
217 // special symbols
218 // ----------------------------------------------------------------------------
219
220 void wxDCBase::DoDrawCheckMark(wxCoord x1, wxCoord y1,
221 wxCoord width, wxCoord height)
222 {
223 wxCHECK_RET( Ok(), wxT("invalid window dc") );
224
225 wxCoord x2 = x1 + width,
226 y2 = y1 + height;
227
228 // the pen width is calibrated to give 3 for width == height == 10
229 wxDCPenChanger pen((wxDC&)*this,
230 wxPen(GetTextForeground(), (width + height + 1)/7));
231
232 // we're drawing a scaled version of wx/generic/tick.xpm here
233 wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
234 y3 = y1 + height / 2; // y of the left tick branch
235 DoDrawLine(x1, y3, x3, y2);
236 DoDrawLine(x3, y2, x2, y1);
237
238 CalcBoundingBox(x1, y1);
239 CalcBoundingBox(x2, y2);
240 }
241
242 // ----------------------------------------------------------------------------
243 // stubs for functions not implemented in all ports
244 // ----------------------------------------------------------------------------
245
246 bool
247 wxDCBase::DoStretchBlit(wxCoord xdest, wxCoord ydest,
248 wxCoord dstWidth, wxCoord dstHeight,
249 wxDC *source,
250 wxCoord xsrc, wxCoord ysrc,
251 wxCoord srcWidth, wxCoord srcHeight,
252 int rop,
253 bool useMask,
254 wxCoord xsrcMask,
255 wxCoord ysrcMask)
256 {
257 wxCHECK_MSG( srcWidth && srcHeight && dstWidth && dstHeight, false,
258 _T("invalid blit size") );
259
260 // emulate the stretching by modifying the DC scale
261 double xscale = (double)srcWidth/dstWidth,
262 yscale = (double)srcHeight/dstHeight;
263
264 double xscaleOld, yscaleOld;
265 GetUserScale(&xscaleOld, &yscaleOld);
266 SetUserScale(xscaleOld/xscale, yscaleOld/yscale);
267
268 bool rc = DoBlit(wxCoord(xdest*xscale), wxCoord(ydest*yscale),
269 wxCoord(dstWidth*xscale), wxCoord(dstHeight*yscale),
270 source,
271 xsrc, ysrc, rop, useMask, xsrcMask, ysrcMask);
272
273 SetUserScale(xscaleOld, yscaleOld);
274
275 return rc;
276 }
277
278 // ----------------------------------------------------------------------------
279 // line/polygons
280 // ----------------------------------------------------------------------------
281
282 void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
283 {
284 int n = list->GetCount();
285 wxPoint *points = new wxPoint[n];
286
287 int i = 0;
288 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
289 {
290 wxPoint *point = (wxPoint *)node->GetData();
291 points[i].x = point->x;
292 points[i].y = point->y;
293 }
294
295 DoDrawLines(n, points, xoffset, yoffset);
296
297 delete [] points;
298 }
299
300
301 void wxDCBase::DrawPolygon(const wxList *list,
302 wxCoord xoffset, wxCoord yoffset,
303 int fillStyle)
304 {
305 int n = list->GetCount();
306 wxPoint *points = new wxPoint[n];
307
308 int i = 0;
309 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
310 {
311 wxPoint *point = (wxPoint *)node->GetData();
312 points[i].x = point->x;
313 points[i].y = point->y;
314 }
315
316 DoDrawPolygon(n, points, xoffset, yoffset, fillStyle);
317
318 delete [] points;
319 }
320
321 void
322 wxDCBase::DoDrawPolyPolygon(int n,
323 int count[],
324 wxPoint points[],
325 wxCoord xoffset, wxCoord yoffset,
326 int fillStyle)
327 {
328 if ( n == 1 )
329 {
330 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
331 return;
332 }
333
334 int i, j, lastOfs;
335 wxPoint* pts;
336 wxPen pen;
337
338 for (i = j = lastOfs = 0; i < n; i++)
339 {
340 lastOfs = j;
341 j += count[i];
342 }
343 pts = new wxPoint[j+n-1];
344 for (i = 0; i < j; i++)
345 pts[i] = points[i];
346 for (i = 2; i <= n; i++)
347 {
348 lastOfs -= count[n-i];
349 pts[j++] = pts[lastOfs];
350 }
351
352 pen = GetPen();
353 SetPen(wxPen(*wxBLACK, 0, wxTRANSPARENT));
354 DoDrawPolygon(j, pts, xoffset, yoffset, fillStyle);
355 SetPen(pen);
356 for (i = j = 0; i < n; i++)
357 {
358 DoDrawLines(count[i], pts+j, xoffset, yoffset);
359 j += count[i];
360 }
361 delete[] pts;
362 }
363
364 // ----------------------------------------------------------------------------
365 // splines
366 // ----------------------------------------------------------------------------
367
368 #if wxUSE_SPLINES
369
370 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
371 void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
372 {
373 wxList point_list;
374
375 wxPoint *point1 = new wxPoint;
376 point1->x = x1; point1->y = y1;
377 point_list.Append((wxObject*)point1);
378
379 wxPoint *point2 = new wxPoint;
380 point2->x = x2; point2->y = y2;
381 point_list.Append((wxObject*)point2);
382
383 wxPoint *point3 = new wxPoint;
384 point3->x = x3; point3->y = y3;
385 point_list.Append((wxObject*)point3);
386
387 DrawSpline(&point_list);
388
389 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
390 {
391 wxPoint *p = (wxPoint *)node->GetData();
392 delete p;
393 }
394 }
395
396 void wxDCBase::DrawSpline(int n, wxPoint points[])
397 {
398 wxList list;
399 for (int i =0; i < n; i++)
400 {
401 list.Append((wxObject*)&points[i]);
402 }
403
404 DrawSpline(&list);
405 }
406
407 // ----------------------------------- spline code ----------------------------------------
408
409 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
410 double a3, double b3, double a4, double b4);
411 void wx_clear_stack();
412 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
413 double *y3, double *x4, double *y4);
414 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
415 double x4, double y4);
416 static bool wx_spline_add_point(double x, double y);
417 static void wx_spline_draw_point_array(wxDCBase *dc);
418
419 wxList wx_spline_point_list;
420
421 #define half(z1, z2) ((z1+z2)/2.0)
422 #define THRESHOLD 5
423
424 /* iterative version */
425
426 void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
427 double b4)
428 {
429 register double xmid, ymid;
430 double x1, y1, x2, y2, x3, y3, x4, y4;
431
432 wx_clear_stack();
433 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
434
435 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
436 xmid = (double)half(x2, x3);
437 ymid = (double)half(y2, y3);
438 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
439 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
440 wx_spline_add_point( x1, y1 );
441 wx_spline_add_point( xmid, ymid );
442 } else {
443 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
444 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
445 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
446 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
447 }
448 }
449 }
450
451 /* utilities used by spline drawing routines */
452
453 typedef struct wx_spline_stack_struct {
454 double x1, y1, x2, y2, x3, y3, x4, y4;
455 } Stack;
456
457 #define SPLINE_STACK_DEPTH 20
458 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
459 static Stack *wx_stack_top;
460 static int wx_stack_count;
461
462 void wx_clear_stack()
463 {
464 wx_stack_top = wx_spline_stack;
465 wx_stack_count = 0;
466 }
467
468 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
469 {
470 wx_stack_top->x1 = x1;
471 wx_stack_top->y1 = y1;
472 wx_stack_top->x2 = x2;
473 wx_stack_top->y2 = y2;
474 wx_stack_top->x3 = x3;
475 wx_stack_top->y3 = y3;
476 wx_stack_top->x4 = x4;
477 wx_stack_top->y4 = y4;
478 wx_stack_top++;
479 wx_stack_count++;
480 }
481
482 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
483 double *x3, double *y3, double *x4, double *y4)
484 {
485 if (wx_stack_count == 0)
486 return (0);
487 wx_stack_top--;
488 wx_stack_count--;
489 *x1 = wx_stack_top->x1;
490 *y1 = wx_stack_top->y1;
491 *x2 = wx_stack_top->x2;
492 *y2 = wx_stack_top->y2;
493 *x3 = wx_stack_top->x3;
494 *y3 = wx_stack_top->y3;
495 *x4 = wx_stack_top->x4;
496 *y4 = wx_stack_top->y4;
497 return (1);
498 }
499
500 static bool wx_spline_add_point(double x, double y)
501 {
502 wxPoint *point = new wxPoint ;
503 point->x = (int) x;
504 point->y = (int) y;
505 wx_spline_point_list.Append((wxObject*)point);
506 return true;
507 }
508
509 static void wx_spline_draw_point_array(wxDCBase *dc)
510 {
511 dc->DrawLines(&wx_spline_point_list, 0, 0 );
512 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
513 while (node)
514 {
515 wxPoint *point = (wxPoint *)node->GetData();
516 delete point;
517 wx_spline_point_list.Erase(node);
518 node = wx_spline_point_list.GetFirst();
519 }
520 }
521
522 void wxDCBase::DoDrawSpline( wxList *points )
523 {
524 wxCHECK_RET( Ok(), wxT("invalid window dc") );
525
526 wxPoint *p;
527 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
528 double x1, y1, x2, y2;
529
530 wxList::compatibility_iterator node = points->GetFirst();
531 if (!node)
532 // empty list
533 return;
534
535 p = (wxPoint *)node->GetData();
536
537 x1 = p->x;
538 y1 = p->y;
539
540 node = node->GetNext();
541 p = (wxPoint *)node->GetData();
542
543 x2 = p->x;
544 y2 = p->y;
545 cx1 = (double)((x1 + x2) / 2);
546 cy1 = (double)((y1 + y2) / 2);
547 cx2 = (double)((cx1 + x2) / 2);
548 cy2 = (double)((cy1 + y2) / 2);
549
550 wx_spline_add_point(x1, y1);
551
552 while ((node = node->GetNext())
553 #if !wxUSE_STL
554 != NULL
555 #endif // !wxUSE_STL
556 )
557 {
558 p = (wxPoint *)node->GetData();
559 x1 = x2;
560 y1 = y2;
561 x2 = p->x;
562 y2 = p->y;
563 cx4 = (double)(x1 + x2) / 2;
564 cy4 = (double)(y1 + y2) / 2;
565 cx3 = (double)(x1 + cx4) / 2;
566 cy3 = (double)(y1 + cy4) / 2;
567
568 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
569
570 cx1 = cx4;
571 cy1 = cy4;
572 cx2 = (double)(cx1 + x2) / 2;
573 cy2 = (double)(cy1 + y2) / 2;
574 }
575
576 wx_spline_add_point( cx1, cy1 );
577 wx_spline_add_point( x2, y2 );
578
579 wx_spline_draw_point_array( this );
580 }
581
582 #endif // wxUSE_SPLINES
583
584 // ----------------------------------------------------------------------------
585 // Partial Text Extents
586 // ----------------------------------------------------------------------------
587
588
589 // Each element of the widths array will be the width of the string up to and
590 // including the corresponding character in text. This is the generic
591 // implementation, the port-specific classes should do this with native APIs
592 // if available and if faster. Note: pango_layout_index_to_pos is much slower
593 // than calling GetTextExtent!!
594
595 #define FWC_SIZE 256
596
597 class FontWidthCache
598 {
599 public:
600 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
601 ~FontWidthCache() { delete []m_widths; }
602
603 void Reset()
604 {
605 if (!m_widths)
606 m_widths = new int[FWC_SIZE];
607
608 memset(m_widths, 0, sizeof(int)*FWC_SIZE);
609 }
610
611 wxFont m_font;
612 double m_scaleX;
613 int *m_widths;
614 };
615
616 static FontWidthCache s_fontWidthCache;
617
618 bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
619 {
620 int totalWidth = 0;
621
622 const size_t len = text.length();
623 widths.Empty();
624 widths.Add(0, len);
625
626 // reset the cache if font or horizontal scale have changed
627 if ( !s_fontWidthCache.m_widths ||
628 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
629 (s_fontWidthCache.m_font != GetFont()) )
630 {
631 s_fontWidthCache.Reset();
632 s_fontWidthCache.m_font = GetFont();
633 s_fontWidthCache.m_scaleX = m_scaleX;
634 }
635
636 // Calculate the position of each character based on the widths of
637 // the previous characters
638 int w, h;
639 for ( size_t i = 0; i < len; i++ )
640 {
641 const wxChar c = text[i];
642 unsigned int c_int = (unsigned int)c;
643
644 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
645 {
646 w = s_fontWidthCache.m_widths[c_int];
647 }
648 else
649 {
650 GetTextExtent(c, &w, &h);
651 if (c_int < FWC_SIZE)
652 s_fontWidthCache.m_widths[c_int] = w;
653 }
654
655 totalWidth += w;
656 widths[i] = totalWidth;
657 }
658
659 return true;
660 }
661
662
663 // ----------------------------------------------------------------------------
664 // enhanced text drawing
665 // ----------------------------------------------------------------------------
666
667 void wxDCBase::GetMultiLineTextExtent(const wxString& text,
668 wxCoord *x,
669 wxCoord *y,
670 wxCoord *h,
671 const wxFont *font) const
672 {
673 wxCoord widthTextMax = 0, widthLine,
674 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
675
676 wxString curLine;
677 for ( const wxChar *pc = text; ; pc++ )
678 {
679 if ( *pc == _T('\n') || *pc == _T('\0') )
680 {
681 if ( curLine.empty() )
682 {
683 // we can't use GetTextExtent - it will return 0 for both width
684 // and height and an empty line should count in height
685 // calculation
686
687 // assume that this line has the same height as the previous
688 // one
689 if ( !heightLineDefault )
690 heightLineDefault = heightLine;
691
692 if ( !heightLineDefault )
693 {
694 // but we don't know it yet - choose something reasonable
695 GetTextExtent(_T("W"), NULL, &heightLineDefault,
696 NULL, NULL, font);
697 }
698
699 heightTextTotal += heightLineDefault;
700 }
701 else
702 {
703 GetTextExtent(curLine, &widthLine, &heightLine,
704 NULL, NULL, font);
705 if ( widthLine > widthTextMax )
706 widthTextMax = widthLine;
707 heightTextTotal += heightLine;
708 }
709
710 if ( *pc == _T('\n') )
711 {
712 curLine.clear();
713 }
714 else
715 {
716 // the end of string
717 break;
718 }
719 }
720 else
721 {
722 curLine += *pc;
723 }
724 }
725
726 if ( x )
727 *x = widthTextMax;
728 if ( y )
729 *y = heightTextTotal;
730 if ( h )
731 *h = heightLine;
732 }
733
734 void wxDCBase::DrawLabel(const wxString& text,
735 const wxBitmap& bitmap,
736 const wxRect& rect,
737 int alignment,
738 int indexAccel,
739 wxRect *rectBounding)
740 {
741 // find the text position
742 wxCoord widthText, heightText, heightLine;
743 GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
744
745 wxCoord width, height;
746 if ( bitmap.Ok() )
747 {
748 width = widthText + bitmap.GetWidth();
749 height = bitmap.GetHeight();
750 }
751 else // no bitmap
752 {
753 width = widthText;
754 height = heightText;
755 }
756
757 wxCoord x, y;
758 if ( alignment & wxALIGN_RIGHT )
759 {
760 x = rect.GetRight() - width;
761 }
762 else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
763 {
764 x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
765 }
766 else // alignment & wxALIGN_LEFT
767 {
768 x = rect.GetLeft();
769 }
770
771 if ( alignment & wxALIGN_BOTTOM )
772 {
773 y = rect.GetBottom() - height;
774 }
775 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
776 {
777 y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
778 }
779 else // alignment & wxALIGN_TOP
780 {
781 y = rect.GetTop();
782 }
783
784 // draw the bitmap first
785 wxCoord x0 = x,
786 y0 = y,
787 width0 = width;
788 if ( bitmap.Ok() )
789 {
790 DrawBitmap(bitmap, x, y, true /* use mask */);
791
792 wxCoord offset = bitmap.GetWidth() + 4;
793 x += offset;
794 width -= offset;
795
796 y += (height - heightText) / 2;
797 }
798
799 // we will draw the underscore under the accel char later
800 wxCoord startUnderscore = 0,
801 endUnderscore = 0,
802 yUnderscore = 0;
803
804 // split the string into lines and draw each of them separately
805 wxString curLine;
806 for ( wxString::const_iterator pc = text.begin(); ; ++pc )
807 {
808 if ( *pc == _T('\n') || pc == text.end() )
809 {
810 int xRealStart = x; // init it here to avoid compielr warnings
811
812 if ( !curLine.empty() )
813 {
814 // NB: can't test for !(alignment & wxALIGN_LEFT) because
815 // wxALIGN_LEFT is 0
816 if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
817 {
818 wxCoord widthLine;
819 GetTextExtent(curLine, &widthLine, NULL);
820
821 if ( alignment & wxALIGN_RIGHT )
822 {
823 xRealStart += width - widthLine;
824 }
825 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
826 {
827 xRealStart += (width - widthLine) / 2;
828 }
829 }
830 //else: left aligned, nothing to do
831
832 DrawText(curLine, xRealStart, y);
833 }
834
835 y += heightLine;
836
837 // do we have underscore in this line? we can check yUnderscore
838 // because it is set below to just y + heightLine if we do
839 if ( y == yUnderscore )
840 {
841 // adjust the horz positions to account for the shift
842 startUnderscore += xRealStart;
843 endUnderscore += xRealStart;
844 }
845
846 if ( pc == text.end() )
847 break;
848
849 curLine.clear();
850 }
851 else // not end of line
852 {
853 if ( pc - text.begin() == indexAccel )
854 {
855 // remeber to draw underscore here
856 GetTextExtent(curLine, &startUnderscore, NULL);
857 curLine += *pc;
858 GetTextExtent(curLine, &endUnderscore, NULL);
859
860 yUnderscore = y + heightLine;
861 }
862 else
863 {
864 curLine += *pc;
865 }
866 }
867 }
868
869 // draw the underscore if found
870 if ( startUnderscore != endUnderscore )
871 {
872 // it should be of the same colour as text
873 SetPen(wxPen(GetTextForeground(), 0, wxSOLID));
874
875 yUnderscore--;
876
877 DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
878 }
879
880 // return bounding rect if requested
881 if ( rectBounding )
882 {
883 *rectBounding = wxRect(x, y - heightText, widthText, heightText);
884 }
885
886 CalcBoundingBox(x0, y0);
887 CalcBoundingBox(x0 + width0, y0 + height);
888 }
889
890
891 void wxDCBase::DoGradientFillLinear(const wxRect& rect,
892 const wxColour& initialColour,
893 const wxColour& destColour,
894 wxDirection nDirection)
895 {
896 // save old pen
897 wxPen oldPen = m_pen;
898 wxBrush oldBrush = m_brush;
899
900 wxUint8 nR1 = initialColour.Red();
901 wxUint8 nG1 = initialColour.Green();
902 wxUint8 nB1 = initialColour.Blue();
903 wxUint8 nR2 = destColour.Red();
904 wxUint8 nG2 = destColour.Green();
905 wxUint8 nB2 = destColour.Blue();
906 wxUint8 nR, nG, nB;
907
908 if ( nDirection == wxEAST || nDirection == wxWEST )
909 {
910 wxInt32 x = rect.GetWidth();
911 wxInt32 w = x; // width of area to shade
912 wxInt32 xDelta = w/256; // height of one shade bend
913 if (xDelta < 1)
914 xDelta = 1;
915
916 while (x >= xDelta)
917 {
918 x -= xDelta;
919 if (nR1 > nR2)
920 nR = nR1 - (nR1-nR2)*(w-x)/w;
921 else
922 nR = nR1 + (nR2-nR1)*(w-x)/w;
923
924 if (nG1 > nG2)
925 nG = nG1 - (nG1-nG2)*(w-x)/w;
926 else
927 nG = nG1 + (nG2-nG1)*(w-x)/w;
928
929 if (nB1 > nB2)
930 nB = nB1 - (nB1-nB2)*(w-x)/w;
931 else
932 nB = nB1 + (nB2-nB1)*(w-x)/w;
933
934 wxColour colour(nR,nG,nB);
935 SetPen(wxPen(colour, 1, wxSOLID));
936 SetBrush(wxBrush(colour));
937 if(nDirection == wxEAST)
938 DrawRectangle(rect.GetRight()-x-xDelta, rect.GetTop(),
939 xDelta, rect.GetHeight());
940 else //nDirection == wxWEST
941 DrawRectangle(rect.GetLeft()+x, rect.GetTop(),
942 xDelta, rect.GetHeight());
943 }
944 }
945 else // nDirection == wxNORTH || nDirection == wxSOUTH
946 {
947 wxInt32 y = rect.GetHeight();
948 wxInt32 w = y; // height of area to shade
949 wxInt32 yDelta = w/255; // height of one shade bend
950 if (yDelta < 1)
951 yDelta = 1;
952
953 while (y > 0)
954 {
955 y -= yDelta;
956 if (nR1 > nR2)
957 nR = nR1 - (nR1-nR2)*(w-y)/w;
958 else
959 nR = nR1 + (nR2-nR1)*(w-y)/w;
960
961 if (nG1 > nG2)
962 nG = nG1 - (nG1-nG2)*(w-y)/w;
963 else
964 nG = nG1 + (nG2-nG1)*(w-y)/w;
965
966 if (nB1 > nB2)
967 nB = nB1 - (nB1-nB2)*(w-y)/w;
968 else
969 nB = nB1 + (nB2-nB1)*(w-y)/w;
970
971 wxColour colour(nR,nG,nB);
972 SetPen(wxPen(colour, 1, wxSOLID));
973 SetBrush(wxBrush(colour));
974 if(nDirection == wxNORTH)
975 DrawRectangle(rect.GetLeft(), rect.GetTop()+y,
976 rect.GetWidth(), yDelta);
977 else //nDirection == wxSOUTH
978 DrawRectangle(rect.GetLeft(), rect.GetBottom()-y-yDelta,
979 rect.GetWidth(), yDelta);
980 }
981 }
982
983 SetPen(oldPen);
984 SetBrush(oldBrush);
985 }
986
987 void wxDCBase::DoGradientFillConcentric(const wxRect& rect,
988 const wxColour& initialColour,
989 const wxColour& destColour,
990 const wxPoint& circleCenter)
991 {
992 //save the old pen color
993 wxColour oldPenColour = m_pen.GetColour();
994
995 wxUint8 nR1 = destColour.Red();
996 wxUint8 nG1 = destColour.Green();
997 wxUint8 nB1 = destColour.Blue();
998 wxUint8 nR2 = initialColour.Red();
999 wxUint8 nG2 = initialColour.Green();
1000 wxUint8 nB2 = initialColour.Blue();
1001 wxUint8 nR, nG, nB;
1002
1003
1004 //Radius
1005 wxInt32 cx = rect.GetWidth() / 2;
1006 wxInt32 cy = rect.GetHeight() / 2;
1007 wxInt32 nRadius;
1008 if (cx < cy)
1009 nRadius = cx;
1010 else
1011 nRadius = cy;
1012
1013 //Offset of circle
1014 wxInt32 nCircleOffX = circleCenter.x - (rect.GetWidth() / 2);
1015 wxInt32 nCircleOffY = circleCenter.y - (rect.GetHeight() / 2);
1016
1017 for ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
1018 {
1019 for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
1020 {
1021 //get color difference
1022 wxInt32 nGradient = ((nRadius -
1023 (wxInt32)sqrt(
1024 pow((double)(x - cx - nCircleOffX), 2) +
1025 pow((double)(y - cy - nCircleOffY), 2)
1026 )) * 100) / nRadius;
1027
1028 //normalize Gradient
1029 if (nGradient < 0 )
1030 nGradient = 0;
1031
1032 //get dest colors
1033 nR = (wxUint8)(nR1 + ((nR2 - nR1) * nGradient / 100));
1034 nG = (wxUint8)(nG1 + ((nG2 - nG1) * nGradient / 100));
1035 nB = (wxUint8)(nB1 + ((nB2 - nB1) * nGradient / 100));
1036
1037 //set the pixel
1038 m_pen.SetColour(wxColour(nR,nG,nB));
1039 DrawPoint(wxPoint(x + rect.GetLeft(), y + rect.GetTop()));
1040 }
1041 }
1042 //return old pen color
1043 m_pen.SetColour(oldPenColour);
1044 }
1045
1046 /*
1047 Notes for wxWidgets DrawEllipticArcRot(...)
1048
1049 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
1050 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
1051 which are also new.
1052
1053 All methods are generic, so they can be implemented in wxDCBase.
1054 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
1055 methods like (WinCE) wxDC::DoDrawArc(...).
1056
1057 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
1058 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
1059 parts) or every column (in steep parts) only one pixel is calculated.
1060 Trigonometric calculation (sin, cos, tan, atan) is only done if the
1061 starting angle is not equal to the ending angle. The calculation of the
1062 pixels is done using simple arithmetic only and should perform not too
1063 bad even on devices without floating point processor. I didn't test this yet.
1064
1065 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
1066 For instance: an ellipse rotated 180 degrees is drawn
1067 slightly different from the original.
1068
1069 The points are then moved to an array and used to draw a polyline and/or polygon
1070 (with center added, the pie).
1071 The result looks quite similar to the native ellipse, only e few pixels differ.
1072
1073 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
1074 slower as DrawEllipse(...), which calls the native API.
1075 An rotated ellipse outside the clipping region takes nearly the same time,
1076 while an native ellipse outside takes nearly no time to draw.
1077
1078 If you draw an arc with this new method, you will see the starting and ending angles
1079 are calculated properly.
1080 If you use DrawEllipticArc(...), you will see they are only correct for circles
1081 and not properly calculated for ellipses.
1082
1083 Peter Lenhard
1084 p.lenhard@t-online.de
1085 */
1086
1087 #ifdef __WXWINCE__
1088 void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
1089 wxCoord w, wxCoord h,
1090 double sa, double ea, double angle )
1091 {
1092 wxList list;
1093
1094 CalculateEllipticPoints( &list, x, y, w, h, sa, ea );
1095 Rotate( &list, angle, wxPoint( x+w/2, y+h/2 ) );
1096
1097 // Add center (for polygon/pie)
1098 list.Append( (wxObject*) new wxPoint( x+w/2, y+h/2 ) );
1099
1100 // copy list into array and delete list elements
1101 int n = list.GetCount();
1102 wxPoint *points = new wxPoint[n];
1103 int i = 0;
1104 wxNode* node = 0;
1105 for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
1106 {
1107 wxPoint *point = (wxPoint *)node->GetData();
1108 points[i].x = point->x;
1109 points[i].y = point->y;
1110 delete point;
1111 }
1112
1113 // first draw the pie without pen, if necessary
1114 if( GetBrush() != *wxTRANSPARENT_BRUSH )
1115 {
1116 wxPen tempPen( GetPen() );
1117 SetPen( *wxTRANSPARENT_PEN );
1118 DoDrawPolygon( n, points, 0, 0 );
1119 SetPen( tempPen );
1120 }
1121
1122 // then draw the arc without brush, if necessary
1123 if( GetPen() != *wxTRANSPARENT_PEN )
1124 {
1125 // without center
1126 DoDrawLines( n-1, points, 0, 0 );
1127 }
1128
1129 delete [] points;
1130
1131 } // DrawEllipticArcRot
1132
1133 void wxDCBase::Rotate( wxList* points, double angle, wxPoint center )
1134 {
1135 if( angle != 0.0 )
1136 {
1137 double pi(M_PI);
1138 double dSinA = -sin(angle*2.0*pi/360.0);
1139 double dCosA = cos(angle*2.0*pi/360.0);
1140 for ( wxNode* node = points->GetFirst(); node; node = node->GetNext() )
1141 {
1142 wxPoint* point = (wxPoint*)node->GetData();
1143
1144 // transform coordinates, if necessary
1145 if( center.x ) point->x -= center.x;
1146 if( center.y ) point->y -= center.y;
1147
1148 // calculate rotation, rounding simply by implicit cast to integer
1149 int xTemp = point->x * dCosA - point->y * dSinA;
1150 point->y = point->x * dSinA + point->y * dCosA;
1151 point->x = xTemp;
1152
1153 // back transform coordinates, if necessary
1154 if( center.x ) point->x += center.x;
1155 if( center.y ) point->y += center.y;
1156 }
1157 }
1158 }
1159
1160 void wxDCBase::CalculateEllipticPoints( wxList* points,
1161 wxCoord xStart, wxCoord yStart,
1162 wxCoord w, wxCoord h,
1163 double sa, double ea )
1164 {
1165 double pi = M_PI;
1166 double sar = 0;
1167 double ear = 0;
1168 int xsa = 0;
1169 int ysa = 0;
1170 int xea = 0;
1171 int yea = 0;
1172 int sq = 0;
1173 int eq = 0;
1174 bool bUseAngles = false;
1175 if( w<0 ) w = -w;
1176 if( h<0 ) h = -h;
1177 // half-axes
1178 wxCoord a = w/2;
1179 wxCoord b = h/2;
1180 // decrement 1 pixel if ellipse is smaller than 2*a, 2*b
1181 int decrX = 0;
1182 if( 2*a == w ) decrX = 1;
1183 int decrY = 0;
1184 if( 2*b == h ) decrY = 1;
1185 // center
1186 wxCoord xCenter = xStart + a;
1187 wxCoord yCenter = yStart + b;
1188 // calculate data for start and end, if necessary
1189 if( sa != ea )
1190 {
1191 bUseAngles = true;
1192 // normalisation of angles
1193 while( sa<0 ) sa += 360;
1194 while( ea<0 ) ea += 360;
1195 while( sa>=360 ) sa -= 360;
1196 while( ea>=360 ) ea -= 360;
1197 // calculate quadrant numbers
1198 if( sa > 270 ) sq = 3;
1199 else if( sa > 180 ) sq = 2;
1200 else if( sa > 90 ) sq = 1;
1201 if( ea > 270 ) eq = 3;
1202 else if( ea > 180 ) eq = 2;
1203 else if( ea > 90 ) eq = 1;
1204 sar = sa * pi / 180.0;
1205 ear = ea * pi / 180.0;
1206 // correct angle circle -> ellipse
1207 sar = atan( -a/(double)b * tan( sar ) );
1208 if ( sq == 1 || sq == 2 ) sar += pi;
1209 ear = atan( -a/(double)b * tan( ear ) );
1210 if ( eq == 1 || eq == 2 ) ear += pi;
1211 // coordinates of points
1212 xsa = xCenter + a * cos( sar );
1213 if( sq == 0 || sq == 3 ) xsa -= decrX;
1214 ysa = yCenter + b * sin( sar );
1215 if( sq == 2 || sq == 3 ) ysa -= decrY;
1216 xea = xCenter + a * cos( ear );
1217 if( eq == 0 || eq == 3 ) xea -= decrX;
1218 yea = yCenter + b * sin( ear );
1219 if( eq == 2 || eq == 3 ) yea -= decrY;
1220 } // if iUseAngles
1221 // calculate c1 = b^2, c2 = b^2/a^2 with a = w/2, b = h/2
1222 double c1 = b * b;
1223 double c2 = 2.0 / w;
1224 c2 *= c2;
1225 c2 *= c1;
1226 wxCoord x = 0;
1227 wxCoord y = b;
1228 long x2 = 1;
1229 long y2 = y*y;
1230 long y2_old = 0;
1231 long y_old = 0;
1232 // Lists for quadrant 1 to 4
1233 wxList pointsarray[4];
1234 // Calculate points for first quadrant and set in all quadrants
1235 for( x = 0; x <= a; ++x )
1236 {
1237 x2 = x2+x+x-1;
1238 y2_old = y2;
1239 y_old = y;
1240 bool bNewPoint = false;
1241 while( y2 > c1 - c2 * x2 && y > 0 )
1242 {
1243 bNewPoint = true;
1244 y2 = y2-y-y+1;
1245 --y;
1246 }
1247 // old y now to big: set point with old y, old x
1248 if( bNewPoint && x>1)
1249 {
1250 int x1 = x - 1;
1251 // remove points on the same line
1252 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter - y_old ) );
1253 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - x1, yCenter - y_old ) );
1254 pointsarray[2].Insert( (wxObject*) new wxPoint( xCenter - x1, yCenter + y_old - decrY ) );
1255 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + x1 - decrX, yCenter + y_old - decrY ) );
1256 } // set point
1257 } // calculate point
1258
1259 // Starting and/or ending points for the quadrants, first quadrant gets both.
1260 pointsarray[0].Insert( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
1261 pointsarray[0].Append( (wxObject*) new wxPoint( xCenter, yCenter - b ) );
1262 pointsarray[1].Append( (wxObject*) new wxPoint( xCenter - a, yCenter ) );
1263 pointsarray[2].Append( (wxObject*) new wxPoint( xCenter, yCenter + b - decrY ) );
1264 pointsarray[3].Append( (wxObject*) new wxPoint( xCenter + a - decrX, yCenter ) );
1265
1266 // copy quadrants in original list
1267 if( bUseAngles )
1268 {
1269 // Copy the right part of the points in the lists
1270 // and delete the wxPoints, because they do not leave this method.
1271 points->Append( (wxObject*) new wxPoint( xsa, ysa ) );
1272 int q = sq;
1273 bool bStarted = false;
1274 bool bReady = false;
1275 bool bForceTurn = ( sq == eq && sa > ea );
1276 while( !bReady )
1277 {
1278 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1279 {
1280 // once: go to starting point in start quadrant
1281 if( !bStarted &&
1282 (
1283 ( (wxPoint*) node->GetData() )->x < xsa+1 && q <= 1
1284 ||
1285 ( (wxPoint*) node->GetData() )->x > xsa-1 && q >= 2
1286 )
1287 )
1288 {
1289 bStarted = true;
1290 }
1291
1292 // copy point, if not at ending point
1293 if( bStarted )
1294 {
1295 if( q != eq || bForceTurn
1296 ||
1297 ( (wxPoint*) node->GetData() )->x > xea+1 && q <= 1
1298 ||
1299 ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
1300 )
1301 {
1302 // copy point
1303 wxPoint* pPoint = new wxPoint( *((wxPoint*) node->GetData() ) );
1304 points->Append( (wxObject*) pPoint );
1305 }
1306 else if( q == eq && !bForceTurn || ( (wxPoint*) node->GetData() )->x == xea)
1307 {
1308 bReady = true;
1309 }
1310 }
1311 } // for node
1312 ++q;
1313 if( q > 3 ) q = 0;
1314 bForceTurn = false;
1315 bStarted = true;
1316 } // while not bReady
1317 points->Append( (wxObject*) new wxPoint( xea, yea ) );
1318
1319 // delete points
1320 for( q = 0; q < 4; ++q )
1321 {
1322 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1323 {
1324 wxPoint *p = (wxPoint *)node->GetData();
1325 delete p;
1326 }
1327 }
1328 }
1329 else
1330 {
1331 wxNode* node;
1332 // copy whole ellipse, wxPoints will be deleted outside
1333 for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
1334 {
1335 wxObject *p = node->GetData();
1336 points->Append( p );
1337 }
1338 for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
1339 {
1340 wxObject *p = node->GetData();
1341 points->Append( p );
1342 }
1343 for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
1344 {
1345 wxObject *p = node->GetData();
1346 points->Append( p );
1347 }
1348 for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
1349 {
1350 wxObject *p = node->GetData();
1351 points->Append( p );
1352 }
1353 } // not iUseAngles
1354 } // CalculateEllipticPoints
1355
1356 #endif // __WXWINCE__