refactored common parts of DrawItem() in the base class; implemented GetTextTotal...
[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 #endif //WX_PRECOMP
28
29 #include "wx/univ/stdrend.h"
30 #include "wx/univ/colschem.h"
31
32 // ============================================================================
33 // wxStdRenderer implementation
34 // ============================================================================
35
36 // ----------------------------------------------------------------------------
37 // ctor
38 // ----------------------------------------------------------------------------
39
40 wxStdRenderer::wxStdRenderer(const wxColourScheme *scheme)
41 : m_scheme(scheme)
42 {
43 m_penBlack = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_DARK));
44 m_penDarkGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_OUT));
45 m_penLightGrey = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_IN));
46 m_penHighlight = wxPen(wxSCHEME_COLOUR(scheme, SHADOW_HIGHLIGHT));
47 }
48
49 // ----------------------------------------------------------------------------
50 // helper functions
51 // ----------------------------------------------------------------------------
52
53 void
54 wxStdRenderer::DrawSolidRect(wxDC& dc, const wxColour& col, const wxRect& rect)
55 {
56 wxBrush brush(col, wxSOLID);
57 dc.SetBrush(brush);
58 dc.SetPen(*wxTRANSPARENT_PEN);
59 dc.DrawRectangle(rect);
60 }
61
62 void wxStdRenderer::DrawRect(wxDC& dc, wxRect *rect, const wxPen& pen)
63 {
64 // draw
65 dc.SetPen(pen);
66 dc.SetBrush(*wxTRANSPARENT_BRUSH);
67 dc.DrawRectangle(*rect);
68
69 // adjust the rect
70 rect->Inflate(-1);
71 }
72
73 void wxStdRenderer::DrawShadedRect(wxDC& dc, wxRect *rect,
74 const wxPen& pen1, const wxPen& pen2)
75 {
76 // draw the rectangle
77 dc.SetPen(pen1);
78 dc.DrawLine(rect->GetLeft(), rect->GetTop(),
79 rect->GetLeft(), rect->GetBottom());
80 dc.DrawLine(rect->GetLeft() + 1, rect->GetTop(),
81 rect->GetRight(), rect->GetTop());
82 dc.SetPen(pen2);
83 dc.DrawLine(rect->GetRight(), rect->GetTop(),
84 rect->GetRight(), rect->GetBottom());
85 dc.DrawLine(rect->GetLeft(), rect->GetBottom(),
86 rect->GetRight() + 1, rect->GetBottom());
87
88 // adjust the rect
89 rect->Inflate(-1);
90 }
91
92 // ----------------------------------------------------------------------------
93 // background
94 // ----------------------------------------------------------------------------
95
96 void wxStdRenderer::DrawBackground(wxDC& dc,
97 const wxColour& col,
98 const wxRect& rect,
99 int WXUNUSED(flags),
100 wxWindow *window)
101 {
102 wxColour colBg = col.Ok() ? col
103 : window ? m_scheme->GetBackground(window)
104 : wxSCHEME_COLOUR(m_scheme, CONTROL);
105
106 DrawSolidRect(dc, colBg, rect);
107 }
108
109
110 void wxStdRenderer::DrawButtonSurface(wxDC& dc,
111 const wxColour& col,
112 const wxRect& rect,
113 int flags)
114 {
115 DrawBackground(dc, col, rect, flags);
116 }
117
118 // ----------------------------------------------------------------------------
119 // text
120 // ----------------------------------------------------------------------------
121
122 void wxStdRenderer::DrawFocusRect(wxDC& dc, const wxRect& rect)
123 {
124 // draw the pixels manually because the "dots" in wxPen with wxDOT style
125 // may be short traits and not really dots
126 //
127 // note that to behave in the same manner as DrawRect(), we must exclude
128 // the bottom and right borders from the rectangle
129 wxCoord x1 = rect.GetLeft(),
130 y1 = rect.GetTop(),
131 x2 = rect.GetRight(),
132 y2 = rect.GetBottom();
133
134 dc.SetPen(m_penBlack);
135
136 // this seems to be closer than what Windows does than wxINVERT although
137 // I'm still not sure if it's correct
138 dc.SetLogicalFunction(wxAND_REVERSE);
139
140 wxCoord z;
141 for ( z = x1 + 1; z < x2; z += 2 )
142 dc.DrawPoint(z, rect.GetTop());
143
144 wxCoord shift = z == x2 ? 0 : 1;
145 for ( z = y1 + shift; z < y2; z += 2 )
146 dc.DrawPoint(x2, z);
147
148 shift = z == y2 ? 0 : 1;
149 for ( z = x2 - shift; z > x1; z -= 2 )
150 dc.DrawPoint(z, y2);
151
152 shift = z == x1 ? 0 : 1;
153 for ( z = y2 - shift; z > y1; z -= 2 )
154 dc.DrawPoint(x1, z);
155
156 dc.SetLogicalFunction(wxCOPY);
157 }
158
159 void wxStdRenderer::DrawLabel(wxDC& dc,
160 const wxString& label,
161 const wxRect& rect,
162 int flags,
163 int alignment,
164 int indexAccel,
165 wxRect *rectBounds)
166 {
167 DrawButtonLabel(dc, label, wxNullBitmap, rect, flags,
168 alignment, indexAccel, rectBounds);
169 }
170
171 void wxStdRenderer::DrawButtonLabel(wxDC& dc,
172 const wxString& label,
173 const wxBitmap& image,
174 const wxRect& rect,
175 int flags,
176 int alignment,
177 int indexAccel,
178 wxRect *rectBounds)
179 {
180 wxRect rectLabel = rect;
181 if ( !label.empty() && (flags & wxCONTROL_DISABLED) )
182 {
183 if ( flags & wxCONTROL_PRESSED )
184 {
185 // shift the label if a button is pressed
186 rectLabel.Offset(1, 1);
187 }
188
189 // draw shadow of the text
190 dc.SetTextForeground(m_penHighlight.GetColour());
191 wxRect rectShadow = rect;
192 rectShadow.Offset(1, 1);
193 dc.DrawLabel(label, rectShadow, alignment, indexAccel);
194
195 // make the main label text grey
196 dc.SetTextForeground(m_penDarkGrey.GetColour());
197
198 if ( flags & wxCONTROL_FOCUSED )
199 {
200 // leave enough space for the focus rect
201 rectLabel.Inflate(-2);
202 }
203 }
204
205 dc.DrawLabel(label, image, rectLabel, alignment, indexAccel, rectBounds);
206
207 if ( !label.empty() && (flags & wxCONTROL_FOCUSED) )
208 {
209 rectLabel.Inflate(-1);
210
211 DrawFocusRect(dc, rectLabel);
212 }
213 }
214
215 // ----------------------------------------------------------------------------
216 // borders
217 // ----------------------------------------------------------------------------
218
219 /*
220 We implement standard-looking 3D borders which have the following appearance:
221
222 The raised border:
223
224 WWWWWWWWWWWWWWWWWWWWWWB
225 WHHHHHHHHHHHHHHHHHHHHGB
226 WH GB W = white (HILIGHT)
227 WH GB H = light grey (LIGHT)
228 WH GB G = dark grey (SHADOI)
229 WH GB B = black (DKSHADOI)
230 WH GB
231 WH GB
232 WGGGGGGGGGGGGGGGGGGGGGB
233 BBBBBBBBBBBBBBBBBBBBBBB
234
235 The sunken border looks like this:
236
237 GGGGGGGGGGGGGGGGGGGGGGW
238 GBBBBBBBBBBBBBBBBBBBBHW
239 GB HW
240 GB HW
241 GB HW
242 GB HW
243 GB HW
244 GB HW
245 GHHHHHHHHHHHHHHHHHHHHHW
246 WWWWWWWWWWWWWWWWWWWWWWW
247
248 The static border (used for the controls which don't get focus) is like
249 this:
250
251 GGGGGGGGGGGGGGGGGGGGGGW
252 G W
253 G W
254 G W
255 G W
256 G W
257 G W
258 G W
259 WWWWWWWWWWWWWWWWWWWWWWW
260
261 The most complicated is the double border which is a combination of special
262 "anti-sunken" border and an extra border inside it:
263
264 HHHHHHHHHHHHHHHHHHHHHHB
265 HWWWWWWWWWWWWWWWWWWWWGB
266 HWHHHHHHHHHHHHHHHHHHHGB
267 HWH HGB
268 HWH HGB
269 HWH HGB
270 HWH HGB
271 HWHHHHHHHHHHHHHHHHHHHGB
272 HGGGGGGGGGGGGGGGGGGGGGB
273 BBBBBBBBBBBBBBBBBBBBBBB
274
275 And the simple border is, well, simple:
276
277 BBBBBBBBBBBBBBBBBBBBBBB
278 B B
279 B B
280 B B
281 B B
282 B B
283 B B
284 B B
285 B B
286 BBBBBBBBBBBBBBBBBBBBBBB
287 */
288
289 void wxStdRenderer::DrawRaisedBorder(wxDC& dc, wxRect *rect)
290 {
291 DrawShadedRect(dc, rect, m_penHighlight, m_penBlack);
292 DrawShadedRect(dc, rect, m_penLightGrey, m_penDarkGrey);
293 }
294
295 void wxStdRenderer::DrawSunkenBorder(wxDC& dc, wxRect *rect)
296 {
297 DrawShadedRect(dc, rect, m_penDarkGrey, m_penHighlight);
298 DrawShadedRect(dc, rect, m_penBlack, m_penLightGrey);
299 }
300
301 void wxStdRenderer::DrawAntiSunkenBorder(wxDC& dc, wxRect *rect)
302 {
303 DrawShadedRect(dc, rect, m_penLightGrey, m_penBlack);
304 DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey);
305 }
306
307 void wxStdRenderer::DrawFrameBorder(wxDC& dc, wxRect *rect)
308 {
309 DrawShadedRect(dc, rect, m_penDarkGrey, m_penHighlight);
310 DrawShadedRect(dc, rect, m_penHighlight, m_penDarkGrey);
311 }
312
313 void wxStdRenderer::DrawBorder(wxDC& dc,
314 wxBorder border,
315 const wxRect& rectTotal,
316 int WXUNUSED(flags),
317 wxRect *rectIn)
318 {
319 wxRect rect = rectTotal;
320
321 switch ( border )
322 {
323 case wxBORDER_SUNKEN:
324 DrawSunkenBorder(dc, &rect);
325 break;
326
327 case wxBORDER_DOUBLE:
328 DrawAntiSunkenBorder(dc, &rect);
329 DrawRect(dc, &rect, m_penLightGrey);
330 break;
331
332 case wxBORDER_STATIC:
333 DrawShadedRect(dc, &rect, m_penDarkGrey, m_penHighlight);
334 break;
335
336 case wxBORDER_RAISED:
337 DrawRaisedBorder(dc, &rect);
338 break;
339
340 case wxBORDER_SIMPLE:
341 DrawRect(dc, &rect, m_penBlack);
342 break;
343
344 default:
345 wxFAIL_MSG(_T("unknown border type"));
346 // fall through
347
348 case wxBORDER_DEFAULT:
349 case wxBORDER_NONE:
350 break;
351 }
352
353 if ( rectIn )
354 *rectIn = rect;
355 }
356
357 wxRect wxStdRenderer::GetBorderDimensions(wxBorder border) const
358 {
359 wxCoord width;
360 switch ( border )
361 {
362 case wxBORDER_SIMPLE:
363 case wxBORDER_STATIC:
364 width = 1;
365 break;
366
367 case wxBORDER_RAISED:
368 case wxBORDER_SUNKEN:
369 width = 2;
370 break;
371
372 case wxBORDER_DOUBLE:
373 width = 3;
374 break;
375
376 default:
377 wxFAIL_MSG(_T("unknown border type"));
378 // fall through
379
380 case wxBORDER_DEFAULT:
381 case wxBORDER_NONE:
382 width = 0;
383 break;
384 }
385
386 wxRect rect;
387 rect.x =
388 rect.y =
389 rect.width =
390 rect.height = width;
391
392 return rect;
393 }
394
395 bool wxStdRenderer::AreScrollbarsInsideBorder() const
396 {
397 return false;
398 }
399
400 void wxStdRenderer::DrawTextBorder(wxDC& dc,
401 wxBorder border,
402 const wxRect& rect,
403 int flags,
404 wxRect *rectIn)
405 {
406 DrawBorder(dc, border, rect, flags, rectIn);
407 }
408
409 // ----------------------------------------------------------------------------
410 // lines and boxes
411 // ----------------------------------------------------------------------------
412
413 void
414 wxStdRenderer::DrawHorizontalLine(wxDC& dc, wxCoord y, wxCoord x1, wxCoord x2)
415 {
416 dc.SetPen(m_penDarkGrey);
417 dc.DrawLine(x1, y, x2 + 1, y);
418
419 dc.SetPen(m_penHighlight);
420 y++;
421 dc.DrawLine(x1, y, x2 + 1, y);
422 }
423
424 void
425 wxStdRenderer::DrawVerticalLine(wxDC& dc, wxCoord x, wxCoord y1, wxCoord y2)
426 {
427 dc.SetPen(m_penDarkGrey);
428 dc.DrawLine(x, y1, x, y2 + 1);
429
430 dc.SetPen(m_penHighlight);
431 x++;
432 dc.DrawLine(x, y1, x, y2 + 1);
433 }
434
435 void wxStdRenderer::DrawFrameWithoutLabel(wxDC& dc,
436 const wxRect& rectFrame,
437 const wxRect& rectLabel)
438 {
439 // draw left, bottom and right lines entirely
440 DrawVerticalLine(dc, rectFrame.GetLeft(),
441 rectFrame.GetTop(), rectFrame.GetBottom() - 2);
442 DrawHorizontalLine(dc, rectFrame.GetBottom() - 1,
443 rectFrame.GetLeft(), rectFrame.GetRight());
444 DrawVerticalLine(dc, rectFrame.GetRight() - 1,
445 rectFrame.GetTop(), rectFrame.GetBottom() - 1);
446
447 // and 2 parts of the top line
448 DrawHorizontalLine(dc, rectFrame.GetTop(),
449 rectFrame.GetLeft() + 1, rectLabel.GetLeft());
450 DrawHorizontalLine(dc, rectFrame.GetTop(),
451 rectLabel.GetRight(), rectFrame.GetRight() - 2);
452 }
453
454 void wxStdRenderer::DrawFrameWithLabel(wxDC& dc,
455 const wxString& label,
456 const wxRect& rectFrame,
457 const wxRect& rectText,
458 int flags,
459 int alignment,
460 int indexAccel)
461 {
462 wxRect rectLabel;
463 DrawLabel(dc, label, rectText, flags, alignment, indexAccel, &rectLabel);
464
465 DrawFrameWithoutLabel(dc, rectFrame, rectLabel);
466 }
467
468 void wxStdRenderer::DrawFrame(wxDC& dc,
469 const wxString& label,
470 const wxRect& rect,
471 int flags,
472 int alignment,
473 int indexAccel)
474 {
475 wxCoord height = 0; // of the label
476 wxRect rectFrame = rect;
477 if ( !label.empty() )
478 {
479 // the text should touch the top border of the rect, so the frame
480 // itself should be lower
481 dc.GetTextExtent(label, NULL, &height);
482 rectFrame.y += height / 2;
483 rectFrame.height -= height / 2;
484
485 // we have to draw each part of the frame individually as we can't
486 // erase the background beyond the label as it might contain some
487 // pixmap already, so drawing everything and then overwriting part of
488 // the frame with label doesn't work
489
490 // TODO: the +5 shouldn't be hard coded
491 wxRect rectText;
492 rectText.x = rectFrame.x + 5;
493 rectText.y = rect.y;
494 rectText.width = rectFrame.width - 7; // +2 border width
495 rectText.height = height;
496
497 DrawFrameWithLabel(dc, label, rectFrame, rectText, flags,
498 alignment, indexAccel);
499 }
500 else // no label
501 {
502 DrawFrameBorder(dc, &rectFrame);
503 }
504 }
505
506 void wxStdRenderer::DrawItem(wxDC& dc,
507 const wxString& label,
508 const wxRect& rect,
509 int flags)
510 {
511 wxDCTextColourChanger colChanger(dc);
512
513 if ( flags & wxCONTROL_SELECTED )
514 {
515 colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
516
517 const wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
518 dc.SetBrush(colBg);
519 dc.SetPen(colBg);
520 dc.DrawRectangle(rect);
521 }
522
523 wxRect rectText = rect;
524 rectText.x += 2;
525 rectText.width -= 2;
526 dc.DrawLabel(label, wxNullBitmap, rectText);
527
528 if ( flags & wxCONTROL_FOCUSED )
529 {
530 DrawFocusRect(dc, rect);
531 }
532 }
533
534 void wxStdRenderer::DrawCheckItemBitmap(wxDC& dc,
535 const wxBitmap& bitmap,
536 const wxRect& rect,
537 int flags)
538 {
539 DrawCheckButton(dc, wxEmptyString, bitmap, rect, flags);
540 }
541
542 void wxStdRenderer::DrawCheckItem(wxDC& dc,
543 const wxString& label,
544 const wxBitmap& bitmap,
545 const wxRect& rect,
546 int flags)
547 {
548 wxRect rectBitmap = rect;
549 rectBitmap.width = GetCheckBitmapSize().x;
550 DrawCheckItemBitmap(dc, bitmap, rectBitmap, flags);
551
552 wxRect rectLabel = rect;
553 wxCoord shift = rectBitmap.width + 2*GetCheckItemMargin();
554 rectLabel.x += shift;
555 rectLabel.width -= shift;
556 DrawItem(dc, label, rectLabel, flags);
557 }
558
559 // ----------------------------------------------------------------------------
560 // check and radio bitmaps
561 // ----------------------------------------------------------------------------
562
563 /* static */
564 void wxStdRenderer::GetIndicatorsFromFlags(int flags,
565 IndicatorState& state,
566 IndicatorStatus& status)
567 {
568 if ( flags & wxCONTROL_SELECTED )
569 state = flags & wxCONTROL_DISABLED ? IndicatorState_SelectedDisabled
570 : IndicatorState_Selected;
571 else if ( flags & wxCONTROL_DISABLED )
572 state = IndicatorState_Disabled;
573 else if ( flags & wxCONTROL_PRESSED )
574 state = IndicatorState_Pressed;
575 else
576 state = IndicatorState_Normal;
577
578 status = flags & wxCONTROL_CHECKED ? IndicatorStatus_Checked
579 : flags & wxCONTROL_UNDETERMINED
580 ? IndicatorStatus_Undetermined
581 : IndicatorStatus_Unchecked;
582 }
583
584 void wxStdRenderer::DrawCheckButton(wxDC& dc,
585 const wxString& label,
586 const wxBitmap& bitmap,
587 const wxRect& rect,
588 int flags,
589 wxAlignment align,
590 int indexAccel)
591 {
592 wxBitmap bmp(bitmap.Ok() ? bitmap : GetCheckBitmap(flags));
593
594 DrawCheckOrRadioButton(dc, label, bmp, rect, flags, align, indexAccel);
595 }
596
597 void wxStdRenderer::DrawRadioButton(wxDC& dc,
598 const wxString& label,
599 const wxBitmap& bitmap,
600 const wxRect& rect,
601 int flags,
602 wxAlignment align,
603 int indexAccel)
604 {
605 wxBitmap bmp(bitmap.Ok() ? bitmap : GetRadioBitmap(flags));
606
607 DrawCheckOrRadioButton(dc, label, bmp, rect, flags, align, indexAccel);
608 }
609
610 void wxStdRenderer::DrawCheckOrRadioButton(wxDC& dc,
611 const wxString& label,
612 const wxBitmap& bitmap,
613 const wxRect& rect,
614 int flags,
615 wxAlignment align,
616 int indexAccel)
617 {
618 // calculate the position of the bitmap and of the label
619 wxCoord heightBmp = bitmap.GetHeight();
620 wxCoord xBmp,
621 yBmp = rect.y + (rect.height - heightBmp) / 2;
622
623 wxRect rectLabel;
624 dc.GetMultiLineTextExtent(label, NULL, &rectLabel.height);
625 rectLabel.y = rect.y + (rect.height - rectLabel.height) / 2;
626
627 // align label vertically with the bitmap - looks nicer like this
628 rectLabel.y -= (rectLabel.height - heightBmp) % 2;
629
630 // calc horz position
631 if ( align == wxALIGN_RIGHT )
632 {
633 xBmp = rect.GetRight() - bitmap.GetWidth();
634 rectLabel.x = rect.x + 3;
635 rectLabel.SetRight(xBmp);
636 }
637 else // normal (checkbox to the left of the text) case
638 {
639 xBmp = rect.x;
640 rectLabel.x = xBmp + bitmap.GetWidth() + 5;
641 rectLabel.SetRight(rect.GetRight());
642 }
643
644 dc.DrawBitmap(bitmap, xBmp, yBmp, true /* use mask */);
645
646 DrawLabel(dc, label, rectLabel, flags,
647 wxALIGN_LEFT | wxALIGN_TOP, indexAccel);
648 }
649
650 #if wxUSE_TEXTCTRL
651
652 void wxStdRenderer::DrawTextLine(wxDC& dc,
653 const wxString& text,
654 const wxRect& rect,
655 int selStart,
656 int selEnd,
657 int flags)
658 {
659 if ( (selStart == -1) || !(flags & wxCONTROL_FOCUSED) )
660 {
661 // just draw it as is
662 dc.DrawText(text, rect.x, rect.y);
663 }
664 else // we have selection
665 {
666 wxCoord width,
667 x = rect.x;
668
669 // draw the part before selection
670 wxString s(text, (size_t)selStart);
671 if ( !s.empty() )
672 {
673 dc.DrawText(s, x, rect.y);
674
675 dc.GetTextExtent(s, &width, NULL);
676 x += width;
677 }
678
679 // draw the selection itself
680 s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
681 if ( !s.empty() )
682 {
683 wxColour colFg = dc.GetTextForeground(),
684 colBg = dc.GetTextBackground();
685 dc.SetTextForeground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
686 dc.SetTextBackground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT));
687 dc.SetBackgroundMode(wxSOLID);
688
689 dc.DrawText(s, x, rect.y);
690 dc.GetTextExtent(s, &width, NULL);
691 x += width;
692
693 dc.SetBackgroundMode(wxTRANSPARENT);
694 dc.SetTextBackground(colBg);
695 dc.SetTextForeground(colFg);
696 }
697
698 // draw the final part
699 s = text.c_str() + selEnd;
700 if ( !s.empty() )
701 {
702 dc.DrawText(s, x, rect.y);
703 }
704 }
705 }
706
707 void wxStdRenderer::DrawLineWrapMark(wxDC& WXUNUSED(dc),
708 const wxRect& WXUNUSED(rect))
709 {
710 // nothing by default
711 }
712
713 int wxStdRenderer::GetTextBorderWidth(const wxTextCtrl * WXUNUSED(text)) const
714 {
715 return 1;
716 }
717
718 wxRect
719 wxStdRenderer::GetTextTotalArea(const wxTextCtrl *text, const wxRect& rect) const
720 {
721 wxRect rectTotal = rect;
722 rectTotal.Inflate(GetTextBorderWidth(text));
723 return rectTotal;
724 }
725
726 wxRect wxStdRenderer::GetTextClientArea(const wxTextCtrl *text,
727 const wxRect& rect,
728 wxCoord *extraSpaceBeyond) const
729 {
730 wxRect rectText = rect;
731 rectText.Deflate(GetTextBorderWidth(text));
732
733 if ( extraSpaceBeyond )
734 *extraSpaceBeyond = 0;
735
736 return rectText;
737 }
738
739 #endif // wxUSE_TEXTCTRL
740
741 // ----------------------------------------------------------------------------
742 // scrollbars geometry
743 // ----------------------------------------------------------------------------
744
745 #if wxUSE_SCROLLBAR
746
747 /* static */
748 void wxStdRenderer::GetScrollBarThumbSize(wxCoord length,
749 int thumbPos,
750 int thumbSize,
751 int range,
752 wxCoord *thumbStart,
753 wxCoord *thumbEnd)
754 {
755 // the thumb can't be made less than this number of pixels
756 static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable
757
758 *thumbStart = (length*thumbPos) / range;
759 *thumbEnd = (length*(thumbPos + thumbSize)) / range;
760
761 if ( *thumbEnd - *thumbStart < thumbMinWidth )
762 {
763 // adjust the end if possible
764 if ( *thumbStart <= length - thumbMinWidth )
765 {
766 // yes, just make it wider
767 *thumbEnd = *thumbStart + thumbMinWidth;
768 }
769 else // it is at the bottom of the scrollbar
770 {
771 // so move it a bit up
772 *thumbStart = length - thumbMinWidth;
773 *thumbEnd = length;
774 }
775 }
776 }
777
778 wxRect wxStdRenderer::GetScrollbarRect(const wxScrollBar *scrollbar,
779 wxScrollBar::Element elem,
780 int thumbPos) const
781 {
782 if ( thumbPos == -1 )
783 {
784 thumbPos = scrollbar->GetThumbPosition();
785 }
786
787 const wxSize sizeArrow = GetScrollbarArrowSize();
788
789 wxSize sizeTotal = scrollbar->GetClientSize();
790 wxCoord *start, *width;
791 wxCoord length, arrow;
792 wxRect rect;
793 if ( scrollbar->IsVertical() )
794 {
795 rect.x = 0;
796 rect.width = sizeTotal.x;
797 length = sizeTotal.y;
798 start = &rect.y;
799 width = &rect.height;
800 arrow = sizeArrow.y;
801 }
802 else // horizontal
803 {
804 rect.y = 0;
805 rect.height = sizeTotal.y;
806 length = sizeTotal.x;
807 start = &rect.x;
808 width = &rect.width;
809 arrow = sizeArrow.x;
810 }
811
812 switch ( elem )
813 {
814 case wxScrollBar::Element_Arrow_Line_1:
815 *start = 0;
816 *width = arrow;
817 break;
818
819 case wxScrollBar::Element_Arrow_Line_2:
820 *start = length - arrow;
821 *width = arrow;
822 break;
823
824 case wxScrollBar::Element_Arrow_Page_1:
825 case wxScrollBar::Element_Arrow_Page_2:
826 // we don't have them at all
827 break;
828
829 case wxScrollBar::Element_Thumb:
830 case wxScrollBar::Element_Bar_1:
831 case wxScrollBar::Element_Bar_2:
832 // we need to calculate the thumb position - do it
833 {
834 length -= 2*arrow;
835 wxCoord thumbStart, thumbEnd;
836 int range = scrollbar->GetRange();
837 if ( !range )
838 {
839 thumbStart =
840 thumbEnd = 0;
841 }
842 else
843 {
844 GetScrollBarThumbSize(length,
845 thumbPos,
846 scrollbar->GetThumbSize(),
847 range,
848 &thumbStart,
849 &thumbEnd);
850 }
851
852 if ( elem == wxScrollBar::Element_Thumb )
853 {
854 *start = thumbStart;
855 *width = thumbEnd - thumbStart;
856 }
857 else if ( elem == wxScrollBar::Element_Bar_1 )
858 {
859 *start = 0;
860 *width = thumbStart;
861 }
862 else // elem == wxScrollBar::Element_Bar_2
863 {
864 *start = thumbEnd;
865 *width = length - thumbEnd;
866 }
867
868 // everything is relative to the start of the shaft so far
869 *start += arrow;
870 }
871 break;
872
873 case wxScrollBar::Element_Max:
874 default:
875 wxFAIL_MSG( _T("unknown scrollbar element") );
876 }
877
878 return rect;
879 }
880
881 wxCoord wxStdRenderer::GetScrollbarSize(const wxScrollBar *scrollbar)
882 {
883 const wxSize sizeArrowSB = GetScrollbarArrowSize();
884
885 wxCoord sizeArrow, sizeTotal;
886 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
887 {
888 sizeArrow = sizeArrowSB.y;
889 sizeTotal = scrollbar->GetSize().y;
890 }
891 else // horizontal
892 {
893 sizeArrow = sizeArrowSB.x;
894 sizeTotal = scrollbar->GetSize().x;
895 }
896
897 return sizeTotal - 2*sizeArrow;
898 }
899
900 wxHitTest
901 wxStdRenderer::HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const
902 {
903 // we only need to work with either x or y coord depending on the
904 // orientation, choose one (but still check the other one to verify if the
905 // mouse is in the window at all)
906 const wxSize sizeArrowSB = GetScrollbarArrowSize();
907
908 wxCoord coord, sizeArrow, sizeTotal;
909 wxSize size = scrollbar->GetSize();
910 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
911 {
912 if ( pt.x < 0 || pt.x > size.x )
913 return wxHT_NOWHERE;
914
915 coord = pt.y;
916 sizeArrow = sizeArrowSB.y;
917 sizeTotal = size.y;
918 }
919 else // horizontal
920 {
921 if ( pt.y < 0 || pt.y > size.y )
922 return wxHT_NOWHERE;
923
924 coord = pt.x;
925 sizeArrow = sizeArrowSB.x;
926 sizeTotal = size.x;
927 }
928
929 // test for the arrows first as it's faster
930 if ( coord < 0 || coord > sizeTotal )
931 {
932 return wxHT_NOWHERE;
933 }
934 else if ( coord < sizeArrow )
935 {
936 return wxHT_SCROLLBAR_ARROW_LINE_1;
937 }
938 else if ( coord > sizeTotal - sizeArrow )
939 {
940 return wxHT_SCROLLBAR_ARROW_LINE_2;
941 }
942 else
943 {
944 // calculate the thumb position in pixels
945 sizeTotal -= 2*sizeArrow;
946 wxCoord thumbStart, thumbEnd;
947 int range = scrollbar->GetRange();
948 if ( !range )
949 {
950 // clicking the scrollbar without range has no effect
951 return wxHT_NOWHERE;
952 }
953 else
954 {
955 GetScrollBarThumbSize(sizeTotal,
956 scrollbar->GetThumbPosition(),
957 scrollbar->GetThumbSize(),
958 range,
959 &thumbStart,
960 &thumbEnd);
961 }
962
963 // now compare with the thumb position
964 coord -= sizeArrow;
965 if ( coord < thumbStart )
966 return wxHT_SCROLLBAR_BAR_1;
967 else if ( coord > thumbEnd )
968 return wxHT_SCROLLBAR_BAR_2;
969 else
970 return wxHT_SCROLLBAR_THUMB;
971 }
972 }
973
974
975 wxCoord
976 wxStdRenderer::ScrollbarToPixel(const wxScrollBar *scrollbar, int thumbPos)
977 {
978 int range = scrollbar->GetRange();
979 if ( !range )
980 {
981 // the only valid position anyhow
982 return 0;
983 }
984
985 if ( thumbPos == -1 )
986 {
987 // by default use the current thumb position
988 thumbPos = scrollbar->GetThumbPosition();
989 }
990
991 const wxSize sizeArrow = GetScrollbarArrowSize();
992 return (thumbPos*GetScrollbarSize(scrollbar)) / range
993 + (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x);
994 }
995
996 int wxStdRenderer::PixelToScrollbar(const wxScrollBar *scrollbar, wxCoord coord)
997 {
998 const wxSize sizeArrow = GetScrollbarArrowSize();
999 return ((coord - (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x)) *
1000 scrollbar->GetRange() ) / GetScrollbarSize(scrollbar);
1001 }
1002
1003 #endif // wxUSE_SCROLLBAR
1004