A KDevelop Programozói Kézikönyv: Új alkalmazások létrehozása

Következő Előző Tartalom

## 3. Új alkalmazások létrehozása

The KAppWizard, or also called the KDE Application Wizard, is intended to let you start working on new projects with KDevelop. Therefore, all your projects are first created by the wizard; then you can start building them and extend the already provided source skeleton. KAppWizard also allows to choose between several project types according to your project's goals:

- Normal KDE Application: includes source code for a complete frame structure of a standard KDE application with support for the Document-View-Controller model, a menubar, toolbar and statusbar as well as a set of standard documentation based on SGML, KDE-links and application icons. This is the application type usually needed for a new KDE project.
- Mini KDE Application: contains the same general structure as the Normal KDE Application type but with the difference that the application's code only provides a widget as a window.
- Normal Qt Application: works like the normal KDE application by it's code with the difference that the project is based on the Qt library only and doesn't contain KDE support. This is intended for projects that have to be portable to Windows platforms or that don't want to require KDE libraries by the end-user.
- C Application: Is based on the C compiler only and runs in a console.
- C++ Application: Is based on the C++ compiler only and runs in a console like the C application, therefore doesn't require X-Window.
- Custom project: creates an empty project without any sourcecode. This is intended for already existing projects to port to KDevelop or for projects where you want to start from scratch. Mind that you have to take care for makefiles and configure scripts all by yourself.

In this chapter we'll see how the KAppWizard can be invoked and what has to be done to generate a KDE application project. This will also be the initial step of our coverage, where we will create the initial version of a sample project. For all other project types the steps are usually the same, just you may not have certain options available.

## 3.1 Invoking KAppWizard and Project Generation

### Starting KAppWizard and the First Page

To start with your first KDE application, open KDevelop. Then select "New..." from the "Project"-menu. The KAppWizard starts, and you see a tree on the first page, containing the project types. When a type is selected, you see a preview how it will look like after the initial build process. Choose the KDE subtree, Normal type. Then press the "Next" button on the bottom of the first wizard page. This will switch to the next page, where you have to set the general project options.

### The Generate Settings Page

For our sample application, we choose the project name `KScribble`; therefore insert this in the field "Projectname". Then select the directory you want to have your project build in; the default is your home directory. You can enter the path manually or you can as well press the button on the right to select the directory by a dialog.

Next, you have to enter the Version number. For the first version, set this to `0.1`. It is usual to number new applications that are in development for the first release lower than 1, and as the initial version will only contain the standard framework, we'll name this the 0.1 version.

Finally, add your name to the "Author" field and your email address. You can leave all other options to their default settings.

To give you some information about all other options, you can press the right mouse button over the options, and you will get a quick-help window that describes the option's purpose.

These are:

- generate sources and headers: generates the application source code
- GNU-Standard-Files: adds a copy of the GNU-General Public License to the project as well as some standard files for user information when distributing the package.
- User-Documentation: the user handbook in SGML, already prepared for your project.
- API-Documentation: creates an initial HTML documentation set for the Application Programming Interface.
- lsm-File: the Linux Software Map, used by distribution sites and contains short information about the project's purpose and requirements.
- .kdelnk-File: a KDE link that will install your application in the "Applications" tree of the KDE-Panel.
- Program-Icon: an Icon that represents your project and can be used to create a link on the desktop.
- Mini-Icon: a Mini-version of the program icon that represents your application besides its name in the KDE-Panel and is shown in your application's dialogs and main-window in the left upper corner.

Now we'll switch to the next page by pressing the "Next" button again to set the template for the header files of your project.

### The Header and Source Templates

The header template page allows you to automatically include a preface for your header files, containing the filename, the construction date, the year of the copyright, also your name and your email address. You don't have to change those uppercase parts yourself, as KAppWizard does this automatically and stores the template for this project, so it can be used later again for creating new files.

The second part of the default header template contains a license information. By default, your project is set under the GNU General Public License, which is also included in the package. This license is used to protect your source code against any person that just copies your sources for his own purpose. The General Public License offers you this license for free and thereby protects your rights as the author, and is common for distributing free software. To get more information about the license, you should read the COPYING file in the base directory of your new project later which is a copy of the GPL and ships with your application already.

Anyway, you may want to choose another license or another header template you're already using for your projects. Therefore you can either edit the given default template directly. To do this, you're given the template in an editing window. To clear the default page, select "New", to use another template, select "Load...", which lets you choose the template file.

When you're done, go to the next page by entering "Next". This is the template page for your source files and is generally the same as the header template page. The only difference is that this template is used for your implementation files.

### Creating the Project

Now that you've set all options for KScribble, select "Next" and press the "Generate" button on the bottom of the wizard window. If the button is not available, you haven't set all options correctly. To correct any errors, step back in the Wizard with "Back".

Then you'll see what KAppWizard does- he copies all templates to your project directory and creates the new project. After KAppWizard is finished, the "Cancel" button changes to an "Exit" button to leave the wizard.

After this last step, you're finished with creating a new project. KDevelop then loads it and the tree-views let you browse through the project's files and classes.

In the next section, we'll discuss how to build and run your first version of KScribble and how the source code is organized.

## 3.2 The First Build

After our project is generated, we'll first make a trip through the source code to get a general understanding how the application frame works. This won't only help to get started but we'll know where to change what in later steps.

When opening the LFV (Logical File Viewer) page on the tree-view, you see some folders that already sort the project files relevant to the developer. The first two folders are "Header" and "Sources". The Header-folder therefore logically contains all header files of the project, the Sources-folder all sourcecodes. All other folders are of no interest right now, so we'll turn back here later to see what they contain.

The two folders then contain the following files:

Headers:

- kscribble.h : contains the class declaration for the class KScribbleApp.
- kscribbledoc.h : contains the class declaration for the class KScribbleDoc.
- kscribbleview.h : contains the class declaration for the class KScribbleView.
- resource.h : contains a macro collection for the menu-ID's

Sources:

- kscribble.cpp : contains the implementation of the class KScribbleApp.
- kscribbledoc.cpp : contains the implementation of the class KScribbleDoc.
- kscribbleview.cpp : contains the implementation of the class KScribbleView.
- main.cpp : contains the main() function implementation.

Before diving into the sources, we'll let KDevelop build and run our new application. To do this, select "Make" from the "Build"-menu or hit the according button on the toolbar. The output window opens from the bottom of KDevelop and lets you see what `make` does by the messages it gives us:

> `

>     1   Making all in docs
>     2   make[1]: Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     3   Making all in en
>     4   make[2]: Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs/en'
>     5   make[2]: Nothing to be done for `all'.
>     6   make[2]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs/en'
>     7   make[2]: Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     8   make[2]: Nothing to be done for `all-am'.
>     9   make[2]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     10  make[1]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble/docs'
>     11  make[1]: Entering directory `/home/rnolden/Tutorial/kscribble1/kscribble'
>     12  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         kscribbleview.cpp
>     13  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         kscribbledoc.cpp
>     14  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         kscribble.cpp
>     15  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         main.cpp
>     16  /usr/bin/moc ./kscribble.h -o kscribble.moc.cpp
>     17  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         kscribble.moc.cpp
>     18  /usr/bin/moc ./kscribbledoc.h -o kscribbledoc.moc.cpp
>     19  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         kscribbledoc.moc.cpp
>     20  /usr/bin/moc ./kscribbleview.h -o kscribbleview.moc.cpp
>     21  g++ -DHAVE_CONFIG_H -I. -I. -I.. -I/opt/kde/include -I/usr/lib/qt/include  -I/usr/X11R6/include    -O0 -g -Wall  -c
>         kscribbleview.moc.cpp

>     22  /bin/sh ../libtool --silent --mode=link g++  -O0 -g -Wall   -o kscribble -L/opt/kde/lib  -L/usr/X11R6/lib -rpath /opt/kde/lib
>         -rpath /usr/X11R6/lib kscribbleview.o kscribbledoc.o kscribble.o main.o kscribble.moc.o kscribbledoc.moc.o kscribbleview.moc.o
>         -lkfile -lkfm -lkdeui -lkdecore -lqt -lXext -lX11

>     23  make[1]: Leaving directory `/home/rnolden/Tutorial/kscribble1/kscribble'

> `

As you see, we've put line numbers in front of each line, which won't appear in your output; it just makes it easier to describe what happened during the build now. First of all, `make` works recursively. That means, it starts from the directory it is invoked in and then goes into the subdirectories first, returns and processes the next directory. Finally, the directory it was started is processed and `make` finishes. Therefore, `make` started in the main project directory containing the sources first. In line 1 and 2, you see how the `make` process goes into the `docs` directory, then into the `en` subdirectory. As there isn't anything to do, it leaves these directories until it returns to the source-directory `kscribble` in line 11. Then, the real work starts: `make` invokes the compiler, here `g++` to compile the source-file `kscribbleview.cpp`. The macro `-DHAVE_CONFIG_H` says that the file `config.h` should be used. This is a file containing macros for the specific platform and application and is located in the main project directory. The following `-I` commands add the include path where `g++` can find the includes it needs. These are the current directory, the main project directory (by `-I..`) and the include path for the KDE, Qt and X11 library header files. The directories for these include files were determined by the `configure` script and set in the Makefiles, therefore, the compiler knows where these are located. Finally, `-O0` sets the optimization to zero (no optimization), `-g` enables debugging, `-Wall` sets the compiler warnings to `all` and `-c` tells the compiler to produce an object file, so only compile the file.

This is done for the other source files of our project as well in lines 13-15. Obviously, our sources are compiled, but instead of linking the object files of the sources to the final binary, we see some other commands. In line 16, you see that the program "moc" is called to process the header file `kscribble.h`, with its output in `kscribble.moc.cpp`. Then, in line 17, this source file is compiled as well. The same happens with the other project header files until line 21. Now, as the Qt toolkit contains the signal/slot mechanism, but still stays a C++ implementation, you're using certain keywords that are not originally C++ language, such as the `signals:` and `slots:` declaration in your classes. This gives you the ability to easily allow object communication for all class objects that inherit the class `QObject`, so you can avoid the usual callback pointer functions. Therefore, the application needs the sources that implement this functionality, and that is why `moc` is called. `Moc` is the Meta Object Compiler of the Qt toolkit and builds the implementation for signals and slots mechanisms by parsing the header file and producing a source output that has to be compiled in the binary. As KDevelop projects use `automoc` to determine, which header file needs to be processed, you don't have to take care for any call on `moc` and the C++ compiler on the moc output files. Just remember the rules that make a class use the signals and slots- inheritance from `QObject` or any class that inherits `QObject` itself, inclusion of the `Q_OBJECT` macro (without semicolon !) at the beginning of the class declaration and the declarations for signals and slots.

Finally, your binary is built by the compiler. The output binary is called `kscribble`, the linker includes the path for the KDE and X11 libraries and links the sources against the libraries `kfile, kfm, kdeui, kdecore, qt, Xext` and `X11`. Then you're done and make exits.

## 3.3 The Source Skeleton

To gain a concept of how a KDE application works, we'll first have a very close look at the source skeleton already provided by the Application Wizard. As we already saw, we're having a set of source and header files that build the initial code for the application and make it ready-to-run. Therefore, the easiest way to explain the code is to follow the implementation line by line as it is processed during executing the program until it enters the main event loop and is ready to accept user input. Then, we'll have a look at the functionality that enables user interaction and how certain things work. This is probably the best way to explain the framework and, as it is similar to almost all KDE applications, will enable you to read source codes from other projects as well; additionally, you will know where to change what part of the code to make your applications behave the way they are designed for.

### The `main()` Function

As the application begins its execution with entering the `main()` function, this will be the start for our code examination. The `main()` function of KScribble is implemented in the file `main.cpp` and can also be found using the Class Browser by selecting the "Globals" folder, sub-folder "Functions":

> `

>     1  #include "kscribble.h"
>     2
>     3  int main(int argc, char* argv[]) {
>     4    KApplication app(argc,argv,"KScribble");
>     5
>     6    if (app.isRestored())
>     7    {
>     8       RESTORE(KScribbleApp);
>     9    }
>     10   else
>     11   {
>     12      KScribbleApp* kscribble = new KScribbleApp;
>     13      kscribble->show();
>     14      if(argc > 1){
>     15        kscribble->openFile(argv[1]);
>     16      }
>     17    }
>     18    return app.exec();
>     19  }

> `

Now, what happens first is the usual creation of a `KApplication` object, which gets our application name KScribble as a third parameter. When creating a new `KApplication`, a new `KConfig` instance is created as well which is connected to a configuration file in $HOME/.kde/share/config/appname + rc which stores all information we want to use when starting application windows. The name we passed the constructor of `app` will be used as the window title later.

Despite of the example code for turning the first Qt application into a KDE one, the following code is somewhat different. After the `KApplication` object is present, we're testing if the application is started by the session management of `kwm` or manually by the user. This can be found out when calling `isRestored()` on the `app` object, which returns `true` for session management and `false` for a normal start.

As session management is a main feature of KDE applications and widely used by the framework but a lot more to explain, we'll follow the `else{}` section first; then we'll come back and explain the session functionality in a later step.

### User Application Start

The `else{}` section now creates an instance of the class `KScribbleApp` in line 12. This object is called to show itself in line 13 as usual; line 14 determines if a command-line argument has been passed and, as this is usually the name of a file, calls the `kscribble` object to open it with `openFile()`.

Note that we didn't call the method `setTopWidget(kscribble)` for our application- this is already done by the class that `KScribbleApp` inherits. Now we'll have a look at our `KScribbleApp` object- what is it and what does it provide already ? The only thing we know until now is that it has to be a Widget to represent the user interface in the main window. Let's turn to the class implementation of `KScribbleApp`, which can be found in the file `kscribble.cpp` or by a click on the class icon in the Class Browser. As the instance is created by the constructor. First of all, we see that it inherits the class `KTMainWindow`, which is a part of the `kdeui` library. This class itself inherits `QWidget`, so, as usual, we have a normal widget as the top-level window. `KTMainWindow` contains a lot of functionality that the class `KScribbleApp` makes use of. It provides a menubar, toolbar, statusbar and session management support. The only thing we have to do when sub-classing `KTMainWindow` is to create all the objects we need and create another widget that is managed by our `KTMainWindow` instance as the main view in the center of the window; usually this is the place where the user works like a text-editing view.

### The Constructor

Let's have a look at the code for the constructor and see how the instance is created:

> `

>     1   KScribbleApp::KScribbleApp()
>     2   {
>     3     config=kapp->getConfig();
>     4
>     5
>     6     ///////////////////////////////////////////////////////////////////
>     7     // call inits to invoke all other construction parts
>     8     initMenuBar();
>     9     initToolBar();
>     10    initStatusBar();
>     11    initKeyAccel();
>     12    initDocument();
>     13    initView();
>     14
>     15    readOptions();
>     16
>     17    ///////////////////////////////////////////////////////////////////
>     18    // disable menu and toolbar items at startup
>     19    disableCommand(ID_FILE_SAVE);
>     20    disableCommand(ID_FILE_SAVE_AS);
>     21    disableCommand(ID_FILE_PRINT);
>     22
>     23    disableCommand(ID_EDIT_CUT);
>     24    disableCommand(ID_EDIT_COPY);
>     25    disableCommand(ID_EDIT_PASTE);
>     26  }

> `

We see that our config instance of `KConfig` now points to the applications configuration, so we can operate with the configuration file entries later.

Then, all parts of the application that are needed are created by their according member functions that are specific to our main window:

- initMenuBar(): constructs the menubar,
- initToolBar(): constructs the toolbar,
- initStatusBar(): creates the statusbar,
- initKeyAccel(): sets all keyboard accelerator s for our application by the global and application specific keyboard configuration
- initDocument(): creates the document object for the application window
- initView(): creates the main widget for our view within the main window
- readOptions(): reads all application specific settings from the configuration file and initializes the rest of the application such as the recent file list, the bar positions and the window size.

Finally, we disable some commands that the user can do, because they should not be available in the current application state. As we now have a general overview how the application window is created, we will look into the details of how the user elements are constructed by following the above methods.

### The Menubar

As shown above, the menubar of KScribble is created by the method `initMenuBar()`. There, we create a set of `QPopupMenu`s that pop up if the user selected a menuentry. Then, we insert them into the menubar and connect to the entries.

First, we create our `recent_file_menu`, which will contain the names of the last 5 opened files. We have to do this first, because this menu is inserted into the `file_menu`. Then we add the connection directly- we just retrieve the signal that is emitted by the menuentry with its entry number and call the `slotFileOpenRecent( int )`, which then calls the right file from the recent file list to be opened.

Then we create our "File"-menu. This will be the menu that will be visible in the menubar. The standard actions are then inserted into the popup-menu one by one- first the commands for creating a new file, open a file, close a file etc., finally "E&xit" to close the application. All menu entries have to be created in the order as they appear later, so we have to keep an eye on which we want to have at what place. As an example, we look at the following entries:

> `

>     file_menu->insertItem(Icon("fileopen.xpm"), i18n("&Open..."), ID_FILE_OPEN );
>     file_menu->insertItem(i18n("Open &recent"), recent_files_menu, ID_FILE_OPEN_RECENT );

> `

The first one inserts the "Open..." entry. As we want to have it with an icon, we use the `insertItem()` method with the icon's name. To understand the icon loading process, we need to know what or where `Icon()` is declared- in fact, it is a macro provided by the class `KApplication`:

> `

>     #define Icon(x) kapp->getIconLoader()->loadIcon(x)

> `

Additionally, it uses the following macro internally to get access to the application object:

> `

>     #define kapp KApplication::getKApplication()

> `

This means that the `KApplication` object already contains an Icon loader instance- we only have to get access to it; then it will load the according icon. As our icons are all from the KDE libraries, we don't have to take care for anything else- they are installed on the system automatically, therefore we also don't have to include them into our application package to use them.

After the icon parameter (which is optional), we insert the menuentry name by `i18n("&Open...")`. There, we have to watch two things: first, the entry is inserted with the `i18n()` method. Like the `Icon()` entry, it is a macro defined in `kapp.h` as well and calls the `KLocale` object of `KApplication` to translate the entry to the currently used language:

> `

>     #define i18n(X) KApplication::getKApplication()->getLocale()->translate(X)

> `

Hereby, it should be mentioned that one could think "I don't want to use macros"- you can do that in most cases. But here it is immanent to use `i18n()` because for internationalization the according language files have to be build. As this build process depends on the `i18n` string, you have to use the macro.

As you might have already guessed, the ampersand within menu entries is later interpreted as a line under the following letter in the menuentry. This allows fast access to the menu command via the keyboard when the user presses the `Alt`-key in conjuction with the underlined letter.

Finally, we're giving the menuentry an ID, which is an integer value by which we can find the entry later. To keep an overview over the used values, these are defined by macros and are collected in the file `resource.h` within your project. For consistency, these macros are all uppercase and begin with ID_, then the menu name followed by the entry. This makes it very easy to remember the sense of each entry anywhere within the code, so you don't have to turn to the menubar implementation again to look up the entries.

The second example entry shows another variant of the `insertItem()` method. Here, we add the recent_files_menu popup menu as a menuitem. This means, that the entry shows itself with the given string "Open recent", followed by a right arrow. On selection, the recent file popup menu appears and the user can choose the last file.

Last but not least there are a lot of other ways to insert menu items- the framework keeps this as simple as possible. More information can be obtained in the Qt documentation about the `QMenuData` class.

Now, after we created the popup menus `file_menu, edit_menu` and `view_menu`, we have to include a "Help"-menu as well. We could do this like the others as well, but the `KApplication` class offers a nice and quick method to cover this:

> `

>     help_menu = kapp->getHelpMenu(true, i18n("KScribble\n" VERSION ));

> `

This is all we have to do to get a help menu that contains an entry for the help contents with the F1 keyboard shortcut , an about-box for the application and an about-box for the KDE (which can be disabled by calling `getHelpMenu(false,...);`). The contents for our applications about-box is set with the `i18n()` string again- VERSION takes the macro that is defined for the project version number in the file `config.h`, so we don't have to change this every time manually when we want to give out a new release. Feel free to add any information about your application here, e.g. your name, email address, copyright and the like.

Now we only have to insert the pop-ups into the menubar. As `KTMainWindow` already constructs a menubar for us, we just insert them by calling `menuBar()->insertItem();`.

What is left to do is to connect the menu-entries with the methods they will execute. Therefore, we connect each popup menu by its signal `activated( int )` to a method `commandCallback( int )`, which contains a `switch` statement that calls the according methods for the menu entries. Additionally, we connect the pop-ups by their signal `highlighted( int )` to provide statusbar help on each entry. Whenever the user moves his mouse or keyboard focus to an entry, the statusbar then shows the according help message.

After we finished with the menubar, we can continue with the toolbar in the following section. Mind that an instance of a `KTMainWindow` can only have one menubar visible at a time; therefore if you want to construct several menu bars, you have to create them separately with instances of `KMenuBar` and set one of them by the according methods of `KTMainWindow` as the current menubar. See the class documentation of `KMenuBar` for more detailed information about how to extend the features, also see Menüsorok és Eszközsorok Konfigurálása.

### The Toolbar

The creation of toolbars now is even simpler than that of menubars. As `KTMainWindow` already provides toolbars, which are created by the first insertion, you are free to create several ones. Just add the buttons for the functions you want to provide:

> `

>     toolBar()->insertButton(Icon("filenew.xpm"), ID_FILE_NEW, true, i18n("New File") );

> `

This adds a left-aligned button with the icon "filenew.xpm" with the according ID to the toolbar. The third parameter decides if the button should be enabled or not; by default we set this to `true`, because our `disableCommand()` methods at the end of the constructor do this for us automatically for both menu and toolbar entries. Finally, the last parameter is uses as a so-called "Quick-Tip"- when the user moves the mouse pointer over the button so that it gets highlighted, a small window appears that contains a short help message, whose contents can be set here.

Finally, all toolbar buttons are connected to our `commandCallback()` method again by their signal `clicked()`. On the signal `pressed()`, we let the user receive the according help message in the statusbar.

Additional Information:

As toolbars are created using the class `KToolBar`, you should have a look at the according documentation. With `KToolBar`, a lot of things needed in a toolbar can be realized such as delayed pop-ups if your button wants to pop up a menu when the button keeps being pressed or even widgets like combos. Also, by default, the toolbar fills the complete width of the window, which makes it look nice for using a single bar. When using more than one, you should also think about setting the bar size to end at the most right button, so other bars can be displayed in the same row below the menubar. We will discuss certain techniques about designing and extending toolbars in section Menüsorok és Eszközsorok Konfigurálása.

### The Statusbar

The statusbar is, as well as the other bars, already provided by the `KTMainWindow` instance, so we just have to insert our items as we want to. By default, the framework contains only one entry that displays statusbar help. For a lot of applications this may not last; then you would add the entries you need for displaying e.g. coordinates and the like.

Also, an application can only have one statusbar at a time like a menubar. If you want to construct several ones, you should create them separately and set the current bar by the according method of `KTMainWindow`. The statusbar also offers to insert widgets, which can be used to produce nice habits for displaying progress-bars like KDevelop does. Refer to the class documentation of `KStatusBar`.

### Keyboard Accelerators

With reaching the method `initKeyAccel()`, we already constructed the standard items of an application main window- the menubar, toolbar and statusbar. Indeed, we didn't set any keyboard accelerator s by which advanced users that only want to work with the keyboard have a quick access to certain commands that are used most often during work with our program. To do this, we could have inserted the accelerator keys by the insertion of the menu-items for example, but KDE offers a good solution to construct and maintain keyboard accelerator s. A lot of users want to have them configurable on one hand and on the other standard accelerator s should be the same over all applications. Therefore, the KDE control center offers configuring standard keyboard accelerator s globally by using the `KAccel` class. Additionally, the KDE libraries contain a widget that lets users configure application specific keyboard shortcut s easily. As the application framework only uses menu-items that have standard actions such as "New" or "Exit", these are set by the method `initKeyAccel()`. Standard actions just have to be connected, for your application specific keyboard values, you have to insert them first by specifying the keyboard accelerator name and then connect them. As our accelerator s are all present in the menubar, we have to change the accelerator s for the popup entries. Finally we call `readSettings()`, which reads the current settings from the root window of KDE containing the configuration of standard accelerator s, then the settings for accelerator s specified in the application's config file. When we're going further into our example project, we will also talk about how to configure our application specific accelerator s by a configuration dialog, see Menüsorok és Eszközsorok Konfigurálása for that part of the development process.

### The Document-View Model

The next two member function calls, `initDocument()` and `initView()`, are finally building the part that the application windows are supposed to provide to the user: an interface to work with data that the application is supposed to manipulate; and that is also the reason why the application framework contains three classes, an `*App`, `*View` and `*Doc` class. To understand, why this structure is helpful, we'll look a bit aside the actual code and introduce some theory, then we'll switch to the program again to see how the KDevelop frameworks support such a model.

Basically, all what has been explained about the framework is that we need an application instance that contains a main window. This window is responsible to provide the basic interface for the user- it contains the menubar, toolbars and statusbar and the event controlling for user interaction. Also, it contains an area, that is described as a "view". Now, the purpose of a view is generally, to display the data that the user can manipulate, e.g. a part of a text file. Although the text file is probably larger than the view is able to display on the screen, it offers the user to go to the part that he wants to see (therefore it is a view), and there the user can as well change the data of the file contents. To give the programmer a better way to separate parts of the application by code, the Document-View Model has been invented. Although not a standard, it provides a structure how an application should work:

- The application contains a controller object,
- a View object that displays the data the user works with
- and a Document object that actually contains the data to manipulate.

Back to the example of working with a text file- there, this model would work the way that the Document would read the file contents and provides methods to change the data as well as to save the file again. The view then processes the events that the user produces by the keyboard and the mouse and uses the document object's methods to manipulate the document data.

Finally, the controller object is responsible for user interaction by providing the document and the view objects as well as the interfaces to send commands like opening and saving. Additionally, certain methods of the view object can be provided by commands that can be accessed via keyboard accelerator s or the mouse on menubars and toolbars.

This Document-View model has some advantages- it separates the program's code more object-oriented and by this offers more flexibility in general, e.g. the same document object could be displayed by two views at the same time; either by a new view in a new window or by tiling the current one that then contains two view object that build the actual window view region.

Now, if you're coming from MS-Windows systems you may have some experience with that- the MFC already provide a document model that is ready to use. For KDE and Qt applications, things are a bit different. Qt is a powerful toolkit as it provides the most needed classes, widgets etc. But there wasn't any intention to take care of the document-view model, and as KDE is inheriting Qt, there weren't any tendencies to introduce such a model either. This somehow has its reason in the fact that usually X-applications don't work with an MDI (Multiple Document Interface). Each main window is responsible for its data and that reduces the need of a document model to the fact that methods to work on documents are always implied into widgets. The only exception from this currently is the KOffice project that is intended to provide a complete office suite of applications like a word processor, a spreadsheet etc. Technically, this is realized by two changes two the normal usage of Qt and KDE:

- KOffice uses KOM and the free MICO implementation of CORBA for object communication,
- the KOffice applications use a document-view model to allow all applications to work with any KOffice data objects

But as KDevelop currently targets on using the current libraries of KDE 1.1.x and Qt 1.4x, we can't use this model by default- this will come in further releases of a KDE 2, which will (hopefully) contain two new major changes in relation to the current situation:

1. an MDI interface for KTMainWindow
2. the KOM libraries that provide a document model

Therefore, the current way for application developers can be to either implement all needed document methods within their view or to try to reproduce a document model by themselves. KDevelop therefore contains such a reproduction by providing the needed classes and the basic methods that are generally used for a Document-View model with the application frameworks for Qt and KDE.

Back to the code, you now can imagine the purpose of the two methods we mentioned at the beginning of this section: the `initDocument()` and `initView()` functions. The `initDocument()` constructs the document object that represents the application window data and initializes the basic attributes like setting the modification bit that indicates if the data currently used has been changed by the user. Then, the `initView()` method constructs the `*View` widget, connects it to the document and calls the `setView()` method of `KTMainWindow` to tell the `*App` window to use the `*View` widget as it's center view.

For the developer, it is important to know that during the development process he has to:

- re-implement the virtual methods for mouse and keyboard events provided by `QWidget` in the `*View` object to provide the means to manipulate data,
- re implement the `paintEvent()` of `QWidget` in the `*View` object to repaint() the view after changes,
- complete the implementation for printing the document via the printing method of the `*View` object,
- add the serialization for the `*Doc` object to provide file loading and saving,
- add the document data structure implementation to the `*Doc` object that is representing the document data logically in the memory.
- add any methods that have to be accessible by the user via accelerator keys and menus/toolbars.

### Application Configuration

Now, after we created all instances of the `KTMainWindow` instance of our application to create the first window, we have to initialize certain values that influence the look of the program. For this, we call `readOptions()`, which gets all values and calls the methods needed to set the according attributes. The KDE-Core library contains the class `KConfig` that provides a good possibility to store values in configuration files as well as to read them in again. Also, as each `KApplication` instance creates it's resource file already, we only have to access this file and create our values. As `KConfig` provides us the file object, we have to use the class `KConfigBase` to read and write all entries. As writing is very easy to do with `writeEntry()` methods, reading depends on the attribute type which we want to initialize. Generally, an entry in the configuration file contains a value name and a value. Values that belong together in some context can be collected in groups, therefore we have to set the group name before we access the value afterwards; the group has to be set only once for reading a set of attributes that are in the same group. Let's have a look at what we want to read in:

> `

>     1   void KScribbleApp::readOptions()
>     2   {
>     3
>     4      config->setGroup("General Options");
>     5
>     6      // bar status settings
>     7      bool bViewToolbar = config->readBoolEntry("Show Toolbar", true);
>     8      view_menu->setItemChecked(ID_VIEW_TOOLBAR, bViewToolbar);
>     9      if(!bViewToolbar)
>     10       enableToolBar(KToolBar::Hide);
>     11
>     12     bool bViewStatusbar = config->readBoolEntry("Show Statusbar", true);
>     13     view_menu->setItemChecked(ID_VIEW_STATUSBAR, bViewStatusbar);
>     14     if(!bViewStatusbar)
>     15       enableStatusBar(KStatusBar::Hide);
>     16
>     17     // bar position settings
>     18     KMenuBar::menuPosition menu_bar_pos;
>     19     menu_bar_pos=(KMenuBar::menuPosition)config->readNumEntry("MenuBar Position", KMenuBar::Top);
>     20
>     21     KToolBar::BarPosition tool_bar_pos;
>     22     tool_bar_pos=(KToolBar::BarPosition)config->readNumEntry("ToolBar Position", KToolBar::Top);
>     23
>     24     menuBar()->setMenuBarPos(menu_bar_pos);
>     25     toolBar()->setBarPos(tool_bar_pos);
>     26
>     27     // initialize the recent file list
>     28     recent_files.setAutoDelete(TRUE);
>     29     config->readListEntry("Recent Files",recent_files);
>     30
>     31     uint i;
>     32     for ( i =0 ; i < recent_files.count(); i++){
>     33       recent_files_menu->insertItem(recent_files.at(i));
>     34     }
>     35
>     36     QSize size=config->readSizeEntry("Geometry");
>     37     if(!size.isEmpty())
>     38       resize(size);
>     39   }

> `

As we have seen in one of the above code parts, the first action our constructor does was:

> `

>     config=kapp->getConfig();

> `

which sets the `KConfig` pointer `config` to the application configuration. Therefore, we don't have to care for the location of the configuration file. Indeed, the file is, according to the KDE File System Standard (KDE FSS), located in `$HOME/.kde/share/config/`; we will have a closer look about the KDE FSS in a later step when we're setting installation locations for project files. As the config file is placed in the user's home directory, each user has it's own appearance of his application except for values that are located in a system wide configuration file that can optionally be created and installed by the programmer in the KDE directory. But, although this could help in some cases, we should avoid any dependency of our application towards the existing of file entries. Therefore, all read methods provided by `KConfigBase` allow to add a default value to be used when the entry doesn't exist. Another thing important to a programmer is that the configuration file is stored in plain text, and this is for some reasons as well as you have to watch some criteria:

- the user is able change the configuration file by a plain text editor
- if the user wants to change values by hand, the entries should be very transparent to determine their purpose
- for entries that have to be saved, but are critical in terms of security like passwords, you have to look for a proper solution to ensure the security.

Now that we know the basics, we're going to analyze the code. As said, we only have to use our config pointer to access the values. First, in line 4, we set the current group to "General Options". This indicates that the values used are somewhat general attributes for the application. Then we read the values for the toolbar and statusbar- these have to be saved when the application closes to restore their status again when the user restarts the program. As the bars can only be on or off, we use a boolean value, therefore, our method is `readBoolEntry()`. The process is identical for both bars, so we only have a look at the lines 7-10 to watch what's happening for the toolbar. First, we read the value into the temporary variable `bViewToolbar` at line 7. The value name in the file is "Show Toolbar" and, if the value is not present (which would be the case the first time the application starts), the default value is set to `true`. Next, we set the checkmark for the menuentry for en-/disabling the toolbar by this value: we call `setItemChecked()` on the view menu, entry `ID_VIEW_TOOLBAR` with our attribute. Finally, we set the toolbar to use the value. By default, the toolbar is visible, therefore, we only have to do something if `bViewToolbar` is `false`. With `enableToolBar()` (line 10) we're setting the bar to hide itself if it is disabled.

Next, we have to read the bar positions. As the user might have changed the bar position by dragging a bar with the mouse to another view area, these have to be saved as well and their status restored. Looking at the classes `KToolBar` and `KMenuBar`, we see that the bar positions can be:

> `

>     enum BarPosition {Top, Left, Bottom, Right, Floating, Flat}

> `

As this value has been written in a numeric value, we have to read it with `readNumEntry()` and convert it to a position value. With `setMenuBarPos()` and `setBarPos()` we tell the bars where to show up.

Now you probably have noticed that our "File" menu contains a menu for recently used files. The filenames are stored in a list of strings, which has to be saved on application closing and now has to be read in to restore the menu. First, we initialize the list with the entries stored by using the `readListEntry()`. Then, in a `for-`loop, we create a menu entry for each list item.

Finally, we only have to take care for the geometry of our window. We read in the appearance by a `QSize` variable containing an x and y value for width and height of the window. As the window is initialized by `KTMainWindow`, we don't have to take care for a default value and only will use `resize()` if the entry is not empty.

What is left to explain on application construction is that we initially have to disable available user commands that shouldn't be available if some instances don't match the needed criteria. These are file saving and operations that are using the clipboard. During the application's lifetime, we have to take care of these several times, but which is quite easy. The framework only gives us two methods to enable/disable menubar and toolbar items with one method call at the same time.

### Executing

During the past section, we have only monitored what happens during the constructor call of our `KScribbleApp` instance providing us the main window. After returning to the `main()` function, we have to call `show()` to display the window. What is different from any `KApplication` or `QApplication` here is that when we're using `KTMainWindow` as the instance for our main widget, we don't have to set it with `setMainWidget()`. This is done by `KTMainWindow` itself and we don't have to take care of that. The only thing left then is to interpret the command-line. We get the command-line option and ask, if `int argc` is > 1, which indicates that the user called our application with `kscribble filename_to_open`. Our window is then asked to open the file by it's name and calls `openDocumentFile()` with the filename.

The last line of the `main()` function does the known job: it executes the application instance and the program enters the event loop.

Now, in section The main() Function, we started to separate the execution process by `if( app.isRestored() )` and described the usual invocation process. The following now gives an introduction to session management and how our application makes use of this.

### Invocation by Session Management

As we said, the `main()` function tests, if the application is invoked by the session manager. The session manager is responsible to save the current status of all open application windows on the user's desktop and has to restore them when the user logs in the next time, which means that the application is not started by the user but automatically invoked. The part of the code which is executed was:

> `

>     6    if (app.isRestored())
>     7    {
>     8       RESTORE(KScribbleApp);
>     9    }

> `

In The main() Function, we stated that we test the invocation by asking `app.isRestored()`. Then line 8 gets executed. It looks like a simple statement, but in fact this will result in a complex execution process which we want to follow in this section.

RESTORE() itself is a macro provided by `KTMainWindow`. It expands to the following code:

> `

>     if (app.isRestored()){
>       int n = 1;
>       while (KTMainWindow::canBeRestored(n)){
>         (new KScribbleApp)->restore(n);
>         n++;
>       }
>     }

> `

This will restore all application windows of the class `KScribbleApp` by creating the instances and calling `restore()` to the new window. It is important to realize that if your application uses several different widgets that inherit `KTMainWindow`, you have to expand the macro and determine the type of the top widgets by using `KTMainWindow::classNameOfToplevel(n)` instead of the class `KScribbleApp`. The `restore()` method then reads the part of the session file that contains the information about the window. As `KTMainWindow` stores all of this for us, we don't have to care for anything else. Only information that belong to our specific instance of `KScribbleApp` has to be found then. Usually this would be a temporary file that we created to store the document or other initialization that we might need. To get to this restoration information, we only have to overwrite two virtual methods of `KTMainWindow`, `saveProperties()` and `readProperties()`. The information we have to save on session end is if the currently opened document is modified or not and the filename. If the file is modified, we will get a temporary filename to save it to. On session beginning, this information now is used to restore the document contents:

> `

>     void KScribbleApp::readProperties(KConfig*)
>     {
>       QString filename = config->readEntry("filename","");
>       bool modified = config->readBoolEntry("modified",false);
>       if( modified ){
>         bool b_canRecover;
>         QString tempname = kapp->checkRecoverFile(filename,b_canRecover);

>         if(b_canRecover){
>           doc->openDocument(tempname);
>           doc->setModified();
>           QFileInfo info(filename);
>           doc->pathName(info.absFilePath());
>           doc->title(info.fileName());
>           QFile::remove(tempname);
>         }
>       }
>       else if(!filename.isEmpty()){
>       doc->openDocument(filename);
>       }
>       setCaption(kapp->appName()+": "+doc->getTitle());
>     }

> `

Here, the line `kapp->checkRecoverFile()` seems a bit strange, as `b_canRecover` is not initialized. This is done by the method which sets it to `true`, if there is a recover file. As we only saved a document in a recover file if it was modified, we set the modified bit directly to indicate that the information hasn't been saved to the belonging file. Also we have to take care that the recover file has another filename than the original file which was opened. Therefore, we have to reset the filename and path to the old filename. Finally, we have the information we wanted to recover and we can delete the temporary file by the session manager.

Summary:

During this chapter, you got to know how the application starts either by normal user invocation or by the session manager. We went through the code to learn how the parts of the visual interface of the application are constructed as well as how to initialize attributes by configuration file entries. Now you can execute the framework application to test these functions and see how the program window reacts.

## 3.4 Additional Contents of KDevelop Projects

Besides the source code provided, KDevelop projects contain a lot of other additional parts that are of interest to the developer. These are:

- a program icon
- a program mini-icon
- a .kdelnk file
- a sample SGML-documentation file
- a set of API-documentation generated from the framework source

Except the API-documentation, these elements of the project will be installed together with the application binary. As the project framework has to be as open as possible, you have to adapt these parts towards your project goals. These are first to edit the icons provided. This will give your application a unique identifier by which the user can determine your application visually in window manager menus. The .kdelnk file then is a file that installs your application into `kpanel` in the `Applications` menu. This has to be edited by setting the installation path which will be discussed later in this handbook. Finally, the documentation that you will provide to the user is written in SGML. This makes it very easy to create several different output from the same source. By default, KDevelop offers to create a set of HTML files from this source, for KDE-projects this will automatically use the `ksgml2html` program to add a consistent KDE look and feel to the documentation. In a later section, we will see how the SGML source is edited and what we have to watch for installation on the end-user.

Finally, the API (Application Programming Interface) documentation allows you and other developers to quickly get into the code and use the classes without having to guess what purpose each class is for. We will learn how to extend the API documentation in a later step, for now it lasts to know that the documentation is generated by the `KDoc` program, which processes the header files and creates the HTML output, therefore all documentation is placed in the headers.

Következő Előző Tartalom