Converting the Design into an Executable Application

We've built the user interface through Qt Designer and connected those slots that provided sufficient default functionality. The last steps are to code the slots that require customization and then to create main.cpp so that we can compile and build our application.

Implementing the Main Window's Functionality

When the user starts the richedit application we want the focus to be in the textEdit widget so we'll add one line of code to the init() function to achieve this. (All the code snippets are from eg/richedit/richedit.cpp which is generated by uic from richedit.ui.)
    textEdit->setFocus();  

New Files and Loading and Saving Existing Files

The code for these tasks is straightforward. When the user clicks File|New we check to see if there are unsaved changes in the existing text and give them the opportunity to save, continue without saving or cancel the operation. When the user opts to open an existing file or exit the application we perform the same check and offer them the same choices.

void EditorForm::fileNew()
{
    if ( saveAndContinue( "New" ) )
            textEdit->clear();
}
The fileNew() function clears the text and the filename.

void EditorForm::fileOpen()
{
    if ( saveAndContinue( "Open" ) ) {
            QString fn( QFileDialog::getOpenFileName( 
                        QString::null, 
                        "Rich Text Files (*.htm*)", this ) );   
        if ( !fn.isEmpty() ) {
            fileName = fn;
            QFile file( fileName );
            if ( file.open( IO_ReadOnly ) ) {
                QTextStream ts( &file );
                textEdit->setText( ts.read() );
            }
        }
    }
}
The fileOpen() function asks the user to choose a file using QFileDialog::getOpenFileName(). If they choose a file we set the fileName member to its name, open it and read its contents directly into the text edit via a text stream.

void EditorForm::fileSave()
{
    if ( fileName.isEmpty() ) {
        fileSaveAs();
    } else {
        QFile f( fileName );
        if ( f.open( IO_WriteOnly ) ) {
            QTextStream ts( &f );
            ts << textEdit->text();
            textEdit->setModified( FALSE );
        }
    }
}
If there is no current file name we call fileSaveAs() which will prompt for a file name and if a file name is given calls fileSave(). If we have a file name we open a file and write the text from the text edit into the file via a text stream. We also set the text edit's modified property to FALSE.

void EditorForm::fileSaveAs()
{
    QString fn = QFileDialog::getSaveFileName( 
                                    "", "Rich Text Files (*.htm*)", this );
    if ( !fn.isEmpty() ) {
        fileName = fn;
        fileSave();
    }
}
The fileSaveAs function prompts the user for a file name and if they give a file name, saves the text to the file by calling fileSave().

void EditorForm::fileExit()
{
    if ( saveAndContinue( "Exit" ) )
        qApp->exit();
}
When we exit the application we must perform the same check for unsaved changes as we've done in the preceding functions, so we've included the fileExit function's code here.

int EditorForm::saveAndContinue( const QString & action )
{
    int continueAction = 1;
    
    if ( textEdit->isModified() ) {  
        switch( QMessageBox::information(     
                this, "Rich Edit",   
                "The document contains unsaved changes.\n"   
                "Do you want to save the changes?",   
                "&Save", "&Don't Save", "&Cancel " + action,   
                0, // Enter == button 0   
                2 ) ) { // Escape == button 2   
        case 0: // Save; continue  
                     fileSave();  
                break;   
            case 1: // Do not save; continue
                break;   
            case 2: // Cancel
                continueAction = 0;
                break;  
            }          
    }  
    
    return continueAction;
}
The saveAndContinue function is included for completeness.

Aligning Text

void EditorForm::changeAlignment( QAction * align )
{
    if ( align == leftAlignAction ) 
        textEdit->setAlignment( Qt::AlignLeft );
    else if ( align == rightAlignAction )
        textEdit->setAlignment( Qt::AlignRight );
    else if ( align == centerAlignAction )
        textEdit->setAlignment( Qt::AlignCenter );
}
We compare the chosen alignment action's pointer to the the pointers stored in the form and if we get a match set the appropriate alignment in the textEdit widget.

Changing Fonts

We've already connected the fontComboBox's activated() signal to the textEdit's setFamily() slot so we just have to populate the combo box with the font names when we call init().
void EditorForm::init()
{
    textEdit->setFocus();  
 
    QFontDatabase fonts;
    fontComboBox->insertStringList( fonts.families() );
    QString font = textEdit->family();
    font = font.lower();
    for ( int i = 0 ; i < fontComboBox->count(); i++ ) {
        if ( font == fontComboBox->text( i ) ) {
            fontComboBox->setCurrentItem( i );
            break;
        }
    }
}
The first line sets the focus as we've already mentioned. We then create a QFontDatabase object and insert its list of font families into the fontComboBox. Finally we set the fontComboBox's current item to the textEdit's current font.

Making the Application Run

With all the connections and code in place we are now ready to make our application run. Click on the Source tab of the Object Hierarchy window and click on the Includes (in Implementation) item. We need to include the files that our source code depends on. Right click the Includes item and click New. Type in <qapplication.h> for fileExit()'s exit() call. In the same way add <qmessagebox.h> for saveAndContinue()'s message box, <qfiledialog.h> for the fileOpen() and fileSaveAs() functions, and <qfontdatabase.h> for the QFontDatabase class in init().

We referred to a member variable, fileName, in our source code so we must add it to the form. Click the Source tab, right click the Class Variables item, click New from the pop up menu, then enter 'QString fileName;'.

Create a main.cpp file in a plain text editor. Ours looks like this:
#include <qapplication.h>
#include "richedit.h"

int main( int argc, char *argv[] ) 
{
    QApplication app( argc, argv );

    EditorForm richeditForm;
    app.setMainWidget( &richeditForm );
    richeditForm.show();

    return app.exec();
}

In a plain text editor open the richedit.pro project file and add the line SOURCES += main.cpp to the end of the file. The file should look similar to this:
TEMPLATE        = app
CONFIG += qt warn_on release
TARGET        = richedit
INTERFACES        = richedit.ui 
DBFILE        = richedit.db
IMAGEFILE        = images.cpp
PROJECTNAME        = richedit
LANGUAGE        = C++
{SOURCES+=images.cpp}
SOURCES += main.cpp
All that's left to do is to generate the Makefile, compile and run. The Makefile is created with qmake: qmake -o Makefile richedit.pro.

The richedit application demonstrates how easy it is to create a Qt application's main window with menus and dockable toolbars. A great deal of functionality was obtained by connecting the appropriate built-in signals and slots. The remaining functionality was achieved by connecting built-in signals to our own custom slots. We could continue developing the application, for example updating the fontComboBox, the font size spinbox and the actions with the font attributes as the user moves the cursor through their text. But our objective has been to demonstrate the creation of a main window with actions, menus and toolbars so we must stop at this point and leave further development and experimentation to you.