<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook V3.1-Based Variant V1.0//EN" [
 <!ENTITY kdevelop "<application>kdevelop</application>">
 <!ENTITY % addindex "IGNORE">
 <![ %addindex; [ <!ENTITY docindex SYSTEM "docindex.docbook"> ]]>
]>

<book lang="en">
<bookinfo>
<title>KDE Application Tutorials</title>
<subtitle>The KDE Application Tutorials Handbook for the KDevelop Integrated Development Environment</subtitle>
<authorgroup>
<author>
<firstname>Ralf</firstname>
<surname>Nolden</surname>
<affiliation>
<orgname>The KDevelop Team</orgname>
<address><email>Ralf.Nolden@post.rwth-aachen.de</email></address>
</affiliation>
</author>
</authorgroup>
<date>21/03/2000</date>
<releaseinfo>1.02.00</releaseinfo>
<abstract>
<para>The KDE Application Tutorials Handbook for the KDevelop Integrated Development Environment, a tutorial collection on how to create KDE
applications by examples. This handbook itself is part of the KDevelop Integrated Development Environment and is therefore also
licensed under the GNU General Public License; see <link linkend="Copyright">Copyright</link> for more information.</para>
</abstract>
<keywordset>
<keyword>KDE</keyword>
<keyword>KDevelop</keyword>
<keyword>tutorials</keyword>
</keywordset>
</bookinfo>

<chapter id="introduction">
<title>Introduction</title>

<para>As KDE is currently becoming a standard for Unix System's desktops, more and more developers want to take advantage of the well-written
API that the KDE project includes to create new applications that offer a GUI as well as an easy use of the classes provided with
Qt/KDE. The KDevelop project therefore intends to provide an IDE which enables developers to create and extend their applications
rapidly with the main focus on KDE. As beginners always have difficulties to get started, this handbook tries to explain certain
aspects of application creation for KDE by describing how to create running applications with the sample code included with the Qt
library by using the KDevelop IDE as well as creating KDE applications by examples.</para>

<para>Therefore, the next chapter will explain you how to create a project with KDevelop that is already a complete application; then we'll
walk on to the Qt online documentation containing the sources for examples on how to use the Qt library.</para>

<para>This will give you a first understanding how the underlying Qt library functions and how to use the classes provided with it; Qt is the
point where to get started as KDE is on top of the Qt library classes and KDE applications make a wide use of them.</para>

<para>Further, we'll cover on how to create a KDE application with KDevelop's application wizard. From the minimum framework provided,
we will create a running example KDE application that makes use of the KDE libraries and will explain the different parts of a KDE
application in detail.</para>

<para>The KDevelop Team wishes you success in learing about KDE and Qt with this handbook and hope it will enable you to become another
programmer contributing to the KDE project.</para>

<sect1 id="what-you-should-know-already">
<title>What you should know already</title>

<para>This handbook requires a base knowledge about the C++ programming language as a minimum to understand the code examples and their
functionality. Further, it is assumed that you have read <ulink url="index.html">The User Manual to KDevelop</ulink> as well as
<ulink url="programming.html">The KDevelop Programming Handbook</ulink> and know how the KDevelop IDE works in general and what
generated projects are.</para>
</sect1>
</chapter>

<chapter id="getting-started">
<title>Getting Started</title>

<para>Getting Started- that means, you should have set up KDevelop to work correctly first. Ensure you have access to the Qt online reference
documentation by the "Help" menu or the according book in the documentation tree. When looking a the first page of the Ot
documentation, you will see that it contains a link to a section "Tutorials" and "Examples". Under "Tutorials", you will find a 14-step
introduction into using the Qt library to build applications. On the other hand, "Examples" will lead you to a list of example
programs that Qt includes and takes for example usage reference in certain classes. Now, if you are an absolute beginner, you should be
comfortable with the idea behind Qt- providing a library that offers:</para>

<para><itemizedlist>
<listitem>
<para>a base application class (<literal remap="tt">QApplication</literal>)</para>
</listitem>
<listitem>
<para>a widget library for graphical user interfaces</para>
</listitem>
<listitem>
<para>a set of additional helper classes for graphics, file and data handling</para>
</listitem>
<listitem>
<para>the signal-slot mechanism for object communication</para>
</listitem>
<listitem>
<para>event controlling by event loops and virtual methods</para>
</listitem>
</itemizedlist>
</para>

<para>If that doesn't tell you anything right now, you should read about the Qt library in <ulink url="programming-2.html#ss2.1">The KDevelop Programming Handbook</ulink> where the basics for a Qt application are described in detail. There, you will also find and
introduction about the difference between a Qt and a KDE application, which is not much at the first level of the Qt tutorial step.</para>

<para>Now, to learn about Qt first would be a good start for programming KDE applications as well, so we will create a project that will
produce the tutorial applications after editing.</para>

<para>To do this, start with opening KDevelop and select "Project"-"New" from the menubar. The application wizard opens and you have to
select which type of project you want to build. For Qt applications, you would choose the Qt application, but we will use the mini-KDE
project to build our examples. The reason is simple: the Qt tutorials don't contain much code, and  by choosing the Qt application in
the application wizard, we would have to do a lot of changing on the provided code to create the "true" examples.</para>

<para>But as the mini-KDE application only consists of an empty main window but also checks for the Qt library and header files, it will fit
for our purposes and doesn't require much changes.</para>

<para>When entering the next wizard page, we have to enter the project-specific information. There, we can leave out any KDE-specific
additions such as a mini-icon and a main icon, documentation etc. We just fill in the information that will be useful for building a
suitable base for the tutorials. As the structure of the tutorial steps builds the next step on the previously created code, we can
start with <literal remap="tt">MyWidget</literal> as the project name. This will already build us the base class that is needed in chapter 4, so we don't have
to care for that later. As we want to start with the first tutorial step, we will use '1' as the project number, which we can increase
later when moving to the next step.</para>

<para>Then, fill in the other required information such as your name, email address and project directory. That would be all we need and we
can flip to the next page. Insert your header for the project's header file here or use the given example. The same is done with the
4th page, where the same has to be set for the source files. Finally, the last page is entered, where you have to press "Create" to
build the project. If the button is not available, start at the first page again and make sure you didn't miss to insert any required
information.</para>

<para>If everything went OK, the project has been build and the messages window in the application wizard will have "READY" at it's last
line.  Select "Close" to finish the wizard.</para>

<para>When looking at the treeview on the left, you will see that the project alreay includes a class "MyWidget"- this is because we chose
this to be the application name. Further, when opening "Functions" in the classviewer's "Globals" folder, you will see that the
application already contains a <literal remap="tt">main()</literal> function where the execution will start.</para>

<para>Now the application is already set up. You could do an "Execute" or "Make", but as we want to create the tutorial applications, we can
go ahead and change the code first to create our tutorial examples- which we'll do in the next step.</para>
</chapter>

<chapter id="building-the-qt-tutorials">
<title>Building the Qt Tutorials</title>

<sect1 id="step-one-hello-world">
<title>Step One: Hello World!</title>

<para>As the previous chapter explains, we created a project called <literal remap="tt">MyWidget</literal>- now, when looking at the first tutorial page of the Qt
documentation, you will see that the first application only uses a <literal remap="tt">main()</literal> function. Therefore, we only have to change the given
<literal remap="tt">main()</literal> function of our generated project to get the first tutorial step running.</para>

<para>To access the <literal remap="tt">main()</literal> function, select it from the classviewer's "Globals" folder, which contains a subfolder for "Functions" where
all non-class functions are listed. This will open the file <literal remap="tt">main.cpp</literal> in the "C/C++ Files" window and the editing cursor is already
set in front of the first line of <literal remap="tt">main()</literal>.</para>

<sect2 id="what-to-change-first">
<title>What to change first</title>

<para>We could start changing the given code towards the desired one which is listed in the tutorial- but it can be even easier. Select the
contents of the <literal remap="tt">main()</literal> function with the mouse or by holding the Shift-key pressed while selecting the lines with the down-arrow
key. Then select "Cut" from the "Edit" menu or press the DEL-key to remove the selected code. This will clear the main function and we
can start from scratch to build our first tutorial.</para>
</sect2>

<sect2 id="entering-the-tutorial-code">
<title>Entering the tutorial code</title>

<para>Now, to insert the code from the tutorial, you could first read it carefully and try to remember which line contained which code. Then,
after reading the tutorial's line-by-line description, you could use it as an example to write the tutorial by yourself.</para>

<para>Those who want to go the easy way: select the contents of the listed <literal remap="tt">main()</literal> function in the documentation browser and press the
"Copy"-icon from the toolbar. Optionally, you could use the context-menu or the according entry in the "Edit" menu. This will copy the
selection into the clipboard and, after switching back to the sourcecode window, can be inserted with "Paste" (either from the context
menu, the toolbar icon or the according entry in the "Edit" menu).</para>

<para>Finally, add the according header files outside the <literal remap="tt">main()</literal> function:</para>

<para><screen>#include &lt;qapplication.h&gt;
#include &lt;qpushbutton.h&gt;
</screen>
</para>

<para>That's it ! The source code is done for your first tutorial application.</para>
</sect2>

<sect2 id="building-hello-world">
<title>Building Hello World!</title>

<para>To build the first tutorial, select "Make" from the "Build" menu or press the according toolbar icon. Optionally, you could choose
"Execute" as well, which results in running the application after the build was successful.</para>

<para>If the build stops due to an error, be assured the fault was on the programmer's side, not on the tutorial ! This would be sad to
search for an error just when you decided to program for KDE and/or Qt, but you can use this to train yourself to use KDevelop. Now, to
find an error, you have two options: either select the according error message in the output window, which will bring you to the
according line the error occured, or press the F4 key; optionally you could use the "Next Error" entry in the "View" menu.</para>
</sect2>

<sect2 id="exercises">
<title>Exercises</title>

<para>You may ask "Exercises ? This one was so simple, I don't need to excercise !", but be assured, things will get more complicated. This
one was the easiest example, but the more complex they will get the more thinking is asked on your side to understand and follow the
tutorial steps. Also the excercises will enable you to learn how to acutally use KDevelop and it's features.</para>

<para>To teach yourself more with the first tutorial, you should try to do the following changes to the given code and see how this will
influence the application behavoir:</para>

<para><orderedlist><listitem>
<para>change the pushbutton to a <literal remap="tt">QLabel</literal>.</para>
</listitem>
<listitem>
<para>use <literal remap="tt">setText()</literal> instead of directly setting the text in the pushbutton's constructor</para>
</listitem>
<listitem>
<para>use <literal remap="tt">QPushButton::resize ( const QSize & s )</literal> instead of setting the size by width and height directly. Create a <literal remap="tt">QSize</literal>
instance first that contains the size</para>
</listitem>
<listitem>
<para>play with the sizes to see which is width and heigth</para>
</listitem>
</orderedlist>
</para>
</sect2>
</sect1>

<sect1 id="step-2-and-3-extensions">
<title>Step 2 and 3: Extensions</title>

<para>The tutorial steps 2 and 3 are extending the facilities of the first tutorial. There, you will learn about signals and slots as well as
parent-child relationships between widgets.</para>

<para>Now, why is it important to learn about that ? Well, the signals and slots are building the technology which make the difference when
comparing the Qt library to other widget library.  Whenever you will hit an implementation problem where you want to communicate
between objects, this will make your work incredible short and nothing serious will happen if something went wrong, even misconnections
don't lead to a segmentation fault. This is the strength of the Qt library and a lot of KDE/Qt developer's won't want to miss it
anymore because of it's flexibility. Understanding the signal/slot mechanism therefore is essential to create any KDE/Qt application.</para>

<para>Another thing to watch out for is the fact that the button terminates the <literal remap="tt">QApplication</literal> instance by calling <literal remap="tt">quit()</literal>. You will
meet <literal remap="tt">quit()</literal> again when working with <literal remap="tt">QApplication</literal>'s KDE-pendant, <literal remap="tt">KApplication</literal></para>
</sect1>

<sect1 id="writing-your-own-widgets">
<title>Writing your own Widgets</title>

<para>Now, after having successfully modified and extended the <literal remap="tt">main()</literal> function, you will hit the point to create a new widget- a unique
widget that only can be constructed with the class you're about to write in step 4.  Actually, the class has been created by the
application wizard for you, so there is not much to add- just that you have to insert the code that lesson 4 places in the
<literal remap="tt">main.cpp</literal> file into the <literal remap="tt">MyWidget</literal> classes' header and implementation file. Fortunately, you only have to add the implementation
to the constructor code.</para>

<para>To access the constructor, select it from the classviewer. This will automatically bring you to the implementation where you have to
add the code.</para>

<para>Here, you only have to watch out for the include of <literal remap="tt">qfont.h</literal>- <literal remap="tt">QFont</literal> is only used in the constructor code of <literal remap="tt">MyWidget</literal>.
Therefore, add <literal remap="tt">&num;include &lt;qfont.h&gt;</literal> to the top of the <literal remap="tt">mywidget.cpp</literal> file.</para>

<para>To run the new application, just hit "Execute". This will result in compling the changes after an autosaving.</para>
</sect1>

<sect1 id="extending-the-widget-class">
<title>Extending the Widget Class</title>

<para>Now, in lesson 5 the target is to extend the widget class. Therefore, you will learn about virtual functions as well. Here, the
<literal remap="tt">resizeEvent()</literal> is reimplemented. What is important to learn here (besides extending a class) is that Qt works with user events by
virtual functions that take an event as a parameter. Therefore, you should make yourself comfortable with the following classes of the
Qt library:</para>

<para><itemizedlist>
<listitem>
<para>QEvent</para>
</listitem>
<listitem>
<para>QChildEvent</para>
</listitem>
<listitem>
<para>QCloseEvent</para>
</listitem>
<listitem>
<para>QFocusEvent</para>
</listitem>
<listitem>
<para>QKeyEvent</para>
</listitem>
<listitem>
<para>QMouseEvent</para>
</listitem>
<listitem>
<para>QMoveEvent</para>
</listitem>
<listitem>
<para>QPaintEvent</para>
</listitem>
<listitem>
<para>QResizeEvent</para>
</listitem>
<listitem>
<para>QTimerEvent</para>
</listitem>
</itemizedlist>
</para>

<para>Whenever writing custom widgets, especially view areas of applications, you will have to overwrite the default implementation of the
widget's event-methods by your own for certain events that have to be processed. Those would be e.g.</para>

<para><literal remap="tt">virtual void mousePressEvent ( QMouseEvent * )</literal> for processing a mouse event to pop up a context menu. In the implementation, you
have to insert a formal parameter into the function header, most developers either use <literal remap="tt">event</literal> or just <literal remap="tt">e</literal> as the parameter name.</para>

<para>Then, you can process the event's parameters. For a <literal remap="tt">QMouseEvent</literal>, you have to ask if the right, middle or left mouse button was the
one that caused the event.</para>
</sect1>

<sect1 id="adding-a-new-class">
<title>Adding a new Class</title>

<para>With lesson 6, you will have to add a class to the tutorial application. Now, normally you would think "I just will create a header
file and an implementation file- then I'm done"- but KDevelop will make it easier for you. When adding a class, you should always use
the Classgenerator. This will do all the work for you so you just will have to add the specific code.</para>

<para>To add a class with the Classgenerator, choose "Project"-"New Class", which will open a dialog to enter all needed values for the class
you want to add. The first thing we have to insert is the classname. The tutorial name this <literal remap="tt">LCDRange</literal>, so this has to be inserted
first. Then proceed to the baseclass. The baseclass is the one that the new class inherits. Looking at the tutorial (Qt 1.42), this is
<literal remap="tt">QWidget</literal>.  Now, as most GUI classes to add would inherit <literal remap="tt">QWidget</literal>, the Classgenerator makes it even easier. Leave the baseclass
empty and instead check "QWidget child class" in the "Additional Options" section. This will automatically add the required
<literal remap="tt">Q_OBJECT</literal> macro to the headerfile to add slots and signals later (which is required in chapter 7).</para>

<para>As the filenames are automatically inserted, you don't have to care for that. The only thing that we would suggest adding is the
documentation. It is always a good style to add a desciptive documentation to the class, especially as the classname <literal remap="tt">LCDRange</literal>
doesn't inform you specifically about the purpose of the widget.</para>
</sect1>

<sect1 id="step-7-14">
<title>Step 7-14</title>

<para>For the rest of the tutorial steps, you are prepared and know everything you have to know- adding the classes required and doing the
changes.</para>

<para>After each change you should do a new build and check your code for errors. Run the application and follow it's execution;
additionally, you should play with KDevelop's options for "Build"- execute the application with a commandline argument such as
--geometry and debug it with KDbg in the Tools-window. Then you should be able to proceed with the Qt examples that are provided with
Qt. In the next chapter, you will be introduced into development for KDE 2 by an example application <emphasis>KScribble</emphasis>, which is a small
drawing application that will show you the concepts of application design and how to implement you own program.</para>
</sect1>
</chapter>

<chapter id="creating-kde-applications">
<title>Creating KDE Applications</title>

<para>This handbook will try to teach you KDE/Qt application design by creating an example application step-by-step. The major intention is
to gain a certain understanding how two of the most important ideas of graphical user interface programming work: the document-view
model and the multiple document interface (MDI) model.</para>

<para>By this, you will also get a good idea of concepts that are coming up ahead: KDE 2 and the Qt 2.1 library, bringing new enhancements
and technology to the programmer's hands that will improve application design and widen the possibilities that are open to use.
Keywords such as widget themes, Inter-Process Communication and embedded applications are popularly talked about and will allow even
more than one can think about right now.</para>

<para>Therefore, KDevelop 1.1, still based on KDE 1.1.x, provides developers some of the most requested possibilities: developing
applications for KDE 2, while still running a stable desktop environment. This also implies that this tutorial will be based on a KDE 2
application that already makes use of Qt-2.1, offering a solution for the mentioned MDI model - which we provide as a ready-to-go frame
application. During this tutorial you will get a clear insight on how it works and this will also make it much easier to understand the
document-view model as well - which is hard to understand even for advanced programmers. That will help you create other type of
applications, which use the document-view model even if they only contain one window and you wouldn't obviously see a need for it. But
let's just go inside it - you will see what's coming up on you and how easy it actually is.</para>

<para>The further chapters of the tutorial cover the following several steps:
<itemizedlist>
<listitem>
<para>installing KDE 2 (libraries) and setting up KDevelop to create KDE 2 applications.</para>
</listitem>
<listitem>
<para>understanding the basic concepts of MDI and document-view model.</para>
</listitem>
<listitem>
<para>creating the generic framework for <emphasis>KScribble</emphasis> and explaining the concept of the framework.</para>
</listitem>
<listitem>
<para>implementing the document class, who will provide the interface to read and write the document data as well as maintaining the
document.</para>
</listitem>
<listitem>
<para>implementing the view class to visualize the data. This class also takes over the interaction with the user.</para>
</listitem>
<listitem>
<para>adding dialogs to set the pen width and color of the pen to draw with, including their integration into the toolbar and menubar</para>
</listitem>
<listitem>
<para>changing the view from the default <literal remap="tt">QWidget</literal> inheritance to a scrollable view.</para>
</listitem>
</itemizedlist>
</para>

<para>The tutorial example itself is based on the code found in the scribble example application that comes with the Qt library. The source
of the original example can also be found within the Qt documentation, section examples.</para>
</chapter>

<chapter id="installing-kde-2">
<title>Installing KDE 2</title>

<para>In this chapter we will first go through a couple of steps that will help you set up KDevelop for creating KDE 2 / Qt 2.x
applications. The requirements are:
<itemizedlist>
<listitem>
<para>a current Qt-2.1 snapshot available at <ulink url="http://www.troll.no">http://www.troll.no</ulink></para>
</listitem>
<listitem>
<para>a current KDE 2 snapshot of the packages <literal remap="tt">kdesupport</literal> and <literal remap="tt">kdelibs</literal></para>
</listitem>
</itemizedlist>
</para>

<para>Both the Qt-2.1 and KDE 2 libraries are currently under development but are to be expected as final and changes to be expected for the
final versions are marginal and may not even affect your application. This means you're getting to know the newest libraries coming up-
and you're in charge of a valuable time-saving to create your application even before the libraries are completed.</para>

<para>The following sections describe what to do in detail within three steps:
<itemizedlist>
<listitem>
<para>installing the Qt library</para>
</listitem>
<listitem>
<para>installing the KDE libraries</para>
</listitem>
<listitem>
<para>setting up KDevelop</para>
</listitem>
</itemizedlist>
</para>

<para>Then you're ready to go and we'll proceed with creating the first step of the example program <emphasis>KScribble</emphasis> in the next chapter.</para>

<sect1 id="setting-up-qt-2.1">
<title>Setting up Qt-2.1</title>

<para>This first step will lead you to the minimum requirement to follow our example- which implies that there is a possibility to avoid
installing KDE 2 if you're only interested in creating Qt-only programs. As Qt-2.1 contains all needed classes and KDevelop providing a
project template for multiple document interface applications for Qt-2.1 as well based on the same framecode that is used in the KDE 2
template, you should have no problem at all following the example with a Qt-only application.</para>

<para>Now, to get the newest Qt library, get a recent version from the Troll Tech website at <ulink url="http://www.troll.no">http://www.troll.no</ulink>, section download. Then change to root and copy the tarball to the directory where your currently used Qt
version is placed as well - on a SuSE Linux system this would be <literal remap="tt">/usr/lib</literal>, where you should have a directory for either
qt-1.44 or qt-2.0.2, depending on the distribution version. Untar the library sources with</para>

<para><literal remap="tt">tar zxvf qtxxx.tar.gz</literal></para>

<para>depending on the name of the tarball. Then set the environment variable <literal remap="tt">QTDIR</literal> to point to the directory where your new Qt-2.1
library is placed with e.g. <literal remap="tt">setenv QTDIR=/usr/lib/qt-2.1</literal>. Another solution would be to move the qt-1.44 directory, usually
just named qt, to qt-1.44 and create a symlink that for now has to point to qt-2.1, after the library is build, change it back to
point to the qt-1.44 directory.</para>

<para>Change to the Qt-2.1 directory and type:
<screen>./configure
make
</screen>
</para>

<para>You can change back your QTDIR variable to point to your qt-1.44 installation after successfully
compiling the Qt-2.1 library.</para>

<para>Now switch to the directory <literal remap="tt">/usr/lib/qt-2.1/lib</literal> and copy the created libraries to <literal remap="tt">/usr/lib</literal> <emphasis remap="bf">exept</emphasis> the
symbolic link <literal remap="tt">libqt.so</literal>.</para>

<para>Then you're done with Qt and you can proceed to the next step, setting up KDE 2 libraries. If you want to develop for Qt only, proceed
to section Setting up KDevelop.</para>
</sect1>

<sect1 id="setting-up-kde-2-libraries">
<title>Setting up KDE 2 Libraries</title>

<para>The second step leads you to install the needed KDE libraries. Download a recent snapshot of the library packages <literal remap="tt">kdesupport</literal>
and <literal remap="tt">kdelibs</literal> from <ulink url="http://www.kde.org">http://www.kde.org</ulink>. Then things will go a bit easier than for the
Qt library. Just untar the sources to your directory and change to the kdesupport directory first. There, type:</para>

<para><screen>./configure --with-qt-dir=/usr/lib/qt-2.1 --prefix=/opt/kde2
make

and as root:

make install
</screen>
</para>

<para>This will install the kdesupport package to the directory given in the --prefix option and compile using the new build Qt-2.1 library,
to which you give the path to configure by the option --with-qt-dir.</para>

<para>The same applies to the kdelibs package, just change to the kdelibs directory and type the same as above for the kdesupport package.</para>

<para>Then you're done with setting up the requirements for the needed libraries to build a KDE 2 application.</para>

<para>As a note aside, the KDE library package contains support for automatic HTML documentation generation, which you can easily get by
installing KDoc, version 2.x. Then type <literal remap="tt">makekdedoc</literal> in your kdelibs directory and KDoc will create the HTML documentation in a
subdirectory <literal remap="tt">srcdoc</literal>. In KDevelop you can access the KDE library documentation online with setting the KDE library
documentation path in the KDevelop Setup dialog to point to <literal remap="tt">kdelibs/srcdoc</literal>. On the same way set your Qt-Online documentation
path in KDevelop to point to <literal remap="tt">/usr/lib/qt-2.1/html</literal>, so you can access the documentation right away from within KDevelop.</para>

<para>If you successfully installed the Qt-2.1 and KDE 2 libraries, you're just one step away from creating your first KDE 2 application, you
just need to give KDevelop the needed information where you installed the libraries - which we'll do in the next section.</para>
</sect1>

<sect1 id="setting-up-kdevelop">
<title>Setting up KDevelop</title>

<para>This part is somewhat the easiest in the whole configuration process for KDE 2 development: Setting up KDevelop. Just open KDevelop,
then select "Options"-&gt;"KDevelop Setup" to access the KDevelop configuration. There, change to the last page, titled "Path". This page
contains two entry fields, one for the Qt library and one for the KDE 2 libraries. As you have successfully installed everything,
select <literal remap="tt">/usr/lib/qt-2.1</literal> for the Qt-2.x library path and <literal remap="tt">/opt/kde2</literal> for the KDE 2 library path. Press the "OK" button
and you're done.</para>

<para>Users that only want to develop for Qt-2.x (including the current Qt-2.0.2 version) just have to set the Qt-2.x library path.</para>

<para>Now we can proceed to the next chapter where we will create the first step of the <emphasis>KScribble</emphasis> example.</para>
</sect1>
</chapter>

<chapter id="application-concepts">
<title>Application Concepts</title>

<para>This chapter now introduces you into the ideas of the application models mentioned already: the MDI (Multiple Document Interface) and
the Document-View model. A basic introduction into the Document-View model and the generating of a project with KDevelop is already
given in <ulink url="../programming/index.html">The KDevelop Programming Handbook</ulink>, but based on a Single Document Interface
(SDI). In any case, you should be familiar with the basics of the KDE and Qt classes that are explained in <ulink url="../kde_libref/index.html">The KDE Library Reference Guide</ulink>, there the base classes of the underlying libraries are
explained in detail and how to use them, with an additional description of the Qt signal-slot mechanism and event handling.</para>

<para>As a guideline, you should know already:
<itemizedlist>
<listitem>
<para>that every KDE application needs one instance of <literal remap="tt">KApplication</literal> (for a Qt application one instance of
<literal remap="tt">QApplication</literal>)</para>
</listitem>
<listitem>
<para>generally a main window is inherited from <literal remap="tt">KTMainWindow</literal> for KDE (for a Qt application inherited from
<literal remap="tt">QMainWindow</literal>)</para>
</listitem>
<listitem>
<para>that events (e.g. keyboard presses or mouse movements) are sent to the application by the underlying window system</para>
</listitem>
<listitem>
<para>that <literal remap="tt">QWidget</literal> as the base class for all visible Graphical User Interface (GUI) components provides specialized event
handlers by virtual functions that filter out the according events by reimplementing <literal remap="tt">QObject::event()</literal></para>
</listitem>
<listitem>
<para>that the Qt signal-slot mechanism means basically that a component can send out signals and a receiver can connect to those
signals to process some actions</para>
</listitem>
<listitem>
<para>that this mechanism avoids inheriting classes for GUI components because the signals inform you about e.g. mouse presses  where
appropriate for a wiget</para>
</listitem>
</itemizedlist>
</para>

<sect1 id="the-document-view-model">
<title>The Document-View Model</title>

<para>The Document-View Model is one of the most basic concepts in application design that rely on graphical user interfaces. Therefore, a
certain understanding of the why is needed to see that although the programmer has other possibilities, it makes sense to make use of
it. But first let's follow the usual design of a typical KDE / Qt application:</para>

<para>Your application instance provides the first connection to your application and is the starting point of the event handling for a
program. The program itself represents itself to the user by a Graphical User Interface which is most often called a main-window. The
main window then provides the appropriate functions for the user such as key accelerators, a menubar, toolbar and a statusbar. In the
center it contains a so-called "view-area" which means that this part is an instance of another class, usually called a "View". The
view instance is created when the main window gets constructed at program start and has to be set as the view area component with a
method provided by the main window: <literal remap="tt">setView(your_view)</literal> for KDE applications using <literal remap="tt">KTMainWindow</literal>,
<literal remap="tt">setCentralWidget(your_view)</literal> for a Qt application using <literal remap="tt">QMainWindow</literal>. Now, the view obviously is the area that is
responsible for interacting with the user to manipulate the data that it represents. As an example, you could use a
<literal remap="tt">QMultiLineEdit</literal> as a view and you will have an editor. Then you can use the provided slots of the view for connections to
menubar or toolbar command like this:</para>

<para>While creating the menubar, you want to provide a method for the command "cut" in the "Edit" menu:
<screen>  pEditMenu-&gt;insertItem(BarIcon("editcut"), i18n("Cu&amp;t"),view, SLOT(cut()),KAccel::Cut, ID_EDIT_CUT);
</screen>
</para>

<para>This creates a menu item in the "Edit" menu, that, when activated, directly calls the slot <literal remap="tt">cut()</literal> of the instance
<literal remap="tt">view</literal>, whereby we suppose you have created this instance as a <literal remap="tt">QMultiLineEdit</literal> and set as the view area. The
multilineedit's slot gets called and cuts out the selected text as a reaction, the functionality is already provided by the class
itself and so there is no need to inherit from <literal remap="tt">QMultiLineEdit</literal> to create a view area that is capable of such actions. They are
ready to use and make application development very fast- you just need the application instance and the main window including the
connections to your view area and you're done ! That means, a simple editor can be written by creating one single class that defines
the main window behavoir and how to save and read files into the editor - just some basic slots your main view has to implement itself.</para>

<para>But here is the reason why we're now introducing this mysterious Document-View model: You have to provide methods by your own to read
and write the actual files you want to edit with the <literal remap="tt">QMultiLineEdit</literal> view-area <emphasis remap="bf">within</emphasis> the main window's interface.
Yes, this is obviously the easiest thing to do in this case, and the most logic. Now, if we have a look at the files and their contents
as a so-called "Document", which we can subscribe with the attribute of an "Object" in C++ terminology, the next step is just a little
one: If I have a document, a view and a main window- why don't I separate these three objects from each other ? We could easily create
a small class that is responsible for reading in a file into a text stream and then call the view to draw the text visible to the user.
The same applies to saving the file again- the document class should then provide a saving method that retrieves the text from the view
again and saves it as a file. In the example these two methods would be the only actions that are needed to be done by a document
class, because the edit-view already provides all methods basically needed for and editor by slots and you can manipulate the content
of the view by them directly.</para>

<para>Now, the main idea behind the need for this separation into three objects (document, view, main window) instead of the minimum of two,
the view and the main window, is the question: what if I want to give the user the possibility to work with a file by <emphasis remap="bf">two</emphasis> or
even more views ? Such things can even be done within one main window by splitters or deviders containing two view instances which
shall both display <emphasis remap="bf">one</emphasis> file. There it is: the solution can only be that if the user manipulates the file contents in one view,
the other view has to be notified about that and to actualize its contents. Otherwise a bad scenario will occur: if the user closes one
view where he added something at the end of the file which he cutted out at the beginning of the file <emphasis remap="bf">in</emphasis> the other view, the
file will be saved later by still containing the cutted section, because if the second and last view did not recognize this, it still
contains the text without modification from the beginning plus the inserted one. This means that both views have to be synchronized by
their contents they are displaying and that can be done if all views get notified about each action that the user does, which view he
actually uses should be equal. That means that finally a document class is needed to be the one and only holder of the true contents of
the document and is providing the views the possibility to manipulate the contents.</para>

<para>I hope this has given some insight into this model, although in most cases it seems that the programmer can live without it - equally
to just using a provided class as the view area or writing a widget on his own to handle user interaction, as long as you represent
<emphasis remap="bf">one</emphasis> document or file by <emphasis remap="bf">one</emphasis> view area, the view can be responsible for the data as well and only provide methods to
retrieve or set the document contents for actions like reading a file or saving it. The next model to describe, the Multiple Document
Interface, will make a difference at this point- there you will see the actual need and the functionality the Document-View model
provides.</para>
</sect1>

<sect1 id="the-multiple-document-interface-mdi">
<title>The Multiple Document Interface (MDI)</title>

<para>As the last section described the Document-View model, you will probably guess what MDI means. Users that come from other Operating
Systems than Unix/Linux are used to it as well as programmers developing for those platforms. While X-Window applications traditionally
are more targeting towards functionality and stability, Unix users are used to single windows that provide functionality, therefore
even the Document-View model is often not needed to create applications. With Qt as a multi-platform toolkit, developers are having
even more choice - developing for MS Windows(tm) as well as for Unix systems. While on Windows the lack of creating applications that
are capable of handling so-called child windows has been getting to some kind of standard, this is adressed by the Qt 2.1 library, but
on the other hand Unix users can profit from this architecture as well.</para>

<para>What is now the meaning of MDI ? An MDI application generally has the same concept of a usual application that has, as described above,
one application instance and a main window. The view area now make the difference: you don't directly use a view that represents data
and provides interaction to manipulate that data but a view that handles other windows looking like top-level windows. These windows
are now representing the former view area and the main difference is that the interaction chain changes from</para>

<para><screen>application instance -&gt; main window -&gt; view

to

appliation instance -&gt; main window -&gt; view -&gt; active child window
</screen>
</para>

<para>The view now is capable of several actions:
<itemizedlist>
<listitem>
<para>creating as much child windows as the user requests</para>
</listitem>
<listitem>
<para>providing methods to retrieve the currently active child window</para>
</listitem>
<listitem>
<para>maintaining a list of open views</para>
</listitem>
<listitem>
<para>managing the child window behavoir for maximize, minimize such as a window manager does for top-level windows</para>
</listitem>
</itemizedlist>
</para>

<para>Now, you can use "complete" widgets like the <literal remap="tt">QMultiLineEdit</literal> as child windows like for an application that only provides one
window and each child window is responsible for it's own data. That could be described as "Multiple Document Interface", whereby each
child window is equal to a single document. The application then manages the usual actions such as providing methods for the child
window interaction such as cut or copy. Extending this concept with the document-view model enhances the possibilities by far: imagine
that you can open as much windows as you like within the main window and that a new child window can be a new view of a document that
is already shown by another child window. The management for this requires a separation into the already described three objects model,
but doesn't limit the actual number of instances of the documents as well as the views.</para>

<para>Fortunately, Qt 2.1 contains the possibility to create such applications and KDevelop provides you with the according application
frameworks for both, Qt-only programs as well as KDE 2 applications with the same interface methods- so it is adequate wether you want
to develop for one of these. Using KDE 2 interfaces will offer you still more possibilites by library functions as well as
inter-process communication, but those are special aspects that are to cover by separate introductions to these techniques.</para>

<para>Now you are prepared for following the development for KDE 2 - just follow the next chapter to get a first look at the functionality
already provided by KDevelop when creating applications. There, we will generate the framework for our tutorial application
<emphasis>KScribble</emphasis> and describe the practical aspects of programming MDI applications.</para>
</sect1>
</chapter>

<chapter id="the-mdi-framework">
<title>The MDI Framework</title>

<para>If you're already familiar with previous versions of KDevelop, you will know that your IDE uses so-called "frameworks" as a starting
point of application development. These frameworks by default contain a support for a generic document-view model, but were limited to
SDI (Single Document Interface, one window with one view area) model structures. From KDevelop 1.1 on, the application wizard is
capable of a lot more than the previous versions: it offers some new project types especially for those developers that want to stay
up-to-date with the upcoming KDE 2 as well as for Qt 2.1. One major extension are MDI application frameworks for both, KDE 2 and Qt
2.1. These frameworks differ only in the use of some library functions such as file dialogs or message boxes as well as the used
toolbar and statusbar classes - the KDE version uses KDE 2 methods, the Qt version the according Qt functions.</para>

<para>To create the fist step of this tutorial application <emphasis>KScribble</emphasis>, be sure that you have followed the instructions in chapter 2
of this tutorial handbook: setting up KDE 2. If that went all right, nothing can go wrong. If you couldn't set up the KDE 2 libraries,
be sure that you have Qt-2.1 installed and set up KDevelop for that at least. You can follow this tutorial by choosing the according Qt
framework as well (which has the differences as mentioned above: other method calls for dialogs and GUI components).</para>

<sect1 id="creating-an-mdi-framework">
<title>Creating an MDI Framework</title>

<para>Now we will start jumping into development by creating the frame application for <emphasis>KScribble</emphasis>. Start KDevelop and choose
"Project"-"New" from the menubar to invoke the application wizard. On the first page, you will be shown a tree of project types. These
contain KDE and Qt projects. There, select "KDE 2 MDI" from the KDE section. If you only have Qt 2.1 installed, choose "Qt 2.1 MDI"
from the Qt section. Then select "Next", enter "KScribble" as project name and "1.0" as version number. Add your name
and email address into the according fields and you're done. Click on the "Create" button and KDevelop will generate our first version
of <emphasis>KScribble</emphasis> while you can watch the output in the last page of the wizard. Finally, press "Exit" to return to KDevelop. In
the output window you will see an additional message showing that the messages file has been set up for your project, which will be
important to translate <emphasis>KScribble</emphasis> into other languages.</para>
</sect1>

<sect1 id="overview">
<title>Overview</title>

<para>Now it is important to have an understanding where to find what in the generated sources and where your place as a programmer can be
found to make <emphasis>KScribble</emphasis> a unique KDE application.</para>

<para>First of all, in the Classviewer you will see three already created classes, <literal remap="tt">KScribbleApp</literal>, <literal remap="tt">KScribbleDoc</literal> and
<literal remap="tt">KScribbleView</literal>. As explained above, each of them has a certain part within the application; the App class builds the main
window and coordinates user interaction, the Doc class maintains the documents that an application can manipulate; finally the View
class is responsible for user interaction with the child windows and communicates with the document connected to it. This has several
consequences: to make a good use of the provided functionality, the programmer is bound to use the given View class. By default, the
View inherits <literal remap="tt">QWidget</literal> as a minimum requirement, but you're by far not limited to that. But inheriting from another class is
not too much work and finally there is also the possibility to use a <literal remap="tt">QMainWindow</literal> or <literal remap="tt">KTMainWindow</literal> for example as the
class to inherit and use another class that manages the view.</para>

<para>In the next chapter we'll continue with extending <emphasis>KScribble</emphasis> to manage it's documents. After that we'll implement the user
interaction and you will see the first functionality that is unique to our application: we can draw pictures, load and save them.</para>
</sect1>
</chapter>

<chapter id="defining-the-documents">
<title>Defining the Documents</title>

<para>The first step when creating an application based on the Document-View model should always be to think what kind of data the
application has to manage. This decides how the view class will look like but especially how the document clas will read and write data
to and from files and offer methods to manipulate the data. As <emphasis>KScribble</emphasis> will be a simple drawing application that operates on
graphical data, we will use the Qt class <literal remap="tt">QPixmap</literal> for storing our paintings while it is edited. <literal remap="tt">QPixmap</literal> also offers
simple methods to read and write pictures into files, so the serialization of the document data is done in just two lines, one for
reading and one for writing. Further, we need to define a pen that draws into a document, set it's width and color and make it
available for the view class to retrieve the pen - actually you want the view offering the drawing methods, but the document as the
central element for all views has to hold the pen originally, because two views of the same document would otherwise use different
pens!</para>

<para>Therefore to define how our document class should work, we will add one instance of <literal remap="tt">QPixmap</literal>, one of <literal remap="tt">QPen</literal> and edit the
methods <literal remap="tt">newDocument()</literal>, <literal remap="tt">openDocument()</literal> and <literal remap="tt">saveDocument()</literal>.</para>

<sect1 id="adding-the-instances">
<title>Adding the Instances</title>

<para>Open the file <literal remap="tt">kscribbledoc.h</literal> by selecting it in one of the fileviewers or by a click on the classviewer over the class
<literal remap="tt">KScribbleDoc</literal>. Then add the lines marked with -&gt; from the following code snippet:</para>

<para><programlisting>-&gt;  #include &lt;qpixmap.h&gt;
-&gt;  #include &lt;qpen.h&gt;

    class KScribbleDoc
    {

-&gt;    	protected:

-&gt;        QPen currentPen(){ return pen;};	
    		
-&gt;    		int penWidth()
-&gt;    			{ return pen.width(); }

      public slots:
        void updateAllViews(KScribbleView *sender);
    	
    	protected:
    	
-&gt;    		QPixmap buffer;
     	
      private:
-&gt;    		QPen pen;
        /** the modified flag of the current document */
        bool modified;</programlisting>
</para>

<para>As you see, we added pen and buffer as well as <literal remap="tt">currentPen()</literal> and <literal remap="tt">penWidth()</literal>. As pen is declared private, we offer a
possibility to retrive the pen as well as the pen width. As these are already implemented within the classdeclaration, we don't have to
add them to the implementation file, where we're turning to now.</para>
</sect1>

<sect1 id="initialization-of-the-document">
<title>Initialization of the Document</title>

<para>Select the method <literal remap="tt">newDocument()</literal> in the <literal remap="tt">KScribbleDoc</literal> class to jump to the method declaration. Here, we're only adding
one line, marked with the arrow:
<programlisting>  kscribbledoc.cpp

  bool KScribbleDoc::newDocument()
  {
    /////////////////////////////////////////////////
    // TODO: Add your document initialization code here
-&gt;  pen=QPen( Qt::black, 3 );
    /////////////////////////////////////////////////
    modified=false;
    return true;
  }</programlisting>
</para>

<para>This initializes the pen with the color black and width of 3 pixels; the <literal remap="tt">QPen</literal> class has some more constructors, but this one lasts our
needs here.</para>
</sect1>

<sect1 id="implementing-the-serialization">
<title>Implementing the Serialization</title>

<para>What is left to do is to define how to open and save our pictures. This is done in the according methods:</para>

<para><programlisting>    bool KScribbleDoc::openDocument(const QString &amp;filename, const char *format /*=0*/)
    {

    	QFile f( filename );
-&gt;     //if ( !f.open( IO_ReadOnly ) )
-&gt;     //	return false;
     /////////////////////////////////////////////////
     // TODO: Add your document opening code here
-&gt;     if(!buffer.load( filename, "PNG" ))
-&gt;     	return false;
     /////////////////////////////////////////////////
-&gt;     //f.close();


    bool KScribbleDoc::saveDocument(const QString &amp;filename, const char *format /*=0*/)
    {
      QFile f( filename );
-&gt;     // if ( !f.open( IO_WriteOnly ) )
-&gt;     // 	return false;

      /////////////////////////////////////////////////
      // TODO: Add your document saving code here
-&gt;    	if(!buffer.save( filename, "PNG" ))
-&gt;    		return false;
      /////////////////////////////////////////////////

-&gt;      //f.close();
</programlisting>
</para>

<para>Add the lines marked with the arrow again to your code. What we did here is to comment out the passages where the file
<literal remap="tt">filename</literal> is opened, because that is done automatically by the load and save methods of <literal remap="tt">QPixmap</literal>, which we add instead. Other
documents may open a file and read in its contents such as text lines or whatever, so the <literal remap="tt">QFile</literal> methods are already present in the
codeframe. As <literal remap="tt">save()</literal> and <literal remap="tt">load()</literal> return a boolean value if the operation was successful, we're returning false if not,
so the rest of the framework gets a return value and can give out warnings if the operation was not successful.
The <literal remap="tt">load()</literal> and <literal remap="tt">save()</literal> methods now are already provided in <literal remap="tt">QPixmap</literal>. They require the filename and the format as
argument; the source framework on the other hand <emphasis remap="bf">does not</emphasis> call the document methods with the format yet. If only one format is
used, it lasts to set the format here. other methods could detect the format for example- but we will turn to this later. For now,
we're using "PNG" as format; see <literal remap="tt">QImageIO</literal> for more details about the image formats that can be opened.</para>

<para>Now we're already finished defining our document structure. The <literal remap="tt">QPixmap</literal> buffer serves us as a buffer storing the original picture
contents while we're working on it, the pen is a valid pen for all views connected to the document. Note that the initialization of the
pen is done in <literal remap="tt">newDocument()</literal>. This method is always called after the constructor within the framework internally, so you
should add document instances initializations there as we did with the pen.</para>

<para>In the next chapter we will turn to the view class to define how the view shall cooperate with the user as well as how it accesses the
document- and then we'll be ready to paint !</para>
</sect1>
</chapter>

<chapter id="defining-the-view">
<title>Defining the View</title>

<sect1 id="interactivity-with-the-user">
<title>Interactivity with the User</title>

<para>In this chapter we'll turn to the view class of <emphasis>KScribble</emphasis> to define how the child windows shall work. First of all, we notice that
KScribbleView is derived from <literal remap="tt">QWidget</literal> by default. That is the minimum requirement for a child window, but it lasts already to fullfill
our needs. When it comes to defining a new widget's behavoir, we need to know how the user shall interact with the window. In our
example, this would be obviously the mouse. Therefore, we have to overwrite some virtual methods from <literal remap="tt">QWidget</literal> that process mouse
events the widget receives. What we need is to know when the user presses a mouse button, because the drawing shall only take place
when the mouse is pressed. Then we need to know when the mouse is moved (to know where it moves to) as well as when it is released-to
finish the stroke the user has drawn. Further we want our picture to be painted on the window and resized if the user decides to resize
the window he draws into. As members we will also add a <literal remap="tt">QPointArray</literal> polyline and a boolean value mousePressed. Add the code with the
arrow to your include file for the class KScribbleView:</para>

<para><programlisting>   kscribbleview.h

-&gt;   #include &lt;qpointarray.h&gt;

    class KScribbleView
    {
    .
    .
     protected:
        virtual void closeEvent(QCloseEvent* );

-&gt;     	virtual void mousePressEvent( QMouseEvent * );
-&gt;     	virtual void mouseReleaseEvent( QMouseEvent * );
-&gt;     	virtual void mouseMoveEvent( QMouseEvent * );
-&gt;     	virtual void resizeEvent( QResizeEvent * );
-&gt;     	virtual void paintEvent( QPaintEvent * );
   	
   	  KScribbleDoc *doc;
     		
-&gt;     private:
-&gt;   		bool mousePressed;
-&gt;   		QPointArray polyline;

     }</programlisting>
</para>
</sect1>

<sect1 id="reimplementing-event-handlers">
<title>Reimplementing Event Handlers</title>

<para>Now we're coming to the actual implementation of the event handlers. As explained in <ulink url="../kde_libref/index.html">The KDE Library Reference Guide</ulink>, Qt has a good way of handling user events, especially when they target on to widgets. <literal remap="tt">QWidget</literal> as a
baseclass preselects the events and provides basic event handlers which, as they are declared as virtual, we can overwrite to define
how our widget shall react on user actions. One is already overwritten: the <literal remap="tt">closeEvent()</literal> method. This is needed, because our
main window, represented in the App class, already preselects closing child windows and handles this; therefore the default event
handler, which just accepts the closing, must be overwritten to prevent that and let the App class do the job.</para>

<para>First of all, we have to declare the widget default behavoir in the constructor by initializing members and setting predefined values:</para>

<para><programlisting>    kscribbleview.cpp


    KScribbleView::KScribbleView(KScribbleDoc* pDoc, QWidget *parent, const char* name, int wflags)
     : QWidget(parent, name, wflags)
    {
        doc=pDoc;

-&gt;      setBackgroundMode( QWidget::NoBackground );
-&gt;      setCursor( Qt::crossCursor );
-&gt;    	mousePressed=false;
-&gt;      polyline=QPointArray(3);
    }</programlisting>
</para>

<para>We're setting the background to NoBackground, a cursor (crossCursor) and initialize mousePressed and polyline. Then we'll start
implementing our first event handler, <literal remap="tt">mousePressEvent()</literal>, to recognize when the user presses the mouse and where:</para>

<para>Note: the following implementations have to be inserted completely, so the lines to add are not marked with an arrow !</para>

<para><programlisting>void KScribbleView::mousePressEvent( QMouseEvent *e )
{
  mousePressed = TRUE;
  polyline[2] = polyline[1] = polyline[0] = e-&gt;pos();
}</programlisting>
</para>

<para>Here, we're setting mousePressed to true, so we have stored this event somehow. The second line is not so obvious: we're storing the
position where the mouse was pressed into our array's first three elements. As the array is a <literal remap="tt">QPointArray</literal>, it can store values of the
type <literal remap="tt">QPoint</literal> (which contain an x and y value themselves). What we will do with this array is to store positions of the mouse and create
the drawing routine from there in the mouseMoveEvent:</para>

<para><programlisting>void KScribbleView::mouseMoveEvent( QMouseEvent *e )
{
  if ( mousePressed ) {
		
    QPainter painter;
    painter.begin( &amp;doc-&gt;buffer );
    painter.setPen( doc-&gt;currentPen() );
    polyline[2] = polyline[1];
    polyline[1] = polyline[0];
    polyline[0] = e-&gt;pos();
    painter.drawPolyline( polyline );
    painter.end();

    QRect r = polyline.boundingRect();
    r = r.normalize();
    r.setLeft( r.left() - doc-&gt;penWidth() );
    r.setTop( r.top() - doc-&gt;penWidth() );
    r.setRight( r.right() + doc-&gt;penWidth() );
    r.setBottom( r.bottom() + doc-&gt;penWidth() );

	  doc-&gt;setModified();
    bitBlt( this, r.x(), r.y(), &amp;doc-&gt;buffer, r.x(), r.y(), r.width(), r.height() );
  }
}</programlisting>
</para>

<para>This event handler is probably the most difficult, so we will do a step-by-step walkthrough to understand what's been done. First of
all, the event handler receives all mouse movements over the widget. But as we're only interested in the move if the mouse is pressed,
because that is the time to draw, we have to ask if mousePressed is true. That has been done by the <literal remap="tt">mousePressEvent()</literal> event
handler before, so we don't have to take care for more. Now we're starting the painting action. First we create a <literal remap="tt">QPainter</literal> and let it
draw into the buffer of the document. This is important, because the document's buffer contains the real contents, the view only acts
as a communicator between the document and the user. We get the pen from the document instance as well by calling
<literal remap="tt">currentPen()</literal>. The next three lines assign the values inside the polyline <literal remap="tt">QPoint</literal> array, setting point 2 to 1, 1 to 0 and 0 to
the point to where the move went (this is the contents of the event we're interested in). Assuming we've just pressed the mouse (so all
three values of the array contain the pressing position) and the first mouse move event appears that contains the first position to
draw a line to; this value is moved into the first position in the array again. You may wonder why we need three points in the array
then, if we're only interested to draw a line from one position to the next. The following lines explain that: after drawing into our
buffer is finished (with <literal remap="tt">drawPolyline()</literal> and <literal remap="tt">painter.end()</literal>), we create a rectangle r and use <literal remap="tt">boundingRect()</literal>
from <literal remap="tt">QPointArray</literal> to get a <literal remap="tt">QRect</literal> that contains all three points. Therefore we need three values to have a most-complete rectangle. Then
we use <literal remap="tt">normalize()</literal> to have the leftmost and topmost values the smallest (as coordinates are counted from top-&gt;bottom and
left-&gt;right). The next thing to do is adapt the size of the rectangle by the size of the pen, because the pen has a thickness we get
with <literal remap="tt">penWidth()</literal> and widen the rectangle by the width of the pen. (Imagine the mouse movement was only two pixels away but the
pen thickness is set to ten- then the rectangle wouldn't contain the whole painted area).</para>

<para>Finally, we set the document modified and use the <literal remap="tt">bitBlt()</literal> function to copy the rectangle out of the buffer into the widget.
bitBlt operates bitwise and is very fast, so that it is a good method to copy the painted area from the buffer on the widget instead of
repainting the whole window. It's arguments are: first the object to draw to (the destination), here it is our widget, so we have to
use the pointer <emphasis remap="bf">this</emphasis>. The next two arguments give the destination topleft position to start copying to, then follows the
source to draw from with it's coordinates now including the width and height. As the pixmap coordinates are the same as the coordinates
that the widget uses (because our pixmap is drawn into the topleft corner), the coordinates for the source and destination topleft
point are the same. This is something to watch out for in some of the next step, so it may be mentioned here already.</para>

<para>Next comes what happens if we release the mouse button. Then the drawing has to stop when we move the mouse again, so we set
mousePressed to false here:</para>

<para><programlisting>void KScribbleView::mouseReleaseEvent( QMouseEvent * ) {
	mousePressed = FALSE;
}</programlisting>
</para>

<para>Now we have finished implementing the user interaction when it comes to the actual drawing operations. The example shows it's not too
complicated to use a document-view model. Just create your document instance so that it contains the contents and copy the contents to
your view.</para>
</sect1>

<sect1 id="painting-and-resizing-the-document">
<title>Painting and Resizing the Document</title>

<para>What is left to do are two other virtual event handlers that need a reimplementation. First of all, we have to take care that our
picture gets painted into the window when something else happens: when you open another window that obscures the painting - then you
change to your painting again, but it won't be there, unless your paint event gets processed to redraw the picture:</para>

<para><programlisting>void KScribbleView::paintEvent( QPaintEvent *e )
{
  QWidget::paintEvent( e );

  QRect r = e-&gt;rect();

  bitBlt( this, r.x(), r.y(), &amp;doc-&gt;buffer, r.x(), r.y(), r.width(), r.height() );
}</programlisting>
</para>

<para>This method also uses <literal remap="tt">bitBlt()</literal> to draw the picture from the buffer into the widget. Here, we only need the rectangle that gets
repainted, so we retrieve the geometry from the event ( <literal remap="tt">e-&gt;rect()</literal> ) and use the coordinates for <literal remap="tt">bitBlt()</literal>, just as we
did in the <literal remap="tt">mouseMoveEvent()</literal>.</para>

<para>The only thing where we didn't do anything about is the size of the pixmap. We didn't set it anywhere - we did not even use the pixmap
in the document class except for loading and saving - but these methods aren't called when creating a new picture. So it seems our
pixmap doesn't have a size nor a predefined background at all (even if we would have set the size, the contents would be random colors
because it is uninitialized).
On the other hand we have the fact that the KScribbleView instances get resized when they show up- at least with the minimum size. This
is the point where we can add the initialization as well, because the user can change the size manually and the widget will receive a
resize event as well. For reasons of simplicity, we want to set the pixmap size the same size the widget has. All this is done in the
event handler <literal remap="tt">resizeEvent()</literal>:</para>

<para><programlisting>void KScribbleView::resizeEvent( QResizeEvent *e )
{
  QWidget::resizeEvent( e );

  int w = width() &gt; doc-&gt;buffer.width() ?
  width() : doc-&gt;buffer.width();
  int h = height() &gt; doc-&gt;buffer.height() ?
  height() : doc-&gt;buffer.height();

  QPixmap tmp( doc-&gt;buffer );
  doc-&gt;buffer.resize( w, h );
  doc-&gt;buffer.fill( Qt::white );
  bitBlt( &amp;doc-&gt;buffer, 0, 0, &amp;tmp, 0, 0, tmp.width(), tmp.height() );
}</programlisting>

Here, we first call the resizeEvent handler or <literal remap="tt">QWidget</literal>. Then we calculate the size of our picture - because we can resize a window to
make it smaller or bigger, we have to separate these two cases: if we resize to a smaller geometry, the picture shall still contain
it's contents. On the other hand, if we resize to a bigger widget, we have to resize the pixmap as well to that bigger size. The
calculated values are stored in w and h. But before the resize takes place, we create a copy of our pixmap in the document in tmp. Then
we resize the buffer (the document), fill it with white color and then copy back the contents from tmp into buffer. This resizes our
pixmap always syncronous with the widget that displays it but doesn't loose contents which is outside the visible area if the resizing
makes the widget smaller.</para>

<para>Now our first application has gained a step where we can test it's functionality. Just hit "Run" in KDevelop and after <emphasis>KScribble</emphasis> shows
up, you're ready to paint your first picture with it !</para>
</sect1>
</chapter>

<chapter id="extending-the-gui">
<title>Extending the GUI</title>

<para>As we have seen, we have already provided <emphasis>KScribble</emphasis> the ability to open and save pictures with the document class and enabled user
interaction by overwriting virtual methods in the view class and we gained the first functionaliy - we can draw pictures as well. But
when we created the <literal remap="tt">QPen</literal> instance in the document class, we set some pre-defined values for the pen; the color is black and the pen
width set to 3 pixels. As you usually want to change these values in a drawing application, we have to enhance the main GUI by
providing ways to set these, according to the currently active window and document connected to it. This chapter will therefore
introduce you to:</para>

<para><itemizedlist>
<listitem>
<para>adding a new menu to the menubar</para>
</listitem>
<listitem>
<para>extending the toolbar with icons for the actions defined in the menubar</para>
</listitem>
<listitem>
<para>creating a new dialog with the KDevelop Dialogeditor</para>
</listitem>
<listitem>
<para>connecting the new commands of the menubar and toolbar to calling the dialogs.</para>
</listitem>
<listitem>
<para>enabling the document class to let us change the pen values</para>
</listitem>
</itemizedlist>
</para>

<para>Further, we also add a method to delete the document contents at all with a menubar command.</para>

<sect1 id="adding-the-pen-menu">
<title>Adding the "Pen" Menu</title>

<para>As the title of this section says, we will add a menu for setting the pen values of the documents here. Menus that are inserted into
the menubar are instances of <literal remap="tt">QPopupMenu</literal>, and you can have a look at how the current menubar is created when you switch to the
<literal remap="tt">KScribbleApp</literal> class, method <literal remap="tt">initMenubar()</literal>. You will see that the menubar items are created in the order they appear on the
menubar - but this isn't necessary. There are two things important on how the menubar will look like:
<itemizedlist>
<listitem>
<para>in the menus the order of the menuitems</para>
</listitem>
<listitem>
<para>in the menubar the order of inserting the popup menus.</para>
</listitem>
</itemizedlist>
</para>

<para>Last but not least you have to create menus first with calling the constructor. The class declaration already contains the pointers to
the popup menus, so we will have to add our "Pen" menu here first:</para>

<para><programlisting>kscribbleapp.h

class KScribbleApp
{
.
.
  private:
    QPopupMenu* pPenMenu;

}</programlisting>
</para>

<para>Now we are going to create the menu itself. Change to the implementation of the method <literal remap="tt">KScribbleApp::initMenuBar()</literal> and add the
lines marked with an arrow:</para>

<para><programlisting>void KScribbleApp::initMenuBar()
{
..

-&gt;  ///////////////////////////////////////////////////////////////////
-&gt;  // menuBar entry pen-Menu
-&gt;  pPenMenu = new QPopupMenu();
-&gt;  pPenMenu-&gt;insertItem(i18n("&amp;Color"), ID_PEN_COLOR);
-&gt;  pPenMenu-&gt;insertItem(i18n("&amp;Brush"), ID_PEN_BRUSH);

    menuBar()-&gt;insertItem(i18n("&amp;Edit"), pEditMenu);
-&gt;  menuBar()-&gt;insertItem(i18n("&amp;Pen"), pPenMenu);
    menuBar()-&gt;insertItem(i18n("&amp;View"), pViewMenu);


-&gt;  connect(pPenMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
-&gt;  connect(pPenMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
}</programlisting>
</para>

<para>You see that we first create the menu with <literal remap="tt">new QPopupMenu()</literal>. Then we use the insertItem methods to add two menu entries, Color
and Brush. The visible commands are inserted with the method <literal remap="tt">i18n()</literal>, which ensures that you can internationalize your
appliction. So as a general rule, you would declare all visual text that will appear later by the method <literal remap="tt">i18n()</literal>. Qt-only
programs using Qt &gt; 2.0 would use the according method <literal remap="tt">tr()</literal> instead of <literal remap="tt">i18n()</literal>, as Qt has it's own ways of
internationalizing applications. The second argument is a macro, the ID of the menubar item. This ID is a number that we have to set
using &num;define in the file resource.h, where you will see all other already used ID's declared. There are also other ways to insert
menus by directly connecting a slot to the inserted entry, but the application framework uses ID's to select which action has been
activated- and highlighted. Therefore each menu entry, independent of the popup menu it appears, has to be a unique number, and as we
can hardly remember numbers later, setting a &num;define for the ID is a nice solution. The popup menu is now inserted into the menubar
with <literal remap="tt">insertItem()</literal> as well, and with the pointer to the menu as second argument.</para>

<para>Note that we inserted the popup menu after the "Edit" menu and before the "View" menu, so it will appear between those menus later in
the menubar. What is also important when creating menus is that they should be available to the user with shortcuts; ususally in menus
you will see underlined characters that the user can jump to directly by pressing ALT and the according underlined letter of the
menuitem. As a programmer, you have to set this character by a leading ampersand, so the "Pen" menu will later be accessible via the
keyboard by pressing ALT+P. Within the menu, the user can press another button to go directly to the command he wants to, so in the
menu all items should have this kind of shortcuts as well. Note that you should write item insertions together in groups that have the
same visible access, so you can keep a better overview of the characters you already used so that there are no menu accelerators used
twice. (this is also important for your translators: in other languages the used accelerator may not be available in the translated
word, so they have to set some accelerators again.)</para>

<para>In the last two lines we're connecting the pen menu with two slots: one for when the menu signals that it is activated and the action
should be executed, and one for when it is highlighted. That allows making a statusbar help message available for the user. You can
have a look at the methods the menu is connected to, they contain switch statements where the sent menu ID is compared and the
following action called.</para>

<para>What is left to do is to add the &num;define statements to the file resource.h:</para>

<para><programlisting>resource.h

///////////////////////////////////////////////////////////////////
// Pen-menu entries
#define ID_PEN_COLOR                14010
#define ID_PEN_BRUSH                14020</programlisting>
</para>

<para>You will see that the numbers are unique for these entries- you have to watch out not to set the same number for two entries- but if it
happens by accident, there's still the compiler that informs you about redefining.</para>

<para>This is currently all you have to do to add a new menu for your menubar. The actions they will execute are: "Color" will call a color
selection dialog, "Brush" will call a dialog (which we still have to create) to select the brush width.</para>

<para>But first we'll extend the toolbar as well by two icons for these actions in the next section.</para>
</sect1>

<sect1 id="adding-toolbar-buttons">
<title>Adding Toolbar Buttons</title>

<para>Whenever you think that some new commands should be made available by toolbar buttons as well because they are often used and you want
to offer additional functionality, you can easily do that by adding buttons in the framework's <literal remap="tt">initToolBar()</literal> method of the
<literal remap="tt">App</literal> class. Here, we decide to add a button for both menu entries in the Pen-menu, but those need icons - which you can either
find in the KDE directory /toolbar or, when you don't find an icon that matches your action, have to create yourself. KIconEdit is very
suitable to paint icons, so we will first create them. Choose "New" from the KDevelop "File" menu and select "Icon" as the filetype.
The first icon will be named "pencolor.xpm". Now we have to select where we want to have the icon created in our project directory.
Press the directory selection button and change to your project directory containing the <emphasis>KScribble</emphasis> sources. Then create a new
directory "toolbar". Change to that directory and press "OK". The new icon will then be created in the new directory "toolbar" and will
be opened by KIconEdit within KDevelop automatically. Paint something that will signalize the user what the button is intended to do,
save the pixmap and then switch to the RFV / LFV in KDevelop. Select the icon by a right mouse button press and select "Properties"
from the popup menu. You will see that the icon is included in the distribution, but for your program to find the icon again later, you
have to set the installation destination as well. Check the "install" option and enter into the line now active below:</para>

<para><literal remap="tt">$(kde_datadir)/kscribble/toolbar/pencolor.xpm</literal></para>

<para>This will install the pixmap in the KDE file system hierarchy's data directory, where each application has its subdirectory containing
additional files needed by the application. Icons have to be installed into another subdirectory "toolbar", so the application's icon
loader can find the pixmaps for your program.</para>

<para>After you're finished, repeat all these above steps with the second icon for selecting the pen width. Name this pixmap "penwidth.xpm".</para>

<para>Now we only have to insert the buttons into the toolbar; add the lines marked with the arrow into your code:</para>

<para><programlisting>void KScribbleApp::initToolBar()
{
..
    toolBar()-&gt;insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy"));
    toolBar()-&gt;insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste"));
    toolBar()-&gt;insertSeparator();
-&gt;  toolBar()-&gt;insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") );
-&gt;  toolBar()-&gt;insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") );
-&gt;  toolBar()-&gt;insertSeparator();
    toolBar()-&gt;insertButton(BarIcon("help"), ID_HELP_CONTENTS, SIGNAL(clicked()),
..
}</programlisting>

Here, we use the methods of KToolBar to insert buttons. The first argument, <literal remap="tt">BarIcon()</literal>, tells the method to load the icon for
the button. What seems unusual is that we don't have to care for the file extension. The preferred format for KDE 2 is *.PNG, but it
works with xpm's as well. (You could use ImageMagick for that as well which can do that- or use <emphasis>KScribble</emphasis> in a later step to
convert your icons to PNG !)</para>

<para>The second argument is again the ID. The commands are then automatically activated, as the <literal remap="tt">toolBar()</literal> is already connected to
the same methods as the menubar is for signal <literal remap="tt">activated()</literal>. The third argument stands for "available" when true, "deactivated"
when false; as we want to have these available, we set this to true. At last, we add a tooltip for the the buttons, which we also
embrace with <literal remap="tt">i18n()</literal> to allow internationalization.</para>

<para>Now you're done for now- the GUI is extended at least visually. You can compile and run <emphasis>KScribble</emphasis> again and see how it looks like- of
course the new items in the menubar and toolbar can't execute any action - that is what we're going to add in the next section.
You will also note that the toolbar icons we added are not displayed - which is because we didn't install <emphasis>KScribble</emphasis> and so they can't
be found. All other used icons are already shipped with the KDE libraries, so these are already visible.</para>
</sect1>

<sect1 id="creating-the-pen-width-dialog">
<title>Creating the Pen Width Dialog</title>

<para>As we´ve already created the according menubar and toolbar commands, we now have to build the first dialog to set the pen width. For
this, select "New" from the KDevelop "File" menu and select "Qt/KDE Dialog". Then enter the dialog file name as <literal remap="tt">kpenbrushdlg</literal>,
the extension will be automatically added. Enter "OK" and the dialogeditor opens an empty widget that will be our dialog background.
When constructing a dialog, we have to think about what is really needed by the user; here, we need a label to display what will be
set; a spinbox with up and down buttons to set the pen width value and three buttons, one for resetting the pen width to the default
value, one to cancel the dialog and one for taking over the new value - the OK button. In this order we will add the items to the
dialog - which is important because the tab-focus follows the order by which the widgets are created. So if you´re starting with the OK
button, then the spinbox and then the cancel button, the input focus will change from the ok button to the spinbox and then to the
cancel button - which is not what the user expects. The tab focus should follow the widget´s items top-down from left to right, so we
have to construct the dialog in this order as well. To add items to the dialog, select the "Widgets" tab on the left pane. There you
have all available widgets present by icons to construct your dialog. Pressing a widget button will create the new item and place it at
the top-left corner of the widget. From there, you can place it with the mouse to the position you would like it to show up. Further,
when a widget item is selected, you can set the according values in the "Widget Properties" pane on the right.</para>

<para><emphasis remap="bf">The Label:</emphasis> press the "QLabel" button on the "Widgets" tab and place it at position x:50, y:20. Then select the "General"
section in the widget properties pane. Change the text in properity "Text" from "Label" to "Pen Width:". Adjust the width of the label
to a width that matches the label contents in x-direction; a width of 120 should last. You can do this either by using the mouse or set
the value in the "Geometry" section of the properties.</para>

<para><emphasis remap="bf">The Spinbox:</emphasis> press the "QSpinBox" button on the "Widgets" tab and place it at the right of the label we created in the last
step. Now set the variable name in section "C++Code" to "width_spbox". The minimum and maximum values are 1 and 100, which should last
for setting the brush width.</para>

<para><emphasis remap="bf">The Buttons:</emphasis> finally, we need the mentioned three buttons. The leftmost button will be the default button. Create a
<literal remap="tt">QPushbutton</literal> and place it somewhere nicely on the bottom of the dialog, set the variable name to "default_btn" and the button text to
"Default". Proceed with the OK button with variable name "ok_btn" and the cancel button with variable name "cancel_btn" and set the
button text to "&amp;OK" and "&amp;Cancel".</para>

<para>If you´re fine with the layout of the dialog, choose "Generate complete sources" from the Build menu and set the classname to
"KPenBrushDlg", the inheritance to <literal remap="tt">QDialog</literal>. After pressing "OK", the sources for the dialog are created and added to the project. Now
you can return to the editor view in KDevelop and we can add the code needed to give the dialog some execution purpose.</para>
</sect1>

<sect1 id="connections-and-setting-up">
<title>Connections and Setting Up</title>

<para>After we have created the GUI of the dialog, we have to add some functionality to the buttons and provide ways to set and retrieve the
selected value of the spinbox - because we want the dialog to display the current value when it gets called and to access the selected
value when the user pressed the OK button to quit the dialog.</para>

<para>In the generated class for the dialog, <literal remap="tt">KPenBrushDlg</literal>, you can see one method besides the constructor and the destructor,
<literal remap="tt">initDialog()</literal>. This method implements the whole GUI construction, so we don´t have to care for that anymore and we can go
directly to add the usual connections for the push buttons first. Add the lines marked by arrows to the constructor of the
dialog:</para>

<para><programlisting>KPenBrushDlg::KPenBrushDlg(int curr, QWidget *parent, const char *name) : QDialog(parent,name,true){
	initDialog();

-&gt;	connect(default_btn, SIGNAL(clicked()), this, SLOT(slotDefault()));
-&gt;	connect(ok_btn, SIGNAL(clicked()), this, SLOT(accept()));
-&gt;	connect(cancel_btn, SIGNAL(clicked()), this, SLOT(reject()));
}</programlisting>
</para>

<para>This provides the functionality for the buttons on the bottom of the dialog when the user clicks the button. First, we set the default
button to execute a slot called <literal remap="tt">slotDefault()</literal>. This slot is still to be implemented below, where we will set the default value
of the spinbox directly.</para>

<para>The second <literal remap="tt">connect()</literal> call connects the ok button to call the slot <literal remap="tt">accept()</literal> provided by <literal remap="tt">QDialog</literal>, as well as the cancel
button gets connected to <literal remap="tt">QDialog</literal>´s slot <literal remap="tt">reject()</literal>. This will both close the dialog and will set the result value which we will
use later when we implement the method that calls the dialog to determine if we want to use the value set or to ignore any changes.</para>

<para>Now we have to add two methods to set and retrieve the spinbox value:</para>

<para><programlisting>void setCurrent(int curr){ width_spbox-&gt;setValue(curr); }
int width() { return width_spbox-&gt;value(); };</programlisting>
</para>

<para>Add these methods to the class declaration with the modifier "public", as we want to set and retrieve the values when we call the
dialog to show up. The <literal remap="tt">setCurrent()</literal> method will be used to set the current value the pen has, the <literal remap="tt">width()</literal> method
returns us the selected with when the user presses OK and we want to know which value has been chosen.</para>

<para>Last but not least, we need to implement the <literal remap="tt">slotDefault()</literal> method:</para>

<para><programlisting>//kpenbrushdlg.h:
//method declaration:

public slots:
  void slotDefault();

//kpenbrushdlg.cpp:
//method implementation:

void KPenBrushDlg::slotDefault()
{
  width_spbox-&gt;setValue(3);
}</programlisting>
</para>

<para>This will set the default value to 3 pixels for the pen.</para>

<para>Now we´re ready with our first dialog and we can turn to over to the other application classes to adapt some things and add the method
calls to invoke the dialog.</para>
</sect1>

<sect1 id="calling-the-dialogs">
<title>Calling the Dialogs</title>

<para>As you may guess, calling the dialogs means that we will not only implement calling our width selection dialog but also add the method
for selecting the pen color, but one after another. First, create a method <literal remap="tt">slotPenBrush()</literal> in the class <literal remap="tt">KScribbleApp</literal>:</para>

<para><programlisting>void KScribbleApp::slotPenBrush()
{
  slotStatusMsg(i18n("Setting brush width..."));

  // get one window with document for a current pen width
  QWidgetList windows = pWorkspace-&gt;windowList();
  KScribbleView* m = (KScribbleView*)windows.at(0);
	KScribbleDoc* pDoc = m-&gt;getDocument();
  int curr_width=pDoc-&gt;penWidth();

  // create the dialog, get the new width and set the pen width for all documents
  KPenBrushDlg* dlg= new KPenBrushDlg(this);
  dlg-&gt;setCurrent(curr_width);
  if(dlg-&gt;exec()){
    int width=dlg-&gt;width();
  	for ( int i = 0; i &lt; int(windows.count()); ++i )
  	{
    	m = (KScribbleView*)windows.at(i);
    	if ( m )
    	{
    		pDoc = m-&gt;getDocument();
        pDoc-&gt;setPenWidth(width);
    	}
  	}
  }
  slotStatusMsg(i18n("Ready."));
}</programlisting>

Here, we first have to access the window list and retrieve a pointer to a document - which can be a document of any window, because all
documents should have the same current pen width. Then we create an integer variable curr_width that stores the current pen width.</para>

<para>Now we can call the dialog by creating the dlg instance of KPenBrushDlg. Then we set the current pen width by calling
<literal remap="tt">dlg-&gt;setCurrent()</literal>, which method we added to the dialog.
By calling <literal remap="tt">dlg-&gt;exec()</literal> we invoke the dialog. The <literal remap="tt">if()</literal> statement ensures that the following code is only executed when
the result code of the dialog has the accept flag set - which means, the code is executed if the user pressed the OK button on the
dialog.</para>

<para>Assuming the user changed the value and pressed OK, we have to set all documents to use the new pen width. For that we use the
<literal remap="tt">for()</literal> loop and set every document´s pen width to the width variable we retrieved before with <literal remap="tt">dlg-&gt;width()</literal>.</para>

<para>We don´t have implemented the method <literal remap="tt">setPenWidth()</literal> in the document class, so we´ll do this right now:</para>

<para><programlisting>
kscribbledoc.h:

public:
  void setPenWidth( int w ){ pen.setWidth( w ); }</programlisting>
</para>

<para>What is missing to execute any action is to add the methods that shall be called when the menu items are activated or the toolbar
buttons pressed. For this, we have to add the ID´s to the slot <literal remap="tt">commandCallback()</literal>, which selects and executes the according
methods we want to call if a menu or toolbar item was chosen:</para>

<para><programlisting>void KScribbleApp::commandCallback(int id_)
{
  switch (id_)
  {
    case ID_PEN_BRUSH:
      slotPenBrush();
      break;

    case ID_PEN_COLOR:
      slotPenColor();
      break;
....
  }
}</programlisting>
</para>

<para>This addition also adds the <literal remap="tt">slotPenColor()</literal> method to the execution list to set the pen color, which we will implement now:</para>

<para><programlisting>void KScribbleApp::slotPenColor()
{
  slotStatusMsg(i18n("Selecting pen color..."));

  QColor myColor;
  int result = KColorDialog::getColor( myColor, this );
  if ( result == KColorDialog::Accepted )
  {
    QWidgetList windows = pWorkspace-&gt;windowList();
    KScribbleDoc* pDoc;
    KScribbleView* m;
    for ( int i = 0; i &lt; int(windows.count()); ++i )
    {
      m = (KScribbleView*)windows.at(i);
      if ( m )
      {
        pDoc = m-&gt;getDocument();
        pDoc-&gt;setPenColor(myColor);
      }
    }
  }
  slotStatusMsg(i18n("Ready."));
}</programlisting>

When looking at the code, we see that we use another new method of <literal remap="tt">KScribbleDoc</literal> to set the pen color. This one has to be implemented as
well:
<programlisting>kscribbledoc.h:

    /** sets the pen color */
    void setPenColor( const QColor &amp;c ){ pen.setColor( c ); }</programlisting>
</para>

<para>Watch out for adding the declaration of the two new methods <literal remap="tt">slotPenBrush()</literal> and <literal remap="tt">slotPenColor()</literal> to the class
<literal remap="tt">KScribbleApp</literal>, so our class knows about these methods.</para>

<para>Now you´re ready ! Let´s summarize what we´ve done in this chapter:
<itemizedlist>
<listitem>
<para> we first added menubar and toolbar commands/items for two dialogs that we want to call;</para>
</listitem>
<listitem>
<para> then we created the first dialog to set the pen width with the KDevelop dialogeditor</para>
</listitem>
<listitem>
<para> after that we created the methods we want to call by the menubar/toolbar items which invoke the dialogs and set the poperties we
wanted to change</para>
</listitem>
<listitem>
<para> finally, we added the needed set functions to the document so we are able to change the values after the dialogs get executed.</para>
</listitem>
</itemizedlist>
</para>

<para>By this structure, you are provided the general way how to extend your application with more functionality and manipulating settings
that influence the behavoir of the document and view interaction.</para>
</sect1>
</chapter>

<chapter id="extended-views">
<title>Extended Views</title>

<para>In this chapter we´re going to extend the functionality of our view widget by two enhancements: syncronized views and scrollviews.</para>

<sect1 id="syncronizing-views">
<title>Syncronizing Views</title>

<para>Let´s first explain what this will bring us and how we´re going to do it. While playing with <emphasis>KScribble</emphasis>, you may have noticed, that if
you open another view of a document by calling "Window"-&gt;"New Window", this new view works with the same data as the first view, and
does like any other view you create with that command. But when it comes to painting into the document, you can only do that in one
view - the other views are not displaying the document contents at the same time. If you obscure one view that doesn´t contain the
actual contents with another window and then bring it up to the front again, it will display the acutal contents. That comes because
after a widget has been obscured and then activated again, it receives a paint event from the window system, which will call
<literal remap="tt">KScribbleView::paintEvent()</literal> again and that finally redraws the contents of the area that has been obscured. What we want to
achieve is that all views should paint syncronous with the one the user actually paints to. In fact, you will see that this enhancement
is a really easy task. The document class already provides us a method <literal remap="tt">updateAllViews()</literal>, which calls the <literal remap="tt">update()</literal>
method on each view in the document´s view list. This makes it very easy to syncronize the document contents - every time the contents
is changed, here by mouse movements (where we copy the changings to the buffer with <literal remap="tt">bitBlt()</literal>), we just have to call
updateAllViews(this). The this pointer is needed, because the calling view doesn´t need a repaint and the <literal remap="tt">update()</literal> method is
only executed if the sender view is not the same as it´s own.</para>

<para>What you´ve got to do here is only to call updateAllViews(this) at the end of the virtual methods <literal remap="tt">mousePressEvent()</literal>,
<literal remap="tt">mouseMoveEvent()</literal> and <literal remap="tt">mouseReleaseEvent()</literal> - and you´re done !  Take this as a general rule in your applications: each
time the contents of the document is changed by a view, call <literal remap="tt">updateAllViews()</literal>. How the update has to be executed has to be
implemented in the widget´s <literal remap="tt">update()</literal> method; one may be content by setting e.g. the changed text in an editor, in our
application we just call <literal remap="tt">repaint()</literal>, which generates a paint event and copies the contents of the document into the view again.</para>
</sect1>

<sect1 id="scrolled-views">
<title>Scrolled Views</title>

<para>In this section we will add a functionality that is most often a thread to developers - if you can´t use an already implemented widget
that provides the scrolling already. What does scrolling mean ? In our context, the problem begins where we want to open a picture that
is bigger than a view can display. therefore, the result will be that you can only see as much as the view provides, beginning from the
topleft corner; the rest will be cut away from the user´s view. A scrollview on the other hand is a widget that provides a scrollbar on
the right side and on the bottom of the widget by which the user can "move" the contents. In fact, it shows the same size of the
document contents, but the view area can be moved within the document, so each part can be displayed if the user wants to by moving the
scrollbar sliders up and down, left and right. Fortunately, Qt provides a class <literal remap="tt">QScrollView</literal> that itself inherits from
<literal remap="tt">QWidget</literal> and offers the same base functionality as an ordinary widget but manages the contents by scrollbars automatically -
with the additional option that the programmer can either just use an instance of the <literal remap="tt">QScrollView</literal>, create the child widgets
to manage with the scrollview as parent and add them to the scrollview with <literal remap="tt">addChild()</literal> or create a view by inheriting
<literal remap="tt">QScrollView</literal> and draw into the viewport, which is a defined area inside the scrollview, instead of directly to the widget. The
difference here is that <literal remap="tt">QScrollView</literal> provides a set of event handlers similar to the <literal remap="tt">QWidget</literal> event handlers especially for the
viewport. So what was formerly a <literal remap="tt">mousePressEvent()</literal> in our view will become a viewportMousePressEvent, a <literal remap="tt">paintEvent()</literal>
will become a viewportPaintEvent etc. The second possibility will suite our needs to make KScribbleView a scrollable widget and so we
will have to make the following modifications:</para>

<para><itemizedlist>
<listitem>
<para>first we have to make the document use a size of its contents. This can be done by getting the size for an opened picture, for a
new picture we have to define a default start size. In other painting applications, this size can be changed most often by a command
provided by the user interface, e.g. a dialog that asks for the new width and height as well as a method to shrink the picture
contents to fit into that new size.</para>
</listitem>
<listitem>
<para>changing the inheritance of KScribbleView from <literal remap="tt">QWidget</literal> to <literal remap="tt">QScrollView</literal></para>
</listitem>
<listitem>
<para>renaming the virtual methods to the according viewport methods of <literal remap="tt">QScrollView</literal></para>
</listitem>
<listitem>
<para>adapt the virtual event handlers to act on the geometry of the viewport. This means that the old implementation relies on the
geometries of <literal remap="tt">QWidget</literal> starting at the topleft corner of a widget. If the view is scrolled and the topleft corner is not visible, we
have to ensure the positions retrieved from the <literal remap="tt">QWidget</literal> coordinates are translated to viewport coordinates</para>
</listitem>
</itemizedlist>
</para>

<sect2 id="sizing-the-document-contents">
<title>Sizing the Document Contents</title>

<para>As already mentioned, we have to set a size to the document contents as well as to initialize this size and provide a method to
retrieve the size by the views. For this, we add a variable <literal remap="tt">QSize size</literal> to <literal remap="tt">KScribbleDoc</literal> as well as the method
<literal remap="tt">docSize()</literal>:</para>

<para><programlisting>kscribbledoc.h:

#include &lt;qsize.h&gt;

...
public:
  const QSize docSize(){ return size;};

private:
  QSize size;
</programlisting>
</para>

<para>Now we have to modify all methods that deal with initializing and opening the document contents - <literal remap="tt">newDocument()</literal> and
<literal remap="tt">openDocument()</literal>:
<programlisting>  bool KScribbleDoc::newDocument()
  {
    /////////////////////////////////////////////////
    // TODO: Add your document initialization code here
-&gt;  size=QSize(300,200 );
   	pen=QPen( Qt::black, 3 );
-&gt;  buffer.resize(size);
-&gt;  buffer.fill( Qt::white );
    /////////////////////////////////////////////////
    modified=false;
    return true;
  }

  bool KScribbleDoc::openDocument(const QString &amp;filename, const char *format /*=0*/)
  {

  	QFile f( filename );
  //	if ( !f.open( IO_ReadOnly ) )
  //		return false;
    /////////////////////////////////////////////////
    // TODO: Add your document opening code here
  	if(!buffer.load( filename, format ))
  		return false;
-&gt;  size=buffer.size();
    /////////////////////////////////////////////////
  //	f.close();
  	
    modified=false;
    m_filename=filename;
  	m_title=QFileInfo(f).fileName();
    return true;
  }</programlisting>
</para>

<para>In <literal remap="tt">newDocument()</literal>, we initialize the size with a default value of 300 pixels wide and 200 pixels high. This is enough for a
small picture for now and we could add a dialog for resizing as well if we want.
When it comes to opening a picture, we have to set the size to the size of the picture. This can be done by calling
<literal remap="tt">QPixmap::size()</literal>, which we used in <literal remap="tt">openDocument()</literal>. Then we´re done with setting the sizes and we can move on to
reimplementing KScribbleView and make it a scrollview.</para>
</sect2>
</sect1>

<sect1 id="adapting-the-view">
<title>Adapting the View</title>

<para>As said above, we first have to change some things in the interface of KScribbleView. The following code shows these changings:</para>

<para><programlisting>#include &lt;qscrollview.h&gt;

class KScribbleView : public QScrollView
{
  Q_OBJECT

  protected:
    /** changed from mousePressEvent() overwriting QScrollView method */
    virtual void viewportMousePressEvent( QMouseEvent* );
    /** changed from mouseReleaseEvent() overwriting QScrollView method */
    virtual void viewportMouseReleaseEvent( QMouseEvent* );
    /** changed from mouseMoveEvent() overwriting QScrollView method */
    virtual void viewportMouseMoveEvent( QMouseEvent* );

    /** commeted out because we have a document size defined */
//    resizeEvent( QResizeEvent* );

    /** changed from paintEvent() overwriting QScrollView method */
    virtual void viewportPaintEvent( QPaintEvent* );
}</programlisting>
</para>

<para>Here, we changed the inheritance from <literal remap="tt">QWidget</literal> to <literal remap="tt">QScrollView</literal> first and added the according include file we need. Also we changed all
implemented event handlers that deal with interaction on the contents of the scrollview to the according methods <literal remap="tt">QScrollView</literal> provides
for this purpose and commented out the resizeEvent. Now we can go over to the implementation of these methods and make use of the size
our picture has. As a view is always created after the document exists, we can resize the widget directly in the constructor to fit
this size and as well resize the contents (which is the viewport size):
<programlisting>#include &lt;qsize.h&gt;

KScribbleView::KScribbleView(KScribbleDoc* pDoc, QWidget *parent, const char* name, int wflags)
 : QScrollView(parent, name, wflags | WPaintClever | WNorthWestGravity | WRepaintNoErase)
{
    doc=pDoc;
		mousePressed=false;
    polyline=QPointArray(3);

-&gt;  setResizePolicy ( QScrollView::ResizeOne );
-&gt;  viewport()-&gt;setCursor( Qt::crossCursor );

-&gt;    QSize size=doc-&gt;docSize();
      // resize the viewport - this makes the resizeEvent obsolete
-&gt;    resizeContents(size.width(), size.height());
      // resize the widget to show up with the document size
-&gt;    resize(size);
}</programlisting>
</para>

<para>Note that formerly, the <literal remap="tt">resizeEvent()</literal> took care of resizing the drawing area to the same as the widget size. At the same time,
this changed the document size as well, so the document picture had always the same size as the widget. With the already initialized
size of the document (which we set in <literal remap="tt">newDocument()</literal> and <literal remap="tt">openDocument()</literal>), we just resize the contents by calling
<literal remap="tt">resizeContents()</literal> provided by <literal remap="tt">QScrollView</literal> with the size of the document. You may also notice that we changed the
cursor over the widget from the overall widget to the viewport widget, which we can retrieve with <literal remap="tt">viewport()</literal>. Now we can
reimplement the event handlers. At first, we should take care for the paintEvent, as this is one of the most important ones, because it
gets called whenever the widget shows up or is resized.</para>

<para><emphasis remap="bf">Attention:</emphasis> take care to comment out the <literal remap="tt">resizeEvent()</literal> implementation!</para>

<para>Now, the paint event will have to copy the pixmap in the buffer to the according position in the view. For this, we have to change the
destination of <literal remap="tt">bitBlt()</literal> from this to <literal remap="tt">viewport()</literal>, set the topleft position to 0,0 and set the target (the buffer) to
copy from the contentsX and contentsY position on into the viewport:
<programlisting>void KScribbleView::viewportPaintEvent( QPaintEvent *e )
{
  bitBlt( viewport(),0,0, &amp;doc-&gt;buffer,contentsX() ,contentsY() );
}</programlisting>

The <literal remap="tt">contentsX()</literal> thereby is the position in x-direction of the scrollview´s contents - which goes to position 0 in the
viewport´s absolute position, which is the topleft point visible in the scrollview. The same applies to the y-direction. This part is
sometimes hard to understand and you may have to do a bit "try and error" when implementing your own scrollviews. The other possible
call of <literal remap="tt">bitBlt()</literal> would be to switch the values of the positions and inverting the contents values:</para>

<para>bitBlt( viewport(), -contentsX(), -contentsY(), &amp;doc-&gt;buffer, 0, 0 );</para>

<para>The last changes we need to do are changing the mouse event handlers. First, the <literal remap="tt">mouseMoveEvent()</literal>, which changes to
<literal remap="tt">viewportMouseMoveEvent()</literal>, has a <literal remap="tt">bitBlt()</literal> call as well. Here, we have to apply the same chages as in the paint event.
Further, in the <literal remap="tt">mousePressEvent()</literal> and the <literal remap="tt">mouseMoveEvent()</literal>, we have retrieved the position of the mouse events with
<literal remap="tt">e-&gt;pos()</literal>. This position now will deliver us a widget position - not the contents position, so we have to translate this to
draw into the correct position of the document with <literal remap="tt">viewportToContents()</literal>:
<programlisting>  void KScribbleView::viewportMousePressEvent( QMouseEvent *e )
  {
    mousePressed = TRUE;
-&gt;  doc-&gt;polyline[2] = doc-&gt;polyline[1] = doc-&gt;polyline[0] = viewportToContents(e-&gt;pos());
    doc-&gt;updateAllViews(this);
  }

  void KScribbleView::viewportMouseMoveEvent( QMouseEvent *e )
  {
    if ( mousePressed ) {
  ....
      doc-&gt;polyline[1] = doc-&gt;polyline[0];
-&gt;    doc-&gt;polyline[0] = viewportToContents(e-&gt;pos());
      painter.drawPolyline( doc-&gt;polyline );
  ....
      r.setBottom( r.bottom() + doc-&gt;penWidth() );

  	  doc-&gt;setModified();
-&gt;    bitBlt(viewport(), r.x()-contentsX(), r.y()-contentsY() ,
-&gt;            &amp;doc-&gt;buffer, r.x(), r.y(), r.width(), r.height() );
  	  doc-&gt;updateAllViews(this);
    }
  }</programlisting>
</para>

<para>In the <literal remap="tt">viewportMouseMoveEvent()</literal>, we had to change the destination again from this to <literal remap="tt">viewport()</literal> - and with that
translate the positions. This time, we used the second version of the call we used in <literal remap="tt">viewportPaintEvent()</literal>, with subtracting
the contentsX and contentsY values to copy the rectangle containing the current painting into the correct position of the viewport.</para>

<para>At last, we will apply a small change in conjunction with the <literal remap="tt">update()</literal> method: why should we repaint the whole widget every
time ? This will reduce performance most often and lead to a so-called "flicker" effect. This effect sometimes occurs with widgets, but
there are some ways to reduce this behavoir. Instead of calling <literal remap="tt">repaint()</literal>, we could call <literal remap="tt">repaint(false)</literal> as well. This
will not erase the widget contents before redrawing it. As we copy the document contents directly into the widget, we don´t need to
erase it anyway, because all the data will be overwritten anyway. In conjunction with <literal remap="tt">QScrollView</literal>, we will reduce the painting
even more: we limit the update method to call <literal remap="tt">repaint()</literal> on the viewport() widget, because that will call
<literal remap="tt">viewportPaintEvent()</literal>. On the other hand, the painting area we use is the rectangle containing the document contents, which, when the
document size is smaller than the viewport size. So we can limit the paint event to the rectangle of the viewport where the document 
is displayed, whose visible width and height we can retrieve and compose to the rectangle. Additionally, we use the erase parameter with false,
so the document area doesn´t get erased:</para>

<para><programlisting>void KScribbleView::update(KScribbleView* pSender){
  if(pSender != this)
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
}</programlisting>
</para>

<para>Now you´re ready !  This chapter has been one of the hardest to implement and understand - especially when it comes to the geometries
that change. But on the other hand, we gave our application a whole new functionality by the new scrollview and the syncronized views.</para>

<para>With that, we´re moving on to the last chapter of our tutorial. There, we will apply only a few changes by making use of some new
methods of the KDE2 libraries, but as usual, this will bring us some interesting functionality - <emphasis>KScribble</emphasis> will be able to open and
save a whole range of picture formats and thereby we will remove the restriction of operating only on the png file format.</para>
</sect1>
</chapter>

<chapter id="using-kimageio-with-kscribble">
<title>Using KImageIO with KScribble</title>

<para>When it comes to images, the Qt and KDE libraries offer a wide variety of operations. Besides actual drawing routines, the libraries
support a whole set of image formats which they can read and write - all without any cost on our view. The main class for these
operations is <literal remap="tt">QImageIO</literal>, which has a support library by KDE: <literal remap="tt">KImageIO</literal>. As a preparation before we can make use of these nice methods, we
have to add the according library to <emphasis>KScribble</emphasis>: libkimgio. Open "Project"-&gt;"Options" in KDevelop and add the line -lkimgio to the
additional libraries to link <emphasis>KScribble</emphasis> with.</para>

<sect1 id="preparing-the-document">
<title>Preparing the Document</title>

<para>Now we can go on and apply our changes. First, we have to remove the current restriction in the document class to only read and write
PNG files. Just replace "PNG" in the following methods with format, as given by the <literal remap="tt">openDocument()</literal> and <literal remap="tt">saveDocument()</literal>
methods:</para>

<para>In method <literal remap="tt">KScribbleDoc::openDocument()</literal>:</para>

<para><literal remap="tt">	if(!buffer.load( filename, format ))</literal></para>

<para>In method <literal remap="tt">KScribbleDoc:: saveDocument()</literal>:</para>

<para><literal remap="tt">	if(!buffer.save( filename, format ))</literal></para>

<para>Then we have a default behavoir of these methods. Now, for now we don´t have all available file formats yet. This will be our next
task, together with adapting the slots in <emphasis>KScribble</emphasis> that deliver us filenames.</para>
</sect1>

<sect1 id="registering-file-formats">
<title>Registering File Formats</title>

<para>To use <literal remap="tt">KImageIO</literal>, we have to initialize the library first. For this, we add a call for <literal remap="tt">registerFormats()</literal> in our
<literal remap="tt">main()</literal> function:
<programlisting> 	
main.cpp:

............
  KApplication app;
  KImageIO::registerFormats();

  if (app.isRestored())
............</programlisting>

Note that this call is after the application is instanciated with KApplication app - without the application instance, our program will
not run, as <literal remap="tt">KImageIO</literal> then doen´t know on which application to register the formats. The include file for this call will be added to
kscribble.h, as we´re going to use some of its methods in <literal remap="tt">KScribbleApp</literal>:</para>

<para>kscribble.h:</para>

<para>&num;include &lt;kimgio.h&gt;</para>
</sect1>

<sect1 id="opening-images">
<title>Opening Images</title>

<para>Now that we can make use of <literal remap="tt">KImageIO</literal>, we have to apply the first change to the most important method of <literal remap="tt">KScribbleApp</literal>:
<literal remap="tt">openDocumentFile()</literal>. This method opens us any document until now only on the behalf of the filename. It just leaves out the
extension, as the format is not required by the document class by default. But as we have changed that, we just need a format - and
have to adapt the call for <literal remap="tt">KScribbleDoc::openDocument()</literal> in the method <literal remap="tt">openDocumentFile()</literal>:</para>

<para><programlisting>kscribble.cpp:

  void KScribbleApp::openDocumentFile(const char* file)
  {
  	...........
  	
  	else
    {
-&gt;    QString format=KImageIO::type(file);
-&gt;	  if(!doc-&gt;openDocument(file,format))
    		KMessageBox::error (this,i18n("Could not open document !"), i18n("Error !"));
  		addRecentFile(file);
  	}
    ............
  }</programlisting>
</para>

<para>Of course, this works the same as using <literal remap="tt">QString format=QImageIO::imageFormat(file);</literal>. Here, <literal remap="tt">KImageIO</literal> delivers us the format of
the image and we can call the document to open the file by filename and format. (Another possiblity would be to detect the format in
the document class as well).</para>
</sect1>

<sect1 id="setting-file-filters-with-kimageio">
<title>Setting File Filters with KImageIO</title>

<para>Here, we´re finishing our tutorial with the last section - we will adapt the file dialogs of <emphasis>KScribble</emphasis> to make use of file filters.
For these, <literal remap="tt">KImageIO</literal> provides nice methods to give us the needed strings for all image file formats that are available for opening and
saving. The following implementation replaces the default file filter (which is in fact none - you have to change your own
applications´ file filter to your mime type accordingly) with the <literal remap="tt">pattern()</literal> method of <literal remap="tt">KImageIO</literal>:</para>

<para><programlisting>  void KScribbleApp::slotFileOpen()
  {
    slotStatusMsg(i18n("Opening file..."));
  	
-&gt;    QString fileToOpen=KFileDialog::getOpenFileName(QDir::currentDirPath(),
-&gt;              KImageIO::pattern(KImageIO::Reading), this, i18n("Open File..."));
    if(!fileToOpen.isEmpty())
    {
  		openDocumentFile(fileToOpen);		
    }

    slotStatusMsg(i18n("Ready."));
  }</programlisting>
</para>

<para>Here, the mode in <literal remap="tt">pattern()</literal> is set to Reading - which may differ from the patterns that are retrieved when set to writing. Now
we have finished opening files by its filename and format completely. What is missing to complete this structure, is setting the
patterns as well for saving a file. For that, the <literal remap="tt">slotFileSaveAs()</literal> is called, which itself invokes the file dialog that
retrieves a file name. There, we will set the pattern mode to Writing:</para>

<para><programlisting>void KScribbleApp::slotFileSaveAs()
{
  slotStatusMsg(i18n("Saving file with a new filename..."));

-&gt;  QString newName=KFileDialog::getSaveFileName(QDir::currentDirPath(),
-&gt;                               KImageIO::pattern(KImageIO::Writing), this, i18n("Save as..."));
  if(!newName.isEmpty())
  {
    KScribbleView* m = (KScribbleView*)pWorkspace-&gt;activeWindow();
    if( m )
    {
      KScribbleDoc* doc =	m-&gt;getDocument();
  	  QString format=QFileInfo(newName).extension();
  	  format=format.upper();
		  if(!doc-&gt;saveDocument(newName,format))
  	  {
  		  KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
				return;
			}
      doc-&gt;changedViewList();
      setWndTitle(m);
    }
  }
  slotStatusMsg(i18n("Ready."));
}</programlisting>
</para>
</sect1>
</chapter>

<chapter id="adding-printing-functionality">
<title>Adding Printing Functionality</title>

<para>In this chapter we will show you how easy it is to implement printing functions using Qt. It´s actually just one line of code for us to
do here, but we will start understanding who is actually doing the printing job. When the user presses the print button in
<emphasis>KScribble</emphasis> or chooses "Print" from the "File" menu, the <literal remap="tt">slotFilePrint()</literal> method is called in <literal remap="tt">KScribbleApp</literal>.
This method detects which child window is currently activ and creates a printer instance of the class <literal remap="tt">QPrinter</literal>. Then it calls
the widget´s printing method, <literal remap="tt">KScribbleView::print()</literal>. Here, the framework already contains the base implementation - which
already shows you that for printing you just have to use <literal remap="tt">QPainter</literal> which then draws on the printer. This method also calls the
printing dialog.</para>

<para>What we have to do here is to use <literal remap="tt">QPainter</literal> methods to draw the pixmap of the document connected to the view. As
<literal remap="tt">QPainter</literal> already offers a whole set of methods <literal remap="tt">drawPixmap()</literal>, we will of course use one of them:</para>

<para><programlisting>void KScribbleView::print(QPrinter *pPrinter)
{
  if (pPrinter-&gt;setup(this))
  {
    QPainter p;
    p.begin(pPrinter);
    	  	
    ///////////////////////////////
    // TODO: add your printing code here
-&gt;	p.drawPixmap(0,0,doc-&gt;buffer);
    ///////////////////////////////
    p.end();
  }
}</programlisting>
</para>

<para>Here, we paint into the offset of the printer page at 0,0 with our buffer pixmap of the document. That´s all !</para>

<para>You can just go ahead and test it - now you can print any graphics that <emphasis>KScribble</emphasis> is able to open.</para>

<para>This is now the end of our trour through creating a KDE 2 application. You can find the source package of KScribble
completely with an extension that adds cut, copy, paste and undo functions as well as drag´n drop here:</para>

<para><ulink url="../../../../../apps/kdevelop/examples/kscribble-1.0.tar.gz">$(KDEDIR)/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</ulink>   	
The example tarball is locally installed and can be downloaded to your home directory, where you can untar it and test it.
After untarring the tarball with <literal remap="tt">tar zxvf kscribble-1.0.tar.gz</literal>, load the project and call "Automake and autoconf" from the
"Build" menu in KDevelop, then call "./configure" from the same menu. The configure options are those of my installtion of the KDE 2
and Qt 2.1, so you have to change them manually to match your installation path for these options.</para>

<para>The appendix also contains the complete sourcecode for this package to read through online.</para>
</chapter>

<chapter id="copyright">
<title>Copyright </title>

<para> <screen>KDevelop Copyright 1998,1999,2000 The KDevelop Team.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
</screen>
</para>
</chapter>
<appendix id="kscribble-1.0-example-sourcecode">
<title>KScribble-1.0 Example Sourcecode</title>

<sect1 id="project-tarball">
<title>Project Tarball</title>

<para>The example tarball is locally installed and can be downloaded to your home directory, where you can untar it and test it.
After untarring the tarball with <literal remap="tt">tar zxvf kscribble-1.0.tar.gz</literal>, load the project and call "Automake and autoconf" from the
"Build" menu in KDevelop, then call "./configure" from the same menu. The configure options are those of my installtion of the KDE 2
and Qt 2.1, so you have to change them manually to match your installation path for these options.</para>

<para><ulink url="../../../../../apps/kdevelop/examples/kscribble-1.0.tar.gz">$(KDEDIR)/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</ulink>   	</para>
</sect1>

<sect1 id="main.cpp">
<title>main.cpp</title>

<para><programlisting>/***************************************************************************
                          main.cpp  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include &lt;kcmdlineargs.h&gt;
#include &lt;kaboutdata.h&gt;
#include &lt;klocale.h&gt;

#include "kscribble.h"

static const char *description =
   I18N_NOOP("KDE 2 example application");

static KCmdLineOptions options[] =
{
   {  "+[File]", I18N_NOOP("image file to open"), 0  },
   {  0, 0, 0  }
};


int main(int argc, char *argv[])
{
   KAboutData aboutData( "kscribble", I18N_NOOP("KScribble"),
      VERSION, description, KAboutData::License_GPL,
      "(c) 2000, Ralf Nolden");
   aboutData.addAuthor("Ralf Nolden",0, "rnolden@kdevelop.de");
   KCmdLineArgs::init( argc, argv, &amp;aboutData );
   KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.

   KApplication app;
   KImageIO::registerFormats();

   if (app.isRestored())
   {
      RESTORE(KScribbleApp);
   }
   else
   {
      KScribbleApp *kscribble = new KScribbleApp();
      kscribble-&gt;show();

      KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

      if (args-&gt;count())
        for(int i=0;i&lt;args-&gt;count();i++)
          kscribble-&gt;openDocumentFile(args-&gt;arg(i));
      else
        kscribble-&gt;openDocumentFile();

      args-&gt;clear();
   }

   return app.exec();
}</programlisting>
</para>
</sect1>

<sect1 id="kscribble.h">
<title>kscribble.h</title>

<para><programlisting>/***************************************************************************
                          kscribble.h  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef KSCRIBBLE_H
#define KSCRIBBLE_H


#ifdef HAVE_CONFIG_H
#include &lt;config.h&gt;
#endif

// include files for Qt
#include &lt;qstrlist.h&gt;
#include &lt;qworkspace.h&gt;

// include files for KDE
#include &lt;kapp.h&gt;
#include &lt;ktmainwindow.h&gt;
#include &lt;kaccel.h&gt;
#include &lt;kimgio.h&gt;

// forward declaration of the KScribble classes
class KScribbleDoc;
class KScribbleView;

/**
  * The base class for KScribble application windows. It sets up the main
  * window and reads the config file as well as providing a menubar, toolbar
  * and statusbar. In initView(), your main view is created as the MDI child window manager.
  * Child windows are created in createClient(), which gets a document instance as it's document to
  * display whereby one document can have several views.The MDI child is an instance of KScribbleView,
  * the document an instance of KScribbleDoc.
  * KScribbleApp reimplements the methods that KTMainWindow provides for main window handling and supports
  * full session management as well as keyboard accelerator configuration by using KAccel.
  * @see KTMainWindow
  * @see KApplication
  * @see KConfig
  * @see KAccel
  *
  * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
  * @version KDevelop version 1.1 code generation
  */
class KScribbleApp : public KTMainWindow
{
  Q_OBJECT

  public:
    /** construtor of KScribbleApp, calls all init functions to create the application.
     *  @see initMenuBar initToolBar
     */
    KScribbleApp();
    ~KScribbleApp();
    /** enables menuentries/toolbar items
     */
    void enableCommand(int id_);
    /** disables menuentries/toolbar items
     */
    void disableCommand(int id_);
    /** opens a file specified by commandline option
     */
    void openDocumentFile(const char *file=0);

  protected:
    /** queryClose is called by KTMainWindow on each closeEvent of a window. Against the
     *  default implementation (only returns true), this overridden function retrieves all modified documents
     *  from the open document list and asks the user to select which files to save before exiting the application.
     *  @see KTMainWindow#queryClose
     *  @see KTMainWindow#closeEvent
     */
    virtual bool queryClose();
    /** queryExit is called by KTMainWindow when the last window of the application is going to be closed
     *  during the closeEvent().
     *  Against the default implementation that just returns true, this calls saveOptions() to save the
     *  settings of the last window's properties.
     *  @see KTMainWindow#queryExit
     *  @see KTMainWindow#closeEvent
     */
    virtual bool queryExit();
    /** saves the window properties for each open window during session end to the session config file,
     *  including saving the currently opened file by a temporary filename provided by KApplication.
     *  @see KTMainWindow#saveProperties
     */
    virtual void saveProperties(KConfig *_cfg);
    /** reads the session config file and restores the application's state including the last
     *  opened files and documents by reading the temporary files saved by saveProperties()
     *  @see KTMainWindow#readProperties
     */
    virtual void readProperties(KConfig *_cfg);
    /** event filter to catch close events for MDI child windows and is installed in createClient() on every
      * child window. Closing a window calls the eventFilter first which removes the view from the connected
      * documents' view list. If the last view is going to be closed, the eventFilter() tests if the document
      * is modified; if yes, it asks the user to save the document. If the document title contains "Untitled",
      * slotFileSaveAs() gets called to get a save name and path.
      */
    virtual bool eventFilter(QObject* object, QEvent* event);
    /** creates a new child window. The document that will be connected to it
     *  has to be created before and the instances filled, with e.g. openDocument().
     *  Then call createClient() to get a new MDI child window.
     *  @see KScribbleDoc#addView
     *  @see KScribbleDoc#openDocument
     *  @param doc pointer to the document instance that the view will
     *  be connected to.
     */
    void createClient(KScribbleDoc* doc);
    /** accepts drag events for images */
    virtual void dragEnterEvent( QDragEnterEvent* );
    /** accepts drops and opens a new document
     *  for each drop */
    virtual void dropEvent( QDropEvent* );

  private slots:
    /** sets the main application window title each time the active MDI child window changes. */
    void setWndTitle(QWidget*);
    /** switch argument for slot selection by menu or toolbar ID */
    void commandCallback(int id_);
    /** switch argument for Statusbar help entries on slot selection. Add your ID's help
     *  here for toolbars and menubar entries. */
    void statusCallback(int id_);
    /** add a opened file to the recent file list and update recent file menu*/
    void addRecentFile(const QString &amp;file);
    /** clears the document in the actual view to reuse it as the new document */
    void slotFileNew();
    /** open a file and load it into the document*/
    void slotFileOpen();
    /** opens a file from the recent files menu */
    void slotFileOpenRecent(int id_);
    /** save a document */
    void slotFileSave();
    /** save a document by a new filename*/
    void slotFileSaveAs();
    /** asks for saving if the file is modified, then closes the actual file and window*/
    void slotFileClose();
    /** print the actual file */
    void slotFilePrint();
    /** closes all documents and quits the application.*/
    void slotFileQuit();
    /** reverts the last user action for the active window */
    void slotEditUndo();
    /** put the marked text/object into the clipboard and remove
     *  it from the document
     */
    void slotEditCut();
    /** put the marked text/object into the clipboard
     */
    void slotEditCopy();
    /** paste the clipboard into the document
     */
    void slotEditPaste();
    /** clears the current document */
    void slotEditClearAll();
    /** sets the pen width */
    void slotPenBrush();
    /** sets the pen color */
    void slotPenColor();
    /** toggles the toolbar
     */
    void slotViewToolBar();
    /** toggles the statusbar
     */
    void slotViewStatusBar();
    /** creates a new view for the document in the active child window and adds the new view to the
     * list of views the document maintains.
     */
    void slotWindowNewWindow();
    /** changes the statusbar contents for the standard label permanently, used to indicate current actions.
     * @param text the text that is displayed in the statusbar
     */
    void slotStatusMsg(const QString &amp;text);
    /** changes the status message of the whole statusbar for two seconds, then restores the last status.
     * This is used to display statusbar messages that give information about actions for
     * toolbar icons and menuentries.
     * @param text the text that is displayed in the statusbar
     */
    void slotStatusHelpMsg(const QString &amp;text);
    /** gets called when the window menu is activated; recreates the window menu with all opened window titles. */
    void windowMenuAboutToShow();
    /** activates the MDI child widget when it gets selected from the window menu. */
    void windowMenuActivated( int id );

  private:
    /** save general Options like all bar positions and status as well as the geometry and
     * the recent file list to the configuration file
     */   
    void saveOptions();
    /** read general Options again and initialize all variables like the recent file list
     */
    void readOptions();
    /** initKeyAccel creates the keyboard accelerator items for the available slots and changes the menu accelerators.
     * @see KAccel
     */
    void initKeyAccel();
    /** initMenuBar creates the menubar and inserts the menupopups as well as creating the helpMenu.
     */
    void initMenuBar();
    /** this creates the toolbars.
     */
    void initToolBar();
    /** sets up the statusbar for the main window by initialzing a statuslabel.
     */
    void initStatusBar();

    /** Creates the main view of the KTMainWindow instance and initializes the MDI view area including any needed
     *  connections.
     */
    void initView();

    /** contains the recently used filenames */
    QStrList recentFiles;

    /** the configuration object of the application */
    KConfig *config;
    /** the key accelerator container */
    KAccel *keyAccel;
    /** the recent file menu containing the last five opened files */
    QPopupMenu *pRecentFileMenu;
    /** the file menu */
    QPopupMenu* pFileMenu;
    /** the edit menu */
    QPopupMenu* pEditMenu;
    /** the pen menu */
    QPopupMenu* pPenMenu;    
    /** the view menu */
    QPopupMenu* pViewMenu;
    /** the window menu */
    QPopupMenu *pWindowMenu;
    /** pWorkspace is the MDI frame widget that handles MDI child widgets. Inititalized in
     * initView()
     */
    QWorkspace *pWorkspace;
    /** the printer instance */
    QPrinter *printer;
    /** a counter that gets increased each time the user creates a new document with "File"-&gt;"New" */
    int untitledCount;
    /** a list of all open documents. If the last window of a document gets closed, the installed eventFilter
     * removes this document from the list. The document list is checked for modified documents when the user
     * is about to close the application. */
    QList&lt;KScribbleDoc&gt; *pDocList;  

};

#endif // KSCRIBBLE_H</programlisting>
</para>
</sect1>

<sect1 id="kscribble.cpp">
<title>kscribble.cpp</title>

<para><programlisting>/***************************************************************************
                          kscribble.cpp  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

// include files for QT
#include &lt;qdir.h&gt;
#include &lt;qprinter.h&gt;
#include &lt;qvbox.h&gt;
#include &lt;qwhatsthis.h&gt;
#include &lt;qtooltip.h&gt;
#include &lt;qtoolbutton.h&gt;
#include &lt;qimage.h&gt;
#include &lt;qdragobject.h&gt;


// include files for KDE
#include &lt;kiconloader.h&gt;
#include &lt;kmessagebox.h&gt;
#include &lt;kfiledialog.h&gt;
#include &lt;kcolordlg.h&gt;
#include &lt;kmenubar.h&gt;
#include &lt;klocale.h&gt;
#include &lt;kconfig.h&gt;

// application specific includes
#include "kscribble.h"
#include "kscribbleview.h"
#include "kscribbledoc.h"
#include "resource.h"
#include "kpenbrushdlg.h"


KScribbleApp::KScribbleApp()
{
  config=kapp-&gt;config();
  printer = new QPrinter;
  untitledCount=0;
  pDocList = new QList&lt;KScribbleDoc&gt;();
  pDocList-&gt;setAutoDelete(true);
  setAcceptDrops(true);

  ///////////////////////////////////////////////////////////////////
  // call inits to invoke all other construction parts
  initMenuBar();
  initToolBar();
  initStatusBar();
  initKeyAccel();
  initView();
  
  readOptions();

  ///////////////////////////////////////////////////////////////////
  // disable menu and toolbar items at startup
  disableCommand(ID_EDIT_UNDO);
}

KScribbleApp::~KScribbleApp()
{
  delete printer;
}

void KScribbleApp::initKeyAccel()
{
  keyAccel = new KAccel(this);
  
  // fileMenu accelerators
  keyAccel-&gt;connectItem(KStdAccel::New, this, SLOT(slotFileNew()));
  keyAccel-&gt;connectItem(KStdAccel::Open, this, SLOT(slotFileOpen()));
  keyAccel-&gt;connectItem(KStdAccel::Save, this, SLOT(slotFileSave()));
  keyAccel-&gt;connectItem(KStdAccel::Close, this, SLOT(slotFileClose()));
  keyAccel-&gt;connectItem(KStdAccel::Print, this, SLOT(slotFilePrint()));
  keyAccel-&gt;connectItem(KStdAccel::Quit, this, SLOT(slotFileQuit()));
  // editMenu accelerators
  keyAccel-&gt;connectItem(KStdAccel::Cut, this, SLOT(slotEditCut()));
  keyAccel-&gt;connectItem(KStdAccel::Copy, this, SLOT(slotEditCopy()));
  keyAccel-&gt;connectItem(KStdAccel::Paste, this, SLOT(slotEditPaste()));

  keyAccel-&gt;connectItem(KStdAccel::Help, this, SLOT(appHelpActivated()));
      
  keyAccel-&gt;changeMenuAccel(pFileMenu, ID_FILE_NEW, KStdAccel::New);
  keyAccel-&gt;changeMenuAccel(pFileMenu, ID_FILE_OPEN, KStdAccel::Open);
  keyAccel-&gt;changeMenuAccel(pFileMenu, ID_FILE_SAVE, KStdAccel::Save);
  keyAccel-&gt;changeMenuAccel(pFileMenu, ID_FILE_CLOSE, KStdAccel::Close);
  keyAccel-&gt;changeMenuAccel(pFileMenu, ID_FILE_PRINT, KStdAccel::Print);
  keyAccel-&gt;changeMenuAccel(pFileMenu, ID_FILE_QUIT, KStdAccel::Quit);

  keyAccel-&gt;changeMenuAccel(pEditMenu, ID_EDIT_CUT, KStdAccel::Cut);
  keyAccel-&gt;changeMenuAccel(pEditMenu, ID_EDIT_COPY, KStdAccel::Copy);
  keyAccel-&gt;changeMenuAccel(pEditMenu, ID_EDIT_PASTE, KStdAccel::Paste);

  keyAccel-&gt;readSettings();  
}

void KScribbleApp::initMenuBar()
{
  ///////////////////////////////////////////////////////////////////
  // MENUBAR
  pRecentFileMenu = new QPopupMenu(this);
  connect(pRecentFileMenu, SIGNAL(activated(int)), SLOT(slotFileOpenRecent(int)));

  ///////////////////////////////////////////////////////////////////
  // menuBar entry file-Menu
  pFileMenu = new QPopupMenu(this);
  pFileMenu-&gt;insertItem(BarIcon("filenew"), i18n("&amp;New"), ID_FILE_NEW);
  pFileMenu-&gt;insertItem(BarIcon("fileopen"), i18n("&amp;Open..."), ID_FILE_OPEN);
  pFileMenu-&gt;insertItem(i18n("Open &amp;recent"), pRecentFileMenu, ID_FILE_OPEN_RECENT);

  pFileMenu-&gt;insertItem(i18n("&amp;Close"), ID_FILE_CLOSE);
  pFileMenu-&gt;insertSeparator();
  pFileMenu-&gt;insertItem(BarIcon("filefloppy") ,i18n("&amp;Save"), ID_FILE_SAVE);
  pFileMenu-&gt;insertItem(i18n("Save &amp;As..."), ID_FILE_SAVE_AS);
  pFileMenu-&gt;insertSeparator();
  pFileMenu-&gt;insertItem(BarIcon("fileprint"), i18n("&amp;Print..."), ID_FILE_PRINT);
  pFileMenu-&gt;insertSeparator();
  pFileMenu-&gt;insertItem(i18n("E&amp;xit"), ID_FILE_QUIT);
  
  ///////////////////////////////////////////////////////////////////
  // menuBar entry edit-Menu
  pEditMenu = new QPopupMenu(this);
  pEditMenu-&gt;insertItem(BarIcon("undo"), i18n("&amp;Undo"), ID_EDIT_UNDO);
  pEditMenu-&gt;insertSeparator();
  pEditMenu-&gt;insertItem(BarIcon("editcut"), i18n("Cu&amp;t"), ID_EDIT_CUT);
  pEditMenu-&gt;insertItem(BarIcon("editcopy"), i18n("&amp;Copy"), ID_EDIT_COPY);
  pEditMenu-&gt;insertItem(BarIcon("editpaste"), i18n("&amp;Paste"), ID_EDIT_PASTE);
  pEditMenu-&gt;insertItem(BarIcon("delete"),i18n("&amp;Clear All"), ID_EDIT_CLEAR_ALL);

  ///////////////////////////////////////////////////////////////////
  // menuBar entry pen-Menu
  pPenMenu = new QPopupMenu();
  pPenMenu-&gt;insertItem(i18n("&amp;Color"), ID_PEN_COLOR);
  pPenMenu-&gt;insertItem(i18n("&amp;Brush"), ID_PEN_BRUSH);

  ///////////////////////////////////////////////////////////////////
  // menuBar entry view-Menu
  pViewMenu = new QPopupMenu(this);
  pViewMenu-&gt;setCheckable(true);
  pViewMenu-&gt;insertItem(i18n("&amp;Toolbar"), ID_VIEW_TOOLBAR);
  pViewMenu-&gt;insertItem(i18n("&amp;Statusbar"), ID_VIEW_STATUSBAR);

  ///////////////////////////////////////////////////////////////////
  // menuBar entry window-Menu
  pWindowMenu = new QPopupMenu(this);
  pWindowMenu-&gt;setCheckable(true);


  ///////////////////////////////////////////////////////////////////
  // menuBar entry helpMenu
  QPopupMenu* pHelpMenu = helpMenu(i18n("KScribble" VERSION "\n\n(c) 2000 by\n"
                                  "Ralf Nolden\nRalf.Nolden@post.rwth-aachen.de"));

  ///////////////////////////////////////////////////////////////////
  // MENUBAR CONFIGURATION
  // insert your popup menus with the according menubar entries in the order
  // they will appear later from left to right
  menuBar()-&gt;insertItem(i18n("&amp;File"), pFileMenu);
  menuBar()-&gt;insertItem(i18n("&amp;Edit"), pEditMenu);
  menuBar()-&gt;insertItem(i18n("&amp;Pen"), pPenMenu);
  menuBar()-&gt;insertItem(i18n("&amp;View"), pViewMenu);
  menuBar()-&gt;insertItem(i18n("&amp;Window"), pWindowMenu );
  menuBar()-&gt;insertItem(i18n("&amp;Help"), pHelpMenu);

  ///////////////////////////////////////////////////////////////////
  // CONNECT THE MENU SLOTS WITH SIGNALS
  // for execution slots and statusbar messages
  connect(pFileMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
  connect(pFileMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));

  connect(pEditMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
  connect(pEditMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));

  connect(pPenMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
  connect(pPenMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));

  connect(pViewMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
  connect(pViewMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));

  connect(pWindowMenu, SIGNAL(aboutToShow() ), SLOT( windowMenuAboutToShow() ) );
  connect(pWindowMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
  connect(pWindowMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));

}

void KScribbleApp::initToolBar()
{

  ///////////////////////////////////////////////////////////////////
  // TOOLBAR
  toolBar()-&gt;insertButton(BarIcon("filenew"), ID_FILE_NEW, true, i18n("New File"));
  toolBar()-&gt;insertButton(BarIcon("fileopen"), ID_FILE_OPEN, true, i18n("Open File"));
  toolBar()-&gt;insertButton(BarIcon("filefloppy"), ID_FILE_SAVE, true, i18n("Save File"));
  toolBar()-&gt;insertButton(BarIcon("fileprint"), ID_FILE_PRINT, true, i18n("Print"));
  toolBar()-&gt;insertSeparator();
  toolBar()-&gt;insertButton(BarIcon("editcut"), ID_EDIT_CUT, true, i18n("Cut"));
  toolBar()-&gt;insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy"));
  toolBar()-&gt;insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste"));
  toolBar()-&gt;insertSeparator();
  toolBar()-&gt;insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") );
  toolBar()-&gt;insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") );
  toolBar()-&gt;insertSeparator();
  toolBar()-&gt;insertButton(BarIcon("help"), ID_HELP_CONTENTS, SIGNAL(clicked()),
          this, SLOT(appHelpActivated()), true,i18n("Help"));

  QToolButton *btnwhat = QWhatsThis::whatsThisButton(toolBar());
  QToolTip::add(btnwhat, i18n("What's this...?"));
  toolBar()-&gt;insertWidget(ID_HELP_WHATS_THIS, btnwhat-&gt;sizeHint().width(), btnwhat);

  ///////////////////////////////////////////////////////////////////
  // INSERT YOUR APPLICATION SPECIFIC TOOLBARS HERE WITH toolBar(n)


  ///////////////////////////////////////////////////////////////////
  // CONNECT THE TOOLBAR SLOTS WITH SIGNALS - add new created toolbars by their according number
  // connect for invoking the slot actions
  connect(toolBar(), SIGNAL(clicked(int)), SLOT(commandCallback(int)));
  // connect for the status help on holing icons pressed with the mouse button
  connect(toolBar(), SIGNAL(pressed(int)), SLOT(statusCallback(int)));

}

void KScribbleApp::initStatusBar()
{
  ///////////////////////////////////////////////////////////////////
  // STATUSBAR
  // TODO: add your own items you need for displaying current application status.
  statusBar()-&gt;insertItem(i18n("Ready."), ID_STATUS_MSG);
}


void KScribbleApp::initView()
{
  ////////////////////////////////////////////////////////////////////
  // here the main view of the KTMainWindow is created by a background box and
  // the QWorkspace instance for MDI view.
  QVBox* view_back = new QVBox( this );
  view_back-&gt;setFrameStyle( QFrame::StyledPanel | QFrame::Sunken );
  pWorkspace = new QWorkspace( view_back );
  connect(pWorkspace, SIGNAL(windowActivated(QWidget*)), this, SLOT(setWndTitle(QWidget*)));
  setView(view_back);
}

void KScribbleApp::setWndTitle(QWidget*){
  setCaption(pWorkspace-&gt;activeWindow()-&gt;caption());
}

void KScribbleApp::enableCommand(int id_)
{
  ///////////////////////////////////////////////////////////////////
  // enable menu and toolbar functions by their ID's
  menuBar()-&gt;setItemEnabled(id_, true);
  toolBar()-&gt;setItemEnabled(id_, true);
}

void KScribbleApp::disableCommand(int id_)
{
  ///////////////////////////////////////////////////////////////////
  // disable menu and toolbar functions by their ID's
  menuBar()-&gt;setItemEnabled(id_, false);
  toolBar()-&gt;setItemEnabled(id_, false);
}

void KScribbleApp::addRecentFile(const QString &amp;file)
{
  if(recentFiles.contains(file))
    return; // it's already there

  if( recentFiles.count() &lt; 5)
    recentFiles.prepend(file);
  else{
    recentFiles.remove(recentFiles.last());
    recentFiles.prepend(file);
  }

  pRecentFileMenu-&gt;clear();

  for ( int i =0 ; i &lt; (int)recentFiles.count(); i++){
    pRecentFileMenu-&gt;insertItem(recentFiles.at(i));
  }

}

void KScribbleApp::createClient(KScribbleDoc* doc)
{
  KScribbleView* w = new KScribbleView(doc, pWorkspace,0,WDestructiveClose);
  w-&gt;installEventFilter(this);
  doc-&gt;addView(w);
  w-&gt;setIcon(kapp-&gt;miniIcon());
  if ( pWorkspace-&gt;windowList().isEmpty() ) // show the very first window in maximized mode
    w-&gt;showMaximized();
  else
    w-&gt;show();
}

void KScribbleApp::openDocumentFile(const char* file)
{
  slotStatusMsg(i18n("Opening file..."));
  KScribbleDoc* doc;
  // check, if document already open. If yes, set the focus to the first view
  for(doc=pDocList-&gt;first(); doc &gt; 0; doc=pDocList-&gt;next())
  {
    if(doc-&gt;pathName()==file)
    {
       KScribbleView* view=doc-&gt;firstView();  
       view-&gt;setFocus();
       return;
     }
  }
  doc = new KScribbleDoc();
  pDocList-&gt;append(doc);
  doc-&gt;newDocument();
  // Creates an untitled window if file is 0  
  if(!file)
  {
    untitledCount+=1;
    QString fileName=QString(i18n("Untitled%1")).arg(untitledCount);
    doc-&gt;setPathName(fileName);
    doc-&gt;setTitle(fileName);
  }
  // Open the file
  else
  {
    QString format=QImageIO::imageFormat(file);
    if(!doc-&gt;openDocument(file,format))
      KMessageBox::error (this,i18n("Could not open document !"), i18n("Error !"));
    addRecentFile(file);
  }
  // create the window
  createClient(doc);

  slotStatusMsg(i18n("Ready."));
}


void KScribbleApp::saveOptions()
{  
  config-&gt;setGroup("General Options");
  config-&gt;writeEntry("Geometry", size());
  config-&gt;writeEntry("Show Toolbar", toolBar()-&gt;isVisible());
  config-&gt;writeEntry("Show Statusbar",statusBar()-&gt;isVisible());
  config-&gt;writeEntry("ToolBarPos", (int) toolBar()-&gt;barPos());
  config-&gt;writeEntry("Recent Files", recentFiles);
}


void KScribbleApp::readOptions()
{
  
  config-&gt;setGroup("General Options");

  // bar status settings
  bool bViewToolbar = config-&gt;readBoolEntry("Show Toolbar", true);
  menuBar()-&gt;setItemChecked(ID_VIEW_TOOLBAR, bViewToolbar);
  if(!bViewToolbar)
  {
     enableToolBar(KToolBar::Hide);
  }
  
  bool bViewStatusbar = config-&gt;readBoolEntry("Show Statusbar", true);
  menuBar()-&gt;setItemChecked(ID_VIEW_STATUSBAR, bViewStatusbar);
  if(!bViewStatusbar)
  {
    enableStatusBar(KStatusBar::Hide);
  }

  // bar position settings
  KToolBar::BarPosition toolBarPos;
  toolBarPos=(KToolBar::BarPosition) config-&gt;readNumEntry("ToolBarPos", KToolBar::Top);
  toolBar()-&gt;setBarPos(toolBarPos);

  // initialize the recent file list
  config-&gt;readListEntry("Recent Files",recentFiles);

  for (int i=0; i &lt; (int) recentFiles.count(); i++)
  {
    pRecentFileMenu-&gt;insertItem(recentFiles.at(i));
  }

  QSize size=config-&gt;readSizeEntry("Geometry");
  if(!size.isEmpty())
  {
    resize(size);
  }
  else
    resize(400,350);

}

void KScribbleApp::saveProperties(KConfig *_cfg)
{

}


void KScribbleApp::readProperties(KConfig* _cfg)
{
}    

bool KScribbleApp::queryClose()
{

  QStringList saveFiles;
  KScribbleDoc* doc;
  if(pDocList-&gt;isEmpty())
    return true;

  for(doc=pDocList-&gt;first(); doc!=0;doc=pDocList-&gt;next()){
    if(doc-&gt;isModified())
      saveFiles.append(doc-&gt;title());
  }
  if(saveFiles.isEmpty())
    return true;
        
  switch (KMessageBox::questionYesNoList(this,
      i18n("One or more documents have been modified.\nSave changes before exiting?"),saveFiles))     
  {
    case KMessageBox::Yes:
      for(doc=pDocList-&gt;first(); doc!=0;doc=pDocList-&gt;next()){
        if(doc-&gt;title().contains(i18n("Untitled")))
          slotFileSaveAs();
        else
        {
          if(!doc-&gt;saveDocument(doc-&gt;pathName())){
            KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
            return false;
          }
        }
       }
      return true;
    case KMessageBox::No:
    default:
      return true;
  }
}

bool KScribbleApp::queryExit()
{
  saveOptions();
  return true;
}

bool KScribbleApp::eventFilter(QObject* object, QEvent* event){
  if(event-&gt;type() == QEvent::Close)
  {
    QCloseEvent* e=(QCloseEvent*)event;
    KScribbleView* pView=(KScribbleView*)object;
    KScribbleDoc* pDoc=pView-&gt;getDocument();
    if(pDoc-&gt;canCloseFrame(pView))
    {
       pDoc-&gt;removeView(pView);
       if(!pDoc-&gt;firstView())
         pDocList-&gt;remove(pDoc);
       
      e-&gt;accept();
      //////////////  
      if(pWorkspace-&gt;windowList().count()==1)
        setPlainCaption(kapp-&gt;caption());
      else
        setCaption(pWorkspace-&gt;activeWindow()-&gt;caption());      
      //////////////
    }
    else
      e-&gt;ignore();
  }
  return QWidget::eventFilter( object, event );    // standard event processing
}

/////////////////////////////////////////////////////////////////////
// SLOT IMPLEMENTATION
/////////////////////////////////////////////////////////////////////


void KScribbleApp::slotFileNew()
{
  slotStatusMsg(i18n("Creating new document..."));

  openDocumentFile();

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileOpen()
{
  slotStatusMsg(i18n("Opening file..."));
  
  QString fileToOpen=KFileDialog::getOpenFileName(QDir::currentDirPath(),
            KImageIO::pattern(KImageIO::Reading), this, i18n("Open File..."));
  if(!fileToOpen.isEmpty())
  {
    openDocumentFile(fileToOpen);    
  }

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileOpenRecent(int id_)
{
  slotStatusMsg(i18n("Opening file..."));
    
  openDocumentFile(pRecentFileMenu-&gt;text(id_));
  
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileSave()
{
  slotStatusMsg(i18n("Saving file..."));
  KScribbleView* m = (KScribbleView*)pWorkspace-&gt;activeWindow();
  if( m )
  {
    KScribbleDoc* doc =  m-&gt;getDocument();
    if(doc-&gt;title().contains(i18n("Untitled")))
     slotFileSaveAs();
    else
      if(!doc-&gt;saveDocument(doc-&gt;pathName()))
        KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
  }
  

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileSaveAs()
{
  slotStatusMsg(i18n("Saving file with a new filename..."));

  QString newName=KFileDialog::getSaveFileName(QDir::currentDirPath(),
                               KImageIO::pattern(KImageIO::Writing), this, i18n("Save as..."));
  if(!newName.isEmpty())
  {
    KScribbleView* m = (KScribbleView*)pWorkspace-&gt;activeWindow();
    if( m )
    {
      KScribbleDoc* doc =  m-&gt;getDocument();
      QString format=QFileInfo(newName).extension();
      format=format.upper();
      if(!doc-&gt;saveDocument(newName,format))
      {
        KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
        return;
      }
      doc-&gt;changedViewList();
      setWndTitle(m);
    }
    
  }

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileClose()
{
  slotStatusMsg(i18n("Closing file..."));
  
  KScribbleView* m = (KScribbleView*)pWorkspace-&gt;activeWindow();
  if( m )
  {
    KScribbleDoc* doc=m-&gt;getDocument();
    doc-&gt;closeDocument();
  }

  
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFilePrint()
{
  slotStatusMsg(i18n("Printing..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m )
    m-&gt;print( printer );

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileQuit()
{
  slotStatusMsg(i18n("Exiting..."));
  saveOptions();
  // close the first window, the list makes the next one the first again.
  // This ensures that queryClose() is called on each window to ask for closing
  KTMainWindow* w;
  if(memberList)
  {
    for(w=memberList-&gt;first(); w!=0; w=memberList-&gt;first())
    {
      // only close the window if the closeEvent is accepted. If the user
      // presses Cancel on the saveModified() dialog,
      // the window and the application stay open.
      if(!w-&gt;close())
      break;
    }
  }  
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotEditUndo()
{
  slotStatusMsg(i18n("Reverting last action..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m )
//    m-&gt;undo();

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotEditCut()
{
  slotStatusMsg(i18n("Cutting selection..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m )
    m-&gt;cutSelection();  

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotEditCopy()
{
  slotStatusMsg(i18n("Copying selection to clipboard..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m )
    m-&gt;copySelection();
    
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotEditPaste()
{
  slotStatusMsg(i18n("Inserting clipboard contents..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m )
    m-&gt;pasteSelection();
    
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotEditClearAll()
{
  slotStatusMsg(i18n("Clearing the document contents..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m ){
    KScribbleDoc* pDoc = m-&gt;getDocument();
    pDoc-&gt;editClearAll();
  }
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotPenBrush()
{
  slotStatusMsg(i18n("Setting brush width..."));

  // get one window with document for a current pen width
  QWidgetList windows = pWorkspace-&gt;windowList();
  KScribbleView* m = (KScribbleView*)windows.at(0);
  KScribbleDoc* pDoc = m-&gt;getDocument();
  int curr_width=pDoc-&gt;penWidth();

  // create the dialog, get the new width and set the pen width for all documents
  KPenBrushDlg* dlg= new KPenBrushDlg(curr_width,this);
  if(dlg-&gt;exec()){
    int width=dlg-&gt;width();
    for ( int i = 0; i &lt; int(windows.count()); ++i )
    {
      m = (KScribbleView*)windows.at(i);
      if ( m )
      {
        pDoc = m-&gt;getDocument();
        pDoc-&gt;setPenWidth(width);
      }
    }
  }
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotPenColor()
{
  slotStatusMsg(i18n("Selecting pen color..."));

  QColor myColor;
  int result = KColorDialog::getColor( myColor, this );
  if ( result == KColorDialog::Accepted )
  {
    QWidgetList windows = pWorkspace-&gt;windowList();
    KScribbleDoc* pDoc;
    KScribbleView* m;
    for ( int i = 0; i &lt; int(windows.count()); ++i )
    {
      m = (KScribbleView*)windows.at(i);
      if ( m )
      {
        pDoc = m-&gt;getDocument();
        pDoc-&gt;setPenColor(myColor);
      }
    }
  }
  slotStatusMsg(i18n("Ready."));
}


void KScribbleApp::slotViewToolBar()
{
  slotStatusMsg(i18n("Toggle the toolbar..."));
  ///////////////////////////////////////////////////////////////////
  // turn Toolbar on or off
  if( menuBar()-&gt;isItemChecked(ID_VIEW_TOOLBAR))
  {
    menuBar()-&gt;setItemChecked(ID_VIEW_TOOLBAR, false);
    enableToolBar(KToolBar::Hide);
  }
  else
  {
    menuBar()-&gt;setItemChecked(ID_VIEW_TOOLBAR, true);
    enableToolBar(KToolBar::Show);
  }    

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotViewStatusBar()
{
  slotStatusMsg(i18n("Toggle the statusbar..."));
  ///////////////////////////////////////////////////////////////////
  //turn Statusbar on or off
  if( menuBar()-&gt;isItemChecked(ID_VIEW_STATUSBAR))
  {
    menuBar()-&gt;setItemChecked(ID_VIEW_STATUSBAR, false);
    enableStatusBar(KStatusBar::Hide);
  }
  else
  {
    menuBar()-&gt;setItemChecked(ID_VIEW_STATUSBAR, true);
    enableStatusBar(KStatusBar::Show);
  }

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotWindowNewWindow()
{
  slotStatusMsg(i18n("Opening a new application window..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace-&gt;activeWindow();
  if ( m ){
     KScribbleDoc* doc = m-&gt;getDocument();
    createClient(doc);
  }

  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotStatusMsg(const QString &amp;text)
{
  ///////////////////////////////////////////////////////////////////
  // change status message permanently
  statusBar()-&gt;clear();
  statusBar()-&gt;changeItem(text, ID_STATUS_MSG);
}


void KScribbleApp::slotStatusHelpMsg(const QString &amp;text)
{
  ///////////////////////////////////////////////////////////////////
  // change status message of whole statusbar temporary (text, msec)
  statusBar()-&gt;message(text, 2000);
}

void KScribbleApp::windowMenuAboutToShow()
{
  pWindowMenu-&gt;clear();
  
  pWindowMenu-&gt;insertItem(i18n("&amp;New Window"), ID_WINDOW_NEW_WINDOW);
  pWindowMenu-&gt;insertItem(i18n("&amp;Cascade"),
                          pWorkspace, SLOT(cascade() ),0 , ID_WINDOW_CASCADE );
  pWindowMenu-&gt;insertItem(i18n("&amp;Tile"),
                          pWorkspace, SLOT(tile() ),0 , ID_WINDOW_TILE );
  
  if ( pWorkspace-&gt;windowList().isEmpty() ) {
    disableCommand(ID_WINDOW_NEW_WINDOW);
    disableCommand(ID_WINDOW_CASCADE);
    disableCommand(ID_WINDOW_TILE);
  }
  
  pWindowMenu-&gt;insertSeparator();
  
  QWidgetList windows = pWorkspace-&gt;windowList();
  for ( int i = 0; i &lt; int(windows.count()); ++i ) {
    int id = pWindowMenu-&gt;insertItem(QString("&amp;%1 ").arg(i+1)+windows.at(i)-&gt;caption(),
                                     this, SLOT( windowMenuActivated( int ) ) );
    pWindowMenu-&gt;setItemParameter( id, i );
    pWindowMenu-&gt;setItemChecked( id, pWorkspace-&gt;activeWindow() == windows.at(i) );
  }
}

void KScribbleApp::windowMenuActivated( int id )
{
  QWidget* w = pWorkspace-&gt;windowList().at( id );
  if ( w )
    w-&gt;setFocus();
}


void KScribbleApp::commandCallback(int id_)
{
  switch (id_)
  {
    case ID_FILE_NEW:
       slotFileNew();
         break;

    case ID_FILE_OPEN:
         slotFileOpen();
         break;

    case ID_FILE_SAVE:
         slotFileSave();
         break;

    case ID_FILE_SAVE_AS:
         slotFileSaveAs();
         break;

    case ID_FILE_CLOSE:
         slotFileClose();
         break;

    case ID_FILE_PRINT:
         slotFilePrint();
         break;

    case ID_FILE_QUIT:
         slotFileQuit();
         break;

    case ID_EDIT_CUT:
         slotEditCut();
         break;

    case ID_EDIT_COPY:
         slotEditCopy();
         break;

    case ID_EDIT_PASTE:
         slotEditPaste();
         break;

    case ID_EDIT_CLEAR_ALL:
         slotEditClearAll();
         break;

    case ID_PEN_BRUSH:
         slotPenBrush();
         break;

    case ID_PEN_COLOR:
         slotPenColor();
         break;

    case ID_VIEW_TOOLBAR:
         slotViewToolBar();
         break;

    case ID_VIEW_STATUSBAR:
         slotViewStatusBar();
         break;

    case ID_WINDOW_NEW_WINDOW:
         slotWindowNewWindow();
       break;

    default:
         break;
  }
}

void KScribbleApp::statusCallback(int id_)
{
  switch (id_)
  {
    case ID_FILE_NEW:
         slotStatusHelpMsg(i18n("Creates a new document"));
         break;

    case ID_FILE_OPEN:
         slotStatusHelpMsg(i18n("Opens an existing document"));
         break;

    case ID_FILE_OPEN_RECENT:
         slotStatusHelpMsg(i18n("Opens a recently used file"));
         break;

    case ID_FILE_SAVE:
         slotStatusHelpMsg(i18n("Saves the currently active document"));
         break;

    case ID_FILE_SAVE_AS:
         slotStatusHelpMsg(i18n("Saves the currently active document as under a new filename"));
         break;

    case ID_FILE_CLOSE:
         slotStatusHelpMsg(i18n("Closes the currently active document"));
         break;

    case ID_FILE_PRINT:
         slotStatusHelpMsg(i18n("Prints out the actual document"));
         break;

    case ID_FILE_QUIT:
         slotStatusHelpMsg(i18n("Quits the application"));
         break;

    case ID_EDIT_UNDO:
         slotStatusHelpMsg(i18n("Reverts the last editing action"));
         break;

    case ID_EDIT_CUT:
         slotStatusHelpMsg(i18n("Cuts the selected section and puts it to the clipboard"));
         break;

    case ID_EDIT_COPY:
         slotStatusHelpMsg(i18n("Copies the selected section to the clipboard"));
         break;

    case ID_EDIT_PASTE:
         slotStatusHelpMsg(i18n("Pastes the clipboard contents to actual position"));
         break;

    case ID_EDIT_CLEAR_ALL:
         slotStatusHelpMsg(i18n("Clears the document contents"));
         break;

    case ID_PEN_BRUSH:
         slotStatusHelpMsg(i18n("Sets the pen width"));
         break;

    case ID_PEN_COLOR:
         slotStatusHelpMsg(i18n("Sets the current pen color"));
         break;

    case ID_VIEW_TOOLBAR:
         slotStatusHelpMsg(i18n("Enables/disables the toolbar"));
         break;

    case ID_VIEW_STATUSBAR:
         slotStatusHelpMsg(i18n("Enables/disables the statusbar"));
         break;

    case ID_WINDOW_NEW_WINDOW:
         slotStatusHelpMsg(i18n("Opens a new view for the current document"));
         break;

    case ID_WINDOW_CASCADE:
         slotStatusHelpMsg(i18n("Cascades all windows"));
         break;

    case ID_WINDOW_TILE:
         slotStatusHelpMsg(i18n("Tiles all windows"));
         break;

    default:
         break;
  }
}
/** accepts drops and opens a new document
for each drop */
void KScribbleApp::dropEvent( QDropEvent* e){

  QImage img;
  if ( QImageDrag::decode(e, img) )
  {
    KScribbleDoc* doc = new KScribbleDoc();
    untitledCount+=1;
    QString fileName=QString(i18n("Untitled%1")).arg(untitledCount);
    doc-&gt;setPathName(fileName);
    doc-&gt;setTitle(fileName);
    doc-&gt;newDocument();
    pDocList-&gt;append(doc);
    KPixmap tmp;
    tmp.resize(img.size());
    tmp.convertFromImage(img);
    doc-&gt;setPixmap(tmp);
    doc-&gt;resizeDocument(tmp.size());
    doc-&gt;setModified();
    createClient(doc);
  }
}
/** accepts drag events for images */
void KScribbleApp::dragEnterEvent( QDragEnterEvent* e){
  e-&gt;accept(QImageDrag::canDecode(e));
}</programlisting>
</para>
</sect1>

<sect1 id="kscribbledoc.h">
<title>kscribbledoc.h</title>

<para><programlisting>/***************************************************************************
                          kscribbledoc.h  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef KSCRIBBLEDOC_H
#define KSCRIBBLEDOC_H

#ifdef HAVE_CONFIG_H
#include &lt;config.h&gt;
#endif

// include files for QT
#include &lt;qobject.h&gt;
#include &lt;qstring.h&gt;
#include &lt;qlist.h&gt;

#include &lt;qsize.h&gt;
#include &lt;qpen.h&gt;
#include &lt;qpoint.h&gt;
//#include &lt;qpixmap.h&gt;
#include &lt;qpointarray.h&gt;

#include &lt;kpixmap.h&gt;


// forward declaration of the KScribble classes
class KScribbleView;

/**  KScribbleDoc provides a document object for a document-view model.
  *
  * The KScribbleDoc class provides a document object that can be used in conjunction with the classes
  * KScribbleApp and KScribbleView to create a document-view model for MDI (Multiple Document Interface)
  * KDE 2 applications based on KApplication and KTMainWindow as main classes and QWorkspace as MDI manager widget.
  * Thereby, the document object is created by the KScribbleApp instance (and kept in a document list) and contains
  * the document structure with the according methods for manipulating the document
  * data by KScribbleView objects. Also, KScribbleDoc contains the methods for serialization of the document data
  * from and to files.
  * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.   
  * @version KDevelop version 1.1 code generation
  */
class KScribbleDoc : public QObject
{
  Q_OBJECT

  friend KScribbleView;

  public:
    /** Constructor for the fileclass of the application */
    KScribbleDoc();
    /** Destructor for the fileclass of the application */
    ~KScribbleDoc();

    /** adds a view to the document which represents the document contents. Usually this is your main view. */
    void addView(KScribbleView *view);
    /** removes a view from the list of currently connected views */
    void removeView(KScribbleView *view);
    /** gets called if a view is removed or added */
    void changedViewList();
    /** returns the first view instance */
    KScribbleView* firstView(){ return pViewList-&gt;first(); };
    /** returns true, if the requested view is the last view of the document */
    bool isLastView();
    /** This method gets called when the user is about to close a frame window. It checks, if more than one view
     * is connected to the document (then the frame can be closed), if pFrame is the last view and the document is
     * modified, the user gets asked if he wants to save the document.
     */
    bool canCloseFrame(KScribbleView* pFrame);
    /** sets the modified flag for the document after a modifying action on the view connected to the document.*/
    void setModified(bool _m=true){ modified=_m; };
    /** returns if the document is modified or not. Use this to determine if your document needs
     * saving by the user on closing.
     */
    bool isModified(){ return modified; };
    /** deletes the document's contents */
    void deleteContents();
    /** initializes the document generally */
    bool newDocument();
    /** closes the acutal document */
    void closeDocument();
    /** loads the document by filename and format and emits the updateViews() signal */
    bool openDocument(const QString &amp;filename, const char *format=0);
    /** saves the document under filename and format.*/  
    bool saveDocument(const QString &amp;filename, const char *format=0);
    /** sets the path to the file connected with the document */
    void setPathName(const QString &amp;name);
    /** returns the pathname of the current document file*/
    const QString&amp; pathName() const;

    /** sets the filename of the document */
    void setTitle(const QString &amp;title);
    /** returns the title of the document */
    const QString&amp; title() const;
    /** get the current Pen */
    const QPen currentPen(){ return pen;};  
    /** returns the pen width */
    const int penWidth() { return pen.width(); }
    /** returns the pen color */
    const QColor penColor(){ return pen.color(); }
    /** sets the pen width */
    void setPenWidth( int w ){ pen.setWidth( w ); }
    /** sets the pen color */
    void setPenColor( const QColor &amp;c ){ pen.setColor( c ); }
    /** sets the pen style by a second toolbar */
    void setPenStyle( PenStyle s){ pen.setStyle(s);}
    /** clears the document contents */
    void editClearAll();

    /** get the document size */
    const QSize docSize(){ return size;};
    /** sets the pixmap contents. Used by KScribbleApp
    to create a new document by drop events */
    void setPixmap(KPixmap pix) { buffer=pix;};
    void resizeDocument(QSize m_size) { size=m_size; };
  public slots:
    /** calls repaint() on all views connected to the document object and is called by the view by
     * which the document has been changed.
     * As this view normally repaints itself, it is excluded from the paintEvent.
     */
    void updateAllViews(KScribbleView *sender);
  
  protected:
  
    QPen pen;
    QPointArray polyline;
    KPixmap buffer;
   
  private:
    /** the modified flag of the current document */
    bool modified;
    QString m_title;
    QString m_filename;
    /** the list of the views currently connected to the document */
    QList&lt;KScribbleView&gt; *pViewList;  
    QSize size;
};

#endif // KSCRIBBLEDOC_H
</programlisting>
</para>
</sect1>

<sect1 id="kscribbledoc.cpp">
<title>kscribbledoc.cpp</title>

<para><programlisting>/***************************************************************************
                          kscribbledoc.cpp  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

// include files for Qt
#include &lt;qdir.h&gt;
#include &lt;qfileinfo.h&gt;
#include &lt;qwidget.h&gt;

// include files for KDE
#include &lt;klocale.h&gt;
#include &lt;kmessagebox.h&gt;
#include &lt;kfiledialog.h&gt;

// application specific includes
#include "kscribbledoc.h"
#include "kscribble.h"
#include "kscribbleview.h"


KScribbleDoc::KScribbleDoc()
{
  pViewList = new QList&lt;KScribbleView&gt;;
  pViewList-&gt;setAutoDelete(false);
}

KScribbleDoc::~KScribbleDoc()
{
  delete pViewList;
}

void KScribbleDoc::addView(KScribbleView *view)
{
  pViewList-&gt;append(view);
  changedViewList();
}

void KScribbleDoc::removeView(KScribbleView *view)
{
    pViewList-&gt;remove(view);
    if(!pViewList-&gt;isEmpty())
      changedViewList();
    else
      deleteContents();
}

void KScribbleDoc::changedViewList(){  
  
  KScribbleView *w;
  if((int)pViewList-&gt;count() == 1){
    w=pViewList-&gt;first();
    w-&gt;setCaption(m_title);
  }
  else{  
    int i;
    for( i=1,w=pViewList-&gt;first(); w!=0; i++, w=pViewList-&gt;next())
      w-&gt;setCaption(QString(m_title+":%1").arg(i));  
  }
}

bool KScribbleDoc::isLastView() {
  return ((int) pViewList-&gt;count() == 1);
}


void KScribbleDoc::updateAllViews(KScribbleView *sender)
{
  KScribbleView *w;
  for(w=pViewList-&gt;first(); w!=0; w=pViewList-&gt;next())
  {
      w-&gt;update(sender);
  }

}

void KScribbleDoc::setPathName(const QString &amp;name)
{
  m_filename=name;
  m_title=QFileInfo(name).fileName();
}

const QString&amp; KScribbleDoc::pathName() const
{
  return m_filename;
}

void KScribbleDoc::setTitle(const QString &amp;title)
{
  m_title=title;
}

const QString &amp;KScribbleDoc::title() const
{
  return m_title;
}


void KScribbleDoc::closeDocument()
{
  KScribbleView *w;
  if(!isLastView())
  {
    for(w=pViewList-&gt;first(); w!=0; w=pViewList-&gt;next())
    {
        if(!w-&gt;close())
         break;
    }
  }
  if(isLastView())
  {
    w=pViewList-&gt;first();
    w-&gt;close();
  }
}

bool KScribbleDoc::newDocument()
{
  /////////////////////////////////////////////////
  // TODO: Add your document initialization code here
  size=QSize(300,200 );
  pen=QPen( Qt::black, 3 );
  polyline=QPointArray(3);
  buffer.resize(size);
  buffer.fill( Qt::white );
  /////////////////////////////////////////////////
  modified=false;
  return true;
}

bool KScribbleDoc::openDocument(const QString &amp;filename, const char *format /*=0*/)
{

  QFile f( filename );
//  if ( !f.open( IO_ReadOnly ) )
//    return false;
  /////////////////////////////////////////////////
  // TODO: Add your document opening code here
  if(!buffer.load( filename, format ))
    return false;
  size=buffer.size();
  /////////////////////////////////////////////////
//  f.close();
  
  modified=false;
  m_filename=filename;
  m_title=QFileInfo(f).fileName();
  return true;
}

bool KScribbleDoc::saveDocument(const QString &amp;filename, const char *format /*=0*/)
{
  QFile f( filename );
//  if ( !f.open( IO_WriteOnly ) )
//    return false;

  /////////////////////////////////////////////////
  // TODO: Add your document saving code here
  if(!buffer.save( filename, format ))
    return false;
  /////////////////////////////////////////////////

//  f.close();

  modified=false;
  m_filename=filename;
  m_title=QFileInfo(f).fileName();
  return true;
}

void KScribbleDoc::deleteContents()
{
  /////////////////////////////////////////////////
  // TODO: Add implementation to delete the document contents
  buffer.fill( Qt::white );
  /////////////////////////////////////////////////

}

bool KScribbleDoc::canCloseFrame(KScribbleView* pFrame)
{
  if(!isLastView())
    return true;
      
  bool ret=false;
  if(isModified())
  {
    QString saveName;
    switch(KMessageBox::warningYesNoCancel(pFrame, i18n("The current file has been modified.\n"
    "Do you want to save it?"),title()))
    {
    case KMessageBox::Yes:
      if(title().contains(i18n("Untitled")))
      {
        saveName=KFileDialog::getSaveFileName(QDir::currentDirPath(),
                             i18n("*|All files"), pFrame, i18n("Save as..."));
        if(saveName.isEmpty())
        return false;
      }
      else
       saveName=pathName();
          
      if(!saveDocument(saveName))
      {
        switch(KMessageBox::warningYesNo(pFrame,i18n("Could not save the current document !\n"
                          "Close anyway ?"), i18n("I/O Error !")))
        {
          case KMessageBox::Yes:
            ret=true;
          case KMessageBox::No:
            ret=false;
        }                
      }
      else
        ret=true;
        break;
    case KMessageBox::No:
      ret=true;
      break;
    case KMessageBox::Cancel:
    default:
      ret=false;         
      break;
    }
  }
  else
    ret=true;
    
  return ret;
}

void KScribbleDoc::editClearAll()
{
  deleteContents();
  setModified();
  updateAllViews(0);
}
</programlisting>
</para>
</sect1>

<sect1 id="kscribbleview.h">
<title>kscribbleview.h</title>

<para><programlisting>/***************************************************************************
                          kscribbleview.h  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef KSCRIBBLEVIEW_H
#define KSCRIBBLEVIEW_H

#ifdef HAVE_CONFIG_H
#include &lt;config.h&gt;
#endif

// include files for Qt
#include &lt;qscrollview.h&gt;
#include &lt;kpixmap.h&gt;

class KScribbleDoc;

/** The KScribbleView class provides the view widget for the document instance connected to it and is displayed
 * as a MDI child window in the main view area of the KScribbleApp class instance. The KScribbleApp
 * class also has an eventFilter()  method that gets installed on every KScribbleView instance to
 * control events of the type QEvent::Close.The document connected to the view instance keeps a list
 * of all view that represent the document contents as there can be more than one view. Views get created in
 * KScribbleApp::createClient() and automatically added to the list of views.
 * The KScribbleView class inherits QWidget as a base. Another possible inheritance besides specialized
 + widgets could be QMainWindow so that you can easily set up the main area of your view by setting another view
 * as main widget (QMainWindow::setMainWidget() ).
 * NOTE: The close event always has to be empty (DON`T CALL QWidget::closeEvent(e) in closeEvent())
 * because the installed event filter can only manage a forward implementation. If the QCloseEvent
 * is received by the KScribbleView, the overwritten event handler has to do nothing as the eventFilter
 * has set accept() or ignore() already. If QWidget::closeEvent() is called again, the default event
 * handler will accept the close event and the window gets destroyed even if the installed eventFilter
 * has set the event to be ignored.  
 * @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
 * @version KDevelop version 1.1 code generation
 */
class KScribbleView : public QScrollView
{
  Q_OBJECT

  friend KScribbleDoc;

  public:
    /** Constructor for the view
      * @param pDoc  your document instance that the view represents. Create a document
      * before calling the constructor or connect an already existing document to a new MDI child widget.*/
    KScribbleView(KScribbleDoc* pDoc, QWidget* parent, const char *name, int wflags);
    /** Destructor for the main view */
    ~KScribbleView();
    /** returns a pointer to the document connected to the view*/
    KScribbleDoc *getDocument() const;
    /** gets called to redraw the document contents if it has been modified */
    void update(KScribbleView* pSender);
    /** contains the implementation for printing functionality and gets called by KScribbleApp::slotFilePrint() */
    void print(QPrinter *pPrinter);
    /** cuts out a selection */
    void cutSelection();
    /** copies a selection to the clipboard */
    void copySelection();
    /** pastes the clipboard contents to a selection that can be inserted into the picture */
    void pasteSelection();
        
  protected:
  
    /** overwritten QWidget::closeEvent() to catch closing views. Does nothing, as the closeEvents for
    * KScribbleView's are processed by KScribbleApp::eventFilter(), so this overwitten closeEvent is necessary
    * and has to be empty. Don't overwrite this method !
    */
    virtual void closeEvent(QCloseEvent* );
    /** overwritten to interpret key events for scrollbars */
    virtual void keyPressEvent( QKeyEvent* );
    /** changed from mousePressEvent() overwriting QScrollView method */
    virtual void viewportMousePressEvent( QMouseEvent* );
    /** changed from mouseReleaseEvent() overwriting QScrollView method */
    virtual void viewportMouseReleaseEvent( QMouseEvent* );
    /** On paste actions inserts the pasted clipboard contents */
    virtual void viewportMouseDoubleClickEvent(QMouseEvent* e);
    /** changed from mouseMoveEvent() overwriting QScrollView method */
    virtual void viewportMouseMoveEvent( QMouseEvent* );
    /** changed from resizeEvent() overwriting QScrollView method */
//    virtual void viewportResizeEvent( QResizeEvent* );
    /** changed from paintEvent() overwriting QScrollView method */
    virtual void viewportPaintEvent( QPaintEvent* );
      
    virtual void viewportDragEnterEvent ( QDragEnterEvent * );

    virtual void viewportDragMoveEvent ( QDragMoveEvent * );

    virtual void viewportDragLeaveEvent ( QDragLeaveEvent * );

    virtual void viewportDropEvent ( QDropEvent * );
  
    /** the document instance */
    KScribbleDoc *doc;
      
  private:
    KPixmap tmp;
    QRect select;
    QClipboard *cb;
    enum Action{IDLE=0, DRAW, SELECT, PASTE, DRAG} action;
};

#endif // KSCRIBBLEVIEW_H
</programlisting>
</para>
</sect1>

<sect1 id="kscribbleview.cpp">
<title>kscribbleview.cpp</title>

<para><programlisting>/***************************************************************************
                          kscribbleview.cpp  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include &lt;iostream.h&gt;

// include files for Qt
#include &lt;qprinter.h&gt;
#include &lt;qpainter.h&gt;
#include &lt;qdir.h&gt;
#include &lt;qsize.h&gt;
#include &lt;qclipboard.h&gt;
#include &lt;qimage.h&gt;
#include &lt;qdragobject.h&gt;

// include files for KDE
#include &lt;kiconloader.h&gt;

// application specific includes
#include "kscribbleview.h"
#include "kscribbledoc.h"
#include "kscribble.h"


KScribbleView::KScribbleView(KScribbleDoc* pDoc, QWidget *parent, const char* name, int wflags)
 : QScrollView(parent, name, wflags | WPaintClever | WNorthWestGravity | WRepaintNoErase)
{
  cb = QApplication::clipboard();
  viewport()-&gt;setAcceptDrops(true);
  setDragAutoScroll(true);
  doc=pDoc;
  action=IDLE;
  viewport()-&gt;setCursor( Qt::crossCursor );
  QSize size=doc-&gt;docSize();
  resizeContents(size.width(), size.height());
  resize(size);
}

KScribbleView::~KScribbleView()
{
}

KScribbleDoc *KScribbleView::getDocument() const
{
  return doc;
}

void KScribbleView::update(KScribbleView* pSender){
  if(pSender != this)
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
}

void KScribbleView::print(QPrinter *pPrinter)
{
  if (pPrinter-&gt;setup(this))
  {
    QPainter p;
    p.begin(pPrinter);
          
    ///////////////////////////////
    // TODO: add your printing code here
    p.drawPixmap(0,0,doc-&gt;buffer);
    ///////////////////////////////
    p.end();
  }
}

/** cuts out a selection */
void KScribbleView::cutSelection(){
  select=select.normalize();
  QPixmap cb_pix;
  cb_pix.resize(select.size());
  // copy selection to cb_pix and copy to clipboard
  bitBlt(&amp;cb_pix, 0, 0,
         &amp;doc-&gt;buffer, select.x()+contentsX(),  select.y()+contentsY(), cb_pix.width(), cb_pix.height());
  cb-&gt;setPixmap(cb_pix);
  // fill cb_pix with white and copy to selection area
  cb_pix.fill(Qt::white);
  bitBlt(&amp;doc-&gt;buffer, select.x()+contentsX(), select.y()+contentsY(),
         &amp;cb_pix, 0, 0, cb_pix.width(), cb_pix.height());
  action = IDLE;
  doc-&gt;setModified();
  doc-&gt;updateAllViews(this);
  viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
}
/** copies a selection to the clipboard */
void KScribbleView::copySelection(){
  select=select.normalize();
  QPixmap cb_pix;
  cb_pix.resize(select.size());
  // copy selection to cb_pix and copy to clipboard
  bitBlt(&amp;cb_pix, 0, 0,
         &amp;doc-&gt;buffer, select.x()+contentsX(),  select.y()+contentsY(),cb_pix.width(), cb_pix.height());
  cb-&gt;setPixmap(cb_pix);
  action = IDLE;
  viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
}
/** pastes the clipboard contents to a selection that can be inserted into the picture */
void KScribbleView::pasteSelection(){
  select=cb-&gt;pixmap().rect();
  action = PASTE;
  viewport()-&gt;setCursor( Qt::sizeAllCursor );
}

void KScribbleView::closeEvent(QCloseEvent* e){

// DO NOT CALL QWidget::closeEvent(e) here !!
// This will accept the closing by QCloseEvent::accept() by default.
// The installed eventFilter() in KScribbleApp takes care for closing the widget
// or ignoring the close event
    
}

void KScribbleView::keyPressEvent( QKeyEvent *e )
{
  switch (e-&gt;key())
  {
    case Key_Right:
      scrollBy( 10, 0 );
      break;
    case Key_Left:
      scrollBy( -10,0);
      break;
    case Key_Up:
      scrollBy( 0, -10 );
      break;
    case Key_Down:
      scrollBy( 0, 10 );
      break;
    case Key_Home:
      setContentsPos(0,0);
      break;
    case Key_End:
      setContentsPos(0,viewport()-&gt;height()-viewport()-&gt;height());
      break;
    case Key_PageUp:
      scrollBy( 0, -viewport()-&gt;height() );
      break;
    case Key_PageDown:
      scrollBy( 0, viewport()-&gt;height() );
      break;
  }

}

void KScribbleView::viewportMousePressEvent( QMouseEvent *e )
{
  if ( e-&gt;button() == LeftButton &amp;&amp; action == IDLE)
  {
    action=DRAW;
    doc-&gt;polyline[2] = doc-&gt;polyline[1] = doc-&gt;polyline[0] = viewportToContents(e-&gt;pos());
    doc-&gt;updateAllViews(this);
  }
  else if ( e-&gt;button() == RightButton &amp;&amp; action == IDLE)
  {
    action = SELECT;
    QPoint pt=e-&gt;pos();
    int x = pt.x() &gt; contentsWidth() ? contentsWidth() : pt.x();
    int y = pt.y() &gt; contentsHeight() ? contentsHeight() : pt.y();
    select.setLeft(x-1);
    select.setTop(y-1);
    select.setRight(x-1);
    select.setBottom(y-1);
  }
  else if( action == SELECT )
  {
    action = IDLE;
    select=select.normalize();
    // drag
    if(select.contains(e-&gt;pos(), true)) // point inside the selection
    {
      tmp.resize(select.size());
      bitBlt(&amp;tmp, 0, 0,
             &amp;doc-&gt;buffer, select.x()+contentsX(),  select.y()+contentsY(), tmp.width(), tmp.height());
      QImage img =tmp.convertToImage();
      QDragObject *d = new QImageDrag( img, viewport() );
      d-&gt;setPixmap(BarIcon("filenew"));
      d-&gt;drag();
    }
    // remove selection
    else
      viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
  }
  else if( action == PASTE )
  {
    if ( e-&gt;button() == RightButton )
    {
      action = IDLE;
      viewport()-&gt;setCursor( Qt::crossCursor );
    }
    QPoint mv_pt (viewport()-&gt;height(), viewport()-&gt;width());
    if(QRect(0,0,mv_pt.x(),mv_pt.y()).contains(e-&gt;pos()))
      select.moveCenter(e-&gt;pos());
    else
    {
      select.moveBottomRight(mv_pt);
    }
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
  }
}

void KScribbleView::viewportMouseReleaseEvent( QMouseEvent *e )
{
  if ( action == DRAW )
  {
    action = IDLE;
    doc-&gt;updateAllViews(this);
  }
  if ( action == SELECT)
  {
    QPoint pt=e-&gt;pos();
    int x = pt.x() &gt; 0 ? pt.x() : 0;
    int y = pt.y() &gt; 0 ? pt.y() : 0;
    select.setRight(x);
    select.setBottom(y);
    QSize size=doc-&gt;docSize();
    select = select.intersect(QRect(0,0,size.width(), size.height()));  
  }
}

/** On paste actions inserts the pasted clipboard contents
 */
void KScribbleView::viewportMouseDoubleClickEvent(QMouseEvent* e)
{
  if( action == PASTE )
  {
    action = IDLE;
    select.moveCenter(e-&gt;pos());
    viewport()-&gt;setCursor( Qt::crossCursor );
    QPixmap cb_pix;
    cb_pix.resize(cb-&gt;pixmap().size());
    cb_pix=cb-&gt;pixmap();
    bitBlt( &amp;doc-&gt;buffer, contentsX()+select.x(), contentsY()+select.y(),
            &amp;cb_pix, 0,0 , select.width(),select.height() );
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
    doc-&gt;setModified();
    doc-&gt;updateAllViews(this);
  }

}

void KScribbleView::viewportMouseMoveEvent( QMouseEvent *e )
{
  if ( action == DRAW )
  {    
    QPainter painter;
    painter.begin( &amp;doc-&gt;buffer );
    painter.setPen( doc-&gt;currentPen() );
    doc-&gt;polyline[2] = doc-&gt;polyline[1];
    doc-&gt;polyline[1] = doc-&gt;polyline[0];
    doc-&gt;polyline[0] = viewportToContents(e-&gt;pos());
    painter.drawPolyline( doc-&gt;polyline );
    painter.end();

    QRect r = doc-&gt;polyline.boundingRect();
    r = r.normalize();
    r.setLeft( r.left() - doc-&gt;penWidth() );
    r.setTop( r.top() - doc-&gt;penWidth() );
    r.setRight( r.right() + doc-&gt;penWidth() );
    r.setBottom( r.bottom() + doc-&gt;penWidth() );

    bitBlt(viewport(), r.x()-contentsX(), r.y()-contentsY() ,
           &amp;doc-&gt;buffer, r.x(), r.y(), r.width(), r.height() );
    doc-&gt;setModified();
    doc-&gt;updateAllViews(this);
  }
  if ( action == SELECT )
  {
    QPoint pt=e-&gt;pos();
    select.setWidth(select.x()+pt.x());
    select.setHeight(select.y()+pt.y());
    select.setRight(pt.x());
    select.setBottom(pt.y());
    QSize size=doc-&gt;docSize();
    select = select.intersect(QRect(0,0,size.width(), size.height()));  
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
  }
  if( action == PASTE )
  {
    QPoint mv_pt (viewport()-&gt;height(), viewport()-&gt;width());
    if(QRect(0,0,mv_pt.x(),mv_pt.y()).contains(e-&gt;pos()))
      select.moveCenter(e-&gt;pos());
    else
    {
      select.moveBottomRight(mv_pt);
    }
    QRect pm_rect=cb-&gt;pixmap().rect();
    select.setWidth(pm_rect.width());
    select.setHeight(pm_rect.height());
    QSize size=doc-&gt;docSize();
    select = select.intersect(QRect(0,0,size.width(), size.height()));  
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
    doc-&gt;setModified();
    doc-&gt;updateAllViews(this);
  }
}

//void KScribbleView::viewportResizeEvent( QResizeEvent *e )
//{
//}

void KScribbleView::viewportPaintEvent( QPaintEvent *e )
{
  bitBlt( viewport(),0,0, &amp;doc-&gt;buffer,contentsX() ,contentsY() );

  if( action == PASTE )
  {
    tmp.resize(cb-&gt;pixmap().size());
    tmp=cb-&gt;pixmap();
  }
  if( action == PASTE || action == DRAG )
  {
    QSize size=doc-&gt;docSize();
    select = select.intersect(QRect(0,0,size.width(), size.height()));  
    if(select.intersects(e-&gt;rect()))
      bitBlt(viewport(), select.x(), select.y(), &amp;tmp, 0, 0, select.width(), select.height());
  }
  if( action == PASTE || action == DRAG || action == SELECT )
  {
//    if(select.intersects(e-&gt;rect()))
//    {
      QPainter paint_area;
      paint_area.begin(viewport());
      paint_area.setPen(QPen(Qt::black, 0, DashLine));
      paint_area.drawRect( select );
      paint_area.end();
//    }
  }
  QScrollView::viewportPaintEvent(e);
}

void  KScribbleView::viewportDragEnterEvent ( QDragEnterEvent * e)
{
  e-&gt;accept(QImageDrag::canDecode(e));
  action = DRAG;
}

void  KScribbleView::viewportDragMoveEvent ( QDragMoveEvent * e)
{
  QImage img;
  if ( QImageDrag::decode(e, img) ){
    tmp.resize(img.size());
    tmp.convertFromImage(img);
    select.setWidth(tmp.width());
    select.setHeight(tmp.height());
    select.moveCenter(e-&gt;pos());
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
  }
}

void  KScribbleView::viewportDragLeaveEvent ( QDragLeaveEvent * )
{
  action = IDLE;
  viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
}

void  KScribbleView::viewportDropEvent ( QDropEvent * e)
{
  QImage img;
  if ( QImageDrag::decode(e, img) )
  {
    tmp.resize(img.size());
    tmp.convertFromImage(img);
    select.setWidth(tmp.width());
    select.setHeight(tmp.height());
    select.moveCenter(e-&gt;pos());
    bitBlt(&amp;doc-&gt;buffer, select.x()+contentsX(), select.y()+contentsY(),
           &amp;tmp, 0, 0, tmp.width(), tmp.height());
    doc-&gt;setModified();
    doc-&gt;updateAllViews(this);
  }
  action = IDLE;
    viewport()-&gt;repaint(0,0,visibleWidth(), visibleHeight(), false);
}
</programlisting>
</para>
</sect1>

<sect1 id="kpenbrushdlg.h">
<title>kpenbrushdlg.h</title>

<para><programlisting>/***************************************************************************
                          kpenbrushdlg.h  -  description
                             -------------------
    begin                : Fri Jul 23 1999
    copyright            : (C) 1999 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#ifndef KPENBRUSHDLG_H
#define KPENBRUSHDLG_H

//Generated area. DO NOT EDIT!!!(begin)
#include &lt;qwidget.h&gt;
#include &lt;qspinbox.h&gt;
#include &lt;qlabel.h&gt;
#include &lt;qpushbutton.h&gt;
//Generated area. DO NOT EDIT!!!(end)

#include &lt;qdialog.h&gt;
#include &lt;klocale.h&gt;

/**
  *@author Ralf Nolden
  */

class KPenBrushDlg : public QDialog  {
   Q_OBJECT
public:
  KPenBrushDlg(int curr, QWidget *parent=0, const char *name=0);
  ~KPenBrushDlg();

  int width() { return width_spbox-&gt;value(); };
  
protected slots:
  void slotDefault();

protected:
  void initDialog();
  //Generated area. DO NOT EDIT!!!(begin)
  QSpinBox *width_spbox;
  QLabel *width_label;
  QPushButton *default_btn;
  QPushButton *ok_btn;
  QPushButton *cancel_btn;
  //Generated area. DO NOT EDIT!!!(end)

private:
};

#endif

</programlisting>
</para>
</sect1>

<sect1 id="kpenbrushdlg.cpp">
<title>kpenbrushdlg.cpp</title>

<para><programlisting>/***************************************************************************
                          kpenbrushdlg.cpp  -  description
                             -------------------
    begin                : Fri Jul 23 1999
    copyright            : (C) 1999 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kpenbrushdlg.h"
#include &lt;qwhatsthis.h&gt;
#include &lt;kapp.h&gt;

KPenBrushDlg::KPenBrushDlg(int curr, QWidget *parent, const char *name)
  : QDialog(parent,name,true,WStyle_ContextHelp)
{
  initDialog();
  QWhatsThis::add(width_spbox,i18n("Select brush width"));

  width_spbox-&gt;setValue(curr);
  connect(default_btn, SIGNAL(clicked()), this, SLOT(slotDefault()));
  connect(ok_btn, SIGNAL(clicked()), this, SLOT(accept()));
  connect(cancel_btn, SIGNAL(clicked()), this, SLOT(reject()));
}

KPenBrushDlg::~KPenBrushDlg(){
}

void KPenBrushDlg::slotDefault(){
  width_spbox-&gt;setValue(3);
}
</programlisting>
</para>
</sect1>

<sect1 id="kpenbrushdlgdata.cpp">
<title>kpenbrushdlgdata.cpp</title>

<para><programlisting>/**********************************************************************
            --- KDevelop (KDlgEdit)  generated file ---

            Last generated: Fri Jul 23 10:43:10 1999

            DO NOT EDIT!!!  This file will be automatically
            regenerated by KDevelop.  All changes will be lost.

**********************************************************************/
#include &lt;kapp.h&gt;
#include "kpenbrushdlg.h"

void  KPenBrushDlg::initDialog(){
  this-&gt;resize(370,210);
  this-&gt;setMinimumSize(0,0);
  width_spbox= new QSpinBox(this,"width_spbox");
  width_spbox-&gt;setGeometry(150,50,100,25);
  width_spbox-&gt;setMinimumSize(0,0);
  width_spbox-&gt;setValue(1);
  width_spbox-&gt;setRange(1,99);

  width_label= new QLabel(this,"width_label");
  width_label-&gt;setGeometry(20,50,120,25);
  width_label-&gt;setMinimumSize(0,0);
  width_label-&gt;setText(i18n("Pen Width:"));

  default_btn= new QPushButton(this,"default");
  default_btn-&gt;setGeometry(30,160,100,30);
  default_btn-&gt;setMinimumSize(0,0);
  default_btn-&gt;setText(i18n("Default"));
  default_btn-&gt;setAutoDefault(true);

  ok_btn= new QPushButton(this,"ok");
  ok_btn-&gt;setGeometry(140,160,100,30);
  ok_btn-&gt;setMinimumSize(0,0);
  ok_btn-&gt;setText(i18n("OK"));
  ok_btn-&gt;setAutoDefault(true);

  cancel_btn= new QPushButton(this,"cancel");
  cancel_btn-&gt;setGeometry(250,160,100,30);
  cancel_btn-&gt;setMinimumSize(0,0);
  cancel_btn-&gt;setText(i18n("Cancel"));
  cancel_btn-&gt;setAutoDefault(true);

}</programlisting>
</para>
</sect1>

<sect1 id="resource.h">
<title>resource.h</title>

<para><programlisting>/***************************************************************************
                          resource.h  -  description
                             -------------------
    begin                : Mon Jan 31 11:05:05 CET 2000
    copyright            : (C) 2000 by Ralf Nolden
    email                : Ralf.Nolden@post.rwth-aachen.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef RESOURCE_H
#define RESOURCE_H

#ifdef HAVE_CONFIG_H
#include &lt;config.h&gt;
#endif

///////////////////////////////////////////////////////////////////
// resource.h  -- contains macros used for commands


///////////////////////////////////////////////////////////////////
// COMMAND VALUES FOR MENUBAR AND TOOLBAR ENTRIES


///////////////////////////////////////////////////////////////////
// File-menu entries
#define ID_FILE_NEW                 10010
#define ID_FILE_OPEN                10020
#define ID_FILE_OPEN_RECENT         10030
#define ID_FILE_CLOSE               10040

#define ID_FILE_SAVE                10050
#define ID_FILE_SAVE_AS             10060

#define ID_FILE_PRINT               10070

#define ID_FILE_QUIT                10080

///////////////////////////////////////////////////////////////////
// Edit-menu entries
#define ID_EDIT_UNDO                11010
#define ID_EDIT_COPY                11020
#define ID_EDIT_CUT                 11030
#define ID_EDIT_PASTE               11040
#define ID_EDIT_CLEAR_ALL           11050

///////////////////////////////////////////////////////////////////
// Pen-menu entries
#define ID_PEN_COLOR                14010
#define ID_PEN_BRUSH                14020

///////////////////////////////////////////////////////////////////
// Draw-menu entries
#define ID_DRAW_FIND                15010
#define ID_DRAW_FREEHAND            15020
#define ID_DRAW_LINE                15030
#define ID_DRAW_RECT                15040
#define ID_DRAW_RECT_FILL           15050
#define ID_DRAW_CIRCLE              15060
#define ID_DRAW_CIRCLE_FILL         15070
#define ID_DRAW_ELLIPSE             15080
#define ID_DRAW_ELLIPSE_FILL        15090
#define ID_DRAW_SPRAY               15100
#define ID_DRAW_FILL                15110
#define ID_DRAW_ERASE               15120

///////////////////////////////////////////////////////////////////
// View-menu entries
#define ID_VIEW_TOOLBAR             12010
#define ID_VIEW_STATUSBAR           12020

///////////////////////////////////////////////////////////////////
// Window-menu entries
#define ID_WINDOW_NEW_WINDOW        13010
#define ID_WINDOW_CASCADE           13020
#define ID_WINDOW_TILE              13030

///////////////////////////////////////////////////////////////////
// Help-menu entries
#define ID_HELP_CONTENTS            1002
#define ID_HELP_WHATS_THIS          1003
///////////////////////////////////////////////////////////////////
// General application values
#define ID_STATUS_MSG               1001
#define TOOLS_TOOLBAR               1
#endif // RESOURCE_H
</programlisting>
</para>
</sect1>
  </appendix>
<![ %addindex; [ &docindex; ]]>
</book>
<!--
Local Variables:
mode: sgml
sgml-omittag: nil
sgml-shorttag: t
sgml-general-insert-case: lower
End:
-->
