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
