/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



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 bound 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
	}
}
