1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/univ/ctrlrend.cpp
3 // Purpose: wxControlRenderer implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/control.h"
30 #include "wx/checklst.h"
31 #include "wx/listbox.h"
32 #include "wx/scrolbar.h"
40 #include "wx/univ/theme.h"
41 #include "wx/univ/renderer.h"
42 #include "wx/univ/colschem.h"
44 // ============================================================================
46 // ============================================================================
48 // ----------------------------------------------------------------------------
49 // wxRenderer: drawing helpers
50 // ----------------------------------------------------------------------------
52 void wxRenderer::StandardDrawFrame(wxDC
& dc
,
53 const wxRect
& rectFrame
,
54 const wxRect
& rectLabel
)
56 // draw left, bottom and right lines entirely
57 DrawVerticalLine(dc
, rectFrame
.GetLeft(),
58 rectFrame
.GetTop(), rectFrame
.GetBottom() - 2);
59 DrawHorizontalLine(dc
, rectFrame
.GetBottom() - 1,
60 rectFrame
.GetLeft(), rectFrame
.GetRight());
61 DrawVerticalLine(dc
, rectFrame
.GetRight() - 1,
62 rectFrame
.GetTop(), rectFrame
.GetBottom() - 1);
64 // and 2 parts of the top line
65 DrawHorizontalLine(dc
, rectFrame
.GetTop(),
66 rectFrame
.GetLeft() + 1, rectLabel
.GetLeft());
67 DrawHorizontalLine(dc
, rectFrame
.GetTop(),
68 rectLabel
.GetRight(), rectFrame
.GetRight() - 2);
72 void wxRenderer::StandardDrawTextLine(wxDC
& dc
,
75 int selStart
, int selEnd
,
78 if ( (selStart
== -1) || !(flags
& wxCONTROL_FOCUSED
) )
81 dc
.DrawText(text
, rect
.x
, rect
.y
);
83 else // we have selection
88 // draw the part before selection
89 wxString
s(text
, (size_t)selStart
);
92 dc
.DrawText(s
, x
, rect
.y
);
94 dc
.GetTextExtent(s
, &width
, NULL
);
98 // draw the selection itself
99 s
= wxString(text
.c_str() + selStart
, text
.c_str() + selEnd
);
102 wxColour colFg
= dc
.GetTextForeground(),
103 colBg
= dc
.GetTextBackground();
104 dc
.SetTextForeground(wxTHEME_COLOUR(HIGHLIGHT_TEXT
));
105 dc
.SetTextBackground(wxTHEME_COLOUR(HIGHLIGHT
));
106 dc
.SetBackgroundMode(wxSOLID
);
108 dc
.DrawText(s
, x
, rect
.y
);
109 dc
.GetTextExtent(s
, &width
, NULL
);
112 dc
.SetBackgroundMode(wxTRANSPARENT
);
113 dc
.SetTextBackground(colBg
);
114 dc
.SetTextForeground(colFg
);
117 // draw the final part
118 s
= text
.c_str() + selEnd
;
121 dc
.DrawText(s
, x
, rect
.y
);
126 // ----------------------------------------------------------------------------
127 // wxRenderer: scrollbar geometry
128 // ----------------------------------------------------------------------------
133 void wxRenderer::StandardScrollBarThumbSize(wxCoord length
,
140 // the thumb can't be made less than this number of pixels
141 static const wxCoord thumbMinWidth
= 8; // FIXME: should be configurable
143 *thumbStart
= (length
*thumbPos
) / range
;
144 *thumbEnd
= (length
*(thumbPos
+ thumbSize
)) / range
;
146 if ( *thumbEnd
- *thumbStart
< thumbMinWidth
)
148 // adjust the end if possible
149 if ( *thumbStart
<= length
- thumbMinWidth
)
151 // yes, just make it wider
152 *thumbEnd
= *thumbStart
+ thumbMinWidth
;
154 else // it is at the bottom of the scrollbar
156 // so move it a bit up
157 *thumbStart
= length
- thumbMinWidth
;
164 wxRect
wxRenderer::StandardGetScrollbarRect(const wxScrollBar
*scrollbar
,
165 wxScrollBar::Element elem
,
167 const wxSize
& sizeArrow
)
169 if ( thumbPos
== -1 )
171 thumbPos
= scrollbar
->GetThumbPosition();
174 wxSize sizeTotal
= scrollbar
->GetClientSize();
175 wxCoord
*start
, *width
;
176 wxCoord length
, arrow
;
178 if ( scrollbar
->IsVertical() )
181 rect
.width
= sizeTotal
.x
;
182 length
= sizeTotal
.y
;
184 width
= &rect
.height
;
190 rect
.height
= sizeTotal
.y
;
191 length
= sizeTotal
.x
;
199 case wxScrollBar::Element_Arrow_Line_1
:
204 case wxScrollBar::Element_Arrow_Line_2
:
205 *start
= length
- arrow
;
209 case wxScrollBar::Element_Arrow_Page_1
:
210 case wxScrollBar::Element_Arrow_Page_2
:
211 // we don't have them at all
214 case wxScrollBar::Element_Thumb
:
215 case wxScrollBar::Element_Bar_1
:
216 case wxScrollBar::Element_Bar_2
:
217 // we need to calculate the thumb position - do it
220 wxCoord thumbStart
, thumbEnd
;
221 int range
= scrollbar
->GetRange();
229 StandardScrollBarThumbSize(length
,
231 scrollbar
->GetThumbSize(),
237 if ( elem
== wxScrollBar::Element_Thumb
)
240 *width
= thumbEnd
- thumbStart
;
242 else if ( elem
== wxScrollBar::Element_Bar_1
)
247 else // elem == wxScrollBar::Element_Bar_2
250 *width
= length
- thumbEnd
;
253 // everything is relative to the start of the shaft so far
258 case wxScrollBar::Element_Max
:
260 wxFAIL_MSG( _T("unknown scrollbar element") );
267 wxCoord
wxRenderer::StandardScrollBarSize(const wxScrollBar
*scrollbar
,
268 const wxSize
& sizeArrowSB
)
270 wxCoord sizeArrow
, sizeTotal
;
271 if ( scrollbar
->GetWindowStyle() & wxVERTICAL
)
273 sizeArrow
= sizeArrowSB
.y
;
274 sizeTotal
= scrollbar
->GetSize().y
;
278 sizeArrow
= sizeArrowSB
.x
;
279 sizeTotal
= scrollbar
->GetSize().x
;
282 return sizeTotal
- 2*sizeArrow
;
286 wxCoord
wxRenderer::StandardScrollbarToPixel(const wxScrollBar
*scrollbar
,
288 const wxSize
& sizeArrow
)
290 int range
= scrollbar
->GetRange();
293 // the only valid position anyhow
297 if ( thumbPos
== -1 )
299 // by default use the current thumb position
300 thumbPos
= scrollbar
->GetThumbPosition();
303 return ( thumbPos
*StandardScrollBarSize(scrollbar
, sizeArrow
) ) / range
304 + (scrollbar
->IsVertical() ? sizeArrow
.y
: sizeArrow
.x
);
308 int wxRenderer::StandardPixelToScrollbar(const wxScrollBar
*scrollbar
,
310 const wxSize
& sizeArrow
)
312 return ( (coord
- (scrollbar
->IsVertical() ? sizeArrow
.y
: sizeArrow
.x
)) *
313 scrollbar
->GetRange() ) /
314 StandardScrollBarSize(scrollbar
, sizeArrow
);
318 wxHitTest
wxRenderer::StandardHitTestScrollbar(const wxScrollBar
*scrollbar
,
320 const wxSize
& sizeArrowSB
)
322 // we only need to work with either x or y coord depending on the
323 // orientation, choose one (but still check the other one to verify if the
324 // mouse is in the window at all)
325 wxCoord coord
, sizeArrow
, sizeTotal
;
326 wxSize size
= scrollbar
->GetSize();
327 if ( scrollbar
->GetWindowStyle() & wxVERTICAL
)
329 if ( pt
.x
< 0 || pt
.x
> size
.x
)
333 sizeArrow
= sizeArrowSB
.y
;
338 if ( pt
.y
< 0 || pt
.y
> size
.y
)
342 sizeArrow
= sizeArrowSB
.x
;
346 // test for the arrows first as it's faster
347 if ( coord
< 0 || coord
> sizeTotal
)
351 else if ( coord
< sizeArrow
)
353 return wxHT_SCROLLBAR_ARROW_LINE_1
;
355 else if ( coord
> sizeTotal
- sizeArrow
)
357 return wxHT_SCROLLBAR_ARROW_LINE_2
;
361 // calculate the thumb position in pixels
362 sizeTotal
-= 2*sizeArrow
;
363 wxCoord thumbStart
, thumbEnd
;
364 int range
= scrollbar
->GetRange();
367 // clicking the scrollbar without range has no effect
372 StandardScrollBarThumbSize(sizeTotal
,
373 scrollbar
->GetThumbPosition(),
374 scrollbar
->GetThumbSize(),
380 // now compare with the thumb position
382 if ( coord
< thumbStart
)
383 return wxHT_SCROLLBAR_BAR_1
;
384 else if ( coord
> thumbEnd
)
385 return wxHT_SCROLLBAR_BAR_2
;
387 return wxHT_SCROLLBAR_THUMB
;
391 #endif // wxUSE_SCROLLBAR
393 wxRenderer::~wxRenderer()
397 // ----------------------------------------------------------------------------
399 // ----------------------------------------------------------------------------
401 wxControlRenderer::wxControlRenderer(wxWindow
*window
,
403 wxRenderer
*renderer
)
407 m_renderer
= renderer
;
409 wxSize size
= m_window
->GetClientSize();
412 m_rect
.width
= size
.x
;
413 m_rect
.height
= size
.y
;
416 void wxControlRenderer::DrawLabel(const wxBitmap
& bitmap
,
417 wxCoord marginX
, wxCoord marginY
)
419 m_dc
.SetBackgroundMode(wxTRANSPARENT
);
420 m_dc
.SetFont(m_window
->GetFont());
421 m_dc
.SetTextForeground(m_window
->GetForegroundColour());
423 wxString label
= m_window
->GetLabel();
424 if ( !label
.empty() || bitmap
.Ok() )
426 wxRect rectLabel
= m_rect
;
429 rectLabel
.Inflate(-marginX
, -marginY
);
432 wxControl
*ctrl
= wxStaticCast(m_window
, wxControl
);
434 m_renderer
->DrawButtonLabel(m_dc
,
438 m_window
->GetStateFlags(),
439 ctrl
->GetAlignment(),
440 ctrl
->GetAccelIndex());
444 void wxControlRenderer::DrawFrame()
446 m_dc
.SetFont(m_window
->GetFont());
447 m_dc
.SetTextForeground(m_window
->GetForegroundColour());
448 m_dc
.SetTextBackground(m_window
->GetBackgroundColour());
450 wxControl
*ctrl
= wxStaticCast(m_window
, wxControl
);
452 m_renderer
->DrawFrame(m_dc
,
453 m_window
->GetLabel(),
455 m_window
->GetStateFlags(),
456 ctrl
->GetAlignment(),
457 ctrl
->GetAccelIndex());
460 void wxControlRenderer::DrawButtonBorder()
462 int flags
= m_window
->GetStateFlags();
464 m_renderer
->DrawButtonBorder(m_dc
, m_rect
, flags
, &m_rect
);
467 // m_renderer->DrawButtonSurface(m_dc, wxTHEME_BG_COLOUR(m_window), m_rect, flags );
470 void wxControlRenderer::DrawBitmap(const wxBitmap
& bitmap
)
472 int style
= m_window
->GetWindowStyle();
473 DrawBitmap(m_dc
, bitmap
, m_rect
,
474 style
& wxALIGN_MASK
,
475 style
& wxBI_EXPAND
? wxEXPAND
: wxSTRETCH_NOT
);
479 void wxControlRenderer::DrawBitmap(wxDC
&dc
,
480 const wxBitmap
& bitmap
,
485 // we may change the bitmap if we stretch it
486 wxBitmap bmp
= bitmap
;
490 int width
= bmp
.GetWidth(),
491 height
= bmp
.GetHeight();
495 if ( stretch
& wxTILE
)
498 for ( ; x
< rect
.width
; x
+= width
)
500 for ( y
= 0; y
< rect
.height
; y
+= height
)
502 // no need to use mask here as we cover the entire window area
503 dc
.DrawBitmap(bmp
, x
, y
);
508 else if ( stretch
& wxEXPAND
)
510 // stretch bitmap to fill the entire control
511 bmp
= wxBitmap(wxImage(bmp
.ConvertToImage()).Scale(rect
.width
, rect
.height
));
513 #endif // wxUSE_IMAGE
514 else // not stretched, not tiled
516 if ( alignment
& wxALIGN_RIGHT
)
518 x
= rect
.GetRight() - width
;
520 else if ( alignment
& wxALIGN_CENTRE
)
522 x
= (rect
.GetLeft() + rect
.GetRight() - width
+ 1) / 2;
524 else // alignment & wxALIGN_LEFT
529 if ( alignment
& wxALIGN_BOTTOM
)
531 y
= rect
.GetBottom() - height
;
533 else if ( alignment
& wxALIGN_CENTRE_VERTICAL
)
535 y
= (rect
.GetTop() + rect
.GetBottom() - height
+ 1) / 2;
537 else // alignment & wxALIGN_TOP
544 dc
.DrawBitmap(bmp
, x
, y
, true /* use mask */);
549 void wxControlRenderer::DrawScrollbar(const wxScrollBar
*scrollbar
,
550 int WXUNUSED(thumbPosOld
))
552 // we will only redraw the parts which must be redrawn and not everything
553 wxRegion rgnUpdate
= scrollbar
->GetUpdateRegion();
556 wxRect rectUpdate
= rgnUpdate
.GetBox();
557 wxLogTrace(_T("scrollbar"),
558 _T("%s redraw: update box is (%d, %d)-(%d, %d)"),
559 scrollbar
->IsVertical() ? _T("vert") : _T("horz"),
560 rectUpdate
.GetLeft(),
562 rectUpdate
.GetRight(),
563 rectUpdate
.GetBottom());
565 #if 0 //def WXDEBUG_SCROLLBAR
566 static bool s_refreshDebug
= false;
567 if ( s_refreshDebug
)
569 wxClientDC
dc(wxConstCast(scrollbar
, wxScrollBar
));
570 dc
.SetBrush(*wxRED_BRUSH
);
571 dc
.SetPen(*wxTRANSPARENT_PEN
);
572 dc
.DrawRectangle(rectUpdate
);
574 // under Unix we use "--sync" X option for this
580 #endif // WXDEBUG_SCROLLBAR
583 wxOrientation orient
= scrollbar
->IsVertical() ? wxVERTICAL
587 for ( int nBar
= 0; nBar
< 2; nBar
++ )
589 wxScrollBar::Element elem
=
590 (wxScrollBar::Element
)(wxScrollBar::Element_Bar_1
+ nBar
);
592 wxRect rectBar
= m_renderer
->GetScrollbarRect(scrollbar
, elem
);
594 if ( rgnUpdate
.Contains(rectBar
) )
596 wxLogTrace(_T("scrollbar"),
597 _T("drawing bar part %d at (%d, %d)-(%d, %d)"),
602 rectBar
.GetBottom());
604 m_renderer
->DrawScrollbarShaft(m_dc
,
607 scrollbar
->GetState(elem
));
612 for ( int nArrow
= 0; nArrow
< 2; nArrow
++ )
614 wxScrollBar::Element elem
=
615 (wxScrollBar::Element
)(wxScrollBar::Element_Arrow_Line_1
+ nArrow
);
617 wxRect rectArrow
= m_renderer
->GetScrollbarRect(scrollbar
, elem
);
618 if ( rgnUpdate
.Contains(rectArrow
) )
620 wxLogTrace(_T("scrollbar"),
621 _T("drawing arrow %d at (%d, %d)-(%d, %d)"),
625 rectArrow
.GetRight(),
626 rectArrow
.GetBottom());
628 scrollbar
->GetArrows().DrawArrow
630 (wxScrollArrows::Arrow
)nArrow
,
633 true // draw a scrollbar arrow, not just an arrow
638 // TODO: support for page arrows
641 wxScrollBar::Element elem
= wxScrollBar::Element_Thumb
;
642 wxRect rectThumb
= m_renderer
->GetScrollbarRect(scrollbar
, elem
);
643 if ( rectThumb
.width
&& rectThumb
.height
&& rgnUpdate
.Contains(rectThumb
) )
645 wxLogTrace(_T("scrollbar"),
646 _T("drawing thumb at (%d, %d)-(%d, %d)"),
649 rectThumb
.GetRight(),
650 rectThumb
.GetBottom());
652 m_renderer
->DrawScrollbarThumb(m_dc
,
655 scrollbar
->GetState(elem
));
659 #endif // wxUSE_SCROLLBAR
661 void wxControlRenderer::DrawLine(wxCoord x1
, wxCoord y1
, wxCoord x2
, wxCoord y2
)
663 wxASSERT_MSG( x1
== x2
|| y1
== y2
,
664 _T("line must be either horizontal or vertical") );
667 m_renderer
->DrawVerticalLine(m_dc
, x1
, y1
, y2
);
669 m_renderer
->DrawHorizontalLine(m_dc
, y1
, x1
, x2
);
674 void wxControlRenderer::DrawItems(const wxListBox
*lbox
,
675 size_t itemFirst
, size_t itemLast
)
677 DoDrawItems(lbox
, itemFirst
, itemLast
);
680 void wxControlRenderer::DoDrawItems(const wxListBox
*lbox
,
681 size_t itemFirst
, size_t itemLast
,
682 #if wxUSE_CHECKLISTBOX
685 bool WXUNUSED(isCheckLbox
))
688 // prepare for the drawing: calc the initial position
689 wxCoord lineHeight
= lbox
->GetLineHeight();
691 // note that SetClippingRegion() needs the physical (unscrolled)
692 // coordinates while we use the logical (scrolled) ones for the drawing
695 wxSize size
= lbox
->GetClientSize();
697 rect
.height
= size
.y
;
699 // keep the text inside the client rect or we will overwrite the vertical
700 // scrollbar for the long strings
701 m_dc
.SetClippingRegion(rect
.x
, rect
.y
, rect
.width
+ 1, rect
.height
+ 1);
703 // adjust the rect position now
704 lbox
->CalcScrolledPosition(rect
.x
, rect
.y
, &rect
.x
, &rect
.y
);
705 rect
.y
+= itemFirst
*lineHeight
;
706 rect
.height
= lineHeight
;
708 // the rect should go to the right visible border so adjust the width if x
709 // is shifted (rightmost point should stay the same)
710 rect
.width
-= rect
.x
;
712 // we'll keep the text colour unchanged
713 m_dc
.SetTextForeground(lbox
->GetForegroundColour());
715 // an item should have the focused rect only when the lbox has focus, so
716 // make sure that we never set wxCONTROL_FOCUSED flag if it doesn't
717 int itemCurrent
= wxWindow::FindFocus() == (wxWindow
*)lbox
// cast needed
718 ? lbox
->GetCurrentItem()
720 for ( size_t n
= itemFirst
; n
< itemLast
; n
++ )
723 if ( (int)n
== itemCurrent
)
724 flags
|= wxCONTROL_FOCUSED
;
725 if ( lbox
->IsSelected(n
) )
726 flags
|= wxCONTROL_SELECTED
;
728 #if wxUSE_CHECKLISTBOX
731 wxCheckListBox
*checklstbox
= wxStaticCast(lbox
, wxCheckListBox
);
732 if ( checklstbox
->IsChecked(n
) )
733 flags
|= wxCONTROL_CHECKED
;
735 m_renderer
->DrawCheckItem(m_dc
, lbox
->GetString(n
),
741 #endif // wxUSE_CHECKLISTBOX
743 m_renderer
->DrawItem(m_dc
, lbox
->GetString(n
), rect
, flags
);
746 rect
.y
+= lineHeight
;
750 #endif // wxUSE_LISTBOX
752 #if wxUSE_CHECKLISTBOX
754 void wxControlRenderer::DrawCheckItems(const wxCheckListBox
*lbox
,
755 size_t itemFirst
, size_t itemLast
)
757 DoDrawItems(lbox
, itemFirst
, itemLast
, true);
760 #endif // wxUSE_CHECKLISTBOX
764 void wxControlRenderer::DrawProgressBar(const wxGauge
*gauge
)
767 m_dc
.SetBrush(wxBrush(m_window
->GetBackgroundColour(), wxSOLID
));
768 m_dc
.SetPen(*wxTRANSPARENT_PEN
);
769 m_dc
.DrawRectangle(m_rect
);
771 int max
= gauge
->GetRange();
778 // calc the filled rect
779 int pos
= gauge
->GetValue();
780 int left
= max
- pos
;
782 wxRect rect
= m_rect
;
783 rect
.Deflate(1); // FIXME this depends on the border width
785 wxColour col
= m_window
->UseFgCol() ? m_window
->GetForegroundColour()
786 : wxTHEME_COLOUR(GAUGE
);
787 m_dc
.SetBrush(wxBrush(col
, wxSOLID
));
789 if ( gauge
->IsSmooth() )
791 // just draw the rectangle in one go
792 if ( gauge
->IsVertical() )
794 // vert bars grow from bottom to top
795 wxCoord dy
= ((rect
.height
- 1) * left
) / max
;
801 // grow from left to right
802 rect
.width
-= ((rect
.width
- 1) * left
) / max
;
805 m_dc
.DrawRectangle(rect
);
809 wxSize sizeStep
= m_renderer
->GetProgressBarStep();
810 int step
= gauge
->IsVertical() ? sizeStep
.y
: sizeStep
.x
;
812 // we divide by it below!
813 wxCHECK_RET( step
, _T("invalid wxGauge step") );
815 // round up to make the progress appear to start faster
816 int lenTotal
= gauge
->IsVertical() ? rect
.height
: rect
.width
;
817 int steps
= ((lenTotal
+ step
- 1) * pos
) / (max
* step
);
819 // calc the coords of one small rect
822 if ( gauge
->IsVertical() )
824 // draw from bottom to top: so first set y to the bottom
825 rect
.y
+= rect
.height
- 1;
827 // then adjust the height
830 // and then adjust y again to be what it should for the first rect
831 rect
.y
-= rect
.height
;
836 // remember that this will be the coord which will change
844 // don't leave 2 empty pixels in the beginning
854 for ( int n
= 0; n
< steps
; n
++ )
856 wxRect rectSegment
= rect
;
857 rectSegment
.Deflate(dx
, dy
);
859 m_dc
.DrawRectangle(rectSegment
);
864 // this can only happen for the last step of vertical gauge
865 rect
.height
= *px
- step
- 1;
868 else if ( *px
> lenTotal
- step
)
870 // this can only happen for the last step of horizontal gauge
871 rect
.width
= lenTotal
- *px
- 1;
877 #endif // wxUSE_GAUGE