/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sd.hxx"
#include <com/sun/star/drawing/XShapes.hpp>
#include <com/sun/star/presentation/ShapeAnimationSubType.hpp>
#include <com/sun/star/presentation/EffectNodeType.hpp>
#include <com/sun/star/presentation/ParagraphTarget.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/animations/XIterateContainer.hpp>
#include <com/sun/star/presentation/EffectPresetClass.hpp>
#include <com/sun/star/presentation/EffectCommands.hpp>
#include <com/sun/star/text/XTextRange.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/drawing/XDrawPage.hpp>
#include <svx/ShapeTypeHandler.hxx>
#include "CustomAnimationList.hxx"
#ifndef _SD_CUSTOMANIMATIONPANE_HRC
#include "CustomAnimationPane.hrc"
#endif
#ifndef _SD_CUSTOMANIMATION_HRC
#include "CustomAnimation.hrc"
#endif
#include "CustomAnimationPreset.hxx"
#include <svtools/treelist.hxx>
#include <vcl/svapp.hxx>
#include "sdresid.hxx"

#include "res_bmp.hrc"
#include "glob.hrc"

#include <algorithm>

using namespace ::com::sun::star;
using namespace ::com::sun::star::animations;
using namespace ::com::sun::star::presentation;

using ::rtl::OUString;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::uno::XInterface;
using ::com::sun::star::text::XTextRange;
using ::com::sun::star::drawing::XShape;
using ::com::sun::star::drawing::XShapes;
using ::com::sun::star::drawing::XDrawPage;
using ::com::sun::star::container::XChild;
using ::com::sun::star::container::XIndexAccess;
using ::com::sun::star::container::XEnumerationAccess;
using ::com::sun::star::container::XEnumeration;
using ::com::sun::star::beans::XPropertySet;
using ::com::sun::star::beans::XPropertySetInfo;
using ::accessibility::ShapeTypeHandler;

namespace sd {

// go recursivly through all shapes in the given XShapes collection and return true as soon as the
// given shape is found. nIndex is incremented for each shape with the same shape type as the given
// shape is found until the given shape is found.
static bool getShapeIndex(  const Reference< XShapes >& xShapes, const Reference< XShape >& xShape, sal_Int32& nIndex )
{
	const sal_Int32 nCount = xShapes->getCount();
	sal_Int32 n;
	for( n = 0; n < nCount; n++ )
	{
		Reference< XShape > xChild;
		xShapes->getByIndex( n ) >>= xChild;
		if( xChild == xShape )
			return true;

		if( xChild->getShapeType() == xShape->getShapeType() )
			nIndex++;

		Reference< XShapes > xChildContainer( xChild, UNO_QUERY );
		if( xChildContainer.is() )
		{
			if( getShapeIndex( xChildContainer, xShape, nIndex ) )
				return true;
		}
	}

	return false;
}

// returns the index of the shape type from the given shape
static sal_Int32 getShapeIndex( const Reference< XShape >& xShape )
{
	Reference< XChild > xChild( xShape, UNO_QUERY );
	Reference< XShapes > xPage;

	while( xChild.is() && !xPage.is() )
	{
		Reference< XInterface > x( xChild->getParent() );
		xChild = Reference< XChild >::query( x );
		Reference< XDrawPage > xTestPage( x, UNO_QUERY );
		if( xTestPage.is() )
			xPage = Reference< XShapes >::query( x );
	}

	sal_Int32 nIndex = 1;

	if( xPage.is() && getShapeIndex( xPage, xShape, nIndex ) )
		return nIndex;
	else
		return -1;
}

OUString getShapeDescription( const Reference< XShape >& xShape, bool bWithText = true )
{
	OUString aDescription;
	Reference< XPropertySet > xSet( xShape, UNO_QUERY );
	if( xSet.is() )
	{
		Reference< XPropertySetInfo > xInfo( xSet->getPropertySetInfo() );
		const OUString aPropName( RTL_CONSTASCII_USTRINGPARAM("UINameSingular") );
		if( xInfo->hasPropertyByName( aPropName ) )
			xSet->getPropertyValue( aPropName ) >>= aDescription;
	}

	aDescription += OUString( RTL_CONSTASCII_USTRINGPARAM(" "));
	aDescription += OUString::valueOf( getShapeIndex( xShape ) );

	if( bWithText )
	{
		Reference< XTextRange > xText( xShape, UNO_QUERY );
		if( xText.is() )
		{
			OUString aText( xText->getString() );
			if( aText.getLength() )
			{
				aDescription += OUString(RTL_CONSTASCII_USTRINGPARAM(": "));

				aText = aText.replace( (sal_Unicode)'\n', (sal_Unicode)' ' );
				aText = aText.replace( (sal_Unicode)'\r', (sal_Unicode)' ' );

				aDescription += aText;
			}
		}
	}
	return aDescription;
}

static OUString getDescription( const Any& rTarget, bool bWithText = true )
{
	OUString aDescription;

	if( rTarget.getValueType() == ::getCppuType((const ParagraphTarget*)0) )
	{
		ParagraphTarget aParaTarget;
		rTarget >>= aParaTarget;

		Reference< XEnumerationAccess > xText( aParaTarget.Shape, UNO_QUERY_THROW );
		Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_QUERY_THROW );
		sal_Int32 nPara = aParaTarget.Paragraph;

		while( xEnumeration->hasMoreElements() && nPara )
		{
			xEnumeration->nextElement();
			nPara--;
		}

		DBG_ASSERT( xEnumeration->hasMoreElements(), "sd::CustomAnimationEffect::prepareText(), paragraph out of range!" );

		if( xEnumeration->hasMoreElements() )
		{
			Reference< XTextRange > xParagraph;
			xEnumeration->nextElement() >>= xParagraph;
								
			if( xParagraph.is() )
				aDescription = xParagraph->getString();
		}
	}
	else
	{
		Reference< XShape > xShape;
		rTarget >>= xShape;
		if( xShape.is() )
			aDescription = getShapeDescription( xShape, bWithText );
	}

	return aDescription;
}

// ====================================================================

class CustomAnimationListEntryItem : public SvLBoxString
{
public:
					CustomAnimationListEntryItem( SvLBoxEntry*,sal_uInt16 nFlags, OUString aDescription, CustomAnimationEffectPtr pEffect, CustomAnimationList* pParent  );
	virtual			~CustomAnimationListEntryItem();
	void			InitViewData( SvLBox*,SvLBoxEntry*,SvViewDataItem* );
	void			Paint( const Point&, SvLBox& rDev, sal_uInt16 nFlags,SvLBoxEntry* );
	SvLBoxItem* 	Create() const;
	void 			Clone( SvLBoxItem* pSource );

private:
	CustomAnimationList* mpParent;
	OUString		maDescription;
	CustomAnimationEffectPtr mpEffect;
};

// --------------------------------------------------------------------

CustomAnimationListEntryItem::CustomAnimationListEntryItem( SvLBoxEntry* pEntry, sal_uInt16 nFlags, OUString aDescription, CustomAnimationEffectPtr pEffect, CustomAnimationList* pParent  )
: SvLBoxString( pEntry, nFlags, aDescription )
, mpParent( pParent )
, maDescription( aDescription )
, mpEffect(pEffect)
{
}

// --------------------------------------------------------------------

CustomAnimationListEntryItem::~CustomAnimationListEntryItem()
{
}

// --------------------------------------------------------------------

void CustomAnimationListEntryItem::InitViewData( SvLBox* pView, SvLBoxEntry* pEntry, SvViewDataItem* pViewData )
{
	if( !pViewData )
		pViewData = pView->GetViewDataItem( pEntry, this );

	Size aSize(pView->GetTextWidth( maDescription ) + 2 * 19, pView->GetTextHeight() );
	if( aSize.Height() < 19 )
		aSize.Height() = 19;
	pViewData->aSize = aSize;
}

// --------------------------------------------------------------------

void CustomAnimationListEntryItem::Paint( const Point& rPos, SvLBox& rDev, sal_uInt16, SvLBoxEntry* pEntry )
{
	const bool bHighContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode();

	SvViewDataItem* pViewData = mpParent->GetViewDataItem( pEntry, this );

	Point aPos( rPos );
	Size aSize( pViewData->aSize );

	sal_Int16 nNodeType = mpEffect->getNodeType();
	if( nNodeType == EffectNodeType::ON_CLICK )
	{
		rDev.DrawImage( aPos, mpParent->getImage( IMG_CUSTOMANIMATION_ON_CLICK, bHighContrast ) );
	}
	else if( nNodeType == EffectNodeType::AFTER_PREVIOUS )
	{
		rDev.DrawImage( aPos, mpParent->getImage( IMG_CUSTOMANIMATION_AFTER_PREVIOUS, bHighContrast ) );
	}

	aPos.X() += 19;

	sal_uInt16 nImage;
	switch( mpEffect->getPresetClass() )
	{
	case EffectPresetClass::ENTRANCE:	nImage = IMG_CUSTOMANIMATION_ENTRANCE_EFFECT; break;
	case EffectPresetClass::EXIT:		nImage =  IMG_CUSTOMANIMATION_EXIT_EFFECT; break;
	case EffectPresetClass::EMPHASIS:	nImage =  IMG_CUSTOMANIMATION_EMPHASIS_EFFECT; break;
	case EffectPresetClass::MOTIONPATH: nImage = IMG_CUSTOMANIMATION_MOTION_PATH; break;
	case EffectPresetClass::OLEACTION:	nImage = IMG_CUSTOMANIMATION_OLE; break;
	case EffectPresetClass::MEDIACALL:
		switch( mpEffect->getCommand() )
		{
		case EffectCommands::TOGGLEPAUSE:	nImage = IMG_CUSTOMANIMATION_MEDIA_PAUSE; break;
		case EffectCommands::STOP:			nImage = IMG_CUSTOMANIMATION_MEDIA_STOP; break;
		case EffectCommands::PLAY:			
		default:							nImage = IMG_CUSTOMANIMATION_MEDIA_PLAY; break;
		}
		break;
	default:							nImage = 0xffff;
	}

	if( nImage != 0xffff )
	{
		const Image& rImage = mpParent->getImage( nImage, bHighContrast );
		Point aImagePos( aPos );
		aImagePos.Y() += ( aSize.Height() - rImage.GetSizePixel().Height() ) >> 1;
		rDev.DrawImage( aImagePos, rImage );
	}

	aPos.X() += 19;
	aPos.Y() += ( aSize.Height() - rDev.GetTextHeight()) >> 1;

    rDev.DrawText( aPos, rDev.GetEllipsisString( maDescription, rDev.GetOutputSizePixel().Width() - aPos.X() ) );
}

// --------------------------------------------------------------------

SvLBoxItem* CustomAnimationListEntryItem::Create() const
{
	return NULL;
}

// --------------------------------------------------------------------

void CustomAnimationListEntryItem::Clone( SvLBoxItem* )
{
}

// ====================================================================

class CustomAnimationListEntry : public SvLBoxEntry
{
public:
	CustomAnimationListEntry();
	CustomAnimationListEntry( CustomAnimationEffectPtr pEffect );
	virtual ~CustomAnimationListEntry();

	CustomAnimationEffectPtr getEffect() const { return mpEffect; }

private:
	CustomAnimationEffectPtr mpEffect;
};

// --------------------------------------------------------------------

CustomAnimationListEntry::CustomAnimationListEntry()
{
}

// --------------------------------------------------------------------

CustomAnimationListEntry::CustomAnimationListEntry( CustomAnimationEffectPtr pEffect )
: mpEffect( pEffect )
{
}

// --------------------------------------------------------------------

CustomAnimationListEntry::~CustomAnimationListEntry()
{
}

// ====================================================================

class CustomAnimationTriggerEntryItem : public SvLBoxString
{
public:
					CustomAnimationTriggerEntryItem( SvLBoxEntry*,sal_uInt16 nFlags, OUString aDescription, CustomAnimationList* pParent  );
	virtual			~CustomAnimationTriggerEntryItem();
	virtual sal_uInt16	IsA();
	void			InitViewData( SvLBox*,SvLBoxEntry*,SvViewDataItem* );
	void			Paint( const Point&, SvLBox& rDev, sal_uInt16 nFlags,SvLBoxEntry* );
	SvLBoxItem* 	Create() const;
	void 			Clone( SvLBoxItem* pSource );

private:
	CustomAnimationList* mpParent;
	OUString		maDescription;
};

// --------------------------------------------------------------------

CustomAnimationTriggerEntryItem::CustomAnimationTriggerEntryItem( SvLBoxEntry* pEntry, sal_uInt16 nFlags, OUString aDescription, CustomAnimationList* pParent  )
: SvLBoxString( pEntry, nFlags, aDescription ), mpParent( pParent ), maDescription( aDescription )
{
}

// --------------------------------------------------------------------

CustomAnimationTriggerEntryItem::~CustomAnimationTriggerEntryItem()
{
}

// --------------------------------------------------------------------

sal_uInt16 CustomAnimationTriggerEntryItem::IsA()
{
	return (sal_uInt16)-1;
}

// --------------------------------------------------------------------

void CustomAnimationTriggerEntryItem::InitViewData( SvLBox* pView, SvLBoxEntry* pEntry, SvViewDataItem* pViewData )
{
	if( !pViewData )
		pViewData = pView->GetViewDataItem( pEntry, this );

	Size aSize(pView->GetTextWidth( maDescription ) + 2 * 19, pView->GetTextHeight() );
	if( aSize.Height() < 19 )
		aSize.Height() = 19;
	pViewData->aSize = aSize;

/*
		SvViewData* pViewData = pView->GetViewData( pEntry );
		if( pViewData )
			pViewData->SetSelectable(false);
*/
}

// --------------------------------------------------------------------

void CustomAnimationTriggerEntryItem::Paint( const Point& rPos, SvLBox& rDev, sal_uInt16, SvLBoxEntry* )
{
	Size aSize( rDev.GetOutputSizePixel().Width(), static_cast< SvTreeListBox* >(&rDev)->GetEntryHeight() );

	Point aPos( 0, rPos.Y() );

	Rectangle aOutRect( aPos, aSize );

	// fill the background
    Color aColor (rDev.GetSettings().GetStyleSettings().GetDialogColor());

	rDev.Push();
    rDev.SetFillColor (aColor);
    rDev.SetLineColor ();
    rDev.DrawRect(aOutRect);

    // Erase the four corner pixels to make the rectangle appear rounded.
    rDev.SetLineColor( rDev.GetSettings().GetStyleSettings().GetWindowColor());
    rDev.DrawPixel( aOutRect.TopLeft());
    rDev.DrawPixel( Point(aOutRect.Right(), aOutRect.Top()));
    rDev.DrawPixel( Point(aOutRect.Left(), aOutRect.Bottom()));
    rDev.DrawPixel( Point(aOutRect.Right(), aOutRect.Bottom()));

	// draw the category title

	int nVertBorder = (( aSize.Height() - rDev.GetTextHeight()) >> 1);
	int nHorzBorder = rDev.LogicToPixel( Size( 3, 3 ), MAP_APPFONT ).Width();

	aOutRect.nLeft += nHorzBorder;
	aOutRect.nRight -= nHorzBorder;
	aOutRect.nTop += nVertBorder;
	aOutRect.nBottom -= nVertBorder;

	rDev.DrawText (aOutRect, rDev.GetEllipsisString( maDescription, aOutRect.GetWidth() ) );
	rDev.Pop();
}

// --------------------------------------------------------------------

SvLBoxItem* CustomAnimationTriggerEntryItem::Create() const
{
	return NULL;
}

// --------------------------------------------------------------------

void CustomAnimationTriggerEntryItem::Clone( SvLBoxItem* )
{
}

// ====================================================================

CustomAnimationList::CustomAnimationList( ::Window* pParent, const ResId& rResId, ICustomAnimationListController* pController )
:	SvTreeListBox( pParent, rResId )
,	mbIgnorePaint( false )
,	mpController( pController )
,	mpLastParentEntry(0)
{
	SetStyle( GetStyle() | WB_TABSTOP | WB_BORDER | WB_HASLINES | WB_HASBUTTONS | WB_HASBUTTONSATROOT );

	EnableContextMenuHandling();
	SetSelectionMode( MULTIPLE_SELECTION );
	SetIndent(16);
	SetNodeDefaultImages();
}

// --------------------------------------------------------------------

const Image&  CustomAnimationList::getImage( sal_uInt16 nId, bool bHighContrast )
{
	DBG_ASSERT( (nId >= IMG_CUSTOMANIMATION_ON_CLICK) && (nId <= IMG_CUSTOMANIMATION_MEDIA_STOP), "sd::CustomAnimationList::getImage(), illegal index!" ); 

	if( bHighContrast )
		nId += 1;

	Image& rImage = maImages[nId - IMG_CUSTOMANIMATION_ON_CLICK];

	// load on demand
	if( rImage.GetSizePixel().Width() == 0 )
		rImage = Image(SdResId( nId ) );

	return rImage;
}

// --------------------------------------------------------------------

CustomAnimationList::~CustomAnimationList()
{
	if( mpMainSequence.get() )
		mpMainSequence->removeListener( this );

	clear();
}

// --------------------------------------------------------------------

void CustomAnimationList::KeyInput( const KeyEvent& rKEvt )
{
	const int nKeyCode = rKEvt.GetKeyCode().GetCode();
	switch( nKeyCode )
	{
		case KEY_DELETE:	mpController->onContextMenu( CM_REMOVE ); return;
		case KEY_INSERT:	mpController->onContextMenu( CM_CREATE ); return;
		case KEY_SPACE:		
			{
				const Point aPos;
				const CommandEvent aCEvt( aPos, COMMAND_CONTEXTMENU ); 
				Command( aCEvt );
				return;
			}
			
	}

	::SvTreeListBox::KeyInput( rKEvt );
}

// --------------------------------------------------------------------

/** selects or deselects the given effect.
	Selections of other effects are not changed */
void CustomAnimationList::select( CustomAnimationEffectPtr pEffect, bool bSelect /* = true */ )
{
	CustomAnimationListEntry* pEntry = static_cast< CustomAnimationListEntry* >(First());
	while( pEntry )
	{
		if( pEntry->getEffect() == pEffect )
		{	
			Select( pEntry, bSelect );
			break;
		}
		pEntry = static_cast< CustomAnimationListEntry* >(Next( pEntry ));
	}

	if( !pEntry && bSelect )
	{
		append( pEffect );
		select( pEffect );
	}
}

// --------------------------------------------------------------------

void CustomAnimationList::clear()
{
	Clear();

	mpLastParentEntry = 0;
	mxLastTargetShape = 0;
}

// --------------------------------------------------------------------

void CustomAnimationList::update( MainSequencePtr pMainSequence )
{
	if( mpMainSequence.get() )
		mpMainSequence->removeListener( this );

	mpMainSequence = pMainSequence;
	update();

	if( mpMainSequence.get() )
		mpMainSequence->addListener( this );
}

// --------------------------------------------------------------------

struct stl_append_effect_func : public std::unary_function<CustomAnimationEffectPtr, void>
{
	stl_append_effect_func( CustomAnimationList& rList ) : mrList( rList ) {}
	void operator()(CustomAnimationEffectPtr pEffect);
	CustomAnimationList& mrList;
};

void stl_append_effect_func::operator()(CustomAnimationEffectPtr pEffect)
{
	mrList.append( pEffect );
}
// --------------------------------------------------------------------

void CustomAnimationList::update()
{
	mbIgnorePaint = true;

	CustomAnimationListEntry* pEntry = 0;

	std::list< CustomAnimationEffectPtr > aExpanded;
	std::list< CustomAnimationEffectPtr > aSelected;

	CustomAnimationEffectPtr pFirstVisibleEffect;

	if( mpMainSequence.get() )
	{
		// save selection and expand states
		pEntry = static_cast<CustomAnimationListEntry*>(FirstVisible());
		if( pEntry )
			pFirstVisibleEffect = pEntry->getEffect();

		pEntry = static_cast<CustomAnimationListEntry*>(First());

		while( pEntry )
		{
			CustomAnimationEffectPtr pEffect( pEntry->getEffect() );
			if( pEffect.get() )
			{
				if( IsExpanded( pEntry ) )
					aExpanded.push_back( pEffect );

				if( IsSelected( pEntry ) )
					aSelected.push_back( pEffect );
			}

			pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry ));
		}
	}

	// rebuild list
	clear();
	if( mpMainSequence.get() )
	{
		std::for_each( mpMainSequence->getBegin(), mpMainSequence->getEnd(), stl_append_effect_func( *this ) );
		mpLastParentEntry = 0;

		const InteractiveSequenceList& rISL = mpMainSequence->getInteractiveSequenceList();

		InteractiveSequenceList::const_iterator aIter( rISL.begin() );
		const InteractiveSequenceList::const_iterator aEnd( rISL.end() );
		while( aIter != aEnd )
		{
			InteractiveSequencePtr pIS( (*aIter++) );

			Reference< XShape > xShape( pIS->getTriggerShape() );
			if( xShape.is() )
			{
				SvLBoxEntry* pLBoxEntry = new CustomAnimationListEntry;
				pLBoxEntry->AddItem( new SvLBoxContextBmp( pLBoxEntry, 0, Image(), Image(), 0));
				OUString aDescription = String( SdResId( STR_CUSTOMANIMATION_TRIGGER ) );
				aDescription += OUString( RTL_CONSTASCII_USTRINGPARAM(": ") );
				aDescription += getShapeDescription( xShape, false );
				pLBoxEntry->AddItem( new CustomAnimationTriggerEntryItem( pLBoxEntry, 0, aDescription, this ) );
				Insert( pLBoxEntry );
				SvViewData* pViewData = GetViewData( pLBoxEntry );
				if( pViewData )
					pViewData->SetSelectable(false);

				std::for_each( pIS->getBegin(), pIS->getEnd(), stl_append_effect_func( *this ) );
				mpLastParentEntry = 0;
			}
		}

		// restore selection and expand states
		pEntry = static_cast<CustomAnimationListEntry*>(First());

		while( pEntry )
		{
			CustomAnimationEffectPtr pEffect( pEntry->getEffect() );
			if( pEffect.get() )
			{
				if( std::find( aExpanded.begin(), aExpanded.end(), pEffect ) != aExpanded.end() )
					Expand( pEntry );

				if( std::find( aSelected.begin(), aSelected.end(), pEffect ) != aSelected.end() )
					Select( pEntry );

				if( pFirstVisibleEffect == pEffect )
					MakeVisible( pEntry );
			}

			pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry ));
		}
	}

	mbIgnorePaint = false;
	Invalidate();
}

// --------------------------------------------------------------------

/*
void CustomAnimationList::update( CustomAnimationEffectPtr pEffect )
{
	SvLBoxEntry* pEntry = First();
	while( pEntry )
	{
		if( static_cast< CustomAnimationEffectPtr * >( pEntry->GetUserData() )->get() == pEffect.get() )
		{
			CustomAnimationPresetsPtr pPresets = mpController->getPresets();
			const CustomAnimationPresetPtr pPreset = pPresets->getEffectDescriptor( pEffect->getPresetId() );
			if( pPreset.get() )
				pEffect->setName( pPresets->getUINameForPresetId( pPreset->getPresetId() ) );
			else
				pEffect->setName( pEffect->getPresetId() );
			break;
		}
		pEntry = Next( pEntry );
	}

	Invalidate();
}
*/

// --------------------------------------------------------------------

void CustomAnimationList::append( CustomAnimationEffectPtr pEffect )
{
	// create a ui description
	OUString aDescription;

	Any aTarget( pEffect->getTarget() );
	if( aTarget.hasValue() ) try
	{
		aDescription = getDescription( aTarget, pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_BACKGROUND );

		SvLBoxEntry* pParentEntry = 0;

		Reference< XShape > xTargetShape( pEffect->getTargetShape() );
		sal_Int32 nGroupId = pEffect->getGroupId();

		// if this effect has the same target and group-id as the last root effect,
		// the last root effect is also this effects parent
		if( mpLastParentEntry && (nGroupId != -1) && (mxLastTargetShape == xTargetShape) && (mnLastGroupId == nGroupId) )
			pParentEntry = mpLastParentEntry;

		// create an entry for the effect
		SvLBoxEntry* pEntry = new CustomAnimationListEntry( pEffect );

		pEntry->AddItem( new SvLBoxContextBmp( pEntry, 0, Image(), Image(), 0));
		pEntry->AddItem( new CustomAnimationListEntryItem( pEntry, 0, aDescription, pEffect, this ) );

		if( pParentEntry )
		{
			// add a subentry
			Insert( pEntry, pParentEntry );
		}
		else
		{
			// add a root entry
			Insert( pEntry );

			// and the new root entry becomes the possible next group header
			mxLastTargetShape = xTargetShape;
			mnLastGroupId = nGroupId;
			mpLastParentEntry = pEntry;
		}
	}
	catch( Exception& e )
	{
		(void)e;
		DBG_ERROR("sd::CustomAnimationList::append(), exception catched!" );
	}
}

// --------------------------------------------------------------------

/*
void CustomAnimationList::remove( CustomAnimationEffectPtr pEffect )
{
	SvLBoxEntry* pEntry = First();
	while( pEntry )
	{
		if( static_cast< CustomAnimationEffectPtr * >( pEntry->GetUserData() )->get() == pEffect.get() )
		{
			GetModel()->Remove( pEntry );
			if( pEntry == mpLastParentEntry )
			{
				mpLastParentEntry = 0;
				mxLastTargetShape = 0;
			}
			break;
		}
		pEntry = Next( pEntry );
	}

	Invalidate();
}
*/

// --------------------------------------------------------------------

void selectShape( SvTreeListBox* pTreeList, Reference< XShape > xShape )
{
	CustomAnimationListEntry* pEntry = static_cast< CustomAnimationListEntry* >(pTreeList->First());
	while( pEntry )
	{
		CustomAnimationEffectPtr pEffect( pEntry->getEffect() );
		if( pEffect.get() )
		{
			if( pEffect->getTarget() == xShape )
				pTreeList->Select( pEntry );
		}

		pEntry = static_cast< CustomAnimationListEntry* >(pTreeList->Next( pEntry ));
	}
}

// --------------------------------------------------------------------

void CustomAnimationList::onSelectionChanged( Any aSelection )
{
	try
	{
		SelectAll(sal_False);

		if( aSelection.hasValue() )
		{
			Reference< XIndexAccess > xShapes( aSelection, UNO_QUERY );
			if( xShapes.is() )
			{
				sal_Int32 nCount = xShapes->getCount();
				sal_Int32 nIndex;
				for( nIndex = 0; nIndex < nCount; nIndex++ )
				{
					Reference< XShape > xShape( xShapes->getByIndex( nIndex ), UNO_QUERY );
					if( xShape.is() )
						selectShape( this, xShape );
				}
			}
			else
			{
				Reference< XShape > xShape( aSelection, UNO_QUERY );
				if( xShape.is() )
					selectShape( this, xShape );
			}
		}

		SelectHdl();
	}
	catch( Exception& )
	{
		DBG_ERROR( "sd::CustomAnimationList::onSelectionChanged(), Exception catched!" );
	}
}

// --------------------------------------------------------------------

void CustomAnimationList::SelectHdl()
{
	SvTreeListBox::SelectHdl();
	mpController->onSelect();
}

// --------------------------------------------------------------------

bool CustomAnimationList::isExpanded( const CustomAnimationEffectPtr& pEffect ) const
{
	CustomAnimationListEntry* pEntry = static_cast<CustomAnimationListEntry*>(First());

	while( pEntry )
	{
		if( pEntry->getEffect() == pEffect )
			break;

		pEntry = static_cast<CustomAnimationListEntry*>(Next( pEntry ));
	}

	if( pEntry )
		pEntry = static_cast<CustomAnimationListEntry*>(GetParent( pEntry ));

	return (pEntry == 0) || IsExpanded( pEntry );
}

// --------------------------------------------------------------------
EffectSequence CustomAnimationList::getSelection() const
{
	EffectSequence aSelection;

	CustomAnimationListEntry* pEntry = dynamic_cast< CustomAnimationListEntry* >(FirstSelected());
	while( pEntry )
	{
		CustomAnimationEffectPtr pEffect( pEntry->getEffect() );
		if( pEffect.get() )
			aSelection.push_back( pEffect );

		// if the selected effect is not expanded and has children
		// we say that the children are automaticly selected
		if( !IsExpanded( pEntry ) )
		{
			CustomAnimationListEntry* pChild = dynamic_cast< CustomAnimationListEntry* >( FirstChild( pEntry ) );
			while( pChild )
			{
				if( !IsSelected( pChild ) )
				{
					CustomAnimationEffectPtr pChildEffect( pChild->getEffect() );
					if( pChildEffect.get() )
						aSelection.push_back( pChildEffect );
				}

				pChild = dynamic_cast< CustomAnimationListEntry* >(  NextSibling( pChild ) );
			}
		}

		pEntry = static_cast< CustomAnimationListEntry* >(NextSelected( pEntry ));
	}

	return aSelection;
}

// --------------------------------------------------------------------

sal_Bool CustomAnimationList::DoubleClickHdl()
{
	mpController->onDoubleClick();
	return false;
}

// --------------------------------------------------------------------

PopupMenu* CustomAnimationList::CreateContextMenu()
{
	PopupMenu* pMenu = new PopupMenu(SdResId( RID_EFFECT_CONTEXTMENU ));

	sal_Int16 nNodeType = -1;
	sal_Int16 nEntries = 0;

	CustomAnimationListEntry* pEntry = static_cast< CustomAnimationListEntry* >(FirstSelected());
	while( pEntry )
	{
		nEntries++;
		CustomAnimationEffectPtr pEffect( pEntry->getEffect() );
		if( pEffect.get() )
		{
			if( nNodeType == -1 )
			{
				nNodeType = pEffect->getNodeType();
			}
			else
			{
				if( nNodeType != pEffect->getNodeType() )
				{
					nNodeType = -1;
					break;
				}
			}
		}

		pEntry = static_cast< CustomAnimationListEntry* >(NextSelected( pEntry ));
	}

	pMenu->CheckItem( CM_WITH_CLICK, nNodeType == EffectNodeType::ON_CLICK );
	pMenu->CheckItem( CM_WITH_PREVIOUS, nNodeType == EffectNodeType::WITH_PREVIOUS );
	pMenu->CheckItem( CM_AFTER_PREVIOUS, nNodeType == EffectNodeType::AFTER_PREVIOUS );
	pMenu->EnableItem( CM_OPTIONS, nEntries == 1 );
	pMenu->EnableItem( CM_DURATION, nEntries == 1 );

	return pMenu;
}

// --------------------------------------------------------------------

void CustomAnimationList::ExcecuteContextMenuAction( sal_uInt16 nSelectedPopupEntry )
{
	mpController->onContextMenu( nSelectedPopupEntry );
}

// --------------------------------------------------------------------

void CustomAnimationList::SetTabs()
{
	SvTreeListBox::SetTabs();
}

// --------------------------------------------------------------------

void CustomAnimationList::notify_change()
{
	update();
	mpController->onSelect();
}

void CustomAnimationList::Paint( const Rectangle& rRect )
{
	if( mbIgnorePaint )
		return;

	SvTreeListBox::Paint( rRect );

	// draw help text if list box is still empty
	if( First() == 0 )
	{
		Color aOldColor( GetTextColor() );
		SetTextColor( GetSettings().GetStyleSettings().GetDisableColor() );
		::Point aOffset( LogicToPixel( Point( 6, 6 ), MAP_APPFONT ) );

		Rectangle aRect( Point( 0,0 ), GetOutputSizePixel() );

		aRect.Left() += aOffset.X();
		aRect.Top() += aOffset.Y();
		aRect.Right() -= aOffset.X();
		aRect.Bottom() -= aOffset.Y();

		DrawText( aRect, String( SdResId( STR_CUSTOMANIMATION_LIST_HELPTEXT ) ),
			TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK | TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER );

		SetTextColor( aOldColor );
	}
}

}