Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions |

# Replacing the View Widget

[ Previous: Refactoring Existing Code ] [ Home ] [ Next: Replacing the Print Dialog ]

We are ready to start replacing the View widget. However, our example program uses the `XmNotebook` widget class. Qt does not provide a direct equivalent of this class, so we are faced with three possibilities, each with several advantages and disadvantages.

1. We can continue the conversion using existing Qt widgets.

   - Advantages - The widgets provided by Qt are well designed and tested, allowing us to quickly redesign the user interface.

   - Disadvantages - Most, if not all, of the existing data structures and code will need to be modified or rewritten. New code must be written in a way that maintains compatibility with previous versions of our application.

2. We can write a new QWidget subclass that is identical to the `XmNotebook` widget class.

   - Advantages - The existing data structures will not change, allowing compatibility with previous and future versions.

   - Disadvantages - The new widget will need to be written and regression tested. Existing code in our application will need to be changed in order to deal with the new widget's API.

3. We can leave the `XmNotebook` widget untouched.

   - Advantages - The existing data structures and code remain unchanged, allowing us to continue development on other projects, new features, etc.

   - Disadvantages - This is the simplest solution, but the application will still be dependent upon X11; we will not be able to deploy our application on all platforms supported by Qt.

We will use the first approach to complete the migration of the example project used in this walkthrough, using QTextEdit, QLabel and QSpinBox to provide a similar look. The only difference is that we will not have tabs.

We use Qt Designer to add the QTextEdit, QLabel and QSpinBox widgets to the Main Window widget.

## Data Structure Modifications

The `Page` struct contains `majorPB` and `minorPB` members which need to be removed. These members correspond to the tabs displayed in the existing version. The new version will not have any tabs, so we remove them.

## Code Modifications

Most of the existing functions in our application need to be modified to work with the new View widget. The `MainWindow` class has five new functions that correspond to existing functions.

Existing Function | New Function
--- | ---
`void SetPage( int )` | `void MainWindow::setPage( int )`
`void PageChange( ... )` | `void MainWnidow::pageChange( int )`
`void TextChanged( ... )` | `void MainWnidow::textChanged()`
`void ReadDB( char * )` | `void MainWindow::readDB( char * )`
`void SaveDB( char * )` | `void MainWindow::saveDB( char * )`

The `SetPage()` function implementation is moved to the `MainWindow::setPage()` function in `mainwindow.ui.h`. We remove the `SetPage()` function declaration and implementation from `page.h` and `actions.cpp`, respectively. In order to make `MainWindow::setPage()` work correctly, we need to modify the code to use the new widgets in our Main Window widget.

First, we set the current value of the `spinbox` to the current page number.

Next, we set the current text and cursor position of the `textedit` to the contents of the current page.

If the current page has a custom label, we set it as the current text of the `textlabel`; otherwise we set the `textlabel` contents to "Page X" (where X is the current page number).

If the current page has major and/or minor tab text, we append these to the `labeltext`. This ensures that all information entered by the user remains visible.

We should continue to handle the possibility that the current page does not exist. In this case, we clear the contents of the `textedit` widget and set the `textlabel` contents to the current page number (with an indication that the page is invalid).

The `PageChange()` function is moved from `todo.cpp` to the `MainWindow::pageChange()` function in `mainwindow.ui.h`. As with the `MainWindow::setPae()` function, we need to modify the code to use the new widgets in our Main Window widget.

Note: QTextEdit::text() returns a QString, which needs to be converted into a normal `char*` array. To do this we create a copy of the string in the local encoding. We need to make the copy using qstrdup() because the data contained in the QCString returned by QString::local8Bit() is deallocated when the QCString is destroyed.

The `TextChanged()` function does nothing more than set the `modified` variable to 1. Our new `MainWindow::textChanged()` function does exactly the same.

Since both the `MainWindow::pageChange()` and `MainWindow::textChanged()` functions access the `modified` global variable, we add a forward declaration at the top of `mainwindow.ui.h`.

The `ReadDB()` and \s SaveDB() implementations in `io.cpp` are renamed to `MainWindow::readDB()` and `MainWindow::saveDB()`, respectively. We need to modify the code in order to make the code work properly.

First, We add `#include` statements for the `MainWindow`, QSpinBox and QTextEdit classes.

The new `MainWindow::readDB()` and `MainWindow::saveDB()` functions will not use any Xt/Motif functions, so we remove the Xt/Motif `#include` statements and the global variables `notebook` and `textw`. These functions remain largely unchanged, maintaining compatibility with previous versions. Also, the `ReadDB()` and `SaveDB()` functions have been converted into `MainWindow` member functions, so we can pass this as the parent argument to the QMessageBox functions.

After reading the file in the `MainWindow::readDB()` function, we set the current and maximum values of the `spinbox` to the appropriate values.

In the `MainWindow::saveDB()` function, we need to store the text currently displayed, so we use QTextEdit::text() instead of `XmTextGetString()`. Note: QTextEdit::text() returns a QString, which needs to be converted into a normal `char*` array. To do this we create a copy of the string in the local encoding. We need to make the copy using qstrdup() because the data contained in the QCString returned by QString::local8Bit() is deallocated when the QCString is destroyed.

Due to the removal of the `majorPB` and `minorPB` members from the `Page` struct, the `FixPages()` function in `actions.cpp` is no longer needed. We remove the implementation and forward declaration of `FixPages()` from `actions.cpp` and `page.h`, respectively. Calls to `FixPages()` are removed from the `MainWindow::selNewPage()` and `MainWindow::selDeletePage()`, both of which are in `mainwindow.ui.h`.

We move `AdjustPages()` to `mainwindow.ui.h` and make it `static`, since it is only used in this file. We remove the forward declaration from `page.h` as well.

After our modifications, the `actions.cpp` file is empty. We remove it from our project file and regenerate our `Makefile`.

Now that we have implemented our new View widget, we need to remove the old Motif based view widget from `todo.cpp`.

Since we will not be using any Motif widgets, we remove all Motif `#include` statements, including qmotifwidget.h.

We also remove the forward declarations of the `ReadDB()` function and the `notebook`, `textw` and `labelw` global variables.

Next, we remove the `center` widget, which uses QMotifWidget. The Main Window widget and View widget are contained entirely in our `MainWindow` class, so no extra initialization is needed after creating the `mainwindow` widget.

Since the `ReadDB()` and `SetPage()` functions have been changed into `MainWindow` member functions, we need to call them using our `mainwindow` instance.

The View widget has now been replaced. After building our project, we confirm that the application works correctly.

[ Previous: Refactoring Existing Code ] [ Home ] [ Next: Replacing the Print Dialog ]