]>

# The KDE 2 Developer's Guide

The KDE 2 information guide to development, I/O slaves and KOM/OpenParts
Ralf
Nolden
Kurt
Granroth
Simon
Hausmann
David
Sweet
Preston
Brown
Torben
Weis
Bern
Wuebben
21/03/2000
1.02.00

This Handbook is a collection of various documentation about KDE 2 development. Each chapter remains the copyright of the original author mentioned at the beginnig of the chapters and in section Copyright.

KDE
KDevelop
I/O slaves
KOM
OpenParts

## Preface

This handbook has been assembled to provide KDE developers the information they will need when porting their applications to KDE 2, currently under development. KDE 2 is based on the Qt 2.x and KDE 1.1.x libraries, whereby the KDE libraries are ported to work with the current Qt library. Further, the KDE libraries will introduce a component technology based on the MICO CORBA implementation which is developed and tested with the KOffice suite.

As KDE application developers not involved in implementing for the KDE core team will probably want to port their applications, they will not only have to watch out for the changes in Qt, but also for KDE's improvements and additions. As KDE 1.x applications will not only be binary incompatible but also sourcecode incompatible, we want to provide enough information that can be used by developers that are using the currently stable KDE 1.x implementation for applications that are suitable for production use, so transitioning will be made less complicated and further implementations in current projects can be reviewed for possible problems that will arise when porting.

Also the usual KDE developer does not have too much information about CORBA, not to speak of KOM. Therefore, this handbook tries to give an insight where to get information and how the current implementation works in general. This will enable you to find out where your applications can probably take advantage of distributed component technology where needed. In conjunction with the latest development, this version of the KDE 2 Developer's Guide also contains a short HOWTO for the new DCOP (Desktop COmmunications Protocol), which was created due to performance problems of local desktop applications using CORBA. Therefore, a few things mentioned and explained in the documents relying on CORBA technology can be out of date and reading yourself into the dcop library reference and reviewing current KDE CVS sourcecode will give you probably more up-to-date information on IPC (Inter-Process-Communication) between local desktop applications using DCOP.

I want to thank all authors that were willing to contribute their current documentation to this handbook and hope this collection will be of good use to those who want to stay on the bleeding edge of KDE development.

Ralf Nolden

## The KDE Developer's mini-HOWTO

David Sweet &<;dsweet@chaos.umd.edu&>;

March, 11, 2000

To help develop KDE or a KDE application you need to know how to (i) find KDE information and code, (ii) use CVS, and (iii) compile. This document aims to help to you do these things without losing too much hair. This HOWTO is geared toward new developers, occasional developers, and anyone considering contributing the the most excellent free software project that is KDE. It addresses questions that I and others have been asking on the kde-devel mailing list recently along with some more general information. You can find a current version of this document at the KDE Developer's HOWTO homepage. To contribute, comment, or correct, please email me at dsweet@chaos.umd.edu

### Information and Source Code

#### General Information

The K Desktop Environment is an open source Unix desktop created by volunteers from around the world. There are many ways to contribute to the project. You can write new code, improve old code, write documentation, translate to other languages, create artwork, sounds, & music, report bugs, and suggest new features. If you would like to develop code, read on. If you would like to contribute in another way, please visit the KDE web site (\|\| name="http://www.kde.org">) for more information.

KDE uses the Qt toolkit which is developed by Troll Tech (http://www.troll.no). The main components of the desktop are a file manager (kfm), window manager (kwm), and panel (kpanel). There are many more utilities and applications which are included in the base distribution and available elsewhere. The primary programming language used for development is C++, although bindings are available for Python (pyKDE). The KDE code consists of libraries containing classes for, for example: (libkdecore) an application base (KApplication), accessing configuration files (KConfig), launching external processs (KProcess); (libkdeui) widgets (KEdit, KFontDialog, KToolBar, etc.); and other utility classes like KFileDialog (a file dialog) and KSpell (a spellchecker).

Various desktop, configuration, and system administration utilities are also included in the distribution. These are some of the things KDE developers work on. A more recent creation is KOffice, a productivity suite which includes a word processor, spreadsheet, and presentation tool The currently released version of KDE is 1.1.2.

KDE 2.0, which is, perhaps, the main focus of development right now, will include rewrites or major updates of kfm, khtmlw (an HTML widget), kpanel, kmail, and maybe kwm, unicode support, Inter-Process-Communication protocols and much more.

#### URLs

- The main KDE web site is at: http://www.kde.org.

- The developer's library is at: http://developer.kde.org.

- Bug reports can be found at (and sent to): http://bugs.kde.org.

- Troll Tech (makers of the Qt toolkit) main site: http://www.troll.no.

- KDE Source Code and binaries: ftp://ftp.kde.org (or mirrors)

- Qt Source Code and binaries: ftp://ftp.troll.no

#### Mailing lists

The list kde-devel is for KDE developers in general. The kde-koffice mailing list is for developers interested in koffice, and kfm-devel is for kfm (the file manager) developers.

Send a message to either kde-devel-request or koffice-request with the message "subscribe myid@myserver" (where myid@myserver stands for your email address). Go to the KDE mail page a click on "Mailing Lists" for more information about other KDE mailing lists and the mailing list archive.

Toll Tech provides a list for users of the Qt 2.1 snapshots (discussed below). You may subscribe to this by sending an email to snapshot-users-request@troll.no with the message "subscribe".

As a KDE developer you may want or need a kde email address, like joedeveloper@kde.org. To obtain one, send an email with a polite request to Martin Konold at konold@kde.org.

To gain access to the KDE CVS respository (discussed below), you firsly need to have a good reason for wanting access. For example, you may be maintaining a CVS module. If you think you have a good reason, send a polite email request to Stephan Kulow &<;coolo@kde.org&>;.

You'll also need to send him an encrypted password to get the account set up. To create your encrpyted password, type:

    	perl -e print\ crypt\('passwd','sa'\)\.\"\\n\"

where `passwd` is your choice of password and `sa` is two random characters from the set (a-zA-Z0-9./). The output is your encrypted password.

### KDE CVS

#### Introduction

The KDE CVS (Concurrent Versions System) is the source code repository for the KDE project. You can access it via (i) WWW: http://www., (ii) cvs utility, (iii) cvsup utility, or (iv) snapshots. You will need CVS access only for (ii); (i), (iii), and (iv) are read-only methods and available to the public.

The web page for method (i) explains its usage. I haven't an explanation for method (iii), cvsup, yet. Please see http://www.kde.org/cvsup.html &&lcub;; -- Perhaps someone who uses it will contribute a short explanation of usage and information on obtaining it. -- &&rcub;;

The snapshots, (iv), are .tar.bz2 files which contain a section of KDE code (called "modules"; ex, kdelibs, kdeutils) as it looked on some specified day (specified in the filename: ex, kdelibs990517.tar.bz2). The snapshots are posted daily in ftp://ftp.kde.org/pub/kde/unstable/CVS/snapshots. &&lcub;; (ii) is described below. &&rcub;;

The repository (or, just "CVS") stores all of the changes made to the source code by all of the contributors so that changes may be undone. Each time a user makes a change (s)he includes a comment so that the devlopment of code can be more easily followed. These comments are sent to the kde-cvs mailing list. The CVS splits into &&lowbar;;branches&&lowbar;; which may contain different versions of the KDE project. For example, the two branches being developed now are KDE&&lowbar;;1&&lowbar;;1&&lowbar;;BRANCH and HEAD. They both are derived from the same code (i.e., if you "undid" enough of the changes made to either branch you'd reveal identical source code) but used for different purposes. Here are some branches and descriptions for your reference:

- KDE&&lowbar;;1&&lowbar;;1&&lowbar;;2&&lowbar;;RELEASE - code that was considered stable, released, and distributed as KDE 1.1.2

- HEAD - code which will become KDE 2.0. It is based on Qt 2.1, has a new kwm, kpanel, etc. LOTS of changes.

#### Using `cvs`

The cvs utility is probably on your system. If not, you should visit http://www.cyclic.com/cyclic-pages/howget.html To learn to use it I would recommend reading the man page! But, I've included explanations of some common functions below.

Let's assume for this section that your username is `joedeveloper`. As written below you should enter all of these commands from some base directory. (KDE/CVS is not a bad choice!)

Set the environment variable `CVSROOT` to

`:pserver:joedeveloper@cvs.kde.org:/home/kde.`

Listing CVS modules You can't do this directly unfortunately. You can, however, view the contents of the file /home/kde/modules with

    cvs -z6 co -c

The `-z6` option tells the server to compress the code at "level 6" before sending it to you. This may speed things up for you. (In this case, since the module listing is small, it may not matter.)

Checking out a module, e.g. kdelibs from HEAD

    cvs -z6 checkout -r HEAD kdelibs
    cvs -z6 checkout kdelibs

The -r options tells cvs which branch you want to checkout from. The default is the HEAD branch.

Checking out a module, ex. kdelibs, from KDE&&lowbar;;1&&lowbar;;1&&lowbar;;2&&lowbar;;RELEASE

    cvs -z6 checkout -r KDE_1_1_2_RELEASE kdelibs

Note: you could use co as an abbreviation for checkout.

Checking out an application from within a module (ex kjots, which is in the kdeutils module) from the HEAD branch

    (1)	cvs -z6 co -l kdeutils
    (2)	cvs -z6 co -l admin
    (3)	cvs -z6 co -l kdeutils/kjots
    (4)	cd kdeutils; ln -s ../admin

The `-l` in line (1) tells cvs not to recurse the subdirectories of kdeutils. This means will get the configure script and its companions (discussed below), but none of the application source code.

Line (2) gets the admin directory which contains support files for autoconf and friends. (This directory is retrieved automatically when checking out an entire module.)

Line (3) gets the kjots source.

Line (4) makes a link to the admin directory. (This is better than copying or moving the directory here. If you leave admin where cvs put if then you can easily update the admin directory with cvs. You could also make links to admin from any other modules you check out this way and thus have only one, up-to-date copy of admin.)

Updating source code you've previously checked out (ex. kdeutils/kjots)

    	cvs -z6 update -P -d kdeutils/kjots

The source code for kjots on your hard drive will be updated to match the code in the CVS. You don't need to specify the branch here. The correct branch is stored in `kdeutils/kjots/CVS/Tag`.

Commiting changes (putting them into CVS) (ex. kdeutils/kjots)

    cvs -z6 commit kdeutils/jots

You'll be prompted to edit a comment. Enter a short one which desribes the changes you're making with this commmit. (You can use your editor of choice by setting the EDITOR or CVSEDITOR environment variable.)

Adding a file (ex. kdeutils/kmyapp/greatnewcode.cpp)

    (create the file first!)
    cd kdeutils/kmyapp
    cvs add greatnewcode.cpp
    cvs commit

Deleting a file (ex. kdeutils/kmyapp/badoldcode.cpp)

    cd kdeutils/kmyapp
    rm badoldcode.cpp
    cvs remove badoldcode.cpp
    cvs commit

Adding a directory (a module, like a new app) (ex. kdeutils/kmyapp, with the source file kmysource.cpp)

    cd kdeutils
    mkdir kmyapp
    (create the kmyapp/kmysource.cpp file)
    cvs add kmyapp
    cvs add kmyapp/kmysource.cpp
    cvs commit    (actually puts the directory and file in the CVS)

Note: You need to have files in a directory to commit it. Removing a directory (a module, like a new app) (ex. kdeutils/kmyapp)

    cd kdeutils/kmyapp
    (delete all files, as described above in "Deleting a file")
    cd ..
    cvs -P update  (will remove the local kmyapp automatically)

### Compiling and Safe Development Practices (with configure)

Before you start downloading and compiling the latest sources you should be aware that there's a good chance they won't work! They are in a state of constant development so they could very well have bugs.

Knowing this, you should find a way to compile and run new KDE stuff without interfering with your existing stable KDE setup. Here's one way, using the HEAD branch as an example. We, again, assume your login is joedeveloper. We also assume that your home directory is in /home/joedeveloper. (This would be the case for Red Hat systems.)

#### Organizing

Make a directory called KDE in the home directory of your *user* account. You should not be doing any of this as root! Make another called KDE/CVS-HEAD. cd into that directory and get the KDE sources from the HEAD branch that you want. The (minumum) modules needed to use an application are: kdesupport, kdelibs. You should compile and install them in that order (see below for compiling). Others you might want are: kdebase, kdeutils, kdegraphics, etc. Now, make a directory called KDE/kde-HEAD. This is where you'll store the compiled code from the HEAD branch -- as well as Qt 2.1!

#### Compiling Qt 2.1

Let's get Qt 2.1. Take the KDE copy of the current Qt library from the KDE CVS with

    cvs -z6 co qt-copy
    cd qt-copy
    setenv QTDIR $PWD     (if you use csh/tcsh)
    OR
    export QTDIR=`pwd`    (if you use bash)
    make -f Makefile.cvs (creates symlinks -without that, qt will not compile !)
    ./configure   (-gif optionally)
    make

#### Compiling KDE (HEAD)

Now, for the KDE code. `cd` to KDE/CVS-HEAD/kdesupport. Type

    make -f Makefile.cvs
    ./configure --prefix=/home/joedeveloper/KDE/kde-HEAD
    --with-qt-dir=/home/joedeveloper/KDE/kde-HEAD/qt-copy
    --with-qt-libs=/home/joedeveloper/KDE/kde-HEAD/qt-copy/lib
    make

    If all goes well, then

    make install

    If not, try to fix things, then type

    make install

    Repeat this process for the other modules.  You should alter the
    ./configure line to read
    	./configure --prefix=/home/joedeveloper/KDE/kde-HEAD
      --with-qt-dir=/home/joedeveloper/KDE/kde-HEAD/qt-copy
      --with-qt-libs=/home/joedeveloper/KDE/kde-HEAD/qt-copy/lib

#### Compiling tips

- I usually put that long configure command into a file called `conf` and leave it in my CVS-HEAD directory. Then I can do a `../conf` from any module and get a good configure.

- It seems that you need to compile and install qimageio (part of Qt) separately. Just `cd` to `/$QTDIR/extensions/imageio/src` and type `make install`

### Documentation

It's important to document your application so that end-users can make the most of it. You should be clear and concise. Describe any non-standard installation, usage and UI features. There's no need to talk about how to use the File menu, for example, unless you've put some special entry on it. Include contact and bug-reporting information as well as a hyperlink to the application's home page. Writing documentation for KDE 2 should be done in docbook-sgml. Look at http://developer.kde.org for more details how to get the needed packages for installing the docbook tools. KDE extends these tools by the module kdesdk/ksgmltools. This module also contains information where to get the docbook tools, additional documentation as well as how to use docbook and KDE.

You should also document your widgets and other classes. You can include comments in your header ( `.h`) files which describe each `public` or `protected` method. It you do this as you write the methods it will make the documentation proceSss seem easier. (It may also help you be certain about what function(s) that method is to perform.) The script `kdoc` will turn your header files into beautiful class documentation. You can see examples of `kdoc` output if you look at http://www.ph.unimelb.edu.au/~ssk/kde/srcdoc/kdecore/index-long.html. This is the annotated list of the core KDE classes.

You can find `ksgmltools` in the KDE SDK which is available in the kdesdk module of CVS, `kdoc` in the kdoc module. (Methods for getting things from CVS are described above.)

#### KDOC: The KDE Class Documentation Tool for C++

It's called `kdoc` and is in the kdedoc subdirectory of the CVS module kdesdk.

If you mark up your header files like this:

      /**
       * Short description of this class
       *
       * This is a longer description of my class.  It does the following
       * @li Some bulleted thing
       * @li Some other bulleted thing
       *  You should use it when ...  It's not appropriate for ...
       *
       * @author My Name <myemail@wherever.edu>
       * @version 0.0.1
       **/

    class KMyClass
    {
            .
      /**
       * Describe method.  It takes &<;i&>;argument&<;/i&>; to mean ...
       *
       * @return A pointer to another class
       * @see KOtherClass
       **/
       KOtherClass *method (int argument);
    }

The text in the comments beginning with `/**` (two asterisks, mind you!) will be taken by `kdoc` as class documentation and formatted appropriately. You can use some HTML tags (like the &<;i&>; above). The `@author` and other tags are meaningful to `kdoc` and used for formatting.

To run `kdoc`, use the following command

     kdoc -dhtml -L$KDEDIR/share/kdoc -a Title header1.h header2.h ...

This instructs `kdoc` to generate HTML output (the default) in the `html` directory (by the `-d` option) from the specified header files. It uses `Title` as the title for the documentation. ( `-a` says to do work on all header files, even if they don't "ask for it". I haven't told you how to "ask for it." The `-L` tells kdoc where its libraries are.) Type `kdoc -h` for more information.

### Packaging and Submitting Code to KDE

#### Packaging

'Packing' means putting your (in this case source) code into some format that is

- easy for others to configure and install, and

- easy to transfer,

- can be indexed so that it is easy to find.

These three concepts are expanded upon in the next three subsections.

##### GNU Configure

The standard KDE application (or other code) source code package includes the GNU `configure` script which determines some information about the user's system and provides it to your source code as `&&num;;define` statements in a header file called `config.h`.

To get started with this, get the kdesdk from CVS using a method described above. Then prepare kexample, an example package, for your application

       cd kdesdk
       cd kexample
       make -f Makefile.cvs
       cd ..; cp -r kexample ~/KDE/kmyapp-0.0.1

(That last directory is just an example. Replace kmyapp with your application's (or widget's) name, and replace 0.0.1 with its version number.)

Now, we'll put your source code into the example package. Let's say your source code was in the directory & ;/KDE/KMyApp:

       cd ~/KDE/kmyapp-0.0.1
       mkdir kmyapp
       cp ~/KDE/KMyApp/*.cpp kmyapp
       cp ~/KDE/KMyApp/*.h kmyapp
       (There may be other files to copy, but leave your old Makefile behind!)

Now, edit kmyapp-0.0.1/Makefile.am and change the line

       SUBDIRS = kexample

to

       SUBDIRS = kmyapp

Note: kmyapp here refers to the subdirectory by that name. You could include more subdirectories to be compiled. For example:

       SUBDIRS = kmyapp kmysupportclass

Also edit the last line of configure.in to read

    AC_OUTPUT(Makefile \
              kmyapp/Makefile \
              po/Makefile)

The `po` directory contains translations of strings that you used in your code (this is about i18n(), which is not covered in this HOWTO). We'll get to that it a minute.

Now we want to set up the Makefile for the kmyapp subdirectory. Edit `kmyapp/Makefile.am` according to the instructions given in the comments. They should be clear enough.

Now `cd &˜;/KDE/kmyapp` and type

       ./configure

This should create:

- Makefile

- kmyapp/Makfile

- config.h

You can now include config.h in your source code with `&&num;;include "../config.h"` and have your code compile differently on different systems based on the `&&num;;define`s. Eh? Well, different systems have slightly differnent ideas about implementing standards and such, and your code my need take this into account to be portable, i.e. to work on various Unices. Take a look inside `config.h` for descriptions of the `&&num;;define`s.

Shared Libraries! If you are packaging a widget or other class you should be compiling a shared library. Luckily, this is easy to do within the kexample packge. You only need to change the Makefile.am that resides in your code's sudirectory. Unluckily, now example for a shared-library Makefile.am is included. So, I've included one in the next section. Note: If you distribute a widget, you should also distribute a small program which tests the widget. Put that program in the same package in another subdirectory and have it compile along with the widget.

Next, make a compressed archive. You can do it this way

       cd ~/KDE/kmyapp
       make dist
       OR
       tar -cvf kmyapp-0.1.1.tgz kmyapp-0.1.1

or however you like. Just be sure that the archive expands to one directory containing all of the files. This is neater and easier for the user to deal with.

##### Example Makfile.am for a Shared Library

    # Example Makefile.am for a shared library.  It makes a library
    #  called "example" as libexample.so.2.1.2
    # This Makefile.am was taken from the kdelibs distribution and modified
    #  to serve as an example.
    #
    # David Sweet
    #

    INCLUDES=  $(all_includes)

    lib_LTLIBRARIES  = libexample.la

    # Note:  If you specify a:b:c as the version in the next line,
    #  the library that is made has version (a-c).c.b.  In this
    #  example, the version is 2.1.2.
    libexample_la_LDFLAGS = -version-info 3:2:1 $(all_libraries)

    include_HEADERS = header1.h header2.h\
                      header3.h

    # Which headers shouldn't be installed when a   make install  is done?
    noinst_HEADERS = version.h

    libexample_la_SOURCES = code1.cpp code2.cpp
                            code3.cpp

    #  AUTO is great.  This takes care of all of your  moc'ing
    #   dependencies.
    #  (You still need to include, for example, header1.moc in code1.cpp.)
    libexample_la_METASOURCES = AUTO

##### LSM file

Next, you need and LSM file. You can keep a copy in kmyapp-0.1.1 for distribution.

Here's a sample `.lsm`:

    Begin3
    Title:          KLab
    Version:        0.1.0
    Entered-date:   3/1/99
    Description:    GUI and more for RLab
    Keywords:       kde rlab math plot plotting
    Author:         David Sweet &<;dsweet@chaos.umd.edu&>;
    Maintained-by:  David Sweet &<;'dsweet@chaos.umd.edu&>;
    Home-page:      http://www.glue.umd.edu/~dsweet/KDE/KLab
    Primary-site:   ftp://ftp.kde.org/pub/kde/unstable/apps/scientific
    Alternate-site: http://www.glue.umd.edu/~dsweet/KDE/KLab/
    Original-site:  ftp://upload.kde.org/pub/kde/Incoming
    Platform:       unix
    Copying-policy: GPL
    End

You can copy and paste this text into a file called "kmyapp.lsm" and make the appropriate changes.

#### Submitting

If you are hacking at CVS, you should follow the commit procedure outlined above. If you want to submit changes to code that you are not maintaining, you should first check with the maintainer of the application. You can usually find his/her email address in the directory containing the source. Also check the program's "About" box if it has one. If no maintainer is specifically listed, you should contact the author.

If you are developing outside of CVS, you can submit your code (widgets, applications, etc.) via FTP to ftp://upload.kde.org/pub/kde/Incoming

When you do this be sure to include an `.lsm` (Linux Software Map) file. This way your code can be automatically placed in the appropriate spot on ftp.kde.org and its mirrors and an announcement can be automatically sent to the kde-announce mailing list.

Note: The `.lsm` file should be uploaded separately from the source code (as a plain ASCII file). The source code should be packaged as a .tar.gz (or .tgz) or .tar.bz2. This archive should expand to a single directory with all of your stuff in it.

### Acknowledgements

I'd like to thank the following people for their suggestions (in no particular order): Roberto Alsina, Waldo Bastian, Harri Porten, Samuel Wuethrich, Richard Moore, Daniel Naber, Ralf Nolden, Martin Konold, and Pietro Iglio, Stephan Kulow, Junji Takagi.

### About this document

#### Finding

The current version of this document is available at http://www.chaos.umd.edu/~dsweet/KDE/MiniHOWTO.

A Japanese language version is available at http://www.asahi-net.or.jp/~hc3j-tkg/kde-jp/DevelMiniHOWTO-jp.txt thanks to Junji Takagi.

This document is included in the KDE Developer's Web Site (http://developer.kde.org) and is part of the KDevelop (http://www.kdevelop.org) programming manuals.

#### Changes

##### Changes from version &<;July 1, 1999&>; to version &<;March, 11, 2000&>;:

- Added "About this document" section

- Added more acknowledgements

- Multiple formatting changes (thanks Ralf!)

- Fixed instructions for getting CVS access

- updated contents for current KDE 2 development state (March, 11, 2000)

## Using KDE I/O Slaves

Kurt Granroth \|<\|granroth@kde.org\|>\|

v0.1, 15, June 1999

This HOWTO describes how the KDE I/O slaves (ioslaves) work. It is intended for both ioslave implementers as well as users. In this document, I will attempt to describe both how ioslaves work as well as how to use them.

### Introduction

#### What Are I/O Slaves?

The K Desktop Environment (KDE) I/O slaves (ioslaves) are a series of small programs that have intimate knowledge on working with a very specific protocol. For instance, the HTTP ioslave (`
kio&&lowbar;;http`) knows all about sending and receiving data to and from a web server
. It knows all about SSL
, encoding, and what all of the different header fields mean. It knows this so that KDE developers won't have to -- if they want a web page, they merely have to use `
kio&&lowbar;;http` for it and it will take care of everything for them.

The ioslaves are based on the KIO library (`
libkio`). This library implements a method of asynchronous communication between applications as well as provides a "protocol registry" of sorts. This has many advantages. Two of the major ones are:

- The client does not need to know anything about the ioslave that it is calling. It merely specifies the protocol and `cdx/libkio/` will automatically determine the proper ioslave to use.

- All communication is done asynchronously. All `
  libkio` calls will return immediately. Whenever events occur, `
  libkio` will send signals altering the client to that fact. This means that the client does not have to engage in any "busy waiting."

Here is a fully working snippet of code to download a web page:

     KIOJob *job = new KIOJob;
     connect(job, SIGNAL(sigData(int, const char*, int)),
             this, SIGNAL(slotData(int, const char*, int)));
     job->get("http://www.kde.org/news_dyn.html");

That's it! When the ioslave is done getting the KDE news page, it will call your application's slotData() function with the page.

#### Copyright

Copyright (c) 1999 Kurt Granroth, All rights reserved. This is free documentware; you can redistribute it and/or modify it under the terms of version 2 or later of the GNU General Public License

### Setting Up IOSlaves

Using the KDE ioslaves is very easy once the framework is in place. This section will describe getting the "back-end" stuff setup.

#### The KIO Library

The first part that needs to be installed is the KDE IO library (`
libkio`). This library is part of the KDE 2.x `
kdelibs` package. It will be installed by default on all KDE 2.x systems when 2.x is finally released. In the meantime, you will need to get it through either CVSUP or the snapshots. See www.kde.org (or a later appendix when I get around to it later) for details on that.

`
libkio` depends on the KDE UI library (`
libkdeui`), the KDE Core library (`
libkdecore`), and Qt (`
libqt`). These are the "standard" KDE libraries so there shouldn't be any problems, there.

#### The IOSlaves

There are ioslaves for `
http`, `
file`, `
gzip`, `
tar`, `
file`, `
smb`, `
pop3`, and `
imap4` as of the time of this writing. They are all found in the `
kdebase` package under `
kioslaves`. Again, these are available only from the KDE development CVS so you'll need to use CVSUP or the snapshots to retrieve them.

### Using ioslaves in your Application

Now that you have the backend stuff all setup, you can start setting up your application to use them. This requires adding the `
libkio` library to your linking and including the proper header files.

#### Modifying your Makefile

If you are using the standard KDE automake/autoconf system for your application, then you are in luck! Adding another library is trivial:

- Edit Makefile.am

- Change the line &<;yourapp&>;
  &&lowbar;;LDADD to look something like:

           myapp_LDADD = $(LIB_KIO)

That's it! If you are not using the KDE setup, then just make sure that you are including the following in your link stage:

        -lkio -lkdeui -lkdecore -lqt

#### Adding the Proper Headers

This is also simple:

       #include <kio_job.h>

Will take care of everything.

### Using IOSlaves via KIOJob

Now that your application is all prepped for using ioslaves, you can actually start using them!

The only class you need to deal with at a client level is the KIOJob class. You will use it to "call" the ioslaves and it will send you signals when it received events from them.

The basic procedure is something like this:

- Create a KIOJob
  instance

- Connect all of the signals (events) that you are interested in to some local slots (callbacks).

- Send the actual request

- Process the resulting response in your slots.

#### Typical Example

An example looks like so:

     KIOJob *job = new KIOJob;
     connect(job, SIGNAL(sigData(int, const char*, int)),
             this, SLOT(slotData(int, const char*, int)));
     connect(job, SIGNAL(sigError(int, int, const char*)),
             this, SLOT(slotError(int, int, const char*)));
     connect(job, SIGNAL(sigDataEnd(int)),
             this, SLOT(slotDataEnd(int)));
     connect(job, SIGNAL(sigFinished(int)),
             this, SLOT(slotFinished(int)));
     job->get("http://www.pobox.com/~kurt_granroth/index.html");

This will get the web page at the above URL. When the ioslave is done receiving the page, it will send it to you with your `
slotData()` function. If there was an error, then you receive it in `slotError()`. If the page was large, then it will be sent in chunks. You know that you are done receiving data when `slotDataEnd()` is called. The `slotFinished()` function is called when the ioslave is completely done.

### KIOJob Calls

There are numerous operations that you can do with KIOJob. Some of them are: `
put`, `
get`, `
mkdir`, `
copy`, `
move`, `
del`, `
unmount`, and `
mount`. I will refer only to `
put()` and `
get()` in this HOWTO. The others follow similar patterns.

#### get(const char* url)

This is probably the most common operation. It tells the ioslave to "get" the resource described in the URL. This may be a web page, a POP3 message, or a local file -- it all depends on your URL.

This operation is not very interactive. You tell the ioslave what you want and it gets it for you. Period.

Specifically, it will send back your data with the `
sigData(int id, char char* data, int length)` signal.

Parameters:

const char *url - The URL of the resource that you wish to get

#### put(const char* url, int mode, bool overwrite, bool resume, int size)

This operation will start the process of "putting" or sending data to the location specified in the URL. This is used, for instance, to send files to a remote FTP server or do do a PUT or POST request with HTTP. It is not quite a straight-forward as a `
get()` operation.

The basic procedure looks like:

- Connect `
  sigReady(int)` to a local slot (e.g., `slotReady(int)`)

- Send a `
  KIOJob::put(..)` request

- When `slotReady(..)` is called, you know that the ioslave is ready to relay your data to its final destination.

- Send all of your data using the `
  KIOJob::data(const char*, int)` operation. When you are done, notify the ioslave of this by sending a `
  KIOJob::dataEnd()`

Some sample code looks like so:

     KIOJob *job;
     char *data = "My message";
     Client::Client()
     {
     	job = new KIOJob;
    	connect(job, SIGNAL(sigReady(int)), this, SLOT(slotReady(int)));
    	connect(job, SIGNAL(sigData(int, const char*, int)),
    	        this, SLOT(slotData(int, const char*, int)));
    	connect(job, SIGNAL(sigDataEnd(int)),
    	        this, SLOT(slotDataEnd(int)));
    	job->put("http://server.com/cgi-bin/post.cgi", -1, true, false, strlen(data));
     }

     void Client::sigReady(int id)
     {
        job->data(data, strlen(data));
    	job->dataEnd();
     }

Parameters:

const char* url - The end location for your data int mode - Special permissions for your data. This should be set to -1 if there are no special permissions bool overwrite - Instructs the ioslave to overwrite anything that may already be there. bool resume - Instructs the ioslave to resume a previously aborted transaction. int size - This is the size of the data that you will be sending

Beyond that, everything else is identical to the 'get' method.

#### data(void *data, int size)

This is used to send data to an ioslave. It is used in conjunction with the `
KIOJob::put(..)` operation. It is almost always called from your object's `slotReady()` slot as you must wait for the `
sigReady(int)` signal before sending any data. If your data is greater than 2048 bytes, then you must break it up into many chunks and send each chunk individually.

Parameters:

void *data - Your data that needs to go to the ioslave. This should be no more than 2048 bytes. int size - The size of this data

#### dataEnd()

This is used along with `
KIOJob::data(..)` and `
KIOJob::put(..)`. It signals the ioslave that you are done sending it data. If you do not send this signal, then the ioslave will essentially hang. You do not need to use this if you are using `
KIOJob::get(..)`.

Parameters:

None

### KIOJob Signals

All communication from the ioslaves come through KIOJob in the form of signals. There are quite a few of them (see `
kio&&lowbar;;job.h` for a complete listing), but I'll only discuss the "essential" ones.

#### sigError(int id, int errid, const char* text)

This signal is emitted whenever an error occurs. You should always connect a slot to this signal unless you really don't care if there is an error or not.

Parameters:

int id - The job id int errid - The error code. This corresponds to the list of errors defined in kio&&lowbar;;interface.h const char* text - A textual description of the error

#### sigReady(int id)

This signal occurs when the ioslave is ready to accept data. If you are using only `
get(..)` methods, then you should not have to connect to this signal. If you are using a `
put(..)` method, then you must connect to this signal and begin sending data from there.

Parameters:

int id - The job id

#### sigData(int id, const char *data, int size)

This signal happens whenever the ioslave is sending you data. This is typically the data that it just downloaded. It will never be more than 2048 bytes, so plan on having it called several times. You are responsible for collecting all of the data. You will know that the ioslave is done sending you data when you get the `
sigDataEnd(int)` signal.

Parameters:

int id - The job id const char *data - The data that the ioslave just downloaded int size - The size of this data chunk

#### sigDataEnd(int id)

This signal is sent to signify that the ioslave is done sending you data. You should use this signal as an assurance that you can use the data as `
sigData(..)` will never again be called.

Parameters:

int id - The job id

#### sigFinished(int id)

This signal indicates the the ioslave is completely done.

Parameters:

int id - The job id

## KDE KOM/Open Parts

Torben Weis `weis@kde.org`, Bernd Wuebben `wuebben@kde.org`

v 1.0 August 1998

A free Object Model for Unix

### Introduction

Although it is nice to have applications like StarOffice and ApplixWare available for Unix/Linux, it is a sad fact that they do not interact with other applications. In fact user is expected to use the supplied e-mail-client only, the StarOffice spreadsheet cannot be embedded into an Applix document, and users cannot use the WWW browser of their choice.

Even a rather simple component-based approach like COM (Component Object Model) can do lots of magic. Take a glimpse into Bill Gates' world to convince yourself. The spread sheet of one software house can be used in a word processor of another and many applications support scripting interfaces. A veritable industry of OCX/ActiveX component writers has cropped up. Using easy to learn languages such as Visual Basic, users with a minimal programming background can glue separate components together with ease in order to create new applications exhibiting previously unavailable functionality. As Aristotle put it: The whole is more than the sum of its parts.

Even the most faithful of all Unix supporters had to recognize that the Unix community had technologically fallen far behind the windows as well as Macintosh worlds with regard to GUI and desktop technologies. Fortunately, distributed objects make sense beyond a the realm of desktop and GUI applications. The development of distributed databases may serve as an example and component technology in general fits nicely into the strategy of major software companies which in turn helped to push CORBA development on the UNIX platform. Consequently, there is a standardized, network transparent, platform- and language-independent solution to the IPC-problem available, that is based on an object-oriented approach and that offers exception handling as well as support for management of complex data structures.

While initially only available commercially, free implementations of the CORBA 2.0 standard object request broker (ORB) became available as well. A particularly well done free implementation of the CORBA standard, that will soon cover the full CORBA-2.1 standard, was developed at the university of Frankfurt, Germany. This GNU GPL'd ORB named MICO, can compete with commercial implementations with respect to completeness and stability.

### CORBA-based KDE Object Model

However, CORBA alone is not enough, as the standard only describes how distributed objects communicate; the COSS (CORBA Standard Services) define interfaces for a whole range of so called services, such as trading services, security services, transactions sercies and license management services and others. As is apparent, desktop development or even GUI application development did not play an important role when designing CORBA. The nearly indispensable event service does not support event filtering, furthermore it lacks a mechanism for so called 'callbacks'. Thus, in order for X11 and CORBA to work well together, CORBA´s well done industry standard object model had to be enhanced in order to be suitable as the underlying distributed object broker on which to build the all important compound document framework. Of course all this had to be undertaking within the limits of the accepted CORBA industry standards.

The KDE Object Model (KOM) addresses these issues and makes life easier for a programmer by automatically giving each object a base set of functionalities. This includes first and foremost event handling. Each KOM object is capable of receiving events. An event consists of an arbitrary data structure (`CORBA::Any`) as well as a string, describing the type of the event, for example Desktop/Font/ChangeFont or Desktop/Color. By reading this string, an object can decide whether it is interested in that event, and will know how the unknown data structure is to be interpreted semantically.

### Flexibility with events and filters

An important aspect when processing events is filtering. It allows the developer to enhance the functionality of a program without having to change the sources. There are three types of filters: those which merely recognize events, those which are allowed to change or discard events, and finally those which finally process them. This model can be illustrated with a simple WWW browser example: If a program wants the browser to open a new URL, it has to send it an event. To enhance the browser with a history function and the user only has to plug in a filter of the first category into the browser. This filter can record all URLs, thereby managing a history function. A tool for blocking certain pages (in order to protect minors for example) belongs to category two: If the URL is rated unsuitable for minors, the filter discards the event or replaces the URL in it with a different one. If the Browser does not support mailto, we need a third category filter: upon arrival of a URLOpen event with mailto:joe@doe in it, the event is discarded from the browser's point of view, but the filter offers an alternative implementation.

The filter principle is of great importance, as in CORBA only interfaces are inherited, not implementations. It would not be possible to derive from the browser object and to just overload the function that opens a new URL. The event model does not just solve this problem, it enables developers to install several enhancements at the same time. A second filter, filtering FTP URLs for example, can be easily installed in addition. It is important that an user can plug-in an arbitrary number of such filters at run-time. A filter is installed this way:

          KOM::EventTypeSeq types;

          types.length (1);

          types [0] = CORBA::string_dup ("OpenURL");

          browser->installEventFilter ( this, "eventFilter", types);

Now, all you have to do is wait for events:

    boolean MailFilter::eventFilter (in Object obj, in EventType type, in any value)
    {

        if ( type == "OpenURL" )
        {

            char *p;

            if (( value >>=p ) && strcmp (type, "mailto:", 7) == 0)
            {

                // open mail app

                CORBA::string_free (p);

                return true;

            }
        }

         return false;
    }

When a user enters a URL in the example above, the browser sends an event to itself. This concept can also serve for a macro recorder, because events can be filtered, saved and re-send later. As event handling works the same way for all KOM objects, there is now - at last - the possibility of creating a supra-component macro recording.

To avoid performance loss, filters can be installed in a way that they only receive events whose type matches a certain expression. Desktop/Font/* would filter all events that have to do with the font settings of the whole desktop.

### Callbacks with signal and slots

In an event-driven environment, programmers have to deal with so called callbacks. A callback is a mechanism by which a button for example activates a procedure that is supposed to be called once the button is pressed. C offers pointers to functions for that, under C++ we can use elegant signals and slots. A wonderful implementation of this idea is offered by the QT toolkit. KOM offers this technique for distributed objects as well, it uses CORBA´s DII (Dynamic Invocation Interface) for that purpose. An object can be target and sender of signals at the same time. To receive a signal, an object must have a slot with a matching list of parameters. Such a slot is not different from a usual CORBA method without return value. The lines

          MySender_var s = new MySender;

          MyReceiver_var r = new MyReceiver;

          r.connect ( "selected", s, "myslot" );

connect two objects with each other. As soon as `s` emits the selected signal, the function myslot of object `r` is called. If one of the objects is destroyed, the connection between the two is released automatically. It is possible to connect one signal to different slots and one slot to different signals. Theoretically, it would also be possible to work with events here. Signals and slots however work a lot faster and are easier to handle for the programmer, because he/she does not have to deal with event processing. Furthermore they are type-safe. On the other hand, you have to work without the advantages of filtering.

As already mentioned, interface inheritance does not solve all problems. The browser example shows that installing multiple enhancements at run-time works only because filters are loaded which can plug into the browser. The principle behind this mechanism is called dynamic aggregation. You take a core object (the browser) and enhance it by other objects (plug-ins). The interface of the browser is expanded with the sum of the plug-ins' interfaces. KOM support run-time installation and uninstallation of those plug-ins. Neither is required to run within the same process or even the same computer.

An object that communicates with the browser does not notice which interface was implemented by the core object and which by the plug-ins. If a client wants to know whether a component supports a special interface, a simple

    CORBA::Object_var obj = browser->getInterface ('IDL:/foo/bar:1.0');

is enough to get a reference to that interface. It is even possible to load the plug-in with the required interface at run-time. This saves a lot of memory, because the plug-ins allocate resources only then when they are really needed.

### OpenParts: KOM meets GUI

Until now, the word GUI has not been mentioned a single time. Indeed, KOM is solely based on CORBA. There are in effect quite a number of applications which do not have or do not need a GUI. It would not make sense to burden those applications with GUI code; we better leave that to the supporters of the Redmond doctrine. OpenParts form a layer on top of CORBA, KOM, and X11. Embedding GUI components in own programs has become widely known since introduction of MS Internet Explorer 4.0. In the windows world, a lot of controls have been created, a whole industry has formed around the creation of controls and nearly everything, from a simple push-button to a complete WWW browser, can be realized as a control.

The idea behind controls is actualized in KDE's OpenParts. This is done using window objects that export a well-defined interface. The CORBA Implementation Repository registers the implementation of those components.

When an application needs a specific control, it relays a request to the repository. In the OpenParts framework, separate server processes handle these requests. Thus, a high level of toolkit, compiler, language and multi threading-support independence is achieved. The repository returns a reference to a factory. CORBA does not support the creation of new objects with a special construct. Because of that, factories have been introduced, which serve the purpose of creating a new object and returning a reference to it.

The actual embedding is done via X11 swallowing. The element that is to be embedded (a X11 window) is assigned a new parent using the X11 Xlib call `XReparentWindow`. In order to keep developers away from messing around with pure X11, there exist so called control frames. This class depends on the used toolkit and (viewed from outside) looks like a normal window implemented using a specific GUI toolkit. The control frame is made the new father of the control. Is it moved or resized, the control is moved or resized as well.

### Dynamic Elements realized easily

Some elements of a window are subject to restrictions. There can only be one menu bar as well as one status bar. Great confusion would ensue if controls were allowed to take control of these restricted elements. One way to avoid this confusion is offered by an enhancement of controls: Parts. A part can be in three different modes: inactive, marked, or active. In a top-level window only one can be active. This part gains control over the restricted elements. The top-level window is called PartShell; it owns menu bar, tool bars, status bar etc. Each part can register its menus and toolbars with the Shell. The Shell, however, only displays the restricted elements of the active Part.

Using Parts, one can build the basic structure of an integrated office suite. Text processing, spread sheet, drawing application and all the rest is put into such structures. As Parts can contain Parts, a spread-sheet Part can be embedded into a text-Part with no problems. Instead of ControlFrames, the developer can use the mightier PartFrames as well. A single click on a part creates a window frame, the user can move and resize the frame now; another click makes it active. The window frame is changed automatically to show the user, that the part has been made active. The Shell changes menu bar and tool bar to the needs of the Part that has become active.

What is missing is a common file menu: it is the Shell's privilege to provide a menu and a toolbar of its own for that purpose. Via these widgets, the user can access the document as a whole, for example to save or print it.

### The Document View Architecture

OpenParts support the Document View Model, known from Smalltalk and popularized in Microsoft's MFC. A view displays the document on the screen and is derived from the Parts class. The document contains data as well as the associated algorithms. The advantage of this solution is that users can have several views of the document. It is, for example, possible to display two paragraphs of a longer article in two windows or to display an image in a separate window when manipulating it. When zooming in on this picture, the rest of the document does not have to be enlarged as well.

For reasons of performance, document and view run within the same process. In order to achieve a clean design, the developer has to strictly follow the principle of separating data and belonging manipulating algorithms on one side and viewing algorithms on the other side. Every user action first affects the view. This view must then signal the Document to change the data basis accordingly. After that, the Document must inform all views to update their windows in order to correctly reflect the new data state.

An important aspect is using a Document without a View. This allows to make use of an office suite's functionality in a batch job, for example. The Document should export methods for manipulating data. A CORBA-aware scripting language could make use of those methods. At this time, CORBA-binding for Perl, Tcl, and CORBA-Script exist, a Python binding is being worked on.

Object models, and operating systems are alike in that they are of mere academic value as long as there are no applications making use of their qualities. KOffice is, at the moment, the largest and most widely known application using OpenParts technology. As the KDE project makes wider use of CORBA, and KOM and OpenParts respectively, there is reason to hope that Unix will soon have a free implementation of a object model that transfers the advantages of contemporary compound document framework technology to the user.

### Using OpenParts: KOffice

Based on OpenParts technology, the KOffice project aims at offering a free and easily extendible office suite for KDE.

The 'mother' of all KOffice components (called Parts) is the spread sheet KSpread. Although officially still declared 'alpha', it is already working very well.. Its mathematical functions can be easily enhanced by means of an embedded Python language interpreter.

KPresenter is a presentation application written by Reginald Stadlbauer, which convincingly demonstrated its usability at the Linux Congress in Cologne where a talk about KDE was given using KPresenter. In the future, Linus Torvalds will no longer have use Windows applications when giving presentations.

KChart is an application to create diagrams. It supports different modes (bars, lines, etc.) and can be embedded into other KOffice application without problems. When data is changed in, for example, KSpread, the chart automatically changes itself accordingly.

There is a component for displaying graphics, and work is being done adapting a formula editor and a vector-oriented painting application (KIllustrator). For the future, adaptation of KLyx is planned to embed Parts in LaTeX documents. Other KDE applications like KOrganizer, KAddressbook and KMail will be enhanced with CORBA interfaces to provide seamless integration in the KOffice suite.

If possible, components save their data in XML format. To save an aggregate document in a file, the MIME multi-part format is used. XML and MIME multi-part share the advantage that import and exports filters can easily be written in scripting languages like Python and Perl. Ever since a WinWord filter attracted attention on c.o.l.a. (comp.os.linux.announce), developers began writing import filters for the most important MS applications.

KOffice runs quite stably for a alpha release. To compile it, a fast CPU and a reasonable amount of RAM ( 64 MByte) should be available; for using KOffice, 32 MByte and a Pentium 133 MHz will suffice. KOffice is reported to work on DEC Alpha and Sun Sparcs as well. The sources for KOffice, KOM and OpenParts are available on http://www.kde.org

### Code Re-use with CORBA

A developer, sitting in front of his editor and trying to build new software components out of old ones, might sadly remember the good old Lego pieces from his/her childhood. Those always fit together nicely, and from a great number of primitive elements, great buildings could be constructed. From the software developer's point of view, software pieces do not fit at all, and each new building requires a lot of work to make it usable. Many developers therefore choose to rewrite the sources rather than to re-use code.

Under Unix, the usual method concerning code re-use is putting code into libraries and linking the application against them. However, sometimes it happens that those libraries use different GUI toolkits, or that some support multi-threading and some do not. Additionally, all libraries should have been compiled with the same compiler, otherwise you will be subject to problems at the linking stage.

One solution is to split an application into several processes. One process might, for example, offer database functionality with multi-threading, another one might offer the X11-GUI single-threaded. Now, the problem of Interprocess Communication (IPC) remains to be solved. CORBA (Common Object Request Broker Architecture) offers a modern and object-oriented solution, but the developer is tied to the CORBA object model, what is somewhat limited in contrast to the C++, Python or Smalltalk object model. First of all one can derive only from interfaces, not from implementations. If there is a text editor as a CORBA object, it usually is impossible to derive from this editor and overload just a few of its functions. The derivation of implementations works only if the sources of the editor are available, or if it is available as a library. But that was what we wanted to avoid.

As the main article describes, the KDE Object Model (KOM) offers a solution for this problem; it is based on events and event filters. Instead of calling a function directly, it is possible to send an event. By filtering events, function overloading can be simulated.

There are other obstacles when re-using code: According to Murphy's laws, the desperately needed module is always written in a different programming language than expected. Mixing different languages in one application is always a problem for programmers. Even semi-automatic wrapper generators like SWIG expect a certain amount of refining from the programmer. Further, a look at the KDE bindings in Python, Perl and Tcl shows that this wrapper code can easily grow huge. As OpenParts is CORBA-based, objects can be implemented in any of the languages for which CORBA bindings are available for. For interpreter languages, in most cases no special wrapper code is necessary, for compiler languages like C/C++, wrapper code generation is done automatically.

### Credits

Most of this document was written by Torben Weis weis@kde.org. Additions, corrections as well as the editorial work was done by Bernd Johannes Wuebben wuebben@kde.org.

## Using KOM / OpenParts HOWTO

Simon Hausmann <hausmann@kde.org>

v1.0 18, June 1999

This documentation is meant to help programmers who are already familiar with the standard Qt/KDE application framework and who are interested in using the KDE component technology, the KDE Object Model (KOM) and OpenParts, for their application. It covers both "why" you should use KOM/OP and "how" to do so.

### Introduction

This documentation is meant to help programmers who are already familiar with the standard Qt/KDE application framework and who are interested in using the KDE component technology, the KDE Object Model (KOM) and OpenParts, for their application. The goals of using components in case of standard KDE applications might be:

- make several modules of the application re-usable components, being available for other applications, not matter in what language they are written or on what platform/machine they're running.

- make use of OpenPart's embedding facility and its way to manage shared GUI elements to provide seamless graphical integration of components in applications.

- use CORBA and KOM as an easy way of inter-process communication

I recommend reading the article by Torben Weis about KOM/Openparts Torben Weis is the author/creator/master/god of KOM/OpenParts.

### The KDE Object Model (KOM)

#### First ways of communication - the KOM::Base interface

The Base interface and its implementation in `
libkom` provide the basic functionality for a standardized communication between CORBA objects using KOM. This includes

- signals and slots, similar to the mechanism used in the Qt toolkit

- event handling, including filtering

- relatives management

- reference counting

#### Signals and Slots

KOM supports signals and slots just like in the Qt toolkit, with a few differences in usage and implementation. The first and biggest difference is that signals and slots are no more typesafe again, meaning there's no moc compiler generating meta data for KOM signals/slots to enable type checking at run-time, when connecting.

Another difference is the way you declare signal and slot functions. Signals have to be declared with the `
SIGNAL&&lowbar;;IMPL` macro from `
komBase.h`, without specifying any signal arguments. Slot methods have to be defined in your CORBA interface description.

For KOM signals the equivalent to the "emit" keyword from Qt is the `
SIGNAL&&lowbar;;CALLx` macro, were "x" is one of 0, 1, 2, depending on the number of arguments.
Example:

In your CORBA interface description:

      #include &<;kom.idl&>;

      interface FooSender : KOM::Base
      {
        signal void mySignal( in long foobaz );
      };

      interface FooReceiver : KOM::Base
      {
        slot void mySlot( in long gosh );
      };

In the implementation of FooSender:

      FooSender_Impl::FooSender_Impl( ... )
      {
        ...
        SIGNAL_IMPL( "mySignal" );
        ...
      }

      FooSender_Impl::mySignal( CORBA::Long foobaz )
      {
        SIGNAL_CALL1( "mySignal", foobaz );
      }

In the implementation of FooReceiver:

      FooReceiver_Impl::mySlot( CORBA::Long gosh )
      {
        ...
      }

First some words about the sender: It is not required to define the signal in the interface description and to provide an implementation which simply emits the signal. However in many cases this is recommended, because this makes it easier for other developers to use your interface because they aren't required to seek in the implementation sources just to find out about the signals this object emits. Another way is to simply document the existence of the signal in the interface description, without defining a method. From the technical point only the `
SIGNAL&&lowbar;;CALLx` macro counts when emitting the signal.

What's left is connecting and disconnecting. In the above described example it could like this:

      ...
      SenderObject->connect( "mySignal", ReceiverObject, "mySlot" );
      ...
      SenderObject->disconnect( "mySignal", ReceiverObject", "mySlot");

Well, this is quite self-explaining I think. Just make sure to always disconnect from your object upon destruction.

#### Events

An event consists of two elements, the event name, being a string, and an event argument, being a CORBA::Any value and therefore freely choosable by the developer.

Events, sent to a specified object, can be imagined as being put through a pipe until they reach the destination object. This "pipe" is filled with installed event filters. There are three kinds of filters.

- reading filters ( `
  FM&&lowbar;;READ` )

- writing filters ( `
  FM&&lowbar;;WRITE` )

- implementing filters ( `
  FM&&lowbar;;IMPLEMENT` )

In the current implementation in KOM these filter modes only specify the order how the event is processed. When an event is emitted it gets first filtered by all event filters with the filter mode `
FM&&lowbar;;WRITE`, then followed by `
FM&&lowbar;;IMPLEMENT` and finally by `
FM&&lowbar;;READ`. Event filters have two possibilities what they can do with the actual event: They can just read it or they can discard it, which means the event is discarded and will never receive its destination object.

The actual event name has a special meaning in regard to event filters. When installing an event filter to an object you have to specify, beside a reference to the filter object and the name of the filter mapping function, a sequence of so called event type patterns. An event type pattern can be the name of a single event as well as a special pattern (see `
kom.idl` for more information about event type patterns) .

When an event is meant to be processed by a filter, the specified filter function gets called, with the event name and the event value as arguments. This filter function has to return (through a boolean value) whether the event should be discarded or not.

When all filtering is done and none of the installed filters discarded the event, it is finally received by the destination object, by calling the object's `
event()` method, defined in the KOM::Base
interface. The default implementation does actually nothing, so you may want to re-implement this virtual function.

The very low-level usage of events is to call the receive/receiveASync methods of an object for sending an event and to re-implement
KOMBase::event for mapping an event. But KOM provides some nice macros which simplify the processing of events.

##### Sending Events

For sending komBase.h defines some useful `
EMIT&&lowbar;;EVENT` macros, all using the same syntax: `EMIT_EVENT_x( destination_object, event_name, event_argument)`

"destination&&lowbar;;object" is a reference to the object which is meant to receive/process the event. The event will be filtered through all event filters which are installed in this destination object. "event&&lowbar;;name" is self-explaining ;-) . The event argument depends on the specific macro, which are in particular:

- `EMIT&&lowbar;;EVENT`, the general macro for sending. The event argument is required to have a `<<=` operator for CORBA::Any defined. You will want to use this macro whenever the event argument is a structure for example. (and don't forget to compile your idl file with the "--any" option, so that the idl compiler generates the necessary operator methods)

- `
  EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN`, useful for boolean event arguments. The only difference to the `EMIT&&lowbar;;EVENT` macro is that it uses CORBA::Any::from&&lowbar;;boolean for you to convert the boolean value. So it doesn't really matter whether you use:

  `EMIT_EVENT( receiver, name, CORBA::Any::from_boolean( value ) );` or `EMIT_EVENT_BOOLEAN( receiver, name, value );`

- `
  EMIT&&lowbar;;EVENT&&lowbar;;OCTET`, similar to EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN, useable for
  CORBA::Octet values.

- `
  EMIT&&lowbar;;EVENT&&lowbar;;CHAR`, similar to EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN, useable for char (
  CORBA::Char) values.

- `
  EMIT&&lowbar;;EVENT&&lowbar;;WCHAR`, similar to EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN, useable for
  CORBA::WChar values.

- `
  EMIT&&lowbar;;EVENT&&lowbar;;STRING`, similar to EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN, useable for char * (
  CORBA::Char*) values.

- `
  EMIT&&lowbar;;EVENT&&lowbar;;WSTRING`, similar to EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN, useable for
  CORBA::WChar* values.

- `
  EMIT&&lowbar;;EVENT&&lowbar;;OBJECT`, similar to EMIT&&lowbar;;EVENT&&lowbar;;BOOLEAN, useable for CORBA objects (
  CORBA::Object).

##### Receiving Events

The process of receiving events is a little bit more difficult, compared to sending, since we have to process all kinds of events an object can receive in one handler method, KOM::Base
::event (IDL
) / KOMBase::event (C++) . Just like with sending events you can again do everything on low CORBA level, but why should we go the hard way? KOM again provides very nice and easy-to-use macros for this (defined in komBase.h) :-) . Usually all this looks like the following example:

    #include &<;kom.idl&>;

    module MyModule
    {
      // we say: the event argument is a string
      const string eventFirstFoo = "MyFooEventNameOrWhateverYouNameIt";

      struct MyStruct
      {
        boolean kde_rules;
      };

      const string eventSecondFoo = "Blaafooo";
      typedef MyStruct EventSecondFoo;

      interface SomethingElse
      {
        //..
      };

      const string eventThirdFoo = "KOMIsCool";
      typedef SomethingElse EventThirdFoo;

      interface Foo : KOM::Base
      {
        //...
      };

    };

    bool FooImpl::event( const char *event, const CORBA::Any &&;value )
    {
      EVENT_MAPPER( event, value );

      MAPPING_STRING( MyModule::eventFirstFoo, mappingFirstFoo );

      MAPPING( MyModule::eventSecondFoo, MyModule::EventSecondFoo, mappingSecondFoo );

      MAPPING( MyModule::eventThirdFoo, MyModule::EventThirdFoo_ptr, mappingThirdFoo );

      END_EVENT_MAPPER; //the macro executes "return false;" for us, to indicate that
                        //we did not handle the event if we reach this point
    }

    bool FooImpl::mappingFirstFoo( const char *myArgument )
    {
      ...
      //don't forget to return with a boolean value, indicating whether you sucessfully
      //processed the event or not.
    }

    bool FooImpl::mappingSecondFoo( MyModule::MyString anotherArg )
    {
      //...
    }

    bool FooImpl::mappingThirdFoo( MyModule::SomethingElse_ptr whaaboo )
    {
      //...
    }

As you can see an event handler usually begins with the `EVENT&&lowbar;;MAPPER` macro and ends with `END&&lowbar;;EVENT&&lowbar;;MAPPER` . Similar to the `EMIT&&lowbar;;EVENT&&lowbar;;x` macros, the `MAPPING` macros consist of a general `MAPPING` macro and the following friends:

- `MAPPING&&lowbar;;BOOLEAN`

- `MAPPING&&lowbar;;OCTET`

- `MAPPING&&lowbar;;CHAR`

- `MAPPING&&lowbar;;WCHAR`

- `MAPPING&&lowbar;;STRING`

- `MAPPING&&lowbar;;WSTRING`

- `MAPPING&&lowbar;;OBJECT`

In order to structurize the process of event handling a little bit, every event gets its own event handling function. These functions are called by the mapping macros (last argument) . The above used naming scheme is not a requirement, however it is used in most applications using KOM.

#### Adopting

You should use adopting whenever you want to hold a reference to an object and want to be informed when the object dies in order to free all your references to this object. But this should only be used when you're not the parent object, meaning you didn't reference the object directly via the KOM reference counter. When using adopting you should re-implement the `leaveNotify` (and perhaps `adoptNotify`) methods of your object (and don't forget to call the original KOMBase method!) .

#### KOM referencing

KOM reference counting should be used to "express" that you possess the object. This gives you direct control over the lifecycle of the object by letting the reference counter act directly on the server object, in contrary to CORBA reference counting, where the reference counter only acts on the stub object, in case of remote objects (this is different for local objects, where stub = server object) . When the KOM reference counter drops down to zero the object gets destroyed. This destruction is done by calling `cleanUp()`, which closes all connections to other objects and leaves all relatives. After this call is finished the object truly gets released. You might want to re-implement the `cleanUp()` method. In this case make sure that you don't forget two things:

- only execute if the boolean variable `m&&lowbar;;bIsClean` is false, otherwise simply return

- make sure you call the previous implementation!

As a short summary to this KOM reference stuff just keep in mind, that there are three ways to hold a reference to an object:

- you're connected to the object via signals/slots or via event filters

- you're the parent object, meaning you hold a reference through the KOM reference counter

- otherwise you should use adopting to get informed whenever the object wants to die, in order to free all references to the object which are not of one of the above kinds. The idea behind all this is that all connections/references between objects are symmetric, meaning both sides know about the connection and both sides clean up all connections between each other whenever one of the two objects wants to die. In case of signal/slot connections this is done automatically by KOM. In case of KOM reference counters you have the direct control over the lifecylcle for the object. Adopting, as third way, can be used to keep other connections symmetric, connections which are neither signal/slot connections nor direct references via the KOM reference counter.

Hint: Using the `KOMVar` template makes handling KOM references much easier, they can be used similar to the CORBA &&lowbar;;var types.

#### THE component - the KOM::Component
interface

The Component interface, being derived from the Base interface, additionally provides a kind of small interface repository for only this component, combined with the possibility to provide new interfaces by dynamic aggregation and a standard way to add plugin components. This gives CORBA objects the possibility to enhance their functionality at run-time.

There are five kinds of interfaces:

- Builtin Interfaces (derived Interfaces)

- Builtin Aggregate Interfaces

- Dynamic Aggregate Interfaces

- Builtin Plugin Interfaces

- Dynamic Plugin Interfaces

Builtin interfaces are all interfaces the object directly implements. This means they are part of the actual object implementation and can be specified via the `
ADD&&lowbar;;INTERFACE` macro (in
komComponent.h) . So for example if your interface description looks like this:

    module Foo
    {
      interface MyInterface : AnotherInterface
      {
        ...
      };
    };

You should add the following line into the constructor of an implementation of this interface:

    ConstructorNameOfMyInterface::ConstructorNameOfMyInterface( ... )
    {
      ...
      ADD_INTERFACE( "IDL:Foo/MyInterface:1.0" );
      ...
    }

This way you tell your component that it supports the interface "Foo/Interface" and therefore makes it available through the three functions `getInterface()`, `interfaces()` and `supportsInterface()` .

#### Extend your component by aggregation - KOM::Aggregate

Aggregates solve a problem with distributed objects, the problem of derivation. Since the implementation of an interface is completely encapsulated there has to be another way to extend the functionality of an already existing object. By using aggregate components you can add new interfaces to an object, at run-time. This means you extend the functionality but you do not change the behaviour of the object itself.

##### Builtin Aggregates

Builtin aggregate interfaces are the interfaces of aggregate implementations which run in the same process as our component. See in komComponent.h the four functions of the `
KOMComponent` class for adding builtin aggregates, it's easy.

##### Dynamic Aggregates

Dynamic aggregates are similar to builtin aggregates, with two differences:

- They can (are) be installed from "outside" of the component, using AggregateFactories (see kom.idl
  ) . This way they do neither have to run within the same process nor on the same computer.

- They can depend on other available interfaces. KOM does the job of resolving these dependencies for you.

#### Plugins - the KOM::Plugin
interface

Plugins are the kind of counterpart to aggregates. They do not extend the functionality of an object by providing new interfaces, but instead usually change the behaviour of it, by

1. doing things like installing event filters to the object, etc.

2. providing special plugin interfaces

#### Collect your components - the KOM::Container
interface

Containers do something simple but extremly useful: They act as repository for Container members. A container member structure consists of two elements:

- the member name, a freely chooseable and unique string identifier

- the member object, a KOM::Base
  type You can add, remove, replace, list and lookup container members or just clear the whole container. In addition the container emits signals whenever a member has been added or removed.

#### Factories

KOM contains two abstract factory interfaces:

1. KOM::AggregateFactory

2. KOM::PluginFactory

They both serve the job of creating objects and are needed for the creation of dynamic plugin and aggregate components. Whenever you want to install a such a dynamically created object to a component you have to provide an implementation of a factory interface.

#### KOMApplication

KOMApplication
is the drop-in replacement for KApplication
, required when using CORBA in your KDE Application. It, internally, combines CORBA event handling with Qt event handling and initializes the ORB
and the BOA
on startup. komApplication.h defines two smart macros to get a reference to the ORB/BOA: `komapp&&lowbar;;orb` and `komapp&&lowbar;;boa` . Usually you will want to use your own application class, derived from KOMApplication, and re-implement `start()` and/or `restore()` , which will be called >from `
KOMApplication::exec()`, depending on the BOA's state about restoring objects. For further information about KOMApplication's API see
komApplication.h .

### OpenParts

#### Introduction

The goals of the OpenParts technology, based upon KOM, are:

- provide an easy way to graphically embed other application's "widgets"

- provide an clever way to manage shared GUI elements and provide a CORBA/KOM interface/implementation of these

- implement the basic support for the document view model

To simplify the act of understanding OpenParts I will give a short example situation:

Imagine you have an a word processor and a formula editor, both being separate applications. If you now want to insert a formula into your word processor document by using your formula editor application this arises several problems: You can of course embed the formula editor's main widget window via swallowing by using XReparentWindow and friends, or easier by using QXEmbed. But then how do you want to edit your formula without having access to the formula editor's menubar / toolbar? In any way it would look ugly if these are part of the formula window. Wouldn't it be nice if the menus / toolbars of your word processor application would get replaced by the formula editor ones, except for some general menu /toolbar items? And when you go back to your text document the old menus / toolbars come back again.

Well, this is a perfect job for OpenParts :-) .

OpenParts solves the above described problem by introducing a new sytem of visual components and a new way of creating shared GUI elements, such as menus or toolbars, dynamically on demand.

In the implementation of OpenParts every element consists usually of two classes, the interface implementation, where the class name ends with "If", and the Qt/KDE object. So for example the OpenParts StatusBar element is represented by two classes: `
OPStatusBar`, being derived from
KStatusBar, handles the Qt/KDE specific extensions, and `
OPStatusBarIf` which is responsible for providing an implementation of the actual
OpenPartsUI::StatusBar interface by "translating" the interface functionality into Qt/KDE function calls.

Since every Qt/KDE object in OpenParts is most often bound to such an interface, like described above, there is usually an interface() function which returns a reference to the OpenParts interface of the element. In case of the above example `
OPStatusBar::interface()` returns a reference to an `
OPStatusBarIf` object which is directly bound to this `
OPStatusBar` object.

#### OPApplication

Similar to
KOMApplication, the class
OPApplication (derived from KOMApplication) is required when using OpenParts.

####
QWidget as Component? - the OpenParts::Part interface

In OpenParts every window which has its own GUI and which is meant to be displayed in a MainWindow is called a Part (just like the formula editor view or the word processor document view in the above described example) , and implements the
OpenParts::Part interface by deriving from the class `OPPartIf`.

If you want to make a widget class a full-featured Part component then you have to handle some things different than you might be used to, in regard to the standard Qt/KDE widget framework. In fact now a widget is no more a simple window in which you display some data, no, a Part is much more than this. In particular a Part has, beside it's window (widget, which may of course contain sub-windows or even other Parts (see `
OPFrame` documentation) ) a full-featured GUI, consisting of a menubar with menus, toolbar(s) and a statusbar.

The special thing with the GUI is the way it is created, handled/used and "destroyed" . All this has to be highly dynamic because now the user decides about which Part he wants to have active. OpenParts provides the basic framework for this:

- it tells you when you have to create your GUI and provides you the necessary references to the GUI related objects of OpenParts.

- it also tells you when the GUI is destructed.

- in addition you are notified when your Part gets the focus and gives you the choice whether to accept or reject the focus.

- you are also notified when your Part gets registered to a MainWindow, in order to register your component at several GUI servant objects, just like the menubar manager for example.

The very first step you have to make is to tell the OpenParts Part Interface (`
OPPartIf`) , the class you have to inherit from, what the actual widget is, because `
OPartIf` does not inherit from QWidget. This gives you the flexibility to separate your Part component from your actualy widget, but you don't have to do this. You can simply multiply inherit from `OPPartIf` and `
QWidget` or the appropriate widget class. In any way you specify your Part widget by calling `setWidget( your&&lowbar;;widget&&lowbar;;here )` . In most cases, when the Part component is the widget at the same time, you simply call `setWidget( this )` :-) . Make sure this call is done in the constructor of your class!

The next important point is that you will want to re-implement the virtual `init()` function of `
OPPartIf` . This is highly recommended since this function is called after your Part got registered by a MainWindow. The idea behind this function is that at the time the constructor of a Part gets executed, the Part itself is definitely not registered to a MainWindow, yet. But in fact you need to know when your Part gets registered, in order to register your Part at the GUI servant objects, which are only available via the MainWindow's interface. A reference to the MainWindow is available through the `
m&&lowbar;;vMainWindow` variable, which will be automatically initialized when the MainWindow registration is done, so don't use this variable before your `init()` function gets called (`m&&lowbar;;vMainWindow` will be nil anyway) .

Now over to the details of the `
init()` function. Here you should place all initialisation stuff which depends on being registered to a MainWindow. In addition you can do the above mentioned registration at the GUI managing objects. Usually this looks like the following example:

    void MyPart::init()
    {
      //register at the menubar manager if you want to use/display a menubar
      OpenParts::MenuBarManager_var menuBarManager = m_vMainWindow->menuBarManager();
      if ( !CORBA::is_nil( menuBarManager ) ) //check whether the shell window allows us to have a menubar
        menuBarManager->registerClient( id(), this ); //see chapter about the
                  //*barManager objects
                  //for further explanations

      //...the same with the toolbar
      OpenParts::ToolBarManager_var toolBarManager = m_vMainWindow->toolBarManager();
      if ( !CORBA::is_nil( toolBarManager ) )
        toolBarManager->registerClient( id(), this );

      //better define a class wide variable, of course
      OpenPartsUI::StatusBar_var m_vMyStatusBar;

      OpenParts::StatusBarManager_var statusBarManager = m_vMainWindow->statusBarManager();
      if ( !CORBA::is_nil( statusBarManager ) )
        m_vMyStatusBar = statusBarManager->registerClient( id() );
    }

Note that the registration calls for these three GUI element types are only necessary if you really want to use them. For example if your Part does not want to display any toolbar you should leave out the corresponding call. In addition you might come up with the situation that for example the `
toolBarManager()` call returns a nil reference, which indicates that the MainWindow does not allow its Parts to have a toolbar. Obviously the same applies for the menubar and the statusbar.

OpenParts makes use of KOM events to tell a Part about the construction/destruction of it's GUI. These are in particular:

- `OpenPartsUI::eventCreateMenuBar` ("OpenPartsUI/CreateMenuBar")

The attached argument is a `OpenPartsUI::MenuBar`

- `OpenPartsUI::eventCreateToolBar` ("OpenPartsUI/CreateToolBar")

The attached argument is a `OpenPartsUI::ToolBarFactory`

Depending on whether a Part wants to display a menubar and/or toolbar, the managing objects emit these two events to it. In regard to your implementation this means that you have to re-implement the `
event()` function (remember: A Part is a full-featured KOM Component) .

The attached event arguments indicate whether the toolbar(s) or the menubar are to be created or cleared. Check these arguments against `CORBA::is&&lowbar;;nil()` and you know :-) .

The OpenParts StatusBar is handled different compared to the MenuBar/ToolBar(s) . In fact it is easier: When registering at the OpenParts StatusBarManager you receive your `
OpenParts::StatusBar` as return value. You can then use the StatusBar everywhere in your Part, independend from whether it is visible (active) or not.

In the `
init()` function a lot of registration stuff is done, and corresponding to this in the `
cleanUp()` function (see chapter about KOM::Base
) you have to unregister from the GUI servant objects and free all appropriate references, following KOM's model of symmetric references and connections. Usually the code looks like this:

    void MyPart::cleanUp()
    {
      if ( m_bIsClean )
        return;

      //unregister our menubar
      OpenParts::MenuBarManager_var menuBarManager = m_vMainWindow->menuBarManager();
      if ( !CORBA::is_nil( menuBarManager ) )
        menuBarManager->unregisterClient( id() );

      //...the same with the toolbar
      OpenParts::ToolBarManager_var toolBarManager = m_vMainWindow->toolBarManager();
      if ( !CORBA::is_nil( toolBarManager ) )
        toolBarManager->unregisterClient( id() );

      OpenParts::StatusBarManager_var statusBarManager = m_vMainWindow->statusBarManager();
      if ( !CORBA::is_nil( statusBarManager ) )
        statusBarManager->unregisterClient( id() );

      //free other references here
      //...

      //this is IMPORTANT!!!
      //Always call the cleanUp() method of the base class when you're done!
      OPPartIf::cleanUp();
    }

#### Part Children

A Part Child is a usual Part with three extra features:

1. A Part Child has a Parent Part assigned.

2. It does not have a GUI and it therefore does not receive any GUI creation events.

3. The Parent Child receives events whenever there are any child related focus changes. Since the Part Child functionality is integrated in the `
   OpenParts::Part` interface and it's implementation, `
   OPPartIf`, you don't have to deal with additional classes when using Child Parts. Simply leave out the mapping of the OpenParts GUI events in the Child Part and instead map the Child Part events described below and assign the Parent Part via `
   setParent()`. See the interface description of `
   OpenParts::Part` , in
   openparts.idl, for further information about the events and the API in general.

#### How to embed a Part - the
OPFrame class

Now that you know how to create full-featured Part components it is still unexplained how Parts are really displayed/shown. Since Parts are no simple QWidgets but CORBA objects we need a helping hand here, which is the `OPFrame` class. In fact `
OPFrame` is a
QWidget, but in conjuction with Qt's
QXEmbed it embeds the Part's widget window. The usage of `OPFrame` is really easy, usually the code looks like this:

    ... somewhere in an application's widget ...
    myFrame = new OPFrame( the_parent_widget );
    myFrame->attach( a_reference_to_the_part_we_want_to_embed );
    myFrame->show();

In addition to the above example you can `
detach()` your Part, which you should usually do on exit. Just have a look at
opFrame.h, it is documented.

One last important thing you have to know about `
OPFrame` is that this class internally uses KOM referencing (using a `KOMVar` variable) to hold the Part. This means that there are two possible situations when using `OPFrame` :

- If the embedding Widget/Object uses KOM referencing as well to keep a reference to the Part then there are two objects influencing the Part's lifecycle. On the one hand there's the `OPFrame` which increases/decreases the Part's KOM reference counter when it gets attached/detached, and on the other hand there's the embedding Widget/Object. So make sure that you know when you free your KOM reference to the Part in regard to detaching the Part!

- If the embedding Widget/Object does not use KOM referencing then you have to know that when detaching the Part it automatically gets destroyed, since usually the `OPFrame` is the only object holding a KOM reference to the Part and the KOM reference counter therefore drops down to zero and issues the complete destruction of the object.

#### The center of OpenParts - the OpenParts::MainWindow interface

Another important component is the so called
OpenParts::MainWindow, being derived from a
KTMainWindow in the implementation (and therefore the top-level window of your application) and being the shell around visible sub-windows and shared GUI elements.

The MainWindow's functionality is extended by some builtin aggregates, the managing objects for the menu-/tool-/statusbar. These objects are either available directly via the `*barManager()` methods of the MainWindow's interface or indirectly by being aggregates and therefore available via the components interface repository (`
getInterface()`, `
supportsInterface()`, ...) .

A Part can only be displayed in a MainWindow and the MainWindow has to know about this. So before you can display a Part you have to register it to the MainWindow. This is done by calling the Part Interface's `
setMainWindow()` method (see previous chapter for further information about the process of registration) and this will give the Part a unique ID (which is for example used when addressing the part's GUI via the *bar manager objects) .

The MainWindow, as shell, has full control over all shared GUI elements. This means that it is responsible for

- creating the appropriate *bar managing objects

- providing a skeleton/standard GUI which is meant to be always present, no matter what Part is active

- telling the GUI managing objects to activate/deactivate a Part's GUI

The creation of the *bar managers can be easily done by simply performing a dummy call to `*barManager()` which usually returns a pointer to the appropriate manager and also creates a new one if it does not exist yet. It is recommended to perform these calls in the constructor of your MainWindow.

The creation/handling of the skeleton GUI is explained later in the chapters about
OPMenu(Bar)/
OPToolBar .

Your MainWindow emits a Qt signal (`
activePartChanged`) which informs you about a focus change of the active part, meaning whenever the user clicks on a non-active Part and it accepts the focus. Beside the pure informative sense of this signal it is recommended to connect to this signal and perform the following two steps in the slot implementation:

1. deactivate the previous active Part's GUI by calling the *bar managers `
   clear()` function, which will do the job and, beside some internal stuff, emit the GUI events (see previous chapter) to the Part.

2. activate the new active Part's GUI by calling the *bar managers `
   create()` function, which will, similar to the activation, emit events to the Part. This is not really required but it is highly recommended. The following code is usually used for this:

             void NameOfYourMainWindow::slotActivePartChanged( unsigned long old_id,
                                                               unsigned long new_id )
             {
               // clear the menu/tool/statusbar(s)
               menuBarManager()->clear();
               toolBarManager()->clear();
               statusBarManager()->clear();
               // create the new Part's GUI
               menuBarManager()->create( new_id );
               toolBarManager()->create( new_id );
               statusBarManager()->create( new_id );
             }

Now that the MainWindow handles all the shared "stuff" there is one thing which was not mentioned in this documentation, yet: What about the MainWindow's caption? The OpenParts MainWindow interface allows parts to have their own window captions, but how does OpenParts handle this?

Well, there are two ways:

1. `
   OPMainWindow` provides you a so called `
   AutoCaption` mode which automatically changes the MainWindow's caption whenever the active part changes. This is enabled by default.

2. But sometimes the shell wants to have full control over the window's caption, and this is accomplished by disabling the `AutoCaption` mode, which leads to the situation that the window's caption is not changed by OpenParts in any way but instead gives you control over it.

#### Access shared GUI elements through CORBA - OpenPartsUI and its interfaces

Well, now that we know when we have to construct/destruct a Part's GUI, via the
OpenPartsUI events, we have to learn how to really create it, because we don't have the common
KMenuBar,
KToolBar, etc. classes anymore available. The replacement for them are CORBA Objects, described in openparts&&lowbar;;ui.idl which is, together with the corresponding implementations, a part of the partsui module. The interfaces are 98&&percnt;;; similar to the KDE/Qt classes, so they're quite easy to use. Instead of bloating up this documentation with example code I rather suggest reading the tutorials in kdelibs/corba/tutorials .

#### Pixmaps
and Strings
in OpenParts -
OPUIUtils

Toolbars and menus are usually beautified with pixmaps, using QPixmap classes. As we now use a CORBA interface to access our GUI elements,
QPixmap has become
OpenPartsUI::Pixmap for OpenParts applications. OpenPartsUI::Pixmap is just a "stringified" QPixmap, and opUIUtils.(h,cc) contains some easy to use conversion routines.

In addition OPUIUtils contains string conversion routines between CORBA::WChar* and
QString. This is necessary since Qt version >=2.0 supports
Unicode, via QString, all over the place, and obviously GUI elements like menus or toolbars have been converted to support this. OpenParts has been converted, too, by using "wstring" (
CORBA::WChar *) in the interfaces and by using and providing conversion routines. These routines are static member functions of the OPUIUtils class, just like with the pixmap conversion. To simplify the usage, two macros have been defined: `
Q2C` and `
C2Q` . The first one converts a QString into a CORBA::WChar * string and the second one vice-versa.

When converting from QString to CORBA::WChar * the conversion routine allocates memory. To avoid memory leaks
it is highly recommended to use CORBA::WString&&lowbar;;var variables. Exactly the same applies for QPixmap -> OpenPartsUI::Pixmap conversions: Use
OpenPartsUI::Pixmap&&lowbar;;var , and you don't have to worry about leaks :-) .

Here's some example code, to show how to do it right:

        ...
        OpenPartsUI::Pixmap_var pm = OPUIUtils::convertPixmap( QPixmap_variable_here );
        someToolBar->insertButton( pm, ... );
        ...
        //use the same pm variable again
        pm = OPUIUtils::convertPixmap( another_qpixmap );
        ...

Similar things have to be done with QString's:

        ...
        CORBA::WString_var text = Q2C( QString_here );
        someMenuBar->insertItem7( text, ... );
        ...
        // or you can write:
        someMenuBar->insertItem7( ( text = Q2C( QString_here ) ) , ...);
        ...

One note left: When you return a "wide string" (CORBA::WChar *) as a function result by using `Q2C`, make sure not to use `
CORBA::wstring&&lowbar;;dup()`.

Example:

      return CORBA::string_dup( Q2C( QString_here ) ); //!!!! WRONG!!!!!
      ...
      return Q2C( QString_here ); // RIGHT! because Q2C already allocates the string

That's it! Have fun using KOM/OpenParts :-)

TODO:

- KOMShutdownManager

- AutoLoader template classes

- OPApplication Interface + Factories

## The KDE Daemon

Simon Hausmann <hausmann@kde.org>

v1.0 26, June 1999

This documentation describes the KDE Daemon, the services it provides and how to use it. So if you're dealing with CORBA server
s in the KDE or if you're interested in a smart way of accessing KService
data in your application then you might want to read this and use
kded

### Introduction

The KDE Daemon, herein simply named
kded, is a central daemon in the KDE Desktop Environment. It provides three services:

- The KDE Trader (
  KTrader) , which provides an easy-to-use but still extremly powerful interface to the KDE Registry.

- The KDE Activator (
  KActivator) , which makes accessing CORBA based services in KDE the easiest thing of the world ;-) .

- The KDE Naming Service (
  KNaming) , which provides a simple but useful naming service for CORBA Objects.

### Using The KDE Daemon In Your Application

Before we can use any of
kded's services, we have to know something about it's "position" in the KDE and the way it itself and the services can be accessed.

As
kded uses CORBA to communicate with it's clients, your application (as client), has to

- link to the
  kded library,
  libkded.

- link to libmico

- initialize the MICO
  ORB

The first two points are fixed for all clients, the third one depends on the specific client. In general you have to choices:

- If your application already uses KOM
  , then you're fine with using
  KOMApplication (as you need it anyway) .

- If you don't want to use KOM
  , then you can initialize the ORBA "manually": `CORBA::ORB_ptr orb = CORBA::ORB_init( argc, argv, "mico-local-orb" );` Please note that when using this construct or similar ones, your application is not able to act as CORBA server
  if you are using KDE/Qt classes at the same time. This is due to the fact that the ORB
  needs it's own event loop, beside the main Qt event loop. Currently only
  KOMApplication, as part of the KDE Object Model, implements the needed functionality to combine both event loops and thus making KDE apps able to serve CORBA Objects.

Although
kded uses CORBA extensively, the API is kept simple and free from complicated CORBA stuff. In fact when talking about
kded and it's API not the
kded server is meant but the KDE Daemon library. This library contains the interface to the server (for the clients) as well as the whole server functionality. The reason for this is based on the idea that an application using
kded should not be forced to rely on an existing
kded server binary nor a running server at all. The following three situations may exist when a client app gets started:

- No
  kded is running and there's no
  kded server binary in the path. In this situation
  libkded will automatically start a local instance of
  kded in the application's process.

- No
  kded is running but there's a
  kded server binary available. Here
  libkded will start the kded executable and connect to it.

- kded is already running :-) . The fact that a
  kded is running is indicated by a special property on the X Root Window.
  kded publishes it's IOR
  (Interoperable Object Reference) in this property. This also ensures that a kded
  instance is specific to a X display and therefore to your KDE session.

To sum it up: libkded
will always make sure that the services of kded
are available for your client application, no matter in what alien environment the app is running :-) . And, although you don't have to care about this, you can optionally control this behaviour of libkded
by adding one of the following three commandline arguments
to your app:

- "-kdedlocal" , which will make libkded
  always start a process-local instance of kded and it's services.

- "-kdedremote" , which will make libkded
  try to look for a running kded
  instance before starting a local one.

- "-kdedior ior&&lowbar;;here" , which will make libkded
  connect to the kded server
  specified by the given IOR
  .

Your actual interface to kded
and it's services is the `
KdedInstance` class, defined in kded&&lowbar;;instance.h . So if you want to use kded
(I guess that's why you're reading this shit ;) ) then make sure to create one single instance of it, preferably by adding the following line somewhere in the beginning of your `main()` : `KdedInstance( argc, argv, _a_reference_to_the_orb_here_ );` If you're using KOMApplication
as application object (make sure to create the instance before this line) , then you're fine by specifying `komapp&&lowbar;;orb` as reference to the ORB
.

As there is always only one single instance of this class, you can simply access it by the static `
self()` method of the class from anywhere you want. No need to pass
KdedInstance arguments all around in your program ;-) .

For further information about `
KdedInstance` you might want to read `
kded&&lowbar;;instance.h` , it's pretty good documented.

### KTrader

As already mentioned in the introduction, KTrader
gives you access to the registry. "Now what the hell is the registry?" you might ask. As this is just a documentation about kded
/libkded
, I can only respond: "Please consult the documentation of/in `
libkio` for more information" :-&&rcub;; . Just one thing about it here: KTrader
loads the whole and bloaty registry for you. Thanks to the magic of `
libkio` the loaded registry will always be in sync with the "real" registry, the .desktop
files in the following standard directories (both types, the system wide and the user ones) :

- applnk

- mimelnk

- services

- servicetypes

Now over to KTrader
and it's API. Similar to `
KdedInstance` there can be only one single instance. The difference is that you don't have to care about allocating it, just simply get a reference to the KTrader
by calling the `ktrader()` method of `
KdedInstance` . And: don't even think about deleting the returned reference! Just simply use it and be happy with it :-) . (hey, kded
is designed to be easy to use, no need for difficult stuff...)

The KTrader
API is even so simple that it contains only two methods ;-) . But before I describe these methods you have to know something about the kind of data KTrader
returns. In simple words: You will always get a list of KService
objects. More detailed: The returned list is a `
QValueList` and the entries are `
KSharedPtr`'s (FIXME: will soon be renamed to QSharedPtr, as it will become part of Qt.... AFAIK) to `KService
` objects. Please read the corresponding Qt documentation about these two classes. The big advantage of using these two template classes is that everything becomes easy for you and that the memory consumption is kept at a minimum . You don't have to care about pointers, freeing them and cleaning up the list, as long as you use `
KTrader::ServicePtr` variables to hold the KService
objects and as long as you use `
KTrader::OfferList` to pass the list around in your program. So: Remember to always use these two types when dealing with KTrader
!

Now over to the two methods.

`
KTrader::listServices()` returns your a list of all available services in the whole KDE. (no need for further explanations I think...)

`
KTrader::query()` is the key method of this beast. It performs a lookup in the registry database, given your information about what you want to have. The first argument is the name of the servicetype which all returned services must implement. If you're unsure about the word "servicetype" , then you can replace it with "mimetype" , for most, but not all, cases.

The second argument is an additional constraint expression, which has to be fulfilled by a service.

The third argument is a preference expression after which the returned services will be sorted. The value of the expression has to be numeric.

The syntax of these two expressions is equal with the language of the standard CORBA Trader (this is due to the fact that the parsing code is from the COS
Trader of MICO
) . The language is not very difficult and I don't want to bloat this documentation with further explanations about it. Please consult your CORBA literature for more information. Just one thing you have to know: Comparisons are always done with the properties of the KService
object, which are the standard entries (Name, ServiceType, RepoIds, ...) plus the ones specified in the servicetype declaration and read by KService
.

Well, after so much theoretical explanations it's time for some practical example code:

      ...
      //get a reference to the KTrader
      KTrader *trader = KdedInstance::self()->ktrader();

      ...
      //will return a list of all services which implement the servicetype
      //named "text/plain"
      KTrader::OfferList offers = trader->query( "text/plain" );

      ...
      //will return a list of all services which implement the servicetype
      //named "image/gif" and which have the AllowAsDefault property set true
      KTrader::OfferList offers = trader->query( "image/gif", "AllowAsDefault == TRUE" );

      ...
      //will return KSpread ;-)
      KTrader::OfferList offers = trader->query( "KOfficeDocument", "(Exec == 'kspread') and (Path != '/opt/gnome/bin')" );

      ...
      //will return a list of all services which implement the servicetype
      //named "BlahFoo" and which will be sorted (from lowest to highest) by
      //the value of the property "Price" , declared in the servicetype
      //declaration of BlahFoo.
      KTrader::OfferList offers = trader->query( "BlahFoo", QString::null, "min Price" );

Please note that KTrader
, since it queries `
libkio` for services, will always return services sorted by the user's preferences for the specific servicetype. These preferences can be specified in the file "profilerc" .

#### KTraderServiceProvider

This section requires to be familiar with `
libkio` and it is meant for everybody who wants to use KRun
in his application.

KRun
requires a fully loaded registry in order to resolve mimetype <-> application bindings. A fully loaded registry means that you need a `
KServiceTypeFactory` and a KServiceFactory, which both load the appropriate `
KServiceType``
KService` objects. Now the KServiceType information doesn't need that much memory, but the KService
object really eat loooots of it. And isn't it kind of stupid to load this information if this is already done by kded
? Yes, it is ;-) .

What we would need is to make KRun
query KTrader
for KService
data, instead of directly using KServiceTypeProfile
. Fortunately KRun
is flexible enough for this, we just need a re-implementation of the KServiceProvider
, defined in `
krun.h` and used by KRun
. Guess what, but KTrader
provides you this re-implementation :-) . Just have a look at the end of `
ktrader.h`.

To sum it up: The following line makes KRun
query kded
, in your application:

      ...
      //place this somewhere BEFORE the first usage of KRun, preferable somewhere
      //in main()
      KTraderServiceProvider serviceProvider;
      ...

That's all, except that you must have a
KdedInstance in order to be able to use it.

### KActivator

One often mentioned problem, when talking about applications which provide their services via CORBA, is how to start and access these services. Solutions like making apps write the IOR
of an object somewhere into a file in the filesystem or similar approaches are just hacks ;-) . Better use KActivator
, since it can automatically, on demand, start servers for you or use already running ones. This is accomplished with the help of the mediators for BOA/POA
and the IMR
(Implementation Repository), both provided by MICO
. Fortunately you don't have to deal with IMR entries and the mediators.

Before you can use KActivator
to start a server for you, you have to register the server's service. There are two possible ways, the last one is highly recommended though:

- Register and unregister it manually, by using the two corresponding methods of the KActivator
  .

- Go the smart way and provide the necessary information in a .desktop
  file. Fortunately KService
  supports the necessary properties, like repository ids, activation mode and others.

For the second point it's important that the .desktop
file is available for KRegistry
, by placing it in a directory which gets scanned by the registry. If your application has already a .desktop
file in the applnk tree for example, then you're fine with adding the necessary fields in there. Otherwise the directory named "services" (either system-wide or user-local) is the best place for it. If you provide the CORBA service information this way, then KActivator
will automatically detect it and register it automatically at the IMR. This means that it is immediately available for KActivator
and thus to your client app. And since KRegistry
is such a cool thing :-) , you can do all this even at run-time, when kded
is running. Just place the .desktop
file in one of the right directories for it and KActivator
will update the IMR on-the-fly. The same applies obviously for just deleted or modified .desktop
files, which will make KActivator
adjust the IMR. Now you might get the idea why this is the preferred way :-)) .

Now that you know how to register CORBA services you will want to know how to "access" it. `activateService()` is your friend here. Simply pass it the name of the service, the repository id of the server object and the object's tag and it will return you a functional object reference. That's all :-)

Depending on the service's activation mode, KActivator
will either return a reference to an already running server or it will start a new instance.

One note about the returned object reference: This is a virtual reference, which means that that server object is started when the first call is invoked on this reference, thus making your server get started "lazy", only on-demand. But that's just additional information, you don't have to deal and know about it at all. Just be happy with your functional object reference :-) .

Want some examples? Here we go:

This is how a .desktop
file could look like, assuming that the commandline "--server" starts the app in CORBA server
mode:

    Name=MyApp
    Exec=fooblah
    CORBAExec=fooblah --server
    X-KDE-RepoIds=IDL:Foo/Blah:1.0#MyFoo
    X-KDE-ActivationMode=shared

If KActivator
"gets" this file, it will register the service and then you're able to do something like this:

      ...
      KActivator *activator = KdedInstance::self()->kactivator();
      ...
      CORBA::Object_var obj = activator->activateServer( "MyApp", "IDL:Foo/Blah:1.0", "MyFoo );
      ...

The above example will either start a new fooblah instance or connect to a running one.

For further information please have a look at the examples in kdelibs/corba/tutorials/kded . The example application there registers the server manually via `registerService` .

Please note: The server has to be started by kded
in order to make KActivator
return a reference to a running one. Executing "fooblah" from somewhere else will not make KActivator
use it. This is a problem for persistent servers like KDesktop
for example. But there's a solution available, just read the next chapter about the KDE Naming Service :-) .

### KNaming

The KDE Naming Service, KNaming
, is also a very simple service, but it's pretty useful and in some cases a very nice solution to make a CORBA client connect to a persistent CORBA server
.

With KNaming
you can bind a freely chooseable name to a CORBA object. And since kded
is system (session) wide available, your object becomes available for any client which is able to connect to kded
.

There's not much to explain here :-&&rcub;; , so I suggest having a look at the KNaming
API, in `
knaming.h`.

Example code can be, again, found in kdelibs/corba/tutorials/kded . Here's just a short real example situation:

Just think of KDesktop
, that nice app providing your background desktop icons. It provides some functionality via CORBA, just have a look at kdesktop.idl to see what I'm talking about. Now the problem for KDesktop
is: How can it provide this service to other apps in the system? Writing an IOR
into some file is no clean solution IMHO, and using KActivator
doesn't work because KDesktop
does not get started by kded
but by the startkde script on KDE startup. So we find a better way and make KDesktop
register it's object at KNaming
. This is done by the following lines:

      ...
      KNaming *knaming = KdedInstance::self()->knaming();
      naming->bind( "KDesktop", kdesktop_object_here );
      ...

Well, I told you a lie ;) : KNaming
does not really bind to "KDesktop
" but instead to "IDL:KDesktopIf:1.0" , but since there's no naming convention for the naming I have choosen a more readable name, IMHO of course :-) (don't mind me David :] ) . I personally prefer human readable names ;) , in contrary to repository ids with tags.

Back to KNaming
and KDesktop
: Now any client application, kfmclient for example, can "connect" to KDesktop
. Just like this:

      ..
      KNaming *knaming = KdedInstance::self()->knaming();
      ...
      CORBA::Object_var obj = knaming->resolve( "KDesktop" );
      KDesktopIf_var kdesky = KDesktopIf::_narrow( obj );
      kdesky->selectAll(); //let's confuse the user by selecting all icons ;-)
      ...

## DCOP: Desktop COmmunications Protocol

Preston Brown <pbrown@kde.org>

Version 1.0, October 14, 1999

Howto for the KDE Desktop COmmunincations Protocol implementation

### Motivation and Background

The motivation behind building a protocol like DCOP is simple. For the past year, we have been attempting to enable interprocess communication between KDE applications. KDE already has an extremely simple IPC mechanism called KWMcom, which is (was!) used for communicating between the panel and the window manager for instance. It is about as simple as it gets, passing messages via X Atoms. For this reason it is limited in the size and complexity of the data that can be passed (X atoms must be small to remain efficient) and it also makes it so that X is required. CORBA was thought to be a more effective IPC/RPC solution. However, after a year of attempting to make heavy use of CORBA in KDE, we have realized that it is a bit slow and memory intensive for simple use. It also has no authentication available.

What we really needed was an extremely simple protocol with basic authorization, along the lines of MIT-MAGIC-COOKIE, as used by X. It would not be able to do NEARLY what CORBA was able to do, but for the simple tasks required it would be sufficient. Some examples of such tasks might be an application sending a message to the panel saying, "I have started, stop displaying the 'application starting' wait state," or having a new application that starts query to see if any other applications of the same name are running. If they are, simply call a function on the remote application to create a new window, rather than starting a new process.

### Implementation

DCOP is a simple IPC/RPC mechanism built to operate over sockets. Either unix domain sockets or tcp/ip sockets are supported. DCOP is built on top of the Inter Client Exchange (ICE) protocol, which comes standard as a part of X11R6 and later. It also depends on Qt, but beyond that it does not require any other libraries. Because of this, it is extremely lightweight, enabling it to be linked into all KDE applications with low overhead.

#### Model

The model is simple. Each application using DCOP is a client. They communicate to each other through a DCOP server, which functions like a traffic director, dispatching messages/calls to the proper destinations. All clients are peers of each other.

Two types of actions are possible with DCOP: "send and forget" messages, which do not block, and "calls," which block waiting for some data to be returned.

Any data that will be sent is serialized (marshalled, for you CORBA types) using the built-in QDataStream operators available in all of the Qt classes. This is fast and easy. Currently, there is no type checking or parameter checking available for RPC, but this may be provided at some time in the future in the form of a simple IDL-like compiler (NOTE: 5 days later the IDL compiler is already started; look in dcopidl/). Until that is available, you will have to code some things by hand that normally the compiler or CORBA take care of automatically, but it is not a lot of work.

#### Establishing the Connection

KApplication has gained a method called "KApplication::dcopClient()" which returns a pointer to a DCOPClient instance. The first time this method is called, the client class will be created. DCOPClients have unique identifiers attached to them which are based on what KApplication::name() returns. In fact, if there is only a single instance of the program running, the appId will be equal to KApplication::name().

To actually enable DCOP communication to begin, you must use DCOPClient::attach(). This will attempt to attach to the DCOP server. If no server is found or there is any other type of error, attach() will return false. Applications which are DCOP-enabled should probably do something like this at startup time:

    client = kApp->dcopClient();
    if (!client->attach()) {
      QMessageBox::error(this, i18n("Error connecting to DCOP server"),
                         i18n("There was an error connecting to the Desktop\n"
                              "communications server.  Please make sure that\n"
                              "the 'dcopserver' process has been started, and\n"
                              "then try again.\n"));
      exit(1);
    }

After connecting with the server via DCOPClient::attach(), you need to register this appId with the server so it knows about you. Otherwise, you are communicating anonymously. Use the DCOPClient::registerAs(const QCString &&;name) to do so. In the simple case:

    /*
     * returns the appId that is actually registered, which _may_ be
     * different from what you passed
     */
    appId = client->registerAs(kApp->name());

If you never retrieve the DCOPClient pointer from KApplication, the object will not be created and thus there will be no memory overhead.

You may also detach from the server by calling DCOPClient::detach(). If you wish to attach again you will need to re-register as well. If you only wish to change the ID under which you are registered, simply call DCOPClient::registerAs() with the new name.

#### Sending Data to a Remote Application

To actually communicate, you have one of two choices. You may either call the "send" or the "call" method. Both methods require three identification parameters: an application identifier, a remote object, a remote function. Sending is asynchronous (i.e. it returns immediately) and may or may not result in your own application being sent a message at some point in the future. Then "send" requires one and "call" requires two data parameters.

The remote object must be specified as an object hierarchy. That is, if the toplevel object is called "fooObject" and has the child "barObject", you would reference this object as "fooObject/barObject". Functions must be described by a full function signature. If the remote function is called "doIt", and it takes an int, it would be described as "doIt(int)". Please note that the return type is not specified here, as it is not part of the function signature (or at least the C++ understanding of a function signature). You will get the return type of a function back as an extra parameter to DCOPClient::call(). See the section on call() for more details.

In order to actually get the data to the remote client, it must be "serialized" via a QDataStream operating on a QByteArray. This is how the data parameter is "built". A few examples will make clear how this works.

Say you want to call "doIt" as described above, and not block (or wait for a response). You will not receive the return value of the remotely called function, but you will not hang while the RPC is processed either. The return value of send() indicates whether DCOP communication succeeded or not.

    QByteArray params;
    QDataStream stream(params, IO_WriteOnly);
    params << 5;
    if (!client->send("someAppId", "fooObject/barObject", "QString doIt(int)",
    	          params))
      qDebug("there was some error using DCOP.");

OK, now let's say we wanted to get the data back from the remotely called function. You have to execute a call() instead of a send(). The returned value will then be available in the data parameter "reply". The actual return value of call() is still whether or not DCOP communication was successful.

    QByteArray params, reply;
    QCString replyType;
    QDataStream stream(params, IO_WriteOnly);
    params << 5;
    if (!client->call("someAppId", "fooObject/barObject", "doIt(int)",
                      params, replyType, reply))
      qDebug("there was some error using DCOP.");
    else {
      QDataStream stream2(reply, IO_ReadOnly);
      if (replyType == "QString") {
        QString result;
        stream2 >> result;
        print("the result is: %s",result.latin1());
      } else
        qDebug("doIt returned an unexpected type of reply!");
    }

#### Receiving Data via DCOP

Currently the only real way to receive data from DCOP is to multiply inherit from the normal class that you are inheriting (usually some sort of QWidget subclass or QObject) as well as the DCOPObject class. DCOPObject provides one very important method: DCOPObject::process(). This is a pure virtual method that you must implement in order to process DCOP messages that you receive. It takes a function signature, QByteArray of parameters, and a reference to a QByteArray for the reply data that you must fill in.

Think of DCOPObject::process() as a sort of dispatch agent. In the future, there will probably be a precompiler for your sources to write this method for you. However, until that point you need to examine the incoming function signature and take action accordingly. Here is an example implementation.

    bool BarObject::process(const QCString &&;fun, const QByteArray &&;data,
    		        QCString &&;replyType, QByteArray &&;replyData)
    {
      if (fun == "doIt(int)") {
        QDataStream stream(data, IO_ReadOnly);
        int arg;
        QString res;
        stream >> arg;
        res = self->doIt(arg);
        QDataStream stream2(replyData, IO_WriteOnly);
        stream2 << res;
        replyType = "QString";
        return true;
      } else {
        qDebug("unknown function call to BarObject::process()");
        return false;
      }
    }

### Conclusion

Hopefully this document will get you well on your way into the world of inter-process communication with KDE! Please direct all comments and/or suggestions to Preston Brown <pbrown@kde.org>.

#### Performance Tests

A few back-of-the-napkin tests folks:

Code:

    #include <kapp.h>

    int main(int argc, char **argv)
    {
      KApplication *app;

      app = new KApplication(argc, argv, "testit");
      return app->exec();
    }

Compiled with:

g++ -O2 -o testit testit.cpp -I$QTDIR/include -L$QTDIR/lib -lkdecore

on Linux yields the following memory use statistics:

    VmSize:     8076 kB
    VmLck:         0 kB
    VmRSS:      4532 kB
    VmData:      208 kB
    VmStk:        20 kB
    VmExe:         4 kB
    VmLib:      6588 kB

If I create the KApplication's DCOPClient, and call attach() and registerAs(), it changes to this:

    VmSize:     8080 kB
    VmLck:         0 kB
    VmRSS:      4624 kB
    VmData:      208 kB
    VmStk:        20 kB
    VmExe:         4 kB
    VmLib:      6588 kB

Basically it appears that using DCOP causes 100k more memory to be resident, but no more data or stack. So this will be shared between all processes, right? 100k to enable DCOP in all apps doesn't seem bad at all. :)

OK now for some timings. Just creating a KApplication and then exiting (i.e. removing the call to KApplication::exec) takes this much time:

0.28user 0.02system 0:00.32elapsed 92&&percnt;;CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (1084major+62minor)pagefaults 0swaps

I.e. about 1/3 of a second on my PII-233. Now, if we create our DCOP object and attach to the server, it takes this long:

0.27user 0.03system 0:00.34elapsed 87&&percnt;;CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (1107major+65minor)pagefaults 0swaps

I.e. about 1/3 of a second. Basically DCOPClient creation and attaching gets lost in the statistical variation ("noise"). I was getting times between .32 and .48 over several runs for both of the example programs, so obviously system load is more relevant than the extra two calls to DCOPClient::attach and DCOPClient::registerAs, as well as the actual DCOPClient constructor time.

## Copyright

    KDevelop Copyright 1998, 1999, 2000 The KDevelop Team.

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Each chapter of this handbook contains the documentation of the according authors, therefore the copyright remains on the side of those. This handbook has been assembled by agreement with each author about the usage of his documentation. Reprint is therefore only allowed on agreement with the KDevelop Team and the authors of each chapter.

KDevelop is Copyright 1998,1999,2000 The KDevelop Team.

This handbook itself is part of the KDevelop Intergrated Development environment.

The KDE Developer's mini-HOWTO is copyright of David Sweet.<dsweet@chaos.umd.edu>

KDE KOM / OpenParts is copyright of Torben Weis <weis@kde.org> and Bernd Wuebben <wuebben@kde.org>

Using KDE KOM / OpenParts HOWTO and The KDE Daemon are copyright of Simon Hausmann <hausmann@kde.org>

DCOP: Desktop COmmunications Protocol is copyright of Preston Brown <pbrown@kde.org>

## Glossary

CORBA

  Common Object Request Broker Architecture, standard introduced by the OMG for distributed components that can be used independently of platform, implementation and operating system. CORBA itself is only the standard which requires a corresponding implementation that fulfills the requirements of the standard. KDE uses the MICO implementation, which provides a C++ interface to CORBA services.

BOA

  Basic Object Adapter. Interface defined by the OMG, which offers fundamental operations for managing remote objects.

COS

  Common Object Services. Collection of standard services to simplify developing standard applications. Naming, Event and Lifecycle-services are a part of the COS collection.

DII

  Dynamic Invocation Interface, interface to execute dynamic invocations in opposition to static invocations (clent side)

Dynamic Invocation

  Creation and execution of a request, whose signature is probably first known at runtime in opposition to static invocation.

DSI

  Dynamic Skeleton Interface. Server side in opposition to a DII.

GIOP

  General-Inter-ORB-Protocol

IIOP

  Internet-Inter-ORB-Protocol

IMR

  Implementation Repository

IOR

  Interoperable Object Reference. The reference is specificated in the two parts GIOP and IIOP.

IDL

  Interface Definition Language. C++-Syntax similar programming language to describe object interfaces. The implementation translates IDL's to their target programming language.

Interface

  in CORBA, all exported methods and attributes of an object are called an interface, whereby these are defined in IDL. Elements which are not specified in the interface are not accessible by clients.

IOP

  Interoperability Profile

MICO

  free implementation of the CORBA standard. KDE's KOM/OpenParts technology is based on the MICO implementation.

OMA

  Object Management Architecture

OMG

  Object Management Group

ORB

  Object Request Broker, abstract name for the communication service between clients and remote objects.

POA

  Portable Object Adapter. The POA extends the features the BOA provides; introduced in the CORBA 2.2 standard.