Brush up the DOM bookmarks example

- Use modern string literals (use QStringLiteral instead of
  QLatin1StringView for strings that go into the DOM API).
- Use mime types in the file dialog handling
- Streamline code
- Remove mentions of SAX
- Use per class includes
- Do not use QObject::tr()
- Use the configure system instead of QT_NO... defines
- Fix some doc text typos

Complements 3dd3268ded4dd74c64d7ec726fd534375ab9f018.

Task-number: QTBUG-111974
Change-Id: If0dc7b61e729d0a71f37743efc9b82e285d3f451
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
(cherry picked from commit 3a553507a134bee1562d34ebbf786a053d36fc05)
This commit is contained in:
Friedemann Kleint 2023-06-12 11:48:21 +02:00
parent 919d479142
commit 9e85c70b37
4 changed files with 72 additions and 54 deletions

View File

@ -20,7 +20,7 @@
The XbelTree class has functions for reading and writing to the filesystem. The XbelTree class has functions for reading and writing to the filesystem.
It inherits from the QTreeWidget class, contains the model for the It inherits from the QTreeWidget class, contains the model for the
dispalying of the bookmarks, and allows it to be edited. displaying of the bookmarks, and allows it to be edited.
\snippet dombookmarks/xbeltree.h 0 \snippet dombookmarks/xbeltree.h 0
@ -68,23 +68,22 @@
\snippet dombookmarks/mainwindow.cpp 0 \snippet dombookmarks/mainwindow.cpp 0
The \c createMenus() function poulates the menus and sets keyboard The \c createMenus() function populates the menus and sets keyboard
shortcuts. shortcuts.
\snippet dombookmarks/mainwindow.cpp 4 \snippet dombookmarks/mainwindow.cpp 4
The \c open() function enables the user to open an XBEL file using The \c open() function enables the user to open an XBEL file using
QFileDialog::getOpenFileName(). A warning message is displayed along QFileDialog. A warning message is displayed along
with the \c fileName and \c errorString if the file cannot be read or with the \c fileName and \c errorString if the file cannot be read or
if there is a parse error. If it succeeds it calls \c XbelTree::read(). if there is a parse error. If it succeeds it calls \c XbelTree::read().
\snippet dombookmarks/mainwindow.cpp 1 \snippet dombookmarks/mainwindow.cpp 1
The \c saveAs() function displays a QFileDialog, prompting the user for The \c saveAs() function displays a QFileDialog, prompting the user for
a \c fileName using QFileDialog::getSaveFileName(). Similar to the a \c fileName. Similar to the \c open() function, this function also
\c open() function, this function also displays a warning message if displays a warning message if the file cannot be written to. If this
the file cannot be written to. IF this succeeds it calls \c succeeds it calls \c XbelTree::write().
XbelTree::write().
\snippet dombookmarks/mainwindow.cpp 2 \snippet dombookmarks/mainwindow.cpp 2

View File

@ -1,11 +1,20 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "mainwindow.h" #include "mainwindow.h"
#include "xbeltree.h" #include "xbeltree.h"
#include <QApplication>
#include <QFileDialog>
#include <QMenuBar>
#include <QMessageBox>
#include <QStatusBar>
#include <QAction>
#include <QScreen>
using namespace Qt::StringLiterals;
//! [0] //! [0]
MainWindow::MainWindow() MainWindow::MainWindow()
{ {
@ -25,16 +34,15 @@ MainWindow::MainWindow()
//! [1] //! [1]
void MainWindow::open() void MainWindow::open()
{ {
QString fileName = QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath());
QFileDialog::getOpenFileName(this, tr("Open Bookmark File"), fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
QDir::currentPath(), if (fileDialog.exec() != QDialog::Accepted)
tr("XBEL Files (*.xbel *.xml)"));
if (fileName.isEmpty())
return; return;
const QString fileName = fileDialog.selectedFiles().constFirst();
QFile file(fileName); QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
QMessageBox::warning(this, tr("SAX Bookmarks"), QMessageBox::warning(this, tr("DOM Bookmarks"),
tr("Cannot read file %1:\n%2.") tr("Cannot read file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), .arg(QDir::toNativeSeparators(fileName),
file.errorString())); file.errorString()));
@ -49,16 +57,17 @@ void MainWindow::open()
//! [2] //! [2]
void MainWindow::saveAs() void MainWindow::saveAs()
{ {
QString fileName = QFileDialog fileDialog(this, tr("Save Bookmark File"), QDir::currentPath());
QFileDialog::getSaveFileName(this, tr("Save Bookmark File"), fileDialog.setAcceptMode(QFileDialog::AcceptSave);
QDir::currentPath(), fileDialog.setDefaultSuffix("xbel"_L1);
tr("XBEL Files (*.xbel *.xml)")); fileDialog.setMimeTypeFilters({"application/x-xbel"_L1});
if (fileName.isEmpty()) if (fileDialog.exec() != QDialog::Accepted)
return; return;
const QString fileName = fileDialog.selectedFiles().constFirst();
QFile file(fileName); QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)) { if (!file.open(QFile::WriteOnly | QFile::Text)) {
QMessageBox::warning(this, tr("SAX Bookmarks"), QMessageBox::warning(this, tr("DOM Bookmarks"),
tr("Cannot write file %1:\n%2.") tr("Cannot write file %1:\n%2.")
.arg(QDir::toNativeSeparators(fileName), .arg(QDir::toNativeSeparators(fileName),
file.errorString())); file.errorString()));

View File

@ -1,31 +1,41 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QtWidgets>
#include "xbeltree.h" #include "xbeltree.h"
#include <QHeaderView>
#include <QMenu>
#include <QMessageBox>
#include <QDesktopServices>
#include <QGuiApplication>
#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
# include <QClipboard>
# include <QContextMenuEvent>
#endif
#include <QUrl>
using namespace Qt::StringLiterals;
enum { DomElementRole = Qt::UserRole + 1 }; enum { DomElementRole = Qt::UserRole + 1 };
Q_DECLARE_METATYPE(QDomElement) Q_DECLARE_METATYPE(QDomElement)
static inline QString titleElement() { return QStringLiteral("title"); } static const auto titleElement = u"title"_s;
static inline QString folderElement() { return QStringLiteral("folder"); } static const auto folderElement = u"folder"_s;
static inline QString bookmarkElement() { return QStringLiteral("bookmark"); } static const auto bookmarkElement = u"bookmark"_s;
static inline QString versionAttribute() { return QStringLiteral("version"); } static const auto versionAttribute = u"version"_s;
static inline QString hrefAttribute() { return QStringLiteral("href"); } static const auto hrefAttribute = u"href"_s;
static inline QString foldedAttribute() { return QStringLiteral("folded"); } static const auto foldedAttribute = u"folded"_s;
//! [0] //! [0]
XbelTree::XbelTree(QWidget *parent) XbelTree::XbelTree(QWidget *parent)
: QTreeWidget(parent) : QTreeWidget(parent)
{ {
QStringList labels;
labels << tr("Title") << tr("Location");
header()->setSectionResizeMode(QHeaderView::Stretch); header()->setSectionResizeMode(QHeaderView::Stretch);
setHeaderLabels(labels); setHeaderLabels({tr("Title"), tr("Location")});
folderIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon), folderIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon),
QIcon::Normal, QIcon::Off); QIcon::Normal, QIcon::Off);
@ -35,7 +45,7 @@ XbelTree::XbelTree(QWidget *parent)
} }
//! [0] //! [0]
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD) #if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
void XbelTree::contextMenuEvent(QContextMenuEvent *event) void XbelTree::contextMenuEvent(QContextMenuEvent *event)
{ {
const QTreeWidgetItem *item = itemAt(event->pos()); const QTreeWidgetItem *item = itemAt(event->pos());
@ -51,7 +61,7 @@ void XbelTree::contextMenuEvent(QContextMenuEvent *event)
else if (action == openAction) else if (action == openAction)
QDesktopServices::openUrl(QUrl(url)); QDesktopServices::openUrl(QUrl(url));
} }
#endif // !QT_NO_CONTEXTMENU && !QT_NO_CLIPBOARD #endif // QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
//! [1] //! [1]
bool XbelTree::read(QIODevice *device) bool XbelTree::read(QIODevice *device)
@ -72,8 +82,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(versionAttribute()) } else if (root.hasAttribute(versionAttribute)
&& root.attribute(versionAttribute()) != QLatin1String("1.0")) { && root.attribute(versionAttribute) != "1.0"_L1) {
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."));
@ -84,10 +94,10 @@ bool XbelTree::read(QIODevice *device)
disconnect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement); disconnect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement);
QDomElement child = root.firstChildElement(folderElement()); QDomElement child = root.firstChildElement(folderElement);
while (!child.isNull()) { while (!child.isNull()) {
parseFolderElement(child); parseFolderElement(child);
child = child.nextSiblingElement(folderElement()); child = child.nextSiblingElement(folderElement);
} }
connect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement); connect(this, &QTreeWidget::itemChanged, this, &XbelTree::updateDomElement);
@ -112,16 +122,16 @@ void XbelTree::updateDomElement(const QTreeWidgetItem *item, int column)
QDomElement element = qvariant_cast<QDomElement>(item->data(0, DomElementRole)); QDomElement element = qvariant_cast<QDomElement>(item->data(0, DomElementRole));
if (!element.isNull()) { if (!element.isNull()) {
if (column == 0) { if (column == 0) {
QDomElement oldTitleElement = element.firstChildElement(titleElement()); QDomElement oldTitleElement = element.firstChildElement(titleElement);
QDomElement newTitleElement = domDocument.createElement(titleElement()); 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() == bookmarkElement()) if (element.tagName() == bookmarkElement)
element.setAttribute(hrefAttribute(), item->text(1)); element.setAttribute(hrefAttribute, item->text(1));
} }
} }
} }
@ -132,35 +142,35 @@ void XbelTree::parseFolderElement(const QDomElement &element,
{ {
QTreeWidgetItem *item = createItem(element, parentItem); QTreeWidgetItem *item = createItem(element, parentItem);
QString title = element.firstChildElement(titleElement()).text(); QString title = element.firstChildElement(titleElement).text();
if (title.isEmpty()) if (title.isEmpty())
title = QObject::tr("Folder"); title = tr("Folder");
item->setFlags(item->flags() | Qt::ItemIsEditable); item->setFlags(item->flags() | Qt::ItemIsEditable);
item->setIcon(0, folderIcon); item->setIcon(0, folderIcon);
item->setText(0, title); item->setText(0, title);
bool folded = (element.attribute(foldedAttribute()) != QLatin1String("no")); bool folded = (element.attribute(foldedAttribute) != "no"_L1);
item->setExpanded(!folded); item->setExpanded(!folded);
constexpr char16_t midDot = u'\xB7'; constexpr char16_t midDot = u'\xB7';
static const QString dots = QString(30, midDot); static const QString dots = QString(30, midDot);
QDomElement child = element.firstChildElement(); QDomElement child = element.firstChildElement();
while (!child.isNull()) { while (!child.isNull()) {
if (child.tagName() == folderElement()) { if (child.tagName() == folderElement) {
parseFolderElement(child, item); parseFolderElement(child, item);
} else if (child.tagName() == bookmarkElement()) { } else if (child.tagName() == bookmarkElement) {
QTreeWidgetItem *childItem = createItem(child, item); QTreeWidgetItem *childItem = createItem(child, item);
QString title = child.firstChildElement(titleElement()).text(); QString title = child.firstChildElement(titleElement).text();
if (title.isEmpty()) if (title.isEmpty())
title = QObject::tr("Folder"); title = 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(hrefAttribute())); childItem->setText(1, child.attribute(hrefAttribute));
} else if (child.tagName() == QLatin1String("separator")) { } else if (child.tagName() == "separator"_L1) {
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, dots); childItem->setText(0, dots);

View File

@ -14,13 +14,13 @@ class XbelTree : public QTreeWidget
Q_OBJECT Q_OBJECT
public: public:
XbelTree(QWidget *parent = nullptr); explicit XbelTree(QWidget *parent = nullptr);
bool read(QIODevice *device); bool read(QIODevice *device);
bool write(QIODevice *device) const; bool write(QIODevice *device) const;
protected: protected:
#if !defined(QT_NO_CONTEXTMENU) && !defined(QT_NO_CLIPBOARD) #if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu)
void contextMenuEvent(QContextMenuEvent *event) override; void contextMenuEvent(QContextMenuEvent *event) override;
#endif #endif