<!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>

<keywordset>
<keyword>KDE</keyword>
<keyword>KDevelop</keyword>
<keyword>application tutorials</keyword>
</keywordset>

<abstract>
<para>The KDE Application Tutorials Handbook for the KDevelop Integrated
Development Environment, a tutorial collection on how to create KDE applications
through 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>

</bookinfo>

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

<para>As KDE is currently becoming a standard for Unix System desktops, more and
more developers want to take advantage of the well-written <abbrev>API</abbrev>
that the KDE project includes, to create new applications that offer a
<abbrev>GUI</abbrev> as well as an easy use of the classes provided with
Qt/KDE. The &kdevelop; project therefore intends to provide an
<abbrev>IDE</abbrev> which enables developers to create and extend their
applications rapidly with the main focus on KDE.</para><para>As beginners always have
difficulties getting 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, using the &kdevelop; 
<abbrev>IDE</abbrev> as well as creating KDE applications by examples.</para>

<para>The next chapter will explain to you how to create a project with
&kdevelop; that is already a complete application.  Then we'll walk through 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 of how the underlying Qt library
functions, and how to use the classes provided with it. Qt is our starting
point, as KDE is on top of the Qt library classes, and KDE applications make a
wide use of the QT classes.</para>

<para>Later, we will cover 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 that 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 <abbrev>IDE</abbrev>
works in general, and what generated projects are.</para>
</sect1>
</chapter>

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

<para>Before you start the tutorial, you should have set up &kdevelop; to work correctly. Ensure you have
access to the Qt online reference documentation in the <guimenu>"Help"</guimenu>
menu or the corresponding book in the documentation tree. When looking at 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. 
"Examples" will lead you to a list of example programs that Qt includes, and
show for example, usage reference in certain classes. Now, if you are an
absolute beginner, you should make yourself comfortable with the idea behind Qt - providing a
library that offers:</para>

<itemizedlist>
<listitem>
<para>a base application class (<classname>QApplication</classname>)</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 through event loops and virtual methods</para>
</listitem>
</itemizedlist>

<para>If the above list doesn't make sense to you 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 an introduction about the difference between
a Qt and a KDE application, which is not covered much at the first level of the
Qt tutorial step.</para>

<para>To learn about Qt first is 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
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"New"</guimenuitem></menuchoice>
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
in the provided code, to create "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 change.</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 "MyWidget" as the project name. This will already build
us the base class that is needed in chapter 4, so we don't have to take care of
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 fourth page, where the same has to be
set for the source files. Finally, the last page is entered, where you have to
press <guibutton>"Create"</guibutton> to build the project. If the button is not
available, start at the first page again and make sure you didn't miss inserting
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
<guibutton>"Close"</guibutton> to finish the wizard.</para>
    
<para>When looking at the treeview on the left, you will see that the project
alreay includes a class <classname>"MyWidget"</classname> - this is because we
chose this to be the application name. Further, when opening
<guilabel>"Functions"</guilabel> in the classviewer's
<guilabel>"Globals"</guilabel> folder, you will see that the application already
contains a <function>main()</function> function where the execution will
start.</para>

<para>Now the application is already set up. You could do an
<guimenuitem>"Execute"</guimenuitem> or <guimenuitem>"Make"</guimenuitem>, 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 "MyWidget".
Now, when looking at the first tutorial page of the Qt documentation, you will
see that the first application only uses a <function>main()</function>
function. Therefore, we only have to change the given <function>main()</function>
function of our generated project to get the first tutorial step running.</para>

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

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

<para>We could start changing the given code towards the desired result which is
listed in the tutorial, but it can be even easier. Select the contents of the
<function>main()</function> function with the mouse, or by holding the
<keycombo><keycap>Shift</keycap></keycombo> key pressed while selecting the
lines with the <keycombo><keycap>down-arrow</keycap></keycombo> key. Then select
<guimenuitem>"Cut"</guimenuitem> from the <guimenu>"Edit"</guimenu> menu or
press the <keycombo><keycap>DEL</keycap></keycombo> 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
<function>main()</function> function in the documentation browser and press the 
<guiicon>"Copy"</guiicon> icon from the toolbar. Optionally, you could use the
context-menu or the according entry in the <guimenu>"Edit"</guimenu> menu. This
will copy the selection into the clipboard and, after switching back to the
sourcecode window, can be inserted with <guimenuitem>"Paste"</guimenuitem>
(either from the context menu, the toolbar icon, or the according entry in the
<guimenu>"Edit"</guimenu> menu).</para>

<para>Finally, add the according header files outside the
<function>main()</function> function:</para>

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

<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 <guimenuitem>"Make"</guimenuitem> from
the <guimenu>"Build"</guimenu> menu or press the corresponding toolbar
icon. Optionally, you could choose <guimenuitem>"Execute"</guimenuitem> 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! It would be sad to have 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;.</para>

<para> To find an error, you have two options: either select the according error
message in the output window, which will bring you to the line the error
occured, or press the <keycap>F4</keycap> key. If there is
more than one error, you could use the <guimenuitem>"Next Error"</guimenuitem>
entry in the <guimenu>"View"</guimenu> 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, and the more complex they 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 actually 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 behaviour:</para>

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

</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. Here, you will learn about signals and slots, as well as parent-child
relationships between widgets.</para>

<para>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
incredibly short, and nothing serious will happen if something went wrong, even
misconnections do not lead to a segmentation fault. This is the strength of the
Qt library and a lot of KDE/Qt developers will not want to miss it's
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 <classname>QApplication</classname> instance by calling <function>quit()</function>. You will meet <function>quit()</function> again when working with <classname>QApplication</classname>'s KDE-pendant, <application>KApplication</application></para>
</sect1>
    
<sect1 id="writing-your-own-widgets">
<title>Writing your own Widgets</title>

<para>Now, after having successfully modified and extended the
<function>main()</function> function, you will hit the point where you need 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. You have to insert the
code that lesson 4 places in the <filename>main.cpp</filename> file into the
<classname>MyWidget</classname> 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
<filename>qfont.h</filename>.  <classname>QFont</classname> is only used in the
constructor code of <classname>MyWidget</classname>.  Therefore, add
<userinput>#include &lt;qfont.h&gt;</userinput> to the top of the
<filename>mywidget.cpp</filename> file.</para>
      
<para>To run the new application, just hit
<guimenuitem>"Execute"</guimenuitem>. 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, and you will
learn about virtual functions as well. Here, the
<function>resizeEvent()</function> 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><classname>QEvent</classname></para>
</listitem>
<listitem>
<para><classname>QChildEvent</classname></para>
</listitem>
<listitem>
<para><classname>QCloseEvent</classname></para>
</listitem>
<listitem>
<para><classname>QFocusEvent</classname></para>
</listitem>
<listitem>
<para><classname>QKeyEvent</classname></para>
</listitem>
<listitem>
<para><classname>QMouseEvent</classname></para>
</listitem>
<listitem>
<para><classname>QMoveEvent</classname></para>
</listitem>
<listitem>
<para><classname>QPaintEvent</classname></para>
</listitem>
<listitem>
<para><classname>QResizeEvent</classname></para>
</listitem>
<listitem>
<para><classname>QTimerEvent</classname></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 with your own for certain events that have to be processed. Those
would be <abbrev>e.g.</abbrev></para>
<informalexample>
<para><function><type>virtual void </type> mousePressEvent ( <type>QMouseEvent *</type>
)</function></para> </informalexample> <para> 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
<parameter>event</parameter> or just <parameter>e</parameter> as the parameter
name.</para>
      
<para>Then, you can process the event's parameters. For a
<classname>QMouseEvent</classname>, you have to ask if the
<mousebutton>right</mousebutton>, <mousebutton>middle</mousebutton> or
<mousebutton>left</mousebutton> mouse button was the one that caused the
event.</para>
</sect1>

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

<para>In 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". &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
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"New
Class"</guimenuitem></menuchoice>, 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 <classname>LCDRange</classname>, 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
<classname>QWidget</classname>.  Now, as most <abbrev>GUI</abbrev> classes to
add would inherit <classname>QWidget</classname>, the Classgenerator makes it
even easier. Leave the baseclass empty, and instead check <guilabel>"QWidget
child class"</guilabel> in the <guilabel>"Additional Options"</guilabel>
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 <link linkend="step-7-14">chapter 7</link>).</para>
      
<para>As the filenames are automatically inserted, you don't have to take care
of 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 <classname>LCDRange</classname> 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 you already know
everything you have to know about 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 <guimenu>"Build"</guimenu>.  Try executing the
application with a commandline argument such as <option>--geometry</option>, and
debugging it with <application>KDbg</application> in the Tools-window.</para><para> Then you
should be able to proceed with the Qt examples that are provided with Qt.</para>

<para> In the next chapter, you will be introduced into development for KDE 2,
by an example application <application>KScribble</application>, 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
(<abbrev>MDI</abbrev>) model.</para>

<para>You will also get a good idea of concepts that are coming up ahead.  These
include the KDE 2 and the Qt 2.1 libraries, 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 talked about and
will allow even more than you can imagine right now.</para>
    
<para>&kdevelop; 1.1, still based on KDE 1.1.x, provides developers some of the
most exciting possibilities, including the ability to begin developing
applications for KDE 2, while still running a stable desktop environment. For this reason, 
this tutorial will be based on a KDE 2 application that
already makes use of Qt-2.1, offering a solution for the mentioned
<abbrev>MDI</abbrev> 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,
which is hard to understand even for advanced programmers. That will help you
create other type of applications, which uses the document-view model, even if
they only contain one window and you can't see an obvious need for
it.</para>

<para>But let's just move on - you will see what's coming up on you and how
easy it actually is.</para>

<para>The upcoming chapters of the tutorial cover the following several
steps:</para>

<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 <abbrev>MDI</abbrev> and
document-view model.</para></listitem>
<listitem><para>creating the generic framework for
<application>KScribble</application>, and explaining the concept of the
framework.</para></listitem>
<listitem><para>implementing the document class, which 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
<classname>QWidget</classname> inheritance to a scrollable
view.</para></listitem>
</itemizedlist>
    
<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, in the examples section.</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:</para>

<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>kdesupport</literal> and <literal> kdelibs</literal></para>
</listitem>
</itemizedlist>

<para>Both the Qt-2.1 and KDE 2 libraries are currently under development but
are you may consider them close to final.  Changes to be expected for the final
versions are minimal, and may not even affect your application. This means
you are getting to know the newest libraries available - and you have got a
valuable time-saving, enabling you to create your application even before the libraries are
completed.</para>

<para>The following sections describe what to do in detail within three steps:</para>

<procedure>
<step><para>installing the Qt library</para></step>
<step><para>installing the KDE libraries</para></step>
<step><para>setting up &kdevelop;</para></step>
</procedure>

<para>Then you are ready to go, and we will proceed with creating the first step
of the example program <application>KScribble</application> in the next
chapter.</para>
    
<sect1 id="setting-up-qt-2.1">
<title>Setting up Qt-2.1</title>

<para>This first step will give you the minimum requirement to follow our
example.  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; provides 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>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>,
 in the download section. Then change user 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 <filename>/usr/lib</filename>, 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>

<informalexample><screen><command>tar zxvf <replaceable>qtxxx.tar.gz</replaceable></command></screen></informalexample>

<para>depending on the name of the tarball. Then set the environment variable
<envar>QTDIR</envar> to point to the directory where your new Qt-2.1 library is
placed with <abbrev>e.g.</abbrev></para>
<informalexample><screen><command>setenv <envar>QTDIR</envar>=<replaceable>/usr/lib/qt-2.1</replaceable></command></screen></informalexample>

<para> Another solution would be to move the qt-1.44 directory, usually just
named <filename>qt</filename>, to <filename>qt-1.44</filename> and create a
symlink that for now has to point to <filename>qt-2.1</filename>.  After the
library is built, change it back to point to the
<filename>qt-1.44</filename>directory.</para>

<para>Change to the Qt-2.1 directory and type:</para>

<screen><command>./configure</command> 
<command>make</command>
</screen>

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

<para>Now switch to the directory <filename>/usr/lib/qt-2.1/lib</filename> and
copy the created libraries to <filename>/usr/lib</filename> 
<emphasis>exept</emphasis> the symbolic link
<filename>libqt.so</filename>.</para>

<para>Then you are 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 directly to the
section <link linkend="setting-up-kdevelop">Setting up &kdevelop;</link>.</para>
</sect1>

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

<para>The second step leads you through installing the needed KDE
libraries. Download a recent snapshot of the library packages <literal>kdesupport</literal> and <literal>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 new kdesupport directory first. There, type:</para>

<informalexample><screen>
<command>./configure <option>--with-qt-dir=<replaceable>/usr/lib/qt-2.1</replaceable> --prefix=<replaceable>/opt/kde2</replaceable></option></command> 
<command>make</command>

and as root:

<command>make install</command>
</screen></informalexample>

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

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

<para>You are now done with setting up the requirements for the required
libraries to build a KDE 2 application.</para>

<note><para>As a note aside, the KDE library package contains support for
automatic <abbrev>HTML</abbrev> documentation generation, which you can easily
get by installing <application>KDoc</application>, version 2.x. Then type
<command>makekdedoc</command> in your kdelibs directory and
<application>KDoc</application> will create the <abbrev>HTML</abbrev>
documentation in a subdirectory <filename>srcdoc</filename>. 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
<filename>kdelibs/srcdoc</filename>. In the same way, set your Qt-Online
documentation path in &kdevelop; to point to
<filename>/usr/lib/qt-2.1/html</filename>, so you can access the documentation
right away from within &kdevelop;.</para></note>

<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 information as to 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
<menuchoice><guimenu>"Options"</guimenu><guimenuitem>"KDevelop
Setup"</guimenuitem></menuchoice> to access the &kdevelop; configuration. There,
change to the last page, titled <guilabel>"Path"</guilabel>. 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
<filename>/usr/lib/qt-2.1</filename> for the Qt-2.x library path and
<filename>/opt/kde2</filename> for the KDE 2 library path. Press the
<guibutton>"OK"</guibutton> 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 <application>KScribble</application> 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 <abbrev>MDI</abbrev> (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 (<abbrev>SDI</abbrev>). 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:</para>
<itemizedlist>
<listitem>
<para>that every KDE application needs one instance of
<classname>KApplication</classname> (for a Qt application one instance of
<classname>QApplication</classname>)</para>
</listitem>
<listitem>
<para>generally a main window is inherited from
<classname>KTMainWindow</classname> for KDE (for a Qt application inherited from
<classname>QMainWindow</classname>)</para>
</listitem>
<listitem>
<para>that events (<abbrev>e.g.</abbrev>, keyboard presses or mouse movements)
are sent to the application by the underlying window system</para>
</listitem>
<listitem>
<para>that <application>QWidget</application> as the base class for all visible
Graphical User Interface (<abbrev>GUI</abbrev>) components provides specialized
event handlers by virtual functions that filter out the according events by
reimplementing <literal remap="bf">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 <abbrev>GUI</abbrev>
components because the signals inform you about <abbrev>e.g.</abbrev> mouse
presses where appropriate for a wiget</para>
</listitem>
</itemizedlist>

<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 the design of
applications that rely on graphical user interfaces. Therefore, a certain
understanding of the why this is, helps you understand that although the
programmer has other possibilities, it makes sense to make use of the
Document-View Model.</para>

<para> 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 is constructed at program start,
and has to be set as the view area component with a method provided by the main
window: <function>setView(<symbol>your_view</symbol>)</function> for KDE applications using
<classname>KTMainWindow</classname>,
<function>setCentralWidget(<symbol>your_view</symbol>)</function> for a Qt application using
<classname>QMainWindow</classname>. 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 <classname>QMultiLineEdit</classname>
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
<guimenuitem>"cut"</guimenuitem> in the <guimenu>"Edit"</guimenu> menu:</para>

<informalexample>
<screen>  
pEditMenu-&gt;insertItem(BarIcon("editcut"), i18n("Cu&amp;t"),view, SLOT(cut()),KAccel::Cut, ID_EDIT_CUT);
</screen>
</informalexample>

<para>This creates a menu item in the <guimenu>"Edit"</guimenu> menu, that, when
activated, directly calls the slot <function>cut()</function> of the instance
<literal remap="tt">view</literal>.  We are assuming here, that you have created
this instance as a <classname>QMultiLineEdit</classname> and set it as the view
area. The Multiline-edit's slot is called, and cuts out the selected text as a
result.  The functionality is already provided by the class itself, and so there
is no need to inherit from <classname>QMultiLineEdit</classname> 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!</para>
      
<para>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 of your own, to read and write
the actual files you want to edit with the <classname>QMultiLineEdit</classname>
view-area <emphasis>within</emphasis> the main window's interface.
Yes, this is obviously the easiest thing to do in this case, and the most
logical.</para>

<para>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>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 this: what if I want to give the user the possibility to work
with a file in <emphasis>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>one</emphasis> file. 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 had cut out at the beginning
of the file <emphasis>in</emphasis> the other view, the file will be saved later
still containing the cut 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 in the 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 irrelevant. So 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 by 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>one</emphasis>
document or file by <emphasis>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 <abbrev>MDI</abbrev> means. Users that come from other Operating
Systems than Unix/Linux are used to it, as are programmers developing for those
platforms. X-Window applications traditionally are more targeted 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 <abbrev>MS</abbrev> Windows(tm) as well as for
Unix systems. While on Windows the difficult 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 the meaning of <abbrev>MDI</abbrev>? An <abbrev>MDI</abbrev>
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>
      
<informalexample>
<para>application instance -&gt; main window -&gt; view</para>
</informalexample>
<para>to</para>
<informalexample>
<para>application instance -&gt; main window -&gt; view -&gt; active child window</para>
</informalexample>

<para>The view now is capable of several actions:</para>
<itemizedlist>
<listitem>
<para>creating as many 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 behaviour for maximize, minimize, as a window manager does for top-level windows.</para>
</listitem>
</itemizedlist>

<para>Now, you can use "complete" widgets like the
<classname>QMultiLineEdit</classname> as child windows, for example 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.</para> 

<para>Extending this concept with the document-view model enhances the
possibilities by far: imagine that you can open as many 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 corresponding application frameworks for
both Qt-only programs, as well as KDE 2 applications, both with the same
interface methods.  So &kdevelop; is appropriate wether you want to develop for
either one of these. Using KDE 2 interfaces will offer you still more
possibilites through library functions as well as inter-process communication,
but those are special aspects that we 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 <application>KScribble</application> and describe the
practical aspects of programming <abbrev>MDI</abbrev> 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 <abbrev>IDE</abbrev> 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 <abbrev>SDI</abbrev>
(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 now 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.</para>

<para>One major extension are <abbrev>MDI</abbrev> 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
<application>KScribble</application>, 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 <abbrev>GUI</abbrev> components).</para>

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

<para>Now we will start jumping into development by creating the frame
 application for <application>KScribble</application>.</para>

<para> Start &kdevelop; and choose
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"New"</guimenuitem></menuchoice>
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 <guilabel>"KDE 2 MDI"</guilabel> from the KDE section. If you only have
Qt 2.1 installed, choose <guilabel>"Qt 2.1 MDI"</guilabel> from the Qt
section. Then select <guibutton>"Next"</guibutton>, 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 <guibutton>"Create"</guibutton>
button and &kdevelop; will generate our first version of
<application>KScribble</application>, while you can watch the output in the last
page of the wizard. Finally, press <guibutton>"Exit"</guibutton> 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 <application>KScribble</application> into other languages.</para>

</sect1>

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

<para>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
<application>KScribble</application> a unique KDE application.</para>

<para>First of all, in the Classviewer you will see three already created
classes, <classname>KScribbleApp</classname>,
<classname>KScribbleDoc</classname> and <classname>KScribbleView</classname>. 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, and 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
<classname>QWidget</classname> as a minimum requirement, but you are not limited
to that at all. Inheriting from another class is not too much work and there is
also the possibility to use a <classname>QMainWindow</classname>, or
<classname>KTMainWindow</classname> 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
<application>KScribble</application> 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, and especially how the
document clas will read and write data to and from files, and offer methods to
manipulate the data. As <application>KScribble</application> will be a simple
drawing application that operates on graphical data, we will use the Qt class
<classname>QPixmap</classname> for storing our paintings while it is
edited.</para>

<para><classname>QPixmap</classname> 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 <classname>QPixmap</classname>, one of <classname>QPen</classname>
and edit the methods <function>newDocument()</function>,
<function>openDocument()</function> and
<function>saveDocument()</function>.</para>

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

<para>Open the file <filename>kscribbledoc.h</filename>, by selecting it in one
of the fileviewers, or by a click on the classviewer over the class 
<classname>KScribbleDoc</classname>.</para>

<para>Then add the lines marked with -&gt; from the following code snippet:</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>As you see, we added pen and buffer, as well as
<function>currentPen()</function> and <function>penWidth()</function>. 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 <function>newDocument()</function> in the
<classname>KScribbleDoc</classname> class to jump to the method declaration. Here,
we're only adding one line, marked with the arrow:</para>

<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>This initializes the pen with the color black and width of 3 pixels; the
<classname>QPen</classname> class has some more constructors, but this one suits
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>

<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>Add the lines marked with the arrow again to your code. What we did here
is to comment out the passages where the file <filename>filename</filename> is
opened, because that is done automatically by the load and save methods of
<classname>QPixmap</classname>, which we add instead. Other documents may open a
file and read in its contents such as text lines or whatever, so the
<classname>QFile</classname> methods are already present in the codeframe. As
<function>save()</function> and <function>load()</function> return a boolean
value if the operation was successful, we're returning <varname>false</varname>
if not, so the rest of the framework gets a return value and can give out
warnings if the operation was not successful.</para>

<para>The <function>load()</function> and <function>save()</function> methods
now are already provided in <classname>QPixmap</classname>. They require the
filename and the format as argument. The source framework on the other hand
<emphasis>does not</emphasis> call the document methods with the format yet. If
only one format is used, it sets the format here. Other methods could detect the
format, but we will turn to this later. For now, we're using "PNG" as
format. See <classname>QImageIO</classname> for more details about the image
formats that can be opened.</para>

<para>Now we're already finished defining our document structure. The
<classname>QPixmap</classname> 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 <function>newDocument()</function>. 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
<application>KScribble</application> to define how the child windows shall
work. First of all, we notice that <classname>KScribbleView</classname> is
derived from <classname>QWidget</classname> by default. That is the minimum
requirement for a child window, but it already fulfils our needs.</para>

<para>When it comes to defining a new
widget's behaviour, 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 <classname>QWidget</classname> 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
<classname>QPointArray</classname> polyline and a boolean value
<varname>mousePressed</varname>. Add the code with the 
arrow to your include file for the class <classname>KScribbleView</classname>:</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>
</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. <classname>QWidget</classname> 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 <function>closeEvent()</function>
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 happening, 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>

<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>We're setting the background to <constant>NoBackground</constant>, a cursor
<constant>(crossCursor)</constant>, and initialize <varname>mousePressed</varname>
and <varname>polyline</varname>. Then we'll start implementing our first event
handler, <function>mousePressEvent()</function>, to recognize when the user
presses the mouse and where:</para>
      
<note><para>Note: the following implementations have to be inserted completely, so the
lines to add are not marked with an arrow!</para></note>

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

<para>Here, we're setting <varname>mousePressed</varname> 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 <classname>QPointArray</classname>, it can store values of the
type <classname>QPoint</classname> (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>

<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>This event handler is probably the most difficult, so we will do a
step-by-step walkthrough to understand what's been done.</para>

<para>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 <function>mousePressEvent()</function> event handler before,
so we don't have to take care of it anymore.</para>

<para> Now we're starting the painting action. First we create a
<classname>QPainter</classname> 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
<function>currentPen()</function>.</para>

<para> The next three lines assign the values inside the polyline
<classname>QPoint</classname> 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.</para>

<para> The following lines explain that: after drawing into our buffer is
finished (with <function>drawPolyline()</function> and
<function>painter.end()</function>), we create a rectangle <literal>r</literal>
and use <function>boundingRect()</function> from
<classname>QPointArray</classname> to get a <classname>QRect</classname> that
contains all three points. Therefore we need three values to have a
most-complete rectangle.</para>
      
<para> Then we use <function>normalize()</function> 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
<function>penWidth()</function> 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
<function>bitBlt()</function> function to copy the rectangle out of the buffer
into the widget. <function>bitBlt</function> 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 <symbol>this</symbol>.</para>

<para>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>

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

<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>

<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>This method also uses <function>bitBlt()</function> 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 (
<function>e-&gt;rect()</function> ) and use the coordinates for
<function>bitBlt()</function>, just as we did in the
<function>mouseMoveEvent()</function>.</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 <classname>KScribbleView</classname> 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 <function>resizeEvent()</function>:</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>

<para>Here, we first call the <function>resizeEvent</function> handler of
<classname>QWidget</classname>. 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 lose contents which are 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 <guimenuitem>"Run"</guimenuitem> in &kdevelop; and after
<application>KScribble</application> 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
<application>KScribble</application> the ability to open and save pictures with
the document class, and enabled user interaction by overwriting virtual methods
in the view class.  Next we gained the first functionaliy - we can draw
pictures. But when we created the <classname>QPen</classname> 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 <abbrev>GUI</abbrev> 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>

<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>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 <classname>QPopupMenu</classname>, and you can have
a look at how the current menubar is created when you switch to the 
<classname>KScribbleApp</classname> class, method
<function>initMenubar()</function>.</para>

<para> 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 to how
the menubar will look like:</para>

<itemizedlist>
<listitem>
<para>in the menus the order of the menu-items</para>
</listitem>
<listitem>
<para>in the menubar the order of inserting the popup menus.</para>
</listitem>
</itemizedlist>

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

<programlisting>kscribbleapp.h

class KScribbleApp
{
.
.
  private:
    QPopupMenu* pPenMenu;

}</programlisting>

<para>Now we are going to create the menu itself. Change to the implementation
of the method <literal>KScribbleApp::initMenuBar()</literal> and add the 
lines marked with an arrow:</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>You see that we first create the menu with <literal>new
QPopupMenu()</literal>. Then we use the <function>insertItem</function> methods
to add two menu entries, <guimenuitem>Color</guimenuitem> and
<guimenuitem>Brush</guimenuitem>. The visible commands are inserted with the
method <function>i18n()</function>, 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 <function>i18n()</function>. Qt-only programs using Qt &gt; 2.0 would use the corresponding method <function>tr()</function> instead of <function>i18n()</function>, as Qt has it's own ways of
internationalizing applications.</para>

<para>The second argument is a macro, the <abbrev>ID</abbrev> of the
menubar item. This <abbrev>ID</abbrev> is a number that we have to set 
using <literal>#define</literal> in the file <filename>resource.h</filename>, 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 <abbrev>ID</abbrev>'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 #define for the <abbrev>ID</abbrev> is a 
nice solution. The popup menu is now inserted into the menubar with
<function>insertItem()</function> as well, and with the pointer to the menu as
second argument.</para>

<para>Note that we inserted the popup menu after the <guimenu>"Edit"</guimenu>
menu and before the <guimenu>"View"</guimenu> 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
<keycap>Alt</keycap>and the appropriate underlined letter of the menuitem. As a
programmer, you have to set this character by a leading ampersand, so the
<guimenu>"Pen"</guimenu> menu will later be accessible via the keyboard by
pressing <keycombo><keycap>Alt</keycap><keycap>P</keycap></keycombo>. 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.</para>

<note><para>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></note>

<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
<abbrev>ID</abbrev> is compared and the following action called.</para>
      
<para>What is left to do is to add the #define statements to the file
<filename>resource.h</filename>:</para>

<programlisting>resource.h

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

<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.  If it happens by
accident, there's still the compiler to inform 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 <function>initToolBar()</function> method of the
<classname>App</classname> class. Here, we decide to add a button for both menu
entries in the <guimenu>Pen</guimenu> menu, but those need icons - which you can
either find in the KDE directory <filename>/toolbar</filename> or, when you
don't find an icon that matches your action, have to create yourself.</para>

<para><application>KIconEdit</application> is very suitable to paint icons, so
we will first create them. Choose <guimenuitem>"New"</guimenuitem> from the
<application>KDevelop</application><guimenu>"File"</guimenu> menu and select
<guilabel>"Icon"</guilabel> as the filetype.</para> <para>The first icon will be
named "<filename>pencolor.xpm</filename>". 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
<application>KScribble</application> sources. Then create a new directory
"<filename>toolbar</filename>". Change to that directory and press
<guilabel>"OK"</guilabel>. The new icon will then be created in the new
directory "<filename>toolbar</filename>" and will be opened by
<application>KIconEdit</application> 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 <abbrev>RFV</abbrev> / <abbrev>LFV</abbrev> in
&kdevelop;. Select the icon by a <mousebutton>right</mousebutton> mouse button
press and select <guimenuitem>"Properties"</guimenuitem> 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 <guilabel>"install"</guilabel> option and enter
into the line now active below:</para>
      
<para><userinput>$(kde_datadir)/kscribble/toolbar/pencolor.xpm</userinput></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 "<filename>toolbar</filename>", 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
"<filename>penwidth.xpm</filename>".</para>

<para>Now we only have to insert the buttons into the toolbar; add the lines
marked with the arrow into your code:</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>

<para>Here, we use the methods of <classname>KToolBar</classname> to insert
buttons. The first argument, <function>BarIcon()</function>, 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
<filename>*.PNG</filename>, but it works with xpm's as well. (You could use
<application>ImageMagick</application> for that as well which can do that- or
use <application>KScribble</application> in a later step to convert your icons
to PNG!)</para>

<para>The second argument is again the <abbrev>ID</abbrev>. The commands are
then automatically activated, as the <function>toolBar()</function> is already
connected to the same methods as the menubar is for signal
<function>activated()</function>. 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 <function>i18n()</function> to allow internationalization.</para>

<para>You're done for now, the <abbrev>GUI</abbrev> is extended at least
visually. You can compile and run <application>KScribble</application> again and
see how it looks.  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 <application>KScribble</application> 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
<guimenuitem>"New"</guimenuitem> from the KDevelop <guimenu>"File"</guimenu>
menu and select <guilabel>"Qt/KDE Dialog"</guilabel>. Then enter the dialog file
name as <filename>kpenbrushdlg</filename>.  The extension will be automatically
added. Enter <guilabel>"OK"</guilabel> and the dialogeditor opens an empty
widget that will be our dialog background.</para>

<para>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 <guibutton>OK</guibutton> 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 <guibutton>OK</guibutton> button, then the spinbox and then
the <guibutton>cancel</guibutton> button, the input focus will change from the
<guibutton>ok</guibutton> button to the spinbox, and then to the
<guibutton>cancel</guibutton> button - which is not what the user
expects.</para>

<para>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 <guilabel>"Widgets"</guilabel> tab on the left
pane. There you have all available widgets present as 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 <guilabel>"Widget
Properties"</guilabel> pane on the right.</para>

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

<varlistentry>
<term>The Spinbox</term> 
<listitem><para>Press the
<guilabel>"QSpinBox"</guilabel> button on the <guilabel>"Widgets"</guilabel> tab
and place it at the right of the label we created in the last step. Now set the
variable name in section <guilabel>"C++Code"</guilabel> to
<guilabel>"width_spbox"</guilabel>. The minimum and maximum values are 1 and
100, which should be fine for setting the brush width.</para>
</listitem>
</varlistentry>

<varlistentry>
<term>The Buttons</term> 
<listitem><para>Finally, we need the mentioned three
buttons. The leftmost button will be the default button. Create a
<classname>QPushbutton</classname> and place it somewhere nicely on the bottom of
the dialog, set the variable name to <varname>"default_btn"</varname> and the
button text to <guilabel>"Default"</guilabel>. Proceed with the
<guibutton>OK</guibutton> button with variable name
<guibutton>"ok_btn"</guibutton> and the cancel button with variable name
<guilabel>"cancel_btn"</guilabel> and set the button text to
<guilabel>"&amp;OK"</guilabel> and <guilabel>"&amp;Cancel"</guilabel>.</para>
</listitem>
</varlistentry>
</variablelist>

<para>If you´re fine with the layout of the dialog, choose <guilabel>"Generate
complete sources"</guilabel> from the Build menu and set the classname to 
"<classname>KPenBrushDlg</classname>", the inheritance to
<classname>QDialog</classname>. After pressing <guibutton>"OK"</guibutton>, 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 <abbrev>GUI</abbrev> 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 <guibutton>OK</guibutton> button to quit the dialog.</para>

<para>In the generated class for the dialog,
<classname>KPenBrushDlg</classname>, you can see one method besides the
constructor and the destructor, <function>initDialog()</function>. This method implements the whole
<abbrev>GUI</abbrev> 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>

<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>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 <function>slotDefault()</function>. This slot is still to be
implemented below, where we will set the default value of the spinbox
directly.</para>

<para>The second <function>connect()</function> call connects the ok button to
call the slot <function>accept()</function> provided by
<classname>QDialog</classname>, as well as the <guibutton>cancel</guibutton> 
button gets connected to <classname>QDialog</classname>´s slot
<function>reject()</function>. 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>

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

<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
<function>setCurrent()</function> method will be used to set the current value the
pen has, the <function>width()</function> 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 <function>slotDefault()</function> method:</para>

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

public slots:
  void slotDefault();

//kpenbrushdlg.cpp:
//method implementation:

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

<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
<function>slotPenBrush()</function> in the class
<classname>KScribbleApp</classname>:</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>

<para>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
<varname>curr_width</varname> that stores the current pen width.</para>

<para>Now we can call the dialog by creating the dlg instance of
<classname>KPenBrushDlg</classname>. Then we set the current pen width by
calling <function>dlg-&gt;setCurrent()</function>, which method we added to the
dialog.  By calling <function>dlg-&gt;exec()</function> we invoke the dialog. The
<literal>if(<varname>m</varname>)</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 <guibutton>OK</guibutton>
button on the dialog.</para>

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

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

<programlisting>
kscribbledoc.h:

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


<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 <abbrev>ID</abbrev>´s to the slot
<function>commandCallback()</function>, which selects and executes the according
methods we want to call if a menu or toolbar item was chosen:</para>

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

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

<para>This addition also adds the <literal>slotPenColor()</literal> method to
the execution list to set the pen color, which we will implement now:</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>

<para>When looking at the code, we see that we use another new method of
<classname>KScribbleDoc</classname> to set the pen color. This one has to be
implemented as well:</para>


<programlisting>kscribbledoc.h:

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

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

<para>Now you´re ready! Let´s summarize what we´ve done in this chapter:</para>

<procedure>
<step><para> we first added menubar and toolbar commands/items for two dialogs
that we want to call;</para></step>
<step><para> then we created the first dialog to set the pen width with the
KDevelop dialogeditor</para></step>
<step><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></step>
<step><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></step>
</procedure>

<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 then how we´re going to
do it. While playing with <application>KScribble</application>, you may have
noticed, that if you open another view of a document by calling
<menuchoice><guimenu>"Window"</guimenu><guimenuitem>"New
Window"</guimenuitem></menuchoice>, 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>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 <function>updateAllViews()</function>, which
calls the <function>update()</function> 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 <function>bitBlt()</function>), we just have to
call updateAllViews(this). The this pointer is needed, because the calling view
doesn´t need a repaint and the <function>update()</function> 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 <function>mousePressEvent()</function>,
<function>mouseMoveEvent()</function> and
<function>mouseReleaseEvent()</function> - 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 <function>updateAllViews()</function>. How the update
has to be executed has to be implemented in the widget´s
<function>update()</function> method; one may be content by setting e.g. the
changed text in an editor, in our application we just call
<function>repaint()</function>, 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
<classname>QScrollView</classname> that itself inherits from
<classname>QWidget</classname> 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
<classname>QScrollView</classname>, create the child widgets to manage with the
scrollview as parent and add them to the scrollview with
<function>addChild()</function> or create a view by inheriting
<classname>QScrollView</classname> and draw into the viewport, which is a
defined area inside the scrollview, instead of directly to the widget. The
difference here is that <classname>QScrollView</classname> provides a set of
event handlers similar to the <classname>QWidget</classname> event handlers
especially for the viewport. So what was formerly a
<function>mousePressEvent()</function> in our view will become a
viewportMousePressEvent, a <function>paintEvent()</function> will become a
viewportPaintEvent etc. The second possibility will suite our needs to make
<application>KScribbleView</application> a scrollable widget and so we will have
to make the following modifications:</para>
      
<procedure>
<step><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, <abbrev>e.g.</abbrev> 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></step>
<step><para>changing the inheritance of <classname>KScribbleView</classname>
from <classname>QWidget</classname> to
<classname>QScrollView</classname></para></step>
<step><para>renaming the virtual methods to the according viewport methods of
<classname>QScrollView</classname></para></step>
<step><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 <classname>QWidget</classname> 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 <classname>QWidget</classname>
coordinates are translated to viewport coordinates</para></step>
</procedure>

<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 <varname>QSize
size</varname> to <classname>KScribbleDoc</classname> as well as the method 
<function>docSize()</function>:</para>

<programlisting>kscribbledoc.h:

#include &lt;qsize.h&gt;

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

private:
  QSize size;
</programlisting>

<para>Now we have to modify all methods that deal with initializing and opening
the document contents - <function>newDocument()</function> and
<function>openDocument()</function>:</para>

<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>In <function>newDocument()</function>, 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>QPixmap::size()</literal>, which we used in <function>openDocument()</function>. Then we´re done with setting the sizes and we can move on to reimplementing <classname>KScribbleView</classname> 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
<classname>KScribbleView</classname>. The following code shows these
changings:</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>Here, we changed the inheritance from <classname>QWidget</classname> to
<classname>QScrollView</classname> 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
<classname>QScrollView</classname> 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):</para>

<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>Note that formerly, the <function>resizeEvent()</function> 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 <function>newDocument()</function> and <function>openDocument()</function>), we just resize the contents by calling
<function>resizeContents()</function> provided by
<classname>QScrollView</classname> 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 <function>viewport()</function>. 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>

<important><para>Attention: take care to comment out the <function>resizeEvent()</function> implementation!</para></important>

<para>Now, the paint event will have to copy the pixmap in the buffer to the
corresponding position in the view. For this, we have to change the 
destination of <function>bitBlt()</function> from this to
<function>viewport()</function>, 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:</para>

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

<para>The <function>contentsX()</function> is the position in the
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 "trial and error" when
implementing your own scrollviews. The other possible 
call of <function>bitBlt()</function> 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 <function>mouseMoveEvent()</function>, which
changes to <function>viewportMouseMoveEvent()</function>, has a
<function>bitBlt()</function> call as well. Here, we have to apply the same chages
as in the paint event.</para>

<para>Further, in the <function>mousePressEvent()</function> and the
<function>mouseMoveEvent()</function>, we have retrieved the position of the mouse
events with <literal>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
<function>viewportToContents()</function>:</para>

<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>In the <function>viewportMouseMoveEvent()</function>, we had to change the
destination again from <literal>this</literal> to <function>viewport()</function>, and
with that translate the positions. This time, we used the second version of the
call we used in <function>viewportPaintEvent()</function>, 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 <function>update()</function> method: why should we repaint the whole widget every time?
This will reduce performance and lead to a so-called "flicker"
effect. This effect sometimes occurs with widgets, but there are some ways to reduce this behavior. Instead of calling <function>repaint()</function>, we could call <function>repaint(<constant>false</constant>)</function> 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
<classname>QScrollView</classname>, we will reduce the painting  
even more: we limit the update method to call <function>repaint()</function> on
the <function>viewport()</function> widget, because that will call
<function>viewportPaintEvent()</function>. On the other hand, the painting area we
use is the rectangle containing the document contents, 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 <literal>false</literal>, 
so the document area does not get erased:</para>

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

<para>Now you´re ready!  This chapter has been one of the hardest to implement,
and to understand, especially when it comes to the geometries that change. On
the other hand, we gave our application a whole new functionality through 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, making use of some new methods of the KDE2
libraries, but as usual, this will bring us some interesting functionality.
<application>KScribble</application> will soon be able to open and save a whole range
of picture formats, as 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 <classname>QImageIO</classname>,
which has a support library by KDE: <classname>KImageIO</classname>. As a
preparation before we can make use of these nice methods, we have to add the
according library to <application>KScribble</application>:
<literal>libkimgio</literal>.</para>

<para> Open
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"Options"</guimenuitem></menuchoice>
in &kdevelop; and add the line <userinput>-lkimgio</userinput> to the additional
libraries to link <application>KScribble</application> 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 <function>openDocument()</function> and <function>saveDocument()</function> 
methods:</para>

<para>In method <literal>KScribbleDoc::openDocument()</literal>:</para>

<para><literal>	if(!buffer.load( filename, format ))</literal></para>

<para>In method <literal>KScribbleDoc:: saveDocument()</literal>:</para>

<para><literal>	if(!buffer.save( filename, format ))</literal></para>

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

</sect1>

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

<para>To use <classname>KImageIO</classname>, we have to initialize the library
first. For this, we add a call for <function>registerFormats()</function> in our 
<function>main()</function> function:</para>

<programlisting> 	
main.cpp:

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

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

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

<programlisting>
kscribble.h:

#include &lt;kimgio.h&gt;</programlisting>
</sect1>

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

<para>Now that we can make use of <classname>KImageIO</classname>, we have to
apply the first change to the most important method of <classname>KScribbleApp</classname>: 
<function>openDocumentFile()</function>. This method opens us any
document, but until now only with a 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>KScribbleDoc::openDocument()</literal> in the method <function>openDocumentFile()</function>:</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>Of course, this works the same as using <literal>QString
format=QImageIO::imageFormat(file);</literal>. Here, <classname>KImageIO</classname>
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 are finishing our tutorial with the last section - we will adapt
the file dialogs of <application>KScribble</application> to make use of file filters. 
For these, <classname>KImageIO</classname> 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 <function>pattern()</function> method
of <classname>KImageIO</classname>:</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>Here, the mode in <function>pattern()</function> 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 <function>slotFileSaveAs()</function> is called,
which itself invokes the file dialog that retrieves a file name. There, we will
set the pattern mode to Writing:</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>

</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 <application>KScribble</application> or chooses <guimenuitem>"Print"</guimenuitem> from the <guimenu>"File"</guimenu> menu, the <function>slotFilePrint()</function> method is called in <classname>KScribbleApp</classname>.</para>

<para>
This method detects which child window is currently active, and creates a printer
instance of the class <classname>QPrinter</classname>. Then it calls the widget´s
printing method, <literal>KScribbleView::print()</literal>. Here, the framework
already contains the base implementation - which already shows you that for
printing you just have to use <classname>QPainter</classname> which then
draws on the printer. This method also calls the printing dialog.</para>

<para>What we have to do here is to use <classname>QPainter</classname> methods
to draw the pixmap of the document connected to the view. As 
<classname>QPainter</classname> already offers a whole set of methods
<function>drawPixmap()</function>, we will of course use one of them:</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>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 file that
<application>KScribble</application> is able to open.</para>

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

<para><filename>$(KDEDIR)/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</filename></para>

<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 <command>tar <option>zxvf</option> kscribble-1.0.tar.gz</command>, load the project and call <guimenuitem>"Automake and autoconf"</guimenuitem> from the
<guimenu>"Build"</guimenu> menu in &kdevelop;, then call
<guimenuitem>"./configure"</guimenuitem> 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.

<!-- note to self: insert link to GPL -->

</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 <command>tar <option>zxvf</option>
kscribble-1.0.tar.gz</command>, load the project and call <guimenuitem>"Automake and
autoconf"</guimenuitem> from the 
<guimenu>"Build"</guimenu> menu in &kdevelop;, then call
<guimenuitem>"./configure"</guimenuitem> 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><filename>$(KDEDIR)/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</filename></para>
</sect1>

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

<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>
</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
time-stamp-format:"%02d/%02m/%04y"
time-stamp-line-limit:100
time-stamp-start:"<date>"
time-stamp-end:"</date>"
sgml-namecase-general: t
sgml-namecase-entity: nil
sgml-minimize-attributes: nil
sgml-omittag: nil
sgml-shorttag: t
sgml-general-insert-case: lower
End:
-->
