]> git.saurik.com Git - wxWidgets.git/blob - src/common/dcbase.cpp
Reverted last wxBookCtrl change and added alignment check to notebook
[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/math.h"
29
30 // bool wxDCBase::sm_cacheing = false;
31
32 IMPLEMENT_ABSTRACT_CLASS(wxDCBase, wxObject)
33
34 // ============================================================================
35 // implementation
36 // ============================================================================
37
38 #if WXWIN_COMPATIBILITY_2_6
39 void wxDCBase::BeginDrawing()
40 {
41 }
42
43 void wxDCBase::EndDrawing()
44 {
45 }
46 #endif // WXWIN_COMPATIBILITY_2_6
47
48 // ----------------------------------------------------------------------------
49 // special symbols
50 // ----------------------------------------------------------------------------
51
52 void 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
73 // ----------------------------------------------------------------------------
74 // line/polygons
75 // ----------------------------------------------------------------------------
76
77 void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
78 {
79 int n = list->GetCount();
80 wxPoint *points = new wxPoint[n];
81
82 int i = 0;
83 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
84 {
85 wxPoint *point = (wxPoint *)node->GetData();
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
96 void wxDCBase::DrawPolygon(const wxList *list,
97 wxCoord xoffset, wxCoord yoffset,
98 int fillStyle)
99 {
100 int n = list->GetCount();
101 wxPoint *points = new wxPoint[n];
102
103 int i = 0;
104 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
105 {
106 wxPoint *point = (wxPoint *)node->GetData();
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
116 void
117 wxDCBase::DoDrawPolyPolygon(int n,
118 int count[],
119 wxPoint points[],
120 wxCoord xoffset, wxCoord yoffset,
121 int fillStyle)
122 {
123 if ( n == 1 )
124 {
125 DoDrawPolygon(count[0], points, xoffset, yoffset, fillStyle);
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;
136 j += count[i];
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 {
143 lastOfs -= count[n-i];
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 {
153 DoDrawLines(count[i], pts+j, xoffset, yoffset);
154 j += count[i];
155 }
156 delete[] pts;
157 }
158
159 // ----------------------------------------------------------------------------
160 // splines
161 // ----------------------------------------------------------------------------
162
163 #if wxUSE_SPLINES
164
165 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
166 void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
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
184 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
185 {
186 wxPoint *p = (wxPoint *)node->GetData();
187 delete p;
188 }
189 }
190
191 void 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
202 // ----------------------------------- spline code ----------------------------------------
203
204 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
205 double a3, double b3, double a4, double b4);
206 void wx_clear_stack();
207 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
208 double *y3, double *x4, double *y4);
209 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
210 double x4, double y4);
211 static bool wx_spline_add_point(double x, double y);
212 static void wx_spline_draw_point_array(wxDCBase *dc);
213
214 wxList wx_spline_point_list;
215
216 #define half(z1, z2) ((z1+z2)/2.0)
217 #define THRESHOLD 5
218
219 /* iterative version */
220
221 void 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
248 typedef struct wx_spline_stack_struct {
249 double x1, y1, x2, y2, x3, y3, x4, y4;
250 } Stack;
251
252 #define SPLINE_STACK_DEPTH 20
253 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
254 static Stack *wx_stack_top;
255 static int wx_stack_count;
256
257 void wx_clear_stack()
258 {
259 wx_stack_top = wx_spline_stack;
260 wx_stack_count = 0;
261 }
262
263 void 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
277 int 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
295 static 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);
301 return true;
302 }
303
304 static void wx_spline_draw_point_array(wxDCBase *dc)
305 {
306 dc->DrawLines(&wx_spline_point_list, 0, 0 );
307 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
308 while (node)
309 {
310 wxPoint *point = (wxPoint *)node->GetData();
311 delete point;
312 wx_spline_point_list.Erase(node);
313 node = wx_spline_point_list.GetFirst();
314 }
315 }
316
317 void 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
325 wxList::compatibility_iterator node = points->GetFirst();
326 if (node == wxList::compatibility_iterator())
327 // empty list
328 return;
329
330 p = (wxPoint *)node->GetData();
331
332 x1 = p->x;
333 y1 = p->y;
334
335 node = node->GetNext();
336 p = (wxPoint *)node->GetData();
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
347 while ((node = node->GetNext())
348 #if !wxUSE_STL
349 != NULL
350 #endif // !wxUSE_STL
351 )
352 {
353 p = (wxPoint *)node->GetData();
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
377 #endif // wxUSE_SPLINES
378
379 // ----------------------------------------------------------------------------
380 // Partial Text Extents
381 // ----------------------------------------------------------------------------
382
383
384 // Each element of the widths array will be the width of the string up to and
385 // including the corresponding character in text. This is the generic
386 // implementation, the port-specific classes should do this with native APIs
387 // if available and if faster. Note: pango_layout_index_to_pos is much slower
388 // than calling GetTextExtent!!
389
390 #define FWC_SIZE 256
391
392 class FontWidthCache
393 {
394 public:
395 FontWidthCache() : m_scaleX(1), m_widths(NULL) { }
396 ~FontWidthCache() { delete []m_widths; }
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
406 wxFont m_font;
407 double m_scaleX;
408 int *m_widths;
409 };
410
411 static FontWidthCache s_fontWidthCache;
412
413 bool wxDCBase::DoGetPartialTextExtents(const wxString& text, wxArrayInt& widths) const
414 {
415 int totalWidth = 0;
416
417 const size_t len = text.Length();
418 widths.Empty();
419 widths.Add(0, len);
420
421 // reset the cache if font or horizontal scale have changed
422 if ( !s_fontWidthCache.m_widths ||
423 !wxIsSameDouble(s_fontWidthCache.m_scaleX, m_scaleX) ||
424 (s_fontWidthCache.m_font != GetFont()) )
425 {
426 s_fontWidthCache.Reset();
427 s_fontWidthCache.m_font = GetFont();
428 s_fontWidthCache.m_scaleX = m_scaleX;
429 }
430
431 // Calculate the position of each character based on the widths of
432 // the previous characters
433 int w, h;
434 for ( size_t i = 0; i < len; i++ )
435 {
436 const wxChar c = text[i];
437 unsigned int c_int = (unsigned int)c;
438
439 if ((c_int < FWC_SIZE) && (s_fontWidthCache.m_widths[c_int] != 0))
440 {
441 w = s_fontWidthCache.m_widths[c_int];
442 }
443 else
444 {
445 GetTextExtent(c, &w, &h);
446 if (c_int < FWC_SIZE)
447 s_fontWidthCache.m_widths[c_int] = w;
448 }
449
450 totalWidth += w;
451 widths[i] = totalWidth;
452 }
453
454 return true;
455 }
456
457
458 // ----------------------------------------------------------------------------
459 // enhanced text drawing
460 // ----------------------------------------------------------------------------
461
462 void wxDCBase::GetMultiLineTextExtent(const wxString& text,
463 wxCoord *x,
464 wxCoord *y,
465 wxCoord *h,
466 wxFont *font)
467 {
468 wxCoord widthTextMax = 0, widthLine,
469 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
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
529 void 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 {
585 DrawBitmap(bitmap, x, y, true /* use mask */);
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 {
648 if ( pc - text.c_str() == indexAccel )
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 }
684
685
686 void 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
776 void 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
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
806 for ( wxInt32 x = 0; x < rect.GetWidth(); x++ )
807 {
808 for ( wxInt32 y = 0; y < rect.GetHeight(); y++ )
809 {
810 //get color difference
811 wxInt32 nGradient = ((nRadius -
812 (wxInt32)sqrt(
813 pow((double)(x - cx - nCircleOffX), 2) +
814 pow((double)(y - cy - nCircleOffY), 2)
815 )) * 100) / nRadius;
816
817 //normalize Gradient
818 if (nGradient < 0 )
819 nGradient = 0;
820
821 //get dest colors
822 nR = (wxUint8)(nR1 + ((nR2 - nR1) * nGradient / 100));
823 nG = (wxUint8)(nG1 + ((nG2 - nG1) * nGradient / 100));
824 nB = (wxUint8)(nB1 + ((nB2 - nB1) * nGradient / 100));
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
835 /*
836 Notes for wxWidgets DrawEllipticArcRot(...)
837
838 wxDCBase::DrawEllipticArcRot(...) draws a rotated elliptic arc or an ellipse.
839 It uses wxDCBase::CalculateEllipticPoints(...) and wxDCBase::Rotate(...),
840 which are also new.
841
842 All methods are generic, so they can be implemented in wxDCBase.
843 DoDrawEllipticArcRot(...) is virtual, so it can be called from deeper
844 methods like (WinCE) wxDC::DoDrawArc(...).
845
846 CalculateEllipticPoints(...) fills a given list of wxPoints with some points
847 of an elliptic arc. The algorithm is pixel-based: In every row (in flat
848 parts) or every column (in steep parts) only one pixel is calculated.
849 Trigonometric calculation (sin, cos, tan, atan) is only done if the
850 starting angle is not equal to the ending angle. The calculation of the
851 pixels is done using simple arithmetic only and should perform not too
852 bad even on devices without floating point processor. I didn't test this yet.
853
854 Rotate(...) rotates a list of point pixel-based, you will see rounding errors.
855 For instance: an ellipse rotated 180 degrees is drawn
856 slightly different from the original.
857
858 The points are then moved to an array and used to draw a polyline and/or polygon
859 (with center added, the pie).
860 The result looks quite similar to the native ellipse, only e few pixels differ.
861
862 The performance on a desktop system (Athlon 1800, WinXP) is about 7 times
863 slower as DrawEllipse(...), which calls the native API.
864 An rotated ellipse outside the clipping region takes nearly the same time,
865 while an native ellipse outside takes nearly no time to draw.
866
867 If you draw an arc with this new method, you will see the starting and ending angles
868 are calculated properly.
869 If you use DrawEllipticArc(...), you will see they are only correct for circles
870 and not properly calculated for ellipses.
871
872 Peter Lenhard
873 p.lenhard@t-online.de
874 */
875
876 #ifdef __WXWINCE__
877 void wxDCBase::DoDrawEllipticArcRot( wxCoord x, wxCoord y,
878 wxCoord w, wxCoord h,
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
890 int n = list.GetCount();
891 wxPoint *points = new wxPoint[n];
892 int i = 0;
893 wxNode* node = 0;
894 for ( node = list.GetFirst(); node; node = node->GetNext(), i++ )
895 {
896 wxPoint *point = (wxPoint *)node->GetData();
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
903 if( GetBrush() != *wxTRANSPARENT_BRUSH )
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
912 if( GetPen() != *wxTRANSPARENT_PEN )
913 {
914 // without center
915 DoDrawLines( n-1, points, 0, 0 );
916 }
917
918 delete [] points;
919
920 } // DrawEllipticArcRot
921
922 void wxDCBase::Rotate( wxList* points, double angle, wxPoint center )
923 {
924 if( angle != 0.0 )
925 {
926 double pi(M_PI);
927 double dSinA = -sin(angle*2.0*pi/360.0);
928 double dCosA = cos(angle*2.0*pi/360.0);
929 for ( wxNode* node = points->GetFirst(); node; node = node->GetNext() )
930 {
931 wxPoint* point = (wxPoint*)node->GetData();
932
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
949 void wxDCBase::CalculateEllipticPoints( wxList* points,
950 wxCoord xStart, wxCoord yStart,
951 wxCoord w, wxCoord h,
952 double sa, double ea )
953 {
954 double pi = M_PI;
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;
971 if( 2*a == w ) decrX = 1;
972 int decrY = 0;
973 if( 2*b == h ) decrY = 1;
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 ) );
997 if ( sq == 1 || sq == 2 ) sar += pi;
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 }
1036 // old y now to big: set point with old y, old x
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
1047
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 {
1067 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1068 {
1069 // once: go to starting point in start quadrant
1070 if( !bStarted &&
1071 (
1072 ( (wxPoint*) node->GetData() )->x < xsa+1 && q <= 1
1073 ||
1074 ( (wxPoint*) node->GetData() )->x > xsa-1 && q >= 2
1075 )
1076 )
1077 {
1078 bStarted = true;
1079 }
1080
1081 // copy point, if not at ending point
1082 if( bStarted )
1083 {
1084 if( q != eq || bForceTurn
1085 ||
1086 ( (wxPoint*) node->GetData() )->x > xea+1 && q <= 1
1087 ||
1088 ( (wxPoint*) node->GetData() )->x < xea-1 && q >= 2
1089 )
1090 {
1091 // copy point
1092 wxPoint* pPoint = new wxPoint( *((wxPoint*) node->GetData() ) );
1093 points->Append( (wxObject*) pPoint );
1094 }
1095 else if( q == eq && !bForceTurn || ( (wxPoint*) node->GetData() )->x == xea)
1096 {
1097 bReady = true;
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 {
1111 for( wxNode *node = pointsarray[q].GetFirst(); node; node = node->GetNext() )
1112 {
1113 wxPoint *p = (wxPoint *)node->GetData();
1114 delete p;
1115 }
1116 }
1117 }
1118 else
1119 {
1120 wxNode* node;
1121 // copy whole ellipse, wxPoints will be deleted outside
1122 for( node = pointsarray[0].GetFirst(); node; node = node->GetNext() )
1123 {
1124 wxObject *p = node->GetData();
1125 points->Append( p );
1126 }
1127 for( node = pointsarray[1].GetFirst(); node; node = node->GetNext() )
1128 {
1129 wxObject *p = node->GetData();
1130 points->Append( p );
1131 }
1132 for( node = pointsarray[2].GetFirst(); node; node = node->GetNext() )
1133 {
1134 wxObject *p = node->GetData();
1135 points->Append( p );
1136 }
1137 for( node = pointsarray[3].GetFirst(); node; node = node->GetNext() )
1138 {
1139 wxObject *p = node->GetData();
1140 points->Append( p );
1141 }
1142 } // not iUseAngles
1143 } // CalculateEllipticPoints
1144
1145 #endif