mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-13 16:18:42 +00:00
Perspectives - Part 4a of 4
.. Import and Export perspectives to an '.gchartset' file as XML data. .. Added to MainWindow's View menu and the Manage Perspectives dialog.
This commit is contained in:
@@ -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()));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<handler.perspectives.count(); i++) delete (handler.perspectives[i]);
|
||||
|
||||
// return it, but bear in mind it hasn't been initialised (current ride, date range etc)
|
||||
return returning;
|
||||
}
|
||||
|
||||
bool
|
||||
Perspective::toFile(QString filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QFile::WriteOnly)) return false;
|
||||
|
||||
// truncate and use 8bit encoding
|
||||
file.resize(0);
|
||||
QTextStream out(&file);
|
||||
out.setCodec("UTF-8");
|
||||
|
||||
// write to output stream
|
||||
toXml(out);
|
||||
|
||||
// all done
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Perspective::toXml(QTextStream &out)
|
||||
{
|
||||
out<<"<layout name=\""<< title_ <<"\" style=\"" << currentStyle <<"\">\n";
|
||||
|
||||
// iterate over charts
|
||||
foreach (GcChartWindow *chart, charts) {
|
||||
GcWinID type = chart->property("type").value<GcWinID>();
|
||||
|
||||
out<<"\t<chart id=\""<<static_cast<int>(type)<<"\" "
|
||||
<<"name=\""<<Utils::xmlprotect(chart->property("instanceName").toString())<<"\" "
|
||||
<<"title=\""<<Utils::xmlprotect(chart->property("title").toString())<<"\" >\n";
|
||||
|
||||
// iterate over chart properties
|
||||
const QMetaObject *m = chart->metaObject();
|
||||
for (int i=0; i<m->propertyCount(); i++) {
|
||||
QMetaProperty p = m->property(i);
|
||||
if (p.isUser(chart)) {
|
||||
out<<"\t\t<property name=\""<<Utils::xmlprotect(p.name())<<"\" "
|
||||
<<"type=\""<<p.typeName()<<"\" "
|
||||
<<"value=\"";
|
||||
|
||||
if (QString(p.typeName()) == "int") out<<p.read(chart).toInt();
|
||||
if (QString(p.typeName()) == "double") out<<p.read(chart).toDouble();
|
||||
if (QString(p.typeName()) == "QDate") out<<p.read(chart).toDate().toString();
|
||||
if (QString(p.typeName()) == "QString") out<<Utils::xmlprotect(p.read(chart).toString());
|
||||
if (QString(p.typeName()) == "bool") out<<p.read(chart).toBool();
|
||||
if (QString(p.typeName()) == "LTMSettings") {
|
||||
QByteArray marshall;
|
||||
QDataStream s(&marshall, QIODevice::WriteOnly);
|
||||
LTMSettings x = p.read(chart).value<LTMSettings>();
|
||||
s << x;
|
||||
out<<marshall.toBase64();
|
||||
}
|
||||
|
||||
out<<"\" />\n";
|
||||
}
|
||||
}
|
||||
out<<"\t</chart>\n";
|
||||
}
|
||||
out<<"</layout>\n";
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------------
|
||||
* Import Chart Dialog - select/deselect charts before importing them
|
||||
* -----------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
///
|
||||
/// 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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -294,44 +294,8 @@ TabView::saveState()
|
||||
// so lets run through the perspectives
|
||||
foreach(Perspective *page, perspectives_) {
|
||||
|
||||
out<<"<layout name=\""<< page->title_ <<"\" style=\"" << page->currentStyle <<"\">\n";
|
||||
|
||||
// iterate over charts
|
||||
foreach (GcChartWindow *chart, page->charts) {
|
||||
GcWinID type = chart->property("type").value<GcWinID>();
|
||||
|
||||
out<<"\t<chart id=\""<<static_cast<int>(type)<<"\" "
|
||||
<<"name=\""<<Utils::xmlprotect(chart->property("instanceName").toString())<<"\" "
|
||||
<<"title=\""<<Utils::xmlprotect(chart->property("title").toString())<<"\" >\n";
|
||||
|
||||
// iterate over chart properties
|
||||
const QMetaObject *m = chart->metaObject();
|
||||
for (int i=0; i<m->propertyCount(); i++) {
|
||||
QMetaProperty p = m->property(i);
|
||||
if (p.isUser(chart)) {
|
||||
out<<"\t\t<property name=\""<<Utils::xmlprotect(p.name())<<"\" "
|
||||
<<"type=\""<<p.typeName()<<"\" "
|
||||
<<"value=\"";
|
||||
|
||||
if (QString(p.typeName()) == "int") out<<p.read(chart).toInt();
|
||||
if (QString(p.typeName()) == "double") out<<p.read(chart).toDouble();
|
||||
if (QString(p.typeName()) == "QDate") out<<p.read(chart).toDate().toString();
|
||||
if (QString(p.typeName()) == "QString") out<<Utils::xmlprotect(p.read(chart).toString());
|
||||
if (QString(p.typeName()) == "bool") out<<p.read(chart).toBool();
|
||||
if (QString(p.typeName()) == "LTMSettings") {
|
||||
QByteArray marshall;
|
||||
QDataStream s(&marshall, QIODevice::WriteOnly);
|
||||
LTMSettings x = p.read(chart).value<LTMSettings>();
|
||||
s << x;
|
||||
out<<marshall.toBase64();
|
||||
}
|
||||
|
||||
out<<"\" />\n";
|
||||
}
|
||||
}
|
||||
out<<"\t</chart>\n";
|
||||
}
|
||||
out<<"</layout>\n";
|
||||
// write to output stream
|
||||
page->toXml(out);
|
||||
}
|
||||
out<<"</layouts>\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
|
||||
QList<Perspective*>restored;
|
||||
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
|
||||
|
||||
@@ -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(QMap<QString,QString>properties, bool select) { perspective_->importChart(properties, select); }
|
||||
|
||||
void importPerspective(QString filename);
|
||||
void exportPerspective(Perspective *, QString filename);
|
||||
|
||||
signals:
|
||||
|
||||
void sidebarClosed(); // the user dragged the sidebar closed.
|
||||
|
||||
Reference in New Issue
Block a user