<!DOCTYPE book PUBLIC  "-//KDE//DTD DocBook V3.1-Based Variant V1.0//EN" [
  <!ENTITY kappname "&kdevelop;" -- replace kapp here -->
  <!ENTITY kdevelop "<application>KDevelop</application>">
  <!ENTITY IDE "<acronym>IDE</acronym>">
  <!ENTITY gui "<acronym>GUI</acronym>">
  <!ENTITY kiconedit "<application>kiconedit</application>">
  <!ENTITY % addindex "IGNORE">
  <!ENTITY % English "INCLUDE" -- change language only here -->
  <!ENTITY % ents PUBLIC "-//KDE//ENTITIES Documentation V2.0//EN">
  %ents;
]>

<book lang="&language;">

<bookinfo>
<title>The &kdevelop; &kde; application Tutorial</title>

<authorgroup>
<author>
<firstname>Ralf</firstname>
<surname>Nolden</surname>
<affiliation>
<address><email>Ralf.Nolden@post.rwth-aachen.de</email></address>
</affiliation>
</author>

</authorgroup>

<copyright>
<year>2000</year><year>2001</year>
<holder>Ralf Nolden</holder>
</copyright>

<!-- Translators: put here the copyright notice of the translation -->

<legalnotice>&FDLNotice;</legalnotice>

<date>01/03/2001</date>
<releaseinfo>1.40.00</releaseinfo>

<abstract>
<para>
&kdevelop;is an integrated and easy to use environment for C/C++ programmers.
</para>
<para>
The &kde; Application Tutorials Handbook for the &kdevelop; Integrated
Development Environment, a tutorial collection on how to create &kde;
applications by examples.
</para> 
</abstract>

<keywordset>
<keyword>KDE</keyword>
<keyword>kdevelop</keyword>
<keyword>programming</keyword>
<keyword>IDE</keyword>
<keyword>development</keyword>
</keywordset>

</bookinfo>

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

<para>
As &kde; is becoming a standard for &UNIX; system desktops, more and more
developers want to take advantage of the mature and well-written
<acronym>API</acronym> that the &kde; project provides.  This
<acronym>API</acronym> allows developers to easily create &GUI; applications,
and make using the classes provided by &Qt; and &kde; easy.
</para>

<para>
The &kdevelop; project intends to provide an &IDE; that enables developers to
create and extend their applications rapidly, with the main focus on building
&kde; applications, although you are not tied to this.  
</para>

<para>To help beginners get off the ground, this handbook tries to explain
certain aspects of application creation for &kde; by describing how to create
running applications.  This is achieved by combining the sample code included
with the &Qt; library, and the functionality of the &kdevelop; &IDE;, so you are
creating &kde; applications in a minimum of time by following the examples.
</para>

<para>
The goal of this book is that you should by the time you have completed it have
a broad understanding of how the underlying &Qt; library functions, the classes
it provides, how &kde; works with this.
</para>

<para>
The &kdevelop; team wishes you success in learning about &kde; and &Qt;,
and we hope &kdevelop; and this handbook will enable you to join the ranks of
the programmers who contribute to the &kde; project.
</para>

</chapter>

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

<sect1 id="prior-knowledge">
<title>Things you should know before you begin</title>

<para>
This handbook requires a certain base knowledge about the C++ programming
language.  You will need this to understand the code examples and their
functionality.  It is further assumed you have read the <!-- link to User Manual -->
&kdevelop; user manual, as well as the <!-- link to programming handbook -->
&kdevelop; programming handbook.  If this is the case, you will already have an
understanding how the &kdevelop; &IDE; works in general, and what generated
projects are.  These things will not be covered again in this handbook.
</para>

<para>
If you've already gone through the User Manual <!-- link to user manual -->, you
should have &kdevelop; set up to your satisfaction.  To follow this tutorial,
you should check that you have access to the &Qt; online reference
documentation, through either the <guimenu>Help</guimenu> menu, or that you can
see this book in the documentation tree.
</para>

<para>
When looking a the first page of the &Qt; documentation, you will see that it
contains a link to a section <guilabel>Tutorials</guilabel> and
<guilabel>Examples</guilabel>. Under <guilabel>Tutorials</guilabel>, you will
find a 14-step introduction into using the &Qt; library to build
applications. <guilabel>Examples</guilabel> will lead you to a list of example
programs that &Qt; includes which show you example usage reference for certain
classes.
</para>

<para>
 If you are an absolute beginner, you should be at least comfortable with the
idea behind &Qt;.  That is, 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 that list is meaningless to you, you should start with the &Qt; library in
<!-- Link to programming handbook -->the Programming handbook, where the basics
of a &Qt; application are described in detail.  There you will also find an
introduction to the difference between a pure &Qt; application and a &kde;
application.
</para>

</sect1>

<sect1 id="the-first-steps">
<title>The first steps</title>

<para>
To learn about &Qt; will give you a good start in programming &kde; applications
as well, so we will be creating a project that will produce the tutorial
applications (after some editing, of course).
</para>

<para>
To do this, start by opening &kdevelop; and select
<menuchoice><guimenu>Project</guimenu> <guimenuitem>New</guimenuitem>
</menuchoice> from the menu.  The application wizard will open and you can
select the type of project you wish to build.  For &Qt; applications you would
choose the <guilabel>Qt application</guilabel> option, but we will be using the
<guilabel>Mini-KDE</guilabel> project to build our examples.  The reason is
simple, the &Qt; examples don't contain much code, and by choosing the &Qt;
application in the application wizard, we would have to heavily edit the
provided code to create true examples.  As the <guilabel>Mini-KDE</guilabel>
consists of only an empty main window and some checks for the &Qt; library and
header files, it will fit our purposes with the least amount of changes.
</para>

<para>
When entering the next wizard page, you should enter the project specific
information.  You 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.
</para>

<para>As the structure of the tutorial steps builds each stage on the previously
created code, we can start with <userinput>MyWidget</userinput> as the project
name.  This will then build us the base class that is needed in <!-- link to
whatever chapter 4 is now -->.  As we want to start with the first tutorial
step, use <userinput>1</userinput> as the project <!-- version? --> number.  We
can increase this later, when moving to the next step.
</para>

<para>
Next fill in any other required information, such as your name, email address,
and project directory.  These are all that are required for our purposes, so we
can flip to the next page, where you can insert any header information you want
to include in the project header files (or you can use the example provided).
The same is done for the next page, which sets header information to be included
in the source files.  Finally the last page, where you press
<guibutton>Create</guibutton> to build the project.  If the
<guibutton>Create</guibutton> is not available, start at the first page again,
and make sure you didn't miss any required information.
</para>

<para>
If everything went <guibutton>Ok</guibutton>, the project has been built, and
the message window in the application wizard will have
<computeroutput>READY</computeroutput> as it's final line.  Select
<guibutton>Close</guibutton> to exit the wizard.
</para>

<para>
When looking at the tree view on the left, you will see that the project already
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 class viewer <guilabel>Globals</guilabel> folder, you will see the
application already contains a <function>main()</function> function where the
execution will start.
</para>

<para>
Now the application is set up.  You could do an
<guimenuitem>Execute</guimenuitem> or <guimenuitem>Make</guimenuitem> already,
but as we want to create the tutorial applications, we will instead go ahead and
change the code to create the tutorial examples.
</para>

</sect1>
</chapter>

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

<sect1 id="hello-world">
<title>Hello World!</title>

<para>
As the previous chapter explains, we have created a project called MyWidget.
When looking at the first tutorial page of the &Qt; documentation, you will see
that the first application uses only the <function>main()</function>.
Therefore, we have only to change the already created
<function>main()</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 with the editing cursor already set in
front of the first line of <function>main()</function>.
</para>

</sect1>

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

<para>
We could start by just changing the already given code to the desired code as
listed in the tutorial.  There is however an even easier way.  Select the
contents of the <function>main()</function> with the mouse, or by holding the
<keycap>Shift</keycap> key while selecting the lines with the <keycap>Down
Arrow</keycap>. Then select <guimenuitem>Cut</guimenuitem> from the
<guimenu>Edit</guimenu> menu, or press the <keycap>Delete</keycap> key.  This
removes the selected code, clearing the <function>main()</function> and leaving
the way clear to start from scratch.
</para>

<para>
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
example by yourself.
</para>

<para>
Or, you could do it the easy way.  Select the contents of the listed
<function>main()</function> in the documentation browser, and press the
<guiicon>Copy</guiicon> from the toolbar (or use the context menu, or the
appropriate entry in the <guimenu>Edit</guimenu> menu).  This will copy the
selection to the clipboard, and, after switching back to the sourcecode window,
can be inserted with <guimenuitem>Paste</guimenuitem>, either from the context
menu, the toolbar, or the <guimenuitem>Paste</guimenuitem> entry in the
<guimenu>Edit</guimenu> menu.
</para>

<para>
Whichever method you choose, you need to add the appropriate header files,
outside the <function>main()</function>:
</para>

<example>
<title>Headers for the first Tutorial example</title>

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

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

</sect1>

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

<para>
To build the first tutorial, you can simply choose
<guimenuitem>Make</guimenuitem> from the <guimenu>Build</guimenu> menu, or press
the appropriate toolbar button.  You could choose instead
<guimenuitem>Execute</guimenuitem>, which results in running the application
after the build was successful.
</para>

<para>
If the build stops due to an error, you can rest assured the fault was on the
programmer's side, not the tutorial.  It would be sad to be stopped in your
tracks just when you're taking your first steps towards programming for &kde;
(and/or &Qt;), but you can use it as an opportunity to learn more about
&kdevelop;!
</para>

<para>
To find an error, you have two options.  Either select the appropriate error
message in the output window, which will take you to the line of source code
where the error occurred, or press the <keycap>F4</keycap> key.  If you are
unfortunate enough to have more than one error, you can use the
<guimenuitem>Next Error</guimenuitem> in the <guimenu>View</guimenu> menu, to
work your way through the list.
</para>

</sect1>

<sect1 id="exercises-1">
<title>Excercises</title>

<para>
You may be asking <quote>Excercises?  This was so simple, I don't need to do any
exercises!</quote>, but be assured, things will be getting more complicated.
This was the easiest example, and the more complex they become, the more
thinking is required on your side to understand and follow the tutorial steps.
The exercises will additionally show you how to actually use &kdevelop; and it's
features.
</para>

<para>
To teach yourself more with the first tutorial, you should try the following
changes to the source code, and see how this influences 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 <function>QPushButton::resize ( const QSize & s )</function> 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 the width, and which the height.
</para>
</listitem>
</orderedlist>

</sect1>

<sect1 id="extensions">
<title>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 the parent-child
relationships between widgets.
</para>

<para>
Why are these things important?  Well, signals and slots are the building blocks
of the technology which differentiates &Qt; from other widget libraries.
Whenever you hit an implementation problem where you want to communicate between
objects, this mechanism will make your work incredibly short, and nothing
serious can happen even if something goes wrong - misconnections don't lead to a
segmentation fault.  This is one of the major strengths of the &Qt; library, and
a lot of &kde;/&Qt; developers wouldn't want to do without it's flexibility.
Understanding the signal/slot mechanism is therefor essential to creating a
&kde;/&Qt; application.
</para>

<tip>
<para>
Watch out for the fact that the button terminates the
<classname>QApplication</classname> instance by calling
<function>quit()</function>. You will be meeting <function>quit()</function>
again when working with <classname>QApplication</classname>'s KDE equivalent,
<classname>KApplication</classname>
</para>
</tip>

</sect1>

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

<para>
After having successfully followed the tutorial steps, modifying and extending
the <function>main()</function>, you will hit the point where you want to create
a new widget.  A unique widget, that can only be constructed with the class
you're about to write in step 4<!-- Qt tutorial step 4? -->.  Actually, the
class has been created by the application wizard for you, so there is not much
to add.  Just insert the code that lesson 4 places in the
<filename>main.cpp</filename> into the <classname>MyWidget</classname> header
and implementation file.  Fortunately, you have only to add the implementation
to the constructor code.
</para>

<para>
To access the constructor, select it from the class viewer.  This will
automatically take you to the implementation where you can add the code.
</para>

<para>
Here you have only 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 your new application, just hit the <guiicon>Execute</guiicon> icon.
&kdevelop; will first save and then compile your changes, and finally your
applicaiton will be run.
</para>

</sect1>

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

<para>
In tutorial lesson 5, the target is to extend the widget class.  You'll also be
learning here about virtual functions.  Here the
<function>resizeEvent()</function> function is reimplemented.  What is important
to learn here, besides extending a class, is that &Qt; works with user events
through virtual functions that take an event as a parameter.  
</para>

<para>
You should make yourself comfortable with the following classes of the &Qt;
library:
</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>
Whenever writing custom widgets, especially the view areas of applications, you
will may to overwrite the default implementation of the widget's event-methods
with your own.
</para>

<para>
An example of this could be <userinput>virtual void <function>mousePressEvent (
QMouseEvent * )</function></userinput>, 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 use either <parameter>event</parameter> or
<parameter>e</parameter> as the parameter name.
</para>

<para>
After this you can process the event's parameters.  For a
<classname>QMouseEvent</classname>, you have to ask if the &RMB;, &MMB; or &LMB;
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 might think <quote>I will just create a header file, and an
implementation file, and then I'm done!</quote>.  &kdevelop; once again makes it
easier for you.  When adding a class, you should always use the class generator.
This will do all the hard work for you, so you will just have to add the
specific code.
</para>

<para>
To add a class with the class generator, choose <menuchoice>
<guimenu>Project</guimenu> <guimenuitem>New
Class...</guimenuitem></menuchoice>. A dialog will open for you to enter the
values for the class you wish to add.  The first thing you need to add is the
classname, which is <classname>LCDRange</classname> in the tutorial.
</para>

<para>
Next add the baseclass.  The baseclass is the one that the new class inherits.
Looking at the tutorial, this is <classname>QWidget</classname>.  As most
<acronym>GUI</acronym> classes to add will be inheriting
<classname>QWidget</classname>, &kdevelop; has a shortcut for you.  Simply leave
the <guilabel>Baseclass</guilabel> field empty, and instead check
<guilabel>generate a QWidget-Childclass</guilabel> in the <guilabel>Additional
Options</guilabel> section.  This will automatically add the required
<function>Q_OBJECT</function> macro to the headerfile, to later add slots and
signals.  Slots and signals will be required in step 7.
</para>

<para>
As the filenames are automatically inserted, you don't have to worry about
that.  The only thing we would suggest (strongly!) adding here is
documentation. It is always good style to add descriptive documentation to the
class, especially as a classname such as <classname>LCDRange</classname> may not
inform you specifically about the purpose of the widget.
</para>

</sect1>

<sect1 id="steps-7-through-14">
<title>Steps 7 through 14</title>

<para>
For the rest of the tutorial steps, you are now prepared, and know everything
you need to know, so far as using the &kdevelop; <acronym>IDE</acronym> to aid
your way with such things as adding classes and editing sources.
</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.  You should additionally
play with &kdevelop;'s options for <guimenuitem>Build</guimenuitem>.  Execute
the application with a commandline argument such as <option>--geometry</option>,
for example, or run the debugger.
</para>

<para>
When you are comfortable, you should be able to proceed with the rest of the
provided &Qt; examples.
</para>

<para>
In the following chapter, you will be introduced to development for &kde; 2.x,
through an example application, <application>KScribble</application>.  This will
be a small drawing application, which will show you the concepts of application
design, and how to implement your own program.
</para>

</sect1>

</chapter>

<chapter id="creating-kde-applications">
<title>Creating &kde; applications</title>

<para>
This part of the tutorial will be teaching you about &kde;/&Qt; aplication
design by creating an example application step by step.  The major intention
here is to help you gain an understanding of how the two most important ideas of
a graphical user interface programming work: the Document View model and the
Multiple Document Interface (<acronym>MDI</acronym>) model.
</para>

<para>
You will also gain a good idea of concepts that are new in &kde; 2.x and the
&Qt; 2.x library.  These bring new enhancements and technology to the
programmers hands, improving application design and widening the possibilities
for your use.  Key concepts include widget themes, inter-process communication,
and embedded applications.
</para>

<para>
During this tutorial you will gain a clear insight into how the
<acronym>MDI</acronym> model works, and also into the Document View model, which
is difficult to understand for even seasoned programmers.  This will help you
create other types of application, which use the document view model even if
they only contain one window, and you can't see an obvious need for it.
</para>

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

<itemizedlist>
<listitem>
<para>
Understanding the basic concepts of the <acronym>MDI</acronym> and document view
models.
</para>
</listitem>
<listitem>
<para>
Creating the generic framework for <application>KScribble</application>, and
explaining the concepts of that 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 user interaction.
</para>
</listitem>
<listitem>
<para>
Adding dialogs to set such things as pen width, and the 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 itself is based on the code found in the
<application>Scribble</application> 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="application-concepts">
<title>Application Concepts</title>

<para>
This chapter now introduces you the the ideas of the application models
mentioned already: The <acronym>MDI</acronym> (Multiple Document Interface) and
the document view models.  A basic introduction into the document view model and
the generating of such a project with &kdevelop; is also covered in the <!--
link to programming book --> &kdevelop; Programming Manual, but based on a
Single Document Interface (<acronym>SDI</acronym>).  In any case, you should be
familiar already with the basics of the &kde; and &Qt; classes that are
explained in the <!-- link to libref? --> &kde; Library Reference Guide.  There
the base classes of the underlying libraries are explained in detail, and how to
use them, with additional description of the &Qt; signal/slot mechanism and
event handling.
</para>

<para>
As a general guideline you should already know:
</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>
That generally a main window is inherited from
<classname>KTMainWindow</classname> for &kde; and
<classname>QMainWindow</classname> for &Qt;.
</para>
</listitem>
<listitem>
<para>
That events (&eg; keyboard presses, or mouse movements) are sent to the
application by the underlying window system.
</para>
</listitem>
<listitem>
<para>
That <classname>QWidget</classname>, as the base class for all visible Graphical
User Interface (<acronym>GUI</acronym>) components, provices specialized event
handlers through virtual functions.  These filter out the appropriate events by
reimplementing the <function>QObject::event()</function>.
</para>
</listitem>
<listitem>
<para>
That the &Qt; signal/slot mechanism means basically that a component can send
out signals, and a reciever can connect to those signals to process some
actions.
</para>
</listitem>
<listitem>
<para>
That this mechanism avoids inheriting classes for <acronym>GUI</acronym>
components, because the signals inform you about things like mouse presses, as
appropriate for a widget.
</para>
</listitem>
</itemizedlist>

<sect1 id="document-view-model">
<title>The document view model</title>

<para>
The document view model is one of the most basic concept sin application design
for those applications that rely on graphical user interfaces.  Therefore, a
certain understanding of it's purpose is needed before the programmer can see
it's possibilities, and the many ways to make use of it.  
</para>

<para>
First, lets take a look at the <quote>usual</quote> design of a typical
&kde;/&Qt; application:
</para>

<informalexample>

<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
represents itself to the user through 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, a toolbar, and a
statusbar.  In the center, it contains a so called <quote>view area</quote>,
which means that this part is an instance of another class, usually called a
<quote>View</quote>.
</para>

<para>
The view instance is created when the main window is constructed at program
start, and then has to be set as the view area component with a method provide
by the main window: <function>setView(your_view)</function> for &kde;
applications using the <classname>KTMainWindow</classname> class,
<function>setCentralWidget(your_view)</function> for a &Qt; application using
the <classname>QMainWindow</classname> class.
</para>

<para>
Now the view is obviously 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 the menubar
or toolbar command, like this:
</para>

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

<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>view</literal>.  We suppose you have already created this instance as a
<classname>QMultiLineEdit</classname> and set this as the view area.
</para>

<para>
The multi line edit's slot gets called, and cuts out the selected text as a
reaction.  The functionality is already provided by the class itself, and so
there is no need to inherit from <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 mean a simple editor can be written by creating one single class that
defines the main window behaviour, and how to save and read files into the
editor, just a few basic slots that your main view has to implement itself.
</para>

</informalexample>

<para>
Here is the reason we're now introducing this mysterious document view model:
You have to provide the methods on your own to read and write the actual files
you want to edit, with this method of using
<classname>QMultiLineEdit</classname> view area <emphasis>within</emphasis> the
main window's interface.
</para>

<para>
Yes this is obviously the easiest thing to do in this case, and perhaps the most
logical.  Yet, take a look at the files and their contents as a so-called
<quote>Document</quote>, to which we can apply the attribute of an
<quote>Object</quote> in C++ terminology.  Then 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?
</para>

<para>
We can easily create a small class that is responsible for reading 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 could then provide a
saving method that retrieves the text from the view again, and saves it as a
file.
</para>

<para>
In the example, these two methods would be the only actions that are necessary
in the document class, because the edit view already provides all the methods
basically needed for an editor, through slots, and you can manipulate the
content of the view through these slots directly.
</para>

<para>
The main idea for this separation into three objects (document, view and main
window) instead of the usual two (view and main window) is the answer to this
question: What if I want to give the user the possibility to work with the file
in <emphasis>two</emphasis>, or even <emphasis>more</emphasis> windows?
</para>

<para>
Such things could even be done within one main window, through splitters or
dividers, containing two (or more) view instances which both display
<emphasis>one</emphasis> file.  
</para>

<para>
There it is then, the solution can only be that if the user manipulates
the file contents in one view, the other view must be notified about this and
refresh it's contents.  Otherwise a bad scenario will occur if the user closes
one view where he added something to the end of the file which he had cut out at
the beginning, but from the other view, the file would be saved later still
containing the cut section.  This is because if the second and final view did
not recognize the cutting, it still contains the text without modification from
the beginning, plus the newly inserted text.
</para>

<para>
This means that both views have to be synchronized, and this can be done if all
views are notified about each action that the user does.  In that case, which
view he actually uses is immaterial.
</para>

<para>
So finally, a document class is needed to be the one and only holder of the true
contents of the document, and it then is providing the views the possibility to
manipulate those contents.
</para>

<para>
I hope this has given some insight into this model, although in most cases the
programmer can live without it, whether by just using a provided class as the
view area, or by writing a widget on his own to handle user interaction.  Just
so long as you represent <emphasis>one</emphasis> document or file by
<emphasis>one</emphasis> view area, then the view can be responsible for the
data as well as the display, and only provide the methods to retrive or set the
document contents for actions such as reading a file or saving it.
</para>

<para>
The next model to describe, the <acronym>MDI</acronym>, will make a
difference at this point.  There you will see the actual necessity for the
document view model, and the extra functionality it provides.
</para>

</sect1>

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

<para>
As the last section described the document view model, you will probably have
long since guessed what the <acronym>MDI</acronym> means.  Users coming from
other operating systems than &UNIX;/&Linux; are used to it, as well as
programmers developing for those platforms.
</para>

<para>
X-Window applications traditionally are more targeted towards functionality and
stability, and &UNIX; users are used to single windows that provide
functionality, so the document view model is often not required to create
applications.
</para>

<para>
With &Qt; as a multi-platform toolkit, developers have even more choice,
developing for &Microsoft; &Windows; as well as for &UNIX; systems.  On windows,
the lack of &IDE;'s that are capable of handling so called child windows has
been getting to some kind of standard, so this is well addressed by the &Qt; 2.x
library.  So, &UNIX; users can profit from this architecture as well.
</para>

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

<informalexample>
<screen>aplication instance -> main window -> view</screen>
</informalexample>

to 

<informalexample>
<screen>application instance -> main window -> view -> active child window</screen>
</informalexample>
</para>

<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 behavior for maximize, minimize, much as a window
manager does for top level windows.
</para>
</listitem>
</itemizedlist>

<para>
You can use <quote>complete</quote> widgets, such as
<classname>QMultiLineEdit</classname> as child windows just as you do for an
application that only provides one window, and have each child window
responsible for it's own data.  This could be described as <quote>Multiple
Document Interface</quote>, where each child window is equal to a single
document.  The application then manages the usual actions such as providing
methods for the child window interactions like cut or copy.
</para>

<para>
Extending this concept with the document view model inhances 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.x contains the possibility to create such applications, and
&kdevelop; provides you with the appropriate application framework for both &Qt;
only programs and for &kde; applications.  Using &kde; interfaces will offer you
still more possiblilities, through library functions, as well as inter-process
communication, but these are specail aspects that are covered in more specific
introductions to these techniques <!-- link to those? -->
</para>

<para>
Now you are prepared for following the development of such an application for
&kde; 2.x.  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 <acronym>MDI</acronym> applications.
</para>

</sect1>

</chapter>

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

<para>
If you're already familiar with previous versions of &kdevelop;, then you will
know that your &IDE; uses so-called <quote>frameworks</quote> as a starting
point for application development.  These frameworks by default contain support
for a generic document-view model, but were previously limited to
<acronym>SDI</acronym> (Single Document Interface, one window with one view
area) model structures.
</para>

<para>
&kdevelop;'s application wizard is now capable of a lot more than previous
versions.  It offers new project types, including the major extensions we're
interested in, <acronym>MDI</acronym> application frameworks for both &kde; 2.x
and &Qt; 2.x.  These frameworks differ only in the use of some library
functions, such as file dialogs or message boxes, as well as the choice of
toolbar and statusbar classes.  The &kde; version uses &kde; 2.x methods, the
&Qt; version the corresponding &Qt; functions.
</para>

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

<para>
We're going to jump right 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.
</para>

<para>
On the first page, you will be shown a tree of project types.  These contain
&kde; and &Qt; projects, and from this list, select <guilabel>KDE 2
MDI</guilabel> from the <guilabel>KDE</guilabel> section.  If you want to make a
&Qt; application instead, choose the corresponding option, <guilabel>Qt 2.x
MDI</guilabel>.  Then choose <guibutton>Next</guibutton> to continue.
</para>

<para>
On this page, enter <userinput>KScribble</userinput> as the project name, and
<userinput>1.0</userinput> as the version number.  Add your name and email
address in the appropriate fields, and you're done.
</para>

<para>
Click on the <guibutton>Create</guibutton> button, and &kdevelop; will generate
our first version of <application>KScribble</application>.  You can watch the
output in the last page of the wizard.  Notice the messages telling you that the
<quote>messages</quote> file has been set up for your project, this will later
allow you to have <application>KScribble</application> translated into other
languages. When everything is done, you can finally press
<guibutton>Exit</guibutton> to return to the &kdevelop; &IDE;.
</para>

</sect1>

<sect1 id="overview-of-kscribble">
<title>Overview</title>

<para>
It's important at this point to have some understanding of where in the newly
generated sources things are located, and what you as a programmer can now do to
make <application>KScribble</application> into a unique &kde; application.
</para>

<para>
First of all, in the <guilabel>Classes</guilabel> viewer, you will see three
already created classes, <classname>KScribbleApp</classname>,
<classname>KScribbleDoc</classname>, and <classname>KScribbleView</classname>.
As previously explained, each of them has a certain part to play within the
application.  The App class builds the main window, and coordinates user
interaction.  The Doc class maintains the documents that an application can
manipulate.  Finally the View class is responsible for user interaction with the
child windows, and communicates with the document connected to it.
</para>

<para>
These facts have several consequences.  To make best 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, but you're far
from limited to this.  Inheriting from another class is not too much work, and
there is also the possibility to use &eg; a <classname>QMainWindow</classname> or
<classname>KTMainWindow</classname> as the class to inherit from, and use
another class to manage the view.
</para>

<para>
In the next chapter we'll begin 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 pictures and save them too!
</para>

</sect1>

</chapter>

<chapter id="defining-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 about what kind of data the application has to
manage.  This decides for you what the view class will look like, but more
especially it decides how the document class will read and write data to and
from files, and how it will offer methods to manipulate that data.
</para>

<para>
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 they are being
edited.  <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.
</para>

<para>
We need also to define a pen that draws into a document, set it's width, and
it's color, and make it available for the view class to retrieve.  Actually, you
want the view offering the drawing methods, but the document as the central
element for all the views has to hold the pen originally, because you could
otherwise end up with two views of the same document using different pens!
</para>

<para>
So, to define how our document class should work, we will add one instance of
<classname>QPixmap</classname>, one of <classname>QPen</classname>, and then
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 class="headerfile">kscribbledoc.h</filename> by
selecting it in one of the file viewers, or by a click on the
<guilabel>Classes</guilabel> viewer over the class
<classname>KScribbleDoc</classname>.  Then add the lines marked with -> from the
following code snippet:
</para>

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

    class KScribbleDoc
    {

->    	protected:

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

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

<para>
As you can see, we have added pen and buffer, as well as
<function>currentPen()</function> and <function>penWidth()</function>.  As pen
is declared <type>private</type>, we offer a possibility to retrieve the pen as
well as the pen width.  As these are already implemented within the class
declaration, we don't have to add them to the implementation file, which is
where we now turn our attention.
</para>

</sect1>

<sect1 id="intialization-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
->  pen=QPen( Qt::black, 3 );
    /////////////////////////////////////////////////
    modified=false;
    return true;
  }
</programlisting>

<para>
This initializes the pen with the color black, and a width of 3 pixels. The
<classname>QPen</classname> class has some more constructors, but this one is
sufficient for our needs here.
</para>

</sect1>

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

<para>
What is now left to do is to define how to open and save oru pictures.  This is
done through the following methods:
</para>

<programlisting>
    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, "PNG" ))
->     	return false;
     /////////////////////////////////////////////////
->     //f.close();


    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, "PNG" ))
->    		return false;
      /////////////////////////////////////////////////

->      //f.close();
</programlisting>

<para>
Add the lines marked with arrows to your code.  What we have done here is to
comment out the passages where the file
<filename><replaceable>filename</replaceable></filename> is opened, because that
is done automatically by the load and save methods of
<classname>QPixmap</classname> which we have added instead.
</para>

<para>
Other documents may open a file and read in it's contents, such as text lines,
so the <classname>QFile</classname> methods are already present in the
codeframe.
</para>

<para>
As <function>save()</function> and <function>load()</function> return a boolean
value if the operation is successful, we're returning
<returnvalue>false</returnvalue> if not, so that 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 are now
already provided in <classname>QPixmap</classname>.  They require the filename
and format as arguments.  The source framework, on the other hand, does
<emphasis>not</emphasis> call the document methods with the format just yet.
</para>

<para>
If only one format is used, it is sufficient to set the format here.  For now we
are using <quote>PNG</quote> as the format.  See <classname>QImageIO</classname>
for more details about the image formats that can be opened.
</para>

<para>
Now we're almost finished defining our document structure.  The
<classname>QPixmap</classname> buffer serves us as a buffer, storing the
original picture contents while we're wroking on it, and the pen is a valid pen
for all views connected to the document.  Note that the intialization of the pen
is done in <function>newDocument()</function>.  This method is aways called
after the constructor within the framework internally, so you should add
document instances initializations there, as we have done 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.  Then we'll be
ready to paint!
</para>

</sect1>

</chapter>

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

<para>
In this chapter we'll turn to the view class of
<application>KScribble</application>, to define how the child windows shall
work.
</para>

<para>
First of all, we notice that <classname>KScribbleView</classname> is derived
from <classname>QWidget</classname> by default.  This is the minimum requirement
for a child window, but it's is sufficient for our needs.
</para>

<para>
When it comes to defining a new widget's behavior, we need to know how the user
shall interact with the window.  In our example, this will obviously be with the
moust.  Therefore, we have to overwrite some virtual methods from
<classname>QWidget</classname> that process mouse events the widget recieves.
</para>

<para>
What we need is to know when the user presses a mouse button, because the
drawing shall only take place when the mouse button is pressed.  then we need to
know when the mouse is moved (and to know where it moves to), as well as when it
has been released, in order 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
<literal>mousePressed</literal>.
</para>

<para>
From the example below, add the code with the arrow to your include file for the
class <classname>KScribbleView</classname>.
</para>

<programlisting>
   kscribbleview.h

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

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

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

     }
</programlisting>

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

<para>
Now we come to the actual implementation of the event handlers.  As explained in
<!-- link to libref --> the &kde; library reference, &Qt; has a good way of
handling user events, especially when they target widgets.
<classname>QWidget</classname> as a base class preselects the events and
provides basic code handlers.  As these are declared as <type>virtual</type>, we
can overwrite to define how our widget shall react to user actions. One is
already overwritten, the <function>closeEvent()</function> method.  This is
necessary 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 to let the App class do the job.
</para>

<para>
First of all we have to declare the widget default behavior 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;

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

<para>
We're setting the background to <literal>NoBackground</literal>, a cursor
(<literal>crossCursor</literal>), and initializing
<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>
The following implementations have to be inserted as a whole, 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->pos();
}
</programlisting>

<para>
Here, we are setting <varname>mousePressed</varname> to
<returnvalue>true</returnvalue>, so this event is stored somehow.  the second
line is not so obvious.  There we are 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 a
drawing routine from there, in the <function>mouseMoveEvent</function>.
</para>

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

    QRect r = polyline.boundingRect();                      <co id="co-rectangle">
    r = r.normalize();
    r.setLeft( r.left() - doc->penWidth() );                <co id="co-pen-width">
    r.setTop( r.top() - doc->penWidth() );
    r.setRight( r.right() + doc->penWidth() );
    r.setBottom( r.bottom() + doc->penWidth() );

	  doc->setModified();                               <co id="co-bitblt">
    bitBlt( this, r.x(), r.y(), &amp;doc->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>

<calloutlist>
<callout arearefs="co-receive">
<para>
First of all the event handler receives all mouse movements over the widget.
</para>
</callout>

<callout arearefs="co-mousepressed">
<para>
As we're only interested in the move if the mouse is pressed, because that is
the time to draw, we have to ask then if <varname>mousePressed</varname> is
<returnvalue>true</returnvalue>.  This has been already taken care of through
the <function>mousePressEvent()</function> event handler, so we don't have to
worry any more about it.
</para>
</callout>

<callout arearefs="co-painting">
<para>
Now we're ready to start 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 is only acting as a communicator between the document and the
user.
</para>
</callout>

<callout arearefs="co-pen">
<para>
We get the pen from the document instance as well, by calling
<function>currentPen()</function>.</para>
</callout>

<callout arearefs="co-polyline">
<para>
The next three lines assign values inside the polyline
<classname>QPoint</classname> array, setting point 2 to 1, 1 to 0 and 0 to the
point the move went to.  This is the contents of the event we're interested in.
Suppose 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.
</para>
</callout>

<callout arearefs="co-rectangle">
<para>
You may be wondering why the array needs three points then, if we're only
interested in drawing a line from one position to the next. These lines explain
that.  After drawing into our buffer is finished, with
<function>drawPolyline()</function> and <function>painter.end()</function>, we
create a rectangle <varname>r</varname> 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 the most complete rectangle.  Then we use
<function>normalize()</function> to have the leftmost and topmost values the
smallest (as coordinates are counted from top to bottom, and from left to
right).
</para>
</callout>

<callout arearefs="co-pen-width">
<para>
The next thing to do is to adapt the size of the rectangle to the size of the
pen.  The pen has a thickness we get with <function>penWidth()</function>, and
we widen the rectangle by the width of the pen.  Imagine the mouse movement was
only two pixels, but the pen thickness is set to ten pixels wide - the rectangle
wouldn't contain the entire painted area if it isn't enlarged by the pen width
first.
</para>
</callout>

<callout arearefs="co-bitblt">
<para>
Finally, we set the document modified, and use <function>bitBlt()</function> to
copy the rectangle out of the buffer and into the widget.
<function>bitBlt</function> operates bitwise, and is very fast, so that is a
good method to copy the painted area from the buffer to the widget, instead of
repainting the whole window.  It's first argument is the object to draw onto
(the destination), in this case our widget, so we use the pointer
<literal>this</literal>.
</para>

<para>
The next two arguments give the destination top left position to start copying
to, then follows the source to copy the picture from, including it's coordinates
and the width and height.
</para>
</callout>
</calloutlist>

<note>
<para>
As the pixmap coordinates are the same as the coordinates the widget uses,
because our pixmap is drawn into the top left corner, the coordinates for the
source and destination top left point are the same.  This is however, something
you will need to watch out for in some of the next steps, so we bring it to your
attention now that this may not always be the case.
</para>
</note>

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

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

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

</sect1>

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

<para>
What is now left to do are two more virtual event handlers that need
reimplementation.  First of all, we have to take care that our picture gets
painted into the window when something else happens.  For example, when you open
another window that obscures the painting, then change focus 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->rect();

  bitBlt( this, r.x(), r.y(), &amp;doc->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 need only the rectangle that gets repainted, so
we we retrieve the gemoetry from the event (<literal>e->rect()</literal> and use
the coordinates for <function>bitBlt()</function>, just as we did in the
<function>mouseMoveEvent()</function>
</para>

<para>
We still haven't done anything about 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.  Even if we would have set the size, it's 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
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 for now to the same size as the widget.  All this is done
in the event handler <function>resizeEvent()</function>.
</para>

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

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

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

<para>
Here we first call the <function>resizeEvent</function> handler or
<classname>QWidget</classname>.  Then we calculate the size of our picture.
Because we can resize a window to be either bigger or smaller, we have to
separate these two cases.  If we resize to a smaller geometry, the picture
should still contain it's contents.  If, on the other hand, we resize to a
bigger widget, we have to resize the pixmap as well to that bigger size.
</para>

<para>
The calculated values are stored in <varname>w</varname> and
<varname>h</varname>.  Before the resize takes place though, we create a copy of
our pixmap in the document in tmp.  Then we resize the buffer
(the document), fill it with white, and then copy back the contents from tmp to
the buffer.  This resizes our pixmap always synchronous with the widget that
displays it, but doesn't loose contents which are outside the visible area, if
the resizing made the widget smaller.
</para>

<para>
Now, finally, 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 teh document class, and we have
enabled user interaction through overwriting virtual methods in the view class.
All this has gained us our first functionality, and now we can draw pictures as
well.
</para>

<para>
When we created the <classname>QPen</classname> instance in the document class
however, we set some predefined values for the pen.  The color is black, and the
pen is 3 pixels wide.  As you would usually want to change these values in a
drawing application, we have to enhance the main &GUI; by providing ways to set
these, according to which window is currently active, and the document connected
to it.
</para>

<para>
This chapter then, will introduce you to the following concepts:
</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 &Qt; <application>Designer</application>.
</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>
Finally, we also add a method to delete the document contents complete with a
menubar command.
</para>

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

<para>
As the title of this section says, we will be adding a menu for setting pen
values 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>.  You will see that the menubar
items are created in the order they appear on the menubar, but that isn't
necessary.  There are two important things that affect the appearance of the
menubar:
</para>

<itemizedlist>
<listitem>
<para>
The order of the menu items in the menus.
</para>
</listitem>
<listitem>
<para>
The order of insertion of the popup menu into the menubar.
</para>
</listitem>
</itemizedlist>

<para>
Last but not least, you have to create some menus by calling the constructor.
The class declaration already contains the pointers to the popup menus, so we
will first have to add our <guimenu>Pen</guimenU> here.
</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 <function>KScribbleApp::initMenuBar()</function> and add the lines marked
with an arrow below:
</para>

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

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

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


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

<para>
You see now that we first create the menu with <userinput>new
<function>QpopupMenu</function></userinput>.  Then we use the
<function>insertItem()</function> methods to add two menu entries,
<guimenuitem>Color</guimenuitem> and <guimenuitem>Brush</guimenuitem>.
</para>

<para>
The visible commands are inserted with the method <function>i18n()</function>,
which ensures that you can interanationalize your application.  As a rule you
should alwyas declare all visible-to-the-user text with the method
<function>i18n()</function>.  for &Qt; only programs, us it's 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 above is a macro, the <acronym>ID</acronym> of the menubar
item.  This <acronym>ID</acronym> is a number that we have to set using
<literal>#define</literal> in the file <filename>resource.h</filename>, where
you will see all the other already used <acronym>ID</acronym>s declared.  There
are also other ways to insert menus, by directly connecting a slot to the
inserted entry, but the applicaiton framework uses <acronym>ID</acronym>s to
select which action has been activated, and highlighted.  Therefore, each menu
entry, independent of which popup menu it appears in, has it's own unique
number.  Since the numbers aren't too easy to remember later, setting a
<literal>#define</literal> for the <acronym>ID</acronym> is a nice solution.
</para>

<para>
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 insert the popup menu after the <guimenu>Edit</guimenu> men and
before the <guimenu>View</guimenu> menu, so that it will appear between those
menus in the menubar.
</para>

<para>
What is also importnat when creating menus is that they should be available to
the user with shortcuts.  In menus, you will often see underlined characters
that the user can jump directly to, by pressing &Alt; and the underlined letter
of the menu entry.  As a programmer, you set this character with an ampersand,
so in our example the <guimenu>Pen</guimenu> will later be accessible from the
keyboard by pressing <keycombo action="simul">&Alt;<keycap>P</keycap></keycombo>
.  Within the menu, the user can press another button to go directly to the
command he wants, so in the menu itself all items should have this kind of
accelerator key as well.  Note that you should write item insertions together in
group that have the same visible access, so you can kee a better overview of the
characters you hvae already used.  No accelerators should be visible twice at
the same time.
</para>

<para>
This is also important for translators.  In other languages, the character you
chose as accelerator may not be in the menu entry anymore, so translators will
have to reset them sometimes.
</para>

<para>
Finally, in the last two lines above, we're connecting the pen menu with two
slots.  One is 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 to the user.  You can have a look at the
methods the menu is connected to, as they contain switch statements where the
menu <acronym>ID</acronym> sent is compared and the corresponding action is
called.
</para>

<para>
What's left now to do is add the <literal>#define</literal> 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 must be careful
ont to set the same number for two entries, but if this should happen by
accident there's still the compiler which will inform you about redefining.
</para>

<para>
This is currently all you have to do to add a new menu to your menubar.  The
actions they will execute simple, <guimenuitem>Color</guimenuitem> will call a
color setting dialog box, and <guimenuitem>Brush</guimenuitem> will call a
dialog to select the brush width.  Of course, we haven't created this dialog
yet!
</para>

<para>
First, lets extend the toolbar with two icons for these actions.
</para>

</sect1>

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

<para>
You might think that some new commands should be made available as toolbar
buttons, because they are often used and offer increased functionality.  You can
easily do this by adding buttons in the framework's
<function>initToolBar()</function> method of the App class.  
</para>

<para>
Here we've decided to add a button for both of our new menu entries in the
<guimenu>Pen</guimenu> menu, but they first need icons.
</para>

<para>
You will very likely find icons to use in the &kde; directory <filename
class="directory">/toolbar</filename>, or if you are of an artistic mind you can
create some for yourself.  <application>KIconEdit</application> is very suitable 
for painting icons, so we'll take a small detour and make our own icons.
</para>

<para>
Choose <guimenuitem>New</guimenuitem> from the &kdevelop;
<guimenu>File</guimenu> menu, and select from the <guilabel>Linux/KDE</guilabel>
tab the entry for <guilabel>Icon (*.xpm)</guilabel>.  Enter the name you want to
give your new icon in the field on the top right, which for our example could be
<filename>pencolor.xpm</filename>.  You can choose where in your project
directory you want the icon to be saved, by browsing to a different location in
the bottom part of the dialog.  It is good practise to keep toolbar icons in a
separate directory, so create a new directory under your
<application>KScribble</application> project directory, and name it <filename
class="directory">toolbar</filename>.  Change to this directory in the
<guilabel>New File...</guilabel> dialog and press the <guibutton>OK</guibutton>
button.
</para>

<para>
The new icon should now be created in the new <filename
class="directory">toolbar</filename> directory, and will be opened by
&kiconedit; automatically.  Paint something that will symbolize to the user what
the button is intended to do, save the pixmap within &kiconedit; and then switch
back to &kdevelop;  In the <guilabel>Files</guilabel> or
<guilabel>Groups</guilabel> panel, select the icon by a &RMB; click 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 newly active field the
following:
</para>

<screen>
<userinput>$(kde_datadir)/kscribble/toolbar/pencolor.xpm</userinput>
</screen>

<para>
This will install the pixmap in the &kde, file system hierarchy's data
directory, where each application has it's own subdirectory containing
additional files needed by the application.  Icons have to be installed into a
subdirectory here called <filename class="directory">toolbar</filename>, so the
application's icon loader can find the pixmaps for your program.
</para>

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

<para>
Now we have only to insert these buttons into the toolbar.  Add the arrow marked
lines from the following example into your sources:
</para>

<programlisting>
void KScribbleApp::initToolBar()
{
..
    toolBar()->insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy"));
    toolBar()->insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste"));
    toolBar()->insertSeparator();
->  toolBar()->insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") );
->  toolBar()->insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") );
->  toolBar()->insertSeparator();
    toolBar()->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 lad the
icon for the button.  What may seem unusual is there is no mention of the
format.  The preferred format for &kde; 2 is PNG, but it works just fine with
xpm files as well.  You can use &pixie; to convert them in any case, or perhaps
a later version of <application>KScribble</application>!
</para>

<para>
The second argument is again the <acronym>ID</acronym>  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 means
<literal>available</literal> when <returnvalue>true</returnvalue> and
<literal>deactivated</literal> when <returnvalue>false</returnvalue>.  As we
want these new icons to be available, we set this to
<returnvalue>true</returnvalue>.
</para>

<para>
Finally, we add a tooltip for these buttons, which we also embrace with
<function>i18n()</function> to allow internationalization.
</para>

<para>
Now the &gui; is extended, at least visually.  You can compile and run
<application>KScribble</application> again, and see how it looks now.  Of course
the new items in the menu and toolbars can't execute any action, that is what
we're going to be doing in the next section.  You will also note that the
toolbar icons we just added are not displayed - which is because we didn't
install <application>KScribble</application>, and so they can't be found.  The
other icons you can see were already shipped with &kde;, so they are visible
without needing any new installation.
</para>

</sect1>

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

<para>
As we've already created the appropriate 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 Designer File (*.ui)</guilabel>.  Enter the dialog file
name as <userinput>kpenbrushdlg</userinput>, and the extension will be
automatically added.  Press the <guibutton>OK</guibutton> button.
</para>

<para>
When constructing a dialog, we have to think about what is really needed by the
user.  Here we will need a label, to display what will be set.  A spinbox, with
up and down buttons to set the pen width value is needed, and three other
buttons, one for resetting the pen width to the default value, one to cancel the
dialog, and an <guibutton>OK</guibutton> button for applying the new value.
</para>

<para>
The &Qt; <application>Designer</application> will open, first asking you to
choose which kind of interface item you wish to create (in this case, we are
making a <guilabel>Dialog</guilabel>, and then when you have chosen, showing you
an empty canvas that will be the dialog background.
</para>

<!-- Lauri: Ralf, can check this please, Qt Designer's help makes no mention of -->
<!-- it, does this still apply as written below? -->

<para>
We will add the buttons in this order to the dialog.  Order is important,
because the tab focus follows the order by which the widgets are created.  If
you start with the <guibutton>OK</guibutton> button, then the spinbox, then the
<guibutton>Cancel</guibutton> button, the input focus will also start with the
<guibutton>OK</guibutton> button, then the spinbox, and then the
<guibutton>Cancel</guibutton> button, which is probably not what the user is
expecting.  The tab focus should follow the widget's items top to bottom, from
left to right, so we should construct the dialog in this order also.
</para>

<variablelist>
<!-- Lauri: This needs massive amounts of rewriting.  I'm rewriting all the -->
<!-- Qt designer GUI stuff, so please check it for me very carefully! -->

<varlistentry>
<term>The Label</term>
<listitem>
<para>
Choose the icon that looks like an <guiicon>A</guiicon>, or choose <menuchoice>
<guimenu>Tools</guimenu> <guisubmenu>Display</guisubmenu>
<guimenuitem>TextLabel</guimenuitem></menuchoice> to select the label tool.  You
can now use the cursor to draw out a label on the canvas.  You don't need to be
too precise in your dimensions, as they are very simple to change afterwards,
but place the top left corner at position x: 50, y: 20.  &Qt;
<application>Designer</application> shows you this position with a yellow
tooltip, as you drag the label around the canvas.
</para>

<para>
In the <guilabel>Property Editor</guilabel> on the left side of the screen,
choose the <guilabel>text</guilabel> property.  Change the default text
(<quote>TextLabel1</quote>) to say <userinput>Pen Width</userinput>.  Adjust the
width of the label to a width that matches the label contents in the x
direction.  About 120 pixels should be fine, and you can either use your mouse
to drag the label to a new size, or by setting the values in the
<guilabel>geometry</guilabel> section of the <guilabel>Property
Editor</guilabel>.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>The Spinbox</term>
<listitem>
<para>
Press the <guiicon>Spinbox</guiicon> icon on the toolbar, or choose <menuchoice>
<guimenu>Tools</guimenu> <guisubmenu>Input</guisubmenu>
<guimenuitem>SpinBox</guimenuitem></menuchoice> from the menu.  Place your
spinbox on the widget background, to the right of the label we created in the
last step.
</para>

<para>
In the <guilabel>Property Editor</guilabel>, set the name of the spin box to
<userinput>width_spbox</userinput>.  In the <guilabel>maxValue</guilabel> and
<guilabel>minValue</guilabel> fields, set them to 100 and 1 respectively. This
should be sufficient for brush widths.
</para>
</listitem>
</varlistentry>

<varlistentry>
<term>The Buttons</term>
<listitem>
<para>
Finally, we need to create the three buttons.  The leftmost button is the
default one.  Create a button, with either the icon that looks like an
<guiicon>Ok</guiicon> button, or the <menuchoice>
<guimenu>Tools</guimenu> <guisubmenu>Buttons</guisubmenu>
<guimenuitem>Pushbutton</guimenuitem></menuchoice> menu entry.  Place it
somewhere nicely on the bottom of the dailog, and set the name to
<userinput>default_btn</userinput>, and the button text to
<userinput>Default</userinput>.
</para>

<para>
Repeat this for the <guibutton>OK</guibutton> button, with the name
<userinput>ok_btn</userinput> and the <guibutton>Cancel</guibutton> button, with
the name <userinput>cancel_btn</userinput>.  Set the button text to
<userinput>&amp;OK</userinput> and <userinput>&amp;Cancel</userinput>
respectively.
</para>
</listitem>
</varlistentry>
</variablelist>

<para>
When you're happy with the layout of the dialog choose
<menuchoice><guimenu>File</guimenu>
<guimenuitem>Save</guimenuitem></menuchoice> and then close &Qt; <application>Designer</application>
</para>

</sect1>

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

<para>
After we have created the &gui; of the dialog, we have to add some functionality
to the buttons, and provide ways to set and retrieve the selected value of the
spinbox.  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> to quit the dialog.
</para>

<!-- Lauri: well, where did the class get generated, or do you need to do New -->
<!-- Class to get the framework? -->

<para>
In the generated <!-- see comment above --> 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 &gui; construction, so we don't have to worry about that
anymore, and we can go directly to adding the usual connections for the
pushbuttons first.
</para>

<para>
Add the lines marked with arrows to the constructor of the dialog:
</para>

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

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

<para>
This provides the functionality for the three buttons on the bottom of the
dialog, when the user clicks on them.  First we set the
<guibutton>Default</guibutton> 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
<guibutton>OK</guibutton> to call the slot <function>accept()</function>,
provided by <classname>QDialog</classname>.  The next line connects the
<guibutton>Cancel</guibutton> to <classname>QDialog</classname>'s
<function>reject()</function> slot.  This will both close the dialog and reset
the resulting value, which we will use later, when we implement the method that
calls the dialog to determine if we want to see 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->setValue(curr); }
int width() { return width_spbox->value(); };
</programlisting>

<para>
Add these methods to the class declaration with the modifier
<literal>public</literal>, 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 width when the user presses
<guibutton>OK</guibutton>, 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->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 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 the method added for selecting the
pen color, one after the other.
</para>

<para>
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->windowList();
  KScribbleView* m = (KScribbleView*)windows.at(0);
	KScribbleDoc* pDoc = m->getDocument();
  int curr_width=pDoc->penWidth();

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

<para>
Here we first have to access the window list, and retrieve a pointer to a
document.  This can be the document of any window, because all documents should
have the same current pen width.  Then we create an integer variable
<varname>cur_width</varname>, which 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->setCurrent()</function>, the method we added to the
dialog.
</para>

<para>
The <function>if()</function> ensures that the following code is only executed
when the result code of the dialog box has the accept flag set, which means of
course, the code is only 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 this we use the
<function>for()</function> loop, and set every document's pen width to the
<varname>width</varname> variable we retrieved before with
<function>dlg->width()</function>.
</para>

<para>
We haven't yet implemented the method <function>setPenWidth()</function> in the
document class, so we'll do that 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 <acronym>ID</acronym>´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 <function>slotPenColor()</function> 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->windowList();
    KScribbleDoc* pDoc;
    KScribbleView* m;
    for ( int i = 0; i < int(windows.count()); ++i )
    {
      m = (KScribbleView*)windows.at(i);
      if ( m )
      {
        pDoc = m->getDocument();
        pDoc->setPenColor(myColor);
      }
    }
  }
  slotStatusMsg(i18n("Ready."));
}
</programlisting>

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

<programlisting>
kscribbledoc.h:

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

<para>
Be sure to add the declaration of the two new methods,
<function>slotPenBrush()</function> and <function>slotPenColor()</function> to
the class <classname>KScribbleApp</classname>, so that our class knows about
these methods.
</para>

<para>
All done!  Lets do quick summary on what we've done in this chapter:
</para>

<itemizedlist>
<listitem>
<para>
First we added menubar and toolbar commands for two dialogs that we want to
call.
</para>
</listitem>

<listitem>
<para>
Then we created the first dialog, to set the pen width, with &Qt;
<application>Designer</application>.
</para>
</listitem>

<listitem>
<para>
After that, we created all the necessary methods for our menubar/toolbar items
to invoke the dialogs, and to set the properties we want to change.
</para>
</listitem>

<listitem>
<para>
Finally, we added the necessary set of functions to the document, so that we are
able to change the values after the dialogs are executed.
</para>
</listitem>
</itemizedlist>

<para>
Through this structure, you should now have an overview of how to extend your
application, adding more functionality and manipulating settings that influence
the behavior 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 funtionality of our view widget with
two enhancements: synchronized views and scrolled views.
</para>

<sect1 id="synchronizing-views">
<title>Synchronizing views</title>

<para>
Let's first explain what advantage this will bring us, and 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 so do any further views you create.  
</para>

<para>
When it comes to painting into the document however, you can only do this in one
view, because the other views are not displaying the document contents at the
same time.  Only if you obscure one view that doesn't display the current (updated)
contents, and then bring it to the front again, it will then display the current
contents.
</para>

<para>
This is because after a widget has been obscured and then activated again, it
receives a paint event from the window system.  This calls
<function>KScribbleView::paintEvent()</function> again, and that is the one that
finally redraws the contents of the area that was obscured.
</para>

<para>
What we want to achieve though, is that all views should paint in synchrony with
each the one the user is actualy painting into.   You will see in fact, this
enhancement is a very simple task.
</para>

<para>
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 synchronize the document contents.  Every time the
document contents are changed, in our case by mouse movoements (where we copy
the changes to the buffer with <function>bitBlt()</function>, we just have to
call <function>updateAllViews(this)</function>.  The
<literal>this</literal> pointer is necessary 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
<function>updateAllViews(this)</function> at the end of the virtual methods
<function>mousePressEvent()</function>, <function>mouseMoveEvent()</function>,
and <function>mouseReleaseEvent()</function>, then you're finished.
</para>

<para>
Take this as a general rule in your applications, that each time the contents of
the document is changed by a view, call <function>updateAllViews()</function>.
How the update is to be executed is implemented in the widget's
<function>update()</function> method.  You may be content to simply setting
&eg;, the changed text in an editor.  For 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 piece of functionality that can be quite
threatening to developers, if you can't use an already implemented widget that
provides the scrolling for you.
</para>

<para>
What precisely does scrolling mean?  In our context, the problem begins when we
want to see a picture that is bigger than the view can display.  The result so
far is that you can only see as much as the size of the view allows, starting at
the top left corner.  The rest will be hidden from the user's sight.
</para>

<para>
A scrollview on the other hand, is a widget that provides a scrollbar on the
right and bottom sides of the widget, by which the user can <quote>move</quote>
the contents.
</para>

<para>
In fact, the contents are not moving, but the view area is moving instead, to a
different position within the document.  Fortunately &Qt; provides a class
(<classname>QScrollView</classname>) that itself inhereits from
<classname>QWidget</classname> and offers the same base functionality as an
ordinary widget, while managing the contents through scrollbars.  You have the
additional option of using either an instance of
<classname>QScrollView</classname>, creating the child widgets to manage the
scroll view as parent and then add them to the scrollview with
<function>addChild()</function>, or to create a view by inheriting
<classname>QScrollView</classname> and draw into the viewport.
</para>

<para>
The viewport is a defined area inside the scrollview which you can draw into,
instead of directly to the widget.  The difference here is that
<classname>QScrollView</classname> provides a set of event handlers similar to
<classname>QWidget</classname>, but especially for the viewport.  So, what was
formerly a <function>mousePressEvent()</function> in our view, will become
instead a <function>viewportMousePressEvent()</function>, and a
<function>paintEvent()</function> will become a
<function>viewportPaintEvent()</function>.
</para>

<para>
This second possibility then will suit our needs to make
<classname>KScribbleView</classname> a scrollable widget, and so we have to make
the following modifications:
</para>

<itemizedlist>
<listitem>
<para>
First we have to make the document use a size of it's contents.  This can be
done by getting the size of the opened picture, and for a new picture, defining
a default start size.  In other painting applications, this size can normally be
changed by a command provided by the &gui;, &eg; a dialog that asks for a new
width and height as well as a method to shrink the picture contents to fit that
new size.
</para>
</listitem>

<listitem>
<para>
Changing the inheritance of <classname>KScribbleView</classname> from
<classname>QWidget</classname> to <classname>QScrollView</classname>.
</para>
</listitem>

<listitem>
<para>
Renaming the virtual methods to the corresponding viewport methods of
<classname>QScrollView</classname>.
</para>
</listitem>

<listitem>
<para>
Adapting the virtual event handlers to act on the geometry of the viewport.  The
old implementation relies on the geometries of <classname>QWidget</classname>
starting at the top left corner of the widget.  If the view is scrolled, and the
top left corner is not visible, we have to ensure that the positions retrieved
from <classname>QWidget</classname> coordinates are then translated to viewport
coordinates.
</para>
</listitem>
</itemizedlist>

</sect1>

<sect1 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 for all views to retrieve this
size.
</para>

<para>
To do 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 want to modify all methods that deal with initializing, and opening the
document contents.  These are <function>newDocument()</function> and
<function>openDocument()</function>:
</para>

<programlisting>
  bool KScribbleDoc::newDocument()
  {
    /////////////////////////////////////////////////
    // TODO: Add your document initialization code here
->  size=QSize(300,200 );
   	pen=QPen( Qt::black, 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;
  }
</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.
</para>

<para>
When it comes to opening a new picture, we have to set the size to the size of
that picture.  This can be done by calling <function>QPixmap::size()</function>,
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>

</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 changes:
</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</> first, and then added the required include file.  We
also changed all implemented event handlers that deal with the interaction
between the contents and the scroll view to the corresponding
<classname>QScrollView</> provided methods, and commented out the
<function>resizeEvent()</function>.
</para>

<para>
Now we can go over to the implementation of these methods, and make use of the
size our picture now 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 to resize the contents (that 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);

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

->    QSize size=doc->docSize();
      // resize the viewport - this makes the resizeEvent obsolete
->    resizeContents(size.width(), size.height());
      // resize the widget to show up with the document size
->    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, 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> along with the size of the
document.
</para>

<para>
Now we can reimplement the event handlers.  First, we should take care of the
<function>paintEvent</function>, as this is one of the most important ones,
because it is called whenever the widget first shows up and also when it is
resized.
</para>

<note>
<para>
Be sure to comment out the <function>resizeEvent()</function> implementation!
</para>
</note>

<para>
The paint event will have to copy the pixmap in the buffer to the appropriate
position in the view.  For this we have to change the destination of
<function>bitBlt()</function> from <literal>this</literal> to
<function>viewport()</function>, set the top left position to 0,0, and then set
the target (the buffer) to cpoy from the <function>contentsX</function> and
<function>contentsY</function> position onto the viewport:
</para>

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

<para>
The <function>contentsX()</function> is the position in the x direction of the
scrollview's contents, which becomes position 0 in the viewport's absolute
position, and is now the top left point visible in the scroll view.  The same
applies to the y direction.  This part is sometimes hard to understand, and ou
may have to do a bit of <quote>trial and error</quote> when implementing your
own scroll views.
</para>

<para>
The other possible call of <function>bitBlt()</function> would be to switch the
values of the positions, and invert the contents values:
</para>

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

<para>
The final changes we must make are changing the mouse event handlers.
</para>

<para>
First the <function>mouseMoveEvent()</function>, which changes to
<function>viewportMouseMoveEvent()</function>, and has a
<function>bitBlt()</function> call as well.  Here we have to apply the same
changes as in the paint event.
</para>

<para>
Further, in the case of <function>mousePressEvent()</function> and the
<function>mouseMoveEvent()</function>, we have retrieved the position of the
mouse events with <function>e->pos()</function>.  This position will now be
delivering us a widget position, and not the contents position, so we have to
translate this to draw into the correct position in the document with
<function>viewportToContents()</function>:
</para>

<programlisting>
  void KScribbleView::viewportMousePressEvent( QMouseEvent *e )
  {
    mousePressed = TRUE;
->  doc->polyline[2] = doc->polyline[1] = doc->polyline[0] = viewportToContents(e->pos());
    doc->updateAllViews(this);
  }

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

  	  doc->setModified();
->    bitBlt(viewport(), r.x()-contentsX(), r.y()-contentsY() ,
->            &amp;doc->buffer, r.x(), r.y(), r.width(), r.height() );
  	  doc->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>, subtracting the
<function>contentsX()</function> and <function>contentsY</function> values to
copy the rectangle containing the current painting into the correct position in
the viewport.
</para>

<para>
Finally, 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 often leads to the so-called
<quote>flicker</quote> effect.
</para>

<para>
This flicker effect sometimes occurs with widgets, but there are ways to reduce
this behavior.  Instead of calling <function>repaint()</function>, we could call
<function>repaint(false)</function> as well.  This will not erase the widget
contents before redrawing it.  As we are copying the document contents directly
into the widget, we don't need to erase it first, as all the data will be
overwritten anyway.</para>

<para>
In conjunction with <classname>QScrollView</classname>, we will reduce the
painting even more: we can limit the update method to call
<function>repaint()</function> on the <function>viewport()</function> widget,
because that will call <function>viewportPaintEvent()</function>. 
</para>

<para>
On the other hand, the painting area we use is the rectangle containing the
document contents, which can result in the document size being smaller than the
viewport size.  So we can limit the paint event in this case the rectangle of
the viewport where the document is displayed, whose visible height and width we
can retrieve and compose to the rectangle.  
</para>

<para>
Additionally, we use the erase parameter with <returnvalue>false</returnvalue>,
so the document area doesn't get erased:
</para>

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

<para>
Now you're ready!  This chapter has been one of the hardest to implement and
understand, especially when it comes to the geometries that change.  On the
other hand, we gave our application a whole new level of functionality with the
new scrollview and synchronized views.
</para>

<para>
With that, we move on to the last chapter of our tutorial.  There we will apply
only a few changes, making use of some methods of the &kde; 2.x libraries.  As
usual, this will bring us new and interesting functionality, allowing
<application>KScribble</application> to open and save a whole range of picture
formats, and thereby remove the restriction of operating only on the PNG file
format.
</para>

</sect1>

</chapter>

<chapter id="using-kimagio">
<title>Using <classname>KImageIO</classname> with
<application>KScribble</application></title>

<para>
When it comes to images, the &Qt; and &kde; libraries offer a wide variety of
operations.  Apart from actual drawing routines, the libraries support a whole
set of image formats which they can read and write, all without any cost on our
part.
</para>

<para>
The main class for thse operations is <classname>KImageIO</classname>, which has
a support library by &kde;, also called KImageIO.
</para>

<para>
As a preparation before we can make use of these nice methods, we have to add
the appropriate library to <application>KScribble</application>: libkimgio.  Open
the <menuchoice><guimenu>Project</guimenu> <guimenuitem>Options</guimenuitem>
</menuchoice> menu choice 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 and apply our changes.  First we have to remove the curent
restrictions in the document class, which until now have allowed us to only read
and write PNG files.  Just replace <quote>PNG</quote> in the following methods
with <quote>format</quote>, as given by the <function>openDocument()</function>
and <function>saveDocument()</function> methods.
</para>

<programlisting>
In method <function>KScribbleDoc::openDocument()</function>

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

<programlisting>
In method <function>KScribbleDoc:: saveDocument()</function>

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

<para>
Then we have a default behavior for these methods.  For now, we don't have all
the available file formats yet, so this will be our next task, together with
adapting the slots in <application>KSribble</application> that deliver us the
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 to <function>registerFormats()</function> in our
<function>main()</function> function>:
</para>

<programlisting>
<filename>main.cpp</filename>
............
  KApplication app;
  KImageIO::registerFormats();

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

<para>
Note that the call is after the application is instanciated with
<classname>KApplication></classname> app.  Without the application instance, our
program will not run, as <classname>KImageIO</classname> then doesn't know on
which application to register the formats.
</para>

<para>
The include file for this call will be added to
<filename>kscribble.h</filename>, as we're going to use some of it's methods in
<classname>KScribbleApp</classname>:
</para>

<programlisting>
#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 any document, but until now only with the filename.  It just
leaves out the extension, as the format is not required by the document class by
default.  As we have just changed that, we need now a format, and we have to
adapt the call for <function>KScribbleDoc::openDocument()</function> in the
method <function>openDocumentFile()</function>.
</para>

<programlisting>
kscribble.cpp:

  void KScribbleApp::openDocumentFile(const char* file)
  {
  	...........
  	
  	else
    {
->    QString format=KImageIO::type(file);
->	  if(!doc->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 possibility
would be to detect the format in the document class.
</para>

</sect1>

<sect1 id="setting-file-filters">
<title>Setting file filters with <classname>KImageIO</classname></title>

<para>
Here we're finally at the end of our tutoral.  We will adapt the file dialogs of
<application>KScribble</application> to make use of file filters.
</para>

<para>
For these, <classname>KImageIO</classname> provides nice methods to give us all
the required strings for all image file formats that are available for reading
or saving.
</para>

<para>
The following implementation replaces the default file filter, which is, in
fact, no filter at all.  You have to change your own application file filter to
your mime type according to the <function>method()</function> of
<classname>KImageIO</classname>:
</para>

<programlisting>
 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."));
  }
</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.
</para>

<para>
Now that we have finished opening files by filename and format combined, we're
not quite done.  Something is missing to complete this structure.  Setting the
patters for saving a file also.
</para>


<para>
For this job, <function>slotFileSaveAs</function> is called, which invokes for
itself the file dialog that retrieves a filename.  There we will set the pattern
mode to writing.
</para>

<programlisting>
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->activeWindow();
    if( m )
    {
      KScribbleDoc* doc =	m->getDocument();
  	  QString format=QFileInfo(newName).extension();
  	  format=format.upper();
		  if(!doc->saveDocument(newName,format))
  	  {
  		  KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
				return;
			}
      doc->changedViewList();
      setWndTitle(m);
    }
  }
  slotStatusMsg(i18n("Ready."));
}
</programlisting>

</sect1>

</chapter>

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

<para>
In this chapter we will show you how easy it is to implement printing functions
using &Qt;.
</para>

<para>
It's actually just one line of code for us to do here, but we will start by
giving you an overview of who is actually doing the printing job.
</para>

<para>
When the user presses the <guiicon>print</guiicon> button in
<application>KScribble</application>, or chooses
<menuchoice><guimenu>File</guimenu>
<guimenuitem>Print</guimenuitem></menuchoice>, 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>.
</para>

<para>
This then calls the widget's printing method,
<function>ScribbleView::print()</function>.  Here, the framework already
contains a base implementation.  A quick look will tell 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 such as
<function>drawPixmap()</function>, we will of course use one of them:
</para>

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

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

<para>
Go right ahead and test it.  Now you can print any graphics 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 of <application>KScribble</application>, complete with a
further extension that adds cut, copy, paste, and undo functions, as well as
drag'n drop in the <link linkend="sources">Appendix</link>.
</para>

</chapter>

<chapter id="credits-and-licenses">
<title>Credits and Licenses</title>

<para>
&kdevelop;
</para>

&underFDL;
&underGPL;

</chapter>

<appendix id="sources">
<title>KScribble-1.0 Example sourcecode</title>

<para>
The example tarball is locally installed and can be downloaded into your home
directory, where you can untar it and test it.  After untarring the tarball with
<userinput><command>tar</command> <option>zxvf</option>
<filename>kscribble-1.0.tar.gz</filename></userinput>, 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 the authors installation of &kde; 2.x and &Qt; 2.x, so you may have
to change them manually to match your installation path for these options.
</para>

<para>
The tarball can be found in
<filename>$KDEDIR/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</filename>
</para>

<sect1 id="code-main-cpp">
<title><filename>main.cpp</filename></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->show();

      KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

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

      args->clear();
   }

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

</sect1>

<sect1 id="code-kscribble-h">
<title><filename>kscribble.h</filename></title>

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

</sect1>

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

<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->config();
  printer = new QPrinter;
  untitledCount=0;
  pDocList = new QList&lt;KScribbleDoc&gt;();
  pDocList->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->connectItem(KStdAccel::New, this, SLOT(slotFileNew()));
  keyAccel->connectItem(KStdAccel::Open, this, SLOT(slotFileOpen()));
  keyAccel->connectItem(KStdAccel::Save, this, SLOT(slotFileSave()));
  keyAccel->connectItem(KStdAccel::Close, this, SLOT(slotFileClose()));
  keyAccel->connectItem(KStdAccel::Print, this, SLOT(slotFilePrint()));
  keyAccel->connectItem(KStdAccel::Quit, this, SLOT(slotFileQuit()));
  // editMenu accelerators
  keyAccel->connectItem(KStdAccel::Cut, this, SLOT(slotEditCut()));
  keyAccel->connectItem(KStdAccel::Copy, this, SLOT(slotEditCopy()));
  keyAccel->connectItem(KStdAccel::Paste, this, SLOT(slotEditPaste()));

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

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

  keyAccel->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->insertItem(BarIcon("filenew"), i18n("&amp;New"), ID_FILE_NEW);
  pFileMenu->insertItem(BarIcon("fileopen"), i18n("&amp;Open..."), ID_FILE_OPEN);
  pFileMenu->insertItem(i18n("Open &amp;recent"), pRecentFileMenu, ID_FILE_OPEN_RECENT);

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

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

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

  ///////////////////////////////////////////////////////////////////
  // menuBar entry window-Menu
  pWindowMenu = new QPopupMenu(this);
  pWindowMenu->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()->insertItem(i18n("&amp;File"), pFileMenu);
  menuBar()->insertItem(i18n("&amp;Edit"), pEditMenu);
  menuBar()->insertItem(i18n("&amp;Pen"), pPenMenu);
  menuBar()->insertItem(i18n("&amp;View"), pViewMenu);
  menuBar()->insertItem(i18n("&amp;Window"), pWindowMenu );
  menuBar()->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()->insertButton(BarIcon("filenew"), ID_FILE_NEW, true, i18n("New File"));
  toolBar()->insertButton(BarIcon("fileopen"), ID_FILE_OPEN, true, i18n("Open File"));
  toolBar()->insertButton(BarIcon("filefloppy"), ID_FILE_SAVE, true, i18n("Save File"));
  toolBar()->insertButton(BarIcon("fileprint"), ID_FILE_PRINT, true, i18n("Print"));
  toolBar()->insertSeparator();
  toolBar()->insertButton(BarIcon("editcut"), ID_EDIT_CUT, true, i18n("Cut"));
  toolBar()->insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy"));
  toolBar()->insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste"));
  toolBar()->insertSeparator();
  toolBar()->insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") );
  toolBar()->insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") );
  toolBar()->insertSeparator();
  toolBar()->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()->insertWidget(ID_HELP_WHATS_THIS, btnwhat->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()->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->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->activeWindow()->caption());
}

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

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

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

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

  pRecentFileMenu->clear();

  for ( int i =0 ; i < (int)recentFiles.count(); i++){
    pRecentFileMenu->insertItem(recentFiles.at(i));
  }

}

void KScribbleApp::createClient(KScribbleDoc* doc)
{
  KScribbleView* w = new KScribbleView(doc, pWorkspace,0,WDestructiveClose);
  w->installEventFilter(this);
  doc->addView(w);
  w->setIcon(kapp->miniIcon());
  if ( pWorkspace->windowList().isEmpty() ) // show the very first window in maximized mode
    w->showMaximized();
  else
    w->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->first(); doc > 0; doc=pDocList->next())
  {
    if(doc->pathName()==file)
    {
       KScribbleView* view=doc->firstView();  
       view->setFocus();
       return;
     }
  }
  doc = new KScribbleDoc();
  pDocList->append(doc);
  doc->newDocument();
  // Creates an untitled window if file is 0  
  if(!file)
  {
    untitledCount+=1;
    QString fileName=QString(i18n("Untitled%1")).arg(untitledCount);
    doc->setPathName(fileName);
    doc->setTitle(fileName);
  }
  // Open the file
  else
  {
    QString format=QImageIO::imageFormat(file);
    if(!doc->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->setGroup("General Options");
  config->writeEntry("Geometry", size());
  config->writeEntry("Show Toolbar", toolBar()->isVisible());
  config->writeEntry("Show Statusbar",statusBar()->isVisible());
  config->writeEntry("ToolBarPos", (int) toolBar()->barPos());
  config->writeEntry("Recent Files", recentFiles);
}


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

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

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

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

  for (int i=0; i < (int) recentFiles.count(); i++)
  {
    pRecentFileMenu->insertItem(recentFiles.at(i));
  }

  QSize size=config->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->isEmpty())
    return true;

  for(doc=pDocList->first(); doc!=0;doc=pDocList->next()){
    if(doc->isModified())
      saveFiles.append(doc->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->first(); doc!=0;doc=pDocList->next()){
        if(doc->title().contains(i18n("Untitled")))
          slotFileSaveAs();
        else
        {
          if(!doc->saveDocument(doc->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->type() == QEvent::Close)
  {
    QCloseEvent* e=(QCloseEvent*)event;
    KScribbleView* pView=(KScribbleView*)object;
    KScribbleDoc* pDoc=pView->getDocument();
    if(pDoc->canCloseFrame(pView))
    {
       pDoc->removeView(pView);
       if(!pDoc->firstView())
         pDocList->remove(pDoc);
       
      e->accept();
      //////////////  
      if(pWorkspace->windowList().count()==1)
        setPlainCaption(kapp->caption());
      else
        setCaption(pWorkspace->activeWindow()->caption());      
      //////////////
    }
    else
      e->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->text(id_));
  
  slotStatusMsg(i18n("Ready."));
}

void KScribbleApp::slotFileSave()
{
  slotStatusMsg(i18n("Saving file..."));
  KScribbleView* m = (KScribbleView*)pWorkspace->activeWindow();
  if( m )
  {
    KScribbleDoc* doc =  m->getDocument();
    if(doc->title().contains(i18n("Untitled")))
     slotFileSaveAs();
    else
      if(!doc->saveDocument(doc->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->activeWindow();
    if( m )
    {
      KScribbleDoc* doc =  m->getDocument();
      QString format=QFileInfo(newName).extension();
      format=format.upper();
      if(!doc->saveDocument(newName,format))
      {
        KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
        return;
      }
      doc->changedViewList();
      setWndTitle(m);
    }
    
  }

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

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

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

void KScribbleApp::slotFilePrint()
{
  slotStatusMsg(i18n("Printing..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
  if ( m )
    m->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->first(); w!=0; w=memberList->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->close())
      break;
    }
  }  
  slotStatusMsg(i18n("Ready."));
}

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

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

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

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

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

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

void KScribbleApp::slotEditClearAll()
{
  slotStatusMsg(i18n("Clearing the document contents..."));
  
  KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
  if ( m ){
    KScribbleDoc* pDoc = m->getDocument();
    pDoc->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->windowList();
  KScribbleView* m = (KScribbleView*)windows.at(0);
  KScribbleDoc* pDoc = m->getDocument();
  int curr_width=pDoc->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->exec()){
    int width=dlg->width();
    for ( int i = 0; i < int(windows.count()); ++i )
    {
      m = (KScribbleView*)windows.at(i);
      if ( m )
      {
        pDoc = m->getDocument();
        pDoc->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->windowList();
    KScribbleDoc* pDoc;
    KScribbleView* m;
    for ( int i = 0; i < int(windows.count()); ++i )
    {
      m = (KScribbleView*)windows.at(i);
      if ( m )
      {
        pDoc = m->getDocument();
        pDoc->setPenColor(myColor);
      }
    }
  }
  slotStatusMsg(i18n("Ready."));
}


void KScribbleApp::slotViewToolBar()
{
  slotStatusMsg(i18n("Toggle the toolbar..."));
  ///////////////////////////////////////////////////////////////////
  // turn Toolbar on or off
  if( menuBar()->isItemChecked(ID_VIEW_TOOLBAR))
  {
    menuBar()->setItemChecked(ID_VIEW_TOOLBAR, false);
    enableToolBar(KToolBar::Hide);
  }
  else
  {
    menuBar()->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()->isItemChecked(ID_VIEW_STATUSBAR))
  {
    menuBar()->setItemChecked(ID_VIEW_STATUSBAR, false);
    enableStatusBar(KStatusBar::Hide);
  }
  else
  {
    menuBar()->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->activeWindow();
  if ( m ){
     KScribbleDoc* doc = m->getDocument();
    createClient(doc);
  }

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

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


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

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

void KScribbleApp::windowMenuActivated( int id )
{
  QWidget* w = pWorkspace->windowList().at( id );
  if ( w )
    w->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->setPathName(fileName);
    doc->setTitle(fileName);
    doc->newDocument();
    pDocList->append(doc);
    KPixmap tmp;
    tmp.resize(img.size());
    tmp.convertFromImage(img);
    doc->setPixmap(tmp);
    doc->resizeDocument(tmp.size());
    doc->setModified();
    createClient(doc);
  }
}
/** accepts drag events for images */
void KScribbleApp::dragEnterEvent( QDragEnterEvent* e){
  e->accept(QImageDrag::canDecode(e));
}
</programlisting>

</sect1>

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

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

</sect1>

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

<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->append(view);
  changedViewList();
}

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

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

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


void KScribbleDoc::updateAllViews(KScribbleView *sender)
{
  KScribbleView *w;
  for(w=pViewList->first(); w!=0; w=pViewList->next())
  {
      w->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->first(); w!=0; w=pViewList->next())
    {
        if(!w->close())
         break;
    }
  }
  if(isLastView())
  {
    w=pViewList->first();
    w->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>

</sect1>

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

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

</sect1>

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

<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()->setAcceptDrops(true);
  setDragAutoScroll(true);
  doc=pDoc;
  action=IDLE;
  viewport()->setCursor( Qt::crossCursor );
  QSize size=doc->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()->repaint(0,0,visibleWidth(), visibleHeight(), false);
}

void KScribbleView::print(QPrinter *pPrinter)
{
  if (pPrinter->setup(this))
  {
    QPainter p;
    p.begin(pPrinter);
          
    ///////////////////////////////
    // TODO: add your printing code here
    p.drawPixmap(0,0,doc->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->buffer, select.x()+contentsX(),  select.y()+contentsY(), cb_pix.width(), cb_pix.height());
  cb->setPixmap(cb_pix);
  // fill cb_pix with white and copy to selection area
  cb_pix.fill(Qt::white);
  bitBlt(&amp;doc->buffer, select.x()+contentsX(), select.y()+contentsY(),
         &amp;cb_pix, 0, 0, cb_pix.width(), cb_pix.height());
  action = IDLE;
  doc->setModified();
  doc->updateAllViews(this);
  viewport()->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->buffer, select.x()+contentsX(),  select.y()+contentsY(),cb_pix.width(), cb_pix.height());
  cb->setPixmap(cb_pix);
  action = IDLE;
  viewport()->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->pixmap().rect();
  action = PASTE;
  viewport()->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->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()->height()-viewport()->height());
      break;
    case Key_PageUp:
      scrollBy( 0, -viewport()->height() );
      break;
    case Key_PageDown:
      scrollBy( 0, viewport()->height() );
      break;
  }

}

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

void KScribbleView::viewportMouseReleaseEvent( QMouseEvent *e )
{
  if ( action == DRAW )
  {
    action = IDLE;
    doc->updateAllViews(this);
  }
  if ( action == SELECT)
  {
    QPoint pt=e->pos();
    int x = pt.x() > 0 ? pt.x() : 0;
    int y = pt.y() > 0 ? pt.y() : 0;
    select.setRight(x);
    select.setBottom(y);
    QSize size=doc->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->pos());
    viewport()->setCursor( Qt::crossCursor );
    QPixmap cb_pix;
    cb_pix.resize(cb->pixmap().size());
    cb_pix=cb->pixmap();
    bitBlt( &amp;doc->buffer, contentsX()+select.x(), contentsY()+select.y(),
            &amp;cb_pix, 0,0 , select.width(),select.height() );
    viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
    doc->setModified();
    doc->updateAllViews(this);
  }

}

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

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

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

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

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

  if( action == PASTE )
  {
    tmp.resize(cb->pixmap().size());
    tmp=cb->pixmap();
  }
  if( action == PASTE || action == DRAG )
  {
    QSize size=doc->docSize();
    select = select.intersect(QRect(0,0,size.width(), size.height()));  
    if(select.intersects(e->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->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->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->pos());
    viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
  }
}

void  KScribbleView::viewportDragLeaveEvent ( QDragLeaveEvent * )
{
  action = IDLE;
  viewport()->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->pos());
    bitBlt(&amp;doc->buffer, select.x()+contentsX(), select.y()+contentsY(),
           &amp;tmp, 0, 0, tmp.width(), tmp.height());
    doc->setModified();
    doc->updateAllViews(this);
  }
  action = IDLE;
    viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
}
</programlisting>

</sect1>

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

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

</sect1>

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

<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->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->setValue(3);
}
</programlisting>

</sect1>

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

<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->resize(370,210);
  this->setMinimumSize(0,0);
  width_spbox= new QSpinBox(this,"width_spbox");
  width_spbox->setGeometry(150,50,100,25);
  width_spbox->setMinimumSize(0,0);
  width_spbox->setValue(1);
  width_spbox->setRange(1,99);

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

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

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

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

}
</programlisting>

</sect1>

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

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

</sect1>

</appendix>

</book>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-indent-step:0
sgml-indent-data:nil
End:
-->
