/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::io; using namespace ::com::sun::star::datatransfer; constexpr sal_uInt32 SDTRANSFER_OBJECTTYPE_DRAWMODEL = 1; constexpr sal_uInt32 SDTRANSFER_OBJECTTYPE_DRAWOLE = 2; SdTransferable::SdTransferable( SdDrawDocument* pSrcDoc, ::sd::View* pWorkView, bool bInitOnGetData ) : mpPageDocShell( nullptr ) , mpSdView( pWorkView ) , mpSdViewIntern( pWorkView ) , mpSdDrawDocument( nullptr ) , mpSdDrawDocumentIntern( nullptr ) , mpSourceDoc( pSrcDoc ) , mpVDev( nullptr ) , mbInternalMove( false ) , mbOwnDocument( false ) , mbOwnView( false ) , mbLateInit( bInitOnGetData ) , mbPageTransferable( false ) , mbPageTransferablePersistent( false ) { if( mpSourceDoc ) StartListening( *mpSourceDoc ); if( pWorkView ) StartListening( *pWorkView ); if( !mbLateInit ) CreateData(); } SdTransferable::~SdTransferable() { SolarMutexGuard g; if( mpSourceDoc ) EndListening( *mpSourceDoc ); if( mpSdView ) EndListening( *const_cast< sd::View *>( mpSdView) ); ObjectReleased(); if( mbOwnView ) delete mpSdViewIntern; mpOLEDataHelper.reset(); if( maDocShellRef.is() ) { SfxObjectShell* pObj = maDocShellRef.get(); ::sd::DrawDocShell* pDocSh = static_cast< ::sd::DrawDocShell*>(pObj); pDocSh->DoClose(); } maDocShellRef.clear(); if( mbOwnDocument ) delete mpSdDrawDocumentIntern; moGraphic.reset(); moBookmark.reset(); mpImageMap.reset(); mpVDev.disposeAndClear(); mpObjDesc.reset(); //call explicitly at end of dtor to be covered by above SolarMutex maUserData.clear(); } void SdTransferable::CreateObjectReplacement( SdrObject* pObj ) { if( !pObj ) return; mpOLEDataHelper.reset(); moGraphic.reset(); moBookmark.reset(); mpImageMap.reset(); if( auto pOleObj = dynamic_cast< SdrOle2Obj* >( pObj ) ) { try { uno::Reference < embed::XEmbeddedObject > xObj = pOleObj->GetObjRef(); uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); if( xObj.is() && xPersist.is() && xPersist->hasEntry() ) { mpOLEDataHelper.reset( new TransferableDataHelper( new SvEmbedTransferHelper( xObj, pOleObj->GetGraphic(), pOleObj->GetAspect() ) ) ); // TODO/LATER: the standalone handling of the graphic should not be used any more in future // The EmbedDataHelper should bring the graphic in future const Graphic* pObjGr = pOleObj->GetGraphic(); if ( pObjGr ) moGraphic.emplace(*pObjGr); } } catch( uno::Exception& ) {} } else if( dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr && (mpSourceDoc && !SdDrawDocument::GetAnimationInfo( pObj )) ) { moGraphic.emplace( static_cast< SdrGrafObj* >( pObj )->GetTransformedGraphic() ); } else if( pObj->IsUnoObj() && SdrInventor::FmForm == pObj->GetObjInventor() && ( pObj->GetObjIdentifier() == SdrObjKind::FormButton ) ) { SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( pObj ); if (SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) { const Reference< css::awt::XControlModel >& xControlModel( pUnoCtrl->GetUnoControlModel() ); if( !xControlModel.is() ) return; Reference< css::beans::XPropertySet > xPropSet( xControlModel, UNO_QUERY ); if( !xPropSet.is() ) return; css::form::FormButtonType eButtonType; Any aTmp( xPropSet->getPropertyValue( u"ButtonType"_ustr ) ); if( aTmp >>= eButtonType ) { OUString aLabel, aURL; xPropSet->getPropertyValue( u"Label"_ustr ) >>= aLabel; xPropSet->getPropertyValue( u"TargetURL"_ustr ) >>= aURL; moBookmark.emplace( aURL, aLabel ); } } } else if( auto pTextObj = DynCastSdrTextObj( pObj ) ) { const OutlinerParaObject* pPara; if( (pPara = pTextObj->GetOutlinerParaObject()) != nullptr ) { const SvxFieldItem* pField; if( (pField = pPara->GetTextObject().GetField()) != nullptr ) { const SvxFieldData* pData = pField->GetField(); if( auto pURL = dynamic_cast< const SvxURLField *>( pData ) ) { // #i63399# This special code identifies TextFrames which have just a URL // as content and directly add this to the clipboard, probably to avoid adding // an unnecessary DrawObject to the target where paste may take place. This is // wanted only for SdrObjects with no fill and no line, else it is necessary to // use the whole SdrObject. Test here for Line/FillStyle and take shortcut only // when both are unused if(!pObj->HasFillStyle() && !pObj->HasLineStyle()) { moBookmark.emplace( pURL->GetURL(), pURL->GetRepresentation() ); } } } } } SvxIMapInfo* pInfo = SvxIMapInfo::GetIMapInfo( pObj ); if( pInfo ) mpImageMap.reset( new ImageMap( pInfo->GetImageMap() ) ); } void SdTransferable::CreateData() { if( mpSdDrawDocument && !mpSdViewIntern ) { mbOwnView = true; SdPage* pPage = mpSdDrawDocument->GetSdPage(0, PageKind::Standard); if( pPage && 1 == pPage->GetObjCount() ) CreateObjectReplacement( pPage->GetObj( 0 ) ); mpVDev = VclPtr::Create( *Application::GetDefaultDevice() ); mpVDev->SetMapMode(MapMode(mpSdDrawDocumentIntern->GetScaleUnit())); mpSdViewIntern = new ::sd::View( *mpSdDrawDocumentIntern, mpVDev ); mpSdViewIntern->EndListening(*mpSdDrawDocumentIntern ); mpSdViewIntern->hideMarkHandles(); SdrPageView* pPageView = mpSdViewIntern->ShowSdrPage(pPage); mpSdViewIntern->MarkAllObj(pPageView); } else if( mpSdView && !mpSdDrawDocumentIntern ) { const SdrMarkList& rMarkList = mpSdView->GetMarkedObjectList(); if( rMarkList.GetMarkCount() == 1 ) CreateObjectReplacement( rMarkList.GetMark( 0 )->GetMarkedSdrObj() ); if( mpSourceDoc ) mpSourceDoc->CreatingDataObj(this); mpSdDrawDocumentIntern = static_cast( mpSdView->CreateMarkedObjModel().release() ); if( mpSourceDoc ) mpSourceDoc->CreatingDataObj(nullptr); if( !maDocShellRef.is() && mpSdDrawDocumentIntern->GetDocSh() ) maDocShellRef = mpSdDrawDocumentIntern->GetDocSh(); if( !maDocShellRef.is() ) { OSL_FAIL( "SdTransferable::CreateData(), failed to create a model with persist, clipboard operation will fail for OLE objects!" ); mbOwnDocument = true; } // Use dimension of source page SdrPageView* pPgView = mpSdView->GetSdrPageView(); SdPage* pOldPage = static_cast( pPgView->GetPage() ); SdStyleSheetPool* pOldStylePool = static_cast(mpSdView->GetModel().GetStyleSheetPool()); SdStyleSheetPool* pNewStylePool = static_cast( mpSdDrawDocumentIntern->GetStyleSheetPool() ); SdPage* pPage = mpSdDrawDocumentIntern->GetSdPage( 0, PageKind::Standard ); OUString aOldLayoutName( pOldPage->GetLayoutName() ); pPage->SetSize( pOldPage->GetSize() ); pPage->SetLayoutName( aOldLayoutName ); pNewStylePool->CopyGraphicSheets( *pOldStylePool ); pNewStylePool->CopyCellSheets( *pOldStylePool ); pNewStylePool->CopyTableStyles( *pOldStylePool ); sal_Int32 nPos = aOldLayoutName.indexOf( SD_LT_SEPARATOR ); if( nPos != -1 ) aOldLayoutName = aOldLayoutName.copy( 0, nPos ); StyleSheetCopyResultVector aCreatedSheets; pNewStylePool->CopyLayoutSheets( aOldLayoutName, *pOldStylePool, aCreatedSheets ); } // set VisArea and adjust objects if necessary if( !(maVisArea.IsEmpty() && mpSdDrawDocumentIntern && mpSdViewIntern && mpSdDrawDocumentIntern->GetPageCount()) ) return; SdPage* pPage = mpSdDrawDocumentIntern->GetSdPage( 0, PageKind::Standard ); if( 1 == mpSdDrawDocumentIntern->GetPageCount() ) { // #112978# need to use GetAllMarkedBoundRect instead of GetAllMarkedRect to get // fat lines correctly maVisArea = mpSdViewIntern->GetAllMarkedBoundRect(); Point aOrigin( maVisArea.TopLeft() ); Size aVector( -aOrigin.X(), -aOrigin.Y() ); for (const rtl::Reference& pObj : *pPage) pObj->NbcMove( aVector ); } else maVisArea.SetSize( pPage->GetSize() ); // output is at the zero point maVisArea.SetPos( Point() ); } static bool lcl_HasOnlyControls( SdrModel* pModel ) { bool bOnlyControls = false; // default if there are no objects if ( pModel ) { SdrPage* pPage = pModel->GetPage(0); if (pPage) { SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); SdrObject* pObj = aIter.Next(); if ( pObj ) { bOnlyControls = true; // only set if there are any objects at all while ( pObj ) { if (dynamic_cast< const SdrUnoObj *>( pObj ) == nullptr) { bOnlyControls = false; break; } pObj = aIter.Next(); } } } } return bOnlyControls; } static bool lcl_HasOnlyOneTable( SdrModel* pModel ) { if ( pModel ) { SdrPage* pPage = pModel->GetPage(0); if (pPage && pPage->GetObjCount() == 1 ) { if( dynamic_cast< sdr::table::SdrTableObj* >( pPage->GetObj(0) ) != nullptr ) return true; } } return false; } void SdTransferable::AddSupportedFormats() { if( mbPageTransferable && !mbPageTransferablePersistent ) return; if( !mbLateInit ) CreateData(); if( mpObjDesc ) AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); if( mpOLEDataHelper ) { AddFormat( SotClipboardFormatId::EMBED_SOURCE ); DataFlavorExVector aVector( mpOLEDataHelper->GetDataFlavorExVector() ); for( const auto& rItem : aVector ) AddFormat( rItem ); } else if( moGraphic ) { // #i25616# AddFormat( SotClipboardFormatId::DRAWING ); AddFormat( SotClipboardFormatId::SVXB ); if( moGraphic->GetType() == GraphicType::Bitmap ) { AddFormat( SotClipboardFormatId::PNG ); AddFormat( SotClipboardFormatId::BITMAP ); AddFormat( SotClipboardFormatId::GDIMETAFILE ); } else { AddFormat( SotClipboardFormatId::GDIMETAFILE ); AddFormat( SotClipboardFormatId::PNG ); AddFormat( SotClipboardFormatId::BITMAP ); } } else if( moBookmark ) { AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); AddFormat( SotClipboardFormatId::STRING ); } else { AddFormat( SotClipboardFormatId::EMBED_SOURCE ); AddFormat( SotClipboardFormatId::DRAWING ); if( !mpSdDrawDocument || !lcl_HasOnlyControls( mpSdDrawDocument ) ) { AddFormat( SotClipboardFormatId::GDIMETAFILE ); AddFormat( SotClipboardFormatId::PNG ); AddFormat( SotClipboardFormatId::BITMAP ); } if( lcl_HasOnlyOneTable( mpSdDrawDocument ) ) { AddFormat( SotClipboardFormatId::RTF ); AddFormat( SotClipboardFormatId::RICHTEXT ); } } if( mpImageMap ) AddFormat( SotClipboardFormatId::SVIM ); } bool SdTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) { if (SD_MOD()==nullptr) return false; SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); bool bOK = false; CreateData(); if( nFormat == SotClipboardFormatId::RTF && lcl_HasOnlyOneTable( mpSdDrawDocument ) ) { bOK = SetTableRTF( mpSdDrawDocument ); } else if( mpOLEDataHelper && mpOLEDataHelper->HasFormat( rFlavor ) ) { // TODO/LATER: support all the graphical formats, the embedded object scenario should not have separated handling if( nFormat == SotClipboardFormatId::GDIMETAFILE && moGraphic ) bOK = SetGDIMetaFile( moGraphic->GetGDIMetaFile() ); else bOK = SetAny( mpOLEDataHelper->GetAny(rFlavor, rDestDoc) ); } else if( HasFormat( nFormat ) ) { if( ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) && mpObjDesc ) { bOK = SetTransferableObjectDescriptor( *mpObjDesc ); } else if( nFormat == SotClipboardFormatId::DRAWING ) { SfxObjectShellRef aOldRef( maDocShellRef ); maDocShellRef.clear(); if( mpSdViewIntern ) { SdDrawDocument& rInternDoc = mpSdViewIntern->GetDoc(); rInternDoc.CreatingDataObj(this); SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( mpSdViewIntern->CreateMarkedObjModel().release() ); rInternDoc.CreatingDataObj(nullptr); bOK = SetObject( pDoc, SDTRANSFER_OBJECTTYPE_DRAWMODEL, rFlavor ); if( maDocShellRef.is() ) { maDocShellRef->DoClose(); } else { delete pDoc; } } maDocShellRef = std::move(aOldRef); } else if( nFormat == SotClipboardFormatId::GDIMETAFILE ) { if (mpSdViewIntern) { const bool bToggleOnlineSpell = mpSdDrawDocumentIntern && mpSdDrawDocumentIntern->GetOnlineSpell(); if (bToggleOnlineSpell) mpSdDrawDocumentIntern->SetOnlineSpell(false); bOK = SetGDIMetaFile( mpSdViewIntern->GetMarkedObjMetaFile( true ) ); if (bToggleOnlineSpell) mpSdDrawDocumentIntern->SetOnlineSpell(true); } } else if( SotClipboardFormatId::BITMAP == nFormat || SotClipboardFormatId::PNG == nFormat ) { if (mpSdViewIntern) { const bool bToggleOnlineSpell = mpSdDrawDocumentIntern && mpSdDrawDocumentIntern->GetOnlineSpell(); if (bToggleOnlineSpell) mpSdDrawDocumentIntern->SetOnlineSpell(false); bOK = SetBitmapEx( mpSdViewIntern->GetMarkedObjBitmapEx(true), rFlavor ); if (bToggleOnlineSpell) mpSdDrawDocumentIntern->SetOnlineSpell(true); } } else if( ( nFormat == SotClipboardFormatId::STRING ) && moBookmark ) { bOK = SetString( moBookmark->GetURL() ); } else if( ( nFormat == SotClipboardFormatId::SVXB ) && moGraphic ) { bOK = SetGraphic( *moGraphic ); } else if( ( nFormat == SotClipboardFormatId::SVIM ) && mpImageMap ) { bOK = SetImageMap( *mpImageMap ); } else if( moBookmark ) { bOK = SetINetBookmark( *moBookmark, rFlavor ); } else if( nFormat == SotClipboardFormatId::EMBED_SOURCE ) { if( mpSdDrawDocumentIntern ) { if( !maDocShellRef.is() ) { maDocShellRef = new ::sd::DrawDocShell( mpSdDrawDocumentIntern, SfxObjectCreateMode::EMBEDDED, true, mpSdDrawDocumentIntern->GetDocumentType()); mbOwnDocument = false; maDocShellRef->DoInitNew(); } maDocShellRef->SetVisArea( maVisArea ); bOK = SetObject( maDocShellRef.get(), SDTRANSFER_OBJECTTYPE_DRAWOLE, rFlavor ); } } } return bOK; } bool SdTransferable::WriteObject( SvStream& rOStm, void* pObject, sal_uInt32 nObjectType, const DataFlavor& ) { bool bRet = false; switch( nObjectType ) { case SDTRANSFER_OBJECTTYPE_DRAWMODEL: { try { static const bool bDontBurnInStyleSheet = ( getenv( "AVOID_BURN_IN_FOR_GALLERY_THEME" ) != nullptr ); SdDrawDocument* pDoc = static_cast(pObject); if ( !bDontBurnInStyleSheet ) pDoc->BurnInStyleSheetAttributes(); rOStm.SetBufferSize( 16348 ); rtl::Reference< SdXImpressDocument > xComponent( new SdXImpressDocument( pDoc, true ) ); pDoc->setUnoModel( xComponent ); { css::uno::Reference xDocOut( new utl::OOutputStreamWrapper( rOStm ) ); SvxDrawingLayerExport( pDoc, xDocOut, xComponent, (pDoc->GetDocumentType() == DocumentType::Impress) ? "com.sun.star.comp.Impress.XMLClipboardExporter" : "com.sun.star.comp.DrawingLayer.XMLExporter" ); } xComponent->dispose(); bRet = ( rOStm.GetError() == ERRCODE_NONE ); } catch( Exception& ) { TOOLS_WARN_EXCEPTION( "sd", "sd::SdTransferable::WriteObject()" ); bRet = false; } } break; case SDTRANSFER_OBJECTTYPE_DRAWOLE: { SfxObjectShell* pEmbObj = static_cast(pObject); ::utl::TempFileFast aTempFile; SvStream* pTempStream = aTempFile.GetStream(StreamMode::READWRITE); try { uno::Reference< embed::XStorage > xWorkStore = ::comphelper::OStorageHelper::GetStorageFromStream( new utl::OStreamWrapper(*pTempStream), embed::ElementModes::READWRITE ); // write document storage pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); // mba: no relative URLs for clipboard! SfxMedium aMedium( xWorkStore, OUString() ); pEmbObj->DoSaveObjectAs( aMedium, false ); pEmbObj->DoSaveCompleted(); uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY ); if ( xTransact.is() ) xTransact->commit(); rOStm.SetBufferSize( 0xff00 ); rOStm.WriteStream( *pTempStream ); bRet = true; } catch ( Exception& ) {} } break; default: break; } return bRet; } void SdTransferable::DragFinished( sal_Int8 nDropAction ) { if( mpSdView ) const_cast< ::sd::View* >(mpSdView)->DragFinished( nDropAction ); } void SdTransferable::ObjectReleased() { SdModule *pModule = SD_MOD(); if (!pModule) return; if( this == pModule->pTransferClip ) pModule->pTransferClip = nullptr; if( this == pModule->pTransferDrag ) pModule->pTransferDrag = nullptr; if( this == pModule->pTransferSelection ) pModule->pTransferSelection = nullptr; } void SdTransferable::SetObjectDescriptor( std::unique_ptr pObjDesc ) { mpObjDesc = std::move(pObjDesc); PrepareOLE( *mpObjDesc ); } void SdTransferable::SetPageBookmarks( std::vector && rPageBookmarks, bool bPersistent ) { if( !mpSourceDoc ) return; if( mpSdViewIntern ) mpSdViewIntern->HideSdrPage(); mpSdDrawDocument->ClearModel(false); mpPageDocShell = nullptr; maPageBookmarks.clear(); if( bPersistent ) { mpSdDrawDocument->CreateFirstPages(mpSourceDoc); mpSdDrawDocument->InsertBookmarkAsPage( rPageBookmarks, nullptr, false, true, 1, true, mpSourceDoc->GetDocSh(), true, true, false ); } else { mpPageDocShell = mpSourceDoc->GetDocSh(); maPageBookmarks = std::move(rPageBookmarks); } if( mpSdViewIntern ) { SdPage* pPage = mpSdDrawDocument->GetSdPage( 0, PageKind::Standard ); if( pPage ) { mpSdViewIntern->MarkAllObj( mpSdViewIntern->ShowSdrPage( pPage ) ); } } // set flags for page transferable; if ( mbPageTransferablePersistent == sal_False ), // don't offer any formats => it's just for internal purposes mbPageTransferable = true; mbPageTransferablePersistent = bPersistent; } void SdTransferable::AddUserData (const std::shared_ptr& rpData) { maUserData.push_back(rpData); } sal_Int32 SdTransferable::GetUserDataCount() const { return maUserData.size(); } std::shared_ptr SdTransferable::GetUserData (const sal_Int32 nIndex) const { if (nIndex>=0 && o3tl::make_unsigned(nIndex)(); } SdTransferable* SdTransferable::getImplementation( const Reference< XInterface >& rxData ) noexcept { return dynamic_cast(rxData.get()); } void SdTransferable::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) { if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) { const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint ); if( SdrHintKind::ModelCleared == pSdrHint->GetKind() ) { EndListening(*mpSourceDoc); mpSourceDoc = nullptr; } } else { if( rHint.GetId() == SfxHintId::Dying ) { if( &rBC == mpSourceDoc ) mpSourceDoc = nullptr; if( &rBC == mpSdViewIntern ) mpSdViewIntern = nullptr; if( &rBC == mpSdView ) mpSdView = nullptr; } } } void SdTransferable::SetView(const ::sd::View* pView) { if (mpSdView) EndListening(*const_cast(mpSdView)); mpSdView = pView; if (mpSdView) StartListening(*const_cast(mpSdView)); } bool SdTransferable::SetTableRTF( SdDrawDocument* pModel ) { if ( pModel ) { SdrPage* pPage = pModel->GetPage(0); if (pPage && pPage->GetObjCount() == 1 ) { sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pPage->GetObj(0) ); if( pTableObj ) { SvMemoryStream aMemStm( 65535, 65535 ); sdr::table::ExportAsRTF( aMemStm, *pTableObj ); return SetAny( Any( Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() ) ) ); } } } return false; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */