diff --git a/src/Athlete.cpp b/src/Athlete.cpp index a26a5f626..f3d0ae9e3 100644 --- a/src/Athlete.cpp +++ b/src/Athlete.cpp @@ -153,9 +153,15 @@ Athlete::Athlete(Context *context, const QDir &homeDir) sqlModel->setTable("metrics"); sqlModel->setEditStrategy(QSqlTableModel::OnManualSubmit); - sqlIntervalsModel = new QSqlTableModel(this, metricDB->db()->connection()); - sqlIntervalsModel->setTable("interval_metrics"); - sqlIntervalsModel->setEditStrategy(QSqlTableModel::OnManualSubmit); + sqlRouteIntervalsModel = new QSqlTableModel(this, metricDB->db()->connection()); + sqlRouteIntervalsModel->setTable("interval_metrics"); + sqlRouteIntervalsModel->setFilter("type='Route'"); + sqlRouteIntervalsModel->setEditStrategy(QSqlTableModel::OnManualSubmit); + + sqlBestIntervalsModel = new QSqlTableModel(this, metricDB->db()->connection()); + sqlBestIntervalsModel->setTable("interval_metrics"); + sqlBestIntervalsModel->setFilter("type='Best'"); + sqlBestIntervalsModel->setEditStrategy(QSqlTableModel::OnManualSubmit); // Downloaders withingsDownload = new WithingsDownload(context); @@ -251,7 +257,8 @@ Athlete::~Athlete() // close the db connection (but clear models first!) delete sqlModel; - delete sqlIntervalsModel; + delete sqlRouteIntervalsModel; + delete sqlBestIntervalsModel; delete metricDB; #ifdef GC_HAVE_LUCENE diff --git a/src/Athlete.h b/src/Athlete.h index 7969c7cce..80a359be5 100644 --- a/src/Athlete.h +++ b/src/Athlete.h @@ -82,7 +82,8 @@ class Athlete : public QObject bool isclean; MetricAggregator *metricDB; QSqlTableModel *sqlModel; - QSqlTableModel *sqlIntervalsModel; + QSqlTableModel *sqlRouteIntervalsModel; + QSqlTableModel *sqlBestIntervalsModel; RideMetadata *rideMetadata_; Seasons *seasons; QList PDEstimates; diff --git a/src/DBAccess.cpp b/src/DBAccess.cpp index 47cad2015..3e6023a83 100644 --- a/src/DBAccess.cpp +++ b/src/DBAccess.cpp @@ -1035,6 +1035,17 @@ DBAccess::deleteIntervalsForRide(QString filename) return query.exec(); } +bool +DBAccess::deleteIntervalsForTypeAndGroupName(QString type, QString groupName) +{ + QSqlQuery query(db->database(sessionid)); + + query.prepare("DELETE FROM interval_metrics WHERE type = :type AND groupName = :groupName;"); + query.addBindValue(type); // type + query.addBindValue(groupName); // groupName, + return query.exec(); +} + bool DBAccess::getInterval(QString filename, QString type, QString groupName, int start, SummaryMetrics &summaryMetrics, QColor&color) { diff --git a/src/DBAccess.h b/src/DBAccess.h index 285aee47d..e3d0d9bcb 100644 --- a/src/DBAccess.h +++ b/src/DBAccess.h @@ -89,6 +89,7 @@ class DBAccess bool getInterval(QString filename, QString type, QString groupName, int start, SummaryMetrics &summaryMetrics, QColor&color); bool getIntervalForRide(QString); bool deleteIntervalsForRide(QString filename); + bool deleteIntervalsForTypeAndGroupName(QString type, QString groupName); QList getAllDates(); QList getAllSeasons(); diff --git a/src/IntervalNavigator.cpp b/src/IntervalNavigator.cpp index 82714fca9..933d75788 100644 --- a/src/IntervalNavigator.cpp +++ b/src/IntervalNavigator.cpp @@ -32,7 +32,7 @@ #include #include -IntervalNavigator::IntervalNavigator(Context *context, bool mainwindow) : context(context), active(false), _groupBy(-1) +IntervalNavigator::IntervalNavigator(Context *context, QString type, bool mainwindow) : context(context), type(type), active(false), _groupBy(-1) { // get column headings // default column layouts etc @@ -54,11 +54,20 @@ IntervalNavigator::IntervalNavigator(Context *context, bool mainwindow) : contex if (mainwindow) mainLayout->setContentsMargins(0,0,0,0); else mainLayout->setContentsMargins(2,2,2,2); // so we can resize! - context->athlete->sqlIntervalsModel->select(); - while (context->athlete->sqlIntervalsModel->canFetchMore(QModelIndex())) context->athlete->sqlIntervalsModel->fetchMore(QModelIndex()); + if (type == "Best") + sqlModel = context->athlete->sqlBestIntervalsModel; + else + sqlModel= context->athlete->sqlRouteIntervalsModel; + + //QString filter = QString("type='%1'").arg(type); + //context->athlete->sqlIntervalsModel->setFilter(filter); + sqlModel->select(); + + + while (sqlModel->canFetchMore(QModelIndex())) sqlModel->fetchMore(QModelIndex()); searchFilter = new IntervalSearchFilter(this); - searchFilter->setSourceModel(context->athlete->sqlIntervalsModel); // filter out/in search results + searchFilter->setSourceModel(sqlModel); // filter out/in search results groupByModel = new IntervalGroupByModel(this); @@ -252,9 +261,11 @@ IntervalNavigator::configChanged() void IntervalNavigator::refresh() { - context->athlete->sqlIntervalsModel->select(); - while (context->athlete->sqlIntervalsModel->canFetchMore(QModelIndex())) - context->athlete->sqlIntervalsModel->fetchMore(QModelIndex()); + //QString filter = QString("type='%1'").arg(type); + //context->athlete->sqlIntervalsModel->setFilter(filter); + sqlModel->select(); + while (sqlModel->canFetchMore(QModelIndex())) + sqlModel->fetchMore(QModelIndex()); active=false; diff --git a/src/IntervalNavigator.h b/src/IntervalNavigator.h index d8edec363..cea8b604a 100644 --- a/src/IntervalNavigator.h +++ b/src/IntervalNavigator.h @@ -74,7 +74,7 @@ class IntervalNavigator : public GcWindow friend class ::SearchBox; public: - IntervalNavigator(Context *, bool mainwindow = false); + IntervalNavigator(Context *, QString type, bool mainwindow = false); ~IntervalNavigator(); void borderMenu(const QPoint &pos); @@ -156,6 +156,8 @@ class IntervalNavigator : public GcWindow QMap nameMap; QMap columnMetrics; + QString type; + private: bool active; bool mainwindow; @@ -165,6 +167,8 @@ class IntervalNavigator : public GcWindow IntervalNavigatorCellDelegate *delegate; QVBoxLayout *mainLayout; + QSqlTableModel *sqlModel; + // properties int _sortByIndex; int _sortByOrder; diff --git a/src/IntervalSidebar.cpp b/src/IntervalSidebar.cpp index 931b974b4..f02c89eba 100644 --- a/src/IntervalSidebar.cpp +++ b/src/IntervalSidebar.cpp @@ -47,10 +47,15 @@ IntervalSidebar::IntervalSidebar(Context *context) : QWidget(context->mainWindow mainLayout->addWidget(splitter); // Route - routeNavigator = new IntervalNavigator(context, true); + routeNavigator = new IntervalNavigator(context, "Route", true); routeNavigator->setProperty("nomenu", true); groupByMapper = NULL; + // Bests + bestNavigator = new IntervalNavigator(context, "Best", true); + bestNavigator->setProperty("nomenu", true); + + // retrieve settings (properties are saved when we close the window) if (appsettings->cvalue(context->athlete->cyclist, GC_ROUTEHEADINGS, "").toString() != "") { routeNavigator->setSortByIndex(appsettings->cvalue(context->athlete->cyclist, GC_ROUTESORTBY).toInt()); @@ -59,6 +64,12 @@ IntervalSidebar::IntervalSidebar(Context *context) : QWidget(context->mainWindow routeNavigator->setColumns(appsettings->cvalue(context->athlete->cyclist, GC_ROUTEHEADINGS).toString()); routeNavigator->setWidths(appsettings->cvalue(context->athlete->cyclist, GC_ROUTEHEADINGWIDTHS).toString()); } + if (appsettings->cvalue(context->athlete->cyclist, GC_BESTHEADINGS, "").toString() != "") { + bestNavigator->setSortByIndex(appsettings->cvalue(context->athlete->cyclist, GC_BESTSORTBY).toInt()); + bestNavigator->setSortByOrder(appsettings->cvalue(context->athlete->cyclist, GC_BESTSORTBYORDER).toInt()); + bestNavigator->setColumns(appsettings->cvalue(context->athlete->cyclist, GC_BESTHEADINGS).toString()); + bestNavigator->setWidths(appsettings->cvalue(context->athlete->cyclist, GC_BESTHEADINGWIDTHS).toString()); + } QWidget *routeWidget = new QWidget(this); routeWidget->setContentsMargins(0,0,0,0); @@ -76,7 +87,27 @@ IntervalSidebar::IntervalSidebar(Context *context) : QWidget(context->mainWindow connect(routeAction, SIGNAL(triggered(void)), this, SLOT(routePopup())); routeItem->addWidget(routeWidget); + + + + QWidget *bestWidget = new QWidget(this); + bestWidget->setContentsMargins(0,0,0,0); +#ifndef Q_OS_MAC // not on mac thanks + bestWidget->setStyleSheet("padding: 0px; border: 0px; margin: 0px;"); +#endif + QVBoxLayout *bestLayout = new QVBoxLayout(bestWidget); + bestLayout->setSpacing(0); + bestLayout->setContentsMargins(0,0,0,0); + bestLayout->addWidget(bestNavigator); + + bestItem = new GcSplitterItem(tr("Bests"), iconFromPNG(":images/sidebar/folder.png"), this); + QAction *bestAction = new QAction(iconFromPNG(":images/sidebar/extra.png"), tr("Menu"), this); + bestItem->addAction(bestAction); + connect(bestAction, SIGNAL(triggered(void)), this, SLOT(bestPopup())); + bestItem->addWidget(bestWidget); + splitter->addWidget(routeItem); + splitter->addWidget(bestItem); splitter->prepare(context->athlete->cyclist, "interval"); @@ -84,8 +115,9 @@ IntervalSidebar::IntervalSidebar(Context *context) : QWidget(context->mainWindow connect(context, SIGNAL(configChanged()), this, SLOT(configChanged())); // right click menus... - connect(routeNavigator,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showActivityMenu(const QPoint &))); + connect(routeNavigator,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showRouteMenu(const QPoint &))); //connect(context->athlete->intervalWidget,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showIntervalMenu(const QPoint &))); + connect(bestNavigator,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showBestMenu(const QPoint &))); connect (context, SIGNAL(filterChanged()), this, SLOT(filterChanged())); @@ -108,6 +140,9 @@ IntervalSidebar::configChanged() routeNavigator->tableView->viewport()->setPalette(GCColor::palette()); routeNavigator->tableView->viewport()->setStyleSheet(QString("background: %1;").arg(GColor(CPLOTBACKGROUND).name())); + bestNavigator->tableView->viewport()->setPalette(GCColor::palette()); + bestNavigator->tableView->viewport()->setStyleSheet(QString("background: %1;").arg(GColor(CPLOTBACKGROUND).name())); + // interval tree context->athlete->intervalWidget->setPalette(GCColor::palette()); context->athlete->intervalWidget->setStyleSheet(GCColor::stylesheet()); @@ -126,12 +161,14 @@ void IntervalSidebar::setFilter(QStringList filter) { routeNavigator->searchStrings(filter); + bestNavigator->searchStrings(filter); } void IntervalSidebar::clearFilter() { routeNavigator->clearSearch(); + bestNavigator->clearSearch(); } /*********************************************************************** @@ -141,11 +178,11 @@ void IntervalSidebar::routePopup() { // set the point for the menu and call below - showActivityMenu(this->mapToGlobal(QPoint(routeItem->pos().x()+routeItem->width()-20, routeItem->pos().y()))); + showRouteMenu(this->mapToGlobal(QPoint(routeItem->pos().x()+routeItem->width()-20, routeItem->pos().y()))); } void -IntervalSidebar::showActivityMenu(const QPoint &pos) +IntervalSidebar::showRouteMenu(const QPoint &pos) { if (context->athlete->treeWidget->selectedItems().size() == 0) return; //none selected! @@ -185,3 +222,37 @@ IntervalSidebar::showActivityMenu(const QPoint &pos) menu.exec(pos); } } + +void +IntervalSidebar::bestPopup() +{ + // set the point for the menu and call below + showBestMenu(this->mapToGlobal(QPoint(routeItem->pos().x()+routeItem->width()-20, routeItem->pos().y()))); +} + +void +IntervalSidebar::showBestMenu(const QPoint &pos) +{ + if (context->athlete->treeWidget->selectedItems().size() == 0) return; //none selected! + + RideItem *rideItem = (RideItem *)context->athlete->treeWidget->selectedItems().first(); + if (rideItem != NULL && rideItem->text(0) != tr("All Rides")) { + QMenu menu(context->athlete->treeWidget); + + // ride navigator stuff + QAction *colChooser = new QAction(tr("Show Column Chooser"), context->athlete->treeWidget); + connect(colChooser, SIGNAL(triggered(void)), bestNavigator, SLOT(showColumnChooser())); + menu.addAction(colChooser); + + // expand / collapse + QAction *expandAll = new QAction(tr("Expand All"), context->athlete->treeWidget); + connect(expandAll, SIGNAL(triggered(void)), bestNavigator->tableView, SLOT(expandAll())); + menu.addAction(expandAll); + + // expand / collapse + QAction *collapseAll = new QAction(tr("Collapse All"), context->athlete->treeWidget); + connect(collapseAll, SIGNAL(triggered(void)), bestNavigator->tableView, SLOT(collapseAll())); + menu.addAction(collapseAll); + menu.exec(pos); + } +} diff --git a/src/IntervalSidebar.h b/src/IntervalSidebar.h index 200d87977..a48be5a31 100644 --- a/src/IntervalSidebar.h +++ b/src/IntervalSidebar.h @@ -39,6 +39,7 @@ class IntervalSidebar : public QWidget void close(); void setWidth(int x) { routeNavigator->setWidth(x); } IntervalNavigator *routeNavigator; + IntervalNavigator *bestNavigator; signals: @@ -53,7 +54,9 @@ class IntervalSidebar : public QWidget // analysis menu void routePopup(); - void showActivityMenu(const QPoint &pos); + void showRouteMenu(const QPoint &pos); + void bestPopup(); + void showBestMenu(const QPoint &pos); // interval menu /*void intervalPopup(); @@ -86,8 +89,8 @@ class IntervalSidebar : public QWidget QSignalMapper *groupByMapper; GcSplitterItem *routeItem; + GcSplitterItem *bestItem; - GcSplitterItem *intervalItem; QSplitter *intervalSplitter; IntervalSummaryWindow *intervalSummaryWindow; IntervalItem *activeInterval; // currently active for context menu popup diff --git a/src/MetricAggregator.cpp b/src/MetricAggregator.cpp index 0a75d5b3b..d27d5c942 100644 --- a/src/MetricAggregator.cpp +++ b/src/MetricAggregator.cpp @@ -330,6 +330,8 @@ void MetricAggregator::refreshMetrics(QDateTime forceAfterThisDate) #endif context->athlete->isclean = true; + refreshBestIntervals(); + // clear out the estimates if something changed! if (updates) context->athlete->PDEstimates.clear(); @@ -359,6 +361,9 @@ MetricAggregator::refreshBestIntervals() QString symbol = "20m_critical_power"; int n = 10; + // Remove old interval + dbaccess->deleteIntervalsForTypeAndGroupName("Best", "Best 20min"); + // get all fields... QList allRides = context->athlete->metricDB->getAllMetricsFor(QDateTime(), QDateTime()); diff --git a/src/Settings.h b/src/Settings.h index df110450f..bd4d1e29b 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -155,19 +155,24 @@ #define GC_DPFHRS_MAX "dataprocess/fixhrspikes/max" // ride navigator -#define GC_NAVHEADINGS "navigator/headings" +#define GC_NAVHEADINGS "navigator/headings" #define GC_NAVHEADINGWIDTHS "bavigator/headingwidths" #define GC_NAVGROUPBY "navigator/groupby" #define GC_SORTBY "navigator/sortby" #define GC_SORTBYORDER "navigator/sortbyorder" -// route navigator +// route interval navigator #define GC_ROUTEHEADINGS "routenavigator/headings" #define GC_ROUTEHEADINGWIDTHS "routenavigator/headingwidths" -#define GC_ROUTEGROUPBY "routenavigator/groupby" #define GC_ROUTESORTBY "routenavigator/sortby" #define GC_ROUTESORTBYORDER "routenavigator/sortbyorder" +// best interval navigator +#define GC_BESTHEADINGS "bestnavigator/headings" +#define GC_BESTHEADINGWIDTHS "bestnavigator/headingwidths" +#define GC_BESTSORTBY "bestnavigator/sortby" +#define GC_BESTSORTBYORDER "bestnavigator/sortbyorder" + //Twitter oauth keys #define GC_TWITTER_CONSUMER_KEY "qbbmhDt8bG8ZBcT3r9nYw" //< consumer key #define GC_TWITTER_CONSUMER_SECRET "IWXu2G6mQC5xvhM8V0ohA0mPTUOqAFutiuKIva3LQg" diff --git a/src/Tab.cpp b/src/Tab.cpp index 141ba7740..aed41a73d 100644 --- a/src/Tab.cpp +++ b/src/Tab.cpp @@ -115,16 +115,24 @@ Tab::~Tab() delete views; } -RideNavigator *Tab::rideNavigator() +RideNavigator * +Tab::rideNavigator() { return analysisView->rideNavigator(); } -IntervalNavigator *Tab::routeNavigator() +IntervalNavigator * +Tab::routeNavigator() { return intervalView->routeNavigator(); } +IntervalNavigator * +Tab::bestNavigator() +{ + return intervalView->bestNavigator(); +} + void Tab::close() { diff --git a/src/Tab.h b/src/Tab.h index 79ba9258b..6b4aef696 100644 --- a/src/Tab.h +++ b/src/Tab.h @@ -41,6 +41,7 @@ class Tab: public QWidget RideNavigator *rideNavigator(); // to get logical headings IntervalNavigator *routeNavigator(); // to get logical headings + IntervalNavigator *bestNavigator(); // to get logical headings protected: diff --git a/src/TabView.cpp b/src/TabView.cpp index 9cbc96f57..75a648eca 100644 --- a/src/TabView.cpp +++ b/src/TabView.cpp @@ -384,9 +384,12 @@ TabView::sidebarChanged() // being adjusted as the splitter gets resized and reset if (type == VIEW_ANALYSIS && active == false && context->tab->rideNavigator()->geometry().width() != 100) context->tab->rideNavigator()->setWidth(context->tab->rideNavigator()->geometry().width()); - if (type == VIEW_INTERVAL && active == false && context->tab->routeNavigator()->geometry().width() != 100) + if (type == VIEW_INTERVAL && active == false && context->tab->routeNavigator()->geometry().width() != 100) { context->tab->routeNavigator()->setWidth(context->tab->routeNavigator()->geometry().width()); - + } + if (type == VIEW_INTERVAL && active == false && context->tab->bestNavigator()->geometry().width() != 100) { + context->tab->bestNavigator()->setWidth(context->tab->bestNavigator()->geometry().width()); + } setUpdatesEnabled(true); } else sidebar_->hide(); diff --git a/src/Views.cpp b/src/Views.cpp index 3015712df..3f3f79fb4 100644 --- a/src/Views.cpp +++ b/src/Views.cpp @@ -251,6 +251,12 @@ IntervalView::routeNavigator() return intervalSidebar->routeNavigator; } +IntervalNavigator* +IntervalView::bestNavigator() +{ + return intervalSidebar->bestNavigator; +} + IntervalView::~IntervalView() { } diff --git a/src/Views.h b/src/Views.h index 799bda14d..938b16141 100644 --- a/src/Views.h +++ b/src/Views.h @@ -120,6 +120,7 @@ class IntervalView : public TabView void setRide(RideItem*ride); IntervalNavigator *routeNavigator(); + IntervalNavigator *bestNavigator(); public slots: