Das KDevelop-Programmierhandbuch: Leitfaden zur C++-Anwendungsentwicklung für das K Desktop Environment (KDE) mit Hilfe der KDevelop-IDE in der Version 1.2 | ||
---|---|---|
Zurück | Kapitel 3. Creating new Applications | Vor |
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.
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.
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.
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 accelerators 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.
As shown above, the menubar of KScribble is created by the method initMenuBar(). There, we create a set of QPopupMenus 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) |
#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 Configuring Menubars and Toolbars.
The creation of toolbar s now is even simpler than that of menubars. As KTMainWindow already provides toolbar s, 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 toolbar s 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 toolbar s in section Configuring Menubars and Toolbars.
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.
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 accelerators 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 accelerators. A lot of users want to have them configurable on one hand and on the other standard accelerators should be the same over all applications. Therefore, the KDE control center offers configuring standard keyboard accelerators globally by using the KAccel class. Additionally, the KDE libraries contain a widget that lets users configure application specific keyboard shortcuts 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 accelerators are all present in the menubar, we have to change the accelerators for the popup entries. Finally we call readSettings(), which reads the current settings from the root window of KDE containing the configuration of standard accelerators, then the settings for accelerators 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 accelerators by a configuration dialog, see Configuring Menubars and Toolbars for that part of the development process.
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, toolbar s 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 accelerators or the mouse on menubars and toolbar s.
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:
an MDI interface for KTMainWindow
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/toolbar s.
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.
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.
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()); } |
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.