diff --git a/src/Charts/ChartBar.cpp b/src/Charts/ChartBar.cpp index 0e49d7680..6c8f567a3 100644 --- a/src/Charts/ChartBar.cpp +++ b/src/Charts/ChartBar.cpp @@ -119,13 +119,14 @@ ChartBar::ChartBar(Context *context) : QWidget(context->mainWindow), context(con connect(menuMapper, SIGNAL(mapped(int)), this, SLOT(triggerContextMenu(int))); barMenu = new QMenu("Add"); - chartMenu = barMenu->addMenu(tr("New ")); + chartMenu = barMenu->addMenu(tr("New Chart")); - barMenu->addAction(tr("Import ..."), context->mainWindow, SLOT(importChart())); + barMenu->addAction(tr("Import Chart ..."), context->mainWindow, SLOT(importChart())); #ifdef GC_HAS_CLOUD_DB - barMenu->addAction(tr("Download ..."), context->mainWindow, SLOT(addChartFromCloudDB())); + barMenu->addAction(tr("Download Chart..."), context->mainWindow, SLOT(addChartFromCloudDB())); #endif + // menu connect(menuButton, SIGNAL(clicked()), this, SLOT(menuPopup())); connect(chartMenu, SIGNAL(aboutToShow()), this, SLOT(setChartMenu())); diff --git a/src/Gui/MainWindow.cpp b/src/Gui/MainWindow.cpp index 396547583..4f218d5b0 100644 --- a/src/Gui/MainWindow.cpp +++ b/src/Gui/MainWindow.cpp @@ -658,6 +658,9 @@ MainWindow::MainWindow(const QDir &home) viewMenu->addAction(tr("Trends"), this, SLOT(selectHome())); viewMenu->addAction(tr("Train"), this, SLOT(selectTrain())); viewMenu->addSeparator(); + viewMenu->addAction(tr("Import Perspective..."), this, SLOT(importPerspective())); + viewMenu->addAction(tr("Export Perspective..."), this, SLOT(exportPerspective())); + viewMenu->addSeparator(); subChartMenu = viewMenu->addMenu(tr("Add Chart")); viewMenu->addAction(tr("Import Chart..."), this, SLOT(importChart())); #ifdef GC_HAS_CLOUD_DB @@ -947,6 +950,64 @@ MainWindow::importChart() } } +void +MainWindow::exportPerspective() +{ + int view = currentTab->currentView(); + TabView *current = NULL; + + switch (view) { + case 0: current = currentTab->homeView; break; + case 1: current = currentTab->analysisView; break; + case 2: current = currentTab->diaryView; break; + case 3: current = currentTab->trainView; break; + } + + // export the current perspective to a file + QString suffix; + QString fileName = QFileDialog::getSaveFileName(this, tr("Export Persepctive"), + QDir::homePath()+"/"+ current->perspective_->title() + ".gchartset", + ("*.gchartset;;"), &suffix, QFileDialog::DontUseNativeDialog); // native dialog hangs when threads in use (!) + + if (fileName.isEmpty()) { + QMessageBox::critical(this, tr("Export Perspective"), tr("No perspective file selected!")); + } else { + current->exportPerspective(current->perspective_, fileName); + } +} + +void +MainWindow::importPerspective() +{ + int view = currentTab->currentView(); + TabView *current = NULL; + + switch (view) { + case 0: current = currentTab->homeView; break; + case 1: current = currentTab->analysisView; break; + case 2: current = currentTab->diaryView; break; + case 3: current = currentTab->trainView; break; + } + + // import a new perspective from a file + QString fileName = QFileDialog::getOpenFileName(this, tr("Select Perspective file to export"), "", tr("GoldenCheetah Perspective Files (*.gchartset)")); + if (fileName.isEmpty()) { + QMessageBox::critical(this, tr("Import Perspective"), tr("No perspective file selected!")); + } else { + + // import and select it + pactive = true; + current->importPerspective(fileName); + current->setPerspectives(perspectiveSelector); + + // and select remember pactive is true, so we do the heavy lifting here + perspectiveSelector->setCurrentIndex(current->perspectives_.count()-1); + current->perspectiveSelected(perspectiveSelector->currentIndex()); + pactive = false; + } + +} + #ifdef GC_HAS_CLOUD_DB void diff --git a/src/Gui/MainWindow.h b/src/Gui/MainWindow.h index 63bc9c3b5..960c30431 100644 --- a/src/Gui/MainWindow.h +++ b/src/Gui/MainWindow.h @@ -145,6 +145,10 @@ class MainWindow : public QMainWindow void perspectiveSelected(int index); void perspectivesChanged(); // when the list of perspectives is updated in PerspectivesDialog + // import and export perspectives + void exportPerspective(); + void importPerspective(); + // chart importing void importCharts(QStringList); diff --git a/src/Gui/Perspective.cpp b/src/Gui/Perspective.cpp index 8ce6e2220..bb023e629 100644 --- a/src/Gui/Perspective.cpp +++ b/src/Gui/Perspective.cpp @@ -1410,6 +1410,112 @@ Perspective::presetSelected(int n) } } +/*-------------------------------------------------------------------------------- + * Import and Export the Perspective to xml + * -----------------------------------------------------------------------------*/ +Perspective *Perspective::fromFile(Context *context, QString filename, int type) +{ + Perspective *returning = NULL; + + QFileInfo finfo(filename); + QString content = ""; + + // no such file + if (!finfo.exists()) return returning; + + QFile file(filename); + if (file.open(QIODevice::ReadOnly)) { + content = file.readAll(); + file.close(); + } else return returning; + + // parse the content + QXmlInputSource source; + source.setData(content); + QXmlSimpleReader xmlReader; + ViewParser handler(context, type, false); + xmlReader.setContentHandler(&handler); + xmlReader.setErrorHandler(&handler); + + // parse and instantiate the charts + xmlReader.parse(source); + + // none loaded ? + if (handler.perspectives.count() == 0) return returning; + + // return the first one (if there are multiple) + returning = handler.perspectives[0]; + + // delete any further perspectives + for(int i=1; i\n"; + + // iterate over charts + foreach (GcChartWindow *chart, charts) { + GcWinID type = chart->property("type").value(); + + out<<"\t(type)<<"\" " + <<"name=\""<property("instanceName").toString())<<"\" " + <<"title=\""<property("title").toString())<<"\" >\n"; + + // iterate over chart properties + const QMetaObject *m = chart->metaObject(); + for (int i=0; ipropertyCount(); i++) { + QMetaProperty p = m->property(i); + if (p.isUser(chart)) { + out<<"\t\t(); + s << x; + out<\n"; + } + } + out<<"\t\n"; + } + out<<"\n"; +} + /*-------------------------------------------------------------------------------- * Import Chart Dialog - select/deselect charts before importing them * -----------------------------------------------------------------------------*/ diff --git a/src/Gui/Perspective.h b/src/Gui/Perspective.h index 3b98d51d1..7ca7ea120 100644 --- a/src/Gui/Perspective.h +++ b/src/Gui/Perspective.h @@ -43,6 +43,7 @@ class LTMSettings; class TabView; class ViewParser; class PerspectiveDialog; +class QTextStream; class Perspective : public GcWindow { @@ -58,6 +59,11 @@ class Perspective : public GcWindow Perspective(Context *, QString title, int type); ~Perspective(); + // import and export + static Perspective *fromFile(Context *context, QString filename, int type); + bool toFile(QString filename); + void toXml(QTextStream &out); + QString title() const { return title_; } void resetLayout(); diff --git a/src/Gui/PerspectiveDialog.cpp b/src/Gui/PerspectiveDialog.cpp index d330f1607..e039de2db 100644 --- a/src/Gui/PerspectiveDialog.cpp +++ b/src/Gui/PerspectiveDialog.cpp @@ -23,6 +23,7 @@ #include #include #include +#include /// /// PerspectiveDialog @@ -31,7 +32,7 @@ PerspectiveDialog::PerspectiveDialog(QWidget *parent, TabView *tabView) : QDialo { setWindowTitle("Manage Perspectives"); - setMinimumWidth(400*dpiXFactor); + setMinimumWidth(450*dpiXFactor); setMinimumHeight(450*dpiXFactor); //setAttribute(Qt::WA_DeleteOnClose); @@ -77,6 +78,9 @@ PerspectiveDialog::PerspectiveDialog(QWidget *parent, TabView *tabView) : QDialo upPerspective->setFixedSize(20*dpiXFactor,20*dpiYFactor); downPerspective->setFixedSize(20*dpiXFactor,20*dpiYFactor); + importPerspective = new QPushButton(tr("Import"), this); + exportPerspective = new QPushButton(tr("Export"), this); + addPerspective = new QPushButton("+", this); removePerspective = new QPushButton("-", this); @@ -98,6 +102,9 @@ PerspectiveDialog::PerspectiveDialog(QWidget *parent, TabView *tabView) : QDialo xb->addWidget(upPerspective); xb->addWidget(downPerspective); xb->addStretch(); + xb->addWidget(importPerspective); + xb->addWidget(exportPerspective); + xb->addStretch(); xb->addWidget(addPerspective); xb->addWidget(removePerspective); mainLayout->addLayout(xb); @@ -112,6 +119,9 @@ PerspectiveDialog::PerspectiveDialog(QWidget *parent, TabView *tabView) : QDialo connect(perspectiveTable, SIGNAL(chartMoved(GcChartWindow*)), this, SLOT(perspectiveSelected())); // just reset the chart list connect(perspectiveTable, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(perspectiveNameChanged(QTableWidgetItem*))); // user edit + connect(importPerspective, SIGNAL(clicked(bool)), this, SLOT(importPerspectiveClicked())); + connect(exportPerspective, SIGNAL(clicked(bool)), this, SLOT(exportPerspectiveClicked())); + connect(removePerspective, SIGNAL(clicked(bool)), this, SLOT(removePerspectiveClicked())); connect(addPerspective, SIGNAL(clicked(bool)), this, SLOT(addPerspectiveClicked())); connect(upPerspective, SIGNAL(clicked(bool)), this, SLOT(upPerspectiveClicked())); @@ -227,6 +237,47 @@ PerspectiveDialog::addPerspectiveClicked() } } +void +PerspectiveDialog::exportPerspectiveClicked() +{ + int index = perspectiveTable->selectedItems()[0]->row(); + + // wipe it - tabView will worry about bounds and switching if we delete the currently selected one + Perspective *current = tabView->perspectives_[index]; + + // export the current perspective to a file + QString suffix; + QString fileName = QFileDialog::getSaveFileName(this, tr("Export Persepctive"), + QDir::homePath()+"/"+ current->title() + ".gchartset", + ("*.gchartset;;"), &suffix, QFileDialog::DontUseNativeDialog); // native dialog hangs when threads in use (!) + + if (fileName.isEmpty()) { + QMessageBox::critical(this, tr("Export Perspective"), tr("No perspective file selected!")); + } else { + tabView->exportPerspective(current, fileName); + } +} + +void +PerspectiveDialog::importPerspectiveClicked() +{ + // import a new perspective from a file + QString fileName = QFileDialog::getOpenFileName(this, tr("Select Perspective file to export"), "", tr("GoldenCheetah Perspective Files (*.gchartset)")); + if (fileName.isEmpty()) { + QMessageBox::critical(this, tr("Import Perspective"), tr("No perspective file selected!")); + } else { + + // import and select it + tabView->importPerspective(fileName); + + // update the table + setTables(); + + // new one added + emit perspectivesChanged(); + } +} + void PerspectiveDialog::upPerspectiveClicked() { diff --git a/src/Gui/PerspectiveDialog.h b/src/Gui/PerspectiveDialog.h index 7924c196c..14ee4a843 100644 --- a/src/Gui/PerspectiveDialog.h +++ b/src/Gui/PerspectiveDialog.h @@ -80,6 +80,9 @@ class PerspectiveDialog : public QDialog void upPerspectiveClicked(); void downPerspectiveClicked(); + void exportPerspectiveClicked(); + void importPerspectiveClicked(); + void perspectiveNameChanged(QTableWidgetItem *); signals: @@ -91,6 +94,7 @@ class PerspectiveDialog : public QDialog PerspectiveTableWidget *perspectiveTable; ChartTableWidget *chartTable; + QPushButton *exportPerspective, *importPerspective; QPushButton *addPerspective, *removePerspective; QToolButton *upPerspective, *downPerspective; QLabel *instructions; diff --git a/src/Gui/TabView.cpp b/src/Gui/TabView.cpp index 4e48b6021..d320b6b22 100644 --- a/src/Gui/TabView.cpp +++ b/src/Gui/TabView.cpp @@ -294,44 +294,8 @@ TabView::saveState() // so lets run through the perspectives foreach(Perspective *page, perspectives_) { - out<<"title_ <<"\" style=\"" << page->currentStyle <<"\">\n"; - - // iterate over charts - foreach (GcChartWindow *chart, page->charts) { - GcWinID type = chart->property("type").value(); - - out<<"\t(type)<<"\" " - <<"name=\""<property("instanceName").toString())<<"\" " - <<"title=\""<property("title").toString())<<"\" >\n"; - - // iterate over chart properties - const QMetaObject *m = chart->metaObject(); - for (int i=0; ipropertyCount(); i++) { - QMetaProperty p = m->property(i); - if (p.isUser(chart)) { - out<<"\t\t(); - s << x; - out<\n"; - } - } - out<<"\t\n"; - } - out<<"\n"; + // write to output stream + page->toXml(out); } out<<"\n"; file.close(); @@ -419,6 +383,7 @@ TabView::restoreState(bool useDefault) // if we *still* don't have content then something went // badly wrong, so only reset if its not blank + QListrestored; if (content != "") { // whilst this happens don't show user @@ -434,22 +399,16 @@ TabView::restoreState(bool useDefault) // parse and instantiate the charts xmlReader.parse(source); - perspectives_ = handler.perspectives; + restored = handler.perspectives; } - if (legacy && perspectives_.count() == 1) perspectives_[0]->title_ = "General"; + if (legacy && restored.count() == 1) restored[0]->title_ = "General"; - if (perspectives_.count() == 0) { - perspective_ = new Perspective(context, "empty", type); - perspectives_ << perspective_; - } + // MUST have at least one + if (restored.count() == 0) restored << new Perspective(context, "empty", type); - // add to stack - foreach(Perspective *page, perspectives_) { - pstack->addWidget(page); - cstack->addWidget(page->controls()); - page->configChanged(0); // set colors correctly- will have missed from startup - } + // initialise them + foreach(Perspective *page, restored) appendPerspective(page); // default to first one perspective_ = perspectives_[0]; @@ -471,7 +430,29 @@ TabView::restoreState(bool useDefault) context->athlete->selectRideFile(context->athlete->rideCache->rides().last()->fileName); } } +} +void +TabView::appendPerspective(Perspective *page) +{ + perspectives_ << page; + pstack->addWidget(page); + cstack->addWidget(page->controls()); + page->configChanged(0); // set colors correctly- will have missed from startup +} + +void +TabView::importPerspective(QString filename) +{ + Perspective *newone = Perspective::fromFile(context, filename, type); + if (newone) appendPerspective(newone); +} + +void +TabView::exportPerspective(Perspective *p, QString filename) +{ + // write to file + p->toFile(filename); } void @@ -483,10 +464,8 @@ TabView::addPerspective(QString name) if (type == VIEW_TRAIN) page->styleChanged(2); else page->styleChanged(0); - perspectives_ << page; - pstack->addWidget(page); - cstack->addWidget(page->controls()); - page->configChanged(0); // set colors correctly- will be empty... + // append + appendPerspective(page); } void diff --git a/src/Gui/TabView.h b/src/Gui/TabView.h index e6ab7d518..a2acf4789 100644 --- a/src/Gui/TabView.h +++ b/src/Gui/TabView.h @@ -76,6 +76,7 @@ class TabView : public QWidget // load/save perspectives void restoreState(bool useDefault = false); void saveState(); + void appendPerspective(Perspective *page); void setPerspectives(QComboBox *perspectiveSelector); // set the combobox when view selected void perspectiveSelected(int index); // combobox selections changed because the user selected a perspective @@ -104,6 +105,9 @@ class TabView : public QWidget void importChart(QMapproperties, bool select) { perspective_->importChart(properties, select); } + void importPerspective(QString filename); + void exportPerspective(Perspective *, QString filename); + signals: void sidebarClosed(); // the user dragged the sidebar closed.