STL-ification patch for wxMSW and wxGTK.
[wxWidgets.git] / src / common / dcbase.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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) wxWindows team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 #ifdef __GNUG__
17 #pragma implementation "dcbase.h"
18 #endif
19
20 // ----------------------------------------------------------------------------
21 // headers
22 // ----------------------------------------------------------------------------
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #include "wx/dc.h"
32
33 #include <math.h>
34
35 // bool wxDCBase::sm_cacheing = FALSE;
36
37 // ============================================================================
38 // implementation
39 // ============================================================================
40
41 // ----------------------------------------------------------------------------
42 // special symbols
43 // ----------------------------------------------------------------------------
44
45 void wxDCBase::DoDrawCheckMark(wxCoord x1, wxCoord y1,
46 wxCoord width, wxCoord height)
47 {
48 wxCHECK_RET( Ok(), wxT("invalid window dc") );
49
50 wxCoord x2 = x1 + width,
51 y2 = y1 + height;
52
53 // this is to yield width of 3 for width == height == 10
54 SetPen(wxPen(GetTextForeground(), (width + height + 1) / 7, wxSOLID));
55
56 // we're drawing a scaled version of wx/generic/tick.xpm here
57 wxCoord x3 = x1 + (4*width) / 10, // x of the tick bottom
58 y3 = y1 + height / 2; // y of the left tick branch
59 DoDrawLine(x1, y3, x3, y2);
60 DoDrawLine(x3, y2, x2, y1);
61
62 CalcBoundingBox(x1, y1);
63 CalcBoundingBox(x2, y2);
64 }
65
66 // ----------------------------------------------------------------------------
67 // line/polygons
68 // ----------------------------------------------------------------------------
69
70 void wxDCBase::DrawLines(const wxList *list, wxCoord xoffset, wxCoord yoffset)
71 {
72 int n = list->GetCount();
73 wxPoint *points = new wxPoint[n];
74
75 int i = 0;
76 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
77 {
78 wxPoint *point = (wxPoint *)node->GetData();
79 points[i].x = point->x;
80 points[i].y = point->y;
81 }
82
83 DoDrawLines(n, points, xoffset, yoffset);
84
85 delete [] points;
86 }
87
88
89 void wxDCBase::DrawPolygon(const wxList *list,
90 wxCoord xoffset, wxCoord yoffset,
91 int fillStyle)
92 {
93 int n = list->GetCount();
94 wxPoint *points = new wxPoint[n];
95
96 int i = 0;
97 for ( wxList::compatibility_iterator node = list->GetFirst(); node; node = node->GetNext(), i++ )
98 {
99 wxPoint *point = (wxPoint *)node->GetData();
100 points[i].x = point->x;
101 points[i].y = point->y;
102 }
103
104 DoDrawPolygon(n, points, xoffset, yoffset, fillStyle);
105
106 delete [] points;
107 }
108
109 // ----------------------------------------------------------------------------
110 // splines
111 // ----------------------------------------------------------------------------
112
113 #if wxUSE_SPLINES
114
115 // TODO: this API needs fixing (wxPointList, why (!const) "wxList *"?)
116 void wxDCBase::DrawSpline(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, wxCoord x3, wxCoord y3)
117 {
118 wxList point_list;
119
120 wxPoint *point1 = new wxPoint;
121 point1->x = x1; point1->y = y1;
122 point_list.Append((wxObject*)point1);
123
124 wxPoint *point2 = new wxPoint;
125 point2->x = x2; point2->y = y2;
126 point_list.Append((wxObject*)point2);
127
128 wxPoint *point3 = new wxPoint;
129 point3->x = x3; point3->y = y3;
130 point_list.Append((wxObject*)point3);
131
132 DrawSpline(&point_list);
133
134 for( wxList::compatibility_iterator node = point_list.GetFirst(); node; node = node->GetNext() )
135 {
136 wxPoint *p = (wxPoint *)node->GetData();
137 delete p;
138 }
139 }
140
141 void wxDCBase::DrawSpline(int n, wxPoint points[])
142 {
143 wxList list;
144 for (int i =0; i < n; i++)
145 {
146 list.Append((wxObject*)&points[i]);
147 }
148
149 DrawSpline(&list);
150 }
151
152 // ----------------------------------- spline code ----------------------------------------
153
154 void wx_quadratic_spline(double a1, double b1, double a2, double b2,
155 double a3, double b3, double a4, double b4);
156 void wx_clear_stack();
157 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2, double *x3,
158 double *y3, double *x4, double *y4);
159 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3,
160 double x4, double y4);
161 static bool wx_spline_add_point(double x, double y);
162 static void wx_spline_draw_point_array(wxDCBase *dc);
163
164 wxList wx_spline_point_list;
165
166 #define half(z1, z2) ((z1+z2)/2.0)
167 #define THRESHOLD 5
168
169 /* iterative version */
170
171 void wx_quadratic_spline(double a1, double b1, double a2, double b2, double a3, double b3, double a4,
172 double b4)
173 {
174 register double xmid, ymid;
175 double x1, y1, x2, y2, x3, y3, x4, y4;
176
177 wx_clear_stack();
178 wx_spline_push(a1, b1, a2, b2, a3, b3, a4, b4);
179
180 while (wx_spline_pop(&x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4)) {
181 xmid = (double)half(x2, x3);
182 ymid = (double)half(y2, y3);
183 if (fabs(x1 - xmid) < THRESHOLD && fabs(y1 - ymid) < THRESHOLD &&
184 fabs(xmid - x4) < THRESHOLD && fabs(ymid - y4) < THRESHOLD) {
185 wx_spline_add_point( x1, y1 );
186 wx_spline_add_point( xmid, ymid );
187 } else {
188 wx_spline_push(xmid, ymid, (double)half(xmid, x3), (double)half(ymid, y3),
189 (double)half(x3, x4), (double)half(y3, y4), x4, y4);
190 wx_spline_push(x1, y1, (double)half(x1, x2), (double)half(y1, y2),
191 (double)half(x2, xmid), (double)half(y2, ymid), xmid, ymid);
192 }
193 }
194 }
195
196 /* utilities used by spline drawing routines */
197
198 typedef struct wx_spline_stack_struct {
199 double x1, y1, x2, y2, x3, y3, x4, y4;
200 } Stack;
201
202 #define SPLINE_STACK_DEPTH 20
203 static Stack wx_spline_stack[SPLINE_STACK_DEPTH];
204 static Stack *wx_stack_top;
205 static int wx_stack_count;
206
207 void wx_clear_stack()
208 {
209 wx_stack_top = wx_spline_stack;
210 wx_stack_count = 0;
211 }
212
213 void wx_spline_push(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
214 {
215 wx_stack_top->x1 = x1;
216 wx_stack_top->y1 = y1;
217 wx_stack_top->x2 = x2;
218 wx_stack_top->y2 = y2;
219 wx_stack_top->x3 = x3;
220 wx_stack_top->y3 = y3;
221 wx_stack_top->x4 = x4;
222 wx_stack_top->y4 = y4;
223 wx_stack_top++;
224 wx_stack_count++;
225 }
226
227 int wx_spline_pop(double *x1, double *y1, double *x2, double *y2,
228 double *x3, double *y3, double *x4, double *y4)
229 {
230 if (wx_stack_count == 0)
231 return (0);
232 wx_stack_top--;
233 wx_stack_count--;
234 *x1 = wx_stack_top->x1;
235 *y1 = wx_stack_top->y1;
236 *x2 = wx_stack_top->x2;
237 *y2 = wx_stack_top->y2;
238 *x3 = wx_stack_top->x3;
239 *y3 = wx_stack_top->y3;
240 *x4 = wx_stack_top->x4;
241 *y4 = wx_stack_top->y4;
242 return (1);
243 }
244
245 static bool wx_spline_add_point(double x, double y)
246 {
247 wxPoint *point = new wxPoint ;
248 point->x = (int) x;
249 point->y = (int) y;
250 wx_spline_point_list.Append((wxObject*)point);
251 return TRUE;
252 }
253
254 static void wx_spline_draw_point_array(wxDCBase *dc)
255 {
256 dc->DrawLines(&wx_spline_point_list, 0, 0 );
257 wxList::compatibility_iterator node = wx_spline_point_list.GetFirst();
258 while (node)
259 {
260 wxPoint *point = (wxPoint *)node->GetData();
261 delete point;
262 wx_spline_point_list.Erase(node);
263 node = wx_spline_point_list.GetFirst();
264 }
265 }
266
267 void wxDCBase::DoDrawSpline( wxList *points )
268 {
269 wxCHECK_RET( Ok(), wxT("invalid window dc") );
270
271 wxPoint *p;
272 double cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4;
273 double x1, y1, x2, y2;
274
275 wxList::compatibility_iterator node = points->GetFirst();
276 p = (wxPoint *)node->GetData();
277
278 x1 = p->x;
279 y1 = p->y;
280
281 node = node->GetNext();
282 p = (wxPoint *)node->GetData();
283
284 x2 = p->x;
285 y2 = p->y;
286 cx1 = (double)((x1 + x2) / 2);
287 cy1 = (double)((y1 + y2) / 2);
288 cx2 = (double)((cx1 + x2) / 2);
289 cy2 = (double)((cy1 + y2) / 2);
290
291 wx_spline_add_point(x1, y1);
292
293 while ((node = node->GetNext()))
294 {
295 p = (wxPoint *)node->GetData();
296 x1 = x2;
297 y1 = y2;
298 x2 = p->x;
299 y2 = p->y;
300 cx4 = (double)(x1 + x2) / 2;
301 cy4 = (double)(y1 + y2) / 2;
302 cx3 = (double)(x1 + cx4) / 2;
303 cy3 = (double)(y1 + cy4) / 2;
304
305 wx_quadratic_spline(cx1, cy1, cx2, cy2, cx3, cy3, cx4, cy4);
306
307 cx1 = cx4;
308 cy1 = cy4;
309 cx2 = (double)(cx1 + x2) / 2;
310 cy2 = (double)(cy1 + y2) / 2;
311 }
312
313 wx_spline_add_point( cx1, cy1 );
314 wx_spline_add_point( x2, y2 );
315
316 wx_spline_draw_point_array( this );
317 }
318
319 #endif // wxUSE_SPLINES
320
321 // ----------------------------------------------------------------------------
322 // enhanced text drawing
323 // ----------------------------------------------------------------------------
324
325 void wxDCBase::GetMultiLineTextExtent(const wxString& text,
326 wxCoord *x,
327 wxCoord *y,
328 wxCoord *h,
329 wxFont *font)
330 {
331 wxCoord widthTextMax = 0, widthLine,
332 heightTextTotal = 0, heightLineDefault = 0, heightLine = 0;
333
334 wxString curLine;
335 for ( const wxChar *pc = text; ; pc++ )
336 {
337 if ( *pc == _T('\n') || *pc == _T('\0') )
338 {
339 if ( curLine.empty() )
340 {
341 // we can't use GetTextExtent - it will return 0 for both width
342 // and height and an empty line should count in height
343 // calculation
344
345 // assume that this line has the same height as the previous
346 // one
347 if ( !heightLineDefault )
348 heightLineDefault = heightLine;
349
350 if ( !heightLineDefault )
351 {
352 // but we don't know it yet - choose something reasonable
353 GetTextExtent(_T("W"), NULL, &heightLineDefault,
354 NULL, NULL, font);
355 }
356
357 heightTextTotal += heightLineDefault;
358 }
359 else
360 {
361 GetTextExtent(curLine, &widthLine, &heightLine,
362 NULL, NULL, font);
363 if ( widthLine > widthTextMax )
364 widthTextMax = widthLine;
365 heightTextTotal += heightLine;
366 }
367
368 if ( *pc == _T('\n') )
369 {
370 curLine.clear();
371 }
372 else
373 {
374 // the end of string
375 break;
376 }
377 }
378 else
379 {
380 curLine += *pc;
381 }
382 }
383
384 if ( x )
385 *x = widthTextMax;
386 if ( y )
387 *y = heightTextTotal;
388 if ( h )
389 *h = heightLine;
390 }
391
392 void wxDCBase::DrawLabel(const wxString& text,
393 const wxBitmap& bitmap,
394 const wxRect& rect,
395 int alignment,
396 int indexAccel,
397 wxRect *rectBounding)
398 {
399 // find the text position
400 wxCoord widthText, heightText, heightLine;
401 GetMultiLineTextExtent(text, &widthText, &heightText, &heightLine);
402
403 wxCoord width, height;
404 if ( bitmap.Ok() )
405 {
406 width = widthText + bitmap.GetWidth();
407 height = bitmap.GetHeight();
408 }
409 else // no bitmap
410 {
411 width = widthText;
412 height = heightText;
413 }
414
415 wxCoord x, y;
416 if ( alignment & wxALIGN_RIGHT )
417 {
418 x = rect.GetRight() - width;
419 }
420 else if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
421 {
422 x = (rect.GetLeft() + rect.GetRight() + 1 - width) / 2;
423 }
424 else // alignment & wxALIGN_LEFT
425 {
426 x = rect.GetLeft();
427 }
428
429 if ( alignment & wxALIGN_BOTTOM )
430 {
431 y = rect.GetBottom() - height;
432 }
433 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
434 {
435 y = (rect.GetTop() + rect.GetBottom() + 1 - height) / 2;
436 }
437 else // alignment & wxALIGN_TOP
438 {
439 y = rect.GetTop();
440 }
441
442 // draw the bitmap first
443 wxCoord x0 = x,
444 y0 = y,
445 width0 = width;
446 if ( bitmap.Ok() )
447 {
448 DrawBitmap(bitmap, x, y, TRUE /* use mask */);
449
450 wxCoord offset = bitmap.GetWidth() + 4;
451 x += offset;
452 width -= offset;
453
454 y += (height - heightText) / 2;
455 }
456
457 // we will draw the underscore under the accel char later
458 wxCoord startUnderscore = 0,
459 endUnderscore = 0,
460 yUnderscore = 0;
461
462 // split the string into lines and draw each of them separately
463 wxString curLine;
464 for ( const wxChar *pc = text; ; pc++ )
465 {
466 if ( *pc == _T('\n') || *pc == _T('\0') )
467 {
468 int xRealStart = x; // init it here to avoid compielr warnings
469
470 if ( !curLine.empty() )
471 {
472 // NB: can't test for !(alignment & wxALIGN_LEFT) because
473 // wxALIGN_LEFT is 0
474 if ( alignment & (wxALIGN_RIGHT | wxALIGN_CENTRE_HORIZONTAL) )
475 {
476 wxCoord widthLine;
477 GetTextExtent(curLine, &widthLine, NULL);
478
479 if ( alignment & wxALIGN_RIGHT )
480 {
481 xRealStart += width - widthLine;
482 }
483 else // if ( alignment & wxALIGN_CENTRE_HORIZONTAL )
484 {
485 xRealStart += (width - widthLine) / 2;
486 }
487 }
488 //else: left aligned, nothing to do
489
490 DrawText(curLine, xRealStart, y);
491 }
492
493 y += heightLine;
494
495 // do we have underscore in this line? we can check yUnderscore
496 // because it is set below to just y + heightLine if we do
497 if ( y == yUnderscore )
498 {
499 // adjust the horz positions to account for the shift
500 startUnderscore += xRealStart;
501 endUnderscore += xRealStart;
502 }
503
504 if ( *pc == _T('\0') )
505 break;
506
507 curLine.clear();
508 }
509 else // not end of line
510 {
511 if ( pc - text.c_str() == indexAccel )
512 {
513 // remeber to draw underscore here
514 GetTextExtent(curLine, &startUnderscore, NULL);
515 curLine += *pc;
516 GetTextExtent(curLine, &endUnderscore, NULL);
517
518 yUnderscore = y + heightLine;
519 }
520 else
521 {
522 curLine += *pc;
523 }
524 }
525 }
526
527 // draw the underscore if found
528 if ( startUnderscore != endUnderscore )
529 {
530 // it should be of the same colour as text
531 SetPen(wxPen(GetTextForeground(), 0, wxSOLID));
532
533 yUnderscore--;
534
535 DrawLine(startUnderscore, yUnderscore, endUnderscore, yUnderscore);
536 }
537
538 // return bounding rect if requested
539 if ( rectBounding )
540 {
541 *rectBounding = wxRect(x, y - heightText, widthText, heightText);
542 }
543
544 CalcBoundingBox(x0, y0);
545 CalcBoundingBox(x0 + width0, y0 + height);
546 }