mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-16 01:19:57 +00:00
Add preset charts to the LTM sidebar, and allow LTM charts to refer to sidebar for config instead of the local config. In this way we can have a chart that changes as we select different charts in the sidebar and remove the need to have lots and lots of different charts on the tabs. But it still allows for the old way of doing things -- just adds another type of flexibility -- for instance a user could add charts with set date ranges but no specific curve setup so they could havethis year, this month, last month charts that change as charts are selected in the sidebar. This first commit just adds the sidebar item, next we will update the LTM chart to refer to it in part 2 and then in part 3 we will add functions on the sidebar to manage the chart presets in the same way as in LTMTool.
1254 lines
42 KiB
C++
1254 lines
42 KiB
C++
/*
|
|
* Copyright (c) 2012 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 "LTMSidebar.h"
|
|
#include "MainWindow.h"
|
|
#include "Context.h"
|
|
#include "Athlete.h"
|
|
#include "Settings.h"
|
|
#include "Units.h"
|
|
|
|
#include <QApplication>
|
|
#include <QWebView>
|
|
#include <QWebFrame>
|
|
#include <QScrollBar>
|
|
#include <QtGui>
|
|
#include <QStyle>
|
|
#include <QStyleFactory>
|
|
#include <QScrollBar>
|
|
|
|
// seasons support
|
|
#include "Season.h"
|
|
#include "SeasonParser.h"
|
|
#include <QXmlInputSource>
|
|
#include <QXmlSimpleReader>
|
|
|
|
// named searchs
|
|
#include "NamedSearch.h"
|
|
#include "DataFilter.h"
|
|
#ifdef GC_HAVE_LUCENE
|
|
#include "Lucene.h"
|
|
#endif
|
|
|
|
// metadata support
|
|
#include "RideMetadata.h"
|
|
#include "SpecialFields.h"
|
|
|
|
#include "MetricAggregator.h"
|
|
#include "SummaryMetrics.h"
|
|
|
|
LTMSidebar::LTMSidebar(Context *context) : QWidget(context->mainWindow), context(context), active(false),
|
|
isqueryfilter(false), isautofilter(false)
|
|
{
|
|
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setContentsMargins(0,0,0,0);
|
|
mainLayout->setSpacing(0);
|
|
setContentsMargins(0,0,0,0);
|
|
|
|
seasonsWidget = new GcSplitterItem(tr("Date Ranges"), iconFromPNG(":images/sidebar/calendar.png"), this);
|
|
QAction *moreSeasonAct = new QAction(iconFromPNG(":images/sidebar/extra.png"), tr("Menu"), this);
|
|
seasonsWidget->addAction(moreSeasonAct);
|
|
connect(moreSeasonAct, SIGNAL(triggered(void)), this, SLOT(dateRangePopup(void)));
|
|
|
|
dateRangeTree = new SeasonTreeView(context); // context needed for drag/drop across contexts
|
|
allDateRanges=dateRangeTree->invisibleRootItem();
|
|
// Drop for Seasons
|
|
allDateRanges->setFlags(Qt::ItemIsEnabled | Qt::ItemIsDropEnabled);
|
|
allDateRanges->setText(0, tr("Date Ranges"));
|
|
dateRangeTree->setFrameStyle(QFrame::NoFrame);
|
|
dateRangeTree->setColumnCount(1);
|
|
dateRangeTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
dateRangeTree->header()->hide();
|
|
dateRangeTree->setIndentation(5);
|
|
dateRangeTree->expandItem(allDateRanges);
|
|
dateRangeTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
#ifdef Q_OS_MAC
|
|
dateRangeTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
QStyle *cde = QStyleFactory::create(OS_STYLE);
|
|
dateRangeTree->verticalScrollBar()->setStyle(cde);
|
|
#endif
|
|
seasonsWidget->addWidget(dateRangeTree);
|
|
|
|
// events
|
|
eventsWidget = new GcSplitterItem(tr("Events"), iconFromPNG(":images/sidebar/bookmark.png"), this);
|
|
QAction *moreEventAct = new QAction(iconFromPNG(":images/sidebar/extra.png"), tr("Menu"), this);
|
|
eventsWidget->addAction(moreEventAct);
|
|
connect(moreEventAct, SIGNAL(triggered(void)), this, SLOT(eventPopup(void)));
|
|
|
|
eventTree = new QTreeWidget;
|
|
eventTree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
allEvents = eventTree->invisibleRootItem();
|
|
allEvents->setText(0, tr("Events"));
|
|
eventTree->setFrameStyle(QFrame::NoFrame);
|
|
eventTree->setColumnCount(2);
|
|
eventTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
eventTree->header()->hide();
|
|
eventTree->setIndentation(5);
|
|
eventTree->expandItem(allEvents);
|
|
eventTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
eventTree->horizontalScrollBar()->setDisabled(true);
|
|
eventTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
#ifdef Q_OS_MAC
|
|
eventTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
cde = QStyleFactory::create(OS_STYLE);
|
|
eventTree->verticalScrollBar()->setStyle(cde);
|
|
#endif
|
|
eventsWidget->addWidget(eventTree);
|
|
|
|
// charts
|
|
chartsWidget = new GcSplitterItem(tr("Charts"), iconFromPNG(":images/sidebar/charts.png"), this);
|
|
QAction *moreChartAct = new QAction(iconFromPNG(":images/sidebar/extra.png"), tr("Menu"), this);
|
|
chartsWidget->addAction(moreChartAct);
|
|
//XXXconnect(moreEventAct, SIGNAL(triggered(void)), this, SLOT(eventPopup(void)));
|
|
|
|
chartTree = new QTreeWidget;
|
|
chartTree->setFrameStyle(QFrame::NoFrame);
|
|
allCharts = chartTree->invisibleRootItem();
|
|
allCharts->setText(0, tr("Events"));
|
|
chartTree->setColumnCount(1);
|
|
chartTree->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
chartTree->header()->hide();
|
|
chartTree->setIndentation(5);
|
|
chartTree->expandItem(allCharts);
|
|
chartTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
chartTree->horizontalScrollBar()->setDisabled(true);
|
|
chartTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
#ifdef Q_OS_MAC
|
|
chartTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
cde = QStyleFactory::create(OS_STYLE);
|
|
chartTree->verticalScrollBar()->setStyle(cde);
|
|
#endif
|
|
chartsWidget->addWidget(chartTree);
|
|
|
|
// setup for first time
|
|
presetsChanged();
|
|
|
|
// filters
|
|
#ifdef GC_HAVE_LUCENE
|
|
filtersWidget = new GcSplitterItem(tr("Filters"), iconFromPNG(":images/toolbar/filter3.png"), this);
|
|
QAction *moreFilterAct = new QAction(iconFromPNG(":images/sidebar/extra.png"), tr("Menu"), this);
|
|
filtersWidget->addAction(moreFilterAct);
|
|
connect(moreFilterAct, SIGNAL(triggered(void)), this, SLOT(filterPopup(void)));
|
|
|
|
filterTree = new QTreeWidget;
|
|
filterTree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
allFilters = filterTree->invisibleRootItem();
|
|
allFilters->setText(0, tr("Filters"));
|
|
allFilters->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
filterTree->setFrameStyle(QFrame::NoFrame);
|
|
filterTree->setColumnCount(1);
|
|
filterTree->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
filterTree->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
filterTree->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
filterTree->header()->hide();
|
|
filterTree->setIndentation(5);
|
|
filterTree->expandItem(allFilters);
|
|
filterTree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
filterTree->horizontalScrollBar()->setDisabled(true);
|
|
filterTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
#ifdef Q_OS_MAC
|
|
filterTree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
cde = QStyleFactory::create(OS_STYLE);
|
|
filterTree->verticalScrollBar()->setStyle(cde);
|
|
#endif
|
|
// we cast the filter tree and this because we use the same constructor XXX fix this!!!
|
|
filterSplitter = new GcSubSplitter(Qt::Vertical, (GcSplitterControl*)filterTree, (GcSplitter*)this, true);
|
|
filtersWidget->addWidget(filterSplitter);
|
|
#endif
|
|
|
|
seasons = context->athlete->seasons;
|
|
resetSeasons(); // reset the season list
|
|
resetFilters(); // reset the filters list
|
|
|
|
autoFilterMenu = new QMenu(tr("Autofilter"),this);
|
|
configChanged(); // will reset the metric tree and the autofilters
|
|
|
|
splitter = new GcSplitter(Qt::Vertical);
|
|
splitter->addWidget(seasonsWidget); // goes alongside events
|
|
splitter->addWidget(eventsWidget); // goes alongside date ranges
|
|
#ifdef GC_HAVE_LUCENE
|
|
splitter->addWidget(filtersWidget);
|
|
#endif
|
|
splitter->addWidget(chartsWidget); // for charts that 'use sidebar chart' charts ! (confusing or what?!)
|
|
|
|
GcSplitterItem *summaryWidget = new GcSplitterItem(tr("Summary"), iconFromPNG(":images/sidebar/dashboard.png"), this);
|
|
|
|
summary = new QWebView(this);
|
|
summary->setContentsMargins(0,0,0,0);
|
|
summary->page()->view()->setContentsMargins(0,0,0,0);
|
|
summary->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);
|
|
summary->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
summary->setAcceptDrops(false);
|
|
|
|
summaryWidget->addWidget(summary);
|
|
|
|
QFont defaultFont; // mainwindow sets up the defaults.. we need to apply
|
|
summary->settings()->setFontSize(QWebSettings::DefaultFontSize, defaultFont.pointSize());
|
|
summary->settings()->setFontFamily(QWebSettings::StandardFont, defaultFont.family());
|
|
splitter->addWidget(summaryWidget);
|
|
|
|
mainLayout->addWidget(splitter);
|
|
|
|
splitter->prepare(context->athlete->cyclist, "LTM");
|
|
|
|
// our date ranges
|
|
connect(dateRangeTree,SIGNAL(itemSelectionChanged()), this, SLOT(dateRangeTreeWidgetSelectionChanged()));
|
|
connect(dateRangeTree,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(dateRangePopup(const QPoint &)));
|
|
connect(dateRangeTree,SIGNAL(itemChanged(QTreeWidgetItem *,int)), this, SLOT(dateRangeChanged(QTreeWidgetItem*, int)));
|
|
connect(dateRangeTree,SIGNAL(itemMoved(QTreeWidgetItem *,int, int)), this, SLOT(dateRangeMoved(QTreeWidgetItem*, int, int)));
|
|
#ifdef GC_HAVE_LUCENE
|
|
connect(filterTree,SIGNAL(itemSelectionChanged()), this, SLOT(filterTreeWidgetSelectionChanged()));
|
|
#endif
|
|
connect(eventTree,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(eventPopup(const QPoint &)));
|
|
|
|
// GC signal
|
|
#ifdef GC_HAVE_LUCENE
|
|
connect(context->athlete->metricDB, SIGNAL(dataChanged()), this, SLOT(autoFilterRefresh()));
|
|
#endif
|
|
connect(context, SIGNAL(configChanged()), this, SLOT(configChanged()));
|
|
connect(seasons, SIGNAL(seasonsChanged()), this, SLOT(resetSeasons()));
|
|
connect(context->athlete, SIGNAL(namedSearchesChanged()), this, SLOT(resetFilters()));
|
|
|
|
connect(this, SIGNAL(dateRangeChanged(DateRange)), this, SLOT(setSummary(DateRange)));
|
|
connect(context, SIGNAL(presetsChanged()), this, SLOT(presetsChanged()));
|
|
|
|
// let everyone know what date range we are starting with
|
|
dateRangeTreeWidgetSelectionChanged();
|
|
|
|
// setup colors
|
|
configChanged();
|
|
}
|
|
|
|
void
|
|
LTMSidebar::presetsChanged()
|
|
{
|
|
// rebuild the preset chart list as the presets have changed
|
|
chartTree->clear();
|
|
foreach(LTMSettings chart, context->athlete->presets) {
|
|
QTreeWidgetItem *add;
|
|
add = new QTreeWidgetItem(chartTree->invisibleRootItem());
|
|
add->setFlags(add->flags() | Qt::ItemIsEditable);
|
|
add->setText(0, chart.name);
|
|
}
|
|
chartTree->setCurrentItem(chartTree->invisibleRootItem()->child(0));
|
|
}
|
|
|
|
void
|
|
LTMSidebar::configChanged()
|
|
{
|
|
seasonsWidget->setStyleSheet(GCColor::stylesheet());
|
|
eventsWidget->setStyleSheet(GCColor::stylesheet());
|
|
chartsWidget->setStyleSheet(GCColor::stylesheet());
|
|
#ifdef GC_HAVE_LUCENE
|
|
filtersWidget->setStyleSheet(GCColor::stylesheet());
|
|
#endif
|
|
|
|
setAutoFilterMenu();
|
|
|
|
// set or reset the autofilter widgets
|
|
autoFilterChanged();
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
* Selections Made
|
|
*----------------------------------------------------------------------*/
|
|
|
|
void
|
|
LTMSidebar::dateRangeTreeWidgetSelectionChanged()
|
|
{
|
|
if (active == true) return;
|
|
|
|
const Season *dateRange = NULL;
|
|
|
|
if (dateRangeTree->selectedItems().isEmpty()) dateRange = NULL;
|
|
else {
|
|
QTreeWidgetItem *which = dateRangeTree->selectedItems().first();
|
|
if (which != allDateRanges) {
|
|
dateRange = &seasons->seasons.at(allDateRanges->indexOfChild(which));
|
|
} else {
|
|
dateRange = NULL;
|
|
}
|
|
}
|
|
|
|
if (dateRange) {
|
|
int i;
|
|
// clear events - we need to add for currently selected season
|
|
for (i=allEvents->childCount(); i > 0; i--) {
|
|
delete allEvents->takeChild(0);
|
|
}
|
|
|
|
// add this seasons events
|
|
for (i=0; i <dateRange->events.count(); i++) {
|
|
SeasonEvent event = dateRange->events.at(i);
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(allEvents);
|
|
add->setText(0, event.name);
|
|
add->setText(1, event.date.toString("MMM d, yyyy"));
|
|
}
|
|
|
|
// make sure they fit
|
|
eventTree->header()->resizeSections(QHeaderView::ResizeToContents);
|
|
appsettings->setCValue(context->athlete->cyclist, GC_LTM_LAST_DATE_RANGE, dateRange->id().toString());
|
|
|
|
}
|
|
|
|
// Let the view know its changed....
|
|
if (dateRange) emit dateRangeChanged(DateRange(dateRange->start, dateRange->end, dateRange->name));
|
|
else emit dateRangeChanged(DateRange());
|
|
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
* Seasons stuff
|
|
*--------------------------------------------------------------------*/
|
|
|
|
void
|
|
LTMSidebar::resetSeasons()
|
|
{
|
|
if (active == true) return;
|
|
|
|
active = true;
|
|
int i;
|
|
for (i=allDateRanges->childCount(); i > 0; i--) {
|
|
delete allDateRanges->takeChild(0);
|
|
}
|
|
QString id = appsettings->cvalue(context->athlete->cyclist, GC_LTM_LAST_DATE_RANGE, seasons->seasons.at(0).id().toString()).toString();
|
|
for (i=0; i <seasons->seasons.count(); i++) {
|
|
Season season = seasons->seasons.at(i);
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(allDateRanges, season.getType());
|
|
if (season.id().toString()==id)
|
|
add->setSelected(true);
|
|
|
|
// Drag and Drop is FINE for temporary seasons -- IT IS JUST A DATE RANGE!
|
|
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
|
|
add->setText(0, season.getName());
|
|
}
|
|
|
|
active = false;
|
|
}
|
|
|
|
int
|
|
LTMSidebar::newSeason(QString name, QDate start, QDate end, int type)
|
|
{
|
|
seasons->newSeason(name, start, end, type);
|
|
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(Season::season);
|
|
item->setText(0, name);
|
|
allDateRanges->insertChild(0, item);
|
|
return 0; // always add at the top
|
|
}
|
|
|
|
void
|
|
LTMSidebar::updateSeason(int index, QString name, QDate start, QDate end, int type)
|
|
{
|
|
seasons->updateSeason(index, name, start, end, type);
|
|
allDateRanges->child(index)->setText(0, name);
|
|
}
|
|
|
|
void
|
|
LTMSidebar::dateRangePopup(QPoint pos)
|
|
{
|
|
QTreeWidgetItem *item = dateRangeTree->itemAt(pos);
|
|
if (item != NULL) {
|
|
|
|
// out of bounds or not user defined
|
|
int index = allDateRanges->indexOfChild(item);
|
|
if (index == -1 || index >= seasons->seasons.count()
|
|
|| seasons->seasons[index].getType() == Season::temporary) {
|
|
// on system date we just offer to add a Season, since its
|
|
// the only way of doing it when no seasons are defined!!
|
|
|
|
// create context menu
|
|
QMenu menu(dateRangeTree);
|
|
QAction *add = new QAction(tr("Add season"), dateRangeTree);
|
|
menu.addAction(add);
|
|
|
|
// connect menu to functions
|
|
connect(add, SIGNAL(triggered(void)), this, SLOT(addRange(void)));
|
|
|
|
// execute the menu
|
|
menu.exec(dateRangeTree->mapToGlobal(pos));
|
|
|
|
} else {
|
|
|
|
// create context menu
|
|
QMenu menu(dateRangeTree);
|
|
QAction *add = new QAction(tr("Add season"), dateRangeTree);
|
|
QAction *edit = new QAction(tr("Edit season"), dateRangeTree);
|
|
QAction *del = new QAction(tr("Delete season"), dateRangeTree);
|
|
QAction *event = new QAction(tr("Add Event"), dateRangeTree);
|
|
menu.addAction(add);
|
|
menu.addAction(edit);
|
|
menu.addAction(del);
|
|
menu.addAction(event);
|
|
|
|
// connect menu to functions
|
|
connect(add, SIGNAL(triggered(void)), this, SLOT(addRange(void)));
|
|
connect(edit, SIGNAL(triggered(void)), this, SLOT(editRange(void)));
|
|
connect(del, SIGNAL(triggered(void)), this, SLOT(deleteRange(void)));
|
|
connect(event, SIGNAL(triggered(void)), this, SLOT(addEvent(void)));
|
|
|
|
// execute the menu
|
|
menu.exec(dateRangeTree->mapToGlobal(pos));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LTMSidebar::dateRangePopup()
|
|
{
|
|
// no current season selected
|
|
if (dateRangeTree->selectedItems().isEmpty()) return;
|
|
|
|
QTreeWidgetItem *item = dateRangeTree->selectedItems().at(0);
|
|
|
|
// OK - we are working with a specific event..
|
|
QMenu menu(dateRangeTree);
|
|
QAction *add = new QAction(tr("Add season"), dateRangeTree);
|
|
menu.addAction(add);
|
|
connect(add, SIGNAL(triggered(void)), this, SLOT(addRange(void)));
|
|
|
|
if (item != NULL && allDateRanges->indexOfChild(item) != -1) {
|
|
QAction *edit = new QAction(tr("Edit season"), dateRangeTree);
|
|
QAction *del = new QAction(tr("Delete season"), dateRangeTree);
|
|
QAction *event = new QAction(tr("Add Event"), dateRangeTree);
|
|
|
|
menu.addAction(edit);
|
|
menu.addAction(del);
|
|
menu.addAction(event);
|
|
|
|
// connect menu to functions
|
|
|
|
connect(edit, SIGNAL(triggered(void)), this, SLOT(editRange(void)));
|
|
connect(del, SIGNAL(triggered(void)), this, SLOT(deleteRange(void)));
|
|
connect(event, SIGNAL(triggered(void)), this, SLOT(addEvent(void)));
|
|
}
|
|
// execute the menu
|
|
menu.exec(splitter->mapToGlobal(QPoint(seasonsWidget->pos().x()+seasonsWidget->width()-20,
|
|
seasonsWidget->pos().y())));
|
|
}
|
|
|
|
void
|
|
LTMSidebar::eventPopup(QPoint pos)
|
|
{
|
|
// no current season selected
|
|
if (dateRangeTree->selectedItems().isEmpty()) return;
|
|
|
|
QTreeWidgetItem *item = eventTree->itemAt(pos);
|
|
|
|
// save context - which season and event are we working with?
|
|
QTreeWidgetItem *which = dateRangeTree->selectedItems().first();
|
|
if (!which || which == allDateRanges) return;
|
|
|
|
// OK - we are working with a specific event..
|
|
QMenu menu(eventTree);
|
|
if (item != NULL && allEvents->indexOfChild(item) != -1) {
|
|
|
|
QAction *edit = new QAction(tr("Edit details"), eventTree);
|
|
QAction *del = new QAction(tr("Delete event"), eventTree);
|
|
menu.addAction(edit);
|
|
menu.addAction(del);
|
|
|
|
// connect menu to functions
|
|
connect(edit, SIGNAL(triggered(void)), this, SLOT(editEvent(void)));
|
|
connect(del, SIGNAL(triggered(void)), this, SLOT(deleteEvent(void)));
|
|
}
|
|
|
|
// we can always add, regardless of any event being selected...
|
|
QAction *addEvent = new QAction(tr("Add event"), eventTree);
|
|
menu.addAction(addEvent);
|
|
connect(addEvent, SIGNAL(triggered(void)), this, SLOT(addEvent(void)));
|
|
|
|
// execute the menu
|
|
menu.exec(eventTree->mapToGlobal(pos));
|
|
}
|
|
|
|
void
|
|
LTMSidebar::eventPopup()
|
|
{
|
|
// events are against a selected season
|
|
if (dateRangeTree->selectedItems().count() == 0) return; // need a season selected!
|
|
|
|
// and the season must be user defined not temporary
|
|
int seasonindex = allDateRanges->indexOfChild(dateRangeTree->selectedItems().first());
|
|
if (seasons->seasons[seasonindex].getType() == Season::temporary) return;
|
|
|
|
// have we selected an event?
|
|
QTreeWidgetItem *item = NULL;
|
|
if (!eventTree->selectedItems().isEmpty()) item = eventTree->selectedItems().at(0);
|
|
|
|
QMenu menu(eventTree);
|
|
|
|
// we can always add, regardless of any event being selected...
|
|
QAction *addEvent = new QAction(tr("Add event"), eventTree);
|
|
menu.addAction(addEvent);
|
|
connect(addEvent, SIGNAL(triggered(void)), this, SLOT(addEvent(void)));
|
|
|
|
if (item != NULL && allEvents->indexOfChild(item) != -1) {
|
|
|
|
QAction *edit = new QAction(tr("Edit details"), eventTree);
|
|
QAction *del = new QAction(tr("Delete event"), eventTree);
|
|
menu.addAction(edit);
|
|
menu.addAction(del);
|
|
|
|
// connect menu to functions
|
|
connect(edit, SIGNAL(triggered(void)), this, SLOT(editEvent(void)));
|
|
connect(del, SIGNAL(triggered(void)), this, SLOT(deleteEvent(void)));
|
|
}
|
|
|
|
// execute the menu
|
|
menu.exec(splitter->mapToGlobal(QPoint(eventsWidget->pos().x()+eventsWidget->width()-20, eventsWidget->pos().y())));
|
|
}
|
|
|
|
void
|
|
LTMSidebar::manageFilters()
|
|
{
|
|
#ifdef GC_HAVE_LUCENE
|
|
EditNamedSearches *editor = new EditNamedSearches(this, context);
|
|
editor->move(QCursor::pos()+QPoint(10,-200));
|
|
editor->show();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LTMSidebar::setAutoFilterMenu()
|
|
{
|
|
#ifdef GC_HAVE_LUCENE
|
|
active = true;
|
|
|
|
QStringList on = appsettings->cvalue(context->athlete->cyclist, GC_LTM_AUTOFILTERS, tr("Workout Code|Sport")).toString().split("|");
|
|
autoFilterMenu->clear();
|
|
autoFilterState.clear();
|
|
|
|
// Convert field names for Internal to Display (to work with the translated values)
|
|
SpecialFields sp;
|
|
foreach(FieldDefinition field, context->athlete->rideMetadata()->getFields()) {
|
|
|
|
if (field.tab != "" && (field.type == 0 || field.type == 2)) { // we only do text or shorttext fields
|
|
|
|
QAction *action = new QAction(sp.displayName(field.name), this);
|
|
action->setCheckable(true);
|
|
|
|
if (on.contains(sp.displayName(field.name))) action->setChecked(true);
|
|
else action->setChecked(false);
|
|
|
|
connect(action, SIGNAL(triggered()), this, SLOT(autoFilterChanged()));
|
|
|
|
// remove from tree if its already there
|
|
GcSplitterItem *item = filterSplitter->removeItem(action->text());
|
|
if (item) delete item; // will be removed from splitter too
|
|
|
|
// if you crash on this line compile with QT5.3 or higher
|
|
// or at least avoid the 5.3 RC1 release (see QTBUG-38685)
|
|
autoFilterMenu->addAction(action);
|
|
autoFilterState << false;
|
|
}
|
|
}
|
|
active = false;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LTMSidebar::autoFilterChanged()
|
|
{
|
|
if (active) return;
|
|
|
|
QString on;
|
|
|
|
// boop
|
|
int i=0;
|
|
foreach (QAction *action, autoFilterMenu->actions()) {
|
|
|
|
// activate
|
|
if (action->isChecked() && autoFilterState.at(i) == false) {
|
|
autoFilterState[i] = true;
|
|
|
|
GcSplitterItem *item = new GcSplitterItem(action->text(), QIcon(), this);
|
|
|
|
// get a tree of values
|
|
QTreeWidget *tree = new QTreeWidget(item);
|
|
tree->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
tree->setFrameStyle(QFrame::NoFrame);
|
|
tree->setColumnCount(1);
|
|
tree->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
tree->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
tree->setSelectionMode(QAbstractItemView::MultiSelection);
|
|
tree->header()->hide();
|
|
tree->setIndentation(5);
|
|
tree->expandItem(tree->invisibleRootItem());
|
|
tree->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
tree->horizontalScrollBar()->setDisabled(true);
|
|
tree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
#ifdef Q_OS_MAC
|
|
tree->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#endif
|
|
#ifdef Q_OS_WIN
|
|
QStyle *cde = QStyleFactory::create(OS_STYLE);
|
|
tree->verticalScrollBar()->setStyle(cde);
|
|
#endif
|
|
item->addWidget(tree);
|
|
filterSplitter->addWidget(item);
|
|
|
|
// Convert field names for Internal to Display (to work with the translated values)
|
|
SpecialFields sp;
|
|
// update the values available in the tree
|
|
foreach(FieldDefinition field, context->athlete->rideMetadata()->getFields()) {
|
|
if (sp.displayName(field.name) == action->text()) {
|
|
foreach (QString value, context->athlete->metricDB->db()->getDistinctValues(field)) {
|
|
if (value == "") value = tr("(blank)");
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(tree->invisibleRootItem(), 0);
|
|
|
|
// No Drag/Drop for autofilters
|
|
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
add->setText(0, value);
|
|
}
|
|
}
|
|
}
|
|
connect(tree,SIGNAL(itemSelectionChanged()), this, SLOT(autoFilterSelectionChanged()));
|
|
|
|
}
|
|
|
|
// deactivate
|
|
if (!action->isChecked() && autoFilterState.at(i) == true) {
|
|
autoFilterState[i] = false;
|
|
|
|
// remove from tree
|
|
GcSplitterItem *item = filterSplitter->removeItem(action->text());
|
|
if (item) {
|
|
// if there were items selected in the tree and we
|
|
// remove it we need to clear the filter
|
|
bool selected = static_cast<QTreeWidget*>(item->content)->selectedItems().count() > 0;
|
|
|
|
// will remove from the splitter (is only way to do this!)
|
|
delete item; // splitter deletes handle too !
|
|
|
|
// clear any results of the selection
|
|
if (selected) autoFilterSelectionChanged();
|
|
}
|
|
}
|
|
|
|
// reset the selected fields
|
|
if (action->isChecked()) {
|
|
if (on != "") on += "|";
|
|
on += action->text();
|
|
}
|
|
|
|
i++;
|
|
}
|
|
appsettings->setCValue(context->athlete->cyclist, GC_LTM_AUTOFILTERS, on);
|
|
}
|
|
|
|
void
|
|
LTMSidebar::filterTreeWidgetSelectionChanged()
|
|
{
|
|
#ifdef GC_HAVE_LUCENE
|
|
int selected = filterTree->selectedItems().count();
|
|
|
|
if (selected) {
|
|
|
|
QStringList errors, files; // results of all the selections
|
|
bool first = true;
|
|
|
|
foreach (QTreeWidgetItem *item, filterTree->selectedItems()) {
|
|
|
|
int index = filterTree->invisibleRootItem()->indexOfChild(item);
|
|
|
|
NamedSearch ns = context->athlete->namedSearches->get(index);
|
|
QStringList errors, results;
|
|
|
|
switch(ns.type) {
|
|
|
|
case NamedSearch::filter :
|
|
{
|
|
// use a data filter
|
|
DataFilter f(this, context);
|
|
errors = f.parseFilter(ns.text, &results);
|
|
}
|
|
break;
|
|
|
|
case NamedSearch::search :
|
|
{
|
|
// use clucence
|
|
Lucene s(this, context);
|
|
results = s.search(ns.text);
|
|
}
|
|
|
|
}
|
|
|
|
// lets filter the results!
|
|
if (first) files = results;
|
|
else {
|
|
QStringList filtered;
|
|
foreach(QString file, files)
|
|
if (results.contains(file))
|
|
filtered << file;
|
|
files = filtered;
|
|
}
|
|
|
|
first = false;
|
|
}
|
|
|
|
queryFilterFiles = files;
|
|
isqueryfilter = true;
|
|
|
|
} else {
|
|
|
|
queryFilterFiles.clear();
|
|
isqueryfilter = false;
|
|
}
|
|
|
|
// tell the world
|
|
filterNotify();
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LTMSidebar::filterNotify()
|
|
{
|
|
// either the auto filter or query filter
|
|
// has been updated, so we need to merge the results
|
|
// of both and then notify via context
|
|
|
|
// no filter so clear
|
|
if (isqueryfilter == false && isautofilter == false) {
|
|
context->clearHomeFilter();
|
|
} else {
|
|
|
|
if (isqueryfilter == false) {
|
|
|
|
// autofilter only
|
|
context->setHomeFilter(autoFilterFiles);
|
|
|
|
} else if (isautofilter == false) {
|
|
|
|
// queryfilter only
|
|
context->setHomeFilter(queryFilterFiles);
|
|
|
|
} else {
|
|
|
|
// both are set, so merge results
|
|
QStringList merged = autoFilterFiles.toSet().intersect(queryFilterFiles.toSet()).toList();
|
|
context->setHomeFilter(merged);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LTMSidebar::autoFilterRefresh()
|
|
{
|
|
// the data has changed so refresh the trees
|
|
for (int i=1; i<filterSplitter->count(); i++) {
|
|
|
|
GcSplitterItem *item = static_cast<GcSplitterItem*>(filterSplitter->widget(i));
|
|
QTreeWidget *tree = static_cast<QTreeWidget*>(item->content);
|
|
|
|
qDeleteAll(tree->invisibleRootItem()->takeChildren());
|
|
|
|
// translate fields back from Display Name to internal Name !
|
|
SpecialFields sp;
|
|
|
|
// what is the field?
|
|
QString fieldname = sp.internalName(item->splitterHandle->title());
|
|
|
|
// update the values available in the tree
|
|
foreach(FieldDefinition field, context->athlete->rideMetadata()->getFields()) {
|
|
if (field.name == fieldname) {
|
|
foreach (QString value, context->athlete->metricDB->db()->getDistinctValues(field)) {
|
|
if (value == "") value = tr("(blank)");
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(tree->invisibleRootItem(), 0);
|
|
|
|
// No Drag/Drop for autofilters
|
|
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
add->setText(0, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
LTMSidebar::autoFilterSelectionChanged()
|
|
{
|
|
// only fetch when we know we need to filter ..
|
|
QList<SummaryMetrics> allRides;
|
|
QSet<QString> matched;
|
|
|
|
// assume nothing to do...
|
|
isautofilter = false;
|
|
|
|
// are any auto filters applied?
|
|
for (int i=1; i<filterSplitter->count(); i++) {
|
|
|
|
GcSplitterItem *item = static_cast<GcSplitterItem*>(filterSplitter->widget(i));
|
|
QTreeWidget *tree = static_cast<QTreeWidget*>(item->content);
|
|
|
|
// are some values selected?
|
|
if (tree->selectedItems().count() > 0) {
|
|
|
|
// we have a selection!
|
|
if (isautofilter == false) {
|
|
isautofilter = true;
|
|
allRides = context->athlete->metricDB->getAllMetricsFor(QDateTime(), QDateTime());
|
|
foreach(SummaryMetrics x, allRides) matched << x.getFileName();
|
|
}
|
|
|
|
// translate fields back from Display Name to internal Name !
|
|
SpecialFields sp;
|
|
|
|
// what is the field?
|
|
QString fieldname = sp.internalName(item->splitterHandle->title());
|
|
|
|
// what values are highlighted
|
|
QStringList values;
|
|
foreach (QTreeWidgetItem *wi, tree->selectedItems()) values << sp.internalName(wi->text(0));
|
|
|
|
// get a set of filenames that match
|
|
QSet<QString> matches;
|
|
foreach(SummaryMetrics x, allRides) {
|
|
|
|
// we use XXX___XXX___XXX because it is not likely to exist
|
|
QString value = x.getText(fieldname, "XXX___XXX___XXX");
|
|
if (value == "") value = tr("(blank)"); // match blanks!
|
|
|
|
if (values.contains(value)) matches << x.getFileName();
|
|
}
|
|
|
|
// now remove items from the matched list that
|
|
// are not in my list of matches
|
|
matched = matched.intersect(matches);
|
|
}
|
|
}
|
|
|
|
// all done
|
|
autoFilterFiles = matched.toList();
|
|
|
|
// tell the world
|
|
filterNotify();
|
|
}
|
|
|
|
void
|
|
LTMSidebar::resetFilters()
|
|
{
|
|
#ifdef GC_HAVE_LUCENE
|
|
if (active == true) return;
|
|
|
|
active = true;
|
|
int i;
|
|
for (i=allFilters->childCount(); i > 0; i--) {
|
|
delete allFilters->takeChild(0);
|
|
}
|
|
|
|
foreach(NamedSearch ns, context->athlete->namedSearches->getList()) {
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(allFilters, 0);
|
|
|
|
// No Drag/Drop for filters
|
|
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
add->setText(0, ns.name);
|
|
}
|
|
|
|
active = false;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LTMSidebar::filterPopup()
|
|
{
|
|
#ifdef GC_HAVE_LUCENE
|
|
// is one selected for deletion?
|
|
int selected = filterTree->selectedItems().count();
|
|
|
|
QMenu menu(filterTree);
|
|
|
|
// we can always add, regardless of any event being selected...
|
|
QAction *addEvent = new QAction(tr("Manage Filters"), filterTree);
|
|
menu.addAction(addEvent);
|
|
connect(addEvent, SIGNAL(triggered(void)), this, SLOT(manageFilters(void)));
|
|
|
|
if (selected) {
|
|
|
|
QAction *del = new QAction(QString(tr("Delete Filter%1")).arg(selected>1 ? "s" : ""), filterTree);
|
|
menu.addAction(del);
|
|
|
|
// connect menu to functions
|
|
connect(del, SIGNAL(triggered(void)), this, SLOT(deleteFilter(void)));
|
|
}
|
|
|
|
menu.addSeparator();
|
|
menu.addMenu(autoFilterMenu);
|
|
|
|
// execute the menu
|
|
menu.exec(splitter->mapToGlobal(QPoint(filtersWidget->pos().x()+filtersWidget->width()-20, filtersWidget->pos().y())));
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LTMSidebar::deleteFilter()
|
|
{
|
|
#ifdef GC_HAVE_LUCENE
|
|
if (filterTree->selectedItems().count() <= 0) return;
|
|
|
|
active = true; // no need to reset tree when items deleted from model!
|
|
while (filterTree->selectedItems().count()) {
|
|
int index = allFilters->indexOfChild(filterTree->selectedItems().first());
|
|
|
|
// now delete!
|
|
delete allFilters->takeChild(index);
|
|
context->athlete->namedSearches->deleteNamedSearch(index);
|
|
}
|
|
active = false;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
LTMSidebar::dateRangeChanged(QTreeWidgetItem*item, int)
|
|
{
|
|
if (active == true) return;
|
|
|
|
int index = allDateRanges->indexOfChild(item);
|
|
seasons->seasons[index].setName(item->text(0));
|
|
|
|
// save changes away
|
|
active = true;
|
|
seasons->writeSeasons();
|
|
active = false;
|
|
|
|
// signal date selected changed
|
|
//dateRangeSelected(&seasons->seasons[index]);
|
|
}
|
|
|
|
void
|
|
LTMSidebar::dateRangeMoved(QTreeWidgetItem*item, int oldposition, int newposition)
|
|
{
|
|
// report the move in the seasons
|
|
seasons->seasons.move(oldposition, newposition);
|
|
|
|
// save changes away
|
|
active = true;
|
|
seasons->writeSeasons();
|
|
active = false;
|
|
|
|
// deselect actual selection
|
|
dateRangeTree->selectedItems().first()->setSelected(false);
|
|
// select the move/drop item
|
|
item->setSelected(true);
|
|
}
|
|
|
|
void
|
|
LTMSidebar::addRange()
|
|
{
|
|
Season newOne;
|
|
|
|
EditSeasonDialog dialog(context, &newOne);
|
|
|
|
if (dialog.exec()) {
|
|
|
|
active = true;
|
|
|
|
// save
|
|
seasons->seasons.insert(0, newOne);
|
|
seasons->writeSeasons();
|
|
active = false;
|
|
|
|
// signal its changed!
|
|
resetSeasons();
|
|
}
|
|
}
|
|
|
|
void
|
|
LTMSidebar::editRange()
|
|
{
|
|
// throw up modal dialog box to edit all the season
|
|
if (dateRangeTree->selectedItems().count() != 1) return;
|
|
|
|
int index = allDateRanges->indexOfChild(dateRangeTree->selectedItems().first());
|
|
|
|
if (seasons->seasons[index].getType() == Season::temporary) {
|
|
QMessageBox::warning(this, tr("Edit Season"), tr("You can only edit user defined seasons. Please select a season you have created for editing."));
|
|
return; // must be a user season
|
|
}
|
|
EditSeasonDialog dialog(context, &seasons->seasons[index]);
|
|
|
|
if (dialog.exec()) {
|
|
|
|
active = true;
|
|
|
|
// update name
|
|
dateRangeTree->selectedItems().first()->setText(0, seasons->seasons[index].getName());
|
|
|
|
// save changes away
|
|
seasons->writeSeasons();
|
|
active = false;
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
LTMSidebar::deleteRange()
|
|
{
|
|
if (dateRangeTree->selectedItems().count() != 1) return;
|
|
int index = allDateRanges->indexOfChild(dateRangeTree->selectedItems().first());
|
|
|
|
if (seasons->seasons[index].getType() == Season::temporary) {
|
|
QMessageBox::warning(this, tr("Delete Season"), tr("You can only delete user defined seasons. Please select a season you have created for deletion."));
|
|
return; // must be a user season
|
|
}
|
|
|
|
// now delete!
|
|
delete allDateRanges->takeChild(index);
|
|
seasons->deleteSeason(index);
|
|
}
|
|
|
|
void
|
|
LTMSidebar::addEvent()
|
|
{
|
|
if (dateRangeTree->selectedItems().count() == 0) {
|
|
QMessageBox::warning(this, tr("Add Event"), tr("You can only add events to user defined seasons. Please select a season you have created before adding an event."));
|
|
return; // need a season selected!
|
|
}
|
|
|
|
int seasonindex = allDateRanges->indexOfChild(dateRangeTree->selectedItems().first());
|
|
|
|
if (seasons->seasons[seasonindex].getType() == Season::temporary) {
|
|
QMessageBox::warning(this, tr("Add Event"), tr("You can only add events to user defined seasons. Please select a season you have created before adding an event."));
|
|
return; // must be a user season
|
|
}
|
|
|
|
SeasonEvent myevent("", QDate());
|
|
EditSeasonEventDialog dialog(context, &myevent);
|
|
|
|
if (dialog.exec()) {
|
|
|
|
active = true;
|
|
seasons->seasons[seasonindex].events.append(myevent);
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(allEvents);
|
|
add->setText(0, myevent.name);
|
|
add->setText(1, myevent.date.toString("MMM d, yyyy"));
|
|
|
|
// make sure they fit
|
|
eventTree->header()->resizeSections(QHeaderView::ResizeToContents);
|
|
|
|
// save changes away
|
|
seasons->writeSeasons();
|
|
active = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
LTMSidebar::deleteEvent()
|
|
{
|
|
active = true;
|
|
|
|
if (dateRangeTree->selectedItems().count()) {
|
|
|
|
int seasonindex = allDateRanges->indexOfChild(dateRangeTree->selectedItems().first());
|
|
|
|
// only delete those that are selected
|
|
if (eventTree->selectedItems().count() > 0) {
|
|
|
|
// wipe them away
|
|
foreach(QTreeWidgetItem *d, eventTree->selectedItems()) {
|
|
int index = allEvents->indexOfChild(d);
|
|
|
|
delete allEvents->takeChild(index);
|
|
seasons->seasons[seasonindex].events.removeAt(index);
|
|
}
|
|
}
|
|
|
|
// save changes away
|
|
seasons->writeSeasons();
|
|
|
|
}
|
|
active = false;
|
|
}
|
|
|
|
void
|
|
LTMSidebar::editEvent()
|
|
{
|
|
active = true;
|
|
|
|
if (dateRangeTree->selectedItems().count()) {
|
|
|
|
int seasonindex = allDateRanges->indexOfChild(dateRangeTree->selectedItems().first());
|
|
|
|
// only delete those that are selected
|
|
if (eventTree->selectedItems().count() == 1) {
|
|
|
|
QTreeWidgetItem *ours = eventTree->selectedItems().first();
|
|
int index = allEvents->indexOfChild(ours);
|
|
|
|
EditSeasonEventDialog dialog(context, &seasons->seasons[seasonindex].events[index]);
|
|
|
|
if (dialog.exec()) {
|
|
|
|
// update name
|
|
ours->setText(0, seasons->seasons[seasonindex].events[index].name);
|
|
ours->setText(1, seasons->seasons[seasonindex].events[index].date.toString("MMM d, yyyy"));
|
|
|
|
// save changes away
|
|
seasons->writeSeasons();
|
|
}
|
|
}
|
|
}
|
|
active = false;
|
|
}
|
|
|
|
void
|
|
LTMSidebar::setSummary(DateRange dateRange)
|
|
{
|
|
// where we construct the text
|
|
QString summaryText("");
|
|
|
|
// main totals
|
|
static const QStringList totalColumn = QStringList()
|
|
<< "workout_time"
|
|
<< "time_riding"
|
|
<< "total_distance"
|
|
<< "total_work"
|
|
<< "elevation_gain";
|
|
|
|
static const QStringList averageColumn = QStringList()
|
|
<< "average_speed"
|
|
<< "average_power"
|
|
<< "average_hr"
|
|
<< "average_cad";
|
|
|
|
static const QStringList maximumColumn = QStringList()
|
|
<< "max_speed"
|
|
<< "max_power"
|
|
<< "max_heartrate"
|
|
<< "max_cadence";
|
|
|
|
// user defined
|
|
QString s = appsettings->value(this, GC_SETTINGS_SUMMARY_METRICS, GC_SETTINGS_SUMMARY_METRICS_DEFAULT).toString();
|
|
|
|
// in case they were set tand then unset
|
|
if (s == "") s = GC_SETTINGS_SUMMARY_METRICS_DEFAULT;
|
|
QStringList metricColumn = s.split(",");
|
|
|
|
// what date range should we use?
|
|
QDate newFrom = dateRange.from;
|
|
QDate newTo = dateRange.to;
|
|
|
|
if (newFrom == from && newTo == to) return;
|
|
else {
|
|
|
|
// date range changed lets refresh
|
|
from = newFrom;
|
|
to = newTo;
|
|
|
|
// lets get the metrics
|
|
QList<SummaryMetrics>results = context->athlete->metricDB->getAllMetricsFor(QDateTime(from,QTime(0,0,0)), QDateTime(to, QTime(24,59,59)));
|
|
|
|
// foreach of the metrics get an aggregated value
|
|
// header of summary
|
|
summaryText = QString("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2//EN\">"
|
|
"%1"
|
|
"<html>"
|
|
"<head>"
|
|
"<title></title>"
|
|
"</head>"
|
|
"<body>"
|
|
"<center>").arg(GCColor::css());
|
|
|
|
for (int i=0; i<4; i++) {
|
|
|
|
QString aggname;
|
|
QStringList list;
|
|
|
|
switch(i) {
|
|
case 0 : // Totals
|
|
aggname = tr("Totals");
|
|
list = totalColumn;
|
|
break;
|
|
|
|
case 1 : // Averages
|
|
aggname = tr("Averages");
|
|
list = averageColumn;
|
|
break;
|
|
|
|
case 3 : // Maximums
|
|
aggname = tr("Maximums");
|
|
list = maximumColumn;
|
|
break;
|
|
|
|
case 2 : // User defined..
|
|
aggname = tr("Metrics");
|
|
list = metricColumn;
|
|
break;
|
|
|
|
}
|
|
|
|
summaryText += QString("<p><table width=\"85%\">"
|
|
"<tr>"
|
|
"<td align=\"center\" colspan=\"2\">"
|
|
"<b>%1</b>"
|
|
"</td>"
|
|
"</tr>").arg(aggname);
|
|
|
|
foreach(QString metricname, list) {
|
|
|
|
const RideMetric *metric = RideMetricFactory::instance().rideMetric(metricname);
|
|
|
|
QStringList empty; // filter list not used at present
|
|
QString value = SummaryMetrics::getAggregated(context, metricname, results, empty, false, context->athlete->useMetricUnits);
|
|
|
|
// Maximum Max and Average Average looks nasty, remove from name for display
|
|
QString s = metric ? metric->name().replace(QRegExp(tr("^(Average|Max) ")), "") : "unknown";
|
|
|
|
// don't show units for time values
|
|
if (metric && (metric->units(context->athlete->useMetricUnits) == "seconds" ||
|
|
metric->units(context->athlete->useMetricUnits) == tr("seconds") ||
|
|
metric->units(context->athlete->useMetricUnits) == "")) {
|
|
|
|
summaryText += QString("<tr><td>%1:</td><td align=\"right\"> %2</td>")
|
|
.arg(s)
|
|
.arg(value);
|
|
|
|
} else {
|
|
summaryText += QString("<tr><td>%1(%2):</td><td align=\"right\"> %3</td>")
|
|
.arg(s)
|
|
.arg(metric ? metric->units(context->athlete->useMetricUnits) : "unknown")
|
|
.arg(value);
|
|
}
|
|
}
|
|
summaryText += QString("</tr>" "</table>");
|
|
|
|
}
|
|
|
|
// finish off the html document
|
|
summaryText += QString("</center>"
|
|
"</body>"
|
|
"</html>");
|
|
|
|
// set webview contents
|
|
summary->page()->mainFrame()->setHtml(summaryText);
|
|
|
|
}
|
|
}
|