[ Previous document | Content Table | Next document ]

6    Office Development

This chapter describes the application environment of the OpenOffice.org application. It assumes that you have read the chapter 2 First Steps, and that you are able to connect to the office and load documents.

In most cases, developers use the functionality of OpenOffice.org by opening and modifying documents. The interfaces and services common to all document types and how documents are embedded in the surrounding application environment are discussed.

It is also possible to extend the functionality of OpenOffice.org by replacing the services mentioned here by intercepting the communication between objects or by creating your own document type and integrating it into the desktop environment. All these things are discussed in this chapter.

6.1    OpenOffice.org Application Environment

6.1.1    Overview

The OpenOffice.org application environment is made up of the desktop environment and the framework API.


Illustration 6.1: OpenOffice.org Application Environment

The desktop environment consists of the desktop and auxiliary objects. It employs the framework API to carry out its functions. The framework API currently has two parts: the component framework and dispatch framework. The component framework follows a special Frame-Controller-Model paradigm to manage components viewable in OpenOffice.org. The dispatch framework handles command requests sent by the GUI.

Desktop Environment

The com.sun.star.frame.Desktop service is the central management instance for the OpenOffice.org application framework. All OpenOffice.org application windows are organized in a hierarchy of frames that contain viewable components. The desktop is the root frame for this hierarchy. From the desktop you can load viewable components, access frames and components, terminate the office, traverse the frame hierarchy and dispatch command requests.

The name of this service originates at StarOffice 5.x, where all document windows were embedded into a common application window that was occupied by the StarOffice desktop, mirroring the Windows desktop. The root frame of this hierarchy was called the desktop frame. The name of this service and the interface name com.sun.star.frame.XDesktop were kept for compatibility reasons.

The desktop object and frame objects use auxiliary services, such as the com.sun.star.document.TypeDetection service and other, opaque implementations that interact with the UNO-based office, but are not accessible through the OpenOffice.org API. Examples for the latter are the global document event handling and its user interface (Tools – Configure – Events), and the menu bars that use the dispatch API without being UNO services themselves. The desktop service, together with these surrounding objects, is called the desktop environment.


Illustration 6.2: The Desktop terminates the office and manages components and frames

The viewable components managed by the desktop can be three different kinds of objects: full-blown office documents with a document model and controllers, components with a controller but no model, such as the bibliography and database browser, or simple windows without API-enabled controllers, for example, preview windows. The commonality between these types of components is the com.sun.star.lang.XComponent interface. Components with controllers are also called office components, whereas simple window components are called trivial components.

Frames in the OpenOffice.org API are the connecting link between windows, components and the desktop environment. The relationship between frames and components are discussed in the next section 6.1.1 Office Development - OpenOffice.org Application Environment - Overview - Framework API.

Like all other services, the com.sun.star.frame.Desktop service can be exchanged by another implementation that extends the functionality of OpenOffice.org. By exchanging the desktop service it is possible to use different kinds of windows or to make OpenOffice.org use MDI instead of SDI. This is not an easy thing to do, but it is possible without changing any code elsewhere in OpenOffice.org.

Framework API

The framework API does not define an all-in-one framework with strongly coupled interfaces, but defines specialized frameworks that are grouped together by implementing the relevant interfaces at OpenOffice.org components. Each framework concentrates on a particular aspect, so that each component decides the frameworks it wants to participate in.

Currently, there are two of these frameworks: the component framework that implements the frame-controller-model paradigm and the dispatch framework that handles command requests from and to the application environment. The controller and frame implementations form the bridge between the two frameworks, because controllers and frames implement interfaces from the component framework and dispatch framework.

The framework API is an abstract specification. Its current implementation uses the Abstract Window Toolkit (AWT) specified in com.sun.star.awt, which is an abstract specification as well. The current implementation of the AWT is the Visual Component Library (VCL), a cross-platform toolkit for windows and controls written in C++ created before the specification of com.sun.star.awt and adapted to support com.sun.star.awt.

Frame-Controller-Model Paradigm in OpenOffice.org

The well known Model-View-Controller (MVC) paradigm separates three application areas: document data (model), presentation (view) and interaction (controller). OpenOffice.org has a similar abstraction, called the Frame-Controller-Model (FCM) paradigm. The FCM paradigm shares certain aspects with MVC , but it has different purposes, therefore it is best to approach FCM independently from MVC. The model and controller in MVC and FCM are quite different things.

The FCM paradigm in OpenOffice.org separates three application areas: document object (model), screen interaction with the model (controller) and controller-window linkage (frame).

The purpose of FCM is to have three exchangeable parts that are used with an exchangeable window system:

It is possible to write a new controller that presents an existing model in a different manner without changing the model or the frame. A controller depends on the model it presents, therefore a new controller for a new model can be written.

Developers can introduce new models for new document types without taking care of the frame and underlying window management system. However, since there is no default controller, it is necessary to write a suitable controller also.

By keeping all window-related functionality separate from the frame, it is possible to use one single frame implementation for every possible window in the entire OpenOffice.org application. Thus, the presentation of all visible components is customized by exchanging the frame implementation. At runtime you can access a frame and replace the controller, together with the model it controls, by a different controller instance.

Frames
Linking Components and Windows

The main role of a frame in the Frame-Controller-Model paradigm is to act as a liaison between viewable components and the window system.

Frames can hold one component, or a component and one or more subframes. The following diagrams: and depict both possibilities. The first illustration shows a frame containing only a component. It is connected with two window instances: the container window and component window.


Illustration 6.3: Frame containing a component

When a frame is constructed, the frame must be initialized with a container window using com.sun.star.frame.XFrame:initialize(). This method expects the com.sun.star.awt.XWindow interface of a surrounding window instance, which becomes the container window of the frame. The window instance passed to initialize() must also support com.sun.star.awt.XTopWindow to become a container window. The container window must broadcast window events, such as windowActivated(), and appear in front of other windows or be sent to the background. The fact that container windows support com.sun.star.awt.XTopWindow does not mean the container window is an independent window of the underlying window system with a title bar and a system menu. An XTopWindow acts as a window if necessary, but it can also be docked or depend on a surrounding application window.

After initializing the frame, a component is set into the frame by a frame loader implementation that loads a component into the frame. It calls com.sun.star.frame.XFrame:setComponent() that takes another com.sun.star.awt.XWindow instance and the com.sun.star.frame.XController interface of a controller.Usually the controller is holding a model, therefore the component gets a component window of its own, separate from the container window.

A frame with a component is associated with two windows: the container window which is an XTopWindow and the component window, which is the rectangular area that displays the component and receives GUI events for the component while it is active. When a frame is initialized with an instance of a window in a call to initialize(), this window becomes its container window. When a component is set into a frame using setComponent(), another com.sun.star.awt.XWindow instance is passed becoming the component window.

When a frame is added to the desktop frame hierarchy, the desktop becomes the parent frame of our frame. For this purpose, the com.sun.star.frame.XFramesSupplier interface of the desktop is passed to the method setCreator() at the XFrame interface. This happens internally when the method append() is called at the com.sun.star.frame.XFrames interface supplied by the desktop.


A component window can have sub-windows, and that is the case with all documents in OpenOffice.org. For instance, a text document has sub-windows for the toolbars and the editable text. Form controls are sub-windows, as well, however, these sub-windows depend on the component window and do not appear in the Frame-Controller-Model paradigm, as discussed above.

The second diagram shows a frame with a component and a sub-frame with another component. Each frame has a container window and component window.


Illustration 6.4: Frame containing a component and a sub-frame

In the OpenOffice.org GUI, sub-frames appear as dependent windows. The sub-frame in could be a dockable window, such as the beamer showing the database browser or a floating frame in a document created with Insert – Frame.

Note that a frame with a component and sub-frame is associated with four windows. The frame and the sub-frame have a container window and a component window for the component.

When a sub-frame is added to a surrounding frame, the frame becomes the parent of the sub-frame by a call to setCreator() at the sub-frame. This happens internally when the method append() is called at the com.sun.star.frame.XFrames interface supplied by the surrounding frame.

The section 6.1.4 Office Development - OpenOffice.org Application Environment - Creating Frames Manually shows examples for the usage of the XFrame interface that creates frames in the desktop environment, constructs dockable and standalone windows, and inserts components into frames.

Communication through Dispatch Framework

Besides the main role of frames as expressed in the com.sun.star.frame.XFrame interface, frames play another role by providinga communication context for the component they contain, that is, every communication from a controller to the desktop environment, and the user interface and conversely is done through the frame. This aspect of a frame is published through the com.sun.star.frame.XDispatchProvider interface, that uses special command requests to trigger actions.

The section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework discusses the usage of the dispatch API.

Components in Frames

The desktop environment section discussed the three kinds of viewable components that can be inserted into a frame. If the component has a controller and a model like a document, or if it has only a controller, such as the bibliography and database browser, it implements the com.sun.star.frame.Controller service represented by the interface com.sun.star.frame.XController. In the call to com.sun.star.frame.XFrame:setComponent(), the controller is passed with the component window instance. If the component has no controller, it directly implements com.sun.star.lang.XComponent and com.sun.star.awt.XWindow. In this case, the component is passed as XWindow parameter, and the XController parameter must be an XController reference set to null.

If the viewable component is a trivial component (implementing XWindow only), the frame holds a reference to the component window, controls the lifetime of the component and propagates certain events from the container window to the component window. If the viewable component is an office component (having a controller), the frame adds to these basic functions a set of features for integration of the component into the environment by supporting additional command URLs for the component at its com.sun.star.frame.XDispatchProvider interface.

Controllers

Controllers in OpenOffice.org are between a frame and document model. This is their basic role as expressed in com.sun.star.frame.XController, which has methods getModel() and getFrame(). The method getFrame() provides the frame the controller is attached to. The method getModel() returns a document model, but it may return an empty reference if the component does not have a model.

Usually the controller objects support additional interfaces specific to the document type they control, such as com.sun.star.sheet.XSpreadsheetView for Calc document controllers or com.sun.star.text.XTextViewCursorSupplier for Writer document controllers.


Illustration 6.5: Controller with Model and Frame

There can be more than one controller instance with frames of their own controlling the same document model simultaneously. Multiple controllers and frames are created by OpenOffice.org when the user clicks Window – New Window.

Windows

Windows in the OpenOffice.org API are rectangular areas that are positioned and resized, and inform listeners about UI events (com.sun.star.awt.XWindow). They have a platform-specific counterpart that is wrapped in the com.sun.star.awt.XWindowPeer interface, which is invalidated (redrawn), and sets the system pointer and hands out the toolkit for the window. The usage of the window interfaces is outlined in the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Window Interfaces below.

Dispatch Framework

The dispatch framework is designed to provide a uniform access to components for a GUI by using command URLs that mirror menu items, such as Edit – Select All with various document components. Only the component knows how to execute a command. Similarly, different document components trigger changes in the UI by common commands. For example, a controller might create UI elements like a menu bar, or open a hyperlink.

Command dispatching follows a chain of responsibility. Calls to the dispatch API are moderated by the frame, so all dispatch API calls from the UI to the component and conversely are handled by the frame. The frame passes on the command until an object is found that can handle it. It is possible to restrict, extend or redirect commands at the frame through a different frame implementation or through other components connecting to the frame.

It has already been discussed that frames and controllers have an interface com.sun.star.frame.XDispatchProvider. The interface is used to query a dispatch object for a command URL from a frame and have the dispatch object execute the command. This interface is one element of the dispatch framework.

By offering the interception of dispatches through the interface com.sun.star.frame.XDispatchProviderInterception, the Frame service offers a method to modify a component's handling of GUI event s while keeping its whole API available simultaneously.


Normally, command URL dispatches go to a target frame which decides what to do with it. A component can use globally accessible objects like the desktop service to bypass restrictions set by a frame, but this is not recommended. It is impossible to prevent a implemention of components against the design principles, because the framework API is made for components that adhere to its design.

The usage of the Dispatch Framework is described in the section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

6.1.2    Using the Desktop


Illustration 6.6: Desktop Service and Component Framework

The com.sun.star.frame.Desktop service available at the global service manager includes the service com.sun.star.frame.Frame. The Desktop service specification provides three interfaces: com.sun.star.frame.XDesktop, com.sun.star.frame.XComponentLoader and com.sun.star.document.XEventBroadcaster, as shown in the following UML chart:


Illustration 6.7: UML description of the desktop service

The interface com.sun.star.frame.XDesktop provides access to frames and components, and controls the termination of the office process. It defines the following methods:

com::sun::star::frame::XFrame getCurrentFrame ()

com::sun::star::container::XEnumerationAccess getComponents ()

com::sun::star::lang::XComponent getCurrentComponent ()

boolean terminate ()

void addTerminateListener ( [in] com::sun::star::frame::XTerminateListener xListener)

void removeTerminateListener ( [in] com::sun::star::frame::XTerminateListener xListener)

The methods getCurrentFrame() and getCurrentComponent() distribute the active frame and document model, whereas getComponents() returns a com.sun.star.container.XEnumerationAccess to all loaded documents. For documents loaded in the desktop environment the methods getComponents() and getCurrentComponent() always return the com.sun.star.lang.XComponent interface of the document model.


If a specific document component is required, but are not sure whether this component is the current component, use getComponents() to get an enumeration of all document components, check each for the existence of the com.sun.star.frame.XModel interface and use getURL() at XModel to identify your document. Since not all components have to support XModel, test for XModel before calling getURL().

The office process is usually terminated when the user selects File - Exit or after the last application window has been closed. Clients can terminate the office through a call to terminate()and add a terminate listener to veto the shutdown process.

As long as the Windows quickstarter is active, the soffice executable is not terminated.

The following sample shows an com.sun.star.frame.XTerminateListener implementation that prevents the office from being terminated when the class TerminationTest is still active:

import com.sun.star.frame.TerminationVetoException;

import com.sun.star.frame.XTerminateListener;

public class TerminateListener implements XTerminateListener {

    public void notifyTermination (com.sun.star.lang.EventObject eventObject) {

        System.out.println("about to terminate...");

    }

    public void queryTermination (com.sun.star.lang.EventObject eventObject)

        throws TerminationVetoException {

       

        // test if we can terminate now

        if (TerminationTest.isAtWork() == true) {

            System.out.println("Terminate while we are at work? No way!");

            throw new TerminationVetoException() ; // this will veto the termination,

                                                  // a call to terminate() returns false

        }

    }

    public void disposing (com.sun.star.lang.EventObject eventObject) {

    }    

}

The following class TerminationTest tests the TerminateListener above.

import com.sun.star.bridge.XUnoUrlResolver;

import com.sun.star.uno.UnoRuntime;

import com.sun.star.uno.XComponentContext;

import com.sun.star.lang.XMultiComponentFactory;

import com.sun.star.beans.XPropertySet;

import com.sun.star.beans.PropertyValue;

import com.sun.star.frame.XDesktop;

import com.sun.star.frame.TerminationVetoException;

import com.sun.star.frame.XTerminateListener;

public class TerminationTest extends java.lang.Object {

   

    private static boolean atWork = false;

    public static void main(String[] args) {

        XComponentContext xRemoteContext = null;

        XMultiComponentFactory xRemoteServiceManager = null;

        XDesktop xDesktop = null;

       

        try {          

            // connect and retrieve a remote service manager and component context

            XComponentContext xLocalContext =

                com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);

            XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();

            Object urlResolver  = xLocalServiceManager.createInstanceWithContext(

                "com.sun.star.bridge.UnoUrlResolver", xLocalContext );

            XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface(

                XUnoUrlResolver.class, urlResolver );

            Object initialObject = xUnoUrlResolver.resolve(

                "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager" );

            XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(

                XPropertySet.class, initialObject);

            Object context = xPropertySet.getPropertyValue("DefaultContext");            

            xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(

                XComponentContext.class, context);

            xRemoteServiceManager = xRemoteContext.getServiceManager();

            // get Desktop instance

            Object desktop = xRemoteServiceManager.createInstanceWithContext (

                "com.sun.star.frame.Desktop ", xRemoteContext);

            xDesktop = (XDesktop)UnoRuntime.queryInterface(XDesktop.class, desktop);

           

            TerminateListener terminateListener = new TerminateListener ();

            xDesktop.addTerminateListener (terminateListener);

           

            // try to terminate while we are at work

            atWork = true;

            boolean terminated = xDesktop.terminate ();

            System.out.println("The Office " +

                (terminated == true ? "has been terminated" : "is still running, we are at work"));

     

            // no longer at work

            atWork = false;

            // once more: try to terminate

            terminated = xDesktop.terminate ();

            System.out.println("The Office " +

                (terminated == true ? "has been terminated" :

                    "is still running. Someone else prevents termination, e.g. the quickstarter"));

        }

        catch (java.lang.Exception e){

            e.printStackTrace();

        }

        finally {

            System.exit(0);

        }

       

       

    }

    public static boolean isAtWork() {

        return atWork;

    }

}

The office freezes when terminate() is called if there are unsaved changes. As a workaround set all documents into an unmodified state through their com.sun.star.util.XModifiable interface or store them using com.sun.star.frame.XStorable.

The Desktop offers a facility to load components through its interface com.sun.star.frame.XComponentLoader. It has one method:

com::sun::star::lang::XComponent loadComponentFromURL ( [in] string aURL,

                [in] string aTargetFrameName,

                [in] long nSearchFlags,

                [in] sequence < com::sun::star::beans::PropertyValue aArgs > )

Refer to chapter 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents for details about the loading process.

For versions beyond 641, the desktop also provides an interface that allows listeners to be notified about certain document events through its interface com.sun.star.document.XEventBroadcaster.

void addEventListener ( [in] com::sun::star::document::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::document::XEventListener xListener)

The XEventListener must implement a single method (besides disposing()):

[oneway] void notifyEvent ( [in] com::sun::star::document::EventObject Event )

The struct com.sun.star.document.EventObject has a string member EventName that assumes one of the values specified in com.sun.star.document.Events. The corresponding events are found on the Events tab of the Tools – Configure dialog when the option OpenOffice.org is selected.

The desktop broadcasts these events for all loaded documents.

The current version of OpenOffice.org does not have a GUI element as a desktop. The redesign of the OpenOffice.org GUI in StarOffice 5.x and later resulted in the com.sun.star.frame.Frame service part of the desktop service is now non-functional. While the XFrame interface can still be queried from the desktop, almost all of its methods are dummy implementations. The default implementation of the desktop object in OpenOffice.org is not able to contain a component and refuses to be attached to it, because the desktop is still a frame that is the root for the common hierarchy of all frames in OpenOffice.org. The desktop has to be a frame because its com.sun.star.frame.XFramesSupplier interface must be passed to com.sun.star.frame.XFrame:setCreator() at the child frames, therefore the desktop becomes the parent frame. However, the following functionality of com.sun.star.frame.Frame is still in place:

The desktop interface com.sun.star.frame.XFramesSupplier offers methods to access frames. This interface inherits from com.sun.star.frame.XFrame, and introduces the following methods:

com::sun::star::frame::XFrames getFrames ()

com::sun::star::frame::XFrame getActiveFrame ()

void setActiveFrame ( [in] com::sun::star::frame::XFrame xFrame)

The method getFrames() returns a com.sun.star.frame.XFrames container, that is a com.sun.star.container.XIndexAccess, with additional methods to add and remove frames:

void append ( [in] com::sun::star::frame::XFrame xFrame )

sequence < com::sun::star::frame::XFrame > queryFrames ( [in] long nSearchFlags )

void remove ( [in] com::sun::star::frame::XFrame xFrame )

This XFrames collection is used when frames are added to the desktop to become application windows.

Through getActiveFrame(), you access the active sub-frame of the desktop frame, whereas setActiveFrame() is called by a sub-frame to inform the desktop about the active sub-frame.

The object returned by getFrames() does not support XTypeProvider, therefore it cannot be used with OpenOffice.org Basic.

The parent interface of XFramesSupplier, com.sun.star.frame.XFrame is functional by accessing the frame hierarchy below the desktop. These methods are discussed in the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Frames below:

com::sun::star::frame::XFrame findFrame ( [in] string aTargetFrameName, [in] long nSearchFlags );

boolean isTop ();

The generic dispatch interface com.sun.star.frame.XDispatchProvider executes functions of the internal Desktop implementation that are not accessible through specialized interfaces. Dispatch functions are described by a command URL. The XDispatchProvider returns a dispatch object that dispatches a given command URL. A reference of command URLs supported by the desktop is available in the appendix of this manual. ((This document still missing in Appendix – we left this note during review, so that we do not forget to find a way to publish this reference)) Through the com.sun.star.frame.XDispatchProviderInterception, client code intercepts thecommand dispatches at the desktop. The dispatching process is described in section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

6.1.3    Using the Component Framework

The component framework comprises the interfaces of frames, controllers and models used to manage components in the OpenOffice.org desktop environment. In our context, everything that "dwells" in a frame of the desktop environment is called a component, because the interface com.sun.star.lang.XComponent is the common denominator for objects that are loaded into frames.

Frames, controllers and models hold references to each other. The frame is by definition the default owner of the controller and the model, that is,. it is responsible to call dispose() on the controller and model when it is destroyed itself. Other objects that are to hold references to the frame, controller, or model must register as listeners to be informed when these references become invalid. Therefore XModel,
XController and XFrame inherit from XComponent:

void dispose ()

void addEventListener ( [in] com::sun::star::lang::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::lang::XEventListener aListener)

The process to resolve the circular dependencies of the component framework is a complex. For instance, the objects involved in the process may be in a condition where they may not be disposed of. Refer to the section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Closing Documents for additional details.

Theoretically every UNO object could exist in a frame, as long as it is willing to let the frame control its existence when it ends.

A trivial component (XWindow only) is enough for simple viewing purposes, where no activation of a component and related actions like cursor positioning or user interactions are necessary.

If the component participates in more complex interactions, it must implement the controller service.

Many features of the desktop environment are only available if the URL of a component is known. For example:

In this case, com.sun.star.frame.XModel comes into operation, since it has methods to handle URLs, among others.

So a complete office component is made up of

Getting Frames, Controllers and Models from Each Other

Usually developers require the controller and frame of an already loaded document model. The com.sun.star.frame.XModel interface of OpenOffice.org document models gets the controller that provides access to the frame through its com.sun.star.frame.XController interface. The following illustration shows the methods that get the controller and frame for a document model and conversely. From the frame , obtain the corresponding component and container window.


Illustration 6.8: Frame-Controller-Model Organization

If the loaded component is a trivial component and implements com.sun.star.awt.XWindow only, the window and the window peer is reached by querying these interfaces from the com.sun.star.lang.XComponent returned by loadComponentFromURL().

Frames


Illustration 6.9: Frame Service

XFrame
Frame Setup

The main role of a frame is to link components into a surrounding window system. This role is expressed by the following methods of the frame's main interface com.sun.star.frame.XFrame:

// methods for container window

void initialize ( [in] com::sun::star::awt::XWindow xWindow);

com::sun::star::awt::XWindow getContainerWindow ();

// methods for component window and controller

boolean setComponent ( [in] com::sun::star::awt::XWindow xComponentWindow,

                      [in] com::sun::star::frame::XController xController );

com::sun::star::awt::XWindow getComponentWindow ();

com::sun::star::frame::XController getController ();

The first two methods deal with the container window of a frame, the latter three are about linking the component and the component window with the frame. The method initialize() expects a top window that is created by the AWT toolkit that becomes the container window of the frame and is retrieved by getContainerWindow().

Frame Hierarchies

When frames link components into a surrounding window system, they build a frame hierarchy. This aspect is covered by the hierarchy-related XFrame methods:

[oneway] void setCreator ( [in] com::sun::star::frame::XFramesSupplier xCreator );

com::sun::star::frame::XFramesSupplier getCreator ();

string getName ();

[oneway] void setName ( [in] string aName );

com::sun::star::frame::XFrame findFrame ( [in] string aTargetFrameName, [in] long nSearchFlags );

boolean isTop ();

The XFrame method setCreator() informs a frame about its parent frame and must be called by a frames container (com.sun.star.frame.XFrames) when a frame is added to it by a call to com.sun.star.frame.XFrames:append(). A frames container is provided by frames supporting the interface com.sun.star.frame.XFramesSupplier. XFramesSupplier is currently supported by the desktop frame and by the default frame implementation used by OpenOffice.org documents. It is described below.

The frame has a custom name that is read through getName() and written through setName(). Frames in the desktop hierarchy created by GUI interaction usually do not have names. The getName() returns an empty string for them, whereas frames that are created for special purposes, such as the beamer frame or the online help, have names. Developers can set a name and use it to address a frame in findFrame() calls or when loading a component into the frame. Custom frame names must not start with an underscore.Leading underscores are reserved for special frame names.See below.

Every frame in the frame hierarchy is accessed through any other frame in this hierarchy by calling the findFrame() method. This method searches for a frame with a given name in five steps: self, children, siblings, parent, and create if not found. The findFrame() checks the called frame, then calls findFrame() at its children, then its siblings and at its parent frame. The fifth step in the search strategy is reached if the search makes it to the desktop without finding a frame with the given name. In this case, a new frame is created and assigned the name that was searched for. If the top frame is outside the desktop hierarchy, a new frame is not created.

The name used with findFrame() can be an arbitrary string without a leading underscore or one of the following reserved frame names. These names are for internal use for loading documents.Some of the reserved names are logical in a findFrame() call, also. A complete list of reserved frame names can be found in section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - Target Frame.

_top

Returns the top frame of the called frame, first frame where isTop() returns true when traveling up the hierarchy.

_parent

Returns the next frame above in the frame hierarchy.

_self

Returns the frame itself, same as an empty target frame name. This means you are searching for a frame you already have, but it is legal to do so.

_blank

Creates a new top-level frame whose parent is the desktop frame.

Calls with "_top" or "_parent" return the frame itself if the called frame is a top frame or has no parent. This is compatible to the targetting strategies of web browsers.

We have seen that findFrame() is called recursively. To control the recursion, the search flags parameter specified in the constants group com.sun.star.frame.FrameSearchFlag is used. For all of the five steps mentioned above, a suitable flag exists (SELF, CHILDREN, SIBLINGS, PARENT, CREATE ). Every search step can be prohibited by deleting the appropriate FrameSearchFlag. The search flag parameter can also be used to avoid ambiguities caused by multiple occurrences of a frame name in a hierarchy by excluding parts of the frame tree from the search. If findFrame() is called for a reserved frame name, the search flags are ignored.


An additional flag can be used to extend a bottom-up search to all OpenOffice.org application windows, no matter where the search starts. Based on the five flags for the five steps, the default frame search stops searching when it reaches a top frame and does not continue with other OpenOffice.org windows. Setting the TASKS flag overrides this.

There are separate frame hierarchies that do not interact with each other. If a frame is created, but not inserted into any hierarchy, it becomes the top frame of its own hierarchy. This frame and its contents can not be accessed from other hierarchies by traversing the frame hierarchies through API calls. , Also, this frame and its content cannot reach frames and their contents in other hierarchies. It is the code that creates a frame and decides if the new frame becomes part of an existing hierarchy, thus enabling it to find other frames ,and making it and its viewable component visible to the other frames. Examples for frames that are not inserted into an existing hierarchy are preview frames in dialogs, such as the document preview in the File – New – Templates and Documents dialog.


This is the only way the current frame and desktop implementation handle this. If one exchanges either or both of them by another implementation, the treatment of the "_blank" target and the CREATE SearchFlag may differ.

Frame Actions

Several actions take place at a frame. The context of viewable components can change, a frame may be activated or the relationship between frame and component may be altered. For instance, when the current selection in a document has been changed, the controller informs the frame about it by calling contextChanged(). The frame then tells its frame action listeners that the context has changed. The frame action listeners are also informed about changes in the relationship between the frame and component, and about frame activation. The corresponding XFrame methods are:

void contextChanged ();

[oneway] void activate ();

[oneway] void deactivate ();

boolean isActive ();

[oneway] void addFrameActionListener ( [in] com::sun::star::frame::XFrameActionListener xListener );

[oneway] void removeFrameActionListener ( [in] com::sun::star::frame::XFrameActionListener xListener );

The method activate() makes the given frame the active frame in its parent container. If the parent is the desktop frame, this makes the associated component the current component. However, this is not reflected in the user interface by making the corresponding window the top window. If the container of the active frame is to be the top window, use setFocus() at the com.sun.star.awt.XWindow interface of the container window.

The interface com.sun.star.frame.XFrameActionListener used with addFrameActionListener() must implement the following method:

Method of com.sun.star.frame.XFrameActionListener

frameAction()

Takes a struct com.sun.star.frame.FrameActionEvent. The struct contains two members: the source com.sun.star.frame.XFrame Frame and an enum com.sun.star.frame.FrameActionEvent Action value with one of the following values:

COMPONENT_ATTACHED: a component has been attached to a frame. This is almost the same as the instantiation of the component within that frame. The component is attached to the frame immediately before this event is broadcast.

COMPONENT_DETACHING: a component is detaching from a frame. This is the same as the destruction of the component which was in that frame. The moment the event is broadcast the component is still attached to the frame, but in the next moment it will not be..

COMPONENT_REATTACHED: a component has been attached to a new model. In this case, the component remains the same, but operates on a new model component.

FRAME_ACTIVATED: a component has been activated. Activations are broadcast from the top component which was not active, down to the innermost component.

FRAME_DEACTIVATING: broadcast immediately before the component is deactivated. Deactivations are broadcast from the innermost component which does not stay active up to the outermost component which does not stay active.

CONTEXT_CHANGED: a component has changed its internal context, for example, the selection. If the activation status within a frame changes, this is a context change, also.

FRAME_UI_ACTIVATED: broadcast by an active frame when it is getting UI control (tool control).

FRAME_UI_DEACTIVATING: broadcast by an active frame when it is losing UI control (tool control).


At this time, the XFrame methods used to build a frame-controller-model relationship can only be fully utilized by frame loader implementations or customized trivial components. Outside a frame loader you can create a frame, but the current implementations cannot create a standalone controller that could be used with setComponent(). Therefore, you can not remove components from one frame and add them to another or create additional controllers for a loaded model using the component framework. This is due to restrictions of the VCL and the C++ implementation of the current document components.

Currently, the only way for clients to construct a frame and insert a OpenOffice.org document into it, is to use the com.sun.star.frame.XComponentLoader interface of the com.sun.star.frame.Desktop or the interfaces com.sun.star.frame.XSynchronousFrameLoader, the preferred frame loader interface, and the asynchronous com.sun.star.frame.XFrameLoader of the com.sun.star.frame.FrameLoader service that is available at the global service factory.

The recommended method to get additional controllers for loaded models is to use the OpenNewView property with loadComponentFromURL() at the com.sun.star.frame.XComponentLoader interface of the desktop.

There is also another possibility: dispatch a “.uno:NewWindow” command to a frame that contains that model.

XFramesSupplier

The Frame interface com.sun.star.frame.XFramesSupplier offers methods to access sub-frames of a frame. The frame implementation of OpenOffice.org supports this interface. This interface inherits from com.sun.star.frame.XFrame, and introduces the following methods:

com::sun::star::frame::XFrames getFrames ()

com::sun::star::frame::XFrame getActiveFrame ()

void setActiveFrame ( [in] com::sun::star::frame::XFrame xFrame)

The method getFrames() returns a com.sun.star.frame.XFrames container, that is a com.sun.star.container.XIndexAccess with additional methods to add and remove frames:

void append ( [in] com::sun::star::frame::XFrame xFrame )

sequence < com::sun::star::frame::XFrame > queryFrames ( [in] long nSearchFlags )

void remove ( [in] com::sun::star::frame::XFrame xFrame );

This XFrames collection is used when frames are appended to a frame to become sub-frames. The append() method implementation must extend the existing frame hierarchy by an internal call to setCreator() at the parent frame in the frame hierarchy. The parent frame is always the frame whose XFramesSupplier interface is used to append a new frame.

Through getActiveFrame() access the active sub-frame in a frame with subframes. If there are no sub-frames or a sub-frame is currently non active, the active frame is null. The setActiveFrame() is called by a sub-frame to inform the frame about the activation of the sub-frame. In setActiveFrame(), the method setActiveFrame() at the creator is called, then the registered frame action listeners are notified by an appropriate call to frameAction() with com.sun.star.frame.FrameActionEvent:Action set to FRAME_UI_ACTIVATED.

XDispatchProvider and XDispatchProviderInterception

Frame services also support com.sun.star.frame.XDispatchProvider and com.sun.star.frame.XDispatchProviderInterception. The section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework explains how these interfaces are used.

XStatusIndicatorFactory

The frame implementation supplies a status indicator through its interface com.sun.star.task.XStatusIndicatorFactory. A status indicator can be used by a frame loader to show the loading process for a document. The factory has only one method that returns an object supporting com.sun.star.task.XStatusIndicator:

com::sun::star::task::XStatusIndicator createStatusIndicator ()

The status indicator is displayed by a call to com.sun.star.task.XStatusIndicator:start(). Pass a text and a numeric range, and use setValue() to let the status bar grow until the maximum range is reached. The method end() removes the status indicator.

Controllers


Illustration 6.10: Controller Service

XController

A com.sun.star.frame.XController inherits from com.sun.star.lang.XComponent and introduces the following methods:

com::sun::star::frame::XFrame getFrame ()

void attachFrame (com::sun::star::frame::XFrame xFrame)

com::sun::star::frame::XModel getModel ()

boolean attachModel (com::sun::star::frame::XModel xModel)

boolean suspend (boolean bSuspend)

any getViewData ()

void restoreViewData (any Data)

The com.sun.star.frame.XController links model and frame through the methods get/attachModel() and get/attachFrame(). These methods and the corresponding methods in the com.sun.star.frame.XModel and com.sun.star.frame.XFrame interfaces act together. calling attachModel() at the controller must be accompanied by a corresponding call of connectController() at the model, and attachFrame() at the controller must have its counterpart setComponent() at the frame.

The controller is asked for permission to dispose of the entire associated component by using suspend(). The suspend() method shows dialogs, for example, to save changes. To avoid the dialog, close the corresponding frame without using suspend() before. The section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Closing Documents provides additional information.

Developers retrieve and restore data used to setup the view at the controller by calling get/restoreViewData(). These methods are usually called on loading and saving the document, but they also allow developers to manipulate the state of a view from the outside. The exact content of this data depends on the concrete controller/model pair.

XDispatchProvider

Through com.sun.star.frame.XDispatchProvider, the controller participates in the dispatch framework. It is described in section 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

XSelectionSupplier

The optional Controller interface com.sun.star.view.XSelectionSupplier accesses the selected object and informs listeners when the selection changes:

boolean select ( [in] any aSelection)

any getSelection ()

void addSelectionChangeListener ( [in] com::sun::star::view::XSelectionChangeListener xListener)

void removeSelectionChangeListener ( [in] com::sun::star::view::XSelectionChangeListener xListener)

The type of selection depends on the type of the document and the selected object. It is also possible to get the current selection in the active or last controller of a model by calling the method getCurrentSelection() in the com.sun.star.frame.XModel interface.

XContextMenuInterception

The optional Controller interface com.sun.star.ui.XContextMenuInterception intercepts requests for context menus in the document's window. See chapter 6.1.7 Office Development - OpenOffice.org Application Environment - Intercepting Context Menus.

Document Specific Controller Services

The com.sun.star.frame.Controller specification is generic and does not describe additional features required for a fully functional document controller specification, such as the controller specifications for Writer, Calc and Draw documents. The following table shows the controller services specified for OpenOffice.org document components.

Once the reference to a controller is retrieved, you can query for these interfaces. Use the com.sun.star.lang.XServiceInfo interface of the model to ask it for the supported service(s). The component implementations in OpenOffice.org support the following services. Refer to the related chapters for additional information about the interfaces you get from the controllers of OpenOffice.org documents.

Component and Chapter

Specialized Controller Service

General Description

Writer 7.5 Text Documents - Text Document Controller

com.sun.star.text.TextDocumentView

The text view supplies a text view cursor that has knowledge about the current page layout and page number. It can walk through document pages, screen pages and lines. The selected ruby text is also available, a special Asian text formatting, comparable to superscript.

Calc 8.5 Spreadsheet Documents - Controlling Spreadsheet Documents

com.sun.star.sheet.SpreadsheetView

The spreadsheet view is extremely powerful. It includes the services com.sun.star.sheet.SpreadsheetViewPane and com.sun.star.sheet.SpreadsheetViewSettings. The view pane handles the currently visible cell range and provides controllers for form controls in the spreadsheet. The view settings deal with the visibility of spreadsheet elements, such as the grid and current zoom mode. Furthermore, the spreadsheet view provides access to the active sheet in the view and the collection of all view panes, allowing to split and freeze the view, and control the interactive selection of a cell range.

Draw 9.7 Drawing - Drawing and Presentation Document Controller

com.sun.star.drawing.DrawingDocumentDrawView

The drawing document view toggles master page mode and layer mode, controls the current page and supplies the currently visible rectangle.

Impress 9.7 Drawing - Drawing and Presentation Document Controller

com.sun.star.drawing.DrawingDocumentDrawView

com.sun.star.presentation.PresentationView

The presentation view does not introduce presentation specific features. Running presentations are controlled by the com.sun.star.presentation.XPresentationSupplier interface of the presentation document model.

DataBaseAccess

com.sun.star.sdb.DataSourceBrowser

This controller has no published functionality that would be useful for developers.

Bibliography

(no special controller specified)

-

Writer (PagePreview)

(no special controller specified)

-

Writer/Webdocument (SourceView)

(no special controller specified)

-

Calc (PagePreview)

(no special controller specified)

-

Chart 10.4 Charts - Chart Document Controller

(no special controller specified)

-

Math

(no special controller specified)

-

Models

There is not an independent specification for a model service. The interface com.sun.star.frame.XModel is currently supported by Writer, Calc, Draw and Impress document components. In our context, we call objects supporting com.sun.star.frame.XModel, model objects. All OpenOffice.org document components have the service com.sun.star.document.OfficeDocument in common. An OfficeDocument implements the following interfaces:

XModel

The interface com.sun.star.frame.XModel inherits from com.sun.star.lang.XComponent and introduces the following methods, which handle the model's resource description, manage its controllers and retrieves the current selection.

string getURL ()

sequence < com::sun::star::beans::PropertyValue > getArgs ()

boolean attachResource ( [in] string aURL,

                        [in] sequence < com::sun::star::beans::PropertyValue aArgs > )

com::sun::star::frame::XController getCurrentController ()

void setCurrentController (com::sun::star::frame::XController xController)

void connectController (com::sun::star::frame::XController xController)

void disconnectController (com::sun::star::frame::XController xController)

void lockControllers ()

void unlockControllers ()

boolean hasControllersLocked ()

com::sun::star::uno::XInterface getCurrentSelection ()

The method getURL() provides the URL where a document was loaded from or last stored using storeAsURL(). As long as a new document has not been saved, the URL is an empty string. The method getArgs() returns a sequence of property values that report the resource description according to com.sun.star.document.MediaDescriptor, specified on loading or saving with storeAsURL. The method attachResource() is used by the frame loader implementations to inform the model about its URL and MediaDescriptor.

The current or last active controller for a model isretrieved through getCurrentController(). The corresponding method setCurrentController() sets a different current controller at models where additional controllers are available. However, additional controllers can not be created at this time for OpenOffice.org components using the component API. The method connectController() is used by frame loader implementations and provides the model with a new controller that has been created for it, without making it the current controller. The disconnectController() tells the model that a controller may no longer be used. Finally, the model holds back screen updates using lockControllers() and unlockControllers(). For each call to lockControllers(), there must be a call to unlockControllers() to remove the lock. The method hasControllersLocked() tells if the controllers are locked.

The currently selected object is retrieved by a call to getCurrentSelection(). This method is an alternative to getSelection() at the com.sun.star.view.XSelectionSupplier interface supported by controller services.

XModifiable

The interface com.sun.star.util.XModifiable traces the modified status of a document:

void addModifyListener ( [in] com::sun::star::util::XModifyListener aListener)

void removeModifyListener ( [in] com::sun::star::util::XModifyListener aListener)

boolean isModified ()

void setModified ( [in] boolean bModified)

XStorable

The interface com.sun.star.frame.XStorable stores a document under an arbitrary URL or its current location. Details about how to use this interface are discussed in the chapter 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents

XPrintable

The interface com.sun.star.view.XPrintable is used to set and get the printer and its settings, and dispatch print jobs. These methods and special printing features for the various document types are described in the chapters 7.2.3 Text Documents - Handling Text Document Files - Printing Text Documents, 8.2.3 Spreadsheet Documents - Handling Spreadsheet Document Files - Printing Spreadsheet Documents, 9.2.3 Drawing - Handling Drawing Document Files - Printing Drawing Documents and 9.4.2 Drawing - Handling Presentation Document Files - Printing Presentation Documents.

sequence< com::sun::star::beans::PropertyValue > getPrinter ()

void setPrinter ( [in] sequence< com::sun::star::beans::PropertyValue > aPrinter )

void print ( [in] sequence< com::sun::star::beans::PropertyValue > xOptions )

XEventBroadcaster

For versions later than 641, the optional interface com.sun.star.document.XEventBroadcaster at office documents enables developers to add listeners for events related to office documents in general, or for events specific for the individual document type.See 6.2.10 Office Development - Common Application Features - Document Events).

void addEventListener ( [in] com::sun::star::document::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::document::XEventListener xListener)

The XEventListener must implement a single method, besides disposing():

[oneway] void notifyEvent ( [in] com::sun::star::document::EventObject Event )

The struct com.sun.star.document.EventObject has a string member EventName, that assumes one of the values specified in com.sun.star.document.Events. These events are also on the Events tab of the Tools – Configure dialog.

The general events are the same events as those provided at the XEventBroadcaster interface of the desktop. While the model is only concerned about its own events, the desktop broadcasts the events for all the loaded documents.

XEventsSupplier

The optional interface com.sun.star.document.XEventsSupplier binds the execution of dispatch URLs to document events, thus providing a configurable event listener as a simplification for the more general event broadcaster or listener mechanism of the com.sun.star.document.XEventBroadcaster interface. This is done programmatically versus manually in Tools – Configure – Events.

XDocumentInfoSupplier

The optional interface com.sun.star.document.XDocumentInfoSupplier provides access to document information as described in section 6.2.7 Office Development - Common Application Features - Document Info.Document information is presented in the File – Properties dialog in the GUI.

XViewDataSupplier

The optional com.sun.star.document.XViewDataSupplier interface sets and restores view data.

com::sun::star::container::XIndexAccess getViewData ()

void setViewData ( [in] com::sun::star::container::XIndexAccess aData)

The view data are a com.sun.star.container.XIndexAccess to sequences of com.sun.star.beans.PropertyValue structs. Each sequence represents the settings of a view to the model that supplies the view data.

Document Specific Features

Every service specification for real model objects provides more interfaces that constitute the actual model functionality For example, a text document service com.sun.star.text.TextDocument provides text related interfaces. Having received a reference to a model, developers query for these interfaces. The com.sun.star.lang.XServiceInfo interface of a model can be used to ask for supported services. The OpenOffice.org document types support the following services:

Document

Service

Chapter

Calc

com.sun.star.sheet.SpreadsheetDocument

8 Spreadsheet Documents

Draw

com.sun.star.drawing.DrawingDocument

9 Drawing

Impress

com.sun.star.presentation.PresentationDocument

9 Drawing

Math

com.sun.star.formula.FormulaProperties

-

Writer (all Writer modules)

com.sun.star.text.TextDocument

7 Text Documents

Chart

com.sun.star.chart.ChartDocument

10 Charts

Refer to the related chapters for additional information about the interfaces of the documents of OpenOffice.org.

Window Interfaces

The window interfaces of the component window and container window control the OpenOffice.org application windows. This chapter provides a short overview.

XWindow

The interface com.sun.star.awt.XWindow is supported by the component and controller windows. This interface comprises methods to resize a window, control its visibility, enable and disable it, and make it the focus for input device events. Listeners are informed about window events.

[oneway] void setPosSize ( long X, long Y, long Width, long Height, short Flags );

com::sun::star::awt::Rectangle getPosSize ();

[oneway] void setVisible ( boolean Visible );

[oneway] void setEnable ( boolean Enable );

[oneway] void setFocus ();

[oneway] void addWindowListener ( com::sun::star::awt::XWindowListener xListener );

[oneway] void removeWindowListener ( com::sun::star::awt::XWindowListener xListener );

[oneway] void addFocusListener ( com::sun::star::awt::XFocusListener xListener );

[oneway] void removeFocusListener ( com::sun::star::awt::XFocusListener xListener );

[oneway] void addKeyListener ( com::sun::star::awt::XKeyListener xListener );

[oneway] void removeKeyListener ( com::sun::star::awt::XKeyListener xListener );

[oneway] void addMouseListener ( com::sun::star::awt::XMouseListener xListener );

[oneway] void removeMouseListener ( com::sun::star::awt::XMouseListener xListener );

[oneway] void addMouseMotionListener ( com::sun::star::awt::XMouseMotionListener xListener );

[oneway] void removeMouseMotionListener ( com::sun::star::awt::XMouseMotionListener xListener );

[oneway] void addPaintListener ( com::sun::star::awt::XPaintListener xListener );

[oneway] void removePaintListener ( com::sun::star::awt::XPaintListener xListener );

The com.sun.star.awt.XWindowListener gets the following notifications. The com.sun.star.awt.WindowEvent has members describing the size and position of the window.

[oneway] void windowResized ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowMoved ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowShown ( [in] com::sun::star::lang::EventObject e )

[oneway] void windowHidden ( [in] com::sun::star::lang::EventObject e );

What the other listeners do are evident by their names.

XTopWindow

The interface com.sun.star.awt.XTopWindow is available at container windows. It informs listeners about top window events, and it can put itself in front of other windows or withdraw into the background. It also has a method to control the current menu bar:

[oneway] void addTopWindowListener ( com::sun::star::awt::XTopWindowListener xListener );

[oneway] void removeTopWindowListener ( com::sun::star::awt::XTopWindowListener xListener );

[oneway] void toFront ();

[oneway] void toBack ();

[oneway] void setMenuBar ( com::sun::star::awt::XMenuBar xMenu );


Although the XTopWindow interface has a method setMenuBar(), this method is not usable at this time. The com.sun.star.awt.XMenuBar interface is deprecated.

The top window listener receives the following messages. All methods take a com.sun.star.awt.WindowEvent with members describing the size and position of the window.

[oneway] void windowOpened ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowClosing ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowClosed ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowMinimized ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowNormalized ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowActivated ( [in] com::sun::star::awt::WindowEvent e )

[oneway] void windowDeactivated ( [in] com::sun::star::awt::WindowEvent e )

XWindowPeer

Each XWindow has a com.sun.star.awt.XWindowPeer. The com.sun.star.awt.XWindowPeer interface accesses the window toolkit implementation used to create it and provides the pointer of the pointing device, and controls the background color. It is also used to invalidate a window or portions of it to trigger a redraw cycle.

com::sun::star::awt::XToolkit getToolkit ()

[oneway] void setPointer ( [in] com::sun::star::awt::XPointer Pointer )

[oneway] void setBackground ( [in] long Color )

[oneway] void invalidate ( [in] short Flags )

[oneway] void invalidateRect ( [in] com::sun::star::awt::Rectangle Rect,

                                [in] short Flags )

6.1.4    Creating Frames Manually

Frame Creation

]Every time a frame is needed in OpenOffice.org, the com.sun.star.frame.Frame service is created. OpenOffice.org has an implementation for this service, available at the global service manager.

This service can be replaced by a different implementation, for example, your own implementation in Java, by registering it at the service manager. In special cases, it is possible to use a custom frame implementation instead of the com.sun.star.frame.Frame service by instantiating a specific implementation using the implementation name with the factory methods of the service manager. Both methods can alter the default window and document handling in OpenOffice.org, thus changing or extending its functionality.

Assigning Windows to Frames

Every frame can be assigned to any OpenOffice.org window. For instance, the same frame implementation is used to load a component into an application window of the underlying windowing system or into a preview window of a OpenOffice.org dialog. The com.sun.star.frame.Frame service implementation does not depend on the type of the window, although the entirety of the frame and window will be a different object by the user.

If you have a window in your application and want to load a OpenOffice.org document, create a frame and window object, and put them together by a call to initialize(). A default frame is created by instantiating an object implementing the com.sun.star.frame.Frame service at the global service manager. For window creation, the current com.sun.star.awt implementation has to be used to create windows in all languages supporting UNO. This toolkit offers a method to create window objects that wrap a platform specific window, such as a Java AWT window or a Windows system window represented by its window handle. A Java example is given below.

Two conditions apply to windows that are to be used with OpenOffice.org frames.

The first condition is that the window must be created by the current com.sun.star.awt.Toolkit service implementation. Not every object implementing the com.sun.star.awt.XWindow interface is used as an argument in the initialize() method, because it is syntactically correct, but it is restricted to objects created by the current com.sun.star.awt implementation. The insertion of a component into a frame only works if all involved windows are .xbl created by the same toolkit implementation. All internal office components, such as Writer and Calc, are implemented using the Visual Component Library (VCL), so that they do not work if the container window is not implemented by VCL. The current toolkit uses this library internally, so all the windows created by the awt toolkit are passed to a frame.No others work at this time. Using VCL directly is not recommended. The code has to be rewritten, whenever this complication has incurred by the current office implementation and is removed, and the toolkit implementation is exchangeable.

The second condition is that if a frame and its component are supposed to get windowActivated() messages, the window object implements the additional interface com.sun.star.awt.XTopWindow. This is necessary for editing components, because the windowActivated event shows a cursor or a selection in the document. As long as this condition is met, further code is not necessary for the interaction between the frame and window, because the frame gets all the necessary events from the window by registering the appropriate listeners in the call to initialize().

When you use the com.sun.star.awt.Toolkit to create windows, supply a com.sun.star.awt.WindowDescriptor struct to describe what kind of window is required. Set the Type member of this struct to com.sun.star.awt.WindowClass:TOP and the WindowServiceName member to "window" if you want to have an application window, or to "dockingwindow" if a window is need to be inserted in other windows created by the toolkit.

Setting Components into Frame Hierarchies

Once a frame has been initialized with a window, it can be added to a frames supplier, such as the desktop using the frames container provided by com.sun.star.frame.XFramesSupplier:getFrames(). Its method com.sun.star.frame.XFrames:append() inserts the new frame into the XFrames container and calls setCreator() at the new frame, passing the XFramesSupplier interface of the parent frame.


The parent frame must be set as the creator of the newly created frame. The current implementation of the frames container calls setCreator() internally when frames are added to it using append().

The following example creates a new window and a frame, plugs them together, and adds them to the desktop, thus creating a new, empty OpenOffice.org application window. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

 // Conditions: xSMGR = m_xServiceManager
// Get access to vcl toolkit of remote office to create
// the container window of new target frame.
com.sun.star.awt.XToolkit xToolkit =
 (com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(
  com.sun.star.awt.XToolkit.class,
  xSMGR.createInstance("com.sun.star.awt.Toolkit") );

// Describe the properties of the container window.
// Tip: It is possible to use native window handle of a java window
// as parent for this. see chapter "OfficeBean" for further informations
com.sun.star.awt.WindowDescriptor aDescriptor =
 new com.sun.star.awt.WindowDescriptor();

 aDescriptor.Type              = com.sun.star.awt.WindowClass.TOP ;
aDescriptor.WindowServiceName = "window" ;
aDescriptor.ParentIndex       = -1;
aDescriptor.Parent            = null;
aDescriptor.Bounds            = new com.sun.star.awt.Rectangle(0,0,0,0);

 aDescriptor.WindowAttributes  =
 com.sun.star.awt.WindowAttribute.BORDER    |
 com.sun.star.awt.WindowAttribute.MOVEABLE  |
 com.sun.star.awt.WindowAttribute.SIZEABLE  |
 com.sun.star.awt.WindowAttribute.CLOSEABLE ;

com.sun.star.awt.XWindowPeer xPeer = xToolkit.createWindow(aDescriptor) ;

 com.sun.star.awt.XWindow xWindow = (com.sun.star.awt.XWindow)UnoRuntime.queryInterface (
  com.sun.star.awt.XWindow .class, xPeer);

// Create a new empty target frame.
// Attention: Before OpenOffice.org build 643 we must use
// com.sun.star.frame.Task instead of com.sun.star.frame.Frame,

// because the desktop environment accepts only this special frame type
// as direct children. It will be deprecated from build 643
xFrame = (com.sun.star.frame.XFrame)UnoRuntime.queryInterface(
   com.sun.star.frame.XFrame.class,

        xSMGR.createInstance ("com.sun.star.frame.Task "));

// Set the container window on it.
xFrame.initialize(xWindow) ;

// Insert the new frame in desktop hierarchy.
// Use XFrames interface to do so. It provides access to the
// child frame container of the parent node.
// Note: append(xFrame) calls xFrame.setCreator(Desktop) automaticly.
com.sun.star.frame.XFramesSupplier xTreeRoot =
(com.sun.star.frame.XFramesSupplier)UnoRuntime.queryInterface(
       com.sun.star.frame.XFramesSupplier.class,
           xSMGR.createInstance("com.sun.star.frame.Desktop") );

com.sun.star.frame.XFrames xChildContainer = xTreeRoot.getFrames ();

xChildContainer.append(xFrame) ;

// Make some other initializations.
xPeer.setBackground(0xFFFFFFFF);
xWindow.setVisible(true);
xFrame.setName("newly created 1") ;

6.1.5    Handling Documents

Loading Documents

The framework API defines a simple but powerful interface to load viewable components, the com.sun.star.frame.XComponentLoader. This interface is implemented by the globally accessible com.sun.star.frame.Desktop service,to query the XComponentLoader from the desktop.


Illustration 6.11: Services Involved in Document Loading

The interface com.sun.star.frame.XComponentLoader has one method:

com::sun::star::lang::XComponent loadComponentFromURL ( [in] string aURL,

                [in] string aTargetFrameName,

                [in] long nSearchFlags,

                [in] sequence < com::sun::star::beans::PropertyValue aArgs > )

The use fo this method is demonstrated below in the service com.sun.star.document.MediaDescriptor.

MediaDescriptor

A call to loadComponentFromURL() receives a sequence of com.sun.star.beans.PropertyValue structs as a parameter, which implements the com.sun.star.document.MediaDescriptor service, consisting of property definitions. It describes where a resource or medium should be loaded from and how this should be done.

The media descriptor is also used for saving a document to a location using the interface com.sun.star.frame.XStorable. It transports the "where to" and the "how" of the storing procedure. The table below shows the properties defined in the media descriptor.

Some properties are used for loading and saving while others apply to one or the other. If a media descriptor is used, only a few of the members are specified. The others assume default values. Strings default to empty strings in general and interface references default to empty references. For all other properties, the default values are specified in the description column of the table.

Some properties are tagged deprecated. There are old implementations that still use these properties. They are supported, but are discouraged to use them. Use the new property that can be found in the description column of the deprecated property.

To develop a UNO component that uses the media descriptor, note that all the properties are under control of the framework API. Never create your own property names for the media descriptor, or name clashes may be induced if the framework defines a property that uses the same name. Instead, use the ComponentData property to transport document specific information. ComponentData is specified to be an any, therefore it can be a sequence of property values by itself. If you do use it. make an appropriate specification available to users of your component.

Properties of com.sun.star.document.MediaDescriptor

AsTemplate

boolean. Setting AsTemplate to true creates a new untitled document out of the loaded document, even if it has no template extension.

Loading a template, that is, a document with a template extension, creates a new untitled document by default, but setting the AsTemplate property to false loads a template for editing.

Author

string. Only for storing versions in components supporting versioning: author of version.

CharacterSet

string. Defines the character set for document formats that contain single byte characters, if necessary. Which character set names are valid depends on the filter implementation, but with the current filters you can employ the character sets used for the conversion of byte to unicode strings.

Comment

string. Only for storing versions in components supporting versioning: comment (description) for stored version.

ComponentData

any. This is a parameter that is used for any properties specific for a special office component type.

FileName - deprecated

string. Same as URL (added for compatibility reasons)

FilterData

any. This is a parameter that is used for any properties specific for a special filter type.

FilterName

string. Name of a filter that should be used for loading or storing the component. Names must match the names of the typedetection configuration.Invalid names are ignored. If a name is specified on loading, it will be verified by a filter detection, but in case of doubt it will be preferred.

FilterFlags - deprecated

string. For compatibility reasons: same as FilterOptions

FilterOptions

string. Some filters need additional parameters. Use only together with property FilterName. Details must be documented by the filter. This is an old format for some filters. If a string is not enough, filters can use the property FilterData.

Hidden

boolean. Defines if the loaded component is made visible. If this property is not specified, the component is made visible by default. Making a hidden component visible by calling setVisible() at the container window is not recommended at this time.

InputStream

com.sun.star.io.XInputStream. Used when loading a document. Reading must be done using this stream. If no stream is provided, the loader creates a stream by itself using the URL, version number, readonly flag, password, or anything requiredfor stream creation, given in the media descriptor.

The model becomes the final owner of the stream and usually holds the reference to lock the file. Therefore, it is not allowed to keep a reference to this InputStream after loading the component.It is useless, because an InputStream is only usable once for reading. Even if it implements the com.sun.star.io.XSeekable interface, do not interfere with the model's reading process. Consider all the objects involved in the loading process as temporary.

InteractionHandler

com.sun.star.task.XInteractionHandler. Object implementing the com.sun.star.task.InteractionHandler service that handles exceptional situations where proceeding with the task is impossible without additional information or impossible at all.

OpenOffice.org provides a default implementation that can handle many situations. If no InteractionHandler is set, a suitable exception is thrown.

It is not allowed to keep a reference to this object, not even in the loaded or stored components' copy of the MediaDescriptor provided by its arguments attribute.

JumpMark

string. Jump to a marked position after loading. The office document loaders expect simple strings used like targets in HTML documents.Do not use a leading # character. The meaning of a jump mark depends upon the filter, but in Writer, bookmarks can be used, whereas in Calc cells, cell ranges and named areas are supported.

MediaType (string)

string. Type of the medium to load that must match to one of the types defined in the typedetection configuration, otherwise it is ignored. The typedetection configuration is found in the TypeDetection.xml file in the config/registry/instance/org/openoffice/Office folders of the user or share tree. The MediaType is found in the "type" entries. Here, it is the second member of the "data" value. This parameter bypasses the type detection of the desktop environment, so that passing a wrong MediaType causes load failures.

OpenFlags - deprecated

string. For compatibility reasons: string that summarizes some flags for loading. The string contains capital letters for the flags:

"ReadOnly" - "R"
"Preview" - "B"
"AsTemplate" - "T"
"Hidden" - "H"

Use the corresponding boolean parameters instead.

OpenNewView

boolean. Affects the behavior of the component loader when a resource is already loaded. If true, the loader tries to open a new view for a document already loaded. For components supporting multiple views, a second window is opened as if the user clicked Window – New Window.Other components are loaded one more time. Without this property, the default behavior of the loader applies, for example, the loader of the desktop activates a document if the user tries to load it a second time.

Overwrite

boolean. For storing only: overwrite existing files with the same name, default is true, so an com.sun.star.io.IOException occurs if the target file already exists. If the default is changed and the file exists, the UCB throws an exception. If the file is loaded through API, this exception is transported to the caller or handled by an interaction handler.

Password

string. A password for loading or storing a component, if necessary. If no password is specified, loading of a password protected document fails, storing is done without encryption.

PostData

reference <XinputStream>. HTTP post data to send to a location described by the media descriptor to get a result that is loaded as a component, usually in webforms. Default is: no PostData.

PostString - deprecated

string. Same as PostData, but the data is transferred as a string (just for compatibility).

Preview

boolean. Setting this to true tells the loaded component that it is loaded as a preview, so that it can optimize loading and viewing for this special purpose. Default is false.

ReadOnly

boolean. Tells if a document is to be loaded in a (logical) readonly or in read/write mode. If opening in the desired mode is impossible, an error occurs. By default, the loaded content decides what to do. If its UCB content supports a "readonly" property, the logical open mode depends on that property, otherwise it is read/write.

This property only affects the UI. Opening a document in read only mode does not prevent the component from being modified by API calls, but all modifying functionality in the UI is disabled or removed.

Referer

(the wrong spelling is kept for compatibility reasons)

string. A URL describing the environment of the request; for example,. a referrer may be the URL of a document, if a hyperlink inside this document is clicked to load another document. The referrer may be evaluated by the addressed UCB content or the loaded document.

Without a referrer, the processing of URLs that require security checks is denied, for instance macro: URLs.

StatusIndicator

com.sun.star.task.XStatusIndicator. Object implementing the com.sun.star.task.XStatusIndicator interface that gives status information, such as text or progress, for the target frame.

OpenOffice.org provides a default implementation that is retrieved by calling createStatusIndicator() at the frame you load a component into. Usually you do not need this parameter if you do not want to use any other indicator than the one in the status bar of the document window. It is not allowed to keep a reference to this object, not even in the loaded or stored component's copy of the MediaDescriptor provided by its getArgs() method.

TemplateName

string. The logical name of a template to load. Together with the TemplateRegionName property this is used instead of the URL of the template. The logical names are the template names you see in the templates dialog.

TemplateRegionName

string. See TemplateName. The template region names are the folder names you see in the templates dialog.

Unpacked

boolean. For storing: Setting this to true means that a zip file is not used to save the document. Use a folder instead for UCB contents that support folders, such as file, WebDAV, and ftp. Default is false.

URL

string. The location of the component in URL syntax.

Version

short. For components supporting versioning: the number of the version to be loaded or saved. Default is zero and means that no version is created or loaded, and the main document is processed.

ViewData

any. Data to set a special view state after loading. The type depends on the component and is retrieved from a controller object by its com.sun.star.document.XViewDataSupplier interface. Default is: no ViewData.

ViewId

short. For components supporting different views: a number to define the view that should be constructed after loading. Default is: zero, and this should be treated by the component as the default view.

MacroExecutionMode

short. How should the macro be executed - the value should be one from com.sun.star.document.MacroExecMode constants group

UpdateDocMode

short. Can the document be updated depending on links. The value should be one from com.sun.star.document.UpdateDocMode constant group

The media descriptor used for loading and storing components is passed as an in/out parameter to some objects that participate in the loading or storing process, that is, the com.sun.star.document.TypeDetection service or a com.sun.star.document.ExtendedTypeDetection service. These objects add additional information they have gathered to the media descriptor, so that other objects called later do not have to reinvestigate this.

The first object that gets the media descriptor might need an input stream, but assume that there is currently none. The object creates one and uses it. If the stream happens to be seekable (usually it is), the object puts the stream into the media descriptor, so that it passes it to other objects that need the stream as well. They do not have to create it again. It is important for streams created for a remote resource, such as http contents.

If the stream, provided from the outside or created by the first consumer, is not seekable, every consumer creates one. It creates a buffering stream component that reads in the original stream and provides a seekable stream for all further consumers. This buffered stream can be put into the media descriptor.

As previously mentioned, the easiest way to load a document is to call loadComponentFromURL() at the desktop service, but any other object could implement this interface.

URL Parameter

The URL is part of the media descriptor and also an explicit parameter for loadComponentFromURL(). This enables script code to load a document without creating a media descriptor at the cost of code redundancy. The URL parameter of loadComponentFromURL() overrides a possible URL property passed in the media descriptor. Aside from valid URLs that describe an existing file, the following URLs are used to open viewable components in OpenOffice.org:

Component

URL

Writer

private:factory/swriter

Calc

private:factory/scalc

Draw

private:factory/sdraw

Impress

private:factory/simpress

Database

.component:DB/QueryDesign
.component:DB/TableDesign
.component:DB/RelationDesign
.component:DB/DataSourceBrowser
.component:DB/FormGridView

Bibliography

.component:Bibliography/View1

Target Frame

The URL and media descriptor loadComponentFromURL() have two additional arguments, the target frame name and search flags. The method loadComponentFromURL() looks for a frame in the frame hierarchy and loads the component into the frame it finds. It uses the same algorithm as findFrame() at the com.sun.star.frame.XFrame interface, described in section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Frames - XFrame - Frame Hierarchies.

The target frame name is a reserved name starting with an underscore or arbitrary name. The reserved names denote frequently used frames in the frame hierarchy or special functions, whereas an arbitrary name is searched recursively. If a reserved name is used, the search flags are ignored and set to 0. The following reserved names are supported:

_self

Returns the frame itself. The same as with an empty target frame name. This means to search for a frame you already have, but it is legal.

_top

Returns the top frame of the called frame .,The first frame where isTop() returns true when traveling up the hierarchy. If the starting frame does not have a parent frame, the call is treated as a search for "_self". This behavior is compatible to the frame targeting in a web browser.

_parent

Returns the next frame above in the frame hierarchy. If the starting frame does not have a parent frame, the call is treated as a search for "_self". This behavior is compatible to the frame targeting in a web browser.

_blank

Creates a new top-level frame as a child frame of the desktop. If the called frame is not part of the desktop hierarchy, this call fails. Using the "_blank" target loads open documents again that result in a read-only document, depending on the UCB content provider for the component. If loading is done as a result of a user action, this becomes confusing to theusers, therefore the "_default" target is recommended in calls from a user interface, instead of "_blank". Refer to the next section for a discussion about the _default target..

_default

Similar to "_blank", but the implementation defines further behavior that has to be documented by the implementer. The com.sun.star.frame.XComponentLoader implemented at the desktop object shows the following default behavior.

First, it checks if the component to load is already loaded in another top-level frame. If this is the case, the frame is activated and brought to the foreground. When the OpenNewView property is set to true in the media descriptor, the loader creates a second controller to show another view for the loaded document. For components supporting this, a second window is opened as if the user clicked Window – New Window. The other components are loaded one more time, as if the "_blank" target had been used. Currently, almost all office components implementing com.sun.star.frame.XModel have multiple controllers, except for HTML and writer documents in the online view. The database and bibliography components have no model, therefore they cannot open a second view at all and OpenNewView leads to an exception with them.

Next, the loader checks if the active frame contains an unmodified, empty document of the same document type as the component that is being loaded. If so, the component is loaded into that frame, replacing the empty document, otherwise a new top-level frame is created similar to a call with "_blank".

Names starting with an underscore must not be used as real names for a frame.

If the given frame name is an arbitrary string, the loader searches for this frame in the frame hierarchy. The search is done in the following order: self, children, siblings, parent, create if not found. Each of these search steps can be skipped by deleting it from the com.sun.star.frame.FrameSearchFlag bit vector:

Constants in com.sun.star.frame.FrameSearchFlag group

SELF

search current frame

CHILDREN

search children recursively

SIBLINGS

search frames on the same level

PARENT

search frame above the current frame in the hierarchy

CREATE

create new frame if not found

TASKS

do not stop searching when a top frame is reached, but continue with other top frames

ALL

search the frame hierarchy below the current top frame, do not create new frame: SELF | CHILDREN | SIBLINGS | PARENT

GLOBAL

search all frames, do not create new frame: SELF | CHILDREN | SIBLINGS | PARENT | TASKS

A typical case for a named frame is a situation where a frame is needed to be reused for subsequent loading of components, for example, a frame attached to a preview window or a docked frame, such as the frame in OpenOffice.org that opens the address book when the F4 key is pressed.

The frame names "_self", "_top" and "_parent" define a frame target relative to a starting frame. They can only be used if the component loader interface finds the frame and the setComponent() can be used with the frame. The desktop frame is the root, therefore it does not have a top and parent frame. The component loader of the desktop cannot use these names, because the desktop refuses to have a component set into it.. However, if a frame implemented com.sun.star.frame.XComponentLoader, these names could be used.


SO6.1/OOo1.1 will have a frame implementation that supports XComponentLoader.

The reserved frame names are also used as a targeting mechanism in the dispatch framework with regard to as far as the relative frame names being resolved. For additional information, see chapter 6.1.6 Office Development - OpenOffice.org Application Environment - Using the Dispatch Framework.

The example below creates a frame, and uses the target frame and search flag parameters of loadComponentFromURL() to load a document into it. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

 // Conditions: sURL = "private:factory/swriter"
//             xSMGR = m_xServiceManager
//             xFrame = reference to a frame

 //             lProperties[] = new com.sun.star.beans.PropertyValue[0]


// First prepare frame for loading.
// We must adress it inside the frame tree without any complications.
// To do so we set an unambiguous name and use it later.
// Don't forget to reset the name to the original name after that.


String sOldName = xFrame.getName();
String sTarget  = "odk_officedev_desk";
xFrame.setName(sTarget);

// Get access to the global component loader of the office
// for synchronous loading of documents.
com.sun.star.frame.XComponentLoader xLoader =
 (com.sun.star.frame.XComponentLoader)UnoRuntime.queryInterface(
  com.sun.star.frame.XComponentLoader.class,
  xSMGR.createInstance("com.sun.star.frame.Desktop"));

// Load the document into the target frame by using our unambigous name
// and special search flags.
xDocument = xLoader.loadComponentFromURL(
 sURL, sTarget, com.sun.star.frame.FrameSearchFlag.CHILDREN, lProperties);

// dont forget to restore old frame name ...
xFrame.setName(sOldName);

The loadComponentFromURL() call returns a reference to a com.sun.star.lang.XComponent interface. The object belonging to this interface depends on the loaded component. If it is a component that only provides a component window, but not a controller, the returned component is this window. If it is an office component that provides a controller, the returned component is the controller or its model, if these is one. All Writer, Calc, Draw, Impress or Math documents in OpenOffice.org support a model, therefore the loadComponentFromURL() call returns it. The database and bibliography components however, return a controller, because they do not have a model.

Closing Documents

The loadComponentFromURL() returns a com.sun.star.lang.XComponent interface has previously been discussed. The return value is a reference to a com.sun.star.lang.XComponent interface, the corresponding object is a disposable component, and the caller must take care of lifetime problems. An XComponent supports the following methods:

void dispose ()

void addEventListener ( [in] com::sun::star::lang::XEventListener xListener)

void removeEventListener ( [in] com::sun::star::lang::XEventListener aListener)

In principle, there is a simple rule. The documentation of a com.sun.star.lang.XComponent specifies the objects that can own a component. Normally, a client using an XComponent is the owner of the XComponent and has the responsibility to dispose of it or it is not the owner. If it is not the owner, it may add itself as a com.sun.star.lang.XEventListener at the XComponent and not call dispose() on it. This type of XEventListener supports one method in which a component reacts upon the fact that another component is about to be disposed of:

void disposing ( [in] com::sun::star::lang::EventObject Source )

However, the frame, controller and model are interwoven tightly, and situations do occur in which there are several owners, for example, if there is more than one view for one model, or one of these components is in use and cannot be disposed of, for example, while a print job is running or a modal dialog is open. Therefore, developers must cope with these situations and remember a few things concerning the deletion of components.

Closing a document has two aspects. It is possible that someone else wants to close a document being currently worked on And you may want to close a component someone else is using at the same time. Both aspects are discussed in the following sections. A code example that closes a document is provided at the end of this section.

Reacting Upon Closing

The first aspect is that someone else wants to close a component for which you hold a reference. In the current version of OpenOffice.org, there are three possibilities.

Sometimes it is necessary to exercise more control over the closing process, therefore a new, optional interface com.sun.star.util.XCloseable has been introduced whichis supported in versions beyond 641. If the object you are referencing is a com.sun.star.util.XCloseable, register it as a com.sun.star.util.XCloseListener and throw a com.sun.star.util.CloseVetoException when prompted to close. Since XCloseable is specified as an optional interface for frames and models, do not assume that this interface is supported. It is possible that the code runs with a OpenOffice.org version where frames and models do not implement XCloseable. Therefore ,be prepared for the case when you receive null when you try to query XCloseable. The XCloseable interface is described in more detail below.

How to Trigger Closing

The second aspect – to close a view of a component or the entire viewable component yourself – is more complex. The necessary steps depend on how you want to treat modified documents. Besides you have to prepare for the new com.sun.star.util.XCloseable interface, which will be implemented in future versions of OpenOffice.org.


Although XCloseable is not supported in the current version of OpenOffice.org, you already have to check for this interface to write compatible code. Not checking for XCloseable will be illegal in future versions. If a component supports this interface, you must not use any closing procedure other than calling close() at that interface.

The following three diagrams show the decisions to be made when closing a frame or a document model. The important points are: if you expect modifications, you must either handle them using com.sun.star.util.XModifiable and com.sun.star.frame.XStorable, or let the user do the necessary interaction by calling suspend() on the controller. In any case, check if the frame or model is an XCloseable and prefer com.sun.star.util.XCloseable:close() over a call to dispose(). The first two diagrams illustrate the separate closing process for frames and models, the third diagram covers the actual termination of frames and models.


Illustration 6.12: Closing a Frame


Illustration 6.13: Closing a Model


Illustration 6.14: Terminate Frame/Model

XCloseable

The dispose mechanism has shortcomings in complex situations, such as the frame-controller-model interaction. The dispose call cannot be rejected, but as shown above, sometimes it is necessary to prevent destruction of objects due to shared ownership or a state of the documents that forbids destruction.

A closing mechanism is required that enables all involved objects to negotiate if deletion is possible and to veto, if necessary. By offering the interface com.sun.star.util.XCloseable, a component tells it must be destroyed by calling close(). Calling dispose() on an XCloseable might lead to deadlocks or crash the entire application.

In OpenOffice.org, model or frame objects are possible candidates for implementing the interface XCloseable, therefore query for that interface before destroying the object. Call dispose() directly if the model or frame does not support the interface, thus declaring that it handles all the problems.

An object implementing XCloseable registers close listeners. When a close request is received, all listeners are asked for permission. If a listener wants to deprecate, it throws an exception derived from com.sun.star.util.CloseVetoException containing the reason why the component can not be closed. This exception is passed to the close requester. The XCloseable itself can veto the destruction by throwing an exception. If there is no veto, the XCloseable calls dispose() on itself and returns.

The XCloseable handles problems that occur if a component rejects destruction. A script programmer usually can not cope with a component not used anymore and refuses to be destroyed. Ensure that the component is destroyed to avoid a memory leak. The close() method offers a method to pass the responsibility to close the object to any possible close listener that vetoes closing or to the XCloseable if the initial caller is not able to stay in memory to try again later. This responsibility is referred to as delivered ownership. The mechanism sets some constraints on the possible reasons for an objection against a close request.

A close listener that is asked for permission can object for any reason if the close call does not force it to assume ownership of the closeable object.The close requester is aware of a possible failure. If the close call forces the ownership, the close listener must be careful. An objection is only allowed if the reason is temporary. As soon as the reason no longer exists, the owner automatically calls close on the object that should be closed, now being in the same situation as the initial close requester.

A permanent reason for objection is not allowed. For example,. the document is modified is not a valid reason to object, because it is unlikely that the document becomes unmodified by itself. Consequently, it could never be closed. Therefore, if an API programmer wants to avoid data loss, he must use the com.sun.star.util.XModifiable and com.sun.star.frame.XStorable interfaces of the document. The fact that a model refuses to be closed if it is modified is not dependable.

The interface com.sun.star.util.XCloseable inherits from com.sun.star.util.XCloseBroadcaster and has the following methods:

[oneway] void addCloseListener ( [in] com::sun::star::util::XCloseListener Listener );

[oneway] void removeCloseListener ( [in] com::sun::star::util::XCloseListener Listener );

void close ( [in] boolean DeliverOwnership )

The com.sun.star.util.XCloseListener is notified twice when close() is called on an XClosable :

void queryClosing ( [in] com::sun::star::lang::EventObject Source,

                   [in] boolean GetsOwnership )

void notifyClosing ( [in] com::sun::star::lang::EventObject Source )

Both com.sun.star.util.XCloseable:close() and com.sun.star.util.XCloseListener:queryClosing() throw a com.sun.star.util.CloseVetoException.

In the closing negotiations, an XClosable is asked to close itself. In the call to close(), the caller passes a boolean parameter DeliverOwnership to tell the XClosable that it will give up ownership in favor of an XCloseListener, or the XCloseable that might have to finish a job first, but will close the XClosable immediately when the job is completed.

After a call to close(), the XClosable notifies its listeners twice. First, it checks if itcan be closed. If not, it throws a CloseVetoException, otherwise it uses queryClosing() to see if a listener has any objections against closing. The value of DeliverOwnership is conveyed in the GetsOwnership parameter of queryClosing(). If no listener disapproves of closing, the XClosable exercises notifyClosing() on the listeners and disposes itself. The result of a call to close() on a model is that all frames, controllers and the model itself are destroyed. The result of a call to close() on a frame is that this frame is closed, but the model stays alive if there are other controllers.

If an XCloseListener does not agree on closing, it throws a CloseVetoException, and the XClosable lets the exception pass in close(), so that the caller receives the exception. The CloseVetoException tells the caller that closing failed. If the caller delegated its ownership in the call to close() by setting the DeliverOwnership parameter to true, an XCloseListener knows that it automatically assumes ownership by throwing a CloseVetoException.The caller knows that someone else is now the owner if it receives a CloseVetoException. The new owner is compelled to close the XClosable as soon as possible. If the XCloseable was the object that threw an exception, it is compelled also to close itself as soon as possible.


No API exists for trivial components. As a consequence, components are not allowed to do anything that prevents them from being destroyed. For example, since the office crashes when a container window or component window has an open modal dialog, every component that wants to open a modal dialog must implement the com.sun.star.frame.XController interface.

If a model object supports XCloseable, calling dispose() on it is forbidden, try to close() the XCloseable and catch a possible CloseVetoException. Components that cannot cope with a destroyed model add a close listener at the model. This enables them to object when the model receives a close() request. They also add as a close listener if they are not already added as an (dispose) event listener. This can be done by every controller object that uses that model. Tt is also possible to let the model iterate through its controllers and call their suspend() methods explicitly as a part of its implementation of the close method. It is only necessary to know that a method close() must be called to close the model with its controllers. The method the model chooses is an implementation detail.

The example below closes a loaded document component. It does not save modified documents or prompts the user to save. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

// Conditions: xDocument = m_xLoadedDocument
// Check supported functionality of the document (model or controller).
com.sun.star.frame.XModel xModel =
 (com.sun.star.frame.XModel)UnoRuntime.queryInterface(
  com.sun.star.frame.XModel.class,xDocument);

if(xModel!=null)
{
 // It is a full featured office document.
 // Try to use close mechanism instead of a hard dispose().
 // But maybe such service is not available on this model.
 com.sun.star.util.XCloseable xCloseable =
   (com.sun.star.util.XCloseable)UnoRuntime.queryInterface(
    com.sun.star.util.XCloseable.class,xModel);


 if(xCloseable!=null)
 {
   try
   {
     // use close(boolean DeliverOwnership)

      // The boolean parameter DeliverOwnership tells objects vetoing the close process that they may

      // assume ownership if they object the closure by throwing a CloseVetoException

      // Here we give up ownership. To be on the safe side, catch possible veto exception anyway.
     xCloseable.close(true);
   }
   catch(com.sun.star.util.CloseVetoException exCloseVeto)
   {
   }
 }
 // If close is not supported by this model - try to dispose it.
 // But if the model disagree with a reset request for the modify state
 // we shouldn't do so. Otherwhise some strange things can happen.
 else
 {
     com.sun.star.lang.XComponent xDisposeable =
       (com.sun.star.lang.XComponent)UnoRuntime.queryInterface(
        com.sun.star.lang.XComponent.class,xModel);      
        xDisposeable.dispose();
      }
      catch(com.sun.star.beans.PropertyVetoException exModifyVeto)
      {
      }
    }
  }
}

Storing Documents

After loading an office component successfully, the returned interface cis used to manipulate the component. Document specific interfaces, such as the interfaces com.sun.star.text.XTextDocument, com.sun.star.sheet.XSpreadsheetDocument or com.sun.star.drawing.XDrawPagesSupplier are retrieved using queryInterface().

If the office component supports the com.sun.star.frame.XStorable interface applying to every component implementing the service com.sun.star.document.OfficeDocument, it can be stored:

void store ( )

void storeAsURL ( [in] string sURL,

                 [in] sequence< com::sun::star::beans::PropertyValue > lArguments )

void storeToURL ( [in] string sURL,

                 [in] sequence< com::sun::star::beans::PropertyValue > lArguments )

boolean hasLocation ()

string getLocation ()

boolean isReadonly ()

The XStorable offers the methods store(), storeAsURL() and storeToURL() for storing. The latter two methods are called with a media descriptor.

The method store() overwrites an existing file. Calling this method on a document that was created from scratch using a private:factory/... URL leads to an exception.

The other two methods storeAsURL() and storeToURL() leave the original file untouched and differ after the storing procedure. The storeToURL() method saves the current document to the desired location without touching the internal state of the document. The method storeAsURL sets the Modified attribute of the document, accessible through its com.sun.star.util.XModifiable interface, to false and updates the internal media descriptor of the document with the parameters passed in the call. This changes the document URL.

The following example exports a Writer document, Writer/Web document or Calc sheet to HTML. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

 // Conditions: sURL      = "file:///home/target.htm"
//             xDocument = m_xLoadedDocument

// Export can be achieved by saving the document and using
// a special filter which can write the desired format.
// Normally this filter should be searched inside the filter
// configuration (using service com.sun.star.document.FilterFactory)
// but here we use well known filter names directly.

 String sFilter = null;

 // Detect document type by asking XServiceInfo

 com.sun.star.lang.XServiceInfo xInfo = (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface (
  com.sun.star.lang.XServiceInfo .class, xDocument);

 // Determine suitable HTML filter name for export.

 if(xInfo!=null)
{
 if(xInfo.supportsService ("com.sun.star.text.TextDocument ") == true)
     sFilter = new String("HTML (StarWriter) ");
 else
 if(xInfo.supportsService ("com.sun.star.text.WebDocument ") == true)
     sFilter = new String("HTML ");
 else
 if(xInfo.supportsService ("com.sun.star.sheet.SpreadsheetDocument ") == true)
     sFilter = new String("HTML (StarCalc) ");
}

 if(sFilter!=null)
{
 // Build necessary argument list for store properties.
 // Use flag "Overwrite" to prevent exceptions, if file already exists.

  com.sun.star.beans.PropertyValue[] lProperties =
     new com.sun.star.beans.PropertyValue[2];
 lProperties[0]       = new com.sun.star.beans.PropertyValue();
 lProperties[0].Name  = "FilterName ";
 lProperties[0].Value = sFilter;
 lProperties[1]       = new com.sun.star.beans.PropertyValue();
 lProperties[1].Name  = "Overwrite ";
 lProperties[1].Value = new Boolean(true);

 com.sun.star.frame.XStorable xStore = (com.sun.star.frame.XStorable)UnoRuntime.queryInterface (

      com.sun.star.frame.XStorable .class, xDocument);

  xStore.storeAsURL (sURL, lProperties);
}

If a model is loaded or stored successfully, all parts of the media descriptor not explicitly excluded according to the media descriptor table in section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - MediaDescriptor must be provided by the methods getURL() and getArgs() in thecom.sun.star.frame.XModel interface. The separation of the URL and the other arguments is used, because the URL is the often the most wanted part for itsperformance optimized access.


The XModel offers a method attachResource() that changes the media descriptor of the document, but this method should only be used in special cases, for example, by the implementer of a new document model and controller. The method attachResource() does not force reloading of the document. Validation checks are done when a document is loaded through MediaDescriptor. For example, if the resource is write protected, add Readonly to the MediaDescriptor and the filter name must match the data. A possible use for attachResource() could be creating a document from a template, where after loading successfully, the document's resource is changed to an "unnamed" state by deleting the URL.

Printing Documents

Printing revolves around the interface com.sun.star.view.XPrintable. Its methods and special printing features for the various document types are described in the document chapters 7.2.3 Text Documents - Handling Text Document Files - Printing Text Documents, 8.2.3 Spreadsheet Documents - Handling Spreadsheet Document Files - Printing Spreadsheet Documents, 9.2.3 Drawing - Handling Drawing Document Files - Printing Drawing Documents and 9.4.2 Drawing - Handling Presentation Document Files - Printing Presentation Documents.

6.1.6    Using the Dispatch Framework

The component framework with the Frame-Controller-Model paradigm builds the skeleton of the global object structure. Other frameworks are defined that enrich the communication between an office component and the desktop environment. Usually they start at a frame object for the frame anchors an office component in the desktop environment.

One framework is the dispatch framework. Its main purpose defines interfaces for a generic communication between an office component and a user interface. This communication process handles requests for command executions and gives information about the various attributes of an office component. Generic means that the user interface does not have to know all the interfaces supported by the office component.The user interfaces sends messages to the office component and receives notifications.Tthe messages use a simple format. The entire negotiation about supported commands and parameters can happen at runtime while an application built on the specialized interfaces of the component are created at compile or interpret time. This generic approach is achieved by looking at an office component differently, not as objects with method-based interfaces, but as slot machines that take standardized command tokens.

We have discussed the differences between the different document types. The common functionality covers the generic features, that is, an office component is considered to be the entirety of its controller, its model and many document-specific interfaces. To implement a user interface for a component, it would be closely bound to the component and its specialized interfaces. If different components use different interfaces and methods for their implementations, similar functions cannot be visualized by the same user interface implementation. For instance, an action like Edit – Select All leads to different interface calls depending on the document type it is sent to. From a user interface perspective, it would be better to define abstract descriptions of the actions to be performed and let the components decide how to handle these actions, or not to handle . These abstract descriptions and how to handle them is specified in the dispatch framework.

Command URL

In the dispatch framework, every possible user action is defined as an executable command, and every possible visualization as a reflection of something that is exposed by the component is defined as an attribute. Every executable command and every attribute is a feature of the office component, and the dispatch framework gives every feature a name called command URL. It is represented by a com.sun.star.util.URL struct.

Command URLs are strings that follow the protocol_scheme:protocol_specific_part pattern. Public URL schemes, such as file: or http can be used here.Executing a request with a URL that points to a location of a document means that this document is loaded. In general, both parts of the command URL can be arbitrary strings, but a request cannot be executed if there is an object that does not know how to handle its command URL.

Processing Chain

A request is created by any object.User interface objects can create requests. Consider a toolbox where different functions acting on the office component are presented as buttons. When a button is clicked, the desired functionality is executed. If the code assigned to the button is provided with a suitable command URL, it handles the user action by creating the request and finding a component that can handle it. The button handler does not require any prior knowledge of the component and how it would go about its task.

This situation is handled by the design pattern chain of responsibility. Everything a component needs to know to execute a request is the last link of a chain of objects capable of executing requests. If this object gets the request, it checks if it can handle it or passes it to the next chain member until the request is executed, or the end of the chain is reached.

The chain members in the dispatch framework are objects implementing the interface com.sun.star.frame.XDispatchProvider. Every frame and controller supports it.In the simplest case, the chain consists of two members, a frame and its controller, but concatenating several chain parts on demand of a frame or a controller is possible. A controller once called, passes on the call, that is, it can use internal frames created by its implementation. A frame also passes the call to other objects, for example, its parent frame.

The current implementation of the chain is different from a simple chain.A frame is always the leading chain member and must be called initially, but in the default implementation used in OpenOffice.org, the frame first(!) asks its controller before it goes on with the request. Other frame implementations handle this in a different way. Other chain members are inserted into the call sequence before the controller uses the dispatch interception capability of a frame. The developers should not rely on any particular order inside the chain.

The dispatch framework uses a generic approach to describe and handle requests with a lose coupling between the participating objects. To work correctly, it is necessary to follow certain rules:

  1. Every chain starts at a frame, and this object decides if it passes on the call to its controller. The controller is not called directly from the outside. This is not compulsory for internal usage of the dispatch API inside an office component implementation. Ther two reasons for this rule are:

  1. A command URL isparsed into a com.sun.star.util.URL struct before passing it to a dispatch provider, because it is assumed that the call is passed on to several objects. Having a preparsed URL saves parsing the command string repeatedly. Parsing means that the members Complete, Main, Protocol and at least one more member of the com.sun.star.util.URL struct, depending on the given protocol scheme have to be set. Additional members are set if the concrete URL protocol supports them. For well known protocol schemes and protocol schemes specific to OpenOffice.org, the service com.sun.star.util.URLTransformer is used to fill the struct from a command URL string. For other protocols, the members are set explicitly, but it is also possible to write an extended version of the URLTransformer service to carry out URL parsing. An extended URLTransformer must support all protocols supported by the default URLTransformer implementation, for example, by instantiating the old implementation by its implementation name and forwarding all known URLs to it, except URLs with new protocols.

The dispatch framework connects an object that creates a request with another object that reacts on the request. In addition, it provides feedback to the requester. It can tell if the request is currently allowed or not. If the request acts on a specific attribute of an object, it c provides the current status of this attribute. Altogether, this is called status information, represented by a com.sun.star.frame.FeatureStateEvent struct. This information is reflected in a user interface by enabling or disabling controls to show their availability, or by displaying the status of objects. For example, a pressed button for the bold attribute of text, or a numeric value for the text height in a combo box.

The com.sun.star.frame.XDispatchProvider interface does not handle requests, but delegates every request to an individual dispatch object implementing com.sun.star.frame.XDispatch.


This is the concept, but the implementation is not forced and it may decide to return the same object for every request. It is not recommened to use the dispatch provider object as a dispatch object.

Dispatch Process

This section describes the necessary steps to handle dispatch providers and dispatch objects. The illustration below shows the services and interfaces of the the Dispatch framework.


Illustration 6.15: Dispatch Framework

Getting a Dispatch Object

First, create a command URL that represents the desired functionality ensuring that it is parsed as described above. Tables with possible command URLs for the default office components of OpenOffice.org are located in the appendix.

Request the com.sun.star.frame.XDispatchProvider interface of the frame that contains the office component for a dispatch object for the command URL by calling its queryDispatch() method.

com::sun::star::frame::XDispatch queryDispatch ( [in] com::sun::star::util::URL URL,

                              [in] string TargetFrameName,

                              [in] long SearchFlags )

sequence< com::sun::star::frame::XDispatch > queryDispatches (

                             [in] sequence< com::sun::star::frame::DispatchDescriptor > Requests )

The additional parameters (TargetFrameName, SearchFlags) of this call are only used for dispatching public URL schemes, because they specify a target frame and frame search mode to the loading process. Valid target names and search flags are described in the section 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - Target Frame. The targets "_self", "_parent" and "_top" are well defined, so that they can be used, because a queryDispatch() call starts at a frame object. Using frame names or search flags with command URLs does not have any meaning in the office components in OpenOffice.org.

You receive a dispatch object that supports at least com.sun.star.frame.XDispatch:

[oneway] void dispatch ( [in] com::sun::star::util::URL URL,

                         [in] sequence< com::sun::star::beans::PropertyValue > Arguments )

[oneway] void addStatusListener ( [in] com::sun::star::frame::XStatusListener Control,

                          [in] com::sun::star::util::URL URL )

[oneway] void removeStatusListener ( [in] com::sun::star::frame::XStatusListener Control,

                          [in] com::sun::star::util::URL URL )

 

Listening for Status Information

If a dispatch object is received, add a listener for status events by calling its addStatusListener() method. A com.sun.star.frame.XStatusListener implements:

[oneway] void statusChanged ( [in] com::sun::star::frame::FeatureStateEvent Event )

Keep a reference to the dispatch object until you call the removeStatusListener() method, because it is not sure that any other object will keep it alive. If a status listener is not registered, because you want to dispatch a command,and are not interested in status events, release all references to the dispatch object immediately after usage. If a dispatch object is not received, the desired functionality is not available. If you have a visual user interface element that represents that functionality, disable it.

If a status listener is registered and there is status information, a com.sun.star.frame.FeatureStateEvent is received immediately after registering the listener. Status information is still received later if the status changes and you are still listening. The IsEnabled member of the com.sun.star.frame.FeatureStateEvent tells you if the functionality is currently available, and the State member holds information about a status that could be represented by UI elements. Its type depends on the command URL. A boolean status information is visualized in a pressed or not pressed look of a toolbox button. Other types need complex elements, such as combo boxes or spinfields embedded in a toolbox that show the current font and font size. If the State member is empty, the action does not have an explicit status, such as the menu item File – Print. The current status can be ambiguous, because more than one object is selected and the objects are in a different status, for example. selected text that is partly formatted bold and partly regular.

A special event is a status event where the Requery flag is set. This is a request to release all references to the dispatch object and to ask the dispatch provider for a new object, because the old one has become invalid. This allows the office components to accommodate internal context changes. It is possible that a dispatch object is not received, because the desired functionality has become unavailable.

If you do not get any status information in your statusChanged() implementation, assume that the functionality is always available, but has no explicit status.

If you are no longer interested in status events, use the removeStatusListener() method and release all references to the dispatch object. You may get a disposing() callback from the dispatch object when it is going to be destroyed. It is not necessary to call removeStatusListener(). Ensure that you do not hold any references to the dispatch object anymore.

Listening for Context Changes

Sometimes internal changes, for example, travelling from a text paragraph to a text table, or selecting a different type of object, force an office component to invalidate all referenced dispatch objects and provides other dispatch objects, including dispatches for command URLs it could not handle before. The component then calls the contextChanged() method of its frame, and the frame broadcasts the corresponding com.sun.star.frame.FrameActionEvent. For this reason, register a frame action listener using addFrameActionListener() at frames you want dispatch objects. Refer to section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Frames - XFrame - Frame Actions for additional information. If the listener is called back with a CONTEXT_CHANGED event, release all dispatch objects and query new dispatch objects for every command URL you require. You can also try command URLs that did not get a dispatch object before.

If you are no longer interested in context changes of a frame, use the removeFrameActionListener() method of the frame to deregister and release all references to the frame. If you get a disposing() request from the frame in between, it is not necessary to call removeFrameActionListener(), but you must release all frame references you are currently holding.

Dispatching a Command

If the desired functionality is available, execute it by calling the dispatch() method of the dispatch object. This method is called with the same command URL you used to get it, and optionally with a sequence of arguments of type com.sun.star.beans.PropertyValue that depend on the command. It is not redundant that supplied the URL again, because it is allowed to use one dispatch object for many command URLs. The appendix shows the names and types for the parameters. However, the command URLs for simple user interface elements, such as menu entries or toolbox buttons send no parameters. Complex user interface elements use parameters, for example, a combo box in a toolbar that changes the font height. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

 // Conditions: sURL        = "private:factory/swriter"
//             lProperties = new com.sun.star.beans.PropertyValue[0]
//             xSMGR       = m_xServiceManager
//             xListener   = this

//             xFrame      = a given frame

// Query the frame for right interface which provides access to all
// available dispatch objects.
com.sun.star.frame.XDispatchProvider xProvider =
   (com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface (
       com.sun.star.frame.XDispatchProvider .class, xFrame );

// Create and parse a valid URL
// Note: because it is an in/out parameter we must use an array of URLs
com.sun.star.util.XURLTransformer xParser =
   (com.sun.star.util.XURLTransformer)UnoRuntime.queryInterface (
       com.sun.star.util.XURLTransformer .class,

            xSMGR.createInstance("com.sun.star.util.URLTransformer" ));

com.sun.star.util.URL[] aParseURL = new com.sun.star.util.URL[1];
aParseURL[0]          = new com.sun.star.util.URL();
aParseURL[0].Complete = sURL;

xParser.parseStrict (aParseURL);

// Ask for dispatch object for requested URL and use it.
// Force given frame as target "" which means the same like "_self".
xDispatcher = xProvider.queryDispatch(aParseURL[0],"",0);

if(xDispatcher!=null)
{
 xDispatcher.addStatusListener (xListener,aParseURL[0]);
 xDispatcher.dispatch (aParseURL[0],lProperties);
}

Dispatch Results

Every dispatch object implement optional interfaces. An important extension is the com.sun.star.frame.XNotifyingDispatch interface for dispatch results. The dispatch() call is a void method and should be treated as an asynchronous or oneway call, therefore a dispatch result can not be passed as a return value, rather, a callback interface is necessary. The interface that provides dispatch results by a callback is the com.sun.star.frame.XNotifyingDispatch interface:

[oneway] void dispatchWithNotification ( [in] com::sun::star::util::URL URL,

                           [in] sequence< com::sun::star::beans::PropertyValue > Arguments,

                           [in] com::sun::star::frame::XDispatchResultListener Listener )

Its method dispatchWithNotification() takes a com.sun.star.frame.XDispatchResultListener interface that is called after a dispatched URL has been executed.


Although the dispatch process is considered to be asynchronous, this is not necessarily so. Therefore, be prepared to get the dispatch result notification before the dispatch call returns.

The dispatch result is transferred as a com.sun.star.frame.DispatchResultEvent struct in the callback method dispatchFinished(). The State member of this struct tells if the dispatch was successful or not, while the Result member contains the value that would be returned if the call had been executed as a synchronous function call. The appendix shows the types of return values. If a public URL is dispatched, the dispatch result is a reference to the frame the component was loaded into. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

 // Conditions: sURL        = "private:factory/swriter"
//             lProperties = new com.sun.star.beans.PropertyValue[0]
//             xSMGR       = m_xServiceManager
//             xListener   = this

// Query the frame for right interface which provides access to all
// available dispatch objects.
com.sun.star.frame.XDispatchProvider xProvider =
   (com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface (
       com.sun.star.frame.XDispatchProvider .class, xFrame);

 // Create and parse a valid URL
// Note: because it is an in/out parameter we must use an array of URLs
com.sun.star.util.XURLTransformer xParser =
   (com.sun.star.util.XURLTransformer)UnoRuntime.queryInterface(
      com.sun.star.util.XURLTransformer .class,
          xSMGR.createInstance ("com.sun.star.util.URLTransformer "));

 // Ask for right dispatch object for requested URL and use it.
// Force given frame as target "" which means the same like "_self".
// Attention: The interface XNotifyingDispatch is an optional one!

 com.sun.star.frame.XDispatch   xDispatcher =
   xProvider.queryDispatch (aURL,"",0);

 com.sun.star.frame.XNotifyingDispatch xNotifyingDispatcher =
   (com.sun.star.frame.XNotifyingDispatch)UnoRuntime.queryInterface (
       com.sun.star.frame.XNotifyingDispatch.class , xDispatcher );

 if(xNotifyingDispatcher!=null)
   xNotifyingDispatcher.dispatchWithNotification (aURL, lProperties, xListener);

Dispatch Interception

The dispatch framework described in the last chapter establishes a communication between a user interfaces and an office component. Both can be OpenOffice.org default components or custom components. Sometimes it is not necessary to replace a UI element by a new implementation. It can be sufficient to influence its visualized state or to redirect user interactions to external code. This is the typical use for dispatch interception.

The dispatch communication works in two directions: status information is transferred from the office component to the UI elements and user requests travel from the UI element to the office component. Both go through the same switching center that is, an object implementing com.sun.star.frame.XDispatch. The UI element gets this object by calling queryDispatch() at the frame containing the office component, and usually receives an object that connects to code inside the frame, the office component or global services in OpenOffice.org. The frame offers an interface that is used to return third-party dispatch objects that provide the UI element with status updates. For example, it is possible to disable a UI element that would not be disabled otherwise. Another possibility is to write replacement code that is called by the UI element if the user performs a suitable action.

Dispatch objects are provided by objects implementing the com.sun.star.frame.XDispatchProvider interface, and that is the interface you are required to implement. There is an extra step where the dispatch provider must be attached to the frame to intercept the dispatching communication, therefore the dispatch provider becomes a part of the chain of responsibility described in the previous section. This is accomplished by implementing com.sun.star.frame.XDispatchProviderInterceptor.

This chain usually only consists of the frame and the controller of the office component it contains, but the frame offers the com.sun.star.frame.XDispatchProviderInterception interface where other providers are inserted. They are called before the frame tries to find a dispatch object for a command URL, so that it is possible to put the complete dispatch communication in a frame under external control. More than one interceptor can be registered, thus building a bigger chain.

Routing every dispatch through the whole chain becomes a performance problem, because could be more than a hundred possible clients asking for a dispatch object. For this reason there is also an API that limits the routing procedure to particular commands or command groups. This is described below.

Once the connection is established, the dispatch interceptor decides how requests for a dispatch object are dealt with. When asked for a dispatch object for a Command URL, it can:

The slave dispatch provider and master dispatch provider in the com.sun.star.frame.XDispatchProviderInterceptor interface are a bit obscure at first. They are two pointers to chain members in both directions, next and previous, where the first and last member in the chain have special meanings and responsibilities.

The command dispatching passes through a chain of dispatch providers, starting at the frame. If the frame is answered to include an interceptor in this chain, the frame inserts the interceptor in the chain and passes the following chain member to the new chain member, so that calls are passed along the chain if it does not want to handle them.

If any interceptor is deregistered, the frame puts the lose ends together by adjusting the master and slave pointer of the chain successor and predecessor of the element that is going to be removed from the chain. All of them are interceptors, so only the last slave is a dispatch provider.

The frame takes care of the whole chain in the register or deregister of calls in the dispatch provider interceptor, so that the implementer of an interceptor does not have to be concerned with the chain construction.

6.1.7    Intercepting Context Menus

A context menu is displayed when an object is right clicked. Typically, a context menu has context dependent functions to manipulate the selected object, such as cut, copy and paste. Developers can intercept context menus before they are displayed to cancel the execution of a context menu, add, delete, or modify the menu by replacing context menu entries or complete sub menus. It is possible to provide new customized context menus.

Context menu interception is implemented by the observer pattern. This pattern defines a one-to-many dependency between objects, so that when an object changes state, all its dependents are notified. The implementation supports more than one interceptor.
The root access point for intercepting context menus is a com.sun.star.frame.Controller object. The controller implements the interface com.sun.star.ui.XContextMenuInterception to support context menu interception.

Register and Remove an Interceptor

The com.sun.star.ui.XContextMenuInterception interface enables the developer to register and remove the interceptor code. When an interceptor is registered, it is notified whenever a context menu is about to be executed. Registering an interceptor adds it to the front of the interceptor chain, so that it is called first. The order of removals is arbitrary. It is not necessary to remove the interceptor that registered last.

Writing an Interceptor

Notification

A context menu interceptor implements the com.sun.star.ui.XContextMenuInterceptor interface. This interface has one function that is called by the responsible controller whenever a context menu is about to be executed.

ContextMenuInterceptorAction notifyContextMenuExecute ( [in] ContextMenuExecuteEvent aEvent)

The com.sun.star.ui.ContextMenuExecuteEvent is a struct that holds all the important information for an interceptor.

Members of com.sun.star.ui.ContextMenuExecuteEvent

ExecutePosition

com.sun.star.awt.Point. Contains the position the context menu will be executed.

SourceWindow

com.sun.star.awt.XWindow. Contains the window where the context menu has been requested.

ActionTriggerContainer

com.sun.star.container.XIndexContainer. The structure of the intercepted context menu. The member implements the com.sun.star.ui.ActionTriggerContainer service.

Selection

com.sun.star.view.XSelectionSupplier. Provides the current selection inside the source window.

Querying a Menu Structure

The ActionTriggerContainer member is an indexed container of context menu entries, where each menu entry is a property set. It implements the com.sun.star.ui.ActionTriggerContainer service. The interface com.sun.star.container.XIndexContainer directly accesses the intercepted context menu structure through methods to access, insert, remove and replace menu entries.

All elements in an ActionTriggerContainer member support the com.sun.star.beans.XPropertySet interface to get and set property values. There are two different types of menu entries with different sets of properties:

Type of Menu Entry

Service Name

Menu entry

"com.sun.star.ui.ActionTrigger"

Separator

"com.sun.star.ui.ActionTriggerSeparator"

It is essential to determine the type of each menu entry be querying it for the interface com.sun.star.lang.XServiceInfo and calling

boolean supportsService ( [in] string ServiceName )

The following example shows a small helper class to determine the correct menu entry type. (OfficeDev/MenuElement.java)

// A helper class to determine the menu element type

public class MenuElement

{

    static public boolean IsMenuEntry( com.sun.star.beans.XPropertySet xMenuElement ) {

        com.sun.star.lang.XServiceInfo xServiceInfo =

            (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface(

                com.sun.star.lang.XServiceInfo.class, xMenuElement );

        return xServiceInfo.supportsService( "com.sun.star.ui.ActionTrigger" );

    }

   

    static public boolean IsMenuSeparator( com.sun.star.beans.XPropertySet xMenuElement ) {

        com.sun.star.lang.XServiceInfo xServiceInfo =

            (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface(

                com.sun.star.lang.XServiceInfo.class, xMenuElement );

        return xServiceInfo.supportsService( "com.sun.star.ui.ActionTriggerSeparator" );

    }

}

Figure 6.1: Determine the menu element type

The com.sun.star.ui.ActionTrigger service supported by selectable menu entries has the following properties:

Properties of com.sun.star.ui.ActionTrigger

Text

string. Contains the text of the label of the menu entry.

CommandURL

string. Contains the command URL that defines which function will be executed if the menu entry is selected by the user.

HelpURL

string. This optional property contains a help URL that points to the help text.

Image

com.sun.star.awt.XBitmap. This property contains an image that is shown left of the menu label. The use is optional so that no image is used if this member is not initialized.

SubContainer

com.sun.star.container.XIndexContainer. This property contains an optional sub menu.

The com.sun.star.ui.ActionTriggerSeparator service defines only one optional property:

Property of com.sun.star.ui.ActionTriggerSeparator

SeparatorType

com.sun.star.ui.ActionTriggerSeparatorType. Specifies a certain type of a separator. Currently the following types are possible:

const int LINE = 0
const int SPACE = 1
const int LINEBREAK = 2

Changing a Menu

It is possible to accomplish certain tasks without implementing code in a context menu interceptor, such as preventing a context menu from being activated. Normally, a context menu is changed to provide additional functions to the user.

As previously discussed, the context menu structure is queried through the ActionTriggerContainer member that is part of the com.sun.star.ui.ContextMenuExecuteEvent structure. The com.sun.star.ui.ActionTriggerContainer service has an additional interface com.sun.star.lang.XMultiServiceFactory that creates com.sun.star.ui.ActionTriggerContainer, com.sun.star.ui.ActionTrigger and com.sun.star.ui.ActionTriggerSeparator objects. These objects are used to extend a context menu.

The com.sun.star.lang.XMultiServiceFactory implementation of the ActionTriggerContainer implementation supports the following strings:

String

Object

"com.sun.star.ui.ActionTrigger"

Creates a normal menu entry.

"com.sun.star.ui.ActionTriggerContainer"

Creates an empty sub menu1 .

"com.sun.star.ui.ActionTriggerSeparator"

Creates an unspecified separator2 .

1 A sub menu cannot exist by itself. It has to be inserted into a com.sun.star.ui.ActionTrigger!

2 The separator has no special type. It is the responsibility of the concrete implementation to render an unspecified separator.

Finishing Interception

Every interceptor that is called directs the controller how it continues after the call returns. The enumeration com.sun.star.ui.ContextMenuInterceptorAction defines the possible return values.

Values of com.sun.star.ui.ContextMenuInterceptorAction

IGNORED

Called object has ignored the call. The next registered com.sun.star.ui.XContextMenuInterceptor should be notified.

CANCELLED

The context menu must not be executed. No remaining interceptor will be called.

EXECUTE_MODIFIED

The context menu has been modified and should be executed without notifying the next registered com.sun.star.ui.XContextMenuInterceptor.

CONTINUE_MODIFIED

The context menu was modified by the called object. The next registered com.sun.star.ui.XContextMenuInterceptor should be notified.

The following example shows a context menu interceptor that adds a a sub menu to a menu that has been intercepted at a controller, where this com.sun.star.ui.XContextMenuInterceptor has been registered. This sub menu is inserted ino the context menu at the topmost position. It provides help functions to the user that are reachable through the menu Help. (OfficeDev/ContextMenuInterceptor.java)

import com.sun.star.ui.*;

import com.sun.star.lang.XMultiServiceFactory;

import com.sun.star.beans.XPropertySet;

import com.sun.star.container.XIndexContainer;

import com.sun.star.uno.UnoRuntime;

import com.sun.star.uno.Exception;

import com.sun.star.beans.UnknownPropertyException;

import com.sun.star.lang.IllegalArgumentException;

public class ContextMenuInterceptor implements XContextMenuInterceptor {

    public ContextMenuInterceptorAction notifyContextMenuExecute(

             com.sun.star.ui.ContextMenuExecuteEvent aEvent ) throws RuntimeException {

        try {

            // Retrieve context menu container and query for service factory to

            // create sub menus, menu entries and separators

            com.sun.star.container.XIndexContainer xContextMenu = aEvent.ActionTriggerContainer;

            com.sun.star.lang.XMultiServiceFactory xMenuElementFactory =

                (com.sun.star.lang.XMultiServiceFactory)UnoRuntime.queryInterface(

                    com.sun.star.lang.XMultiServiceFactory.class, xContextMenu );

            if ( xMenuElementFactory != null ) {

                // create root menu entry for sub menu and sub menu

                com.sun.star.beans.XPropertySet xRootMenuEntry =

                    (XPropertySet)UnoRuntime.queryInterface(

                        com.sun.star.beans.XPropertySet.class,

                        xMenuElementFactory.createInstance ( "com.sun.star.ui.ActionTrigger " ));

                // create a line separator for our new help sub menu

                com.sun.star.beans.XPropertySet xSeparator =

                    (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface(

                        com.sun.star.beans.XPropertySet.class,

                        xMenuElementFactory.createInstance( "com.sun.star.ui.ActionTriggerSeparator" ) );

               

                Short aSeparatorType = new Short( ActionTriggerSeparatorType.LINE );

               xSeparator.setPropertyValue( "SeparatorType", (Object)aSeparatorType );

               

                // query sub menu for index container to get access

                com.sun.star.container.XIndexContainer xSubMenuContainer =

                    (com.sun.star.container.XIndexContainer)UnoRuntime.queryInterface(

                        com.sun.star.container.XIndexContainer.class,

                            xMenuElementFactory.createInstance(

                                "com.sun.star.ui.ActionTriggerContainer" ));

                // intialize root menu entry "Help"

                xRootMenuEntry.setPropertyValue( "Text", new String( "Help" ));

                xRootMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5410" ));

                xRootMenuEntry.setPropertyValue( "HelpURL", new String( "5410" ));

                xRootMenuEntry.setPropertyValue( "SubContainer", (Object)xSubMenuContainer );

                // create menu entries for the new sub menu

                               

                // intialize help/content menu entry

                // entry "Content"

                XPropertySet xMenuEntry = (XPropertySet)UnoRuntime.queryInterface(

                                              XPropertySet.class, xMenuElementFactory.createInstance (

                                                  "com.sun.star.ui.ActionTrigger " ));

                xMenuEntry.setPropertyValue( "Text", new String( "Content" ));

                xMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5401" ));

                xMenuEntry.setPropertyValue( "HelpURL", new String( "5401" ));

               

                // insert menu entry to sub menu

                xSubMenuContainer.insertByIndex ( 0, (Object)xMenuEntry );

                // intialize help/help agent

                // entry "Help Agent"

                xMenuEntry = (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface(

                                 com.sun.star.beans.XPropertySet.class,

                                     xMenuElementFactory.createInstance (

                                         "com.sun.star.ui.ActionTrigger " ));

                xMenuEntry.setPropertyValue( "Text", new String( "Help Agent" ));

                xMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5962" ));

                xMenuEntry.setPropertyValue( "HelpURL", new String( "5962" ));

                // insert menu entry to sub menu

                xSubMenuContainer.insertByIndex( 1, (Object)xMenuEntry );

                       

                // intialize help/tips

                // entry "Tips"

                xMenuEntry = (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface(

                                 com.sun.star.beans.XPropertySet.class,

                                     xMenuElementFactory.createInstance(

                                         "com.sun.star.ui.ActionTrigger " ));

                xMenuEntry.setPropertyValue( "Text", new String( "Tips" ));

                xMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5404" ));

                xMenuEntry.setPropertyValue( "HelpURL", new String( "5404" ));

                // insert menu entry to sub menu

                xSubMenuContainer.insertByIndex ( 2, (Object)xMenuEntry );

                // add separator into the given context menu

                xContextMenu.insertByIndex ( 0, (Object)xSeparator );

                // add new sub menu into the given context menu

                xContextMenu.insertByIndex ( 0, (Object)xRootMenuEntry );

               

                // The controller should execute the modified context menu and stop notifying other

                // interceptors.

                return com.sun.star.ui.ContextMenuInterceptorAction.EXECUTE_MODIFIED ;

            }

        }

        catch ( com.sun.star.beans.UnknownPropertyException ex ) {

            // do something useful

            // we used a unknown property

        }

        catch ( com.sun.star.lang.IndexOutOfBoundsException ex ) {

            // do something useful

            // we used an invalid index for accessing a container

        }

        catch ( com.sun.star.uno.Exception ex ) {

            // something strange has happend!

        }

        catch ( java.lang.Throwable ex ) {

            // catch java exceptions – do something useful

        }

               

        return com.sun.star.ui.ContextMenuInterceptorAction.IGNORED;

    }

}

6.1.8    Java Window Integration

This section discusses experiences obtained during the development of Java-OpenOffice.org integration. Usually, developers use the OfficeBean for this purpose. The following provides background information about possible strategies to reach this goal.

There are multiple possibilities to integrate local windows with OpenOffice.org windows. This chapter shows the integration of OpenOffice.org windows into a Java bean environment. Some of this information maybe helpful with other local window integrations.

The Window Handle

An important precondition is the existence of a system window handle of the own Java window. For this, use a java.awt.Canvas and the following JNI methods:

For an example, see bean/com/sun/star/beans/LocalOfficeWindow.java

The two methods getNativeWindow() and getNativeWindowSystemType() are declared and exported, but implemented for windows in bean/native/win32/com_sun_star_beans_LocalOfficeWindow.c through JNI


It has to be a java.awt.Cavans. These JNI methods cannot be implemented at a Swing control, because it does not have its own system window. You can use a java.awt.Canvas in a Swing container environment.


The handle is not available before the window is visible, otherwise the JNI function does not work. One possibility is to cache the handle and set it in show() or setVisible().

Using the Window Handle

The window handle create sa [PRODUCTMAME] window. There are two ways to accomplish this:

A Hack

This option is mentioned because there are situations where this is the only feasible method. The knowledge of this option can help in other situations.

Add the UNO interface com.sun.star.awt.XWindowPeer so that it is usable for the [PRODUCTMAME] window toolkit. This interface can have an empty implementation. In com.sun.star.awt.XToolkit:createWindow(), another interface com.sun.star.awt.XSystemDependentWindowPeer is expected that queries the HWND. Thus, XWindowPeer is for transporting and com.sun.star.awt.XSystemDependentWindowPeer queries the HWND.

This method getsa com.sun.star.awt.XWindow as a child of your own Java window, that is used to initialize a com.sun.star.frame.XFrame. (OfficeDev/DesktopEnvironment/FunctionHelper.java)

com.sun.star.awt.XToolkit xToolkit =
        (com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(
        com.sun.star.awt.XToolkit.class,
        xSMGR.createInstance("com.sun.star.awt.Toolkit"));

// this is the canvas object with the JNI methods
aParentView = ...
// some JNI methods cannot work before this

aParentView.setVisible(true);

// now wrap the canvas (JavaWindowPeerFake) and add the necessary interfaces

com.sun.star.awt.XWindowPeer xParentPeer =
        (com.sun.star.awt.XWindowPeer)UnoRuntime.queryInterface(

             com.sun.star.awt.XWindowPeer.class,

             new JavaWindowPeerFake(aParentView));

com.sun.star.awt.WindowDescriptor aDescriptor = new com.sun.star.awt.WindowDescriptor();

aDescriptor.Type                =   com.sun.star.awt.WindowClass.TOP;

aDescriptor.WindowServiceName   =   "workwindow";

aDescriptor.ParentIndex         =   1;

aDescriptor.Parent              =   xParentPeer;

aDescriptor.Bounds              =   new com.sun.star.awt.Rectangle(0,0,0,0);

if (aParentView.getNativeWindowSystemType()==com.sun.star.lang.SystemDependent.SYSTEM_WIN32)
        aDescriptor.WindowAttributes = com.sun.star.awt.WindowAttribute.SHOW;

else

        aDescriptor.WindowAttributes = com.sun.star.awt.WindowAttribute.SYSTEMDEPENDENT;

// now the toolkit can create an com.sun.star.awt.XWindow
com.sun.star.awt.XWindowPeer xPeer = xToolkit.createWindow( aDescriptor );

com.sun.star.awt.XWindow xWindow =
        (com.sun.star.awt.XWindow)UnoRuntime.queryInterface(

        com.sun.star.awt.XWindow.class,

        xPeer);

Legal Solution

The com.sun.star.awt.Toolkit service has a method com.sun.star.awt.XSystemChildFactory with a method createSystemChild(). This accepts an any with a wrapped HWND or X Window ID, as long and the system type, such as Windows, Java, and UNIX directly. Here you create an com.sun.star.awt.XWindow. This method cannot be used in OpenOffice.org build versions before src642, because the process ID parameter is unknown to the Java environment. Newer versions do not check this parameter, thus this new, method works.


As a user of com.sun.star.awt.XSystemChildFactory:createSystemChild() ensure that your client (Java application) and your server ([PRODUCTMAME]) use the same display. Otherwise the window handle is not interchangeable.

(OfficeDev/DesktopEnvironment/FunctionHelper.java)

com.sun.star.awt.XToolkit xToolkit =
        (com.sun.star.awt.XToolkit)UnoRuntime.queryInterface(

             com.sun.star.awt.XToolkit.class,

             xSMGR.createInstance("com.sun.star.awt.Toolkit"));

// this is the canvas with the JNI functions
aParentView = ...
// some JNI funtions will not work withouth this

aParentView.setVisible(true);

// no wrapping necessary, simply use the HWND

com.sun.star.awt.XSystemChildFactory xFac =
        (com.sun.star.awt.XSystemChildFactory)UnoRuntime.queryInterface(

             com.sun.star.awt.XSystemChildFactory.class,

             xToolkit);

Integer nHandle = aParentView.getHWND();

byte[] lIgnoredProcessID = new byte[0];

com.sun.star.awt.XWindowPeer xPeer =
        xFac.createSystemChild(
                (Object)nHandle,
                lIgnoredProcessID,
                com.sun.star.lang.SystemDependent.SYSTEM_WIN32);

com.sun.star.awt.XWindow xWindow =
        (com.sun.star.awt.XWindow)UnoRuntime.queryInterface(

             com.sun.star.awt.XWindow.class,

             xPeer);


The old method still works and can be used, but it should be considered deprecated. If in doubt, implement both and try the new method at runtime. If it does not work, try the hack.

Resizing

Another difficulty is resizing the window. Normally, the child window expects resize events of the parent. The child does not resize it window, because it must know the layout of the parent window. The VCL, [PRODUCTMAME]'s windowing engine creates a special system child window, thus we can resize windows.

The parent window can be filled "full size" with the child window, but only for UNIX and not for Windows. The VCL's implementation is system dependent.

The bean deals with this issue by adding another function to the local library. Windows adds arbitrary properties to an HWND. You can also subclass the window, that is, each Windows window has a function pointer or callback to the function that performs the event handling (WindowProcedure). Using this, it is possible to treat events by calling your own methods. This is useful whenever the window is not created by you and you need to influence the behavior of the window.

In this case, the Java window has not been created by us, but we need to learn about resize events to forward these to the [PRODUCTMAME] window. Look at the file bean/native/win32/com_sun_star_beans_LocalOfficeWindow.c, and find the method OpenOfficeWndProc(). In the first call of the JNI function Java_com_sun_star_beans_LocalOfficeWindow_getNativeWindow() of this file, the own handler is applied to the foreign window.


The old bean implementation had a bug that is fixed in newer versions. If you did not check if the function pointer was set, and called Java_com_sun_star_beans_LocalOfficeWindow_getNativeWindow() multiple times, you created a chain of functions that called each other with the result of an endless recursion leading to a stack overflow. If the own handler is already registered, it is now marked in one of the previously mentioned properties registered with an HWND:

In the future, VCL will do this sub-classing by itself, even on Windows. This will lead to equal behavior between Windows and UNIX.

The initial size of the window is a related problem. If a canvas is connected with a [PRODUCTMAME] window, set both sizes to a valid, positive value, otherwise the [PRODUCTMAME] window will not be visible. If you are using a non-product build of OpenOffice.org, you see an assertion failed "small world isn't it". This might change when the sub-classing is done by VCL in the future.

There is still one unresolved problem. The code mentioned above works with Java 1.3, but not for Java 1.4. There, the behavior of windows is changed. Where Java 1.3 sends real resize events from the own WindowProc, Java 1.4 does a re-parenting. The canvas window is destroyed and created again. This leads to an empty window with no OpenOffice.org window. This problem is under investigation.

More Remote Problems

There are additional difficulties to window handles and local window handles. Some personal experiences of one of the OpenOffice.org authors are provided:

6.2    Common Application Features

6.2.1    Clipboard

This chapter introduces the usage of the clipboard service com.sun.star.datatransfer.clipboard.SystemClipboard. The clipboard serves as a data exchange mechanism between OpenOffice.org custom components, or between custom components and external applications. It is usually used for copy and paste operations.


Note: The architecture of the OpenOffice.org clipboard service is strongly conforming to the Java clipboard specification.

Different platforms use different methods for describing data formats available on the clipboard. Under Windows, clipboard formats are identified by unique numbers, for example, under X11, a clipboard format is identified by an ATOM. To have a platform independent mechanism, the OpenOffice.org clipboard supports the concept of DataFlavors. Each instance of a DataFlavor represents the opaque concept of a data format as it would appear on a clipboard. A DataFlavor defined in com.sun.star.datatransfer.DataFlavor has three members:

Members of com.sun.star.datatransfer.DataFlavor

MimeType

A string that describes the data. This string must conform to Rfc2045 and Rfc2046 with one exception. The quoted parameter may contain spaces. In section 6.2.1 Office Development - Common Application Features - Clipboard - OpenOffice.org Clipboard Data Formats, a list of common DataFlavors supported by OpenOffice.org is provided.

HumanPresentableName

The human presentable name for the data format that this DataFlavor represents.

DataType

The type of the data. In section 6.2.1 Office Development - Common Application Features - Clipboard - OpenOffice.org Clipboard Data Formats there is a list of common DataFlavors supported by OpenOffice.org and their corresponding DataType.

The carrier of the clipboard data is a transferable object that implements the interface com.sun.star.datatransfer.XTransferable. A transferable object offers one or many different DataFlavors.

Using the Clipboard

Pasting Data

The following Java example demonstrates the use of the clipboard service to paste from the clipboard. (OfficeDev/clipboard/Clipboard.java)

import com.sun.star.datatransfer.*;
import com.sun.star.datatransfer.clipboard.*;
import com.sun.star.uno.AnyConverter;

...

// instantiate the clipboard service

Object oClipboard =

        xMultiComponentFactory.createInstanceWithContext(

        "com.sun.star.datatransfer.clipboard.SystemClipboard",

        xComponentContext);

                       

// query for the interface XClipboard

XClipboard xClipboard = (XClipboard)

        UnoRuntime.queryInterface(XClipboard.class, oClipboard);

//---------------------------------------------------

// get a list of formats currently on the clipboard

//---------------------------------------------------

XTransferable xTransferable = xClipboard.getContents();

DataFlavor[] aDflvArr = xTransferable.getTransferDataFlavors();

// print all available formats

System.out.println("Reading the clipboard...");

System.out.println("Available clipboard formats:");

                                                                       

DataFlavor aUniFlv = null;

for (int i=0;i<aDflvArr.length;i++)

{

        System.out.println( "MimeType: " +

                aDflvArr[i].MimeType +

                " HumanPresentableName: " +

                aDflvArr[i].HumanPresentableName );                               

        // if there is the format unicode text on the clipboard save the

        // corresponding DataFlavor so that we can later output the string

        if (aDflvArr[i].MimeType.equals("text/plain;charset=utf-16"))

        {                                       

                aUniFlv = aDflvArr[i];

        }

}

                       

System.out.println("");

try       

{

        if (aUniFlv != null)

        {

                System.out.println("Unicode text on the clipboard...");

                Object aData = xTransferable.getTransferData(aUniFlv);                                                System.out.println(AnyConverter.toString(aData));

        }

}

catch(UnsupportedFlavorException ex)

{

        System.err.println( "Requested format is not available" );

}

...

Copying Data

To copy to the clipboard, implement a transferable object that supports the interface com.sun.star.datatransfer.XTransferable. The transferable object offers arbitrary formats described by DataFlavors.

The following Java example demonstrates the implementation of a transferable object. This transferable object contains only one format, unicode text. (OfficeDev/clipboard/TextTransferable.java)

//---------------------------------------
// A simple transferable containing only
// one format, unicode text
//---------------------------------------

public class TextTransferable implements XTransferable
{
        public TextTransferable(String aText)
        {
                text = aText;
        }

        // XTransferable methods

        public Object getTransferData(DataFlavor aFlavor) throws UnsupportedFlavorException
        {
                if ( !aFlavor.MimeType.equalsIgnoreCase( UNICODE_CONTENT_TYPE ) )
                        throw new UnsupportedFlavorException();

                return text;
        }

        public DataFlavor[] getTransferDataFlavors()
        {
                DataFlavor[] adf = new DataFlavor[1];
                DataFlavor uniflv = new DataFlavor(
                        UNICODE_CONTENT_TYPE,
                        "Unicode Text",
                        new Type(String.class) );

                adf[0] = uniflv;

                return adf;
        }

        public boolean isDataFlavorSupported(DataFlavor aFlavor)
        {
                return aFlavor.MimeType.equalsIgnoreCase(UNICODE_CONTENT_TYPE);
        }

        // members
        private final String text;
        private final String UNICODE_CONTENT_TYPE = "text/plain;charset=utf-16";
}

Everyone providing data to the clipboard becomes a clipboard owner. A clipboard owner is an object that implements the interface com.sun.star.datatransfer.clipboard.XClipboardOwner. If the current clipboard owner loses ownership of the clipboard, it receives a notification from the clipboard service. The clipboard owner can use this notification to destroy the transferable object that was formerly on the clipboard. If the transferable object is a self-destroying object, destroying clears all references to the object. If the clipboard service is the last client, clearing the reference to the transferable object leads to destruction.

All data types except for text have to be transferred as byte array. The next example shows this for a bitmap.

public class BmpTransferable implements XTransferable

{

    public BmpTransferable(byte[] aBitmap)

    {

        mBitmapData = aBitmap;

    }

    // XTransferable methods

    public Object getTransferData(DataFlavor aFlavor) throws UnsupportedFlavorException

    {

        if ( !aFlavor.MimeType.equalsIgnoreCase(BITMAP_CONTENT_TYPE ) )

            throw new UnsupportedFlavorException();

        return mBitmapData;

    }

    public DataFlavor[] getTransferDataFlavors()

    {

        DataFlavor[] adf = new DataFlavor[1];

        DataFlavor bmpflv= new DataFlavor(

            BITMAP_CONTENT_TYPE,

            "Bitmap",

            new Type(byte[].class) );

        adf[0] = bmpflv;

        return adf;

    }

    public boolean isDataFlavorSupported(DataFlavor aFlavor)

    {

        return aFlavor.MimeType.equalsIgnoreCase(BITMAP_CONTENT_TYPE);

    }

    // members

    private byte[] mBitmapData;

    private final String BITMAP_CONTENT_TYPE = "application/x-openoffice;windows_formatname="Bitmap"";

}

The following Java example shows an implementation of the interface com.sun.star.datatransfer.clipboard.XClipboardOwner. (OfficeDev/clipboard/ClipboardOwner.java)

...

//----------------------------------------

// A simple clipboard owner implementation

//----------------------------------------

public class ClipboardOwner implements XClipboardOwner

{

        public void lostOwnership(

                XClipboard xClipboard,

                XTransferable xTransferable )

        {

                System.out.println("");

                System.out.println( "Lost clipboard ownership..." );

                System.out.println("");

                isowner = false;

        }

        public boolean isClipboardOwner()

        {

                return isowner;

        }

        private boolean isowner = true;

}

...

The last two samples combined show how it is possible to copy data to the clipboard as demonstrated in the following Java example. (OfficeDev/clipboard/Clipboard.java)

import com.sun.star.datatransfer.*;
import com.sun.star.datatransfer.clipboard.*;
import com.sun.star.uno.AnyConverter;

...

// instantiate the clipboard service

Object oClipboard =

        xMultiComponentFactory.createInstanceWithContext(

        "com.sun.star.datatransfer.clipboard.SystemClipboard",

        xComponentContext);

                       

// query for the interface XClipboard

XClipboard xClipboard = (Xclipboard)UnoRuntime.queryInterface(XClipboard.class, oClipboard);

//---------------------------------------------------
// becoming a clipboard owner
//---------------------------------------------------

System.out.println("Becoming a clipboard owner...");
System.out.println("");

ClipboardOwner aClipOwner = new ClipboardOwner();

xClipboard.setContents(new TextTransferable("Hello World!"), aClipOwner);

while (aClipOwner.isClipboardOwner())
{
        System.out.println("Still clipboard owner...");
        Thread.sleep(1000);
}

...

Becoming a Clipboard Viewer

It is useful to listen to clipboard changes. User interface controls may change their visible appearance depending on the current clipboard content. To avoid polling on the clipboard, the clipboard service supports an asynchronous notification mechanism. Every client that needs notification about clipboard changes implements the interface com.sun.star.datatransfer.clipboard.XClipboardListener and registers as a clipboard listener.
Implementing the interface com.sun.star.datatransfer.clipboard.XClipboardListener is simple as the next Java example demonstrates. (OfficeDev/clipboard/ClipboardListener.java)

//----------------------------
// A simple clipboard listener
//----------------------------

public class ClipboardListener implements XClipboardListener
{
        public void disposing(EventObject event)
        {
        }

        public void changedContents(ClipboardEvent event)
        {
                System.out.println("");
                System.out.println("Clipboard content has changed!");
                System.out.println("");
        }
}

If the interface was implemented by the object, it registers as a clipboard listener. A clipboard listener deregisters if clipboard notifications are no longer necessary. Both aspects are demonstrated in the next example. (OfficeDev/clipboard/Clipboard.java)

// instantiate the clipboard service

Object oClipboard =
        xMultiComponentFactory.createInstanceWithContext(
        "com.sun.star.datatransfer.clipboard.SystemClipboard",
        xComponentContext);

// query for the interface XClipboard

XClipboard xClipboard = (XClipboard)
        UnoRuntime.queryInterface(XClipboard.class, oClipboard);

//---------------------------------------------------
// registering as clipboard listener
//---------------------------------------------------

XClipboardNotifier xClipNotifier = (XClipboardNotifier)
        UnoRuntime.queryInterface(XClipboardNotifier.class, oClipboard);

ClipboardListener aClipListener= new ClipboardListener();

xClipNotifier.addClipboardListener(aClipListener);

...

//---------------------------------------------------
// unregistering as clipboard listener
//---------------------------------------------------

xClipNotifier.removeClipboardListener(aClipListener);

...

OpenOffice.org Clipboard Data Formats

This section describes common clipboard data formats that OpenOffice.org supports and their corresponding DataType.
As previously mentioned, data formats are described by DataFlavors. The important characteristics of a DataFlavor are the MimeType and DataType. The OpenOffice.org clipboard service uses a standard MimeType for different data formats if there is one registered at Iana. For example, for HTML text, the MimeType "text/html" is used, Rich Text uses the MimeType "text/richtext", and text uses "text/plain". If there is no corresponding MimeType registered at Iana, OpenOffice.org defines a private MimeType. Private OpenOffice.org MimeType always has the MimeType "application/x-openoffice". Each private OpenOffice.org MimeType has a parameter "windows_formatname" identifying the clipboard format name used under Windows. The used Windows format names are the format names used with older OpenOffice.org versions. Common Windows format names are "Bitmap", "GDIMetaFile", "FileName", "FileList", and "DIF".
The DataType of a DataFlavor identifies how the data are exchanged. There are only two DataTypes that can be used. The DataType for Unicode text is a string, and in Java, String.class, For all other data formats, the DataType is a sequence of bytes in Java byte[].class.

The following table lists common data formats, and their corresponding MimeType and DataTypes:

Form

MimeType

DataType (in Java)

Description

Unicode Text

text/plain;charset=utf-16

String.class

Unicode Text

Richtext

text/richtext

byte[].class

Richtext

Bitmap

application/x-openoffice;windows_formatname="Bitmap"

byte[].class

A bitmap in OpenOffice bitmap format.

HTML Text

text/html

byte[].class

HTML Text

6.2.2    Internationalization ((later))

6.2.3    Linguistics

The Linguistic API provides a set of UNO services used for spell checking, hyphenation or accessing a thesaurus. Through the Linguistic API, developers add new implementations and integrate them into OpenOffice.org. Users of the Linguistic API call its methods Usually this functionality is used by one or more clients, that is, applications or components, to process documents , such as text documents or spreadsheets.

Services Overview

The services provided by the Linguistic API are:

Also there is at least one or more implementation for each of the following services:

The service implementations for spell checker, thesaurus and hyphenator supply the respective functionality. Each of the implementations support a different set of languages. Refer to com.sun.star.linguistic2.XSupportedLocales.

For example, there could be two implementations for a spell checker, usually from different supporting parties: the first supporting English, French and German, and the second supporting Russian and English. Similar settings occur for the hyphenator and thesaurus.

It is not convenient for each application or component to know all these implementations and to choose the appropriate implementation for the specific purpose and language, therefore a mediating instance is required.

This instance is the LinguServiceManager. Spell checking, hyphenation and thesaurus functionality is accessed from a client by using the respective interfaces from the LinguServiceManager.

The LinguServiceManager dispatches the interface calls from the client to a specific service implementation,if any, of the respective type that supports the required language.
For example, if the client requires spell checking of a French word, the first spell checker implementations from those mentioned above are called.

If there is more than one spell checker available for one language, as in the above example for the English language, the LinguServiceManager starts with the first one that was supplied in the setConfiguredServices() method of its interface. The thesaurus behaves in a similar manner.
For more details, refer to the interface description com.sun.star.linguistic2.XLinguServiceManager.

The LinguProperties service provides, among others, properties that are required by the spell checker, hyphenator and thesaurus that are modified by the client. Refer to thecom.sun.star.linguistic2.LinguProperties.

The DictionaryList (see com.sun.star.linguistic2.DictionaryList) provides a set of user defined or predefined dictionaries for languages that are activated and deactivated. If they are active, they are used by the spell checker and hyphenator. These are used by the user to override results from the spell checker and hyphenator implementations, thus allowing the user to customize spell checking and hyphenation.

In the code snippets and examples in the following chapters, we will use the following members and interfaces: (OfficeDev/linguistic/LinguisticExamples.java)

// used interfaces

import com.sun.star.lang.XMultiServiceFactory;

import com.sun.star.linguistic2.XLinguServiceManager;

import com.sun.star.linguistic2.XSpellChecker;

import com.sun.star.linguistic2.XHyphenator;

import com.sun.star.linguistic2.XThesaurus;

import com.sun.star.linguistic2.XSpellAlternatives;

import com.sun.star.linguistic2.XHyphenatedWord;

import com.sun.star.linguistic2.XPossibleHyphens;

import com.sun.star.linguistic2.XMeaning;

import com.sun.star.linguistic2.XSearchableDictionaryList;

import com.sun.star.linguistic2.XLinguServiceEventListener;

import com.sun.star.linguistic2.LinguServiceEvent;

import com.sun.star.beans.XPropertySet;

import com.sun.star.beans.PropertyValue;

import com.sun.star.uno.XComponentContext;

import com.sun.star.uno.XNamingService;

import com.sun.star.lang.XMultiComponentFactory;

import com.sun.star.lang.EventObject;

import com.sun.star.lang.Locale;

import com.sun.star.bridge.XUnoUrlResolver;

import com.sun.star.uno.UnoRuntime;

import com.sun.star.uno.Any;

import com.sun.star.lang.XComponent;

//

// members for commonly used interfaces

//

// The MultiServiceFactory interface of the Office

protected XMultiServiceFactory mxFactory = null;

// The LinguServiceManager interface

protected XLinguServiceManager mxLinguSvcMgr = null;

// The SpellChecker interface

protected XSpellChecker mxSpell = null;

// The Hyphenator interface

protected XHyphenator mxHyph = null;

// The Thesaurus interface

protected XThesaurus mxThes = null;

// The DictionaryList interface

protected XSearchableDictionaryList mxDicList = null;

// The LinguProperties interface

protected XPropertySet mxLinguProps = null;

To establish a connection to the office and have our mxFactory object initialized with its XMultiServiceFactory, the following code is used: (OfficeDev/linguistic/LinguisticExamples.java)

public void Connect( String sConnection )

    throws com.sun.star.uno.Exception,

    com.sun.star.uno.RuntimeException,

    Exception

{

    XComponentContext xContext =

        com.sun.star.comp.helper.Bootstrap.createInitialComponentContext( null );

    XMultiComponentFactory xLocalServiceManager = xContext.getServiceManager();

   

    Object  xUrlResolver  = xLocalServiceManager.createInstanceWithContext(

        "com.sun.star.bridge.UnoUrlResolver", xContext );

    XUnoUrlResolver urlResolver = (XUnoUrlResolver)UnoRuntime.queryInterface(

        XUnoUrlResolver.class, xUrlResolver );

    Object rInitialObject = urlResolver.resolve( "uno:" + sConnection +

        ";urp;StarOffice.NamingService" );

    XNamingService rName = (XNamingService)UnoRuntime.queryInterface(XNamingService.class,

        rInitialObject );

    if( rName != null )

    {

        Object rXsmgr = rName.getRegisteredObject( "StarOffice.ServiceManager" );

        mxFactory = (XMultiServiceFactory)

            UnoRuntime.queryInterface( XMultiServiceFactory.class, rXsmgr );

    }

}

And the LinguServiceManager object mxLinguSvcMgr is initialized like similar to the following snippet: (OfficeDev/linguistic/LinguisticExamples.java)

/** Get the LinguServiceManager to be used. For example to access spell checker,

    thesaurus and hyphenator, also the component may choose to register itself

    as listener to it in order to get notified of relevant events. */

public boolean GetLinguSvcMgr()

    throws com.sun.star.uno.Exception

{

    if (mxFactory != null) {

        Object aObj = mxFactory.createInstance(

            "com.sun.star.linguistic2.LinguServiceManager" );

        mxLinguSvcMgr = (XLinguServiceManager)

                UnoRuntime.queryInterface(XLinguServiceManager.class, aObj);

    }

    return mxLinguSvcMgr != null;

}    

The empty list of temporary property values used for the current function call only and the language used may look like the following:

// list of property values to used in function calls below.

// Only properties with values different from the (default) values

// in the LinguProperties property set need to be supllied.

// Thus we may stay with an empty list in order to use the ones

// form the property set.

PropertyValue[] aEmptyProps = new PropertyValue[0];

// use american english as language

Locale aLocale = new Locale("en","US","");

Using temporary property values:

To change a value for the example IsGermanPreReform to a different value for one or a limited number of calls without modifying the default values, provide this value as a member of the last function argument used in the examples below before calling the respective functions.

// another list of property values to used in function calls below.

// Only properties with values different from the (default) values

// in the LinguProperties property set need to be supllied.

PropertyValue[] aProps = new PropertyValue[1];

aProps[0] = new PropertyValue();

aProps[0].Name  = "IsGermanPreReform";

aProps[0].Value = new Boolean( true );

Replace the aEmptyProps argument in the function calls with aProps to override the value of IsGermanPreReform from the LinguProperties. Other properties are overridden by adding them to the aProps object.

Using Spellchecker

The interface used for spell checking is com.sun.star.linguistic2.XSpellChecker. Accessing the spell checker through the LinguServiceManager and initializing the mxSpell object is done by: (OfficeDev/linguistic/LinguisticExamples.java)

/** Get the SpellChecker to be used.

*/

public boolean GetSpell()

    throws com.sun.star.uno.Exception,

    com.sun.star.uno.RuntimeException

{

    if (mxLinguSvcMgr != null)

        mxSpell = mxLinguSvcMgr.getSpellChecker();

    return mxSpell != null;

}

Relevant properties

The properties of the LinguProperties service evaluated by the spell checker are:

Spell-checking Properties of com.sun.star.linguistic2.LinguProperties Description

IsIgnoreControlCharacters

Defines if control characters should be ignored or not.

IsUseDictionaryList

Defines if the dictionary-list should be used or not.

IsGermanPreReform

Defines if the new German spelling rules should be used for German language text or not.

IsSpellUpperCase

Defines if words with only uppercase letters should be subject to spellchecking or not.

IsSpellWithDigits

Defines if words containing digits or numbers should be subject to spellchecking or not.

IsSpellCapitalization

dDefines if the captitalization of words should be checked or not.

Changing the values of these properties in the LinguProperties affect all subsequent calls to the spell checker. Instantiate a com.sun.star.linguistic2.LinguProperties instance and change it by calling com.sun.star.beans.XPropertySet:setPropertyValue(). The changes affect the whole office unless another modifies the properties again. This is done implicitly when changing the linguistic settings through Tools - Options - Language Settings - Writing Aids.

The following example shows verifying single words: (OfficeDev/linguistic/LinguisticExamples.java)

// test with correct word

String aWord = "horseback";

boolean bIsCorrect = mxSpell.isValid( aWord, aLocale, aEmptyProps );

System.out.println( aWord + ": " +  bIsCorrect );

// test with incorrect word

aWord = "course";

bIsCorrect = mxSpell.isValid( aWord, aLocale , aEmptyProps );

System.out.println( aWord + ": " +  bIsCorrect );    

Tne following example shows  spelling a single word and retrieving possible corrections:

aWord = "house";

XSpellAlternatives xAlt = mxSpell.spell( aWord, aLocale, aEmptyProps );

if (xAlt == null)

    System.out.println( aWord + " is correct." );

else

{

    System.out.println( aWord + " is not correct. A list of proposals follows." );

    String[] aAlternatives = xAlt.getAlternatives();

    if (aAlternatives.length == 0)

        System.out.println( "no proposal found." );

    else

    {

        for (int i = 0; i < aAlternatives.length; ++i)

            System.out.println( aAlternatives[i] );

    }

}

For a description of the return types interface, refer to com.sun.star.linguistic2.XSpellAlternatives.

Using Hyphenator

The interface used for hyphenation is com.sun.star.linguistic2.XHyphenator. Accessing the hyphenator through the LinguServiceManager and initializing the mxHyph object is done by: (OfficeDev/linguistic/LinguisticExamples.java)

/** Get the Hyphenator to be used.

*/

public boolean GetHyph()

    throws com.sun.star.uno.Exception,

    com.sun.star.uno.RuntimeException

{

    if (mxLinguSvcMgr != null)

        mxHyph = mxLinguSvcMgr.getHyphenator();

    return mxHyph != null;

}

Relevant properties

The properties of the LinguProperties service evaluated by the hyphenator are:

Hyphenating Properties of com.sun.star.linguistic2.LinguProperties

IsIgnoreControlCharacters

Defines if control characters should be ignored or not.

IsUseDictionaryList

Defines if the dictionary-list should be used or not.

IsGermanPreReform

Defines if the new German spelling rules should be used for German language text or not.

HyphMinLeading

The minimum number of characters of a hyphenated word to remain before the hyphenation character.

HyphMinTrailing

The minimum number of characters of a hyphenated word to remain after the hyphenation character.

HyphMinWordLength

The minimum length of a word to be hyphenated.

Changing the values of these properties in the Lingu-Properties affect all subsequent calls to the hyphenator.

A valid hyphenation position is a possible one that meets the restrictions given by the HyphMinLeading, HyphMinTrailing and HyphMinWordLength values.

For example, if HyphMinWordLength is 7, "remove" does not have a valid hyphenation position. Also, this is the case when HyphMinLeading is 3 or HyphMinTrailing is 5.

The following example shows a word hypenated: (OfficeDev/linguistic/LinguisticExamples.java)

// maximum number of characters to remain before the hyphen

// character in the resulting word of the hyphenation

short nMaxLeading = 6;

XHyphenatedWord xHyphWord = mxHyph.hyphenate( "horseback", aLocale, nMaxLeading , aEmptyProps );

if (xHyphWord == null)

    System.out.println( "no valid hyphenation position found" );

else

{

    System.out.println( "valid hyphenation pos found at " + xHyphWord.getHyphenationPos()

            + " in " + xHyphWord.getWord() );

    System.out.println( "hyphenation char will be after char " + xHyphWord.getHyphenPos()

            + " in " + xHyphWord.getHyphenatedWord() );

}

If the hyphenator implementation is working correctly, it reports a valid hyphenation position of 4 that is after the 'horse' part. Experiment with other values for nMaxLeading and other words. For example, if you set it to 4, no valid hyphenation position is found since there is no hyphenation position in the word 'horseback' before and including the 's'.

For a description of the return types interface, refer tocom.sun.star.linguistic2.XHyphenatedWord.

The example below shows querying for an alternative spelling. In some languages, for example German in the old (pre-reform) spelling, there are words where the spelling of changes when they are hyphenated at specific positions. To inquire about the existence of alternative spellings, the queryAlternativeSpelling() function is used: (OfficeDev/linguistic/LinguisticExamples.java)

//! Note: 'aProps' needs to have set 'IsGermanPreReform' to true!

xHyphWord = mxHyph.queryAlternativeSpelling( "Schiffahrt",

                    new Locale("de","DE",""), (short)4, aProps );

if (xHyphWord == null)

    System.out.println( "no alternative spelling found at specified position." );

else

{

    if (xHyphWord.isAlternativeSpelling())

        System.out.println( "alternative spelling detectetd!" );

    System.out.println( "valid hyphenation pos found at " + xHyphWord.getHyphenationPos()

            + " in " + xHyphWord.getWord() );

    System.out.println( "hyphenation char will be after char " + xHyphWord.getHyphenPos()

            + " in " + xHyphWord.getHyphenatedWord() );

}

The return types interface is the same as in the above example (com.sun.star.linguistic2.XHyphenatedWord).

The next example demonstrates getting possible hyphenation positions. To determine all possible hyphenation positions in a word, do this: (OfficeDev/linguistic/LinguisticExamples.java)

XPossibleHyphens xPossHyph = mxHyph.createPossibleHyphens( "waterfall", aLocale, aEmptyProps );

if (xPossHyph == null)

    System.out.println( "no hyphenation positions found." );

else

    System.out.println( xPossHyph.getPossibleHyphens() );    

For a description of the return types interface, refer to com.sun.star.linguistic2.XPossibleHyphens.

Using Thesaurus

The interface used for the thesaurus is com.sun.star.linguistic2.XThesaurus. Accessing the thesaurus through the LinguServiceManager and initializing the mxThes object is done by: (OfficeDev/linguistic/LinguisticExamples.java)

/** Get the Thesaurus to be used.

*/

public boolean GetThes()

    throws com.sun.star.uno.Exception,

    com.sun.star.uno.RuntimeException

{

    if (mxLinguSvcMgr != null)

        mxThes = mxLinguSvcMgr.getThesaurus();

    return mxThes != null;

}

The properties of the LinguProperties service evaluated by the thesaurus are:

Thesaurus related Properties of com.sun.star.linguistic2.LinguProperties

IsIgnoreControlCharacters

Defines if control characters should be ignored or not.

IsGermanPreReform

Defines if the new German spelling rules should be used for German language text or not.

Changing the values of these properties in the LinguProperties affect all subsequent calls to the thesaurus. The following example about retrieving synonyms shows this: (OfficeDev/linguistic/LinguisticExamples.java)

XMeaning[] xMeanings = mxThes.queryMeanings( "house", aLocale, aEmptyProps );

if (xMeanings == null)

    System.out.println( "nothing found." );

else

{

    for (int i = 0; i < xMeanings.length; ++i)

    {

        System.out.println( "Meaning: " + xMeanings[i].getMeaning() );

        String[] aSynonyms = xMeanings[i].querySynonyms();

        for (int k = 0; k < aSynonyms.length; ++k)

            System.out.println( "    Synonym: " + aSynonyms[k] );

    }

}

The reason to subdivide synonyms into different meanings is becausethere are different synonyms for some words that are not even closely related. For example, the word 'house' has the synonyms 'home', 'place', 'dwelling', 'family', 'clan', 'kindred', 'room', 'board', and 'put up'.

The first three in the aboce list have the meaning of 'building where one lives' where the next three mean that of 'a group of people sharing common ancestry' and the last three means that of 'to provide with lodging'. Thus, having meanings is a way to group large sets of synonyms into smaller ones with approximately the same definition.

Events

There are several types of events. For example, all user dictionaries com.sun.star.linguistic2.XDictionary report their status changes as events com.sun.star.linguistic2.DictionaryEvent to the DictionaryList, which collects and transforms their information into DictionaryList events com.sun.star.linguistic2.DictionaryListEvent, and passes those on to its own listeners.

Thus, it is possible to register to the DictionaryList as a listener to be informed about relevant changes in the dictionaries., There is no need to register as a listener for each dictionary.

The spell checker and hyphenator implementations monitor the changes in the LinguProperties for changes of their relevant properties. If such a property changes its value, the implementation launches an event com.sun.star.linguistic2.LinguServiceEventthat hints to its listeners that spelling or hyphenation should be reevaluated. For this purpose, those implementations support the com.sun.star.linguistic2.XLinguServiceEventBroadcaster interface.

The LinguServiceManager acts as a listener for com.sun.star.linguistic2.DictionaryListEvent and com.sun.star.linguistic2.LinguServiceEvent events. The respective interfaces are com.sun.star.linguistic2.XDictionaryListEventListener] and com.sun.star.linguistic2.XLinguServiceEventListener. The events from the DictionaryList are transformed into com.sun.star.linguistic2.LinguServiceEvent events and passed to the listeners of the LinguServiceManager, along with the received events from the spell checkers and hyphenators.

Therefore, a client that wants to be notified when spell checking or hyphenation changes, for example, when it features automatic spell checking or automatic hyphenation, needs to be registered as com.sun.star.linguistic2.XLinguServiceEventListener to the LinguServiceManager only.

Implementing the com.sun.star.linguistic2.XLinguServiceEventListener interface is similar to the following snippet: (OfficeDev/linguistic/LinguisticExamples.java)

/** simple sample implementation of a clients XLinguServiceEventListener

    *  interface implementation

    */

public class Client

        implements XLinguServiceEventListener

{

    public void disposing ( EventObject aEventObj )

    {

        //! any references to the EventObjects source have to be

        //! released here now!

           

        System.out.println("object listened to will be disposed");

    }

    public void processLinguServiceEvent( LinguServiceEvent aServiceEvent )

    {  

        //! do here whatever you think needs to be done depending

        //! on the event recieved (e.g. trigger background spellchecking

        //! or hyphenation again.)

                   

        System.out.println("Listener called");

    }

};

After the client has been instantiated, it needs to register as com.sun.star.linguistic2.XLinguServiceEventListener. For the sample client above, this looks like: (OfficeDev/linguistic/LinguisticExamples.java)

XLinguServiceEventListener aClient = new Client();

// now add the client as listener to the service manager to

// get informed when spellchecking or hyphenation may produce

// different results then before.

mxLinguSvcMgr.addLinguServiceManagerListener( aClient );

This enables the sample client to receive com.sun.star.linguistic2.LinguServiceEvents and act accordingly. Before the sample client terminates, it has to stop listening for events from the LinguServiceManager:

//! remove listener before programm termination.

//! should not be omitted.

mxLinguSvcMgr.removeLinguServiceManagerListener(aClient);

In the LinguisticExamples.java sample, a property is modified for the listener to be called.

Implementing a Spell Checker

A sample implementation of a spell checker isfound in the (OfficeDev/linguistic/SampleSpellChecker.java) file from the examples for linguistics.

The spell checker implements the following interfaces:

and

To implement a spell checker of your own, modify the sample in the following ways:

Choose a unique service implementation nameto distinguish your service implementation from any other. To do this, edit the string in the line

    public static String _aSvcImplName = "com.sun.star.linguistic2.JavaSamples.SampleSpellChecker";

Then, specify the list of languages supported by your service. Edit the

public Locale[] getLocales()

function and modify the

public boolean hasLocale( Locale aLocale )

function accordingly. The next step is to change the

private short GetSpellFailure(...)

as required. This function determines if a word is spelled correctly in a given language. If the word is OK return -1, otherwise return an appropriate value of the type com.sun.star.linguistic2.SpellFailure.

Check if you need to edit or remove the

private boolean IsUpper(...)

and

private boolean HasDigits(...)

functions. Consider this only if you are planning to support non-western languages and need sophisticated versions of those, or do not need them at all. Do not forget to change the code at the end of

public boolean isValid(...)

accordingly.

Supply your own version of

private XSpellAlternatives GetProposals(...)

It provides the return value for the

public XSpellAlternatives spell(...)

function call if the word was found to be incorrect. The main purpose is to provide proposals for how the word might be written correctly. Note the list ay be empty.

Next, edit the text in

public String getServiceDisplayName(...)

It should be unique but it is not necessary. If you are developing a set of services, that is, spellchecker, hyphenator and thesaurus, it should be the same for all of them. This text is displayed in dialogs to show a more meaningful text than the service implementation name.

Now, have a look in the constructor

public SampleSpellChecker()

at the property names. Remove the entries for the properties that are not relevant to your service implementation. If you make modification, also look in the file PropChgHelper_Spell.java in the function

public void propertyChange(...)

and change it accordingly.

Set the values of bSCWA and bSWWA to true only for those properties that are relevant to your implementation, thus avoiding sending unnecessary com.sun.star.linguistic2.LinguServiceEvent events, that is, avoid triggering spell-checking in clients if there is no requirement.

Finally, after registration of the service (see [Chapter:Components.Deployment]) it has to be activated to be used by the LinguServiceManager. After restarting OpenOffice.org, this is done in the following manner:

Open the dialog Tools – Options – Language Settings – Writing Aids. In the section Writing Aids, in the box Available Language Modules, a new entry with text of the Service Display Name that you chose is displayed in the implementation. Check the empty checkbox to the left of that entry. If you want to use your module, uncheck any other listed entry. If you want to make more specific settings per language, press the Edit button next to the modules box and use that dialog.

The Context menu of the Writer that pops up when pressing the right-mouse button over an incorrectly spelled word currently has a bug that may crash the program when the Java implementation of a spell checker is used. The spell check dialog is functioning.

Implementing a Hyphenator

A sample implementation of a hyphenator is found in the (OfficeDev/linguistic/SampleHyphenator.java) file from the examples for linguistic.

The hyphenator implements the following interfaces:

and

Aside from choosing a new service implementation name, the process of implementing the hyphenator is the same as implementing the spell checker, except that you need to implement the com.sun.star.linguistic2.XHyphenator interface instead of the com.sun.star.linguistic2.XSpellChecker interface.

You can choose a different set of languages to be supported. When editing the sample code, modify the hasLocale() and getLocales() methods to reflect the set of languages your implementation supports.

To implement the com.sun.star.linguistic2.XHyphenator interface, modify the functions

public XHyphenatedWord hyphenate(...)

public XHyphenatedWord queryAlternativeSpelling(...)

public XPossibleHyphens createPossibleHyphens(...)<

in the sample hyphenator source file at the stated positions.

Look in the constructor

public SampleHyphenator()

at the relevant properties and modify the

public void propertyChange(...)

function in the file (OfficeDev/linguistic/PropChgHelper_Hyph.java) accordingly.

The rest, registration and activation is again the same as for the spell checker.

Implementing a Thesaurus

A sample implementation of a thesaurus is found in the (OfficeDev/linguistic/SampleThesaurus.java) file from the examples for linguistic.

The thesaurus implements the following interfaces:

and

For the implementation of the thesaurus, modify the sample thesaurus by following the same procedure as for the spell checker and thesaurus:

Choose a different implementation name for the service and modify the

public Locale[] getLocales()

and

public boolean hasLocale(...)

functions.

The only function to be modified at the stated position to implement the com.sun.star.linguistic2.XThesaurus interface is

public XMeaning[] queryMeanings(...)

Look in the constructor

public SampleThesaurus()

to see if there are properties you do not require.

Registration and activation is the same as for the spell checker and hyphenator.

6.2.4    Integrating Import and Export Filters

This section explains the implementation of OpenOffice.org import and export filter components, focussing on filter components. It is intended as a brief introduction for developers who want to implement OpenOffice.org filters for foreign file formats.

Approaches

They are several ways to get information into or out of OpenOffice.org: You can

Each method has unique advantages and disadvantages, that are summarized briefly:

Using the core data structure and linking against the application core is the traditional way to implement filters in OpenOffice.org. The advantages of this method is efficiency and direct access to the document. However, the core implementation provides an implementation centric view of the applications. Additionally, there are a number of technical disadvantages. Every change in the core data structures or objects must be followed by corresponding changes in code that use them. Consequently, filters need to be recompiled to match the binary layout of the application core objects. While these are manageable, albeit cumbersome, for closed source applications, this method is expected to create a maintenance nightmare if application and filters are developed separately as is customary in open source applications. Simultaneous delivery of a new application build and the corresponding filters developed by third parties looks challenging.

Using the OpenOffice.org API based on UNO is more advantageous, since it solves the technical problems indicated in the above paragraph. The idea is to read data from a file on loading and build up a document using the OpenOffice.org API, and to iterate over a document model and write the corresponding data to a file on storing. The UNO component technology insulates the filter from binary layout, and other compiler and version dependent issues. Additionally, the API is expected to be more stable than the core interfaces, and provide abstraction from the core applications. In fact, the example filter implementation of this section makes use of this strategy and is based on the OpenOffice.org API.

The third is to import and export documents using the XML-based file format. UNO-based XML import and export components feature all of the advantages of the previous method, but additionally provides the filter implementer with a clean, structured, and fully documented view of the document. As a significant difficulty in conversion between formats is the conceptual mapping from the one format to the other, a clean, well-structured view of the document may turn out to be beneficial.

This section describes the second method using the UNO-based API. Further details on the third method, based on the generic XML format are found in the xml project of OpenOffice.org under http://xml.openoffice.org/filter/.

Internals of a OpenOffice.org Filter

First, we provide an overview of the import and export process using UNO components, and gain an understanding of the general concepts.

Filtering against a Document API

Inside OpenOffice.org a document is represented by its document service, called model. On disk, the same document is represented as a file or possibly as a dynamic generated output, for example, of a database statement. We cannot assign it to a file on disk, so we call it content to describe it. A filter component is used to convert between these different formats.


Illustration 6.16: Import/Export Filter Process

If you make use of UNO, this above diagram can be turned into programming reality quite easily. The three entities in the diagram, content, model, and filter, all have direct counterparts in UNO services. The services consist of several interfaces that map to a specific implementation, for example, using C++ or Java.

If the implementer decides to make use of the OpenOffice.org API directly, this diagram is the starting point: The filter writer creates a class that implements the com.sun.star.document.ExportFilter or com.sun.star.document.ImportFilter services, or both. To achieve this, the corresponding stream or URL is obtained from the com.sun.star.document.MediaDescriptor. The incoming data is then interpreted and the model is used by calling the appropriate methods. The available methods depend on the type of document as described by the document service.

For a list of available document services, refer to the section 6.1.3 Office Development - OpenOffice.org Application Environment - Using the Component Framework - Models - Document Specific Features.

Filtering Process

Inside OpenOffice.org, the whole process of loading or saving contents is realized as a modular system that is based on UNO services. It functions generically in many components and is easily adapted to the developer's needs through the addition of custom modules or the removal of others.

Loading:

A URL or a stream is passed to com.sun.star.frame.XComponentLoader:loadComponentFromURL(). The load properties create a com.sun.star.document.MediaDescriptor that is filled with the URL or stream, and the load properties. The component loader implementation passes the information about the resource to the TypeDetection.

The com.sun.star.document.TypeDetection uses the MediaDescriptor to determine a unique type name that is necessary to create a filter instance at the com.sun.star.document.FilterFactory.

The TypeDetection also employs the com.sun.star.document.ExtendedTypeDetectionthat examines the given resource and confirms the unique type name determined by TypeDetection. The MediaDescriptor is updated, if necessary, and a unique type name is returned.

Finally, the component loader ensures there is a frame, or creates a new one, if necessary, and asks a  frame loader service (com.sun.star.frame.FrameLoader or com.sun.star.frame.SynchronousFrameLoader) to load the resource into the frame. Its interface com.sun.star.frame.XFrameLoader has a method load() that takes a frame, the MediaDescriptor and an event listener, and creates a com.sun.star.document.ImportFilter instance at the FilterFactory to load the resource into the given frame. For this purpose, it calls createInstance() with the filter implementation name (such as com.sun.star.comp.Writer.GenericXMLFilter) or createInstanceWithArguments() with the implementation name and additional arguments used to initialize the filter.

Then, the loader calls setTargetDocument() and filter() on the ImportFilter service. The ImportFilter creates its results in the given target document.

Storing to a URL:

A URL or a stream is passed to storeToURL() or storeAsURL() in the interface com.sun.star.frame.XStorable, implemented by office documents. The store properties create a media descriptor that is filled with the URL or stream, and the store properties. The TypeDetection provides a unique type name that is used with the FilterFactory to create a com.sun.star.document.ExportFilter.

The XStorable implementation calls setSourceDocument() and filter() at the filter, which writes the results to the storage specified in the MediaDescriptor passed to filter().


Many existing filters are legacy filters. The XStorable implementation does not use the FilterFactory to create them, but triggers filtering by internal calls.

If a URL or an already open stream takes part in the load or save process of the OpenOffice.org, the following services and operations are involved:


Illustration 6.17: General Filtering Process

In the following, the modules that participate in the loading process are discussed in detail.

MediaDescriptor

The media descriptor is an abstract description of a content specifying the where from and the how for the handling of the content to be performed. A content is also called a medium. Refer to 6.1.5 Office Development - OpenOffice.org Application Environment - Handling Documents - Loading Documents - MediaDescriptor for further information. Inside the OpenOffice.org, it is realized as a sequence of com.sun.star.beans.PropertyValue structs as a parameter.

A descriptor is passed to various methods which are involved in the load and save process.

Every member of the process can use this descriptor and change it to update the information about the document. This descriptor is used as an [inout] parameter by com.sun.star.document.XTypeDetection:queryTypeByDescriptor() and com.sun.star.document.XExtendedFilterDetection:detect(). The MediaDescriptor is [in] only in com.sun.star.frame.XComponentLoader:loadComponentFromURL(), com.sun.star.frame.XFrameLoader:load() and com.sun.star.document.XFilter:filter(). With methods that take the MediaDescriptor as [in] parameter only, a manual synchronization must be done by the outside code. The caller of a method that accepts the MediaDescriptor as [in] parameter only merges the results, for example, return values, manually into the original descriptor. The model is not available at loading time. It is the result of the load request.


It is not allowed to hold a member of this descriptor by reference longer than it is used, especially a possible stream item. For example, it would not be possible to close a stream that is still referenced by others. It is only allowed to use it directly or as a copy.


The stream part of the MediaDescriptor is a special item. If a stream exists, it must be used. Only if a stream does not exist, is it allowed to open a new one using the URL. The stream should be set in the MediaDescriptor to provide it for following users of the descriptor.
One rule exists for all: the stream inside the descriptor should be seekable. In case it is not, it makes no sense to provide it to the other members of the whole process, especially used sub-modules. On the other hand, a module can be called with a non-seekable stream from outside to perform the operation. For example, for detection or loading it should be no problem. In case a non-seekable stream comes in, but seeking is important, it must be used buffered.
Another central question is: who controls the lifetime of the stream or the stream position ? The lifetime of a non-seekable stream is controlled by the creator everytime. It has to be deleted after using. Seekable streams should be added to the MediaDescriptor and will be released by the creator of the MediaDescriptor.
Every (sub-) module must be called with a stream seeked to position 0. Of course, non-seekable streams must be newly created and unused. Internally it can do anything with this stream. Furthermore it is not necessary (or even impossible) to restore any positions. The user of the module has to do such things.

TypeDetection

Every content to be loaded must be specified, that is, the type of content represented in the OpenOffice.org must be well known in OpenOffice.org. The type is usually document type,.however, the results of active contents, for example, macros, or database contents are also described here.

A special service com.sun.star.document.TypeDetection is used to accomplish this. It provides an API to associate, for example, a URL or a stream with the extensions well known to OpenOffice.org, MIME types or clipboard formats. The resulting value is an internal unique type name used for further operations by using other services, for example, com.sun.star.frame.FrameLoaderFactory. This type name can be a part of the already mentioned MediaDescriptor.

It is not necessary or useful to replace this service by custom implementations.,It works in a generic method on top of a special configuration. Extending the type detection is done by changing the configuration and is described later. It is required to make these changes if new content formats are provided for OpenOffice.org, because this is the reason to integrate custom filters into the product.

ExtendedTypeDetection

Based on the registered types, flat detection is already possible, that is,. the assignment of types, for example, to a URL, on the basis of configuration data only. Tlat detection cannot always get a correct result if you imagine someone modifying the file extension of a text document from .sxw to .txt.. To ensure correct results, we need deep detection, that is, the content has to be examinedThe com.sun.star.document.ExtendedTypeDetection service performs this task. It is called detector. It gets all the information collected on a document and decides the type to assign it to. In the new modular type detection, the detector is meant as a UNO service that registers itself in the OpenOffice.org and is requested by the generic TypeDetection mechanism, if necessary.

To extend the list of the known content types of OpenOffice.org, we suggest implementing a detector component in addition to a filter. It improves the generic detection of OpenOffice.org and makes the results more secure.

Inside OpenOffice.org, a detector service is called with an already opened stream that is used to find out the content type. In case no stream is given, it indicates that someone else uses this service, for example, outside OpenOffice.org). It is then allowed to open your own stream by using the URL part of the MediaDescriptor. If the resulting stream is seekable, it should be set inside the descriptor after its position is reset to 0. If the stream is not seekable, it is not allowed to set it. Please follow the already mentioned rules for handling streams.

FrameLoader

Frame loaders load a detected type. A visual component is expected as the result. Such visual components are:

Further details are found in section 6.1.1 Office Development - OpenOffice.org Application Environment - Overview - Framework API.

A frame loader service exist in different versions:

It can be searched or created by another service com.sun.star.frame.FrameLoaderFactorythat is described below. The synchronous version is optional. Both services can be implemented at the same component, but the synchronous version is preferred, if it is supported.

There are two ways to extend OpenOffice.org to load a new content format:

Note that the first method does not work for exporting, because a loader service can not be used at save timeTo enable a content format for import and export is to provide a filter service. A generic frame loader implementation already exists in OpenOffice.orgthat uses all well known registered filters in a uniform way. So the second method is preferred.

Filter

Most of the services described before are used for loading. Normally, they are not necessary for saving, except the MediaDescriptor. Only filters are fixed members of both processes.

These objects also represent a service. Their task is to import or export the content of a type into or from a model. Accordingly, import filters are distinguished from export filters. It is possible to provide both functionality in the same implementation.

A filter is acquired from the factory service com.sun.star.document.FilterFactory. It provides a low-level access to the configuration that knows all registered filters of OpenOffice.org, supports search functionality, and creates and initializes filter components. The description of this factory and its configuration are provided below.

If a filter wants to be initialized with its own configuration data or get existing parameters of the corresponding create request, it implements the interface com.sun.star.lang.XInitialization. The method initialize() is used directly after creation by the factory and is the first request on a new filter instance. The parameter list of initialize() uses the following protocol:

A filter should be initialized, because one generic implementation is registered to handle different types, it must know which specialization is required. The simplest way to achieve this for the filter is to know its own configuration data, especially the unique internal name.

This information is used internally then, or it is provided by the interface com.sun.star.container.XNamed. An owner of a filter uses the provided name to find specific information about this component by using the FilterFactory service.


The interface provides functionality for reading and writing of this name. It is not allowed to change an internal filter name during runtime of OpenOffice.org, because all filter names must be unique and it is not possible for a filter instance to alter its name. Calls to com.sun.star.container.XNamed:setName() should be ignored or forwarded to the FilterFactory service, which knows all unique names and can solve ambigities!

This code snippet initializes a filter instance:

private String m_sInternalName;

public void initialize( Object[] lArguments )
        throws com.sun.star.uno.Exception
{
        // no arguments – no initialization
        if (lArguments.length<1)
                return;

        // Arguments[0] = own configuration data
        com.sun.star.beans.PropertyValue[] lConfig =
                (com.sun.star.beans.PropertyValue[])lArguments[0];

        // Arguments[1..n] = optional arguments of create request
     for (int n=1; n<lArguments.length; ++n)
        {
                ...
        }

     // analyze own configuration data for our own internal
        // filter name! Important for generic filter services,
        // which are registered more then once. They can use this
        // information to find out, which specialization of it
        // is required.
        for (int i=0; i<lConfig.length; ++i)
        {
                if (lConfig[i].Name.equals("Name"))
                {
                        m_sInternalName =
                                AnyConverter.toString(lConfig[i].Value);

                        // Tip: A generic filter implementation can use this internal
                        // name at runtime, to detect which specialization of it is required.
                        if (m_sInternalName==”filter_format_1”)
                                m_eHandle = E_FORMAT_1;
                        else
                        if (m_sInternalName==”filter_format_2”)
                                ...
                }
        }
}

Furthermore, depending on its action a filter supports the services com.sun.star.document.ImportFilter for import or com.sun.star.document.ExportFilter for export functionality.

The common interface of both services is com.sun.star.document.XFilter starts or cancels the filter process. How the cancelling is implemented is an internal detail of the filter implementation, however a thread is a good solution.

On calling com.sun.star.document.XFilter:filter(), the already mentioned MediaDescriptor is passed to the service. It includes the necessary information about the content, for example, the URL or the stream, but not the source or the target model for the filter process.

Additional interfaces are part of the service description, com.sun.star.document.XImporter and com.sun.star.document.XExporter to get this information. These interfaces are used directly before the filter operation is started. A filter saves the model set by setTargetDocument() and setSourceDocument(), and uses it inside its filter operation.


The filter() method does not include any information about the required import or export functionality. It seems that it is not possible to implement both at the same object. The interfaces XImporter/XExporter are used to solve this conflict. Only one of them is called for one filter() request. So an internal flag that indicates the using of an interface helps.

This example code detects the required filter operation: (OfficeDev/Filter/AsciiReplaceFilter.java)

private boolean m_bImport;

// used to tell us: "you will be used for import"
public void setTargetDocument(
 com.sun.star.lang.XComponent xDocument )
   throws com.sun.star.lang.IllegalArgumentException
{
    m_bImport = true;
}

// used to tell us: "you will be used for export"
public void setSourceDocument(
 com.sun.star.lang.XComponent xDocument )
   throws com.sun.star.lang.IllegalArgumentException
{
    m_bImport = false;
}

// detect required type of filter operation
public boolean filter(
 com.sun.star.beans.PropertyValue[] lDescriptor )
{
    boolean bState = false;
    if (m_bImport==true)
      bState = impl_import( lDescriptor );
    else
      bState = impl_export( lDescriptor );
    return bState;
}

The MediaDescriptor does not include the model, but it should include the already opened stream, true for the current implementation in OpenOffice.org. If it is there, it must be used. Only if a stream does not exist, it indicates that someone else uses this filter service, for example, outside OpenOffice.org, it creates a stream of your own by using the URL parameter of the descriptor.

In general, a filter must not change the position of an incoming stream without reading or writing data. The position inside the stream is 0. Follow the previouslymentioned rules for handling streams of the section about the MediaDescriptor above. We can make these rules easier, because currently there are no external filters used inside office. See descriptions of the chapter “MediaDescriptor” before ... )).

Filter Options

It is possible to parameterize a filter component. For example, the OpenOffice.org filter "Text - txt - csv (StarCalc)" needs a separator used to detect columns. This information is transported inside the MediaDescriptor. A special property named FilterData of type any exists. The value depends on the filter implementation and is not specified.


There is another string property named FilterOptions. It should be used if the flexibility of an any is not required. For historical reasons, a third-string property FilterFlags exists. It is deprecated, so it is not recommend for use.

A generic UI that uses a filter as one part of a load request does not know about special parameters. Normally, the FilterData are not set inside the media descriptor, therefore a filter should use default values. It should be possible to prompt the user for better values by registering another component that implements the service com.sun.star.ui.dialogs.FilterOptionsDialog. It is called UIComponent. It enables a filter developer to query for user options before the filter operation is performed. It does not show this dialog inside the filter, because any UI can be suppressed, for example, an external application uses the API of OpenOffice.org for scripting running in a hidden mode. The code that uses the filter decides if it is necessary and allowed to use the dialog. If not, the filter lives with missing parameters and uses default values. If it is not possible to have defaults, it aborts the filter() request returning false.

The UIComponent provides an interface com.sun.star.beans.XPropertyAccess used to set the whole MediaDescriptor before executing the dialog using the FilterOptionsDialog interface com.sun.star.ui.dialogs.XExecutableDialog and retrieves the changes. The user of the dialog decides if the changes are merged with the original ones or replaced. Using the whole descriptor provides the informtion about the environment in which the filter works, for example, the URL or information about preview mode. The parameters of a filter depend on it. Normally a UIComponent is shown if no FilterData or FilterOptions are part of the descriptor, so that they are added. In the case where they exist, it is necessary to change it.


If the filter programmer wants to implement a generic dialog for different filters, then he must know which of these filters the UIComponent is shown. This information exists inside the MediaDescriptor, called FilterName. The outside code which uses the dialog knows this filter alsoand should set it in the descriptor, because the implementation name of the component must be known to create the dialog. This information exists inside the configuration where it is registered for a filter.

Configuring a Filter in OpenOffice.org

As previously discussed, the whole process of loading and saving content works generically in many components and can be adapted to the needs of a user through the addition of custom modules or the removal of others. All this information about services and parameters are organized in a special configuration branch of OpenOffice.org called org.openoffice.Office.TypeDetection. The principal structure is shown below:


Illustration 6.18: Structure of org.openoffice.Office.TypeDetection Configuration Branch

As shown on the left, the file consists of lists called sets. The list items are described by the structures shown on the right to which the arrows point. It works similar to 1:n relations in a database. Every filter, frame loader, detector is registered for one or multiple types. The detection of the proper type is important for the functionality of the whole system. If the right loader or filter cannot be found, the load or save request does not produce the right results.

To extend OpenOffice.org to load or save new content formats, a new type entry is added describing the new content. Furthermore, a filter item is registered for this new type. An optional and recommended change for a detector can be done.


It is not a good idea to edit the configuration branch files directly to make these changes. It is better to use the configuration API to do so, because the format of the file may be changed in the future. The properties describing the components, such as types and filters, are always the same and are not likely to be changed or in an incompatible manner. It is better to add entries by specifying their properties using the API only. To make this easier for external programmers, this manual provides a OpenOffice.org Basic script that is used for that purpose called regfilter.bas.

The work to be done by the filter programmer is to provide an ini file that includes the properties and start the basic script inside OpenOffice.org. The script reads the file and uses it to change the configuration package. These changes are done for the user layer of the configuration, so it is possible to restore the original state. There is also an example ini file in the samples folder for this manual that can be used for your own purposes called regfilter.ini.

General Notes

In OpenOffice.org, there are services providing a special API to access the underlying configuration repository. Most of these services support container functionality and allow read access whereas some services offer write access also . During runtime, every configuration item, such as type, filter, and detector, is represented as a sequence of com.sun.star.beans.PropertyValue structs. The next sections describe the names and values of those structures.

Necessary Steps

To extend OpenOffice.org by new content formats, use the following steps:

  1. Implement a filter component. It must be able to load or save the type it is registered for. For access to the office, only the API of the document service or universal content provider keeps the filter compatible with new versions of OpenOffice.org.

  2. Provide an implementation of a com.sun.star.document.ExtendedTypeDetection service to analyze a given content. It must return an internal type name representing the type or an empty value for unknown formats.

  3. Add a filter options dialog if the implemented filter requires additional parameters. Keep it separate from the filter and change the given MediaDescriptor based on user input.
    Document the parameters so that an external script programmer can use this information to provide proper values to the MediaDescriptor.

  4. Register the component libraries as UNO services inside OpenOffice.org. This is done by the mechanism described in the chapter 4.7 Writing UNO Components - Deployment Options for Components.

  5. Adapt the configuration branch org.openoffice.Office.TypeDetection so that it knows these new components. Use OpenOffice.org Basic script regfilter.bas that is provided as an additional tool in this chapter. It requires an ini file that is specified inside the subroutine Main of the script and has to be adjusted for your own purposes. It is well documented, and uses the names and value types described in this manual.

Properties of a Type

Every type inside OpenOffice.org is specified by the properties shown in the table below. These values are accessible at the previously mentioned service com.sun.star.document.TypeDetection using the interface com.sun.star.container.XNameAccess. Write access is not available here. All types are addressed by their internal names.

Properties of a Document Type, available at TypeDetection

Name

string. The internal name of a type must be unique and is also used as a list entry. It contains any special characters, but they must been coded.

UIName

string. Displays the type at the user interface under a localized name. You must assign a value for a language, thus supporting CJK versions. All Unicode characters are permitted here.

MediaType

string. Describes the MIME type of the contents. The reason is that the internal names can be altered at any time without affecting the process.

ClipboardFormat

string. The format is a unique description of this type for use in clipboards.

URLPattern

sequence<string>. Important components of a type are the patterns. They enable the support of your own URL schemata, for example, in OpenOffice.org "private:factory/swriter" for opening an empty text document. The wildcards '*' or '?' are supported here.

Extensions

sequence<string>. The type of a content can be derived from its URL by its extension. In most cases, the flat detection depends on them alone.

Preferred

boolean. Since file extensions cannot always be assigned to a unique type, this flag was introduced. It indicates the preferred type for a group of types with similar properties, otherwise, the first match is used.

DocumentIconID

int. You can assign an icon to a type. To do this, the ID is used as reference to a resource. This feature is currently not supported in OpenOffice.org.

Properties of an ExtendedTypeDetection Service

In contrast to filters or frame loaders, the ExtendedTypeDetection has no configuration API on top of its configuration data. The normal configuration API of OpenOffice.org has to be used, as described in [Chapter:Config]. The configuration set org.openoffice.Office.TypeDetection/DetectServices could be used, but it is better to use the already mentioned basic macro regfilter.bas in combination with regfilter.ini. Such detector services are used automatically during type detection of content. A detector service is addressed by its UNO implementation name.

Property Name

Description

ServiceName

string. This must be a valid UNO implementation name. This field cannot contain the service name, because this value must be unique, otherwise it would be impossible to distinguish more than one registered entry, for a service name is not unique. This value is also an entry in the corresponding configuration list.

Types

sequence<string>. A list of type names recognized by this service that makes it possible to write a servicethat detects more than one type.

Properties of a Filter

Every filter is registered for only one type . Multiple registrations are to be done by multiple configuration entries. One type is handled by more than one filter. Flags also regulate the use of the preferred filter. A filter is described by the following properties:

Property Name

Description

Name

string. The internal name of a filter must be unique and is also used as list entry. It contains special characters, but they must be encoded.

UIName

string. A filter should be able to show a localized name in selection dialogs. You must assign a value for a language, thus supporting CJK versions. All Unicode characters are permitted here.

Installed

boolean. This flag indicates the installation status of a filter. A filter is generally registered equally for all users. In a network installation you should deactivate this for certain groups or single users.

Note: A filter works only if the component library has already been registered in OpenOffice.org.

Order

int. This number shows filters in a user defined order. Valid values are greater then 0. If the number is set to 0, sorting is done alphabetically by the UIName property of the filter. The same applies to filters that have the same Order value.

Type

string. A filter must register itself for the type it can handle. Multiple assignments are not allowed. Multiple configuration entries must be created, one for every supported type.

DocumentService

string. Describes the component for which the filter operates,For example, "com.sun.star.text.TextDocument", depending upon the use., This is considered the output or goal of the filter process. A UNO service name is expected. Note: The implementation name cannot be used here, the generic type of the document is needed.

FilterService

string. This is the UNO implementation name of the filter. It should be clear that this field can not contain the service name of a filter, otherwise OpenOffice.org could not distinguish more then one registered filter.

UIComponent

string. Describes an implementation of a UI dialog used by the filter to let the user modify certain properties for filtering. For example, the "Text - txt - csv (StarCalc)" needs information about the used column separators to load data. To distinguish between different implementations, it must be the real UNO implementation name, not a service name.

Flags

int. Describes the filter, as shown in the table below. This is where, the organization into import and export filters takes place. Note that external filters must set the ThirdParty flag to be detected.

UserData

sequence<string>. Some filters need to store more configuration data than usual. This is realized through this entry. The format of the string list is not restricted.

FileFormatVersion

int. Indicates a version number of a document that can be edited by this filter.

TemplateName

string. The name of a template file for importing styles. It is a special feature for importing documents only and not useable for export. Every OpenOffice.org document service knows default styles. If this TemplateName is set, it merges these default styles with the styles of the template, and the template styles are merged with all styles of a document that is imported by this filter.

Most functionality of a Filter is listed by its flags. They are necessary to prevent a filter from being displayed in a UI, and to classify import and export, or internal a nd external filters, and prefer some filters to others. Currently supported flags are:

Name

Value

Description

Import

0x00000001 h

This filter supports the specification of a com.sun.star.document.ImportFilter and is used for loading content.

Export

0x00000002 h

This filter supports the specification of a com.sun.star.document.ExportFilter and is used for saving content.

Template

0x00000004 h

These filters are specialized to handle template formats. By default, a filtered document is used as a template to create a new document .

Internal

0x00000008 h

This filter should never be shown on any UI and not be available.

OwnTemplate

0x00000010 h

Templates used with the template API of OpenOffice.org and it supports the internal template features. For older versions, it is useable for internal content formats only.

Own

0x00000020 h

Tag the intrinsic content formats of OpenOffice.org based on OLE storage or zip packages.

Alien

0x00000040 h

A filter with this flag is not fully compatible with the current document format. It is unclear what document features will be lost during saving. This flag decides if a warning box on saving has to be shown.

UsesOptions
(deprecated)

0x00000080 h

This filter could be customized during processing. Older versions of OpenOffice.org used it to customize the "SaveAs" dialog. Newer versions uses the filter property "UIComponent" to tell if a filter provides filter options.

Default

0x00000100 h

Mark a filter as the default filter for saving. Only one filter in an application module, distinguished through the DocumentService property, has this flag set.

NotInFileDialog

0x00001000 h

Suppress display of a filter in file open and save dialogs.

NotInChooser

0x00002000 h

Suppress display of a filter in UI elements for choosing filters.

ThirdParty

0x00080000 h

These filters are developed by external parties. For historical reasons, the filter detection of OpenOffice.org differentiates between old internal and new external ones, because the former are not UNO based and are used differently.

Preferred

0x10000000 h

If more than one filter is registered for the same type, this flag prefers one of them at loading time if the user does not select a specific filter. In contrast to the Default flag, it does not depend on the application module, but there can only be one preferred filter for a type.


Besides these filter flags there are other flags existing that are used currently, but are not documented here. Use documented flags only.

The service com.sun.star.document.FilterFactory provides these data. It supports read access by using the interface com.sun.star.container.XNameAccess. All items are addressed by their internal names. The return value is represented as a list of type com.sun.star.beans.PropertyValue structures. It uses the filter properties shown above.

Another aspect of this service is the factory interface com.sun.star.lang.XMultiServiceFactory. It creates filter instances using an internal type, or an internal filter name directly. Using a type name searches for a suitable filter and creates, initializes and returns it. Using a filter name directly follows the algorithm shown in the box below. Note that creation of filters is possible for external ones only that have set the FilterService property. Most of the current filters of OpenOffice.org are internal filters, implemented as local code, but not as a UNO service. They can not be created by this FilterFactory. It is possible to ask only for their properties.


Direct creation of a filter instance is only possible using a special argument in the createInstanceWithArguments() call of the interface XMultiServiceFactory. To do so, a com.sun.star.beans.PropertyValue FilterName with the internal name of the requested filter as value must be used. Otherwise, the service specifier, that is, the first argument of the create call, is interpreted as an internal type name. It will be used to search a suitable, preferred filter that will be created. It is a combination of searching and creation. Future implementations will split that to make it clearer. In future implementations, a registered filter must be searched through the provided query mechanism and created by using this factory interface.

Properties of a FrameLoader

OpenOffice.org distinguishes asynchronous (com.sun.star.frame.FrameLoader) and synchronous (com.sun.star.frame.SynchronousFrameLoader) frame loader implementations, but the configuration does not recognize that. The interface is supported by the loader is detected at runtime , the synchronous interface being preferred. The following properties describe a loader:

Properties of a FrameLoader

Name

string. This must be a valid UNO implementation name. It should be obvious that this field can not contain the service name, because this value must be unique. Otherwise OpenOffice.org could not distinguish more than one registered entry, for there can be several implementations for a service name. This value is also an entry in the corresponding configuration list.

UIName

string. Displays the loader at a localized user interface. You must assign a value for a language, thus supporting CJK versions. All Unicode characters are permitted.

Types

sequence<string>. A list of type names recognized by this service You can also implement and register loader for groups of types.

The service com.sun.star.frame.FrameLoaderFactory makes this data available. It uses the same mechanism as the com.sun.star.document.FilterFactory, that is, an interface for data access, com.sun.star.container.XNameAccess, and another one for creation of such a FrameLoader, com.sun.star.lang.XMultiServiceFactory.

There are other properties than the properties described, for example, for the ContentHandler. They are not necessary for the environment of filters, or loading and saving documents, so they are not described. Additional information is found at http://framework.openoffice.org.

There is one entry in the configuration, used as a fallback if a registered item is not found, the generic FrameLoader. It is not necessary for an external developer to provide a frame loader to add support for an unknown document format to OpenOffice.org. It is enough to register a new filter component that is used by this special loader in a generic manner.

6.2.5    Number Formats

Number formats are template strings consisting of format codes defining how numbers or text appear, for example,, whether or not to display trailing zeroes, group by thousands, separators, colors, and how many decimals are displayed. This does not include any font attributes, except for colors. They are found wherever number formats are applied, for example, on the Numbers tab of the Format – Cells dialog in spreadsheets.

Number formats are defined on the document level. A document displaying formatted values has a collection of number formats, each with a unique index key within that document. Identical formats are not necessarily represented by the same index key in different documents.

Managing Number Formats

Documents provide their formats through the interface com.sun.star.util.XNumberFormatsSupplierthat has one method getNumberFormats() that returns com.sun.star.util.NumberFormats. Using NumberFormats, developers can read and modify number formats in documents, and also add new formats.

You have to retrieve the NumberFormatsSupplier as a property at a few objects from their com.sun.star.beans.XPropertySet interface, for example, from data sources supporting the com.sun.star.sdb.DataSource service and from database connections supporting the service com.sun.star.sdb.DatabaseEnvironment, or com.sun.star.sdb.DatabaseAccess. In addition, all UNO controls offering the service com.sun.star.awt.UnoControlFormattedFieldModel have a NumberFormatsSupplier property.

NumberFormats Service

The com.sun.star.util.NumberFormats service specifies a container of number formats and implements the interfaces com.sun.star.util.XNumberFormatTypes and com.sun.star.util.XNumberFormats.

XNumberFormats

NumberFormats supports the interface com.sun.star.util.XNumberFormats. This interface provides access to the number formats of a container. It is used to query the properties of a number format by an index key, retrieve a list of available number format keys of a given type for a given locale, query the key for a user-defined format string, or add new format codes into the list or to remove formats.

com::sun::star::beans::XPropertySet getByKey ( [in] long nKey )

sequence< long > queryKeys ( [in] short nType,

               [in] com::sun::star::lang::Locale nLocale,

               [in] boolean bCreate )

long queryKey ( [in] string aFormat,

               [in] com::sun::star::lang::Locale nLocale,

               [in] boolean bScan )

long addNew ( [in] string aFormat, [in] com::sun::star::lang::Locale nLocale )

long addNewConverted ( [in] string aFormat, [in] com::sun::star::lang::Locale nLocale,

             [in] com::sun::star::lang::Locale nNewLocale )

void removeByKey ( [in] long nKey )

string generateFormat ( [in] long nBaseKey, [in] com::sun::star::lang::Locale nLocale,

             [in] boolean bThousands, [in] boolean bRed, [in] short nDecimals, [in] short nLeading )

The important methods are probably queryKey() and addNew(). The method queryKey() finds the key for a given format string and locale, whereas addNew() creates a new format in the container and returns its key for immediate use. The bScan is reserved for future use and should be set to false.

The properties of a single number format are obtained by a call to getByKey() which returns a [IDL:com.sun.star.util.NumberFormatProperties] service for the given index key.

XNumberFormatTypes

The interface com.sun.star.util.XNumberFormatTypes offers functions to retreive the index keys of specific predefined number format types. The predefined types are addressed by constants from com.sun.star.util.NumberFormat.The NumberFormat contains values for predefined format types, such as PERCENT, TIME, CURRENCY, and TEXT.

long getStandardIndex ( [in] com::sun::star::lang::Locale nLocale )

long getStandardFormat ( [in] short nType,

                [in] com::sun::star::lang::Locale nLocale )

long getFormatIndex ( [in] short nIndex,

                [in] com::sun::star::lang::Locale nLocale )

boolean isTypeCompatible ( [in] short nOldType, [in] short nNewType )

long getFormatForLocale ( [in] long nKey,

                [in] com::sun::star::lang::Locale nLocale )

In most cases you will need getStandardFormat(). It expects a type constant from the NumberFormat group and the locale t to use, and returns the key of the corresponding predefined format.

Applying Number Formats

To format numeric values, an XNumberFormatsSupplier is attached to an instance of a com.sun.star.util.NumberFormatter, available at the global service manager. For this purpose, its main interface com.sun.star.util.XNumberFormatter has a method attachNumberFormatsSupplier(). When the XNumberFormatsSupplier is attached, strings and numeric values are formatted using the methods of the NumberFormatter. To specify the format to apply, you have to get the unique index key for one of the formats defined in NumberFormats. These keys are available at the XNumberFormats and XNumberFormatTypes interface of NumberFormats.

Numbers in documents, such as in table cells, formulas, and text fields, are formatted by applying the format key to the NumberFormat property of the appropriate element.

NumberFormatter Service

The service com.sun.star.util.NumberFormatter implements the interfaces com.sun.star.util.XNumberFormatter and com.sun.star.util.XNumberFormatPreviewer.

XNumberformatter

The interface com.sun.star.util.XNumberFormatter converts numbers to strings, or strings to numbers, or detects a number format matching a given string.

void attachNumberFormatsSupplier ( [in] com::sun::star::util::XNumberFormatsSupplier xSupplier )

com::sun::star::util::XNumberFormatsSupplier getNumberFormatsSupplier ()

long detectNumberFormat ( [in] long nKey, [in] string aString )

double convertStringToNumber ( [in] long nKey, [in] string aString )

string convertNumberToString ( [in] long nKey, [in] double fValue );

com::sun::star::util::color queryColorForNumber ( [in] long nKey, [in] double fValue,

                                                 [in] com::sun::star::util::color aDefaultColor )

string formatString ( [in] long nKey, [in] string aString );

com::sun::star::util::color queryColorForString ( [in] long nKey, [in] string aString,

                                                 [in] com::sun::star::util::color aDefaultColor )

string getInputString ( [in] long nKey, [in] double fValue )

XNumberformatPreviewer

string convertNumberToPreviewString ( [in] string aFormat, [in] double fValue,

                                     [in] com::sun::star::lang::Locale nLocale,

                                     [in] boolean bAllowEnglish )

com::sun::star::util::color queryPreviewColorForNumber ( [in] string aFormat, [in] double fValue,

                                     [in] com::sun::star::lang::Locale nLocale,

                                     [in] boolean bAllowEnglish,

                                     [in] com::sun::star::util::color aDefaultColor )

This interface com.sun.star.util.XNumberFormatPreviewerconverts values to strings according to a given format code without inserting the format code into the underlying com.sun.star.util.NumberFormats collection.

The example below demonstrates the usage of these interfaces. (OfficeDev/NumberFormats.java)

public void doSampleFunction() throws RuntimeException, Exception

{

    // Assume:

    // com.sun.star.sheet.XSpreadsheetDocument maSpreadsheetDoc;

    // com.sun.star.sheet.XSpreadsheet maSheet;

    // Query the number formats supplier of the spreadsheet document

    com.sun.star.util.XNumberFormatsSupplier xNumberFormatsSupplier =

        (com.sun.star.util.XNumberFormatsSupplier)

        UnoRuntime.queryInterface(

        com.sun.star.util.XNumberFormatsSupplier.class, maSpreadsheetDoc );

    // Get the number formats from the supplier

    com.sun.star.util.XNumberFormats xNumberFormats =

        xNumberFormatsSupplier.getNumberFormats();

    // Query the XNumberFormatTypes interface

    com.sun.star.util.XNumberFormatTypes xNumberFormatTypes =

        (com.sun.star.util.XNumberFormatTypes)

        UnoRuntime.queryInterface(

        com.sun.star.util.XNumberFormatTypes.class, xNumberFormats );

    // Get the number format index key of the default currency format,

    // note the empty locale for default locale

    com.sun.star.lang.Locale aLocale = new com.sun.star.lang.Locale();

    int nCurrencyKey = xNumberFormatTypes.getStandardFormat(

        com.sun.star.util.NumberFormat.CURRENCY, aLocale );

    // Get cell range B3:B11

    com.sun.star.table.XCellRange xCellRange =

        maSheet.getCellRangeByPosition( 1, 2, 1, 10 );

    // Query the property set of the cell range

    com.sun.star.beans.XPropertySet xCellProp =

        (com.sun.star.beans.XPropertySet)

        UnoRuntime.queryInterface(

        com.sun.star.beans.XPropertySet.class, xCellRange );

    // Set number format to default currency

    xCellProp.setPropertyValue( "NumberFormat", new Integer(nCurrencyKey) );

    // Get cell B3

    com.sun.star.table.XCell xCell = maSheet.getCellByPosition( 1, 2 );

    // Query the property set of the cell

    xCellProp = (com.sun.star.beans.XPropertySet)

        UnoRuntime.queryInterface(

        com.sun.star.beans.XPropertySet.class, xCell );

    // Get the number format index key of the cell's properties

    int nIndexKey = ((Integer) xCellProp.getPropertyValue( "NumberFormat" )).intValue();

    // Get the properties of the number format

    com.sun.star.beans.XPropertySet xProp = xNumberFormats.getByKey( nIndexKey );

    // Get the format code string of the number format's properties

    String aFormatCode = (String) xProp.getPropertyValue( "FormatString" );

    System.out.println( "FormatString: `" + aFormatCode + "'" );

    // Create an arbitrary format code

    aFormatCode = "\"wonderful \"" + aFormatCode;

    // Test if it is already present

    nIndexKey = xNumberFormats.queryKey( aFormatCode, aLocale, false );

    // If not, add to number formats collection

    if ( nIndexKey == -1 )

    {

        try

        {

            nIndexKey = xNumberFormats.addNew( aFormatCode, aLocale );

        }

        catch( com.sun.star.util.MalformedNumberFormatException ex )

        {

            System.out.println( "Bad number format code: " + ex );

            nIndexKey = -1;

        }

    }

    // Set the new format at the cell

    if ( nIndexKey != -1 )

        xCellProp.setPropertyValue( "NumberFormat", new Integer(nIndexKey) );

}

6.2.6    Common Dialogs ((later))

6.2.7    DocumentInfo ((later))

6.2.8    Search and Replace ((possibly later))

6.2.9    Package File Formats ((later))

6.2.10    Document Events ((later))

[ Previous document | Content Table | Next document ]