Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
++
++ /*!
++ \example itemviews/addressbook
++ \title Address Book
++ \examplecategory {User Interface Components}
++ \ingroup examples-itemviews
++ \brief The address book example shows how to use proxy models to display
++ different views onto data from a single model.
++
++ \image addressbook-example.png {Address book showing contacts and addresses}
++
++ This example provides an address book that allows contacts to be
++ grouped alphabetically into 9 groups: ABC, DEF, GHI, ... , VW,
++ ..., XYZ. This is achieved by using multiple views on the same
++ model, each of which is filtered using an instance of the
++ QSortFilterProxyModel class.
++
++
++ \section1 Overview
++
++ The address book contains 4 classes: \c MainWindow,
++ \c AddressWidget, \c NewAddressTab and
++ \c AddDialog. The \c MainWindow class uses \c AddressWidget as
++ its central widget and provides \uicontrol File and \uicontrol Tools menus.
++
++ \image addressbook-classes.png {Address book main classes and elements}
++
++ The \c AddressWidget class is a QTabWidget subclass that is used
++ to manipulate the 10 tabs displayed in the example: the 9
++ alphabet group tabs and an instance of \c NewAddressTab.
++ The \c NewAddressTab class is a subclass of QWidget that
++ is only used whenever the address book is empty, prompting the
++ user to add some contacts. \c AddressWidget also interacts with
++ an instance of \c TableModel to add, edit and remove entries to
++ the address book.
++
++ \c{struct Contact} stores a contact and provides
++ comparison and serialization via QDataStream:
++
++ \snippet itemviews/addressbook/contact.h 0
++
++ \c TableModel is a subclass of QAbstractTableModel that provides
++ the standard model/view API to access data. It holds a list of
++ added contacts.
++ However, this data is not all visible in a single tab. Instead,
++ QTableView is used to provide 9 different views of the same
++ data, according to the alphabet groups.
++
++ QSortFilterProxyModel is the class responsible for filtering
++ the contacts for each group of contacts. Each proxy model uses
++ a QRegularExpression to filter out contacts that do not belong in the
++ corresponding alphabetical group. The \c AddDialog class is
++ used to obtain information from the user for the address book.
++ This QDialog subclass is instantiated by \c NewAddressTab to
++ add contacts, and by \c AddressWidget to add and edit contacts.
++
++ \section1 AddressWidget Class Definition
++
++ The \c AddressWidget class is technically the main class
++ involved in this example as it provides functions to add, edit
++ and remove contacts, to save the contacts to a file and to load
++ them from a file.
++
++ \snippet itemviews/addressbook/addresswidget.h 0
++
++ \c AddressWidget extends QTabWidget in order to hold 10 tabs
++ (\c NewAddressTab and the 9 alphabet group tabs) and also
++ manipulates \c table, the \c TableModel object, \c proxyModel,
++ the QSortFilterProxyModel object that we use to filter the
++ entries, and \c tableView, the QTableView object.
++
++ It stores the contacts in a QList and uses a QRangeModelAdapter
++ to operate on it.
++
++ We provide the row header values for \c{struct Contact} via
++ a specialization of QRangeModel::RowOptions:
++
++ \snippet itemviews/addressbook/addresswidget.h RowOptions declaration
++ \snippet itemviews/addressbook/addresswidget.h RowOptions headerData
++
++ \section1 AddressWidget Class Implementation
++
++ The \c AddressWidget constructor accepts a parent widget and
++ instantiates \c NewAddressTab, \c TableModel and
++ QSortFilterProxyModel. The \c NewAddressTab object, which is
++ used to indicate that the address book is empty, is added
++ and the rest of the 9 tabs are set up with \c setupTabs().
++
++ \snippet itemviews/addressbook/addresswidget.cpp 0
++
++ The \c setupTabs() function is used to set up the 9 alphabet
++ group tabs, table views and proxy models in
++ \c AddressWidget. Each proxy model in turn is set to filter
++ contact names according to the relevant alphabet group using a
++ case-insensitive QRegularExpression object. The
++ table views are also sorted in ascending order using the
++ corresponding proxy model's \l{QSortFilterProxyModel::}{sort()}
++ function.
++
++ Each table view's \l{QTableView::}{selectionMode} is set to
++ QAbstractItemView::SingleSelection and
++ \l{QTableView::}{selectionBehavior} is set to
++ QAbstractItemView::SelectRows, allowing the user to select
++ all the items in one row at the same time. Each QTableView object
++ is automatically given a QItemSelectionModel that keeps track
++ of the selected indexes.
++
++ \snippet itemviews/addressbook/addresswidget.cpp 1
++
++ The QItemSelectionModel class provides a
++ \l{QItemSelectionModel::selectionChanged()}{selectionChanged}
++ signal that is connected to \c{AddressWidget}'s
++ \c selectionChanged() signal. We also connect
++ QTabWidget::currentChanged() signal to the lambda expression which
++ emits \c{AddressWidget}'s \c selectionChanged() as well. These
++ connections are necessary to enable the \uicontrol{Edit Entry...} and
++ \uicontrol{Remove Entry} actions in \c MainWindow's Tools menu.
++ It is further explained in \c MainWindow's implementation.
++
++ Each table view in the address book is added as a tab to the
++ QTabWidget with the relevant label, obtained from the QStringList
++ of groups.
++
++ \image addressbook-signals.png {Address book signals and slots connections}
++
++ We provide two \c addEntry() functions: One which is intended to be
++ used to accept user input, and the other which performs the actual
++ task of adding new entries to the address book. We divide the
++ responsibility of adding entries into two parts to allow
++ \c newAddressTab to insert data without having to popup a dialog.
++
++ The first \c addEntry() function is a slot connected to the
++ \c MainWindow's \uicontrol{Add Entry...} action. This function creates an
++ \c AddDialog object and then calls the second \c addEntry()
++ function to actually add the contact to \c table.
++
++ \snippet itemviews/addressbook/addresswidget.cpp 2
++
++ Basic validation is done in the second \c addEntry() function to
++ prevent duplicate entries in the address book. As mentioned with
++ \c TableModel, this is part of the reason why we require the
++ getter method \c getContacts().
++
++ \snippet itemviews/addressbook/addresswidget.cpp 3
++
++ If the model does not already contain an entry with the same name,
++ we call \c setData() to insert the name and address into the
++ first and second columns. Otherwise, we display a QMessageBox
++ to inform the user.
++
++ \note The \c newAddressTab is removed once a contact is added
++ as the address book is no longer empty.
++
++ Editing an entry is a way to update the contact's address only,
++ as the example does not allow the user to change the name of an
++ existing contact.
++
++ Firstly, we obtain the active tab's QTableView object using
++ QTabWidget::currentWidget(). Then we extract the
++ \c selectionModel from the \c tableView to obtain the selected
++ indexes.
++
++ \snippet itemviews/addressbook/addresswidget.cpp 4a
++
++ Next we extract data from the row the user intends to
++ edit. This data is displayed in an instance of \c AddDialog
++ with a different window title. The \c table is only
++ updated if changes have been made to data in \c aDialog.
++
++ \snippet itemviews/addressbook/addresswidget.cpp 4b
++
++ \image addressbook-editdialog.png {Dialog for editing name and address}
++
++ Entries are removed using the \c removeEntry() function.
++ The selected row is removed by accessing it through the
++ QItemSelectionModel object, \c selectionModel. The
++ \c newAddressTab is re-added to the \c AddressWidget only if
++ the user removes all the contacts in the address book.
++
++ \snippet itemviews/addressbook/addresswidget.cpp 5
++
++ The \c writeToFile() function is used to save a file containing
++ all the contacts in the address book. The file is saved in a
++ custom \c{.dat} format. The contents of the list of contacts
++ are written to \c file using QDataStream. If the file cannot be
++ opened, a QMessageBox is displayed with the related error message.
++
++ \snippet itemviews/addressbook/addresswidget.cpp 6
++
++ The \c readFromFile() function loads a file containing all the
++ contacts in the address book, previously saved using
++ \c writeToFile(). QDataStream is used to read the contents of a
++ \c{.dat} file into a list of contacts and each of these is added
++ using \c addEntry().
++
++ \snippet itemviews/addressbook/addresswidget.cpp 7
++
++
++ \section1 NewAddressTab Class Definition
++
++ The \c NewAddressTab class provides an informative tab telling
++ the user that the address book is empty. It appears and
++ disappears according to the contents of the address book, as
++ mentioned in \c{AddressWidget}'s implementation.
++
++ \image addressbook-newaddresstab.png {Tab for adding new contacts}
++
++ The \c NewAddressTab class extends QWidget and contains a QLabel
++ and QPushButton.
++
++ \snippet itemviews/addressbook/newaddresstab.h 0
++
++
++ \section1 NewAddressTab Class Implementation
++
++ The constructor instantiates the \c addButton,
++ \c descriptionLabel and connects the \c{addButton}'s signal to
++ the \c{triggered()} signal.
++
++ \snippet itemviews/addressbook/newaddresstab.cpp 0
++
++ \image signals-n-slots-aw-nat.png {Function calls for adding a new contact}
++
++
++ \section1 AddDialog Class Definition
++
++ The \c AddDialog class extends QDialog and provides the user
++ with a QLineEdit and a QTextEdit to input data into the
++ address book.
++
++ \snippet itemviews/addressbook/adddialog.h 0
++
++ \image addressbook-adddialog.png {Dialog for adding a new name and address}
++
++
++ \section1 AddDialog Class Implementation
++
++ The \c AddDialog's constructor sets up the user interface,
++ creating the necessary widgets and placing them into layouts.
++
++ \snippet itemviews/addressbook/adddialog.cpp 0
++
++ To give the dialog the desired behavior, we connect the \uicontrol OK
++ and \uicontrol Cancel buttons to the dialog's \l{QDialog::}{accept()} and
++ \l{QDialog::}{reject()} slots. Since the dialog only acts as a
++ container for name and address information, we do not need to
++ implement any other functions for it.
++
++
++ \section1 MainWindow Class Definition
++
++ The \c MainWindow class extends QMainWindow and implements the
++ menus and actions necessary to manipulate the address book.
++
++ \table
++ \row \li \inlineimage addressbook-filemenu.png
++ {Open, Save, and Exit items in File menu}
++ \li \inlineimage addressbook-toolsmenu.png
++ {Add Entry, Edit Entry, and Remove Entry in Tools menu}
++ \endtable
++
++ \snippet itemviews/addressbook/mainwindow.h 0
++
++ The \c MainWindow class uses an \c AddressWidget as its central
++ widget and provides the File menu with \uicontrol Open, \uicontrol Close and
++ \uicontrol Exit actions, as well as the \uicontrol Tools menu with
++ \uicontrol{Add Entry...}, \uicontrol{Edit Entry...} and \uicontrol{Remove Entry}
++ actions.
++
++
++ \section1 MainWindow Class Implementation
++
++ The constructor for \c MainWindow instantiates AddressWidget,
++ sets it as its central widget and calls the \c createMenus()
++ function.
++
++ \snippet itemviews/addressbook/mainwindow.cpp 0
++
++ The \c createMenus() function sets up the \uicontrol File and
++ \uicontrol Tools menus, connecting the actions to their respective slots.
++ Both the \uicontrol{Edit Entry...} and \uicontrol{Remove Entry} actions are
++ disabled by default as such actions cannot be carried out on an empty
++ address book. They are only enabled when one or more contacts
++ are added.
++
++ \snippet itemviews/addressbook/mainwindow.cpp 1a
++ \dots
++ \codeline
++ \snippet itemviews/addressbook/mainwindow.cpp 1b
++
++ Apart from connecting all the actions' signals to their
++ respective slots, we also connect \c AddressWidget's
++ \c selectionChanged() signal to its \c updateActions() slot.
++
++ The \c openFile() function opens a custom \c{addressbook.dat} file that
++ contains address book contacts. This function is a slot connected to
++ \c openAct in the \uicontrol File menu.
++
++ \snippet itemviews/addressbook/mainwindow.cpp 2
++
++ The \c saveFile() function saves a custom \c{addressbook.dat} file that
++ will contain the address book contacts. This function is a slot connected
++ to \c saveAct in the \uicontrol File menu.
++
++ \snippet itemviews/addressbook/mainwindow.cpp 3
++
++ The \c updateActions() function enables and disables
++ \uicontrol{Edit Entry...} and \uicontrol{Remove Entry} depending on the contents of
++ the address book. If the address book is empty, these actions
++ are disabled; otherwise, they are enabled. This function is a slot
++ is connected to the \c AddressWidget's \c selectionChanged()
++ signal.
++
++ \snippet itemviews/addressbook/mainwindow.cpp 4
++
++
++ \section1 main() Function
++
++ The main function for the address book instantiates QApplication
++ and opens a \c MainWindow before running the event loop.
++
++ \snippet itemviews/addressbook/main.cpp 0
++ */