diff options
Diffstat (limited to 'src/libpcp_qwt')
155 files changed, 48668 insertions, 0 deletions
diff --git a/src/libpcp_qwt/GNUmakefile b/src/libpcp_qwt/GNUmakefile new file mode 100644 index 0000000..552118e --- /dev/null +++ b/src/libpcp_qwt/GNUmakefile @@ -0,0 +1,27 @@ +# +# Copyright (c) 2000,2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +# License for more details. +# + +TOPDIR = ../.. +include $(TOPDIR)/src/include/builddefs + +SUBDIRS = src + +default install : $(SUBDIRS) + $(SUBDIRS_MAKERULE) + +include $(BUILDRULES) + +default_pcp : default + +install_pcp : install diff --git a/src/libpcp_qwt/src/GNUmakefile b/src/libpcp_qwt/src/GNUmakefile new file mode 100644 index 0000000..d1c2139 --- /dev/null +++ b/src/libpcp_qwt/src/GNUmakefile @@ -0,0 +1,24 @@ +TOPDIR = ../../.. +LIBRARY = libpcp_qwt +PROJECT = $(LIBRARY).pro +include $(TOPDIR)/src/include/builddefs + +HEADERS = $(shell echo *.h) +SOURCES = $(shell echo *.cpp) + +default: build-me + +ifeq "$(ENABLE_QT)" "true" +build-me: $(PROJECT) + $(QTMAKE) +else +build-me: +endif + +include $(BUILDRULES) + +install: default + +default_pcp: default + +install_pcp: install diff --git a/src/libpcp_qwt/src/libpcp_qwt.pro b/src/libpcp_qwt/src/libpcp_qwt.pro new file mode 100644 index 0000000..9b64ab5 --- /dev/null +++ b/src/libpcp_qwt/src/libpcp_qwt.pro @@ -0,0 +1,173 @@ +TARGET = pcp_qwt +TEMPLATE = lib +VERSION = 6.0.2 +CONFIG += qt staticlib warn_on +QT = core gui network svg + +HEADERS += \ + qwt.h \ + qwt_abstract_scale_draw.h \ + qwt_interval_symbol.h \ + qwt_clipper.h \ + qwt_color_map.h \ + qwt_compat.h \ + qwt_column_symbol.h \ + qwt_interval.h \ + qwt_dyngrid_layout.h \ + qwt_global.h \ + qwt_math.h \ + qwt_magnifier.h \ + qwt_null_paintdevice.h \ + qwt_painter.h \ + qwt_panner.h \ + qwt_picker.h \ + qwt_picker_machine.h \ + qwt_point_3d.h \ + qwt_point_polar.h \ + qwt_round_scale_draw.h \ + qwt_scale_div.h \ + qwt_scale_draw.h \ + qwt_scale_engine.h \ + qwt_scale_map.h \ + qwt_spline.h \ + qwt_symbol.h \ + qwt_system_clock.h \ + qwt_text_engine.h \ + qwt_text_label.h \ + qwt_text.h + +SOURCES += \ + qwt_abstract_scale_draw.cpp \ + qwt_interval_symbol.cpp \ + qwt_clipper.cpp \ + qwt_color_map.cpp \ + qwt_column_symbol.cpp \ + qwt_interval.cpp \ + qwt_dyngrid_layout.cpp \ + qwt_math.cpp \ + qwt_magnifier.cpp \ + qwt_panner.cpp \ + qwt_null_paintdevice.cpp \ + qwt_painter.cpp \ + qwt_picker.cpp \ + qwt_round_scale_draw.cpp \ + qwt_scale_div.cpp \ + qwt_scale_draw.cpp \ + qwt_scale_map.cpp \ + qwt_spline.cpp \ + qwt_text_engine.cpp \ + qwt_text_label.cpp \ + qwt_text.cpp \ + qwt_event_pattern.cpp \ + qwt_picker_machine.cpp \ + qwt_point_3d.cpp \ + qwt_point_polar.cpp \ + qwt_scale_engine.cpp \ + qwt_symbol.cpp \ + qwt_system_clock.cpp + +# qwt plot +HEADERS += \ + qwt_curve_fitter.h \ + qwt_event_pattern.h \ + qwt_legend.h \ + qwt_legend_item.h \ + qwt_legend_itemmanager.h \ + qwt_plot.h \ + qwt_plot_renderer.h \ + qwt_plot_curve.h \ + qwt_plot_dict.h \ + qwt_plot_directpainter.h \ + qwt_plot_grid.h \ + qwt_plot_histogram.h \ + qwt_plot_item.h \ + qwt_plot_intervalcurve.h \ + qwt_plot_layout.h \ + qwt_plot_marker.h \ + qwt_plot_rasteritem.h \ + qwt_plot_spectrogram.h \ + qwt_plot_spectrocurve.h \ + qwt_plot_scaleitem.h \ + qwt_plot_seriesitem.h \ + qwt_plot_canvas.h \ + qwt_plot_panner.h \ + qwt_plot_picker.h \ + qwt_plot_zoomer.h \ + qwt_plot_magnifier.h \ + qwt_plot_rescaler.h \ + qwt_raster_data.h \ + qwt_matrix_raster_data.h \ + qwt_sampling_thread.h \ + qwt_series_data.h \ + qwt_scale_widget.h + +SOURCES += \ + qwt_curve_fitter.cpp \ + qwt_legend.cpp \ + qwt_legend_item.cpp \ + qwt_plot.cpp \ + qwt_plot_renderer.cpp \ + qwt_plot_xml.cpp \ + qwt_plot_axis.cpp \ + qwt_plot_curve.cpp \ + qwt_plot_dict.cpp \ + qwt_plot_directpainter.cpp \ + qwt_plot_grid.cpp \ + qwt_plot_histogram.cpp \ + qwt_plot_item.cpp \ + qwt_plot_intervalcurve.cpp \ + qwt_plot_spectrogram.cpp \ + qwt_plot_spectrocurve.cpp \ + qwt_plot_scaleitem.cpp \ + qwt_plot_seriesitem.cpp \ + qwt_plot_marker.cpp \ + qwt_plot_layout.cpp \ + qwt_plot_canvas.cpp \ + qwt_plot_panner.cpp \ + qwt_plot_rasteritem.cpp \ + qwt_plot_picker.cpp \ + qwt_plot_zoomer.cpp \ + qwt_plot_magnifier.cpp \ + qwt_plot_rescaler.cpp \ + qwt_raster_data.cpp \ + qwt_matrix_raster_data.cpp \ + qwt_sampling_thread.cpp \ + qwt_series_data.cpp \ + qwt_scale_widget.cpp + +# svg +HEADERS += qwt_plot_svgitem.h +SOURCES += qwt_plot_svgitem.cpp + +# widgets +HEADERS += \ + qwt_abstract_slider.h \ + qwt_abstract_scale.h \ + qwt_arrow_button.h \ + qwt_analog_clock.h \ + qwt_compass.h \ + qwt_compass_rose.h \ + qwt_counter.h \ + qwt_dial.h \ + qwt_dial_needle.h \ + qwt_double_range.h \ + qwt_knob.h \ + qwt_slider.h \ + qwt_thermo.h \ + qwt_wheel.h + +SOURCES += \ + qwt_abstract_slider.cpp \ + qwt_abstract_scale.cpp \ + qwt_arrow_button.cpp \ + qwt_analog_clock.cpp \ + qwt_compass.cpp \ + qwt_compass_rose.cpp \ + qwt_counter.cpp \ + qwt_dial.cpp \ + qwt_dial_needle.cpp \ + qwt_double_range.cpp \ + qwt_knob.cpp \ + qwt_slider.cpp \ + qwt_thermo.cpp \ + qwt_wheel.cpp diff --git a/src/libpcp_qwt/src/qwt.h b/src/libpcp_qwt/src/qwt.h new file mode 100644 index 0000000..3749493 --- /dev/null +++ b/src/libpcp_qwt/src/qwt.h @@ -0,0 +1,22 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_H +#define QWT_H + +#include "qwt_global.h" + +/*! + Some constants for use within Qwt. +*/ +namespace Qwt +{ +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_abstract_scale.cpp b/src/libpcp_qwt/src/qwt_abstract_scale.cpp new file mode 100644 index 0000000..e44776e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_abstract_scale.cpp @@ -0,0 +1,310 @@ +/* -*- 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_abstract_scale.h" +#include "qwt_scale_engine.h" +#include "qwt_scale_draw.h" +#include "qwt_scale_div.h" +#include "qwt_scale_map.h" +#include "qwt_interval.h" + +class QwtAbstractScale::PrivateData +{ +public: + PrivateData(): + maxMajor( 5 ), + maxMinor( 3 ), + stepSize( 0.0 ), + autoScale( true ) + { + scaleEngine = new QwtLinearScaleEngine; + scaleDraw = new QwtScaleDraw(); + } + + ~PrivateData() + { + delete scaleEngine; + delete scaleDraw; + } + + QwtScaleEngine *scaleEngine; + QwtAbstractScaleDraw *scaleDraw; + + int maxMajor; + int maxMinor; + double stepSize; + + bool autoScale; +}; + +/*! + Constructor + + Creates a default QwtScaleDraw and a QwtLinearScaleEngine. + Autoscaling is enabled, and the stepSize is initialized by 0.0. +*/ + +QwtAbstractScale::QwtAbstractScale() +{ + d_data = new PrivateData; + rescale( 0.0, 100.0 ); +} + +//! Destructor +QwtAbstractScale::~QwtAbstractScale() +{ + delete d_data; +} + +/*! + \brief Specify a scale. + + Disable autoscaling and define a scale by an interval and a step size + + \param vmin lower limit of the scale interval + \param vmax upper limit of the scale interval + \param stepSize major step size + \sa setAutoScale() +*/ +void QwtAbstractScale::setScale( double vmin, double vmax, double stepSize ) +{ + d_data->autoScale = false; + d_data->stepSize = stepSize; + + rescale( vmin, vmax, stepSize ); +} + +/*! + \brief Specify a scale. + + Disable autoscaling and define a scale by an interval and a step size + + \param interval Interval + \param stepSize major step size + \sa setAutoScale() +*/ +void QwtAbstractScale::setScale( const QwtInterval &interval, double stepSize ) +{ + setScale( interval.minValue(), interval.maxValue(), stepSize ); +} + + +/*! + \brief Specify a scale. + + Disable autoscaling and define a scale by a scale division + + \param scaleDiv Scale division + \sa setAutoScale() +*/ +void QwtAbstractScale::setScale( const QwtScaleDiv &scaleDiv ) +{ + d_data->autoScale = false; + + if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) + { + d_data->scaleDraw->setScaleDiv( scaleDiv ); + scaleChange(); + } +} + +/*! + Recalculate the scale division and update the scale draw. + + \param vmin Lower limit of the scale interval + \param vmax Upper limit of the scale interval + \param stepSize Major step size + + \sa scaleChange() +*/ +void QwtAbstractScale::rescale( double vmin, double vmax, double stepSize ) +{ + const QwtScaleDiv scaleDiv = d_data->scaleEngine->divideScale( + vmin, vmax, d_data->maxMajor, d_data->maxMinor, stepSize ); + + if ( scaleDiv != d_data->scaleDraw->scaleDiv() ) + { + d_data->scaleDraw->setTransformation( + d_data->scaleEngine->transformation() ); + d_data->scaleDraw->setScaleDiv( scaleDiv ); + scaleChange(); + } +} + +/*! + \brief Advise the widget to control the scale range internally. + + Autoscaling is on by default. + \sa setScale(), autoScale() +*/ +void QwtAbstractScale::setAutoScale() +{ + if ( !d_data->autoScale ) + { + d_data->autoScale = true; + scaleChange(); + } +} + +/*! + \return \c true if autoscaling is enabled +*/ +bool QwtAbstractScale::autoScale() const +{ + return d_data->autoScale; +} + +/*! + \brief Set the maximum number of major tick intervals. + + The scale's major ticks are calculated automatically such that + the number of major intervals does not exceed ticks. + The default value is 5. + \param ticks maximal number of major ticks. + \sa QwtAbstractScaleDraw +*/ +void QwtAbstractScale::setScaleMaxMajor( int ticks ) +{ + if ( ticks != d_data->maxMajor ) + { + d_data->maxMajor = ticks; + updateScaleDraw(); + } +} + +/*! + \brief Set the maximum number of minor tick intervals + + The scale's minor ticks are calculated automatically such that + the number of minor intervals does not exceed ticks. + The default value is 3. + \param ticks + \sa QwtAbstractScaleDraw +*/ +void QwtAbstractScale::setScaleMaxMinor( int ticks ) +{ + if ( ticks != d_data->maxMinor ) + { + d_data->maxMinor = ticks; + updateScaleDraw(); + } +} + +/*! + \return Max. number of minor tick intervals + The default value is 3. +*/ +int QwtAbstractScale::scaleMaxMinor() const +{ + return d_data->maxMinor; +} + +/*! + \return Max. number of major tick intervals + The default value is 5. +*/ +int QwtAbstractScale::scaleMaxMajor() const +{ + return d_data->maxMajor; +} + +/*! + \brief Set a scale draw + + scaleDraw has to be created with new and will be deleted in + ~QwtAbstractScale or the next call of setAbstractScaleDraw. +*/ +void QwtAbstractScale::setAbstractScaleDraw( QwtAbstractScaleDraw *scaleDraw ) +{ + if ( scaleDraw == NULL || scaleDraw == d_data->scaleDraw ) + return; + + if ( d_data->scaleDraw != NULL ) + scaleDraw->setScaleDiv( d_data->scaleDraw->scaleDiv() ); + + delete d_data->scaleDraw; + d_data->scaleDraw = scaleDraw; +} + +/*! + \return Scale draw + \sa setAbstractScaleDraw() +*/ +QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() +{ + return d_data->scaleDraw; +} + +/*! + \return Scale draw + \sa setAbstractScaleDraw() +*/ +const QwtAbstractScaleDraw *QwtAbstractScale::abstractScaleDraw() const +{ + return d_data->scaleDraw; +} + +void QwtAbstractScale::updateScaleDraw() +{ + rescale( d_data->scaleDraw->scaleDiv().lowerBound(), + d_data->scaleDraw->scaleDiv().upperBound(), d_data->stepSize ); +} + +/*! + \brief Set a scale engine + + The scale engine is responsible for calculating the scale division, + and in case of auto scaling how to align the scale. + + scaleEngine has to be created with new and will be deleted in + ~QwtAbstractScale or the next call of setScaleEngine. +*/ +void QwtAbstractScale::setScaleEngine( QwtScaleEngine *scaleEngine ) +{ + if ( scaleEngine != NULL && scaleEngine != d_data->scaleEngine ) + { + delete d_data->scaleEngine; + d_data->scaleEngine = scaleEngine; + } +} + +/*! + \return Scale engine + \sa setScaleEngine() +*/ +const QwtScaleEngine *QwtAbstractScale::scaleEngine() const +{ + return d_data->scaleEngine; +} + +/*! + \return Scale engine + \sa setScaleEngine() +*/ +QwtScaleEngine *QwtAbstractScale::scaleEngine() +{ + return d_data->scaleEngine; +} + +/*! + \brief Notify changed scale + + Dummy empty implementation, intended to be overloaded by derived classes +*/ +void QwtAbstractScale::scaleChange() +{ +} + +/*! + \return abstractScaleDraw()->scaleMap() +*/ +const QwtScaleMap &QwtAbstractScale::scaleMap() const +{ + return d_data->scaleDraw->scaleMap(); +} diff --git a/src/libpcp_qwt/src/qwt_abstract_scale.h b/src/libpcp_qwt/src/qwt_abstract_scale.h new file mode 100644 index 0000000..670d228 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_abstract_scale.h @@ -0,0 +1,70 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_SCALE_H +#define QWT_ABSTRACT_SCALE_H + +#include "qwt_global.h" + +class QwtScaleEngine; +class QwtAbstractScaleDraw; +class QwtScaleDiv; +class QwtScaleMap; +class QwtInterval; + +/*! + \brief An abstract base class for classes containing a scale + + QwtAbstractScale is used to provide classes with a QwtScaleDraw, + and a QwtScaleDiv. The QwtScaleDiv might be set explicitely + or calculated by a QwtScaleEngine. +*/ + +class QWT_EXPORT QwtAbstractScale +{ +public: + QwtAbstractScale(); + virtual ~QwtAbstractScale(); + + void setScale( double vmin, double vmax, double step = 0.0 ); + void setScale( const QwtInterval &, double step = 0.0 ); + void setScale( const QwtScaleDiv & ); + + void setAutoScale(); + bool autoScale() const; + + void setScaleMaxMajor( int ticks ); + int scaleMaxMinor() const; + + void setScaleMaxMinor( int ticks ); + int scaleMaxMajor() const; + + void setScaleEngine( QwtScaleEngine * ); + const QwtScaleEngine *scaleEngine() const; + QwtScaleEngine *scaleEngine(); + + const QwtScaleMap &scaleMap() const; + +protected: + void rescale( double vmin, double vmax, double step = 0.0 ); + + void setAbstractScaleDraw( QwtAbstractScaleDraw * ); + const QwtAbstractScaleDraw *abstractScaleDraw() const; + QwtAbstractScaleDraw *abstractScaleDraw(); + + virtual void scaleChange(); + +private: + void updateScaleDraw(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_abstract_scale_draw.cpp b/src/libpcp_qwt/src/qwt_abstract_scale_draw.cpp new file mode 100644 index 0000000..49230b7 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_abstract_scale_draw.cpp @@ -0,0 +1,412 @@ +/* -*- 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_abstract_scale_draw.h" +#include "qwt_math.h" +#include "qwt_text.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include <qpainter.h> +#include <qpalette.h> +#include <qmap.h> +#include <qlocale.h> + +class QwtAbstractScaleDraw::PrivateData +{ +public: + PrivateData(): + spacing( 4.0 ), + penWidth( 0 ), + minExtent( 0.0 ) + { + components = QwtAbstractScaleDraw::Backbone + | QwtAbstractScaleDraw::Ticks + | QwtAbstractScaleDraw::Labels; + + tickLength[QwtScaleDiv::MinorTick] = 4.0; + tickLength[QwtScaleDiv::MediumTick] = 6.0; + tickLength[QwtScaleDiv::MajorTick] = 8.0; + } + + ScaleComponents components; + + QwtScaleMap map; + QwtScaleDiv scldiv; + + double spacing; + double tickLength[QwtScaleDiv::NTickTypes]; + int penWidth; + + double minExtent; + + QMap<double, QwtText> labelCache; +}; + +/*! + \brief Constructor + + The range of the scale is initialized to [0, 100], + The spacing (distance between ticks and labels) is + set to 4, the tick lengths are set to 4,6 and 8 pixels +*/ +QwtAbstractScaleDraw::QwtAbstractScaleDraw() +{ + d_data = new QwtAbstractScaleDraw::PrivateData; +} + +//! Destructor +QwtAbstractScaleDraw::~QwtAbstractScaleDraw() +{ + delete d_data; +} + +/*! + En/Disable a component of the scale + + \param component Scale component + \param enable On/Off + + \sa hasComponent() +*/ +void QwtAbstractScaleDraw::enableComponent( + ScaleComponent component, bool enable ) +{ + if ( enable ) + d_data->components |= component; + else + d_data->components &= ~component; +} + +/*! + Check if a component is enabled + \sa enableComponent() +*/ +bool QwtAbstractScaleDraw::hasComponent( ScaleComponent component ) const +{ + return ( d_data->components & component ); +} + +/*! + Change the scale division + \param sd New scale division +*/ +void QwtAbstractScaleDraw::setScaleDiv( const QwtScaleDiv &sd ) +{ + d_data->scldiv = sd; + d_data->map.setScaleInterval( sd.lowerBound(), sd.upperBound() ); + d_data->labelCache.clear(); +} + +/*! + Change the transformation of the scale + \param transformation New scale transformation +*/ +void QwtAbstractScaleDraw::setTransformation( + QwtScaleTransformation *transformation ) +{ + d_data->map.setTransformation( transformation ); +} + +//! \return Map how to translate between scale and pixel values +const QwtScaleMap &QwtAbstractScaleDraw::scaleMap() const +{ + return d_data->map; +} + +//! \return Map how to translate between scale and pixel values +QwtScaleMap &QwtAbstractScaleDraw::scaleMap() +{ + return d_data->map; +} + +//! \return scale division +const QwtScaleDiv& QwtAbstractScaleDraw::scaleDiv() const +{ + return d_data->scldiv; +} + +/*! + \brief Specify the width of the scale pen + \param width Pen width + \sa penWidth() +*/ +void QwtAbstractScaleDraw::setPenWidth( int width ) +{ + if ( width < 0 ) + width = 0; + + if ( width != d_data->penWidth ) + d_data->penWidth = width; +} + +/*! + \return Scale pen width + \sa setPenWidth() +*/ +int QwtAbstractScaleDraw::penWidth() const +{ + return d_data->penWidth; +} + +/*! + \brief Draw the scale + + \param painter The painter + + \param palette Palette, text color is used for the labels, + foreground color for ticks and backbone +*/ +void QwtAbstractScaleDraw::draw( QPainter *painter, + const QPalette& palette ) const +{ + painter->save(); + + QPen pen = painter->pen(); + pen.setWidth( d_data->penWidth ); + pen.setCosmetic( false ); + painter->setPen( pen ); + + if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) + { + painter->save(); + painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style + + const QList<double> &majorTicks = + d_data->scldiv.ticks( QwtScaleDiv::MajorTick ); + + for ( int i = 0; i < majorTicks.count(); i++ ) + { + const double v = majorTicks[i]; + if ( d_data->scldiv.contains( v ) ) + drawLabel( painter, v ); + } + + painter->restore(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + { + painter->save(); + + QPen pen = painter->pen(); + pen.setColor( palette.color( QPalette::WindowText ) ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + + for ( int tickType = QwtScaleDiv::MinorTick; + tickType < QwtScaleDiv::NTickTypes; tickType++ ) + { + const QList<double> &ticks = d_data->scldiv.ticks( tickType ); + for ( int i = 0; i < ticks.count(); i++ ) + { + const double v = ticks[i]; + if ( d_data->scldiv.contains( v ) ) + drawTick( painter, v, d_data->tickLength[tickType] ); + } + } + + painter->restore(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) + { + painter->save(); + + QPen pen = painter->pen(); + pen.setColor( palette.color( QPalette::WindowText ) ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + + drawBackbone( painter ); + + painter->restore(); + } + + painter->restore(); +} + +/*! + \brief Set the spacing between tick and labels + + The spacing is the distance between ticks and labels. + The default spacing is 4 pixels. + + \param spacing Spacing + + \sa spacing() +*/ +void QwtAbstractScaleDraw::setSpacing( double spacing ) +{ + if ( spacing < 0 ) + spacing = 0; + + d_data->spacing = spacing; +} + +/*! + \brief Get the spacing + + The spacing is the distance between ticks and labels. + The default spacing is 4 pixels. + + \sa setSpacing() +*/ +double QwtAbstractScaleDraw::spacing() const +{ + return d_data->spacing; +} + +/*! + \brief Set a minimum for the extent + + The extent is calculated from the coomponents of the + scale draw. In situations, where the labels are + changing and the layout depends on the extent (f.e scrolling + a scale), setting an upper limit as minimum extent will + avoid jumps of the layout. + + \param minExtent Minimum extent + + \sa extent(), minimumExtent() +*/ +void QwtAbstractScaleDraw::setMinimumExtent( double minExtent ) +{ + if ( minExtent < 0.0 ) + minExtent = 0.0; + + d_data->minExtent = minExtent; +} + +/*! + Get the minimum extent + \sa extent(), setMinimumExtent() +*/ +double QwtAbstractScaleDraw::minimumExtent() const +{ + return d_data->minExtent; +} + +/*! + Set the length of the ticks + + \param tickType Tick type + \param length New length + + \warning the length is limited to [0..1000] +*/ +void QwtAbstractScaleDraw::setTickLength( + QwtScaleDiv::TickType tickType, double length ) +{ + if ( tickType < QwtScaleDiv::MinorTick || + tickType > QwtScaleDiv::MajorTick ) + { + return; + } + + if ( length < 0.0 ) + length = 0.0; + + const double maxTickLen = 1000.0; + if ( length > maxTickLen ) + length = maxTickLen; + + d_data->tickLength[tickType] = length; +} + +/*! + Return the length of the ticks + + \sa setTickLength(), maxTickLength() +*/ +double QwtAbstractScaleDraw::tickLength( QwtScaleDiv::TickType tickType ) const +{ + if ( tickType < QwtScaleDiv::MinorTick || + tickType > QwtScaleDiv::MajorTick ) + { + return 0; + } + + return d_data->tickLength[tickType]; +} + +/*! + \return Length of the longest tick + + Useful for layout calculations + \sa tickLength(), setTickLength() +*/ +double QwtAbstractScaleDraw::maxTickLength() const +{ + double length = 0.0; + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + length = qMax( length, d_data->tickLength[i] ); + + return length; +} + +/*! + \brief Convert a value into its representing label + + The value is converted to a plain text using + QLocale::system().toString(value). + This method is often overloaded by applications to have individual + labels. + + \param value Value + \return Label string. +*/ +QwtText QwtAbstractScaleDraw::label( double value ) const +{ + return QLocale().toString( value ); +} + +/*! + \brief Convert a value into its representing label and cache it. + + The conversion between value and label is called very often + in the layout and painting code. Unfortunately the + calculation of the label sizes might be slow (really slow + for rich text in Qt4), so it's necessary to cache the labels. + + \param font Font + \param value Value + + \return Tick label +*/ +const QwtText &QwtAbstractScaleDraw::tickLabel( + const QFont &font, double value ) const +{ + QMap<double, QwtText>::const_iterator it = d_data->labelCache.find( value ); + if ( it == d_data->labelCache.end() ) + { + QwtText lbl = label( value ); + lbl.setRenderFlags( 0 ); + lbl.setLayoutAttribute( QwtText::MinimumLayout ); + + ( void )lbl.textSize( font ); // initialize the internal cache + + it = d_data->labelCache.insert( value, lbl ); + } + + return ( *it ); +} + +/*! + Invalidate the cache used by QwtAbstractScaleDraw::tickLabel + + The cache is invalidated, when a new QwtScaleDiv is set. If + the labels need to be changed. while the same QwtScaleDiv is set, + invalidateCache() needs to be called manually. +*/ +void QwtAbstractScaleDraw::invalidateCache() +{ + d_data->labelCache.clear(); +} diff --git a/src/libpcp_qwt/src/qwt_abstract_scale_draw.h b/src/libpcp_qwt/src/qwt_abstract_scale_draw.h new file mode 100644 index 0000000..1dd64c0 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_abstract_scale_draw.h @@ -0,0 +1,139 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_SCALE_DRAW_H +#define QWT_ABSTRACT_SCALE_DRAW_H + +#include "qwt_global.h" +#include "qwt_scale_div.h" +#include "qwt_text.h" + +class QPalette; +class QPainter; +class QFont; +class QwtScaleTransformation; +class QwtScaleMap; + +/*! + \brief A abstract base class for drawing scales + + QwtAbstractScaleDraw can be used to draw linear or logarithmic scales. + + After a scale division has been specified as a QwtScaleDiv object + using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s), + the scale can be drawn with the QwtAbstractScaleDraw::draw() member. +*/ +class QWT_EXPORT QwtAbstractScaleDraw +{ +public: + + /*! + Components of a scale + \sa enableComponent(), hasComponent + */ + enum ScaleComponent + { + //! Backbone = the line where the ticks are located + Backbone = 0x01, + + //! Ticks + Ticks = 0x02, + + //! Labels + Labels = 0x04 + }; + + //! Scale components + typedef QFlags<ScaleComponent> ScaleComponents; + + QwtAbstractScaleDraw(); + virtual ~QwtAbstractScaleDraw(); + + void setScaleDiv( const QwtScaleDiv &s ); + const QwtScaleDiv& scaleDiv() const; + + void setTransformation( QwtScaleTransformation * ); + const QwtScaleMap &scaleMap() const; + QwtScaleMap &scaleMap(); + + void enableComponent( ScaleComponent, bool enable = true ); + bool hasComponent( ScaleComponent ) const; + + void setTickLength( QwtScaleDiv::TickType, double length ); + double tickLength( QwtScaleDiv::TickType ) const; + double maxTickLength() const; + + void setSpacing( double margin ); + double spacing() const; + + void setPenWidth( int width ); + int penWidth() const; + + virtual void draw( QPainter *, const QPalette & ) const; + + virtual QwtText label( double ) const; + + /*! + Calculate the extent + + The extent is the distcance from the baseline to the outermost + pixel of the scale draw in opposite to its orientation. + It is at least minimumExtent() pixels. + + \sa setMinimumExtent(), minimumExtent() + */ + virtual double extent( const QFont & ) const = 0; + + void setMinimumExtent( double ); + double minimumExtent() const; + +protected: + /*! + Draw a tick + + \param painter Painter + \param value Value of the tick + \param len Lenght of the tick + + \sa drawBackbone(), drawLabel() + */ + virtual void drawTick( QPainter *painter, double value, double len ) const = 0; + + /*! + Draws the baseline of the scale + \param painter Painter + + \sa drawTick(), drawLabel() + */ + virtual void drawBackbone( QPainter *painter ) const = 0; + + /*! + Draws the label for a major scale tick + + \param painter Painter + \param value Value + + \sa drawTick(), drawBackbone() + */ + virtual void drawLabel( QPainter *painter, double value ) const = 0; + + void invalidateCache(); + const QwtText &tickLabel( const QFont &, double value ) const; + +private: + QwtAbstractScaleDraw( const QwtAbstractScaleDraw & ); + QwtAbstractScaleDraw &operator=( const QwtAbstractScaleDraw & ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtAbstractScaleDraw::ScaleComponents ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_abstract_slider.cpp b/src/libpcp_qwt/src/qwt_abstract_slider.cpp new file mode 100644 index 0000000..e2dbcff --- /dev/null +++ b/src/libpcp_qwt/src/qwt_abstract_slider.cpp @@ -0,0 +1,597 @@ +/* -*- 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_abstract_slider.h" +#include "qwt_math.h" +#include <qevent.h> +#include <qdatetime.h> + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#define qExp(x) ::exp(x) +#endif + +class QwtAbstractSlider::PrivateData +{ +public: + PrivateData(): + scrollMode( QwtAbstractSlider::ScrNone ), + mouseOffset( 0.0 ), + tracking( true ), + tmrID( 0 ), + updTime( 150 ), + mass( 0.0 ), + readOnly( false ) + { + } + + QwtAbstractSlider::ScrollMode scrollMode; + double mouseOffset; + int direction; + int tracking; + + int tmrID; + int updTime; + int timerTick; + QTime time; + double speed; + double mass; + Qt::Orientation orientation; + bool readOnly; +}; + +/*! + \brief Constructor + + \param orientation Orientation + \param parent Parent widget +*/ +QwtAbstractSlider::QwtAbstractSlider( + Qt::Orientation orientation, QWidget *parent ): + QWidget( parent, NULL ) +{ + d_data = new QwtAbstractSlider::PrivateData; + d_data->orientation = orientation; + + setFocusPolicy( Qt::TabFocus ); +} + +//! Destructor +QwtAbstractSlider::~QwtAbstractSlider() +{ + if ( d_data->tmrID ) + killTimer( d_data->tmrID ); + + delete d_data; +} + +/*! + En/Disable read only mode + + In read only mode the slider can't be controlled by mouse + or keyboard. + + \param readOnly Enables in case of true + \sa isReadOnly() +*/ +void QwtAbstractSlider::setReadOnly( bool readOnly ) +{ + d_data->readOnly = readOnly; + update(); +} + +/*! + In read only mode the slider can't be controlled by mouse + or keyboard. + + \return true if read only + \sa setReadOnly() +*/ +bool QwtAbstractSlider::isReadOnly() const +{ + return d_data->readOnly; +} + +/*! + \brief Set the orientation. + \param o Orientation. Allowed values are + Qt::Horizontal and Qt::Vertical. +*/ +void QwtAbstractSlider::setOrientation( Qt::Orientation o ) +{ + d_data->orientation = o; +} + +/*! + \return Orientation + \sa setOrientation() +*/ +Qt::Orientation QwtAbstractSlider::orientation() const +{ + return d_data->orientation; +} + +//! Stop updating if automatic scrolling is active + +void QwtAbstractSlider::stopMoving() +{ + if ( d_data->tmrID ) + { + killTimer( d_data->tmrID ); + d_data->tmrID = 0; + } +} + +/*! + \brief Specify the update interval for automatic scrolling + \param t update interval in milliseconds + \sa getScrollMode() +*/ +void QwtAbstractSlider::setUpdateTime( int t ) +{ + if ( t < 50 ) + t = 50; + d_data->updTime = t; +} + + +/*! + Mouse press event handler + \param e Mouse event +*/ +void QwtAbstractSlider::mousePressEvent( QMouseEvent *e ) +{ + if ( isReadOnly() ) + { + e->ignore(); + return; + } + if ( !isValid() ) + return; + + const QPoint &p = e->pos(); + + d_data->timerTick = 0; + + getScrollMode( p, d_data->scrollMode, d_data->direction ); + stopMoving(); + + switch ( d_data->scrollMode ) + { + case ScrPage: + case ScrTimer: + d_data->mouseOffset = 0; + d_data->tmrID = startTimer( qMax( 250, 2 * d_data->updTime ) ); + break; + + case ScrMouse: + d_data->time.start(); + d_data->speed = 0; + d_data->mouseOffset = getValue( p ) - value(); + Q_EMIT sliderPressed(); + break; + + default: + d_data->mouseOffset = 0; + d_data->direction = 0; + break; + } +} + + +//! Emits a valueChanged() signal if necessary +void QwtAbstractSlider::buttonReleased() +{ + if ( ( !d_data->tracking ) || ( value() != prevValue() ) ) + Q_EMIT valueChanged( value() ); +} + + +/*! + Mouse Release Event handler + \param e Mouse event +*/ +void QwtAbstractSlider::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( isReadOnly() ) + { + e->ignore(); + return; + } + if ( !isValid() ) + return; + + const double inc = step(); + + switch ( d_data->scrollMode ) + { + case ScrMouse: + { + setPosition( e->pos() ); + d_data->direction = 0; + d_data->mouseOffset = 0; + if ( d_data->mass > 0.0 ) + { + const int ms = d_data->time.elapsed(); + if ( ( qFabs( d_data->speed ) > 0.0 ) && ( ms < 50 ) ) + d_data->tmrID = startTimer( d_data->updTime ); + } + else + { + d_data->scrollMode = ScrNone; + buttonReleased(); + } + Q_EMIT sliderReleased(); + + break; + } + + case ScrDirect: + { + setPosition( e->pos() ); + d_data->direction = 0; + d_data->mouseOffset = 0; + d_data->scrollMode = ScrNone; + buttonReleased(); + break; + } + + case ScrPage: + { + stopMoving(); + if ( !d_data->timerTick ) + QwtDoubleRange::incPages( d_data->direction ); + d_data->timerTick = 0; + buttonReleased(); + d_data->scrollMode = ScrNone; + break; + } + + case ScrTimer: + { + stopMoving(); + if ( !d_data->timerTick ) + QwtDoubleRange::fitValue( value() + double( d_data->direction ) * inc ); + d_data->timerTick = 0; + buttonReleased(); + d_data->scrollMode = ScrNone; + break; + } + + default: + { + d_data->scrollMode = ScrNone; + buttonReleased(); + } + } +} + + +/*! + Move the slider to a specified point, adjust the value + and emit signals if necessary. +*/ +void QwtAbstractSlider::setPosition( const QPoint &p ) +{ + QwtDoubleRange::fitValue( getValue( p ) - d_data->mouseOffset ); +} + + +/*! + \brief Enables or disables tracking. + + If tracking is enabled, the slider emits a + valueChanged() signal whenever its value + changes (the default behaviour). If tracking + is disabled, the value changed() signal will only + be emitted if:<ul> + <li>the user releases the mouse + button and the value has changed or + <li>at the end of automatic scrolling.</ul> + Tracking is enabled by default. + \param enable \c true (enable) or \c false (disable) tracking. +*/ +void QwtAbstractSlider::setTracking( bool enable ) +{ + d_data->tracking = enable; +} + +/*! + Mouse Move Event handler + \param e Mouse event +*/ +void QwtAbstractSlider::mouseMoveEvent( QMouseEvent *e ) +{ + if ( isReadOnly() ) + { + e->ignore(); + return; + } + + if ( !isValid() ) + return; + + if ( d_data->scrollMode == ScrMouse ) + { + setPosition( e->pos() ); + if ( d_data->mass > 0.0 ) + { + double ms = double( d_data->time.elapsed() ); + if ( ms < 1.0 ) + ms = 1.0; + d_data->speed = ( exactValue() - exactPrevValue() ) / ms; + d_data->time.start(); + } + if ( value() != prevValue() ) + Q_EMIT sliderMoved( value() ); + } +} + +/*! + Wheel Event handler + \param e Whell event +*/ +void QwtAbstractSlider::wheelEvent( QWheelEvent *e ) +{ + if ( isReadOnly() ) + { + e->ignore(); + return; + } + + if ( !isValid() ) + return; + + QwtAbstractSlider::ScrollMode mode = ScrNone; + int direction = 0; + + // Give derived classes a chance to say ScrNone + getScrollMode( e->pos(), mode, direction ); + if ( mode != QwtAbstractSlider::ScrNone ) + { + // Most mouse types work in steps of 15 degrees, in which case + // the delta value is a multiple of 120 + + const int inc = e->delta() / 120; + QwtDoubleRange::incPages( inc ); + if ( value() != prevValue() ) + Q_EMIT sliderMoved( value() ); + } +} + +/*! + Handles key events + + - Key_Down, KeyLeft\n + Decrement by 1 + - Key_Up, Key_Right\n + Increment by 1 + + \param e Key event + \sa isReadOnly() +*/ +void QwtAbstractSlider::keyPressEvent( QKeyEvent *e ) +{ + if ( isReadOnly() ) + { + e->ignore(); + return; + } + + if ( !isValid() ) + return; + + int increment = 0; + switch ( e->key() ) + { + case Qt::Key_Down: + if ( orientation() == Qt::Vertical ) + increment = -1; + break; + case Qt::Key_Up: + if ( orientation() == Qt::Vertical ) + increment = 1; + break; + case Qt::Key_Left: + if ( orientation() == Qt::Horizontal ) + increment = -1; + break; + case Qt::Key_Right: + if ( orientation() == Qt::Horizontal ) + increment = 1; + break; + default:; + e->ignore(); + } + + if ( increment != 0 ) + { + QwtDoubleRange::incValue( increment ); + if ( value() != prevValue() ) + Q_EMIT sliderMoved( value() ); + } +} + +/*! + Qt timer event + \param e Timer event +*/ +void QwtAbstractSlider::timerEvent( QTimerEvent * ) +{ + const double inc = step(); + + switch ( d_data->scrollMode ) + { + case ScrMouse: + { + if ( d_data->mass > 0.0 ) + { + d_data->speed *= qExp( - double( d_data->updTime ) * 0.001 / d_data->mass ); + const double newval = + exactValue() + d_data->speed * double( d_data->updTime ); + QwtDoubleRange::fitValue( newval ); + // stop if d_data->speed < one step per second + if ( qFabs( d_data->speed ) < 0.001 * qFabs( step() ) ) + { + d_data->speed = 0; + stopMoving(); + buttonReleased(); + } + + } + else + stopMoving(); + break; + } + + case ScrPage: + { + QwtDoubleRange::incPages( d_data->direction ); + if ( !d_data->timerTick ) + { + killTimer( d_data->tmrID ); + d_data->tmrID = startTimer( d_data->updTime ); + } + break; + } + case ScrTimer: + { + QwtDoubleRange::fitValue( value() + double( d_data->direction ) * inc ); + if ( !d_data->timerTick ) + { + killTimer( d_data->tmrID ); + d_data->tmrID = startTimer( d_data->updTime ); + } + break; + } + default: + { + stopMoving(); + break; + } + } + + d_data->timerTick = 1; +} + + +/*! + Notify change of value + + This function can be reimplemented by derived classes + in order to keep track of changes, i.e. repaint the widget. + The default implementation emits a valueChanged() signal + if tracking is enabled. +*/ +void QwtAbstractSlider::valueChange() +{ + if ( d_data->tracking ) + Q_EMIT valueChanged( value() ); +} + +/*! + \brief Set the slider's mass for flywheel effect. + + If the slider's mass is greater then 0, it will continue + to move after the mouse button has been released. Its speed + decreases with time at a rate depending on the slider's mass. + A large mass means that it will continue to move for a + long time. + + Derived widgets may overload this function to make it public. + + \param val New mass in kg + + \bug If the mass is smaller than 1g, it is set to zero. + The maximal mass is limited to 100kg. + \sa mass() +*/ +void QwtAbstractSlider::setMass( double val ) +{ + if ( val < 0.001 ) + d_data->mass = 0.0; + else if ( val > 100.0 ) + d_data->mass = 100.0; + else + d_data->mass = val; +} + +/*! + \return mass + \sa setMass() +*/ +double QwtAbstractSlider::mass() const +{ + return d_data->mass; +} + + +/*! + \brief Move the slider to a specified value + + This function can be used to move the slider to a value + which is not an integer multiple of the step size. + \param val new value + \sa fitValue() +*/ +void QwtAbstractSlider::setValue( double val ) +{ + if ( d_data->scrollMode == ScrMouse ) + stopMoving(); + QwtDoubleRange::setValue( val ); +} + + +/*! + \brief Set the slider's value to the nearest integer multiple + of the step size. + + \param value Value + \sa setValue(), incValue() +*/ +void QwtAbstractSlider::fitValue( double value ) +{ + if ( d_data->scrollMode == ScrMouse ) + stopMoving(); + QwtDoubleRange::fitValue( value ); +} + +/*! + \brief Increment the value by a specified number of steps + \param steps number of steps + \sa setValue() +*/ +void QwtAbstractSlider::incValue( int steps ) +{ + if ( d_data->scrollMode == ScrMouse ) + stopMoving(); + QwtDoubleRange::incValue( steps ); +} + +/*! + \sa mouseOffset() +*/ +void QwtAbstractSlider::setMouseOffset( double offset ) +{ + d_data->mouseOffset = offset; +} + +/*! + \sa setMouseOffset() +*/ +double QwtAbstractSlider::mouseOffset() const +{ + return d_data->mouseOffset; +} + +//! sa ScrollMode +int QwtAbstractSlider::scrollMode() const +{ + return d_data->scrollMode; +} diff --git a/src/libpcp_qwt/src/qwt_abstract_slider.h b/src/libpcp_qwt/src/qwt_abstract_slider.h new file mode 100644 index 0000000..d9facbb --- /dev/null +++ b/src/libpcp_qwt/src/qwt_abstract_slider.h @@ -0,0 +1,188 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_ABSTRACT_SLIDER_H +#define QWT_ABSTRACT_SLIDER_H + +#include "qwt_global.h" +#include "qwt_double_range.h" +#include <qwidget.h> + +/*! + \brief An abstract base class for slider widgets + + QwtAbstractSlider is a base class for + slider widgets. It handles mouse events + and updates the slider's value accordingly. Derived classes + only have to implement the getValue() and + getScrollMode() members, and should react to a + valueChange(), which normally requires repainting. +*/ + +class QWT_EXPORT QwtAbstractSlider : public QWidget, public QwtDoubleRange +{ + Q_OBJECT + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool valid READ isValid WRITE setValid ) + Q_PROPERTY( double mass READ mass WRITE setMass ) + Q_PROPERTY( Qt::Orientation orientation + READ orientation WRITE setOrientation ) + +public: + /*! + Scroll mode + \sa getScrollMode() + */ + enum ScrollMode + { + //! Scrolling switched off. Don't change the value. + ScrNone, + + /*! + Change the value while the user keeps the + button pressed and moves the mouse. + */ + ScrMouse, + + /*! + Automatic scrolling. Increment the value in the specified direction + as long as the user keeps the button pressed. + */ + ScrTimer, + + ScrDirect, + + //! Automatic scrolling. Same as ScrTimer, but increment by page size. + ScrPage + }; + + explicit QwtAbstractSlider( Qt::Orientation, QWidget *parent = NULL ); + virtual ~QwtAbstractSlider(); + + void setUpdateTime( int t ); + void stopMoving(); + void setTracking( bool enable ); + + virtual void setMass( double val ); + virtual double mass() const; + + virtual void setOrientation( Qt::Orientation o ); + Qt::Orientation orientation() const; + + bool isReadOnly() const; + + /* + Wrappers for QwtDblRange::isValid/QwtDblRange::setValid made + to be available as Q_PROPERTY in the designer. + */ + + /*! + \sa QwtDblRange::isValid() + */ + bool isValid() const + { + return QwtDoubleRange::isValid(); + } + + /*! + \param valid true/false + \sa QwtDblRange::isValid() + */ + void setValid( bool valid ) + { + QwtDoubleRange::setValid( valid ); + } + +public Q_SLOTS: + virtual void setValue( double val ); + virtual void fitValue( double val ); + virtual void incValue( int steps ); + + virtual void setReadOnly( bool ); + +Q_SIGNALS: + + /*! + \brief Notify a change of value. + + In the default setting + (tracking enabled), this signal will be emitted every + time the value changes ( see setTracking() ). + \param value new value + */ + void valueChanged( double value ); + + /*! + This signal is emitted when the user presses the + movable part of the slider (start ScrMouse Mode). + */ + void sliderPressed(); + + /*! + This signal is emitted when the user releases the + movable part of the slider. + */ + + void sliderReleased(); + /*! + This signal is emitted when the user moves the + slider with the mouse. + \param value new value + */ + void sliderMoved( double value ); + +protected: + virtual void setPosition( const QPoint & ); + virtual void valueChange(); + + virtual void timerEvent( QTimerEvent *e ); + virtual void mousePressEvent( QMouseEvent *e ); + virtual void mouseReleaseEvent( QMouseEvent *e ); + virtual void mouseMoveEvent( QMouseEvent *e ); + virtual void keyPressEvent( QKeyEvent *e ); + virtual void wheelEvent( QWheelEvent *e ); + + /*! + \brief Determine the value corresponding to a specified poind + + This is an abstract virtual function which is called when + the user presses or releases a mouse button or moves the + mouse. It has to be implemented by the derived class. + \param p point + */ + virtual double getValue( const QPoint & p ) = 0; + + /*! + \brief Determine what to do when the user presses a mouse button. + + This function is abstract and has to be implemented by derived classes. + It is called on a mousePress event. The derived class can determine + what should happen next in dependence of the position where the mouse + was pressed by returning scrolling mode and direction. + + \param pos point where the mouse was pressed + \retval scrollMode The scrolling mode + \retval direction direction: 1, 0, or -1. + */ + virtual void getScrollMode( const QPoint &pos, + ScrollMode &scrollMode, int &direction ) const = 0; + + void setMouseOffset( double ); + double mouseOffset() const; + + int scrollMode() const; + +private: + void buttonReleased(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_analog_clock.cpp b/src/libpcp_qwt/src/qwt_analog_clock.cpp new file mode 100644 index 0000000..fe723d1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_analog_clock.cpp @@ -0,0 +1,228 @@ +/* -*- 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_analog_clock.h" +#include <qmath.h> + +/*! + Constructor + \param parent Parent widget +*/ +QwtAnalogClock::QwtAnalogClock( QWidget *parent ): + QwtDial( parent ) +{ + initClock(); +} + +void QwtAnalogClock::initClock() +{ + setWrapping( true ); + setReadOnly( true ); + + setOrigin( 270.0 ); + setRange( 0.0, 60.0 * 60.0 * 12.0 ); // seconds + setScale( -1, 5, 60.0 * 60.0 ); + + setScaleComponents( + QwtAbstractScaleDraw::Ticks | QwtAbstractScaleDraw::Labels ); + setScaleTicks( 1, 0, 8 ); + scaleDraw()->setSpacing( 8 ); + + QColor knobColor = palette().color( QPalette::Active, QPalette::Text ); + knobColor = knobColor.dark( 120 ); + + QColor handColor; + int width; + + for ( int i = 0; i < NHands; i++ ) + { + if ( i == SecondHand ) + { + width = 2; + handColor = knobColor.dark( 120 ); + } + else + { + width = 8; + handColor = knobColor; + } + + QwtDialSimpleNeedle *hand = new QwtDialSimpleNeedle( + QwtDialSimpleNeedle::Arrow, true, handColor, knobColor ); + hand->setWidth( width ); + + d_hand[i] = NULL; + setHand( ( Hand )i, hand ); + } +} + +//! Destructor +QwtAnalogClock::~QwtAnalogClock() +{ + for ( int i = 0; i < NHands; i++ ) + delete d_hand[i]; +} + +/*! + Nop method, use setHand instead + \sa setHand() +*/ +void QwtAnalogClock::setNeedle( QwtDialNeedle * ) +{ + // no op + return; +} + +/*! + Set a clockhand + \param hand Specifies the type of hand + \param needle Hand + \sa hand() +*/ +void QwtAnalogClock::setHand( Hand hand, QwtDialNeedle *needle ) +{ + if ( hand >= 0 || hand < NHands ) + { + delete d_hand[hand]; + d_hand[hand] = needle; + } +} + +/*! + \return Clock hand + \param hd Specifies the type of hand + \sa setHand() +*/ +QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) +{ + if ( hd < 0 || hd >= NHands ) + return NULL; + + return d_hand[hd]; +} + +/*! + \return Clock hand + \param hd Specifies the type of hand + \sa setHand() +*/ +const QwtDialNeedle *QwtAnalogClock::hand( Hand hd ) const +{ + return const_cast<QwtAnalogClock *>( this )->hand( hd ); +} + +/*! + \brief Set the current time + + This is the same as QwtAnalogClock::setTime(), but Qt < 3.0 + can't handle default parameters for slots. +*/ +void QwtAnalogClock::setCurrentTime() +{ + setTime( QTime::currentTime() ); +} + +/*! + Set a time + \param time Time to display +*/ +void QwtAnalogClock::setTime( const QTime &time ) +{ + if ( time.isValid() ) + { + setValue( ( time.hour() % 12 ) * 60.0 * 60.0 + + time.minute() * 60.0 + time.second() ); + } + else + setValid( false ); +} + +/*! + Find the scale label for a given value + + \param value Value + \return Label +*/ +QwtText QwtAnalogClock::scaleLabel( double value ) const +{ + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + value = 60.0 * 60.0 * 12.0; + + return QString::number( qRound( value / ( 60.0 * 60.0 ) ) ); +} + +/*! + \brief Draw the needle + + A clock has no single needle but three hands instead. drawNeedle + translates value() into directions for the hands and calls + drawHand(). + + \param painter Painter + \param center Center of the clock + \param radius Maximum length for the hands + \param dir Dummy, not used. + \param colorGroup ColorGroup + + \sa drawHand() +*/ +void QwtAnalogClock::drawNeedle( QPainter *painter, const QPointF ¢er, + double radius, double dir, QPalette::ColorGroup colorGroup ) const +{ + Q_UNUSED( dir ); + + if ( isValid() ) + { + const double hours = value() / ( 60.0 * 60.0 ); + const double minutes = + ( value() - qFloor(hours) * 60.0 * 60.0 ) / 60.0; + const double seconds = value() - qFloor(hours) * 60.0 * 60.0 + - qFloor(minutes) * 60.0; + + double angle[NHands]; + angle[HourHand] = 360.0 * hours / 12.0; + angle[MinuteHand] = 360.0 * minutes / 60.0; + angle[SecondHand] = 360.0 * seconds / 60.0; + + for ( int hand = 0; hand < NHands; hand++ ) + { + double d = angle[hand]; + if ( direction() == Clockwise ) + d = 360.0 - d; + + d -= origin(); + + drawHand( painter, ( Hand )hand, center, radius, d, colorGroup ); + } + } +} + +/*! + Draw a clock hand + + \param painter Painter + \param hd Specify the type of hand + \param center Center of the clock + \param radius Maximum length for the hands + \param direction Direction of the hand in degrees, counter clockwise + \param cg ColorGroup +*/ +void QwtAnalogClock::drawHand( QPainter *painter, Hand hd, + const QPointF ¢er, double radius, double direction, + QPalette::ColorGroup cg ) const +{ + const QwtDialNeedle *needle = hand( hd ); + if ( needle ) + { + if ( hd == HourHand ) + radius = qRound( 0.8 * radius ); + + needle->draw( painter, center, radius, direction, cg ); + } +} diff --git a/src/libpcp_qwt/src/qwt_analog_clock.h b/src/libpcp_qwt/src/qwt_analog_clock.h new file mode 100644 index 0000000..f20c3f1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_analog_clock.h @@ -0,0 +1,96 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_ANALOG_CLOCK_H +#define QWT_ANALOG_CLOCK_H + +#include "qwt_global.h" +#include "qwt_dial.h" +#include "qwt_dial_needle.h" +#include <qdatetime.h> + +/*! + \brief An analog clock + + \image html analogclock.png + + \par Example + \verbatim #include <qwt_analog_clock.h> + + QwtAnalogClock *clock = new QwtAnalogClock(...); + clock->scaleDraw()->setPenWidth(3); + clock->setLineWidth(6); + clock->setFrameShadow(QwtDial::Sunken); + clock->setTime(); + + // update the clock every second + QTimer *timer = new QTimer(clock); + timer->connect(timer, SIGNAL(timeout()), clock, SLOT(setCurrentTime())); + timer->start(1000); + + \endverbatim + + Qwt is missing a set of good looking hands. + Contributions are very welcome. + + \note The examples/dials example shows how to use QwtAnalogClock. +*/ + +class QWT_EXPORT QwtAnalogClock: public QwtDial +{ + Q_OBJECT + +public: + /*! + Hand type + \sa setHand(), hand() + */ + enum Hand + { + //! Needle displaying the seconds + SecondHand, + + //! Needle displaying the minutes + MinuteHand, + + //! Needle displaying the hours + HourHand, + + //! Number of needles + NHands + }; + + explicit QwtAnalogClock( QWidget* parent = NULL ); + virtual ~QwtAnalogClock(); + + virtual void setHand( Hand, QwtDialNeedle * ); + const QwtDialNeedle *hand( Hand ) const; + QwtDialNeedle *hand( Hand ); + +public Q_SLOTS: + void setCurrentTime(); + void setTime( const QTime & = QTime::currentTime() ); + +protected: + virtual QwtText scaleLabel( double ) const; + + virtual void drawNeedle( QPainter *, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; + + virtual void drawHand( QPainter *, Hand, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; + +private: + virtual void setNeedle( QwtDialNeedle * ); + void initClock(); + + QwtDialNeedle *d_hand[NHands]; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_arrow_button.cpp b/src/libpcp_qwt/src/qwt_arrow_button.cpp new file mode 100644 index 0000000..f451b44 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_arrow_button.cpp @@ -0,0 +1,332 @@ +/* -*- 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_arrow_button.h" +#include "qwt_math.h" +#include <qpainter.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qevent.h> +#include <qapplication.h> + +static const int MaxNum = 3; +static const int Margin = 2; +static const int Spacing = 1; + +class QwtArrowButton::PrivateData +{ +public: + int num; + Qt::ArrowType arrowType; +}; + +static QStyleOptionButton styleOpt( const QwtArrowButton* btn ) +{ + QStyleOptionButton option; + option.init( btn ); + option.features = QStyleOptionButton::None; + if ( btn->isFlat() ) + option.features |= QStyleOptionButton::Flat; + if ( btn->menu() ) + option.features |= QStyleOptionButton::HasMenu; + if ( btn->autoDefault() || btn->isDefault() ) + option.features |= QStyleOptionButton::AutoDefaultButton; + if ( btn->isDefault() ) + option.features |= QStyleOptionButton::DefaultButton; + if ( btn->isDown() ) + option.state |= QStyle::State_Sunken; + if ( !btn->isFlat() && !btn->isDown() ) + option.state |= QStyle::State_Raised; + + return option; +} + +/*! + \param num Number of arrows + \param arrowType see Qt::ArowType in the Qt docs. + \param parent Parent widget +*/ +QwtArrowButton::QwtArrowButton( int num, + Qt::ArrowType arrowType, QWidget *parent ): + QPushButton( parent ) +{ + d_data = new PrivateData; + d_data->num = qBound( 1, num, MaxNum ); + d_data->arrowType = arrowType; + + setAutoRepeat( true ); + setAutoDefault( false ); + + switch ( d_data->arrowType ) + { + case Qt::LeftArrow: + case Qt::RightArrow: + setSizePolicy( QSizePolicy::Expanding, + QSizePolicy::Fixed ); + break; + default: + setSizePolicy( QSizePolicy::Fixed, + QSizePolicy::Expanding ); + } +} + +//! Destructor +QwtArrowButton::~QwtArrowButton() +{ + delete d_data; + d_data = NULL; +} + +/*! + \brief The direction of the arrows +*/ +Qt::ArrowType QwtArrowButton::arrowType() const +{ + return d_data->arrowType; +} + +/*! + \brief The number of arrows +*/ +int QwtArrowButton::num() const +{ + return d_data->num; +} + +/*! + \return the bounding rect for the label +*/ +QRect QwtArrowButton::labelRect() const +{ + const int m = Margin; + + QRect r = rect(); + r.setRect( r.x() + m, r.y() + m, + r.width() - 2 * m, r.height() - 2 * m ); + + if ( isDown() ) + { + QStyleOptionButton option = styleOpt( this ); + const int ph = style()->pixelMetric( + QStyle::PM_ButtonShiftHorizontal, &option, this ); + const int pv = style()->pixelMetric( + QStyle::PM_ButtonShiftVertical, &option, this ); + + r.translate( ph, pv ); + } + + return r; +} + +/*! + Paint event handler + \param event Paint event +*/ +void QwtArrowButton::paintEvent( QPaintEvent *event ) +{ + QPushButton::paintEvent( event ); + QPainter painter( this ); + drawButtonLabel( &painter ); +} + +/*! + \brief Draw the button label + + \param painter Painter + \sa The Qt Manual on QPushButton +*/ +void QwtArrowButton::drawButtonLabel( QPainter *painter ) +{ + const bool isVertical = d_data->arrowType == Qt::UpArrow || + d_data->arrowType == Qt::DownArrow; + + const QRect r = labelRect(); + QSize boundingSize = labelRect().size(); + if ( isVertical ) + boundingSize.transpose(); + + const int w = + ( boundingSize.width() - ( MaxNum - 1 ) * Spacing ) / MaxNum; + + QSize arrow = arrowSize( Qt::RightArrow, + QSize( w, boundingSize.height() ) ); + + if ( isVertical ) + arrow.transpose(); + + QRect contentsSize; // aligned rect where to paint all arrows + if ( d_data->arrowType == Qt::LeftArrow || d_data->arrowType == Qt::RightArrow ) + { + contentsSize.setWidth( d_data->num * arrow.width() + + ( d_data->num - 1 ) * Spacing ); + contentsSize.setHeight( arrow.height() ); + } + else + { + contentsSize.setWidth( arrow.width() ); + contentsSize.setHeight( d_data->num * arrow.height() + + ( d_data->num - 1 ) * Spacing ); + } + + QRect arrowRect( contentsSize ); + arrowRect.moveCenter( r.center() ); + arrowRect.setSize( arrow ); + + painter->save(); + for ( int i = 0; i < d_data->num; i++ ) + { + drawArrow( painter, arrowRect, d_data->arrowType ); + + int dx = 0; + int dy = 0; + + if ( isVertical ) + dy = arrow.height() + Spacing; + else + dx = arrow.width() + Spacing; + + arrowRect.translate( dx, dy ); + } + painter->restore(); + + if ( hasFocus() ) + { + QStyleOptionFocusRect option; + option.init( this ); + option.backgroundColor = palette().color( QPalette::Window ); + + style()->drawPrimitive( QStyle::PE_FrameFocusRect, + &option, painter, this ); + } +} + +/*! + Draw an arrow int a bounding rect + + \param painter Painter + \param r Rectangle where to paint the arrow + \param arrowType Arrow type +*/ +void QwtArrowButton::drawArrow( QPainter *painter, + const QRect &r, Qt::ArrowType arrowType ) const +{ + QPolygon pa( 3 ); + + switch ( arrowType ) + { + case Qt::UpArrow: + pa.setPoint( 0, r.bottomLeft() ); + pa.setPoint( 1, r.bottomRight() ); + pa.setPoint( 2, r.center().x(), r.top() ); + break; + case Qt::DownArrow: + pa.setPoint( 0, r.topLeft() ); + pa.setPoint( 1, r.topRight() ); + pa.setPoint( 2, r.center().x(), r.bottom() ); + break; + case Qt::RightArrow: + pa.setPoint( 0, r.topLeft() ); + pa.setPoint( 1, r.bottomLeft() ); + pa.setPoint( 2, r.right(), r.center().y() ); + break; + case Qt::LeftArrow: + pa.setPoint( 0, r.topRight() ); + pa.setPoint( 1, r.bottomRight() ); + pa.setPoint( 2, r.left(), r.center().y() ); + break; + default: + break; + } + + painter->save(); + + painter->setPen( palette().color( QPalette::ButtonText ) ); + painter->setBrush( palette().brush( QPalette::ButtonText ) ); + painter->drawPolygon( pa ); + + painter->restore(); +} + +/*! + \return a size hint +*/ +QSize QwtArrowButton::sizeHint() const +{ + const QSize hint = minimumSizeHint(); + return hint.expandedTo( QApplication::globalStrut() ); +} + +/*! + \brief Return a minimum size hint +*/ +QSize QwtArrowButton::minimumSizeHint() const +{ + const QSize asz = arrowSize( Qt::RightArrow, QSize() ); + + QSize sz( + 2 * Margin + ( MaxNum - 1 ) * Spacing + MaxNum * asz.width(), + 2 * Margin + asz.height() + ); + + if ( d_data->arrowType == Qt::UpArrow || d_data->arrowType == Qt::DownArrow ) + sz.transpose(); + + QStyleOption styleOption; + styleOption.init( this ); + + sz = style()->sizeFromContents( QStyle::CT_PushButton, + &styleOption, sz, this ); + + return sz; +} + +/*! + Calculate the size for a arrow that fits into a rect of a given size + + \param arrowType Arrow type + \param boundingSize Bounding size + \return Size of the arrow +*/ +QSize QwtArrowButton::arrowSize( Qt::ArrowType arrowType, + const QSize &boundingSize ) const +{ + QSize bs = boundingSize; + if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow ) + bs.transpose(); + + const int MinLen = 2; + const QSize sz = bs.expandedTo( + QSize( MinLen, 2 * MinLen - 1 ) ); // minimum + + int w = sz.width(); + int h = 2 * w - 1; + + if ( h > sz.height() ) + { + h = sz.height(); + w = ( h + 1 ) / 2; + } + + QSize arrSize( w, h ); + if ( arrowType == Qt::UpArrow || arrowType == Qt::DownArrow ) + arrSize.transpose(); + + return arrSize; +} + +/*! + \brief autoRepeat for the space keys +*/ +void QwtArrowButton::keyPressEvent( QKeyEvent *event ) +{ + if ( event->isAutoRepeat() && event->key() == Qt::Key_Space ) + Q_EMIT clicked(); + + QPushButton::keyPressEvent( event ); +} diff --git a/src/libpcp_qwt/src/qwt_arrow_button.h b/src/libpcp_qwt/src/qwt_arrow_button.h new file mode 100644 index 0000000..ae436fe --- /dev/null +++ b/src/libpcp_qwt/src/qwt_arrow_button.h @@ -0,0 +1,52 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_ARROW_BUTTON_H +#define QWT_ARROW_BUTTON_H + +#include "qwt_global.h" +#include <qpushbutton.h> + +/*! + \brief Arrow Button + + A push button with one or more filled triangles on its front. + An Arrow button can have 1 to 3 arrows in a row, pointing + up, down, left or right. +*/ +class QWT_EXPORT QwtArrowButton : public QPushButton +{ +public: + explicit QwtArrowButton ( int num, Qt::ArrowType, QWidget *parent = NULL ); + virtual ~QwtArrowButton(); + + Qt::ArrowType arrowType() const; + int num() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + +protected: + virtual void paintEvent( QPaintEvent *event ); + + virtual void drawButtonLabel( QPainter *p ); + virtual void drawArrow( QPainter *, + const QRect &, Qt::ArrowType ) const; + virtual QRect labelRect() const; + virtual QSize arrowSize( Qt::ArrowType, + const QSize &boundingSize ) const; + + virtual void keyPressEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_clipper.cpp b/src/libpcp_qwt/src/qwt_clipper.cpp new file mode 100644 index 0000000..fea3fd4 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_clipper.cpp @@ -0,0 +1,486 @@ +/* -*- 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_clipper.h" +#include "qwt_point_polar.h" +#include <qrect.h> + +#if QT_VERSION < 0x040601 +#define qAtan(x) ::atan(x) +#endif + +namespace QwtClip +{ + // some templates used for inlining + template <class Point, typename T> class LeftEdge; + template <class Point, typename T> class RightEdge; + template <class Point, typename T> class TopEdge; + template <class Point, typename T> class BottomEdge; + + template <class Point> class PointBuffer; +} + +template <class Point, typename Value> +class QwtClip::LeftEdge +{ +public: + inline LeftEdge( Value x1, Value, Value, Value ): + d_x1( x1 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.x() >= d_x1; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); + return Point( d_x1, ( Value ) ( p2.y() + ( d_x1 - p2.x() ) * dy ) ); + } +private: + const Value d_x1; +}; + +template <class Point, typename Value> +class QwtClip::RightEdge +{ +public: + inline RightEdge( Value, Value x2, Value, Value ): + d_x2( x2 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.x() <= d_x2; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dy = ( p1.y() - p2.y() ) / double( p1.x() - p2.x() ); + return Point( d_x2, ( Value ) ( p2.y() + ( d_x2 - p2.x() ) * dy ) ); + } + +private: + const Value d_x2; +}; + +template <class Point, typename Value> +class QwtClip::TopEdge +{ +public: + inline TopEdge( Value, Value, Value y1, Value ): + d_y1( y1 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.y() >= d_y1; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); + return Point( ( Value )( p2.x() + ( d_y1 - p2.y() ) * dx ), d_y1 ); + } + +private: + const Value d_y1; +}; + +template <class Point, typename Value> +class QwtClip::BottomEdge +{ +public: + inline BottomEdge( Value, Value, Value, Value y2 ): + d_y2( y2 ) + { + } + + inline bool isInside( const Point &p ) const + { + return p.y() <= d_y2; + } + + inline Point intersection( const Point &p1, const Point &p2 ) const + { + double dx = ( p1.x() - p2.x() ) / double( p1.y() - p2.y() ); + return Point( ( Value )( p2.x() + ( d_y2 - p2.y() ) * dx ), d_y2 ); + } + +private: + const Value d_y2; +}; + +template<class Point> +class QwtClip::PointBuffer +{ +public: + PointBuffer( int capacity = 0 ): + m_capacity( 0 ), + m_size( 0 ), + m_buffer( NULL ) + { + if ( capacity > 0 ) + reserve( capacity ); + } + + ~PointBuffer() + { + if ( m_buffer ) + qFree( m_buffer ); + } + + inline void setPoints( int numPoints, const Point *points ) + { + reserve( numPoints ); + + m_size = numPoints; + qMemCopy( m_buffer, points, m_size * sizeof( Point ) ); + } + + inline void reset() + { + m_size = 0; + } + + inline int size() const + { + return m_size; + } + + inline Point *data() const + { + return m_buffer; + } + + inline Point &operator[]( int i ) + { + return m_buffer[i]; + } + + inline const Point &operator[]( int i ) const + { + return m_buffer[i]; + } + + inline void add( const Point &point ) + { + if ( m_capacity <= m_size ) + reserve( m_size + 1 ); + + m_buffer[m_size++] = point; + } + +private: + inline void reserve( int size ) + { + if ( m_capacity == 0 ) + m_capacity = 1; + + while ( m_capacity < size ) + m_capacity *= 2; + + m_buffer = ( Point * ) qRealloc( + m_buffer, m_capacity * sizeof( Point ) ); + } + + int m_capacity; + int m_size; + Point *m_buffer; +}; + +using namespace QwtClip; + +template <class Polygon, class Rect, class Point, typename T> +class QwtPolygonClipper +{ +public: + QwtPolygonClipper( const Rect &clipRect ): + d_clipRect( clipRect ) + { + } + + Polygon clipPolygon( const Polygon &polygon, bool closePolygon ) const + { +#if 0 + if ( d_clipRect.contains( polygon.boundingRect() ) ) + return polygon; +#endif + + PointBuffer<Point> points1; + PointBuffer<Point> points2( qMin( 256, polygon.size() ) ); + + points1.setPoints( polygon.size(), polygon.data() ); + + clipEdge< LeftEdge<Point, T> >( closePolygon, points1, points2 ); + clipEdge< RightEdge<Point, T> >( closePolygon, points2, points1 ); + clipEdge< TopEdge<Point, T> >( closePolygon, points1, points2 ); + clipEdge< BottomEdge<Point, T> >( closePolygon, points2, points1 ); + + Polygon p; + p.resize( points1.size() ); + qMemCopy( p.data(), points1.data(), points1.size() * sizeof( Point ) ); + + return p; + } + +private: + template <class Edge> + inline void clipEdge( bool closePolygon, + PointBuffer<Point> &points, PointBuffer<Point> &clippedPoints ) const + { + clippedPoints.reset(); + + if ( points.size() < 2 ) + { + if ( points.size() == 1 ) + clippedPoints.add( points[0] ); + return; + } + + const Edge edge( d_clipRect.x(), d_clipRect.x() + d_clipRect.width(), + d_clipRect.y(), d_clipRect.y() + d_clipRect.height() ); + + int lastPos, start; + if ( closePolygon ) + { + start = 0; + lastPos = points.size() - 1; + } + else + { + start = 1; + lastPos = 0; + + if ( edge.isInside( points[0] ) ) + clippedPoints.add( points[0] ); + } + + const uint nPoints = points.size(); + for ( uint i = start; i < nPoints; i++ ) + { + const Point &p1 = points[i]; + const Point &p2 = points[lastPos]; + + if ( edge.isInside( p1 ) ) + { + if ( edge.isInside( p2 ) ) + { + clippedPoints.add( p1 ); + } + else + { + clippedPoints.add( edge.intersection( p1, p2 ) ); + clippedPoints.add( p1 ); + } + } + else + { + if ( edge.isInside( p2 ) ) + { + clippedPoints.add( edge.intersection( p1, p2 ) ); + } + } + lastPos = i; + } + } + + const Rect d_clipRect; +}; + +class QwtCircleClipper +{ +public: + QwtCircleClipper( const QRectF &r ); + QVector<QwtInterval> clipCircle( const QPointF &, double radius ) const; + +private: + enum Edge + { + Left, + Top, + Right, + Bottom, + + NEdges + }; + + QList<QPointF> cuttingPoints( + Edge, const QPointF &pos, double radius ) const; + + double toAngle( const QPointF &, const QPointF & ) const; + + const QRectF d_rect; +}; + + +QwtCircleClipper::QwtCircleClipper( const QRectF &r ): + d_rect( r ) +{ +} + +QVector<QwtInterval> QwtCircleClipper::clipCircle( + const QPointF &pos, double radius ) const +{ + QList<QPointF> points; + for ( int edge = 0; edge < NEdges; edge++ ) + points += cuttingPoints( ( Edge )edge, pos, radius ); + + QVector<QwtInterval> intv; + if ( points.size() <= 0 ) + { + QRectF cRect( 0, 0, 2 * radius, 2 * radius ); + cRect.moveCenter( pos ); + if ( d_rect.contains( cRect ) ) + intv += QwtInterval( 0.0, 2 * M_PI ); + } + else + { + QList<double> angles; + for ( int i = 0; i < points.size(); i++ ) + angles += toAngle( pos, points[i] ); + qSort( angles ); + + const int in = d_rect.contains( qwtPolar2Pos( pos, radius, + angles[0] + ( angles[1] - angles[0] ) / 2 ) ); + + if ( in ) + { + for ( int i = 0; i < angles.size() - 1; i += 2 ) + intv += QwtInterval( angles[i], angles[i+1] ); + } + else + { + for ( int i = 1; i < angles.size() - 1; i += 2 ) + intv += QwtInterval( angles[i], angles[i+1] ); + intv += QwtInterval( angles.last(), angles.first() ); + } + } + + return intv; +} + +double QwtCircleClipper::toAngle( + const QPointF &from, const QPointF &to ) const +{ + if ( from.x() == to.x() ) + return from.y() <= to.y() ? M_PI / 2.0 : 3 * M_PI / 2.0; + + const double m = qAbs( ( to.y() - from.y() ) / ( to.x() - from.x() ) ); + + double angle = qAtan( m ); + if ( to.x() > from.x() ) + { + if ( to.y() > from.y() ) + angle = 2 * M_PI - angle; + } + else + { + if ( to.y() > from.y() ) + angle = M_PI + angle; + else + angle = M_PI - angle; + } + + return angle; +} + +QList<QPointF> QwtCircleClipper::cuttingPoints( + Edge edge, const QPointF &pos, double radius ) const +{ + QList<QPointF> points; + + if ( edge == Left || edge == Right ) + { + const double x = ( edge == Left ) ? d_rect.left() : d_rect.right(); + if ( qAbs( pos.x() - x ) < radius ) + { + const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.x() - x ) ); + const double m_y1 = pos.y() + off; + if ( m_y1 >= d_rect.top() && m_y1 <= d_rect.bottom() ) + points += QPointF( x, m_y1 ); + + const double m_y2 = pos.y() - off; + if ( m_y2 >= d_rect.top() && m_y2 <= d_rect.bottom() ) + points += QPointF( x, m_y2 ); + } + } + else + { + const double y = ( edge == Top ) ? d_rect.top() : d_rect.bottom(); + if ( qAbs( pos.y() - y ) < radius ) + { + const double off = qSqrt( qwtSqr( radius ) - qwtSqr( pos.y() - y ) ); + const double x1 = pos.x() + off; + if ( x1 >= d_rect.left() && x1 <= d_rect.right() ) + points += QPointF( x1, y ); + + const double m_x2 = pos.x() - off; + if ( m_x2 >= d_rect.left() && m_x2 <= d_rect.right() ) + points += QPointF( m_x2, y ); + } + } + return points; +} + +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygon QwtClipper::clipPolygon( + const QRect &clipRect, const QPolygon &polygon, bool closePolygon ) +{ + QwtPolygonClipper<QPolygon, QRect, QPoint, int> clipper( clipRect ); + return clipper.clipPolygon( polygon, closePolygon ); +} + +/*! + Sutherland-Hodgman polygon clipping + + \param clipRect Clip rectangle + \param polygon Polygon + \param closePolygon True, when the polygon is closed + + \return Clipped polygon +*/ +QPolygonF QwtClipper::clipPolygonF( + const QRectF &clipRect, const QPolygonF &polygon, bool closePolygon ) +{ + QwtPolygonClipper<QPolygonF, QRectF, QPointF, double> clipper( clipRect ); + return clipper.clipPolygon( polygon, closePolygon ); +} + +/*! + Circle clipping + + clipCircle() devides a circle into intervals of angles representing arcs + of the circle. When the circle is completely inside the clip rectangle + an interval [0.0, 2 * M_PI] is returned. + + \param clipRect Clip rectangle + \param center Center of the circle + \param radius Radius of the circle + + \return Arcs of the circle +*/ +QVector<QwtInterval> QwtClipper::clipCircle( const QRectF &clipRect, + const QPointF ¢er, double radius ) +{ + QwtCircleClipper clipper( clipRect ); + return clipper.clipCircle( center, radius ); +} diff --git a/src/libpcp_qwt/src/qwt_clipper.h b/src/libpcp_qwt/src/qwt_clipper.h new file mode 100644 index 0000000..98316d3 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_clipper.h @@ -0,0 +1,37 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_CLIPPER_H +#define QWT_CLIPPER_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include <qpolygon.h> +#include <qvector.h> + +class QRect; +class QRectF; + +/*! + \brief Some clipping algos +*/ + +class QWT_EXPORT QwtClipper +{ +public: + static QPolygon clipPolygon( const QRect &, + const QPolygon &, bool closePolygon = false ); + static QPolygonF clipPolygonF( const QRectF &, + const QPolygonF &, bool closePolygon = false ); + + static QVector<QwtInterval> clipCircle( + const QRectF &, const QPointF &, double radius ); +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_color_map.cpp b/src/libpcp_qwt/src/qwt_color_map.cpp new file mode 100644 index 0000000..4318e5a --- /dev/null +++ b/src/libpcp_qwt/src/qwt_color_map.cpp @@ -0,0 +1,440 @@ +/* -*- 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_color_map.h" +#include "qwt_math.h" +#include "qwt_interval.h" +#include <qnumeric.h> + +class QwtLinearColorMap::ColorStops +{ +public: + ColorStops() + { + _stops.reserve( 256 ); + } + + void insert( double pos, const QColor &color ); + QRgb rgb( QwtLinearColorMap::Mode, double pos ) const; + + QVector<double> stops() const; + +private: + + class ColorStop + { + public: + ColorStop(): + pos( 0.0 ), + rgb( 0 ) + { + }; + + ColorStop( double p, const QColor &c ): + pos( p ), + rgb( c.rgb() ) + { + r = qRed( rgb ); + g = qGreen( rgb ); + b = qBlue( rgb ); + } + + double pos; + QRgb rgb; + int r, g, b; + }; + + inline int findUpper( double pos ) const; + QVector<ColorStop> _stops; +}; + +void QwtLinearColorMap::ColorStops::insert( double pos, const QColor &color ) +{ + // Lookups need to be very fast, insertions are not so important. + // Anyway, a balanced tree is what we need here. TODO ... + + if ( pos < 0.0 || pos > 1.0 ) + return; + + int index; + if ( _stops.size() == 0 ) + { + index = 0; + _stops.resize( 1 ); + } + else + { + index = findUpper( pos ); + if ( index == _stops.size() || + qAbs( _stops[index].pos - pos ) >= 0.001 ) + { + _stops.resize( _stops.size() + 1 ); + for ( int i = _stops.size() - 1; i > index; i-- ) + _stops[i] = _stops[i-1]; + } + } + + _stops[index] = ColorStop( pos, color ); +} + +inline QVector<double> QwtLinearColorMap::ColorStops::stops() const +{ + QVector<double> positions( _stops.size() ); + for ( int i = 0; i < _stops.size(); i++ ) + positions[i] = _stops[i].pos; + return positions; +} + +inline int QwtLinearColorMap::ColorStops::findUpper( double pos ) const +{ + int index = 0; + int n = _stops.size(); + + const ColorStop *stops = _stops.data(); + + while ( n > 0 ) + { + const int half = n >> 1; + const int middle = index + half; + + if ( stops[middle].pos <= pos ) + { + index = middle + 1; + n -= half + 1; + } + else + n = half; + } + + return index; +} + +inline QRgb QwtLinearColorMap::ColorStops::rgb( + QwtLinearColorMap::Mode mode, double pos ) const +{ + if ( pos <= 0.0 ) + return _stops[0].rgb; + if ( pos >= 1.0 ) + return _stops[ _stops.size() - 1 ].rgb; + + const int index = findUpper( pos ); + if ( mode == FixedColors ) + { + return _stops[index-1].rgb; + } + else + { + const ColorStop &s1 = _stops[index-1]; + const ColorStop &s2 = _stops[index]; + + const double ratio = ( pos - s1.pos ) / ( s2.pos - s1.pos ); + + const int r = s1.r + qRound( ratio * ( s2.r - s1.r ) ); + const int g = s1.g + qRound( ratio * ( s2.g - s1.g ) ); + const int b = s1.b + qRound( ratio * ( s2.b - s1.b ) ); + + return qRgb( r, g, b ); + } +} + +//! Constructor +QwtColorMap::QwtColorMap( Format format ): + d_format( format ) +{ +} + +//! Destructor +QwtColorMap::~QwtColorMap() +{ +} + +/*! + Build and return a color map of 256 colors + + The color table is needed for rendering indexed images in combination + with using colorIndex(). + + \param interval Range for the values + \return A color table, that can be used for a QImage +*/ +QVector<QRgb> QwtColorMap::colorTable( const QwtInterval &interval ) const +{ + QVector<QRgb> table( 256 ); + + if ( interval.isValid() ) + { + const double step = interval.width() / ( table.size() - 1 ); + for ( int i = 0; i < table.size(); i++ ) + table[i] = rgb( interval, interval.minValue() + step * i ); + } + + return table; +} + +class QwtLinearColorMap::PrivateData +{ +public: + ColorStops colorStops; + QwtLinearColorMap::Mode mode; +}; + +/*! + Build a color map with two stops at 0.0 and 1.0. The color + at 0.0 is Qt::blue, at 1.0 it is Qt::yellow. + + \param format Preferred format of the color map +*/ +QwtLinearColorMap::QwtLinearColorMap( QwtColorMap::Format format ): + QwtColorMap( format ) +{ + d_data = new PrivateData; + d_data->mode = ScaledColors; + + setColorInterval( Qt::blue, Qt::yellow ); +} + +/*! + Build a color map with two stops at 0.0 and 1.0. + + \param color1 Color used for the minimum value of the value interval + \param color2 Color used for the maximum value of the value interval + \param format Preferred format of the coor map +*/ +QwtLinearColorMap::QwtLinearColorMap( const QColor &color1, + const QColor &color2, QwtColorMap::Format format ): + QwtColorMap( format ) +{ + d_data = new PrivateData; + d_data->mode = ScaledColors; + setColorInterval( color1, color2 ); +} + +//! Destructor +QwtLinearColorMap::~QwtLinearColorMap() +{ + delete d_data; +} + +/*! + \brief Set the mode of the color map + + FixedColors means the color is calculated from the next lower + color stop. ScaledColors means the color is calculated + by interpolating the colors of the adjacent stops. + + \sa mode() +*/ +void QwtLinearColorMap::setMode( Mode mode ) +{ + d_data->mode = mode; +} + +/*! + \return Mode of the color map + \sa setMode() +*/ +QwtLinearColorMap::Mode QwtLinearColorMap::mode() const +{ + return d_data->mode; +} + +/*! + Set the color range + + Add stops at 0.0 and 1.0. + + \param color1 Color used for the minimum value of the value interval + \param color2 Color used for the maximum value of the value interval + + \sa color1(), color2() +*/ +void QwtLinearColorMap::setColorInterval( + const QColor &color1, const QColor &color2 ) +{ + d_data->colorStops = ColorStops(); + d_data->colorStops.insert( 0.0, color1 ); + d_data->colorStops.insert( 1.0, color2 ); +} + +/*! + Add a color stop + + The value has to be in the range [0.0, 1.0]. + F.e. a stop at position 17.0 for a range [10.0,20.0] must be + passed as: (17.0 - 10.0) / (20.0 - 10.0) + + \param value Value between [0.0, 1.0] + \param color Color stop +*/ +void QwtLinearColorMap::addColorStop( double value, const QColor& color ) +{ + if ( value >= 0.0 && value <= 1.0 ) + d_data->colorStops.insert( value, color ); +} + +/*! + Return all positions of color stops in increasing order +*/ +QVector<double> QwtLinearColorMap::colorStops() const +{ + return d_data->colorStops.stops(); +} + +/*! + \return the first color of the color range + \sa setColorInterval() +*/ +QColor QwtLinearColorMap::color1() const +{ + return QColor( d_data->colorStops.rgb( d_data->mode, 0.0 ) ); +} + +/*! + \return the second color of the color range + \sa setColorInterval() +*/ +QColor QwtLinearColorMap::color2() const +{ + return QColor( d_data->colorStops.rgb( d_data->mode, 1.0 ) ); +} + +/*! + Map a value of a given interval into a rgb value + + \param interval Range for all values + \param value Value to map into a rgb value +*/ +QRgb QwtLinearColorMap::rgb( + const QwtInterval &interval, double value ) const +{ + if ( qIsNaN(value) ) + return qRgba(0, 0, 0, 0); + + const double width = interval.width(); + + double ratio = 0.0; + if ( width > 0.0 ) + ratio = ( value - interval.minValue() ) / width; + + return d_data->colorStops.rgb( d_data->mode, ratio ); +} + +/*! + Map a value of a given interval into a color index, between 0 and 255 + + \param interval Range for all values + \param value Value to map into a color index +*/ +unsigned char QwtLinearColorMap::colorIndex( + const QwtInterval &interval, double value ) const +{ + const double width = interval.width(); + + if ( qIsNaN(value) || width <= 0.0 || value <= interval.minValue() ) + return 0; + + if ( value >= interval.maxValue() ) + return ( unsigned char )255; + + const double ratio = ( value - interval.minValue() ) / width; + + unsigned char index; + if ( d_data->mode == FixedColors ) + index = ( unsigned char )( ratio * 255 ); // always floor + else + index = ( unsigned char )qRound( ratio * 255 ); + + return index; +} + +class QwtAlphaColorMap::PrivateData +{ +public: + QColor color; + QRgb rgb; +}; + + +/*! + Constructor + \param color Color of the map +*/ +QwtAlphaColorMap::QwtAlphaColorMap( const QColor &color ): + QwtColorMap( QwtColorMap::RGB ) +{ + d_data = new PrivateData; + d_data->color = color; + d_data->rgb = color.rgb() & qRgba( 255, 255, 255, 0 ); +} + +//! Destructor +QwtAlphaColorMap::~QwtAlphaColorMap() +{ + delete d_data; +} + +/*! + Set the color + + \param color Color + \sa color() +*/ +void QwtAlphaColorMap::setColor( const QColor &color ) +{ + d_data->color = color; + d_data->rgb = color.rgb(); +} + +/*! + \return the color + \sa setColor() +*/ +QColor QwtAlphaColorMap::color() const +{ + return d_data->color; +} + +/*! + \brief Map a value of a given interval into a alpha value + + alpha := (value - interval.minValue()) / interval.width(); + + \param interval Range for all values + \param value Value to map into a rgb value + \return rgb value, with an alpha value +*/ +QRgb QwtAlphaColorMap::rgb( const QwtInterval &interval, double value ) const +{ + const double width = interval.width(); + if ( !qIsNaN(value) && width >= 0.0 ) + { + const double ratio = ( value - interval.minValue() ) / width; + int alpha = qRound( 255 * ratio ); + if ( alpha < 0 ) + alpha = 0; + if ( alpha > 255 ) + alpha = 255; + + return d_data->rgb | ( alpha << 24 ); + } + return d_data->rgb; +} + +/*! + Dummy function, needed to be implemented as it is pure virtual + in QwtColorMap. Color indices make no sense in combination with + an alpha channel. + + \return Always 0 +*/ +unsigned char QwtAlphaColorMap::colorIndex( + const QwtInterval &, double ) const +{ + return 0; +} diff --git a/src/libpcp_qwt/src/qwt_color_map.h b/src/libpcp_qwt/src/qwt_color_map.h new file mode 100644 index 0000000..e754830 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_color_map.h @@ -0,0 +1,198 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_COLOR_MAP_H +#define QWT_COLOR_MAP_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include <qcolor.h> +#include <qvector.h> + +/*! + \brief QwtColorMap is used to map values into colors. + + For displaying 3D data on a 2D plane the 3rd dimension is often + displayed using colors, like f.e in a spectrogram. + + Each color map is optimized to return colors for only one of the + following image formats: + + - QImage::Format_Indexed8\n + - QImage::Format_ARGB32\n + + \sa QwtPlotSpectrogram, QwtScaleWidget +*/ + +class QWT_EXPORT QwtColorMap +{ +public: + /*! + Format for color mapping + \sa rgb(), colorIndex(), colorTable() + */ + + enum Format + { + //! The map is intended to map into QRgb values. + RGB, + + /*! + The map is intended to map into 8 bit values, that + are indices into the color table. + */ + Indexed + }; + + QwtColorMap( Format = QwtColorMap::RGB ); + virtual ~QwtColorMap(); + + Format format() const; + + /*! + Map a value of a given interval into a rgb value. + \param interval Range for the values + \param value Value + \return rgb value, corresponding to value + */ + virtual QRgb rgb( const QwtInterval &interval, + double value ) const = 0; + + /*! + Map a value of a given interval into a color index + \param interval Range for the values + \param value Value + \return color index, corresponding to value + */ + virtual unsigned char colorIndex( + const QwtInterval &interval, double value ) const = 0; + + QColor color( const QwtInterval &, double value ) const; + virtual QVector<QRgb> colorTable( const QwtInterval & ) const; + +private: + Format d_format; +}; + +/*! + \brief QwtLinearColorMap builds a color map from color stops. + + A color stop is a color at a specific position. The valid + range for the positions is [0.0, 1.0]. When mapping a value + into a color it is translated into this interval according to mode(). +*/ +class QWT_EXPORT QwtLinearColorMap: public QwtColorMap +{ +public: + /*! + Mode of color map + \sa setMode(), mode() + */ + enum Mode + { + //! Return the color from the next lower color stop + FixedColors, + + //! Interpolating the colors of the adjacent stops. + ScaledColors + }; + + QwtLinearColorMap( QwtColorMap::Format = QwtColorMap::RGB ); + QwtLinearColorMap( const QColor &from, const QColor &to, + QwtColorMap::Format = QwtColorMap::RGB ); + + virtual ~QwtLinearColorMap(); + + void setMode( Mode ); + Mode mode() const; + + void setColorInterval( const QColor &color1, const QColor &color2 ); + void addColorStop( double value, const QColor& ); + QVector<double> colorStops() const; + + QColor color1() const; + QColor color2() const; + + virtual QRgb rgb( const QwtInterval &, double value ) const; + virtual unsigned char colorIndex( + const QwtInterval &, double value ) const; + + class ColorStops; + +private: + // Disabled copy constructor and operator= + QwtLinearColorMap( const QwtLinearColorMap & ); + QwtLinearColorMap &operator=( const QwtLinearColorMap & ); + + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief QwtAlphaColorMap variies the alpha value of a color +*/ +class QWT_EXPORT QwtAlphaColorMap: public QwtColorMap +{ +public: + QwtAlphaColorMap( const QColor & = QColor( Qt::gray ) ); + virtual ~QwtAlphaColorMap(); + + void setColor( const QColor & ); + QColor color() const; + + virtual QRgb rgb( const QwtInterval &, double value ) const; + +private: + QwtAlphaColorMap( const QwtAlphaColorMap & ); + QwtAlphaColorMap &operator=( const QwtAlphaColorMap & ); + + virtual unsigned char colorIndex( + const QwtInterval &, double value ) const; + + class PrivateData; + PrivateData *d_data; +}; + + +/*! + Map a value into a color + + \param interval Valid interval for values + \param value Value + + \return Color corresponding to value + + \warning This method is slow for Indexed color maps. If it is + necessary to map many values, its better to get the + color table once and find the color using colorIndex(). +*/ +inline QColor QwtColorMap::color( + const QwtInterval &interval, double value ) const +{ + if ( d_format == RGB ) + { + return QColor( rgb( interval, value ) ); + } + else + { + const unsigned int index = colorIndex( interval, value ); + return colorTable( interval )[index]; // slow + } +} + +/*! + \return Intended format of the color map + \sa Format +*/ +inline QwtColorMap::Format QwtColorMap::format() const +{ + return d_format; +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_column_symbol.cpp b/src/libpcp_qwt/src/qwt_column_symbol.cpp new file mode 100644 index 0000000..ecbcd9a --- /dev/null +++ b/src/libpcp_qwt/src/qwt_column_symbol.cpp @@ -0,0 +1,293 @@ +/* -*- 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_column_symbol.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include <qpainter.h> +#include <qpalette.h> + +static void drawBox( QPainter *p, const QRectF &rect, + const QPalette &pal, double lw ) +{ + if ( lw > 0.0 ) + { + if ( rect.width() == 0.0 ) + { + p->setPen( pal.dark().color() ); + p->drawLine( rect.topLeft(), rect.bottomLeft() ); + return; + } + + if ( rect.height() == 0.0 ) + { + p->setPen( pal.dark().color() ); + p->drawLine( rect.topLeft(), rect.topRight() ); + return; + } + + lw = qMin( lw, rect.height() / 2.0 - 1.0 ); + lw = qMin( lw, rect.width() / 2.0 - 1.0 ); + + const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); + QPolygonF polygon( outerRect ); + + if ( outerRect.width() > 2 * lw && + outerRect.height() > 2 * lw ) + { + const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); + polygon = polygon.subtracted( innerRect ); + } + + p->setPen( Qt::NoPen ); + + p->setBrush( pal.dark() ); + p->drawPolygon( polygon ); + } + + const QRectF windowRect = rect.adjusted( lw, lw, -lw + 1, -lw + 1 ); + if ( windowRect.isValid() ) + p->fillRect( windowRect, pal.window() ); +} + +static void drawPanel( QPainter *painter, const QRectF &rect, + const QPalette &pal, double lw ) +{ + if ( lw > 0.0 ) + { + if ( rect.width() == 0.0 ) + { + painter->setPen( pal.window().color() ); + painter->drawLine( rect.topLeft(), rect.bottomLeft() ); + return; + } + + if ( rect.height() == 0.0 ) + { + painter->setPen( pal.window().color() ); + painter->drawLine( rect.topLeft(), rect.topRight() ); + return; + } + + lw = qMin( lw, rect.height() / 2.0 - 1.0 ); + lw = qMin( lw, rect.width() / 2.0 - 1.0 ); + + const QRectF outerRect = rect.adjusted( 0, 0, 1, 1 ); + const QRectF innerRect = outerRect.adjusted( lw, lw, -lw, -lw ); + + QPolygonF lines[2]; + + lines[0] += outerRect.bottomLeft(); + lines[0] += outerRect.topLeft(); + lines[0] += outerRect.topRight(); + lines[0] += innerRect.topRight(); + lines[0] += innerRect.topLeft(); + lines[0] += innerRect.bottomLeft(); + + lines[1] += outerRect.topRight(); + lines[1] += outerRect.bottomRight(); + lines[1] += outerRect.bottomLeft(); + lines[1] += innerRect.bottomLeft(); + lines[1] += innerRect.bottomRight(); + lines[1] += innerRect.topRight(); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( pal.light() ); + painter->drawPolygon( lines[0] ); + painter->setBrush( pal.dark() ); + painter->drawPolygon( lines[1] ); + } + + painter->fillRect( rect.adjusted( lw, lw, -lw + 1, -lw + 1 ), pal.window() ); +} + +class QwtColumnSymbol::PrivateData +{ +public: + PrivateData(): + style( QwtColumnSymbol::Box ), + frameStyle( QwtColumnSymbol::Raised ), + lineWidth( 2 ) + { + palette = QPalette( Qt::gray ); + } + + QwtColumnSymbol::Style style; + QwtColumnSymbol::FrameStyle frameStyle; + + QPalette palette; + int lineWidth; +}; + +/*! + Constructor + + \param style Style of the symbol + \sa setStyle(), style(), Style +*/ +QwtColumnSymbol::QwtColumnSymbol( Style style ) +{ + d_data = new PrivateData(); + d_data->style = style; +} + +//! Destructor +QwtColumnSymbol::~QwtColumnSymbol() +{ + delete d_data; +} + +/*! + Specify the symbol style + + \param style Style + \sa style(), setPalette() +*/ +void QwtColumnSymbol::setStyle( Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtColumnSymbol::Style QwtColumnSymbol::style() const +{ + return d_data->style; +} + +/*! + Assign a palette for the symbol + + \param palette Palette + \sa palette(), setStyle() +*/ +void QwtColumnSymbol::setPalette( const QPalette &palette ) +{ + d_data->palette = palette; +} + +/*! + \return Current palette + \sa setPalette() +*/ +const QPalette& QwtColumnSymbol::palette() const +{ + return d_data->palette; +} + +/*! + Set the frame, that is used for the Box style. + + \param frameStyle Frame style + \sa frameStyle(), setLineWidth(), setStyle() +*/ +void QwtColumnSymbol::setFrameStyle( FrameStyle frameStyle ) +{ + d_data->frameStyle = frameStyle; +} + +/*! + \return Current frame style, that is used for the Box style. + \sa setFrameStyle(), lineWidth(), setStyle() +*/ +QwtColumnSymbol::FrameStyle QwtColumnSymbol::frameStyle() const +{ + return d_data->frameStyle; +} + +/*! + Set the line width of the frame, that is used for the Box style. + + \param width Width + \sa lineWidth(), setFrameStyle() +*/ +void QwtColumnSymbol::setLineWidth( int width ) +{ + if ( width < 0 ) + width = 0; + + d_data->lineWidth = width; +} + +/*! + \return Line width of the frame, that is used for the Box style. + \sa setLineWidth(), frameStyle(), setStyle() +*/ +int QwtColumnSymbol::lineWidth() const +{ + return d_data->lineWidth; +} + +/*! + Draw the symbol depending on its style. + + \param painter Painter + \param rect Directed rectangle + + \sa drawBox() +*/ +void QwtColumnSymbol::draw( QPainter *painter, + const QwtColumnRect &rect ) const +{ + painter->save(); + + switch ( d_data->style ) + { + case QwtColumnSymbol::Box: + { + drawBox( painter, rect ); + break; + } + default:; + } + + painter->restore(); +} + +/*! + Draw the symbol when it is in Box style. + + \param painter Painter + \param rect Directed rectangle + + \sa draw() +*/ +void QwtColumnSymbol::drawBox( QPainter *painter, + const QwtColumnRect &rect ) const +{ + QRectF r = rect.toRect(); + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + switch ( d_data->frameStyle ) + { + case QwtColumnSymbol::Raised: + { + ::drawPanel( painter, r, d_data->palette, d_data->lineWidth ); + break; + } + case QwtColumnSymbol::Plain: + { + ::drawBox( painter, r, d_data->palette, d_data->lineWidth ); + break; + } + default: + { + painter->fillRect( r, d_data->palette.window() ); + } + } +} diff --git a/src/libpcp_qwt/src/qwt_column_symbol.h b/src/libpcp_qwt/src/qwt_column_symbol.h new file mode 100644 index 0000000..3c278f1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_column_symbol.h @@ -0,0 +1,161 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_COLUMN_SYMBOL_H +#define QWT_COLUMN_SYMBOL_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include <qpen.h> +#include <qsize.h> +#include <qrect.h> + +class QPainter; +class QPalette; +class QRect; +class QwtText; + +/*! + \brief Directed rectangle representing bounding rectangle und orientation + of a column. +*/ +class QWT_EXPORT QwtColumnRect +{ +public: + //! Direction of the column + enum Direction + { + //! From left to right + LeftToRight, + + //! From right to left + RightToLeft, + + //! From bottom to top + BottomToTop, + + //! From top to bottom + TopToBottom + }; + + //! Build an rectangle with invalid intervals directed BottomToTop. + QwtColumnRect(): + direction( BottomToTop ) + { + } + + //! \return A normalized QRect built from the intervals + QRectF toRect() const + { + QRectF r( hInterval.minValue(), vInterval.minValue(), + hInterval.maxValue() - hInterval.minValue(), + vInterval.maxValue() - vInterval.minValue() ); + r = r.normalized(); + + if ( hInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + r.adjust( 1, 0, 0, 0 ); + if ( hInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + r.adjust( 0, 0, -1, 0 ); + if ( vInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + r.adjust( 0, 1, 0, 0 ); + if ( vInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + r.adjust( 0, 0, 0, -1 ); + + return r; + } + + //! \return Orientation + Qt::Orientation orientation() const + { + if ( direction == LeftToRight || direction == RightToLeft ) + return Qt::Horizontal; + + return Qt::Vertical; + } + + //! Interval for the horizontal coordinates + QwtInterval hInterval; + + //! Interval for the vertical coordinates + QwtInterval vInterval; + + //! Direction + Direction direction; +}; + +//! A drawing primitive for columns +class QWT_EXPORT QwtColumnSymbol +{ +public: + /*! + Style + \sa setStyle(), style() + */ + enum Style + { + //! No Style, the symbol draws nothing + NoStyle = -1, + + /*! + The column is painted with a frame depending on the frameStyle() + and lineWidth() using the palette(). + */ + Box, + + /*! + Styles >= QwtColumnSymbol::UserStyle are reserved for derived + classes of QwtColumnSymbol that overload draw() with + additional application specific symbol types. + */ + UserStyle = 1000 + }; + + /*! + Frame Style used in Box style(). + \sa Style, setFrameStyle(), frameStyle(), setStyle(), setPalette() + */ + enum FrameStyle + { + //! No frame + NoFrame, + + //! A plain frame style + Plain, + + //! A raised frame style + Raised + }; + +public: + QwtColumnSymbol( Style = NoStyle ); + virtual ~QwtColumnSymbol(); + + void setFrameStyle( FrameStyle style ); + FrameStyle frameStyle() const; + + void setLineWidth( int width ); + int lineWidth() const; + + void setPalette( const QPalette & ); + const QPalette &palette() const; + + void setStyle( Style ); + Style style() const; + + virtual void draw( QPainter *, const QwtColumnRect & ) const; + +protected: + void drawBox( QPainter *, const QwtColumnRect & ) const; + +private: + class PrivateData; + PrivateData* d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_compass.cpp b/src/libpcp_qwt/src/qwt_compass.cpp new file mode 100644 index 0000000..5ee3e4e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_compass.cpp @@ -0,0 +1,292 @@ +/* -*- 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_compass.h" +#include "qwt_compass_rose.h" +#include "qwt_math.h" +#include "qwt_scale_draw.h" +#include "qwt_painter.h" +#include "qwt_dial_needle.h" +#include <qpainter.h> +#include <qpixmap.h> +#include <qevent.h> + +class QwtCompass::PrivateData +{ +public: + PrivateData(): + rose( NULL ) + { + } + + ~PrivateData() + { + delete rose; + } + + QwtCompassRose *rose; + QMap<double, QString> labelMap; +}; + +/*! + \brief Constructor + \param parent Parent widget + + Create a compass widget with a scale, no needle and no rose. + The default origin is 270.0 with no valid value. It accepts + mouse and keyboard inputs and has no step size. The default mode + is QwtDial::RotateNeedle. +*/ +QwtCompass::QwtCompass( QWidget* parent ): + QwtDial( parent ) +{ + initCompass(); +} + +//! Destructor +QwtCompass::~QwtCompass() +{ + delete d_data; +} + +void QwtCompass::initCompass() +{ + d_data = new PrivateData; + + // Only labels, no backbone, no ticks + setScaleComponents( QwtAbstractScaleDraw::Labels ); + + setOrigin( 270.0 ); + setWrapping( true ); + + + d_data->labelMap.insert( 0.0, QString::fromLatin1( "N" ) ); + d_data->labelMap.insert( 45.0, QString::fromLatin1( "NE" ) ); + d_data->labelMap.insert( 90.0, QString::fromLatin1( "E" ) ); + d_data->labelMap.insert( 135.0, QString::fromLatin1( "SE" ) ); + d_data->labelMap.insert( 180.0, QString::fromLatin1( "S" ) ); + d_data->labelMap.insert( 225.0, QString::fromLatin1( "SW" ) ); + d_data->labelMap.insert( 270.0, QString::fromLatin1( "W" ) ); + d_data->labelMap.insert( 315.0, QString::fromLatin1( "NW" ) ); + +#if 0 + d_data->labelMap.insert( 22.5, QString::fromLatin1( "NNE" ) ); + d_data->labelMap.insert( 67.5, QString::fromLatin1( "NEE" ) ); + d_data->labelMap.insert( 112.5, QString::fromLatin1( "SEE" ) ); + d_data->labelMap.insert( 157.5, QString::fromLatin1( "SSE" ) ); + d_data->labelMap.insert( 202.5, QString::fromLatin1( "SSW" ) ); + d_data->labelMap.insert( 247.5, QString::fromLatin1( "SWW" ) ); + d_data->labelMap.insert( 292.5, QString::fromLatin1( "NWW" ) ); + d_data->labelMap.insert( 337.5, QString::fromLatin1( "NNW" ) ); +#endif +} + +/*! + Draw the contents of the scale + + \param painter Painter + \param center Center of the content circle + \param radius Radius of the content circle +*/ +void QwtCompass::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + QPalette::ColorGroup cg; + if ( isEnabled() ) + cg = hasFocus() ? QPalette::Active : QPalette::Inactive; + else + cg = QPalette::Disabled; + + double north = origin(); + if ( isValid() ) + { + if ( mode() == RotateScale ) + north -= value(); + } + + const int margin = 4; + drawRose( painter, center, radius - margin, 360.0 - north, cg ); +} + +/*! + Draw the compass rose + + \param painter Painter + \param center Center of the compass + \param radius of the circle, where to paint the rose + \param north Direction pointing north, in degrees counter clockwise + \param cg Color group +*/ +void QwtCompass::drawRose( QPainter *painter, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup cg ) const +{ + if ( d_data->rose ) + d_data->rose->draw( painter, center, radius, north, cg ); +} + +/*! + Set a rose for the compass + \param rose Compass rose + \warning The rose will be deleted, when a different rose is + set or in ~QwtCompass + \sa rose() +*/ +void QwtCompass::setRose( QwtCompassRose *rose ) +{ + if ( rose != d_data->rose ) + { + if ( d_data->rose ) + delete d_data->rose; + + d_data->rose = rose; + update(); + } +} + +/*! + \return rose + \sa setRose() +*/ +const QwtCompassRose *QwtCompass::rose() const +{ + return d_data->rose; +} + +/*! + \return rose + \sa setRose() +*/ +QwtCompassRose *QwtCompass::rose() +{ + return d_data->rose; +} + +/*! + Handles key events + + Beside the keys described in QwtDial::keyPressEvent numbers + from 1-9 (without 5) set the direction according to their + position on the num pad. + + \sa isReadOnly() +*/ +void QwtCompass::keyPressEvent( QKeyEvent *kev ) +{ + if ( isReadOnly() ) + return; + +#if 0 + if ( kev->key() == Key_5 ) + { + invalidate(); // signal ??? + return; + } +#endif + + double newValue = value(); + + if ( kev->key() >= Qt::Key_1 && kev->key() <= Qt::Key_9 ) + { + if ( mode() != RotateNeedle || kev->key() == Qt::Key_5 ) + return; + + switch ( kev->key() ) + { + case Qt::Key_6: + newValue = 180.0 * 0.0; + break; + case Qt::Key_3: + newValue = 180.0 * 0.25; + break; + case Qt::Key_2: + newValue = 180.0 * 0.5; + break; + case Qt::Key_1: + newValue = 180.0 * 0.75; + break; + case Qt::Key_4: + newValue = 180.0 * 1.0; + break; + case Qt::Key_7: + newValue = 180.0 * 1.25; + break; + case Qt::Key_8: + newValue = 180.0 * 1.5; + break; + case Qt::Key_9: + newValue = 180.0 * 1.75; + break; + } + newValue -= origin(); + setValue( newValue ); + } + else + { + QwtDial::keyPressEvent( kev ); + } +} + +/*! + \return map, mapping values to labels + \sa setLabelMap() +*/ +const QMap<double, QString> &QwtCompass::labelMap() const +{ + return d_data->labelMap; +} + +/*! + \return map, mapping values to labels + \sa setLabelMap() +*/ +QMap<double, QString> &QwtCompass::labelMap() +{ + return d_data->labelMap; +} + +/*! + \brief Set a map, mapping values to labels + \param map value to label map + + The values of the major ticks are found by looking into this + map. The default map consists of the labels N, NE, E, SE, S, SW, W, NW. + + \warning The map will have no effect for values that are no major + tick values. Major ticks can be changed by QwtScaleDraw::setScale + + \sa labelMap(), scaleDraw(), setScale() +*/ +void QwtCompass::setLabelMap( const QMap<double, QString> &map ) +{ + d_data->labelMap = map; +} + +/*! + Map a value to a corresponding label + \param value Value that will be mapped + \return Label, or QString::null + + label() looks in a map for a corresponding label for value + or return an null text. + \sa labelMap(), setLabelMap() +*/ + +QwtText QwtCompass::scaleLabel( double value ) const +{ + if ( qFuzzyCompare( value + 1.0, 1.0 ) ) + value = 0.0; + + if ( value < 0.0 ) + value += 360.0; + + if ( d_data->labelMap.contains( value ) ) + return d_data->labelMap[value]; + + return QwtText(); +} diff --git a/src/libpcp_qwt/src/qwt_compass.h b/src/libpcp_qwt/src/qwt_compass.h new file mode 100644 index 0000000..a1044b7 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_compass.h @@ -0,0 +1,65 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_COMPASS_H +#define QWT_COMPASS_H 1 + +#include "qwt_global.h" +#include "qwt_dial.h" +#include <qstring.h> +#include <qmap.h> + +class QwtCompassRose; + +/*! + \brief A Compass Widget + + QwtCompass is a widget to display and enter directions. It consists + of a scale, an optional needle and rose. + + \image html dials1.png + + \note The examples/dials example shows how to use QwtCompass. +*/ + +class QWT_EXPORT QwtCompass: public QwtDial +{ + Q_OBJECT + +public: + explicit QwtCompass( QWidget* parent = NULL ); + virtual ~QwtCompass(); + + void setRose( QwtCompassRose *rose ); + const QwtCompassRose *rose() const; + QwtCompassRose *rose(); + + const QMap<double, QString> &labelMap() const; + QMap<double, QString> &labelMap(); + void setLabelMap( const QMap<double, QString> &map ); + +protected: + virtual QwtText scaleLabel( double value ) const; + + virtual void drawRose( QPainter *, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup ) const; + + virtual void drawScaleContents( QPainter *, + const QPointF ¢er, double radius ) const; + + virtual void keyPressEvent( QKeyEvent * ); + +private: + void initCompass(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_compass_rose.cpp b/src/libpcp_qwt/src/qwt_compass_rose.cpp new file mode 100644 index 0000000..375a5bd --- /dev/null +++ b/src/libpcp_qwt/src/qwt_compass_rose.cpp @@ -0,0 +1,265 @@ +/* -*- 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_compass_rose.h" +#include "qwt_point_polar.h" +#include "qwt_painter.h" +#include <qpainter.h> + +static QPointF qwtIntersection( + QPointF p11, QPointF p12, QPointF p21, QPointF p22 ) +{ + const QLineF line1( p11, p12 ); + const QLineF line2( p21, p22 ); + + QPointF pos; + if ( line1.intersect( line2, &pos ) == QLineF::NoIntersection ) + return QPointF(); + + return pos; +} + +class QwtSimpleCompassRose::PrivateData +{ +public: + PrivateData(): + width( 0.2 ), + numThorns( 8 ), + numThornLevels( -1 ), + shrinkFactor( 0.9 ) + { + } + + double width; + int numThorns; + int numThornLevels; + double shrinkFactor; +}; + +/*! + Constructor + + \param numThorns Number of thorns + \param numThornLevels Number of thorn levels +*/ +QwtSimpleCompassRose::QwtSimpleCompassRose( + int numThorns, int numThornLevels ) +{ + d_data = new PrivateData(); + d_data->numThorns = numThorns; + d_data->numThornLevels = numThornLevels; + + const QColor dark( 128, 128, 255 ); + const QColor light( 192, 255, 255 ); + + QPalette palette; + palette.setColor( QPalette::Dark, dark ); + palette.setColor( QPalette::Light, light ); + + setPalette( palette ); +} + +//! Destructor +QwtSimpleCompassRose::~QwtSimpleCompassRose() +{ + delete d_data; +} + +/*! + Set the Factor how to shrink the thorns with each level + The default value is 0.9. + + \sa shrinkFactor() +*/ +void QwtSimpleCompassRose::setShrinkFactor( double factor ) +{ + d_data->shrinkFactor = factor; +} + +/*! + \return Factor how to shrink the thorns with each level + \sa setShrinkFactor() +*/ +double QwtSimpleCompassRose::shrinkFactor() const +{ + return d_data->shrinkFactor; +} + +/*! + Draw the rose + + \param painter Painter + \param center Center point + \param radius Radius of the rose + \param north Position + \param cg Color group +*/ +void QwtSimpleCompassRose::draw( QPainter *painter, const QPointF ¢er, + double radius, double north, QPalette::ColorGroup cg ) const +{ + QPalette pal = palette(); + pal.setCurrentColorGroup( cg ); + + drawRose( painter, pal, center, radius, north, d_data->width, + d_data->numThorns, d_data->numThornLevels, d_data->shrinkFactor ); +} + +/*! + Draw the rose + + \param painter Painter + \param palette Palette + \param center Center of the rose + \param radius Radius of the rose + \param north Position pointing to north + \param width Width of the rose + \param numThorns Number of thorns + \param numThornLevels Number of thorn levels + \param shrinkFactor Factor to shrink the thorns with each level +*/ +void QwtSimpleCompassRose::drawRose( + QPainter *painter, + const QPalette &palette, + const QPointF ¢er, double radius, double north, double width, + int numThorns, int numThornLevels, double shrinkFactor ) +{ + if ( numThorns < 4 ) + numThorns = 4; + + if ( numThorns % 4 ) + numThorns += 4 - numThorns % 4; + + if ( numThornLevels <= 0 ) + numThornLevels = numThorns / 4; + + if ( shrinkFactor >= 1.0 ) + shrinkFactor = 1.0; + + if ( shrinkFactor <= 0.5 ) + shrinkFactor = 0.5; + + painter->save(); + + painter->setPen( Qt::NoPen ); + + for ( int j = 1; j <= numThornLevels; j++ ) + { + double step = qPow( 2.0, j ) * M_PI / numThorns; + if ( step > M_PI_2 ) + break; + + double r = radius; + for ( int k = 0; k < 3; k++ ) + { + if ( j + k < numThornLevels ) + r *= shrinkFactor; + } + + double leafWidth = r * width; + if ( 2.0 * M_PI / step > 32 ) + leafWidth = 16; + + const double origin = north / 180.0 * M_PI; + for ( double angle = origin; + angle < 2.0 * M_PI + origin; angle += step ) + { + const QPointF p = qwtPolar2Pos( center, r, angle ); + const QPointF p1 = qwtPolar2Pos( center, leafWidth, angle + M_PI_2 ); + const QPointF p2 = qwtPolar2Pos( center, leafWidth, angle - M_PI_2 ); + const QPointF p3 = qwtPolar2Pos( center, r, angle + step / 2.0 ); + const QPointF p4 = qwtPolar2Pos( center, r, angle - step / 2.0 ); + + QPainterPath darkPath; + darkPath.moveTo( center ); + darkPath.lineTo( p ); + darkPath.lineTo( qwtIntersection( center, p3, p1, p ) ); + + painter->setBrush( palette.brush( QPalette::Dark ) ); + painter->drawPath( darkPath ); + + QPainterPath lightPath; + lightPath.moveTo( center ); + lightPath.lineTo( p ); + lightPath.lineTo( qwtIntersection( center, p4, p2, p ) ); + + painter->setBrush( palette.brush( QPalette::Light ) ); + painter->drawPath( lightPath ); + } + } + painter->restore(); +} + +/*! + Set the width of the rose heads. Lower value make thinner heads. + The range is limited from 0.03 to 0.4. + + \param width Width +*/ +void QwtSimpleCompassRose::setWidth( double width ) +{ + d_data->width = width; + if ( d_data->width < 0.03 ) + d_data->width = 0.03; + + if ( d_data->width > 0.4 ) + d_data->width = 0.4; +} + +//! \sa setWidth() +double QwtSimpleCompassRose::width() const +{ + return d_data->width; +} + +/*! + Set the number of thorns on one level + The number is aligned to a multiple of 4, with a minimum of 4 + + \param numThorns Number of thorns + \sa numThorns(), setNumThornLevels() +*/ +void QwtSimpleCompassRose::setNumThorns( int numThorns ) +{ + if ( numThorns < 4 ) + numThorns = 4; + + if ( numThorns % 4 ) + numThorns += 4 - numThorns % 4; + + d_data->numThorns = numThorns; +} + +/*! + \return Number of thorns + \sa setNumThorns(), setNumThornLevels() +*/ +int QwtSimpleCompassRose::numThorns() const +{ + return d_data->numThorns; +} + +/*! + Set the of thorns levels + + \param numThornLevels Number of thorns levels + \sa setNumThorns(), numThornLevels() +*/ +void QwtSimpleCompassRose::setNumThornLevels( int numThornLevels ) +{ + d_data->numThornLevels = numThornLevels; +} + +/*! + \return Number of thorn levels + \sa setNumThorns(), setNumThornLevels() +*/ +int QwtSimpleCompassRose::numThornLevels() const +{ + return d_data->numThornLevels; +} diff --git a/src/libpcp_qwt/src/qwt_compass_rose.h b/src/libpcp_qwt/src/qwt_compass_rose.h new file mode 100644 index 0000000..9b715df --- /dev/null +++ b/src/libpcp_qwt/src/qwt_compass_rose.h @@ -0,0 +1,89 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_COMPASS_ROSE_H +#define QWT_COMPASS_ROSE_H 1 + +#include "qwt_global.h" +#include <qpalette.h> + +class QPainter; + +/*! + \brief Abstract base class for a compass rose +*/ +class QWT_EXPORT QwtCompassRose +{ +public: + //! Destructor + virtual ~QwtCompassRose() {}; + + //! Assign a palette + virtual void setPalette( const QPalette &p ) + { + d_palette = p; + } + + //! \return Current palette + const QPalette &palette() const + { + return d_palette; + } + + /*! + Draw the rose + + \param painter Painter + \param center Center point + \param radius Radius of the rose + \param north Position + \param colorGroup Color group + */ + virtual void draw( QPainter *painter, + const QPointF ¢er, double radius, double north, + QPalette::ColorGroup colorGroup = QPalette::Active ) const = 0; + +private: + QPalette d_palette; +}; + +/*! + \brief A simple rose for QwtCompass +*/ +class QWT_EXPORT QwtSimpleCompassRose: public QwtCompassRose +{ +public: + QwtSimpleCompassRose( int numThorns = 8, int numThornLevels = -1 ); + virtual ~QwtSimpleCompassRose(); + + void setWidth( double w ); + double width() const; + + void setNumThorns( int count ); + int numThorns() const; + + void setNumThornLevels( int count ); + int numThornLevels() const; + + void setShrinkFactor( double factor ); + double shrinkFactor() const; + + virtual void draw( QPainter *, const QPointF ¢er, double radius, + double north, QPalette::ColorGroup = QPalette::Active ) const; + + static void drawRose( QPainter *, const QPalette &, + const QPointF ¢er, double radius, double origin, double width, + int numThorns, int numThornLevels, double shrinkFactor ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_compat.h b/src/libpcp_qwt/src/qwt_compat.h new file mode 100644 index 0000000..c97cf6b --- /dev/null +++ b/src/libpcp_qwt/src/qwt_compat.h @@ -0,0 +1,42 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef _QWT_COMPAT_H_ +#define _QWT_COMPAT_H_ + +#include "qwt_global.h" +#include "qwt_interval.h" +#include "qwt_point_3d.h" +#include <qlist.h> +#include <qvector.h> +#include <qpoint.h> +#include <qsize.h> +#include <qrect.h> +#include <qpolygon.h> + +// A couple of definition for Qwt5 compatibility + +#define qwtMax qMax +#define qwtMin qMin +#define qwtAbs qAbs +#define qwtRound qRound + +#define QwtArray QVector + +typedef QList<double> QwtValueList; +typedef QPointF QwtDoublePoint; +typedef QSizeF QwtDoubleSize; +typedef QRectF QwtDoubleRect; + +typedef QPolygon QwtPolygon; +typedef QPolygonF QwtPolygonF; +typedef QwtInterval QwtDoubleInterval; +typedef QwtPoint3D QwtDoublePoint3D; + +#endif diff --git a/src/libpcp_qwt/src/qwt_counter.cpp b/src/libpcp_qwt/src/qwt_counter.cpp new file mode 100644 index 0000000..2c7a895 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_counter.cpp @@ -0,0 +1,603 @@ +/* -*- 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_arrow_button.h" +#include "qwt_math.h" +#include "qwt_counter.h" +#include <qlayout.h> +#include <qlineedit.h> +#include <qvalidator.h> +#include <qevent.h> +#include <qstyle.h> + +class QwtCounter::PrivateData +{ +public: + PrivateData(): + editable( true ) + { + increment[Button1] = 1; + increment[Button2] = 10; + increment[Button3] = 100; + } + + QwtArrowButton *buttonDown[ButtonCnt]; + QwtArrowButton *buttonUp[ButtonCnt]; + QLineEdit *valueEdit; + + int increment[ButtonCnt]; + int numButtons; + + bool editable; +}; + +/*! + The default number of buttons is set to 2. The default increments are: + \li Button 1: 1 step + \li Button 2: 10 steps + \li Button 3: 100 steps + + \param parent + */ +QwtCounter::QwtCounter( QWidget *parent ): + QWidget( parent ) +{ + initCounter(); +} + +void QwtCounter::initCounter() +{ + d_data = new PrivateData; + + QHBoxLayout *layout = new QHBoxLayout( this ); + layout->setSpacing( 0 ); + layout->setMargin( 0 ); + + for ( int i = ButtonCnt - 1; i >= 0; i-- ) + { + QwtArrowButton *btn = + new QwtArrowButton( i + 1, Qt::DownArrow, this ); + btn->setFocusPolicy( Qt::NoFocus ); + btn->installEventFilter( this ); + layout->addWidget( btn ); + + connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) ); + connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) ); + + d_data->buttonDown[i] = btn; + } + + d_data->valueEdit = new QLineEdit( this ); + d_data->valueEdit->setReadOnly( false ); + d_data->valueEdit->setValidator( new QDoubleValidator( d_data->valueEdit ) ); + layout->addWidget( d_data->valueEdit ); + + connect( d_data->valueEdit, SIGNAL( editingFinished() ), + SLOT( textChanged() ) ); + + layout->setStretchFactor( d_data->valueEdit, 10 ); + + for ( int i = 0; i < ButtonCnt; i++ ) + { + QwtArrowButton *btn = + new QwtArrowButton( i + 1, Qt::UpArrow, this ); + btn->setFocusPolicy( Qt::NoFocus ); + btn->installEventFilter( this ); + layout->addWidget( btn ); + + connect( btn, SIGNAL( released() ), SLOT( btnReleased() ) ); + connect( btn, SIGNAL( clicked() ), SLOT( btnClicked() ) ); + + d_data->buttonUp[i] = btn; + } + + setNumButtons( 2 ); + setRange( 0.0, 1.0, 0.001 ); + setValue( 0.0 ); + + setSizePolicy( + QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); + + setFocusProxy( d_data->valueEdit ); + setFocusPolicy( Qt::StrongFocus ); +} + +//! Destructor +QwtCounter::~QwtCounter() +{ + delete d_data; +} + +//! Set from lineedit +void QwtCounter::textChanged() +{ + if ( !d_data->editable ) + return; + + bool converted = false; + + const double value = d_data->valueEdit->text().toDouble( &converted ); + if ( converted ) + setValue( value ); +} + +/** + \brief Allow/disallow the user to manually edit the value + + \param editable true enables editing + \sa editable() +*/ +void QwtCounter::setEditable( bool editable ) +{ + if ( editable == d_data->editable ) + return; + + d_data->editable = editable; + d_data->valueEdit->setReadOnly( !editable ); +} + +//! returns whether the line edit is edatble. (default is yes) +bool QwtCounter::editable() const +{ + return d_data->editable; +} + +/*! + Handle PolishRequest events + \param event Event +*/ +bool QwtCounter::event( QEvent *event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + const int w = d_data->valueEdit->fontMetrics().width( "W" ) + 8; + for ( int i = 0; i < ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setMinimumWidth( w ); + d_data->buttonUp[i]->setMinimumWidth( w ); + } + } + + return QWidget::event( event ); +} + +/*! + Handle key events + + - Ctrl + Qt::Key_Home\n + Step to minValue() + - Ctrl + Qt::Key_End\n + Step to maxValue() + - Qt::Key_Up\n + Increment by incSteps(QwtCounter::Button1) + - Qt::Key_Down\n + Decrement by incSteps(QwtCounter::Button1) + - Qt::Key_PageUp\n + Increment by incSteps(QwtCounter::Button2) + - Qt::Key_PageDown\n + Decrement by incSteps(QwtCounter::Button2) + - Shift + Qt::Key_PageUp\n + Increment by incSteps(QwtCounter::Button3) + - Shift + Qt::Key_PageDown\n + Decrement by incSteps(QwtCounter::Button3) + + \param event Key event +*/ +void QwtCounter::keyPressEvent ( QKeyEvent *event ) +{ + bool accepted = true; + + switch ( event->key() ) + { + case Qt::Key_Home: + { + if ( event->modifiers() & Qt::ControlModifier ) + setValue( minValue() ); + else + accepted = false; + break; + } + case Qt::Key_End: + { + if ( event->modifiers() & Qt::ControlModifier ) + setValue( maxValue() ); + else + accepted = false; + break; + } + case Qt::Key_Up: + { + incValue( d_data->increment[0] ); + break; + } + case Qt::Key_Down: + { + incValue( -d_data->increment[0] ); + break; + } + case Qt::Key_PageUp: + case Qt::Key_PageDown: + { + int increment = d_data->increment[0]; + if ( d_data->numButtons >= 2 ) + increment = d_data->increment[1]; + if ( d_data->numButtons >= 3 ) + { + if ( event->modifiers() & Qt::ShiftModifier ) + increment = d_data->increment[2]; + } + if ( event->key() == Qt::Key_PageDown ) + increment = -increment; + incValue( increment ); + break; + } + default: + { + accepted = false; + } + } + + if ( accepted ) + { + event->accept(); + return; + } + + QWidget::keyPressEvent ( event ); +} + +/*! + Handle wheel events + \param event Wheel event +*/ +void QwtCounter::wheelEvent( QWheelEvent *event ) +{ + event->accept(); + + if ( d_data->numButtons <= 0 ) + return; + + int increment = d_data->increment[0]; + if ( d_data->numButtons >= 2 ) + { + if ( event->modifiers() & Qt::ControlModifier ) + increment = d_data->increment[1]; + } + if ( d_data->numButtons >= 3 ) + { + if ( event->modifiers() & Qt::ShiftModifier ) + increment = d_data->increment[2]; + } + + for ( int i = 0; i < d_data->numButtons; i++ ) + { + if ( d_data->buttonDown[i]->geometry().contains( event->pos() ) || + d_data->buttonUp[i]->geometry().contains( event->pos() ) ) + { + increment = d_data->increment[i]; + } + } + + const int wheel_delta = 120; + + int delta = event->delta(); + if ( delta >= 2 * wheel_delta ) + delta /= 2; // Never saw an abs(delta) < 240 + + incValue( delta / wheel_delta * increment ); +} + +/*! + Specify the number of steps by which the value + is incremented or decremented when a specified button + is pushed. + + \param button Button index + \param nSteps Number of steps + + \sa incSteps() +*/ +void QwtCounter::setIncSteps( QwtCounter::Button button, int nSteps ) +{ + if ( button >= 0 && button < ButtonCnt ) + d_data->increment[button] = nSteps; +} + +/*! + \return the number of steps by which a specified button increments the value + or 0 if the button is invalid. + \param button Button index + + \sa setIncSteps() +*/ +int QwtCounter::incSteps( QwtCounter::Button button ) const +{ + if ( button >= 0 && button < ButtonCnt ) + return d_data->increment[button]; + + return 0; +} + +/*! + \brief Set a new value + + Calls QwtDoubleRange::setValue and does all visual updates. + + \param value New value + \sa QwtDoubleRange::setValue() +*/ + +void QwtCounter::setValue( double value ) +{ + QwtDoubleRange::setValue( value ); + + showNum( this->value() ); + updateButtons(); +} + +/*! + \brief Notify a change of value +*/ +void QwtCounter::valueChange() +{ + if ( isValid() ) + showNum( value() ); + else + d_data->valueEdit->setText( QString::null ); + + updateButtons(); + + if ( isValid() ) + Q_EMIT valueChanged( value() ); +} + +/*! + \brief Update buttons according to the current value + + When the QwtCounter under- or over-flows, the focus is set to the smallest + up- or down-button and counting is disabled. + + Counting is re-enabled on a button release event (mouse or space bar). +*/ +void QwtCounter::updateButtons() +{ + if ( isValid() ) + { + // 1. save enabled state of the smallest down- and up-button + // 2. change enabled state on under- or over-flow + + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setEnabled( value() > minValue() ); + d_data->buttonUp[i]->setEnabled( value() < maxValue() ); + } + } + else + { + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + d_data->buttonDown[i]->setEnabled( false ); + d_data->buttonUp[i]->setEnabled( false ); + } + } +} + +/*! + \brief Specify the number of buttons on each side of the label + \param numButtons Number of buttons +*/ +void QwtCounter::setNumButtons( int numButtons ) +{ + if ( numButtons < 0 || numButtons > QwtCounter::ButtonCnt ) + return; + + for ( int i = 0; i < QwtCounter::ButtonCnt; i++ ) + { + if ( i < numButtons ) + { + d_data->buttonDown[i]->show(); + d_data->buttonUp[i]->show(); + } + else + { + d_data->buttonDown[i]->hide(); + d_data->buttonUp[i]->hide(); + } + } + + d_data->numButtons = numButtons; +} + +/*! + \return The number of buttons on each side of the widget. +*/ +int QwtCounter::numButtons() const +{ + return d_data->numButtons; +} + +/*! + Display number string + + \param number Number +*/ +void QwtCounter::showNum( double number ) +{ + QString text; + text.setNum( number ); + + const int cursorPos = d_data->valueEdit->cursorPosition(); + d_data->valueEdit->setText( text ); + d_data->valueEdit->setCursorPosition( cursorPos ); +} + +//! Button clicked +void QwtCounter::btnClicked() +{ + for ( int i = 0; i < ButtonCnt; i++ ) + { + if ( d_data->buttonUp[i] == sender() ) + incValue( d_data->increment[i] ); + + if ( d_data->buttonDown[i] == sender() ) + incValue( -d_data->increment[i] ); + } +} + +//! Button released +void QwtCounter::btnReleased() +{ + Q_EMIT buttonReleased( value() ); +} + +/*! + \brief Notify change of range + + This function updates the enabled property of + all buttons contained in QwtCounter. +*/ +void QwtCounter::rangeChange() +{ + updateButtons(); +} + +//! A size hint +QSize QwtCounter::sizeHint() const +{ + QString tmp; + + int w = tmp.setNum( minValue() ).length(); + int w1 = tmp.setNum( maxValue() ).length(); + if ( w1 > w ) + w = w1; + w1 = tmp.setNum( minValue() + step() ).length(); + if ( w1 > w ) + w = w1; + w1 = tmp.setNum( maxValue() - step() ).length(); + if ( w1 > w ) + w = w1; + + tmp.fill( '9', w ); + + QFontMetrics fm( d_data->valueEdit->font() ); + w = fm.width( tmp ) + 2; + if ( d_data->valueEdit->hasFrame() ) + w += 2 * style()->pixelMetric( QStyle::PM_DefaultFrameWidth ); + + // Now we replace default sizeHint contribution of d_data->valueEdit by + // what we really need. + + w += QWidget::sizeHint().width() - d_data->valueEdit->sizeHint().width(); + + const int h = qMin( QWidget::sizeHint().height(), + d_data->valueEdit->minimumSizeHint().height() ); + return QSize( w, h ); +} + +//! returns the step size +double QwtCounter::step() const +{ + return QwtDoubleRange::step(); +} + +/*! + Set the step size + \param stepSize Step size + \sa QwtDoubleRange::setStep() +*/ +void QwtCounter::setStep( double stepSize ) +{ + QwtDoubleRange::setStep( stepSize ); +} + +//! returns the minimum value of the range +double QwtCounter::minValue() const +{ + return QwtDoubleRange::minValue(); +} + +/*! + Set the minimum value of the range + + \param value Minimum value + \sa setMaxValue(), minValue() +*/ +void QwtCounter::setMinValue( double value ) +{ + setRange( value, maxValue(), step() ); +} + +//! returns the maximum value of the range +double QwtCounter::maxValue() const +{ + return QwtDoubleRange::maxValue(); +} + +/*! + Set the maximum value of the range + + \param value Maximum value + \sa setMinValue(), maxVal() +*/ +void QwtCounter::setMaxValue( double value ) +{ + setRange( minValue(), value, step() ); +} + +/*! + Set the number of increment steps for button 1 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton1( int nSteps ) +{ + setIncSteps( Button1, nSteps ); +} + +//! returns the number of increment steps for button 1 +int QwtCounter::stepButton1() const +{ + return incSteps( Button1 ); +} + +/*! + Set the number of increment steps for button 2 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton2( int nSteps ) +{ + setIncSteps( Button2, nSteps ); +} + +//! returns the number of increment steps for button 2 +int QwtCounter::stepButton2() const +{ + return incSteps( Button2 ); +} + +/*! + Set the number of increment steps for button 3 + \param nSteps Number of steps +*/ +void QwtCounter::setStepButton3( int nSteps ) +{ + setIncSteps( Button3, nSteps ); +} + +//! returns the number of increment steps for button 3 +int QwtCounter::stepButton3() const +{ + return incSteps( Button3 ); +} + +//! \return Current value +double QwtCounter::value() const +{ + return QwtDoubleRange::value(); +} + diff --git a/src/libpcp_qwt/src/qwt_counter.h b/src/libpcp_qwt/src/qwt_counter.h new file mode 100644 index 0000000..459fdf9 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_counter.h @@ -0,0 +1,148 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_COUNTER_H +#define QWT_COUNTER_H + +#include "qwt_global.h" +#include "qwt_double_range.h" +#include <qwidget.h> + +/*! + \brief The Counter Widget + + A Counter consists of a lineEdit displaying a number and + one ore more (up to three) push buttons on each side + of the lineEdit which can be used to increment or decrement + the counter's value. + + A Counter has a range from a minimum value to a maximum value + and a step size. The range can be specified using setRange(). + The number of steps by which a button increments or decrements + the value can be specified using setIncSteps(). + The number of buttons can be changed with setNumButtons(). + + Example: +\code +#include <qwt_counter.h> + +QwtCounter *counter = new QwtCounter(parent); + +counter->setRange(0.0, 100.0, 1.0); // From 0.0 to 100, step 1.0 +counter->setNumButtons(2); // Two buttons each side +counter->setIncSteps(QwtCounter::Button1, 1); // Button 1 increments 1 step +counter->setIncSteps(QwtCounter::Button2, 20); // Button 2 increments 20 steps + +connect(counter, SIGNAL(valueChanged(double)), my_class, SLOT(newValue(double))); +\endcode + */ + +class QWT_EXPORT QwtCounter : public QWidget, public QwtDoubleRange +{ + Q_OBJECT + + Q_PROPERTY( int numButtons READ numButtons WRITE setNumButtons ) + Q_PROPERTY( double basicstep READ step WRITE setStep ) + Q_PROPERTY( double minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( double maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( int stepButton1 READ stepButton1 WRITE setStepButton1 ) + Q_PROPERTY( int stepButton2 READ stepButton2 WRITE setStepButton2 ) + Q_PROPERTY( int stepButton3 READ stepButton3 WRITE setStepButton3 ) + Q_PROPERTY( double value READ value WRITE setValue ) + Q_PROPERTY( bool editable READ editable WRITE setEditable ) + +public: + //! Button index + enum Button + { + //! Button intended for minor steps + Button1, + + //! Button intended for medium steps + Button2, + + //! Button intended for large steps + Button3, + + //! Number of buttons + ButtonCnt + }; + + explicit QwtCounter( QWidget *parent = NULL ); + virtual ~QwtCounter(); + + bool editable() const; + void setEditable( bool ); + + void setNumButtons( int n ); + int numButtons() const; + + void setIncSteps( QwtCounter::Button btn, int nSteps ); + int incSteps( QwtCounter::Button btn ) const; + + virtual void setValue( double ); + virtual QSize sizeHint() const; + + // a set of dummies to help the designer + + double step() const; + void setStep( double s ); + + double minValue() const; + void setMinValue( double m ); + + double maxValue() const; + void setMaxValue( double m ); + + void setStepButton1( int nSteps ); + int stepButton1() const; + + void setStepButton2( int nSteps ); + int stepButton2() const; + + void setStepButton3( int nSteps ); + int stepButton3() const; + + virtual double value() const; + +Q_SIGNALS: + /*! + This signal is emitted when a button has been released + \param value The new value + */ + void buttonReleased ( double value ); + + /*! + This signal is emitted when the counter's value has changed + \param value The new value + */ + void valueChanged ( double value ); + +protected: + virtual bool event( QEvent * ); + virtual void wheelEvent( QWheelEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void rangeChange(); + +private Q_SLOTS: + void btnReleased(); + void btnClicked(); + void textChanged(); + +private: + void initCounter(); + void updateButtons(); + void showNum( double ); + virtual void valueChange(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_curve_fitter.cpp b/src/libpcp_qwt/src/qwt_curve_fitter.cpp new file mode 100644 index 0000000..639a80d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_curve_fitter.cpp @@ -0,0 +1,405 @@ +/* -*- 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_curve_fitter.h" +#include "qwt_math.h" +#include "qwt_spline.h" +#include <qstack.h> +#include <qvector.h> + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#endif + +//! Constructor +QwtCurveFitter::QwtCurveFitter() +{ +} + +//! Destructor +QwtCurveFitter::~QwtCurveFitter() +{ +} + +class QwtSplineCurveFitter::PrivateData +{ +public: + PrivateData(): + fitMode( QwtSplineCurveFitter::Auto ), + splineSize( 250 ) + { + } + + QwtSpline spline; + QwtSplineCurveFitter::FitMode fitMode; + int splineSize; +}; + +//! Constructor +QwtSplineCurveFitter::QwtSplineCurveFitter() +{ + d_data = new PrivateData; +} + +//! Destructor +QwtSplineCurveFitter::~QwtSplineCurveFitter() +{ + delete d_data; +} + +/*! + Select the algorithm used for building the spline + + \param mode Mode representing a spline algorithm + \sa fitMode() +*/ +void QwtSplineCurveFitter::setFitMode( FitMode mode ) +{ + d_data->fitMode = mode; +} + +/*! + \return Mode representing a spline algorithm + \sa setFitMode() +*/ +QwtSplineCurveFitter::FitMode QwtSplineCurveFitter::fitMode() const +{ + return d_data->fitMode; +} + +/*! + Assign a spline + + \param spline Spline + \sa spline() +*/ +void QwtSplineCurveFitter::setSpline( const QwtSpline &spline ) +{ + d_data->spline = spline; + d_data->spline.reset(); +} + +/*! + \return Spline + \sa setSpline() +*/ +const QwtSpline &QwtSplineCurveFitter::spline() const +{ + return d_data->spline; +} + +/*! + \return Spline + \sa setSpline() +*/ +QwtSpline &QwtSplineCurveFitter::spline() +{ + return d_data->spline; +} + +/*! + Assign a spline size ( has to be at least 10 points ) + + \param splineSize Spline size + \sa splineSize() +*/ +void QwtSplineCurveFitter::setSplineSize( int splineSize ) +{ + d_data->splineSize = qMax( splineSize, 10 ); +} + +/*! + \return Spline size + \sa setSplineSize() +*/ +int QwtSplineCurveFitter::splineSize() const +{ + return d_data->splineSize; +} + +/*! + Find a curve which has the best fit to a series of data points + + \param points Series of data points + \return Curve points +*/ +QPolygonF QwtSplineCurveFitter::fitCurve( const QPolygonF &points ) const +{ + const int size = points.size(); + if ( size <= 2 ) + return points; + + FitMode fitMode = d_data->fitMode; + if ( fitMode == Auto ) + { + fitMode = Spline; + + const QPointF *p = points.data(); + for ( int i = 1; i < size; i++ ) + { + if ( p[i].x() <= p[i-1].x() ) + { + fitMode = ParametricSpline; + break; + } + }; + } + + if ( fitMode == ParametricSpline ) + return fitParametric( points ); + else + return fitSpline( points ); +} + +QPolygonF QwtSplineCurveFitter::fitSpline( const QPolygonF &points ) const +{ + d_data->spline.setPoints( points ); + if ( !d_data->spline.isValid() ) + return points; + + QPolygonF fittedPoints( d_data->splineSize ); + + const double x1 = points[0].x(); + const double x2 = points[int( points.size() - 1 )].x(); + const double dx = x2 - x1; + const double delta = dx / ( d_data->splineSize - 1 ); + + for ( int i = 0; i < d_data->splineSize; i++ ) + { + QPointF &p = fittedPoints[i]; + + const double v = x1 + i * delta; + const double sv = d_data->spline.value( v ); + + p.setX( v ); + p.setY( sv ); + } + d_data->spline.reset(); + + return fittedPoints; +} + +QPolygonF QwtSplineCurveFitter::fitParametric( const QPolygonF &points ) const +{ + int i; + const int size = points.size(); + + QPolygonF fittedPoints( d_data->splineSize ); + QPolygonF splinePointsX( size ); + QPolygonF splinePointsY( size ); + + const QPointF *p = points.data(); + QPointF *spX = splinePointsX.data(); + QPointF *spY = splinePointsY.data(); + + double param = 0.0; + for ( i = 0; i < size; i++ ) + { + const double x = p[i].x(); + const double y = p[i].y(); + if ( i > 0 ) + { + const double delta = qSqrt( qwtSqr( x - spX[i-1].y() ) + + qwtSqr( y - spY[i-1].y() ) ); + param += qMax( delta, 1.0 ); + } + spX[i].setX( param ); + spX[i].setY( x ); + spY[i].setX( param ); + spY[i].setY( y ); + } + + d_data->spline.setPoints( splinePointsX ); + if ( !d_data->spline.isValid() ) + return points; + + const double deltaX = + splinePointsX[size - 1].x() / ( d_data->splineSize - 1 ); + for ( i = 0; i < d_data->splineSize; i++ ) + { + const double dtmp = i * deltaX; + fittedPoints[i].setX( d_data->spline.value( dtmp ) ); + } + + d_data->spline.setPoints( splinePointsY ); + if ( !d_data->spline.isValid() ) + return points; + + const double deltaY = + splinePointsY[size - 1].x() / ( d_data->splineSize - 1 ); + for ( i = 0; i < d_data->splineSize; i++ ) + { + const double dtmp = i * deltaY; + fittedPoints[i].setY( d_data->spline.value( dtmp ) ); + } + + return fittedPoints; +} + +class QwtWeedingCurveFitter::PrivateData +{ +public: + PrivateData(): + tolerance( 1.0 ) + { + } + + double tolerance; +}; + +class QwtWeedingCurveFitter::Line +{ +public: + Line( int i1 = 0, int i2 = 0 ): + from( i1 ), + to( i2 ) + { + } + + int from; + int to; +}; + +/*! + Constructor + + \param tolerance Tolerance + \sa setTolerance(), tolerance() +*/ +QwtWeedingCurveFitter::QwtWeedingCurveFitter( double tolerance ) +{ + d_data = new PrivateData; + setTolerance( tolerance ); +} + +//! Destructor +QwtWeedingCurveFitter::~QwtWeedingCurveFitter() +{ + delete d_data; +} + +/*! + Assign the tolerance + + The tolerance is the maximum distance, that is accaptable + between the original curve and the smoothed curve. + + Increasing the tolerance will reduce the number of the + resulting points. + + \param tolerance Tolerance + + \sa tolerance() +*/ +void QwtWeedingCurveFitter::setTolerance( double tolerance ) +{ + d_data->tolerance = qMax( tolerance, 0.0 ); +} + +/*! + \return Tolerance + \sa setTolerance() +*/ +double QwtWeedingCurveFitter::tolerance() const +{ + return d_data->tolerance; +} + +/*! + \param points Series of data points + \return Curve points +*/ +QPolygonF QwtWeedingCurveFitter::fitCurve( const QPolygonF &points ) const +{ + QStack<Line> stack; + stack.reserve( 500 ); + + const QPointF *p = points.data(); + const int nPoints = points.size(); + + QVector<bool> usePoint( nPoints, false ); + + double distToSegment; + + stack.push( Line( 0, nPoints - 1 ) ); + + while ( !stack.isEmpty() ) + { + const Line r = stack.pop(); + + // initialize line segment + const double vecX = p[r.to].x() - p[r.from].x(); + const double vecY = p[r.to].y() - p[r.from].y(); + + const double vecLength = qSqrt( vecX * vecX + vecY * vecY ); + + const double unitVecX = ( vecLength != 0.0 ) ? vecX / vecLength : 0.0; + const double unitVecY = ( vecLength != 0.0 ) ? vecY / vecLength : 0.0; + + double maxDist = 0.0; + int nVertexIndexMaxDistance = r.from + 1; + for ( int i = r.from + 1; i < r.to; i++ ) + { + //compare to anchor + const double fromVecX = p[i].x() - p[r.from].x(); + const double fromVecY = p[i].y() - p[r.from].y(); + const double fromVecLength = + qSqrt( fromVecX * fromVecX + fromVecY * fromVecY ); + + if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 ) + { + distToSegment = fromVecLength; + } + if ( fromVecX * unitVecX + fromVecY * unitVecY < 0.0 ) + { + distToSegment = fromVecLength; + } + else + { + const double toVecX = p[i].x() - p[r.to].x(); + const double toVecY = p[i].y() - p[r.to].y(); + const double toVecLength = qSqrt( toVecX * toVecX + toVecY * toVecY ); + const double s = toVecX * ( -unitVecX ) + toVecY * ( -unitVecY ); + if ( s < 0.0 ) + distToSegment = toVecLength; + else + { + distToSegment = qSqrt( qFabs( toVecLength * toVecLength - s * s ) ); + } + } + + if ( maxDist < distToSegment ) + { + maxDist = distToSegment; + nVertexIndexMaxDistance = i; + } + } + if ( maxDist <= d_data->tolerance ) + { + usePoint[r.from] = true; + usePoint[r.to] = true; + } + else + { + stack.push( Line( r.from, nVertexIndexMaxDistance ) ); + stack.push( Line( nVertexIndexMaxDistance, r.to ) ); + } + } + + int cnt = 0; + + QPolygonF stripped( nPoints ); + for ( int i = 0; i < nPoints; i++ ) + { + if ( usePoint[i] ) + stripped[cnt++] = p[i]; + } + stripped.resize( cnt ); + return stripped; +} diff --git a/src/libpcp_qwt/src/qwt_curve_fitter.h b/src/libpcp_qwt/src/qwt_curve_fitter.h new file mode 100644 index 0000000..c9ae603 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_curve_fitter.h @@ -0,0 +1,128 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_CURVE_FITTER_H +#define QWT_CURVE_FITTER_H + +#include "qwt_global.h" +#include <qpolygon.h> +#include <qrect.h> + +class QwtSpline; + +/*! + \brief Abstract base class for a curve fitter +*/ +class QWT_EXPORT QwtCurveFitter +{ +public: + virtual ~QwtCurveFitter(); + + /*! + Find a curve which has the best fit to a series of data points + + \param polygon Series of data points + \return Curve points + */ + virtual QPolygonF fitCurve( const QPolygonF &polygon ) const = 0; + +protected: + QwtCurveFitter(); + +private: + QwtCurveFitter( const QwtCurveFitter & ); + QwtCurveFitter &operator=( const QwtCurveFitter & ); +}; + +/*! + \brief A curve fitter using cubic splines +*/ +class QWT_EXPORT QwtSplineCurveFitter: public QwtCurveFitter +{ +public: + /*! + Spline type + The default setting is Auto + \sa setFitMode(), FitMode() + */ + enum FitMode + { + /*! + Use the default spline algorithm for polygons with + increasing x values ( p[i-1] < p[i] ), otherwise use + a parametric spline algorithm. + */ + Auto, + + //! Use a default spline algorithm + Spline, + + //! Use a parametric spline algorithm + ParametricSpline + }; + + QwtSplineCurveFitter(); + virtual ~QwtSplineCurveFitter(); + + void setFitMode( FitMode ); + FitMode fitMode() const; + + void setSpline( const QwtSpline& ); + const QwtSpline &spline() const; + QwtSpline &spline(); + + void setSplineSize( int size ); + int splineSize() const; + + virtual QPolygonF fitCurve( const QPolygonF & ) const; + +private: + QPolygonF fitSpline( const QPolygonF & ) const; + QPolygonF fitParametric( const QPolygonF & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief A curve fitter implementing Douglas and Peucker algorithm + + The purpose of the Douglas and Peucker algorithm is that given a 'curve' + composed of line segments to find a curve not too dissimilar but that + has fewer points. The algorithm defines 'too dissimilar' based on the + maximum distance (tolerance) between the original curve and the + smoothed curve. + + The smoothed curve consists of a subset of the points that defined the + original curve. + + In opposite to QwtSplineCurveFitter the Douglas and Peucker algorithm reduces + the number of points. By adjusting the tolerance parameter according to the + axis scales QwtSplineCurveFitter can be used to implement different + level of details to speed up painting of curves of many points. +*/ +class QWT_EXPORT QwtWeedingCurveFitter: public QwtCurveFitter +{ +public: + QwtWeedingCurveFitter( double tolerance = 1.0 ); + virtual ~QwtWeedingCurveFitter(); + + void setTolerance( double ); + double tolerance() const; + + virtual QPolygonF fitCurve( const QPolygonF & ) const; + +private: + class Line; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_dial.cpp b/src/libpcp_qwt/src/qwt_dial.cpp new file mode 100644 index 0000000..cf05561 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_dial.cpp @@ -0,0 +1,1156 @@ +/* -*- 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_dial.h" +#include "qwt_dial_needle.h" +#include "qwt_math.h" +#include "qwt_scale_engine.h" +#include "qwt_scale_map.h" +#include "qwt_painter.h" +#include <qpainter.h> +#include <qbitmap.h> +#include <qpalette.h> +#include <qpixmap.h> +#include <qevent.h> +#include <qalgorithms.h> +#include <qmath.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qapplication.h> + +#if QT_VERSION < 0x040601 +#define qAtan(x) ::atan(x) +#endif + +class QwtDial::PrivateData +{ +public: + PrivateData(): + frameShadow( Sunken ), + lineWidth( 0 ), + mode( RotateNeedle ), + direction( Clockwise ), + origin( 90.0 ), + minScaleArc( 0.0 ), + maxScaleArc( 0.0 ), + scaleDraw( 0 ), + maxMajIntv( 36 ), + maxMinIntv( 10 ), + scaleStep( 0.0 ), + needle( 0 ) + { + } + + ~PrivateData() + { + delete scaleDraw; + delete needle; + } + Shadow frameShadow; + int lineWidth; + + QwtDial::Mode mode; + QwtDial::Direction direction; + + double origin; + double minScaleArc; + double maxScaleArc; + + QwtDialScaleDraw *scaleDraw; + int maxMajIntv; + int maxMinIntv; + double scaleStep; + + QwtDialNeedle *needle; + + static double previousDir; +}; + +double QwtDial::PrivateData::previousDir = -1.0; + +/*! + Constructor + + \param parent Parent dial widget +*/ +QwtDialScaleDraw::QwtDialScaleDraw( QwtDial *parent ): + d_parent( parent ), + d_penWidth( 1.0 ) +{ +} + +/*! + Set the pen width used for painting the scale + + \param penWidth Pen width + \sa penWidth(), QwtDial::drawScale() +*/ + +void QwtDialScaleDraw::setPenWidth( double penWidth ) +{ + d_penWidth = qMax( penWidth, 0.0 ); +} + +/*! + \return Pen width used for painting the scale + \sa setPenWidth, QwtDial::drawScale() +*/ +double QwtDialScaleDraw::penWidth() const +{ + return d_penWidth; +} + +/*! + Call QwtDial::scaleLabel of the parent dial widget. + + \param value Value to display + + \sa QwtDial::scaleLabel() +*/ +QwtText QwtDialScaleDraw::label( double value ) const +{ + if ( d_parent == NULL ) + return QwtRoundScaleDraw::label( value ); + + return d_parent->scaleLabel( value ); +} + +/*! + \brief Constructor + \param parent Parent widget + + Create a dial widget with no scale and no needle. + The default origin is 90.0 with no valid value. It accepts + mouse and keyboard inputs and has no step size. The default mode + is QwtDial::RotateNeedle. +*/ +QwtDial::QwtDial( QWidget* parent ): + QwtAbstractSlider( Qt::Horizontal, parent ) +{ + initDial(); +} + +void QwtDial::initDial() +{ + d_data = new PrivateData; + + setFocusPolicy( Qt::TabFocus ); + + QPalette p = palette(); + for ( int i = 0; i < QPalette::NColorGroups; i++ ) + { + const QPalette::ColorGroup colorGroup = ( QPalette::ColorGroup )i; + + // Base: background color of the circle inside the frame. + // WindowText: background color of the circle inside the scale + + p.setColor( colorGroup, QPalette::WindowText, + p.color( colorGroup, QPalette::Base ) ); + } + setPalette( p ); + + d_data->scaleDraw = new QwtDialScaleDraw( this ); + d_data->scaleDraw->setRadius( 0 ); + + setScaleArc( 0.0, 360.0 ); // scale as a full circle + setRange( 0.0, 360.0, 1.0, 10 ); // degrees as default +} + +//! Destructor +QwtDial::~QwtDial() +{ + delete d_data; +} + +/*! + Sets the frame shadow value from the frame style. + \param shadow Frame shadow + \sa setLineWidth(), QFrame::setFrameShadow() +*/ +void QwtDial::setFrameShadow( Shadow shadow ) +{ + if ( shadow != d_data->frameShadow ) + { + d_data->frameShadow = shadow; + if ( lineWidth() > 0 ) + update(); + } +} + +/*! + \return Frame shadow + /sa setFrameShadow(), lineWidth(), QFrame::frameShadow +*/ +QwtDial::Shadow QwtDial::frameShadow() const +{ + return d_data->frameShadow; +} + +/*! + Sets the line width + + \param lineWidth Line width + \sa setFrameShadow() +*/ +void QwtDial::setLineWidth( int lineWidth ) +{ + if ( lineWidth < 0 ) + lineWidth = 0; + + if ( d_data->lineWidth != lineWidth ) + { + d_data->lineWidth = lineWidth; + update(); + } +} + +/*! + \return Line width of the frame + \sa setLineWidth(), frameShadow(), lineWidth() +*/ +int QwtDial::lineWidth() const +{ + return d_data->lineWidth; +} + +/*! + \return bounding rect of the circle inside the frame + \sa setLineWidth(), scaleInnerRect(), boundingRect() +*/ +QRectF QwtDial::innerRect() const +{ + const double lw = lineWidth(); + return boundingRect().adjusted( lw, lw, -lw, -lw ); +} + +/*! + \return bounding rect of the dial including the frame + \sa setLineWidth(), scaleInnerRect(), innerRect() +*/ +QRectF QwtDial::boundingRect() const +{ + const QRectF cr = contentsRect(); + + const double dim = qMin( cr.width(), cr.height() ); + + QRectF inner( 0, 0, dim, dim ); + inner.moveCenter( cr.center() ); + + return inner; +} + +/*! + \return rect inside the scale + \sa setLineWidth(), boundingRect(), innerRect() +*/ +QRectF QwtDial::scaleInnerRect() const +{ + QRectF rect = innerRect(); + + if ( d_data->scaleDraw ) + { + double scaleDist = qCeil( d_data->scaleDraw->extent( font() ) ); + scaleDist++; // margin + + rect.adjust( scaleDist, scaleDist, -scaleDist, -scaleDist ); + } + + return rect; +} + +/*! + \brief Change the mode of the meter. + \param mode New mode + + The value of the meter is indicated by the difference + between north of the scale and the direction of the needle. + In case of QwtDial::RotateNeedle north is pointing + to the origin() and the needle is rotating, in case of + QwtDial::RotateScale, the needle points to origin() + and the scale is rotating. + + The default mode is QwtDial::RotateNeedle. + + \sa mode(), setValue(), setOrigin() +*/ +void QwtDial::setMode( Mode mode ) +{ + if ( mode != d_data->mode ) + { + d_data->mode = mode; + update(); + } +} + +/*! + \return mode of the dial. + + The value of the dial is indicated by the difference + between the origin and the direction of the needle. + In case of QwtDial::RotateNeedle the scale arc is fixed + to the origin() and the needle is rotating, in case of + QwtDial::RotateScale, the needle points to origin() + and the scale is rotating. + + The default mode is QwtDial::RotateNeedle. + + \sa setMode(), origin(), setScaleArc(), value() +*/ +QwtDial::Mode QwtDial::mode() const +{ + return d_data->mode; +} + +/*! + Sets whether it is possible to step the value from the highest value to + the lowest value and vice versa to on. + + \param wrapping en/disables wrapping + + \sa wrapping(), QwtDoubleRange::periodic() + \note The meaning of wrapping is like the wrapping property of QSpinBox, + but not like it is used in QDial. +*/ +void QwtDial::setWrapping( bool wrapping ) +{ + setPeriodic( wrapping ); +} + +/*! + wrapping() holds whether it is possible to step the value from the + highest value to the lowest value and vice versa. + + \sa setWrapping(), QwtDoubleRange::setPeriodic() + \note The meaning of wrapping is like the wrapping property of QSpinBox, + but not like it is used in QDial. +*/ +bool QwtDial::wrapping() const +{ + return periodic(); +} + +/*! + Set the direction of the dial (clockwise/counterclockwise) + + \param direction Direction + \sa direction() +*/ +void QwtDial::setDirection( Direction direction ) +{ + if ( direction != d_data->direction ) + { + d_data->direction = direction; + update(); + } +} + +/*! + \return Direction of the dial + + The default direction of a dial is QwtDial::Clockwise + + \sa setDirection() +*/ +QwtDial::Direction QwtDial::direction() const +{ + return d_data->direction; +} + +/*! + Paint the dial + \param event Paint event +*/ +void QwtDial::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + painter.setRenderHint( QPainter::Antialiasing, true ); + + painter.save(); + drawContents( &painter ); + painter.restore(); + + painter.save(); + drawFrame( &painter ); + painter.restore(); + + if ( hasFocus() ) + drawFocusIndicator( &painter ); +} + +/*! + Draw a dotted round circle, if !isReadOnly() + + \param painter Painter +*/ +void QwtDial::drawFocusIndicator( QPainter *painter ) const +{ + if ( !isReadOnly() ) + { + QRectF focusRect = innerRect(); + + const int margin = 2; + focusRect.adjust( margin, margin, -margin, -margin ); + + QColor color = palette().color( QPalette::Base ); + if ( color.isValid() ) + { + const QColor gray( Qt::gray ); + + int h, s, v; + color.getHsv( &h, &s, &v ); + color = ( v > 128 ) ? gray.dark( 120 ) : gray.light( 120 ); + } + else + color = Qt::darkGray; + + painter->save(); + painter->setBrush( Qt::NoBrush ); + painter->setPen( QPen( color, 0, Qt::DotLine ) ); + painter->drawEllipse( focusRect ); + painter->restore(); + } +} + +/*! + Draw the frame around the dial + + \param painter Painter + \sa lineWidth(), frameShadow() +*/ +void QwtDial::drawFrame( QPainter *painter ) +{ + if ( lineWidth() <= 0 ) + return; + + const double lw2 = 0.5 * lineWidth(); + + QRectF r = boundingRect(); + r.adjust( lw2, lw2, -lw2, -lw2 ); + + QPen pen; + + switch ( d_data->frameShadow ) + { + case QwtDial::Raised: + case QwtDial::Sunken: + { + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Dark ); + + if ( d_data->frameShadow == QwtDial::Sunken ) + qSwap( c1, c2 ); + + QLinearGradient gradient( r.topLeft(), r.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); +#if 0 + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); +#endif + gradient.setColorAt( 1.0, c2 ); + + pen = QPen( gradient, lineWidth() ); + break; + } + default: // Plain + { + pen = QPen( palette().brush( QPalette::Dark ), lineWidth() ); + } + } + + painter->save(); + + painter->setPen( pen ); + painter->setBrush( Qt::NoBrush ); + painter->drawEllipse( r ); + + painter->restore(); +} + +/*! + \brief Draw the contents inside the frame + + QPalette::Window is the background color outside of the frame. + QPalette::Base is the background color inside the frame. + QPalette::WindowText is the background color inside the scale. + + \param painter Painter + \sa boundingRect(), innerRect(), + scaleInnerRect(), QWidget::setPalette() +*/ +void QwtDial::drawContents( QPainter *painter ) const +{ + if ( testAttribute( Qt::WA_NoSystemBackground ) || + palette().brush( QPalette::Base ) != + palette().brush( QPalette::Window ) ) + { + const QRectF br = boundingRect(); + + painter->save(); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::Base ) ); + painter->drawEllipse( br ); + painter->restore(); + } + + + const QRectF insideScaleRect = scaleInnerRect(); + if ( palette().brush( QPalette::WindowText ) != + palette().brush( QPalette::Base ) ) + { + painter->save(); + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( QPalette::WindowText ) ); + painter->drawEllipse( insideScaleRect ); + painter->restore(); + } + + const QPointF center = insideScaleRect.center(); + const double radius = 0.5 * insideScaleRect.width(); + + double direction = d_data->origin; + + if ( isValid() ) + { + direction = d_data->minScaleArc; + if ( maxValue() > minValue() && + d_data->maxScaleArc > d_data->minScaleArc ) + { + const double ratio = + ( value() - minValue() ) / ( maxValue() - minValue() ); + direction += ratio * ( d_data->maxScaleArc - d_data->minScaleArc ); + } + + if ( d_data->direction == QwtDial::CounterClockwise ) + direction = d_data->maxScaleArc - ( direction - d_data->minScaleArc ); + + direction += d_data->origin; + if ( direction >= 360.0 ) + direction -= 360.0; + else if ( direction < 0.0 ) + direction += 360.0; + } + + double origin = d_data->origin; + if ( mode() == RotateScale ) + { + origin -= direction - d_data->origin; + direction = d_data->origin; + } + + painter->save(); + drawScale( painter, center, radius, origin, + d_data->minScaleArc, d_data->maxScaleArc ); + painter->restore(); + + painter->save(); + drawScaleContents( painter, center, radius ); + painter->restore(); + + if ( isValid() ) + { + QPalette::ColorGroup cg; + if ( isEnabled() ) + cg = hasFocus() ? QPalette::Active : QPalette::Inactive; + else + cg = QPalette::Disabled; + + painter->save(); + drawNeedle( painter, center, radius, direction, cg ); + painter->restore(); + } +} + +/*! + Draw the needle + + \param painter Painter + \param center Center of the dial + \param radius Length for the needle + \param direction Direction of the needle in degrees, counter clockwise + \param cg ColorGroup +*/ +void QwtDial::drawNeedle( QPainter *painter, const QPointF ¢er, + double radius, double direction, QPalette::ColorGroup cg ) const +{ + if ( d_data->needle ) + { + direction = 360.0 - direction; // counter clockwise + d_data->needle->draw( painter, center, radius, direction, cg ); + } +} + +/*! + Draw the scale + + \param painter Painter + \param center Center of the dial + \param radius Radius of the scale + \param origin Origin of the scale + \param minArc Minimum of the arc + \param maxArc Minimum of the arc + + \sa QwtRoundScaleDraw::setAngleRange() +*/ +void QwtDial::drawScale( QPainter *painter, const QPointF ¢er, + double radius, double origin, double minArc, double maxArc ) const +{ + if ( d_data->scaleDraw == NULL ) + return; + + origin -= 270.0; // hardcoded origin of QwtScaleDraw + + double angle = maxArc - minArc; + if ( angle > 360.0 ) + angle = ::fmod( angle, 360.0 ); + + minArc += origin; + if ( minArc < -360.0 ) + minArc = ::fmod( minArc, 360.0 ); + + maxArc = minArc + angle; + if ( maxArc > 360.0 ) + { + // QwtRoundScaleDraw::setAngleRange accepts only values + // in the range [-360.0..360.0] + minArc -= 360.0; + maxArc -= 360.0; + } + + if ( d_data->direction == QwtDial::CounterClockwise ) + qSwap( minArc, maxArc ); + + painter->setFont( font() ); + + d_data->scaleDraw->setAngleRange( minArc, maxArc ); + d_data->scaleDraw->setRadius( qFloor( radius ) ); + d_data->scaleDraw->moveCenter( center ); + + QPalette pal = palette(); + + const QColor textColor = pal.color( QPalette::Text ); + pal.setColor( QPalette::WindowText, textColor ); //ticks, backbone + + painter->setPen( QPen( textColor, d_data->scaleDraw->penWidth() ) ); + + painter->setBrush( Qt::red ); + d_data->scaleDraw->draw( painter, pal ); +} + +/*! + Draw the contents inside the scale + + Paints nothing. + + \param painter Painter + \param center Center of the contents circle + \param radius Radius of the contents circle +*/ + +void QwtDial::drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const +{ + Q_UNUSED(painter); + Q_UNUSED(center); + Q_UNUSED(radius); +} + +/*! + Set a needle for the dial + + Qwt is missing a set of good looking needles. + Contributions are very welcome. + + \param needle Needle + \warning The needle will be deleted, when a different needle is + set or in ~QwtDial() +*/ +void QwtDial::setNeedle( QwtDialNeedle *needle ) +{ + if ( needle != d_data->needle ) + { + if ( d_data->needle ) + delete d_data->needle; + + d_data->needle = needle; + update(); + } +} + +/*! + \return needle + \sa setNeedle() +*/ +const QwtDialNeedle *QwtDial::needle() const +{ + return d_data->needle; +} + +/*! + \return needle + \sa setNeedle() +*/ +QwtDialNeedle *QwtDial::needle() +{ + return d_data->needle; +} + +//! QwtDoubleRange update hook +void QwtDial::rangeChange() +{ + updateScale(); +} + +/*! + Update the scale with the current attributes + \sa setScale() +*/ +void QwtDial::updateScale() +{ + if ( d_data->scaleDraw ) + { + QwtLinearScaleEngine scaleEngine; + + const QwtScaleDiv scaleDiv = scaleEngine.divideScale( + minValue(), maxValue(), + d_data->maxMajIntv, d_data->maxMinIntv, d_data->scaleStep ); + + d_data->scaleDraw->setTransformation( scaleEngine.transformation() ); + d_data->scaleDraw->setScaleDiv( scaleDiv ); + } +} + +//! Return the scale draw +QwtDialScaleDraw *QwtDial::scaleDraw() +{ + return d_data->scaleDraw; +} + +//! Return the scale draw +const QwtDialScaleDraw *QwtDial::scaleDraw() const +{ + return d_data->scaleDraw; +} + +/*! + Set an individual scale draw + + \param scaleDraw Scale draw + \warning The previous scale draw is deleted +*/ +void QwtDial::setScaleDraw( QwtDialScaleDraw *scaleDraw ) +{ + if ( scaleDraw != d_data->scaleDraw ) + { + if ( d_data->scaleDraw ) + delete d_data->scaleDraw; + + d_data->scaleDraw = scaleDraw; + updateScale(); + update(); + } +} + +/*! + Change the intervals of the scale + + \param maxMajIntv Maximum for the number of major steps + \param maxMinIntv Maximum number of minor steps + \param step Step size + + \sa QwtScaleEngine::divideScale() +*/ +void QwtDial::setScale( int maxMajIntv, int maxMinIntv, double step ) +{ + d_data->maxMajIntv = maxMajIntv; + d_data->maxMinIntv = maxMinIntv; + d_data->scaleStep = step; + + updateScale(); +} + +/*! + A wrapper method for accessing the scale draw. + + \param components Scale components + \sa QwtAbstractScaleDraw::enableComponent() +*/ +void QwtDial::setScaleComponents( + QwtAbstractScaleDraw::ScaleComponents components ) +{ + if ( components == 0 ) + setScaleDraw( NULL ); + + QwtDialScaleDraw *sd = d_data->scaleDraw; + if ( sd == NULL ) + return; + + sd->enableComponent( QwtAbstractScaleDraw::Backbone, + components & QwtAbstractScaleDraw::Backbone ); + + sd->enableComponent( QwtAbstractScaleDraw::Ticks, + components & QwtAbstractScaleDraw::Ticks ); + + sd->enableComponent( QwtAbstractScaleDraw::Labels, + components & QwtAbstractScaleDraw::Labels ); +} + +/*! + Assign length and width of the ticks + + \param minLen Length of the minor ticks + \param medLen Length of the medium ticks + \param majLen Length of the major ticks + \param penWidth Width of the pen for all ticks + + \sa QwtAbstractScaleDraw::setTickLength(), QwtDialScaleDraw::setPenWidth() +*/ +void QwtDial::setScaleTicks( int minLen, int medLen, + int majLen, int penWidth ) +{ + QwtDialScaleDraw *sd = d_data->scaleDraw; + if ( sd ) + { + sd->setTickLength( QwtScaleDiv::MinorTick, minLen ); + sd->setTickLength( QwtScaleDiv::MediumTick, medLen ); + sd->setTickLength( QwtScaleDiv::MajorTick, majLen ); + sd->setPenWidth( penWidth ); + } +} + +/*! + Find the label for a value + + \param value Value + \return label +*/ +QwtText QwtDial::scaleLabel( double value ) const +{ + if ( value == -0.0 ) + value = 0.0; + + return QString::number( value ); +} + +//! \return Lower limit of the scale arc +double QwtDial::minScaleArc() const +{ + return d_data->minScaleArc; +} + +//! \return Upper limit of the scale arc +double QwtDial::maxScaleArc() const +{ + return d_data->maxScaleArc; +} + +/*! + \brief Change the origin + + The origin is the angle where scale and needle is relative to. + + \param origin New origin + \sa origin() +*/ +void QwtDial::setOrigin( double origin ) +{ + d_data->origin = origin; + update(); +} + +/*! + The origin is the angle where scale and needle is relative to. + + \return Origin of the dial + \sa setOrigin() +*/ +double QwtDial::origin() const +{ + return d_data->origin; +} + +/*! + Change the arc of the scale + + \param minArc Lower limit + \param maxArc Upper limit +*/ +void QwtDial::setScaleArc( double minArc, double maxArc ) +{ + if ( minArc != 360.0 && minArc != -360.0 ) + minArc = ::fmod( minArc, 360.0 ); + if ( maxArc != 360.0 && maxArc != -360.0 ) + maxArc = ::fmod( maxArc, 360.0 ); + + d_data->minScaleArc = qMin( minArc, maxArc ); + d_data->maxScaleArc = qMax( minArc, maxArc ); + if ( d_data->maxScaleArc - d_data->minScaleArc > 360.0 ) + d_data->maxScaleArc = d_data->minScaleArc + 360.0; + + update(); +} + +//! QwtDoubleRange update hook +void QwtDial::valueChange() +{ + update(); + QwtAbstractSlider::valueChange(); +} + +/*! + \return Size hint +*/ +QSize QwtDial::sizeHint() const +{ + int sh = 0; + if ( d_data->scaleDraw ) + sh = qCeil( d_data->scaleDraw->extent( font() ) ); + + const int d = 6 * sh + 2 * lineWidth(); + + QSize hint( d, d ); + if ( !isReadOnly() ) + hint = hint.expandedTo( QApplication::globalStrut() ); + + return hint; +} + +/*! + \brief Return a minimum size hint + \warning The return value of QwtDial::minimumSizeHint() depends on the + font and the scale. +*/ +QSize QwtDial::minimumSizeHint() const +{ + int sh = 0; + if ( d_data->scaleDraw ) + sh = qCeil( d_data->scaleDraw->extent( font() ) ); + + const int d = 3 * sh + 2 * lineWidth(); + + return QSize( d, d ); +} + +static double line2Degrees( const QPointF &p1, const QPointF &p2 ) +{ + const QPointF p = p2 - p1; + + double angle; + if ( p.x() == 0.0 ) + { + angle = ( p.y() <= 0.0 ) ? M_PI_2 : 3 * M_PI_2; + } + else + { + angle = qAtan( double( -p.y() ) / double( p.x() ) ); + if ( p.x() < 0.0 ) + angle += M_PI; + if ( angle < 0.0 ) + angle += 2 * M_PI; + } + return 360.0 - angle * 180.0 / M_PI; +} + +/*! + Find the value for a given position + + \param pos Position + \return Value +*/ +double QwtDial::getValue( const QPoint &pos ) +{ + if ( d_data->maxScaleArc == d_data->minScaleArc || maxValue() == minValue() ) + return minValue(); + + double dir = line2Degrees( innerRect().center(), pos ) - d_data->origin; + if ( dir < 0.0 ) + dir += 360.0; + + if ( mode() == RotateScale ) + dir = 360.0 - dir; + + // The position might be in the area that is outside the scale arc. + // We need the range of the scale if it was a complete circle. + + const double completeCircle = 360.0 / ( d_data->maxScaleArc - d_data->minScaleArc ) + * ( maxValue() - minValue() ); + + double posValue = minValue() + completeCircle * dir / 360.0; + + if ( scrollMode() == ScrMouse ) + { + if ( d_data->previousDir >= 0.0 ) // valid direction + { + // We have to find out whether the mouse is moving + // clock or counter clockwise + + bool clockWise = false; + + const double angle = dir - d_data->previousDir; + if ( ( angle >= 0.0 && angle <= 180.0 ) || angle < -180.0 ) + clockWise = true; + + if ( clockWise ) + { + if ( dir < d_data->previousDir && mouseOffset() > 0.0 ) + { + // We passed 360 -> 0 + setMouseOffset( mouseOffset() - completeCircle ); + } + + if ( wrapping() ) + { + if ( posValue - mouseOffset() > maxValue() ) + { + // We passed maxValue and the value will be set + // to minValue. We have to adjust the mouseOffset. + + setMouseOffset( posValue - minValue() ); + } + } + else + { + if ( posValue - mouseOffset() > maxValue() || + value() == maxValue() ) + { + // We fix the value at maxValue by adjusting + // the mouse offset. + + setMouseOffset( posValue - maxValue() ); + } + } + } + else + { + if ( dir > d_data->previousDir && mouseOffset() < 0.0 ) + { + // We passed 0 -> 360 + setMouseOffset( mouseOffset() + completeCircle ); + } + + if ( wrapping() ) + { + if ( posValue - mouseOffset() < minValue() ) + { + // We passed minValue and the value will be set + // to maxValue. We have to adjust the mouseOffset. + + setMouseOffset( posValue - maxValue() ); + } + } + else + { + if ( posValue - mouseOffset() < minValue() || + value() == minValue() ) + { + // We fix the value at minValue by adjusting + // the mouse offset. + + setMouseOffset( posValue - minValue() ); + } + } + } + } + d_data->previousDir = dir; + } + + return posValue; +} + +/*! + See QwtAbstractSlider::getScrollMode() + + \param pos point where the mouse was pressed + \retval scrollMode The scrolling mode + \retval direction direction: 1, 0, or -1. + + \sa QwtAbstractSlider::getScrollMode() +*/ +void QwtDial::getScrollMode( const QPoint &pos, + QwtAbstractSlider::ScrollMode &scrollMode, int &direction ) const +{ + direction = 0; + scrollMode = QwtAbstractSlider::ScrNone; + + const QRegion region( innerRect().toRect(), QRegion::Ellipse ); + if ( region.contains( pos ) && pos != innerRect().center() ) + { + scrollMode = QwtAbstractSlider::ScrMouse; + d_data->previousDir = -1.0; + } +} + +/*! + Handles key events + + - Key_Down, KeyLeft\n + Decrement by 1 + - Key_Prior\n + Decrement by pageSize() + - Key_Home\n + Set the value to minValue() + + - Key_Up, KeyRight\n + Increment by 1 + - Key_Next\n + Increment by pageSize() + - Key_End\n + Set the value to maxValue() + + \param event Key event + \sa isReadOnly() +*/ +void QwtDial::keyPressEvent( QKeyEvent *event ) +{ + if ( isReadOnly() ) + { + event->ignore(); + return; + } + + if ( !isValid() ) + return; + + const double previousValue = value(); + + switch ( event->key() ) + { + case Qt::Key_Down: + case Qt::Key_Left: + QwtDoubleRange::incValue( -1 ); + break; + case Qt::Key_PageUp: + QwtDoubleRange::incValue( -pageSize() ); + break; + case Qt::Key_Home: + setValue( minValue() ); + break; + + case Qt::Key_Up: + case Qt::Key_Right: + QwtDoubleRange::incValue( 1 ); + break; + case Qt::Key_PageDown: + QwtDoubleRange::incValue( pageSize() ); + break; + case Qt::Key_End: + setValue( maxValue() ); + break; + default:; + event->ignore(); + } + + if ( value() != previousValue ) + Q_EMIT sliderMoved( value() ); +} diff --git a/src/libpcp_qwt/src/qwt_dial.h b/src/libpcp_qwt/src/qwt_dial.h new file mode 100644 index 0000000..128ceda --- /dev/null +++ b/src/libpcp_qwt/src/qwt_dial.h @@ -0,0 +1,215 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_DIAL_H +#define QWT_DIAL_H 1 + +#include "qwt_global.h" +#include "qwt_abstract_slider.h" +#include "qwt_round_scale_draw.h" +#include <qframe.h> +#include <qpalette.h> + +class QwtDialNeedle; +class QwtDial; + +/*! + \brief A special scale draw made for QwtDial + + \sa QwtDial, QwtCompass +*/ +class QWT_EXPORT QwtDialScaleDraw: public QwtRoundScaleDraw +{ +public: + explicit QwtDialScaleDraw( QwtDial * ); + + virtual QwtText label( double value ) const; + + void setPenWidth( double ); + double penWidth() const; + +private: + QwtDial *d_parent; + double d_penWidth; +}; + +/*! + \brief QwtDial class provides a rounded range control. + + QwtDial is intended as base class for dial widgets like + speedometers, compass widgets, clocks ... + + \image html dials2.png + + A dial contains a scale and a needle indicating the current value + of the dial. Depending on Mode one of them is fixed and the + other is rotating. If not isReadOnly() the + dial can be rotated by dragging the mouse or using keyboard inputs + (see keyPressEvent()). A dial might be wrapping, what means + a rotation below/above one limit continues on the other limit (f.e compass). + The scale might cover any arc of the dial, its values are related to + the origin() of the dial. + + Qwt is missing a set of good looking needles (QwtDialNeedle). + Contributions are very welcome. + + \sa QwtCompass, QwtAnalogClock, QwtDialNeedle + \note The examples/dials example shows different types of dials. +*/ + +class QWT_EXPORT QwtDial: public QwtAbstractSlider +{ + Q_OBJECT + + Q_ENUMS( Shadow ) + Q_ENUMS( Mode ) + Q_ENUMS( Direction ) + + Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth ) + Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow ) + Q_PROPERTY( Mode mode READ mode WRITE setMode ) + Q_PROPERTY( double origin READ origin WRITE setOrigin ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) + Q_PROPERTY( Direction direction READ direction WRITE setDirection ) + + friend class QwtDialScaleDraw; +public: + + /*! + \brief Frame shadow + + Unfortunately it is not possible to use QFrame::Shadow + as a property of a widget that is not derived from QFrame. + The following enum is made for the designer only. It is safe + to use QFrame::Shadow instead. + */ + enum Shadow + { + //! QFrame::Plain + Plain = QFrame::Plain, + + //! QFrame::Raised + Raised = QFrame::Raised, + + //! QFrame::Sunken + Sunken = QFrame::Sunken + }; + + //! Mode controlling wether the needle or the scale is rotating + enum Mode + { + //! The needle is rotating + RotateNeedle, + + //! The needle is fixed, the scales are rotating + RotateScale + }; + + //! Direction of the dial + enum Direction + { + //! Clockwise + Clockwise, + + //! Counter clockwise + CounterClockwise + }; + + explicit QwtDial( QWidget *parent = NULL ); + virtual ~QwtDial(); + + void setFrameShadow( Shadow ); + Shadow frameShadow() const; + + void setLineWidth( int ); + int lineWidth() const; + + void setMode( Mode ); + Mode mode() const; + + virtual void setWrapping( bool ); + bool wrapping() const; + + virtual void setScale( int maxMajIntv, int maxMinIntv, double step = 0.0 ); + + void setScaleArc( double min, double max ); + void setScaleComponents( QwtAbstractScaleDraw::ScaleComponents ); + void setScaleTicks( int minLen, int medLen, int majLen, int penWidth = 1 ); + + double minScaleArc() const; + double maxScaleArc() const; + + virtual void setOrigin( double ); + double origin() const; + + void setDirection( Direction ); + Direction direction() const; + + virtual void setNeedle( QwtDialNeedle * ); + const QwtDialNeedle *needle() const; + QwtDialNeedle *needle(); + + QRectF boundingRect() const; + QRectF innerRect() const; + virtual QRectF scaleInnerRect() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + virtual void setScaleDraw( QwtDialScaleDraw * ); + + QwtDialScaleDraw *scaleDraw(); + const QwtDialScaleDraw *scaleDraw() const; + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + + virtual void drawFrame( QPainter *p ); + virtual void drawContents( QPainter * ) const; + virtual void drawFocusIndicator( QPainter * ) const; + + virtual void drawScale( + QPainter *, const QPointF ¢er, + double radius, double origin, + double arcMin, double arcMax ) const; + + /*! + Draw the contents inside the scale + + Paints nothing. + + \param painter Painter + \param center Center of the contents circle + \param radius Radius of the contents circle + */ + virtual void drawScaleContents( QPainter *painter, + const QPointF ¢er, double radius ) const; + + virtual void drawNeedle( QPainter *, const QPointF &, + double radius, double direction, QPalette::ColorGroup ) const; + + virtual QwtText scaleLabel( double ) const; + void updateScale(); + + virtual void rangeChange(); + virtual void valueChange(); + + virtual double getValue( const QPoint & ); + virtual void getScrollMode( const QPoint &, + QwtAbstractSlider::ScrollMode &, int &direction ) const; + +private: + void initDial(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_dial_needle.cpp b/src/libpcp_qwt/src/qwt_dial_needle.cpp new file mode 100644 index 0000000..3888c11 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_dial_needle.cpp @@ -0,0 +1,441 @@ +/* -*- 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_dial_needle.h" +#include "qwt_global.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include <qapplication.h> +#include <qpainter.h> + +#if QT_VERSION < 0x040601 +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) +#endif + +static void qwtDrawStyle1Needle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length ) +{ + const double r[] = { 0.4, 0.3, 1, 0.8, 1, 0.3, 0.4 }; + const double a[] = { -45, -20, -15, 0, 15, 20, 45 }; + + QPainterPath path; + for ( int i = 0; i < 7; i++ ) + { + const double angle = a[i] / 180.0 * M_PI; + const double radius = r[i] * length; + + const double x = radius * qFastCos( angle ); + const double y = radius * qFastSin( angle ); + + path.lineTo( x, -y ); + } + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette.brush( colorGroup, QPalette::Light ) ); + painter->drawPath( path ); +} + +static void qwtDrawStyle2Needle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, double length ) +{ + const double ratioX = 0.7; + const double ratioY = 0.3; + + QPainterPath path1; + path1.lineTo( ratioX * length, 0.0 ); + path1.lineTo( length, ratioY * length ); + + QPainterPath path2; + path2.lineTo( ratioX * length, 0.0 ); + path2.lineTo( length, -ratioY * length ); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( palette.brush( colorGroup, QPalette::Light ) ); + painter->drawPath( path1 ); + + painter->setBrush( palette.brush( colorGroup, QPalette::Dark ) ); + painter->drawPath( path2 ); +} + +static void qwtDrawShadedPointer( QPainter *painter, + const QColor &lightColor, const QColor &darkColor, + double length, double width ) +{ + const double peak = qMax( length / 10.0, 5.0 ); + + const double knobWidth = width + 8; + QRectF knobRect( 0, 0, knobWidth, knobWidth ); + knobRect.moveCenter( QPointF(0, 0) ); + + QPainterPath path1; + path1.lineTo( 0.0, 0.5 * width ); + path1.lineTo( length - peak, 0.5 * width ); + path1.lineTo( length, 0.0 ); + path1.lineTo( 0.0, 0.0 ); + + QPainterPath arcPath1; + arcPath1.arcTo( knobRect, 0.0, -90.0 ); + + path1 = path1.united( arcPath1 ); + + QPainterPath path2; + path2.lineTo( 0.0, -0.5 * width ); + path2.lineTo( length - peak, -0.5 * width ); + path2.lineTo( length, 0.0 ); + path2.lineTo( 0.0, 0.0 ); + + QPainterPath arcPath2; + arcPath2.arcTo( knobRect, 0.0, 90.0 ); + + path2 = path2.united( arcPath2 ); + + painter->setPen( Qt::NoPen ); + + painter->setBrush( lightColor ); + painter->drawPath( path1 ); + + painter->setBrush( darkColor ); + painter->drawPath( path2 ); +} + +static void qwtDrawArrowNeedle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length, double width ) +{ + if ( width <= 0 ) + width = qMax( length * 0.06, 9.0 ); + + const double peak = qMax( 2.0, 0.4 * width ); + + QPainterPath path; + path.moveTo( 0.0, 0.5 * width ); + path.lineTo( length - peak, 0.3 * width ); + path.lineTo( length, 0.0 ); + path.lineTo( length - peak, -0.3 * width ); + path.lineTo( 0.0, -0.5 * width ); + + QRectF br = path.boundingRect(); + + QPalette pal( palette.color( QPalette::Mid ) ); + QColor c1 = pal.color( QPalette::Light ); + QColor c2 = pal.color( QPalette::Dark ); + + QLinearGradient gradient( br.topLeft(), br.bottomLeft() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.5, c1 ); + gradient.setColorAt( 0.5001, c2 ); + gradient.setColorAt( 1.0, c2 ); + + QPen pen( gradient, 1 ); + pen.setJoinStyle( Qt::MiterJoin ); + + painter->setPen( pen ); + painter->setBrush( palette.brush( colorGroup, QPalette::Mid ) ); + + painter->drawPath( path ); +} + +static void qwtDrawTriangleNeedle( QPainter *painter, + const QPalette &palette, QPalette::ColorGroup colorGroup, + double length ) +{ + const double width = qRound( length / 3.0 ); + + QPainterPath path[4]; + + path[0].lineTo( length, 0.0 ); + path[0].lineTo( 0.0, width / 2 ); + + path[1].lineTo( length, 0.0 ); + path[1].lineTo( 0.0, -width / 2 ); + + path[2].lineTo( -length, 0.0 ); + path[2].lineTo( 0.0, width / 2 ); + + path[3].lineTo( -length, 0.0 ); + path[3].lineTo( 0.0, -width / 2 ); + + + const int colorOffset = 10; + const QColor darkColor = palette.color( colorGroup, QPalette::Dark ); + const QColor lightColor = palette.color( colorGroup, QPalette::Light ); + + QColor color[4]; + color[0] = darkColor.light( 100 + colorOffset ); + color[1] = darkColor.dark( 100 + colorOffset ); + color[2] = lightColor.light( 100 + colorOffset ); + color[3] = lightColor.dark( 100 + colorOffset ); + + painter->setPen( Qt::NoPen ); + + for ( int i = 0; i < 4; i++ ) + { + painter->setBrush( color[i] ); + painter->drawPath( path[i] ); + } +} + +//! Constructor +QwtDialNeedle::QwtDialNeedle(): + d_palette( QApplication::palette() ) +{ +} + +//! Destructor +QwtDialNeedle::~QwtDialNeedle() +{ +} + +/*! + Sets the palette for the needle. + + \param palette New Palette +*/ +void QwtDialNeedle::setPalette( const QPalette &palette ) +{ + d_palette = palette; +} + +/*! + \return the palette of the needle. +*/ +const QPalette &QwtDialNeedle::palette() const +{ + return d_palette; +} + +/*! + Draw the needle + + \param painter Painter + \param center Center of the dial, start position for the needle + \param length Length of the needle + \param direction Direction of the needle, in degrees counter clockwise + \param colorGroup Color group, used for painting +*/ +void QwtDialNeedle::draw( QPainter *painter, + const QPointF ¢er, double length, double direction, + QPalette::ColorGroup colorGroup ) const +{ + painter->save(); + + painter->translate( center ); + painter->rotate( -direction ); + + drawNeedle( painter, length, colorGroup ); + + painter->restore(); +} + +//! Draw the knob +void QwtDialNeedle::drawKnob( QPainter *painter, + double width, const QBrush &brush, bool sunken ) const +{ + QPalette palette( brush.color() ); + + QColor c1 = palette.color( QPalette::Light ); + QColor c2 = palette.color( QPalette::Dark ); + + if ( sunken ) + qSwap( c1, c2 ); + + QRectF rect( 0.0, 0.0, width, width ); + rect.moveCenter( painter->combinedTransform().map( QPointF() ) ); + + QLinearGradient gradient( rect.topLeft(), rect.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); + gradient.setColorAt( 1.0, c2 ); + + painter->save(); + + painter->resetTransform(); + + painter->setPen( QPen( gradient, 1 ) ); + painter->setBrush( brush ); + painter->drawEllipse( rect ); + + painter->restore(); +} + +/*! + Constructor + + \param style Style + \param hasKnob With/Without knob + \param mid Middle color + \param base Base color +*/ +QwtDialSimpleNeedle::QwtDialSimpleNeedle( Style style, bool hasKnob, + const QColor &mid, const QColor &base ): + d_style( style ), + d_hasKnob( hasKnob ), + d_width( -1 ) +{ + QPalette palette; + palette.setColor( QPalette::Mid, mid ); + palette.setColor( QPalette::Base, base ); + + setPalette( palette ); +} + +/*! + Set the width of the needle + \param width Width + \sa width() +*/ +void QwtDialSimpleNeedle::setWidth( double width ) +{ + d_width = width; +} + +/*! + \return the width of the needle + \sa setWidth() +*/ +double QwtDialSimpleNeedle::width() const +{ + return d_width; +} + +/*! + Draw the needle + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtDialSimpleNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + double knobWidth = 0.0; + double width = d_width; + + if ( d_style == Arrow ) + { + if ( width <= 0.0 ) + width = qMax(length * 0.06, 6.0); + + qwtDrawArrowNeedle( painter, + palette(), colorGroup, length, width ); + + knobWidth = qMin( width * 2.0, 0.2 * length ); + } + else + { + if ( width <= 0.0 ) + width = 5.0; + + QPen pen ( palette().brush( colorGroup, QPalette::Mid ), width ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->drawLine( QPointF( 0.0, 0.0 ), QPointF( length, 0.0 ) ); + + knobWidth = qMax( width * 3.0, 5.0 ); + } + + if ( d_hasKnob && knobWidth > 0.0 ) + { + drawKnob( painter, knobWidth, + palette().brush( colorGroup, QPalette::Base ), false ); + } +} + +//! Constructor + +QwtCompassMagnetNeedle::QwtCompassMagnetNeedle( Style style, + const QColor &light, const QColor &dark ): + d_style( style ) +{ + QPalette palette; + palette.setColor( QPalette::Light, light ); + palette.setColor( QPalette::Dark, dark ); + palette.setColor( QPalette::Base, Qt::gray ); + + setPalette( palette ); +} + +/*! + Draw the needle + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtCompassMagnetNeedle::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + if ( d_style == ThinStyle ) + { + const double width = qMax( length / 6.0, 3.0 ); + + const int colorOffset = 10; + + const QColor light = palette().color( colorGroup, QPalette::Light ); + const QColor dark = palette().color( colorGroup, QPalette::Dark ); + + qwtDrawShadedPointer( painter, + dark.light( 100 + colorOffset ), + dark.dark( 100 + colorOffset ), + length, width ); + + painter->rotate( 180.0 ); + + qwtDrawShadedPointer( painter, + light.light( 100 + colorOffset ), + light.dark( 100 + colorOffset ), + length, width ); + + const QBrush baseBrush = palette().brush( colorGroup, QPalette::Base ); + drawKnob( painter, width, baseBrush, true ); + } + else + { + qwtDrawTriangleNeedle( painter, palette(), colorGroup, length ); + } +} + +/*! + Constructor + + \param style Arrow style + \param light Light color + \param dark Dark color +*/ +QwtCompassWindArrow::QwtCompassWindArrow( Style style, + const QColor &light, const QColor &dark ): + d_style( style ) +{ + QPalette palette; + palette.setColor( QPalette::Light, light ); + palette.setColor( QPalette::Dark, dark ); + + setPalette( palette ); +} + +/*! + Draw the needle + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting +*/ +void QwtCompassWindArrow::drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const +{ + if ( d_style == Style1 ) + qwtDrawStyle1Needle( painter, palette(), colorGroup, length ); + else + qwtDrawStyle2Needle( painter, palette(), colorGroup, length ); +} diff --git a/src/libpcp_qwt/src/qwt_dial_needle.h b/src/libpcp_qwt/src/qwt_dial_needle.h new file mode 100644 index 0000000..a02a590 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_dial_needle.h @@ -0,0 +1,190 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_DIAL_NEEDLE_H +#define QWT_DIAL_NEEDLE_H 1 + +#include "qwt_global.h" +#include <qpalette.h> + +class QPainter; +class QPoint; + +/*! + \brief Base class for needles that can be used in a QwtDial. + + QwtDialNeedle is a pointer that indicates a value by pointing + to a specific direction. + + Qwt is missing a set of good looking needles. + Contributions are very welcome. + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtDialNeedle +{ +public: + QwtDialNeedle(); + virtual ~QwtDialNeedle(); + + virtual void setPalette( const QPalette & ); + const QPalette &palette() const; + + virtual void draw( QPainter *painter, const QPointF ¢er, + double length, double direction, + QPalette::ColorGroup = QPalette::Active ) const; + +protected: + /*! + \brief Draw the needle + + The origin of the needle is at position (0.0, 0.0 ) + pointing in direction 0.0 ( = east ). + + The painter is already initilaized with translation and + rotation. + + \param painter Painter + \param length Length of the needle + \param colorGroup Color group, used for painting + + \sa setPalette(), palette() + */ + virtual void drawNeedle( QPainter *painter, + double length, QPalette::ColorGroup colorGroup ) const = 0; + + virtual void drawKnob( QPainter *, double width, + const QBrush &, bool sunken ) const; + +private: + QPalette d_palette; +}; + +/*! + \brief A needle for dial widgets + + The following colors are used: + + - QPalette::Mid\n + Pointer + - QPalette::Base\n + Knob + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtDialSimpleNeedle: public QwtDialNeedle +{ +public: + //! Style of the needle + enum Style + { + //! Arrow + Arrow, + + //! A straight line from the center + Ray + }; + + QwtDialSimpleNeedle( Style, bool hasKnob = true, + const QColor &mid = Qt::gray, const QColor &base = Qt::darkGray ); + + void setWidth( double width ); + double width() const; + +protected: + virtual void drawNeedle( QPainter *, double length, + QPalette::ColorGroup ) const; + +private: + Style d_style; + bool d_hasKnob; + double d_width; +}; + +/*! + \brief A magnet needle for compass widgets + + A magnet needle points to two opposite directions indicating + north and south. + + The following colors are used: + - QPalette::Light\n + Used for pointing south + - QPalette::Dark\n + Used for pointing north + - QPalette::Base\n + Knob (ThinStyle only) + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtCompassMagnetNeedle: public QwtDialNeedle +{ +public: + //! Style of the needle + enum Style + { + //! A needle with a triangular shape + TriangleStyle, + + //! A thin needle + ThinStyle + }; + + QwtCompassMagnetNeedle( Style = TriangleStyle, + const QColor &light = Qt::white, const QColor &dark = Qt::red ); + +protected: + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; + +private: + Style d_style; +}; + +/*! + \brief An indicator for the wind direction + + QwtCompassWindArrow shows the direction where the wind comes from. + + - QPalette::Light\n + Used for Style1, or the light half of Style2 + - QPalette::Dark\n + Used for the dark half of Style2 + + \sa QwtDial, QwtCompass +*/ + +class QWT_EXPORT QwtCompassWindArrow: public QwtDialNeedle +{ +public: + //! Style of the arrow + enum Style + { + //! A needle pointing to the center + Style1, + + //! A needle pointing to the center + Style2 + }; + + QwtCompassWindArrow( Style, const QColor &light = Qt::white, + const QColor &dark = Qt::gray ); + +protected: + virtual void drawNeedle( QPainter *, + double length, QPalette::ColorGroup ) const; + +private: + Style d_style; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_double_range.cpp b/src/libpcp_qwt/src/qwt_double_range.cpp new file mode 100644 index 0000000..b073eab --- /dev/null +++ b/src/libpcp_qwt/src/qwt_double_range.cpp @@ -0,0 +1,410 @@ +/* -*- 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_double_range.h" +#include "qwt_math.h" + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#endif + +class QwtDoubleRange::PrivateData +{ +public: + PrivateData(): + minValue( 0.0 ), + maxValue( 0.0 ), + step( 1.0 ), + pageSize( 1 ), + isValid( false ), + value( 0.0 ), + exactValue( 0.0 ), + exactPrevValue( 0.0 ), + prevValue( 0.0 ), + periodic( false ) + { + } + + double minValue; + double maxValue; + double step; + int pageSize; + + bool isValid; + double value; + double exactValue; + double exactPrevValue; + double prevValue; + + bool periodic; +}; + +/*! + The range is initialized to [0.0, 100.0], the + step size to 1.0, and the value to 0.0. +*/ +QwtDoubleRange::QwtDoubleRange() +{ + d_data = new PrivateData(); +} + +//! Destroys the QwtDoubleRange +QwtDoubleRange::~QwtDoubleRange() +{ + delete d_data; +} + +//! Set the value to be valid/invalid +void QwtDoubleRange::setValid( bool isValid ) +{ + if ( isValid != d_data->isValid ) + { + d_data->isValid = isValid; + valueChange(); + } +} + +//! Indicates if the value is valid +bool QwtDoubleRange::isValid() const +{ + return d_data->isValid; +} + +void QwtDoubleRange::setNewValue( double value, bool align ) +{ + d_data->prevValue = d_data->value; + + const double vmin = qMin( d_data->minValue, d_data->maxValue ); + const double vmax = qMax( d_data->minValue, d_data->maxValue ); + + if ( value < vmin ) + { + if ( d_data->periodic && vmin != vmax ) + { + d_data->value = value + + ::ceil( ( vmin - value ) / ( vmax - vmin ) ) * ( vmax - vmin ); + } + else + d_data->value = vmin; + } + else if ( value > vmax ) + { + if ( ( d_data->periodic ) && ( vmin != vmax ) ) + { + d_data->value = value - + ::ceil( ( value - vmax ) / ( vmax - vmin ) ) * ( vmax - vmin ); + } + else + d_data->value = vmax; + } + else + { + d_data->value = value; + } + + d_data->exactPrevValue = d_data->exactValue; + d_data->exactValue = d_data->value; + + if ( align ) + { + if ( d_data->step != 0.0 ) + { + d_data->value = d_data->minValue + + qRound( ( d_data->value - d_data->minValue ) / d_data->step ) * d_data->step; + } + else + d_data->value = d_data->minValue; + + const double minEps = 1.0e-10; + // correct rounding error at the border + if ( qFabs( d_data->value - d_data->maxValue ) < minEps * qAbs( d_data->step ) ) + d_data->value = d_data->maxValue; + + // correct rounding error if value = 0 + if ( qFabs( d_data->value ) < minEps * qAbs( d_data->step ) ) + d_data->value = 0.0; + } + + if ( !d_data->isValid || d_data->prevValue != d_data->value ) + { + d_data->isValid = true; + valueChange(); + } +} + +/*! + \brief Adjust the value to the closest point in the step raster. + \param x value + \warning The value is clipped when it lies outside the range. + When the range is QwtDoubleRange::periodic, it will + be mapped to a point in the interval such that + \verbatim new value := x + n * (max. value - min. value)\endverbatim + with an integer number n. +*/ +void QwtDoubleRange::fitValue( double x ) +{ + setNewValue( x, true ); +} + + +/*! + \brief Set a new value without adjusting to the step raster + \param x new value + \warning The value is clipped when it lies outside the range. + When the range is QwtDoubleRange::periodic, it will + be mapped to a point in the interval such that + \verbatim new value := x + n * (max. value - min. value)\endverbatim + with an integer number n. +*/ +void QwtDoubleRange::setValue( double x ) +{ + setNewValue( x, false ); +} + +/*! + \brief Specify range and step size + + \param vmin lower boundary of the interval + \param vmax higher boundary of the interval + \param vstep step width + \param pageSize page size in steps + \warning + \li A change of the range changes the value if it lies outside the + new range. The current value + will *not* be adjusted to the new step raster. + \li vmax < vmin is allowed. + \li If the step size is left out or set to zero, it will be + set to 1/100 of the interval length. + \li If the step size has an absurd value, it will be corrected + to a better one. +*/ +void QwtDoubleRange::setRange( + double vmin, double vmax, double vstep, int pageSize ) +{ + const bool rchg = ( d_data->maxValue != vmax || d_data->minValue != vmin ); + + if ( rchg ) + { + d_data->minValue = vmin; + d_data->maxValue = vmax; + } + + // look if the step width has an acceptable + // value or otherwise change it. + setStep( vstep ); + + // limit page size + const int max = + int( qAbs( ( d_data->maxValue - d_data->minValue ) / d_data->step ) ); + d_data->pageSize = qBound( 0, pageSize, max ); + + // If the value lies out of the range, it + // will be changed. Note that it will not be adjusted to + // the new step width. + setNewValue( d_data->value, false ); + + // call notifier after the step width has been + // adjusted. + if ( rchg ) + rangeChange(); +} + +/*! + \brief Change the step raster + \param vstep new step width + \warning The value will \e not be adjusted to the new step raster. +*/ +void QwtDoubleRange::setStep( double vstep ) +{ + const double intv = d_data->maxValue - d_data->minValue; + + double newStep; + if ( vstep == 0.0 ) + { + const double defaultRelStep = 1.0e-2; + newStep = intv * defaultRelStep; + } + else + { + if ( ( intv > 0.0 && vstep < 0.0 ) || ( intv < 0.0 && vstep > 0.0 ) ) + newStep = -vstep; + else + newStep = vstep; + + const double minRelStep = 1.0e-10; + if ( qFabs( newStep ) < qFabs( minRelStep * intv ) ) + newStep = minRelStep * intv; + } + + if ( newStep != d_data->step ) + { + d_data->step = newStep; + stepChange(); + } +} + + +/*! + \brief Make the range periodic + + When the range is periodic, the value will be set to a point + inside the interval such that + + \verbatim point = value + n * width \endverbatim + + if the user tries to set a new value which is outside the range. + If the range is nonperiodic (the default), values outside the + range will be clipped. + + \param tf true for a periodic range +*/ +void QwtDoubleRange::setPeriodic( bool tf ) +{ + d_data->periodic = tf; +} + +/*! + \brief Increment the value by a specified number of steps + \param nSteps Number of steps to increment + \warning As a result of this operation, the new value will always be + adjusted to the step raster. +*/ +void QwtDoubleRange::incValue( int nSteps ) +{ + if ( isValid() ) + setNewValue( d_data->value + double( nSteps ) * d_data->step, true ); +} + +/*! + \brief Increment the value by a specified number of pages + \param nPages Number of pages to increment. + A negative number decrements the value. + \warning The Page size is specified in the constructor. +*/ +void QwtDoubleRange::incPages( int nPages ) +{ + if ( isValid() ) + { + const double off = d_data->step * d_data->pageSize * nPages; + setNewValue( d_data->value + off, true ); + } +} + +/*! + \brief Notify a change of value + + This virtual function is called whenever the value changes. + The default implementation does nothing. +*/ +void QwtDoubleRange::valueChange() +{ +} + + +/*! + \brief Notify a change of the range + + This virtual function is called whenever the range changes. + The default implementation does nothing. +*/ +void QwtDoubleRange::rangeChange() +{ +} + + +/*! + \brief Notify a change of the step size + + This virtual function is called whenever the step size changes. + The default implementation does nothing. +*/ +void QwtDoubleRange::stepChange() +{ +} + +/*! + \return the step size + \sa setStep(), setRange() +*/ +double QwtDoubleRange::step() const +{ + return qAbs( d_data->step ); +} + +/*! + \brief Returns the value of the second border of the range + + maxValue returns the value which has been specified + as the second parameter in QwtDoubleRange::setRange. + + \sa setRange() +*/ +double QwtDoubleRange::maxValue() const +{ + return d_data->maxValue; +} + +/*! + \brief Returns the value at the first border of the range + + minValue returns the value which has been specified + as the first parameter in setRange(). + + \sa setRange() +*/ +double QwtDoubleRange::minValue() const +{ + return d_data->minValue; +} + +/*! + \brief Returns true if the range is periodic + \sa setPeriodic() +*/ +bool QwtDoubleRange::periodic() const +{ + return d_data->periodic; +} + +//! Returns the page size in steps. +int QwtDoubleRange::pageSize() const +{ + return d_data->pageSize; +} + +//! Returns the current value. +double QwtDoubleRange::value() const +{ + return d_data->value; +} + +/*! + \brief Returns the exact value + + The exact value is the value which QwtDoubleRange::value would return + if the value were not adjusted to the step raster. It differs from + the current value only if fitValue() or incValue() have been used before. + This function is intended for internal use in derived classes. +*/ +double QwtDoubleRange::exactValue() const +{ + return d_data->exactValue; +} + +//! Returns the exact previous value +double QwtDoubleRange::exactPrevValue() const +{ + return d_data->exactPrevValue; +} + +//! Returns the previous value +double QwtDoubleRange::prevValue() const +{ + return d_data->prevValue; +} diff --git a/src/libpcp_qwt/src/qwt_double_range.h b/src/libpcp_qwt/src/qwt_double_range.h new file mode 100644 index 0000000..29f2f66 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_double_range.h @@ -0,0 +1,78 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_DOUBLE_RANGE_H +#define QWT_DOUBLE_RANGE_H + +#include "qwt_global.h" + +/*! + \brief A class which controls a value within an interval + + This class is useful as a base class or a member for sliders. + It represents an interval of type double within which a value can + be moved. The value can be either an arbitrary point inside + the interval (see QwtDoubleRange::setValue), or it can be fitted + into a step raster (see QwtDoubleRange::fitValue and + QwtDoubleRange::incValue). + + As a special case, a QwtDoubleRange can be periodic, which means that + a value outside the interval will be mapped to a value inside the + interval when QwtDoubleRange::setValue(), QwtDoubleRange::fitValue(), + QwtDoubleRange::incValue() or QwtDoubleRange::incPages() are called. +*/ + +class QWT_EXPORT QwtDoubleRange +{ +public: + QwtDoubleRange(); + virtual ~QwtDoubleRange(); + + void setRange( double vmin, double vmax, + double vstep = 0.0, int pagesize = 1 ); + + void setValid( bool ); + bool isValid() const; + + virtual void setValue( double ); + double value() const; + + void setPeriodic( bool tf ); + bool periodic() const; + + void setStep( double ); + double step() const; + + double maxValue() const; + double minValue() const; + + int pageSize() const; + + virtual void incValue( int ); + virtual void incPages( int ); + virtual void fitValue( double ); + +protected: + + double exactValue() const; + double exactPrevValue() const; + double prevValue() const; + + virtual void valueChange(); + virtual void stepChange(); + virtual void rangeChange(); + +private: + void setNewValue( double value, bool align = false ); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_dyngrid_layout.cpp b/src/libpcp_qwt/src/qwt_dyngrid_layout.cpp new file mode 100644 index 0000000..e6968ae --- /dev/null +++ b/src/libpcp_qwt/src/qwt_dyngrid_layout.cpp @@ -0,0 +1,575 @@ +/* -*- 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_dyngrid_layout.h" +#include "qwt_math.h" +#include <qvector.h> +#include <qlist.h> + +class QwtDynGridLayout::PrivateData +{ +public: + PrivateData(): + isDirty( true ) + { + } + + void updateLayoutCache(); + + mutable QList<QLayoutItem*> itemList; + + uint maxCols; + uint numRows; + uint numCols; + + Qt::Orientations expanding; + + bool isDirty; + QVector<QSize> itemSizeHints; +}; + +void QwtDynGridLayout::PrivateData::updateLayoutCache() +{ + itemSizeHints.resize( itemList.count() ); + + int index = 0; + + for ( QList<QLayoutItem*>::iterator it = itemList.begin(); + it != itemList.end(); ++it, index++ ) + { + itemSizeHints[ index ] = ( *it )->sizeHint(); + } + + isDirty = false; +} + +/*! + \param parent Parent widget + \param margin Margin + \param spacing Spacing +*/ + +QwtDynGridLayout::QwtDynGridLayout( QWidget *parent, + int margin, int spacing ): + QLayout( parent ) +{ + init(); + + setSpacing( spacing ); + setMargin( margin ); +} + +/*! + \param spacing Spacing +*/ + +QwtDynGridLayout::QwtDynGridLayout( int spacing ) +{ + init(); + setSpacing( spacing ); +} + +/*! + Initialize the layout with default values. +*/ +void QwtDynGridLayout::init() +{ + d_data = new QwtDynGridLayout::PrivateData; + d_data->maxCols = d_data->numRows = d_data->numCols = 0; + d_data->expanding = 0; +} + +//! Destructor + +QwtDynGridLayout::~QwtDynGridLayout() +{ + for ( int i = 0; i < d_data->itemList.size(); i++ ) + delete d_data->itemList[i]; + + delete d_data; +} + +//! Invalidate all internal caches +void QwtDynGridLayout::invalidate() +{ + d_data->isDirty = true; + QLayout::invalidate(); +} + +/*! + Limit the number of columns. + \param maxCols upper limit, 0 means unlimited + \sa maxCols() +*/ +void QwtDynGridLayout::setMaxCols( uint maxCols ) +{ + d_data->maxCols = maxCols; +} + +/*! + Return the upper limit for the number of columns. + 0 means unlimited, what is the default. + \sa setMaxCols() +*/ + +uint QwtDynGridLayout::maxCols() const +{ + return d_data->maxCols; +} + +//! Adds item to the next free position. + +void QwtDynGridLayout::addItem( QLayoutItem *item ) +{ + d_data->itemList.append( item ); + invalidate(); +} + +/*! + \return true if this layout is empty. +*/ + +bool QwtDynGridLayout::isEmpty() const +{ + return d_data->itemList.isEmpty(); +} + +/*! + \return number of layout items +*/ + +uint QwtDynGridLayout::itemCount() const +{ + return d_data->itemList.count(); +} + +/*! + Find the item at a spcific index + + \param index Index + \sa takeAt() +*/ +QLayoutItem *QwtDynGridLayout::itemAt( int index ) const +{ + if ( index < 0 || index >= d_data->itemList.count() ) + return NULL; + + return d_data->itemList.at( index ); +} + +/*! + Find the item at a spcific index and remove it from the layout + + \param index Index + \sa itemAt() +*/ +QLayoutItem *QwtDynGridLayout::takeAt( int index ) +{ + if ( index < 0 || index >= d_data->itemList.count() ) + return NULL; + + d_data->isDirty = true; + return d_data->itemList.takeAt( index ); +} + +//! \return Number of items in the layout +int QwtDynGridLayout::count() const +{ + return d_data->itemList.count(); +} + +/*! + Set whether this layout can make use of more space than sizeHint(). + A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only + one dimension, while Qt::Vertical | Qt::Horizontal means that it wants + to grow in both dimensions. The default value is 0. + + \param expanding Or'd orientations + \sa expandingDirections() +*/ +void QwtDynGridLayout::setExpandingDirections( Qt::Orientations expanding ) +{ + d_data->expanding = expanding; +} + +/*! + Returns whether this layout can make use of more space than sizeHint(). + A value of Qt::Vertical or Qt::Horizontal means that it wants to grow in only + one dimension, while Qt::Vertical | Qt::Horizontal means that it wants + to grow in both dimensions. + \sa setExpandingDirections() +*/ +Qt::Orientations QwtDynGridLayout::expandingDirections() const +{ + return d_data->expanding; +} + +/*! + Reorganizes columns and rows and resizes managed items within + the rectangle rect. + + \param rect Layout geometry +*/ +void QwtDynGridLayout::setGeometry( const QRect &rect ) +{ + QLayout::setGeometry( rect ); + + if ( isEmpty() ) + return; + + d_data->numCols = columnsForWidth( rect.width() ); + d_data->numRows = itemCount() / d_data->numCols; + if ( itemCount() % d_data->numCols ) + d_data->numRows++; + + QList<QRect> itemGeometries = layoutItems( rect, d_data->numCols ); + + int index = 0; + for ( QList<QLayoutItem*>::iterator it = d_data->itemList.begin(); + it != d_data->itemList.end(); ++it ) + { + ( *it )->setGeometry( itemGeometries[index] ); + index++; + } +} + +/*! + Calculate the number of columns for a given width. It tries to + use as many columns as possible (limited by maxCols()) + + \param width Available width for all columns + \sa maxCols(), setMaxCols() +*/ + +uint QwtDynGridLayout::columnsForWidth( int width ) const +{ + if ( isEmpty() ) + return 0; + + uint maxCols = itemCount(); + if ( d_data->maxCols > 0 ) + maxCols = qMin( d_data->maxCols, maxCols ); + + if ( maxRowWidth( maxCols ) <= width ) + return maxCols; + + for ( uint numCols = 2; numCols <= maxCols; numCols++ ) + { + const int rowWidth = maxRowWidth( numCols ); + if ( rowWidth > width ) + return numCols - 1; + } + + return 1; // At least 1 column +} + +/*! + Calculate the width of a layout for a given number of + columns. + + \param numCols Given number of columns + \param itemWidth Array of the width hints for all items +*/ +int QwtDynGridLayout::maxRowWidth( int numCols ) const +{ + int col; + + QVector<int> colWidth( numCols ); + for ( col = 0; col < numCols; col++ ) + colWidth[col] = 0; + + if ( d_data->isDirty ) + d_data->updateLayoutCache(); + + for ( int index = 0; + index < d_data->itemSizeHints.count(); index++ ) + { + col = index % numCols; + colWidth[col] = qMax( colWidth[col], + d_data->itemSizeHints[int( index )].width() ); + } + + int rowWidth = 2 * margin() + ( numCols - 1 ) * spacing(); + for ( col = 0; col < numCols; col++ ) + rowWidth += colWidth[col]; + + return rowWidth; +} + +/*! + \return the maximum width of all layout items +*/ +int QwtDynGridLayout::maxItemWidth() const +{ + if ( isEmpty() ) + return 0; + + if ( d_data->isDirty ) + d_data->updateLayoutCache(); + + int w = 0; + for ( int i = 0; i < d_data->itemSizeHints.count(); i++ ) + { + const int itemW = d_data->itemSizeHints[i].width(); + if ( itemW > w ) + w = itemW; + } + + return w; +} + +/*! + Calculate the geometries of the layout items for a layout + with numCols columns and a given rect. + + \param rect Rect where to place the items + \param numCols Number of columns + \return item geometries +*/ + +QList<QRect> QwtDynGridLayout::layoutItems( const QRect &rect, + uint numCols ) const +{ + QList<QRect> itemGeometries; + if ( numCols == 0 || isEmpty() ) + return itemGeometries; + + uint numRows = itemCount() / numCols; + if ( numCols % itemCount() ) + numRows++; + + if ( numRows == 0 ) + return itemGeometries; + + QVector<int> rowHeight( numRows ); + QVector<int> colWidth( numCols ); + + layoutGrid( numCols, rowHeight, colWidth ); + + bool expandH, expandV; + expandH = expandingDirections() & Qt::Horizontal; + expandV = expandingDirections() & Qt::Vertical; + + if ( expandH || expandV ) + stretchGrid( rect, numCols, rowHeight, colWidth ); + + const int maxCols = d_data->maxCols; + d_data->maxCols = numCols; + const QRect alignedRect = alignmentRect( rect ); + d_data->maxCols = maxCols; + + const int xOffset = expandH ? 0 : alignedRect.x(); + const int yOffset = expandV ? 0 : alignedRect.y(); + + QVector<int> colX( numCols ); + QVector<int> rowY( numRows ); + + const int xySpace = spacing(); + + rowY[0] = yOffset + margin(); + for ( uint r = 1; r < numRows; r++ ) + rowY[r] = rowY[r-1] + rowHeight[r-1] + xySpace; + + colX[0] = xOffset + margin(); + for ( uint c = 1; c < numCols; c++ ) + colX[c] = colX[c-1] + colWidth[c-1] + xySpace; + + const int itemCount = d_data->itemList.size(); + for ( int i = 0; i < itemCount; i++ ) + { + const int row = i / numCols; + const int col = i % numCols; + + QRect itemGeometry( colX[col], rowY[row], + colWidth[col], rowHeight[row] ); + itemGeometries.append( itemGeometry ); + } + + return itemGeometries; +} + + +/*! + Calculate the dimensions for the columns and rows for a grid + of numCols columns. + + \param numCols Number of columns. + \param rowHeight Array where to fill in the calculated row heights. + \param colWidth Array where to fill in the calculated column widths. +*/ + +void QwtDynGridLayout::layoutGrid( uint numCols, + QVector<int>& rowHeight, QVector<int>& colWidth ) const +{ + if ( numCols <= 0 ) + return; + + if ( d_data->isDirty ) + d_data->updateLayoutCache(); + + for ( int index = 0; index < d_data->itemSizeHints.count(); index++ ) + { + const int row = index / numCols; + const int col = index % numCols; + + const QSize &size = d_data->itemSizeHints[int( index )]; + + rowHeight[row] = ( col == 0 ) + ? size.height() : qMax( rowHeight[row], size.height() ); + colWidth[col] = ( row == 0 ) + ? size.width() : qMax( colWidth[col], size.width() ); + } +} + +/*! + \return true: QwtDynGridLayout implements heightForWidth. + \sa heightForWidth() +*/ +bool QwtDynGridLayout::hasHeightForWidth() const +{ + return true; +} + +/*! + \return The preferred height for this layout, given the width w. + \sa hasHeightForWidth() +*/ +int QwtDynGridLayout::heightForWidth( int width ) const +{ + if ( isEmpty() ) + return 0; + + const uint numCols = columnsForWidth( width ); + uint numRows = itemCount() / numCols; + if ( itemCount() % numCols ) + numRows++; + + QVector<int> rowHeight( numRows ); + QVector<int> colWidth( numCols ); + + layoutGrid( numCols, rowHeight, colWidth ); + + int h = 2 * margin() + ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) + h += rowHeight[row]; + + return h; +} + +/*! + Stretch columns in case of expanding() & QSizePolicy::Horizontal and + rows in case of expanding() & QSizePolicy::Vertical to fill the entire + rect. Rows and columns are stretched with the same factor. + + \sa setExpanding(), expanding() +*/ +void QwtDynGridLayout::stretchGrid( const QRect &rect, + uint numCols, QVector<int>& rowHeight, QVector<int>& colWidth ) const +{ + if ( numCols == 0 || isEmpty() ) + return; + + bool expandH, expandV; + expandH = expandingDirections() & Qt::Horizontal; + expandV = expandingDirections() & Qt::Vertical; + + if ( expandH ) + { + int xDelta = rect.width() - 2 * margin() - ( numCols - 1 ) * spacing(); + for ( uint col = 0; col < numCols; col++ ) + xDelta -= colWidth[col]; + + if ( xDelta > 0 ) + { + for ( uint col = 0; col < numCols; col++ ) + { + const int space = xDelta / ( numCols - col ); + colWidth[col] += space; + xDelta -= space; + } + } + } + + if ( expandV ) + { + uint numRows = itemCount() / numCols; + if ( itemCount() % numCols ) + numRows++; + + int yDelta = rect.height() - 2 * margin() - ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) + yDelta -= rowHeight[row]; + + if ( yDelta > 0 ) + { + for ( uint row = 0; row < numRows; row++ ) + { + const int space = yDelta / ( numRows - row ); + rowHeight[row] += space; + yDelta -= space; + } + } + } +} + +/*! + Return the size hint. If maxCols() > 0 it is the size for + a grid with maxCols() columns, otherwise it is the size for + a grid with only one row. + + \sa maxCols(), setMaxCols() +*/ +QSize QwtDynGridLayout::sizeHint() const +{ + if ( isEmpty() ) + return QSize(); + + uint numCols = itemCount(); + if ( d_data->maxCols > 0 ) + numCols = qMin( d_data->maxCols, numCols ); + + uint numRows = itemCount() / numCols; + if ( itemCount() % numCols ) + numRows++; + + QVector<int> rowHeight( numRows ); + QVector<int> colWidth( numCols ); + + layoutGrid( numCols, rowHeight, colWidth ); + + int h = 2 * margin() + ( numRows - 1 ) * spacing(); + for ( uint row = 0; row < numRows; row++ ) + h += rowHeight[row]; + + int w = 2 * margin() + ( numCols - 1 ) * spacing(); + for ( uint col = 0; col < numCols; col++ ) + w += colWidth[col]; + + return QSize( w, h ); +} + +/*! + \return Number of rows of the current layout. + \sa numCols() + \warning The number of rows might change whenever the geometry changes +*/ +uint QwtDynGridLayout::numRows() const +{ + return d_data->numRows; +} + +/*! + \return Number of columns of the current layout. + \sa numRows() + \warning The number of columns might change whenever the geometry changes +*/ +uint QwtDynGridLayout::numCols() const +{ + return d_data->numCols; +} diff --git a/src/libpcp_qwt/src/qwt_dyngrid_layout.h b/src/libpcp_qwt/src/qwt_dyngrid_layout.h new file mode 100644 index 0000000..0b835f0 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_dyngrid_layout.h @@ -0,0 +1,83 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_DYNGRID_LAYOUT_H +#define QWT_DYNGRID_LAYOUT_H + +#include "qwt_global.h" +#include <qlayout.h> +#include <qsize.h> +#include <qlist.h> + +/*! + \brief The QwtDynGridLayout class lays out widgets in a grid, + adjusting the number of columns and rows to the current size. + + QwtDynGridLayout takes the space it gets, divides it up into rows and + columns, and puts each of the widgets it manages into the correct cell(s). + It lays out as many number of columns as possible (limited by maxCols()). +*/ + +class QWT_EXPORT QwtDynGridLayout : public QLayout +{ + Q_OBJECT +public: + explicit QwtDynGridLayout( QWidget *, int margin = 0, int space = -1 ); + explicit QwtDynGridLayout( int space = -1 ); + + virtual ~QwtDynGridLayout(); + + virtual void invalidate(); + + void setMaxCols( uint maxCols ); + uint maxCols() const; + + uint numRows () const; + uint numCols () const; + + virtual void addItem( QLayoutItem * ); + + virtual QLayoutItem *itemAt( int index ) const; + virtual QLayoutItem *takeAt( int index ); + virtual int count() const; + + void setExpandingDirections( Qt::Orientations ); + virtual Qt::Orientations expandingDirections() const; + QList<QRect> layoutItems( const QRect &, uint numCols ) const; + + virtual int maxItemWidth() const; + + virtual void setGeometry( const QRect &rect ); + + virtual bool hasHeightForWidth() const; + virtual int heightForWidth( int ) const; + + virtual QSize sizeHint() const; + + virtual bool isEmpty() const; + uint itemCount() const; + + virtual uint columnsForWidth( int width ) const; + +protected: + + void layoutGrid( uint numCols, + QVector<int>& rowHeight, QVector<int>& colWidth ) const; + void stretchGrid( const QRect &rect, uint numCols, + QVector<int>& rowHeight, QVector<int>& colWidth ) const; + +private: + void init(); + int maxRowWidth( int numCols ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_event_pattern.cpp b/src/libpcp_qwt/src/qwt_event_pattern.cpp new file mode 100644 index 0000000..24bcbbc --- /dev/null +++ b/src/libpcp_qwt/src/qwt_event_pattern.cpp @@ -0,0 +1,274 @@ +/* -*- 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_event_pattern.h" +#include <qevent.h> + +/*! + Constructor + + \sa MousePatternCode, KeyPatternCode +*/ + +QwtEventPattern::QwtEventPattern(): + d_mousePattern( MousePatternCount ), + d_keyPattern( KeyPatternCount ) +{ + initKeyPattern(); + initMousePattern( 3 ); +} + +//! Destructor +QwtEventPattern::~QwtEventPattern() +{ +} + +/*! + Set default mouse patterns, depending on the number of mouse buttons + + \param numButtons Number of mouse buttons ( <= 3 ) + \sa MousePatternCode +*/ +void QwtEventPattern::initMousePattern( int numButtons ) +{ + const int altButton = Qt::AltModifier; + const int controlButton = Qt::ControlModifier; + const int shiftButton = Qt::ShiftModifier; + + d_mousePattern.resize( MousePatternCount ); + + switch ( numButtons ) + { + case 1: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::LeftButton, controlButton ); + setMousePattern( MouseSelect3, Qt::LeftButton, altButton ); + break; + } + case 2: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::RightButton ); + setMousePattern( MouseSelect3, Qt::LeftButton, altButton ); + break; + } + default: + { + setMousePattern( MouseSelect1, Qt::LeftButton ); + setMousePattern( MouseSelect2, Qt::RightButton ); + setMousePattern( MouseSelect3, Qt::MidButton ); + } + } + for ( int i = 0; i < 3; i++ ) + { + setMousePattern( MouseSelect4 + i, + d_mousePattern[MouseSelect1 + i].button, + d_mousePattern[MouseSelect1 + i].state | shiftButton ); + } +} + +/*! + Set default mouse patterns. + + \sa KeyPatternCode +*/ +void QwtEventPattern::initKeyPattern() +{ + d_keyPattern.resize( KeyPatternCount ); + + setKeyPattern( KeySelect1, Qt::Key_Return ); + setKeyPattern( KeySelect2, Qt::Key_Space ); + setKeyPattern( KeyAbort, Qt::Key_Escape ); + + setKeyPattern( KeyLeft, Qt::Key_Left ); + setKeyPattern( KeyRight, Qt::Key_Right ); + setKeyPattern( KeyUp, Qt::Key_Up ); + setKeyPattern( KeyDown, Qt::Key_Down ); + + setKeyPattern( KeyRedo, Qt::Key_Plus ); + setKeyPattern( KeyUndo, Qt::Key_Minus ); + setKeyPattern( KeyHome, Qt::Key_Escape ); +} + +/*! + Change one mouse pattern + + \param pattern Index of the pattern + \param button Button + \param state State + + \sa QMouseEvent +*/ +void QwtEventPattern::setMousePattern( uint pattern, int button, int state ) +{ + if ( pattern < ( uint )d_mousePattern.count() ) + { + d_mousePattern[int( pattern )].button = button; + d_mousePattern[int( pattern )].state = state; + } +} + +/*! + Change one key pattern + + \param pattern Index of the pattern + \param key Key + \param state State + + \sa QKeyEvent +*/ +void QwtEventPattern::setKeyPattern( uint pattern, int key, int state ) +{ + if ( pattern < ( uint )d_keyPattern.count() ) + { + d_keyPattern[int( pattern )].key = key; + d_keyPattern[int( pattern )].state = state; + } +} + +//! Change the mouse event patterns +void QwtEventPattern::setMousePattern( const QVector<MousePattern> &pattern ) +{ + d_mousePattern = pattern; +} + +//! Change the key event patterns +void QwtEventPattern::setKeyPattern( const QVector<KeyPattern> &pattern ) +{ + d_keyPattern = pattern; +} + +//! Return mouse patterns +const QVector<QwtEventPattern::MousePattern> & +QwtEventPattern::mousePattern() const +{ + return d_mousePattern; +} + +//! Return key patterns +const QVector<QwtEventPattern::KeyPattern> & +QwtEventPattern::keyPattern() const +{ + return d_keyPattern; +} + +//! Return ,ouse patterns +QVector<QwtEventPattern::MousePattern> &QwtEventPattern::mousePattern() +{ + return d_mousePattern; +} + +//! Return Key patterns +QVector<QwtEventPattern::KeyPattern> &QwtEventPattern::keyPattern() +{ + return d_keyPattern; +} + +/*! + \brief Compare a mouse event with an event pattern. + + A mouse event matches the pattern when both have the same button + value and in the state value the same key flags(Qt::KeyButtonMask) + are set. + + \param pattern Index of the event pattern + \param event Mouse event + \return true if matches + + \sa keyMatch() +*/ +bool QwtEventPattern::mouseMatch( uint pattern, + const QMouseEvent *event ) const +{ + bool ok = false; + + if ( event && pattern < ( uint )d_mousePattern.count() ) + ok = mouseMatch( d_mousePattern[int( pattern )], event ); + + return ok; +} + +/*! + \brief Compare a mouse event with an event pattern. + + A mouse event matches the pattern when both have the same button + value and in the state value the same key flags(Qt::KeyButtonMask) + are set. + + \param pattern Mouse event pattern + \param event Mouse event + \return true if matches + + \sa keyMatch() +*/ + +bool QwtEventPattern::mouseMatch( const MousePattern &pattern, + const QMouseEvent *event ) const +{ + if ( event->button() != pattern.button ) + return false; + + const bool matched = + ( event->modifiers() & Qt::KeyboardModifierMask ) == + ( int )( pattern.state & Qt::KeyboardModifierMask ); + + return matched; +} + +/*! + \brief Compare a key event with an event pattern. + + A key event matches the pattern when both have the same key + value and in the state value the same key flags (Qt::KeyButtonMask) + are set. + + \param pattern Index of the event pattern + \param event Key event + \return true if matches + + \sa mouseMatch() +*/ +bool QwtEventPattern::keyMatch( uint pattern, + const QKeyEvent *event ) const +{ + bool ok = false; + + if ( event && pattern < ( uint )d_keyPattern.count() ) + ok = keyMatch( d_keyPattern[int( pattern )], event ); + + return ok; +} + +/*! + \brief Compare a key event with an event pattern. + + A key event matches the pattern when both have the same key + value and in the state value the same key flags (Qt::KeyButtonMask) + are set. + + \param pattern Key event pattern + \param event Key event + \return true if matches + + \sa mouseMatch() +*/ + +bool QwtEventPattern::keyMatch( + const KeyPattern &pattern, const QKeyEvent *event ) const +{ + if ( event->key() != pattern.key ) + return false; + + const bool matched = + ( event->modifiers() & Qt::KeyboardModifierMask ) == + ( int )( pattern.state & Qt::KeyboardModifierMask ); + + return matched; +} diff --git a/src/libpcp_qwt/src/qwt_event_pattern.h b/src/libpcp_qwt/src/qwt_event_pattern.h new file mode 100644 index 0000000..a88f5f4 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_event_pattern.h @@ -0,0 +1,225 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_EVENT_PATTERN +#define QWT_EVENT_PATTERN 1 + +#include "qwt_global.h" +#include <qnamespace.h> +#include <qvector.h> + +class QMouseEvent; +class QKeyEvent; + +/*! + \brief A collection of event patterns + + QwtEventPattern introduces an level of indirection for mouse and + keyboard inputs. Those are represented by symbolic names, so + the application code can be configured by individual mappings. + + \sa QwtPicker, QwtPickerMachine, QwtPlotZoomer +*/ +class QWT_EXPORT QwtEventPattern +{ +public: + /*! + \brief Symbolic mouse input codes + + The default initialization for 3 button mice is: + - MouseSelect1\n + Qt::LeftButton + - MouseSelect2\n + Qt::RightButton + - MouseSelect3\n + Qt::MidButton + - MouseSelect4\n + Qt::LeftButton + Qt::ShiftButton + - MouseSelect5\n + Qt::RightButton + Qt::ShiftButton + - MouseSelect6\n + Qt::MidButton + Qt::ShiftButton + + The default initialization for 2 button mice is: + - MouseSelect1\n + Qt::LeftButton + - MouseSelect2\n + Qt::RightButton + - MouseSelect3\n + Qt::LeftButton + Qt::AltButton + - MouseSelect4\n + Qt::LeftButton + Qt::ShiftButton + - MouseSelect5\n + Qt::RightButton + Qt::ShiftButton + - MouseSelect6\n + Qt::LeftButton + Qt::AltButton + Qt::ShiftButton + + The default initialization for 1 button mice is: + - MouseSelect1\n + Qt::LeftButton + - MouseSelect2\n + Qt::LeftButton + Qt::ControlButton + - MouseSelect3\n + Qt::LeftButton + Qt::AltButton + - MouseSelect4\n + Qt::LeftButton + Qt::ShiftButton + - MouseSelect5\n + Qt::LeftButton + Qt::ControlButton + Qt::ShiftButton + - MouseSelect6\n + Qt::LeftButton + Qt::AltButton + Qt::ShiftButton + + \sa initMousePattern() + */ + + enum MousePatternCode + { + MouseSelect1, + MouseSelect2, + MouseSelect3, + MouseSelect4, + MouseSelect5, + MouseSelect6, + + MousePatternCount + }; + + /*! + \brief Symbolic keyboard input codes + + Default initialization: + - KeySelect1\n + Qt::Key_Return + - KeySelect2\n + Qt::Key_Space + - KeyAbort\n + Qt::Key_Escape + + - KeyLeft\n + Qt::Key_Left + - KeyRight\n + Qt::Key_Right + - KeyUp\n + Qt::Key_Up + - KeyDown\n + Qt::Key_Down + + - KeyUndo\n + Qt::Key_Minus + - KeyRedo\n + Qt::Key_Plus + - KeyHome\n + Qt::Key_Escape + */ + enum KeyPatternCode + { + KeySelect1, + KeySelect2, + KeyAbort, + + KeyLeft, + KeyRight, + KeyUp, + KeyDown, + + KeyRedo, + KeyUndo, + KeyHome, + + KeyPatternCount + }; + + //! A pattern for mouse events + class MousePattern + { + public: + //! Constructor + MousePattern( int btn = Qt::NoButton, int st = Qt::NoButton ) + { + button = btn; + state = st; + } + + //! Button code + int button; + + //! State + int state; + }; + + //! A pattern for key events + class KeyPattern + { + public: + //! Constructor + KeyPattern( int k = 0, int st = Qt::NoButton ) + { + key = k; + state = st; + } + + //! Key code + int key; + + //! State + int state; + }; + + QwtEventPattern(); + virtual ~QwtEventPattern(); + + void initMousePattern( int numButtons ); + void initKeyPattern(); + + void setMousePattern( uint pattern, int button, int state = Qt::NoButton ); + void setKeyPattern( uint pattern, int key, int state = Qt::NoButton ); + + void setMousePattern( const QVector<MousePattern> & ); + void setKeyPattern( const QVector<KeyPattern> & ); + + const QVector<MousePattern> &mousePattern() const; + const QVector<KeyPattern> &keyPattern() const; + + QVector<MousePattern> &mousePattern(); + QVector<KeyPattern> &keyPattern(); + + bool mouseMatch( uint pattern, const QMouseEvent * ) const; + bool keyMatch( uint pattern, const QKeyEvent * ) const; + +protected: + virtual bool mouseMatch( const MousePattern &, const QMouseEvent * ) const; + virtual bool keyMatch( const KeyPattern &, const QKeyEvent * ) const; + +private: + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4251) +#endif + QVector<MousePattern> d_mousePattern; + QVector<KeyPattern> d_keyPattern; +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +}; + +//! Compare operator +inline bool operator==( QwtEventPattern::MousePattern b1, + QwtEventPattern::MousePattern b2 ) +{ + return b1.button == b2.button && b1.state == b2.state; +} + +//! Compare operator +inline bool operator==( QwtEventPattern::KeyPattern b1, + QwtEventPattern::KeyPattern b2 ) +{ + return b1.key == b2.key && b1.state == b2.state; +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_global.h b/src/libpcp_qwt/src/qwt_global.h new file mode 100644 index 0000000..6b3f830 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_global.h @@ -0,0 +1,41 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_GLOBAL_H +#define QWT_GLOBAL_H + +#include <qglobal.h> + +// QWT_VERSION is (major << 16) + (minor << 8) + patch. + +#define QWT_VERSION 0x060002 +#define QWT_VERSION_STR "6.0.2" + +#if defined(_MSC_VER) /* MSVC Compiler */ +/* template-class specialization 'identifier' is already instantiated */ +#pragma warning(disable: 4660) +#endif // _MSC_VER + +#ifdef QWT_DLL + +#if defined(QWT_MAKEDLL) // create a Qwt DLL library +#define QWT_EXPORT Q_DECL_EXPORT +#else // use a Qwt DLL library +#define QWT_EXPORT Q_DECL_IMPORT +#endif + +#endif // QWT_DLL + +#ifndef QWT_EXPORT +#define QWT_EXPORT +#endif + +// #define QWT_NO_COMPAT 1 // disable withdrawn functionality + +#endif diff --git a/src/libpcp_qwt/src/qwt_interval.cpp b/src/libpcp_qwt/src/qwt_interval.cpp new file mode 100644 index 0000000..f835567 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_interval.cpp @@ -0,0 +1,334 @@ +/* -*- 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_interval.h" +#include "qwt_math.h" +#include <qalgorithms.h> + +/*! + \brief Normalize the limits of the interval + + If maxValue() < minValue() the limits will be inverted. + \return Normalized interval + + \sa isValid(), inverted() +*/ +QwtInterval QwtInterval::normalized() const +{ + if ( d_minValue > d_maxValue ) + { + return inverted(); + } + if ( d_minValue == d_maxValue && d_borderFlags == ExcludeMinimum ) + { + return inverted(); + } + + return *this; +} + +/*! + Invert the limits of the interval + \return Inverted interval + \sa normalized() +*/ +QwtInterval QwtInterval::inverted() const +{ + BorderFlags borderFlags = IncludeBorders; + if ( d_borderFlags & ExcludeMinimum ) + borderFlags |= ExcludeMaximum; + if ( d_borderFlags & ExcludeMaximum ) + borderFlags |= ExcludeMinimum; + + return QwtInterval( d_maxValue, d_minValue, borderFlags ); +} + +/*! + Test if a value is inside an interval + + \param value Value + \return true, if value >= minValue() && value <= maxValue() +*/ +bool QwtInterval::contains( double value ) const +{ + if ( !isValid() ) + return false; + + if ( value < d_minValue || value > d_maxValue ) + return false; + + if ( value == d_minValue && d_borderFlags & ExcludeMinimum ) + return false; + + if ( value == d_maxValue && d_borderFlags & ExcludeMaximum ) + return false; + + return true; +} + +//! Unite 2 intervals +QwtInterval QwtInterval::unite( const QwtInterval &other ) const +{ + /* + If one of the intervals is invalid return the other one. + If both are invalid return an invalid default interval + */ + if ( !isValid() ) + { + if ( !other.isValid() ) + return QwtInterval(); + else + return other; + } + if ( !other.isValid() ) + return *this; + + QwtInterval united; + BorderFlags flags = IncludeBorders; + + // minimum + if ( d_minValue < other.minValue() ) + { + united.setMinValue( d_minValue ); + flags &= d_borderFlags & ExcludeMinimum; + } + else if ( other.minValue() < d_minValue ) + { + united.setMinValue( other.minValue() ); + flags &= other.borderFlags() & ExcludeMinimum; + } + else // d_minValue == other.minValue() + { + united.setMinValue( d_minValue ); + flags &= ( d_borderFlags & other.borderFlags() ) & ExcludeMinimum; + } + + // maximum + if ( d_maxValue > other.maxValue() ) + { + united.setMaxValue( d_maxValue ); + flags &= d_borderFlags & ExcludeMaximum; + } + else if ( other.maxValue() > d_maxValue ) + { + united.setMaxValue( other.maxValue() ); + flags &= other.borderFlags() & ExcludeMaximum; + } + else // d_maxValue == other.maxValue() ) + { + united.setMaxValue( d_maxValue ); + flags &= d_borderFlags & other.borderFlags() & ExcludeMaximum; + } + + united.setBorderFlags( flags ); + return united; +} + +//! Intersect 2 intervals +QwtInterval QwtInterval::intersect( const QwtInterval &other ) const +{ + if ( !other.isValid() || !isValid() ) + return QwtInterval(); + + QwtInterval i1 = *this; + QwtInterval i2 = other; + + // swap i1/i2, so that the minimum of i1 + // is smaller then the minimum of i2 + + if ( i1.minValue() > i2.minValue() ) + { + qSwap( i1, i2 ); + } + else if ( i1.minValue() == i2.minValue() ) + { + if ( i1.borderFlags() & ExcludeMinimum ) + qSwap( i1, i2 ); + } + + if ( i1.maxValue() < i2.minValue() ) + { + return QwtInterval(); + } + + if ( i1.maxValue() == i2.minValue() ) + { + if ( i1.borderFlags() & ExcludeMaximum || + i2.borderFlags() & ExcludeMinimum ) + { + return QwtInterval(); + } + } + + QwtInterval intersected; + BorderFlags flags = IncludeBorders; + + intersected.setMinValue( i2.minValue() ); + flags |= i2.borderFlags() & ExcludeMinimum; + + if ( i1.maxValue() < i2.maxValue() ) + { + intersected.setMaxValue( i1.maxValue() ); + flags |= i1.borderFlags() & ExcludeMaximum; + } + else if ( i2.maxValue() < i1.maxValue() ) + { + intersected.setMaxValue( i2.maxValue() ); + flags |= i2.borderFlags() & ExcludeMaximum; + } + else // i1.maxValue() == i2.maxValue() + { + intersected.setMaxValue( i1.maxValue() ); + flags |= i1.borderFlags() & i2.borderFlags() & ExcludeMaximum; + } + + intersected.setBorderFlags( flags ); + return intersected; +} + +//! Unites this interval with the given interval. +QwtInterval& QwtInterval::operator|=( const QwtInterval & interval ) +{ + *this = *this | interval; + return *this; +} + +//! Intersects this interval with the given interval. +QwtInterval& QwtInterval::operator&=( const QwtInterval & interval ) +{ + *this = *this & interval; + return *this; +} + +/*! + Test if two intervals overlap +*/ +bool QwtInterval::intersects( const QwtInterval &other ) const +{ + if ( !isValid() || !other.isValid() ) + return false; + + QwtInterval i1 = *this; + QwtInterval i2 = other; + + // swap i1/i2, so that the minimum of i1 + // is smaller then the minimum of i2 + + if ( i1.minValue() > i2.minValue() ) + { + qSwap( i1, i2 ); + } + else if ( i1.minValue() == i2.minValue() && + i1.borderFlags() & ExcludeMinimum ) + { + qSwap( i1, i2 ); + } + + if ( i1.maxValue() > i2.minValue() ) + { + return true; + } + if ( i1.maxValue() == i2.minValue() ) + { + return !( ( i1.borderFlags() & ExcludeMaximum ) || + ( i2.borderFlags() & ExcludeMinimum ) ); + } + return false; +} + +/*! + Adjust the limit that is closer to value, so that value becomes + the center of the interval. + + \param value Center + \return Interval with value as center +*/ +QwtInterval QwtInterval::symmetrize( double value ) const +{ + if ( !isValid() ) + return *this; + + const double delta = + qMax( qAbs( value - d_maxValue ), qAbs( value - d_minValue ) ); + + return QwtInterval( value - delta, value + delta ); +} + +/*! + Limit the interval, keeping the border modes + + \param lowerBound Lower limit + \param upperBound Upper limit + + \return Limited interval +*/ +QwtInterval QwtInterval::limited( double lowerBound, double upperBound ) const +{ + if ( !isValid() || lowerBound > upperBound ) + return QwtInterval(); + + double minValue = qMax( d_minValue, lowerBound ); + minValue = qMin( minValue, upperBound ); + + double maxValue = qMax( d_maxValue, lowerBound ); + maxValue = qMin( maxValue, upperBound ); + + return QwtInterval( minValue, maxValue, d_borderFlags ); +} + +/*! + Extend the interval + + If value is below minValue, value becomes the lower limit. + If value is above maxValue, value becomes the upper limit. + + extend has no effect for invalid intervals + + \param value Value + \sa isValid() +*/ +QwtInterval QwtInterval::extend( double value ) const +{ + if ( !isValid() ) + return *this; + + return QwtInterval( qMin( value, d_minValue ), + qMax( value, d_maxValue ), d_borderFlags ); +} + +/*! + Extend an interval + + \param value Value + \return Reference of the extended interval + + \sa extend() +*/ +QwtInterval& QwtInterval::operator|=( double value ) +{ + *this = *this | value; + return *this; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<( QDebug debug, const QwtInterval &interval ) +{ + const int flags = interval.borderFlags(); + + debug.nospace() << "QwtInterval(" + << ( ( flags & QwtInterval::ExcludeMinimum ) ? "]" : "[" ) + << interval.minValue() << "," << interval.maxValue() + << ( ( flags & QwtInterval::ExcludeMaximum ) ? "[" : "]" ) + << ")"; + + return debug.space(); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_interval.h b/src/libpcp_qwt/src/qwt_interval.h new file mode 100644 index 0000000..6e30920 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_interval.h @@ -0,0 +1,296 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_INTERVAL_H +#define QWT_INTERVAL_H + +#include "qwt_global.h" +#ifndef QT_NO_DEBUG_STREAM +#include <qdebug.h> +#endif + +/*! + \brief A class representing an interval + + The interval is represented by 2 doubles, the lower and the upper limit. +*/ + +class QWT_EXPORT QwtInterval +{ +public: + /*! + Flag indicating if a border is included or excluded + \sa setBorderFlags(), borderFlags() + */ + enum BorderFlag + { + //! Min/Max values are inside the interval + IncludeBorders = 0x00, + + //! Min value is not included in the interval + ExcludeMinimum = 0x01, + + //! Max value is not included in the interval + ExcludeMaximum = 0x02, + + //! Min/Max values are not included in the interval + ExcludeBorders = ExcludeMinimum | ExcludeMaximum + }; + + //! Border flags + typedef QFlags<BorderFlag> BorderFlags; + + QwtInterval(); + QwtInterval( double minValue, double maxValue, + BorderFlags = IncludeBorders ); + + void setInterval( double minValue, double maxValue, + BorderFlags = IncludeBorders ); + + QwtInterval normalized() const; + QwtInterval inverted() const; + QwtInterval limited( double minValue, double maxValue ) const; + + bool operator==( const QwtInterval & ) const; + bool operator!=( const QwtInterval & ) const; + + void setBorderFlags( BorderFlags ); + BorderFlags borderFlags() const; + + double minValue() const; + double maxValue() const; + + double width() const; + + void setMinValue( double ); + void setMaxValue( double ); + + bool contains( double value ) const; + + bool intersects( const QwtInterval & ) const; + QwtInterval intersect( const QwtInterval & ) const; + QwtInterval unite( const QwtInterval & ) const; + + QwtInterval operator|( const QwtInterval & ) const; + QwtInterval operator&( const QwtInterval & ) const; + + QwtInterval &operator|=( const QwtInterval & ); + QwtInterval &operator&=( const QwtInterval & ); + + QwtInterval extend( double value ) const; + QwtInterval operator|( double ) const; + QwtInterval &operator|=( double ); + + bool isValid() const; + bool isNull() const; + void invalidate(); + + QwtInterval symmetrize( double value ) const; + +private: + double d_minValue; + double d_maxValue; + BorderFlags d_borderFlags; +}; + +Q_DECLARE_TYPEINFO(QwtInterval, Q_MOVABLE_TYPE); + +/*! + \brief Default Constructor + + Creates an invalid interval [0.0, -1.0] + \sa setInterval(), isValid() +*/ +inline QwtInterval::QwtInterval(): + d_minValue( 0.0 ), + d_maxValue( -1.0 ), + d_borderFlags( IncludeBorders ) +{ +} + +/*! + Constructor + + Build an interval with from min/max values + + \param minValue Minimum value + \param maxValue Maximum value + \param borderFlags Include/Exclude borders +*/ +inline QwtInterval::QwtInterval( + double minValue, double maxValue, BorderFlags borderFlags ): + d_minValue( minValue ), + d_maxValue( maxValue ), + d_borderFlags( borderFlags ) +{ +} + +/*! + Assign the limits of the interval + + \param minValue Minimum value + \param maxValue Maximum value + \param borderFlags Include/Exclude borders +*/ +inline void QwtInterval::setInterval( + double minValue, double maxValue, BorderFlags borderFlags ) +{ + d_minValue = minValue; + d_maxValue = maxValue; + d_borderFlags = borderFlags; +} + +/*! + Change the border flags + + \param borderFlags Or'd BorderMode flags + \sa borderFlags() +*/ +inline void QwtInterval::setBorderFlags( BorderFlags borderFlags ) +{ + d_borderFlags = borderFlags; +} + +/*! + \return Border flags + \sa setBorderFlags() +*/ +inline QwtInterval::BorderFlags QwtInterval::borderFlags() const +{ + return d_borderFlags; +} + +/*! + Assign the lower limit of the interval + + \param minValue Minimum value +*/ +inline void QwtInterval::setMinValue( double minValue ) +{ + d_minValue = minValue; +} + +/*! + Assign the upper limit of the interval + + \param maxValue Maximum value +*/ +inline void QwtInterval::setMaxValue( double maxValue ) +{ + d_maxValue = maxValue; +} + +//! \return Lower limit of the interval +inline double QwtInterval::minValue() const +{ + return d_minValue; +} + +//! \return Upper limit of the interval +inline double QwtInterval::maxValue() const +{ + return d_maxValue; +} + +/*! + A interval is valid when minValue() <= maxValue(). + In case of QwtInterval::ExcludeBorders it is true + when minValue() < maxValue() +*/ +inline bool QwtInterval::isValid() const +{ + if ( ( d_borderFlags & ExcludeBorders ) == 0 ) + return d_minValue <= d_maxValue; + else + return d_minValue < d_maxValue; +} + +/*! + Return the width of an interval + The width of invalid intervals is 0.0, otherwise the result is + maxValue() - minValue(). + + \sa isValid() +*/ +inline double QwtInterval::width() const +{ + return isValid() ? ( d_maxValue - d_minValue ) : 0.0; +} + +/*! + Intersection of two intervals + \sa intersect() +*/ +inline QwtInterval QwtInterval::operator&( + const QwtInterval &interval ) const +{ + return intersect( interval ); +} + +/*! + Union of two intervals + \sa unite() +*/ +inline QwtInterval QwtInterval::operator|( + const QwtInterval &interval ) const +{ + return unite( interval ); +} + +//! Compare two intervals +inline bool QwtInterval::operator==( const QwtInterval &other ) const +{ + return ( d_minValue == other.d_minValue ) && + ( d_maxValue == other.d_maxValue ) && + ( d_borderFlags == other.d_borderFlags ); +} + +//! Compare two intervals +inline bool QwtInterval::operator!=( const QwtInterval &other ) const +{ + return ( !( *this == other ) ); +} + +/*! + Extend an interval + + \param value Value + \return Extended interval + \sa extend() +*/ +inline QwtInterval QwtInterval::operator|( double value ) const +{ + return extend( value ); +} + +//! \return true, if isValid() && (minValue() >= maxValue()) +inline bool QwtInterval::isNull() const +{ + return isValid() && d_minValue >= d_maxValue; +} + +/*! + Invalidate the interval + + The limits are set to interval [0.0, -1.0] + \sa isValid() +*/ +inline void QwtInterval::invalidate() +{ + d_minValue = 0.0; + d_maxValue = -1.0; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtInterval::BorderFlags ) + +#ifndef QT_NO_DEBUG_STREAM +QWT_EXPORT QDebug operator<<( QDebug, const QwtInterval & ); +#endif + +#endif diff --git a/src/libpcp_qwt/src/qwt_interval_symbol.cpp b/src/libpcp_qwt/src/qwt_interval_symbol.cpp new file mode 100644 index 0000000..e3cef16 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_interval_symbol.cpp @@ -0,0 +1,300 @@ +/* -*- 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_interval_symbol.h" +#include "qwt_painter.h" +#include "qwt_math.h" +#include <qpainter.h> + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) +#endif + +class QwtIntervalSymbol::PrivateData +{ +public: + PrivateData(): + style( QwtIntervalSymbol::NoSymbol ), + width( 6 ) + { + } + + bool operator==( const PrivateData &other ) const + { + return ( style == other.style ) + && ( width == other.width ) + && ( brush == other.brush ) + && ( pen == other.pen ); + } + + QwtIntervalSymbol::Style style; + int width; + + QPen pen; + QBrush brush; +}; + +/*! + Constructor + + \param style Style of the symbol + \sa setStyle(), style(), Style +*/ +QwtIntervalSymbol::QwtIntervalSymbol( Style style ) +{ + d_data = new PrivateData(); + d_data->style = style; +} + +//! Copy constructor +QwtIntervalSymbol::QwtIntervalSymbol( const QwtIntervalSymbol &other ) +{ + d_data = new PrivateData(); + *d_data = *other.d_data; +} + +//! Destructor +QwtIntervalSymbol::~QwtIntervalSymbol() +{ + delete d_data; +} + +//! \brief Assignment operator +QwtIntervalSymbol &QwtIntervalSymbol::operator=( + const QwtIntervalSymbol &other ) +{ + *d_data = *other.d_data; + return *this; +} + +//! \brief Compare two symbols +bool QwtIntervalSymbol::operator==( + const QwtIntervalSymbol &other ) const +{ + return *d_data == *other.d_data; +} + +//! \brief Compare two symbols +bool QwtIntervalSymbol::operator!=( + const QwtIntervalSymbol &other ) const +{ + return !( *d_data == *other.d_data ); +} + +/*! + Specify the symbol style + + \param style Style + \sa style(), Style +*/ +void QwtIntervalSymbol::setStyle( Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtIntervalSymbol::Style QwtIntervalSymbol::style() const +{ + return d_data->style; +} + +/*! + Specify the width of the symbol + It is used depending on the style. + + \param width Width + \sa width(), setStyle() +*/ +void QwtIntervalSymbol::setWidth( int width ) +{ + d_data->width = width; +} + +/*! + \return Width of the symbol. + \sa setWidth(), setStyle() +*/ +int QwtIntervalSymbol::width() const +{ + return d_data->width; +} + +/*! + \brief Assign a brush + + The brush is used for the Box style. + + \param brush Brush + \sa brush() +*/ +void QwtIntervalSymbol::setBrush( const QBrush &brush ) +{ + d_data->brush = brush; +} + +/*! + \return Brush + \sa setBrush() +*/ +const QBrush& QwtIntervalSymbol::brush() const +{ + return d_data->brush; +} + +/*! + Assign a pen + + \param pen Pen + \sa pen(), setBrush() +*/ +void QwtIntervalSymbol::setPen( const QPen &pen ) +{ + d_data->pen = pen; +} + +/*! + \return Pen + \sa setPen(), brush() +*/ +const QPen& QwtIntervalSymbol::pen() const +{ + return d_data->pen; +} + +/*! + Draw a symbol depending on its style + + \param painter Painter + \param orientation Orientation + \param from Start point of the interval in target device coordinates + \param to End point of the interval in target device coordinates + + \sa setStyle() +*/ +void QwtIntervalSymbol::draw( QPainter *painter, Qt::Orientation orientation, + const QPointF &from, const QPointF &to ) const +{ + const qreal pw = qMax( painter->pen().widthF(), qreal( 1.0 ) ); + + QPointF p1 = from; + QPointF p2 = to; + if ( QwtPainter::roundingAlignment( painter ) ) + { + p1 = p1.toPoint(); + p2 = p2.toPoint(); + } + + switch ( d_data->style ) + { + case QwtIntervalSymbol::Bar: + { + QwtPainter::drawLine( painter, p1, p2 ); + if ( d_data->width > pw ) + { + if ( ( orientation == Qt::Horizontal ) + && ( p1.y() == p2.y() ) ) + { + const double sw = d_data->width; + + const double y = p1.y() - sw / 2; + QwtPainter::drawLine( painter, + p1.x(), y, p1.x(), y + sw ); + QwtPainter::drawLine( painter, + p2.x(), y, p2.x(), y + sw ); + } + else if ( ( orientation == Qt::Vertical ) + && ( p1.x() == p2.x() ) ) + { + const double sw = d_data->width; + + const double x = p1.x() - sw / 2; + QwtPainter::drawLine( painter, + x, p1.y(), x + sw, p1.y() ); + QwtPainter::drawLine( painter, + x, p2.y(), x + sw, p2.y() ); + } + else + { + const double sw = d_data->width; + + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + const double angle = qAtan2( dy, dx ) + M_PI_2; + double dw2 = sw / 2.0; + + const double cx = qFastCos( angle ) * dw2; + const double sy = qFastSin( angle ) * dw2; + + QwtPainter::drawLine( painter, + p1.x() - cx, p1.y() - sy, + p1.x() + cx, p1.y() + sy ); + QwtPainter::drawLine( painter, + p2.x() - cx, p2.y() - sy, + p2.x() + cx, p2.y() + sy ); + } + } + break; + } + case QwtIntervalSymbol::Box: + { + if ( d_data->width <= pw ) + { + QwtPainter::drawLine( painter, p1, p2 ); + } + else + { + if ( ( orientation == Qt::Horizontal ) + && ( p1.y() == p2.y() ) ) + { + const double sw = d_data->width; + + const double y = p1.y() - d_data->width / 2; + QwtPainter::drawRect( painter, + p1.x(), y, p2.x() - p1.x(), sw ); + } + else if ( ( orientation == Qt::Vertical ) + && ( p1.x() == p2.x() ) ) + { + const double sw = d_data->width; + + const double x = p1.x() - d_data->width / 2; + QwtPainter::drawRect( painter, + x, p1.y(), sw, p2.y() - p1.y() ); + } + else + { + const double sw = d_data->width; + + const double dx = p2.x() - p1.x(); + const double dy = p2.y() - p1.y(); + const double angle = qAtan2( dy, dx ) + M_PI_2; + double dw2 = sw / 2.0; + + const double cx = qFastCos( angle ) * dw2; + const double sy = qFastSin( angle ) * dw2; + + QPolygonF polygon; + polygon += QPointF( p1.x() - cx, p1.y() - sy ); + polygon += QPointF( p1.x() + cx, p1.y() + sy ); + polygon += QPointF( p2.x() + cx, p2.y() + sy ); + polygon += QPointF( p2.x() - cx, p2.y() - sy ); + + QwtPainter::drawPolygon( painter, polygon ); + } + } + break; + } + default:; + } +} diff --git a/src/libpcp_qwt/src/qwt_interval_symbol.h b/src/libpcp_qwt/src/qwt_interval_symbol.h new file mode 100644 index 0000000..3ea4fe4 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_interval_symbol.h @@ -0,0 +1,86 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_INTERVAL_SYMBOL_H +#define QWT_INTERVAL_SYMBOL_H + +#include "qwt_global.h" +#include <qpen.h> +#include <qsize.h> + +class QPainter; +class QRect; +class QPointF; + +/*! + \brief A drawing primitive for displaying an interval like an error bar + + \sa QwtPlotIntervalCurve +*/ +class QWT_EXPORT QwtIntervalSymbol +{ +public: + //! Symbol style + enum Style + { + //! No Style. The symbol cannot be drawn. + NoSymbol = -1, + + /*! + The symbol displays a line with caps at the beginning/end. + The size of the caps depends on the symbol width(). + */ + Bar, + + /*! + The symbol displays a plain rectangle using pen() and brush(). + The size of the rectangle depends on the translated interval and + the width(), + */ + Box, + + /*! + Styles >= UserSymbol are reserved for derived + classes of QwtIntervalSymbol that overload draw() with + additional application specific symbol types. + */ + UserSymbol = 1000 + }; + +public: + QwtIntervalSymbol( Style = NoSymbol ); + QwtIntervalSymbol( const QwtIntervalSymbol & ); + virtual ~QwtIntervalSymbol(); + + QwtIntervalSymbol &operator=( const QwtIntervalSymbol & ); + bool operator==( const QwtIntervalSymbol & ) const; + bool operator!=( const QwtIntervalSymbol & ) const; + + void setWidth( int ); + int width() const; + + void setBrush( const QBrush& b ); + const QBrush& brush() const; + + void setPen( const QPen & ); + const QPen& pen() const; + + void setStyle( Style ); + Style style() const; + + virtual void draw( QPainter *, Qt::Orientation, + const QPointF& from, const QPointF& to ) const; + +private: + + class PrivateData; + PrivateData* d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_knob.cpp b/src/libpcp_qwt/src/qwt_knob.cpp new file mode 100644 index 0000000..2e41879 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_knob.cpp @@ -0,0 +1,665 @@ +/* -*- 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_knob.h" +#include "qwt_round_scale_draw.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include <qpainter.h> +#include <qpalette.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qevent.h> +#include <qmath.h> +#include <qapplication.h> + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#define qFabs(x) ::fabs(x) +#define qFastCos(x) qCos(x) +#define qFastSin(x) qSin(x) +#endif + +class QwtKnob::PrivateData +{ +public: + PrivateData() + { + angle = 0.0; + nTurns = 0.0; + borderWidth = 2; + borderDist = 4; + totalAngle = 270.0; + scaleDist = 4; + markerStyle = QwtKnob::Notch; + maxScaleTicks = 11; + knobStyle = QwtKnob::Raised; + knobWidth = 50; + markerSize = 8; + } + + QwtKnob::KnobStyle knobStyle; + QwtKnob::MarkerStyle markerStyle; + + int borderWidth; + int borderDist; + int scaleDist; + int maxScaleTicks; + int knobWidth; + int markerSize; + + double angle; + double totalAngle; + double nTurns; + + mutable QRectF knobRect; // bounding rect of the knob without scale +}; + +/*! + Constructor + \param parent Parent widget +*/ +QwtKnob::QwtKnob( QWidget* parent ): + QwtAbstractSlider( Qt::Horizontal, parent ) +{ + initKnob(); +} + +void QwtKnob::initKnob() +{ + d_data = new PrivateData; + + setScaleDraw( new QwtRoundScaleDraw() ); + + setUpdateTime( 50 ); + setTotalAngle( 270.0 ); + recalcAngle(); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); + + setRange( 0.0, 10.0, 1.0 ); + setValue( 0.0 ); +} + +//! Destructor +QwtKnob::~QwtKnob() +{ + delete d_data; +} + +/*! + \brief Set the knob type + + \param knobStyle Knob type + \sa knobStyle(), setBorderWidth() +*/ +void QwtKnob::setKnobStyle( KnobStyle knobStyle ) +{ + if ( d_data->knobStyle != knobStyle ) + { + d_data->knobStyle = knobStyle; + update(); + } +} + +/*! + \return Marker type of the knob + \sa setKnobStyle(), setBorderWidth() +*/ +QwtKnob::KnobStyle QwtKnob::knobStyle() const +{ + return d_data->knobStyle; +} + +/*! + \brief Set the marker type of the knob + + \param markerStyle Marker type + \sa markerStyle(), setMarkerSize() +*/ +void QwtKnob::setMarkerStyle( MarkerStyle markerStyle ) +{ + if ( d_data->markerStyle != markerStyle ) + { + d_data->markerStyle = markerStyle; + update(); + } +} + +/*! + \return Marker type of the knob + \sa setMarkerStyle(), setMarkerSize() +*/ +QwtKnob::MarkerStyle QwtKnob::markerStyle() const +{ + return d_data->markerStyle; +} + +/*! + \brief Set the total angle by which the knob can be turned + \param angle Angle in degrees. + + The default angle is 270 degrees. It is possible to specify + an angle of more than 360 degrees so that the knob can be + turned several times around its axis. +*/ +void QwtKnob::setTotalAngle ( double angle ) +{ + if ( angle < 10.0 ) + d_data->totalAngle = 10.0; + else + d_data->totalAngle = angle; + + scaleDraw()->setAngleRange( -0.5 * d_data->totalAngle, + 0.5 * d_data->totalAngle ); + layoutKnob( true ); +} + +//! Return the total angle +double QwtKnob::totalAngle() const +{ + return d_data->totalAngle; +} + +/*! + Change the scale draw of the knob + + For changing the labels of the scales, it + is necessary to derive from QwtRoundScaleDraw and + overload QwtRoundScaleDraw::label(). + + \sa scaleDraw() +*/ +void QwtKnob::setScaleDraw( QwtRoundScaleDraw *scaleDraw ) +{ + setAbstractScaleDraw( scaleDraw ); + setTotalAngle( d_data->totalAngle ); +} + +/*! + \return the scale draw of the knob + \sa setScaleDraw() +*/ +const QwtRoundScaleDraw *QwtKnob::scaleDraw() const +{ + return static_cast<const QwtRoundScaleDraw *>( abstractScaleDraw() ); +} + +/*! + \return the scale draw of the knob + \sa setScaleDraw() +*/ +QwtRoundScaleDraw *QwtKnob::scaleDraw() +{ + return static_cast<QwtRoundScaleDraw *>( abstractScaleDraw() ); +} + +/*! + \brief Notify change of value + + Sets the knob's value to the nearest multiple + of the step size. +*/ +void QwtKnob::valueChange() +{ + recalcAngle(); + update(); + QwtAbstractSlider::valueChange(); +} + +/*! + \brief Determine the value corresponding to a specified position + + Called by QwtAbstractSlider + \param pos point +*/ +double QwtKnob::getValue( const QPoint &pos ) +{ + const double dx = rect().center().x() - pos.x(); + const double dy = rect().center().y() - pos.y(); + + const double arc = qAtan2( -dx, dy ) * 180.0 / M_PI; + + double newValue = 0.5 * ( minValue() + maxValue() ) + + ( arc + d_data->nTurns * 360.0 ) * ( maxValue() - minValue() ) + / d_data->totalAngle; + + const double oneTurn = qFabs( maxValue() - minValue() ) * 360.0 / d_data->totalAngle; + const double eqValue = value() + mouseOffset(); + + if ( qFabs( newValue - eqValue ) > 0.5 * oneTurn ) + { + if ( newValue < eqValue ) + newValue += oneTurn; + else + newValue -= oneTurn; + } + + return newValue; +} + +/*! + \brief Set the scrolling mode and direction + + Called by QwtAbstractSlider + \param pos Point in question + \param scrollMode Scrolling mode + \param direction Direction +*/ +void QwtKnob::getScrollMode( const QPoint &pos, + QwtAbstractSlider::ScrollMode &scrollMode, int &direction ) const +{ + const double r = 0.5 * d_data->knobRect.width(); + const double dx = d_data->knobRect.x() + r - pos.x(); + const double dy = d_data->knobRect.y() + r - pos.y(); + + if ( qwtSqr( dx ) + qwtSqr( dy ) <= qwtSqr( r ) ) + { + // point is inside the knob + + scrollMode = QwtAbstractSlider::ScrMouse; + direction = 0; + } + else // point lies outside + { + scrollMode = QwtAbstractSlider::ScrTimer; + + double arc = qAtan2( double( -dx ), double( dy ) ) * 180.0 / M_PI; + if ( arc < d_data->angle ) + direction = -1; + else if ( arc > d_data->angle ) + direction = 1; + else + direction = 0; + } +} + +/*! + \brief Notify a change of the range + + Called by QwtAbstractSlider +*/ +void QwtKnob::rangeChange() +{ + if ( autoScale() ) + rescale( minValue(), maxValue() ); + + layoutKnob( true ); + recalcAngle(); +} + +/*! + Qt Resize Event + \param event Resize event +*/ +void QwtKnob::resizeEvent( QResizeEvent *event ) +{ + Q_UNUSED( event ); + layoutKnob( false ); +} + +/*! + Handle QEvent::StyleChange and QEvent::FontChange; + \param event Change event +*/ +void QwtKnob::changeEvent( QEvent *event ) +{ + switch( event->type() ) + { + case QEvent::StyleChange: + case QEvent::FontChange: + layoutKnob( true ); + break; + default: + break; + } +} + +/*! + Recalculate the knob'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 QwtKnob::layoutKnob( bool update_geometry ) +{ + const double d = d_data->knobWidth; + + d_data->knobRect.setWidth( d ); + d_data->knobRect.setHeight( d ); + d_data->knobRect.moveCenter( rect().center() ); + + scaleDraw()->setRadius( 0.5 * d + d_data->scaleDist ); + scaleDraw()->moveCenter( rect().center() ); + + if ( update_geometry ) + { + updateGeometry(); + update(); + } +} + +/*! + Repaint the knob + \param event Paint event +*/ +void QwtKnob::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + painter.setRenderHint( QPainter::Antialiasing, true ); + + if ( !d_data->knobRect.contains( event->region().boundingRect() ) ) + scaleDraw()->draw( &painter, palette() ); + + drawKnob( &painter, d_data->knobRect ); + drawMarker( &painter, d_data->knobRect, d_data->angle ); + + painter.setRenderHint( QPainter::Antialiasing, false ); + + if ( hasFocus() ) + QwtPainter::drawFocusRect( &painter, this ); +} + +/*! + \brief Draw the knob + \param painter painter + \param knobRect Bounding rectangle of the knob (without scale) +*/ +void QwtKnob::drawKnob( QPainter *painter, const QRectF &knobRect ) const +{ + double dim = qMin( knobRect.width(), knobRect.height() ); + dim -= d_data->borderWidth * 0.5; + + QRectF aRect( 0, 0, dim, dim ); + aRect.moveCenter( knobRect.center() ); + + QPen pen( Qt::NoPen ); + if ( d_data->borderWidth > 0 ) + { + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Dark ); + + QLinearGradient gradient( aRect.topLeft(), aRect.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 0.3, c1 ); + gradient.setColorAt( 0.7, c2 ); + gradient.setColorAt( 1.0, c2 ); + + pen = QPen( gradient, d_data->borderWidth ); + } + + QBrush brush; + switch( d_data->knobStyle ) + { + case QwtKnob::Raised: + { + double off = 0.3 * knobRect.width(); + QRadialGradient gradient( knobRect.center(), + knobRect.width(), knobRect.topLeft() + QPointF( off, off ) ); + + gradient.setColorAt( 0.0, palette().color( QPalette::Midlight ) ); + gradient.setColorAt( 1.0, palette().color( QPalette::Button ) ); + + brush = QBrush( gradient ); + + break; + } + case QwtKnob::Sunken: + { + QLinearGradient gradient( + knobRect.topLeft(), knobRect.bottomRight() ); + gradient.setColorAt( 0.0, palette().color( QPalette::Mid ) ); + gradient.setColorAt( 0.5, palette().color( QPalette::Button ) ); + gradient.setColorAt( 1.0, palette().color( QPalette::Midlight ) ); + brush = QBrush( gradient ); + + break; + } + default: + brush = palette().brush( QPalette::Button ); + } + + painter->setPen( pen ); + painter->setBrush( brush ); + painter->drawEllipse( aRect ); +} + + +/*! + \brief Draw the marker at the knob's front + \param painter Painter + \param rect Bounding rectangle of the knob without scale + \param angle Angle of the marker in degrees +*/ +void QwtKnob::drawMarker( QPainter *painter, + const QRectF &rect, double angle ) const +{ + if ( d_data->markerStyle == NoMarker || !isValid() ) + return; + + const double radians = angle * M_PI / 180.0; + const double sinA = -qFastSin( radians ); + const double cosA = qFastCos( radians ); + + const double xm = rect.center().x(); + const double ym = rect.center().y(); + const double margin = 4.0; + + double radius = 0.5 * ( rect.width() - d_data->borderWidth ) - margin; + if ( radius < 1.0 ) + radius = 1.0; + + switch ( d_data->markerStyle ) + { + case Notch: + case Nub: + { + const double dotWidth = + qMin( double( d_data->markerSize ), radius); + + const double dotCenterDist = radius - 0.5 * dotWidth; + if ( dotCenterDist > 0.0 ) + { + const QPointF center( xm - sinA * dotCenterDist, + ym - cosA * dotCenterDist ); + + QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth ); + ellipse.moveCenter( center ); + + QColor c1 = palette().color( QPalette::Light ); + QColor c2 = palette().color( QPalette::Mid ); + + if ( d_data->markerStyle == Notch ) + qSwap( c1, c2 ); + + QLinearGradient gradient( + ellipse.topLeft(), ellipse.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 1.0, c2 ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( gradient ); + + painter->drawEllipse( ellipse ); + } + break; + } + case Dot: + { + const double dotWidth = + qMin( double( d_data->markerSize ), radius); + + const double dotCenterDist = radius - 0.5 * dotWidth; + if ( dotCenterDist > 0.0 ) + { + const QPointF center( xm - sinA * dotCenterDist, + ym - cosA * dotCenterDist ); + + QRectF ellipse( 0.0, 0.0, dotWidth, dotWidth ); + ellipse.moveCenter( center ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().color( QPalette::ButtonText ) ); + painter->drawEllipse( ellipse ); + } + + break; + } + case Tick: + { + const double rb = qMax( radius - d_data->markerSize, 1.0 ); + const double re = radius; + + const QLineF line( xm - sinA * rb, ym - cosA * rb, + xm - sinA * re, ym - cosA * re ); + + QPen pen( palette().color( QPalette::ButtonText ), 0 ); + pen.setCapStyle( Qt::FlatCap ); + painter->setPen( pen ); + painter->drawLine ( line ); + + break; + } +#if 0 + case Triangle: + { + const double rb = qMax( radius - d_data->markerSize, 1.0 ); + const double re = radius; + + painter->translate( rect.center() ); + painter->rotate( angle - 90.0 ); + + QPolygonF polygon; + polygon += QPointF( re, 0.0 ); + polygon += QPointF( rb, 0.5 * ( re - rb ) ); + polygon += QPointF( rb, -0.5 * ( re - rb ) ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().color( QPalette::Text ) ); + painter->drawPolygon( polygon ); + break; + } +#endif + default: + break; + } +} + +/*! + \brief Change the knob's width. + + The specified width must be >= 5, or it will be clipped. + \param width New width +*/ +void QwtKnob::setKnobWidth( int width ) +{ + d_data->knobWidth = qMax( width, 5 ); + layoutKnob( true ); +} + +//! Return the width of the knob +int QwtKnob::knobWidth() const +{ + return d_data->knobWidth; +} + +/*! + \brief Set the knob's border width + \param borderWidth new border width +*/ +void QwtKnob::setBorderWidth( int borderWidth ) +{ + d_data->borderWidth = qMax( borderWidth, 0 ); + layoutKnob( true ); +} + +//! Return the border width +int QwtKnob::borderWidth() const +{ + return d_data->borderWidth; +} + +/*! + \brief Set the size of the marker + \sa markerSize(), markerStyle() +*/ +void QwtKnob::setMarkerSize( int size ) +{ + if ( d_data->markerSize != size ) + { + d_data->markerSize = size; + update(); + } +} + +//! Return the marker size +int QwtKnob::markerSize() const +{ + return d_data->markerSize; +} + +/*! + \brief Recalculate the marker angle corresponding to the + current value +*/ +void QwtKnob::recalcAngle() +{ + // calculate the angle corresponding to the value + if ( maxValue() == minValue() ) + { + d_data->angle = 0; + d_data->nTurns = 0; + } + else + { + d_data->angle = ( value() - 0.5 * ( minValue() + maxValue() ) ) + / ( maxValue() - minValue() ) * d_data->totalAngle; + d_data->nTurns = qFloor( ( d_data->angle + 180.0 ) / 360.0 ); + d_data->angle = d_data->angle - d_data->nTurns * 360.0; + } +} + + +/*! + Recalculates the layout + \sa layoutKnob() +*/ +void QwtKnob::scaleChange() +{ + layoutKnob( true ); +} + +/*! + \return minimumSizeHint() +*/ +QSize QwtKnob::sizeHint() const +{ + const QSize hint = minimumSizeHint(); + return hint.expandedTo( QApplication::globalStrut() ); +} + +/*! + \brief Return a minimum size hint + \warning The return value of QwtKnob::minimumSizeHint() depends on the + font and the scale. +*/ +QSize QwtKnob::minimumSizeHint() const +{ + // Add the scale radial thickness to the knobWidth + const int sh = qCeil( scaleDraw()->extent( font() ) ); + const int d = 2 * sh + 2 * d_data->scaleDist + d_data->knobWidth; + + return QSize( d, d ); +} diff --git a/src/libpcp_qwt/src/qwt_knob.h b/src/libpcp_qwt/src/qwt_knob.h new file mode 100644 index 0000000..355b4c5 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_knob.h @@ -0,0 +1,159 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_KNOB_H +#define QWT_KNOB_H + +#include "qwt_global.h" +#include "qwt_abstract_slider.h" +#include "qwt_abstract_scale.h" + +class QwtRoundScaleDraw; + +/*! + \brief The Knob Widget + + The QwtKnob widget imitates look and behaviour of a volume knob on a radio. + It contains a scale around the knob which is set up automatically or can + be configured manually (see QwtAbstractScale). + Automatic scrolling is enabled when the user presses a mouse + button on the scale. For a description of signals, slots and other + members, see QwtAbstractSlider. + + \image html knob.png + \sa QwtAbstractSlider and QwtAbstractScale for the descriptions + of the inherited members. +*/ + +class QWT_EXPORT QwtKnob : public QwtAbstractSlider, public QwtAbstractScale +{ + Q_OBJECT + + Q_ENUMS ( KnobStyle ) + Q_ENUMS ( MarkerStyle ) + + Q_PROPERTY( KnobStyle knobStyle READ knobStyle WRITE setKnobStyle ) + Q_PROPERTY( MarkerStyle markerStyle READ markerStyle WRITE setMarkerStyle ) + Q_PROPERTY( int knobWidth READ knobWidth WRITE setKnobWidth ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle ) + Q_PROPERTY( int markerSize READ markerSize WRITE setMarkerSize ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + +public: + /*! + \brief Style of the knob surface + + Depending on the KnobStyle the surface of the knob is + filled from the brushes of the widget palette(). + + \sa setKnobStyle(), knobStyle() + */ + enum KnobStyle + { + //! Fill the knob with a brush from QPalette::Button. + NoStyle = -1, + + //! Build a gradient from QPalette::Midlight and QPalette::Button + Raised, + + /*! + Build a gradient from QPalette::Midlight, QPalette::Button + and QPalette::Midlight + */ + Sunken + }; + + /*! + \brief Marker type + + The marker indicates the current value on the knob + The default setting is a Notch marker. + + \sa setMarkerStyle(), setMarkerSize() + */ + enum MarkerStyle + { + //! Don't paint any marker + NoMarker = -1, + + //! Paint a single tick in QPalette::ButtonText color + Tick, + + //! Paint a circle in QPalette::ButtonText color + Dot, + + /*! + Draw a raised ellipse with a gradient build from + QPalette::Light and QPalette::Mid + */ + Nub, + + /*! + Draw a sunken ellipse with a gradient build from + QPalette::Light and QPalette::Mid + */ + Notch + }; + + explicit QwtKnob( QWidget* parent = NULL ); + virtual ~QwtKnob(); + + void setKnobWidth( int w ); + int knobWidth() const; + + void setTotalAngle ( double angle ); + double totalAngle() const; + + void setKnobStyle( KnobStyle ); + KnobStyle knobStyle() const; + + void setBorderWidth( int bw ); + int borderWidth() const; + + void setMarkerStyle( MarkerStyle ); + MarkerStyle markerStyle() const; + + void setMarkerSize( int ); + int markerSize() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + void setScaleDraw( QwtRoundScaleDraw * ); + const QwtRoundScaleDraw *scaleDraw() const; + QwtRoundScaleDraw *scaleDraw(); + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + virtual void changeEvent( QEvent * ); + + virtual void drawKnob( QPainter *, const QRectF & ) const; + virtual void drawMarker( QPainter *, + const QRectF &, double arc ) const; + + virtual double getValue( const QPoint &p ); + virtual void getScrollMode( const QPoint &, + QwtAbstractSlider::ScrollMode &, int &direction ) const; + +private: + void initKnob(); + void layoutKnob( bool update ); + void recalcAngle(); + + virtual void valueChange(); + virtual void rangeChange(); + virtual void scaleChange(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_legend.cpp b/src/libpcp_qwt/src/qwt_legend.cpp new file mode 100644 index 0000000..f4797e6 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_legend.cpp @@ -0,0 +1,519 @@ +/* -*- 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_legend.h" +#include "qwt_legend_itemmanager.h" +#include "qwt_legend_item.h" +#include "qwt_dyngrid_layout.h" +#include "qwt_math.h" +#include <qapplication.h> +#include <qmap.h> +#include <qscrollbar.h> +#include <qscrollarea.h> + +class QwtLegend::PrivateData +{ +public: + class LegendMap + { + public: + void insert( const QwtLegendItemManager *, QWidget * ); + + void remove( const QwtLegendItemManager * ); + void remove( QWidget * ); + + void clear(); + + uint count() const; + + inline const QWidget *find( const QwtLegendItemManager * ) const; + inline QWidget *find( const QwtLegendItemManager * ); + + inline const QwtLegendItemManager *find( const QWidget * ) const; + inline QwtLegendItemManager *find( const QWidget * ); + + const QMap<QWidget *, const QwtLegendItemManager *> &widgetMap() const; + QMap<QWidget *, const QwtLegendItemManager *> &widgetMap(); + + private: + QMap<QWidget *, const QwtLegendItemManager *> d_widgetMap; + QMap<const QwtLegendItemManager *, QWidget *> d_itemMap; + }; + + QwtLegend::LegendItemMode itemMode; + + LegendMap map; + + class LegendView; + LegendView *view; +}; + +class QwtLegend::PrivateData::LegendView: public QScrollArea +{ +public: + LegendView( QWidget *parent ): + QScrollArea( parent ) + { + setFocusPolicy( Qt::NoFocus ); + + contentsWidget = new QWidget( this ); + contentsWidget->setObjectName( "QwtLegendViewContents" ); + + setWidget( contentsWidget ); + setWidgetResizable( false ); + + viewport()->setObjectName( "QwtLegendViewport" ); + + // QScrollArea::setWidget internally sets autoFillBackground to true + // But we don't want a background. + contentsWidget->setAutoFillBackground( false ); + viewport()->setAutoFillBackground( false ); + } + + virtual bool viewportEvent( QEvent *e ) + { + bool ok = QScrollArea::viewportEvent( e ); + + if ( e->type() == QEvent::Resize ) + { + QEvent event( QEvent::LayoutRequest ); + QApplication::sendEvent( contentsWidget, &event ); + } + return ok; + } + + QSize viewportSize( int w, int h ) const + { + const int sbHeight = horizontalScrollBar()->sizeHint().height(); + const int sbWidth = verticalScrollBar()->sizeHint().width(); + + const int cw = contentsRect().width(); + const int ch = contentsRect().height(); + + int vw = cw; + int vh = ch; + + if ( w > vw ) + vh -= sbHeight; + + if ( h > vh ) + { + vw -= sbWidth; + if ( w > vw && vh == ch ) + vh -= sbHeight; + } + return QSize( vw, vh ); + } + + QWidget *contentsWidget; +}; + +void QwtLegend::PrivateData::LegendMap::insert( + const QwtLegendItemManager *item, QWidget *widget ) +{ + d_itemMap.insert( item, widget ); + d_widgetMap.insert( widget, item ); +} + +void QwtLegend::PrivateData::LegendMap::remove( const QwtLegendItemManager *item ) +{ + QWidget *widget = d_itemMap[item]; + d_itemMap.remove( item ); + d_widgetMap.remove( widget ); +} + +void QwtLegend::PrivateData::LegendMap::remove( QWidget *widget ) +{ + const QwtLegendItemManager *item = d_widgetMap[widget]; + d_itemMap.remove( item ); + d_widgetMap.remove( widget ); +} + +void QwtLegend::PrivateData::LegendMap::clear() +{ + + /* + We can't delete the widgets in the following loop, because + we would get ChildRemoved events, changing d_itemMap, while + we are iterating. + */ + + QList<const QWidget *> widgets; + + QMap<const QwtLegendItemManager *, QWidget *>::const_iterator it; + for ( it = d_itemMap.begin(); it != d_itemMap.end(); ++it ) + widgets.append( it.value() ); + + d_itemMap.clear(); + d_widgetMap.clear(); + + for ( int i = 0; i < widgets.size(); i++ ) + delete widgets[i]; +} + +uint QwtLegend::PrivateData::LegendMap::count() const +{ + return d_itemMap.count(); +} + +inline const QWidget *QwtLegend::PrivateData::LegendMap::find( + const QwtLegendItemManager *item ) const +{ + if ( !d_itemMap.contains( item ) ) + return NULL; + + return d_itemMap[item]; +} + +inline QWidget *QwtLegend::PrivateData::LegendMap::find( + const QwtLegendItemManager *item ) +{ + if ( !d_itemMap.contains( item ) ) + return NULL; + + return d_itemMap[item]; +} + +inline const QwtLegendItemManager *QwtLegend::PrivateData::LegendMap::find( + const QWidget *widget ) const +{ + QWidget *w = const_cast<QWidget *>( widget ); + if ( !d_widgetMap.contains( w ) ) + return NULL; + + return d_widgetMap[w]; +} + +inline QwtLegendItemManager *QwtLegend::PrivateData::LegendMap::find( + const QWidget *widget ) +{ + QWidget *w = const_cast<QWidget *>( widget ); + if ( !d_widgetMap.contains( w ) ) + return NULL; + + return const_cast<QwtLegendItemManager *>( d_widgetMap[w] ); +} + +inline const QMap<QWidget *, const QwtLegendItemManager *> & +QwtLegend::PrivateData::LegendMap::widgetMap() const +{ + return d_widgetMap; +} + +inline QMap<QWidget *, const QwtLegendItemManager *> & +QwtLegend::PrivateData::LegendMap::widgetMap() +{ + return d_widgetMap; +} + +/*! + Constructor + + \param parent Parent widget +*/ +QwtLegend::QwtLegend( QWidget *parent ): + QFrame( parent ) +{ + setFrameStyle( NoFrame ); + + d_data = new QwtLegend::PrivateData; + d_data->itemMode = QwtLegend::ReadOnlyItem; + + d_data->view = new QwtLegend::PrivateData::LegendView( this ); + d_data->view->setObjectName( "QwtLegendView" ); + d_data->view->setFrameStyle( NoFrame ); + + QwtDynGridLayout *gridLayout = new QwtDynGridLayout( + d_data->view->contentsWidget ); + gridLayout->setAlignment( Qt::AlignHCenter | Qt::AlignTop ); + + d_data->view->contentsWidget->installEventFilter( this ); + + QVBoxLayout *layout = new QVBoxLayout( this ); + layout->setContentsMargins( 0, 0, 0, 0 ); + layout->addWidget( d_data->view ); +} + +//! Destructor +QwtLegend::~QwtLegend() +{ + delete d_data; +} + +//! \sa LegendItemMode +void QwtLegend::setItemMode( LegendItemMode mode ) +{ + d_data->itemMode = mode; +} + +//! \sa LegendItemMode +QwtLegend::LegendItemMode QwtLegend::itemMode() const +{ + return d_data->itemMode; +} + +/*! + The contents widget is the only child of the viewport of + the internal QScrollArea and the parent widget of all legend items. + + \return Container widget of the legend items +*/ +QWidget *QwtLegend::contentsWidget() +{ + return d_data->view->contentsWidget; +} + +/*! + \return Horizontal scrollbar + \sa verticalScrollBar() +*/ +QScrollBar *QwtLegend::horizontalScrollBar() const +{ + return d_data->view->horizontalScrollBar(); +} + +/*! + \return Vertical scrollbar + \sa horizontalScrollBar() +*/ +QScrollBar *QwtLegend::verticalScrollBar() const +{ + return d_data->view->verticalScrollBar(); +} + +/*! + The contents widget is the only child of the viewport of + the internal QScrollArea and the parent widget of all legend items. + + \return Container widget of the legend items + +*/ +const QWidget *QwtLegend::contentsWidget() const +{ + return d_data->view->contentsWidget; +} + +/*! + Insert a new item for a plot item + \param plotItem Plot item + \param legendItem New legend item + \note The parent of item will be changed to contentsWidget() +*/ +void QwtLegend::insert( const QwtLegendItemManager *plotItem, QWidget *legendItem ) +{ + if ( legendItem == NULL || plotItem == NULL ) + return; + + QWidget *contentsWidget = d_data->view->contentsWidget; + + if ( legendItem->parent() != contentsWidget ) + legendItem->setParent( contentsWidget ); + + legendItem->show(); + + d_data->map.insert( plotItem, legendItem ); + + layoutContents(); + + if ( contentsWidget->layout() ) + { + contentsWidget->layout()->addWidget( legendItem ); + + // set tab focus chain + + QWidget *w = NULL; + + for ( int i = 0; i < contentsWidget->layout()->count(); i++ ) + { + QLayoutItem *item = contentsWidget->layout()->itemAt( i ); + if ( w && item->widget() ) + QWidget::setTabOrder( w, item->widget() ); + + w = item->widget(); + } + } + if ( parentWidget() && parentWidget()->layout() == NULL ) + { + /* + updateGeometry() doesn't post LayoutRequest in certain + situations, like when we are hidden. But we want the + parent widget notified, so it can show/hide the legend + depending on its items. + */ + QApplication::postEvent( parentWidget(), + new QEvent( QEvent::LayoutRequest ) ); + } +} + +/*! + Find the widget that represents a plot item + + \param plotItem Plot item + \return Widget on the legend, or NULL +*/ +QWidget *QwtLegend::find( const QwtLegendItemManager *plotItem ) const +{ + return d_data->map.find( plotItem ); +} + +/*! + Find the widget that represents a plot item + + \param legendItem Legend item + \return Widget on the legend, or NULL +*/ +QwtLegendItemManager *QwtLegend::find( const QWidget *legendItem ) const +{ + return d_data->map.find( legendItem ); +} + +/*! + Find the corresponding item for a plotItem and remove it + from the item list. + + \param plotItem Plot item +*/ +void QwtLegend::remove( const QwtLegendItemManager *plotItem ) +{ + QWidget *legendItem = d_data->map.find( plotItem ); + d_data->map.remove( legendItem ); + delete legendItem; +} + +//! Remove all items. +void QwtLegend::clear() +{ + bool doUpdate = updatesEnabled(); + if ( doUpdate ) + setUpdatesEnabled( false ); + + d_data->map.clear(); + + if ( doUpdate ) + setUpdatesEnabled( true ); + + update(); +} + +//! Return a size hint. +QSize QwtLegend::sizeHint() const +{ + QSize hint = d_data->view->contentsWidget->sizeHint(); + hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); + + return hint; +} + +/*! + \return The preferred height, for the width w. + \param width Width +*/ +int QwtLegend::heightForWidth( int width ) const +{ + width -= 2 * frameWidth(); + + int h = d_data->view->contentsWidget->heightForWidth( width ); + if ( h >= 0 ) + h += 2 * frameWidth(); + + return h; +} + +/*! + Adjust contents widget and item layout to the size of the viewport(). +*/ +void QwtLegend::layoutContents() +{ + const QSize visibleSize = + d_data->view->viewport()->contentsRect().size(); + + const QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>( + d_data->view->contentsWidget->layout() ); + if ( tl ) + { + const int minW = int( tl->maxItemWidth() ) + 2 * tl->margin(); + + int w = qMax( visibleSize.width(), minW ); + int h = qMax( tl->heightForWidth( w ), visibleSize.height() ); + + const int vpWidth = d_data->view->viewportSize( w, h ).width(); + if ( w > vpWidth ) + { + w = qMax( vpWidth, minW ); + h = qMax( tl->heightForWidth( w ), visibleSize.height() ); + } + + d_data->view->contentsWidget->resize( w, h ); + } +} + +/*! + Handle QEvent::ChildRemoved andQEvent::LayoutRequest events + for the contentsWidget(). + + \param object Object to be filtered + \param event Event +*/ +bool QwtLegend::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == d_data->view->contentsWidget ) + { + switch ( event->type() ) + { + case QEvent::ChildRemoved: + { + const QChildEvent *ce = + static_cast<const QChildEvent *>(event); + if ( ce->child()->isWidgetType() ) + { + QWidget *w = static_cast< QWidget * >( ce->child() ); + d_data->map.remove( w ); + } + break; + } + case QEvent::LayoutRequest: + { + layoutContents(); + break; + } + default: + break; + } + } + + return QFrame::eventFilter( object, event ); +} + + +//! Return true, if there are no legend items. +bool QwtLegend::isEmpty() const +{ + return d_data->map.count() == 0; +} + +//! Return the number of legend items. +uint QwtLegend::itemCount() const +{ + return d_data->map.count(); +} + +//! Return a list of all legend items +QList<QWidget *> QwtLegend::legendItems() const +{ + const QMap<QWidget *, const QwtLegendItemManager *> &map = + d_data->map.widgetMap(); + + QList<QWidget *> list; + + QMap<QWidget *, const QwtLegendItemManager *>::const_iterator it; + for ( it = map.begin(); it != map.end(); ++it ) + list += it.key(); + + return list; +}
\ No newline at end of file diff --git a/src/libpcp_qwt/src/qwt_legend.h b/src/libpcp_qwt/src/qwt_legend.h new file mode 100644 index 0000000..e241783 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_legend.h @@ -0,0 +1,95 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_LEGEND_H +#define QWT_LEGEND_H + +#include "qwt_global.h" +#include <qframe.h> +#include <qlist.h> + +class QScrollBar; +class QwtLegendItemManager; + +/*! + \brief The legend widget + + The QwtLegend widget is a tabular arrangement of legend items. Legend + items might be any type of widget, but in general they will be + a QwtLegendItem. + + \sa QwtLegendItem, QwtLegendItemManager QwtPlot +*/ + +class QWT_EXPORT QwtLegend : public QFrame +{ + Q_OBJECT + +public: + /*! + \brief Interaction mode for the legend items + + The default is QwtLegend::ReadOnlyItem. + + \sa setItemMode(), itemMode(), QwtLegendItem::IdentifierMode + QwtLegendItem::clicked(), QwtLegendItem::checked(), + QwtPlot::legendClicked(), QwtPlot::legendChecked() + */ + + enum LegendItemMode + { + //! The legend item is not interactive, like a label + ReadOnlyItem, + + //! The legend item is clickable, like a push button + ClickableItem, + + //! The legend item is checkable, like a checkable button + CheckableItem + }; + + explicit QwtLegend( QWidget *parent = NULL ); + virtual ~QwtLegend(); + + void setItemMode( LegendItemMode ); + LegendItemMode itemMode() const; + + QWidget *contentsWidget(); + const QWidget *contentsWidget() const; + + void insert( const QwtLegendItemManager *, QWidget * ); + void remove( const QwtLegendItemManager * ); + + QWidget *find( const QwtLegendItemManager * ) const; + QwtLegendItemManager *find( const QWidget * ) const; + + virtual QList<QWidget *> legendItems() const; + + void clear(); + + bool isEmpty() const; + uint itemCount() const; + + virtual bool eventFilter( QObject *, QEvent * ); + + virtual QSize sizeHint() const; + virtual int heightForWidth( int w ) const; + + QScrollBar *horizontalScrollBar() const; + QScrollBar *verticalScrollBar() const; + +protected: + virtual void layoutContents(); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_legend_item.cpp b/src/libpcp_qwt/src/qwt_legend_item.cpp new file mode 100644 index 0000000..5d59af4 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_legend_item.cpp @@ -0,0 +1,407 @@ +/* -*- 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_legend_item.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include "qwt_symbol.h" +#include <qpainter.h> +#include <qdrawutil.h> +#include <qstyle.h> +#include <qpen.h> +#include <qevent.h> +#include <qstyleoption.h> +#include <qapplication.h> + +static const int PixmapHeight = 10; +static const int PixmapWidth = 15; +static const int ButtonFrame = 2; +static const int Margin = 2; + +static QSize buttonShift( const QwtLegendItem *w ) +{ + QStyleOption option; + option.init( w ); + + const int ph = w->style()->pixelMetric( + QStyle::PM_ButtonShiftHorizontal, &option, w ); + const int pv = w->style()->pixelMetric( + QStyle::PM_ButtonShiftVertical, &option, w ); + return QSize( ph, pv ); +} + +class QwtLegendItem::PrivateData +{ +public: + PrivateData(): + itemMode( QwtLegend::ReadOnlyItem ), + isDown( false ), + identifierSize( PixmapWidth, PixmapHeight ), + spacing( Margin ) + { + } + + QwtLegend::LegendItemMode itemMode; + bool isDown; + + QSize identifierSize; + QPixmap identifier; + + int spacing; +}; + +/*! + \param parent Parent widget +*/ +QwtLegendItem::QwtLegendItem( QWidget *parent ): + QwtTextLabel( parent ) +{ + d_data = new PrivateData; + setMargin( Margin ); + setIndent( Margin + d_data->identifierSize.width() + 2 * d_data->spacing ); +} + +//! Destructor +QwtLegendItem::~QwtLegendItem() +{ + delete d_data; + d_data = NULL; +} + +/*! + Set the text to the legend item + + \param text Text label + \sa QwtTextLabel::text() +*/ +void QwtLegendItem::setText( const QwtText &text ) +{ + const int flags = Qt::AlignLeft | Qt::AlignVCenter + | Qt::TextExpandTabs | Qt::TextWordWrap; + + QwtText txt = text; + txt.setRenderFlags( flags ); + + QwtTextLabel::setText( txt ); +} + +/*! + Set the item mode + The default is QwtLegend::ReadOnlyItem + + \param mode Item mode + \sa itemMode() +*/ +void QwtLegendItem::setItemMode( QwtLegend::LegendItemMode mode ) +{ + if ( mode != d_data->itemMode ) + { + d_data->itemMode = mode; + d_data->isDown = false; + + setFocusPolicy( mode != QwtLegend::ReadOnlyItem ? Qt::TabFocus : Qt::NoFocus ); + setMargin( ButtonFrame + Margin ); + + updateGeometry(); + } +} + +/*! + Return the item mode + + \sa setItemMode() +*/ +QwtLegend::LegendItemMode QwtLegendItem::itemMode() const +{ + return d_data->itemMode; +} + +/*! + Assign the identifier + The identifier needs to be created according to the identifierWidth() + + \param identifier Pixmap representing a plot item + + \sa identifier(), identifierWidth() +*/ +void QwtLegendItem::setIdentifier( const QPixmap &identifier ) +{ + d_data->identifier = identifier; + update(); +} + +/*! + \return pixmap representing a plot item + \sa setIdentifier() +*/ +QPixmap QwtLegendItem::identifier() const +{ + return d_data->identifier; +} + +/*! + Set the size for the identifier + Default is PixmapWidth x PixmapHeight pixels + + \param size New size + + \sa identifierSize() +*/ +void QwtLegendItem::setIdentifierSize( const QSize &size ) +{ + QSize sz = size.expandedTo( QSize( 0, 0 ) ); + if ( sz != d_data->identifierSize ) + { + d_data->identifierSize = sz; + setIndent( margin() + d_data->identifierSize.width() + + 2 * d_data->spacing ); + updateGeometry(); + } +} +/*! + Return the width of the identifier + + \sa setIdentifierSize() +*/ +QSize QwtLegendItem::identifierSize() const +{ + return d_data->identifierSize; +} + +/*! + Change the spacing + \param spacing Spacing + \sa spacing(), identifierWidth(), QwtTextLabel::margin() +*/ +void QwtLegendItem::setSpacing( int spacing ) +{ + spacing = qMax( spacing, 0 ); + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + setIndent( margin() + d_data->identifierSize.width() + + 2 * d_data->spacing ); + } +} + +/*! + Return the spacing + \sa setSpacing(), identifierWidth(), QwtTextLabel::margin() +*/ +int QwtLegendItem::spacing() const +{ + return d_data->spacing; +} + +/*! + Check/Uncheck a the item + + \param on check/uncheck + \sa setItemMode() +*/ +void QwtLegendItem::setChecked( bool on ) +{ + if ( d_data->itemMode == QwtLegend::CheckableItem ) + { + const bool isBlocked = signalsBlocked(); + blockSignals( true ); + + setDown( on ); + + blockSignals( isBlocked ); + } +} + +//! Return true, if the item is checked +bool QwtLegendItem::isChecked() const +{ + return d_data->itemMode == QwtLegend::CheckableItem && isDown(); +} + +//! Set the item being down +void QwtLegendItem::setDown( bool down ) +{ + if ( down == d_data->isDown ) + return; + + d_data->isDown = down; + update(); + + if ( d_data->itemMode == QwtLegend::ClickableItem ) + { + if ( d_data->isDown ) + Q_EMIT pressed(); + else + { + Q_EMIT released(); + Q_EMIT clicked(); + } + } + + if ( d_data->itemMode == QwtLegend::CheckableItem ) + Q_EMIT checked( d_data->isDown ); +} + +//! Return true, if the item is down +bool QwtLegendItem::isDown() const +{ + return d_data->isDown; +} + +//! Return a size hint +QSize QwtLegendItem::sizeHint() const +{ + QSize sz = QwtTextLabel::sizeHint(); + sz.setHeight( qMax( sz.height(), d_data->identifier.height() + 4 ) ); + + if ( d_data->itemMode != QwtLegend::ReadOnlyItem ) + { + sz += buttonShift( this ); + sz = sz.expandedTo( QApplication::globalStrut() ); + } + + return sz; +} + +//! Paint event +void QwtLegendItem::paintEvent( QPaintEvent *e ) +{ + const QRect cr = contentsRect(); + + QPainter painter( this ); + painter.setClipRegion( e->region() ); + + if ( d_data->isDown ) + { + qDrawWinButton( &painter, 0, 0, width(), height(), + palette(), true ); + } + + painter.save(); + + if ( d_data->isDown ) + { + const QSize shiftSize = buttonShift( this ); + painter.translate( shiftSize.width(), shiftSize.height() ); + } + + painter.setClipRect( cr ); + + drawContents( &painter ); + + if ( !d_data->identifier.isNull() ) + { + QRect identRect = cr; + identRect.setX( identRect.x() + margin() ); + if ( d_data->itemMode != QwtLegend::ReadOnlyItem ) + identRect.setX( identRect.x() + ButtonFrame ); + + identRect.setSize( d_data->identifier.size() ); + identRect.moveCenter( QPoint( identRect.center().x(), cr.center().y() ) ); + + painter.drawPixmap( identRect, d_data->identifier ); + } + + painter.restore(); +} + +//! Handle mouse press events +void QwtLegendItem::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + { + switch ( d_data->itemMode ) + { + case QwtLegend::ClickableItem: + { + setDown( true ); + return; + } + case QwtLegend::CheckableItem: + { + setDown( !isDown() ); + return; + } + default:; + } + } + QwtTextLabel::mousePressEvent( e ); +} + +//! Handle mouse release events +void QwtLegendItem::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + { + switch ( d_data->itemMode ) + { + case QwtLegend::ClickableItem: + { + setDown( false ); + return; + } + case QwtLegend::CheckableItem: + { + return; // do nothing, but accept + } + default:; + } + } + QwtTextLabel::mouseReleaseEvent( e ); +} + +//! Handle key press events +void QwtLegendItem::keyPressEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Space ) + { + switch ( d_data->itemMode ) + { + case QwtLegend::ClickableItem: + { + if ( !e->isAutoRepeat() ) + setDown( true ); + return; + } + case QwtLegend::CheckableItem: + { + if ( !e->isAutoRepeat() ) + setDown( !isDown() ); + return; + } + default:; + } + } + + QwtTextLabel::keyPressEvent( e ); +} + +//! Handle key release events +void QwtLegendItem::keyReleaseEvent( QKeyEvent *e ) +{ + if ( e->key() == Qt::Key_Space ) + { + switch ( d_data->itemMode ) + { + case QwtLegend::ClickableItem: + { + if ( !e->isAutoRepeat() ) + setDown( false ); + return; + } + case QwtLegend::CheckableItem: + { + return; // do nothing, but accept + } + default:; + } + } + + QwtTextLabel::keyReleaseEvent( e ); +} diff --git a/src/libpcp_qwt/src/qwt_legend_item.h b/src/libpcp_qwt/src/qwt_legend_item.h new file mode 100644 index 0000000..1d315f6 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_legend_item.h @@ -0,0 +1,78 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_LEGEND_ITEM_H +#define QWT_LEGEND_ITEM_H + +#include "qwt_global.h" +#include "qwt_legend.h" +#include "qwt_text.h" +#include "qwt_text_label.h" +#include <qpixmap.h> + +/*! + \brief A widget representing something on a QwtLegend(). +*/ +class QWT_EXPORT QwtLegendItem: public QwtTextLabel +{ + Q_OBJECT +public: + explicit QwtLegendItem( QWidget *parent = 0 ); + virtual ~QwtLegendItem(); + + void setItemMode( QwtLegend::LegendItemMode ); + QwtLegend::LegendItemMode itemMode() const; + + void setSpacing( int spacing ); + int spacing() const; + + virtual void setText( const QwtText & ); + + void setIdentifier( const QPixmap & ); + QPixmap identifier() const; + + void setIdentifierSize( const QSize & ); + QSize identifierSize() const; + + virtual QSize sizeHint() const; + + bool isChecked() const; + +public Q_SLOTS: + void setChecked( bool on ); + +Q_SIGNALS: + //! Signal, when the legend item has been clicked + void clicked(); + + //! Signal, when the legend item has been pressed + void pressed(); + + //! Signal, when the legend item has been relased + void released(); + + //! Signal, when the legend item has been toggled + void checked( bool ); + +protected: + void setDown( bool ); + bool isDown() const; + + virtual void paintEvent( QPaintEvent * ); + virtual void mousePressEvent( QMouseEvent * ); + virtual void mouseReleaseEvent( QMouseEvent * ); + virtual void keyPressEvent( QKeyEvent * ); + virtual void keyReleaseEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_legend_itemmanager.h b/src/libpcp_qwt/src/qwt_legend_itemmanager.h new file mode 100644 index 0000000..ccea820 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_legend_itemmanager.h @@ -0,0 +1,66 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_LEGEND_ITEM_MANAGER_H +#define QWT_LEGEND_ITEM_MANAGER_H + +#include "qwt_global.h" + +class QwtLegend; +class QWidget; +class QRectF; +class QPainter; + +/*! + \brief Abstract API to bind plot items to the legend +*/ + +class QWT_EXPORT QwtLegendItemManager +{ +public: + //! Constructor + QwtLegendItemManager() + { + } + + //! Destructor + virtual ~QwtLegendItemManager() + { + } + + /*! + Update the widget that represents the item on the legend + \param legend Legend + \sa legendItem() + */ + virtual void updateLegend( QwtLegend *legend ) const = 0; + + /*! + Allocate the widget that represents the item on the legend + \return Allocated widget + \sa updateLegend() QwtLegend() + */ + + virtual QWidget *legendItem() const = 0; + + /*! + QwtLegendItem can display an icon-identifier followed + by a text. The icon helps to identify a plot item on + the plot canvas and depends on the type of information, + that is displayed. + + The default implementation paints nothing. + */ + virtual void drawLegendIdentifier( QPainter *, const QRectF & ) const + { + } +}; + +#endif + diff --git a/src/libpcp_qwt/src/qwt_magnifier.cpp b/src/libpcp_qwt/src/qwt_magnifier.cpp new file mode 100644 index 0000000..5e8ffb0 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_magnifier.cpp @@ -0,0 +1,467 @@ +/* -*- 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_magnifier.h" +#include "qwt_math.h" +#include <qevent.h> +#include <qwidget.h> + +class QwtMagnifier::PrivateData +{ +public: + PrivateData(): + isEnabled( false ), + wheelFactor( 0.9 ), + wheelButtonState( Qt::NoButton ), + mouseFactor( 0.95 ), + mouseButton( Qt::RightButton ), + mouseButtonState( Qt::NoButton ), + keyFactor( 0.9 ), + zoomInKey( Qt::Key_Plus ), + zoomOutKey( Qt::Key_Minus ), + zoomInKeyModifiers( Qt::NoModifier ), + zoomOutKeyModifiers( Qt::NoModifier ), + mousePressed( false ) + { + } + + bool isEnabled; + + double wheelFactor; + int wheelButtonState; + + double mouseFactor; + int mouseButton; + int mouseButtonState; + + double keyFactor; + int zoomInKey; + int zoomOutKey; + int zoomInKeyModifiers; + int zoomOutKeyModifiers; + + bool mousePressed; + bool hasMouseTracking; + QPoint mousePos; +}; + +/*! + Constructor + \param parent Widget to be magnified +*/ +QwtMagnifier::QwtMagnifier( QWidget *parent ): + QObject( parent ) +{ + d_data = new PrivateData(); + setEnabled( true ); +} + +//! Destructor +QwtMagnifier::~QwtMagnifier() +{ + delete d_data; +} + +/*! + \brief En/disable the magnifier + + When enabled is true an event filter is installed for + the observed widget, otherwise the event filter is removed. + + \param on true or false + \sa isEnabled(), eventFilter() +*/ +void QwtMagnifier::setEnabled( bool on ) +{ + if ( d_data->isEnabled != on ) + { + d_data->isEnabled = on; + + QObject *o = parent(); + if ( o ) + { + if ( d_data->isEnabled ) + o->installEventFilter( this ); + else + o->removeEventFilter( this ); + } + } +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled(), eventFilter() +*/ +bool QwtMagnifier::isEnabled() const +{ + return d_data->isEnabled; +} + +/*! + \brief Change the wheel factor + + The wheel factor defines the ratio between the current range + on the parent widget and the zoomed range for each step of the wheel. + The default value is 0.9. + + \param factor Wheel factor + \sa wheelFactor(), setWheelButtonState(), + setMouseFactor(), setKeyFactor() +*/ +void QwtMagnifier::setWheelFactor( double factor ) +{ + d_data->wheelFactor = factor; +} + +/*! + \return Wheel factor + \sa setWheelFactor() +*/ +double QwtMagnifier::wheelFactor() const +{ + return d_data->wheelFactor; +} + +/*! + Assign a mandatory button state for zooming in/out using the wheel. + The default button state is Qt::NoButton. + + \param buttonState Button state + \sa wheelButtonState() +*/ +void QwtMagnifier::setWheelButtonState( int buttonState ) +{ + d_data->wheelButtonState = buttonState; +} + +/*! + \return Wheel button state + \sa setWheelButtonState() +*/ +int QwtMagnifier::wheelButtonState() const +{ + return d_data->wheelButtonState; +} + +/*! + \brief Change the mouse factor + + The mouse factor defines the ratio between the current range + on the parent widget and the zoomed range for each vertical mouse movement. + The default value is 0.95. + + \param factor Wheel factor + \sa mouseFactor(), setMouseButton(), setWheelFactor(), setKeyFactor() +*/ +void QwtMagnifier::setMouseFactor( double factor ) +{ + d_data->mouseFactor = factor; +} + +/*! + \return Mouse factor + \sa setMouseFactor() +*/ +double QwtMagnifier::mouseFactor() const +{ + return d_data->mouseFactor; +} + +/*! + Assign the mouse button, that is used for zooming in/out. + The default value is Qt::RightButton. + + \param button Button + \param buttonState Button state + \sa getMouseButton() +*/ +void QwtMagnifier::setMouseButton( int button, int buttonState ) +{ + d_data->mouseButton = button; + d_data->mouseButtonState = buttonState; +} + +//! \sa setMouseButton() +void QwtMagnifier::getMouseButton( + int &button, int &buttonState ) const +{ + button = d_data->mouseButton; + buttonState = d_data->mouseButtonState; +} + +/*! + \brief Change the key factor + + The key factor defines the ratio between the current range + on the parent widget and the zoomed range for each key press of + the zoom in/out keys. The default value is 0.9. + + \param factor Key factor + \sa keyFactor(), setZoomInKey(), setZoomOutKey(), + setWheelFactor, setMouseFactor() +*/ +void QwtMagnifier::setKeyFactor( double factor ) +{ + d_data->keyFactor = factor; +} + +/*! + \return Key factor + \sa setKeyFactor() +*/ +double QwtMagnifier::keyFactor() const +{ + return d_data->keyFactor; +} + +/*! + Assign the key, that is used for zooming in. + The default combination is Qt::Key_Plus + Qt::NoModifier. + + \param key + \param modifiers + \sa getZoomInKey(), setZoomOutKey() +*/ +void QwtMagnifier::setZoomInKey( int key, int modifiers ) +{ + d_data->zoomInKey = key; + d_data->zoomInKeyModifiers = modifiers; +} + +//! \sa setZoomInKey() +void QwtMagnifier::getZoomInKey( int &key, int &modifiers ) const +{ + key = d_data->zoomInKey; + modifiers = d_data->zoomInKeyModifiers; +} + +/*! + Assign the key, that is used for zooming out. + The default combination is Qt::Key_Minus + Qt::NoModifier. + + \param key + \param modifiers + \sa getZoomOutKey(), setZoomOutKey() +*/ +void QwtMagnifier::setZoomOutKey( int key, int modifiers ) +{ + d_data->zoomOutKey = key; + d_data->zoomOutKeyModifiers = modifiers; +} + +//! \sa setZoomOutKey() +void QwtMagnifier::getZoomOutKey( int &key, int &modifiers ) const +{ + key = d_data->zoomOutKey; + modifiers = d_data->zoomOutKeyModifiers; +} + +/*! + \brief Event filter + + When isEnabled() the mouse events of the observed widget are filtered. + + \param object Object to be filtered + \param event Event + + \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent(), widgetWheelEvent(), widgetKeyPressEvent() + widgetKeyReleaseEvent() +*/ +bool QwtMagnifier::eventFilter( QObject *object, QEvent *event ) +{ + if ( object && object == parent() ) + { + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::Wheel: + { + widgetWheelEvent( ( QWheelEvent * )event ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( ( QKeyEvent * )event ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( ( QKeyEvent * )event ); + break; + } + default:; + } + } + return QObject::eventFilter( object, event ); +} + +/*! + Handle a mouse press event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMouseReleaseEvent(), widgetMouseMoveEvent() +*/ +void QwtMagnifier::widgetMousePressEvent( QMouseEvent *mouseEvent ) +{ + if ( ( mouseEvent->button() != d_data->mouseButton) + || parentWidget() == NULL ) + { + return; + } + + if ( ( mouseEvent->modifiers() & Qt::KeyboardModifierMask ) != + ( int )( d_data->mouseButtonState & Qt::KeyboardModifierMask ) ) + { + return; + } + + d_data->hasMouseTracking = parentWidget()->hasMouseTracking(); + parentWidget()->setMouseTracking( true ); + d_data->mousePos = mouseEvent->pos(); + d_data->mousePressed = true; +} + +/*! + Handle a mouse release event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseMoveEvent(), +*/ +void QwtMagnifier::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) +{ + Q_UNUSED( mouseEvent ); + + if ( d_data->mousePressed && parentWidget() ) + { + d_data->mousePressed = false; + parentWidget()->setMouseTracking( d_data->hasMouseTracking ); + } +} + +/*! + Handle a mouse move event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), +*/ +void QwtMagnifier::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) +{ + if ( !d_data->mousePressed ) + return; + + const int dy = mouseEvent->pos().y() - d_data->mousePos.y(); + if ( dy != 0 ) + { + double f = d_data->mouseFactor; + if ( dy < 0 ) + f = 1 / f; + + rescale( f ); + } + + d_data->mousePos = mouseEvent->pos(); +} + +/*! + Handle a wheel event for the observed widget. + + \param wheelEvent Wheel event + \sa eventFilter() +*/ +void QwtMagnifier::widgetWheelEvent( QWheelEvent *wheelEvent ) +{ + if ( ( wheelEvent->modifiers() & Qt::KeyboardModifierMask ) != + ( int )( d_data->wheelButtonState & Qt::KeyboardModifierMask ) ) + { + return; + } + + if ( d_data->wheelFactor != 0.0 ) + { + /* + A positive delta indicates that the wheel was + rotated forwards away from the user; a negative + value indicates that the wheel was rotated + backwards toward the user. + Most mouse types work in steps of 15 degrees, + in which case the delta value is a multiple + of 120 (== 15 * 8). + */ + double f = qPow( d_data->wheelFactor, + qAbs( wheelEvent->delta() / 120.0 ) ); + + if ( wheelEvent->delta() > 0 ) + f = 1 / f; + + rescale( f ); + } +} + +/*! + Handle a key press event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtMagnifier::widgetKeyPressEvent( QKeyEvent *keyEvent ) +{ + const int key = keyEvent->key(); + const int state = keyEvent->modifiers(); + + if ( key == d_data->zoomInKey && + state == d_data->zoomInKeyModifiers ) + { + rescale( d_data->keyFactor ); + } + else if ( key == d_data->zoomOutKey && + state == d_data->zoomOutKeyModifiers ) + { + rescale( 1.0 / d_data->keyFactor ); + } +} + +/*! + Handle a key release event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtMagnifier::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) +{ + Q_UNUSED( keyEvent ); +} + +//! \return Parent widget, where the rescaling happens +QWidget *QwtMagnifier::parentWidget() +{ + return qobject_cast<QWidget *>( parent() ); +} + +//! \return Parent widget, where the rescaling happens +const QWidget *QwtMagnifier::parentWidget() const +{ + return qobject_cast<const QWidget *>( parent() ); +} + diff --git a/src/libpcp_qwt/src/qwt_magnifier.h b/src/libpcp_qwt/src/qwt_magnifier.h new file mode 100644 index 0000000..f2f4bbd --- /dev/null +++ b/src/libpcp_qwt/src/qwt_magnifier.h @@ -0,0 +1,86 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_MAGNIFIER_H +#define QWT_MAGNIFIER_H 1 + +#include "qwt_global.h" +#include <qobject.h> + +class QWidget; +class QMouseEvent; +class QWheelEvent; +class QKeyEvent; + +/*! + \brief QwtMagnifier provides zooming, by magnifying in steps. + + Using QwtMagnifier a plot can be zoomed in/out in steps using + keys, the mouse wheel or moving a mouse button in vertical direction. +*/ +class QWT_EXPORT QwtMagnifier: public QObject +{ + Q_OBJECT + +public: + explicit QwtMagnifier( QWidget * ); + virtual ~QwtMagnifier(); + + QWidget *parentWidget(); + const QWidget *parentWidget() const; + + void setEnabled( bool ); + bool isEnabled() const; + + // mouse + void setMouseFactor( double ); + double mouseFactor() const; + + void setMouseButton( int button, int buttonState = Qt::NoButton ); + void getMouseButton( int &button, int &buttonState ) const; + + // mouse wheel + void setWheelFactor( double ); + double wheelFactor() const; + + void setWheelButtonState( int buttonState ); + int wheelButtonState() const; + + // keyboard + void setKeyFactor( double ); + double keyFactor() const; + + void setZoomInKey( int key, int modifiers ); + void getZoomInKey( int &key, int &modifiers ) const; + + void setZoomOutKey( int key, int modifiers ); + void getZoomOutKey( int &key, int &modifiers ) const; + + virtual bool eventFilter( QObject *, QEvent * ); + +protected: + /*! + Rescale the parent widget + \param factor Scale factor + */ + virtual void rescale( double factor ) = 0; + + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetWheelEvent( QWheelEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_math.cpp b/src/libpcp_qwt/src/qwt_math.cpp new file mode 100644 index 0000000..06a039a --- /dev/null +++ b/src/libpcp_qwt/src/qwt_math.cpp @@ -0,0 +1,45 @@ +/* -*- 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_math.h" + +/*! + \brief Find the smallest value in an array + \param array Pointer to an array + \param size Array size +*/ +double qwtGetMin( const double *array, int size ) +{ + if ( size <= 0 ) + return 0.0; + + double rv = array[0]; + for ( int i = 1; i < size; i++ ) + rv = qMin( rv, array[i] ); + + return rv; +} + + +/*! + \brief Find the largest value in an array + \param array Pointer to an array + \param size Array size +*/ +double qwtGetMax( const double *array, int size ) +{ + if ( size <= 0 ) + return 0.0; + + double rv = array[0]; + for ( int i = 1; i < size; i++ ) + rv = qMax( rv, array[i] ); + + return rv; +} diff --git a/src/libpcp_qwt/src/qwt_math.h b/src/libpcp_qwt/src/qwt_math.h new file mode 100644 index 0000000..fa8a476 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_math.h @@ -0,0 +1,182 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_MATH_H +#define QWT_MATH_H + +#include "qwt_global.h" + +#if defined(_MSC_VER) +/* + Microsoft says: + + Define _USE_MATH_DEFINES before including math.h to expose these macro + definitions for common math constants. These are placed under an #ifdef + since these commonly-defined names are not part of the C/C++ standards. +*/ +#define _USE_MATH_DEFINES 1 +#endif + +#include <qpoint.h> +#include <qmath.h> +#include "qwt_global.h" + +#ifndef LOG10_2 +#define LOG10_2 0.30102999566398119802 /* log10(2) */ +#endif + +#ifndef LOG10_3 +#define LOG10_3 0.47712125471966243540 /* log10(3) */ +#endif + +#ifndef LOG10_5 +#define LOG10_5 0.69897000433601885749 /* log10(5) */ +#endif + +#ifndef M_2PI +#define M_2PI 6.28318530717958623200 /* 2 pi */ +#endif + +#ifndef LOG_MIN +//! Mininum value for logarithmic scales +#define LOG_MIN 1.0e-100 +#endif + +#ifndef LOG_MAX +//! Maximum value for logarithmic scales +#define LOG_MAX 1.0e100 +#endif + +#ifndef M_E +#define M_E 2.7182818284590452354 /* e */ +#endif + +#ifndef M_LOG2E +#define M_LOG2E 1.4426950408889634074 /* log_2 e */ +#endif + +#ifndef M_LOG10E +#define M_LOG10E 0.43429448190325182765 /* log_10 e */ +#endif + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 /* log_e 2 */ +#endif + +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 /* log_e 10 */ +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif + +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 /* pi/2 */ +#endif + +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 /* pi/4 */ +#endif + +#ifndef M_1_PI +#define M_1_PI 0.31830988618379067154 /* 1/pi */ +#endif + +#ifndef M_2_PI +#define M_2_PI 0.63661977236758134308 /* 2/pi */ +#endif + +#ifndef M_2_SQRTPI +#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +#endif + +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#endif + +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#endif + +QWT_EXPORT double qwtGetMin( const double *array, int size ); +QWT_EXPORT double qwtGetMax( const double *array, int size ); + +/*! + \brief Compare 2 values, relative to an interval + + Values are "equal", when : + \f$\cdot value2 - value1 <= abs(intervalSize * 10e^{-6})\f$ + + \param value1 First value to compare + \param value2 Second value to compare + \param intervalSize interval size + + \return 0: if equal, -1: if value2 > value1, 1: if value1 > value2 +*/ +inline int qwtFuzzyCompare( double value1, double value2, double intervalSize ) +{ + const double eps = qAbs( 1.0e-6 * intervalSize ); + + if ( value2 - value1 > eps ) + return -1; + + if ( value1 - value2 > eps ) + return 1; + + return 0; +} + + +inline bool qwtFuzzyGreaterOrEqual( double d1, double d2 ) +{ + return ( d1 >= d2 ) || qFuzzyCompare( d1, d2 ); +} + +inline bool qwtFuzzyLessOrEqual( double d1, double d2 ) +{ + return ( d1 <= d2 ) || qFuzzyCompare( d1, d2 ); +} + +//! Return the sign +inline int qwtSign( double x ) +{ + if ( x > 0.0 ) + return 1; + else if ( x < 0.0 ) + return ( -1 ); + else + return 0; +} + +//! Return the square of a number +inline double qwtSqr( double x ) +{ + return x * x; +} + +//! Like qRound, but without converting the result to an int +inline double qwtRoundF(double d) +{ + return ::floor( d + 0.5 ); +} + +//! Like qFloor, but without converting the result to an int +inline double qwtFloorF(double d) +{ + return ::floor( d ); +} + +//! Like qCeil, but without converting the result to an int +inline double qwtCeilF(double d) +{ + return ::ceil( d ); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_matrix_raster_data.cpp b/src/libpcp_qwt/src/qwt_matrix_raster_data.cpp new file mode 100644 index 0000000..2176ec9 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_matrix_raster_data.cpp @@ -0,0 +1,270 @@ +/* -*- 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_matrix_raster_data.h" +#include <qnumeric.h> +#include <qmath.h> + +class QwtMatrixRasterData::PrivateData +{ +public: + PrivateData(): + resampleMode(QwtMatrixRasterData::NearestNeighbour), + numColumns(0) + { + } + + inline double value(size_t row, size_t col) const + { + return values.data()[ row * numColumns + col ]; + } + + QwtMatrixRasterData::ResampleMode resampleMode; + + QVector<double> values; + size_t numColumns; + size_t numRows; + + double dx; + double dy; +}; + +//! Constructor +QwtMatrixRasterData::QwtMatrixRasterData() +{ + d_data = new PrivateData(); + update(); +} + +//! Destructor +QwtMatrixRasterData::~QwtMatrixRasterData() +{ + delete d_data; +} + +/*! + \brief Set the resampling algorithm + + \param mode Resampling mode + \sa resampleMode(), value() +*/ +void QwtMatrixRasterData::setResampleMode(ResampleMode mode) +{ + d_data->resampleMode = mode; +} + +/*! + \return resampling algorithm + \sa setResampleMode(), value() +*/ +QwtMatrixRasterData::ResampleMode QwtMatrixRasterData::resampleMode() const +{ + return d_data->resampleMode; +} + +/*! + \brief Assign the bounding interval for an axis + + Setting the bounding intervals for the X/Y axis is mandatory + to define the positions for the values of the value matrix. + The interval in Z direction defines the possible range for + the values in the matrix, what is f.e used by QwtPlotSpectrogram + to map values to colors. The Z-interval might be the bounding + interval of the values in the matrix, but usually it isn't. + ( f.e a interval of 0.0-100.0 for values in percentage ) + + \param axis X, Y or Z axis + \param interval Interval + + \sa QwtRasterData::interval(), setValueMatrix() +*/ +void QwtMatrixRasterData::setInterval( + Qt::Axis axis, const QwtInterval &interval ) +{ + QwtRasterData::setInterval( axis, interval ); + update(); +} + +/*! + \brief Assign a value matrix + + The positions of the values are calculated by dividing + the bounding rectangle of the X/Y intervals into equidistant + rectangles ( pixels ). Each value corresponds to the center of + a pixel. + + \param values Vector of values + \param numColumns Number of columns + + \sa valueMatrix(), numColumns(), numRows(), setInterval()() +*/ +void QwtMatrixRasterData::setValueMatrix( + const QVector<double> &values, size_t numColumns ) +{ + d_data->values = values; + d_data->numColumns = numColumns; + update(); +} + +/*! + \return Value matrix + \sa setValueMatrix(), numColumns(), numRows(), setInterval() +*/ +const QVector<double> QwtMatrixRasterData::valueMatrix() const +{ + return d_data->values; +} + +/*! + \return Number of columns of the value matrix + \sa valueMatrix(), numRows(), setValueMatrix() +*/ +size_t QwtMatrixRasterData::numColumns() const +{ + return d_data->numColumns; +} + +/*! + \return Number of rows of the value matrix + \sa valueMatrix(), numColumns(), setValueMatrix() +*/ +size_t QwtMatrixRasterData::numRows() const +{ + return d_data->numRows; +} + +/*! + \brief Pixel hint + + - NearestNeighbour\n + pixelHint() returns the surrounding pixel of the top left value + in the matrix. + + - BilinearInterpolation\n + Returns an empty rectangle recommending + to render in target device ( f.e. screen ) resolution. + + \sa ResampleMode, setMatrix(), setInterval() +*/ +QRectF QwtMatrixRasterData::pixelHint( const QRectF & ) const +{ + QRectF rect; + if ( d_data->resampleMode == NearestNeighbour ) + { + const QwtInterval intervalX = interval( Qt::XAxis ); + const QwtInterval intervalY = interval( Qt::YAxis ); + if ( intervalX.isValid() && intervalY.isValid() ) + { + rect = QRectF( intervalX.minValue(), intervalY.minValue(), + d_data->dx, d_data->dy ); + } + } + + return rect; +} + +/*! + \return the value at a raster position + + \param x X value in plot coordinates + \param y Y value in plot coordinates + + \sa ResampleMode +*/ +double QwtMatrixRasterData::value( double x, double y ) const +{ + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + + if ( !( xInterval.contains(x) && yInterval.contains(y) ) ) + return qQNaN(); + + double value; + + switch( d_data->resampleMode ) + { + case BilinearInterpolation: + { + int col1 = qRound( (x - xInterval.minValue() ) / d_data->dx ) - 1; + int row1 = qRound( (y - yInterval.minValue() ) / d_data->dy ) - 1; + int col2 = col1 + 1; + int row2 = row1 + 1; + + if ( col1 < 0 ) + col1 = col2; + else if ( col2 >= (int)d_data->numColumns ) + col2 = col1; + + if ( row1 < 0 ) + row1 = row2; + else if ( row2 >= (int)d_data->numRows ) + row2 = row1; + + const double v11 = d_data->value( row1, col1 ); + const double v21 = d_data->value( row1, col2 ); + const double v12 = d_data->value( row2, col1 ); + const double v22 = d_data->value( row2, col2 ); + + const double x2 = xInterval.minValue() + + ( col2 + 0.5 ) * d_data->dx; + const double y2 = yInterval.minValue() + + ( row2 + 0.5 ) * d_data->dy; + + const double rx = ( x2 - x ) / d_data->dx; + const double ry = ( y2 - y ) / d_data->dy; + + const double vr1 = rx * v11 + ( 1.0 - rx ) * v21; + const double vr2 = rx * v12 + ( 1.0 - rx ) * v22; + + value = ry * vr1 + ( 1.0 - ry ) * vr2; + + break; + } + case NearestNeighbour: + default: + { + uint row = uint( (y - yInterval.minValue() ) / d_data->dy ); + uint col = uint( (x - xInterval.minValue() ) / d_data->dx ); + + // In case of intervals, where the maximum is included + // we get out of bound for row/col, when the value for the + // maximum is requested. Instead we return the value + // from the last row/col + + if ( row >= d_data->numRows ) + row = d_data->numRows - 1; + + if ( col >= d_data->numColumns ) + col = d_data->numColumns - 1; + + value = d_data->value( row, col ); + } + } + + return value; +} + +void QwtMatrixRasterData::update() +{ + d_data->numRows = 0; + d_data->dx = 0.0; + d_data->dy = 0.0; + + if ( d_data->numColumns > 0 ) + { + d_data->numRows = d_data->values.size() / d_data->numColumns; + + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + if ( xInterval.isValid() ) + d_data->dx = xInterval.width() / d_data->numColumns; + if ( yInterval.isValid() ) + d_data->dy = yInterval.width() / d_data->numRows; + } +} diff --git a/src/libpcp_qwt/src/qwt_matrix_raster_data.h b/src/libpcp_qwt/src/qwt_matrix_raster_data.h new file mode 100644 index 0000000..a8940f8 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_matrix_raster_data.h @@ -0,0 +1,71 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_MATRIX_RASTER_DATA_H +#define QWT_MATRIX_RASTER_DATA_H 1 + +#include "qwt_global.h" +#include "qwt_raster_data.h" +#include <qvector.h> + +/*! + \brief A class representing a matrix of values as raster data + + QwtMatrixRasterData implements an interface for a matrix of + equidistant values, that can be used by a QwtPlotRasterItem. + It implements a couple of resampling algorithms, to provide + values for positions, that or not on the value matrix. +*/ +class QWT_EXPORT QwtMatrixRasterData: public QwtRasterData +{ +public: + /*! + \brief Resampling algorithm + The default setting is NearestNeighbour; + */ + enum ResampleMode + { + /*! + Return the value from the matrix, that is nearest to the + the requested position. + */ + NearestNeighbour, + + /*! + Interpolate the value from the distances and values of the + 4 surrounding values in the matrix, + */ + BilinearInterpolation + }; + + QwtMatrixRasterData(); + virtual ~QwtMatrixRasterData(); + + void setResampleMode(ResampleMode mode); + ResampleMode resampleMode() const; + + virtual void setInterval( Qt::Axis, const QwtInterval & ); + void setValueMatrix( const QVector<double> &values, size_t numColumns ); + + const QVector<double> valueMatrix() const; + size_t numColumns() const; + size_t numRows() const; + + virtual QRectF pixelHint( const QRectF & ) const; + + virtual double value( double x, double y ) const; + +private: + void update(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_null_paintdevice.cpp b/src/libpcp_qwt/src/qwt_null_paintdevice.cpp new file mode 100644 index 0000000..cdab2a6 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_null_paintdevice.cpp @@ -0,0 +1,428 @@ +/* -*- 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_null_paintdevice.h" +#include <qpaintengine.h> +#include <qpixmap.h> + +class QwtNullPaintDevice::PrivateData +{ +public: + PrivateData(): + size( 0, 0 ) + { + } + + QSize size; +}; + +class QwtNullPaintDevice::PaintEngine: public QPaintEngine +{ +public: + PaintEngine( QPaintEngine::PaintEngineFeatures ); + + virtual bool begin( QPaintDevice * ); + virtual bool end(); + + virtual Type type () const; + virtual void updateState(const QPaintEngineState &); + + virtual void drawRects(const QRect *, int ); + virtual void drawRects(const QRectF *, int ); + + virtual void drawLines(const QLine *, int ); + virtual void drawLines(const QLineF *, int ); + + virtual void drawEllipse(const QRectF &); + virtual void drawEllipse(const QRect &); + + virtual void drawPath(const QPainterPath &); + + virtual void drawPoints(const QPointF *, int ); + virtual void drawPoints(const QPoint *, int ); + + virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ); + virtual void drawPolygon(const QPoint *, int , PolygonDrawMode ); + + virtual void drawPixmap(const QRectF &, + const QPixmap &, const QRectF &); + + virtual void drawTextItem(const QPointF &, const QTextItem &); + virtual void drawTiledPixmap(const QRectF &, + const QPixmap &, const QPointF &s); + virtual void drawImage(const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + +private: + QwtNullPaintDevice *d_device; +}; + +QwtNullPaintDevice::PaintEngine::PaintEngine( + QPaintEngine::PaintEngineFeatures features ): + QPaintEngine( features ), + d_device(NULL) +{ +} + +bool QwtNullPaintDevice::PaintEngine::begin( + QPaintDevice *device ) +{ + d_device = static_cast<QwtNullPaintDevice *>( device ); + return true; +} + +bool QwtNullPaintDevice::PaintEngine::end() +{ + d_device = NULL; + return true; +} + +QPaintEngine::Type +QwtNullPaintDevice::PaintEngine::type () const +{ + return QPaintEngine::User; +} + +void QwtNullPaintDevice::PaintEngine::drawRects( + const QRect *rects, int rectCount) +{ + if ( d_device ) + d_device->drawRects( rects, rectCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawRects( + const QRectF *rects, int rectCount) +{ + if ( d_device ) + d_device->drawRects( rects, rectCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawLines( + const QLine *lines, int lineCount) +{ + if ( d_device ) + d_device->drawLines( lines, lineCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawLines( + const QLineF *lines, int lineCount) +{ + if ( d_device ) + d_device->drawLines( lines, lineCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawEllipse( + const QRectF &rect) +{ + if ( d_device ) + d_device->drawEllipse( rect ); +} + +void QwtNullPaintDevice::PaintEngine::drawEllipse( + const QRect &rect) +{ + if ( d_device ) + d_device->drawEllipse( rect ); +} + + +void QwtNullPaintDevice::PaintEngine::drawPath( + const QPainterPath &path) +{ + if ( d_device ) + d_device->drawPath( path ); +} + +void QwtNullPaintDevice::PaintEngine::drawPoints( + const QPointF *points, int pointCount) +{ + if ( d_device ) + d_device->drawPoints( points, pointCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawPoints( + const QPoint *points, int pointCount) +{ + if ( d_device ) + d_device->drawPoints( points, pointCount ); +} + +void QwtNullPaintDevice::PaintEngine::drawPolygon( + const QPointF *points, int pointCount, PolygonDrawMode mode) +{ + if ( d_device ) + d_device->drawPolygon( points, pointCount, mode ); +} + +void QwtNullPaintDevice::PaintEngine::drawPolygon( + const QPoint *points, int pointCount, PolygonDrawMode mode) +{ + if ( d_device ) + d_device->drawPolygon( points, pointCount, mode ); +} + +void QwtNullPaintDevice::PaintEngine::drawPixmap( + const QRectF &rect, const QPixmap &pm, const QRectF &subRect ) +{ + if ( d_device ) + d_device->drawPixmap( rect, pm, subRect ); +} + +void QwtNullPaintDevice::PaintEngine::drawTextItem( + const QPointF &pos, const QTextItem &textItem) +{ + if ( d_device ) + d_device->drawTextItem( pos, textItem ); +} + +void QwtNullPaintDevice::PaintEngine::drawTiledPixmap( + const QRectF &rect, const QPixmap &pixmap, + const QPointF &subRect) +{ + if ( d_device ) + d_device->drawTiledPixmap( rect, pixmap, subRect ); +} + +void QwtNullPaintDevice::PaintEngine::drawImage( + const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + if ( d_device ) + d_device->drawImage( rect, image, subRect, flags ); +} + +void QwtNullPaintDevice::PaintEngine::updateState( + const QPaintEngineState &state) +{ + if ( d_device ) + d_device->updateState( state ); +} + +//! Constructor +QwtNullPaintDevice::QwtNullPaintDevice( + QPaintEngine::PaintEngineFeatures features ) +{ + init( features ); +} + +//! Constructor +QwtNullPaintDevice::QwtNullPaintDevice( const QSize &size, + QPaintEngine::PaintEngineFeatures features ) +{ + init( features ); + d_data->size = size; +} + +void QwtNullPaintDevice::init( + QPaintEngine::PaintEngineFeatures features ) +{ + d_engine = new PaintEngine( features ); + d_data = new PrivateData; +} + +//! Destructor +QwtNullPaintDevice::~QwtNullPaintDevice() +{ + delete d_engine; + delete d_data; +} + +/*! + Set the size of the paint device + + \param size Size + \sa size() +*/ +void QwtNullPaintDevice::setSize( const QSize & size ) +{ + d_data->size = size; +} + +/*! + \return Size of the paint device + \sa setSize() +*/ +QSize QwtNullPaintDevice::size() const +{ + return d_data->size; +} + +//! See QPaintDevice::paintEngine() +QPaintEngine *QwtNullPaintDevice::paintEngine() const +{ + return d_engine; +} + +/*! + See QPaintDevice::metric() + \sa setSize() +*/ +int QwtNullPaintDevice::metric( PaintDeviceMetric metric ) const +{ + static QPixmap pm; + + int value; + + switch ( metric ) + { + case PdmWidth: + value = qMax( d_data->size.width(), 0 ); + break; + case PdmHeight: + value = qMax( d_data->size.height(), 0 ); + break; + case PdmNumColors: + value = 16777216; + break; + case PdmDepth: + value = 24; + break; + case PdmPhysicalDpiX: + case PdmDpiY: + case PdmPhysicalDpiY: + case PdmWidthMM: + case PdmHeightMM: + case PdmDpiX: + default: + value = 0; + } + return value; + +} + +//! See QPaintEngine::drawRects() +void QwtNullPaintDevice::drawRects( + const QRect *rects, int rectCount) +{ + Q_UNUSED(rects); + Q_UNUSED(rectCount); +} + +//! See QPaintEngine::drawRects() +void QwtNullPaintDevice::drawRects( + const QRectF *rects, int rectCount) +{ + Q_UNUSED(rects); + Q_UNUSED(rectCount); +} + +//! See QPaintEngine::drawLines() +void QwtNullPaintDevice::drawLines( + const QLine *lines, int lineCount) +{ + Q_UNUSED(lines); + Q_UNUSED(lineCount); +} + +//! See QPaintEngine::drawLines() +void QwtNullPaintDevice::drawLines( + const QLineF *lines, int lineCount) +{ + Q_UNUSED(lines); + Q_UNUSED(lineCount); +} + +//! See QPaintEngine::drawEllipse() +void QwtNullPaintDevice::drawEllipse( const QRectF &rect ) +{ + Q_UNUSED(rect); +} + +//! See QPaintEngine::drawEllipse() +void QwtNullPaintDevice::drawEllipse( const QRect &rect ) +{ + Q_UNUSED(rect); +} + +//! See QPaintEngine::drawPath() +void QwtNullPaintDevice::drawPath( const QPainterPath &path ) +{ + Q_UNUSED(path); +} + +//! See QPaintEngine::drawPoints() +void QwtNullPaintDevice::drawPoints( + const QPointF *points, int pointCount) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); +} + +//! See QPaintEngine::drawPoints() +void QwtNullPaintDevice::drawPoints( + const QPoint *points, int pointCount) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); +} + +//! See QPaintEngine::drawPolygon() +void QwtNullPaintDevice::drawPolygon( + const QPointF *points, int pointCount, + QPaintEngine::PolygonDrawMode mode) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); + Q_UNUSED(mode); +} + +//! See QPaintEngine::drawPolygon() +void QwtNullPaintDevice::drawPolygon( + const QPoint *points, int pointCount, + QPaintEngine::PolygonDrawMode mode) +{ + Q_UNUSED(points); + Q_UNUSED(pointCount); + Q_UNUSED(mode); +} + +//! See QPaintEngine::drawPixmap() +void QwtNullPaintDevice::drawPixmap( const QRectF &rect, + const QPixmap &pm, const QRectF &subRect ) +{ + Q_UNUSED(rect); + Q_UNUSED(pm); + Q_UNUSED(subRect); +} + +//! See QPaintEngine::drawTextItem() +void QwtNullPaintDevice::drawTextItem( + const QPointF &pos, const QTextItem &textItem) +{ + Q_UNUSED(pos); + Q_UNUSED(textItem); +} + +//! See QPaintEngine::drawTiledPixmap() +void QwtNullPaintDevice::drawTiledPixmap( + const QRectF &rect, const QPixmap &pixmap, + const QPointF &subRect) +{ + Q_UNUSED(rect); + Q_UNUSED(pixmap); + Q_UNUSED(subRect); +} + +//! See QPaintEngine::drawImage() +void QwtNullPaintDevice::drawImage( + const QRectF &rect, const QImage &image, + const QRectF &subRect, Qt::ImageConversionFlags flags) +{ + Q_UNUSED(rect); + Q_UNUSED(image); + Q_UNUSED(subRect); + Q_UNUSED(flags); +} + +//! See QPaintEngine::updateState() +void QwtNullPaintDevice::updateState( + const QPaintEngineState &state ) +{ + Q_UNUSED(state); +} diff --git a/src/libpcp_qwt/src/qwt_null_paintdevice.h b/src/libpcp_qwt/src/qwt_null_paintdevice.h new file mode 100644 index 0000000..aae9c2d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_null_paintdevice.h @@ -0,0 +1,89 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_NULL_PAINT_DEVICE_H +#define QWT_NULL_PAINT_DEVICE_H 1 + +#include "qwt_global.h" +#include <qpaintdevice.h> +#include <qpaintengine.h> + +/*! + \brief A null paint device doing nothing + + Sometimes important layout/rendering geometries are not + available or changable from the public Qt class interface. + ( f.e hidden in the style implementation ). + + QwtNullPaintDevice can be used to manipulate or filter out + these informations by analyzing the stream of paint primitives. + + F.e. QwtNullPaintDevice is used by QwtPlotCanvas to identify + styled backgrounds with rounded corners. +*/ + +class QWT_EXPORT QwtNullPaintDevice: public QPaintDevice +{ +public: + QwtNullPaintDevice( QPaintEngine::PaintEngineFeatures ); + QwtNullPaintDevice( const QSize &size, + QPaintEngine::PaintEngineFeatures ); + + virtual ~QwtNullPaintDevice(); + + void setSize( const QSize &); + QSize size() const; + + virtual QPaintEngine *paintEngine() const; + virtual int metric( PaintDeviceMetric metric ) const; + + virtual void drawRects(const QRect *, int ); + virtual void drawRects(const QRectF *, int ); + + virtual void drawLines(const QLine *, int ); + virtual void drawLines(const QLineF *, int ); + + virtual void drawEllipse(const QRectF &); + virtual void drawEllipse(const QRect &); + + virtual void drawPath(const QPainterPath &); + + virtual void drawPoints(const QPointF *, int ); + virtual void drawPoints(const QPoint *, int ); + + virtual void drawPolygon( + const QPointF *, int , QPaintEngine::PolygonDrawMode ); + + virtual void drawPolygon( + const QPoint *, int , QPaintEngine::PolygonDrawMode ); + + virtual void drawPixmap(const QRectF &, + const QPixmap &, const QRectF &); + + virtual void drawTextItem(const QPointF &, const QTextItem &); + + virtual void drawTiledPixmap(const QRectF &, + const QPixmap &, const QPointF &s); + + virtual void drawImage(const QRectF &, + const QImage &, const QRectF &, Qt::ImageConversionFlags ); + + virtual void updateState( const QPaintEngineState &state ); + +private: + void init( QPaintEngine::PaintEngineFeatures ); + + class PaintEngine; + PaintEngine *d_engine; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_painter.cpp b/src/libpcp_qwt/src/qwt_painter.cpp new file mode 100644 index 0000000..adc0859 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_painter.cpp @@ -0,0 +1,765 @@ +/* -*- 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_math.h" +#include "qwt_clipper.h" +#include "qwt_color_map.h" +#include "qwt_scale_map.h" +#include <qwindowdefs.h> +#include <qwidget.h> +#include <qframe.h> +#include <qrect.h> +#include <qpainter.h> +#include <qpalette.h> +#include <qpaintdevice.h> +#include <qpixmap.h> +#include <qstyle.h> +#include <qtextdocument.h> +#include <qabstracttextdocumentlayout.h> +#include <qstyleoption.h> +#include <qpaintengine.h> +#include <qapplication.h> +#include <qdesktopwidget.h> + +bool QwtPainter::d_polylineSplitting = true; +bool QwtPainter::d_roundingAlignment = true; + +static inline bool isClippingNeeded( const QPainter *painter, QRectF &clipRect ) +{ + bool doClipping = false; + const QPaintEngine *pe = painter->paintEngine(); + if ( pe && pe->type() == QPaintEngine::SVG ) + { + // The SVG paint engine ignores any clipping, + + if ( painter->hasClipping() ) + { + doClipping = true; + clipRect = painter->clipRegion().boundingRect(); + } + } + + return doClipping; +} + +static inline void drawPolyline( QPainter *painter, + const QPointF *points, int pointCount, bool polylineSplitting ) +{ + bool doSplit = false; + if ( polylineSplitting ) + { + const QPaintEngine *pe = painter->paintEngine(); + if ( pe && pe->type() == QPaintEngine::Raster ) + { + /* + The raster paint engine seems to use some algo with O(n*n). + ( Qt 4.3 is better than Qt 4.2, but remains unacceptable) + To work around this problem, we have to split the polygon into + smaller pieces. + */ + doSplit = true; + } + } + + if ( doSplit ) + { + const int splitSize = 20; + for ( int i = 0; i < pointCount; i += splitSize ) + { + const int n = qMin( splitSize + 1, pointCount - i ); + painter->drawPolyline( points + i, n ); + } + } + else + painter->drawPolyline( points, pointCount ); +} + +static inline void unscaleFont( QPainter *painter ) +{ + if ( painter->font().pixelSize() >= 0 ) + return; + + static QSize screenResolution; + if ( !screenResolution.isValid() ) + { + QDesktopWidget *desktop = QApplication::desktop(); + if ( desktop ) + { + screenResolution.setWidth( desktop->logicalDpiX() ); + screenResolution.setHeight( desktop->logicalDpiY() ); + } + } + + const QPaintDevice *pd = painter->device(); + if ( pd->logicalDpiX() != screenResolution.width() || + pd->logicalDpiY() != screenResolution.height() ) + { + QFont pixelFont( painter->font(), QApplication::desktop() ); + pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() ); + + painter->setFont( pixelFont ); + } +} + +/*! + Check if the painter is using a paint engine, that aligns + coordinates to integers. Today these are all paint engines + beside QPaintEngine::Pdf and QPaintEngine::SVG. + + \param painter Painter + \return true, when the paint engine is aligning + + \sa setRoundingAlignment() +*/ +bool QwtPainter::isAligning( QPainter *painter ) +{ + if ( painter && painter->isActive() ) + { + switch ( painter->paintEngine()->type() ) + { + case QPaintEngine::Pdf: + case QPaintEngine::SVG: + return false; + + default:; + } + } + + return true; +} + +/*! + Enable whether coordinates should be rounded, before they are painted + to a paint engine that floors to integer values. For other paint engines + this ( Pdf, SVG ), this flag has no effect. + QwtPainter stores this flag only, the rounding itsself is done in + the painting code ( f.e the plot items ). + + The default setting is true. + + \sa roundingAlignment(), isAligning() +*/ +void QwtPainter::setRoundingAlignment( bool enable ) +{ + d_roundingAlignment = enable; +} + +/*! + \brief En/Disable line splitting for the raster paint engine + + The raster paint engine paints polylines of many points + much faster when they are splitted in smaller chunks. + + \sa polylineSplitting() +*/ +void QwtPainter::setPolylineSplitting( bool enable ) +{ + d_polylineSplitting = enable; +} + +//! Wrapper for QPainter::drawPath() +void QwtPainter::drawPath( QPainter *painter, const QPainterPath &path ) +{ + painter->drawPath( path ); +} + +//! Wrapper for QPainter::drawRect() +void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h ) +{ + drawRect( painter, QRectF( x, y, w, h ) ); +} + +//! Wrapper for QPainter::drawRect() +void QwtPainter::drawRect( QPainter *painter, const QRectF &rect ) +{ + const QRectF r = rect; + + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + if ( !clipRect.intersects( r ) ) + return; + + if ( !clipRect.contains( r ) ) + { + fillRect( painter, r & clipRect, painter->brush() ); + + painter->save(); + painter->setBrush( Qt::NoBrush ); + drawPolyline( painter, QPolygonF( r ) ); + painter->restore(); + + return; + } + } + + painter->drawRect( r ); +} + +//! Wrapper for QPainter::fillRect() +void QwtPainter::fillRect( QPainter *painter, + const QRectF &rect, const QBrush &brush ) +{ + if ( !rect.isValid() ) + return; + + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + /* + Performance of Qt4 is horrible for non trivial brushs. Without + clipping expect minutes or hours for repainting large rects + (might result from zooming) + */ + + if ( deviceClipping ) + clipRect &= painter->window(); + else + clipRect = painter->window(); + + if ( painter->hasClipping() ) + clipRect &= painter->clipRegion().boundingRect(); + + QRectF r = rect; + if ( deviceClipping ) + r = r.intersect( clipRect ); + + if ( r.isValid() ) + painter->fillRect( r, brush ); +} + +//! Wrapper for QPainter::drawPie() +void QwtPainter::drawPie( QPainter *painter, const QRectF &rect, + int a, int alen ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + if ( deviceClipping && !clipRect.contains( rect ) ) + return; + + painter->drawPie( rect, a, alen ); +} + +//! Wrapper for QPainter::drawEllipse() +void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + if ( deviceClipping && !clipRect.contains( rect ) ) + return; + + painter->drawEllipse( rect ); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, double x, double y, + const QString &text ) +{ + drawText( painter, QPointF( x, y ), text ); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, const QPointF &pos, + const QString &text ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + if ( deviceClipping && !clipRect.contains( pos ) ) + return; + + + painter->save(); + unscaleFont( painter ); + painter->drawText( pos, text ); + painter->restore(); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, + double x, double y, double w, double h, + int flags, const QString &text ) +{ + drawText( painter, QRectF( x, y, w, h ), flags, text ); +} + +//! Wrapper for QPainter::drawText() +void QwtPainter::drawText( QPainter *painter, const QRectF &rect, + int flags, const QString &text ) +{ + painter->save(); + unscaleFont( painter ); + painter->drawText( rect, flags, text ); + painter->restore(); +} + +#ifndef QT_NO_RICHTEXT + +/*! + Draw a text document into a rectangle + + \param painter Painter + \param rect Traget rectangle + \param flags Alignments/Text flags, see QPainter::drawText() + \param text Text document +*/ +void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect, + int flags, const QTextDocument &text ) +{ + QTextDocument *txt = text.clone(); + + painter->save(); + + painter->setFont( txt->defaultFont() ); + unscaleFont( painter ); + + txt->setDefaultFont( painter->font() ); + txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) ); + + QAbstractTextDocumentLayout* layout = txt->documentLayout(); + + const double height = layout->documentSize().height(); + double y = rect.y(); + if ( flags & Qt::AlignBottom ) + y += ( rect.height() - height ); + else if ( flags & Qt::AlignVCenter ) + y += ( rect.height() - height ) / 2; + + QAbstractTextDocumentLayout::PaintContext context; + context.palette.setColor( QPalette::Text, painter->pen().color() ); + + painter->translate( rect.x(), y ); + layout->draw( painter, context ); + + painter->restore(); + delete txt; +} + +#endif // !QT_NO_RICHTEXT + + +//! Wrapper for QPainter::drawLine() +void QwtPainter::drawLine( QPainter *painter, + const QPointF &p1, const QPointF &p2 ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + if ( deviceClipping && + !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) ) + { + QPolygonF polygon; + polygon += p1; + polygon += p2; + drawPolyline( painter, polygon ); + return; + } + + painter->drawLine( p1, p2 ); +} + +//! Wrapper for QPainter::drawPolygon() +void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + QPolygonF cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygonF( clipRect, polygon ); + + painter->drawPolygon( cpa ); +} + +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + QPolygonF cpa = polygon; + if ( deviceClipping ) + cpa = QwtClipper::clipPolygonF( clipRect, cpa ); + + ::drawPolyline( painter, + cpa.constData(), cpa.size(), d_polylineSplitting ); +} + +//! Wrapper for QPainter::drawPolyline() +void QwtPainter::drawPolyline( QPainter *painter, + const QPointF *points, int pointCount ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + if ( deviceClipping ) + { + QPolygonF polygon( pointCount ); + qMemCopy( polygon.data(), points, pointCount * sizeof( QPointF ) ); + + polygon = QwtClipper::clipPolygonF( clipRect, polygon ); + ::drawPolyline( painter, + polygon.constData(), polygon.size(), d_polylineSplitting ); + } + else + ::drawPolyline( painter, points, pointCount, d_polylineSplitting ); +} + +//! Wrapper for QPainter::drawPoint() +void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos ) +{ + QRectF clipRect; + const bool deviceClipping = isClippingNeeded( painter, clipRect ); + + if ( deviceClipping && !clipRect.contains( pos ) ) + return; + + painter->drawPoint( pos ); +} + +//! Wrapper for QPainter::drawImage() +void QwtPainter::drawImage( QPainter *painter, + const QRectF &rect, const QImage &image ) +{ + const QRect alignedRect = rect.toAlignedRect(); + + if ( alignedRect != rect ) + { + const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + painter->save(); + painter->setClipRect( clipRect, Qt::IntersectClip ); + painter->drawImage( alignedRect, image ); + painter->restore(); + } + else + { + painter->drawImage( alignedRect, image ); + } +} + +//! Wrapper for QPainter::drawPixmap() +void QwtPainter::drawPixmap( QPainter *painter, + const QRectF &rect, const QPixmap &pixmap ) +{ + const QRect alignedRect = rect.toAlignedRect(); + + if ( alignedRect != rect ) + { + const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + painter->save(); + painter->setClipRect( clipRect, Qt::IntersectClip ); + painter->drawPixmap( alignedRect, pixmap ); + painter->restore(); + } + else + { + painter->drawPixmap( alignedRect, pixmap ); + } +} + +//! Draw a focus rectangle on a widget using its style. +void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget ) +{ + drawFocusRect( painter, widget, widget->rect() ); +} + +//! Draw a focus rectangle on a widget using its style. +void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget, + const QRect &rect ) +{ + QStyleOptionFocusRect opt; + opt.init( widget ); + opt.rect = rect; + opt.state |= QStyle::State_HasFocus; + + widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect, + &opt, painter, widget ); +} + +/*! + Draw a frame with rounded borders + + \param painter Painter + \param rect Frame rectangle + \param xRadius x-radius of the ellipses defining the corners + \param yRadius y-radius of the ellipses defining the corners + \param palette QPalette::WindowText is used for plain borders + QPalette::Dark and QPalette::Light for raised + or sunken borders + \param lineWidth Line width + \param frameStyle bitwise OR´ed value of QFrame::Shape and QFrame::Shadow +*/ + +void QwtPainter::drawRoundedFrame( QPainter *painter, + const QRectF &rect, double xRadius, double yRadius, + const QPalette &palette, int lineWidth, int frameStyle ) +{ + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->setBrush( Qt::NoBrush ); + + double lw2 = lineWidth * 0.5; + QRectF r = rect.adjusted( lw2, lw2, -lw2, -lw2 ); + + QPainterPath path; + path.addRoundedRect( r, xRadius, yRadius ); + + enum Style + { + Plain, + Sunken, + Raised + }; + + Style style = Plain; + if ( (frameStyle & QFrame::Sunken) == QFrame::Sunken ) + style = Sunken; + else if ( (frameStyle & QFrame::Raised) == QFrame::Raised ) + style = Raised; + + if ( style != Plain && path.elementCount() == 17 ) + { + // move + 4 * ( cubicTo + lineTo ) + QPainterPath pathList[8]; + + for ( int i = 0; i < 4; i++ ) + { + const int j = i * 4 + 1; + + pathList[ 2 * i ].moveTo( + path.elementAt(j - 1).x, path.elementAt( j - 1 ).y + ); + + pathList[ 2 * i ].cubicTo( + path.elementAt(j + 0).x, path.elementAt(j + 0).y, + path.elementAt(j + 1).x, path.elementAt(j + 1).y, + path.elementAt(j + 2).x, path.elementAt(j + 2).y ); + + pathList[ 2 * i + 1 ].moveTo( + path.elementAt(j + 2).x, path.elementAt(j + 2).y + ); + pathList[ 2 * i + 1 ].lineTo( + path.elementAt(j + 3).x, path.elementAt(j + 3).y + ); + } + + QColor c1( palette.color( QPalette::Dark ) ); + QColor c2( palette.color( QPalette::Light ) ); + + if ( style == Raised ) + qSwap( c1, c2 ); + + for ( int i = 0; i < 4; i++ ) + { + QRectF r = pathList[2 * i].controlPointRect(); + + QPen arcPen; + arcPen.setWidth( lineWidth ); + + QPen linePen; + linePen.setWidth( lineWidth ); + + switch( i ) + { + case 0: + { + arcPen.setColor( c1 ); + linePen.setColor( c1 ); + break; + } + case 1: + { + QLinearGradient gradient; + gradient.setStart( r.topLeft() ); + gradient.setFinalStop( r.bottomRight() ); + gradient.setColorAt( 0.0, c1 ); + gradient.setColorAt( 1.0, c2 ); + + arcPen.setBrush( gradient ); + linePen.setColor( c2 ); + break; + } + case 2: + { + arcPen.setColor( c2 ); + linePen.setColor( c2 ); + break; + } + case 3: + { + QLinearGradient gradient; + + gradient.setStart( r.bottomRight() ); + gradient.setFinalStop( r.topLeft() ); + gradient.setColorAt( 0.0, c2 ); + gradient.setColorAt( 1.0, c1 ); + + arcPen.setBrush( gradient ); + linePen.setColor( c1 ); + break; + } + } + + + painter->setPen( arcPen ); + painter->drawPath( pathList[ 2 * i] ); + + painter->setPen( linePen ); + painter->drawPath( pathList[ 2 * i + 1] ); + } + } + else + { + QPen pen( palette.color( QPalette::WindowText ), lineWidth ); + painter->setPen( pen ); + painter->drawPath( path ); + } + + painter->restore(); +} + +/*! + Draw a color bar into a rectangle + + \param painter Painter + \param colorMap Color map + \param interval Value range + \param scaleMap Scale map + \param orientation Orientation + \param rect Traget rectangle +*/ +void QwtPainter::drawColorBar( QPainter *painter, + const QwtColorMap &colorMap, const QwtInterval &interval, + const QwtScaleMap &scaleMap, Qt::Orientation orientation, + const QRectF &rect ) +{ + QVector<QRgb> colorTable; + if ( colorMap.format() == QwtColorMap::Indexed ) + colorTable = colorMap.colorTable( interval ); + + QColor c; + + const QRect devRect = rect.toAlignedRect(); + + /* + We paint to a pixmap first to have something scalable for printing + ( f.e. in a Pdf document ) + */ + + QPixmap pixmap( devRect.size() ); + QPainter pmPainter( &pixmap ); + pmPainter.translate( -devRect.x(), -devRect.y() ); + + if ( orientation == Qt::Horizontal ) + { + QwtScaleMap sMap = scaleMap; + sMap.setPaintInterval( rect.left(), rect.right() ); + + for ( int x = devRect.left(); x <= devRect.right(); x++ ) + { + const double value = sMap.invTransform( x ); + + if ( colorMap.format() == QwtColorMap::RGB ) + c.setRgb( colorMap.rgb( interval, value ) ); + else + c = colorTable[colorMap.colorIndex( interval, value )]; + + pmPainter.setPen( c ); + pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() ); + } + } + else // Vertical + { + QwtScaleMap sMap = scaleMap; + sMap.setPaintInterval( rect.bottom(), rect.top() ); + + for ( int y = devRect.top(); y <= devRect.bottom(); y++ ) + { + const double value = sMap.invTransform( y ); + + if ( colorMap.format() == QwtColorMap::RGB ) + c.setRgb( colorMap.rgb( interval, value ) ); + else + c = colorTable[colorMap.colorIndex( interval, value )]; + + pmPainter.setPen( c ); + pmPainter.drawLine( devRect.left(), y, devRect.right(), y ); + } + } + pmPainter.end(); + + drawPixmap( painter, rect, pixmap ); +} + +#if QT_VERSION >= 0x050000 + +static inline void qwtFillRect(QPainter *painter, const QRect &rect, const QBrush &brush) +{ + if ( brush.style() == Qt::TexturePattern ) + { + painter->setClipRect( rect ); + painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft()); + } + else if (brush.gradient() + && brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode) + { + painter->save(); + painter->setClipRect( rect ); + painter->fillRect(0, 0, painter->device()->width(), + painter->device()->height(), brush); + painter->restore(); + } + else + { + painter->fillRect(rect, brush); + } +} + +void QwtPainter::fillPixmap( const QWidget *widget, + QPixmap &pixmap, const QPoint &offset ) +{ + // Qwt 5.0.0 Alpha offers an empty dummy implementation + // of QPixmap::fill, that does nothing helpful beside converting + // a compiler into a runtime error + + const QRect rect( offset, pixmap.size() ); + + QPainter painter( &pixmap ); + painter.translate( -offset ); + + const QBrush autoFillBrush = + widget->palette().brush( widget->backgroundRole() ); + + if ( !( widget->autoFillBackground() && autoFillBrush.isOpaque() ) ) + { + const QBrush bg = widget->palette().brush( QPalette::Window ); + qwtFillRect( &painter, rect, bg); + } + + if ( widget->autoFillBackground() ) + qwtFillRect( &painter, rect, autoFillBrush); + + if ( widget->testAttribute(Qt::WA_StyledBackground) ) + { + painter.setClipRegion( rect ); + + QStyleOption opt; + opt.initFrom( widget ); + widget->style()->drawPrimitive( QStyle::PE_Widget, + &opt, &painter, widget ); + } +} + +#endif // QT_VERSION >= 0x050000 diff --git a/src/libpcp_qwt/src/qwt_painter.h b/src/libpcp_qwt/src/qwt_painter.h new file mode 100644 index 0000000..3235099 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_painter.h @@ -0,0 +1,154 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PAINTER_H +#define QWT_PAINTER_H + +#include "qwt_global.h" + +#include <qpoint.h> +#include <qrect.h> +#include <qpen.h> +#include <qline.h> + +class QPainter; +class QBrush; +class QColor; +class QWidget; +class QPolygonF; +class QRectF; +class QImage; +class QPixmap; +class QwtScaleMap; +class QwtColorMap; +class QwtInterval; + +class QPalette; +class QTextDocument; +class QPainterPath; + +/*! + \brief A collection of QPainter workarounds +*/ +class QWT_EXPORT QwtPainter +{ +public: + static void setPolylineSplitting( bool ); + static bool polylineSplitting(); + + static void setRoundingAlignment( bool ); + static bool roundingAlignment(); + static bool roundingAlignment(QPainter *); + + static void drawText( QPainter *, double x, double y, const QString & ); + static void drawText( QPainter *, const QPointF &, const QString & ); + static void drawText( QPainter *, double x, double y, double w, double h, + int flags, const QString & ); + static void drawText( QPainter *, const QRectF &, + int flags, const QString & ); + +#ifndef QT_NO_RICHTEXT + static void drawSimpleRichText( QPainter *, const QRectF &, + int flags, const QTextDocument & ); +#endif + + static void drawRect( QPainter *, double x, double y, double w, double h ); + static void drawRect( QPainter *, const QRectF &rect ); + static void fillRect( QPainter *, const QRectF &, const QBrush & ); + + static void drawEllipse( QPainter *, const QRectF & ); + static void drawPie( QPainter *, const QRectF & r, int a, int alen ); + + static void drawLine( QPainter *, double x1, double y1, double x2, double y2 ); + static void drawLine( QPainter *, const QPointF &p1, const QPointF &p2 ); + static void drawLine( QPainter *, const QLineF & ); + + static void drawPolygon( QPainter *, const QPolygonF &pa ); + static void drawPolyline( QPainter *, const QPolygonF &pa ); + static void drawPolyline( QPainter *, const QPointF *, int pointCount ); + + static void drawPoint( QPainter *, double x, double y ); + static void drawPoint( QPainter *, const QPointF & ); + + static void drawPath( QPainter *, const QPainterPath & ); + static void drawImage( QPainter *, const QRectF &, const QImage & ); + static void drawPixmap( QPainter *, const QRectF &, const QPixmap & ); + + static void drawRoundedFrame( QPainter *, + const QRectF &, double xRadius, double yRadius, + const QPalette &, int lineWidth, int frameStyle ); + + static void drawFocusRect( QPainter *, QWidget * ); + static void drawFocusRect( QPainter *, QWidget *, const QRect & ); + + static void drawColorBar( QPainter *painter, + const QwtColorMap &, const QwtInterval &, + const QwtScaleMap &, Qt::Orientation, const QRectF & ); + + static bool isAligning( QPainter *painter ); + +#if QT_VERSION >= 0x050000 + static void fillPixmap( const QWidget *, + QPixmap &, const QPoint &offset = QPoint() ); +#endif + +private: + static bool d_polylineSplitting; + static bool d_roundingAlignment; +}; + +//! Wrapper for QPainter::drawPoint() +inline void QwtPainter::drawPoint( QPainter *painter, double x, double y ) +{ + QwtPainter::drawPoint( painter, QPointF( x, y ) ); +} + +//! Wrapper for QPainter::drawLine() +inline void QwtPainter::drawLine( QPainter *painter, + double x1, double y1, double x2, double y2 ) +{ + QwtPainter::drawLine( painter, QPointF( x1, y1 ), QPointF( x2, y2 ) ); +} + +//! Wrapper for QPainter::drawLine() +inline void QwtPainter::drawLine( QPainter *painter, const QLineF &line ) +{ + QwtPainter::drawLine( painter, line.p1(), line.p2() ); +} + +/*! + Returns whether line splitting for the raster paint engine is enabled. + \sa setPolylineSplitting() +*/ +inline bool QwtPainter::polylineSplitting() +{ + return d_polylineSplitting; +} + +/*! + Returns whether coordinates should be rounded, before they are painted + to a paint engine that floors to integer values. For other paint engines + this ( Pdf, SVG ), this flag has no effect. + + \sa setRoundingAlignment(), isAligning() +*/ +inline bool QwtPainter::roundingAlignment() +{ + return d_roundingAlignment; +} + +/*! + \return roundingAlignment() && isAligning(painter); + \param painter Painter +*/ +inline bool QwtPainter::roundingAlignment(QPainter *painter) +{ + return d_roundingAlignment && isAligning(painter); +} +#endif diff --git a/src/libpcp_qwt/src/qwt_panner.cpp b/src/libpcp_qwt/src/qwt_panner.cpp new file mode 100644 index 0000000..709a2ea --- /dev/null +++ b/src/libpcp_qwt/src/qwt_panner.cpp @@ -0,0 +1,537 @@ +/* -*- 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_panner.h" +#include "qwt_picker.h" +#if QT_VERSION >= 0x050000 +#include "qwt_painter.h" +#endif +#include <qpainter.h> +#include <qpixmap.h> +#include <qevent.h> +#include <qcursor.h> +#include <qbitmap.h> + +static QVector<QwtPicker *> qwtActivePickers( QWidget *w ) +{ + QVector<QwtPicker *> pickers; + + QObjectList children = w->children(); + for ( int i = 0; i < children.size(); i++ ) + { + QwtPicker *picker = qobject_cast<QwtPicker *>( children[i] ); + if ( picker && picker->isEnabled() ) + pickers += picker; + } + + return pickers; +} + +class QwtPanner::PrivateData +{ +public: + PrivateData(): + button( Qt::LeftButton ), + buttonState( Qt::NoButton ), + abortKey( Qt::Key_Escape ), + abortKeyState( Qt::NoButton ), +#ifndef QT_NO_CURSOR + cursor( NULL ), + restoreCursor( NULL ), + hasCursor( false ), +#endif + isEnabled( false ) + { + orientations = Qt::Vertical | Qt::Horizontal; + } + + ~PrivateData() + { +#ifndef QT_NO_CURSOR + delete cursor; + delete restoreCursor; +#endif + } + + int button; + int buttonState; + int abortKey; + int abortKeyState; + + QPoint initialPos; + QPoint pos; + + QPixmap pixmap; + QBitmap contentsMask; + +#ifndef QT_NO_CURSOR + QCursor *cursor; + QCursor *restoreCursor; + bool hasCursor; +#endif + bool isEnabled; + Qt::Orientations orientations; +}; + +/*! + Creates an panner that is enabled for the left mouse button. + + \param parent Parent widget to be panned +*/ +QwtPanner::QwtPanner( QWidget *parent ): + QWidget( parent ) +{ + d_data = new PrivateData(); + + setAttribute( Qt::WA_TransparentForMouseEvents ); + setAttribute( Qt::WA_NoSystemBackground ); + setFocusPolicy( Qt::NoFocus ); + hide(); + + setEnabled( true ); +} + +//! Destructor +QwtPanner::~QwtPanner() +{ + delete d_data; +} + +/*! + Change the mouse button + The defaults are Qt::LeftButton and Qt::NoButton +*/ +void QwtPanner::setMouseButton( int button, int buttonState ) +{ + d_data->button = button; + d_data->buttonState = buttonState; +} + +//! Get the mouse button +void QwtPanner::getMouseButton( int &button, int &buttonState ) const +{ + button = d_data->button; + buttonState = d_data->buttonState; +} + +/*! + Change the abort key + The defaults are Qt::Key_Escape and Qt::NoButton + + \param key Key ( See Qt::Keycode ) + \param state State +*/ +void QwtPanner::setAbortKey( int key, int state ) +{ + d_data->abortKey = key; + d_data->abortKeyState = state; +} + +//! Get the abort key +void QwtPanner::getAbortKey( int &key, int &state ) const +{ + key = d_data->abortKey; + state = d_data->abortKeyState; +} + +/*! + Change the cursor, that is active while panning + The default is the cursor of the parent widget. + + \param cursor New cursor + + \sa setCursor() +*/ +#ifndef QT_NO_CURSOR +void QwtPanner::setCursor( const QCursor &cursor ) +{ + d_data->cursor = new QCursor( cursor ); +} +#endif + +/*! + \return Cursor that is active while panning + \sa setCursor() +*/ +#ifndef QT_NO_CURSOR +const QCursor QwtPanner::cursor() const +{ + if ( d_data->cursor ) + return *d_data->cursor; + + if ( parentWidget() ) + return parentWidget()->cursor(); + + return QCursor(); +} +#endif + +/*! + \brief En/disable the panner + + When enabled is true an event filter is installed for + the observed widget, otherwise the event filter is removed. + + \param on true or false + \sa isEnabled(), eventFilter() +*/ +void QwtPanner::setEnabled( bool on ) +{ + if ( d_data->isEnabled != on ) + { + d_data->isEnabled = on; + + QWidget *w = parentWidget(); + if ( w ) + { + if ( d_data->isEnabled ) + { + w->installEventFilter( this ); + } + else + { + w->removeEventFilter( this ); + hide(); + } + } + } +} + +/*! + Set the orientations, where panning is enabled + The default value is in both directions: Qt::Horizontal | Qt::Vertical + + /param o Orientation +*/ +void QwtPanner::setOrientations( Qt::Orientations o ) +{ + d_data->orientations = o; +} + +//! Return the orientation, where paning is enabled +Qt::Orientations QwtPanner::orientations() const +{ + return d_data->orientations; +} + +/*! + Return true if a orientatio is enabled + \sa orientations(), setOrientations() +*/ +bool QwtPanner::isOrientationEnabled( Qt::Orientation o ) const +{ + return d_data->orientations & o; +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled, eventFilter() +*/ +bool QwtPanner::isEnabled() const +{ + return d_data->isEnabled; +} + +/*! + \brief Paint event + + Repaint the grabbed pixmap on its current position and + fill the empty spaces by the background of the parent widget. + + \param pe Paint event +*/ +void QwtPanner::paintEvent( QPaintEvent *pe ) +{ + int dx = d_data->pos.x() - d_data->initialPos.x(); + int dy = d_data->pos.y() - d_data->initialPos.y(); + + QRect r( 0, 0, d_data->pixmap.width(), d_data->pixmap.height() ); + r.moveCenter( QPoint( r.center().x() + dx, r.center().y() + dy ) ); + + QPixmap pm( size() ); +#if QT_VERSION >= 0x050000 + QwtPainter::fillPixmap( parentWidget(), pm ); +#else + pm.fill( parentWidget(), 0, 0 ); +#endif + + QPainter painter( &pm ); + + if ( !d_data->contentsMask.isNull() ) + { + QPixmap masked = d_data->pixmap; + masked.setMask( d_data->contentsMask ); + painter.drawPixmap( r, masked ); + } + else + { + painter.drawPixmap( r, d_data->pixmap ); + } + + painter.end(); + + if ( !d_data->contentsMask.isNull() ) + pm.setMask( d_data->contentsMask ); + + painter.begin( this ); + painter.setClipRegion( pe->region() ); + painter.drawPixmap( 0, 0, pm ); +} + +/*! + \brief Calculate a mask for the contents of the panned widget + + Sometimes only parts of the contents of a widget should be + panned. F.e. for a widget with a styled background with rounded borders + only the area inside of the border should be panned. + + \return An empty bitmap, indicating no mask +*/ +QBitmap QwtPanner::contentsMask() const +{ + return QBitmap(); +} + +/*! + Grab the widget into a pixmap. +*/ +QPixmap QwtPanner::grab() const +{ + return QPixmap::grabWidget( parentWidget() ); +} + +/*! + \brief Event filter + + When isEnabled() the mouse events of the observed widget are filtered. + + \param object Object to be filtered + \param event Event + + \sa widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent() +*/ +bool QwtPanner::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == NULL || object != parentWidget() ) + return false; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( ( QKeyEvent * )event ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( ( QKeyEvent * )event ); + break; + } + case QEvent::Paint: + { + if ( isVisible() ) + return true; + break; + } + default:; + } + + return false; +} + +/*! + Handle a mouse press event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent(), +*/ +void QwtPanner::widgetMousePressEvent( QMouseEvent *mouseEvent ) +{ + if ( mouseEvent->button() != d_data->button ) + return; + + QWidget *w = parentWidget(); + if ( w == NULL ) + return; + + if ( ( mouseEvent->modifiers() & Qt::KeyboardModifierMask ) != + ( int )( d_data->buttonState & Qt::KeyboardModifierMask ) ) + { + return; + } + +#ifndef QT_NO_CURSOR + showCursor( true ); +#endif + + d_data->initialPos = d_data->pos = mouseEvent->pos(); + + setGeometry( parentWidget()->rect() ); + + // We don't want to grab the picker ! + QVector<QwtPicker *> pickers = qwtActivePickers( parentWidget() ); + for ( int i = 0; i < pickers.size(); i++ ) + pickers[i]->setEnabled( false ); + + d_data->pixmap = grab(); + d_data->contentsMask = contentsMask(); + + for ( int i = 0; i < pickers.size(); i++ ) + pickers[i]->setEnabled( true ); + + show(); +} + +/*! + Handle a mouse move event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent() +*/ +void QwtPanner::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) +{ + if ( !isVisible() ) + return; + + QPoint pos = mouseEvent->pos(); + if ( !isOrientationEnabled( Qt::Horizontal ) ) + pos.setX( d_data->initialPos.x() ); + if ( !isOrientationEnabled( Qt::Vertical ) ) + pos.setY( d_data->initialPos.y() ); + + if ( pos != d_data->pos && rect().contains( pos ) ) + { + d_data->pos = pos; + update(); + + Q_EMIT moved( d_data->pos.x() - d_data->initialPos.x(), + d_data->pos.y() - d_data->initialPos.y() ); + } +} + +/*! + Handle a mouse release event for the observed widget. + + \param mouseEvent Mouse event + \sa eventFilter(), widgetMousePressEvent(), + widgetMouseMoveEvent(), +*/ +void QwtPanner::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) +{ + if ( isVisible() ) + { + hide(); +#ifndef QT_NO_CURSOR + showCursor( false ); +#endif + + QPoint pos = mouseEvent->pos(); + if ( !isOrientationEnabled( Qt::Horizontal ) ) + pos.setX( d_data->initialPos.x() ); + if ( !isOrientationEnabled( Qt::Vertical ) ) + pos.setY( d_data->initialPos.y() ); + + d_data->pixmap = QPixmap(); + d_data->contentsMask = QBitmap(); + d_data->pos = pos; + + if ( d_data->pos != d_data->initialPos ) + { + Q_EMIT panned( d_data->pos.x() - d_data->initialPos.x(), + d_data->pos.y() - d_data->initialPos.y() ); + } + } +} + +/*! + Handle a key press event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtPanner::widgetKeyPressEvent( QKeyEvent *keyEvent ) +{ + if ( keyEvent->key() == d_data->abortKey ) + { + const bool matched = + ( keyEvent->modifiers() & Qt::KeyboardModifierMask ) == + ( int )( d_data->abortKeyState & Qt::KeyboardModifierMask ); + if ( matched ) + { + hide(); +#ifndef QT_NO_CURSOR + showCursor( false ); +#endif + d_data->pixmap = QPixmap(); + } + } +} + +/*! + Handle a key release event for the observed widget. + + \param keyEvent Key event + \sa eventFilter(), widgetKeyReleaseEvent() +*/ +void QwtPanner::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) +{ + Q_UNUSED( keyEvent ); +} + +#ifndef QT_NO_CURSOR +void QwtPanner::showCursor( bool on ) +{ + if ( on == d_data->hasCursor ) + return; + + QWidget *w = parentWidget(); + if ( w == NULL || d_data->cursor == NULL ) + return; + + d_data->hasCursor = on; + + if ( on ) + { + if ( w->testAttribute( Qt::WA_SetCursor ) ) + { + delete d_data->restoreCursor; + d_data->restoreCursor = new QCursor( w->cursor() ); + } + w->setCursor( *d_data->cursor ); + } + else + { + if ( d_data->restoreCursor ) + { + w->setCursor( *d_data->restoreCursor ); + delete d_data->restoreCursor; + d_data->restoreCursor = NULL; + } + else + w->unsetCursor(); + } +} +#endif diff --git a/src/libpcp_qwt/src/qwt_panner.h b/src/libpcp_qwt/src/qwt_panner.h new file mode 100644 index 0000000..247d4a3 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_panner.h @@ -0,0 +1,100 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PANNER_H +#define QWT_PANNER_H 1 + +#include "qwt_global.h" +#include <qwidget.h> +#include <qpixmap.h> + +class QCursor; + +/*! + \brief QwtPanner provides panning of a widget + + QwtPanner grabs the contents of a widget, that can be dragged + in all directions. The offset between the start and the end position + is emitted by the panned signal. + + QwtPanner grabs the content of the widget into a pixmap and moves + the pixmap around, without initiating any repaint events for the widget. + Areas, that are not part of content are not painted while panning. + This makes panning fast enough for widgets, where + repaints are too slow for mouse movements. + + For widgets, where repaints are very fast it might be better to + implement panning manually by mapping mouse events into paint events. +*/ +class QWT_EXPORT QwtPanner: public QWidget +{ + Q_OBJECT + +public: + QwtPanner( QWidget* parent ); + virtual ~QwtPanner(); + + void setEnabled( bool ); + bool isEnabled() const; + + void setMouseButton( int button, int buttonState = Qt::NoButton ); + void getMouseButton( int &button, int &buttonState ) const; + void setAbortKey( int key, int state = Qt::NoButton ); + void getAbortKey( int &key, int &state ) const; + + void setCursor( const QCursor & ); + const QCursor cursor() const; + + void setOrientations( Qt::Orientations ); + Qt::Orientations orientations() const; + + bool isOrientationEnabled( Qt::Orientation ) const; + + virtual bool eventFilter( QObject *, QEvent * ); + +Q_SIGNALS: + /*! + Signal emitted, when panning is done + + \param dx Offset in horizontal direction + \param dy Offset in vertical direction + */ + void panned( int dx, int dy ); + + /*! + Signal emitted, while the widget moved, but panning + is not finished. + + \param dx Offset in horizontal direction + \param dy Offset in vertical direction + */ + void moved( int dx, int dy ); + +protected: + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + + virtual void paintEvent( QPaintEvent * ); + + virtual QBitmap contentsMask() const; + virtual QPixmap grab() const; + +private: +#ifndef QT_NO_CURSOR + void showCursor( bool ); +#endif + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_picker.cpp b/src/libpcp_qwt/src/qwt_picker.cpp new file mode 100644 index 0000000..2b7afa1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_picker.cpp @@ -0,0 +1,1462 @@ +/* -*- 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_picker.h" +#include "qwt_picker_machine.h" +#include "qwt_painter.h" +#include "qwt_math.h" +#include <qapplication.h> +#include <qevent.h> +#include <qpainter.h> +#include <qframe.h> +#include <qcursor.h> +#include <qbitmap.h> +#include <qpointer.h> +#include <qpaintengine.h> +#include <qmath.h> + +class QwtPicker::PickerWidget: public QWidget +{ +public: + enum Type + { + RubberBand, + Text + }; + + PickerWidget( QwtPicker *, QWidget *, Type ); + void updateMask(); + + /* + For a tracker text with a background we can use the background + rect as mask. Also for "regular" Qt widgets >= 4.3.0 we + don't need to mask the text anymore. + */ + bool d_hasTextMask; + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + + QwtPicker *d_picker; + Type d_type; +}; + +class QwtPicker::PrivateData +{ +public: + bool enabled; + + QwtPickerMachine *stateMachine; + + QwtPicker::ResizeMode resizeMode; + + QwtPicker::RubberBand rubberBand; + QPen rubberBandPen; + + QwtPicker::DisplayMode trackerMode; + QPen trackerPen; + QFont trackerFont; + + QPolygon pickedPoints; + bool isActive; + QPoint trackerPosition; + + bool mouseTracking; // used to save previous value + + /* + On X11 the widget below the picker widgets gets paint events + with a region that is the bounding rect of the mask, if it is complex. + In case of (f.e) a CrossRubberBand and a text this creates complete + repaints of the widget. So we better use two different widgets. + */ + + QPointer<PickerWidget> rubberBandWidget; + QPointer<PickerWidget> trackerWidget; +}; + +QwtPicker::PickerWidget::PickerWidget( + QwtPicker *picker, QWidget *parent, Type type ): + QWidget( parent ), + d_hasTextMask( false ), + d_picker( picker ), + d_type( type ) +{ + setAttribute( Qt::WA_TransparentForMouseEvents ); + setAttribute( Qt::WA_NoSystemBackground ); + setFocusPolicy( Qt::NoFocus ); +} + +void QwtPicker::PickerWidget::updateMask() +{ + QRegion mask; + + if ( d_type == RubberBand ) + { + QBitmap bm( width(), height() ); + bm.fill( Qt::color0 ); + + QPainter painter( &bm ); + QPen pen = d_picker->rubberBandPen(); + pen.setColor( Qt::color1 ); + painter.setPen( pen ); + + d_picker->drawRubberBand( &painter ); + + mask = QRegion( bm ); + } + if ( d_type == Text ) + { + d_hasTextMask = parentWidget()->testAttribute( Qt::WA_PaintOnScreen ); + + if ( d_hasTextMask ) + { + const QwtText label = d_picker->trackerText( + d_picker->trackerPosition() ); + + if ( label.testPaintAttribute( QwtText::PaintBackground ) + && label.backgroundBrush().style() != Qt::NoBrush ) + { + if ( label.backgroundBrush().color().alpha() > 0 ) + { + // We don't need a text mask, when we have a background + d_hasTextMask = false; + } + } + } + + if ( d_hasTextMask ) + { + QBitmap bm( width(), height() ); + bm.fill( Qt::color0 ); + + QPainter painter( &bm ); + painter.setFont( font() ); + + QPen pen = d_picker->trackerPen(); + pen.setColor( Qt::color1 ); + painter.setPen( pen ); + + d_picker->drawTracker( &painter ); + + mask = QRegion( bm ); + } + else + { + mask = d_picker->trackerRect( font() ); + } + } + + QWidget *w = parentWidget(); + if ( w && !w->testAttribute( Qt::WA_PaintOnScreen ) ) + { + // The parent widget gets an update for its complete rectangle + // when the mask is changed in visible state. + // With this hide/show we only get an update for the + // previous mask. + + hide(); + } + setMask( mask ); + setVisible( !mask.isEmpty() ); +} + +void QwtPicker::PickerWidget::paintEvent( QPaintEvent *e ) +{ + QPainter painter( this ); + painter.setClipRegion( e->region() ); + + if ( d_type == RubberBand ) + { + painter.setPen( d_picker->rubberBandPen() ); + d_picker->drawRubberBand( &painter ); + } + + if ( d_type == Text ) + { + /* + If we have a text mask we simply fill the region of + the mask. This gives better results for antialiased fonts. + */ + if ( d_hasTextMask ) + { + painter.fillRect( e->rect(), + QBrush( d_picker->trackerPen().color() ) ); + } + else + { + painter.setPen( d_picker->trackerPen() ); + d_picker->drawTracker( &painter ); + } + } +} + +void QwtPicker::PickerWidget::resizeEvent( QResizeEvent *event ) +{ + QWidget::resizeEvent( event ); + if ( isVisible() ) + updateMask(); +} + +/*! + Constructor + + Creates an picker that is enabled, but without a state machine. + rubberband and tracker are disabled. + + \param parent Parent widget, that will be observed + */ + +QwtPicker::QwtPicker( QWidget *parent ): + QObject( parent ) +{ + init( parent, NoRubberBand, AlwaysOff ); +} + +/*! + Constructor + + \param rubberBand Rubberband style + \param trackerMode Tracker mode + \param parent Parent widget, that will be observed + */ +QwtPicker::QwtPicker( RubberBand rubberBand, + DisplayMode trackerMode, QWidget *parent ): + QObject( parent ) +{ + init( parent, rubberBand, trackerMode ); +} + +//! Destructor +QwtPicker::~QwtPicker() +{ + setMouseTracking( false ); + delete d_data->stateMachine; + delete d_data->rubberBandWidget; + delete d_data->trackerWidget; + delete d_data; +} + +//! Init the picker, used by the constructors +void QwtPicker::init( QWidget *parent, + RubberBand rubberBand, DisplayMode trackerMode ) +{ + d_data = new PrivateData; + + d_data->rubberBandWidget = NULL; + d_data->trackerWidget = NULL; + + d_data->rubberBand = rubberBand; + d_data->enabled = false; + d_data->resizeMode = Stretch; + d_data->trackerMode = AlwaysOff; + d_data->isActive = false; + d_data->trackerPosition = QPoint( -1, -1 ); + d_data->mouseTracking = false; + + d_data->stateMachine = NULL; + + if ( parent ) + { + if ( parent->focusPolicy() == Qt::NoFocus ) + parent->setFocusPolicy( Qt::WheelFocus ); + + d_data->trackerFont = parent->font(); + d_data->mouseTracking = parent->hasMouseTracking(); + setEnabled( true ); + } + setTrackerMode( trackerMode ); +} + +/*! + Set a state machine and delete the previous one + + \param stateMachine State machine + \sa stateMachine() +*/ +void QwtPicker::setStateMachine( QwtPickerMachine *stateMachine ) +{ + if ( d_data->stateMachine != stateMachine ) + { + reset(); + + delete d_data->stateMachine; + d_data->stateMachine = stateMachine; + + if ( d_data->stateMachine ) + d_data->stateMachine->reset(); + } +} + +/*! + \return Assigned state machine + \sa setStateMachine() +*/ +QwtPickerMachine *QwtPicker::stateMachine() +{ + return d_data->stateMachine; +} + +/*! + \return Assigned state machine + \sa setStateMachine() +*/ +const QwtPickerMachine *QwtPicker::stateMachine() const +{ + return d_data->stateMachine; +} + +//! Return the parent widget, where the selection happens +QWidget *QwtPicker::parentWidget() +{ + QObject *obj = parent(); + if ( obj && obj->isWidgetType() ) + return static_cast<QWidget *>( obj ); + + return NULL; +} + +//! Return the parent widget, where the selection happens +const QWidget *QwtPicker::parentWidget() const +{ + QObject *obj = parent(); + if ( obj && obj->isWidgetType() ) + return static_cast< const QWidget *>( obj ); + + return NULL; +} + +/*! + Set the rubberband style + + \param rubberBand Rubberband style + The default value is NoRubberBand. + + \sa rubberBand(), RubberBand, setRubberBandPen() +*/ +void QwtPicker::setRubberBand( RubberBand rubberBand ) +{ + d_data->rubberBand = rubberBand; +} + +/*! + \return Rubberband style + \sa setRubberBand(), RubberBand, rubberBandPen() +*/ +QwtPicker::RubberBand QwtPicker::rubberBand() const +{ + return d_data->rubberBand; +} + +/*! + \brief Set the display mode of the tracker. + + A tracker displays information about current position of + the cursor as a string. The display mode controls + if the tracker has to be displayed whenever the observed + widget has focus and cursor (AlwaysOn), never (AlwaysOff), or + only when the selection is active (ActiveOnly). + + \param mode Tracker display mode + + \warning In case of AlwaysOn, mouseTracking will be enabled + for the observed widget. + \sa trackerMode(), DisplayMode +*/ + +void QwtPicker::setTrackerMode( DisplayMode mode ) +{ + if ( d_data->trackerMode != mode ) + { + d_data->trackerMode = mode; + setMouseTracking( d_data->trackerMode == AlwaysOn ); + } +} + +/*! + \return Tracker display mode + \sa setTrackerMode(), DisplayMode +*/ +QwtPicker::DisplayMode QwtPicker::trackerMode() const +{ + return d_data->trackerMode; +} + +/*! + \brief Set the resize mode. + + The resize mode controls what to do with the selected points of an active + selection when the observed widget is resized. + + Stretch means the points are scaled according to the new + size, KeepSize means the points remain unchanged. + + The default mode is Stretch. + + \param mode Resize mode + \sa resizeMode(), ResizeMode +*/ +void QwtPicker::setResizeMode( ResizeMode mode ) +{ + d_data->resizeMode = mode; +} + +/*! + \return Resize mode + \sa setResizeMode(), ResizeMode +*/ + +QwtPicker::ResizeMode QwtPicker::resizeMode() const +{ + return d_data->resizeMode; +} + +/*! + \brief En/disable the picker + + When enabled is true an event filter is installed for + the observed widget, otherwise the event filter is removed. + + \param enabled true or false + \sa isEnabled(), eventFilter() +*/ +void QwtPicker::setEnabled( bool enabled ) +{ + if ( d_data->enabled != enabled ) + { + d_data->enabled = enabled; + + QWidget *w = parentWidget(); + if ( w ) + { + if ( enabled ) + w->installEventFilter( this ); + else + w->removeEventFilter( this ); + } + + updateDisplay(); + } +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled(), eventFilter() +*/ + +bool QwtPicker::isEnabled() const +{ + return d_data->enabled; +} + +/*! + Set the font for the tracker + + \param font Tracker font + \sa trackerFont(), setTrackerMode(), setTrackerPen() +*/ +void QwtPicker::setTrackerFont( const QFont &font ) +{ + if ( font != d_data->trackerFont ) + { + d_data->trackerFont = font; + updateDisplay(); + } +} + +/*! + \return Tracker font + \sa setTrackerFont(), trackerMode(), trackerPen() +*/ + +QFont QwtPicker::trackerFont() const +{ + return d_data->trackerFont; +} + +/*! + Set the pen for the tracker + + \param pen Tracker pen + \sa trackerPen(), setTrackerMode(), setTrackerFont() +*/ +void QwtPicker::setTrackerPen( const QPen &pen ) +{ + if ( pen != d_data->trackerPen ) + { + d_data->trackerPen = pen; + updateDisplay(); + } +} + +/*! + \return Tracker pen + \sa setTrackerPen(), trackerMode(), trackerFont() +*/ +QPen QwtPicker::trackerPen() const +{ + return d_data->trackerPen; +} + +/*! + Set the pen for the rubberband + + \param pen Rubberband pen + \sa rubberBandPen(), setRubberBand() +*/ +void QwtPicker::setRubberBandPen( const QPen &pen ) +{ + if ( pen != d_data->rubberBandPen ) + { + d_data->rubberBandPen = pen; + updateDisplay(); + } +} + +/*! + \return Rubberband pen + \sa setRubberBandPen(), rubberBand() +*/ +QPen QwtPicker::rubberBandPen() const +{ + return d_data->rubberBandPen; +} + +/*! + \brief Return the label for a position + + In case of HLineRubberBand the label is the value of the + y position, in case of VLineRubberBand the value of the x position. + Otherwise the label contains x and y position separated by a ',' . + + The format for the string conversion is "%d". + + \param pos Position + \return Converted position as string +*/ + +QwtText QwtPicker::trackerText( const QPoint &pos ) const +{ + QString label; + + switch ( rubberBand() ) + { + case HLineRubberBand: + label.sprintf( "%d", pos.y() ); + break; + case VLineRubberBand: + label.sprintf( "%d", pos.x() ); + break; + default: + label.sprintf( "%d, %d", pos.x(), pos.y() ); + } + return label; +} + +/*! + Draw a rubberband, depending on rubberBand() + + \param painter Painter, initialized with clip rect + + \sa rubberBand(), RubberBand +*/ + +void QwtPicker::drawRubberBand( QPainter *painter ) const +{ + if ( !isActive() || rubberBand() == NoRubberBand || + rubberBandPen().style() == Qt::NoPen ) + { + return; + } + + const QRect &pRect = pickRect(); + const QPolygon pa = adjustedPoints( d_data->pickedPoints ); + + QwtPickerMachine::SelectionType selectionType = + QwtPickerMachine::NoSelection; + + if ( d_data->stateMachine ) + selectionType = d_data->stateMachine->selectionType(); + + switch ( selectionType ) + { + case QwtPickerMachine::NoSelection: + case QwtPickerMachine::PointSelection: + { + if ( pa.count() < 1 ) + return; + + const QPoint pos = pa[0]; + + switch ( rubberBand() ) + { + case VLineRubberBand: + QwtPainter::drawLine( painter, pos.x(), + pRect.top(), pos.x(), pRect.bottom() ); + break; + + case HLineRubberBand: + QwtPainter::drawLine( painter, pRect.left(), + pos.y(), pRect.right(), pos.y() ); + break; + + case CrossRubberBand: + QwtPainter::drawLine( painter, pos.x(), + pRect.top(), pos.x(), pRect.bottom() ); + QwtPainter::drawLine( painter, pRect.left(), + pos.y(), pRect.right(), pos.y() ); + break; + default: + break; + } + break; + } + case QwtPickerMachine::RectSelection: + { + if ( pa.count() < 2 ) + return; + + const QPoint p1 = pa[0]; + const QPoint p2 = pa[int( pa.count() - 1 )]; + + const QRect rect = QRect( p1, p2 ).normalized(); + switch ( rubberBand() ) + { + case EllipseRubberBand: + QwtPainter::drawEllipse( painter, rect ); + break; + case RectRubberBand: + QwtPainter::drawRect( painter, rect ); + break; + default: + break; + } + break; + } + case QwtPickerMachine::PolygonSelection: + { + if ( rubberBand() == PolygonRubberBand ) + painter->drawPolyline( pa ); + break; + } + default: + break; + } +} + +/*! + Draw the tracker + + \param painter Painter + \sa trackerRect(), trackerText() +*/ + +void QwtPicker::drawTracker( QPainter *painter ) const +{ + const QRect textRect = trackerRect( painter->font() ); + if ( !textRect.isEmpty() ) + { + const QwtText label = trackerText( d_data->trackerPosition ); + if ( !label.isEmpty() ) + label.draw( painter, textRect ); + } +} + +/*! + \brief Map the pickedPoints() into a selection() + + adjustedPoints() maps the points, that have been collected on + the parentWidget() into a selection(). The default implementation + simply returns the points unmodified. + + The reason, why a selection() differs from the picked points + depends on the application requirements. F.e. : + + - A rectangular selection might need to have a specific aspect ratio only.\n + - A selection could accept non intersecting polygons only.\n + - ...\n + + The example below is for a rectangular selection, where the first + point is the center of the selected rectangle. + \par Example + \verbatim QPolygon MyPicker::adjustedPoints(const QPolygon &points) const +{ + QPolygon adjusted; + if ( points.size() == 2 ) + { + const int width = qAbs(points[1].x() - points[0].x()); + const int height = qAbs(points[1].y() - points[0].y()); + + QRect rect(0, 0, 2 * width, 2 * height); + rect.moveCenter(points[0]); + + adjusted += rect.topLeft(); + adjusted += rect.bottomRight(); + } + return adjusted; +}\endverbatim\n +*/ +QPolygon QwtPicker::adjustedPoints( const QPolygon &points ) const +{ + return points; +} + +/*! + \return Selected points + \sa pickedPoints(), adjustedPoints() +*/ +QPolygon QwtPicker::selection() const +{ + return adjustedPoints( d_data->pickedPoints ); +} + +//! \return Current position of the tracker +QPoint QwtPicker::trackerPosition() const +{ + return d_data->trackerPosition; +} + +/*! + Calculate the bounding rectangle for the tracker text + from the current position of the tracker + + \param font Font of the tracker text + \return Bounding rectangle of the tracker text + + \sa trackerPosition() +*/ +QRect QwtPicker::trackerRect( const QFont &font ) const +{ + if ( trackerMode() == AlwaysOff || + ( trackerMode() == ActiveOnly && !isActive() ) ) + { + return QRect(); + } + + if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) + return QRect(); + + QwtText text = trackerText( d_data->trackerPosition ); + if ( text.isEmpty() ) + return QRect(); + + const QSizeF textSize = text.textSize( font ); + QRect textRect( 0, 0, qCeil( textSize.width() ), qCeil( textSize.height() ) ); + + const QPoint &pos = d_data->trackerPosition; + + int alignment = 0; + if ( isActive() && d_data->pickedPoints.count() > 1 + && rubberBand() != NoRubberBand ) + { + const QPoint last = + d_data->pickedPoints[int( d_data->pickedPoints.count() ) - 2]; + + alignment |= ( pos.x() >= last.x() ) ? Qt::AlignRight : Qt::AlignLeft; + alignment |= ( pos.y() > last.y() ) ? Qt::AlignBottom : Qt::AlignTop; + } + else + alignment = Qt::AlignTop | Qt::AlignRight; + + const int margin = 5; + + int x = pos.x(); + if ( alignment & Qt::AlignLeft ) + x -= textRect.width() + margin; + else if ( alignment & Qt::AlignRight ) + x += margin; + + int y = pos.y(); + if ( alignment & Qt::AlignBottom ) + y += margin; + else if ( alignment & Qt::AlignTop ) + y -= textRect.height() + margin; + + textRect.moveTopLeft( QPoint( x, y ) ); + + int right = qMin( textRect.right(), pickRect().right() - margin ); + int bottom = qMin( textRect.bottom(), pickRect().bottom() - margin ); + textRect.moveBottomRight( QPoint( right, bottom ) ); + + int left = qMax( textRect.left(), pickRect().left() + margin ); + int top = qMax( textRect.top(), pickRect().top() + margin ); + textRect.moveTopLeft( QPoint( left, top ) ); + + return textRect; +} + +/*! + \brief Event filter + + When isEnabled() == true all events of the observed widget are filtered. + Mouse and keyboard events are translated into widgetMouse- and widgetKey- + and widgetWheel-events. Paint and Resize events are handled to keep + rubberband and tracker up to date. + + \param object Object to be filtered + \param event Event + + \sa widgetEnterEvent(), widgetLeaveEvent(), + widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent(), + QObject::installEventFilter(), QObject::event() +*/ +bool QwtPicker::eventFilter( QObject *object, QEvent *event ) +{ + if ( object && object == parentWidget() ) + { + switch ( event->type() ) + { + case QEvent::Resize: + { + const QResizeEvent *re = ( QResizeEvent * )event; + if ( d_data->resizeMode == Stretch ) + stretchSelection( re->oldSize(), re->size() ); + + if ( d_data->rubberBandWidget ) + d_data->rubberBandWidget->resize( re->size() ); + + if ( d_data->trackerWidget ) + d_data->trackerWidget->resize( re->size() ); + break; + } + case QEvent::Enter: + { + widgetEnterEvent( event ); + break; + } + case QEvent::Leave: + { + widgetLeaveEvent( event ); + break; + } + case QEvent::MouseButtonPress: + { + widgetMousePressEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseButtonRelease: + { + widgetMouseReleaseEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseButtonDblClick: + { + widgetMouseDoubleClickEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::MouseMove: + { + widgetMouseMoveEvent( ( QMouseEvent * )event ); + break; + } + case QEvent::KeyPress: + { + widgetKeyPressEvent( ( QKeyEvent * )event ); + break; + } + case QEvent::KeyRelease: + { + widgetKeyReleaseEvent( ( QKeyEvent * )event ); + break; + } + case QEvent::Wheel: + { + widgetWheelEvent( ( QWheelEvent * )event ); + break; + } + default: + break; + } + } + return false; +} + +/*! + Handle a mouse press event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMousePressEvent( QMouseEvent *mouseEvent ) +{ + transition( mouseEvent ); +} + +/*! + Handle a mouse move event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMouseMoveEvent( QMouseEvent *mouseEvent ) +{ + if ( pickRect().contains( mouseEvent->pos() ) ) + d_data->trackerPosition = mouseEvent->pos(); + else + d_data->trackerPosition = QPoint( -1, -1 ); + + if ( !isActive() ) + updateDisplay(); + + transition( mouseEvent ); +} + +/*! + Handle a enter event for the observed widget. + + \param event Qt event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetEnterEvent( QEvent *event ) +{ + transition( event ); +} + +/*! + Handle a leave event for the observed widget. + + \param event Qt event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetLeaveEvent( QEvent *event ) +{ + transition( event ); + + d_data->trackerPosition = QPoint( -1, -1 ); + if ( !isActive() ) + updateDisplay(); +} + +/*! + Handle a mouse relase event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMouseReleaseEvent( QMouseEvent *mouseEvent ) +{ + transition( mouseEvent ); +} + +/*! + Handle mouse double click event for the observed widget. + + \param mouseEvent Mouse event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetMouseDoubleClickEvent( QMouseEvent *mouseEvent ) +{ + transition( mouseEvent ); +} + + +/*! + Handle a wheel event for the observed widget. + + Move the last point of the selection in case of isActive() == true + + \param wheelEvent Wheel event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetKeyPressEvent(), widgetKeyReleaseEvent() +*/ +void QwtPicker::widgetWheelEvent( QWheelEvent *wheelEvent ) +{ + if ( pickRect().contains( wheelEvent->pos() ) ) + d_data->trackerPosition = wheelEvent->pos(); + else + d_data->trackerPosition = QPoint( -1, -1 ); + + updateDisplay(); + + transition( wheelEvent ); +} + +/*! + Handle a key press event for the observed widget. + + Selections can be completely done by the keyboard. The arrow keys + move the cursor, the abort key aborts a selection. All other keys + are handled by the current state machine. + + \param keyEvent Key event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyReleaseEvent(), stateMachine(), + QwtEventPattern::KeyPatternCode +*/ +void QwtPicker::widgetKeyPressEvent( QKeyEvent *keyEvent ) +{ + int dx = 0; + int dy = 0; + + int offset = 1; + if ( keyEvent->isAutoRepeat() ) + offset = 5; + + if ( keyMatch( KeyLeft, keyEvent ) ) + dx = -offset; + else if ( keyMatch( KeyRight, keyEvent ) ) + dx = offset; + else if ( keyMatch( KeyUp, keyEvent ) ) + dy = -offset; + else if ( keyMatch( KeyDown, keyEvent ) ) + dy = offset; + else if ( keyMatch( KeyAbort, keyEvent ) ) + { + reset(); + } + else + transition( keyEvent ); + + if ( dx != 0 || dy != 0 ) + { + const QRect rect = pickRect(); + const QPoint pos = parentWidget()->mapFromGlobal( QCursor::pos() ); + + int x = pos.x() + dx; + x = qMax( rect.left(), x ); + x = qMin( rect.right(), x ); + + int y = pos.y() + dy; + y = qMax( rect.top(), y ); + y = qMin( rect.bottom(), y ); + + QCursor::setPos( parentWidget()->mapToGlobal( QPoint( x, y ) ) ); + } +} + +/*! + Handle a key release event for the observed widget. + + Passes the event to the state machine. + + \param keyEvent Key event + + \sa eventFilter(), widgetMousePressEvent(), widgetMouseReleaseEvent(), + widgetMouseDoubleClickEvent(), widgetMouseMoveEvent(), + widgetWheelEvent(), widgetKeyPressEvent(), stateMachine() +*/ +void QwtPicker::widgetKeyReleaseEvent( QKeyEvent *keyEvent ) +{ + transition( keyEvent ); +} + +/*! + Passes an event to the state machine and executes the resulting + commands. Append and Move commands use the current position + of the cursor (QCursor::pos()). + + \param event Event +*/ +void QwtPicker::transition( const QEvent *event ) +{ + if ( !d_data->stateMachine ) + return; + + const QList<QwtPickerMachine::Command> commandList = + d_data->stateMachine->transition( *this, event ); + + QPoint pos; + switch ( event->type() ) + { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + { + const QMouseEvent *me = + static_cast< const QMouseEvent * >( event ); + pos = me->pos(); + break; + } + default: + pos = parentWidget()->mapFromGlobal( QCursor::pos() ); + } + + for ( int i = 0; i < commandList.count(); i++ ) + { + switch ( commandList[i] ) + { + case QwtPickerMachine::Begin: + { + begin(); + break; + } + case QwtPickerMachine::Append: + { + append( pos ); + break; + } + case QwtPickerMachine::Move: + { + move( pos ); + break; + } + case QwtPickerMachine::Remove: + { + remove(); + break; + } + case QwtPickerMachine::End: + { + end(); + break; + } + } + } +} + +/*! + Open a selection setting the state to active + + \sa isActive(), end(), append(), move() +*/ +void QwtPicker::begin() +{ + if ( d_data->isActive ) + return; + + d_data->pickedPoints.resize( 0 ); + d_data->isActive = true; + Q_EMIT activated( true ); + + if ( trackerMode() != AlwaysOff ) + { + if ( d_data->trackerPosition.x() < 0 || d_data->trackerPosition.y() < 0 ) + { + QWidget *w = parentWidget(); + if ( w ) + d_data->trackerPosition = w->mapFromGlobal( QCursor::pos() ); + } + } + + updateDisplay(); + setMouseTracking( true ); +} + +/*! + \brief Close a selection setting the state to inactive. + + The selection is validated and maybe fixed by accept(). + + \param ok If true, complete the selection and emit a selected signal + otherwise discard the selection. + \return true if the selection is accepted, false otherwise + \sa isActive(), begin(), append(), move(), selected(), accept() +*/ +bool QwtPicker::end( bool ok ) +{ + if ( d_data->isActive ) + { + setMouseTracking( false ); + + d_data->isActive = false; + Q_EMIT activated( false ); + + if ( trackerMode() == ActiveOnly ) + d_data->trackerPosition = QPoint( -1, -1 ); + + if ( ok ) + ok = accept( d_data->pickedPoints ); + + if ( ok ) + Q_EMIT selected( d_data->pickedPoints ); + else + d_data->pickedPoints.resize( 0 ); + + updateDisplay(); + } + else + ok = false; + + return ok; +} + +/*! + Reset the state machine and terminate (end(false)) the selection +*/ +void QwtPicker::reset() +{ + if ( d_data->stateMachine ) + d_data->stateMachine->reset(); + + if ( isActive() ) + end( false ); +} + +/*! + Append a point to the selection and update rubberband and tracker. + The appended() signal is emitted. + + \param pos Additional point + + \sa isActive(), begin(), end(), move(), appended() +*/ +void QwtPicker::append( const QPoint &pos ) +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count(); + d_data->pickedPoints.resize( idx + 1 ); + d_data->pickedPoints[idx] = pos; + + updateDisplay(); + Q_EMIT appended( pos ); + } +} + +/*! + Move the last point of the selection + The moved() signal is emitted. + + \param pos New position + \sa isActive(), begin(), end(), append() +*/ +void QwtPicker::move( const QPoint &pos ) +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count() - 1; + if ( idx >= 0 ) + { + if ( d_data->pickedPoints[idx] != pos ) + { + d_data->pickedPoints[idx] = pos; + + updateDisplay(); + Q_EMIT moved( pos ); + } + } + } +} + +/*! + Remove the last point of the selection + The removed() signal is emitted. + + \sa isActive(), begin(), end(), append(), move() +*/ +void QwtPicker::remove() +{ + if ( d_data->isActive ) + { + const int idx = d_data->pickedPoints.count() - 1; + if ( idx > 0 ) + { + const int idx = d_data->pickedPoints.count(); + + const QPoint pos = d_data->pickedPoints[idx - 1]; + d_data->pickedPoints.resize( idx - 1 ); + + updateDisplay(); + Q_EMIT removed( pos ); + } + } +} + +/*! + \brief Validate and fixup the selection + + Accepts all selections unmodified + + \param selection Selection to validate and fixup + \return true, when accepted, false otherwise +*/ +bool QwtPicker::accept( QPolygon &selection ) const +{ + Q_UNUSED( selection ); + return true; +} + +/*! + A picker is active between begin() and end(). + \return true if the selection is active. +*/ +bool QwtPicker::isActive() const +{ + return d_data->isActive; +} + +/*! + Return the points, that have been collected so far. The selection() + is calculated from the pickedPoints() in adjustedPoints(). + \return Picked points +*/ +const QPolygon &QwtPicker::pickedPoints() const +{ + return d_data->pickedPoints; +} + +/*! + Scale the selection by the ratios of oldSize and newSize + The changed() signal is emitted. + + \param oldSize Previous size + \param newSize Current size + + \sa ResizeMode, setResizeMode(), resizeMode() +*/ +void QwtPicker::stretchSelection( const QSize &oldSize, const QSize &newSize ) +{ + if ( oldSize.isEmpty() ) + { + // avoid division by zero. But scaling for small sizes also + // doesn't make much sense, because of rounding losses. TODO ... + return; + } + + const double xRatio = + double( newSize.width() ) / double( oldSize.width() ); + const double yRatio = + double( newSize.height() ) / double( oldSize.height() ); + + for ( int i = 0; i < int( d_data->pickedPoints.count() ); i++ ) + { + QPoint &p = d_data->pickedPoints[i]; + p.setX( qRound( p.x() * xRatio ) ); + p.setY( qRound( p.y() * yRatio ) ); + + Q_EMIT changed( d_data->pickedPoints ); + } +} + +/*! + Set mouse tracking for the observed widget. + + In case of enable is true, the previous value + is saved, that is restored when enable is false. + + \warning Even when enable is false, mouse tracking might be restored + to true. When mouseTracking for the observed widget + has been changed directly by QWidget::setMouseTracking + while mouse tracking has been set to true, this value can't + be restored. +*/ + +void QwtPicker::setMouseTracking( bool enable ) +{ + QWidget *widget = parentWidget(); + if ( !widget ) + return; + + if ( enable ) + { + d_data->mouseTracking = widget->hasMouseTracking(); + widget->setMouseTracking( true ); + } + else + { + widget->setMouseTracking( d_data->mouseTracking ); + } +} + +/*! + Find the area of the observed widget, where selection might happen. + + \return parentWidget()->contentsRect() +*/ +QRect QwtPicker::pickRect() const +{ + const QWidget *widget = parentWidget(); + if ( widget ) + return widget->contentsRect(); + + return QRect(); +} + +//! Update the state of rubberband and tracker label +void QwtPicker::updateDisplay() +{ + QWidget *w = parentWidget(); + + bool showRubberband = false; + bool showTracker = false; + if ( w && w->isVisible() && d_data->enabled ) + { + if ( rubberBand() != NoRubberBand && isActive() && + rubberBandPen().style() != Qt::NoPen ) + { + showRubberband = true; + } + + if ( trackerMode() == AlwaysOn || + ( trackerMode() == ActiveOnly && isActive() ) ) + { + if ( trackerPen() != Qt::NoPen ) + showTracker = true; + } + } + + QPointer<PickerWidget> &rw = d_data->rubberBandWidget; + if ( showRubberband ) + { + if ( rw.isNull() ) + { + rw = new PickerWidget( this, w, PickerWidget::RubberBand ); + rw->setObjectName( "PickerRubberBand" ); + rw->resize( w->size() ); + } + rw->updateMask(); + rw->update(); // Needed, when the mask doesn't change + } + else + delete rw; + + QPointer<PickerWidget> &tw = d_data->trackerWidget; + if ( showTracker ) + { + if ( tw.isNull() ) + { + tw = new PickerWidget( this, w, PickerWidget::Text ); + tw->setObjectName( "PickerTracker" ); + tw->resize( w->size() ); + } + tw->setFont( d_data->trackerFont ); + tw->updateMask(); + tw->update(); // Needed, when the mask doesn't change + } + else + delete tw; +} + +//! \return Widget displaying the rubberband +const QWidget *QwtPicker::rubberBandWidget() const +{ + return d_data->rubberBandWidget; +} + +//! \return Widget displaying the tracker text +const QWidget *QwtPicker::trackerWidget() const +{ + return d_data->trackerWidget; +} + diff --git a/src/libpcp_qwt/src/qwt_picker.h b/src/libpcp_qwt/src/qwt_picker.h new file mode 100644 index 0000000..c0b22dc --- /dev/null +++ b/src/libpcp_qwt/src/qwt_picker.h @@ -0,0 +1,327 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PICKER +#define QWT_PICKER 1 + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_event_pattern.h" +#include <qobject.h> +#include <qpen.h> +#include <qfont.h> +#include <qrect.h> + +class QWidget; +class QMouseEvent; +class QWheelEvent; +class QKeyEvent; +class QwtPickerMachine; + +/*! + \brief QwtPicker provides selections on a widget + + QwtPicker filters all enter, leave, mouse and keyboard events of a widget + and translates them into an array of selected points. + + The way how the points are collected depends on type of state machine + that is connected to the picker. Qwt offers a couple of predefined + state machines for selecting: + + - Nothing\n + QwtPickerTrackerMachine + - Single points\n + QwtPickerClickPointMachine, QwtPickerDragPointMachine + - Rectangles\n + QwtPickerClickRectMachine, QwtPickerDragRectMachine + - Polygons\n + QwtPickerPolygonMachine + + While these state machines cover the most common ways to collect points + it is also possible to implement individual machines as well. + + QwtPicker translates the picked points into a selection using the + adjustedPoints method. adjustedPoints is intended to be reimplemented + to fixup the selection according to application specific requirements. + (F.e. when an application accepts rectangles of a fixed aspect ratio only.) + + Optionally QwtPicker support the process of collecting points by a + rubberband and tracker displaying a text for the current mouse + position. + + \par Example + \verbatim #include <qwt_picker.h> +#include <qwt_picker_machine.h> + +QwtPicker *picker = new QwtPicker(widget); +picker->setStateMachine(new QwtPickerDragRectMachine); +picker->setTrackerMode(QwtPicker::ActiveOnly); +picker->setRubberBand(QwtPicker::RectRubberBand); \endverbatim\n + + The state machine triggers the following commands: + + - begin()\n + Activate/Initialize the selection. + - append()\n + Add a new point + - move() \n + Change the position of the last point. + - remove()\n + Remove the last point. + - end()\n + Terminate the selection and call accept to validate the picked points. + + The picker is active (isActive()), between begin() and end(). + In active state the rubberband is displayed, and the tracker is visible + in case of trackerMode is ActiveOnly or AlwaysOn. + + The cursor can be moved using the arrow keys. All selections can be aborted + using the abort key. (QwtEventPattern::KeyPatternCode) + + \warning In case of QWidget::NoFocus the focus policy of the observed + widget is set to QWidget::WheelFocus and mouse tracking + will be manipulated while the picker is active, + or if trackerMode() is AlwayOn. +*/ + +class QWT_EXPORT QwtPicker: public QObject, public QwtEventPattern +{ + Q_OBJECT + + Q_ENUMS( RubberBand ) + Q_ENUMS( DisplayMode ) + Q_ENUMS( ResizeMode ) + + Q_PROPERTY( bool isEnabled READ isEnabled WRITE setEnabled ) + Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode ) + + Q_PROPERTY( DisplayMode trackerMode READ trackerMode WRITE setTrackerMode ) + Q_PROPERTY( QPen trackerPen READ trackerPen WRITE setTrackerPen ) + Q_PROPERTY( QFont trackerFont READ trackerFont WRITE setTrackerFont ) + + Q_PROPERTY( RubberBand rubberBand READ rubberBand WRITE setRubberBand ) + Q_PROPERTY( QPen rubberBandPen READ rubberBandPen WRITE setRubberBandPen ) + +public: + /*! + Rubberband style + + The default value is QwtPicker::NoRubberBand. + \sa setRubberBand(), rubberBand() + */ + + enum RubberBand + { + //! No rubberband. + NoRubberBand = 0, + + //! A horizontal line ( only for QwtPicker::PointSelection ) + HLineRubberBand, + + //! A vertical line ( only for QwtPicker::PointSelection ) + VLineRubberBand, + + //! A crosshair ( only for QwtPicker::PointSelection ) + CrossRubberBand, + + //! A rectangle ( only for QwtPicker::RectSelection ) + RectRubberBand, + + //! An ellipse ( only for QwtPicker::RectSelection ) + EllipseRubberBand, + + //! A polygon ( only for QwtPicker::&PolygonSelection ) + PolygonRubberBand, + + /*! + Values >= UserRubberBand can be used to define additional + rubber bands. + */ + UserRubberBand = 100 + }; + + /*! + \brief Display mode + \sa setTrackerMode(), trackerMode(), isActive() + */ + enum DisplayMode + { + //! Display never + AlwaysOff, + + //! Display always + AlwaysOn, + + //! Display only when the selection is active + ActiveOnly + }; + + /*! + Controls what to do with the selected points of an active + selection when the observed widget is resized. + + The default value is QwtPicker::Stretch. + \sa setResizeMode() + */ + + enum ResizeMode + { + //! All points are scaled according to the new size, + Stretch, + + //! All points remain unchanged. + KeepSize + }; + + explicit QwtPicker( QWidget *parent ); + explicit QwtPicker( RubberBand rubberBand, + DisplayMode trackerMode, QWidget * ); + + virtual ~QwtPicker(); + + void setStateMachine( QwtPickerMachine * ); + const QwtPickerMachine *stateMachine() const; + QwtPickerMachine *stateMachine(); + + void setRubberBand( RubberBand ); + RubberBand rubberBand() const; + + void setTrackerMode( DisplayMode ); + DisplayMode trackerMode() const; + + void setResizeMode( ResizeMode ); + ResizeMode resizeMode() const; + + void setRubberBandPen( const QPen & ); + QPen rubberBandPen() const; + + void setTrackerPen( const QPen & ); + QPen trackerPen() const; + + void setTrackerFont( const QFont & ); + QFont trackerFont() const; + + bool isEnabled() const; + bool isActive() const; + + virtual bool eventFilter( QObject *, QEvent * ); + + QWidget *parentWidget(); + const QWidget *parentWidget() const; + + virtual QRect pickRect() const; + + virtual void drawRubberBand( QPainter * ) const; + virtual void drawTracker( QPainter * ) const; + + virtual QwtText trackerText( const QPoint &pos ) const; + QPoint trackerPosition() const; + virtual QRect trackerRect( const QFont & ) const; + + QPolygon selection() const; + +public Q_SLOTS: + void setEnabled( bool ); + +Q_SIGNALS: + /*! + A signal indicating, when the picker has been activated. + Together with setEnabled() it can be used to implement + selections with more than one picker. + + \param on True, when the picker has been activated + */ + void activated( bool on ); + + /*! + A signal emitting the selected points, + at the end of a selection. + + \param polygon Selected points + */ + void selected( const QPolygon &polygon ); + + /*! + A signal emitted when a point has been appended to the selection + + \param pos Position of the appended point. + \sa append(). moved() + */ + void appended( const QPoint &pos ); + + /*! + A signal emitted whenever the last appended point of the + selection has been moved. + + \param pos Position of the moved last point of the selection. + \sa move(), appended() + */ + void moved( const QPoint &pos ); + + /*! + A signal emitted whenever the last appended point of the + selection has been removed. + + \sa remove(), appended() + */ + void removed( const QPoint &pos ); + /*! + A signal emitted when the active selection has been changed. + This might happen when the observed widget is resized. + + \param selection Changed selection + \sa stretchSelection() + */ + void changed( const QPolygon &selection ); + +protected: + virtual QPolygon adjustedPoints( const QPolygon & ) const; + + virtual void transition( const QEvent * ); + + virtual void begin(); + virtual void append( const QPoint & ); + virtual void move( const QPoint & ); + virtual void remove(); + virtual bool end( bool ok = true ); + + virtual bool accept( QPolygon & ) const; + virtual void reset(); + + virtual void widgetMousePressEvent( QMouseEvent * ); + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetMouseDoubleClickEvent( QMouseEvent * ); + virtual void widgetMouseMoveEvent( QMouseEvent * ); + virtual void widgetWheelEvent( QWheelEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + virtual void widgetKeyReleaseEvent( QKeyEvent * ); + virtual void widgetEnterEvent( QEvent * ); + virtual void widgetLeaveEvent( QEvent * ); + + virtual void stretchSelection( const QSize &oldSize, + const QSize &newSize ); + + virtual void updateDisplay(); + + const QWidget *rubberBandWidget() const; + const QWidget *trackerWidget() const; + + const QPolygon &pickedPoints() const; + +private: + void init( QWidget *, RubberBand rubberBand, DisplayMode trackerMode ); + + void setMouseTracking( bool ); + + class PickerWidget; + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_picker_machine.cpp b/src/libpcp_qwt/src/qwt_picker_machine.cpp new file mode 100644 index 0000000..d43cc88 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_picker_machine.cpp @@ -0,0 +1,455 @@ +/* -*- 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_picker_machine.h" +#include "qwt_event_pattern.h" +#include <qevent.h> + +//! Constructor +QwtPickerMachine::QwtPickerMachine( SelectionType type ): + d_selectionType( type ), + d_state( 0 ) +{ +} + +//! Destructor +QwtPickerMachine::~QwtPickerMachine() +{ +} + +//! Return the selection type +QwtPickerMachine::SelectionType QwtPickerMachine::selectionType() const +{ + return d_selectionType; +} + +//! Return the current state +int QwtPickerMachine::state() const +{ + return d_state; +} + +//! Change the current state +void QwtPickerMachine::setState( int state ) +{ + d_state = state; +} + +//! Set the current state to 0. +void QwtPickerMachine::reset() +{ + setState( 0 ); +} + +//! Constructor +QwtPickerTrackerMachine::QwtPickerTrackerMachine(): + QwtPickerMachine( NoSelection ) +{ +} + +//! Transition +QList<QwtPickerMachine::Command> QwtPickerTrackerMachine::transition( + const QwtEventPattern &, const QEvent *e ) +{ + QList<QwtPickerMachine::Command> cmdList; + + switch ( e->type() ) + { + case QEvent::Enter: + case QEvent::MouseMove: + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Move; + } + break; + } + case QEvent::Leave: + { + cmdList += Remove; + cmdList += End; + setState( 0 ); + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerClickPointMachine::QwtPickerClickPointMachine(): + QwtPickerMachine( PointSelection ) +{ +} + +//! Transition +QList<QwtPickerMachine::Command> QwtPickerClickPointMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList<QwtPickerMachine::Command> cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect1, ( const QMouseEvent * )event ) ) + { + cmdList += Begin; + cmdList += Append; + cmdList += End; + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( + QwtEventPattern::KeySelect1, ( const QKeyEvent * )event ) ) + { + cmdList += Begin; + cmdList += Append; + cmdList += End; + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerDragPointMachine::QwtPickerDragPointMachine(): + QwtPickerMachine( PointSelection ) +{ +} + +//! Transition +QList<QwtPickerMachine::Command> QwtPickerDragPointMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList<QwtPickerMachine::Command> cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect1, ( const QMouseEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() != 0 ) + { + cmdList += End; + setState( 0 ); + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( + QwtEventPattern::KeySelect1, ( const QKeyEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerClickRectMachine::QwtPickerClickRectMachine(): + QwtPickerMachine( RectSelection ) +{ +} + +//! Transition +QList<QwtPickerMachine::Command> QwtPickerClickRectMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList<QwtPickerMachine::Command> cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect1, ( const QMouseEvent * )event ) ) + { + switch ( state() ) + { + case 0: + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + break; + } + case 1: + { + // Uh, strange we missed the MouseButtonRelease + break; + } + default: + { + cmdList += End; + setState( 0 ); + } + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect1, ( const QMouseEvent * )event ) ) + { + if ( state() == 1 ) + { + cmdList += Append; + setState( 2 ); + } + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( + QwtEventPattern::KeySelect1, ( const QKeyEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + setState( 1 ); + } + else + { + if ( state() == 1 ) + { + cmdList += Append; + setState( 2 ); + } + else if ( state() == 2 ) + { + cmdList += End; + setState( 0 ); + } + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerDragRectMachine::QwtPickerDragRectMachine(): + QwtPickerMachine( RectSelection ) +{ +} + +//! Transition +QList<QwtPickerMachine::Command> QwtPickerDragRectMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList<QwtPickerMachine::Command> cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect1, ( const QMouseEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 2 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::MouseButtonRelease: + { + if ( state() == 2 ) + { + cmdList += End; + setState( 0 ); + } + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( + QwtEventPattern::KeySelect1, ( const QKeyEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 2 ); + } + else + { + cmdList += End; + setState( 0 ); + } + } + break; + } + default: + break; + } + + return cmdList; +} + +//! Constructor +QwtPickerPolygonMachine::QwtPickerPolygonMachine(): + QwtPickerMachine( PolygonSelection ) +{ +} + +//! Transition +QList<QwtPickerMachine::Command> QwtPickerPolygonMachine::transition( + const QwtEventPattern &eventPattern, const QEvent *event ) +{ + QList<QwtPickerMachine::Command> cmdList; + + switch ( event->type() ) + { + case QEvent::MouseButtonPress: + { + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect1, ( const QMouseEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Append; + } + } + if ( eventPattern.mouseMatch( + QwtEventPattern::MouseSelect2, ( const QMouseEvent * )event ) ) + { + if ( state() == 1 ) + { + cmdList += End; + setState( 0 ); + } + } + break; + } + case QEvent::MouseMove: + case QEvent::Wheel: + { + if ( state() != 0 ) + cmdList += Move; + break; + } + case QEvent::KeyPress: + { + if ( eventPattern.keyMatch( + QwtEventPattern::KeySelect1, ( const QKeyEvent * )event ) ) + { + if ( state() == 0 ) + { + cmdList += Begin; + cmdList += Append; + cmdList += Append; + setState( 1 ); + } + else + { + cmdList += Append; + } + } + else if ( eventPattern.keyMatch( + QwtEventPattern::KeySelect2, ( const QKeyEvent * )event ) ) + { + if ( state() == 1 ) + { + cmdList += End; + setState( 0 ); + } + } + break; + } + default: + break; + } + + return cmdList; +} diff --git a/src/libpcp_qwt/src/qwt_picker_machine.h b/src/libpcp_qwt/src/qwt_picker_machine.h new file mode 100644 index 0000000..8527115 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_picker_machine.h @@ -0,0 +1,190 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PICKER_MACHINE +#define QWT_PICKER_MACHINE 1 + +#include "qwt_global.h" +#include <qlist.h> + +class QEvent; +class QwtEventPattern; + +/*! + \brief A state machine for QwtPicker selections + + QwtPickerMachine accepts key and mouse events and translates them + into selection commands. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerMachine +{ +public: + /*! + Type of a selection. + \sa selectionType() + */ + enum SelectionType + { + //! The state machine not usable for any type of selection. + NoSelection = -1, + + //! The state machine is for selecting a single point. + PointSelection, + + //! The state machine is for selecting a rectangle (2 points). + RectSelection, + + //! The state machine is for selecting a polygon (many points). + PolygonSelection + }; + + //! Commands - the output of a state machine + enum Command + { + Begin, + Append, + Move, + Remove, + End + }; + + QwtPickerMachine( SelectionType ); + virtual ~QwtPickerMachine(); + + //! Transition + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ) = 0; + void reset(); + + int state() const; + void setState( int ); + + SelectionType selectionType() const; + +private: + const SelectionType d_selectionType; + int d_state; +}; + +/*! + \brief A state machine for indicating mouse movements + + QwtPickerTrackerMachine supports displaying information + corresponding to mouse movements, but is not intended for + selecting anything. Begin/End are related to Enter/Leave events. +*/ +class QWT_EXPORT QwtPickerTrackerMachine: public QwtPickerMachine +{ +public: + QwtPickerTrackerMachine(); + + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for point selections + + Pressing QwtEventPattern::MouseSelect1 or + QwtEventPattern::KeySelect1 selects a point. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ +class QWT_EXPORT QwtPickerClickPointMachine: public QwtPickerMachine +{ +public: + QwtPickerClickPointMachine(); + + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for point selections + + Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 + starts the selection, releasing QwtEventPattern::MouseSelect1 or + a second press of QwtEventPattern::KeySelect1 terminates it. +*/ +class QWT_EXPORT QwtPickerDragPointMachine: public QwtPickerMachine +{ +public: + QwtPickerDragPointMachine(); + + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for rectangle selections + + Pressing QwtEventPattern::MouseSelect1 starts + the selection, releasing it selects the first point. Pressing it + again selects the second point and terminates the selection. + Pressing QwtEventPattern::KeySelect1 also starts the + selection, a second press selects the first point. A third one selects + the second point and terminates the selection. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerClickRectMachine: public QwtPickerMachine +{ +public: + QwtPickerClickRectMachine(); + + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for rectangle selections + + Pressing QwtEventPattern::MouseSelect1 selects + the first point, releasing it the second point. + Pressing QwtEventPattern::KeySelect1 also selects the + first point, a second press selects the second point and terminates + the selection. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerDragRectMachine: public QwtPickerMachine +{ +public: + QwtPickerDragRectMachine(); + + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ); +}; + +/*! + \brief A state machine for polygon selections + + Pressing QwtEventPattern::MouseSelect1 or QwtEventPattern::KeySelect1 + starts the selection and selects the first point, or appends a point. + Pressing QwtEventPattern::MouseSelect2 or QwtEventPattern::KeySelect2 + appends the last point and terminates the selection. + + \sa QwtEventPattern::MousePatternCode, QwtEventPattern::KeyPatternCode +*/ + +class QWT_EXPORT QwtPickerPolygonMachine: public QwtPickerMachine +{ +public: + QwtPickerPolygonMachine(); + + virtual QList<Command> transition( + const QwtEventPattern &, const QEvent * ); +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot.cpp b/src/libpcp_qwt/src/qwt_plot.cpp new file mode 100644 index 0000000..1e2fb3e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot.cpp @@ -0,0 +1,751 @@ +/* -*- 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_plot.h" +#include "qwt_plot_dict.h" +#include "qwt_plot_layout.h" +#include "qwt_scale_widget.h" +#include "qwt_scale_engine.h" +#include "qwt_text_label.h" +#include "qwt_legend.h" +#include "qwt_dyngrid_layout.h" +#include "qwt_plot_canvas.h" +#include <qpainter.h> +#include <qpointer.h> +#include <qpaintengine.h> +#include <qapplication.h> +#include <qevent.h> + +class QwtPlot::PrivateData +{ +public: + QPointer<QwtTextLabel> titleLabel; + QPointer<QwtPlotCanvas> canvas; + QPointer<QwtLegend> legend; + QwtPlotLayout *layout; + + bool autoReplot; +}; + +/*! + \brief Constructor + \param parent Parent widget + */ +QwtPlot::QwtPlot( QWidget *parent ): + QFrame( parent ) +{ + initPlot( QwtText() ); +} + +/*! + \brief Constructor + \param title Title text + \param parent Parent widget + */ +QwtPlot::QwtPlot( const QwtText &title, QWidget *parent ): + QFrame( parent ) +{ + initPlot( title ); +} + +//! Destructor +QwtPlot::~QwtPlot() +{ + detachItems( QwtPlotItem::Rtti_PlotItem, autoDelete() ); + + delete d_data->layout; + deleteAxesData(); + delete d_data; +} + +/*! + \brief Initializes a QwtPlot instance + \param title Title text + */ +void QwtPlot::initPlot( const QwtText &title ) +{ + d_data = new PrivateData; + + d_data->layout = new QwtPlotLayout; + d_data->autoReplot = false; + + // title + d_data->titleLabel = new QwtTextLabel( this ); + d_data->titleLabel->setObjectName( "QwtPlotTitle" ); + d_data->titleLabel->setFont( QFont( fontInfo().family(), 14, QFont::Bold ) ); + + QwtText text( title ); + text.setRenderFlags( Qt::AlignCenter | Qt::TextWordWrap ); + d_data->titleLabel->setText( text ); + + // legend + d_data->legend = NULL; + + // axis + initAxesData(); + + // canvas + d_data->canvas = new QwtPlotCanvas( this ); + d_data->canvas->setObjectName( "QwtPlotCanvas" ); + d_data->canvas->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + d_data->canvas->setLineWidth( 2 ); + + updateTabOrder(); + + setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding ); + + resize( 200, 200 ); +} + +/*! + \brief Adds handling of layout requests + \param event Event +*/ +bool QwtPlot::event( QEvent *event ) +{ + bool ok = QFrame::event( event ); + switch ( event->type() ) + { + case QEvent::LayoutRequest: + updateLayout(); + break; + case QEvent::PolishRequest: + replot(); + break; + default:; + } + return ok; +} + +//! Replots the plot if autoReplot() is \c true. +void QwtPlot::autoRefresh() +{ + if ( d_data->autoReplot ) + replot(); +} + +/*! + \brief Set or reset the autoReplot option + + If the autoReplot option is set, the plot will be + updated implicitly by manipulating member functions. + Since this may be time-consuming, it is recommended + to leave this option switched off and call replot() + explicitly if necessary. + + The autoReplot option is set to false by default, which + means that the user has to call replot() in order to make + changes visible. + \param tf \c true or \c false. Defaults to \c true. + \sa replot() +*/ +void QwtPlot::setAutoReplot( bool tf ) +{ + d_data->autoReplot = tf; +} + +/*! + \return true if the autoReplot option is set. + \sa setAutoReplot() +*/ +bool QwtPlot::autoReplot() const +{ + return d_data->autoReplot; +} + +/*! + Change the plot's title + \param title New title +*/ +void QwtPlot::setTitle( const QString &title ) +{ + if ( title != d_data->titleLabel->text().text() ) + { + d_data->titleLabel->setText( title ); + updateLayout(); + } +} + +/*! + Change the plot's title + \param title New title +*/ +void QwtPlot::setTitle( const QwtText &title ) +{ + if ( title != d_data->titleLabel->text() ) + { + d_data->titleLabel->setText( title ); + updateLayout(); + } +} + +//! \return Title of the plot +QwtText QwtPlot::title() const +{ + return d_data->titleLabel->text(); +} + +//! \return the plot's title +QwtPlotLayout *QwtPlot::plotLayout() +{ + return d_data->layout; +} + +//! \return the plot's layout +const QwtPlotLayout *QwtPlot::plotLayout() const +{ + return d_data->layout; +} + +//! \return the plot's titel label. +QwtTextLabel *QwtPlot::titleLabel() +{ + return d_data->titleLabel; +} + +/*! + \return the plot's titel label. +*/ +const QwtTextLabel *QwtPlot::titleLabel() const +{ + return d_data->titleLabel; +} + +/*! + \return the plot's legend + \sa insertLegend() +*/ +QwtLegend *QwtPlot::legend() +{ + return d_data->legend; +} + +/*! + \return the plot's legend + \sa insertLegend() +*/ +const QwtLegend *QwtPlot::legend() const +{ + return d_data->legend; +} + + +/*! + \return the plot's canvas +*/ +QwtPlotCanvas *QwtPlot::canvas() +{ + return d_data->canvas; +} + +/*! + \return the plot's canvas +*/ +const QwtPlotCanvas *QwtPlot::canvas() const +{ + return d_data->canvas; +} + +/*! + Return sizeHint + \sa minimumSizeHint() +*/ + +QSize QwtPlot::sizeHint() const +{ + int dw = 0; + int dh = 0; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( axisEnabled( axisId ) ) + { + const int niceDist = 40; + const QwtScaleWidget *scaleWidget = axisWidget( axisId ); + const QwtScaleDiv &scaleDiv = scaleWidget->scaleDraw()->scaleDiv(); + const int majCnt = scaleDiv.ticks( QwtScaleDiv::MajorTick ).count(); + + if ( axisId == yLeft || axisId == yRight ) + { + int hDiff = ( majCnt - 1 ) * niceDist + - scaleWidget->minimumSizeHint().height(); + if ( hDiff > dh ) + dh = hDiff; + } + else + { + int wDiff = ( majCnt - 1 ) * niceDist + - scaleWidget->minimumSizeHint().width(); + if ( wDiff > dw ) + dw = wDiff; + } + } + } + return minimumSizeHint() + QSize( dw, dh ); +} + +/*! + \brief Return a minimum size hint +*/ +QSize QwtPlot::minimumSizeHint() const +{ + QSize hint = d_data->layout->minimumSizeHint( this ); + hint += QSize( 2 * frameWidth(), 2 * frameWidth() ); + + return hint; +} + +/*! + Resize and update internal layout + \param e Resize event +*/ +void QwtPlot::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + updateLayout(); +} + +/*! + \brief Redraw the plot + + If the autoReplot option is not set (which is the default) + or if any curves are attached to raw data, the plot has to + be refreshed explicitly in order to make changes visible. + + \sa setAutoReplot() + \warning Calls canvas()->repaint, take care of infinite recursions +*/ +void QwtPlot::replot() +{ + bool doAutoReplot = autoReplot(); + setAutoReplot( false ); + + updateAxes(); + + /* + Maybe the layout needs to be updated, because of changed + axes labels. We need to process them here before painting + to avoid that scales and canvas get out of sync. + */ + QApplication::sendPostedEvents( this, QEvent::LayoutRequest ); + + d_data->canvas->replot(); + + setAutoReplot( doAutoReplot ); +} + +/*! + \brief Adjust plot content to its current size. + \sa resizeEvent() +*/ +void QwtPlot::updateLayout() +{ + d_data->layout->activate( this, contentsRect() ); + + QRect titleRect = d_data->layout->titleRect().toRect(); + QRect scaleRect[QwtPlot::axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + scaleRect[axisId] = d_data->layout->scaleRect( axisId ).toRect(); + QRect legendRect = d_data->layout->legendRect().toRect(); + QRect canvasRect = d_data->layout->canvasRect().toRect(); + + // resize and show the visible widgets + if ( !d_data->titleLabel->text().isEmpty() ) + { + d_data->titleLabel->setGeometry( titleRect ); + if ( !d_data->titleLabel->isVisibleTo( this ) ) + d_data->titleLabel->show(); + } + else + d_data->titleLabel->hide(); + + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + if ( axisEnabled( axisId ) ) + { + axisWidget( axisId )->setGeometry( scaleRect[axisId] ); + +#if 1 + if ( axisId == xBottom || axisId == xTop ) + { + // do we need this code any longer ??? + + QRegion r( scaleRect[axisId] ); + if ( axisEnabled( yLeft ) ) + r = r.subtract( QRegion( scaleRect[yLeft] ) ); + if ( axisEnabled( yRight ) ) + r = r.subtract( QRegion( scaleRect[yRight] ) ); + r.translate( -scaleRect[ axisId ].x(), + -scaleRect[axisId].y() ); + + axisWidget( axisId )->setMask( r ); + } +#endif + if ( !axisWidget( axisId )->isVisibleTo( this ) ) + axisWidget( axisId )->show(); + } + else + axisWidget( axisId )->hide(); + } + + if ( d_data->legend && + d_data->layout->legendPosition() != ExternalLegend ) + { + if ( d_data->legend->itemCount() > 0 ) + { + d_data->legend->setGeometry( legendRect ); + d_data->legend->show(); + } + else + d_data->legend->hide(); + } + + d_data->canvas->setGeometry( canvasRect ); +} + +/*! + Update the focus tab order + + The order is changed so that the canvas will be in front of the + first legend item, or behind the last legend item - depending + on the position of the legend. +*/ + +void QwtPlot::updateTabOrder() +{ + if ( d_data->canvas->focusPolicy() == Qt::NoFocus ) + return; + if ( d_data->legend.isNull() + || d_data->layout->legendPosition() == ExternalLegend + || d_data->legend->legendItems().count() == 0 ) + { + return; + } + + // Depending on the position of the legend the + // tab order will be changed that the canvas is + // next to the last legend item, or before + // the first one. + + const bool canvasFirst = + d_data->layout->legendPosition() == QwtPlot::BottomLegend || + d_data->layout->legendPosition() == QwtPlot::RightLegend; + + QWidget *previous = NULL; + + QWidget *w = d_data->canvas; + while ( ( w = w->nextInFocusChain() ) != d_data->canvas ) + { + bool isLegendItem = false; + if ( w->focusPolicy() != Qt::NoFocus + && w->parent() && w->parent() == d_data->legend->contentsWidget() ) + { + isLegendItem = true; + } + + if ( canvasFirst ) + { + if ( isLegendItem ) + break; + + previous = w; + } + else + { + if ( isLegendItem ) + previous = w; + else + { + if ( previous ) + break; + } + } + } + + if ( previous && previous != d_data->canvas ) + setTabOrder( previous, d_data->canvas ); +} + +/*! + Redraw the canvas. + \param painter Painter used for drawing + + \warning drawCanvas calls drawItems what is also used + for printing. Applications that like to add individual + plot items better overload drawItems() + \sa drawItems() +*/ +void QwtPlot::drawCanvas( QPainter *painter ) +{ + QwtScaleMap maps[axisCnt]; + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + maps[axisId] = canvasMap( axisId ); + + drawItems( painter, d_data->canvas->contentsRect(), maps ); +} + +/*! + Redraw the canvas items. + \param painter Painter used for drawing + \param canvasRect Bounding rectangle where to paint + \param map QwtPlot::axisCnt maps, mapping between plot and paint device coordinates +*/ + +void QwtPlot::drawItems( QPainter *painter, const QRectF &canvasRect, + const QwtScaleMap map[axisCnt] ) const +{ + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item && item->isVisible() ) + { + painter->save(); + + painter->setRenderHint( QPainter::Antialiasing, + item->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + + item->draw( painter, + map[item->xAxis()], map[item->yAxis()], + canvasRect ); + + painter->restore(); + } + } +} + +/*! + \param axisId Axis + \return Map for the axis on the canvas. With this map pixel coordinates can + translated to plot coordinates and vice versa. + \sa QwtScaleMap, transform(), invTransform() + +*/ +QwtScaleMap QwtPlot::canvasMap( int axisId ) const +{ + QwtScaleMap map; + if ( !d_data->canvas ) + return map; + + map.setTransformation( axisScaleEngine( axisId )->transformation() ); + + const QwtScaleDiv *sd = axisScaleDiv( axisId ); + map.setScaleInterval( sd->lowerBound(), sd->upperBound() ); + + if ( axisEnabled( axisId ) ) + { + const QwtScaleWidget *s = axisWidget( axisId ); + if ( axisId == yLeft || axisId == yRight ) + { + double y = s->y() + s->startBorderDist() - d_data->canvas->y(); + double h = s->height() - s->startBorderDist() - s->endBorderDist(); + map.setPaintInterval( y + h, y ); + } + else + { + double x = s->x() + s->startBorderDist() - d_data->canvas->x(); + double w = s->width() - s->startBorderDist() - s->endBorderDist(); + map.setPaintInterval( x, x + w ); + } + } + else + { + int margin = 0; + if ( !plotLayout()->alignCanvasToScales() ) + margin = plotLayout()->canvasMargin( axisId ); + + const QRect &canvasRect = d_data->canvas->contentsRect(); + if ( axisId == yLeft || axisId == yRight ) + { + map.setPaintInterval( canvasRect.bottom() - margin, + canvasRect.top() + margin ); + } + else + { + map.setPaintInterval( canvasRect.left() + margin, + canvasRect.right() - margin ); + } + } + return map; +} + +/*! + \brief Change the background of the plotting area + + Sets brush to QPalette::Window of all colorgroups of + the palette of the canvas. Using canvas()->setPalette() + is a more powerful way to set these colors. + + \param brush New background brush + \sa canvasBackground() +*/ +void QwtPlot::setCanvasBackground( const QBrush &brush ) +{ + QPalette pal = d_data->canvas->palette(); + pal.setBrush( QPalette::Window, brush ); + + canvas()->setPalette( pal ); +} + +/*! + Nothing else than: canvas()->palette().brush( + QPalette::Normal, QPalette::Window); + + \return Background brush of the plotting area. + \sa setCanvasBackground() +*/ +QBrush QwtPlot::canvasBackground() const +{ + return canvas()->palette().brush( + QPalette::Normal, QPalette::Window ); +} + +/*! + \brief Change the border width of the plotting area + + Nothing else than canvas()->setLineWidth(w), + left for compatibility only. + + \param width New border width +*/ +void QwtPlot::setCanvasLineWidth( int width ) +{ + canvas()->setLineWidth( width ); + updateLayout(); +} + +/*! + Nothing else than: canvas()->lineWidth(), + left for compatibility only. + + \return the border width of the plotting area +*/ +int QwtPlot::canvasLineWidth() const +{ + return canvas()->lineWidth(); +} + +/*! + \return \c true if the specified axis exists, otherwise \c false + \param axisId axis index + */ +bool QwtPlot::axisValid( int axisId ) +{ + return ( ( axisId >= QwtPlot::yLeft ) && ( axisId < QwtPlot::axisCnt ) ); +} + +/*! + Called internally when the legend has been clicked on. + Emits a legendClicked() signal. +*/ +void QwtPlot::legendItemClicked() +{ + if ( d_data->legend && sender()->isWidgetType() ) + { + QwtPlotItem *plotItem = + ( QwtPlotItem* )d_data->legend->find( ( QWidget * )sender() ); + if ( plotItem ) + Q_EMIT legendClicked( plotItem ); + } +} + +/*! + Called internally when the legend has been checked + Emits a legendClicked() signal. +*/ +void QwtPlot::legendItemChecked( bool on ) +{ + if ( d_data->legend && sender()->isWidgetType() ) + { + QwtPlotItem *plotItem = + ( QwtPlotItem* )d_data->legend->find( ( QWidget * )sender() ); + if ( plotItem ) + Q_EMIT legendChecked( plotItem, on ); + } +} + +/*! + \brief Insert a legend + + If the position legend is \c QwtPlot::LeftLegend or \c QwtPlot::RightLegend + the legend will be organized in one column from top to down. + Otherwise the legend items will be placed in a table + with a best fit number of columns from left to right. + + If pos != QwtPlot::ExternalLegend the plot widget will become + parent of the legend. It will be deleted when the plot is deleted, + or another legend is set with insertLegend(). + + \param legend Legend + \param pos The legend's position. For top/left position the number + of colums will be limited to 1, otherwise it will be set to + unlimited. + + \param ratio Ratio between legend and the bounding rect + of title, canvas and axes. The legend will be shrinked + if it would need more space than the given ratio. + The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 + it will be reset to the default ratio. + The default vertical/horizontal ratio is 0.33/0.5. + + \sa legend(), QwtPlotLayout::legendPosition(), + QwtPlotLayout::setLegendPosition() +*/ +void QwtPlot::insertLegend( QwtLegend *legend, + QwtPlot::LegendPosition pos, double ratio ) +{ + d_data->layout->setLegendPosition( pos, ratio ); + + if ( legend != d_data->legend ) + { + if ( d_data->legend && d_data->legend->parent() == this ) + delete d_data->legend; + + d_data->legend = legend; + + if ( d_data->legend ) + { + if ( pos != ExternalLegend ) + { + if ( d_data->legend->parent() != this ) + d_data->legend->setParent( this ); + } + + const QwtPlotItemList& itmList = itemList(); + for ( QwtPlotItemIterator it = itmList.begin(); + it != itmList.end(); ++it ) + { + ( *it )->updateLegend( d_data->legend ); + } + + QwtDynGridLayout *tl = qobject_cast<QwtDynGridLayout *>( + d_data->legend->contentsWidget()->layout() ); + if ( tl ) + { + switch ( d_data->layout->legendPosition() ) + { + case LeftLegend: + case RightLegend: + tl->setMaxCols( 1 ); // 1 column: align vertical + break; + case TopLegend: + case BottomLegend: + tl->setMaxCols( 0 ); // unlimited + break; + case ExternalLegend: + break; + } + } + } + updateTabOrder(); + } + + updateLayout(); +} diff --git a/src/libpcp_qwt/src/qwt_plot.h b/src/libpcp_qwt/src/qwt_plot.h new file mode 100644 index 0000000..8a81f9b --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot.h @@ -0,0 +1,290 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_H +#define QWT_PLOT_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_plot_dict.h" +#include "qwt_scale_map.h" +#include "qwt_interval.h" +#include <qframe.h> + +class QwtPlotLayout; +class QwtLegend; +class QwtScaleWidget; +class QwtScaleEngine; +class QwtScaleDiv; +class QwtScaleDraw; +class QwtTextLabel; +class QwtPlotCanvas; + +/*! + \brief A 2-D plotting widget + + QwtPlot is a widget for plotting two-dimensional graphs. + An unlimited number of plot items can be displayed on + its canvas. Plot items might be curves (QwtPlotCurve), markers + (QwtPlotMarker), the grid (QwtPlotGrid), or anything else derived + from QwtPlotItem. + A plot can have up to four axes, with each plot item attached to an x- and + a y axis. The scales at the axes can be explicitely set (QwtScaleDiv), or + are calculated from the plot items, using algorithms (QwtScaleEngine) which + can be configured separately for each axis. + + \image html plot.png + + \par Example + The following example shows (schematically) the most simple + way to use QwtPlot. By default, only the left and bottom axes are + visible and their scales are computed automatically. + \verbatim +#include <qwt_plot.h> +#include <qwt_plot_curve.h> + +QwtPlot *myPlot = new QwtPlot("Two Curves", parent); + +// add curves +QwtPlotCurve *curve1 = new QwtPlotCurve("Curve 1"); +QwtPlotCurve *curve2 = new QwtPlotCurve("Curve 2"); + +// connect or copy the data to the curves +curve1->setData(...); +curve2->setData(...); + +curve1->attach(myPlot); +curve2->attach(myPlot); + +// finally, refresh the plot +myPlot->replot(); +\endverbatim +*/ + +class QWT_EXPORT QwtPlot: public QFrame, public QwtPlotDict +{ + Q_OBJECT + Q_PROPERTY( QString propertiesDocument + READ grabProperties WRITE applyProperties ) + +public: + //! \brief Axis index + enum Axis + { + //! Y axis left of the canvas + yLeft, + + //! Y axis right of the canvas + yRight, + + //! X axis below the canvas + xBottom, + + //! X axis above the canvas + xTop, + + //! Number of axes + axisCnt + }; + + /*! + Position of the legend, relative to the canvas. + + \sa insertLegend() + \note In case of ExternalLegend, the legend is not + handled by QwtPlotRenderer + */ + enum LegendPosition + { + //! The legend will be left from the QwtPlot::yLeft axis. + LeftLegend, + + //! The legend will be right from the QwtPlot::yRight axis. + RightLegend, + + //! The legend will be below QwtPlot::xBottom axis. + BottomLegend, + + //! The legend will be between QwtPlot::xTop axis and the title. + TopLegend, + + /*! + External means that only the content of the legend + will be handled by QwtPlot, but not its geometry. + This type can be used to have a legend in an + external window ( or on the canvas ). + */ + ExternalLegend + }; + + explicit QwtPlot( QWidget * = NULL ); + explicit QwtPlot( const QwtText &title, QWidget *p = NULL ); + + virtual ~QwtPlot(); + + void applyProperties( const QString & ); + QString grabProperties() const; + + void setAutoReplot( bool tf = true ); + bool autoReplot() const; + + // Layout + + QwtPlotLayout *plotLayout(); + const QwtPlotLayout *plotLayout() const; + + // Title + + void setTitle( const QString & ); + void setTitle( const QwtText &t ); + QwtText title() const; + + QwtTextLabel *titleLabel(); + const QwtTextLabel *titleLabel() const; + + // Canvas + + QwtPlotCanvas *canvas(); + const QwtPlotCanvas *canvas() const; + + void setCanvasBackground( const QBrush & ); + QBrush canvasBackground() const; + + void setCanvasLineWidth( int w ); + int canvasLineWidth() const; + + virtual QwtScaleMap canvasMap( int axisId ) const; + + double invTransform( int axisId, int pos ) const; + double transform( int axisId, double value ) const; + + // Axes + + QwtScaleEngine *axisScaleEngine( int axisId ); + const QwtScaleEngine *axisScaleEngine( int axisId ) const; + void setAxisScaleEngine( int axisId, QwtScaleEngine * ); + + void setAxisAutoScale( int axisId, bool on = true ); + bool axisAutoScale( int axisId ) const; + + void enableAxis( int axisId, bool tf = true ); + bool axisEnabled( int axisId ) const; + + void setAxisFont( int axisId, const QFont &f ); + QFont axisFont( int axisId ) const; + + void setAxisScale( int axisId, double min, double max, double step = 0 ); + void setAxisScaleDiv( int axisId, const QwtScaleDiv & ); + void setAxisScaleDraw( int axisId, QwtScaleDraw * ); + + double axisStepSize( int axisId ) const; + QwtInterval axisInterval( int axisId ) const; + + const QwtScaleDiv *axisScaleDiv( int axisId ) const; + QwtScaleDiv *axisScaleDiv( int axisId ); + + const QwtScaleDraw *axisScaleDraw( int axisId ) const; + QwtScaleDraw *axisScaleDraw( int axisId ); + + const QwtScaleWidget *axisWidget( int axisId ) const; + QwtScaleWidget *axisWidget( int axisId ); + + void setAxisLabelAlignment( int axisId, Qt::Alignment ); + void setAxisLabelRotation( int axisId, double rotation ); + + void setAxisTitle( int axisId, const QString & ); + void setAxisTitle( int axisId, const QwtText & ); + QwtText axisTitle( int axisId ) const; + + void setAxisMaxMinor( int axisId, int maxMinor ); + int axisMaxMinor( int axisId ) const; + + void setAxisMaxMajor( int axisId, int maxMajor ); + int axisMaxMajor( int axisId ) const; + + // Legend + + void insertLegend( QwtLegend *, LegendPosition = QwtPlot::RightLegend, + double ratio = -1.0 ); + + QwtLegend *legend(); + const QwtLegend *legend() const; + + // Misc + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + virtual void updateLayout(); + virtual void drawCanvas( QPainter * ); + + void updateAxes(); + + virtual bool event( QEvent * ); + + virtual void drawItems( QPainter *, const QRectF &, + const QwtScaleMap maps[axisCnt] ) const; + +Q_SIGNALS: + /*! + A signal which is emitted when the user has clicked on + a legend item, which is in QwtLegend::ClickableItem mode. + + \param plotItem Corresponding plot item of the + selected legend item + + \note clicks are disabled as default + \sa QwtLegend::setItemMode(), QwtLegend::itemMode() + */ + void legendClicked( QwtPlotItem *plotItem ); + + /*! + A signal which is emitted when the user has clicked on + a legend item, which is in QwtLegend::CheckableItem mode + + \param plotItem Corresponding plot item of the + selected legend item + \param on True when the legen item is checked + + \note clicks are disabled as default + \sa QwtLegend::setItemMode(), QwtLegend::itemMode() + */ + + void legendChecked( QwtPlotItem *plotItem, bool on ); + +public Q_SLOTS: + virtual void replot(); + void autoRefresh(); + +protected Q_SLOTS: + virtual void legendItemClicked(); + virtual void legendItemChecked( bool ); + +protected: + static bool axisValid( int axisId ); + + virtual void updateTabOrder(); + + virtual void resizeEvent( QResizeEvent *e ); + +private: + void initAxesData(); + void deleteAxesData(); + void updateScaleDiv(); + + void initPlot( const QwtText &title ); + + class AxisData; + AxisData *d_axisData[axisCnt]; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_axis.cpp b/src/libpcp_qwt/src/qwt_plot_axis.cpp new file mode 100644 index 0000000..66f2fa1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_axis.cpp @@ -0,0 +1,670 @@ +/* -*- 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_plot.h" +#include "qwt_math.h" +#include "qwt_scale_widget.h" +#include "qwt_scale_div.h" +#include "qwt_scale_engine.h" + +class QwtPlot::AxisData +{ +public: + bool isEnabled; + bool doAutoScale; + + double minValue; + double maxValue; + double stepSize; + + int maxMajor; + int maxMinor; + + QwtScaleDiv scaleDiv; + QwtScaleEngine *scaleEngine; + QwtScaleWidget *scaleWidget; +}; + +//! Initialize axes +void QwtPlot::initAxesData() +{ + int axisId; + + for ( axisId = 0; axisId < axisCnt; axisId++ ) + d_axisData[axisId] = new AxisData; + + d_axisData[yLeft]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::LeftScale, this ); + d_axisData[yRight]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::RightScale, this ); + d_axisData[xTop]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::TopScale, this ); + d_axisData[xBottom]->scaleWidget = + new QwtScaleWidget( QwtScaleDraw::BottomScale, this ); + + d_axisData[yLeft]->scaleWidget->setObjectName( "QwtPlotAxisYLeft" ); + d_axisData[yRight]->scaleWidget->setObjectName( "QwtPlotAxisYRight" ); + d_axisData[xTop]->scaleWidget->setObjectName( "QwtPlotAxisXTop" ); + d_axisData[xBottom]->scaleWidget->setObjectName( "QwtPlotAxisXBottom" ); + + QFont fscl( fontInfo().family(), 10 ); + QFont fttl( fontInfo().family(), 12, QFont::Bold ); + + for ( axisId = 0; axisId < axisCnt; axisId++ ) + { + AxisData &d = *d_axisData[axisId]; + + d.scaleWidget->setFont( fscl ); + d.scaleWidget->setMargin( 2 ); + + QwtText text = d.scaleWidget->title(); + text.setFont( fttl ); + d.scaleWidget->setTitle( text ); + + d.doAutoScale = true; + + d.minValue = 0.0; + d.maxValue = 1000.0; + d.stepSize = 0.0; + + d.maxMinor = 5; + d.maxMajor = 8; + + d.scaleEngine = new QwtLinearScaleEngine; + + d.scaleDiv.invalidate(); + } + + d_axisData[yLeft]->isEnabled = true; + d_axisData[yRight]->isEnabled = false; + d_axisData[xBottom]->isEnabled = true; + d_axisData[xTop]->isEnabled = false; +} + +void QwtPlot::deleteAxesData() +{ + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + delete d_axisData[axisId]->scaleEngine; + delete d_axisData[axisId]; + d_axisData[axisId] = NULL; + } +} + +/*! + \return specified axis, or NULL if axisId is invalid. + \param axisId axis index +*/ +const QwtScaleWidget *QwtPlot::axisWidget( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleWidget; + + return NULL; +} + +/*! + \return specified axis, or NULL if axisId is invalid. + \param axisId axis index +*/ +QwtScaleWidget *QwtPlot::axisWidget( int axisId ) +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleWidget; + + return NULL; +} + +/*! + Change the scale engine for an axis + + \param axisId axis index + \param scaleEngine Scale engine + + \sa axisScaleEngine() +*/ +void QwtPlot::setAxisScaleEngine( int axisId, QwtScaleEngine *scaleEngine ) +{ + if ( axisValid( axisId ) && scaleEngine != NULL ) + { + AxisData &d = *d_axisData[axisId]; + + delete d.scaleEngine; + d.scaleEngine = scaleEngine; + + d.scaleDiv.invalidate(); + + autoRefresh(); + } +} + +/*! + \param axisId axis index + \return Scale engine for a specific axis +*/ +QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleEngine; + else + return NULL; +} + +/*! + \param axisId axis index + \return Scale engine for a specific axis +*/ +const QwtScaleEngine *QwtPlot::axisScaleEngine( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->scaleEngine; + else + return NULL; +} +/*! + \return \c true if autoscaling is enabled + \param axisId axis index +*/ +bool QwtPlot::axisAutoScale( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->doAutoScale; + else + return false; + +} + +/*! + \return \c true if a specified axis is enabled + \param axisId axis index +*/ +bool QwtPlot::axisEnabled( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->isEnabled; + else + return false; +} + +/*! + \return the font of the scale labels for a specified axis + \param axisId axis index +*/ +QFont QwtPlot::axisFont( int axisId ) const +{ + if ( axisValid( axisId ) ) + return axisWidget( axisId )->font(); + else + return QFont(); + +} + +/*! + \return the maximum number of major ticks for a specified axis + \param axisId axis index + \sa setAxisMaxMajor() +*/ +int QwtPlot::axisMaxMajor( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->maxMajor; + else + return 0; +} + +/*! + \return the maximum number of minor ticks for a specified axis + \param axisId axis index + \sa setAxisMaxMinor() +*/ +int QwtPlot::axisMaxMinor( int axisId ) const +{ + if ( axisValid( axisId ) ) + return d_axisData[axisId]->maxMinor; + else + return 0; +} + +/*! + \brief Return the scale division of a specified axis + + axisScaleDiv(axisId)->lowerBound(), axisScaleDiv(axisId)->upperBound() + are the current limits of the axis scale. + + \param axisId axis index + \return Scale division + + \sa QwtScaleDiv, setAxisScaleDiv() +*/ +const QwtScaleDiv *QwtPlot::axisScaleDiv( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return NULL; + + return &d_axisData[axisId]->scaleDiv; +} + +/*! + \brief Return the scale division of a specified axis + + axisScaleDiv(axisId)->lowerBound(), axisScaleDiv(axisId)->upperBound() + are the current limits of the axis scale. + + \param axisId axis index + \return Scale division + + \sa QwtScaleDiv, setAxisScaleDiv() +*/ +QwtScaleDiv *QwtPlot::axisScaleDiv( int axisId ) +{ + if ( !axisValid( axisId ) ) + return NULL; + + return &d_axisData[axisId]->scaleDiv; +} + +/*! + \returns the scale draw of a specified axis + \param axisId axis index + \return specified scaleDraw for axis, or NULL if axis is invalid. + \sa QwtScaleDraw +*/ +const QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return NULL; + + return axisWidget( axisId )->scaleDraw(); +} + +/*! + \returns the scale draw of a specified axis + \param axisId axis index + \return specified scaleDraw for axis, or NULL if axis is invalid. + \sa QwtScaleDraw +*/ +QwtScaleDraw *QwtPlot::axisScaleDraw( int axisId ) +{ + if ( !axisValid( axisId ) ) + return NULL; + + return axisWidget( axisId )->scaleDraw(); +} + +/*! + Return the step size parameter, that has been set + in setAxisScale. This doesn't need to be the step size + of the current scale. + + \param axisId axis index + \return step size parameter value + + \sa setAxisScale() +*/ +double QwtPlot::axisStepSize( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return 0; + + return d_axisData[axisId]->stepSize; +} + +/*! + \brief Return the current interval of the specified axis + + This is only a convenience function for axisScaleDiv( axisId )->interval(); + + \param axisId axis index + \return Scale interval + + \sa QwtScaleDiv, axisScaleDiv() +*/ +QwtInterval QwtPlot::axisInterval( int axisId ) const +{ + if ( !axisValid( axisId ) ) + return QwtInterval(); + + return d_axisData[axisId]->scaleDiv.interval(); +} + +/*! + \return the title of a specified axis + \param axisId axis index +*/ +QwtText QwtPlot::axisTitle( int axisId ) const +{ + if ( axisValid( axisId ) ) + return axisWidget( axisId )->title(); + else + return QwtText(); +} + +/*! + \brief Enable or disable a specified axis + + When an axis is disabled, this only means that it is not + visible on the screen. Curves, markers and can be attached + to disabled axes, and transformation of screen coordinates + into values works as normal. + + Only xBottom and yLeft are enabled by default. + \param axisId axis index + \param tf \c true (enabled) or \c false (disabled) +*/ +void QwtPlot::enableAxis( int axisId, bool tf ) +{ + if ( axisValid( axisId ) && tf != d_axisData[axisId]->isEnabled ) + { + d_axisData[axisId]->isEnabled = tf; + updateLayout(); + } +} + +/*! + Transform the x or y coordinate of a position in the + drawing region into a value. + \param axisId axis index + \param pos position + \warning The position can be an x or a y coordinate, + depending on the specified axis. +*/ +double QwtPlot::invTransform( int axisId, int pos ) const +{ + if ( axisValid( axisId ) ) + return( canvasMap( axisId ).invTransform( pos ) ); + else + return 0.0; +} + + +/*! + \brief Transform a value into a coordinate in the plotting region + \param axisId axis index + \param value value + \return X or y coordinate in the plotting region corresponding + to the value. +*/ +double QwtPlot::transform( int axisId, double value ) const +{ + if ( axisValid( axisId ) ) + return( canvasMap( axisId ).transform( value ) ); + else + return 0.0; +} + +/*! + \brief Change the font of an axis + \param axisId axis index + \param f font + \warning This function changes the font of the tick labels, + not of the axis title. +*/ +void QwtPlot::setAxisFont( int axisId, const QFont &f ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setFont( f ); +} + +/*! + \brief Enable autoscaling for a specified axis + + This member function is used to switch back to autoscaling mode + after a fixed scale has been set. Autoscaling is enabled by default. + + \param axisId axis index + \param on On/Off + \sa setAxisScale(), setAxisScaleDiv(), updateAxes() + + \note The autoscaling flag has no effect until updateAxes() is executed + ( called by replot() ). +*/ +void QwtPlot::setAxisAutoScale( int axisId, bool on ) +{ + if ( axisValid( axisId ) && ( d_axisData[axisId]->doAutoScale != on ) ) + { + d_axisData[axisId]->doAutoScale = on; + autoRefresh(); + } +} + +/*! + \brief Disable autoscaling and specify a fixed scale for a selected axis. + \param axisId axis index + \param min + \param max minimum and maximum of the scale + \param stepSize Major step size. If <code>step == 0</code>, the step size is + calculated automatically using the maxMajor setting. + \sa setAxisMaxMajor(), setAxisAutoScale(), axisStepSize() +*/ +void QwtPlot::setAxisScale( int axisId, double min, double max, double stepSize ) +{ + if ( axisValid( axisId ) ) + { + AxisData &d = *d_axisData[axisId]; + + d.doAutoScale = false; + d.scaleDiv.invalidate(); + + d.minValue = min; + d.maxValue = max; + d.stepSize = stepSize; + + autoRefresh(); + } +} + +/*! + \brief Disable autoscaling and specify a fixed scale for a selected axis. + \param axisId axis index + \param scaleDiv Scale division + \sa setAxisScale(), setAxisAutoScale() +*/ +void QwtPlot::setAxisScaleDiv( int axisId, const QwtScaleDiv &scaleDiv ) +{ + if ( axisValid( axisId ) ) + { + AxisData &d = *d_axisData[axisId]; + + d.doAutoScale = false; + d.scaleDiv = scaleDiv; + + autoRefresh(); + } +} + +/*! + \brief Set a scale draw + \param axisId axis index + \param scaleDraw object responsible for drawing scales. + + By passing scaleDraw it is possible to extend QwtScaleDraw + functionality and let it take place in QwtPlot. Please note + that scaleDraw has to be created with new and will be deleted + by the corresponding QwtScale member ( like a child object ). + + \sa QwtScaleDraw, QwtScaleWidget + \warning The attributes of scaleDraw will be overwritten by those of the + previous QwtScaleDraw. +*/ + +void QwtPlot::setAxisScaleDraw( int axisId, QwtScaleDraw *scaleDraw ) +{ + if ( axisValid( axisId ) ) + { + axisWidget( axisId )->setScaleDraw( scaleDraw ); + autoRefresh(); + } +} + +/*! + Change the alignment of the tick labels + \param axisId axis index + \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h> + \sa QwtScaleDraw::setLabelAlignment() +*/ +void QwtPlot::setAxisLabelAlignment( int axisId, Qt::Alignment alignment ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setLabelAlignment( alignment ); +} + +/*! + Rotate all tick labels + \param axisId axis index + \param rotation Angle in degrees. When changing the label rotation, + the label alignment might be adjusted too. + \sa QwtScaleDraw::setLabelRotation(), setAxisLabelAlignment() +*/ +void QwtPlot::setAxisLabelRotation( int axisId, double rotation ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setLabelRotation( rotation ); +} + +/*! + Set the maximum number of minor scale intervals for a specified axis + + \param axisId axis index + \param maxMinor maximum number of minor steps + \sa axisMaxMinor() +*/ +void QwtPlot::setAxisMaxMinor( int axisId, int maxMinor ) +{ + if ( axisValid( axisId ) ) + { + maxMinor = qBound( 0, maxMinor, 100 ); + + AxisData &d = *d_axisData[axisId]; + if ( maxMinor != d.maxMinor ) + { + d.maxMinor = maxMinor; + d.scaleDiv.invalidate(); + autoRefresh(); + } + } +} + +/*! + Set the maximum number of major scale intervals for a specified axis + + \param axisId axis index + \param maxMajor maximum number of major steps + \sa axisMaxMajor() +*/ +void QwtPlot::setAxisMaxMajor( int axisId, int maxMajor ) +{ + if ( axisValid( axisId ) ) + { + maxMajor = qBound( 1, maxMajor, 10000 ); + + AxisData &d = *d_axisData[axisId]; + if ( maxMajor != d.maxMajor ) + { + d.maxMajor = maxMajor; + d.scaleDiv.invalidate(); + autoRefresh(); + } + } +} + +/*! + \brief Change the title of a specified axis + \param axisId axis index + \param title axis title +*/ +void QwtPlot::setAxisTitle( int axisId, const QString &title ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setTitle( title ); +} + +/*! + \brief Change the title of a specified axis + \param axisId axis index + \param title axis title +*/ +void QwtPlot::setAxisTitle( int axisId, const QwtText &title ) +{ + if ( axisValid( axisId ) ) + axisWidget( axisId )->setTitle( title ); +} + +//! Rebuild the scales +void QwtPlot::updateAxes() +{ + // Find bounding interval of the item data + // for all axes, where autoscaling is enabled + + QwtInterval intv[axisCnt]; + + const QwtPlotItemList& itmList = itemList(); + + QwtPlotItemIterator it; + for ( it = itmList.begin(); it != itmList.end(); ++it ) + { + const QwtPlotItem *item = *it; + + if ( !item->testItemAttribute( QwtPlotItem::AutoScale ) ) + continue; + + if ( !item->isVisible() ) + continue; + + if ( axisAutoScale( item->xAxis() ) || axisAutoScale( item->yAxis() ) ) + { + const QRectF rect = item->boundingRect(); + intv[item->xAxis()] |= QwtInterval( rect.left(), rect.right() ); + intv[item->yAxis()] |= QwtInterval( rect.top(), rect.bottom() ); + } + } + + // Adjust scales + + for ( int axisId = 0; axisId < axisCnt; axisId++ ) + { + AxisData &d = *d_axisData[axisId]; + + double minValue = d.minValue; + double maxValue = d.maxValue; + double stepSize = d.stepSize; + + if ( d.doAutoScale && intv[axisId].isValid() ) + { + d.scaleDiv.invalidate(); + + minValue = intv[axisId].minValue(); + maxValue = intv[axisId].maxValue(); + + d.scaleEngine->autoScale( d.maxMajor, + minValue, maxValue, stepSize ); + } + if ( !d.scaleDiv.isValid() ) + { + d.scaleDiv = d.scaleEngine->divideScale( + minValue, maxValue, + d.maxMajor, d.maxMinor, stepSize ); + } + + QwtScaleWidget *scaleWidget = axisWidget( axisId ); + scaleWidget->setScaleDiv( + d.scaleEngine->transformation(), d.scaleDiv ); + + int startDist, endDist; + scaleWidget->getBorderDistHint( startDist, endDist ); + scaleWidget->setBorderDist( startDist, endDist ); + } + + for ( it = itmList.begin(); it != itmList.end(); ++it ) + { + QwtPlotItem *item = *it; + item->updateScaleDiv( *axisScaleDiv( item->xAxis() ), + *axisScaleDiv( item->yAxis() ) ); + } +} + diff --git a/src/libpcp_qwt/src/qwt_plot_canvas.cpp b/src/libpcp_qwt/src/qwt_plot_canvas.cpp new file mode 100644 index 0000000..69669d2 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_canvas.cpp @@ -0,0 +1,1095 @@ +/* -*- 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_plot_canvas.h" +#include "qwt_painter.h" +#include "qwt_null_paintdevice.h" +#include "qwt_math.h" +#include "qwt_plot.h" +#include <qpainter.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qpaintengine.h> +#include <qevent.h> +#include <qbitmap.h> +#ifdef Q_WS_X11 +#include <qx11info_x11.h> +#endif + +class QwtStyleSheetRecorder: public QwtNullPaintDevice +{ +public: + QwtStyleSheetRecorder( const QSize &size ): + QwtNullPaintDevice( QPaintEngine::AllFeatures ) + { + setSize( size ); + } + + virtual void updateState( const QPaintEngineState &state ) + { + if ( state.state() & QPaintEngine::DirtyPen ) + { + d_pen = state.pen(); + } + if ( state.state() & QPaintEngine::DirtyBrush ) + { + d_brush = state.brush(); + } + if ( state.state() & QPaintEngine::DirtyBrushOrigin ) + { + d_origin = state.brushOrigin(); + } + } + + virtual void drawRects(const QRectF *rects, int count ) + { + for ( int i = 0; i < count; i++ ) + border.rectList += rects[i]; + } + + virtual void drawPath( const QPainterPath &path ) + { + const QRectF rect( QPointF( 0.0, 0.0 ) , size() ); + if ( path.controlPointRect().contains( rect.center() ) ) + { + setCornerRects( path ); + alignCornerRects( rect ); + + background.path = path; + background.brush = d_brush; + background.origin = d_origin; + } + else + { + border.pathList += path; + } + } + + void setCornerRects( const QPainterPath &path ) + { + QPointF pos( 0.0, 0.0 ); + + for ( int i = 0; i < path.elementCount(); i++ ) + { + QPainterPath::Element el = path.elementAt(i); + switch( el.type ) + { + case QPainterPath::MoveToElement: + case QPainterPath::LineToElement: + { + pos.setX( el.x ); + pos.setY( el.y ); + break; + } + case QPainterPath::CurveToElement: + { + QRectF r( pos, QPointF( el.x, el.y ) ); + clipRects += r.normalized(); + + pos.setX( el.x ); + pos.setY( el.y ); + + break; + } + case QPainterPath::CurveToDataElement: + { + if ( clipRects.size() > 0 ) + { + QRectF r = clipRects.last(); + r.setCoords( + qMin( r.left(), el.x ), + qMin( r.top(), el.y ), + qMax( r.right(), el.x ), + qMax( r.bottom(), el.y ) + ); + clipRects.last() = r.normalized(); + } + break; + } + } + } + } + +private: + void alignCornerRects( const QRectF &rect ) + { + for ( int i = 0; i < clipRects.size(); i++ ) + { + QRectF &r = clipRects[i]; + if ( r.center().x() < rect.center().x() ) + r.setLeft( rect.left() ); + else + r.setRight( rect.right() ); + + if ( r.center().y() < rect.center().y() ) + r.setTop( rect.top() ); + else + r.setBottom( rect.bottom() ); + } + } + + +public: + QVector<QRectF> clipRects; + + struct Border + { + QList<QPainterPath> pathList; + QList<QRectF> rectList; + QRegion clipRegion; + } border; + + struct Background + { + QPainterPath path; + QBrush brush; + QPointF origin; + } background; + +private: + QPen d_pen; + QBrush d_brush; + QPointF d_origin; +}; + +static void qwtDrawBackground( QPainter *painter, QWidget *widget ) +{ + const QBrush &brush = + widget->palette().brush( widget->backgroundRole() ); + + if ( brush.style() == Qt::TexturePattern ) + { + QPixmap pm( widget->size() ); +#if QT_VERSION >= 0x050000 + QwtPainter::fillPixmap( widget, pm ); +#else + pm.fill( widget, 0, 0 ); +#endif + painter->drawPixmap( 0, 0, pm ); + } + else if ( brush.gradient() ) + { + QVector<QRect> rects; + + if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode ) + { + rects += widget->rect(); + } + else + { + rects = painter->clipRegion().rects(); + } + +#if 1 + bool useRaster = false; + + if ( painter->paintEngine()->type() == QPaintEngine::X11 ) + { + // Qt 4.7.1: gradients on X11 are broken ( subrects + + // QGradient::StretchToDeviceMode ) and horrible slow. + // As workaround we have to use the raster paintengine. + // Even if the QImage -> QPixmap translation is slow + // it is three times faster, than using X11 directly + + useRaster = true; + } +#endif + if ( useRaster ) + { + QImage::Format format = QImage::Format_RGB32; + + const QGradientStops stops = brush.gradient()->stops(); + for ( int i = 0; i < stops.size(); i++ ) + { + if ( stops[i].second.alpha() != 255 ) + { + // don't use Format_ARGB32_Premultiplied. It's + // recommended by the Qt docs, but QPainter::drawImage() + // is horrible slow on X11. + + format = QImage::Format_ARGB32; + break; + } + } + + QImage image( widget->size(), format ); + + QPainter p( &image ); + p.setPen( Qt::NoPen ); + p.setBrush( brush ); + + p.drawRects( rects ); + + p.end(); + + painter->drawImage( 0, 0, image ); + } + else + { + painter->save(); + + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + painter->drawRects( rects ); + + painter->restore(); + } + } + else + { + painter->save(); + + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + painter->drawRects( painter->clipRegion().rects() ); + + painter->restore(); + } +} + +static inline void qwtRevertPath( QPainterPath &path ) +{ + if ( path.elementCount() == 4 ) + { + QPainterPath::Element el0 = path.elementAt(0); + QPainterPath::Element el3 = path.elementAt(3); + + path.setElementPositionAt( 0, el3.x, el3.y ); + path.setElementPositionAt( 3, el0.x, el0.y ); + } +} + +static QPainterPath qwtCombinePathList( const QRectF &rect, + const QList<QPainterPath> &pathList ) +{ + if ( pathList.isEmpty() ) + return QPainterPath(); + + QPainterPath ordered[8]; // starting top left + + for ( int i = 0; i < pathList.size(); i++ ) + { + int index = -1; + QPainterPath subPath = pathList[i]; + + const QRectF br = pathList[i].controlPointRect(); + if ( br.center().x() < rect.center().x() ) + { + if ( br.center().y() < rect.center().y() ) + { + if ( qAbs( br.top() - rect.top() ) < + qAbs( br.left() - rect.left() ) ) + { + index = 1; + } + else + { + index = 0; + } + } + else + { + if ( qAbs( br.bottom() - rect.bottom() ) < + qAbs( br.left() - rect.left() ) ) + { + index = 6; + } + else + { + index = 7; + } + } + + if ( subPath.currentPosition().y() > br.center().y() ) + qwtRevertPath( subPath ); + } + else + { + if ( br.center().y() < rect.center().y() ) + { + if ( qAbs( br.top() - rect.top() ) < + qAbs( br.right() - rect.right() ) ) + { + index = 2; + } + else + { + index = 3; + } + } + else + { + if ( qAbs( br.bottom() - rect.bottom() ) < + qAbs( br.right() - rect.right() ) ) + { + index = 5; + } + else + { + index = 4; + } + } + if ( subPath.currentPosition().y() < br.center().y() ) + qwtRevertPath( subPath ); + } + ordered[index] = subPath; + } + + for ( int i = 0; i < 4; i++ ) + { + if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() ) + { + // we don't accept incomplete rounded borders + return QPainterPath(); + } + } + + + const QPolygonF corners( rect ); + + QPainterPath path; + //path.moveTo( rect.topLeft() ); + + for ( int i = 0; i < 4; i++ ) + { + if ( ordered[2 * i].isEmpty() ) + { + path.lineTo( corners[i] ); + } + else + { + path.connectPath( ordered[2 * i] ); + path.connectPath( ordered[2 * i + 1] ); + } + } + + path.closeSubpath(); + +#if 0 + return path.simplified(); +#else + return path; +#endif +} + +static inline void qwtDrawStyledBackground( + QWidget *w, QPainter *painter ) +{ + QStyleOption opt; + opt.initFrom(w); + w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w); +} + +static QWidget *qwtBackgroundWidget( QWidget *w ) +{ + if ( w->parentWidget() == NULL ) + return w; + + if ( w->autoFillBackground() ) + { + const QBrush brush = w->palette().brush( w->backgroundRole() ); + if ( brush.color().alpha() > 0 ) + return w; + } + + if ( w->testAttribute( Qt::WA_StyledBackground ) ) + { + QImage image( 1, 1, QImage::Format_ARGB32 ); + image.fill( Qt::transparent ); + + QPainter painter( &image ); + painter.translate( -w->rect().center() ); + qwtDrawStyledBackground( w, &painter ); + painter.end(); + + if ( qAlpha( image.pixel( 0, 0 ) ) != 0 ) + return w; + } + + return qwtBackgroundWidget( w->parentWidget() ); +} + +static void qwtFillBackground( QPainter *painter, + QWidget *widget, const QVector<QRectF> &fillRects ) +{ + if ( fillRects.isEmpty() ) + return; + + QRegion clipRegion; + if ( painter->hasClipping() ) + clipRegion = painter->transform().map( painter->clipRegion() ); + else + clipRegion = widget->contentsRect(); + + // Try to find out which widget fills + // the unfilled areas of the styled background + + QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() ); + + for ( int i = 0; i < fillRects.size(); i++ ) + { + const QRect rect = fillRects[i].toAlignedRect(); + if ( clipRegion.intersects( rect ) ) + { + const QPoint topLeft = widget->mapTo( bgWidget, rect.topLeft() ); + + QPixmap pm( rect.size() ); +#if QT_VERSION >= 0x050000 + QwtPainter::fillPixmap( bgWidget, pm, topLeft ); +#else + pm.fill( bgWidget, topLeft ); +#endif + painter->drawPixmap( rect, pm ); + } + } +} + +static void qwtFillBackground( QPainter *painter, QwtPlotCanvas *canvas ) +{ + QVector<QRectF> rects; + + if ( canvas->testAttribute( Qt::WA_StyledBackground ) ) + { + QwtStyleSheetRecorder recorder( canvas->size() ); + + QPainter p( &recorder ); + qwtDrawStyledBackground( canvas, &p ); + p.end(); + + if ( recorder.background.brush.isOpaque() ) + rects = recorder.clipRects; + else + rects += canvas->rect(); + } + else + { + const QRectF r = canvas->rect(); + const double radius = canvas->borderRadius(); + if ( radius > 0.0 ) + { + QSizeF sz( radius, radius ); + + rects += QRectF( r.topLeft(), sz ); + rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz ); + rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz ); + rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz ); + } + } + + qwtFillBackground( painter, canvas, rects); +} + + +class QwtPlotCanvas::PrivateData +{ +public: + PrivateData(): + focusIndicator( NoFocusIndicator ), + borderRadius( 0 ), + paintAttributes( 0 ), + backingStore( NULL ) + { + styleSheet.hasBorder = false; + } + + ~PrivateData() + { + delete backingStore; + } + + FocusIndicator focusIndicator; + double borderRadius; + QwtPlotCanvas::PaintAttributes paintAttributes; + QPixmap *backingStore; + + struct StyleSheet + { + bool hasBorder; + QPainterPath borderPath; + QVector<QRectF> cornerRects; + + struct StyleSheetBackground + { + QBrush brush; + QPointF origin; + } background; + + } styleSheet; + +}; + +//! Sets a cross cursor, enables QwtPlotCanvas::BackingStore + +QwtPlotCanvas::QwtPlotCanvas( QwtPlot *plot ): + QFrame( plot ) +{ + d_data = new PrivateData; + +#ifndef QT_NO_CURSOR + setCursor( Qt::CrossCursor ); +#endif + + setAutoFillBackground( true ); + setPaintAttribute( QwtPlotCanvas::BackingStore, true ); + setPaintAttribute( QwtPlotCanvas::Opaque, true ); + setPaintAttribute( QwtPlotCanvas::HackStyledBackground, true ); +} + +//! Destructor +QwtPlotCanvas::~QwtPlotCanvas() +{ + delete d_data; +} + +//! Return parent plot widget +QwtPlot *QwtPlotCanvas::plot() +{ + return qobject_cast<QwtPlot *>( parent() ); +} + +//! Return parent plot widget +const QwtPlot *QwtPlotCanvas::plot() const +{ + return qobject_cast<const QwtPlot *>( parent() ); +} + +/*! + \brief Changing the paint attributes + + \param attribute Paint attribute + \param on On/Off + + \sa testPaintAttribute(), backingStore() +*/ +void QwtPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( bool( d_data->paintAttributes & attribute ) == on ) + return; + + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; + + switch ( attribute ) + { + case BackingStore: + { + if ( on ) + { + if ( d_data->backingStore == NULL ) + d_data->backingStore = new QPixmap(); + + if ( isVisible() ) + { +#if QT_VERSION >= 0x050000 + *d_data->backingStore = grab( rect() ); +#else + *d_data->backingStore = + QPixmap::grabWidget( this, rect() ); +#endif + } + } + else + { + delete d_data->backingStore; + d_data->backingStore = NULL; + } + break; + } + case Opaque: + { + if ( on ) + setAttribute( Qt::WA_OpaquePaintEvent, true ); + + break; + } + case HackStyledBackground: + case ImmediatePaint: + { + break; + } + } +} + +/*! + Test wether a paint attribute is enabled + + \param attribute Paint attribute + \return true if the attribute is enabled + \sa setPaintAttribute() +*/ +bool QwtPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const +{ + return d_data->paintAttributes & attribute; +} + +//! \return Backing store, might be null +const QPixmap *QwtPlotCanvas::backingStore() const +{ + return d_data->backingStore; +} + +//! Invalidate the internal backing store +void QwtPlotCanvas::invalidateBackingStore() +{ + if ( d_data->backingStore ) + *d_data->backingStore = QPixmap(); +} + +/*! + Set the focus indicator + + \sa FocusIndicator, focusIndicator() +*/ +void QwtPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator ) +{ + d_data->focusIndicator = focusIndicator; +} + +/*! + \return Focus indicator + + \sa FocusIndicator, setFocusIndicator() +*/ +QwtPlotCanvas::FocusIndicator QwtPlotCanvas::focusIndicator() const +{ + return d_data->focusIndicator; +} + +/*! + Set the radius for the corners of the border frame + + \param radius Radius of a rounded corner + \sa borderRadius() +*/ +void QwtPlotCanvas::setBorderRadius( double radius ) +{ + d_data->borderRadius = qMax( 0.0, radius ); +} + +/*! + \return Radius for the corners of the border frame + \sa setBorderRadius() +*/ +double QwtPlotCanvas::borderRadius() const +{ + return d_data->borderRadius; +} + +/*! + Qt event handler for QEvent::PolishRequest and QEvent::StyleChange + \param event Qt Event +*/ +bool QwtPlotCanvas::event( QEvent *event ) +{ + if ( event->type() == QEvent::PolishRequest ) + { + if ( testPaintAttribute( QwtPlotCanvas::Opaque ) ) + { + // Setting a style sheet changes the + // Qt::WA_OpaquePaintEvent attribute, but we insist + // on painting the background. + + setAttribute( Qt::WA_OpaquePaintEvent, true ); + } + } + + if ( event->type() == QEvent::PolishRequest || + event->type() == QEvent::StyleChange ) + { + updateStyleSheetInfo(); + } + + return QFrame::event( event ); +} + +/*! + Paint event + \param event Paint event +*/ +void QwtPlotCanvas::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + if ( testPaintAttribute( QwtPlotCanvas::BackingStore ) && + d_data->backingStore != NULL ) + { + QPixmap &bs = *d_data->backingStore; + if ( bs.size() != size() ) + { + bs = QPixmap( size() ); + +#ifdef Q_WS_X11 + if ( bs.x11Info().screen() != x11Info().screen() ) + bs.x11SetScreen( x11Info().screen() ); +#endif + + if ( testAttribute(Qt::WA_StyledBackground) ) + { + QPainter p( &bs ); + qwtFillBackground( &p, this ); + drawCanvas( &p, true ); + } + else + { + QPainter p; + if ( d_data->borderRadius <= 0.0 ) + { +#if QT_VERSION >= 0x050000 + QwtPainter::fillPixmap( this, bs ); +#else + bs.fill( this, 0, 0 ); +#endif + p.begin( &bs ); + drawCanvas( &p, false ); + } + else + { + p.begin( &bs ); + qwtFillBackground( &p, this ); + drawCanvas( &p, true ); + } + + if ( frameWidth() > 0 ) + drawBorder( &p ); + } + } + + painter.drawPixmap( 0, 0, *d_data->backingStore ); + } + else + { + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) + { + qwtFillBackground( &painter, this ); + drawCanvas( &painter, true ); + } + else + { + drawCanvas( &painter, false ); + } + } + else + { + if ( testAttribute( Qt::WA_OpaquePaintEvent ) ) + { + if ( autoFillBackground() ) + qwtDrawBackground( &painter, this ); + } + + drawCanvas( &painter, false ); + + if ( frameWidth() > 0 ) + drawBorder( &painter ); + } + } + + if ( hasFocus() && focusIndicator() == CanvasFocusIndicator ) + drawFocusIndicator( &painter ); +} + +void QwtPlotCanvas::drawCanvas( QPainter *painter, bool withBackground ) +{ + bool hackStyledBackground = false; + + if ( withBackground && testAttribute( Qt::WA_StyledBackground ) + && testPaintAttribute( HackStyledBackground ) ) + { + // Antialiasing rounded borders is done by + // inserting pixels with colors between the + // border color and the color on the canvas, + // When the border is painted before the plot items + // these colors are interpolated for the canvas + // and the plot items need to be clipped excluding + // the anialiased pixels. In situations, where + // the plot items fill the area at the rounded + // borders this is noticeable. + // The only way to avoid these annoying "artefacts" + // is to paint the border on top of the plot items. + + if ( d_data->styleSheet.hasBorder && + !d_data->styleSheet.borderPath.isEmpty() ) + { + // We have a border with at least one rounded corner + hackStyledBackground = true; + } + } + + if ( withBackground ) + { + painter->save(); + + if ( testAttribute( Qt::WA_StyledBackground ) ) + { + if ( hackStyledBackground ) + { + // paint background without border + + painter->setPen( Qt::NoPen ); + painter->setBrush( d_data->styleSheet.background.brush ); + painter->setBrushOrigin( d_data->styleSheet.background.origin ); + painter->setClipPath( d_data->styleSheet.borderPath ); + painter->drawRect( contentsRect() ); + } + else + { + qwtDrawStyledBackground( this, painter ); + } + } + else if ( autoFillBackground() ) + { + painter->setPen( Qt::NoPen ); + painter->setBrush( palette().brush( backgroundRole() ) ); + + if ( d_data->borderRadius > 0.0 ) + { + if ( frameWidth() > 0 ) + { + painter->setClipPath( borderPath( rect() ) ); + painter->drawRect( rect() ); + } + else + { + painter->setRenderHint( QPainter::Antialiasing, true ); + painter->drawPath( borderPath( rect() ) ); + } + } + else + { + painter->drawRect( contentsRect() ); + } + } + + painter->restore(); + } + + painter->save(); + + if ( !d_data->styleSheet.borderPath.isEmpty() ) + { + painter->setClipPath( + d_data->styleSheet.borderPath, Qt::IntersectClip ); + } + else + { + if ( d_data->borderRadius > 0.0 ) + painter->setClipPath( borderPath( rect() ), Qt::IntersectClip ); + else + painter->setClipRect( contentsRect(), Qt::IntersectClip ); + } + + plot()->drawCanvas( painter ); + + painter->restore(); + + if ( withBackground && hackStyledBackground ) + { + // Now paint the border on top + QStyleOptionFrame opt; + opt.initFrom(this); + style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this); + } +} + +/*! + Draw the border of the plot canvas + + \param painter Painter + \sa setBorderRadius(), QFrame::drawFrame() +*/ +void QwtPlotCanvas::drawBorder( QPainter *painter ) +{ + if ( d_data->borderRadius > 0 ) + { + if ( frameWidth() > 0 ) + { + QwtPainter::drawRoundedFrame( painter, QRectF( rect() ), + d_data->borderRadius, d_data->borderRadius, + palette(), frameWidth(), frameStyle() ); + } + } + else + { + drawFrame( painter ); + } +} + +/*! + Resize event + \param event Resize event +*/ +void QwtPlotCanvas::resizeEvent( QResizeEvent *event ) +{ + QFrame::resizeEvent( event ); + updateStyleSheetInfo(); +} + +/*! + Draw the focus indication + \param painter Painter +*/ +void QwtPlotCanvas::drawFocusIndicator( QPainter *painter ) +{ + const int margin = 1; + + QRect focusRect = contentsRect(); + focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin, + focusRect.width() - 2 * margin, focusRect.height() - 2 * margin ); + + QwtPainter::drawFocusRect( painter, this, focusRect ); +} + +/*! + Invalidate the paint cache and repaint the canvas + \sa invalidatePaintCache() +*/ +void QwtPlotCanvas::replot() +{ + invalidateBackingStore(); + + if ( testPaintAttribute( QwtPlotCanvas::ImmediatePaint ) ) + repaint( contentsRect() ); + else + update( contentsRect() ); +} + +//! Update the cached informations about the current style sheet +void QwtPlotCanvas::updateStyleSheetInfo() +{ + if ( !testAttribute(Qt::WA_StyledBackground ) ) + return; + + QwtStyleSheetRecorder recorder( size() ); + + QPainter painter( &recorder ); + + QStyleOption opt; + opt.initFrom(this); + style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); + + painter.end(); + + d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty(); + d_data->styleSheet.cornerRects = recorder.clipRects; + + if ( recorder.background.path.isEmpty() ) + { + if ( !recorder.border.rectList.isEmpty() ) + { + d_data->styleSheet.borderPath = + qwtCombinePathList( rect(), recorder.border.pathList ); + } + } + else + { + d_data->styleSheet.borderPath = recorder.background.path; + d_data->styleSheet.background.brush = recorder.background.brush; + d_data->styleSheet.background.origin = recorder.background.origin; + } +} + +/*! + Calculate the painter path for a styled or rounded border + + When the canvas has no styled background or rounded borders + the painter path is empty. + + \param rect Bounding rectangle of the canvas + \return Painter path, that can be used for clipping +*/ +QPainterPath QwtPlotCanvas::borderPath( const QRect &rect ) const +{ + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + QwtStyleSheetRecorder recorder( rect.size() ); + + QPainter painter( &recorder ); + + QStyleOption opt; + opt.initFrom(this); + opt.rect = rect; + style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this); + + painter.end(); + + if ( !recorder.background.path.isEmpty() ) + return recorder.background.path; + + if ( !recorder.border.rectList.isEmpty() ) + return qwtCombinePathList( rect, recorder.border.pathList ); + } + else if ( d_data->borderRadius > 0.0 ) + { + double fw2 = frameWidth() * 0.5; + QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 ); + + QPainterPath path; + path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius ); + return path; + } + + return QPainterPath(); +} + +/*! + Calculate a mask, that can be used to clip away the border frame + + \param size Size including the frame +*/ +QBitmap QwtPlotCanvas::borderMask( const QSize &size ) const +{ + const QRect r( 0, 0, size.width(), size.height() ); + + const QPainterPath path = borderPath( r ); + if ( path.isEmpty() ) + return QBitmap(); + + QImage image( size, QImage::Format_ARGB32_Premultiplied ); + image.fill( Qt::color0 ); + + QPainter painter( &image ); + painter.setClipPath( path ); + painter.fillRect( r, Qt::color1 ); + + // now erase the frame + + painter.setCompositionMode( QPainter::CompositionMode_DestinationOut ); + + if ( testAttribute(Qt::WA_StyledBackground ) ) + { + QStyleOptionFrame opt; + opt.initFrom(this); + opt.rect = r; + style()->drawPrimitive( QStyle::PE_Frame, &opt, &painter, this ); + } + else + { + if ( d_data->borderRadius > 0 && frameWidth() > 0 ) + { + painter.setPen( QPen( Qt::color1, frameWidth() ) ); + painter.setBrush( Qt::NoBrush ); + painter.setRenderHint( QPainter::Antialiasing, true ); + + painter.drawPath( path ); + } + } + + painter.end(); + + const QImage mask = image.createMaskFromColor( + QColor( Qt::color1 ).rgb(), Qt::MaskOutColor ); + + return QBitmap::fromImage( mask ); +} diff --git a/src/libpcp_qwt/src/qwt_plot_canvas.h b/src/libpcp_qwt/src/qwt_plot_canvas.h new file mode 100644 index 0000000..2f4c163 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_canvas.h @@ -0,0 +1,171 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_CANVAS_H +#define QWT_PLOT_CANVAS_H + +#include "qwt_global.h" +#include <qframe.h> +#include <qpen.h> +#include <qpainterpath.h> +#include <qbitmap.h> + +class QwtPlot; +class QPixmap; + +/*! + \brief Canvas of a QwtPlot. + + Canvas is the widget where all plot items are displayed + + \sa QwtPlot +*/ +class QWT_EXPORT QwtPlotCanvas : public QFrame +{ + Q_OBJECT + +public: + + /*! + \brief Paint attributes + + The default setting enables BackingStore and Opaque. + + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + \brief Paint double buffered reusing the content + of the pixmap buffer when possible. + + Using a backing store might improve the performance + significantly, when workin with widget overlays ( like rubberbands ). + Disabling the cache might improve the performance for + incremental paints (using QwtPlotDirectPainter ). + + \sa backingStore(), invalidateBackingStore() + */ + BackingStore = 1, + + /*! + \brief Try to fill the complete contents rectangle + of the plot canvas + + When using styled backgrounds Qt assumes, that the + canvas doesn't fill its area completely + ( f.e because of rounded borders ) and fills the area + below the canvas. When this is done with gradients it might + result in a serious performance bottleneck - depending on the size. + + When the Opaque attribute is enabled the canvas tries to + identify the gaps with some heuristics and to fill those only. + + \warning Will not work for semitransparent backgrounds + */ + Opaque = 2, + + /*! + \brief Try to improve painting of styled backgrounds + + QwtPlotCanvas supports the box model attributes for + customizing the layout with style sheets. Unfortunately + the design of Qt style sheets has no concept how to + handle backgrounds with rounded corners - beside of padding. + + When HackStyledBackground is enabled the plot canvas tries + to seperate the background from the background border + by reverse engeneering to paint the background before and + the border after the plot items. In this order the border + gets prefectly antialiased and you can avoid some pixel + artifacts in the corners. + */ + HackStyledBackground = 4, + + /*! + When ImmediatePaint is set replot() calls repaint() + instead of update(). + + \sa replot(), QWidget::repaint(), QWidget::update() + */ + ImmediatePaint = 8 + }; + + //! Paint attributes + typedef QFlags<PaintAttribute> PaintAttributes; + + /*! + \brief Focus indicator + The default setting is NoFocusIndicator + \sa setFocusIndicator(), focusIndicator(), paintFocus() + */ + + enum FocusIndicator + { + //! Don't paint a focus indicator + NoFocusIndicator, + + /*! + The focus is related to the complete canvas. + Paint the focus indicator using paintFocus() + */ + CanvasFocusIndicator, + + /*! + The focus is related to an item (curve, point, ...) on + the canvas. It is up to the application to display a + focus indication using f.e. highlighting. + */ + ItemFocusIndicator + }; + + explicit QwtPlotCanvas( QwtPlot * ); + virtual ~QwtPlotCanvas(); + + QwtPlot *plot(); + const QwtPlot *plot() const; + + void setFocusIndicator( FocusIndicator ); + FocusIndicator focusIndicator() const; + + void setBorderRadius( double ); + double borderRadius() const; + + QPainterPath borderPath( const QRect &rect ) const; + QBitmap borderMask( const QSize & ) const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + const QPixmap *backingStore() const; + void invalidateBackingStore(); + + void replot(); + + virtual bool event( QEvent * ); + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + + virtual void drawFocusIndicator( QPainter * ); + virtual void drawBorder( QPainter * ); + + void updateStyleSheetInfo(); + +private: + void drawCanvas( QPainter *, bool withBackground ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCanvas::PaintAttributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_curve.cpp b/src/libpcp_qwt/src/qwt_plot_curve.cpp new file mode 100644 index 0000000..f5cc449 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_curve.cpp @@ -0,0 +1,1127 @@ +/* -*- 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_plot_curve.h" +#include "qwt_math.h" +#include "qwt_clipper.h" +#include "qwt_painter.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include "qwt_scale_map.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_curve_fitter.h" +#include "qwt_symbol.h" +#include <qpainter.h> +#include <qpixmap.h> +#include <qalgorithms.h> +#include <qmath.h> + +static int verifyRange( int size, int &i1, int &i2 ) +{ + if ( size < 1 ) + return 0; + + i1 = qBound( 0, i1, size - 1 ); + i2 = qBound( 0, i2, size - 1 ); + + if ( i1 > i2 ) + qSwap( i1, i2 ); + + return ( i2 - i1 + 1 ); +} + +class QwtPlotCurve::PrivateData +{ +public: + PrivateData(): + style( QwtPlotCurve::Lines ), + baseline( 0.0 ), + symbol( NULL ), + attributes( 0 ), + paintAttributes( QwtPlotCurve::ClipPolygons ), + legendAttributes( 0 ) + { + pen = QPen( Qt::black ); + legendPen = Qt::NoPen; + curveFitter = new QwtSplineCurveFitter; + } + + ~PrivateData() + { + delete symbol; + delete curveFitter; + } + + QwtPlotCurve::CurveStyle style; + double baseline; + + const QwtSymbol *symbol; + QwtCurveFitter *curveFitter; + + QPen pen; + QPen legendPen; + QBrush brush; + + QwtPlotCurve::CurveAttributes attributes; + QwtPlotCurve::PaintAttributes paintAttributes; + + QwtPlotCurve::LegendAttributes legendAttributes; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotCurve::QwtPlotCurve( const QwtText &title ): + QwtPlotSeriesItem<QPointF>( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotCurve::QwtPlotCurve( const QString &title ): + QwtPlotSeriesItem<QPointF>( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotCurve::~QwtPlotCurve() +{ + delete d_data; +} + +//! Initialize internal members +void QwtPlotCurve::init() +{ + setItemAttribute( QwtPlotItem::Legend ); + setItemAttribute( QwtPlotItem::AutoScale ); + + d_data = new PrivateData; + d_series = new QwtPointSeriesData(); + + setZ( 20.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotCurve +int QwtPlotCurve::rtti() const +{ + return QwtPlotItem::Rtti_PlotCurve; +} + +/*! + Specify an attribute how to draw the curve + + \param attribute Paint attribute + \param on On/Off + \sa testPaintAttribute() +*/ +void QwtPlotCurve::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \brief Return the current paint attributes + \sa setPaintAttribute() +*/ +bool QwtPlotCurve::testPaintAttribute( PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + Specify an attribute how to draw the legend identifier + + \param attribute Attribute + \param on On/Off + /sa testLegendAttribute() +*/ +void QwtPlotCurve::setLegendAttribute( LegendAttribute attribute, bool on ) +{ + if ( on ) + d_data->legendAttributes |= attribute; + else + d_data->legendAttributes &= ~attribute; +} + +/*! + \brief Return the current paint attributes + \sa setLegendAttribute() +*/ +bool QwtPlotCurve::testLegendAttribute( LegendAttribute attribute ) const +{ + return ( d_data->legendAttributes & attribute ); +} + +/*! + Set the curve's drawing style + + \param style Curve style + \sa style() +*/ +void QwtPlotCurve::setStyle( CurveStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + itemChanged(); + } +} + +/*! + Return the current style + \sa setStyle() +*/ +QwtPlotCurve::CurveStyle QwtPlotCurve::style() const +{ + return d_data->style; +} + +/*! + Assign a symbol + + The curve will take the ownership of the symbol, hence the previously + set symbol will be delete by setting a new one. If \p symbol is + \c NULL no symbol will be drawn. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotCurve::setSymbol( const QwtSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtSymbol *QwtPlotCurve::symbol() const +{ + return d_data->symbol; +} + +/*! + Assign a pen + + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotCurve::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + itemChanged(); + } +} + +/*! + \return Pen used to draw the lines + \sa setPen(), brush() +*/ +const QPen& QwtPlotCurve::pen() const +{ + return d_data->pen; +} + +/*! + \brief Assign a brush. + + In case of brush.style() != QBrush::NoBrush + and style() != QwtPlotCurve::Sticks + the area between the curve and the baseline will be filled. + + In case !brush.color().isValid() the area will be filled by + pen.color(). The fill algorithm simply connects the first and the + last curve point to the baseline. So the curve data has to be sorted + (ascending or descending). + + \param brush New brush + \sa brush(), setBaseline(), baseline() +*/ +void QwtPlotCurve::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + itemChanged(); + } +} + +/*! + \return Brush used to fill the area between lines and the baseline + \sa setBrush(), setBaseline(), baseline() +*/ +const QBrush& QwtPlotCurve::brush() const +{ + return d_data->brush; +} + +/*! + Draw an interval of the curve + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + curve will be painted to its last point. + + \sa drawCurve(), drawSymbols(), +*/ +void QwtPlotCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( !painter || dataSize() <= 0 ) + return; + + if ( to < 0 ) + to = dataSize() - 1; + + if ( verifyRange( dataSize(), from, to ) > 0 ) + { + painter->save(); + painter->setPen( d_data->pen ); + + /* + Qt 4.0.0 is slow when drawing lines, but it's even + slower when the painter has a brush. So we don't + set the brush before we really need it. + */ + + drawCurve( painter, d_data->style, xMap, yMap, canvasRect, from, to ); + painter->restore(); + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + painter->save(); + drawSymbols( painter, *d_data->symbol, + xMap, yMap, canvasRect, from, to ); + painter->restore(); + } + } +} + +/*! + \brief Draw the line part (without symbols) of a curve interval. + \param painter Painter + \param style curve style, see QwtPlotCurve::CurveStyle + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + \sa draw(), drawDots(), drawLines(), drawSteps(), drawSticks() +*/ +void QwtPlotCurve::drawCurve( QPainter *painter, int style, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + switch ( style ) + { + case Lines: + if ( testCurveAttribute( Fitted ) ) + { + // we always need the complete + // curve for fitting + from = 0; + to = dataSize() - 1; + } + drawLines( painter, xMap, yMap, canvasRect, from, to ); + break; + case Sticks: + drawSticks( painter, xMap, yMap, canvasRect, from, to ); + break; + case Steps: + drawSteps( painter, xMap, yMap, canvasRect, from, to ); + break; + case Dots: + drawDots( painter, xMap, yMap, canvasRect, from, to ); + break; + case NoCurve: + default: + break; + } +} + +/*! + \brief Draw lines + + If the CurveAttribute Fitted is enabled a QwtCurveFitter tries + to interpolate/smooth the curve, before it is painted. + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa setCurveAttribute(), setCurveFitter(), draw(), + drawLines(), drawDots(), drawSteps(), drawSticks() +*/ +void QwtPlotCurve::drawLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + int size = to - from + 1; + if ( size <= 0 ) + return; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + QPolygonF polyline( size ); + + QPointF *points = polyline.data(); + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = d_series->sample( i ); + + double x = xMap.transform( sample.x() ); + double y = yMap.transform( sample.y() ); + if ( doAlign ) + { + x = qRound( x ); + y = qRound( y ); + } + + points[i - from].rx() = x; + points[i - from].ry() = y; + } + + if ( ( d_data->attributes & Fitted ) && d_data->curveFitter ) + polyline = d_data->curveFitter->fitCurve( polyline ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF()); + const QPolygonF clipped = QwtClipper::clipPolygonF( + canvasRect.adjusted(-pw, -pw, pw, pw), polyline, false ); + + QwtPainter::drawPolyline( painter, clipped ); + } + else + { + QwtPainter::drawPolyline( painter, polyline ); + } + + if ( d_data->brush.style() != Qt::NoBrush ) + fillCurve( painter, xMap, yMap, canvasRect, polyline ); +} + +/*! + Draw sticks + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa draw(), drawCurve(), drawDots(), drawLines(), drawSteps() +*/ +void QwtPlotCurve::drawSticks( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &, int from, int to ) const +{ + painter->save(); + painter->setRenderHint( QPainter::Antialiasing, false ); + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double x0 = xMap.transform( d_data->baseline ); + double y0 = yMap.transform( d_data->baseline ); + if ( doAlign ) + { + x0 = qRound( x0 ); + y0 = qRound( y0 ); + } + + const Qt::Orientation o = orientation(); + + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = d_series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + if ( o == Qt::Horizontal ) + QwtPainter::drawLine( painter, x0, yi, xi, yi ); + else + QwtPainter::drawLine( painter, xi, y0, xi, yi ); + } + + painter->restore(); +} + +/*! + Draw dots + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa draw(), drawCurve(), drawSticks(), drawLines(), drawSteps() +*/ +void QwtPlotCurve::drawDots( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doFill = d_data->brush.style() != Qt::NoBrush; + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + QPolygonF polyline; + if ( doFill ) + polyline.resize( to - from + 1 ); + + QPointF *points = polyline.data(); + + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = d_series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + QwtPainter::drawPoint( painter, QPointF( xi, yi ) ); + + if ( doFill ) + { + points[i - from].rx() = xi; + points[i - from].ry() = yi; + } + } + + if ( doFill ) + fillCurve( painter, xMap, yMap, canvasRect, polyline ); +} + +/*! + Draw step function + + The direction of the steps depends on Inverted attribute. + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from index of the first point to be painted + \param to index of the last point to be painted + + \sa CurveAttribute, setCurveAttribute(), + draw(), drawCurve(), drawDots(), drawLines(), drawSticks() +*/ +void QwtPlotCurve::drawSteps( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + QPolygonF polygon( 2 * ( to - from ) + 1 ); + QPointF *points = polygon.data(); + + bool inverted = orientation() == Qt::Vertical; + if ( d_data->attributes & Inverted ) + inverted = !inverted; + + int i, ip; + for ( i = from, ip = 0; i <= to; i++, ip += 2 ) + { + const QPointF sample = d_series->sample( i ); + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + if ( ip > 0 ) + { + const QPointF &p0 = points[ip - 2]; + QPointF &p = points[ip - 1]; + + if ( inverted ) + { + p.rx() = p0.x(); + p.ry() = yi; + } + else + { + p.rx() = xi; + p.ry() = p0.y(); + } + } + + points[ip].rx() = xi; + points[ip].ry() = yi; + } + + if ( d_data->paintAttributes & ClipPolygons ) + { + const QPolygonF clipped = QwtClipper::clipPolygonF( + canvasRect, polygon, false ); + + QwtPainter::drawPolyline( painter, clipped ); + } + else + { + QwtPainter::drawPolyline( painter, polygon ); + } + + if ( d_data->brush.style() != Qt::NoBrush ) + fillCurve( painter, xMap, yMap, canvasRect, polygon ); +} + + +/*! + Specify an attribute for drawing the curve + + \param attribute Curve attribute + \param on On/Off + + /sa testCurveAttribute(), setCurveFitter() +*/ +void QwtPlotCurve::setCurveAttribute( CurveAttribute attribute, bool on ) +{ + if ( bool( d_data->attributes & attribute ) == on ) + return; + + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + itemChanged(); +} + +/*! + \return true, if attribute is enabled + \sa setCurveAttribute() +*/ +bool QwtPlotCurve::testCurveAttribute( CurveAttribute attribute ) const +{ + return d_data->attributes & attribute; +} + +/*! + Assign a curve fitter + + The curve fitter "smooths" the curve points, when the Fitted + CurveAttribute is set. setCurveFitter(NULL) also disables curve fitting. + + The curve fitter operates on the translated points ( = widget coordinates) + to be functional for logarithmic scales. Obviously this is less performant + for fitting algorithms, that reduce the number of points. + + For situations, where curve fitting is used to improve the performance + of painting huge series of points it might be better to execute the fitter + on the curve points once and to cache the result in the QwtSeriesData object. + + \param curveFitter() Curve fitter + \sa Fitted +*/ +void QwtPlotCurve::setCurveFitter( QwtCurveFitter *curveFitter ) +{ + delete d_data->curveFitter; + d_data->curveFitter = curveFitter; + + itemChanged(); +} + +/*! + Get the curve fitter. If curve fitting is disabled NULL is returned. + + \return Curve fitter + \sa setCurveFitter(), Fitted +*/ +QwtCurveFitter *QwtPlotCurve::curveFitter() const +{ + return d_data->curveFitter; +} + +/*! + Fill the area between the curve and the baseline with + the curve brush + + \param painter Painter + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param polygon Polygon - will be modified ! + + \sa setBrush(), setBaseline(), setStyle() +*/ +void QwtPlotCurve::fillCurve( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, QPolygonF &polygon ) const +{ + if ( d_data->brush.style() == Qt::NoBrush ) + return; + + closePolyline( painter, xMap, yMap, polygon ); + if ( polygon.count() <= 2 ) // a line can't be filled + return; + + QBrush brush = d_data->brush; + if ( !brush.color().isValid() ) + brush.setColor( d_data->pen.color() ); + + if ( d_data->paintAttributes & ClipPolygons ) + polygon = QwtClipper::clipPolygonF( canvasRect, polygon, true ); + + painter->save(); + + painter->setPen( Qt::NoPen ); + painter->setBrush( brush ); + + QwtPainter::drawPolygon( painter, polygon ); + + painter->restore(); +} + +/*! + \brief Complete a polygon to be a closed polygon including the + area between the original polygon and the baseline. + + \param painter Painter + \param xMap X map + \param yMap Y map + \param polygon Polygon to be completed +*/ +void QwtPlotCurve::closePolyline( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + QPolygonF &polygon ) const +{ + if ( polygon.size() < 2 ) + return; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double baseline = d_data->baseline; + + if ( orientation() == Qt::Vertical ) + { + if ( yMap.transformation()->type() == QwtScaleTransformation::Log10 ) + { + if ( baseline < QwtScaleMap::LogMin ) + baseline = QwtScaleMap::LogMin; + } + + double refY = yMap.transform( baseline ); + if ( doAlign ) + refY = qRound( refY ); + + polygon += QPointF( polygon.last().x(), refY ); + polygon += QPointF( polygon.first().x(), refY ); + } + else + { + if ( xMap.transformation()->type() == QwtScaleTransformation::Log10 ) + { + if ( baseline < QwtScaleMap::LogMin ) + baseline = QwtScaleMap::LogMin; + } + + double refX = xMap.transform( baseline ); + if ( doAlign ) + refX = qRound( refX ); + + polygon += QPointF( refX, polygon.last().y() ); + polygon += QPointF( refX, polygon.first().y() ); + } +} + +/*! + Draw symbols + + \param painter Painter + \param symbol Curve symbol + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted + + \sa setSymbol(), drawSeries(), drawCurve() +*/ +void QwtPlotCurve::drawSymbols( QPainter *painter, const QwtSymbol &symbol, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + bool usePixmap = testPaintAttribute( CacheSymbols ); + if ( usePixmap && !doAlign ) + { + // Don't use the pixmap, when the paint device + // could generate scalable vectors + + usePixmap = false; + } + + if ( usePixmap ) + { + const QSize sz = ( 2 * symbol.boundingSize() + QSize( 1, 1 ) ) / 2; + const int w2 = sz.width() / 2; + const int h2 = sz.height() / 2; + + QPixmap pm( sz ); + pm.fill( Qt::transparent ); + + QPainter p( &pm ); + p.setRenderHints( painter->renderHints() ); + symbol.drawSymbol( &p, QPointF( w2, h2 ) ); + p.end(); + + for ( int i = from; i <= to; i++ ) + { + const QPointF sample = d_series->sample( i ); + + const double xi = xMap.transform( sample.x() ); + const double yi = yMap.transform( sample.y() ); + + if ( canvasRect.contains( xi, yi ) ) + { + const int left = qRound( xi ) - w2; + const int top = qRound( yi ) - h2; + + painter->drawPixmap( left, top, pm ); + } + } + } + else + { + const int chunkSize = 500; + + for ( int i = from; i <= to; i += chunkSize ) + { + const int n = qMin( chunkSize, to - i + 1 ); + + QPolygonF points; + for ( int j = 0; j < n; j++ ) + { + const QPointF sample = d_series->sample( i + j ); + + const double xi = xMap.transform( sample.x() ); + const double yi = yMap.transform( sample.y() ); + + if ( canvasRect.contains( xi, yi ) ) + points += QPointF( xi, yi ); + } + + if ( points.size() > 0 ) + symbol.drawSymbols( painter, points ); + } + } +} + +/*! + \brief Set the value of the baseline + + The baseline is needed for filling the curve with a brush or + the Sticks drawing style. + + The interpretation of the baseline depends on the orientation(). + With Qt::Horizontal, the baseline is interpreted as a horizontal line + at y = baseline(), with Qt::Vertical, it is interpreted as a vertical + line at x = baseline(). + + The default value is 0.0. + + \param value Value of the baseline + \sa baseline(), setBrush(), setStyle(), QwtPlotAbstractSeriesItem::orientation() +*/ +void QwtPlotCurve::setBaseline( double value ) +{ + if ( d_data->baseline != value ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value of the baseline + \sa setBaseline() +*/ +double QwtPlotCurve::baseline() const +{ + return d_data->baseline; +} + +/*! + Find the closest curve point for a specific position + + \param pos Position, where to look for the closest curve point + \param dist If dist != NULL, closestPoint() returns the distance between + the position and the clostest curve point + \return Index of the closest curve point, or -1 if none can be found + ( f.e when the curve has no points ) + \note closestPoint() implements a dumb algorithm, that iterates + over all points +*/ +int QwtPlotCurve::closestPoint( const QPoint &pos, double *dist ) const +{ + if ( plot() == NULL || dataSize() <= 0 ) + return -1; + + const QwtScaleMap xMap = plot()->canvasMap( xAxis() ); + const QwtScaleMap yMap = plot()->canvasMap( yAxis() ); + + int index = -1; + double dmin = 1.0e10; + + for ( uint i = 0; i < dataSize(); i++ ) + { + const QPointF sample = d_series->sample( i ); + + const double cx = xMap.transform( sample.x() ) - pos.x(); + const double cy = yMap.transform( sample.y() ) - pos.y(); + + const double f = qwtSqr( cx ) + qwtSqr( cy ); + if ( f < dmin ) + { + index = i; + dmin = f; + } + } + if ( dist ) + *dist = qSqrt( dmin ); + + return index; +} + +/*! + \brief Update the widget that represents the item on the legend + + \param legend Legend + \sa drawLegendIdentifier(), legendItem(), QwtPlotItem::Legend +*/ +void QwtPlotCurve::updateLegend( QwtLegend *legend ) const +{ + if ( legend && testItemAttribute( QwtPlotItem::Legend ) + && ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol ) + && d_data->symbol + && d_data->symbol->style() != QwtSymbol::NoSymbol ) + { + QWidget *lgdItem = legend->find( this ); + if ( lgdItem == NULL ) + { + lgdItem = legendItem(); + if ( lgdItem ) + legend->insert( this, lgdItem ); + } + + QwtLegendItem *l = qobject_cast<QwtLegendItem *>( lgdItem ); + if ( l ) + { + QSize sz = d_data->symbol->boundingSize(); + sz += QSize( 2, 2 ); // margin + + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine ) + { + // Avoid, that the line is completely covered by the symbol + + int w = qCeil( 1.5 * sz.width() ); + if ( w % 2 ) + w++; + + sz.setWidth( qMax( 8, w ) ); + } + + l->setIdentifierSize( sz ); + } + } + + QwtPlotItem::updateLegend( legend ); +} + +/*! + \brief Draw the identifier representing the curve on the legend + + \param painter Painter + \param rect Bounding rectangle for the identifier + + \sa setLegendAttribute(), QwtPlotItem::Legend +*/ +void QwtPlotCurve::drawLegendIdentifier( + QPainter *painter, const QRectF &rect ) const +{ + if ( rect.isEmpty() ) + return; + + const double dim = qMin( rect.width(), rect.height() ); + + QSizeF size( dim, dim ); + + QRectF r( 0, 0, size.width(), size.height() ); + r.moveCenter( rect.center() ); + + if ( d_data->legendAttributes == 0 ) + { + QBrush brush = d_data->brush; + if ( brush.style() == Qt::NoBrush ) + { + if ( style() != QwtPlotCurve::NoCurve ) + brush = QBrush( pen().color() ); + else if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + brush = QBrush( d_data->symbol->pen().color() ); + } + } + if ( brush.style() != Qt::NoBrush ) + painter->fillRect( r, brush ); + } + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowBrush ) + { + if ( d_data->brush.style() != Qt::NoBrush ) + painter->fillRect( r, d_data->brush ); + } + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowLine ) + { + if ( pen() != Qt::NoPen ) + { + painter->setPen( pen() ); + QwtPainter::drawLine( painter, rect.left(), rect.center().y(), + rect.right() - 1.0, rect.center().y() ); + } + } + if ( d_data->legendAttributes & QwtPlotCurve::LegendShowSymbol ) + { + if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + QSize symbolSize = d_data->symbol->boundingSize(); + symbolSize -= QSize( 2, 2 ); + + // scale the symbol size down if it doesn't fit into rect. + + double xRatio = 1.0; + if ( rect.width() < symbolSize.width() ) + xRatio = rect.width() / symbolSize.width(); + double yRatio = 1.0; + if ( rect.height() < symbolSize.height() ) + yRatio = rect.height() / symbolSize.height(); + + const double ratio = qMin( xRatio, yRatio ); + + painter->save(); + painter->scale( ratio, ratio ); + + d_data->symbol->drawSymbol( painter, rect.center() / ratio ); + + painter->restore(); + } + } +} + +/*! + Initialize data with an array of points (explicitly shared). + + \param samples Vector of points +*/ +void QwtPlotCurve::setSamples( const QVector<QPointF> &samples ) +{ + delete d_series; + d_series = new QwtPointSeriesData( samples ); + itemChanged(); +} + +#ifndef QWT_NO_COMPAT + +/*! + \brief Initialize the data by pointing to memory blocks which + are not managed by QwtPlotCurve. + + setRawSamples is provided for efficiency. + It is important to keep the pointers + during the lifetime of the underlying QwtCPointerData class. + + \param xData pointer to x data + \param yData pointer to y data + \param size size of x and y + + \sa QwtCPointerData +*/ +void QwtPlotCurve::setRawSamples( + const double *xData, const double *yData, int size ) +{ + delete d_series; + d_series = new QwtCPointerData( xData, yData, size ); + itemChanged(); +} + +/*! + Set data by copying x- and y-values from specified memory blocks. + Contrary to setRawSamples(), this function makes a 'deep copy' of + the data. + + \param xData pointer to x values + \param yData pointer to y values + \param size size of xData and yData + + \sa QwtPointArrayData +*/ +void QwtPlotCurve::setSamples( + const double *xData, const double *yData, int size ) +{ + delete d_series; + d_series = new QwtPointArrayData( xData, yData, size ); + itemChanged(); +} + +/*! + \brief Initialize data with x- and y-arrays (explicitly shared) + + \param xData x data + \param yData y data + + \sa QwtPointArrayData +*/ +void QwtPlotCurve::setSamples( const QVector<double> &xData, + const QVector<double> &yData ) +{ + delete d_series; + d_series = new QwtPointArrayData( xData, yData ); + itemChanged(); +} +#endif // !QWT_NO_COMPAT + diff --git a/src/libpcp_qwt/src/qwt_plot_curve.h b/src/libpcp_qwt/src/qwt_plot_curve.h new file mode 100644 index 0000000..a957c4b --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_curve.h @@ -0,0 +1,319 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_CURVE_H +#define QWT_PLOT_CURVE_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" +#include "qwt_text.h" +#include <qpen.h> +#include <qstring.h> + +class QPainter; +class QPolygonF; +class QwtScaleMap; +class QwtSymbol; +class QwtCurveFitter; + +/*! + \brief A plot item, that represents a series of points + + A curve is the representation of a series of points in the x-y plane. + It supports different display styles, interpolation ( f.e. spline ) + and symbols. + + \par Usage + <dl><dt>a) Assign curve properties</dt> + <dd>When a curve is created, it is configured to draw black solid lines + with in QwtPlotCurve::Lines style and no symbols. + You can change this by calling + setPen(), setStyle() and setSymbol().</dd> + <dt>b) Connect/Assign data.</dt> + <dd>QwtPlotCurve gets its points using a QwtSeriesData object offering + a bridge to the real storage of the points ( like QAbstractItemModel ). + There are several convenience classes derived from QwtSeriesData, that also store + the points inside ( like QStandardItemModel ). QwtPlotCurve also offers + a couple of variations of setSamples(), that build QwtSeriesData objects from + arrays internally.</dd> + <dt>c) Attach the curve to a plot</dt> + <dd>See QwtPlotItem::attach() + </dd></dl> + + \par Example: + see examples/bode + + \sa QwtPointSeriesData, QwtSymbol, QwtScaleMap +*/ +class QWT_EXPORT QwtPlotCurve: public QwtPlotSeriesItem<QPointF> +{ +public: + /*! + Curve styles. + \sa setStyle(), style() + */ + enum CurveStyle + { + /*! + Don't draw a curve. Note: This doesn't affect the symbols. + */ + NoCurve = -1, + + /*! + Connect the points with straight lines. The lines might + be interpolated depending on the 'Fitted' attribute. Curve + fitting can be configured using setCurveFitter(). + */ + Lines, + + /*! + Draw vertical or horizontal sticks ( depending on the + orientation() ) from a baseline which is defined by setBaseline(). + */ + Sticks, + + /*! + Connect the points with a step function. The step function + is drawn from the left to the right or vice versa, + depending on the QwtPlotCurve::Inverted attribute. + */ + Steps, + + /*! + Draw dots at the locations of the data points. Note: + This is different from a dotted line (see setPen()), and faster + as a curve in QwtPlotCurve::NoStyle style and a symbol + painting a point. + */ + Dots, + + /*! + Styles >= QwtPlotCurve::UserCurve are reserved for derived + classes of QwtPlotCurve that overload drawCurve() with + additional application specific curve types. + */ + UserCurve = 100 + }; + + /*! + Attribute for drawing the curve + \sa setCurveAttribute(), testCurveAttribute(), curveFitter() + */ + enum CurveAttribute + { + /*! + For QwtPlotCurve::Steps only. + Draws a step function from the right to the left. + */ + Inverted = 0x01, + + /*! + Only in combination with QwtPlotCurve::Lines + A QwtCurveFitter tries to + interpolate/smooth the curve, before it is painted. + + \note Curve fitting requires temorary memory + for calculating coefficients and additional points. + If painting in QwtPlotCurve::Fitted mode is slow it might be better + to fit the points, before they are passed to QwtPlotCurve. + */ + Fitted = 0x02 + }; + + //! Curve attributes + typedef QFlags<CurveAttribute> CurveAttributes; + + /*! + Attributes how to represent the curve on the legend + + \sa setLegendAttribute(), testLegendAttribute(), + drawLegendIdentifier() + */ + + enum LegendAttribute + { + /*! + QwtPlotCurve tries to find a color representing the curve + and paints a rectangle with it. + */ + LegendNoAttribute = 0x00, + + /*! + If the style() is not QwtPlotCurve::NoCurve a line + is painted with the curve pen(). + */ + LegendShowLine = 0x01, + + /*! + If the curve has a valid symbol it is painted. + */ + LegendShowSymbol = 0x02, + + /*! + If the curve has a brush a rectangle filled with the + curve brush() is painted. + */ + LegendShowBrush = 0x04 + }; + + //! Legend attributes + typedef QFlags<LegendAttribute> LegendAttributes; + + /*! + Attributes to modify the drawing algorithm. + The default setting enables ClipPolygons + + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + Clip polygons before painting them. In situations, where points + are far outside the visible area (f.e when zooming deep) this + might be a substantial improvement for the painting performance + */ + ClipPolygons = 0x01, + + /*! + Paint the symbol to a QPixmap and paint the pixmap + instead rendering the symbol for each point. The flag has + no effect, when the curve is not painted to the canvas + ( f.e when exporting the plot to a PDF document ). + */ + CacheSymbols = 0x02 + }; + + //! Paint attributes + typedef QFlags<PaintAttribute> PaintAttributes; + + explicit QwtPlotCurve( const QString &title = QString::null ); + explicit QwtPlotCurve( const QwtText &title ); + + virtual ~QwtPlotCurve(); + + virtual int rtti() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setLegendAttribute( LegendAttribute, bool on = true ); + bool testLegendAttribute( LegendAttribute ) const; + +#ifndef QWT_NO_COMPAT + void setRawSamples( const double *xData, const double *yData, int size ); + void setSamples( const double *xData, const double *yData, int size ); + void setSamples( const QVector<double> &xData, const QVector<double> &yData ); +#endif + void setSamples( const QVector<QPointF> & ); + + int closestPoint( const QPoint &pos, double *dist = NULL ) const; + + double minXValue() const; + double maxXValue() const; + double minYValue() const; + double maxYValue() const; + + void setCurveAttribute( CurveAttribute, bool on = true ); + bool testCurveAttribute( CurveAttribute ) const; + + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setBaseline( double ref ); + double baseline() const; + + void setStyle( CurveStyle style ); + CurveStyle style() const; + + void setSymbol( const QwtSymbol *s ); + const QwtSymbol *symbol() const; + + void setCurveFitter( QwtCurveFitter * ); + QwtCurveFitter *curveFitter() const; + + virtual void drawSeries( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void updateLegend( QwtLegend * ) const; + virtual void drawLegendIdentifier( QPainter *, const QRectF & ) const; + +protected: + + void init(); + + virtual void drawCurve( QPainter *, int style, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSymbols( QPainter *, const QwtSymbol &, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + void drawLines( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + void drawSticks( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + void drawDots( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + void drawSteps( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void fillCurve( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, + const QRectF &canvasRect, QPolygonF & ) const; + + void closePolyline( QPainter *, + const QwtScaleMap &, const QwtScaleMap &, QPolygonF & ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +//! boundingRect().left() +inline double QwtPlotCurve::minXValue() const +{ + return boundingRect().left(); +} + +//! boundingRect().right() +inline double QwtPlotCurve::maxXValue() const +{ + return boundingRect().right(); +} + +//! boundingRect().top() +inline double QwtPlotCurve::minYValue() const +{ + return boundingRect().top(); +} + +//! boundingRect().bottom() +inline double QwtPlotCurve::maxYValue() const +{ + return boundingRect().bottom(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::PaintAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::LegendAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotCurve::CurveAttributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_dict.cpp b/src/libpcp_qwt/src/qwt_plot_dict.cpp new file mode 100644 index 0000000..4125137 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_dict.cpp @@ -0,0 +1,188 @@ +/* -*- 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_plot_dict.h" + +class QwtPlotDict::PrivateData +{ +public: + + class ItemList: public QList<QwtPlotItem *> + { + public: + void insertItem( QwtPlotItem *item ) + { + if ( item == NULL ) + return; + + QList<QwtPlotItem *>::iterator it = + qUpperBound( begin(), end(), item, LessZThan() ); + insert( it, item ); + } + + void removeItem( QwtPlotItem *item ) + { + if ( item == NULL ) + return; + + QList<QwtPlotItem *>::iterator it = + qLowerBound( begin(), end(), item, LessZThan() ); + + for ( ; it != end(); ++it ) + { + if ( item == *it ) + { + erase( it ); + break; + } + } + } + private: + class LessZThan + { + public: + inline bool operator()( const QwtPlotItem *item1, + const QwtPlotItem *item2 ) const + { + return item1->z() < item2->z(); + } + }; + }; + + ItemList itemList; + bool autoDelete; +}; + +/*! + Constructor + + Auto deletion is enabled. + \sa setAutoDelete(), attachItem() +*/ +QwtPlotDict::QwtPlotDict() +{ + d_data = new QwtPlotDict::PrivateData; + d_data->autoDelete = true; +} + +/*! + Destructor + + If autoDelete is on, all attached items will be deleted + \sa setAutoDelete(), autoDelete(), attachItem() +*/ +QwtPlotDict::~QwtPlotDict() +{ + detachItems( QwtPlotItem::Rtti_PlotItem, d_data->autoDelete ); + delete d_data; +} + +/*! + En/Disable Auto deletion + + If Auto deletion is on all attached plot items will be deleted + in the destructor of QwtPlotDict. The default value is on. + + \sa autoDelete(), attachItem() +*/ +void QwtPlotDict::setAutoDelete( bool autoDelete ) +{ + d_data->autoDelete = autoDelete; +} + +/*! + \return true if auto deletion is enabled + \sa setAutoDelete(), attachItem() +*/ +bool QwtPlotDict::autoDelete() const +{ + return d_data->autoDelete; +} + +/*! + Attach/Detach a plot item + + Attached items will be deleted in the destructor, + if auto deletion is enabled (default). Manually detached + items are not deleted. + + \param item Plot item to attach/detach + \ on If true attach, else detach the item + + \sa setAutoDelete(), ~QwtPlotDict() +*/ +void QwtPlotDict::attachItem( QwtPlotItem *item, bool on ) +{ + if ( on ) + d_data->itemList.insertItem( item ); + else + d_data->itemList.removeItem( item ); +} + +/*! + Detach items from the dictionary + + \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items + otherwise only those items of the type rtti. + \param autoDelete If true, delete all detached items +*/ +void QwtPlotDict::detachItems( int rtti, bool autoDelete ) +{ + PrivateData::ItemList list = d_data->itemList; + QwtPlotItemIterator it = list.begin(); + while ( it != list.end() ) + { + QwtPlotItem *item = *it; + + ++it; // increment before removing item from the list + + if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti ) + { + item->attach( NULL ); + if ( autoDelete ) + delete item; + } + } +} + +/*! + \brief A QwtPlotItemList of all attached plot items. + + Use caution when iterating these lists, as removing/detaching an item will + invalidate the iterator. Instead you can place pointers to objects to be + removed in a removal list, and traverse that list later. + + \return List of all attached plot items. +*/ +const QwtPlotItemList &QwtPlotDict::itemList() const +{ + return d_data->itemList; +} + +/*! + \return List of all attached plot items of a specific type. + \sa QwtPlotItem::rtti() +*/ +QwtPlotItemList QwtPlotDict::itemList( int rtti ) const +{ + if ( rtti == QwtPlotItem::Rtti_PlotItem ) + return d_data->itemList; + + QwtPlotItemList items; + + PrivateData::ItemList list = d_data->itemList; + for ( QwtPlotItemIterator it = list.begin(); it != list.end(); ++it ) + { + QwtPlotItem *item = *it; + if ( item->rtti() == rtti ) + items += item; + } + + return items; +} diff --git a/src/libpcp_qwt/src/qwt_plot_dict.h b/src/libpcp_qwt/src/qwt_plot_dict.h new file mode 100644 index 0000000..0882d28 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_dict.h @@ -0,0 +1,58 @@ +/* -*- 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 + *****************************************************************************/ + +/*! \file !*/ +#ifndef QWT_PLOT_DICT +#define QWT_PLOT_DICT + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include <qlist.h> + +/// \var typedef QList< QwtPlotItem *> QwtPlotItemList +/// \brief See QT 4.x assistant documentation for QList +typedef QList<QwtPlotItem *> QwtPlotItemList; +typedef QList<QwtPlotItem *>::ConstIterator QwtPlotItemIterator; + +/*! + \brief A dictionary for plot items + + QwtPlotDict organizes plot items in increasing z-order. + If autoDelete() is enabled, all attached items will be deleted + in the destructor of the dictionary. + QwtPlotDict can be used to get access to all QwtPlotItem items - or all + items of a specific type - that are currently on the plot. + + \sa QwtPlotItem::attach(), QwtPlotItem::detach(), QwtPlotItem::z() +*/ +class QWT_EXPORT QwtPlotDict +{ +public: + explicit QwtPlotDict(); + virtual ~QwtPlotDict(); + + void setAutoDelete( bool ); + bool autoDelete() const; + + const QwtPlotItemList& itemList() const; + QwtPlotItemList itemList( int rtti ) const; + + void detachItems( int rtti = QwtPlotItem::Rtti_PlotItem, + bool autoDelete = true ); + +private: + friend class QwtPlotItem; + + void attachItem( QwtPlotItem *, bool ); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_directpainter.cpp b/src/libpcp_qwt/src/qwt_plot_directpainter.cpp new file mode 100644 index 0000000..28682aa --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_directpainter.cpp @@ -0,0 +1,313 @@ +/* -*- 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_plot_directpainter.h" +#include "qwt_scale_map.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_plot_seriesitem.h" +#include <qpainter.h> +#include <qevent.h> +#include <qapplication.h> +#include <qpixmap.h> + +static inline void renderItem( + QPainter *painter, const QRect &canvasRect, + QwtPlotAbstractSeriesItem *seriesItem, int from, int to ) +{ + // A minor performance improvement is possible + // with caching the maps. TODO ... + + QwtPlot *plot = seriesItem->plot(); + const QwtScaleMap xMap = plot->canvasMap( seriesItem->xAxis() ); + const QwtScaleMap yMap = plot->canvasMap( seriesItem->yAxis() ); + + painter->setRenderHint( QPainter::Antialiasing, + seriesItem->testRenderHint( QwtPlotItem::RenderAntialiased ) ); + seriesItem->drawSeries( painter, xMap, yMap, canvasRect, from, to ); +} + +class QwtPlotDirectPainter::PrivateData +{ +public: + PrivateData(): + attributes( 0 ), + hasClipping(false), + seriesItem( NULL ) + { + } + + QwtPlotDirectPainter::Attributes attributes; + + bool hasClipping; + QRegion clipRegion; + + QPainter painter; + + QwtPlotAbstractSeriesItem *seriesItem; + int from; + int to; +}; + +//! Constructor +QwtPlotDirectPainter::QwtPlotDirectPainter( QObject *parent ): + QObject( parent ) +{ + d_data = new PrivateData; +} + +//! Destructor +QwtPlotDirectPainter::~QwtPlotDirectPainter() +{ + delete d_data; +} + +/*! + Change an attribute + + \param attribute Attribute to change + \param on On/Off + + \sa Attribute, testAttribute() +*/ +void QwtPlotDirectPainter::setAttribute( Attribute attribute, bool on ) +{ + if ( bool( d_data->attributes & attribute ) != on ) + { + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + if ( ( attribute == AtomicPainter ) && on ) + reset(); + } +} + +/*! + Check if a attribute is set. + + \param attribute Attribute to be tested + \sa Attribute, setAttribute() +*/ +bool QwtPlotDirectPainter::testAttribute( Attribute attribute ) const +{ + return d_data->attributes & attribute; +} + +/*! + En/Disables clipping + + \param enable Enables clipping is true, disable it otherwise + \sa hasClipping(), clipRegion(), setClipRegion() +*/ +void QwtPlotDirectPainter::setClipping( bool enable ) +{ + d_data->hasClipping = enable; +} + +/*! + \return true, when clipping is enabled + \sa setClipping(), clipRegion(), setClipRegion() +*/ +bool QwtPlotDirectPainter::hasClipping() const +{ + return d_data->hasClipping; +} + +/*! + \brief Assign a clip region and enable clipping + + Depending on the environment setting a proper clip region might improve + the performance heavily. F.e. on Qt embedded only the clipped part of + the backing store will be copied to a ( maybe unaccelerated ) frame buffer + device. + + \param region Clip region + \sa clipRegion(), hasClipping(), setClipping() +*/ +void QwtPlotDirectPainter::setClipRegion( const QRegion ®ion ) +{ + d_data->clipRegion = region; + d_data->hasClipping = true; +} + +/*! + \return Currently set clip region. + \sa setClipRegion(), setClipping(), hasClipping() +*/ +QRegion QwtPlotDirectPainter::clipRegion() const +{ + return d_data->clipRegion; +} + +/*! + \brief Draw a set of points of a seriesItem. + + When observing an measurement while it is running, new points have to be + added to an existing seriesItem. drawSeries can be used to display them avoiding + a complete redraw of the canvas. + + Setting plot()->canvas()->setAttribute(Qt::WA_PaintOutsidePaintEvent, true); + will result in faster painting, if the paint engine of the canvas widget + supports this feature. + + \param seriesItem Item to be painted + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + series will be painted to its last point. +*/ +void QwtPlotDirectPainter::drawSeries( + QwtPlotAbstractSeriesItem *seriesItem, int from, int to ) +{ + if ( seriesItem == NULL || seriesItem->plot() == NULL ) + return; + + QwtPlotCanvas *canvas = seriesItem->plot()->canvas(); + const QRect canvasRect = canvas->contentsRect(); + + const bool hasBackingStore = + canvas->testPaintAttribute( QwtPlotCanvas::BackingStore ) + && canvas->backingStore() && !canvas->backingStore()->isNull(); + + if ( hasBackingStore ) + { + QPainter painter( const_cast<QPixmap *>( canvas->backingStore() ) ); + + if ( d_data->hasClipping ) + painter.setClipRegion( d_data->clipRegion ); + + renderItem( &painter, canvasRect, seriesItem, from, to ); + + if ( testAttribute( QwtPlotDirectPainter::FullRepaint ) ) + { + canvas->repaint(); + return; + } + } + + bool immediatePaint = true; + if ( !canvas->testAttribute( Qt::WA_WState_InPaintEvent ) ) + { +#if QT_VERSION < 0x050000 + if ( !canvas->testAttribute( Qt::WA_PaintOutsidePaintEvent ) ) +#endif + immediatePaint = false; + } + + if ( immediatePaint ) + { + if ( !d_data->painter.isActive() ) + { + reset(); + + d_data->painter.begin( canvas ); + canvas->installEventFilter( this ); + } + + if ( d_data->hasClipping ) + { + d_data->painter.setClipRegion( + QRegion( canvasRect ) & d_data->clipRegion ); + } + else + { + if ( !d_data->painter.hasClipping() ) + d_data->painter.setClipRect( canvasRect ); + } + + renderItem( &d_data->painter, canvasRect, seriesItem, from, to ); + + if ( d_data->attributes & QwtPlotDirectPainter::AtomicPainter ) + { + reset(); + } + else + { + if ( d_data->hasClipping ) + d_data->painter.setClipping( false ); + } + } + else + { + reset(); + + d_data->seriesItem = seriesItem; + d_data->from = from; + d_data->to = to; + + QRegion clipRegion = canvasRect; + if ( d_data->hasClipping ) + clipRegion &= d_data->clipRegion; + + canvas->installEventFilter( this ); + canvas->repaint(clipRegion); + canvas->removeEventFilter( this ); + + d_data->seriesItem = NULL; + } +} + +//! Close the internal QPainter +void QwtPlotDirectPainter::reset() +{ + if ( d_data->painter.isActive() ) + { + QWidget *w = ( QWidget * )d_data->painter.device(); + if ( w ) + w->removeEventFilter( this ); + + d_data->painter.end(); + } +} + +//! Event filter +bool QwtPlotDirectPainter::eventFilter( QObject *, QEvent *event ) +{ + if ( event->type() == QEvent::Paint ) + { + reset(); + + if ( d_data->seriesItem ) + { + const QPaintEvent *pe = static_cast< QPaintEvent *>( event ); + + QwtPlotCanvas *canvas = d_data->seriesItem->plot()->canvas(); + + QPainter painter( canvas ); + painter.setClipRegion( pe->region() ); + + bool copyCache = testAttribute( CopyBackingStore ) + && canvas->testPaintAttribute( QwtPlotCanvas::BackingStore ); + + if ( copyCache ) + { + // is something valid in the cache ? + copyCache = ( canvas->backingStore() != NULL ) + && !canvas->backingStore()->isNull(); + } + + if ( copyCache ) + { + painter.drawPixmap( + canvas->contentsRect().topLeft(), + *canvas->backingStore() ); + } + else + { + renderItem( &painter, canvas->contentsRect(), + d_data->seriesItem, d_data->from, d_data->to ); + } + + return true; // don't call QwtPlotCanvas::paintEvent() + } + } + + return false; +} diff --git a/src/libpcp_qwt/src/qwt_plot_directpainter.h b/src/libpcp_qwt/src/qwt_plot_directpainter.h new file mode 100644 index 0000000..ca7dbf9 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_directpainter.h @@ -0,0 +1,100 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_DIRECT_PAINTER_H +#define QWT_PLOT_DIRECT_PAINTER_H + +#include "qwt_global.h" +#include <qobject.h> + +class QRegion; +class QwtPlotAbstractSeriesItem; + +/*! + \brief Painter object trying to paint incrementally + + Often applications want to display samples while they are + collected. When there are too many samples complete replots + will be expensive to be processed in a collection cycle. + + QwtPlotDirectPainter offers an API to paint + subsets ( f.e all additions points ) without erasing/repainting + the plot canvas. + + On certain environments it might be important to calculate a proper + clip region before painting. F.e. for Qt Embedded only the clipped part + of the backing store will be copied to a ( maybe unaccelerated ) + frame buffer. + + \warning Incremental painting will only help when no replot is triggered + by another operation ( like changing scales ) and nothing needs + to be erased. +*/ +class QWT_EXPORT QwtPlotDirectPainter: public QObject +{ +public: + /*! + \brief Paint attributes + \sa setAttribute(), testAttribute(), drawSeries() + */ + enum Attribute + { + /*! + Initializing a QPainter is an expensive operation. + When AtomicPainter is set each call of drawSeries() opens/closes + a temporary QPainter. Otherwise QwtPlotDirectPainter tries to + use the same QPainter as long as possible. + */ + AtomicPainter = 0x01, + + /*! + When FullRepaint is set the plot canvas is explicitely repainted + after the samples have been rendered. + */ + FullRepaint = 0x02, + + /*! + When QwtPlotCanvas::BackingStore is enabled the painter + has to paint to the backing store and the widget. In certain + situations/environments it might be faster to paint to + the backing store only and then copy the backingstore to the canvas. + This flag can also be useful for settings, where Qt fills the + the clip region with the widget background. + */ + CopyBackingStore = 0x04 + }; + + //! Paint attributes + typedef QFlags<Attribute> Attributes; + + QwtPlotDirectPainter( QObject *parent = NULL ); + virtual ~QwtPlotDirectPainter(); + + void setAttribute( Attribute, bool on ); + bool testAttribute( Attribute ) const; + + void setClipping( bool ); + bool hasClipping() const; + + void setClipRegion( const QRegion & ); + QRegion clipRegion() const; + + void drawSeries( QwtPlotAbstractSeriesItem *, int from, int to ); + void reset(); + + virtual bool eventFilter( QObject *, QEvent * ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotDirectPainter::Attributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_grid.cpp b/src/libpcp_qwt/src/qwt_plot_grid.cpp new file mode 100644 index 0000000..8e25aeb --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_grid.cpp @@ -0,0 +1,367 @@ +/* -*- 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_plot_grid.h" +#include "qwt_painter.h" +#include "qwt_text.h" +#include "qwt_scale_map.h" +#include "qwt_scale_div.h" +#include "qwt_math.h" +#include <qpainter.h> +#include <qpen.h> + +class QwtPlotGrid::PrivateData +{ +public: + PrivateData(): + xEnabled( true ), + yEnabled( true ), + xMinEnabled( false ), + yMinEnabled( false ) + { + } + + bool xEnabled; + bool yEnabled; + bool xMinEnabled; + bool yMinEnabled; + + QwtScaleDiv xScaleDiv; + QwtScaleDiv yScaleDiv; + + QPen majPen; + QPen minPen; +}; + +//! Enables major grid, disables minor grid +QwtPlotGrid::QwtPlotGrid(): + QwtPlotItem( QwtText( "Grid" ) ) +{ + d_data = new PrivateData; + setZ( 10.0 ); +} + +//! Destructor +QwtPlotGrid::~QwtPlotGrid() +{ + delete d_data; +} + +//! \return QwtPlotItem::Rtti_PlotGrid +int QwtPlotGrid::rtti() const +{ + return QwtPlotItem::Rtti_PlotGrid; +} + +/*! + \brief Enable or disable vertical gridlines + \param tf Enable (true) or disable + + \sa Minor gridlines can be enabled or disabled with + enableXMin() +*/ +void QwtPlotGrid::enableX( bool tf ) +{ + if ( d_data->xEnabled != tf ) + { + d_data->xEnabled = tf; + itemChanged(); + } +} + +/*! + \brief Enable or disable horizontal gridlines + \param tf Enable (true) or disable + \sa Minor gridlines can be enabled or disabled with enableYMin() +*/ +void QwtPlotGrid::enableY( bool tf ) +{ + if ( d_data->yEnabled != tf ) + { + d_data->yEnabled = tf; + itemChanged(); + } +} + +/*! + \brief Enable or disable minor vertical gridlines. + \param tf Enable (true) or disable + \sa enableX() +*/ +void QwtPlotGrid::enableXMin( bool tf ) +{ + if ( d_data->xMinEnabled != tf ) + { + d_data->xMinEnabled = tf; + itemChanged(); + } +} + +/*! + \brief Enable or disable minor horizontal gridlines + \param tf Enable (true) or disable + \sa enableY() +*/ +void QwtPlotGrid::enableYMin( bool tf ) +{ + if ( d_data->yMinEnabled != tf ) + { + d_data->yMinEnabled = tf; + itemChanged(); + } +} + +/*! + Assign an x axis scale division + + \param scaleDiv Scale division +*/ +void QwtPlotGrid::setXDiv( const QwtScaleDiv &scaleDiv ) +{ + if ( d_data->xScaleDiv != scaleDiv ) + { + d_data->xScaleDiv = scaleDiv; + itemChanged(); + } +} + +/*! + Assign a y axis division + + \param scaleDiv Scale division +*/ +void QwtPlotGrid::setYDiv( const QwtScaleDiv &scaleDiv ) +{ + if ( d_data->yScaleDiv != scaleDiv ) + { + d_data->yScaleDiv = scaleDiv; + itemChanged(); + } +} + +/*! + Assign a pen for both major and minor gridlines + + \param pen Pen + \sa setMajPen(), setMinPen() +*/ +void QwtPlotGrid::setPen( const QPen &pen ) +{ + if ( d_data->majPen != pen || d_data->minPen != pen ) + { + d_data->majPen = pen; + d_data->minPen = pen; + itemChanged(); + } +} + +/*! + Assign a pen for the major gridlines + + \param pen Pen + \sa majPen(), setMinPen(), setPen() +*/ +void QwtPlotGrid::setMajPen( const QPen &pen ) +{ + if ( d_data->majPen != pen ) + { + d_data->majPen = pen; + itemChanged(); + } +} + +/*! + Assign a pen for the minor gridlines + + \param pen Pen + \sa minPen(), setMajPen(), setPen() +*/ +void QwtPlotGrid::setMinPen( const QPen &pen ) +{ + if ( d_data->minPen != pen ) + { + d_data->minPen = pen; + itemChanged(); + } +} + +/*! + \brief Draw the grid + + The grid is drawn into the bounding rectangle such that + gridlines begin and end at the rectangle's borders. The X and Y + maps are used to map the scale divisions into the drawing region + screen. + \param painter Painter + \param xMap X axis map + \param yMap Y axis + \param canvasRect Contents rect of the plot canvas +*/ +void QwtPlotGrid::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + // draw minor gridlines + QPen minPen = d_data->minPen; + minPen.setCapStyle( Qt::FlatCap ); + + painter->setPen( minPen ); + + if ( d_data->xEnabled && d_data->xMinEnabled ) + { + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); + } + + if ( d_data->yEnabled && d_data->yMinEnabled ) + { + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MinorTick ) ); + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MediumTick ) ); + } + + // draw major gridlines + QPen majPen = d_data->majPen; + majPen.setCapStyle( Qt::FlatCap ); + + painter->setPen( majPen ); + + if ( d_data->xEnabled ) + { + drawLines( painter, canvasRect, Qt::Vertical, xMap, + d_data->xScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); + } + + if ( d_data->yEnabled ) + { + drawLines( painter, canvasRect, Qt::Horizontal, yMap, + d_data->yScaleDiv.ticks( QwtScaleDiv::MajorTick ) ); + } +} + +void QwtPlotGrid::drawLines( QPainter *painter, const QRectF &canvasRect, + Qt::Orientation orientation, const QwtScaleMap &scaleMap, + const QList<double> &values ) const +{ + const double x1 = canvasRect.left(); + const double x2 = canvasRect.right() - 1.0; + const double y1 = canvasRect.top(); + const double y2 = canvasRect.bottom() - 1.0; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + for ( int i = 0; i < values.count(); i++ ) + { + double value = scaleMap.transform( values[i] ); + if ( doAlign ) + value = qRound( value ); + + if ( orientation == Qt::Horizontal ) + { + if ( qwtFuzzyGreaterOrEqual( value, y1 ) && + qwtFuzzyLessOrEqual( value, y2 ) ) + { + QwtPainter::drawLine( painter, x1, value, x2, value ); + } + } + else + { + if ( qwtFuzzyGreaterOrEqual( value, x1 ) && + qwtFuzzyLessOrEqual( value, x2 ) ) + { + QwtPainter::drawLine( painter, value, y1, value, y2 ); + } + } + } +} + +/*! + \return the pen for the major gridlines + \sa setMajPen(), setMinPen(), setPen() +*/ +const QPen &QwtPlotGrid::majPen() const +{ + return d_data->majPen; +} + +/*! + \return the pen for the minor gridlines + \sa setMinPen(), setMajPen(), setPen() +*/ +const QPen &QwtPlotGrid::minPen() const +{ + return d_data->minPen; +} + +/*! + \return true if vertical gridlines are enabled + \sa enableX() +*/ +bool QwtPlotGrid::xEnabled() const +{ + return d_data->xEnabled; +} + +/*! + \return true if minor vertical gridlines are enabled + \sa enableXMin() +*/ +bool QwtPlotGrid::xMinEnabled() const +{ + return d_data->xMinEnabled; +} + +/*! + \return true if horizontal gridlines are enabled + \sa enableY() +*/ +bool QwtPlotGrid::yEnabled() const +{ + return d_data->yEnabled; +} + +/*! + \return true if minor horizontal gridlines are enabled + \sa enableYMin() +*/ +bool QwtPlotGrid::yMinEnabled() const +{ + return d_data->yMinEnabled; +} + + +/*! \return the scale division of the x axis */ +const QwtScaleDiv &QwtPlotGrid::xScaleDiv() const +{ + return d_data->xScaleDiv; +} + +/*! \return the scale division of the y axis */ +const QwtScaleDiv &QwtPlotGrid::yScaleDiv() const +{ + return d_data->yScaleDiv; +} + +/*! + Update the grid to changes of the axes scale division + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtPlot::updateAxes() +*/ +void QwtPlotGrid::updateScaleDiv( const QwtScaleDiv& xScaleDiv, + const QwtScaleDiv& yScaleDiv ) +{ + setXDiv( xScaleDiv ); + setYDiv( yScaleDiv ); +} diff --git a/src/libpcp_qwt/src/qwt_plot_grid.h b/src/libpcp_qwt/src/qwt_plot_grid.h new file mode 100644 index 0000000..361ec81 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_grid.h @@ -0,0 +1,84 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_GRID_H +#define QWT_PLOT_GRID_H + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include "qwt_scale_div.h" + +class QPainter; +class QPen; +class QwtScaleMap; +class QwtScaleDiv; + +/*! + \brief A class which draws a coordinate grid + + The QwtPlotGrid class can be used to draw a coordinate grid. + A coordinate grid consists of major and minor vertical + and horizontal gridlines. The locations of the gridlines + are determined by the X and Y scale divisions which can + be assigned with setXDiv() and setYDiv(). + The draw() member draws the grid within a bounding + rectangle. +*/ + +class QWT_EXPORT QwtPlotGrid: public QwtPlotItem +{ +public: + explicit QwtPlotGrid(); + virtual ~QwtPlotGrid(); + + virtual int rtti() const; + + void enableX( bool tf ); + bool xEnabled() const; + + void enableY( bool tf ); + bool yEnabled() const; + + void enableXMin( bool tf ); + bool xMinEnabled() const; + + void enableYMin( bool tf ); + bool yMinEnabled() const; + + void setXDiv( const QwtScaleDiv &sx ); + const QwtScaleDiv &xScaleDiv() const; + + void setYDiv( const QwtScaleDiv &sy ); + const QwtScaleDiv &yScaleDiv() const; + + void setPen( const QPen &p ); + + void setMajPen( const QPen &p ); + const QPen& majPen() const; + + void setMinPen( const QPen &p ); + const QPen& minPen() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + + virtual void updateScaleDiv( + const QwtScaleDiv &xMap, const QwtScaleDiv &yMap ); + +private: + void drawLines( QPainter *painter, const QRectF &, + Qt::Orientation orientation, const QwtScaleMap &, + const QList<double> & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_histogram.cpp b/src/libpcp_qwt/src/qwt_plot_histogram.cpp new file mode 100644 index 0000000..53d0551 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_histogram.cpp @@ -0,0 +1,651 @@ +/* -*- 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_plot_histogram.h" +#include "qwt_plot.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include "qwt_painter.h" +#include "qwt_column_symbol.h" +#include "qwt_scale_map.h" +#include <qstring.h> +#include <qpainter.h> + +static inline bool isCombinable( const QwtInterval &d1, + const QwtInterval &d2 ) +{ + if ( d1.isValid() && d2.isValid() ) + { + if ( d1.maxValue() == d2.minValue() ) + { + if ( !( d1.borderFlags() & QwtInterval::ExcludeMaximum + && d2.borderFlags() & QwtInterval::ExcludeMinimum ) ) + { + return true; + } + } + } + + return false; +} + +class QwtPlotHistogram::PrivateData +{ +public: + PrivateData(): + baseline( 0.0 ), + style( Columns ), + symbol( NULL ) + { + } + + ~PrivateData() + { + delete symbol; + } + + double baseline; + + QPen pen; + QBrush brush; + QwtPlotHistogram::HistogramStyle style; + const QwtColumnSymbol *symbol; +}; + +/*! + Constructor + \param title Title of the histogram. +*/ + +QwtPlotHistogram::QwtPlotHistogram( const QwtText &title ): + QwtPlotSeriesItem<QwtIntervalSample>( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the histogram. +*/ +QwtPlotHistogram::QwtPlotHistogram( const QString &title ): + QwtPlotSeriesItem<QwtIntervalSample>( title ) +{ + init(); +} + +//! Destructor +QwtPlotHistogram::~QwtPlotHistogram() +{ + delete d_data; +} + +//! Initialize data members +void QwtPlotHistogram::init() +{ + d_data = new PrivateData(); + d_series = new QwtIntervalSeriesData(); + + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Legend, true ); + + setZ( 20.0 ); +} + +/*! + Set the histogram's drawing style + + \param style Histogram style + \sa HistogramStyle, style() +*/ +void QwtPlotHistogram::setStyle( HistogramStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + itemChanged(); + } +} + +/*! + Return the current style + \sa HistogramStyle, setStyle() +*/ +QwtPlotHistogram::HistogramStyle QwtPlotHistogram::style() const +{ + return d_data->style; +} + +/*! + Assign a pen, that is used in a style() depending way. + + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotHistogram::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + itemChanged(); + } +} + +/*! + \return Pen used in a style() depending way. + \sa setPen(), brush() +*/ +const QPen &QwtPlotHistogram::pen() const +{ + return d_data->pen; +} + +/*! + Assign a brush, that is used in a style() depending way. + + \param brush New brush + \sa pen(), brush() +*/ +void QwtPlotHistogram::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + itemChanged(); + } +} + +/*! + \return Brush used in a style() depending way. + \sa setPen(), brush() +*/ +const QBrush &QwtPlotHistogram::brush() const +{ + return d_data->brush; +} + +/*! + \brief Assign a symbol + + In Column style an optional symbol can be assigned, that is responsible + for displaying the rectangle that is defined by the interval and + the distance between baseline() and value. When no symbol has been + defined the area is displayed as plain rectangle using pen() and brush(). + + \sa style(), symbol(), drawColumn(), pen(), brush() + + \note In applications, where different intervals need to be displayed + in a different way ( f.e different colors or even using differnt symbols) + it is recommended to overload drawColumn(). +*/ +void QwtPlotHistogram::setSymbol( const QwtColumnSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtColumnSymbol *QwtPlotHistogram::symbol() const +{ + return d_data->symbol; +} + +/*! + \brief Set the value of the baseline + + Each column representing an QwtIntervalSample is defined by its + interval and the interval between baseline and the value of the sample. + + The default value of the baseline is 0.0. + + \param value Value of the baseline + \sa baseline() +*/ +void QwtPlotHistogram::setBaseline( double value ) +{ + if ( d_data->baseline != value ) + { + d_data->baseline = value; + itemChanged(); + } +} + +/*! + \return Value of the baseline + \sa setBaseline() +*/ +double QwtPlotHistogram::baseline() const +{ + return d_data->baseline; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotHistogram::boundingRect() const +{ + QRectF rect = d_series->boundingRect(); + if ( !rect.isValid() ) + return rect; + + if ( orientation() == Qt::Horizontal ) + { + rect = QRectF( rect.y(), rect.x(), + rect.height(), rect.width() ); + + if ( rect.left() > d_data->baseline ) + rect.setLeft( d_data->baseline ); + else if ( rect.right() < d_data->baseline ) + rect.setRight( d_data->baseline ); + } + else + { + if ( rect.bottom() < d_data->baseline ) + rect.setBottom( d_data->baseline ); + else if ( rect.top() > d_data->baseline ) + rect.setTop( d_data->baseline ); + } + + return rect; +} + +//! \return QwtPlotItem::Rtti_PlotHistogram +int QwtPlotHistogram::rtti() const +{ + return QwtPlotItem::Rtti_PlotHistogram; +} + +/*! + Initialize data with an array of samples. + \param samples Vector of points +*/ +void QwtPlotHistogram::setSamples( + const QVector<QwtIntervalSample> &samples ) +{ + delete d_series; + d_series = new QwtIntervalSeriesData( samples ); + itemChanged(); +} + +/*! + Draw a subset of the histogram samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawOutline(), drawLines(), drawColumns +*/ +void QwtPlotHistogram::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &, int from, int to ) const +{ + if ( !painter || dataSize() <= 0 ) + return; + + if ( to < 0 ) + to = dataSize() - 1; + + switch ( d_data->style ) + { + case Outline: + drawOutline( painter, xMap, yMap, from, to ); + break; + case Lines: + drawLines( painter, xMap, yMap, from, to ); + break; + case Columns: + drawColumns( painter, xMap, yMap, from, to ); + break; + default: + break; + } +} + +/*! + Draw a histogram in Outline style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style() + \warning The outline style requires, that the intervals are in increasing + order and not overlapping. +*/ +void QwtPlotHistogram::drawOutline( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double v0 = ( orientation() == Qt::Horizontal ) ? + xMap.transform( baseline() ) : yMap.transform( baseline() ); + if ( doAlign ) + v0 = qRound( v0 ); + + QwtIntervalSample previous; + + QPolygonF polygon; + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = d_series->sample( i ); + + if ( !sample.interval.isValid() ) + { + flushPolygon( painter, v0, polygon ); + previous = sample; + continue; + } + + if ( previous.interval.isValid() ) + { + if ( !isCombinable( previous.interval, sample.interval ) ) + flushPolygon( painter, v0, polygon ); + } + + if ( orientation() == Qt::Vertical ) + { + double x1 = xMap.transform( sample.interval.minValue() ); + double x2 = xMap.transform( sample.interval.maxValue() ); + double y = yMap.transform( sample.value ); + if ( doAlign ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + y = qRound( y ); + } + + if ( polygon.size() == 0 ) + polygon += QPointF( x1, v0 ); + + polygon += QPointF( x1, y ); + polygon += QPointF( x2, y ); + } + else + { + double y1 = yMap.transform( sample.interval.minValue() ); + double y2 = yMap.transform( sample.interval.maxValue() ); + double x = xMap.transform( sample.value ); + if ( doAlign ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + x = qRound( x ); + } + + if ( polygon.size() == 0 ) + polygon += QPointF( v0, y1 ); + + polygon += QPointF( x, y1 ); + polygon += QPointF( x, y2 ); + } + previous = sample; + } + + flushPolygon( painter, v0, polygon ); +} + +/*! + Draw a histogram in Columns style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style(), setSymbol(), drawColumn() +*/ +void QwtPlotHistogram::drawColumns( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + painter->setPen( d_data->pen ); + painter->setBrush( d_data->brush ); + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = d_series->sample( i ); + if ( !sample.interval.isNull() ) + { + const QwtColumnRect rect = columnRect( sample, xMap, yMap ); + drawColumn( painter, rect, sample ); + } + } +} + +/*! + Draw a histogram in Lines style() + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + histogram will be painted to its last point. + + \sa setStyle(), style(), setPen() +*/ +void QwtPlotHistogram::drawLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->setPen( d_data->pen ); + painter->setBrush( Qt::NoBrush ); + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample sample = d_series->sample( i ); + if ( !sample.interval.isNull() ) + { + const QwtColumnRect rect = columnRect( sample, xMap, yMap ); + + QRectF r = rect.toRect(); + if ( doAlign ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + switch ( rect.direction ) + { + case QwtColumnRect::LeftToRight: + { + QwtPainter::drawLine( painter, + r.topRight(), r.bottomRight() ); + break; + } + case QwtColumnRect::RightToLeft: + { + QwtPainter::drawLine( painter, + r.topLeft(), r.bottomLeft() ); + break; + } + case QwtColumnRect::TopToBottom: + { + QwtPainter::drawLine( painter, + r.bottomRight(), r.bottomLeft() ); + break; + } + case QwtColumnRect::BottomToTop: + { + QwtPainter::drawLine( painter, + r.topRight(), r.topLeft() ); + break; + } + } + } + } +} + +//! Internal, used by the Outline style. +void QwtPlotHistogram::flushPolygon( QPainter *painter, + double baseLine, QPolygonF &polygon ) const +{ + if ( polygon.size() == 0 ) + return; + + if ( orientation() == Qt::Horizontal ) + polygon += QPointF( baseLine, polygon.last().y() ); + else + polygon += QPointF( polygon.last().x(), baseLine ); + + if ( d_data->brush.style() != Qt::NoBrush ) + { + painter->setPen( Qt::NoPen ); + painter->setBrush( d_data->brush ); + + if ( orientation() == Qt::Horizontal ) + { + polygon += QPointF( polygon.last().x(), baseLine ); + polygon += QPointF( polygon.first().x(), baseLine ); + } + else + { + polygon += QPointF( baseLine, polygon.last().y() ); + polygon += QPointF( baseLine, polygon.first().y() ); + } + QwtPainter::drawPolygon( painter, polygon ); + int resize = polygon.size(); + if ( resize > 1 ) + resize -= 2; + polygon.resize( resize ); + } + if ( d_data->pen.style() != Qt::NoPen ) + { + painter->setBrush( Qt::NoBrush ); + painter->setPen( d_data->pen ); + QwtPainter::drawPolyline( painter, polygon ); + } + polygon.clear(); +} + +/*! + Calculate the area that is covered by a sample + + \param sample Sample + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + + \return Rectangle, that is covered by a sample +*/ +QwtColumnRect QwtPlotHistogram::columnRect( const QwtIntervalSample &sample, + const QwtScaleMap &xMap, const QwtScaleMap &yMap ) const +{ + QwtColumnRect rect; + + const QwtInterval &iv = sample.interval; + if ( !iv.isValid() ) + return rect; + + if ( orientation() == Qt::Horizontal ) + { + const double x0 = xMap.transform( baseline() ); + const double x = xMap.transform( sample.value ); + const double y1 = yMap.transform( iv.minValue() ); + const double y2 = yMap.transform( iv.maxValue() ); + + rect.hInterval.setInterval( x0, x ); + rect.vInterval.setInterval( y1, y2, iv.borderFlags() ); + rect.direction = ( x < x0 ) ? QwtColumnRect::RightToLeft : + QwtColumnRect::LeftToRight; + } + else + { + const double x1 = xMap.transform( iv.minValue() ); + const double x2 = xMap.transform( iv.maxValue() ); + const double y0 = yMap.transform( baseline() ); + const double y = yMap.transform( sample.value ); + + rect.hInterval.setInterval( x1, x2, iv.borderFlags() ); + rect.vInterval.setInterval( y0, y ); + rect.direction = ( y < y0 ) ? QwtColumnRect::BottomToTop : + QwtColumnRect::TopToBottom; + } + + return rect; +} + +/*! + Draw a column for a sample in Columns style(). + + When a symbol() has been set the symbol is used otherwise the + column is displayed as plain rectangle using pen() and brush(). + + \param painter Painter + \param rect Rectangle where to paint the column in paint device coordinates + \param sample Sample to be displayed + + \note In applications, where different intervals need to be displayed + in a different way ( f.e different colors or even using differnt symbols) + it is recommended to overload drawColumn(). +*/ +void QwtPlotHistogram::drawColumn( QPainter *painter, + const QwtColumnRect &rect, const QwtIntervalSample &sample ) const +{ + Q_UNUSED( sample ); + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtColumnSymbol::NoStyle ) ) + { + d_data->symbol->draw( painter, rect ); + } + else + { + QRectF r = rect.toRect(); + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft( qRound( r.left() ) ); + r.setRight( qRound( r.right() ) ); + r.setTop( qRound( r.top() ) ); + r.setBottom( qRound( r.bottom() ) ); + } + + QwtPainter::drawRect( painter, r ); + } +} + +/*! + Draw a plain rectangle without pen using the brush() as identifier + + \param painter Painter + \param rect Bounding rectangle for the identifier +*/ +void QwtPlotHistogram::drawLegendIdentifier( + QPainter *painter, const QRectF &rect ) const +{ + const double dim = qMin( rect.width(), rect.height() ); + + QSizeF size( dim, dim ); + + QRectF r( 0, 0, size.width(), size.height() ); + r.moveCenter( rect.center() ); + + painter->fillRect( r, d_data->brush ); +} diff --git a/src/libpcp_qwt/src/qwt_plot_histogram.h b/src/libpcp_qwt/src/qwt_plot_histogram.h new file mode 100644 index 0000000..3e40c45 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_histogram.h @@ -0,0 +1,134 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_HISTOGRAM_H +#define QWT_PLOT_HISTOGRAM_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_column_symbol.h" +#include <qcolor.h> +#include <qvector.h> + +class QwtIntervalData; +class QString; +class QPolygonF; + +/*! + \brief QwtPlotHistogram represents a series of samples, where an interval + is associated with a value ( \f$y = f([x1,x2])\f$ ). + + The representation depends on the style() and an optional symbol() + that is displayed for each interval. + + \note The term "histogram" is used in a different way in the areas of + digital image processing and statistics. Wikipedia introduces the + terms "image histogram" and "color histogram" to avoid confusions. + While "image histograms" can be displayed by a QwtPlotCurve there + is no applicable plot item for a "color histogram" yet. +*/ + +class QWT_EXPORT QwtPlotHistogram: public QwtPlotSeriesItem<QwtIntervalSample> +{ +public: + /*! + Histogram styles. + The default style is QwtPlotHistogram::Columns. + + \sa setStyle(), style(), setSymbol(), symbol(), setBaseline() + */ + enum HistogramStyle + { + /*! + Draw an outline around the area, that is build by all intervals + using the pen() and fill it with the brush(). The outline style + requires, that the intervals are in increasing order and + not overlapping. + */ + Outline, + + /*! + Draw a column for each interval. When a symbol() has been set + the symbol is used otherwise the column is displayed as + plain rectangle using pen() and brush(). + */ + Columns, + + /*! + Draw a simple line using the pen() for each interval. + */ + Lines, + + /*! + Styles >= UserStyle are reserved for derived + classes that overload drawSeries() with + additional application specific ways to display a histogram. + */ + UserStyle = 100 + }; + + explicit QwtPlotHistogram( const QString &title = QString::null ); + explicit QwtPlotHistogram( const QwtText &title ); + virtual ~QwtPlotHistogram(); + + virtual int rtti() const; + + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setSamples( const QVector<QwtIntervalSample> & ); + + void setBaseline( double reference ); + double baseline() const; + + void setStyle( HistogramStyle style ); + HistogramStyle style() const; + + void setSymbol( const QwtColumnSymbol * ); + const QwtColumnSymbol *symbol() const; + + virtual void drawSeries( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + + virtual void drawLegendIdentifier( QPainter *, const QRectF & ) const; + +protected: + virtual QwtColumnRect columnRect( const QwtIntervalSample &, + const QwtScaleMap &, const QwtScaleMap & ) const; + + virtual void drawColumn( QPainter *, const QwtColumnRect &, + const QwtIntervalSample & ) const; + + void drawColumns( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + + void drawOutline( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + + void drawLines( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + int from, int to ) const; + +private: + void init(); + void flushPolygon( QPainter *, double baseLine, QPolygonF & ) const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_intervalcurve.cpp b/src/libpcp_qwt/src/qwt_plot_intervalcurve.cpp new file mode 100644 index 0000000..1edf0f1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_intervalcurve.cpp @@ -0,0 +1,548 @@ +/* -*- 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_plot_intervalcurve.h" +#include "qwt_interval_symbol.h" +#include "qwt_scale_map.h" +#include "qwt_clipper.h" +#include "qwt_painter.h" + +#include <qpainter.h> + +static inline bool qwtIsHSampleInside( const QwtIntervalSample &sample, + double xMin, double xMax, double yMin, double yMax ) +{ + const double y = sample.value; + const double x1 = sample.interval.minValue(); + const double x2 = sample.interval.maxValue(); + + const bool isOffScreen = ( y < yMin ) || ( y > yMax ) + || ( x1 < xMin && x2 < xMin ) || ( x1 > xMax && x2 > xMax ); + + return !isOffScreen; +} + +static inline bool qwtIsVSampleInside( const QwtIntervalSample &sample, + double xMin, double xMax, double yMin, double yMax ) +{ + const double x = sample.value; + const double y1 = sample.interval.minValue(); + const double y2 = sample.interval.maxValue(); + + const bool isOffScreen = ( x < xMin ) || ( x > xMax ) + || ( y1 < yMin && y2 < yMin ) || ( y1 > yMax && y2 > yMax ); + + return !isOffScreen; +} + +class QwtPlotIntervalCurve::PrivateData +{ +public: + PrivateData(): + style( QwtPlotIntervalCurve::Tube ), + symbol( NULL ), + pen( Qt::black ), + brush( Qt::white ) + { + paintAttributes = QwtPlotIntervalCurve::ClipPolygons; + paintAttributes |= QwtPlotIntervalCurve::ClipSymbol; + + pen.setCapStyle( Qt::FlatCap ); + } + + ~PrivateData() + { + delete symbol; + } + + QwtPlotIntervalCurve::CurveStyle style; + const QwtIntervalSymbol *symbol; + + QPen pen; + QBrush brush; + + QwtPlotIntervalCurve::PaintAttributes paintAttributes; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QwtText &title ): + QwtPlotSeriesItem<QwtIntervalSample>( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotIntervalCurve::QwtPlotIntervalCurve( const QString &title ): + QwtPlotSeriesItem<QwtIntervalSample>( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotIntervalCurve::~QwtPlotIntervalCurve() +{ + delete d_data; +} + +//! Initialize internal members +void QwtPlotIntervalCurve::init() +{ + setItemAttribute( QwtPlotItem::Legend, true ); + setItemAttribute( QwtPlotItem::AutoScale, true ); + + d_data = new PrivateData; + d_series = new QwtIntervalSeriesData(); + + setZ( 19.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotIntervalCurve +int QwtPlotIntervalCurve::rtti() const +{ + return QwtPlotIntervalCurve::Rtti_PlotIntervalCurve; +} + +/*! + Specify an attribute how to draw the curve + + \param attribute Paint attribute + \param on On/Off + \sa testPaintAttribute() +*/ +void QwtPlotIntervalCurve::setPaintAttribute( + PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \brief Return the current paint attributes + \sa PaintAttribute, setPaintAttribute() +*/ +bool QwtPlotIntervalCurve::testPaintAttribute( + PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + Initialize data with an array of samples. + \param samples Vector of samples +*/ +void QwtPlotIntervalCurve::setSamples( + const QVector<QwtIntervalSample> &samples ) +{ + delete d_series; + d_series = new QwtIntervalSeriesData( samples ); + itemChanged(); +} + +/*! + Set the curve's drawing style + + \param style Curve style + \sa CurveStyle, style() +*/ +void QwtPlotIntervalCurve::setStyle( CurveStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + itemChanged(); + } +} + +/*! + \brief Return the current style + \sa setStyle() +*/ +QwtPlotIntervalCurve::CurveStyle QwtPlotIntervalCurve::style() const +{ + return d_data->style; +} + +/*! + Assign a symbol. + + \param symbol Symbol + \sa symbol() +*/ +void QwtPlotIntervalCurve::setSymbol( const QwtIntervalSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + itemChanged(); + } +} + +/*! + \return Current symbol or NULL, when no symbol has been assigned + \sa setSymbol() +*/ +const QwtIntervalSymbol *QwtPlotIntervalCurve::symbol() const +{ + return d_data->symbol; +} + +/*! + \brief Assign a pen + \param pen New pen + \sa pen(), brush() +*/ +void QwtPlotIntervalCurve::setPen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + itemChanged(); + } +} + +/*! + \brief Return the pen used to draw the lines + \sa setPen(), brush() +*/ +const QPen& QwtPlotIntervalCurve::pen() const +{ + return d_data->pen; +} + +/*! + Assign a brush. + + The brush is used to fill the area in Tube style(). + + \param brush Brush + \sa brush(), pen(), setStyle(), CurveStyle +*/ +void QwtPlotIntervalCurve::setBrush( const QBrush &brush ) +{ + if ( brush != d_data->brush ) + { + d_data->brush = brush; + itemChanged(); + } +} + +/*! + \return Brush used to fill the area in Tube style() + \sa setBrush(), setStyle(), CurveStyle +*/ +const QBrush& QwtPlotIntervalCurve::brush() const +{ + return d_data->brush; +} + +/*! + \return Bounding rectangle of all samples. + For an empty series the rectangle is invalid. +*/ +QRectF QwtPlotIntervalCurve::boundingRect() const +{ + QRectF rect = QwtPlotSeriesItem<QwtIntervalSample>::boundingRect(); + if ( rect.isValid() && orientation() == Qt::Vertical ) + rect.setRect( rect.y(), rect.x(), rect.height(), rect.width() ); + + return rect; +} + +/*! + Draw a subset of the samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawTube(), drawSymbols() +*/ +void QwtPlotIntervalCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( to < 0 ) + to = dataSize() - 1; + + if ( from < 0 ) + from = 0; + + if ( from > to ) + return; + + switch ( d_data->style ) + { + case Tube: + drawTube( painter, xMap, yMap, canvasRect, from, to ); + break; + + case NoCurve: + default: + break; + } + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) + { + drawSymbols( painter, *d_data->symbol, + xMap, yMap, canvasRect, from, to ); + } +} + +/*! + Draw a tube + + Builds 2 curves from the upper and lower limits of the intervals + and draws them with the pen(). The area between the curves is + filled with the brush(). + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawSeries(), drawSymbols() +*/ +void QwtPlotIntervalCurve::drawTube( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->save(); + + const size_t size = to - from + 1; + QPolygonF polygon( 2 * size ); + QPointF *points = polygon.data(); + + for ( uint i = 0; i < size; i++ ) + { + QPointF &minValue = points[i]; + QPointF &maxValue = points[2 * size - 1 - i]; + + const QwtIntervalSample intervalSample = sample( from + i ); + if ( orientation() == Qt::Vertical ) + { + double x = xMap.transform( intervalSample.value ); + double y1 = yMap.transform( intervalSample.interval.minValue() ); + double y2 = yMap.transform( intervalSample.interval.maxValue() ); + if ( doAlign ) + { + x = qRound( x ); + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + minValue.rx() = x; + minValue.ry() = y1; + maxValue.rx() = x; + maxValue.ry() = y2; + } + else + { + double y = yMap.transform( intervalSample.value ); + double x1 = xMap.transform( intervalSample.interval.minValue() ); + double x2 = xMap.transform( intervalSample.interval.maxValue() ); + if ( doAlign ) + { + y = qRound( y ); + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + minValue.rx() = x1; + minValue.ry() = y; + maxValue.rx() = x2; + maxValue.ry() = y; + } + } + + if ( d_data->brush.style() != Qt::NoBrush ) + { + painter->setPen( QPen( Qt::NoPen ) ); + painter->setBrush( d_data->brush ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + const qreal m = 1.0; + const QPolygonF p = QwtClipper::clipPolygonF( + canvasRect.adjusted( -m, -m, m, m ), polygon, true ); + + QwtPainter::drawPolygon( painter, p ); + } + else + { + QwtPainter::drawPolygon( painter, polygon ); + } + } + + if ( d_data->pen.style() != Qt::NoPen ) + { + painter->setPen( d_data->pen ); + painter->setBrush( Qt::NoBrush ); + + if ( d_data->paintAttributes & ClipPolygons ) + { + qreal pw = qMax( qreal( 1.0 ), painter->pen().widthF() ); + const QRectF clipRect = canvasRect.adjusted( -pw, -pw, pw, pw ); + + QPolygonF p; + + p.resize( size ); + qMemCopy( p.data(), points, size * sizeof( QPointF ) ); + p = QwtClipper::clipPolygonF( clipRect, p ); + QwtPainter::drawPolyline( painter, p ); + + p.resize( size ); + qMemCopy( p.data(), points + size, size * sizeof( QPointF ) ); + p = QwtClipper::clipPolygonF( clipRect, p ); + QwtPainter::drawPolyline( painter, p ); + } + else + { + QwtPainter::drawPolyline( painter, points, size ); + QwtPainter::drawPolyline( painter, points + size, size ); + } + } + + painter->restore(); +} + +/*! + Draw symbols for a subset of the samples + + \param painter Painter + \param symbol Interval symbol + \param xMap x map + \param yMap y map + \param canvasRect Contents rect of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted + + \sa setSymbol(), drawSeries(), drawTube() +*/ +void QwtPlotIntervalCurve::drawSymbols( + QPainter *painter, const QwtIntervalSymbol &symbol, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + painter->save(); + + QPen pen = symbol.pen(); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->setBrush( symbol.brush() ); + + const QRectF tr = QwtScaleMap::invTransform( xMap, yMap, canvasRect ); + + const double xMin = tr.left(); + const double xMax = tr.right(); + const double yMin = tr.top(); + const double yMax = tr.bottom(); + + const bool doClip = d_data->paintAttributes & ClipSymbol; + + for ( int i = from; i <= to; i++ ) + { + const QwtIntervalSample s = sample( i ); + + if ( orientation() == Qt::Vertical ) + { + if ( !doClip || qwtIsVSampleInside( s, xMin, xMax, yMin, yMax ) ) + { + const double x = xMap.transform( s.value ); + const double y1 = yMap.transform( s.interval.minValue() ); + const double y2 = yMap.transform( s.interval.maxValue() ); + + symbol.draw( painter, orientation(), + QPointF( x, y1 ), QPointF( x, y2 ) ); + } + } + else + { + if ( !doClip || qwtIsHSampleInside( s, xMin, xMax, yMin, yMax ) ) + { + const double y = yMap.transform( s.value ); + const double x1 = xMap.transform( s.interval.minValue() ); + const double x2 = xMap.transform( s.interval.maxValue() ); + + symbol.draw( painter, orientation(), + QPointF( x1, y ), QPointF( x2, y ) ); + } + } + } + + painter->restore(); +} + +/*! + \brief Draw the identifier for the legend + + In case of Tube style() a plain rectangle filled with the brush() is painted. + If a symbol is assigned it is painted centered into rect. + + \param painter Painter + \param rect Bounding rectangle for the identifier +*/ +void QwtPlotIntervalCurve::drawLegendIdentifier( + QPainter *painter, const QRectF &rect ) const +{ + const double dim = qMin( rect.width(), rect.height() ); + + QSizeF size( dim, dim ); + + QRectF r( 0, 0, size.width(), size.height() ); + r.moveCenter( rect.center() ); + + if ( d_data->style == Tube ) + { + painter->fillRect( r, d_data->brush ); + } + + if ( d_data->symbol && + ( d_data->symbol->style() != QwtIntervalSymbol::NoSymbol ) ) + { + QPen pen = d_data->symbol->pen(); + pen.setWidthF( pen.widthF() ); + pen.setCapStyle( Qt::FlatCap ); + + painter->setPen( pen ); + painter->setBrush( d_data->symbol->brush() ); + + if ( orientation() == Qt::Vertical ) + { + d_data->symbol->draw( painter, orientation(), + QPointF( r.center().x(), r.top() ), + QPointF( r.center().x(), r.bottom() - 1 ) ); + } + else + { + d_data->symbol->draw( painter, orientation(), + QPointF( r.left(), r.center().y() ), + QPointF( r.right() - 1, r.center().y() ) ); + } + } +} diff --git a/src/libpcp_qwt/src/qwt_plot_intervalcurve.h b/src/libpcp_qwt/src/qwt_plot_intervalcurve.h new file mode 100644 index 0000000..b26586e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_intervalcurve.h @@ -0,0 +1,130 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_INTERVAL_CURVE_H +#define QWT_PLOT_INTERVAL_CURVE_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" + +class QwtIntervalSymbol; + +/*! + \brief QwtPlotIntervalCurve represents a series of samples, where each value + is associated with an interval ( \f$[y1,y2] = f(x)\f$ ). + + The representation depends on the style() and an optional symbol() + that is displayed for each interval. QwtPlotIntervalCurve might be used + to disply error bars or the area between 2 curves. +*/ + +class QWT_EXPORT QwtPlotIntervalCurve: public QwtPlotSeriesItem<QwtIntervalSample> +{ +public: + /*! + \brief Curve styles. + The default setting is QwtPlotIntervalCurve::Tube. + + \sa setStyle(), style() + */ + + enum CurveStyle + { + /*! + Don't draw a curve. Note: This doesn't affect the symbols. + */ + NoCurve, + + /*! + Build 2 curves from the upper and lower limits of the intervals + and draw them with the pen(). The area between the curves is + filled with the brush(). + */ + Tube, + + /*! + Styles >= QwtPlotIntervalCurve::UserCurve are reserved for derived + classes that overload drawSeries() with + additional application specific curve types. + */ + UserCurve = 100 + }; + + /*! + Attributes to modify the drawing algorithm. + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + Clip polygons before painting them. In situations, where points + are far outside the visible area (f.e when zooming deep) this + might be a substantial improvement for the painting performance. + */ + ClipPolygons = 0x01, + + //! Check if a symbol is on the plot canvas before painting it. + ClipSymbol = 0x02 + }; + + //! Paint attributes + typedef QFlags<PaintAttribute> PaintAttributes; + + explicit QwtPlotIntervalCurve( const QString &title = QString::null ); + explicit QwtPlotIntervalCurve( const QwtText &title ); + + virtual ~QwtPlotIntervalCurve(); + + virtual int rtti() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setSamples( const QVector<QwtIntervalSample> & ); + + void setPen( const QPen & ); + const QPen &pen() const; + + void setBrush( const QBrush & ); + const QBrush &brush() const; + + void setStyle( CurveStyle style ); + CurveStyle style() const; + + void setSymbol( const QwtIntervalSymbol * ); + const QwtIntervalSymbol *symbol() const; + + virtual void drawSeries( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual QRectF boundingRect() const; + virtual void drawLegendIdentifier( QPainter *, const QRectF & ) const; + +protected: + + void init(); + + virtual void drawTube( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + virtual void drawSymbols( QPainter *, const QwtIntervalSymbol &, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotIntervalCurve::PaintAttributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_item.cpp b/src/libpcp_qwt/src/qwt_plot_item.cpp new file mode 100644 index 0000000..5ed0d40 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_item.cpp @@ -0,0 +1,542 @@ +/* -*- 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_plot_item.h" +#include "qwt_text.h" +#include "qwt_plot.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include "qwt_scale_div.h" +#include <qpainter.h> + +class QwtPlotItem::PrivateData +{ +public: + PrivateData(): + plot( NULL ), + isVisible( true ), + attributes( 0 ), + renderHints( 0 ), + z( 0.0 ), + xAxis( QwtPlot::xBottom ), + yAxis( QwtPlot::yLeft ) + { + } + + mutable QwtPlot *plot; + + bool isVisible; + QwtPlotItem::ItemAttributes attributes; + QwtPlotItem::RenderHints renderHints; + double z; + + int xAxis; + int yAxis; + + QwtText title; +}; + +/*! + Constructor + \param title Title of the item +*/ +QwtPlotItem::QwtPlotItem( const QwtText &title ) +{ + d_data = new PrivateData; + d_data->title = title; +} + +//! Destroy the QwtPlotItem +QwtPlotItem::~QwtPlotItem() +{ + attach( NULL ); + delete d_data; +} + +/*! + \brief Attach the item to a plot. + + This method will attach a QwtPlotItem to the QwtPlot argument. It will first + detach the QwtPlotItem from any plot from a previous call to attach (if + necessary). If a NULL argument is passed, it will detach from any QwtPlot it + was attached to. + + \param plot Plot widget + \sa detach() +*/ +void QwtPlotItem::attach( QwtPlot *plot ) +{ + if ( plot == d_data->plot ) + return; + + // remove the item from the previous plot + + if ( d_data->plot ) + { + if ( d_data->plot->legend() ) + d_data->plot->legend()->remove( this ); + + d_data->plot->attachItem( this, false ); + + if ( d_data->plot->autoReplot() ) + d_data->plot->update(); + } + + d_data->plot = plot; + + if ( d_data->plot ) + { + // insert the item into the current plot + + d_data->plot->attachItem( this, true ); + itemChanged(); + } +} + +/*! + \brief This method detaches a QwtPlotItem from any + QwtPlot it has been associated with. + + detach() is equivalent to calling attach( NULL ) + \sa attach() +*/ +void QwtPlotItem::detach() +{ + attach( NULL ); +} + +/*! + Return rtti for the specific class represented. QwtPlotItem is simply + a virtual interface class, and base classes will implement this method + with specific rtti values so a user can differentiate them. + + The rtti value is useful for environments, where the + runtime type information is disabled and it is not possible + to do a dynamic_cast<...>. + + \return rtti value + \sa RttiValues +*/ +int QwtPlotItem::rtti() const +{ + return Rtti_PlotItem; +} + +//! Return attached plot +QwtPlot *QwtPlotItem::plot() const +{ + return d_data->plot; +} + +/*! + Plot items are painted in increasing z-order. + + \return setZ(), QwtPlotDict::itemList() +*/ +double QwtPlotItem::z() const +{ + return d_data->z; +} + +/*! + \brief Set the z value + + Plot items are painted in increasing z-order. + + \param z Z-value + \sa z(), QwtPlotDict::itemList() +*/ +void QwtPlotItem::setZ( double z ) +{ + if ( d_data->z != z ) + { + if ( d_data->plot ) // update the z order + d_data->plot->attachItem( this, false ); + + d_data->z = z; + + if ( d_data->plot ) + d_data->plot->attachItem( this, true ); + + itemChanged(); + } +} + +/*! + Set a new title + + \param title Title + \sa title() +*/ +void QwtPlotItem::setTitle( const QString &title ) +{ + setTitle( QwtText( title ) ); +} + +/*! + Set a new title + + \param title Title + \sa title() +*/ +void QwtPlotItem::setTitle( const QwtText &title ) +{ + if ( d_data->title != title ) + { + d_data->title = title; + itemChanged(); + } +} + +/*! + \return Title of the item + \sa setTitle() +*/ +const QwtText &QwtPlotItem::title() const +{ + return d_data->title; +} + +/*! + Toggle an item attribute + + \param attribute Attribute type + \param on true/false + + \sa testItemAttribute(), ItemAttribute +*/ +void QwtPlotItem::setItemAttribute( ItemAttribute attribute, bool on ) +{ + if ( bool( d_data->attributes & attribute ) != on ) + { + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; + + itemChanged(); + } +} + +/*! + Test an item attribute + + \param attribute Attribute type + \return true/false + \sa setItemAttribute(), ItemAttribute +*/ +bool QwtPlotItem::testItemAttribute( ItemAttribute attribute ) const +{ + return ( d_data->attributes & attribute ); +} + +/*! + Toggle an render hint + + \param hint Render hint + \param on true/false + + \sa testRenderHint(), RenderHint +*/ +void QwtPlotItem::setRenderHint( RenderHint hint, bool on ) +{ + if ( ( ( d_data->renderHints & hint ) != 0 ) != on ) + { + if ( on ) + d_data->renderHints |= hint; + else + d_data->renderHints &= ~hint; + + itemChanged(); + } +} + +/*! + Test a render hint + + \param hint Render hint + \return true/false + \sa setRenderHint(), RenderHint +*/ +bool QwtPlotItem::testRenderHint( RenderHint hint ) const +{ + return ( d_data->renderHints & hint ); +} + +//! Show the item +void QwtPlotItem::show() +{ + setVisible( true ); +} + +//! Hide the item +void QwtPlotItem::hide() +{ + setVisible( false ); +} + +/*! + Show/Hide the item + + \param on Show if true, otherwise hide + \sa isVisible(), show(), hide() +*/ +void QwtPlotItem::setVisible( bool on ) +{ + if ( on != d_data->isVisible ) + { + d_data->isVisible = on; + itemChanged(); + } +} + +/*! + \return true if visible + \sa setVisible(), show(), hide() +*/ +bool QwtPlotItem::isVisible() const +{ + return d_data->isVisible; +} + +/*! + Update the legend and call QwtPlot::autoRefresh for the + parent plot. + + \sa updateLegend() +*/ +void QwtPlotItem::itemChanged() +{ + if ( d_data->plot ) + { + if ( d_data->plot->legend() ) + updateLegend( d_data->plot->legend() ); + + d_data->plot->autoRefresh(); + } +} + +/*! + Set X and Y axis + + The item will painted according to the coordinates its Axes. + + \param xAxis X Axis + \param yAxis Y Axis + + \sa setXAxis(), setYAxis(), xAxis(), yAxis() +*/ +void QwtPlotItem::setAxes( int xAxis, int yAxis ) +{ + if ( xAxis == QwtPlot::xBottom || xAxis == QwtPlot::xTop ) + d_data->xAxis = xAxis; + + if ( yAxis == QwtPlot::yLeft || yAxis == QwtPlot::yRight ) + d_data->yAxis = yAxis; + + itemChanged(); +} + +/*! + Set the X axis + + The item will painted according to the coordinates its Axes. + + \param axis X Axis + \sa setAxes(), setYAxis(), xAxis() +*/ +void QwtPlotItem::setXAxis( int axis ) +{ + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { + d_data->xAxis = axis; + itemChanged(); + } +} + +/*! + Set the Y axis + + The item will painted according to the coordinates its Axes. + + \param axis Y Axis + \sa setAxes(), setXAxis(), yAxis() +*/ +void QwtPlotItem::setYAxis( int axis ) +{ + if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) + { + d_data->yAxis = axis; + itemChanged(); + } +} + +//! Return xAxis +int QwtPlotItem::xAxis() const +{ + return d_data->xAxis; +} + +//! Return yAxis +int QwtPlotItem::yAxis() const +{ + return d_data->yAxis; +} + +/*! + \return An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0) +*/ +QRectF QwtPlotItem::boundingRect() const +{ + return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid +} + +/*! + \brief Allocate the widget that represents the item on the legend + + The default implementation returns a QwtLegendItem(), but an item + could be represented by any type of widget, + by overloading legendItem() and updateLegend(). + + \return QwtLegendItem() + \sa updateLegend() QwtLegend() +*/ +QWidget *QwtPlotItem::legendItem() const +{ + QwtLegendItem *item = new QwtLegendItem; + if ( d_data->plot ) + { + QObject::connect( item, SIGNAL( clicked() ), + d_data->plot, SLOT( legendItemClicked() ) ); + QObject::connect( item, SIGNAL( checked( bool ) ), + d_data->plot, SLOT( legendItemChecked( bool ) ) ); + } + return item; +} + +/*! + \brief Update the widget that represents the item on the legend + + updateLegend() is called from itemChanged() to adopt the widget + representing the item on the legend to its new configuration. + + The default implementation updates a QwtLegendItem(), + but an item could be represented by any type of widget, + by overloading legendItem() and updateLegend(). + + \param legend Legend + + \sa legendItem(), itemChanged(), QwtLegend() +*/ +void QwtPlotItem::updateLegend( QwtLegend *legend ) const +{ + if ( legend == NULL ) + return; + + QWidget *lgdItem = legend->find( this ); + if ( testItemAttribute( QwtPlotItem::Legend ) ) + { + if ( lgdItem == NULL ) + { + lgdItem = legendItem(); + if ( lgdItem ) + legend->insert( this, lgdItem ); + } + + QwtLegendItem *label = qobject_cast<QwtLegendItem *>( lgdItem ); + if ( label ) + { + // paint the identifier + const QSize sz = label->identifierSize(); + + QPixmap identifier( sz.width(), sz.height() ); + identifier.fill( Qt::transparent ); + + QPainter painter( &identifier ); + painter.setRenderHint( QPainter::Antialiasing, + testRenderHint( QwtPlotItem::RenderAntialiased ) ); + drawLegendIdentifier( &painter, + QRect( 0, 0, sz.width(), sz.height() ) ); + painter.end(); + + const bool doUpdate = label->updatesEnabled(); + if ( doUpdate ) + label->setUpdatesEnabled( false ); + + label->setText( title() ); + label->setIdentifier( identifier ); + label->setItemMode( legend->itemMode() ); + + if ( doUpdate ) + label->setUpdatesEnabled( true ); + + label->update(); + } + } + else + { + if ( lgdItem ) + { + lgdItem->hide(); + lgdItem->deleteLater(); + } + } +} + +/*! + \brief Update the item to changes of the axes scale division + + Update the item, when the axes of plot have changed. + The default implementation does nothing, but items that depend + on the scale division (like QwtPlotGrid()) have to reimplement + updateScaleDiv() + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtPlot::updateAxes() +*/ +void QwtPlotItem::updateScaleDiv( const QwtScaleDiv &xScaleDiv, + const QwtScaleDiv &yScaleDiv ) +{ + Q_UNUSED( xScaleDiv ); + Q_UNUSED( yScaleDiv ); +} + +/*! + \brief Calculate the bounding scale rect of 2 maps + + \param xMap X map + \param yMap Y map + + \return Bounding scale rect of the scale maps, not normalized +*/ +QRectF QwtPlotItem::scaleRect( const QwtScaleMap &xMap, + const QwtScaleMap &yMap ) const +{ + return QRectF( xMap.s1(), yMap.s1(), + xMap.sDist(), yMap.sDist() ); +} + +/*! + \brief Calculate the bounding paint rect of 2 maps + + \param xMap X map + \param yMap Y map + + \return Bounding paint rect of the scale maps, not normalized +*/ +QRectF QwtPlotItem::paintRect( const QwtScaleMap &xMap, + const QwtScaleMap &yMap ) const +{ + const QRectF rect( xMap.p1(), yMap.p1(), + xMap.pDist(), yMap.pDist() ); + + return rect; +} diff --git a/src/libpcp_qwt/src/qwt_plot_item.h b/src/libpcp_qwt/src/qwt_plot_item.h new file mode 100644 index 0000000..5000fff --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_item.h @@ -0,0 +1,214 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_ITEM_H +#define QWT_PLOT_ITEM_H + +#include "qwt_global.h" +#include "qwt_legend_itemmanager.h" +#include "qwt_text.h" +#include <qrect.h> + +class QString; +class QPainter; +class QWidget; +class QwtPlot; +class QwtLegend; +class QwtScaleMap; +class QwtScaleDiv; + +/*! + \brief Base class for items on the plot canvas + + A plot item is "something", that can be painted on the plot canvas, + or only affects the scales of the plot widget. They can be categorized as: + + - Representator\n + A "Representator" is an item that represents some sort of data + on the plot canvas. The different representator classes are organized + according to the characteristics of the data: + - QwtPlotMarker + Represents a point or a horizontal/vertical coordinate + - QwtPlotCurve + Represents a series of points + - QwtPlotSpectrogram ( QwtPlotRasterItem ) + Represents raster data + - ... + + - Decorators\n + A "Decorator" is an item, that displays additional information, that + is not related to any data: + - QwtPlotGrid + - QwtPlotScaleItem + - QwtPlotSvgItem + - ... + + Depending on the QwtPlotItem::ItemAttribute flags, an item is included + into autoscaling or has an entry on the legnd. + + Before misusing the existing item classes it might be better to + implement a new type of plot item + ( don't implement a watermark as spectrogram ). + Deriving a new type of QwtPlotItem primarily means to implement + the YourPlotItem::draw() method. + + \sa The cpuplot example shows the implementation of additional plot items. +*/ + +class QWT_EXPORT QwtPlotItem: public QwtLegendItemManager +{ +public: + /*! + \brief Runtime type information + + RttiValues is used to cast plot items, without + having to enable runtime type information of the compiler. + */ + enum RttiValues + { + //! Unspecific value, that can be used, when it doesn't matter + Rtti_PlotItem = 0, + + //! For QwtPlotGrid + Rtti_PlotGrid, + + //! For QwtPlotScaleItem + Rtti_PlotScale, + + //! For QwtPlotMarker + Rtti_PlotMarker, + + //! For QwtPlotCurve + Rtti_PlotCurve, + + //! For QwtPlotSpectroCurve + Rtti_PlotSpectroCurve, + + //! For QwtPlotIntervalCurve + Rtti_PlotIntervalCurve, + + //! For QwtPlotHistogram + Rtti_PlotHistogram, + + //! For QwtPlotSpectrogram + Rtti_PlotSpectrogram, + + //! For QwtPlotSvgItem + Rtti_PlotSVG, + + /*! + Values >= Rtti_PlotUserItem are reserved for plot items + not implemented in the Qwt library. + */ + Rtti_PlotUserItem = 1000 + }; + + /*! + Plot Item Attributes + \sa setItemAttribute(), testItemAttribute() + */ + enum ItemAttribute + { + //! The item is represented on the legend. + Legend = 0x01, + + /*! + The boundingRect() of the item is included in the + autoscaling calculation. + */ + AutoScale = 0x02 + }; + + //! Plot Item Attributes + typedef QFlags<ItemAttribute> ItemAttributes; + + //! Render hints + enum RenderHint + { + //! Enable antialiasing + RenderAntialiased = 1 + }; + + //! Render hints + typedef QFlags<RenderHint> RenderHints; + + explicit QwtPlotItem( const QwtText &title = QwtText() ); + virtual ~QwtPlotItem(); + + void attach( QwtPlot *plot ); + void detach(); + + QwtPlot *plot() const; + + void setTitle( const QString &title ); + void setTitle( const QwtText &title ); + const QwtText &title() const; + + virtual int rtti() const; + + void setItemAttribute( ItemAttribute, bool on = true ); + bool testItemAttribute( ItemAttribute ) const; + + void setRenderHint( RenderHint, bool on = true ); + bool testRenderHint( RenderHint ) const; + + double z() const; + void setZ( double z ); + + void show(); + void hide(); + virtual void setVisible( bool ); + bool isVisible () const; + + void setAxes( int xAxis, int yAxis ); + + void setXAxis( int axis ); + int xAxis() const; + + void setYAxis( int axis ); + int yAxis() const; + + virtual void itemChanged(); + + /*! + \brief Draw the item + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas in painter coordinates + */ + virtual void draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const = 0; + + virtual QRectF boundingRect() const; + + virtual void updateLegend( QwtLegend * ) const; + virtual void updateScaleDiv( + const QwtScaleDiv&, const QwtScaleDiv& ); + + virtual QWidget *legendItem() const; + + QRectF scaleRect( const QwtScaleMap &, const QwtScaleMap & ) const; + QRectF paintRect( const QwtScaleMap &, const QwtScaleMap & ) const; + +private: + // Disabled copy constructor and operator= + QwtPlotItem( const QwtPlotItem & ); + QwtPlotItem &operator=( const QwtPlotItem & ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::ItemAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotItem::RenderHints ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_layout.cpp b/src/libpcp_qwt/src/qwt_plot_layout.cpp new file mode 100644 index 0000000..0e55a88 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_layout.cpp @@ -0,0 +1,1267 @@ +/* -*- 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_plot_layout.h" +#include "qwt_text.h" +#include "qwt_text_label.h" +#include "qwt_plot_canvas.h" +#include "qwt_scale_widget.h" +#include "qwt_legend.h" +#include <qscrollbar.h> +#include <qmath.h> + +class QwtPlotLayout::LayoutData +{ +public: + void init( const QwtPlot *, const QRectF &rect ); + + struct t_legendData + { + int frameWidth; + int vScrollBarWidth; + int hScrollBarHeight; + QSize hint; + } legend; + + struct t_titleData + { + QwtText text; + int frameWidth; + } title; + + struct t_scaleData + { + bool isEnabled; + const QwtScaleWidget *scaleWidget; + QFont scaleFont; + int start; + int end; + int baseLineOffset; + double tickOffset; + int dimWithoutTitle; + } scale[QwtPlot::axisCnt]; + + struct t_canvasData + { + int frameWidth; + } canvas; +}; + +/* + Extract all layout relevant data from the plot components +*/ + +void QwtPlotLayout::LayoutData::init( const QwtPlot *plot, const QRectF &rect ) +{ + // legend + + if ( plot->plotLayout()->legendPosition() != QwtPlot::ExternalLegend + && plot->legend() ) + { + legend.frameWidth = plot->legend()->frameWidth(); + legend.vScrollBarWidth = + plot->legend()->verticalScrollBar()->sizeHint().width(); + legend.hScrollBarHeight = + plot->legend()->horizontalScrollBar()->sizeHint().height(); + + const QSize hint = plot->legend()->sizeHint(); + + int w = qMin( hint.width(), qFloor( rect.width() ) ); + int h = plot->legend()->heightForWidth( w ); + if ( h == 0 ) + h = hint.height(); + + if ( h > rect.height() ) + w += legend.vScrollBarWidth; + + legend.hint = QSize( w, h ); + } + + // title + + title.frameWidth = 0; + title.text = QwtText(); + + if ( plot->titleLabel() ) + { + const QwtTextLabel *label = plot->titleLabel(); + title.text = label->text(); + if ( !( title.text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) ) + title.text.setFont( label->font() ); + + title.frameWidth = plot->titleLabel()->frameWidth(); + } + + // scales + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( plot->axisEnabled( axis ) ) + { + const QwtScaleWidget *scaleWidget = plot->axisWidget( axis ); + + scale[axis].isEnabled = true; + + scale[axis].scaleWidget = scaleWidget; + + scale[axis].scaleFont = scaleWidget->font(); + + scale[axis].start = scaleWidget->startBorderDist(); + scale[axis].end = scaleWidget->endBorderDist(); + + scale[axis].baseLineOffset = scaleWidget->margin(); + scale[axis].tickOffset = scaleWidget->margin(); + if ( scaleWidget->scaleDraw()->hasComponent( + QwtAbstractScaleDraw::Ticks ) ) + { + scale[axis].tickOffset += + scaleWidget->scaleDraw()->maxTickLength(); + } + + scale[axis].dimWithoutTitle = scaleWidget->dimForLength( + QWIDGETSIZE_MAX, scale[axis].scaleFont ); + + if ( !scaleWidget->title().isEmpty() ) + { + scale[axis].dimWithoutTitle -= + scaleWidget->titleHeightForWidth( QWIDGETSIZE_MAX ); + } + } + else + { + scale[axis].isEnabled = false; + scale[axis].start = 0; + scale[axis].end = 0; + scale[axis].baseLineOffset = 0; + scale[axis].tickOffset = 0.0; + scale[axis].dimWithoutTitle = 0; + } + } + + // canvas + + canvas.frameWidth = plot->canvas()->frameWidth(); +} + +class QwtPlotLayout::PrivateData +{ +public: + PrivateData(): + spacing( 5 ), + alignCanvasToScales( false ) + { + } + + QRectF titleRect; + QRectF legendRect; + QRectF scaleRect[QwtPlot::axisCnt]; + QRectF canvasRect; + + QwtPlotLayout::LayoutData layoutData; + + QwtPlot::LegendPosition legendPos; + double legendRatio; + unsigned int spacing; + unsigned int fixedOffset[QwtPlot::axisCnt]; + unsigned int canvasMargin[QwtPlot::axisCnt]; + bool alignCanvasToScales; +}; + +/*! + \brief Constructor + */ + +QwtPlotLayout::QwtPlotLayout() +{ + d_data = new PrivateData; + + setLegendPosition( QwtPlot::BottomLegend ); + setFixedAxisOffset(0); + setCanvasMargin( 4 ); + + invalidate(); +} + +//! Destructor +QwtPlotLayout::~QwtPlotLayout() +{ + delete d_data; +} + +/*! + Change a margin of the canvas. The margin is the space + above/below the scale ticks. A negative margin will + be set to -1, excluding the borders of the scales. + + \param margin New margin + \param axis One of QwtPlot::Axis. Specifies where the position of the margin. + -1 means margin at all borders. + \sa canvasMargin() + + \warning The margin will have no effect when alignCanvasToScales is true +*/ + +void QwtPlotLayout::setCanvasMargin( int margin, int axis ) +{ + if ( margin < -1 ) + margin = -1; + + if ( axis == -1 ) + { + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->canvasMargin[axis] = margin; + } + else if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->canvasMargin[axis] = margin; +} + +/*! + \return Margin around the scale tick borders + \sa setCanvasMargin() +*/ +int QwtPlotLayout::canvasMargin( int axis ) const +{ + if ( axis < 0 || axis >= QwtPlot::axisCnt ) + return 0; + + return d_data->canvasMargin[axis]; +} + +/*! + * Set a fixed offset for the given axis scale. The offset is the space + * between an outer edge of the plot widget and the scale backbone. + * + * \param offset New offset + * \param axis One of QwtPlot::Axis. Specifies which axis to make fixed offset. + * -1 means margin at all borders. + * \sa fixedAxisOffset() + * */ +void QwtPlotLayout::setFixedAxisOffset(int offset, int axis) +{ + if ( offset < 0 ) + offset = 0; + + if ( axis == -1 ) + { + for (axis = 0; axis < QwtPlot::axisCnt; axis++) + d_data->fixedOffset[axis] = offset; + } + else if ( axis >= 0 || axis < QwtPlot::axisCnt ) + d_data->fixedOffset[axis] = offset; +} + +/*! + * \return Fixed offset, if any, for a given axis scale + * \sa setFixedAxisOffset() + * */ +int QwtPlotLayout::fixedAxisOffset(int axis) const +{ + if ( axis < 0 || axis >= QwtPlot::axisCnt ) + return 0; + + return d_data->fixedOffset[axis]; +} + +/*! + Change the align-canvas-to-axis-scales setting. The canvas may: + - extend beyond the axis scale ends to maximize its size, + - align with the axis scale ends to control its size. + + \param alignCanvasToScales New align-canvas-to-axis-scales setting + + \sa setCanvasMargin() + \note In this context the term 'scale' means the backbone of a scale. + \warning In case of alignCanvasToScales == true canvasMargin will have + no effect +*/ +void QwtPlotLayout::setAlignCanvasToScales( bool alignCanvasToScales ) +{ + d_data->alignCanvasToScales = alignCanvasToScales; +} + +/*! + Return the align-canvas-to-axis-scales setting. The canvas may: + - extend beyond the axis scale ends to maximize its size + - align with the axis scale ends to control its size. + + \return align-canvas-to-axis-scales setting + \sa setAlignCanvasToScales, setCanvasMargin() + \note In this context the term 'scale' means the backbone of a scale. +*/ +bool QwtPlotLayout::alignCanvasToScales() const +{ + return d_data->alignCanvasToScales; +} + +/*! + Change the spacing of the plot. The spacing is the distance + between the plot components. + + \param spacing new spacing + \sa setMargin(), spacing() +*/ +void QwtPlotLayout::setSpacing( int spacing ) +{ + d_data->spacing = qMax( 0, spacing ); +} + +/*! + \return spacing + \sa margin(), setSpacing() +*/ +int QwtPlotLayout::spacing() const +{ + return d_data->spacing; +} + +/*! + \brief Specify the position of the legend + \param pos The legend's position. + \param ratio Ratio between legend and the bounding rect + of title, canvas and axes. The legend will be shrinked + if it would need more space than the given ratio. + The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 + it will be reset to the default ratio. + The default vertical/horizontal ratio is 0.33/0.5. + + \sa QwtPlot::setLegendPosition() +*/ + +void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos, double ratio ) +{ + if ( ratio > 1.0 ) + ratio = 1.0; + + switch ( pos ) + { + case QwtPlot::TopLegend: + case QwtPlot::BottomLegend: + if ( ratio <= 0.0 ) + ratio = 0.33; + d_data->legendRatio = ratio; + d_data->legendPos = pos; + break; + case QwtPlot::LeftLegend: + case QwtPlot::RightLegend: + if ( ratio <= 0.0 ) + ratio = 0.5; + d_data->legendRatio = ratio; + d_data->legendPos = pos; + break; + case QwtPlot::ExternalLegend: + d_data->legendRatio = ratio; // meaningless + d_data->legendPos = pos; + default: + break; + } +} + +/*! + \brief Specify the position of the legend + \param pos The legend's position. Valid values are + \c QwtPlot::LeftLegend, \c QwtPlot::RightLegend, + \c QwtPlot::TopLegend, \c QwtPlot::BottomLegend. + + \sa QwtPlot::setLegendPosition() +*/ +void QwtPlotLayout::setLegendPosition( QwtPlot::LegendPosition pos ) +{ + setLegendPosition( pos, 0.0 ); +} + +/*! + \return Position of the legend + \sa setLegendPosition(), QwtPlot::setLegendPosition(), + QwtPlot::legendPosition() +*/ +QwtPlot::LegendPosition QwtPlotLayout::legendPosition() const +{ + return d_data->legendPos; +} + +/*! + Specify the relative size of the legend in the plot + \param ratio Ratio between legend and the bounding rect + of title, canvas and axes. The legend will be shrinked + if it would need more space than the given ratio. + The ratio is limited to ]0.0 .. 1.0]. In case of <= 0.0 + it will be reset to the default ratio. + The default vertical/horizontal ratio is 0.33/0.5. +*/ +void QwtPlotLayout::setLegendRatio( double ratio ) +{ + setLegendPosition( legendPosition(), ratio ); +} + +/*! + \return The relative size of the legend in the plot. + \sa setLegendPosition() +*/ +double QwtPlotLayout::legendRatio() const +{ + return d_data->legendRatio; +} + +/*! + \return Geometry for the title + \sa activate(), invalidate() +*/ +const QRectF &QwtPlotLayout::titleRect() const +{ + return d_data->titleRect; +} + +/*! + \return Geometry for the legend + \sa activate(), invalidate() +*/ +const QRectF &QwtPlotLayout::legendRect() const +{ + return d_data->legendRect; +} + +/*! + \param axis Axis index + \return Geometry for the scale + \sa activate(), invalidate() +*/ +const QRectF &QwtPlotLayout::scaleRect( int axis ) const +{ + if ( axis < 0 || axis >= QwtPlot::axisCnt ) + { + static QRectF dummyRect; + return dummyRect; + } + return d_data->scaleRect[axis]; +} + +/*! + \return Geometry for the canvas + \sa activate(), invalidate() +*/ +const QRectF &QwtPlotLayout::canvasRect() const +{ + return d_data->canvasRect; +} + +/*! + Invalidate the geometry of all components. + \sa activate() +*/ +void QwtPlotLayout::invalidate() +{ + d_data->titleRect = d_data->legendRect = d_data->canvasRect = QRect(); + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + d_data->scaleRect[axis] = QRect(); +} + +/*! + \brief Return a minimum size hint + \sa QwtPlot::minimumSizeHint() +*/ + +QSize QwtPlotLayout::minimumSizeHint( const QwtPlot *plot ) const +{ + class ScaleData + { + public: + ScaleData() + { + w = h = minLeft = minRight = tickOffset = 0; + } + + int w; + int h; + int minLeft; + int minRight; + int tickOffset; + } scaleData[QwtPlot::axisCnt]; + + int canvasBorder[QwtPlot::axisCnt]; + + int axis; + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( plot->axisEnabled( axis ) ) + { + const QwtScaleWidget *scl = plot->axisWidget( axis ); + ScaleData &sd = scaleData[axis]; + + const QSize hint = scl->minimumSizeHint(); + sd.w = hint.width(); + sd.h = hint.height(); + scl->getBorderDistHint( sd.minLeft, sd.minRight ); + sd.tickOffset = scl->margin(); + if ( scl->scaleDraw()->hasComponent( QwtAbstractScaleDraw::Ticks ) ) + sd.tickOffset += qCeil( scl->scaleDraw()->maxTickLength() ); + } + + canvasBorder[axis] = plot->canvas()->frameWidth() + + d_data->canvasMargin[axis] + 1; + + } + + + for ( axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + ScaleData &sd = scaleData[axis]; + if ( sd.w && ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) ) + { + if ( ( sd.minLeft > canvasBorder[QwtPlot::yLeft] ) + && scaleData[QwtPlot::yLeft].w ) + { + int shiftLeft = sd.minLeft - canvasBorder[QwtPlot::yLeft]; + if ( shiftLeft > scaleData[QwtPlot::yLeft].w ) + shiftLeft = scaleData[QwtPlot::yLeft].w; + + sd.w -= shiftLeft; + } + if ( ( sd.minRight > canvasBorder[QwtPlot::yRight] ) + && scaleData[QwtPlot::yRight].w ) + { + int shiftRight = sd.minRight - canvasBorder[QwtPlot::yRight]; + if ( shiftRight > scaleData[QwtPlot::yRight].w ) + shiftRight = scaleData[QwtPlot::yRight].w; + + sd.w -= shiftRight; + } + } + + if ( sd.h && ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) ) + { + if ( ( sd.minLeft > canvasBorder[QwtPlot::xBottom] ) && + scaleData[QwtPlot::xBottom].h ) + { + int shiftBottom = sd.minLeft - canvasBorder[QwtPlot::xBottom]; + if ( shiftBottom > scaleData[QwtPlot::xBottom].tickOffset ) + shiftBottom = scaleData[QwtPlot::xBottom].tickOffset; + + sd.h -= shiftBottom; + } + if ( ( sd.minLeft > canvasBorder[QwtPlot::xTop] ) && + scaleData[QwtPlot::xTop].h ) + { + int shiftTop = sd.minRight - canvasBorder[QwtPlot::xTop]; + if ( shiftTop > scaleData[QwtPlot::xTop].tickOffset ) + shiftTop = scaleData[QwtPlot::xTop].tickOffset; + + sd.h -= shiftTop; + } + } + } + + const QwtPlotCanvas *canvas = plot->canvas(); + const QSize minCanvasSize = canvas->minimumSize(); + + int w = scaleData[QwtPlot::yLeft].w + scaleData[QwtPlot::yRight].w; + int cw = qMax( scaleData[QwtPlot::xBottom].w, scaleData[QwtPlot::xTop].w ) + + 2 * ( canvas->frameWidth() + 1 ); + w += qMax( cw, minCanvasSize.width() ); + + int h = scaleData[QwtPlot::xBottom].h + scaleData[QwtPlot::xTop].h; + int ch = qMax( scaleData[QwtPlot::yLeft].h, scaleData[QwtPlot::yRight].h ) + + 2 * ( canvas->frameWidth() + 1 ); + h += qMax( ch, minCanvasSize.height() ); + + const QwtTextLabel *title = plot->titleLabel(); + if ( title && !title->text().isEmpty() ) + { + // If only QwtPlot::yLeft or QwtPlot::yRight is showing, + // we center on the plot canvas. + const bool centerOnCanvas = !( plot->axisEnabled( QwtPlot::yLeft ) + && plot->axisEnabled( QwtPlot::yRight ) ); + + int titleW = w; + if ( centerOnCanvas ) + { + titleW -= scaleData[QwtPlot::yLeft].w + + scaleData[QwtPlot::yRight].w; + } + + int titleH = title->heightForWidth( titleW ); + if ( titleH > titleW ) // Compensate for a long title + { + w = titleW = titleH; + if ( centerOnCanvas ) + { + w += scaleData[QwtPlot::yLeft].w + + scaleData[QwtPlot::yRight].w; + } + + titleH = title->heightForWidth( titleW ); + } + h += titleH + d_data->spacing; + } + + // Compute the legend contribution + + const QwtLegend *legend = plot->legend(); + if ( d_data->legendPos != QwtPlot::ExternalLegend + && legend && !legend->isEmpty() ) + { + if ( d_data->legendPos == QwtPlot::LeftLegend + || d_data->legendPos == QwtPlot::RightLegend ) + { + int legendW = legend->sizeHint().width(); + int legendH = legend->heightForWidth( legendW ); + + if ( legend->frameWidth() > 0 ) + w += d_data->spacing; + + if ( legendH > h ) + legendW += legend->verticalScrollBar()->sizeHint().width(); + + if ( d_data->legendRatio < 1.0 ) + legendW = qMin( legendW, int( w / ( 1.0 - d_data->legendRatio ) ) ); + + w += legendW + d_data->spacing; + } + else // QwtPlot::Top, QwtPlot::Bottom + { + int legendW = qMin( legend->sizeHint().width(), w ); + int legendH = legend->heightForWidth( legendW ); + + if ( legend->frameWidth() > 0 ) + h += d_data->spacing; + + if ( d_data->legendRatio < 1.0 ) + legendH = qMin( legendH, int( h / ( 1.0 - d_data->legendRatio ) ) ); + + h += legendH + d_data->spacing; + } + } + + return QSize( w, h ); +} + +/*! + Find the geometry for the legend + \param options Options how to layout the legend + \param rect Rectangle where to place the legend + \return Geometry for the legend + \sa Options +*/ + +QRectF QwtPlotLayout::layoutLegend( Options options, + const QRectF &rect ) const +{ + const QSize hint( d_data->layoutData.legend.hint ); + + int dim; + if ( d_data->legendPos == QwtPlot::LeftLegend + || d_data->legendPos == QwtPlot::RightLegend ) + { + // We don't allow vertical legends to take more than + // half of the available space. + + dim = qMin( hint.width(), int( rect.width() * d_data->legendRatio ) ); + + if ( !( options & IgnoreScrollbars ) ) + { + if ( hint.height() > rect.height() ) + { + // The legend will need additional + // space for the vertical scrollbar. + + dim += d_data->layoutData.legend.vScrollBarWidth; + } + } + } + else + { + dim = qMin( hint.height(), int( rect.height() * d_data->legendRatio ) ); + dim = qMax( dim, d_data->layoutData.legend.hScrollBarHeight ); + } + + QRectF legendRect = rect; + switch ( d_data->legendPos ) + { + case QwtPlot::LeftLegend: + legendRect.setWidth( dim ); + break; + case QwtPlot::RightLegend: + legendRect.setX( rect.right() - dim ); + legendRect.setWidth( dim ); + break; + case QwtPlot::TopLegend: + legendRect.setHeight( dim ); + break; + case QwtPlot::BottomLegend: + legendRect.setY( rect.bottom() - dim ); + legendRect.setHeight( dim ); + break; + case QwtPlot::ExternalLegend: + break; + } + + return legendRect; +} + +/*! + Align the legend to the canvas + \param canvasRect Geometry of the canvas + \param legendRect Maximum geometry for the legend + \return Geometry for the aligned legend +*/ +QRectF QwtPlotLayout::alignLegend( const QRectF &canvasRect, + const QRectF &legendRect ) const +{ + QRectF alignedRect = legendRect; + + if ( d_data->legendPos == QwtPlot::BottomLegend + || d_data->legendPos == QwtPlot::TopLegend ) + { + if ( d_data->layoutData.legend.hint.width() < canvasRect.width() ) + { + alignedRect.setX( canvasRect.x() ); + alignedRect.setWidth( canvasRect.width() ); + } + } + else + { + if ( d_data->layoutData.legend.hint.height() < canvasRect.height() ) + { + alignedRect.setY( canvasRect.y() ); + alignedRect.setHeight( canvasRect.height() ); + } + } + + return alignedRect; +} + +/*! + Expand all line breaks in text labels, and calculate the height + of their widgets in orientation of the text. + + \param options Options how to layout the legend + \param rect Bounding rect for title, axes and canvas. + \param dimTitle Expanded height of the title widget + \param dimAxis Expanded heights of the axis in axis orientation. + + \sa Options +*/ +void QwtPlotLayout::expandLineBreaks( int options, const QRectF &rect, + int &dimTitle, int dimAxis[QwtPlot::axisCnt] ) const +{ + dimTitle = 0; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + dimAxis[axis] = 0; + + int backboneOffset[QwtPlot::axisCnt]; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + backboneOffset[axis] = 0; + if ( !d_data->alignCanvasToScales ) + backboneOffset[axis] += d_data->canvasMargin[axis]; + if ( !( options & IgnoreFrames ) ) + backboneOffset[axis] += d_data->layoutData.canvas.frameWidth; + } + + bool done = false; + while ( !done ) + { + done = true; + + // the size for the 4 axis depend on each other. Expanding + // the height of a horizontal axis will shrink the height + // for the vertical axis, shrinking the height of a vertical + // axis will result in a line break what will expand the + // width and results in shrinking the width of a horizontal + // axis what might result in a line break of a horizontal + // axis ... . So we loop as long until no size changes. + + if ( !d_data->layoutData.title.text.isEmpty() ) + { + double w = rect.width(); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled + != d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // center to the canvas + w -= dimAxis[QwtPlot::yLeft] + dimAxis[QwtPlot::yRight]; + } + + int d = qCeil( d_data->layoutData.title.text.heightForWidth( w ) ); + if ( !( options & IgnoreFrames ) ) + d += 2 * d_data->layoutData.title.frameWidth; + + if ( d > dimTitle ) + { + dimTitle = d; + done = false; + } + } + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + const struct LayoutData::t_scaleData &scaleData = + d_data->layoutData.scale[axis]; + + if ( scaleData.isEnabled ) + { + double length; + if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) + { + length = rect.width() - dimAxis[QwtPlot::yLeft] + - dimAxis[QwtPlot::yRight]; + length -= scaleData.start + scaleData.end; + + if ( dimAxis[QwtPlot::yRight] > 0 ) + length -= 1; + + length += qMin( dimAxis[QwtPlot::yLeft], + scaleData.start - backboneOffset[QwtPlot::yLeft] ); + length += qMin( dimAxis[QwtPlot::yRight], + scaleData.end - backboneOffset[QwtPlot::yRight] ); + } + else // QwtPlot::yLeft, QwtPlot::yRight + { + length = rect.height() - dimAxis[QwtPlot::xTop] + - dimAxis[QwtPlot::xBottom]; + length -= scaleData.start + scaleData.end; + length -= 1; + + if ( dimAxis[QwtPlot::xBottom] <= 0 ) + length -= 1; + if ( dimAxis[QwtPlot::xTop] <= 0 ) + length -= 1; + + if ( dimAxis[QwtPlot::xBottom] > 0 ) + { + length += qMin( + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset, + double( scaleData.start - backboneOffset[QwtPlot::xBottom] ) ); + } + if ( dimAxis[QwtPlot::xTop] > 0 ) + { + length += qMin( + d_data->layoutData.scale[QwtPlot::xTop].tickOffset, + double( scaleData.end - backboneOffset[QwtPlot::xTop] ) ); + } + + if ( dimTitle > 0 ) + length -= dimTitle + d_data->spacing; + } + + if (d_data->fixedOffset[axis]) + { + dimAxis[axis] = d_data->fixedOffset[axis] + + backboneOffset[QwtPlot::yLeft]; + continue; + } + + int d = scaleData.dimWithoutTitle; + if ( !scaleData.scaleWidget->title().isEmpty() ) + { + d += scaleData.scaleWidget->titleHeightForWidth( qFloor( length ) ); + } + + + if ( d > dimAxis[axis] ) + { + dimAxis[axis] = d; + done = false; + } + } + } + } +} + +/*! + Align the ticks of the axis to the canvas borders using + the empty corners. + + \sa Options +*/ + +void QwtPlotLayout::alignScales( int options, + QRectF &canvasRect, QRectF scaleRect[QwtPlot::axisCnt] ) const +{ + int backboneOffset[QwtPlot::axisCnt]; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + backboneOffset[axis] = 0; + if ( !d_data->alignCanvasToScales ) + backboneOffset[axis] += d_data->canvasMargin[axis]; + if ( !( options & IgnoreFrames ) ) + backboneOffset[axis] += d_data->layoutData.canvas.frameWidth; + } + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( !scaleRect[axis].isValid() ) + continue; + + const int startDist = d_data->layoutData.scale[axis].start; + const int endDist = d_data->layoutData.scale[axis].end; + + QRectF &axisRect = scaleRect[axis]; + + if ( axis == QwtPlot::xTop || axis == QwtPlot::xBottom ) + { + const QRectF &leftScaleRect = scaleRect[QwtPlot::yLeft]; + const int leftOffset = + backboneOffset[QwtPlot::yLeft] - startDist; + + if ( leftScaleRect.isValid() ) + { + const double dx = leftOffset + leftScaleRect.width(); + if ( d_data->alignCanvasToScales && dx < 0.0 ) + { + /* + The axis needs more space than the width + of the left scale. + */ + const double cLeft = canvasRect.left(); // qreal -> double + canvasRect.setLeft( qMax( cLeft, axisRect.left() - dx ) ); + } + else + { + const double minLeft = leftScaleRect.left(); + const double left = axisRect.left() + leftOffset; + axisRect.setLeft( qMax( left, minLeft ) ); + } + } + else + { + if ( d_data->alignCanvasToScales && leftOffset < 0 ) + { + canvasRect.setLeft( qMax( canvasRect.left(), + axisRect.left() - leftOffset ) ); + } + else + { + if ( leftOffset > 0 ) + axisRect.setLeft( axisRect.left() + leftOffset ); + } + } + + const QRectF &rightScaleRect = scaleRect[QwtPlot::yRight]; + const int rightOffset = + backboneOffset[QwtPlot::yRight] - endDist + 1; + + if ( rightScaleRect.isValid() ) + { + const double dx = rightOffset + rightScaleRect.width(); + if ( d_data->alignCanvasToScales && dx < 0 ) + { + /* + The axis needs more space than the width + of the right scale. + */ + const double cRight = canvasRect.right(); // qreal -> double + canvasRect.setRight( qMin( cRight, axisRect.right() + dx ) ); + } + + const double maxRight = rightScaleRect.right(); + const double right = axisRect.right() - rightOffset; + axisRect.setRight( qMin( right, maxRight ) ); + } + else + { + if ( d_data->alignCanvasToScales && rightOffset < 0 ) + { + canvasRect.setRight( qMin( canvasRect.right(), + axisRect.right() + rightOffset ) ); + } + else + { + if ( rightOffset > 0 ) + axisRect.setRight( axisRect.right() - rightOffset ); + } + } + } + else // QwtPlot::yLeft, QwtPlot::yRight + { + const QRectF &bottomScaleRect = scaleRect[QwtPlot::xBottom]; + const int bottomOffset = + backboneOffset[QwtPlot::xBottom] - endDist + 1; + + if ( bottomScaleRect.isValid() ) + { + const double dy = bottomOffset + bottomScaleRect.height(); + if ( d_data->alignCanvasToScales && dy < 0 ) + { + /* + The axis needs more space than the height + of the bottom scale. + */ + const double cBottom = canvasRect.bottom(); // qreal -> double + canvasRect.setBottom( qMin( cBottom, axisRect.bottom() + dy ) ); + } + else + { + const double maxBottom = bottomScaleRect.top() + + d_data->layoutData.scale[QwtPlot::xBottom].tickOffset; + const double bottom = axisRect.bottom() - bottomOffset; + axisRect.setBottom( qMin( bottom, maxBottom ) ); + } + } + else + { + if ( d_data->alignCanvasToScales && bottomOffset < 0 ) + { + canvasRect.setBottom( qMin( canvasRect.bottom(), + axisRect.bottom() + bottomOffset ) ); + } + else + { + if ( bottomOffset > 0 ) + axisRect.setBottom( axisRect.bottom() - bottomOffset ); + } + } + + const QRectF &topScaleRect = scaleRect[QwtPlot::xTop]; + const int topOffset = backboneOffset[QwtPlot::xTop] - startDist; + + if ( topScaleRect.isValid() ) + { + const double dy = topOffset + topScaleRect.height(); + if ( d_data->alignCanvasToScales && dy < 0 ) + { + /* + The axis needs more space than the height + of the top scale. + */ + const double cTop = canvasRect.top(); // qreal -> double + canvasRect.setTop( qMax( cTop, axisRect.top() - dy ) ); + } + else + { + const double minTop = topScaleRect.bottom() - + d_data->layoutData.scale[QwtPlot::xTop].tickOffset; + const double top = axisRect.top() + topOffset; + axisRect.setTop( qMax( top, minTop ) ); + } + } + else + { + if ( d_data->alignCanvasToScales && topOffset < 0 ) + { + canvasRect.setTop( qMax( canvasRect.top(), + axisRect.top() - topOffset ) ); + } + else + { + if ( topOffset > 0 ) + axisRect.setTop( axisRect.top() + topOffset ); + } + } + } + } + + if ( d_data->alignCanvasToScales ) + { + /* + The canvas has been aligned to the scale with largest + border distances. Now we have to realign the other scale. + */ + + int fw = 0; + if ( !( options & IgnoreFrames ) ) + fw = d_data->layoutData.canvas.frameWidth; + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( !scaleRect[axis].isValid() ) + continue; + + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { + scaleRect[axis].setLeft( canvasRect.left() + fw + - d_data->layoutData.scale[axis].start ); + scaleRect[axis].setRight( canvasRect.right() - fw - 1 + + d_data->layoutData.scale[axis].end ); + } + else + { + scaleRect[axis].setTop( canvasRect.top() + fw + - d_data->layoutData.scale[axis].start ); + scaleRect[axis].setBottom( canvasRect.bottom() - fw - 1 + + d_data->layoutData.scale[axis].end ); + } + } + + if ( scaleRect[QwtPlot::xTop].isValid() ) + scaleRect[QwtPlot::xTop].setBottom( canvasRect.top() ); + if ( scaleRect[QwtPlot::xBottom].isValid() ) + scaleRect[QwtPlot::xBottom].setTop( canvasRect.bottom() ); + if ( scaleRect[QwtPlot::yLeft].isValid() ) + scaleRect[QwtPlot::yLeft].setRight( canvasRect.left() ); + if ( scaleRect[QwtPlot::yRight].isValid() ) + scaleRect[QwtPlot::yRight].setLeft( canvasRect.right() ); + } +} + +/*! + \brief Recalculate the geometry of all components. + + \param plot Plot to be layout + \param plotRect Rect where to place the components + \param options Layout options + + \sa invalidate(), titleRect(), + legendRect(), scaleRect(), canvasRect() +*/ +void QwtPlotLayout::activate( const QwtPlot *plot, + const QRectF &plotRect, Options options ) +{ + invalidate(); + + QRectF rect( plotRect ); // undistributed rest of the plot rect + + // We extract all layout relevant data from the widgets, + // filter them through pfilter and save them to d_data->layoutData. + + d_data->layoutData.init( plot, rect ); + + if ( !( options & IgnoreLegend ) + && d_data->legendPos != QwtPlot::ExternalLegend + && plot->legend() && !plot->legend()->isEmpty() ) + { + d_data->legendRect = layoutLegend( options, rect ); + + // subtract d_data->legendRect from rect + + const QRegion region( rect.toRect() ); + rect = region.subtract( d_data->legendRect.toRect() ).boundingRect(); + + switch ( d_data->legendPos ) + { + case QwtPlot::LeftLegend: + rect.setLeft( rect.left() + d_data->spacing ); + break; + case QwtPlot::RightLegend: + rect.setRight( rect.right() - d_data->spacing ); + break; + case QwtPlot::TopLegend: + rect.setTop( rect.top() + d_data->spacing ); + break; + case QwtPlot::BottomLegend: + rect.setBottom( rect.bottom() - d_data->spacing ); + break; + case QwtPlot::ExternalLegend: + break; // suppress compiler warning + } + } + + /* + +---+-----------+---+ + | Title | + +---+-----------+---+ + | | Axis | | + +---+-----------+---+ + | A | | A | + | x | Canvas | x | + | i | | i | + | s | | s | + +---+-----------+---+ + | | Axis | | + +---+-----------+---+ + */ + + // axes and title include text labels. The height of each + // label depends on its line breaks, that depend on the width + // for the label. A line break in a horizontal text will reduce + // the available width for vertical texts and vice versa. + // expandLineBreaks finds the height/width for title and axes + // including all line breaks. + + int dimTitle, dimAxes[QwtPlot::axisCnt]; + expandLineBreaks( options, rect, dimTitle, dimAxes ); + + if ( dimTitle > 0 ) + { + d_data->titleRect.setRect( + rect.left(), rect.top(), rect.width(), dimTitle ); + + if ( d_data->layoutData.scale[QwtPlot::yLeft].isEnabled != + d_data->layoutData.scale[QwtPlot::yRight].isEnabled ) + { + // if only one of the y axes is missing we align + // the title centered to the canvas + + d_data->titleRect.setX( rect.left() + dimAxes[QwtPlot::yLeft] ); + d_data->titleRect.setWidth( rect.width() + - dimAxes[QwtPlot::yLeft] - dimAxes[QwtPlot::yRight] ); + } + + // subtract title + rect.setTop( rect.top() + dimTitle + d_data->spacing ); + } + + d_data->canvasRect.setRect( + rect.x() + dimAxes[QwtPlot::yLeft], + rect.y() + dimAxes[QwtPlot::xTop], + rect.width() - dimAxes[QwtPlot::yRight] - dimAxes[QwtPlot::yLeft], + rect.height() - dimAxes[QwtPlot::xBottom] - dimAxes[QwtPlot::xTop] ); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + // set the rects for the axes + + if ( dimAxes[axis] ) + { + int dim = dimAxes[axis]; + QRectF &scaleRect = d_data->scaleRect[axis]; + + scaleRect = d_data->canvasRect; + switch ( axis ) + { + case QwtPlot::yLeft: + scaleRect.setX( d_data->canvasRect.left() - dim ); + scaleRect.setWidth( dim ); + break; + case QwtPlot::yRight: + scaleRect.setX( d_data->canvasRect.right() ); + scaleRect.setWidth( dim ); + break; + case QwtPlot::xBottom: + scaleRect.setY( d_data->canvasRect.bottom() ); + scaleRect.setHeight( dim ); + break; + case QwtPlot::xTop: + scaleRect.setY( d_data->canvasRect.top() - dim ); + scaleRect.setHeight( dim ); + break; + } + scaleRect = scaleRect.normalized(); + } + } + + // +---+-----------+---+ + // | <- Axis -> | + // +-^-+-----------+-^-+ + // | | | | | | + // | | | | + // | A | | A | + // | x | Canvas | x | + // | i | | i | + // | s | | s | + // | | | | + // | | | | | | + // +-V-+-----------+-V-+ + // | <- Axis -> | + // +---+-----------+---+ + + // The ticks of the axes - not the labels above - should + // be aligned to the canvas. So we try to use the empty + // corners to extend the axes, so that the label texts + // left/right of the min/max ticks are moved into them. + + alignScales( options, d_data->canvasRect, d_data->scaleRect ); + + if ( !d_data->legendRect.isEmpty() ) + { + // We prefer to align the legend to the canvas - not to + // the complete plot - if possible. + + d_data->legendRect = alignLegend( d_data->canvasRect, d_data->legendRect ); + } +} diff --git a/src/libpcp_qwt/src/qwt_plot_layout.h b/src/libpcp_qwt/src/qwt_plot_layout.h new file mode 100644 index 0000000..2d9348d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_layout.h @@ -0,0 +1,108 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_LAYOUT_H +#define QWT_PLOT_LAYOUT_H + +#include "qwt_global.h" +#include "qwt_plot.h" + +/*! + \brief Layout engine for QwtPlot. + + It is used by the QwtPlot widget to organize its internal widgets + or by QwtPlot::print() to render its content to a QPaintDevice like + a QPrinter, QPixmap/QImage or QSvgRenderer. +*/ + +class QWT_EXPORT QwtPlotLayout +{ +public: + /*! + Options to configure the plot layout engine + \sa activate(), QwtPlotRenderer + */ + enum Option + { + //! Unused + AlignScales = 0x01, + + /*! + Ignore the dimension of the scrollbars. There are no + scrollbars, when the plot is not rendered to widgets. + */ + IgnoreScrollbars = 0x02, + + //! Ignore all frames. + IgnoreFrames = 0x04, + + //! Ignore the legend. + IgnoreLegend = 0x08 + }; + + //! Layout options + typedef QFlags<Option> Options; + + explicit QwtPlotLayout(); + virtual ~QwtPlotLayout(); + + void setCanvasMargin( int margin, int axis = -1 ); + int canvasMargin( int axis ) const; + + void setFixedAxisOffset(int offset, int axis = -1); + int fixedAxisOffset(int axis) const; + + void setAlignCanvasToScales( bool ); + bool alignCanvasToScales() const; + + void setSpacing( int ); + int spacing() const; + + void setLegendPosition( QwtPlot::LegendPosition pos, double ratio ); + void setLegendPosition( QwtPlot::LegendPosition pos ); + QwtPlot::LegendPosition legendPosition() const; + + void setLegendRatio( double ratio ); + double legendRatio() const; + + virtual QSize minimumSizeHint( const QwtPlot * ) const; + + virtual void activate( const QwtPlot *, + const QRectF &rect, Options options = 0x00 ); + + virtual void invalidate(); + + const QRectF &titleRect() const; + const QRectF &legendRect() const; + const QRectF &scaleRect( int axis ) const; + const QRectF &canvasRect() const; + + class LayoutData; + +protected: + + QRectF layoutLegend( Options options, const QRectF & ) const; + QRectF alignLegend( const QRectF &canvasRect, + const QRectF &legendRect ) const; + + void expandLineBreaks( int options, const QRectF &rect, + int &dimTitle, int dimAxes[QwtPlot::axisCnt] ) const; + + void alignScales( int options, QRectF &canvasRect, + QRectF scaleRect[QwtPlot::axisCnt] ) const; + +private: + class PrivateData; + + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotLayout::Options ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_magnifier.cpp b/src/libpcp_qwt/src/qwt_plot_magnifier.cpp new file mode 100644 index 0000000..b490d80 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_magnifier.cpp @@ -0,0 +1,143 @@ +/* -*- 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_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_scale_div.h" +#include "qwt_plot_magnifier.h" +#include <qevent.h> + +class QwtPlotMagnifier::PrivateData +{ +public: + PrivateData() + { + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + isAxisEnabled[axis] = true; + } + + bool isAxisEnabled[QwtPlot::axisCnt]; +}; + +/*! + Constructor + \param canvas Plot canvas to be magnified +*/ +QwtPlotMagnifier::QwtPlotMagnifier( QwtPlotCanvas *canvas ): + QwtMagnifier( canvas ) +{ + d_data = new PrivateData(); +} + +//! Destructor +QwtPlotMagnifier::~QwtPlotMagnifier() +{ + delete d_data; +} + +/*! + \brief En/Disable an axis + + Only Axes that are enabled will be zoomed. + All other axes will remain unchanged. + + \param axis Axis, see QwtPlot::Axis + \param on On/Off + + \sa isAxisEnabled() +*/ +void QwtPlotMagnifier::setAxisEnabled( int axis, bool on ) +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->isAxisEnabled[axis] = on; +} + +/*! + Test if an axis is enabled + + \param axis Axis, see QwtPlot::Axis + \return True, if the axis is enabled + + \sa setAxisEnabled() +*/ +bool QwtPlotMagnifier::isAxisEnabled( int axis ) const +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + return d_data->isAxisEnabled[axis]; + + return true; +} + +//! Return observed plot canvas +QwtPlotCanvas *QwtPlotMagnifier::canvas() +{ + return qobject_cast<QwtPlotCanvas *>( parent() ); +} + +//! Return Observed plot canvas +const QwtPlotCanvas *QwtPlotMagnifier::canvas() const +{ + return qobject_cast<const QwtPlotCanvas *>( parent() ); +} + +//! Return plot widget, containing the observed plot canvas +QwtPlot *QwtPlotMagnifier::plot() +{ + QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +//! Return plot widget, containing the observed plot canvas +const QwtPlot *QwtPlotMagnifier::plot() const +{ + const QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +/*! + Zoom in/out the axes scales + \param factor A value < 1.0 zooms in, a value > 1.0 zooms out. +*/ +void QwtPlotMagnifier::rescale( double factor ) +{ + factor = qAbs( factor ); + if ( factor == 1.0 || factor == 0.0 ) + return; + + bool doReplot = false; + QwtPlot* plt = plot(); + + const bool autoReplot = plt->autoReplot(); + plt->setAutoReplot( false ); + + for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) + { + const QwtScaleDiv *scaleDiv = plt->axisScaleDiv( axisId ); + if ( isAxisEnabled( axisId ) && scaleDiv->isValid() ) + { + const double center = + scaleDiv->lowerBound() + scaleDiv->range() / 2; + const double width_2 = scaleDiv->range() / 2 * factor; + + plt->setAxisScale( axisId, center - width_2, center + width_2 ); + doReplot = true; + } + } + + plt->setAutoReplot( autoReplot ); + + if ( doReplot ) + plt->replot(); +} diff --git a/src/libpcp_qwt/src/qwt_plot_magnifier.h b/src/libpcp_qwt/src/qwt_plot_magnifier.h new file mode 100644 index 0000000..e7369c7 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_magnifier.h @@ -0,0 +1,55 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_MAGNIFIER_H +#define QWT_PLOT_MAGNIFIER_H 1 + +#include "qwt_global.h" +#include "qwt_magnifier.h" + +class QwtPlotCanvas; +class QwtPlot; + +/*! + \brief QwtPlotMagnifier provides zooming, by magnifying in steps. + + Using QwtPlotMagnifier a plot can be zoomed in/out in steps using + keys, the mouse wheel or moving a mouse button in vertical direction. + + Together with QwtPlotZoomer and QwtPlotPanner it is possible to implement + individual and powerful navigation of the plot canvas. + + \sa QwtPlotZoomer, QwtPlotPanner, QwtPlot +*/ +class QWT_EXPORT QwtPlotMagnifier: public QwtMagnifier +{ + Q_OBJECT + +public: + explicit QwtPlotMagnifier( QwtPlotCanvas * ); + virtual ~QwtPlotMagnifier(); + + void setAxisEnabled( int axis, bool on ); + bool isAxisEnabled( int axis ) const; + + QwtPlotCanvas *canvas(); + const QwtPlotCanvas *canvas() const; + + QwtPlot *plot(); + const QwtPlot *plot() const; + +protected: + virtual void rescale( double factor ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_marker.cpp b/src/libpcp_qwt/src/qwt_plot_marker.cpp new file mode 100644 index 0000000..900f145 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_marker.cpp @@ -0,0 +1,608 @@ +/* -*- 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_plot_marker.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include "qwt_symbol.h" +#include "qwt_text.h" +#include "qwt_math.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include <qpainter.h> + +class QwtPlotMarker::PrivateData +{ +public: + PrivateData(): + labelAlignment( Qt::AlignCenter ), + labelOrientation( Qt::Horizontal ), + spacing( 2 ), + symbol( NULL ), + style( QwtPlotMarker::NoLine ), + xValue( 0.0 ), + yValue( 0.0 ) + { + } + + ~PrivateData() + { + delete symbol; + } + + QwtText label; + Qt::Alignment labelAlignment; + Qt::Orientation labelOrientation; + int spacing; + + QPen pen; + const QwtSymbol *symbol; + LineStyle style; + + double xValue; + double yValue; +}; + +//! Sets alignment to Qt::AlignCenter, and style to QwtPlotMarker::NoLine +QwtPlotMarker::QwtPlotMarker(): + QwtPlotItem( QwtText( "Marker" ) ) +{ + d_data = new PrivateData; + setZ( 30.0 ); +} + +//! Destructor +QwtPlotMarker::~QwtPlotMarker() +{ + delete d_data; +} + +//! \return QwtPlotItem::Rtti_PlotMarker +int QwtPlotMarker::rtti() const +{ + return QwtPlotItem::Rtti_PlotMarker; +} + +//! Return Value +QPointF QwtPlotMarker::value() const +{ + return QPointF( d_data->xValue, d_data->yValue ); +} + +//! Return x Value +double QwtPlotMarker::xValue() const +{ + return d_data->xValue; +} + +//! Return y Value +double QwtPlotMarker::yValue() const +{ + return d_data->yValue; +} + +//! Set Value +void QwtPlotMarker::setValue( const QPointF& pos ) +{ + setValue( pos.x(), pos.y() ); +} + +//! Set Value +void QwtPlotMarker::setValue( double x, double y ) +{ + if ( x != d_data->xValue || y != d_data->yValue ) + { + d_data->xValue = x; + d_data->yValue = y; + itemChanged(); + } +} + +//! Set X Value +void QwtPlotMarker::setXValue( double x ) +{ + setValue( x, d_data->yValue ); +} + +//! Set Y Value +void QwtPlotMarker::setYValue( double y ) +{ + setValue( d_data->xValue, y ); +} + +/*! + Draw the marker + + \param painter Painter + \param xMap x Scale Map + \param yMap y Scale Map + \param canvasRect Contents rect of the canvas in painter coordinates +*/ +void QwtPlotMarker::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + const QPointF pos( xMap.transform( d_data->xValue ), + yMap.transform( d_data->yValue ) ); + + // draw lines + + drawLines( painter, canvasRect, pos ); + + // draw symbol + if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + const QSizeF sz = d_data->symbol->size(); + + const QRectF clipRect = canvasRect.adjusted( + -sz.width(), -sz.height(), sz.width(), sz.height() ); + + if ( clipRect.contains( pos ) ) + d_data->symbol->drawSymbol( painter, pos ); + } + + drawLabel( painter, canvasRect, pos ); +} + +/*! + Draw the lines marker + + \param painter Painter + \param canvasRect Contents rect of the canvas in painter coordinates + \param pos Position of the marker, translated into widget coordinates + + \sa drawLabel(), QwtSymbol::drawSymbol() +*/ +void QwtPlotMarker::drawLines( QPainter *painter, + const QRectF &canvasRect, const QPointF &pos ) const +{ + if ( d_data->style == NoLine ) + return; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + painter->setPen( d_data->pen ); + if ( d_data->style == QwtPlotMarker::HLine || + d_data->style == QwtPlotMarker::Cross ) + { + double y = pos.y(); + if ( doAlign ) + y = qRound( y ); + + QwtPainter::drawLine( painter, canvasRect.left(), + y, canvasRect.right() - 1.0, y ); + } + if ( d_data->style == QwtPlotMarker::VLine || + d_data->style == QwtPlotMarker::Cross ) + { + double x = pos.x(); + if ( doAlign ) + x = qRound( x ); + + QwtPainter::drawLine( painter, x, + canvasRect.top(), x, canvasRect.bottom() - 1.0 ); + } +} + +/*! + Align and draw the text label of the marker + + \param painter Painter + \param canvasRect Contents rect of the canvas in painter coordinates + \param pos Position of the marker, translated into widget coordinates + + \sa drawLabel(), QwtSymbol::drawSymbol() +*/ +void QwtPlotMarker::drawLabel( QPainter *painter, + const QRectF &canvasRect, const QPointF &pos ) const +{ + if ( d_data->label.isEmpty() ) + return; + + Qt::Alignment align = d_data->labelAlignment; + QPointF alignPos = pos; + + QSizeF symbolOff( 0, 0 ); + + switch ( d_data->style ) + { + case QwtPlotMarker::VLine: + { + // In VLine-style the y-position is pointless and + // the alignment flags are relative to the canvas + + if ( d_data->labelAlignment & Qt::AlignTop ) + { + alignPos.setY( canvasRect.top() ); + align &= ~Qt::AlignTop; + align |= Qt::AlignBottom; + } + else if ( d_data->labelAlignment & Qt::AlignBottom ) + { + // In HLine-style the x-position is pointless and + // the alignment flags are relative to the canvas + + alignPos.setY( canvasRect.bottom() - 1 ); + align &= ~Qt::AlignBottom; + align |= Qt::AlignTop; + } + else + { + alignPos.setY( canvasRect.center().y() ); + } + break; + } + case QwtPlotMarker::HLine: + { + if ( d_data->labelAlignment & Qt::AlignLeft ) + { + alignPos.setX( canvasRect.left() ); + align &= ~Qt::AlignLeft; + align |= Qt::AlignRight; + } + else if ( d_data->labelAlignment & Qt::AlignRight ) + { + alignPos.setX( canvasRect.right() - 1 ); + align &= ~Qt::AlignRight; + align |= Qt::AlignLeft; + } + else + { + alignPos.setX( canvasRect.center().x() ); + } + break; + } + default: + { + if ( d_data->symbol && + ( d_data->symbol->style() != QwtSymbol::NoSymbol ) ) + { + symbolOff = d_data->symbol->size() + QSizeF( 1, 1 ); + symbolOff /= 2; + } + } + } + + qreal pw2 = d_data->pen.widthF() / 2.0; + if ( pw2 == 0.0 ) + pw2 = 0.5; + + const int spacing = d_data->spacing; + + const qreal xOff = qMax( pw2, symbolOff.width() ); + const qreal yOff = qMax( pw2, symbolOff.height() ); + + const QSizeF textSize = d_data->label.textSize( painter->font() ); + + if ( align & Qt::AlignLeft ) + { + alignPos.rx() -= xOff + spacing; + if ( d_data->labelOrientation == Qt::Vertical ) + alignPos.rx() -= textSize.height(); + else + alignPos.rx() -= textSize.width(); + } + else if ( align & Qt::AlignRight ) + { + alignPos.rx() += xOff + spacing; + } + else + { + if ( d_data->labelOrientation == Qt::Vertical ) + alignPos.rx() -= textSize.height() / 2; + else + alignPos.rx() -= textSize.width() / 2; + } + + if ( align & Qt::AlignTop ) + { + alignPos.ry() -= yOff + spacing; + if ( d_data->labelOrientation != Qt::Vertical ) + alignPos.ry() -= textSize.height(); + } + else if ( align & Qt::AlignBottom ) + { + alignPos.ry() += yOff + spacing; + if ( d_data->labelOrientation == Qt::Vertical ) + alignPos.ry() += textSize.width(); + } + else + { + if ( d_data->labelOrientation == Qt::Vertical ) + alignPos.ry() += textSize.width() / 2; + else + alignPos.ry() -= textSize.height() / 2; + } + + painter->translate( alignPos.x(), alignPos.y() ); + if ( d_data->labelOrientation == Qt::Vertical ) + painter->rotate( -90.0 ); + + const QRectF textRect( 0, 0, textSize.width(), textSize.height() ); + d_data->label.draw( painter, textRect ); +} + +/*! + \brief Set the line style + \param style Line style. + \sa lineStyle() +*/ +void QwtPlotMarker::setLineStyle( LineStyle style ) +{ + if ( style != d_data->style ) + { + d_data->style = style; + itemChanged(); + } +} + +/*! + \return the line style + \sa setLineStyle() +*/ +QwtPlotMarker::LineStyle QwtPlotMarker::lineStyle() const +{ + return d_data->style; +} + +/*! + \brief Assign a symbol + \param symbol New symbol + \sa symbol() +*/ +void QwtPlotMarker::setSymbol( const QwtSymbol *symbol ) +{ + if ( symbol != d_data->symbol ) + { + delete d_data->symbol; + d_data->symbol = symbol; + itemChanged(); + } +} + +/*! + \return the symbol + \sa setSymbol(), QwtSymbol +*/ +const QwtSymbol *QwtPlotMarker::symbol() const +{ + return d_data->symbol; +} + +/*! + \brief Set the label + \param label label text + \sa label() +*/ +void QwtPlotMarker::setLabel( const QwtText& label ) +{ + if ( label != d_data->label ) + { + d_data->label = label; + itemChanged(); + } +} + +/*! + \return the label + \sa setLabel() +*/ +QwtText QwtPlotMarker::label() const +{ + return d_data->label; +} + +/*! + \brief Set the alignment of the label + + In case of QwtPlotMarker::HLine the alignment is relative to the + y position of the marker, but the horizontal flags correspond to the + canvas rectangle. In case of QwtPlotMarker::VLine the alignment is + relative to the x position of the marker, but the vertical flags + correspond to the canvas rectangle. + + In all other styles the alignment is relative to the marker's position. + + \param align Alignment. + \sa labelAlignment(), labelOrientation() +*/ +void QwtPlotMarker::setLabelAlignment( Qt::Alignment align ) +{ + if ( align != d_data->labelAlignment ) + { + d_data->labelAlignment = align; + itemChanged(); + } +} + +/*! + \return the label alignment + \sa setLabelAlignment(), setLabelOrientation() +*/ +Qt::Alignment QwtPlotMarker::labelAlignment() const +{ + return d_data->labelAlignment; +} + +/*! + \brief Set the orientation of the label + + When orientation is Qt::Vertical the label is rotated by 90.0 degrees + ( from bottom to top ). + + \param orientation Orientation of the label + + \sa labelOrientation(), setLabelAlignment() +*/ +void QwtPlotMarker::setLabelOrientation( Qt::Orientation orientation ) +{ + if ( orientation != d_data->labelOrientation ) + { + d_data->labelOrientation = orientation; + itemChanged(); + } +} + +/*! + \return the label orientation + \sa setLabelOrientation(), labelAlignment() +*/ +Qt::Orientation QwtPlotMarker::labelOrientation() const +{ + return d_data->labelOrientation; +} + +/*! + \brief Set the spacing + + When the label is not centered on the marker position, the spacing + is the distance between the position and the label. + + \param spacing Spacing + \sa spacing(), setLabelAlignment() +*/ +void QwtPlotMarker::setSpacing( int spacing ) +{ + if ( spacing < 0 ) + spacing = 0; + + if ( spacing == d_data->spacing ) + return; + + d_data->spacing = spacing; + itemChanged(); +} + +/*! + \return the spacing + \sa setSpacing() +*/ +int QwtPlotMarker::spacing() const +{ + return d_data->spacing; +} + +/*! + Specify a pen for the line. + + \param pen New pen + \sa linePen() +*/ +void QwtPlotMarker::setLinePen( const QPen &pen ) +{ + if ( pen != d_data->pen ) + { + d_data->pen = pen; + itemChanged(); + } +} + +/*! + \return the line pen + \sa setLinePen() +*/ +const QPen &QwtPlotMarker::linePen() const +{ + return d_data->pen; +} + +QRectF QwtPlotMarker::boundingRect() const +{ + return QRectF( d_data->xValue, d_data->yValue, 0.0, 0.0 ); +} + +/*! + \brief Update the widget that represents the item on the legend + + \param legend Legend + \sa drawLegendIdentifier(), legendItem(), itemChanged(), QwtLegend() + + \note In the default setting QwtPlotItem::Legend is disabled +*/ +void QwtPlotMarker::updateLegend( QwtLegend *legend ) const +{ + if ( legend && testItemAttribute( QwtPlotItem::Legend ) + && d_data->symbol && d_data->symbol->style() != QwtSymbol::NoSymbol ) + { + QWidget *lgdItem = legend->find( this ); + if ( lgdItem == NULL ) + { + lgdItem = legendItem(); + if ( lgdItem ) + legend->insert( this, lgdItem ); + } + + QwtLegendItem *l = qobject_cast<QwtLegendItem *>( lgdItem ); + if ( l ) + l->setIdentifierSize( d_data->symbol->boundingSize() ); + } + + QwtPlotItem::updateLegend( legend ); +} + +/*! + \brief Draw the identifier representing the marker on the legend + + \param painter Painter + \param rect Bounding rectangle for the identifier + + \sa updateLegend(), QwtPlotItem::Legend +*/ +void QwtPlotMarker::drawLegendIdentifier( + QPainter *painter, const QRectF &rect ) const +{ + if ( rect.isEmpty() ) + return; + + painter->save(); + painter->setClipRect( rect, Qt::IntersectClip ); + + if ( d_data->style != QwtPlotMarker::NoLine ) + { + painter->setPen( d_data->pen ); + + if ( d_data->style == QwtPlotMarker::HLine || + d_data->style == QwtPlotMarker::Cross ) + { + QwtPainter::drawLine( painter, rect.left(), rect.center().y(), + rect.right(), rect.center().y() ); + } + + if ( d_data->style == QwtPlotMarker::VLine || + d_data->style == QwtPlotMarker::Cross ) + { + QwtPainter::drawLine( painter, rect.center().x(), rect.top(), + rect.center().x(), rect.bottom() ); + } + } + + if ( d_data->symbol && d_data->symbol->style() != QwtSymbol::NoSymbol ) + { + QSize symbolSize = d_data->symbol->boundingSize(); + symbolSize -= QSize( 2, 2 ); + + // scale the symbol size down if it doesn't fit into rect. + + double xRatio = 1.0; + if ( rect.width() < symbolSize.width() ) + xRatio = rect.width() / symbolSize.width(); + double yRatio = 1.0; + if ( rect.height() < symbolSize.height() ) + yRatio = rect.height() / symbolSize.height(); + + const double ratio = qMin( xRatio, yRatio ); + + painter->scale( ratio, ratio ); + d_data->symbol->drawSymbol( painter, rect.center() / ratio ); + } + + painter->restore(); +} + diff --git a/src/libpcp_qwt/src/qwt_plot_marker.h b/src/libpcp_qwt/src/qwt_plot_marker.h new file mode 100644 index 0000000..63a0d1d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_marker.h @@ -0,0 +1,124 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_MARKER_H +#define QWT_PLOT_MARKER_H + +#include <qpen.h> +#include <qfont.h> +#include <qstring.h> +#include <qbrush.h> +#include "qwt_global.h" +#include "qwt_plot_item.h" + +class QRectF; +class QwtText; +class QwtSymbol; + +/*! + \brief A class for drawing markers + + A marker can be a horizontal line, a vertical line, + a symbol, a label or any combination of them, which can + be drawn around a center point inside a bounding rectangle. + + The QwtPlotMarker::setSymbol() member assigns a symbol to the marker. + The symbol is drawn at the specified point. + + With setLabel(), a label can be assigned to the marker. + The setLabelAlignment() member specifies where the label is + drawn. All the Align*-constants in Qt::AlignmentFlags (see Qt documentation) + are valid. The interpretation of the alignment depends on the marker's + line style. The alignment refers to the center point of + the marker, which means, for example, that the label would be printed + left above the center point if the alignment was set to + Qt::AlignLeft | Qt::AlignTop. +*/ + +class QWT_EXPORT QwtPlotMarker: public QwtPlotItem +{ +public: + + /*! + Line styles. + \sa setLineStyle(), lineStyle() + */ + enum LineStyle + { + //! No line + NoLine, + + //! A horizontal line + HLine, + + //! A vertical line + VLine, + + //! A crosshair + Cross + }; + + explicit QwtPlotMarker(); + virtual ~QwtPlotMarker(); + + virtual int rtti() const; + + double xValue() const; + double yValue() const; + QPointF value() const; + + void setXValue( double ); + void setYValue( double ); + void setValue( double, double ); + void setValue( const QPointF & ); + + void setLineStyle( LineStyle st ); + LineStyle lineStyle() const; + + void setLinePen( const QPen &p ); + const QPen &linePen() const; + + void setSymbol( const QwtSymbol * ); + const QwtSymbol *symbol() const; + + void setLabel( const QwtText& ); + QwtText label() const; + + void setLabelAlignment( Qt::Alignment ); + Qt::Alignment labelAlignment() const; + + void setLabelOrientation( Qt::Orientation ); + Qt::Orientation labelOrientation() const; + + void setSpacing( int ); + int spacing() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF & ) const; + + virtual QRectF boundingRect() const; + + virtual void updateLegend( QwtLegend * ) const; + virtual void drawLegendIdentifier( QPainter *, const QRectF & ) const; + +protected: + virtual void drawLines( QPainter *, + const QRectF &, const QPointF & ) const; + + virtual void drawLabel( QPainter *, + const QRectF &, const QPointF & ) const; + +private: + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_panner.cpp b/src/libpcp_qwt/src/qwt_plot_panner.cpp new file mode 100644 index 0000000..fa8cb54 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_panner.cpp @@ -0,0 +1,175 @@ +/* -*- 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_plot_panner.h" +#include "qwt_scale_div.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" + +class QwtPlotPanner::PrivateData +{ +public: + PrivateData() + { + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + isAxisEnabled[axis] = true; + } + + bool isAxisEnabled[QwtPlot::axisCnt]; +}; + +/*! + \brief Create a plot panner + + The panner is enabled for all axes + + \param canvas Plot canvas to pan, also the parent object + + \sa setAxisEnabled() +*/ +QwtPlotPanner::QwtPlotPanner( QwtPlotCanvas *canvas ): + QwtPanner( canvas ) +{ + d_data = new PrivateData(); + + connect( this, SIGNAL( panned( int, int ) ), + SLOT( moveCanvas( int, int ) ) ); +} + +//! Destructor +QwtPlotPanner::~QwtPlotPanner() +{ + delete d_data; +} + +/*! + \brief En/Disable an axis + + Axes that are enabled will be synchronized to the + result of panning. All other axes will remain unchanged. + + \param axis Axis, see QwtPlot::Axis + \param on On/Off + + \sa isAxisEnabled(), moveCanvas() +*/ +void QwtPlotPanner::setAxisEnabled( int axis, bool on ) +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->isAxisEnabled[axis] = on; +} + +/*! + Test if an axis is enabled + + \param axis Axis, see QwtPlot::Axis + \return True, if the axis is enabled + + \sa setAxisEnabled(), moveCanvas() +*/ +bool QwtPlotPanner::isAxisEnabled( int axis ) const +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + return d_data->isAxisEnabled[axis]; + + return true; +} + +//! Return observed plot canvas +QwtPlotCanvas *QwtPlotPanner::canvas() +{ + return qobject_cast<QwtPlotCanvas *>( parentWidget() ); +} + +//! Return Observed plot canvas +const QwtPlotCanvas *QwtPlotPanner::canvas() const +{ + return qobject_cast<const QwtPlotCanvas *>( parentWidget() ); +} + +//! Return plot widget, containing the observed plot canvas +QwtPlot *QwtPlotPanner::plot() +{ + QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +//! Return plot widget, containing the observed plot canvas +const QwtPlot *QwtPlotPanner::plot() const +{ + const QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +/*! + Adjust the enabled axes according to dx/dy + + \param dx Pixel offset in x direction + \param dy Pixel offset in y direction + + \sa QwtPanner::panned() +*/ +void QwtPlotPanner::moveCanvas( int dx, int dy ) +{ + if ( dx == 0 && dy == 0 ) + return; + + QwtPlot *plot = this->plot(); + if ( plot == NULL ) + return; + + const bool doAutoReplot = plot->autoReplot(); + plot->setAutoReplot( false ); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( !d_data->isAxisEnabled[axis] ) + continue; + + const QwtScaleMap map = plot->canvasMap( axis ); + + const double p1 = map.transform( plot->axisScaleDiv( axis )->lowerBound() ); + const double p2 = map.transform( plot->axisScaleDiv( axis )->upperBound() ); + + double d1, d2; + if ( axis == QwtPlot::xBottom || axis == QwtPlot::xTop ) + { + d1 = map.invTransform( p1 - dx ); + d2 = map.invTransform( p2 - dx ); + } + else + { + d1 = map.invTransform( p1 - dy ); + d2 = map.invTransform( p2 - dy ); + } + + plot->setAxisScale( axis, d1, d2 ); + } + + plot->setAutoReplot( doAutoReplot ); + plot->replot(); +} + +/*! + Calculate a mask from the border mask of the canvas + \sa QwtPlotCanvas::borderMask() +*/ +QBitmap QwtPlotPanner::contentsMask() const +{ + if ( canvas() ) + return canvas()->borderMask( size() ); + + return QwtPanner::contentsMask(); +} diff --git a/src/libpcp_qwt/src/qwt_plot_panner.h b/src/libpcp_qwt/src/qwt_plot_panner.h new file mode 100644 index 0000000..fc783e3 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_panner.h @@ -0,0 +1,60 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_PANNER_H +#define QWT_PLOT_PANNER_H 1 + +#include "qwt_global.h" +#include "qwt_panner.h" + +class QwtPlotCanvas; +class QwtPlot; + +/*! + \brief QwtPlotPanner provides panning of a plot canvas + + QwtPlotPanner is a panner for a QwtPlotCanvas, that + adjusts the scales of the axes after dropping + the canvas on its new position. + + Together with QwtPlotZoomer and QwtPlotMagnifier powerful ways + of navigating on a QwtPlot widget can be implemented easily. + + \note The axes are not updated, while dragging the canvas + \sa QwtPlotZoomer, QwtPlotMagnifier +*/ +class QWT_EXPORT QwtPlotPanner: public QwtPanner +{ + Q_OBJECT + +public: + explicit QwtPlotPanner( QwtPlotCanvas * ); + virtual ~QwtPlotPanner(); + + QwtPlotCanvas *canvas(); + const QwtPlotCanvas *canvas() const; + + QwtPlot *plot(); + const QwtPlot *plot() const; + + void setAxisEnabled( int axis, bool on ); + bool isAxisEnabled( int axis ) const; + +protected Q_SLOTS: + virtual void moveCanvas( int dx, int dy ); + +protected: + virtual QBitmap contentsMask() const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_picker.cpp b/src/libpcp_qwt/src/qwt_plot_picker.cpp new file mode 100644 index 0000000..7a4073c --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_picker.cpp @@ -0,0 +1,383 @@ +/* -*- 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_plot_picker.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_scale_div.h" +#include "qwt_painter.h" +#include "qwt_scale_map.h" +#include "qwt_picker_machine.h" + +/*! + \brief Create a plot picker + + The picker is set to those x- and y-axis of the plot + that are enabled. If both or no x-axis are enabled, the picker + is set to QwtPlot::xBottom. If both or no y-axis are + enabled, it is set to QwtPlot::yLeft. + + \param canvas Plot canvas to observe, also the parent object + + \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect() +*/ + +QwtPlotPicker::QwtPlotPicker( QwtPlotCanvas *canvas ): + QwtPicker( canvas ), + d_xAxis( -1 ), + d_yAxis( -1 ) +{ + if ( !canvas ) + return; + + // attach axes + + int xAxis = QwtPlot::xBottom; + + const QwtPlot *plot = QwtPlotPicker::plot(); + if ( !plot->axisEnabled( QwtPlot::xBottom ) && + plot->axisEnabled( QwtPlot::xTop ) ) + { + xAxis = QwtPlot::xTop; + } + + int yAxis = QwtPlot::yLeft; + if ( !plot->axisEnabled( QwtPlot::yLeft ) && + plot->axisEnabled( QwtPlot::yRight ) ) + { + yAxis = QwtPlot::yRight; + } + + setAxis( xAxis, yAxis ); +} + +/*! + Create a plot picker + + \param xAxis Set the x axis of the picker + \param yAxis Set the y axis of the picker + \param canvas Plot canvas to observe, also the parent object + + \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect() +*/ +QwtPlotPicker::QwtPlotPicker( int xAxis, int yAxis, QwtPlotCanvas *canvas ): + QwtPicker( canvas ), + d_xAxis( xAxis ), + d_yAxis( yAxis ) +{ +} + +/*! + Create a plot picker + + \param xAxis X axis of the picker + \param yAxis Y axis of the picker + \param rubberBand Rubberband style + \param trackerMode Tracker mode + \param canvas Plot canvas to observe, also the parent object + + \sa QwtPicker, QwtPicker::setSelectionFlags(), QwtPicker::setRubberBand(), + QwtPicker::setTrackerMode + + \sa QwtPlot::autoReplot(), QwtPlot::replot(), scaleRect() +*/ +QwtPlotPicker::QwtPlotPicker( int xAxis, int yAxis, + RubberBand rubberBand, DisplayMode trackerMode, + QwtPlotCanvas *canvas ): + QwtPicker( rubberBand, trackerMode, canvas ), + d_xAxis( xAxis ), + d_yAxis( yAxis ) +{ +} + +//! Destructor +QwtPlotPicker::~QwtPlotPicker() +{ +} + +//! Return observed plot canvas +QwtPlotCanvas *QwtPlotPicker::canvas() +{ + return qobject_cast<QwtPlotCanvas *>( parentWidget() ); +} + +//! Return Observed plot canvas +const QwtPlotCanvas *QwtPlotPicker::canvas() const +{ + return qobject_cast<const QwtPlotCanvas *>( parentWidget() ); +} + +//! Return plot widget, containing the observed plot canvas +QwtPlot *QwtPlotPicker::plot() +{ + QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +//! Return plot widget, containing the observed plot canvas +const QwtPlot *QwtPlotPicker::plot() const +{ + const QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +/*! + Return normalized bounding rect of the axes + + \sa QwtPlot::autoReplot(), QwtPlot::replot(). +*/ +QRectF QwtPlotPicker::scaleRect() const +{ + QRectF rect; + + if ( plot() ) + { + const QwtScaleDiv *xs = plot()->axisScaleDiv( xAxis() ); + const QwtScaleDiv *ys = plot()->axisScaleDiv( yAxis() ); + + if ( xs && ys ) + { + rect = QRectF( xs->lowerBound(), ys->lowerBound(), + xs->range(), ys->range() ); + rect = rect.normalized(); + } + } + + return rect; +} + +/*! + Set the x and y axes of the picker + + \param xAxis X axis + \param yAxis Y axis +*/ +void QwtPlotPicker::setAxis( int xAxis, int yAxis ) +{ + const QwtPlot *plt = plot(); + if ( !plt ) + return; + + if ( xAxis != d_xAxis || yAxis != d_yAxis ) + { + d_xAxis = xAxis; + d_yAxis = yAxis; + } +} + +//! Return x axis +int QwtPlotPicker::xAxis() const +{ + return d_xAxis; +} + +//! Return y axis +int QwtPlotPicker::yAxis() const +{ + return d_yAxis; +} + +/*! + Translate a pixel position into a position string + + \param pos Position in pixel coordinates + \return Position string +*/ +QwtText QwtPlotPicker::trackerText( const QPoint &pos ) const +{ + return trackerTextF( invTransform( pos ) ); +} + +/*! + \brief Translate a position into a position string + + In case of HLineRubberBand the label is the value of the + y position, in case of VLineRubberBand the value of the x position. + Otherwise the label contains x and y position separated by a ',' . + + The format for the double to string conversion is "%.4f". + + \param pos Position + \return Position string +*/ +QwtText QwtPlotPicker::trackerTextF( const QPointF &pos ) const +{ + QString text; + + switch ( rubberBand() ) + { + case HLineRubberBand: + text.sprintf( "%.4f", pos.y() ); + break; + case VLineRubberBand: + text.sprintf( "%.4f", pos.x() ); + break; + default: + text.sprintf( "%.4f, %.4f", pos.x(), pos.y() ); + } + return QwtText( text ); +} + +/*! + Append a point to the selection and update rubberband and tracker. + + \param pos Additional point + \sa isActive, begin(), end(), move(), appended() + + \note The appended(const QPoint &), appended(const QDoublePoint &) + signals are emitted. +*/ +void QwtPlotPicker::append( const QPoint &pos ) +{ + QwtPicker::append( pos ); + Q_EMIT appended( invTransform( pos ) ); +} + +/*! + Move the last point of the selection + + \param pos New position + \sa isActive, begin(), end(), append() + + \note The moved(const QPoint &), moved(const QDoublePoint &) + signals are emitted. +*/ +void QwtPlotPicker::move( const QPoint &pos ) +{ + QwtPicker::move( pos ); + Q_EMIT moved( invTransform( pos ) ); +} + +/*! + Close a selection setting the state to inactive. + + \param ok If true, complete the selection and emit selected signals + otherwise discard the selection. + \return true if the selection is accepted, false otherwise +*/ + +bool QwtPlotPicker::end( bool ok ) +{ + ok = QwtPicker::end( ok ); + if ( !ok ) + return false; + + QwtPlot *plot = QwtPlotPicker::plot(); + if ( !plot ) + return false; + + const QPolygon points = selection(); + if ( points.count() == 0 ) + return false; + + QwtPickerMachine::SelectionType selectionType = + QwtPickerMachine::NoSelection; + + if ( stateMachine() ) + selectionType = stateMachine()->selectionType(); + + switch ( selectionType ) + { + case QwtPickerMachine::PointSelection: + { + const QPointF pos = invTransform( points.first() ); + Q_EMIT selected( pos ); + break; + } + case QwtPickerMachine::RectSelection: + { + if ( points.count() >= 2 ) + { + const QPoint p1 = points.first(); + const QPoint p2 = points.last(); + + const QRect rect = QRect( p1, p2 ).normalized(); + Q_EMIT selected( invTransform( rect ) ); + } + break; + } + case QwtPickerMachine::PolygonSelection: + { + QVector<QPointF> dpa( points.count() ); + for ( int i = 0; i < points.count(); i++ ) + dpa[i] = invTransform( points[i] ); + + Q_EMIT selected( dpa ); + } + default: + break; + } + + return true; +} + +/*! + Translate a rectangle from pixel into plot coordinates + + \return Rectangle in plot coordinates + \sa transform() +*/ +QRectF QwtPlotPicker::invTransform( const QRect &rect ) const +{ + const QwtScaleMap xMap = plot()->canvasMap( d_xAxis ); + const QwtScaleMap yMap = plot()->canvasMap( d_yAxis ); + + return QwtScaleMap::invTransform( xMap, yMap, rect ); +} + +/*! + Translate a rectangle from plot into pixel coordinates + \return Rectangle in pixel coordinates + \sa invTransform() +*/ +QRect QwtPlotPicker::transform( const QRectF &rect ) const +{ + const QwtScaleMap xMap = plot()->canvasMap( d_xAxis ); + const QwtScaleMap yMap = plot()->canvasMap( d_yAxis ); + + return QwtScaleMap::transform( xMap, yMap, rect ).toRect(); +} + +/*! + Translate a point from pixel into plot coordinates + \return Point in plot coordinates + \sa transform() +*/ +QPointF QwtPlotPicker::invTransform( const QPoint &pos ) const +{ + QwtScaleMap xMap = plot()->canvasMap( d_xAxis ); + QwtScaleMap yMap = plot()->canvasMap( d_yAxis ); + + return QPointF( + xMap.invTransform( pos.x() ), + yMap.invTransform( pos.y() ) + ); +} + +/*! + Translate a point from plot into pixel coordinates + \return Point in pixel coordinates + \sa invTransform() +*/ +QPoint QwtPlotPicker::transform( const QPointF &pos ) const +{ + QwtScaleMap xMap = plot()->canvasMap( d_xAxis ); + QwtScaleMap yMap = plot()->canvasMap( d_yAxis ); + + const QPointF p( xMap.transform( pos.x() ), + yMap.transform( pos.y() ) ); + + return p.toPoint(); +} diff --git a/src/libpcp_qwt/src/qwt_plot_picker.h b/src/libpcp_qwt/src/qwt_plot_picker.h new file mode 100644 index 0000000..f7d1bee --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_picker.h @@ -0,0 +1,115 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_PICKER_H +#define QWT_PLOT_PICKER_H + +#include "qwt_global.h" +#include "qwt_plot_canvas.h" +#include "qwt_picker.h" +#include <qvector.h> + +class QwtPlot; + +/*! + \brief QwtPlotPicker provides selections on a plot canvas + + QwtPlotPicker is a QwtPicker tailored for selections on + a plot canvas. It is set to a x-Axis and y-Axis and + translates all pixel coordinates into this coodinate system. +*/ + +class QWT_EXPORT QwtPlotPicker: public QwtPicker +{ + Q_OBJECT + +public: + explicit QwtPlotPicker( QwtPlotCanvas * ); + virtual ~QwtPlotPicker(); + + explicit QwtPlotPicker( int xAxis, int yAxis, QwtPlotCanvas * ); + + explicit QwtPlotPicker( int xAxis, int yAxis, + RubberBand rubberBand, DisplayMode trackerMode, + QwtPlotCanvas * ); + + virtual void setAxis( int xAxis, int yAxis ); + + int xAxis() const; + int yAxis() const; + + QwtPlot *plot(); + const QwtPlot *plot() const; + + QwtPlotCanvas *canvas(); + const QwtPlotCanvas *canvas() const; + +Q_SIGNALS: + + /*! + A signal emitted in case of selectionFlags() & PointSelection. + \param pos Selected point + */ + void selected( const QPointF &pos ); + + /*! + A signal emitted in case of selectionFlags() & RectSelection. + \param rect Selected rectangle + */ + void selected( const QRectF &rect ); + + /*! + A signal emitting the selected points, + at the end of a selection. + + \param pa Selected points + */ + void selected( const QVector<QPointF> &pa ); + + /*! + A signal emitted when a point has been appended to the selection + + \param pos Position of the appended point. + \sa append(). moved() + */ + void appended( const QPointF &pos ); + + /*! + A signal emitted whenever the last appended point of the + selection has been moved. + + \param pos Position of the moved last point of the selection. + \sa move(), appended() + */ + void moved( const QPointF &pos ); + +protected: + QRectF scaleRect() const; + +public: + QRectF invTransform( const QRect & ) const; + QRect transform( const QRectF & ) const; + + QPointF invTransform( const QPoint & ) const; + QPoint transform( const QPointF & ) const; + +protected: + virtual QwtText trackerText( const QPoint & ) const; + virtual QwtText trackerTextF( const QPointF & ) const; + + virtual void move( const QPoint & ); + virtual void append( const QPoint & ); + virtual bool end( bool ok = true ); + +private: + int d_xAxis; + int d_yAxis; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_rasteritem.cpp b/src/libpcp_qwt/src/qwt_plot_rasteritem.cpp new file mode 100644 index 0000000..08e02e7 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_rasteritem.cpp @@ -0,0 +1,904 @@ +/* -*- 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_plot_rasteritem.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include "qwt_scale_map.h" +#include "qwt_painter.h" +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qpainter.h> +#include <qpaintengine.h> +#include <float.h> + +class QwtPlotRasterItem::PrivateData +{ +public: + PrivateData(): + alpha( -1 ), + paintAttributes( QwtPlotRasterItem::PaintInDeviceResolution ) + { + cache.policy = QwtPlotRasterItem::NoCache; + } + + int alpha; + QwtPlotRasterItem::PaintAttributes paintAttributes; + + struct ImageCache + { + QwtPlotRasterItem::CachePolicy policy; + QRectF area; + QSizeF size; + QImage image; + } cache; +}; + + +static QRectF qwtAlignRect(const QRectF &rect) +{ + QRectF r; + r.setLeft( qRound( rect.left() ) ); + r.setRight( qRound( rect.right() ) ); + r.setTop( qRound( rect.top() ) ); + r.setBottom( qRound( rect.bottom() ) ); + + return r; +} + +static QRectF qwtStripRect(const QRectF &rect, const QRectF &area, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QwtInterval &xInterval, const QwtInterval &yInterval) +{ + QRectF r = rect; + if ( xInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + { + if ( area.left() <= xInterval.minValue() ) + { + if ( xMap.isInverting() ) + r.adjust(0, 0, -1, 0); + else + r.adjust(1, 0, 0, 0); + } + } + + if ( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + { + if ( area.right() >= xInterval.maxValue() ) + { + if ( xMap.isInverting() ) + r.adjust(1, 0, 0, 0); + else + r.adjust(0, 0, -1, 0); + } + } + + if ( yInterval.borderFlags() & QwtInterval::ExcludeMinimum ) + { + if ( area.top() <= yInterval.minValue() ) + { + if ( yMap.isInverting() ) + r.adjust(0, 0, 0, -1); + else + r.adjust(0, 1, 0, 0); + } + } + + if ( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) + { + if ( area.bottom() >= yInterval.maxValue() ) + { + if ( yMap.isInverting() ) + r.adjust(0, 1, 0, 0); + else + r.adjust(0, 0, 0, -1); + } + } + + return r; +} + +static QImage qwtExpandImage(const QImage &image, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &area, const QRectF &area2, const QRectF &paintRect, + const QwtInterval &xInterval, const QwtInterval &yInterval ) +{ + const QRectF strippedRect = qwtStripRect(paintRect, area2, + xMap, yMap, xInterval, yInterval); + const QSize sz = strippedRect.toRect().size(); + + const int w = image.width(); + const int h = image.height(); + + const QRectF r = QwtScaleMap::transform(xMap, yMap, area).normalized(); + const double pw = ( r.width() - 1) / w; + const double ph = ( r.height() - 1) / h; + + double px0, py0; + if ( !xMap.isInverting() ) + { + px0 = xMap.transform( area2.left() ); + px0 = qRound( px0 ); + px0 = px0 - xMap.transform( area.left() ); + } + else + { + px0 = xMap.transform( area2.right() ); + px0 = qRound( px0 ); + px0 -= xMap.transform( area.right() ); + + px0 -= 1.0; + } + px0 += strippedRect.left() - paintRect.left(); + + if ( !yMap.isInverting() ) + { + py0 = yMap.transform( area2.top() ); + py0 = qRound( py0 ); + py0 -= yMap.transform( area.top() ); + } + else + { + py0 = yMap.transform( area2.bottom() ); + py0 = qRound( py0 ); + py0 -= yMap.transform( area.bottom() ); + + py0 -= 1.0; + } + py0 += strippedRect.top() - paintRect.top(); + + QImage expanded(sz, image.format()); + + switch( image.depth() ) + { + case 32: + { + for ( int y1 = 0; y1 < h; y1++ ) + { + int yy1; + if ( y1 == 0 ) + { + yy1 = 0; + } + else + { + yy1 = qRound( y1 * ph - py0 ); + if ( yy1 < 0 ) + yy1 = 0; + } + + int yy2; + if ( y1 == h - 1 ) + { + yy2 = sz.height(); + } + else + { + yy2 = qRound( ( y1 + 1 ) * ph - py0 ); + if ( yy2 > sz.height() ) + yy2 = sz.height(); + } + + const quint32 *line1 = (const quint32 *) image.scanLine( y1 ); + + for ( int x1 = 0; x1 < w; x1++ ) + { + int xx1; + if ( x1 == 0 ) + { + xx1 = 0; + } + else + { + xx1 = qRound( x1 * pw - px0 ); + if ( xx1 < 0 ) + xx1 = 0; + } + + int xx2; + if ( x1 == w - 1 ) + { + xx2 = sz.width(); + } + else + { + xx2 = qRound( ( x1 + 1 ) * pw - px0 ); + if ( xx2 > sz.width() ) + xx2 = sz.width(); + } + + const quint32 rgb( line1[x1] ); + for ( int y2 = yy1; y2 < yy2; y2++ ) + { + quint32 *line2 = ( quint32 *) expanded.scanLine( y2 ); + for ( int x2 = xx1; x2 < xx2; x2++ ) + line2[x2] = rgb; + } + } + } + break; + } + case 8: + { + for ( int y1 = 0; y1 < h; y1++ ) + { + int yy1; + if ( y1 == 0 ) + { + yy1 = 0; + } + else + { + yy1 = qRound( y1 * ph - py0 ); + if ( yy1 < 0 ) + yy1 = 0; + } + + int yy2; + if ( y1 == h - 1 ) + { + yy2 = sz.height(); + } + else + { + yy2 = qRound( ( y1 + 1 ) * ph - py0 ); + if ( yy2 > sz.height() ) + yy2 = sz.height(); + } + + const uchar *line1 = image.scanLine( y1 ); + + for ( int x1 = 0; x1 < w; x1++ ) + { + int xx1; + if ( x1 == 0 ) + { + xx1 = 0; + } + else + { + xx1 = qRound( x1 * pw - px0 ); + if ( xx1 < 0 ) + xx1 = 0; + } + + int xx2; + if ( x1 == w - 1 ) + { + xx2 = sz.width(); + } + else + { + xx2 = qRound( ( x1 + 1 ) * pw - px0 ); + if ( xx2 > sz.width() ) + xx2 = sz.width(); + } + + for ( int y2 = yy1; y2 < yy2; y2++ ) + { + uchar *line2 = expanded.scanLine( y2 ); + memset( line2 + xx1, line1[x1], xx2 - xx1 ); + } + } + } + break; + } + default: + expanded = image; + } + + return expanded; +} + +static QRectF expandToPixels(const QRectF &rect, const QRectF &pixelRect) +{ + const double pw = pixelRect.width(); + const double ph = pixelRect.height(); + + const double dx1 = pixelRect.left() - rect.left(); + const double dx2 = pixelRect.right() - rect.right(); + const double dy1 = pixelRect.top() - rect.top(); + const double dy2 = pixelRect.bottom() - rect.bottom(); + + QRectF r; + r.setLeft( pixelRect.left() - qCeil( dx1 / pw ) * pw ); + r.setTop( pixelRect.top() - qCeil( dy1 / ph ) * ph ); + r.setRight( pixelRect.right() - qFloor( dx2 / pw ) * pw ); + r.setBottom( pixelRect.bottom() - qFloor( dy2 / ph ) * ph ); + + return r; +} + +static void transformMaps( const QTransform &tr, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + QwtScaleMap &xxMap, QwtScaleMap &yyMap ) +{ + const QPointF p1 = tr.map( QPointF( xMap.p1(), yMap.p1() ) ); + const QPointF p2 = tr.map( QPointF( xMap.p2(), yMap.p2() ) ); + + xxMap = xMap; + xxMap.setPaintInterval( p1.x(), p2.x() ); + + yyMap = yMap; + yyMap.setPaintInterval( p1.y(), p2.y() ); +} + +static void adjustMaps( QwtScaleMap &xMap, QwtScaleMap &yMap, + const QRectF &area, const QRectF &paintRect) +{ + double sx1 = area.left(); + double sx2 = area.right(); + if ( xMap.isInverting() ) + qSwap(sx1, sx2); + + double sy1 = area.top(); + double sy2 = area.bottom(); + + if ( yMap.isInverting() ) + qSwap(sy1, sy2); + + xMap.setPaintInterval(paintRect.left(), paintRect.right()); + xMap.setScaleInterval(sx1, sx2); + + yMap.setPaintInterval(paintRect.top(), paintRect.bottom()); + yMap.setScaleInterval(sy1, sy2); +} + +static bool useCache( QwtPlotRasterItem::CachePolicy policy, + const QPainter *painter ) +{ + bool doCache = false; + + if ( policy == QwtPlotRasterItem::PaintCache ) + { + // Caching doesn't make sense, when the item is + // not painted to screen + + switch ( painter->paintEngine()->type() ) + { + case QPaintEngine::SVG: + case QPaintEngine::Pdf: + case QPaintEngine::PostScript: + case QPaintEngine::MacPrinter: + case QPaintEngine::Picture: + break; + default:; + doCache = true; + } + } + + return doCache; +} + +static QImage toRgba( const QImage& image, int alpha ) +{ + if ( alpha < 0 || alpha >= 255 ) + return image; + + QImage alphaImage( image.size(), QImage::Format_ARGB32 ); + + const QRgb mask1 = qRgba( 0, 0, 0, alpha ); + const QRgb mask2 = qRgba( 255, 255, 255, 0 ); + const QRgb mask3 = qRgba( 0, 0, 0, 255 ); + + const int w = image.size().width(); + const int h = image.size().height(); + + if ( image.depth() == 8 ) + { + for ( int y = 0; y < h; y++ ) + { + QRgb* alphaLine = ( QRgb* )alphaImage.scanLine( y ); + const unsigned char *line = image.scanLine( y ); + + for ( int x = 0; x < w; x++ ) + *alphaLine++ = ( image.color( *line++ ) & mask2 ) | mask1; + } + } + else if ( image.depth() == 32 ) + { + for ( int y = 0; y < h; y++ ) + { + QRgb* alphaLine = ( QRgb* )alphaImage.scanLine( y ); + const QRgb* line = ( const QRgb* ) image.scanLine( y ); + + for ( int x = 0; x < w; x++ ) + { + const QRgb rgb = *line++; + if ( rgb & mask3 ) // alpha != 0 + *alphaLine++ = ( rgb & mask2 ) | mask1; + else + *alphaLine++ = rgb; + } + } + } + + return alphaImage; +} + +//! Constructor +QwtPlotRasterItem::QwtPlotRasterItem( const QString& title ): + QwtPlotItem( QwtText( title ) ) +{ + init(); +} + +//! Constructor +QwtPlotRasterItem::QwtPlotRasterItem( const QwtText& title ): + QwtPlotItem( title ) +{ + init(); +} + +//! Destructor +QwtPlotRasterItem::~QwtPlotRasterItem() +{ + delete d_data; +} + +void QwtPlotRasterItem::init() +{ + d_data = new PrivateData(); + + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Legend, false ); + + setZ( 8.0 ); +} + +/*! + Specify an attribute how to draw the raster item + + \param attribute Paint attribute + \param on On/Off + /sa PaintAttribute, testPaintAttribute() +*/ +void QwtPlotRasterItem::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \brief Return the current paint attributes + \sa PaintAttribute, setPaintAttribute() +*/ +bool QwtPlotRasterItem::testPaintAttribute( PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + \brief Set an alpha value for the raster data + + Often a plot has several types of raster data organized in layers. + ( f.e a geographical map, with weather statistics ). + Using setAlpha() raster items can be stacked easily. + + The alpha value is a value [0, 255] to + control the transparency of the image. 0 represents a fully + transparent color, while 255 represents a fully opaque color. + + \param alpha Alpha value + + - alpha >= 0\n + All alpha values of the pixels returned by renderImage() will be set to + alpha, beside those with an alpha value of 0 (invalid pixels). + - alpha < 0 + The alpha values returned by renderImage() are not changed. + + The default alpha value is -1. + + \sa alpha() +*/ +void QwtPlotRasterItem::setAlpha( int alpha ) +{ + if ( alpha < 0 ) + alpha = -1; + + if ( alpha > 255 ) + alpha = 255; + + if ( alpha != d_data->alpha ) + { + d_data->alpha = alpha; + + itemChanged(); + } +} + +/*! + \return Alpha value of the raster item + \sa setAlpha() +*/ +int QwtPlotRasterItem::alpha() const +{ + return d_data->alpha; +} + +/*! + Change the cache policy + + The default policy is NoCache + + \param policy Cache policy + \sa CachePolicy, cachePolicy() +*/ +void QwtPlotRasterItem::setCachePolicy( + QwtPlotRasterItem::CachePolicy policy ) +{ + if ( d_data->cache.policy != policy ) + { + d_data->cache.policy = policy; + + invalidateCache(); + itemChanged(); + } +} + +/*! + \return Cache policy + \sa CachePolicy, setCachePolicy() +*/ +QwtPlotRasterItem::CachePolicy QwtPlotRasterItem::cachePolicy() const +{ + return d_data->cache.policy; +} + +/*! + Invalidate the paint cache + \sa setCachePolicy() +*/ +void QwtPlotRasterItem::invalidateCache() +{ + d_data->cache.image = QImage(); + d_data->cache.area = QRect(); + d_data->cache.size = QSize(); +} + +/*! + \brief Pixel hint + + The geometry of a pixel is used to calculated the resolution and + alignment of the rendered image. + + Width and height of the hint need to be the horizontal + and vertical distances between 2 neighboured points. + The center of the hint has to be the position of any point + ( it doesn't matter which one ). + + Limiting the resolution of the image might significantly improve + the performance and heavily reduce the amount of memory when rendering + a QImage from the raster data. + + The default implementation returns an empty rectangle (QRectF()), + meaning, that the image will be rendered in target device ( f.e screen ) + resolution. + + \param area In most implementations the resolution of the data doesn't + depend on the requested area. + + \return Bounding rectangle of a pixel + + \sa render(), renderImage() +*/ +QRectF QwtPlotRasterItem::pixelHint( const QRectF &area ) const +{ + Q_UNUSED( area ); + return QRectF(); +} + +/*! + \brief Draw the raster data + \param painter Painter + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param canvasRect Contents rect of the plot canvas +*/ +void QwtPlotRasterItem::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + if ( canvasRect.isEmpty() || d_data->alpha == 0 ) + return; + + const bool doCache = useCache( d_data->cache.policy, painter ); + + const QwtInterval xInterval = interval( Qt::XAxis ); + const QwtInterval yInterval = interval( Qt::YAxis ); + + /* + Scaling a rastered image always results in a loss of + precision/quality. So we always render the image in + paint device resolution. + */ + + QwtScaleMap xxMap, yyMap; + transformMaps( painter->transform(), xMap, yMap, xxMap, yyMap ); + + QRectF paintRect = painter->transform().mapRect( canvasRect ); + QRectF area = QwtScaleMap::invTransform( xxMap, yyMap, paintRect ); + + const QRectF br = boundingRect(); + if ( br.isValid() && !br.contains( area ) ) + { + area &= br; + if ( !area.isValid() ) + return; + + paintRect = QwtScaleMap::transform( xxMap, yyMap, area ); + } + + QRectF imageRect; + QImage image; + + QRectF pixelRect = pixelHint(area); + if ( !pixelRect.isEmpty() ) + { + // pixel in target device resolution + const double dx = qAbs( xxMap.invTransform( 1 ) - xxMap.invTransform( 0 ) ); + const double dy = qAbs( yyMap.invTransform( 1 ) - yyMap.invTransform( 0 ) ); + + if ( dx > pixelRect.width() && dy > pixelRect.height() ) + { + /* + When the resolution of the data pixels is higher than + the resolution of the target device we render in + target device resolution. + */ + pixelRect = QRectF(); + } + } + + if ( pixelRect.isEmpty() ) + { + if ( QwtPainter::roundingAlignment( painter ) ) + { + // we want to have maps, where the boundaries of + // the aligned paint rectangle exactly match the area + + paintRect = qwtAlignRect(paintRect); + adjustMaps(xxMap, yyMap, area, paintRect); + } + + // When we have no information about position and size of + // data pixels we render in resolution of the paint device. + + image = compose(xxMap, yyMap, + area, paintRect, paintRect.size().toSize(), doCache); + if ( image.isNull() ) + return; + + // Remove pixels at the boundaries, when explicitly + // excluded in the intervals + + imageRect = qwtStripRect(paintRect, area, + xxMap, yyMap, xInterval, yInterval); + + if ( imageRect != paintRect ) + { + const QRect r( + qRound( imageRect.x() - paintRect.x()), + qRound( imageRect.y() - paintRect.y() ), + qRound( imageRect.width() ), + qRound( imageRect.height() ) ); + + image = image.copy(r); + } + } + else + { + if ( QwtPainter::roundingAlignment( painter ) ) + paintRect = qwtAlignRect(paintRect); + + // align the area to the data pixels + QRectF imageArea = expandToPixels(area, pixelRect); + + if ( imageArea.right() == xInterval.maxValue() && + !( xInterval.borderFlags() & QwtInterval::ExcludeMaximum ) ) + { + imageArea.adjust(0, 0, pixelRect.width(), 0); + } + if ( imageArea.bottom() == yInterval.maxValue() && + !( yInterval.borderFlags() & QwtInterval::ExcludeMaximum ) ) + { + imageArea.adjust(0, 0, 0, pixelRect.height() ); + } + + QSize imageSize; + imageSize.setWidth( qRound( imageArea.width() / pixelRect.width() ) ); + imageSize.setHeight( qRound( imageArea.height() / pixelRect.height() ) ); + image = compose(xxMap, yyMap, + imageArea, paintRect, imageSize, doCache ); + if ( image.isNull() ) + return; + + imageRect = qwtStripRect(paintRect, area, + xxMap, yyMap, xInterval, yInterval); + + if ( ( image.width() > 1 || image.height() > 1 ) && + testPaintAttribute( PaintInDeviceResolution ) ) + { + // Because of rounding errors the pixels + // need to be expanded manually to rectangles of + // different sizes + + image = qwtExpandImage(image, xxMap, yyMap, + imageArea, area, paintRect, xInterval, yInterval ); + } + } + + painter->save(); + painter->setWorldTransform( QTransform() ); + + QwtPainter::drawImage( painter, imageRect, image ); + + painter->restore(); +} + +/*! + \return Bounding interval for an axis + + This method is intended to be reimplemented by derived classes. + The default implementation returns an invalid interval. + + \param axis X, Y, or Z axis +*/ +QwtInterval QwtPlotRasterItem::interval(Qt::Axis axis) const +{ + Q_UNUSED( axis ); + return QwtInterval(); +} + +/*! + \return Bounding rect of the data + \sa QwtPlotRasterItem::interval() +*/ +QRectF QwtPlotRasterItem::boundingRect() const +{ + const QwtInterval intervalX = interval( Qt::XAxis ); + const QwtInterval intervalY = interval( Qt::YAxis ); + + if ( !intervalX.isValid() && !intervalY.isValid() ) + return QRectF(); // no bounding rect + + QRectF r; + + if ( intervalX.isValid() ) + { + r.setLeft( intervalX.minValue() ); + r.setRight( intervalX.maxValue() ); + } + else + { + r.setLeft(-0.5 * FLT_MAX); + r.setWidth(FLT_MAX); + } + + if ( intervalY.isValid() ) + { + r.setTop( intervalY.minValue() ); + r.setBottom( intervalY.maxValue() ); + } + else + { + r.setTop(-0.5 * FLT_MAX); + r.setHeight(FLT_MAX); + } + + return r.normalized(); +} + +QImage QwtPlotRasterItem::compose( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &imageArea, const QRectF &paintRect, + const QSize &imageSize, bool doCache) const +{ + QImage image; + if ( imageArea.isEmpty() || paintRect.isEmpty() || imageSize.isEmpty() ) + return image; + + if ( doCache ) + { + if ( !d_data->cache.image.isNull() + && d_data->cache.area == imageArea + && d_data->cache.size == paintRect.size() ) + { + image = d_data->cache.image; + } + } + + if ( image.isNull() ) + { + double dx = 0.0; + if ( paintRect.toRect().width() > imageSize.width() ) + dx = imageArea.width() / imageSize.width(); + + const QwtScaleMap xxMap = + imageMap(Qt::Horizontal, xMap, imageArea, imageSize, dx); + + double dy = 0.0; + if ( paintRect.toRect().height() > imageSize.height() ) + dy = imageArea.height() / imageSize.height(); + + const QwtScaleMap yyMap = + imageMap(Qt::Vertical, yMap, imageArea, imageSize, dy); + + image = renderImage( xxMap, yyMap, imageArea, imageSize ); + + if ( doCache ) + { + d_data->cache.area = imageArea; + d_data->cache.size = paintRect.size(); + d_data->cache.image = image; + } + } + + if ( d_data->alpha >= 0 && d_data->alpha < 255 ) + image = toRgba( image, d_data->alpha ); + + return image; +} + +/*! + \brief Calculate a scale map for painting to an image + + \param orientation Orientation, Qt::Horizontal means a X axis + \param map Scale map for rendering the plot item + \param area Area to be painted on the image + \param imageSize Image size + \param pixelSize Width/Height of a data pixel +*/ +QwtScaleMap QwtPlotRasterItem::imageMap( + Qt::Orientation orientation, + const QwtScaleMap &map, const QRectF &area, + const QSize &imageSize, double pixelSize) const +{ + double p1, p2, s1, s2; + + if ( orientation == Qt::Horizontal ) + { + p1 = 0.0; + p2 = imageSize.width(); + s1 = area.left(); + s2 = area.right(); + } + else + { + p1 = 0.0; + p2 = imageSize.height(); + s1 = area.top(); + s2 = area.bottom(); + } + + if ( pixelSize > 0.0 ) + { + double off = 0.5 * pixelSize; + if ( map.isInverting() ) + off = -off; + + s1 += off; + s2 += off; + } + else + { + p2--; + } + + if ( map.isInverting() && ( s1 < s2 ) ) + qSwap( s1, s2 ); + + QwtScaleMap newMap = map; + newMap.setPaintInterval( p1, p2 ); + newMap.setScaleInterval( s1, s2 ); + + return newMap; +} diff --git a/src/libpcp_qwt/src/qwt_plot_rasteritem.h b/src/libpcp_qwt/src/qwt_plot_rasteritem.h new file mode 100644 index 0000000..b2292d8 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_rasteritem.h @@ -0,0 +1,146 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_RASTERITEM_H +#define QWT_PLOT_RASTERITEM_H + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include "qwt_interval.h" +#include <qglobal.h> +#include <qstring.h> +#include <qimage.h> + +/*! + \brief A class, which displays raster data + + Raster data is a grid of pixel values, that can be represented + as a QImage. It is used for many types of information like + spectrograms, cartograms, geographical maps ... + + Often a plot has several types of raster data organized in layers. + ( f.e a geographical map, with weather statistics ). + Using setAlpha() raster items can be stacked easily. + + QwtPlotRasterItem is only implemented for images of the following formats: + QImage::Format_Indexed8, QImage::Format_ARGB32. + + \sa QwtPlotSpectrogram +*/ + +class QWT_EXPORT QwtPlotRasterItem: public QwtPlotItem +{ +public: + /*! + - NoCache\n + renderImage() is called, whenever the item has to be repainted + - PaintCache\n + renderImage() is called, whenever the image cache is not valid, + or the scales, or the size of the canvas has changed. This type + of cache is only useful for improving the performance of hide/show + operations. All other situations are already handled by the + plot canvas cache. + + The default policy is NoCache + */ + enum CachePolicy + { + NoCache, + PaintCache + }; + + /*! + Attributes to modify the drawing algorithm. + \sa setPaintAttribute(), testPaintAttribute() + */ + enum PaintAttribute + { + /*! + When the image is rendered according to the data pixels + ( QwtRasterData::pixelHint() ) it can be expanded to paint + device resolution before it is passed to QPainter. + The expansion algorithm rounds the pixel borders in the same + way as the axis ticks, what is usually better than the + scaling algorithm implemented in Qt. + Disabling this flag might make sense, to reduce the size of a + document/file. If this is possible for a document format + depends on the implementation of the specific QPaintEngine. + */ + + PaintInDeviceResolution = 1 + }; + + //! Paint attributes + typedef QFlags<PaintAttribute> PaintAttributes; + + explicit QwtPlotRasterItem( const QString& title = QString::null ); + explicit QwtPlotRasterItem( const QwtText& title ); + virtual ~QwtPlotRasterItem(); + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setAlpha( int alpha ); + int alpha() const; + + void setCachePolicy( CachePolicy ); + CachePolicy cachePolicy() const; + + void invalidateCache(); + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + + virtual QRectF pixelHint( const QRectF & ) const; + + virtual QwtInterval interval(Qt::Axis) const; + virtual QRectF boundingRect() const; + +protected: + /*! + \brief Render an image + + An implementation of render() might iterate over all + pixels of imageRect. Each pixel has to be translated into + the corresponding position in scale coordinates using the maps. + This position can be used to look up a value in a implementation + specific way and to map it into a color. + + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param area Requested area for the image in scale coordinates + \param imageSize Requested size of the image + */ + virtual QImage renderImage( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &area, + const QSize &imageSize ) const = 0; + + virtual QwtScaleMap imageMap( Qt::Orientation, + const QwtScaleMap &map, const QRectF &area, + const QSize &imageSize, double pixelSize) const; + +private: + QwtPlotRasterItem( const QwtPlotRasterItem & ); + QwtPlotRasterItem &operator=( const QwtPlotRasterItem & ); + + void init(); + + QImage compose( const QwtScaleMap &, const QwtScaleMap &, + const QRectF &imageArea, const QRectF &paintRect, + const QSize &imageSize, bool doCache) const; + + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRasterItem::PaintAttributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_renderer.cpp b/src/libpcp_qwt/src/qwt_plot_renderer.cpp new file mode 100644 index 0000000..4dfbfec --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_renderer.cpp @@ -0,0 +1,897 @@ +/* -*- 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_plot_renderer.h" +#include "qwt_plot.h" +#include "qwt_painter.h" +#include "qwt_plot_canvas.h" +#include "qwt_plot_layout.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include "qwt_dyngrid_layout.h" +#include "qwt_scale_widget.h" +#include "qwt_scale_engine.h" +#include "qwt_text.h" +#include "qwt_text_label.h" +#include "qwt_math.h" +#include <qpainter.h> +#include <qpaintengine.h> +#include <qtransform.h> +#include <qprinter.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qimagewriter.h> +#include <qfileinfo.h> +#ifndef QWT_NO_SVG +#ifdef QT_SVG_LIB +#include <qsvggenerator.h> +#endif +#endif + +class QwtPlotRenderer::PrivateData +{ +public: + PrivateData(): + discardFlags( QwtPlotRenderer::DiscardBackground ), + layoutFlags( QwtPlotRenderer::DefaultLayout ) + { + } + + QwtPlotRenderer::DiscardFlags discardFlags; + QwtPlotRenderer::LayoutFlags layoutFlags; +}; + +static void qwtRenderBackground( QPainter *painter, + const QRectF &rect, const QWidget *widget ) +{ + if ( widget->testAttribute( Qt::WA_StyledBackground ) ) + { + QStyleOption opt; + opt.initFrom( widget ); + opt.rect = rect.toAlignedRect(); + + widget->style()->drawPrimitive( + QStyle::PE_Widget, &opt, painter, widget); + } + else + { + const QBrush brush = + widget->palette().brush( widget->backgroundRole() ); + + painter->fillRect( rect, brush ); + } +} + +/*! + Constructor + \param parent Parent object +*/ +QwtPlotRenderer::QwtPlotRenderer( QObject *parent ): + QObject( parent ) +{ + d_data = new PrivateData; +} + +//! Destructor +QwtPlotRenderer::~QwtPlotRenderer() +{ + delete d_data; +} + +/*! + Change a flag, indicating what to discard from rendering + + \param flag Flag to change + \param on On/Off + + \sa DiscardFlag, testDiscardFlag(), setDiscardFlags(), discardFlags() +*/ +void QwtPlotRenderer::setDiscardFlag( DiscardFlag flag, bool on ) +{ + if ( on ) + d_data->discardFlags |= flag; + else + d_data->discardFlags &= ~flag; +} + +/*! + Check if a flag is set. + + \param flag Flag to be tested + \sa DiscardFlag, setDiscardFlag(), setDiscardFlags(), discardFlags() +*/ +bool QwtPlotRenderer::testDiscardFlag( DiscardFlag flag ) const +{ + return d_data->discardFlags & flag; +} + +/*! + Set the flags, indicating what to discard from rendering + + \param flags Flags + \sa DiscardFlag, setDiscardFlag(), testDiscardFlag(), discardFlags() +*/ +void QwtPlotRenderer::setDiscardFlags( DiscardFlags flags ) +{ + d_data->discardFlags = flags; +} + +/*! + \return Flags, indicating what to discard from rendering + \sa DiscardFlag, setDiscardFlags(), setDiscardFlag(), testDiscardFlag() +*/ +QwtPlotRenderer::DiscardFlags QwtPlotRenderer::discardFlags() const +{ + return d_data->discardFlags; +} + +/*! + Change a layout flag + + \param flag Flag to change + \param on On/Off + + \sa LayoutFlag, testLayoutFlag(), setLayoutFlags(), layoutFlags() +*/ +void QwtPlotRenderer::setLayoutFlag( LayoutFlag flag, bool on ) +{ + if ( on ) + d_data->layoutFlags |= flag; + else + d_data->layoutFlags &= ~flag; +} + +/*! + Check if a flag is set. + + \param flag Flag to be tested + \sa LayoutFlag, setLayoutFlag(), setLayoutFlags(), layoutFlags() +*/ +bool QwtPlotRenderer::testLayoutFlag( LayoutFlag flag ) const +{ + return d_data->layoutFlags & flag; +} + +/*! + Set the layout flags + + \param flags Flags + \sa LayoutFlag, setLayoutFlag(), testLayoutFlag(), layoutFlags() +*/ +void QwtPlotRenderer::setLayoutFlags( LayoutFlags flags ) +{ + d_data->layoutFlags = flags; +} + +/*! + \return Layout flags + \sa LayoutFlag, setLayoutFlags(), setLayoutFlag(), testLayoutFlag() +*/ +QwtPlotRenderer::LayoutFlags QwtPlotRenderer::layoutFlags() const +{ + return d_data->layoutFlags; +} + +/*! + Render a plot to a file + + The format of the document will be autodetected from the + suffix of the filename. + + \param plot Plot widget + \param fileName Path of the file, where the document will be stored + \param sizeMM Size for the document in millimeters. + \param resolution Resolution in dots per Inch (dpi) +*/ +void QwtPlotRenderer::renderDocument( QwtPlot *plot, + const QString &fileName, const QSizeF &sizeMM, int resolution ) +{ + renderDocument( plot, fileName, + QFileInfo( fileName ).suffix(), sizeMM, resolution ); +} + +/*! + Render a plot to a file + + Supported formats are: + + - pdf\n + Portable Document Format PDF + - ps\n + Postcript + - svg\n + Scalable Vector Graphics SVG + - all image formats supported by Qt\n + see QImageWriter::supportedImageFormats() + + Scalable vector graphic formats like PDF or SVG are superior to + raster graphics formats. + + \param plot Plot widget + \param fileName Path of the file, where the document will be stored + \param format Format for the document + \param sizeMM Size for the document in millimeters. + \param resolution Resolution in dots per Inch (dpi) + + \sa renderTo(), render(), QwtPainter::setRoundingAlignment() +*/ +void QwtPlotRenderer::renderDocument( QwtPlot *plot, + const QString &fileName, const QString &format, + const QSizeF &sizeMM, int resolution ) +{ + if ( plot == NULL || sizeMM.isEmpty() || resolution <= 0 ) + return; + + QString title = plot->title().text(); + if ( title.isEmpty() ) + title = "Plot Document"; + + const double mmToInch = 1.0 / 25.4; + const QSizeF size = sizeMM * mmToInch * resolution; + + const QRectF documentRect( 0.0, 0.0, size.width(), size.height() ); + + const QString fmt = format.toLower(); + if ( fmt == "pdf" ) + { +#ifndef QT_NO_PRINTER + QPrinter printer; + printer.setFullPage( true ); + printer.setPaperSize( sizeMM, QPrinter::Millimeter ); + printer.setDocName( title ); + printer.setOutputFileName( fileName ); + printer.setOutputFormat( QPrinter::PdfFormat ); + printer.setResolution( resolution ); + + QPainter painter( &printer ); + render( plot, &painter, documentRect ); +#endif + } + else if ( fmt == "ps" ) + { +#if QT_VERSION < 0x050000 +#ifndef QT_NO_PRINTER + QPrinter printer; + printer.setFullPage( true ); + printer.setPaperSize( sizeMM, QPrinter::Millimeter ); + printer.setDocName( title ); + printer.setOutputFileName( fileName ); + printer.setOutputFormat( QPrinter::PostScriptFormat ); + printer.setResolution( resolution ); + + QPainter painter( &printer ); + render( plot, &painter, documentRect ); +#endif +#endif + } + else if ( fmt == "svg" ) + { +#ifndef QWT_NO_SVG +#ifdef QT_SVG_LIB +#if QT_VERSION >= 0x040500 + QSvgGenerator generator; + generator.setTitle( title ); + generator.setFileName( fileName ); + generator.setResolution( resolution ); + generator.setViewBox( documentRect ); + + QPainter painter( &generator ); + render( plot, &painter, documentRect ); +#endif +#endif +#endif + } + else + { + if ( QImageWriter::supportedImageFormats().indexOf( + format.toLatin1() ) >= 0 ) + { + const QRect imageRect = documentRect.toRect(); + const int dotsPerMeter = qRound( resolution * mmToInch * 1000.0 ); + + QImage image( imageRect.size(), QImage::Format_ARGB32 ); + image.setDotsPerMeterX( dotsPerMeter ); + image.setDotsPerMeterY( dotsPerMeter ); + image.fill( QColor( Qt::white ).rgb() ); + + QPainter painter( &image ); + render( plot, &painter, imageRect ); + painter.end(); + + image.save( fileName, format.toLatin1() ); + } + } +} + +/*! + \brief Render the plot to a \c QPaintDevice + + This function renders the contents of a QwtPlot instance to + \c QPaintDevice object. The target rectangle is derived from + its device metrics. + + \param plot Plot to be rendered + \param paintDevice device to paint on, f.e a QImage + + \sa renderDocument(), render(), QwtPainter::setRoundingAlignment() +*/ + +void QwtPlotRenderer::renderTo( + QwtPlot *plot, QPaintDevice &paintDevice ) const +{ + int w = paintDevice.width(); + int h = paintDevice.height(); + + QPainter p( &paintDevice ); + render( plot, &p, QRectF( 0, 0, w, h ) ); +} + +/*! + \brief Render the plot to a QPrinter + + This function renders the contents of a QwtPlot instance to + \c QPaintDevice object. The size is derived from the printer + metrics. + + \param plot Plot to be rendered + \param printer Printer to paint on + + \sa renderDocument(), render(), QwtPainter::setRoundingAlignment() +*/ + +#ifndef QT_NO_PRINTER + +void QwtPlotRenderer::renderTo( + QwtPlot *plot, QPrinter &printer ) const +{ + int w = printer.width(); + int h = printer.height(); + + QRectF rect( 0, 0, w, h ); + double aspect = rect.width() / rect.height(); + if ( ( aspect < 1.0 ) ) + rect.setHeight( aspect * rect.width() ); + + QPainter p( &printer ); + render( plot, &p, rect ); +} + +#endif + +#ifndef QWT_NO_SVG +#ifdef QT_SVG_LIB +#if QT_VERSION >= 0x040500 + +/*! + \brief Render the plot to a QSvgGenerator + + If the generator has a view box, the plot will be rendered into it. + If it has no viewBox but a valid size the target coordinates + will be (0, 0, generator.width(), generator.height()). Otherwise + the target rectangle will be QRectF(0, 0, 800, 600); + + \param plot Plot to be rendered + \param generator SVG generator +*/ +void QwtPlotRenderer::renderTo( + QwtPlot *plot, QSvgGenerator &generator ) const +{ + QRectF rect = generator.viewBoxF(); + if ( rect.isEmpty() ) + rect.setRect( 0, 0, generator.width(), generator.height() ); + + if ( rect.isEmpty() ) + rect.setRect( 0, 0, 800, 600 ); // something + + QPainter p( &generator ); + render( plot, &p, rect ); +} +#endif +#endif +#endif + +/*! + Paint the contents of a QwtPlot instance into a given rectangle. + + \param plot Plot to be rendered + \param painter Painter + \param plotRect Bounding rectangle + + \sa renderDocument(), renderTo(), QwtPainter::setRoundingAlignment() +*/ +void QwtPlotRenderer::render( QwtPlot *plot, + QPainter *painter, const QRectF &plotRect ) const +{ + int axisId; + + if ( painter == 0 || !painter->isActive() || + !plotRect.isValid() || plot->size().isNull() ) + return; + + if ( !( d_data->discardFlags & DiscardBackground ) ) + qwtRenderBackground( painter, plotRect, plot ); + + /* + The layout engine uses the same methods as they are used + by the Qt layout system. Therefore we need to calculate the + layout in screen coordinates and paint with a scaled painter. + */ + QTransform transform; + transform.scale( + double( painter->device()->logicalDpiX() ) / plot->logicalDpiX(), + double( painter->device()->logicalDpiY() ) / plot->logicalDpiY() ); + + + QRectF layoutRect = transform.inverted().mapRect( plotRect ); + + if ( !( d_data->discardFlags & DiscardBackground ) ) + { + // subtract the contents margins + + int left, top, right, bottom; + plot->getContentsMargins( &left, &top, &right, &bottom ); + layoutRect.adjust( left, top, -right, -bottom ); + } + + int baseLineDists[QwtPlot::axisCnt]; + if ( d_data->layoutFlags & FrameWithScales ) + { + for ( axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) + { + QwtScaleWidget *scaleWidget = plot->axisWidget( axisId ); + if ( scaleWidget ) + { + baseLineDists[axisId] = scaleWidget->margin(); + scaleWidget->setMargin( 0 ); + } + + if ( !plot->axisEnabled( axisId ) ) + { + int left = 0; + int right = 0; + int top = 0; + int bottom = 0; + + // When we have a scale the frame is painted on + // the position of the backbone - otherwise we + // need to introduce a margin around the canvas + + switch( axisId ) + { + case QwtPlot::yLeft: + layoutRect.adjust( 1, 0, 0, 0 ); + break; + case QwtPlot::yRight: + layoutRect.adjust( 0, 0, -1, 0 ); + break; + case QwtPlot::xTop: + layoutRect.adjust( 0, 1, 0, 0 ); + break; + case QwtPlot::xBottom: + layoutRect.adjust( 0, 0, 0, -1 ); + break; + default: + break; + } + layoutRect.adjust( left, top, right, bottom ); + } + } + } + + // Calculate the layout for the document. + + QwtPlotLayout::Options layoutOptions = + QwtPlotLayout::IgnoreScrollbars | QwtPlotLayout::IgnoreFrames; + + if ( d_data->discardFlags & DiscardLegend ) + layoutOptions |= QwtPlotLayout::IgnoreLegend; + + plot->plotLayout()->activate( plot, layoutRect, layoutOptions ); + + // now start painting + + painter->save(); + painter->setWorldTransform( transform, true ); + + // canvas + + QwtScaleMap maps[QwtPlot::axisCnt]; + buildCanvasMaps( plot, plot->plotLayout()->canvasRect(), maps ); + renderCanvas( plot, painter, plot->plotLayout()->canvasRect(), maps ); + + if ( !( d_data->discardFlags & DiscardTitle ) + && ( !plot->titleLabel()->text().isEmpty() ) ) + { + renderTitle( plot, painter, plot->plotLayout()->titleRect() ); + } + + if ( !( d_data->discardFlags & DiscardLegend ) + && plot->legend() && !plot->legend()->isEmpty() ) + { + renderLegend( plot, painter, plot->plotLayout()->legendRect() ); + } + + for ( axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) + { + QwtScaleWidget *scaleWidget = plot->axisWidget( axisId ); + if ( scaleWidget ) + { + int baseDist = scaleWidget->margin(); + + int startDist, endDist; + scaleWidget->getBorderDistHint( startDist, endDist ); + + renderScale( plot, painter, axisId, startDist, endDist, + baseDist, plot->plotLayout()->scaleRect( axisId ) ); + } + } + + + plot->plotLayout()->invalidate(); + + // reset all widgets with their original attributes. + if ( d_data->layoutFlags & FrameWithScales ) + { + // restore the previous base line dists + + for ( axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) + { + QwtScaleWidget *scaleWidget = plot->axisWidget( axisId ); + if ( scaleWidget ) + scaleWidget->setMargin( baseLineDists[axisId] ); + } + } + + painter->restore(); +} + +/*! + Render the title into a given rectangle. + + \param plot Plot widget + \param painter Painter + \param rect Bounding rectangle +*/ +void QwtPlotRenderer::renderTitle( const QwtPlot *plot, + QPainter *painter, const QRectF &rect ) const +{ + painter->setFont( plot->titleLabel()->font() ); + + const QColor color = plot->titleLabel()->palette().color( + QPalette::Active, QPalette::Text ); + + painter->setPen( color ); + plot->titleLabel()->text().draw( painter, rect ); +} + +/*! + Render the legend into a given rectangle. + + \param plot Plot widget + \param painter Painter + \param rect Bounding rectangle +*/ +void QwtPlotRenderer::renderLegend( const QwtPlot *plot, + QPainter *painter, const QRectF &rect ) const +{ + if ( !plot->legend() || plot->legend()->isEmpty() ) + return; + + if ( !( d_data->discardFlags & DiscardBackground ) ) + { + if ( plot->legend()->autoFillBackground() || + plot->legend()->testAttribute( Qt::WA_StyledBackground ) ) + { + qwtRenderBackground( painter, rect, plot->legend() ); + } + } + + const QwtDynGridLayout *legendLayout = qobject_cast<QwtDynGridLayout *>( + plot->legend()->contentsWidget()->layout() ); + if ( legendLayout == NULL ) + return; + + uint numCols = legendLayout->columnsForWidth( qFloor( rect.width() ) ); + QList<QRect> itemRects = + legendLayout->layoutItems( rect.toRect(), numCols ); + + int index = 0; + + for ( int i = 0; i < legendLayout->count(); i++ ) + { + QLayoutItem *item = legendLayout->itemAt( i ); + QWidget *w = item->widget(); + if ( w ) + { + painter->save(); + + painter->setClipRect( itemRects[index] ); + renderLegendItem( plot, painter, w, itemRects[index] ); + + index++; + painter->restore(); + } + } +} + +/*! + Render the legend item into a given rectangle. + + \param plot Plot widget + \param painter Painter + \param widget Widget representing a legend item + \param rect Bounding rectangle + + \note When widget is not derived from QwtLegendItem renderLegendItem + does nothing and needs to be overloaded +*/ +void QwtPlotRenderer::renderLegendItem( const QwtPlot *plot, + QPainter *painter, const QWidget *widget, const QRectF &rect ) const +{ + if ( !( d_data->discardFlags & DiscardBackground ) ) + { + if ( widget->autoFillBackground() || + widget->testAttribute( Qt::WA_StyledBackground ) ) + { + qwtRenderBackground( painter, rect, widget ); + } + } + + const QwtLegendItem *item = qobject_cast<const QwtLegendItem *>( widget ); + if ( item ) + { + const QSize sz = item->identifierSize(); + + const QRectF identifierRect( rect.x() + item->margin(), + rect.center().y() - 0.5 * sz.height(), sz.width(), sz.height() ); + + QwtLegendItemManager *itemManger = plot->legend()->find( item ); + if ( itemManger ) + { + painter->save(); + painter->setClipRect( identifierRect, Qt::IntersectClip ); + itemManger->drawLegendIdentifier( painter, identifierRect ); + painter->restore(); + } + + // Label + + QRectF titleRect = rect; + titleRect.setX( identifierRect.right() + 2 * item->spacing() ); + + painter->setFont( item->font() ); + item->text().draw( painter, titleRect ); + } +} + +/*! + \brief Paint a scale into a given rectangle. + Paint the scale into a given rectangle. + + \param plot Plot widget + \param painter Painter + \param axisId Axis + \param startDist Start border distance + \param endDist End border distance + \param baseDist Base distance + \param rect Bounding rectangle +*/ +void QwtPlotRenderer::renderScale( const QwtPlot *plot, + QPainter *painter, + int axisId, int startDist, int endDist, int baseDist, + const QRectF &rect ) const +{ + if ( !plot->axisEnabled( axisId ) ) + return; + + const QwtScaleWidget *scaleWidget = plot->axisWidget( axisId ); + if ( scaleWidget->isColorBarEnabled() + && scaleWidget->colorBarWidth() > 0 ) + { + scaleWidget->drawColorBar( painter, scaleWidget->colorBarRect( rect ) ); + + const int off = scaleWidget->colorBarWidth() + scaleWidget->spacing(); + if ( scaleWidget->scaleDraw()->orientation() == Qt::Horizontal ) + baseDist += off; + else + baseDist += off; + } + + painter->save(); + + QwtScaleDraw::Alignment align; + double x, y, w; + + switch ( axisId ) + { + case QwtPlot::yLeft: + { + x = rect.right() - 1.0 - baseDist; + y = rect.y() + startDist; + w = rect.height() - startDist - endDist; + align = QwtScaleDraw::LeftScale; + break; + } + case QwtPlot::yRight: + { + x = rect.left() + baseDist; + y = rect.y() + startDist; + w = rect.height() - startDist - endDist; + align = QwtScaleDraw::RightScale; + break; + } + case QwtPlot::xTop: + { + x = rect.left() + startDist; + y = rect.bottom() - 1.0 - baseDist; + w = rect.width() - startDist - endDist; + align = QwtScaleDraw::TopScale; + break; + } + case QwtPlot::xBottom: + { + x = rect.left() + startDist; + y = rect.top() + baseDist; + w = rect.width() - startDist - endDist; + align = QwtScaleDraw::BottomScale; + break; + } + default: + return; + } + + scaleWidget->drawTitle( painter, align, rect ); + + painter->setFont( scaleWidget->font() ); + + QwtScaleDraw *sd = const_cast<QwtScaleDraw *>( scaleWidget->scaleDraw() ); + const QPointF sdPos = sd->pos(); + const double sdLength = sd->length(); + + sd->move( x, y ); + sd->setLength( w ); + + QPalette palette = scaleWidget->palette(); + palette.setCurrentColorGroup( QPalette::Active ); + sd->draw( painter, palette ); + + // reset previous values + sd->move( sdPos ); + sd->setLength( sdLength ); + + painter->restore(); +} + +/*! + Render the canvas into a given rectangle. + + \param plot Plot widget + \param painter Painter + \param map Maps mapping between plot and paint device coordinates + \param canvasRect Canvas rectangle +*/ +void QwtPlotRenderer::renderCanvas( const QwtPlot *plot, + QPainter *painter, const QRectF &canvasRect, + const QwtScaleMap *map ) const +{ + painter->save(); + + QPainterPath clipPath; + + QRectF r = canvasRect.adjusted( 0.0, 0.0, -1.0, -1.0 ); + + if ( d_data->layoutFlags & FrameWithScales ) + { + r.adjust( -1.0, -1.0, 1.0, 1.0 ); + painter->setPen( QPen( Qt::black ) ); + + if ( !( d_data->discardFlags & DiscardCanvasBackground ) ) + { + const QBrush bgBrush = + plot->canvas()->palette().brush( plot->backgroundRole() ); + painter->setBrush( bgBrush ); + } + + QwtPainter::drawRect( painter, r ); + } + else + { + if ( !( d_data->discardFlags & DiscardCanvasBackground ) ) + { + qwtRenderBackground( painter, r, plot->canvas() ); + + if ( plot->canvas()->testAttribute( Qt::WA_StyledBackground ) ) + { + // The clip region is calculated in integers + // To avoid too much rounding errors better + // calculate it in target device resolution + // TODO ... + + int x1 = qCeil( canvasRect.left() ); + int x2 = qFloor( canvasRect.right() ); + int y1 = qCeil( canvasRect.top() ); + int y2 = qFloor( canvasRect.bottom() ); + + clipPath = plot->canvas()->borderPath( + QRect( x1, y1, x2 - x1 - 1, y2 - y1 - 1 ) ); + } + } + } + + painter->restore(); + + painter->save(); + + if ( clipPath.isEmpty() ) + painter->setClipRect( canvasRect ); + else + painter->setClipPath( clipPath ); + + plot->drawItems( painter, canvasRect, map ); + + painter->restore(); +} + +/*! + Calculated the scale maps for rendering the canvas + + \param plot Plot widget + \param canvasRect Target rectangle + \param maps Scale maps to be calculated +*/ +void QwtPlotRenderer::buildCanvasMaps( const QwtPlot *plot, + const QRectF &canvasRect, QwtScaleMap maps[] ) const +{ + for ( int axisId = 0; axisId < QwtPlot::axisCnt; axisId++ ) + { + maps[axisId].setTransformation( + plot->axisScaleEngine( axisId )->transformation() ); + + const QwtScaleDiv &scaleDiv = *plot->axisScaleDiv( axisId ); + maps[axisId].setScaleInterval( + scaleDiv.lowerBound(), scaleDiv.upperBound() ); + + double from, to; + if ( plot->axisEnabled( axisId ) ) + { + const int sDist = plot->axisWidget( axisId )->startBorderDist(); + const int eDist = plot->axisWidget( axisId )->endBorderDist(); + const QRectF &scaleRect = plot->plotLayout()->scaleRect( axisId ); + + if ( axisId == QwtPlot::xTop || axisId == QwtPlot::xBottom ) + { + from = scaleRect.left() + sDist; + to = scaleRect.right() - eDist; + } + else + { + from = scaleRect.bottom() - eDist; + to = scaleRect.top() + sDist; + } + } + else + { + int margin = 0; + if ( !plot->plotLayout()->alignCanvasToScales() ) + margin = plot->plotLayout()->canvasMargin( axisId ); + + if ( axisId == QwtPlot::yLeft || axisId == QwtPlot::yRight ) + { + from = canvasRect.bottom() - margin; + to = canvasRect.top() + margin; + } + else + { + from = canvasRect.left() + margin; + to = canvasRect.right() - margin; + } + } + maps[axisId].setPaintInterval( from, to ); + } +} diff --git a/src/libpcp_qwt/src/qwt_plot_renderer.h b/src/libpcp_qwt/src/qwt_plot_renderer.h new file mode 100644 index 0000000..f79146a --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_renderer.h @@ -0,0 +1,154 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_RENDERER_H +#define QWT_PLOT_RENDERER_H + +#include "qwt_global.h" +#include <qobject.h> + +class QwtPlot; +class QwtScaleMap; +class QSizeF; +class QRectF; +class QPainter; +class QPaintDevice; + +#ifndef QT_NO_PRINTER +class QPrinter; +#endif + +#ifndef QWT_NO_SVG +#ifdef QT_SVG_LIB +class QSvgGenerator; +#endif +#endif + +/*! + \brief Renderer for exporting a plot to a document, a printer + or anything else, that is supported by QPainter/QPaintDevice +*/ +class QWT_EXPORT QwtPlotRenderer: public QObject +{ + Q_OBJECT + +public: + //! Disard flags + enum DiscardFlag + { + //! Render all components of the plot + DiscardNone = 0x00, + + //! Don't render the background of the plot + DiscardBackground = 0x01, + + //! Don't render the title of the plot + DiscardTitle = 0x02, + + //! Don't render the legend of the plot + DiscardLegend = 0x04, + + //! Don't render the background of the canvas + DiscardCanvasBackground = 0x08 + }; + + //! Disard flags + typedef QFlags<DiscardFlag> DiscardFlags; + + /*! + \brief Layout flags + \sa setLayoutFlag(), testLayoutFlag() + */ + enum LayoutFlag + { + //! Use the default layout without margins and frames + DefaultLayout = 0x00, + + //! Render all frames of the plot + KeepFrames = 0x01, + + /*! + Instead of the scales a box is painted around the plot canvas, + where the scale ticks are aligned to. + */ + FrameWithScales = 0x02 + }; + + //! Layout flags + typedef QFlags<LayoutFlag> LayoutFlags; + + explicit QwtPlotRenderer( QObject * = NULL ); + virtual ~QwtPlotRenderer(); + + void setDiscardFlag( DiscardFlag flag, bool on = true ); + bool testDiscardFlag( DiscardFlag flag ) const; + + void setDiscardFlags( DiscardFlags flags ); + DiscardFlags discardFlags() const; + + void setLayoutFlag( LayoutFlag flag, bool on = true ); + bool testLayoutFlag( LayoutFlag flag ) const; + + void setLayoutFlags( LayoutFlags flags ); + LayoutFlags layoutFlags() const; + + void renderDocument( QwtPlot *, const QString &fileName, + const QSizeF &sizeMM, int resolution = 85 ); + + void renderDocument( QwtPlot *, + const QString &fileName, const QString &format, + const QSizeF &sizeMM, int resolution = 85 ); + +#ifndef QWT_NO_SVG +#ifdef QT_SVG_LIB +#if QT_VERSION >= 0x040500 + void renderTo( QwtPlot *, QSvgGenerator & ) const; +#endif +#endif +#endif + +#ifndef QT_NO_PRINTER + void renderTo( QwtPlot *, QPrinter & ) const; +#endif + + void renderTo( QwtPlot *, QPaintDevice &p ) const; + + virtual void render( QwtPlot *, + QPainter *, const QRectF &rect ) const; + + virtual void renderLegendItem( const QwtPlot *, + QPainter *, const QWidget *, const QRectF & ) const; + + virtual void renderTitle( const QwtPlot *, + QPainter *, const QRectF & ) const; + + virtual void renderScale( const QwtPlot *, QPainter *, + int axisId, int startDist, int endDist, + int baseDist, const QRectF & ) const; + + virtual void renderCanvas( const QwtPlot *, + QPainter *, const QRectF &canvasRect, + const QwtScaleMap* maps ) const; + + virtual void renderLegend( + const QwtPlot *, QPainter *, const QRectF & ) const; + +protected: + void buildCanvasMaps( const QwtPlot *, + const QRectF &, QwtScaleMap maps[] ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRenderer::DiscardFlags ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotRenderer::LayoutFlags ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_rescaler.cpp b/src/libpcp_qwt/src/qwt_plot_rescaler.cpp new file mode 100644 index 0000000..69c0f4f --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_rescaler.cpp @@ -0,0 +1,628 @@ +/* -*- 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_plot_rescaler.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_scale_div.h" +#include "qwt_interval.h" +#include <qevent.h> +#include <qalgorithms.h> + +class QwtPlotRescaler::AxisData +{ +public: + AxisData(): + aspectRatio( 1.0 ), + expandingDirection( QwtPlotRescaler::ExpandUp ) + { + } + + double aspectRatio; + QwtInterval intervalHint; + QwtPlotRescaler::ExpandingDirection expandingDirection; + mutable QwtScaleDiv scaleDiv; +}; + +class QwtPlotRescaler::PrivateData +{ +public: + PrivateData(): + referenceAxis( QwtPlot::xBottom ), + rescalePolicy( QwtPlotRescaler::Expanding ), + isEnabled( false ), + inReplot( 0 ) + { + } + + int referenceAxis; + RescalePolicy rescalePolicy; + QwtPlotRescaler::AxisData axisData[QwtPlot::axisCnt]; + bool isEnabled; + + mutable int inReplot; +}; + +/*! + Constructor + + \param canvas Canvas + \param referenceAxis Reference axis, see RescalePolicy + \param policy Rescale policy + + \sa setRescalePolicy(), setReferenceAxis() +*/ +QwtPlotRescaler::QwtPlotRescaler( QwtPlotCanvas *canvas, + int referenceAxis, RescalePolicy policy ): + QObject( canvas ) +{ + d_data = new PrivateData; + d_data->referenceAxis = referenceAxis; + d_data->rescalePolicy = policy; + + setEnabled( true ); +} + +//! Destructor +QwtPlotRescaler::~QwtPlotRescaler() +{ + delete d_data; +} + +/*! + \brief En/disable the rescaler + + When enabled is true an event filter is installed for + the canvas, otherwise the event filter is removed. + + \param on true or false + \sa isEnabled(), eventFilter() +*/ +void QwtPlotRescaler::setEnabled( bool on ) +{ + if ( d_data->isEnabled != on ) + { + d_data->isEnabled = on; + + QWidget *w = canvas(); + if ( w ) + { + if ( d_data->isEnabled ) + w->installEventFilter( this ); + else + w->removeEventFilter( this ); + } + } +} + +/*! + \return true when enabled, false otherwise + \sa setEnabled, eventFilter() +*/ +bool QwtPlotRescaler::isEnabled() const +{ + return d_data->isEnabled; +} + +/*! + Change the rescale policy + + \param policy Rescale policy + \sa rescalePolicy() +*/ +void QwtPlotRescaler::setRescalePolicy( RescalePolicy policy ) +{ + d_data->rescalePolicy = policy; +} + +/*! + \return Rescale policy + \sa setRescalePolicy() +*/ +QwtPlotRescaler::RescalePolicy QwtPlotRescaler::rescalePolicy() const +{ + return d_data->rescalePolicy; +} + +/*! + Set the reference axis ( see RescalePolicy ) + + \param axis Axis index ( QwtPlot::Axis ) + \sa referenceAxis() +*/ +void QwtPlotRescaler::setReferenceAxis( int axis ) +{ + d_data->referenceAxis = axis; +} + +/*! + \return Reference axis ( see RescalePolicy ) + \sa setReferenceAxis() +*/ +int QwtPlotRescaler::referenceAxis() const +{ + return d_data->referenceAxis; +} + +/*! + Set the direction in which all axis should be expanded + + \param direction Direction + \sa expandingDirection() +*/ +void QwtPlotRescaler::setExpandingDirection( + ExpandingDirection direction ) +{ + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + setExpandingDirection( axis, direction ); +} + +/*! + Set the direction in which an axis should be expanded + + \param axis Axis index ( see QwtPlot::AxisId ) + \param direction Direction + \sa expandingDirection() +*/ +void QwtPlotRescaler::setExpandingDirection( + int axis, ExpandingDirection direction ) +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->axisData[axis].expandingDirection = direction; +} + +/*! + Return direction in which an axis should be expanded + + \param axis Axis index ( see QwtPlot::AxisId ) + \sa setExpandingDirection() +*/ +QwtPlotRescaler::ExpandingDirection +QwtPlotRescaler::expandingDirection( int axis ) const +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + return d_data->axisData[axis].expandingDirection; + + return ExpandBoth; +} + +/*! + Set the aspect ratio between the scale of the reference axis + and the other scales. The default ratio is 1.0 + + \param ratio Aspect ratio + \sa aspectRatio() +*/ +void QwtPlotRescaler::setAspectRatio( double ratio ) +{ + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + setAspectRatio( axis, ratio ); +} + +/*! + Set the aspect ratio between the scale of the reference axis + and another scale. The default ratio is 1.0 + + \param axis Axis index ( see QwtPlot::AxisId ) + \param ratio Aspect ratio + \sa aspectRatio() +*/ +void QwtPlotRescaler::setAspectRatio( int axis, double ratio ) +{ + if ( ratio < 0.0 ) + ratio = 0.0; + + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->axisData[axis].aspectRatio = ratio; +} + +/*! + Return aspect ratio between an axis and the reference axis. + + \param axis Axis index ( see QwtPlot::AxisId ) + \sa setAspectRatio() +*/ +double QwtPlotRescaler::aspectRatio( int axis ) const +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + return d_data->axisData[axis].aspectRatio; + + return 0.0; +} + +/*! + Set an interval hint for an axis + + In Fitting mode, the hint is used as minimal interval + taht always needs to be displayed. + + \param axis Axis, see QwtPlot::Axis + \param interval Axis + \sa intervalHint(), RescalePolicy +*/ +void QwtPlotRescaler::setIntervalHint( int axis, + const QwtInterval &interval ) +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + d_data->axisData[axis].intervalHint = interval; +} + +/*! + \param axis Axis, see QwtPlot::Axis + \return Interval hint + \sa setIntervalHint(), RescalePolicy +*/ +QwtInterval QwtPlotRescaler::intervalHint( int axis ) const +{ + if ( axis >= 0 && axis < QwtPlot::axisCnt ) + return d_data->axisData[axis].intervalHint; + + return QwtInterval(); +} + +//! \return plot canvas +QwtPlotCanvas *QwtPlotRescaler::canvas() +{ + return qobject_cast<QwtPlotCanvas *>( parent() ); +} + +//! \return plot canvas +const QwtPlotCanvas *QwtPlotRescaler::canvas() const +{ + return qobject_cast<const QwtPlotCanvas *>( parent() ); +} + +//! \return plot widget +QwtPlot *QwtPlotRescaler::plot() +{ + QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +//! \return plot widget +const QwtPlot *QwtPlotRescaler::plot() const +{ + const QwtPlotCanvas *w = canvas(); + if ( w ) + return w->plot(); + + return NULL; +} + +//! Event filter for the plot canvas +bool QwtPlotRescaler::eventFilter( QObject *object, QEvent *event ) +{ + if ( object && object == canvas() ) + { + switch ( event->type() ) + { + case QEvent::Resize: + { + canvasResizeEvent( ( QResizeEvent * )event ); + break; + } + case QEvent::PolishRequest: + { + rescale(); + break; + } + default:; + } + } + + return false; +} + +/*! + Event handler for resize events of the plot canvas + + \param event Resize event + \sa rescale() +*/ +void QwtPlotRescaler::canvasResizeEvent( QResizeEvent* event ) +{ + const int fw = 2 * canvas()->frameWidth(); + const QSize newSize = event->size() - QSize( fw, fw ); + const QSize oldSize = event->oldSize() - QSize( fw, fw ); + + rescale( oldSize, newSize ); +} + +//! Adjust the plot axes scales +void QwtPlotRescaler::rescale() const +{ + const QSize size = canvas()->contentsRect().size(); + rescale( size, size ); +} + +/*! + Adjust the plot axes scales + + \param oldSize Previous size of the canvas + \param newSize New size of the canvas +*/ +void QwtPlotRescaler::rescale( + const QSize &oldSize, const QSize &newSize ) const +{ + if ( newSize.isEmpty() ) + return; + + QwtInterval intervals[QwtPlot::axisCnt]; + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + intervals[axis] = interval( axis ); + + const int refAxis = referenceAxis(); + intervals[refAxis] = expandScale( refAxis, oldSize, newSize ); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( aspectRatio( axis ) > 0.0 && axis != refAxis ) + intervals[axis] = syncScale( axis, intervals[refAxis], newSize ); + } + + updateScales( intervals ); +} + +/*! + Calculate the new scale interval of a plot axis + + \param axis Axis index ( see QwtPlot::AxisId ) + \param oldSize Previous size of the canvas + \param newSize New size of the canvas + + \return Calculated new interval for the axis +*/ +QwtInterval QwtPlotRescaler::expandScale( int axis, + const QSize &oldSize, const QSize &newSize ) const +{ + const QwtInterval oldInterval = interval( axis ); + + QwtInterval expanded = oldInterval; + switch ( rescalePolicy() ) + { + case Fixed: + { + break; // do nothing + } + case Expanding: + { + if ( !oldSize.isEmpty() ) + { + double width = oldInterval.width(); + if ( orientation( axis ) == Qt::Horizontal ) + width *= double( newSize.width() ) / oldSize.width(); + else + width *= double( newSize.height() ) / oldSize.height(); + + expanded = expandInterval( oldInterval, + width, expandingDirection( axis ) ); + } + break; + } + case Fitting: + { + double dist = 0.0; + for ( int ax = 0; ax < QwtPlot::axisCnt; ax++ ) + { + const double d = pixelDist( ax, newSize ); + if ( d > dist ) + dist = d; + } + if ( dist > 0.0 ) + { + double width; + if ( orientation( axis ) == Qt::Horizontal ) + width = newSize.width() * dist; + else + width = newSize.height() * dist; + + expanded = expandInterval( intervalHint( axis ), + width, expandingDirection( axis ) ); + } + break; + } + } + + return expanded; +} + +/*! + Synchronize an axis scale according to the scale of the reference axis + + \param axis Axis index ( see QwtPlot::AxisId ) + \param reference Interval of the reference axis + \param size Size of the canvas +*/ +QwtInterval QwtPlotRescaler::syncScale( int axis, + const QwtInterval& reference, const QSize &size ) const +{ + double dist; + if ( orientation( referenceAxis() ) == Qt::Horizontal ) + dist = reference.width() / size.width(); + else + dist = reference.width() / size.height(); + + if ( orientation( axis ) == Qt::Horizontal ) + dist *= size.width(); + else + dist *= size.height(); + + dist /= aspectRatio( axis ); + + QwtInterval intv; + if ( rescalePolicy() == Fitting ) + intv = intervalHint( axis ); + else + intv = interval( axis ); + + intv = expandInterval( intv, dist, expandingDirection( axis ) ); + + return intv; +} + +/*! + Return orientation of an axis + \param axis Axis index ( see QwtPlot::AxisId ) +*/ +Qt::Orientation QwtPlotRescaler::orientation( int axis ) const +{ + if ( axis == QwtPlot::yLeft || axis == QwtPlot::yRight ) + return Qt::Vertical; + + return Qt::Horizontal; +} + +/*! + Return interval of an axis + \param axis Axis index ( see QwtPlot::AxisId ) +*/ +QwtInterval QwtPlotRescaler::interval( int axis ) const +{ + if ( axis < 0 || axis >= QwtPlot::axisCnt ) + return QwtInterval(); + + const QwtPlot *plt = plot(); + + const double v1 = plt->axisScaleDiv( axis )->lowerBound(); + const double v2 = plt->axisScaleDiv( axis )->upperBound(); + + return QwtInterval( v1, v2 ).normalized(); +} + +/*! + Expand the interval + + \param interval Interval to be expanded + \param width Distance to be added to the interval + \param direction Direction of the expand operation + + \return Expanded interval +*/ +QwtInterval QwtPlotRescaler::expandInterval( + const QwtInterval &interval, double width, + ExpandingDirection direction ) const +{ + QwtInterval expanded = interval; + + switch ( direction ) + { + case ExpandUp: + expanded.setMinValue( interval.minValue() ); + expanded.setMaxValue( interval.minValue() + width ); + break; + + case ExpandDown: + expanded.setMaxValue( interval.maxValue() ); + expanded.setMinValue( interval.maxValue() - width ); + break; + + case ExpandBoth: + default: + expanded.setMinValue( interval.minValue() + + interval.width() / 2.0 - width / 2.0 ); + expanded.setMaxValue( expanded.minValue() + width ); + } + return expanded; +} + +double QwtPlotRescaler::pixelDist( int axis, const QSize &size ) const +{ + const QwtInterval intv = intervalHint( axis ); + + double dist = 0.0; + if ( !intv.isNull() ) + { + if ( axis == referenceAxis() ) + dist = intv.width(); + else + { + const double r = aspectRatio( axis ); + if ( r > 0.0 ) + dist = intv.width() * r; + } + } + + if ( dist > 0.0 ) + { + if ( orientation( axis ) == Qt::Horizontal ) + dist /= size.width(); + else + dist /= size.height(); + } + + return dist; +} + +/*! + Update the axes scales + + \param intervals Scale intervals +*/ +void QwtPlotRescaler::updateScales( + QwtInterval intervals[QwtPlot::axisCnt] ) const +{ + if ( d_data->inReplot >= 5 ) + { + return; + } + + QwtPlot *plt = const_cast<QwtPlot *>( plot() ); + + const bool doReplot = plt->autoReplot(); + plt->setAutoReplot( false ); + + for ( int axis = 0; axis < QwtPlot::axisCnt; axis++ ) + { + if ( axis == referenceAxis() || aspectRatio( axis ) > 0.0 ) + { + double v1 = intervals[axis].minValue(); + double v2 = intervals[axis].maxValue(); + + if ( plt->axisScaleDiv( axis )->lowerBound() > + plt->axisScaleDiv( axis )->upperBound() ) + { + qSwap( v1, v2 ); + } + + if ( d_data->inReplot >= 1 ) + { + d_data->axisData[axis].scaleDiv = *plt->axisScaleDiv( axis ); + } + + if ( d_data->inReplot >= 2 ) + { + QList<double> ticks[QwtScaleDiv::NTickTypes]; + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + ticks[i] = d_data->axisData[axis].scaleDiv.ticks( i ); + + plt->setAxisScaleDiv( axis, QwtScaleDiv( v1, v2, ticks ) ); + } + else + { + plt->setAxisScale( axis, v1, v2 ); + } + } + } + + const bool immediatePaint = + plt->canvas()->testPaintAttribute( QwtPlotCanvas::ImmediatePaint ); + plt->canvas()->setPaintAttribute( QwtPlotCanvas::ImmediatePaint, false ); + + plt->setAutoReplot( doReplot ); + + d_data->inReplot++; + plt->replot(); + d_data->inReplot--; + + plt->canvas()->setPaintAttribute( + QwtPlotCanvas::ImmediatePaint, immediatePaint ); +} diff --git a/src/libpcp_qwt/src/qwt_plot_rescaler.h b/src/libpcp_qwt/src/qwt_plot_rescaler.h new file mode 100644 index 0000000..40cf3f9 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_rescaler.h @@ -0,0 +1,143 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_RESCALER_H +#define QWT_PLOT_RESCALER_H 1 + +#include "qwt_global.h" +#include "qwt_interval.h" +#include "qwt_plot.h" +#include <qobject.h> + +class QwtPlotCanvas; +class QwtPlot; +class QResizeEvent; + +/*! + \brief QwtPlotRescaler takes care of fixed aspect ratios for plot scales + + QwtPlotRescaler autoadjusts the axes of a QwtPlot according + to fixed aspect ratios. +*/ + +class QWT_EXPORT QwtPlotRescaler: public QObject +{ +public: + /*! + The rescale policy defines how to rescale the reference axis and + their depending axes. + + \sa ExpandingDirection, setIntervalHint() + */ + enum RescalePolicy + { + /*! + The interval of the reference axis remains unchanged, when the + geometry of the canvas changes. All other axes + will be adjusted according to their aspect ratio. + */ + Fixed, + + /*! + The interval of the reference axis will be shrinked/expanded, + when the geometry of the canvas changes. All other axes + will be adjusted according to their aspect ratio. + + The interval, that is represented by one pixel is fixed. + + */ + Expanding, + + /*! + The intervals of the axes are calculated, so that all axes include + their interval hint. + */ + Fitting + }; + + /*! + When rescalePolicy() is set to Expanding its direction depends + on ExpandingDirection + */ + enum ExpandingDirection + { + //! The upper limit of the scale is adjusted + ExpandUp, + + //! The lower limit of the scale is adjusted + ExpandDown, + + //! Both limits of the scale are adjusted + ExpandBoth + }; + + explicit QwtPlotRescaler( QwtPlotCanvas *, + int referenceAxis = QwtPlot::xBottom, + RescalePolicy = Expanding ); + + virtual ~QwtPlotRescaler(); + + void setEnabled( bool ); + bool isEnabled() const; + + void setRescalePolicy( RescalePolicy ); + RescalePolicy rescalePolicy() const; + + void setExpandingDirection( ExpandingDirection ); + void setExpandingDirection( int axis, ExpandingDirection ); + ExpandingDirection expandingDirection( int axis ) const; + + void setReferenceAxis( int axis ); + int referenceAxis() const; + + void setAspectRatio( double ratio ); + void setAspectRatio( int axis, double ratio ); + double aspectRatio( int axis ) const; + + void setIntervalHint( int axis, const QwtInterval& ); + QwtInterval intervalHint( int axis ) const; + + QwtPlotCanvas *canvas(); + const QwtPlotCanvas *canvas() const; + + QwtPlot *plot(); + const QwtPlot *plot() const; + + virtual bool eventFilter( QObject *, QEvent * ); + + void rescale() const; + +protected: + virtual void canvasResizeEvent( QResizeEvent * ); + + virtual void rescale( const QSize &oldSize, const QSize &newSize ) const; + virtual QwtInterval expandScale( + int axis, const QSize &oldSize, const QSize &newSize ) const; + + virtual QwtInterval syncScale( + int axis, const QwtInterval& reference, + const QSize &size ) const; + + virtual void updateScales( + QwtInterval intervals[QwtPlot::axisCnt] ) const; + + Qt::Orientation orientation( int axis ) const; + QwtInterval interval( int axis ) const; + QwtInterval expandInterval( const QwtInterval &, + double width, ExpandingDirection ) const; + +private: + double pixelDist( int axis, const QSize & ) const; + + class AxisData; + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_scaleitem.cpp b/src/libpcp_qwt/src/qwt_plot_scaleitem.cpp new file mode 100644 index 0000000..212f5be --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_scaleitem.cpp @@ -0,0 +1,445 @@ +/* -*- 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_plot_scaleitem.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_scale_map.h" +#include "qwt_interval.h" +#include <qpalette.h> +#include <qpainter.h> + +class QwtPlotScaleItem::PrivateData +{ +public: + PrivateData(): + position( 0.0 ), + borderDistance( -1 ), + scaleDivFromAxis( true ), + scaleDraw( new QwtScaleDraw() ) + { + } + + ~PrivateData() + { + delete scaleDraw; + } + + void updateBorders( const QRectF &, + const QwtScaleMap &, const QwtScaleMap & ); + + QPalette palette; + QFont font; + double position; + int borderDistance; + bool scaleDivFromAxis; + QwtScaleDraw *scaleDraw; + QRectF canvasRectCache; +}; + +void QwtPlotScaleItem::PrivateData::updateBorders( const QRectF &canvasRect, + const QwtScaleMap &xMap, const QwtScaleMap &yMap ) +{ + QwtInterval interval; + if ( scaleDraw->orientation() == Qt::Horizontal ) + { + interval.setMinValue( xMap.invTransform( canvasRect.left() ) ); + interval.setMaxValue( xMap.invTransform( canvasRect.right() - 1 ) ); + } + else + { + interval.setMinValue( yMap.invTransform( canvasRect.bottom() - 1 ) ); + interval.setMaxValue( yMap.invTransform( canvasRect.top() ) ); + } + + QwtScaleDiv scaleDiv = scaleDraw->scaleDiv(); + scaleDiv.setInterval( interval ); + scaleDraw->setScaleDiv( scaleDiv ); +} +/*! + \brief Constructor for scale item at the position pos. + + \param alignment In case of QwtScaleDraw::BottomScale or QwtScaleDraw::TopScale + the scale item is corresponding to the xAxis(), + otherwise it corresponds to the yAxis(). + + \param pos x or y position, depending on the corresponding axis. + + \sa setPosition(), setAlignment() +*/ +QwtPlotScaleItem::QwtPlotScaleItem( + QwtScaleDraw::Alignment alignment, const double pos ): + QwtPlotItem( QwtText( "Scale" ) ) +{ + d_data = new PrivateData; + d_data->position = pos; + d_data->scaleDraw->setAlignment( alignment ); + + setZ( 11.0 ); +} + +//! Destructor +QwtPlotScaleItem::~QwtPlotScaleItem() +{ + delete d_data; +} + +//! \return QwtPlotItem::Rtti_PlotScale +int QwtPlotScaleItem::rtti() const +{ + return QwtPlotItem::Rtti_PlotScale; +} + +/*! + \brief Assign a scale division + + When assigning a scaleDiv the scale division won't be synchronized + with the corresponding axis anymore. + + \param scaleDiv Scale division + \sa scaleDiv(), setScaleDivFromAxis(), isScaleDivFromAxis() +*/ +void QwtPlotScaleItem::setScaleDiv( const QwtScaleDiv& scaleDiv ) +{ + d_data->scaleDivFromAxis = false; + d_data->scaleDraw->setScaleDiv( scaleDiv ); +} + +//! \return Scale division +const QwtScaleDiv& QwtPlotScaleItem::scaleDiv() const +{ + return d_data->scaleDraw->scaleDiv(); +} + +/*! + Enable/Disable the synchronization of the scale division with + the corresponding axis. + + \param on true/false + \sa isScaleDivFromAxis() +*/ +void QwtPlotScaleItem::setScaleDivFromAxis( bool on ) +{ + if ( on != d_data->scaleDivFromAxis ) + { + d_data->scaleDivFromAxis = on; + if ( on ) + { + const QwtPlot *plt = plot(); + if ( plt ) + { + updateScaleDiv( *plt->axisScaleDiv( xAxis() ), + *plt->axisScaleDiv( yAxis() ) ); + itemChanged(); + } + } + } +} + +/*! + \return True, if the synchronization of the scale division with + the corresponding axis is enabled. + \sa setScaleDiv(), setScaleDivFromAxis() +*/ +bool QwtPlotScaleItem::isScaleDivFromAxis() const +{ + return d_data->scaleDivFromAxis; +} + +/*! + Set the palette + \sa QwtAbstractScaleDraw::draw(), palette() +*/ +void QwtPlotScaleItem::setPalette( const QPalette &palette ) +{ + if ( palette != d_data->palette ) + { + d_data->palette = palette; + itemChanged(); + } +} + +/*! + \return palette + \sa setPalette() +*/ +QPalette QwtPlotScaleItem::palette() const +{ + return d_data->palette; +} + +/*! + Change the tick label font + \sa font() +*/ +void QwtPlotScaleItem::setFont( const QFont &font ) +{ + if ( font != d_data->font ) + { + d_data->font = font; + itemChanged(); + } +} + +/*! + \return tick label font + \sa setFont() +*/ +QFont QwtPlotScaleItem::font() const +{ + return d_data->font; +} + +/*! + \brief Set a scale draw + + \param scaleDraw object responsible for drawing scales. + + The main use case for replacing the default QwtScaleDraw is + to overload QwtAbstractScaleDraw::label, to replace or swallow + tick labels. + + \sa scaleDraw() +*/ +void QwtPlotScaleItem::setScaleDraw( QwtScaleDraw *scaleDraw ) +{ + if ( scaleDraw == NULL ) + return; + + if ( scaleDraw != d_data->scaleDraw ) + delete d_data->scaleDraw; + + d_data->scaleDraw = scaleDraw; + + const QwtPlot *plt = plot(); + if ( plt ) + { + updateScaleDiv( *plt->axisScaleDiv( xAxis() ), + *plt->axisScaleDiv( yAxis() ) ); + } + + itemChanged(); +} + +/*! + \return Scale draw + \sa setScaleDraw() +*/ +const QwtScaleDraw *QwtPlotScaleItem::scaleDraw() const +{ + return d_data->scaleDraw; +} + +/*! + \return Scale draw + \sa setScaleDraw() +*/ +QwtScaleDraw *QwtPlotScaleItem::scaleDraw() +{ + return d_data->scaleDraw; +} + +/*! + Change the position of the scale + + The position is interpreted as y value for horizontal axes + and as x value for vertical axes. + + The border distance is set to -1. + + \param pos New position + \sa position(), setAlignment() +*/ +void QwtPlotScaleItem::setPosition( double pos ) +{ + if ( d_data->position != pos ) + { + d_data->position = pos; + d_data->borderDistance = -1; + itemChanged(); + } +} + +/*! + \return Position of the scale + \sa setPosition(), setAlignment() +*/ +double QwtPlotScaleItem::position() const +{ + return d_data->position; +} + +/*! + \brief Align the scale to the canvas + + If distance is >= 0 the scale will be aligned to a + border of the contents rect of the canvas. If + alignment() is QwtScaleDraw::LeftScale, the scale will + be aligned to the right border, if it is QwtScaleDraw::TopScale + it will be aligned to the bottom (and vice versa), + + If distance is < 0 the scale will be at the position(). + + \param distance Number of pixels between the canvas border and the + backbone of the scale. + + \sa setPosition(), borderDistance() +*/ +void QwtPlotScaleItem::setBorderDistance( int distance ) +{ + if ( distance < 0 ) + distance = -1; + + if ( distance != d_data->borderDistance ) + { + d_data->borderDistance = distance; + itemChanged(); + } +} + +/*! + \return Distance from a canvas border + \sa setBorderDistance(), setPosition() +*/ +int QwtPlotScaleItem::borderDistance() const +{ + return d_data->borderDistance; +} + +/*! + Change the alignment of the scale + + The alignment sets the orientation of the scale and the position of + the ticks: + + - QwtScaleDraw::BottomScale: horizontal, ticks below + - QwtScaleDraw::TopScale: horizontal, ticks above + - QwtScaleDraw::LeftScale: vertical, ticks left + - QwtScaleDraw::RightScale: vertical, ticks right + + For horizontal scales the position corresponds to QwtPlotItem::yAxis(), + otherwise to QwtPlotItem::xAxis(). + + \sa scaleDraw(), QwtScaleDraw::alignment(), setPosition() +*/ +void QwtPlotScaleItem::setAlignment( QwtScaleDraw::Alignment alignment ) +{ + QwtScaleDraw *sd = d_data->scaleDraw; + if ( sd->alignment() != alignment ) + { + sd->setAlignment( alignment ); + itemChanged(); + } +} + +/*! + \brief Draw the scale +*/ +void QwtPlotScaleItem::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + if ( d_data->scaleDivFromAxis ) + { + if ( canvasRect != d_data->canvasRectCache ) + { + d_data->updateBorders( canvasRect, xMap, yMap ); + d_data->canvasRectCache = canvasRect; + } + } + + QPen pen = painter->pen(); + pen.setStyle( Qt::SolidLine ); + painter->setPen( pen ); + + QwtScaleDraw *sd = d_data->scaleDraw; + if ( sd->orientation() == Qt::Horizontal ) + { + double y; + if ( d_data->borderDistance >= 0 ) + { + if ( sd->alignment() == QwtScaleDraw::BottomScale ) + y = canvasRect.top() + d_data->borderDistance; + else + { + y = canvasRect.bottom() - d_data->borderDistance; + } + + } + else + { + y = yMap.transform( d_data->position ); + } + + if ( y < canvasRect.top() || y > canvasRect.bottom() ) + return; + + sd->move( canvasRect.left(), y ); + sd->setLength( canvasRect.width() - 1 ); + sd->setTransformation( xMap.transformation()->copy() ); + } + else // == Qt::Vertical + { + double x; + if ( d_data->borderDistance >= 0 ) + { + if ( sd->alignment() == QwtScaleDraw::RightScale ) + x = canvasRect.left() + d_data->borderDistance; + else + { + x = canvasRect.right() - d_data->borderDistance; + } + } + else + { + x = xMap.transform( d_data->position ); + } + if ( x < canvasRect.left() || x > canvasRect.right() ) + return; + + sd->move( x, canvasRect.top() ); + sd->setLength( canvasRect.height() - 1 ); + sd->setTransformation( yMap.transformation()->copy() ); + } + + painter->setFont( d_data->font ); + + sd->draw( painter, d_data->palette ); +} + +/*! + \brief Update the item to changes of the axes scale division + + In case of isScaleDivFromAxis(), the scale draw is synchronized + to the correspond axis. + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtPlot::updateAxes() +*/ + +void QwtPlotScaleItem::updateScaleDiv( const QwtScaleDiv& xScaleDiv, + const QwtScaleDiv& yScaleDiv ) +{ + QwtScaleDraw *sd = d_data->scaleDraw; + if ( d_data->scaleDivFromAxis && sd ) + { + sd->setScaleDiv( + sd->orientation() == Qt::Horizontal ? xScaleDiv : yScaleDiv ); + + const QwtPlot *plt = plot(); + if ( plt != NULL ) + { + d_data->updateBorders( plt->canvas()->contentsRect(), + plt->canvasMap( xAxis() ), plt->canvasMap( yAxis() ) ); + d_data->canvasRectCache = QRect(); + } + } +} diff --git a/src/libpcp_qwt/src/qwt_plot_scaleitem.h b/src/libpcp_qwt/src/qwt_plot_scaleitem.h new file mode 100644 index 0000000..ead67a3 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_scaleitem.h @@ -0,0 +1,94 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_SCALE_ITEM_H +#define QWT_PLOT_SCALE_ITEM_H + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include "qwt_scale_draw.h" + +class QPalette; + +/*! + \brief A class which draws a scale inside the plot canvas + + QwtPlotScaleItem can be used to draw an axis inside the plot canvas. + It might by synchronized to one of the axis of the plot, but can + also display its own ticks and labels. + + It is allowed to synchronize the scale item with a disabled axis. + In plots with vertical and horizontal scale items, it might be + necessary to remove ticks at the intersections, by overloading + updateScaleDiv(). + + The scale might be at a specific position (f.e 0.0) or it might be + aligned to a canvas border. + + \par Example + The following example shows how to replace the left axis, by a scale item + at the x position 0.0. + \verbatim +QwtPlotScaleItem *scaleItem = + new QwtPlotScaleItem(QwtScaleDraw::RightScale, 0.0); +scaleItem->setFont(plot->axisWidget(QwtPlot::yLeft)->font()); +scaleItem->attach(plot); + +plot->enableAxis(QwtPlot::yLeft, false); +\endverbatim +*/ + +class QWT_EXPORT QwtPlotScaleItem: public QwtPlotItem +{ +public: + explicit QwtPlotScaleItem( + QwtScaleDraw::Alignment = QwtScaleDraw::BottomScale, + const double pos = 0.0 ); + + virtual ~QwtPlotScaleItem(); + + virtual int rtti() const; + + void setScaleDiv( const QwtScaleDiv& ); + const QwtScaleDiv& scaleDiv() const; + + void setScaleDivFromAxis( bool on ); + bool isScaleDivFromAxis() const; + + void setPalette( const QPalette & ); + QPalette palette() const; + + void setFont( const QFont& ); + QFont font() const; + + void setScaleDraw( QwtScaleDraw * ); + + const QwtScaleDraw *scaleDraw() const; + QwtScaleDraw *scaleDraw(); + + void setPosition( double pos ); + double position() const; + + void setBorderDistance( int numPixels ); + int borderDistance() const; + + void setAlignment( QwtScaleDraw::Alignment ); + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + + virtual void updateScaleDiv( const QwtScaleDiv &, const QwtScaleDiv & ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_seriesitem.cpp b/src/libpcp_qwt/src/qwt_plot_seriesitem.cpp new file mode 100644 index 0000000..c4cdd53 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_seriesitem.cpp @@ -0,0 +1,90 @@ +/* -*- 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_plot_seriesitem.h" + +class QwtPlotAbstractSeriesItem::PrivateData +{ +public: + PrivateData(): + orientation( Qt::Vertical ) + { + } + + Qt::Orientation orientation; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotAbstractSeriesItem::QwtPlotAbstractSeriesItem( const QwtText &title ): + QwtPlotItem( title ) +{ + d_data = new PrivateData(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotAbstractSeriesItem::QwtPlotAbstractSeriesItem( const QString &title ): + QwtPlotItem( QwtText( title ) ) +{ + d_data = new PrivateData(); +} + +//! Destructor +QwtPlotAbstractSeriesItem::~QwtPlotAbstractSeriesItem() +{ + delete d_data; +} + +/*! + Set the orientation of the item. + + The orientation() might be used in specific way by a plot item. + F.e. a QwtPlotCurve uses it to identify how to display the curve + int QwtPlotCurve::Steps or QwtPlotCurve::Sticks style. + + \sa orientation() +*/ +void QwtPlotAbstractSeriesItem::setOrientation( Qt::Orientation orientation ) +{ + if ( d_data->orientation != orientation ) + { + d_data->orientation = orientation; + itemChanged(); + } +} + +/*! + \return Orientation of the plot item + \sa setOrientation() +*/ +Qt::Orientation QwtPlotAbstractSeriesItem::orientation() const +{ + return d_data->orientation; +} + +/*! + \brief Draw the complete series + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas +*/ +void QwtPlotAbstractSeriesItem::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + drawSeries( painter, xMap, yMap, canvasRect, 0, -1 ); +} + diff --git a/src/libpcp_qwt/src/qwt_plot_seriesitem.h b/src/libpcp_qwt/src/qwt_plot_seriesitem.h new file mode 100644 index 0000000..d6c2d1f --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_seriesitem.h @@ -0,0 +1,206 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_SERIES_ITEM_H +#define QWT_PLOT_SERIES_ITEM_H + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include "qwt_scale_div.h" +#include "qwt_series_data.h" + +/*! + \brief Base class for plot items representing a series of samples +*/ +class QWT_EXPORT QwtPlotAbstractSeriesItem: public QwtPlotItem +{ +public: + explicit QwtPlotAbstractSeriesItem( const QString &title = QString::null ); + explicit QwtPlotAbstractSeriesItem( const QwtText &title ); + + virtual ~QwtPlotAbstractSeriesItem(); + + void setOrientation( Qt::Orientation ); + Qt::Orientation orientation() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF & ) const; + + /*! + Draw a subset of the samples + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first point to be painted + \param to Index of the last point to be painted. If to < 0 the + curve will be painted to its last point. + */ + virtual void drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const = 0; + +private: + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief Class template for plot items representing a series of samples +*/ +template <typename T> +class QwtPlotSeriesItem: public QwtPlotAbstractSeriesItem +{ +public: + explicit QwtPlotSeriesItem<T>( const QString &title = QString::null ); + explicit QwtPlotSeriesItem<T>( const QwtText &title ); + + virtual ~QwtPlotSeriesItem<T>(); + + void setData( QwtSeriesData<T> * ); + + QwtSeriesData<T> *data(); + const QwtSeriesData<T> *data() const; + + size_t dataSize() const; + T sample( int index ) const; + + virtual QRectF boundingRect() const; + virtual void updateScaleDiv( const QwtScaleDiv &, + const QwtScaleDiv & ); + +protected: + //! Series + QwtSeriesData<T> *d_series; +}; + +/*! + Constructor + \param title Title of the series item +*/ +template <typename T> +QwtPlotSeriesItem<T>::QwtPlotSeriesItem( const QString &title ): + QwtPlotAbstractSeriesItem( QwtText( title ) ), + d_series( NULL ) +{ +} + +/*! + Constructor + \param title Title of the series item +*/ +template <typename T> +QwtPlotSeriesItem<T>::QwtPlotSeriesItem( const QwtText &title ): + QwtPlotAbstractSeriesItem( title ), + d_series( NULL ) +{ +} + +//! Destructor +template <typename T> +QwtPlotSeriesItem<T>::~QwtPlotSeriesItem() +{ + delete d_series; +} + +//! \return the the curve data +template <typename T> +inline QwtSeriesData<T> *QwtPlotSeriesItem<T>::data() +{ + return d_series; +} + +//! \return the the curve data +template <typename T> +inline const QwtSeriesData<T> *QwtPlotSeriesItem<T>::data() const +{ + return d_series; +} + +/*! + \param index Index + \return Sample at position index +*/ +template <typename T> +inline T QwtPlotSeriesItem<T>::sample( int index ) const +{ + return d_series ? d_series->sample( index ) : T(); +} + +/*! + Assign a series of samples + + \param data Data + \warning The item takes ownership of the data object, deleting + it when its not used anymore. +*/ +template <typename T> +void QwtPlotSeriesItem<T>::setData( QwtSeriesData<T> *data ) +{ + if ( d_series != data ) + { + delete d_series; + d_series = data; + itemChanged(); + } +} + +/*! + Return the size of the data arrays + \sa setData() +*/ +template <typename T> +size_t QwtPlotSeriesItem<T>::dataSize() const +{ + if ( d_series == NULL ) + return 0; + + return d_series->size(); +} + +/*! + \return Bounding rectangle of the data. + If there is no bounding rect, like for empty data the rectangle is invalid. + + \sa QwtSeriesData<T>::boundingRect(), QRectF::isValid() +*/ +template <typename T> +QRectF QwtPlotSeriesItem<T>::boundingRect() const +{ + if ( d_series == NULL ) + return QRectF( 1.0, 1.0, -2.0, -2.0 ); // invalid + + return d_series->boundingRect(); +} + +/*! + Update the rect of interest according to the current scale ranges + + \param xScaleDiv Scale division of the x-axis + \param yScaleDiv Scale division of the y-axis + + \sa QwtSeriesData<T>::setRectOfInterest() +*/ +template <typename T> +void QwtPlotSeriesItem<T>::updateScaleDiv( + const QwtScaleDiv &xScaleDiv, const QwtScaleDiv &yScaleDiv ) +{ + if ( d_series ) + { + const QRectF rect = QRectF( + xScaleDiv.lowerBound(), yScaleDiv.lowerBound(), + xScaleDiv.range(), yScaleDiv.range() ); + + d_series->setRectOfInterest( rect ); + } +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_spectrocurve.cpp b/src/libpcp_qwt/src/qwt_plot_spectrocurve.cpp new file mode 100644 index 0000000..5df254d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_spectrocurve.cpp @@ -0,0 +1,300 @@ +/* -*- 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_plot_spectrocurve.h" +#include "qwt_color_map.h" +#include "qwt_scale_map.h" +#include "qwt_painter.h" +#include <qpainter.h> + +class QwtPlotSpectroCurve::PrivateData +{ +public: + PrivateData(): + colorRange( 0.0, 1000.0 ), + penWidth(0.0), + paintAttributes( QwtPlotSpectroCurve::ClipPoints ) + { + colorMap = new QwtLinearColorMap(); + } + + ~PrivateData() + { + delete colorMap; + } + + QwtColorMap *colorMap; + QwtInterval colorRange; + QVector<QRgb> colorTable; + double penWidth; + QwtPlotSpectroCurve::PaintAttributes paintAttributes; +}; + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotSpectroCurve::QwtPlotSpectroCurve( const QwtText &title ): + QwtPlotSeriesItem<QwtPoint3D>( title ) +{ + init(); +} + +/*! + Constructor + \param title Title of the curve +*/ +QwtPlotSpectroCurve::QwtPlotSpectroCurve( const QString &title ): + QwtPlotSeriesItem<QwtPoint3D>( QwtText( title ) ) +{ + init(); +} + +//! Destructor +QwtPlotSpectroCurve::~QwtPlotSpectroCurve() +{ + delete d_data; +} + +/*! + \brief Initialize data members +*/ +void QwtPlotSpectroCurve::init() +{ + setItemAttribute( QwtPlotItem::Legend ); + setItemAttribute( QwtPlotItem::AutoScale ); + + d_data = new PrivateData; + d_series = new QwtPoint3DSeriesData(); + + setZ( 20.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotSpectroCurve +int QwtPlotSpectroCurve::rtti() const +{ + return QwtPlotItem::Rtti_PlotSpectroCurve; +} + +/*! + Specify an attribute how to draw the curve + + \param attribute Paint attribute + \param on On/Off + /sa PaintAttribute, testPaintAttribute() +*/ +void QwtPlotSpectroCurve::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + \brief Return the current paint attributes + \sa PaintAttribute, setPaintAttribute() +*/ +bool QwtPlotSpectroCurve::testPaintAttribute( PaintAttribute attribute ) const +{ + return ( d_data->paintAttributes & attribute ); +} + +/*! + Initialize data with an array of samples. + \param samples Vector of points +*/ +void QwtPlotSpectroCurve::setSamples( const QVector<QwtPoint3D> &samples ) +{ + delete d_series; + d_series = new QwtPoint3DSeriesData( samples ); + itemChanged(); +} + +/*! + Change the color map + + Often it is useful to display the mapping between intensities and + colors as an additional plot axis, showing a color bar. + + \param colorMap Color Map + + \sa colorMap(), setColorRange(), QwtColorMap::color(), + QwtScaleWidget::setColorBarEnabled(), QwtScaleWidget::setColorMap() +*/ +void QwtPlotSpectroCurve::setColorMap( QwtColorMap *colorMap ) +{ + if ( colorMap != d_data->colorMap ) + { + delete d_data->colorMap; + d_data->colorMap = colorMap; + } + + itemChanged(); +} + +/*! + \return Color Map used for mapping the intensity values to colors + \sa setColorMap(), setColorRange(), QwtColorMap::color() +*/ +const QwtColorMap *QwtPlotSpectroCurve::colorMap() const +{ + return d_data->colorMap; +} + +/*! + Set the value interval, that corresponds to the color map + + \param interval interval.minValue() corresponds to 0.0, + interval.maxValue() to 1.0 on the color map. + + \sa colorRange(), setColorMap(), QwtColorMap::color() +*/ +void QwtPlotSpectroCurve::setColorRange( const QwtInterval &interval ) +{ + if ( interval != d_data->colorRange ) + { + d_data->colorRange = interval; + itemChanged(); + } +} + +/*! + \return Value interval, that corresponds to the color map + \sa setColorRange(), setColorMap(), QwtColorMap::color() +*/ +QwtInterval &QwtPlotSpectroCurve::colorRange() const +{ + return d_data->colorRange; +} + +/*! + Assign a pen width + + \param penWidth New pen width + \sa penWidth() +*/ +void QwtPlotSpectroCurve::setPenWidth(double penWidth) +{ + if ( penWidth < 0.0 ) + penWidth = 0.0; + + if ( d_data->penWidth != penWidth ) + { + d_data->penWidth = penWidth; + itemChanged(); + } +} + +/*! + \return Pen width used to draw a dot + \sa setPenWidth() +*/ +double QwtPlotSpectroCurve::penWidth() const +{ + return d_data->penWidth; +} + +/*! + Draw a subset of the points + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawDots() +*/ +void QwtPlotSpectroCurve::drawSeries( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( !painter || dataSize() <= 0 ) + return; + + if ( to < 0 ) + to = dataSize() - 1; + + if ( from < 0 ) + from = 0; + + if ( from > to ) + return; + + drawDots( painter, xMap, yMap, canvasRect, from, to ); +} + +/*! + Draw a subset of the points + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas + \param from Index of the first sample to be painted + \param to Index of the last sample to be painted. If to < 0 the + series will be painted to its last sample. + + \sa drawSeries() +*/ +void QwtPlotSpectroCurve::drawDots( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const +{ + if ( !d_data->colorRange.isValid() ) + return; + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + const QwtColorMap::Format format = d_data->colorMap->format(); + if ( format == QwtColorMap::Indexed ) + d_data->colorTable = d_data->colorMap->colorTable( d_data->colorRange ); + + for ( int i = from; i <= to; i++ ) + { + const QwtPoint3D sample = d_series->sample( i ); + + double xi = xMap.transform( sample.x() ); + double yi = yMap.transform( sample.y() ); + if ( doAlign ) + { + xi = qRound( xi ); + yi = qRound( yi ); + } + + if ( d_data->paintAttributes & QwtPlotSpectroCurve::ClipPoints ) + { + if ( !canvasRect.contains( xi, yi ) ) + continue; + } + + if ( format == QwtColorMap::RGB ) + { + const QRgb rgb = d_data->colorMap->rgb( + d_data->colorRange, sample.z() ); + + painter->setPen( QPen( QColor( rgb ), d_data->penWidth ) ); + } + else + { + const unsigned char index = d_data->colorMap->colorIndex( + d_data->colorRange, sample.z() ); + + painter->setPen( QPen( QColor( d_data->colorTable[index] ), + d_data->penWidth ) ); + } + + QwtPainter::drawPoint( painter, QPointF( xi, yi ) ); + } + + d_data->colorTable.clear(); +} diff --git a/src/libpcp_qwt/src/qwt_plot_spectrocurve.h b/src/libpcp_qwt/src/qwt_plot_spectrocurve.h new file mode 100644 index 0000000..93caa17 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_spectrocurve.h @@ -0,0 +1,76 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_CURVE_3D_H +#define QWT_PLOT_CURVE_3D_H + +#include "qwt_global.h" +#include "qwt_plot_seriesitem.h" +#include "qwt_series_data.h" + +class QwtSymbol; +class QwtColorMap; + +/*! + \brief Curve that displays 3D points as dots, where the z coordinate is + mapped to a color. +*/ +class QWT_EXPORT QwtPlotSpectroCurve: public QwtPlotSeriesItem<QwtPoint3D> +{ +public: + //! Paint attributes + enum PaintAttribute + { + //! Clip points outside the canvas rectangle + ClipPoints = 1 + }; + + //! Paint attributes + typedef QFlags<PaintAttribute> PaintAttributes; + + explicit QwtPlotSpectroCurve( const QString &title = QString::null ); + explicit QwtPlotSpectroCurve( const QwtText &title ); + + virtual ~QwtPlotSpectroCurve(); + + virtual int rtti() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setSamples( const QVector<QwtPoint3D> & ); + + void setColorMap( QwtColorMap * ); + const QwtColorMap *colorMap() const; + + void setColorRange( const QwtInterval & ); + QwtInterval & colorRange() const; + + virtual void drawSeries( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + + void setPenWidth(double width); + double penWidth() const; + +protected: + virtual void drawDots( QPainter *, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to ) const; + +private: + void init(); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotSpectroCurve::PaintAttributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_spectrogram.cpp b/src/libpcp_qwt/src/qwt_plot_spectrogram.cpp new file mode 100644 index 0000000..0a0b694 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_spectrogram.cpp @@ -0,0 +1,663 @@ +/* -*- 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_plot_spectrogram.h" +#include "qwt_painter.h" +#include "qwt_interval.h" +#include "qwt_scale_map.h" +#include "qwt_color_map.h" +#include <qimage.h> +#include <qpen.h> +#include <qpainter.h> +#include <qmath.h> +#include <qalgorithms.h> +#if QT_VERSION >= 0x040400 +#include <qthread.h> +#include <qfuture.h> +#include <qtconcurrentrun.h> +#endif + +class QwtPlotSpectrogram::PrivateData +{ +public: + PrivateData(): + data( NULL ), + renderThreadCount( 1 ) + { + colorMap = new QwtLinearColorMap(); + displayMode = ImageMode; + + conrecFlags = QwtRasterData::IgnoreAllVerticesOnLevel; + conrecFlags |= QwtRasterData::IgnoreOutOfRange; + } + ~PrivateData() + { + delete data; + delete colorMap; + } + + QwtRasterData *data; + QwtColorMap *colorMap; + DisplayModes displayMode; + + uint renderThreadCount; + + QList<double> contourLevels; + QPen defaultContourPen; + QwtRasterData::ConrecFlags conrecFlags; +}; + +/*! + Sets the following item attributes: + - QwtPlotItem::AutoScale: true + - QwtPlotItem::Legend: false + + The z value is initialized by 8.0. + + \param title Title + + \sa QwtPlotItem::setItemAttribute(), QwtPlotItem::setZ() +*/ +QwtPlotSpectrogram::QwtPlotSpectrogram( const QString &title ): + QwtPlotRasterItem( title ) +{ + d_data = new PrivateData(); + + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Legend, false ); + + setZ( 8.0 ); +} + +//! Destructor +QwtPlotSpectrogram::~QwtPlotSpectrogram() +{ + delete d_data; +} + +//! \return QwtPlotItem::Rtti_PlotSpectrogram +int QwtPlotSpectrogram::rtti() const +{ + return QwtPlotItem::Rtti_PlotSpectrogram; +} + +/*! + The display mode controls how the raster data will be represented. + + \param mode Display mode + \param on On/Off + + The default setting enables ImageMode. + + \sa DisplayMode, displayMode() +*/ +void QwtPlotSpectrogram::setDisplayMode( DisplayMode mode, bool on ) +{ + if ( on != bool( mode & d_data->displayMode ) ) + { + if ( on ) + d_data->displayMode |= mode; + else + d_data->displayMode &= ~mode; + } + + itemChanged(); +} + +/*! + The display mode controls how the raster data will be represented. + + \param mode Display mode + \return true if mode is enabled +*/ +bool QwtPlotSpectrogram::testDisplayMode( DisplayMode mode ) const +{ + return ( d_data->displayMode & mode ); +} + +/*! + Rendering an image from the raster data can often be done + parallel on a multicore system. + + \param numThreads Number of threads to be used for rendering. + If numThreads is set to 0, the system specific + ideal thread count is used. + + The default thread count is 1 ( = no additional threads ) + + \warning Rendering in multiple threads is only supported for Qt >= 4.4 + \sa renderThreadCount(), renderImage(), renderTile() +*/ +void QwtPlotSpectrogram::setRenderThreadCount( uint numThreads ) +{ + d_data->renderThreadCount = numThreads; +} + +/*! + \return Number of threads to be used for rendering. + If numThreads is set to 0, the system specific + ideal thread count is used. + + \warning Rendering in multiple threads is only supported for Qt >= 4.4 + \sa setRenderThreadCount(), renderImage(), renderTile() +*/ +uint QwtPlotSpectrogram::renderThreadCount() const +{ + return d_data->renderThreadCount; +} + +/*! + Change the color map + + Often it is useful to display the mapping between intensities and + colors as an additional plot axis, showing a color bar. + + \param colorMap Color Map + + \sa colorMap(), QwtScaleWidget::setColorBarEnabled(), + QwtScaleWidget::setColorMap() +*/ +void QwtPlotSpectrogram::setColorMap( QwtColorMap *colorMap ) +{ + if ( d_data->colorMap != colorMap ) + { + delete d_data->colorMap; + d_data->colorMap = colorMap; + } + + invalidateCache(); + itemChanged(); +} + +/*! + \return Color Map used for mapping the intensity values to colors + \sa setColorMap() +*/ +const QwtColorMap *QwtPlotSpectrogram::colorMap() const +{ + return d_data->colorMap; +} + +/*! + \brief Set the default pen for the contour lines + + If the spectrogram has a valid default contour pen + a contour line is painted using the default contour pen. + Otherwise (pen.style() == Qt::NoPen) the pen is calculated + for each contour level using contourPen(). + + \sa defaultContourPen(), contourPen() +*/ +void QwtPlotSpectrogram::setDefaultContourPen( const QPen &pen ) +{ + if ( pen != d_data->defaultContourPen ) + { + d_data->defaultContourPen = pen; + itemChanged(); + } +} + +/*! + \return Default contour pen + \sa setDefaultContourPen() +*/ +QPen QwtPlotSpectrogram::defaultContourPen() const +{ + return d_data->defaultContourPen; +} + +/*! + \brief Calculate the pen for a contour line + + The color of the pen is the color for level calculated by the color map + + \param level Contour level + \return Pen for the contour line + \note contourPen is only used if defaultContourPen().style() == Qt::NoPen + + \sa setDefaultContourPen(), setColorMap(), setContourLevels() +*/ +QPen QwtPlotSpectrogram::contourPen( double level ) const +{ + if ( d_data->data == NULL || d_data->colorMap == NULL ) + return QPen(); + + const QwtInterval intensityRange = d_data->data->interval(Qt::ZAxis); + const QColor c( d_data->colorMap->rgb( intensityRange, level ) ); + + return QPen( c ); +} + +/*! + Modify an attribute of the CONREC algorithm, used to calculate + the contour lines. + + \param flag CONREC flag + \param on On/Off + + \sa testConrecFlag(), renderContourLines(), + QwtRasterData::contourLines() +*/ +void QwtPlotSpectrogram::setConrecFlag( + QwtRasterData::ConrecFlag flag, bool on ) +{ + if ( bool( d_data->conrecFlags & flag ) == on ) + return; + + if ( on ) + d_data->conrecFlags |= flag; + else + d_data->conrecFlags &= ~flag; + + itemChanged(); +} + +/*! + Test an attribute of the CONREC algorithm, used to calculate + the contour lines. + + \param flag CONREC flag + \return true, is enabled + + \sa setConrecClag(), renderContourLines(), + QwtRasterData::contourLines() +*/ +bool QwtPlotSpectrogram::testConrecFlag( + QwtRasterData::ConrecFlag flag ) const +{ + return d_data->conrecFlags & flag; +} + +/*! + Set the levels of the contour lines + + \param levels Values of the contour levels + \sa contourLevels(), renderContourLines(), + QwtRasterData::contourLines() + + \note contourLevels returns the same levels but sorted. +*/ +void QwtPlotSpectrogram::setContourLevels( const QList<double> &levels ) +{ + d_data->contourLevels = levels; + qSort( d_data->contourLevels ); + itemChanged(); +} + +/*! + \brief Return the levels of the contour lines. + + The levels are sorted in increasing order. + + \sa contourLevels(), renderContourLines(), + QwtRasterData::contourLines() +*/ +QList<double> QwtPlotSpectrogram::contourLevels() const +{ + return d_data->contourLevels; +} + +/*! + Set the data to be displayed + + \param data Spectrogram Data + \sa data() +*/ +void QwtPlotSpectrogram::setData( QwtRasterData *data ) +{ + if ( data != d_data->data ) + { + delete d_data->data; + d_data->data = data; + + invalidateCache(); + itemChanged(); + } +} + +/*! + \return Spectrogram data + \sa setData() +*/ +const QwtRasterData *QwtPlotSpectrogram::data() const +{ + return d_data->data; +} + +/*! + \return Spectrogram data + \sa setData() +*/ +QwtRasterData *QwtPlotSpectrogram::data() +{ + return d_data->data; +} + +/*! + \return Bounding interval for an axis + + The default implementation returns the interval of the + associated raster data object. + + \param axis X, Y, or Z axis + \sa QwtRasterData::interval() +*/ +QwtInterval QwtPlotSpectrogram::interval(Qt::Axis axis) const +{ + if ( d_data->data == NULL ) + return QwtInterval(); + + return d_data->data->interval( axis ); +} + +/*! + \brief Pixel hint + + The geometry of a pixel is used to calculated the resolution and + alignment of the rendered image. + + The default implementation returns data()->pixelHint( rect ); + + \param area In most implementations the resolution of the data doesn't + depend on the requested area. + + \return Bounding rectangle of a pixel + + \sa QwtPlotRasterItem::pixelHint(), QwtRasterData::pixelHint(), + render(), renderImage() +*/ +QRectF QwtPlotSpectrogram::pixelHint( const QRectF &area ) const +{ + if ( d_data->data == NULL ) + return QRectF(); + + return d_data->data->pixelHint( area ); +} + +/*! + \brief Render an image from data and color map. + + For each pixel of rect the value is mapped into a color. + + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param area Requested area for the image in scale coordinates + \param imageSize Size of the requested image + + \return A QImage::Format_Indexed8 or QImage::Format_ARGB32 depending + on the color map. + + \sa QwtRasterData::value(), QwtColorMap::rgb(), + QwtColorMap::colorIndex() +*/ +QImage QwtPlotSpectrogram::renderImage( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &area, const QSize &imageSize ) const +{ + if ( imageSize.isEmpty() || d_data->data == NULL + || d_data->colorMap == NULL ) + { + return QImage(); + } + + const QwtInterval intensityRange = d_data->data->interval( Qt::ZAxis ); + if ( !intensityRange.isValid() ) + return QImage(); + + QImage::Format format = ( d_data->colorMap->format() == QwtColorMap::RGB ) + ? QImage::Format_ARGB32 : QImage::Format_Indexed8; + + QImage image( imageSize, format ); + + if ( d_data->colorMap->format() == QwtColorMap::Indexed ) + image.setColorTable( d_data->colorMap->colorTable( intensityRange ) ); + + d_data->data->initRaster( area, image.size() ); + +#if QT_VERSION >= 0x040400 && !defined(QT_NO_QFUTURE) + uint numThreads = d_data->renderThreadCount; + + if ( numThreads <= 0 ) + numThreads = QThread::idealThreadCount(); + + if ( numThreads <= 0 ) + numThreads = 1; + + const int numRows = imageSize.height() / numThreads; + + QList< QFuture<void> > futures; + for ( uint i = 0; i < numThreads; i++ ) + { + QRect tile( 0, i * numRows, image.width(), numRows ); + if ( i == numThreads - 1 ) + { + tile.setHeight( image.height() - i * numRows ); + renderTile( xMap, yMap, tile, &image ); + } + else + { + futures += QtConcurrent::run( + this, &QwtPlotSpectrogram::renderTile, + xMap, yMap, tile, &image ); + } + } + for ( int i = 0; i < futures.size(); i++ ) + futures[i].waitForFinished(); + +#else // QT_VERSION < 0x040400 + const QRect tile( 0, 0, image.width(), image.height() ); + renderTile( xMap, yMap, tile, &image ); +#endif + + d_data->data->discardRaster(); + + return image; +} + +/*! + \brief Render a tile of an image. + + Rendering in tiles can be used to composite an image in parallel + threads. + + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param tile Geometry of the tile in image coordinates + \param image Image to be rendered +*/ +void QwtPlotSpectrogram::renderTile( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRect &tile, QImage *image ) const +{ + const QwtInterval range = d_data->data->interval( Qt::ZAxis ); + if ( !range.isValid() ) + return; + + if ( d_data->colorMap->format() == QwtColorMap::RGB ) + { + for ( int y = tile.top(); y <= tile.bottom(); y++ ) + { + const double ty = yMap.invTransform( y ); + + QRgb *line = ( QRgb * )image->scanLine( y ); + line += tile.left(); + + for ( int x = tile.left(); x <= tile.right(); x++ ) + { + const double tx = xMap.invTransform( x ); + + *line++ = d_data->colorMap->rgb( range, + d_data->data->value( tx, ty ) ); + } + } + } + else if ( d_data->colorMap->format() == QwtColorMap::Indexed ) + { + for ( int y = tile.top(); y <= tile.bottom(); y++ ) + { + const double ty = yMap.invTransform( y ); + + unsigned char *line = image->scanLine( y ); + line += tile.left(); + + for ( int x = tile.left(); x <= tile.right(); x++ ) + { + const double tx = xMap.invTransform( x ); + + *line++ = d_data->colorMap->colorIndex( range, + d_data->data->value( tx, ty ) ); + } + } + } +} + +/*! + \brief Return the raster to be used by the CONREC contour algorithm. + + A larger size will improve the precisision of the CONREC algorithm, + but will slow down the time that is needed to calculate the lines. + + The default implementation returns rect.size() / 2 bounded to + the resolution depending on pixelSize(). + + \param area Rect, where to calculate the contour lines + \param rect Rect in pixel coordinates, where to paint the contour lines + \return Raster to be used by the CONREC contour algorithm. + + \note The size will be bounded to rect.size(). + + \sa drawContourLines(), QwtRasterData::contourLines() +*/ +QSize QwtPlotSpectrogram::contourRasterSize( + const QRectF &area, const QRect &rect ) const +{ + QSize raster = rect.size() / 2; + + const QRectF pixelRect = pixelHint( area ); + if ( !pixelRect.isEmpty() ) + { + const QSize res( qCeil( rect.width() / pixelRect.width() ), + qCeil( rect.height() / pixelRect.height() ) ); + raster = raster.boundedTo( res ); + } + + return raster; +} + +/*! + Calculate contour lines + + \param rect Rectangle, where to calculate the contour lines + \param raster Raster, used by the CONREC algorithm + + \sa contourLevels(), setConrecFlag(), + QwtRasterData::contourLines() +*/ +QwtRasterData::ContourLines QwtPlotSpectrogram::renderContourLines( + const QRectF &rect, const QSize &raster ) const +{ + if ( d_data->data == NULL ) + return QwtRasterData::ContourLines(); + + return d_data->data->contourLines( rect, raster, + d_data->contourLevels, d_data->conrecFlags ); +} + +/*! + Paint the contour lines + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param contourLines Contour lines + + \sa renderContourLines(), defaultContourPen(), contourPen() +*/ +void QwtPlotSpectrogram::drawContourLines( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QwtRasterData::ContourLines &contourLines ) const +{ + if ( d_data->data == NULL ) + return; + + const int numLevels = d_data->contourLevels.size(); + for ( int l = 0; l < numLevels; l++ ) + { + const double level = d_data->contourLevels[l]; + + QPen pen = defaultContourPen(); + if ( pen.style() == Qt::NoPen ) + pen = contourPen( level ); + + if ( pen.style() == Qt::NoPen ) + continue; + + painter->setPen( pen ); + + const QPolygonF &lines = contourLines[level]; + for ( int i = 0; i < lines.size(); i += 2 ) + { + const QPointF p1( xMap.transform( lines[i].x() ), + yMap.transform( lines[i].y() ) ); + const QPointF p2( xMap.transform( lines[i+1].x() ), + yMap.transform( lines[i+1].y() ) ); + + QwtPainter::drawLine( painter, p1, p2 ); + } + } +} + +/*! + \brief Draw the spectrogram + + \param painter Painter + \param xMap Maps x-values into pixel coordinates. + \param yMap Maps y-values into pixel coordinates. + \param canvasRect Contents rect of the canvas in painter coordinates + + \sa setDisplayMode(), renderImage(), + QwtPlotRasterItem::draw(), drawContourLines() +*/ +void QwtPlotSpectrogram::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + if ( d_data->displayMode & ImageMode ) + QwtPlotRasterItem::draw( painter, xMap, yMap, canvasRect ); + + if ( d_data->displayMode & ContourMode ) + { + // Add some pixels at the borders + const int margin = 2; + QRectF rasterRect( canvasRect.x() - margin, canvasRect.y() - margin, + canvasRect.width() + 2 * margin, canvasRect.height() + 2 * margin ); + + QRectF area = QwtScaleMap::invTransform( xMap, yMap, rasterRect ); + + const QRectF br = boundingRect(); + if ( br.isValid() ) + { + area &= br; + if ( area.isEmpty() ) + return; + + rasterRect = QwtScaleMap::transform( xMap, yMap, area ); + } + + QSize raster = contourRasterSize( area, rasterRect.toRect() ); + raster = raster.boundedTo( rasterRect.toRect().size() ); + if ( raster.isValid() ) + { + const QwtRasterData::ContourLines lines = + renderContourLines( area, raster ); + + drawContourLines( painter, xMap, yMap, lines ); + } + } +} diff --git a/src/libpcp_qwt/src/qwt_plot_spectrogram.h b/src/libpcp_qwt/src/qwt_plot_spectrogram.h new file mode 100644 index 0000000..423507c --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_spectrogram.h @@ -0,0 +1,115 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_SPECTROGRAM_H +#define QWT_PLOT_SPECTROGRAM_H + +#include "qwt_global.h" +#include "qwt_raster_data.h" +#include "qwt_plot_rasteritem.h" +#include <qlist.h> + +class QwtColorMap; + +/*! + \brief A plot item, which displays a spectrogram + + A spectrogram displays threedimenional data, where the 3rd dimension + ( the intensity ) is displayed using colors. The colors are calculated + from the values using a color map. + + In ContourMode contour lines are painted for the contour levels. + + \image html spectrogram3.png + + \sa QwtRasterData, QwtColorMap +*/ + +class QWT_EXPORT QwtPlotSpectrogram: public QwtPlotRasterItem +{ +public: + /*! + The display mode controls how the raster data will be represented. + \sa setDisplayMode(), testDisplayMode() + */ + + enum DisplayMode + { + //! The values are mapped to colors using a color map. + ImageMode = 0x01, + + //! The data is displayed using contour lines + ContourMode = 0x02 + }; + + //! Display modes + typedef QFlags<DisplayMode> DisplayModes; + + explicit QwtPlotSpectrogram( const QString &title = QString::null ); + virtual ~QwtPlotSpectrogram(); + + void setRenderThreadCount( uint numThreads ); + uint renderThreadCount() const; + + void setDisplayMode( DisplayMode, bool on = true ); + bool testDisplayMode( DisplayMode ) const; + + void setData( QwtRasterData *data ); + const QwtRasterData *data() const; + QwtRasterData *data(); + + void setColorMap( QwtColorMap * ); + const QwtColorMap *colorMap() const; + + virtual QwtInterval interval(Qt::Axis) const; + virtual QRectF pixelHint( const QRectF & ) const; + + void setDefaultContourPen( const QPen & ); + QPen defaultContourPen() const; + + virtual QPen contourPen( double level ) const; + + void setConrecFlag( QwtRasterData::ConrecFlag, bool on ); + bool testConrecFlag( QwtRasterData::ConrecFlag ) const; + + void setContourLevels( const QList<double> & ); + QList<double> contourLevels() const; + + virtual int rtti() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + +protected: + virtual QImage renderImage( + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &area, const QSize &imageSize ) const; + + virtual QSize contourRasterSize( + const QRectF &, const QRect & ) const; + + virtual QwtRasterData::ContourLines renderContourLines( + const QRectF &rect, const QSize &raster ) const; + + virtual void drawContourLines( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QwtRasterData::ContourLines& lines ) const; + + void renderTile( const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRect &imageRect, QImage *image ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtPlotSpectrogram::DisplayModes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_svgitem.cpp b/src/libpcp_qwt/src/qwt_plot_svgitem.cpp new file mode 100644 index 0000000..c84395d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_svgitem.cpp @@ -0,0 +1,214 @@ +/* -*- 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_plot_svgitem.h" +#include "qwt_scale_map.h" +#include "qwt_legend.h" +#include "qwt_legend_item.h" +#include "qwt_painter.h" +#include <qpainter.h> +#include <qsvgrenderer.h> + +class QwtPlotSvgItem::PrivateData +{ +public: + PrivateData() + { + } + + QRectF boundingRect; + QSvgRenderer renderer; +}; + +/*! + \brief Constructor + + Sets the following item attributes: + - QwtPlotItem::AutoScale: true + - QwtPlotItem::Legend: false + + \param title Title +*/ +QwtPlotSvgItem::QwtPlotSvgItem( const QString& title ): + QwtPlotItem( QwtText( title ) ) +{ + init(); +} + +/*! + \brief Constructor + + Sets the following item attributes: + - QwtPlotItem::AutoScale: true + - QwtPlotItem::Legend: false + + \param title Title +*/ +QwtPlotSvgItem::QwtPlotSvgItem( const QwtText& title ): + QwtPlotItem( title ) +{ + init(); +} + +//! Destructor +QwtPlotSvgItem::~QwtPlotSvgItem() +{ + delete d_data; +} + +void QwtPlotSvgItem::init() +{ + d_data = new PrivateData(); + + setItemAttribute( QwtPlotItem::AutoScale, true ); + setItemAttribute( QwtPlotItem::Legend, false ); + + setZ( 8.0 ); +} + +//! \return QwtPlotItem::Rtti_PlotSVG +int QwtPlotSvgItem::rtti() const +{ + return QwtPlotItem::Rtti_PlotSVG; +} + +/*! + Load a SVG file + + \param rect Bounding rectangle + \param fileName SVG file name + + \return true, if the SVG file could be loaded +*/ +bool QwtPlotSvgItem::loadFile( const QRectF &rect, + const QString &fileName ) +{ + d_data->boundingRect = rect; + const bool ok = d_data->renderer.load( fileName ); + itemChanged(); + return ok; +} + +/*! + Load SVG data + + \param rect Bounding rectangle + \param data in SVG format + + \return true, if the SVG data could be loaded +*/ +bool QwtPlotSvgItem::loadData( const QRectF &rect, + const QByteArray &data ) +{ + d_data->boundingRect = rect; + const bool ok = d_data->renderer.load( data ); + itemChanged(); + return ok; +} + +//! Bounding rect of the item +QRectF QwtPlotSvgItem::boundingRect() const +{ + return d_data->boundingRect; +} + +//! \return Renderer used to render the SVG data +const QSvgRenderer &QwtPlotSvgItem::renderer() const +{ + return d_data->renderer; +} + +//! \return Renderer used to render the SVG data +QSvgRenderer &QwtPlotSvgItem::renderer() +{ + return d_data->renderer; +} + +/*! + Draw the SVG item + + \param painter Painter + \param xMap X-Scale Map + \param yMap Y-Scale Map + \param canvasRect Contents rect of the plot canvas +*/ +void QwtPlotSvgItem::draw( QPainter *painter, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect ) const +{ + const QRectF cRect = QwtScaleMap::invTransform( + xMap, yMap, canvasRect.toRect() ); + const QRectF bRect = boundingRect(); + if ( bRect.isValid() && cRect.isValid() ) + { + QRectF rect = bRect; + if ( bRect.contains( cRect ) ) + rect = cRect; + + const QRectF r = QwtScaleMap::transform( xMap, yMap, rect ); + render( painter, viewBox( rect ), r ); + } +} + +/*! + Render the SVG data + + \param painter Painter + \param viewBox View Box, see QSvgRenderer::viewBox + \param rect Traget rectangle on the paint device +*/ +void QwtPlotSvgItem::render( QPainter *painter, + const QRectF &viewBox, const QRectF &rect ) const +{ + if ( !viewBox.isValid() ) + return; + + QRectF r = rect; + + if ( QwtPainter::roundingAlignment( painter ) ) + { + r.setLeft ( qRound( r.left() ) ); + r.setRight ( qRound( r.right() ) ); + r.setTop ( qRound( r.top() ) ); + r.setBottom ( qRound( r.bottom() ) ); + } + + d_data->renderer.setViewBox( viewBox ); + d_data->renderer.render( painter, r ); +} + +/*! + Calculate the viewBox from an rect and boundingRect(). + + \param rect Rectangle in scale coordinates + \return viewBox View Box, see QSvgRenderer::viewBox +*/ +QRectF QwtPlotSvgItem::viewBox( const QRectF &rect ) const +{ + const QSize sz = d_data->renderer.defaultSize(); + const QRectF br = boundingRect(); + + if ( !rect.isValid() || !br.isValid() || sz.isNull() ) + return QRectF(); + + QwtScaleMap xMap; + xMap.setScaleInterval( br.left(), br.right() ); + xMap.setPaintInterval( 0, sz.width() ); + + QwtScaleMap yMap; + yMap.setScaleInterval( br.top(), br.bottom() ); + yMap.setPaintInterval( sz.height(), 0 ); + + const double x1 = xMap.transform( rect.left() ); + const double x2 = xMap.transform( rect.right() ); + const double y1 = yMap.transform( rect.bottom() ); + const double y2 = yMap.transform( rect.top() ); + + return QRectF( x1, y1, x2 - x1, y2 - y1 ); +} diff --git a/src/libpcp_qwt/src/qwt_plot_svgitem.h b/src/libpcp_qwt/src/qwt_plot_svgitem.h new file mode 100644 index 0000000..1d98dee --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_svgitem.h @@ -0,0 +1,61 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_SVGITEM_H +#define QWT_PLOT_SVGITEM_H + +#include "qwt_global.h" +#include "qwt_plot_item.h" +#include <qstring.h> + +class QSvgRenderer; +class QByteArray; + +/*! + \brief A plot item, which displays + data in Scalable Vector Graphics (SVG) format. + + SVG images are often used to display maps +*/ + +class QWT_EXPORT QwtPlotSvgItem: public QwtPlotItem +{ +public: + explicit QwtPlotSvgItem( const QString& title = QString::null ); + explicit QwtPlotSvgItem( const QwtText& title ); + virtual ~QwtPlotSvgItem(); + + bool loadFile( const QRectF&, const QString &fileName ); + bool loadData( const QRectF&, const QByteArray & ); + + virtual QRectF boundingRect() const; + + virtual void draw( QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &rect ) const; + + virtual int rtti() const; + +protected: + const QSvgRenderer &renderer() const; + QSvgRenderer &renderer(); + + void render( QPainter *painter, + const QRectF &viewBox, const QRectF &rect ) const; + + QRectF viewBox( const QRectF &area ) const; + +private: + void init(); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_plot_xml.cpp b/src/libpcp_qwt/src/qwt_plot_xml.cpp new file mode 100644 index 0000000..0e72a29 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_xml.cpp @@ -0,0 +1,41 @@ +/* -*- 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_plot.h" + +/*! + This method is intended for manipulating the plot widget + from a specific editor in the Qwt designer plugin. + + \warning The plot editor has never been implemented. +*/ +void QwtPlot::applyProperties( const QString & /* xmlDocument */ ) +{ +#if 0 + // Temporary dummy code, for designer tests + setTitle( xmlDocument ); + replot(); +#endif +} + +/*! + This method is intended for manipulating the plot widget + from a specific editor in the Qwt designer plugin. + + \warning The plot editor has never been implemented. +*/ +QString QwtPlot::grabProperties() const +{ +#if 0 + // Temporary dummy code, for designer tests + return title().text(); +#else + return QString::null; +#endif +} diff --git a/src/libpcp_qwt/src/qwt_plot_zoomer.cpp b/src/libpcp_qwt/src/qwt_plot_zoomer.cpp new file mode 100644 index 0000000..2add7d4 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_zoomer.cpp @@ -0,0 +1,607 @@ +/* -*- 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_plot_zoomer.h" +#include "qwt_plot.h" +#include "qwt_plot_canvas.h" +#include "qwt_scale_div.h" +#include "qwt_picker_machine.h" +#include <qalgorithms.h> + +class QwtPlotZoomer::PrivateData +{ +public: + uint zoomRectIndex; + QStack<QRectF> zoomStack; + + int maxStackDepth; +}; + +/*! + \brief Create a zoomer for a plot canvas. + + The zoomer is set to those x- and y-axis of the parent plot of the + canvas that are enabled. If both or no x-axis are enabled, the picker + is set to QwtPlot::xBottom. If both or no y-axis are + enabled, it is set to QwtPlot::yLeft. + + The zoomer is initialized with a QwtPickerDragRectMachine, + the tracker mode is set to QwtPicker::ActiveOnly and the rubberband + is set to QwtPicker::RectRubberBand + + \param canvas Plot canvas to observe, also the parent object + \param doReplot Call replot for the attached plot before initializing + the zoomer with its scales. This might be necessary, + when the plot is in a state with pending scale changes. + + \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase() +*/ +QwtPlotZoomer::QwtPlotZoomer( QwtPlotCanvas *canvas, bool doReplot ): + QwtPlotPicker( canvas ) +{ + if ( canvas ) + init( doReplot ); +} + +/*! + \brief Create a zoomer for a plot canvas. + + The zoomer is initialized with a QwtPickerDragRectMachine, + the tracker mode is set to QwtPicker::ActiveOnly and the rubberband + is set to QwtPicker;;RectRubberBand + + \param xAxis X axis of the zoomer + \param yAxis Y axis of the zoomer + \param canvas Plot canvas to observe, also the parent object + \param doReplot Call replot for the attached plot before initializing + the zoomer with its scales. This might be necessary, + when the plot is in a state with pending scale changes. + + \sa QwtPlot::autoReplot(), QwtPlot::replot(), setZoomBase() +*/ + +QwtPlotZoomer::QwtPlotZoomer( int xAxis, int yAxis, + QwtPlotCanvas *canvas, bool doReplot ): + QwtPlotPicker( xAxis, yAxis, canvas ) +{ + if ( canvas ) + init( doReplot ); +} + +//! Init the zoomer, used by the constructors +void QwtPlotZoomer::init( bool doReplot ) +{ + d_data = new PrivateData; + + d_data->maxStackDepth = -1; + + setTrackerMode( ActiveOnly ); + setRubberBand( RectRubberBand ); + setStateMachine( new QwtPickerDragRectMachine() ); + + if ( doReplot && plot() ) + plot()->replot(); + + setZoomBase( scaleRect() ); +} + +QwtPlotZoomer::~QwtPlotZoomer() +{ + delete d_data; +} + +/*! + \brief Limit the number of recursive zoom operations to depth. + + A value of -1 set the depth to unlimited, 0 disables zooming. + If the current zoom rectangle is below depth, the plot is unzoomed. + + \param depth Maximum for the stack depth + \sa maxStackDepth() + \note depth doesn't include the zoom base, so zoomStack().count() might be + maxStackDepth() + 1. +*/ +void QwtPlotZoomer::setMaxStackDepth( int depth ) +{ + d_data->maxStackDepth = depth; + + if ( depth >= 0 ) + { + // unzoom if the current depth is below d_data->maxStackDepth + + const int zoomOut = + int( d_data->zoomStack.count() ) - 1 - depth; // -1 for the zoom base + + if ( zoomOut > 0 ) + { + zoom( -zoomOut ); + for ( int i = int( d_data->zoomStack.count() ) - 1; + i > int( d_data->zoomRectIndex ); i-- ) + { + ( void )d_data->zoomStack.pop(); // remove trailing rects + } + } + } +} + +/*! + \return Maximal depth of the zoom stack. + \sa setMaxStackDepth() +*/ +int QwtPlotZoomer::maxStackDepth() const +{ + return d_data->maxStackDepth; +} + +/*! + Return the zoom stack. zoomStack()[0] is the zoom base, + zoomStack()[1] the first zoomed rectangle. + + \sa setZoomStack(), zoomRectIndex() +*/ +const QStack<QRectF> &QwtPlotZoomer::zoomStack() const +{ + return d_data->zoomStack; +} + +/*! + \return Initial rectangle of the zoomer + \sa setZoomBase(), zoomRect() +*/ +QRectF QwtPlotZoomer::zoomBase() const +{ + return d_data->zoomStack[0]; +} + +/*! + Reinitialized the zoom stack with scaleRect() as base. + + \param doReplot Call replot for the attached plot before initializing + the zoomer with its scales. This might be necessary, + when the plot is in a state with pending scale changes. + + \sa zoomBase(), scaleRect() QwtPlot::autoReplot(), QwtPlot::replot(). +*/ +void QwtPlotZoomer::setZoomBase( bool doReplot ) +{ + QwtPlot *plt = plot(); + if ( plt == NULL ) + return; + + if ( doReplot ) + plt->replot(); + + d_data->zoomStack.clear(); + d_data->zoomStack.push( scaleRect() ); + d_data->zoomRectIndex = 0; + + rescale(); +} + +/*! + \brief Set the initial size of the zoomer. + + base is united with the current scaleRect() and the zoom stack is + reinitalized with it as zoom base. plot is zoomed to scaleRect(). + + \param base Zoom base + + \sa zoomBase(), scaleRect() +*/ +void QwtPlotZoomer::setZoomBase( const QRectF &base ) +{ + const QwtPlot *plt = plot(); + if ( !plt ) + return; + + const QRectF sRect = scaleRect(); + const QRectF bRect = base | sRect; + + d_data->zoomStack.clear(); + d_data->zoomStack.push( bRect ); + d_data->zoomRectIndex = 0; + + if ( base != sRect ) + { + d_data->zoomStack.push( sRect ); + d_data->zoomRectIndex++; + } + + rescale(); +} + +/*! + Rectangle at the current position on the zoom stack. + + \sa zoomRectIndex(), scaleRect(). +*/ +QRectF QwtPlotZoomer::zoomRect() const +{ + return d_data->zoomStack[d_data->zoomRectIndex]; +} + +/*! + \return Index of current position of zoom stack. +*/ +uint QwtPlotZoomer::zoomRectIndex() const +{ + return d_data->zoomRectIndex; +} + +/*! + \brief Zoom in + + Clears all rectangles above the current position of the + zoom stack and pushs the normalized rect on it. + + \note If the maximal stack depth is reached, zoom is ignored. + \note The zoomed signal is emitted. +*/ + +void QwtPlotZoomer::zoom( const QRectF &rect ) +{ + if ( d_data->maxStackDepth >= 0 && + int( d_data->zoomRectIndex ) >= d_data->maxStackDepth ) + { + return; + } + + const QRectF zoomRect = rect.normalized(); + if ( zoomRect != d_data->zoomStack[d_data->zoomRectIndex] ) + { + for ( uint i = int( d_data->zoomStack.count() ) - 1; + i > d_data->zoomRectIndex; i-- ) + { + ( void )d_data->zoomStack.pop(); + } + + d_data->zoomStack.push( zoomRect ); + d_data->zoomRectIndex++; + + rescale(); + + Q_EMIT zoomed( zoomRect ); + } +} + +/*! + \brief Zoom in or out + + Activate a rectangle on the zoom stack with an offset relative + to the current position. Negative values of offest will zoom out, + positive zoom in. A value of 0 zooms out to the zoom base. + + \param offset Offset relative to the current position of the zoom stack. + \note The zoomed signal is emitted. + \sa zoomRectIndex() +*/ +void QwtPlotZoomer::zoom( int offset ) +{ + if ( offset == 0 ) + d_data->zoomRectIndex = 0; + else + { + int newIndex = d_data->zoomRectIndex + offset; + newIndex = qMax( 0, newIndex ); + newIndex = qMin( int( d_data->zoomStack.count() ) - 1, newIndex ); + + d_data->zoomRectIndex = uint( newIndex ); + } + + rescale(); + + Q_EMIT zoomed( zoomRect() ); +} + +/*! + \brief Assign a zoom stack + + In combination with other types of navigation it might be useful to + modify to manipulate the complete zoom stack. + + \param zoomStack New zoom stack + \param zoomRectIndex Index of the current position of zoom stack. + In case of -1 the current position is at the top + of the stack. + + \note The zoomed signal might be emitted. + \sa zoomStack(), zoomRectIndex() +*/ +void QwtPlotZoomer::setZoomStack( + const QStack<QRectF> &zoomStack, int zoomRectIndex ) +{ + if ( zoomStack.isEmpty() ) + return; + + if ( d_data->maxStackDepth >= 0 && + int( zoomStack.count() ) > d_data->maxStackDepth ) + { + return; + } + + if ( zoomRectIndex < 0 || zoomRectIndex > int( zoomStack.count() ) ) + zoomRectIndex = zoomStack.count() - 1; + + const bool doRescale = zoomStack[zoomRectIndex] != zoomRect(); + + d_data->zoomStack = zoomStack; + d_data->zoomRectIndex = uint( zoomRectIndex ); + + if ( doRescale ) + { + rescale(); + Q_EMIT zoomed( zoomRect() ); + } +} + +/*! + Adjust the observed plot to zoomRect() + + \note Initiates QwtPlot::replot +*/ + +void QwtPlotZoomer::rescale() +{ + QwtPlot *plt = plot(); + if ( !plt ) + return; + + const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex]; + if ( rect != scaleRect() ) + { + const bool doReplot = plt->autoReplot(); + plt->setAutoReplot( false ); + + double x1 = rect.left(); + double x2 = rect.right(); + if ( plt->axisScaleDiv( xAxis() )->lowerBound() > + plt->axisScaleDiv( xAxis() )->upperBound() ) + { + qSwap( x1, x2 ); + } + + plt->setAxisScale( xAxis(), x1, x2 ); + + double y1 = rect.top(); + double y2 = rect.bottom(); + if ( plt->axisScaleDiv( yAxis() )->lowerBound() > + plt->axisScaleDiv( yAxis() )->upperBound() ) + { + qSwap( y1, y2 ); + } + plt->setAxisScale( yAxis(), y1, y2 ); + + plt->setAutoReplot( doReplot ); + + plt->replot(); + } +} + +/*! + Reinitialize the axes, and set the zoom base to their scales. + + \param xAxis X axis + \param yAxis Y axis +*/ + +void QwtPlotZoomer::setAxis( int xAxis, int yAxis ) +{ + if ( xAxis != QwtPlotPicker::xAxis() || yAxis != QwtPlotPicker::yAxis() ) + { + QwtPlotPicker::setAxis( xAxis, yAxis ); + setZoomBase( scaleRect() ); + } +} + +/*! + Qt::MidButton zooms out one position on the zoom stack, + Qt::RightButton to the zoom base. + + Changes the current position on the stack, but doesn't pop + any rectangle. + + \note The mouse events can be changed, using + QwtEventPattern::setMousePattern: 2, 1 +*/ +void QwtPlotZoomer::widgetMouseReleaseEvent( QMouseEvent *me ) +{ + if ( mouseMatch( MouseSelect2, me ) ) + zoom( 0 ); + else if ( mouseMatch( MouseSelect3, me ) ) + zoom( -1 ); + else if ( mouseMatch( MouseSelect6, me ) ) + zoom( +1 ); + else + QwtPlotPicker::widgetMouseReleaseEvent( me ); +} + +/*! + Qt::Key_Plus zooms in, Qt::Key_Minus zooms out one position on the + zoom stack, Qt::Key_Escape zooms out to the zoom base. + + Changes the current position on the stack, but doesn't pop + any rectangle. + + \note The keys codes can be changed, using + QwtEventPattern::setKeyPattern: 3, 4, 5 +*/ + +void QwtPlotZoomer::widgetKeyPressEvent( QKeyEvent *ke ) +{ + if ( !isActive() ) + { + if ( keyMatch( KeyUndo, ke ) ) + zoom( -1 ); + else if ( keyMatch( KeyRedo, ke ) ) + zoom( +1 ); + else if ( keyMatch( KeyHome, ke ) ) + zoom( 0 ); + } + + QwtPlotPicker::widgetKeyPressEvent( ke ); +} + +/*! + Move the current zoom rectangle. + + \param dx X offset + \param dy Y offset + + \note The changed rectangle is limited by the zoom base +*/ +void QwtPlotZoomer::moveBy( double dx, double dy ) +{ + const QRectF &rect = d_data->zoomStack[d_data->zoomRectIndex]; + moveTo( QPointF( rect.left() + dx, rect.top() + dy ) ); +} + +/*! + Move the the current zoom rectangle. + + \param pos New position + + \sa QRectF::moveTo() + \note The changed rectangle is limited by the zoom base +*/ +void QwtPlotZoomer::moveTo( const QPointF &pos ) +{ + double x = pos.x(); + double y = pos.y(); + + if ( x < zoomBase().left() ) + x = zoomBase().left(); + if ( x > zoomBase().right() - zoomRect().width() ) + x = zoomBase().right() - zoomRect().width(); + + if ( y < zoomBase().top() ) + y = zoomBase().top(); + if ( y > zoomBase().bottom() - zoomRect().height() ) + y = zoomBase().bottom() - zoomRect().height(); + + if ( x != zoomRect().left() || y != zoomRect().top() ) + { + d_data->zoomStack[d_data->zoomRectIndex].moveTo( x, y ); + rescale(); + } +} + +/*! + \brief Check and correct a selected rectangle + + Reject rectangles with a hight or width < 2, otherwise + expand the selected rectangle to a minimum size of 11x11 + and accept it. + + \return true If rect is accepted, or has been changed + to a accepted rectangle. +*/ + +bool QwtPlotZoomer::accept( QPolygon &pa ) const +{ + if ( pa.count() < 2 ) + return false; + + QRect rect = QRect( pa[0], pa[int( pa.count() ) - 1] ); + rect = rect.normalized(); + + const int minSize = 2; + if ( rect.width() < minSize && rect.height() < minSize ) + return false; + + const int minZoomSize = 11; + + const QPoint center = rect.center(); + rect.setSize( rect.size().expandedTo( QSize( minZoomSize, minZoomSize ) ) ); + rect.moveCenter( center ); + + pa.resize( 2 ); + pa[0] = rect.topLeft(); + pa[1] = rect.bottomRight(); + + return true; +} + +/*! + \brief Limit zooming by a minimum rectangle + + \return zoomBase().width() / 10e4, zoomBase().height() / 10e4 +*/ +QSizeF QwtPlotZoomer::minZoomSize() const +{ + return QSizeF( d_data->zoomStack[0].width() / 10e4, + d_data->zoomStack[0].height() / 10e4 ); +} + +/*! + Rejects selections, when the stack depth is too deep, or + the zoomed rectangle is minZoomSize(). + + \sa minZoomSize(), maxStackDepth() +*/ +void QwtPlotZoomer::begin() +{ + if ( d_data->maxStackDepth >= 0 ) + { + if ( d_data->zoomRectIndex >= uint( d_data->maxStackDepth ) ) + return; + } + + const QSizeF minSize = minZoomSize(); + if ( minSize.isValid() ) + { + const QSizeF sz = + d_data->zoomStack[d_data->zoomRectIndex].size() * 0.9999; + + if ( minSize.width() >= sz.width() && + minSize.height() >= sz.height() ) + { + return; + } + } + + QwtPlotPicker::begin(); +} + +/*! + Expand the selected rectangle to minZoomSize() and zoom in + if accepted. + + \sa accept(), minZoomSize() +*/ +bool QwtPlotZoomer::end( bool ok ) +{ + ok = QwtPlotPicker::end( ok ); + if ( !ok ) + return false; + + QwtPlot *plot = QwtPlotZoomer::plot(); + if ( !plot ) + return false; + + const QPolygon &pa = selection(); + if ( pa.count() < 2 ) + return false; + + QRect rect = QRect( pa[0], pa[int( pa.count() - 1 )] ); + rect = rect.normalized(); + + QRectF zoomRect = invTransform( rect ).normalized(); + + const QSizeF minSize = minZoomSize(); + if ( minSize.isValid() ) + { + const QPointF center = zoomRect.center(); + zoomRect.setSize( zoomRect.size().expandedTo( minZoomSize() ) ); + zoomRect.moveCenter( center ); + } + + zoom( zoomRect ); + + return true; +} diff --git a/src/libpcp_qwt/src/qwt_plot_zoomer.h b/src/libpcp_qwt/src/qwt_plot_zoomer.h new file mode 100644 index 0000000..84e23c7 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_plot_zoomer.h @@ -0,0 +1,104 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_PLOT_ZOOMER_H +#define QWT_PLOT_ZOOMER_H + +#include "qwt_global.h" +#include "qwt_plot_picker.h" +#include <qstack.h> + +/*! + \brief QwtPlotZoomer provides stacked zooming for a plot widget + + QwtPlotZoomer offers rubberband selections on the plot canvas, + translating the selected rectangles into plot coordinates and + adjusting the axes to them. Zooming can repeated as often as + possible, limited only by maxStackDepth() or minZoomSize(). + Each rectangle is pushed on a stack. + + Zoom rectangles can be selected depending on selectionFlags() using the + mouse or keyboard (QwtEventPattern, QwtPickerMachine). + QwtEventPattern::MouseSelect3,QwtEventPattern::KeyUndo, + or QwtEventPattern::MouseSelect6,QwtEventPattern::KeyRedo + walk up and down the zoom stack. + QwtEventPattern::MouseSelect2 or QwtEventPattern::KeyHome unzoom to + the initial size. + + QwtPlotZoomer is tailored for plots with one x and y axis, but it is + allowed to attach a second QwtPlotZoomer for the other axes. + + \note The realtime example includes an derived zoomer class that adds + scrollbars to the plot canvas. +*/ + +class QWT_EXPORT QwtPlotZoomer: public QwtPlotPicker +{ + Q_OBJECT +public: + explicit QwtPlotZoomer( QwtPlotCanvas *, bool doReplot = true ); + explicit QwtPlotZoomer( int xAxis, int yAxis, + QwtPlotCanvas *, bool doReplot = true ); + + virtual ~QwtPlotZoomer(); + + virtual void setZoomBase( bool doReplot = true ); + virtual void setZoomBase( const QRectF & ); + + QRectF zoomBase() const; + QRectF zoomRect() const; + + virtual void setAxis( int xAxis, int yAxis ); + + void setMaxStackDepth( int ); + int maxStackDepth() const; + + const QStack<QRectF> &zoomStack() const; + void setZoomStack( const QStack<QRectF> &, + int zoomRectIndex = -1 ); + + uint zoomRectIndex() const; + +public Q_SLOTS: + void moveBy( double x, double y ); + virtual void moveTo( const QPointF & ); + + virtual void zoom( const QRectF & ); + virtual void zoom( int up ); + +Q_SIGNALS: + /*! + A signal emitting the zoomRect(), when the plot has been + zoomed in or out. + + \param rect Current zoom rectangle. + */ + + void zoomed( const QRectF &rect ); + +protected: + virtual void rescale(); + + virtual QSizeF minZoomSize() const; + + virtual void widgetMouseReleaseEvent( QMouseEvent * ); + virtual void widgetKeyPressEvent( QKeyEvent * ); + + virtual void begin(); + virtual bool end( bool ok = true ); + virtual bool accept( QPolygon & ) const; + +private: + void init( bool doReplot ); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_point_3d.cpp b/src/libpcp_qwt/src/qwt_point_3d.cpp new file mode 100644 index 0000000..27e4c1e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_point_3d.cpp @@ -0,0 +1,22 @@ +/* -*- 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_point_3d.h" + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<( QDebug debug, const QwtPoint3D &point ) +{ + debug.nospace() << "QwtPoint3D(" << point.x() + << "," << point.y() << "," << point.z() << ")"; + return debug.space(); +} + +#endif + diff --git a/src/libpcp_qwt/src/qwt_point_3d.h b/src/libpcp_qwt/src/qwt_point_3d.h new file mode 100644 index 0000000..ac18938 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_point_3d.h @@ -0,0 +1,189 @@ +/* -*- 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 + *****************************************************************************/ + +/*! \file */ +#ifndef QWT_POINT_3D_H +#define QWT_POINT_3D_H 1 + +#include "qwt_global.h" +#include <qpoint.h> +#ifndef QT_NO_DEBUG_STREAM +#include <qdebug.h> +#endif + +/*! + \brief QwtPoint3D class defines a 3D point in double coordinates +*/ + +class QWT_EXPORT QwtPoint3D +{ +public: + QwtPoint3D(); + QwtPoint3D( double x, double y, double z ); + QwtPoint3D( const QwtPoint3D & ); + QwtPoint3D( const QPointF & ); + + bool isNull() const; + + double x() const; + double y() const; + double z() const; + + double &rx(); + double &ry(); + double &rz(); + + void setX( double x ); + void setY( double y ); + void setZ( double y ); + + QPointF toPoint() const; + + bool operator==( const QwtPoint3D & ) const; + bool operator!=( const QwtPoint3D & ) const; + +private: + double d_x; + double d_y; + double d_z; +}; + +Q_DECLARE_TYPEINFO(QwtPoint3D, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +QWT_EXPORT QDebug operator<<( QDebug, const QwtPoint3D & ); +#endif + +/*! + Constructs a null point. + \sa isNull() +*/ +inline QwtPoint3D::QwtPoint3D(): + d_x( 0.0 ), + d_y( 0.0 ), + d_z( 0.0 ) +{ +} + +//! Constructs a point with coordinates specified by x, y and z. +inline QwtPoint3D::QwtPoint3D( double x, double y, double z = 0.0 ): + d_x( x ), + d_y( y ), + d_z( z ) +{ +} + +/*! + Copy constructor. + Constructs a point using the values of the point specified. +*/ +inline QwtPoint3D::QwtPoint3D( const QwtPoint3D &other ): + d_x( other.d_x ), + d_y( other.d_y ), + d_z( other.d_z ) +{ +} + +/*! + Constructs a point with x and y coordinates from a 2D point, + and a z coordinate of 0. +*/ +inline QwtPoint3D::QwtPoint3D( const QPointF &other ): + d_x( other.x() ), + d_y( other.y() ), + d_z( 0.0 ) +{ +} + +/*! + Returns true if the point is null; otherwise returns false. + + A point is considered to be null if x, y and z-coordinates + are equal to zero. +*/ +inline bool QwtPoint3D::isNull() const +{ + return d_x == 0.0 && d_y == 0.0 && d_z == 0.0; +} + +//! Returns the x-coordinate of the point. +inline double QwtPoint3D::x() const +{ + return d_x; +} + +//! Returns the y-coordinate of the point. +inline double QwtPoint3D::y() const +{ + return d_y; +} + +//! Returns the z-coordinate of the point. +inline double QwtPoint3D::z() const +{ + return d_z; +} + +//! Returns a reference to the x-coordinate of the point. +inline double &QwtPoint3D::rx() +{ + return d_x; +} + +//! Returns a reference to the y-coordinate of the point. +inline double &QwtPoint3D::ry() +{ + return d_y; +} + +//! Returns a reference to the z-coordinate of the point. +inline double &QwtPoint3D::rz() +{ + return d_z; +} + +//! Sets the x-coordinate of the point to the value specified by x. +inline void QwtPoint3D::setX( double x ) +{ + d_x = x; +} + +//! Sets the y-coordinate of the point to the value specified by y. +inline void QwtPoint3D::setY( double y ) +{ + d_y = y; +} + +//! Sets the z-coordinate of the point to the value specified by z. +inline void QwtPoint3D::setZ( double z ) +{ + d_z = z; +} + +/*! + Rounds 2D point, where the z coordinate is dropped. +*/ +inline QPointF QwtPoint3D::toPoint() const +{ + return QPointF( d_x, d_y ); +} + +//! Returns true if this point and other are equal; otherwise returns false. +inline bool QwtPoint3D::operator==( const QwtPoint3D &other ) const +{ + return ( d_x == other.d_x ) && ( d_y == other.d_y ) && ( d_z == other.d_z ); +} + +//! Returns true if this rect and other are different; otherwise returns false. +inline bool QwtPoint3D::operator!=( const QwtPoint3D &other ) const +{ + return !operator==( other ); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_point_polar.cpp b/src/libpcp_qwt/src/qwt_point_polar.cpp new file mode 100644 index 0000000..83224ee --- /dev/null +++ b/src/libpcp_qwt/src/qwt_point_polar.cpp @@ -0,0 +1,114 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * QwtPolar Widget Library + * Copyright (C) 2008 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_point_polar.h" +#include "qwt_math.h" + +#if QT_VERSION < 0x040601 +#define qAtan2(y, x) ::atan2(y, x) +#endif + +/*! + Convert and assign values from a point in Cartesian coordinates + + \param p Point in Cartesian coordinates + \sa setPoint(), toPoint() +*/ +QwtPointPolar::QwtPointPolar( const QPointF &p ) +{ + d_radius = qSqrt( qwtSqr( p.x() ) + qwtSqr( p.y() ) ); + d_azimuth = qAtan2( p.y(), p.x() ); +} + +/*! + Convert and assign values from a point in Cartesian coordinates + \param p Point in Cartesian coordinates +*/ +void QwtPointPolar::setPoint( const QPointF &p ) +{ + d_radius = qSqrt( qwtSqr( p.x() ) + qwtSqr( p.y() ) ); + d_azimuth = qAtan2( p.y(), p.x() ); +} + +/*! + Convert and return values in Cartesian coordinates + + \note Invalid or null points will be returned as QPointF(0.0, 0.0) + \sa isValid(), isNull() +*/ +QPointF QwtPointPolar::toPoint() const +{ + if ( d_radius <= 0.0 ) + return QPointF( 0.0, 0.0 ); + + const double x = d_radius * qCos( d_azimuth ); + const double y = d_radius * qSin( d_azimuth ); + + return QPointF( x, y ); +} + +/*! + Returns true if point1 is equal to point2; otherwise returns false. + + Two points are equal to each other if radius and + azimuth-coordinates are the same. Points are not equal, when + the azimuth differs, but other.azimuth() == azimuth() % (2 * PI). + + \sa normalized() +*/ +bool QwtPointPolar::operator==( const QwtPointPolar &other ) const +{ + return d_radius == other.d_radius && d_azimuth == other.d_azimuth; +} + +/*! + Returns true if point1 is not equal to point2; otherwise returns false. + + Two points are equal to each other if radius and + azimuth-coordinates are the same. Points are not equal, when + the azimuth differs, but other.azimuth() == azimuth() % (2 * PI). + + \sa normalized() +*/ +bool QwtPointPolar::operator!=( const QwtPointPolar &other ) const +{ + return d_radius != other.d_radius || d_azimuth != other.d_azimuth; +} + +/*! + Normalize radius and azimuth + + When the radius is < 0.0 it is set to 0.0. The azimuth is + a value >= 0.0 and < 2 * M_PI. +*/ +QwtPointPolar QwtPointPolar::normalized() const +{ + const double radius = qMax( d_radius, 0.0 ); + + double azimuth = d_azimuth; + if ( azimuth < -2.0 * M_PI || azimuth >= 2 * M_PI ) + azimuth = ::fmod( d_azimuth, 2 * M_PI ); + + if ( azimuth < 0.0 ) + azimuth += 2 * M_PI; + + return QwtPointPolar( azimuth, radius ); +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<( QDebug debug, const QwtPointPolar &point ) +{ + debug.nospace() << "QwtPointPolar(" + << point.azimuth() << "," << point.radius() << ")"; + + return debug.space(); +} + +#endif + diff --git a/src/libpcp_qwt/src/qwt_point_polar.h b/src/libpcp_qwt/src/qwt_point_polar.h new file mode 100644 index 0000000..17c8121 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_point_polar.h @@ -0,0 +1,195 @@ +/* -*- 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 + *****************************************************************************/ + +/*! \file */ +#ifndef _QWT_POINT_POLAR_H_ +#define _QWT_POINT_POLAR_H_ 1 + +#include "qwt_global.h" +#include "qwt_math.h" +#include <qpoint.h> +#ifndef QT_NO_DEBUG_STREAM +#include <qdebug.h> +#endif + +/*! + \brief A point in polar coordinates + + In polar coordinates a point is determined by an angle and a distance. + See http://en.wikipedia.org/wiki/Polar_coordinate_system +*/ + +class QWT_EXPORT QwtPointPolar +{ +public: + QwtPointPolar(); + QwtPointPolar( double azimuth, double radius ); + QwtPointPolar( const QwtPointPolar & ); + QwtPointPolar( const QPointF & ); + + void setPoint( const QPointF & ); + QPointF toPoint() const; + + bool isValid() const; + bool isNull() const; + + double radius() const; + double azimuth() const; + + double &rRadius(); + double &rAzimuth(); + + void setRadius( double ); + void setAzimuth( double ); + + bool operator==( const QwtPointPolar & ) const; + bool operator!=( const QwtPointPolar & ) const; + + QwtPointPolar normalized() const; + +private: + double d_azimuth; + double d_radius; +}; + +/*! + Constructs a null point, with a radius and azimuth set to 0.0. + \sa QPointF::isNull +*/ +inline QwtPointPolar::QwtPointPolar(): + d_azimuth( 0.0 ), + d_radius( 0.0 ) +{ +} + +/*! + Constructs a point with coordinates specified by radius and azimuth. + + \param azimuth Azimuth + \param radius Radius +*/ +inline QwtPointPolar::QwtPointPolar( double azimuth, double radius ): + d_azimuth( azimuth ), + d_radius( radius ) +{ +} + +/*! + Constructs a point using the values of the point specified. + \param other Other point +*/ +inline QwtPointPolar::QwtPointPolar( const QwtPointPolar &other ): + d_azimuth( other.d_azimuth ), + d_radius( other.d_radius ) +{ +} + +//! Returns true if radius() >= 0.0 +inline bool QwtPointPolar::isValid() const +{ + return d_radius >= 0.0; +} + +//! Returns true if radius() >= 0.0 +inline bool QwtPointPolar::isNull() const +{ + return d_radius == 0.0; +} + +//! Returns the radius. +inline double QwtPointPolar::radius() const +{ + return d_radius; +} + +//! Returns the azimuth. +inline double QwtPointPolar::azimuth() const +{ + return d_azimuth; +} + +//! Returns the radius. +inline double &QwtPointPolar::rRadius() +{ + return d_radius; +} + +//! Returns the azimuth. +inline double &QwtPointPolar::rAzimuth() +{ + return d_azimuth; +} + +//! Sets the radius to radius. +inline void QwtPointPolar::setRadius( double radius ) +{ + d_radius = radius; +} + +//! Sets the atimuth to atimuth. +inline void QwtPointPolar::setAzimuth( double azimuth ) +{ + d_azimuth = azimuth; +} + +#ifndef QT_NO_DEBUG_STREAM +QWT_EXPORT QDebug operator<<( QDebug, const QwtPointPolar & ); +#endif + +inline QPoint qwtPolar2Pos( const QPoint &pole, + double radius, double angle ) +{ + const double x = pole.x() + radius * qCos( angle ); + const double y = pole.y() - radius * qSin( angle ); + + return QPoint( qRound( x ), qRound( y ) ); +} + +inline QPoint qwtDegree2Pos( const QPoint &pole, + double radius, double angle ) +{ + return qwtPolar2Pos( pole, radius, angle / 180.0 * M_PI ); +} + +inline QPointF qwtPolar2Pos( const QPointF &pole, + double radius, double angle ) +{ + const double x = pole.x() + radius * qCos( angle ); + const double y = pole.y() - radius * qSin( angle ); + + return QPointF( x, y); +} + +inline QPointF qwtDegree2Pos( const QPointF &pole, + double radius, double angle ) +{ + return qwtPolar2Pos( pole, radius, angle / 180.0 * M_PI ); +} + +inline QPointF qwtFastPolar2Pos( const QPointF &pole, + double radius, double angle ) +{ +#if QT_VERSION < 0x040601 + const double x = pole.x() + radius * ::cos( angle ); + const double y = pole.y() - radius * ::sin( angle ); +#else + const double x = pole.x() + radius * qFastCos( angle ); + const double y = pole.y() - radius * qFastSin( angle ); +#endif + + return QPointF( x, y); +} + +inline QPointF qwtFastDegree2Pos( const QPointF &pole, + double radius, double angle ) +{ + return qwtFastPolar2Pos( pole, radius, angle / 180.0 * M_PI ); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_raster_data.cpp b/src/libpcp_qwt/src/qwt_raster_data.cpp new file mode 100644 index 0000000..f148d81 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_raster_data.cpp @@ -0,0 +1,390 @@ +/* -*- 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_raster_data.h" +#include "qwt_point_3d.h" + +class QwtRasterData::ContourPlane +{ +public: + inline ContourPlane( double z ): + d_z( z ) + { + } + + inline bool intersect( const QwtPoint3D vertex[3], + QPointF line[2], bool ignoreOnPlane ) const; + + inline double z() const { return d_z; } + +private: + inline int compare( double z ) const; + inline QPointF intersection( + const QwtPoint3D& p1, const QwtPoint3D &p2 ) const; + + double d_z; +}; + +inline bool QwtRasterData::ContourPlane::intersect( + const QwtPoint3D vertex[3], QPointF line[2], + bool ignoreOnPlane ) const +{ + bool found = true; + + // Are the vertices below (-1), on (0) or above (1) the plan ? + const int eq1 = compare( vertex[0].z() ); + const int eq2 = compare( vertex[1].z() ); + const int eq3 = compare( vertex[2].z() ); + + /* + (a) All the vertices lie below the contour level. + (b) Two vertices lie below and one on the contour level. + (c) Two vertices lie below and one above the contour level. + (d) One vertex lies below and two on the contour level. + (e) One vertex lies below, one on and one above the contour level. + (f) One vertex lies below and two above the contour level. + (g) Three vertices lie on the contour level. + (h) Two vertices lie on and one above the contour level. + (i) One vertex lies on and two above the contour level. + (j) All the vertices lie above the contour level. + */ + + static const int tab[3][3][3] = + { + // jump table to avoid nested case statements + { { 0, 0, 8 }, { 0, 2, 5 }, { 7, 6, 9 } }, + { { 0, 3, 4 }, { 1, 10, 1 }, { 4, 3, 0 } }, + { { 9, 6, 7 }, { 5, 2, 0 }, { 8, 0, 0 } } + }; + + const int edgeType = tab[eq1+1][eq2+1][eq3+1]; + switch ( edgeType ) + { + case 1: + // d(0,0,-1), h(0,0,1) + line[0] = vertex[0].toPoint(); + line[1] = vertex[1].toPoint(); + break; + case 2: + // d(-1,0,0), h(1,0,0) + line[0] = vertex[1].toPoint(); + line[1] = vertex[2].toPoint(); + break; + case 3: + // d(0,-1,0), h(0,1,0) + line[0] = vertex[2].toPoint(); + line[1] = vertex[0].toPoint(); + break; + case 4: + // e(0,-1,1), e(0,1,-1) + line[0] = vertex[0].toPoint(); + line[1] = intersection( vertex[1], vertex[2] ); + break; + case 5: + // e(-1,0,1), e(1,0,-1) + line[0] = vertex[1].toPoint(); + line[1] = intersection( vertex[2], vertex[0] ); + break; + case 6: + // e(-1,1,0), e(1,0,-1) + line[0] = vertex[2].toPoint(); + line[1] = intersection( vertex[0], vertex[1] ); + break; + case 7: + // c(-1,1,-1), f(1,1,-1) + line[0] = intersection( vertex[0], vertex[1] ); + line[1] = intersection( vertex[1], vertex[2] ); + break; + case 8: + // c(-1,-1,1), f(1,1,-1) + line[0] = intersection( vertex[1], vertex[2] ); + line[1] = intersection( vertex[2], vertex[0] ); + break; + case 9: + // f(-1,1,1), c(1,-1,-1) + line[0] = intersection( vertex[2], vertex[0] ); + line[1] = intersection( vertex[0], vertex[1] ); + break; + case 10: + // g(0,0,0) + // The CONREC algorithm has no satisfying solution for + // what to do, when all vertices are on the plane. + + if ( ignoreOnPlane ) + found = false; + else + { + line[0] = vertex[2].toPoint(); + line[1] = vertex[0].toPoint(); + } + break; + default: + found = false; + } + + return found; +} + +inline int QwtRasterData::ContourPlane::compare( double z ) const +{ + if ( z > d_z ) + return 1; + + if ( z < d_z ) + return -1; + + return 0; +} + +inline QPointF QwtRasterData::ContourPlane::intersection( + const QwtPoint3D& p1, const QwtPoint3D &p2 ) const +{ + const double h1 = p1.z() - d_z; + const double h2 = p2.z() - d_z; + + const double x = ( h2 * p1.x() - h1 * p2.x() ) / ( h2 - h1 ); + const double y = ( h2 * p1.y() - h1 * p2.y() ) / ( h2 - h1 ); + + return QPointF( x, y ); +} + +//! Constructor +QwtRasterData::QwtRasterData() +{ +} + +//! Destructor +QwtRasterData::~QwtRasterData() +{ +} + +/*! + Set the bounding interval for the x, y or z coordinates. + + \param axis Axis + \param interval Bounding interval + + \sa interval() +*/ +void QwtRasterData::setInterval( Qt::Axis axis, const QwtInterval &interval ) +{ + d_intervals[axis] = interval; +} + +/*! + \brief Initialize a raster + + Before the composition of an image QwtPlotSpectrogram calls initRaster, + announcing the area and its resolution that will be requested. + + The default implementation does nothing, but for data sets that + are stored in files, it might be good idea to reimplement initRaster, + where the data is resampled and loaded into memory. + + \param area Area of the raster + \param raster Number of horizontal and vertical pixels + + \sa initRaster(), value() +*/ +void QwtRasterData::initRaster( const QRectF &area, const QSize &raster ) +{ + Q_UNUSED( area ); + Q_UNUSED( raster ); +} + +/*! + \brief Discard a raster + + After the composition of an image QwtPlotSpectrogram calls discardRaster(). + + The default implementation does nothing, but if data has been loaded + in initRaster(), it could deleted now. + + \sa initRaster(), value() +*/ +void QwtRasterData::discardRaster() +{ +} + +/*! + \brief Pixel hint + + pixelHint() returns the geometry of a pixel, that can be used + to calculate the resolution and alignment of the plot item, that is + representing the data. + + Width and height of the hint need to be the horizontal + and vertical distances between 2 neighboured points. + The center of the hint has to be the position of any point + ( it doesn't matter which one ). + + An empty hint indicates, that there are values for any detail level. + + Limiting the resolution of the image might significantly improve + the performance and heavily reduce the amount of memory when rendering + a QImage from the raster data. + + The default implementation returns an empty rectangle recommending + to render in target device ( f.e. screen ) resolution. + + \param area In most implementations the resolution of the data doesn't + depend on the requested area. + + \return Bounding rectangle of a pixel +*/ +QRectF QwtRasterData::pixelHint( const QRectF &area ) const +{ + Q_UNUSED( area ); + return QRectF(); +} + +/*! + Calculate contour lines + + An adaption of CONREC, a simple contouring algorithm. + http://local.wasp.uwa.edu.au/~pbourke/papers/conrec/ +*/ +QwtRasterData::ContourLines QwtRasterData::contourLines( + const QRectF &rect, const QSize &raster, + const QList<double> &levels, ConrecFlags flags ) const +{ + ContourLines contourLines; + + if ( levels.size() == 0 || !rect.isValid() || !raster.isValid() ) + return contourLines; + + const double dx = rect.width() / raster.width(); + const double dy = rect.height() / raster.height(); + + const bool ignoreOnPlane = + flags & QwtRasterData::IgnoreAllVerticesOnLevel; + + const QwtInterval range = interval( Qt::ZAxis ); + bool ignoreOutOfRange = false; + if ( range.isValid() ) + ignoreOutOfRange = flags & IgnoreOutOfRange; + + QwtRasterData *that = const_cast<QwtRasterData *>( this ); + that->initRaster( rect, raster ); + + for ( int y = 0; y < raster.height() - 1; y++ ) + { + enum Position + { + Center, + + TopLeft, + TopRight, + BottomRight, + BottomLeft, + + NumPositions + }; + + QwtPoint3D xy[NumPositions]; + + for ( int x = 0; x < raster.width() - 1; x++ ) + { + const QPointF pos( rect.x() + x * dx, rect.y() + y * dy ); + + if ( x == 0 ) + { + xy[TopRight].setX( pos.x() ); + xy[TopRight].setY( pos.y() ); + xy[TopRight].setZ( + value( xy[TopRight].x(), xy[TopRight].y() ) + ); + + xy[BottomRight].setX( pos.x() ); + xy[BottomRight].setY( pos.y() + dy ); + xy[BottomRight].setZ( + value( xy[BottomRight].x(), xy[BottomRight].y() ) + ); + } + + xy[TopLeft] = xy[TopRight]; + xy[BottomLeft] = xy[BottomRight]; + + xy[TopRight].setX( pos.x() + dx ); + xy[TopRight].setY( pos.y() ); + xy[BottomRight].setX( pos.x() + dx ); + xy[BottomRight].setY( pos.y() + dy ); + + xy[TopRight].setZ( + value( xy[TopRight].x(), xy[TopRight].y() ) + ); + xy[BottomRight].setZ( + value( xy[BottomRight].x(), xy[BottomRight].y() ) + ); + + double zMin = xy[TopLeft].z(); + double zMax = zMin; + double zSum = zMin; + + for ( int i = TopRight; i <= BottomLeft; i++ ) + { + const double z = xy[i].z(); + + zSum += z; + if ( z < zMin ) + zMin = z; + if ( z > zMax ) + zMax = z; + } + + if ( ignoreOutOfRange ) + { + if ( !range.contains( zMin ) || !range.contains( zMax ) ) + continue; + } + + if ( zMax < levels[0] || + zMin > levels[levels.size() - 1] ) + { + continue; + } + + xy[Center].setX( pos.x() + 0.5 * dx ); + xy[Center].setY( pos.y() + 0.5 * dy ); + xy[Center].setZ( 0.25 * zSum ); + + const int numLevels = levels.size(); + for ( int l = 0; l < numLevels; l++ ) + { + const double level = levels[l]; + if ( level < zMin || level > zMax ) + continue; + QPolygonF &lines = contourLines[level]; + const ContourPlane plane( level ); + + QPointF line[2]; + QwtPoint3D vertex[3]; + + for ( int m = TopLeft; m < NumPositions; m++ ) + { + vertex[0] = xy[m]; + vertex[1] = xy[0]; + vertex[2] = xy[m != BottomLeft ? m + 1 : TopLeft]; + + const bool intersects = + plane.intersect( vertex, line, ignoreOnPlane ); + if ( intersects ) + { + lines += line[0]; + lines += line[1]; + } + } + } + } + } + + that->discardRaster(); + + return contourLines; +} diff --git a/src/libpcp_qwt/src/qwt_raster_data.h b/src/libpcp_qwt/src/qwt_raster_data.h new file mode 100644 index 0000000..d8016c9 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_raster_data.h @@ -0,0 +1,95 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_RASTER_DATA_H +#define QWT_RASTER_DATA_H 1 + +#include "qwt_global.h" +#include "qwt_interval.h" +#include <qmap.h> +#include <qlist.h> +#include <qpolygon.h> + +class QwtScaleMap; + +/*! + \brief QwtRasterData defines an interface to any type of raster data. + + QwtRasterData is an abstract interface, that is used by + QwtPlotRasterItem to find the values at the pixels of its raster. + + Often a raster item is used to display values from a matrix. Then the + derived raster data class needs to implement some sort of resampling, + that maps the raster of the matrix into the requested raster of + the raster item ( depending on resolution and scales of the canvas ). +*/ +class QWT_EXPORT QwtRasterData +{ +public: + //! Contour lines + typedef QMap<double, QPolygonF> ContourLines; + + //! Flags to modify the contour algorithm + enum ConrecFlag + { + //! Ignore all verices on the same level + IgnoreAllVerticesOnLevel = 0x01, + + //! Ignore all values, that are out of range + IgnoreOutOfRange = 0x02 + }; + + //! Flags to modify the contour algorithm + typedef QFlags<ConrecFlag> ConrecFlags; + + QwtRasterData(); + virtual ~QwtRasterData(); + + virtual void setInterval( Qt::Axis, const QwtInterval & ); + const QwtInterval &interval(Qt::Axis) const; + + virtual QRectF pixelHint( const QRectF & ) const; + + virtual void initRaster( const QRectF &, const QSize& raster ); + virtual void discardRaster(); + + /*! + \return the value at a raster position + \param x X value in plot coordinates + \param y Y value in plot coordinates + */ + virtual double value( double x, double y ) const = 0; + + virtual ContourLines contourLines( const QRectF &rect, + const QSize &raster, const QList<double> &levels, + ConrecFlags ) const; + + class Contour3DPoint; + class ContourPlane; + +private: + // Disabled copy constructor and operator= + QwtRasterData( const QwtRasterData & ); + QwtRasterData &operator=( const QwtRasterData & ); + + QwtInterval d_intervals[3]; +}; + +/*! + \return Bounding interval for a axis + \sa setInterval +*/ +inline const QwtInterval &QwtRasterData::interval( Qt::Axis axis) const +{ + return d_intervals[axis]; +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtRasterData::ConrecFlags ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_round_scale_draw.cpp b/src/libpcp_qwt/src/qwt_round_scale_draw.cpp new file mode 100644 index 0000000..e59192b --- /dev/null +++ b/src/libpcp_qwt/src/qwt_round_scale_draw.cpp @@ -0,0 +1,309 @@ +/* -*- 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_round_scale_draw.h" +#include "qwt_painter.h" +#include "qwt_scale_div.h" +#include "qwt_scale_map.h" +#include <qpen.h> +#include <qpainter.h> +#include <qfontmetrics.h> +#include <qmath.h> + +class QwtRoundScaleDraw::PrivateData +{ +public: + PrivateData(): + center( 50.0, 50.0 ), + radius( 50.0 ), + startAngle( -135 * 16 ), + endAngle( 135 * 16 ) + { + } + + QPointF center; + double radius; + + double startAngle; + double endAngle; +}; + +/*! + \brief Constructor + + The range of the scale is initialized to [0, 100], + The center is set to (50, 50) with a radius of 50. + The angle range is set to [-135, 135]. +*/ +QwtRoundScaleDraw::QwtRoundScaleDraw() +{ + d_data = new QwtRoundScaleDraw::PrivateData; + + setRadius( 50 ); + scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle ); +} + +//! Destructor +QwtRoundScaleDraw::~QwtRoundScaleDraw() +{ + delete d_data; +} + +/*! + Change of radius the scale + + Radius is the radius of the backbone without ticks and labels. + + \param radius New Radius + \sa moveCenter() +*/ +void QwtRoundScaleDraw::setRadius( int radius ) +{ + d_data->radius = radius; +} + +/*! + Get the radius + + Radius is the radius of the backbone without ticks and labels. + + \sa setRadius(), extent() +*/ +int QwtRoundScaleDraw::radius() const +{ + return qCeil( d_data->radius ); +} + +/*! + Move the center of the scale draw, leaving the radius unchanged + + \param center New center + \sa setRadius() +*/ +void QwtRoundScaleDraw::moveCenter( const QPointF ¢er ) +{ + d_data->center = center; +} + +//! Get the center of the scale +QPointF QwtRoundScaleDraw::center() const +{ + return d_data->center; +} + +/*! + \brief Adjust the baseline circle segment for round scales. + + The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2). + The default setting is [ -135, 135 ]. + An angle of 0 degrees corresponds to the 12 o'clock position, + and positive angles count in a clockwise direction. + \param angle1 + \param angle2 boundaries of the angle interval in degrees. + \warning <ul> + <li>The angle range is limited to [-360, 360] degrees. Angles exceeding + this range will be clipped. + <li>For angles more than 359 degrees above or below min(angle1, angle2), + scale marks will not be drawn. + <li>If you need a counterclockwise scale, use QwtScaleDiv::setRange + </ul> +*/ +void QwtRoundScaleDraw::setAngleRange( double angle1, double angle2 ) +{ + angle1 = qBound( -360.0, angle1, 360.0 ); + angle2 = qBound( -360.0, angle2, 360.0 ); + + d_data->startAngle = angle1 * 16.0; + d_data->endAngle = angle2 * 16.0; + + if ( d_data->startAngle == d_data->endAngle ) + { + d_data->startAngle -= 1; + d_data->endAngle += 1; + } + + scaleMap().setPaintInterval( d_data->startAngle, d_data->endAngle ); +} + +/*! + Draws the label for a major scale tick + + \param painter Painter + \param value Value + + \sa drawTick(), drawBackbone() +*/ +void QwtRoundScaleDraw::drawLabel( QPainter *painter, double value ) const +{ + const QwtText label = tickLabel( painter->font(), value ); + if ( label.isEmpty() ) + return; + + const double tval = scaleMap().transform( value ); + if ( ( tval > d_data->startAngle + 359 * 16 ) + || ( tval < d_data->startAngle - 359 * 16 ) ) + { + return; + } + + double radius = d_data->radius; + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) || + hasComponent( QwtAbstractScaleDraw::Backbone ) ) + { + radius += spacing(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + radius += tickLength( QwtScaleDiv::MajorTick ); + + const QSizeF sz = label.textSize( painter->font() ); + const double arc = tval / 16.0 / 360.0 * 2 * M_PI; + + const double x = d_data->center.x() + + ( radius + sz.width() / 2.0 ) * qSin( arc ); + const double y = d_data->center.y() - + ( radius + sz.height() / 2.0 ) * cos( arc ); + + const QRectF r( x - sz.width() / 2, y - sz.height() / 2, + sz.width(), sz.height() ); + label.draw( painter, r ); +} + +/*! + Draw a tick + + \param painter Painter + \param value Value of the tick + \param len Lenght of the tick + + \sa drawBackbone(), drawLabel() +*/ +void QwtRoundScaleDraw::drawTick( QPainter *painter, double value, double len ) const +{ + if ( len <= 0 ) + return; + + const double tval = scaleMap().transform( value ); + + const double cx = d_data->center.x(); + const double cy = d_data->center.y(); + const double radius = d_data->radius; + + if ( ( tval <= d_data->startAngle + 359 * 16 ) + || ( tval >= d_data->startAngle - 359 * 16 ) ) + { + const double arc = double( tval ) / 16.0 * M_PI / 180.0; + + const double sinArc = qSin( arc ); + const double cosArc = qCos( arc ); + + const double x1 = cx + radius * sinArc; + const double x2 = cx + ( radius + len ) * sinArc; + const double y1 = cy - radius * cosArc; + const double y2 = cy - ( radius + len ) * cosArc; + + QwtPainter::drawLine( painter, x1, y1, x2, y2 ); + } +} + +/*! + Draws the baseline of the scale + \param painter Painter + + \sa drawTick(), drawLabel() +*/ +void QwtRoundScaleDraw::drawBackbone( QPainter *painter ) const +{ + const double deg1 = scaleMap().p1(); + const double deg2 = scaleMap().p2(); + + const int a1 = qRound( qMin( deg1, deg2 ) - 90 * 16 ); + const int a2 = qRound( qMax( deg1, deg2 ) - 90 * 16 ); + + const double radius = d_data->radius; + const double x = d_data->center.x() - radius; + const double y = d_data->center.y() - radius; + + painter->drawArc( QRectF( x, y, 2 * radius, 2 * radius ), + -a2, a2 - a1 + 1 ); // counterclockwise +} + +/*! + Calculate the extent of the scale + + The extent is the distance between the baseline to the outermost + pixel of the scale draw. radius() + extent() is an upper limit + for the radius of the bounding circle. + + \param font Font used for painting the labels + + \sa setMinimumExtent(), minimumExtent() + \warning The implemented algo is not too smart and + calculates only an upper limit, that might be a + few pixels too large +*/ +double QwtRoundScaleDraw::extent( const QFont &font ) const +{ + double d = 0.0; + + if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) + { + const QwtScaleDiv &sd = scaleDiv(); + const QList<double> &ticks = sd.ticks( QwtScaleDiv::MajorTick ); + for ( int i = 0; i < ticks.count(); i++ ) + { + const double value = ticks[i]; + if ( !sd.contains( value ) ) + continue; + + const QwtText label = tickLabel( font, value ); + if ( label.isEmpty() ) + continue; + + const double tval = scaleMap().transform( value ); + if ( ( tval < d_data->startAngle + 360 * 16 ) + && ( tval > d_data->startAngle - 360 * 16 ) ) + { + const double arc = tval / 16.0 / 360.0 * 2 * M_PI; + + const QSizeF sz = label.textSize( font ); + const double off = qMax( sz.width(), sz.height() ); + + double x = off * qSin( arc ); + double y = off * qCos( arc ); + + const double dist = qSqrt( x * x + y * y ); + if ( dist > d ) + d = dist; + } + } + } + + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + { + d += maxTickLength(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) + { + const double pw = qMax( 1, penWidth() ); // penwidth can be zero + d += pw; + } + + if ( hasComponent( QwtAbstractScaleDraw::Labels ) && + ( hasComponent( QwtAbstractScaleDraw::Ticks ) || + hasComponent( QwtAbstractScaleDraw::Backbone ) ) ) + { + d += spacing(); + } + + d = qMax( d, minimumExtent() ); + + return d; +} diff --git a/src/libpcp_qwt/src/qwt_round_scale_draw.h b/src/libpcp_qwt/src/qwt_round_scale_draw.h new file mode 100644 index 0000000..9e02060 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_round_scale_draw.h @@ -0,0 +1,68 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_ROUND_SCALE_DRAW_H +#define QWT_ROUND_SCALE_DRAW_H + +#include "qwt_global.h" +#include "qwt_abstract_scale_draw.h" +#include <qpoint.h> + +class QPen; + +/*! + \brief A class for drawing round scales + + QwtRoundScaleDraw can be used to draw round scales. + The circle segment can be adjusted by QwtRoundScaleDraw::setAngleRange(). + The geometry of the scale can be specified with + QwtRoundScaleDraw::moveCenter() and QwtRoundScaleDraw::setRadius(). + + After a scale division has been specified as a QwtScaleDiv object + using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s), + the scale can be drawn with the QwtAbstractScaleDraw::draw() member. +*/ + +class QWT_EXPORT QwtRoundScaleDraw: public QwtAbstractScaleDraw +{ +public: + QwtRoundScaleDraw(); + virtual ~QwtRoundScaleDraw(); + + void setRadius( int radius ); + int radius() const; + + void moveCenter( double x, double y ); + void moveCenter( const QPointF & ); + QPointF center() const; + + void setAngleRange( double angle1, double angle2 ); + + virtual double extent( const QFont & ) const; + +protected: + virtual void drawTick( QPainter *p, double val, double len ) const; + virtual void drawBackbone( QPainter *p ) const; + virtual void drawLabel( QPainter *p, double val ) const; + +private: + QwtRoundScaleDraw( const QwtRoundScaleDraw & ); + QwtRoundScaleDraw &operator=( const QwtRoundScaleDraw &other ); + + class PrivateData; + PrivateData *d_data; +}; + +//! Move the center of the scale draw, leaving the radius unchanged +inline void QwtRoundScaleDraw::moveCenter( double x, double y ) +{ + moveCenter( QPointF( x, y ) ); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_sampling_thread.cpp b/src/libpcp_qwt/src/qwt_sampling_thread.cpp new file mode 100644 index 0000000..4cffb3d --- /dev/null +++ b/src/libpcp_qwt/src/qwt_sampling_thread.cpp @@ -0,0 +1,106 @@ +/* -*- 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_sampling_thread.h" +#include "qwt_system_clock.h" + +class QwtSamplingThread::PrivateData +{ +public: + QwtSystemClock clock; + + double interval; + bool isStopped; +}; + + +//! Constructor +QwtSamplingThread::QwtSamplingThread( QObject *parent ): + QThread( parent ) +{ + d_data = new PrivateData; + d_data->interval = 1000; // 1 second + d_data->isStopped = true; +} + +//! Destructor +QwtSamplingThread::~QwtSamplingThread() +{ + delete d_data; +} + +/*! + Change the interval (in ms), when sample() is called. + The default interval is 1000.0 ( = 1s ) + + \param interval Interval + \sa interval() +*/ +void QwtSamplingThread::setInterval( double interval ) +{ + if ( interval < 0.0 ) + interval = 0.0; + + d_data->interval = interval; +} + +/*! + \return Interval (in ms), between 2 calls of sample() + \sa setInterval() +*/ +double QwtSamplingThread::interval() const +{ + return d_data->interval; +} + +/*! + \return Time (in ms) since the thread was started + \sa QThread::start(), run() +*/ +double QwtSamplingThread::elapsed() const +{ + if ( d_data->isStopped ) + return 0.0; + + return d_data->clock.elapsed(); +} + +/*! + Terminate the collecting thread + \sa QThread::start(), run() +*/ +void QwtSamplingThread::stop() +{ + d_data->isStopped = true; +} + +/*! + Loop collecting samples started from QThread::start() + \sa stop() +*/ +void QwtSamplingThread::run() +{ + d_data->clock.start(); + d_data->isStopped = false; + + while ( !d_data->isStopped ) + { + const double elapsed = d_data->clock.elapsed(); + sample( elapsed / 1000.0 ); + + if ( d_data->interval > 0.0 ) + { + const double msecs = + d_data->interval - ( d_data->clock.elapsed() - elapsed ); + + if ( msecs > 0.0 ) + usleep( qRound( 1000.0 * msecs ) ); + } + } +} diff --git a/src/libpcp_qwt/src/qwt_sampling_thread.h b/src/libpcp_qwt/src/qwt_sampling_thread.h new file mode 100644 index 0000000..85b876e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_sampling_thread.h @@ -0,0 +1,50 @@ +#ifndef _QWT_SAMPLING_THREAD_H_ +#define _QWT_SAMPLING_THREAD_H_ + +#include "qwt_global.h" +#include <qthread.h> + +/*! + \brief A thread collecting samples at regular intervals. + + Contiounous signals are converted into a discrete signal by + collecting samples at regular intervals. A discrete signal + can be displayed by a QwtPlotSeriesItem on a QwtPlot widget. + + QwtSamplingThread starts a thread calling perodically sample(), + to collect and store ( or emit ) a single sample. + + \sa QwtPlotCurve, QwtPlotSeriesItem +*/ +class QWT_EXPORT QwtSamplingThread: public QThread +{ + Q_OBJECT + +public: + virtual ~QwtSamplingThread(); + + double interval() const; + double elapsed() const; + +public Q_SLOTS: + void setInterval( double interval ); + void stop(); + +protected: + explicit QwtSamplingThread( QObject *parent = NULL ); + + virtual void run(); + + /*! + Collect a sample + + \param elapsed Time since the thread was started in miliseconds + */ + virtual void sample( double elapsed ) = 0; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_scale_div.cpp b/src/libpcp_qwt/src/qwt_scale_div.cpp new file mode 100644 index 0000000..fab2bcc --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_div.cpp @@ -0,0 +1,173 @@ +/* -*- 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_scale_div.h" +#include "qwt_math.h" +#include "qwt_interval.h" +#include <qalgorithms.h> + +//! Construct an invalid QwtScaleDiv instance. +QwtScaleDiv::QwtScaleDiv(): + d_lowerBound( 0.0 ), + d_upperBound( 0.0 ), + d_isValid( false ) +{ +} + +/*! + Construct QwtScaleDiv instance. + + \param interval Interval + \param ticks List of major, medium and minor ticks +*/ +QwtScaleDiv::QwtScaleDiv( const QwtInterval &interval, + QList<double> ticks[NTickTypes] ): + d_lowerBound( interval.minValue() ), + d_upperBound( interval.maxValue() ), + d_isValid( true ) +{ + for ( int i = 0; i < NTickTypes; i++ ) + d_ticks[i] = ticks[i]; +} + +/*! + Construct QwtScaleDiv instance. + + \param lowerBound First interval limit + \param upperBound Second interval limit + \param ticks List of major, medium and minor ticks +*/ +QwtScaleDiv::QwtScaleDiv( + double lowerBound, double upperBound, + QList<double> ticks[NTickTypes] ): + d_lowerBound( lowerBound ), + d_upperBound( upperBound ), + d_isValid( true ) +{ + for ( int i = 0; i < NTickTypes; i++ ) + d_ticks[i] = ticks[i]; +} + +/*! + Change the interval + \param interval Interval +*/ +void QwtScaleDiv::setInterval( const QwtInterval &interval ) +{ + setInterval( interval.minValue(), interval.maxValue() ); +} + +/*! + \brief Equality operator + \return true if this instance is equal to other +*/ +bool QwtScaleDiv::operator==( const QwtScaleDiv &other ) const +{ + if ( d_lowerBound != other.d_lowerBound || + d_upperBound != other.d_upperBound || + d_isValid != other.d_isValid ) + { + return false; + } + + for ( int i = 0; i < NTickTypes; i++ ) + { + if ( d_ticks[i] != other.d_ticks[i] ) + return false; + } + + return true; +} + +/*! + \brief Inequality + \return true if this instance is not equal to s +*/ +bool QwtScaleDiv::operator!=( const QwtScaleDiv &s ) const +{ + return ( !( *this == s ) ); +} + +//! Invalidate the scale division +void QwtScaleDiv::invalidate() +{ + d_isValid = false; + + // detach arrays + for ( int i = 0; i < NTickTypes; i++ ) + d_ticks[i].clear(); + + d_lowerBound = d_upperBound = 0; +} + +//! Check if the scale division is valid +bool QwtScaleDiv::isValid() const +{ + return d_isValid; +} + +/*! + Return if a value is between lowerBound() and upperBound() + + \param value Value + \return true/false +*/ +bool QwtScaleDiv::contains( double value ) const +{ + if ( !d_isValid ) + return false; + + const double min = qMin( d_lowerBound, d_upperBound ); + const double max = qMax( d_lowerBound, d_upperBound ); + + return value >= min && value <= max; +} + +//! Invert the scale divison +void QwtScaleDiv::invert() +{ + qSwap( d_lowerBound, d_upperBound ); + + for ( int i = 0; i < NTickTypes; i++ ) + { + QList<double>& ticks = d_ticks[i]; + + const int size = ticks.count(); + const int size2 = size / 2; + + for ( int i = 0; i < size2; i++ ) + qSwap( ticks[i], ticks[size - 1 - i] ); + } +} + +/*! + Assign ticks + + \param type MinorTick, MediumTick or MajorTick + \param ticks Values of the tick positions +*/ +void QwtScaleDiv::setTicks( int type, const QList<double> &ticks ) +{ + if ( type >= 0 && type < NTickTypes ) + d_ticks[type] = ticks; +} + +/*! + Return a list of ticks + + \param type MinorTick, MediumTick or MajorTick +*/ +const QList<double> &QwtScaleDiv::ticks( int type ) const +{ + if ( type >= 0 && type < NTickTypes ) + return d_ticks[type]; + + static QList<double> noTicks; + return noTicks; +} diff --git a/src/libpcp_qwt/src/qwt_scale_div.h b/src/libpcp_qwt/src/qwt_scale_div.h new file mode 100644 index 0000000..fee0824 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_div.h @@ -0,0 +1,132 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SCALE_DIV_H +#define QWT_SCALE_DIV_H + +#include "qwt_global.h" +#include "qwt_interval.h" +#include <qlist.h> + +class QwtInterval; + +/*! + \brief A class representing a scale division + + A scale division consists of its limits and 3 list + of tick values qualified as major, medium and minor ticks. + + In most cases scale divisions are calculated by a QwtScaleEngine. + + \sa subDivideInto(), subDivide() +*/ + +class QWT_EXPORT QwtScaleDiv +{ +public: + //! Scale tick types + enum TickType + { + //! No ticks + NoTick = -1, + + //! Minor ticks + MinorTick, + + //! Medium ticks + MediumTick, + + //! Major ticks + MajorTick, + + //! Number of valid tick types + NTickTypes + }; + + explicit QwtScaleDiv(); + explicit QwtScaleDiv( const QwtInterval &, QList<double>[NTickTypes] ); + explicit QwtScaleDiv( + double lowerBound, double upperBound, QList<double>[NTickTypes] ); + + bool operator==( const QwtScaleDiv &s ) const; + bool operator!=( const QwtScaleDiv &s ) const; + + void setInterval( double lowerBound, double upperBound ); + void setInterval( const QwtInterval & ); + QwtInterval interval() const; + + double lowerBound() const; + double upperBound() const; + double range() const; + + bool contains( double v ) const; + + void setTicks( int type, const QList<double> & ); + const QList<double> &ticks( int type ) const; + + void invalidate(); + bool isValid() const; + + void invert(); + +private: + double d_lowerBound; + double d_upperBound; + QList<double> d_ticks[NTickTypes]; + + bool d_isValid; +}; + +Q_DECLARE_TYPEINFO(QwtScaleDiv, Q_MOVABLE_TYPE); + +/*! + Change the interval + \param lowerBound lower bound + \param upperBound upper bound +*/ +inline void QwtScaleDiv::setInterval( double lowerBound, double upperBound ) +{ + d_lowerBound = lowerBound; + d_upperBound = upperBound; +} + +/*! + \return lowerBound -> upperBound +*/ +inline QwtInterval QwtScaleDiv::interval() const +{ + return QwtInterval( d_lowerBound, d_upperBound ); +} + +/*! + \return lower bound + \sa upperBound() +*/ +inline double QwtScaleDiv::lowerBound() const +{ + return d_lowerBound; +} + +/*! + \return upper bound + \sa lowerBound() +*/ +inline double QwtScaleDiv::upperBound() const +{ + return d_upperBound; +} + +/*! + \return upperBound() - lowerBound() +*/ +inline double QwtScaleDiv::range() const +{ + return d_upperBound - d_lowerBound; +} +#endif diff --git a/src/libpcp_qwt/src/qwt_scale_draw.cpp b/src/libpcp_qwt/src/qwt_scale_draw.cpp new file mode 100644 index 0000000..9a9b05b --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_draw.cpp @@ -0,0 +1,903 @@ +/* -*- 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_scale_draw.h" +#include "qwt_scale_div.h" +#include "qwt_scale_map.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include <qpen.h> +#include <qpainter.h> +#include <qmath.h> + +#if QT_VERSION < 0x040601 +#define qFastSin(x) qSin(x) +#define qFastCos(x) qCos(x) +#endif + +class QwtScaleDraw::PrivateData +{ +public: + PrivateData(): + len( 0 ), + alignment( QwtScaleDraw::BottomScale ), + labelAlignment( 0 ), + labelRotation( 0.0 ) + { + } + + QPointF pos; + double len; + + Alignment alignment; + + Qt::Alignment labelAlignment; + double labelRotation; +}; + +/*! + \brief Constructor + + The range of the scale is initialized to [0, 100], + The position is at (0, 0) with a length of 100. + The orientation is QwtAbstractScaleDraw::Bottom. +*/ +QwtScaleDraw::QwtScaleDraw() +{ + d_data = new QwtScaleDraw::PrivateData; + setLength( 100 ); +} + +//! Destructor +QwtScaleDraw::~QwtScaleDraw() +{ + delete d_data; +} + +/*! + Return alignment of the scale + \sa setAlignment() +*/ +QwtScaleDraw::Alignment QwtScaleDraw::alignment() const +{ + return d_data->alignment; +} + +/*! + Set the alignment of the scale + + The default alignment is QwtScaleDraw::BottomScale + \sa alignment() +*/ +void QwtScaleDraw::setAlignment( Alignment align ) +{ + d_data->alignment = align; +} + +/*! + Return the orientation + + TopScale, BottomScale are horizontal (Qt::Horizontal) scales, + LeftScale, RightScale are vertical (Qt::Vertical) scales. + + \sa alignment() +*/ +Qt::Orientation QwtScaleDraw::orientation() const +{ + switch ( d_data->alignment ) + { + case TopScale: + case BottomScale: + return Qt::Horizontal; + case LeftScale: + case RightScale: + default: + return Qt::Vertical; + } +} + +/*! + \brief Determine the minimum border distance + + This member function returns the minimum space + needed to draw the mark labels at the scale's endpoints. + + \param font Font + \param start Start border distance + \param end End border distance +*/ +void QwtScaleDraw::getBorderDistHint( const QFont &font, + int &start, int &end ) const +{ + start = 0; + end = 0; + + if ( !hasComponent( QwtAbstractScaleDraw::Labels ) ) + return; + + const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick ); + if ( ticks.count() == 0 ) + return; + + // Find the ticks, that are mapped to the borders. + // minTick is the tick, that is mapped to the top/left-most position + // in widget coordinates. + + double minTick = ticks[0]; + double minPos = scaleMap().transform( minTick ); + double maxTick = minTick; + double maxPos = minPos; + + for ( int i = 1; i < ticks.count(); i++ ) + { + const double tickPos = scaleMap().transform( ticks[i] ); + if ( tickPos < minPos ) + { + minTick = ticks[i]; + minPos = tickPos; + } + if ( tickPos > scaleMap().transform( maxTick ) ) + { + maxTick = ticks[i]; + maxPos = tickPos; + } + } + + double e = 0.0; + double s = 0.0; + if ( orientation() == Qt::Vertical ) + { + s = -labelRect( font, minTick ).top(); + s -= qAbs( minPos - qRound( scaleMap().p2() ) ); + + e = labelRect( font, maxTick ).bottom(); + e -= qAbs( maxPos - scaleMap().p1() ); + } + else + { + s = -labelRect( font, minTick ).left(); + s -= qAbs( minPos - scaleMap().p1() ); + + e = labelRect( font, maxTick ).right(); + e -= qAbs( maxPos - scaleMap().p2() ); + } + + if ( s < 0.0 ) + s = 0.0; + if ( e < 0.0 ) + e = 0.0; + + start = qCeil( s ); + end = qCeil( e ); +} + +/*! + Determine the minimum distance between two labels, that is necessary + that the texts don't overlap. + + \param font Font + \return The maximum width of a label + + \sa getBorderDistHint() +*/ + +int QwtScaleDraw::minLabelDist( const QFont &font ) const +{ + if ( !hasComponent( QwtAbstractScaleDraw::Labels ) ) + return 0; + + const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick ); + if ( ticks.isEmpty() ) + return 0; + + const QFontMetrics fm( font ); + + const bool vertical = ( orientation() == Qt::Vertical ); + + QRectF bRect1; + QRectF bRect2 = labelRect( font, ticks[0] ); + if ( vertical ) + { + bRect2.setRect( -bRect2.bottom(), 0.0, bRect2.height(), bRect2.width() ); + } + + double maxDist = 0.0; + + for ( int i = 1; i < ticks.count(); i++ ) + { + bRect1 = bRect2; + bRect2 = labelRect( font, ticks[i] ); + if ( vertical ) + { + bRect2.setRect( -bRect2.bottom(), 0.0, + bRect2.height(), bRect2.width() ); + } + + double dist = fm.leading(); // space between the labels + if ( bRect1.right() > 0 ) + dist += bRect1.right(); + if ( bRect2.left() < 0 ) + dist += -bRect2.left(); + + if ( dist > maxDist ) + maxDist = dist; + } + + double angle = labelRotation() / 180.0 * M_PI; + if ( vertical ) + angle += M_PI / 2; + + const double sinA = qFastSin( angle ); // qreal -> double + if ( qFuzzyCompare( sinA + 1.0, 1.0 ) ) + return qCeil( maxDist ); + + const int fmHeight = fm.ascent() - 2; + + // The distance we need until there is + // the height of the label font. This height is needed + // for the neighbour labal. + + double labelDist = fmHeight / qFastSin( angle ) * qFastCos( angle ); + if ( labelDist < 0 ) + labelDist = -labelDist; + + // For text orientations close to the scale orientation + + if ( labelDist > maxDist ) + labelDist = maxDist; + + // For text orientations close to the opposite of the + // scale orientation + + if ( labelDist < fmHeight ) + labelDist = fmHeight; + + return qCeil( labelDist ); +} + +/*! + Calculate the width/height that is needed for a + vertical/horizontal scale. + + The extent is calculated from the pen width of the backbone, + the major tick length, the spacing and the maximum width/height + of the labels. + + \param font Font used for painting the labels + + \sa minLength() +*/ +double QwtScaleDraw::extent( const QFont &font ) const +{ + double d = 0; + + if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) + { + if ( orientation() == Qt::Vertical ) + d = maxLabelWidth( font ); + else + d = maxLabelHeight( font ); + + if ( d > 0 ) + d += spacing(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + { + d += maxTickLength(); + } + + if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) + { + const double pw = qMax( 1, penWidth() ); // penwidth can be zero + d += pw; + } + + d = qMax( d, minimumExtent() ); + return d; +} + +/*! + Calculate the minimum length that is needed to draw the scale + + \param font Font used for painting the labels + + \sa extent() +*/ +int QwtScaleDraw::minLength( const QFont &font ) const +{ + int startDist, endDist; + getBorderDistHint( font, startDist, endDist ); + + const QwtScaleDiv &sd = scaleDiv(); + + const uint minorCount = + sd.ticks( QwtScaleDiv::MinorTick ).count() + + sd.ticks( QwtScaleDiv::MediumTick ).count(); + const uint majorCount = + sd.ticks( QwtScaleDiv::MajorTick ).count(); + + int lengthForLabels = 0; + if ( hasComponent( QwtAbstractScaleDraw::Labels ) ) + lengthForLabels = minLabelDist( font ) * majorCount; + + int lengthForTicks = 0; + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + { + const double pw = qMax( 1, penWidth() ); // penwidth can be zero + lengthForTicks = qCeil( ( majorCount + minorCount ) * ( pw + 1.0 ) ); + } + + return startDist + endDist + qMax( lengthForLabels, lengthForTicks ); +} + +/*! + Find the position, where to paint a label + + The position has a distance of majTickLength() + spacing() + 1 + from the backbone. The direction depends on the alignment() + + \param value Value +*/ +QPointF QwtScaleDraw::labelPosition( double value ) const +{ + const double tval = scaleMap().transform( value ); + double dist = spacing(); + if ( hasComponent( QwtAbstractScaleDraw::Backbone ) ) + dist += qMax( 1, penWidth() ); + + if ( hasComponent( QwtAbstractScaleDraw::Ticks ) ) + dist += tickLength( QwtScaleDiv::MajorTick ); + + double px = 0; + double py = 0; + + switch ( alignment() ) + { + case RightScale: + { + px = d_data->pos.x() + dist; + py = tval; + break; + } + case LeftScale: + { + px = d_data->pos.x() - dist; + py = tval; + break; + } + case BottomScale: + { + px = tval; + py = d_data->pos.y() + dist; + break; + } + case TopScale: + { + px = tval; + py = d_data->pos.y() - dist; + break; + } + } + + return QPointF( px, py ); +} + +/*! + Draw a tick + + \param painter Painter + \param value Value of the tick + \param len Lenght of the tick + + \sa drawBackbone(), drawLabel() +*/ +void QwtScaleDraw::drawTick( QPainter *painter, double value, double len ) const +{ + if ( len <= 0 ) + return; + + const bool roundingAlignment = QwtPainter::roundingAlignment( painter ); + + QPointF pos = d_data->pos; + + double tval = scaleMap().transform( value ); + if ( roundingAlignment ) + tval = qRound( tval ); + + const int pw = penWidth(); + int a = 0; + if ( pw > 1 && roundingAlignment ) + a = 1; + + switch ( alignment() ) + { + case LeftScale: + { + double x1 = pos.x() + a; + double x2 = pos.x() + a - pw - len; + if ( roundingAlignment ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + QwtPainter::drawLine( painter, x1, tval, x2, tval ); + break; + } + + case RightScale: + { + double x1 = pos.x(); + double x2 = pos.x() + pw + len; + if ( roundingAlignment ) + { + x1 = qRound( x1 ); + x2 = qRound( x2 ); + } + + QwtPainter::drawLine( painter, x1, tval, x2, tval ); + break; + } + + case BottomScale: + { + double y1 = pos.y(); + double y2 = pos.y() + pw + len; + if ( roundingAlignment ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + QwtPainter::drawLine( painter, tval, y1, tval, y2 ); + break; + } + + case TopScale: + { + double y1 = pos.y() + a; + double y2 = pos.y() - pw - len + a; + if ( roundingAlignment ) + { + y1 = qRound( y1 ); + y2 = qRound( y2 ); + } + + QwtPainter::drawLine( painter, tval, y1, tval, y2 ); + break; + } + } +} + +/*! + Draws the baseline of the scale + \param painter Painter + + \sa drawTick(), drawLabel() +*/ +void QwtScaleDraw::drawBackbone( QPainter *painter ) const +{ + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + const QPointF &pos = d_data->pos; + const double len = d_data->len; + const int pw = qMax( penWidth(), 1 ); + + // pos indicates a border not the center of the backbone line + // so we need to shift its position depending on the pen width + // and the alignment of the scale + + double off; + if ( doAlign ) + { + if ( alignment() == LeftScale || alignment() == TopScale ) + off = ( pw - 1 ) / 2; + else + off = pw / 2; + } + else + { + off = 0.5 * penWidth(); + } + + switch ( alignment() ) + { + case LeftScale: + { + double x = pos.x() - off; + if ( doAlign ) + x = qRound( x ); + + QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len ); + break; + } + case RightScale: + { + double x = pos.x() + off; + if ( doAlign ) + x = qRound( x ); + + QwtPainter::drawLine( painter, x, pos.y(), x, pos.y() + len ); + break; + } + case TopScale: + { + double y = pos.y() - off; + if ( doAlign ) + y = qRound( y ); + + QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y ); + break; + } + case BottomScale: + { + double y = pos.y() + off; + if ( doAlign ) + y = qRound( y ); + + QwtPainter::drawLine( painter, pos.x(), y, pos.x() + len, y ); + break; + } + } +} + +/*! + \brief Move the position of the scale + + The meaning of the parameter pos depends on the alignment: + <dl> + <dt>QwtScaleDraw::LeftScale + <dd>The origin is the topmost point of the + backbone. The backbone is a vertical line. + Scale marks and labels are drawn + at the left of the backbone. + <dt>QwtScaleDraw::RightScale + <dd>The origin is the topmost point of the + backbone. The backbone is a vertical line. + Scale marks and labels are drawn + at the right of the backbone. + <dt>QwtScaleDraw::TopScale + <dd>The origin is the leftmost point of the + backbone. The backbone is a horizontal line. + Scale marks and labels are drawn + above the backbone. + <dt>QwtScaleDraw::BottomScale + <dd>The origin is the leftmost point of the + backbone. The backbone is a horizontal line + Scale marks and labels are drawn + below the backbone. + </dl> + + \param pos Origin of the scale + + \sa pos(), setLength() +*/ +void QwtScaleDraw::move( const QPointF &pos ) +{ + d_data->pos = pos; + updateMap(); +} + +/*! + \return Origin of the scale + \sa move(), length() +*/ +QPointF QwtScaleDraw::pos() const +{ + return d_data->pos; +} + +/*! + Set the length of the backbone. + + The length doesn't include the space needed for + overlapping labels. + + \sa move(), minLabelDist() +*/ +void QwtScaleDraw::setLength( double length ) +{ + if ( length >= 0 && length < 10 ) + length = 10; + if ( length < 0 && length > -10 ) + length = -10; + + d_data->len = length; + updateMap(); +} + +/*! + \return the length of the backbone + \sa setLength(), pos() +*/ +double QwtScaleDraw::length() const +{ + return d_data->len; +} + +/*! + Draws the label for a major scale tick + + \param painter Painter + \param value Value + + \sa drawTick(), drawBackbone(), boundingLabelRect() +*/ +void QwtScaleDraw::drawLabel( QPainter *painter, double value ) const +{ + QwtText lbl = tickLabel( painter->font(), value ); + if ( lbl.isEmpty() ) + return; + + QPointF pos = labelPosition( value ); + + QSizeF labelSize = lbl.textSize( painter->font() ); + + const QTransform transform = labelTransformation( pos, labelSize ); + + painter->save(); + painter->setWorldTransform( transform, true ); + + lbl.draw ( painter, QRect( QPoint( 0, 0 ), labelSize.toSize() ) ); + + painter->restore(); +} + +/*! + Find the bounding rect for the label. The coordinates of + the rect are absolute coordinates ( calculated from pos() ). + in direction of the tick. + + \param font Font used for painting + \param value Value + + \sa labelRect() +*/ +QRect QwtScaleDraw::boundingLabelRect( const QFont &font, double value ) const +{ + QwtText lbl = tickLabel( font, value ); + if ( lbl.isEmpty() ) + return QRect(); + + const QPointF pos = labelPosition( value ); + QSizeF labelSize = lbl.textSize( font ); + + const QTransform transform = labelTransformation( pos, labelSize ); + return transform.mapRect( QRect( QPoint( 0, 0 ), labelSize.toSize() ) ); +} + +/*! + Calculate the transformation that is needed to paint a label + depending on its alignment and rotation. + + \param pos Position where to paint the label + \param size Size of the label + + \sa setLabelAlignment(), setLabelRotation() +*/ +QTransform QwtScaleDraw::labelTransformation( + const QPointF &pos, const QSizeF &size ) const +{ + QTransform transform; + transform.translate( pos.x(), pos.y() ); + transform.rotate( labelRotation() ); + + int flags = labelAlignment(); + if ( flags == 0 ) + { + switch ( alignment() ) + { + case RightScale: + { + if ( flags == 0 ) + flags = Qt::AlignRight | Qt::AlignVCenter; + break; + } + case LeftScale: + { + if ( flags == 0 ) + flags = Qt::AlignLeft | Qt::AlignVCenter; + break; + } + case BottomScale: + { + if ( flags == 0 ) + flags = Qt::AlignHCenter | Qt::AlignBottom; + break; + } + case TopScale: + { + if ( flags == 0 ) + flags = Qt::AlignHCenter | Qt::AlignTop; + break; + } + } + } + + double x, y; + + if ( flags & Qt::AlignLeft ) + x = -size.width(); + else if ( flags & Qt::AlignRight ) + x = 0.0; + else // Qt::AlignHCenter + x = -( 0.5 * size.width() ); + + if ( flags & Qt::AlignTop ) + y = -size.height(); + else if ( flags & Qt::AlignBottom ) + y = 0; + else // Qt::AlignVCenter + y = -( 0.5 * size.height() ); + + transform.translate( x, y ); + + return transform; +} + +/*! + Find the bounding rect for the label. The coordinates of + the rect are relative to spacing + ticklength from the backbone + in direction of the tick. + + \param font Font used for painting + \param value Value +*/ +QRectF QwtScaleDraw::labelRect( const QFont &font, double value ) const +{ + QwtText lbl = tickLabel( font, value ); + if ( lbl.isEmpty() ) + return QRectF( 0.0, 0.0, 0.0, 0.0 ); + + const QPointF pos = labelPosition( value ); + + const QSizeF labelSize = lbl.textSize( font ); + const QTransform transform = labelTransformation( pos, labelSize ); + + QRectF br = transform.mapRect( QRectF( QPointF( 0, 0 ), labelSize ) ); + br.translate( -pos.x(), -pos.y() ); + + return br; +} + +/*! + Calculate the size that is needed to draw a label + + \param font Label font + \param value Value +*/ +QSizeF QwtScaleDraw::labelSize( const QFont &font, double value ) const +{ + return labelRect( font, value ).size(); +} + +/*! + Rotate all labels. + + When changing the rotation, it might be necessary to + adjust the label flags too. Finding a useful combination is + often the result of try and error. + + \param rotation Angle in degrees. When changing the label rotation, + the label flags often needs to be adjusted too. + + \sa setLabelAlignment(), labelRotation(), labelAlignment(). + +*/ +void QwtScaleDraw::setLabelRotation( double rotation ) +{ + d_data->labelRotation = rotation; +} + +/*! + \return the label rotation + \sa setLabelRotation(), labelAlignment() +*/ +double QwtScaleDraw::labelRotation() const +{ + return d_data->labelRotation; +} + +/*! + \brief Change the label flags + + Labels are aligned to the point ticklength + spacing away from the backbone. + + The alignment is relative to the orientation of the label text. + In case of an flags of 0 the label will be aligned + depending on the orientation of the scale: + + QwtScaleDraw::TopScale: Qt::AlignHCenter | Qt::AlignTop\n + QwtScaleDraw::BottomScale: Qt::AlignHCenter | Qt::AlignBottom\n + QwtScaleDraw::LeftScale: Qt::AlignLeft | Qt::AlignVCenter\n + QwtScaleDraw::RightScale: Qt::AlignRight | Qt::AlignVCenter\n + + Changing the alignment is often necessary for rotated labels. + + \param alignment Or'd Qt::AlignmentFlags see <qnamespace.h> + + \sa setLabelRotation(), labelRotation(), labelAlignment() + \warning The various alignments might be confusing. + The alignment of the label is not the alignment + of the scale and is not the alignment of the flags + (QwtText::flags()) returned from QwtAbstractScaleDraw::label(). +*/ + +void QwtScaleDraw::setLabelAlignment( Qt::Alignment alignment ) +{ + d_data->labelAlignment = alignment; +} + +/*! + \return the label flags + \sa setLabelAlignment(), labelRotation() +*/ +Qt::Alignment QwtScaleDraw::labelAlignment() const +{ + return d_data->labelAlignment; +} + +/*! + \param font Font + \return the maximum width of a label +*/ +int QwtScaleDraw::maxLabelWidth( const QFont &font ) const +{ + double maxWidth = 0.0; + + const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick ); + for ( int i = 0; i < ticks.count(); i++ ) + { + const double v = ticks[i]; + if ( scaleDiv().contains( v ) ) + { + const double w = labelSize( font, ticks[i] ).width(); + if ( w > maxWidth ) + maxWidth = w; + } + } + + return qCeil( maxWidth ); +} + +/*! + \param font Font + \return the maximum height of a label +*/ +int QwtScaleDraw::maxLabelHeight( const QFont &font ) const +{ + double maxHeight = 0.0; + + const QList<double> &ticks = scaleDiv().ticks( QwtScaleDiv::MajorTick ); + for ( int i = 0; i < ticks.count(); i++ ) + { + const double v = ticks[i]; + if ( scaleDiv().contains( v ) ) + { + const double h = labelSize( font, ticks[i] ).height(); + if ( h > maxHeight ) + maxHeight = h; + } + } + + return qCeil( maxHeight ); +} + +void QwtScaleDraw::updateMap() +{ + const QPointF pos = d_data->pos; + double len = d_data->len; + + QwtScaleMap &sm = scaleMap(); + if ( orientation() == Qt::Vertical ) + sm.setPaintInterval( pos.y() + len, pos.y() ); + else + sm.setPaintInterval( pos.x(), pos.x() + len ); +} diff --git a/src/libpcp_qwt/src/qwt_scale_draw.h b/src/libpcp_qwt/src/qwt_scale_draw.h new file mode 100644 index 0000000..8eeb71b --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_draw.h @@ -0,0 +1,117 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SCALE_DRAW_H +#define QWT_SCALE_DRAW_H + +#include "qwt_global.h" +#include "qwt_abstract_scale_draw.h" +#include <qpoint.h> +#include <qrect.h> +#include <qtransform.h> + +/*! + \brief A class for drawing scales + + QwtScaleDraw can be used to draw linear or logarithmic scales. + A scale has a position, an alignment and a length, which can be specified . + The labels can be rotated and aligned + to the ticks using setLabelRotation() and setLabelAlignment(). + + After a scale division has been specified as a QwtScaleDiv object + using QwtAbstractScaleDraw::setScaleDiv(const QwtScaleDiv &s), + the scale can be drawn with the QwtAbstractScaleDraw::draw() member. +*/ + +class QWT_EXPORT QwtScaleDraw: public QwtAbstractScaleDraw +{ +public: + /*! + Alignment of the scale draw + \sa setAlignment(), alignment() + */ + enum Alignment + { + //! The scale is below + BottomScale, + + //! The scale is above + TopScale, + + //! The scale is left + LeftScale, + + //! The scale is right + RightScale + }; + + QwtScaleDraw(); + virtual ~QwtScaleDraw(); + + void getBorderDistHint( const QFont &, int &start, int &end ) const; + int minLabelDist( const QFont & ) const; + + int minLength( const QFont & ) const; + virtual double extent( const QFont & ) const; + + void move( double x, double y ); + void move( const QPointF & ); + void setLength( double length ); + + Alignment alignment() const; + void setAlignment( Alignment ); + + Qt::Orientation orientation() const; + + QPointF pos() const; + double length() const; + + void setLabelAlignment( Qt::Alignment ); + Qt::Alignment labelAlignment() const; + + void setLabelRotation( double rotation ); + double labelRotation() const; + + int maxLabelHeight( const QFont & ) const; + int maxLabelWidth( const QFont & ) const; + + QPointF labelPosition( double val ) const; + + QRectF labelRect( const QFont &, double val ) const; + QSizeF labelSize( const QFont &, double val ) const; + + QRect boundingLabelRect( const QFont &, double val ) const; + +protected: + QTransform labelTransformation( const QPointF &, const QSizeF & ) const; + + virtual void drawTick( QPainter *, double val, double len ) const; + virtual void drawBackbone( QPainter * ) const; + virtual void drawLabel( QPainter *, double val ) const; + +private: + QwtScaleDraw( const QwtScaleDraw & ); + QwtScaleDraw &operator=( const QwtScaleDraw &other ); + + void updateMap(); + + class PrivateData; + PrivateData *d_data; +}; + +/*! + Move the position of the scale + \sa move(const QPointF &) +*/ +inline void QwtScaleDraw::move( double x, double y ) +{ + move( QPointF( x, y ) ); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_scale_engine.cpp b/src/libpcp_qwt/src/qwt_scale_engine.cpp new file mode 100644 index 0000000..9e5e97e --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_engine.cpp @@ -0,0 +1,967 @@ +/* -*- 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_scale_engine.h" +#include "qwt_math.h" +#include "qwt_scale_map.h" +#include <qalgorithms.h> +#include <qmath.h> +#include <float.h> + +#if QT_VERSION < 0x040601 +#define qFabs(x) ::fabs(x) +#define qExp(x) ::exp(x) +#endif + +static const double _eps = 1.0e-6; + +/*! + Ceil a value, relative to an interval + + \param value Value to ceil + \param intervalSize Interval size + + \sa floorEps() +*/ +double QwtScaleArithmetic::ceilEps( double value, + double intervalSize ) +{ + const double eps = _eps * intervalSize; + + value = ( value - eps ) / intervalSize; + return ::ceil( value ) * intervalSize; +} + +/*! + Floor a value, relative to an interval + + \param value Value to floor + \param intervalSize Interval size + + \sa floorEps() +*/ +double QwtScaleArithmetic::floorEps( double value, double intervalSize ) +{ + const double eps = _eps * intervalSize; + + value = ( value + eps ) / intervalSize; + return ::floor( value ) * intervalSize; +} + +/*! + \brief Divide an interval into steps + + \f$stepSize = (intervalSize - intervalSize * 10e^{-6}) / numSteps\f$ + + \param intervalSize Interval size + \param numSteps Number of steps + \return Step size +*/ +double QwtScaleArithmetic::divideEps( double intervalSize, double numSteps ) +{ + if ( numSteps == 0.0 || intervalSize == 0.0 ) + return 0.0; + + return ( intervalSize - ( _eps * intervalSize ) ) / numSteps; +} + +/*! + Find the smallest value out of {1,2,5}*10^n with an integer number n + which is greater than or equal to x + + \param x Input value +*/ +double QwtScaleArithmetic::ceil125( double x ) +{ + if ( x == 0.0 ) + return 0.0; + + const double sign = ( x > 0 ) ? 1.0 : -1.0; + const double lx = ::log10( qFabs( x ) ); + const double p10 = ::floor( lx ); + + double fr = qPow( 10.0, lx - p10 ); + if ( fr <= 1.0 ) + fr = 1.0; + else if ( fr <= 2.0 ) + fr = 2.0; + else if ( fr <= 5.0 ) + fr = 5.0; + else + fr = 10.0; + + return sign * fr * qPow( 10.0, p10 ); +} + +/*! + \brief Find the largest value out of {1,2,5}*10^n with an integer number n + which is smaller than or equal to x + + \param x Input value +*/ +double QwtScaleArithmetic::floor125( double x ) +{ + if ( x == 0.0 ) + return 0.0; + + double sign = ( x > 0 ) ? 1.0 : -1.0; + const double lx = ::log10( qFabs( x ) ); + const double p10 = ::floor( lx ); + + double fr = qPow( 10.0, lx - p10 ); + if ( fr >= 10.0 ) + fr = 10.0; + else if ( fr >= 5.0 ) + fr = 5.0; + else if ( fr >= 2.0 ) + fr = 2.0; + else + fr = 1.0; + + return sign * fr * qPow( 10.0, p10 ); +} + +class QwtScaleEngine::PrivateData +{ +public: + PrivateData(): + attributes( QwtScaleEngine::NoAttribute ), + lowerMargin( 0.0 ), + upperMargin( 0.0 ), + referenceValue( 0.0 ) + { + } + + QwtScaleEngine::Attributes attributes; // scale attributes + + double lowerMargin; // margins + double upperMargin; + + double referenceValue; // reference value + +}; + +//! Constructor +QwtScaleEngine::QwtScaleEngine() +{ + d_data = new PrivateData; +} + + +//! Destructor +QwtScaleEngine::~QwtScaleEngine () +{ + delete d_data; +} + +/*! + \return the margin at the lower end of the scale + The default margin is 0. + + \sa setMargins() +*/ +double QwtScaleEngine::lowerMargin() const +{ + return d_data->lowerMargin; +} + +/*! + \return the margin at the upper end of the scale + The default margin is 0. + + \sa setMargins() +*/ +double QwtScaleEngine::upperMargin() const +{ + return d_data->upperMargin; +} + +/*! + \brief Specify margins at the scale's endpoints + \param lower minimum distance between the scale's lower boundary and the + smallest enclosed value + \param upper minimum distance between the scale's upper boundary and the + greatest enclosed value + + Margins can be used to leave a minimum amount of space between + the enclosed intervals and the boundaries of the scale. + + \warning + \li QwtLog10ScaleEngine measures the margins in decades. + + \sa upperMargin(), lowerMargin() +*/ + +void QwtScaleEngine::setMargins( double lower, double upper ) +{ + d_data->lowerMargin = qMax( lower, 0.0 ); + d_data->upperMargin = qMax( upper, 0.0 ); +} + +/*! + Calculate a step size for an interval size + + \param intervalSize Interval size + \param numSteps Number of steps + + \return Step size +*/ +double QwtScaleEngine::divideInterval( + double intervalSize, int numSteps ) const +{ + if ( numSteps <= 0 ) + return 0.0; + + double v = QwtScaleArithmetic::divideEps( intervalSize, numSteps ); + return QwtScaleArithmetic::ceil125( v ); +} + +/*! + Check if an interval "contains" a value + + \param interval Interval + \param value Value + + \sa QwtScaleArithmetic::compareEps() +*/ +bool QwtScaleEngine::contains( + const QwtInterval &interval, double value ) const +{ + if ( !interval.isValid() ) + return false; + + if ( qwtFuzzyCompare( value, interval.minValue(), interval.width() ) < 0 ) + return false; + + if ( qwtFuzzyCompare( value, interval.maxValue(), interval.width() ) > 0 ) + return false; + + return true; +} + +/*! + Remove ticks from a list, that are not inside an interval + + \param ticks Tick list + \param interval Interval + + \return Stripped tick list +*/ +QList<double> QwtScaleEngine::strip( const QList<double>& ticks, + const QwtInterval &interval ) const +{ + if ( !interval.isValid() || ticks.count() == 0 ) + return QList<double>(); + + if ( contains( interval, ticks.first() ) + && contains( interval, ticks.last() ) ) + { + return ticks; + } + + QList<double> strippedTicks; + for ( int i = 0; i < ticks.count(); i++ ) + { + if ( contains( interval, ticks[i] ) ) + strippedTicks += ticks[i]; + } + return strippedTicks; +} + +/*! + \brief Build an interval for a value + + In case of v == 0.0 the interval is [-0.5, 0.5], + otherwide it is [0.5 * v, 1.5 * v] +*/ + +QwtInterval QwtScaleEngine::buildInterval( double v ) const +{ + const double delta = ( v == 0.0 ) ? 0.5 : qAbs( 0.5 * v ); + + if ( DBL_MAX - delta < v ) + return QwtInterval( DBL_MAX - delta, DBL_MAX ); + + if ( -DBL_MAX + delta > v ) + return QwtInterval( -DBL_MAX, -DBL_MAX + delta ); + + return QwtInterval( v - delta, v + delta ); +} + +/*! + Change a scale attribute + + \param attribute Attribute to change + \param on On/Off + + \sa Attribute, testAttribute() +*/ +void QwtScaleEngine::setAttribute( Attribute attribute, bool on ) +{ + if ( on ) + d_data->attributes |= attribute; + else + d_data->attributes &= ~attribute; +} + +/*! + Check if a attribute is set. + + \param attribute Attribute to be tested + \sa Attribute, setAttribute() +*/ +bool QwtScaleEngine::testAttribute( Attribute attribute ) const +{ + return ( d_data->attributes & attribute ); +} + +/*! + Change the scale attribute + + \param attributes Set scale attributes + \sa Attribute, attributes() +*/ +void QwtScaleEngine::setAttributes( Attributes attributes ) +{ + d_data->attributes = attributes; +} + +/*! + Return the scale attributes + \sa Attribute, setAttributes(), testAttribute() +*/ +QwtScaleEngine::Attributes QwtScaleEngine::attributes() const +{ + return d_data->attributes; +} + +/*! + \brief Specify a reference point + \param r new reference value + + The reference point is needed if options IncludeReference or + Symmetric are active. Its default value is 0.0. + + \sa Attribute +*/ +void QwtScaleEngine::setReference( double r ) +{ + d_data->referenceValue = r; +} + +/*! + \return the reference value + \sa setReference(), setAttribute() +*/ +double QwtScaleEngine::reference() const +{ + return d_data->referenceValue; +} + +/*! + Return a transformation, for linear scales +*/ +QwtScaleTransformation *QwtLinearScaleEngine::transformation() const +{ + return new QwtScaleTransformation( QwtScaleTransformation::Linear ); +} + +/*! + Align and divide an interval + + \param maxNumSteps Max. number of steps + \param x1 First limit of the interval (In/Out) + \param x2 Second limit of the interval (In/Out) + \param stepSize Step size (Out) + + \sa setAttribute() +*/ +void QwtLinearScaleEngine::autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const +{ + QwtInterval interval( x1, x2 ); + interval = interval.normalized(); + + interval.setMinValue( interval.minValue() - lowerMargin() ); + interval.setMaxValue( interval.maxValue() + upperMargin() ); + + if ( testAttribute( QwtScaleEngine::Symmetric ) ) + interval = interval.symmetrize( reference() ); + + if ( testAttribute( QwtScaleEngine::IncludeReference ) ) + interval = interval.extend( reference() ); + + if ( interval.width() == 0.0 ) + interval = buildInterval( interval.minValue() ); + + stepSize = divideInterval( interval.width(), qMax( maxNumSteps, 1 ) ); + + if ( !testAttribute( QwtScaleEngine::Floating ) ) + interval = align( interval, stepSize ); + + x1 = interval.minValue(); + x2 = interval.maxValue(); + + if ( testAttribute( QwtScaleEngine::Inverted ) ) + { + qSwap( x1, x2 ); + stepSize = -stepSize; + } +} + +/*! + \brief Calculate a scale division + + \param x1 First interval limit + \param x2 Second interval limit + \param maxMajSteps Maximum for the number of major steps + \param maxMinSteps Maximum number of minor steps + \param stepSize Step size. If stepSize == 0, the scaleEngine + calculates one. + + \sa QwtScaleEngine::stepSize(), QwtScaleEngine::subDivide() +*/ +QwtScaleDiv QwtLinearScaleEngine::divideScale( double x1, double x2, + int maxMajSteps, int maxMinSteps, double stepSize ) const +{ + QwtInterval interval = QwtInterval( x1, x2 ).normalized(); + if ( interval.width() <= 0 ) + return QwtScaleDiv(); + + stepSize = qAbs( stepSize ); + if ( stepSize == 0.0 ) + { + if ( maxMajSteps < 1 ) + maxMajSteps = 1; + + stepSize = divideInterval( interval.width(), maxMajSteps ); + } + + QwtScaleDiv scaleDiv; + + if ( stepSize != 0.0 ) + { + QList<double> ticks[QwtScaleDiv::NTickTypes]; + buildTicks( interval, stepSize, maxMinSteps, ticks ); + + scaleDiv = QwtScaleDiv( interval, ticks ); + } + + if ( x1 > x2 ) + scaleDiv.invert(); + + return scaleDiv; +} + +/*! + \brief Calculate ticks for an interval + + \param interval Interval + \param stepSize Step size + \param maxMinSteps Maximum number of minor steps + \param ticks Arrays to be filled with the calculated ticks + + \sa buildMajorTicks(), buildMinorTicks +*/ +void QwtLinearScaleEngine::buildTicks( + const QwtInterval& interval, double stepSize, int maxMinSteps, + QList<double> ticks[QwtScaleDiv::NTickTypes] ) const +{ + const QwtInterval boundingInterval = align( interval, stepSize ); + + ticks[QwtScaleDiv::MajorTick] = + buildMajorTicks( boundingInterval, stepSize ); + + if ( maxMinSteps > 0 ) + { + buildMinorTicks( ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize, + ticks[QwtScaleDiv::MinorTick], ticks[QwtScaleDiv::MediumTick] ); + } + + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + { + ticks[i] = strip( ticks[i], interval ); + + // ticks very close to 0.0 are + // explicitely set to 0.0 + + for ( int j = 0; j < ticks[i].count(); j++ ) + { + if ( qwtFuzzyCompare( ticks[i][j], 0.0, stepSize ) == 0 ) + ticks[i][j] = 0.0; + } + } +} + +/*! + \brief Calculate major ticks for an interval + + \param interval Interval + \param stepSize Step size + + \return Calculated ticks +*/ +QList<double> QwtLinearScaleEngine::buildMajorTicks( + const QwtInterval &interval, double stepSize ) const +{ + int numTicks = qRound( interval.width() / stepSize ) + 1; + if ( numTicks > 10000 ) + numTicks = 10000; + + QList<double> ticks; + + ticks += interval.minValue(); + for ( int i = 1; i < numTicks - 1; i++ ) + ticks += interval.minValue() + i * stepSize; + ticks += interval.maxValue(); + + return ticks; +} + +/*! + \brief Calculate minor/medium ticks for major ticks + + \param majorTicks Major ticks + \param maxMinSteps Maximum number of minor steps + \param stepSize Step size + \param minorTicks Array to be filled with the calculated minor ticks + \param mediumTicks Array to be filled with the calculated medium ticks + +*/ +void QwtLinearScaleEngine::buildMinorTicks( + const QList<double>& majorTicks, + int maxMinSteps, double stepSize, + QList<double> &minorTicks, + QList<double> &mediumTicks ) const +{ + double minStep = divideInterval( stepSize, maxMinSteps ); + if ( minStep == 0.0 ) + return; + + // # ticks per interval + int numTicks = qCeil( qAbs( stepSize / minStep ) ) - 1; + + // Do the minor steps fit into the interval? + if ( qwtFuzzyCompare( ( numTicks + 1 ) * qAbs( minStep ), + qAbs( stepSize ), stepSize ) > 0 ) + { + numTicks = 1; + minStep = stepSize * 0.5; + } + + int medIndex = -1; + if ( numTicks % 2 ) + medIndex = numTicks / 2; + + // calculate minor ticks + + for ( int i = 0; i < majorTicks.count(); i++ ) + { + double val = majorTicks[i]; + for ( int k = 0; k < numTicks; k++ ) + { + val += minStep; + + double alignedValue = val; + if ( qwtFuzzyCompare( val, 0.0, stepSize ) == 0 ) + alignedValue = 0.0; + + if ( k == medIndex ) + mediumTicks += alignedValue; + else + minorTicks += alignedValue; + } + } +} + +/*! + \brief Align an interval to a step size + + The limits of an interval are aligned that both are integer + multiples of the step size. + + \param interval Interval + \param stepSize Step size + + \return Aligned interval +*/ +QwtInterval QwtLinearScaleEngine::align( + const QwtInterval &interval, double stepSize ) const +{ + double x1 = interval.minValue(); + double x2 = interval.maxValue(); + + if ( -DBL_MAX + stepSize <= x1 ) + { + const double x = QwtScaleArithmetic::floorEps( x1, stepSize ); + if ( qwtFuzzyCompare( x1, x, stepSize ) != 0 ) + x1 = x; + } + + if ( DBL_MAX - stepSize >= x2 ) + { + const double x = QwtScaleArithmetic::ceilEps( x2, stepSize ); + if ( qwtFuzzyCompare( x2, x, stepSize ) != 0 ) + x2 = x; + } + + return QwtInterval( x1, x2 ); +} + +/*! + Return a transformation, for logarithmic (base 10) scales +*/ +QwtScaleTransformation *QwtLog10ScaleEngine::transformation() const +{ + return new QwtScaleTransformation( QwtScaleTransformation::Log10 ); +} + +/*! + Align and divide an interval + + \param maxNumSteps Max. number of steps + \param x1 First limit of the interval (In/Out) + \param x2 Second limit of the interval (In/Out) + \param stepSize Step size (Out) + + \sa QwtScaleEngine::setAttribute() +*/ +void QwtLog10ScaleEngine::autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const +{ + if ( x1 > x2 ) + qSwap( x1, x2 ); + + QwtInterval interval( x1 / qPow( 10.0, lowerMargin() ), + x2 * qPow( 10.0, upperMargin() ) ); + + if ( interval.maxValue() / interval.minValue() < 10.0 ) + { + // scale width is less than one decade -> build linear scale + + QwtLinearScaleEngine linearScaler; + linearScaler.setAttributes( attributes() ); + linearScaler.setReference( reference() ); + linearScaler.setMargins( lowerMargin(), upperMargin() ); + + linearScaler.autoScale( maxNumSteps, x1, x2, stepSize ); + + if ( stepSize < 0.0 ) + stepSize = -::log10( qAbs( stepSize ) ); + else + stepSize = ::log10( stepSize ); + + return; + } + + double logRef = 1.0; + if ( reference() > LOG_MIN / 2 ) + logRef = qMin( reference(), LOG_MAX / 2 ); + + if ( testAttribute( QwtScaleEngine::Symmetric ) ) + { + const double delta = qMax( interval.maxValue() / logRef, + logRef / interval.minValue() ); + interval.setInterval( logRef / delta, logRef * delta ); + } + + if ( testAttribute( QwtScaleEngine::IncludeReference ) ) + interval = interval.extend( logRef ); + + interval = interval.limited( LOG_MIN, LOG_MAX ); + + if ( interval.width() == 0.0 ) + interval = buildInterval( interval.minValue() ); + + stepSize = divideInterval( log10( interval ).width(), qMax( maxNumSteps, 1 ) ); + if ( stepSize < 1.0 ) + stepSize = 1.0; + + if ( !testAttribute( QwtScaleEngine::Floating ) ) + interval = align( interval, stepSize ); + + x1 = interval.minValue(); + x2 = interval.maxValue(); + + if ( testAttribute( QwtScaleEngine::Inverted ) ) + { + qSwap( x1, x2 ); + stepSize = -stepSize; + } +} + +/*! + \brief Calculate a scale division + + \param x1 First interval limit + \param x2 Second interval limit + \param maxMajSteps Maximum for the number of major steps + \param maxMinSteps Maximum number of minor steps + \param stepSize Step size. If stepSize == 0, the scaleEngine + calculates one. + + \sa QwtScaleEngine::stepSize(), QwtLog10ScaleEngine::subDivide() +*/ +QwtScaleDiv QwtLog10ScaleEngine::divideScale( double x1, double x2, + int maxMajSteps, int maxMinSteps, double stepSize ) const +{ + QwtInterval interval = QwtInterval( x1, x2 ).normalized(); + interval = interval.limited( LOG_MIN, LOG_MAX ); + + if ( interval.width() <= 0 ) + return QwtScaleDiv(); + + if ( interval.maxValue() / interval.minValue() < 10.0 ) + { + // scale width is less than one decade -> build linear scale + + QwtLinearScaleEngine linearScaler; + linearScaler.setAttributes( attributes() ); + linearScaler.setReference( reference() ); + linearScaler.setMargins( lowerMargin(), upperMargin() ); + + if ( stepSize != 0.0 ) + { + if ( stepSize < 0.0 ) + stepSize = -qPow( 10.0, -stepSize ); + else + stepSize = qPow( 10.0, stepSize ); + } + + return linearScaler.divideScale( x1, x2, + maxMajSteps, maxMinSteps, stepSize ); + } + + stepSize = qAbs( stepSize ); + if ( stepSize == 0.0 ) + { + if ( maxMajSteps < 1 ) + maxMajSteps = 1; + + stepSize = divideInterval( log10( interval ).width(), maxMajSteps ); + if ( stepSize < 1.0 ) + stepSize = 1.0; // major step must be >= 1 decade + } + + QwtScaleDiv scaleDiv; + if ( stepSize != 0.0 ) + { + QList<double> ticks[QwtScaleDiv::NTickTypes]; + buildTicks( interval, stepSize, maxMinSteps, ticks ); + + scaleDiv = QwtScaleDiv( interval, ticks ); + } + + if ( x1 > x2 ) + scaleDiv.invert(); + + return scaleDiv; +} + +/*! + \brief Calculate ticks for an interval + + \param interval Interval + \param maxMinSteps Maximum number of minor steps + \param stepSize Step size + \param ticks Arrays to be filled with the calculated ticks + + \sa buildMajorTicks(), buildMinorTicks +*/ +void QwtLog10ScaleEngine::buildTicks( + const QwtInterval& interval, double stepSize, int maxMinSteps, + QList<double> ticks[QwtScaleDiv::NTickTypes] ) const +{ + const QwtInterval boundingInterval = align( interval, stepSize ); + + ticks[QwtScaleDiv::MajorTick] = + buildMajorTicks( boundingInterval, stepSize ); + + if ( maxMinSteps > 0 ) + { + ticks[QwtScaleDiv::MinorTick] = buildMinorTicks( + ticks[QwtScaleDiv::MajorTick], maxMinSteps, stepSize ); + } + + for ( int i = 0; i < QwtScaleDiv::NTickTypes; i++ ) + ticks[i] = strip( ticks[i], interval ); +} + +/*! + \brief Calculate major ticks for an interval + + \param interval Interval + \param stepSize Step size + + \return Calculated ticks +*/ +QList<double> QwtLog10ScaleEngine::buildMajorTicks( + const QwtInterval &interval, double stepSize ) const +{ + double width = log10( interval ).width(); + + int numTicks = qRound( width / stepSize ) + 1; + if ( numTicks > 10000 ) + numTicks = 10000; + + const double lxmin = ::log( interval.minValue() ); + const double lxmax = ::log( interval.maxValue() ); + const double lstep = ( lxmax - lxmin ) / double( numTicks - 1 ); + + QList<double> ticks; + + ticks += interval.minValue(); + + for ( int i = 1; i < numTicks - 1; i++ ) + ticks += qExp( lxmin + double( i ) * lstep ); + + ticks += interval.maxValue(); + + return ticks; +} + +/*! + \brief Calculate minor/medium ticks for major ticks + + \param majorTicks Major ticks + \param maxMinSteps Maximum number of minor steps + \param stepSize Step size +*/ +QList<double> QwtLog10ScaleEngine::buildMinorTicks( + const QList<double> &majorTicks, + int maxMinSteps, double stepSize ) const +{ + if ( stepSize < 1.1 ) // major step width is one decade + { + if ( maxMinSteps < 1 ) + return QList<double>(); + + int k0, kstep, kmax; + + if ( maxMinSteps >= 8 ) + { + k0 = 2; + kmax = 9; + kstep = 1; + } + else if ( maxMinSteps >= 4 ) + { + k0 = 2; + kmax = 8; + kstep = 2; + } + else if ( maxMinSteps >= 2 ) + { + k0 = 2; + kmax = 5; + kstep = 3; + } + else + { + k0 = 5; + kmax = 5; + kstep = 1; + } + + QList<double> minorTicks; + + for ( int i = 0; i < majorTicks.count(); i++ ) + { + const double v = majorTicks[i]; + for ( int k = k0; k <= kmax; k += kstep ) + minorTicks += v * double( k ); + } + + return minorTicks; + } + else // major step > one decade + { + double minStep = divideInterval( stepSize, maxMinSteps ); + if ( minStep == 0.0 ) + return QList<double>(); + + if ( minStep < 1.0 ) + minStep = 1.0; + + // # subticks per interval + int nMin = qRound( stepSize / minStep ) - 1; + + // Do the minor steps fit into the interval? + + if ( qwtFuzzyCompare( ( nMin + 1 ) * minStep, + qAbs( stepSize ), stepSize ) > 0 ) + { + nMin = 0; + } + + if ( nMin < 1 ) + return QList<double>(); // no subticks + + // substep factor = 10^substeps + const qreal minFactor = qMax( qPow( 10.0, minStep ), qreal( 10.0 ) ); + + QList<double> minorTicks; + for ( int i = 0; i < majorTicks.count(); i++ ) + { + double val = majorTicks[i]; + for ( int k = 0; k < nMin; k++ ) + { + val *= minFactor; + minorTicks += val; + } + } + return minorTicks; + } +} + +/*! + \brief Align an interval to a step size + + The limits of an interval are aligned that both are integer + multiples of the step size. + + \param interval Interval + \param stepSize Step size + + \return Aligned interval +*/ +QwtInterval QwtLog10ScaleEngine::align( + const QwtInterval &interval, double stepSize ) const +{ + const QwtInterval intv = log10( interval ); + + double x1 = QwtScaleArithmetic::floorEps( intv.minValue(), stepSize ); + if ( qwtFuzzyCompare( interval.minValue(), x1, stepSize ) == 0 ) + x1 = interval.minValue(); + + double x2 = QwtScaleArithmetic::ceilEps( intv.maxValue(), stepSize ); + if ( qwtFuzzyCompare( interval.maxValue(), x2, stepSize ) == 0 ) + x2 = interval.maxValue(); + + return pow10( QwtInterval( x1, x2 ) ); +} + +/*! + Return the interval [log10(interval.minValue(), log10(interval.maxValue] +*/ + +QwtInterval QwtLog10ScaleEngine::log10( const QwtInterval &interval ) const +{ + return QwtInterval( ::log10( interval.minValue() ), + ::log10( interval.maxValue() ) ); +} + +/*! + Return the interval [pow10(interval.minValue(), pow10(interval.maxValue] +*/ +QwtInterval QwtLog10ScaleEngine::pow10( const QwtInterval &interval ) const +{ + return QwtInterval( qPow( 10.0, interval.minValue() ), + qPow( 10.0, interval.maxValue() ) ); +} diff --git a/src/libpcp_qwt/src/qwt_scale_engine.h b/src/libpcp_qwt/src/qwt_scale_engine.h new file mode 100644 index 0000000..a7b2c74 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_engine.h @@ -0,0 +1,217 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SCALE_ENGINE_H +#define QWT_SCALE_ENGINE_H + +#include "qwt_global.h" +#include "qwt_scale_div.h" +#include "qwt_interval.h" + +class QwtScaleTransformation; + +/*! + \brief Arithmetic including a tolerance +*/ +class QWT_EXPORT QwtScaleArithmetic +{ +public: + static double ceilEps( double value, double intervalSize ); + static double floorEps( double value, double intervalSize ); + + static double divideEps( double interval, double steps ); + + static double ceil125( double x ); + static double floor125( double x ); +}; + +/*! + \brief Base class for scale engines. + + A scale engine tries to find "reasonable" ranges and step sizes + for scales. + + The layout of the scale can be varied with setAttribute(). + + Qwt offers implementations for logarithmic (log10) + and linear scales. Contributions for other types of scale engines + (date/time, log2 ... ) are welcome. +*/ + +class QWT_EXPORT QwtScaleEngine +{ +public: + /*! + Layout attributes + \sa setAttribute(), testAttribute(), reference(), + lowerMargin(), upperMargin() + */ + + enum Attribute + { + //! No attributes + NoAttribute = 0x00, + + //! Build a scale which includes the reference() value. + IncludeReference = 0x01, + + //! Build a scale which is symmetric to the reference() value. + Symmetric = 0x02, + + /*! + The endpoints of the scale are supposed to be equal the + outmost included values plus the specified margins + (see setMargins()). + If this attribute is *not* set, the endpoints of the scale will + be integer multiples of the step size. + */ + Floating = 0x04, + + //! Turn the scale upside down. + Inverted = 0x08 + }; + + //! Layout attributes + typedef QFlags<Attribute> Attributes; + + explicit QwtScaleEngine(); + virtual ~QwtScaleEngine(); + + void setAttribute( Attribute, bool on = true ); + bool testAttribute( Attribute ) const; + + void setAttributes( Attributes ); + Attributes attributes() const; + + void setReference( double reference ); + double reference() const; + + void setMargins( double lower, double upper ); + double lowerMargin() const; + double upperMargin() const; + + /*! + Align and divide an interval + + \param maxNumSteps Max. number of steps + \param x1 First limit of the interval (In/Out) + \param x2 Second limit of the interval (In/Out) + \param stepSize Step size (Return value) + */ + virtual void autoScale( int maxNumSteps, + double &x1, double &x2, double &stepSize ) const = 0; + + /*! + \brief Calculate a scale division + + \param x1 First interval limit + \param x2 Second interval limit + \param maxMajSteps Maximum for the number of major steps + \param maxMinSteps Maximum number of minor steps + \param stepSize Step size. If stepSize == 0.0, the scaleEngine + calculates one. + */ + virtual QwtScaleDiv divideScale( double x1, double x2, + int maxMajSteps, int maxMinSteps, + double stepSize = 0.0 ) const = 0; + + //! \return a transformation + virtual QwtScaleTransformation *transformation() const = 0; + +protected: + bool contains( const QwtInterval &, double val ) const; + QList<double> strip( const QList<double>&, const QwtInterval & ) const; + double divideInterval( double interval, int numSteps ) const; + + QwtInterval buildInterval( double v ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief A scale engine for linear scales + + The step size will fit into the pattern + \f$\left\{ 1,2,5\right\} \cdot 10^{n}\f$, where n is an integer. +*/ + +class QWT_EXPORT QwtLinearScaleEngine: public QwtScaleEngine +{ +public: + virtual void autoScale( int maxSteps, + double &x1, double &x2, double &stepSize ) const; + + virtual QwtScaleDiv divideScale( double x1, double x2, + int numMajorSteps, int numMinorSteps, + double stepSize = 0.0 ) const; + + virtual QwtScaleTransformation *transformation() const; + +protected: + QwtInterval align( const QwtInterval&, double stepSize ) const; + + void buildTicks( + const QwtInterval &, double stepSize, int maxMinSteps, + QList<double> ticks[QwtScaleDiv::NTickTypes] ) const; + + QList<double> buildMajorTicks( + const QwtInterval &interval, double stepSize ) const; + + void buildMinorTicks( + const QList<double>& majorTicks, + int maxMinMark, double step, + QList<double> &, QList<double> & ) const; +}; + +/*! + \brief A scale engine for logarithmic (base 10) scales + + The step size is measured in *decades* + and the major step size will be adjusted to fit the pattern + \f$\left\{ 1,2,3,5\right\} \cdot 10^{n}\f$, where n is a natural number + including zero. + + \warning the step size as well as the margins are measured in *decades*. +*/ + +class QWT_EXPORT QwtLog10ScaleEngine: public QwtScaleEngine +{ +public: + virtual void autoScale( int maxSteps, + double &x1, double &x2, double &stepSize ) const; + + virtual QwtScaleDiv divideScale( double x1, double x2, + int numMajorSteps, int numMinorSteps, + double stepSize = 0.0 ) const; + + virtual QwtScaleTransformation *transformation() const; + +protected: + QwtInterval log10( const QwtInterval& ) const; + QwtInterval pow10( const QwtInterval& ) const; + + QwtInterval align( const QwtInterval&, double stepSize ) const; + + void buildTicks( + const QwtInterval &, double stepSize, int maxMinSteps, + QList<double> ticks[QwtScaleDiv::NTickTypes] ) const; + + QList<double> buildMajorTicks( + const QwtInterval &interval, double stepSize ) const; + + QList<double> buildMinorTicks( + const QList<double>& majorTicks, + int maxMinMark, double step ) const; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtScaleEngine::Attributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_scale_map.cpp b/src/libpcp_qwt/src/qwt_scale_map.cpp new file mode 100644 index 0000000..1e16be1 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_map.cpp @@ -0,0 +1,344 @@ +/* -*- 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_scale_map.h" +#include <qrect.h> +#include <qalgorithms.h> +#include <qmath.h> +#include <qdebug.h> + +#if QT_VERSION < 0x040601 +#define qExp(x) ::exp(x) +#endif + +//! Smallest allowed value for logarithmic scales: 1.0e-150 +QT_STATIC_CONST_IMPL double QwtScaleMap::LogMin = 1.0e-150; + +//! Largest allowed value for logarithmic scales: 1.0e150 +QT_STATIC_CONST_IMPL double QwtScaleMap::LogMax = 1.0e150; + +//! Constructor for a linear transformation +QwtScaleTransformation::QwtScaleTransformation( Type type ): + d_type( type ) +{ +} + +//! Destructor +QwtScaleTransformation::~QwtScaleTransformation() +{ +} + +//! Create a clone of the transformation +QwtScaleTransformation *QwtScaleTransformation::copy() const +{ + return new QwtScaleTransformation( d_type ); +} + +/*! + \brief Transform a value from the coordinate system of a scale + into the coordinate system of the paint device + + \param s Value related to the coordinate system of the scale + \param s1 First border of the coordinate system of the scale + \param s2 Second border of the coordinate system of the scale + \param p1 First border of the coordinate system of the paint device + \param p2 Second border of the coordinate system of the paint device + \return + <dl> + <dt>linear mapping:<dd>p1 + (p2 - p1) / (s2 - s1) * (s - s1);</dd> + </dl> + <dl> + <dt>log10 mapping: <dd>p1 + (p2 - p1) / log(s2 / s1) * log(s / s1);</dd> + </dl> +*/ + +double QwtScaleTransformation::xForm( + double s, double s1, double s2, double p1, double p2 ) const +{ + if ( d_type == Log10 ) + return p1 + ( p2 - p1 ) / log( s2 / s1 ) * log( s / s1 ); + else + return p1 + ( p2 - p1 ) / ( s2 - s1 ) * ( s - s1 ); +} + +/*! + \brief Transform a value from the coordinate system of the paint device + into the coordinate system of a scale. + + \param p Value related to the coordinate system of the paint device + \param p1 First border of the coordinate system of the paint device + \param p2 Second border of the coordinate system of the paint device + \param s1 First border of the coordinate system of the scale + \param s2 Second border of the coordinate system of the scale + \return + <dl> + <dt>linear mapping:<dd>s1 + ( s2 - s1 ) / ( p2 - p1 ) * ( p - p1 );</dd> + </dl> + <dl> + <dt>log10 mapping:<dd>exp((p - p1) / (p2 - p1) * log(s2 / s1)) * s1;</dd> + </dl> +*/ + +double QwtScaleTransformation::invXForm( double p, double p1, double p2, + double s1, double s2 ) const +{ + if ( d_type == Log10 ) + return qExp( ( p - p1 ) / ( p2 - p1 ) * log( s2 / s1 ) ) * s1; + else + return s1 + ( s2 - s1 ) / ( p2 - p1 ) * ( p - p1 ); +} + +/*! + \brief Constructor + + The scale and paint device intervals are both set to [0,1]. +*/ +QwtScaleMap::QwtScaleMap(): + d_s1( 0.0 ), + d_s2( 1.0 ), + d_p1( 0.0 ), + d_p2( 1.0 ), + d_cnv( 1.0 ) +{ + d_transformation = new QwtScaleTransformation( + QwtScaleTransformation::Linear ); +} + +//! Copy constructor +QwtScaleMap::QwtScaleMap( const QwtScaleMap& other ): + d_s1( other.d_s1 ), + d_s2( other.d_s2 ), + d_p1( other.d_p1 ), + d_p2( other.d_p2 ), + d_cnv( other.d_cnv ) +{ + d_transformation = other.d_transformation->copy(); +} + +/*! + Destructor +*/ +QwtScaleMap::~QwtScaleMap() +{ + delete d_transformation; +} + +//! Assignment operator +QwtScaleMap &QwtScaleMap::operator=( const QwtScaleMap & other ) +{ + d_s1 = other.d_s1; + d_s2 = other.d_s2; + d_p1 = other.d_p1; + d_p2 = other.d_p2; + d_cnv = other.d_cnv; + + delete d_transformation; + d_transformation = other.d_transformation->copy(); + + return *this; +} + +/*! + Initialize the map with a transformation +*/ +void QwtScaleMap::setTransformation( + QwtScaleTransformation *transformation ) +{ + if ( transformation == NULL ) + return; + + if ( transformation != d_transformation ) + { + delete d_transformation; + d_transformation = transformation; + } + + setScaleInterval( d_s1, d_s2 ); +} + +//! Get the transformation +const QwtScaleTransformation *QwtScaleMap::transformation() const +{ + return d_transformation; +} + +/*! + \brief Specify the borders of the scale interval + \param s1 first border + \param s2 second border + \warning logarithmic scales might be aligned to [LogMin, LogMax] +*/ +void QwtScaleMap::setScaleInterval( double s1, double s2 ) +{ + if ( d_transformation->type() == QwtScaleTransformation::Log10 ) + { + if ( s1 < LogMin ) + s1 = LogMin; + else if ( s1 > LogMax ) + s1 = LogMax; + + if ( s2 < LogMin ) + s2 = LogMin; + else if ( s2 > LogMax ) + s2 = LogMax; + } + + d_s1 = s1; + d_s2 = s2; + + if ( d_transformation->type() != QwtScaleTransformation::Other ) + newFactor(); +} + +/*! + \brief Specify the borders of the paint device interval + \param p1 first border + \param p2 second border +*/ +void QwtScaleMap::setPaintInterval( double p1, double p2 ) +{ + d_p1 = p1; + d_p2 = p2; + + if ( d_transformation->type() != QwtScaleTransformation::Other ) + newFactor(); +} + +/*! + \brief Re-calculate the conversion factor. +*/ +void QwtScaleMap::newFactor() +{ + d_cnv = 0.0; + + switch ( d_transformation->type() ) + { + case QwtScaleTransformation::Linear: + { + if ( d_s2 != d_s1 ) + d_cnv = ( d_p2 - d_p1 ) / ( d_s2 - d_s1 ); + break; + } + case QwtScaleTransformation::Log10: + { + if ( d_s1 != 0 ) + d_cnv = ( d_p2 - d_p1 ) / log( d_s2 / d_s1 ); + break; + } + default:; + } +} + +/*! + Transform a rectangle from scale to paint coordinates + + \param xMap X map + \param yMap Y map + \param rect Rectangle in scale coordinates + \return Rectangle in paint coordinates + + \sa invTransform() +*/ +QRectF QwtScaleMap::transform( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &rect ) +{ + double x1 = xMap.transform( rect.left() ); + double x2 = xMap.transform( rect.right() ); + double y1 = yMap.transform( rect.top() ); + double y2 = yMap.transform( rect.bottom() ); + + if ( x2 < x1 ) + qSwap( x1, x2 ); + if ( y2 < y1 ) + qSwap( y1, y2 ); + + if ( qwtFuzzyCompare( x1, 0.0, x2 - x1 ) == 0 ) + x1 = 0.0; + if ( qwtFuzzyCompare( x2, 0.0, x2 - x1 ) == 0 ) + x2 = 0.0; + if ( qwtFuzzyCompare( y1, 0.0, y2 - y1 ) == 0 ) + y1 = 0.0; + if ( qwtFuzzyCompare( y2, 0.0, y2 - y1 ) == 0 ) + y2 = 0.0; + + return QRectF( x1, y1, x2 - x1 + 1, y2 - y1 + 1 ); +} + +/*! + Transform a rectangle from paint to scale coordinates + + \param xMap X map + \param yMap Y map + \param pos Position in paint coordinates + \return Position in scale coordinates + \sa transform() +*/ +QPointF QwtScaleMap::invTransform( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QPointF &pos ) +{ + return QPointF( + xMap.invTransform( pos.x() ), + yMap.invTransform( pos.y() ) + ); +} + +/*! + Transform a point from scale to paint coordinates + + \param xMap X map + \param yMap Y map + \param pos Position in scale coordinates + \return Position in paint coordinates + + \sa invTransform() +*/ +QPointF QwtScaleMap::transform( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QPointF &pos ) +{ + return QPointF( + xMap.transform( pos.x() ), + yMap.transform( pos.y() ) + ); +} + +/*! + Transform a rectangle from paint to scale coordinates + + \param xMap X map + \param yMap Y map + \param rect Rectangle in paint coordinates + \return Rectangle in scale coordinates + \sa transform() +*/ +QRectF QwtScaleMap::invTransform( const QwtScaleMap &xMap, + const QwtScaleMap &yMap, const QRectF &rect ) +{ + const double x1 = xMap.invTransform( rect.left() ); + const double x2 = xMap.invTransform( rect.right() - 1 ); + const double y1 = yMap.invTransform( rect.top() ); + const double y2 = yMap.invTransform( rect.bottom() - 1 ); + + const QRectF r( x1, y1, x2 - x1, y2 - y1 ); + return r.normalized(); +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<( QDebug debug, const QwtScaleMap &map ) +{ + debug.nospace() << "QwtScaleMap(" + << static_cast<int>( map.transformation()->type() ) + << ", s:" << map.s1() << "->" << map.s2() + << ", p:" << map.p1() << "->" << map.p2() + << ")"; + + return debug.space(); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_scale_map.h b/src/libpcp_qwt/src/qwt_scale_map.h new file mode 100644 index 0000000..31dbb67 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_map.h @@ -0,0 +1,219 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SCALE_MAP_H +#define QWT_SCALE_MAP_H + +#include "qwt_global.h" +#include "qwt_math.h" +#ifndef QT_NO_DEBUG_STREAM +#include <qdebug.h> +#endif + +class QRectF; + +/*! + \brief A transformation between coordinate systems + + QwtScaleTransformation offers transformations from the coordinate system + of a scale into the linear coordinate system of a paint device + and vice versa. +*/ +class QWT_EXPORT QwtScaleTransformation +{ +public: + //! Transformation type + enum Type + { + //! Transformation between 2 linear scales + Linear, + + //! Transformation between a linear and a logarithmic ( base 10 ) scale + Log10, + + //! Any other type of transformation + Other + }; + + QwtScaleTransformation( Type type ); + virtual ~QwtScaleTransformation(); + + virtual double xForm( double s, double s1, double s2, + double p1, double p2 ) const; + virtual double invXForm( double p, double p1, double p2, + double s1, double s2 ) const; + + Type type() const; + + virtual QwtScaleTransformation *copy() const; + +private: + QwtScaleTransformation(); + QwtScaleTransformation &operator=( const QwtScaleTransformation ); + + const Type d_type; +}; + +//! \return Transformation type +inline QwtScaleTransformation::Type QwtScaleTransformation::type() const +{ + return d_type; +} + +/*! + \brief A scale map + + QwtScaleMap offers transformations from the coordinate system + of a scale into the linear coordinate system of a paint device + and vice versa. +*/ +class QWT_EXPORT QwtScaleMap +{ +public: + QwtScaleMap(); + QwtScaleMap( const QwtScaleMap& ); + + ~QwtScaleMap(); + + QwtScaleMap &operator=( const QwtScaleMap & ); + + void setTransformation( QwtScaleTransformation * ); + const QwtScaleTransformation *transformation() const; + + void setPaintInterval( double p1, double p2 ); + void setScaleInterval( double s1, double s2 ); + + double transform( double s ) const; + double invTransform( double p ) const; + + double p1() const; + double p2() const; + + double s1() const; + double s2() const; + + double pDist() const; + double sDist() const; + + QT_STATIC_CONST double LogMin; + QT_STATIC_CONST double LogMax; + + static QRectF transform( const QwtScaleMap &, + const QwtScaleMap &, const QRectF & ); + static QRectF invTransform( const QwtScaleMap &, + const QwtScaleMap &, const QRectF & ); + + static QPointF transform( const QwtScaleMap &, + const QwtScaleMap &, const QPointF & ); + static QPointF invTransform( const QwtScaleMap &, + const QwtScaleMap &, const QPointF & ); + + bool isInverting() const; + +private: + void newFactor(); + + double d_s1, d_s2; // scale interval boundaries + double d_p1, d_p2; // paint device interval boundaries + + double d_cnv; // conversion factor + + QwtScaleTransformation *d_transformation; +}; + +/*! + \return First border of the scale interval +*/ +inline double QwtScaleMap::s1() const +{ + return d_s1; +} + +/*! + \return Second border of the scale interval +*/ +inline double QwtScaleMap::s2() const +{ + return d_s2; +} + +/*! + \return First border of the paint interval +*/ +inline double QwtScaleMap::p1() const +{ + return d_p1; +} + +/*! + \return Second border of the paint interval +*/ +inline double QwtScaleMap::p2() const +{ + return d_p2; +} + +/*! + \return qwtAbs(p2() - p1()) +*/ +inline double QwtScaleMap::pDist() const +{ + return qAbs( d_p2 - d_p1 ); +} + +/*! + \return qwtAbs(s2() - s1()) +*/ +inline double QwtScaleMap::sDist() const +{ + return qAbs( d_s2 - d_s1 ); +} + +/*! + Transform a point related to the scale interval into an point + related to the interval of the paint device + + \param s Value relative to the coordinates of the scale +*/ +inline double QwtScaleMap::transform( double s ) const +{ + // try to inline code from QwtScaleTransformation + + if ( d_transformation->type() == QwtScaleTransformation::Linear ) + return d_p1 + ( s - d_s1 ) * d_cnv; + + if ( d_transformation->type() == QwtScaleTransformation::Log10 ) + return d_p1 + log( s / d_s1 ) * d_cnv; + + return d_transformation->xForm( s, d_s1, d_s2, d_p1, d_p2 ); +} + +/*! + Transform an paint device value into a value in the + interval of the scale. + + \param p Value relative to the coordinates of the paint device + \sa transform() +*/ +inline double QwtScaleMap::invTransform( double p ) const +{ + return d_transformation->invXForm( p, d_p1, d_p2, d_s1, d_s2 ); +} + +//! \return True, when ( p1() < p2() ) != ( s1() < s2() ) +inline bool QwtScaleMap::isInverting() const +{ + return ( ( d_p1 < d_p2 ) != ( d_s1 < d_s2 ) ); +} + +#ifndef QT_NO_DEBUG_STREAM +QWT_EXPORT QDebug operator<<( QDebug, const QwtScaleMap & ); +#endif + +#endif diff --git a/src/libpcp_qwt/src/qwt_scale_widget.cpp b/src/libpcp_qwt/src/qwt_scale_widget.cpp new file mode 100644 index 0000000..d9ffacc --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_widget.cpp @@ -0,0 +1,918 @@ +/* -*- 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_scale_widget.h" +#include "qwt_painter.h" +#include "qwt_color_map.h" +#include "qwt_scale_map.h" +#include "qwt_math.h" +#include "qwt_scale_div.h" +#include "qwt_text.h" +#include <qpainter.h> +#include <qevent.h> +#include <qmath.h> +#include <qstyle.h> +#include <qstyleoption.h> + +class QwtScaleWidget::PrivateData +{ +public: + PrivateData(): + scaleDraw( NULL ) + { + colorBar.colorMap = NULL; + } + + ~PrivateData() + { + delete scaleDraw; + delete colorBar.colorMap; + } + + QwtScaleDraw *scaleDraw; + + int borderDist[2]; + int minBorderDist[2]; + int scaleLength; + int margin; + + int titleOffset; + int spacing; + QwtText title; + + QwtScaleWidget::LayoutFlags layoutFlags; + + struct t_colorBar + { + bool isEnabled; + int width; + QwtInterval interval; + QwtColorMap *colorMap; + } colorBar; +}; + +/*! + \brief Create a scale with the position QwtScaleWidget::Left + \param parent Parent widget +*/ +QwtScaleWidget::QwtScaleWidget( QWidget *parent ): + QWidget( parent ) +{ + initScale( QwtScaleDraw::LeftScale ); +} + +/*! + \brief Constructor + \param align Alignment. + \param parent Parent widget +*/ +QwtScaleWidget::QwtScaleWidget( + QwtScaleDraw::Alignment align, QWidget *parent ): + QWidget( parent ) +{ + initScale( align ); +} + +//! Destructor +QwtScaleWidget::~QwtScaleWidget() +{ + delete d_data; +} + +//! Initialize the scale +void QwtScaleWidget::initScale( QwtScaleDraw::Alignment align ) +{ + d_data = new PrivateData; + + d_data->layoutFlags = 0; + if ( align == QwtScaleDraw::RightScale ) + d_data->layoutFlags |= TitleInverted; + + d_data->borderDist[0] = 0; + d_data->borderDist[1] = 0; + d_data->minBorderDist[0] = 0; + d_data->minBorderDist[1] = 0; + d_data->margin = 4; + d_data->titleOffset = 0; + d_data->spacing = 2; + + d_data->scaleDraw = new QwtScaleDraw; + d_data->scaleDraw->setAlignment( align ); + d_data->scaleDraw->setLength( 10 ); + + d_data->colorBar.colorMap = new QwtLinearColorMap(); + d_data->colorBar.isEnabled = false; + d_data->colorBar.width = 10; + + const int flags = Qt::AlignHCenter + | Qt::TextExpandTabs | Qt::TextWordWrap; + d_data->title.setRenderFlags( flags ); + d_data->title.setFont( font() ); + + QSizePolicy policy( QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed ); + if ( d_data->scaleDraw->orientation() == Qt::Vertical ) + policy.transpose(); + + setSizePolicy( policy ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); +} + +/*! + Toggle an layout flag + + \param flag Layout flag + \param on true/false + + \sa testLayoutFlag(), LayoutFlag +*/ +void QwtScaleWidget::setLayoutFlag( LayoutFlag flag, bool on ) +{ + if ( ( ( d_data->layoutFlags & flag ) != 0 ) != on ) + { + if ( on ) + d_data->layoutFlags |= flag; + else + d_data->layoutFlags &= ~flag; + } +} + +/*! + Test a layout flag + + \param flag Layout flag + \return true/false + \sa setLayoutFlag(), LayoutFlag +*/ +bool QwtScaleWidget::testLayoutFlag( LayoutFlag flag ) const +{ + return ( d_data->layoutFlags & flag ); +} + +/*! + Give title new text contents + + \param title New title + \sa title(), setTitle(const QwtText &); +*/ +void QwtScaleWidget::setTitle( const QString &title ) +{ + if ( d_data->title.text() != title ) + { + d_data->title.setText( title ); + layoutScale(); + } +} + +/*! + Give title new text contents + + \param title New title + \sa title() + \warning The title flags are interpreted in + direction of the label, AlignTop, AlignBottom can't be set + as the title will always be aligned to the scale. +*/ +void QwtScaleWidget::setTitle( const QwtText &title ) +{ + QwtText t = title; + const int flags = title.renderFlags() & ~( Qt::AlignTop | Qt::AlignBottom ); + t.setRenderFlags( flags ); + + if ( t != d_data->title ) + { + d_data->title = t; + layoutScale(); + } +} + +/*! + Change the alignment + + \param alignment New alignment + \sa alignment() +*/ +void QwtScaleWidget::setAlignment( QwtScaleDraw::Alignment alignment ) +{ + if ( d_data->scaleDraw ) + d_data->scaleDraw->setAlignment( alignment ); + + if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) ) + { + QSizePolicy policy( QSizePolicy::MinimumExpanding, + QSizePolicy::Fixed ); + if ( d_data->scaleDraw->orientation() == Qt::Vertical ) + policy.transpose(); + + setSizePolicy( policy ); + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); + } + + layoutScale(); +} + + +/*! + \return position + \sa setPosition() +*/ +QwtScaleDraw::Alignment QwtScaleWidget::alignment() const +{ + if ( !scaleDraw() ) + return QwtScaleDraw::LeftScale; + + return scaleDraw()->alignment(); +} + +/*! + Specify distances of the scale's endpoints from the + widget's borders. The actual borders will never be less + than minimum border distance. + \param dist1 Left or top Distance + \param dist2 Right or bottom distance + \sa borderDist() +*/ +void QwtScaleWidget::setBorderDist( int dist1, int dist2 ) +{ + if ( dist1 != d_data->borderDist[0] || dist2 != d_data->borderDist[1] ) + { + d_data->borderDist[0] = dist1; + d_data->borderDist[1] = dist2; + layoutScale(); + } +} + +/*! + \brief Specify the margin to the colorBar/base line. + \param margin Margin + \sa margin() +*/ +void QwtScaleWidget::setMargin( int margin ) +{ + margin = qMax( 0, margin ); + if ( margin != d_data->margin ) + { + d_data->margin = margin; + layoutScale(); + } +} + +/*! + \brief Specify the distance between color bar, scale and title + \param spacing Spacing + \sa spacing() +*/ +void QwtScaleWidget::setSpacing( int spacing ) +{ + spacing = qMax( 0, spacing ); + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + layoutScale(); + } +} + +/*! + \brief Change the alignment for the labels. + + \sa QwtScaleDraw::setLabelAlignment(), setLabelRotation() +*/ +void QwtScaleWidget::setLabelAlignment( Qt::Alignment alignment ) +{ + d_data->scaleDraw->setLabelAlignment( alignment ); + layoutScale(); +} + +/*! + \brief Change the rotation for the labels. + See QwtScaleDraw::setLabelRotation(). + + \param rotation Rotation + \sa QwtScaleDraw::setLabelRotation(), setLabelFlags() +*/ +void QwtScaleWidget::setLabelRotation( double rotation ) +{ + d_data->scaleDraw->setLabelRotation( rotation ); + layoutScale(); +} + +/*! + Set a scale draw + sd has to be created with new and will be deleted in + ~QwtScaleWidget() or the next call of setScaleDraw(). + + \param sd ScaleDraw object + \sa scaleDraw() +*/ +void QwtScaleWidget::setScaleDraw( QwtScaleDraw *sd ) +{ + if ( sd == NULL || sd == d_data->scaleDraw ) + return; + + if ( d_data->scaleDraw ) + sd->setAlignment( d_data->scaleDraw->alignment() ); + + delete d_data->scaleDraw; + d_data->scaleDraw = sd; + + layoutScale(); +} + +/*! + scaleDraw of this scale + \sa setScaleDraw(), QwtScaleDraw::setScaleDraw() +*/ +const QwtScaleDraw *QwtScaleWidget::scaleDraw() const +{ + return d_data->scaleDraw; +} + +/*! + scaleDraw of this scale + \sa QwtScaleDraw::setScaleDraw() +*/ +QwtScaleDraw *QwtScaleWidget::scaleDraw() +{ + return d_data->scaleDraw; +} + +/*! + \return title + \sa setTitle() +*/ +QwtText QwtScaleWidget::title() const +{ + return d_data->title; +} + +/*! + \return start border distance + \sa setBorderDist() +*/ +int QwtScaleWidget::startBorderDist() const +{ + return d_data->borderDist[0]; +} + +/*! + \return end border distance + \sa setBorderDist() +*/ +int QwtScaleWidget::endBorderDist() const +{ + return d_data->borderDist[1]; +} + +/*! + \return margin + \sa setMargin() +*/ +int QwtScaleWidget::margin() const +{ + return d_data->margin; +} + +/*! + \return distance between scale and title + \sa setMargin() +*/ +int QwtScaleWidget::spacing() const +{ + return d_data->spacing; +} + +/*! + \brief paintEvent +*/ +void QwtScaleWidget::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + draw( &painter ); +} + +/*! + \brief draw the scale +*/ +void QwtScaleWidget::draw( QPainter *painter ) const +{ + d_data->scaleDraw->draw( painter, palette() ); + + if ( d_data->colorBar.isEnabled && d_data->colorBar.width > 0 && + d_data->colorBar.interval.isValid() ) + { + drawColorBar( painter, colorBarRect( contentsRect() ) ); + } + + QRect r = contentsRect(); + if ( d_data->scaleDraw->orientation() == Qt::Horizontal ) + { + r.setLeft( r.left() + d_data->borderDist[0] ); + r.setWidth( r.width() - d_data->borderDist[1] ); + } + else + { + r.setTop( r.top() + d_data->borderDist[0] ); + r.setHeight( r.height() - d_data->borderDist[1] ); + } + + if ( !d_data->title.isEmpty() ) + drawTitle( painter, d_data->scaleDraw->alignment(), r ); +} + +/*! + Calculate the the rectangle for the color bar + + \param rect Bounding rectangle for all components of the scale + \return Rectabgle for the color bar +*/ +QRectF QwtScaleWidget::colorBarRect( const QRectF& rect ) const +{ + QRectF cr = rect; + + if ( d_data->scaleDraw->orientation() == Qt::Horizontal ) + { + cr.setLeft( cr.left() + d_data->borderDist[0] ); + cr.setWidth( cr.width() - d_data->borderDist[1] + 1 ); + } + else + { + cr.setTop( cr.top() + d_data->borderDist[0] ); + cr.setHeight( cr.height() - d_data->borderDist[1] + 1 ); + } + + switch ( d_data->scaleDraw->alignment() ) + { + case QwtScaleDraw::LeftScale: + { + cr.setLeft( cr.right() - d_data->margin + - d_data->colorBar.width ); + cr.setWidth( d_data->colorBar.width ); + break; + } + + case QwtScaleDraw::RightScale: + { + cr.setLeft( cr.left() + d_data->margin ); + cr.setWidth( d_data->colorBar.width ); + break; + } + + case QwtScaleDraw::BottomScale: + { + cr.setTop( cr.top() + d_data->margin ); + cr.setHeight( d_data->colorBar.width ); + break; + } + + case QwtScaleDraw::TopScale: + { + cr.setTop( cr.bottom() - d_data->margin + - d_data->colorBar.width ); + cr.setHeight( d_data->colorBar.width ); + break; + } + } + + return cr; +} + +/*! + Event handler for resize event + \param event Resize event +*/ +void QwtScaleWidget::resizeEvent( QResizeEvent *event ) +{ + Q_UNUSED( event ); + layoutScale( false ); +} + +/*! + Recalculate the scale'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 QwtScaleWidget::layoutScale( bool update_geometry ) +{ + int bd0, bd1; + getBorderDistHint( bd0, bd1 ); + if ( d_data->borderDist[0] > bd0 ) + bd0 = d_data->borderDist[0]; + if ( d_data->borderDist[1] > bd1 ) + bd1 = d_data->borderDist[1]; + + int colorBarWidth = 0; + if ( d_data->colorBar.isEnabled && d_data->colorBar.interval.isValid() ) + colorBarWidth = d_data->colorBar.width + d_data->spacing; + + const QRectF r = contentsRect(); + double x, y, length; + + if ( d_data->scaleDraw->orientation() == Qt::Vertical ) + { + y = r.top() + bd0; + length = r.height() - ( bd0 + bd1 ); + + if ( d_data->scaleDraw->alignment() == QwtScaleDraw::LeftScale ) + x = r.right() - 1.0 - d_data->margin - colorBarWidth; + else + x = r.left() + d_data->margin + colorBarWidth; + } + else + { + x = r.left() + bd0; + length = r.width() - ( bd0 + bd1 ); + + if ( d_data->scaleDraw->alignment() == QwtScaleDraw::BottomScale ) + y = r.top() + d_data->margin + colorBarWidth; + else + y = r.bottom() - 1.0 - d_data->margin - colorBarWidth; + } + + d_data->scaleDraw->move( x, y ); + d_data->scaleDraw->setLength( length ); + + const int extent = qCeil( d_data->scaleDraw->extent( font() ) ); + + d_data->titleOffset = + d_data->margin + d_data->spacing + colorBarWidth + extent; + + if ( update_geometry ) + { + updateGeometry(); + update(); + } +} + +/*! + Draw the color bar of the scale widget + + \param painter Painter + \param rect Bounding rectangle for the color bar + + \sa setColorBarEnabled() +*/ +void QwtScaleWidget::drawColorBar( QPainter *painter, const QRectF& rect ) const +{ + if ( !d_data->colorBar.interval.isValid() ) + return; + + const QwtScaleDraw* sd = d_data->scaleDraw; + + QwtPainter::drawColorBar( painter, *d_data->colorBar.colorMap, + d_data->colorBar.interval.normalized(), sd->scaleMap(), + sd->orientation(), rect ); +} + +/*! + Rotate and paint a title according to its position into a given rectangle. + + \param painter Painter + \param align Alignment + \param rect Bounding rectangle +*/ + +void QwtScaleWidget::drawTitle( QPainter *painter, + QwtScaleDraw::Alignment align, const QRectF &rect ) const +{ + QRectF r = rect; + double angle; + int flags = d_data->title.renderFlags() & + ~( Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter ); + + switch ( align ) + { + case QwtScaleDraw::LeftScale: + angle = -90.0; + flags |= Qt::AlignTop; + r.setRect( r.left(), r.bottom(), + r.height(), r.width() - d_data->titleOffset ); + break; + + case QwtScaleDraw::RightScale: + angle = -90.0; + flags |= Qt::AlignTop; + r.setRect( r.left() + d_data->titleOffset, r.bottom(), + r.height(), r.width() - d_data->titleOffset ); + break; + + case QwtScaleDraw::BottomScale: + angle = 0.0; + flags |= Qt::AlignBottom; + r.setTop( r.top() + d_data->titleOffset ); + break; + + case QwtScaleDraw::TopScale: + default: + angle = 0.0; + flags |= Qt::AlignTop; + r.setBottom( r.bottom() - d_data->titleOffset ); + break; + } + + if ( d_data->layoutFlags & TitleInverted ) + { + if ( align == QwtScaleDraw::LeftScale + || align == QwtScaleDraw::RightScale ) + { + angle = -angle; + r.setRect( r.x() + r.height(), r.y() - r.width(), + r.width(), r.height() ); + } + } + + painter->save(); + painter->setFont( font() ); + painter->setPen( palette().color( QPalette::Text ) ); + + painter->translate( r.x(), r.y() ); + if ( angle != 0.0 ) + painter->rotate( angle ); + + QwtText title = d_data->title; + title.setRenderFlags( flags ); + title.draw( painter, QRectF( 0.0, 0.0, r.width(), r.height() ) ); + + painter->restore(); +} + +/*! + \brief Notify a change of the scale + + This virtual function can be overloaded by derived + classes. The default implementation updates the geometry + and repaints the widget. +*/ + +void QwtScaleWidget::scaleChange() +{ + layoutScale(); +} + +/*! + \return a size hint +*/ +QSize QwtScaleWidget::sizeHint() const +{ + return minimumSizeHint(); +} + +/*! + \return a minimum size hint +*/ +QSize QwtScaleWidget::minimumSizeHint() const +{ + const Qt::Orientation o = d_data->scaleDraw->orientation(); + + // Border Distance cannot be less than the scale borderDistHint + // Note, the borderDistHint is already included in minHeight/minWidth + int length = 0; + int mbd1, mbd2; + getBorderDistHint( mbd1, mbd2 ); + length += qMax( 0, d_data->borderDist[0] - mbd1 ); + length += qMax( 0, d_data->borderDist[1] - mbd2 ); + length += d_data->scaleDraw->minLength( font() ); + + int dim = dimForLength( length, font() ); + if ( length < dim ) + { + // compensate for long titles + length = dim; + dim = dimForLength( length, font() ); + } + + QSize size( length + 2, dim ); + if ( o == Qt::Vertical ) + size.transpose(); + + int left, right, top, bottom; + getContentsMargins( &left, &top, &right, &bottom ); + return size + QSize( left + right, top + bottom ); +} + +/*! + \brief Find the height of the title for a given width. + \param width Width + \return height Height + */ + +int QwtScaleWidget::titleHeightForWidth( int width ) const +{ + return qCeil( d_data->title.heightForWidth( width, font() ) ); +} + +/*! + \brief Find the minimum dimension for a given length. + dim is the height, length the width seen in + direction of the title. + \param length width for horizontal, height for vertical scales + \param scaleFont Font of the scale + \return height for horizontal, width for vertical scales +*/ + +int QwtScaleWidget::dimForLength( int length, const QFont &scaleFont ) const +{ + const int extent = qCeil( d_data->scaleDraw->extent( scaleFont ) ); + + int dim = d_data->margin + extent + 1; + + if ( !d_data->title.isEmpty() ) + dim += titleHeightForWidth( length ) + d_data->spacing; + + if ( d_data->colorBar.isEnabled && d_data->colorBar.interval.isValid() ) + dim += d_data->colorBar.width + d_data->spacing; + + return dim; +} + +/*! + \brief Calculate a hint for the border distances. + + This member function calculates the distance + of the scale's endpoints from the widget borders which + is required for the mark labels to fit into the widget. + The maximum of this distance an the minimum border distance + is returned. + + \warning + <ul> <li>The minimum border distance depends on the font.</ul> + \sa setMinBorderDist(), getMinBorderDist(), setBorderDist() +*/ +void QwtScaleWidget::getBorderDistHint( int &start, int &end ) const +{ + d_data->scaleDraw->getBorderDistHint( font(), start, end ); + + if ( start < d_data->minBorderDist[0] ) + start = d_data->minBorderDist[0]; + + if ( end < d_data->minBorderDist[1] ) + end = d_data->minBorderDist[1]; +} + +/*! + Set a minimum value for the distances of the scale's endpoints from + the widget borders. This is useful to avoid that the scales + are "jumping", when the tick labels or their positions change + often. + + \param start Minimum for the start border + \param end Minimum for the end border + \sa getMinBorderDist(), getBorderDistHint() +*/ +void QwtScaleWidget::setMinBorderDist( int start, int end ) +{ + d_data->minBorderDist[0] = start; + d_data->minBorderDist[1] = end; +} + +/*! + Get the minimum value for the distances of the scale's endpoints from + the widget borders. + + \sa setMinBorderDist(), getBorderDistHint() +*/ +void QwtScaleWidget::getMinBorderDist( int &start, int &end ) const +{ + start = d_data->minBorderDist[0]; + end = d_data->minBorderDist[1]; +} + +/*! + \brief Assign a scale division + + The scale division determines where to set the tick marks. + + \param transformation Transformation, needed to translate between + scale and pixal values + \param scaleDiv Scale Division + \sa For more information about scale divisions, see QwtScaleDiv. +*/ +void QwtScaleWidget::setScaleDiv( + QwtScaleTransformation *transformation, + const QwtScaleDiv &scaleDiv ) +{ + QwtScaleDraw *sd = d_data->scaleDraw; + if ( sd->scaleDiv() != scaleDiv || + sd->scaleMap().transformation()->type() != transformation->type() ) + { + sd->setTransformation( transformation ); + sd->setScaleDiv( scaleDiv ); + layoutScale(); + + Q_EMIT scaleDivChanged(); + } + else + { + /* + The transformation doesn't anything different as the + previous one. So we better throw it silently away instead of + initiating heavy updates + */ + + delete transformation; + } +} + +/*! + En/disable a color bar associated to the scale + \sa isColorBarEnabled(), setColorBarWidth() +*/ +void QwtScaleWidget::setColorBarEnabled( bool on ) +{ + if ( on != d_data->colorBar.isEnabled ) + { + d_data->colorBar.isEnabled = on; + layoutScale(); + } +} + +/*! + \return true, when the color bar is enabled + \sa setColorBarEnabled(), setColorBarWidth() +*/ +bool QwtScaleWidget::isColorBarEnabled() const +{ + return d_data->colorBar.isEnabled; +} + +/*! + Set the width of the color bar + + \param width Width + \sa colorBarWidth(), setColorBarEnabled() +*/ +void QwtScaleWidget::setColorBarWidth( int width ) +{ + if ( width != d_data->colorBar.width ) + { + d_data->colorBar.width = width; + if ( isColorBarEnabled() ) + layoutScale(); + } +} + +/*! + \return Width of the color bar + \sa setColorBarEnabled(), setColorBarEnabled() +*/ +int QwtScaleWidget::colorBarWidth() const +{ + return d_data->colorBar.width; +} + +/*! + \return Value interval for the color bar + \sa setColorMap(), colorMap() +*/ +QwtInterval QwtScaleWidget::colorBarInterval() const +{ + return d_data->colorBar.interval; +} + +/*! + Set the color map and value interval, that are used for displaying + the color bar. + + \param interval Value interval + \param colorMap Color map + + \sa colorMap(), colorBarInterval() +*/ +void QwtScaleWidget::setColorMap( + const QwtInterval &interval, QwtColorMap *colorMap ) +{ + d_data->colorBar.interval = interval; + + if ( colorMap != d_data->colorBar.colorMap ) + { + delete d_data->colorBar.colorMap; + d_data->colorBar.colorMap = colorMap; + } + + if ( isColorBarEnabled() ) + layoutScale(); +} + +/*! + \return Color map + \sa setColorMap(), colorBarInterval() +*/ +const QwtColorMap *QwtScaleWidget::colorMap() const +{ + return d_data->colorBar.colorMap; +} diff --git a/src/libpcp_qwt/src/qwt_scale_widget.h b/src/libpcp_qwt/src/qwt_scale_widget.h new file mode 100644 index 0000000..7007902 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_scale_widget.h @@ -0,0 +1,135 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SCALE_WIDGET_H +#define QWT_SCALE_WIDGET_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include "qwt_scale_draw.h" +#include <qwidget.h> +#include <qfont.h> +#include <qcolor.h> +#include <qstring.h> + +class QPainter; +class QwtScaleTransformation; +class QwtScaleDiv; +class QwtColorMap; + +/*! + \brief A Widget which contains a scale + + This Widget can be used to decorate composite widgets with + a scale. +*/ + +class QWT_EXPORT QwtScaleWidget : public QWidget +{ + Q_OBJECT + +public: + //! Layout flags of the title + enum LayoutFlag + { + /*! + The title of vertical scales is painted from top to bottom. + Otherwise it is painted from bottom to top. + */ + TitleInverted = 1 + }; + + //! Layout flags of the title + typedef QFlags<LayoutFlag> LayoutFlags; + + explicit QwtScaleWidget( QWidget *parent = NULL ); + explicit QwtScaleWidget( QwtScaleDraw::Alignment, QWidget *parent = NULL ); + virtual ~QwtScaleWidget(); + +Q_SIGNALS: + //! Signal emitted, whenever the scale divison changes + void scaleDivChanged(); + +public: + void setTitle( const QString &title ); + void setTitle( const QwtText &title ); + QwtText title() const; + + void setLayoutFlag( LayoutFlag, bool on ); + bool testLayoutFlag( LayoutFlag ) const; + + void setBorderDist( int start, int end ); + int startBorderDist() const; + int endBorderDist() const; + + void getBorderDistHint( int &start, int &end ) const; + + void getMinBorderDist( int &start, int &end ) const; + void setMinBorderDist( int start, int end ); + + void setMargin( int ); + int margin() const; + + void setSpacing( int td ); + int spacing() const; + + void setScaleDiv( QwtScaleTransformation *, const QwtScaleDiv &sd ); + + void setScaleDraw( QwtScaleDraw * ); + const QwtScaleDraw *scaleDraw() const; + QwtScaleDraw *scaleDraw(); + + void setLabelAlignment( Qt::Alignment ); + void setLabelRotation( double rotation ); + + void setColorBarEnabled( bool ); + bool isColorBarEnabled() const; + + void setColorBarWidth( int ); + int colorBarWidth() const; + + void setColorMap( const QwtInterval &, QwtColorMap * ); + + QwtInterval colorBarInterval() const; + const QwtColorMap *colorMap() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + int titleHeightForWidth( int width ) const; + int dimForLength( int length, const QFont &scaleFont ) const; + + void drawColorBar( QPainter *painter, const QRectF & ) const; + void drawTitle( QPainter *painter, QwtScaleDraw::Alignment, + const QRectF &rect ) const; + + void setAlignment( QwtScaleDraw::Alignment ); + QwtScaleDraw::Alignment alignment() const; + + QRectF colorBarRect( const QRectF& ) const; + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + + void draw( QPainter *p ) const; + + void scaleChange(); + void layoutScale( bool update = true ); + +private: + void initScale( QwtScaleDraw::Alignment ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtScaleWidget::LayoutFlags ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_series_data.cpp b/src/libpcp_qwt/src/qwt_series_data.cpp new file mode 100644 index 0000000..faf7fc8 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_series_data.cpp @@ -0,0 +1,591 @@ +/* -*- 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_series_data.h" +#include "qwt_math.h" +#include <qnumeric.h> + +static inline QRectF qwtBoundingRect( const QPointF &sample ) +{ + return QRectF( sample.x(), sample.y(), 0.0, 0.0 ); +} + +static inline QRectF qwtBoundingRect( const QwtPoint3D &sample ) +{ + return QRectF( sample.x(), sample.y(), 0.0, 0.0 ); +} + +static inline QRectF qwtBoundingRect( const QwtPointPolar &sample ) +{ + return QRectF( sample.azimuth(), sample.radius(), 0.0, 0.0 ); +} + +static inline QRectF qwtBoundingRect( const QwtIntervalSample &sample ) +{ + return QRectF( sample.interval.minValue(), sample.value, + sample.interval.maxValue() - sample.interval.minValue(), 0.0 ); +} + +static inline QRectF qwtBoundingRect( const QwtSetSample &sample ) +{ + double minX = sample.set[0]; + double maxX = sample.set[0]; + + for ( int i = 1; i < sample.set.size(); i++ ) + { + if ( sample.set[i] < minX ) + minX = sample.set[i]; + if ( sample.set[i] > maxX ) + maxX = sample.set[i]; + } + + double minY = sample.value; + double maxY = sample.value; + + return QRectF( minX, minY, maxX - minX, maxY - minY ); +} + +/*! + \brief Calculate the bounding rect of a series subset + + Slow implementation, that iterates over the series. + + \param series Series + \param from Index of the first sample, <= 0 means from the beginning + \param to Index of the last sample, < 0 means to the end + + \return Bounding rectangle +*/ + +template <class T> +QRectF qwtBoundingRectT( + const QwtSeriesData<T>& series, int from, int to ) +{ + QRectF boundingRect( 1.0, 1.0, -2.0, -2.0 ); // invalid; + + if ( from < 0 ) + from = 0; + + if ( to < 0 ) + to = series.size() - 1; + + if ( to < from ) + return boundingRect; + + int i; + for ( i = from; i <= to; i++ ) + { + const QRectF rect = qwtBoundingRect( series.sample( i ) ); + if ( rect.width() >= 0.0 && rect.height() >= 0.0 ) + { + boundingRect = rect; + i++; + break; + } + } + + for ( ; i <= to; i++ ) + { + const QRectF rect = qwtBoundingRect( series.sample( i ) ); + if ( rect.width() >= 0.0 && rect.height() >= 0.0 ) + { + if (!qIsNaN(rect.left())) + boundingRect.setLeft( qMin( boundingRect.left(), rect.left() ) ); + if (!qIsNaN(rect.right())) + boundingRect.setRight( qMax( boundingRect.right(), rect.right() ) ); + if (!qIsNaN(rect.top())) + boundingRect.setTop( qMin( boundingRect.top(), rect.top() ) ); + if (!qIsNaN(rect.bottom())) + boundingRect.setBottom( qMax( boundingRect.bottom(), rect.bottom() ) ); + } + } + + return boundingRect; +} + +/*! + \brief Calculate the bounding rect of a series subset + + Slow implementation, that iterates over the series. + + \param series Series + \param from Index of the first sample, <= 0 means from the beginning + \param to Index of the last sample, < 0 means to the end + + \return Bounding rectangle +*/ +QRectF qwtBoundingRect( + const QwtSeriesData<QPointF> &series, int from, int to ) +{ + return qwtBoundingRectT<QPointF>( series, from, to ); +} + +/*! + \brief Calculate the bounding rect of a series subset + + Slow implementation, that iterates over the series. + + \param series Series + \param from Index of the first sample, <= 0 means from the beginning + \param to Index of the last sample, < 0 means to the end + + \return Bounding rectangle +*/ +QRectF qwtBoundingRect( + const QwtSeriesData<QwtPoint3D> &series, int from, int to ) +{ + return qwtBoundingRectT<QwtPoint3D>( series, from, to ); +} + +/*! + \brief Calculate the bounding rect of a series subset + + The horizontal coordinates represent the azimuth, the + vertical coordinates the radius. + + Slow implementation, that iterates over the series. + + \param series Series + \param from Index of the first sample, <= 0 means from the beginning + \param to Index of the last sample, < 0 means to the end + + \return Bounding rectangle +*/ +QRectF qwtBoundingRect( + const QwtSeriesData<QwtPointPolar> &series, int from, int to ) +{ + return qwtBoundingRectT<QwtPointPolar>( series, from, to ); +} + +/*! + \brief Calculate the bounding rect of a series subset + + Slow implementation, that iterates over the series. + + \param series Series + \param from Index of the first sample, <= 0 means from the beginning + \param to Index of the last sample, < 0 means to the end + + \return Bounding rectangle +*/ +QRectF qwtBoundingRect( + const QwtSeriesData<QwtIntervalSample>& series, int from, int to ) +{ + return qwtBoundingRectT<QwtIntervalSample>( series, from, to ); +} + +/*! + \brief Calculate the bounding rect of a series subset + + Slow implementation, that iterates over the series. + + \param series Series + \param from Index of the first sample, <= 0 means from the beginning + \param to Index of the last sample, < 0 means to the end + + \return Bounding rectangle +*/ +QRectF qwtBoundingRect( + const QwtSeriesData<QwtSetSample>& series, int from, int to ) +{ + return qwtBoundingRectT<QwtSetSample>( series, from, to ); +} + +/*! + Constructor + \param samples Samples +*/ +QwtPointSeriesData::QwtPointSeriesData( + const QVector<QPointF> &samples ): + QwtArraySeriesData<QPointF>( samples ) +{ +} + +/*! + \brief Calculate the bounding rect + + The bounding rectangle is calculated once by iterating over all + points and is stored for all following requests. + + \return Bounding rectangle +*/ +QRectF QwtPointSeriesData::boundingRect() const +{ + if ( d_boundingRect.width() < 0.0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; +} + +/*! + Constructor + \param samples Samples +*/ +QwtPoint3DSeriesData::QwtPoint3DSeriesData( + const QVector<QwtPoint3D> &samples ): + QwtArraySeriesData<QwtPoint3D>( samples ) +{ +} + +/*! + \brief Calculate the bounding rect + + The bounding rectangle is calculated once by iterating over all + points and is stored for all following requests. + + \return Bounding rectangle +*/ +QRectF QwtPoint3DSeriesData::boundingRect() const +{ + if ( d_boundingRect.width() < 0.0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; +} + +/*! + Constructor + \param samples Samples +*/ +QwtIntervalSeriesData::QwtIntervalSeriesData( + const QVector<QwtIntervalSample> &samples ): + QwtArraySeriesData<QwtIntervalSample>( samples ) +{ +} + +/*! + \brief Calculate the bounding rect + + The bounding rectangle is calculated once by iterating over all + points and is stored for all following requests. + + \return Bounding rectangle +*/ +QRectF QwtIntervalSeriesData::boundingRect() const +{ + if ( d_boundingRect.width() < 0.0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; +} + +/*! + Constructor + \param samples Samples +*/ +QwtSetSeriesData::QwtSetSeriesData( + const QVector<QwtSetSample> &samples ): + QwtArraySeriesData<QwtSetSample>( samples ) +{ +} + +/*! + \brief Calculate the bounding rect + + The bounding rectangle is calculated once by iterating over all + points and is stored for all following requests. + + \return Bounding rectangle +*/ +QRectF QwtSetSeriesData::boundingRect() const +{ + if ( d_boundingRect.width() < 0.0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; +} + +/*! + Constructor + + \param x Array of x values + \param y Array of y values + + \sa QwtPlotCurve::setData(), QwtPlotCurve::setSamples() +*/ +QwtPointArrayData::QwtPointArrayData( + const QVector<double> &x, const QVector<double> &y ): + d_x( x ), + d_y( y ) +{ +} + +/*! + Constructor + + \param x Array of x values + \param y Array of y values + \param size Size of the x and y arrays + \sa QwtPlotCurve::setData(), QwtPlotCurve::setSamples() +*/ +QwtPointArrayData::QwtPointArrayData( const double *x, + const double *y, size_t size ) +{ + d_x.resize( size ); + qMemCopy( d_x.data(), x, size * sizeof( double ) ); + + d_y.resize( size ); + qMemCopy( d_y.data(), y, size * sizeof( double ) ); +} + +/*! + \brief Calculate the bounding rect + + The bounding rectangle is calculated once by iterating over all + points and is stored for all following requests. + + \return Bounding rectangle +*/ +QRectF QwtPointArrayData::boundingRect() const +{ + if ( d_boundingRect.width() < 0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; +} + +//! \return Size of the data set +size_t QwtPointArrayData::size() const +{ + return qMin( d_x.size(), d_y.size() ); +} + +/*! + Return the sample at position i + + \param i Index + \return Sample at position i +*/ +QPointF QwtPointArrayData::sample( size_t i ) const +{ + return QPointF( d_x[int( i )], d_y[int( i )] ); +} + +//! \return Array of the x-values +const QVector<double> &QwtPointArrayData::xData() const +{ + return d_x; +} + +//! \return Array of the y-values +const QVector<double> &QwtPointArrayData::yData() const +{ + return d_y; +} + +/*! + Constructor + + \param x Array of x values + \param y Array of y values + \param size Size of the x and y arrays + + \warning The programmer must assure that the memory blocks referenced + by the pointers remain valid during the lifetime of the + QwtPlotCPointer object. + + \sa QwtPlotCurve::setData(), QwtPlotCurve::setRawSamples() +*/ +QwtCPointerData::QwtCPointerData( + const double *x, const double *y, size_t size ): + d_x( x ), + d_y( y ), + d_size( size ) +{ +} + +/*! + \brief Calculate the bounding rect + + The bounding rectangle is calculated once by iterating over all + points and is stored for all following requests. + + \return Bounding rectangle +*/ +QRectF QwtCPointerData::boundingRect() const +{ + if ( d_boundingRect.width() < 0 ) + d_boundingRect = qwtBoundingRect( *this ); + + return d_boundingRect; +} + +//! \return Size of the data set +size_t QwtCPointerData::size() const +{ + return d_size; +} + +/*! + Return the sample at position i + + \param i Index + \return Sample at position i +*/ +QPointF QwtCPointerData::sample( size_t i ) const +{ + return QPointF( d_x[int( i )], d_y[int( i )] ); +} + +//! \return Array of the x-values +const double *QwtCPointerData::xData() const +{ + return d_x; +} + +//! \return Array of the y-values +const double *QwtCPointerData::yData() const +{ + return d_y; +} + +/*! + Constructor + + \param size Number of points + \param interval Bounding interval for the points + + \sa setInterval(), setSize() +*/ +QwtSyntheticPointData::QwtSyntheticPointData( + size_t size, const QwtInterval &interval ): + d_size( size ), + d_interval( interval ) +{ +} + +/*! + Change the number of points + + \param size Number of points + \sa size(), setInterval() +*/ +void QwtSyntheticPointData::setSize( size_t size ) +{ + d_size = size; +} + +/*! + \return Number of points + \sa setSize(), interval() +*/ +size_t QwtSyntheticPointData::size() const +{ + return d_size; +} + +/*! + Set the bounding interval + + \param interval Interval + \sa interval(), setSize() +*/ +void QwtSyntheticPointData::setInterval( const QwtInterval &interval ) +{ + d_interval = interval.normalized(); +} + +/*! + \return Bounding interval + \sa setInterval(), size() +*/ +QwtInterval QwtSyntheticPointData::interval() const +{ + return d_interval; +} + +/*! + Set a the "rect of interest" + + QwtPlotSeriesItem defines the current area of the plot canvas + as "rect of interest" ( QwtPlotSeriesItem::updateScaleDiv() ). + + If interval().isValid() == false the x values are calculated + in the interval rect.left() -> rect.right(). + + \sa rectOfInterest() +*/ +void QwtSyntheticPointData::setRectOfInterest( const QRectF &rect ) +{ + d_rectOfInterest = rect; + d_intervalOfInterest = QwtInterval( + rect.left(), rect.right() ).normalized(); +} + +/*! + \return "rect of interest" + \sa setRectOfInterest() +*/ +QRectF QwtSyntheticPointData::rectOfInterest() const +{ + return d_rectOfInterest; +} + +/*! + \brief Calculate the bounding rect + + This implementation iterates over all points, what could often + be implemented much faster using the characteristics of the series. + When there are many points it is recommended to overload and + reimplement this method using the characteristics of the series + ( if possible ). + + \return Bounding rectangle +*/ +QRectF QwtSyntheticPointData::boundingRect() const +{ + if ( d_size == 0 || + !( d_interval.isValid() || d_intervalOfInterest.isValid() ) ) + { + return QRectF( 1.0, 1.0, -2.0, -2.0 ); // something invalid + } + + return qwtBoundingRect( *this ); +} + +/*! + Calculate the point from an index + + \param index Index + \return QPointF(x(index), y(x(index))); + + \warning For invalid indices ( index < 0 || index >= size() ) + (0, 0) is returned. +*/ +QPointF QwtSyntheticPointData::sample( size_t index ) const +{ + if ( index >= d_size ) + return QPointF( 0, 0 ); + + const double xValue = x( index ); + const double yValue = y( xValue ); + + return QPointF( xValue, yValue ); +} + +/*! + Calculate a x-value from an index + + x values are calculated by deviding an interval into + equidistant steps. If !interval().isValid() the + interval is calculated from the "rect of interest". + + \sa interval(), rectOfInterest(), y() +*/ +double QwtSyntheticPointData::x( uint index ) const +{ + const QwtInterval &interval = d_interval.isValid() ? + d_interval : d_intervalOfInterest; + + if ( !interval.isValid() || d_size == 0 || index >= d_size ) + return 0.0; + + const double dx = interval.width() / d_size; + return interval.minValue() + index * dx; +} diff --git a/src/libpcp_qwt/src/qwt_series_data.h b/src/libpcp_qwt/src/qwt_series_data.h new file mode 100644 index 0000000..cf4a8d2 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_series_data.h @@ -0,0 +1,460 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SERIES_DATA_H +#define QWT_SERIES_DATA_H 1 + +#include "qwt_global.h" +#include "qwt_interval.h" +#include "qwt_point_3d.h" +#include "qwt_point_polar.h" +#include <qvector.h> +#include <qrect.h> + +//! \brief A sample of the types (x1-x2, y) or (x, y1-y2) +class QWT_EXPORT QwtIntervalSample +{ +public: + QwtIntervalSample(); + QwtIntervalSample( double, const QwtInterval & ); + QwtIntervalSample( double value, double min, double max ); + + bool operator==( const QwtIntervalSample & ) const; + bool operator!=( const QwtIntervalSample & ) const; + + //! Value + double value; + + //! Interval + QwtInterval interval; +}; + +/*! + Constructor + The value is set to 0.0, the interval is invalid +*/ +inline QwtIntervalSample::QwtIntervalSample(): + value( 0.0 ) +{ +} + +//! Constructor +inline QwtIntervalSample::QwtIntervalSample( + double v, const QwtInterval &intv ): + value( v ), + interval( intv ) +{ +} + +//! Constructor +inline QwtIntervalSample::QwtIntervalSample( + double v, double min, double max ): + value( v ), + interval( min, max ) +{ +} + +//! Compare operator +inline bool QwtIntervalSample::operator==( + const QwtIntervalSample &other ) const +{ + return value == other.value && interval == other.interval; +} + +//! Compare operator +inline bool QwtIntervalSample::operator!=( + const QwtIntervalSample &other ) const +{ + return !( *this == other ); +} + +//! \brief A sample of the types (x1...xn, y) or (x, y1..yn) +class QWT_EXPORT QwtSetSample +{ +public: + QwtSetSample(); + bool operator==( const QwtSetSample &other ) const; + bool operator!=( const QwtSetSample &other ) const; + + //! value + double value; + + //! Vector of values associated to value + QVector<double> set; +}; + +/*! + Constructor + The value is set to 0.0 +*/ +inline QwtSetSample::QwtSetSample(): + value( 0.0 ) +{ +} + +//! Compare operator +inline bool QwtSetSample::operator==( const QwtSetSample &other ) const +{ + return value == other.value && set == other.set; +} + +//! Compare operator +inline bool QwtSetSample::operator!=( const QwtSetSample &other ) const +{ + return !( *this == other ); +} + +/*! + \brief Abstract interface for iterating over samples + + Qwt offers several implementations of the QwtSeriesData API, + but in situations, where data of an application specific format + needs to be displayed, without having to copy it, it is recommended + to implement an individual data access. + + A subclass of QwtSeriesData<QPointF> must implement: + + - size()\n + Should return number of data points. + + - sample()\n + Should return values x and y values of the sample at specific position + as QPointF object. + + - boundingRect()\n + Should return the bounding rectangle of the data series. + It is used for autoscaling and might help certain algorithms for displaying + the data. You can use qwtBoundingRect() for an implementation + but often it is possible to implement a more efficient alogrithm + depending on the characteristics of the series. + The member d_boundingRect is intended for caching the calculated rectangle. + +*/ +template <typename T> +class QwtSeriesData +{ +public: + QwtSeriesData(); + virtual ~QwtSeriesData(); + + //! \return Number of samples + virtual size_t size() const = 0; + + /*! + Return a sample + \param i Index + \return Sample at position i + */ + virtual T sample( size_t i ) const = 0; + + /*! + Calculate the bounding rect of all samples + + The bounding rect is necessary for autoscaling and can be used + for a couple of painting optimizations. + + qwtBoundingRect(...) offers slow implementations iterating + over the samples. For large sets it is recommended to implement + something faster f.e. by caching the bounding rect. + */ + virtual QRectF boundingRect() const = 0; + + virtual void setRectOfInterest( const QRectF & ); + +protected: + //! Can be used to cache a calculated bounding rectangle + mutable QRectF d_boundingRect; + +private: + QwtSeriesData<T> &operator=( const QwtSeriesData<T> & ); +}; + +//! Constructor +template <typename T> +QwtSeriesData<T>::QwtSeriesData(): + d_boundingRect( 0.0, 0.0, -1.0, -1.0 ) +{ +} + +//! Destructor +template <typename T> +QwtSeriesData<T>::~QwtSeriesData() +{ +} + +/*! + Set a the "rect of interest" + + QwtPlotSeriesItem defines the current area of the plot canvas + as "rect of interest" ( QwtPlotSeriesItem::updateScaleDiv() ). + It can be used to implement different levels of details. + + The default implementation does nothing. +*/ +template <typename T> +void QwtSeriesData<T>::setRectOfInterest( const QRectF & ) +{ +} + +/*! + \brief Template class for data, that is organized as QVector + + QVector uses implicit data sharing and can be + passed around as argument efficiently. +*/ +template <typename T> +class QwtArraySeriesData: public QwtSeriesData<T> +{ +public: + QwtArraySeriesData(); + QwtArraySeriesData( const QVector<T> & ); + + void setSamples( const QVector<T> & ); + const QVector<T> samples() const; + + virtual size_t size() const; + virtual T sample( size_t ) const; + +protected: + //! Vector of samples + QVector<T> d_samples; +}; + +//! Constructor +template <typename T> +QwtArraySeriesData<T>::QwtArraySeriesData() +{ +} + +/*! + Constructor + \param samples Array of samples +*/ +template <typename T> +QwtArraySeriesData<T>::QwtArraySeriesData( const QVector<T> &samples ): + d_samples( samples ) +{ +} + +/*! + Assign an array of samples + \param samples Array of samples +*/ +template <typename T> +void QwtArraySeriesData<T>::setSamples( const QVector<T> &samples ) +{ + QwtSeriesData<T>::d_boundingRect = QRectF( 0.0, 0.0, -1.0, -1.0 ); + d_samples = samples; +} + +//! \return Array of samples +template <typename T> +const QVector<T> QwtArraySeriesData<T>::samples() const +{ + return d_samples; +} + +//! \return Number of samples +template <typename T> +size_t QwtArraySeriesData<T>::size() const +{ + return d_samples.size(); +} + +/*! + Return a sample + \param i Index + \return Sample at position i +*/ +template <typename T> +T QwtArraySeriesData<T>::sample( size_t i ) const +{ + return d_samples[ static_cast<int>( i ) ]; +} + +//! Interface for iterating over an array of points +class QWT_EXPORT QwtPointSeriesData: public QwtArraySeriesData<QPointF> +{ +public: + QwtPointSeriesData( + const QVector<QPointF> & = QVector<QPointF>() ); + + virtual QRectF boundingRect() const; +}; + +//! Interface for iterating over an array of 3D points +class QWT_EXPORT QwtPoint3DSeriesData: public QwtArraySeriesData<QwtPoint3D> +{ +public: + QwtPoint3DSeriesData( + const QVector<QwtPoint3D> & = QVector<QwtPoint3D>() ); + virtual QRectF boundingRect() const; +}; + +//! Interface for iterating over an array of intervals +class QWT_EXPORT QwtIntervalSeriesData: public QwtArraySeriesData<QwtIntervalSample> +{ +public: + QwtIntervalSeriesData( + const QVector<QwtIntervalSample> & = QVector<QwtIntervalSample>() ); + + virtual QRectF boundingRect() const; +}; + +//! Interface for iterating over an array of samples +class QWT_EXPORT QwtSetSeriesData: public QwtArraySeriesData<QwtSetSample> +{ +public: + QwtSetSeriesData( + const QVector<QwtSetSample> & = QVector<QwtSetSample>() ); + + virtual QRectF boundingRect() const; +}; + +/*! + \brief Interface for iterating over two QVector<double> objects. +*/ +class QWT_EXPORT QwtPointArrayData: public QwtSeriesData<QPointF> +{ +public: + QwtPointArrayData( const QVector<double> &x, const QVector<double> &y ); + QwtPointArrayData( const double *x, const double *y, size_t size ); + + virtual QRectF boundingRect() const; + + virtual size_t size() const; + virtual QPointF sample( size_t i ) const; + + const QVector<double> &xData() const; + const QVector<double> &yData() const; + +private: + QVector<double> d_x; + QVector<double> d_y; +}; + +/*! + \brief Data class containing two pointers to memory blocks of doubles. + */ +class QWT_EXPORT QwtCPointerData: public QwtSeriesData<QPointF> +{ +public: + QwtCPointerData( const double *x, const double *y, size_t size ); + + virtual QRectF boundingRect() const; + virtual size_t size() const; + virtual QPointF sample( size_t i ) const; + + const double *xData() const; + const double *yData() const; + +private: + const double *d_x; + const double *d_y; + size_t d_size; +}; + +/*! + \brief Synthetic point data + + QwtSyntheticPointData provides a fixed number of points for an interval. + The points are calculated in equidistant steps in x-direction. + + If the interval is invalid, the points are calculated for + the "rect of interest", what normally is the displayed area on the + plot canvas. In this mode you get different levels of detail, when + zooming in/out. + + \par Example + + The following example shows how to implement a sinus curve. + + \verbatim +#include <cmath> +#include <qwt_series_data.h> +#include <qwt_plot_curve.h> +#include <qwt_plot.h> +#include <qapplication.h> + +class SinusData: public QwtSyntheticPointData +{ +public: + SinusData(): + QwtSyntheticPointData(100) + { + } + virtual double y(double x) const + { + return qSin(x); + } +}; + +int main(int argc, char **argv) +{ + QApplication a(argc, argv); + + QwtPlot plot; + plot.setAxisScale(QwtPlot::xBottom, 0.0, 10.0); + plot.setAxisScale(QwtPlot::yLeft, -1.0, 1.0); + + QwtPlotCurve *curve = new QwtPlotCurve("y = sin(x)"); + curve->setData(SinusData()); + curve->attach(&plot); + + plot.show(); + return a.exec(); +} + \endverbatim +*/ +class QWT_EXPORT QwtSyntheticPointData: public QwtSeriesData<QPointF> +{ +public: + QwtSyntheticPointData( size_t size, + const QwtInterval & = QwtInterval() ); + + void setSize( size_t size ); + size_t size() const; + + void setInterval( const QwtInterval& ); + QwtInterval interval() const; + + virtual QRectF boundingRect() const; + virtual QPointF sample( size_t i ) const; + + /*! + Calculate a y value for a x value + + \param x x value + \return Corresponding y value + */ + virtual double y( double x ) const = 0; + virtual double x( uint index ) const; + + virtual void setRectOfInterest( const QRectF & ); + QRectF rectOfInterest() const; + +private: + size_t d_size; + QwtInterval d_interval; + QRectF d_rectOfInterest; + QwtInterval d_intervalOfInterest; +}; + +QWT_EXPORT QRectF qwtBoundingRect( + const QwtSeriesData<QPointF> &, int from = 0, int to = -1 ); +QWT_EXPORT QRectF qwtBoundingRect( + const QwtSeriesData<QwtPoint3D> &, int from = 0, int to = -1 ); +QWT_EXPORT QRectF qwtBoundingRect( + const QwtSeriesData<QwtPointPolar> &, int from = 0, int to = -1 ); +QWT_EXPORT QRectF qwtBoundingRect( + const QwtSeriesData<QwtIntervalSample> &, int from = 0, int to = -1 ); +QWT_EXPORT QRectF qwtBoundingRect( + const QwtSeriesData<QwtSetSample> &, int from = 0, int to = -1 ); + +#endif diff --git a/src/libpcp_qwt/src/qwt_slider.cpp b/src/libpcp_qwt/src/qwt_slider.cpp new file mode 100644 index 0000000..357f920 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_slider.cpp @@ -0,0 +1,805 @@ +/* -*- 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_slider.h" +#include "qwt_painter.h" +#include "qwt_scale_draw.h" +#include "qwt_scale_map.h" +#include <qevent.h> +#include <qdrawutil.h> +#include <qpainter.h> +#include <qalgorithms.h> +#include <qmath.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qapplication.h> + +class QwtSlider::PrivateData +{ +public: + QRect sliderRect; + + QSize handleSize; + int borderWidth; + int spacing; + + QwtSlider::ScalePos scalePos; + QwtSlider::BackgroundStyles 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::Trough draws the + slider button in a trough, QwtSlider::Slot draws + a slot underneath the button. An or-combination of both + may also be used. The default is QwtSlider::Trough. + + 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, + BackgroundStyles bgStyle ): + QwtAbstractSlider( orientation, parent ) +{ + initSlider( orientation, scalePos, bgStyle ); +} + +void QwtSlider::initSlider( Qt::Orientation orientation, + ScalePos scalePos, BackgroundStyles bgStyle ) +{ + if ( orientation == Qt::Vertical ) + setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ); + else + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); + + d_data = new QwtSlider::PrivateData; + + d_data->borderWidth = 2; + d_data->spacing = 4; + d_data->scalePos = scalePos; + d_data->bgStyle = bgStyle; + + const int handleThickness = 16; + d_data->handleSize.setWidth( 2 * handleThickness ); + d_data->handleSize.setHeight( handleThickness ); + + if ( !( bgStyle & QwtSlider::Trough ) ) + d_data->handleSize.transpose(); + + if ( orientation == Qt::Vertical ) + d_data->handleSize.transpose(); + + 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 == QwtSlider::BottomScale ) + || ( d_data->scalePos == QwtSlider::TopScale ) ) + { + d_data->scalePos = NoScale; + } + + // adopt the policy of layoutSlider (NoScale lays out like Left) + if ( d_data->scalePos == QwtSlider::RightScale ) + align = QwtScaleDraw::RightScale; + else + align = QwtScaleDraw::LeftScale; + } + else + { + // enforce a valid combination of scale position and orientation + if ( ( d_data->scalePos == QwtSlider::LeftScale ) + || ( d_data->scalePos == QwtSlider::RightScale ) ) + { + d_data->scalePos = QwtSlider::NoScale; + } + + // adopt the policy of layoutSlider (NoScale lays out like Bottom) + if ( d_data->scalePos == QwtSlider::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 ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) ) + { + QSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); + } + + QwtAbstractSlider::setOrientation( o ); + layoutSlider( true ); +} + +/*! + \brief Change the scale position (and slider orientation). + + \param scalePos 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 scalePos ) +{ + if ( d_data->scalePos == scalePos ) + return; + + d_data->scalePos = scalePos; + + switch ( d_data->scalePos ) + { + case QwtSlider::BottomScale: + { + setOrientation( Qt::Horizontal ); + scaleDraw()->setAlignment( QwtScaleDraw::BottomScale ); + break; + } + case QwtSlider::TopScale: + { + setOrientation( Qt::Horizontal ); + scaleDraw()->setAlignment( QwtScaleDraw::TopScale ); + break; + } + case QwtSlider::LeftScale: + { + setOrientation( Qt::Vertical ); + scaleDraw()->setAlignment( QwtScaleDraw::LeftScale ); + break; + } + case RightScale: + { + QwtSlider::setOrientation( Qt::Vertical ); + scaleDraw()->setAlignment( QwtScaleDraw::RightScale ); + break; + } + default: + { + // nothing + } + } + + layoutSlider( true ); +} + +//! Return the scale position. +QwtSlider::ScalePos QwtSlider::scalePosition() const +{ + return d_data->scalePos; +} + +/*! + \brief Change the slider's border width + \param width Border width +*/ +void QwtSlider::setBorderWidth( int width ) +{ + if ( width < 0 ) + width = 0; + + if ( width != d_data->borderWidth ) + { + d_data->borderWidth = width; + layoutSlider( true ); + } +} + +/*! + \return the border width. + \sa setBorderWidth() +*/ +int QwtSlider::borderWidth() const +{ + return d_data->borderWidth; +} + +/*! + \brief Change the spacing between pipe and scale + + A spacing of 0 means, that the backbone of the scale is below + 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; + layoutSlider( true ); + } +} + +/*! + \return Number of pixels between slider and scale + \sa setSpacing() +*/ +int QwtSlider::spacing() const +{ + return d_data->spacing; +} + +/*! + \brief Set the slider's handle size + \param width Width + \param height Height + + \sa handleSize() +*/ +void QwtSlider::setHandleSize( int width, int height ) +{ + setHandleSize( QSize( width, height ) ); +} + +/*! + \brief Set the slider's handle size + \param size New size + + \sa handleSize() +*/ +void QwtSlider::setHandleSize( const QSize &size ) +{ + const QSize handleSize = size.expandedTo( QSize( 8, 4 ) ); + if ( handleSize != d_data->handleSize ) + { + d_data->handleSize = handleSize; + 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 + new and will be deleted in ~QwtSlider or the next + call of setScaleDraw(). + + \sa scaleDraw() +*/ +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( 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() +{ + return static_cast<QwtScaleDraw *>( abstractScaleDraw() ); +} + +//! Notify changed scale +void QwtSlider::scaleChange() +{ + layoutSlider( true ); +} + +/*! + 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->bgStyle & QwtSlider::Trough ) + { + const int bw = d_data->borderWidth; + + qDrawShadePanel( painter, sliderRect, palette(), true, bw, NULL ); + + innerRect = sliderRect.adjusted( bw, bw, -bw, -bw ); + painter->fillRect( innerRect, palette().brush( QPalette::Mid ) ); + } + + if ( d_data->bgStyle & QwtSlider::Groove ) + { + int ws = 4; + int ds = d_data->handleSize.width() / 2 - 4; + if ( ds < 1 ) + ds = 1; + + QRect rSlot; + if ( orientation() == Qt::Horizontal ) + { + if ( innerRect.height() & 1 ) + ws++; + + rSlot = QRect( innerRect.x() + ds, + innerRect.y() + ( innerRect.height() - ws ) / 2, + innerRect.width() - 2 * ds, ws ); + } + else + { + if ( innerRect.width() & 1 ) + ws++; + + rSlot = QRect( innerRect.x() + ( innerRect.width() - ws ) / 2, + innerRect.y() + ds, + ws, innerRect.height() - 2 * ds ); + } + + QBrush brush = palette().brush( QPalette::Dark ); + qDrawShadePanel( painter, rSlot, palette(), true, 1 , &brush ); + } + + if ( isValid() ) + drawHandle( painter, innerRect, transform( value() ) ); +} + +/*! + Draw the thumb at a position + + \param painter Painter + \param sliderRect Bounding rectangle of the slider + \param pos Position of the slider thumb +*/ +void QwtSlider::drawHandle( QPainter *painter, + const QRect &sliderRect, int pos ) const +{ + const int bw = d_data->borderWidth; + + pos++; // shade line points one pixel below + if ( orientation() == Qt::Horizontal ) + { + QRect handleRect( + pos - d_data->handleSize.width() / 2, + sliderRect.y(), + d_data->handleSize.width(), + sliderRect.height() + ); + + qDrawShadePanel( painter, + handleRect, palette(), false, bw, + &palette().brush( QPalette::Button ) ); + + qDrawShadeLine( painter, pos, sliderRect.top() + bw, + pos, sliderRect.bottom() - bw, + palette(), true, 1 ); + } + else // Vertical + { + QRect handleRect( + sliderRect.left(), + pos - d_data->handleSize.height() / 2, + sliderRect.width(), + d_data->handleSize.height() + ); + + qDrawShadePanel( painter, + handleRect, palette(), false, bw, + &palette().brush( QPalette::Button ) ); + + qDrawShadeLine( painter, sliderRect.left() + bw, pos, + sliderRect.right() - bw, pos, + palette(), true, 1 ); + } +} + +/*! + Map and round a value into widget coordinates + \param value Value +*/ +int QwtSlider::transform( double value ) const +{ + return qRound( d_data->map.transform( value ) ); +} + +/*! + Determine the value corresponding to a specified mouse location. + \param pos Mouse position +*/ +double QwtSlider::getValue( const QPoint &pos ) +{ + return d_data->map.invTransform( + orientation() == Qt::Horizontal ? pos.x() : pos.y() ); +} + +/*! + \brief Determine scrolling mode and direction + \param p point + \param scrollMode Scrolling mode + \param direction Direction +*/ +void QwtSlider::getScrollMode( const QPoint &p, + QwtAbstractSlider::ScrollMode &scrollMode, int &direction ) const +{ + if ( !d_data->sliderRect.contains( p ) ) + { + scrollMode = QwtAbstractSlider::ScrNone; + direction = 0; + return; + } + + const int pos = ( orientation() == Qt::Horizontal ) ? p.x() : p.y(); + const int markerPos = transform( value() ); + + if ( ( pos > markerPos - d_data->handleSize.width() / 2 ) + && ( pos < markerPos + d_data->handleSize.width() / 2 ) ) + { + scrollMode = QwtAbstractSlider::ScrMouse; + direction = 0; + return; + } + + scrollMode = QwtAbstractSlider::ScrPage; + direction = ( pos > markerPos ) ? 1 : -1; + + if ( scaleDraw()->scaleMap().p1() > scaleDraw()->scaleMap().p2() ) + direction = -direction; +} + +/*! + Qt paint event + \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->scalePos != QwtSlider::NoScale ) + { + if ( !d_data->sliderRect.contains( event->rect() ) ) + scaleDraw()->draw( &painter, palette() ); + } + + drawSlider( &painter, d_data->sliderRect ); + + if ( hasFocus() ) + QwtPainter::drawFocusRect( &painter, this, d_data->sliderRect ); +} + +//! Qt resize event +void QwtSlider::resizeEvent( QResizeEvent * ) +{ + layoutSlider( false ); +} + +//! Qt change event handler +void QwtSlider::changeEvent( QEvent *event ) +{ + switch( event->type() ) + { + case QEvent::StyleChange: + case QEvent::FontChange: + layoutSlider( true ); + break; + default: + break; + } +} + +/*! + 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 handleThickness; + if ( orientation() == Qt::Horizontal ) + handleThickness = d_data->handleSize.width(); + else + handleThickness = d_data->handleSize.height(); + + int sld1 = handleThickness / 2 - 1; + int sld2 = handleThickness / 2 + handleThickness % 2; + + if ( d_data->bgStyle & QwtSlider::Trough ) + { + sld1 += d_data->borderWidth; + sld2 += d_data->borderWidth; + } + + int scd = 0; + if ( d_data->scalePos != QwtSlider::NoScale ) + { + int d1, d2; + scaleDraw()->getBorderDistHint( font(), d1, d2 ); + scd = qMax( d1, d2 ); + } + + int slo = scd - sld1; + if ( slo < 0 ) + slo = 0; + + int x, y, length; + QRect sliderRect; + + length = x = y = 0; + + const QRect cr = contentsRect(); + if ( orientation() == Qt::Horizontal ) + { + int sh = d_data->handleSize.height(); + if ( d_data->bgStyle & QwtSlider::Trough ) + sh += 2 * d_data->borderWidth; + + sliderRect.setLeft( cr.left() + slo ); + sliderRect.setRight( cr.right() - slo ); + sliderRect.setTop( cr.top() ); + sliderRect.setBottom( cr.top() + sh - 1); + + if ( d_data->scalePos == QwtSlider::BottomScale ) + { + y = sliderRect.bottom() + d_data->spacing; + } + else if ( d_data->scalePos == QwtSlider::TopScale ) + { + sliderRect.setTop( cr.bottom() - sh + 1 ); + sliderRect.setBottom( cr.bottom() ); + + y = sliderRect.top() - d_data->spacing; + } + + x = sliderRect.left() + sld1; + length = sliderRect.width() - ( sld1 + sld2 ); + } + else // Qt::Vertical + { + int sw = d_data->handleSize.width(); + if ( d_data->bgStyle & QwtSlider::Trough ) + sw += 2 * d_data->borderWidth; + + sliderRect.setLeft( cr.right() - sw + 1 ); + sliderRect.setRight( cr.right() ); + sliderRect.setTop( cr.top() + slo ); + sliderRect.setBottom( cr.bottom() - slo ); + + if ( d_data->scalePos == QwtSlider::LeftScale ) + { + x = sliderRect.left() - d_data->spacing; + } + else if ( d_data->scalePos == QwtSlider::RightScale ) + { + sliderRect.setLeft( cr.left() ); + sliderRect.setRight( cr.left() + sw - 1); + + x = sliderRect.right() + d_data->spacing; + } + + y = sliderRect.top() + sld1; + length = sliderRect.height() - ( sld1 + sld2 ); + } + + d_data->sliderRect = sliderRect; + + scaleDraw()->move( x, y ); + scaleDraw()->setLength( length ); + + d_data->map.setPaintInterval( scaleDraw()->scaleMap().p1(), + scaleDraw()->scaleMap().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( true ); +} + +/*! + Set the background style. +*/ +void QwtSlider::setBackgroundStyle( BackgroundStyles style ) +{ + d_data->bgStyle = style; + layoutSlider( true ); +} + +/*! + \return the background style. +*/ +QwtSlider::BackgroundStyles QwtSlider::backgroundStyle() const +{ + return d_data->bgStyle; +} + +/*! + \return QwtSlider::minimumSizeHint() +*/ +QSize QwtSlider::sizeHint() const +{ + const QSize hint = minimumSizeHint(); + return hint.expandedTo( QApplication::globalStrut() ); +} + +/*! + \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; + + const int minLength = 84; // from QSlider + + int handleLength = d_data->handleSize.width(); + int handleThickness = d_data->handleSize.height(); + + if ( orientation() == Qt::Vertical ) + qSwap( handleLength, handleThickness ); + + int w = minLength; + int h = handleThickness; + + if ( d_data->scalePos != QwtSlider::NoScale ) + { + int d1, d2; + scaleDraw()->getBorderDistHint( font(), d1, d2 ); + + const int sdBorderDist = 2 * qMax( d1, d2 ); + const int sdExtent = qCeil( scaleDraw()->extent( font() ) ); + const int sdLength = scaleDraw()->minLength( font() ); + + int l = sdLength; + if ( handleLength > sdBorderDist ) + { + // We need additional space for the overlapping handle + l += handleLength - sdBorderDist; + } + + w = qMax( l, w ); + h += sdExtent + d_data->spacing; + } + + if ( d_data->bgStyle & QwtSlider::Trough ) + h += 2 * d_data->borderWidth; + + if ( orientation() == Qt::Vertical ) + qSwap( w, h ); + + int left, right, top, bottom; + getContentsMargins( &left, &top, &right, &bottom ); + + w += left + right; + h += top + bottom; + + d_data->sizeHintCache = QSize( w, h ); + return d_data->sizeHintCache; +} diff --git a/src/libpcp_qwt/src/qwt_slider.h b/src/libpcp_qwt/src/qwt_slider.h new file mode 100644 index 0000000..bc39456 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_slider.h @@ -0,0 +1,150 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SLIDER_H +#define QWT_SLIDER_H + +#include "qwt_global.h" +#include "qwt_abstract_scale.h" +#include "qwt_abstract_slider.h" + +class QwtScaleDraw; + +/*! + \brief The Slider Widget + + QwtSlider is a slider widget which operates on an interval + of type double. QwtSlider supports different layouts as + well as a scale. + + \image html sliders.png + + \sa QwtAbstractSlider and QwtAbstractScale for the descriptions + of the inherited members. +*/ + +class QWT_EXPORT QwtSlider : public QwtAbstractSlider, public QwtAbstractScale +{ + Q_OBJECT + Q_ENUMS( ScalePos ) + Q_ENUMS( BackgroundStyle ) + Q_PROPERTY( ScalePos scalePosition READ scalePosition + WRITE setScalePosition ) + Q_PROPERTY( BackgroundStyles backgroundStyle + READ backgroundStyle WRITE setBackgroundStyle ) + Q_PROPERTY( QSize handleSize READ handleSize WRITE setHandleSize ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + Q_PROPERTY( int spacing READ spacing WRITE setSpacing ) + +public: + + /*! + Scale position. QwtSlider tries to enforce valid combinations of its + orientation and scale position: + + - Qt::Horizonal combines with NoScale, TopScale and BottomScale + - Qt::Vertical combines with NoScale, LeftScale and RightScale + + \sa QwtSlider() + */ + enum ScalePos + { + //! The slider has no scale + NoScale, + + //! The scale is left of the slider + LeftScale, + + //! The scale is right of the slider + RightScale, + + //! The scale is above of the slider + TopScale, + + //! The scale is below of the slider + BottomScale + }; + + /*! + Background style. + \sa QwtSlider() + */ + enum BackgroundStyle + { + //! Trough background + Trough = 0x01, + + //! Groove + Groove = 0x02, + }; + + //! Background styles + typedef QFlags<BackgroundStyle> BackgroundStyles; + + explicit QwtSlider( QWidget *parent, + Qt::Orientation = Qt::Horizontal, + ScalePos = NoScale, BackgroundStyles = Trough ); + + virtual ~QwtSlider(); + + virtual void setOrientation( Qt::Orientation ); + + void setBackgroundStyle( BackgroundStyles ); + BackgroundStyles backgroundStyle() const; + + void setScalePosition( ScalePos s ); + ScalePos scalePosition() const; + + void setHandleSize( int width, int height ); + void setHandleSize( const QSize & ); + QSize handleSize() const; + + void setBorderWidth( int bw ); + int borderWidth() const; + + void setSpacing( int ); + int spacing() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + void setScaleDraw( QwtScaleDraw * ); + const QwtScaleDraw *scaleDraw() const; + +protected: + virtual double getValue( const QPoint &p ); + virtual void getScrollMode( const QPoint &p, + QwtAbstractSlider::ScrollMode &, int &direction ) const; + + virtual void drawSlider ( QPainter *, const QRect & ) const; + virtual void drawHandle( QPainter *, const QRect &, int pos ) const; + + virtual void resizeEvent( QResizeEvent * ); + virtual void paintEvent ( QPaintEvent * ); + virtual void changeEvent( QEvent * ); + + virtual void valueChange(); + virtual void rangeChange(); + virtual void scaleChange(); + + int transform( double v ) const; + + QwtScaleDraw *scaleDraw(); + +private: + void layoutSlider( bool ); + void initSlider( Qt::Orientation, ScalePos, BackgroundStyles ); + + class PrivateData; + PrivateData *d_data; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtSlider::BackgroundStyles ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_spline.cpp b/src/libpcp_qwt/src/qwt_spline.cpp new file mode 100644 index 0000000..5c1e13f --- /dev/null +++ b/src/libpcp_qwt/src/qwt_spline.cpp @@ -0,0 +1,380 @@ +/* -*- 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_spline.h" +#include "qwt_math.h" + +class QwtSpline::PrivateData +{ +public: + PrivateData(): + splineType( QwtSpline::Natural ) + { + } + + QwtSpline::SplineType splineType; + + // coefficient vectors + QVector<double> a; + QVector<double> b; + QVector<double> c; + + // control points + QPolygonF points; +}; + +static int lookup( double x, const QPolygonF &values ) +{ +#if 0 +//qLowerBound/qHigherBound ??? +#endif + int i1; + const int size = values.size(); + + if ( x <= values[0].x() ) + i1 = 0; + else if ( x >= values[size - 2].x() ) + i1 = size - 2; + else + { + i1 = 0; + int i2 = size - 2; + int i3 = 0; + + while ( i2 - i1 > 1 ) + { + i3 = i1 + ( ( i2 - i1 ) >> 1 ); + + if ( values[i3].x() > x ) + i2 = i3; + else + i1 = i3; + } + } + return i1; +} + +//! Constructor +QwtSpline::QwtSpline() +{ + d_data = new PrivateData; +} + +/*! + Copy constructor + \param other Spline used for initilization +*/ +QwtSpline::QwtSpline( const QwtSpline& other ) +{ + d_data = new PrivateData( *other.d_data ); +} + +/*! + Assignment operator + \param other Spline used for initilization +*/ +QwtSpline &QwtSpline::operator=( const QwtSpline & other ) +{ + *d_data = *other.d_data; + return *this; +} + +//! Destructor +QwtSpline::~QwtSpline() +{ + delete d_data; +} + +/*! + Select the algorithm used for calculating the spline + + \param splineType Spline type + \sa splineType() +*/ +void QwtSpline::setSplineType( SplineType splineType ) +{ + d_data->splineType = splineType; +} + +/*! + \return the spline type + \sa setSplineType() +*/ +QwtSpline::SplineType QwtSpline::splineType() const +{ + return d_data->splineType; +} + +/*! + \brief Calculate the spline coefficients + + Depending on the value of \a periodic, this function + will determine the coefficients for a natural or a periodic + spline and store them internally. + + \param points Points + \return true if successful + \warning The sequence of x (but not y) values has to be strictly monotone + increasing, which means <code>points[i].x() < points[i+1].x()</code>. + If this is not the case, the function will return false +*/ +bool QwtSpline::setPoints( const QPolygonF& points ) +{ + const int size = points.size(); + if ( size <= 2 ) + { + reset(); + return false; + } + + d_data->points = points; + + d_data->a.resize( size - 1 ); + d_data->b.resize( size - 1 ); + d_data->c.resize( size - 1 ); + + bool ok; + if ( d_data->splineType == Periodic ) + ok = buildPeriodicSpline( points ); + else + ok = buildNaturalSpline( points ); + + if ( !ok ) + reset(); + + return ok; +} + +/*! + Return points passed by setPoints +*/ +QPolygonF QwtSpline::points() const +{ + return d_data->points; +} + +//! \return A coefficients +const QVector<double> &QwtSpline::coefficientsA() const +{ + return d_data->a; +} + +//! \return B coefficients +const QVector<double> &QwtSpline::coefficientsB() const +{ + return d_data->b; +} + +//! \return C coefficients +const QVector<double> &QwtSpline::coefficientsC() const +{ + return d_data->c; +} + + +//! Free allocated memory and set size to 0 +void QwtSpline::reset() +{ + d_data->a.resize( 0 ); + d_data->b.resize( 0 ); + d_data->c.resize( 0 ); + d_data->points.resize( 0 ); +} + +//! True if valid +bool QwtSpline::isValid() const +{ + return d_data->a.size() > 0; +} + +/*! + Calculate the interpolated function value corresponding + to a given argument x. +*/ +double QwtSpline::value( double x ) const +{ + if ( d_data->a.size() == 0 ) + return 0.0; + + const int i = lookup( x, d_data->points ); + + const double delta = x - d_data->points[i].x(); + return( ( ( ( d_data->a[i] * delta ) + d_data->b[i] ) + * delta + d_data->c[i] ) * delta + d_data->points[i].y() ); +} + +/*! + \brief Determines the coefficients for a natural spline + \return true if successful +*/ +bool QwtSpline::buildNaturalSpline( const QPolygonF &points ) +{ + int i; + + const QPointF *p = points.data(); + const int size = points.size(); + + double *a = d_data->a.data(); + double *b = d_data->b.data(); + double *c = d_data->c.data(); + + // set up tridiagonal equation system; use coefficient + // vectors as temporary buffers + QVector<double> h( size - 1 ); + for ( i = 0; i < size - 1; i++ ) + { + h[i] = p[i+1].x() - p[i].x(); + if ( h[i] <= 0 ) + return false; + } + + QVector<double> d( size - 1 ); + double dy1 = ( p[1].y() - p[0].y() ) / h[0]; + for ( i = 1; i < size - 1; i++ ) + { + b[i] = c[i] = h[i]; + a[i] = 2.0 * ( h[i-1] + h[i] ); + + const double dy2 = ( p[i+1].y() - p[i].y() ) / h[i]; + d[i] = 6.0 * ( dy1 - dy2 ); + dy1 = dy2; + } + + // + // solve it + // + + // L-U Factorization + for ( i = 1; i < size - 2; i++ ) + { + c[i] /= a[i]; + a[i+1] -= b[i] * c[i]; + } + + // forward elimination + QVector<double> s( size ); + s[1] = d[1]; + for ( i = 2; i < size - 1; i++ ) + s[i] = d[i] - c[i-1] * s[i-1]; + + // backward elimination + s[size - 2] = - s[size - 2] / a[size - 2]; + for ( i = size - 3; i > 0; i-- ) + s[i] = - ( s[i] + b[i] * s[i+1] ) / a[i]; + s[size - 1] = s[0] = 0.0; + + // + // Finally, determine the spline coefficients + // + for ( i = 0; i < size - 1; i++ ) + { + a[i] = ( s[i+1] - s[i] ) / ( 6.0 * h[i] ); + b[i] = 0.5 * s[i]; + c[i] = ( p[i+1].y() - p[i].y() ) / h[i] + - ( s[i+1] + 2.0 * s[i] ) * h[i] / 6.0; + } + + return true; +} + +/*! + \brief Determines the coefficients for a periodic spline + \return true if successful +*/ +bool QwtSpline::buildPeriodicSpline( const QPolygonF &points ) +{ + int i; + + const QPointF *p = points.data(); + const int size = points.size(); + + double *a = d_data->a.data(); + double *b = d_data->b.data(); + double *c = d_data->c.data(); + + QVector<double> d( size - 1 ); + QVector<double> h( size - 1 ); + QVector<double> s( size ); + + // + // setup equation system; use coefficient + // vectors as temporary buffers + // + for ( i = 0; i < size - 1; i++ ) + { + h[i] = p[i+1].x() - p[i].x(); + if ( h[i] <= 0.0 ) + return false; + } + + const int imax = size - 2; + double htmp = h[imax]; + double dy1 = ( p[0].y() - p[imax].y() ) / htmp; + for ( i = 0; i <= imax; i++ ) + { + b[i] = c[i] = h[i]; + a[i] = 2.0 * ( htmp + h[i] ); + const double dy2 = ( p[i+1].y() - p[i].y() ) / h[i]; + d[i] = 6.0 * ( dy1 - dy2 ); + dy1 = dy2; + htmp = h[i]; + } + + // + // solve it + // + + // L-U Factorization + a[0] = qSqrt( a[0] ); + c[0] = h[imax] / a[0]; + double sum = 0; + + for ( i = 0; i < imax - 1; i++ ) + { + b[i] /= a[i]; + if ( i > 0 ) + c[i] = - c[i-1] * b[i-1] / a[i]; + a[i+1] = qSqrt( a[i+1] - qwtSqr( b[i] ) ); + sum += qwtSqr( c[i] ); + } + b[imax-1] = ( b[imax-1] - c[imax-2] * b[imax-2] ) / a[imax-1]; + a[imax] = qSqrt( a[imax] - qwtSqr( b[imax-1] ) - sum ); + + + // forward elimination + s[0] = d[0] / a[0]; + sum = 0; + for ( i = 1; i < imax; i++ ) + { + s[i] = ( d[i] - b[i-1] * s[i-1] ) / a[i]; + sum += c[i-1] * s[i-1]; + } + s[imax] = ( d[imax] - b[imax-1] * s[imax-1] - sum ) / a[imax]; + + + // backward elimination + s[imax] = - s[imax] / a[imax]; + s[imax-1] = -( s[imax-1] + b[imax-1] * s[imax] ) / a[imax-1]; + for ( i = imax - 2; i >= 0; i-- ) + s[i] = - ( s[i] + b[i] * s[i+1] + c[i] * s[imax] ) / a[i]; + + // + // Finally, determine the spline coefficients + // + s[size-1] = s[0]; + for ( i = 0; i < size - 1; i++ ) + { + a[i] = ( s[i+1] - s[i] ) / ( 6.0 * h[i] ); + b[i] = 0.5 * s[i]; + c[i] = ( p[i+1].y() - p[i].y() ) + / h[i] - ( s[i+1] + 2.0 * s[i] ) * h[i] / 6.0; + } + + return true; +} diff --git a/src/libpcp_qwt/src/qwt_spline.h b/src/libpcp_qwt/src/qwt_spline.h new file mode 100644 index 0000000..802e1da --- /dev/null +++ b/src/libpcp_qwt/src/qwt_spline.h @@ -0,0 +1,101 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SPLINE_H +#define QWT_SPLINE_H + +#include "qwt_global.h" +#include <qpolygon.h> +#include <qvector.h> + +/*! + \brief A class for spline interpolation + + The QwtSpline class is used for cubical spline interpolation. + Two types of splines, natural and periodic, are supported. + + \par Usage: + <ol> + <li>First call setPoints() to determine the spline coefficients + for a tabulated function y(x). + <li>After the coefficients have been set up, the interpolated + function value for an argument x can be determined by calling + QwtSpline::value(). + </ol> + + \par Example: + \code +#include <qwt_spline.h> + +QPolygonF interpolate(const QPolygonF& points, int numValues) +{ + QwtSpline spline; + if ( !spline.setPoints(points) ) + return points; + + QPolygonF interpolatedPoints(numValues); + + const double delta = + (points[numPoints - 1].x() - points[0].x()) / (points.size() - 1); + for(i = 0; i < points.size(); i++) / interpolate + { + const double x = points[0].x() + i * delta; + interpolatedPoints[i].setX(x); + interpolatedPoints[i].setY(spline.value(x)); + } + return interpolatedPoints; +} + \endcode +*/ + +class QWT_EXPORT QwtSpline +{ +public: + //! Spline type + enum SplineType + { + //! A natural spline + Natural, + + //! A periodic spline + Periodic + }; + + QwtSpline(); + QwtSpline( const QwtSpline & ); + + ~QwtSpline(); + + QwtSpline &operator=( const QwtSpline & ); + + void setSplineType( SplineType ); + SplineType splineType() const; + + bool setPoints( const QPolygonF& points ); + QPolygonF points() const; + + void reset(); + + bool isValid() const; + double value( double x ) const; + + const QVector<double> &coefficientsA() const; + const QVector<double> &coefficientsB() const; + const QVector<double> &coefficientsC() const; + +protected: + bool buildNaturalSpline( const QPolygonF & ); + bool buildPeriodicSpline( const QPolygonF & ); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_symbol.cpp b/src/libpcp_qwt/src/qwt_symbol.cpp new file mode 100644 index 0000000..0966814 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_symbol.cpp @@ -0,0 +1,1006 @@ +/* -*- 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_symbol.h" +#include "qwt_painter.h" +#include <qapplication.h> +#include <qpainter.h> +#include <qmath.h> + +namespace QwtTriangle +{ + enum Type + { + Left, + Right, + Up, + Down + }; +} + +static inline void qwtDrawEllipseSymbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + painter->setBrush( symbol.brush() ); + painter->setPen( symbol.pen() ); + + const QSize size = symbol.size(); + + if ( QwtPainter::roundingAlignment( painter ) ) + { + const int sw = size.width(); + const int sh = size.height(); + const int sw2 = size.width() / 2; + const int sh2 = size.height() / 2; + + for ( int i = 0; i < numPoints; i++ ) + { + const int x = qRound( points[i].x() ); + const int y = qRound( points[i].y() ); + + const QRectF r( x - sw2, y - sh2, sw, sh ); + QwtPainter::drawEllipse( painter, r ); + } + } + else + { + const double sw = size.width(); + const double sh = size.height(); + const double sw2 = 0.5 * size.width(); + const double sh2 = 0.5 * size.height(); + + for ( int i = 0; i < numPoints; i++ ) + { + const double x = points[i].x(); + const double y = points[i].y(); + + const QRectF r( x - sw2, y - sh2, sw, sh ); + QwtPainter::drawEllipse( painter, r ); + } + } +} + +static inline void qwtDrawRectSymbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + const QSize size = symbol.size(); + + QPen pen = symbol.pen(); + pen.setJoinStyle( Qt::MiterJoin ); + painter->setPen( pen ); + painter->setBrush( symbol.brush() ); + painter->setRenderHint( QPainter::Antialiasing, false ); + + if ( QwtPainter::roundingAlignment( painter ) ) + { + const int sw = size.width(); + const int sh = size.height(); + const int sw2 = size.width() / 2; + const int sh2 = size.height() / 2; + + for ( int i = 0; i < numPoints; i++ ) + { + const int x = qRound( points[i].x() ); + const int y = qRound( points[i].y() ); + + const QRect r( x - sw2, y - sh2, sw, sh ); + QwtPainter::drawRect( painter, r ); + } + } + else + { + const double sw = size.width(); + const double sh = size.height(); + const double sw2 = 0.5 * size.width(); + const double sh2 = 0.5 * size.height(); + + for ( int i = 0; i < numPoints; i++ ) + { + const double x = points[i].x(); + const double y = points[i].y(); + + const QRectF r( x - sw2, y - sh2, sw, sh ); + QwtPainter::drawRect( painter, r ); + } + } +} + +static inline void qwtDrawDiamondSymbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + const QSize size = symbol.size(); + + QPen pen = symbol.pen(); + pen.setJoinStyle( Qt::MiterJoin ); + painter->setPen( pen ); + painter->setBrush( symbol.brush() ); + + if ( QwtPainter::roundingAlignment( painter ) ) + { + for ( int i = 0; i < numPoints; i++ ) + { + const int x = qRound( points[i].x() ); + const int y = qRound( points[i].y() ); + + const int x1 = x - size.width() / 2; + const int y1 = y - size.height() / 2; + const int x2 = x1 + size.width(); + const int y2 = y1 + size.height(); + + QPolygonF polygon; + polygon += QPointF( x, y1 ); + polygon += QPointF( x1, y ); + polygon += QPointF( x, y2 ); + polygon += QPointF( x2, y ); + + QwtPainter::drawPolygon( painter, polygon ); + } + } + else + { + for ( int i = 0; i < numPoints; i++ ) + { + const QPointF &pos = points[i]; + + const double x1 = pos.x() - 0.5 * size.width(); + const double y1 = pos.y() - 0.5 * size.height(); + const double x2 = x1 + size.width(); + const double y2 = y1 + size.height(); + + QPolygonF polygon; + polygon += QPointF( pos.x(), y1 ); + polygon += QPointF( x2, pos.y() ); + polygon += QPointF( pos.x(), y2 ); + polygon += QPointF( x1, pos.y() ); + + QwtPainter::drawPolygon( painter, polygon ); + } + } +} + +static inline void qwtDrawTriangleSymbols( + QPainter *painter, QwtTriangle::Type type, + const QPointF *points, int numPoints, + const QwtSymbol &symbol ) +{ + const QSize size = symbol.size(); + + QPen pen = symbol.pen(); + pen.setJoinStyle( Qt::MiterJoin ); + painter->setPen( pen ); + + painter->setBrush( symbol.brush() ); + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + double sw2 = 0.5 * size.width(); + double sh2 = 0.5 * size.height(); + + if ( doAlign ) + { + sw2 = qFloor( sw2 ); + sh2 = qFloor( sh2 ); + } + + QPolygonF triangle( 3 ); + QPointF *trianglePoints = triangle.data(); + + for ( int i = 0; i < numPoints; i++ ) + { + const QPointF &pos = points[i]; + + double x = pos.x(); + double y = pos.y(); + + if ( doAlign ) + { + x = qRound( x ); + y = qRound( y ); + } + + const double x1 = x - sw2; + const double x2 = x1 + size.width(); + const double y1 = y - sh2; + const double y2 = y1 + size.height(); + + switch ( type ) + { + case QwtTriangle::Left: + { + trianglePoints[0].rx() = x2; + trianglePoints[0].ry() = y1; + + trianglePoints[1].rx() = x1; + trianglePoints[1].ry() = y; + + trianglePoints[2].rx() = x2; + trianglePoints[2].ry() = y2; + + break; + } + case QwtTriangle::Right: + { + trianglePoints[0].rx() = x1; + trianglePoints[0].ry() = y1; + + trianglePoints[1].rx() = x2; + trianglePoints[1].ry() = y; + + trianglePoints[2].rx() = x1; + trianglePoints[2].ry() = y2; + + break; + } + case QwtTriangle::Up: + { + trianglePoints[0].rx() = x1; + trianglePoints[0].ry() = y2; + + trianglePoints[1].rx() = x; + trianglePoints[1].ry() = y1; + + trianglePoints[2].rx() = x2; + trianglePoints[2].ry() = y2; + + break; + } + case QwtTriangle::Down: + { + trianglePoints[0].rx() = x1; + trianglePoints[0].ry() = y1; + + trianglePoints[1].rx() = x; + trianglePoints[1].ry() = y2; + + trianglePoints[2].rx() = x2; + trianglePoints[2].ry() = y1; + + break; + } + } + QwtPainter::drawPolygon( painter, triangle ); + } +} + +static inline void qwtDrawLineSymbols( + QPainter *painter, int orientations, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + const QSize size = symbol.size(); + + int off = 0; + + QPen pen = symbol.pen(); + if ( pen.width() > 1 ) + { + pen.setCapStyle( Qt::FlatCap ); + off = 1; + } + + painter->setPen( pen ); + painter->setRenderHint( QPainter::Antialiasing, false ); + + if ( QwtPainter::roundingAlignment( painter ) ) + { + const int sw = qFloor( size.width() ); + const int sh = qFloor( size.height() ); + const int sw2 = size.width() / 2; + const int sh2 = size.height() / 2; + + for ( int i = 0; i < numPoints; i++ ) + { + if ( orientations & Qt::Horizontal ) + { + const int x = qRound( points[i].x() ) - sw2; + const int y = qRound( points[i].y() ); + + QwtPainter::drawLine( painter, x, y, x + sw + off, y ); + } + if ( orientations & Qt::Vertical ) + { + const int x = qRound( points[i].x() ); + const int y = qRound( points[i].y() ) - sh2; + + QwtPainter::drawLine( painter, x, y, x, y + sh + off ); + } + } + } + else + { + const double sw = size.width(); + const double sh = size.height(); + const double sw2 = 0.5 * size.width(); + const double sh2 = 0.5 * size.height(); + + for ( int i = 0; i < numPoints; i++ ) + { + if ( orientations & Qt::Horizontal ) + { + const double x = points[i].x() - sw2; + const double y = points[i].y(); + + QwtPainter::drawLine( painter, x, y, x + sw, y ); + } + if ( orientations & Qt::Vertical ) + { + const double y = points[i].y() - sh2; + const double x = points[i].x(); + + QwtPainter::drawLine( painter, x, y, x, y + sh ); + } + } + } +} + +static inline void qwtDrawXCrossSymbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + const QSize size = symbol.size(); + int off = 0; + + QPen pen = symbol.pen(); + if ( pen.width() > 1 ) + { + pen.setCapStyle( Qt::FlatCap ); + off = 1; + } + painter->setPen( pen ); + + + if ( QwtPainter::roundingAlignment( painter ) ) + { + const int sw = size.width(); + const int sh = size.height(); + const int sw2 = size.width() / 2; + const int sh2 = size.height() / 2; + + for ( int i = 0; i < numPoints; i++ ) + { + const QPointF &pos = points[i]; + + const int x = qRound( pos.x() ); + const int y = qRound( pos.y() ); + + const int x1 = x - sw2; + const int x2 = x1 + sw + off; + const int y1 = y - sh2; + const int y2 = y1 + sh + off; + + QwtPainter::drawLine( painter, x1, y1, x2, y2 ); + QwtPainter::drawLine( painter, x2, y1, x1, y2 ); + } + } + else + { + const double sw = size.width(); + const double sh = size.height(); + const double sw2 = 0.5 * size.width(); + const double sh2 = 0.5 * size.height(); + + for ( int i = 0; i < numPoints; i++ ) + { + const QPointF &pos = points[i]; + + const double x1 = pos.x() - sw2; + const double x2 = x1 + sw; + const double y1 = pos.y() - sh2; + const double y2 = y1 + sh; + + QwtPainter::drawLine( painter, x1, y1, x2, y2 ); + QwtPainter::drawLine( painter, x1, y2, x2, y1 ); + } + } +} + +static inline void qwtDrawStar1Symbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + const QSize size = symbol.size(); + painter->setPen( symbol.pen() ); + + if ( QwtPainter::roundingAlignment( painter ) ) + { + QRect r( 0, 0, size.width(), size.height() ); + + for ( int i = 0; i < numPoints; i++ ) + { + r.moveCenter( points[i].toPoint() ); + + const double sqrt1_2 = 0.70710678118654752440; /* 1/sqrt(2) */ + + const double d1 = r.width() / 2.0 * ( 1.0 - sqrt1_2 ); + + QwtPainter::drawLine( painter, + qRound( r.left() + d1 ), qRound( r.top() + d1 ), + qRound( r.right() - d1 ), qRound( r.bottom() - d1 ) ); + QwtPainter::drawLine( painter, + qRound( r.left() + d1 ), qRound( r.bottom() - d1 ), + qRound( r .right() - d1), qRound( r.top() + d1 ) ); + + const QPoint c = r.center(); + + QwtPainter::drawLine( painter, + c.x(), r.top(), c.x(), r.bottom() ); + QwtPainter::drawLine( painter, + r.left(), c.y(), r.right(), c.y() ); + } + } + else + { + QRectF r( 0, 0, size.width(), size.height() ); + + for ( int i = 0; i < numPoints; i++ ) + { + r.moveCenter( points[i] ); + + const double sqrt1_2 = 0.70710678118654752440; /* 1/sqrt(2) */ + + const QPointF c = r.center(); + const double d1 = r.width() / 2.0 * ( 1.0 - sqrt1_2 ); + + QwtPainter::drawLine( painter, + r.left() + d1, r.top() + d1, + r.right() - d1, r.bottom() - d1 ); + QwtPainter::drawLine( painter, + r.left() + d1, r.bottom() - d1, + r.right() - d1, r.top() + d1 ); + QwtPainter::drawLine( painter, + c.x(), r.top(), + c.x(), r.bottom() ); + QwtPainter::drawLine( painter, + r.left(), c.y(), + r.right(), c.y() ); + } + } +} + +static inline void qwtDrawStar2Symbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + QPen pen = symbol.pen(); + if ( pen.width() > 1 ) + pen.setCapStyle( Qt::FlatCap ); + pen.setJoinStyle( Qt::MiterJoin ); + painter->setPen( pen ); + + painter->setBrush( symbol.brush() ); + + const double cos30 = 0.866025; // cos(30°) + + const double dy = 0.25 * symbol.size().height(); + const double dx = 0.5 * symbol.size().width() * cos30 / 3.0; + + QPolygonF star( 12 ); + QPointF *starPoints = star.data(); + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + for ( int i = 0; i < numPoints; i++ ) + { + double x = points[i].x(); + double y = points[i].y(); + if ( doAlign ) + { + x = qRound( x ); + y = qRound( y ); + } + + double x1 = x - 3 * dx; + double y1 = y - 2 * dy; + if ( doAlign ) + { + x1 = qRound( x - 3 * dx ); + y1 = qRound( y - 2 * dy ); + } + + const double x2 = x1 + 1 * dx; + const double x3 = x1 + 2 * dx; + const double x4 = x1 + 3 * dx; + const double x5 = x1 + 4 * dx; + const double x6 = x1 + 5 * dx; + const double x7 = x1 + 6 * dx; + + const double y2 = y1 + 1 * dy; + const double y3 = y1 + 2 * dy; + const double y4 = y1 + 3 * dy; + const double y5 = y1 + 4 * dy; + + starPoints[0].rx() = x4; + starPoints[0].ry() = y1; + + starPoints[1].rx() = x5; + starPoints[1].ry() = y2; + + starPoints[2].rx() = x7; + starPoints[2].ry() = y2; + + starPoints[3].rx() = x6; + starPoints[3].ry() = y3; + + starPoints[4].rx() = x7; + starPoints[4].ry() = y4; + + starPoints[5].rx() = x5; + starPoints[5].ry() = y4; + + starPoints[6].rx() = x4; + starPoints[6].ry() = y5; + + starPoints[7].rx() = x3; + starPoints[7].ry() = y4; + + starPoints[8].rx() = x1; + starPoints[8].ry() = y4; + + starPoints[9].rx() = x2; + starPoints[9].ry() = y3; + + starPoints[10].rx() = x1; + starPoints[10].ry() = y2; + + starPoints[11].rx() = x3; + starPoints[11].ry() = y2; + + QwtPainter::drawPolygon( painter, star ); + } +} + +static inline void qwtDrawHexagonSymbols( QPainter *painter, + const QPointF *points, int numPoints, const QwtSymbol &symbol ) +{ + painter->setBrush( symbol.brush() ); + painter->setPen( symbol.pen() ); + + const double cos30 = 0.866025; // cos(30°) + const double dx = 0.5 * ( symbol.size().width() - cos30 ); + + const double dy = 0.25 * symbol.size().height(); + + QPolygonF hexaPolygon( 6 ); + QPointF *hexaPoints = hexaPolygon.data(); + + const bool doAlign = QwtPainter::roundingAlignment( painter ); + + for ( int i = 0; i < numPoints; i++ ) + { + double x = points[i].x(); + double y = points[i].y(); + if ( doAlign ) + { + x = qRound( x ); + y = qRound( y ); + } + + double x1 = x - dx; + double y1 = y - 2 * dy; + if ( doAlign ) + { + x1 = qCeil( x1 ); + y1 = qCeil( y1 ); + } + + const double x2 = x1 + 1 * dx; + const double x3 = x1 + 2 * dx; + + const double y2 = y1 + 1 * dy; + const double y3 = y1 + 3 * dy; + const double y4 = y1 + 4 * dy; + + hexaPoints[0].rx() = x2; + hexaPoints[0].ry() = y1; + + hexaPoints[1].rx() = x3; + hexaPoints[1].ry() = y2; + + hexaPoints[2].rx() = x3; + hexaPoints[2].ry() = y3; + + hexaPoints[3].rx() = x2; + hexaPoints[3].ry() = y4; + + hexaPoints[4].rx() = x1; + hexaPoints[4].ry() = y3; + + hexaPoints[5].rx() = x1; + hexaPoints[5].ry() = y2; + + QwtPainter::drawPolygon( painter, hexaPolygon ); + } +} + +class QwtSymbol::PrivateData +{ +public: + PrivateData( QwtSymbol::Style st, const QBrush &br, + const QPen &pn, const QSize &sz ): + style( st ), + size( sz ), + brush( br ), + pen( pn ) + { + } + + bool operator==( const PrivateData &other ) const + { + return ( style == other.style ) + && ( size == other.size ) + && ( brush == other.brush ) + && ( pen == other.pen ); + } + + + Style style; + QSize size; + QBrush brush; + QPen pen; +}; + +/*! + Default Constructor + \param style Symbol Style + + The symbol is constructed with gray interior, + black outline with zero width, no size and style 'NoSymbol'. +*/ +QwtSymbol::QwtSymbol( Style style ) +{ + d_data = new PrivateData( style, QBrush( Qt::gray ), + QPen( Qt::black ), QSize( 0.0, 0.0 ) ); +} + +/*! + \brief Constructor + \param style Symbol Style + \param brush brush to fill the interior + \param pen outline pen + \param size size + + \sa setStyle(), setBrush(), setPen(), setSize() +*/ +QwtSymbol::QwtSymbol( QwtSymbol::Style style, const QBrush &brush, + const QPen &pen, const QSize &size ) +{ + d_data = new PrivateData( style, brush, pen, size ); +} + +/*! + \brief Copy constructor + + \param other Symbol +*/ +QwtSymbol::QwtSymbol( const QwtSymbol &other ) +{ + d_data = new PrivateData( other.style(), other.brush(), + other.pen(), other.size() ); +}; + +//! Destructor +QwtSymbol::~QwtSymbol() +{ + delete d_data; +} + +//! \brief Assignment operator +QwtSymbol &QwtSymbol::operator=( const QwtSymbol &other ) +{ + *d_data = *other.d_data; + return *this; +} + +//! \brief Compare two symbols +bool QwtSymbol::operator==( const QwtSymbol &other ) const +{ + return *d_data == *other.d_data; +} + +//! \brief Compare two symbols +bool QwtSymbol::operator!=( const QwtSymbol &other ) const +{ + return !( *d_data == *other.d_data ); +} + +/*! + \brief Specify the symbol's size + + If the 'h' parameter is left out or less than 0, + and the 'w' parameter is greater than or equal to 0, + the symbol size will be set to (w,w). + \param width Width + \param height Height (defaults to -1) + + \sa size() +*/ +void QwtSymbol::setSize( int width, int height ) +{ + if ( ( width >= 0 ) && ( height < 0 ) ) + height = width; + + d_data->size = QSize( width, height ); +} + +/*! + Set the symbol's size + \param size Size + + \sa size() +*/ +void QwtSymbol::setSize( const QSize &size ) +{ + if ( size.isValid() ) + d_data->size = size; +} + +/*! + \return Size + \sa setSize() +*/ +const QSize& QwtSymbol::size() const +{ + return d_data->size; +} + +/*! + \brief Assign a brush + + The brush is used to draw the interior of the symbol. + \param brush Brush + + \sa brush() +*/ +void QwtSymbol::setBrush( const QBrush &brush ) +{ + d_data->brush = brush; +} + +/*! + \return Brush + \sa setBrush() +*/ +const QBrush& QwtSymbol::brush() const +{ + return d_data->brush; +} + +/*! + Assign a pen + + The pen is used to draw the symbol's outline. + + \param pen Pen + \sa pen(), setBrush() +*/ +void QwtSymbol::setPen( const QPen &pen ) +{ + d_data->pen = pen; +} + +/*! + \return Pen + \sa setPen(), brush() +*/ +const QPen& QwtSymbol::pen() const +{ + return d_data->pen; +} + +/*! + \brief Set the color of the symbol + + Change the color of the brush for symbol types with a filled area. + For all other symbol types the color will be assigned to the pen. + + \param color Color + + \sa setBrush(), setPen(), brush(), pen() +*/ +void QwtSymbol::setColor( const QColor &color ) +{ + switch ( d_data->style ) + { + case QwtSymbol::Ellipse: + case QwtSymbol::Rect: + case QwtSymbol::Diamond: + case QwtSymbol::Triangle: + case QwtSymbol::UTriangle: + case QwtSymbol::DTriangle: + case QwtSymbol::RTriangle: + case QwtSymbol::LTriangle: + case QwtSymbol::Star2: + case QwtSymbol::Hexagon: + { + d_data->brush.setColor( color ); + break; + } + case QwtSymbol::Cross: + case QwtSymbol::XCross: + case QwtSymbol::HLine: + case QwtSymbol::VLine: + case QwtSymbol::Star1: + { + d_data->pen.setColor( color ); + break; + } + default: + { + d_data->brush.setColor( color ); + d_data->pen.setColor( color ); + } + } +} + +/*! + Draw an array of symbols + + Painting several symbols is more effective than drawing symbols + one by one, as a couple of layout calculations and setting of pen/brush + can be done once for the complete array. + + \param painter Painter + \param points Array of points + \param numPoints Number of points +*/ +void QwtSymbol::drawSymbols( QPainter *painter, + const QPointF *points, int numPoints ) const +{ + if ( numPoints <= 0 ) + return; + + painter->save(); + + switch ( d_data->style ) + { + case QwtSymbol::Ellipse: + { + qwtDrawEllipseSymbols( painter, points, numPoints, *this ); + break; + } + case QwtSymbol::Rect: + { + qwtDrawRectSymbols( painter, points, numPoints, *this ); + break; + } + case QwtSymbol::Diamond: + { + qwtDrawDiamondSymbols( painter, points, numPoints, *this ); + break; + } + case QwtSymbol::Cross: + { + qwtDrawLineSymbols( painter, Qt::Horizontal | Qt::Vertical, + points, numPoints, *this ); + break; + } + case QwtSymbol::XCross: + { + qwtDrawXCrossSymbols( painter, points, numPoints, *this ); + break; + } + case QwtSymbol::Triangle: + case QwtSymbol::UTriangle: + { + qwtDrawTriangleSymbols( painter, QwtTriangle::Up, + points, numPoints, *this ); + break; + } + case QwtSymbol::DTriangle: + { + qwtDrawTriangleSymbols( painter, QwtTriangle::Down, + points, numPoints, *this ); + break; + } + case QwtSymbol::RTriangle: + { + qwtDrawTriangleSymbols( painter, QwtTriangle::Right, + points, numPoints, *this ); + break; + } + case QwtSymbol::LTriangle: + { + qwtDrawTriangleSymbols( painter, QwtTriangle::Left, + points, numPoints, *this ); + break; + } + case QwtSymbol::HLine: + { + qwtDrawLineSymbols( painter, Qt::Horizontal, + points, numPoints, *this ); + break; + } + case QwtSymbol::VLine: + { + qwtDrawLineSymbols( painter, Qt::Vertical, + points, numPoints, *this ); + break; + } + case QwtSymbol::Star1: + { + qwtDrawStar1Symbols( painter, points, numPoints, *this ); + break; + } + case QwtSymbol::Star2: + { + qwtDrawStar2Symbols( painter, points, numPoints, *this ); + break; + } + case QwtSymbol::Hexagon: + { + qwtDrawHexagonSymbols( painter, points, numPoints, *this ); + break; + } + default:; + } + painter->restore(); +} + +//! \return Size of the bounding rectangle of a symbol +QSize QwtSymbol::boundingSize() const +{ + QSizeF size; + + switch ( d_data->style ) + { + case QwtSymbol::Ellipse: + case QwtSymbol::Rect: + case QwtSymbol::Hexagon: + { + qreal pw = 0.0; + if ( d_data->pen.style() != Qt::NoPen ) + pw = qMax( d_data->pen.widthF(), qreal( 1.0 ) ); + + size = d_data->size + QSizeF( pw, pw ); + + break; + } + case QwtSymbol::XCross: + case QwtSymbol::Diamond: + case QwtSymbol::Triangle: + case QwtSymbol::UTriangle: + case QwtSymbol::DTriangle: + case QwtSymbol::RTriangle: + case QwtSymbol::LTriangle: + case QwtSymbol::Star1: + case QwtSymbol::Star2: + { + qreal pw = 0.0; + if ( d_data->pen.style() != Qt::NoPen ) + pw = qMax( d_data->pen.widthF(), qreal( 1.0 ) ); + + size = d_data->size + QSizeF( 2 * pw, 2 * pw ); + break; + } + default: + { + size = d_data->size; + } + } + + size += QSizeF( 1.0, 1.0 ); // for antialiasing + + return QSize( qCeil( size.width() ), qCeil( size.height() ) ); +} + +/*! + Specify the symbol style + + \param style Style + \sa style() +*/ +void QwtSymbol::setStyle( QwtSymbol::Style style ) +{ + d_data->style = style; +} + +/*! + \return Current symbol style + \sa setStyle() +*/ +QwtSymbol::Style QwtSymbol::style() const +{ + return d_data->style; +} diff --git a/src/libpcp_qwt/src/qwt_symbol.h b/src/libpcp_qwt/src/qwt_symbol.h new file mode 100644 index 0000000..4e38431 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_symbol.h @@ -0,0 +1,154 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SYMBOL_H +#define QWT_SYMBOL_H + +#include "qwt_global.h" +#include <QPolygonF> + +class QPainter; +class QRect; +class QSize; +class QBrush; +class QPen; +class QColor; +class QPointF; + +//! A class for drawing symbols +class QWT_EXPORT QwtSymbol +{ +public: + /*! + Symbol Style + \sa setStyle(), style() + */ + enum Style + { + //! No Style. The symbol cannot be drawn. + NoSymbol = -1, + + //! Ellipse or circle + Ellipse, + + //! Rectangle + Rect, + + //! Diamond + Diamond, + + //! Triangle pointing upwards + Triangle, + + //! Triangle pointing downwards + DTriangle, + + //! Triangle pointing upwards + UTriangle, + + //! Triangle pointing left + LTriangle, + + //! Triangle pointing right + RTriangle, + + //! Cross (+) + Cross, + + //! Diagonal cross (X) + XCross, + + //! Horizontal line + HLine, + + //! Vertical line + VLine, + + //! X combined with + + Star1, + + //! Six-pointed star + Star2, + + //! Hexagon + Hexagon, + + /*! + Styles >= QwtSymbol::UserSymbol are reserved for derived + classes of QwtSymbol that overload drawSymbols() with + additional application specific symbol types. + */ + UserStyle = 1000 + }; + +public: + QwtSymbol( Style = NoSymbol ); + QwtSymbol( Style, const QBrush &, const QPen &, const QSize & ); + QwtSymbol( const QwtSymbol & ); + virtual ~QwtSymbol(); + + QwtSymbol &operator=( const QwtSymbol & ); + bool operator==( const QwtSymbol & ) const; + bool operator!=( const QwtSymbol & ) const; + + void setSize( const QSize & ); + void setSize( int width, int height = -1 ); + const QSize& size() const; + + virtual void setColor( const QColor & ); + + void setBrush( const QBrush& b ); + const QBrush& brush() const; + + void setPen( const QPen & ); + const QPen& pen() const; + + void setStyle( Style ); + Style style() const; + + void drawSymbol( QPainter *, const QPointF & ) const; + void drawSymbols( QPainter *, const QPolygonF & ) const; + + virtual QSize boundingSize() const; + +protected: + virtual void drawSymbols( QPainter *, + const QPointF *, int numPoints ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +/*! + \brief Draw the symbol at a specified position + + \param painter Painter + \param pos Position of the symbol in screen coordinates +*/ +inline void QwtSymbol::drawSymbol( + QPainter *painter, const QPointF &pos ) const +{ + drawSymbols( painter, &pos, 1 ); +} + +/*! + \brief Draw symbols at the specified points + + \param painter Painter + \param points Positions of the symbols in screen coordinates +*/ + +inline void QwtSymbol::drawSymbols( + QPainter *painter, const QPolygonF &points ) const +{ + drawSymbols( painter, points.data(), points.size() ); +} + +#endif diff --git a/src/libpcp_qwt/src/qwt_system_clock.cpp b/src/libpcp_qwt/src/qwt_system_clock.cpp new file mode 100644 index 0000000..4816b12 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_system_clock.cpp @@ -0,0 +1,364 @@ +/* -*- 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_system_clock.h" +#include <qdatetime.h> + +#if !defined(Q_OS_WIN) +#include <unistd.h> +#endif + +#if defined(Q_OS_MAC) +#include <stdint.h> +#include <mach/mach_time.h> +#define QWT_HIGH_RESOLUTION_CLOCK +#elif defined(_POSIX_TIMERS) +#include <time.h> +#define QWT_HIGH_RESOLUTION_CLOCK +#elif defined(Q_OS_WIN) +#define QWT_HIGH_RESOLUTION_CLOCK +#include <qt_windows.h> +#endif + +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + +class QwtHighResolutionClock +{ +public: + QwtHighResolutionClock(); + + void start(); + double restart(); + double elapsed() const; + + bool isNull() const; + + static double precision(); + +private: + +#if defined(Q_OS_MAC) + static double msecsTo( uint64_t, uint64_t ); + + uint64_t d_timeStamp; +#elif defined(_POSIX_TIMERS) + + static double msecsTo( const struct timespec &, + const struct timespec & ); + + static bool isMonotonic(); + + struct timespec d_timeStamp; + clockid_t d_clockId; + +#elif defined(Q_OS_WIN) + + LARGE_INTEGER d_startTicks; + LARGE_INTEGER d_ticksPerSecond; +#endif +}; + +#if defined(Q_OS_MAC) +QwtHighResolutionClock::QwtHighResolutionClock(): + d_timeStamp( 0 ) +{ +} + +double QwtHighResolutionClock::precision() +{ + return 1e-6; +} + +void QwtHighResolutionClock::start() +{ + d_timeStamp = mach_absolute_time(); +} + +double QwtHighResolutionClock::restart() +{ + const uint64_t timeStamp = mach_absolute_time(); + const double elapsed = msecsTo( d_timeStamp, timeStamp ); + d_timeStamp = timeStamp; + + return elapsed; +} + +double QwtHighResolutionClock::elapsed() const +{ + return msecsTo( d_timeStamp, mach_absolute_time() ); +} + +bool QwtHighResolutionClock::isNull() const +{ + return d_timeStamp == 0; +} + +double QwtHighResolutionClock::msecsTo( + uint64_t from, uint64_t to ) +{ + const uint64_t difference = to - from; + + static double conversion = 0.0; + if ( conversion == 0.0 ) + { + mach_timebase_info_data_t info; + kern_return_t err = mach_timebase_info( &info ); + + //Convert the timebase into ms + if ( err == 0 ) + conversion = 1e-6 * ( double ) info.numer / ( double ) info.denom; + } + + return conversion * ( double ) difference; +} + +#elif defined(_POSIX_TIMERS) + +QwtHighResolutionClock::QwtHighResolutionClock() +{ + d_clockId = isMonotonic() ? CLOCK_MONOTONIC : CLOCK_REALTIME; + d_timeStamp.tv_sec = d_timeStamp.tv_nsec = 0; +} + +double QwtHighResolutionClock::precision() +{ + struct timespec resolution; + + int clockId = isMonotonic() ? CLOCK_MONOTONIC : CLOCK_REALTIME; + ::clock_getres( clockId, &resolution ); + + return resolution.tv_nsec / 1e3; +} + +inline bool QwtHighResolutionClock::isNull() const +{ + return d_timeStamp.tv_sec <= 0 && d_timeStamp.tv_nsec <= 0; +} + +inline void QwtHighResolutionClock::start() +{ + ::clock_gettime( d_clockId, &d_timeStamp ); +} + +double QwtHighResolutionClock::restart() +{ + struct timespec timeStamp; + ::clock_gettime( d_clockId, &timeStamp ); + + const double elapsed = msecsTo( d_timeStamp, timeStamp ); + + d_timeStamp = timeStamp; + return elapsed; +} + +inline double QwtHighResolutionClock::elapsed() const +{ + struct timespec timeStamp; + ::clock_gettime( d_clockId, &timeStamp ); + + return msecsTo( d_timeStamp, timeStamp ); +} + +inline double QwtHighResolutionClock::msecsTo( + const struct timespec &t1, const struct timespec &t2 ) +{ + return ( t2.tv_sec - t1.tv_sec ) * 1e3 + + ( t2.tv_nsec - t1.tv_nsec ) * 1e-6; +} + +bool QwtHighResolutionClock::isMonotonic() +{ + // code copied from qcore_unix.cpp + +#if (_POSIX_MONOTONIC_CLOCK-0 > 0) + return true; +#else + static int returnValue = 0; + + if ( returnValue == 0 ) + { +#if (_POSIX_MONOTONIC_CLOCK-0 < 0) || !defined(_SC_MONOTONIC_CLOCK) + returnValue = -1; +#elif (_POSIX_MONOTONIC_CLOCK == 0) + // detect if the system support monotonic timers + const long x = sysconf( _SC_MONOTONIC_CLOCK ); + returnValue = ( x >= 200112L ) ? 1 : -1; +#endif + } + + return returnValue != -1; +#endif +} + +#elif defined(Q_OS_WIN) + +QwtHighResolutionClock::QwtHighResolutionClock() +{ + d_startTicks.QuadPart = 0; + QueryPerformanceFrequency( &d_ticksPerSecond ); +} + +double QwtHighResolutionClock::precision() +{ + LARGE_INTEGER ticks; + if ( QueryPerformanceFrequency( &ticks ) && ticks.QuadPart > 0 ) + return 1e3 / ticks.QuadPart; + + return 0.0; +} + +inline bool QwtHighResolutionClock::isNull() const +{ + return d_startTicks.QuadPart <= 0; +} + +inline void QwtHighResolutionClock::start() +{ + QueryPerformanceCounter( &d_startTicks ); +} + +inline double QwtHighResolutionClock::restart() +{ + LARGE_INTEGER ticks; + QueryPerformanceCounter( &ticks ); + + const double dt = ticks.QuadPart - d_startTicks.QuadPart; + d_startTicks = ticks; + + return dt / d_ticksPerSecond.QuadPart * 1e3; +} + +inline double QwtHighResolutionClock::elapsed() const +{ + LARGE_INTEGER ticks; + QueryPerformanceCounter( &ticks ); + + const double dt = ticks.QuadPart - d_startTicks.QuadPart; + return dt / d_ticksPerSecond.QuadPart * 1e3; +} + +#endif + +#endif // QWT_HIGH_RESOLUTION_CLOCK + +class QwtSystemClock::PrivateData +{ +public: +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + QwtHighResolutionClock *clock; +#endif + QTime time; +}; + +//! Constructs a null clock object. +QwtSystemClock::QwtSystemClock() +{ + d_data = new PrivateData; + +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + d_data->clock = NULL; + if ( QwtHighResolutionClock::precision() > 0.0 ) + d_data->clock = new QwtHighResolutionClock; +#endif +} + +//! Destructor +QwtSystemClock::~QwtSystemClock() +{ +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + delete d_data->clock; +#endif + delete d_data; +} + +/*! + \return true if the clock has never been started. +*/ +bool QwtSystemClock::isNull() const +{ +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + if ( d_data->clock ) + return d_data->clock->isNull(); +#endif + + return d_data->time.isNull(); +} + +/*! + Sets the start time to the current time. +*/ +void QwtSystemClock::start() +{ +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + if ( d_data->clock ) + { + d_data->clock->start(); + return; + } +#endif + + d_data->time.start(); +} + +/*! + The start time to the current time and + return the time, that is elapsed since the + previous start time. +*/ +double QwtSystemClock::restart() +{ +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + if ( d_data->clock ) + return d_data->clock->restart(); +#endif + + return d_data->time.restart(); +} + +/*! + \return Number of milliseconds that have elapsed since the last time + start() or restart() was called or 0.0 for null clocks. +*/ +double QwtSystemClock::elapsed() const +{ + double elapsed = 0.0; + +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + if ( d_data->clock ) + { + if ( !d_data->clock->isNull() ) + elapsed = d_data->clock->elapsed(); + + return elapsed; + } +#endif + + if ( !d_data->time.isNull() ) + elapsed = d_data->time.elapsed(); + + return elapsed; +} + +/*! + \return Accuracy of the system clock in milliseconds. +*/ +double QwtSystemClock::precision() +{ + static double prec = 0.0; + if ( prec <= 0.0 ) + { +#if defined(QWT_HIGH_RESOLUTION_CLOCK) + prec = QwtHighResolutionClock::precision(); +#endif + if ( prec <= 0.0 ) + prec = 1.0; // QTime offers 1 ms + } + + return prec; +} diff --git a/src/libpcp_qwt/src/qwt_system_clock.h b/src/libpcp_qwt/src/qwt_system_clock.h new file mode 100644 index 0000000..a9da150 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_system_clock.h @@ -0,0 +1,49 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_SYSTEM_CLOCK_H +#define QWT_SYSTEM_CLOCK_H + +#include "qwt_global.h" + +/*! + \brief QwtSystemClock provides high resolution clock time functions. + + Sometimes the resolution offered by QTime ( millisecond ) is not accurate + enough for implementing time measurements ( f.e. sampling ). + QwtSystemClock offers a subset of the QTime functionality using higher + resolution timers ( if possible ). + + Precision and time intervals are multiples of milliseconds (ms). + + \note The implementation uses high-resolution performance counter on Windows, + mach_absolute_time() on the Mac or POSIX timers on other systems. + If none is available it falls back on QTimer. +*/ + +class QWT_EXPORT QwtSystemClock +{ +public: + QwtSystemClock(); + virtual ~QwtSystemClock(); + + bool isNull() const; + + void start(); + double restart(); + double elapsed() const; + + static double precision(); + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_text.cpp b/src/libpcp_qwt/src/qwt_text.cpp new file mode 100644 index 0000000..3b0ded5 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_text.cpp @@ -0,0 +1,643 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2003 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_text.h" +#include "qwt_painter.h" +#include "qwt_text_engine.h" +#include <qmap.h> +#include <qfont.h> +#include <qcolor.h> +#include <qpen.h> +#include <qbrush.h> +#include <qpainter.h> +#include <qapplication.h> +#include <qdesktopwidget.h> +#include <qmath.h> + +class QwtTextEngineDict +{ +public: + static QwtTextEngineDict &dict(); + + void setTextEngine( QwtText::TextFormat, QwtTextEngine * ); + + const QwtTextEngine *textEngine( QwtText::TextFormat ) const; + const QwtTextEngine *textEngine( const QString &, + QwtText::TextFormat ) const; + +private: + QwtTextEngineDict(); + ~QwtTextEngineDict(); + + typedef QMap<int, QwtTextEngine *> EngineMap; + + inline const QwtTextEngine *engine( EngineMap::const_iterator &it ) const + { + return it.value(); + } + + EngineMap d_map; +}; + +QwtTextEngineDict &QwtTextEngineDict::dict() +{ + static QwtTextEngineDict engineDict; + return engineDict; +} + +QwtTextEngineDict::QwtTextEngineDict() +{ + d_map.insert( QwtText::PlainText, new QwtPlainTextEngine() ); +#ifndef QT_NO_RICHTEXT + d_map.insert( QwtText::RichText, new QwtRichTextEngine() ); +#endif +} + +QwtTextEngineDict::~QwtTextEngineDict() +{ + for ( EngineMap::const_iterator it = d_map.begin(); + it != d_map.end(); ++it ) + { + const QwtTextEngine *textEngine = engine( it ); + delete textEngine; + } +} + +const QwtTextEngine *QwtTextEngineDict::textEngine( const QString& text, + QwtText::TextFormat format ) const +{ + if ( format == QwtText::AutoText ) + { + for ( EngineMap::const_iterator it = d_map.begin(); + it != d_map.end(); ++it ) + { + if ( it.key() != QwtText::PlainText ) + { + const QwtTextEngine *e = engine( it ); + if ( e && e->mightRender( text ) ) + return e; + } + } + } + + EngineMap::const_iterator it = d_map.find( format ); + if ( it != d_map.end() ) + { + const QwtTextEngine *e = engine( it ); + if ( e ) + return e; + } + + it = d_map.find( QwtText::PlainText ); + return engine( it ); +} + +void QwtTextEngineDict::setTextEngine( QwtText::TextFormat format, + QwtTextEngine *engine ) +{ + if ( format == QwtText::AutoText ) + return; + + if ( format == QwtText::PlainText && engine == NULL ) + return; + + EngineMap::const_iterator it = d_map.find( format ); + if ( it != d_map.end() ) + { + const QwtTextEngine *e = this->engine( it ); + if ( e ) + delete e; + + d_map.remove( format ); + } + + if ( engine != NULL ) + d_map.insert( format, engine ); +} + +const QwtTextEngine *QwtTextEngineDict::textEngine( + QwtText::TextFormat format ) const +{ + const QwtTextEngine *e = NULL; + + EngineMap::const_iterator it = d_map.find( format ); + if ( it != d_map.end() ) + e = engine( it ); + + return e; +} + +class QwtText::PrivateData +{ +public: + PrivateData(): + renderFlags( Qt::AlignCenter ), + backgroundPen( Qt::NoPen ), + backgroundBrush( Qt::NoBrush ), + paintAttributes( 0 ), + layoutAttributes( 0 ), + textEngine( NULL ) + { + } + + int renderFlags; + QString text; + QFont font; + QColor color; + QPen backgroundPen; + QBrush backgroundBrush; + + QwtText::PaintAttributes paintAttributes; + QwtText::LayoutAttributes layoutAttributes; + + const QwtTextEngine *textEngine; +}; + +class QwtText::LayoutCache +{ +public: + void invalidate() + { + textSize = QSizeF(); + } + + QFont font; + QSizeF textSize; +}; + +/*! + Constructor + + \param text Text content + \param textFormat Text format +*/ +QwtText::QwtText( const QString &text, QwtText::TextFormat textFormat ) +{ + d_data = new PrivateData; + d_data->text = text; + d_data->textEngine = textEngine( text, textFormat ); + + d_layoutCache = new LayoutCache; +} + +//! Copy constructor +QwtText::QwtText( const QwtText &other ) +{ + d_data = new PrivateData; + *d_data = *other.d_data; + + d_layoutCache = new LayoutCache; + *d_layoutCache = *other.d_layoutCache; +} + +//! Destructor +QwtText::~QwtText() +{ + delete d_data; + delete d_layoutCache; +} + +//! Assignment operator +QwtText &QwtText::operator=( const QwtText & other ) +{ + *d_data = *other.d_data; + *d_layoutCache = *other.d_layoutCache; + return *this; +} + +//! Relational operator +bool QwtText::operator==( const QwtText &other ) const +{ + return d_data->renderFlags == other.d_data->renderFlags && + d_data->text == other.d_data->text && + d_data->font == other.d_data->font && + d_data->color == other.d_data->color && + d_data->backgroundPen == other.d_data->backgroundPen && + d_data->backgroundBrush == other.d_data->backgroundBrush && + d_data->paintAttributes == other.d_data->paintAttributes && + d_data->textEngine == other.d_data->textEngine; +} + +//! Relational operator +bool QwtText::operator!=( const QwtText &other ) const // invalidate +{ + return !( other == *this ); +} + +/*! + Assign a new text content + + \param text Text content + \param textFormat Text format + + \sa text() +*/ +void QwtText::setText( const QString &text, + QwtText::TextFormat textFormat ) +{ + d_data->text = text; + d_data->textEngine = textEngine( text, textFormat ); + d_layoutCache->invalidate(); +} + +/*! + Return the text. + \sa setText() +*/ +QString QwtText::text() const +{ + return d_data->text; +} + +/*! + \brief Change the render flags + + The default setting is Qt::AlignCenter + + \param renderFlags Bitwise OR of the flags used like in QPainter::drawText + + \sa renderFlags(), QwtTextEngine::draw() + \note Some renderFlags might have no effect, depending on the text format. +*/ +void QwtText::setRenderFlags( int renderFlags ) +{ + if ( renderFlags != d_data->renderFlags ) + { + d_data->renderFlags = renderFlags; + d_layoutCache->invalidate(); + } +} + +/*! + \return Render flags + \sa setRenderFlags() +*/ +int QwtText::renderFlags() const +{ + return d_data->renderFlags; +} + +/*! + Set the font. + + \param font Font + \note Setting the font might have no effect, when + the text contains control sequences for setting fonts. +*/ +void QwtText::setFont( const QFont &font ) +{ + d_data->font = font; + setPaintAttribute( PaintUsingTextFont ); +} + +//! Return the font. +QFont QwtText::font() const +{ + return d_data->font; +} + +/*! + Return the font of the text, if it has one. + Otherwise return defaultFont. + + \param defaultFont Default font + \sa setFont(), font(), PaintAttributes +*/ +QFont QwtText::usedFont( const QFont &defaultFont ) const +{ + if ( d_data->paintAttributes & PaintUsingTextFont ) + return d_data->font; + + return defaultFont; +} + +/*! + Set the pen color used for painting the text. + + \param color Color + \note Setting the color might have no effect, when + the text contains control sequences for setting colors. +*/ +void QwtText::setColor( const QColor &color ) +{ + d_data->color = color; + setPaintAttribute( PaintUsingTextColor ); +} + +//! Return the pen color, used for painting the text +QColor QwtText::color() const +{ + return d_data->color; +} + +/*! + Return the color of the text, if it has one. + Otherwise return defaultColor. + + \param defaultColor Default color + \sa setColor(), color(), PaintAttributes +*/ +QColor QwtText::usedColor( const QColor &defaultColor ) const +{ + if ( d_data->paintAttributes & PaintUsingTextColor ) + return d_data->color; + + return defaultColor; +} + +/*! + Set the background pen + + \param pen Background pen + \sa backgroundPen(), setBackgroundBrush() +*/ +void QwtText::setBackgroundPen( const QPen &pen ) +{ + d_data->backgroundPen = pen; + setPaintAttribute( PaintBackground ); +} + +/*! + \return Background pen + \sa setBackgroundPen(), backgroundBrush() +*/ +QPen QwtText::backgroundPen() const +{ + return d_data->backgroundPen; +} + +/*! + Set the background brush + + \param brush Background brush + \sa backgroundBrush(), setBackgroundPen() +*/ +void QwtText::setBackgroundBrush( const QBrush &brush ) +{ + d_data->backgroundBrush = brush; + setPaintAttribute( PaintBackground ); +} + +/*! + \return Background brush + \sa setBackgroundBrush(), backgroundPen() +*/ +QBrush QwtText::backgroundBrush() const +{ + return d_data->backgroundBrush; +} + +/*! + Change a paint attribute + + \param attribute Paint attribute + \param on On/Off + + \note Used by setFont(), setColor(), + setBackgroundPen() and setBackgroundBrush() + \sa testPaintAttribute() +*/ +void QwtText::setPaintAttribute( PaintAttribute attribute, bool on ) +{ + if ( on ) + d_data->paintAttributes |= attribute; + else + d_data->paintAttributes &= ~attribute; +} + +/*! + Test a paint attribute + + \param attribute Paint attribute + \return true, if attribute is enabled + + \sa setPaintAttribute() +*/ +bool QwtText::testPaintAttribute( PaintAttribute attribute ) const +{ + return d_data->paintAttributes & attribute; +} + +/*! + Change a layout attribute + + \param attribute Layout attribute + \param on On/Off + \sa testLayoutAttribute() +*/ +void QwtText::setLayoutAttribute( LayoutAttribute attribute, bool on ) +{ + if ( on ) + d_data->layoutAttributes |= attribute; + else + d_data->layoutAttributes &= ~attribute; +} + +/*! + Test a layout attribute + + \param attribute Layout attribute + \return true, if attribute is enabled + + \sa setLayoutAttribute() +*/ +bool QwtText::testLayoutAttribute( LayoutAttribute attribute ) const +{ + return d_data->layoutAttributes | attribute; +} + +/*! + Find the height for a given width + + \param defaultFont Font, used for the calculation if the text has no font + \param width Width + + \return Calculated height +*/ +double QwtText::heightForWidth( double width, const QFont &defaultFont ) const +{ + // We want to calculate in screen metrics. So + // we need a font that uses screen metrics + + const QFont font( usedFont( defaultFont ), QApplication::desktop() ); + + double h = 0; + + if ( d_data->layoutAttributes & MinimumLayout ) + { + double left, right, top, bottom; + d_data->textEngine->textMargins( font, d_data->text, + left, right, top, bottom ); + + h = d_data->textEngine->heightForWidth( + font, d_data->renderFlags, d_data->text, + width + left + right ); + + h -= top + bottom; + } + else + { + h = d_data->textEngine->heightForWidth( + font, d_data->renderFlags, d_data->text, width ); + } + + return h; +} + +/*! + Find the height for a given width + + \param defaultFont Font, used for the calculation if the text has no font + + \return Calculated height +*/ + +/*! + Returns the size, that is needed to render text + + \param defaultFont Font of the text + \return Caluclated size +*/ +QSizeF QwtText::textSize( const QFont &defaultFont ) const +{ + // We want to calculate in screen metrics. So + // we need a font that uses screen metrics + + const QFont font( usedFont( defaultFont ), QApplication::desktop() ); + + if ( !d_layoutCache->textSize.isValid() + || d_layoutCache->font != font ) + { + d_layoutCache->textSize = d_data->textEngine->textSize( + font, d_data->renderFlags, d_data->text ); + d_layoutCache->font = font; + } + + QSizeF sz = d_layoutCache->textSize; + + if ( d_data->layoutAttributes & MinimumLayout ) + { + double left, right, top, bottom; + d_data->textEngine->textMargins( font, d_data->text, + left, right, top, bottom ); + sz -= QSizeF( left + right, top + bottom ); + } + + return sz; +} + +/*! + Draw a text into a rectangle + + \param painter Painter + \param rect Rectangle +*/ +void QwtText::draw( QPainter *painter, const QRectF &rect ) const +{ + if ( d_data->paintAttributes & PaintBackground ) + { + if ( d_data->backgroundPen != Qt::NoPen || + d_data->backgroundBrush != Qt::NoBrush ) + { + painter->save(); + painter->setPen( d_data->backgroundPen ); + painter->setBrush( d_data->backgroundBrush ); + QwtPainter::drawRect( painter, rect ); + painter->restore(); + } + } + + painter->save(); + + if ( d_data->paintAttributes & PaintUsingTextFont ) + { + painter->setFont( d_data->font ); + } + + if ( d_data->paintAttributes & PaintUsingTextColor ) + { + if ( d_data->color.isValid() ) + painter->setPen( d_data->color ); + } + + QRectF expandedRect = rect; + if ( d_data->layoutAttributes & MinimumLayout ) + { + // We want to calculate in screen metrics. So + // we need a font that uses screen metrics + + const QFont font( painter->font(), QApplication::desktop() ); + + double left, right, top, bottom; + d_data->textEngine->textMargins( + font, d_data->text, left, right, top, bottom ); + + expandedRect.setTop( rect.top() - top ); + expandedRect.setBottom( rect.bottom() + bottom ); + expandedRect.setLeft( rect.left() - left ); + expandedRect.setRight( rect.right() + right ); + } + + d_data->textEngine->draw( painter, expandedRect, + d_data->renderFlags, d_data->text ); + + painter->restore(); +} + +/*! + Find the text engine for a text format + + In case of QwtText::AutoText the first text engine + (beside QwtPlainTextEngine) is returned, where QwtTextEngine::mightRender + returns true. If there is none QwtPlainTextEngine is returnd. + + If no text engine is registered for the format QwtPlainTextEngine + is returnd. + + \param text Text, needed in case of AutoText + \param format Text format +*/ +const QwtTextEngine *QwtText::textEngine( const QString &text, + QwtText::TextFormat format ) +{ + return QwtTextEngineDict::dict().textEngine( text, format ); +} + +/*! + Assign/Replace a text engine for a text format + + With setTextEngine it is possible to extend Qwt with + other types of text formats. + + For QwtText::PlainText it is not allowed to assign a engine == NULL. + + \param format Text format + \param engine Text engine + + \sa QwtMathMLTextEngine + \warning Using QwtText::AutoText does nothing. +*/ +void QwtText::setTextEngine( QwtText::TextFormat format, + QwtTextEngine *engine ) +{ + QwtTextEngineDict::dict().setTextEngine( format, engine ); +} + +/*! + \brief Find the text engine for a text format + + textEngine can be used to find out if a text format is supported. + + \param format Text format + \return The text engine, or NULL if no engine is available. +*/ +const QwtTextEngine *QwtText::textEngine( QwtText::TextFormat format ) +{ + return QwtTextEngineDict::dict().textEngine( format ); +} diff --git a/src/libpcp_qwt/src/qwt_text.h b/src/libpcp_qwt/src/qwt_text.h new file mode 100644 index 0000000..3c6a438 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_text.h @@ -0,0 +1,216 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2003 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 + *****************************************************************************/ + +#ifndef QWT_TEXT_H +#define QWT_TEXT_H + +#include "qwt_global.h" +#include <qstring.h> +#include <qsize.h> +#include <qfont.h> + +class QColor; +class QPen; +class QBrush; +class QRectF; +class QPainter; +class QwtTextEngine; + +/*! + \brief A class representing a text + + A QwtText is a text including a set of attributes how to render it. + + - Format\n + A text might include control sequences (f.e tags) describing + how to render it. Each format (f.e MathML, TeX, Qt Rich Text) + has its own set of control sequences, that can be handles by + a QwtTextEngine for this format. + - Background\n + A text might have a background, defined by a QPen and QBrush + to improve its visibility. + - Font\n + A text might have an individual font. + - Color\n + A text might have an individual color. + - Render Flags\n + Flags from Qt::AlignmentFlag and Qt::TextFlag used like in + QPainter::drawText. + + \sa QwtTextEngine, QwtTextLabel +*/ + +class QWT_EXPORT QwtText +{ +public: + + /*! + \brief Text format + + The text format defines the QwtTextEngine, that is used to render + the text. + + \sa QwtTextEngine, setTextEngine() + */ + + enum TextFormat + { + /*! + The text format is determined using QwtTextEngine::mightRender for + all available text engines in increasing order > PlainText. + If none of the text engines can render the text is rendered + like QwtText::PlainText. + */ + AutoText = 0, + + //! Draw the text as it is, using a QwtPlainTextEngine. + PlainText, + + //! Use the Scribe framework (Qt Rich Text) to render the text. + RichText, + + /*! + Use a MathML (http://en.wikipedia.org/wiki/MathML) render engine + to display the text. The Qwt MathML extension offers such an engine + based on the MathML renderer of the Qt solutions package. + To enable MathML support the following code needs to be added to the + application: +\verbatim QwtText::setTextEngine(QwtText::MathMLText, new QwtMathMLTextEngine()); \endverbatim + */ + MathMLText, + + /*! + Use a TeX (http://en.wikipedia.org/wiki/TeX) render engine + to display the text ( not implemented yet ). + */ + TeXText, + + /*! + The number of text formats can be extended using setTextEngine. + Formats >= QwtText::OtherFormat are not used by Qwt. + */ + OtherFormat = 100 + }; + + /*! + \brief Paint Attributes + + Font and color and background are optional attributes of a QwtText. + The paint attributes hold the information, if they are set. + */ + enum PaintAttribute + { + //! The text has an individual font. + PaintUsingTextFont = 0x01, + + //! The text has an individual color. + PaintUsingTextColor = 0x02, + + //! The text has an individual background. + PaintBackground = 0x04 + }; + + //! Paint attributes + typedef QFlags<PaintAttribute> PaintAttributes; + + /*! + \brief Layout Attributes + The layout attributes affects some aspects of the layout of the text. + */ + enum LayoutAttribute + { + /*! + Layout the text without its margins. This mode is useful if a + text needs to be aligned accurately, like the tick labels of a scale. + If QwtTextEngine::textMargins is not implemented for the format + of the text, MinimumLayout has no effect. + */ + MinimumLayout = 0x01 + }; + + //! Layout attributes + typedef QFlags<LayoutAttribute> LayoutAttributes; + + QwtText( const QString & = QString::null, + TextFormat textFormat = AutoText ); + QwtText( const QwtText & ); + ~QwtText(); + + QwtText &operator=( const QwtText & ); + + bool operator==( const QwtText & ) const; + bool operator!=( const QwtText & ) const; + + void setText( const QString &, + QwtText::TextFormat textFormat = AutoText ); + QString text() const; + + bool isNull() const; + bool isEmpty() const; + + void setFont( const QFont & ); + QFont font() const; + + QFont usedFont( const QFont & ) const; + + void setRenderFlags( int flags ); + int renderFlags() const; + + void setColor( const QColor & ); + QColor color() const; + + QColor usedColor( const QColor & ) const; + + void setBackgroundPen( const QPen & ); + QPen backgroundPen() const; + + void setBackgroundBrush( const QBrush & ); + QBrush backgroundBrush() const; + + void setPaintAttribute( PaintAttribute, bool on = true ); + bool testPaintAttribute( PaintAttribute ) const; + + void setLayoutAttribute( LayoutAttribute, bool on = true ); + bool testLayoutAttribute( LayoutAttribute ) const; + + double heightForWidth( double width, const QFont & = QFont() ) const; + QSizeF textSize( const QFont & = QFont() ) const; + + void draw( QPainter *painter, const QRectF &rect ) const; + + static const QwtTextEngine *textEngine( + const QString &text, QwtText::TextFormat = AutoText ); + + static const QwtTextEngine *textEngine( QwtText::TextFormat ); + static void setTextEngine( QwtText::TextFormat, QwtTextEngine * ); + +private: + class PrivateData; + PrivateData *d_data; + + class LayoutCache; + LayoutCache *d_layoutCache; +}; + +//! \return text().isNull() +inline bool QwtText::isNull() const +{ + return text().isNull(); +} + +//! \return text().isEmpty() +inline bool QwtText::isEmpty() const +{ + return text().isEmpty(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtText::PaintAttributes ) +Q_DECLARE_OPERATORS_FOR_FLAGS( QwtText::LayoutAttributes ) + +#endif diff --git a/src/libpcp_qwt/src/qwt_text_engine.cpp b/src/libpcp_qwt/src/qwt_text_engine.cpp new file mode 100644 index 0000000..ec74512 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_text_engine.cpp @@ -0,0 +1,344 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2003 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_text_engine.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include <qpainter.h> +#include <qpixmap.h> +#include <qimage.h> +#include <qmap.h> +#include <qwidget.h> +#include <qtextobject.h> +#include <qtextdocument.h> +#include <qabstracttextdocumentlayout.h> + +static QString taggedRichText( const QString &text, int flags ) +{ + QString richText = text; + + // By default QSimpleRichText is Qt::AlignLeft + if ( flags & Qt::AlignJustify ) + { + richText.prepend( QString::fromLatin1( "<div align=\"justify\">" ) ); + richText.append( QString::fromLatin1( "</div>" ) ); + } + else if ( flags & Qt::AlignRight ) + { + richText.prepend( QString::fromLatin1( "<div align=\"right\">" ) ); + richText.append( QString::fromLatin1( "</div>" ) ); + } + else if ( flags & Qt::AlignHCenter ) + { + richText.prepend( QString::fromLatin1( "<div align=\"center\">" ) ); + richText.append( QString::fromLatin1( "</div>" ) ); + } + + return richText; +} + +class QwtRichTextDocument: public QTextDocument +{ +public: + QwtRichTextDocument( const QString &text, int flags, const QFont &font ) + { + setUndoRedoEnabled( false ); + setDefaultFont( font ); + setHtml( text ); + + // make sure we have a document layout + ( void )documentLayout(); + + QTextOption option = defaultTextOption(); + if ( flags & Qt::TextWordWrap ) + option.setWrapMode( QTextOption::WordWrap ); + else + option.setWrapMode( QTextOption::NoWrap ); + + option.setAlignment( ( Qt::Alignment ) flags ); + setDefaultTextOption( option ); + + QTextFrame *root = rootFrame(); + QTextFrameFormat fm = root->frameFormat(); + fm.setBorder( 0 ); + fm.setMargin( 0 ); + fm.setPadding( 0 ); + fm.setBottomMargin( 0 ); + fm.setLeftMargin( 0 ); + root->setFrameFormat( fm ); + + adjustSize(); + } +}; + +class QwtPlainTextEngine::PrivateData +{ +public: + int effectiveAscent( const QFont &font ) const + { + const QString fontKey = font.key(); + + QMap<QString, int>::const_iterator it = + d_ascentCache.find( fontKey ); + if ( it == d_ascentCache.end() ) + { + int ascent = findAscent( font ); + it = d_ascentCache.insert( fontKey, ascent ); + } + + return ( *it ); + } + +private: + int findAscent( const QFont &font ) const + { + static const QString dummy( "E" ); + static const QColor white( Qt::white ); + + const QFontMetrics fm( font ); + QPixmap pm( fm.width( dummy ), fm.height() ); + pm.fill( white ); + + QPainter p( &pm ); + p.setFont( font ); + p.drawText( 0, 0, pm.width(), pm.height(), 0, dummy ); + p.end(); + + const QImage img = pm.toImage(); + + int row = 0; + for ( row = 0; row < img.height(); row++ ) + { + const QRgb *line = ( const QRgb * )img.scanLine( row ); + + const int w = pm.width(); + for ( int col = 0; col < w; col++ ) + { + if ( line[col] != white.rgb() ) + return fm.ascent() - row + 1; + } + } + + return fm.ascent(); + } + + mutable QMap<QString, int> d_ascentCache; +}; + +//! Constructor +QwtTextEngine::QwtTextEngine() +{ +} + +//! Destructor +QwtTextEngine::~QwtTextEngine() +{ +} + +//! Constructor +QwtPlainTextEngine::QwtPlainTextEngine() +{ + d_data = new PrivateData; +} + +//! Destructor +QwtPlainTextEngine::~QwtPlainTextEngine() +{ + delete d_data; +} + +/*! + Find the height for a given width + + \param font Font of the text + \param flags Bitwise OR of the flags used like in QPainter::drawText + \param text Text to be rendered + \param width Width + + \return Calculated height +*/ +double QwtPlainTextEngine::heightForWidth( const QFont& font, int flags, + const QString& text, double width ) const +{ + const QFontMetricsF fm( font ); + const QRectF rect = fm.boundingRect( + QRectF( 0, 0, width, QWIDGETSIZE_MAX ), flags, text ); + + return rect.height(); +} + +/*! + Returns the size, that is needed to render text + + \param font Font of the text + \param flags Bitwise OR of the flags used like in QPainter::drawText + \param text Text to be rendered + + \return Caluclated size +*/ +QSizeF QwtPlainTextEngine::textSize( const QFont &font, + int flags, const QString& text ) const +{ + const QFontMetricsF fm( font ); + const QRectF rect = fm.boundingRect( + QRectF( 0, 0, QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ), flags, text ); + + return rect.size(); +} + +/*! + Return margins around the texts + + \param font Font of the text + \param left Return 0 + \param right Return 0 + \param top Return value for the top margin + \param bottom Return value for the bottom margin +*/ +void QwtPlainTextEngine::textMargins( const QFont &font, const QString &, + double &left, double &right, double &top, double &bottom ) const +{ + left = right = top = 0; + + const QFontMetricsF fm( font ); + top = fm.ascent() - d_data->effectiveAscent( font ); + bottom = fm.descent(); +} + +/*! + \brief Draw the text in a clipping rectangle + + A wrapper for QPainter::drawText. + + \param painter Painter + \param rect Clipping rectangle + \param flags Bitwise OR of the flags used like in QPainter::drawText + \param text Text to be rendered +*/ +void QwtPlainTextEngine::draw( QPainter *painter, const QRectF &rect, + int flags, const QString& text ) const +{ + QwtPainter::drawText( painter, rect, flags, text ); +} + +/*! + Test if a string can be rendered by this text engine. + \return Always true. All texts can be rendered by QwtPlainTextEngine +*/ +bool QwtPlainTextEngine::mightRender( const QString & ) const +{ + return true; +} + +#ifndef QT_NO_RICHTEXT + +//! Constructor +QwtRichTextEngine::QwtRichTextEngine() +{ +} + +/*! + Find the height for a given width + + \param font Font of the text + \param flags Bitwise OR of the flags used like in QPainter::drawText + \param text Text to be rendered + \param width Width + + \return Calculated height +*/ +double QwtRichTextEngine::heightForWidth( const QFont& font, int flags, + const QString& text, double width ) const +{ + QwtRichTextDocument doc( text, flags, font ); + + doc.setPageSize( QSizeF( width, QWIDGETSIZE_MAX ) ); + return doc.documentLayout()->documentSize().height(); +} + +/*! + Returns the size, that is needed to render text + + \param font Font of the text + \param flags Bitwise OR of the flags used like in QPainter::drawText + \param text Text to be rendered + + \return Caluclated size +*/ + +QSizeF QwtRichTextEngine::textSize( const QFont &font, + int flags, const QString& text ) const +{ + QwtRichTextDocument doc( text, flags, font ); + + QTextOption option = doc.defaultTextOption(); + if ( option.wrapMode() != QTextOption::NoWrap ) + { + option.setWrapMode( QTextOption::NoWrap ); + doc.setDefaultTextOption( option ); + doc.adjustSize(); + } + + return doc.size(); +} + +/*! + Draw the text in a clipping rectangle + + \param painter Painter + \param rect Clipping rectangle + \param flags Bitwise OR of the flags like in for QPainter::drawText + \param text Text to be rendered +*/ +void QwtRichTextEngine::draw( QPainter *painter, const QRectF &rect, + int flags, const QString& text ) const +{ + QwtRichTextDocument doc( text, flags, painter->font() ); + QwtPainter::drawSimpleRichText( painter, rect, flags, doc ); +} + +/*! + Wrap text into <div align=...> </div> tags according flags + + \param text Text + \param flags Bitwise OR of the flags like in for QPainter::drawText + + \return Tagged text +*/ +QString QwtRichTextEngine::taggedText( const QString &text, int flags ) const +{ + return taggedRichText( text, flags ); +} + +/*! + Test if a string can be rendered by this text engine + + \param text Text to be tested + \return QStyleSheet::mightBeRichText(text); +*/ +bool QwtRichTextEngine::mightRender( const QString &text ) const +{ + return Qt::mightBeRichText( text ); +} + +/*! + Return margins around the texts + + \param left Return 0 + \param right Return 0 + \param top Return 0 + \param bottom Return 0 +*/ +void QwtRichTextEngine::textMargins( const QFont &, const QString &, + double &left, double &right, double &top, double &bottom ) const +{ + left = right = top = bottom = 0; +} + +#endif // !QT_NO_RICHTEXT diff --git a/src/libpcp_qwt/src/qwt_text_engine.h b/src/libpcp_qwt/src/qwt_text_engine.h new file mode 100644 index 0000000..e378acf --- /dev/null +++ b/src/libpcp_qwt/src/qwt_text_engine.h @@ -0,0 +1,172 @@ +/* -*- mode: C++ ; c-file-style: "stroustrup" -*- ***************************** + * Qwt Widget Library + * Copyright (C) 1997 Josef Wilgen + * Copyright (C) 2003 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 + *****************************************************************************/ + +#ifndef QWT_TEXT_ENGINE_H +#define QWT_TEXT_ENGINE_H 1 + +#include "qwt_global.h" +#include <qsize.h> + +class QFont; +class QRectF; +class QString; +class QPainter; + +/*! + \brief Abstract base class for rendering text strings + + A text engine is responsible for rendering texts for a + specific text format. They are used by QwtText to render a text. + + QwtPlainTextEngine and QwtRichTextEngine are part of the Qwt library. + The implementation of QwtMathMLTextEngine uses code from the + Qt solution package. Because of license implications it is built into + a separate library. + + \sa QwtText::setTextEngine() +*/ + +class QWT_EXPORT QwtTextEngine +{ +public: + virtual ~QwtTextEngine(); + + /*! + Find the height for a given width + + \param font Font of the text + \param flags Bitwise OR of the flags used like in QPainter::drawText + \param text Text to be rendered + \param width Width + + \return Calculated height + */ + virtual double heightForWidth( const QFont &font, int flags, + const QString &text, double width ) const = 0; + + /*! + Returns the size, that is needed to render text + + \param font Font of the text + \param flags Bitwise OR of the flags like in for QPainter::drawText + \param text Text to be rendered + + \return Caluclated size + */ + virtual QSizeF textSize( const QFont &font, int flags, + const QString &text ) const = 0; + + /*! + Test if a string can be rendered by this text engine + + \param text Text to be tested + \return true, if it can be rendered + */ + virtual bool mightRender( const QString &text ) const = 0; + + /*! + Return margins around the texts + + The textSize might include margins around the + text, like QFontMetrics::descent. In situations + where texts need to be aligend in detail, knowing + these margins might improve the layout calculations. + + \param font Font of the text + \param text Text to be rendered + \param left Return value for the left margin + \param right Return value for the right margin + \param top Return value for the top margin + \param bottom Return value for the bottom margin + */ + virtual void textMargins( const QFont &font, const QString &text, + double &left, double &right, double &top, double &bottom ) const = 0; + + /*! + Draw the text in a clipping rectangle + + \param painter Painter + \param rect Clipping rectangle + \param flags Bitwise OR of the flags like in for QPainter::drawText + \param text Text to be rendered + */ + virtual void draw( QPainter *painter, const QRectF &rect, + int flags, const QString &text ) const = 0; + +protected: + QwtTextEngine(); +}; + + +/*! + \brief A text engine for plain texts + + QwtPlainTextEngine renders texts using the basic Qt classes + QPainter and QFontMetrics. +*/ +class QWT_EXPORT QwtPlainTextEngine: public QwtTextEngine +{ +public: + QwtPlainTextEngine(); + virtual ~QwtPlainTextEngine(); + + virtual double heightForWidth( const QFont &font, int flags, + const QString &text, double width ) const; + + virtual QSizeF textSize( const QFont &font, int flags, + const QString &text ) const; + + virtual void draw( QPainter *painter, const QRectF &rect, + int flags, const QString &text ) const; + + virtual bool mightRender( const QString & ) const; + + virtual void textMargins( const QFont &, const QString &, + double &left, double &right, double &top, double &bottom ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + + +#ifndef QT_NO_RICHTEXT + +/*! + \brief A text engine for Qt rich texts + + QwtRichTextEngine renders Qt rich texts using the classes + of the Scribe framework of Qt. +*/ +class QWT_EXPORT QwtRichTextEngine: public QwtTextEngine +{ +public: + QwtRichTextEngine(); + + virtual double heightForWidth( const QFont &font, int flags, + const QString &text, double width ) const; + + virtual QSizeF textSize( const QFont &font, int flags, + const QString &text ) const; + + virtual void draw( QPainter *painter, const QRectF &rect, + int flags, const QString &text ) const; + + virtual bool mightRender( const QString & ) const; + + virtual void textMargins( const QFont &, const QString &, + double &left, double &right, double &top, double &bottom ) const; + +private: + QString taggedText( const QString &, int flags ) const; +}; + +#endif // !QT_NO_RICHTEXT + +#endif diff --git a/src/libpcp_qwt/src/qwt_text_label.cpp b/src/libpcp_qwt/src/qwt_text_label.cpp new file mode 100644 index 0000000..e895928 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_text_label.cpp @@ -0,0 +1,306 @@ +/* -*- 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_text_label.h" +#include "qwt_text.h" +#include "qwt_painter.h" +#include <qpainter.h> +#include <qevent.h> +#include <qmath.h> + +class QwtTextLabel::PrivateData +{ +public: + PrivateData(): + indent( 4 ), + margin( 0 ) + { + } + + int indent; + int margin; + QwtText text; +}; + +/*! + Constructs an empty label. + \param parent Parent widget +*/ +QwtTextLabel::QwtTextLabel( QWidget *parent ): + QFrame( parent ) +{ + init(); +} + +/*! + Constructs a label that displays the text, text + \param parent Parent widget + \param text Text +*/ +QwtTextLabel::QwtTextLabel( const QwtText &text, QWidget *parent ): + QFrame( parent ) +{ + init(); + d_data->text = text; +} + +//! Destructor +QwtTextLabel::~QwtTextLabel() +{ + delete d_data; +} + +void QwtTextLabel::init() +{ + d_data = new PrivateData(); + setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); +} + +/*! + Change the label's text, keeping all other QwtText attributes + \param text New text + \param textFormat Format of text + + \sa QwtText +*/ +void QwtTextLabel::setText( const QString &text, QwtText::TextFormat textFormat ) +{ + d_data->text.setText( text, textFormat ); + + update(); + updateGeometry(); +} + +/*! + Change the label's text + \param text New text +*/ +void QwtTextLabel::setText( const QwtText &text ) +{ + d_data->text = text; + + update(); + updateGeometry(); +} + +//! Return the text +const QwtText &QwtTextLabel::text() const +{ + return d_data->text; +} + +//! Clear the text and all QwtText attributes +void QwtTextLabel::clear() +{ + d_data->text = QwtText(); + + update(); + updateGeometry(); +} + +//! Return label's text indent in pixels +int QwtTextLabel::indent() const +{ + return d_data->indent; +} + +/*! + Set label's text indent in pixels + \param indent Indentation in pixels +*/ +void QwtTextLabel::setIndent( int indent ) +{ + if ( indent < 0 ) + indent = 0; + + d_data->indent = indent; + + update(); + updateGeometry(); +} + +//! Return label's text indent in pixels +int QwtTextLabel::margin() const +{ + return d_data->margin; +} + +/*! + Set label's margin in pixels + \param margin Margin in pixels +*/ +void QwtTextLabel::setMargin( int margin ) +{ + d_data->margin = margin; + + update(); + updateGeometry(); +} + +//! Return label's margin in pixels +QSize QwtTextLabel::sizeHint() const +{ + return minimumSizeHint(); +} + +//! Return a minimum size hint +QSize QwtTextLabel::minimumSizeHint() const +{ + QSizeF sz = d_data->text.textSize( font() ); + + int mw = 2 * ( frameWidth() + d_data->margin ); + int mh = mw; + + int indent = d_data->indent; + if ( indent <= 0 ) + indent = defaultIndent(); + + if ( indent > 0 ) + { + const int align = d_data->text.renderFlags(); + if ( align & Qt::AlignLeft || align & Qt::AlignRight ) + mw += d_data->indent; + else if ( align & Qt::AlignTop || align & Qt::AlignBottom ) + mh += d_data->indent; + } + + sz += QSizeF( mw, mh ); + + return QSize( qCeil( sz.width() ), qCeil( sz.height() ) ); +} + +/*! + \param width Width + \return Preferred height for this widget, given the width. +*/ +int QwtTextLabel::heightForWidth( int width ) const +{ + const int renderFlags = d_data->text.renderFlags(); + + int indent = d_data->indent; + if ( indent <= 0 ) + indent = defaultIndent(); + + width -= 2 * frameWidth(); + if ( renderFlags & Qt::AlignLeft || renderFlags & Qt::AlignRight ) + width -= indent; + + int height = qCeil( d_data->text.heightForWidth( width, font() ) ); + if ( ( renderFlags & Qt::AlignTop ) || ( renderFlags & Qt::AlignBottom ) ) + height += indent; + + height += 2 * frameWidth(); + + return height; +} + +/*! + Qt paint event + \param event Paint event +*/ +void QwtTextLabel::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + + if ( !contentsRect().contains( event->rect() ) ) + { + painter.save(); + painter.setClipRegion( event->region() & frameRect() ); + drawFrame( &painter ); + painter.restore(); + } + + painter.setClipRegion( event->region() & contentsRect() ); + + drawContents( &painter ); +} + +//! Redraw the text and focus indicator +void QwtTextLabel::drawContents( QPainter *painter ) +{ + const QRect r = textRect(); + if ( r.isEmpty() ) + return; + + painter->setFont( font() ); + painter->setPen( palette().color( QPalette::Active, QPalette::Text ) ); + + drawText( painter, r ); + + if ( hasFocus() ) + { + const int margin = 2; + + QRect focusRect = contentsRect(); + focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin, + focusRect.width() - 2 * margin - 2, + focusRect.height() - 2 * margin - 2 ); + + QwtPainter::drawFocusRect( painter, this, focusRect ); + } +} + +//! Redraw the text +void QwtTextLabel::drawText( QPainter *painter, const QRect &textRect ) +{ + d_data->text.draw( painter, textRect ); +} + +/*! + Calculate the rect for the text in widget coordinates + \return Text rect +*/ +QRect QwtTextLabel::textRect() const +{ + QRect r = contentsRect(); + + if ( !r.isEmpty() && d_data->margin > 0 ) + { + r.setRect( r.x() + d_data->margin, r.y() + d_data->margin, + r.width() - 2 * d_data->margin, r.height() - 2 * d_data->margin ); + } + + if ( !r.isEmpty() ) + { + int indent = d_data->indent; + if ( indent <= 0 ) + indent = defaultIndent(); + + if ( indent > 0 ) + { + const int renderFlags = d_data->text.renderFlags(); + + if ( renderFlags & Qt::AlignLeft ) + r.setX( r.x() + indent ); + else if ( renderFlags & Qt::AlignRight ) + r.setWidth( r.width() - indent ); + else if ( renderFlags & Qt::AlignTop ) + r.setY( r.y() + indent ); + else if ( renderFlags & Qt::AlignBottom ) + r.setHeight( r.height() - indent ); + } + } + + return r; +} + +int QwtTextLabel::defaultIndent() const +{ + if ( frameWidth() <= 0 ) + return 0; + + QFont fnt; + if ( d_data->text.testPaintAttribute( QwtText::PaintUsingTextFont ) ) + fnt = d_data->text.font(); + else + fnt = font(); + + return QFontMetrics( fnt ).width( 'x' ) / 2; +} + diff --git a/src/libpcp_qwt/src/qwt_text_label.h b/src/libpcp_qwt/src/qwt_text_label.h new file mode 100644 index 0000000..44d3a56 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_text_label.h @@ -0,0 +1,72 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_TEXT_LABEL_H +#define QWT_TEXT_LABEL_H + +#include "qwt_global.h" +#include "qwt_text.h" +#include <qframe.h> + +class QString; +class QPaintEvent; +class QPainter; + +/*! + \brief A Widget which displays a QwtText +*/ + +class QWT_EXPORT QwtTextLabel : public QFrame +{ + Q_OBJECT + + Q_PROPERTY( int indent READ indent WRITE setIndent ) + Q_PROPERTY( int margin READ margin WRITE setMargin ) + +public: + explicit QwtTextLabel( QWidget *parent = NULL ); + explicit QwtTextLabel( const QwtText &, QWidget *parent = NULL ); + virtual ~QwtTextLabel(); + +public Q_SLOTS: + void setText( const QString &, + QwtText::TextFormat textFormat = QwtText::AutoText ); + virtual void setText( const QwtText & ); + + void clear(); + +public: + const QwtText &text() const; + + int indent() const; + void setIndent( int ); + + int margin() const; + void setMargin( int ); + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + virtual int heightForWidth( int ) const; + + QRect textRect() const; + +protected: + virtual void paintEvent( QPaintEvent *e ); + virtual void drawContents( QPainter * ); + virtual void drawText( QPainter *, const QRect & ); + +private: + void init(); + int defaultIndent() const; + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_thermo.cpp b/src/libpcp_qwt/src/qwt_thermo.cpp new file mode 100644 index 0000000..6bfa8c4 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_thermo.cpp @@ -0,0 +1,1036 @@ +/* -*- 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_thermo.h" +#include "qwt_scale_engine.h" +#include "qwt_scale_draw.h" +#include "qwt_scale_map.h" +#include "qwt_color_map.h" +#include <qpainter.h> +#include <qevent.h> +#include <qdrawutil.h> +#include <qstyle.h> +#include <qstyleoption.h> + +static inline bool qwtIsLogarithmic( const QwtThermo *thermo ) +{ + const QwtScaleTransformation::Type scaleType = + thermo->scaleEngine()->transformation()->type(); + + return ( scaleType == QwtScaleTransformation::Log10 ); +} + +static inline void qwtDrawLine( + QPainter *painter, int pos, + const QColor &color, const QRect pipeRect, + Qt::Orientation orientation ) +{ + painter->setPen( color ); + if ( orientation == Qt::Horizontal ) + painter->drawLine( pos, pipeRect.top(), pos, pipeRect.bottom() ); + else + painter->drawLine( pipeRect.left(), pos, pipeRect.right(), pos ); +} + +QVector<double> qwtTickList( const QwtScaleDiv &scaleDiv, double value ) +{ + QVector<double> values; + + double lowerLimit = scaleDiv.interval().minValue(); + double upperLimit = scaleDiv.interval().maxValue(); + + if ( upperLimit < lowerLimit ) + qSwap( lowerLimit, upperLimit ); + + if ( value < lowerLimit ) + return values; + + if ( value < upperLimit ) + upperLimit = value; + + values += lowerLimit; + + for ( int tickType = QwtScaleDiv::MinorTick; + tickType < QwtScaleDiv::NTickTypes; tickType++ ) + { + const QList<double> ticks = scaleDiv.ticks( tickType ); + + for ( int i = 0; i < ticks.count(); i++ ) + { + const double v = ticks[i]; + if ( v > lowerLimit && v < upperLimit ) + values += v; + } + } + + values += upperLimit; + + return values; +} + +class QwtThermo::PrivateData +{ +public: + PrivateData(): + orientation( Qt::Vertical ), + scalePos( QwtThermo::LeftScale ), + spacing( 3 ), + borderWidth( 2 ), + pipeWidth( 10 ), + minValue( 0.0 ), + maxValue( 0.0 ), + value( 0.0 ), + alarmLevel( 0.0 ), + alarmEnabled( false ), + autoFillPipe( true ), + colorMap( NULL ) + { + rangeFlags = QwtInterval::IncludeBorders; + } + + ~PrivateData() + { + delete colorMap; + } + + QwtScaleMap map; + + Qt::Orientation orientation; + ScalePos scalePos; + int spacing; + int borderWidth; + int pipeWidth; + + double minValue; + double maxValue; + QwtInterval::BorderFlags rangeFlags; + double value; + double alarmLevel; + bool alarmEnabled; + bool autoFillPipe; + + QwtColorMap *colorMap; +}; + +/*! + Constructor + \param parent Parent widget +*/ +QwtThermo::QwtThermo( QWidget *parent ): + QWidget( parent ) +{ + d_data = new PrivateData; + setRange( 0.0, 1.0, false ); + + QSizePolicy policy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); + if ( d_data->orientation == Qt::Vertical ) + policy.transpose(); + + setSizePolicy( policy ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); +} + +//! Destructor +QwtThermo::~QwtThermo() +{ + delete d_data; +} + +/*! + \brief Exclude/Include min/max values + + According to the flags minValue() and maxValue() + are included/excluded from the pipe. In case of an + excluded value the corresponding tick is painted + 1 pixel off of the pipeRect(). + + F.e. when a minimum + of 0.0 has to be displayed as an empty pipe the minValue() + needs to be excluded. + + \param flags Range flags + \sa rangeFlags() +*/ +void QwtThermo::setRangeFlags( QwtInterval::BorderFlags flags ) +{ + if ( d_data->rangeFlags != flags ) + { + d_data->rangeFlags = flags; + update(); + } +} + +/*! + \return Range flags + \sa setRangeFlags() +*/ +QwtInterval::BorderFlags QwtThermo::rangeFlags() const +{ + return d_data->rangeFlags; +} + +/*! + Set the maximum value. + + \param maxValue Maximum value + \sa maxValue(), setMinValue(), setRange() +*/ +void QwtThermo::setMaxValue( double maxValue ) +{ + setRange( d_data->minValue, maxValue, qwtIsLogarithmic( this ) ); +} + +//! Return the maximum value. +double QwtThermo::maxValue() const +{ + return d_data->maxValue; +} + +/*! + Set the minimum value. + + \param minValue Minimum value + \sa minValue(), setMaxValue(), setRange() +*/ +void QwtThermo::setMinValue( double minValue ) +{ + setRange( minValue, d_data->maxValue, qwtIsLogarithmic( this ) ); +} + +//! Return the minimum value. +double QwtThermo::minValue() const +{ + return d_data->minValue; +} + +/*! + Set the current value. + + \param value New Value + \sa value() +*/ +void QwtThermo::setValue( double value ) +{ + if ( d_data->value != value ) + { + d_data->value = value; + update(); + } +} + +//! Return the value. +double QwtThermo::value() const +{ + return d_data->value; +} + +/*! + \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 ~QwtThermo or the next + call of setScaleDraw(). +*/ +void QwtThermo::setScaleDraw( QwtScaleDraw *scaleDraw ) +{ + setAbstractScaleDraw( scaleDraw ); +} + +/*! + \return the scale draw of the thermo + \sa setScaleDraw() +*/ +const QwtScaleDraw *QwtThermo::scaleDraw() const +{ + return static_cast<const QwtScaleDraw *>( abstractScaleDraw() ); +} + +/*! + \return the scale draw of the thermo + \sa setScaleDraw() +*/ +QwtScaleDraw *QwtThermo::scaleDraw() +{ + return static_cast<QwtScaleDraw *>( abstractScaleDraw() ); +} + +/*! + Qt paint event. + \param event Paint event +*/ +void QwtThermo::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + const QRect tRect = pipeRect(); + + if ( !tRect.contains( event->rect() ) ) + { + if ( d_data->scalePos != NoScale ) + scaleDraw()->draw( &painter, palette() ); + } + + const int bw = d_data->borderWidth; + + const QBrush brush = palette().brush( QPalette::Base ); + qDrawShadePanel( &painter, + tRect.adjusted( -bw, -bw, bw, bw ), + palette(), true, bw, + d_data->autoFillPipe ? &brush : NULL ); + + drawLiquid( &painter, tRect ); +} + +/*! + Qt resize event handler + \param event Resize event +*/ +void QwtThermo::resizeEvent( QResizeEvent *event ) +{ + Q_UNUSED( event ); + layoutThermo( false ); +} + +/*! + Qt change event handler + \param event Event +*/ +void QwtThermo::changeEvent( QEvent *event ) +{ + switch( event->type() ) + { + case QEvent::StyleChange: + case QEvent::FontChange: + { + layoutThermo( true ); + break; + } + default: + break; + } +} + +/*! + Recalculate the QwtThermo geometry and layout based on + the QwtThermo::contentsRect() and the fonts. + + \param update_geometry notify the layout system and call update + to redraw the scale +*/ +void QwtThermo::layoutThermo( bool update_geometry ) +{ + const QRect tRect = pipeRect(); + const int bw = d_data->borderWidth + d_data->spacing; + const bool inverted = ( maxValue() < minValue() ); + + int from, to; + + if ( d_data->orientation == Qt::Horizontal ) + { + from = tRect.left(); + to = tRect.right(); + + if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum ) + { + if ( inverted ) + to++; + else + from--; + } + if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum ) + { + if ( inverted ) + from--; + else + to++; + } + + switch ( d_data->scalePos ) + { + case TopScale: + { + scaleDraw()->setAlignment( QwtScaleDraw::TopScale ); + scaleDraw()->move( from, tRect.top() - bw ); + scaleDraw()->setLength( to - from ); + break; + } + + case BottomScale: + case NoScale: + default: + { + scaleDraw()->setAlignment( QwtScaleDraw::BottomScale ); + scaleDraw()->move( from, tRect.bottom() + bw ); + scaleDraw()->setLength( to - from ); + break; + } + } + + d_data->map.setPaintInterval( from, to ); + } + else // Qt::Vertical + { + from = tRect.top(); + to = tRect.bottom(); + + if ( d_data->rangeFlags & QwtInterval::ExcludeMinimum ) + { + if ( inverted ) + from--; + else + to++; + } + if ( d_data->rangeFlags & QwtInterval::ExcludeMaximum ) + { + if ( inverted ) + to++; + else + from--; + } + + switch ( d_data->scalePos ) + { + case RightScale: + { + scaleDraw()->setAlignment( QwtScaleDraw::RightScale ); + scaleDraw()->move( tRect.right() + bw, from ); + scaleDraw()->setLength( to - from ); + break; + } + + case LeftScale: + case NoScale: + default: + { + scaleDraw()->setAlignment( QwtScaleDraw::LeftScale ); + scaleDraw()->move( tRect.left() - bw, from ); + scaleDraw()->setLength( to - from ); + break; + } + } + d_data->map.setPaintInterval( to, from ); + } + + if ( update_geometry ) + { + updateGeometry(); + update(); + } +} + +/*! + \return Bounding rectangle of the pipe ( without borders ) + in widget coordinates +*/ +QRect QwtThermo::pipeRect() const +{ + const QRect cr = contentsRect(); + + int mbd = 0; + if ( d_data->scalePos != NoScale ) + { + int d1, d2; + scaleDraw()->getBorderDistHint( font(), d1, d2 ); + mbd = qMax( d1, d2 ); + } + const int bw = d_data->borderWidth; + + QRect tRect; + if ( d_data->orientation == Qt::Horizontal ) + { + switch ( d_data->scalePos ) + { + case TopScale: + { + tRect.setRect( + cr.x() + mbd + bw, + cr.y() + cr.height() - d_data->pipeWidth - bw, + cr.width() - 2 * ( bw + mbd ), + d_data->pipeWidth + ); + break; + } + + case BottomScale: + case NoScale: + default: + { + tRect.setRect( + cr.x() + mbd + bw, + cr.y() + bw, + cr.width() - 2 * ( bw + mbd ), + d_data->pipeWidth + ); + break; + } + } + } + else // Qt::Vertical + { + switch ( d_data->scalePos ) + { + case RightScale: + { + tRect.setRect( + cr.x() + bw, + cr.y() + mbd + bw, + d_data->pipeWidth, + cr.height() - 2 * ( bw + mbd ) + ); + break; + } + case LeftScale: + case NoScale: + default: + { + tRect.setRect( + cr.x() + cr.width() - bw - d_data->pipeWidth, + cr.y() + mbd + bw, + d_data->pipeWidth, + cr.height() - 2 * ( bw + mbd ) ); + break; + } + } + } + + return tRect; +} + +/*! + \brief Set the thermometer orientation and the scale position. + + The scale position NoScale disables the scale. + \param o orientation. Possible values are Qt::Horizontal and Qt::Vertical. + The default value is Qt::Vertical. + \param s Position of the scale. + The default value is NoScale. + + A valid combination of scale position and orientation is enforced: + - a horizontal thermometer can have the scale positions TopScale, + BottomScale or NoScale; + - a vertical thermometer can have the scale positions LeftScale, + RightScale or NoScale; + - an invalid scale position will default to NoScale. + + \sa setScalePosition() +*/ +void QwtThermo::setOrientation( Qt::Orientation o, ScalePos s ) +{ + if ( o == d_data->orientation && s == d_data->scalePos ) + return; + + switch ( o ) + { + case Qt::Horizontal: + { + if ( ( s == NoScale ) || ( s == BottomScale ) || ( s == TopScale ) ) + d_data->scalePos = s; + else + d_data->scalePos = NoScale; + break; + } + case Qt::Vertical: + { + if ( ( s == NoScale ) || ( s == LeftScale ) || ( s == RightScale ) ) + d_data->scalePos = s; + else + d_data->scalePos = NoScale; + break; + } + } + + if ( o != d_data->orientation ) + { + if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) ) + { + QSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); + } + } + + d_data->orientation = o; + layoutThermo( true ); +} + +/*! + \brief Change the scale position (and thermometer orientation). + + \param scalePos Position of the scale. + + A valid combination of scale position and orientation is enforced: + - if the new scale position is LeftScale or RightScale, the + scale orientation will become Qt::Vertical; + - if the new scale position is BottomScale or TopScale, the scale + orientation will become Qt::Horizontal; + - if the new scale position is NoScale, the scale orientation + will not change. + + \sa setOrientation(), scalePosition() +*/ +void QwtThermo::setScalePosition( ScalePos scalePos ) +{ + if ( ( scalePos == BottomScale ) || ( scalePos == TopScale ) ) + setOrientation( Qt::Horizontal, scalePos ); + else if ( ( scalePos == LeftScale ) || ( scalePos == RightScale ) ) + setOrientation( Qt::Vertical, scalePos ); + else + setOrientation( d_data->orientation, NoScale ); +} + +/*! + Return the scale position. + \sa setScalePosition() +*/ +QwtThermo::ScalePos QwtThermo::scalePosition() const +{ + return d_data->scalePos; +} + +//! Notify a scale change. +void QwtThermo::scaleChange() +{ + layoutThermo( true ); +} + +/*! + Redraw the liquid in thermometer pipe. + \param painter Painter + \param pipeRect Bounding rectangle of the pipe without borders +*/ +void QwtThermo::drawLiquid( + QPainter *painter, const QRect &pipeRect ) const +{ + painter->save(); + painter->setClipRect( pipeRect, Qt::IntersectClip ); + + const bool inverted = ( maxValue() < minValue() ); + if ( d_data->colorMap != NULL ) + { + QwtInterval interval( d_data->minValue, d_data->maxValue ); + interval = interval.normalized(); + + // Because the positions of the ticks are rounded + // we calculate the colors for the rounded tick values + + QVector<double> values = qwtTickList( + scaleDraw()->scaleDiv(), d_data->value ); + + if ( d_data->map.isInverting() ) + qSort( values.begin(), values.end(), qGreater<double>() ); + else + qSort( values.begin(), values.end(), qLess<double>() ); + + int from; + if ( !values.isEmpty() ) + { + from = qRound( d_data->map.transform( values[0] ) ); + qwtDrawLine( painter, from, + d_data->colorMap->color( interval, values[0] ), + pipeRect, d_data->orientation ); + } + + for ( int i = 1; i < values.size(); i++ ) + { + const int to = qRound( d_data->map.transform( values[i] ) ); + + for ( int pos = from + 1; pos < to; pos++ ) + { + const double v = d_data->map.invTransform( pos ); + + qwtDrawLine( painter, pos, + d_data->colorMap->color( interval, v ), + pipeRect, d_data->orientation ); + } + qwtDrawLine( painter, to, + d_data->colorMap->color( interval, values[i] ), + pipeRect, d_data->orientation ); + + from = to; + } + } + else + { + const int tval = qRound( d_data->map.transform( d_data->value ) ); + + QRect fillRect = pipeRect; + if ( d_data->orientation == Qt::Horizontal ) + { + if ( inverted ) + fillRect.setLeft( tval ); + else + fillRect.setRight( tval ); + } + else // Qt::Vertical + { + if ( inverted ) + fillRect.setBottom( tval ); + else + fillRect.setTop( tval ); + } + + if ( d_data->alarmEnabled && + d_data->value >= d_data->alarmLevel ) + { + QRect alarmRect = fillRect; + + const int taval = qRound( d_data->map.transform( d_data->alarmLevel ) ); + if ( d_data->orientation == Qt::Horizontal ) + { + if ( inverted ) + alarmRect.setRight( taval ); + else + alarmRect.setLeft( taval ); + } + else + { + if ( inverted ) + alarmRect.setTop( taval ); + else + alarmRect.setBottom( taval ); + } + + fillRect = QRegion( fillRect ).subtracted( alarmRect ).boundingRect(); + + painter->fillRect( alarmRect, palette().brush( QPalette::Highlight ) ); + } + + painter->fillRect( fillRect, palette().brush( QPalette::ButtonText ) ); + } + + painter->restore(); +} + +/*! + \brief Change the spacing between pipe and scale + + A spacing of 0 means, that the backbone of the scale is below + the pipe. + + The default setting is 3 pixels. + + \param spacing Number of pixels + \sa spacing(); +*/ +void QwtThermo::setSpacing( int spacing ) +{ + if ( spacing <= 0 ) + spacing = 0; + + if ( spacing != d_data->spacing ) + { + d_data->spacing = spacing; + layoutThermo( true ); + } +} + +/*! + \return Number of pixels between pipe and scale + \sa setSpacing() +*/ +int QwtThermo::spacing() const +{ + return d_data->spacing; +} + +/*! + Set the border width of the pipe. + \param width Border width + \sa borderWidth() +*/ +void QwtThermo::setBorderWidth( int width ) +{ + if ( width <= 0 ) + width = 0; + + if ( width != d_data->borderWidth ) + { + d_data->borderWidth = width; + layoutThermo( true ); + } +} + +/*! + Return the border width of the thermometer pipe. + \sa setBorderWidth() +*/ +int QwtThermo::borderWidth() const +{ + return d_data->borderWidth; +} + +/*! + \brief Set the range + + \param minValue value corresponding lower or left end + of the thermometer + \param maxValue value corresponding to the upper or + right end of the thermometer + \param logarithmic logarithmic mapping, true or false +*/ +void QwtThermo::setRange( + double minValue, double maxValue, bool logarithmic ) +{ + if ( minValue == d_data->minValue && maxValue == d_data->maxValue + && logarithmic == qwtIsLogarithmic( this ) ) + { + return; + } + + if ( logarithmic != qwtIsLogarithmic( this ) ) + { + if ( logarithmic ) + setScaleEngine( new QwtLog10ScaleEngine ); + else + setScaleEngine( new QwtLinearScaleEngine ); + } + + d_data->minValue = minValue; + d_data->maxValue = maxValue; + + /* + There are two different maps, one for the scale, the other + for the values. This is confusing and will be changed + in the future. TODO ... + */ + + d_data->map.setTransformation( scaleEngine()->transformation() ); + d_data->map.setScaleInterval( minValue, maxValue ); + + if ( autoScale() ) + rescale( minValue, maxValue ); + + layoutThermo( true ); +} + +/*! + \brief Assign a color map for the fill color + + \param colorMap Color map + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +void QwtThermo::setColorMap( QwtColorMap *colorMap ) +{ + if ( colorMap != d_data->colorMap ) + { + delete d_data->colorMap; + d_data->colorMap = colorMap; + } +} + +/*! + \return Color map for the fill color + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +QwtColorMap *QwtThermo::colorMap() +{ + return d_data->colorMap; +} + +/*! + \return Color map for the fill color + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +const QwtColorMap *QwtThermo::colorMap() const +{ + return d_data->colorMap; +} + +/*! + \brief Change the brush of the liquid. + + Changes the QPalette::ButtonText brush of the palette. + + \param brush New brush. + \sa fillBrush(), QWidget::setPalette() +*/ +void QwtThermo::setFillBrush( const QBrush& brush ) +{ + QPalette pal = palette(); + pal.setBrush( QPalette::ButtonText, brush ); + setPalette( pal ); +} + +/*! + Return the liquid ( QPalette::ButtonText ) brush. + \sa setFillBrush(), QWidget::palette() +*/ +const QBrush& QwtThermo::fillBrush() const +{ + return palette().brush( QPalette::ButtonText ); +} + +/*! + \brief Specify the liquid brush above the alarm threshold + + Changes the QPalette::Highlight brush of the palette. + + \param brush New brush. + \sa alarmBrush(), QWidget::setPalette() + + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +void QwtThermo::setAlarmBrush( const QBrush& brush ) +{ + QPalette pal = palette(); + pal.setBrush( QPalette::Highlight, brush ); + setPalette( pal ); +} + +/*! + Return the liquid brush ( QPalette::Highlight ) above the alarm threshold. + \sa setAlarmBrush(), QWidget::palette() + + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +const QBrush& QwtThermo::alarmBrush() const +{ + return palette().brush( QPalette::Highlight ); +} + +/*! + Specify the alarm threshold. + + \param level Alarm threshold + \sa alarmLevel() + + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +void QwtThermo::setAlarmLevel( double level ) +{ + d_data->alarmLevel = level; + d_data->alarmEnabled = 1; + update(); +} + +/*! + Return the alarm threshold. + \sa setAlarmLevel() + + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +double QwtThermo::alarmLevel() const +{ + return d_data->alarmLevel; +} + +/*! + Change the width of the pipe. + + \param width Width of the pipe + \sa pipeWidth() +*/ +void QwtThermo::setPipeWidth( int width ) +{ + if ( width > 0 ) + { + d_data->pipeWidth = width; + layoutThermo( true ); + } +} + +/*! + Return the width of the pipe. + \sa setPipeWidth() +*/ +int QwtThermo::pipeWidth() const +{ + return d_data->pipeWidth; +} + +/*! + \brief Enable or disable the alarm threshold + \param tf true (disabled) or false (enabled) + + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +void QwtThermo::setAlarmEnabled( bool tf ) +{ + d_data->alarmEnabled = tf; + update(); +} + +/*! + \return True, when the alarm threshold is enabled. + + \warning The alarm threshold has no effect, when + a color map has been assigned +*/ +bool QwtThermo::alarmEnabled() const +{ + return d_data->alarmEnabled; +} + +/*! + \return the minimum size hint + \sa minimumSizeHint() +*/ +QSize QwtThermo::sizeHint() const +{ + return minimumSizeHint(); +} + +/*! + \brief Return a minimum size hint + \warning The return value depends on the font and the scale. + \sa sizeHint() +*/ +QSize QwtThermo::minimumSizeHint() const +{ + int w = 0, h = 0; + + if ( d_data->scalePos != NoScale ) + { + const int sdExtent = qCeil( scaleDraw()->extent( font() ) ); + const int sdLength = scaleDraw()->minLength( font() ); + + w = sdLength; + h = d_data->pipeWidth + sdExtent + d_data->spacing; + + } + else // no scale + { + w = 200; + h = d_data->pipeWidth; + } + + if ( d_data->orientation == Qt::Vertical ) + qSwap( w, h ); + + w += 2 * d_data->borderWidth; + h += 2 * d_data->borderWidth; + + // finally add the margins + int left, right, top, bottom; + getContentsMargins( &left, &top, &right, &bottom ); + w += left + right; + h += top + bottom; + + return QSize( w, h ); +} diff --git a/src/libpcp_qwt/src/qwt_thermo.h b/src/libpcp_qwt/src/qwt_thermo.h new file mode 100644 index 0000000..e43ab12 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_thermo.h @@ -0,0 +1,202 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_THERMO_H +#define QWT_THERMO_H + +#include "qwt_global.h" +#include "qwt_abstract_scale.h" +#include "qwt_interval.h" +#include <qwidget.h> + +class QwtScaleDraw; +class QwtColorMap; + +/*! + \brief The Thermometer Widget + + QwtThermo is a widget which displays a value in an interval. It supports: + - a horizontal or vertical layout; + - a range; + - a scale; + - an alarm level. + + \image html sysinfo.png + + The fill colors might be calculated from an optional color map + If no color map has been assigned QwtThermo uses the + following colors/brushes from the widget palette: + + - QPalette::Base + Background of the pipe + - QPalette::ButtonText + Fill brush below the alarm level + - QPalette::Highlight + Fill brush for the values above the alarm level + - QPalette::WindowText + For the axis of the scale + - QPalette::Text + For the labels of the scale + + By default, the scale and range run over the same interval of values. + QwtAbstractScale::setScale() changes the interval of the scale and allows + easy conversion between physical units. + + The example shows how to make the scale indicate in degrees Fahrenheit and + to set the value in degrees Kelvin: +\code +#include <qapplication.h> +#include <qwt_thermo.h> + +double Kelvin2Fahrenheit(double kelvin) +{ + // see http://en.wikipedia.org/wiki/Kelvin + return 1.8*kelvin - 459.67; +} + +int main(int argc, char **argv) +{ + const double minKelvin = 0.0; + const double maxKelvin = 500.0; + + QApplication a(argc, argv); + QwtThermo t; + t.setRange(minKelvin, maxKelvin); + t.setScale(Kelvin2Fahrenheit(minKelvin), Kelvin2Fahrenheit(maxKelvin)); + // set the value in Kelvin but the scale displays in Fahrenheit + // 273.15 Kelvin = 0 Celsius = 32 Fahrenheit + t.setValue(273.15); + a.setMainWidget(&t); + t.show(); + return a.exec(); +} +\endcode + + \todo Improve the support for a logarithmic range and/or scale. +*/ +class QWT_EXPORT QwtThermo: public QWidget, public QwtAbstractScale +{ + Q_OBJECT + + Q_ENUMS( ScalePos ) + + Q_PROPERTY( bool alarmEnabled READ alarmEnabled WRITE setAlarmEnabled ) + Q_PROPERTY( double alarmLevel READ alarmLevel WRITE setAlarmLevel ) + Q_PROPERTY( ScalePos scalePosition READ scalePosition + WRITE setScalePosition ) + Q_PROPERTY( int spacing READ spacing WRITE setSpacing ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + Q_PROPERTY( double maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( double minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( int pipeWidth READ pipeWidth WRITE setPipeWidth ) + Q_PROPERTY( double value READ value WRITE setValue ) + +public: + /*! + Scale position. QwtThermo tries to enforce valid combinations of its + orientation and scale position: + + - Qt::Horizonal combines with NoScale, TopScale and BottomScale + - Qt::Vertical combines with NoScale, LeftScale and RightScale + + \sa setOrientation(), setScalePosition() + */ + enum ScalePos + { + //! No scale + NoScale, + + //! The scale is left of the pipe + LeftScale, + + //! The scale is right of the pipe + RightScale, + + //! The scale is above the pipe + TopScale, + + //! The scale is below the pipe + BottomScale + }; + + explicit QwtThermo( QWidget *parent = NULL ); + virtual ~QwtThermo(); + + void setOrientation( Qt::Orientation, ScalePos ); + + void setScalePosition( ScalePos s ); + ScalePos scalePosition() const; + + void setSpacing( int ); + int spacing() const; + + void setBorderWidth( int w ); + int borderWidth() const; + + void setFillBrush( const QBrush &b ); + const QBrush &fillBrush() const; + + void setAlarmBrush( const QBrush &b ); + const QBrush &alarmBrush() const; + + void setAlarmLevel( double v ); + double alarmLevel() const; + + void setAlarmEnabled( bool tf ); + bool alarmEnabled() const; + + void setColorMap( QwtColorMap * ); + QwtColorMap *colorMap(); + const QwtColorMap *colorMap() const; + + void setPipeWidth( int w ); + int pipeWidth() const; + + void setRangeFlags( QwtInterval::BorderFlags ); + QwtInterval::BorderFlags rangeFlags() const; + + void setMaxValue( double v ); + double maxValue() const; + + void setMinValue( double v ); + double minValue() const; + + double value() const; + + void setRange( double vmin, double vmax, bool lg = false ); + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + + void setScaleDraw( QwtScaleDraw * ); + const QwtScaleDraw *scaleDraw() const; + +public Q_SLOTS: + virtual void setValue( double val ); + +protected: + virtual void drawLiquid( QPainter *, const QRect & ) const; + virtual void scaleChange(); + + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + virtual void changeEvent( QEvent * ); + + QwtScaleDraw *scaleDraw(); + + QRect pipeRect() const; + +private: + void layoutThermo( bool ); + + class PrivateData; + PrivateData *d_data; +}; + +#endif diff --git a/src/libpcp_qwt/src/qwt_wheel.cpp b/src/libpcp_qwt/src/qwt_wheel.cpp new file mode 100644 index 0000000..1379e18 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_wheel.cpp @@ -0,0 +1,544 @@ +/* -*- 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_wheel.h" +#include "qwt_math.h" +#include "qwt_painter.h" +#include <qevent.h> +#include <qdrawutil.h> +#include <qpainter.h> +#include <qstyle.h> +#include <qstyleoption.h> +#include <qapplication.h> + +#if QT_VERSION < 0x040601 +#define qFastSin(x) ::sin(x) +#endif + +class QwtWheel::PrivateData +{ +public: + PrivateData() + { + viewAngle = 175.0; + totalAngle = 360.0; + tickCnt = 10; + wheelBorderWidth = 2; + borderWidth = 2; + wheelWidth = 20; + }; + + double viewAngle; + double totalAngle; + int tickCnt; + int wheelBorderWidth; + int borderWidth; + int wheelWidth; +}; + +//! Constructor +QwtWheel::QwtWheel( QWidget *parent ): + QwtAbstractSlider( Qt::Horizontal, parent ) +{ + d_data = new PrivateData; + + setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); + setUpdateTime( 50 ); +} + +//! Destructor +QwtWheel::~QwtWheel() +{ + delete d_data; +} + +/*! + \brief Adjust the number of grooves in the wheel's surface. + + The number of grooves is limited to 6 <= cnt <= 50. + Values outside this range will be clipped. + The default value is 10. + + \param cnt Number of grooves per 360 degrees + \sa tickCnt() +*/ +void QwtWheel::setTickCnt( int cnt ) +{ + d_data->tickCnt = qBound( 6, cnt, 50 ); + update(); +} + +/*! + \return Number of grooves in the wheel's surface. + \sa setTickCnt() +*/ +int QwtWheel::tickCnt() const +{ + return d_data->tickCnt; +} + +/*! + \return mass +*/ +double QwtWheel::mass() const +{ + return QwtAbstractSlider::mass(); +} + +/*! + \brief Set the wheel border width of the wheel. + + The wheel border must not be smaller than 1 + and is limited in dependence on the wheel's size. + Values outside the allowed range will be clipped. + + The wheel border defaults to 2. + + \param borderWidth Border width + \sa internalBorder() +*/ +void QwtWheel::setWheelBorderWidth( int borderWidth ) +{ + const int d = qMin( width(), height() ) / 3; + borderWidth = qMin( borderWidth, d ); + d_data->wheelBorderWidth = qMax( borderWidth, 1 ); + update(); +} + +/*! + \return Wheel border width + \sa setWheelBorderWidth() +*/ +int QwtWheel::wheelBorderWidth() const +{ + return d_data->wheelBorderWidth; +} + +/*! + \brief Set the border width + + The border defaults to 2. + + \param width Border width + \sa borderWidth() +*/ +void QwtWheel::setBorderWidth( int width ) +{ + d_data->borderWidth = qMax( width, 0 ); + update(); +} + +/*! + \return Border width + \sa setBorderWidth() +*/ +int QwtWheel::borderWidth() const +{ + return d_data->borderWidth; +} + +/*! + \return Rectangle of the wheel without the outer border +*/ +QRect QwtWheel::wheelRect() const +{ + const int bw = d_data->borderWidth; + return contentsRect().adjusted( bw, bw, -bw, -bw ); +} + +/*! + \brief Set the total angle which the wheel can be turned. + + One full turn of the wheel corresponds to an angle of + 360 degrees. A total angle of n*360 degrees means + that the wheel has to be turned n times around its axis + to get from the minimum value to the maximum value. + + The default setting of the total angle is 360 degrees. + + \param angle total angle in degrees + \sa totalAngle() +*/ +void QwtWheel::setTotalAngle( double angle ) +{ + if ( angle < 0.0 ) + angle = 0.0; + + d_data->totalAngle = angle; + update(); +} + +/*! + \return Total angle which the wheel can be turned. + \sa setTotalAngle() +*/ +double QwtWheel::totalAngle() const +{ + return d_data->totalAngle; +} + +/*! + \brief Set the wheel's orientation. + \param o Orientation. Allowed values are + Qt::Horizontal and Qt::Vertical. + Defaults to Qt::Horizontal. + \sa QwtAbstractSlider::orientation() +*/ +void QwtWheel::setOrientation( Qt::Orientation o ) +{ + if ( orientation() == o ) + return; + + if ( !testAttribute( Qt::WA_WState_OwnSizePolicy ) ) + { + QSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + + setAttribute( Qt::WA_WState_OwnSizePolicy, false ); + } + + QwtAbstractSlider::setOrientation( o ); + update(); +} + +/*! + \brief Specify the visible portion of the wheel. + + You may use this function for fine-tuning the appearance of + the wheel. The default value is 175 degrees. The value is + limited from 10 to 175 degrees. + + \param angle Visible angle in degrees + \sa viewAngle(), setTotalAngle() +*/ +void QwtWheel::setViewAngle( double angle ) +{ + d_data->viewAngle = qBound( 10.0, angle, 175.0 ); + update(); +} + +/*! + \return Visible portion of the wheel + \sa setViewAngle(), totalAngle() +*/ +double QwtWheel::viewAngle() const +{ + return d_data->viewAngle; +} + +//! Determine the value corresponding to a specified point +double QwtWheel::getValue( const QPoint &p ) +{ + const QRectF rect = wheelRect(); + + // The reference position is arbitrary, but the + // sign of the offset is important + double w, dx; + if ( orientation() == Qt::Vertical ) + { + w = rect.height(); + dx = rect.y() - p.y(); + } + else + { + w = rect.width(); + dx = p.x() - rect.x(); + } + + if ( w == 0.0 ) + return 0.0; + + // w pixels is an arc of viewAngle degrees, + // so we convert change in pixels to change in angle + const double ang = dx * d_data->viewAngle / w; + + // value range maps to totalAngle degrees, + // so convert the change in angle to a change in value + const double val = ang * ( maxValue() - minValue() ) / d_data->totalAngle; + + // Note, range clamping and rasterizing to step is automatically + // handled by QwtAbstractSlider, so we simply return the change in value + return val; +} + +/*! + \brief Qt Resize Event + \param event Resize event +*/ +void QwtWheel::resizeEvent( QResizeEvent *event ) +{ + QwtAbstractSlider::resizeEvent( event ); +} + +/*! + \brief Qt Paint Event + \param event Paint event +*/ +void QwtWheel::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + painter.setClipRegion( event->region() ); + + QStyleOption opt; + opt.init(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); + + qDrawShadePanel( &painter, + contentsRect(), palette(), true, d_data->borderWidth ); + + drawWheelBackground( &painter, wheelRect() ); + drawTicks( &painter, wheelRect() ); + + if ( hasFocus() ) + QwtPainter::drawFocusRect( &painter, this ); +} + +/*! + Draw the Wheel's background gradient + + \param painter Painter + \param rect Rectangle for the wheel +*/ +void QwtWheel::drawWheelBackground( + QPainter *painter, const QRectF &rect ) +{ + painter->save(); + + QPalette pal = palette(); + + // draw shaded background + QLinearGradient gradient( rect.topLeft(), + ( orientation() == Qt::Horizontal ) ? rect.topRight() : rect.bottomLeft() ); + gradient.setColorAt( 0.0, pal.color( QPalette::Button ) ); + gradient.setColorAt( 0.2, pal.color( QPalette::Light ) ); + gradient.setColorAt( 0.7, pal.color( QPalette::Mid ) ); + gradient.setColorAt( 1.0, pal.color( QPalette::Dark ) ); + + painter->fillRect( rect, gradient ); + + // draw internal border + + const QPen lightPen( palette().color( QPalette::Light ), + d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap ); + const QPen darkPen( pal.color( QPalette::Dark ), + d_data->wheelBorderWidth, Qt::SolidLine, Qt::FlatCap ); + + const double bw2 = 0.5 * d_data->wheelBorderWidth; + + if ( orientation() == Qt::Horizontal ) + { + painter->setPen( lightPen ); + painter->drawLine( QPointF( rect.left(), rect.top() + bw2 ), + QPointF( rect.right(), rect.top() + bw2 ) ); + + painter->setPen( darkPen ); + painter->drawLine( QPointF( rect.left(), rect.bottom() - bw2 ), + QPointF( rect.right(), rect.bottom() - bw2 ) ); + } + else // Qt::Vertical + { + painter->setPen( lightPen ); + painter->drawLine( QPointF( rect.left() + bw2, rect.top() ), + QPointF( rect.left() + bw2, rect.bottom() ) ); + + painter->setPen( darkPen ); + painter->drawLine( QPointF( rect.right() - bw2, rect.top() ), + QPointF( rect.right() - bw2, rect.bottom() ) ); + } + + painter->restore(); +} + +/*! + Draw the Wheel's ticks + + \param painter Painter + \param rect Rectangle for the wheel +*/ +void QwtWheel::drawTicks( QPainter *painter, const QRectF &rect ) +{ + if ( maxValue() == minValue() || d_data->totalAngle == 0.0 ) + { + return; + } + + const QPen lightPen( palette().color( QPalette::Light ), + 0, Qt::SolidLine, Qt::FlatCap ); + const QPen darkPen( palette().color( QPalette::Dark ), + 0, Qt::SolidLine, Qt::FlatCap ); + + const double sign = ( minValue() < maxValue() ) ? 1.0 : -1.0; + const double cnvFactor = qAbs( d_data->totalAngle / ( maxValue() - minValue() ) ); + const double halfIntv = 0.5 * d_data->viewAngle / cnvFactor; + const double loValue = value() - halfIntv; + const double hiValue = value() + halfIntv; + const double tickWidth = 360.0 / double( d_data->tickCnt ) / cnvFactor; + const double sinArc = qFastSin( d_data->viewAngle * M_PI / 360.0 ); + + if ( orientation() == Qt::Horizontal ) + { + const double halfSize = rect.width() * 0.5; + + double l1 = rect.top() + d_data->wheelBorderWidth; + double l2 = rect.bottom() - d_data->wheelBorderWidth - 1; + + // draw one point over the border if border > 1 + if ( d_data->wheelBorderWidth > 1 ) + { + l1--; + l2++; + } + + const double maxpos = rect.right() - 2; + const double minpos = rect.left() + 2; + + // draw tick marks + for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth; + tickValue < hiValue; tickValue += tickWidth ) + { + const double angle = ( tickValue - value() ) * M_PI / 180.0; + const double s = qFastSin( angle * cnvFactor ); + + const double tickPos = + rect.right() - halfSize * ( sinArc + sign * s ) / sinArc; + + if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) ) + { + painter->setPen( darkPen ); + painter->drawLine( QPointF( tickPos - 1 , l1 ), + QPointF( tickPos - 1, l2 ) ); + painter->setPen( lightPen ); + painter->drawLine( QPointF( tickPos, l1 ), + QPointF( tickPos, l2 ) ); + } + } + } + else // Qt::Vertical + { + const double halfSize = rect.height() * 0.5; + + double l1 = rect.left() + d_data->wheelBorderWidth; + double l2 = rect.right() - d_data->wheelBorderWidth - 1; + + if ( d_data->wheelBorderWidth > 1 ) + { + l1--; + l2++; + } + + const double maxpos = rect.bottom() - 2; + const double minpos = rect.top() + 2; + + for ( double tickValue = ::ceil( loValue / tickWidth ) * tickWidth; + tickValue < hiValue; tickValue += tickWidth ) + { + const double angle = ( tickValue - value() ) * M_PI / 180.0; + const double s = qFastSin( angle * cnvFactor ); + + const double tickPos = + rect.y() + halfSize * ( sinArc + sign * s ) / sinArc; + + if ( ( tickPos <= maxpos ) && ( tickPos > minpos ) ) + { + painter->setPen( darkPen ); + painter->drawLine( QPointF( l1, tickPos - 1 ), + QPointF( l2, tickPos - 1 ) ); + painter->setPen( lightPen ); + painter->drawLine( QPointF( l1, tickPos ), + QPointF( l2, tickPos ) ); + } + } + } +} + +//! Notify value change +void QwtWheel::valueChange() +{ + QwtAbstractSlider::valueChange(); + update(); +} + +/*! + \brief Determine the scrolling mode and direction corresponding + to a specified point + \param p point + \param scrollMode scrolling mode + \param direction direction +*/ +void QwtWheel::getScrollMode( const QPoint &p, + QwtAbstractSlider::ScrollMode &scrollMode, int &direction ) const +{ + if ( wheelRect().contains( p ) ) + scrollMode = QwtAbstractSlider::ScrMouse; + else + scrollMode = QwtAbstractSlider::ScrNone; + + direction = 0; +} + +/*! + \brief Set the mass of the wheel + + Assigning a mass turns the wheel into a flywheel. + \param mass The wheel's mass +*/ +void QwtWheel::setMass( double mass ) +{ + QwtAbstractSlider::setMass( mass ); +} + +/*! + \brief Set the width of the wheel + + Corresponds to the wheel height for horizontal orientation, + and the wheel width for vertical orientation. + + \param width the wheel's width + \sa wheelWidth() +*/ +void QwtWheel::setWheelWidth( int width ) +{ + d_data->wheelWidth = width; + update(); +} + +/*! + \return Width of the wheel + \sa setWheelWidth() +*/ +int QwtWheel::wheelWidth() const +{ + return d_data->wheelWidth; +} + +/*! + \return a size hint +*/ +QSize QwtWheel::sizeHint() const +{ + const QSize hint = minimumSizeHint(); + return hint.expandedTo( QApplication::globalStrut() ); +} + +/*! + \brief Return a minimum size hint + \warning The return value is based on the wheel width. +*/ +QSize QwtWheel::minimumSizeHint() const +{ + QSize sz( 3 * d_data->wheelWidth + 2 * d_data->borderWidth, + d_data->wheelWidth + 2 * d_data->borderWidth ); + if ( orientation() != Qt::Horizontal ) + sz.transpose(); + + return sz; +} diff --git a/src/libpcp_qwt/src/qwt_wheel.h b/src/libpcp_qwt/src/qwt_wheel.h new file mode 100644 index 0000000..7e18e46 --- /dev/null +++ b/src/libpcp_qwt/src/qwt_wheel.h @@ -0,0 +1,89 @@ +/* -*- 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 + *****************************************************************************/ + +#ifndef QWT_WHEEL_H +#define QWT_WHEEL_H + +#include "qwt_global.h" +#include "qwt_abstract_slider.h" + +/*! + \brief The Wheel Widget + + The wheel widget can be used to change values over a very large range + in very small steps. Using the setMass member, it can be configured + as a flywheel. + + \sa The radio example. +*/ +class QWT_EXPORT QwtWheel : public QwtAbstractSlider +{ + Q_OBJECT + + Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle ) + Q_PROPERTY( double viewAngle READ viewAngle WRITE setViewAngle ) + Q_PROPERTY( int tickCnt READ tickCnt WRITE setTickCnt ) + Q_PROPERTY( int wheelWidth READ wheelWidth WRITE setWheelWidth ) + Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) + Q_PROPERTY( int wheelBorderWidth READ wheelBorderWidth WRITE setWheelBorderWidth ) + Q_PROPERTY( double mass READ mass WRITE setMass ) + +public: + explicit QwtWheel( QWidget *parent = NULL ); + virtual ~QwtWheel(); + +public Q_SLOTS: + void setTotalAngle ( double ); + void setViewAngle( double ); + +public: + virtual void setOrientation( Qt::Orientation ); + + double totalAngle() const; + double viewAngle() const; + + void setTickCnt( int ); + int tickCnt() const; + + void setMass( double ); + double mass() const; + + void setWheelWidth( int ); + int wheelWidth() const; + + void setWheelBorderWidth( int ); + int wheelBorderWidth() const; + + void setBorderWidth( int ); + int borderWidth() const; + + QRect wheelRect() const; + + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; + +protected: + virtual void paintEvent( QPaintEvent * ); + virtual void resizeEvent( QResizeEvent * ); + + virtual void drawTicks( QPainter *, const QRectF & ); + virtual void drawWheelBackground( QPainter *, const QRectF & ); + + virtual void valueChange(); + + virtual double getValue( const QPoint & ); + virtual void getScrollMode( const QPoint &, + QwtAbstractSlider::ScrollMode &, int &direction ) const; + +private: + class PrivateData; + PrivateData *d_data; +}; + +#endif |