diff options
Diffstat (limited to 'examples/corelib/serialization/streambookmarks')
14 files changed, 819 insertions, 0 deletions
diff --git a/examples/corelib/serialization/streambookmarks/CMakeLists.txt b/examples/corelib/serialization/streambookmarks/CMakeLists.txt new file mode 100644 index 0000000000..bad55fa661 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(streambookmarks LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(streambookmarks + main.cpp + mainwindow.cpp mainwindow.h + xbelreader.cpp xbelreader.h + xbelwriter.cpp xbelwriter.h +) + +set_target_properties(streambookmarks PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(streambookmarks PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS streambookmarks + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +qt_generate_deploy_app_script( + TARGET streambookmarks + OUTPUT_SCRIPT deploy_script + NO_UNSUPPORTED_PLATFORM_ERROR +) +install(SCRIPT ${deploy_script}) diff --git a/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png b/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png Binary files differnew file mode 100644 index 0000000000..1a92895ccf --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png diff --git a/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png b/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png Binary files differnew file mode 100644 index 0000000000..baa98bee96 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png diff --git a/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png b/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png Binary files differnew file mode 100644 index 0000000000..422873b6d0 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png diff --git a/examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc b/examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc new file mode 100644 index 0000000000..8e32dd8d0b --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc @@ -0,0 +1,223 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \example serialization/streambookmarks + \examplecategory {Data Processing & I/O} + \meta tag {network} + \title QXmlStream Bookmarks Example + \brief Demonstrates how to read and write XBEL files. + \ingroup xml-examples + + The QXmlStream Bookmarks example provides a viewer for XML Bookmark Exchange + Language (XBEL) files. It can read bookmarks using Qt's QXmlStreamReader and + write them back out again using QXmlStreamWriter. As this example aims to + show how to use these reader and writer types, it provides no means to open + a bookmark, add a new one, or merge two bookmark files, and only minimal + scope for editing bookmarks. None the less, it could surely be extended with + such features, if desired. + + \image screenshot.png + + \section1 XbelWriter Class Definition + + The \c XbelWriter class takes a \l{QTreeWidget}{tree widget} describing a + hierarchy of folders containing bookmarks. Its \c writeFile() provides the + means to write out this hierarchy, in XBEL format, to a given output device. + + Internally, it records the tree widget it was given and packages a private + instance of QXmlStreamWriter, which provides it with the means to stream + XML. It has an internal \c writeItem() to write each item in its tree. + + \snippet serialization/streambookmarks/xbelwriter.h 0 + + \section1 XbelWriter Class Implementation + + The \c XbelWriter constructor accepts the \a treeWidget it will describe. It + stores that and enables \l{QXmlStreamWriter}'s auto-formatting property. + This last splits the data into several lines, with indentation to indicate + the structure of the tree, which makes the XML output easier to read. + + \snippet serialization/streambookmarks/xbelwriter.cpp 0 + + The \c writeFile() function accepts a QIODevice object and directs its + QXmlStreamWriter member to write to this device, using \c setDevice(). This + function then writes the document type definition(DTD), the start element, + the version, and delegates writing of each of the \c{treeWidget}'s top-level + items to \c writeItem(). Finally, it closes the document and returns. + + \snippet serialization/streambookmarks/xbelwriter.cpp 1 + + The \c writeItem() function accepts a QTreeWidgetItem object and writes to + its XML stream a representation of the object, which depends on its \c + UserRole, which can be one of a \c{"folder"}, \c{"bookmark"}, + or \c{"separator"}. Within each folder, it calls itself recursively on each + child item, to recursively include a representation of each child within the + folder's XML element. + + \snippet serialization/streambookmarks/xbelwriter.cpp 2 + + \section1 XbelReader Class Definition + + The \c XbelReader takes a \l{QTreeWidget}{tree widget} to populate with + items describing a bookmark hierarchy. It supports reading XBEL data from a + QIODevice as a source of these items. If parsing of the XBEL data fails, it + can report what went wrong. + + Internally, it records the QTreeWidget that it will populate and packages an + instance of QXmlStreamReader, the companion class to QXmlStreamWriter, which + it will use to read XBEL data. + + \snippet serialization/streambookmarks/xbelreader.h 0 + + \section1 XbelReader Class Implementation + + Since the XBEL reader is only concerned with reading XML elements, it makes + extensive use of the \l{QXmlStreamReader::}{readNextStartElement()} + convenience function. + + The \c XbelReader constructor requires a QTreeWidget that it will populate. + It populates the tree widget's style with suitable icons: a folder icon that + changes form to indicate whether each folder as open or closed; and a + standard file icon for the individual bookmarks within those folders. + + \snippet serialization/streambookmarks/xbelreader.cpp 0 + + The \c read() function accepts a QIODevice. It directs its QXmlStreamReader + member to read content from that device. Note that the XML input must be + well-formed to be accepted by QXmlStreamReader. First it reads the outer + structure and verifies the content is an XBEL 1.0 file; if it is, \c read() + delegates the actual reading of content to the internal \c readXBEL(). + + Otherwise, the \l{QXmlStreamReader::}{raiseError()} function is used to + record an error message. The reader itself may also do the same if it + encounters errors in the input. When \c read() has finished, it returns + true if there were no errors. + + \snippet serialization/streambookmarks/xbelreader.cpp 1 + + If \c read() returns false, its caller can obtain a description of the + error, complete with line and column number within the stream, by calling + the \c errorString() function. + + \snippet serialization/streambookmarks/xbelreader.cpp 2 + + The \c readXBEL() function reads the name of a startElement and calls the + appropriate function to read it, depending on whether if its tag name + is \c{"folder"}, \c{"bookmark"} or \c{"separator"}. Any other elements + encountered are skipped. The function starts with a precondition, verifying + that the XML reader has just opened an \c{"xbel"} element. + + \snippet serialization/streambookmarks/xbelreader.cpp 3 + + The \c readBookmark() function creates a new editable item representing a + single bookmark. It records the XML \c{"href"} attribute of the current + element as second column text of the item and provisionally sets its first + column text to \c{"Unknown title"} before scanning the rest of the element + for a title element to over-ride that, skipping any unrecognized child + elements. + + \snippet serialization/streambookmarks/xbelreader.cpp 5 + + The \c readTitle() function reads a bookmark's title and records it as the + title (first column text) of the item for which it was called. + + \snippet serialization/streambookmarks/xbelreader.cpp 6 + + The \c readSeparator() function creates a separator and sets its flags. The + separator item's text is set to 30 centered dots. The rest of the element is + then skipped using \l{QXmlStreamReader::}{skipCurrentElement()}. + + \snippet serialization/streambookmarks/xbelreader.cpp 6 + + The \c readFolder() function creates an item and iterates the content of the + folder element, adding children to this item to represent the contents of + the folder element. The loop over folder content is similar in form to the + one in \c readXBEL(), save that it now accepts a title element to set the + title of the folder. + + \snippet serialization/streambookmarks/xbelreader.cpp 7 + + The \c createChildItem() helper function creates a new tree widget item + that's either a child of the given item or, if no parent item is given, a + direct child of the tree widget. It sets the new item's \c UserRole to the + tag name of the current XML element, matching how XbelWriter::writeFile() + uses that \c UserRole. + + \snippet serialization/streambookmarks/xbelreader.cpp 8 + + \section1 MainWindow Class Definition + + The \c MainWindow class is a subclass of QMainWindow, with a \c File menu + and a \c Help menu. + + \snippet serialization/streambookmarks/mainwindow.h 0 + + \section1 MainWindow Class Implementation + + The \c MainWindow constructor sets up its QTreeWidget object, \c treeWidget, + as its own central widget, with column headings for the title and location + of each book-mark. It configures a custom menu that enables the user to + perform actions on individual bookmarks within the tree widget. + + It invokes \c createMenus() to set up its own menus and their corresponding + actions. It sets its title, announces itself as ready and sets its size to a + reasonable proportion of the available screen space. + + \snippet serialization/streambookmarks/mainwindow.cpp 0 + + A custom menu, triggered when the user right-clicks on a bookmark, provides + for copying the bookmark as a link or directing a desktop browser to open + the URL it references. This menu is implemented (when relevant features are + enabled) by \c onCustomContextMenuRequested(). + + \snippet serialization/streambookmarks/mainwindow.cpp 1 + + The \c createMenus() function creates the \c fileMenu and \c helpMenu and + adds QAction objects to them, bound variously to the \c open(), \c saveAs() + and \c about() functions, along with QWidget::close() and + QApplication::aboutQt(). The connections are as shown below: + + \snippet serialization/streambookmarks/mainwindow.cpp 2 + + This creates the menu shown in the screenshots below: + + \table + \row + \li \inlineimage filemenu.png + \li \inlineimage helpmenu.png + \endtable + + The \c open() function, when triggered, offers the user a file dialog to use + to select a bookmarks file. If a file is selected, it is parsed using an \c + XBelReader to populate the \c treeWidget with bookmarks. If problems arise + with opening or parsing the file, a suitable warning message is displayed to + the user, including file name and error message. Otherwise, the bookmarks + read from the file are displayed and the window's status bar briefly reports + that the file has been loaded. + + \snippet serialization/streambookmarks/mainwindow.cpp 3 + + The \c saveAs() function displays a QFileDialog, prompting the user for a \c + fileName, to which to save a copy of the bookmarks data. Similar to the \c + open() function, this function also displays a warning message if the file + cannot be written to. + + \snippet serialization/streambookmarks/mainwindow.cpp 4 + + The \c about() function displays a QMessageBox with a brief description of + the example, or general information about Qt and the version of it in use. + + \snippet serialization/streambookmarks/mainwindow.cpp 5 + + \section1 \c{main()} Function + + The \c main() function instantiates \c MainWindow and invokes the \c show() + function to display it, then its \c open(), as this is most likely what the + user shall want to do first. + + \snippet serialization/streambookmarks/main.cpp 0 + + See the \l{https://pyxml.sourceforge.net/topics/xbel/} {XML Bookmark + Exchange Language Resource Page} for more information about XBEL files. +*/ diff --git a/examples/corelib/serialization/streambookmarks/jennifer.xbel b/examples/corelib/serialization/streambookmarks/jennifer.xbel new file mode 100644 index 0000000000..d504236830 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/jennifer.xbel @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE xbel> +<xbel version="1.0"> + <folder folded="no"> + <title>Qt Resources</title> + <bookmark href="https://www.qt.io/"> + <title>Qt home page</title> + </bookmark> + <bookmark href="https://www.qt.io/contact-us/partners"> + <title>Qt Partners</title> + </bookmark> + <bookmark href="https://www.qt.io/qt-professional-services"> + <title>Professional Services</title> + </bookmark> + <bookmark href="https://doc.qt.io/"> + <title>Qt Documentation</title> + </bookmark> + <folder folded="yes"> + <title>Community Resources</title> + <bookmark href="https://contribute.qt-project.org"> + <title>The Qt Project</title> + </bookmark> + <bookmark href="https://www.qtcentre.org/content/"> + <title>Qt Centre</title> + </bookmark> + <bookmark href="https://forum.qt.io/"> + <title>Forum.Qt.org</title> + </bookmark> + <bookmark href="https://digitalfanatics.org/projects/qt_tutorial/"> + <title>The Independent Qt Tutorial</title> + </bookmark> + <bookmark href="https://www.qtforum.de/"> + <title>German Qt Forum</title> + </bookmark> + <bookmark href="https://www.qt-dev.com/"> + <title>Korean Qt Community Site</title> + </bookmark> + <bookmark href="http://www.prog.org.ru/"> + <title>Russian Qt Forum</title> + </bookmark> + </folder> + </folder> + <folder folded="no"> + <title>Online Dictionaries</title> + <bookmark href="https://www.dictionary.com/"> + <title>Dictionary.com</title> + </bookmark> + <bookmark href="https://www.merriam-webster.com/"> + <title>Merriam-Webster Online</title> + </bookmark> + <bookmark href="https://dictionary.cambridge.org/"> + <title>Cambridge Dictionaries Online</title> + </bookmark> + <bookmark href="https://www.onelook.com/"> + <title>OneLook Dictionary Search</title> + </bookmark> + <separator/> + <bookmark href="https://dict.tu-chemnitz.de/"> + <title>BEOLINGUS, a service of TU Chemnitz</title> + </bookmark> + <separator/> + <bookmark href="http://atilf.atilf.fr/tlf.htm"> + <title>Trésor de la Langue Française informatisé</title> + </bookmark> + <bookmark href="https://www.dictionnaire-academie.fr/"> + <title>Dictionnaire de l'Académie Française</title> + </bookmark> + </folder> +</xbel> diff --git a/examples/corelib/serialization/streambookmarks/main.cpp b/examples/corelib/serialization/streambookmarks/main.cpp new file mode 100644 index 0000000000..0fd317de43 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" + +#include <QApplication> + +//! [0] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + MainWindow mainWin; + mainWin.show(); + mainWin.open(); + return app.exec(); +} +//! [0] diff --git a/examples/corelib/serialization/streambookmarks/mainwindow.cpp b/examples/corelib/serialization/streambookmarks/mainwindow.cpp new file mode 100644 index 0000000000..a863f77ab7 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/mainwindow.cpp @@ -0,0 +1,151 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "mainwindow.h" +#include "xbelreader.h" +#include "xbelwriter.h" + +#include <QFileDialog> +#include <QHeaderView> +#include <QMenuBar> +#include <QMessageBox> +#include <QStatusBar> +#include <QTreeWidget> + +#include <QAction> +#if QT_CONFIG(clipboard) +# include <QClipboard> +#endif +#include <QDesktopServices> +#include <QApplication> +#include <QScreen> + +using namespace Qt::StringLiterals; + +//! [0] +MainWindow::MainWindow() : treeWidget(new QTreeWidget) +{ + treeWidget->header()->setSectionResizeMode(QHeaderView::Stretch); + treeWidget->setHeaderLabels(QStringList{tr("Title"), tr("Location")}); +#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) + treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(treeWidget, &QWidget::customContextMenuRequested, + this, &MainWindow::onCustomContextMenuRequested); +#endif + setCentralWidget(treeWidget); + + createMenus(); + + statusBar()->showMessage(tr("Ready")); + + setWindowTitle(tr("QXmlStream Bookmarks")); + const QSize availableSize = screen()->availableGeometry().size(); + resize(availableSize.width() / 2, availableSize.height() / 3); +} +//! [0] + +//! [1] +#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) +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_CONFIG(clipboard) && QT_CONFIG(contextmenu) +//! [1] + +//! [2] +void MainWindow::createMenus() +{ + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &MainWindow::open); + openAct->setShortcuts(QKeySequence::Open); + + 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(); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(tr("&About"), this, &MainWindow::about); + helpMenu->addAction(tr("About &Qt"), qApp, &QApplication::aboutQt); +} +//! [2] + +//! [3] +void MainWindow::open() +{ + QFileDialog fileDialog(this, tr("Open Bookmark File"), QDir::currentPath()); + fileDialog.setMimeTypeFilters({"application/x-xbel"_L1}); + if (fileDialog.exec() != QDialog::Accepted) + return; + + treeWidget->clear(); + + const QString fileName = fileDialog.selectedFiles().constFirst(); + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) { + QMessageBox::warning(this, tr("QXmlStream Bookmarks"), + tr("Cannot read file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString())); + return; + } + + XbelReader reader(treeWidget); + if (!reader.read(&file)) { + QMessageBox::warning( + this, tr("QXmlStream Bookmarks"), + tr("Parse error in file %1:\n\n%2") + .arg(QDir::toNativeSeparators(fileName), reader.errorString())); + } else { + statusBar()->showMessage(tr("File loaded"), 2000); + } +} +//! [3] + +//! [4] +void MainWindow::saveAs() +{ + QFileDialog fileDialog(this, tr("Save Bookmark File"), QDir::currentPath()); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + fileDialog.setDefaultSuffix("xbel"_L1); + fileDialog.setMimeTypeFilters({"application/x-xbel"_L1}); + if (fileDialog.exec() != QDialog::Accepted) + return; + + const QString fileName = fileDialog.selectedFiles().constFirst(); + QFile file(fileName); + if (!file.open(QFile::WriteOnly | QFile::Text)) { + QMessageBox::warning(this, tr("QXmlStream Bookmarks"), + tr("Cannot write file %1:\n%2.") + .arg(QDir::toNativeSeparators(fileName), file.errorString())); + return; + } + + XbelWriter writer(treeWidget); + if (writer.writeFile(&file)) + statusBar()->showMessage(tr("File saved"), 2000); +} +//! [4] + +//! [5] +void MainWindow::about() +{ + QMessageBox::about(this, tr("About QXmlStream Bookmarks"), + tr("The <b>QXmlStream Bookmarks</b> example demonstrates how to use Qt's " + "QXmlStream classes to read and write XML documents.")); +} +//! [5] diff --git a/examples/corelib/serialization/streambookmarks/mainwindow.h b/examples/corelib/serialization/streambookmarks/mainwindow.h new file mode 100644 index 0000000000..d9efe6b5a5 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/mainwindow.h @@ -0,0 +1,35 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include <QMainWindow> + +QT_BEGIN_NAMESPACE +class QTreeWidget; +QT_END_NAMESPACE + +//! [0] +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(); + +public slots: + void open(); + void saveAs(); + void about(); +#if QT_CONFIG(clipboard) && QT_CONFIG(contextmenu) + void onCustomContextMenuRequested(const QPoint &pos); +#endif +private: + void createMenus(); + + QTreeWidget *const treeWidget; +}; +//! [0] + +#endif diff --git a/examples/corelib/serialization/streambookmarks/streambookmarks.pro b/examples/corelib/serialization/streambookmarks/streambookmarks.pro new file mode 100644 index 0000000000..34d2caae82 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/streambookmarks.pro @@ -0,0 +1,15 @@ +HEADERS = mainwindow.h \ + xbelreader.h \ + xbelwriter.h +SOURCES = main.cpp \ + mainwindow.cpp \ + xbelreader.cpp \ + xbelwriter.cpp +QT += widgets +requires(qtConfig(filedialog)) + +EXAMPLE_FILES = jennifer.xbel + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/corelib/serialization/streambookmarks +INSTALLS += target diff --git a/examples/corelib/serialization/streambookmarks/xbelreader.cpp b/examples/corelib/serialization/streambookmarks/xbelreader.cpp new file mode 100644 index 0000000000..c622cf6642 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelreader.cpp @@ -0,0 +1,140 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "xbelreader.h" + +#include <QStyle> +#include <QTreeWidget> + +using namespace Qt::StringLiterals; + +//! [0] +XbelReader::XbelReader(QTreeWidget *treeWidget) : treeWidget(treeWidget) +{ + QStyle *style = treeWidget->style(); + + folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirClosedIcon), QIcon::Normal, + QIcon::Off); + folderIcon.addPixmap(style->standardPixmap(QStyle::SP_DirOpenIcon), QIcon::Normal, QIcon::On); + bookmarkIcon.addPixmap(style->standardPixmap(QStyle::SP_FileIcon)); +} +//! [0] + +//! [1] +bool XbelReader::read(QIODevice *device) +{ + xml.setDevice(device); + + if (xml.readNextStartElement()) { + if (xml.name() == "xbel"_L1 && xml.attributes().value("version"_L1) == "1.0"_L1) + readXBEL(); + else + xml.raiseError(QObject::tr("The file is not an XBEL version 1.0 file.")); + } + + return !xml.error(); +} +//! [1] + +//! [2] +QString XbelReader::errorString() const +{ + return QObject::tr("%1\nLine %2, column %3") + .arg(xml.errorString()) + .arg(xml.lineNumber()) + .arg(xml.columnNumber()); +} +//! [2] + +//! [3] +void XbelReader::readXBEL() +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "xbel"_L1); + + while (xml.readNextStartElement()) { + if (xml.name() == "folder"_L1) + readFolder(nullptr); + else if (xml.name() == "bookmark"_L1) + readBookmark(nullptr); + else if (xml.name() == "separator"_L1) + readSeparator(nullptr); + else + xml.skipCurrentElement(); + } +} +//! [3] + +//! [4] +void XbelReader::readBookmark(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "bookmark"_L1); + + QTreeWidgetItem *bookmark = createChildItem(item); + bookmark->setFlags(bookmark->flags() | Qt::ItemIsEditable); + bookmark->setIcon(0, bookmarkIcon); + bookmark->setText(0, QObject::tr("Unknown title")); + bookmark->setText(1, xml.attributes().value("href"_L1).toString()); + + while (xml.readNextStartElement()) { + if (xml.name() == "title"_L1) + readTitle(bookmark); + else + xml.skipCurrentElement(); + } +} +//! [4] + +//! [5] +void XbelReader::readTitle(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "title"_L1); + item->setText(0, xml.readElementText()); +} +//! [5] + +//! [6] +void XbelReader::readSeparator(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "separator"_L1); + constexpr char16_t midDot = u'\xB7'; + static const QString dots(30, midDot); + + QTreeWidgetItem *separator = createChildItem(item); + separator->setFlags(item ? item->flags() & ~Qt::ItemIsSelectable : Qt::ItemFlags{}); + separator->setText(0, dots); + xml.skipCurrentElement(); +} +//! [6] + +//! [7] +void XbelReader::readFolder(QTreeWidgetItem *item) +{ + Q_ASSERT(xml.isStartElement() && xml.name() == "folder"_L1); + + QTreeWidgetItem *folder = createChildItem(item); + bool folded = xml.attributes().value("folded"_L1) != "no"_L1; + folder->setExpanded(!folded); + + while (xml.readNextStartElement()) { + if (xml.name() == "title"_L1) + readTitle(folder); + else if (xml.name() == "folder"_L1) + readFolder(folder); + else if (xml.name() == "bookmark"_L1) + readBookmark(folder); + else if (xml.name() == "separator"_L1) + readSeparator(folder); + else + xml.skipCurrentElement(); + } +} +//! [7] + +//! [8] +QTreeWidgetItem *XbelReader::createChildItem(QTreeWidgetItem *item) +{ + QTreeWidgetItem *childItem = item ? new QTreeWidgetItem(item) : new QTreeWidgetItem(treeWidget); + childItem->setData(0, Qt::UserRole, xml.name().toString()); + return childItem; +} +//! [8] diff --git a/examples/corelib/serialization/streambookmarks/xbelreader.h b/examples/corelib/serialization/streambookmarks/xbelreader.h new file mode 100644 index 0000000000..a3fa59d813 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelreader.h @@ -0,0 +1,45 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef XBELREADER_H +#define XBELREADER_H + +#include <QIcon> +#include <QXmlStreamReader> + +QT_BEGIN_NAMESPACE +class QTreeWidget; +class QTreeWidgetItem; +QT_END_NAMESPACE + +//! [0] +class XbelReader +{ +public: +//! [1] + XbelReader(QTreeWidget *treeWidget); +//! [1] + + bool read(QIODevice *device); + QString errorString() const; + +private: +//! [2] + void readXBEL(); + void readTitle(QTreeWidgetItem *item); + void readSeparator(QTreeWidgetItem *item); + void readFolder(QTreeWidgetItem *item); + void readBookmark(QTreeWidgetItem *item); + + QTreeWidgetItem *createChildItem(QTreeWidgetItem *item); + + QXmlStreamReader xml; + QTreeWidget *treeWidget; +//! [2] + + QIcon folderIcon; + QIcon bookmarkIcon; +}; +//! [0] + +#endif diff --git a/examples/corelib/serialization/streambookmarks/xbelwriter.cpp b/examples/corelib/serialization/streambookmarks/xbelwriter.cpp new file mode 100644 index 0000000000..e50f47a5a5 --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelwriter.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "xbelwriter.h" + +#include <QTreeWidget> + +using namespace Qt::StringLiterals; + +//! [0] +XbelWriter::XbelWriter(const QTreeWidget *treeWidget) : treeWidget(treeWidget) +{ + xml.setAutoFormatting(true); +} +//! [0] + +//! [1] +bool XbelWriter::writeFile(QIODevice *device) +{ + xml.setDevice(device); + + xml.writeStartDocument(); + xml.writeDTD("<!DOCTYPE xbel>"_L1); + xml.writeStartElement("xbel"_L1); + xml.writeAttribute("version"_L1, "1.0"_L1); + for (int i = 0; i < treeWidget->topLevelItemCount(); ++i) + writeItem(treeWidget->topLevelItem(i)); + + xml.writeEndDocument(); + return true; +} +//! [1] + +//! [2] +void XbelWriter::writeItem(const QTreeWidgetItem *item) +{ + QString tagName = item->data(0, Qt::UserRole).toString(); + if (tagName == "folder"_L1) { + bool folded = !item->isExpanded(); + xml.writeStartElement(tagName); + xml.writeAttribute("folded"_L1, folded ? "yes"_L1 : "no"_L1); + xml.writeTextElement("title"_L1, item->text(0)); + for (int i = 0; i < item->childCount(); ++i) + writeItem(item->child(i)); + xml.writeEndElement(); + } else if (tagName == "bookmark"_L1) { + xml.writeStartElement(tagName); + if (!item->text(1).isEmpty()) + xml.writeAttribute("href"_L1, item->text(1)); + xml.writeTextElement("title"_L1, item->text(0)); + xml.writeEndElement(); + } else if (tagName == "separator"_L1) { + xml.writeEmptyElement(tagName); + } +} +//! [2] diff --git a/examples/corelib/serialization/streambookmarks/xbelwriter.h b/examples/corelib/serialization/streambookmarks/xbelwriter.h new file mode 100644 index 0000000000..ec95315c4b --- /dev/null +++ b/examples/corelib/serialization/streambookmarks/xbelwriter.h @@ -0,0 +1,28 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef XBELWRITER_H +#define XBELWRITER_H + +#include <QXmlStreamWriter> + +QT_BEGIN_NAMESPACE +class QTreeWidget; +class QTreeWidgetItem; +QT_END_NAMESPACE + +//! [0] +class XbelWriter +{ +public: + explicit XbelWriter(const QTreeWidget *treeWidget); + bool writeFile(QIODevice *device); + +private: + void writeItem(const QTreeWidgetItem *item); + QXmlStreamWriter xml; + const QTreeWidget *treeWidget; +}; +//! [0] + +#endif |