Files
GoldenCheetah/src/NamedSearch.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

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("<", "&lt;"); // 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("&gt;", ">"); // angle
s.replace("&lt;", "<"); // 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();
}