Use correct object
[LibreOffice.git] / sfx2 / source / view / ipclient.cxx
blob2215e17469e156afd440516d0fc458c3d85df1d5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <com/sun/star/awt/XVclWindowPeer.hpp>
23 #include <com/sun/star/embed/Aspects.hpp>
24 #include <com/sun/star/embed/EmbedStates.hpp>
25 #include <com/sun/star/embed/UnreachableStateException.hpp>
26 #include <com/sun/star/embed/XEmbeddedClient.hpp>
27 #include <com/sun/star/embed/XInplaceClient.hpp>
28 #include <com/sun/star/embed/XInplaceObject.hpp>
29 #include <com/sun/star/embed/XWindowSupplier.hpp>
30 #include <com/sun/star/embed/EmbedVerbs.hpp>
31 #include <com/sun/star/embed/XEmbeddedOleObject.hpp>
32 #include <com/sun/star/embed/XEmbeddedObject.hpp>
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/beans/PropertyValue.hpp>
35 #include <com/sun/star/embed/XStateChangeListener.hpp>
36 #include <com/sun/star/embed/StateChangeInProgressException.hpp>
37 #include <com/sun/star/embed/XLinkageSupport.hpp>
38 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
39 #include <com/sun/star/task/ErrorCodeIOException.hpp>
40 #include <com/sun/star/task/StatusIndicatorFactory.hpp>
41 #include <com/sun/star/task/XStatusIndicator.hpp>
43 #include <com/sun/star/embed/EmbedMisc.hpp>
44 #include <svtools/embedhlp.hxx>
45 #include <vcl/svapp.hxx>
46 #include <vcl/unohelp.hxx>
48 #include <sfx2/ipclient.hxx>
49 #include <sfx2/viewsh.hxx>
50 #include <sfx2/viewfrm.hxx>
51 #include <sfx2/objsh.hxx>
52 #include <guisaveas.hxx>
53 #include <cppuhelper/implbase.hxx>
54 #include <svtools/ehdl.hxx>
56 #include <vcl/timer.hxx>
57 #include <vcl/window.hxx>
58 #include <toolkit/helper/vclunohelper.hxx>
59 #include <tools/debug.hxx>
60 #include <comphelper/diagnose_ex.hxx>
61 #include <tools/fract.hxx>
62 #include <tools/gen.hxx>
63 #include <svtools/soerr.hxx>
64 #include <comphelper/lok.hxx>
65 #include <comphelper/processfactory.hxx>
66 #include <comphelper/propertyvalue.hxx>
67 #include <cppuhelper/exc_hlp.hxx>
69 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
71 #define SFX_CLIENTACTIVATE_TIMEOUT 100
73 using namespace com::sun::star;
75 namespace {
77 // SfxEmbedResizeGuard
78 class SfxBooleanFlagGuard
80 bool& m_rFlag;
81 public:
82 explicit SfxBooleanFlagGuard(bool& bFlag)
83 : m_rFlag( bFlag )
85 m_rFlag = true;
88 ~SfxBooleanFlagGuard()
90 m_rFlag = false;
94 tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
96 return tools::Rectangle(
97 std::max(static_cast<tools::Long>(0l), -rRect.Right()),
98 rRect.Top(),
99 std::max(static_cast<tools::Long>(0l), -rRect.Left()),
100 rRect.Bottom());
105 // SfxInPlaceClient_Impl
108 class SfxInPlaceClient_Impl : public ::cppu::WeakImplHelper< embed::XEmbeddedClient,
109 embed::XInplaceClient,
110 document::XEventListener,
111 embed::XStateChangeListener,
112 embed::XWindowSupplier >
114 public:
115 Timer m_aTimer { "sfx::SfxInPlaceClient m_xImpl::m_aTimer" }; // activation timeout, starts after object connection
116 tools::Rectangle m_aObjArea; // area of object in coordinate system of the container (without scaling)
117 Fraction m_aScaleWidth; // scaling that was applied to the object when it was not active
118 Fraction m_aScaleHeight;
119 SfxInPlaceClient* m_pClient;
120 sal_Int64 m_nAspect; // ViewAspect that is assigned from the container
121 bool m_bStoreObject;
122 bool m_bUIActive; // set and cleared when notification for UI (de)activation is sent
123 bool m_bResizeNoScale;
124 bool m_bNegativeX;
126 uno::Reference < embed::XEmbeddedObject > m_xObject;
129 SfxInPlaceClient_Impl()
130 : m_pClient( nullptr )
131 , m_nAspect( 0 )
132 , m_bStoreObject( true )
133 , m_bUIActive( false )
134 , m_bResizeNoScale( false )
135 , m_bNegativeX( false )
138 void SizeHasChanged();
139 DECL_LINK(TimerHdl, Timer *, void);
140 uno::Reference < frame::XFrame > const & GetFrame() const;
142 // XEmbeddedClient
143 virtual void SAL_CALL saveObject() override;
144 virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) override;
146 // XInplaceClient
147 virtual sal_Bool SAL_CALL canInplaceActivate() override;
148 virtual void SAL_CALL activatingInplace() override;
149 virtual void SAL_CALL activatingUI() override;
150 virtual void SAL_CALL deactivatedInplace() override;
151 virtual void SAL_CALL deactivatedUI() override;
152 virtual uno::Reference< css::frame::XLayoutManager > SAL_CALL getLayoutManager() override;
153 virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() override;
154 virtual awt::Rectangle SAL_CALL getPlacement() override;
155 virtual awt::Rectangle SAL_CALL getClipRectangle() override;
156 virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) override;
157 virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) override;
158 virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) override;
160 // XComponentSupplier
161 virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() override;
163 // XWindowSupplier
164 virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() override;
166 // document::XEventListener
167 virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) override;
169 // XStateChangeListener
170 virtual void SAL_CALL changingState( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
171 virtual void SAL_CALL stateChanged( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
172 virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
175 void SAL_CALL SfxInPlaceClient_Impl::changingState(
176 const css::lang::EventObject& /*aEvent*/,
177 ::sal_Int32 /*nOldState*/,
178 ::sal_Int32 /*nNewState*/ )
182 void SAL_CALL SfxInPlaceClient_Impl::stateChanged(
183 const css::lang::EventObject& /*aEvent*/,
184 ::sal_Int32 nOldState,
185 ::sal_Int32 nNewState )
187 if ( m_pClient && nOldState != embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
189 // deactivation of object
190 uno::Reference< frame::XModel > xDocument;
191 if ( m_pClient->GetViewShell()->GetObjectShell() )
192 xDocument = m_pClient->GetViewShell()->GetObjectShell()->GetModel();
193 SfxObjectShell::SetCurrentComponent( xDocument );
197 void SAL_CALL SfxInPlaceClient_Impl::notifyEvent( const document::EventObject& aEvent )
199 SolarMutexGuard aGuard;
201 if ( m_pClient && aEvent.EventName == "OnVisAreaChanged" && m_nAspect != embed::Aspects::MSOLE_ICON )
203 m_pClient->FormatChanged(); // for Writer when format of the object is changed with the area
204 m_pClient->ViewChanged();
205 m_pClient->Invalidate();
209 void SAL_CALL SfxInPlaceClient_Impl::disposing( const css::lang::EventObject& /*aEvent*/ )
211 delete m_pClient;
212 m_pClient = nullptr;
215 // XEmbeddedClient
217 uno::Reference < frame::XFrame > const & SfxInPlaceClient_Impl::GetFrame() const
219 if ( !m_pClient )
220 throw uno::RuntimeException();
221 return m_pClient->GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
224 void SAL_CALL SfxInPlaceClient_Impl::saveObject()
226 if (!m_bStoreObject || (m_pClient && m_pClient->IsProtected()))
227 // client wants to discard the object (usually it means the container document is closed while an object is active
228 // and the user didn't request saving the changes
229 return;
231 // the common persistence is supported by objects and links
232 uno::Reference< embed::XCommonEmbedPersist > xPersist( m_xObject, uno::UNO_QUERY_THROW );
234 uno::Reference< frame::XFrame > xFrame;
235 uno::Reference< task::XStatusIndicator > xStatusIndicator;
236 uno::Reference< frame::XModel > xModel( m_xObject->getComponent(), uno::UNO_QUERY );
237 const uno::Reference< uno::XComponentContext >& xContext( ::comphelper::getProcessComponentContext() );
239 if ( xModel.is() )
241 uno::Reference< frame::XController > xController = xModel->getCurrentController();
242 if ( xController.is() )
243 xFrame = xController->getFrame();
246 if ( xFrame.is() )
248 // set non-reschedule progress to prevent problems when asynchronous calls are made
249 // during storing of the embedded object
250 uno::Reference< task::XStatusIndicatorFactory > xStatusIndicatorFactory =
251 task::StatusIndicatorFactory::createWithFrame( xContext, xFrame, true/*DisableReschedule*/, false/*AllowParentShow*/ );
253 uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
254 if ( xPropSet.is() )
258 xStatusIndicator = xStatusIndicatorFactory->createStatusIndicator();
259 xPropSet->setPropertyValue( u"IndicatorInterception"_ustr , uno::Any( xStatusIndicator ));
261 catch ( const uno::RuntimeException& )
263 throw;
265 catch ( uno::Exception& )
273 xPersist->storeOwn();
274 m_xObject->update();
276 catch ( uno::Exception& )
278 //TODO/LATER: what should happen if object can't be saved?!
281 // reset status indicator interception after storing
284 uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
285 if ( xPropSet.is() )
287 xStatusIndicator.clear();
288 xPropSet->setPropertyValue( u"IndicatorInterception"_ustr , uno::Any( xStatusIndicator ));
291 catch ( const uno::RuntimeException& )
293 throw;
295 catch ( uno::Exception& )
299 // the client can exist only in case there is a view shell
300 if ( !m_pClient || !m_pClient->GetViewShell() )
301 throw uno::RuntimeException();
303 SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
304 if ( !pDocShell )
305 throw uno::RuntimeException();
307 pDocShell->SetModified();
309 //TODO/LATER: invalidation might be necessary when object was modified, but is not
310 //saved through this method
311 // m_pClient->Invalidate();
315 void SAL_CALL SfxInPlaceClient_Impl::visibilityChanged( sal_Bool bVisible )
317 SolarMutexGuard aGuard;
319 if ( !m_pClient || !m_pClient->GetViewShell() )
320 throw uno::RuntimeException();
322 m_pClient->GetViewShell()->OutplaceActivated( bVisible );
323 if (m_pClient) // it can change in the above code
324 m_pClient->Invalidate();
328 // XInplaceClient
330 sal_Bool SAL_CALL SfxInPlaceClient_Impl::canInplaceActivate()
332 if ( !m_xObject.is() )
333 throw uno::RuntimeException();
335 // we don't want to switch directly from outplace to inplace mode
336 if ( m_xObject->getCurrentState() == embed::EmbedStates::ACTIVE || m_nAspect == embed::Aspects::MSOLE_ICON )
337 return false;
339 return true;
343 void SAL_CALL SfxInPlaceClient_Impl::activatingInplace()
345 if ( !m_pClient || !m_pClient->GetViewShell() )
346 throw uno::RuntimeException();
348 if ( !comphelper::LibreOfficeKit::isActive() )
349 return;
351 if ( SfxViewShell* pViewShell = m_pClient->GetViewShell() )
353 tools::Rectangle aRect(m_pClient->GetObjArea());
355 if (m_pClient->GetEditWin())
357 if (m_pClient->GetEditWin()->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
358 aRect = o3tl::convert(aRect, o3tl::Length::mm100, o3tl::Length::twip);
361 OString str = (m_bNegativeX ? lcl_negateRectX(aRect) : aRect).toString() + ", \"INPLACE\"";
362 pViewShell->libreOfficeKitViewCallback( LOK_CALLBACK_GRAPHIC_SELECTION, str );
368 void SAL_CALL SfxInPlaceClient_Impl::activatingUI()
370 if ( !m_pClient || !m_pClient->GetViewShell() )
371 throw uno::RuntimeException();
373 m_pClient->GetViewShell()->ResetAllClients_Impl(m_pClient);
374 m_bUIActive = true;
375 m_pClient->GetViewShell()->UIActivating( m_pClient );
379 void SAL_CALL SfxInPlaceClient_Impl::deactivatedInplace()
381 if ( !m_pClient || !m_pClient->GetViewShell() )
382 throw uno::RuntimeException();
384 if ( comphelper::LibreOfficeKit::isActive() )
386 if ( SfxViewShell* pViewShell = m_pClient->GetViewShell() ) {
387 pViewShell->libreOfficeKitViewCallback( LOK_CALLBACK_GRAPHIC_SELECTION, "INPLACE EXIT"_ostr );
393 void SAL_CALL SfxInPlaceClient_Impl::deactivatedUI()
395 if ( !m_pClient || !m_pClient->GetViewShell() )
396 throw uno::RuntimeException();
398 m_pClient->GetViewShell()->UIDeactivated( m_pClient );
399 m_bUIActive = false;
403 uno::Reference< css::frame::XLayoutManager > SAL_CALL SfxInPlaceClient_Impl::getLayoutManager()
405 uno::Reference < beans::XPropertySet > xFrame( GetFrame(), uno::UNO_QUERY_THROW );
407 uno::Reference< css::frame::XLayoutManager > xMan;
410 uno::Any aAny = xFrame->getPropertyValue( u"LayoutManager"_ustr );
411 aAny >>= xMan;
413 catch ( uno::Exception& ex )
415 css::uno::Any anyEx = cppu::getCaughtException();
416 throw css::lang::WrappedTargetRuntimeException( ex.Message,
417 nullptr, anyEx );
420 return xMan;
424 uno::Reference< frame::XDispatchProvider > SAL_CALL SfxInPlaceClient_Impl::getInplaceDispatchProvider()
426 return uno::Reference < frame::XDispatchProvider >( GetFrame(), uno::UNO_QUERY_THROW );
430 awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getPlacement()
432 if ( !m_pClient || !m_pClient->GetViewShell() )
433 throw uno::RuntimeException();
435 // apply scaling to object area and convert to pixels
436 tools::Rectangle aRealObjArea( m_aObjArea );
437 aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_aScaleWidth),
438 tools::Long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
440 vcl::Window* pEditWin = m_pClient->GetEditWin();
441 // In Writer and Impress the map mode is disabled. So when a chart is
442 // activated (for in place editing) we get the chart win size in 100th mm
443 // and any method that should return pixels returns 100th mm and the chart
444 // window map mode has a ~26.485 scale factor.
445 // All that does not fit with current implementation for handling chart
446 // editing in LOK.
447 if (comphelper::LibreOfficeKit::isActive())
449 bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
450 if (!bMapModeEnabled)
451 pEditWin->EnableMapMode();
452 aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
453 if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
454 pEditWin->EnableMapMode(false);
456 else
458 aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
461 return vcl::unohelper::ConvertToAWTRect(aRealObjArea);
465 awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getClipRectangle()
467 if ( !m_pClient || !m_pClient->GetViewShell() )
468 throw uno::RuntimeException();
470 // currently(?) same as placement
471 tools::Rectangle aRealObjArea( m_aObjArea );
472 aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_aScaleWidth),
473 tools::Long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
475 vcl::Window* pEditWin = m_pClient->GetEditWin();
476 // See comment for SfxInPlaceClient_Impl::getPlacement.
477 if (comphelper::LibreOfficeKit::isActive())
479 bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
480 if (!bMapModeEnabled)
481 pEditWin->EnableMapMode();
482 aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
483 if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
484 pEditWin->EnableMapMode(false);
486 else
488 aRealObjArea = pEditWin->LogicToPixel(aRealObjArea);
491 return vcl::unohelper::ConvertToAWTRect(aRealObjArea);
495 void SAL_CALL SfxInPlaceClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
497 if ( !m_pClient || !m_pClient->GetViewShell() )
498 throw uno::RuntimeException();
500 // TODO/MBA: keyboard accelerators
504 void SAL_CALL SfxInPlaceClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
506 if ( !m_pClient || !m_pClient->GetViewShell() )
507 throw uno::RuntimeException();
511 void SAL_CALL SfxInPlaceClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
513 uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
514 if ( !m_pClient || !m_pClient->GetEditWin() || !m_pClient->GetViewShell() )
515 throw uno::RuntimeException();
517 // check if the change is at least one pixel in size
518 awt::Rectangle aOldRect = getPlacement();
519 tools::Rectangle aNewPixelRect = vcl::unohelper::ConvertToVCLRect(aPosRect);
520 tools::Rectangle aOldPixelRect = vcl::unohelper::ConvertToVCLRect(aOldRect);
521 if ( aOldPixelRect == aNewPixelRect )
522 // nothing has changed
523 return;
525 // new scaled object area
526 tools::Rectangle aNewLogicRect = m_pClient->GetEditWin()->PixelToLogic( aNewPixelRect );
528 // all the size changes in this method should happen without scaling
529 // SfxBooleanFlagGuard aGuard( m_bResizeNoScale, sal_True );
531 // allow container to apply restrictions on the requested new area;
532 // the container might change the object view during size calculation;
533 // currently only writer does it
534 m_pClient->RequestNewObjectArea( aNewLogicRect);
536 if ( aNewLogicRect != m_pClient->GetScaledObjArea() )
538 // the calculation of the object area has not changed the object size
539 // it should be done here then
540 SfxBooleanFlagGuard aGuard( m_bResizeNoScale );
542 // new size of the object area without scaling
543 Size aNewObjSize( tools::Long( aNewLogicRect.GetWidth() / m_aScaleWidth ),
544 tools::Long( aNewLogicRect.GetHeight() / m_aScaleHeight ) );
546 // now remove scaling from new placement and keep this at the new object area
547 aNewLogicRect.SetSize( aNewObjSize );
548 m_aObjArea = aNewLogicRect;
550 // let the window size be recalculated
551 SizeHasChanged();
554 // notify container view about changes
555 m_pClient->ObjectAreaChanged();
558 // XComponentSupplier
560 uno::Reference< util::XCloseable > SAL_CALL SfxInPlaceClient_Impl::getComponent()
562 if ( !m_pClient || !m_pClient->GetViewShell() )
563 throw uno::RuntimeException();
565 SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
566 if ( !pDocShell )
567 throw uno::RuntimeException();
569 // all the components must implement XCloseable
570 uno::Reference< util::XCloseable > xComp( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
571 return xComp;
575 // XWindowSupplier
577 uno::Reference< awt::XWindow > SAL_CALL SfxInPlaceClient_Impl::getWindow()
579 if ( !m_pClient || !m_pClient->GetEditWin() )
580 throw uno::RuntimeException();
582 uno::Reference< awt::XWindow > xWin( m_pClient->GetEditWin()->GetComponentInterface(), uno::UNO_QUERY );
583 return xWin;
587 // notification to the client implementation that either the object area or the scaling has been changed
588 // as a result the logical size of the window has changed also
589 void SfxInPlaceClient_Impl::SizeHasChanged()
591 if ( !m_pClient || !m_pClient->GetViewShell() )
592 throw uno::RuntimeException();
594 try {
595 if ( m_xObject.is()
596 && ( m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
597 || m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) )
599 // only possible in active states
600 uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
602 if ( m_bResizeNoScale )
604 // the resizing should be done without scaling
605 // set the correct size to the object to avoid the scaling
606 MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xObject->getMapUnit( m_nAspect ) ) );
607 MapMode aClientMap( m_pClient->GetEditWin()->GetMapMode().GetMapUnit() );
609 // convert to logical coordinates of the embedded object
610 Size aNewSize = m_pClient->GetEditWin()->LogicToLogic( m_aObjArea.GetSize(), &aClientMap, &aObjectMap );
611 m_xObject->setVisualAreaSize( m_nAspect, awt::Size( aNewSize.Width(), aNewSize.Height() ) );
614 xInplace->setObjectRectangles( getPlacement(), getClipRectangle() );
617 catch( uno::Exception& )
619 // TODO/LATER: handle error
624 IMPL_LINK_NOARG(SfxInPlaceClient_Impl, TimerHdl, Timer *, void)
626 if ( m_pClient && m_xObject.is() )
628 m_pClient->GetViewShell()->CheckIPClient_Impl(m_pClient,
629 m_pClient->GetViewShell()->GetObjectShell()->GetVisArea());
634 // SfxInPlaceClient
637 SfxInPlaceClient::SfxInPlaceClient( SfxViewShell* pViewShell, vcl::Window *pDraw, sal_Int64 nAspect ) :
638 m_xImp( new SfxInPlaceClient_Impl ),
639 m_pViewSh( pViewShell ),
640 m_pEditWin( pDraw )
642 m_xImp->m_pClient = this;
643 m_xImp->m_nAspect = nAspect;
644 m_xImp->m_aScaleWidth = m_xImp->m_aScaleHeight = Fraction(1,1);
645 pViewShell->NewIPClient_Impl(this);
646 m_xImp->m_aTimer.SetTimeout( SFX_CLIENTACTIVATE_TIMEOUT );
647 m_xImp->m_aTimer.SetInvokeHandler( LINK( m_xImp.get(), SfxInPlaceClient_Impl, TimerHdl ) );
651 SfxInPlaceClient::~SfxInPlaceClient()
653 m_pViewSh->IPClientGone_Impl(this);
655 // deleting the client before storing the object means discarding all changes
656 m_xImp->m_bStoreObject = false;
657 SetObject(nullptr);
659 m_xImp->m_pClient = nullptr;
661 // the next call will destroy m_xImp if no other reference to it exists
662 m_xImp.clear();
664 // TODO/LATER:
665 // the class is not intended to be used in multithreaded environment;
666 // if it will this disconnection and all the parts that use the m_pClient
667 // must be guarded with mutex
671 void SfxInPlaceClient::SetObjectState( sal_Int32 nState )
673 if ( !GetObject().is() )
674 return;
676 if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON
677 && ( nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::INPLACE_ACTIVE ) )
679 OSL_FAIL( "Iconified object should not be activated inplace!" );
680 return;
685 GetObject()->changeState( nState );
687 catch ( uno::Exception& )
692 sal_Int64 SfxInPlaceClient::GetObjectMiscStatus() const
694 if ( GetObject().is() )
695 return GetObject()->getStatus( m_xImp->m_nAspect );
696 return 0;
700 const uno::Reference < embed::XEmbeddedObject >& SfxInPlaceClient::GetObject() const
702 return m_xImp->m_xObject;
706 void SfxInPlaceClient::SetObject( const uno::Reference < embed::XEmbeddedObject >& rObject )
708 if ( m_xImp->m_xObject.is() && rObject != m_xImp->m_xObject )
710 DBG_ASSERT( GetObject()->getClientSite() == getXWeak(m_xImp.get()), "Wrong ClientSite!" );
711 if ( GetObject()->getClientSite() == getXWeak(m_xImp.get()) )
713 if ( GetObject()->getCurrentState() != embed::EmbedStates::LOADED )
714 SetObjectState( embed::EmbedStates::RUNNING );
715 m_xImp->m_xObject->removeEventListener( m_xImp );
716 m_xImp->m_xObject->removeStateChangeListener( m_xImp );
719 m_xImp->m_xObject->setClientSite( nullptr );
721 catch( uno::Exception& )
723 OSL_FAIL( "Can not clean the client site!" );
728 if ( m_pViewSh->GetViewFrame().GetFrame().IsClosing_Impl() )
729 // sometimes applications reconnect clients on shutting down because it happens in their Paint methods
730 return;
732 m_xImp->m_xObject = rObject;
734 if ( rObject.is() )
736 // as soon as an object was connected to a client it has to be checked whether the object wants
737 // to be activated
738 rObject->addStateChangeListener( m_xImp );
739 rObject->addEventListener( m_xImp );
743 rObject->setClientSite( m_xImp );
745 catch( uno::Exception& )
747 OSL_FAIL( "Can not set the client site!" );
750 m_xImp->m_aTimer.Start();
752 else
753 m_xImp->m_aTimer.Stop();
757 bool SfxInPlaceClient::SetObjArea( const tools::Rectangle& rArea )
759 if( rArea != m_xImp->m_aObjArea )
761 m_xImp->m_aObjArea = rArea;
762 m_xImp->SizeHasChanged();
764 Invalidate();
765 return true;
768 return false;
772 const tools::Rectangle& SfxInPlaceClient::GetObjArea() const
774 return m_xImp->m_aObjArea;
777 tools::Rectangle SfxInPlaceClient::GetScaledObjArea() const
779 tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
780 aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_xImp->m_aScaleWidth ),
781 tools::Long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
782 return aRealObjArea;
786 void SfxInPlaceClient::SetSizeScale( const Fraction & rScaleWidth, const Fraction & rScaleHeight )
788 if ( m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
790 m_xImp->m_aScaleWidth = rScaleWidth;
791 m_xImp->m_aScaleHeight = rScaleHeight;
793 m_xImp->SizeHasChanged();
795 // TODO/LATER: Invalidate seems to trigger (wrong) recalculations of the ObjArea, so it's better
796 // not to call it here, but maybe it sounds reasonable to do so.
797 //Invalidate();
802 void SfxInPlaceClient::SetObjAreaAndScale( const tools::Rectangle& rArea, const Fraction& rScaleWidth, const Fraction& rScaleHeight )
804 if( rArea != m_xImp->m_aObjArea || m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
806 m_xImp->m_aObjArea = rArea;
807 m_xImp->m_aScaleWidth = rScaleWidth;
808 m_xImp->m_aScaleHeight = rScaleHeight;
810 m_xImp->SizeHasChanged();
812 Invalidate();
817 const Fraction& SfxInPlaceClient::GetScaleWidth() const
819 return m_xImp->m_aScaleWidth;
823 const Fraction& SfxInPlaceClient::GetScaleHeight() const
825 return m_xImp->m_aScaleHeight;
829 void SfxInPlaceClient::Invalidate()
831 // TODO/LATER: do we need both?
833 // the object area is provided in logical coordinates of the window but without scaling applied
834 tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
835 aRealObjArea.SetSize( Size( tools::Long( aRealObjArea.GetWidth() * m_xImp->m_aScaleWidth ),
836 tools::Long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
838 m_pEditWin->Invalidate( IsNegativeX() ? lcl_negateRectX(aRealObjArea) : aRealObjArea );
840 ViewChanged();
844 bool SfxInPlaceClient::IsObjectUIActive() const
846 try {
847 return ( m_xImp->m_xObject.is() && ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) );
849 catch( uno::Exception& )
852 return false;
856 bool SfxInPlaceClient::IsObjectInPlaceActive() const
858 try {
859 return(
861 m_xImp->m_xObject.is() &&
862 (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE)
863 ) ||
865 m_xImp->m_xObject.is() &&
866 (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE)
870 catch( uno::Exception& )
873 return false;
877 SfxInPlaceClient* SfxInPlaceClient::GetClient( SfxObjectShell const * pDoc, const css::uno::Reference < css::embed::XEmbeddedObject >& xObject )
879 for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc); pFrame; pFrame=SfxViewFrame::GetNext(*pFrame,pDoc) )
881 if( pFrame->GetViewShell() )
883 SfxInPlaceClient* pClient = pFrame->GetViewShell()->FindIPClient( xObject, nullptr );
884 if ( pClient )
885 return pClient;
889 return nullptr;
892 sal_Int64 SfxInPlaceClient::GetAspect() const
894 return m_xImp->m_nAspect;
897 ErrCodeMsg SfxInPlaceClient::DoVerb(sal_Int32 nVerb)
899 SfxErrorContext aEc(ERRCTX_SO_DOVERB, m_pViewSh->GetFrameWeld(), RID_SO_ERRCTX);
900 ErrCodeMsg nError = ERRCODE_NONE;
902 if ( m_xImp->m_xObject.is() )
904 bool bSaveCopyAs = false;
905 if ( nVerb == -8 ) // "Save Copy as..."
907 svt::EmbeddedObjectRef::TryRunningState( m_xImp->m_xObject );
908 // TODO/LATER: this special verb should disappear when outplace activation is completely available
909 uno::Reference< frame::XModel > xEmbModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
910 if ( xEmbModel.is() )
912 bSaveCopyAs = true;
916 SfxStoringHelper aHelper;
917 uno::Sequence< beans::PropertyValue > aDispatchArgs{
918 comphelper::makePropertyValue(u"SaveTo"_ustr, true)
921 aHelper.GUIStoreModel( xEmbModel,
922 u"SaveAs",
923 aDispatchArgs,
924 false,
925 SignatureState::NOSIGNATURES,
926 false );
928 catch( const task::ErrorCodeIOException& aErrorEx )
930 nError = ErrCode(aErrorEx.ErrCode);
932 catch( uno::Exception& )
934 nError = ERRCODE_IO_GENERAL;
935 // TODO/LATER: better error handling
940 if ( !bSaveCopyAs )
942 if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON )
944 // the common persistence is supported by objects and links
946 uno::Reference< embed::XEmbeddedOleObject > xEmbeddedOleObject( m_xImp->m_xObject, uno::UNO_QUERY );
948 if ( xEmbeddedOleObject.is() && (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW ))
949 nVerb = embed::EmbedVerbs::MS_OLEVERB_SHOW;
950 else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW )
951 nVerb = embed::EmbedVerbs::MS_OLEVERB_OPEN; // outplace activation
952 else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE
953 || nVerb == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE )
954 nError = ERRCODE_SO_GENERALERROR;
957 if ( !nError )
959 // See comment for SfxInPlaceClient_Impl::getPlacement.
960 vcl::Window* pEditWin = GetEditWin();
961 bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
962 if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled)
964 pEditWin->EnableMapMode();
966 m_pViewSh->GetViewFrame().GetFrame().LockResize_Impl(true);
969 m_xImp->m_xObject->setClientSite( m_xImp );
971 m_xImp->m_xObject->doVerb( nVerb );
973 catch ( embed::UnreachableStateException& e )
975 if (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW)
977 // a workaround for the default verb, usually makes sense for alien objects
980 m_xImp->m_xObject->doVerb( -9 ); // open own view, a workaround verb that is not visible
982 if ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE )
984 // the object was converted to OOo object
985 awt::Size aSize = m_xImp->m_xObject->getVisualAreaSize( m_xImp->m_nAspect );
986 MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xImp->m_xObject->getMapUnit( m_xImp->m_nAspect ) ) );
987 MapMode aClientMap( GetEditWin()->GetMapMode().GetMapUnit() );
988 Size aNewSize = GetEditWin()->LogicToLogic( Size( aSize.Width, aSize.Height ), &aObjectMap, &aClientMap );
990 tools::Rectangle aScaledArea = GetScaledObjArea();
991 m_xImp->m_aObjArea.SetSize( aNewSize );
992 m_xImp->m_aScaleWidth = Fraction( aScaledArea.GetWidth(), aNewSize.Width() );
993 m_xImp->m_aScaleHeight = Fraction( aScaledArea.GetHeight(), aNewSize.Height() );
996 catch (uno::Exception const&)
998 TOOLS_WARN_EXCEPTION("embeddedobj", "SfxInPlaceClient::DoVerb: -9 fallback path");
999 nError = ErrCodeMsg(ERRCODE_SO_GENERALERROR, e.Message);
1003 catch ( embed::StateChangeInProgressException& )
1005 // TODO/LATER: it would be nice to be able to provide the current target state outside
1006 nError = ERRCODE_SO_CANNOT_DOVERB_NOW;
1008 catch (uno::Exception const&)
1010 TOOLS_WARN_EXCEPTION("embeddedobj", "SfxInPlaceClient::DoVerb");
1011 nError = ERRCODE_SO_GENERALERROR;
1012 //TODO/LATER: better error handling
1015 if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled
1016 && pEditWin->IsMapModeEnabled())
1018 pEditWin->EnableMapMode(false);
1020 SfxViewFrame& rFrame = m_pViewSh->GetViewFrame();
1021 rFrame.GetFrame().LockResize_Impl(false);
1022 rFrame.GetFrame().Resize();
1027 if( nError )
1028 ErrorHandler::HandleError( nError );
1030 return nError;
1033 void SfxInPlaceClient::VisAreaChanged()
1035 uno::Reference < embed::XInplaceObject > xObj( m_xImp->m_xObject, uno::UNO_QUERY );
1036 if ( xObj.is() )
1037 m_xImp->SizeHasChanged();
1040 void SfxInPlaceClient::ObjectAreaChanged()
1042 // dummy implementation
1045 void SfxInPlaceClient::RequestNewObjectArea( tools::Rectangle& )
1047 // dummy implementation
1050 void SfxInPlaceClient::ViewChanged()
1052 // dummy implementation
1055 void SfxInPlaceClient::FormatChanged()
1057 // dummy implementation
1060 bool SfxInPlaceClient::IsProtected() const { return false; }
1062 void SfxInPlaceClient::DeactivateObject()
1064 if ( !GetObject().is() )
1065 return;
1069 m_xImp->m_bUIActive = false;
1070 bool bHasFocus = false;
1071 uno::Reference< frame::XModel > xModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
1072 if ( xModel.is() )
1074 uno::Reference< frame::XController > xController = xModel->getCurrentController();
1075 if ( xController.is() )
1077 VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xController->getFrame()->getContainerWindow() );
1078 bHasFocus = pWindow->HasChildPathFocus( true );
1082 m_pViewSh->GetViewFrame().GetFrame().LockResize_Impl(true);
1084 if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
1086 m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
1087 if (bHasFocus)
1088 m_pViewSh->GetWindow()->GrabFocus();
1090 else
1092 // the links should not stay in running state for long time because of locking
1093 uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
1094 if ( xLink.is() && xLink->isLink() )
1095 m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
1096 else
1097 m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
1100 SfxViewFrame& rFrame = m_pViewSh->GetViewFrame();
1101 SfxViewFrame::SetViewFrame( &rFrame );
1102 rFrame.GetFrame().LockResize_Impl(false);
1103 rFrame.GetFrame().Resize();
1105 catch (css::uno::Exception& )
1109 void SfxInPlaceClient::ResetObject()
1111 if ( !GetObject().is() )
1112 return;
1116 m_xImp->m_bUIActive = false;
1117 if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
1118 m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
1119 else
1121 // the links should not stay in running state for long time because of locking
1122 uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
1123 if ( xLink.is() && xLink->isLink() )
1124 m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
1125 else
1126 m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
1129 catch (css::uno::Exception& )
1133 bool SfxInPlaceClient::IsUIActive() const
1135 return m_xImp->m_bUIActive;
1138 void SfxInPlaceClient::SetNegativeX(bool bSet)
1140 m_xImp->m_bNegativeX = bSet;
1143 bool SfxInPlaceClient::IsNegativeX() const
1145 return m_xImp->m_bNegativeX;
1148 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */