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