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