/*************************************************************************
 *
 *  $RCSfile: ControlLock.java,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hr $ $Date: 2003/06/30 15:27:01 $
 *
 *  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 com.sun.star.uno.*;
import com.sun.star.lang.*;
import com.sun.star.container.*;
import com.sun.star.beans.*;
import com.sun.star.form.*;
import com.sun.star.util.*;
import com.sun.star.sdbc.*;


/**************************************************************************/
/** A helper class for recursively locking control models which are bound
	to a specific field
*/

class LockControlModels extends ComponentTreeTraversal
{
	private String	m_sDataField;
	private Boolean	m_aLockIt;
	private int		m_nLevel;	// nesting level relative to the form we started with

	/* ------------------------------------------------------------------ */
	public LockControlModels( String sDataField, boolean bLockIt )
	{
		m_sDataField = sDataField;
		m_aLockIt = new Boolean( bLockIt );
		m_nLevel = 0;
	}

	/* ------------------------------------------------------------------ */
	protected boolean shouldStepInto( XIndexContainer xContainer ) throws com.sun.star.uno.Exception
	{
		if ( !super.shouldStepInto( xContainer ) )
			return false;	// don't try to be more clever than our base class

		XForm xForm = (XForm)UnoRuntime.queryInterface( XForm.class, xContainer );
		if ( ( null != xForm ) && ( m_nLevel > 1 ) )
			// don't step into sub forms - we only handle the form we were originally
			// applied to
			return false;

		return true;
	}

	/* ------------------------------------------------------------------ */
	public void handle( Object aFormComponent ) throws com.sun.star.uno.Exception
	{
		// entering this nesting level
		++m_nLevel;

		// check if the component has a DataField property
		XPropertySet xCompProps = UNO.queryPropertySet( aFormComponent );
		XPropertySetInfo xPSI = null;
		if ( null != xCompProps )
			xPSI = xCompProps.getPropertySetInfo();

		if ( ( null != xPSI ) && xPSI.hasPropertyByName( "DataField" ) )
		{	// indeed it has ....
			String sDataField = (String)xCompProps.getPropertyValue( "DataField" );
			if ( sDataField.equals( m_sDataField ) )
			{	// we found a control model which is bount to what we're looking for
				xCompProps.setPropertyValue( "ReadOnly", m_aLockIt );
			}
		}

		// allow the super class to step down, if possible
		super.handle( aFormComponent );

		// leaving this nesting level
		--m_nLevel;
	}
};

/**************************************************************************/
/** a class which automatically handles control locking.
	<p>The class has to be bound to a form. Upon every movement of the form,
	all controls which are bound to a (to be specified) field are locked
	on existing and unlocked on new records.</p>
*/
class ControlLock implements XRowSetListener
{
	private	XPropertySet	m_xForm;
	private	String			m_sDataField;
	private boolean			m_bLockingEnabled;
	private boolean			m_bPreviousRoundLock;

	/* ------------------------------------------------------------------ */
	ControlLock( XPropertySet xForm, String sBoundDataField )
	{
		m_xForm = xForm;
		m_sDataField = sBoundDataField;
		m_bLockingEnabled = false;
		m_bPreviousRoundLock = false;
	}

	/* ------------------------------------------------------------------ */
	/** updates the locks on the affected controls
	*/
	protected void updateLocks( )
	{
		try
		{
			// first determine if we need to lock
			Boolean aIsNewRecord = (Boolean)m_xForm.getPropertyValue( "IsNew" );

			boolean bNeedLock = m_bLockingEnabled && !aIsNewRecord.booleanValue();

			if ( m_bPreviousRoundLock != bNeedLock )
			{
				LockControlModels aLocker = new LockControlModels( m_sDataField, bNeedLock );
				aLocker.handle( m_xForm );
				m_bPreviousRoundLock = bNeedLock;
			}

			// please note that we choose the expensive way here: We always loop through
			// _all_ control models belonging to the form. This clearly slows down the
			// whole process.
			// A better solution would be to cache the affected control models. Then we
			// could either rely on the fact that the model hierarchy is static, or we
			// could add ourself as container listener to the form.
		}
		catch(com.sun.star.uno.Exception e)
		{
			System.out.println(e);
			e.printStackTrace();
		}
	}

	/* ------------------------------------------------------------------ */
	/** enables the locking in general
		<p>If the control models are really locked depends on the current
		record of the form: on the insert row, controls are never locked.</p>
	*/
	public void enableLock( boolean bLock )
	{
		// remember this new setting
		m_bLockingEnabled = bLock;

		// add or remove ourself as listener to get notified of cursor moves
		XRowSet xRowSet = (XRowSet)UnoRuntime.queryInterface(
			XRowSet.class, m_xForm );
		if ( m_bLockingEnabled )
		{
			xRowSet.addRowSetListener( this );
		}
		else
		{
			xRowSet.removeRowSetListener( this );
		}

		// update the locks
		updateLocks();
	}

	/* ==================================================================
	   = UNO callbacks
	   ================================================================== */

	/* ------------------------------------------------------------------ */
	// XResetListener overridables
	/* ------------------------------------------------------------------ */
	public void cursorMoved( EventObject aEvent ) throws com.sun.star.uno.RuntimeException
	{
		updateLocks( );
	}

	/* ------------------------------------------------------------------ */
	public void rowChanged( EventObject aEvent ) throws com.sun.star.uno.RuntimeException
	{
		// not interested in
	}

	/* ------------------------------------------------------------------ */
	public void rowSetChanged( EventObject aEvent ) throws com.sun.star.uno.RuntimeException
	{
		// not interested in
	}

	/* ------------------------------------------------------------------ */
	// XEventListener overridables
	/* ------------------------------------------------------------------ */
	public void disposing( EventObject aEvent )
	{
		// not interested in
	}
}
