/*************************************************************************
 *
 *  $RCSfile: OfficeWriter.java,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hr $ $Date: 2003/06/30 15:30:33 $
 *
 *  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.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.util.Vector;

import com.sun.star.uno.XInterface;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.container.XIndexAccess;
import com.sun.star.text.XText;
import com.sun.star.text.XTextDocument;
import com.sun.star.text.XTextRange;
import com.sun.star.text.XTextCursor;
import com.sun.star.frame.XFrame;
import com.sun.star.frame.XModel;
import com.sun.star.view.XSelectionSupplier;
import com.sun.star.view.XSelectionChangeListener;

/** Implementation of the Office Writer Component as a Java Bean.
 *
 * The OfficeWriter provides methods for getting and setting the
 * contents of an embedded Office Writer document and also for adding a
 * listener for changes in the current selection in the document.
 */

public class OfficeWriter extends Office
{
	public static final String NEW_DOCUMENT = "private:factory/swriter";

	private transient XSelectionChangeListener mXSelectionChangeListener = null;
	private transient EventListenerList mSelectionChangeListenerList = null;

	/** Method to get the complete text of the current OfficeWriter document.
	 *
	 * @return the complete text of the current OfficeWriter document
	 */
	public String getString()
	{
		XText xText = getXText();
		String sText = "";

		if (xText != null) {
			XTextCursor xTextCursor = xText.createTextCursor();
			xTextCursor.gotoStart(false);
			xTextCursor.gotoEnd(true);
			sText = xTextCursor.getString();
		}
		return sText;        
	}

	/** Method to replace the complete text of the OfficeWriter document.
	 *
	 * @param s the new text
	 */
	public void setString(String s)
	{
		XText xText = getXText();

		if (xText != null) {
			XTextCursor xTextCursor = xText.createTextCursor();
			xTextCursor.gotoStart(false);
			xTextCursor.gotoEnd(true);
			xTextCursor.setString(s);
		}
	}

	/** Method to get the XTextDocument interface.
	*
	* @return the XTextDocument interface of the current document
	*
	*/    
	public XTextDocument getTextDocument()
	{
		XTextDocument xTextDocument = null;
		XModel xModel = null;

		if ((getFrame() != null) && (getFrame().getController() != null))
			xModel = getFrame().getController().getModel();

		if (xModel != null) {
			xTextDocument =  (XTextDocument) UnoRuntime.queryInterface(
				XTextDocument.class, xModel);
		}
		return xTextDocument;        
	}

	/** Method which gets the text currently selected in the Writer document
	 *
	 * @return the currently selected text as a String 
	 */
	public String getSelection()
	{
		XTextRange xTextRange = null;
		String s = "";

		try {
			xTextRange = queryCurrentPosition();
			if (xTextRange != null)
				s = xTextRange.getString();
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		return s;
	}

	/** Adds a ChangeListener, event is fired when the text selection changes
	 *
	 * @param l ChangeListener to be added
	 */    
	public void addSelectionChangeListener(ChangeListener l)
	{
		if (mSelectionChangeListenerList == null)
			mSelectionChangeListenerList = new EventListenerList();

		mSelectionChangeListenerList.add(ChangeListener.class , l);

		if (mXSelectionChangeListener == null)
			postInitListenersEvent();
	}

	/** Removes a previously added ChangeListener
	 *
	 * @param l ChangeListener to be removed
	 */    
	public void removeSelectionChangeListener(ChangeListener l)
	{
		if (mSelectionChangeListenerList == null)
			return;

		mSelectionChangeListenerList.remove(ChangeListener.class, l);

		if (mSelectionChangeListenerList.getListenerCount() == 0)
		{
			removeXSelectionChangeListener();
			mSelectionChangeListenerList = 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
	{
		// remove XSelectionChangeListener for previous document
		removeXSelectionChangeListener();

		// now call parent load method
		super.load(url);

		// and queue a call to add a XSelectionChangeListener to the new document
		postInitListenersEvent();
	}

	/** Bean is always focusTraversable
	 *
	 * @return boolean, always true as the bean is focusTraversable
	 */    
	public boolean isFocusTraversable()
	{
		return true;
	}

	/* Get the XText interface of the current document */
	private XText getXText()
	{
		XTextDocument xTextDocument = getTextDocument();
		XText xText = null;

		if (xTextDocument != null) {
			xText = (XText) xTextDocument.getText();
		}
		return xText;
	}

	/** Returns the first selected TextRange in the TextDocument,
	 * or the cursor position if no text position is selected.
	 *
	 * @return current cursor position
	 * @throws Exception element could not be retrieved
	 */
	private XTextRange queryCurrentPosition()
		throws java.lang.Exception
	{
		XTextRange xTextRange = null;

		try {
			XIndexAccess xIndexAccess = getSelectionIndex();
			if(xIndexAccess != null) {
				XInterface xInterfaceText = (XInterface)UnoRuntime.queryInterface(
                    XInterface.class, xIndexAccess.getByIndex((int)0));
				xTextRange = (XTextRange)UnoRuntime.queryInterface(
					XTextRange.class, xInterfaceText);
			}
		}
		catch( java.lang.Exception e ) {
			e.printStackTrace(System.err);
		}
		return xTextRange;
	}

	/** Returns a container of all selected text positions in the document,
	 * at least the current cursor position
	 *
	 * @return Indexed Access to container
	 * @throws Exception No SelectionSupplier available
	 */    
	private XIndexAccess getSelectionIndex()
		throws java.lang.Exception
	{
		XIndexAccess xIndexAccess = null;
		XSelectionSupplier xSelectionSupplier = getSelectionSupplier();

		if (xSelectionSupplier == null)
			throw new Exception("queryCurrentPosition: xSelectionSupplier == null");

		try {
			XInterface xInterface = (XInterface) UnoRuntime.queryInterface(
                XInterface.class,xSelectionSupplier.getSelection());
			xIndexAccess = (XIndexAccess)UnoRuntime.queryInterface(
				XIndexAccess.class,xInterface);
		}
		catch( java.lang.Exception e ) {
			e.printStackTrace(System.err);
		}
		return xIndexAccess;
	}

	/** Fires StateChanged Event to Listeners
	 *
	 * @param event StateChanged event
	 */    
	private void fireStateChangedEvent(EventListenerList list, ChangeEvent event)
	{
		Object listenerList[] = list.getListenerList();

		for (int i = listenerList.length - 2; i >= 0; i -= 2) {
			if (listenerList[i] == ChangeListener.class) {
				((ChangeListener) listenerList[i+1]).stateChanged(event);
			}
		}
	}

	private void postInitListenersEvent()
	{
		EventQueue eq = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue();
		eq.postEvent(new AddSelectionChangeEvent(this, AWTEvent.RESERVED_ID_MAX+1));
	}

	/** Creates an XSelectionChangeListener and adds it to the active document
	 */
	private void initListeners()
	{
		XSelectionSupplier xSelectionSupplier = null;

		if (getFrame() == null) return;

		if (mXSelectionChangeListener == null) {
			try {
				mXSelectionChangeListener = new CSelectionChangeListener();
				getSelectionSupplier().addSelectionChangeListener(
					mXSelectionChangeListener);
			}
			catch (Exception e) {
				mXSelectionChangeListener = null;
				e.printStackTrace(System.err);
			}
		}
	}

	private void removeXSelectionChangeListener()
	{
		if (mXSelectionChangeListener != null) {
			try {
				getSelectionSupplier().removeSelectionChangeListener(
					mXSelectionChangeListener);
				mXSelectionChangeListener.disposing(null);
				mXSelectionChangeListener = null;
			}
			catch (java.lang.Exception e) {
				e.printStackTrace(System.err);
			}            
		}              
	}

	/**
	 * Closes the connection.
	 */
	public synchronized void closeConnection()
	{
		removeXSelectionChangeListener();
		super.closeConnection();
	}

	/** Inner Class implements java.awt.ActiveEvent
	 * creates API SelectionChangeListener
	 */    
	private class AddSelectionChangeEvent extends AWTEvent implements ActiveEvent
	{
		/** Constructor of inner class
		 * @param source event source
		 * @param id event id
		 */        
		public AddSelectionChangeEvent(Object source, int id)
		{
			super (source, id);
		}

		/** Creates a Listener */        
		public void dispatch ()
		{
			initListeners();
		}
	}

	private class CSelectionChangeListener implements XSelectionChangeListener
	{
		/** Listener is about to be destroyed
		 * @param aEvent Event
 		 */    
		public void disposing(com.sun.star.lang.EventObject o)
		{
		}

		/** SelectionChanged
		 * @param aEvent Event
		 */    
		public void selectionChanged(com.sun.star.lang.EventObject o)
		{
			fireStateChangedEvent(mSelectionChangeListenerList, new ChangeEvent(o));
		}
	}
}
