refactor some existing themes methods in a new base wxStdRenderer class (before addin...
[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::DrawBorder(wxDC& dc,
308 wxBorder border,
309 const wxRect& rectTotal,
310 int WXUNUSED(flags),
311 wxRect *rectIn)
312 {
313 wxRect rect = rectTotal;
314
315 switch ( border )
316 {
317 case wxBORDER_SUNKEN:
318 DrawSunkenBorder(dc, &rect);
319 break;
320
321 case wxBORDER_DOUBLE:
322 DrawAntiSunkenBorder(dc, &rect);
323 DrawRect(dc, &rect, m_penLightGrey);
324 break;
325
326 case wxBORDER_STATIC:
327 DrawShadedRect(dc, &rect, m_penDarkGrey, m_penHighlight);
328 break;
329
330 case wxBORDER_RAISED:
331 DrawRaisedBorder(dc, &rect);
332 break;
333
334 case wxBORDER_SIMPLE:
335 DrawRect(dc, &rect, m_penBlack);
336 break;
337
338 default:
339 wxFAIL_MSG(_T("unknown border type"));
340 // fall through
341
342 case wxBORDER_DEFAULT:
343 case wxBORDER_NONE:
344 break;
345 }
346
347 if ( rectIn )
348 *rectIn = rect;
349 }
350
351 wxRect wxStdRenderer::GetBorderDimensions(wxBorder border) const
352 {
353 wxCoord width;
354 switch ( border )
355 {
356 case wxBORDER_SIMPLE:
357 case wxBORDER_STATIC:
358 width = 1;
359 break;
360
361 case wxBORDER_RAISED:
362 case wxBORDER_SUNKEN:
363 width = 2;
364 break;
365
366 case wxBORDER_DOUBLE:
367 width = 3;
368 break;
369
370 default:
371 wxFAIL_MSG(_T("unknown border type"));
372 // fall through
373
374 case wxBORDER_DEFAULT:
375 case wxBORDER_NONE:
376 width = 0;
377 break;
378 }
379
380 wxRect rect;
381 rect.x =
382 rect.y =
383 rect.width =
384 rect.height = width;
385
386 return rect;
387 }
388
389 bool wxStdRenderer::AreScrollbarsInsideBorder() const
390 {
391 return false;
392 }
393
394 void wxStdRenderer::DrawTextBorder(wxDC& dc,
395 wxBorder border,
396 const wxRect& rect,
397 int flags,
398 wxRect *rectIn)
399 {
400 DrawBorder(dc, border, rect, flags, rectIn);
401 }
402
403 // ----------------------------------------------------------------------------
404 // lines and boxes
405 // ----------------------------------------------------------------------------
406
407 void
408 wxStdRenderer::DrawHorizontalLine(wxDC& dc, wxCoord y, wxCoord x1, wxCoord x2)
409 {
410 dc.SetPen(m_penDarkGrey);
411 dc.DrawLine(x1, y, x2 + 1, y);
412
413 dc.SetPen(m_penHighlight);
414 y++;
415 dc.DrawLine(x1, y, x2 + 1, y);
416 }
417
418 void
419 wxStdRenderer::DrawVerticalLine(wxDC& dc, wxCoord x, wxCoord y1, wxCoord y2)
420 {
421 dc.SetPen(m_penDarkGrey);
422 dc.DrawLine(x, y1, x, y2 + 1);
423
424 dc.SetPen(m_penHighlight);
425 x++;
426 dc.DrawLine(x, y1, x, y2 + 1);
427 }
428
429 void wxStdRenderer::DrawFrameWithoutLabel(wxDC& dc,
430 const wxRect& rectFrame,
431 const wxRect& rectLabel)
432 {
433 // draw left, bottom and right lines entirely
434 DrawVerticalLine(dc, rectFrame.GetLeft(),
435 rectFrame.GetTop(), rectFrame.GetBottom() - 2);
436 DrawHorizontalLine(dc, rectFrame.GetBottom() - 1,
437 rectFrame.GetLeft(), rectFrame.GetRight());
438 DrawVerticalLine(dc, rectFrame.GetRight() - 1,
439 rectFrame.GetTop(), rectFrame.GetBottom() - 1);
440
441 // and 2 parts of the top line
442 DrawHorizontalLine(dc, rectFrame.GetTop(),
443 rectFrame.GetLeft() + 1, rectLabel.GetLeft());
444 DrawHorizontalLine(dc, rectFrame.GetTop(),
445 rectLabel.GetRight(), rectFrame.GetRight() - 2);
446 }
447
448 void wxStdRenderer::DrawFrameWithLabel(wxDC& dc,
449 const wxString& label,
450 const wxRect& rectFrame,
451 const wxRect& rectText,
452 int flags,
453 int alignment,
454 int indexAccel)
455 {
456 wxRect rectLabel;
457 DrawLabel(dc, label, rectText, flags, alignment, indexAccel, &rectLabel);
458
459 DrawFrameWithoutLabel(dc, rectFrame, rectLabel);
460 }
461
462 void wxStdRenderer::DrawFrame(wxDC& dc,
463 const wxString& label,
464 const wxRect& rect,
465 int flags,
466 int alignment,
467 int indexAccel)
468 {
469 wxCoord height = 0; // of the label
470 wxRect rectFrame = rect;
471 if ( !label.empty() )
472 {
473 // the text should touch the top border of the rect, so the frame
474 // itself should be lower
475 dc.GetTextExtent(label, NULL, &height);
476 rectFrame.y += height / 2;
477 rectFrame.height -= height / 2;
478
479 // we have to draw each part of the frame individually as we can't
480 // erase the background beyond the label as it might contain some
481 // pixmap already, so drawing everything and then overwriting part of
482 // the frame with label doesn't work
483
484 // TODO: the +5 shouldn't be hard coded
485 wxRect rectText;
486 rectText.x = rectFrame.x + 5;
487 rectText.y = rect.y;
488 rectText.width = rectFrame.width - 7; // +2 border width
489 rectText.height = height;
490
491 DrawFrameWithLabel(dc, label, rectFrame, rectText, flags,
492 alignment, indexAccel);
493 }
494 else // no label
495 {
496 // just draw the complete frame
497 DrawShadedRect(dc, &rectFrame, m_penDarkGrey, m_penHighlight);
498 DrawShadedRect(dc, &rectFrame, m_penHighlight, m_penDarkGrey);
499 }
500 }
501
502 void wxStdRenderer::DrawItem(wxDC& dc,
503 const wxString& label,
504 const wxRect& rect,
505 int flags)
506 {
507 wxDCTextColourChanger colChanger(dc);
508
509 if ( flags & wxCONTROL_SELECTED )
510 {
511 colChanger.Set(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
512
513 const wxColour colBg = wxSCHEME_COLOUR(m_scheme, HIGHLIGHT);
514 dc.SetBrush(colBg);
515 dc.SetPen(colBg);
516 dc.DrawRectangle(rect);
517 }
518
519 wxRect rectText = rect;
520 rectText.x += 2;
521 rectText.width -= 2;
522 dc.DrawLabel(label, wxNullBitmap, rectText);
523
524 if ( flags & wxCONTROL_FOCUSED )
525 {
526 DrawFocusRect(dc, rect);
527 }
528 }
529
530 // ----------------------------------------------------------------------------
531 // check and radio bitmaps
532 // ----------------------------------------------------------------------------
533
534 /* static */
535 void wxStdRenderer::GetIndicatorsFromFlags(int flags,
536 IndicatorState& state,
537 IndicatorStatus& status)
538 {
539 if ( flags & wxCONTROL_SELECTED )
540 state = flags & wxCONTROL_DISABLED ? IndicatorState_SelectedDisabled
541 : IndicatorState_Selected;
542 else if ( flags & wxCONTROL_DISABLED )
543 state = IndicatorState_Disabled;
544 else if ( flags & wxCONTROL_PRESSED )
545 state = IndicatorState_Pressed;
546 else
547 state = IndicatorState_Normal;
548
549 status = flags & wxCONTROL_CHECKED ? IndicatorStatus_Checked
550 : flags & wxCONTROL_UNDETERMINED
551 ? IndicatorStatus_Undetermined
552 : IndicatorStatus_Unchecked;
553 }
554
555 void wxStdRenderer::DrawCheckButton(wxDC& dc,
556 const wxString& label,
557 const wxBitmap& bitmap,
558 const wxRect& rect,
559 int flags,
560 wxAlignment align,
561 int indexAccel)
562 {
563 wxBitmap bmp(bitmap.Ok() ? bitmap : GetCheckBitmap(flags));
564
565 DrawCheckOrRadioButton(dc, label, bmp, rect, flags, align, indexAccel);
566 }
567
568 void wxStdRenderer::DrawRadioButton(wxDC& dc,
569 const wxString& label,
570 const wxBitmap& bitmap,
571 const wxRect& rect,
572 int flags,
573 wxAlignment align,
574 int indexAccel)
575 {
576 wxBitmap bmp(bitmap.Ok() ? bitmap : GetRadioBitmap(flags));
577
578 DrawCheckOrRadioButton(dc, label, bmp, rect, flags, align, indexAccel);
579 }
580
581 void wxStdRenderer::DrawCheckOrRadioButton(wxDC& dc,
582 const wxString& label,
583 const wxBitmap& bitmap,
584 const wxRect& rect,
585 int flags,
586 wxAlignment align,
587 int indexAccel)
588 {
589 // calculate the position of the bitmap and of the label
590 wxCoord heightBmp = bitmap.GetHeight();
591 wxCoord xBmp,
592 yBmp = rect.y + (rect.height - heightBmp) / 2;
593
594 wxRect rectLabel;
595 dc.GetMultiLineTextExtent(label, NULL, &rectLabel.height);
596 rectLabel.y = rect.y + (rect.height - rectLabel.height) / 2;
597
598 // align label vertically with the bitmap - looks nicer like this
599 rectLabel.y -= (rectLabel.height - heightBmp) % 2;
600
601 // calc horz position
602 if ( align == wxALIGN_RIGHT )
603 {
604 xBmp = rect.GetRight() - bitmap.GetWidth();
605 rectLabel.x = rect.x + 3;
606 rectLabel.SetRight(xBmp);
607 }
608 else // normal (checkbox to the left of the text) case
609 {
610 xBmp = rect.x;
611 rectLabel.x = xBmp + bitmap.GetWidth() + 5;
612 rectLabel.SetRight(rect.GetRight());
613 }
614
615 dc.DrawBitmap(bitmap, xBmp, yBmp, true /* use mask */);
616
617 DrawLabel(dc, label, rectLabel, flags,
618 wxALIGN_LEFT | wxALIGN_TOP, indexAccel);
619 }
620
621 #if wxUSE_TEXTCTRL
622
623 void wxStdRenderer::DrawTextLine(wxDC& dc,
624 const wxString& text,
625 const wxRect& rect,
626 int selStart,
627 int selEnd,
628 int flags)
629 {
630 if ( (selStart == -1) || !(flags & wxCONTROL_FOCUSED) )
631 {
632 // just draw it as is
633 dc.DrawText(text, rect.x, rect.y);
634 }
635 else // we have selection
636 {
637 wxCoord width,
638 x = rect.x;
639
640 // draw the part before selection
641 wxString s(text, (size_t)selStart);
642 if ( !s.empty() )
643 {
644 dc.DrawText(s, x, rect.y);
645
646 dc.GetTextExtent(s, &width, NULL);
647 x += width;
648 }
649
650 // draw the selection itself
651 s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
652 if ( !s.empty() )
653 {
654 wxColour colFg = dc.GetTextForeground(),
655 colBg = dc.GetTextBackground();
656 dc.SetTextForeground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT_TEXT));
657 dc.SetTextBackground(wxSCHEME_COLOUR(m_scheme, HIGHLIGHT));
658 dc.SetBackgroundMode(wxSOLID);
659
660 dc.DrawText(s, x, rect.y);
661 dc.GetTextExtent(s, &width, NULL);
662 x += width;
663
664 dc.SetBackgroundMode(wxTRANSPARENT);
665 dc.SetTextBackground(colBg);
666 dc.SetTextForeground(colFg);
667 }
668
669 // draw the final part
670 s = text.c_str() + selEnd;
671 if ( !s.empty() )
672 {
673 dc.DrawText(s, x, rect.y);
674 }
675 }
676 }
677
678 void wxStdRenderer::DrawLineWrapMark(wxDC& WXUNUSED(dc),
679 const wxRect& WXUNUSED(rect))
680 {
681 // nothing by default
682 }
683
684 #endif // wxUSE_TEXTCTRL
685
686 // ----------------------------------------------------------------------------
687 // scrollbars geometry
688 // ----------------------------------------------------------------------------
689
690 #if wxUSE_SCROLLBAR
691
692 /* static */
693 void wxStdRenderer::GetScrollBarThumbSize(wxCoord length,
694 int thumbPos,
695 int thumbSize,
696 int range,
697 wxCoord *thumbStart,
698 wxCoord *thumbEnd)
699 {
700 // the thumb can't be made less than this number of pixels
701 static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable
702
703 *thumbStart = (length*thumbPos) / range;
704 *thumbEnd = (length*(thumbPos + thumbSize)) / range;
705
706 if ( *thumbEnd - *thumbStart < thumbMinWidth )
707 {
708 // adjust the end if possible
709 if ( *thumbStart <= length - thumbMinWidth )
710 {
711 // yes, just make it wider
712 *thumbEnd = *thumbStart + thumbMinWidth;
713 }
714 else // it is at the bottom of the scrollbar
715 {
716 // so move it a bit up
717 *thumbStart = length - thumbMinWidth;
718 *thumbEnd = length;
719 }
720 }
721 }
722
723 wxRect wxStdRenderer::GetScrollbarRect(const wxScrollBar *scrollbar,
724 wxScrollBar::Element elem,
725 int thumbPos) const
726 {
727 if ( thumbPos == -1 )
728 {
729 thumbPos = scrollbar->GetThumbPosition();
730 }
731
732 const wxSize sizeArrow = GetScrollbarArrowSize();
733
734 wxSize sizeTotal = scrollbar->GetClientSize();
735 wxCoord *start, *width;
736 wxCoord length, arrow;
737 wxRect rect;
738 if ( scrollbar->IsVertical() )
739 {
740 rect.x = 0;
741 rect.width = sizeTotal.x;
742 length = sizeTotal.y;
743 start = &rect.y;
744 width = &rect.height;
745 arrow = sizeArrow.y;
746 }
747 else // horizontal
748 {
749 rect.y = 0;
750 rect.height = sizeTotal.y;
751 length = sizeTotal.x;
752 start = &rect.x;
753 width = &rect.width;
754 arrow = sizeArrow.x;
755 }
756
757 switch ( elem )
758 {
759 case wxScrollBar::Element_Arrow_Line_1:
760 *start = 0;
761 *width = arrow;
762 break;
763
764 case wxScrollBar::Element_Arrow_Line_2:
765 *start = length - arrow;
766 *width = arrow;
767 break;
768
769 case wxScrollBar::Element_Arrow_Page_1:
770 case wxScrollBar::Element_Arrow_Page_2:
771 // we don't have them at all
772 break;
773
774 case wxScrollBar::Element_Thumb:
775 case wxScrollBar::Element_Bar_1:
776 case wxScrollBar::Element_Bar_2:
777 // we need to calculate the thumb position - do it
778 {
779 length -= 2*arrow;
780 wxCoord thumbStart, thumbEnd;
781 int range = scrollbar->GetRange();
782 if ( !range )
783 {
784 thumbStart =
785 thumbEnd = 0;
786 }
787 else
788 {
789 GetScrollBarThumbSize(length,
790 thumbPos,
791 scrollbar->GetThumbSize(),
792 range,
793 &thumbStart,
794 &thumbEnd);
795 }
796
797 if ( elem == wxScrollBar::Element_Thumb )
798 {
799 *start = thumbStart;
800 *width = thumbEnd - thumbStart;
801 }
802 else if ( elem == wxScrollBar::Element_Bar_1 )
803 {
804 *start = 0;
805 *width = thumbStart;
806 }
807 else // elem == wxScrollBar::Element_Bar_2
808 {
809 *start = thumbEnd;
810 *width = length - thumbEnd;
811 }
812
813 // everything is relative to the start of the shaft so far
814 *start += arrow;
815 }
816 break;
817
818 case wxScrollBar::Element_Max:
819 default:
820 wxFAIL_MSG( _T("unknown scrollbar element") );
821 }
822
823 return rect;
824 }
825
826 wxCoord wxStdRenderer::GetScrollbarSize(const wxScrollBar *scrollbar)
827 {
828 const wxSize sizeArrowSB = GetScrollbarArrowSize();
829
830 wxCoord sizeArrow, sizeTotal;
831 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
832 {
833 sizeArrow = sizeArrowSB.y;
834 sizeTotal = scrollbar->GetSize().y;
835 }
836 else // horizontal
837 {
838 sizeArrow = sizeArrowSB.x;
839 sizeTotal = scrollbar->GetSize().x;
840 }
841
842 return sizeTotal - 2*sizeArrow;
843 }
844
845 wxHitTest
846 wxStdRenderer::HitTestScrollbar(const wxScrollBar *scrollbar, const wxPoint& pt) const
847 {
848 // we only need to work with either x or y coord depending on the
849 // orientation, choose one (but still check the other one to verify if the
850 // mouse is in the window at all)
851 const wxSize sizeArrowSB = GetScrollbarArrowSize();
852
853 wxCoord coord, sizeArrow, sizeTotal;
854 wxSize size = scrollbar->GetSize();
855 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
856 {
857 if ( pt.x < 0 || pt.x > size.x )
858 return wxHT_NOWHERE;
859
860 coord = pt.y;
861 sizeArrow = sizeArrowSB.y;
862 sizeTotal = size.y;
863 }
864 else // horizontal
865 {
866 if ( pt.y < 0 || pt.y > size.y )
867 return wxHT_NOWHERE;
868
869 coord = pt.x;
870 sizeArrow = sizeArrowSB.x;
871 sizeTotal = size.x;
872 }
873
874 // test for the arrows first as it's faster
875 if ( coord < 0 || coord > sizeTotal )
876 {
877 return wxHT_NOWHERE;
878 }
879 else if ( coord < sizeArrow )
880 {
881 return wxHT_SCROLLBAR_ARROW_LINE_1;
882 }
883 else if ( coord > sizeTotal - sizeArrow )
884 {
885 return wxHT_SCROLLBAR_ARROW_LINE_2;
886 }
887 else
888 {
889 // calculate the thumb position in pixels
890 sizeTotal -= 2*sizeArrow;
891 wxCoord thumbStart, thumbEnd;
892 int range = scrollbar->GetRange();
893 if ( !range )
894 {
895 // clicking the scrollbar without range has no effect
896 return wxHT_NOWHERE;
897 }
898 else
899 {
900 GetScrollBarThumbSize(sizeTotal,
901 scrollbar->GetThumbPosition(),
902 scrollbar->GetThumbSize(),
903 range,
904 &thumbStart,
905 &thumbEnd);
906 }
907
908 // now compare with the thumb position
909 coord -= sizeArrow;
910 if ( coord < thumbStart )
911 return wxHT_SCROLLBAR_BAR_1;
912 else if ( coord > thumbEnd )
913 return wxHT_SCROLLBAR_BAR_2;
914 else
915 return wxHT_SCROLLBAR_THUMB;
916 }
917 }
918
919
920 wxCoord
921 wxStdRenderer::ScrollbarToPixel(const wxScrollBar *scrollbar, int thumbPos)
922 {
923 int range = scrollbar->GetRange();
924 if ( !range )
925 {
926 // the only valid position anyhow
927 return 0;
928 }
929
930 if ( thumbPos == -1 )
931 {
932 // by default use the current thumb position
933 thumbPos = scrollbar->GetThumbPosition();
934 }
935
936 const wxSize sizeArrow = GetScrollbarArrowSize();
937 return (thumbPos*GetScrollbarSize(scrollbar)) / range
938 + (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x);
939 }
940
941 int wxStdRenderer::PixelToScrollbar(const wxScrollBar *scrollbar, wxCoord coord)
942 {
943 const wxSize sizeArrow = GetScrollbarArrowSize();
944 return ((coord - (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x)) *
945 scrollbar->GetRange() ) / GetScrollbarSize(scrollbar);
946 }
947
948 #endif // wxUSE_SCROLLBAR
949