diff --git a/src/Train/ErgFile.cpp b/src/Train/ErgFile.cpp index fa97a3eb9..d96f7b42e 100644 --- a/src/Train/ErgFile.cpp +++ b/src/Train/ErgFile.cpp @@ -1175,6 +1175,64 @@ ErgFile::Sections() return returning; } + +// convert points to set of sections, every section will be strictly in one zone +QList +ErgFile::ZoneSections() +{ + QList ret; + + const Zones *zones = context->athlete->zones("Bike"); + int zoneRange = zones->whichRange(QDate::currentDate()); + QList zoneNames = zones->getZoneNames(zoneRange); + if (hasWatts() && Duration > 0) { + for (int i = 0; i < Points.size() - 1; ++i) { + const ErgFilePoint &pl = Points[i]; + const ErgFilePoint &pr = Points[i + 1]; + if (long(pl.x) != long(pr.x)) { + int zoneL = 0; + int zoneR = 0; + if (zoneRange >= 0) { + zoneL = zones->whichZone(zoneRange, pl.y); + zoneR = zones->whichZone(zoneRange, pr.y); + } + if (zoneL == zoneR) { + ret << ErgFileZoneSection(pl.x, pl.y, pr.x, pr.y, zoneL); + } else { + double m = (pr.y - pl.y) / (pr.x - pl.x); + int oldZone = 0; + if (zoneRange >= 0) { + oldZone = zones->whichZone(zoneRange, pl.y); + } + double oldTime = pl.x; + double oldWatts = pl.y; + double sectionStartTime = pl.x; + double sectionStartWatts = pl.y; + for (double i = pl.x; i < pr.x; i += 1000) { + double watts = m * (i - pl.x) + pl.y; + int newZone = 0; + if (zoneRange >= 0) { + newZone = zones->whichZone(zoneRange, watts); + } + if (newZone != oldZone) { + ret << ErgFileZoneSection(sectionStartTime, sectionStartWatts, oldTime, oldWatts, oldZone); + sectionStartTime = oldTime; + sectionStartWatts = watts; + oldZone = newZone; + } + oldTime = i; + oldWatts = watts; + } + ret << ErgFileZoneSection(sectionStartTime, sectionStartWatts, pr.x, pr.y, oldZone); + } + } + } + } + + return ret; +} + + bool ErgFile::save(QStringList &errors) { diff --git a/src/Train/ErgFile.h b/src/Train/ErgFile.h index 1393c3429..1770abe3a 100644 --- a/src/Train/ErgFile.h +++ b/src/Train/ErgFile.h @@ -74,6 +74,20 @@ class ErgFileSection double start, end; }; +class ErgFileZoneSection +: public ErgFileSection +{ + public: + ErgFileZoneSection() : ErgFileSection(), startValue(0), endValue(0), zone(0) {} + ErgFileZoneSection(int startMSecs, int startValue, int endMSecs, int endValue, int zone) + : ErgFileSection(endMSecs - startMSecs, startMSecs, endMSecs), startValue(startValue), endValue(endValue), zone(zone) + {} + + int startValue; + int endValue; + int zone; +}; + class ErgFileText { public: @@ -156,6 +170,7 @@ public: // turn the ergfile into a series of sections rather // than a list of points QList Sections(); + QList ZoneSections(); QString Version, // version number / identifer Units, // units used diff --git a/src/Train/ErgFilePlot.cpp b/src/Train/ErgFilePlot.cpp index d2a484c91..a18a302b3 100644 --- a/src/Train/ErgFilePlot.cpp +++ b/src/Train/ErgFilePlot.cpp @@ -21,8 +21,15 @@ #include "Context.h" #include "Units.h" +#include + #include + +static const int sectionAlphaHovered = 128; +static const int sectionAlphaNeutral = 255; + + // Bridge between QwtPlot and ErgFile to avoid having to // create a separate array for the ergfile data, we plot // directly from the ErgFile points array @@ -66,7 +73,7 @@ QRectF ErgFileData::boundingRect() const } // Now bar -double NowData::x(size_t) const { +double NowData::x(size_t) const { if (!bydist || GlobalContext::context()->useMetricUnits) return context->getNow(); else return context->getNow() * MILES_PER_KM; } @@ -92,6 +99,8 @@ QPointF NowData::sample(size_t i) const ErgFilePlot::ErgFilePlot(Context *context) : context(context) { + workoutActive = context->isRunning; + //insertLegend(new QwtLegend(), QwtPlot::BottomLegend); setCanvasBackground(GColor(CTRAINPLOTBACKGROUND)); static_cast(canvas())->setFrameStyle(QFrame::NoFrame); @@ -161,6 +170,7 @@ ErgFilePlot::ErgFilePlot(Context *context) : context(context) LodCurve = new QwtPlotCurve("Course Load"); LodCurve->setSamples(lodData); LodCurve->attach(this); + LodCurve->setVisible(workoutActive); LodCurve->setBaseline(-1000); LodCurve->setYAxis(QwtAxis::YLeft); @@ -254,6 +264,12 @@ ErgFilePlot::ErgFilePlot(Context *context) : context(context) CPMarker->setYValue(274); CPMarker->attach(this); + // Dummy curve for ensuring headroom in Ergmode + powerHeadroom = new QwtPlotCurve("Dummy Headroom"); + powerHeadroom->setYAxis(QwtAxis::YLeft); + powerHeadroom->setPen(QColor(0, 0, 0, 0)); + powerHeadroom->attach(this); + powerHeadroom->setVisible(false); // Now pointer NowCurve = new QwtPlotCurve("Now"); @@ -263,17 +279,30 @@ ErgFilePlot::ErgFilePlot(Context *context) : context(context) NowCurve->attach(this); NowCurve->setYAxis(QwtAxis::YLeft); + tooltip = new penTooltip(static_cast(canvas())); + tooltip->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton, Qt::ShiftModifier); + + picker = new QwtPlotPicker(QwtAxis::XBottom, QwtAxis::YLeft, canvas()); + picker->setTrackerMode(QwtPlotPicker::AlwaysOff); + picker->setStateMachine(new QwtPickerTrackerMachine()); + connect(picker, SIGNAL(moved(const QPoint&)), this, SLOT(hover(const QPoint&))); + connect(context, SIGNAL(start()), this, SLOT(startWorkout())); + connect(context, SIGNAL(stop()), this, SLOT(stopWorkout())); + bydist = false; ergFile = NULL; + selectTooltip(); + setAutoReplot(false); - setData(ergFile); + setData(ergFile); configChanged(CONFIG_ZONES); connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32))); } + void ErgFilePlot::configChanged(qint32) { @@ -339,7 +368,7 @@ public: // // Build mapping from lapRangeId to index of first/last lap markers in the // group. - // + // // Map provides instant access to start and end of rangeid. int lapCount = laps.count(); @@ -417,6 +446,11 @@ void ErgFilePlot::setData(ErgFile *ergfile) { reset(); + powerHeadroom->setVisible(false); + for (int i = 0; i < powerSectionCurves.length(); ++i) { + delete powerSectionCurves[i]; + } + powerSectionCurves.clear(); ergFile = ergfile; // clear the previous marks (if any) @@ -454,8 +488,32 @@ ErgFilePlot::setData(ErgFile *ergfile) LodCurve->setBrush(linearGradient); // fill below the line QPen Lodpen = QPen(Qt::gray, 1.0); LodCurve->setPen(Lodpen); + LodCurve->show(); } else { + QList zoneSections = ergFile->ZoneSections(); + bool antiAlias = appsettings->value(this, GC_ANTIALIAS, false).toBool(); + for (int i = 0; i < zoneSections.length(); ++i) { + QVector sectionData; + sectionData << QPointF(zoneSections[i].start, zoneSections[i].startValue) + << QPointF(zoneSections[i].end, zoneSections[i].endValue); + QColor color = QColor(zoneColor(zoneSections[i].zone, 0)); + color.setAlpha(sectionAlphaNeutral); + QwtPlotCurve *sectionCurve = new QwtPlotCurve("Course Load"); + sectionCurve->setSamples(sectionData); + sectionCurve->setBaseline(-1000); + sectionCurve->setYAxis(QwtAxis::YLeft); + sectionCurve->setZ(-100); + sectionCurve->setPen(QColor(0, 0, 0, 0)); + sectionCurve->setBrush(color); + sectionCurve->setRenderHint(QwtPlotItem::RenderAntialiased, antiAlias); + sectionCurve->attach(this); + sectionCurve->hide(); + powerSectionCurves.append(sectionCurve); + } + selectCurves(); + powerHeadroom->setVisible(true); + powerHeadroom->setSamples(QVector { dynamic_cast*>(lodData)->boundingRect().bottomLeft() }); QColor brush_color1 = QColor(GColor(CTPOWER)); brush_color1.setAlpha(200); @@ -470,8 +528,8 @@ ErgFilePlot::setData(ErgFile *ergfile) LodCurve->setBrush(linearGradient); // fill below the line QPen Lodpen = QPen(GColor(CTPOWER), 1.0); LodCurve->setPen(Lodpen); - } + selectTooltip(); LapRowDistributor lapRowDistributor(ergFile->Laps); @@ -512,7 +570,7 @@ ErgFilePlot::setData(ErgFile *ergfile) // Literal row translation. We loves ascii art... QString prefix = (row > 0) ? QString("\n").repeated(row) : ""; QwtText text(prefix + decoratedName); - + text.setFont(QFont("Helvetica", 10, QFont::Bold)); text.setColor(GColor(CPLOTMARKER)); @@ -524,7 +582,7 @@ ErgFilePlot::setData(ErgFile *ergfile) // convert to imperial according to settings double unitsFactor = (!bydist || GlobalContext::context()->useMetricUnits) ? 1.0 : MILES_PER_KM; add->setValue(lap.x * unitsFactor, 0); - + add->setLabel(text); add->attach(this); @@ -619,6 +677,53 @@ ErgFilePlot::setNow(long /*msecs*/) replot(); // and update } + +bool +ErgFilePlot::eventFilter +(QObject *obj, QEvent *event) +{ + if (obj == canvas() && event->type() == QEvent::Leave) { + highlightSectionCurve(nullptr); + tooltip->setText(""); + } + return false; +} + + +int +ErgFilePlot::showColorZones +() const +{ + return _showColorZones; +} + + +void +ErgFilePlot::setShowColorZones +(int index) +{ + _showColorZones = index; + selectCurves(); +} + + +int +ErgFilePlot::showTooltip +() const +{ + return _showTooltip; +} + + +void +ErgFilePlot::setShowTooltip +(int index) +{ + _showTooltip = index; + selectTooltip(); +} + + void ErgFilePlot::performancePlot(RealtimeData rtdata) { @@ -714,6 +819,221 @@ ErgFilePlot::reset() speedCurve->setSamples(speedData->x(), speedData->y(), speedData->count()); } + +void +ErgFilePlot::hover +(const QPoint &point) +{ + if ( bydist + || _showTooltip == 0 + || ( _showTooltip == 1 + && workoutActive) + || ergFile == nullptr + || ergFile->Duration == 0) { + tooltip->setText(""); + return; + } + double xvalue = invTransform(QwtAxis::XBottom, point.x()); + double yvalue = invTransform(QwtAxis::YLeft, point.y()); + const int fullSecs = std::min(std::max(0, int(xvalue)), int(ergFile->Duration) - 1000) / 1000; + int duration = 0; + int startPower = 0; + int endPower = 0; + QwtPlotCurve *hoverCurve = nullptr; + for (QwtPlotCurve*& curve : powerSectionCurves) { + if (curve->minXValue() <= xvalue && xvalue <= curve->maxXValue()) { + duration = (curve->maxXValue() - curve->minXValue()) / 1000; + startPower = curve->sample(0).y(); + endPower = curve->sample(1).y(); + hoverCurve = curve; + break; + } + } + int watts = startPower; + if (hoverCurve != nullptr && startPower != endPower) { + QPointF pl = hoverCurve->sample(0); + QPointF pr = hoverCurve->sample(1); + watts = (pr.y() - pl.y()) / (pr.x() - pl.x()) * (xvalue - pl.x()) + pl.y(); + } + if (watts == 0 || yvalue > watts) { + highlightSectionCurve(nullptr); + tooltip->setText(""); + return; + } + if (hoverCurve->brush().color().alpha() != sectionAlphaNeutral) { + return; + } + + double sectionStart = hoverCurve->sample(0).x(); + double sectionEnd = hoverCurve->sample(1).x(); + + highlightSectionCurve(hoverCurve); + QString tooltipText; + tooltipText = QString("%1\n%4: ") + .arg(tr("Section of %1 starts at %2") + .arg(secsToString(duration)) + .arg(secsToString(sectionStart / 1000))) + .arg(tr("Power")); + if (startPower == endPower) { + tooltipText = QString("%1%2 %3") + .arg(tooltipText) + .arg(startPower) + .arg(tr("watts")); + } else { + tooltipText = QString("%1%2..%3 %4") + .arg(tooltipText) + .arg(startPower) + .arg(endPower) + .arg(tr("watts")); + } + const Zones *zones = context->athlete->zones("Bike"); + int zoneRange = zones->whichRange(QDate::currentDate()); + if (zoneRange >= 0) { + tooltipText = QString("%1 (%2)") + .arg(tooltipText) + .arg(zones->getZoneNames(zoneRange)[zones->whichZone(zoneRange, startPower)]); + } + if (wbalCurvePredict != nullptr && wbalCurvePredict->dataSize() >= fullSecs) { + int secsStart = std::min(int(sectionStart / 1000), int(wbalCurvePredict->dataSize() - 1)); + int secsEnd = std::min(int(sectionEnd / 1000), int(wbalCurvePredict->dataSize() - 1)); + double wbalIn = wbalCurvePredict->sample(secsStart).y(); + double wbalOut = wbalCurvePredict->sample(secsEnd).y(); + if (int(wbalIn / 100) == int(wbalOut / 100)) { + tooltipText = QString("%1\n%2: %3 %4") + .arg(tooltipText) + .arg(tr("W' Balance")) + .arg(wbalIn / 1000.0 , 0, 'f', 1) + .arg(tr("kJ")); + } else { + tooltipText = QString("%1\n%2: %3..%4 (%5) %6") + .arg(tooltipText) + .arg(tr("W' Balance")) + .arg(wbalIn / 1000.0 , 0, 'f', 1) + .arg(wbalOut / 1000.0 , 0, 'f', 1) + .arg((wbalOut - wbalIn) / 1000.0 , 0, 'f', 1) + .arg(tr("kJ")); + } + } + tooltip->setText(tooltipText); +} + + +void +ErgFilePlot::startWorkout +() +{ + workoutActive = true; + selectTooltip(); + selectCurves(); +} + + +void +ErgFilePlot::stopWorkout +() +{ + workoutActive = false; + selectCurves(); + selectTooltip(); +} + + +void +ErgFilePlot::selectCurves +() +{ + bool showColored = ergFile + && ! bydist + && ( _showColorZones == 1 + || (_showColorZones == 2 && ! workoutActive)); + if (showColored) { + LodCurve->hide(); + for (int i = 0; i < powerSectionCurves.size(); ++i) { + powerSectionCurves[i]->show(); + } + } else { + LodCurve->show(); + for (int i = 0; i < powerSectionCurves.size(); ++i) { + powerSectionCurves[i]->hide(); + } + } + replot(); +} + + +void +ErgFilePlot::selectTooltip +() +{ + if ( ergFile + && ! bydist + && (_showTooltip == 1 && ! workoutActive)) { + installEventFilter(canvas()); + picker->setEnabled(true); + } else { + removeEventFilter(canvas()); + picker->setEnabled(false); + } +} + + +QString +ErgFilePlot::secsToString +(int fullSecs) const +{ + int secs = fullSecs % 60; + int mins = (fullSecs / 60) % 60; + int hours = fullSecs / 3600; + QString time; + if (hours > 0) { + if (secs > 0) { + time = QString("%1h %2m %3s").arg(hours).arg(mins).arg(secs); + } else { + time = QString("%1h %2m").arg(hours).arg(mins); + } + } else if (mins > 0) { + if (secs > 0) { + time = QString("%1m %2s").arg(mins).arg(secs); + } else { + time = QString("%2m").arg(mins); + } + } else { + time = QString("%1s").arg(secs); + } + return time; +} + + +void +ErgFilePlot::highlightSectionCurve +(QwtPlotCurve const * const highlightedCurve) +{ + bool needsReplot = false; + for (QwtPlotCurve*& curve : powerSectionCurves) { + QBrush brush = curve->brush(); + QColor color = brush.color(); + if (curve != highlightedCurve) { + if (color.alpha() != sectionAlphaNeutral) { + color.setAlpha(sectionAlphaNeutral); + brush.setColor(color); + curve->setBrush(brush); + needsReplot = true; + } + } else { + if (color.alpha() == sectionAlphaNeutral) { + color.setAlpha(sectionAlphaHovered); + brush.setColor(color); + curve->setBrush(brush); + needsReplot = true; + } + } + } + if (needsReplot) { + replot(); + } +} + + // curve data.. code snaffled in from the Qwt example (realtime_plot) CurveData::CurveData(): d_count(0) { } diff --git a/src/Train/ErgFilePlot.h b/src/Train/ErgFilePlot.h index 237f527f8..18eedf839 100644 --- a/src/Train/ErgFilePlot.h +++ b/src/Train/ErgFilePlot.h @@ -40,6 +40,7 @@ #include "Settings.h" #include "Colors.h" +#include "PowerHist.h" // for penTooltip #include "RealtimeData.h" #include @@ -50,83 +51,89 @@ class ErgFileData : public QwtPointArrayData { public: - ErgFileData (Context *context) : QwtPointArrayData(QVector(), QVector()), context(context) {} - double x(size_t i) const ; - double y(size_t i) const ; - size_t size() const ; - void setByDist(bool bd) { bydist = bd; }; - bool byDist() const { return bydist; }; + ErgFileData (Context *context) : QwtPointArrayData(QVector(), QVector()), context(context) {} + double x(size_t i) const; + double y(size_t i) const; + size_t size() const; + void setByDist(bool bd) { bydist = bd; }; + bool byDist() const { return bydist; }; private: - Context *context; - bool bydist = false; + Context *context; + bool bydist = false; - virtual QPointF sample(size_t i) const; - virtual QRectF boundingRect() const; + virtual QPointF sample(size_t i) const; + virtual QRectF boundingRect() const; }; + class NowData : public QwtPointArrayData { public: - NowData (Context *context) : QwtPointArrayData(QVector(), QVector()), context(context) {} - double x(size_t i) const ; - double y(size_t i) const ; - size_t size() const ; - void setByDist(bool bd) { bydist = bd; }; - bool byDist() const { return bydist; }; + NowData (Context *context) : QwtPointArrayData(QVector(), QVector()), context(context) {} + double x(size_t i) const; + double y(size_t i) const; + size_t size() const; + void setByDist(bool bd) { bydist = bd; }; + bool byDist() const { return bydist; }; + + void init(); - void init() ; private: - Context *context; - bool bydist = false; + Context *context; + bool bydist = false; - virtual QPointF sample(size_t i) const; - //virtual QRectF boundingRect() const; + virtual QPointF sample(size_t i) const; + //virtual QRectF boundingRect() const; }; + // incremental data, for each curve class CurveData { // A container class for growing data -public: + public: - CurveData(); + CurveData(); - void append(double *x, double *y, int count); - void clear(); + void append(double *x, double *y, int count); + void clear(); - int count() const; - int size() const; - const double *x() const; - const double *y() const; + int count() const; + int size() const; + const double *x() const; + const double *y() const; -private: - int d_count; - QVector d_x; - QVector d_y; + private: + int d_count; + QVector d_x; + QVector d_y; }; + class DistScaleDraw: public QwtScaleDraw { -public: - DistScaleDraw() { } + public: + DistScaleDraw() { } - // we do 100m for <= a kilo - virtual QwtText label(double v) const { if (v<1000) return QString("%1").arg(v/1000, 0, 'g', 1); - else return QString("%1").arg(round(v/1000)); } + // we do 100m for <= a kilo + virtual QwtText label(double v) const { if (v<1000) return QString("%1").arg(v/1000, 0, 'g', 1); + else return QString("%1").arg(round(v/1000)); } }; + + class HourTimeScaleDraw: public QwtScaleDraw { -public: - HourTimeScaleDraw() { } + public: + HourTimeScaleDraw() { } - virtual QwtText label(double v) const { - v /= 1000; - QTime t = QTime(0,0,0,0).addSecs(v); - if (scaleMap().sDist() > 5) - return t.toString("hh:mm"); - return t.toString("hh:mm:ss"); - } + virtual QwtText label(double v) const { + v /= 1000; + QTime t = QTime(0,0,0,0).addSecs(v); + if (scaleMap().sDist() > 5) + return t.toString("hh:mm"); + return t.toString("hh:mm:ss"); + } }; class ErgFilePlot : public QwtPlot @@ -134,64 +141,81 @@ class ErgFilePlot : public QwtPlot Q_OBJECT G_OBJECT - public: + ErgFilePlot(Context *context); - ErgFilePlot(Context *context); - QList Marks; + QList Marks; - void setData(ErgFile *); // set the course - void reset(); // reset counters etc when plot changes - void setNow(long); // set point we're add for progress pointer + void setData(ErgFile *); // set the course + void reset(); // reset counters etc when plot changes + void setNow(long); // set point we're add for progress pointer + + bool eventFilter(QObject *obj, QEvent *event); public slots: + void performancePlot(RealtimeData); + void configChanged(qint32); + void start(); + void hover(const QPoint &point); + void startWorkout(); + void stopWorkout(); + void selectCurves(); + void selectTooltip(); - void performancePlot(RealtimeData); - void configChanged(qint32); - void start(); + int showColorZones() const; + void setShowColorZones(int index); + int showTooltip() const; + void setShowTooltip(int index); private: + WPrime calculator; + Context *context; + bool bydist; + ErgFile *ergFile; + QwtPlotMarker *CPMarker; - WPrime calculator; - Context *context; - bool bydist; - ErgFile *ergFile; - QwtPlotMarker *CPMarker; + int _showColorZones = 0; + int _showTooltip = 0; - QwtPlotGrid *grid; - QwtPlotCurve *LodCurve; - QwtPlotCurve *wbalCurve; - QwtPlotCurve *wbalCurvePredict; - QwtPlotCurve *wattsCurve; - QwtPlotCurve *hrCurve; - QwtPlotCurve *cadCurve; - QwtPlotCurve *speedCurve; - QwtPlotCurve *NowCurve; + QwtPlotGrid *grid; + QList powerSectionCurves; + QwtPlotCurve *LodCurve; + QwtPlotCurve *wbalCurve; + QwtPlotCurve *wbalCurvePredict; + QwtPlotCurve *wattsCurve; + QwtPlotCurve *hrCurve; + QwtPlotCurve *cadCurve; + QwtPlotCurve *speedCurve; + QwtPlotCurve *NowCurve; + QwtPlotCurve *powerHeadroom; - CurveData *wattsData, - *hrData, - *cadData, - *wbalData, - *speedData; + CurveData *wattsData, + *hrData, + *cadData, + *wbalData, + *speedData; - double counter; - double wattssum, - hrsum, - cadsum, - wbalsum, - speedsum; + double counter; + double wattssum, + hrsum, + cadsum, + wbalsum, + speedsum; - ErgFileData *lodData; - NowData *nowData; + ErgFileData *lodData; + NowData *nowData; - DistScaleDraw *distdraw; - HourTimeScaleDraw *timedraw; + DistScaleDraw *distdraw; + HourTimeScaleDraw *timedraw; - ErgFilePlot(); + QwtPlotPicker *picker; + penTooltip *tooltip; + bool workoutActive = false; + void highlightSectionCurve(QwtPlotCurve const * const highlightedCurve); + QString secsToString(int fullSecs) const; }; - #endif diff --git a/src/Train/WorkoutPlotWindow.cpp b/src/Train/WorkoutPlotWindow.cpp index 6fd781f47..3727823e4 100644 --- a/src/Train/WorkoutPlotWindow.cpp +++ b/src/Train/WorkoutPlotWindow.cpp @@ -21,14 +21,45 @@ #include "Context.h" #include "HelpWhatsThis.h" +#include +#include + + WorkoutPlotWindow::WorkoutPlotWindow(Context *context) : GcChartWindow(context), context(context) { HelpWhatsThis *helpContents = new HelpWhatsThis(this); this->setWhatsThis(helpContents->getWhatsThisText(HelpWhatsThis::ChartTrain_Workout)); + // Chart settings + QWidget *settingsWidget = new QWidget(this); + settingsWidget->setContentsMargins(0, 0, 0, 0); + + QVBoxLayout *commonLayout = new QVBoxLayout(settingsWidget); + + ctrlsGroupBox = new QGroupBox(tr("Ergmode specific settings")); + commonLayout->addWidget(ctrlsGroupBox); + commonLayout->addStretch(); + + QFormLayout *ergmodeLayout = new QFormLayout(ctrlsGroupBox); + + ctrlsSituationLabel = new QLabel(tr("Color power zones")); + ctrlsSituation = new QComboBox(); + ctrlsSituation->addItem(tr("Never")); + ctrlsSituation->addItem(tr("Always")); + ctrlsSituation->addItem(tr("When stopped")); + connect(ctrlsSituation, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowColorZones(int))); + ergmodeLayout->addRow(ctrlsSituationLabel, ctrlsSituation); + + ctrlsShowTooltipLabel = new QLabel(tr("Show tooltip")); + ctrlsShowTooltip = new QComboBox(); + ctrlsShowTooltip->addItem(tr("Never")); + ctrlsShowTooltip->addItem(tr("When stopped")); + connect(ctrlsShowTooltip, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowTooltip(int))); + ergmodeLayout->addRow(ctrlsShowTooltipLabel, ctrlsShowTooltip); + setContentsMargins(0,0,0,0); - setControls(NULL); + setControls(settingsWidget); setProperty("color", GColor(CTRAINPLOTBACKGROUND)); QVBoxLayout *layout = new QVBoxLayout; @@ -69,5 +100,50 @@ void WorkoutPlotWindow::configChanged(qint32) { setProperty("color", GColor(CTRAINPLOTBACKGROUND)); + + ctrlsGroupBox->setTitle(tr("Ergmode specific settings")); + ctrlsSituationLabel->setText(tr("Color power zones")); + ctrlsSituation->setItemText(0, tr("Never")); + ctrlsSituation->setItemText(1, tr("Always")); + ctrlsSituation->setItemText(2, tr("When stopped")); + + ctrlsShowTooltipLabel->setText(tr("Show tooltip")); + ctrlsShowTooltip->setItemText(0, tr("Never")); + ctrlsShowTooltip->setItemText(1, tr("When stopped")); + repaint(); } + + +int +WorkoutPlotWindow::showColorZones +() const +{ + return ctrlsSituation->currentIndex(); +} + + +void +WorkoutPlotWindow::setShowColorZones +(int index) +{ + ctrlsSituation->setCurrentIndex(index); + ergPlot->setShowColorZones(index); +} + + +int +WorkoutPlotWindow::showTooltip +() const +{ + return ctrlsShowTooltip->currentIndex(); +} + + +void +WorkoutPlotWindow::setShowTooltip +(int index) +{ + ctrlsShowTooltip->setCurrentIndex(index); + ergPlot->setShowTooltip(index); +} diff --git a/src/Train/WorkoutPlotWindow.h b/src/Train/WorkoutPlotWindow.h index 6280d3d79..0652b091f 100644 --- a/src/Train/WorkoutPlotWindow.h +++ b/src/Train/WorkoutPlotWindow.h @@ -32,26 +32,44 @@ #include "Settings.h" #include "Colors.h" +#include +#include +#include + class WorkoutPlotWindow : public GcChartWindow { Q_OBJECT G_OBJECT + Q_PROPERTY(int showColorZones READ showColorZones WRITE setShowColorZones USER true) + Q_PROPERTY(int showTooltip READ showTooltip WRITE setShowTooltip USER true) + public: WorkoutPlotWindow(Context *context); - public slots: + public slots: // trap signals void setNow(long now); void ergFileSelected(ErgFile *); void configChanged(qint32); + int showColorZones() const; + void setShowColorZones(int index); + int showTooltip() const; + void setShowTooltip(int index); + private: Context *context; ErgFilePlot *ergPlot; + + QGroupBox *ctrlsGroupBox; + QLabel *ctrlsSituationLabel; + QComboBox *ctrlsSituation; + QLabel *ctrlsShowTooltipLabel; + QComboBox *ctrlsShowTooltip; }; #endif // _GC_WorkoutPlotWindow_h