// 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
    \ingroup corelib_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.
*/
