diff --git a/src/CompareDateRange.cpp b/src/CompareDateRange.cpp new file mode 100644 index 000000000..e82b910ca --- /dev/null +++ b/src/CompareDateRange.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2014 Mark Liversedge (liversedge@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 "CompareDateRange.h" + +#include "Context.h" +#include "Season.h" +#include "SummaryMetrics.h" +#include diff --git a/src/CompareDateRange.h b/src/CompareDateRange.h new file mode 100644 index 000000000..706a410cc --- /dev/null +++ b/src/CompareDateRange.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 Mark Liversedge (liversedge@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 + */ + +#ifndef _GC_CompareDateRange_h +#define _GC_CompareDateRange_h + +class Context; +#include "SummaryMetrics.h" + +#include +#include +#include + +class CompareDateRange +{ + public: + CompareDateRange() : context(NULL), days(0), sourceContext(NULL), checked(false) {} + + Context *context; + QString name; + QColor color; + QList metrics, measures; + QDate start, end; + int days; + Context *sourceContext; + bool checked; + + bool isChecked() const { return checked; } + void setChecked(bool x) { checked=x; } +}; + +#endif diff --git a/src/ComparePane.cpp b/src/ComparePane.cpp index 7d52ed7ef..b415b350d 100644 --- a/src/ComparePane.cpp +++ b/src/ComparePane.cpp @@ -21,6 +21,7 @@ #include "RideFile.h" #include "RideMetric.h" #include "SummaryMetrics.h" +#include "MetricAggregator.h" #include "ColorButton.h" #include "TimeUtils.h" #include "Units.h" @@ -46,10 +47,12 @@ static bool initStandardColors() standardColors << QColor(Qt::darkGreen); standardColors << QColor(Qt::darkBlue); standardColors << QColor(Qt::darkMagenta); + + return true; } static bool init = initStandardColors(); -ComparePane::ComparePane(Context *context, QWidget *parent, CompareMode mode) : context(context), mode_(mode), QWidget(parent) +ComparePane::ComparePane(Context *context, QWidget *parent, CompareMode mode) : QWidget(parent), context(context), mode_(mode) { QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0,0,0,0); @@ -152,7 +155,9 @@ ComparePane::refreshTable() // compute the metrics for this ride SummaryMetrics metrics; - QHash computed = RideMetric::computeMetrics(context, x.data, context->athlete->zones(), context->athlete->hrZones(), worklist); + QHash computed = RideMetric::computeMetrics(context, x.data, + context->athlete->zones(), context->athlete->hrZones(), worklist); + for(int i = 0; i < worklist.count(); ++i) { if (worklist[i] != "") { RideMetricPtr m = computed.value(worklist[i]); @@ -248,7 +253,132 @@ ComparePane::refreshTable() } else { //SEASONS - //XXX seasons not written yet + // STEP ONE : SET THE TABLE HEADINGS + + // clear current contents + table->clear(); + table->setRowCount(0); + + // metric summary + QStringList always; + always << "workout_time" << "total_distance"; + QString s = appsettings->value(this, GC_SETTINGS_SUMMARY_METRICS, GC_SETTINGS_SUMMARY_METRICS_DEFAULT).toString(); + if (s == "") s = GC_SETTINGS_SUMMARY_METRICS_DEFAULT; + QStringList metricColumns = always + s.split(","); // always showm metrics plus user defined summary metrics + + // called after config is updated typically + QStringList list; + list << "" // checkbox + << "" // color + << "Athlete" + << "From" + << "To"; + + QStringList worklist; // metrics to compute + RideMetricFactory &factory = RideMetricFactory::instance(); + + foreach(QString metric, metricColumns) { + + // get the metric name + const RideMetric *m = factory.rideMetric(metric); + if (m) { + worklist << metric; + QString units; + if (m->units(context->athlete->useMetricUnits) != "seconds") units = m->units(context->athlete->useMetricUnits); + if (units != "") list << QString("%1 (%2)").arg(m->name()).arg(units); + else list << QString("%1").arg(m->name()); + } + } + + list << "Date Range"; + + table->setColumnCount(list.count()); + table->setHorizontalHeaderLabels(list); + table->setSortingEnabled(true); + table->verticalHeader()->hide(); + table->setShowGrid(false); + table->setSelectionMode(QAbstractItemView::MultiSelection); + table->setSelectionBehavior(QAbstractItemView::SelectRows); + + // STEP TWO : CLEAR AND RE-ADD TO REFLECT CHANGES + + table->setRowCount(context->compareDateRanges.count()); + int counter = 0; + foreach(CompareDateRange x, context->compareDateRanges) { + + // First few cols always the same + // check - color - athlete - date - time + // now create a row on the compare pane + + // Checkbox + QCheckBox *check = new QCheckBox(this); + check->setChecked(x.checked); + table->setCellWidget(counter, 0, check); + connect(check, SIGNAL(stateChanged(int)), this, SLOT(intervalButtonsChanged())); + + // Color Button + ColorButton *colorButton = new ColorButton(this, "Color", x.color); + table->setCellWidget(counter, 1, colorButton); + connect(colorButton, SIGNAL(colorChosen(QColor)), this, SLOT(intervalButtonsChanged())); + + // athlete + QTableWidgetItem *t = new QTableWidgetItem; + t->setText(x.sourceContext->athlete->cyclist); + t->setFlags(t->flags() & (~Qt::ItemIsEditable)); + table->setItem(counter, 2, t); + + // date from + t = new QTableWidgetItem; + t->setText(x.start.toString("dd MMM, yyyy")); + t->setFlags(t->flags() & (~Qt::ItemIsEditable)); + table->setItem(counter, 3, t); + + // date to + t = new QTableWidgetItem; + t->setText(x.end.toString("dd MMM, yyyy")); + t->setFlags(t->flags() & (~Qt::ItemIsEditable)); + table->setItem(counter, 4, t); + + // metrics + for(int i = 0; i < worklist.count(); ++i) { + + QString value = SummaryMetrics::getAggregated(x.sourceContext, worklist[i], + x.metrics, QStringList(), false, context->athlete->useMetricUnits); + + // add to the table + t = new QTableWidgetItem; + t->setText(value); + t->setFlags(t->flags() & (~Qt::ItemIsEditable)); + table->setItem(counter, i + 5, t); + } + + // Date Range name + t = new QTableWidgetItem; + t->setText(x.name); + t->setFlags(t->flags() & (~Qt::ItemIsEditable)); + table->setItem(counter, worklist.count() + 5, t); + + // align center + for (int i=3; i<(worklist.count()+5); i++) + table->item(counter,i)->setTextAlignment(Qt::AlignVCenter | Qt::AlignHCenter); + + table->setRowHeight(counter, 23); + counter++; + } + + table->resizeColumnsToContents(); // set columns to fit +#if QT_VERSION > 0x050000 // fix the first two if we can + for (int i=0; ihorizontalHeader()->setSectionResizeMode(i, QHeaderView::Fixed); + } else { + table->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Interactive); + } + } +#else + table->horizontalHeader()->setResizeMode(QHeaderView::Interactive); +#endif + table->horizontalHeader()->setStretchLastSection(true); } } @@ -283,7 +413,7 @@ ComparePane::dragEnterEvent(QDragEnterEvent *event) } void -ComparePane::dragLeaveEvent(QDragLeaveEvent *event) +ComparePane::dragLeaveEvent(QDragLeaveEvent *) { // we might consider hiding on this? } @@ -348,7 +478,7 @@ ComparePane::dropEvent(QDropEvent *event) // construct a ridefile for the interval - //XXX RideFile *data; + // RideFile *data; add.data = new RideFile(ride->startTime(), ride->recIntSecs()); add.data->context = context; @@ -404,7 +534,51 @@ ComparePane::dropEvent(QDropEvent *event) } else { // SEASONS - //XXX not written yet! + int count; + + QList newOnes; + + // lets get the basic data + stream >> count; + for (int i=0; i> add.name; + stream >> add.start; + stream >> add.end; + stream >> add.days; + + // get summary metrics for the season + // FROM THE SOURCE CONTEXT + // WE DON'T FETCH BESTS -- THEY NEED TO BE DONE AS NEEDED + add.metrics = sourceContext->athlete->metricDB->getAllMetricsFor(QDateTime(add.start, QTime()),QDateTime(add.end, QTime())); + add.measures = sourceContext->athlete->metricDB->getAllMeasuresFor(QDateTime(add.start, QTime()),QDateTime(add.end, QTime())); + + // just use standard colors and cycle round + // we will of course repeat, but the user can + // just edit them using the button + add.color = standardColors.at((i + context->compareDateRanges.count()) % standardColors.count()); + + // even empty date ranges are valid + newOnes << add; + + } + // how many we get ? + if (newOnes.count()) { + + context->compareDateRanges.append(newOnes); + + // refresh the table to reflect the new list + refreshTable(); + + // let all the charts know + context->notifyCompareDateRangesChanged(); + } } } diff --git a/src/Context.cpp b/src/Context.cpp index a4554023f..a240eab84 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -25,6 +25,7 @@ Context::Context(MainWindow *mainWindow) ride = NULL; workout = NULL; isfiltered = ishomefiltered = false; + isCompareIntervals = isCompareDateRanges = true; } const RideFile * diff --git a/src/Context.h b/src/Context.h index ca6c9ca4e..9f9f8bb17 100644 --- a/src/Context.h +++ b/src/Context.h @@ -23,6 +23,7 @@ #include "RealtimeData.h" // for class RealtimeData #include "SpecialFields.h" // for class RealtimeData #include "CompareInterval.h" // what intervals are being compared? +#include "CompareDateRange.h" // what intervals are being compared? class RideFile; class RideItem; @@ -74,6 +75,9 @@ class Context : public QObject bool isCompareIntervals; QList compareIntervals; + bool isCompareDateRanges; + QList compareDateRanges; + // ********************************************* // APPLICATION EVENTS // ********************************************* @@ -117,6 +121,9 @@ class Context : public QObject void notifyCompareIntervals(bool state) { isCompareIntervals = state; emit compareIntervalsStateChanged(state); } void notifyCompareIntervalsChanged() { emit compareIntervalsChanged(); } + void notifyCompareDateRanges(bool state) { isCompareDateRanges = state; emit compareDateRangesStateChanged(state); } + void notifyCompareDateRangesChanged() { emit compareDateRangesChanged(); } + signals: // global filter changed @@ -152,5 +159,7 @@ class Context : public QObject // comparing things void compareIntervalsStateChanged(bool); void compareIntervalsChanged(); + void compareDateRangesStateChanged(bool); + void compareDateRangesChanged(); }; #endif // _GC_Context_h diff --git a/src/HomeWindow.cpp b/src/HomeWindow.cpp index 6d20aed49..3363f2309 100644 --- a/src/HomeWindow.cpp +++ b/src/HomeWindow.cpp @@ -413,7 +413,7 @@ HomeWindow::styleChanged(int id) } void -HomeWindow::dragEnterEvent(QDragEnterEvent *event) +HomeWindow::dragEnterEvent(QDragEnterEvent *) { #if 0 // drah and drop chart no longer part of the UX if (event->mimeData()->formats().contains("application/x-qabstractitemmodeldatalist")) { @@ -447,7 +447,7 @@ HomeWindow::appendChart(GcWinID id) } void -HomeWindow::dropEvent(QDropEvent *event) +HomeWindow::dropEvent(QDropEvent *) { #if 0 // drah and drop chart no longer part of the UX QStandardItemModel model; diff --git a/src/Season.cpp b/src/Season.cpp index efe024ac6..26352d395 100644 --- a/src/Season.cpp +++ b/src/Season.cpp @@ -408,11 +408,15 @@ SeasonTreeView::dropEvent(QDropEvent* event) int idx1 = invisibleRootItem()->indexOfChild(item); int idx2 = indexAt(event->pos()).row(); - // finalise drop event - QTreeWidget::dropEvent(event); + // don't move temp 'system generated' date ranges! + if (context->athlete->seasons->seasons[idx1].type != Season::temporary) { - // emit the itemMoved signal - Q_EMIT itemMoved(item, idx1, idx2); + // finalise drop event + QTreeWidget::dropEvent(event); + + // emit the itemMoved signal + Q_EMIT itemMoved(item, idx1, idx2); + } } QStringList diff --git a/src/Tab.cpp b/src/Tab.cpp index aff91821c..3d3ef5a93 100644 --- a/src/Tab.cpp +++ b/src/Tab.cpp @@ -105,7 +105,7 @@ Tab::~Tab() RideNavigator *Tab::rideNavigator() { - analysisView->rideNavigator(); + return analysisView->rideNavigator(); } void diff --git a/src/TabView.h b/src/TabView.h index 3faac46cc..6511dfce7 100644 --- a/src/TabView.h +++ b/src/TabView.h @@ -137,8 +137,7 @@ class ViewSplitter : public QSplitter public: ViewSplitter(Qt::Orientation orientation, QString name, TabView *parent=0) : - orientation(orientation), name(name), tabView(parent), showForDrag(false), - QSplitter(orientation, parent) { + QSplitter(orientation, parent), orientation(orientation), name(name), tabView(parent), showForDrag(false) { setAcceptDrops(true); qRegisterMetaType("hpos"); } @@ -163,7 +162,7 @@ protected: } } - virtual void dragLeaveEvent(QDragLeaveEvent *event) { + virtual void dragLeaveEvent(QDragLeaveEvent *) { int X = this->mapFromGlobal(QCursor::pos()).x(); int Y = this->mapFromGlobal(QCursor::pos()).y(); diff --git a/src/src.pro b/src/src.pro index 7b33efbd6..7f6811d84 100644 --- a/src/src.pro +++ b/src/src.pro @@ -277,6 +277,7 @@ HEADERS += \ Colors.h \ ColorButton.h \ CommPort.h \ + CompareDateRange.h \ CompareInterval.h \ ComparePane.h \ Computrainer.h \ @@ -468,6 +469,7 @@ SOURCES += \ Colors.cpp \ ColorButton.cpp \ CommPort.cpp \ + CompareDateRange.cpp \ CompareInterval.cpp \ ComparePane.cpp \ Computrainer.cpp \