Also simplify the code by relying on implicit constructors of wxPen and wxBrush from wxColour. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@77873 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
552 lines
16 KiB
C++
552 lines
16 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/univ/ctrlrend.cpp
|
|
// Purpose: wxControlRenderer implementation
|
|
// Author: Vadim Zeitlin
|
|
// Modified by:
|
|
// Created: 15.08.00
|
|
// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com)
|
|
// Licence: wxWindows licence
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// ===========================================================================
|
|
// declarations
|
|
// ===========================================================================
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// headers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#include "wx/control.h"
|
|
#include "wx/checklst.h"
|
|
#include "wx/listbox.h"
|
|
#include "wx/scrolbar.h"
|
|
#include "wx/dc.h"
|
|
#include "wx/log.h"
|
|
#include "wx/gauge.h"
|
|
#include "wx/image.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#include "wx/univ/theme.h"
|
|
#include "wx/univ/renderer.h"
|
|
#include "wx/univ/colschem.h"
|
|
|
|
// ============================================================================
|
|
// implementation
|
|
// ============================================================================
|
|
|
|
wxRenderer::~wxRenderer()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// wxControlRenderer
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxControlRenderer::wxControlRenderer(wxWindow *window,
|
|
wxDC& dc,
|
|
wxRenderer *renderer)
|
|
: m_dc(dc)
|
|
{
|
|
m_window = window;
|
|
m_renderer = renderer;
|
|
|
|
wxSize size = m_window->GetClientSize();
|
|
m_rect.x =
|
|
m_rect.y = 0;
|
|
m_rect.width = size.x;
|
|
m_rect.height = size.y;
|
|
}
|
|
|
|
void wxControlRenderer::DrawLabel()
|
|
{
|
|
m_dc.SetBackgroundMode(wxTRANSPARENT);
|
|
m_dc.SetFont(m_window->GetFont());
|
|
m_dc.SetTextForeground(m_window->GetForegroundColour());
|
|
|
|
wxString label = m_window->GetLabel();
|
|
if ( !label.empty() )
|
|
{
|
|
wxControl *ctrl = wxStaticCast(m_window, wxControl);
|
|
|
|
m_renderer->DrawLabel(m_dc,
|
|
label,
|
|
m_rect,
|
|
m_window->GetStateFlags(),
|
|
ctrl->GetAlignment(),
|
|
ctrl->GetAccelIndex());
|
|
}
|
|
}
|
|
|
|
void wxControlRenderer::DrawButtonLabel(const wxBitmap& bitmap,
|
|
wxCoord marginX, wxCoord marginY)
|
|
{
|
|
m_dc.SetBackgroundMode(wxTRANSPARENT);
|
|
m_dc.SetFont(m_window->GetFont());
|
|
m_dc.SetTextForeground(m_window->GetForegroundColour());
|
|
|
|
wxString label = m_window->GetLabel();
|
|
if ( !label.empty() || bitmap.IsOk() )
|
|
{
|
|
wxRect rectLabel = m_rect;
|
|
if ( bitmap.IsOk() )
|
|
{
|
|
rectLabel.Inflate(-marginX, -marginY);
|
|
}
|
|
|
|
wxControl *ctrl = wxStaticCast(m_window, wxControl);
|
|
|
|
m_renderer->DrawButtonLabel(m_dc,
|
|
label,
|
|
bitmap,
|
|
rectLabel,
|
|
m_window->GetStateFlags(),
|
|
ctrl->GetAlignment(),
|
|
ctrl->GetAccelIndex());
|
|
}
|
|
}
|
|
|
|
void wxControlRenderer::DrawFrame()
|
|
{
|
|
m_dc.SetFont(m_window->GetFont());
|
|
m_dc.SetTextForeground(m_window->GetForegroundColour());
|
|
m_dc.SetTextBackground(m_window->GetBackgroundColour());
|
|
|
|
wxControl *ctrl = wxStaticCast(m_window, wxControl);
|
|
|
|
m_renderer->DrawFrame(m_dc,
|
|
m_window->GetLabel(),
|
|
m_rect,
|
|
m_window->GetStateFlags(),
|
|
ctrl->GetAlignment(),
|
|
ctrl->GetAccelIndex());
|
|
}
|
|
|
|
void wxControlRenderer::DrawButtonBorder()
|
|
{
|
|
int flags = m_window->GetStateFlags();
|
|
|
|
m_renderer->DrawButtonBorder(m_dc, m_rect, flags, &m_rect);
|
|
|
|
// Why do this here?
|
|
// m_renderer->DrawButtonSurface(m_dc, wxTHEME_BG_COLOUR(m_window), m_rect, flags );
|
|
}
|
|
|
|
void wxControlRenderer::DrawBitmap(const wxBitmap& bitmap)
|
|
{
|
|
int style = m_window->GetWindowStyle();
|
|
DrawBitmap(m_dc, bitmap, m_rect,
|
|
style & wxALIGN_MASK,
|
|
style & wxBI_EXPAND ? wxEXPAND : wxSTRETCH_NOT);
|
|
}
|
|
|
|
/* static */
|
|
void wxControlRenderer::DrawBitmap(wxDC &dc,
|
|
const wxBitmap& bitmap,
|
|
const wxRect& rect,
|
|
int alignment,
|
|
wxStretch stretch)
|
|
{
|
|
// we may change the bitmap if we stretch it
|
|
wxBitmap bmp = bitmap;
|
|
if ( !bmp.IsOk() )
|
|
return;
|
|
|
|
int width = bmp.GetWidth(),
|
|
height = bmp.GetHeight();
|
|
|
|
wxCoord x = 0,
|
|
y = 0;
|
|
if ( stretch & wxTILE )
|
|
{
|
|
// tile the bitmap
|
|
for ( ; x < rect.width; x += width )
|
|
{
|
|
for ( y = 0; y < rect.height; y += height )
|
|
{
|
|
// no need to use mask here as we cover the entire window area
|
|
dc.DrawBitmap(bmp, x, y);
|
|
}
|
|
}
|
|
}
|
|
#if wxUSE_IMAGE
|
|
else if ( stretch & wxEXPAND )
|
|
{
|
|
// stretch bitmap to fill the entire control
|
|
bmp = wxBitmap(wxImage(bmp.ConvertToImage()).Scale(rect.width, rect.height));
|
|
}
|
|
#endif // wxUSE_IMAGE
|
|
else // not stretched, not tiled
|
|
{
|
|
if ( alignment & wxALIGN_RIGHT )
|
|
{
|
|
x = rect.GetRight() - width;
|
|
}
|
|
else if ( alignment & wxALIGN_CENTRE )
|
|
{
|
|
x = (rect.GetLeft() + rect.GetRight() - width + 1) / 2;
|
|
}
|
|
else // alignment & wxALIGN_LEFT
|
|
{
|
|
x = rect.GetLeft();
|
|
}
|
|
|
|
if ( alignment & wxALIGN_BOTTOM )
|
|
{
|
|
y = rect.GetBottom() - height;
|
|
}
|
|
else if ( alignment & wxALIGN_CENTRE_VERTICAL )
|
|
{
|
|
y = (rect.GetTop() + rect.GetBottom() - height + 1) / 2;
|
|
}
|
|
else // alignment & wxALIGN_TOP
|
|
{
|
|
y = rect.GetTop();
|
|
}
|
|
}
|
|
|
|
// do draw it
|
|
dc.DrawBitmap(bmp, x, y, true /* use mask */);
|
|
}
|
|
|
|
#if wxUSE_SCROLLBAR
|
|
|
|
void wxControlRenderer::DrawScrollbar(const wxScrollBar *scrollbar,
|
|
int WXUNUSED(thumbPosOld))
|
|
{
|
|
// we will only redraw the parts which must be redrawn and not everything
|
|
wxRegion rgnUpdate = scrollbar->GetUpdateRegion();
|
|
|
|
{
|
|
wxRect rectUpdate = rgnUpdate.GetBox();
|
|
wxLogTrace(wxT("scrollbar"),
|
|
wxT("%s redraw: update box is (%d, %d)-(%d, %d)"),
|
|
scrollbar->IsVertical() ? wxT("vert") : wxT("horz"),
|
|
rectUpdate.GetLeft(),
|
|
rectUpdate.GetTop(),
|
|
rectUpdate.GetRight(),
|
|
rectUpdate.GetBottom());
|
|
|
|
#if 0 //def WXDEBUG_SCROLLBAR
|
|
static bool s_refreshDebug = false;
|
|
if ( s_refreshDebug )
|
|
{
|
|
wxClientDC dc(wxConstCast(scrollbar, wxScrollBar));
|
|
dc.SetBrush(*wxRED_BRUSH);
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
dc.DrawRectangle(rectUpdate);
|
|
|
|
// under Unix we use "--sync" X option for this
|
|
#ifdef __WXMSW__
|
|
::GdiFlush();
|
|
::Sleep(200);
|
|
#endif // __WXMSW__
|
|
}
|
|
#endif // WXDEBUG_SCROLLBAR
|
|
}
|
|
|
|
wxOrientation orient = scrollbar->IsVertical() ? wxVERTICAL
|
|
: wxHORIZONTAL;
|
|
|
|
// the shaft
|
|
for ( int nBar = 0; nBar < 2; nBar++ )
|
|
{
|
|
wxScrollBar::Element elem =
|
|
(wxScrollBar::Element)(wxScrollBar::Element_Bar_1 + nBar);
|
|
|
|
wxRect rectBar = scrollbar->GetScrollbarRect(elem);
|
|
|
|
if ( rgnUpdate.Contains(rectBar) )
|
|
{
|
|
wxLogTrace(wxT("scrollbar"),
|
|
wxT("drawing bar part %d at (%d, %d)-(%d, %d)"),
|
|
nBar + 1,
|
|
rectBar.GetLeft(),
|
|
rectBar.GetTop(),
|
|
rectBar.GetRight(),
|
|
rectBar.GetBottom());
|
|
|
|
m_renderer->DrawScrollbarShaft(m_dc,
|
|
orient,
|
|
rectBar,
|
|
scrollbar->GetState(elem));
|
|
}
|
|
}
|
|
|
|
// arrows
|
|
for ( int nArrow = 0; nArrow < 2; nArrow++ )
|
|
{
|
|
wxScrollBar::Element elem =
|
|
(wxScrollBar::Element)(wxScrollBar::Element_Arrow_Line_1 + nArrow);
|
|
|
|
wxRect rectArrow = scrollbar->GetScrollbarRect(elem);
|
|
if ( rgnUpdate.Contains(rectArrow) )
|
|
{
|
|
wxLogTrace(wxT("scrollbar"),
|
|
wxT("drawing arrow %d at (%d, %d)-(%d, %d)"),
|
|
nArrow + 1,
|
|
rectArrow.GetLeft(),
|
|
rectArrow.GetTop(),
|
|
rectArrow.GetRight(),
|
|
rectArrow.GetBottom());
|
|
|
|
scrollbar->GetArrows().DrawArrow
|
|
(
|
|
(wxScrollArrows::Arrow)nArrow,
|
|
m_dc,
|
|
rectArrow,
|
|
true // draw a scrollbar arrow, not just an arrow
|
|
);
|
|
}
|
|
}
|
|
|
|
// TODO: support for page arrows
|
|
|
|
// and the thumb
|
|
wxScrollBar::Element elem = wxScrollBar::Element_Thumb;
|
|
wxRect rectThumb = scrollbar->GetScrollbarRect(elem);
|
|
if ( rectThumb.width && rectThumb.height && rgnUpdate.Contains(rectThumb) )
|
|
{
|
|
wxLogTrace(wxT("scrollbar"),
|
|
wxT("drawing thumb at (%d, %d)-(%d, %d)"),
|
|
rectThumb.GetLeft(),
|
|
rectThumb.GetTop(),
|
|
rectThumb.GetRight(),
|
|
rectThumb.GetBottom());
|
|
|
|
m_renderer->DrawScrollbarThumb(m_dc,
|
|
orient,
|
|
rectThumb,
|
|
scrollbar->GetState(elem));
|
|
}
|
|
}
|
|
|
|
#endif // wxUSE_SCROLLBAR
|
|
|
|
void wxControlRenderer::DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
|
|
{
|
|
wxASSERT_MSG( x1 == x2 || y1 == y2,
|
|
wxT("line must be either horizontal or vertical") );
|
|
|
|
if ( x1 == x2 )
|
|
m_renderer->DrawVerticalLine(m_dc, x1, y1, y2);
|
|
else // horizontal
|
|
m_renderer->DrawHorizontalLine(m_dc, y1, x1, x2);
|
|
}
|
|
|
|
#if wxUSE_LISTBOX
|
|
|
|
void wxControlRenderer::DrawItems(const wxListBox *lbox,
|
|
size_t itemFirst, size_t itemLast)
|
|
{
|
|
DoDrawItems(lbox, itemFirst, itemLast);
|
|
}
|
|
|
|
void wxControlRenderer::DoDrawItems(const wxListBox *lbox,
|
|
size_t itemFirst, size_t itemLast,
|
|
#if wxUSE_CHECKLISTBOX
|
|
bool isCheckLbox
|
|
#else
|
|
bool WXUNUSED(isCheckLbox)
|
|
#endif
|
|
)
|
|
{
|
|
// prepare for the drawing: calc the initial position
|
|
wxCoord lineHeight = lbox->GetLineHeight();
|
|
|
|
// note that SetClippingRegion() needs the physical (unscrolled)
|
|
// coordinates while we use the logical (scrolled) ones for the drawing
|
|
// itself
|
|
wxRect rect;
|
|
wxSize size = lbox->GetClientSize();
|
|
rect.width = size.x;
|
|
rect.height = size.y;
|
|
|
|
// keep the text inside the client rect or we will overwrite the vertical
|
|
// scrollbar for the long strings
|
|
m_dc.SetClippingRegion(rect.x, rect.y, rect.width + 1, rect.height + 1);
|
|
|
|
// adjust the rect position now
|
|
lbox->CalcScrolledPosition(rect.x, rect.y, &rect.x, &rect.y);
|
|
rect.y += itemFirst*lineHeight;
|
|
rect.height = lineHeight;
|
|
|
|
// the rect should go to the right visible border so adjust the width if x
|
|
// is shifted (rightmost point should stay the same)
|
|
rect.width -= rect.x;
|
|
|
|
// we'll keep the text colour unchanged
|
|
m_dc.SetTextForeground(lbox->GetForegroundColour());
|
|
|
|
// an item should have the focused rect only when the lbox has focus, so
|
|
// make sure that we never set wxCONTROL_FOCUSED flag if it doesn't
|
|
int itemCurrent = wxWindow::FindFocus() == (wxWindow *)lbox // cast needed
|
|
? lbox->GetCurrentItem()
|
|
: -1;
|
|
for ( size_t n = itemFirst; n < itemLast; n++ )
|
|
{
|
|
int flags = 0;
|
|
if ( (int)n == itemCurrent )
|
|
flags |= wxCONTROL_FOCUSED;
|
|
if ( lbox->IsSelected(n) )
|
|
flags |= wxCONTROL_SELECTED;
|
|
|
|
#if wxUSE_CHECKLISTBOX
|
|
if ( isCheckLbox )
|
|
{
|
|
wxCheckListBox *checklstbox = wxStaticCast(lbox, wxCheckListBox);
|
|
if ( checklstbox->IsChecked(n) )
|
|
flags |= wxCONTROL_CHECKED;
|
|
|
|
m_renderer->DrawCheckItem(m_dc, lbox->GetString(n),
|
|
wxNullBitmap,
|
|
rect,
|
|
flags);
|
|
}
|
|
else
|
|
#endif // wxUSE_CHECKLISTBOX
|
|
{
|
|
m_renderer->DrawItem(m_dc, lbox->GetString(n), rect, flags);
|
|
}
|
|
|
|
rect.y += lineHeight;
|
|
}
|
|
}
|
|
|
|
#endif // wxUSE_LISTBOX
|
|
|
|
#if wxUSE_CHECKLISTBOX
|
|
|
|
void wxControlRenderer::DrawCheckItems(const wxCheckListBox *lbox,
|
|
size_t itemFirst, size_t itemLast)
|
|
{
|
|
DoDrawItems(lbox, itemFirst, itemLast, true);
|
|
}
|
|
|
|
#endif // wxUSE_CHECKLISTBOX
|
|
|
|
#if wxUSE_GAUGE
|
|
|
|
void wxControlRenderer::DrawProgressBar(const wxGauge *gauge)
|
|
{
|
|
// draw background
|
|
m_dc.SetBrush(m_window->GetBackgroundColour());
|
|
m_dc.SetPen(*wxTRANSPARENT_PEN);
|
|
m_dc.DrawRectangle(m_rect);
|
|
|
|
int max = gauge->GetRange();
|
|
if ( !max )
|
|
{
|
|
// nothing to draw
|
|
return;
|
|
}
|
|
|
|
// calc the filled rect
|
|
int pos = gauge->GetValue();
|
|
int left = max - pos;
|
|
|
|
wxRect rect = m_rect;
|
|
rect.Deflate(1); // FIXME this depends on the border width
|
|
|
|
wxColour col = m_window->UseFgCol() ? m_window->GetForegroundColour()
|
|
: wxTHEME_COLOUR(GAUGE);
|
|
m_dc.SetBrush(col);
|
|
|
|
if ( gauge->IsSmooth() )
|
|
{
|
|
// just draw the rectangle in one go
|
|
if ( gauge->IsVertical() )
|
|
{
|
|
// vert bars grow from bottom to top
|
|
wxCoord dy = ((rect.height - 1) * left) / max;
|
|
rect.y += dy;
|
|
rect.height -= dy;
|
|
}
|
|
else // horizontal
|
|
{
|
|
// grow from left to right
|
|
rect.width -= ((rect.width - 1) * left) / max;
|
|
}
|
|
|
|
m_dc.DrawRectangle(rect);
|
|
}
|
|
else // discrete
|
|
{
|
|
wxSize sizeStep = m_renderer->GetProgressBarStep();
|
|
int step = gauge->IsVertical() ? sizeStep.y : sizeStep.x;
|
|
|
|
// we divide by it below!
|
|
wxCHECK_RET( step, wxT("invalid wxGauge step") );
|
|
|
|
// round up to make the progress appear to start faster
|
|
int lenTotal = gauge->IsVertical() ? rect.height : rect.width;
|
|
int steps = ((lenTotal + step - 1) * pos) / (max * step);
|
|
|
|
// calc the coords of one small rect
|
|
wxCoord *px;
|
|
wxCoord dx, dy;
|
|
if ( gauge->IsVertical() )
|
|
{
|
|
// draw from bottom to top: so first set y to the bottom
|
|
rect.y += rect.height - 1;
|
|
|
|
// then adjust the height
|
|
rect.height = step;
|
|
|
|
// and then adjust y again to be what it should for the first rect
|
|
rect.y -= rect.height;
|
|
|
|
// we are going up
|
|
step = -step;
|
|
|
|
// remember that this will be the coord which will change
|
|
px = &rect.y;
|
|
|
|
dy = 1;
|
|
dx = 0;
|
|
}
|
|
else // horizontal
|
|
{
|
|
// don't leave 2 empty pixels in the beginning
|
|
rect.x--;
|
|
|
|
px = &rect.x;
|
|
rect.width = step;
|
|
|
|
dy = 0;
|
|
dx = 1;
|
|
}
|
|
|
|
for ( int n = 0; n < steps; n++ )
|
|
{
|
|
wxRect rectSegment = rect;
|
|
rectSegment.Deflate(dx, dy);
|
|
|
|
m_dc.DrawRectangle(rectSegment);
|
|
|
|
*px += step;
|
|
if ( *px < 1 )
|
|
{
|
|
// this can only happen for the last step of vertical gauge
|
|
rect.height = *px - step - 1;
|
|
*px = 1;
|
|
}
|
|
else if ( *px > lenTotal - step )
|
|
{
|
|
// this can only happen for the last step of horizontal gauge
|
|
rect.width = lenTotal - *px - 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // wxUSE_GAUGE
|