/*************************************************************************
*
* $RCSfile: BasicOfficeBean.java,v $
*
* $Revision: 1.4 $
*
* last change: $Author: hr $ $Date: 2003/06/30 15:29:18 $
*
* The Contents of this file are made available subject to the terms of
* the BSD license.
*
* Copyright (c) 2003 by Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Sun Microsystems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*************************************************************************/
import java.awt.Component;
import java.awt.Container;
import java.awt.Canvas;
import java.awt.Window;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Rectangle;
import java.io.IOException;
import java.util.List;
import java.util.LinkedList;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XEventListener;
import com.sun.star.lang.EventObject;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.lang.DisposedException;
import com.sun.star.awt.XWindow;
import com.sun.star.awt.XWindowPeer;
import com.sun.star.awt.InvalidateStyle;
import com.sun.star.frame.XController;
import com.sun.star.frame.XDesktop;
import com.sun.star.frame.XFrame;
import com.sun.star.frame.XFrames;
import com.sun.star.frame.XFramesSupplier;
import com.sun.star.frame.XModel;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XDispatchProvider;
import com.sun.star.frame.XSynchronousFrameLoader;
import com.sun.star.frame.XStorable;
import com.sun.star.frame.FrameActionEvent;
import com.sun.star.frame.FrameAction;
import com.sun.star.frame.FrameSearchFlag;
import com.sun.star.document.XTypeDetection;
import com.sun.star.document.MacroExecMode;
import com.sun.star.beans.PropertyValue;
import com.sun.star.beans.PropertyState;
import com.sun.star.beans.PropertyVetoException;
import com.sun.star.beans.OfficeConnection;
import com.sun.star.beans.OfficeWindow;
import com.sun.star.beans.LocalOfficeWindow;
import com.sun.star.util.CloseVetoException;
import com.sun.star.util.XCloseable;
import com.sun.star.util.XModifiable;
import com.sun.star.util.XURLTransformer;
import com.sun.star.uno.UnoRuntime;
/**
* This class provides a basic functionality of the office bean.
* The application developer can subclass from this class in order
* to provide application specific methods.
*/
public class BasicOfficeBean
extends Container
{
private transient OfficeConnection mConnection;
private transient OfficeWindow mWindow;
private transient CommandConveyor mCommandConveyor;
protected String mDocumentURL;
protected transient XMultiServiceFactory mServiceFactory;
protected transient Object mDesktop;
protected transient XFrame mFrame;
protected transient XModifiable mModifiable;
// slot command execution environment
protected transient XDispatchProvider mDispatcher;
protected transient XURLTransformer mURLTransformer;
protected transient XTypeDetection mTypeDetector;
/**
* Constructor.
*
* This constructor sets default layout manager (BorderLayout)
* and creates the command execution conveyor. It also sets the
* default appearance of the bean.
*/
public BasicOfficeBean()
{
setLayout(new BorderLayout());
mCommandConveyor = new CommandConveyor();
mCommandConveyor.start();
add(new PlaceHolder());
}
/**
* Sets the office connection object.
* A subclass can provide own implementation of this method
* in order to set up the ContainerFactory on the connection
* object, but it MUST chain with this method implementation.
*
* @param connection The connection object.
*/
public synchronized void setOfficeConnection(OfficeConnection connection)
throws java.lang.IllegalStateException
{
if (mWindow != null)
throw new java.lang.IllegalStateException(
"The connection is still open.");
mConnection = connection;
mConnection.addEventListener(new ConnectionListener());
}
/**
* Determines wether document was loaded or not.
*
* @return <code>true</code> if a document has been loaded.
*/
public boolean isDocumentLoaded()
{
return (mDocumentURL != null);
}
/**
* Loads a document referenced by a URL.
*
* @param url The document's URL string.
* @exception java.io.IOException if the document loading process has
* failed.
*/
public synchronized void load(String url)
throws java.io.IOException
{
try {
if (mWindow == null)
openConnection();
// Determine the document URL.
if (url.equals(""))
url = getDefaultDocumentURL();
// get XComponentLoader (<= 6.0 => global, >= 6.1 => from frame)
XComponentLoader xLoader = (XComponentLoader) UnoRuntime.queryInterface(
XComponentLoader.class, mFrame );
if ( xLoader == null )
{
xLoader = (XComponentLoader)UnoRuntime.queryInterface(
XComponentLoader.class, mDesktop );
}
// Avoid Dialog 'Document changed' while reloading
boolean bWasModified = isModified();
XController xOldController = null;
if ( mFrame != null && mFrame.getController() != null )
xOldController = mFrame.getController();
try {
if ( mFrame != null && xOldController != null )
xOldController.suspend(true);
setModified(false);
} catch (java.lang.IllegalStateException exp) {
}
// load the document.
PropertyValue aArgs[] = new PropertyValue[1];
aArgs[0] = new PropertyValue();
aArgs[0].Name = "MacroExecutionMode";
aArgs[0].Handle = -1;
aArgs[0].Value = new Short( MacroExecMode.USE_CONFIG );
XComponent xComponent = xLoader.loadComponentFromURL( url,
mFrame.getName(), FrameSearchFlag.ALL, aArgs );
// nothing loaded?
if ( xComponent == null )
{
// reactivate old document
if ( mFrame != null && mFrame.getController() != null )
mFrame.getController().suspend(false);
setModified(true);
// throw exception
throw new java.io.IOException(
"Can not load a document: \"" + url + "\"");
}
mDocumentURL = url;
// Get document's XModifiable interface if any.
mModifiable = (XModifiable)UnoRuntime.queryInterface(
XModifiable.class, xComponent );
// show window (just to test bean creation without system window)
setVisible(true);
((LocalOfficeWindow)mWindow).setVisible(true);
// Find top most parent and force it to validate.
Container parent = this;
while(parent.getParent() != null)
parent = parent.getParent();
((Window)parent).validate();
} catch (com.sun.star.uno.Exception exp) {
throw new java.io.IOException(exp.getMessage());
}
}
/**
* Saves the document.
*/
public void save()
throws java.io.IOException
{
if (isDocumentLoaded() == false)
throw new java.io.IOException("The document has not been loaded.");
// else if (mDocumentURL.startsWith("private:") == true)
// throw new java.io.IOException("The document URL has not been defined.");
XStorable storable = (XStorable)UnoRuntime.queryInterface(
XStorable.class, mFrame.getController().getModel());
try {
storable.store();
} catch (com.sun.star.io.IOException exp) {
throw new java.io.IOException(exp.getMessage());
}
}
/**
* Saves the document.
*
* @param url The location where the document should be stored.
*/
public void save(String url)
throws java.io.IOException
{
// The implementation of this method MUST replace
// the document URL with new value on succesful save.
throw new java.lang.UnsupportedOperationException(
"There is no implementation for this operation.");
}
/**
* Queues an office command for asynchronous execution.
*
* @param command The office command for asynchronous execution.
*/
public void queue(OfficeCommand command)
{
if (command == null)
throw new java.lang.IllegalArgumentException(
"The command may not be null.");
mCommandConveyor.queue(command);
}
/**
* Retrives a URL of the default office document.
*
* This method can be overriden by a subclass in order to customize
* the default document URL.
*
* @return The URL of the default document.
*/
public String getDefaultDocumentURL()
{
return null;
}
/**
* Retrives an office document URL.
*
* @return The URL of the office document.
*/
public String getDocumentURL()
{
return mDocumentURL;
}
/**
* Determines is the office document modifiable.
*
* @return <code>true</code> if the document is modifiable.
*/
public boolean isModifiable()
{
return (mModifiable != null);
}
/**
* Sets the office document modification state.
*
* @param state The new state of document modification.
* @exception java.lang.IllegalStateException if the documnent can not
* accept the new modification state.
*/
public void setModified(boolean state)
throws java.lang.IllegalStateException
{
if (isModifiable() == false) {
throw new java.lang.IllegalStateException(
"The document \"" + mDocumentURL + "\" is not modifiable.");
}
try {
mModifiable.setModified(state);
} catch (com.sun.star.beans.PropertyVetoException exp) {
throw new java.lang.IllegalStateException(exp.getMessage());
}
}
/**
* Retrives the office document modification state.
*
* @return The document modification state.
*/
public boolean isModified()
{
return (isModifiable() != false)? mModifiable.isModified(): false;
}
/**
* Opens the connection.
*/
public synchronized void openConnection()
throws com.sun.star.uno.Exception
{
if (mWindow != null)
return;
// Establish the connection by request of the ServiceFactory.
XMultiComponentFactory compfactory;
compfactory = mConnection.getComponentContext().getServiceManager();
mServiceFactory = (XMultiServiceFactory)UnoRuntime.queryInterface(
XMultiServiceFactory.class, compfactory);
// remove existing child windows
removeAll();
// make initially invisible (just to test bean creation without system window)
setVisible(false);
// Create the OfficeWindow.
mWindow = mConnection.createOfficeWindow(this);
add(mWindow.getAWTComponent());
// Create the office document frame and initialize the bean.
initialize();
}
/**
* Closes the connection.
*/
public synchronized void closeConnection()
{
mConnection.dispose();
}
/**
* Recieves notification of the connection closed event.
*
* If a subclass wants to be notified on connection closed event it
* should add a listener to the connection object.
*/
private synchronized void connectionClosed()
{
if (mWindow != null)
{
// close the frame
try
{
XCloseable xCloseable = (XCloseable)UnoRuntime.queryInterface(
XCloseable.class, mFrame);
xCloseable.close(true);
}
catch ( DisposedException aExc )
{
// add your error handling code here
}
catch ( CloseVetoException aExc )
{
// add your error handling code here
}
// Clean up
mFrame = null;
mModifiable = null;
mDispatcher = null;
mTypeDetector = null;
mURLTransformer = null;
mServiceFactory = null;
remove((Component)mWindow);
mWindow = null;
}
}
/**
* This class is a connection life-cycle event listener.
*/
private class ConnectionListener
implements XEventListener
{
/**
* Receives a notification about the connection has been closed.
*
* @param source The event object.
*/
public void disposing(EventObject source)
{
connectionClosed();
}
}
/**
* Office command execution conveyor.
*/
private class CommandConveyor
extends Thread
{
private final List mQueue = new LinkedList();
/**
* Appends a command to the tail of the queue.
*
* @param command The office command to add to the queue.
*/
public void queue(OfficeCommand command)
{
synchronized (mQueue) {
mQueue.add(command);
mQueue.notify();
}
}
/**
* Executes commands from the queue.
*/
public void run()
{
OfficeCommand command;
while (true) {
try {
synchronized (mQueue) {
if (mQueue.isEmpty() == true) {
mQueue.wait();
}
command = (OfficeCommand)mQueue.remove(0);
}
command.execute(BasicOfficeBean.this);
} catch (java.lang.InterruptedException exp) {
// Wait has been interrupted.
// Do nothing, but do not execute a command which
// does not exist.
}
}
}
}
/**
* Initializes the bean.
*
* @exception com.sun.star.uno.Exception if the frame has not been
* created or the slot command execution environment initialization
* has failed.
*/
private void initialize()
throws com.sun.star.uno.Exception
{
Object object;
// Create the document frame from UNO window. (<= 6.0 => Task, >= 6.1 => Frame)
XWindow window = (XWindow) UnoRuntime.queryInterface(
XWindow.class, mWindow.getUNOWindowPeer());
object = mServiceFactory.createInstance( "com.sun.star.frame.Task");
if ( object == null )
object = mServiceFactory.createInstance( "com.sun.star.frame.Frame");
mFrame = (XFrame)UnoRuntime.queryInterface( XFrame.class, object );
mFrame.initialize(window);
mFrame.setName( mFrame.toString() );
mDesktop = mServiceFactory.createInstance( "com.sun.star.frame.Desktop");
XFrames xFrames = ( (XFramesSupplier)UnoRuntime.queryInterface(
XFramesSupplier.class, mDesktop ) ).getFrames();
xFrames.append( mFrame );
// Initializes the slot command execution environment.
object = mServiceFactory.createInstance( "com.sun.star.util.URLTransformer");
mURLTransformer = (XURLTransformer)UnoRuntime.queryInterface(
XURLTransformer.class, object);
object = mServiceFactory.createInstance(
"com.sun.star.document.TypeDetection");
mTypeDetector = (XTypeDetection)UnoRuntime.queryInterface(
XTypeDetection.class, object);
mDispatcher = (XDispatchProvider)UnoRuntime.queryInterface(
XDispatchProvider.class, mFrame);
}
/**
* Frees up resources.
*/
protected void finalize()
throws Throwable
{
super.finalize();
if (mConnection != null)
mConnection.dispose();
}
/**
* Default appearance of the bean.
*/
private class PlaceHolder
extends Canvas
{
public void paint(Graphics g)
{
g.setColor(Color.white);
Rectangle rect = getBounds();
g.fillRect(rect.x, rect.y, rect.width, rect.height);
g.setColor(new Color(0x666699));
rect.setBounds(rect.x + 1, rect.y + 1,
rect.width - 3, rect.height - 3);
g.drawRect(rect.x, rect.y, rect.width, rect.height);
rect.setBounds(rect.x + 2, rect.y + 2,
rect.width - 4, rect.height - 4);
g.drawRect(rect.x, rect.y, rect.width, rect.height);
g.drawString(BasicOfficeBean.this.getClass().getName(),
rect.x + 10, rect.y + 15);
}
}
}