mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 00:28:42 +00:00
With the introduction of the rideSelected signal the RideFile was opened (as previously) by the RideSummaryWindow::htmlSummary() member. In some cases, this signal was processed by RideSummary window AFTER the other charts (AllPlot etc) this results in 'No data' being shown on other charts. This patch moves the file reading to RideItem::ride() which was previously a public RideFile * (that is now a protected member ride_). As a happy by product it removes the need to check if the file has already been read across all other functions ensuring in-core values are not accidentally overwritten. The read errors are made available by a new RideItem::errors() member. This modification is required to support the RideImportWizard in freeing loaded RideFiles during batch import to ensure virtual memory is not exhausted when large numbers of files are imported at once. This modification is also included in this patch.
722 lines
17 KiB
C++
722 lines
17 KiB
C++
/*
|
|
* Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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 General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "PowerHist.h"
|
|
#include "MainWindow.h"
|
|
#include "RideItem.h"
|
|
#include "IntervalItem.h"
|
|
#include "RideFile.h"
|
|
#include "Settings.h"
|
|
#include "Zones.h"
|
|
|
|
#include <assert.h>
|
|
#include <qpainter.h>
|
|
#include <qwt_plot_curve.h>
|
|
#include <qwt_plot_grid.h>
|
|
#include <qwt_plot_zoomer.h>
|
|
#include <qwt_scale_engine.h>
|
|
#include <qwt_text.h>
|
|
#include <qwt_legend.h>
|
|
#include <qwt_data.h>
|
|
|
|
class penTooltip: public QwtPlotZoomer
|
|
{
|
|
public:
|
|
penTooltip(QwtPlotCanvas *canvas):
|
|
QwtPlotZoomer(canvas)
|
|
{
|
|
// With some versions of Qt/Qwt, setting this to AlwaysOn
|
|
// causes an infinite recursion.
|
|
//setTrackerMode(AlwaysOn);
|
|
setTrackerMode(AlwaysOff);
|
|
}
|
|
|
|
virtual QwtText trackerText(const QwtDoublePoint &pos) const
|
|
{
|
|
QColor bg(Qt::white);
|
|
#if QT_VERSION >= 0x040300
|
|
bg.setAlpha(200);
|
|
#endif
|
|
|
|
QwtText text = QString("%1").arg((int)pos.x());
|
|
text.setBackgroundBrush( QBrush( bg ));
|
|
return text;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// define a background class to handle shading of power zones
|
|
// draws power zone bands IF zones are defined and the option
|
|
// to draw bonds has been selected
|
|
class PowerHistBackground: public QwtPlotItem
|
|
{
|
|
private:
|
|
PowerHist *parent;
|
|
|
|
public:
|
|
PowerHistBackground(PowerHist *_parent)
|
|
{
|
|
setZ(0.0);
|
|
parent = _parent;
|
|
}
|
|
|
|
virtual int rtti() const
|
|
{
|
|
return QwtPlotItem::Rtti_PlotUserItem;
|
|
}
|
|
|
|
virtual void draw(QPainter *painter,
|
|
const QwtScaleMap &xMap, const QwtScaleMap &,
|
|
const QRect &rect) const
|
|
{
|
|
RideItem *rideItem = parent->rideItem;
|
|
|
|
if (! rideItem)
|
|
return;
|
|
|
|
const Zones *zones = rideItem->zones;
|
|
int zone_range = rideItem->zoneRange();
|
|
|
|
if (parent->shadeZones() && (zone_range >= 0)) {
|
|
QList <int> zone_lows = zones->getZoneLows(zone_range);
|
|
int num_zones = zone_lows.size();
|
|
if (num_zones > 0) {
|
|
for (int z = 0; z < num_zones; z ++) {
|
|
QRect r = rect;
|
|
|
|
QColor shading_color =
|
|
zoneColor(z, num_zones);
|
|
shading_color.setHsv(
|
|
shading_color.hue(),
|
|
shading_color.saturation() / 4,
|
|
shading_color.value()
|
|
);
|
|
r.setLeft(xMap.transform(zone_lows[z]));
|
|
if (z + 1 < num_zones)
|
|
r.setRight(xMap.transform(zone_lows[z + 1]));
|
|
if (r.right() >= r.left())
|
|
painter->fillRect(r, shading_color);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// Zone labels are drawn if power zone bands are enabled, automatically
|
|
// at the center of the plot
|
|
class PowerHistZoneLabel: public QwtPlotItem
|
|
{
|
|
private:
|
|
PowerHist *parent;
|
|
int zone_number;
|
|
double watts;
|
|
QwtText text;
|
|
|
|
public:
|
|
PowerHistZoneLabel(PowerHist *_parent, int _zone_number)
|
|
{
|
|
parent = _parent;
|
|
zone_number = _zone_number;
|
|
|
|
RideItem *rideItem = parent->rideItem;
|
|
|
|
if (! rideItem)
|
|
return;
|
|
|
|
const Zones *zones = rideItem->zones;
|
|
int zone_range = rideItem->zoneRange();
|
|
|
|
setZ(1.0 + zone_number / 100.0);
|
|
|
|
// create new zone labels if we're shading
|
|
if (parent->shadeZones() && (zone_range >= 0)) {
|
|
QList <int> zone_lows = zones->getZoneLows(zone_range);
|
|
QList <QString> zone_names = zones->getZoneNames(zone_range);
|
|
int num_zones = zone_lows.size();
|
|
assert(zone_names.size() == num_zones);
|
|
if (zone_number < num_zones) {
|
|
watts =
|
|
(
|
|
(zone_number + 1 < num_zones) ?
|
|
0.5 * (zone_lows[zone_number] + zone_lows[zone_number + 1]) :
|
|
(
|
|
(zone_number > 0) ?
|
|
(1.5 * zone_lows[zone_number] - 0.5 * zone_lows[zone_number - 1]) :
|
|
2.0 * zone_lows[zone_number]
|
|
)
|
|
);
|
|
|
|
text = QwtText(zone_names[zone_number]);
|
|
text.setFont(QFont("Helvetica",24, QFont::Bold));
|
|
QColor text_color = zoneColor(zone_number, num_zones);
|
|
text_color.setAlpha(64);
|
|
text.setColor(text_color);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
virtual int rtti() const
|
|
{
|
|
return QwtPlotItem::Rtti_PlotUserItem;
|
|
}
|
|
|
|
void draw(QPainter *painter,
|
|
const QwtScaleMap &xMap, const QwtScaleMap &,
|
|
const QRect &rect) const
|
|
{
|
|
if (parent->shadeZones()) {
|
|
int x = xMap.transform(watts);
|
|
int y = (rect.bottom() + rect.top()) / 2;
|
|
|
|
// the following code based on source for QwtPlotMarker::draw()
|
|
QRect tr(QPoint(0, 0), text.textSize(painter->font()));
|
|
tr.moveCenter(QPoint(y, -x));
|
|
painter->rotate(90); // rotate text to avoid overlap: this needs to be fixed
|
|
text.draw(painter, tr);
|
|
}
|
|
}
|
|
};
|
|
|
|
PowerHist::PowerHist(MainWindow *mainWindow):
|
|
selected(wattsShaded),
|
|
rideItem(NULL),
|
|
mainWindow(mainWindow),
|
|
binw(20),
|
|
withz(true),
|
|
settings(NULL),
|
|
unit(0),
|
|
lny(false)
|
|
{
|
|
|
|
boost::shared_ptr<QSettings> settings = GetApplicationSettings();
|
|
|
|
unit = settings->value(GC_UNIT);
|
|
|
|
useMetricUnits = (unit.toString() == "Metric");
|
|
|
|
// create a background object for shading
|
|
bg = new PowerHistBackground(this);
|
|
bg->attach(this);
|
|
|
|
setCanvasBackground(Qt::white);
|
|
|
|
setParameterAxisTitle();
|
|
setAxisTitle(yLeft, "Cumulative Time (minutes)");
|
|
|
|
curve = new QwtPlotCurve("");
|
|
curve->setStyle(QwtPlotCurve::Steps);
|
|
curve->setRenderHint(QwtPlotItem::RenderAntialiased);
|
|
QPen *pen = new QPen(Qt::black);
|
|
pen->setWidth(2.0);
|
|
curve->setPen(*pen);
|
|
QColor brush_color = Qt::black;
|
|
brush_color.setAlpha(64);
|
|
curve->setBrush(brush_color); // fill below the line
|
|
delete pen;
|
|
curve->attach(this);
|
|
|
|
curveSelected = new QwtPlotCurve("");
|
|
curveSelected->setStyle(QwtPlotCurve::Steps);
|
|
curveSelected->setRenderHint(QwtPlotItem::RenderAntialiased);
|
|
pen = new QPen(Qt::blue);
|
|
pen->setWidth(2.0);
|
|
curveSelected->setPen(*pen);
|
|
brush_color = Qt::blue;
|
|
brush_color.setAlpha(64);
|
|
curveSelected->setBrush(brush_color); // fill below the line
|
|
delete pen;
|
|
curveSelected->attach(this);
|
|
|
|
grid = new QwtPlotGrid();
|
|
grid->enableX(false);
|
|
QPen gridPen;
|
|
gridPen.setStyle(Qt::DotLine);
|
|
grid->setPen(gridPen);
|
|
grid->attach(this);
|
|
|
|
zoneLabels = QList <PowerHistZoneLabel *>::QList();
|
|
|
|
zoomer = new penTooltip(this->canvas());
|
|
}
|
|
|
|
PowerHist::~PowerHist() {
|
|
delete bg;
|
|
delete curve;
|
|
delete curveSelected;
|
|
delete grid;
|
|
}
|
|
|
|
// static const variables from PoweHist.h:
|
|
// discritized unit for smoothing
|
|
const double PowerHist::wattsDelta;
|
|
const double PowerHist::nmDelta;
|
|
const double PowerHist::hrDelta;
|
|
const double PowerHist::kphDelta;
|
|
const double PowerHist::cadDelta;
|
|
|
|
// digits for text entry validator
|
|
const int PowerHist::wattsDigits;
|
|
const int PowerHist::nmDigits;
|
|
const int PowerHist::hrDigits;
|
|
const int PowerHist::kphDigits;
|
|
const int PowerHist::cadDigits;
|
|
|
|
void
|
|
PowerHist::refreshZoneLabels()
|
|
{
|
|
// delete any existing power zone labels
|
|
if (zoneLabels.size()) {
|
|
QListIterator<PowerHistZoneLabel *> i(zoneLabels);
|
|
while (i.hasNext()) {
|
|
PowerHistZoneLabel *label = i.next();
|
|
label->detach();
|
|
delete label;
|
|
}
|
|
}
|
|
zoneLabels.clear();
|
|
|
|
if (! rideItem)
|
|
return;
|
|
|
|
if ((selected == wattsShaded) || (selected == wattsUnshaded)) {
|
|
const Zones *zones = rideItem->zones;
|
|
int zone_range = rideItem->zoneRange();
|
|
|
|
// generate labels for existing zones
|
|
if (zone_range >= 0) {
|
|
int num_zones = zones->numZones(zone_range);
|
|
for (int z = 0; z < num_zones; z ++) {
|
|
PowerHistZoneLabel *label = new PowerHistZoneLabel(this, z);
|
|
label->attach(this);
|
|
zoneLabels.append(label);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PowerHist::recalc()
|
|
{
|
|
QVector<unsigned int> *array;
|
|
QVector<unsigned int> *selectedArray;
|
|
int arrayLength = 0;
|
|
double delta;
|
|
|
|
// make sure the interval length is set
|
|
if (dt <= 0)
|
|
return;
|
|
|
|
if ((selected == wattsShaded) ||
|
|
(selected == wattsUnshaded)
|
|
) {
|
|
array = &wattsArray;
|
|
delta = wattsDelta;
|
|
arrayLength = wattsArray.size();
|
|
selectedArray = &wattsSelectedArray;
|
|
}
|
|
else if (selected == nm) {
|
|
array = &nmArray;
|
|
delta = nmDelta;
|
|
arrayLength = nmArray.size();
|
|
selectedArray = &nmSelectedArray;
|
|
}
|
|
else if (selected == hr) {
|
|
array = &hrArray;
|
|
delta = hrDelta;
|
|
arrayLength = hrArray.size();
|
|
selectedArray = &hrSelectedArray;
|
|
}
|
|
else if (selected == kph) {
|
|
array = &kphArray;
|
|
delta = kphDelta;
|
|
arrayLength = kphArray.size();
|
|
selectedArray = &kphSelectedArray;
|
|
}
|
|
else if (selected == cad) {
|
|
array = &cadArray;
|
|
delta = cadDelta;
|
|
arrayLength = cadArray.size();
|
|
selectedArray = &cadSelectedArray;
|
|
}
|
|
|
|
if (!array)
|
|
return;
|
|
|
|
int count = int(ceil((arrayLength - 1) / binw));
|
|
|
|
// allocate space for data, plus beginning and ending point
|
|
QVector<double> parameterValue(count+2);
|
|
QVector<double> totalTime(count+2);
|
|
QVector<double> totalTimeSelected(count+2);
|
|
int i;
|
|
for (i = 1; i <= count; ++i) {
|
|
int high = i * binw;
|
|
int low = high - binw;
|
|
if (low==0 && !withz)
|
|
low++;
|
|
parameterValue[i] = high * delta;
|
|
totalTime[i] = 1e-9; // nonzero to accomodate log plot
|
|
totalTimeSelected[i] = 1e-9; // nonzero to accomodate log plot
|
|
while (low < high) {
|
|
if (selectedArray && (*selectedArray).size()>low)
|
|
totalTimeSelected[i] += dt * (*selectedArray)[low];
|
|
totalTime[i] += dt * (*array)[low++];
|
|
}
|
|
}
|
|
totalTime[i] = 1e-9; // nonzero to accomodate log plot
|
|
parameterValue[i] = i * delta * binw;
|
|
totalTime[0] = 1e-9;
|
|
parameterValue[0] = 0;
|
|
curve->setData(parameterValue.data(), totalTime.data(), count + 2);
|
|
curveSelected->setData(parameterValue.data(), totalTimeSelected.data(), count + 2);
|
|
setAxisScale(xBottom, 0.0, parameterValue[count + 1]);
|
|
|
|
refreshZoneLabels();
|
|
|
|
setYMax();
|
|
replot();
|
|
}
|
|
|
|
void
|
|
PowerHist::setYMax()
|
|
{
|
|
static const double tmin = 1.0/60;
|
|
setAxisScale(yLeft, (lny ? tmin : 0.0), curve->maxYValue() * 1.1);
|
|
}
|
|
|
|
void
|
|
PowerHist::setData(RideItem *_rideItem)
|
|
{
|
|
rideItem = _rideItem;
|
|
|
|
RideFile *ride = rideItem->ride();
|
|
|
|
if (ride) {
|
|
setTitle(ride->startTime().toString(GC_DATETIME_FORMAT));
|
|
|
|
static const int maxSize = 4096;
|
|
|
|
// recording interval in minutes
|
|
dt = ride->recIntSecs() / 60.0;
|
|
|
|
wattsArray.resize(0);
|
|
nmArray.resize(0);
|
|
hrArray.resize(0);
|
|
kphArray.resize(0);
|
|
cadArray.resize(0);
|
|
|
|
wattsSelectedArray.resize(0);
|
|
nmSelectedArray.resize(0);
|
|
hrSelectedArray.resize(0);
|
|
kphSelectedArray.resize(0);
|
|
cadSelectedArray.resize(0);
|
|
|
|
// unit conversion factor for imperial units for selected parameters
|
|
double torque_factor = (useMetricUnits ? 1.0 : 0.73756215);
|
|
double speed_factor = (useMetricUnits ? 1.0 : 0.62137119);
|
|
|
|
foreach(const RideFilePoint *p1, ride->dataPoints()) {
|
|
bool selected = isSelected(p1);
|
|
|
|
int wattsIndex = int(floor(p1->watts / wattsDelta));
|
|
if (wattsIndex >= 0 && wattsIndex < maxSize) {
|
|
if (wattsIndex >= wattsArray.size())
|
|
wattsArray.resize(wattsIndex + 1);
|
|
wattsArray[wattsIndex]++;
|
|
|
|
if (selected) {
|
|
if (wattsIndex >= wattsSelectedArray.size())
|
|
wattsSelectedArray.resize(wattsIndex + 1);
|
|
wattsSelectedArray[wattsIndex]++;
|
|
}
|
|
}
|
|
|
|
int nmIndex = int(floor(p1->nm * torque_factor / nmDelta));
|
|
if (nmIndex >= 0 && nmIndex < maxSize) {
|
|
if (nmIndex >= nmArray.size())
|
|
nmArray.resize(nmIndex + 1);
|
|
nmArray[nmIndex]++;
|
|
|
|
if (selected) {
|
|
if (nmIndex >= nmSelectedArray.size())
|
|
nmSelectedArray.resize(nmIndex + 1);
|
|
nmSelectedArray[nmIndex]++;
|
|
}
|
|
}
|
|
|
|
int hrIndex = int(floor(p1->hr / hrDelta));
|
|
if (hrIndex >= 0 && hrIndex < maxSize) {
|
|
if (hrIndex >= hrArray.size())
|
|
hrArray.resize(hrIndex + 1);
|
|
hrArray[hrIndex]++;
|
|
|
|
if (selected) {
|
|
if (hrIndex >= hrSelectedArray.size())
|
|
hrSelectedArray.resize(hrIndex + 1);
|
|
hrSelectedArray[hrIndex]++;
|
|
}
|
|
}
|
|
|
|
int kphIndex = int(floor(p1->kph * speed_factor / kphDelta));
|
|
if (kphIndex >= 0 && kphIndex < maxSize) {
|
|
if (kphIndex >= kphArray.size())
|
|
kphArray.resize(kphIndex + 1);
|
|
kphArray[kphIndex]++;
|
|
|
|
if (selected) {
|
|
if (kphIndex >= kphSelectedArray.size())
|
|
kphSelectedArray.resize(kphIndex + 1);
|
|
kphSelectedArray[kphIndex]++;
|
|
}
|
|
}
|
|
|
|
int cadIndex = int(floor(p1->cad / cadDelta));
|
|
if (cadIndex >= 0 && cadIndex < maxSize) {
|
|
if (cadIndex >= cadArray.size())
|
|
cadArray.resize(cadIndex + 1);
|
|
cadArray[cadIndex]++;
|
|
|
|
if (selected) {
|
|
if (cadIndex >= cadSelectedArray.size())
|
|
cadSelectedArray.resize(cadIndex + 1);
|
|
cadSelectedArray[cadIndex]++;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
recalc();
|
|
}
|
|
else {
|
|
setTitle("no data");
|
|
replot();
|
|
}
|
|
zoomer->setZoomBase();
|
|
}
|
|
|
|
void
|
|
PowerHist::setBinWidth(int value)
|
|
{
|
|
binw = value;
|
|
recalc();
|
|
}
|
|
|
|
double
|
|
PowerHist::getDelta()
|
|
{
|
|
switch (selected) {
|
|
case wattsShaded:
|
|
case wattsUnshaded:
|
|
return wattsDelta;
|
|
case nm:
|
|
return nmDelta;
|
|
case hr:
|
|
return hrDelta;
|
|
case kph:
|
|
return kphDelta;
|
|
case cad:
|
|
return cadDelta;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
PowerHist::getDigits()
|
|
{
|
|
switch (selected) {
|
|
case wattsShaded:
|
|
case wattsUnshaded:
|
|
return wattsDigits;
|
|
case nm:
|
|
return nmDigits;
|
|
case hr:
|
|
return hrDigits;
|
|
case kph:
|
|
return kphDigits;
|
|
case cad:
|
|
return cadDigits;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
PowerHist::setBinWidthRealUnits(double value)
|
|
{
|
|
setBinWidth(round(value / getDelta()));
|
|
return binw;
|
|
}
|
|
|
|
double
|
|
PowerHist::getBinWidthRealUnits()
|
|
{
|
|
return binw * getDelta();
|
|
}
|
|
|
|
void
|
|
PowerHist::setWithZeros(bool value)
|
|
{
|
|
withz = value;
|
|
recalc();
|
|
}
|
|
|
|
void
|
|
PowerHist::setlnY(bool value)
|
|
{
|
|
// note: setAxisScaleEngine deletes the old ScaleEngine, so specifying
|
|
// "new" in the argument list is not a leak
|
|
|
|
lny=value;
|
|
if (lny)
|
|
{
|
|
setAxisScaleEngine(yLeft, new QwtLog10ScaleEngine);
|
|
curve->setBaseline(1e-6);
|
|
}
|
|
else
|
|
{
|
|
setAxisScaleEngine(yLeft, new QwtLinearScaleEngine);
|
|
curve->setBaseline(0);
|
|
}
|
|
setYMax();
|
|
replot();
|
|
}
|
|
|
|
void
|
|
PowerHist::setParameterAxisTitle()
|
|
{
|
|
setAxisTitle(
|
|
xBottom,
|
|
((selected == wattsShaded) ||
|
|
(selected == wattsUnshaded)
|
|
) ?
|
|
"watts" :
|
|
((selected == hr) ?
|
|
"beats/minute" :
|
|
((selected == cad) ?
|
|
"revolutions/min" :
|
|
useMetricUnits ?
|
|
((selected == nm) ?
|
|
"newton-meters" :
|
|
((selected == kph) ?
|
|
"km/hr" :
|
|
"undefined"
|
|
)
|
|
) :
|
|
((selected == nm) ?
|
|
"ft-lb" :
|
|
((selected == kph) ?
|
|
"miles/hr" :
|
|
"undefined"
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
void
|
|
PowerHist::setSelection(Selection selection) {
|
|
if (selected == selection)
|
|
return;
|
|
|
|
selected = selection;
|
|
setParameterAxisTitle();
|
|
recalc();
|
|
}
|
|
|
|
|
|
void
|
|
PowerHist::fixSelection() {
|
|
|
|
Selection s = selected;
|
|
RideFile *ride = rideItem->ride();
|
|
|
|
if (ride)
|
|
do
|
|
{
|
|
if ((s == wattsShaded) || (s == wattsUnshaded))
|
|
{
|
|
if (ride->areDataPresent()->watts)
|
|
setSelection(s);
|
|
else
|
|
s = nm;
|
|
}
|
|
|
|
else if (s == nm)
|
|
{
|
|
if (ride->areDataPresent()->nm)
|
|
setSelection(s);
|
|
else
|
|
s = hr;
|
|
}
|
|
|
|
else if (s == hr)
|
|
{
|
|
if (ride->areDataPresent()->hr)
|
|
setSelection(s);
|
|
else
|
|
s = kph;
|
|
}
|
|
|
|
else if (s == kph)
|
|
{
|
|
if (ride->areDataPresent()->kph)
|
|
setSelection(s);
|
|
else
|
|
s = cad;
|
|
}
|
|
|
|
else if (s == cad)
|
|
{
|
|
if (ride->areDataPresent()->cad)
|
|
setSelection(s);
|
|
else
|
|
s = wattsShaded;
|
|
}
|
|
} while (s != selected);
|
|
}
|
|
|
|
|
|
bool PowerHist::shadeZones() const
|
|
{
|
|
return (
|
|
rideItem &&
|
|
rideItem->ride() &&
|
|
selected == wattsShaded
|
|
);
|
|
}
|
|
|
|
bool PowerHist::isSelected(const RideFilePoint *p) {
|
|
if (mainWindow->allIntervalItems() != NULL) {
|
|
for (int i=0; i<mainWindow->allIntervalItems()->childCount(); i++) {
|
|
IntervalItem *current = dynamic_cast<IntervalItem*>(mainWindow->allIntervalItems()->child(i));
|
|
if (current != NULL) {
|
|
if (current->isSelected() && p->secs>=current->start && p->secs<=current->stop) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|