summaryrefslogtreecommitdiff
path: root/sfx2/source/view/ipclient.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sfx2/source/view/ipclient.cxx')
-rw-r--r--sfx2/source/view/ipclient.cxx1166
1 files changed, 1166 insertions, 0 deletions
diff --git a/sfx2/source/view/ipclient.cxx b/sfx2/source/view/ipclient.cxx
new file mode 100644
index 000000000000..cae590128e23
--- /dev/null
+++ b/sfx2/source/view/ipclient.cxx
@@ -0,0 +1,1166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org 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 version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sfx2.hxx"
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XVisualObject.hpp>
+#include <com/sun/star/embed/XEmbeddedClient.hpp>
+#include <com/sun/star/embed/XInplaceClient.hpp>
+#include <com/sun/star/embed/XInplaceObject.hpp>
+#include <com/sun/star/embed/XComponentSupplier.hpp>
+#include <com/sun/star/embed/XWindowSupplier.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/EmbedVerbs.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/embed/XStateChangeListener.hpp>
+#include <com/sun/star/embed/StateChangeInProgressException.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <svtools/embedhlp.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/ipclient.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include "workwin.hxx"
+#include "guisaveas.hxx"
+#include <sfx2/viewfrm.hxx>
+#include <cppuhelper/implbase5.hxx>
+#include <vcl/salbtype.hxx>
+#include <svtools/ehdl.hxx>
+
+#include <vcl/timer.hxx>
+#include <vcl/window.hxx>
+#include <toolkit/awt/vclxwindow.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+#include <tools/fract.hxx>
+#include <tools/gen.hxx>
+#include <svl/rectitem.hxx>
+#include <svtools/soerr.hxx>
+#include <comphelper/processfactory.hxx>
+
+#define SFX_CLIENTACTIVATE_TIMEOUT 100
+
+using namespace com::sun::star;
+
+//====================================================================
+// SfxEmbedResizeGuard
+class SfxBooleanFlagGuard
+{
+ sal_Bool& m_rFlag;
+ sal_Bool m_bLifeValue;
+public:
+ SfxBooleanFlagGuard( sal_Bool& bFlag, sal_Bool bLifeValue )
+ : m_rFlag( bFlag )
+ , m_bLifeValue( bLifeValue )
+ {
+ m_rFlag = m_bLifeValue;
+ }
+
+ ~SfxBooleanFlagGuard()
+ {
+ m_rFlag = !m_bLifeValue;
+ }
+};
+
+//====================================================================
+// SfxInPlaceClient_Impl
+
+//--------------------------------------------------------------------
+class SfxInPlaceClient_Impl : public ::cppu::WeakImplHelper5< embed::XEmbeddedClient,
+ embed::XInplaceClient,
+ document::XEventListener,
+ embed::XStateChangeListener,
+ embed::XWindowSupplier >
+{
+public:
+ Timer m_aTimer; // activation timeout, starts after object connection
+ Rectangle m_aObjArea; // area of object in coordinate system of the container (without scaling)
+ Fraction m_aScaleWidth; // scaling that was applied to the object when it was not active
+ Fraction m_aScaleHeight;
+ SfxInPlaceClient* m_pClient;
+ sal_Int64 m_nAspect; // ViewAspect that is assigned from the container
+ Rectangle m_aLastObjAreaPixel; // area of object in coordinate system of the container (without scaling)
+ sal_Bool m_bStoreObject;
+ sal_Bool m_bUIActive; // set and cleared when notification for UI (de)activation is sent
+ sal_Bool m_bResizeNoScale;
+
+ uno::Reference < embed::XEmbeddedObject > m_xObject;
+ uno::Reference < embed::XEmbeddedClient > m_xClient;
+
+
+ SfxInPlaceClient_Impl()
+ : m_pClient( NULL )
+ , m_nAspect( 0 )
+ , m_bStoreObject( sal_True )
+ , m_bUIActive( sal_False )
+ , m_bResizeNoScale( sal_False )
+ {}
+
+ ~SfxInPlaceClient_Impl();
+
+ void SizeHasChanged();
+ DECL_LINK (TimerHdl, Timer*);
+ uno::Reference < frame::XFrame > GetFrame() const;
+
+ // XEmbeddedClient
+ virtual void SAL_CALL saveObject() throw ( embed::ObjectSaveVetoException, uno::Exception, uno::RuntimeException );
+ virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) throw ( embed::WrongStateException, uno::RuntimeException );
+
+ // XInplaceClient
+ virtual sal_Bool SAL_CALL canInplaceActivate() throw ( uno::RuntimeException );
+ virtual void SAL_CALL activatingInplace() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual void SAL_CALL activatingUI() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual void SAL_CALL deactivatedInplace() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual void SAL_CALL deactivatedUI() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual uno::Reference< ::com::sun::star::frame::XLayoutManager > SAL_CALL getLayoutManager() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual awt::Rectangle SAL_CALL getPlacement() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual awt::Rectangle SAL_CALL getClipRectangle() throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) throw ( embed::WrongStateException, uno::RuntimeException );
+ virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) throw ( embed::WrongStateException, uno::Exception, uno::RuntimeException );
+
+ // XComponentSupplier
+ virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() throw ( uno::RuntimeException );
+
+ // XWindowSupplier
+ virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() throw ( uno::RuntimeException );
+
+ // document::XEventListener
+ virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) throw( uno::RuntimeException );
+
+ // XStateChangeListener
+ virtual void SAL_CALL changingState( const ::com::sun::star::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) throw (::com::sun::star::embed::WrongStateException, ::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL stateChanged( const ::com::sun::star::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) throw (::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& aEvent ) throw (::com::sun::star::uno::RuntimeException);
+};
+
+SfxInPlaceClient_Impl::~SfxInPlaceClient_Impl()
+{
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::changingState(
+ const ::com::sun::star::lang::EventObject& /*aEvent*/,
+ ::sal_Int32 /*nOldState*/,
+ ::sal_Int32 /*nNewState*/ )
+throw (::com::sun::star::embed::WrongStateException, ::com::sun::star::uno::RuntimeException)
+{
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::stateChanged(
+ const ::com::sun::star::lang::EventObject& /*aEvent*/,
+ ::sal_Int32 nOldState,
+ ::sal_Int32 nNewState )
+throw (::com::sun::star::uno::RuntimeException)
+{
+ if ( m_pClient && nOldState != embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ // deactivation of object
+ uno::Reference< frame::XModel > xDocument;
+ if ( m_pClient->GetViewShell()->GetObjectShell() )
+ xDocument = m_pClient->GetViewShell()->GetObjectShell()->GetModel();
+ SfxObjectShell::SetCurrentComponent( xDocument );
+ }
+ else if ( m_pClient && nNewState == embed::EmbedStates::UI_ACTIVE )
+ {
+/*
+ uno::Reference < lang::XUnoTunnel > xObj( m_xObject->getComponent(), uno::UNO_QUERY );
+ uno::Sequence < sal_Int8 > aSeq( SvGlobalName( SFX_GLOBAL_CLASSID ).GetByteSequence() );
+ sal_Int64 nHandle = xObj.is() ? xObj->getSomething( aSeq ) : 0;
+ if ( nHandle )
+ {
+ // currently needs SFX code
+ SfxObjectShell* pDoc = reinterpret_cast< SfxObjectShell* >( sal::static_int_cast< sal_IntPtr >( nHandle ));
+ SfxViewFrame* pFrame = SfxViewFrame::GetFirst( pDoc );
+ SfxWorkWindow *pWorkWin = pFrame->GetFrame().GetWorkWindow_Impl();
+ pWorkWin->UpdateObjectBars_Impl();
+ }
+*/
+ }
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::notifyEvent( const document::EventObject& aEvent ) throw( uno::RuntimeException )
+{
+ SolarMutexGuard aGuard;
+
+ if ( m_pClient && aEvent.EventName.equalsAscii("OnVisAreaChanged") && m_nAspect != embed::Aspects::MSOLE_ICON )
+ {
+ m_pClient->ViewChanged();
+ m_pClient->Invalidate();
+ }
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::disposing( const ::com::sun::star::lang::EventObject& /*aEvent*/ )
+throw (::com::sun::star::uno::RuntimeException)
+{
+ DELETEZ( m_pClient );
+}
+
+// XEmbeddedClient
+//--------------------------------------------------------------------
+uno::Reference < frame::XFrame > SfxInPlaceClient_Impl::GetFrame() const
+{
+ if ( !m_pClient )
+ throw uno::RuntimeException();
+ return m_pClient->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
+}
+
+void SAL_CALL SfxInPlaceClient_Impl::saveObject()
+ throw ( embed::ObjectSaveVetoException,
+ uno::Exception,
+ uno::RuntimeException )
+{
+ if ( !m_bStoreObject )
+ // client wants to discard the object (usually it means the container document is closed while an object is active
+ // and the user didn't request saving the changes
+ return;
+
+ // the common persistance is supported by objects and links
+ uno::Reference< embed::XCommonEmbedPersist > xPersist( m_xObject, uno::UNO_QUERY );
+ if ( !xPersist.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< frame::XFrame > xFrame;
+ uno::Reference< task::XStatusIndicator > xStatusIndicator;
+ uno::Reference< frame::XModel > xModel( m_xObject->getComponent(), uno::UNO_QUERY );
+ uno::Reference< lang::XMultiServiceFactory > xSrvMgr( ::comphelper::getProcessServiceFactory() );
+
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ xFrame = xController->getFrame();
+ }
+
+ if ( xSrvMgr.is() && xFrame.is() )
+ {
+ // set non-reschedule progress to prevent problems when asynchronous calls are made
+ // during storing of the embedded object
+ uno::Reference< lang::XInitialization > xInit(
+ xSrvMgr->createInstance(
+ rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.framework.StatusIndicatorFactory" ))),
+ uno::UNO_QUERY_THROW );
+ beans::PropertyValue aProperty;
+ uno::Sequence< uno::Any > aArgs( 2 );
+ aProperty.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DisableReschedule" ));
+ aProperty.Value = uno::makeAny( sal_True );
+ aArgs[0] = uno::makeAny( aProperty );
+ aProperty.Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Frame" ));
+ aProperty.Value = uno::makeAny( xFrame );
+ aArgs[1] = uno::makeAny( aProperty );
+
+ xInit->initialize( aArgs );
+
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ uno::Reference< task::XStatusIndicatorFactory > xStatusIndicatorFactory( xInit, uno::UNO_QUERY_THROW );
+ xStatusIndicator = xStatusIndicatorFactory->createStatusIndicator();
+ xPropSet->setPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IndicatorInterception" )), uno::makeAny( xStatusIndicator ));
+ }
+ catch ( uno::RuntimeException& e )
+ {
+ throw e;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ try
+ {
+ xPersist->storeOwn();
+ m_xObject->update();
+ }
+ catch ( uno::Exception& )
+ {
+ //TODO/LATER: what should happen if object can't be saved?!
+ }
+
+ // reset status indicator interception after storing
+ try
+ {
+ uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ xStatusIndicator.clear();
+ xPropSet->setPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IndicatorInterception" )), uno::makeAny( xStatusIndicator ));
+ }
+ }
+ catch ( uno::RuntimeException& e )
+ {
+ throw e;
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ // the client can exist only in case there is a view shell
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ pDocShell->SetModified( sal_True );
+
+ //TODO/LATER: invalidation might be necessary when object was modified, but is not
+ //saved through this method
+ // m_pClient->Invalidate();
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::visibilityChanged( sal_Bool bVisible )
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->OutplaceActivated( bVisible, m_pClient );
+ m_pClient->Invalidate();
+}
+
+
+// XInplaceClient
+//--------------------------------------------------------------------
+sal_Bool SAL_CALL SfxInPlaceClient_Impl::canInplaceActivate()
+ throw ( uno::RuntimeException )
+{
+ if ( !m_xObject.is() )
+ throw uno::RuntimeException();
+
+ // we don't want to switch directly from outplace to inplace mode
+ if ( m_xObject->getCurrentState() == embed::EmbedStates::ACTIVE || m_nAspect == embed::Aspects::MSOLE_ICON )
+ return sal_False;
+
+ return sal_True;
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::activatingInplace()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->InplaceActivating( m_pClient );
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::activatingUI()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->ResetAllClients_Impl(m_pClient);
+ m_bUIActive = TRUE;
+ m_pClient->GetViewShell()->UIActivating( m_pClient );
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::deactivatedInplace()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->InplaceDeactivated( m_pClient );
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::deactivatedUI()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ m_pClient->GetViewShell()->UIDeactivated( m_pClient );
+ m_bUIActive = FALSE;
+}
+
+//--------------------------------------------------------------------
+uno::Reference< ::com::sun::star::frame::XLayoutManager > SAL_CALL SfxInPlaceClient_Impl::getLayoutManager()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ uno::Reference < beans::XPropertySet > xFrame( GetFrame(), uno::UNO_QUERY );
+ if ( !xFrame.is() )
+ throw uno::RuntimeException();
+
+ uno::Reference< ::com::sun::star::frame::XLayoutManager > xMan;
+ try
+ {
+ uno::Any aAny = xFrame->getPropertyValue( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("LayoutManager")) );
+ aAny >>= xMan;
+ }
+ catch ( uno::Exception& )
+ {
+ throw uno::RuntimeException();
+ }
+
+ return xMan;
+}
+
+//--------------------------------------------------------------------
+uno::Reference< frame::XDispatchProvider > SAL_CALL SfxInPlaceClient_Impl::getInplaceDispatchProvider()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ return uno::Reference < frame::XDispatchProvider >( GetFrame(), uno::UNO_QUERY_THROW );
+}
+
+//--------------------------------------------------------------------
+awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getPlacement()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // apply scaling to object area and convert to pixels
+ Rectangle aRealObjArea( m_aObjArea );
+ aRealObjArea.SetSize( Size( Fraction( aRealObjArea.GetWidth() ) * m_aScaleWidth,
+ Fraction( aRealObjArea.GetHeight() ) * m_aScaleHeight ) );
+
+ aRealObjArea = m_pClient->GetEditWin()->LogicToPixel( aRealObjArea );
+ return AWTRectangle( aRealObjArea );
+}
+
+//--------------------------------------------------------------------
+awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getClipRectangle()
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // currently(?) same as placement
+ Rectangle aRealObjArea( m_aObjArea );
+ aRealObjArea.SetSize( Size( Fraction( aRealObjArea.GetWidth() ) * m_aScaleWidth,
+ Fraction( aRealObjArea.GetHeight() ) * m_aScaleHeight ) );
+
+ aRealObjArea = m_pClient->GetEditWin()->LogicToPixel( aRealObjArea );
+ return AWTRectangle( aRealObjArea );
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // TODO/MBA: keyboard accelerators
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
+ throw ( embed::WrongStateException,
+ uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+}
+
+//--------------------------------------------------------------------
+void SAL_CALL SfxInPlaceClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
+ throw ( embed::WrongStateException,
+ uno::Exception,
+ uno::RuntimeException )
+{
+ uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY );
+ if ( !xInplace.is() || !m_pClient || !m_pClient->GetEditWin() || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ // check if the change is at least one pixel in size
+ awt::Rectangle aOldRect = getPlacement();
+ Rectangle aNewPixelRect = VCLRectangle( aPosRect );
+ Rectangle aOldPixelRect = VCLRectangle( aOldRect );
+ if ( aOldPixelRect == aNewPixelRect )
+ // nothing has changed
+ return;
+
+ // new scaled object area
+ Rectangle aNewLogicRect = m_pClient->GetEditWin()->PixelToLogic( aNewPixelRect );
+
+ // all the size changes in this method should happen without scaling
+ // SfxBooleanFlagGuard aGuard( m_bResizeNoScale, sal_True );
+
+ // allow container to apply restrictions on the requested new area;
+ // the container might change the object view during size calculation;
+ // currently only writer does it
+ m_pClient->RequestNewObjectArea( aNewLogicRect);
+
+ if ( aNewLogicRect != m_pClient->GetScaledObjArea() )
+ {
+ // the calculation of the object area has not changed the object size
+ // it should be done here then
+ SfxBooleanFlagGuard aGuard( m_bResizeNoScale, sal_True );
+
+ // new size of the object area without scaling
+ Size aNewObjSize( Fraction( aNewLogicRect.GetWidth() ) / m_aScaleWidth,
+ Fraction( aNewLogicRect.GetHeight() ) / m_aScaleHeight );
+
+ // now remove scaling from new placement and keep this a the new object area
+ aNewLogicRect.SetSize( aNewObjSize );
+ m_aObjArea = aNewLogicRect;
+
+ // let the window size be recalculated
+ SizeHasChanged();
+ }
+
+ // notify container view about changes
+ m_pClient->ObjectAreaChanged();
+}
+
+// XComponentSupplier
+//--------------------------------------------------------------------
+uno::Reference< util::XCloseable > SAL_CALL SfxInPlaceClient_Impl::getComponent()
+ throw ( uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
+ if ( !pDocShell )
+ throw uno::RuntimeException();
+
+ // all the components must implement XCloseable
+ uno::Reference< util::XCloseable > xComp( pDocShell->GetModel(), uno::UNO_QUERY );
+ if ( !xComp.is() )
+ throw uno::RuntimeException();
+
+ return xComp;
+}
+
+
+// XWindowSupplier
+//--------------------------------------------------------------------
+uno::Reference< awt::XWindow > SAL_CALL SfxInPlaceClient_Impl::getWindow()
+ throw ( uno::RuntimeException )
+{
+ if ( !m_pClient || !m_pClient->GetEditWin() )
+ throw uno::RuntimeException();
+
+ uno::Reference< awt::XWindow > xWin( m_pClient->GetEditWin()->GetComponentInterface(), uno::UNO_QUERY );
+ return xWin;
+}
+
+//--------------------------------------------------------------------
+// notification to the client implementation that either the object area or the scaling has been changed
+// as a result the logical size of the window has changed also
+void SfxInPlaceClient_Impl::SizeHasChanged()
+{
+ if ( !m_pClient || !m_pClient->GetViewShell() )
+ throw uno::RuntimeException();
+
+ try {
+ if ( m_xObject.is()
+ && ( m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
+ || m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) )
+ {
+ // only possible in active states
+ uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY );
+ if ( !xInplace.is() )
+ throw uno::RuntimeException();
+
+ if ( m_bResizeNoScale )
+ {
+ // the resizing should be done without scaling
+ // set the correct size to the object to avoid the scaling
+ MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xObject->getMapUnit( m_nAspect ) ) );
+ MapMode aClientMap( m_pClient->GetEditWin()->GetMapMode().GetMapUnit() );
+
+ // convert to logical coordinates of the embedded object
+ Size aNewSize = m_pClient->GetEditWin()->LogicToLogic( m_aObjArea.GetSize(), &aClientMap, &aObjectMap );
+ m_xObject->setVisualAreaSize( m_nAspect, awt::Size( aNewSize.Width(), aNewSize.Height() ) );
+ }
+
+ xInplace->setObjectRectangles( getPlacement(), getClipRectangle() );
+ }
+ }
+ catch( uno::Exception& )
+ {
+ // TODO/LATER: handle error
+ }
+}
+
+//--------------------------------------------------------------------
+IMPL_LINK( SfxInPlaceClient_Impl, TimerHdl, Timer*, EMPTYARG )
+{
+ if ( m_pClient && m_xObject.is() )
+ m_pClient->GetViewShell()->CheckIPClient_Impl( m_pClient, m_pClient->GetViewShell()->GetObjectShell()->GetVisArea() );
+ return 0;
+}
+
+
+//====================================================================
+// SfxInPlaceClient
+
+//--------------------------------------------------------------------
+SfxInPlaceClient::SfxInPlaceClient( SfxViewShell* pViewShell, Window *pDraw, sal_Int64 nAspect ) :
+ m_pImp( new SfxInPlaceClient_Impl ),
+ m_pViewSh( pViewShell ),
+ m_pEditWin( pDraw )
+{
+ m_pImp->acquire();
+ m_pImp->m_pClient = this;
+ m_pImp->m_nAspect = nAspect;
+ m_pImp->m_aScaleWidth = m_pImp->m_aScaleHeight = Fraction(1,1);
+ m_pImp->m_xClient = static_cast< embed::XEmbeddedClient* >( m_pImp );
+ pViewShell->NewIPClient_Impl(this);
+ m_pImp->m_aTimer.SetTimeout( SFX_CLIENTACTIVATE_TIMEOUT );
+ m_pImp->m_aTimer.SetTimeoutHdl( LINK( m_pImp, SfxInPlaceClient_Impl, TimerHdl ) );
+}
+
+//--------------------------------------------------------------------
+
+SfxInPlaceClient::~SfxInPlaceClient()
+{
+ m_pViewSh->IPClientGone_Impl(this);
+
+ // deleting the client before storing the object means discarding all changes
+ m_pImp->m_bStoreObject = sal_False;
+ SetObject(0);
+
+ m_pImp->m_pClient = NULL;
+
+ // the next call will destroy m_pImp if no other reference to it exists
+ m_pImp->m_xClient = uno::Reference < embed::XEmbeddedClient >();
+ m_pImp->release();
+
+ // TODO/LATER:
+ // the class is not intended to be used in multithreaded environment;
+ // if it will this disconnection and all the parts that use the m_pClient
+ // must be guarded with mutex
+}
+
+//--------------------------------------------------------------------
+void SfxInPlaceClient::SetObjectState( sal_Int32 nState )
+{
+ if ( GetObject().is() )
+ {
+ if ( m_pImp->m_nAspect == embed::Aspects::MSOLE_ICON
+ && ( nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::INPLACE_ACTIVE ) )
+ {
+ OSL_ENSURE( sal_False, "Iconified object should not be activated inplace!\n" );
+ return;
+ }
+
+ try
+ {
+ GetObject()->changeState( nState );
+ }
+ catch ( uno::Exception& )
+ {}
+ }
+}
+
+//--------------------------------------------------------------------
+sal_Int64 SfxInPlaceClient::GetObjectMiscStatus() const
+{
+ if ( GetObject().is() )
+ return GetObject()->getStatus( m_pImp->m_nAspect );
+ return 0;
+}
+
+//--------------------------------------------------------------------
+uno::Reference < embed::XEmbeddedObject > SfxInPlaceClient::GetObject() const
+{
+ return m_pImp->m_xObject;
+}
+
+//--------------------------------------------------------------------
+void SfxInPlaceClient::SetObject( const uno::Reference < embed::XEmbeddedObject >& rObject )
+{
+ if ( m_pImp->m_xObject.is() && rObject != m_pImp->m_xObject )
+ {
+ DBG_ASSERT( GetObject()->getClientSite() == m_pImp->m_xClient, "Wrong ClientSite!" );
+ if ( GetObject()->getClientSite() == m_pImp->m_xClient )
+ {
+ if ( GetObject()->getCurrentState() != embed::EmbedStates::LOADED )
+ SetObjectState( embed::EmbedStates::RUNNING );
+ m_pImp->m_xObject->removeEventListener( uno::Reference < document::XEventListener >( m_pImp->m_xClient, uno::UNO_QUERY ) );
+ m_pImp->m_xObject->removeStateChangeListener( uno::Reference < embed::XStateChangeListener >( m_pImp->m_xClient, uno::UNO_QUERY ) );
+ try
+ {
+ m_pImp->m_xObject->setClientSite( 0 );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_ENSURE( sal_False, "Can not clean the client site!\n" );
+ }
+ }
+ }
+
+ if ( !m_pViewSh || m_pViewSh->GetViewFrame()->GetFrame().IsClosing_Impl() )
+ // sometimes applications reconnect clients on shutting down because it happens in their Paint methods
+ return;
+
+ m_pImp->m_xObject = rObject;
+
+ if ( rObject.is() )
+ {
+ // as soon as an object was connected to a client it has to be checked wether the object wants
+ // to be activated
+ rObject->addStateChangeListener( uno::Reference < embed::XStateChangeListener >( m_pImp->m_xClient, uno::UNO_QUERY ) );
+ rObject->addEventListener( uno::Reference < document::XEventListener >( m_pImp->m_xClient, uno::UNO_QUERY ) );
+
+ try
+ {
+ rObject->setClientSite( m_pImp->m_xClient );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_ENSURE( sal_False, "Can not set the client site!\n" );
+ }
+
+ m_pImp->m_aTimer.Start();
+ }
+ else
+ m_pImp->m_aTimer.Stop();
+}
+
+//--------------------------------------------------------------------
+BOOL SfxInPlaceClient::SetObjArea( const Rectangle& rArea )
+{
+ if( rArea != m_pImp->m_aObjArea )
+ {
+ m_pImp->m_aObjArea = rArea;
+ m_pImp->SizeHasChanged();
+
+ Invalidate();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//--------------------------------------------------------------------
+Rectangle SfxInPlaceClient::GetObjArea() const
+{
+ return m_pImp->m_aObjArea;
+}
+
+Rectangle SfxInPlaceClient::GetScaledObjArea() const
+{
+ Rectangle aRealObjArea( m_pImp->m_aObjArea );
+ aRealObjArea.SetSize( Size( Fraction( aRealObjArea.GetWidth() ) * m_pImp->m_aScaleWidth,
+ Fraction( aRealObjArea.GetHeight() ) * m_pImp->m_aScaleHeight ) );
+ return aRealObjArea;
+}
+
+//--------------------------------------------------------------------
+void SfxInPlaceClient::SetSizeScale( const Fraction & rScaleWidth, const Fraction & rScaleHeight )
+{
+ if ( m_pImp->m_aScaleWidth != rScaleWidth || m_pImp->m_aScaleHeight != rScaleHeight )
+ {
+ m_pImp->m_aScaleWidth = rScaleWidth;
+ m_pImp->m_aScaleHeight = rScaleHeight;
+
+ m_pImp->SizeHasChanged();
+
+ // TODO/LATER: Invalidate seems to trigger (wrong) recalculations of the ObjArea, so it's better
+ // not to call it here, but maybe it sounds reasonable to do so.
+ //Invalidate();
+ }
+}
+
+//--------------------------------------------------------------------
+sal_Bool SfxInPlaceClient::SetObjAreaAndScale( const Rectangle& rArea, const Fraction& rScaleWidth, const Fraction& rScaleHeight )
+{
+ if( rArea != m_pImp->m_aObjArea || m_pImp->m_aScaleWidth != rScaleWidth || m_pImp->m_aScaleHeight != rScaleHeight )
+ {
+ m_pImp->m_aObjArea = rArea;
+ m_pImp->m_aScaleWidth = rScaleWidth;
+ m_pImp->m_aScaleHeight = rScaleHeight;
+
+ m_pImp->SizeHasChanged();
+
+ Invalidate();
+ return sal_True;
+ }
+
+ return sal_False;
+}
+
+//--------------------------------------------------------------------
+const Fraction& SfxInPlaceClient::GetScaleWidth() const
+{
+ return m_pImp->m_aScaleWidth;
+}
+
+//--------------------------------------------------------------------
+const Fraction& SfxInPlaceClient::GetScaleHeight() const
+{
+ return m_pImp->m_aScaleHeight;
+}
+
+//--------------------------------------------------------------------
+void SfxInPlaceClient::Invalidate()
+{
+ // TODO/LATER: do we need both?
+
+ // the object area is provided in logical coordinates of the window but without scaling applied
+ Rectangle aRealObjArea( m_pImp->m_aObjArea );
+ aRealObjArea.SetSize( Size( Fraction( aRealObjArea.GetWidth() ) * m_pImp->m_aScaleWidth,
+ Fraction( aRealObjArea.GetHeight() ) * m_pImp->m_aScaleHeight ) );
+ m_pEditWin->Invalidate( aRealObjArea );
+
+ ViewChanged();
+}
+
+//--------------------------------------------------------------------
+sal_Bool SfxInPlaceClient::IsObjectUIActive() const
+{
+ try {
+ return ( m_pImp->m_xObject.is() && ( m_pImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return sal_False;
+}
+
+//--------------------------------------------------------------------
+sal_Bool SfxInPlaceClient::IsObjectInPlaceActive() const
+{
+ try {
+ return(
+ (
+ m_pImp->m_xObject.is() &&
+ (m_pImp->m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE)
+ ) ||
+ (
+ m_pImp->m_xObject.is() &&
+ (m_pImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE)
+ )
+ );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return sal_False;
+}
+
+//--------------------------------------------------------------------
+sal_Bool SfxInPlaceClient::IsObjectActive() const
+{
+ try {
+ return ( m_pImp->m_xObject.is() && ( m_pImp->m_xObject->getCurrentState() == embed::EmbedStates::ACTIVE ) );
+ }
+ catch( uno::Exception& )
+ {}
+
+ return sal_False;
+}
+
+//--------------------------------------------------------------------
+Window* SfxInPlaceClient::GetActiveWindow( SfxObjectShell* pDoc, const com::sun::star::uno::Reference < com::sun::star::embed::XEmbeddedObject >& xObject )
+{
+ SfxInPlaceClient* pClient = GetClient( pDoc, xObject );
+ if ( pClient )
+ return pClient->GetEditWin();
+ return NULL;
+}
+
+//--------------------------------------------------------------------
+SfxInPlaceClient* SfxInPlaceClient::GetClient( SfxObjectShell* pDoc, const com::sun::star::uno::Reference < com::sun::star::embed::XEmbeddedObject >& xObject )
+{
+ for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc); pFrame; pFrame=SfxViewFrame::GetNext(*pFrame,pDoc) )
+ {
+ if( pFrame->GetViewShell() )
+ {
+ SfxInPlaceClient* pClient = pFrame->GetViewShell()->FindIPClient( xObject, NULL );
+ if ( pClient )
+ return pClient;
+ }
+ }
+
+ return NULL;
+}
+
+sal_Int64 SfxInPlaceClient::GetAspect() const
+{
+ return m_pImp->m_nAspect;
+}
+
+ErrCode SfxInPlaceClient::DoVerb( long nVerb )
+{
+ SfxErrorContext aEc( ERRCTX_SO_DOVERB, m_pViewSh->GetWindow(), RID_SO_ERRCTX );
+ ErrCode nError = ERRCODE_NONE;
+
+ if ( m_pImp->m_xObject.is() )
+ {
+ sal_Bool bSaveCopyAs = sal_False;
+ if ( nVerb == -8 ) // "Save Copy as..."
+ {
+ svt::EmbeddedObjectRef::TryRunningState( m_pImp->m_xObject );
+ // TODO/LATER: this special verb should disappear when outplace activation is completely available
+ uno::Reference< frame::XModel > xEmbModel( m_pImp->m_xObject->getComponent(), uno::UNO_QUERY );
+ if ( xEmbModel.is() )
+ {
+ bSaveCopyAs = sal_True;
+
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xEmptyFactory;
+ SfxStoringHelper aHelper( xEmptyFactory );
+ uno::Sequence< beans::PropertyValue > aDispatchArgs( 1 );
+ aDispatchArgs[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SaveTo" ) );
+ aDispatchArgs[0].Value <<= (sal_Bool)sal_True;
+
+ aHelper.GUIStoreModel( xEmbModel,
+ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SaveAs" ) ),
+ aDispatchArgs,
+ sal_False,
+ ::rtl::OUString() );
+ }
+ catch( task::ErrorCodeIOException& aErrorEx )
+ {
+ nError = (sal_uInt32)aErrorEx.ErrCode;
+ }
+ catch( uno::Exception& )
+ {
+ nError = ERRCODE_IO_GENERAL;
+ // TODO/LATER: better error handling
+ }
+ }
+ }
+
+ if ( !bSaveCopyAs )
+ {
+ if ( m_pImp->m_nAspect == embed::Aspects::MSOLE_ICON )
+ {
+ if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW )
+ nVerb = embed::EmbedVerbs::MS_OLEVERB_OPEN; // outplace activation
+ else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE
+ || nVerb == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE )
+ nError = ERRCODE_SO_GENERALERROR;
+ }
+
+ if ( !nError )
+ {
+
+ if ( m_pViewSh )
+ m_pViewSh->GetViewFrame()->GetTopFrame().LockResize_Impl(TRUE);
+ try
+ {
+ m_pImp->m_xObject->setClientSite( m_pImp->m_xClient );
+
+ m_pImp->m_xObject->doVerb( nVerb );
+ }
+ catch ( embed::UnreachableStateException& )
+ {
+ if ( nVerb == 0 || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN )
+ {
+ // a workaround for the default verb, usually makes sence for alien objects
+ try
+ {
+ m_pImp->m_xObject->doVerb( -9 ); // open own view, a workaround verb that is not visible
+
+ if ( m_pImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE )
+ {
+ // the object was converted to OOo object
+ awt::Size aSize = m_pImp->m_xObject->getVisualAreaSize( m_pImp->m_nAspect );
+ MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_pImp->m_xObject->getMapUnit( m_pImp->m_nAspect ) ) );
+ MapMode aClientMap( GetEditWin()->GetMapMode().GetMapUnit() );
+ Size aNewSize = GetEditWin()->LogicToLogic( Size( aSize.Width, aSize.Height ), &aObjectMap, &aClientMap );
+
+ Rectangle aScaledArea = GetScaledObjArea();
+ m_pImp->m_aObjArea.SetSize( aNewSize );
+ m_pImp->m_aScaleWidth = Fraction( aScaledArea.GetWidth(), aNewSize.Width() );
+ m_pImp->m_aScaleHeight = Fraction( aScaledArea.GetHeight(), aNewSize.Height() );
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ nError = ERRCODE_SO_GENERALERROR;
+ }
+ }
+ }
+ catch ( embed::StateChangeInProgressException& )
+ {
+ // TODO/LATER: it would be nice to be able to provide the current target state outside
+ nError = ERRCODE_SO_CANNOT_DOVERB_NOW;
+ }
+ catch ( uno::Exception& )
+ {
+ nError = ERRCODE_SO_GENERALERROR;
+ //TODO/LATER: better error handling
+ }
+
+ if ( m_pViewSh )
+ {
+ SfxViewFrame* pFrame = m_pViewSh->GetViewFrame();
+ pFrame->GetTopFrame().LockResize_Impl(FALSE);
+ pFrame->GetTopFrame().Resize();
+ }
+ }
+ }
+ }
+
+ if( nError )
+ ErrorHandler::HandleError( nError );
+
+ return nError;
+}
+
+void SfxInPlaceClient::VisAreaChanged()
+{
+ uno::Reference < embed::XInplaceObject > xObj( m_pImp->m_xObject, uno::UNO_QUERY );
+ uno::Reference < embed::XInplaceClient > xClient( m_pImp->m_xClient, uno::UNO_QUERY );
+ if ( xObj.is() && xClient.is() )
+ m_pImp->SizeHasChanged();
+}
+
+void SfxInPlaceClient::ObjectAreaChanged()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::RequestNewObjectArea( Rectangle& )
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::ViewChanged()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::MakeVisible()
+{
+ // dummy implementation
+}
+
+void SfxInPlaceClient::DeactivateObject()
+{
+ if ( GetObject().is() )
+ {
+ try
+ {
+ m_pImp->m_bUIActive = FALSE;
+ BOOL bHasFocus = FALSE;
+ uno::Reference< frame::XModel > xModel( m_pImp->m_xObject->getComponent(), uno::UNO_QUERY );
+ if ( xModel.is() )
+ {
+ uno::Reference< frame::XController > xController = xModel->getCurrentController();
+ if ( xController.is() )
+ {
+ Window* pWindow = VCLUnoHelper::GetWindow( xController->getFrame()->getContainerWindow() );
+ bHasFocus = pWindow->HasChildPathFocus( TRUE );
+ }
+ }
+
+ if ( m_pViewSh )
+ m_pViewSh->GetViewFrame()->GetTopFrame().LockResize_Impl(TRUE);
+
+ if ( m_pImp->m_xObject->getStatus( m_pImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ {
+ m_pImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ if ( bHasFocus && m_pViewSh )
+ m_pViewSh->GetWindow()->GrabFocus();
+ }
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( m_pImp->m_xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ m_pImp->m_xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ m_pImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+
+ if ( m_pViewSh )
+ {
+ SfxViewFrame* pFrame = m_pViewSh->GetViewFrame();
+ SfxViewFrame::SetViewFrame( pFrame );
+ pFrame->GetTopFrame().LockResize_Impl(FALSE);
+ pFrame->GetTopFrame().Resize();
+ }
+ }
+ catch (com::sun::star::uno::Exception& )
+ {}
+ }
+}
+
+void SfxInPlaceClient::ResetObject()
+{
+ if ( GetObject().is() )
+ {
+ try
+ {
+ m_pImp->m_bUIActive = FALSE;
+ if ( m_pImp->m_xObject->getStatus( m_pImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ m_pImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( m_pImp->m_xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ m_pImp->m_xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ m_pImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+ }
+ catch (com::sun::star::uno::Exception& )
+ {}
+ }
+}
+
+BOOL SfxInPlaceClient::IsUIActive()
+{
+ return m_pImp->m_bUIActive;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */