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