/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: sfx2_statcach.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: rt $ $Date: 2006/10/27 19:10:45 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifdef SOLARIS
// HACK: prevent conflict between STLPORT and Workshop headers on Solaris 8
#include <ctime>
#endif

#include <string> // HACK: prevent conflict between STLPORT and Workshop headers

#include <svtools/eitem.hxx>
#include <svtools/intitem.hxx>
#include <svtools/stritem.hxx>

#pragma hdrstop

#ifndef _COM_SUN_STAR_LANG_XTYPEPROVIDER_HPP_
#include <com/sun/star/lang/XTypeProvider.hpp>
#endif

#include "statcach.hxx"
#include "dispatch.hxx"
#include "unoctitm.hxx"
#include "msgpool.hxx"
#include "viewfrm.hxx"

#ifndef _LEGACYBINFILTERMGR_HXX
#include <legacysmgr/legacy_binfilters_smgr.hxx>	//STRIP002 
#endif

#ifndef _CPPUHELPER_TYPEPROVIDER_HXX_
#include <cppuhelper/typeprovider.hxx>
#endif

namespace binfilter {

using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;

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

/*N*/ DBG_NAME(SfxStateCache)
/*N*/ DBG_NAME(SfxStateCacheSetState)

/*?*/ SFX_IMPL_XINTERFACE_2( BindDispatch_Impl, OWeakObject, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )
/*?*/ SFX_IMPL_XTYPEPROVIDER_2( BindDispatch_Impl, ::com::sun::star::frame::XStatusListener, ::com::sun::star::lang::XEventListener )

//-----------------------------------------------------------------------------
/*N*/ BindDispatch_Impl::BindDispatch_Impl( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > & rDisp, const ::com::sun::star::util::URL& rURL, SfxStateCache *pStateCache )
/*N*/ 	: xDisp( rDisp )
/*N*/ 	, aURL( rURL )
/*N*/ 	, pCache( pStateCache )
/*N*/     , pSlot( pCache->aSlotServ.GetSlot() )
/*N*/ { 
/*N*/     if ( !pSlot )
/*N*/         pSlot = SFX_SLOTPOOL().GetSlot( pCache->GetId() );
/*N*/     DBG_ASSERT( pSlot, "Unknown slot!");
/*N*/     aStatus.IsEnabled = sal_True;
/*N*/ }

/*?*/ void SAL_CALL BindDispatch_Impl::disposing( const ::com::sun::star::lang::EventObject& rEvent ) throw( ::com::sun::star::uno::RuntimeException )
/*?*/ {DBG_BF_ASSERT(0, "STRIP"); //STRIP001 
/*?*/ }

/*N*/ void SAL_CALL  BindDispatch_Impl::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& rEvent ) throw( ::com::sun::star::uno::RuntimeException )
/*N*/ {
/*N*/ 	aStatus = rEvent;
/*N*/ 	if ( !pCache )
/*N*/ 		return;
/*N*/ 
/*N*/ 	::com::sun::star::uno::Reference< ::com::sun::star::frame::XStatusListener >  xRef( (::cppu::OWeakObject*)this, ::com::sun::star::uno::UNO_QUERY );
/*N*/ 	if ( aStatus.Requery )
/*N*/ 		pCache->Invalidate( sal_True );
/*N*/ 	else
/*N*/ 	{
/*N*/ 		pCache->Invalidate( sal_False );
/*N*/ 		if ( !aStatus.IsEnabled )
/*N*/ 			pCache->SetState_Impl( SFX_ITEM_DISABLED, NULL );
/*N*/         else if (aStatus.State.hasValue())
/*N*/ 		{
/*?*/             sal_uInt16 nId = pCache->GetId();
/*?*/ 			SfxItemState eState = SFX_ITEM_AVAILABLE;
/*?*/ 			::com::sun::star::uno::Any aAny = aStatus.State;
/*?*/ 
/*?*/ 			SfxPoolItem *pItem=NULL;
/*?*/ 			::com::sun::star::uno::Type pType =	aAny.getValueType();
/*?*/ 			if ( pType == ::getBooleanCppuType() )
/*?*/ 			{
/*?*/ 				sal_Bool bTemp ;
/*?*/ 				aAny >>= bTemp ;
/*?*/ 				pItem = new SfxBoolItem( nId, bTemp );
/*?*/ 			}
/*?*/ 			else if ( pType == ::getCppuType((const sal_uInt16*)0) )
/*?*/ 			{
/*?*/ 				sal_uInt16 nTemp ;
/*?*/ 				aAny >>= nTemp ;
/*?*/ 				pItem = new SfxUInt16Item( nId, nTemp );
/*?*/ 			}
/*?*/ 			else if ( pType == ::getCppuType((const sal_uInt32*)0) )
/*?*/ 			{
/*?*/ 				sal_uInt32 nTemp ;
/*?*/ 				aAny >>= nTemp ;
/*?*/ 				pItem = new SfxUInt32Item( nId, nTemp );
/*?*/ 			}
/*?*/ 			else if ( pType == ::getCppuType((const ::rtl::OUString*)0) )
/*?*/ 			{
/*?*/ 				::rtl::OUString sTemp ;
/*?*/ 				aAny >>= sTemp ;
/*?*/ 				pItem = new SfxStringItem( nId, sTemp );
/*?*/ 			}
/*?*/ 			else
/*?*/             {
/*?*/                 if ( pSlot )
/*?*/                     pItem = pSlot->GetType()->CreateItem();
/*?*/                 if ( pItem )
/*?*/                 {
/*?*/                     pItem->SetWhich( nId );
/*?*/                     pItem->PutValue( aAny );
/*?*/                 }
/*?*/                 else
/*?*/                     pItem = new SfxVoidItem( nId );
/*?*/             }
/*?*/ 
/*?*/             pCache->SetState_Impl( eState, pItem );
/*?*/             delete pItem;
/*N*/ 		}
/*N*/ 		else
/*N*/ 		{
/*N*/ 			// DONTCARE status
/*N*/ 			SfxVoidItem aVoid(0);
/*N*/ 			pCache->SetState_Impl( SFX_ITEM_UNKNOWN, &aVoid );
/*N*/ 		}
/*N*/ 	}
/*N*/ }

/*N*/ void BindDispatch_Impl::Release()
/*N*/ { 
/*N*/ 	if ( xDisp.is() )
/*N*/ 	{
/*N*/         xDisp->removeStatusListener( (::com::sun::star::frame::XStatusListener*) this, aURL );
/*N*/ 		xDisp = ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
/*N*/ 	}
/*N*/ 
/*N*/ 	pCache = NULL;
/*N*/ 	release();
/*N*/ }

/*?*/ const ::com::sun::star::frame::FeatureStateEvent& BindDispatch_Impl::GetStatus() const
/*?*/ {
/*?*/ 	return aStatus;
/*?*/ }

/*?*/ void BindDispatch_Impl::Dispatch( sal_Bool bForceSynchron )
/*?*/ {DBG_BF_ASSERT(0, "STRIP"); //STRIP001 
/*?*/ }

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

/*	Dieser Konstruktor fuer einen ungueltigen Cache, der sich also
	bei der ersten Anfrage zun"achst updated.
 */

/*N*/ SfxStateCache::SfxStateCache( sal_uInt16 nFuncId ):
/*N*/ 	nId(nFuncId),
/*N*/ 	pController(0),
/*N*/ 	pLastItem( 0 ),
/*N*/ 	eLastState( 0 ),
/*N*/ 	pDispatch( 0 )
/*N*/ {
/*N*/ 	DBG_MEMTEST();
/*N*/ 	DBG_CTOR(SfxStateCache,	0);
/*N*/ 	bCtrlDirty = sal_True;
/*N*/ 	bSlotDirty = sal_True;
/*N*/ 	bItemDirty = sal_True;
/*N*/ }

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

/*	Der Destruktor pr"uft per Assertion, ob noch Controller angemeldet
	sind.
 */

/*N*/ SfxStateCache::~SfxStateCache()
/*N*/ {
/*N*/ 	DBG_MEMTEST();
/*N*/ 	DBG_DTOR(SfxStateCache,	0);
/*N*/ 	DBG_ASSERT( pController == 0, "es sind noch Controller angemeldet" );
/*N*/ 	if ( !IsInvalidItem(pLastItem) )
/*N*/ 		delete pLastItem;
/*N*/ 	if ( pDispatch )
/*N*/ 	{
/*?*/ 		pDispatch->Release();
/*?*/ 		pDispatch = NULL;
/*N*/ 	}
/*N*/ }

//--------------------------------------------------------------------
// invalidates the cache (next request will force update)
/*N*/ void SfxStateCache::Invalidate( sal_Bool bWithMsg )
/*N*/ {
/*N*/ 	bCtrlDirty = sal_True;
/*N*/ 	if ( bWithMsg )
/*N*/ 	{
/*N*/ 		bSlotDirty = sal_True;
/*N*/ 		aSlotServ.SetSlot( 0 );
/*N*/ 		if ( pDispatch )
/*N*/ 		{
/*?*/ 			pDispatch->Release();
/*?*/ 			pDispatch = NULL;
/*N*/ 		}
/*N*/ 	}
/*N*/ }

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

// gets the corresponding function from the dispatcher or the cache

/*N*/ const SfxSlotServer* SfxStateCache::GetSlotServer( SfxDispatcher &rDispat , const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatchProvider > & xProv )
/*N*/ {
/*N*/ 	DBG_MEMTEST();
/*N*/ 	DBG_CHKTHIS(SfxStateCache, 0);
/*N*/ 
/*N*/ 
/*N*/ 	if ( bSlotDirty )
/*N*/ 	{
/*N*/         if ( xProv.is() )
/*N*/         {
/*N*/             DBG_ASSERT( !pDispatch, "Altes Dispatch nicht entfernt!" );
/*N*/ 
/*N*/             // get the slot - even if it is disabled on the dispatcher
/*N*/             const SfxSlot* pSlot = SFX_APP()->GetSlotPool( rDispat.GetFrame() ).GetSlot( nId );
/*N*/ 
/*N*/             // create the dispatch name from the slot data
/*N*/             ::com::sun::star::util::URL aURL;
/*N*/             String aName( pSlot && pSlot->pUnoName ? String::CreateFromAscii(pSlot->GetUnoName()) : String() );
/*N*/             String aCmd;
/*N*/             if ( aName.Len() )
/*N*/             {
/*N*/                 aCmd = DEFINE_CONST_UNICODE(".uno:");
/*N*/                 aCmd += aName;
/*N*/             }
/*N*/             else
/*N*/             {
/*N*/                 aCmd = DEFINE_CONST_UNICODE("slot:");
/*N*/                 aCmd += String::CreateFromInt32( nId );
/*N*/             }
/*N*/ 
/*N*/             // try to get a dispatch object for this command
/*N*/             aURL.Complete = aCmd;
/*N*/             Reference < XURLTransformer > xTrans( ::legacy_binfilters::getLegacyProcessServiceFactory()->createInstance( ::rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer" )), UNO_QUERY );
/*N*/             xTrans->parseStrict( aURL );
/*N*/             ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch >  xDisp = xProv->queryDispatch( aURL, ::rtl::OUString(), 0 );
/*N*/             if ( xDisp.is() )
/*N*/             {
/*N*/                 // test the dispatch object if it is just a wrapper for a SfxDispatcher
/*N*/ 				::com::sun::star::uno::Reference< ::com::sun::star::lang::XUnoTunnel > xTunnel( xDisp, ::com::sun::star::uno::UNO_QUERY );
/*N*/                 SfxOfficeDispatch* pDisp = NULL;
/*N*/                 if ( xTunnel.is() )
/*N*/                 {
/*N*/                     sal_Int64 nImplementation = xTunnel->getSomething(SfxOfficeDispatch::impl_getStaticIdentifier());
/*N*/                     pDisp = (SfxOfficeDispatch*)(nImplementation);
/*N*/                 }
/*N*/ 
/*N*/                 if ( pDisp )
/*N*/                 {
/*N*/                     // The intercepting object is an SFX component
/*N*/ 					// If it's not using the wanted dispatcher or the AppDispatcher, it's treated like any other UNO component
/*N*/                     SfxDispatcher *pDispatcher = pDisp->GetDispatcher_Impl();
/*N*/ 					if ( pDispatcher == &rDispat || pDispatcher == SFX_APP()->GetAppDispatcher_Impl() )
/*N*/ 					{
/*N*/                         // so we can use this shell direct without StarONE connection
/*N*/                     	rDispat._FindServer( nId, aSlotServ, sal_False );
/*N*/                         bSlotDirty = sal_False;
/*N*/                         bCtrlDirty = sal_True;
/*N*/ 
/*N*/                         //MI: wozu das? bItemDirty = sal_True;
/*N*/                         return aSlotServ.GetSlot()? &aSlotServ: 0;
/*N*/                     }
/*N*/                 }

                // here we are if the dispatch object isn't a SfxDispatcher wrapper or if the wrapper uses another
                // dispatcher, but not rDispat
                // first we need the SlotServer temporarily, because the BindDispatch will need it to access the slot
/*?*/                 rDispat._FindServer( nId, aSlotServ, sal_False );
/*?*/                 pDispatch = new BindDispatch_Impl( xDisp, aURL, this );
/*?*/                 pDispatch->acquire();
/*?*/ 
/*?*/                 // flags must be set before adding StatusListener because the dispatch object will set the state
/*?*/                 bSlotDirty = sal_False;
/*?*/                 bCtrlDirty = sal_True;
/*?*/                 xDisp->addStatusListener( pDispatch, aURL );
/*?*/ 
/*?*/                 // now we must forget the SlotServer
/*?*/                 aSlotServ.SetSlot(0);
/*?*/                 return NULL;
/*N*/             }
/*N*/ 
/*N*/             else if ( rDispat.GetFrame() )
/*N*/             {
/*N*/                 ::com::sun::star::uno::Reference < ::com::sun::star::frame::XDispatchProvider > xFrameProv(
/*N*/                         rDispat.GetFrame()->GetFrame()->GetFrameInterface(), ::com::sun::star::uno::UNO_QUERY );
/*N*/                 if ( xFrameProv != xProv )
/*?*/                     return GetSlotServer( rDispat, xFrameProv );
/*N*/             }
/*N*/         }
/*N*/ 		else
/*N*/ 		{
/*N*/             // Without a dispatch provider we are on our own!
/*N*/             // Don't find a server for the case "Dispatch Provider, but no dispatch".
/*N*/             // We have a configuration file that can disable specified commands. See bug #98419#
/*?*/ 			rDispat._FindServer(nId, aSlotServ, sal_False);
/*N*/ 		}
/*N*/ 
/*N*/ 		bSlotDirty = sal_False;
/*N*/ 		bCtrlDirty = sal_True;
/*N*/         //MI: wozu das? bItemDirty = sal_True;
/*N*/ 	}
/*N*/ 
/*N*/ 	return aSlotServ.GetSlot()? &aSlotServ: 0;
/*N*/ }


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

// Status setzen in allen Controllern

/*N*/ void SfxStateCache::SetState
/*N*/ (
/*N*/ 	SfxItemState		eState, 	// <SfxItemState> von 'pState'
/*N*/ 	const SfxPoolItem*	pState,		// Status des Slots, ggf. 0 oder -1
/*N*/ 	BOOL bMaybeDirty
/*N*/ )

/*	[Beschreibung]

	Diese Methode verteilt die Status auf alle an dieser SID gebundenen
	<SfxControllerItem>s. Ist der Wert derselbe wie zuvor und wurde in-
	zwischen weder ein Controller angemeldet, noch ein Controller invalidiert,
	dann wird kein Wert weitergeleitet. Dadurch wird z.B. Flackern in
	ListBoxen vermieden.
*/

/*N*/ {
/*N*/ 	if ( pDispatch )
/*N*/ 		return;
/*N*/ 	SetState_Impl( eState, pState, bMaybeDirty );
/*N*/ }


/*N*/ void SfxStateCache::SetState_Impl
/*N*/ (
/*N*/ 	SfxItemState		eState, 	// <SfxItemState> von 'pState'
/*N*/ 	const SfxPoolItem*	pState,		// Status des Slots, ggf. 0 oder -1
/*N*/ 	BOOL bMaybeDirty
/*N*/ )
/*N*/ {
/*N*/ 	DBG_MEMTEST();
/*N*/ 	DBG_CHKTHIS(SfxStateCache, 0);
/*N*/ 
/*N*/ 	// wenn zwischen Enter- und LeaveRegistrations ein hartes Update kommt
/*N*/ 	// k"onnen zwischenzeitlich auch Cached ohne Controller exisitieren
/*N*/ 	if ( !pController )
/*N*/ 		return;
/*N*/ 
/*N*/ 	DBG_ASSERT( pController->GetId()==nId, "Cache mit falschem ControllerItem" );
/*N*/ 	DBG_ASSERT( bMaybeDirty || !bSlotDirty, "setting state of dirty message" );
/*N*/ 	DBG_ASSERT( bCtrlDirty || ( aSlotServ.GetSlot() && aSlotServ.GetSlot()->IsMode(SFX_SLOT_VOLATILE) ),
/*N*/ 				"setting state of non dirty controller" );
/*N*/ 	DBG_ASSERT( SfxControllerItem::GetItemState(pState) == eState,
/*N*/ 				"invalid SfxItemState" );
/*N*/ 	DBG_PROFSTART(SfxStateCacheSetState);
/*N*/ 
/*N*/ 	// m"ussen die Controller "uberhaupt benachrichtigt werden?
/*N*/ 	FASTBOOL bNotify = bItemDirty;
/*N*/ 	if ( !bItemDirty )
/*N*/ 	{
/*N*/ 		FASTBOOL bBothAvailable = pLastItem && pState &&
/*N*/ 					!IsInvalidItem(pState) && !IsInvalidItem(pLastItem);
/*N*/ 		DBG_ASSERT( !bBothAvailable || pState != pLastItem, "setting state with own item" );
/*N*/ 		if ( bBothAvailable )
/*N*/ 			bNotify = pState->Type() != pLastItem->Type() ||
/*N*/ 					  *pState != *pLastItem;
/*N*/ 		else
/*N*/ 			bNotify = ( pState != pLastItem ) || ( eState != eLastState );
/*N*/ 	}
/*N*/ 
/*N*/ 	if ( bNotify )
/*N*/ 	{
/*N*/ 		// Controller updaten
/*N*/ 		for ( SfxControllerItem *pCtrl = pController;
/*N*/ 			  pCtrl;
/*N*/ 			  pCtrl = pCtrl->GetItemLink() )
/*N*/ 			pCtrl->StateChanged( nId, eState, pState );
/*N*/ 
/*N*/ 		// neuen Wert merken
/*N*/ 		if ( !IsInvalidItem(pLastItem) )
/*N*/ 			DELETEZ(pLastItem);
/*N*/ 		if ( pState && !IsInvalidItem(pState) )
/*N*/ 			pLastItem = pState->Clone();
/*N*/ 		else
/*N*/ 			pLastItem = 0;
/*N*/ 		eLastState = eState;
/*N*/ 		bItemDirty = sal_False;
/*N*/ 	}
/*N*/ 
/*N*/ 	bCtrlDirty = sal_False;
/*N*/ 	DBG_PROFSTOP(SfxStateCacheSetState);
/*N*/ }


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

// alten Status in allen Controllern nochmal setzen

/*N*/ void SfxStateCache::SetCachedState( BOOL bAlways )
/*N*/ {
/*N*/ 	DBG_MEMTEST();
/*N*/ 	DBG_CHKTHIS(SfxStateCache, 0);
/*N*/ 	DBG_ASSERT( pController, "Cache ohne ControllerItem" );
/*N*/ 	DBG_ASSERT( pController->GetId()==nId, "Cache mit falschem ControllerItem" );
/*N*/ 	DBG_PROFSTART(SfxStateCacheSetState);
/*N*/ 
/*N*/ 	// nur updaten wenn cached item vorhanden und auch verarbeitbar
/*N*/ 	// (Wenn der State gesendet wird, mu\s sichergestellt sein, da\s ein
/*N*/ 	// Slotserver vorhanden ist, s. SfxControllerItem::GetCoreMetric() )
/*N*/ 	if ( bAlways || ( !bItemDirty && !bSlotDirty ) )
/*N*/ 	{
/*N*/ 		// Controller updaten
/*N*/ 		for ( SfxControllerItem *pCtrl = pController;
/*N*/ 			  pCtrl;
/*N*/ 			  pCtrl = pCtrl->GetItemLink() )
/*N*/ 			pCtrl->StateChanged( nId, eLastState, pLastItem );
/*N*/ 
/*N*/ 		// Controller sind jetzt ok
/*N*/ 		bCtrlDirty = sal_True;
/*N*/ 	}
/*N*/ 
/*N*/ 	DBG_PROFSTOP(SfxStateCacheSetState);
/*N*/ }


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

// FloatingWindows in allen Controls mit dieser Id zerstoeren

/*N*/ void SfxStateCache::DeleteFloatingWindows()
/*N*/ {
/*N*/ 	DBG_MEMTEST();
/*N*/ 	DBG_CHKTHIS(SfxStateCache, 0);
/*N*/ 
/*N*/ 	SfxControllerItem *pNextCtrl=0;
/*N*/ 	for ( SfxControllerItem *pCtrl=pController; pCtrl; pCtrl=pNextCtrl )
/*N*/ 	{
/*N*/ 		DBG_TRACE((ByteString("pCtrl: ").Append(ByteString::CreateFromInt32((sal_uInt32)pCtrl))).GetBuffer());
/*N*/ 		pNextCtrl = pCtrl->GetItemLink();
/*N*/ 		pCtrl->DeleteFloatingWindow();
/*N*/ 	}
/*N*/ }

/*N*/ ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch >  SfxStateCache::GetDispatch() const
/*N*/ {
/*N*/ 	if ( pDispatch )
/*?*/ 		return pDispatch->xDisp;
/*N*/ 	return ::com::sun::star::uno::Reference< ::com::sun::star::frame::XDispatch > ();
/*N*/ }

/*N*/ void SfxStateCache::Dispatch( sal_Bool bForceSynchron )
/*N*/ {DBG_BF_ASSERT(0, "STRIP"); //STRIP001 
/*N*/ }


}
