diff options
Diffstat (limited to 'tests/manual/examples/widgets/tutorials')
67 files changed, 3745 insertions, 0 deletions
diff --git a/tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt new file mode 100644 index 0000000000..066a323d61 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +qt_internal_add_example(part1) +qt_internal_add_example(part2) +qt_internal_add_example(part3) +qt_internal_add_example(part4) +qt_internal_add_example(part5) +qt_internal_add_example(part6) +qt_internal_add_example(part7) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/README b/tests/manual/examples/widgets/tutorials/addressbook/README new file mode 100644 index 0000000000..07897b9683 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/README @@ -0,0 +1,40 @@ +The Address Book Tutorial shows how to put together a simple yet +fully-functioning GUI application. The tutorial chapters can be found in the +Qt documentation, which can be viewed using Qt Assistant or a Web browser. + +The tutorial is also available online at + +http://qt-project.org/doc/qt-5.0/qtwidgets/tutorials-addressbook.html + +All programs corresponding to the chapters in the tutorial should +automatically be built when Qt is compiled, or will be provided as +pre-built executables if you have obtained a binary package of Qt. + +If you have only compiled the Qt libraries, use the following instructions +to build the tutorial. + +On Linux/Unix: + +Typing 'make' in this directory builds all the programs (part1/part1, +part2/part2, part3/part3 and so on). Typing 'make' in each subdirectory +builds just that tutorial program. + +On Windows: + +Create a single Visual Studio project for the tutorial directory in +the usual way. You can do this by typing the following at the command +line: + +qmake -tp vc + +You should now be able to open the project file in Visual Studio and +build all of the tutorial programs at the same time. + +On Mac OS X: + +Create an Xcode project with the .pro file in the tutorial directory. +You can do this by typing the following at the command line: + +qmake -spec macx-xcode + +Then open the generated Xcode project in Xcode and build it. diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.png Binary files differnew file mode 100644 index 0000000000..b19cb360a1 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-layout.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.png Binary files differnew file mode 100644 index 0000000000..f9b91eebe6 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-labeled-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.png Binary files differnew file mode 100644 index 0000000000..454b0959e6 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part1-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.png Binary files differnew file mode 100644 index 0000000000..6f2b947b21 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-contact.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.png Binary files differnew file mode 100644 index 0000000000..ca9af3720d --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-flowchart.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.png Binary files differnew file mode 100644 index 0000000000..99a2154007 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-add-successful.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.png Binary files differnew file mode 100644 index 0000000000..1e000c8f31 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-labeled-layout.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.png Binary files differnew file mode 100644 index 0000000000..e49f8dc262 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-signals-and-slots.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.png Binary files differnew file mode 100644 index 0000000000..d9f7f31227 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part2-stretch-effects.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.png Binary files differnew file mode 100644 index 0000000000..1981ba8cb6 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-labeled-layout.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.png Binary files differnew file mode 100644 index 0000000000..e7f4725dce --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-linkedlist.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.png Binary files differnew file mode 100644 index 0000000000..75159b4045 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part3-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.png Binary files differnew file mode 100644 index 0000000000..8eb259ef02 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part4-remove.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.png Binary files differnew file mode 100644 index 0000000000..743d92ef6f --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-finddialog.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.png Binary files differnew file mode 100644 index 0000000000..2d35766ab5 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-notfound.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.png Binary files differnew file mode 100644 index 0000000000..3abe2775c2 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.png Binary files differnew file mode 100644 index 0000000000..1771e7bbbf --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part5-signals-and-slots.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.png Binary files differnew file mode 100644 index 0000000000..a027a1decb --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-load.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.png Binary files differnew file mode 100644 index 0000000000..757feeb9ac --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-save.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.png Binary files differnew file mode 100644 index 0000000000..7bb2f749bf --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part6-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.png Binary files differnew file mode 100644 index 0000000000..3e7b3ca522 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-part7-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.png b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.png Binary files differnew file mode 100644 index 0000000000..3fba6e849e --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial-screenshot.png diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc new file mode 100644 index 0000000000..2f7884bee8 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook-tutorial.qdoc @@ -0,0 +1,948 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + \page tutorials-addressbook.html + + \title Address Book Tutorial + \ingroup examples-layout + \brief An introduction to GUI programming, showing how to put together a + simple yet fully-functioning application. + + This tutorial is an introduction to GUI programming with the Qt + cross-platform framework. + + \image addressbook-tutorial-screenshot.png + + \omit + It doesn't cover everything; the emphasis is on teaching the programming + philosophy of GUI programming, and Qt's features are introduced as needed. + Some commonly used features are never used in this tutorial. + \endomit + + In this tutorial, you will learn about some of the basic + components of Qt, including: + + \list + \li Widgets and layout managers + \li Container classes + \li Signals and slots + \li Input and output devices + \endlist + + Tutorial contents: + + \list 1 + \li \l{tutorials/addressbook/part1}{Designing the User Interface} + \li \l{tutorials/addressbook/part2}{Adding Addresses} + \li \l{tutorials/addressbook/part3}{Navigating between Entries} + \li \l{tutorials/addressbook/part4}{Editing and Removing Addresses} + \li \l{tutorials/addressbook/part5}{Adding a Find Function} + \li \l{tutorials/addressbook/part6}{Loading and Saving} + \li \l{tutorials/addressbook/part7}{Additional Features} + \endlist + + The tutorial source code is located in \c{tutorials/addressbook}. + + Although this little application does not look much like a + fully-fledged modern GUI application, it uses many of the basic + elements that are used in more complex applications. After you + have worked through this tutorial, we recommend reading the + \l{mainwindows/application}{Application} example, which presents a + small GUI application, with menus, toolbars, a status bar, and so + on. +*/ + +/*! + \example tutorials/addressbook/part1 + \title Part 1 - Designing the User Interface + \brief Describes how to code the user interface of the Address Book Example. + This first part covers the design of the basic graphical user + interface (GUI) for our address book application. + + The first step in creating a GUI program is to design the user + interface. Here the our goal is to set up the labels and input + fields to implement a basic address book. The figure below is a + screenshot of the expected output. + + \image addressbook-tutorial-part1-screenshot.png + + We require two QLabel objects, \c nameLabel and \c addressLabel, as well + as two input fields, a QLineEdit object, \c nameLine, and a QTextEdit + object, \c addressText, to enable the user to enter a contact's name and + address. The widgets used and their positions are shown in the figure + below. + + \image addressbook-tutorial-part1-labeled-screenshot.png + + There are three files used to implement this address book: + + \list + \li \c{addressbook.h} - the definition file for the \c AddressBook + class, + \li \c{addressbook.cpp} - the implementation file for the + \c AddressBook class, and + \li \c{main.cpp} - the file containing a \c main() function, with + an instance of \c AddressBook. + \endlist + + \section1 Qt Programming - Subclassing + + When writing Qt programs, we usually subclass Qt objects to add + functionality. This is one of the essential concepts behind creating + custom widgets or collections of standard widgets. Subclassing to + extend or change the behavior of a widget has the following advantages: + + \list + \li We can write implementations of virtual or pure virtual functions to + obtain exactly what we need, falling back on the base class's implementation + when necessary. + \li It allows us to encapsulate parts of the user interface within a class, + so that the other parts of the application don't need to know about the + individual widgets in the user interface. + \li The subclass can be used to create multiple custom widgets in the same + application or library, and the code for the subclass can be reused in other + projects. + \endlist + + Since Qt does not provide a specific address book widget, we subclass a + standard Qt widget class and add features to it. The \c AddressBook class + we create in this tutorial can be reused in situations where a basic address + book widget is needed. + + \section1 Defining the AddressBook Class + + The \c{tutorials/addressbook/part1/addressbook.h} file is + used to define the \c AddressBook class. + + We start by defining \c AddressBook as a QWidget subclass and declaring + a constructor. We also use the Q_OBJECT macro to indicate that the class + uses internationalization and Qt's signals and slots features, even + if we do not use all of these features at this stage. + + \snippet tutorials/addressbook/part1/addressbook.h class definition + + The class holds declarations of \c nameLine and \c addressText, + the private instances of QLineEdit and QTextEdit mentioned + earlier. The data stored in \c nameLine and \c addressText will + be needed for many of the address book functions. + + We don't include declarations of the QLabel objects we will use + because we will not need to reference them once they have been + created. The way Qt tracks the ownership of objects is explained + in the next section. + + The Q_OBJECT macro itself implements some of the more advanced features of Qt. + For now, it is useful to think of the Q_OBJECT macro as a shortcut which allows + us to use the \l{QObject::}{tr()} and \l{QObject::}{connect()} functions. + + We have now completed the \c addressbook.h file and we move on to + implement the corresponding \c addressbook.cpp file. + + \section1 Implementing the AddressBook Class + + The constructor of \c AddressBook accepts a QWidget parameter, \a parent. + By convention, we pass this parameter to the base class's constructor. + This concept of ownership, where a parent can have one or more children, + is useful for grouping widgets in Qt. For example, if you delete a parent, + all of its children will be deleted as well. + + \snippet tutorials/addressbook/part1/addressbook.cpp constructor and input fields + + In this constructor, the QLabel objects \c nameLabel and \c + addressLabel are instantiated, as well as \c nameLine and \c + addressText. The \l{QObject::tr()}{tr()} function returns a + translated version of the string, if there is one + available. Otherwise it returns the string itself. This function + marks its QString parameter as one that should be translated into + other languages. It should be used wherever a translatable string + appears. + + When programming with Qt, it is useful to know how layouts work. + Qt provides three main layout classes: QHBoxLayout, QVBoxLayout + and QGridLayout to handle the positioning of widgets. + + \image addressbook-tutorial-part1-labeled-layout.png + + We use a QGridLayout to position our labels and input fields in a + structured manner. QGridLayout divides the available space into a grid and + places widgets in the cells we specify with row and column numbers. The + diagram above shows the layout cells and the position of our widgets, and + we specify this arrangement using the following code: + + \snippet tutorials/addressbook/part1/addressbook.cpp layout + + Notice that \c addressLabel is positioned using Qt::AlignTop as an + additional argument. This is to make sure it is not vertically centered in + cell (1,0). For a basic overview on Qt Layouts, refer to the + \l{Layout Management} documentation. + + In order to install the layout object onto the widget, we have to invoke + the widget's \l{QWidget::setLayout()}{setLayout()} function: + + \snippet tutorials/addressbook/part1/addressbook.cpp setting the layout + + Lastly, we set the widget's title to "Simple Address Book". + + \section1 Running the Application + + A separate file, \c main.cpp, is used for the \c main() function. Within + this function, we instantiate a QApplication object, \c app. QApplication + is responsible for various application-wide resources, such as the default + font and cursor, and for running an event loop. Hence, there is always one + QApplication object in every GUI application using Qt. + + \snippet tutorials/addressbook/part1/main.cpp main function + + We construct a new \c AddressBook widget on the stack and invoke + its \l{QWidget::show()}{show()} function to display it. + However, the widget will not be shown until the application's event loop + is started. We start the event loop by calling the application's + \l{QApplication::}{exec()} function; the result returned by this function + is used as the return value from the \c main() function. At this point, + it becomes apparent why we instantiated \c AddressBook on the stack: It + will now go out of scope. Therefore, \c AddressBook and all its child widgets + will be deleted, thus preventing memory leaks. +*/ + +/*! + \example tutorials/addressbook/part2 + \title Part 2 - Adding Addresses + \brief Describes the code for inserting records in the Address Book Example. + + The next step in creating the address book is to implement some + user interactions. + + \image addressbook-tutorial-part2-add-contact.png + + We will provide a push button that the user can click to add a new contact. + Also, some form of data structure is needed to store these contacts in an + organized way. + + \section1 Defining the AddressBook Class + + Now that we have the labels and input fields set up, we add push buttons to + complete the process of adding a contact. This means that our + \c addressbook.h file now has three QPushButton objects declared and three + corresponding public slots. + + \snippet tutorials/addressbook/part2/addressbook.h slots + + A slot is a function that responds to a particular signal. We will discuss + this concept in further detail when implementing the \c AddressBook class. + However, for an overview of Qt's signals and slots concept, you can refer + to the \l{Signals and Slots} document. + + Three QPushButton objects (\c addButton, \c submitButton, and + \c cancelButton) are now included in our private variable declarations, + along with \c nameLine and \c addressText. + + \snippet tutorials/addressbook/part2/addressbook.h pushbutton declaration + + We need a container to store our address book contacts, so that we can + traverse and display them. A QMap object, \c contacts, is used for this + purpose as it holds a key-value pair: the contact's name as the \e key, + and the contact's address as the \e{value}. + + \snippet tutorials/addressbook/part2/addressbook.h remaining private variables + + We also declare two private QString objects, \c oldName and \c oldAddress. + These objects are needed to hold the name and address of the contact that + was last displayed, before the user clicked \uicontrol Add. So, when the user clicks + \uicontrol Cancel, we can revert to displaying the details of the last contact. + + \section1 Implementing the AddressBook Class + + Within the constructor of \c AddressBook, we set the \c nameLine and + \c addressText to read-only, so that we can only display but not edit + existing contact details. + + \dots + \snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 1 + \dots + \snippet tutorials/addressbook/part2/addressbook.cpp setting readonly 2 + + Then, we instantiate our push buttons: \c addButton, \c submitButton, and + \c cancelButton. + + \snippet tutorials/addressbook/part2/addressbook.cpp pushbutton declaration + + The \c addButton is displayed by invoking the \l{QPushButton::show()} + {show()} function, while the \c submitButton and \c cancelButton are + hidden by invoking \l{QPushButton::hide()}{hide()}. These two push + buttons will only be displayed when the user clicks \uicontrol Add and this is + handled by the \c addContact() function discussed below. + + \snippet tutorials/addressbook/part2/addressbook.cpp connecting signals and slots + + We connect the push buttons' \l{QPushButton::clicked()}{clicked()} signal + to their respective slots. The figure below illustrates this. + + \image addressbook-tutorial-part2-signals-and-slots.png + + Next, we arrange our push buttons neatly to the right of our address book + widget, using a QVBoxLayout to line them up vertically. + + \snippet tutorials/addressbook/part2/addressbook.cpp vertical layout + + The \l{QBoxLayout::addStretch()}{addStretch()} function is used to ensure + the push buttons are not evenly spaced, but arranged closer to the top of + the widget. The figure below shows the difference between using + \l{QBoxLayout::addStretch()}{addStretch()} and not using it. + + \image addressbook-tutorial-part2-stretch-effects.png + + We then add \c buttonLayout1 to \c mainLayout, using + \l{QGridLayout::addLayout()}{addLayout()}. This gives us nested layouts + as \c buttonLayout1 is now a child of \c mainLayout. + + \snippet tutorials/addressbook/part2/addressbook.cpp grid layout + + Our layout coordinates now look like this: + + \image addressbook-tutorial-part2-labeled-layout.png + + In the \c addContact() function, we store the last displayed contact + details in \c oldName and \c oldAddress. Then we clear these input + fields and turn off the read-only mode. The focus is set on \c nameLine + and we display \c submitButton and \c cancelButton. + + \snippet tutorials/addressbook/part2/addressbook.cpp addContact + + The \c submitContact() function can be divided into three parts: + + \list 1 + \li We extract the contact's details from \c nameLine and \c addressText + and store them in QString objects. We also validate to make sure that the + user did not click \uicontrol Submit with empty input fields; otherwise, a + QMessageBox is displayed to remind the user for a name and address. + + \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part1 + + \li We then proceed to check if the contact already exists. If it does not + exist, we add the contact to \c contacts and we display a QMessageBox to + inform the user that the contact has been added. + + \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part2 + + If the contact already exists, again, we display a QMessageBox to inform + the user about this, preventing the user from adding duplicate contacts. + Our \c contacts object is based on key-value pairs of name and address, + hence, we want to ensure that \e key is unique. + + \li Once we have handled both cases mentioned above, we restore the push + buttons to their normal state with the following code: + + \snippet tutorials/addressbook/part2/addressbook.cpp submitContact part3 + + \endlist + + The screenshot below shows the QMessageBox object we use to display + information messages to the user. + + \image addressbook-tutorial-part2-add-successful.png + + The \c cancel() function restores the last displayed contact details and + enables \c addButton, as well as hides \c submitButton and + \c cancelButton. + + \snippet tutorials/addressbook/part2/addressbook.cpp cancel + + The general idea behind adding a contact is to give the user the + flexibility to click \uicontrol Submit or \uicontrol Cancel at any time. The flowchart below + further explains this concept: + + \image addressbook-tutorial-part2-add-flowchart.png +*/ + +/*! + \example tutorials/addressbook/part3 + \title Part 3 - Navigating between Entries + \brief Explains the code that enables navigating the contacts. + + The address book is now about half complete. We should add the + capability to navigate the contacts, but first we must + decide what sort of a data structure we need for containing these + contacts. + + In the previous section, we used a QMap of key-value pairs with + the contact's name as the \e key, and the contact's address as the + \e value. This works well for our case. However, in order to + navigate and display each entry, a little bit of enhancement is + needed. + + We enhance the QMap by making it replicate a data structure similar to a + circularly-linked list, where all elements are connected, including the + first element and the last element. The figure below illustrates this data + structure. + + \image addressbook-tutorial-part3-linkedlist.png + + \section1 Defining the AddressBook Class + + To add navigation functions to the address book, we must add two + more slots to the \c AddressBook class: \c next() and \c + previous() to the \c addressbook.h file: + + \snippet tutorials/addressbook/part3/addressbook.h navigation functions + + We also require another two QPushButton objects, so we declare \c nextButton + and \c previousButton as private variables: + + \snippet tutorials/addressbook/part3/addressbook.h navigation pushbuttons + + \section1 Implementing the AddressBook Class + + In the \c AddressBook constructor in \c addressbook.cpp, we instantiate + \c nextButton and \c previousButton and disable them by default. This is + because navigation is only enabled when there is more than one contact + in the address book. + + \snippet tutorials/addressbook/part3/addressbook.cpp navigation pushbuttons + + We then connect these push buttons to their respective slots: + + \snippet tutorials/addressbook/part3/addressbook.cpp connecting navigation signals + + The image below is the expected graphical user interface. + + \image addressbook-tutorial-part3-screenshot.png + + We follow basic conventions for \c next() and \c previous() functions by + placing the \c nextButton on the right and the \c previousButton on the + left. In order to achieve this intuitive layout, we use QHBoxLayout to + place the widgets side-by-side: + + \snippet tutorials/addressbook/part3/addressbook.cpp navigation layout + + The QHBoxLayout object, \c buttonLayout2, is then added to \c mainLayout. + + \snippet tutorials/addressbook/part3/addressbook.cpp adding navigation layout + + The figure below shows the coordinates of the widgets in \c mainLayout. + \image addressbook-tutorial-part3-labeled-layout.png + + Within our \c addContact() function, we have to disable these buttons so + that the user does not attempt to navigate while adding a contact. + + \snippet tutorials/addressbook/part3/addressbook.cpp disabling navigation + + Also, in our \c submitContact() function, we enable the navigation + buttons, \c nextButton and \c previousButton, depending on the size + of \c contacts. As mentioned earlier, navigation is only enabled when + there is more than one contact in the address book. The following lines + of code demonstrates how to do this: + + \snippet tutorials/addressbook/part3/addressbook.cpp enabling navigation + + We also include these lines of code in the \c cancel() function. + + Recall that we intend to emulate a circularly-linked list with our QMap + object, \c contacts. So, in the \c next() function, we obtain an iterator + for \c contacts and then: + + \list + \li If the iterator is not at the end of \c contacts, we increment it + by one. + \li If the iterator is at the end of \c contacts, we move it to the + beginning of \c contacts. This gives us the illusion that our QMap is + working like a circularly-linked list. + \endlist + + \snippet tutorials/addressbook/part3/addressbook.cpp next() function + + Once we have iterated to the correct object in \c contacts, we display + its contents on \c nameLine and \c addressText. + + Similarly, for the \c previous() function, we obtain an iterator for + \c contacts and then: + + \list + \li If the iterator is at the end of \c contacts, we clear the + display and return. + \li If the iterator is at the beginning of \c contacts, we move it to + the end. + \li We then decrement the iterator by one. + \endlist + + \snippet tutorials/addressbook/part3/addressbook.cpp previous() function + + Again, we display the contents of the current object in \c contacts. + +*/ + +/*! + \example tutorials/addressbook/part4 + \title Part 4 - Editing and Removing Addresses + \brief Explains how to add edit and remove functionality. + + Now we look at ways to modify the contents of contacts stored in + the address book. + + \image addressbook-tutorial-screenshot.png + + We now have an address book that not only holds contacts in an + organized manner, but also allows navigation. It would be + convenient to include edit and remove functions so that a + contact's details can be changed when needed. However, this + requires a little improvement, in the form of enums. We defined + two modes: \c{AddingMode} and \c{NavigationMode}, but they were + not defined as enum values. Instead, we enabled and disabled the + corresponding buttons manually, resulting in multiple lines of + repeated code. + + Here we define the \c Mode enum with three different values: + + \list + \li \c{NavigationMode}, + \li \c{AddingMode}, and + \li \c{EditingMode}. + \endlist + + \section1 Defining the AddressBook Class + + The \c addressbook.h file is updated to contain the \c Mode enum: + + \snippet tutorials/addressbook/part4/addressbook.h Mode enum + + We also add two new slots, \c editContact() and \c removeContact(), to + our current list of public slots. + + \snippet tutorials/addressbook/part4/addressbook.h edit and remove slots + + In order to switch between modes, we introduce the \c updateInterface() function + to control the enabling and disabling of all QPushButton objects. We also + add two new push buttons, \c editButton and \c removeButton, for the edit + and remove functions mentioned earlier. + + \snippet tutorials/addressbook/part4/addressbook.h updateInterface() declaration + \dots + \snippet tutorials/addressbook/part4/addressbook.h buttons declaration + \dots + \snippet tutorials/addressbook/part4/addressbook.h mode declaration + + Lastly, we declare \c currentMode to keep track of the enum's current mode. + + \section1 Implementing the AddressBook Class + + We now implement the mode-changing features of the address + book. The \c editButton and \c removeButton are instantiated and + disabled by default. The address book starts with zero contacts + in memory. + + \snippet tutorials/addressbook/part4/addressbook.cpp edit and remove buttons + + These buttons are then connected to their respective slots, \c editContact() + and \c removeContact(), and we add them to \c buttonLayout1. + + \snippet tutorials/addressbook/part4/addressbook.cpp connecting edit and remove + \dots + \snippet tutorials/addressbook/part4/addressbook.cpp adding edit and remove to the layout + + The \c editContact() function stores the contact's old details in + \c oldName and \c oldAddress, before switching the mode to \c EditingMode. + In this mode, the \c submitButton and \c cancelButton are both enabled, + hence, the user can change the contact's details and click either button. + + \snippet tutorials/addressbook/part4/addressbook.cpp editContact() function + + The \c submitContact() function has been divided in two with an \c{if-else} + statement. We check \c currentMode to see if it's in \c AddingMode. If it is, + we proceed with our adding process. + + \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function beginning + \dots + \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part1 + + Otherwise, we check to see if \c currentMode is in \c EditingMode. If it + is, we compare \c oldName with \c name. If the name has changed, we remove + the old contact from \c contacts and insert the newly updated contact. + + \snippet tutorials/addressbook/part4/addressbook.cpp submitContact() function part2 + + If only the address has changed (i.e., \c oldAddress is not the same as \c address), + we update the contact's address. Lastly, we set \c currentMode to + \c NavigationMode. This is an important step as it re-enables all the + disabled push buttons. + + To remove a contact from the address book, we implement the + \c removeContact() function. This function checks to see if the contact + exists in \c contacts. + + \snippet tutorials/addressbook/part4/addressbook.cpp removeContact() function + + If it does, we display a QMessageBox, to confirm the removal with the + user. Once the user has confirmed, we call \c previous() to ensure that the + user interface shows another contact, and we remove the contact using \l{QMap}'s + \l{QMap::remove()}{remove()} function. As a courtesy, we display a QMessageBox + to inform the user. Both the message boxes used in this function are shown below: + + \image addressbook-tutorial-part4-remove.png + + \section2 Updating the User Interface + + We mentioned the \c updateInterface() function earlier as a means to + enable and disable the push buttons depending on the current mode. + The function updates the current mode according to the \c mode argument + passed to it, assigning it to \c currentMode before checking its value. + + Each of the push buttons is then enabled or disabled, depending on the + current mode. The code for \c AddingMode and \c EditingMode is shown below: + + \snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 1 + + For \c NavigationMode, however, we include conditions within the parameters + of the QPushButton::setEnabled() function. This is to ensure that + \c editButton and \c removeButton are enabled when there is at least one + contact in the address book; \c nextButton and \c previousButton are only + enabled when there is more than one contact in the address book. + + \snippet tutorials/addressbook/part4/addressbook.cpp update interface() part 2 + + By setting the mode and updating the user interface in the same + function, we avoid the possibility of the user interface getting + out of sync with the internal state of the application. + */ + +/*! + \example tutorials/addressbook/part5 + \title Part 5 - Adding a Find Function + \brief Describes how to add a find function. + + Here we look at ways to locate contacts and addresses in the + address book. + + \image addressbook-tutorial-part5-screenshot.png + + As we add contacts to our address book, it becomes tedious to + navigate the list with the \e Next and \e Previous buttons. A \e + Find function would be more efficient. The screenshot above shows + the \e Find button and its position on the panel of buttons. + + When the user clicks on the \e Find button, it is useful to + display a dialog that prompts for a contact's name. Qt provides + QDialog, which we subclass here to implement a \c FindDialog + class. + + \section1 Defining the FindDialog Class + + \image addressbook-tutorial-part5-finddialog.png + + In order to subclass QDialog, we first include the header for QDialog in + the \c finddialog.h file. Also, we use forward declaration to declare + QLineEdit and QPushButton since we will be using those widgets in our + dialog class. + + As in our \c AddressBook class, the \c FindDialog class includes + the Q_OBJECT macro and its constructor is defined to accept a parent + QWidget, even though the dialog will be opened as a separate window. + + \snippet tutorials/addressbook/part5/finddialog.h FindDialog header + + We define a public function, \c getFindText(), to be used by classes that + instantiate \c FindDialog. This function allows these classes to obtain the + search string entered by the user. A public slot, \c findClicked(), is also + defined to handle the search string when the user clicks the \uicontrol Find + button. + + Lastly, we define the private variables, \c findButton, \c lineEdit + and \c findText, corresponding to the \uicontrol Find button, the line edit + into which the user types the search string, and an internal string + used to store the search string for later use. + + \section1 Implementing the FindDialog Class + + Within the constructor of \c FindDialog, we set up the private variables, + \c lineEdit, \c findButton and \c findText. We use a QHBoxLayout to + position the widgets. + + \snippet tutorials/addressbook/part5/finddialog.cpp constructor + + We set the layout and window title, as well as connect the signals to their + respective slots. Notice that \c{findButton}'s \l{QPushButton::clicked()} + {clicked()} signal is connected to \c findClicked() and + \l{QDialog::accept()}{accept()}. The \l{QDialog::accept()}{accept()} slot + provided by QDialog hides the dialog and sets the result code to + \l{QDialog::}{Accepted}. We use this function to help \c{AddressBook}'s + \c findContact() function know when the \c FindDialog object has been + closed. We will explain this logic in further detail when discussing the + \c findContact() function. + + \image addressbook-tutorial-part5-signals-and-slots.png + + In \c findClicked(), we validate \c lineEdit to ensure that the user + did not click the \uicontrol Find button without entering a contact's name. Then, we set + \c findText to the search string, extracted from \c lineEdit. After that, + we clear the contents of \c lineEdit and hide the dialog. + + \snippet tutorials/addressbook/part5/finddialog.cpp findClicked() function + + The \c findText variable has a public getter function, \c getFindText(), + associated with it. Since we only ever set \c findText directly in both the + constructor and in the \c findClicked() function, we do not create a + setter function to accompany \c getFindText(). + Because \c getFindText() is public, classes instantiating and using + \c FindDialog can always access the search string that the user has + entered and accepted. + + \snippet tutorials/addressbook/part5/finddialog.cpp getFindText() function + + \section1 Defining the AddressBook Class + + To ensure we can use \c FindDialog from within our \c AddressBook class, we + include \c finddialog.h in the \c addressbook.h file. + + \snippet tutorials/addressbook/part5/addressbook.h include finddialog's header + + So far, all our address book features have a QPushButton and a + corresponding slot. Similarly, for the \uicontrol Find feature we have + \c findButton and \c findContact(). + + The \c findButton is declared as a private variable and the + \c findContact() function is declared as a public slot. + + \snippet tutorials/addressbook/part5/addressbook.h findContact() declaration + \dots + \snippet tutorials/addressbook/part5/addressbook.h findButton declaration + + Lastly, we declare the private variable, \c dialog, which we will use to + refer to an instance of \c FindDialog. + + \snippet tutorials/addressbook/part5/addressbook.h FindDialog declaration + + Once we have instantiated a dialog, we will want to use it more than once; + using a private variable allows us to refer to it from more than one place + in the class. + + \section1 Implementing the AddressBook Class + + Within the \c AddressBook class's constructor, we instantiate our private + objects, \c findButton and \c findDialog: + + \snippet tutorials/addressbook/part5/addressbook.cpp instantiating findButton + \dots + \snippet tutorials/addressbook/part5/addressbook.cpp instantiating FindDialog + + Next, we connect the \c{findButton}'s + \l{QPushButton::clicked()}{clicked()} signal to \c findContact(). + + \snippet tutorials/addressbook/part5/addressbook.cpp signals and slots for find + + Now all that is left is the code for our \c findContact() function: + + \snippet tutorials/addressbook/part5/addressbook.cpp findContact() function + + We start out by displaying the \c FindDialog instance, \c dialog. This is + when the user enters a contact name to look up. Once the user clicks + the dialog's \c findButton, the dialog is hidden and the result code is + set to QDialog::Accepted. This ensures that + our \c if statement is always true. + + We then proceed to extract the search string, which in this case is + \c contactName, using \c{FindDialog}'s \c getFindText() function. If the + contact exists in our address book, we display it immediately. Otherwise, + we display the QMessageBox shown below to indicate that their search + failed. + + \image addressbook-tutorial-part5-notfound.png +*/ + +/*! + \example tutorials/addressbook/part6 + \title Part 6 - Loading and Saving + \brief Describes how to add save and load functionality. + + This part covers the Qt file handling features we use to write + loading and saving routines for the address book. + + \image addressbook-tutorial-part6-screenshot.png + + Although browsing and searching the contact list are useful + features, our address book is not complete until we can save + existing contacts and load them again at a later time. + + Qt provides a number of classes for \l{Input/Output and Networking} + {input and output}, but we have chosen to use two which are simple to use + in combination: QFile and QDataStream. + + A QFile object represents a file on disk that can be read from and written + to. QFile is a subclass of the more general QIODevice class which + represents many different kinds of devices. + + A QDataStream object is used to serialize binary data so that it can be + stored in a QIODevice and retrieved again later. Reading from a QIODevice + and writing to it is as simple as opening the stream - with the respective + device as a parameter - and reading from or writing to it. + + + \section1 Defining the AddressBook Class + + We declare two public slots, \c saveToFile() and \c loadFromFile(), as well + as two QPushButton objects, \c loadButton and \c saveButton. + + \snippet tutorials/addressbook/part6/addressbook.h save and load functions declaration + \dots + \snippet tutorials/addressbook/part6/addressbook.h save and load buttons declaration + + \section1 Implementing the AddressBook Class + + In our constructor, we instantiate \c loadButton and \c saveButton. + Ideally, it would be more user-friendly to set the push buttons' labels + to "Load contacts from a file" and "Save contacts to a file". However, due + to the size of our other push buttons, we set the labels to \uicontrol{Load...} + and \uicontrol{Save...}. Fortunately, Qt provides a simple way to set tooltips with + \l{QWidget::setToolTip()}{setToolTip()} and we use it in the following way + for our push buttons: + + \snippet tutorials/addressbook/part6/addressbook.cpp tooltip 1 + \dots + \snippet tutorials/addressbook/part6/addressbook.cpp tooltip 2 + + Although it is not shown here, just like the other features we implemented, + we add the push buttons to the layout panel on the right, \c buttonLayout1, + and we connect the push buttons' \l{QPushButton::clicked()}{clicked()} + signals to their respective slots. + + For the saving feature, we first obtain \c fileName using + QFileDialog::getSaveFileName(). This is a convenience function provided + by QFileDialog, which pops up a modal file dialog and allows the user to + enter a file name or select any existing \c{.abk} file. The \c{.abk} file + is our Address Book extension that we create when we save contacts. + + \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part1 + + The file dialog that pops up is displayed in the screenshot below: + + \image addressbook-tutorial-part6-save.png + + If \c fileName is not empty, we create a QFile object, \c file, with + \c fileName. QFile works with QDataStream as QFile is a QIODevice. + + Next, we attempt to open the file in \l{QIODeviceBase::}{WriteOnly} mode. + If this is unsuccessful, we display a QMessageBox to inform the user. + + \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part2 + + Otherwise, we instantiate a QDataStream object, \c out, to write the open + file. QDataStream requires that the same version of the stream is used + for reading and writing. We ensure that this is the case by setting the + version used to the \l{QDataStream::Qt_4_5}{version introduced with Qt 4.5} + before serializing the data to \c file. + + \snippet tutorials/addressbook/part6/addressbook.cpp saveToFile() function part3 + + For the loading feature, we also obtain \c fileName using + QFileDialog::getOpenFileName(). This function, the counterpart to + QFileDialog::getSaveFileName(), also pops up the modal file dialog and + allows the user to enter a file name or select any existing \c{.abk} file + to load it into the address book. + + \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part1 + + On Windows, for example, this function pops up a native file dialog, as + shown in the following screenshot. + + \image addressbook-tutorial-part6-load.png + + If \c fileName is not empty, again, we use a QFile object, \c file, and + attempt to open it in \l{QIODeviceBase::}{ReadOnly} mode. Similar to our + implementation of \c saveToFile(), if this attempt is unsuccessful, we + display a QMessageBox to inform the user. + + \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part2 + + Otherwise, we instantiate a QDataStream object, \c in, set its version as + above and read the serialized data into the \c contacts data structure. + The \c contacts object is emptied before data is read into it to simplify + the file reading process. A more advanced method would be to read the + contacts into a temporary QMap object, and copy over non-duplicate contacts + into \c contacts. + + \snippet tutorials/addressbook/part6/addressbook.cpp loadFromFile() function part3 + + To display the contacts that have been read from the file, we must first + validate the data obtained to ensure that the file we read from actually + contains address book contacts. If it does, we display the first contact; + otherwise, we display a QMessageBox to inform the user about the problem. + Lastly, we update the interface to enable and disable the push buttons + accordingly. +*/ + +/*! + \example tutorials/addressbook/part7 + \title Part 7 - Additional Features + \brief Describes how to export data in VCard format. + + This part covers some additional features that make the address + book more convenient for the frequent user. + + \image addressbook-tutorial-part7-screenshot.png + + Although our address book is useful in isolation, it would be + better if we could exchange contact data with other applications. + The vCard format is a popular file format that can be used for + this purpose. Here we extend our address book client to allow + contacts to be exported to vCard \c{.vcf} files. + + \section1 Defining the AddressBook Class + + We add a QPushButton object, \c exportButton, and a corresponding public + slot, \c exportAsVCard() to our \c AddressBook class in the + \c addressbook.h file. + + \snippet tutorials/addressbook/part7/addressbook.h exportAsVCard() declaration + \dots + \snippet tutorials/addressbook/part7/addressbook.h exportButton declaration + + \section1 Implementing the AddressBook Class + + Within the \c AddressBook constructor, we connect \c{exportButton}'s + \l{QPushButton::clicked()}{clicked()} signal to \c exportAsVCard(). + We also add this button to our \c buttonLayout1, the layout responsible + for our panel of buttons on the right. + + In our \c exportAsVCard() function, we start by extracting the contact's + name into \c name. We declare \c firstName, \c lastName and \c nameList. + Next, we look for the index of the first white space in \c name. If there + is a white space, we split the contact's name into \c firstName and + \c lastName. Then, we replace the space with an underscore ("_"). + Alternately, if there is no white space, we assume that the contact only + has a first name. + + \snippet tutorials/addressbook/part7/addressbook.cpp export function part1 + + As with the \c saveToFile() function, we open a file dialog to let the user + choose a location for the file. Using the file name chosen, we create an + instance of QFile to write to. + + We attempt to open the file in \l{QIODeviceBase::}{WriteOnly} mode. If this + process fails, we display a QMessageBox to inform the user about the + problem and return. Otherwise, we pass the file as a parameter to a + QTextStream object, \c out. Like QDataStream, the QTextStream class + provides functionality to read and write plain text to files. As a result, + the \c{.vcf} file generated can be opened for editing in a text editor. + + \snippet tutorials/addressbook/part7/addressbook.cpp export function part2 + + We then write out a vCard file with the \c{BEGIN:VCARD} tag, followed by + the \c{VERSION:2.1} tag. The contact's name is written with the \c{N:} + tag. For the \c{FN:} tag, which fills in the "File as" property of a vCard, + we have to check whether the contact has a last name or not. If the contact + does, we use the details in \c nameList to fill it. Otherwise, we write + \c firstName only. + + \snippet tutorials/addressbook/part7/addressbook.cpp export function part3 + + We proceed to write the contact's address. The semicolons in the address + are escaped with "\\", the newlines are replaced with semicolons, and the + commas are replaced with spaces. Lastly, we write the \c{ADR;HOME:;} + tag, followed by \c address and then the \c{END:VCARD} tag. + + \snippet tutorials/addressbook/part7/addressbook.cpp export function part4 + + In the end, a QMessageBox is displayed to inform the user that the vCard + has been successfully exported. + + \e{vCard is a trademark of the \l{http://www.imc.org} + {Internet Mail Consortium}}. +*/ diff --git a/tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro b/tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro new file mode 100644 index 0000000000..d31424998e --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/addressbook.pro @@ -0,0 +1,6 @@ +TEMPLATE = subdirs +SUBDIRS = part1 part2 part3 part4 part5 part6 part7 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt new file mode 100644 index 0000000000..12eaccd384 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part1 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part1") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part1 + addressbook.cpp addressbook.h + main.cpp +) + +set_target_properties(part1 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part1 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part1 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp new file mode 100644 index 0000000000..eae818dbbf --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.cpp @@ -0,0 +1,30 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +//! [constructor and input fields] +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; + + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; +//! [constructor and input fields] + +//! [layout] + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); +//! [layout] + +//![setting the layout] + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} +//! [setting the layout] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h new file mode 100644 index 0000000000..f2e28b4135 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/addressbook.h @@ -0,0 +1,29 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +class QTextEdit; +QT_END_NAMESPACE + +//! [class definition] +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); + +private: + QLineEdit *nameLine; + QTextEdit *addressText; +}; +//! [class definition] + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp new file mode 100644 index 0000000000..879fb606a2 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +//! [main function] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} +//! [main function] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro b/tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro new file mode 100644 index 0000000000..35d4a0152e --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part1/part1.pro @@ -0,0 +1,11 @@ +QT += widgets + +SOURCES = addressbook.cpp \ + main.cpp +HEADERS = addressbook.h + +QMAKE_PROJECT_NAME = ab_part1 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part1 +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt new file mode 100644 index 0000000000..0231e7bce8 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part2 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part2") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part2 + addressbook.cpp addressbook.h + main.cpp +) + +set_target_properties(part2 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part2 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part2 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp new file mode 100644 index 0000000000..085103c791 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.cpp @@ -0,0 +1,123 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; +//! [setting readonly 1] + nameLine->setReadOnly(true); +//! [setting readonly 1] + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; +//! [setting readonly 2] + addressText->setReadOnly(true); +//! [setting readonly 2] + +//! [pushbutton declaration] + addButton = new QPushButton(tr("&Add")); + addButton->show(); + submitButton = new QPushButton(tr("&Submit")); + submitButton->hide(); + cancelButton = new QPushButton(tr("&Cancel")); + cancelButton->hide(); +//! [pushbutton declaration] +//! [connecting signals and slots] + connect(addButton, &QPushButton::clicked, + this, &AddressBook::addContact); + connect(submitButton, &QPushButton::clicked, + this, &AddressBook::submitContact); + connect(cancelButton, &QPushButton::clicked, + this, &AddressBook::cancel); +//! [connecting signals and slots] +//! [vertical layout] + QVBoxLayout *buttonLayout1 = new QVBoxLayout; + buttonLayout1->addWidget(addButton, Qt::AlignTop); + buttonLayout1->addWidget(submitButton); + buttonLayout1->addWidget(cancelButton); + buttonLayout1->addStretch(); +//! [vertical layout] +//! [grid layout] + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); + mainLayout->addLayout(buttonLayout1, 1, 2); +//! [grid layout] + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} +//! [addContact] +void AddressBook::addContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + nameLine->clear(); + addressText->clear(); + + nameLine->setReadOnly(false); + nameLine->setFocus(Qt::OtherFocusReason); + addressText->setReadOnly(false); + + addButton->setEnabled(false); + submitButton->show(); + cancelButton->show(); +} +//! [addContact] + +//! [submitContact part1] +void AddressBook::submitContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (name.isEmpty() || address.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name and address.")); + return; + } +//! [submitContact part1] +//! [submitContact part2] + if (!contacts.contains(name)) { + contacts.insert(name, address); + QMessageBox::information(this, tr("Add Successful"), + tr("\"%1\" has been added to your address book.").arg(name)); + } else { + QMessageBox::information(this, tr("Add Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + return; + } +//! [submitContact part2] +//! [submitContact part3] + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + submitButton->hide(); + cancelButton->hide(); +} +//! [submitContact part3] +//! [cancel] +void AddressBook::cancel() +{ + nameLine->setText(oldName); + nameLine->setReadOnly(true); + + addressText->setText(oldAddress); + addressText->setReadOnly(true); + + addButton->setEnabled(true); + submitButton->hide(); + cancelButton->hide(); +} +//! [cancel] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h new file mode 100644 index 0000000000..ecc1a71cee --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/addressbook.h @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> +#include <QMap> + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +class QPushButton; +class QTextEdit; +QT_END_NAMESPACE + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); + +//! [slots] +public slots: + void addContact(); + void submitContact(); + void cancel(); +//! [slots] + +//! [pushbutton declaration] +private: + QPushButton *addButton; + QPushButton *submitButton; + QPushButton *cancelButton; + QLineEdit *nameLine; + QTextEdit *addressText; +//! [pushbutton declaration] + +//! [remaining private variables] + QMap<QString, QString> contacts; + QString oldName; + QString oldAddress; +}; +//! [remaining private variables] + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp new file mode 100644 index 0000000000..879fb606a2 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/main.cpp @@ -0,0 +1,17 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +//! [main function] +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} +//! [main function] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro b/tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro new file mode 100644 index 0000000000..643ffcfebd --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part2/part2.pro @@ -0,0 +1,11 @@ +QT += widgets + +SOURCES = addressbook.cpp \ + main.cpp +HEADERS = addressbook.h + +QMAKE_PROJECT_NAME = ab_part2 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part2 +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt new file mode 100644 index 0000000000..77d8d8a610 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part3 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part3") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part3 + addressbook.cpp addressbook.h + main.cpp +) + +set_target_properties(part3 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part3 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part3 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp new file mode 100644 index 0000000000..1b37e56880 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.cpp @@ -0,0 +1,182 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; + nameLine->setReadOnly(true); + + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; + addressText->setReadOnly(true); + + addButton = new QPushButton(tr("&Add")); + addButton->show(); + submitButton = new QPushButton(tr("&Submit")); + submitButton->hide(); + cancelButton = new QPushButton(tr("&Cancel")); + cancelButton->hide(); +//! [navigation pushbuttons] + nextButton = new QPushButton(tr("&Next")); + nextButton->setEnabled(false); + previousButton = new QPushButton(tr("&Previous")); + previousButton->setEnabled(false); +//! [navigation pushbuttons] + connect(addButton, &QPushButton::clicked, + this, &AddressBook::addContact); + connect(submitButton, &QPushButton::clicked, + this, &AddressBook::submitContact); + connect(cancelButton, &QPushButton::clicked, + this, &AddressBook::cancel); +//! [connecting navigation signals] + connect(nextButton, &QPushButton::clicked, + this, &AddressBook::next); + connect(previousButton, &QPushButton::clicked, + this, &AddressBook::previous); +//! [connecting navigation signals] + + QVBoxLayout *buttonLayout1 = new QVBoxLayout; + buttonLayout1->addWidget(addButton, Qt::AlignTop); + buttonLayout1->addWidget(submitButton); + buttonLayout1->addWidget(cancelButton); + buttonLayout1->addStretch(); +//! [navigation layout] + QHBoxLayout *buttonLayout2 = new QHBoxLayout; + buttonLayout2->addWidget(previousButton); + buttonLayout2->addWidget(nextButton); +//! [ navigation layout] + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); + mainLayout->addLayout(buttonLayout1, 1, 2); +//! [adding navigation layout] + mainLayout->addLayout(buttonLayout2, 2, 1); +//! [adding navigation layout] + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} + +void AddressBook::addContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + nameLine->clear(); + addressText->clear(); + + nameLine->setReadOnly(false); + nameLine->setFocus(Qt::OtherFocusReason); + addressText->setReadOnly(false); + + addButton->setEnabled(false); +//! [disabling navigation] + nextButton->setEnabled(false); + previousButton->setEnabled(false); +//! [disabling navigation] + submitButton->show(); + cancelButton->show(); +} + +void AddressBook::submitContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (name.isEmpty() || address.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name and address.")); + return; + } + + if (!contacts.contains(name)) { + contacts.insert(name, address); + QMessageBox::information(this, tr("Add Successful"), + tr("\"%1\" has been added to your address book.").arg(name)); + } else { + QMessageBox::information(this, tr("Add Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + +//! [enabling navigation] + int number = contacts.size(); + nextButton->setEnabled(number > 1); + previousButton->setEnabled(number > 1); +//! [enabling navigation] + submitButton->hide(); + cancelButton->hide(); +} + +void AddressBook::cancel() +{ + nameLine->setText(oldName); + addressText->setText(oldAddress); + + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + + int number = contacts.size(); + nextButton->setEnabled(number > 1); + previousButton->setEnabled(number > 1); + + submitButton->hide(); + cancelButton->hide(); +} + +//! [next() function] +void AddressBook::next() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i != contacts.end()) + i++; + + if (i == contacts.end()) + i = contacts.begin(); + + nameLine->setText(i.key()); + addressText->setText(i.value()); +} +//! [next() function] +//! [previous() function] +void AddressBook::previous() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i == contacts.end()){ + nameLine->clear(); + addressText->clear(); + return; + } + + if (i == contacts.begin()) + i = contacts.end(); + + i--; + nameLine->setText(i.key()); + addressText->setText(i.value()); +} +//! [previous() function] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h new file mode 100644 index 0000000000..0e3aea1e05 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/addressbook.h @@ -0,0 +1,49 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> +#include <QMap> + +QT_BEGIN_NAMESPACE +class QLabel; +class QLineEdit; +class QPushButton; +class QTextEdit; +QT_END_NAMESPACE + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); + +public slots: + void addContact(); + void submitContact(); + void cancel(); +//! [navigation functions] + void next(); + void previous(); +//! [navigation functions] + +private: + QPushButton *addButton; + QPushButton *submitButton; + QPushButton *cancelButton; +//! [navigation pushbuttons] + QPushButton *nextButton; + QPushButton *previousButton; +//! [navigation pushbuttons] + QLineEdit *nameLine; + QTextEdit *addressText; + + QMap<QString, QString> contacts; + QString oldName; + QString oldAddress; +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp new file mode 100644 index 0000000000..1f3aac3397 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/main.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro b/tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro new file mode 100644 index 0000000000..3bacdd9501 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part3/part3.pro @@ -0,0 +1,11 @@ +QT += widgets + +SOURCES = addressbook.cpp \ + main.cpp +HEADERS = addressbook.h + +QMAKE_PROJECT_NAME = ab_part3 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part3 +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt new file mode 100644 index 0000000000..eb136dbf10 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part4 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part4") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part4 + addressbook.cpp addressbook.h + main.cpp +) + +set_target_properties(part4 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part4 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part4 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp new file mode 100644 index 0000000000..a54a888073 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.cpp @@ -0,0 +1,258 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; + nameLine->setReadOnly(true); + + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; + addressText->setReadOnly(true); + + addButton = new QPushButton(tr("&Add")); +//! [edit and remove buttons] + editButton = new QPushButton(tr("&Edit")); + editButton->setEnabled(false); + removeButton = new QPushButton(tr("&Remove")); + removeButton->setEnabled(false); +//! [edit and remove buttons] + submitButton = new QPushButton(tr("&Submit")); + submitButton->hide(); + cancelButton = new QPushButton(tr("&Cancel")); + cancelButton->hide(); + + nextButton = new QPushButton(tr("&Next")); + nextButton->setEnabled(false); + previousButton = new QPushButton(tr("&Previous")); + previousButton->setEnabled(false); + + connect(addButton, &QPushButton::clicked, + this, &AddressBook::addContact); + connect(submitButton, &QPushButton::clicked, + this, &AddressBook::submitContact); +//! [connecting edit and remove] + connect(editButton, &QPushButton::clicked, + this, &AddressBook::editContact); + connect(removeButton, &QPushButton::clicked, + this, &AddressBook::removeContact); +//! [connecting edit and remove] + connect(cancelButton, &QPushButton::clicked, + this, &AddressBook::cancel); + connect(nextButton, &QPushButton::clicked, + this, &AddressBook::next); + connect(previousButton, &QPushButton::clicked, + this, &AddressBook::previous); + + QVBoxLayout *buttonLayout1 = new QVBoxLayout; + buttonLayout1->addWidget(addButton); +//! [adding edit and remove to the layout] + buttonLayout1->addWidget(editButton); + buttonLayout1->addWidget(removeButton); +//! [adding edit and remove to the layout] + buttonLayout1->addWidget(submitButton); + buttonLayout1->addWidget(cancelButton); + buttonLayout1->addStretch(); + + QHBoxLayout *buttonLayout2 = new QHBoxLayout; + buttonLayout2->addWidget(previousButton); + buttonLayout2->addWidget(nextButton); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); + mainLayout->addLayout(buttonLayout1, 1, 2); + mainLayout->addLayout(buttonLayout2, 2, 1); + + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} + +void AddressBook::addContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + nameLine->clear(); + addressText->clear(); + + updateInterface(AddingMode); +} +//! [editContact() function] +void AddressBook::editContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + updateInterface(EditingMode); +} +//! [editContact() function] +//! [submitContact() function beginning] +void AddressBook::submitContact() +{ +//! [submitContact() function beginning] + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (name.isEmpty() || address.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name and address.")); + return; + } +//! [submitContact() function part1] + if (currentMode == AddingMode) { + + if (!contacts.contains(name)) { + contacts.insert(name, address); + QMessageBox::information(this, tr("Add Successful"), + tr("\"%1\" has been added to your address book.").arg(name)); + } else { + QMessageBox::information(this, tr("Add Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } +//! [submitContact() function part1] +//! [submitContact() function part2] + } else if (currentMode == EditingMode) { + + if (oldName != name) { + if (!contacts.contains(name)) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(oldName)); + contacts.remove(oldName); + contacts.insert(name, address); + } else { + QMessageBox::information(this, tr("Edit Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (oldAddress != address) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(name)); + contacts[name] = address; + } + } + + updateInterface(NavigationMode); +} +//! [submitContact() function part2] + +void AddressBook::cancel() +{ + nameLine->setText(oldName); + addressText->setText(oldAddress); + updateInterface(NavigationMode); +} +//! [removeContact() function] +void AddressBook::removeContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (contacts.contains(name)) { + + int button = QMessageBox::question(this, + tr("Confirm Remove"), + tr("Are you sure you want to remove \"%1\"?").arg(name), + QMessageBox::Yes | QMessageBox::No); + + if (button == QMessageBox::Yes) { + + previous(); + contacts.remove(name); + + QMessageBox::information(this, tr("Remove Successful"), + tr("\"%1\" has been removed from your address book.").arg(name)); + } + } + + updateInterface(NavigationMode); +} +//! [removeContact() function] +void AddressBook::next() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i != contacts.end()) + i++; + + if (i == contacts.end()) + i = contacts.begin(); + + nameLine->setText(i.key()); + addressText->setText(i.value()); +} + +void AddressBook::previous() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i == contacts.end()) { + nameLine->clear(); + addressText->clear(); + return; + } + + if (i == contacts.begin()) + i = contacts.end(); + + i--; + nameLine->setText(i.key()); + addressText->setText(i.value()); +} +//! [update interface() part 1] +void AddressBook::updateInterface(Mode mode) +{ + currentMode = mode; + + switch (currentMode) { + + case AddingMode: + case EditingMode: + + nameLine->setReadOnly(false); + nameLine->setFocus(Qt::OtherFocusReason); + addressText->setReadOnly(false); + + addButton->setEnabled(false); + editButton->setEnabled(false); + removeButton->setEnabled(false); + + nextButton->setEnabled(false); + previousButton->setEnabled(false); + + submitButton->show(); + cancelButton->show(); + break; +//! [update interface() part 1] +//! [update interface() part 2] + case NavigationMode: + + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + + int number = contacts.size(); + editButton->setEnabled(number >= 1); + removeButton->setEnabled(number >= 1); + nextButton->setEnabled(number > 1); + previousButton->setEnabled(number >1 ); + + submitButton->hide(); + cancelButton->hide(); + break; + } +} +//! [update interface() part 2] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h new file mode 100644 index 0000000000..e77bbd3961 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/addressbook.h @@ -0,0 +1,62 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> +#include <QMap> + +QT_BEGIN_NAMESPACE +class QPushButton; +class QLabel; +class QLineEdit; +class QTextEdit; +QT_END_NAMESPACE + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); +//! [Mode enum] + enum Mode { NavigationMode, AddingMode, EditingMode }; +//! [Mode enum] + +public slots: + void addContact(); + void submitContact(); + void cancel(); +//! [edit and remove slots] + void editContact(); + void removeContact(); +//! [edit and remove slots] + void next(); + void previous(); + +private: +//! [updateInterface() declaration] + void updateInterface(Mode mode); +//! [updateInterface() declaration] + QPushButton *addButton; +//! [buttons declaration] + QPushButton *editButton; + QPushButton *removeButton; +//! [buttons declaration] + QPushButton *submitButton; + QPushButton *cancelButton; + QPushButton *nextButton; + QPushButton *previousButton; + QLineEdit *nameLine; + QTextEdit *addressText; + + QMap<QString, QString> contacts; + QString oldName; + QString oldAddress; +//! [mode declaration] + Mode currentMode; +//! [mode declaration] +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp new file mode 100644 index 0000000000..1f3aac3397 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/main.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro b/tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro new file mode 100644 index 0000000000..02cc5b8e07 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part4/part4.pro @@ -0,0 +1,11 @@ +QT += widgets + +SOURCES = addressbook.cpp \ + main.cpp +HEADERS = addressbook.h + +QMAKE_PROJECT_NAME = ab_part4 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part4 +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt new file mode 100644 index 0000000000..4c6ec27fae --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part5 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part5") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part5 + addressbook.cpp addressbook.h + finddialog.cpp finddialog.h + main.cpp +) + +set_target_properties(part5 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part5 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part5 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp new file mode 100644 index 0000000000..52aa5a0b28 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.cpp @@ -0,0 +1,283 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; + nameLine->setReadOnly(true); + + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; + addressText->setReadOnly(true); + + addButton = new QPushButton(tr("&Add")); + + editButton = new QPushButton(tr("&Edit")); + editButton->setEnabled(false); + removeButton = new QPushButton(tr("&Remove")); + removeButton->setEnabled(false); +//! [instantiating findButton] + findButton = new QPushButton(tr("&Find")); + findButton->setEnabled(false); +//! [instantiating findButton] + submitButton = new QPushButton(tr("&Submit")); + submitButton->hide(); + cancelButton = new QPushButton(tr("&Cancel")); + cancelButton->hide(); + + nextButton = new QPushButton(tr("&Next")); + nextButton->setEnabled(false); + previousButton = new QPushButton(tr("&Previous")); + previousButton->setEnabled(false); + +//! [instantiating FindDialog] + dialog = new FindDialog(this); +//! [instantiating FindDialog] + + connect(addButton, &QPushButton::clicked, + this, &AddressBook::addContact); + connect(submitButton, &QPushButton::clicked, + this, &AddressBook::submitContact); + connect(editButton, &QPushButton::clicked, + this, &AddressBook::editContact); + connect(removeButton, &QPushButton::clicked, + this, &AddressBook::removeContact); + connect(cancelButton, &QPushButton::clicked, + this, &AddressBook::cancel); +//! [signals and slots for find] + connect(findButton, &QPushButton::clicked, + this, &AddressBook::findContact); +//! [signals and slots for find] + connect(nextButton, &QPushButton::clicked, + this, &AddressBook::next); + connect(previousButton, &QPushButton::clicked, + this, &AddressBook::previous); + + QVBoxLayout *buttonLayout1 = new QVBoxLayout; + buttonLayout1->addWidget(addButton); + buttonLayout1->addWidget(editButton); + buttonLayout1->addWidget(removeButton); +//! [adding findButton to layout] + buttonLayout1->addWidget(findButton); +//! [adding findButton to layout] + buttonLayout1->addWidget(submitButton); + buttonLayout1->addWidget(cancelButton); + buttonLayout1->addStretch(); + + QHBoxLayout *buttonLayout2 = new QHBoxLayout; + buttonLayout2->addWidget(previousButton); + buttonLayout2->addWidget(nextButton); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); + mainLayout->addLayout(buttonLayout1, 1, 2); + mainLayout->addLayout(buttonLayout2, 2, 1); + + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} + +void AddressBook::addContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + nameLine->clear(); + addressText->clear(); + + updateInterface(AddingMode); +} + +void AddressBook::editContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + updateInterface(EditingMode); +} + +void AddressBook::submitContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (name.isEmpty() || address.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name and address.")); + return; + } + + if (currentMode == AddingMode) { + + if (!contacts.contains(name)) { + contacts.insert(name, address); + QMessageBox::information(this, tr("Add Successful"), + tr("\"%1\" has been added to your address book.").arg(name)); + } else { + QMessageBox::information(this, tr("Add Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (currentMode == EditingMode) { + + if (oldName != name) { + if (!contacts.contains(name)) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(oldName)); + contacts.remove(oldName); + contacts.insert(name, address); + } else { + QMessageBox::information(this, tr("Edit Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (oldAddress != address) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(name)); + contacts[name] = address; + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::cancel() +{ + nameLine->setText(oldName); + addressText->setText(oldAddress); + updateInterface(NavigationMode); +} + +void AddressBook::removeContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (contacts.contains(name)) { + + int button = QMessageBox::question(this, + tr("Confirm Remove"), + tr("Are you sure you want to remove \"%1\"?").arg(name), + QMessageBox::Yes | QMessageBox::No); + + if (button == QMessageBox::Yes) { + + previous(); + contacts.remove(name); + + QMessageBox::information(this, tr("Remove Successful"), + tr("\"%1\" has been removed from your address book.").arg(name)); + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::next() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i != contacts.end()) + i++; + + if (i == contacts.end()) + i = contacts.begin(); + + nameLine->setText(i.key()); + addressText->setText(i.value()); +} + +void AddressBook::previous() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i == contacts.end()) { + nameLine->clear(); + addressText->clear(); + return; + } + + if (i == contacts.begin()) + i = contacts.end(); + + i--; + nameLine->setText(i.key()); + addressText->setText(i.value()); +} +//! [findContact() function] +void AddressBook::findContact() +{ + dialog->show(); + + if (dialog->exec() == QDialog::Accepted) { + QString contactName = dialog->getFindText(); + + if (contacts.contains(contactName)) { + nameLine->setText(contactName); + addressText->setText(contacts.value(contactName)); + } else { + QMessageBox::information(this, tr("Contact Not Found"), + tr("Sorry, \"%1\" is not in your address book.").arg(contactName)); + return; + } + } + + updateInterface(NavigationMode); +} +//! [findContact() function] + +void AddressBook::updateInterface(Mode mode) +{ + currentMode = mode; + + switch (currentMode) { + + case AddingMode: + case EditingMode: + + nameLine->setReadOnly(false); + nameLine->setFocus(Qt::OtherFocusReason); + addressText->setReadOnly(false); + + addButton->setEnabled(false); + editButton->setEnabled(false); + removeButton->setEnabled(false); + + nextButton->setEnabled(false); + previousButton->setEnabled(false); + + submitButton->show(); + cancelButton->show(); + break; + + case NavigationMode: + + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + + int number = contacts.size(); + editButton->setEnabled(number >= 1); + removeButton->setEnabled(number >= 1); + findButton->setEnabled(number > 2); + nextButton->setEnabled(number > 1); + previousButton->setEnabled(number > 1); + + submitButton->hide(); + cancelButton->hide(); + break; + } +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h new file mode 100644 index 0000000000..93da08038c --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/addressbook.h @@ -0,0 +1,65 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> +#include <QMap> +//! [include finddialog's header] +#include "finddialog.h" +//! [include finddialog's header] +QT_BEGIN_NAMESPACE +class QPushButton; +class QLabel; +class QLineEdit; +class QTextEdit; +QT_END_NAMESPACE + + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); + enum Mode { NavigationMode, AddingMode, EditingMode }; + +public slots: + void addContact(); + void editContact(); + void submitContact(); + void cancel(); + void removeContact(); +//! [findContact() declaration] + void findContact(); +//! [findContact() declaration] + void next(); + void previous(); + +private: + void updateInterface(Mode mode); + + QPushButton *addButton; + QPushButton *editButton; + QPushButton *removeButton; +//! [findButton declaration] + QPushButton *findButton; +//! [findButton declaration] + QPushButton *submitButton; + QPushButton *cancelButton; + QPushButton *nextButton; + QPushButton *previousButton; + QLineEdit *nameLine; + QTextEdit *addressText; + + QMap<QString, QString> contacts; +//! [FindDialog declaration] + FindDialog *dialog; +//! [FindDialog declaration] + QString oldName; + QString oldAddress; + Mode currentMode; +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp new file mode 100644 index 0000000000..d5daa661d0 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "finddialog.h" + +//! [constructor] +FindDialog::FindDialog(QWidget *parent) + : QDialog(parent) +{ + QLabel *findLabel = new QLabel(tr("Enter the name of a contact:")); + lineEdit = new QLineEdit; + + findButton = new QPushButton(tr("&Find")); + findText = ""; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(findLabel); + layout->addWidget(lineEdit); + layout->addWidget(findButton); + + setLayout(layout); + setWindowTitle(tr("Find a Contact")); + connect(findButton, &QPushButton::clicked, + this, &FindDialog::findClicked); + connect(findButton, &QPushButton::clicked, + this, &FindDialog::accept); +} +//! [constructor] +//! [findClicked() function] +void FindDialog::findClicked() +{ + QString text = lineEdit->text(); + + if (text.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name.")); + return; + } else { + findText = text; + lineEdit->clear(); + hide(); + } +} +//! [findClicked() function] +//! [getFindText() function] +QString FindDialog::getFindText() +{ + return findText; +} +//! [getFindText() function] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h new file mode 100644 index 0000000000..7cedcffa60 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/finddialog.h @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef FINDDIALOG_H +#define FINDDIALOG_H +//! [FindDialog header] +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QLineEdit; +class QPushButton; +QT_END_NAMESPACE + +class FindDialog : public QDialog +{ + Q_OBJECT + +public: + FindDialog(QWidget *parent = nullptr); + QString getFindText(); + +public slots: + void findClicked(); + +private: + QPushButton *findButton; + QLineEdit *lineEdit; + QString findText; +}; +//! [FindDialog header] +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp new file mode 100644 index 0000000000..1f3aac3397 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/main.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro b/tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro new file mode 100644 index 0000000000..da5469f655 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part5/part5.pro @@ -0,0 +1,13 @@ +QT += widgets + +SOURCES = addressbook.cpp \ + finddialog.cpp \ + main.cpp +HEADERS = addressbook.h \ + finddialog.h + +QMAKE_PROJECT_NAME = ab_part5 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part5 +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt new file mode 100644 index 0000000000..007e0c5171 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part6 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part6") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part6 + addressbook.cpp addressbook.h + finddialog.cpp finddialog.h + main.cpp +) + +set_target_properties(part6 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part6 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part6 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp new file mode 100644 index 0000000000..455ccc899a --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.cpp @@ -0,0 +1,366 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; + nameLine->setReadOnly(true); + + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; + addressText->setReadOnly(true); + + addButton = new QPushButton(tr("&Add")); + + editButton = new QPushButton(tr("&Edit")); + editButton->setEnabled(false); + removeButton = new QPushButton(tr("&Remove")); + removeButton->setEnabled(false); + findButton = new QPushButton(tr("&Find")); + findButton->setEnabled(false); + submitButton = new QPushButton(tr("&Submit")); + submitButton->hide(); + cancelButton = new QPushButton(tr("&Cancel")); + cancelButton->hide(); + + nextButton = new QPushButton(tr("&Next")); + nextButton->setEnabled(false); + previousButton = new QPushButton(tr("&Previous")); + previousButton->setEnabled(false); + + loadButton = new QPushButton(tr("&Load...")); +//! [tooltip 1] + loadButton->setToolTip(tr("Load contacts from a file")); +//! [tooltip 1] + saveButton = new QPushButton(tr("&Save...")); +//! [tooltip 2] + saveButton->setToolTip(tr("Save contacts to a file")); +//! [tooltip 2] + saveButton->setEnabled(false); + + dialog = new FindDialog(this); + + connect(addButton, &QPushButton::clicked, + this, &AddressBook::addContact); + connect(submitButton, &QPushButton::clicked, + this, &AddressBook::submitContact); + connect(editButton, &QPushButton::clicked, + this, &AddressBook::editContact); + connect(removeButton, &QPushButton::clicked, + this, &AddressBook::removeContact); + connect(cancelButton, &QPushButton::clicked, + this, &AddressBook::cancel); + connect(findButton, &QPushButton::clicked, + this, &AddressBook::findContact); + connect(nextButton, &QPushButton::clicked, + this, &AddressBook::next); + connect(previousButton, &QPushButton::clicked, + this, &AddressBook::previous); + connect(loadButton, &QPushButton::clicked, + this, &AddressBook::loadFromFile); + connect(saveButton, &QPushButton::clicked, + this, &AddressBook::saveToFile); + + QVBoxLayout *buttonLayout1 = new QVBoxLayout; + buttonLayout1->addWidget(addButton); + buttonLayout1->addWidget(editButton); + buttonLayout1->addWidget(removeButton); + buttonLayout1->addWidget(findButton); + buttonLayout1->addWidget(submitButton); + buttonLayout1->addWidget(cancelButton); + buttonLayout1->addWidget(loadButton); + buttonLayout1->addWidget(saveButton); + buttonLayout1->addStretch(); + + QHBoxLayout *buttonLayout2 = new QHBoxLayout; + buttonLayout2->addWidget(previousButton); + buttonLayout2->addWidget(nextButton); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); + mainLayout->addLayout(buttonLayout1, 1, 2); + mainLayout->addLayout(buttonLayout2, 2, 1); + + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} + +void AddressBook::addContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + nameLine->clear(); + addressText->clear(); + + updateInterface(AddingMode); +} + +void AddressBook::editContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + updateInterface(EditingMode); +} + +void AddressBook::submitContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (name.isEmpty() || address.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name and address.")); + return; + } + + if (currentMode == AddingMode) { + + if (!contacts.contains(name)) { + contacts.insert(name, address); + QMessageBox::information(this, tr("Add Successful"), + tr("\"%1\" has been added to your address book.").arg(name)); + } else { + QMessageBox::information(this, tr("Add Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (currentMode == EditingMode) { + + if (oldName != name) { + if (!contacts.contains(name)) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(oldName)); + contacts.remove(oldName); + contacts.insert(name, address); + } else { + QMessageBox::information(this, tr("Edit Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (oldAddress != address) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(name)); + contacts[name] = address; + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::cancel() +{ + nameLine->setText(oldName); + addressText->setText(oldAddress); + updateInterface(NavigationMode); +} + +void AddressBook::removeContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (contacts.contains(name)) { + + int button = QMessageBox::question(this, + tr("Confirm Remove"), + tr("Are you sure you want to remove \"%1\"?").arg(name), + QMessageBox::Yes | QMessageBox::No); + + if (button == QMessageBox::Yes) { + + previous(); + contacts.remove(name); + + QMessageBox::information(this, tr("Remove Successful"), + tr("\"%1\" has been removed from your address book.").arg(name)); + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::next() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i != contacts.end()) + i++; + + if (i == contacts.end()) + i = contacts.begin(); + + nameLine->setText(i.key()); + addressText->setText(i.value()); +} + +void AddressBook::previous() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i == contacts.end()) { + nameLine->clear(); + addressText->clear(); + return; + } + + if (i == contacts.begin()) + i = contacts.end(); + + i--; + nameLine->setText(i.key()); + addressText->setText(i.value()); +} + +void AddressBook::findContact() +{ + dialog->show(); + + if (dialog->exec() == 1) { + QString contactName = dialog->getFindText(); + + if (contacts.contains(contactName)) { + nameLine->setText(contactName); + addressText->setText(contacts.value(contactName)); + } else { + QMessageBox::information(this, tr("Contact Not Found"), + tr("Sorry, \"%1\" is not in your address book.").arg(contactName)); + return; + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::updateInterface(Mode mode) +{ + currentMode = mode; + + switch (currentMode) { + + case AddingMode: + case EditingMode: + + nameLine->setReadOnly(false); + nameLine->setFocus(Qt::OtherFocusReason); + addressText->setReadOnly(false); + + addButton->setEnabled(false); + editButton->setEnabled(false); + removeButton->setEnabled(false); + + nextButton->setEnabled(false); + previousButton->setEnabled(false); + + submitButton->show(); + cancelButton->show(); + + loadButton->setEnabled(false); + saveButton->setEnabled(false); + break; + + case NavigationMode: + + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + + int number = contacts.size(); + editButton->setEnabled(number >= 1); + removeButton->setEnabled(number >= 1); + findButton->setEnabled(number > 2); + nextButton->setEnabled(number > 1); + previousButton->setEnabled(number > 1); + + submitButton->hide(); + cancelButton->hide(); + + loadButton->setEnabled(true); + saveButton->setEnabled(number >= 1); + break; + } +} + +//! [saveToFile() function part1] +void AddressBook::saveToFile() +{ + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save Address Book"), "", + tr("Address Book (*.abk);;All Files (*)")); + +//! [saveToFile() function part1] +//! [saveToFile() function part2] + if (fileName.isEmpty()) + return; + else { + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("Unable to open file"), + file.errorString()); + return; + } + +//! [saveToFile() function part2] +//! [saveToFile() function part3] + QDataStream out(&file); + out.setVersion(QDataStream::Qt_4_5); + out << contacts; + } +} +//! [saveToFile() function part3] + +//! [loadFromFile() function part1] +void AddressBook::loadFromFile() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Address Book"), "", + tr("Address Book (*.abk);;All Files (*)")); +//! [loadFromFile() function part1] + +//! [loadFromFile() function part2] + if (fileName.isEmpty()) + return; + else { + + QFile file(fileName); + + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::information(this, tr("Unable to open file"), + file.errorString()); + return; + } + + QDataStream in(&file); + in.setVersion(QDataStream::Qt_4_5); + contacts.clear(); // clear existing contacts + in >> contacts; +//! [loadFromFile() function part2] + +//! [loadFromFile() function part3] + if (contacts.isEmpty()) { + QMessageBox::information(this, tr("No contacts in file"), + tr("The file you are attempting to open contains no contacts.")); + } else { + QMap<QString, QString>::iterator i = contacts.begin(); + nameLine->setText(i.key()); + addressText->setText(i.value()); + } + } + + updateInterface(NavigationMode); +} +//! [loadFromFile() function part3] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h new file mode 100644 index 0000000000..26389d7446 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/addressbook.h @@ -0,0 +1,66 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> +#include <QMap> +#include "finddialog.h" + +QT_BEGIN_NAMESPACE +class QPushButton; +class QLabel; +class QLineEdit; +class QTextEdit; +QT_END_NAMESPACE + + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); + enum Mode { NavigationMode, AddingMode, EditingMode }; + +public slots: + void addContact(); + void editContact(); + void submitContact(); + void cancel(); + void removeContact(); + void findContact(); + void next(); + void previous(); +//! [save and load functions declaration] + void saveToFile(); + void loadFromFile(); +//! [save and load functions declaration] + +private: + void updateInterface(Mode mode); + + QPushButton *addButton; + QPushButton *editButton; + QPushButton *removeButton; + QPushButton *findButton; + QPushButton *submitButton; + QPushButton *cancelButton; + QPushButton *nextButton; + QPushButton *previousButton; +//! [save and load buttons declaration] + QPushButton *loadButton; + QPushButton *saveButton; +//! [save and load buttons declaration] + QLineEdit *nameLine; + QTextEdit *addressText; + + QMap<QString, QString> contacts; + FindDialog *dialog; + QString oldName; + QString oldAddress; + Mode currentMode; +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp new file mode 100644 index 0000000000..90729d9c12 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "finddialog.h" + +FindDialog::FindDialog(QWidget *parent) + : QDialog(parent) +{ + QLabel *findLabel = new QLabel(tr("Enter the name of a contact:")); + lineEdit = new QLineEdit; + + findButton = new QPushButton(tr("&Find")); + findText = ""; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(findLabel); + layout->addWidget(lineEdit); + layout->addWidget(findButton); + + setLayout(layout); + setWindowTitle(tr("Find a Contact")); + connect(findButton, &QPushButton::clicked, + this, &FindDialog::findClicked); + connect(findButton, &QPushButton::clicked, + this, &FindDialog::accept); +} + +void FindDialog::findClicked() +{ + QString text = lineEdit->text(); + + if (text.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name.")); + return; + } else { + findText = text; + lineEdit->clear(); + hide(); + } +} + +QString FindDialog::getFindText() +{ + return findText; +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h new file mode 100644 index 0000000000..7c9a3af30f --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/finddialog.h @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef FINDDIALOG_H +#define FINDDIALOG_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QLineEdit; +class QPushButton; +QT_END_NAMESPACE + +class FindDialog : public QDialog +{ + Q_OBJECT + +public: + FindDialog(QWidget *parent = nullptr); + QString getFindText(); + +public slots: + void findClicked(); + +private: + QPushButton *findButton; + QLineEdit *lineEdit; + QString findText; +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp new file mode 100644 index 0000000000..1f3aac3397 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/main.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro b/tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro new file mode 100644 index 0000000000..6796f30a73 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part6/part6.pro @@ -0,0 +1,14 @@ +QT += widgets +requires(qtConfig(filedialog)) + +SOURCES = addressbook.cpp \ + finddialog.cpp \ + main.cpp +HEADERS = addressbook.h \ + finddialog.h + +QMAKE_PROJECT_NAME = ab_part6 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part6 +INSTALLS += target diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt b/tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt new file mode 100644 index 0000000000..f480e22c21 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +cmake_minimum_required(VERSION 3.16) +project(part7 LANGUAGES CXX) + +if(NOT DEFINED INSTALL_EXAMPLESDIR) + set(INSTALL_EXAMPLESDIR "examples") +endif() + +set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/tutorials/addressbook/part7") + +find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) + +qt_standard_project_setup() + +qt_add_executable(part7 + addressbook.cpp addressbook.h + finddialog.cpp finddialog.h + main.cpp +) + +set_target_properties(part7 PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(part7 PRIVATE + Qt6::Core + Qt6::Gui + Qt6::Widgets +) + +install(TARGETS part7 + RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" + BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" + LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" +) diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp new file mode 100644 index 0000000000..30878d7bbc --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.cpp @@ -0,0 +1,419 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +AddressBook::AddressBook(QWidget *parent) + : QWidget(parent) +{ + QLabel *nameLabel = new QLabel(tr("Name:")); + nameLine = new QLineEdit; + nameLine->setReadOnly(true); + + QLabel *addressLabel = new QLabel(tr("Address:")); + addressText = new QTextEdit; + addressText->setReadOnly(true); + + addButton = new QPushButton(tr("&Add")); + + editButton = new QPushButton(tr("&Edit")); + editButton->setEnabled(false); + removeButton = new QPushButton(tr("&Remove")); + removeButton->setEnabled(false); + findButton = new QPushButton(tr("&Find")); + findButton->setEnabled(false); + submitButton = new QPushButton(tr("&Submit")); + submitButton->hide(); + cancelButton = new QPushButton(tr("&Cancel")); + cancelButton->hide(); + + nextButton = new QPushButton(tr("&Next")); + nextButton->setEnabled(false); + previousButton = new QPushButton(tr("&Previous")); + previousButton->setEnabled(false); + + loadButton = new QPushButton(tr("&Load...")); + loadButton->setToolTip(tr("Load contacts from a file")); + saveButton = new QPushButton(tr("&Save...")); + saveButton->setToolTip(tr("Save contacts to a file")); + saveButton->setEnabled(false); + + exportButton = new QPushButton(tr("E&xport")); + exportButton->setToolTip(tr("Export as vCard")); + exportButton->setEnabled(false); + + dialog = new FindDialog(this); + + connect(addButton, &QPushButton::clicked, + this, &AddressBook::addContact); + connect(submitButton, &QPushButton::clicked, + this, &AddressBook::submitContact); + connect(editButton, &QPushButton::clicked, + this, &AddressBook::editContact); + connect(removeButton, &QPushButton::clicked, + this, &AddressBook::removeContact); + connect(cancelButton, &QPushButton::clicked, + this, &AddressBook::cancel); + connect(findButton, &QPushButton::clicked, + this, &AddressBook::findContact); + connect(nextButton, &QPushButton::clicked, + this, &AddressBook::next); + connect(previousButton, &QPushButton::clicked, + this, &AddressBook::previous); + connect(loadButton, &QPushButton::clicked, + this, &AddressBook::loadFromFile); + connect(saveButton, &QPushButton::clicked, + this, &AddressBook::saveToFile); + connect(exportButton, &QPushButton::clicked, + this, &AddressBook::exportAsVCard); + + QVBoxLayout *buttonLayout1 = new QVBoxLayout; + buttonLayout1->addWidget(addButton); + buttonLayout1->addWidget(editButton); + buttonLayout1->addWidget(removeButton); + buttonLayout1->addWidget(findButton); + buttonLayout1->addWidget(submitButton); + buttonLayout1->addWidget(cancelButton); + buttonLayout1->addWidget(loadButton); + buttonLayout1->addWidget(saveButton); + buttonLayout1->addWidget(exportButton); + buttonLayout1->addStretch(); + + QHBoxLayout *buttonLayout2 = new QHBoxLayout; + buttonLayout2->addWidget(previousButton); + buttonLayout2->addWidget(nextButton); + + QGridLayout *mainLayout = new QGridLayout; + mainLayout->addWidget(nameLabel, 0, 0); + mainLayout->addWidget(nameLine, 0, 1); + mainLayout->addWidget(addressLabel, 1, 0, Qt::AlignTop); + mainLayout->addWidget(addressText, 1, 1); + mainLayout->addLayout(buttonLayout1, 1, 2); + mainLayout->addLayout(buttonLayout2, 2, 1); + + setLayout(mainLayout); + setWindowTitle(tr("Simple Address Book")); +} + +void AddressBook::addContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + nameLine->clear(); + addressText->clear(); + + updateInterface(AddingMode); +} + +void AddressBook::editContact() +{ + oldName = nameLine->text(); + oldAddress = addressText->toPlainText(); + + updateInterface(EditingMode); +} + +void AddressBook::submitContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (name.isEmpty() || address.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name and address.")); + return; + } + + if (currentMode == AddingMode) { + + if (!contacts.contains(name)) { + contacts.insert(name, address); + QMessageBox::information(this, tr("Add Successful"), + tr("\"%1\" has been added to your address book.").arg(name)); + } else { + QMessageBox::information(this, tr("Add Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (currentMode == EditingMode) { + + if (oldName != name) { + if (!contacts.contains(name)) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(oldName)); + contacts.remove(oldName); + contacts.insert(name, address); + } else { + QMessageBox::information(this, tr("Edit Unsuccessful"), + tr("Sorry, \"%1\" is already in your address book.").arg(name)); + } + } else if (oldAddress != address) { + QMessageBox::information(this, tr("Edit Successful"), + tr("\"%1\" has been edited in your address book.").arg(name)); + contacts[name] = address; + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::cancel() +{ + nameLine->setText(oldName); + addressText->setText(oldAddress); + updateInterface(NavigationMode); +} + +void AddressBook::removeContact() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + + if (contacts.contains(name)) { + + int button = QMessageBox::question(this, + tr("Confirm Remove"), + tr("Are you sure you want to remove \"%1\"?").arg(name), + QMessageBox::Yes | QMessageBox::No); + + if (button == QMessageBox::Yes) { + + previous(); + contacts.remove(name); + + QMessageBox::information(this, tr("Remove Successful"), + tr("\"%1\" has been removed from your address book.").arg(name)); + } + } + + updateInterface(NavigationMode); +} + +void AddressBook::next() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i != contacts.end()) + i++; + + if (i == contacts.end()) + i = contacts.begin(); + + nameLine->setText(i.key()); + addressText->setText(i.value()); +} + +void AddressBook::previous() +{ + QString name = nameLine->text(); + QMap<QString, QString>::iterator i = contacts.find(name); + + if (i == contacts.end()) { + nameLine->clear(); + addressText->clear(); + return; + } + + if (i == contacts.begin()) + i = contacts.end(); + + i--; + nameLine->setText(i.key()); + addressText->setText(i.value()); +} + +void AddressBook::findContact() +{ + dialog->show(); + + if (dialog->exec() == 1) { + QString contactName = dialog->getFindText(); + + if (contacts.contains(contactName)) { + nameLine->setText(contactName); + addressText->setText(contacts.value(contactName)); + } else { + QMessageBox::information(this, tr("Contact Not Found"), + tr("Sorry, \"%1\" is not in your address book.").arg(contactName)); + return; + } + } + + updateInterface(NavigationMode); +} +void AddressBook::updateInterface(Mode mode) +{ + currentMode = mode; + + switch (currentMode) { + + case AddingMode: + case EditingMode: + + nameLine->setReadOnly(false); + nameLine->setFocus(Qt::OtherFocusReason); + addressText->setReadOnly(false); + + addButton->setEnabled(false); + editButton->setEnabled(false); + removeButton->setEnabled(false); + + nextButton->setEnabled(false); + previousButton->setEnabled(false); + + submitButton->show(); + cancelButton->show(); + + loadButton->setEnabled(false); + saveButton->setEnabled(false); + exportButton->setEnabled(false); + break; + + case NavigationMode: + + if (contacts.isEmpty()) { + nameLine->clear(); + addressText->clear(); + } + + nameLine->setReadOnly(true); + addressText->setReadOnly(true); + addButton->setEnabled(true); + + int number = contacts.size(); + editButton->setEnabled(number >= 1); + removeButton->setEnabled(number >= 1); + findButton->setEnabled(number > 2); + nextButton->setEnabled(number > 1); + previousButton->setEnabled(number > 1); + + submitButton->hide(); + cancelButton->hide(); + + exportButton->setEnabled(number >= 1); + + loadButton->setEnabled(true); + saveButton->setEnabled(number >= 1); + break; + } +} + +void AddressBook::saveToFile() +{ + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save Address Book"), "", + tr("Address Book (*.abk);;All Files (*)")); + + if (fileName.isEmpty()) + return; + else { + QFile file(fileName); + + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("Unable to open file"), + file.errorString()); + return; + } + + QDataStream out(&file); + out.setVersion(QDataStream::Qt_4_3); + out << contacts; + } + + updateInterface(NavigationMode); +} + +void AddressBook::loadFromFile() +{ + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open Address Book"), "", + tr("Address Book (*.abk);;All Files (*)")); + + if (fileName.isEmpty()) + return; + else { + QFile file(fileName); + + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::information(this, tr("Unable to open file"), + file.errorString()); + return; + } + + QDataStream in(&file); + in.setVersion(QDataStream::Qt_4_3); + in >> contacts; + + QMap<QString, QString>::iterator i = contacts.begin(); + nameLine->setText(i.key()); + addressText->setText(i.value()); + } + + updateInterface(NavigationMode); +} + +//! [export function part1] +void AddressBook::exportAsVCard() +{ + QString name = nameLine->text(); + QString address = addressText->toPlainText(); + QString firstName; + QString lastName; + QStringList nameList; + + int index = name.indexOf(" "); + + if (index != -1) { + nameList = name.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); + firstName = nameList.first(); + lastName = nameList.last(); + } else { + firstName = name; + lastName = ""; + } + + QString fileName = QFileDialog::getSaveFileName(this, + tr("Export Contact"), "", + tr("vCard Files (*.vcf);;All Files (*)")); + + if (fileName.isEmpty()) + return; + + QFile file(fileName); +//! [export function part1] + +//! [export function part2] + if (!file.open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("Unable to open file"), + file.errorString()); + return; + } + + QTextStream out(&file); +//! [export function part2] + +//! [export function part3] + out << "BEGIN:VCARD" << '\n'; + out << "VERSION:2.1" << '\n'; + out << "N:" << lastName << ';' << firstName << '\n'; + + if (!nameList.isEmpty()) + out << "FN:" << nameList.join(' ') << '\n'; + else + out << "FN:" << firstName << '\n'; +//! [export function part3] + +//! [export function part4] + address.replace(";", "\\;", Qt::CaseInsensitive); + address.replace('\n', ";", Qt::CaseInsensitive); + address.replace(",", " ", Qt::CaseInsensitive); + + out << "ADR;HOME:;" << address << '\n'; + out << "END:VCARD" << '\n'; + + QMessageBox::information(this, tr("Export Successful"), + tr("\"%1\" has been exported as a vCard.").arg(name)); +} +//! [export function part4] diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h new file mode 100644 index 0000000000..c408a97642 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/addressbook.h @@ -0,0 +1,68 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ADDRESSBOOK_H +#define ADDRESSBOOK_H + +#include <QWidget> +#include <QMap> +#include "finddialog.h" + +QT_BEGIN_NAMESPACE +class QPushButton; +class QLabel; +class QLineEdit; +class QTextEdit; +QT_END_NAMESPACE + + +class AddressBook : public QWidget +{ + Q_OBJECT + +public: + AddressBook(QWidget *parent = nullptr); + enum Mode { NavigationMode, AddingMode, EditingMode }; + +public slots: + void addContact(); + void editContact(); + void submitContact(); + void cancel(); + void removeContact(); + void findContact(); + void next(); + void previous(); + void saveToFile(); + void loadFromFile(); +//! [exportAsVCard() declaration] + void exportAsVCard(); +//! [exportAsVCard() declaration] + +private: + void updateInterface(Mode mode); + + QPushButton *addButton; + QPushButton *editButton; + QPushButton *removeButton; + QPushButton *findButton; + QPushButton *submitButton; + QPushButton *cancelButton; + QPushButton *nextButton; + QPushButton *previousButton; + QPushButton *loadButton; + QPushButton *saveButton; +//! [exportButton declaration] + QPushButton *exportButton; +//! [exportButton declaration] + QLineEdit *nameLine; + QTextEdit *addressText; + + QMap<QString, QString> contacts; + FindDialog *dialog; + QString oldName; + QString oldAddress; + Mode currentMode; +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp new file mode 100644 index 0000000000..90729d9c12 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "finddialog.h" + +FindDialog::FindDialog(QWidget *parent) + : QDialog(parent) +{ + QLabel *findLabel = new QLabel(tr("Enter the name of a contact:")); + lineEdit = new QLineEdit; + + findButton = new QPushButton(tr("&Find")); + findText = ""; + + QHBoxLayout *layout = new QHBoxLayout; + layout->addWidget(findLabel); + layout->addWidget(lineEdit); + layout->addWidget(findButton); + + setLayout(layout); + setWindowTitle(tr("Find a Contact")); + connect(findButton, &QPushButton::clicked, + this, &FindDialog::findClicked); + connect(findButton, &QPushButton::clicked, + this, &FindDialog::accept); +} + +void FindDialog::findClicked() +{ + QString text = lineEdit->text(); + + if (text.isEmpty()) { + QMessageBox::information(this, tr("Empty Field"), + tr("Please enter a name.")); + return; + } else { + findText = text; + lineEdit->clear(); + hide(); + } +} + +QString FindDialog::getFindText() +{ + return findText; +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h new file mode 100644 index 0000000000..7c9a3af30f --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/finddialog.h @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef FINDDIALOG_H +#define FINDDIALOG_H + +#include <QDialog> + +QT_BEGIN_NAMESPACE +class QLineEdit; +class QPushButton; +QT_END_NAMESPACE + +class FindDialog : public QDialog +{ + Q_OBJECT + +public: + FindDialog(QWidget *parent = nullptr); + QString getFindText(); + +public slots: + void findClicked(); + +private: + QPushButton *findButton; + QLineEdit *lineEdit; + QString findText; +}; + +#endif diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp b/tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp new file mode 100644 index 0000000000..1f3aac3397 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/main.cpp @@ -0,0 +1,15 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWidgets> +#include "addressbook.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + AddressBook addressBook; + addressBook.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro b/tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro new file mode 100644 index 0000000000..6a99799c67 --- /dev/null +++ b/tests/manual/examples/widgets/tutorials/addressbook/part7/part7.pro @@ -0,0 +1,14 @@ +QT += widgets +requires(qtConfig(filedialog)) + +SOURCES = addressbook.cpp \ + finddialog.cpp \ + main.cpp +HEADERS = addressbook.h \ + finddialog.h + +QMAKE_PROJECT_NAME = ab_part7 + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tutorials/addressbook/part7 +INSTALLS += target |