doc/src/tutorials/addressbook.qdoc
branchRCL_3
changeset 7 3f74d0d4af4c
equal deleted inserted replaced
6:dee5afe5301f 7:3f74d0d4af4c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the documentation of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /*!
       
    43     \page tutorials-addressbook.html
       
    44 
       
    45     \startpage {index.html}{Qt Reference Documentation}
       
    46     \contentspage Tutorials
       
    47     \nextpage {tutorials/addressbook/part1}{Chapter 1}
       
    48 
       
    49     \title Address Book Tutorial
       
    50     \brief An introduction to GUI programming, showing how to put together a
       
    51     simple yet fully-functioning application.
       
    52 
       
    53     This tutorial gives an introduction to GUI programming using the Qt
       
    54     cross-platform framework.
       
    55 
       
    56     \image addressbook-tutorial-screenshot.png
       
    57 
       
    58     \omit
       
    59     It doesn't cover everything; the emphasis is on teaching the programming
       
    60     philosophy of GUI programming, and Qt's features are introduced as needed.
       
    61     Some commonly used features are never used in this tutorial.
       
    62     \endomit
       
    63 
       
    64     In the process, we will learn about some basic technologies provided by Qt,
       
    65     such as
       
    66 
       
    67     \list
       
    68     \o Widgets and layout managers
       
    69     \o Container classes
       
    70     \o Signals and slots
       
    71     \o Input and output devices
       
    72     \endlist
       
    73 
       
    74     If you are completely new to Qt, please read \l{How to Learn Qt} if you
       
    75     have not already done so.
       
    76 
       
    77     The tutorial's source code is located in Qt's \c examples/tutorials/addressbook
       
    78     directory.
       
    79 
       
    80     Tutorial chapters:
       
    81 
       
    82     \list 1
       
    83     \o \l{tutorials/addressbook/part1}{Designing the User Interface}
       
    84     \o \l{tutorials/addressbook/part2}{Adding Addresses}
       
    85     \o \l{tutorials/addressbook/part3}{Navigating between Entries}
       
    86     \o \l{tutorials/addressbook/part4}{Editing and Removing Addresses}
       
    87     \o \l{tutorials/addressbook/part5}{Adding a Find Function}
       
    88     \o \l{tutorials/addressbook/part6}{Loading and Saving}
       
    89     \o \l{tutorials/addressbook/part7}{Additional Features}
       
    90     \endlist
       
    91 
       
    92     Although this little application does not look much like a fully-fledged
       
    93     modern GUI application, it uses many of the basic techniques that are used
       
    94     in more complex applications. After you have worked through it, we
       
    95     recommend checking out the \l{mainwindows/application}{Application}
       
    96     example, which presents a small GUI application, with menus, toolbars, a
       
    97     status bar, and so on.
       
    98 */
       
    99 
       
   100 /*!
       
   101     \page tutorials-addressbook-part1.html
       
   102     \contentspage {Address Book Tutorial}{Contents}
       
   103     \nextpage {tutorials/addressbook/part2}{Chapter 2}
       
   104     \example tutorials/addressbook/part1
       
   105     \title Address Book 1 - Designing the User Interface
       
   106 
       
   107     The first part of this tutorial covers the design of the basic graphical
       
   108     user interface (GUI) we use for the Address Book application.
       
   109 
       
   110     The first step to creating a GUI program is to design the user interface.
       
   111     In this chapter, our goal is to set up the labels and input fields needed
       
   112     to implement a basic address book application. The figure below is a
       
   113     screenshot of our expected output.
       
   114 
       
   115     \image addressbook-tutorial-part1-screenshot.png
       
   116 
       
   117     We require two QLabel objects, \c nameLabel and \c addressLabel, as well
       
   118     as two input fields, a QLineEdit object, \c nameLine, and a QTextEdit
       
   119     object, \c addressText, to enable the user to enter a contact's name and
       
   120     address. The widgets used and their positions are shown in the figure
       
   121     below.
       
   122 
       
   123     \image addressbook-tutorial-part1-labeled-screenshot.png
       
   124 
       
   125     There are three files used to implement this address book:
       
   126 
       
   127     \list
       
   128         \o \c{addressbook.h} - the definition file for the \c AddressBook
       
   129             class,
       
   130         \o \c{addressbook.cpp} - the implementation file for the
       
   131             \c AddressBook class, and
       
   132         \o \c{main.cpp} - the file containing a \c main() function, with
       
   133             an instance of \c AddressBook.
       
   134     \endlist
       
   135 
       
   136     \section1 Qt Programming - Subclassing
       
   137 
       
   138     When writing Qt programs, we usually subclass Qt objects to add
       
   139     functionality. This is one of the essential concepts behind creating
       
   140     custom widgets or collections of standard widgets. Subclassing to
       
   141     extend or change the behavior of a widget has the following advantages:
       
   142 
       
   143     \list
       
   144     \o We can write implementations of virtual or pure virtual functions to
       
   145     obtain exactly what we need, falling back on the base class's implementation
       
   146     when necessary.
       
   147     \o It allows us to encapsulate parts of the user interface within a class,
       
   148     so that the other parts of the application don't need to know about the
       
   149     individual widgets in the user interface.
       
   150     \o The subclass can be used to create multiple custom widgets in the same
       
   151     application or library, and the code for the subclass can be reused in other
       
   152     projects.
       
   153     \endlist
       
   154 
       
   155     Since Qt does not provide a specific address book widget, we subclass a
       
   156     standard Qt widget class and add features to it. The \c AddressBook class
       
   157     we create in this tutorial can be reused in situations where a basic address
       
   158     book widget is needed.
       
   159 
       
   160     \section1 Defining the AddressBook Class
       
   161 
       
   162     The \l{tutorials/addressbook/part1/addressbook.h}{\c addressbook.h} file is
       
   163     used to define the \c AddressBook class.
       
   164 
       
   165     We start by defining \c AddressBook as a QWidget subclass and declaring
       
   166     a constructor. We also use the Q_OBJECT macro to indicate that the class
       
   167     uses internationalization and Qt's signals and slots features, even
       
   168     if we do not use all of these features at this stage.
       
   169 
       
   170     \snippet tutorials/addressbook/part1/addressbook.h class definition
       
   171 
       
   172     The class holds declarations of \c nameLine and \c addressText, the
       
   173     private instances of QLineEdit and QTextEdit mentioned earlier.
       
   174     You will see, in the coming chapters, that data stored in \c nameLine and
       
   175     \c addressText is needed for many of the address book's functions.
       
   176 
       
   177     We do not need to include declarations of the QLabel objects we will use
       
   178     because we will not need to reference them once they have been created.
       
   179     The way Qt tracks the ownership of objects is explained in the next section.
       
   180 
       
   181     The Q_OBJECT macro itself implements some of the more advanced features of Qt.
       
   182     For now, it is useful to think of the Q_OBJECT macro as a shortcut which allows
       
   183     us to use the \l{QObject::}{tr()} and \l{QObject::}{connect()} functions.
       
   184 
       
   185     We have now completed the \c addressbook.h file and we move on to
       
   186     implement the corresponding \c addressbook.cpp file.
       
   187 
       
   188     \section1 Implementing the AddressBook Class
       
   189 
       
   190     The constructor of \c AddressBook accepts a QWidget parameter, \a parent.
       
   191     By convention, we pass this parameter to the base class's constructor.
       
   192     This concept of ownership, where a parent can have one or more children,
       
   193     is useful for grouping widgets in Qt. For example, if you delete a parent,
       
   194     all of its children will be deleted as well.
       
   195 
       
   196     \snippet tutorials/addressbook/part1/addressbook.cpp constructor and input fields
       
   197 
       
   198     Within this constructor, we declare and instantiate two local QLabel objects,
       
   199     \c nameLabel and \c addressLabel, as well as instantiate \c nameLine and
       
   200     \c addressText. The
       
   201     \l{QObject::tr()}{tr()} function returns a translated version of the
       
   202     string, if there is one available; otherwise, it returns the string itself.
       
   203     Think of this function as an \c{<insert translation here>} marker to mark
       
   204     QString objects for translation. You will notice, in the coming chapters as
       
   205     well as in the \l{Qt Examples}, that we include it whenever we use a
       
   206     translatable string.
       
   207 
       
   208     When programming with Qt, it is useful to know how layouts work.
       
   209     Qt provides three main layout classes: QHBoxLayout, QVBoxLayout
       
   210     and QGridLayout to handle the positioning of widgets.
       
   211 
       
   212     \image addressbook-tutorial-part1-labeled-layout.png
       
   213 
       
   214     We use a QGridLayout to position our labels and input fields in a
       
   215     structured manner. QGridLayout divides the available space into a grid and
       
   216     places widgets in the cells we specify with row and column numbers. The
       
   217     diagram above shows the layout cells and the position of our widgets, and
       
   218     we specify this arrangement using the following code:
       
   219 
       
   220     \snippet tutorials/addressbook/part1/addressbook.cpp layout
       
   221 
       
   222     Notice that \c addressLabel is positioned using Qt::AlignTop as an
       
   223     additional argument. This is to make sure it is not vertically centered in
       
   224     cell (1,0). For a basic overview on Qt Layouts, refer to the 
       
   225     \l{Layout Management} documentation.
       
   226 
       
   227     In order to install the layout object onto the widget, we have to invoke
       
   228     the widget's \l{QWidget::setLayout()}{setLayout()} function:
       
   229 
       
   230     \snippet tutorials/addressbook/part1/addressbook.cpp setting the layout
       
   231 
       
   232     Lastly, we set the widget's title to "Simple Address Book".
       
   233 
       
   234     \section1 Running the Application
       
   235 
       
   236     A separate file, \c main.cpp, is used for the \c main() function. Within
       
   237     this function, we instantiate a QApplication object, \c app. QApplication
       
   238     is responsible for various application-wide resources, such as the default
       
   239     font and cursor, and for running an event loop. Hence, there is always one
       
   240     QApplication object in every GUI application using Qt.
       
   241 
       
   242     \snippet tutorials/addressbook/part1/main.cpp main function
       
   243 
       
   244     We construct a new \c AddressBook widget on the stack and invoke
       
   245     its \l{QWidget::show()}{show()} function to display it.
       
   246     However, the widget will not be shown until the application's event loop
       
   247     is started. We start the event loop by calling the application's
       
   248     \l{QApplication::}{exec()} function; the result returned by this function
       
   249     is used as the return value from the \c main() function. At this point,
       
   250     it becomes apparent why we instanciated \c AddressBook on the stack: It
       
   251     will now go out of scope. Therefore, \c AddressBook and all its child widgets
       
   252     will be deleted, thus preventing memory leaks.
       
   253 */
       
   254 
       
   255 /*!
       
   256     \page tutorials-addressbook-part2.html
       
   257     \previouspage Address Book 1 - Designing the User Interface
       
   258     \contentspage {Address Book Tutorial}{Contents}
       
   259     \nextpage {tutorials/addressbook/part3}{Chapter 3}
       
   260     \example tutorials/addressbook/part2
       
   261     \title Address Book 2 - Adding Addresses
       
   262 
       
   263     The next step to creating our basic address book application is to allow
       
   264     a little bit of user interaction.
       
   265 
       
   266     \image addressbook-tutorial-part2-add-contact.png
       
   267 
       
   268     We will provide a push button that the user can click to add a new contact.
       
   269     Also, some form of data structure is needed to store these contacts in an
       
   270     organized way.
       
   271 
       
   272     \section1 Defining the AddressBook Class
       
   273 
       
   274     Now that we have the labels and input fields set up, we add push buttons to
       
   275     complete the process of adding a contact. This means that our
       
   276     \c addressbook.h file now has three QPushButton objects declared and three
       
   277     corresponding public slots.
       
   278 
       
   279     \snippet tutorials/addressbook/part2/addressbook.h slots
       
   280 
       
   281     A slot is a function that responds to a particular signal. We will discuss
       
   282     this concept in further detail when implementing the \c AddressBook class.
       
   283     However, for an overview of Qt's signals and slots concept, you can refer
       
   284     to the \l{Signals and Slots} document.
       
   285 
       
   286     Three QPushButton objects: \c addButton, \c submitButton and
       
   287     \c cancelButton, are now included in our private variable declarations,
       
   288     along with \c nameLine and \c addressText from the last chapter.
       
   289 
       
   290     \snippet tutorials/addressbook/part2/addressbook.h pushbutton declaration
       
   291 
       
   292     We need a container to store our address book contacts, so that we can
       
   293     traverse and display them. A QMap object, \c contacts, is used for this
       
   294     purpose as it holds a key-value pair: the contact's name as the \e key,
       
   295     and the contact's address as the \e{value}.
       
   296 
       
   297     \snippet tutorials/addressbook/part2/addressbook.h remaining private variables
       
   298 
       
   299     We also declare two private QString objects, \c oldName and \c oldAddress.
       
   300     These objects are needed to hold the name and address of the contact that
       
   301     was last displayed, before the user clicked \gui Add. So, when the user clicks
       
   302     \gui Cancel, we can revert to displaying the details of the last contact.
       
   303 
       
   304     \section1 Implementing the AddressBook Class
       
   305 
       
   306     Within the constructor of \c AddressBook, we set the \c nameLine and
       
   307     \c addressText to read-only, so that we can only display but not edit
       
   308     existing contact details.
       
   309 
       
   310     \dots
       
   311     \snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 1
       
   312     \dots
       
   313     \snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 2
       
   314 
       
   315     Then, we instantiate our push buttons: \c addButton, \c submitButton, and
       
   316     \c cancelButton.
       
   317 
       
   318     \snippet tutorials/addressbook/part2/addressbook.cpp pushbutton declaration
       
   319 
       
   320     The \c addButton is displayed by invoking the \l{QPushButton::show()}
       
   321     {show()} function, while the \c submitButton and \c cancelButton are
       
   322     hidden by invoking \l{QPushButton::hide()}{hide()}. These two push
       
   323     buttons will only be displayed when the user clicks \gui Add and this is
       
   324     handled by the \c addContact() function discussed below.
       
   325 
       
   326     \snippet tutorials/addressbook/part2/addressbook.cpp connecting signals and slots
       
   327 
       
   328     We connect the push buttons' \l{QPushButton::clicked()}{clicked()} signal
       
   329     to their respective slots. The figure below illustrates this.
       
   330 
       
   331     \image addressbook-tutorial-part2-signals-and-slots.png
       
   332 
       
   333     Next, we arrange our push buttons neatly to the right of our address book
       
   334     widget, using a QVBoxLayout to line them up vertically.
       
   335 
       
   336     \snippet tutorials/addressbook/part2/addressbook.cpp vertical layout
       
   337 
       
   338     The \l{QBoxLayout::addStretch()}{addStretch()} function is used to ensure
       
   339     the push buttons are not evenly spaced, but arranged closer to the top of
       
   340     the widget. The figure below shows the difference between using
       
   341     \l{QBoxLayout::addStretch()}{addStretch()} and not using it.
       
   342 
       
   343     \image addressbook-tutorial-part2-stretch-effects.png
       
   344 
       
   345     We then add \c buttonLayout1 to \c mainLayout, using
       
   346     \l{QGridLayout::addLayout()}{addLayout()}. This gives us nested layouts
       
   347     as \c buttonLayout1 is now a child of \c mainLayout.
       
   348 
       
   349     \snippet tutorials/addressbook/part2/addressbook.cpp grid layout
       
   350 
       
   351     Our layout coordinates now look like this:
       
   352 
       
   353     \image addressbook-tutorial-part2-labeled-layout.png
       
   354 
       
   355     In the \c addContact() function, we store the last displayed contact
       
   356     details in \c oldName and \c oldAddress. Then we clear these input
       
   357     fields and turn off the read-only mode. The focus is set on \c nameLine
       
   358     and we display \c submitButton and \c cancelButton.
       
   359 
       
   360     \snippet tutorials/addressbook/part2/addressbook.cpp addContact
       
   361 
       
   362     The \c submitContact() function can be divided into three parts:
       
   363 
       
   364     \list 1
       
   365     \o We extract the contact's details from \c nameLine and \c addressText
       
   366     and store them in QString objects. We also validate to make sure that the
       
   367     user did not click \gui Submit with empty input fields; otherwise, a
       
   368     QMessageBox is displayed to remind the user for a name and address.
       
   369 
       
   370     \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part1
       
   371 
       
   372     \o We then proceed to check if the contact already exists. If it does not
       
   373     exist, we add the contact to \c contacts and we display a QMessageBox to
       
   374     inform the user that the contact has been added.
       
   375 
       
   376     \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part2
       
   377 
       
   378     If the contact already exists, again, we display a QMessageBox to inform
       
   379     the user about this, preventing the user from adding duplicate contacts.
       
   380     Our \c contacts object is based on key-value pairs of name and address,
       
   381     hence, we want to ensure that \e key is unique.
       
   382 
       
   383     \o Once we have handled both cases mentioned above, we restore the push
       
   384     buttons to their normal state with the following code:
       
   385 
       
   386     \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part3
       
   387 
       
   388     \endlist
       
   389 
       
   390     The screenshot below shows the QMessageBox object we use to display
       
   391     information messages to the user.
       
   392 
       
   393     \image addressbook-tutorial-part2-add-successful.png
       
   394 
       
   395     The \c cancel() function restores the last displayed contact details and
       
   396     enables \c addButton, as well as hides \c submitButton and
       
   397     \c cancelButton.
       
   398 
       
   399     \snippet tutorials/addressbook/part2/addressbook.cpp cancel
       
   400 
       
   401     The general idea behind adding a contact is to give the user the
       
   402     flexibility to click \gui Submit or \gui Cancel at any time. The flowchart below
       
   403     further explains this concept:
       
   404 
       
   405     \image addressbook-tutorial-part2-add-flowchart.png
       
   406 */
       
   407 
       
   408 /*!
       
   409     \page tutorials-addressbook-part3.html
       
   410     \previouspage Address Book 2 - Adding Addresses
       
   411     \contentspage {Address Book Tutorial}{Contents}
       
   412     \nextpage {tutorials/addressbook/part4}{Chapter 4}
       
   413     \example tutorials/addressbook/part3
       
   414     \title Address Book 3 - Navigating between Entries
       
   415 
       
   416     The address book application is now half complete. We need to add some
       
   417     functions to navigate between contacts. But first, we have to decide
       
   418     what sort of a data structure we would like to use to hold these contacts.
       
   419 
       
   420     In Chapter 2, we used a QMap of key-value pairs with the contact's name
       
   421     as the \e key, and the contact's address as the \e value. This works well
       
   422     for our case. However, in order to navigate and display each entry, a
       
   423     little bit of enhancement is needed.
       
   424 
       
   425     We enhance the QMap by making it replicate a data structure similar to a
       
   426     circularly-linked list, where all elements are connected, including the
       
   427     first element and the last element. The figure below illustrates this data
       
   428     structure.
       
   429 
       
   430     \image addressbook-tutorial-part3-linkedlist.png
       
   431 
       
   432     \section1 Defining the AddressBook Class
       
   433 
       
   434     In order to add navigation functions to the address book application, we
       
   435     need to add two more slots to our \c AddressBook class: \c next() and
       
   436     \c previous(). These are added to our \c addressbook.h file:
       
   437 
       
   438     \snippet tutorials/addressbook/part3/addressbook.h navigation functions
       
   439 
       
   440     We also require another two QPushButton objects, so we declare \c nextButton
       
   441     and \c previousButton as private variables:
       
   442 
       
   443     \snippet tutorials/addressbook/part3/addressbook.h navigation pushbuttons
       
   444 
       
   445     \section1 Implementing the AddressBook Class
       
   446 
       
   447     In the \c AddressBook constructor in \c addressbook.cpp, we instantiate
       
   448     \c nextButton and \c previousButton and disable them by default. This is
       
   449     because navigation is only enabled when there is more than one contact
       
   450     in the address book.
       
   451 
       
   452     \snippet tutorials/addressbook/part3/addressbook.cpp navigation pushbuttons
       
   453 
       
   454     We then connect these push buttons to their respective slots:
       
   455 
       
   456     \snippet tutorials/addressbook/part3/addressbook.cpp connecting navigation signals
       
   457 
       
   458     The image below is our expected graphical user interface. Notice that it
       
   459     is getting closer to our final application.
       
   460 
       
   461     \image addressbook-tutorial-part3-screenshot.png
       
   462 
       
   463     We follow basic conventions for \c next() and \c previous() functions by
       
   464     placing the \c nextButton on the right and the \c previousButton on the
       
   465     left. In order to achieve this intuitive layout, we use QHBoxLayout to
       
   466     place the widgets side-by-side:
       
   467 
       
   468     \snippet tutorials/addressbook/part3/addressbook.cpp navigation layout
       
   469 
       
   470     The QHBoxLayout object, \c buttonLayout2, is then added to \c mainLayout.
       
   471 
       
   472     \snippet tutorials/addressbook/part3/addressbook.cpp adding navigation layout
       
   473 
       
   474     The figure below shows the coordinates of the widgets in \c mainLayout.
       
   475     \image addressbook-tutorial-part3-labeled-layout.png
       
   476 
       
   477     Within our \c addContact() function, we have to disable these buttons so
       
   478     that the user does not attempt to navigate while adding a contact.
       
   479 
       
   480     \snippet tutorials/addressbook/part3/addressbook.cpp disabling navigation
       
   481 
       
   482     Also, in our \c submitContact() function, we enable the navigation
       
   483     buttons, \c nextButton and \c previousButton, depending on the size
       
   484     of \c contacts. As mentioned earlier, navigation is only enabled when
       
   485     there is more than one contact in the address book. The following lines
       
   486     of code demonstrates how to do this:
       
   487 
       
   488     \snippet tutorials/addressbook/part3/addressbook.cpp enabling navigation
       
   489 
       
   490     We also include these lines of code in the \c cancel() function.
       
   491 
       
   492     Recall that we intend to emulate a circularly-linked list with our QMap
       
   493     object, \c contacts. So, in the \c next() function, we obtain an iterator
       
   494     for \c contacts and then:
       
   495 
       
   496     \list
       
   497         \o If the iterator is not at the end of \c contacts, we increment it
       
   498         by one.
       
   499         \o If the iterator is at the end of \c contacts, we move it to the
       
   500         beginning of \c contacts. This gives us the illusion that our QMap is
       
   501         working like a circularly-linked list.
       
   502     \endlist
       
   503 
       
   504     \snippet tutorials/addressbook/part3/addressbook.cpp next() function
       
   505 
       
   506     Once we have iterated to the correct object in \c contacts, we display
       
   507     its contents on \c nameLine and \c addressText.
       
   508 
       
   509     Similarly, for the \c previous() function, we obtain an iterator for
       
   510     \c contacts and then:
       
   511 
       
   512     \list
       
   513         \o If the iterator is at the end of \c contacts, we clear the
       
   514         display and return.
       
   515         \o If the iterator is at the beginning of \c contacts, we move it to
       
   516         the end.
       
   517         \o We then decrement the iterator by one.
       
   518     \endlist
       
   519 
       
   520     \snippet tutorials/addressbook/part3/addressbook.cpp previous() function
       
   521 
       
   522     Again, we display the contents of the current object in \c contacts.
       
   523 
       
   524 */
       
   525 
       
   526 /*!
       
   527     \page tutorials-addressbook-part4.html
       
   528     \previouspage Address Book 3 - Navigating between Entries
       
   529     \contentspage {Address Book Tutorial}{Contents}
       
   530     \nextpage {tutorials/addressbook/part5}{Chapter 5}
       
   531     \example tutorials/addressbook/part4
       
   532     \title Address Book 4 - Editing and Removing Addresses
       
   533 
       
   534     In this chapter, we look at ways to modify the contents of contacts stored
       
   535     in the address book application.
       
   536 
       
   537     \image addressbook-tutorial-screenshot.png
       
   538 
       
   539     We now have an address book that not only holds contacts in an organized
       
   540     manner, but also allows navigation. It would be convenient to include
       
   541     edit and remove functions so that a contact's details can be changed
       
   542     when needed. However, this requires a little improvement, in the form of
       
   543     enums. In our previous chapters, we had two modes: \c{AddingMode} and
       
   544     \c{NavigationMode} - but they were not defined as enums. Instead, we
       
   545     enabled and disabled the corresponding buttons manually, resulting in
       
   546     multiple lines of repeated code.
       
   547 
       
   548     In this chapter, we define the \c Mode enum with three different values:
       
   549 
       
   550     \list
       
   551         \o \c{NavigationMode},
       
   552         \o \c{AddingMode}, and
       
   553         \o \c{EditingMode}.
       
   554     \endlist
       
   555 
       
   556     \section1 Defining the AddressBook Class
       
   557 
       
   558     The \c addressbook.h file is updated to contain the \c Mode enum:
       
   559 
       
   560     \snippet tutorials/addressbook/part4/addressbook.h Mode enum
       
   561 
       
   562     We also add two new slots, \c editContact() and \c removeContact(), to
       
   563     our current list of public slots.
       
   564 
       
   565     \snippet tutorials/addressbook/part4/addressbook.h edit and remove slots
       
   566 
       
   567     In order to switch between modes, we introduce the \c updateInterface() function
       
   568     to control the enabling and disabling of all QPushButton objects. We also
       
   569     add two new push buttons, \c editButton and \c removeButton, for the edit
       
   570     and remove functions mentioned earlier.
       
   571 
       
   572     \snippet tutorials/addressbook/part4/addressbook.h updateInterface() declaration
       
   573     \dots
       
   574     \snippet tutorials/addressbook/part4/addressbook.h buttons declaration
       
   575     \dots
       
   576     \snippet tutorials/addressbook/part4/addressbook.h mode declaration
       
   577 
       
   578     Lastly, we declare \c currentMode to keep track of the enum's current mode.
       
   579 
       
   580     \section1 Implementing the AddressBook Class
       
   581 
       
   582     We now have to implement the mode-changing features of the address book
       
   583     application. The \c editButton and \c removeButton are instantiated and
       
   584     disabled by default, as the address book starts up with zero contacts in
       
   585     memory.
       
   586 
       
   587     \snippet tutorials/addressbook/part4/addressbook.cpp edit and remove buttons
       
   588 
       
   589     These buttons are then connected to their respective slots, \c editContact()
       
   590     and \c removeContact(), and we add them to \c buttonLayout1.
       
   591 
       
   592     \snippet tutorials/addressbook/part4/addressbook.cpp connecting edit and remove
       
   593     \dots
       
   594     \snippet tutorials/addressbook/part4/addressbook.cpp adding edit and remove to the layout
       
   595 
       
   596     The \c editContact() function stores the contact's old details in
       
   597     \c oldName and \c oldAddress, before switching the mode to \c EditingMode.
       
   598     In this mode, the \c submitButton and \c cancelButton are both enabled,
       
   599     hence, the user can change the contact's details and click either button.
       
   600 
       
   601     \snippet tutorials/addressbook/part4/addressbook.cpp editContact() function
       
   602 
       
   603     The \c submitContact() function has been divided in two with an \c{if-else}
       
   604     statement. We check \c currentMode to see if it's in \c AddingMode. If it is,
       
   605     we proceed with our adding process.
       
   606 
       
   607     \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function beginning
       
   608     \dots
       
   609     \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part1
       
   610 
       
   611     Otherwise, we check to see if \c currentMode is in \c EditingMode. If it
       
   612     is, we compare \c oldName with \c name. If the name has changed, we remove
       
   613     the old contact from \c contacts and insert the newly updated contact.
       
   614 
       
   615     \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part2
       
   616 
       
   617     If only the address has changed (i.e., \c oldAddress is not the same as \c address),
       
   618     we update the contact's address. Lastly, we set \c currentMode to
       
   619     \c NavigationMode. This is an important step as it re-enables all the
       
   620     disabled push buttons.
       
   621 
       
   622     To remove a contact from the address book, we implement the
       
   623     \c removeContact() function. This function checks to see if the contact
       
   624     exists in \c contacts.
       
   625 
       
   626     \snippet tutorials/addressbook/part4/addressbook.cpp removeContact() function
       
   627 
       
   628     If it does, we display a QMessageBox, to confirm the removal with the
       
   629     user. Once the user has confirmed, we call \c previous() to ensure that the
       
   630     user interface shows another contact, and we remove the contact using \l{QMap}'s
       
   631     \l{QMap::remove()}{remove()} function. As a courtesy, we display a QMessageBox
       
   632     to inform the user. Both the message boxes used in this function are shown below:
       
   633 
       
   634     \image addressbook-tutorial-part4-remove.png
       
   635 
       
   636     \section2 Updating the User Interface
       
   637 
       
   638     We mentioned the \c updateInterface() function earlier as a means to
       
   639     enable and disable the push buttons depending on the current mode.
       
   640     The function updates the current mode according to the \c mode argument
       
   641     passed to it, assigning it to \c currentMode before checking its value.
       
   642 
       
   643     Each of the push buttons is then enabled or disabled, depending on the
       
   644     current mode. The code for \c AddingMode and \c EditingMode is shown below:
       
   645 
       
   646     \snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 1
       
   647 
       
   648     For \c NavigationMode, however, we include conditions within the parameters
       
   649     of the QPushButton::setEnabled() function. This is to ensure that
       
   650     \c editButton and \c removeButton are enabled when there is at least one
       
   651     contact in the address book; \c nextButton and \c previousButton are only
       
   652     enabled when there is more than one contact in the address book.
       
   653 
       
   654     \snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 2
       
   655 
       
   656     By performing the task of setting the mode and updating the user interface in
       
   657     the same function, we avoid the possibility of the user interface getting "out
       
   658     of sync" with the internal state of the application.
       
   659 */
       
   660 
       
   661 /*!
       
   662     \page tutorials-addressbook-part5.html
       
   663     \previouspage Address Book 4 - Editing and Removing Addresses
       
   664     \contentspage {Address Book Tutorial}{Contents}
       
   665     \nextpage {tutorials/addressbook/part6}{Chapter 6}
       
   666     \example tutorials/addressbook/part5
       
   667     \title Address Book 5 - Adding a Find Function
       
   668 
       
   669     In this chapter, we look at ways to locate contacts and addresses in
       
   670     the address book application.
       
   671 
       
   672     \image addressbook-tutorial-part5-screenshot.png
       
   673 
       
   674     As we keep adding contacts to our address book application, it becomes
       
   675     tedious to navigate them with the \e Next and \e Previous buttons. In this
       
   676     case, a \e Find function would be more efficient in looking up contacts.
       
   677     The screenshot above shows the \e Find button and its position on the panel
       
   678     of buttons.
       
   679 
       
   680     When the user clicks on the \e Find button, it is useful to display a
       
   681     dialog that can prompt the user for a contact's name. Qt provides QDialog,
       
   682     which we subclass in this chapter, to implement a \c FindDialog class.
       
   683 
       
   684     \section1 Defining the FindDialog Class
       
   685 
       
   686     \image addressbook-tutorial-part5-finddialog.png
       
   687 
       
   688     In order to subclass QDialog, we first include the header for QDialog in
       
   689     the \c finddialog.h file. Also, we use forward declaration to declare
       
   690     QLineEdit and QPushButton since we will be using those widgets in our
       
   691     dialog class.
       
   692 
       
   693     As in our \c AddressBook class, the \c FindDialog class includes
       
   694     the Q_OBJECT macro and its constructor is defined to accept a parent
       
   695     QWidget, even though the dialog will be opened as a separate window.
       
   696 
       
   697     \snippet tutorials/addressbook/part5/finddialog.h FindDialog header
       
   698 
       
   699     We define a public function, \c getFindText(), to be used by classes that
       
   700     instantiate \c FindDialog. This function allows these classes to obtain the
       
   701     search string entered by the user. A public slot, \c findClicked(), is also
       
   702     defined to handle the search string when the user clicks the \gui Find
       
   703     button.
       
   704 
       
   705     Lastly, we define the private variables, \c findButton, \c lineEdit
       
   706     and \c findText, corresponding to the \gui Find button, the line edit
       
   707     into which the user types the search string, and an internal string
       
   708     used to store the search string for later use.
       
   709 
       
   710     \section1 Implementing the FindDialog Class
       
   711 
       
   712     Within the constructor of \c FindDialog, we set up the private variables,
       
   713     \c lineEdit, \c findButton and \c findText. We use a QHBoxLayout to
       
   714     position the widgets.
       
   715 
       
   716     \snippet tutorials/addressbook/part5/finddialog.cpp constructor
       
   717 
       
   718     We set the layout and window title, as well as connect the signals to their
       
   719     respective slots. Notice that \c{findButton}'s \l{QPushButton::clicked()}
       
   720     {clicked()} signal is connected to to \c findClicked() and
       
   721     \l{QDialog::accept()}{accept()}. The \l{QDialog::accept()}{accept()} slot
       
   722     provided by QDialog hides the dialog and sets the result code to
       
   723     \l{QDialog::}{Accepted}. We use this function to help \c{AddressBook}'s
       
   724     \c findContact() function know when the \c FindDialog object has been
       
   725     closed. We will explain this logic in further detail when discussing the
       
   726     \c findContact() function.
       
   727 
       
   728     \image addressbook-tutorial-part5-signals-and-slots.png
       
   729 
       
   730     In \c findClicked(), we validate \c lineEdit to ensure that the user
       
   731     did not click the \gui Find button without entering a contact's name. Then, we set
       
   732     \c findText to the search string, extracted from \c lineEdit. After that,
       
   733     we clear the contents of \c lineEdit and hide the dialog.
       
   734 
       
   735     \snippet tutorials/addressbook/part5/finddialog.cpp findClicked() function
       
   736 
       
   737     The \c findText variable has a public getter function, \c getFindText(),
       
   738     associated with it. Since we only ever set \c findText directly in both the
       
   739     constructor and in the \c findClicked() function, we do not create a
       
   740     setter function to accompany \c getFindText().
       
   741     Because \c getFindText() is public, classes instantiating and using
       
   742     \c FindDialog can always access the search string that the user has
       
   743     entered and accepted.
       
   744 
       
   745     \snippet tutorials/addressbook/part5/finddialog.cpp getFindText() function
       
   746 
       
   747     \section1 Defining the AddressBook Class
       
   748 
       
   749     To ensure we can use \c FindDialog from within our \c AddressBook class, we
       
   750     include \c finddialog.h in the \c addressbook.h file.
       
   751 
       
   752     \snippet tutorials/addressbook/part5/addressbook.h include finddialog's header
       
   753 
       
   754     So far, all our address book features have a QPushButton and a
       
   755     corresponding slot. Similarly, for the \gui Find feature we have
       
   756     \c findButton and \c findContact().
       
   757 
       
   758     The \c findButton is declared as a private variable and the
       
   759     \c findContact() function is declared as a public slot.
       
   760 
       
   761     \snippet tutorials/addressbook/part5/addressbook.h findContact() declaration
       
   762     \dots
       
   763     \snippet tutorials/addressbook/part5/addressbook.h findButton declaration
       
   764 
       
   765     Lastly, we declare the private variable, \c dialog, which we will use to
       
   766     refer to an instance of \c FindDialog.
       
   767 
       
   768     \snippet tutorials/addressbook/part5/addressbook.h FindDialog declaration
       
   769 
       
   770     Once we have instantiated a dialog, we will want to use it more than once;
       
   771     using a private variable allows us to refer to it from more than one place
       
   772     in the class.
       
   773 
       
   774     \section1 Implementing the AddressBook Class
       
   775 
       
   776     Within the \c AddressBook class's constructor, we instantiate our private
       
   777     objects, \c findButton and \c findDialog:
       
   778 
       
   779     \snippet tutorials/addressbook/part5/addressbook.cpp instantiating findButton
       
   780     \dots
       
   781     \snippet tutorials/addressbook/part5/addressbook.cpp instantiating FindDialog
       
   782 
       
   783     Next, we connect the \c{findButton}'s
       
   784     \l{QPushButton::clicked()}{clicked()} signal to \c findContact().
       
   785 
       
   786     \snippet tutorials/addressbook/part5/addressbook.cpp signals and slots for find
       
   787 
       
   788     Now all that is left is the code for our \c findContact() function:
       
   789 
       
   790     \snippet tutorials/addressbook/part5/addressbook.cpp findContact() function
       
   791 
       
   792     We start out by displaying the \c FindDialog instance, \c dialog. This is
       
   793     when the user enters a contact name to look up. Once the user clicks
       
   794     the dialog's \c findButton, the dialog is hidden and the result code is
       
   795     set to QDialog::Accepted. This ensures that
       
   796     our \c if statement is always true.
       
   797 
       
   798     We then proceed to extract the search string, which in this case is
       
   799     \c contactName, using \c{FindDialog}'s \c getFindText() function. If the
       
   800     contact exists in our address book, we display it immediately. Otherwise,
       
   801     we display the QMessageBox shown below to indicate that their search
       
   802     failed.
       
   803 
       
   804     \image addressbook-tutorial-part5-notfound.png
       
   805 */
       
   806 
       
   807 /*!
       
   808     \page tutorials-addressbook-part6.html
       
   809     \previouspage Address Book 5 - Adding a Find Function
       
   810     \contentspage {Address Book Tutorial}{Contents}
       
   811     \nextpage {tutorials/addressbook/part7}{Chapter 7}
       
   812     \example tutorials/addressbook/part6
       
   813     \title Address Book 6 - Loading and Saving
       
   814 
       
   815     This chapter covers the file handling features of Qt that we use to write
       
   816     loading and saving routines for the address book application.
       
   817 
       
   818     \image addressbook-tutorial-part6-screenshot.png
       
   819 
       
   820     Although browsing and searching for contacts are useful features, our
       
   821     address book is not ready for use until we can save existing contacts and
       
   822     load them again at a later time.
       
   823 
       
   824     Qt provides a number of classes for \l{Input/Output and Networking}
       
   825     {input and output}, but we have chosen to use two which are simple to use
       
   826     in combination: QFile and QDataStream.
       
   827 
       
   828     A QFile object represents a file on disk that can be read from and written
       
   829     to. QFile is a subclass of the more general QIODevice class which
       
   830     represents many different kinds of devices.
       
   831 
       
   832     A QDataStream object is used to serialize binary data so that it can be
       
   833     stored in a QIODevice and retrieved again later. Reading from a QIODevice
       
   834     and writing to it is as simple as opening the stream - with the respective
       
   835     device as a parameter - and reading from or writing to it.
       
   836 
       
   837 
       
   838     \section1 Defining the AddressBook Class
       
   839 
       
   840     We declare two public slots, \c saveToFile() and \c loadFromFile(), as well
       
   841     as two QPushButton objects, \c loadButton and \c saveButton.
       
   842 
       
   843     \snippet tutorials/addressbook/part6/addressbook.h save and load functions declaration
       
   844     \dots
       
   845     \snippet tutorials/addressbook/part6/addressbook.h save and load buttons declaration
       
   846 
       
   847     \section1 Implementing the AddressBook Class
       
   848 
       
   849     In our constructor, we instantiate \c loadButton and \c saveButton.
       
   850     Ideally, it would be more user-friendly to set the push buttons' labels
       
   851     to "Load contacts from a file" and "Save contacts to a file". However, due
       
   852     to the size of our other push buttons, we set the labels to \gui{Load...}
       
   853     and \gui{Save...}. Fortunately, Qt provides a simple way to set tooltips with
       
   854     \l{QWidget::setToolTip()}{setToolTip()} and we use it in the following way
       
   855     for our push buttons:
       
   856 
       
   857     \snippet tutorials/addressbook/part6/addressbook.cpp tooltip 1
       
   858     \dots
       
   859     \snippet tutorials/addressbook/part6/addressbook.cpp tooltip 2
       
   860 
       
   861     Although it is not shown here, just like the other features we implemented,
       
   862     we add the push buttons to the layout panel on the right, \c button1Layout,
       
   863     and we connect the push buttons' \l{QPushButton::clicked()}{clicked()}
       
   864     signals to their respective slots.
       
   865 
       
   866     For the saving feature, we first obtain \c fileName using
       
   867     QFileDialog::getSaveFileName(). This is a convenience function provided
       
   868     by QFileDialog, which pops up a modal file dialog and allows the user to
       
   869     enter a file name or select any existing \c{.abk} file. The \c{.abk} file
       
   870     is our Address Book extension that we create when we save contacts.
       
   871 
       
   872     \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part1
       
   873 
       
   874     The file dialog that pops up is displayed in the screenshot below:
       
   875 
       
   876     \image addressbook-tutorial-part6-save.png
       
   877 
       
   878     If \c fileName is not empty, we create a QFile object, \c file, with
       
   879     \c fileName. QFile works with QDataStream as QFile is a QIODevice.
       
   880 
       
   881     Next, we attempt to open the file in \l{QIODevice::}{WriteOnly} mode.
       
   882     If this is unsuccessful, we display a QMessageBox to inform the user.
       
   883 
       
   884     \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part2
       
   885 
       
   886     Otherwise, we instantiate a QDataStream object, \c out, to write the open
       
   887     file. QDataStream requires that the same version of the stream is used
       
   888     for reading and writing. We ensure that this is the case by setting the
       
   889     version used to the \l{QDataStream::Qt_4_5}{version introduced with Qt 4.5}
       
   890     before serializing the data to \c file.
       
   891 
       
   892     \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part3
       
   893 
       
   894     For the loading feature, we also obtain \c fileName using
       
   895     QFileDialog::getOpenFileName(). This function, the counterpart to
       
   896     QFileDialog::getSaveFileName(), also pops up the modal file dialog and
       
   897     allows the user to enter a file name or select any existing \c{.abk} file
       
   898     to load it into the address book.
       
   899 
       
   900     \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part1
       
   901 
       
   902     On Windows, for example, this function pops up a native file dialog, as
       
   903     shown in the following screenshot.
       
   904 
       
   905     \image addressbook-tutorial-part6-load.png
       
   906 
       
   907     If \c fileName is not empty, again, we use a QFile object, \c file, and
       
   908     attempt to open it in \l{QIODevice::}{ReadOnly} mode. Similar to our
       
   909     implementation of \c saveToFile(), if this attempt is unsuccessful, we
       
   910     display a QMessageBox to inform the user.
       
   911 
       
   912     \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part2
       
   913 
       
   914     Otherwise, we instantiate a QDataStream object, \c in, set its version as
       
   915     above and read the serialized data into the \c contacts data structure.
       
   916     The \c contacts object is emptied before data is read into it to simplify
       
   917     the file reading process. A more advanced method would be to read the
       
   918     contacts into a temporary QMap object, and copy over non-duplicate contacts
       
   919     into \c contacts.
       
   920 
       
   921     \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part3
       
   922 
       
   923     To display the contacts that have been read from the file, we must first
       
   924     validate the data obtained to ensure that the file we read from actually
       
   925     contains address book contacts. If it does, we display the first contact;
       
   926     otherwise, we display a QMessageBox to inform the user about the problem.
       
   927     Lastly, we update the interface to enable and disable the push buttons
       
   928     accordingly.
       
   929 */
       
   930 
       
   931 /*!
       
   932     \page tutorials-addressbook-part7.html
       
   933     \previouspage Address Book 6 - Loading and Saving
       
   934     \contentspage {Address Book Tutorial}{Contents}
       
   935     \example tutorials/addressbook/part7
       
   936     \title Address Book 7 - Additional Features
       
   937 
       
   938     This chapter covers some additional features that make the address book
       
   939     application more convenient for everyday use.
       
   940 
       
   941     \image addressbook-tutorial-part7-screenshot.png
       
   942 
       
   943     Although our address book application is useful in its own right, it would
       
   944     be useful if we could exchange contact data with other applications.
       
   945     The vCard format is a popular file format that can be used for this purpose.
       
   946     In this chapter, we extend our address book client to allow contacts to
       
   947     be exported to vCard \c{.vcf} files.
       
   948 
       
   949     \section1 Defining the AddressBook Class
       
   950 
       
   951     We add a QPushButton object, \c exportButton, and a corresponding public
       
   952     slot, \c exportAsVCard() to our \c AddressBook class in the
       
   953     \c addressbook.h file.
       
   954 
       
   955     \snippet tutorials/addressbook/part7/addressbook.h exportAsVCard() declaration
       
   956     \dots
       
   957     \snippet tutorials/addressbook/part7/addressbook.h exportButton declaration
       
   958 
       
   959     \section1 Implementing the AddressBook Class
       
   960 
       
   961     Within the \c AddressBook constructor, we connect \c{exportButton}'s
       
   962     \l{QPushButton::clicked()}{clicked()} signal to \c exportAsVCard().
       
   963     We also add this button to our \c buttonLayout1, the layout responsible
       
   964     for our panel of buttons on the right.
       
   965 
       
   966     In our \c exportAsVCard() function, we start by extracting the contact's
       
   967     name into \c name. We declare \c firstName, \c lastName and \c nameList.
       
   968     Next, we look for the index of the first white space in \c name. If there
       
   969     is a white space, we split the contact's name into \c firstName and
       
   970     \c lastName. Then, we replace the space with an underscore ("_").
       
   971     Alternately, if there is no white space, we assume that the contact only
       
   972     has a first name.
       
   973 
       
   974     \snippet tutorials/addressbook/part7/addressbook.cpp export function part1
       
   975 
       
   976     As with the \c saveToFile() function, we open a file dialog to let the user
       
   977     choose a location for the file. Using the file name chosen, we create an
       
   978     instance of QFile to write to.
       
   979 
       
   980     We attempt to open the file in \l{QIODevice::}{WriteOnly} mode. If this
       
   981     process fails, we display a QMessageBox to inform the user about the
       
   982     problem and return. Otherwise, we pass the file as a parameter to a
       
   983     QTextStream object, \c out. Like QDataStream, the QTextStream class
       
   984     provides functionality to read and write plain text to files. As a result,
       
   985     the \c{.vcf} file generated can be opened for editing in a text editor.
       
   986 
       
   987     \snippet tutorials/addressbook/part7/addressbook.cpp export function part2
       
   988 
       
   989     We then write out a vCard file with the \c{BEGIN:VCARD} tag, followed by
       
   990     the \c{VERSION:2.1} tag. The contact's name is written with the \c{N:}
       
   991     tag. For the \c{FN:} tag, which fills in the "File as" property of a vCard,
       
   992     we have to check whether the contact has a last name or not. If the contact
       
   993     does, we use the details in \c nameList to fill it. Otherwise, we write
       
   994     \c firstName only.
       
   995 
       
   996     \snippet tutorials/addressbook/part7/addressbook.cpp export function part3
       
   997 
       
   998     We proceed to write the contact's address. The semicolons in the address
       
   999     are escaped with "\\", the newlines are replaced with semicolons, and the
       
  1000     commas are replaced with spaces. Lastly, we write the \c{ADR;HOME:;}
       
  1001     tag, followed by \c address and then the \c{END:VCARD} tag.
       
  1002 
       
  1003     \snippet tutorials/addressbook/part7/addressbook.cpp export function part4
       
  1004 
       
  1005     In the end, a QMessageBox is displayed to inform the user that the vCard
       
  1006     has been successfully exported.
       
  1007 
       
  1008     \e{vCard is a trademark of the \l{http://www.imc.org}
       
  1009     {Internet Mail Consortium}}.
       
  1010 */