Next Previous Table of Contents

### 5. 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.

### 5.1 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.

### 5.2 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_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_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_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_READ` )
- writing filters ( `FM_WRITE` )
- implementing filters ( `FM_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_WRITE`, then followed by `FM_IMPLEMENT` and finally by `FM_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_EVENT` macros, all using the same syntax:

    EMIT_EVENT_x( destination_object, event_name, event_argument)

"destination_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_name" is self-explaining ;-) . The event argument depends on the specific macro, which are in particular:

- `EMIT_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_EVENT_BOOLEAN`, useful for boolean event arguments. The only difference to the `EMIT_EVENT` macro is that it uses CORBA::Any::from_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_EVENT_OCTET`, similar to EMIT_EVENT_BOOLEAN, useable for CORBA::Octet values.
- `EMIT_EVENT_CHAR`, similar to EMIT_EVENT_BOOLEAN, useable for char (CORBA::Char) values.
- `EMIT_EVENT_WCHAR`, similar to EMIT_EVENT_BOOLEAN, useable for CORBA::WChar values.
- `EMIT_EVENT_STRING`, similar to EMIT_EVENT_BOOLEAN, useable for char * (CORBA::Char*) values.
- `EMIT_EVENT_WSTRING`, similar to EMIT_EVENT_BOOLEAN, useable for CORBA::WChar* values.
- `EMIT_EVENT_OBJECT`, similar to EMIT_EVENT_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_MAPPER` macro and ends with `END_EVENT_MAPPER` . Similar to the `EMIT_EVENT_x` macros, the `MAPPING` macros consist of a general `MAPPING` macro and the following friends:

- `MAPPING_BOOLEAN`
- `MAPPING_OCTET`
- `MAPPING_CHAR`
- `MAPPING_WCHAR`
- `MAPPING_STRING`
- `MAPPING_WSTRING`
- `MAPPING_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_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 _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_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_orb` and `komapp_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 .

### 5.3 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_widget_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_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_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_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_ui.idl which is, together with the corresponding implementations, a part of the partsui module. The interfaces are 98%; 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_var variables. Exactly the same applies for QPixmap -> OpenPartsUI::Pixmap conversions: Use OpenPartsUI::Pixmap_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_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

Next Previous Table of Contents