This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 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(wxBrush(m_window->GetBackgroundColour(), wxSOLID));
|
|
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(wxBrush(col, wxSOLID));
|
|
|
|
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
|