summaryrefslogtreecommitdiffstats
path: root/examples/corelib/serialization/streambookmarks
diff options
context:
space:
mode:
Diffstat (limited to 'examples/corelib/serialization/streambookmarks')
-rw-r--r--examples/corelib/serialization/streambookmarks/CMakeLists.txt40
-rw-r--r--examples/corelib/serialization/streambookmarks/doc/images/filemenu.pngbin0 -> 3728 bytes
-rw-r--r--examples/corelib/serialization/streambookmarks/doc/images/helpmenu.pngbin0 -> 2099 bytes
-rw-r--r--examples/corelib/serialization/streambookmarks/doc/images/screenshot.pngbin0 -> 66567 bytes
-rw-r--r--examples/corelib/serialization/streambookmarks/doc/src/qxmlstreambookmarks.qdoc223
-rw-r--r--examples/corelib/serialization/streambookmarks/jennifer.xbel69
-rw-r--r--examples/corelib/serialization/streambookmarks/main.cpp17
-rw-r--r--examples/corelib/serialization/streambookmarks/mainwindow.cpp151
-rw-r--r--examples/corelib/serialization/streambookmarks/mainwindow.h35
-rw-r--r--examples/corelib/serialization/streambookmarks/streambookmarks.pro15
-rw-r--r--examples/corelib/serialization/streambookmarks/xbelreader.cpp140
-rw-r--r--examples/corelib/serialization/streambookmarks/xbelreader.h45
-rw-r--r--examples/corelib/serialization/streambookmarks/xbelwriter.cpp56
-rw-r--r--examples/corelib/serialization/streambookmarks/xbelwriter.h28
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
new file mode 100644
index 0000000000..1a92895ccf
--- /dev/null
+++ b/examples/corelib/serialization/streambookmarks/doc/images/filemenu.png
Binary files differ
diff --git a/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png b/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png
new file mode 100644
index 0000000000..baa98bee96
--- /dev/null
+++ b/examples/corelib/serialization/streambookmarks/doc/images/helpmenu.png
Binary files differ
diff --git a/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png b/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png
new file mode 100644
index 0000000000..422873b6d0
--- /dev/null
+++ b/examples/corelib/serialization/streambookmarks/doc/images/screenshot.png
Binary files differ
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