]> git.saurik.com Git - wxWidgets.git/blob - src/univ/stdrend.cpp
Fix discrepancy between different ways of measuring text extents under Mac.
[wxWidgets.git] / src / univ / stdrend.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/stdrend.cpp
3 // Purpose: implementation of wxStdRenderer
4 // Author: Vadim Zeitlin
5 // Created: 2006-09-16
6 // RCS-ID: $Id$
7 // Copyright: (c) 2006 Vadim Zeitlin <vadim@wxwindows.org>
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // for compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/settings.h"
28 #include "wx/brush.h"
29 #include "wx/dc.h"
30 #include "wx/statusbr.h"
31 #include "wx/toplevel.h"
32 #endif //WX_PRECOMP
33
34 #include "wx/univ/stdrend.h"
35 #include "wx/univ/colschem.h"
36
37 // ----------------------------------------------------------------------------
38 // constants
39 // ----------------------------------------------------------------------------
40
41 static const int FRAME_TITLEBAR_HEIGHT = 18;
42 static const int FRAME_BUTTON_WIDTH = 16;
43 static const int FRAME_BUTTON_HEIGHT = 14;
44
45 // the margin between listbox item text and its rectangle
46 static const int ITEM_MARGIN = 1;
47
48 // ============================================================================
49 // wxStdRenderer implementation
50 // ============================================================================
51
52 // ----------------------------------------------------------------------------
53 // ctor
54 // ----------------------------------------------------------------------------
55
56 wxStdRenderer::wxStdRenderer(const wxColourScheme *scheme)
57 : m_scheme(scheme)
58 {
59 m_penBlack = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_DARK));
60 m_penDarkGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_OUT));
61 m_penLightGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_IN));
62 m_penHighlight = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_HIGHLIGHT));
63
64 m_titlebarFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
65 m_titlebarFont.SetWeight(wxFONTWEIGHT_BOLD);
66 }
67
68 // ----------------------------------------------------------------------------
69 // helper functions
70 // ----------------------------------------------------------------------------
71
72 void
73 wxStdRenderer::DrawSolidRect(wxDC& dc, const wxColour& col, const wxRect& rect)
74 {
75 wxBrush brush(col, wxSOLID);
76 dc.SetBrush(brush);
77 dc.SetPen(*wxTRANSPARENT_PEN);
78 dc.DrawRectangle(rect);
79 }
80
81 void wxStdRenderer::DrawRect(wxDC& dc, wxRect *rect, const wxPen& pen)
82 {
83 // draw
84 dc.SetPen(pen);
85 dc.SetBrush(*wxTRANSPARENT_BRUSH);
86 dc.DrawRectangle(*rect);
87
88 // adjust the rect
89 rect->Inflate(-1);
90 }
91
92 void wxStdRenderer::DrawShadedRect(wxDC& dc, wxRect *rect,
93 const wxPen& pen1, const wxPen& pen2)
94 {
95 // draw the rectangle
96 dc.SetPen(pen1);
97 dc.DrawLine(rect->GetLeft(), rect->GetTop(),
98 rect->GetLeft(), rect->GetBottom());
99 dc.DrawLine(rect->GetLeft() + 1, rect->GetTop(),
100 rect->GetRight(), rect->GetTop());
101 dc.SetPen(pen2);
102 dc.DrawLine(rect->GetRight(), rect->GetTop(),
103 rect->GetRight(), rect->GetBottom());
104 dc.DrawLine(rect->GetLeft(), rect->GetBottom(),
105 rect->GetRight() + 1, rect->GetBottom());
106
107 // adjust the rect
108 rect->Inflate(-1);
109 }
110
111 // ----------------------------------------------------------------------------
112 // translate various flags into corresponding renderer constants
113 // ----------------------------------------------------------------------------
114
115 /* static */
116 void wxStdRenderer::GetIndicatorsFromFlags(int flags,
117 IndicatorState& state,
118 IndicatorStatus& status)
119 {
120 if ( flags & wxCONTROL_SELECTED )
121 state = flags & wxCONTROL_DISABLED ? IndicatorState_SelectedDisabled
122 : IndicatorState_Selected;
123 else if ( flags & wxCONTROL_DISABLED )
124 state = IndicatorState_Disabled;
125 else if ( flags & wxCONTROL_PRESSED )
126 state = IndicatorState_Pressed;
127 else
128 state = IndicatorState_Normal;
129
130 status = flags & wxCONTROL_CHECKED ? IndicatorStatus_Checked
131 : flags & wxCONTROL_UNDETERMINED
132 ? IndicatorStatus_Undetermined
133 : IndicatorStatus_Unchecked;
134 }
135
136 /* static */
137 wxStdRenderer::ArrowDirection wxStdRenderer::GetArrowDirection(wxDirection dir)
138 {
139 switch ( dir )
140 {
141 case wxLEFT:
142 return Arrow_Left;
143
144 case wxRIGHT:
145 return Arrow_Right;
146
147 case wxUP:
148 return Arrow_Up;
149
150 case wxDOWN:
151 return Arrow_Down;
152
153 default:
154 wxFAIL_MSG(wxT("unknown arrow direction"));
155 }
156
157 return Arrow_Max;
158 }
159
160 // ----------------------------------------------------------------------------
161 // background
162 // ----------------------------------------------------------------------------
163
164 void wxStdRenderer::DrawBackground(wxDC& dc,
165 const wxColour& col,
166 const wxRect& rect,
167 int WXUNUSED(flags),
168 wxWindow *window)
169 {
170 wxColour colBg;
171
172 if (col.Ok())
173 {
174 colBg = col;
175 }
176 else if (window)
177 {
178 colBg = m_scheme->GetBackground(window);
179 }
180 else
181 {
182 colBg = wxSCHEME_COLOUR(m_scheme, CONTROL);
183 }
184
185 DrawSolidRect(dc, colBg, rect);
186 }
187
188
189 void wxStdRenderer::DrawButtonSurface(wxDC& dc,
190 const wxColour& col,
191 const wxRect& rect,
192 int flags)
193 {
194 DrawBackground(dc, col, rect, flags);
195 }
196
197 // ----------------------------------------------------------------------------
198 // text
199 // ----------------------------------------------------------------------------
200
201 void
202 wxStdRenderer::DrawFocusRect(wxWindow* WXUNUSED(win), wxDC& dc, const wxRect& rect, int WXUNUSED(flags))
203 {
204 // draw the pixels manually because the "dots" in wxPen with wxDOT style
205 // may be short traits and not really dots
206 //
207 // note that to behave in the same manner as DrawRect(), we must exclude
208 // the bottom and right borders from the rectangle
209 wxCoord x1 = rect.GetLeft(),
210 y1 = rect.GetTop(),
211 x2 = rect.GetRight(),
212 y2 = rect.GetBottom();
213
214 dc.SetPen(m_penBlack);
215
216 // this seems to be closer than what Windows does than wxINVERT although
217 // I'm still not sure if it's correct
218 dc.SetLogicalFunction(wxAND_REVERSE);
219
220 wxCoord z;
221 for ( z = x1 + 1; z < x2; z += 2 )
222 dc.DrawPoint(z, rect.GetTop());
223
224 wxCoord shift = z == x2 ? 0 : 1;
225 for ( z = y1 + shift; z < y2; z += 2 )
226 dc.DrawPoint(x2, z);
227
228 shift = z == y2 ? 0 : 1;
229 for ( z = x2 - shift; z > x1; z -= 2 )
230 dc.DrawPoint(z, y2);
231
232 shift = z == x1 ? 0 : 1;
233 for ( z = y2 - shift; z > y1; z -= 2 )
234 dc.DrawPoint(x1, z);
235
236 dc.SetLogicalFunction(wxCOPY);
237 }
238
239 void wxStdRenderer::DrawLabel(wxDC& dc,
240 const wxString& label,
241 const wxRect& rect,
242 int flags,
243 int alignment,
244 int indexAccel,
245 wxRect *rectBounds)
246 {
247 DrawButtonLabel(dc, label, wxNullBitmap, rect, flags,
248 alignment, indexAccel, rectBounds);
249 }
250
251 void wxStdRenderer::DrawButtonLabel(wxDC& dc,
252 const wxString& label,
253 const wxBitmap& image,
254 const wxRect& rect,
255 int flags,
256 int alignment,
257 int indexAccel,
258 wxRect *rectBounds)
259 {
260 wxDCTextColourChanger clrChanger(dc);
261
262 wxRect rectLabel = rect;
263 if ( !label.empty() && (flags & wxCONTROL_DISABLED) )
264 {
265 if ( flags & wxCONTROL_PRESSED )
266 {
267 // shift the label if a button is pressed
268 rectLabel.Offset(1, 1);
269 }
270
271 // draw shadow of the text
272 clrChanger.Set(m_penHighlight.GetColour());
273 wxRect rectShadow = rect;
274 rectShadow.Offset(1, 1);
275 dc.DrawLabel(label, rectShadow, alignment, indexAccel);
276
277 // make the main label text grey
278 clrChanger.Set(m_penDarkGrey.GetColour());
279
280 if ( flags & wxCONTROL_FOCUSED )
281 {
282 // leave enough space for the focus rect
283 rectLabel.Inflate(-2);
284 }
285 }
286
287 dc.DrawLabel(label, image, rectLabel, alignment, indexAccel, rectBounds);
288
289 if ( !label.empty() && (flags & wxCONTROL_FOCUSED) )
290 {
291 rectLabel.Inflate(-1);
292
293 DrawFocusRect(NULL, dc, rectLabel);
294 }
295 }
296
297 // ----------------------------------------------------------------------------
298 // borders
299 // ----------------------------------------------------------------------------
300
301 /*
302 We implement standard-looking 3D borders which have the following appearance:
303
304 The raised border:
305
306 WWWWWWWWWWWWWWWWWWWWWWB
307 WHHHHHHHHHHHHHHHHHHHHGB
308 WH GB W = white (HILIGHT)
309 WH GB H = light grey (LIGHT)
310 WH GB G = dark grey (SHADOI)
311 WH GB B = black (DKSHADOI)
312 WH GB
313 WH GB
314 WGGGGGGGGGGGGGGGGGGGGGB
315 BBBBBBBBBBBBBBBBBBBBBBB
316
317 The sunken border looks like this:
318
319 GGGGGGGGGGGGGGGGGGGGGGW
320 GBBBBBBBBBBBBBBBBBBBBHW
321 GB HW
322 GB HW
323 GB HW
324 GB HW
325 GB HW
326 GB HW
327 GHHHHHHHHHHHHHHHHHHHHHW
328 WWWWWWWWWWWWWWWWWWWWWWW
329
330 The static border (used for the controls which don't get focus) is like
331 this:
332
333 GGGGGGGGGGGGGGGGGGGGGGW
334 G W
335 G W
336 G W
337 G W
338 G W
339 G W
340 G W
341 WWWWWWWWWWWWWWWWWWWWWWW
342
343 The most complicated is the double border which is a combination of special
344 "anti-sunken" border and an extra border inside it:
345
346 HHHHHHHHHHHHHHHHHHHHHHB
347 HWWWWWWWWWWWWWWWWWWWWGB
348 HWHHHHHHHHHHHHHHHHHHHGB
349 HWH HGB
350 HWH HGB
351 HWH HGB
352 HWH HGB
353 HWHHHHHHHHHHHHHHHHHHHGB
354 HGGGGGGGGGGGGGGGGGGGGGB
355 BBBBBBBBBBBBBBBBBBBBBBB
356
357 And the simple border is, well, simple:
358
359 BBBBBBBBBBBBBBBBBBBBBBB
360 B B
361 B B
362 B B
363 B B
364 B B
365 B B
366 B B
367 B B
368 BBBBBBBBBBBBBBBBBBBBBBB
369 */
370
371 void wxStdRenderer::DrawRaisedBorder(wxDC& dc, wxRect *rect)
372 {
373 DrawShadedRect(dc, rect, m_penHighlight, m_penBlack);
374 DrawShadedRect(dc, rect, m_penLightGrey, m_penDarkGrey);
375 }
376
377 void wxStdRenderer::DrawSunkenBorder(wxDC& dc, wxRect *rect)
378 {
379 DrawShadedRect(dc, rect, m_penDarkGrey, m_penHighlight);
380 DrawShadedRect(dc, rect, m_penBlack, m_penLightGrey);
381 }
382
383 void wxStdRenderer::DrawAntiSunkenBorder(wxDC& dc, wxRect *rect)
384 {
385 DrawShadedRect(dc, rect, m_penLightGrey, m_penBlack);
386 DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey);
387 }
388
389 void wxStdRenderer::DrawBoxBorder(wxDC& dc, wxRect *rect)
390 {
391 DrawShadedRect(dc, rect, m_penDarkGrey, m_penHighlight);
392 DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey);
393 }
394
395 void wxStdRenderer::DrawStaticBorder(wxDC& dc, wxRect *rect)
396 {
397 DrawShadedRect(dc, rect, m_penDarkGrey, m_penHighlight);
398 }
399
400 void wxStdRenderer::DrawExtraBorder(wxDC& dc, wxRect *rect)
401 {
402 DrawRect(dc, rect, m_penLightGrey);
403 }
404
405 void wxStdRenderer::DrawBorder(wxDC& dc,
406 wxBorder border,
407 const wxRect& rectTotal,
408 int WXUNUSED(flags),
409 wxRect *rectIn)
410 {
411 wxRect rect = rectTotal;
412
413 switch ( border )
414 {
415 case wxBORDER_SUNKEN:
416 case wxBORDER_THEME:
417 DrawSunkenBorder(dc, &rect);
418 break;
419
420 // wxBORDER_DOUBLE and wxBORDER_THEME are currently the same value.
421 #if 0
422 case wxBORDER_DOUBLE:
423 DrawAntiSunkenBorder(dc, &rect);
424 DrawExtraBorder(dc, &rect);
425 break;
426 #endif
427
428 case wxBORDER_STATIC:
429 DrawStaticBorder(dc, &rect);
430 break;
431
432 case wxBORDER_RAISED:
433 DrawRaisedBorder(dc, &rect);
434 break;
435
436 case wxBORDER_SIMPLE:
437 DrawRect(dc, &rect, m_penBlack);
438 break;
439
440 default:
441 wxFAIL_MSG(wxT("unknown border type"));
442 // fall through
443
444 case wxBORDER_DEFAULT:
445 case wxBORDER_NONE:
446 break;
447 }
448
449 if ( rectIn )
450 *rectIn = rect;
451 }
452
453 wxRect wxStdRenderer::GetBorderDimensions(wxBorder border) const
454 {
455 wxCoord width;
456 switch ( border )
457 {
458 case wxBORDER_SIMPLE:
459 case wxBORDER_STATIC:
460 width = 1;
461 break;
462
463 case wxBORDER_RAISED:
464 case wxBORDER_SUNKEN:
465 case wxBORDER_THEME:
466 width = 2;
467 break;
468 #if 0
469 case wxBORDER_DOUBLE:
470 width = 3;
471 break;
472 #endif
473 default:
474 wxFAIL_MSG(wxT("unknown border type"));
475 // fall through
476
477 case wxBORDER_DEFAULT:
478 case wxBORDER_NONE:
479 width = 0;
480 break;
481 }
482
483 wxRect rect;
484 rect.x =
485 rect.y =
486 rect.width =
487 rect.height = width;
488
489 return rect;
490 }
491
492 void wxStdRenderer::AdjustSize(wxSize *size, const wxWindow *window)
493 {
494 // take into account the border width
495 wxRect rectBorder = GetBorderDimensions(window->GetBorder());
496 size->x += rectBorder.x + rectBorder.width;
497 size->y += rectBorder.y + rectBorder.height;
498 }
499
500 bool wxStdRenderer::AreScrollbarsInsideBorder() const
501 {
502 return false;
503 }
504
505 wxCoord wxStdRenderer::GetListboxItemHeight(wxCoord fontHeight)
506 {
507 return fontHeight + 2*ITEM_MARGIN;
508 }
509
510 void wxStdRenderer::DrawTextBorder(wxDC& dc,
511 wxBorder border,
512 const wxRect& rect,
513 int flags,
514 wxRect *rectIn)
515 {
516 DrawBorder(dc, border, rect, flags, rectIn);
517 }
518
519 // ----------------------------------------------------------------------------
520 // lines and boxes
521 // ----------------------------------------------------------------------------
522
523 void
524 wxStdRenderer::DrawHorizontalLine(wxDC& dc, wxCoord y, wxCoord x1, wxCoord x2)
525 {
526 dc.SetPen(m_penDarkGrey);
527 dc.DrawLine(x1, y, x2 + 1, y);
528
529 dc.SetPen(m_penHighlight);
530 y++;
531 dc.DrawLine(x1, y, x2 + 1, y);
532 }
533
534 void
535 wxStdRenderer::DrawVerticalLine(wxDC& dc, wxCoord x, wxCoord y1, wxCoord y2)
536 {
537 dc.SetPen(m_penDarkGrey);
538 dc.DrawLine(x, y1, x, y2 + 1);
539
540 dc.SetPen(m_penHighlight);
541 x++;
542 dc.DrawLine(x, y1, x, y2 + 1);
543 }
544
545 void wxStdRenderer::DrawFrameWithoutLabel(wxDC& dc,
546 const wxRect& rectFrame,
547 const wxRect& rectLabel)
548 {
549 // draw left, bottom and right lines entirely
550 DrawVerticalLine(dc, rectFrame.GetLeft(),
551 rectFrame.GetTop(), rectFrame.GetBottom() - 2);
552 DrawHorizontalLine(dc, rectFrame.GetBottom() - 1,
553 rectFrame.GetLeft(), rectFrame.GetRight());
554 DrawVerticalLine(dc, rectFrame.GetRight() - 1,
555 rectFrame.GetTop(), rectFrame.GetBottom() - 1);
556
557 // and 2 parts of the top line
558 DrawHorizontalLine(dc, rectFrame.GetTop(),
559 rectFrame.GetLeft() + 1, rectLabel.GetLeft());
560 DrawHorizontalLine(dc, rectFrame.GetTop(),
561 rectLabel.GetRight(), rectFrame.GetRight() - 2);
562 }
563
564 void wxStdRenderer::DrawFrameWithLabel(wxDC& dc,
565 const wxString& label,
566 const wxRect& rectFrame,
567 const wxRect& rectText,
568 int flags,
569 int alignment,
570 int indexAccel)
571 {
572 wxRect rectLabel;
573 DrawLabel(dc, label, rectText, flags, alignment, indexAccel, &rectLabel);
574
575 DrawFrameWithoutLabel(dc, rectFrame, rectLabel);
576 }
577
578 void wxStdRenderer::DrawFrame(wxDC& dc,
579 const wxString& label,
580 const wxRect& rect,
581 int flags,
582 int alignment,
583 int indexAccel)
584 {
585 wxCoord height = 0; // of the label
586 wxRect rectFrame = rect;
587 if ( !label.empty() )
588 {
589 // the text should touch the top border of the rect, so the frame
590 // itself should be lower
591 dc.GetTextExtent(label, NULL, &height);
592 rectFrame.y += height / 2;
593 rectFrame.height -= height / 2;
594
595 // we have to draw each part of the frame individually as we can't
596 // erase the background beyond the label as it might contain some
597 // pixmap already, so drawing everything and then overwriting part of
598 // the frame with label doesn't work
599
600 // TODO: the +5 shouldn't be hard coded
601 wxRect rectText;
602 rectText.x = rectFrame.x + 5;
603 rectText.y = rect.y;
604 rectText.width = rectFrame.width - 7; // +2 border width
605 rectText.height = height;
606
607 DrawFrameWithLabel(dc, label, rectFrame, rectText, flags,
608 alignment, indexAccel);
609 }
610 else // no label
611 {
612 DrawBoxBorder(dc, &rectFrame);
613 }
614 }
615
616 void wxStdRenderer::DrawItem(wxDC& dc,
617 const wxString& label,
618 const wxRect& rect,
619 int flags)
620 {
621 wxDCTextColourChanger colChanger(dc);
622
623 if ( flags & wxCONTROL_SELECTED )
624 {
625 colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
626
627 const wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
628 dc.SetBrush(colBg);
629 dc.SetPen(colBg);
630 dc.DrawRectangle(rect);
631 }
632
633 // horizontal adjustment is arbitrary
634 wxRect rectText = rect;
635 rectText.Deflate(2, ITEM_MARGIN);
636 dc.DrawLabel(label, wxNullBitmap, rectText);
637
638 if ( flags & wxCONTROL_FOCUSED )
639 {
640 DrawFocusRect(NULL, dc, rect, flags);
641 }
642 }
643
644 void wxStdRenderer::DrawCheckItemBitmap(wxDC& dc,
645 const wxBitmap& bitmap,
646 const wxRect& rect,
647 int flags)
648 {
649 DrawCheckButton(dc, wxEmptyString, bitmap, rect, flags);
650 }
651
652 void wxStdRenderer::DrawCheckItem(wxDC& dc,
653 const wxString& label,
654 const wxBitmap& bitmap,
655 const wxRect& rect,
656 int flags)
657 {
658 wxRect rectBitmap = rect;
659 rectBitmap.width = GetCheckBitmapSize().x;
660 DrawCheckItemBitmap(dc, bitmap, rectBitmap, flags);
661
662 wxRect rectLabel = rect;
663 wxCoord shift = rectBitmap.width + 2*GetCheckItemMargin();
664 rectLabel.x += shift;
665 rectLabel.width -= shift;
666 DrawItem(dc, label, rectLabel, flags);
667 }
668
669 // ----------------------------------------------------------------------------
670 // check and radio bitmaps
671 // ----------------------------------------------------------------------------
672
673 void wxStdRenderer::DrawCheckButton(wxDC& dc,
674 const wxString& label,
675 const wxBitmap& bitmap,
676 const wxRect& rect,
677 int flags,
678 wxAlignment align,
679 int indexAccel)
680 {
681 if (bitmap.Ok())
682 DrawCheckOrRadioButton(dc, label, bitmap, rect, flags, align, indexAccel);
683 else
684 DrawCheckOrRadioButton(dc, label, GetCheckBitmap(flags), rect, flags, align, indexAccel);
685 }
686
687 void wxStdRenderer::DrawRadioButton(wxDC& dc,
688 const wxString& label,
689 const wxBitmap& bitmap,
690 const wxRect& rect,
691 int flags,
692 wxAlignment align,
693 int indexAccel)
694 {
695 if (bitmap.Ok())
696 DrawCheckOrRadioButton(dc, label, bitmap, rect, flags, align, indexAccel);
697 else
698 DrawCheckOrRadioButton(dc, label, GetRadioBitmap(flags), rect, flags, align, indexAccel);
699
700 }
701
702 void wxStdRenderer::DrawCheckOrRadioButton(wxDC& dc,
703 const wxString& label,
704 const wxBitmap& bitmap,
705 const wxRect& rect,
706 int flags,
707 wxAlignment align,
708 int indexAccel)
709 {
710 // calculate the position of the bitmap and of the label
711 wxCoord heightBmp = bitmap.GetHeight();
712 wxCoord xBmp,
713 yBmp = rect.y + (rect.height - heightBmp) / 2;
714
715 wxRect rectLabel;
716 dc.GetMultiLineTextExtent(label, NULL, &rectLabel.height);
717 rectLabel.y = rect.y + (rect.height - rectLabel.height) / 2;
718
719 // align label vertically with the bitmap - looks nicer like this
720 rectLabel.y -= (rectLabel.height - heightBmp) % 2;
721
722 // calc horz position
723 if ( align == wxALIGN_RIGHT )
724 {
725 xBmp = rect.GetRight() - bitmap.GetWidth();
726 rectLabel.x = rect.x + 3;
727 rectLabel.SetRight(xBmp);
728 }
729 else // normal (checkbox to the left of the text) case
730 {
731 xBmp = rect.x;
732 rectLabel.x = xBmp + bitmap.GetWidth() + 5;
733 rectLabel.SetRight(rect.GetRight());
734 }
735
736 dc.DrawBitmap(bitmap, xBmp, yBmp, true /* use mask */);
737
738 DrawLabel(dc, label, rectLabel, flags,
739 wxALIGN_LEFT | wxALIGN_TOP, indexAccel);
740 }
741
742 #if wxUSE_TEXTCTRL
743
744 void wxStdRenderer::DrawTextLine(wxDC& dc,
745 const wxString& text,
746 const wxRect& rect,
747 int selStart,
748 int selEnd,
749 int flags)
750 {
751 if ( (selStart == -1) || !(flags & wxCONTROL_FOCUSED) )
752 {
753 // just draw it as is
754 dc.DrawText(text, rect.x, rect.y);
755 }
756 else // we have selection
757 {
758 wxCoord width,
759 x = rect.x;
760
761 // draw the part before selection
762 wxString s(text, (size_t)selStart);
763 if ( !s.empty() )
764 {
765 dc.DrawText(s, x, rect.y);
766
767 dc.GetTextExtent(s, &width, NULL);
768 x += width;
769 }
770
771 // draw the selection itself
772 s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
773 if ( !s.empty() )
774 {
775 wxColour colFg = dc.GetTextForeground(),
776 colBg = dc.GetTextBackground();
777 dc.SetTextForeground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
778 dc.SetTextBackground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT));
779 dc.SetBackgroundMode(wxSOLID);
780
781 dc.DrawText(s, x, rect.y);
782 dc.GetTextExtent(s, &width, NULL);
783 x += width;
784
785 dc.SetBackgroundMode(wxTRANSPARENT);
786 dc.SetTextBackground(colBg);
787 dc.SetTextForeground(colFg);
788 }
789
790 // draw the final part
791 s = text.c_str() + selEnd;
792 if ( !s.empty() )
793 {
794 dc.DrawText(s, x, rect.y);
795 }
796 }
797 }
798
799 void wxStdRenderer::DrawLineWrapMark(wxDC& WXUNUSED(dc),
800 const wxRect& WXUNUSED(rect))
801 {
802 // nothing by default
803 }
804
805 int wxStdRenderer::GetTextBorderWidth(const wxTextCtrl * WXUNUSED(text)) const
806 {
807 return 1;
808 }
809
810 wxRect
811 wxStdRenderer::GetTextTotalArea(const wxTextCtrl *text, const wxRect& rect) const
812 {
813 wxRect rectTotal = rect;
814 rectTotal.Inflate(GetTextBorderWidth(text));
815 return rectTotal;
816 }
817
818 wxRect wxStdRenderer::GetTextClientArea(const wxTextCtrl *text,
819 const wxRect& rect,
820 wxCoord *extraSpaceBeyond) const
821 {
822 wxRect rectText = rect;
823 rectText.Deflate(GetTextBorderWidth(text));
824
825 if ( extraSpaceBeyond )
826 *extraSpaceBeyond = 0;
827
828 return rectText;
829 }
830
831 #endif // wxUSE_TEXTCTRL
832
833 // ----------------------------------------------------------------------------
834 // scrollbars drawing
835 // ----------------------------------------------------------------------------
836
837 void wxStdRenderer::DrawScrollbarArrow(wxDC& dc,
838 wxDirection dir,
839 const wxRect& rect,
840 int flags)
841 {
842 DrawArrow(dc, dir, rect, flags);
843 }
844
845 void wxStdRenderer::DrawScrollCorner(wxDC& dc, const wxRect& rect)
846 {
847 DrawSolidRect(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), rect);
848 }
849
850 // ----------------------------------------------------------------------------
851 // status bar
852 // ----------------------------------------------------------------------------
853
854 #if wxUSE_STATUSBAR
855
856 wxSize wxStdRenderer::GetStatusBarBorders() const
857 {
858 // Rendered border may be different depending on field's style, we use
859 // the largest value so that any field certainly fits into the borders
860 // we return:
861 wxRect raised = GetBorderDimensions(wxBORDER_RAISED);
862 wxRect flat = GetBorderDimensions(wxBORDER_STATIC);
863 wxASSERT_MSG( raised.x == raised.width && raised.y == raised.height &&
864 flat.x == flat.width && flat.y == flat.height,
865 wxT("this code expects uniform borders, you must override GetStatusBarBorders") );
866
867 // take the larger of flat/raised values:
868 wxSize border(wxMax(raised.x, flat.x), wxMax(raised.y, flat.y));
869
870 return border;
871 }
872
873 wxCoord wxStdRenderer::GetStatusBarBorderBetweenFields() const
874 {
875 return 2;
876 }
877
878 wxSize wxStdRenderer::GetStatusBarFieldMargins() const
879 {
880 return wxSize(2, 2);
881 }
882
883 void wxStdRenderer::DrawStatusField(wxDC& dc,
884 const wxRect& rect,
885 const wxString& label,
886 int flags,
887 int style)
888 {
889 wxRect rectIn;
890
891 if ( style == wxSB_RAISED )
892 DrawBorder(dc, wxBORDER_RAISED, rect, flags, &rectIn);
893 else if ( style != wxSB_FLAT )
894 DrawBorder(dc, wxBORDER_STATIC, rect, flags, &rectIn);
895 else
896 rectIn = rect;
897
898 rectIn.Deflate(GetStatusBarFieldMargins());
899
900 wxDCClipper clipper(dc, rectIn);
901 DrawLabel(dc, label, rectIn, flags, wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
902 }
903
904 #endif // wxUSE_STATUSBAR
905
906 // ----------------------------------------------------------------------------
907 // top level windows
908 // ----------------------------------------------------------------------------
909
910 int wxStdRenderer::HitTestFrame(const wxRect& rect, const wxPoint& pt, int flags) const
911 {
912 wxRect client = GetFrameClientArea(rect, flags);
913
914 if ( client.Contains(pt) )
915 return wxHT_TOPLEVEL_CLIENT_AREA;
916
917 if ( flags & wxTOPLEVEL_TITLEBAR )
918 {
919 wxRect client = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
920
921 if ( flags & wxTOPLEVEL_ICON )
922 {
923 if ( wxRect(client.GetPosition(), GetFrameIconSize()).Contains(pt) )
924 return wxHT_TOPLEVEL_ICON;
925 }
926
927 wxRect btnRect(client.GetRight() - 2 - FRAME_BUTTON_WIDTH,
928 client.GetTop() + (FRAME_TITLEBAR_HEIGHT-FRAME_BUTTON_HEIGHT)/2,
929 FRAME_BUTTON_WIDTH, FRAME_BUTTON_HEIGHT);
930
931 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
932 {
933 if ( btnRect.Contains(pt) )
934 return wxHT_TOPLEVEL_BUTTON_CLOSE;
935 btnRect.x -= FRAME_BUTTON_WIDTH + 2;
936 }
937 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
938 {
939 if ( btnRect.Contains(pt) )
940 return wxHT_TOPLEVEL_BUTTON_MAXIMIZE;
941 btnRect.x -= FRAME_BUTTON_WIDTH;
942 }
943 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
944 {
945 if ( btnRect.Contains(pt) )
946 return wxHT_TOPLEVEL_BUTTON_RESTORE;
947 btnRect.x -= FRAME_BUTTON_WIDTH;
948 }
949 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
950 {
951 if ( btnRect.Contains(pt) )
952 return wxHT_TOPLEVEL_BUTTON_ICONIZE;
953 btnRect.x -= FRAME_BUTTON_WIDTH;
954 }
955 if ( flags & wxTOPLEVEL_BUTTON_HELP )
956 {
957 if ( btnRect.Contains(pt) )
958 return wxHT_TOPLEVEL_BUTTON_HELP;
959 btnRect.x -= FRAME_BUTTON_WIDTH;
960 }
961
962 if ( pt.y >= client.y && pt.y < client.y + FRAME_TITLEBAR_HEIGHT )
963 return wxHT_TOPLEVEL_TITLEBAR;
964 }
965
966 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
967 {
968 // we are certainly at one of borders, let's decide which one:
969
970 int border = 0;
971 // dirty trick, relies on the way wxHT_TOPLEVEL_XXX are defined!
972 if ( pt.x < client.x )
973 border |= wxHT_TOPLEVEL_BORDER_W;
974 else if ( pt.x >= client.width + client.x )
975 border |= wxHT_TOPLEVEL_BORDER_E;
976 if ( pt.y < client.y )
977 border |= wxHT_TOPLEVEL_BORDER_N;
978 else if ( pt.y >= client.height + client.y )
979 border |= wxHT_TOPLEVEL_BORDER_S;
980 return border;
981 }
982
983 return wxHT_NOWHERE;
984 }
985
986 void wxStdRenderer::DrawFrameTitleBar(wxDC& dc,
987 const wxRect& rect,
988 const wxString& title,
989 const wxIcon& icon,
990 int flags,
991 int specialButton,
992 int specialButtonFlags)
993 {
994 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
995 {
996 DrawFrameBorder(dc, rect, flags);
997 }
998 if ( flags & wxTOPLEVEL_TITLEBAR )
999 {
1000 DrawFrameBackground(dc, rect, flags);
1001 if ( flags & wxTOPLEVEL_ICON )
1002 DrawFrameIcon(dc, rect, icon, flags);
1003 DrawFrameTitle(dc, rect, title, flags);
1004
1005 wxRect client = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
1006 wxCoord x,y;
1007 x = client.GetRight() - 2 - FRAME_BUTTON_WIDTH;
1008 y = client.GetTop() + (FRAME_TITLEBAR_HEIGHT-FRAME_BUTTON_HEIGHT)/2;
1009
1010 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
1011 {
1012 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_CLOSE,
1013 (specialButton == wxTOPLEVEL_BUTTON_CLOSE) ?
1014 specialButtonFlags : 0);
1015 x -= FRAME_BUTTON_WIDTH + 2;
1016 }
1017 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
1018 {
1019 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_MAXIMIZE,
1020 (specialButton == wxTOPLEVEL_BUTTON_MAXIMIZE) ?
1021 specialButtonFlags : 0);
1022 x -= FRAME_BUTTON_WIDTH;
1023 }
1024 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
1025 {
1026 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_RESTORE,
1027 (specialButton == wxTOPLEVEL_BUTTON_RESTORE) ?
1028 specialButtonFlags : 0);
1029 x -= FRAME_BUTTON_WIDTH;
1030 }
1031 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
1032 {
1033 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_ICONIZE,
1034 (specialButton == wxTOPLEVEL_BUTTON_ICONIZE) ?
1035 specialButtonFlags : 0);
1036 x -= FRAME_BUTTON_WIDTH;
1037 }
1038 if ( flags & wxTOPLEVEL_BUTTON_HELP )
1039 {
1040 DrawFrameButton(dc, x, y, wxTOPLEVEL_BUTTON_HELP,
1041 (specialButton == wxTOPLEVEL_BUTTON_HELP) ?
1042 specialButtonFlags : 0);
1043 }
1044 }
1045 }
1046
1047 void wxStdRenderer::DrawFrameBorder(wxDC& dc, const wxRect& rect, int flags)
1048 {
1049 if ( !(flags & wxTOPLEVEL_BORDER) )
1050 return;
1051
1052 wxRect r(rect);
1053
1054 DrawAntiSunkenBorder(dc, &r);
1055 DrawExtraBorder(dc, &r);
1056 if ( flags & wxTOPLEVEL_RESIZEABLE )
1057 DrawExtraBorder(dc, &r);
1058 }
1059
1060 void wxStdRenderer::DrawFrameBackground(wxDC& dc, const wxRect& rect, int flags)
1061 {
1062 if ( !(flags & wxTOPLEVEL_TITLEBAR) )
1063 return;
1064
1065 wxColour col = m_scheme->Get(flags & wxTOPLEVEL_ACTIVE
1066 ? wxColourScheme::TITLEBAR_ACTIVE
1067 : wxColourScheme::TITLEBAR);
1068
1069 wxRect r = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
1070 r.height = FRAME_TITLEBAR_HEIGHT;
1071
1072 DrawBackground(dc, col, r);
1073 }
1074
1075 void wxStdRenderer::DrawFrameTitle(wxDC& dc,
1076 const wxRect& rect,
1077 const wxString& title,
1078 int flags)
1079 {
1080 wxColour col = m_scheme->Get(flags & wxTOPLEVEL_ACTIVE
1081 ? wxColourScheme::TITLEBAR_ACTIVE_TEXT
1082 : wxColourScheme::TITLEBAR_TEXT);
1083 dc.SetTextForeground(col);
1084
1085 wxRect r = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
1086 r.height = FRAME_TITLEBAR_HEIGHT;
1087 if ( flags & wxTOPLEVEL_ICON )
1088 {
1089 r.x += FRAME_TITLEBAR_HEIGHT;
1090 r.width -= FRAME_TITLEBAR_HEIGHT + 2;
1091 }
1092 else
1093 {
1094 r.x += 1;
1095 r.width -= 3;
1096 }
1097
1098 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
1099 r.width -= FRAME_BUTTON_WIDTH + 2;
1100 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
1101 r.width -= FRAME_BUTTON_WIDTH;
1102 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
1103 r.width -= FRAME_BUTTON_WIDTH;
1104 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
1105 r.width -= FRAME_BUTTON_WIDTH;
1106 if ( flags & wxTOPLEVEL_BUTTON_HELP )
1107 r.width -= FRAME_BUTTON_WIDTH;
1108
1109 dc.SetFont(m_titlebarFont);
1110
1111 wxString s;
1112 wxCoord textW;
1113 dc.GetTextExtent(title, &textW, NULL);
1114 if ( textW > r.width )
1115 {
1116 // text is too big, let's shorten it and add "..." after it:
1117 size_t len = title.length();
1118 wxCoord WSoFar, letterW;
1119
1120 dc.GetTextExtent(wxT("..."), &WSoFar, NULL);
1121 if ( WSoFar > r.width )
1122 {
1123 // not enough space to draw anything
1124 return;
1125 }
1126
1127 s.Alloc(len);
1128 for (size_t i = 0; i < len; i++)
1129 {
1130 dc.GetTextExtent(title[i], &letterW, NULL);
1131 if ( letterW + WSoFar > r.width )
1132 break;
1133 WSoFar += letterW;
1134 s << title[i];
1135 }
1136 s << wxT("...");
1137 }
1138 else // no need to truncate the title
1139 {
1140 s = title;
1141 }
1142
1143 dc.DrawLabel(s, wxNullBitmap, r, wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
1144 }
1145
1146 void wxStdRenderer::DrawFrameIcon(wxDC& dc,
1147 const wxRect& rect,
1148 const wxIcon& icon,
1149 int flags)
1150 {
1151 if ( icon.Ok() )
1152 {
1153 wxRect r = GetFrameClientArea(rect, flags & ~wxTOPLEVEL_TITLEBAR);
1154 dc.DrawIcon(icon, r.x, r.y);
1155 }
1156 }
1157
1158 void wxStdRenderer::DrawFrameButton(wxDC& dc,
1159 wxCoord x, wxCoord y,
1160 int button,
1161 int flags)
1162 {
1163 FrameButtonType idx;
1164 switch (button)
1165 {
1166 case wxTOPLEVEL_BUTTON_CLOSE: idx = FrameButton_Close; break;
1167 case wxTOPLEVEL_BUTTON_MAXIMIZE: idx = FrameButton_Maximize; break;
1168 case wxTOPLEVEL_BUTTON_ICONIZE: idx = FrameButton_Minimize; break;
1169 case wxTOPLEVEL_BUTTON_RESTORE: idx = FrameButton_Restore; break;
1170 case wxTOPLEVEL_BUTTON_HELP: idx = FrameButton_Help; break;
1171 default:
1172 wxFAIL_MSG(wxT("incorrect button specification"));
1173 return;
1174 }
1175
1176 wxBitmap bmp = GetFrameButtonBitmap(idx);
1177 if ( !bmp.Ok() )
1178 return;
1179
1180 wxRect rectBtn(x, y, FRAME_BUTTON_WIDTH, FRAME_BUTTON_HEIGHT);
1181 if ( flags & wxCONTROL_PRESSED )
1182 {
1183 DrawSunkenBorder(dc, &rectBtn);
1184
1185 rectBtn.Offset(1, 1);
1186 }
1187 else
1188 {
1189 DrawRaisedBorder(dc, &rectBtn);
1190 }
1191
1192 DrawBackground(dc, wxSCHEME_COLOUR(m_scheme, CONTROL), rectBtn);
1193
1194 wxRect rectBmp(0, 0, bmp.GetWidth(), bmp.GetHeight());
1195 dc.DrawBitmap(bmp, rectBmp.CentreIn(rectBtn).GetPosition(), true);
1196 }
1197
1198 int wxStdRenderer::GetFrameBorderWidth(int flags) const
1199 {
1200 return flags & wxTOPLEVEL_RESIZEABLE ? 4 : 3;
1201 }
1202
1203
1204 wxRect wxStdRenderer::GetFrameClientArea(const wxRect& rect, int flags) const
1205 {
1206 wxRect r(rect);
1207
1208 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
1209 {
1210 r.Inflate(-GetFrameBorderWidth(flags));
1211 }
1212
1213 if ( flags & wxTOPLEVEL_TITLEBAR )
1214 {
1215 r.y += FRAME_TITLEBAR_HEIGHT;
1216 r.height -= FRAME_TITLEBAR_HEIGHT;
1217 }
1218
1219 return r;
1220 }
1221
1222 wxSize
1223 wxStdRenderer::GetFrameTotalSize(const wxSize& clientSize, int flags) const
1224 {
1225 wxSize s(clientSize);
1226
1227 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
1228 {
1229 s.IncBy(2*GetFrameBorderWidth(flags));
1230 }
1231
1232 if ( flags & wxTOPLEVEL_TITLEBAR )
1233 s.y += FRAME_TITLEBAR_HEIGHT;
1234
1235 return s;
1236 }
1237
1238 wxSize wxStdRenderer::GetFrameMinSize(int flags) const
1239 {
1240 wxSize s;
1241
1242 if ( (flags & wxTOPLEVEL_BORDER) && !(flags & wxTOPLEVEL_MAXIMIZED) )
1243 {
1244 s.IncBy(2*GetFrameBorderWidth(flags));
1245 }
1246
1247 if ( flags & wxTOPLEVEL_TITLEBAR )
1248 {
1249 s.y += FRAME_TITLEBAR_HEIGHT;
1250
1251 if ( flags & wxTOPLEVEL_ICON )
1252 s.x += FRAME_TITLEBAR_HEIGHT + 2;
1253 if ( flags & wxTOPLEVEL_BUTTON_CLOSE )
1254 s.x += FRAME_BUTTON_WIDTH + 2;
1255 if ( flags & wxTOPLEVEL_BUTTON_MAXIMIZE )
1256 s.x += FRAME_BUTTON_WIDTH;
1257 if ( flags & wxTOPLEVEL_BUTTON_RESTORE )
1258 s.x += FRAME_BUTTON_WIDTH;
1259 if ( flags & wxTOPLEVEL_BUTTON_ICONIZE )
1260 s.x += FRAME_BUTTON_WIDTH;
1261 if ( flags & wxTOPLEVEL_BUTTON_HELP )
1262 s.x += FRAME_BUTTON_WIDTH;
1263 }
1264
1265 return s;
1266 }
1267
1268 wxSize wxStdRenderer::GetFrameIconSize() const
1269 {
1270 return wxSize(16, 16);
1271 }