Files
GoldenCheetah/src/MainWindow.cpp
Mark Liversedge aa8605e8d5 QT5 -- 1 of 3
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.
2013-12-09 09:57:13 +00:00

1521 lines
49 KiB
C++

/*
* Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net)
* Copyright (c) 2013 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
*/
// QT
#include <QApplication>
#include <QtGui>
#include <QRegExp>
#include <QDesktopWidget>
#include <QNetworkProxyQuery>
#include <QMenuBar>
#include <QStyle>
#include <QStyleFactory>
// DATA STRUCTURES
#include "MainWindow.h"
#include "Context.h"
#include "Athlete.h"
#include "Colors.h"
#include "RideItem.h"
#include "IntervalItem.h"
#include "RideFile.h"
#include "Settings.h"
#include "ErgDB.h"
#include "Library.h"
#include "LibraryParser.h"
#include "TrainDB.h"
#include "GcUpgrade.h"
// DIALOGS / DOWNLOADS / UPLOADS
#include "AboutDialog.h"
#include "ChooseCyclistDialog.h"
#include "ConfigDialog.h"
#include "DownloadRideDialog.h"
#include "ManualRideDialog.h"
#include "RideImportWizard.h"
#include "ToolsDialog.h"
#include "ToolsRhoEstimator.h"
#include "SplitActivityWizard.h"
#include "MergeActivityWizard.h"
#include "BatchExportDialog.h"
#include "TwitterDialog.h"
#include "ShareDialog.h"
#include "TtbDialog.h"
#include "WithingsDownload.h"
#include "ZeoDownload.h"
#include "WorkoutWizard.h"
#include "ErgDBDownloadDialog.h"
#include "AddDeviceWizard.h"
#ifdef GC_HAVE_SOAP
#include "TPUploadDialog.h"
#include "TPDownloadDialog.h"
#endif
#ifdef GC_HAVE_ICAL
#include "CalDAV.h"
#endif
#include "CalendarDownload.h"
// GUI Widgets
#include "Tab.h"
#include "GcToolBar.h"
#include "HelpWindow.h"
#include "HomeWindow.h"
#include "GcScopeBar.h"
#ifdef Q_OS_MAC
#include "QtMacButton.h" // mac
#include "QtMacSegmentedButton.h" // mac
#else
#include "QTFullScreen.h" // not mac!
#include "../qtsolutions/segmentcontrol/qtsegmentcontrol.h"
#endif
// SEARCH / FILTER
#ifdef GC_HAVE_LUCENE
#include "Lucene.h"
#include "NamedSearch.h"
#include "SearchFilterBox.h"
#endif
#ifdef GC_HAVE_WFAPI
#include "WFApi.h"
#endif
// We keep track of all theopen mainwindows
QList<MainWindow *> mainwindows;
QDesktopWidget *desktop = NULL;
MainWindow::MainWindow(const QDir &home)
{
/*----------------------------------------------------------------------
* Bootstrap
*--------------------------------------------------------------------*/
setAttribute(Qt::WA_DeleteOnClose);
mainwindows.append(this); // add us to the list of open windows
context = new Context(this);
context->athlete = new Athlete(context, home);
setWindowIcon(QIcon(":images/gc.png"));
setWindowTitle(context->athlete->home.dirName());
setContentsMargins(0,0,0,0);
setAcceptDrops(true);
GCColor *GCColorSet = new GCColor(context); // get/keep colorset
GCColorSet->colorSet(); // shut up the compiler
#ifdef Q_OS_MAC
// get an autorelease pool setup
static CocoaInitializer cocoaInitializer;
#endif
#ifdef GC_HAVE_WFAPI
WFApi *w = WFApi::getInstance(); // ensure created on main thread
w->apiVersion();//shutup compiler
#endif
Library::initialise(context->athlete->home);
QNetworkProxyQuery npq(QUrl("http://www.google.com"));
QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(npq);
if (listOfProxies.count() > 0) {
QNetworkProxy::setApplicationProxy(listOfProxies.first());
}
if (desktop == NULL) desktop = QApplication::desktop();
static const QIcon hideIcon(":images/toolbar/main/hideside.png");
static const QIcon rhideIcon(":images/toolbar/main/hiderside.png");
static const QIcon showIcon(":images/toolbar/main/showside.png");
static const QIcon rshowIcon(":images/toolbar/main/showrside.png");
static const QIcon tabIcon(":images/toolbar/main/tab.png");
static const QIcon tileIcon(":images/toolbar/main/tile.png");
static const QIcon fullIcon(":images/toolbar/main/togglefull.png");
#ifndef Q_OS_MAC
fullScreen = new QTFullScreen(context);
#endif
// if no workout directory is configured, default to the
// top level GoldenCheetah directory
if (appsettings->value(NULL, GC_WORKOUTDIR).toString() == "")
appsettings->setValue(GC_WORKOUTDIR, QFileInfo(context->athlete->home.absolutePath() + "/../").absolutePath());
/*----------------------------------------------------------------------
* GUI setup
*--------------------------------------------------------------------*/
// need to restore geometry before setUnifiedToolBar.. on Mac
appsettings->setValue(GC_SETTINGS_LAST, context->athlete->home.dirName());
QVariant geom = appsettings->value(this, GC_SETTINGS_MAIN_GEOM);
if (geom == QVariant()) {
// first run -- lets set some sensible defaults...
// lets put it in the middle of screen 1
QRect size = desktop->availableGeometry();
struct SizeSettings app = GCColor::defaultSizes(size.height(), size.width());
// center on the available screen (minus toolbar/sidebar)
move((size.width()-size.x())/2 - app.width/2,
(size.height()-size.y())/2 - app.height/2);
// set to the right default
resize(app.width, app.height);
// set all the default font sizes
appsettings->setValue(GC_FONT_DEFAULT_SIZE, app.defaultFont);
appsettings->setValue(GC_FONT_TITLES_SIZE, app.titleFont);
appsettings->setValue(GC_FONT_CHARTMARKERS_SIZE, app.markerFont);
appsettings->setValue(GC_FONT_CHARTLABELS_SIZE, app.labelFont);
appsettings->setValue(GC_FONT_CALENDAR_SIZE, app.calendarFont);
appsettings->setValue(GC_FONT_POPUP_SIZE, app.popupFont);
// set the default fontsize
QFont font;
font.setPointSize(app.defaultFont);
QApplication::setFont(font);
} else {
QRect size = desktop->availableGeometry();
// ensure saved geometry isn't greater than current screen size
if ((geom.toRect().height() >= size.height()) || (geom.toRect().width() >= size.width()))
setGeometry(size.x()+30,size.y()+30,size.width()-60,size.height()-60);
else
setGeometry(geom.toRect());
}
/*----------------------------------------------------------------------
* Mac Toolbar
*--------------------------------------------------------------------*/
#ifdef Q_OS_MAC
setUnifiedTitleAndToolBarOnMac(true);
head = addToolBar(context->athlete->cyclist);
head->setContentsMargins(0,0,0,0);
// widgets
QWidget *macAnalButtons = new QWidget(this);
macAnalButtons->setContentsMargins(0,0,20,0);
// lhs buttons
QHBoxLayout *lb = new QHBoxLayout(macAnalButtons);
lb->setContentsMargins(0,0,0,0);
lb->setSpacing(0);
import = new QtMacButton(this, QtMacButton::TexturedRounded);
QPixmap *importImg = new QPixmap(":images/mac/download.png");
import->setImage(importImg);
import->setToolTip("Download");
lb->addWidget(import);
lb->addWidget(new Spacer(this));
compose = new QtMacButton(this, QtMacButton::TexturedRounded);
QPixmap *composeImg = new QPixmap(":images/mac/compose.png");
compose->setImage(composeImg);
compose->setToolTip("Create");
lb->addWidget(compose);
// connect to actions
connect(import, SIGNAL(clicked(bool)), this, SLOT(downloadRide()));
connect(compose, SIGNAL(clicked(bool)), this, SLOT(manualRide()));
lb->addWidget(new Spacer(this));
// activity actions .. peaks, split, delete
QWidget *acts = new QWidget(this);
acts->setContentsMargins(0,0,0,0);
QHBoxLayout *pp = new QHBoxLayout(acts);
pp->setContentsMargins(0,0,0,0);
pp->setContentsMargins(0,0,0,0);
pp->setSpacing(5);
sidebar = new QtMacButton(this, QtMacButton::TexturedRounded);
QPixmap *sidebarImg = new QPixmap(":images/mac/sidebar.png");
sidebar->setImage(sidebarImg);
sidebar->setMinimumSize(25, 25);
sidebar->setMaximumSize(25, 25);
sidebar->setToolTip("Sidebar");
sidebar->setSelected(true); // assume always start up with sidebar selected
lowbar = new QtMacButton(this, QtMacButton::TexturedRounded);
QPixmap *lowbarImg = new QPixmap(":images/mac/lowbar.png");
lowbar->setImage(lowbarImg);
lowbar->setMinimumSize(25, 25);
lowbar->setMaximumSize(25, 25);
lowbar->setToolTip("Compare");
lowbar->setSelected(false); // assume always start up with lowbar deselected
actbuttons = new QtMacSegmentedButton(3, acts);
actbuttons->setWidth(115);
actbuttons->setNoSelect();
actbuttons->setImage(0, new QPixmap(":images/mac/stop.png"));
actbuttons->setImage(1, new QPixmap(":images/mac/split.png"));
actbuttons->setImage(2, new QPixmap(":images/mac/trash.png"));
pp->addWidget(actbuttons);
lb->addWidget(acts);
lb->addStretch();
connect(actbuttons, SIGNAL(clicked(int,bool)), this, SLOT(actionClicked(int)));
lb->addWidget(new Spacer(this));
QWidget *viewsel = new QWidget(this);
viewsel->setContentsMargins(0,0,0,0);
QHBoxLayout *pq = new QHBoxLayout(viewsel);
pq->setContentsMargins(0,0,0,0);
pq->setSpacing(5);
QHBoxLayout *ps = new QHBoxLayout;
ps->setContentsMargins(0,0,0,0);
ps->setSpacing (2); // low and sidebar button close together
ps->addStretch();
ps->addWidget(sidebar);
ps->addWidget(lowbar);
ps->addStretch();
pq->addLayout(ps);
styleSelector = new QtMacSegmentedButton(2, viewsel);
styleSelector->setWidth(80); // actually its 80 but we want a 30px space between is and the searchbox
styleSelector->setImage(0, new QPixmap(":images/mac/tabbed.png"), 24);
styleSelector->setImage(1, new QPixmap(":images/mac/tiled.png"), 24);
pq->addWidget(styleSelector);
connect(sidebar, SIGNAL(clicked(bool)), this, SLOT(toggleSidebar()));
connect(lowbar, SIGNAL(clicked(bool)), this, SLOT(toggleLowbar()));
connect(styleSelector, SIGNAL(clicked(int,bool)), this, SLOT(toggleStyle()));
// setup Mac thetoolbar
head->addWidget(macAnalButtons);
head->addWidget(new Spacer(this));
head->addWidget(new Spacer(this));
head->addWidget(viewsel);
#ifdef GC_HAVE_LUCENE
SearchFilterBox *searchBox = new SearchFilterBox(this,context,false);
QStyle *toolStyle = QStyleFactory::create("fusion");
searchBox->setStyle(toolStyle);
searchBox->setFixedWidth(200);
head->addWidget(searchBox);
connect(searchBox, SIGNAL(searchResults(QStringList)), this, SLOT(setFilter(QStringList)));
connect(searchBox, SIGNAL(searchClear()), this, SLOT(clearFilter()));
#endif
#endif
/*----------------------------------------------------------------------
* Windows and Linux Toolbar
*--------------------------------------------------------------------*/
#ifndef Q_OS_MAC
head = new GcToolBar(this);
QStyle *toolStyle = QStyleFactory::create("fusion");
QPalette metal;
metal.setColor(QPalette::Button, QColor(215,215,215));
// get those icons
importIcon = iconFromPNG(":images/mac/download.png");
composeIcon = iconFromPNG(":images/mac/compose.png");
intervalIcon = iconFromPNG(":images/mac/stop.png");
splitIcon = iconFromPNG(":images/mac/split.png");
deleteIcon = iconFromPNG(":images/mac/trash.png");
sidebarIcon = iconFromPNG(":images/mac/sidebar.png");
lowbarIcon = iconFromPNG(":images/mac/lowbar.png");
tabbedIcon = iconFromPNG(":images/mac/tabbed.png");
tiledIcon = iconFromPNG(":images/mac/tiled.png");
QSize isize(19,19);
Spacer *spacerl = new Spacer(this);
spacerl->setFixedWidth(5);
import = new QPushButton(this);
import->setIcon(importIcon);
import->setIconSize(isize);
import->setFixedHeight(25);
import->setStyle(toolStyle);
import->setToolTip(tr("Download from Device"));
import->setPalette(metal);
connect(import, SIGNAL(clicked(bool)), this, SLOT(downloadRide()));
compose = new QPushButton(this);
compose->setIcon(composeIcon);
compose->setIconSize(isize);
compose->setFixedHeight(25);
compose->setStyle(toolStyle);
compose->setToolTip(tr("Create Manual Activity"));
compose->setPalette(metal);
connect(compose, SIGNAL(clicked(bool)), this, SLOT(manualRide()));
lowbar = new QPushButton(this);
lowbar->setIcon(lowbarIcon);
lowbar->setIconSize(isize);
lowbar->setFixedHeight(25);
lowbar->setStyle(toolStyle);
lowbar->setToolTip(tr("Toggle Compare Pane"));
lowbar->setPalette(metal);
connect(lowbar, SIGNAL(clicked(bool)), this, SLOT(toggleLowbar()));
sidebar = new QPushButton(this);
sidebar->setIcon(sidebarIcon);
sidebar->setIconSize(isize);
sidebar->setFixedHeight(25);
sidebar->setStyle(toolStyle);
sidebar->setToolTip(tr("Toggle Sidebar"));
sidebar->setPalette(metal);
connect(sidebar, SIGNAL(clicked(bool)), this, SLOT(toggleSidebar()));
actbuttons = new QtSegmentControl(this);
actbuttons->setStyle(toolStyle);
actbuttons->setIconSize(isize);
actbuttons->setCount(3);
actbuttons->setSegmentIcon(0, intervalIcon);
actbuttons->setSegmentIcon(1, splitIcon);
actbuttons->setSegmentIcon(2, deleteIcon);
actbuttons->setSelectionBehavior(QtSegmentControl::SelectNone); //wince. spelling. ugh
actbuttons->setFixedHeight(25);
actbuttons->setSegmentToolTip(0, tr("Find Intervals..."));
actbuttons->setSegmentToolTip(1, tr("Split Activity..."));
actbuttons->setSegmentToolTip(2, tr("Delete Activity"));
actbuttons->setPalette(metal);
connect(actbuttons, SIGNAL(segmentSelected(int)), this, SLOT(actionClicked(int)));
styleSelector = new QtSegmentControl(this);
styleSelector->setStyle(toolStyle);
styleSelector->setIconSize(isize);
styleSelector->setCount(2);
styleSelector->setSegmentIcon(0, tabbedIcon);
styleSelector->setSegmentIcon(1, tiledIcon);
styleSelector->setSegmentToolTip(0, tr("Tabbed View"));
styleSelector->setSegmentToolTip(1, tr("Tiled View"));
styleSelector->setSelectionBehavior(QtSegmentControl::SelectOne); //wince. spelling. ugh
styleSelector->setFixedHeight(25);
styleSelector->setPalette(metal);
connect(styleSelector, SIGNAL(segmentSelected(int)), this, SLOT(setStyleFromSegment(int))); //avoid toggle infinitely
head->addWidget(spacerl);
head->addWidget(import);
head->addWidget(compose);
head->addWidget(actbuttons);
head->addStretch();
head->addWidget(sidebar);
head->addWidget(lowbar);
head->addWidget(styleSelector);
#ifdef GC_HAVE_LUCENE
// add a search box on far right, but with a little space too
SearchFilterBox *searchBox = new SearchFilterBox(this,context,false);
searchBox->setStyle(toolStyle);
searchBox->setFixedWidth(200);
head->addWidget(searchBox);
connect(searchBox, SIGNAL(searchResults(QStringList)), this, SLOT(setFilter(QStringList)));
connect(searchBox, SIGNAL(searchClear()), this, SLOT(clearFilter()));
#endif
Spacer *spacer = new Spacer(this);
spacer->setFixedWidth(5);
head->addWidget(spacer);
#endif
/*----------------------------------------------------------------------
* ScopeBar
*--------------------------------------------------------------------*/
scopebar = new GcScopeBar(context);
connect(scopebar, SIGNAL(selectDiary()), this, SLOT(selectDiary()));
connect(scopebar, SIGNAL(selectHome()), this, SLOT(selectHome()));
connect(scopebar, SIGNAL(selectAnal()), this, SLOT(selectAnalysis()));
connect(scopebar, SIGNAL(selectTrain()), this, SLOT(selectTrain()));
// Add chart is on the scope bar
chartMenu = new QMenu(this);
QStyle *styler = QStyleFactory::create("fusion");
QPushButton *newchart = new QPushButton("+", this);
scopebar->addWidget(newchart);
newchart->setStyle(styler);
newchart->setFixedHeight(20);
newchart->setFixedWidth(24);
newchart->setFlat(true);
newchart->setFocusPolicy(Qt::NoFocus);
newchart->setToolTip(tr("Add Chart"));
newchart->setAutoFillBackground(false);
newchart->setAutoDefault(false);
newchart->setMenu(chartMenu);
connect(chartMenu, SIGNAL(aboutToShow()), this, SLOT(setChartMenu()));
connect(chartMenu, SIGNAL(triggered(QAction*)), this, SLOT(addChart(QAction*)));
/*----------------------------------------------------------------------
* Central Widget
*--------------------------------------------------------------------*/
tab = new Tab(context);
/*----------------------------------------------------------------------
* Central Widget
*--------------------------------------------------------------------*/
QWidget *central = new QWidget(this);
setContentsMargins(0,0,0,0);
central->setContentsMargins(0,0,0,0);
QVBoxLayout *mainLayout = new QVBoxLayout(central);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0,0,0,0);
#ifndef Q_OS_MAC // nonmac toolbar on main view -- its not
// unified with the title bar.
mainLayout->addWidget(head);
#endif
mainLayout->addWidget(scopebar);
mainLayout->addWidget(tab);
setCentralWidget(central);
/*----------------------------------------------------------------------
* Application Menus
*--------------------------------------------------------------------*/
#ifdef WIN32
menuBar()->setStyleSheet("QMenuBar { background: rgba(225,225,225); }"
"QMenuBar::item { background: rgba(225,225,225); }");
menuBar()->setContentsMargins(0,0,0,0);
#endif
QMenu *fileMenu = menuBar()->addMenu(tr("&Athlete"));
fileMenu->addAction(tr("&New..."), this, SLOT(newCyclist()), tr("Ctrl+N"));
fileMenu->addAction(tr("&Open..."), this, SLOT(openCyclist()), tr("Ctrl+O"));
fileMenu->addAction(tr("&Close Window"), this, SLOT(close()), tr ("Ctrl+W"));
fileMenu->addAction(tr("&Quit All Windows"), this, SLOT(closeAll()), tr("Ctrl+Q"));
QMenu *rideMenu = menuBar()->addMenu(tr("A&ctivity"));
rideMenu->addAction(tr("&Download from device..."), this, SLOT(downloadRide()), tr("Ctrl+D"));
rideMenu->addAction(tr("&Import from file..."), this, SLOT (importFile()), tr ("Ctrl+I"));
rideMenu->addAction(tr("&Manual activity entry..."), this, SLOT(manualRide()), tr("Ctrl+M"));
rideMenu->addSeparator ();
rideMenu->addAction(tr("&Export..."), this, SLOT(exportRide()), tr("Ctrl+E"));
rideMenu->addAction(tr("&Batch export..."), this, SLOT(exportBatch()), tr("Ctrl+B"));
rideMenu->addAction(tr("Export Metrics as CSV..."), this, SLOT(exportMetrics()), tr(""));
#ifdef GC_HAVE_SOAP
rideMenu->addSeparator ();
rideMenu->addAction(tr("&Upload to TrainingPeaks"), this, SLOT(uploadTP()), tr("Ctrl+U"));
rideMenu->addAction(tr("Down&load from TrainingPeaks..."), this, SLOT(downloadTP()), tr("Ctrl+L"));
#endif
#ifdef GC_HAVE_LIBOAUTH
tweetAction = new QAction(tr("Tweet Activity"), this);
connect(tweetAction, SIGNAL(triggered(bool)), this, SLOT(tweetRide()));
rideMenu->addAction(tweetAction);
shareAction = new QAction(tr("Share (Strava, RideWithGPS, CyclingAnalytics)..."), this);
connect(shareAction, SIGNAL(triggered(bool)), this, SLOT(share()));
rideMenu->addAction(shareAction);
#endif
ttbAction = new QAction(tr("Upload to Trainingstagebuch..."), this);
connect(ttbAction, SIGNAL(triggered(bool)), this, SLOT(uploadTtb()));
rideMenu->addAction(ttbAction);
rideMenu->addSeparator ();
rideMenu->addAction(tr("&Save activity"), this, SLOT(saveRide()), tr("Ctrl+S"));
rideMenu->addAction(tr("D&elete activity..."), this, SLOT(deleteRide()));
rideMenu->addAction(tr("Split &activity..."), this, SLOT(splitRide()));
rideMenu->addAction(tr("Merge activities..."), this, SLOT(mergeRide()));
rideMenu->addSeparator ();
QMenu *optionsMenu = menuBar()->addMenu(tr("&Tools"));
optionsMenu->addAction(tr("&Options..."), this, SLOT(showOptions()));
optionsMenu->addAction(tr("CP and W' Estimator..."), this, SLOT(showTools()));
optionsMenu->addAction(tr("Air Density (Rho) Estimator..."), this, SLOT(showRhoEstimator()));
optionsMenu->addSeparator();
optionsMenu->addAction(tr("Get &Withings Data..."), this,
SLOT (downloadMeasures()));
optionsMenu->addAction(tr("Get &Zeo Data..."), this,
SLOT (downloadMeasuresFromZeo()));
optionsMenu->addSeparator();
optionsMenu->addAction(tr("Create a new workout..."), this, SLOT(showWorkoutWizard()));
optionsMenu->addAction(tr("Download workouts from ErgDB..."), this, SLOT(downloadErgDB()));
optionsMenu->addAction(tr("Import workouts or videos..."), this, SLOT(importWorkout()));
optionsMenu->addAction(tr("Scan disk for videos and workouts..."), this, SLOT(manageLibrary()));
#ifdef GC_HAVE_ICAL
optionsMenu->addSeparator();
optionsMenu->addAction(tr("Upload Activity to Calendar"), this, SLOT(uploadCalendar()), tr (""));
//optionsMenu->addAction(tr("Import Calendar..."), this, SLOT(importCalendar()), tr ("")); // planned for v3.1
//optionsMenu->addAction(tr("Export Calendar..."), this, SLOT(exportCalendar()), tr ("")); // planned for v3.1
optionsMenu->addAction(tr("Refresh Calendar"), this, SLOT(refreshCalendar()), tr (""));
#endif
optionsMenu->addSeparator();
optionsMenu->addAction(tr("Find intervals..."), this, SLOT(addIntervals()), tr (""));
// Add all the data processors to the tools menu
const DataProcessorFactory &factory = DataProcessorFactory::instance();
QMap<QString, DataProcessor*> processors = factory.getProcessors();
if (processors.count()) {
optionsMenu->addSeparator();
toolMapper = new QSignalMapper(this); // maps each option
QMapIterator<QString, DataProcessor*> i(processors);
connect(toolMapper, SIGNAL(mapped(const QString &)), this, SLOT(manualProcess(const QString &)));
i.toFront();
while (i.hasNext()) {
i.next();
// The localized processor name is shown in menu
QAction *action = new QAction(QString("%1...").arg(i.value()->name()), this);
optionsMenu->addAction(action);
connect(action, SIGNAL(triggered()), toolMapper, SLOT(map()));
toolMapper->setMapping(action, i.key());
}
}
QMenu *viewMenu = menuBar()->addMenu(tr("&View"));
#ifndef Q_OS_MAC
viewMenu->addAction(tr("Toggle Full Screen"), this, SLOT(toggleFullScreen()), QKeySequence("F11"));
#endif
showhideSidebar = viewMenu->addAction(tr("Show Left Sidebar"), this, SLOT(showSidebar(bool)));
showhideSidebar->setCheckable(true);
showhideSidebar->setChecked(true);
showhideLowbar = viewMenu->addAction(tr("Show Compare Pane"), this, SLOT(showLowbar(bool)));
showhideLowbar->setCheckable(true);
showhideLowbar->setChecked(false);
#ifndef Q_OS_MAC // not on a Mac
QAction *showhideToolbar = viewMenu->addAction(tr("Show Toolbar"), this, SLOT(showToolbar(bool)));
showhideToolbar->setCheckable(true);
showhideToolbar->setChecked(true);
#endif
styleAction = viewMenu->addAction(tr("Tabbed View"), this, SLOT(toggleStyle()));
styleAction->setCheckable(true);
styleAction->setChecked(true);
//connect(showhideSidebar, SIGNAL(triggered(bool)), this, SLOT(showSidebar(bool)));
viewMenu->addSeparator();
viewMenu->addAction(tr("Analysis"), this, SLOT(selectAnalysis()));
viewMenu->addAction(tr("Home"), this, SLOT(selectHome()));
viewMenu->addAction(tr("Train"), this, SLOT(selectTrain()));
#ifdef GC_HAVE_ICAL
viewMenu->addAction(tr("Diary"), this, SLOT(selectDiary()));
#endif
viewMenu->addSeparator();
subChartMenu = viewMenu->addMenu(tr("Add Chart"));
viewMenu->addAction(tr("Reset Layout"), this, SLOT(resetWindowLayout()));
windowMenu = menuBar()->addMenu(tr("&Window"));
connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(setWindowMenu()));
connect(rideMenu, SIGNAL(aboutToShow()), this, SLOT(setActivityMenu()));
connect(subChartMenu, SIGNAL(aboutToShow()), this, SLOT(setSubChartMenu()));
connect(subChartMenu, SIGNAL(triggered(QAction*)), this, SLOT(addChart(QAction*)));
connect(windowMenu, SIGNAL(triggered(QAction*)), this, SLOT(selectWindow(QAction*)));
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(tr("&User Guide"), this, SLOT(helpView()));
helpMenu->addAction(tr("&Log a bug or feature request"), this, SLOT(logBug()));
helpMenu->addSeparator();
helpMenu->addAction(tr("&About GoldenCheetah"), this, SLOT(aboutDialog()));
/*----------------------------------------------------------------------
* Lets go, choose latest ride and get GUI up and running
*--------------------------------------------------------------------*/
// selects the latest ride in the list:
if (context->athlete->allRides->childCount() != 0)
context->athlete->treeWidget->setCurrentItem(context->athlete->allRides->child(context->athlete->allRides->childCount()-1));
//XXX!!! We really do need a mechanism for showing if a ride needs saving...
//connect(this, SIGNAL(rideDirty()), this, SLOT(enableSaveButton()));
//connect(this, SIGNAL(rideClean()), this, SLOT(enableSaveButton()));
// cpx aggregate cache check
connect(context,SIGNAL(rideSelected(RideItem*)), this, SLOT(rideSelected(RideItem*)));
// Kick off - select a ride and switch to Analysis View
context->athlete->rideTreeWidgetSelectionChanged();
selectAnalysis();
}
/*----------------------------------------------------------------------
* GUI
*--------------------------------------------------------------------*/
void
MainWindow::toggleSidebar()
{
tab->toggleSidebar();
setToolButtons();
}
void
MainWindow::showSidebar(bool want)
{
tab->setSidebarEnabled(want);
setToolButtons();
}
void
MainWindow::toggleLowbar()
{
if (tab->hasBottom()) tab->setShowBottom(!tab->isShowBottom());
setToolButtons();
}
void
MainWindow::showLowbar(bool want)
{
if (tab->hasBottom()) tab->setShowBottom(want);
setToolButtons();
}
void
MainWindow::showToolbar(bool want)
{
if (want) {
head->show();
scopebar->show();
}
else {
head->hide();
scopebar->hide();
}
}
void
MainWindow::setChartMenu()
{
unsigned int mask=0;
// called when chart menu about to be shown
// setup to only show charts that are relevant
// to this view
switch(tab->currentView()) {
case 0 : mask = VIEW_HOME; break;
default:
case 1 : mask = VIEW_ANALYSIS; break;
case 2 : mask = VIEW_DIARY; break;
case 3 : mask = VIEW_TRAIN; break;
}
chartMenu->clear();
if (!mask) return;
for(int i=0; GcWindows[i].relevance; i++) {
if (GcWindows[i].relevance & mask)
chartMenu->addAction(GcWindows[i].name);
}
}
void
MainWindow::setSubChartMenu()
{
unsigned int mask=0;
// called when chart menu about to be shown
// setup to only show charts that are relevant
// to this view
switch(tab->currentView()) {
case 0 : mask = VIEW_HOME; break;
default:
case 1 : mask = VIEW_ANALYSIS; break;
case 2 : mask = VIEW_DIARY; break;
case 3 : mask = VIEW_TRAIN; break;
}
subChartMenu->clear();
if (!mask) return;
for(int i=0; GcWindows[i].relevance; i++) {
if (GcWindows[i].relevance & mask)
subChartMenu->addAction(GcWindows[i].name);
}
}
void
MainWindow::setActivityMenu()
{
// enable/disable upload if already uploaded
if (context->ride && context->ride->ride()) {
QString activityId = context->ride->ride()->getTag("TtbExercise", "");
if (activityId == "") ttbAction->setEnabled(true);
else ttbAction->setEnabled(false);
} else {
ttbAction->setEnabled(false);
}
}
void
MainWindow::addChart(QAction*action)
{
GcWinID id = GcWindowTypes::None;
for (int i=0; GcWindows[i].relevance; i++) {
if (GcWindows[i].name == action->text()) {
id = GcWindows[i].id;
break;
}
}
if (id != GcWindowTypes::None)
tab->addChart(id); // called from MainWindow to inset chart
}
void
MainWindow::setWindowMenu()
{
windowMenu->clear();
foreach (MainWindow *m, mainwindows) {
windowMenu->addAction(m->context->athlete->cyclist);
}
}
void
MainWindow::selectWindow(QAction *act)
{
foreach (MainWindow *m, mainwindows) {
if (m->context->athlete->cyclist == act->text()) {
m->activateWindow();
m->raise();
break;
}
}
}
void
MainWindow::setStyleFromSegment(int segment)
{
tab->setTiled(segment);
styleAction->setChecked(!segment);
}
void
MainWindow::toggleStyle()
{
tab->toggleTile();
styleAction->setChecked(tab->isTiled());
setToolButtons();
}
#ifndef Q_OS_MAC
void
MainWindow::toggleFullScreen()
{
if (fullScreen) fullScreen->toggle();
else qDebug()<<"no fullscreen support compiled in.";
}
#endif
void
MainWindow::resizeEvent(QResizeEvent*)
{
appsettings->setValue(GC_SETTINGS_MAIN_GEOM, geometry());
#ifdef Q_OS_MAC
head->updateGeometry();
repaint();
#endif
}
void
MainWindow::showOptions()
{
ConfigDialog *cd = new ConfigDialog(context->athlete->home, context->athlete->zones_, context);
// move to the centre of the screen
cd->move(geometry().center()-QPoint(cd->geometry().width()/2, cd->geometry().height()/2));
cd->show();
}
void
MainWindow::moveEvent(QMoveEvent*)
{
appsettings->setValue(GC_SETTINGS_MAIN_GEOM, geometry());
}
void
MainWindow::closeEvent(QCloseEvent* event)
{
if (saveRideExitDialog() == false) event->ignore();
else {
#ifdef GC_HAVE_LUCENE
// save the named searches
context->athlete->namedSearches->write();
#endif
// clear the clipboard if neccessary
QApplication::clipboard()->setText("");
// close the tab
tab->close();
// close athlete
context->athlete->close();
// now remove from the list
if(mainwindows.removeOne(this) == false)
qDebug()<<"closeEvent: mainwindows list error";
}
}
MainWindow::~MainWindow()
{
delete tab;
delete context->athlete;
}
// global search/data filter
void MainWindow::setFilter(QStringList f) { context->setFilter(f); }
void MainWindow::clearFilter() { context->clearFilter(); }
void
MainWindow::closeAll()
{
QList<MainWindow *> windows = mainwindows; // get a copy, since it is updated as closed
foreach(MainWindow *window, windows)
if (window != this) window->close();
// now close us down!
close();
}
void
MainWindow::aboutDialog()
{
AboutDialog *ad = new AboutDialog(context, context->athlete->home);
ad->exec();
}
void MainWindow::showTools()
{
ToolsDialog *td = new ToolsDialog();
td->show();
}
void MainWindow::showRhoEstimator()
{
ToolsRhoEstimator *tre = new ToolsRhoEstimator(context);
tre->show();
}
void MainWindow::showWorkoutWizard()
{
WorkoutWizard *ww = new WorkoutWizard(context);
ww->show();
}
void MainWindow::resetWindowLayout()
{
tab->resetLayout();
}
void MainWindow::manualProcess(QString name)
{
// open a dialog box and let the users
// configure the options to use
// and also show the explanation
// of what this function does
// then call it!
RideItem *rideitem = (RideItem*)context->currentRideItem();
if (rideitem) {
ManualDataProcessorDialog *p = new ManualDataProcessorDialog(context, name, rideitem);
p->setWindowModality(Qt::ApplicationModal); // don't allow select other ride or it all goes wrong!
p->exec();
}
}
void
MainWindow::logBug()
{
QDesktopServices::openUrl(QUrl("http://www.goldencheetah.org/bug-tracker.html"));
}
void
MainWindow::helpView()
{
QDesktopServices::openUrl(QUrl("http://www.goldencheetah.org/wiki.html"));
}
void
MainWindow::selectAnalysis()
{
tab->selectView(1);
setToolButtons();
}
void
MainWindow::selectTrain()
{
tab->selectView(3);
setToolButtons();
}
void
MainWindow::selectDiary()
{
tab->selectView(2);
setToolButtons();
}
void
MainWindow::selectHome()
{
tab->selectView(0);
setToolButtons();
}
void
MainWindow::setToolButtons()
{
int select = tab->isTiled() ? 1 : 0;
int lowselected = tab->isShowBottom() ? 1 : 0;
styleAction->setChecked(select);
showhideLowbar->setChecked(lowselected);
#ifdef Q_OS_MAC
styleSelector->setSelected(select, true);
lowbar->setSelected(lowselected);
#else
if (styleSelector->isSegmentSelected(select) == false)
styleSelector->setSegmentSelected(select, true);
#endif
}
/*----------------------------------------------------------------------
* Drag and Drop
*--------------------------------------------------------------------*/
void
MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
bool accept = true;
// we reject http, since we want a file!
foreach (QUrl url, event->mimeData()->urls())
if (url.toString().startsWith("http"))
accept = false;
if (accept) event->acceptProposedAction(); // whatever you wanna drop we will try and process!
else event->ignore();
}
void
MainWindow::dropEvent(QDropEvent *event)
{
QList<QUrl> urls = event->mimeData()->urls();
if (urls.isEmpty()) return;
if (tab->currentView() != 3) { // we're not on train view
// We have something to process then
RideImportWizard *dialog = new RideImportWizard (&urls, context->athlete->home, context);
dialog->process(); // do it!
} else {
QStringList filenames;
for (int i=0; i<urls.count(); i++)
filenames.append(QFileInfo(urls.value(i).toLocalFile()).absoluteFilePath());
Library::importFiles(context, filenames);
}
return;
}
/*----------------------------------------------------------------------
* Ride Library Functions
*--------------------------------------------------------------------*/
void
MainWindow::downloadRide()
{
(new DownloadRideDialog(context, context->athlete->home))->show();
}
void
MainWindow::manualRide()
{
(new ManualRideDialog(context))->show();
}
void
MainWindow::exportBatch()
{
BatchExportDialog *d = new BatchExportDialog(context);
d->exec();
}
void
MainWindow::exportRide()
{
if ((context->athlete->treeWidget->selectedItems().size() != 1)
|| (context->athlete->treeWidget->selectedItems().first()->type() != RIDE_TYPE)) {
QMessageBox::critical(this, tr("Select Activity"), tr("No activity selected!"));
return;
}
// what format?
const RideFileFactory &rff = RideFileFactory::instance();
QStringList allFormats;
foreach(QString suffix, rff.writeSuffixes())
allFormats << QString("%1 (*.%2)").arg(rff.description(suffix)).arg(suffix);
QString suffix; // what was selected?
QString fileName = QFileDialog::getSaveFileName(this, tr("Export Activity"), QDir::homePath(), allFormats.join(";;"), &suffix);
if (fileName.length() == 0) return;
// which file type was selected
// extract from the suffix returned
QRegExp getSuffix("^[^(]*\\(\\*\\.([^)]*)\\)$");
getSuffix.exactMatch(suffix);
QFile file(fileName);
bool result = RideFileFactory::instance().writeRideFile(context, context->currentRide(), file, getSuffix.cap(1));
if (result == false) {
QMessageBox oops(QMessageBox::Critical, tr("Export Failed"),
tr("Failed to export ride, please check permissions"));
oops.exec();
}
}
void
MainWindow::importFile()
{
QVariant lastDirVar = appsettings->value(this, GC_SETTINGS_LAST_IMPORT_PATH);
QString lastDir = (lastDirVar != QVariant())
? lastDirVar.toString() : QDir::homePath();
const RideFileFactory &rff = RideFileFactory::instance();
QStringList suffixList = rff.suffixes();
suffixList.replaceInStrings(QRegExp("^"), "*.");
QStringList fileNames;
QStringList allFormats;
allFormats << QString("All Supported Formats (%1)").arg(suffixList.join(" "));
foreach(QString suffix, rff.suffixes())
allFormats << QString("%1 (*.%2)").arg(rff.description(suffix)).arg(suffix);
allFormats << "All files (*.*)";
fileNames = QFileDialog::getOpenFileNames( this, tr("Import from File"), lastDir, allFormats.join(";;"));
if (!fileNames.isEmpty()) {
lastDir = QFileInfo(fileNames.front()).absolutePath();
appsettings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir);
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
RideImportWizard *import = new RideImportWizard(fileNamesCopy, context->athlete->home, context);
import->process();
}
}
void
MainWindow::saveRide()
{
if (context->ride)
saveRideSingleDialog(context->ride); // will signal save to everyone
else {
QMessageBox oops(QMessageBox::Critical, tr("No Activity To Save"),
tr("There is no currently selected ride to save."));
oops.exec();
return;
}
}
void
MainWindow::revertRide()
{
context->ride->freeMemory();
context->ride->ride(); // force re-load
// in case reverted ride has different starttime
context->ride->setStartTime(context->ride->ride()->startTime()); // Note: this will also signal rideSelected()
context->ride->ride()->emitReverted();
}
void
MainWindow::splitRide()
{
if (context->ride && context->ride->ride() && context->ride->ride()->dataPoints().count()) (new SplitActivityWizard(context))->exec();
else {
if (!context->ride || !context->ride->ride())
QMessageBox::critical(this, tr("Split Activity"), tr("No activity selected"));
else
QMessageBox::critical(this, tr("Split Activity"), tr("Current activity contains no data to split"));
}
}
void
MainWindow::mergeRide()
{
if (context->ride && context->ride->ride() && context->ride->ride()->dataPoints().count()) (new MergeActivityWizard(context))->exec();
else {
if (!context->ride || !context->ride->ride())
QMessageBox::critical(this, tr("Split Activity"), tr("No activity selected"));
else
QMessageBox::critical(this, tr("Split Activity"), tr("Current activity contains no data to merge"));
}
}
void
MainWindow::deleteRide()
{
QTreeWidgetItem *_item = context->athlete->treeWidget->currentItem();
if (_item==NULL || _item->type() != RIDE_TYPE) {
QMessageBox::critical(this, tr("Delete Activity"), tr("No activity selected!"));
return;
}
RideItem *item = static_cast<RideItem*>(_item);
QMessageBox msgBox;
msgBox.setText(tr("Are you sure you want to delete the activity:"));
msgBox.setInformativeText(item->fileName);
QPushButton *deleteButton = msgBox.addButton(tr("Delete"),QMessageBox::YesRole);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
msgBox.setIcon(QMessageBox::Critical);
msgBox.exec();
if(msgBox.clickedButton() == deleteButton)
context->athlete->removeCurrentRide();
}
/*----------------------------------------------------------------------
* Realtime Devices and Workouts
*--------------------------------------------------------------------*/
void
MainWindow::addDevice()
{
// lets get a new one
AddDeviceWizard *p = new AddDeviceWizard(context);
p->show();
}
/*----------------------------------------------------------------------
* Cyclists
*--------------------------------------------------------------------*/
void
MainWindow::newCyclist()
{
QDir newHome = context->athlete->home;
newHome.cdUp();
QString name = ChooseCyclistDialog::newCyclistDialog(newHome, this);
if (!name.isEmpty()) {
newHome.cd(name);
if (!newHome.exists()) return;
MainWindow *main = new MainWindow(newHome);
main->show();
}
}
void
MainWindow::openCyclist()
{
QDir newHome = context->athlete->home;
newHome.cdUp();
ChooseCyclistDialog d(newHome, false);
d.setModal(true);
if (d.exec() == QDialog::Accepted) {
newHome.cd(d.choice());
if (!newHome.exists()) return;
MainWindow *main = new MainWindow(newHome);
main->show();
}
}
/*----------------------------------------------------------------------
* MetricDB
*--------------------------------------------------------------------*/
void
MainWindow::exportMetrics()
{
QString fileName = QFileDialog::getSaveFileName( this, tr("Export Metrics"), QDir::homePath(), tr("Comma Separated Variables (*.csv)"));
if (fileName.length() == 0)
return;
context->athlete->metricDB->writeAsCSV(fileName);
}
/*----------------------------------------------------------------------
* Twitter
*--------------------------------------------------------------------*/
#ifdef GC_HAVE_LIBOAUTH
void
MainWindow::tweetRide()
{
QTreeWidgetItem *_item = context->athlete->treeWidget->currentItem();
if (_item==NULL || _item->type() != RIDE_TYPE) return;
RideItem *item = dynamic_cast<RideItem*>(_item);
if (item) { // menu is disabled anyway, but belt and braces
TwitterDialog *twitterDialog = new TwitterDialog(context, item);
twitterDialog->setWindowModality(Qt::ApplicationModal);
twitterDialog->exec();
}
}
/*----------------------------------------------------------------------
* Share : Twitter, Strava, RideWithGPS
*--------------------------------------------------------------------*/
void
MainWindow::share()
{
QTreeWidgetItem *_item = context->athlete->treeWidget->currentItem();
if (_item==NULL || _item->type() != RIDE_TYPE) return;
RideItem *item = dynamic_cast<RideItem*>(_item);
if (item) { // menu is disabled anyway, but belt and braces
ShareDialog d(context, item);
d.exec();
}
}
#endif
/*----------------------------------------------------------------------
* trainingstagebuch.org
*--------------------------------------------------------------------*/
void
MainWindow::uploadTtb()
{
QTreeWidgetItem *_item = context->athlete->treeWidget->currentItem();
if (_item==NULL || _item->type() != RIDE_TYPE) return;
RideItem *item = dynamic_cast<RideItem*>(_item);
if (item) { // menu is disabled anyway, but belt and braces
TtbDialog d(context, item);
d.exec();
}
}
/*----------------------------------------------------------------------
* Import Workout from Disk
*--------------------------------------------------------------------*/
void
MainWindow::importWorkout()
{
// go look at last place we imported workouts from...
QVariant lastDirVar = appsettings->value(this, GC_SETTINGS_LAST_WORKOUT_PATH);
QString lastDir = (lastDirVar != QVariant())
? lastDirVar.toString() : QDir::homePath();
// anything for now, we could add filters later
QStringList allFormats;
allFormats << "All files (*.*)";
QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Import from File"), lastDir, allFormats.join(";;"));
// lets process them
if (!fileNames.isEmpty()) {
// save away last place we looked
lastDir = QFileInfo(fileNames.front()).absolutePath();
appsettings->setValue(GC_SETTINGS_LAST_WORKOUT_PATH, lastDir);
QStringList fileNamesCopy = fileNames; // QT doc says iterate over a copy
// import them via the workoutimporter
Library::importFiles(context, fileNamesCopy);
}
}
/*----------------------------------------------------------------------
* ErgDB
*--------------------------------------------------------------------*/
void
MainWindow::downloadErgDB()
{
QString workoutDir = appsettings->value(this, GC_WORKOUTDIR).toString();
QFileInfo fi(workoutDir);
if (fi.exists() && fi.isDir()) {
ErgDBDownloadDialog *d = new ErgDBDownloadDialog(context);
d->exec();
} else{
QMessageBox::critical(this, tr("Workout Directory Invalid"),
"The workout directory is not configured, or the directory"
" selected no longer exists.\n\n"
"Please check your preference settings.");
}
}
/*----------------------------------------------------------------------
* Workout/Media Library
*--------------------------------------------------------------------*/
void
MainWindow::manageLibrary()
{
LibrarySearchDialog *search = new LibrarySearchDialog(context);
search->exec();
}
/*----------------------------------------------------------------------
* TrainingPeaks.com
*--------------------------------------------------------------------*/
#ifdef GC_HAVE_SOAP
void
MainWindow::uploadTP()
{
if (context->ride) {
TPUploadDialog uploader(context->athlete->cyclist, context->currentRide(), context);
uploader.exec();
}
}
void
MainWindow::downloadTP()
{
TPDownloadDialog downloader(context);
downloader.exec();
}
#endif
/*----------------------------------------------------------------------
* Utility
*--------------------------------------------------------------------*/
/*----------------------------------------------------------------------
* Notifiers - application level events
*--------------------------------------------------------------------*/
void
Context::notifyConfigChanged()
{
// now tell everyone else
configChanged();
}
void
Athlete::configChanged()
{
// re-read Zones in case it changed
QFile zonesFile(home.absolutePath() + "/power.zones");
if (zonesFile.exists()) {
if (!zones_->read(zonesFile)) {
QMessageBox::critical(context->mainWindow, tr("Zones File Error"),
zones_->errorString());
}
else if (! zones_->warningString().isEmpty())
QMessageBox::warning(context->mainWindow, tr("Reading Zones File"), zones_->warningString());
}
// reread HR zones
QFile hrzonesFile(home.absolutePath() + "/hr.zones");
if (hrzonesFile.exists()) {
if (!hrzones_->read(hrzonesFile)) {
QMessageBox::critical(context->mainWindow, tr("HR Zones File Error"),
hrzones_->errorString());
}
else if (! hrzones_->warningString().isEmpty())
QMessageBox::warning(context->mainWindow, tr("Reading HR Zones File"), hrzones_->warningString());
}
QVariant unit = appsettings->cvalue(cyclist, GC_UNIT);
useMetricUnits = (unit.toString() == GC_UNIT_METRIC);
}
/*----------------------------------------------------------------------
* Measures
*--------------------------------------------------------------------*/
void
MainWindow::downloadMeasures()
{
context->athlete->withingsDownload->download();
}
void
MainWindow::downloadMeasuresFromZeo()
{
context->athlete->zeoDownload->download();
}
void
MainWindow::refreshCalendar()
{
#ifdef GC_HAVE_ICAL
context->athlete->davCalendar->download();
context->athlete->calendarDownload->download();
#endif
}
/*----------------------------------------------------------------------
* Calendar
*--------------------------------------------------------------------*/
#ifdef GC_HAVE_ICAL
void
MainWindow::uploadCalendar()
{
context->athlete->davCalendar->upload((RideItem*)context->currentRideItem()); // remove const coz it updates the ride
// to set GID and upload date
}
#endif
void
MainWindow::actionClicked(int index)
{
switch(index) {
default:
case 0: tab->addIntervals();
break;
case 1 : splitRide();
break;
case 2 : deleteRide();
break;
}
}
void
MainWindow::addIntervals()
{
tab->addIntervals();
}
void
MainWindow::rideSelected(RideItem*)
{
// update the ride property on all widgets
// to let them know they need to replot new
// selected ride
tab->setRide(context->ride);
if (!context->ride) return;
// refresh interval list for bottom left
// first lets wipe away the existing intervals
QList<QTreeWidgetItem *> intervals = context->athlete->allIntervals->takeChildren();
for (int i=0; i<intervals.count(); i++) delete intervals.at(i);
// now add the intervals for the current ride
if (context->ride) { // only if we have a ride pointer
RideFile *selected = context->ride->ride();
if (selected) {
// get all the intervals in the currently selected RideFile
QList<RideFileInterval> intervals = selected->intervals();
for (int i=0; i < intervals.count(); i++) {
// add as a child to context->athlete->allIntervals
IntervalItem *add = new IntervalItem(selected,
intervals.at(i).name,
intervals.at(i).start,
intervals.at(i).stop,
selected->timeToDistance(intervals.at(i).start),
selected->timeToDistance(intervals.at(i).stop),
context->athlete->allIntervals->childCount()+1);
add->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
context->athlete->allIntervals->addChild(add);
}
}
}
}