mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-14 16:39:57 +00:00
Porting the codebase to QT 5 (5.2) to get the latest bug fixes, performance and improved platform support. This first part is to fixup the codebase to compile on Qt 5, but some aspects have been broken (video). The second part is to migrate from Qwt 6.0.1 to the latest Qwt for multiaxis support. The third part will be to fixup any platform specific issues or issues identified at runtime.
367 lines
11 KiB
C++
367 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2012 Mark Liversedge (liversedge@gmail.com)
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the Free
|
|
* Software Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc., 51
|
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "NamedSearch.h"
|
|
#include "SearchBox.h"
|
|
#include "Context.h"
|
|
#include "Athlete.h"
|
|
#include "GcSideBarItem.h" // for iconFromPNG
|
|
|
|
// Escape special characters (JSON compliance)
|
|
static QString protect(const QString string)
|
|
{
|
|
QString s = string;
|
|
s.replace("\\", "\\\\"); // backslash
|
|
s.replace("\"", "\\\""); // quote
|
|
s.replace("\t", "\\t"); // tab
|
|
s.replace("\n", "\\n"); // newline
|
|
s.replace("\r", "\\r"); // carriage-return
|
|
s.replace("\b", "\\b"); // backspace
|
|
s.replace("\f", "\\f"); // formfeed
|
|
s.replace("/", "\\/"); // solidus
|
|
s.replace(">", ">"); // angle
|
|
s.replace("<", "<"); // angle
|
|
return s;
|
|
}
|
|
|
|
// Un-Escape special characters (JSON compliance)
|
|
static QString unprotect(const QString string)
|
|
{
|
|
QString string2 = QString::fromLocal8Bit(string.toLatin1().data());
|
|
|
|
// this is a quoted string
|
|
QString s = string2.mid(1,string2.length()-2);
|
|
|
|
// now un-escape the control characters
|
|
s.replace("\\t", "\t"); // tab
|
|
s.replace("\\n", "\n"); // newline
|
|
s.replace("\\r", "\r"); // carriage-return
|
|
s.replace("\\b", "\b"); // backspace
|
|
s.replace("\\f", "\f"); // formfeed
|
|
s.replace("\\/", "/"); // solidus
|
|
s.replace("\\\"", "\""); // quote
|
|
s.replace("\\\\", "\\"); // backslash
|
|
s.replace(">", ">"); // angle
|
|
s.replace("<", "<"); // angle
|
|
|
|
return s;
|
|
}
|
|
void
|
|
NamedSearches::read()
|
|
{
|
|
QFile namedSearchFile(home.absolutePath() + "/namedsearches.xml");
|
|
QXmlInputSource source( &namedSearchFile );
|
|
QXmlSimpleReader xmlReader;
|
|
NamedSearchParser( handler );
|
|
xmlReader.setContentHandler(&handler);
|
|
xmlReader.setErrorHandler(&handler);
|
|
xmlReader.parse( source );
|
|
|
|
// go read them!
|
|
list = handler.getResults();
|
|
|
|
// let everyone know they have changed
|
|
changed();
|
|
}
|
|
|
|
|
|
NamedSearch NamedSearches::get(QString name)
|
|
{
|
|
NamedSearch returning;
|
|
foreach (NamedSearch x, list) {
|
|
if (x.name == name) {
|
|
returning = x;
|
|
break;
|
|
}
|
|
}
|
|
return returning;
|
|
}
|
|
|
|
NamedSearch NamedSearches::get(int index)
|
|
{
|
|
return list[index];
|
|
}
|
|
|
|
void
|
|
NamedSearches::write()
|
|
{
|
|
// update namedSearchs.xml
|
|
QString file = QString(home.absolutePath() + "/namedsearches.xml");
|
|
NamedSearchParser::serialize(file, list);
|
|
athlete->notifyNamedSearchesChanged();
|
|
}
|
|
|
|
void
|
|
NamedSearches::deleteNamedSearch(int index)
|
|
{
|
|
list.removeAt(index);
|
|
write();
|
|
}
|
|
|
|
bool NamedSearchParser::startDocument()
|
|
{
|
|
buffer.clear();
|
|
return true;
|
|
}
|
|
|
|
bool NamedSearchParser::endElement( const QString&, const QString&, const QString &qName )
|
|
{
|
|
if(qName == "name")
|
|
namedSearch.name = unprotect(buffer.trimmed());
|
|
else if (qName == "count")
|
|
namedSearch.count = unprotect(buffer.trimmed()).toInt();
|
|
else if (qName == "type")
|
|
namedSearch.type = unprotect(buffer.trimmed()).toInt();
|
|
else if (qName == "text")
|
|
namedSearch.text = unprotect(buffer.trimmed());
|
|
else if(qName == "NamedSearch") {
|
|
|
|
result.append(namedSearch);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool NamedSearchParser::startElement( const QString&, const QString&, const QString &name, const QXmlAttributes & )
|
|
{
|
|
buffer.clear();
|
|
if(name == "NamedSearch") {
|
|
namedSearch = NamedSearch();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool NamedSearchParser::characters( const QString& str )
|
|
{
|
|
buffer += str;
|
|
return true;
|
|
}
|
|
|
|
bool NamedSearchParser::endDocument()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
NamedSearchParser::serialize(QString filename, QList<NamedSearch>NamedSearches)
|
|
{
|
|
// open file - truncate contents
|
|
QFile file(filename);
|
|
file.open(QFile::WriteOnly);
|
|
file.resize(0);
|
|
QTextStream out(&file);
|
|
out.setCodec("UTF-8");
|
|
|
|
// begin document
|
|
out << "<NamedSearches>\n";
|
|
|
|
// write out to file
|
|
foreach (NamedSearch search, NamedSearches) {
|
|
// main attributes
|
|
out<<QString("\t<NamedSearch>\n"
|
|
"\t\t<name>\"%1\"</name>\n"
|
|
"\t\t<type>\"%2\"</type>\n"
|
|
"\t\t<text>\"%3\"</text>\n") .arg(protect(search.name))
|
|
.arg(search.type)
|
|
.arg(protect(search.text));
|
|
out<<"\t</NamedSearch>\n";
|
|
|
|
}
|
|
|
|
// end document
|
|
out << "</NamedSearches>\n";
|
|
|
|
// close file
|
|
file.close();
|
|
|
|
return true; // success
|
|
}
|
|
|
|
|
|
EditNamedSearches::EditNamedSearches(QWidget *parent, Context *context) : QDialog(parent), context(context), active(false)
|
|
{
|
|
filterIcon = iconFromPNG(":images/toolbar/filter3.png", false);
|
|
searchIcon = iconFromPNG(":images/toolbar/search3.png", false);
|
|
|
|
setWindowTitle(tr("Manage Filters"));
|
|
setWindowFlags(windowFlags() | Qt::Tool);
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
setWindowModality(Qt::NonModal);
|
|
#ifdef Q_OS_MAC
|
|
setFixedSize(350,400);
|
|
#else
|
|
setFixedSize(450,400);
|
|
#endif
|
|
|
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
|
|
// editing the search/filter
|
|
QHBoxLayout *row1 = new QHBoxLayout;
|
|
layout->addLayout(row1);
|
|
QLabel *name = new QLabel(tr("Name"), this);
|
|
row1->addWidget(name);
|
|
editName = new QLineEdit(this);
|
|
row1->addWidget(editName);
|
|
QHBoxLayout *row2 = new QHBoxLayout;
|
|
layout->addLayout(row2);
|
|
QLabel *filter = new QLabel(tr("Filter"), this);
|
|
row2->addWidget(filter);
|
|
editSearch = new SearchBox(context, this, true);
|
|
row2->addWidget(editSearch);
|
|
|
|
// add/update buttons
|
|
QHBoxLayout *row3 = new QHBoxLayout;
|
|
layout->addLayout(row3);
|
|
row3->addStretch();
|
|
addButton = new QPushButton(tr("Add"), this);
|
|
row3->addWidget(addButton);
|
|
updateButton = new QPushButton(tr("Update"), this);
|
|
row3->addWidget(updateButton);
|
|
|
|
// Selection List
|
|
searchList = new QTreeWidget(this);
|
|
layout->addWidget(searchList);
|
|
#ifdef Q_OS_MAC
|
|
searchList->setAttribute(Qt::WA_MacShowFocusRect, 0);
|
|
#endif
|
|
searchList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
searchList->setColumnCount(3);
|
|
searchList->setIndentation(3);
|
|
QStringList headings;
|
|
headings<<"";
|
|
headings<<"Name";
|
|
headings<<"Query";
|
|
searchList->setHeaderLabels(headings);
|
|
searchList->header()->setMinimumSectionSize(30);
|
|
searchList->header()->resizeSection(0, 30);
|
|
#ifdef Q_OS_MAC
|
|
searchList->header()->resizeSection(1, 120);
|
|
#else
|
|
searchList->header()->resizeSection(1, 150);
|
|
#endif
|
|
searchList->header()->setStretchLastSection(true);
|
|
|
|
// delete button
|
|
QHBoxLayout *row4 = new QHBoxLayout;
|
|
layout->addLayout(row4);
|
|
row4->addStretch();
|
|
deleteButton = new QPushButton(tr("Delete"), this);
|
|
row4->addWidget(deleteButton);
|
|
|
|
// Populate the list of named searches
|
|
foreach(NamedSearch x, context->athlete->namedSearches->getList()) {
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(searchList->invisibleRootItem(), 0);
|
|
add->setIcon(0, x.type == NamedSearch::search ? searchIcon : filterIcon);
|
|
add->setText(1, x.name);
|
|
add->setText(2, x.text);
|
|
}
|
|
connect(searchList, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
|
|
|
|
// and select the first one
|
|
if (context->athlete->namedSearches->getList().count()) {
|
|
searchList->setCurrentItem(searchList->invisibleRootItem()->child(0));
|
|
}
|
|
|
|
// connect the buttons
|
|
connect(addButton, SIGNAL(clicked()), this, SLOT(addClicked()));
|
|
connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked()));
|
|
connect(updateButton, SIGNAL(clicked()), this, SLOT(updateClicked()));
|
|
}
|
|
|
|
void
|
|
EditNamedSearches::selectionChanged()
|
|
{
|
|
if (active || searchList->currentItem() == NULL) return;
|
|
|
|
int index = searchList->invisibleRootItem()->indexOfChild(searchList->currentItem());
|
|
NamedSearch x = context->athlete->namedSearches->getList().at(index);
|
|
|
|
editName->setText(x.name);
|
|
editSearch->setText(x.text);
|
|
editSearch->setMode(static_cast<SearchBox::SearchBoxMode>(x.type));
|
|
}
|
|
|
|
void
|
|
EditNamedSearches::addClicked()
|
|
{
|
|
if (editName->text() == "" || editSearch->text() == "" || active) return;
|
|
active = true;
|
|
|
|
NamedSearch x;
|
|
x.text = editSearch->text();
|
|
x.name = editName->text();
|
|
x.type = editSearch->getMode();
|
|
context->athlete->namedSearches->getList().append(x);
|
|
|
|
QTreeWidgetItem *add = new QTreeWidgetItem(searchList->invisibleRootItem(), 0);
|
|
add->setIcon(0, x.type == NamedSearch::search ? searchIcon : filterIcon);
|
|
add->setText(1, x.name);
|
|
add->setText(2, x.text);
|
|
|
|
searchList->setCurrentItem(add);
|
|
active=false;
|
|
selectionChanged();
|
|
}
|
|
|
|
void
|
|
EditNamedSearches::updateClicked()
|
|
{
|
|
if (active || searchList->currentItem() == NULL) return;
|
|
active = true;
|
|
|
|
int index = searchList->invisibleRootItem()->indexOfChild(searchList->currentItem());
|
|
|
|
// update the text
|
|
context->athlete->namedSearches->getList()[index].name = editName->text();
|
|
context->athlete->namedSearches->getList()[index].type = editSearch->getMode();
|
|
context->athlete->namedSearches->getList()[index].text = editSearch->text();
|
|
|
|
QTreeWidgetItem *here = searchList->invisibleRootItem()->child(index);
|
|
here->setIcon(0, editSearch->getMode() == 0 ? searchIcon : filterIcon);
|
|
here->setText(1, editName->text());
|
|
here->setText(2, editSearch->text());
|
|
|
|
active = false;
|
|
selectionChanged(); // QT signals whilst rows are being removed, this is very confusing
|
|
}
|
|
|
|
void
|
|
EditNamedSearches::deleteClicked()
|
|
{
|
|
if (active || searchList->currentItem() == NULL) return;
|
|
active = true;
|
|
|
|
int index = searchList->invisibleRootItem()->indexOfChild(searchList->currentItem());
|
|
context->athlete->namedSearches->getList().removeAt(index);
|
|
delete searchList->invisibleRootItem()->takeChild(index);
|
|
|
|
active = false;
|
|
selectionChanged(); // QT signals whilst rows are being removed, this is very confusing
|
|
}
|
|
|
|
// trap close dialog and update named searches in mainwindow/on disk
|
|
void EditNamedSearches::closeEvent(QCloseEvent*) { writeSearches(); }
|
|
void EditNamedSearches::reject() { writeSearches(); }
|
|
|
|
void
|
|
EditNamedSearches::writeSearches()
|
|
{
|
|
context->athlete->namedSearches->write();
|
|
}
|
|
|