Polish the XML bookmarks examples

- Use Qt 5 connect syntax.
- Streamline code, remove unused members.
- Add a context menu for copying and opening the URLs.
- Add const to XML code.
- In the XML code, show the use of QStringLiteral in static inline
  functions to create strings versus QLatin1String in comparison
  overloads to avoid allocating strings from const char * literals.

Change-Id: Ib5e62ca188e271ffe01996dff3c9ea8e0b60739a
Reviewed-by: Topi Reiniö <topi.reinio@qt.io>
This commit is contained in:
Friedemann Kleint 2016-10-31 11:09:00 +01:00
parent 146f6d261b
commit 8d71fae080
16 changed files with 238 additions and 219 deletions

View File

@ -58,13 +58,13 @@ MainWindow::MainWindow()
xbelTree = new XbelTree; xbelTree = new XbelTree;
setCentralWidget(xbelTree); setCentralWidget(xbelTree);
createActions();
createMenus(); createMenus();
statusBar()->showMessage(tr("Ready")); statusBar()->showMessage(tr("Ready"));
setWindowTitle(tr("DOM Bookmarks")); setWindowTitle(tr("DOM Bookmarks"));
resize(480, 320); const QSize availableSize = QApplication::desktop()->availableGeometry(this).size();
resize(availableSize.width() / 2, availableSize.height() / 3);
} }
void MainWindow::open() void MainWindow::open()
@ -80,8 +80,8 @@ void MainWindow::open()
if (!file.open(QFile::ReadOnly | QFile::Text)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("SAX Bookmarks"), QMessageBox::warning(this, tr("SAX Bookmarks"),
tr("Cannot read file %1:\n%2.") tr("Cannot read file %1:\n%2.")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(file.errorString())); file.errorString()));
return; return;
} }
@ -102,8 +102,8 @@ void MainWindow::saveAs()
if (!file.open(QFile::WriteOnly | QFile::Text)) { if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, tr("SAX Bookmarks"), QMessageBox::warning(this, tr("SAX Bookmarks"),
tr("Cannot write file %1:\n%2.") tr("Cannot write file %1:\n%2.")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(file.errorString())); file.errorString()));
return; return;
} }
@ -119,37 +119,21 @@ void MainWindow::about()
"documents.")); "documents."));
} }
void MainWindow::createActions()
{
openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
saveAsAct = new QAction(tr("&Save As..."), this);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcuts(QKeySequence::Quit);
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
aboutAct = new QAction(tr("&About"), this);
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
aboutQtAct = new QAction(tr("About &Qt"), this);
connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
}
void MainWindow::createMenus() void MainWindow::createMenus()
{ {
fileMenu = menuBar()->addMenu(tr("&File")); QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAct); QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open);
fileMenu->addAction(saveAsAct); openAct->setShortcuts(QKeySequence::Open);
fileMenu->addAction(exitAct);
QAction *saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &MainWindow::saveAs);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
exitAct->setShortcuts(QKeySequence::Quit);
menuBar()->addSeparator(); menuBar()->addSeparator();
helpMenu = menuBar()->addMenu(tr("&Help")); QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAct); helpMenu->addAction(tr("&About"), this, &MainWindow::about);
helpMenu->addAction(aboutQtAct); helpMenu->addAction(tr("About &Qt"), qApp, &QCoreApplication::quit);
} }

View File

@ -68,18 +68,9 @@ public slots:
void about(); void about();
private: private:
void createActions();
void createMenus(); void createMenus();
XbelTree *xbelTree; XbelTree *xbelTree;
QMenu *fileMenu;
QMenu *helpMenu;
QAction *openAct;
QAction *saveAsAct;
QAction *exitAct;
QAction *aboutAct;
QAction *aboutQtAct;
}; };
#endif #endif

View File

@ -52,6 +52,18 @@
#include "xbeltree.h" #include "xbeltree.h"
enum { DomElementRole = Qt::UserRole + 1 };
Q_DECLARE_METATYPE(QDomElement)
static inline QString titleElement() { return QStringLiteral("title"); }
static inline QString folderElement() { return QStringLiteral("folder"); }
static inline QString bookmarkElement() { return QStringLiteral("bookmark"); }
static inline QString versionAttribute() { return QStringLiteral("version"); }
static inline QString hrefAttribute() { return QStringLiteral("href"); }
static inline QString foldedAttribute() { return QStringLiteral("folded"); }
XbelTree::XbelTree(QWidget *parent) XbelTree::XbelTree(QWidget *parent)
: QTreeWidget(parent) : QTreeWidget(parent)
{ {
@ -68,6 +80,24 @@ XbelTree::XbelTree(QWidget *parent)
bookmarkIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon)); bookmarkIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon));
} }
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
void XbelTree::contextMenuEvent(QContextMenuEvent *event)
{
const QTreeWidgetItem *item = itemAt(event->pos());
if (!item)
return;
const QString url = item->text(1);
QMenu contextMenu;
QAction *copyAction = contextMenu.addAction(tr("Copy Link to Clipboard"));
QAction *openAction = contextMenu.addAction(tr("Open"));
QAction *action = contextMenu.exec(event->globalPos());
if (action == copyAction)
QGuiApplication::clipboard()->setText(url);
else if (action == openAction)
QDesktopServices::openUrl(QUrl(url));
}
#endif // !QT_NO_CONTEXTMENU && !QT_NO_CLIPBOARD
bool XbelTree::read(QIODevice *device) bool XbelTree::read(QIODevice *device)
{ {
QString errorStr; QString errorStr;
@ -89,8 +119,8 @@ bool XbelTree::read(QIODevice *device)
QMessageBox::information(window(), tr("DOM Bookmarks"), QMessageBox::information(window(), tr("DOM Bookmarks"),
tr("The file is not an XBEL file.")); tr("The file is not an XBEL file."));
return false; return false;
} else if (root.hasAttribute("version") } else if (root.hasAttribute(versionAttribute())
&& root.attribute("version") != "1.0") { && root.attribute(versionAttribute()) != QLatin1String("1.0")) {
QMessageBox::information(window(), tr("DOM Bookmarks"), QMessageBox::information(window(), tr("DOM Bookmarks"),
tr("The file is not an XBEL version 1.0 " tr("The file is not an XBEL version 1.0 "
"file.")); "file."));
@ -99,22 +129,20 @@ bool XbelTree::read(QIODevice *device)
clear(); clear();
disconnect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), disconnect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement);
this, SLOT(updateDomElement(QTreeWidgetItem*,int)));
QDomElement child = root.firstChildElement("folder"); QDomElement child = root.firstChildElement(folderElement());
while (!child.isNull()) { while (!child.isNull()) {
parseFolderElement(child); parseFolderElement(child);
child = child.nextSiblingElement("folder"); child = child.nextSiblingElement(folderElement());
} }
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), connect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement);
this, SLOT(updateDomElement(QTreeWidgetItem*,int)));
return true; return true;
} }
bool XbelTree::write(QIODevice *device) bool XbelTree::write(QIODevice *device) const
{ {
const int IndentSize = 4; const int IndentSize = 4;
@ -123,21 +151,21 @@ bool XbelTree::write(QIODevice *device)
return true; return true;
} }
void XbelTree::updateDomElement(QTreeWidgetItem *item, int column) void XbelTree::updateDomElement(const QTreeWidgetItem *item, int column)
{ {
QDomElement element = domElementForItem.value(item); QDomElement element = item->data(0, DomElementRole).value<QDomElement>();
if (!element.isNull()) { if (!element.isNull()) {
if (column == 0) { if (column == 0) {
QDomElement oldTitleElement = element.firstChildElement("title"); QDomElement oldTitleElement = element.firstChildElement(titleElement());
QDomElement newTitleElement = domDocument.createElement("title"); QDomElement newTitleElement = domDocument.createElement(titleElement());
QDomText newTitleText = domDocument.createTextNode(item->text(0)); QDomText newTitleText = domDocument.createTextNode(item->text(0));
newTitleElement.appendChild(newTitleText); newTitleElement.appendChild(newTitleText);
element.replaceChild(newTitleElement, oldTitleElement); element.replaceChild(newTitleElement, oldTitleElement);
} else { } else {
if (element.tagName() == "bookmark") if (element.tagName() == bookmarkElement())
element.setAttribute("href", item->text(1)); element.setAttribute(hrefAttribute(), item->text(1));
} }
} }
} }
@ -147,7 +175,7 @@ void XbelTree::parseFolderElement(const QDomElement &element,
{ {
QTreeWidgetItem *item = createItem(element, parentItem); QTreeWidgetItem *item = createItem(element, parentItem);
QString title = element.firstChildElement("title").text(); QString title = element.firstChildElement(titleElement()).text();
if (title.isEmpty()) if (title.isEmpty())
title = QObject::tr("Folder"); title = QObject::tr("Folder");
@ -155,25 +183,25 @@ void XbelTree::parseFolderElement(const QDomElement &element,
item->setIcon(0, folderIcon); item->setIcon(0, folderIcon);
item->setText(0, title); item->setText(0, title);
bool folded = (element.attribute("folded") != "no"); bool folded = (element.attribute(foldedAttribute()) != QLatin1String("no"));
setItemExpanded(item, !folded); setItemExpanded(item, !folded);
QDomElement child = element.firstChildElement(); QDomElement child = element.firstChildElement();
while (!child.isNull()) { while (!child.isNull()) {
if (child.tagName() == "folder") { if (child.tagName() == folderElement()) {
parseFolderElement(child, item); parseFolderElement(child, item);
} else if (child.tagName() == "bookmark") { } else if (child.tagName() == bookmarkElement()) {
QTreeWidgetItem *childItem = createItem(child, item); QTreeWidgetItem *childItem = createItem(child, item);
QString title = child.firstChildElement("title").text(); QString title = child.firstChildElement(titleElement()).text();
if (title.isEmpty()) if (title.isEmpty())
title = QObject::tr("Folder"); title = QObject::tr("Folder");
childItem->setFlags(item->flags() | Qt::ItemIsEditable); childItem->setFlags(item->flags() | Qt::ItemIsEditable);
childItem->setIcon(0, bookmarkIcon); childItem->setIcon(0, bookmarkIcon);
childItem->setText(0, title); childItem->setText(0, title);
childItem->setText(1, child.attribute("href")); childItem->setText(1, child.attribute(hrefAttribute()));
} else if (child.tagName() == "separator") { } else if (child.tagName() == QLatin1String("separator")) {
QTreeWidgetItem *childItem = createItem(child, item); QTreeWidgetItem *childItem = createItem(child, item);
childItem->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEditable)); childItem->setFlags(item->flags() & ~(Qt::ItemIsSelectable | Qt::ItemIsEditable));
childItem->setText(0, QString(30, 0xB7)); childItem->setText(0, QString(30, 0xB7));
@ -191,6 +219,6 @@ QTreeWidgetItem *XbelTree::createItem(const QDomElement &element,
} else { } else {
item = new QTreeWidgetItem(this); item = new QTreeWidgetItem(this);
} }
domElementForItem.insert(item, element); item->setData(0, DomElementRole, QVariant::fromValue(element));
return item; return item;
} }

View File

@ -52,7 +52,6 @@
#define XBELTREE_H #define XBELTREE_H
#include <QDomDocument> #include <QDomDocument>
#include <QHash>
#include <QIcon> #include <QIcon>
#include <QTreeWidget> #include <QTreeWidget>
@ -64,10 +63,15 @@ public:
XbelTree(QWidget *parent = 0); XbelTree(QWidget *parent = 0);
bool read(QIODevice *device); bool read(QIODevice *device);
bool write(QIODevice *device); bool write(QIODevice *device) const;
protected:
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
void contextMenuEvent(QContextMenuEvent *event) Q_DECL_OVERRIDE;
#endif
private slots: private slots:
void updateDomElement(QTreeWidgetItem *item, int column); void updateDomElement(const QTreeWidgetItem *item, int column);
private: private:
void parseFolderElement(const QDomElement &element, void parseFolderElement(const QDomElement &element,
@ -76,7 +80,6 @@ private:
QTreeWidgetItem *parentItem = 0); QTreeWidgetItem *parentItem = 0);
QDomDocument domDocument; QDomDocument domDocument;
QHash<QTreeWidgetItem *, QDomElement> domElementForItem;
QIcon folderIcon; QIcon folderIcon;
QIcon bookmarkIcon; QIcon bookmarkIcon;
}; };

View File

@ -62,17 +62,40 @@ MainWindow::MainWindow()
treeWidget = new QTreeWidget; treeWidget = new QTreeWidget;
treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
treeWidget->setHeaderLabels(labels); treeWidget->setHeaderLabels(labels);
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeWidget, &QWidget::customContextMenuRequested,
this, &MainWindow::onCustomContextMenuRequested);
#endif
setCentralWidget(treeWidget); setCentralWidget(treeWidget);
createActions();
createMenus(); createMenus();
statusBar()->showMessage(tr("Ready")); statusBar()->showMessage(tr("Ready"));
setWindowTitle(tr("SAX Bookmarks")); setWindowTitle(tr("SAX Bookmarks"));
resize(480, 320); const QSize availableSize = QApplication::desktop()->availableGeometry(this).size();
resize(availableSize.width() / 2, availableSize.height() / 3);
} }
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
{
const QTreeWidgetItem *item = treeWidget->itemAt(pos);
if (!item)
return;
const QString url = item->text(1);
QMenu contextMenu;
QAction *copyAction = contextMenu.addAction(tr("Copy Link to Clipboard"));
QAction *openAction = contextMenu.addAction(tr("Open"));
QAction *action = contextMenu.exec(treeWidget->viewport()->mapToGlobal(pos));
if (action == copyAction)
QGuiApplication::clipboard()->setText(url);
else if (action == openAction)
QDesktopServices::openUrl(QUrl(url));
}
#endif // !QT_NO_CONTEXTMENU && !QT_NO_CLIPBOARD
void MainWindow::open() void MainWindow::open()
{ {
QString fileName = QString fileName =
@ -93,8 +116,8 @@ void MainWindow::open()
if (!file.open(QFile::ReadOnly | QFile::Text)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("SAX Bookmarks"), QMessageBox::warning(this, tr("SAX Bookmarks"),
tr("Cannot read file %1:\n%2.") tr("Cannot read file %1:\n%2.")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(file.errorString())); file.errorString()));
return; return;
} }
@ -116,8 +139,8 @@ void MainWindow::saveAs()
if (!file.open(QFile::WriteOnly | QFile::Text)) { if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, tr("SAX Bookmarks"), QMessageBox::warning(this, tr("SAX Bookmarks"),
tr("Cannot write file %1:\n%2.") tr("Cannot write file %1:\n%2.")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(file.errorString())); file.errorString()));
return; return;
} }
@ -134,37 +157,21 @@ void MainWindow::about()
"hand.")); "hand."));
} }
void MainWindow::createActions()
{
openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
saveAsAct = new QAction(tr("&Save As..."), this);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcuts(QKeySequence::Quit);
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
aboutAct = new QAction(tr("&About"), this);
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
aboutQtAct = new QAction(tr("About &Qt"), this);
connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
}
void MainWindow::createMenus() void MainWindow::createMenus()
{ {
fileMenu = menuBar()->addMenu(tr("&File")); QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAct); QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open);
fileMenu->addAction(saveAsAct); openAct->setShortcuts(QKeySequence::Open);
fileMenu->addAction(exitAct);
QAction *saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &MainWindow::saveAs);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
exitAct->setShortcuts(QKeySequence::Quit);
menuBar()->addSeparator(); menuBar()->addSeparator();
helpMenu = menuBar()->addMenu(tr("&Help")); QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAct); helpMenu->addAction(tr("&About"), this, &MainWindow::about);
helpMenu->addAction(aboutQtAct); helpMenu->addAction(tr("About &Qt"), qApp, &QCoreApplication::quit);
} }

View File

@ -68,20 +68,13 @@ public slots:
void open(); void open();
void saveAs(); void saveAs();
void about(); void about();
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
void onCustomContextMenuRequested(const QPoint &pos);
#endif
private: private:
void createActions();
void createMenus(); void createMenus();
QTreeWidget *treeWidget; QTreeWidget *treeWidget;
QMenu *fileMenu;
QMenu *helpMenu;
QAction *openAct;
QAction *saveAsAct;
QAction *exitAct;
QAction *aboutAct;
QAction *aboutQtAct;
}; };
#endif #endif

View File

@ -52,7 +52,7 @@
#include "xbelgenerator.h" #include "xbelgenerator.h"
XbelGenerator::XbelGenerator(QTreeWidget *treeWidget) XbelGenerator::XbelGenerator(const QTreeWidget *treeWidget)
: treeWidget(treeWidget) : treeWidget(treeWidget)
{ {
} }
@ -81,25 +81,25 @@ QString XbelGenerator::indent(int depth)
QString XbelGenerator::escapedText(const QString &str) QString XbelGenerator::escapedText(const QString &str)
{ {
QString result = str; QString result = str;
result.replace("&", "&amp;"); result.replace('&', "&amp;");
result.replace("<", "&lt;"); result.replace('<', "&lt;");
result.replace(">", "&gt;"); result.replace('>', "&gt;");
return result; return result;
} }
QString XbelGenerator::escapedAttribute(const QString &str) QString XbelGenerator::escapedAttribute(const QString &str)
{ {
QString result = escapedText(str); QString result = escapedText(str);
result.replace("\"", "&quot;"); result.replace(QLatin1Char('"'), "&quot;");
result.prepend(QLatin1Char('"')); result.prepend(QLatin1Char('"'));
result.append(QLatin1Char('"')); result.append(QLatin1Char('"'));
return result; return result;
} }
void XbelGenerator::generateItem(QTreeWidgetItem *item, int depth) void XbelGenerator::generateItem(const QTreeWidgetItem *item, int depth)
{ {
QString tagName = item->data(0, Qt::UserRole).toString(); QString tagName = item->data(0, Qt::UserRole).toString();
if (tagName == "folder") { if (tagName == QLatin1String("folder")) {
bool folded = !treeWidget->isItemExpanded(item); bool folded = !treeWidget->isItemExpanded(item);
out << indent(depth) << "<folder folded=\"" << (folded ? "yes" : "no") out << indent(depth) << "<folder folded=\"" << (folded ? "yes" : "no")
<< "\">\n" << "\">\n"
@ -110,7 +110,7 @@ void XbelGenerator::generateItem(QTreeWidgetItem *item, int depth)
generateItem(item->child(i), depth + 1); generateItem(item->child(i), depth + 1);
out << indent(depth) << "</folder>\n"; out << indent(depth) << "</folder>\n";
} else if (tagName == "bookmark") { } else if (tagName == QLatin1String("bookmark")) {
out << indent(depth) << "<bookmark"; out << indent(depth) << "<bookmark";
if (!item->text(1).isEmpty()) if (!item->text(1).isEmpty())
out << " href=" << escapedAttribute(item->text(1)); out << " href=" << escapedAttribute(item->text(1));
@ -118,7 +118,7 @@ void XbelGenerator::generateItem(QTreeWidgetItem *item, int depth)
<< indent(depth + 1) << "<title>" << escapedText(item->text(0)) << indent(depth + 1) << "<title>" << escapedText(item->text(0))
<< "</title>\n" << "</title>\n"
<< indent(depth) << "</bookmark>\n"; << indent(depth) << "</bookmark>\n";
} else if (tagName == "separator") { } else if (tagName == QLatin1String("separator")) {
out << indent(depth) << "<separator/>\n"; out << indent(depth) << "<separator/>\n";
} }
} }

View File

@ -61,7 +61,7 @@ QT_END_NAMESPACE
class XbelGenerator class XbelGenerator
{ {
public: public:
XbelGenerator(QTreeWidget *treeWidget); explicit XbelGenerator(const QTreeWidget *treeWidget);
bool write(QIODevice *device); bool write(QIODevice *device);
@ -69,9 +69,9 @@ private:
static QString indent(int indentLevel); static QString indent(int indentLevel);
static QString escapedText(const QString &str); static QString escapedText(const QString &str);
static QString escapedAttribute(const QString &str); static QString escapedAttribute(const QString &str);
void generateItem(QTreeWidgetItem *item, int depth); void generateItem(const QTreeWidgetItem *item, int depth);
QTreeWidget *treeWidget; const QTreeWidget *treeWidget;
QTextStream out; QTextStream out;
}; };

View File

@ -52,6 +52,10 @@
#include "xbelhandler.h" #include "xbelhandler.h"
static inline QString versionAttribute() { return QStringLiteral("version"); }
static inline QString hrefAttribute() { return QStringLiteral("href"); }
static inline QString foldedAttribute() { return QStringLiteral("folded"); }
XbelHandler::XbelHandler(QTreeWidget *treeWidget) XbelHandler::XbelHandler(QTreeWidget *treeWidget)
: treeWidget(treeWidget) : treeWidget(treeWidget)
{ {
@ -72,32 +76,32 @@ bool XbelHandler::startElement(const QString & /* namespaceURI */,
const QString &qName, const QString &qName,
const QXmlAttributes &attributes) const QXmlAttributes &attributes)
{ {
if (!metXbelTag && qName != "xbel") { if (!metXbelTag && qName != QLatin1String("xbel")) {
errorStr = QObject::tr("The file is not an XBEL file."); errorStr = QObject::tr("The file is not an XBEL file.");
return false; return false;
} }
if (qName == "xbel") { if (qName == QLatin1String("xbel")) {
QString version = attributes.value("version"); QString version = attributes.value(versionAttribute());
if (!version.isEmpty() && version != "1.0") { if (!version.isEmpty() && version != QLatin1String("1.0")) {
errorStr = QObject::tr("The file is not an XBEL version 1.0 file."); errorStr = QObject::tr("The file is not an XBEL version 1.0 file.");
return false; return false;
} }
metXbelTag = true; metXbelTag = true;
} else if (qName == "folder") { } else if (qName == QLatin1String("folder")) {
item = createChildItem(qName); item = createChildItem(qName);
item->setFlags(item->flags() | Qt::ItemIsEditable); item->setFlags(item->flags() | Qt::ItemIsEditable);
item->setIcon(0, folderIcon); item->setIcon(0, folderIcon);
item->setText(0, QObject::tr("Folder")); item->setText(0, QObject::tr("Folder"));
bool folded = (attributes.value("folded") != "no"); bool folded = (attributes.value(foldedAttribute()) != QLatin1String("no"));
treeWidget->setItemExpanded(item, !folded); treeWidget->setItemExpanded(item, !folded);
} else if (qName == "bookmark") { } else if (qName == QLatin1String("bookmark")) {
item = createChildItem(qName); item = createChildItem(qName);
item->setFlags(item->flags() | Qt::ItemIsEditable); item->setFlags(item->flags() | Qt::ItemIsEditable);
item->setIcon(0, bookmarkIcon); item->setIcon(0, bookmarkIcon);
item->setText(0, QObject::tr("Unknown title")); item->setText(0, QObject::tr("Unknown title"));
item->setText(1, attributes.value("href")); item->setText(1, attributes.value(hrefAttribute()));
} else if (qName == "separator") { } else if (qName == QLatin1String("separator")) {
item = createChildItem(qName); item = createChildItem(qName);
item->setFlags(item->flags() & ~Qt::ItemIsSelectable); item->setFlags(item->flags() & ~Qt::ItemIsSelectable);
item->setText(0, QString(30, 0xB7)); item->setText(0, QString(30, 0xB7));
@ -111,11 +115,11 @@ bool XbelHandler::endElement(const QString & /* namespaceURI */,
const QString & /* localName */, const QString & /* localName */,
const QString &qName) const QString &qName)
{ {
if (qName == "title") { if (qName == QLatin1String("title")) {
if (item) if (item)
item->setText(0, currentText); item->setText(0, currentText);
} else if (qName == "folder" || qName == "bookmark" } else if (qName == QLatin1String("folder") || qName == QLatin1String("bookmark")
|| qName == "separator") { || qName == QLatin1String("separator")) {
item = item->parent(); item = item->parent();
} }
return true; return true;

View File

@ -167,7 +167,7 @@
add them to the \c fileMenu and \c helpMenu. The connections are as shown add them to the \c fileMenu and \c helpMenu. The connections are as shown
below: below:
\snippet streambookmarks/mainwindow.cpp 4 \snippet streambookmarks/mainwindow.cpp 5
The \c createMenus() function creates the \c fileMenu and \c helpMenu The \c createMenus() function creates the \c fileMenu and \c helpMenu
and adds the QAction objects to them in order to create the menu shown and adds the QAction objects to them in order to create the menu shown

View File

@ -63,18 +63,41 @@ MainWindow::MainWindow()
treeWidget = new QTreeWidget; treeWidget = new QTreeWidget;
treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch);
treeWidget->setHeaderLabels(labels); treeWidget->setHeaderLabels(labels);
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeWidget, &QWidget::customContextMenuRequested,
this, &MainWindow::onCustomContextMenuRequested);
#endif
setCentralWidget(treeWidget); setCentralWidget(treeWidget);
createActions();
createMenus(); createMenus();
statusBar()->showMessage(tr("Ready")); statusBar()->showMessage(tr("Ready"));
setWindowTitle(tr("QXmlStream Bookmarks")); setWindowTitle(tr("QXmlStream Bookmarks"));
resize(480, 320); const QSize availableSize = QApplication::desktop()->availableGeometry(this).size();
resize(availableSize.width() / 2, availableSize.height() / 3);
} }
//! [0] //! [0]
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
{
const QTreeWidgetItem *item = treeWidget->itemAt(pos);
if (!item)
return;
const QString url = item->text(1);
QMenu contextMenu;
QAction *copyAction = contextMenu.addAction(tr("Copy Link to Clipboard"));
QAction *openAction = contextMenu.addAction(tr("Open"));
QAction *action = contextMenu.exec(treeWidget->viewport()->mapToGlobal(pos));
if (action == copyAction)
QGuiApplication::clipboard()->setText(url);
else if (action == openAction)
QDesktopServices::openUrl(QUrl(url));
}
#endif // !QT_NO_CONTEXTMENU && !QT_NO_CLIPBOARD
//! [1] //! [1]
void MainWindow::open() void MainWindow::open()
{ {
@ -92,8 +115,8 @@ void MainWindow::open()
if (!file.open(QFile::ReadOnly | QFile::Text)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"), QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot read file %1:\n%2.") tr("Cannot read file %1:\n%2.")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(file.errorString())); file.errorString()));
return; return;
} }
@ -101,8 +124,8 @@ void MainWindow::open()
if (!reader.read(&file)) { if (!reader.read(&file)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"), QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Parse error in file %1:\n\n%2") tr("Parse error in file %1:\n\n%2")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(reader.errorString())); reader.errorString()));
} else { } else {
statusBar()->showMessage(tr("File loaded"), 2000); statusBar()->showMessage(tr("File loaded"), 2000);
} }
@ -124,8 +147,8 @@ void MainWindow::saveAs()
if (!file.open(QFile::WriteOnly | QFile::Text)) { if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, tr("QXmlStream Bookmarks"), QMessageBox::warning(this, tr("QXmlStream Bookmarks"),
tr("Cannot write file %1:\n%2.") tr("Cannot write file %1:\n%2.")
.arg(fileName) .arg(QDir::toNativeSeparators(fileName),
.arg(file.errorString())); file.errorString()));
return; return;
} }
@ -144,41 +167,23 @@ void MainWindow::about()
} }
//! [3] //! [3]
//! [4]
void MainWindow::createActions()
{
openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
saveAsAct = new QAction(tr("&Save As..."), this);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs()));
exitAct = new QAction(tr("E&xit"), this);
exitAct->setShortcuts(QKeySequence::Quit);
connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
aboutAct = new QAction(tr("&About"), this);
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
aboutQtAct = new QAction(tr("About &Qt"), this);
connect(aboutQtAct, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
}
//! [4]
//! [5] //! [5]
void MainWindow::createMenus() void MainWindow::createMenus()
{ {
fileMenu = menuBar()->addMenu(tr("&File")); QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(openAct); QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open);
fileMenu->addAction(saveAsAct); openAct->setShortcuts(QKeySequence::Open);
fileMenu->addAction(exitAct);
QAction *saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &MainWindow::saveAs);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &QWidget::close);
exitAct->setShortcuts(QKeySequence::Quit);
menuBar()->addSeparator(); menuBar()->addSeparator();
helpMenu = menuBar()->addMenu(tr("&Help")); QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
helpMenu->addAction(aboutAct); helpMenu->addAction(tr("&About"), this, &MainWindow::about);
helpMenu->addAction(aboutQtAct); helpMenu->addAction(tr("About &Qt"), qApp, &QCoreApplication::quit);
} }
//! [5] //! [5]

View File

@ -69,20 +69,13 @@ public slots:
void open(); void open();
void saveAs(); void saveAs();
void about(); void about();
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD)
void onCustomContextMenuRequested(const QPoint &pos);
#endif
private: private:
void createActions();
void createMenus(); void createMenus();
QTreeWidget *treeWidget; QTreeWidget *treeWidget;
QMenu *fileMenu;
QMenu *helpMenu;
QAction *openAct;
QAction *saveAsAct;
QAction *exitAct;
QAction *aboutAct;
QAction *aboutQtAct;
}; };
//! [0] //! [0]

View File

@ -72,10 +72,12 @@ bool XbelReader::read(QIODevice *device)
xml.setDevice(device); xml.setDevice(device);
if (xml.readNextStartElement()) { if (xml.readNextStartElement()) {
if (xml.name() == "xbel" && xml.attributes().value("version") == "1.0") if (xml.name() == QLatin1String("xbel")
&& xml.attributes().value(versionAttribute()) == QLatin1String("1.0")) {
readXBEL(); readXBEL();
else } else {
xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file.")); xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file."));
}
} }
return !xml.error(); return !xml.error();
@ -95,14 +97,14 @@ QString XbelReader::errorString() const
//! [3] //! [3]
void XbelReader::readXBEL() void XbelReader::readXBEL()
{ {
Q_ASSERT(xml.isStartElement() && xml.name() == "xbel"); Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("xbel"));
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
if (xml.name() == "folder") if (xml.name() == QLatin1String("folder"))
readFolder(0); readFolder(0);
else if (xml.name() == "bookmark") else if (xml.name() == QLatin1String("bookmark"))
readBookmark(0); readBookmark(0);
else if (xml.name() == "separator") else if (xml.name() == QLatin1String("separator"))
readSeparator(0); readSeparator(0);
else else
xml.skipCurrentElement(); xml.skipCurrentElement();
@ -113,7 +115,7 @@ void XbelReader::readXBEL()
//! [4] //! [4]
void XbelReader::readTitle(QTreeWidgetItem *item) void XbelReader::readTitle(QTreeWidgetItem *item)
{ {
Q_ASSERT(xml.isStartElement() && xml.name() == "title"); Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("title"));
QString title = xml.readElementText(); QString title = xml.readElementText();
item->setText(0, title); item->setText(0, title);
@ -123,7 +125,7 @@ void XbelReader::readTitle(QTreeWidgetItem *item)
//! [5] //! [5]
void XbelReader::readSeparator(QTreeWidgetItem *item) void XbelReader::readSeparator(QTreeWidgetItem *item)
{ {
Q_ASSERT(xml.isStartElement() && xml.name() == "separator"); Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("separator"));
QTreeWidgetItem *separator = createChildItem(item); QTreeWidgetItem *separator = createChildItem(item);
separator->setFlags(item->flags() & ~Qt::ItemIsSelectable); separator->setFlags(item->flags() & ~Qt::ItemIsSelectable);
@ -134,20 +136,20 @@ void XbelReader::readSeparator(QTreeWidgetItem *item)
void XbelReader::readFolder(QTreeWidgetItem *item) void XbelReader::readFolder(QTreeWidgetItem *item)
{ {
Q_ASSERT(xml.isStartElement() && xml.name() == "folder"); Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("folder"));
QTreeWidgetItem *folder = createChildItem(item); QTreeWidgetItem *folder = createChildItem(item);
bool folded = (xml.attributes().value("folded") != "no"); bool folded = (xml.attributes().value(foldedAttribute()) != QLatin1String("no"));
treeWidget->setItemExpanded(folder, !folded); treeWidget->setItemExpanded(folder, !folded);
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
if (xml.name() == "title") if (xml.name() == QLatin1String("title"))
readTitle(folder); readTitle(folder);
else if (xml.name() == "folder") else if (xml.name() == QLatin1String("folder"))
readFolder(folder); readFolder(folder);
else if (xml.name() == "bookmark") else if (xml.name() == QLatin1String("bookmark"))
readBookmark(folder); readBookmark(folder);
else if (xml.name() == "separator") else if (xml.name() == QLatin1String("separator"))
readSeparator(folder); readSeparator(folder);
else else
xml.skipCurrentElement(); xml.skipCurrentElement();
@ -156,16 +158,16 @@ void XbelReader::readFolder(QTreeWidgetItem *item)
void XbelReader::readBookmark(QTreeWidgetItem *item) void XbelReader::readBookmark(QTreeWidgetItem *item)
{ {
Q_ASSERT(xml.isStartElement() && xml.name() == "bookmark"); Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("bookmark"));
QTreeWidgetItem *bookmark = createChildItem(item); QTreeWidgetItem *bookmark = createChildItem(item);
bookmark->setFlags(bookmark->flags() | Qt::ItemIsEditable); bookmark->setFlags(bookmark->flags() | Qt::ItemIsEditable);
bookmark->setIcon(0, bookmarkIcon); bookmark->setIcon(0, bookmarkIcon);
bookmark->setText(0, QObject::tr("Unknown title")); bookmark->setText(0, QObject::tr("Unknown title"));
bookmark->setText(1, xml.attributes().value("href").toString()); bookmark->setText(1, xml.attributes().value(hrefAttribute()).toString());
while (xml.readNextStartElement()) { while (xml.readNextStartElement()) {
if (xml.name() == "title") if (xml.name() == QLatin1String("title"))
readTitle(bookmark); readTitle(bookmark);
else else
xml.skipCurrentElement(); xml.skipCurrentElement();

View File

@ -71,6 +71,10 @@ public:
QString errorString() const; QString errorString() const;
static inline QString versionAttribute() { return QStringLiteral("version"); }
static inline QString hrefAttribute() { return QStringLiteral("href"); }
static inline QString foldedAttribute() { return QStringLiteral("folded"); }
private: private:
//! [2] //! [2]
void readXBEL(); void readXBEL();

View File

@ -51,9 +51,14 @@
#include <QtWidgets> #include <QtWidgets>
#include "xbelwriter.h" #include "xbelwriter.h"
#include "xbelreader.h"
static inline QString yesValue() { return QStringLiteral("yes"); }
static inline QString noValue() { return QStringLiteral("no"); }
static inline QString titleElement() { return QStringLiteral("title"); }
//! [0] //! [0]
XbelWriter::XbelWriter(QTreeWidget *treeWidget) XbelWriter::XbelWriter(const QTreeWidget *treeWidget)
: treeWidget(treeWidget) : treeWidget(treeWidget)
{ {
xml.setAutoFormatting(true); xml.setAutoFormatting(true);
@ -66,9 +71,9 @@ bool XbelWriter::writeFile(QIODevice *device)
xml.setDevice(device); xml.setDevice(device);
xml.writeStartDocument(); xml.writeStartDocument();
xml.writeDTD("<!DOCTYPE xbel>"); xml.writeDTD(QStringLiteral("<!DOCTYPE xbel>"));
xml.writeStartElement("xbel"); xml.writeStartElement(QStringLiteral("xbel"));
xml.writeAttribute("version", "1.0"); xml.writeAttribute(XbelReader::versionAttribute(), QStringLiteral("1.0"));
for (int i = 0; i < treeWidget->topLevelItemCount(); ++i) for (int i = 0; i < treeWidget->topLevelItemCount(); ++i)
writeItem(treeWidget->topLevelItem(i)); writeItem(treeWidget->topLevelItem(i));
@ -78,24 +83,24 @@ bool XbelWriter::writeFile(QIODevice *device)
//! [1] //! [1]
//! [2] //! [2]
void XbelWriter::writeItem(QTreeWidgetItem *item) void XbelWriter::writeItem(const QTreeWidgetItem *item)
{ {
QString tagName = item->data(0, Qt::UserRole).toString(); QString tagName = item->data(0, Qt::UserRole).toString();
if (tagName == "folder") { if (tagName == QLatin1String("folder")) {
bool folded = !treeWidget->isItemExpanded(item); bool folded = !treeWidget->isItemExpanded(item);
xml.writeStartElement(tagName); xml.writeStartElement(tagName);
xml.writeAttribute("folded", folded ? "yes" : "no"); xml.writeAttribute(XbelReader::foldedAttribute(), folded ? yesValue() : noValue());
xml.writeTextElement("title", item->text(0)); xml.writeTextElement(titleElement(), item->text(0));
for (int i = 0; i < item->childCount(); ++i) for (int i = 0; i < item->childCount(); ++i)
writeItem(item->child(i)); writeItem(item->child(i));
xml.writeEndElement(); xml.writeEndElement();
} else if (tagName == "bookmark") { } else if (tagName == QLatin1String("bookmark")) {
xml.writeStartElement(tagName); xml.writeStartElement(tagName);
if (!item->text(1).isEmpty()) if (!item->text(1).isEmpty())
xml.writeAttribute("href", item->text(1)); xml.writeAttribute(XbelReader::hrefAttribute(), item->text(1));
xml.writeTextElement("title", item->text(0)); xml.writeTextElement(titleElement(), item->text(0));
xml.writeEndElement(); xml.writeEndElement();
} else if (tagName == "separator") { } else if (tagName == QLatin1String("separator")) {
xml.writeEmptyElement(tagName); xml.writeEmptyElement(tagName);
} }
} }

View File

@ -62,13 +62,13 @@ QT_END_NAMESPACE
class XbelWriter class XbelWriter
{ {
public: public:
XbelWriter(QTreeWidget *treeWidget); explicit XbelWriter(const QTreeWidget *treeWidget);
bool writeFile(QIODevice *device); bool writeFile(QIODevice *device);
private: private:
void writeItem(QTreeWidgetItem *item); void writeItem(const QTreeWidgetItem *item);
QXmlStreamWriter xml; QXmlStreamWriter xml;
QTreeWidget *treeWidget; const QTreeWidget *treeWidget;
}; };
//! [0] //! [0]