From 100c0be8817948031dfc299a9b8f4eecf58fff5e Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Sat, 9 May 2015 18:49:49 +0100 Subject: [PATCH] Auto Interval Discovery (Part 2 of 3) In this part we have updated all the charts to reference the RideItem::intervals() members instead of the TreeWidget and RideFile::intervals(). The code to create/change/delete intervals is not included so selecting and editing on charts/sidebar is disabled til part 3 of the update, but hover should work properly. Still left todo in future updates; * Updates to the interval sidebar to list intervals in a tree (by interval type) with a color selector * Code to create, edit, delete etc the intervals via the rideitem/intervalitem and see them reflected in the ridefile * Update to search for all the different types of IntervalItems including routes and sustained intervals --- src/AddIntervalDialog.cpp | 4 +- src/Aerolab.cpp | 51 +++------------ src/AllPlot.cpp | 115 ++++++++++++++------------------- src/AllPlot.h | 4 +- src/AllPlotInterval.cpp | 90 ++++++++++++-------------- src/AllPlotInterval.h | 10 +-- src/AllPlotWindow.cpp | 8 ++- src/AnalysisSidebar.cpp | 1 + src/Athlete.cpp | 2 +- src/Athlete.h | 20 ++++-- src/BestIntervalDialog.cpp | 3 + src/BingMap.cpp | 70 ++++++++------------ src/Colors.h | 1 + src/ComparePane.cpp | 5 ++ src/Context.h | 4 +- src/CriticalPowerWindow.cpp | 42 +++++------- src/CriticalPowerWindow.h | 3 +- src/GcWindowRegistry.cpp | 7 +- src/GoogleMapControl.cpp | 93 +++++++++++---------------- src/HistogramWindow.cpp | 2 +- src/IntervalItem.cpp | 45 ++++++++++++- src/IntervalItem.h | 18 ++++-- src/IntervalSummaryWindow.cpp | 117 ++++++++++------------------------ src/IntervalSummaryWindow.h | 8 +-- src/IntervalTreeView.cpp | 2 +- src/PfPvPlot.cpp | 58 +++++++---------- src/PfPvPlot.h | 2 +- src/PfPvWindow.cpp | 4 +- src/PfPvWindow.h | 2 +- src/PowerHist.cpp | 28 ++++---- src/PowerHist.h | 5 +- src/RideDB.y | 2 +- src/RideEditor.cpp | 25 +++----- src/RideFile.h | 35 ++++++++-- src/RideItem.cpp | 20 +++++- src/RideItem.h | 1 + src/RideSummaryWindow.cpp | 84 ++++-------------------- src/ScatterPlot.cpp | 32 +++++----- src/ScatterPlot.h | 3 +- src/ScatterWindow.cpp | 9 +-- src/Tab.cpp | 2 + src/src.pro | 6 +- 42 files changed, 456 insertions(+), 587 deletions(-) diff --git a/src/AddIntervalDialog.cpp b/src/AddIntervalDialog.cpp index 88eb96803..41fba869a 100644 --- a/src/AddIntervalDialog.cpp +++ b/src/AddIntervalDialog.cpp @@ -859,6 +859,8 @@ AddIntervalDialog::addClicked() // get stop in secs as a string from column 4 for (int i=0; irowCount(); i++) { +//XXX REFACTOR NEED TO DECIDE HOW TO DO THIS!! +#if 0 // is it checked? QCheckBox *c = (QCheckBox *)resultsTable->cellWidget(i,0); if (c->isChecked()) { @@ -866,7 +868,6 @@ AddIntervalDialog::addClicked() double stop = resultsTable->item(i,4)->text().toDouble(); QString name = resultsTable->item(i,2)->text(); const RideFile *ride = context->ride ? context->ride->ride() : NULL; - QTreeWidgetItem *allIntervals = context->athlete->mutableIntervalItems(); QTreeWidgetItem *last = new IntervalItem(ride, name, start, stop, @@ -878,6 +879,7 @@ AddIntervalDialog::addClicked() // add allIntervals->addChild(last); } +#endif } context->athlete->updateRideFileIntervals(); done(0); diff --git a/src/Aerolab.cpp b/src/Aerolab.cpp index 947561d15..887d5b2be 100644 --- a/src/Aerolab.cpp +++ b/src/Aerolab.cpp @@ -89,26 +89,8 @@ IntervalItem *IntervalAerolabData::intervalNum int number ) const { - int highlighted = 0; - const QTreeWidgetItem *allIntervals = context->athlete->allIntervalItems(); - for ( int ii = 0; ii < allIntervals->childCount(); ii++) - - { - IntervalItem *current = (IntervalItem *) allIntervals->child( ii ); - - if ( current == NULL) - { - return NULL; - } - if ( current->isSelected() == true ) - { - ++highlighted; - } - if ( highlighted == number ) - { - return current; - } - } + if (number >= 0 && aerolab->rideItem->intervalsSelected().count() > number) + return aerolab->rideItem->intervalsSelected().at(number); return NULL; } @@ -118,24 +100,7 @@ IntervalItem *IntervalAerolabData::intervalNum // ------------------------------------------------------------------------------------------------------------ int IntervalAerolabData::intervalCount() const { - int highlighted = 0; - - if ( context->athlete->allIntervalItems() != NULL ) - { - const QTreeWidgetItem *allIntervals = context->athlete->allIntervalItems(); - for ( int ii = 0; ii < allIntervals->childCount(); ii++) - { - IntervalItem *current = (IntervalItem *) allIntervals->child( ii ); - if ( current != NULL ) - { - if ( current->isSelected() == true ) - { - ++highlighted; - } - } - } - } - return highlighted; + return aerolab->rideItem->intervalsSelected().count(); } /* * INTERVAL HIGHLIGHTING CURVE @@ -761,9 +726,9 @@ void Aerolab::refreshIntervalMarkers() QRegExp wkoAuto("^(Peak *[0-9]*(s|min)|Entire workout|Find #[0-9]*) *\\([^)]*\\)$"); if ( rideItem->ride() ) { - foreach(const RideFileInterval &interval, rideItem->ride()->intervals()) { + foreach(IntervalItem *interval, rideItem->intervals()) { // skip WKO autogenerated peak intervals - if (wkoAuto.exactMatch(interval.name)) + if (wkoAuto.exactMatch(interval->name)) continue; QwtPlotMarker *mrk = new QwtPlotMarker; d_mrk.append(mrk); @@ -771,14 +736,14 @@ void Aerolab::refreshIntervalMarkers() mrk->setLineStyle(QwtPlotMarker::VLine); mrk->setLabelAlignment(Qt::AlignRight | Qt::AlignTop); mrk->setLinePen(QPen(GColor(CPLOTMARKER), 0, Qt::DashDotLine)); - QwtText text(interval.name); + QwtText text(interval->name); text.setFont(QFont("Helvetica", 10, QFont::Bold)); text.setColor(GColor(CPLOTMARKER)); if (!bydist) - mrk->setValue(interval.start / 60.0, 0.0); + mrk->setValue(interval->start / 60.0, 0.0); else mrk->setValue((context->athlete->useMetricUnits ? 1 : MILES_PER_KM) * - rideItem->ride()->timeToDistance(interval.start), 0.0); + interval->startKM, 0.0); mrk->setLabel(text); } } diff --git a/src/AllPlot.cpp b/src/AllPlot.cpp index d24ad57bb..2483334e1 100644 --- a/src/AllPlot.cpp +++ b/src/AllPlot.cpp @@ -53,8 +53,8 @@ class IntervalPlotData : public QwtSeriesData { public: - IntervalPlotData(AllPlot *allPlot, Context *context) : - allPlot(allPlot), context(context) {} + IntervalPlotData(AllPlot *allPlot, Context *context, AllPlotWindow *window) : + allPlot(allPlot), context(context), window(window) {} double x(size_t i) const ; double y(size_t i) const ; size_t size() const ; @@ -64,6 +64,7 @@ class IntervalPlotData : public QwtSeriesData int intervalCount() const; AllPlot *allPlot; Context *context; + AllPlotWindow *window; virtual QPointF sample(size_t i) const; virtual QRectF boundingRect() const; @@ -824,7 +825,7 @@ AllPlot::AllPlot(QWidget *parent, AllPlotWindow *window, Context *context, RideF standard = new AllPlotObject(this); - standard->intervalHighlighterCurve->setSamples(new IntervalPlotData(this, context)); + standard->intervalHighlighterCurve->setSamples(new IntervalPlotData(this, context, window)); setAxisMaxMinor(xBottom, 0); enableAxis(xBottom, true); @@ -2308,16 +2309,12 @@ AllPlot::refreshIntervalMarkers() } standard->d_mrk.clear(); if (rideItem && rideItem->ride()) { - foreach(const RideFileInterval &interval, rideItem->ride()->intervals()) { + foreach(IntervalItem *interval, rideItem->intervals()) { bool nolabel = false; - // ignore climbs, its just for marking - if (interval.isClimb()) continue; - // no label, but do add - if (interval.isBest() || interval.isPeak() || - interval.isMatch()) nolabel = true; + if (interval->type != RideFileInterval::USER) nolabel = true; QwtPlotMarker *mrk = new QwtIndPlotMarker; standard->d_mrk.append(mrk); @@ -2329,24 +2326,24 @@ AllPlot::refreshIntervalMarkers() else mrk->setLinePen(QPen(GColor(CPLOTMARKER), 0, Qt::DashLine)); // put matches on second line down - QString name(interval.name); - if (interval.name.startsWith(tr("Match"))) name = QString("\n%1").arg(interval.name); + QString name(interval->name); + if (interval->name.startsWith(tr("Match"))) name = QString("\n%1").arg(interval->name); QwtText text(wanttext && !nolabel ? name : ""); if (!nolabel) { text.setFont(QFont("Helvetica", 10, QFont::Bold)); - if (interval.name.startsWith(tr("Match"))) + if (interval->name.startsWith(tr("Match"))) text.setColor(GColor(CWBAL)); else text.setColor(GColor(CPLOTMARKER)); } if (!bydist) - mrk->setValue(interval.start / 60.0, 0.0); + mrk->setValue(interval->start / 60.0, 0.0); else mrk->setValue((context->athlete->useMetricUnits ? 1 : MILES_PER_KM) * - rideItem->ride()->timeToDistance(interval.start), 0.0); + interval->startKM, 0.0); mrk->setLabel(text); } } @@ -6321,17 +6318,13 @@ AllPlot::distanceIndex(double km) const // selectedItems() member. N starts a one not zero. IntervalItem *IntervalPlotData::intervalNum(int n) const { - int highlighted=0; - const QTreeWidgetItem *allIntervals = context->athlete->allIntervalItems(); - for (int i=0; ichildCount(); i++) { - IntervalItem *current = (IntervalItem *)allIntervals->child(i); + RideItem *rideItem = window->current; + if (!rideItem) return NULL; - if (current != NULL) { - if (current->isSelected() == true) ++highlighted; - } else { - return NULL; - } - if (highlighted == n) return current; + int highlighted=0; + foreach(IntervalItem *p, rideItem->intervals()) { + if (p->selected) highlighted++; + if (highlighted == n) return p; } return NULL; } @@ -6339,19 +6332,13 @@ IntervalItem *IntervalPlotData::intervalNum(int n) const // how many intervals selected? int IntervalPlotData::intervalCount() const { - int highlighted; - highlighted = 0; - if (context->athlete->allIntervalItems() == NULL) return 0; // not inited yet! + RideItem *rideItem = window->current; + if (!rideItem) return 0; - const QTreeWidgetItem *allIntervals = context->athlete->allIntervalItems(); - for (int i=0; ichildCount(); i++) { - IntervalItem *current = (IntervalItem *)allIntervals->child(i); - if (current != NULL) { - if (current->isSelected() == true) { - ++highlighted; - } - } - } + int highlighted=0; + foreach(IntervalItem *p, rideItem->intervals()) + if (p->selected) highlighted++; + return highlighted; } @@ -6514,7 +6501,7 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) } // we don't want hoveing or we have intervals selected so no need to mouse over - if (!window->showHover->isChecked() || context->athlete->intervalWidget->selectedItems().count()) return; + if (!window->showHover->isChecked() || rideItem->intervalsSelected().count()) return; if (!context->isCompareIntervals && rideItem && rideItem->ride()) { @@ -6522,7 +6509,7 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) if (bydist) X = rideItem->ride()->distanceToTime(X) / 60.00f; QVectorxdata, ydata; - RideFileInterval chosen; + IntervalItem *chosen = NULL; if (rideItem->ride()->dataPoints().count() > 1) { @@ -6533,26 +6520,22 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) int duration = rideduration; // loop through intervals and select FIRST we are in - foreach(RideFileInterval i, rideItem->ride()->intervals()) { - if (i.start < (X*60.00f) && i.stop > (X*60.00f)) { - if ((i.stop-i.start) < duration) { - duration = i.stop - i.start; + foreach(IntervalItem *i, rideItem->intervals()) { + if (i->start < (X*60.00f) && i->stop > (X*60.00f)) { + if ((i->stop-i->start) < duration) { + duration = i->stop - i->start; chosen = i; } } } // we already chose it! - if (chosen == hovered) return; + if (chosen == NULL || chosen == hovered) return; if (duration < rideduration) { // hover curve color aligns to the type of interval we are highlighting - QColor hbrush = GColor(CINTERVALHIGHLIGHTER); // for user defined - if (chosen.isPeak()) hbrush = QColor(Qt::lightGray); - if (chosen.isMatch()) hbrush = QColor(Qt::red); - if (chosen.isClimb()) hbrush = QColor(Qt::darkGreen); - if (chosen.isBest()) hbrush = QColor(Qt::darkYellow); + QColor hbrush = chosen->color; hbrush.setAlpha(64); standard->intervalHoverCurve->setBrush(hbrush); // fill below the line @@ -6560,8 +6543,8 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) if (bydist) { double multiplier = context->athlete->useMetricUnits ? 1 : MILES_PER_KM; - double start = multiplier * rideItem->ride()->timeToDistance(chosen.start); - double stop = multiplier * rideItem->ride()->timeToDistance(chosen.stop); + double start = multiplier * chosen->startKM; + double stop = multiplier * chosen->stopKM; xdata << start; ydata << -20; @@ -6574,13 +6557,13 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) } else { - xdata << chosen.start / 60.00f; + xdata << chosen->start / 60.00f; ydata << -20; - xdata << chosen.start / 60.00f; + xdata << chosen->start / 60.00f; ydata << 100; - xdata << chosen.stop / 60.00f; + xdata << chosen->stop / 60.00f; ydata << 100; - xdata << chosen.stop / 60.00f; + xdata << chosen->stop / 60.00f; ydata << -20; } } @@ -6601,27 +6584,23 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) } void -AllPlot::intervalHover(RideFileInterval chosen) +AllPlot::intervalHover(IntervalItem *chosen) { // no point! if (!isVisible() || chosen == hovered) return; QVectorxdata, ydata; - if (chosen != RideFileInterval()) { + if (chosen) { // hover curve color aligns to the type of interval we are highlighting - QColor hbrush = GColor(CINTERVALHIGHLIGHTER); // for user defined - if (chosen.isPeak()) hbrush = QColor(Qt::lightGray); - if (chosen.isMatch()) hbrush = QColor(Qt::red); - if (chosen.isClimb()) hbrush = QColor(Qt::darkGreen); - if (chosen.isBest()) hbrush = QColor(Qt::darkYellow); + QColor hbrush = chosen->color; hbrush.setAlpha(64); standard->intervalHoverCurve->setBrush(hbrush); // fill below the line if (bydist) { double multiplier = context->athlete->useMetricUnits ? 1 : MILES_PER_KM; - double start = multiplier * rideItem->ride()->timeToDistance(chosen.start); - double stop = multiplier * rideItem->ride()->timeToDistance(chosen.stop); + double start = multiplier * chosen->startKM; + double stop = multiplier * chosen->stopKM; xdata << start; ydata << -20; @@ -6632,13 +6611,13 @@ AllPlot::intervalHover(RideFileInterval chosen) xdata << stop; ydata << -20; } else { - xdata << chosen.start / 60.00f; + xdata << chosen->start / 60.00f; ydata << -20; - xdata << chosen.start / 60.00f; + xdata << chosen->start / 60.00f; ydata << 100; - xdata << chosen.stop / 60.00f; + xdata << chosen->stop / 60.00f; ydata << 100; - xdata << chosen.stop / 60.00f; + xdata << chosen->stop / 60.00f; ydata << -20; } } @@ -6773,7 +6752,7 @@ AllPlot::eventFilter(QObject *obj, QEvent *event) } // turn off hover when mouse leaves - if (event->type() == QEvent::Leave) context->notifyIntervalHover(RideFileInterval()); + if (event->type() == QEvent::Leave) context->notifyIntervalHover(NULL); return false; } diff --git a/src/AllPlot.h b/src/AllPlot.h index aedf9c540..2f74cf49f 100644 --- a/src/AllPlot.h +++ b/src/AllPlot.h @@ -614,7 +614,7 @@ class AllPlot : public QwtPlot // for tooltip void pointHover(QwtPlotCurve*, int); - void intervalHover(RideFileInterval h); + void intervalHover(IntervalItem *h); protected: @@ -628,7 +628,7 @@ class AllPlot : public QwtPlot RideItem *rideItem; AllPlotBackground *bg; QSettings *settings; - RideFileInterval hovered; + IntervalItem *hovered; // controls bool shade_zones; diff --git a/src/AllPlotInterval.cpp b/src/AllPlotInterval.cpp index d39cc7cc8..ce54e2285 100644 --- a/src/AllPlotInterval.cpp +++ b/src/AllPlotInterval.cpp @@ -35,7 +35,7 @@ class AllPlotIntervalData : public QwtArraySeriesData { public: - AllPlotIntervalData(AllPlotInterval *plot, Context *context, int level, int max, RideItem *rideItem, const RideFileInterval interval) : + AllPlotIntervalData(AllPlotInterval *plot, Context *context, int level, int max, RideItem *rideItem, const IntervalItem *interval) : plot(plot), context(context), level(level), max(max), rideItem(rideItem), interval(interval) {} double x(size_t i) const ; @@ -54,7 +54,7 @@ class AllPlotIntervalData : public QwtArraySeriesData int max; RideItem *rideItem; - const RideFileInterval interval; + const IntervalItem *interval; virtual QwtIntervalSample sample(size_t i) const; virtual QRectF boundingRect() const; @@ -110,7 +110,7 @@ AllPlotInterval::AllPlotInterval(QWidget *parent, Context *context): canvasPicker = new AllPlotIntervalCanvasPicker(this); - connect(context, SIGNAL(intervalHover(RideFileInterval)), this, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), this, SLOT(intervalHover(IntervalItem*))); connect(canvasPicker, SIGNAL(pointHover(QwtPlotIntervalCurve*, int)), this, SLOT(intervalCurveHover(QwtPlotIntervalCurve*))); connect(canvasPicker, SIGNAL(pointClicked(QwtPlotIntervalCurve*,int)), this, SLOT(intervalCurveClick(QwtPlotIntervalCurve*))); connect(canvasPicker, SIGNAL(pointDblClicked(QwtPlotIntervalCurve*,int)), this, SLOT(intervalCurveDblClick(QwtPlotIntervalCurve*))); @@ -159,23 +159,23 @@ AllPlotInterval::refreshIntervals() } // Compare two RideFileInterval on duration. -bool intervalBiggerThan(const RideFileInterval &i1, const RideFileInterval &i2) +bool intervalBiggerThan(const IntervalItem* i1, const IntervalItem* i2) { - return (i1.stop-i1.start) > (i2.stop-i2.start); + return (i1->stop-i1->start) > (i2->stop-i2->start); } void -AllPlotInterval::sortIntervals(QList &intervals, QList< QList > &intervalsGroups) +AllPlotInterval::sortIntervals(QList &intervals, QList< QList > &intervalsGroups) { // Sort by duration qSort(intervals.begin(), intervals.end(), intervalBiggerThan); - QList matchesGroup; + QList matchesGroup; for (int i=0; itype == RideFileInterval::MATCH) { matchesGroup.append(interval); intervals.removeOne(interval); //intervals.move(i, place++); @@ -190,8 +190,8 @@ AllPlotInterval::sortIntervals(QList &intervals, QList< QList< void AllPlotInterval::placeIntervals() { - QList intervals = rideItem->ride()->intervals(); - QList< QList > intervalsGroups; + QList intervals = rideItem->intervals(); + QList< QList > intervalsGroups; sortIntervals(intervals, intervalsGroups); @@ -200,12 +200,12 @@ AllPlotInterval::placeIntervals() if (intervalsGroups.count()>0) intervalLigns.append(intervalsGroups.at(0)); else { - QList intervalsLign1; + QList intervalsLign1; intervalLigns.append(intervalsLign1); } while (intervals.count()>0) { - const RideFileInterval &interval = intervals.first(); + IntervalItem *interval = intervals.first(); int lign = 0; bool placed = false; @@ -218,8 +218,8 @@ AllPlotInterval::placeIntervals() placed = true; }*/ - foreach(const RideFileInterval &placedinterval, intervalLigns.at(lign)) { - if (interval.stop>placedinterval.start && interval.startstop>placedinterval->start && interval->startstop) place = false; } if (place) { @@ -229,7 +229,7 @@ AllPlotInterval::placeIntervals() } else { lign++; if (intervalLigns.count()<=lign) { - QList intervalsLign; + QList intervalsLign; intervalLigns.append(intervalsLign); } } @@ -241,21 +241,9 @@ AllPlotInterval::placeIntervals() } void -AllPlotInterval::setColorForIntervalCurve(QwtPlotIntervalCurve *intervalCurve, const RideFileInterval &interval, bool selected) +AllPlotInterval::setColorForIntervalCurve(QwtPlotIntervalCurve *intervalCurve, const IntervalItem *interval, bool selected) { - QColor color; - if (interval.isPeak()) { - color = Qt::lightGray; - } else if (interval.isMatch()) { - color = Qt::red; - } else if (interval.isClimb()) { - color = Qt::darkGreen; - } else if (interval.isBest()) { - color = Qt::darkYellow; - } else { - color = GColor(CINTERVALHIGHLIGHTER); - } - + QColor color = interval->color; QPen ihlPen = QPen(color); intervalCurve->setPen(ihlPen); QColor ihlbrush = QColor(color); @@ -275,9 +263,9 @@ AllPlotInterval::refreshIntervalCurve() curves.clear(); int level=0; - foreach(const QList &intervalsLign, intervalLigns) { + foreach(const QList &intervalsLign, intervalLigns) { - foreach(const RideFileInterval &interval, intervalsLign) { + foreach(IntervalItem *interval, intervalsLign) { QwtPlotIntervalCurve *intervalCurve = new QwtPlotIntervalCurve(); intervalCurve->setYAxis(QwtAxis::yLeft); @@ -294,13 +282,10 @@ AllPlotInterval::refreshIntervalCurve() } void -AllPlotInterval::intervalHover(RideFileInterval chosen) +AllPlotInterval::intervalHover(IntervalItem *chosen) { - foreach(RideFileInterval interval, curves.keys()) { - if (chosen == interval || - (rideItem->ride()->intervals().indexOf(interval) > -1 && - context->athlete->allIntervalItems()->child(rideItem->ride()->intervals().indexOf(interval)) != NULL && - context->athlete->allIntervalItems()->child(rideItem->ride()->intervals().indexOf(interval))->isSelected() )) { + foreach(IntervalItem *interval, curves.keys()) { + if (chosen == interval) { setColorForIntervalCurve(curves.value(interval), interval, true); } else { setColorForIntervalCurve(curves.value(interval), interval, false); @@ -313,27 +298,29 @@ void AllPlotInterval::intervalCurveHover(QwtPlotIntervalCurve *curve) { if (curve != NULL) { - RideFileInterval interval = curves.key(curve); + IntervalItem *interval = curves.key(curve); //intervalHover(interval); // tell the charts -- and block signals whilst they occur - tooltip->setText(interval.name); + tooltip->setText(interval->name); blockSignals(true); context->notifyIntervalHover(interval); blockSignals(false); } else { - context->notifyIntervalHover(RideFileInterval()); // clear + context->notifyIntervalHover(NULL); // clear tooltip->setText(""); } } void AllPlotInterval::intervalCurveClick(QwtPlotIntervalCurve *curve) { - RideFileInterval interval = curves.key(curve); - int idx = rideItem->ride()->intervals().indexOf(interval); + IntervalItem *interval = curves.key(curve); + int idx = rideItem->intervals().indexOf(interval); +//XXX REFACTOR HOW TO SELECT/DESELECT AN INTERVAL +#if 0 if (idx != -1) { context->athlete->allIntervalItems()->child(idx)->setSelected(!context->athlete->allIntervalItems()->child(idx)->isSelected()); @@ -345,18 +332,21 @@ AllPlotInterval::intervalCurveClick(QwtPlotIntervalCurve *curve) { } } } +#endif } void AllPlotInterval::intervalCurveDblClick(QwtPlotIntervalCurve *curve) { - RideFileInterval interval = curves.key(curve); - int idx = rideItem->ride()->intervals().indexOf(interval); - + IntervalItem *interval = curves.key(curve); + int idx = rideItem->intervals().indexOf(interval); +//XXX REFACTOR HOW TO SELECT/DESELECT AN INTERVAL +#if 0 if (idx != -1) { context->athlete->allIntervalItems()->child(idx)->setSelected(!context->athlete->allIntervalItems()->child(idx)->isSelected()); context->notifyIntervalZoom((IntervalItem *)context->athlete->allIntervalItems()->child(idx)); } +#endif } @@ -374,10 +364,10 @@ AllPlotIntervalData::x(size_t i) const // which point are we returning? switch (i%4) { - case 0 : return plot->bydist ? multiplier * rideItem->ride()->timeToDistance(interval.start) : interval.start/60; // bottom left - case 1 : return plot->bydist ? multiplier * rideItem->ride()->timeToDistance(interval.start) : interval.start/60; // top left - case 2 : return plot->bydist ? multiplier * rideItem->ride()->timeToDistance(interval.stop) : interval.stop/60; // top right - case 3 : return plot->bydist ? multiplier * rideItem->ride()->timeToDistance(interval.stop) : interval.stop/60; // bottom right + case 0 : return plot->bydist ? (multiplier * interval->startKM) : interval->start/60; // bottom left + case 1 : return plot->bydist ? (multiplier * interval->startKM) : interval->start/60; // top left + case 2 : return plot->bydist ? (multiplier * interval->stopKM) : interval->stop/60; // top right + case 3 : return plot->bydist ? (multiplier * interval->stopKM) : interval->stop/60; // bottom right } return 0; // shouldn't get here, but keeps compiler happy } diff --git a/src/AllPlotInterval.h b/src/AllPlotInterval.h index f4bc4e679..3b937202d 100644 --- a/src/AllPlotInterval.h +++ b/src/AllPlotInterval.h @@ -68,7 +68,7 @@ class AllPlotInterval : public QwtPlot public slots: - void intervalHover(RideFileInterval chosen); + void intervalHover(IntervalItem *chosen); void intervalCurveHover(QwtPlotIntervalCurve *); // for tooltip void intervalCurveClick(QwtPlotIntervalCurve *curve); void intervalCurveDblClick(QwtPlotIntervalCurve *curve); @@ -76,21 +76,21 @@ class AllPlotInterval : public QwtPlot void configChanged(qint32); // when colors change protected: - void sortIntervals(QList &intervals, QList > &intervalsGroups); + void sortIntervals(QList &intervals, QList > &intervalsGroups); void placeIntervals(); void refreshIntervalCurve(); Context *context; RideItem *rideItem; - QList< QList > intervalLigns; + QList< QList > intervalLigns; QVector markers; //QVector curves; - QMap curves; + QMap curves; private: - void setColorForIntervalCurve(QwtPlotIntervalCurve *intervalCurve, const RideFileInterval &interval, bool selected); + void setColorForIntervalCurve(QwtPlotIntervalCurve *intervalCurve, const IntervalItem *interval, bool selected); AllPlotIntervalCanvasPicker *canvasPicker; // allow point selection/hover LTMToolTip *tooltip; diff --git a/src/AllPlotWindow.cpp b/src/AllPlotWindow.cpp index 39e84f110..0c1875ad0 100644 --- a/src/AllPlotWindow.cpp +++ b/src/AllPlotWindow.cpp @@ -442,7 +442,7 @@ AllPlotWindow::AllPlotWindow(Context *context) : allPlot->_canvasPicker = new LTMCanvasPicker(allPlot); - connect(context, SIGNAL(intervalHover(RideFileInterval)), allPlot, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), allPlot, SLOT(intervalHover(IntervalItem*))); connect(allPlot->_canvasPicker, SIGNAL(pointHover(QwtPlotCurve*, int)), allPlot, SLOT(pointHover(QwtPlotCurve*, int))); connect(allPlot->tooltip, SIGNAL(moved(const QPoint &)), this, SLOT(plotPickerMoved(const QPoint &))); connect(allPlot->tooltip, SIGNAL(appended(const QPoint &)), this, SLOT(plotPickerSelected(const QPoint &))); @@ -2048,7 +2048,8 @@ AllPlotWindow::setEndSelection(AllPlot* plot, double xValue, bool newInterval, Q allMarker1->setLabel(QString("")); } - +//XXX REFACTOR WHEN DECIDED HOW TO ADD A NEW INTERVAL +#if 0 if (newInterval) { QTreeWidgetItem *allIntervals = context->athlete->mutableIntervalItems(); @@ -2075,6 +2076,7 @@ AllPlotWindow::setEndSelection(AllPlot* plot, double xValue, bool newInterval, Q // now update the RideFileIntervals and all the plots etc context->athlete->updateRideFileIntervals(); } +#endif } active = false; } @@ -3565,7 +3567,7 @@ AllPlotWindow::addPickers(AllPlot *_allPlot) _allPlot->tooltip->setEnabled(true); _allPlot->_canvasPicker = new LTMCanvasPicker(_allPlot); - connect(context, SIGNAL(intervalHover(RideFileInterval)), _allPlot, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), _allPlot, SLOT(intervalHover(IntervalItem*))); connect(_allPlot->_canvasPicker, SIGNAL(pointHover(QwtPlotCurve*, int)), _allPlot, SLOT(pointHover(QwtPlotCurve*, int))); connect(_allPlot->tooltip, SIGNAL(moved(const QPoint &)), this, SLOT(plotPickerMoved(const QPoint &))); connect(_allPlot->tooltip, SIGNAL(appended(const QPoint &)), this, SLOT(plotPickerSelected(const QPoint &))); diff --git a/src/AnalysisSidebar.cpp b/src/AnalysisSidebar.cpp index 19ee3e420..cd5d6a973 100644 --- a/src/AnalysisSidebar.cpp +++ b/src/AnalysisSidebar.cpp @@ -404,6 +404,7 @@ AnalysisSidebar::addIntervalForPowerPeaksForSecs(RideFile *ride, int windowSizeS ride->timeToDistance(i.start), ride->timeToDistance(i.stop), context->athlete->allIntervals->childCount()+1, + QColor(0,0,0), RideFileInterval::PEAKPOWER); peak->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled); context->athlete->allIntervals->addChild(peak); diff --git a/src/Athlete.cpp b/src/Athlete.cpp index e595365c6..056c078e7 100644 --- a/src/Athlete.cpp +++ b/src/Athlete.cpp @@ -253,7 +253,7 @@ void Athlete::selectRideFile(QString fileName) void Athlete::intervalTreeWidgetSelectionChanged() { - context->notifyIntervalHover(RideFileInterval()); // clear + context->notifyIntervalHover(NULL); // clear context->notifyIntervalSelected(); } diff --git a/src/Athlete.h b/src/Athlete.h index 2a1cd2faa..c8bf97112 100644 --- a/src/Athlete.h +++ b/src/Athlete.h @@ -56,6 +56,8 @@ class RideCache; class IntervalCache; class Context; class ColorEngine; +class AnalysisSidebar; +class Tab; class Athlete : public QObject { @@ -135,13 +137,6 @@ class Athlete : public QObject void addRide(QString name, bool bSelect=true, bool useTempActivities=false); void removeCurrentRide(); - // interval selection - QTreeWidgetItem *allIntervals; - IntervalTreeView *intervalWidget; - const QTreeWidgetItem *allIntervalItems() { return allIntervals; } - IntervalTreeView *intervalTreeWidget() { return intervalWidget; } - QTreeWidgetItem *mutableIntervalItems() { return allIntervals; } - // xones etc void notifyZonesChanged() { zonesChanged(); } void notifySeasonsChanged() { seasonsChanged(); } @@ -161,6 +156,17 @@ class Athlete : public QObject void updateRideFileIntervals(); void configChanged(qint32); + protected: + //XXX REFACTOR THIS WILL GO EVENTUALLY + friend class ::AnalysisSidebar; + friend class ::Tab; + + // interval selection + QTreeWidgetItem *allIntervals; + IntervalTreeView *intervalWidget; + const QTreeWidgetItem *allIntervalItems() { return allIntervals; } + IntervalTreeView *intervalTreeWidget() { return intervalWidget; } + QTreeWidgetItem *mutableIntervalItems() { return allIntervals; } }; diff --git a/src/BestIntervalDialog.cpp b/src/BestIntervalDialog.cpp index 8689ca6c5..895697a50 100644 --- a/src/BestIntervalDialog.cpp +++ b/src/BestIntervalDialog.cpp @@ -311,6 +311,8 @@ BestIntervalDialog::doneClicked() void BestIntervalDialog::addClicked() { +//XXX REFACTOR UPDATE WHEN DECIDE HOW TO ADD AN INTERVAL +#if 0 // run through the table row by row // and when the checkbox is shown // get name from column 2 @@ -339,4 +341,5 @@ BestIntervalDialog::addClicked() } } context->athlete->updateRideFileIntervals(); +#endif } diff --git a/src/BingMap.cpp b/src/BingMap.cpp index cec04f6fc..a6fc52d7f 100644 --- a/src/BingMap.cpp +++ b/src/BingMap.cpp @@ -466,12 +466,12 @@ BingMap::createMarkers() // // INTERVAL MARKERS // - if (context->athlete->allIntervalItems() == NULL) return; // none to do, we are all done then + if (myRideItem->intervals().count() == 0) return; int interval=0; - foreach (const RideFileInterval x, myRideItem->ride()->intervals()) { + foreach (IntervalItem *x, myRideItem->intervals()) { - int offset = myRideItem->ride()->intervalBegin(x); + int offset = myRideItem->ride()->intervalBeginSecs(x->start); code = QString("{ var latlng = new Microsoft.Maps.Location(%1,%2);\n" "var pushpinOptions = { };\n" "var pushpin = new Microsoft.Maps.Pushpin(latlng, pushpinOptions);\n" @@ -481,7 +481,7 @@ BingMap::createMarkers() " }") .arg(myRideItem->ride()->dataPoints()[offset]->lat,0,'g',GPS_COORD_TO_STRING) .arg(myRideItem->ride()->dataPoints()[offset]->lon,0,'g',GPS_COORD_TO_STRING) - .arg(x.name) + .arg(x->name) .arg(interval); view->page()->mainFrame()->evaluateJavaScript(code); @@ -541,22 +541,8 @@ void BWebBridge::call(int count) { qDebug()<<"webBridge call:"<property("ride").value(); - - if (context->athlete->allIntervalItems() == NULL || - rideItem == NULL || rideItem->ride() == NULL) return 0; // not inited yet! - - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - if (current->isSelected() == true) { - ++highlighted; - } - } - } - return highlighted; + if (rideItem) return rideItem->intervalsSelected().count(); } // get a latlon array for the i'th selected interval @@ -564,41 +550,29 @@ QVariantList BWebBridge::getLatLons(int i) { QVariantList latlons; - int highlighted=0; RideItem *rideItem = gm->property("ride").value(); - if (context->athlete->allIntervalItems() == NULL || - rideItem ==NULL || rideItem->ride() == NULL) return latlons; // not inited yet! + if (rideItem == NULL) return latlons; - if (i) { + // valid highlighted interval ? + if (i < 0 || i > rideItem->intervalsSelected().count()) { - // get for specific interval - for (int j=0; jathlete->allIntervalItems()->childCount(); j++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(j)); - if (current != NULL) { - if (current->isSelected() == true) { - ++highlighted; + IntervalItem *current = rideItem->intervalsSelected().at(i); - // this one! - if (highlighted==i) { + // so this one is the interval we need.. lets + // snaffle up the points in this section + foreach (RideFilePoint *p1, rideItem->ride()->dataPoints()) { + if (p1->secs+rideItem->ride()->recIntSecs() > current->start + && p1->secs< current->stop) { - // so this one is the interval we need.. lets - // snaffle up the points in this section - foreach (RideFilePoint *p1, rideItem->ride()->dataPoints()) { - if (p1->secs+rideItem->ride()->recIntSecs() > current->start - && p1->secs< current->stop) { - - if (p1->lat || p1->lon) { - latlons << p1->lat; - latlons << p1->lon; - } - } - } - return latlons; - } + if (p1->lat || p1->lon) { + latlons << p1->lat; + latlons << p1->lon; } } } + return latlons; + } else { // get latlons for entire route @@ -627,6 +601,12 @@ BWebBridge::drawOverlays() void BWebBridge::toggleInterval(int x) { + RideItem *rideItem = gm->property("ride").value(); + if (x < 0 || rideItem->intervals().count() >= x) return; + +//XXX WHEN DECIDED HOW TO SELECT/UNSELECT INTERVALS +#if 0 IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(x)); if (current) current->setSelected(!current->isSelected()); +#endif } diff --git a/src/Colors.h b/src/Colors.h index 8c133ee58..1404500f4 100644 --- a/src/Colors.h +++ b/src/Colors.h @@ -48,6 +48,7 @@ struct SizeSettings { extern SizeSettings defaultAppearance[]; extern float GCDPIScale; // font scaling for display +extern QColor standardColor(int num); class Colors { diff --git a/src/ComparePane.cpp b/src/ComparePane.cpp index 9608c4489..ef73fc6a8 100644 --- a/src/ComparePane.cpp +++ b/src/ComparePane.cpp @@ -54,6 +54,11 @@ static bool initStandardColors() } static bool init = initStandardColors(); +QColor standardColor(int num) +{ + return standardColors.at(num % standardColors.count()); +} + // we need to fix the sort order! class CTableWidgetItem : public QTableWidgetItem { diff --git a/src/Context.h b/src/Context.h index b8d8146f4..d2a79388a 100644 --- a/src/Context.h +++ b/src/Context.h @@ -152,7 +152,7 @@ class Context : public QObject void notifyZoomOut() { emit zoomOut(); } void notifyIntervalSelected() { intervalSelected(); } void notifyIntervalsChanged() { emit intervalsChanged(); } - void notifyIntervalHover(RideFileInterval x) { emit intervalHover(x); } + void notifyIntervalHover(IntervalItem *x) { emit intervalHover(x); } void notifyRideClean() { rideClean(ride); } void notifyRideDirty() { rideDirty(ride); } void notifyMetadataFlush() { metadataFlush(); } @@ -195,7 +195,7 @@ class Context : public QObject void intervalSelected(); void intervalsChanged(); - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); void intervalZoom(IntervalItem*); void zoomOut(); void metadataFlush(); diff --git a/src/CriticalPowerWindow.cpp b/src/CriticalPowerWindow.cpp index 1a8749acd..90eb049e8 100644 --- a/src/CriticalPowerWindow.cpp +++ b/src/CriticalPowerWindow.cpp @@ -469,7 +469,7 @@ CriticalPowerWindow::CriticalPowerWindow(Context *context, bool rangemode) : connect(cComboSeason, SIGNAL(currentIndexChanged(int)), this, SLOT(seasonSelected(int))); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged())); - connect(context, SIGNAL(intervalHover(RideFileInterval)), this, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), this, SLOT(intervalHover(IntervalItem*))); // Compare connect(context, SIGNAL(compareIntervalsStateChanged(bool)), SLOT(forceReplot())); @@ -956,11 +956,10 @@ CriticalPowerWindow::intervalSelected() // nothing to plot if (!amVisible() || myRideItem == NULL) return; - if (context->athlete->allIntervalItems() == NULL) return; // not inited yet! // if the array hasn't been initialised properly then clean it up // this is because intervalsChanged gets called when selecting rides - if (intervalCurves.count() != context->athlete->allIntervalItems()->childCount()) { + if (intervalCurves.count() != myRideItem->intervals().count()) { // wipe away what we got, even if not visible // clear any interval curves -- even if we are not visible foreach(QwtPlotCurve *p, intervalCurves) { @@ -972,36 +971,37 @@ CriticalPowerWindow::intervalSelected() // clear, resize to interval count and set to null intervalCurves.clear(); - if (myRideItem && myRideItem->ride() && myRideItem->ride()->intervals().count()) - for (int i=0; i< myRideItem->ride()->intervals().count(); i++) + if (myRideItem && myRideItem->ride() && myRideItem->intervals().count()) + for (int i=0; i< myRideItem->intervals().count(); i++) intervalCurves << NULL; } // which itervals are selected? IntervalItem *current=NULL; - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - current = static_cast(context->athlete->allIntervalItems()->child(i)); + int i=0; + foreach (IntervalItem*p, myRideItem->intervals()) { if (current != NULL) { - if (current->isSelected() == true) { + if (current->selected == true) { showIntervalCurve(current, i); // set it all up } else { hideIntervalCurve(i); // in case its shown at present } } + i++; } cpPlot->replot(); } // user hovered over an interval void -CriticalPowerWindow::intervalHover(RideFileInterval x) +CriticalPowerWindow::intervalHover(IntervalItem* x) { // ignore in compare mode if (!amVisible() || context->isCompareIntervals) return; // do we need to fill with nulls ? - if (intervalCurves.count() == 0 && myRideItem && myRideItem->ride() && myRideItem->ride()->intervals().count()) - for (int i=0; i< myRideItem->ride()->intervals().count(); i++) + if (intervalCurves.count() == 0 && myRideItem && myRideItem->ride() && myRideItem->intervals().count()) + for (int i=0; i< myRideItem->intervals().count(); i++) intervalCurves << NULL; // only one interval can be hovered at any one time @@ -1009,19 +1009,7 @@ CriticalPowerWindow::intervalHover(RideFileInterval x) // any nasty artefacts behind. And its always gray :) // first lets see what interval this actually is? - IntervalItem *current=NULL; - int index = -1; - - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - // is this the one ? - if (x.start == current->start && x.stop == current->stop) { - index = i; - break; - } - } - } + int index = myRideItem->intervals().indexOf(x); if (index >=0 && index < intervalCurves.count()) { @@ -1031,7 +1019,7 @@ CriticalPowerWindow::intervalHover(RideFileInterval x) // get the data setup // but if there is no data for the ride series // selected they will still be null - showIntervalCurve(current, index); // set it all up + showIntervalCurve(x, index); // set it all up hideIntervalCurve(index); // in case its shown at present } @@ -1136,7 +1124,7 @@ CriticalPowerWindow::showIntervalCurve(IntervalItem *current, int index) // set its color - based upon index in intervals! QColor intervalColor; - int count=context->athlete->allIntervalItems()->childCount(); + int count=myRideItem->intervals().count(); intervalColor.setHsv(index * (255/count), 255,255); QPen pen(intervalColor); double width = appsettings->value(this, GC_LINEWIDTH, 0.5).toDouble(); @@ -1299,7 +1287,7 @@ CriticalPowerWindow::setSeries(int index) // clear, resize to interval count and set to null intervalCurves.clear(); - for (int i=0; i<= context->athlete->allIntervalItems()->childCount(); i++) intervalCurves << NULL; + for (int i=0; i<= myRideItem->intervals().count(); i++) intervalCurves << NULL; cpPlot->setSeries(series); cpPlot->setRide(currentRide); diff --git a/src/CriticalPowerWindow.h b/src/CriticalPowerWindow.h index 48419a219..4717f3c6e 100644 --- a/src/CriticalPowerWindow.h +++ b/src/CriticalPowerWindow.h @@ -36,6 +36,7 @@ class CPPlot; class QwtPlotCurve; class Context; class RideItem; +class IntervalItem; class QwtPlotPicker; class MUPlot; @@ -204,7 +205,7 @@ class CriticalPowerWindow : public GcChartWindow void configChanged(qint32); void intervalSelected(); void intervalsChanged(); - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); void seasonSelected(int season); void shadingSelected(int shading); void showHeatChanged(int check); diff --git a/src/GcWindowRegistry.cpp b/src/GcWindowRegistry.cpp index b6c41a99d..4b57eb500 100644 --- a/src/GcWindowRegistry.cpp +++ b/src/GcWindowRegistry.cpp @@ -53,7 +53,8 @@ #include "SpinScanPlotWindow.h" #include "WorkoutPlotWindow.h" #include "BingMap.h" -#include "RouteWindow.h" +// Not until v4.0 +//#include "RouteWindow.h" #define VIEW_TRAIN 0x01 #define VIEW_ANALYSIS 0x02 @@ -202,7 +203,11 @@ GcWindowRegistry::newGcWindow(GcWinID id, Context *context) case GcWindowTypes::MapWindow: returning = new MapWindow(context); break; case GcWindowTypes::StreetViewWindow: returning = new StreetViewWindow(context); break; case GcWindowTypes::ActivityNavigator: returning = new RideNavigator(context); break; +#if 0 // not till v4.0 case GcWindowTypes::RouteSegment: returning = new RouteWindow(context); break; +#else + case GcWindowTypes::RouteSegment: returning = new GcWindow(); break; +#endif default: return NULL; break; } if (returning) returning->setProperty("type", QVariant::fromValue(id)); diff --git a/src/GoogleMapControl.cpp b/src/GoogleMapControl.cpp index b682b0a60..f13e0154b 100644 --- a/src/GoogleMapControl.cpp +++ b/src/GoogleMapControl.cpp @@ -579,12 +579,10 @@ GoogleMapControl::createMarkers() // // INTERVAL MARKERS // - if (context->athlete->allIntervalItems() == NULL) return; // none to do, we are all done then - int interval=0; - foreach (const RideFileInterval x, myRideItem->ride()->intervals()) { + foreach (IntervalItem *x, myRideItem->intervals()) { - int offset = myRideItem->ride()->intervalBegin(x); + int offset = myRideItem->ride()->intervalBeginSecs(x->start); code = QString( "{" " var latlng = new google.maps.LatLng(%1,%2);" @@ -597,7 +595,7 @@ GoogleMapControl::createMarkers() "}") .arg(myRideItem->ride()->dataPoints()[offset]->lat,0,'g',GPS_COORD_TO_STRING) .arg(myRideItem->ride()->dataPoints()[offset]->lon,0,'g',GPS_COORD_TO_STRING) - .arg(x.name) + .arg(x->name) .arg(interval) ; view->page()->mainFrame()->evaluateJavaScript(code); @@ -656,22 +654,9 @@ void WebBridge::call(int count) { qDebug()<<"webBridge call:"<property("ride").value(); - - if (context->athlete->allIntervalItems() == NULL || - rideItem == NULL || rideItem->ride() == NULL) return 0; // not inited yet! - - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - if (current->isSelected() == true) { - ++highlighted; - } - } - } - return highlighted; + if (rideItem) return rideItem->intervalsSelected().count(); + return 0; } // get a latlon array for the i'th selected interval @@ -679,41 +664,26 @@ QVariantList WebBridge::getLatLons(int i) { QVariantList latlons; - int highlighted=0; RideItem *rideItem = gm->property("ride").value(); - if (context->athlete->allIntervalItems() == NULL || - rideItem ==NULL || rideItem->ride() == NULL) return latlons; // not inited yet! + if (rideItem && i >= 0 && rideItem->intervalsSelected().count() > i) { - if (i) { + IntervalItem *current = rideItem->intervalsSelected().at(i); - // get for specific interval - for (int j=0; jathlete->allIntervalItems()->childCount(); j++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(j)); - if (current != NULL) { - if (current->isSelected() == true) { - ++highlighted; + // so this one is the interval we need.. lets + // snaffle up the points in this section + foreach (RideFilePoint *p1, rideItem->ride()->dataPoints()) { + if (p1->secs+rideItem->ride()->recIntSecs() > current->start + && p1->secs< current->stop) { - // this one! - if (highlighted==i) { - - // so this one is the interval we need.. lets - // snaffle up the points in this section - foreach (RideFilePoint *p1, rideItem->ride()->dataPoints()) { - if (p1->secs+rideItem->ride()->recIntSecs() > current->start - && p1->secs< current->stop) { - - if (p1->lat || p1->lon) { - latlons << p1->lat; - latlons << p1->lon; - } - } - } - return latlons; - } + if (p1->lat || p1->lon) { + latlons << p1->lat; + latlons << p1->lon; } } } + return latlons; + } else { // get latlons for entire route @@ -742,16 +712,22 @@ WebBridge::drawOverlays() void WebBridge::toggleInterval(int x) { + RideItem *rideItem = gm->property("ride").value(); + if (x < 0 || rideItem->intervals().count() >= x) return; + +//XXX WHEN DECIDED HOW TO SELECT/UNSELECT INTERVALS +#if 0 IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(x)); if (current) current->setSelected(!current->isSelected()); - return; +#endif } + void WebBridge::hoverInterval(int n) { RideItem *rideItem = gm->property("ride").value(); - if (rideItem && rideItem->ride() && rideItem->ride()->intervals().count() > n) { - context->notifyIntervalHover(rideItem->ride()->intervals().at(n)); + if (rideItem && rideItem->ride() && rideItem->intervals().count() > n) { + context->notifyIntervalHover(rideItem->intervals().at(n)); } } @@ -789,10 +765,12 @@ void WebBridge::hoverPath(double lat, double lng) { if (point) { +#if 0 //XXX REFACTOR -- HOW TO CREATE AND EDIT AN INTERVAL! + + RideItem *rideItem = gm->property("ride").value(); QString name = QString(tr("Selection #%1 ")).arg(selection); - QTreeWidgetItem *allIntervals = context->athlete->mutableIntervalItems(); - int count = allIntervals->childCount(); + int count = if (count > 0) { IntervalItem *bottom = (IntervalItem *) allIntervals->child(count-1); @@ -829,18 +807,18 @@ WebBridge::hoverPath(double lat, double lng) // now update the RideFileIntervals and all the plots etc //context->athlete->updateRideFileIntervals(); - +#endif } } void WebBridge::clickPath(double lat, double lng) { + +#if 0 //XXX REFACTOR HOW TO SELECT AND EDIT AND INTERVAL selection++; - QString name = QString(tr("Selection #%1 ")).arg(selection); - RideItem *rideItem = gm->property("ride").value(); - + QString name = QString(tr("Selection #%1 ")).arg(selection); QList list = searchPoint(lat, lng); if (list.count() > 0) { @@ -858,11 +836,13 @@ WebBridge::clickPath(double lat, double lng) } else point = NULL; +#endif } void WebBridge::mouseup() { +#if 0 //XXX REFACTOR CREATE AND EDIT NEW INTERVAL if (point) { gm->clearTempInterval(); QTreeWidgetItem *allIntervals = context->athlete->mutableIntervalItems(); @@ -876,6 +856,7 @@ WebBridge::mouseup() context->athlete->updateRideFileIntervals(); point = NULL; } +#endif } diff --git a/src/HistogramWindow.cpp b/src/HistogramWindow.cpp index c4b8816b0..35175254c 100644 --- a/src/HistogramWindow.cpp +++ b/src/HistogramWindow.cpp @@ -311,7 +311,7 @@ HistogramWindow::HistogramWindow(Context *context, bool rangemode) : GcChartWind connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); connect(context, SIGNAL(rideChanged(RideItem*)), this, SLOT(forceReplot())); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); - connect(context, SIGNAL(intervalHover(RideFileInterval)), powerHist, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), powerHist, SLOT(intervalHover(IntervalItem*))); // comparing things connect(context, SIGNAL(compareIntervalsStateChanged(bool)), this, SLOT(compareChanged())); diff --git a/src/IntervalItem.cpp b/src/IntervalItem.cpp index f64f23c44..54125e6cf 100644 --- a/src/IntervalItem.cpp +++ b/src/IntervalItem.cpp @@ -22,7 +22,7 @@ #include "Athlete.h" IntervalItem::IntervalItem(const RideFile *ride, QString name, double start, double stop, - double startKM, double stopKM, int displaySequence, + double startKM, double stopKM, int displaySequence, QColor color, RideFileInterval::IntervalType type) { this->name = name; @@ -34,6 +34,8 @@ IntervalItem::IntervalItem(const RideFile *ride, QString name, double start, dou this->displaySequence = displaySequence; this->rideItem_ = NULL; this->type = type; + this->color = color; + this->selected = false; metrics_.fill(0, RideMetricFactory::instance().metricCount()); setText(0, name); } @@ -48,6 +50,7 @@ void IntervalItem::setFrom(IntervalItem &other) { *this = other; + selected = false; } void @@ -104,6 +107,46 @@ IntervalItem::refresh() metrics_[j] = 0.00f; } + +double +IntervalItem::getForSymbol(QString name, bool useMetricUnits) +{ + if (metrics_.size()) { + // return the precomputed metric value + const RideMetricFactory &factory = RideMetricFactory::instance(); + const RideMetric *m = factory.rideMetric(name); + if (m) { + if (useMetricUnits) return metrics_[m->index()]; + else { + // little hack to set/get for conversion + const_cast(m)->setValue(metrics_[m->index()]); + return m->value(useMetricUnits); + } + } + } + return 0.0f; +} + +QString +IntervalItem::getStringForSymbol(QString name, bool useMetricUnits) +{ + QString returning("-"); + + if (metrics_.size()) { + // return the precomputed metric value + const RideMetricFactory &factory = RideMetricFactory::instance(); + const RideMetric *m = factory.rideMetric(name); + if (m) { + + double value = metrics_[m->index()]; + if (std::isinf(value) || std::isnan(value)) value=0; + const_cast(m)->setValue(value); + returning = m->toString(useMetricUnits); + } + } + return returning; +} + /*---------------------------------------------------------------------- * Edit Interval dialog *--------------------------------------------------------------------*/ diff --git a/src/IntervalItem.h b/src/IntervalItem.h index 409b98839..c1b66f081 100644 --- a/src/IntervalItem.h +++ b/src/IntervalItem.h @@ -34,17 +34,24 @@ class IntervalItem : public QTreeWidgetItem public: // constructors and accessors - IntervalItem(const RideFile *, QString, double, double, double, double, int, RideFileInterval::IntervalType); + IntervalItem(const RideFile *, QString, double, double, double, double, int, QColor, RideFileInterval::IntervalType); IntervalItem(); + // ride item we are in RideItem* rideItem() { return rideItem_; } - void setDisplaySequence(int seq) { displaySequence = seq; } + RideItem* rideItem_; // set from other void setFrom(IntervalItem &other); - // ride item we are in - RideItem* rideItem_; + // is this interval currently selected ? + bool selected; + + // access the metric value + double getForSymbol(QString name, bool useMetricUnits=true); + + // as a well formatted string + QString getStringForSymbol(QString name, bool useMetricUnits=true); // interval details QString name; // name @@ -54,6 +61,9 @@ class IntervalItem : public QTreeWidgetItem int displaySequence; // order to display on ride plots QColor color; // color to use on plots that differentiate by color + // order to show on plot + void setDisplaySequence(int seq) { displaySequence = seq; } + // precomputed metrics void refresh(); QVector metrics_; diff --git a/src/IntervalSummaryWindow.cpp b/src/IntervalSummaryWindow.cpp index 31d87c175..ec8f80c1e 100644 --- a/src/IntervalSummaryWindow.cpp +++ b/src/IntervalSummaryWindow.cpp @@ -46,7 +46,7 @@ IntervalSummaryWindow::IntervalSummaryWindow(Context *context) : context(context #endif connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalSelected())); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); - connect(context, SIGNAL(intervalHover(RideFileInterval)), this, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), this, SLOT(intervalHover(IntervalItem*))); connect(context, SIGNAL(configChanged(qint32)), this, SLOT(intervalSelected())); setHtml(GCColor::css() + ""); @@ -60,7 +60,7 @@ void IntervalSummaryWindow::intervalSelected() // if no ride available don't bother - just reset for color changes RideItem *rideItem = const_cast(context->currentRideItem()); - if (context->athlete->intervalTreeWidget()->selectedItems().count() == 0 || + if (rideItem->intervalsSelected().count() == 0 || rideItem == NULL || rideItem->ride() == NULL) { // no ride just update the colors QString html = GCColor::css(); @@ -73,36 +73,13 @@ void IntervalSummaryWindow::intervalSelected() QString html = GCColor::css(); html += ""; - // summary of all intervals selected - QList list; - if (context->athlete->allIntervalItems() != NULL) { - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - if (current->isSelected()) { - list << current; - } - } - } - } - if (list.count()>1) calcInterval(list, html); + // summarise all the intervals selected - this is painful! + //if (rideItem->intervalsSelected().count()>1) summarise(rideItem->intervalsSelected(), html); // summary for each of the currently selected intervals - if (context->athlete->allIntervalItems() != NULL) { - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - if (current->isSelected()) { - list << current; - calcInterval(current, html); - } - } - } - } + foreach(IntervalItem *interval, rideItem->intervalsSelected()) html += summary(interval); - if (html == GCColor::css()+"") { - html += "" + tr("select an interval for summary info") + ""; - } + if (html == GCColor::css()+"") html += "" + tr("select an interval for summary info") + ""; html += ""; setHtml(html); @@ -110,21 +87,21 @@ void IntervalSummaryWindow::intervalSelected() } void -IntervalSummaryWindow::intervalHover(RideFileInterval x) +IntervalSummaryWindow::intervalHover(IntervalItem* x) { // if we're not visible don't bother if (!isVisible()) return; // we already have summaries! - if (context->athlete->intervalWidget->selectedItems().count()) return; + if (x && x->rideItem()->intervalsSelected().count()) return; QString html = GCColor::css(); html += ""; - if (x == RideFileInterval()) { + if (x == NULL) { html += "" + tr("select an interval for summary info") + ""; } else { - calcInterval(x, html); + html += summary(x); } html += ""; setHtml(html); @@ -142,10 +119,14 @@ static bool contains(const RideFile*ride, QList intervals, int in return false; } +#if 0 void IntervalSummaryWindow::calcInterval(QList intervals, QString &html) { + // We need to create a special ridefile just for the selected intervals + // to calculate the aggregated metrics because intervals can OVERLAP! + // so we can't just aggregate the pre-computed metrics as this will lead + // to overstated totals and skewed averages. const RideFile* ride = context->ride ? context->ride->ride() : NULL; - RideFile f(const_cast(ride)); // for concatenating intervals @@ -186,45 +167,17 @@ void IntervalSummaryWindow::calcInterval(QList intervals, QString } } - summary(f, QString("%1 intervals").arg(intervals.count()), html); + // EEK this is getting complicated ! + // WE NEED TO CREATE A TEMPORARY INTERVALITEM TOO.............. + //XXX REFACTOR summary(QString("%1 intervals").arg(intervals.count()), html); } +#endif -void IntervalSummaryWindow::calcInterval(IntervalItem* interval, QString& html) +QString IntervalSummaryWindow::summary(IntervalItem *interval) { - const RideFile* ride = context->ride ? context->ride->ride() : NULL; + QString html; - RideFile f(const_cast(ride)); - int start = ride->timeIndex(interval->start); - int end = ride->timeIndex(interval->stop); - for (int i = start; i <= end; ++i) { - const RideFilePoint *p = ride->dataPoints()[i]; - f.appendPoint(p->secs, p->cad, p->hr, p->km, p->kph, p->nm, - p->watts, p->alt, p->lon, p->lat, p->headwind, p->slope, p->temp, p->lrbalance, - p->lte, p->rte, p->lps, p->rps, - p->lpco, p->rpco, - p->lppb, p->rppb, p->lppe, p->rppe, - p->lpppb, p->rpppb, p->lpppe, p->rpppe, - p->smo2, p->thb, - p->rvert, p->rcad, p->rcontact, 0); - - // derived data - RideFilePoint *l = f.dataPoints().last(); - l->np = p->np; - l->xp = p->xp; - l->apower = p->apower; - } - - summary(f, interval->text(0), html); -} - -void IntervalSummaryWindow::summary(RideFile &f, QString name, QString &html) -{ - bool metricUnits = context->athlete->useMetricUnits; - - if (f.dataPoints().size() == 0) { - // Interval empty, do not compute any metrics - html += "" + tr("empty interval") + ""; - } + bool useMetricUnits = context->athlete->useMetricUnits; QString s; if (appsettings->contains(GC_SETTINGS_INTERVAL_METRICS)) @@ -233,21 +186,17 @@ void IntervalSummaryWindow::summary(RideFile &f, QString name, QString &html) s = GC_SETTINGS_INTERVAL_METRICS_DEFAULT; QStringList intervalMetrics = s.split(","); - QHash metrics = - RideMetric::computeMetrics(context, &f, context->athlete->zones(), context->athlete->hrZones(), intervalMetrics); - - html += "" + name + ""; + html += "" + interval->name + ""; html += "(context->currentRideItem()); - + RideMetricFactory &factory = RideMetricFactory::instance(); foreach (QString symbol, intervalMetrics) { - RideMetricPtr m = metrics.value(symbol); + const RideMetric *m = factory.rideMetric(symbol); if (!m) continue; // skip metrics that are not relevant for this ride - if (!rideItem || m->isRelevantForRide(rideItem) == false) continue; + if (!interval->rideItem() || m->isRelevantForRide(interval->rideItem()) == false) continue; html += ""; // left column (names) @@ -255,21 +204,24 @@ void IntervalSummaryWindow::summary(RideFile &f, QString name, QString &html) // right column (values) QString s(""); - html += s.arg(m->toString(metricUnits)); + html += s.arg(interval->getStringForSymbol(symbol, useMetricUnits)); html += ""; html += ""; } html += "
%1"; - if (m->units(metricUnits) == "seconds" || - m->units(metricUnits) == tr("seconds")) + if (m->units(useMetricUnits) == "seconds" || + m->units(useMetricUnits) == tr("seconds")) ; // don't do anything - else if (m->units(metricUnits).size() > 0) - html += m->units(metricUnits); + else if (m->units(useMetricUnits).size() > 0) + html += m->units(useMetricUnits); html += "
"; + + return html; } +#if 0 void IntervalSummaryWindow::calcInterval(RideFileInterval interval, QString& html) { const RideFile* ride = context->ride ? context->ride->ride() : NULL; @@ -345,3 +297,4 @@ void IntervalSummaryWindow::calcInterval(RideFileInterval interval, QString& htm } html += ""; } +#endif diff --git a/src/IntervalSummaryWindow.h b/src/IntervalSummaryWindow.h index 75fb700bd..8d2ecbb6b 100644 --- a/src/IntervalSummaryWindow.h +++ b/src/IntervalSummaryWindow.h @@ -37,13 +37,11 @@ public: public slots: void intervalSelected(); - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); protected: - void calcInterval(QList intervals, QString& html); // summarise a list - void calcInterval(IntervalItem* interval, QString& html); // summarise a single interval - void calcInterval(RideFileInterval interval, QString& html); // summarise a single interval - void summary(RideFile &f, QString title, QString &html); + //XXX refactor void summarise(QList, QString& html); // summarise for all intervals + QString summary(IntervalItem *); Context *context; }; diff --git a/src/IntervalTreeView.cpp b/src/IntervalTreeView.cpp index 68b38ce96..6d4eea5f6 100644 --- a/src/IntervalTreeView.cpp +++ b/src/IntervalTreeView.cpp @@ -53,7 +53,7 @@ IntervalTreeView::mouseHover(QTreeWidgetItem *item, int) if (index >=0 && context->rideItem() && context->rideItem()->ride() && context->rideItem()->ride()->intervals().count() > index) { - context->notifyIntervalHover(context->rideItem()->ride()->intervals()[index]); + //XXX REFACTORING XXX context->notifyIntervalHover(context->rideItem()->ride()->intervals()[index]); } } diff --git a/src/PfPvPlot.cpp b/src/PfPvPlot.cpp index 012333fa9..0a8035991 100644 --- a/src/PfPvPlot.cpp +++ b/src/PfPvPlot.cpp @@ -388,19 +388,7 @@ PfPvPlot::refreshZoneItems() // how many intervals selected? int PfPvPlot::intervalCount() const { - int highlighted; - highlighted = 0; - if (context->athlete->allIntervalItems() == NULL) return 0; // not inited yet! - - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - if (current->isSelected() == true) { - ++highlighted; - } - } - } - return highlighted; + return rideItem->intervalsSelected().count(); } void @@ -411,7 +399,7 @@ PfPvPlot::mouseTrack(double cad, double watts) double aepf = (watts * 60.0) / (cad * cl_ * 2.0 * PI); double cpv = (cad * cl_ * 2.0 * PI) / 60.0; - if (rideItem && rideItem->ride() && rideItem->ride()->intervals().count() >= intervalMarkers.count()) { + if (rideItem && rideItem->ride() && rideItem->intervals().count() >= intervalMarkers.count()) { // are we hovering "close" to an interval marker ? int index = 0; foreach (QwtPlotMarker *is, intervalMarkers) { @@ -420,7 +408,7 @@ PfPvPlot::mouseTrack(double cad, double watts) double y = is->yValue() - aepf; if ((x > -0.05f && x < 0.05f) && (y > -3 && y < 3)) { - context->notifyIntervalHover(rideItem->ride()->intervals()[index]); + context->notifyIntervalHover(rideItem->intervals()[index]); } index++; @@ -443,7 +431,7 @@ PfPvPlot::refreshIntervalMarkers() // do we have a ride with intervals to refresh ? int count=0; - if (rideItem && rideItem->ride() && rideItem->ride()->dataPoints().count() && (count = rideItem->ride()->intervals().count())) { + if (rideItem && rideItem->ride() && rideItem->ride()->dataPoints().count() && (count = rideItem->intervals().count())) { // accumulating... QVector intervalAccumulator(count); @@ -459,10 +447,10 @@ PfPvPlot::refreshIntervalMarkers() // accumulate values for each interval here .... for(int i=0; i < count; i++) { - RideFileInterval v = rideItem->ride()->intervals()[i]; + IntervalItem *v = rideItem->intervals()[i]; // in our interval ? - if (p1->secs >= v.start && p1->secs <= v.stop) { + if (p1->secs >= v->start && p1->secs <= v->stop) { intervalAccumulator[i].aepf += aepf; intervalAccumulator[i].cpv += cpv; intervalAccumulator[i].count++; @@ -678,7 +666,7 @@ PfPvPlot::setData(RideItem *_rideItem) } void -PfPvPlot::intervalHover(RideFileInterval x) +PfPvPlot::intervalHover(IntervalItem *x) { if (!isVisible()) return; if (context->isCompareIntervals) return; @@ -696,7 +684,7 @@ PfPvPlot::intervalHover(RideFileInterval x) QVector aepfArray, cpvArray; foreach(const RideFilePoint *p1, rideItem->ride()->dataPoints()) { - if (p1->secs < x.start || p1->secs > x.stop) continue; + if (p1->secs < x->start || p1->secs > x->stop) continue; if (p1->watts != 0 && p1->cad != 0) { double aepf = (p1->watts * 60.0) / (p1->cad * cl_ * 2.0 * PI); @@ -710,8 +698,8 @@ PfPvPlot::intervalHover(RideFileInterval x) // which interval is it or how many ? int count = 0; int ours = 0; - foreach(RideFileInterval p, rideItem->ride()->intervals()) { - if (p.start == x.start && p.stop == x.stop) ours = count; + foreach(IntervalItem *p, rideItem->intervals()) { + if (p->start == x->start && p->stop == x->stop) ours = count; count++; } @@ -775,11 +763,11 @@ PfPvPlot::showIntervals(RideItem *_rideItem) double aepf = (p1->watts * 60.0) / (p1->cad * cl_ * 2.0 * PI); double cpv = (p1->cad * cl_ * 2.0 * PI) / 60.0; - for (int high=-1, t=0; tathlete->allIntervalItems()->childCount(); t++) { + for (int high=-1, t=0; tintervals().count(); t++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(t)); + IntervalItem *current = dynamic_cast(rideItem->intervals().at(t)); - if ((current != NULL) && current->isSelected()) { + if ((current != NULL) && current->selected) { ++high; if (p1->secs+ride->recIntSecs() > current->start && p1->secs< current->stop) { if (mergeIntervals()) @@ -818,15 +806,13 @@ PfPvPlot::showIntervals(RideItem *_rideItem) // ensure same colors are used for each interval selected int num_intervals_defined=0; QVector intervalmap; - if (context->athlete->allIntervalItems() != NULL) { - num_intervals_defined = context->athlete->allIntervalItems()->childCount(); + num_intervals_defined = rideItem->intervals().count(); - for (int g=0; gathlete->allIntervalItems()->childCount(); g++) { - IntervalItem *curr = dynamic_cast(context->athlete->allIntervalItems()->child(g)); - if (curr->isSelected()) intervalmap.append(g); - } - } + for (int g=0; gintervals().count(); g++) { + IntervalItem *curr = dynamic_cast(rideItem->intervals().at(g)); + if (curr->selected) intervalmap.append(g); + } // honor display sequencing QMap intervalOrder; @@ -834,12 +820,12 @@ PfPvPlot::showIntervals(RideItem *_rideItem) if (mergeIntervals()) intervalOrder.insert(1,0); else { - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { + for (int i=0; iintervals().count(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); + IntervalItem *current = dynamic_cast(rideItem->intervals().at(i)); - if (current != NULL && current->isSelected() == true) { - intervalOrder.insert(current->displaySequence, count++); + if (current != NULL && current->selected == true) { + intervalOrder.insert(current->displaySequence, count++); } } } diff --git a/src/PfPvPlot.h b/src/PfPvPlot.h index ad4c4e755..395c3a94c 100644 --- a/src/PfPvPlot.h +++ b/src/PfPvPlot.h @@ -80,7 +80,7 @@ class PfPvPlot : public QwtPlot public slots: void configChanged(qint32); - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); signals: void changedCP( const QString& ); diff --git a/src/PfPvWindow.cpp b/src/PfPvWindow.cpp index e8148c398..095a9b5f3 100644 --- a/src/PfPvWindow.cpp +++ b/src/PfPvWindow.cpp @@ -200,7 +200,7 @@ PfPvWindow::PfPvWindow(Context *context) : connect(context, SIGNAL(rideChanged(RideItem*)), this, SLOT(forceReplot())); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalSelected())); - connect(context, SIGNAL(intervalHover(RideFileInterval)), this, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), this, SLOT(intervalHover(IntervalItem*))); connect(context->athlete, SIGNAL(zonesChanged()), this, SLOT(zonesChanged())); connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32))); connect(context, SIGNAL(configChanged(qint32)), pfPvPlot, SLOT(configChanged(qint32))); @@ -270,7 +270,7 @@ PfPvWindow::rideSelected() } void -PfPvWindow::intervalHover(RideFileInterval x) +PfPvWindow::intervalHover(IntervalItem *x) { pfPvPlot->intervalHover(x); } diff --git a/src/PfPvWindow.h b/src/PfPvWindow.h index e75e58006..f1171ff00 100644 --- a/src/PfPvWindow.h +++ b/src/PfPvWindow.h @@ -95,7 +95,7 @@ class PfPvWindow : public GcChartWindow void rideSelected(); void forceReplot(); void intervalSelected(); - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); void zonesChanged(); protected slots: diff --git a/src/PowerHist.cpp b/src/PowerHist.cpp index 10139e3df..8a854a81d 100644 --- a/src/PowerHist.cpp +++ b/src/PowerHist.cpp @@ -1341,10 +1341,10 @@ PowerHist::setData(RideFileCache *cache) } void -PowerHist::intervalHover(RideFileInterval x) +PowerHist::intervalHover(IntervalItem *x) { // telling me to hide - if (x.start == 0 && x.stop == 0) { + if (x == NULL) { curveHover->hide(); return; } @@ -1783,7 +1783,7 @@ PowerHist::setData(RideItem *_rideItem, bool force) if (ride && hasData) { //setTitle(ride->startTime().toString(GC_DATETIME_FORMAT)); - setArraysFromRide(ride, standard, context->athlete->zones(), RideFileInterval()); + setArraysFromRide(ride, standard, context->athlete->zones(), NULL); } else { @@ -1802,7 +1802,7 @@ PowerHist::setData(RideItem *_rideItem, bool force) } void -PowerHist::setArraysFromRide(RideFile *ride, HistData &standard, const Zones *zones, RideFileInterval hover) +PowerHist::setArraysFromRide(RideFile *ride, HistData &standard, const Zones *zones, IntervalItem *hover) { // predefined deltas for each series static const double wattsDelta = 1.0; @@ -1866,8 +1866,8 @@ PowerHist::setArraysFromRide(RideFile *ride, HistData &standard, const Zones *zo // selected if hovered -or- selected depending on // whether we were passed a blank or real RideFileInterval bool selected = false; - if (hover.start != 0 && hover.stop != 0) { - if (p1->secs >= hover.start && p1->secs <= hover.stop) { selected = true; } + if (hover) { + if (p1->secs >= hover->start && p1->secs <= hover->stop) { selected = true; } } else { selected = isSelected(p1, ride->recIntSecs()); } @@ -2253,15 +2253,13 @@ bool PowerHist::shadePaceZones() const return (rideItem && rideItem->ride() && series == RideFile::kph && !zoned && shade == true); } -bool PowerHist::isSelected(const RideFilePoint *p, double sample) { - if (context->athlete->allIntervalItems() != NULL) { - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL) { - if (current->isSelected() && p->secs+sample>current->start && p->secsstop) { - return true; - } - } +bool PowerHist::isSelected(const RideFilePoint *p, double sample) +{ + if (!rideItem) { + + foreach (IntervalItem *interval, rideItem->intervalsSelected()) { + if (interval->isSelected() && p->secs+sample>interval->start && p->secsstop) + return true; } } return false; diff --git a/src/PowerHist.h b/src/PowerHist.h index 5a3f3a2c1..10a562acd 100644 --- a/src/PowerHist.h +++ b/src/PowerHist.h @@ -46,6 +46,7 @@ class QwtPlotCurve; class QwtPlotGrid; class Context; class RideItem; +class IntervalItem; struct RideFilePoint; class RideFileCache; class HistogramWindow; @@ -149,7 +150,7 @@ class PowerHist : public QwtPlot void setData(RideItem *_rideItem, bool force=false); // used to set and bin ride data - void setArraysFromRide(RideFile *ride, HistData &standard, const Zones *zones, RideFileInterval hover); + void setArraysFromRide(RideFile *ride, HistData &standard, const Zones *zones, IntervalItem *hover); void binData(HistData &standard, QVector&, QVector&, QVector&, QVector&); // set data from the compare intervals -or- dateranges @@ -182,7 +183,7 @@ class PowerHist : public QwtPlot // react to plot signals void pointHover(QwtPlotCurve *curve, int index); - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); // get told to refresh void recalc(bool force=false); // normal mode recalc diff --git a/src/RideDB.y b/src/RideDB.y index c9a154eef..f2eeccec8 100644 --- a/src/RideDB.y +++ b/src/RideDB.y @@ -196,7 +196,7 @@ interval_tuple: string ':' string { else if ($1 == "stopKM") jc->interval.startKM = $3.toDouble(); else if ($1 == "type") jc->interval.type = static_cast($3.toInt()); else if ($1 == "color") jc->interval.color = QColor($3); - else if ($1 == "seq") jc->interval.start = $3.toInt(); + else if ($1 == "seq") jc->interval.displaySequence = $3.toInt(); } interval_metrics: METRICS ':' '{' interval_metrics_list '}' ; diff --git a/src/RideEditor.cpp b/src/RideEditor.cpp index 3d8bfaeee..826797817 100644 --- a/src/RideEditor.cpp +++ b/src/RideEditor.cpp @@ -1218,23 +1218,18 @@ RideEditor::intervalSelected() table->selectionModel()->clear(); // highlight selection and jump to last - foreach(QTreeWidgetItem *x, context->athlete->allIntervalItems()->treeWidget()->selectedItems()) { + foreach(IntervalItem *interval, ride->intervalsSelected()) { - IntervalItem *current = (IntervalItem*)x; + // what is the first dataPoint index for this interval? + int start = ride->ride()->timeIndex(interval->start); + int end = ride->ride()->timeIndex(interval->stop); - if (current != NULL && current->isSelected() == true) { - - // what is the first dataPoint index for this interval? - int start = ride->ride()->timeIndex(current->start); - int end = ride->ride()->timeIndex(current->stop); - - // select all the rows - table->selectionModel()->clearSelection(); - table->selectionModel()->setCurrentIndex(model->index(start,0), QItemSelectionModel::Select); - table->selectionModel()->select(QItemSelection(model->index(start,0), - model->index(end,model->columnCount()-1)), - QItemSelectionModel::Select); - } + // select all the rows + table->selectionModel()->clearSelection(); + table->selectionModel()->setCurrentIndex(model->index(start,0), QItemSelectionModel::Select); + table->selectionModel()->select(QItemSelection(model->index(start,0), + model->index(end,model->columnCount()-1)), + QItemSelectionModel::Select); } } } diff --git a/src/RideFile.h b/src/RideFile.h index 293adfdeb..7b6e4b6cc 100644 --- a/src/RideFile.h +++ b/src/RideFile.h @@ -30,7 +30,7 @@ class RideItem; class RideCache; -class IntervalCache; +class IntervalItem; class WPrime; class RideFile; struct RideFilePoint; @@ -96,7 +96,8 @@ class RideFileInterval PEAKHR, // PEAK HR CLIMB, // Hills and Cols EFFORT, // Sustained effort - LAP // When a loop we extract each lap + LAP, // When a loop we extract each lap + MATCH // W'bal based "match from a matchbook" } types; // ALWAYS ADD TO END (RideDB.json uses int values) typedef enum intervaltype IntervalType; @@ -145,10 +146,29 @@ class RideFile : public QObject // QObject to emit signals friend class RideFileCommand; // tells us we were modified friend class RideCache; // tells us if wbal is stale - friend class IntervalCache; // tells us if wbal is stale friend class RideItem; // derived/wbal stale + friend class IntervalItem; // access intervals friend class MainWindow; // tells us we were modified friend class Context; // tells us we were saved + friend class Athlete; // tells us we were saved + + // file format writers have more access + friend class RideFileFactory; + friend class FitlogFileReader; + friend class GcFileReader; + friend class TcxFileReader; + friend class PwxFileReader; + friend class JsonFileReader; + + // split and mergers + friend class MergeActivityWizard; + friend class SplitActivityWizard; + friend class SplitConfirm; + friend class SplitSelect; + + // TEMPORARY WHILST REFACTORING! + friend class IntervalTreeView; + friend class ManualRideDialog; // utility static unsigned int computeFileCRC(QString); @@ -240,7 +260,6 @@ class RideFile : public QObject // QObject to emit signals void setId(const QString &value) { id_ = value; } // Working with INTERVALS - const QList &intervals() const { return intervals_; } void addInterval(RideFileInterval::IntervalType type, double start, double stop, const QString &name) { intervals_.append(RideFileInterval(type, start, stop, name)); } @@ -256,8 +275,6 @@ class RideFile : public QObject // QObject to emit signals } intervals_.append(RideFileInterval(type, start, stop, name)); } - void clearIntervals(); - void fillInIntervals(); int intervalBegin(const RideFileInterval &interval) const; int intervalBeginSecs(const double secs) const; @@ -320,6 +337,11 @@ class RideFile : public QObject // QObject to emit signals protected: + // should access via IntervalItem + const QList &intervals() const { return intervals_; } + void clearIntervals(); + void fillInIntervals(); + void emitSaved(); void emitReverted(); void emitModified(); @@ -451,7 +473,6 @@ class RideFileFactory { friend class ::MetricAggregator; friend class ::RideCache; - friend class ::IntervalCache; // will become private as code should work with // in memory representation not on disk .. but as we diff --git a/src/RideItem.cpp b/src/RideItem.cpp index f6660907b..280b87634 100644 --- a/src/RideItem.cpp +++ b/src/RideItem.cpp @@ -564,7 +564,8 @@ RideItem::updateIntervals() begin->secs, end->secs, f->timeToDistance(begin->secs), f->timeToDistance(end->secs), - 0, // sequence defaults to count + 0, + QColor(Qt::darkBlue), RideFileInterval::ALL); // same as the whole ride, not need to compute @@ -578,6 +579,9 @@ RideItem::updateIntervals() // skip peaks, they're autodiscovered now if (interval.isPeak()) continue; + // skip climbs, they're autodiscovered now + if (interval.isClimb()) continue; + // skip entire ride, they're autodiscovered too if (interval.start <= begin->secs && interval.stop >= end->secs) continue; @@ -592,7 +596,8 @@ RideItem::updateIntervals() interval.start, interval.stop, f->timeToDistance(interval.start), f->timeToDistance(interval.stop), - count++, // sequence defaults to count + count, + standardColor(count++), RideFileInterval::USER); intervalItem->rideItem_ = this; // XXX will go when we refactor and be passed instead of ridefile intervalItem->refresh(); // XXX will get called in constructore when refactor @@ -633,7 +638,8 @@ RideItem::updateIntervals() start, stop, f->timeToDistance(start), f->timeToDistance(stop), - count++, // sequence defaults to count + count++, + QColor(Qt::green), RideFileInterval::CLIMB); intervalItem->rideItem_ = this; // XXX will go when we refactor and be passed instead of ridefile intervalItem->refresh(); // XXX will get called in constructore when refactor @@ -688,3 +694,11 @@ RideItem::updateIntervals() }*/ } +QList RideItem::intervalsSelected() +{ + QList returning; + foreach(IntervalItem *p, intervals_) { + if (p->selected) returning << p; + } + return returning; +} diff --git a/src/RideItem.h b/src/RideItem.h index 536a6ecb0..e7cdef147 100644 --- a/src/RideItem.h +++ b/src/RideItem.h @@ -124,6 +124,7 @@ class RideItem : public QObject RideFileCache *fileCache(); QVector &metrics() { return metrics_; } QList &intervals() { return intervals_; } + QList intervalsSelected(); QMap &metadata() { return metadata_; } const QStringList errors() { return errors_; } double getWeight(); diff --git a/src/RideSummaryWindow.cpp b/src/RideSummaryWindow.cpp index e96078af4..9c413c108 100644 --- a/src/RideSummaryWindow.cpp +++ b/src/RideSummaryWindow.cpp @@ -23,6 +23,7 @@ #include "RideFile.h" #include "RideCache.h" #include "RideItem.h" +#include "IntervalItem.h" #include "RideMetric.h" #include "PMCData.h" #include "Season.h" @@ -921,16 +922,10 @@ RideSummaryWindow::htmlSummary() // // Interval Summary (recalculated on every refresh since they are not cached at present) // - if (ride->intervals().size() > 0) { + if (rideItem->intervals().size() > 0) { Season rideSeason; - bool wantRank=false; -#ifdef GC_HAVE_RANKING - if (!ride->isRun() && !ride->isSwim() && ride->areDataPresent()->watts == true) { - rideSeason = context->athlete->seasons->seasonFor(ride->startTime().date()); - wantRank = true; - } -#endif + bool firstRow = true; QString s; if (appsettings->contains(GC_SETTINGS_INTERVAL_METRICS)) @@ -941,42 +936,15 @@ RideSummaryWindow::htmlSummary() summary += "

"+tr("Intervals")+"

\n

\n"; summary += "intervals()) { - RideFile f(ride); - for (int i = ride->intervalBegin(interval); i>= 0 &&i < ride->dataPoints().size(); ++i) { - const RideFilePoint *p = ride->dataPoints()[i]; - if (p->secs > interval.stop) - break; - f.appendPoint(p->secs, p->cad, p->hr, p->km, p->kph, p->nm, - p->watts, p->alt, p->lon, p->lat, p->headwind, - p->slope, p->temp, p->lrbalance, - p->lte, p->rte, p->lps, p->rps, - p->lpco, p->rpco, p->lppb, p->rppb, p->lppe, p->rppe, p->lpppb, p->rpppb, p->lpppe, p->rpppe, - p->smo2, p->thb, p->rvert, p->rcad, p->rcontact, 0); - - // derived data - RideFilePoint *l = f.dataPoints().last(); - l->np = p->np; - l->xp = p->xp; - l->apower = p->apower; - } - if (f.dataPoints().size() == 0) { - // Interval empty, do not compute any metrics - continue; - } - - QHash metrics = - RideMetric::computeMetrics(context, &f, context->athlete->zones(), context->athlete->hrZones(), intervalMetrics); + foreach (IntervalItem *interval, rideItem->intervals()) { if (firstRow) { summary += ""; summary += ""; - if (wantRank) { - summary += ""; - } foreach (QString symbol, intervalMetrics) { - RideMetricPtr m = metrics.value(symbol); + const RideMetric *m = factory.rideMetric(symbol); if (!m || !m->isRelevantForRide(rideItem)) continue; summary += "watts; - duration += f.recIntSecs(); - } - double value = duration ? (total / duration) : 0; - - Specification spec; - spec.setDateRange(DateRange(rideSeason.start, rideSeason.end)); - - rank = RideFileCache::rank(context, RideFile::watts, duration, value, spec, of); - - rankString = rankingString(rank); - - } - } - - // top 3 efforts this season get color - if (wantRank && rank <= 3) summary += " id=\"sharp\" "; summary += ">"; - summary += ""; - - if (wantRank) summary += ""; - + summary += ""; foreach (QString symbol, intervalMetrics) { - RideMetricPtr m = metrics.value(symbol); + + const RideMetric *m = factory.rideMetric(symbol); if (!m || !m->isRelevantForRide(rideItem)) continue; + QString s(""); - summary += s.arg(m->toString(useMetricUnits)); + summary += s.arg(interval->getStringForSymbol(symbol, useMetricUnits)); } diff --git a/src/ScatterPlot.cpp b/src/ScatterPlot.cpp index c11f06c7d..270b90598 100644 --- a/src/ScatterPlot.cpp +++ b/src/ScatterPlot.cpp @@ -221,7 +221,7 @@ ScatterPlot::ScatterPlot(Context *context) : context(context) setAxisScaleDraw(QwtPlot::yLeft, sd); connect(context, SIGNAL(configChanged(qint32)), this, SLOT(configChanged(qint32))); - connect(context, SIGNAL(intervalHover(RideFileInterval)), this, SLOT(intervalHover(RideFileInterval))); + connect(context, SIGNAL(intervalHover(IntervalItem*)), this, SLOT(intervalHover(IntervalItem*))); // lets watch the mouse move... new mouseTracker(this); @@ -277,9 +277,9 @@ void ScatterPlot::setData (ScatterSettings *settings) QVector intervals; QMap displaySequence; - for (int child=0; childathlete->allIntervalItems()->childCount(); child++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(child)); - if ((current != NULL) && current->isSelected()) { + for (int child=0; childride->intervals().count(); child++) { + IntervalItem *current = settings->ride->intervals().at(child); + if (current->selected) { intervals.append(child); displaySequence.insert(current->displaySequence, intervals.count()-1); } @@ -359,9 +359,9 @@ void ScatterPlot::setData (ScatterSettings *settings) if (!(settings->ignore && (x == 0 && y ==0))) { // which interval is it in? - for (int idx=0; idxride->intervals().count(); idx++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(intervals[idx])); + IntervalItem *current = settings->ride->intervals().at(idx); if (point->secs+settings->ride->ride()->recIntSecs() > current->start && point->secs< current->stop) { xvals[idx].append(x); @@ -379,7 +379,7 @@ void ScatterPlot::setData (ScatterSettings *settings) int idx = order.value(); QColor intervalColor; - intervalColor.setHsv((255/context->athlete->allIntervalItems()->childCount()) * (intervals[idx]), 255,255); + intervalColor.setHsv((255/settings->ride->intervals().count()) * (intervals[idx]), 255,255); // left / right are darker lighter if (side) intervalColor = intervalColor.lighter(50); @@ -589,7 +589,7 @@ void ScatterPlot::mouseMoved() { if (!isVisible()) return; - if (ride && ride->ride() && ride->ride()->intervals().count() >= intervalMarkers.count()) { + if (ride && ride->ride() && ride->intervals().count() >= intervalMarkers.count()) { // where is the mouse ? QPoint pos = QCursor::pos(); @@ -604,7 +604,7 @@ void ScatterPlot::mouseMoved() int dy = mpos.y() - pos.y(); if ((dx > -6 && dx < 6) && (dy > -6 && dy < 6)) - context->notifyIntervalHover(ride->ride()->intervals()[index]); + context->notifyIntervalHover(ride->intervals()[index]); index++; } @@ -613,7 +613,7 @@ void ScatterPlot::mouseMoved() } void -ScatterPlot::intervalHover(RideFileInterval ri) +ScatterPlot::intervalHover(IntervalItem *ri) { if (!isVisible()) return; if (context->isCompareIntervals) return; @@ -653,7 +653,7 @@ ScatterPlot::intervalHover(RideFileInterval ri) double y = pointType(p1, xseries, side, context->athlete->useMetricUnits, cranklength); double x = pointType(p1, yseries, side, context->athlete->useMetricUnits, cranklength); - if (p1->secs < ri.start || p1->secs > ri.stop) continue; + if (p1->secs < ri->start || p1->secs > ri->stop) continue; xArray << x; yArray << y; @@ -662,8 +662,8 @@ ScatterPlot::intervalHover(RideFileInterval ri) // which interval is it or how many ? int count = 0; int ours = 0; - foreach(RideFileInterval p, ride->ride()->intervals()) { - if (p.start == ri.start && p.stop == ri.stop) ours = count; + foreach(IntervalItem *p, ride->intervals()) { + if (p->start == ri->start && p->stop == ri->stop) ours = count; count++; } @@ -721,7 +721,7 @@ ScatterPlot::refreshIntervalMarkers(ScatterSettings *settings) // do we have a ride with intervals to refresh ? int count=0; - if (settings->ride && settings->ride->ride() && settings->ride->ride()->dataPoints().count() && (count = settings->ride->ride()->intervals().count())) { + if (settings->ride && settings->ride->ride() && settings->ride->ride()->dataPoints().count() && (count = settings->ride->intervals().count())) { // accumulating... QVector intervalAccumulator(count); @@ -740,10 +740,10 @@ ScatterPlot::refreshIntervalMarkers(ScatterSettings *settings) // accumulate values for each interval here .... for(int i=0; i < count; i++) { - RideFileInterval v = settings->ride->ride()->intervals()[i]; + IntervalItem *v = settings->ride->intervals()[i]; // in our interval ? - if (point->secs >= v.start && point->secs <= v.stop) { + if (point->secs >= v->start && point->secs <= v->stop) { intervalAccumulator[i].x += x; intervalAccumulator[i].y += y; intervalAccumulator[i].count++; diff --git a/src/ScatterPlot.h b/src/ScatterPlot.h index a97066140..709bdbcfe 100644 --- a/src/ScatterPlot.h +++ b/src/ScatterPlot.h @@ -27,6 +27,7 @@ #include "Context.h" #include "IntervalItem.h" #include "RideItem.h" +#include "IntervalItem.h" #include "RideFile.h" #include "Units.h" #include "cmath" @@ -86,7 +87,7 @@ class ScatterPlot : public QwtPlot void setAxisTitle(int axis, QString label); public slots: - void intervalHover(RideFileInterval); + void intervalHover(IntervalItem*); void mouseMoved(); void configChanged(qint32); diff --git a/src/ScatterWindow.cpp b/src/ScatterWindow.cpp index 36ce65609..dc7cd0d28 100644 --- a/src/ScatterWindow.cpp +++ b/src/ScatterWindow.cpp @@ -425,12 +425,9 @@ ScatterWindow::setData() */ // any intervals to plot? - settings.intervals.clear(); - for (int i=0; iathlete->allIntervalItems()->childCount(); i++) { - IntervalItem *current = dynamic_cast(context->athlete->allIntervalItems()->child(i)); - if (current != NULL && current->isSelected() == true) - settings.intervals.append(current); - } + if (ride) settings.intervals = ride->intervalsSelected(); + else settings.intervals.clear(); + scatterPlot->setData(&settings); } diff --git a/src/Tab.cpp b/src/Tab.cpp index c229b94ee..2eb464b90 100644 --- a/src/Tab.cpp +++ b/src/Tab.cpp @@ -186,6 +186,7 @@ Tab::rideSelected(RideItem*) QList intervals = context->athlete->allIntervals->takeChildren(); for (int i=0; iride) { // only if we have a ride pointer RideFile *selected = context->ride->ride(); @@ -207,6 +208,7 @@ Tab::rideSelected(RideItem*) } } } +#endif // all done, so connected widgets can receive signals now context->athlete->intervalWidget->blockSignals(false); diff --git a/src/src.pro b/src/src.pro index 3d598e719..138cfe40f 100644 --- a/src/src.pro +++ b/src/src.pro @@ -420,7 +420,6 @@ HEADERS += \ Route.h \ RouteItem.h \ RouteParser.h \ - RouteWindow.h \ ScatterPlot.h \ ScatterWindow.h \ Season.h \ @@ -475,6 +474,10 @@ YACCSOURCES += JsonRideFile.y WithingsParser.y RideDB.y #-t turns on debug, use with caution #QMAKE_YACCFLAGS = -t -d +# code that is pending later releases and not compiled in currently +DEFERRES += RouteWindow.h \ + RouteWindow.cpp + SOURCES += \ AboutDialog.cpp \ AddDeviceWizard.cpp \ @@ -645,7 +648,6 @@ SOURCES += \ Route.cpp \ RouteItem.cpp \ RouteParser.cpp \ - RouteWindow.cpp \ SaveDialogs.cpp \ ScatterPlot.cpp \ ScatterWindow.cpp \
"+tr("Interval Name")+"" + tr("Rank ") + rideSeason.name + "" + m->name(); if (m->units(useMetricUnits) == "seconds" || m->units(useMetricUnits) == tr("seconds")) { @@ -999,46 +967,16 @@ RideSummaryWindow::htmlSummary() else summary += "
" + interval.name + "" + rankString + "" + interval->name + "%1