1
<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook V3.1-Based Variant V1.0//EN" [
2
<!ENTITY kdevelop "<application>KDevelop</application>">
3
<!ENTITY % addindex "IGNORE">
4
<![ %addindex; [ <!ENTITY docindex SYSTEM "docindex.docbook"> ]]>
8
<title>KDE Application Tutorials
10
<subtitle>The KDE Application Tutorials Handbook for the KDevelop Integrated Development Environment</subtitle>
14
<firstname>Ralf</firstname>
15
<surname>Nolden</surname>
17
<orgname>The KDevelop Team</orgname>
18
<address><email>Ralf.Nolden@post.rwth-aachen.de</email></address>
23
<date>21/03/2000</date>
24
<releaseinfo>1.02.00</releaseinfo>
27
<keyword>KDE</keyword>
28
<keyword>KDevelop</keyword>
29
<keyword>application tutorials</keyword>
33
<para>The KDE Application Tutorials Handbook for the KDevelop Integrated
34
Development Environment, a tutorial collection on how to create KDE applications
35
through examples. This handbook itself is part of the KDevelop Integrated Development
36
Environment and is therefore also licensed under the GNU General Public License;
37
see <link linkend="copyright">Copyright</link> for more information.</para>
42
<chapter id="introduction">
43
<title>Introduction</title>
45
<para>As KDE is currently becoming a standard for Unix System desktops, more and
46
more developers want to take advantage of the well-written <abbrev>API</abbrev>
47
that the KDE project includes, to create new applications that offer a
48
<abbrev>GUI</abbrev> as well as an easy use of the classes provided with
49
Qt/KDE. The &kdevelop; project therefore intends to provide an
50
<abbrev>IDE</abbrev> which enables developers to create and extend their
51
applications rapidly with the main focus on KDE.</para><para>As beginners always have
52
difficulties getting started, this handbook tries to explain certain aspects of
53
application creation for KDE, by describing how to create running applications
54
with the sample code included with the Qt library, using the &kdevelop;
55
<abbrev>IDE</abbrev> as well as creating KDE applications by examples.</para>
57
<para>The next chapter will explain to you how to create a project with
58
&kdevelop; that is already a complete application. Then we'll walk through the Qt
59
online documentation, containing the sources for examples on how to use the Qt
62
<para>This will give you a first understanding of how the underlying Qt library
63
functions, and how to use the classes provided with it. Qt is our starting
64
point, as KDE is on top of the Qt library classes, and KDE applications make a
65
wide use of the QT classes.</para>
67
<para>Later, we will cover how to create a KDE application with &kdevelop;'s
68
application wizard. From the minimum framework provided, we will create a
69
running example KDE application that makes use of the KDE libraries, and will
70
explain the different parts of a KDE application in detail.</para>
72
<para>The &kdevelop; Team wishes you success in learing about KDE and Qt with
73
this handbook, and hope that it will enable you to become another programmer
74
contributing to the KDE project.</para>
76
<sect1 id="what-you-should-know-already">
77
<title>What you should know already</title>
79
<para>This handbook requires a base knowledge about the C++ programming language
80
as a minimum to understand the code examples and their functionality. Further,
81
it is assumed that you have read <ulink url="index.html">The User Manual to
82
&kdevelop;</ulink> as well as <ulink url="programming.html">The &kdevelop
83
Programming Handbook</ulink> and know how the &kdevelop <abbrev>IDE</abbrev>
84
works in general, and what generated projects are.</para>
88
<chapter id="getting-started">
89
<title>Getting Started</title>
91
<para>Before you start the tutorial, you should have set up &kdevelop; to work correctly. Ensure you have
92
access to the Qt online reference documentation in the <guimenu>"Help"</guimenu>
93
menu or the corresponding book in the documentation tree. When looking at the
94
first page of the Ot documentation, you will see that it contains a link to a
95
section "Tutorials" and "Examples". Under "Tutorials", you will find a 14-step
96
introduction into using the Qt library to build applications.
97
"Examples" will lead you to a list of example programs that Qt includes, and
98
show for example, usage reference in certain classes. Now, if you are an
99
absolute beginner, you should make yourself comfortable with the idea behind Qt - providing a
100
library that offers:</para>
104
<para>a base application class (<classname>QApplication</classname>)</para>
107
<para>a widget library for graphical user interfaces</para>
110
<para>a set of additional helper classes for graphics, file, and data handling</para>
113
<para>the signal-slot mechanism for object communication</para>
116
<para>event controlling through event loops and virtual methods</para>
120
<para>If the above list doesn't make sense to you right now, you should read about the Qt
121
library in <ulink url="programming-2.html#ss2.1">The &kdevelop; Programming
122
Handbook</ulink> where the basics for a Qt application are described in
123
detail. There, you will also find an introduction about the difference between
124
a Qt and a KDE application, which is not covered much at the first level of the
125
Qt tutorial step.</para>
127
<para>To learn about Qt first is a good start for programming KDE
128
applications as well, so we will create a project that will produce the tutorial
129
applications after editing.</para>
131
<para>To do this, start with opening &kdevelop; and select
132
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"New"</guimenuitem></menuchoice>
133
from the menubar. The application wizard opens and you have to select which type
134
of project you want to build. For Qt applications, you would choose the Qt
135
application, but we will use the mini-KDE project to build our examples. The
136
reason is simple: the Qt tutorials don't contain much code, and by choosing the
137
Qt application in the application wizard, we would have to do a lot of changing
138
in the provided code, to create "true" examples.</para>
140
<para>But as the mini-KDE application only consists of an empty main window but
141
also checks for the Qt library and header files, it will fit for our purposes
142
and doesn't require much change.</para>
144
<para>When entering the next wizard page, we have to enter the project-specific
145
information. There, we can leave out any KDE-specific additions such as a
146
mini-icon and a main icon, documentation etc. We just fill in the information
147
that will be useful for building a suitable base for the tutorials. As the
148
structure of the tutorial steps builds the next step on the previously created
149
code, we can start with "MyWidget" as the project name. This will already build
150
us the base class that is needed in chapter 4, so we don't have to take care of
151
that later. As we want to start with the first tutorial step, we will use '1' as
152
the project number, which we can increase later when moving to the next
155
<para>Then, fill in the other required information such as your name, email
156
address, and project directory. That would be all we need, and we can flip to
157
the next page. Insert your header for the project's header file here or use the
158
given example. The same is done with the fourth page, where the same has to be
159
set for the source files. Finally, the last page is entered, where you have to
160
press <guibutton>"Create"</guibutton> to build the project. If the button is not
161
available, start at the first page again and make sure you didn't miss inserting
162
any required information.</para>
164
<para>If everything went OK, the project has been build and the messages window
165
in the application wizard will have "READY" at it's last line. Select
166
<guibutton>"Close"</guibutton> to finish the wizard.</para>
168
<para>When looking at the treeview on the left, you will see that the project
169
alreay includes a class <classname>"MyWidget"</classname> - this is because we
170
chose this to be the application name. Further, when opening
171
<guilabel>"Functions"</guilabel> in the classviewer's
172
<guilabel>"Globals"</guilabel> folder, you will see that the application already
173
contains a <function>main()</function> function where the execution will
176
<para>Now the application is already set up. You could do an
177
<guimenuitem>"Execute"</guimenuitem> or <guimenuitem>"Make"</guimenuitem>, but
178
as we want to create the tutorial applications, we can go ahead and change the
179
code first to create our tutorial examples, which we'll do in the next
183
<chapter id="building-the-qt-tutorials">
184
<title>Building the Qt Tutorials</title>
186
<sect1 id="step-one-hello-world">
187
<title>Step One: Hello World!</title>
189
<para>As the previous chapter explains, we created a project called "MyWidget".
190
Now, when looking at the first tutorial page of the Qt documentation, you will
191
see that the first application only uses a <function>main()</function>
192
function. Therefore, we only have to change the given <function>main()</function>
193
function of our generated project to get the first tutorial step running.</para>
195
<para>To access the <function>main()</function> function, select it from the
196
classviewer's <guilabel>"Globals"</guilabel> folder, which contains a subfolder
197
for <guilabel>"Functions"</guilabel> where all non-class functions are
198
listed. This will open the file <filename>main.cpp</filename> in the
199
<guilabel>"C/C++ Files"</guilabel> window and the editing cursor is already set
200
in front of the first line of <function>main()</function>.</para>
202
<sect2 id="what-to-change-first">
203
<title>What to change first</title>
205
<para>We could start changing the given code towards the desired result which is
206
listed in the tutorial, but it can be even easier. Select the contents of the
207
<function>main()</function> function with the mouse, or by holding the
208
<keycombo><keycap>Shift</keycap></keycombo> key pressed while selecting the
209
lines with the <keycombo><keycap>down-arrow</keycap></keycombo> key. Then select
210
<guimenuitem>"Cut"</guimenuitem> from the <guimenu>"Edit"</guimenu> menu or
211
press the <keycombo><keycap>DEL</keycap></keycombo> key to remove the selected
212
code. This will clear the main function and we can start from scratch to build
213
our first tutorial.</para>
217
<sect2 id="entering-the-tutorial-code">
218
<title>Entering the tutorial code</title>
220
<para>Now, to insert the code from the tutorial, you could first read it
221
carefully and try to remember which line contained which code. Then,
222
after reading the tutorial's line-by-line description, you could use it as an
223
example to write the tutorial by yourself.</para>
225
<para>Those who want to go the easy way: select the contents of the listed
226
<function>main()</function> function in the documentation browser and press the
227
<guiicon>"Copy"</guiicon> icon from the toolbar. Optionally, you could use the
228
context-menu or the according entry in the <guimenu>"Edit"</guimenu> menu. This
229
will copy the selection into the clipboard and, after switching back to the
230
sourcecode window, can be inserted with <guimenuitem>"Paste"</guimenuitem>
231
(either from the context menu, the toolbar icon, or the according entry in the
232
<guimenu>"Edit"</guimenu> menu).</para>
234
<para>Finally, add the according header files outside the
235
<function>main()</function> function:</para>
238
#include <qapplication.h>
239
#include <qpushbutton.h>
242
<para>That's it! The source code is done for your first tutorial
247
<sect2 id="building-hello-world">
248
<title>Building Hello World!</title>
250
<para>To build the first tutorial, select <guimenuitem>"Make"</guimenuitem> from
251
the <guimenu>"Build"</guimenu> menu or press the corresponding toolbar
252
icon. Optionally, you could choose <guimenuitem>"Execute"</guimenuitem> as well,
253
which results in running the application after the build was successful.</para>
255
<para>If the build stops due to an error, be assured the fault was on the
256
programmer's side, not on the tutorial! It would be sad to have to
257
search for an error, just when you decided to program for KDE and/or Qt, but you
258
can use this to train yourself to use &kdevelop;.</para>
260
<para> To find an error, you have two options: either select the according error
261
message in the output window, which will bring you to the line the error
262
occured, or press the <keycap>F4</keycap> key. If there is
263
more than one error, you could use the <guimenuitem>"Next Error"</guimenuitem>
264
entry in the <guimenu>"View"</guimenu> menu.</para>
268
<sect2 id="exercises">
269
<title>Exercises</title>
271
<para>You may ask "Exercises? This one was so simple, I don't need to
272
excercise!", but be assured, things will get more complicated. This one was the
273
easiest example, and the more complex they get, the more thinking is asked
274
on your side, to understand and follow the tutorial steps. Also the excercises
275
will enable you to learn how to actually use &kdevelop; and it's
278
<para>To teach yourself more with the first tutorial, you should try to do the
279
following changes to the given code and see how this will influence the
280
application behaviour:</para>
283
<listitem><para>change the pushbutton to a
284
<classname>QLabel</classname>.</para></listitem> <listitem><para>use
285
<function>setText()</function> instead of directly setting the text in the
286
pushbutton's constructor</para></listitem> <listitem><para>use
287
<literal>QPushButton::resize ( const QSize & s )</literal> instead of setting
288
the size by width and height directly. Create a <classname>QSize</classname>
289
instance first that contains the size</para></listitem> <listitem><para>play
290
with the sizes to see which is width and which is height</para></listitem>
296
<sect1 id="step-2-and-3-extensions">
297
<title>Step 2 and 3: Extensions</title>
299
<para>The tutorial steps 2 and 3 are extending the facilities of the first
300
tutorial. Here, you will learn about signals and slots, as well as parent-child
301
relationships between widgets.</para>
303
<para>Why is it important to learn about that? Well, the signals and slots
304
are building the technology which make the difference when comparing the Qt
305
library to other widget library. Whenever you will hit an implementation
306
problem where you want to communicate between objects, this will make your work
307
incredibly short, and nothing serious will happen if something went wrong, even
308
misconnections do not lead to a segmentation fault. This is the strength of the
309
Qt library and a lot of KDE/Qt developers will not want to miss it's
310
it's flexibility. Understanding the signal/slot mechanism therefore,
311
is essential to create any KDE/Qt application.</para>
313
<para>Another thing to watch out for is the fact that the button terminates the <classname>QApplication</classname> instance by calling <function>quit()</function>. You will meet <function>quit()</function> again when working with <classname>QApplication</classname>'s KDE-pendant, <application>KApplication</application></para>
316
<sect1 id="writing-your-own-widgets">
317
<title>Writing your own Widgets</title>
319
<para>Now, after having successfully modified and extended the
320
<function>main()</function> function, you will hit the point where you need to
321
create a new widget. A unique widget, that only can be constructed with the class
322
you're about to write in step 4. Actually, the class has been created by the
323
application wizard for you, so there is not much to add. You have to insert the
324
code that lesson 4 places in the <filename>main.cpp</filename> file into the
325
<classname>MyWidget</classname> classes' header and implementation
326
file. Fortunately, you only have to add the implementation to the constructor
329
<para>To access the constructor, select it from the classviewer. This will
330
automatically bring you to the implementation where you have to add the
333
<para>Here, you only have to watch out for the include of
334
<filename>qfont.h</filename>. <classname>QFont</classname> is only used in the
335
constructor code of <classname>MyWidget</classname>. Therefore, add
336
<userinput>#include <qfont.h></userinput> to the top of the
337
<filename>mywidget.cpp</filename> file.</para>
339
<para>To run the new application, just hit
340
<guimenuitem>"Execute"</guimenuitem>. This will result in compling the changes
341
after an autosaving.</para>
344
<sect1 id="extending-the-widget-class">
345
<title>Extending the Widget Class</title>
347
<para>Now, in lesson 5 the target is to extend the widget class, and you will
348
learn about virtual functions as well. Here, the
349
<function>resizeEvent()</function> is reimplemented. What is important to learn
350
here (besides extending a class,) is that Qt works with user events by virtual
351
functions that take an event as a parameter. Therefore, you should make yourself
352
comfortable with the following classes of the Qt library:</para>
356
<para><classname>QEvent</classname></para>
359
<para><classname>QChildEvent</classname></para>
362
<para><classname>QCloseEvent</classname></para>
365
<para><classname>QFocusEvent</classname></para>
368
<para><classname>QKeyEvent</classname></para>
371
<para><classname>QMouseEvent</classname></para>
374
<para><classname>QMoveEvent</classname></para>
377
<para><classname>QPaintEvent</classname></para>
380
<para><classname>QResizeEvent</classname></para>
383
<para><classname>QTimerEvent</classname></para>
388
<para>Whenever writing custom widgets, especially view areas of applications,
389
you will have to overwrite the default implementation of the widget's
390
event-methods with your own for certain events that have to be processed. Those
391
would be <abbrev>e.g.</abbrev></para>
393
<para><function><type>virtual void </type> mousePressEvent ( <type>QMouseEvent *</type>
394
)</function></para> </informalexample> <para> for processing a mouse event to pop
395
up a context menu. In the implementation, you have to insert a formal parameter
396
into the function header, most developers either use
397
<parameter>event</parameter> or just <parameter>e</parameter> as the parameter
400
<para>Then, you can process the event's parameters. For a
401
<classname>QMouseEvent</classname>, you have to ask if the
402
<mousebutton>right</mousebutton>, <mousebutton>middle</mousebutton> or
403
<mousebutton>left</mousebutton> mouse button was the one that caused the
407
<sect1 id="adding-a-new-class">
408
<title>Adding a new Class</title>
410
<para>In lesson 6, you will have to add a class to the tutorial
411
application. Now, normally you would think "I just will create a header file and
412
an implementation file, then I'm done". &kdevelop; will make it easier for you!
413
When adding a class, you should always use the Classgenerator. This will do all
414
the work for you, so you just will have to add the specific code.</para>
416
<para>To add a class with the Classgenerator, choose
417
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"New
418
Class"</guimenuitem></menuchoice>, which will open a dialog to enter all needed
419
values for the class you want to add. The first thing we have to insert is the
420
classname. The tutorial name this <classname>LCDRange</classname>, so this has
421
to be inserted first. Then proceed to the baseclass. The baseclass is the one
422
that the new class inherits. Looking at the tutorial (Qt 1.42), this is
423
<classname>QWidget</classname>. Now, as most <abbrev>GUI</abbrev> classes to
424
add would inherit <classname>QWidget</classname>, the Classgenerator makes it
425
even easier. Leave the baseclass empty, and instead check <guilabel>"QWidget
426
child class"</guilabel> in the <guilabel>"Additional Options"</guilabel>
427
section. This will automatically add the required <literal remap="tt">Q_OBJECT</literal> macro to the headerfile, to add slots and signals
428
later (which is required in <link linkend="step-7-14">chapter 7</link>).</para>
430
<para>As the filenames are automatically inserted, you don't have to take care
431
of that. The only thing that we would suggest adding is the documentation. It is
432
always a good style to add a desciptive documentation to the class, especially
433
as the classname <classname>LCDRange</classname> doesn't inform you specifically
434
about the purpose of the widget.</para>
437
<sect1 id="step-7-14">
438
<title>Step 7-14</title>
440
<para>For the rest of the tutorial steps, you are prepared and you already know
441
everything you have to know about adding the classes required, and doing the
444
<para>After each change you should do a new build, and check your code for
445
errors. Run the application, and follow it's execution. Additionally, you should
446
play with &kdevelop;'s options for <guimenu>"Build"</guimenu>. Try executing the
447
application with a commandline argument such as <option>--geometry</option>, and
448
debugging it with <application>KDbg</application> in the Tools-window.</para><para> Then you
449
should be able to proceed with the Qt examples that are provided with Qt.</para>
451
<para> In the next chapter, you will be introduced into development for KDE 2,
452
by an example application <application>KScribble</application>, which is a small
453
drawing application that will show you the concepts of application design, and
454
how to implement you own program.</para>
459
<chapter id="creating-kde-applications">
460
<title>Creating KDE Applications</title>
462
<para>This handbook will try to teach you KDE/Qt application design by creating
463
an example application step-by-step. The major intention is to gain a certain
464
understanding how two of the most important ideas of graphical user interface
465
programming work: the document-view model, and the multiple document interface
466
(<abbrev>MDI</abbrev>) model.</para>
468
<para>You will also get a good idea of concepts that are coming up ahead. These
469
include the KDE 2 and the Qt 2.1 libraries, bringing new enhancements and
470
technology to the programmer's hands, that will improve application design and
471
widen the possibilities that are open to use. Keywords such as "widget themes",
472
"Inter-Process Communication" and "embedded applications" are talked about and
473
will allow even more than you can imagine right now.</para>
475
<para>&kdevelop; 1.1, still based on KDE 1.1.x, provides developers some of the
476
most exciting possibilities, including the ability to begin developing
477
applications for KDE 2, while still running a stable desktop environment. For this reason,
478
this tutorial will be based on a KDE 2 application that
479
already makes use of Qt-2.1, offering a solution for the mentioned
480
<abbrev>MDI</abbrev> model, which we provide as a ready-to-go frame
481
application. During this tutorial you will get a clear insight on how it works
482
and this will also make it much easier to understand the document-view model,
483
which is hard to understand even for advanced programmers. That will help you
484
create other type of applications, which uses the document-view model, even if
485
they only contain one window and you can't see an obvious need for
488
<para>But let's just move on - you will see what's coming up on you and how
489
easy it actually is.</para>
491
<para>The upcoming chapters of the tutorial cover the following several
495
<listitem><para>installing KDE 2 (libraries) and setting up &kdevelop; to create
496
KDE 2 applications.</para></listitem>
497
<listitem><para>understanding the basic concepts of <abbrev>MDI</abbrev> and
498
document-view model.</para></listitem>
499
<listitem><para>creating the generic framework for
500
<application>KScribble</application>, and explaining the concept of the
501
framework.</para></listitem>
502
<listitem><para>implementing the document class, which will provide the
503
interface to read and write the document data, as well as maintaining the
504
document.</para></listitem>
505
<listitem><para>implementing the view class, to visualize the data. This class
506
also takes over the interaction with the user.</para></listitem>
507
<listitem><para>adding dialogs to set the pen width, and color of the pen to
508
draw with, including their integration into the toolbar and
509
menubar</para></listitem>
510
<listitem><para>changing the view from the default
511
<classname>QWidget</classname> inheritance to a scrollable
512
view.</para></listitem>
515
<para>The tutorial example itself is based on the code found in the scribble example application that comes with the Qt
516
library. The source of the original example can also be found within the Qt documentation, in the examples section.</para>
519
<chapter id="installing-kde-2">
520
<title>Installing KDE 2</title>
522
<para>In this chapter we will first go through a couple of steps that will help
523
you set up &kdevelop; for creating KDE 2 / Qt 2.x applications. The requirements
528
<para>a current Qt-2.1 snapshot, available at <ulink url="http://www.troll.no">http://www.troll.no</ulink></para>
531
<para>a current KDE 2 snapshot of the packages <literal>kdesupport</literal> and <literal> kdelibs</literal></para>
535
<para>Both the Qt-2.1 and KDE 2 libraries are currently under development but
536
are you may consider them close to final. Changes to be expected for the final
537
versions are minimal, and may not even affect your application. This means
538
you are getting to know the newest libraries available - and you have got a
539
valuable time-saving, enabling you to create your application even before the libraries are
542
<para>The following sections describe what to do in detail within three steps:</para>
545
<step><para>installing the Qt library</para></step>
546
<step><para>installing the KDE libraries</para></step>
547
<step><para>setting up &kdevelop;</para></step>
550
<para>Then you are ready to go, and we will proceed with creating the first step
551
of the example program <application>KScribble</application> in the next
554
<sect1 id="setting-up-qt-2.1">
555
<title>Setting up Qt-2.1</title>
557
<para>This first step will give you the minimum requirement to follow our
558
example. There is a possibility to avoid installing KDE 2 if
559
you're only interested in creating Qt-only programs. As Qt-2.1 contains all
560
needed classes, and &kdevelop; provides a project template for multiple document
561
interface applications for Qt-2.1, (as well based on the same framecode that is
562
used in the KDE 2 template), you should have no problem at all following the
563
example with a Qt-only application.</para>
565
<para>To get the newest Qt library, get a recent version from the Troll
566
Tech website at <ulink url="http://www.troll.no">http://www.troll.no</ulink>,
567
in the download section. Then change user to root, and copy the tarball to the
568
directory where your currently used Qt version is placed as well - on a SuSE
569
Linux system this would be <filename>/usr/lib</filename>, where you should
570
have a directory for either qt-1.44 or qt-2.0.2, depending on the distribution
571
version. Untar the library sources with</para>
573
<informalexample><screen><command>tar zxvf <replaceable>qtxxx.tar.gz</replaceable></command></screen></informalexample>
575
<para>depending on the name of the tarball. Then set the environment variable
576
<envar>QTDIR</envar> to point to the directory where your new Qt-2.1 library is
577
placed with <abbrev>e.g.</abbrev></para>
578
<informalexample><screen><command>setenv <envar>QTDIR</envar>=<replaceable>/usr/lib/qt-2.1</replaceable></command></screen></informalexample>
580
<para> Another solution would be to move the qt-1.44 directory, usually just
581
named <filename>qt</filename>, to <filename>qt-1.44</filename> and create a
582
symlink that for now has to point to <filename>qt-2.1</filename>. After the
583
library is built, change it back to point to the
584
<filename>qt-1.44</filename>directory.</para>
586
<para>Change to the Qt-2.1 directory and type:</para>
588
<screen><command>./configure</command>
589
<command>make</command>
592
<para>You can now change back your <envar>QTDIR</envar> variable to point to
593
your qt-1.44 installation, after successfully compiling the Qt-2.1
596
<para>Now switch to the directory <filename>/usr/lib/qt-2.1/lib</filename> and
597
copy the created libraries to <filename>/usr/lib</filename>
598
<emphasis>exept</emphasis> the symbolic link
599
<filename>libqt.so</filename>.</para>
601
<para>Then you are done with Qt, and you can proceed to the next step, setting
602
up KDE 2 libraries. If you want to develop for Qt only, proceed directly to the
603
section <link linkend="setting-up-kdevelop">Setting up &kdevelop;</link>.</para>
606
<sect1 id="setting-up-kde-2-libraries">
607
<title>Setting up KDE 2 Libraries</title>
609
<para>The second step leads you through installing the needed KDE
610
libraries. Download a recent snapshot of the library packages <literal>kdesupport</literal> and <literal>kdelibs</literal> from
611
<ulink url="http://www.kde.org/">http://www.kde.org/</ulink>. Then things will
612
go a bit easier than for the Qt library. Just untar the sources to your
613
directory, and change to the new kdesupport directory first. There, type:</para>
615
<informalexample><screen>
616
<command>./configure <option>--with-qt-dir=<replaceable>/usr/lib/qt-2.1</replaceable> --prefix=<replaceable>/opt/kde2</replaceable></option></command>
617
<command>make</command>
621
<command>make install</command>
622
</screen></informalexample>
624
<para>This will install the kdesupport package to the directory given in the
625
<option>--prefix</option> option and compile using the new build Qt-2.1 library,
626
to which you give the path to configure by the option
627
<option>--with-qt-dir</option>.</para>
629
<para>The same applies to the kdelibs package, just change to the kdelibs
630
directory, and type the same as you did above for the kdesupport package.</para>
632
<para>You are now done with setting up the requirements for the required
633
libraries to build a KDE 2 application.</para>
635
<note><para>As a note aside, the KDE library package contains support for
636
automatic <abbrev>HTML</abbrev> documentation generation, which you can easily
637
get by installing <application>KDoc</application>, version 2.x. Then type
638
<command>makekdedoc</command> in your kdelibs directory and
639
<application>KDoc</application> will create the <abbrev>HTML</abbrev>
640
documentation in a subdirectory <filename>srcdoc</filename>. In &kdevelop; you
641
can access the KDE library documentation online with setting the KDE library
642
documentation path in the &kdevelop; Setup dialog to point to
643
<filename>kdelibs/srcdoc</filename>. In the same way, set your Qt-Online
644
documentation path in &kdevelop; to point to
645
<filename>/usr/lib/qt-2.1/html</filename>, so you can access the documentation
646
right away from within &kdevelop;.</para></note>
648
<para>If you successfully installed the Qt-2.1 and KDE 2 libraries, you're just
649
one step away from creating your first KDE 2 application. You just need to give
650
&kdevelop; the information as to where you installed the libraries - which we'll
651
do in the next section.</para>
654
<sect1 id="setting-up-kdevelop">
655
<title>Setting up KDevelop</title>
657
<para>This part is somewhat the easiest in the whole configuration process for
658
KDE 2 development: Setting up &kdevelop;. Just open &kdevelop;, then select
659
<menuchoice><guimenu>"Options"</guimenu><guimenuitem>"KDevelop
660
Setup"</guimenuitem></menuchoice> to access the &kdevelop; configuration. There,
661
change to the last page, titled <guilabel>"Path"</guilabel>. This page contains
662
two entry fields, one for the Qt library, and one for the KDE 2 libraries. As
663
you have successfully installed everything, select
664
<filename>/usr/lib/qt-2.1</filename> for the Qt-2.x library path and
665
<filename>/opt/kde2</filename> for the KDE 2 library path. Press the
666
<guibutton>"OK"</guibutton> button and you're done.</para>
668
<para>Users that only want to develop for Qt-2.x (including the current Qt-2.0.2
669
version) just have to set the Qt-2.x library path.</para>
671
<para>Now we can proceed to the next chapter where we will create the first step
672
of the <application>KScribble</application> example.</para>
676
<chapter id="application-concepts">
677
<title>Application Concepts</title>
679
<para>This chapter now introduces you into the ideas of the application models
680
mentioned already: the <abbrev>MDI</abbrev> (Multiple Document Interface) and
681
the Document-View model. A basic introduction into the Document-View model and
682
the generating of a project with &kdevelop; is already given in <ulink url="../programming/index.html">The &kdevelop; Programming Handbook</ulink>, but
683
based on a Single Document Interface (<abbrev>SDI</abbrev>). In any case, you
684
should be familiar with the basics of the KDE and Qt classes that are explained
685
in <ulink url="../kde_libref/index.html">The KDE Library Reference
686
Guide</ulink>. There, the base classes of the underlying libraries are explained
687
in detail, and how to use them, with an additional description of the Qt
688
signal-slot mechanism and event handling.</para>
690
<para>As a guideline, you should know already:</para>
693
<para>that every KDE application needs one instance of
694
<classname>KApplication</classname> (for a Qt application one instance of
695
<classname>QApplication</classname>)</para>
698
<para>generally a main window is inherited from
699
<classname>KTMainWindow</classname> for KDE (for a Qt application inherited from
700
<classname>QMainWindow</classname>)</para>
703
<para>that events (<abbrev>e.g.</abbrev>, keyboard presses or mouse movements)
704
are sent to the application by the underlying window system</para>
707
<para>that <application>QWidget</application> as the base class for all visible
708
Graphical User Interface (<abbrev>GUI</abbrev>) components provides specialized
709
event handlers by virtual functions that filter out the according events by
710
reimplementing <literal remap="bf">QObject::event()</literal></para>
713
<para>that the Qt signal-slot mechanism means basically that a component can
714
send out signals and a receiver can connect to those signals to process some
718
<para>that this mechanism avoids inheriting classes for <abbrev>GUI</abbrev>
719
components because the signals inform you about <abbrev>e.g.</abbrev> mouse
720
presses where appropriate for a wiget</para>
724
<sect1 id="the-document-view-model">
725
<title>The Document-View Model</title>
727
<para>The Document-View Model is one of the most basic concepts in the design of
728
applications that rely on graphical user interfaces. Therefore, a certain
729
understanding of the why this is, helps you understand that although the
730
programmer has other possibilities, it makes sense to make use of the
731
Document-View Model.</para>
733
<para> But first let's follow the usual design of a typical KDE / Qt
736
<para>Your application instance provides the first connection to your
737
application and is the starting point of the event handling for a program. The
738
program itself represents itself to the user by a Graphical User Interface which
739
is most often called a main-window. The main window then provides the
740
appropriate functions for the user such as key accelerators, a menubar, toolbar
741
and a statusbar. In the center, it contains a so-called "view-area" which means
742
that this part is an instance of another class, usually called a "View". The
743
view instance is created when the main window is constructed at program start,
744
and has to be set as the view area component with a method provided by the main
745
window: <function>setView(<symbol>your_view</symbol>)</function> for KDE applications using
746
<classname>KTMainWindow</classname>,
747
<function>setCentralWidget(<symbol>your_view</symbol>)</function> for a Qt application using
748
<classname>QMainWindow</classname>. Now, the view obviously is the area that is
749
responsible for interacting with the user to manipulate the data that it
750
represents. As an example, you could use a <classname>QMultiLineEdit</classname>
751
as a view and you will have an editor. Then you can use the provided slots of
752
the view for connections to menubar or toolbar command like this:</para>
754
<para>While creating the menubar, you want to provide a method for the command
755
<guimenuitem>"cut"</guimenuitem> in the <guimenu>"Edit"</guimenu> menu:</para>
759
pEditMenu->insertItem(BarIcon("editcut"), i18n("Cu&t"),view, SLOT(cut()),KAccel::Cut, ID_EDIT_CUT);
763
<para>This creates a menu item in the <guimenu>"Edit"</guimenu> menu, that, when
764
activated, directly calls the slot <function>cut()</function> of the instance
765
<literal remap="tt">view</literal>. We are assuming here, that you have created
766
this instance as a <classname>QMultiLineEdit</classname> and set it as the view
767
area. The Multiline-edit's slot is called, and cuts out the selected text as a
768
result. The functionality is already provided by the class itself, and so there
769
is no need to inherit from <classname>QMultiLineEdit</classname> to create a
770
view area that is capable of such actions. They are ready to use, and make
771
application development very fast- you just need the application instance and
772
the main window, including the connections to your view area, and you're
775
<para>That means, a simple editor can be written by creating one single class
776
that defines the main window behavoir and how to save and read files into the
777
editor - just some basic slots your main view has to implement itself.</para>
779
<para>But here is the reason why we're now introducing this mysterious
780
Document-View model: You have to provide methods of your own, to read and write
781
the actual files you want to edit with the <classname>QMultiLineEdit</classname>
782
view-area <emphasis>within</emphasis> the main window's interface.
783
Yes, this is obviously the easiest thing to do in this case, and the most
786
<para>Now, if we have a look at the files and their contents as a so-called
787
"Document", which we can subscribe with the attribute of an "Object" in C++
788
terminology, the next step is just a little one: If I have a document, a view
789
and a main window, why don't I separate these three objects from each other? We
790
could easily create a small class that is responsible for reading in a file into
791
a text stream, and then call the view to draw the text visible to the user. The
792
same applies to saving the file again - the document class should then provide a
793
saving method that retrieves the text from the view again and saves it as a
794
file. In the example these two methods would be the only actions that are needed
795
to be done by a document class, because the edit-view already provides all
796
methods basically needed for and editor by slots and you can manipulate the
797
content of the view by them directly.</para>
799
<para>The main idea behind the need for this separation into three objects
800
(document, view, main window) instead of the minimum of two, the view and the
801
main window, is this: what if I want to give the user the possibility to work
802
with a file in <emphasis>two</emphasis> or even more views? Such things can
803
even be done within one main window by splitters or deviders, containing two
804
view instances which shall both display <emphasis>one</emphasis> file. The
805
solution can only be that if the user manipulates the file contents in one view,
806
the other view has to be notified about that and to actualize its
807
contents. Otherwise a bad scenario will occur: if the user closes one view where
808
he added something at the end of the file, which he had cut out at the beginning
809
of the file <emphasis>in</emphasis> the other view, the file will be saved later
810
still containing the cut section, because if the second and last view did not
811
recognize this, it still contains the text without modification from the
812
beginning, plus the inserted one. This means that both views have to be
813
synchronized in the contents they are displaying, and that can be done if all
814
views get notified about each action that the user does, which view he actually
815
uses should be irrelevant. So finally a document class is needed to be the one
816
and only holder of the true contents of the document and is providing the views
817
the possibility to manipulate the contents.</para>
819
<para>I hope this has given some insight into this model, although in most cases
820
it seems that the programmer can live without it - equally by just using a
821
provided class as the view area, or writing a widget on his own to handle user
822
interaction, as long as you represent <emphasis>one</emphasis>
823
document or file by <emphasis>one</emphasis> view area, the view can
824
be responsible for the data as well and only provide methods to retrieve or set
825
the document contents for actions like reading a file or saving it. The next
826
model to describe, the Multiple Document Interface, will make a difference at
827
this point- there you will see the actual need and the functionality the
828
Document-View model provides.</para>
831
<sect1 id="the-multiple-document-interface-mdi">
832
<title>The Multiple Document Interface (MDI)</title>
834
<para>As the last section described the Document-View model, you will probably
835
guess what <abbrev>MDI</abbrev> means. Users that come from other Operating
836
Systems than Unix/Linux are used to it, as are programmers developing for those
837
platforms. X-Window applications traditionally are more targeted towards
838
functionality and stability, Unix users are used to single windows that provide
839
functionality, therefore even the Document-View model is often not needed to
840
create applications. With Qt as a multi-platform toolkit, developers are having
841
even more choice - developing for <abbrev>MS</abbrev> Windows(tm) as well as for
842
Unix systems. While on Windows the difficult of creating applications that are
843
capable of handling so-called child windows has been getting to some kind of
844
standard, this is adressed by the Qt 2.1 library, but on the other hand Unix
845
users can profit from this architecture as well.</para>
847
<para>What is the meaning of <abbrev>MDI</abbrev>? An <abbrev>MDI</abbrev>
848
application generally has the same concept of a usual application that has, as
849
described above, one application instance and a main window. The view area now
850
make the difference: you don't directly use a view that represents data and
851
provides interaction to manipulate that data but a view that handles other
852
windows looking like top-level windows. These windows are now representing the
853
former view area and the main difference is that the interaction chain changes
857
<para>application instance -> main window -> view</para>
861
<para>application instance -> main window -> view -> active child window</para>
864
<para>The view now is capable of several actions:</para>
867
<para>creating as many child windows as the user requests,</para>
870
<para>providing methods to retrieve the currently active child window,</para>
873
<para>maintaining a list of open views,</para>
876
<para>managing the child window behaviour for maximize, minimize, as a window manager does for top-level windows.</para>
880
<para>Now, you can use "complete" widgets like the
881
<classname>QMultiLineEdit</classname> as child windows, for example for an
882
application that only provides one window and each child window is responsible
883
for it's own data. That could be described as "Multiple Document Interface",
884
whereby each child window is equal to a single document. The application then
885
manages the usual actions such as providing methods for the child window
886
interaction such as cut or copy.</para>
888
<para>Extending this concept with the document-view model enhances the
889
possibilities by far: imagine that you can open as many windows as you like
890
within the main window, and that a new child window can be a new view of a
891
document that is already shown by another child window. The management for this
892
requires a separation into the already described three objects model, but
893
doesn't limit the actual number of instances of the documents as well as the
896
<para>Fortunately, Qt 2.1 contains the possibility to create such applications,
897
and &kdevelop; provides you with the corresponding application frameworks for
898
both Qt-only programs, as well as KDE 2 applications, both with the same
899
interface methods. So &kdevelop; is appropriate wether you want to develop for
900
either one of these. Using KDE 2 interfaces will offer you still more
901
possibilites through library functions as well as inter-process communication,
902
but those are special aspects that we are to cover by separate introductions to
903
these techniques.</para>
905
<para>Now you are prepared for following the development for KDE 2 - just follow
906
the next chapter to get a first look at the functionality already provided by
907
&kdevelop; when creating applications. There, we will generate the framework for
908
our tutorial application <application>KScribble</application> and describe the
909
practical aspects of programming <abbrev>MDI</abbrev> applications.</para>
914
<chapter id="the-mdi-framework">
915
<title>The MDI Framework</title>
917
<para>If you're already familiar with previous versions of &kdevelop;, you will
918
know that your <abbrev>IDE</abbrev> uses so-called "frameworks" as a starting
919
point of application development. These frameworks by default contain a support
920
for a generic document-view model, but were limited to <abbrev>SDI</abbrev>
921
(Single Document Interface, one window with one view area) model
922
structures. From &kdevelop; 1.1 on, the application wizard is capable of a lot
923
more than the previous versions. It now offers some new project types especially
924
for those developers that want to stay up-to-date with the upcoming KDE 2, as
925
well as for Qt 2.1.</para>
927
<para>One major extension are <abbrev>MDI</abbrev> application frameworks for
928
both, KDE 2 and Qt 2.1. These frameworks differ only in the use of some library
929
functions such as file dialogs or message boxes as well as the used toolbar and
930
statusbar classes - the KDE version uses KDE 2 methods, the Qt version the
931
according Qt functions.</para>
933
<para>To create the fist step of this tutorial application
934
<application>KScribble</application>, be sure that you have followed the
935
instructions in chapter 2 of this tutorial handbook: setting up KDE 2. If that
936
went all right, nothing can go wrong. If you couldn't set up the KDE 2
937
libraries, be sure that you have Qt-2.1 installed and set up &kdevelop; for that
938
at least. You can follow this tutorial by choosing the according Qt framework as
939
well (which has the differences as mentioned above: other method calls for
940
dialogs and <abbrev>GUI</abbrev> components).</para>
942
<sect1 id="creating-an-mdi-framework">
943
<title>Creating an <abbrev>MDI</abbrev> Framework</title>
945
<para>Now we will start jumping into development by creating the frame
946
application for <application>KScribble</application>.</para>
948
<para> Start &kdevelop; and choose
949
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"New"</guimenuitem></menuchoice>
950
from the menubar, to invoke the application wizard. On the first page, you will
951
be shown a tree of project types. These contain KDE and Qt projects. There,
952
select <guilabel>"KDE 2 MDI"</guilabel> from the KDE section. If you only have
953
Qt 2.1 installed, choose <guilabel>"Qt 2.1 MDI"</guilabel> from the Qt
954
section. Then select <guibutton>"Next"</guibutton>, enter "KScribble" as project
955
name and "1.0" as version number. Add your name and email address into the
956
according fields and you're done. Click on the <guibutton>"Create"</guibutton>
957
button and &kdevelop; will generate our first version of
958
<application>KScribble</application>, while you can watch the output in the last
959
page of the wizard. Finally, press <guibutton>"Exit"</guibutton> to return to
960
&kdevelop;. In the output window you will see an additional message showing that
961
the messages file has been set up for your project, which will be important to
962
translate <application>KScribble</application> into other languages.</para>
966
<sect1 id="overview">
967
<title>Overview</title>
969
<para>It is important to have an understanding "where to find what" in the
970
generated sources, and where your place as a programmer can be found to make
971
<application>KScribble</application> a unique KDE application.</para>
973
<para>First of all, in the Classviewer you will see three already created
974
classes, <classname>KScribbleApp</classname>,
975
<classname>KScribbleDoc</classname> and <classname>KScribbleView</classname>. As
976
explained above, each of them has a certain part within the application; the App
977
class builds the main window and coordinates user interaction, the Doc class
978
maintains the documents that an application can manipulate, and finally the View
979
class is responsible for user interaction with the child windows and
980
communicates with the document connected to it. This has several
981
consequences. To make a good use of the provided functionality, the programmer
982
is bound to use the given View class. By default, the View inherits
983
<classname>QWidget</classname> as a minimum requirement, but you are not limited
984
to that at all. Inheriting from another class is not too much work and there is
985
also the possibility to use a <classname>QMainWindow</classname>, or
986
<classname>KTMainWindow</classname> for example, as the class to inherit and use
987
another class that manages the view.</para>
989
<para>In the next chapter we'll continue with extending
990
<application>KScribble</application> to manage it's documents. After that we'll
991
implement the user interaction and you will see the first functionality that is
992
unique to our application: we can draw pictures, load and save them.</para>
997
<chapter id="defining-the-documents">
998
<title>Defining the Documents</title>
1000
<para>The first step when creating an application based on the Document-View
1001
model should always be to think what kind of data the application has to
1002
manage. This decides how the view class will look, and especially how the
1003
document clas will read and write data to and from files, and offer methods to
1004
manipulate the data. As <application>KScribble</application> will be a simple
1005
drawing application that operates on graphical data, we will use the Qt class
1006
<classname>QPixmap</classname> for storing our paintings while it is
1009
<para><classname>QPixmap</classname> also offers simple methods to read
1010
and write pictures into files, so the serialization of the document data is done
1011
in just two lines, one for reading and one for writing. Further, we need to
1012
define a pen that draws into a document, set it's width and color and make it
1013
available for the view class to retrieve the pen. Actually you want the view
1014
offering the drawing methods, but the document as the central element for all
1015
views has to hold the pen originally, because two views of the same document
1016
would otherwise use different pens!</para>
1018
<para>Therefore to define how our document class should work, we will add one
1019
instance of <classname>QPixmap</classname>, one of <classname>QPen</classname>
1020
and edit the methods <function>newDocument()</function>,
1021
<function>openDocument()</function> and
1022
<function>saveDocument()</function>.</para>
1024
<sect1 id="adding-the-instances">
1025
<title>Adding the Instances</title>
1027
<para>Open the file <filename>kscribbledoc.h</filename>, by selecting it in one
1028
of the fileviewers, or by a click on the classviewer over the class
1029
<classname>KScribbleDoc</classname>.</para>
1031
<para>Then add the lines marked with -> from the following code snippet:</para>
1033
<programlisting>-> #include <qpixmap.h>
1034
-> #include <qpen.h>
1041
-> QPen currentPen(){ return pen;};
1043
-> int penWidth()
1044
-> { return pen.width(); }
1047
void updateAllViews(KScribbleView *sender);
1051
-> QPixmap buffer;
1055
/** the modified flag of the current document */
1056
bool modified;</programlisting>
1058
<para>As you see, we added pen and buffer, as well as
1059
<function>currentPen()</function> and <function>penWidth()</function>. As pen is
1060
declared private, we offer a possibility to retrive the pen as well as the pen
1061
width. As these are already implemented within the classdeclaration, we don't
1062
have to add them to the implementation file, where we're turning to now.</para>
1065
<sect1 id="initialization-of-the-document">
1066
<title>Initialization of the Document</title>
1068
<para>Select the method <function>newDocument()</function> in the
1069
<classname>KScribbleDoc</classname> class to jump to the method declaration. Here,
1070
we're only adding one line, marked with the arrow:</para>
1072
<programlisting> kscribbledoc.cpp
1074
bool KScribbleDoc::newDocument()
1076
/////////////////////////////////////////////////
1077
// TODO: Add your document initialization code here
1078
-> pen=QPen( Qt::black, 3 );
1079
/////////////////////////////////////////////////
1084
<para>This initializes the pen with the color black and width of 3 pixels; the
1085
<classname>QPen</classname> class has some more constructors, but this one suits
1086
our needs here.</para>
1089
<sect1 id="implementing-the-serialization">
1090
<title>Implementing the Serialization</title>
1092
<para>What is left to do is to define how to open and save our pictures. This is
1093
done in the according methods:</para>
1096
bool KScribbleDoc::openDocument(const QString &filename, const char *format /*=0*/)
1099
QFile f( filename );
1100
-> //if ( !f.open( IO_ReadOnly ) )
1101
-> // return false;
1102
/////////////////////////////////////////////////
1103
// TODO: Add your document opening code here
1104
-> if(!buffer.load( filename, "PNG" ))
1106
/////////////////////////////////////////////////
1110
bool KScribbleDoc::saveDocument(const QString &filename, const char *format /*=0*/)
1112
QFile f( filename );
1113
-> // if ( !f.open( IO_WriteOnly ) )
1114
-> // return false;
1116
/////////////////////////////////////////////////
1117
// TODO: Add your document saving code here
1118
-> if(!buffer.save( filename, "PNG" ))
1120
/////////////////////////////////////////////////
1125
<para>Add the lines marked with the arrow again to your code. What we did here
1126
is to comment out the passages where the file <filename>filename</filename> is
1127
opened, because that is done automatically by the load and save methods of
1128
<classname>QPixmap</classname>, which we add instead. Other documents may open a
1129
file and read in its contents such as text lines or whatever, so the
1130
<classname>QFile</classname> methods are already present in the codeframe. As
1131
<function>save()</function> and <function>load()</function> return a boolean
1132
value if the operation was successful, we're returning <varname>false</varname>
1133
if not, so the rest of the framework gets a return value and can give out
1134
warnings if the operation was not successful.</para>
1136
<para>The <function>load()</function> and <function>save()</function> methods
1137
now are already provided in <classname>QPixmap</classname>. They require the
1138
filename and the format as argument. The source framework on the other hand
1139
<emphasis>does not</emphasis> call the document methods with the format yet. If
1140
only one format is used, it sets the format here. Other methods could detect the
1141
format, but we will turn to this later. For now, we're using "PNG" as
1142
format. See <classname>QImageIO</classname> for more details about the image
1143
formats that can be opened.</para>
1145
<para>Now we're already finished defining our document structure. The
1146
<classname>QPixmap</classname> buffer serves us as a buffer, storing the
1147
original picture contents while we're working on it. The pen is a valid pen for
1148
all views connected to the document. Note that the initialization of the pen is
1149
done in <function>newDocument()</function>. This method is always called after
1150
the constructor within the framework internally, so you should add document
1151
instances initializations there as we did with the pen.</para>
1153
<para>In the next chapter we will turn to the view class to define how the view
1154
shall cooperate with the user as well as how it accesses the
1155
document - and then we'll be ready to paint!</para>
1159
<chapter id="defining-the-view">
1160
<title>Defining the View</title>
1162
<sect1 id="interactivity-with-the-user">
1163
<title>Interactivity with the User</title>
1165
<para>In this chapter we'll turn to the view class of
1166
<application>KScribble</application> to define how the child windows shall
1167
work. First of all, we notice that <classname>KScribbleView</classname> is
1168
derived from <classname>QWidget</classname> by default. That is the minimum
1169
requirement for a child window, but it already fulfils our needs.</para>
1171
<para>When it comes to defining a new
1172
widget's behaviour, we need to know how the user shall interact with the
1173
window. In our example, this would be obviously the mouse. Therefore, we have to
1174
overwrite some virtual methods from <classname>QWidget</classname> that process
1175
mouse events the widget receives. What we need is to know when the user presses
1176
a mouse button, because the drawing shall only take place
1177
when the mouse is pressed. Then we need to know when the mouse is moved (to know
1178
where it moves to) as well as when it is released-to
1179
finish the stroke the user has drawn. Further we want our picture to be painted
1180
on the window and resized if the user decides to resize
1181
the window he draws into. As members we will also add a
1182
<classname>QPointArray</classname> polyline and a boolean value
1183
<varname>mousePressed</varname>. Add the code with the
1184
arrow to your include file for the class <classname>KScribbleView</classname>:</para>
1186
<programlisting> kscribbleview.h
1188
-> #include <qpointarray.h>
1195
virtual void closeEvent(QCloseEvent* );
1197
-> virtual void mousePressEvent( QMouseEvent * );
1198
-> virtual void mouseReleaseEvent( QMouseEvent * );
1199
-> virtual void mouseMoveEvent( QMouseEvent * );
1200
-> virtual void resizeEvent( QResizeEvent * );
1201
-> virtual void paintEvent( QPaintEvent * );
1206
-> bool mousePressed;
1207
-> QPointArray polyline;
1212
<sect1 id="reimplementing-event-handlers">
1213
<title>Reimplementing Event Handlers</title>
1215
<para>Now we're coming to the actual implementation of the event handlers. As
1216
explained in <ulink url="../kde_libref/index.html">The KDE Library Reference
1217
Guide</ulink>, Qt has a good way of handling user events, especially when they
1218
target on to widgets. <classname>QWidget</classname> as a baseclass preselects
1219
the events, and provides basic event handlers which, as they are declared as
1220
virtual, we can overwrite to define how our widget shall react on user
1221
actions. One is already overwritten: the <function>closeEvent()</function>
1222
method. This is needed because our main window, represented in the App class,
1223
already preselects closing child windows and handles this. Therefore the default
1224
event handler, which just accepts the closing, must be overwritten to prevent
1225
that happening, and let the App class do the job.</para>
1227
<para>First of all, we have to declare the widget default behavoir in the
1228
constructor, by initializing members and setting predefined values:</para>
1230
<programlisting> kscribbleview.cpp
1233
KScribbleView::KScribbleView(KScribbleDoc* pDoc, QWidget *parent, const char* name, int wflags)
1234
: QWidget(parent, name, wflags)
1238
-> setBackgroundMode( QWidget::NoBackground );
1239
-> setCursor( Qt::crossCursor );
1240
-> mousePressed=false;
1241
-> polyline=QPointArray(3);
1244
<para>We're setting the background to <constant>NoBackground</constant>, a cursor
1245
<constant>(crossCursor)</constant>, and initialize <varname>mousePressed</varname>
1246
and <varname>polyline</varname>. Then we'll start implementing our first event
1247
handler, <function>mousePressEvent()</function>, to recognize when the user
1248
presses the mouse and where:</para>
1250
<note><para>Note: the following implementations have to be inserted completely, so the
1251
lines to add are not marked with an arrow!</para></note>
1253
<programlisting>void KScribbleView::mousePressEvent( QMouseEvent *e )
1255
mousePressed = TRUE;
1256
polyline[2] = polyline[1] = polyline[0] = e->pos();
1259
<para>Here, we're setting <varname>mousePressed</varname> to true, so we have
1260
stored this event somehow. The second line is not so obvious: we're storing the
1261
position where the mouse was pressed into our array's first three elements. As
1262
the array is a <classname>QPointArray</classname>, it can store values of the
1263
type <classname>QPoint</classname> (which contain an x and y value
1264
themselves). What we will do with this array is to store positions of the mouse
1265
and create the drawing routine from there in the mouseMoveEvent:</para>
1267
<programlisting>void KScribbleView::mouseMoveEvent( QMouseEvent *e )
1269
if ( mousePressed ) {
1272
painter.begin( &doc->buffer );
1273
painter.setPen( doc->currentPen() );
1274
polyline[2] = polyline[1];
1275
polyline[1] = polyline[0];
1276
polyline[0] = e->pos();
1277
painter.drawPolyline( polyline );
1280
QRect r = polyline.boundingRect();
1282
r.setLeft( r.left() - doc->penWidth() );
1283
r.setTop( r.top() - doc->penWidth() );
1284
r.setRight( r.right() + doc->penWidth() );
1285
r.setBottom( r.bottom() + doc->penWidth() );
1287
doc->setModified();
1288
bitBlt( this, r.x(), r.y(), &doc->buffer, r.x(), r.y(), r.width(), r.height() );
1292
<para>This event handler is probably the most difficult, so we will do a
1293
step-by-step walkthrough to understand what's been done.</para>
1295
<para>First of all, the event handler receives all mouse movements over the
1296
widget. But as we're only interested in the move if the mouse is pressed,
1297
because that is the time to draw, we have to ask if mousePressed is true. That
1298
has been done by the <function>mousePressEvent()</function> event handler before,
1299
so we don't have to take care of it anymore.</para>
1301
<para> Now we're starting the painting action. First we create a
1302
<classname>QPainter</classname> and let it draw into the buffer of the
1303
document. This is important, because the document's buffer contains the real
1304
contents, the view only acts as a communicator between the document and the
1305
user. We get the pen from the document instance as well by calling
1306
<function>currentPen()</function>.</para>
1308
<para> The next three lines assign the values inside the polyline
1309
<classname>QPoint</classname> array, setting point 2 to 1, 1 to 0 and 0 to the
1310
point to where the move went (this is the contents of the event we're interested
1311
in). Assuming we've just pressed the mouse (so all three values of the array
1312
contain the pressing position) and the first mouse move event appears that
1313
contains the first position to draw a line to; this value is moved into the
1314
first position in the array again. You may wonder why we need three points in
1315
the array then, if we're only interested to draw a line from one position to the
1318
<para> The following lines explain that: after drawing into our buffer is
1319
finished (with <function>drawPolyline()</function> and
1320
<function>painter.end()</function>), we create a rectangle <literal>r</literal>
1321
and use <function>boundingRect()</function> from
1322
<classname>QPointArray</classname> to get a <classname>QRect</classname> that
1323
contains all three points. Therefore we need three values to have a
1324
most-complete rectangle.</para>
1326
<para> Then we use <function>normalize()</function> to have the leftmost and
1327
topmost values the smallest (as coordinates are counted from top->bottom and
1328
left->right). The next thing to do is adapt the size of the rectangle by the
1329
size of the pen, because the pen has a thickness we get with
1330
<function>penWidth()</function> and widen the rectangle by the width of the
1331
pen. (Imagine the mouse movement was only two pixels away but the pen thickness
1332
is set to ten- then the rectangle wouldn't contain the whole painted
1335
<para>Finally, we set the document modified, and use the
1336
<function>bitBlt()</function> function to copy the rectangle out of the buffer
1337
into the widget. <function>bitBlt</function> operates bitwise, and is very fast,
1338
so that it is a good method to copy the painted area from the buffer on the
1339
widget instead of repainting the whole window. It's arguments are: first the
1340
object to draw to (the destination), here it is our widget, so we have to use
1341
the pointer <symbol>this</symbol>.</para>
1343
<para>The next two arguments give the destination topleft position to start
1344
copying to, then follows the source to draw from with it's coordinates now
1345
including the width and height. As the pixmap coordinates are the same as the
1346
coordinates that the widget uses (because our pixmap is drawn into the topleft
1347
corner), the coordinates for the source and destination topleft point are the
1348
same. This is something to watch out for in some of the next step, so it may be
1349
mentioned here already.</para>
1351
<para>Next comes what happens if we release the mouse button. Then the drawing
1352
has to stop when we move the mouse again, so we set mousePressed to false here:</para>
1354
<programlisting>void KScribbleView::mouseReleaseEvent( QMouseEvent * ) {
1355
mousePressed = FALSE;
1358
<para>Now we have finished implementing the user interaction when it comes to
1359
the actual drawing operations. The example shows it's not too complicated to use
1360
a document-view model. Just create your document instance so that it contains
1361
the contents and copy the contents to your view.</para>
1364
<sect1 id="painting-and-resizing-the-document">
1365
<title>Painting and Resizing the Document</title>
1367
<para>What is left to do are two other virtual event handlers that need a
1368
reimplementation. First of all, we have to take care that our picture gets
1369
painted into the window when something else happens: when you open another
1370
window that obscures the painting - then you change to your painting again, but
1371
it won't be there, unless your paint event gets processed to redraw the
1374
<programlisting>void KScribbleView::paintEvent( QPaintEvent *e )
1376
QWidget::paintEvent( e );
1378
QRect r = e->rect();
1380
bitBlt( this, r.x(), r.y(), &doc->buffer, r.x(), r.y(), r.width(), r.height() );
1383
<para>This method also uses <function>bitBlt()</function> to draw the picture from
1384
the buffer into the widget. Here, we only need the rectangle that gets
1385
repainted, so we retrieve the geometry from the event (
1386
<function>e->rect()</function> ) and use the coordinates for
1387
<function>bitBlt()</function>, just as we did in the
1388
<function>mouseMoveEvent()</function>.</para>
1390
<para>The only thing where we didn't do anything about is the size of the
1391
pixmap. We didn't set it anywhere - we did not even use the pixmap in the
1392
document class except for loading and saving - but these methods aren't called
1393
when creating a new picture. So it seems our pixmap doesn't have a size nor a
1394
predefined background at all (even if we would have set the size, the contents
1395
would be random colors because it is uninitialized). On the other hand we have
1396
the fact that the <classname>KScribbleView</classname> instances get resized
1397
when they show up - at least with the minimum size. This is the point where we
1398
can add the initialization as well, because the user can change the size
1399
manually and the widget will receive a resize event as well. For reasons of
1400
simplicity, we want to set the pixmap size the same size the widget has. All
1401
this is done in the event handler <function>resizeEvent()</function>:</para>
1403
<programlisting>void KScribbleView::resizeEvent( QResizeEvent *e )
1405
QWidget::resizeEvent( e );
1407
int w = width() > doc->buffer.width() ?
1408
width() : doc->buffer.width();
1409
int h = height() > doc->buffer.height() ?
1410
height() : doc->buffer.height();
1412
QPixmap tmp( doc->buffer );
1413
doc->buffer.resize( w, h );
1414
doc->buffer.fill( Qt::white );
1415
bitBlt( &doc->buffer, 0, 0, &tmp, 0, 0, tmp.width(), tmp.height() );
1418
<para>Here, we first call the <function>resizeEvent</function> handler of
1419
<classname>QWidget</classname>. Then we calculate the size of our picture -
1420
because we can resize a window to make it smaller or bigger, we have to separate
1421
these two cases: if we resize to a smaller geometry, the picture shall still
1422
contain it's contents. On the other hand, if we resize to a bigger widget, we
1423
have to resize the pixmap as well to that bigger size. The calculated values are
1424
stored in w and h. But before the resize takes place, we create a copy of our
1425
pixmap, in the document in tmp. Then we resize the buffer (the document), fill it
1426
with white color and then copy back the contents from tmp into buffer. This
1427
resizes our pixmap always syncronous with the widget that displays it but
1428
doesn't lose contents which are outside the visible area, if the resizing
1429
makes the widget smaller.</para>
1431
<para>Now our first application has gained a step where we can test it's
1432
functionality. Just hit <guimenuitem>"Run"</guimenuitem> in &kdevelop; and after
1433
<application>KScribble</application> shows up, you're ready to paint your first
1434
picture with it!</para>
1439
<chapter id="extending-the-gui">
1440
<title>Extending the GUI</title>
1442
<para>As we have seen, we have already provided
1443
<application>KScribble</application> the ability to open and save pictures with
1444
the document class, and enabled user interaction by overwriting virtual methods
1445
in the view class. Next we gained the first functionaliy - we can draw
1446
pictures. But when we created the <classname>QPen</classname> instance in the
1447
document class, we set some pre-defined values for the pen; the color is black
1448
and the pen width set to 3 pixels. As you usually want to change these values in
1449
a drawing application, we have to enhance the main <abbrev>GUI</abbrev> by
1450
providing ways to set these, according to the currently active window and
1451
document connected to it. This chapter will therefore introduce you to:</para>
1455
<para>adding a new menu to the menubar</para>
1458
<para>extending the toolbar with icons for the actions defined in the menubar</para>
1461
<para>creating a new dialog with the &kdevelop; Dialogeditor</para>
1464
<para>connecting the new commands of the menubar and toolbar to calling the dialogs</para>
1467
<para>enabling the document class to let us change the pen values</para>
1471
<para>Further, we also add a method to delete the document contents at all with
1472
a menubar command.</para>
1474
<sect1 id="adding-the-pen-menu">
1475
<title>Adding the "Pen" Menu</title>
1477
<para>As the title of this section says, we will add a menu for setting the pen
1478
values of the documents here. Menus that are inserted into
1479
the menubar are instances of <classname>QPopupMenu</classname>, and you can have
1480
a look at how the current menubar is created when you switch to the
1481
<classname>KScribbleApp</classname> class, method
1482
<function>initMenubar()</function>.</para>
1484
<para> You will see that the menubar items are created in the order they appear
1485
on the menubar - but this isn't necessary. There are two things important to how
1486
the menubar will look like:</para>
1490
<para>in the menus the order of the menu-items</para>
1493
<para>in the menubar the order of inserting the popup menus.</para>
1497
<para>Last but not least you have to create menus by calling the
1498
constructor. The class declaration already contains the pointers to
1499
the popup menus, so we will have to add our <guimenu>"Pen"</guimenu> menu here first:</para>
1501
<programlisting>kscribbleapp.h
1508
QPopupMenu* pPenMenu;
1512
<para>Now we are going to create the menu itself. Change to the implementation
1513
of the method <literal>KScribbleApp::initMenuBar()</literal> and add the
1514
lines marked with an arrow:</para>
1516
<programlisting>void KScribbleApp::initMenuBar()
1520
-> ///////////////////////////////////////////////////////////////////
1521
-> // menuBar entry pen-Menu
1522
-> pPenMenu = new QPopupMenu();
1523
-> pPenMenu->insertItem(i18n("&Color"), ID_PEN_COLOR);
1524
-> pPenMenu->insertItem(i18n("&Brush"), ID_PEN_BRUSH);
1526
menuBar()->insertItem(i18n("&Edit"), pEditMenu);
1527
-> menuBar()->insertItem(i18n("&Pen"), pPenMenu);
1528
menuBar()->insertItem(i18n("&View"), pViewMenu);
1531
-> connect(pPenMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
1532
-> connect(pPenMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
1535
<para>You see that we first create the menu with <literal>new
1536
QPopupMenu()</literal>. Then we use the <function>insertItem</function> methods
1537
to add two menu entries, <guimenuitem>Color</guimenuitem> and
1538
<guimenuitem>Brush</guimenuitem>. The visible commands are inserted with the
1539
method <function>i18n()</function>, which ensures that you can internationalize
1540
your appliction. So as a general rule, you would declare all visual text that
1541
will appear later by the method <function>i18n()</function>. Qt-only programs using Qt > 2.0 would use the corresponding method <function>tr()</function> instead of <function>i18n()</function>, as Qt has it's own ways of
1542
internationalizing applications.</para>
1544
<para>The second argument is a macro, the <abbrev>ID</abbrev> of the
1545
menubar item. This <abbrev>ID</abbrev> is a number that we have to set
1546
using <literal>#define</literal> in the file <filename>resource.h</filename>, where you will see all other already
1547
used ID's declared. There are also other ways to insert menus by directly
1548
connecting a slot to the inserted entry, but the application framework uses <abbrev>ID</abbrev>'s
1549
to select which action has been activated - and highlighted. Therefore each menu
1550
entry, independent of the popup menu it appears, has to be a unique number, and
1551
as we can hardly remember numbers later, setting a #define for the <abbrev>ID</abbrev> is a
1552
nice solution. The popup menu is now inserted into the menubar with
1553
<function>insertItem()</function> as well, and with the pointer to the menu as
1554
second argument.</para>
1556
<para>Note that we inserted the popup menu after the <guimenu>"Edit"</guimenu>
1557
menu and before the <guimenu>"View"</guimenu> menu, so it will appear between
1558
those menus later in the menubar. What is also important when creating menus is
1559
that they should be available to the user with shortcuts; ususally in menus you
1560
will see underlined characters that the user can jump to directly by pressing
1561
<keycap>Alt</keycap>and the appropriate underlined letter of the menuitem. As a
1562
programmer, you have to set this character by a leading ampersand, so the
1563
<guimenu>"Pen"</guimenu> menu will later be accessible via the keyboard by
1564
pressing <keycombo><keycap>Alt</keycap><keycap>P</keycap></keycombo>. Within the
1565
menu, the user can press another button to go directly to the command he wants
1566
to, so in the menu all items should have this kind of shortcuts as well.</para>
1568
<note><para>Note that you should write item insertions together in groups that
1569
have the same visible access, so you can keep a better overview of the
1570
characters you already used so that there are no menu accelerators used
1571
twice. (this is also important for your translators: in other languages the used
1572
accelerator may not be available in the translated word, so they have to set
1573
some accelerators again.)</para></note>
1575
<para>In the last two lines we're connecting the pen menu with two slots: one
1576
for when the menu signals that it is activated and the action should be
1577
executed, and one for when it is highlighted. That allows making a statusbar
1578
help message available for the user. You can have a look at the methods the menu
1579
is connected to, they contain switch statements where the sent menu
1580
<abbrev>ID</abbrev> is compared and the following action called.</para>
1582
<para>What is left to do is to add the #define statements to the file
1583
<filename>resource.h</filename>:</para>
1585
<programlisting>resource.h
1587
///////////////////////////////////////////////////////////////////
1589
#define ID_PEN_COLOR 14010
1590
#define ID_PEN_BRUSH 14020</programlisting>
1592
<para>You will see that the numbers are unique for these entries - you have to
1593
watch out not to set the same number for two entries. If it happens by
1594
accident, there's still the compiler to inform you about redefining.</para>
1596
<para>This is currently all you have to do to add a new menu for your
1597
menubar. The actions they will execute are: "Color" will call a color
1598
selection dialog, "Brush" will call a dialog (which we still have to create) to
1599
select the brush width.</para>
1601
<para>But first we'll extend the toolbar as well by two icons for these actions
1602
in the next section.</para>
1605
<sect1 id="adding-toolbar-buttons">
1606
<title>Adding Toolbar Buttons</title>
1608
<para>Whenever you think that some new commands should be made available by
1609
toolbar buttons as well because they are often used and you want to offer
1610
additional functionality, you can easily do that by adding buttons in the
1611
framework's <function>initToolBar()</function> method of the
1612
<classname>App</classname> class. Here, we decide to add a button for both menu
1613
entries in the <guimenu>Pen</guimenu> menu, but those need icons - which you can
1614
either find in the KDE directory <filename>/toolbar</filename> or, when you
1615
don't find an icon that matches your action, have to create yourself.</para>
1617
<para><application>KIconEdit</application> is very suitable to paint icons, so
1618
we will first create them. Choose <guimenuitem>"New"</guimenuitem> from the
1619
<application>KDevelop</application><guimenu>"File"</guimenu> menu and select
1620
<guilabel>"Icon"</guilabel> as the filetype.</para> <para>The first icon will be
1621
named "<filename>pencolor.xpm</filename>". Now we have to select where we want
1622
to have the icon created in our project directory. Press the directory selection
1623
button and change to your project directory containing the
1624
<application>KScribble</application> sources. Then create a new directory
1625
"<filename>toolbar</filename>". Change to that directory and press
1626
<guilabel>"OK"</guilabel>. The new icon will then be created in the new
1627
directory "<filename>toolbar</filename>" and will be opened by
1628
<application>KIconEdit</application> within &kdevelop; automatically. Paint
1629
something that will signalize the user what the button is intended to do, save
1630
the pixmap and then switch to the <abbrev>RFV</abbrev> / <abbrev>LFV</abbrev> in
1631
&kdevelop;. Select the icon by a <mousebutton>right</mousebutton> mouse button
1632
press and select <guimenuitem>"Properties"</guimenuitem> from the popup
1633
menu. You will see that the icon is included in the distribution, but for your
1634
program to find the icon again later, you have to set the installation
1635
destination as well. Check the <guilabel>"install"</guilabel> option and enter
1636
into the line now active below:</para>
1638
<para><userinput>$(kde_datadir)/kscribble/toolbar/pencolor.xpm</userinput></para>
1640
<para>This will install the pixmap in the KDE file system hierarchy's data
1641
directory, where each application has its subdirectory containing additional
1642
files needed by the application. Icons have to be installed into another
1643
subdirectory "<filename>toolbar</filename>", so the application's icon loader
1644
can find the pixmaps for your program.</para>
1646
<para>After you're finished, repeat all these above steps with the second icon
1647
for selecting the pen width. Name this pixmap
1648
"<filename>penwidth.xpm</filename>".</para>
1650
<para>Now we only have to insert the buttons into the toolbar; add the lines
1651
marked with the arrow into your code:</para>
1653
<programlisting>void KScribbleApp::initToolBar()
1656
toolBar()->insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy"));
1657
toolBar()->insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste"));
1658
toolBar()->insertSeparator();
1659
-> toolBar()->insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") );
1660
-> toolBar()->insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") );
1661
-> toolBar()->insertSeparator();
1662
toolBar()->insertButton(BarIcon("help"), ID_HELP_CONTENTS, SIGNAL(clicked()),
1666
<para>Here, we use the methods of <classname>KToolBar</classname> to insert
1667
buttons. The first argument, <function>BarIcon()</function>, tells the method to
1668
load the icon for the button. What seems unusual is that we don't have to care
1669
for the file extension. The preferred format for KDE 2 is
1670
<filename>*.PNG</filename>, but it works with xpm's as well. (You could use
1671
<application>ImageMagick</application> for that as well which can do that- or
1672
use <application>KScribble</application> in a later step to convert your icons
1675
<para>The second argument is again the <abbrev>ID</abbrev>. The commands are
1676
then automatically activated, as the <function>toolBar()</function> is already
1677
connected to the same methods as the menubar is for signal
1678
<function>activated()</function>. The third argument stands for "available" when
1679
true, "deactivated" when false; as we want to have these available, we set this
1680
to true. At last, we add a tooltip for the the buttons, which we also embrace
1681
with <function>i18n()</function> to allow internationalization.</para>
1683
<para>You're done for now, the <abbrev>GUI</abbrev> is extended at least
1684
visually. You can compile and run <application>KScribble</application> again and
1685
see how it looks. Of course the new items in the menubar and toolbar can't
1686
execute any action, that is what we're going to add in the next section. You
1687
will also note that the toolbar icons we added are not displayed - which is
1688
because we didn't install <application>KScribble</application> and so they can't
1689
be found. All other used icons are already shipped with the KDE libraries, so
1690
these are already visible.</para>
1694
<sect1 id="creating-the-pen-width-dialog">
1695
<title>Creating the Pen Width Dialog</title>
1697
<para>As we�ve already created the according menubar and toolbar commands, we
1698
now have to build the first dialog to set the pen width. For this, select
1699
<guimenuitem>"New"</guimenuitem> from the KDevelop <guimenu>"File"</guimenu>
1700
menu and select <guilabel>"Qt/KDE Dialog"</guilabel>. Then enter the dialog file
1701
name as <filename>kpenbrushdlg</filename>. The extension will be automatically
1702
added. Enter <guilabel>"OK"</guilabel> and the dialogeditor opens an empty
1703
widget that will be our dialog background.</para>
1705
<para>When constructing a dialog, we have to think about what is really needed
1706
by the user. Here, we need a label to display what will be set, a spinbox with
1707
up and down buttons to set the pen width value, and three buttons: one for
1708
resetting the pen width to the default value, one to cancel the dialog, and one
1709
for taking over the new value - the <guibutton>OK</guibutton> button. In this
1710
order we will add the items to the dialog - which is important because the
1711
tab-focus follows the order by which the widgets are created. So if you�re
1712
starting with the <guibutton>OK</guibutton> button, then the spinbox and then
1713
the <guibutton>cancel</guibutton> button, the input focus will change from the
1714
<guibutton>ok</guibutton> button to the spinbox, and then to the
1715
<guibutton>cancel</guibutton> button - which is not what the user
1718
<para>The tab focus should follow the widget�s items top down, from left to
1719
right, so we have to construct the dialog in this order as well. To add items to
1720
the dialog, select the <guilabel>"Widgets"</guilabel> tab on the left
1721
pane. There you have all available widgets present as icons to construct your
1722
dialog. Pressing a widget button will create the new item and place it at the
1723
top-left corner of the widget. From there, you can place it with the mouse to
1724
the position you would like it to show up. Further, when a widget item is
1725
selected, you can set the according values in the <guilabel>"Widget
1726
Properties"</guilabel> pane on the right.</para>
1730
<term>The Label</term> <listitem><para>press the <guilabel>"QLabel"</guilabel>
1731
button on the <guilabel>"Widgets"</guilabel> tab and place it at position x:50,
1732
y:20. Then select the <guilabel>"General"</guilabel> section in the widget
1733
properties pane. Change the text in properity <guilabel>"Text"</guilabel> from
1734
<guilabel>"Label"</guilabel> to <guilabel>"Pen Width:"</guilabel>. Adjust the
1735
width of the label to a width that matches the label contents in x-direction; a
1736
width of 120 should be fine. You can do this either by using the mouse or set
1737
the value in the <guilabel>"Geometry"</guilabel> section of the
1743
<term>The Spinbox</term>
1744
<listitem><para>Press the
1745
<guilabel>"QSpinBox"</guilabel> button on the <guilabel>"Widgets"</guilabel> tab
1746
and place it at the right of the label we created in the last step. Now set the
1747
variable name in section <guilabel>"C++Code"</guilabel> to
1748
<guilabel>"width_spbox"</guilabel>. The minimum and maximum values are 1 and
1749
100, which should be fine for setting the brush width.</para>
1754
<term>The Buttons</term>
1755
<listitem><para>Finally, we need the mentioned three
1756
buttons. The leftmost button will be the default button. Create a
1757
<classname>QPushbutton</classname> and place it somewhere nicely on the bottom of
1758
the dialog, set the variable name to <varname>"default_btn"</varname> and the
1759
button text to <guilabel>"Default"</guilabel>. Proceed with the
1760
<guibutton>OK</guibutton> button with variable name
1761
<guibutton>"ok_btn"</guibutton> and the cancel button with variable name
1762
<guilabel>"cancel_btn"</guilabel> and set the button text to
1763
<guilabel>"&OK"</guilabel> and <guilabel>"&Cancel"</guilabel>.</para>
1768
<para>If you�re fine with the layout of the dialog, choose <guilabel>"Generate
1769
complete sources"</guilabel> from the Build menu and set the classname to
1770
"<classname>KPenBrushDlg</classname>", the inheritance to
1771
<classname>QDialog</classname>. After pressing <guibutton>"OK"</guibutton>, the
1772
sources for the dialog are created and added to the project. Now
1773
you can return to the editor view in &kdevelop; and we can add the code needed
1774
to give the dialog some execution purpose.</para>
1778
<sect1 id="connections-and-setting-up">
1779
<title>Connections and Setting Up</title>
1781
<para>After we have created the <abbrev>GUI</abbrev> of the dialog, we have to
1782
add some functionality to the buttons and provide ways to set and retrieve the
1783
selected value of the spinbox - because we want the dialog to display the
1784
current value when it gets called, and to access the selected value when the user
1785
pressed the <guibutton>OK</guibutton> button to quit the dialog.</para>
1787
<para>In the generated class for the dialog,
1788
<classname>KPenBrushDlg</classname>, you can see one method besides the
1789
constructor and the destructor, <function>initDialog()</function>. This method implements the whole
1790
<abbrev>GUI</abbrev> construction, so we don�t have to care for that anymore and
1791
we can go directly to add the usual connections for the push buttons first. Add
1792
the lines marked by arrows to the constructor of the dialog:</para>
1794
<programlisting>KPenBrushDlg::KPenBrushDlg(int curr, QWidget *parent, const char *name) : QDialog(parent,name,true){
1797
-> connect(default_btn, SIGNAL(clicked()), this, SLOT(slotDefault()));
1798
-> connect(ok_btn, SIGNAL(clicked()), this, SLOT(accept()));
1799
-> connect(cancel_btn, SIGNAL(clicked()), this, SLOT(reject()));
1802
<para>This provides the functionality for the buttons on the bottom of the
1803
dialog when the user clicks the button. First, we set the default button to
1804
execute a slot called <function>slotDefault()</function>. This slot is still to be
1805
implemented below, where we will set the default value of the spinbox
1808
<para>The second <function>connect()</function> call connects the ok button to
1809
call the slot <function>accept()</function> provided by
1810
<classname>QDialog</classname>, as well as the <guibutton>cancel</guibutton>
1811
button gets connected to <classname>QDialog</classname>�s slot
1812
<function>reject()</function>. This will both close the dialog and will set the
1813
result value which we will use later when we implement the method that calls the
1814
dialog to determine if we want to use the value set or to ignore any
1817
<para>Now we have to add two methods to set and retrieve the spinbox value:</para>
1819
<programlisting>void setCurrent(int curr){ width_spbox->setValue(curr); }
1820
int width() { return width_spbox->value(); };</programlisting>
1822
<para>Add these methods to the class declaration with the modifier "public", as
1823
we want to set and retrieve the values when we call the dialog to show up. The
1824
<function>setCurrent()</function> method will be used to set the current value the
1825
pen has, the <function>width()</function> method returns us the selected with when
1826
the user presses OK and we want to know which value has been chosen.</para>
1828
<para>Last but not least, we need to implement the <function>slotDefault()</function> method:</para>
1830
<programlisting>//kpenbrushdlg.h:
1831
//method declaration:
1837
//method implementation:
1839
void KPenBrushDlg::slotDefault()
1841
width_spbox->setValue(3);
1844
<para>This will set the default value to 3 pixels for the pen.</para>
1846
<para>Now we�re ready with our first dialog and we can turn to over to the other
1847
application classes to adapt some things and add the method calls to invoke the
1851
<sect1 id="calling-the-dialogs">
1852
<title>Calling the Dialogs</title>
1854
<para>As you may guess, calling the dialogs means that we will not only
1855
implement calling our width selection dialog but also add the method for
1856
selecting the pen color, but one after another. First, create a method
1857
<function>slotPenBrush()</function> in the class
1858
<classname>KScribbleApp</classname>:</para>
1860
<programlisting>void KScribbleApp::slotPenBrush()
1862
slotStatusMsg(i18n("Setting brush width..."));
1864
// get one window with document for a current pen width
1865
QWidgetList windows = pWorkspace->windowList();
1866
KScribbleView* m = (KScribbleView*)windows.at(0);
1867
KScribbleDoc* pDoc = m->getDocument();
1868
int curr_width=pDoc->penWidth();
1870
// create the dialog, get the new width and set the pen width for all documents
1871
KPenBrushDlg* dlg= new KPenBrushDlg(this);
1872
dlg->setCurrent(curr_width);
1874
int width=dlg->width();
1875
for ( int i = 0; i < int(windows.count()); ++i )
1877
m = (KScribbleView*)windows.at(i);
1880
pDoc = m->getDocument();
1881
pDoc->setPenWidth(width);
1885
slotStatusMsg(i18n("Ready."));
1888
<para>Here, we first have to access the window list and retrieve a pointer to a
1889
document - which can be a document of any window, because all documents should
1890
have the same current pen width. Then we create an integer variable
1891
<varname>curr_width</varname> that stores the current pen width.</para>
1893
<para>Now we can call the dialog by creating the dlg instance of
1894
<classname>KPenBrushDlg</classname>. Then we set the current pen width by
1895
calling <function>dlg->setCurrent()</function>, which method we added to the
1896
dialog. By calling <function>dlg->exec()</function> we invoke the dialog. The
1897
<literal>if(<varname>m</varname>)</literal> statement ensures that the following code is only
1898
executed when the result code of the dialog has the accept flag set - which
1899
means, the code is executed if the user pressed the <guibutton>OK</guibutton>
1900
button on the dialog.</para>
1902
<para>Assuming the user changed the value and pressed <guibutton>OK</guibutton>,
1903
we have to set all documents to use the new pen width. For that we use the
1904
<function>for()</function> loop and set every document�s pen width to the width
1905
variable we retrieved before with <function>dlg->width()</function>.</para>
1907
<para>We don�t have implemented the method <function>setPenWidth()</function> in
1908
the document class, so we�ll do this right now:</para>
1914
void setPenWidth( int w ){ pen.setWidth( w ); }</programlisting>
1917
<para>What is missing to execute any action is to add the methods that shall be
1918
called when the menu items are activated or the toolbar buttons pressed. For
1919
this, we have to add the <abbrev>ID</abbrev>�s to the slot
1920
<function>commandCallback()</function>, which selects and executes the according
1921
methods we want to call if a menu or toolbar item was chosen:</para>
1923
<programlisting>void KScribbleApp::commandCallback(int id_)
1938
<para>This addition also adds the <literal>slotPenColor()</literal> method to
1939
the execution list to set the pen color, which we will implement now:</para>
1941
<programlisting>void KScribbleApp::slotPenColor()
1943
slotStatusMsg(i18n("Selecting pen color..."));
1946
int result = KColorDialog::getColor( myColor, this );
1947
if ( result == KColorDialog::Accepted )
1949
QWidgetList windows = pWorkspace->windowList();
1952
for ( int i = 0; i < int(windows.count()); ++i )
1954
m = (KScribbleView*)windows.at(i);
1957
pDoc = m->getDocument();
1958
pDoc->setPenColor(myColor);
1962
slotStatusMsg(i18n("Ready."));
1965
<para>When looking at the code, we see that we use another new method of
1966
<classname>KScribbleDoc</classname> to set the pen color. This one has to be
1967
implemented as well:</para>
1970
<programlisting>kscribbledoc.h:
1972
/** sets the pen color */
1973
void setPenColor( const QColor &c ){ pen.setColor( c ); }</programlisting>
1975
<para>Watch out for adding the declaration of the two new methods
1976
<function>slotPenBrush()</function> and <function>slotPenColor()</function> to the
1977
class <classname>KScribbleApp</classname>, so our class knows about these
1980
<para>Now you�re ready! Let�s summarize what we�ve done in this chapter:</para>
1983
<step><para> we first added menubar and toolbar commands/items for two dialogs
1984
that we want to call;</para></step>
1985
<step><para> then we created the first dialog to set the pen width with the
1986
KDevelop dialogeditor</para></step>
1987
<step><para> after that we created the methods we want to call by the
1988
menubar/toolbar items which invoke the dialogs and set the poperties we
1989
wanted to change</para></step>
1990
<step><para> finally, we added the needed set functions to the document so we
1991
are able to change the values after the dialogs get executed.</para></step>
1994
<para>By this structure, you are provided the general way how to extend your
1995
application with more functionality and manipulating settings
1996
that influence the behavoir of the document and view interaction.</para>
2000
<chapter id="extended-views">
2001
<title>Extended Views</title>
2003
<para>In this chapter we�re going to extend the functionality of our view widget
2004
by two enhancements: syncronized views and scrollviews.</para>
2006
<sect1 id="syncronizing-views">
2007
<title>Syncronizing Views</title>
2009
<para>Let�s first explain what this will bring us, and then how we�re going to
2010
do it. While playing with <application>KScribble</application>, you may have
2011
noticed, that if you open another view of a document by calling
2012
<menuchoice><guimenu>"Window"</guimenu><guimenuitem>"New
2013
Window"</guimenuitem></menuchoice>, this new view works with the same data as
2014
the first view, and does like any other view you create with that command. But
2015
when it comes to painting into the document, you can only do that in one view -
2016
the other views are not displaying the document contents at the same time. If
2017
you obscure one view that doesn�t contain the actual contents with another
2018
window and then bring it up to the front again, it will display the acutal
2019
contents. That comes because after a widget has been obscured and then activated
2020
again, it receives a paint event from the window system, which will call
2021
<literal>KScribbleView::paintEvent()</literal> again and that finally redraws
2022
the contents of the area that has been obscured. What we want to achieve is that
2023
all views should paint syncronous with the one the user actually paints to. In
2024
fact, you will see that this enhancement is a really easy task. The document
2025
class already provides us a method <function>updateAllViews()</function>, which
2026
calls the <function>update()</function> method on each view in the document�s
2027
view list. This makes it very easy to syncronize the document contents - every
2028
time the contents is changed, here by mouse movements (where we copy the
2029
changings to the buffer with <function>bitBlt()</function>), we just have to
2030
call updateAllViews(this). The this pointer is needed, because the calling view
2031
doesn�t need a repaint and the <function>update()</function> method is only
2032
executed if the sender view is not the same as it�s own.</para>
2034
<para>What you�ve got to do here is only to call updateAllViews(this) at the end
2035
of the virtual methods <function>mousePressEvent()</function>,
2036
<function>mouseMoveEvent()</function> and
2037
<function>mouseReleaseEvent()</function> - and you�re done! Take this as a
2038
general rule in your applications: each time the contents of the document is
2039
changed by a view, call <function>updateAllViews()</function>. How the update
2040
has to be executed has to be implemented in the widget�s
2041
<function>update()</function> method; one may be content by setting e.g. the
2042
changed text in an editor, in our application we just call
2043
<function>repaint()</function>, which generates a paint event and copies the
2044
contents of the document into the view again.</para>
2047
<sect1 id="scrolled-views">
2048
<title>Scrolled Views</title>
2050
<para>In this section we will add a functionality that is most often a thread to
2051
developers - if you can�t use an already implemented widget that provides the
2052
scrolling already. What does scrolling mean? In our context, the problem begins
2053
where we want to open a picture that is bigger than a view can
2054
display. therefore, the result will be that you can only see as much as the view
2055
provides, beginning from the topleft corner; the rest will be cut away from the
2056
user�s view. A scrollview on the other hand is a widget that provides a
2057
scrollbar on the right side and on the bottom of the widget by which the user
2058
can "move" the contents. In fact, it shows the same size of the document
2059
contents, but the view area can be moved within the document, so each part can
2060
be displayed if the user wants to by moving the scrollbar sliders up and down,
2061
left and right. Fortunately, Qt provides a class
2062
<classname>QScrollView</classname> that itself inherits from
2063
<classname>QWidget</classname> and offers the same base functionality as an
2064
ordinary widget but manages the contents by scrollbars automatically - with the
2065
additional option that the programmer can either just use an instance of the
2066
<classname>QScrollView</classname>, create the child widgets to manage with the
2067
scrollview as parent and add them to the scrollview with
2068
<function>addChild()</function> or create a view by inheriting
2069
<classname>QScrollView</classname> and draw into the viewport, which is a
2070
defined area inside the scrollview, instead of directly to the widget. The
2071
difference here is that <classname>QScrollView</classname> provides a set of
2072
event handlers similar to the <classname>QWidget</classname> event handlers
2073
especially for the viewport. So what was formerly a
2074
<function>mousePressEvent()</function> in our view will become a
2075
viewportMousePressEvent, a <function>paintEvent()</function> will become a
2076
viewportPaintEvent etc. The second possibility will suite our needs to make
2077
<application>KScribbleView</application> a scrollable widget and so we will have
2078
to make the following modifications:</para>
2081
<step><para>first we have to make the document use a size of its contents. This
2082
can be done by getting the size for an opened picture, for a
2083
new picture we have to define a default start size. In other painting
2084
applications, this size can be changed most often by a command
2085
provided by the user interface, <abbrev>e.g.</abbrev> a dialog that asks for the new width and
2086
height as well as a method to shrink the picture
2087
contents to fit into that new size.</para></step>
2088
<step><para>changing the inheritance of <classname>KScribbleView</classname>
2089
from <classname>QWidget</classname> to
2090
<classname>QScrollView</classname></para></step>
2091
<step><para>renaming the virtual methods to the according viewport methods of
2092
<classname>QScrollView</classname></para></step>
2093
<step><para>adapt the virtual event handlers to act on the geometry of the
2094
viewport. This means that the old implementation relies on the
2095
geometries of <classname>QWidget</classname> starting at the topleft corner of a
2096
widget. If the view is scrolled and the topleft corner is not visible, we
2097
have to ensure the positions retrieved from the <classname>QWidget</classname>
2098
coordinates are translated to viewport coordinates</para></step>
2101
<sect2 id="sizing-the-document-contents">
2102
<title>Sizing the Document Contents</title>
2104
<para>As already mentioned, we have to set a size to the document contents as
2105
well as to initialize this size and provide a method to
2106
retrieve the size by the views. For this, we add a variable <varname>QSize
2107
size</varname> to <classname>KScribbleDoc</classname> as well as the method
2108
<function>docSize()</function>:</para>
2110
<programlisting>kscribbledoc.h:
2112
#include <qsize.h>
2116
const QSize docSize(){ return size;};
2122
<para>Now we have to modify all methods that deal with initializing and opening
2123
the document contents - <function>newDocument()</function> and
2124
<function>openDocument()</function>:</para>
2126
<programlisting> bool KScribbleDoc::newDocument()
2128
/////////////////////////////////////////////////
2129
// TODO: Add your document initialization code here
2130
-> size=QSize(300,200 );
2131
pen=QPen( Qt::black, 3 );
2132
-> buffer.resize(size);
2133
-> buffer.fill( Qt::white );
2134
/////////////////////////////////////////////////
2139
bool KScribbleDoc::openDocument(const QString &filename, const char *format /*=0*/)
2142
QFile f( filename );
2143
// if ( !f.open( IO_ReadOnly ) )
2145
/////////////////////////////////////////////////
2146
// TODO: Add your document opening code here
2147
if(!buffer.load( filename, format ))
2149
-> size=buffer.size();
2150
/////////////////////////////////////////////////
2154
m_filename=filename;
2155
m_title=QFileInfo(f).fileName();
2159
<para>In <function>newDocument()</function>, we initialize the size with a default
2160
value of 300 pixels wide and 200 pixels high. This is enough for a
2161
small picture for now and we could add a dialog for resizing as well if we want.
2162
When it comes to opening a picture, we have to set the size to the size of the picture. This can be done by calling
2163
<literal>QPixmap::size()</literal>, which we used in <function>openDocument()</function>. Then we�re done with setting the sizes and we can move on to reimplementing <classname>KScribbleView</classname> and make it a scrollview.</para>
2167
<sect1 id="adapting-the-view">
2168
<title>Adapting the View</title>
2170
<para>As said above, we first have to change some things in the interface of
2171
<classname>KScribbleView</classname>. The following code shows these
2174
<programlisting>#include <qscrollview.h>
2176
class KScribbleView : public QScrollView
2181
/** changed from mousePressEvent() overwriting QScrollView method */
2182
virtual void viewportMousePressEvent( QMouseEvent* );
2183
/** changed from mouseReleaseEvent() overwriting QScrollView method */
2184
virtual void viewportMouseReleaseEvent( QMouseEvent* );
2185
/** changed from mouseMoveEvent() overwriting QScrollView method */
2186
virtual void viewportMouseMoveEvent( QMouseEvent* );
2188
/** commeted out because we have a document size defined */
2189
// resizeEvent( QResizeEvent* );
2191
/** changed from paintEvent() overwriting QScrollView method */
2192
virtual void viewportPaintEvent( QPaintEvent* );
2195
<para>Here, we changed the inheritance from <classname>QWidget</classname> to
2196
<classname>QScrollView</classname> first and added the according include file we
2197
need. Also we changed all implemented event handlers that deal with interaction
2198
on the contents of the scrollview to the according methods
2199
<classname>QScrollView</classname> provides for this purpose and commented out
2200
the resizeEvent. Now we can go over to the implementation of these methods and
2201
make use of the size our picture has. As a view is always created after the
2202
document exists, we can resize the widget directly in the constructor to fit
2203
this size and as well resize the contents (which is the viewport size):</para>
2205
<programlisting>#include <qsize.h>
2207
KScribbleView::KScribbleView(KScribbleDoc* pDoc, QWidget *parent, const char* name, int wflags)
2208
: QScrollView(parent, name, wflags | WPaintClever | WNorthWestGravity | WRepaintNoErase)
2212
polyline=QPointArray(3);
2214
-> setResizePolicy ( QScrollView::ResizeOne );
2215
-> viewport()->setCursor( Qt::crossCursor );
2217
-> QSize size=doc->docSize();
2218
// resize the viewport - this makes the resizeEvent obsolete
2219
-> resizeContents(size.width(), size.height());
2220
// resize the widget to show up with the document size
2224
<para>Note that formerly, the <function>resizeEvent()</function> took
2225
care of resizing the drawing area to the same as the widget size. At the same
2226
time, this changed the document size as well, so the document picture had always
2227
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
2228
<function>resizeContents()</function> provided by
2229
<classname>QScrollView</classname> with the size of the document. You may also
2230
notice that we changed the cursor over the widget from the overall widget to the
2231
viewport widget, which we can retrieve with <function>viewport()</function>. Now
2232
we can reimplement the event handlers. At first, we should take care for the
2233
paintEvent, as this is one of the most important ones, because it gets called
2234
whenever the widget shows up or is resized.</para>
2236
<important><para>Attention: take care to comment out the <function>resizeEvent()</function> implementation!</para></important>
2238
<para>Now, the paint event will have to copy the pixmap in the buffer to the
2239
corresponding position in the view. For this, we have to change the
2240
destination of <function>bitBlt()</function> from this to
2241
<function>viewport()</function>, set the topleft position to 0,0 and set the
2242
target (the buffer) to copy from the contentsX and contentsY position on into
2243
the viewport:</para>
2245
<programlisting>void KScribbleView::viewportPaintEvent( QPaintEvent *e )
2247
bitBlt( viewport(),0,0, &doc->buffer,contentsX() ,contentsY() );
2250
<para>The <function>contentsX()</function> is the position in the
2251
x-direction of the scrollview�s contents - which goes to position 0 in the
2252
viewport�s absolute position, which is the topleft point visible in the
2253
scrollview. The same applies to the y-direction. This part is
2254
sometimes hard to understand and you may have to do a bit "trial and error" when
2255
implementing your own scrollviews. The other possible
2256
call of <function>bitBlt()</function> would be to switch the values of the
2257
positions and inverting the contents values:</para>
2259
<para>bitBlt( viewport(), -contentsX(), -contentsY(), &doc->buffer, 0, 0 );</para>
2261
<para>The last changes we need to do are changing the mouse event
2262
handlers. First, the <function>mouseMoveEvent()</function>, which
2263
changes to <function>viewportMouseMoveEvent()</function>, has a
2264
<function>bitBlt()</function> call as well. Here, we have to apply the same chages
2265
as in the paint event.</para>
2267
<para>Further, in the <function>mousePressEvent()</function> and the
2268
<function>mouseMoveEvent()</function>, we have retrieved the position of the mouse
2269
events with <literal>e->pos()</literal>. This position now will deliver us a
2270
widget position - not the contents position, so we have to translate this to
2271
draw into the correct position of the document with
2272
<function>viewportToContents()</function>:</para>
2274
<programlisting> void KScribbleView::viewportMousePressEvent( QMouseEvent *e )
2276
mousePressed = TRUE;
2277
-> doc->polyline[2] = doc->polyline[1] = doc->polyline[0] = viewportToContents(e->pos());
2278
doc->updateAllViews(this);
2281
void KScribbleView::viewportMouseMoveEvent( QMouseEvent *e )
2283
if ( mousePressed ) {
2285
doc->polyline[1] = doc->polyline[0];
2286
-> doc->polyline[0] = viewportToContents(e->pos());
2287
painter.drawPolyline( doc->polyline );
2289
r.setBottom( r.bottom() + doc->penWidth() );
2291
doc->setModified();
2292
-> bitBlt(viewport(), r.x()-contentsX(), r.y()-contentsY() ,
2293
-> &doc->buffer, r.x(), r.y(), r.width(), r.height() );
2294
doc->updateAllViews(this);
2298
<para>In the <function>viewportMouseMoveEvent()</function>, we had to change the
2299
destination again from <literal>this</literal> to <function>viewport()</function>, and
2300
with that translate the positions. This time, we used the second version of the
2301
call we used in <function>viewportPaintEvent()</function>, with subtracting the
2302
contentsX and contentsY values to copy the rectangle containing the current
2303
painting into the correct position of the viewport.</para>
2305
<para>At last, we will apply a small change in conjunction with the <function>update()</function> method: why should we repaint the whole widget every time?
2306
This will reduce performance and lead to a so-called "flicker"
2307
effect. This effect sometimes occurs with widgets, but there are some ways to reduce this behavior. Instead of calling <function>repaint()</function>, we could call <function>repaint(<constant>false</constant>)</function> as well. This
2308
will not erase the widget contents before redrawing it. As we copy the document
2309
contents directly into the widget, we don�t need to erase it anyway, because all
2310
the data will be overwritten anyway. In conjunction with
2311
<classname>QScrollView</classname>, we will reduce the painting
2312
even more: we limit the update method to call <function>repaint()</function> on
2313
the <function>viewport()</function> widget, because that will call
2314
<function>viewportPaintEvent()</function>. On the other hand, the painting area we
2315
use is the rectangle containing the document contents, when the
2316
document size is smaller than the viewport size. So we can limit the paint event
2317
to the rectangle of the viewport where the document
2318
is displayed, whose visible width and height we can retrieve and compose to the
2319
rectangle. Additionally, we use the erase parameter with <literal>false</literal>,
2320
so the document area does not get erased:</para>
2322
<programlisting>void KScribbleView::update(KScribbleView* pSender){
2324
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
2327
<para>Now you�re ready! This chapter has been one of the hardest to implement,
2328
and to understand, especially when it comes to the geometries that change. On
2329
the other hand, we gave our application a whole new functionality through the new
2330
scrollview, and the syncronized views.</para>
2332
<para>With that, we�re moving on to the last chapter of our tutorial. There we
2333
will apply only a few changes, making use of some new methods of the KDE2
2334
libraries, but as usual, this will bring us some interesting functionality.
2335
<application>KScribble</application> will soon be able to open and save a whole range
2336
of picture formats, as we will remove the restriction of operating only
2337
on the png file format.</para>
2342
<chapter id="using-kimageio-with-kscribble">
2343
<title>Using KImageIO with KScribble</title>
2345
<para>When it comes to images, the Qt and KDE libraries offer a wide variety of
2346
operations. Besides actual drawing routines, the libraries support a whole set
2347
of image formats which they can read and write - all without any cost on our
2348
view. The main class for these operations is <classname>QImageIO</classname>,
2349
which has a support library by KDE: <classname>KImageIO</classname>. As a
2350
preparation before we can make use of these nice methods, we have to add the
2351
according library to <application>KScribble</application>:
2352
<literal>libkimgio</literal>.</para>
2355
<menuchoice><guimenu>"Project"</guimenu><guimenuitem>"Options"</guimenuitem></menuchoice>
2356
in &kdevelop; and add the line <userinput>-lkimgio</userinput> to the additional
2357
libraries to link <application>KScribble</application> with.</para>
2359
<sect1 id="preparing-the-document">
2360
<title>Preparing the Document</title>
2362
<para>Now we can go on and apply our changes. First, we have to remove the
2363
current restriction in the document class to only read and write
2364
PNG files. Just replace "PNG" in the following methods with format, as given by
2365
the <function>openDocument()</function> and <function>saveDocument()</function>
2368
<para>In method <literal>KScribbleDoc::openDocument()</literal>:</para>
2370
<para><literal> if(!buffer.load( filename, format ))</literal></para>
2372
<para>In method <literal>KScribbleDoc:: saveDocument()</literal>:</para>
2374
<para><literal> if(!buffer.save( filename, format ))</literal></para>
2376
<para>Then we have a default behavior of these methods. For now we don�t
2377
have all available file formats. This will be our next task, together with
2378
adapting the slots in <application>KScribble</application> that deliver us
2383
<sect1 id="registering-file-formats">
2384
<title>Registering File Formats</title>
2386
<para>To use <classname>KImageIO</classname>, we have to initialize the library
2387
first. For this, we add a call for <function>registerFormats()</function> in our
2388
<function>main()</function> function:</para>
2395
KImageIO::registerFormats();
2397
if (app.isRestored())
2398
............</programlisting>
2400
<para>Note that this call is after the application is instanciated with
2401
<application>KApplication</application> app - without the application instance, our program will
2402
not run, as <classname>KImageIO</classname> then doen�t know on which
2403
application to register the formats. The include file for this call will be
2404
added to <filename>kscribble.h</filename>, as we�re going to use some of its methods in <classname>KScribbleApp</classname>:</para>
2409
#include <kimgio.h></programlisting>
2412
<sect1 id="opening-images">
2413
<title>Opening Images</title>
2415
<para>Now that we can make use of <classname>KImageIO</classname>, we have to
2416
apply the first change to the most important method of <classname>KScribbleApp</classname>:
2417
<function>openDocumentFile()</function>. This method opens us any
2418
document, but until now only with a filename. It just leaves out the
2419
extension, as the format is not required by the document class by default. But
2420
as we have changed that, we just need a format - and have to adapt the call for
2421
<literal>KScribbleDoc::openDocument()</literal> in the method <function>openDocumentFile()</function>:</para>
2423
<programlisting>kscribble.cpp:
2425
void KScribbleApp::openDocumentFile(const char* file)
2431
-> QString format=KImageIO::type(file);
2432
-> if(!doc->openDocument(file,format))
2433
KMessageBox::error (this,i18n("Could not open document !"), i18n("Error !"));
2434
addRecentFile(file);
2439
<para>Of course, this works the same as using <literal>QString
2440
format=QImageIO::imageFormat(file);</literal>. Here, <classname>KImageIO</classname>
2441
delivers us the format of the image, and we can call the document to open the
2442
file by filename and format. Another possiblity would be to detect the format
2443
in the document class as well.</para>
2447
<sect1 id="setting-file-filters-with-kimageio">
2448
<title>Setting File Filters with KImageIO</title>
2450
<para>Here, we are finishing our tutorial with the last section - we will adapt
2451
the file dialogs of <application>KScribble</application> to make use of file filters.
2452
For these, <classname>KImageIO</classname> provides nice methods to give us the
2453
needed strings for all image file formats that are available for opening and
2454
saving. The following implementation replaces the default file filter (which is
2455
in fact none - you have to change your own applications� file filter to your
2456
mime type accordingly) with the <function>pattern()</function> method
2457
of <classname>KImageIO</classname>:</para>
2459
<programlisting> void KScribbleApp::slotFileOpen()
2461
slotStatusMsg(i18n("Opening file..."));
2463
-> QString fileToOpen=KFileDialog::getOpenFileName(QDir::currentDirPath(),
2464
-> KImageIO::pattern(KImageIO::Reading), this, i18n("Open File..."));
2465
if(!fileToOpen.isEmpty())
2467
openDocumentFile(fileToOpen);
2470
slotStatusMsg(i18n("Ready."));
2473
<para>Here, the mode in <function>pattern()</function> is set to Reading - which
2474
may differ from the patterns that are retrieved when set to writing. Now
2475
we have finished opening files by its filename and format completely. What is
2476
missing to complete this structure, is setting the patterns as well for saving a
2477
file. For that, the <function>slotFileSaveAs()</function> is called,
2478
which itself invokes the file dialog that retrieves a file name. There, we will
2479
set the pattern mode to Writing:</para>
2481
<programlisting>void KScribbleApp::slotFileSaveAs()
2483
slotStatusMsg(i18n("Saving file with a new filename..."));
2485
-> QString newName=KFileDialog::getSaveFileName(QDir::currentDirPath(),
2486
-> KImageIO::pattern(KImageIO::Writing), this, i18n("Save as..."));
2487
if(!newName.isEmpty())
2489
KScribbleView* m = (KScribbleView*)pWorkspace->activeWindow();
2492
KScribbleDoc* doc = m->getDocument();
2493
QString format=QFileInfo(newName).extension();
2494
format=format.upper();
2495
if(!doc->saveDocument(newName,format))
2497
KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
2500
doc->changedViewList();
2504
slotStatusMsg(i18n("Ready."));
2510
<chapter id="adding-printing-functionality">
2511
<title>Adding Printing Functionality</title>
2513
<para>In this chapter we will show you how easy it is to implement printing
2514
functions using Qt. It�s actually just one line of code for us to
2515
do here, but we will start understanding who is actually doing the printing
2516
job. When the user presses the print button in <application>KScribble</application> or chooses <guimenuitem>"Print"</guimenuitem> from the <guimenu>"File"</guimenu> menu, the <function>slotFilePrint()</function> method is called in <classname>KScribbleApp</classname>.</para>
2519
This method detects which child window is currently active, and creates a printer
2520
instance of the class <classname>QPrinter</classname>. Then it calls the widget�s
2521
printing method, <literal>KScribbleView::print()</literal>. Here, the framework
2522
already contains the base implementation - which already shows you that for
2523
printing you just have to use <classname>QPainter</classname> which then
2524
draws on the printer. This method also calls the printing dialog.</para>
2526
<para>What we have to do here is to use <classname>QPainter</classname> methods
2527
to draw the pixmap of the document connected to the view. As
2528
<classname>QPainter</classname> already offers a whole set of methods
2529
<function>drawPixmap()</function>, we will of course use one of them:</para>
2531
<programlisting>void KScribbleView::print(QPrinter *pPrinter)
2533
if (pPrinter->setup(this))
2538
///////////////////////////////
2539
// TODO: add your printing code here
2540
-> p.drawPixmap(0,0,doc->buffer);
2541
///////////////////////////////
2546
<para>Here, we paint into the offset of the printer page at 0,0 with our buffer pixmap of the document. That�s all!</para>
2548
<para>You can just go ahead and test it - now you can print any graphics file that
2549
<application>KScribble</application> is able to open.</para>
2551
<para>This is now the end of our tour through creating a KDE 2 application. You
2552
can find the source package of <application>KScribble</application> complete with an extension that adds cut, copy, paste, and undo functions, as well as drag�n drop here:</para>
2554
<para><filename>$(KDEDIR)/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</filename></para>
2556
<para>The example tarball is locally installed and can be downloaded to your
2557
home directory, where you can untar it and test it. After untarring the tarball with <command>tar <option>zxvf</option> kscribble-1.0.tar.gz</command>, load the project and call <guimenuitem>"Automake and autoconf"</guimenuitem> from the
2558
<guimenu>"Build"</guimenu> menu in &kdevelop;, then call
2559
<guimenuitem>"./configure"</guimenuitem> from the same menu. The configure
2560
options are those of my installtion of the KDE 2 and Qt 2.1, so you have to
2561
change them manually to match your installation path for these options.</para>
2563
<para>The appendix also contains the complete sourcecode for this package to read through online.</para>
2566
<chapter id="copyright">
2567
<title>Copyright </title>
2569
<para> <screen>KDevelop Copyright 1998,1999,2000 The KDevelop Team.
2571
This program is free software; you can redistribute it and/or modify
2572
it under the terms of the GNU General Public License as published by
2573
the Free Software Foundation; either version 2 of the License, or
2574
(at your option) any later version.
2576
This program is distributed in the hope that it will be useful,
2577
but WITHOUT ANY WARRANTY; without even the implied warranty of
2578
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2579
GNU General Public License for more details.
2581
You should have received a copy of the GNU General Public License
2582
along with this program; if not, write to the Free Software
2583
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2585
<!-- note to self: insert link to GPL -->
2591
<appendix id="kscribble-1.0-example-sourcecode">
2592
<title>KScribble-1.0 Example Sourcecode</title>
2594
<sect1 id="project-tarball">
2595
<title>Project Tarball</title>
2597
<para>The example tarball is locally installed and can be downloaded to your
2598
home directory, where you can untar it and test it.
2599
After untarring the tarball with <command>tar <option>zxvf</option>
2600
kscribble-1.0.tar.gz</command>, load the project and call <guimenuitem>"Automake and
2601
autoconf"</guimenuitem> from the
2602
<guimenu>"Build"</guimenu> menu in &kdevelop;, then call
2603
<guimenuitem>"./configure"</guimenuitem> from the same menu. The configure
2604
options are those of my installtion of the KDE 2 and Qt 2.1, so you have to
2605
change them manually to match your installation path for these options.</para>
2607
<para><filename>$(KDEDIR)/share/apps/kdevelop/examples/kscribble-1.0.tar.gz</filename></para>
2610
<sect1 id="main.cpp">
2611
<title>main.cpp</title>
2614
/***************************************************************************
2615
main.cpp - description
2617
begin : Mon Jan 31 11:05:05 CET 2000
2618
copyright : (C) 2000 by Ralf Nolden
2619
email : Ralf.Nolden@post.rwth-aachen.de
2620
***************************************************************************/
2622
/***************************************************************************
2624
* This program is free software; you can redistribute it and/or modify *
2625
* it under the terms of the GNU General Public License as published by *
2626
* the Free Software Foundation; either version 2 of the License, or *
2627
* (at your option) any later version. *
2629
***************************************************************************/
2630
#include <kcmdlineargs.h>
2631
#include <kaboutdata.h>
2632
#include <klocale.h>
2634
#include "kscribble.h"
2636
static const char *description =
2637
I18N_NOOP("KDE 2 example application");
2639
static KCmdLineOptions options[] =
2641
{ "+[File]", I18N_NOOP("image file to open"), 0 },
2646
int main(int argc, char *argv[])
2648
KAboutData aboutData( "kscribble", I18N_NOOP("KScribble"),
2649
VERSION, description, KAboutData::License_GPL,
2650
"(c) 2000, Ralf Nolden");
2651
aboutData.addAuthor("Ralf Nolden",0, "rnolden@kdevelop.de");
2652
KCmdLineArgs::init( argc, argv, &aboutData );
2653
KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
2656
KImageIO::registerFormats();
2658
if (app.isRestored())
2660
RESTORE(KScribbleApp);
2664
KScribbleApp *kscribble = new KScribbleApp();
2665
kscribble->show();
2667
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
2669
if (args->count())
2670
for(int i=0;i<args->count();i++)
2671
kscribble->openDocumentFile(args->arg(i));
2673
kscribble->openDocumentFile();
2682
<sect1 id="kscribble.h">
2683
<title>kscribble.h</title>
2685
<para><programlisting>/***************************************************************************
2686
kscribble.h - description
2688
begin : Mon Jan 31 11:05:05 CET 2000
2689
copyright : (C) 2000 by Ralf Nolden
2690
email : Ralf.Nolden@post.rwth-aachen.de
2691
***************************************************************************/
2693
/***************************************************************************
2695
* This program is free software; you can redistribute it and/or modify *
2696
* it under the terms of the GNU General Public License as published by *
2697
* the Free Software Foundation; either version 2 of the License, or *
2698
* (at your option) any later version. *
2700
***************************************************************************/
2706
#ifdef HAVE_CONFIG_H
2707
#include <config.h>
2710
// include files for Qt
2711
#include <qstrlist.h>
2712
#include <qworkspace.h>
2714
// include files for KDE
2715
#include <kapp.h>
2716
#include <ktmainwindow.h>
2717
#include <kaccel.h>
2718
#include <kimgio.h>
2720
// forward declaration of the KScribble classes
2722
class KScribbleView;
2725
* The base class for KScribble application windows. It sets up the main
2726
* window and reads the config file as well as providing a menubar, toolbar
2727
* and statusbar. In initView(), your main view is created as the MDI child window manager.
2728
* Child windows are created in createClient(), which gets a document instance as it's document to
2729
* display whereby one document can have several views.The MDI child is an instance of KScribbleView,
2730
* the document an instance of KScribbleDoc.
2731
* KScribbleApp reimplements the methods that KTMainWindow provides for main window handling and supports
2732
* full session management as well as keyboard accelerator configuration by using KAccel.
2738
* @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
2739
* @version KDevelop version 1.1 code generation
2741
class KScribbleApp : public KTMainWindow
2746
/** construtor of KScribbleApp, calls all init functions to create the application.
2747
* @see initMenuBar initToolBar
2751
/** enables menuentries/toolbar items
2753
void enableCommand(int id_);
2754
/** disables menuentries/toolbar items
2756
void disableCommand(int id_);
2757
/** opens a file specified by commandline option
2759
void openDocumentFile(const char *file=0);
2762
/** queryClose is called by KTMainWindow on each closeEvent of a window. Against the
2763
* default implementation (only returns true), this overridden function retrieves all modified documents
2764
* from the open document list and asks the user to select which files to save before exiting the application.
2765
* @see KTMainWindow#queryClose
2766
* @see KTMainWindow#closeEvent
2768
virtual bool queryClose();
2769
/** queryExit is called by KTMainWindow when the last window of the application is going to be closed
2770
* during the closeEvent().
2771
* Against the default implementation that just returns true, this calls saveOptions() to save the
2772
* settings of the last window's properties.
2773
* @see KTMainWindow#queryExit
2774
* @see KTMainWindow#closeEvent
2776
virtual bool queryExit();
2777
/** saves the window properties for each open window during session end to the session config file,
2778
* including saving the currently opened file by a temporary filename provided by KApplication.
2779
* @see KTMainWindow#saveProperties
2781
virtual void saveProperties(KConfig *_cfg);
2782
/** reads the session config file and restores the application's state including the last
2783
* opened files and documents by reading the temporary files saved by saveProperties()
2784
* @see KTMainWindow#readProperties
2786
virtual void readProperties(KConfig *_cfg);
2787
/** event filter to catch close events for MDI child windows and is installed in createClient() on every
2788
* child window. Closing a window calls the eventFilter first which removes the view from the connected
2789
* documents' view list. If the last view is going to be closed, the eventFilter() tests if the document
2790
* is modified; if yes, it asks the user to save the document. If the document title contains "Untitled",
2791
* slotFileSaveAs() gets called to get a save name and path.
2793
virtual bool eventFilter(QObject* object, QEvent* event);
2794
/** creates a new child window. The document that will be connected to it
2795
* has to be created before and the instances filled, with e.g. openDocument().
2796
* Then call createClient() to get a new MDI child window.
2797
* @see KScribbleDoc#addView
2798
* @see KScribbleDoc#openDocument
2799
* @param doc pointer to the document instance that the view will
2802
void createClient(KScribbleDoc* doc);
2803
/** accepts drag events for images */
2804
virtual void dragEnterEvent( QDragEnterEvent* );
2805
/** accepts drops and opens a new document
2807
virtual void dropEvent( QDropEvent* );
2810
/** sets the main application window title each time the active MDI child window changes. */
2811
void setWndTitle(QWidget*);
2812
/** switch argument for slot selection by menu or toolbar ID */
2813
void commandCallback(int id_);
2814
/** switch argument for Statusbar help entries on slot selection. Add your ID's help
2815
* here for toolbars and menubar entries. */
2816
void statusCallback(int id_);
2817
/** add a opened file to the recent file list and update recent file menu*/
2818
void addRecentFile(const QString &file);
2819
/** clears the document in the actual view to reuse it as the new document */
2821
/** open a file and load it into the document*/
2822
void slotFileOpen();
2823
/** opens a file from the recent files menu */
2824
void slotFileOpenRecent(int id_);
2825
/** save a document */
2826
void slotFileSave();
2827
/** save a document by a new filename*/
2828
void slotFileSaveAs();
2829
/** asks for saving if the file is modified, then closes the actual file and window*/
2830
void slotFileClose();
2831
/** print the actual file */
2832
void slotFilePrint();
2833
/** closes all documents and quits the application.*/
2834
void slotFileQuit();
2835
/** reverts the last user action for the active window */
2836
void slotEditUndo();
2837
/** put the marked text/object into the clipboard and remove
2838
* it from the document
2841
/** put the marked text/object into the clipboard
2843
void slotEditCopy();
2844
/** paste the clipboard into the document
2846
void slotEditPaste();
2847
/** clears the current document */
2848
void slotEditClearAll();
2849
/** sets the pen width */
2850
void slotPenBrush();
2851
/** sets the pen color */
2852
void slotPenColor();
2853
/** toggles the toolbar
2855
void slotViewToolBar();
2856
/** toggles the statusbar
2858
void slotViewStatusBar();
2859
/** creates a new view for the document in the active child window and adds the new view to the
2860
* list of views the document maintains.
2862
void slotWindowNewWindow();
2863
/** changes the statusbar contents for the standard label permanently, used to indicate current actions.
2864
* @param text the text that is displayed in the statusbar
2866
void slotStatusMsg(const QString &text);
2867
/** changes the status message of the whole statusbar for two seconds, then restores the last status.
2868
* This is used to display statusbar messages that give information about actions for
2869
* toolbar icons and menuentries.
2870
* @param text the text that is displayed in the statusbar
2872
void slotStatusHelpMsg(const QString &text);
2873
/** gets called when the window menu is activated; recreates the window menu with all opened window titles. */
2874
void windowMenuAboutToShow();
2875
/** activates the MDI child widget when it gets selected from the window menu. */
2876
void windowMenuActivated( int id );
2879
/** save general Options like all bar positions and status as well as the geometry and
2880
* the recent file list to the configuration file
2883
/** read general Options again and initialize all variables like the recent file list
2886
/** initKeyAccel creates the keyboard accelerator items for the available slots and changes the menu accelerators.
2889
void initKeyAccel();
2890
/** initMenuBar creates the menubar and inserts the menupopups as well as creating the helpMenu.
2893
/** this creates the toolbars.
2896
/** sets up the statusbar for the main window by initialzing a statuslabel.
2898
void initStatusBar();
2900
/** Creates the main view of the KTMainWindow instance and initializes the MDI view area including any needed
2905
/** contains the recently used filenames */
2906
QStrList recentFiles;
2908
/** the configuration object of the application */
2910
/** the key accelerator container */
2912
/** the recent file menu containing the last five opened files */
2913
QPopupMenu *pRecentFileMenu;
2914
/** the file menu */
2915
QPopupMenu* pFileMenu;
2916
/** the edit menu */
2917
QPopupMenu* pEditMenu;
2919
QPopupMenu* pPenMenu;
2920
/** the view menu */
2921
QPopupMenu* pViewMenu;
2922
/** the window menu */
2923
QPopupMenu *pWindowMenu;
2924
/** pWorkspace is the MDI frame widget that handles MDI child widgets. Inititalized in
2927
QWorkspace *pWorkspace;
2928
/** the printer instance */
2930
/** a counter that gets increased each time the user creates a new document with "File"->"New" */
2932
/** a list of all open documents. If the last window of a document gets closed, the installed eventFilter
2933
* removes this document from the list. The document list is checked for modified documents when the user
2934
* is about to close the application. */
2935
QList<KScribbleDoc> *pDocList;
2939
#endif // KSCRIBBLE_H</programlisting>
2943
<sect1 id="kscribble.cpp">
2944
<title>kscribble.cpp</title>
2946
<para><programlisting>/***************************************************************************
2947
kscribble.cpp - description
2949
begin : Mon Jan 31 11:05:05 CET 2000
2950
copyright : (C) 2000 by Ralf Nolden
2951
email : Ralf.Nolden@post.rwth-aachen.de
2952
***************************************************************************/
2954
/***************************************************************************
2956
* This program is free software; you can redistribute it and/or modify *
2957
* it under the terms of the GNU General Public License as published by *
2958
* the Free Software Foundation; either version 2 of the License, or *
2959
* (at your option) any later version. *
2961
***************************************************************************/
2963
// include files for QT
2964
#include <qdir.h>
2965
#include <qprinter.h>
2966
#include <qvbox.h>
2967
#include <qwhatsthis.h>
2968
#include <qtooltip.h>
2969
#include <qtoolbutton.h>
2970
#include <qimage.h>
2971
#include <qdragobject.h>
2974
// include files for KDE
2975
#include <kiconloader.h>
2976
#include <kmessagebox.h>
2977
#include <kfiledialog.h>
2978
#include <kcolordlg.h>
2979
#include <kmenubar.h>
2980
#include <klocale.h>
2981
#include <kconfig.h>
2983
// application specific includes
2984
#include "kscribble.h"
2985
#include "kscribbleview.h"
2986
#include "kscribbledoc.h"
2987
#include "resource.h"
2988
#include "kpenbrushdlg.h"
2991
KScribbleApp::KScribbleApp()
2993
config=kapp->config();
2994
printer = new QPrinter;
2996
pDocList = new QList<KScribbleDoc>();
2997
pDocList->setAutoDelete(true);
2998
setAcceptDrops(true);
3000
///////////////////////////////////////////////////////////////////
3001
// call inits to invoke all other construction parts
3010
///////////////////////////////////////////////////////////////////
3011
// disable menu and toolbar items at startup
3012
disableCommand(ID_EDIT_UNDO);
3015
KScribbleApp::~KScribbleApp()
3020
void KScribbleApp::initKeyAccel()
3022
keyAccel = new KAccel(this);
3024
// fileMenu accelerators
3025
keyAccel->connectItem(KStdAccel::New, this, SLOT(slotFileNew()));
3026
keyAccel->connectItem(KStdAccel::Open, this, SLOT(slotFileOpen()));
3027
keyAccel->connectItem(KStdAccel::Save, this, SLOT(slotFileSave()));
3028
keyAccel->connectItem(KStdAccel::Close, this, SLOT(slotFileClose()));
3029
keyAccel->connectItem(KStdAccel::Print, this, SLOT(slotFilePrint()));
3030
keyAccel->connectItem(KStdAccel::Quit, this, SLOT(slotFileQuit()));
3031
// editMenu accelerators
3032
keyAccel->connectItem(KStdAccel::Cut, this, SLOT(slotEditCut()));
3033
keyAccel->connectItem(KStdAccel::Copy, this, SLOT(slotEditCopy()));
3034
keyAccel->connectItem(KStdAccel::Paste, this, SLOT(slotEditPaste()));
3036
keyAccel->connectItem(KStdAccel::Help, this, SLOT(appHelpActivated()));
3038
keyAccel->changeMenuAccel(pFileMenu, ID_FILE_NEW, KStdAccel::New);
3039
keyAccel->changeMenuAccel(pFileMenu, ID_FILE_OPEN, KStdAccel::Open);
3040
keyAccel->changeMenuAccel(pFileMenu, ID_FILE_SAVE, KStdAccel::Save);
3041
keyAccel->changeMenuAccel(pFileMenu, ID_FILE_CLOSE, KStdAccel::Close);
3042
keyAccel->changeMenuAccel(pFileMenu, ID_FILE_PRINT, KStdAccel::Print);
3043
keyAccel->changeMenuAccel(pFileMenu, ID_FILE_QUIT, KStdAccel::Quit);
3045
keyAccel->changeMenuAccel(pEditMenu, ID_EDIT_CUT, KStdAccel::Cut);
3046
keyAccel->changeMenuAccel(pEditMenu, ID_EDIT_COPY, KStdAccel::Copy);
3047
keyAccel->changeMenuAccel(pEditMenu, ID_EDIT_PASTE, KStdAccel::Paste);
3049
keyAccel->readSettings();
3052
void KScribbleApp::initMenuBar()
3054
///////////////////////////////////////////////////////////////////
3056
pRecentFileMenu = new QPopupMenu(this);
3057
connect(pRecentFileMenu, SIGNAL(activated(int)), SLOT(slotFileOpenRecent(int)));
3059
///////////////////////////////////////////////////////////////////
3060
// menuBar entry file-Menu
3061
pFileMenu = new QPopupMenu(this);
3062
pFileMenu->insertItem(BarIcon("filenew"), i18n("&New"), ID_FILE_NEW);
3063
pFileMenu->insertItem(BarIcon("fileopen"), i18n("&Open..."), ID_FILE_OPEN);
3064
pFileMenu->insertItem(i18n("Open &recent"), pRecentFileMenu, ID_FILE_OPEN_RECENT);
3066
pFileMenu->insertItem(i18n("&Close"), ID_FILE_CLOSE);
3067
pFileMenu->insertSeparator();
3068
pFileMenu->insertItem(BarIcon("filefloppy") ,i18n("&Save"), ID_FILE_SAVE);
3069
pFileMenu->insertItem(i18n("Save &As..."), ID_FILE_SAVE_AS);
3070
pFileMenu->insertSeparator();
3071
pFileMenu->insertItem(BarIcon("fileprint"), i18n("&Print..."), ID_FILE_PRINT);
3072
pFileMenu->insertSeparator();
3073
pFileMenu->insertItem(i18n("E&xit"), ID_FILE_QUIT);
3075
///////////////////////////////////////////////////////////////////
3076
// menuBar entry edit-Menu
3077
pEditMenu = new QPopupMenu(this);
3078
pEditMenu->insertItem(BarIcon("undo"), i18n("&Undo"), ID_EDIT_UNDO);
3079
pEditMenu->insertSeparator();
3080
pEditMenu->insertItem(BarIcon("editcut"), i18n("Cu&t"), ID_EDIT_CUT);
3081
pEditMenu->insertItem(BarIcon("editcopy"), i18n("&Copy"), ID_EDIT_COPY);
3082
pEditMenu->insertItem(BarIcon("editpaste"), i18n("&Paste"), ID_EDIT_PASTE);
3083
pEditMenu->insertItem(BarIcon("delete"),i18n("&Clear All"), ID_EDIT_CLEAR_ALL);
3085
///////////////////////////////////////////////////////////////////
3086
// menuBar entry pen-Menu
3087
pPenMenu = new QPopupMenu();
3088
pPenMenu->insertItem(i18n("&Color"), ID_PEN_COLOR);
3089
pPenMenu->insertItem(i18n("&Brush"), ID_PEN_BRUSH);
3091
///////////////////////////////////////////////////////////////////
3092
// menuBar entry view-Menu
3093
pViewMenu = new QPopupMenu(this);
3094
pViewMenu->setCheckable(true);
3095
pViewMenu->insertItem(i18n("&Toolbar"), ID_VIEW_TOOLBAR);
3096
pViewMenu->insertItem(i18n("&Statusbar"), ID_VIEW_STATUSBAR);
3098
///////////////////////////////////////////////////////////////////
3099
// menuBar entry window-Menu
3100
pWindowMenu = new QPopupMenu(this);
3101
pWindowMenu->setCheckable(true);
3104
///////////////////////////////////////////////////////////////////
3105
// menuBar entry helpMenu
3106
QPopupMenu* pHelpMenu = helpMenu(i18n("KScribble" VERSION "\n\n(c) 2000 by\n"
3107
"Ralf Nolden\nRalf.Nolden@post.rwth-aachen.de"));
3109
///////////////////////////////////////////////////////////////////
3110
// MENUBAR CONFIGURATION
3111
// insert your popup menus with the according menubar entries in the order
3112
// they will appear later from left to right
3113
menuBar()->insertItem(i18n("&File"), pFileMenu);
3114
menuBar()->insertItem(i18n("&Edit"), pEditMenu);
3115
menuBar()->insertItem(i18n("&Pen"), pPenMenu);
3116
menuBar()->insertItem(i18n("&View"), pViewMenu);
3117
menuBar()->insertItem(i18n("&Window"), pWindowMenu );
3118
menuBar()->insertItem(i18n("&Help"), pHelpMenu);
3120
///////////////////////////////////////////////////////////////////
3121
// CONNECT THE MENU SLOTS WITH SIGNALS
3122
// for execution slots and statusbar messages
3123
connect(pFileMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
3124
connect(pFileMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
3126
connect(pEditMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
3127
connect(pEditMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
3129
connect(pPenMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
3130
connect(pPenMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
3132
connect(pViewMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
3133
connect(pViewMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
3135
connect(pWindowMenu, SIGNAL(aboutToShow() ), SLOT( windowMenuAboutToShow() ) );
3136
connect(pWindowMenu, SIGNAL(activated(int)), SLOT(commandCallback(int)));
3137
connect(pWindowMenu, SIGNAL(highlighted(int)), SLOT(statusCallback(int)));
3141
void KScribbleApp::initToolBar()
3144
///////////////////////////////////////////////////////////////////
3146
toolBar()->insertButton(BarIcon("filenew"), ID_FILE_NEW, true, i18n("New File"));
3147
toolBar()->insertButton(BarIcon("fileopen"), ID_FILE_OPEN, true, i18n("Open File"));
3148
toolBar()->insertButton(BarIcon("filefloppy"), ID_FILE_SAVE, true, i18n("Save File"));
3149
toolBar()->insertButton(BarIcon("fileprint"), ID_FILE_PRINT, true, i18n("Print"));
3150
toolBar()->insertSeparator();
3151
toolBar()->insertButton(BarIcon("editcut"), ID_EDIT_CUT, true, i18n("Cut"));
3152
toolBar()->insertButton(BarIcon("editcopy"), ID_EDIT_COPY, true, i18n("Copy"));
3153
toolBar()->insertButton(BarIcon("editpaste"), ID_EDIT_PASTE, true, i18n("Paste"));
3154
toolBar()->insertSeparator();
3155
toolBar()->insertButton(BarIcon("pencolor"), ID_PEN_COLOR, true, i18n("Color") );
3156
toolBar()->insertButton(BarIcon("penwidth"), ID_PEN_BRUSH, true, i18n("Width") );
3157
toolBar()->insertSeparator();
3158
toolBar()->insertButton(BarIcon("help"), ID_HELP_CONTENTS, SIGNAL(clicked()),
3159
this, SLOT(appHelpActivated()), true,i18n("Help"));
3161
QToolButton *btnwhat = QWhatsThis::whatsThisButton(toolBar());
3162
QToolTip::add(btnwhat, i18n("What's this...?"));
3163
toolBar()->insertWidget(ID_HELP_WHATS_THIS, btnwhat->sizeHint().width(), btnwhat);
3165
///////////////////////////////////////////////////////////////////
3166
// INSERT YOUR APPLICATION SPECIFIC TOOLBARS HERE WITH toolBar(n)
3169
///////////////////////////////////////////////////////////////////
3170
// CONNECT THE TOOLBAR SLOTS WITH SIGNALS - add new created toolbars by their according number
3171
// connect for invoking the slot actions
3172
connect(toolBar(), SIGNAL(clicked(int)), SLOT(commandCallback(int)));
3173
// connect for the status help on holing icons pressed with the mouse button
3174
connect(toolBar(), SIGNAL(pressed(int)), SLOT(statusCallback(int)));
3178
void KScribbleApp::initStatusBar()
3180
///////////////////////////////////////////////////////////////////
3182
// TODO: add your own items you need for displaying current application status.
3183
statusBar()->insertItem(i18n("Ready."), ID_STATUS_MSG);
3187
void KScribbleApp::initView()
3189
////////////////////////////////////////////////////////////////////
3190
// here the main view of the KTMainWindow is created by a background box and
3191
// the QWorkspace instance for MDI view.
3192
QVBox* view_back = new QVBox( this );
3193
view_back->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken );
3194
pWorkspace = new QWorkspace( view_back );
3195
connect(pWorkspace, SIGNAL(windowActivated(QWidget*)), this, SLOT(setWndTitle(QWidget*)));
3199
void KScribbleApp::setWndTitle(QWidget*){
3200
setCaption(pWorkspace->activeWindow()->caption());
3203
void KScribbleApp::enableCommand(int id_)
3205
///////////////////////////////////////////////////////////////////
3206
// enable menu and toolbar functions by their ID's
3207
menuBar()->setItemEnabled(id_, true);
3208
toolBar()->setItemEnabled(id_, true);
3211
void KScribbleApp::disableCommand(int id_)
3213
///////////////////////////////////////////////////////////////////
3214
// disable menu and toolbar functions by their ID's
3215
menuBar()->setItemEnabled(id_, false);
3216
toolBar()->setItemEnabled(id_, false);
3219
void KScribbleApp::addRecentFile(const QString &file)
3221
if(recentFiles.contains(file))
3222
return; // it's already there
3224
if( recentFiles.count() < 5)
3225
recentFiles.prepend(file);
3227
recentFiles.remove(recentFiles.last());
3228
recentFiles.prepend(file);
3231
pRecentFileMenu->clear();
3233
for ( int i =0 ; i < (int)recentFiles.count(); i++){
3234
pRecentFileMenu->insertItem(recentFiles.at(i));
3239
void KScribbleApp::createClient(KScribbleDoc* doc)
3241
KScribbleView* w = new KScribbleView(doc, pWorkspace,0,WDestructiveClose);
3242
w->installEventFilter(this);
3244
w->setIcon(kapp->miniIcon());
3245
if ( pWorkspace->windowList().isEmpty() ) // show the very first window in maximized mode
3246
w->showMaximized();
3251
void KScribbleApp::openDocumentFile(const char* file)
3253
slotStatusMsg(i18n("Opening file..."));
3255
// check, if document already open. If yes, set the focus to the first view
3256
for(doc=pDocList->first(); doc > 0; doc=pDocList->next())
3258
if(doc->pathName()==file)
3260
KScribbleView* view=doc->firstView();
3261
view->setFocus();
3265
doc = new KScribbleDoc();
3266
pDocList->append(doc);
3267
doc->newDocument();
3268
// Creates an untitled window if file is 0
3272
QString fileName=QString(i18n("Untitled%1")).arg(untitledCount);
3273
doc->setPathName(fileName);
3274
doc->setTitle(fileName);
3279
QString format=QImageIO::imageFormat(file);
3280
if(!doc->openDocument(file,format))
3281
KMessageBox::error (this,i18n("Could not open document !"), i18n("Error !"));
3282
addRecentFile(file);
3284
// create the window
3287
slotStatusMsg(i18n("Ready."));
3291
void KScribbleApp::saveOptions()
3293
config->setGroup("General Options");
3294
config->writeEntry("Geometry", size());
3295
config->writeEntry("Show Toolbar", toolBar()->isVisible());
3296
config->writeEntry("Show Statusbar",statusBar()->isVisible());
3297
config->writeEntry("ToolBarPos", (int) toolBar()->barPos());
3298
config->writeEntry("Recent Files", recentFiles);
3302
void KScribbleApp::readOptions()
3305
config->setGroup("General Options");
3307
// bar status settings
3308
bool bViewToolbar = config->readBoolEntry("Show Toolbar", true);
3309
menuBar()->setItemChecked(ID_VIEW_TOOLBAR, bViewToolbar);
3312
enableToolBar(KToolBar::Hide);
3315
bool bViewStatusbar = config->readBoolEntry("Show Statusbar", true);
3316
menuBar()->setItemChecked(ID_VIEW_STATUSBAR, bViewStatusbar);
3319
enableStatusBar(KStatusBar::Hide);
3322
// bar position settings
3323
KToolBar::BarPosition toolBarPos;
3324
toolBarPos=(KToolBar::BarPosition) config->readNumEntry("ToolBarPos", KToolBar::Top);
3325
toolBar()->setBarPos(toolBarPos);
3327
// initialize the recent file list
3328
config->readListEntry("Recent Files",recentFiles);
3330
for (int i=0; i < (int) recentFiles.count(); i++)
3332
pRecentFileMenu->insertItem(recentFiles.at(i));
3335
QSize size=config->readSizeEntry("Geometry");
3345
void KScribbleApp::saveProperties(KConfig *_cfg)
3351
void KScribbleApp::readProperties(KConfig* _cfg)
3355
bool KScribbleApp::queryClose()
3358
QStringList saveFiles;
3360
if(pDocList->isEmpty())
3363
for(doc=pDocList->first(); doc!=0;doc=pDocList->next()){
3364
if(doc->isModified())
3365
saveFiles.append(doc->title());
3367
if(saveFiles.isEmpty())
3370
switch (KMessageBox::questionYesNoList(this,
3371
i18n("One or more documents have been modified.\nSave changes before exiting?"),saveFiles))
3373
case KMessageBox::Yes:
3374
for(doc=pDocList->first(); doc!=0;doc=pDocList->next()){
3375
if(doc->title().contains(i18n("Untitled")))
3379
if(!doc->saveDocument(doc->pathName())){
3380
KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
3386
case KMessageBox::No:
3392
bool KScribbleApp::queryExit()
3398
bool KScribbleApp::eventFilter(QObject* object, QEvent* event){
3399
if(event->type() == QEvent::Close)
3401
QCloseEvent* e=(QCloseEvent*)event;
3402
KScribbleView* pView=(KScribbleView*)object;
3403
KScribbleDoc* pDoc=pView->getDocument();
3404
if(pDoc->canCloseFrame(pView))
3406
pDoc->removeView(pView);
3407
if(!pDoc->firstView())
3408
pDocList->remove(pDoc);
3412
if(pWorkspace->windowList().count()==1)
3413
setPlainCaption(kapp->caption());
3415
setCaption(pWorkspace->activeWindow()->caption());
3421
return QWidget::eventFilter( object, event ); // standard event processing
3424
/////////////////////////////////////////////////////////////////////
3425
// SLOT IMPLEMENTATION
3426
/////////////////////////////////////////////////////////////////////
3429
void KScribbleApp::slotFileNew()
3431
slotStatusMsg(i18n("Creating new document..."));
3435
slotStatusMsg(i18n("Ready."));
3438
void KScribbleApp::slotFileOpen()
3440
slotStatusMsg(i18n("Opening file..."));
3442
QString fileToOpen=KFileDialog::getOpenFileName(QDir::currentDirPath(),
3443
KImageIO::pattern(KImageIO::Reading), this, i18n("Open File..."));
3444
if(!fileToOpen.isEmpty())
3446
openDocumentFile(fileToOpen);
3449
slotStatusMsg(i18n("Ready."));
3452
void KScribbleApp::slotFileOpenRecent(int id_)
3454
slotStatusMsg(i18n("Opening file..."));
3456
openDocumentFile(pRecentFileMenu->text(id_));
3458
slotStatusMsg(i18n("Ready."));
3461
void KScribbleApp::slotFileSave()
3463
slotStatusMsg(i18n("Saving file..."));
3464
KScribbleView* m = (KScribbleView*)pWorkspace->activeWindow();
3467
KScribbleDoc* doc = m->getDocument();
3468
if(doc->title().contains(i18n("Untitled")))
3471
if(!doc->saveDocument(doc->pathName()))
3472
KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
3476
slotStatusMsg(i18n("Ready."));
3479
void KScribbleApp::slotFileSaveAs()
3481
slotStatusMsg(i18n("Saving file with a new filename..."));
3483
QString newName=KFileDialog::getSaveFileName(QDir::currentDirPath(),
3484
KImageIO::pattern(KImageIO::Writing), this, i18n("Save as..."));
3485
if(!newName.isEmpty())
3487
KScribbleView* m = (KScribbleView*)pWorkspace->activeWindow();
3490
KScribbleDoc* doc = m->getDocument();
3491
QString format=QFileInfo(newName).extension();
3492
format=format.upper();
3493
if(!doc->saveDocument(newName,format))
3495
KMessageBox::error (this,i18n("Could not save the current document !"), i18n("I/O Error !"));
3498
doc->changedViewList();
3504
slotStatusMsg(i18n("Ready."));
3507
void KScribbleApp::slotFileClose()
3509
slotStatusMsg(i18n("Closing file..."));
3511
KScribbleView* m = (KScribbleView*)pWorkspace->activeWindow();
3514
KScribbleDoc* doc=m->getDocument();
3515
doc->closeDocument();
3519
slotStatusMsg(i18n("Ready."));
3522
void KScribbleApp::slotFilePrint()
3524
slotStatusMsg(i18n("Printing..."));
3526
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3528
m->print( printer );
3530
slotStatusMsg(i18n("Ready."));
3533
void KScribbleApp::slotFileQuit()
3535
slotStatusMsg(i18n("Exiting..."));
3537
// close the first window, the list makes the next one the first again.
3538
// This ensures that queryClose() is called on each window to ask for closing
3542
for(w=memberList->first(); w!=0; w=memberList->first())
3544
// only close the window if the closeEvent is accepted. If the user
3545
// presses Cancel on the saveModified() dialog,
3546
// the window and the application stay open.
3551
slotStatusMsg(i18n("Ready."));
3554
void KScribbleApp::slotEditUndo()
3556
slotStatusMsg(i18n("Reverting last action..."));
3558
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3562
slotStatusMsg(i18n("Ready."));
3565
void KScribbleApp::slotEditCut()
3567
slotStatusMsg(i18n("Cutting selection..."));
3569
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3571
m->cutSelection();
3573
slotStatusMsg(i18n("Ready."));
3576
void KScribbleApp::slotEditCopy()
3578
slotStatusMsg(i18n("Copying selection to clipboard..."));
3580
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3582
m->copySelection();
3584
slotStatusMsg(i18n("Ready."));
3587
void KScribbleApp::slotEditPaste()
3589
slotStatusMsg(i18n("Inserting clipboard contents..."));
3591
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3593
m->pasteSelection();
3595
slotStatusMsg(i18n("Ready."));
3598
void KScribbleApp::slotEditClearAll()
3600
slotStatusMsg(i18n("Clearing the document contents..."));
3602
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3604
KScribbleDoc* pDoc = m->getDocument();
3605
pDoc->editClearAll();
3607
slotStatusMsg(i18n("Ready."));
3610
void KScribbleApp::slotPenBrush()
3612
slotStatusMsg(i18n("Setting brush width..."));
3614
// get one window with document for a current pen width
3615
QWidgetList windows = pWorkspace->windowList();
3616
KScribbleView* m = (KScribbleView*)windows.at(0);
3617
KScribbleDoc* pDoc = m->getDocument();
3618
int curr_width=pDoc->penWidth();
3620
// create the dialog, get the new width and set the pen width for all documents
3621
KPenBrushDlg* dlg= new KPenBrushDlg(curr_width,this);
3623
int width=dlg->width();
3624
for ( int i = 0; i < int(windows.count()); ++i )
3626
m = (KScribbleView*)windows.at(i);
3629
pDoc = m->getDocument();
3630
pDoc->setPenWidth(width);
3634
slotStatusMsg(i18n("Ready."));
3637
void KScribbleApp::slotPenColor()
3639
slotStatusMsg(i18n("Selecting pen color..."));
3642
int result = KColorDialog::getColor( myColor, this );
3643
if ( result == KColorDialog::Accepted )
3645
QWidgetList windows = pWorkspace->windowList();
3648
for ( int i = 0; i < int(windows.count()); ++i )
3650
m = (KScribbleView*)windows.at(i);
3653
pDoc = m->getDocument();
3654
pDoc->setPenColor(myColor);
3658
slotStatusMsg(i18n("Ready."));
3662
void KScribbleApp::slotViewToolBar()
3664
slotStatusMsg(i18n("Toggle the toolbar..."));
3665
///////////////////////////////////////////////////////////////////
3666
// turn Toolbar on or off
3667
if( menuBar()->isItemChecked(ID_VIEW_TOOLBAR))
3669
menuBar()->setItemChecked(ID_VIEW_TOOLBAR, false);
3670
enableToolBar(KToolBar::Hide);
3674
menuBar()->setItemChecked(ID_VIEW_TOOLBAR, true);
3675
enableToolBar(KToolBar::Show);
3678
slotStatusMsg(i18n("Ready."));
3681
void KScribbleApp::slotViewStatusBar()
3683
slotStatusMsg(i18n("Toggle the statusbar..."));
3684
///////////////////////////////////////////////////////////////////
3685
//turn Statusbar on or off
3686
if( menuBar()->isItemChecked(ID_VIEW_STATUSBAR))
3688
menuBar()->setItemChecked(ID_VIEW_STATUSBAR, false);
3689
enableStatusBar(KStatusBar::Hide);
3693
menuBar()->setItemChecked(ID_VIEW_STATUSBAR, true);
3694
enableStatusBar(KStatusBar::Show);
3697
slotStatusMsg(i18n("Ready."));
3700
void KScribbleApp::slotWindowNewWindow()
3702
slotStatusMsg(i18n("Opening a new application window..."));
3704
KScribbleView* m = (KScribbleView*) pWorkspace->activeWindow();
3706
KScribbleDoc* doc = m->getDocument();
3710
slotStatusMsg(i18n("Ready."));
3713
void KScribbleApp::slotStatusMsg(const QString &text)
3715
///////////////////////////////////////////////////////////////////
3716
// change status message permanently
3717
statusBar()->clear();
3718
statusBar()->changeItem(text, ID_STATUS_MSG);
3722
void KScribbleApp::slotStatusHelpMsg(const QString &text)
3724
///////////////////////////////////////////////////////////////////
3725
// change status message of whole statusbar temporary (text, msec)
3726
statusBar()->message(text, 2000);
3729
void KScribbleApp::windowMenuAboutToShow()
3731
pWindowMenu->clear();
3733
pWindowMenu->insertItem(i18n("&New Window"), ID_WINDOW_NEW_WINDOW);
3734
pWindowMenu->insertItem(i18n("&Cascade"),
3735
pWorkspace, SLOT(cascade() ),0 , ID_WINDOW_CASCADE );
3736
pWindowMenu->insertItem(i18n("&Tile"),
3737
pWorkspace, SLOT(tile() ),0 , ID_WINDOW_TILE );
3739
if ( pWorkspace->windowList().isEmpty() ) {
3740
disableCommand(ID_WINDOW_NEW_WINDOW);
3741
disableCommand(ID_WINDOW_CASCADE);
3742
disableCommand(ID_WINDOW_TILE);
3745
pWindowMenu->insertSeparator();
3747
QWidgetList windows = pWorkspace->windowList();
3748
for ( int i = 0; i < int(windows.count()); ++i ) {
3749
int id = pWindowMenu->insertItem(QString("&%1 ").arg(i+1)+windows.at(i)->caption(),
3750
this, SLOT( windowMenuActivated( int ) ) );
3751
pWindowMenu->setItemParameter( id, i );
3752
pWindowMenu->setItemChecked( id, pWorkspace->activeWindow() == windows.at(i) );
3756
void KScribbleApp::windowMenuActivated( int id )
3758
QWidget* w = pWorkspace->windowList().at( id );
3764
void KScribbleApp::commandCallback(int id_)
3780
case ID_FILE_SAVE_AS:
3808
case ID_EDIT_CLEAR_ALL:
3820
case ID_VIEW_TOOLBAR:
3824
case ID_VIEW_STATUSBAR:
3825
slotViewStatusBar();
3828
case ID_WINDOW_NEW_WINDOW:
3829
slotWindowNewWindow();
3837
void KScribbleApp::statusCallback(int id_)
3842
slotStatusHelpMsg(i18n("Creates a new document"));
3846
slotStatusHelpMsg(i18n("Opens an existing document"));
3849
case ID_FILE_OPEN_RECENT:
3850
slotStatusHelpMsg(i18n("Opens a recently used file"));
3854
slotStatusHelpMsg(i18n("Saves the currently active document"));
3857
case ID_FILE_SAVE_AS:
3858
slotStatusHelpMsg(i18n("Saves the currently active document as under a new filename"));
3862
slotStatusHelpMsg(i18n("Closes the currently active document"));
3866
slotStatusHelpMsg(i18n("Prints out the actual document"));
3870
slotStatusHelpMsg(i18n("Quits the application"));
3874
slotStatusHelpMsg(i18n("Reverts the last editing action"));
3878
slotStatusHelpMsg(i18n("Cuts the selected section and puts it to the clipboard"));
3882
slotStatusHelpMsg(i18n("Copies the selected section to the clipboard"));
3886
slotStatusHelpMsg(i18n("Pastes the clipboard contents to actual position"));
3889
case ID_EDIT_CLEAR_ALL:
3890
slotStatusHelpMsg(i18n("Clears the document contents"));
3894
slotStatusHelpMsg(i18n("Sets the pen width"));
3898
slotStatusHelpMsg(i18n("Sets the current pen color"));
3901
case ID_VIEW_TOOLBAR:
3902
slotStatusHelpMsg(i18n("Enables/disables the toolbar"));
3905
case ID_VIEW_STATUSBAR:
3906
slotStatusHelpMsg(i18n("Enables/disables the statusbar"));
3909
case ID_WINDOW_NEW_WINDOW:
3910
slotStatusHelpMsg(i18n("Opens a new view for the current document"));
3913
case ID_WINDOW_CASCADE:
3914
slotStatusHelpMsg(i18n("Cascades all windows"));
3917
case ID_WINDOW_TILE:
3918
slotStatusHelpMsg(i18n("Tiles all windows"));
3925
/** accepts drops and opens a new document
3927
void KScribbleApp::dropEvent( QDropEvent* e){
3930
if ( QImageDrag::decode(e, img) )
3932
KScribbleDoc* doc = new KScribbleDoc();
3934
QString fileName=QString(i18n("Untitled%1")).arg(untitledCount);
3935
doc->setPathName(fileName);
3936
doc->setTitle(fileName);
3937
doc->newDocument();
3938
pDocList->append(doc);
3940
tmp.resize(img.size());
3941
tmp.convertFromImage(img);
3942
doc->setPixmap(tmp);
3943
doc->resizeDocument(tmp.size());
3944
doc->setModified();
3948
/** accepts drag events for images */
3949
void KScribbleApp::dragEnterEvent( QDragEnterEvent* e){
3950
e->accept(QImageDrag::canDecode(e));
3955
<sect1 id="kscribbledoc.h">
3956
<title>kscribbledoc.h</title>
3958
<para><programlisting>/***************************************************************************
3959
kscribbledoc.h - description
3961
begin : Mon Jan 31 11:05:05 CET 2000
3962
copyright : (C) 2000 by Ralf Nolden
3963
email : Ralf.Nolden@post.rwth-aachen.de
3964
***************************************************************************/
3966
/***************************************************************************
3968
* This program is free software; you can redistribute it and/or modify *
3969
* it under the terms of the GNU General Public License as published by *
3970
* the Free Software Foundation; either version 2 of the License, or *
3971
* (at your option) any later version. *
3973
***************************************************************************/
3975
#ifndef KSCRIBBLEDOC_H
3976
#define KSCRIBBLEDOC_H
3978
#ifdef HAVE_CONFIG_H
3979
#include <config.h>
3982
// include files for QT
3983
#include <qobject.h>
3984
#include <qstring.h>
3985
#include <qlist.h>
3987
#include <qsize.h>
3988
#include <qpen.h>
3989
#include <qpoint.h>
3990
//#include <qpixmap.h>
3991
#include <qpointarray.h>
3993
#include <kpixmap.h>
3996
// forward declaration of the KScribble classes
3997
class KScribbleView;
3999
/** KScribbleDoc provides a document object for a document-view model.
4001
* The KScribbleDoc class provides a document object that can be used in conjunction with the classes
4002
* KScribbleApp and KScribbleView to create a document-view model for MDI (Multiple Document Interface)
4003
* KDE 2 applications based on KApplication and KTMainWindow as main classes and QWorkspace as MDI manager widget.
4004
* Thereby, the document object is created by the KScribbleApp instance (and kept in a document list) and contains
4005
* the document structure with the according methods for manipulating the document
4006
* data by KScribbleView objects. Also, KScribbleDoc contains the methods for serialization of the document data
4007
* from and to files.
4008
* @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
4009
* @version KDevelop version 1.1 code generation
4011
class KScribbleDoc : public QObject
4015
friend KScribbleView;
4018
/** Constructor for the fileclass of the application */
4020
/** Destructor for the fileclass of the application */
4023
/** adds a view to the document which represents the document contents. Usually this is your main view. */
4024
void addView(KScribbleView *view);
4025
/** removes a view from the list of currently connected views */
4026
void removeView(KScribbleView *view);
4027
/** gets called if a view is removed or added */
4028
void changedViewList();
4029
/** returns the first view instance */
4030
KScribbleView* firstView(){ return pViewList->first(); };
4031
/** returns true, if the requested view is the last view of the document */
4033
/** This method gets called when the user is about to close a frame window. It checks, if more than one view
4034
* is connected to the document (then the frame can be closed), if pFrame is the last view and the document is
4035
* modified, the user gets asked if he wants to save the document.
4037
bool canCloseFrame(KScribbleView* pFrame);
4038
/** sets the modified flag for the document after a modifying action on the view connected to the document.*/
4039
void setModified(bool _m=true){ modified=_m; };
4040
/** returns if the document is modified or not. Use this to determine if your document needs
4041
* saving by the user on closing.
4043
bool isModified(){ return modified; };
4044
/** deletes the document's contents */
4045
void deleteContents();
4046
/** initializes the document generally */
4048
/** closes the acutal document */
4049
void closeDocument();
4050
/** loads the document by filename and format and emits the updateViews() signal */
4051
bool openDocument(const QString &filename, const char *format=0);
4052
/** saves the document under filename and format.*/
4053
bool saveDocument(const QString &filename, const char *format=0);
4054
/** sets the path to the file connected with the document */
4055
void setPathName(const QString &name);
4056
/** returns the pathname of the current document file*/
4057
const QString& pathName() const;
4059
/** sets the filename of the document */
4060
void setTitle(const QString &title);
4061
/** returns the title of the document */
4062
const QString& title() const;
4063
/** get the current Pen */
4064
const QPen currentPen(){ return pen;};
4065
/** returns the pen width */
4066
const int penWidth() { return pen.width(); }
4067
/** returns the pen color */
4068
const QColor penColor(){ return pen.color(); }
4069
/** sets the pen width */
4070
void setPenWidth( int w ){ pen.setWidth( w ); }
4071
/** sets the pen color */
4072
void setPenColor( const QColor &c ){ pen.setColor( c ); }
4073
/** sets the pen style by a second toolbar */
4074
void setPenStyle( PenStyle s){ pen.setStyle(s);}
4075
/** clears the document contents */
4076
void editClearAll();
4078
/** get the document size */
4079
const QSize docSize(){ return size;};
4080
/** sets the pixmap contents. Used by KScribbleApp
4081
to create a new document by drop events */
4082
void setPixmap(KPixmap pix) { buffer=pix;};
4083
void resizeDocument(QSize m_size) { size=m_size; };
4085
/** calls repaint() on all views connected to the document object and is called by the view by
4086
* which the document has been changed.
4087
* As this view normally repaints itself, it is excluded from the paintEvent.
4089
void updateAllViews(KScribbleView *sender);
4094
QPointArray polyline;
4098
/** the modified flag of the current document */
4102
/** the list of the views currently connected to the document */
4103
QList<KScribbleView> *pViewList;
4107
#endif // KSCRIBBLEDOC_H
4112
<sect1 id="kscribbledoc.cpp">
4113
<title>kscribbledoc.cpp</title>
4115
<para><programlisting>/***************************************************************************
4116
kscribbledoc.cpp - description
4118
begin : Mon Jan 31 11:05:05 CET 2000
4119
copyright : (C) 2000 by Ralf Nolden
4120
email : Ralf.Nolden@post.rwth-aachen.de
4121
***************************************************************************/
4123
/***************************************************************************
4125
* This program is free software; you can redistribute it and/or modify *
4126
* it under the terms of the GNU General Public License as published by *
4127
* the Free Software Foundation; either version 2 of the License, or *
4128
* (at your option) any later version. *
4130
***************************************************************************/
4132
// include files for Qt
4133
#include <qdir.h>
4134
#include <qfileinfo.h>
4135
#include <qwidget.h>
4137
// include files for KDE
4138
#include <klocale.h>
4139
#include <kmessagebox.h>
4140
#include <kfiledialog.h>
4142
// application specific includes
4143
#include "kscribbledoc.h"
4144
#include "kscribble.h"
4145
#include "kscribbleview.h"
4148
KScribbleDoc::KScribbleDoc()
4150
pViewList = new QList<KScribbleView>;
4151
pViewList->setAutoDelete(false);
4154
KScribbleDoc::~KScribbleDoc()
4159
void KScribbleDoc::addView(KScribbleView *view)
4161
pViewList->append(view);
4165
void KScribbleDoc::removeView(KScribbleView *view)
4167
pViewList->remove(view);
4168
if(!pViewList->isEmpty())
4174
void KScribbleDoc::changedViewList(){
4177
if((int)pViewList->count() == 1){
4178
w=pViewList->first();
4179
w->setCaption(m_title);
4183
for( i=1,w=pViewList->first(); w!=0; i++, w=pViewList->next())
4184
w->setCaption(QString(m_title+":%1").arg(i));
4188
bool KScribbleDoc::isLastView() {
4189
return ((int) pViewList->count() == 1);
4193
void KScribbleDoc::updateAllViews(KScribbleView *sender)
4196
for(w=pViewList->first(); w!=0; w=pViewList->next())
4198
w->update(sender);
4203
void KScribbleDoc::setPathName(const QString &name)
4206
m_title=QFileInfo(name).fileName();
4209
const QString& KScribbleDoc::pathName() const
4214
void KScribbleDoc::setTitle(const QString &title)
4219
const QString &KScribbleDoc::title() const
4225
void KScribbleDoc::closeDocument()
4230
for(w=pViewList->first(); w!=0; w=pViewList->next())
4238
w=pViewList->first();
4243
bool KScribbleDoc::newDocument()
4245
/////////////////////////////////////////////////
4246
// TODO: Add your document initialization code here
4247
size=QSize(300,200 );
4248
pen=QPen( Qt::black, 3 );
4249
polyline=QPointArray(3);
4250
buffer.resize(size);
4251
buffer.fill( Qt::white );
4252
/////////////////////////////////////////////////
4257
bool KScribbleDoc::openDocument(const QString &filename, const char *format /*=0*/)
4260
QFile f( filename );
4261
// if ( !f.open( IO_ReadOnly ) )
4263
/////////////////////////////////////////////////
4264
// TODO: Add your document opening code here
4265
if(!buffer.load( filename, format ))
4268
/////////////////////////////////////////////////
4272
m_filename=filename;
4273
m_title=QFileInfo(f).fileName();
4277
bool KScribbleDoc::saveDocument(const QString &filename, const char *format /*=0*/)
4279
QFile f( filename );
4280
// if ( !f.open( IO_WriteOnly ) )
4283
/////////////////////////////////////////////////
4284
// TODO: Add your document saving code here
4285
if(!buffer.save( filename, format ))
4287
/////////////////////////////////////////////////
4292
m_filename=filename;
4293
m_title=QFileInfo(f).fileName();
4297
void KScribbleDoc::deleteContents()
4299
/////////////////////////////////////////////////
4300
// TODO: Add implementation to delete the document contents
4301
buffer.fill( Qt::white );
4302
/////////////////////////////////////////////////
4306
bool KScribbleDoc::canCloseFrame(KScribbleView* pFrame)
4315
switch(KMessageBox::warningYesNoCancel(pFrame, i18n("The current file has been modified.\n"
4316
"Do you want to save it?"),title()))
4318
case KMessageBox::Yes:
4319
if(title().contains(i18n("Untitled")))
4321
saveName=KFileDialog::getSaveFileName(QDir::currentDirPath(),
4322
i18n("*|All files"), pFrame, i18n("Save as..."));
4323
if(saveName.isEmpty())
4327
saveName=pathName();
4329
if(!saveDocument(saveName))
4331
switch(KMessageBox::warningYesNo(pFrame,i18n("Could not save the current document !\n"
4332
"Close anyway ?"), i18n("I/O Error !")))
4334
case KMessageBox::Yes:
4336
case KMessageBox::No:
4343
case KMessageBox::No:
4346
case KMessageBox::Cancel:
4358
void KScribbleDoc::editClearAll()
4368
<sect1 id="kscribbleview.h">
4369
<title>kscribbleview.h</title>
4371
<para><programlisting>/***************************************************************************
4372
kscribbleview.h - description
4374
begin : Mon Jan 31 11:05:05 CET 2000
4375
copyright : (C) 2000 by Ralf Nolden
4376
email : Ralf.Nolden@post.rwth-aachen.de
4377
***************************************************************************/
4379
/***************************************************************************
4381
* This program is free software; you can redistribute it and/or modify *
4382
* it under the terms of the GNU General Public License as published by *
4383
* the Free Software Foundation; either version 2 of the License, or *
4384
* (at your option) any later version. *
4386
***************************************************************************/
4388
#ifndef KSCRIBBLEVIEW_H
4389
#define KSCRIBBLEVIEW_H
4391
#ifdef HAVE_CONFIG_H
4392
#include <config.h>
4395
// include files for Qt
4396
#include <qscrollview.h>
4397
#include <kpixmap.h>
4401
/** The KScribbleView class provides the view widget for the document instance connected to it and is displayed
4402
* as a MDI child window in the main view area of the KScribbleApp class instance. The KScribbleApp
4403
* class also has an eventFilter() method that gets installed on every KScribbleView instance to
4404
* control events of the type QEvent::Close.The document connected to the view instance keeps a list
4405
* of all view that represent the document contents as there can be more than one view. Views get created in
4406
* KScribbleApp::createClient() and automatically added to the list of views.
4407
* The KScribbleView class inherits QWidget as a base. Another possible inheritance besides specialized
4408
+ widgets could be QMainWindow so that you can easily set up the main area of your view by setting another view
4409
* as main widget (QMainWindow::setMainWidget() ).
4410
* NOTE: The close event always has to be empty (DON`T CALL QWidget::closeEvent(e) in closeEvent())
4411
* because the installed event filter can only manage a forward implementation. If the QCloseEvent
4412
* is received by the KScribbleView, the overwritten event handler has to do nothing as the eventFilter
4413
* has set accept() or ignore() already. If QWidget::closeEvent() is called again, the default event
4414
* handler will accept the close event and the window gets destroyed even if the installed eventFilter
4415
* has set the event to be ignored.
4416
* @author Source Framework Automatically Generated by KDevelop, (c) The KDevelop Team.
4417
* @version KDevelop version 1.1 code generation
4419
class KScribbleView : public QScrollView
4423
friend KScribbleDoc;
4426
/** Constructor for the view
4427
* @param pDoc your document instance that the view represents. Create a document
4428
* before calling the constructor or connect an already existing document to a new MDI child widget.*/
4429
KScribbleView(KScribbleDoc* pDoc, QWidget* parent, const char *name, int wflags);
4430
/** Destructor for the main view */
4432
/** returns a pointer to the document connected to the view*/
4433
KScribbleDoc *getDocument() const;
4434
/** gets called to redraw the document contents if it has been modified */
4435
void update(KScribbleView* pSender);
4436
/** contains the implementation for printing functionality and gets called by KScribbleApp::slotFilePrint() */
4437
void print(QPrinter *pPrinter);
4438
/** cuts out a selection */
4439
void cutSelection();
4440
/** copies a selection to the clipboard */
4441
void copySelection();
4442
/** pastes the clipboard contents to a selection that can be inserted into the picture */
4443
void pasteSelection();
4447
/** overwritten QWidget::closeEvent() to catch closing views. Does nothing, as the closeEvents for
4448
* KScribbleView's are processed by KScribbleApp::eventFilter(), so this overwitten closeEvent is necessary
4449
* and has to be empty. Don't overwrite this method !
4451
virtual void closeEvent(QCloseEvent* );
4452
/** overwritten to interpret key events for scrollbars */
4453
virtual void keyPressEvent( QKeyEvent* );
4454
/** changed from mousePressEvent() overwriting QScrollView method */
4455
virtual void viewportMousePressEvent( QMouseEvent* );
4456
/** changed from mouseReleaseEvent() overwriting QScrollView method */
4457
virtual void viewportMouseReleaseEvent( QMouseEvent* );
4458
/** On paste actions inserts the pasted clipboard contents */
4459
virtual void viewportMouseDoubleClickEvent(QMouseEvent* e);
4460
/** changed from mouseMoveEvent() overwriting QScrollView method */
4461
virtual void viewportMouseMoveEvent( QMouseEvent* );
4462
/** changed from resizeEvent() overwriting QScrollView method */
4463
// virtual void viewportResizeEvent( QResizeEvent* );
4464
/** changed from paintEvent() overwriting QScrollView method */
4465
virtual void viewportPaintEvent( QPaintEvent* );
4467
virtual void viewportDragEnterEvent ( QDragEnterEvent * );
4469
virtual void viewportDragMoveEvent ( QDragMoveEvent * );
4471
virtual void viewportDragLeaveEvent ( QDragLeaveEvent * );
4473
virtual void viewportDropEvent ( QDropEvent * );
4475
/** the document instance */
4482
enum Action{IDLE=0, DRAW, SELECT, PASTE, DRAG} action;
4485
#endif // KSCRIBBLEVIEW_H
4490
<sect1 id="kscribbleview.cpp">
4491
<title>kscribbleview.cpp</title>
4493
<para><programlisting>/***************************************************************************
4494
kscribbleview.cpp - description
4496
begin : Mon Jan 31 11:05:05 CET 2000
4497
copyright : (C) 2000 by Ralf Nolden
4498
email : Ralf.Nolden@post.rwth-aachen.de
4499
***************************************************************************/
4501
/***************************************************************************
4503
* This program is free software; you can redistribute it and/or modify *
4504
* it under the terms of the GNU General Public License as published by *
4505
* the Free Software Foundation; either version 2 of the License, or *
4506
* (at your option) any later version. *
4508
***************************************************************************/
4509
#include <iostream.h>
4511
// include files for Qt
4512
#include <qprinter.h>
4513
#include <qpainter.h>
4514
#include <qdir.h>
4515
#include <qsize.h>
4516
#include <qclipboard.h>
4517
#include <qimage.h>
4518
#include <qdragobject.h>
4520
// include files for KDE
4521
#include <kiconloader.h>
4523
// application specific includes
4524
#include "kscribbleview.h"
4525
#include "kscribbledoc.h"
4526
#include "kscribble.h"
4529
KScribbleView::KScribbleView(KScribbleDoc* pDoc, QWidget *parent, const char* name, int wflags)
4530
: QScrollView(parent, name, wflags | WPaintClever | WNorthWestGravity | WRepaintNoErase)
4532
cb = QApplication::clipboard();
4533
viewport()->setAcceptDrops(true);
4534
setDragAutoScroll(true);
4537
viewport()->setCursor( Qt::crossCursor );
4538
QSize size=doc->docSize();
4539
resizeContents(size.width(), size.height());
4543
KScribbleView::~KScribbleView()
4547
KScribbleDoc *KScribbleView::getDocument() const
4552
void KScribbleView::update(KScribbleView* pSender){
4554
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4557
void KScribbleView::print(QPrinter *pPrinter)
4559
if (pPrinter->setup(this))
4564
///////////////////////////////
4565
// TODO: add your printing code here
4566
p.drawPixmap(0,0,doc->buffer);
4567
///////////////////////////////
4572
/** cuts out a selection */
4573
void KScribbleView::cutSelection(){
4574
select=select.normalize();
4576
cb_pix.resize(select.size());
4577
// copy selection to cb_pix and copy to clipboard
4578
bitBlt(&cb_pix, 0, 0,
4579
&doc->buffer, select.x()+contentsX(), select.y()+contentsY(), cb_pix.width(), cb_pix.height());
4580
cb->setPixmap(cb_pix);
4581
// fill cb_pix with white and copy to selection area
4582
cb_pix.fill(Qt::white);
4583
bitBlt(&doc->buffer, select.x()+contentsX(), select.y()+contentsY(),
4584
&cb_pix, 0, 0, cb_pix.width(), cb_pix.height());
4586
doc->setModified();
4587
doc->updateAllViews(this);
4588
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4590
/** copies a selection to the clipboard */
4591
void KScribbleView::copySelection(){
4592
select=select.normalize();
4594
cb_pix.resize(select.size());
4595
// copy selection to cb_pix and copy to clipboard
4596
bitBlt(&cb_pix, 0, 0,
4597
&doc->buffer, select.x()+contentsX(), select.y()+contentsY(),cb_pix.width(), cb_pix.height());
4598
cb->setPixmap(cb_pix);
4600
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4602
/** pastes the clipboard contents to a selection that can be inserted into the picture */
4603
void KScribbleView::pasteSelection(){
4604
select=cb->pixmap().rect();
4606
viewport()->setCursor( Qt::sizeAllCursor );
4609
void KScribbleView::closeEvent(QCloseEvent* e){
4611
// DO NOT CALL QWidget::closeEvent(e) here !!
4612
// This will accept the closing by QCloseEvent::accept() by default.
4613
// The installed eventFilter() in KScribbleApp takes care for closing the widget
4614
// or ignoring the close event
4618
void KScribbleView::keyPressEvent( QKeyEvent *e )
4620
switch (e->key())
4635
setContentsPos(0,0);
4638
setContentsPos(0,viewport()->height()-viewport()->height());
4641
scrollBy( 0, -viewport()->height() );
4644
scrollBy( 0, viewport()->height() );
4650
void KScribbleView::viewportMousePressEvent( QMouseEvent *e )
4652
if ( e->button() == LeftButton && action == IDLE)
4655
doc->polyline[2] = doc->polyline[1] = doc->polyline[0] = viewportToContents(e->pos());
4656
doc->updateAllViews(this);
4658
else if ( e->button() == RightButton && action == IDLE)
4661
QPoint pt=e->pos();
4662
int x = pt.x() > contentsWidth() ? contentsWidth() : pt.x();
4663
int y = pt.y() > contentsHeight() ? contentsHeight() : pt.y();
4664
select.setLeft(x-1);
4666
select.setRight(x-1);
4667
select.setBottom(y-1);
4669
else if( action == SELECT )
4672
select=select.normalize();
4674
if(select.contains(e->pos(), true)) // point inside the selection
4676
tmp.resize(select.size());
4677
bitBlt(&tmp, 0, 0,
4678
&doc->buffer, select.x()+contentsX(), select.y()+contentsY(), tmp.width(), tmp.height());
4679
QImage img =tmp.convertToImage();
4680
QDragObject *d = new QImageDrag( img, viewport() );
4681
d->setPixmap(BarIcon("filenew"));
4686
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4688
else if( action == PASTE )
4690
if ( e->button() == RightButton )
4693
viewport()->setCursor( Qt::crossCursor );
4695
QPoint mv_pt (viewport()->height(), viewport()->width());
4696
if(QRect(0,0,mv_pt.x(),mv_pt.y()).contains(e->pos()))
4697
select.moveCenter(e->pos());
4700
select.moveBottomRight(mv_pt);
4702
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4706
void KScribbleView::viewportMouseReleaseEvent( QMouseEvent *e )
4708
if ( action == DRAW )
4711
doc->updateAllViews(this);
4713
if ( action == SELECT)
4715
QPoint pt=e->pos();
4716
int x = pt.x() > 0 ? pt.x() : 0;
4717
int y = pt.y() > 0 ? pt.y() : 0;
4719
select.setBottom(y);
4720
QSize size=doc->docSize();
4721
select = select.intersect(QRect(0,0,size.width(), size.height()));
4725
/** On paste actions inserts the pasted clipboard contents
4727
void KScribbleView::viewportMouseDoubleClickEvent(QMouseEvent* e)
4729
if( action == PASTE )
4732
select.moveCenter(e->pos());
4733
viewport()->setCursor( Qt::crossCursor );
4735
cb_pix.resize(cb->pixmap().size());
4736
cb_pix=cb->pixmap();
4737
bitBlt( &doc->buffer, contentsX()+select.x(), contentsY()+select.y(),
4738
&cb_pix, 0,0 , select.width(),select.height() );
4739
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4740
doc->setModified();
4741
doc->updateAllViews(this);
4746
void KScribbleView::viewportMouseMoveEvent( QMouseEvent *e )
4748
if ( action == DRAW )
4751
painter.begin( &doc->buffer );
4752
painter.setPen( doc->currentPen() );
4753
doc->polyline[2] = doc->polyline[1];
4754
doc->polyline[1] = doc->polyline[0];
4755
doc->polyline[0] = viewportToContents(e->pos());
4756
painter.drawPolyline( doc->polyline );
4759
QRect r = doc->polyline.boundingRect();
4761
r.setLeft( r.left() - doc->penWidth() );
4762
r.setTop( r.top() - doc->penWidth() );
4763
r.setRight( r.right() + doc->penWidth() );
4764
r.setBottom( r.bottom() + doc->penWidth() );
4766
bitBlt(viewport(), r.x()-contentsX(), r.y()-contentsY() ,
4767
&doc->buffer, r.x(), r.y(), r.width(), r.height() );
4768
doc->setModified();
4769
doc->updateAllViews(this);
4771
if ( action == SELECT )
4773
QPoint pt=e->pos();
4774
select.setWidth(select.x()+pt.x());
4775
select.setHeight(select.y()+pt.y());
4776
select.setRight(pt.x());
4777
select.setBottom(pt.y());
4778
QSize size=doc->docSize();
4779
select = select.intersect(QRect(0,0,size.width(), size.height()));
4780
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4782
if( action == PASTE )
4784
QPoint mv_pt (viewport()->height(), viewport()->width());
4785
if(QRect(0,0,mv_pt.x(),mv_pt.y()).contains(e->pos()))
4786
select.moveCenter(e->pos());
4789
select.moveBottomRight(mv_pt);
4791
QRect pm_rect=cb->pixmap().rect();
4792
select.setWidth(pm_rect.width());
4793
select.setHeight(pm_rect.height());
4794
QSize size=doc->docSize();
4795
select = select.intersect(QRect(0,0,size.width(), size.height()));
4796
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4797
doc->setModified();
4798
doc->updateAllViews(this);
4802
//void KScribbleView::viewportResizeEvent( QResizeEvent *e )
4806
void KScribbleView::viewportPaintEvent( QPaintEvent *e )
4808
bitBlt( viewport(),0,0, &doc->buffer,contentsX() ,contentsY() );
4810
if( action == PASTE )
4812
tmp.resize(cb->pixmap().size());
4813
tmp=cb->pixmap();
4815
if( action == PASTE || action == DRAG )
4817
QSize size=doc->docSize();
4818
select = select.intersect(QRect(0,0,size.width(), size.height()));
4819
if(select.intersects(e->rect()))
4820
bitBlt(viewport(), select.x(), select.y(), &tmp, 0, 0, select.width(), select.height());
4822
if( action == PASTE || action == DRAG || action == SELECT )
4824
// if(select.intersects(e->rect()))
4826
QPainter paint_area;
4827
paint_area.begin(viewport());
4828
paint_area.setPen(QPen(Qt::black, 0, DashLine));
4829
paint_area.drawRect( select );
4833
QScrollView::viewportPaintEvent(e);
4836
void KScribbleView::viewportDragEnterEvent ( QDragEnterEvent * e)
4838
e->accept(QImageDrag::canDecode(e));
4842
void KScribbleView::viewportDragMoveEvent ( QDragMoveEvent * e)
4845
if ( QImageDrag::decode(e, img) ){
4846
tmp.resize(img.size());
4847
tmp.convertFromImage(img);
4848
select.setWidth(tmp.width());
4849
select.setHeight(tmp.height());
4850
select.moveCenter(e->pos());
4851
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4855
void KScribbleView::viewportDragLeaveEvent ( QDragLeaveEvent * )
4858
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4861
void KScribbleView::viewportDropEvent ( QDropEvent * e)
4864
if ( QImageDrag::decode(e, img) )
4866
tmp.resize(img.size());
4867
tmp.convertFromImage(img);
4868
select.setWidth(tmp.width());
4869
select.setHeight(tmp.height());
4870
select.moveCenter(e->pos());
4871
bitBlt(&doc->buffer, select.x()+contentsX(), select.y()+contentsY(),
4872
&tmp, 0, 0, tmp.width(), tmp.height());
4873
doc->setModified();
4874
doc->updateAllViews(this);
4877
viewport()->repaint(0,0,visibleWidth(), visibleHeight(), false);
4883
<sect1 id="kpenbrushdlg.h">
4884
<title>kpenbrushdlg.h</title>
4886
<para><programlisting>/***************************************************************************
4887
kpenbrushdlg.h - description
4889
begin : Fri Jul 23 1999
4890
copyright : (C) 1999 by Ralf Nolden
4891
email : Ralf.Nolden@post.rwth-aachen.de
4892
***************************************************************************/
4894
/***************************************************************************
4896
* This program is free software; you can redistribute it and/or modify *
4897
* it under the terms of the GNU General Public License as published by *
4898
* the Free Software Foundation; either version 2 of the License, or *
4899
* (at your option) any later version. *
4901
***************************************************************************/
4904
#ifndef KPENBRUSHDLG_H
4905
#define KPENBRUSHDLG_H
4907
//Generated area. DO NOT EDIT!!!(begin)
4908
#include <qwidget.h>
4909
#include <qspinbox.h>
4910
#include <qlabel.h>
4911
#include <qpushbutton.h>
4912
//Generated area. DO NOT EDIT!!!(end)
4914
#include <qdialog.h>
4915
#include <klocale.h>
4918
*@author Ralf Nolden
4921
class KPenBrushDlg : public QDialog {
4924
KPenBrushDlg(int curr, QWidget *parent=0, const char *name=0);
4927
int width() { return width_spbox->value(); };
4934
//Generated area. DO NOT EDIT!!!(begin)
4935
QSpinBox *width_spbox;
4936
QLabel *width_label;
4937
QPushButton *default_btn;
4938
QPushButton *ok_btn;
4939
QPushButton *cancel_btn;
4940
//Generated area. DO NOT EDIT!!!(end)
4951
<sect1 id="kpenbrushdlg.cpp">
4952
<title>kpenbrushdlg.cpp</title>
4954
<para><programlisting>/***************************************************************************
4955
kpenbrushdlg.cpp - description
4957
begin : Fri Jul 23 1999
4958
copyright : (C) 1999 by Ralf Nolden
4959
email : Ralf.Nolden@post.rwth-aachen.de
4960
***************************************************************************/
4962
/***************************************************************************
4964
* This program is free software; you can redistribute it and/or modify *
4965
* it under the terms of the GNU General Public License as published by *
4966
* the Free Software Foundation; either version 2 of the License, or *
4967
* (at your option) any later version. *
4969
***************************************************************************/
4971
#include "kpenbrushdlg.h"
4972
#include <qwhatsthis.h>
4973
#include <kapp.h>
4975
KPenBrushDlg::KPenBrushDlg(int curr, QWidget *parent, const char *name)
4976
: QDialog(parent,name,true,WStyle_ContextHelp)
4979
QWhatsThis::add(width_spbox,i18n("Select brush width"));
4981
width_spbox->setValue(curr);
4982
connect(default_btn, SIGNAL(clicked()), this, SLOT(slotDefault()));
4983
connect(ok_btn, SIGNAL(clicked()), this, SLOT(accept()));
4984
connect(cancel_btn, SIGNAL(clicked()), this, SLOT(reject()));
4987
KPenBrushDlg::~KPenBrushDlg(){
4990
void KPenBrushDlg::slotDefault(){
4991
width_spbox->setValue(3);
4997
<sect1 id="kpenbrushdlgdata.cpp">
4998
<title>kpenbrushdlgdata.cpp</title>
5000
<para><programlisting>/**********************************************************************
5001
--- KDevelop (KDlgEdit) generated file ---
5003
Last generated: Fri Jul 23 10:43:10 1999
5005
DO NOT EDIT!!! This file will be automatically
5006
regenerated by KDevelop. All changes will be lost.
5008
**********************************************************************/
5009
#include <kapp.h>
5010
#include "kpenbrushdlg.h"
5012
void KPenBrushDlg::initDialog(){
5013
this->resize(370,210);
5014
this->setMinimumSize(0,0);
5015
width_spbox= new QSpinBox(this,"width_spbox");
5016
width_spbox->setGeometry(150,50,100,25);
5017
width_spbox->setMinimumSize(0,0);
5018
width_spbox->setValue(1);
5019
width_spbox->setRange(1,99);
5021
width_label= new QLabel(this,"width_label");
5022
width_label->setGeometry(20,50,120,25);
5023
width_label->setMinimumSize(0,0);
5024
width_label->setText(i18n("Pen Width:"));
5026
default_btn= new QPushButton(this,"default");
5027
default_btn->setGeometry(30,160,100,30);
5028
default_btn->setMinimumSize(0,0);
5029
default_btn->setText(i18n("Default"));
5030
default_btn->setAutoDefault(true);
5032
ok_btn= new QPushButton(this,"ok");
5033
ok_btn->setGeometry(140,160,100,30);
5034
ok_btn->setMinimumSize(0,0);
5035
ok_btn->setText(i18n("OK"));
5036
ok_btn->setAutoDefault(true);
5038
cancel_btn= new QPushButton(this,"cancel");
5039
cancel_btn->setGeometry(250,160,100,30);
5040
cancel_btn->setMinimumSize(0,0);
5041
cancel_btn->setText(i18n("Cancel"));
5042
cancel_btn->setAutoDefault(true);
5048
<sect1 id="resource.h">
5049
<title>resource.h</title>
5051
<para><programlisting>/***************************************************************************
5052
resource.h - description
5054
begin : Mon Jan 31 11:05:05 CET 2000
5055
copyright : (C) 2000 by Ralf Nolden
5056
email : Ralf.Nolden@post.rwth-aachen.de
5057
***************************************************************************/
5059
/***************************************************************************
5061
* This program is free software; you can redistribute it and/or modify *
5062
* it under the terms of the GNU General Public License as published by *
5063
* the Free Software Foundation; either version 2 of the License, or *
5064
* (at your option) any later version. *
5066
***************************************************************************/
5071
#ifdef HAVE_CONFIG_H
5072
#include <config.h>
5075
///////////////////////////////////////////////////////////////////
5076
// resource.h -- contains macros used for commands
5079
///////////////////////////////////////////////////////////////////
5080
// COMMAND VALUES FOR MENUBAR AND TOOLBAR ENTRIES
5083
///////////////////////////////////////////////////////////////////
5084
// File-menu entries
5085
#define ID_FILE_NEW 10010
5086
#define ID_FILE_OPEN 10020
5087
#define ID_FILE_OPEN_RECENT 10030
5088
#define ID_FILE_CLOSE 10040
5090
#define ID_FILE_SAVE 10050
5091
#define ID_FILE_SAVE_AS 10060
5093
#define ID_FILE_PRINT 10070
5095
#define ID_FILE_QUIT 10080
5097
///////////////////////////////////////////////////////////////////
5098
// Edit-menu entries
5099
#define ID_EDIT_UNDO 11010
5100
#define ID_EDIT_COPY 11020
5101
#define ID_EDIT_CUT 11030
5102
#define ID_EDIT_PASTE 11040
5103
#define ID_EDIT_CLEAR_ALL 11050
5105
///////////////////////////////////////////////////////////////////
5107
#define ID_PEN_COLOR 14010
5108
#define ID_PEN_BRUSH 14020
5110
///////////////////////////////////////////////////////////////////
5111
// Draw-menu entries
5112
#define ID_DRAW_FIND 15010
5113
#define ID_DRAW_FREEHAND 15020
5114
#define ID_DRAW_LINE 15030
5115
#define ID_DRAW_RECT 15040
5116
#define ID_DRAW_RECT_FILL 15050
5117
#define ID_DRAW_CIRCLE 15060
5118
#define ID_DRAW_CIRCLE_FILL 15070
5119
#define ID_DRAW_ELLIPSE 15080
5120
#define ID_DRAW_ELLIPSE_FILL 15090
5121
#define ID_DRAW_SPRAY 15100
5122
#define ID_DRAW_FILL 15110
5123
#define ID_DRAW_ERASE 15120
5125
///////////////////////////////////////////////////////////////////
5126
// View-menu entries
5127
#define ID_VIEW_TOOLBAR 12010
5128
#define ID_VIEW_STATUSBAR 12020
5130
///////////////////////////////////////////////////////////////////
5131
// Window-menu entries
5132
#define ID_WINDOW_NEW_WINDOW 13010
5133
#define ID_WINDOW_CASCADE 13020
5134
#define ID_WINDOW_TILE 13030
5136
///////////////////////////////////////////////////////////////////
5137
// Help-menu entries
5138
#define ID_HELP_CONTENTS 1002
5139
#define ID_HELP_WHATS_THIS 1003
5140
///////////////////////////////////////////////////////////////////
5141
// General application values
5142
#define ID_STATUS_MSG 1001
5143
#define TOOLS_TOOLBAR 1
5144
#endif // RESOURCE_H
5150
<![ %addindex; [ &docindex; ]]>
5156
time-stamp-format:"%02d/%02m/%04y"
5157
time-stamp-line-limit:100
5158
time-stamp-start:"<date>"
5159
time-stamp-end:"</date>"
5160
sgml-namecase-general: t
5161
sgml-namecase-entity: nil
5162
sgml-minimize-attributes: nil
5165
sgml-general-insert-case: lower