Newer
Older
/* -*- 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
*****************************************************************************/
#include "qwt_painter.h"
#include "qwt_scale_draw.h"
#include "qwt_scale_map.h"
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <qevent.h>
#include <qdrawutil.h>
#include <qpainter.h>
#include <qalgorithms.h>
#include <qmath.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qapplication.h>
static QSize qwtHandleSize( const QSize &size,
Qt::Orientation orientation, bool hasTrough )
{
QSize handleSize = size;
if ( handleSize.isEmpty() )
{
const int handleThickness = 16;
handleSize.setWidth( 2 * handleThickness );
handleSize.setHeight( handleThickness );
if ( !hasTrough )
handleSize.transpose();
if ( orientation == Qt::Vertical )
handleSize.transpose();
}
return handleSize;
}
static QwtScaleDraw::Alignment qwtScaleDrawAlignment(
Qt::Orientation orientation, QwtSlider::ScalePosition scalePos )
{
QwtScaleDraw::Alignment align;
if ( orientation == Qt::Vertical )
{
// NoScale lays out like Left
if ( scalePos == QwtSlider::LeadingScale )
align = QwtScaleDraw::RightScale;
else
align = QwtScaleDraw::LeftScale;
}
else
{
// NoScale lays out like Bottom
if ( scalePos == QwtSlider::TrailingScale )
align = QwtScaleDraw::TopScale;
else
align = QwtScaleDraw::BottomScale;
}
return align;
}
PrivateData():
repeatTimerId( 0 ),
updateInterval( 150 ),
stepsIncrement( 0 ),
pendingValueChange( false ),
borderWidth( 2 ),
spacing( 4 ),
scalePosition( QwtSlider::TrailingScale ),
hasTrough( true ),
hasGroove( false ),
mouseOffset( 0 )
{
}
int repeatTimerId;
bool timerTick;
int updateInterval;
int stepsIncrement;
bool pendingValueChange;
Qt::Orientation orientation;
QwtSlider::ScalePosition scalePosition;
bool hasTrough;
bool hasGroove;
int mouseOffset;
Construct vertical slider in QwtSlider::Trough style
with a scale to the left.
The scale is initialized to [0.0, 100.0] and the value set to 0.0.
\param parent Parent widget
\sa setOrientation(), setScalePosition(), setBackgroundStyle()
QwtSlider::QwtSlider( QWidget *parent ):
QwtAbstractSlider( parent )
Construct a slider in QwtSlider::Trough style
When orientation is Qt::Vertical the scale will be aligned to
the left - otherwise at the the top of the slider.
The scale is initialized to [0.0, 100.0] and the value set to 0.0.
\param parent Parent widget
\param orientation Orientation of the slider.
QwtSlider::QwtSlider( Qt::Orientation orientation, QWidget *parent ):
QwtAbstractSlider( parent )
void QwtSlider::initSlider( Qt::Orientation orientation )
{
if ( orientation == Qt::Vertical )
setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding );
else
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
scaleDraw()->setAlignment(
qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
scaleDraw()->setLength( 100 );
\param orientation Allowed values are Qt::Horizontal and Qt::Vertical.
void QwtSlider::setOrientation( Qt::Orientation orientation )
scaleDraw()->setAlignment(
qwtScaleDrawAlignment( orientation, d_data->scalePosition ) );
if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) )
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
\return Orientation
\sa setOrientation()
*/
Qt::Orientation QwtSlider::orientation() const
{
return d_data->orientation;
}
/*!
\brief Change the position of the scale
\param scalePosition Position of the scale.
void QwtSlider::setScalePosition( ScalePosition scalePosition )
d_data->scalePosition = scalePosition;
scaleDraw()->setAlignment(
qwtScaleDrawAlignment( d_data->orientation, scalePosition ) );
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
/*!
\return Position of the scale
\sa setScalePosition()
*/
QwtSlider::ScalePosition QwtSlider::scalePosition() const
The border width is used for drawing the slider handle and the
trough.
\param width Border width
\sa borderWidth()
if ( width < 0 )
width = 0;
if ( width != d_data->borderWidth )
{
d_data->borderWidth = width;
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
return d_data->borderWidth;
}
/*!
\brief Change the spacing between trough and scale
A spacing of 0 means, that the backbone of the scale is covered
by the trough.
The default setting is 4 pixels.
\param spacing Number of pixels
\sa spacing();
*/
void QwtSlider::setSpacing( int spacing )
{
if ( spacing <= 0 )
spacing = 0;
if ( spacing != d_data->spacing )
{
d_data->spacing = spacing;
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
\return Number of pixels between slider and scale
\sa setSpacing()
return d_data->spacing;
}
/*!
\brief Set the slider's handle size
When the size is empty the slider handle will be painted with a
default size depending on its orientation() and backgroundStyle().
\param size New size
\sa handleSize()
*/
void QwtSlider::setHandleSize( const QSize &size )
{
if ( size != d_data->handleSize )
{
d_data->handleSize = size;
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
/*!
\return Size of the handle.
\sa setHandleSize()
*/
QSize QwtSlider::handleSize() const
{
return d_data->handleSize;
}
/*!
\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
{
const QwtScaleDraw *previousScaleDraw = this->scaleDraw();
if ( scaleDraw == NULL || scaleDraw == previousScaleDraw )
return;
if ( previousScaleDraw )
scaleDraw->setAlignment( previousScaleDraw->alignment() );
setAbstractScaleDraw( scaleDraw );
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
}
/*!
\return the scale draw of the slider
\sa setScaleDraw()
*/
const QwtScaleDraw *QwtSlider::scaleDraw() const
{
return static_cast<const QwtScaleDraw *>( abstractScaleDraw() );
}
/*!
\return the scale draw of the slider
\sa setScaleDraw()
*/
QwtScaleDraw *QwtSlider::scaleDraw()
{
}
//! Notify changed scale
void QwtSlider::scaleChange()
{
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
QwtAbstractSlider::scaleChange();
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
}
/*!
\brief Specify the update interval for automatic scrolling
The minimal accepted value is 50 ms.
\param interval Update interval in milliseconds
\sa setUpdateInterval()
*/
void QwtSlider::setUpdateInterval( int interval )
{
d_data->updateInterval = qMax( interval, 50 );
}
/*!
\return Update interval in milliseconds for automatic scrolling
\sa setUpdateInterval()
*/
int QwtSlider::updateInterval() const
{
return d_data->updateInterval;
}
/*!
Draw the slider into the specified rectangle.
\param painter Painter
\param sliderRect Bounding rectangle of the slider
*/
void QwtSlider::drawSlider(
QPainter *painter, const QRect &sliderRect ) const
{
QRect innerRect( sliderRect );
if ( d_data->hasTrough )
{
const int bw = d_data->borderWidth;
innerRect = sliderRect.adjusted( bw, bw, -bw, -bw );
painter->fillRect( innerRect, palette().brush( QPalette::Mid ) );
qDrawShadePanel( painter, sliderRect, palette(), true, bw, NULL );
}
const QSize handleSize = qwtHandleSize( d_data->handleSize,
d_data->orientation, d_data->hasTrough );
if ( d_data->hasGroove )
{
const int slotExtent = 4;
const int slotMargin = 4;
QRect slotRect;
if ( orientation() == Qt::Horizontal )
{
int slotOffset = qMax( 1, handleSize.width() / 2 - slotMargin );
int slotHeight = slotExtent + ( innerRect.height() % 2 );
slotRect.setWidth( innerRect.width() - 2 * slotOffset );
slotRect.setHeight( slotHeight );
else
{
int slotOffset = qMax( 1, handleSize.height() / 2 - slotMargin );
int slotWidth = slotExtent + ( innerRect.width() % 2 );
slotRect.setWidth( slotWidth );
slotRect.setHeight( innerRect.height() - 2 * slotOffset );
}
slotRect.moveCenter( innerRect.center() );
QBrush brush = palette().brush( QPalette::Dark );
qDrawShadePanel( painter, slotRect, palette(), true, 1 , &brush );
/*!
Draw the thumb at a position
\param painter Painter
\param handleRect Bounding rectangle of the handle
\param pos Position of the handle marker in widget coordinates
*/
void QwtSlider::drawHandle( QPainter *painter,
const QRect &handleRect, int pos ) const
const int bw = d_data->borderWidth;
qDrawShadePanel( painter,
handleRect, palette(), false, bw,
&palette().brush( QPalette::Button ) );
if ( orientation() == Qt::Horizontal )
{
qDrawShadeLine( painter, pos, handleRect.top() + bw,
pos, handleRect.bottom() - bw, palette(), true, 1 );
}
else // Vertical
{
qDrawShadeLine( painter, handleRect.left() + bw, pos,
handleRect.right() - bw, pos, palette(), true, 1 );
}
}
/*!
\brief Determine what to do when the user presses a mouse button.
\param pos Mouse position
\retval True, when handleRect() contains pos
\sa scrolledTo()
if ( handleRect().contains( pos ) )
{
const double v = ( orientation() == Qt::Horizontal )
? pos.x() : pos.y();
d_data->mouseOffset = v - transform( value() );
return true;
return false;
}
/*!
\brief Determine the value for a new position of the
slider handle.
\param pos Mouse position
\return Value for the mouse position
\sa isScrollPosition()
*/
double QwtSlider::scrolledTo( const QPoint &pos ) const
{
int p = ( orientation() == Qt::Horizontal )
? pos.x() : pos.y();
p -= d_data->mouseOffset;
int min = transform( lowerBound() );
int max = transform( upperBound() );
if ( min > max )
qSwap( min, max );
p = qBound( min, p, max );
return invTransform( p );
}
/*!
Mouse press event handler
\param event Mouse event
*/
void QwtSlider::mousePressEvent( QMouseEvent *event )
{
if ( isReadOnly() )
{
event->ignore();
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
const QPoint pos = event->pos();
if ( isValid() && d_data->sliderRect.contains( pos ) )
{
if ( !handleRect().contains( pos ) )
{
const int markerPos = transform( value() );
d_data->stepsIncrement = pageSteps();
if ( d_data->orientation == Qt::Horizontal )
{
if ( pos.x() < markerPos )
d_data->stepsIncrement = -d_data->stepsIncrement;
}
else
{
if ( pos.y() < markerPos )
d_data->stepsIncrement = -d_data->stepsIncrement;
}
if ( isInverted() )
d_data->stepsIncrement = -d_data->stepsIncrement;
d_data->timerTick = false;
d_data->repeatTimerId = startTimer( qMax( 250, 2 * updateInterval() ) );
return;
}
}
QwtAbstractSlider::mousePressEvent( event );
}
/*!
Mouse release event handler
\param event Mouse event
*/
void QwtSlider::mouseReleaseEvent( QMouseEvent *event )
{
if ( d_data->repeatTimerId > 0 )
{
killTimer( d_data->repeatTimerId );
d_data->repeatTimerId = 0;
d_data->timerTick = false;
d_data->stepsIncrement = 0;
}
if ( d_data->pendingValueChange )
{
d_data->pendingValueChange = false;
Q_EMIT valueChanged( value() );
}
/*!
Timer event handler
Handles the timer, when the mouse stays pressed
inside the sliderRect().
\param event Mouse event
*/
void QwtSlider::timerEvent( QTimerEvent *event )
if ( event->timerId() != d_data->repeatTimerId )
{
QwtAbstractSlider::timerEvent( event );
return;
}
if ( !isValid() )
{
killTimer( d_data->repeatTimerId );
d_data->repeatTimerId = 0;
return;
}
const double v = value();
incrementValue( d_data->stepsIncrement );
if ( v != value() )
{
if ( isTracking() )
Q_EMIT valueChanged( value() );
else
d_data->pendingValueChange = true;
Q_EMIT sliderMoved( value() );
if ( !d_data->timerTick )
{
// restart the timer with a shorter interval
killTimer( d_data->repeatTimerId );
d_data->repeatTimerId = startTimer( updateInterval() );
d_data->timerTick = true;
}
/*!
Qt paint event handler
\param event Paint event
*/
void QwtSlider::paintEvent( QPaintEvent *event )
QPainter painter( this );
painter.setClipRegion( event->region() );
QStyleOption opt;
opt.init(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
if ( d_data->scalePosition != QwtSlider::NoScale )
{
if ( !d_data->sliderRect.contains( event->rect() ) )
scaleDraw()->draw( &painter, palette() );
QwtPainter::drawFocusRect( &painter, this, d_data->sliderRect );
/*!
Qt resize event handler
\param event Resize event
*/
void QwtSlider::resizeEvent( QResizeEvent *event )
/*!
Handles QEvent::StyleChange and QEvent::FontChange events
\param event Change event
*/
void QwtSlider::changeEvent( QEvent *event )
{
if ( event->type() == QEvent::StyleChange ||
event->type() == QEvent::FontChange )
{
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
}
QwtAbstractSlider::changeEvent( event );
}
\param update_geometry notify the layout system and call update
to redraw the scale
*/
void QwtSlider::layoutSlider( bool update_geometry )
{
int bw = 0;
if ( d_data->hasTrough )
bw = d_data->borderWidth;
const QSize handleSize = qwtHandleSize( d_data->handleSize,
d_data->orientation, d_data->hasTrough );
/*
The marker line of the handle needs to be aligned to
the scale. But the marker is in the center
and we need space enough to display the rest of the handle.
But the scale itself usually needs margins for displaying
the tick labels, that also might needs space beyond the
backbone.
Now it depends on what needs more margins. If it is the
slider the scale gets shrunk, otherwise the slider.
*/
int scaleMargin = 0;
if ( d_data->scalePosition != QwtSlider::NoScale )
{
int d1, d2;
scaleDraw()->getBorderDistHint( font(), d1, d2 );
scaleMargin = qMax( d1, d2 ) - bw;
}
if ( d_data->orientation == Qt::Horizontal )
{
const int handleMargin = handleSize.width() / 2 - 1;
if ( scaleMargin > handleMargin )
{
int off = scaleMargin - handleMargin;
sliderRect.adjust( off, 0, -off, 0 );
}
scaleX = sliderRect.left() + bw + handleSize.width() / 2 - 1;
scaleLength = sliderRect.width() - handleSize.width();
}
else
{
int handleMargin = handleSize.height() / 2 - 1;
if ( scaleMargin > handleMargin )
{
int off = scaleMargin - handleMargin;
sliderRect.adjust( 0, off, 0, -off );
scaleY = sliderRect.top() + bw + handleSize.height() / 2 - 1;
scaleLength = sliderRect.height() - handleSize.height();
}
scaleLength -= 2 * bw;
// now align slider and scale according to the ScalePosition
if ( d_data->orientation == Qt::Horizontal )
{
const int h = handleSize.height() + 2 * bw;
if ( d_data->scalePosition == QwtSlider::TrailingScale )
{
sliderRect.setTop( sliderRect.bottom() + 1 - h );
scaleY = sliderRect.top() - d_data->spacing;
else
{
sliderRect.setHeight( h );
scaleY = sliderRect.bottom() + 1 + d_data->spacing;
}
else // Qt::Vertical
{
const int w = handleSize.width() + 2 * bw;
if ( d_data->scalePosition == QwtSlider::LeadingScale )
{
sliderRect.setWidth( w );
scaleX = sliderRect.right() + 1 + d_data->spacing;
}
else
{
sliderRect.setLeft( sliderRect.right() + 1 - w );
scaleX = sliderRect.left() - d_data->spacing;
scaleDraw()->move( scaleX, scaleY );
scaleDraw()->setLength( scaleLength );
d_data->sizeHintCache = QSize(); // invalidate
updateGeometry();
update();
}
}
The slider can be cutomized by showing a trough for the
handle.
\param on When true, the groove is visible
\sa hasTrough(), setGroove()
*/
void QwtSlider::setTrough( bool on )
if ( d_data->hasTrough != on )
{
d_data->hasTrough = on;
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
\return True, when the trough is visisble
\sa setTrough(), hasGroove()
*/
bool QwtSlider::hasTrough() const
The slider can be cutomized by showing a groove for the
handle.
\param on When true, the groove is visible
\sa hasGroove(), setThrough()
*/
void QwtSlider::setGroove( bool on )
if ( d_data->hasGroove != on )
{
d_data->hasGroove = on;
if ( testAttribute( Qt::WA_WState_Polished ) )
layoutSlider( true );
}
\return True, when the groove is visisble
\sa setGroove(), hasTrough()
*/
bool QwtSlider::hasGroove() const
const QSize hint = minimumSizeHint();
return hint.expandedTo( QApplication::globalStrut() );
const QSize handleSize = qwtHandleSize( d_data->handleSize,
d_data->orientation, d_data->hasTrough );
int bw = 0;
if ( d_data->hasTrough )
bw = d_data->borderWidth;
int sliderLength = 0;
int scaleExtent = 0;
int handleBorderDist;
if ( d_data->orientation == Qt::Horizontal )
handleBorderDist = handleSize.width();
else
handleBorderDist = handleSize.height();
sliderLength = scaleDraw()->minLength( font() );
if ( handleBorderDist > scaleBorderDist )
{
// We need additional space for the overlapping handle
sliderLength += handleBorderDist - scaleBorderDist;
}
scaleExtent += d_data->spacing;
scaleExtent += qCeil( scaleDraw()->extent( font() ) );
if ( d_data->orientation == Qt::Horizontal )
{
w = sliderLength;
h = handleSize.height() + 2 * bw + scaleExtent;
}
else
{
w = handleSize.width() + 2 * bw + scaleExtent;
h = sliderLength;
}
// finally add margins
int left, right, top, bottom;
getContentsMargins( &left, &top, &right, &bottom );
w += left + right;
h += top + bottom;
d_data->sizeHintCache = QSize( w, h );
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
/*!
\return Bounding rectangle of the slider handle
*/
QRect QwtSlider::handleRect() const
{
if ( !isValid() )
return QRect();
const int markerPos = transform( value() );
QPoint center = d_data->sliderRect.center();
if ( d_data->orientation == Qt::Horizontal )
center.setX( markerPos );
else
center.setY( markerPos );
QRect rect;
rect.setSize( qwtHandleSize( d_data->handleSize,
d_data->orientation, d_data->hasTrough ) );
rect.moveCenter( center );
return rect;
}
/*!
\return Bounding rectangle of the slider - without the scale
*/
QRect QwtSlider::sliderRect() const
{
return d_data->sliderRect;
}