地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

833 lines
21 KiB

/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
* Qwt Widget Library
* Copyright (C) 1997 Josef Wilgen
* Copyright (C) 2002 Uwe Rathmann
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Qwt License, Version 1.0
*****************************************************************************/
// vim: expandtab
#include <math.h>
#include <qevent.h>
#include <qdrawutil.h>
#include <qpainter.h>
#include "qwt_painter.h"
#include "qwt_paint_buffer.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_map.h"
#include "qwt_slider.h"
class QwtSlider::PrivateData
{
public:
QRect sliderRect;
int thumbLength;
int thumbWidth;
int borderWidth;
int scaleDist;
int xMargin;
int yMargin;
QwtSlider::ScalePos scalePos;
QwtSlider::BGSTYLE bgStyle;
/*
Scale and values might have different maps. This is
confusing and I can't see strong arguments for such
a feature. TODO ...
*/
QwtScaleMap map; // linear map
mutable QSize sizeHintCache;
};
/*!
\brief Constructor
\param parent parent widget
\param orientation Orientation of the slider. Can be Qt::Horizontal
or Qt::Vertical. Defaults to Qt::Horizontal.
\param scalePos Position of the scale.
Defaults to QwtSlider::NoScale.
\param bgStyle Background style. QwtSlider::BgTrough draws the
slider button in a trough, QwtSlider::BgSlot draws
a slot underneath the button. An or-combination of both
may also be used. The default is QwtSlider::BgTrough.
QwtSlider enforces valid combinations of its orientation and scale position.
If the combination is invalid, the scale position will be set to NoScale.
Valid combinations are:
- Qt::Horizonal with NoScale, TopScale, or BottomScale;
- Qt::Vertical with NoScale, LeftScale, or RightScale.
*/
QwtSlider::QwtSlider(QWidget *parent,
Qt::Orientation orientation, ScalePos scalePos, BGSTYLE bgStyle):
QwtAbstractSlider(orientation, parent)
{
initSlider(orientation, scalePos, bgStyle);
}
#if QT_VERSION < 0x040000
/*!
\brief Constructor
Build a horizontal slider with no scale and BgTrough as
background style
\param parent parent widget
\param name Object name
*/
QwtSlider::QwtSlider(QWidget *parent, const char* name):
QwtAbstractSlider(Qt::Horizontal, parent)
{
setName(name);
initSlider(Qt::Horizontal, NoScale, BgTrough);
}
#endif
void QwtSlider::initSlider(Qt::Orientation orientation,
ScalePos scalePos, BGSTYLE bgStyle)
{
if (orientation == Qt::Vertical)
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
else
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
#if QT_VERSION >= 0x040000
setAttribute(Qt::WA_WState_OwnSizePolicy, false);
#else
clearWState( WState_OwnSizePolicy );
#endif
#if QT_VERSION < 0x040000
setWFlags(Qt::WNoAutoErase);
#endif
d_data = new QwtSlider::PrivateData;
d_data->borderWidth = 2;
d_data->scaleDist = 4;
d_data->scalePos = scalePos;
d_data->xMargin = 0;
d_data->yMargin = 0;
d_data->bgStyle = bgStyle;
if (bgStyle == BgSlot) {
d_data->thumbLength = 16;
d_data->thumbWidth = 30;
} else {
d_data->thumbLength = 31;
d_data->thumbWidth = 16;
}
d_data->sliderRect.setRect(0,0,8,8);
QwtScaleDraw::Alignment align;
if ( orientation == Qt::Vertical ) {
// enforce a valid combination of scale position and orientation
if ((d_data->scalePos == BottomScale) || (d_data->scalePos == TopScale))
d_data->scalePos = NoScale;
// adopt the policy of layoutSlider (NoScale lays out like Left)
if (d_data->scalePos == RightScale)
align = QwtScaleDraw::RightScale;
else
align = QwtScaleDraw::LeftScale;
} else {
// enforce a valid combination of scale position and orientation
if ((d_data->scalePos == LeftScale) || (d_data->scalePos == RightScale))
d_data->scalePos = NoScale;
// adopt the policy of layoutSlider (NoScale lays out like Bottom)
if (d_data->scalePos == TopScale)
align = QwtScaleDraw::TopScale;
else
align = QwtScaleDraw::BottomScale;
}
scaleDraw()->setAlignment(align);
scaleDraw()->setLength(100);
setRange(0.0, 100.0, 1.0);
setValue(0.0);
}
QwtSlider::~QwtSlider()
{
delete d_data;
}
/*!
\brief Set the orientation.
\param o Orientation. Allowed values are Qt::Horizontal and Qt::Vertical.
If the new orientation and the old scale position are an invalid combination,
the scale position will be set to QwtSlider::NoScale.
\sa QwtAbstractSlider::orientation()
*/
void QwtSlider::setOrientation(Qt::Orientation o)
{
if ( o == orientation() )
return;
if (o == Qt::Horizontal) {
if ((d_data->scalePos == LeftScale) || (d_data->scalePos == RightScale))
d_data->scalePos = NoScale;
} else { // if (o == Qt::Vertical)
if ((d_data->scalePos == BottomScale) || (d_data->scalePos == TopScale))
d_data->scalePos = NoScale;
}
#if QT_VERSION >= 0x040000
if ( !testAttribute(Qt::WA_WState_OwnSizePolicy) )
#else
if ( !testWState( WState_OwnSizePolicy ) )
#endif
{
QSizePolicy sp = sizePolicy();
sp.transpose();
setSizePolicy(sp);
#if QT_VERSION >= 0x040000
setAttribute(Qt::WA_WState_OwnSizePolicy, false);
#else
clearWState( WState_OwnSizePolicy );
#endif
}
QwtAbstractSlider::setOrientation(o);
layoutSlider();
}
/*!
\brief Change the scale position (and slider orientation).
\param s Position of the scale.
A valid combination of scale position and orientation is enforced:
- if the new scale position is Left or Right, the scale orientation will
become Qt::Vertical;
- if the new scale position is Bottom or Top the scale orientation will
become Qt::Horizontal;
- if the new scale position is QwtSlider::NoScale, the scale
orientation will not change.
*/
void QwtSlider::setScalePosition(ScalePos s)
{
if ( d_data->scalePos == s )
return;
d_data->scalePos = s;
switch(d_data->scalePos) {
case BottomScale: {
setOrientation(Qt::Horizontal);
scaleDraw()->setAlignment(QwtScaleDraw::BottomScale);
break;
}
case TopScale: {
setOrientation(Qt::Horizontal);
scaleDraw()->setAlignment(QwtScaleDraw::TopScale);
break;
}
case LeftScale: {
setOrientation(Qt::Vertical);
scaleDraw()->setAlignment(QwtScaleDraw::LeftScale);
break;
}
case RightScale: {
setOrientation(Qt::Vertical);
scaleDraw()->setAlignment(QwtScaleDraw::RightScale);
break;
}
default: {
// nothing
}
}
layoutSlider();
}
//! Return the scale position.
QwtSlider::ScalePos QwtSlider::scalePosition() const
{
return d_data->scalePos;
}
/*!
\brief Change the slider's border width
\param bd border width
*/
void QwtSlider::setBorderWidth(int bd)
{
if ( bd < 0 )
bd = 0;
if ( bd != d_data->borderWidth ) {
d_data->borderWidth = bd;
layoutSlider();
}
}
/*!
\brief Set the slider's thumb length
\param thumbLength new length
*/
void QwtSlider::setThumbLength(int thumbLength)
{
if ( thumbLength < 8 )
thumbLength = 8;
if ( thumbLength != d_data->thumbLength ) {
d_data->thumbLength = thumbLength;
layoutSlider();
}
}
/*!
\brief Change the width of the thumb
\param w new width
*/
void QwtSlider::setThumbWidth(int w)
{
if ( w < 4 )
w = 4;
if ( d_data->thumbWidth != w ) {
d_data->thumbWidth = w;
layoutSlider();
}
}
/*!
\brief Set a scale draw
For changing the labels of the scales, it
is necessary to derive from QwtScaleDraw and
overload QwtScaleDraw::label().
\param scaleDraw ScaleDraw object, that has to be created with
new and will be deleted in ~QwtSlider or the next
call of setScaleDraw().
*/
void QwtSlider::setScaleDraw(QwtScaleDraw *scaleDraw)
{
const QwtScaleDraw *previousScaleDraw = this->scaleDraw();
if ( scaleDraw == NULL || scaleDraw == previousScaleDraw )
return;
if ( previousScaleDraw )
scaleDraw->setAlignment(previousScaleDraw->alignment());
setAbstractScaleDraw(scaleDraw);
layoutSlider();
}
/*!
\return the scale draw of the slider
\sa setScaleDraw()
*/
const QwtScaleDraw *QwtSlider::scaleDraw() const
{
return (QwtScaleDraw *)abstractScaleDraw();
}
/*!
\return the scale draw of the slider
\sa setScaleDraw()
*/
QwtScaleDraw *QwtSlider::scaleDraw()
{
return (QwtScaleDraw *)abstractScaleDraw();
}
//! Notify changed scale
void QwtSlider::scaleChange()
{
layoutSlider();
}
//! Notify change in font
void QwtSlider::fontChange(const QFont &f)
{
QwtAbstractSlider::fontChange( f );
layoutSlider();
}
//! Draw the slider into the specified rectangle.
void QwtSlider::drawSlider(QPainter *p, const QRect &r)
{
QRect cr(r);
if (d_data->bgStyle & BgTrough) {
qDrawShadePanel(p, r.x(), r.y(),
r.width(), r.height(),
#if QT_VERSION < 0x040000
colorGroup(),
#else
palette(),
#endif
true, d_data->borderWidth,0);
cr.setRect(r.x() + d_data->borderWidth,
r.y() + d_data->borderWidth,
r.width() - 2 * d_data->borderWidth,
r.height() - 2 * d_data->borderWidth);
p->fillRect(cr.x(), cr.y(), cr.width(), cr.height(),
#if QT_VERSION < 0x040000
colorGroup().brush(QColorGroup::Mid)
#else
palette().brush(QPalette::Mid)
#endif
);
}
if ( d_data->bgStyle & BgSlot) {
int ws = 4;
int ds = d_data->thumbLength / 2 - 4;
if ( ds < 1 )
ds = 1;
QRect rSlot;
if (orientation() == Qt::Horizontal) {
if ( cr.height() & 1 )
ws++;
rSlot = QRect(cr.x() + ds,
cr.y() + (cr.height() - ws) / 2,
cr.width() - 2 * ds, ws);
} else {
if ( cr.width() & 1 )
ws++;
rSlot = QRect(cr.x() + (cr.width() - ws) / 2,
cr.y() + ds,
ws, cr.height() - 2 * ds);
}
p->fillRect(rSlot.x(), rSlot.y(), rSlot.width(), rSlot.height(),
#if QT_VERSION < 0x040000
colorGroup().brush(QColorGroup::Dark)
#else
palette().brush(QPalette::Dark)
#endif
);
qDrawShadePanel(p, rSlot.x(), rSlot.y(),
rSlot.width(), rSlot.height(),
#if QT_VERSION < 0x040000
colorGroup(),
#else
palette(),
#endif
true, 1 ,0);
}
if ( isValid() )
drawThumb(p, cr, xyPosition(value()));
}
//! Draw the thumb at a position
void QwtSlider::drawThumb(QPainter *p, const QRect &sliderRect, int pos)
{
pos++; // shade line points one pixel below
if (orientation() == Qt::Horizontal) {
qDrawShadePanel(p, pos - d_data->thumbLength / 2,
sliderRect.y(), d_data->thumbLength, sliderRect.height(),
#if QT_VERSION < 0x040000
colorGroup(),
#else
palette(),
#endif
false, d_data->borderWidth,
#if QT_VERSION < 0x040000
&colorGroup().brush(QColorGroup::Button)
#else
&palette().brush(QPalette::Button)
#endif
);
qDrawShadeLine(p, pos, sliderRect.y(),
pos, sliderRect.y() + sliderRect.height() - 2,
#if QT_VERSION < 0x040000
colorGroup(),
#else
palette(),
#endif
true, 1);
} else { // Vertical
qDrawShadePanel(p,sliderRect.x(), pos - d_data->thumbLength / 2,
sliderRect.width(), d_data->thumbLength,
#if QT_VERSION < 0x040000
colorGroup(),
#else
palette(),
#endif
false, d_data->borderWidth,
#if QT_VERSION < 0x040000
&colorGroup().brush(QColorGroup::Button)
#else
&palette().brush(QPalette::Button)
#endif
);
qDrawShadeLine(p, sliderRect.x(), pos,
sliderRect.x() + sliderRect.width() - 2, pos,
#if QT_VERSION < 0x040000
colorGroup(),
#else
palette(),
#endif
true, 1);
}
}
//! Find the x/y position for a given value v
int QwtSlider::xyPosition(double v) const
{
return d_data->map.transform(v);
}
//! Determine the value corresponding to a specified mouse location.
double QwtSlider::getValue(const QPoint &p)
{
return d_data->map.invTransform(
orientation() == Qt::Horizontal ? p.x() : p.y());
}
/*!
\brief Determine scrolling mode and direction
\param p point
\param scrollMode Scrolling mode
\param direction Direction
*/
void QwtSlider::getScrollMode(const QPoint &p,
int &scrollMode, int &direction )
{
if (!d_data->sliderRect.contains(p)) {
scrollMode = ScrNone;
direction = 0;
return;
}
const int pos = ( orientation() == Qt::Horizontal ) ? p.x() : p.y();
const int markerPos = xyPosition(value());
if ((pos > markerPos - d_data->thumbLength / 2)
&& (pos < markerPos + d_data->thumbLength / 2)) {
scrollMode = ScrMouse;
direction = 0;
return;
}
scrollMode = ScrPage;
direction = (pos > markerPos) ? 1 : -1;
if ( scaleDraw()->map().p1() > scaleDraw()->map().p2() )
direction = -direction;
}
//! Qt paint event
void QwtSlider::paintEvent(QPaintEvent *e)
{
const QRect &ur = e->rect();
if ( ur.isValid() ) {
#if QT_VERSION < 0x040000
QwtPaintBuffer paintBuffer(this, ur);
draw(paintBuffer.painter(), ur);
#else
QPainter painter(this);
draw(&painter, ur);
#endif
}
}
//! Draw the QwtSlider
void QwtSlider::draw(QPainter *painter, const QRect&)
{
if (d_data->scalePos != NoScale) {
#if QT_VERSION < 0x040000
scaleDraw()->draw(painter, colorGroup());
#else
scaleDraw()->draw(painter, palette());
#endif
}
drawSlider(painter, d_data->sliderRect);
if ( hasFocus() )
QwtPainter::drawFocusRect(painter, this, d_data->sliderRect);
}
//! Qt resize event
void QwtSlider::resizeEvent(QResizeEvent *)
{
layoutSlider( false );
}
/*!
Recalculate the slider's geometry and layout based on
the current rect and fonts.
\param update_geometry notify the layout system and call update
to redraw the scale
*/
void QwtSlider::layoutSlider( bool update_geometry )
{
int sliderWidth = d_data->thumbWidth;
int sld1 = d_data->thumbLength / 2 - 1;
int sld2 = d_data->thumbLength / 2 + d_data->thumbLength % 2;
if ( d_data->bgStyle & BgTrough ) {
sliderWidth += 2 * d_data->borderWidth;
sld1 += d_data->borderWidth;
sld2 += d_data->borderWidth;
}
int scd = 0;
if ( d_data->scalePos != NoScale ) {
int d1, d2;
scaleDraw()->getBorderDistHint(font(), d1, d2);
scd = qwtMax(d1, d2);
}
int slo = scd - sld1;
if ( slo < 0 )
slo = 0;
int x, y, length;
const QRect r = rect();
if (orientation() == Qt::Horizontal) {
switch (d_data->scalePos) {
case TopScale: {
d_data->sliderRect.setRect(
r.x() + d_data->xMargin + slo,
r.y() + r.height() -
d_data->yMargin - sliderWidth,
r.width() - 2 * d_data->xMargin - 2 * slo,
sliderWidth);
x = d_data->sliderRect.x() + sld1;
y = d_data->sliderRect.y() - d_data->scaleDist;
break;
}
case BottomScale: {
d_data->sliderRect.setRect(
r.x() + d_data->xMargin + slo,
r.y() + d_data->yMargin,
r.width() - 2 * d_data->xMargin - 2 * slo,
sliderWidth);
x = d_data->sliderRect.x() + sld1;
y = d_data->sliderRect.y() + d_data->sliderRect.height()
+ d_data->scaleDist;
break;
}
case NoScale: // like Bottom, but no scale. See QwtSlider().
default: { // inconsistent orientation and scale position
d_data->sliderRect.setRect(
r.x() + d_data->xMargin + slo,
r.y() + d_data->yMargin,
r.width() - 2 * d_data->xMargin - 2 * slo,
sliderWidth);
x = d_data->sliderRect.x() + sld1;
y = 0;
break;
}
}
length = d_data->sliderRect.width() - (sld1 + sld2);
} else { // if (orientation() == Qt::Vertical
switch (d_data->scalePos) {
case RightScale:
d_data->sliderRect.setRect(
r.x() + d_data->xMargin,
r.y() + d_data->yMargin + slo,
sliderWidth,
r.height() - 2 * d_data->yMargin - 2 * slo);
x = d_data->sliderRect.x() + d_data->sliderRect.width()
+ d_data->scaleDist;
y = d_data->sliderRect.y() + sld1;
break;
case LeftScale:
d_data->sliderRect.setRect(
r.x() + r.width() - sliderWidth - d_data->xMargin,
r.y() + d_data->yMargin + slo,
sliderWidth,
r.height() - 2 * d_data->yMargin - 2 * slo);
x = d_data->sliderRect.x() - d_data->scaleDist;
y = d_data->sliderRect.y() + sld1;
break;
case NoScale: // like Left, but no scale. See QwtSlider().
default: // inconsistent orientation and scale position
d_data->sliderRect.setRect(
r.x() + r.width() - sliderWidth - d_data->xMargin,
r.y() + d_data->yMargin + slo,
sliderWidth,
r.height() - 2 * d_data->yMargin - 2 * slo);
x = 0;
y = d_data->sliderRect.y() + sld1;
break;
}
length = d_data->sliderRect.height() - (sld1 + sld2);
}
scaleDraw()->move(x, y);
scaleDraw()->setLength(length);
d_data->map.setPaintXInterval(scaleDraw()->map().p1(),
scaleDraw()->map().p2());
if ( update_geometry ) {
d_data->sizeHintCache = QSize(); // invalidate
updateGeometry();
update();
}
}
//! Notify change of value
void QwtSlider::valueChange()
{
QwtAbstractSlider::valueChange();
update();
}
//! Notify change of range
void QwtSlider::rangeChange()
{
d_data->map.setScaleInterval(minValue(), maxValue());
if (autoScale())
rescale(minValue(), maxValue());
QwtAbstractSlider::rangeChange();
layoutSlider();
}
/*!
\brief Set distances between the widget's border and internals.
\param xMargin Horizontal margin
\param yMargin Vertical margin
*/
void QwtSlider::setMargins(int xMargin, int yMargin)
{
if ( xMargin < 0 )
xMargin = 0;
if ( yMargin < 0 )
yMargin = 0;
if ( xMargin != d_data->xMargin || yMargin != d_data->yMargin ) {
d_data->xMargin = xMargin;
d_data->yMargin = yMargin;
layoutSlider();
}
}
/*!
Set the background style.
*/
void QwtSlider::setBgStyle(BGSTYLE st)
{
d_data->bgStyle = st;
layoutSlider();
}
/*!
\return the background style.
*/
QwtSlider::BGSTYLE QwtSlider::bgStyle() const
{
return d_data->bgStyle;
}
/*!
\return the thumb length.
*/
int QwtSlider::thumbLength() const
{
return d_data->thumbLength;
}
/*!
\return the thumb width.
*/
int QwtSlider::thumbWidth() const
{
return d_data->thumbWidth;
}
/*!
\return the border width.
*/
int QwtSlider::borderWidth() const
{
return d_data->borderWidth;
}
/*!
\return QwtSlider::minimumSizeHint()
*/
QSize QwtSlider::sizeHint() const
{
return minimumSizeHint();
}
/*!
\brief Return a minimum size hint
\warning The return value of QwtSlider::minimumSizeHint() depends on
the font and the scale.
*/
QSize QwtSlider::minimumSizeHint() const
{
if (!d_data->sizeHintCache.isEmpty())
return d_data->sizeHintCache;
int sliderWidth = d_data->thumbWidth;
if (d_data->bgStyle & BgTrough)
sliderWidth += 2 * d_data->borderWidth;
int w = 0, h = 0;
if (d_data->scalePos != NoScale) {
int d1, d2;
scaleDraw()->getBorderDistHint(font(), d1, d2);
int msMbd = qwtMax(d1, d2);
int mbd = d_data->thumbLength / 2;
if (d_data->bgStyle & BgTrough)
mbd += d_data->borderWidth;
if ( mbd < msMbd )
mbd = msMbd;
const int sdExtent = scaleDraw()->extent( QPen(), font() );
const int sdLength = scaleDraw()->minLength( QPen(), font() );
h = sliderWidth + sdExtent + d_data->scaleDist;
w = sdLength - 2 * msMbd + 2 * mbd;
} else { // no scale
w = 200;
h = sliderWidth;
}
if ( orientation() == Qt::Vertical )
qSwap(w, h);
w += 2 * d_data->xMargin;
h += 2 * d_data->yMargin;
d_data->sizeHintCache = QSize(w, h);
return d_data->sizeHintCache;
}