mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-16 01:19:57 +00:00
Porting the codebase to QT 5 (5.2) to get the latest bug fixes, performance and improved platform support. This first part is to fixup the codebase to compile on Qt 5, but some aspects have been broken (video). The second part is to migrate from Qwt 6.0.1 to the latest Qwt for multiaxis support. The third part will be to fixup any platform specific issues or issues identified at runtime.
889 lines
29 KiB
C++
889 lines
29 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>
|
|
|
|
// seasons support
|
|
#include "Season.h"
|
|
#include "SeasonParser.h"
|
|
#include <QXmlInputSource>
|
|
#include <QXmlSimpleReader>
|
|
|
|
// named searchs
|
|
#include "NamedSearch.h"
|
|
#include "DataFilter.h"
|
|
|
|
// 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)
|
|
{
|
|
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;
|
|
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
|
|
seasonsWidget->addWidget(dateRangeTree);
|
|
|
|
// filters
|
|
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
|
|
filtersWidget->addWidget(filterTree);
|
|
|
|
// 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
|
|
eventsWidget->addWidget(eventTree);
|
|
|
|
seasons = context->athlete->seasons;
|
|
resetSeasons(); // reset the season list
|
|
resetFilters(); // reset the filters list
|
|
|
|
configChanged(); // will reset the metric tree
|
|
|
|
splitter = new GcSplitter(Qt::Vertical);
|
|
splitter->addWidget(seasonsWidget);
|
|
splitter->addWidget(filtersWidget);
|
|
splitter->addWidget(eventsWidget);
|
|
|
|
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)));
|
|
connect(filterTree,SIGNAL(itemSelectionChanged()), this, SLOT(filterTreeWidgetSelectionChanged()));
|
|
connect(eventTree,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(eventPopup(const QPoint &)));
|
|
|
|
// GC signal
|
|
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)));
|
|
|
|
// let everyone know what date range we are starting with
|
|
dateRangeTreeWidgetSelectionChanged();
|
|
|
|
}
|
|
|
|
void
|
|
LTMSidebar::configChanged()
|
|
{
|
|
}
|
|
|
|
/*----------------------------------------------------------------------
|
|
* 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);
|
|
|
|
// No Drag/Drop for temporary Season
|
|
if (season.getType() == Season::temporary)
|
|
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
|
else
|
|
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()
|
|
{
|
|
EditNamedSearches *editor = new EditNamedSearches(this, context);
|
|
editor->move(QCursor::pos()+QPoint(10,-200));
|
|
editor->show();
|
|
}
|
|
|
|
void
|
|
LTMSidebar::filterTreeWidgetSelectionChanged()
|
|
{
|
|
int selected = filterTree->selectedItems().count();
|
|
|
|
if (selected) {
|
|
|
|
QStringList errors, files; // results of all the selections
|
|
bool first = true;
|
|
int index=0;
|
|
|
|
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
|
|
//XXX todo
|
|
}
|
|
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
context->setHomeFilter(files);
|
|
|
|
} else
|
|
context->clearHomeFilter();
|
|
}
|
|
|
|
void
|
|
LTMSidebar::resetFilters()
|
|
{
|
|
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;
|
|
}
|
|
|
|
void
|
|
LTMSidebar::filterPopup()
|
|
{
|
|
// 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)));
|
|
}
|
|
|
|
// execute the menu
|
|
menu.exec(splitter->mapToGlobal(QPoint(filtersWidget->pos().x()+filtersWidget->width()-20, filtersWidget->pos().y())));
|
|
}
|
|
|
|
void
|
|
LTMSidebar::deleteFilter()
|
|
{
|
|
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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
// no drop in the temporary seasons
|
|
if (newposition>allDateRanges->childCount()-12) {
|
|
newposition = allDateRanges->childCount()-12;
|
|
allDateRanges->removeChild(item);
|
|
allDateRanges->insertChild(newposition, item);
|
|
}
|
|
|
|
// 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());
|
|
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());
|
|
|
|
// 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\">"
|
|
"<html>"
|
|
"<head>"
|
|
"<title></title>"
|
|
"</head>"
|
|
"<body>"
|
|
"<center>");
|
|
|
|
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);
|
|
|
|
}
|
|
}
|