xref: /core/sfx2/source/view/ipclient.cxx (revision 8411b80d)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <com/sun/star/embed/EmbedStates.hpp>
21 #include <com/sun/star/embed/UnreachableStateException.hpp>
22 #include <com/sun/star/embed/XVisualObject.hpp>
23 #include <com/sun/star/embed/XEmbeddedClient.hpp>
24 #include <com/sun/star/embed/XInplaceClient.hpp>
25 #include <com/sun/star/embed/XInplaceObject.hpp>
26 #include <com/sun/star/embed/XComponentSupplier.hpp>
27 #include <com/sun/star/embed/XWindowSupplier.hpp>
28 #include <com/sun/star/embed/XEmbedPersist.hpp>
29 #include <com/sun/star/embed/EmbedVerbs.hpp>
30 #include <com/sun/star/embed/XEmbeddedOleObject.hpp>
31 #include <com/sun/star/container/XChild.hpp>
32 #include <com/sun/star/beans/XPropertySet.hpp>
33 #include <com/sun/star/beans/PropertyValue.hpp>
34 #include <com/sun/star/embed/XStateChangeListener.hpp>
35 #include <com/sun/star/embed/StateChangeInProgressException.hpp>
36 #include <com/sun/star/embed/XLinkageSupport.hpp>
37 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
38 #include <com/sun/star/lang/XInitialization.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>
42 
43 #include <com/sun/star/embed/EmbedMisc.hpp>
44 #include <svtools/embedhlp.hxx>
45 #include <vcl/svapp.hxx>
46 
47 #include <sfx2/ipclient.hxx>
48 #include <sfx2/viewsh.hxx>
49 #include <sfx2/viewfrm.hxx>
50 #include <sfx2/objsh.hxx>
51 #include <sfx2/dispatch.hxx>
52 #include <workwin.hxx>
53 #include <guisaveas.hxx>
54 #include <cppuhelper/implbase.hxx>
55 #include <svtools/ehdl.hxx>
56 #include <sal/log.hxx>
57 
58 #include <vcl/timer.hxx>
59 #include <vcl/window.hxx>
60 #include <toolkit/awt/vclxwindow.hxx>
61 #include <toolkit/helper/vclunohelper.hxx>
62 #include <toolkit/helper/convert.hxx>
63 #include <tools/fract.hxx>
64 #include <tools/gen.hxx>
65 #include <svl/rectitem.hxx>
66 #include <svtools/soerr.hxx>
67 #include <comphelper/lok.hxx>
68 #include <comphelper/processfactory.hxx>
69 #include <cppuhelper/exc_hlp.hxx>
70 
71 #include <sfx2/lokhelper.hxx>
72 
73 #define SFX_CLIENTACTIVATE_TIMEOUT 100
74 
75 using namespace com::sun::star;
76 
77 
78 // SfxEmbedResizeGuard
79 class SfxBooleanFlagGuard
80 {
81     bool& m_rFlag;
82 public:
83     explicit SfxBooleanFlagGuard(bool& bFlag)
84         : m_rFlag( bFlag )
85     {
86         m_rFlag = true;
87     }
88 
89     ~SfxBooleanFlagGuard()
90     {
91         m_rFlag = false;
92     }
93 };
94 
95 
96 // SfxInPlaceClient_Impl
97 
98 
99 class SfxInPlaceClient_Impl : public ::cppu::WeakImplHelper< embed::XEmbeddedClient,
100                                                               embed::XInplaceClient,
101                                                               document::XEventListener,
102                                                               embed::XStateChangeListener,
103                                                               embed::XWindowSupplier >
104 {
105 public:
106     Timer                           m_aTimer;               // activation timeout, starts after object connection
107     tools::Rectangle                       m_aObjArea;             // area of object in coordinate system of the container (without scaling)
108     Fraction                        m_aScaleWidth;          // scaling that was applied to the object when it was not active
109     Fraction                        m_aScaleHeight;
110     SfxInPlaceClient*               m_pClient;
111     sal_Int64                       m_nAspect;              // ViewAspect that is assigned from the container
112     bool                            m_bStoreObject;
113     bool                            m_bUIActive;            // set and cleared when notification for UI (de)activation is sent
114     bool                            m_bResizeNoScale;
115 
116     uno::Reference < embed::XEmbeddedObject > m_xObject;
117     uno::Reference < embed::XEmbeddedClient > m_xClient;
118 
119 
120     SfxInPlaceClient_Impl()
121     : m_pClient( nullptr )
122     , m_nAspect( 0 )
123     , m_bStoreObject( true )
124     , m_bUIActive( false )
125     , m_bResizeNoScale( false )
126     {}
127 
128     void SizeHasChanged();
129     DECL_LINK(TimerHdl, Timer *, void);
130     uno::Reference < frame::XFrame > const & GetFrame() const;
131 
132     // XEmbeddedClient
133     virtual void SAL_CALL saveObject() override;
134     virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) override;
135 
136     // XInplaceClient
137     virtual sal_Bool SAL_CALL canInplaceActivate() override;
138     virtual void SAL_CALL activatingInplace() override;
139     virtual void SAL_CALL activatingUI() override;
140     virtual void SAL_CALL deactivatedInplace() override;
141     virtual void SAL_CALL deactivatedUI() override;
142     virtual uno::Reference< css::frame::XLayoutManager > SAL_CALL getLayoutManager() override;
143     virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() override;
144     virtual awt::Rectangle SAL_CALL getPlacement() override;
145     virtual awt::Rectangle SAL_CALL getClipRectangle() override;
146     virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) override;
147     virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) override;
148     virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) override;
149 
150     // XComponentSupplier
151     virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() override;
152 
153     // XWindowSupplier
154     virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() override;
155 
156     // document::XEventListener
157     virtual void SAL_CALL       notifyEvent( const document::EventObject& aEvent ) override;
158 
159     // XStateChangeListener
160     virtual void SAL_CALL changingState( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
161     virtual void SAL_CALL stateChanged( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
162     virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
163 };
164 
165 void SAL_CALL SfxInPlaceClient_Impl::changingState(
166     const css::lang::EventObject& /*aEvent*/,
167     ::sal_Int32 /*nOldState*/,
168     ::sal_Int32 /*nNewState*/ )
169 {
170 }
171 
172 void SAL_CALL SfxInPlaceClient_Impl::stateChanged(
173     const css::lang::EventObject& /*aEvent*/,
174     ::sal_Int32 nOldState,
175     ::sal_Int32 nNewState )
176 {
177     if ( m_pClient && nOldState != embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
178     {
179         // deactivation of object
180         uno::Reference< frame::XModel > xDocument;
181         if ( m_pClient->GetViewShell()->GetObjectShell() )
182             xDocument = m_pClient->GetViewShell()->GetObjectShell()->GetModel();
183         SfxObjectShell::SetCurrentComponent( xDocument );
184     }
185 }
186 
187 void SAL_CALL SfxInPlaceClient_Impl::notifyEvent( const document::EventObject& aEvent )
188 {
189     SolarMutexGuard aGuard;
190 
191     if ( m_pClient && aEvent.EventName == "OnVisAreaChanged" && m_nAspect != embed::Aspects::MSOLE_ICON )
192     {
193         m_pClient->FormatChanged(); // for Writer when format of the object is changed with the area
194         m_pClient->ViewChanged();
195         m_pClient->Invalidate();
196     }
197 }
198 
199 void SAL_CALL SfxInPlaceClient_Impl::disposing( const css::lang::EventObject& /*aEvent*/ )
200 {
201     DELETEZ( m_pClient );
202 }
203 
204 // XEmbeddedClient
205 
206 uno::Reference < frame::XFrame > const & SfxInPlaceClient_Impl::GetFrame() const
207 {
208     if ( !m_pClient )
209         throw uno::RuntimeException();
210     return m_pClient->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
211 }
212 
213 void SAL_CALL SfxInPlaceClient_Impl::saveObject()
214 {
215     if ( !m_bStoreObject )
216         // client wants to discard the object (usually it means the container document is closed while an object is active
217         // and the user didn't request saving the changes
218         return;
219 
220     // the common persistence is supported by objects and links
221     uno::Reference< embed::XCommonEmbedPersist > xPersist( m_xObject, uno::UNO_QUERY_THROW );
222 
223     uno::Reference< frame::XFrame >              xFrame;
224     uno::Reference< task::XStatusIndicator >     xStatusIndicator;
225     uno::Reference< frame::XModel >              xModel( m_xObject->getComponent(), uno::UNO_QUERY );
226     uno::Reference< uno::XComponentContext >     xContext( ::comphelper::getProcessComponentContext() );
227 
228     if ( xModel.is() )
229     {
230         uno::Reference< frame::XController > xController = xModel->getCurrentController();
231         if ( xController.is() )
232             xFrame = xController->getFrame();
233     }
234 
235     if ( xFrame.is() )
236     {
237         // set non-reschedule progress to prevent problems when asynchronous calls are made
238         // during storing of the embedded object
239         uno::Reference< task::XStatusIndicatorFactory > xStatusIndicatorFactory =
240                task::StatusIndicatorFactory::createWithFrame( xContext, xFrame, true/*DisableReschedule*/, false/*AllowParentShow*/ );
241 
242         uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
243         if ( xPropSet.is() )
244         {
245             try
246             {
247                 xStatusIndicator = xStatusIndicatorFactory->createStatusIndicator();
248                 xPropSet->setPropertyValue( "IndicatorInterception" , uno::makeAny( xStatusIndicator ));
249             }
250             catch ( const uno::RuntimeException& )
251             {
252                 throw;
253             }
254             catch ( uno::Exception& )
255             {
256             }
257         }
258     }
259 
260     try
261     {
262         xPersist->storeOwn();
263         m_xObject->update();
264     }
265     catch ( uno::Exception& )
266     {
267         //TODO/LATER: what should happen if object can't be saved?!
268     }
269 
270     // reset status indicator interception after storing
271     try
272     {
273         uno::Reference< beans::XPropertySet > xPropSet( xFrame, uno::UNO_QUERY );
274         if ( xPropSet.is() )
275         {
276             xStatusIndicator.clear();
277             xPropSet->setPropertyValue( "IndicatorInterception" , uno::makeAny( xStatusIndicator ));
278         }
279     }
280     catch ( const uno::RuntimeException& )
281     {
282         throw;
283     }
284     catch ( uno::Exception& )
285     {
286     }
287 
288     // the client can exist only in case there is a view shell
289     if ( !m_pClient || !m_pClient->GetViewShell() )
290         throw uno::RuntimeException();
291 
292     SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
293     if ( !pDocShell )
294         throw uno::RuntimeException();
295 
296     pDocShell->SetModified();
297 
298     //TODO/LATER: invalidation might be necessary when object was modified, but is not
299     //saved through this method
300     // m_pClient->Invalidate();
301 }
302 
303 
304 void SAL_CALL SfxInPlaceClient_Impl::visibilityChanged( sal_Bool bVisible )
305 {
306     SolarMutexGuard aGuard;
307 
308     if ( !m_pClient || !m_pClient->GetViewShell() )
309         throw uno::RuntimeException();
310 
311     m_pClient->GetViewShell()->OutplaceActivated( bVisible );
312     m_pClient->Invalidate();
313 }
314 
315 
316 // XInplaceClient
317 
318 sal_Bool SAL_CALL SfxInPlaceClient_Impl::canInplaceActivate()
319 {
320     if ( !m_xObject.is() )
321         throw uno::RuntimeException();
322 
323     // we don't want to switch directly from outplace to inplace mode
324     if ( m_xObject->getCurrentState() == embed::EmbedStates::ACTIVE || m_nAspect == embed::Aspects::MSOLE_ICON )
325         return false;
326 
327     return true;
328 }
329 
330 
331 void SAL_CALL SfxInPlaceClient_Impl::activatingInplace()
332 {
333     if ( !m_pClient || !m_pClient->GetViewShell() )
334         throw uno::RuntimeException();
335 }
336 
337 
338 void SAL_CALL SfxInPlaceClient_Impl::activatingUI()
339 {
340     if ( !m_pClient || !m_pClient->GetViewShell() )
341         throw uno::RuntimeException();
342 
343     m_pClient->GetViewShell()->ResetAllClients_Impl(m_pClient);
344     m_bUIActive = true;
345     m_pClient->GetViewShell()->UIActivating( m_pClient );
346 }
347 
348 
349 void SAL_CALL SfxInPlaceClient_Impl::deactivatedInplace()
350 {
351     if ( !m_pClient || !m_pClient->GetViewShell() )
352         throw uno::RuntimeException();
353 }
354 
355 
356 void SAL_CALL SfxInPlaceClient_Impl::deactivatedUI()
357 {
358     if ( !m_pClient || !m_pClient->GetViewShell() )
359         throw uno::RuntimeException();
360 
361     m_pClient->GetViewShell()->UIDeactivated( m_pClient );
362     m_bUIActive = false;
363 }
364 
365 
366 uno::Reference< css::frame::XLayoutManager > SAL_CALL SfxInPlaceClient_Impl::getLayoutManager()
367 {
368     uno::Reference < beans::XPropertySet > xFrame( GetFrame(), uno::UNO_QUERY_THROW );
369 
370     uno::Reference< css::frame::XLayoutManager > xMan;
371     try
372     {
373         uno::Any aAny = xFrame->getPropertyValue( "LayoutManager" );
374         aAny >>= xMan;
375     }
376     catch ( uno::Exception& ex )
377     {
378         css::uno::Any anyEx = cppu::getCaughtException();
379         throw css::lang::WrappedTargetRuntimeException( ex.Message,
380                         nullptr, anyEx );
381     }
382 
383     return xMan;
384 }
385 
386 
387 uno::Reference< frame::XDispatchProvider > SAL_CALL SfxInPlaceClient_Impl::getInplaceDispatchProvider()
388 {
389     return uno::Reference < frame::XDispatchProvider >( GetFrame(), uno::UNO_QUERY_THROW );
390 }
391 
392 
393 awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getPlacement()
394 {
395     if ( !m_pClient || !m_pClient->GetViewShell() )
396         throw uno::RuntimeException();
397 
398     // apply scaling to object area and convert to pixels
399     tools::Rectangle aRealObjArea( m_aObjArea );
400     aRealObjArea.SetSize( Size( long( aRealObjArea.GetWidth() * m_aScaleWidth),
401                                 long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
402 
403     // In Writer and Impress the map mode is disabled. So when a chart is
404     // activated (for in place editing) we get the chart win size in 100th mm
405     // and any method that should return pixels returns 100th mm and the chart
406     // window map mode has a ~26.485 scale factor.
407     // All that does not fit with current implementation for handling chart
408     // editing in LOK.
409     if (comphelper::LibreOfficeKit::isActive())
410     {
411         vcl::Window* pEditWin = m_pClient->GetEditWin();
412         bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
413         if (!bMapModeEnabled)
414             pEditWin->EnableMapMode();
415         aRealObjArea = m_pClient->GetEditWin()->LogicToPixel( aRealObjArea );
416         if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
417             pEditWin->EnableMapMode(false);
418     }
419     else
420     {
421         aRealObjArea = m_pClient->GetEditWin()->LogicToPixel( aRealObjArea );
422     }
423 
424     return AWTRectangle( aRealObjArea );
425 }
426 
427 
428 awt::Rectangle SAL_CALL SfxInPlaceClient_Impl::getClipRectangle()
429 {
430     if ( !m_pClient || !m_pClient->GetViewShell() )
431         throw uno::RuntimeException();
432 
433     // currently(?) same as placement
434     tools::Rectangle aRealObjArea( m_aObjArea );
435     aRealObjArea.SetSize( Size( long( aRealObjArea.GetWidth() * m_aScaleWidth),
436                                 long( aRealObjArea.GetHeight() * m_aScaleHeight) ) );
437 
438     // See comment for SfxInPlaceClient_Impl::getPlacement.
439     if (comphelper::LibreOfficeKit::isActive())
440     {
441         vcl::Window* pEditWin = m_pClient->GetEditWin();
442         bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
443         if (!bMapModeEnabled)
444             pEditWin->EnableMapMode();
445         aRealObjArea = m_pClient->GetEditWin()->LogicToPixel( aRealObjArea );
446         if (!bMapModeEnabled && pEditWin->IsMapModeEnabled())
447             pEditWin->EnableMapMode(false);
448     }
449     else
450     {
451         aRealObjArea = m_pClient->GetEditWin()->LogicToPixel( aRealObjArea );
452     }
453 
454     return AWTRectangle( aRealObjArea );
455 }
456 
457 
458 void SAL_CALL SfxInPlaceClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
459 {
460     if ( !m_pClient || !m_pClient->GetViewShell() )
461         throw uno::RuntimeException();
462 
463     // TODO/MBA: keyboard accelerators
464 }
465 
466 
467 void SAL_CALL SfxInPlaceClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
468 {
469     if ( !m_pClient || !m_pClient->GetViewShell() )
470         throw uno::RuntimeException();
471 }
472 
473 
474 void SAL_CALL SfxInPlaceClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
475 {
476     uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
477     if ( !m_pClient || !m_pClient->GetEditWin() || !m_pClient->GetViewShell() )
478         throw uno::RuntimeException();
479 
480     // check if the change is at least one pixel in size
481     awt::Rectangle aOldRect = getPlacement();
482     tools::Rectangle aNewPixelRect = VCLRectangle( aPosRect );
483     tools::Rectangle aOldPixelRect = VCLRectangle( aOldRect );
484     if ( aOldPixelRect == aNewPixelRect )
485         // nothing has changed
486         return;
487 
488     // new scaled object area
489     tools::Rectangle aNewLogicRect = m_pClient->GetEditWin()->PixelToLogic( aNewPixelRect );
490 
491     // all the size changes in this method should happen without scaling
492     // SfxBooleanFlagGuard aGuard( m_bResizeNoScale, sal_True );
493 
494     // allow container to apply restrictions on the requested new area;
495     // the container might change the object view during size calculation;
496     // currently only writer does it
497     m_pClient->RequestNewObjectArea( aNewLogicRect);
498 
499     if ( aNewLogicRect != m_pClient->GetScaledObjArea() )
500     {
501         // the calculation of the object area has not changed the object size
502         // it should be done here then
503         SfxBooleanFlagGuard aGuard( m_bResizeNoScale );
504 
505         // new size of the object area without scaling
506         Size aNewObjSize( long( aNewLogicRect.GetWidth()  / m_aScaleWidth ),
507                           long( aNewLogicRect.GetHeight() / m_aScaleHeight ) );
508 
509         // now remove scaling from new placement and keep this a the new object area
510         aNewLogicRect.SetSize( aNewObjSize );
511         m_aObjArea = aNewLogicRect;
512 
513         // let the window size be recalculated
514         SizeHasChanged();
515     }
516 
517     // notify container view about changes
518     m_pClient->ObjectAreaChanged();
519 }
520 
521 // XComponentSupplier
522 
523 uno::Reference< util::XCloseable > SAL_CALL SfxInPlaceClient_Impl::getComponent()
524 {
525     if ( !m_pClient || !m_pClient->GetViewShell() )
526         throw uno::RuntimeException();
527 
528     SfxObjectShell* pDocShell = m_pClient->GetViewShell()->GetObjectShell();
529     if ( !pDocShell )
530         throw uno::RuntimeException();
531 
532     // all the components must implement XCloseable
533     uno::Reference< util::XCloseable > xComp( pDocShell->GetModel(), uno::UNO_QUERY_THROW );
534     return xComp;
535 }
536 
537 
538 // XWindowSupplier
539 
540 uno::Reference< awt::XWindow > SAL_CALL SfxInPlaceClient_Impl::getWindow()
541 {
542     if ( !m_pClient || !m_pClient->GetEditWin() )
543         throw uno::RuntimeException();
544 
545     uno::Reference< awt::XWindow > xWin( m_pClient->GetEditWin()->GetComponentInterface(), uno::UNO_QUERY );
546     return xWin;
547 }
548 
549 
550 // notification to the client implementation that either the object area or the scaling has been changed
551 // as a result the logical size of the window has changed also
552 void SfxInPlaceClient_Impl::SizeHasChanged()
553 {
554     if ( !m_pClient || !m_pClient->GetViewShell() )
555         throw uno::RuntimeException();
556 
557     try {
558         if ( m_xObject.is()
559           && ( m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
560                 || m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) )
561         {
562             // only possible in active states
563             uno::Reference< embed::XInplaceObject > xInplace( m_xObject, uno::UNO_QUERY_THROW );
564 
565             if ( m_bResizeNoScale )
566             {
567                 // the resizing should be done without scaling
568                 // set the correct size to the object to avoid the scaling
569                 MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xObject->getMapUnit( m_nAspect ) ) );
570                 MapMode aClientMap( m_pClient->GetEditWin()->GetMapMode().GetMapUnit() );
571 
572                 // convert to logical coordinates of the embedded object
573                 Size aNewSize = m_pClient->GetEditWin()->LogicToLogic( m_aObjArea.GetSize(), &aClientMap, &aObjectMap );
574                 m_xObject->setVisualAreaSize( m_nAspect, awt::Size( aNewSize.Width(), aNewSize.Height() ) );
575             }
576 
577             xInplace->setObjectRectangles( getPlacement(), getClipRectangle() );
578         }
579     }
580     catch( uno::Exception& )
581     {
582         // TODO/LATER: handle error
583     }
584 }
585 
586 
587 IMPL_LINK_NOARG(SfxInPlaceClient_Impl, TimerHdl, Timer *, void)
588 {
589     if ( m_pClient && m_xObject.is() )
590     {
591         m_pClient->GetViewShell()->CheckIPClient_Impl(m_pClient,
592                 m_pClient->GetViewShell()->GetObjectShell()->GetVisArea());
593     }
594 }
595 
596 
597 // SfxInPlaceClient
598 
599 
600 SfxInPlaceClient::SfxInPlaceClient( SfxViewShell* pViewShell, vcl::Window *pDraw, sal_Int64 nAspect ) :
601     m_xImp( new SfxInPlaceClient_Impl ),
602     m_pViewSh( pViewShell ),
603     m_pEditWin( pDraw )
604 {
605     m_xImp->m_pClient = this;
606     m_xImp->m_nAspect = nAspect;
607     m_xImp->m_aScaleWidth = m_xImp->m_aScaleHeight = Fraction(1,1);
608     m_xImp->m_xClient = static_cast< embed::XEmbeddedClient* >( m_xImp.get() );
609     pViewShell->NewIPClient_Impl(this);
610     m_xImp->m_aTimer.SetDebugName( "sfx::SfxInPlaceClient m_xImpl::m_aTimer" );
611     m_xImp->m_aTimer.SetTimeout( SFX_CLIENTACTIVATE_TIMEOUT );
612     m_xImp->m_aTimer.SetInvokeHandler( LINK( m_xImp.get(), SfxInPlaceClient_Impl, TimerHdl ) );
613 }
614 
615 
616 SfxInPlaceClient::~SfxInPlaceClient()
617 {
618     m_pViewSh->IPClientGone_Impl(this);
619 
620     // deleting the client before storing the object means discarding all changes
621     m_xImp->m_bStoreObject = false;
622     SetObject(nullptr);
623 
624     m_xImp->m_pClient = nullptr;
625 
626     // the next call will destroy m_xImp if no other reference to it exists
627     m_xImp->m_xClient.clear();
628 
629     // TODO/LATER:
630     // the class is not intended to be used in multithreaded environment;
631     // if it will this disconnection and all the parts that use the m_pClient
632     // must be guarded with mutex
633 }
634 
635 
636 void SfxInPlaceClient::SetObjectState( sal_Int32 nState )
637 {
638     if ( GetObject().is() )
639     {
640         if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON
641           && ( nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::INPLACE_ACTIVE ) )
642         {
643             OSL_FAIL( "Iconified object should not be activated inplace!" );
644             return;
645         }
646 
647         try
648         {
649             GetObject()->changeState( nState );
650         }
651         catch ( uno::Exception& )
652         {}
653     }
654 }
655 
656 
657 sal_Int64 SfxInPlaceClient::GetObjectMiscStatus() const
658 {
659     if ( GetObject().is() )
660         return GetObject()->getStatus( m_xImp->m_nAspect );
661     return 0;
662 }
663 
664 
665 const uno::Reference < embed::XEmbeddedObject >& SfxInPlaceClient::GetObject() const
666 {
667     return m_xImp->m_xObject;
668 }
669 
670 
671 void SfxInPlaceClient::SetObject( const uno::Reference < embed::XEmbeddedObject >& rObject )
672 {
673     if ( m_xImp->m_xObject.is() && rObject != m_xImp->m_xObject )
674     {
675         DBG_ASSERT( GetObject()->getClientSite() == m_xImp->m_xClient, "Wrong ClientSite!" );
676         if ( GetObject()->getClientSite() == m_xImp->m_xClient )
677         {
678             if ( GetObject()->getCurrentState() != embed::EmbedStates::LOADED )
679                 SetObjectState( embed::EmbedStates::RUNNING );
680             m_xImp->m_xObject->removeEventListener( uno::Reference < document::XEventListener >( m_xImp->m_xClient, uno::UNO_QUERY ) );
681             m_xImp->m_xObject->removeStateChangeListener( uno::Reference < embed::XStateChangeListener >( m_xImp->m_xClient, uno::UNO_QUERY ) );
682             try
683             {
684                 m_xImp->m_xObject->setClientSite( nullptr );
685             }
686             catch( uno::Exception& )
687             {
688                 OSL_FAIL( "Can not clean the client site!" );
689             }
690         }
691     }
692 
693     if ( m_pViewSh->GetViewFrame()->GetFrame().IsClosing_Impl() )
694         // sometimes applications reconnect clients on shutting down because it happens in their Paint methods
695         return;
696 
697     m_xImp->m_xObject = rObject;
698 
699     if ( rObject.is() )
700     {
701         // as soon as an object was connected to a client it has to be checked whether the object wants
702         // to be activated
703         rObject->addStateChangeListener( uno::Reference < embed::XStateChangeListener >( m_xImp->m_xClient, uno::UNO_QUERY ) );
704         rObject->addEventListener( uno::Reference < document::XEventListener >( m_xImp->m_xClient, uno::UNO_QUERY ) );
705 
706         try
707         {
708             rObject->setClientSite( m_xImp->m_xClient );
709         }
710         catch( uno::Exception& )
711         {
712             OSL_FAIL( "Can not set the client site!" );
713         }
714 
715         m_xImp->m_aTimer.Start();
716     }
717     else
718         m_xImp->m_aTimer.Stop();
719 }
720 
721 
722 bool SfxInPlaceClient::SetObjArea( const tools::Rectangle& rArea )
723 {
724     if( rArea != m_xImp->m_aObjArea )
725     {
726         m_xImp->m_aObjArea = rArea;
727         m_xImp->SizeHasChanged();
728 
729         Invalidate();
730         return true;
731     }
732 
733     return false;
734 }
735 
736 
737 const tools::Rectangle& SfxInPlaceClient::GetObjArea() const
738 {
739     return m_xImp->m_aObjArea;
740 }
741 
742 tools::Rectangle SfxInPlaceClient::GetScaledObjArea() const
743 {
744     tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
745     aRealObjArea.SetSize( Size( long( aRealObjArea.GetWidth()  * m_xImp->m_aScaleWidth ),
746                                 long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
747     return aRealObjArea;
748 }
749 
750 
751 void SfxInPlaceClient::SetSizeScale( const Fraction & rScaleWidth, const Fraction & rScaleHeight )
752 {
753     if ( m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
754     {
755         m_xImp->m_aScaleWidth = rScaleWidth;
756         m_xImp->m_aScaleHeight = rScaleHeight;
757 
758         m_xImp->SizeHasChanged();
759 
760         // TODO/LATER: Invalidate seems to trigger (wrong) recalculations of the ObjArea, so it's better
761         // not to call it here, but maybe it sounds reasonable to do so.
762         //Invalidate();
763     }
764 }
765 
766 
767 void SfxInPlaceClient::SetObjAreaAndScale( const tools::Rectangle& rArea, const Fraction& rScaleWidth, const Fraction& rScaleHeight )
768 {
769     if( rArea != m_xImp->m_aObjArea || m_xImp->m_aScaleWidth != rScaleWidth || m_xImp->m_aScaleHeight != rScaleHeight )
770     {
771         m_xImp->m_aObjArea = rArea;
772         m_xImp->m_aScaleWidth = rScaleWidth;
773         m_xImp->m_aScaleHeight = rScaleHeight;
774 
775         m_xImp->SizeHasChanged();
776 
777         Invalidate();
778     }
779 }
780 
781 
782 const Fraction& SfxInPlaceClient::GetScaleWidth() const
783 {
784     return m_xImp->m_aScaleWidth;
785 }
786 
787 
788 const Fraction& SfxInPlaceClient::GetScaleHeight() const
789 {
790     return m_xImp->m_aScaleHeight;
791 }
792 
793 
794 void SfxInPlaceClient::Invalidate()
795 {
796     // TODO/LATER: do we need both?
797 
798     // the object area is provided in logical coordinates of the window but without scaling applied
799     tools::Rectangle aRealObjArea( m_xImp->m_aObjArea );
800     aRealObjArea.SetSize( Size( long( aRealObjArea.GetWidth()  * m_xImp->m_aScaleWidth ),
801                                 long( aRealObjArea.GetHeight() * m_xImp->m_aScaleHeight ) ) );
802     m_pEditWin->Invalidate( aRealObjArea );
803 
804     ViewChanged();
805 }
806 
807 
808 bool SfxInPlaceClient::IsObjectUIActive() const
809 {
810     try {
811         return ( m_xImp->m_xObject.is() && ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE ) );
812     }
813     catch( uno::Exception& )
814     {}
815 
816     return false;
817 }
818 
819 
820 bool SfxInPlaceClient::IsObjectInPlaceActive() const
821 {
822     try {
823         return(
824                (
825                 m_xImp->m_xObject.is() &&
826                 (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE)
827                ) ||
828                (
829                 m_xImp->m_xObject.is() &&
830                 (m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE)
831                )
832               );
833     }
834     catch( uno::Exception& )
835     {}
836 
837     return false;
838 }
839 
840 
841 SfxInPlaceClient* SfxInPlaceClient::GetClient( SfxObjectShell const * pDoc, const css::uno::Reference < css::embed::XEmbeddedObject >& xObject )
842 {
843     for ( SfxViewFrame* pFrame = SfxViewFrame::GetFirst(pDoc); pFrame; pFrame=SfxViewFrame::GetNext(*pFrame,pDoc) )
844     {
845         if( pFrame->GetViewShell() )
846         {
847             SfxInPlaceClient* pClient = pFrame->GetViewShell()->FindIPClient( xObject, nullptr );
848             if ( pClient )
849                 return pClient;
850         }
851     }
852 
853     return nullptr;
854 }
855 
856 sal_Int64 SfxInPlaceClient::GetAspect() const
857 {
858     return m_xImp->m_nAspect;
859 }
860 
861 ErrCode SfxInPlaceClient::DoVerb( long nVerb )
862 {
863     vcl::Window* pWin = m_pViewSh->GetWindow();
864     SfxErrorContext aEc(ERRCTX_SO_DOVERB, pWin ? pWin->GetFrameWeld() : nullptr, RID_SO_ERRCTX);
865     ErrCode nError = ERRCODE_NONE;
866 
867     if ( m_xImp->m_xObject.is() )
868     {
869         bool bSaveCopyAs = false;
870         if ( nVerb == -8 ) // "Save Copy as..."
871         {
872             svt::EmbeddedObjectRef::TryRunningState( m_xImp->m_xObject );
873             // TODO/LATER: this special verb should disappear when outplace activation is completely available
874             uno::Reference< frame::XModel > xEmbModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
875             if ( xEmbModel.is() )
876             {
877                 bSaveCopyAs = true;
878 
879                 try
880                 {
881                     SfxStoringHelper aHelper;
882                     uno::Sequence< beans::PropertyValue > aDispatchArgs( 1 );
883                     aDispatchArgs[0].Name = "SaveTo";
884                     aDispatchArgs[0].Value <<= true;
885 
886                     aHelper.GUIStoreModel( xEmbModel,
887                                             "SaveAs",
888                                             aDispatchArgs,
889                                             false,
890                                             SignatureState::NOSIGNATURES );
891                 }
892                 catch( const task::ErrorCodeIOException& aErrorEx )
893                 {
894                     nError = ErrCode(aErrorEx.ErrCode);
895                 }
896                 catch( uno::Exception& )
897                 {
898                     nError = ERRCODE_IO_GENERAL;
899                     // TODO/LATER: better error handling
900                 }
901             }
902         }
903 
904         if ( !bSaveCopyAs )
905         {
906             if ( m_xImp->m_nAspect == embed::Aspects::MSOLE_ICON )
907             {
908                 // the common persistence is supported by objects and links
909 
910                 uno::Reference< embed::XEmbeddedOleObject > xEmbeddedOleObject( m_xImp->m_xObject, uno::UNO_QUERY );
911 
912                 if ( xEmbeddedOleObject.is() && (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW ))
913                     nVerb = embed::EmbedVerbs::MS_OLEVERB_SHOW;
914                 else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW )
915                     nVerb = embed::EmbedVerbs::MS_OLEVERB_OPEN; // outplace activation
916                 else if ( nVerb == embed::EmbedVerbs::MS_OLEVERB_UIACTIVATE
917                        || nVerb == embed::EmbedVerbs::MS_OLEVERB_IPACTIVATE )
918                     nError = ERRCODE_SO_GENERALERROR;
919             }
920 
921             if ( !nError )
922             {
923                 // See comment for SfxInPlaceClient_Impl::getPlacement.
924                 vcl::Window* pEditWin = GetEditWin();
925                 bool bMapModeEnabled = pEditWin->IsMapModeEnabled();
926                 if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled)
927                 {
928                     pEditWin->EnableMapMode();
929                 }
930                 m_pViewSh->GetViewFrame()->GetFrame().LockResize_Impl(true);
931                 try
932                 {
933                     m_xImp->m_xObject->setClientSite( m_xImp->m_xClient );
934 
935                     m_xImp->m_xObject->doVerb( nVerb );
936                 }
937                 catch ( embed::UnreachableStateException& )
938                 {
939                     if (nVerb == embed::EmbedVerbs::MS_OLEVERB_PRIMARY || nVerb == embed::EmbedVerbs::MS_OLEVERB_OPEN || nVerb == embed::EmbedVerbs::MS_OLEVERB_SHOW)
940                     {
941                         // a workaround for the default verb, usually makes sense for alien objects
942                         try
943                         {
944                             m_xImp->m_xObject->doVerb( -9 ); // open own view, a workaround verb that is not visible
945 
946                             if ( m_xImp->m_xObject->getCurrentState() == embed::EmbedStates::UI_ACTIVE )
947                             {
948                                 // the object was converted to OOo object
949                                 awt::Size aSize = m_xImp->m_xObject->getVisualAreaSize( m_xImp->m_nAspect );
950                                 MapMode aObjectMap( VCLUnoHelper::UnoEmbed2VCLMapUnit( m_xImp->m_xObject->getMapUnit( m_xImp->m_nAspect ) ) );
951                                 MapMode aClientMap( GetEditWin()->GetMapMode().GetMapUnit() );
952                                 Size aNewSize = GetEditWin()->LogicToLogic( Size( aSize.Width, aSize.Height ), &aObjectMap, &aClientMap );
953 
954                                 tools::Rectangle aScaledArea = GetScaledObjArea();
955                                 m_xImp->m_aObjArea.SetSize( aNewSize );
956                                 m_xImp->m_aScaleWidth = Fraction( aScaledArea.GetWidth(), aNewSize.Width() );
957                                 m_xImp->m_aScaleHeight = Fraction( aScaledArea.GetHeight(), aNewSize.Height() );
958                             }
959                         }
960                         catch (uno::Exception const& e)
961                         {
962                             SAL_WARN("embeddedobj", "SfxInPlaceClient::DoVerb: -9 fallback path: " << e);
963                             nError = ERRCODE_SO_GENERALERROR;
964                         }
965                     }
966                 }
967                 catch ( embed::StateChangeInProgressException& )
968                 {
969                     // TODO/LATER: it would be nice to be able to provide the current target state outside
970                     nError = ERRCODE_SO_CANNOT_DOVERB_NOW;
971                 }
972                 catch (uno::Exception const& e)
973                 {
974                     SAL_WARN("embeddedobj", "SfxInPlaceClient::DoVerb:"
975                             " exception caught: " << e);
976                     nError = ERRCODE_SO_GENERALERROR;
977                     //TODO/LATER: better error handling
978 
979                 }
980                 if (comphelper::LibreOfficeKit::isActive() && !bMapModeEnabled
981                         && pEditWin->IsMapModeEnabled())
982                 {
983                     pEditWin->EnableMapMode(false);
984                 }
985                 SfxViewFrame* pFrame = m_pViewSh->GetViewFrame();
986                 pFrame->GetFrame().LockResize_Impl(false);
987                 pFrame->GetFrame().Resize();
988             }
989         }
990     }
991 
992     if( nError )
993         ErrorHandler::HandleError( nError );
994 
995     return nError;
996 }
997 
998 void SfxInPlaceClient::VisAreaChanged()
999 {
1000     uno::Reference < embed::XInplaceObject > xObj( m_xImp->m_xObject, uno::UNO_QUERY );
1001     uno::Reference < embed::XInplaceClient > xClient( m_xImp->m_xClient, uno::UNO_QUERY );
1002     if ( xObj.is() && xClient.is() )
1003         m_xImp->SizeHasChanged();
1004 }
1005 
1006 void SfxInPlaceClient::ObjectAreaChanged()
1007 {
1008     // dummy implementation
1009 }
1010 
1011 void SfxInPlaceClient::RequestNewObjectArea( tools::Rectangle& )
1012 {
1013     // dummy implementation
1014 }
1015 
1016 void SfxInPlaceClient::ViewChanged()
1017 {
1018     // dummy implementation
1019 }
1020 
1021 void SfxInPlaceClient::FormatChanged()
1022 {
1023     // dummy implementation
1024 }
1025 
1026 void SfxInPlaceClient::DeactivateObject()
1027 {
1028     if ( GetObject().is() )
1029     {
1030         try
1031         {
1032             m_xImp->m_bUIActive = false;
1033             bool bHasFocus = false;
1034             uno::Reference< frame::XModel > xModel( m_xImp->m_xObject->getComponent(), uno::UNO_QUERY );
1035             if ( xModel.is() )
1036             {
1037                 uno::Reference< frame::XController > xController = xModel->getCurrentController();
1038                 if ( xController.is() )
1039                 {
1040                     VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xController->getFrame()->getContainerWindow() );
1041                     bHasFocus = pWindow->HasChildPathFocus( true );
1042                 }
1043             }
1044 
1045             m_pViewSh->GetViewFrame()->GetFrame().LockResize_Impl(true);
1046 
1047             if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
1048             {
1049                 m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
1050                 if (bHasFocus)
1051                     m_pViewSh->GetWindow()->GrabFocus();
1052             }
1053             else
1054             {
1055                 // the links should not stay in running state for long time because of locking
1056                 uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
1057                 if ( xLink.is() && xLink->isLink() )
1058                     m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
1059                 else
1060                     m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
1061             }
1062 
1063             SfxViewFrame* pFrame = m_pViewSh->GetViewFrame();
1064             SfxViewFrame::SetViewFrame( pFrame );
1065             pFrame->GetFrame().LockResize_Impl(false);
1066             pFrame->GetFrame().Resize();
1067         }
1068         catch (css::uno::Exception& )
1069         {}
1070     }
1071 }
1072 
1073 void SfxInPlaceClient::ResetObject()
1074 {
1075     if ( GetObject().is() )
1076     {
1077         try
1078         {
1079             m_xImp->m_bUIActive = false;
1080             if ( m_xImp->m_xObject->getStatus( m_xImp->m_nAspect ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
1081                 m_xImp->m_xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
1082             else
1083             {
1084                 // the links should not stay in running state for long time because of locking
1085                 uno::Reference< embed::XLinkageSupport > xLink( m_xImp->m_xObject, uno::UNO_QUERY );
1086                 if ( xLink.is() && xLink->isLink() )
1087                     m_xImp->m_xObject->changeState( embed::EmbedStates::LOADED );
1088                 else
1089                     m_xImp->m_xObject->changeState( embed::EmbedStates::RUNNING );
1090             }
1091         }
1092         catch (css::uno::Exception& )
1093         {}
1094     }
1095 }
1096 
1097 bool SfxInPlaceClient::IsUIActive()
1098 {
1099     return m_xImp->m_bUIActive;
1100 }
1101 
1102 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1103