/* * Copyright (c) 2011 Eric Brandt (eric.l.brandt@gmail.com) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Context.h" #include "Athlete.h" #include "RideFile.h" #include "RideItem.h" #include "RideMetric.h" #include "IntervalItem.h" #include "IntervalTreeView.h" #include "IntervalSummaryWindow.h" #include "Settings.h" #include "TimeUtils.h" #include "Colors.h" #include #include #include IntervalSummaryWindow::IntervalSummaryWindow(Context *context) : context(context) { setWindowTitle(tr("Interval Summary")); setReadOnly(true); setFrameStyle(QFrame::NoFrame); #ifdef Q_OS_WIN QStyle *cde = QStyleFactory::create(OS_STYLE); verticalScrollBar()->setStyle(cde); #endif #ifdef Q_OS_MAC setAttribute(Qt::WA_MacShowFocusRect, 0); #endif connect(context, SIGNAL(intervalsChanged()), this, SLOT(intervalSelected())); connect(context, SIGNAL(intervalSelected()), this, SLOT(intervalSelected())); connect(context, SIGNAL(intervalHover(IntervalItem*)), this, SLOT(intervalHover(IntervalItem*))); connect(context, SIGNAL(configChanged(qint32)), this, SLOT(intervalSelected())); setHtml(GCColor::css() + ""); } IntervalSummaryWindow::~IntervalSummaryWindow() { } void IntervalSummaryWindow::intervalSelected() { // if no ride available don't bother - just reset for color changes RideItem *rideItem = const_cast(context->currentRideItem()); if (rideItem == NULL || rideItem->intervalsSelected().count() == 0 || rideItem->ride() == NULL) { // no ride just update the colors QString html = GCColor::css(); html += ""; setHtml(html); return; } // summary is html QString html = GCColor::css(); html += ""; // summarise all the intervals selected - this is painful! // now also summarises for entire ride EXCLUDING the intervals selected QString notincluding; if (rideItem->intervalsSelected().count()>1) html += summary(rideItem->intervalsSelected(), notincluding); // summary for each of the currently selected intervals foreach(IntervalItem *interval, rideItem->intervalsSelected()) html += summary(interval); // now add the excluding text html += notincluding; if (html == GCColor::css()+"") html += "" + tr("select an interval for summary info") + ""; html += ""; setHtml(html); return; } void IntervalSummaryWindow::intervalHover(IntervalItem* x) { // if we're not visible don't bother if (!isVisible()) return; // we already have summaries! if (x && x->rideItem()->intervalsSelected().count()) return; // its to clear, but if the current ride has selected intervals then we will ignore it RideItem *rideItem = const_cast(context->currentRideItem()); if (!x && rideItem && rideItem->intervalsSelected().count()) return; QString html = GCColor::css(); html += ""; if (x == NULL) { html += "" + tr("select an interval for summary info") + ""; } else { html += summary(x); } html += ""; setHtml(html); return; } static bool contains(const RideFile*ride, QList intervals, int index) { foreach(IntervalItem *item, intervals) { int start = ride->timeIndex(item->start); int end = ride->timeIndex(item->stop); if (index >= start && index <= end) return true; } return false; } QString IntervalSummaryWindow::summary(QList intervals, QString ¬including) { // 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)); RideFile notf(const_cast(ride)); // for concatenating intervals RideFilePoint *last = NULL; double timeOff=0; double distOff=0; RideFilePoint *notlast = NULL; double nottimeOff=0; double notdistOff=0; for (int i = 0; i < ride->dataPoints().count(); ++i) { // append points for selected intervals const RideFilePoint *p = ride->dataPoints()[i]; if (contains(ride, intervals, i)) { // drag back time/distance for data not included below if (notlast) { nottimeOff = p->secs - notlast->secs; notdistOff = p->km - notlast->km; } else { nottimeOff = p->secs; notdistOff = p->km; } f.appendPoint(p->secs-timeOff, p->cad, p->hr, p->km-distOff, 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, p->tcore, 0); // derived data last = f.dataPoints().last(); last->np = p->np; last->xp = p->xp; last->apower = p->apower; } else { // drag back time/distance for data not included above if (last) { timeOff = p->secs - last->secs; distOff = p->km - last->km; } else { timeOff = p->secs; distOff = p->km; } notf.appendPoint(p->secs-nottimeOff, p->cad, p->hr, p->km-notdistOff, 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, p->tcore, 0); // derived data notlast = notf.dataPoints().last(); notlast->np = p->np; notlast->xp = p->xp; notlast->apower = p->apower; } } QString s; if (appsettings->contains(GC_SETTINGS_INTERVAL_METRICS)) s = appsettings->value(this, GC_SETTINGS_INTERVAL_METRICS).toString(); else s = GC_SETTINGS_INTERVAL_METRICS_DEFAULT; QStringList intervalMetrics = s.split(","); const RideMetricFactory &factory = RideMetricFactory::instance(); QHash metrics = RideMetric::computeMetrics(context, &f, context->athlete->zones(), context->athlete->hrZones(), intervalMetrics); QHash notmetrics = RideMetric::computeMetrics(context, ¬f, context->athlete->zones(), context->athlete->hrZones(), intervalMetrics); // create temp interval item to use by interval summary IntervalItem temp; // pack the metrics away and clean up if needed temp.metrics().fill(0, factory.metricCount()); // NOTE INCLUDED // snaffle away all the computed values into the array QHashIterator i(metrics); while (i.hasNext()) { i.next(); temp.metrics()[i.value()->index()] = i.value()->value(); } // clean any bad values for(int j=0; jrideItem_; QString returning = summary(&temp); if (notf.dataPoints().count()) { // EXCLUDING // snaffle away all the computed values into the array // pack the metrics away and clean up if needed temp.metrics().fill(0, factory.metricCount()); QHashIterator i(notmetrics); while (i.hasNext()) { i.next(); temp.metrics()[i.value()->index()] = i.value()->value(); } // clean any bad values for(int j=0; jrideItem_; // use standard method from above notincluding = summary(&temp); } return returning; } QString IntervalSummaryWindow::summary(IntervalItem *interval) { QString html; bool useMetricUnits = context->athlete->useMetricUnits; QString s; if (appsettings->contains(GC_SETTINGS_INTERVAL_METRICS)) s = appsettings->value(this, GC_SETTINGS_INTERVAL_METRICS).toString(); else s = GC_SETTINGS_INTERVAL_METRICS_DEFAULT; QStringList intervalMetrics = s.split(","); html += "" + interval->name + ""; html += "rideItem() || m->isRelevantForRide(interval->rideItem()) == false) continue; html += ""; // left column (names) html += ""; // right column (values) QString s(""); html += s.arg(interval->getStringForSymbol(symbol, useMetricUnits)); html += ""; html += ""; } html += "
" + m->name() + "%1"; if (m->units(useMetricUnits) == "seconds" || m->units(useMetricUnits) == tr("seconds")) ; // don't do anything else if (m->units(useMetricUnits).size() > 0) html += m->units(useMetricUnits); html += "
"; return html; }