mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 08:38:45 +00:00
Move intervals to AnalysisSidebar
Further MainWindow updates, this time moving the code for working with intervals to the AnalysisSidebar and the associate menus. Ultimately, the functions for working with Activities and their Intervals will move from MainWindow and Sidebars to the ActivityCollection classes. This is a step as we strip out MainWindow to just GUI aspects.
This commit is contained in:
@@ -21,8 +21,6 @@
|
||||
#include "Context.h"
|
||||
#include "Athlete.h"
|
||||
#include "AboutDialog.h"
|
||||
#include "AddIntervalDialog.h"
|
||||
#include "BestIntervalDialog.h"
|
||||
#include "BlankState.h"
|
||||
#include "ChooseCyclistDialog.h"
|
||||
#include "Colors.h"
|
||||
@@ -718,7 +716,7 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
optionsMenu->addAction(tr("Refresh Calendar"), this, SLOT(refreshCalendar()), tr (""));
|
||||
#endif
|
||||
optionsMenu->addSeparator();
|
||||
optionsMenu->addAction(tr("Find intervals..."), this, SLOT(addIntervals()), tr (""));
|
||||
optionsMenu->addAction(tr("Find intervals..."), analysisSidebar, SLOT(addIntervals()), tr (""));
|
||||
|
||||
// Add all the data processors to the tools menu
|
||||
const DataProcessorFactory &factory = DataProcessorFactory::instance();
|
||||
@@ -801,7 +799,6 @@ MainWindow::MainWindow(const QDir &home) :
|
||||
|
||||
// cpx aggregate cache check
|
||||
connect(context,SIGNAL(rideSelected(RideItem*)), this, SLOT(rideSelected(RideItem*)));
|
||||
connect(context->athlete->intervalWidget,SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showContextMenuPopup(const QPoint &)));
|
||||
|
||||
// when metricDB updates check if BlankState needs to be closed
|
||||
connect(context->athlete->metricDB, SIGNAL(dataChanged()), this, SLOT(checkBlankState()));
|
||||
@@ -1027,177 +1024,6 @@ MainWindow::dateRangeChangedLTM(DateRange dr)
|
||||
context->_dr = dr;
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::analysisPopup()
|
||||
{
|
||||
// set the point for the menu and call below
|
||||
//XXX showTreeContextMenuPopup(analSidebar->mapToGlobal(QPoint(analItem->pos().x()+analItem->width()-20, analItem->pos().y())));
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::showTreeContextMenuPopup(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 Activities")) {
|
||||
QMenu menu(context->athlete->treeWidget);
|
||||
|
||||
|
||||
QAction *actSaveRide = new QAction(tr("Save Changes"), context->athlete->treeWidget);
|
||||
connect(actSaveRide, SIGNAL(triggered(void)), this, SLOT(saveRide()));
|
||||
|
||||
QAction *revertRide = new QAction(tr("Revert to Saved version"), context->athlete->treeWidget);
|
||||
connect(revertRide, SIGNAL(triggered(void)), this, SLOT(revertRide()));
|
||||
|
||||
QAction *actDeleteRide = new QAction(tr("Delete Activity"), context->athlete->treeWidget);
|
||||
connect(actDeleteRide, SIGNAL(triggered(void)), this, SLOT(deleteRide()));
|
||||
|
||||
QAction *actSplitRide = new QAction(tr("Split Activity"), context->athlete->treeWidget);
|
||||
connect(actSplitRide, SIGNAL(triggered(void)), this, SLOT(splitRide()));
|
||||
|
||||
QAction *actMergeRide = new QAction(tr("Merge Activities"), context->athlete->treeWidget);
|
||||
connect(actMergeRide, SIGNAL(triggered(void)), this, SLOT(mergeRide()));
|
||||
|
||||
if (rideItem->isDirty() == true) {
|
||||
menu.addAction(actSaveRide);
|
||||
menu.addAction(revertRide);
|
||||
}
|
||||
|
||||
menu.addAction(actDeleteRide);
|
||||
menu.addAction(actSplitRide);
|
||||
#ifdef GC_HAVE_ICAL
|
||||
QAction *actUploadCalendar = new QAction(tr("Upload Activity to Calendar"), context->athlete->treeWidget);
|
||||
connect(actUploadCalendar, SIGNAL(triggered(void)), this, SLOT(uploadCalendar()));
|
||||
menu.addAction(actUploadCalendar);
|
||||
#endif
|
||||
menu.addSeparator();
|
||||
|
||||
// ride navigator stuff
|
||||
//XXX QAction *colChooser = new QAction(tr("Show Column Chooser"), context->athlete->treeWidget);
|
||||
//XXX connect(colChooser, SIGNAL(triggered(void)), listView, SLOT(showColumnChooser()));
|
||||
//XXX menu.addAction(colChooser);
|
||||
|
||||
#if 0 //XXX
|
||||
if (listView->groupBy() >= 0) {
|
||||
|
||||
// already grouped lets ungroup
|
||||
QAction *nogroups = new QAction(tr("Do Not Show In Groups"), context->athlete->treeWidget);
|
||||
connect(nogroups, SIGNAL(triggered(void)), listView, SLOT(noGroups()));
|
||||
menu.addAction(nogroups);
|
||||
|
||||
} else {
|
||||
|
||||
QMenu *groupByMenu = new QMenu(tr("Group By"), context->athlete->treeWidget);
|
||||
groupByMenu->setEnabled(true);
|
||||
menu.addMenu(groupByMenu);
|
||||
|
||||
// add menu options for each column
|
||||
if (groupByMapper) delete groupByMapper;
|
||||
groupByMapper = new QSignalMapper(this);
|
||||
connect(groupByMapper, SIGNAL(mapped(const QString &)), listView, SLOT(setGroupByColumnName(QString)));
|
||||
|
||||
foreach(QString heading, listView->columnNames()) {
|
||||
if (heading == "*") continue; // special hidden column
|
||||
|
||||
QAction *groupByAct = new QAction(heading, context->athlete->treeWidget);
|
||||
connect(groupByAct, SIGNAL(triggered()), groupByMapper, SLOT(map()));
|
||||
groupByMenu->addAction(groupByAct);
|
||||
|
||||
// map action to column heading
|
||||
groupByMapper->setMapping(groupByAct, heading);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
menu.exec(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::intervalPopup()
|
||||
{
|
||||
//XXX
|
||||
#if 0
|
||||
// always show the 'find best' 'find peaks' options
|
||||
QMenu menu(intervalItem);
|
||||
|
||||
RideItem *rideItem = (RideItem *)context->athlete->treeWidget->selectedItems().first();
|
||||
|
||||
if (rideItem != NULL && rideItem->ride() && rideItem->ride()->dataPoints().count()) {
|
||||
QAction *actFindPeak = new QAction(tr("Find Peak Intervals"), intervalItem);
|
||||
QAction *actFindBest = new QAction(tr("Find Best Intervals"), intervalItem);
|
||||
connect(actFindPeak, SIGNAL(triggered(void)), this, SLOT(findPowerPeaks(void)));
|
||||
connect(actFindBest, SIGNAL(triggered(void)), this, SLOT(addIntervals(void)));
|
||||
menu.addAction(actFindPeak);
|
||||
menu.addAction(actFindBest);
|
||||
|
||||
// sort but only if 2 or more intervals
|
||||
if (context->athlete->allIntervals->childCount() > 1) {
|
||||
QAction *actSort = new QAction(tr("Sort Intervals"), intervalItem);
|
||||
connect(actSort, SIGNAL(triggered(void)), this, SLOT(sortIntervals(void)));
|
||||
menu.addAction(actSort);
|
||||
}
|
||||
|
||||
if (context->athlete->intervalWidget->selectedItems().count()) menu.addSeparator();
|
||||
}
|
||||
|
||||
|
||||
if (context->athlete->intervalWidget->selectedItems().count() == 1) {
|
||||
|
||||
// we can zoom, rename etc if only 1 interval is selected
|
||||
QAction *actZoomInt = new QAction(tr("Zoom to interval"), context->athlete->intervalWidget);
|
||||
QAction *actEditInt = new QAction(tr("Edit interval"), context->athlete->intervalWidget);
|
||||
QAction *actDeleteInt = new QAction(tr("Delete interval"), context->athlete->intervalWidget);
|
||||
|
||||
connect(actZoomInt, SIGNAL(triggered(void)), this, SLOT(zoomIntervalSelected(void)));
|
||||
connect(actEditInt, SIGNAL(triggered(void)), this, SLOT(editIntervalSelected(void)));
|
||||
connect(actDeleteInt, SIGNAL(triggered(void)), this, SLOT(deleteIntervalSelected(void)));
|
||||
|
||||
menu.addAction(actZoomInt);
|
||||
menu.addAction(actEditInt);
|
||||
menu.addAction(actDeleteInt);
|
||||
}
|
||||
|
||||
if (context->athlete->intervalWidget->selectedItems().count() > 1) {
|
||||
QAction *actRenameInt = new QAction(tr("Rename selected intervals"), context->athlete->intervalWidget);
|
||||
connect(actRenameInt, SIGNAL(triggered(void)), this, SLOT(renameIntervalsSelected(void)));
|
||||
QAction *actDeleteInt = new QAction(tr("Delete selected intervals"), context->athlete->intervalWidget);
|
||||
connect(actDeleteInt, SIGNAL(triggered(void)), this, SLOT(deleteIntervalSelected(void)));
|
||||
|
||||
menu.addAction(actRenameInt);
|
||||
menu.addAction(actDeleteInt);
|
||||
}
|
||||
|
||||
//XXX menu.exec(analSidebar->mapToGlobal((QPoint(intervalItem->pos().x()+intervalItem->width()-20, intervalItem->pos().y()))));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::showContextMenuPopup(const QPoint &pos)
|
||||
{
|
||||
QTreeWidgetItem *trItem = context->athlete->intervalWidget->itemAt( pos );
|
||||
if (trItem != NULL && trItem->text(0) != tr("Intervals")) {
|
||||
QMenu menu(context->athlete->intervalWidget);
|
||||
|
||||
activeInterval = (IntervalItem *)trItem;
|
||||
|
||||
QAction *actEditInt = new QAction(tr("Edit interval"), context->athlete->intervalWidget);
|
||||
QAction *actDeleteInt = new QAction(tr("Delete interval"), context->athlete->intervalWidget);
|
||||
QAction *actZoomInt = new QAction(tr("Zoom to interval"), context->athlete->intervalWidget);
|
||||
QAction *actFrontInt = new QAction(tr("Bring to Front"), context->athlete->intervalWidget);
|
||||
QAction *actBackInt = new QAction(tr("Send to back"), context->athlete->intervalWidget);
|
||||
connect(actEditInt, SIGNAL(triggered(void)), this, SLOT(editInterval(void)));
|
||||
connect(actDeleteInt, SIGNAL(triggered(void)), this, SLOT(deleteInterval(void)));
|
||||
connect(actZoomInt, SIGNAL(triggered(void)), this, SLOT(zoomInterval(void)));
|
||||
connect(actFrontInt, SIGNAL(triggered(void)), this, SLOT(frontInterval(void)));
|
||||
connect(actBackInt, SIGNAL(triggered(void)), this, SLOT(backInterval(void)));
|
||||
|
||||
menu.addAction(actZoomInt);
|
||||
menu.addAction(actEditInt);
|
||||
menu.addAction(actDeleteInt);
|
||||
menu.exec(context->athlete->intervalWidget->mapToGlobal( pos ));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::resizeEvent(QResizeEvent*)
|
||||
@@ -1908,274 +1734,6 @@ MainWindow::downloadTP()
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Intervals
|
||||
*--------------------------------------------------------------------*/
|
||||
|
||||
void
|
||||
MainWindow::addIntervals()
|
||||
{
|
||||
if (context->ride && context->ride->ride() && context->ride->ride()->dataPoints().count()) {
|
||||
|
||||
AddIntervalDialog *p = new AddIntervalDialog(context);
|
||||
p->setWindowModality(Qt::ApplicationModal); // don't allow select other ride or it all goes wrong!
|
||||
p->exec();
|
||||
|
||||
} else {
|
||||
|
||||
if (!context->ride || !context->ride->ride())
|
||||
QMessageBox::critical(this, tr("Find Best Intervals"), tr("No activity selected"));
|
||||
else
|
||||
QMessageBox::critical(this, tr("Find Best Intervals"), tr("Current activity contains no data"));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::addIntervalForPowerPeaksForSecs(RideFile *ride, int windowSizeSecs, QString name)
|
||||
{
|
||||
QList<BestIntervalDialog::BestInterval> results;
|
||||
BestIntervalDialog::findBests(ride, windowSizeSecs, 1, results);
|
||||
if (results.isEmpty()) return;
|
||||
const BestIntervalDialog::BestInterval &i = results.first();
|
||||
QTreeWidgetItem *peak =
|
||||
new IntervalItem(ride, name+tr(" (%1 watts)").arg((int) round(i.avg)),
|
||||
i.start, i.stop,
|
||||
ride->timeToDistance(i.start),
|
||||
ride->timeToDistance(i.stop),
|
||||
context->athlete->allIntervals->childCount()+1);
|
||||
peak->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
|
||||
context->athlete->allIntervals->addChild(peak);
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::findPowerPeaks()
|
||||
{
|
||||
|
||||
if (!context->ride) return;
|
||||
|
||||
QTreeWidgetItem *which = context->athlete->treeWidget->selectedItems().first();
|
||||
if (which->type() != RIDE_TYPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context->ride && context->ride->ride() && context->ride->ride()->dataPoints().count()) {
|
||||
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 5, "Peak 5s");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 10, "Peak 10s");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 20, "Peak 20s");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 30, "Peak 30s");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 60, "Peak 1min");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 120, "Peak 2min");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 300, "Peak 5min");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 600, "Peak 10min");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 1200, "Peak 20min");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 1800, "Peak 30min");
|
||||
addIntervalForPowerPeaksForSecs(context->ride->ride(), 3600, "Peak 60min");
|
||||
|
||||
// now update the RideFileIntervals
|
||||
context->athlete->updateRideFileIntervals();
|
||||
|
||||
} else {
|
||||
if (!context->ride || !context->ride->ride())
|
||||
QMessageBox::critical(this, tr("Find Power Peaks"), tr("No activity selected"));
|
||||
else
|
||||
QMessageBox::critical(this, tr("Find Power Peaks"), tr("Current activity contains no data"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
lessItem(const IntervalItem *s1, const IntervalItem *s2) {
|
||||
return s1->start < s2->start;
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::sortIntervals()
|
||||
{
|
||||
// sort them chronologically
|
||||
QList<IntervalItem*> intervals;
|
||||
|
||||
// set string to first interval selected
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();i++)
|
||||
intervals.append((IntervalItem*)(context->athlete->allIntervals->child(i)));
|
||||
|
||||
// now sort them into start time order
|
||||
qStableSort(intervals.begin(), intervals.end(), lessItem);
|
||||
|
||||
// empty context->athlete->allIntervals
|
||||
context->athlete->allIntervals->takeChildren();
|
||||
|
||||
// and put em back in chronological sequence
|
||||
foreach(IntervalItem* item, intervals) {
|
||||
context->athlete->allIntervals->addChild(item);
|
||||
}
|
||||
|
||||
// now update the ridefile
|
||||
context->athlete->updateRideFileIntervals(); // will emit intervalChanged() signal
|
||||
}
|
||||
|
||||
// rename multiple intervals
|
||||
void
|
||||
MainWindow::renameIntervalsSelected()
|
||||
{
|
||||
QString string;
|
||||
|
||||
// set string to first interval selected
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();i++) {
|
||||
if (context->athlete->allIntervals->child(i)->isSelected()) {
|
||||
string = context->athlete->allIntervals->child(i)->text(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// type in a name and we will renumber all the intervals
|
||||
// in the same fashion -- esp if the last characters are
|
||||
RenameIntervalDialog dialog(string, this);
|
||||
dialog.setFixedWidth(320);
|
||||
|
||||
if (dialog.exec()) {
|
||||
|
||||
int number = 1;
|
||||
|
||||
// does it end in a number?
|
||||
// if so we use that to renumber from
|
||||
QRegExp ends("^(.*[^0-9])([0-9]+)$");
|
||||
if (ends.exactMatch(string)) {
|
||||
|
||||
string = ends.cap(1);
|
||||
number = ends.cap(2).toInt();
|
||||
|
||||
} else if (!string.endsWith(" ")) string += " ";
|
||||
|
||||
// now go and renumber from 'number' with prefix 'string'
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();i++) {
|
||||
if (context->athlete->allIntervals->child(i)->isSelected())
|
||||
context->athlete->allIntervals->child(i)->setText(0, QString("%1%2").arg(string).arg(number++));
|
||||
}
|
||||
|
||||
context->athlete->updateRideFileIntervals(); // will emit intervalChanged() signal
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::deleteIntervalSelected()
|
||||
{
|
||||
// delete the intervals that are selected (from the menu)
|
||||
// the normal delete intervals does that already
|
||||
deleteInterval();
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::deleteInterval()
|
||||
{
|
||||
// now delete highlighted!
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();) {
|
||||
if (context->athlete->allIntervals->child(i)->isSelected()) delete context->athlete->allIntervals->takeChild(i);
|
||||
else i++;
|
||||
}
|
||||
|
||||
context->athlete->updateRideFileIntervals(); // will emit intervalChanged() signal
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::renameIntervalSelected()
|
||||
{
|
||||
// go edit the name
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();) {
|
||||
if (context->athlete->allIntervals->child(i)->isSelected()) {
|
||||
context->athlete->allIntervals->child(i)->setFlags(context->athlete->allIntervals->child(i)->flags() | Qt::ItemIsEditable);
|
||||
context->athlete->intervalWidget->editItem(context->athlete->allIntervals->child(i), 0);
|
||||
break;
|
||||
} else i++;
|
||||
}
|
||||
context->athlete->updateRideFileIntervals(); // will emit intervalChanged() signal
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::renameInterval() {
|
||||
// go edit the name
|
||||
activeInterval->setFlags(activeInterval->flags() | Qt::ItemIsEditable);
|
||||
context->athlete->intervalWidget->editItem(activeInterval, 0);
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::editIntervalSelected()
|
||||
{
|
||||
// go edit the interval
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();) {
|
||||
if (context->athlete->allIntervals->child(i)->isSelected()) {
|
||||
activeInterval = (IntervalItem*)context->athlete->allIntervals->child(i);
|
||||
editInterval();
|
||||
break;
|
||||
} else i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::editInterval()
|
||||
{
|
||||
IntervalItem temp = *activeInterval;
|
||||
EditIntervalDialog dialog(this, temp);
|
||||
|
||||
if (dialog.exec()) {
|
||||
*activeInterval = temp;
|
||||
context->athlete->updateRideFileIntervals(); // will emit intervalChanged() signal
|
||||
context->athlete->intervalWidget->update();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::zoomIntervalSelected()
|
||||
{
|
||||
// zoom the one interval that is selected via popup menu
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount();) {
|
||||
if (context->athlete->allIntervals->child(i)->isSelected()) {
|
||||
context->notifyIntervalZoom((IntervalItem*)(context->athlete->allIntervals->child(i)));
|
||||
break;
|
||||
} else i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::zoomInterval() {
|
||||
// zoom into this interval on allPlot
|
||||
context->notifyIntervalZoom(activeInterval);
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::frontInterval()
|
||||
{
|
||||
int oindex = activeInterval->displaySequence;
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount(); i++) {
|
||||
IntervalItem *it = (IntervalItem *)context->athlete->allIntervals->child(i);
|
||||
int ds = it->displaySequence;
|
||||
if (ds > oindex)
|
||||
it->setDisplaySequence(ds-1);
|
||||
}
|
||||
activeInterval->setDisplaySequence(context->athlete->allIntervals->childCount());
|
||||
|
||||
// signal!
|
||||
context->notifyIntervalsChanged();
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::backInterval()
|
||||
{
|
||||
int oindex = activeInterval->displaySequence;
|
||||
for (int i=0; i<context->athlete->allIntervals->childCount(); i++) {
|
||||
IntervalItem *it = (IntervalItem *)context->athlete->allIntervals->child(i);
|
||||
int ds = it->displaySequence;
|
||||
if (ds < oindex)
|
||||
it->setDisplaySequence(ds+1);
|
||||
}
|
||||
activeInterval->setDisplaySequence(1);
|
||||
|
||||
// signal!
|
||||
context->notifyIntervalsChanged();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
* Utility
|
||||
@@ -2322,7 +1880,7 @@ MainWindow::actionClicked(int index)
|
||||
switch(index) {
|
||||
|
||||
default:
|
||||
case 0: addIntervals();
|
||||
case 0: analysisSidebar->addIntervals();
|
||||
break;
|
||||
|
||||
case 1 : splitRide();
|
||||
|
||||
Reference in New Issue
Block a user