1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: univ/renderer.cpp
3 // Purpose: wxControlRenderer implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
21 #pragma implementation "renderer.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
33 #include "wx/control.h"
34 #include "wx/checklst.h"
35 #include "wx/listbox.h"
36 #include "wx/scrolbar.h"
42 #include "wx/univ/theme.h"
43 #include "wx/univ/renderer.h"
44 #include "wx/univ/colschem.h"
50 // ============================================================================
52 // ============================================================================
54 // ----------------------------------------------------------------------------
55 // wxRenderer: drawing helpers
56 // ----------------------------------------------------------------------------
58 void wxRenderer::StandardDrawFrame(wxDC& dc,
59 const wxRect& rectFrame,
60 const wxRect& rectLabel)
62 // draw left, bottom and right lines entirely
63 DrawVerticalLine(dc, rectFrame.GetLeft(),
64 rectFrame.GetTop(), rectFrame.GetBottom() - 2);
65 DrawHorizontalLine(dc, rectFrame.GetBottom() - 1,
66 rectFrame.GetLeft(), rectFrame.GetRight());
67 DrawVerticalLine(dc, rectFrame.GetRight() - 1,
68 rectFrame.GetTop(), rectFrame.GetBottom() - 1);
70 // and 2 parts of the top line
71 DrawHorizontalLine(dc, rectFrame.GetTop(),
72 rectFrame.GetLeft() + 1, rectLabel.GetLeft());
73 DrawHorizontalLine(dc, rectFrame.GetTop(),
74 rectLabel.GetRight(), rectFrame.GetRight() - 2);
78 void wxRenderer::StandardDrawTextLine(wxDC& dc,
81 int selStart, int selEnd,
84 if ( (selStart == -1) || !(flags & wxCONTROL_FOCUSED) )
87 dc.DrawText(text, rect.x, rect.y);
89 else // we have selection
94 // draw the part before selection
95 wxString s(text, (size_t)selStart);
98 dc.DrawText(s, x, rect.y);
100 dc.GetTextExtent(s, &width, NULL);
104 // draw the selection itself
105 s = wxString(text.c_str() + selStart, text.c_str() + selEnd);
108 wxColour colFg = dc.GetTextForeground(),
109 colBg = dc.GetTextBackground();
110 dc.SetTextForeground(wxTHEME_COLOUR(HIGHLIGHT_TEXT));
111 dc.SetTextBackground(wxTHEME_COLOUR(HIGHLIGHT));
112 dc.SetBackgroundMode(wxSOLID);
114 dc.DrawText(s, x, rect.y);
115 dc.GetTextExtent(s, &width, NULL);
118 dc.SetBackgroundMode(wxTRANSPARENT);
119 dc.SetTextBackground(colBg);
120 dc.SetTextForeground(colFg);
123 // draw the final part
124 s = text.c_str() + selEnd;
127 dc.DrawText(s, x, rect.y);
132 // ----------------------------------------------------------------------------
133 // wxRenderer: scrollbar geometry
134 // ----------------------------------------------------------------------------
137 void wxRenderer::StandardScrollBarThumbSize(wxCoord length,
144 // the thumb can't be made less than this number of pixels
145 static const wxCoord thumbMinWidth = 8; // FIXME: should be configurable
147 *thumbStart = (length*thumbPos) / range;
148 *thumbEnd = (length*(thumbPos + thumbSize)) / range;
150 if ( *thumbEnd - *thumbStart < thumbMinWidth )
152 // adjust the end if possible
153 if ( *thumbStart <= length - thumbMinWidth )
155 // yes, just make it wider
156 *thumbEnd = *thumbStart + thumbMinWidth;
158 else // it is at the bottom of the scrollbar
160 // so move it a bit up
161 *thumbStart = length - thumbMinWidth;
168 wxRect wxRenderer::StandardGetScrollbarRect(const wxScrollBar *scrollbar,
169 wxScrollBar::Element elem,
171 const wxSize& sizeArrow)
173 if ( thumbPos == -1 )
175 thumbPos = scrollbar->GetThumbPosition();
178 wxSize sizeTotal = scrollbar->GetClientSize();
179 wxCoord *start, *width, length, arrow;
181 if ( scrollbar->IsVertical() )
184 rect.width = sizeTotal.x;
185 length = sizeTotal.y;
187 width = &rect.height;
193 rect.height = sizeTotal.y;
194 length = sizeTotal.x;
202 case wxScrollBar::Element_Arrow_Line_1:
207 case wxScrollBar::Element_Arrow_Line_2:
208 *start = length - arrow;
212 case wxScrollBar::Element_Arrow_Page_1:
213 case wxScrollBar::Element_Arrow_Page_2:
214 // we don't have them at all
217 case wxScrollBar::Element_Thumb:
218 case wxScrollBar::Element_Bar_1:
219 case wxScrollBar::Element_Bar_2:
220 // we need to calculate the thumb position - do it
223 wxCoord thumbStart, thumbEnd;
224 int range = scrollbar->GetRange();
232 StandardScrollBarThumbSize(length,
234 scrollbar->GetThumbSize(),
240 if ( elem == wxScrollBar::Element_Thumb )
243 *width = thumbEnd - thumbStart;
245 else if ( elem == wxScrollBar::Element_Bar_1 )
250 else // elem == wxScrollBar::Element_Bar_2
253 *width = length - thumbEnd;
256 // everything is relative to the start of the shaft so far
261 case wxScrollBar::Element_Max:
263 wxFAIL_MSG( _T("unknown scrollbar element") );
270 wxCoord wxRenderer::StandardScrollBarSize(const wxScrollBar *scrollbar,
271 const wxSize& sizeArrowSB)
273 wxCoord sizeArrow, sizeTotal;
274 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
276 sizeArrow = sizeArrowSB.y;
277 sizeTotal = scrollbar->GetSize().y;
281 sizeArrow = sizeArrowSB.x;
282 sizeTotal = scrollbar->GetSize().x;
285 return sizeTotal - 2*sizeArrow;
289 wxCoord wxRenderer::StandardScrollbarToPixel(const wxScrollBar *scrollbar,
291 const wxSize& sizeArrow)
293 int range = scrollbar->GetRange();
296 // the only valid position anyhow
300 if ( thumbPos == -1 )
302 // by default use the current thumb position
303 thumbPos = scrollbar->GetThumbPosition();
306 return ( thumbPos*StandardScrollBarSize(scrollbar, sizeArrow) ) / range
307 + (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x);
311 int wxRenderer::StandardPixelToScrollbar(const wxScrollBar *scrollbar,
313 const wxSize& sizeArrow)
315 return ( (coord - (scrollbar->IsVertical() ? sizeArrow.y : sizeArrow.x)) *
316 scrollbar->GetRange() ) /
317 StandardScrollBarSize(scrollbar, sizeArrow);
321 wxHitTest wxRenderer::StandardHitTestScrollbar(const wxScrollBar *scrollbar,
323 const wxSize& sizeArrowSB)
325 // we only need to work with either x or y coord depending on the
326 // orientation, choose one (but still check the other one to verify if the
327 // mouse is in the window at all)
328 wxCoord coord, sizeArrow, sizeTotal;
329 wxSize size = scrollbar->GetSize();
330 if ( scrollbar->GetWindowStyle() & wxVERTICAL )
332 if ( pt.x < 0 || pt.x > size.x )
336 sizeArrow = sizeArrowSB.y;
341 if ( pt.y < 0 || pt.y > size.y )
345 sizeArrow = sizeArrowSB.x;
349 // test for the arrows first as it's faster
350 if ( coord < 0 || coord > sizeTotal )
354 else if ( coord < sizeArrow )
356 return wxHT_SCROLLBAR_ARROW_LINE_1;
358 else if ( coord > sizeTotal - sizeArrow )
360 return wxHT_SCROLLBAR_ARROW_LINE_2;
364 // calculate the thumb position in pixels
365 sizeTotal -= 2*sizeArrow;
366 wxCoord thumbStart, thumbEnd;
367 int range = scrollbar->GetRange();
370 // clicking the scrollbar without range has no effect
375 StandardScrollBarThumbSize(sizeTotal,
376 scrollbar->GetThumbPosition(),
377 scrollbar->GetThumbSize(),
383 // now compare with the thumb position
385 if ( coord < thumbStart )
386 return wxHT_SCROLLBAR_BAR_1;
387 else if ( coord > thumbEnd )
388 return wxHT_SCROLLBAR_BAR_2;
390 return wxHT_SCROLLBAR_THUMB;
394 wxRenderer::~wxRenderer()
398 // ----------------------------------------------------------------------------
400 // ----------------------------------------------------------------------------
402 wxControlRenderer::wxControlRenderer(wxWindow *window,
404 wxRenderer *renderer)
408 m_renderer = renderer;
410 wxSize size = m_window->GetClientSize();
413 m_rect.width = size.x;
414 m_rect.height = size.y;
417 void wxControlRenderer::DrawLabel(const wxBitmap& bitmap,
418 wxCoord marginX, wxCoord marginY)
420 m_dc.SetBackgroundMode(wxTRANSPARENT);
421 m_dc.SetFont(m_window->GetFont());
422 m_dc.SetTextForeground(m_window->GetForegroundColour());
424 wxString label = m_window->GetLabel();
425 if ( !label.empty() || bitmap.Ok() )
427 wxRect rectLabel = m_rect;
430 rectLabel.Inflate(-marginX, -marginY);
432 // I don't know why this is necessary. RR.
437 wxControl *ctrl = wxStaticCast(m_window, wxControl);
439 m_renderer->DrawButtonLabel(m_dc,
443 m_window->GetStateFlags(),
444 ctrl->GetAlignment(),
445 ctrl->GetAccelIndex());
449 void wxControlRenderer::DrawFrame()
451 m_dc.SetFont(m_window->GetFont());
452 m_dc.SetTextForeground(m_window->GetForegroundColour());
453 m_dc.SetTextBackground(m_window->GetBackgroundColour());
455 wxControl *ctrl = wxStaticCast(m_window, wxControl);
457 m_renderer->DrawFrame(m_dc,
458 m_window->GetLabel(),
460 m_window->GetStateFlags(),
461 ctrl->GetAlignment(),
462 ctrl->GetAccelIndex());
465 void wxControlRenderer::DrawButtonBorder()
467 int flags = m_window->GetStateFlags();
469 m_renderer->DrawButtonBorder(m_dc, m_rect, flags, &m_rect);
472 // m_renderer->DrawButtonSurface(m_dc, wxTHEME_BG_COLOUR(m_window), m_rect, flags );
475 void wxControlRenderer::DrawBitmap(const wxBitmap& bitmap)
477 int style = m_window->GetWindowStyle();
478 DrawBitmap(m_dc, bitmap, m_rect,
479 style & wxALIGN_MASK,
480 style & wxBI_EXPAND ? wxEXPAND : wxSTRETCH_NOT);
484 void wxControlRenderer::DrawBitmap(wxDC &dc,
485 const wxBitmap& bitmap,
490 // we may change the bitmap if we stretch it
491 wxBitmap bmp = bitmap;
495 int width = bmp.GetWidth(),
496 height = bmp.GetHeight();
500 if ( stretch & wxTILE )
503 for ( ; x < rect.width; x += width )
505 for ( y = 0; y < rect.height; y += height )
507 // no need to use mask here as we cover the entire window area
508 dc.DrawBitmap(bmp, x, y);
512 else if ( stretch & wxEXPAND )
514 // stretch bitmap to fill the entire control
515 bmp = wxBitmap(wxImage(bmp.ConvertToImage()).Scale(rect.width, rect.height));
517 else // not stretched, not tiled
519 if ( alignment & wxALIGN_RIGHT )
521 x = rect.GetRight() - width;
523 else if ( alignment & wxALIGN_CENTRE )
525 x = (rect.GetLeft() + rect.GetRight() - width + 1) / 2;
527 else // alignment & wxALIGN_LEFT
532 if ( alignment & wxALIGN_BOTTOM )
534 y = rect.GetBottom() - height;
536 else if ( alignment & wxALIGN_CENTRE_VERTICAL )
538 y = (rect.GetTop() + rect.GetBottom() - height + 1) / 2;
540 else // alignment & wxALIGN_TOP
547 dc.DrawBitmap(bmp, x, y, TRUE /* use mask */);
550 void wxControlRenderer::DrawScrollbar(const wxScrollBar *scrollbar,
553 // we will only redraw the parts which must be redrawn and not everything
554 wxRegion rgnUpdate = scrollbar->GetUpdateRegion();
557 wxRect rectUpdate = rgnUpdate.GetBox();
558 wxLogTrace(_T("scrollbar"),
559 _T("%s redraw: update box is (%d, %d)-(%d, %d)"),
560 scrollbar->IsVertical() ? _T("vert") : _T("horz"),
561 rectUpdate.GetLeft(),
563 rectUpdate.GetRight(),
564 rectUpdate.GetBottom());
566 #if 0 //def WXDEBUG_SCROLLBAR
567 static bool s_refreshDebug = FALSE;
568 if ( s_refreshDebug )
570 wxClientDC dc(wxConstCast(scrollbar, wxScrollBar));
571 dc.SetBrush(*wxRED_BRUSH);
572 dc.SetPen(*wxTRANSPARENT_PEN);
573 dc.DrawRectangle(rectUpdate);
575 // under Unix we use "--sync" X option for this
581 #endif // WXDEBUG_SCROLLBAR
584 wxOrientation orient = scrollbar->IsVertical() ? wxVERTICAL
588 for ( int nBar = 0; nBar < 2; nBar++ )
590 wxScrollBar::Element elem =
591 (wxScrollBar::Element)(wxScrollBar::Element_Bar_1 + nBar);
593 wxRect rectBar = m_renderer->GetScrollbarRect(scrollbar, elem);
595 if ( rgnUpdate.Contains(rectBar) )
597 wxLogTrace(_T("scrollbar"),
598 _T("drawing bar part %d at (%d, %d)-(%d, %d)"),
603 rectBar.GetBottom());
605 m_renderer->DrawScrollbarShaft(m_dc,
608 scrollbar->GetState(elem));
613 for ( int nArrow = 0; nArrow < 2; nArrow++ )
615 wxScrollBar::Element elem =
616 (wxScrollBar::Element)(wxScrollBar::Element_Arrow_Line_1 + nArrow);
618 wxRect rectArrow = m_renderer->GetScrollbarRect(scrollbar, elem);
619 if ( rgnUpdate.Contains(rectArrow) )
621 wxLogTrace(_T("scrollbar"),
622 _T("drawing arrow %d at (%d, %d)-(%d, %d)"),
626 rectArrow.GetRight(),
627 rectArrow.GetBottom());
629 scrollbar->GetArrows().DrawArrow
631 (wxScrollArrows::Arrow)nArrow,
634 TRUE // draw a scrollbar arrow, not just an arrow
639 // TODO: support for page arrows
642 wxScrollBar::Element elem = wxScrollBar::Element_Thumb;
643 wxRect rectThumb = m_renderer->GetScrollbarRect(scrollbar, elem);
644 if ( rectThumb.width && rectThumb.height && rgnUpdate.Contains(rectThumb) )
646 wxLogTrace(_T("scrollbar"),
647 _T("drawing thumb at (%d, %d)-(%d, %d)"),
650 rectThumb.GetRight(),
651 rectThumb.GetBottom());
653 m_renderer->DrawScrollbarThumb(m_dc,
656 scrollbar->GetState(elem));
660 void wxControlRenderer::DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
662 wxASSERT_MSG( x1 == x2 || y1 == y2,
663 _T("line must be either horizontal or vertical") );
666 m_renderer->DrawVerticalLine(m_dc, x1, y1, y2);
668 m_renderer->DrawHorizontalLine(m_dc, y1, x1, x2);
673 void wxControlRenderer::DrawItems(const wxListBox *lbox,
674 size_t itemFirst, size_t itemLast)
676 DoDrawItems(lbox, itemFirst, itemLast);
679 void wxControlRenderer::DoDrawItems(const wxListBox *lbox,
680 size_t itemFirst, size_t itemLast,
683 // prepare for the drawing: calc the initial position
684 wxCoord lineHeight = lbox->GetLineHeight();
686 // note that SetClippingRegion() needs the physical (unscrolled)
687 // coordinates while we use the logical (scrolled) ones for the drawing
690 wxSize size = lbox->GetClientSize();
692 rect.height = size.y;
694 // keep the text inside the client rect or we will overwrite the vertical
695 // scrollbar for the long strings
696 m_dc.SetClippingRegion(rect.x, rect.y, rect.width + 1, rect.height + 1);
698 // adjust the rect position now
699 lbox->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
700 rect.y += itemFirst*lineHeight;
701 rect.height = lineHeight;
703 // the rect should go to the right visible border so adjust the width if x
704 // is shifted (rightmost point should stay the same)
705 rect.width -= rect.x;
707 // we'll keep the text colour unchanged
708 m_dc.SetTextForeground(lbox->GetForegroundColour());
710 // an item should have the focused rect only when the lbox has focus, so
711 // make sure that we never set wxCONTROL_FOCUSED flag if it doesn't
712 int itemCurrent = wxWindow::FindFocus() == (wxWindow *)lbox // cast needed
713 ? lbox->GetCurrentItem()
715 for ( size_t n = itemFirst; n < itemLast; n++ )
718 if ( (int)n == itemCurrent )
719 flags |= wxCONTROL_FOCUSED;
720 if ( lbox->IsSelected(n) )
721 flags |= wxCONTROL_SELECTED;
723 #if wxUSE_CHECKLISTBOX
726 wxCheckListBox *checklstbox = wxStaticCast(lbox, wxCheckListBox);
727 if ( checklstbox->IsChecked(n) )
728 flags |= wxCONTROL_CHECKED;
730 m_renderer->DrawCheckItem(m_dc, lbox->GetString(n),
736 #endif // wxUSE_CHECKLISTBOX
738 m_renderer->DrawItem(m_dc, lbox->GetString(n), rect, flags);
741 rect.y += lineHeight;
745 #endif // wxUSE_LISTBOX
747 #if wxUSE_CHECKLISTBOX
749 void wxControlRenderer::DrawCheckItems(const wxCheckListBox *lbox,
750 size_t itemFirst, size_t itemLast)
752 DoDrawItems(lbox, itemFirst, itemLast, TRUE);
755 #endif // wxUSE_CHECKLISTBOX
759 void wxControlRenderer::DrawProgressBar(const wxGauge *gauge)
762 m_dc.SetBrush(wxBrush(m_window->GetBackgroundColour(), wxSOLID));
763 m_dc.SetPen(*wxTRANSPARENT_PEN);
764 m_dc.DrawRectangle(m_rect);
766 int max = gauge->GetRange();
773 // calc the filled rect
774 int pos = gauge->GetValue();
775 int left = max - pos;
777 wxRect rect = m_rect;
778 rect.Deflate(1); // FIXME this depends on the border width
780 wxColour col = m_window->UseFgCol() ? m_window->GetForegroundColour()
781 : wxTHEME_COLOUR(GAUGE);
782 m_dc.SetBrush(wxBrush(col, wxSOLID));
784 if ( gauge->IsSmooth() )
786 // just draw the rectangle in one go
787 if ( gauge->IsVertical() )
789 // vert bars grow from bottom to top
790 wxCoord dy = ((rect.height - 1) * left) / max;
796 // grow from left to right
797 rect.width -= ((rect.width - 1) * left) / max;
800 m_dc.DrawRectangle(rect);
804 wxSize sizeStep = m_renderer->GetProgressBarStep();
805 int step = gauge->IsVertical() ? sizeStep.y : sizeStep.x;
807 // we divide by it below!
808 wxCHECK_RET( step, _T("invalid wxGauge step") );
810 // round up to make the progress appear to start faster
811 int lenTotal = gauge->IsVertical() ? rect.height : rect.width;
812 int steps = ((lenTotal + step - 1) * pos) / (max * step);
814 // calc the coords of one small rect
816 if ( gauge->IsVertical() )
818 // draw from bottom to top: so first set y to the bottom
819 rect.y += rect.height - 1;
821 // then adjust the height
824 // and then adjust y again to be what it should for the first rect
825 rect.y -= rect.height;
830 // remember that this will be the coord which will change
838 // don't leave 2 empty pixels in the beginning
848 for ( int n = 0; n < steps; n++ )
850 wxRect rectSegment = rect;
851 rectSegment.Deflate(dx, dy);
853 m_dc.DrawRectangle(rectSegment);
858 // this can only happen for the last step of vertical gauge
859 rect.height = *px - step - 1;
862 else if ( *px > lenTotal - step )
864 // this can only happen for the last step of horizontal gauge
865 rect.width = lenTotal - *px - 1;
871 #endif // wxUSE_GAUGE