Java Window Integration

From Apache OpenOffice Wiki
Jump to: navigation, search



This section discusses experiences obtained during the development of Java-Apache OpenOffice 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 Apache OpenOffice windows. This chapter shows the integration of Apache OpenOffice windows into a Java bean environment. Some of this information may be 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:

  • a method to query the window handle (HWND on Windows, X11 ID on UNIX)
  • a method to identify the operating system, for example, UNIX, Windows, or Macintosh

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

Documentation caution.png It has to be a java.awt.Canvas. 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.
Documentation note.png 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 the Apache OpenOffice 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 Apache OpenOffice window toolkit. This interface can have an empty implementation. In 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 gets a com.sun.star.awt.XWindow as a child of your own Java window, that is used to initialize a com.sun.star.frame.XFrame.

  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 implements the interface 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 Apache OpenOffice 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.

Documentation caution.png As a user of createSystemChild() ensure that your client (Java application) and your server (Apache OpenOffice) use the same display. Otherwise the window handle is not interchangeable.
  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 without 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);
Documentation note.png 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, Apache OpenOffice'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 Apache OpenOffice 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.

Documentation caution.png 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 Apache OpenOffice window, set both sizes to a valid, positive value, otherwise the Apache OpenOffice 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 Apache OpenOffice 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 Apache OpenOffice authors are provided:

  • Listeners in Java should be implemented in a thread. The problem is that SolarMutex, a mutex semaphore of Apache OpenOffice, one-way UNO methods and the global Java GUI thread do not work together.
  • The Java applet should release its listeners. If they stay in the containers of Apache OpenOffice after the Java process ends, UNO throws a com.sun.star.lang.DisposedException, which are not caught correctly. Java does not know destructors, therefore it is a difficult to follow this advice. One possibility is to register a Thread object at java.Runtime as a ShutDownHook. This is called even when CTRL-C is pressed on the command line where you can deregister the listeners. Because listeners are threads, there is some effort.
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages