/* -*- 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 #include #include #ifdef _WIN32 #include #include #include #include #endif #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* default (A4 format) width of 210mm - 2 * border size (border on both sides) */ constexpr tools::Long constOleWidthInMm = 210 - 2 * lMinBorderInMm; constexpr Size constOleSize100mm( constOleWidthInMm * 100, // convert from mm to 100mm 3000 // 3 cm ); constexpr Size constOleSizeTwip = o3tl::convert(constOleSize100mm, o3tl::Length::mm100, o3tl::Length::twip); constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_DRAWMODEL = 0x00000001; constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_HTML = 0x00000002; constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_RTF = 0x00000004; constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_STRING = 0x00000008; constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_SWOLE = 0x00000010; constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_DDE = 0x00000020; constexpr sal_uInt32 SWTRANSFER_OBJECTTYPE_RICHTEXT = 0x00000040; using namespace ::svx; using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::datatransfer; namespace { void collectUIInformation(const OUString& rAction, const OUString& aParameters) { EventDescription aDescription; aDescription.aAction = rAction; aDescription.aParameters = {{"parameters", aParameters}}; aDescription.aID = "writer_edit"; aDescription.aKeyWord = "SwEditWinUIObject"; aDescription.aParent = "MainWindow"; UITestLogger::getInstance().logEvent(aDescription); } } class SwTransferDdeLink : public ::sfx2::SvBaseLink { OUString m_sName; ::sfx2::SvLinkSourceRef m_xRefObj; SwTransferable& m_rTransfer; SwDocShell* m_pDocShell; sal_uLong m_nOldTimeOut; bool m_bDelBookmark : 1; bool m_bInDisconnect : 1; bool FindDocShell(); using sfx2::SvBaseLink::Disconnect; protected: virtual ~SwTransferDdeLink() override; public: SwTransferDdeLink( SwTransferable& rTrans, SwWrtShell& rSh ); virtual ::sfx2::SvBaseLink::UpdateResult DataChanged( const OUString& rMimeType, const css::uno::Any & rValue ) override; virtual void Closed() override; bool WriteData( SvStream& rStrm ); void Disconnect( bool bRemoveDataAdvise ); }; /// Tracks the boundaries of pasted content and notifies listeners. class SwPasteContext { public: SwPasteContext(SwWrtShell& rWrtShell); ~SwPasteContext(); void remember(); void forget(); private: SwWrtShell& m_rWrtShell; std::optional m_oPaM; sal_Int32 m_nStartContent = 0; }; namespace { // helper class for Action and Undo enclosing class SwTrnsfrActionAndUndo { SwWrtShell *pSh; public: SwTrnsfrActionAndUndo( SwWrtShell *pS, bool bDelSel = false, SwPasteContext* pContext = nullptr) : pSh( pS ) { pSh->StartUndo( SwUndoId::PASTE_CLIPBOARD ); if( bDelSel ) { if (pContext) pContext->forget(); pSh->DelRight(); if (pContext) pContext->remember(); } pSh->StartAllAction(); } ~SwTrnsfrActionAndUndo() COVERITY_NOEXCEPT_FALSE { pSh->EndUndo(); pSh->EndAllAction(); } }; } SwTransferable::SwTransferable( SwWrtShell& rSh ) : m_pWrtShell( &rSh ), m_pCreatorView( nullptr ), m_pOrigGraphic( nullptr ), m_eBufferType( TransferBufferType::NONE ), m_bOldIdle(false), m_bCleanUp(false) { rSh.GetView().AddTransferable(*this); SwDocShell* pDShell = rSh.GetDoc()->GetDocShell(); if( !pDShell ) return; pDShell->FillTransferableObjectDescriptor( m_aObjDesc ); if( pDShell->GetMedium() ) { const INetURLObject& rURLObj = pDShell->GetMedium()->GetURLObject(); m_aObjDesc.maDisplayName = URIHelper::removePassword( rURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), INetURLObject::EncodeMechanism::WasEncoded, INetURLObject::DecodeMechanism::Unambiguous ); } PrepareOLE( m_aObjDesc ); } SwTransferable::~SwTransferable() { SolarMutexGuard aSolarGuard; // the DDELink still needs the WrtShell! DisconnectDDE(); m_pWrtShell = nullptr; // release reference to the document so that aDocShellRef will delete // it (if aDocShellRef is set). Otherwise, the OLE nodes keep references // to their sub-storage when the storage is already dead. m_pClpDocFac.reset(); // first close, then the Ref. can be cleared as well, so that // the DocShell really gets deleted! if( m_aDocShellRef.Is() ) { SfxObjectShell * pObj = m_aDocShellRef; SwDocShell* pDocSh = static_cast(pObj); pDocSh->DoClose(); } m_aDocShellRef.Clear(); SwModule* pMod = SW_MOD(); if(pMod) { if ( pMod->m_pDragDrop == this ) pMod->m_pDragDrop = nullptr; else if ( pMod->m_pXSelection == this ) pMod->m_pXSelection = nullptr; } m_eBufferType = TransferBufferType::NONE; } static SwDoc& lcl_GetDoc(SwDocFac & rDocFac) { SwDoc& rDoc = rDocFac.GetDoc(); rDoc.SetClipBoard( true ); return rDoc; } void SwTransferable::ObjectReleased() { SwModule *pMod = SW_MOD(); if (!pMod) return; if( this == pMod->m_pDragDrop ) pMod->m_pDragDrop = nullptr; else if( this == pMod->m_pXSelection ) pMod->m_pXSelection = nullptr; } void SwTransferable::AddSupportedFormats() { // only need if we are the current XSelection Object SwModule *pMod = SW_MOD(); if( this == pMod->m_pXSelection || comphelper::LibreOfficeKit::isActive()) { SetDataForDragAndDrop( Point( 0,0) ); } } void SwTransferable::InitOle( SfxObjectShell* pDoc ) { //set OleVisArea. Upper left corner of the page and size of //RealSize in Twips. const Size aSz(constOleSizeTwip); SwRect aVis( Point( DOCUMENTBORDER, DOCUMENTBORDER ), aSz ); pDoc->SetVisArea( aVis.SVRect() ); } uno::Reference < embed::XEmbeddedObject > SwTransferable::FindOLEObj( sal_Int64& nAspect ) const { uno::Reference < embed::XEmbeddedObject > xObj; if( m_pClpDocFac ) { SwIterator aIter( *m_pClpDocFac->GetDoc().GetDfltGrfFormatColl() ); for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) if( SwNodeType::Ole == pNd->GetNodeType() ) { xObj = static_cast(pNd)->GetOLEObj().GetOleRef(); nAspect = static_cast(pNd)->GetAspect(); break; } } return xObj; } const Graphic* SwTransferable::FindOLEReplacementGraphic() const { if( m_pClpDocFac ) { SwIterator aIter( *m_pClpDocFac->GetDoc().GetDfltGrfFormatColl() ); for( SwContentNode* pNd = aIter.First(); pNd; pNd = aIter.Next() ) if( SwNodeType::Ole == pNd->GetNodeType() ) { return static_cast(pNd)->GetGraphic(); } } return nullptr; } void SwTransferable::RemoveDDELinkFormat(vcl::Window& rWin) { RemoveFormat( SotClipboardFormatId::LINK ); if (rWin.GetClipboard()->getContents().get() == this) CopyToClipboard(&rWin); } void SwTransferable::DisconnectDDE() { if( m_xDdeLink.is() ) { m_xDdeLink->Disconnect( true ); m_xDdeLink.clear(); } } namespace { //Resolves: fdo#40717 surely when we create a clipboard document we should //overwrite the clipboard documents styles and settings with that of the //source, so that we can WYSIWYG paste. If we want that the destinations //styles are used over the source styles, that's a matter of the //destination paste code to handle, not the source paste code. void lclOverWriteDoc(SwWrtShell &rSrcWrtShell, SwDoc &rDest) { const SwDoc &rSrc = *rSrcWrtShell.GetDoc(); rDest.ReplaceCompatibilityOptions(rSrc); rDest.ReplaceDefaults(rSrc); //It would probably make most sense here to only insert the styles used //by the selection, e.g. apply SwDoc::IsUsed on styles ? rDest.ReplaceStyles(rSrc, false); rSrcWrtShell.Copy(rDest); rDest.GetMetaFieldManager().copyDocumentProperties(rSrc); } void lclCheckAndPerformRotation(Graphic& aGraphic) { GraphicNativeMetadata aMetadata; if ( !aMetadata.read(aGraphic) ) return; Degree10 aRotation = aMetadata.getRotation(); if (aRotation) { GraphicNativeTransform aTransform( aGraphic ); aTransform.rotate( aRotation ); } } } sal_Bool SAL_CALL SwTransferable::isComplex() { sal_Int32 nTextLength = 0; SwNodes& aNodes = m_pWrtShell->GetDoc()->GetNodes(); for (SwPaM& rPaM : m_pWrtShell->GetCursor()->GetRingContainer()) { for (SwNodeOffset nIndex = rPaM.GetMark()->GetNodeIndex(); nIndex <= rPaM.GetPoint()->GetNodeIndex(); ++nIndex) { SwNode& rNd = *aNodes[nIndex]; SwTextNode* pTextNode = rNd.GetTextNode(); if (pTextNode) { if (pTextNode->HasHints()) { for (size_t nHint = 0; nHint < pTextNode->GetSwpHints().Count(); ++nHint) { SwTextAttr* pHint = pTextNode->GetSwpHints().Get(nHint); if (pHint->Which() == RES_TXTATR_FLYCNT) { return true; // Complex } } } nTextLength += pTextNode->GetText().getLength(); if (nTextLength >= 1024 * 512) return true; // Complex } } } if (m_pWrtShell->GetSelectionType() == SelectionType::DrawObject) return true; // Complex // Simple return false; } bool SwTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) { SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); // we can only fulfil the request if // 1) we have data for this format // 2) we have either a clipboard document (pClpDocFac), or // we have a SwWrtShell (so we can generate a new clipboard document) if( !HasFormat( nFormat ) || ( m_pClpDocFac == nullptr && m_pWrtShell == nullptr ) ) return false; if( !m_pClpDocFac ) { SelectionType nSelectionType = m_pWrtShell->GetSelectionType(); // when pending we will not get the correct type, but SelectionType::Text // as fallback. This *happens* during D&D, so we need to check if we are in // the fallback and just try to get a graphic const bool bPending(m_pWrtShell->ActionPend()); // SEL_GRF is from ContentType of editsh if(bPending || ((SelectionType::Graphic | SelectionType::DrawObject | SelectionType::DbForm) & nSelectionType)) { m_oClpGraphic.emplace(); if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_oClpGraphic )) m_pOrigGraphic = &*m_oClpGraphic; m_oClpBitmap.emplace(); if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_oClpBitmap )) m_pOrigGraphic = &*m_oClpBitmap; // is it a URL-Button ? OUString sURL; OUString sDesc; if( m_pWrtShell->GetURLFromButton( sURL, sDesc ) ) { m_oBookmark.emplace( sURL, sDesc ); m_eBufferType = TransferBufferType::InetField; } } m_pClpDocFac.reset(new SwDocFac); SwDoc& rTmpDoc = lcl_GetDoc(*m_pClpDocFac); rTmpDoc.getIDocumentFieldsAccess().LockExpFields(); // never update fields - leave text as it is lclOverWriteDoc(*m_pWrtShell, rTmpDoc); // in CORE a new one was created (OLE-objects copied!) m_aDocShellRef = rTmpDoc.GetTmpDocShell(); if( m_aDocShellRef.Is() ) SwTransferable::InitOle( m_aDocShellRef ); rTmpDoc.SetTmpDocShell( nullptr ); if( nSelectionType & SelectionType::Text && !m_pWrtShell->HasMark() ) { SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); Point aPos( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY()); bool bSelect = g_bExecuteDrag && m_pWrtShell->GetView().GetDocShell() && !m_pWrtShell->GetView().GetDocShell()->IsReadOnly(); if( m_pWrtShell->GetContentAtPos( aPos, aContentAtPos, bSelect ) ) { m_oBookmark.emplace( static_cast(aContentAtPos.aFnd.pAttr)->GetValue(), aContentAtPos.sStr ); m_eBufferType = TransferBufferType::InetField; if( bSelect ) m_pWrtShell->SelectTextAttr( RES_TXTATR_INETFMT ); } } if( m_pWrtShell->IsFrameSelected() ) { SfxItemSetFixed aSet( m_pWrtShell->GetAttrPool() ); m_pWrtShell->GetFlyFrameAttr( aSet ); const SwFormatURL& rURL = aSet.Get( RES_URL ); if( rURL.GetMap() ) m_pImageMap.reset(new ImageMap( *rURL.GetMap() )); else if( !rURL.GetURL().isEmpty() ) m_pTargetURL.reset(new INetImage(OUString(), rURL.GetURL(), rURL.GetTargetFrameName() )); } } bool bOK = false; if( TransferBufferType::Ole == m_eBufferType ) { //TODO/MBA: testing - is this the "single OLE object" case?! // get OLE-Object from ClipDoc and get the data from that. sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; // will be set in the next statement uno::Reference < embed::XEmbeddedObject > xObj = FindOLEObj( nAspect ); const Graphic* pOLEGraph = FindOLEReplacementGraphic(); if( xObj.is() ) { TransferableDataHelper aD( new SvEmbedTransferHelper( xObj, pOLEGraph, nAspect ) ); uno::Any aAny = aD.GetAny(rFlavor, rDestDoc); if( aAny.hasValue() ) bOK = SetAny( aAny ); } // the following solution will be used in the case when the object can not generate the image // TODO/LATER: in future the transferhelper must probably be created based on object and the replacement stream // TODO: Block not required now, SvEmbedTransferHelper should be able to handle GDIMetaFile format if ( nFormat == SotClipboardFormatId::GDIMETAFILE ) { pOLEGraph = FindOLEReplacementGraphic(); if ( pOLEGraph ) bOK = SetGDIMetaFile( pOLEGraph->GetGDIMetaFile() ); } } else { switch( nFormat ) { case SotClipboardFormatId::LINK: if( m_xDdeLink.is() ) bOK = SetObject( m_xDdeLink.get(), SWTRANSFER_OBJECTTYPE_DDE, rFlavor ); break; case SotClipboardFormatId::OBJECTDESCRIPTOR: case SotClipboardFormatId::LINKSRCDESCRIPTOR: bOK = SetTransferableObjectDescriptor( m_aObjDesc ); break; case SotClipboardFormatId::DRAWING: { SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); bOK = SetObject( rDoc.getIDocumentDrawModelAccess().GetDrawModel(), SWTRANSFER_OBJECTTYPE_DRAWMODEL, rFlavor ); } break; case SotClipboardFormatId::STRING: { SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); bOK = SetObject( &rDoc, SWTRANSFER_OBJECTTYPE_STRING, rFlavor ); } break; case SotClipboardFormatId::RTF: { SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); bOK = SetObject( &rDoc, SWTRANSFER_OBJECTTYPE_RTF, rFlavor ); } break; case SotClipboardFormatId::RICHTEXT: { SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); bOK = SetObject( &rDoc, SWTRANSFER_OBJECTTYPE_RICHTEXT, rFlavor ); } break; case SotClipboardFormatId::HTML: { SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); bOK = SetObject( &rDoc, SWTRANSFER_OBJECTTYPE_HTML, rFlavor ); } break; case SotClipboardFormatId::SVXB: if( m_eBufferType & TransferBufferType::Graphic && m_pOrigGraphic ) bOK = SetGraphic( *m_pOrigGraphic ); break; case SotClipboardFormatId::GDIMETAFILE: if( m_eBufferType & TransferBufferType::Graphic ) bOK = SetGDIMetaFile( m_oClpGraphic->GetGDIMetaFile() ); break; case SotClipboardFormatId::BITMAP: case SotClipboardFormatId::PNG: // Neither pClpBitmap nor pClpGraphic are necessarily set if( (m_eBufferType & TransferBufferType::Graphic) && (m_oClpBitmap || m_oClpGraphic)) bOK = SetBitmapEx( (m_oClpBitmap ? m_oClpBitmap : m_oClpGraphic)->GetBitmapEx(), rFlavor ); break; case SotClipboardFormatId::SVIM: if( m_pImageMap ) bOK = SetImageMap( *m_pImageMap ); break; case SotClipboardFormatId::INET_IMAGE: if( m_pTargetURL ) bOK = SetINetImage( *m_pTargetURL, rFlavor ); break; case SotClipboardFormatId::SOLK: case SotClipboardFormatId::NETSCAPE_BOOKMARK: case SotClipboardFormatId::FILEGRPDESCRIPTOR: case SotClipboardFormatId::FILECONTENT: case SotClipboardFormatId::UNIFORMRESOURCELOCATOR: case SotClipboardFormatId::SIMPLE_FILE: if( (TransferBufferType::InetField & m_eBufferType) && m_oBookmark ) bOK = SetINetBookmark( *m_oBookmark, rFlavor ); break; case SotClipboardFormatId::EMBED_SOURCE: if( !m_aDocShellRef.Is() ) { SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); SwDocShell* pNewDocSh = new SwDocShell( rDoc, SfxObjectCreateMode::EMBEDDED ); m_aDocShellRef = pNewDocSh; m_aDocShellRef->DoInitNew(); SwTransferable::InitOle( m_aDocShellRef ); } bOK = SetObject( &m_aDocShellRef, SWTRANSFER_OBJECTTYPE_SWOLE, rFlavor ); break; default: break; } } return bOK; } bool SwTransferable::WriteObject( tools::SvRef& xStream, void* pObject, sal_uInt32 nObjectType, const DataFlavor& /*rFlavor*/ ) { bool bRet = false; WriterRef xWrt; switch( nObjectType ) { case SWTRANSFER_OBJECTTYPE_DRAWMODEL: { // don't change the sequence of commands SdrModel *pModel = static_cast(pObject); xStream->SetBufferSize( 16348 ); // for the changed pool defaults from drawing layer pool set those // attributes as hard attributes to preserve them for saving const SfxItemPool& rItemPool = pModel->GetItemPool(); const SvxFontHeightItem& rDefaultFontHeight = rItemPool.GetDefaultItem(EE_CHAR_FONTHEIGHT); // SW should have no MasterPages OSL_ENSURE(0 == pModel->GetMasterPageCount(), "SW with MasterPages (!)"); for(sal_uInt16 a(0); a < pModel->GetPageCount(); a++) { const SdrPage* pPage = pModel->GetPage(a); SdrObjListIter aIter(pPage, SdrIterMode::DeepNoGroups); while(aIter.IsMore()) { SdrObject* pObj = aIter.Next(); const SvxFontHeightItem& rItem = pObj->GetMergedItem(EE_CHAR_FONTHEIGHT); if(rItem.GetHeight() == rDefaultFontHeight.GetHeight()) { pObj->SetMergedItem(rDefaultFontHeight); } } } { uno::Reference xDocOut( new utl::OOutputStreamWrapper( *xStream ) ); SvxDrawingLayerExport( pModel, xDocOut ); } bRet = ERRCODE_NONE == xStream->GetError(); } break; case SWTRANSFER_OBJECTTYPE_SWOLE: { SfxObjectShell* pEmbObj = static_cast(pObject); try { ::utl::TempFileFast aTempFile; SvStream* pTempStream = aTempFile.GetStream(StreamMode::READWRITE); 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 BaseURL 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(); xStream->SetBufferSize( 0xff00 ); xStream->WriteStream( *pTempStream ); xWorkStore->dispose(); xWorkStore.clear(); } catch (const uno::Exception&) { } bRet = ( xStream->GetError() == ERRCODE_NONE ); } break; case SWTRANSFER_OBJECTTYPE_DDE: { xStream->SetBufferSize( 1024 ); SwTransferDdeLink* pDdeLnk = static_cast(pObject); if( pDdeLnk->WriteData( *xStream ) ) { bRet = ERRCODE_NONE == xStream->GetError(); } } break; case SWTRANSFER_OBJECTTYPE_HTML: { // LOK is interested in getting images embedded for copy/paste support. GetHTMLWriter( comphelper::LibreOfficeKit::isActive() ? OUString("EmbedImages;NoPrettyPrint") : OUString(), OUString(), xWrt ); break; } case SWTRANSFER_OBJECTTYPE_RTF: case SWTRANSFER_OBJECTTYPE_RICHTEXT: GetRTFWriter(std::u16string_view(), OUString(), xWrt); break; case SWTRANSFER_OBJECTTYPE_STRING: GetASCWriter(std::u16string_view(), OUString(), xWrt); if( xWrt.is() ) { SwAsciiOptions aAOpt; aAOpt.SetCharSet( RTL_TEXTENCODING_UTF8 ); xWrt->SetAsciiOptions( aAOpt ); // no start char for clipboard xWrt->m_bUCS2_WithStartChar = false; } break; default: break; } if( xWrt.is() ) { SwDoc* pDoc = static_cast(pObject); xWrt->m_bWriteClipboardDoc = true; xWrt->m_bWriteOnlyFirstTable = bool(TransferBufferType::Table & m_eBufferType); xWrt->SetShowProgress(false); #if defined(DEBUGPASTE) SvFileStream aPasteDebug(OUString( "PASTEBUFFER.debug"), StreamMode::WRITE|StreamMode::TRUNC); SwWriter aDbgWrt( aPasteDebug, *pDoc ); aDbgWrt.Write( xWrt ); #endif SwWriter aWrt( *xStream, *pDoc ); if( ! aWrt.Write( xWrt ).IsError() ) { xStream->WriteChar( '\0' ); // terminate with a zero bRet = true; } } return bRet; } int SwTransferable::Cut() { int nRet = Copy( true ); if( nRet ) DeleteSelection(); collectUIInformation("CUT", "parameter"); return nRet; } void SwTransferable::DeleteSelection() { if(!m_pWrtShell) return; // ask for type of selection before action-bracketing const SelectionType nSelection = m_pWrtShell->GetSelectionType(); // cut rows or columns selected by enhanced table selection and wholly selected tables bool bCutMode = ( SelectionType::TableCell & nSelection ) && ( (SelectionType::TableRow | SelectionType::TableCol) & nSelection || m_pWrtShell->HasWholeTabSelection() ); m_pWrtShell->StartUndo( SwUndoId::START ); if( bCutMode ) { if( !(SelectionType::TableCol & nSelection) ) m_pWrtShell->DeleteTable(); else { SfxDispatcher* pDispatch = m_pWrtShell->GetView().GetViewFrame().GetDispatcher(); pDispatch->Execute(FN_TABLE_DELETE_COL, SfxCallMode::SYNCHRON); } } else { if( ( SelectionType::Text | SelectionType::Table ) & nSelection ) m_pWrtShell->IntelligentCut( nSelection ); m_pWrtShell->DelRight(); } m_pWrtShell->EndUndo( SwUndoId::END ); } static void DeleteDDEMarks(SwDoc & rDest) { IDocumentMarkAccess *const pMarkAccess = rDest.getIDocumentMarkAccess(); std::vector< ::sw::mark::IMark* > vDdeMarks; // find all DDE-Bookmarks for (IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->getAllMarksBegin(); ppMark != pMarkAccess->getAllMarksEnd(); ++ppMark) { if (IDocumentMarkAccess::MarkType::DDE_BOOKMARK == IDocumentMarkAccess::GetType(**ppMark)) { vDdeMarks.push_back(*ppMark); } } // remove all DDE-Bookmarks, they are invalid inside the clipdoc! for (const auto& rpMark : vDdeMarks) { pMarkAccess->deleteMark(rpMark); } } void SwTransferable::PrepareForCopyTextRange(SwPaM & rPaM) { std::optional oWait; if (m_pWrtShell->ShouldWait()) { oWait.emplace( *m_pWrtShell->GetView().GetDocShell(), true ); } m_pClpDocFac.reset(new SwDocFac); SwDoc& rDest(lcl_GetDoc(*m_pClpDocFac)); rDest.getIDocumentFieldsAccess().LockExpFields(); // Never update fields - leave text as is { SwDoc const& rSrc(*m_pWrtShell->GetDoc()); assert(&rSrc == &rPaM.GetDoc()); rDest.ReplaceCompatibilityOptions(rSrc); rDest.ReplaceDefaults(rSrc); //It would probably make most sense here to only insert the styles used //by the selection, e.g. apply SwDoc::IsUsed on styles ? rDest.ReplaceStyles(rSrc, false); // relevant bits of rSrcWrtShell.Copy(rDest); rDest.GetIDocumentUndoRedo().DoUndo(false); // always false! rDest.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::DeleteRedlines ); SwNodeIndex const aIdx(rDest.GetNodes().GetEndOfContent(), -1); SwContentNode *const pContentNode(aIdx.GetNode().GetContentNode()); SwPosition aPos(aIdx, pContentNode, pContentNode ? pContentNode->Len() : 0); rSrc.getIDocumentContentOperations().CopyRange(rPaM, aPos, SwCopyFlags::CheckPosInFly); rDest.getIDocumentRedlineAccess().SetRedlineFlags_intern( RedlineFlags::NONE ); rDest.GetMetaFieldManager().copyDocumentProperties(rSrc); } DeleteDDEMarks(rDest); // a new one was created in core (OLE objects copied!) m_aDocShellRef = rDest.GetTmpDocShell(); if (m_aDocShellRef.Is()) SwTransferable::InitOle( m_aDocShellRef ); rDest.SetTmpDocShell( nullptr ); // let's add some formats AddFormat( SotClipboardFormatId::EMBED_SOURCE ); AddFormat( SotClipboardFormatId::RTF ); #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::RICHTEXT ); AddFormat( SotClipboardFormatId::HTML ); #endif AddFormat( SotClipboardFormatId::STRING ); } int SwTransferable::PrepareForCopy( bool bIsCut ) { int nRet = 1; if(!m_pWrtShell) return 0; if ( m_pWrtShell->GetTableInsertMode() != SwTable::SEARCH_NONE ) m_pWrtShell->SetTableInsertMode( SwTable::SEARCH_NONE ); if ( m_pWrtShell->GetTableCopied() ) m_pWrtShell->SetTableCopied( false ); OUString sGrfNm; const SelectionType nSelection = m_pWrtShell->GetSelectionType(); if( nSelection == SelectionType::Graphic ) { m_oClpGraphic.emplace(); if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_oClpGraphic )) m_pOrigGraphic = &*m_oClpGraphic; m_oClpBitmap.emplace(); if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_oClpBitmap )) m_pOrigGraphic = &*m_oClpBitmap; m_pClpDocFac.reset(new SwDocFac); SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); m_pWrtShell->Copy(rDoc); #if HAVE_FEATURE_DESKTOP if (m_pOrigGraphic && !m_pOrigGraphic->GetBitmapEx().IsEmpty()) AddFormat( SotClipboardFormatId::SVXB ); #endif PrepareOLE( m_aObjDesc ); AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); const Graphic* pGrf = m_pWrtShell->GetGraphic(); if( pGrf && pGrf->IsSupportedGraphic() ) { AddFormat( SotClipboardFormatId::PNG ); #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::GDIMETAFILE ); AddFormat( SotClipboardFormatId::BITMAP ); #endif } m_eBufferType = TransferBufferType::Graphic; m_pWrtShell->GetGrfNms( &sGrfNm, nullptr ); } else if ( nSelection == SelectionType::Ole ) { m_pClpDocFac.reset(new SwDocFac); SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); m_aDocShellRef = new SwDocShell(rDoc, SfxObjectCreateMode::EMBEDDED); m_aDocShellRef->DoInitNew(); m_pWrtShell->Copy(rDoc); AddFormat( SotClipboardFormatId::EMBED_SOURCE ); // --> OD #i98753# // set size of embedded object at the object description structure m_aObjDesc.maSize = o3tl::convert(m_pWrtShell->GetObjSize(), o3tl::Length::twip, o3tl::Length::mm100); // <-- PrepareOLE( m_aObjDesc ); #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); AddFormat( SotClipboardFormatId::GDIMETAFILE ); // Fetch the formats supported via embedtransferhelper as well sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; uno::Reference < embed::XEmbeddedObject > xObj = FindOLEObj( nAspect ); const Graphic* pOLEGraph = FindOLEReplacementGraphic(); if( xObj.is() ) { TransferableDataHelper aD( new SvEmbedTransferHelper( xObj, pOLEGraph, nAspect ) ); if ( aD.GetTransferable().is() ) { DataFlavorExVector aVector( aD.GetDataFlavorExVector() ); for( const auto& rItem : aVector ) AddFormat( rItem ); } } #endif m_eBufferType = TransferBufferType::Ole; } // Is there anything to provide anyway? else if ( m_pWrtShell->IsSelection() || m_pWrtShell->IsFrameSelected() || m_pWrtShell->IsObjSelected() ) { std::optional oWait; if( m_pWrtShell->ShouldWait() ) oWait.emplace( *m_pWrtShell->GetView().GetDocShell(), true ); m_pClpDocFac.reset(new SwDocFac); // create additional cursor so that equal treatment of keyboard // and mouse selection is possible. // In AddMode with keyboard selection, the new cursor is not created // before the cursor is moved after end of selection. if( m_pWrtShell->IsAddMode() && m_pWrtShell->SwCursorShell::HasSelection() ) m_pWrtShell->CreateCursor(); SwDoc& rTmpDoc = lcl_GetDoc(*m_pClpDocFac); rTmpDoc.getIDocumentFieldsAccess().LockExpFields(); // Never update fields - leave text as is lclOverWriteDoc(*m_pWrtShell, rTmpDoc); DeleteDDEMarks(rTmpDoc); // a new one was created in CORE (OLE objects copied!) m_aDocShellRef = rTmpDoc.GetTmpDocShell(); if( m_aDocShellRef.Is() ) SwTransferable::InitOle( m_aDocShellRef ); rTmpDoc.SetTmpDocShell( nullptr ); if( m_pWrtShell->IsObjSelected() ) m_eBufferType = TransferBufferType::Drawing; else { m_eBufferType = TransferBufferType::Document; if (m_pWrtShell->IntelligentCut(nSelection, false) != SwWrtShell::NO_WORD) m_eBufferType = TransferBufferType::DocumentWord | m_eBufferType; } bool bDDELink = m_pWrtShell->IsSelection(); if( nSelection & SelectionType::TableCell ) { m_eBufferType = TransferBufferType::Table | m_eBufferType; bDDELink = m_pWrtShell->HasWholeTabSelection(); m_pWrtShell->SetTableCopied(true); if ( bIsCut && (SelectionType::TableRow | SelectionType::TableCol) & nSelection ) m_pWrtShell->SetTableInsertMode( (SelectionType::TableRow & nSelection) ? SwTable::SEARCH_ROW : SwTable::SEARCH_COL ); } #if HAVE_FEATURE_DESKTOP //When someone needs it, we 'OLE' him something AddFormat( SotClipboardFormatId::EMBED_SOURCE ); #endif //put RTF ahead of the OLE's Metafile to have less loss if( !m_pWrtShell->IsObjSelected() ) { AddFormat( SotClipboardFormatId::RTF ); #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::RICHTEXT ); AddFormat( SotClipboardFormatId::HTML ); #endif } if( m_pWrtShell->IsSelection() ) AddFormat( SotClipboardFormatId::STRING ); if( nSelection & ( SelectionType::DrawObject | SelectionType::DbForm )) { AddFormat( SotClipboardFormatId::DRAWING ); if ( nSelection & SelectionType::DrawObject ) { #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::GDIMETAFILE ); AddFormat( SotClipboardFormatId::BITMAP ); #endif AddFormat( SotClipboardFormatId::PNG ); } m_eBufferType = static_cast( TransferBufferType::Graphic | m_eBufferType ); m_oClpGraphic.emplace(); if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::GDIMETAFILE, *m_oClpGraphic )) m_pOrigGraphic = &*m_oClpGraphic; m_oClpBitmap.emplace(); if( !m_pWrtShell->GetDrawObjGraphic( SotClipboardFormatId::BITMAP, *m_oClpBitmap )) m_pOrigGraphic = &*m_oClpBitmap; // is it a URL-Button ? OUString sURL; OUString sDesc; if( m_pWrtShell->GetURLFromButton( sURL, sDesc ) ) { AddFormat( SotClipboardFormatId::STRING ); #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::SOLK ); AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); AddFormat( SotClipboardFormatId::FILECONTENT ); AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); #endif AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); m_eBufferType = TransferBufferType::InetField | m_eBufferType; nRet = 1; } } // at Cut, DDE-Link doesn't make sense!! SwDocShell* pDShell; if( !bIsCut && bDDELink && nullptr != ( pDShell = m_pWrtShell->GetDoc()->GetDocShell()) && SfxObjectCreateMode::STANDARD == pDShell->GetCreateMode() ) { #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::LINK ); #endif m_xDdeLink = new SwTransferDdeLink( *this, *m_pWrtShell ); } //ObjectDescriptor was already filly from the old DocShell. //Now adjust it. Thus in GetData the first query can still //be answered with delayed rendering. m_aObjDesc.maSize = constOleSize100mm; PrepareOLE( m_aObjDesc ); #if HAVE_FEATURE_DESKTOP AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); #endif } else nRet = 0; if( m_pWrtShell->IsFrameSelected() ) { SfxItemSetFixed aSet( m_pWrtShell->GetAttrPool() ); m_pWrtShell->GetFlyFrameAttr( aSet ); const SwFormatURL& rURL = aSet.Get( RES_URL ); if( rURL.GetMap() ) { m_pImageMap.reset( new ImageMap( *rURL.GetMap() ) ); AddFormat( SotClipboardFormatId::SVIM ); } else if( !rURL.GetURL().isEmpty() ) { m_pTargetURL.reset(new INetImage( sGrfNm, rURL.GetURL(), rURL.GetTargetFrameName() )); AddFormat( SotClipboardFormatId::INET_IMAGE ); } } return nRet; } int SwTransferable::Copy( bool bIsCut ) { if (m_pWrtShell->GetView().GetObjectShell()->isContentExtractionLocked()) return 0; int nRet = PrepareForCopy( bIsCut ); if ( nRet ) { CopyToClipboard( &m_pWrtShell->GetView().GetEditWin() ); } if( !bIsCut ){ collectUIInformation("COPY", "parameter"); } return nRet; } void SwTransferable::CalculateAndCopy() { if(!m_pWrtShell) return; SwWait aWait( *m_pWrtShell->GetView().GetDocShell(), true ); OUString aStr( m_pWrtShell->Calculate() ); m_pClpDocFac.reset(new SwDocFac); SwDoc& rDoc = lcl_GetDoc(*m_pClpDocFac); m_pWrtShell->Copy(rDoc, &aStr); m_eBufferType = TransferBufferType::Document; AddFormat( SotClipboardFormatId::STRING ); CopyToClipboard( &m_pWrtShell->GetView().GetEditWin() ); } bool SwTransferable::CopyGlossary( SwTextBlocks& rGlossary, const OUString& rStr ) { if(!m_pWrtShell) return false; SwWait aWait( *m_pWrtShell->GetView().GetDocShell(), true ); m_pClpDocFac.reset(new SwDocFac); SwDoc& rCDoc = lcl_GetDoc(*m_pClpDocFac); SwNodes& rNds = rCDoc.GetNodes(); SwNodeIndex aNodeIdx( *rNds.GetEndOfContent().StartOfSectionNode() ); SwContentNode* pCNd = rNds.GoNext( &aNodeIdx ); // go to 1st ContentNode SwPaM aPam( *pCNd ); rCDoc.getIDocumentFieldsAccess().LockExpFields(); // never update fields - leave text as it is rCDoc.InsertGlossary( rGlossary, rStr, aPam ); // a new one was created in CORE (OLE-Objects copied!) m_aDocShellRef = rCDoc.GetTmpDocShell(); if( m_aDocShellRef.Is() ) SwTransferable::InitOle( m_aDocShellRef ); rCDoc.SetTmpDocShell( nullptr ); m_eBufferType = TransferBufferType::Document; //When someone needs it, we 'OLE' her something. AddFormat( SotClipboardFormatId::EMBED_SOURCE ); AddFormat( SotClipboardFormatId::RTF ); AddFormat( SotClipboardFormatId::RICHTEXT ); AddFormat( SotClipboardFormatId::HTML ); AddFormat( SotClipboardFormatId::STRING ); //ObjectDescriptor was already filled from the old DocShell. //Now adjust it. Thus in GetData the first query can still //be answered with delayed rendering. m_aObjDesc.maSize = constOleSize100mm; PrepareOLE( m_aObjDesc ); AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); CopyToClipboard( &m_pWrtShell->GetView().GetEditWin() ); return true; } static uno::Reference < XTransferable > * lcl_getTransferPointer ( uno::Reference < XTransferable > &xRef ) { return &xRef; } SwPasteContext::SwPasteContext(SwWrtShell& rWrtShell) : m_rWrtShell(rWrtShell) { remember(); } void SwPasteContext::remember() { if (m_rWrtShell.GetPasteListeners().getLength() == 0) return; SwPaM* pCursor = m_rWrtShell.GetCursor(); if (!pCursor) return; // Set point to the previous node, so it is not moved. const SwNode& rNode = pCursor->GetPoint()->GetNode(); m_oPaM.emplace(rNode, rNode, SwNodeOffset(0), SwNodeOffset(-1)); m_nStartContent = pCursor->GetPoint()->GetContentIndex(); } void SwPasteContext::forget() { m_oPaM.reset(); } SwPasteContext::~SwPasteContext() { try { if (m_rWrtShell.GetPasteListeners().getLength() == 0) return; beans::PropertyValue aPropertyValue; switch (m_rWrtShell.GetView().GetShellMode()) { case ShellMode::Graphic: { SwFrameFormat* pFormat = m_rWrtShell.GetFlyFrameFormat(); if (!pFormat) return; aPropertyValue.Name = "TextGraphicObject"; aPropertyValue.Value <<= uno::Reference(SwXTextGraphicObject::CreateXTextGraphicObject(*pFormat->GetDoc(), pFormat)); break; } default: { if (!m_oPaM) return; SwPaM* pCursor = m_rWrtShell.GetCursor(); if (!pCursor) return; if (!pCursor->GetPoint()->GetNode().IsTextNode()) // Non-text was pasted. return; // Update mark after paste. *m_oPaM->GetMark() = *pCursor->GetPoint(); // Restore point. m_oPaM->GetPoint()->Adjust(SwNodeOffset(1)); SwNode& rNode = m_oPaM->GetPointNode(); if (!rNode.IsTextNode()) // Starting point is no longer text. return; m_oPaM->GetPoint()->SetContent(m_nStartContent); aPropertyValue.Name = "TextRange"; const rtl::Reference xTextRange = SwXTextRange::CreateXTextRange( m_oPaM->GetDoc(), *m_oPaM->GetPoint(), m_oPaM->GetMark()); aPropertyValue.Value <<= uno::Reference(xTextRange); break; } } if (aPropertyValue.Name.isEmpty()) return; // Invoke the listeners. uno::Sequence aEvent{ aPropertyValue }; m_rWrtShell.GetPasteListeners().notifyEach( &css::text::XPasteListener::notifyPasteEvent, aEvent ); } catch (const uno::Exception& rException) { SAL_WARN("sw", "SwPasteContext::~SwPasteContext: uncaught exception: " << rException.Message); } } bool SwTransferable::IsPaste( const SwWrtShell& rSh, const TransferableDataHelper& rData ) { // Check the common case first: We can always paste our own data! // If _only_ the internal format can be pasted, this check will // yield 'true', while the one below would give a (wrong) result 'false'. bool bIsPaste = ( GetSwTransferable( rData ) != nullptr ); // if it's not our own data, we need to have a closer look: if( ! bIsPaste ) { // determine the proper paste action, and return true if we find one uno::Reference xTransferable( rData.GetXTransferable() ); SotExchangeDest nDestination = SwTransferable::GetSotDestination( rSh ); sal_uInt16 nSourceOptions = (( SotExchangeDest::DOC_TEXTFRAME == nDestination || SotExchangeDest::SWDOC_FREE_AREA == nDestination || SotExchangeDest::DOC_TEXTFRAME_WEB == nDestination || SotExchangeDest::SWDOC_FREE_AREA_WEB == nDestination ) ? EXCHG_IN_ACTION_COPY : EXCHG_IN_ACTION_MOVE); SotClipboardFormatId nFormat; // output param for GetExchangeAction sal_uInt8 nEventAction; // output param for GetExchangeAction sal_uInt8 nAction = SotExchange::GetExchangeAction( rData.GetDataFlavorExVector(), nDestination, nSourceOptions, /* ?? */ EXCHG_IN_ACTION_DEFAULT, /* ?? */ nFormat, nEventAction, SotClipboardFormatId::NONE, lcl_getTransferPointer ( xTransferable ) ); // if we find a suitable action, we can paste! bIsPaste = (EXCHG_INOUT_ACTION_NONE != nAction); } return bIsPaste; } void SwTransferable::SelectPasteFormat(TransferableDataHelper& rData, sal_uInt8& nAction, SotClipboardFormatId& nFormat) { if (nFormat != SotClipboardFormatId::RICHTEXT) { return; } if (!rData.HasFormat(SotClipboardFormatId::EMBED_SOURCE)) { return; } if (!rData.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) { return; } TransferableObjectDescriptor aObjDesc; if (!rData.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc)) { return; } if (aObjDesc.maClassName != SvGlobalName(SO3_SW_CLASSID)) { return; } // At this point we know that we paste from Writer to Writer and the clipboard has the content // in both RTF and ODF formats. Prefer ODF in this case. nAction = EXCHG_OUT_ACTION_INSERT_OLE; nFormat = SotClipboardFormatId::EMBED_SOURCE; } // get HTML indentation level by counting tabulator characters before the index // (also index value -1 returns with 0) static sal_Int32 lcl_getLevel(OUString& sText, sal_Int32 nIdx) { sal_Int32 nRet = 0; while ( nIdx-- > 0 && sText[nIdx] == '\t' ) { nRet++; } return nRet; } bool SwTransferable::Paste(SwWrtShell& rSh, TransferableDataHelper& rData, RndStdIds nAnchorType, bool bIgnoreComments, PasteTableType ePasteTable) { SwPasteContext aPasteContext(rSh); sal_uInt8 nAction=0; SotExchangeDest nDestination = SwTransferable::GetSotDestination( rSh ); SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; SotExchangeActionFlags nActionFlags = SotExchangeActionFlags::NONE; bool bSingleCellTable = false; if( GetSwTransferable( rData ) ) { nAction = EXCHG_OUT_ACTION_INSERT_PRIVATE; } else { sal_uInt16 nSourceOptions = (( SotExchangeDest::DOC_TEXTFRAME == nDestination || SotExchangeDest::SWDOC_FREE_AREA == nDestination || SotExchangeDest::DOC_TEXTFRAME_WEB == nDestination || SotExchangeDest::SWDOC_FREE_AREA_WEB == nDestination ) ? EXCHG_IN_ACTION_COPY : EXCHG_IN_ACTION_MOVE); uno::Reference xTransferable( rData.GetXTransferable() ); sal_uInt8 nEventAction; nAction = SotExchange::GetExchangeAction( rData.GetDataFlavorExVector(), nDestination, nSourceOptions, /* ?? */ EXCHG_IN_ACTION_DEFAULT, /* ?? */ nFormat, nEventAction, SotClipboardFormatId::NONE, lcl_getTransferPointer ( xTransferable ), &nActionFlags ); } // when HTML is just an image don't generate new section if (rData.HasFormat(SotClipboardFormatId::HTML_SIMPLE) && rData.HasFormat(SotClipboardFormatId::HTML_NO_COMMENT) && rData.HasFormat(SotClipboardFormatId::BITMAP) && nFormat == SotClipboardFormatId::FILE_LIST) nFormat = SotClipboardFormatId::BITMAP; // tdf#37223 avoid non-native insertion of Calc worksheets in the following cases: // content of 1-cell worksheets are inserted as simple text using RTF format, // bigger worksheets within native (Writer) table cells are inserted as native tables, // ie. cell by cell instead of embedding the worksheet in a single cell of the Writer table if ( EXCHG_IN_ACTION_COPY == nAction && ( rData.HasFormat( SotClipboardFormatId::SYLK ) || rData.HasFormat( SotClipboardFormatId::SYLK_BIGCAPS ) ) ) { // is it a 1-cell worksheet? OUString aExpand; if( rData.GetString( SotClipboardFormatId::STRING, aExpand )) { const sal_Int32 nNewlines{comphelper::string::getTokenCount(aExpand, '\n')}; const sal_Int32 nRows = nNewlines ? nNewlines-1 : 0; if ( nRows == 1 ) { const sal_Int32 nCols = comphelper::string::getTokenCount(o3tl::getToken(aExpand, 0, '\n'), '\t'); if (nCols == 1) bSingleCellTable = true; } } // convert the worksheet to a temporary native table using HTML format, and copy that into the original native table if (!bSingleCellTable && rData.HasFormat( SotClipboardFormatId::HTML ) && SwDoc::IsInTable(rSh.GetCursor()->GetPointNode()) != nullptr && rSh.DoesUndo()) { SfxDispatcher* pDispatch = rSh.GetView().GetViewFrame().GetDispatcher(); sal_uInt32 nLevel = 0; // within Writer table cells, inserting worksheets using HTML format results only plain text, not a native table, // so remove all outer nested tables temporary to get a working insertion point // (RTF format has no such problem, but that inserts the hidden rows of the original Calc worksheet, too) // For this, switch off change tracking temporarily, if needed RedlineFlags eOld = rSh.GetDoc()->getIDocumentRedlineAccess().GetRedlineFlags(); if ( eOld & RedlineFlags::On ) rSh.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOld & ~RedlineFlags::On ); OUString sPreviousTableName; do { // tdf#152245 add a limit to the loop, if it's not possible to delete the table const SwTableNode* pNode = rSh.GetCursor()->GetPointNode().FindTableNode(); const OUString sTableName = pNode->GetTable().GetFrameFormat()->GetName(); if ( sTableName == sPreviousTableName ) break; sPreviousTableName = sTableName; // insert a random character to redo the place of the insertion at the end pDispatch->Execute(FN_INSERT_NNBSP, SfxCallMode::SYNCHRON); pDispatch->Execute(FN_TABLE_DELETE_TABLE, SfxCallMode::SYNCHRON); nLevel++; } while (SwDoc::IsInTable(rSh.GetCursor()->GetPointNode()) != nullptr); // restore change tracking settings if ( eOld & RedlineFlags::On ) rSh.GetDoc()->getIDocumentRedlineAccess().SetRedlineFlags( eOld ); if ( SwTransferable::PasteData( rData, rSh, EXCHG_OUT_ACTION_INSERT_STRING, nActionFlags, SotClipboardFormatId::HTML, nDestination, false, false, nullptr, 0, false, nAnchorType, bIgnoreComments, &aPasteContext, ePasteTable) ) { bool bFoundTemporaryTable = false; pDispatch->Execute(FN_LINE_UP, SfxCallMode::SYNCHRON); if (SwDoc::IsInTable(rSh.GetCursor()->GetPointNode()) != nullptr) { bFoundTemporaryTable = true; pDispatch->Execute(FN_TABLE_SELECT_ALL, SfxCallMode::SYNCHRON); pDispatch->Execute(SID_COPY, SfxCallMode::SYNCHRON); } for(sal_uInt32 a = 0; a < 1 + (nLevel * 2); a++) pDispatch->Execute(SID_UNDO, SfxCallMode::SYNCHRON); // clipboard content hasn't changed (limit potential infinite // recursion with the same non-native table, as was in tdf#138688) if (!bFoundTemporaryTable) return false; if (ePasteTable == PasteTableType::PASTE_TABLE) pDispatch->Execute(FN_PASTE_NESTED_TABLE, SfxCallMode::SYNCHRON); else if (ePasteTable == PasteTableType::PASTE_ROW) pDispatch->Execute(FN_TABLE_PASTE_ROW_BEFORE, SfxCallMode::SYNCHRON); else if (ePasteTable == PasteTableType::PASTE_COLUMN) pDispatch->Execute(FN_TABLE_PASTE_COL_BEFORE, SfxCallMode::SYNCHRON); else pDispatch->Execute(SID_PASTE, SfxCallMode::SYNCHRON); return true; } else { for(sal_uInt32 a = 0; a < (nLevel * 2); a++) pDispatch->Execute(SID_UNDO, SfxCallMode::SYNCHRON); } } } // insert clipboard content as new table rows/columns before the actual row/column instead of overwriting it else if ( (rSh.GetTableInsertMode() != SwTable::SEARCH_NONE || ePasteTable == PasteTableType::PASTE_ROW || ePasteTable == PasteTableType::PASTE_COLUMN) && rData.HasFormat( SotClipboardFormatId::HTML ) && SwDoc::IsInTable(rSh.GetCursor()->GetPointNode()) != nullptr ) { OUString aExpand; sal_Int32 nIdx; bool bRowMode = rSh.GetTableInsertMode() == SwTable::SEARCH_ROW || ePasteTable == PasteTableType::PASTE_ROW; if( rData.GetString( SotClipboardFormatId::HTML, aExpand ) && (nIdx = aExpand.indexOf(" -1 ) { // calculate table row/column count by analysing indentation of the HTML table extract // calculate indentation level of , which is the base of the next calculations // (tdf#148791 table alignment can enlarge it using first level
,
or
) sal_Int32 nTableLevel = lcl_getLevel(aExpand, nIdx); // table rows repeated heading use extra indentation, too: //
is always used here, and the first table with is not nested, // if its indentation level is greater only by 1, than indentation level of the table bool bShifted = lcl_getLevel(aExpand, aExpand.indexOf("") : OUString("-1) { // skip rows/columns of nested tables, based on HTML indentation if ( lcl_getLevel(aExpand, nIdx) == nTableLevel + (bShifted ? 2 : 1) && // skip also strange hidden empty rows !aExpand.match("", nIdx - 4) ) { ++nSelectedRowsOrCols; } ++nIdx; } // are we at the beginning of the cell? bool bStartTableBoxNode = // first paragraph of the cell? rSh.GetCursor()->GetPointNode().GetIndex() == rSh.GetCursor()->GetPointNode().FindTableBoxStartNode()->GetIndex()+1 && // beginning of the paragraph? !rSh.GetCursor()->GetPoint()->GetContentIndex(); SfxDispatcher* pDispatch = rSh.GetView().GetViewFrame().GetDispatcher(); // go start of the cell if (!bStartTableBoxNode) pDispatch->Execute(FN_START_OF_DOCUMENT, SfxCallMode::SYNCHRON); // store cursor position in row mode ::sw::mark::IMark* pMark = (!bRowMode || nSelectedRowsOrCols == 0) ? nullptr : rSh.SetBookmark( vcl::KeyCode(), OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK ); // add a new empty row/column before the actual table row/column and go there const sal_uInt16 nDispatchSlot = bRowMode ? FN_TABLE_INSERT_ROW_BEFORE : FN_TABLE_INSERT_COL_BEFORE; pDispatch->Execute(nDispatchSlot, SfxCallMode::SYNCHRON); pDispatch->Execute(bRowMode ? FN_LINE_UP : FN_CHAR_LEFT, SfxCallMode::SYNCHRON); // add the other new empty rows/columns after the actual table row/column if ( nSelectedRowsOrCols > 1 ) { SfxInt16Item aCountItem( nDispatchSlot, nSelectedRowsOrCols-1 ); SfxBoolItem aAfter( FN_PARAM_INSERT_AFTER, true ); pDispatch->ExecuteList(nDispatchSlot, SfxCallMode::SYNCHRON|SfxCallMode::RECORD, { &aCountItem, &aAfter }); } // paste rows bool bResult = SwTransferable::PasteData( rData, rSh, nAction, nActionFlags, nFormat, nDestination, false, false, nullptr, 0, false, nAnchorType, bIgnoreComments, &aPasteContext ); // restore cursor position if (pMark != nullptr) { rSh.GotoMark( pMark ); rSh.getIDocumentMarkAccess()->deleteMark( pMark ); } return bResult; } } // special case for tables from draw application or 1-cell tables if( EXCHG_OUT_ACTION_INSERT_DRAWOBJ == nAction || bSingleCellTable ) { if( rData.HasFormat( SotClipboardFormatId::RTF ) ) { nAction = EXCHG_OUT_ACTION_INSERT_STRING; nFormat = SotClipboardFormatId::RTF; } else if( rData.HasFormat( SotClipboardFormatId::RICHTEXT ) ) { nAction = EXCHG_OUT_ACTION_INSERT_STRING; nFormat = SotClipboardFormatId::RICHTEXT; } } // Tweak the format if necessary: the source application can be considered in this context, // while not in sot/ code. SwTransferable::SelectPasteFormat(rData, nAction, nFormat); collectUIInformation("PASTE", "parameter"); return EXCHG_INOUT_ACTION_NONE != nAction && SwTransferable::PasteData( rData, rSh, nAction, nActionFlags, nFormat, nDestination, false, false, nullptr, 0, false, nAnchorType, bIgnoreComments, &aPasteContext, ePasteTable); } bool SwTransferable::PasteData( TransferableDataHelper& rData, SwWrtShell& rSh, sal_uInt8 nAction, SotExchangeActionFlags nActionFlags, SotClipboardFormatId nFormat, SotExchangeDest nDestination, bool bIsPasteFormat, bool bIsDefault, const Point* pPt, sal_Int8 nDropAction, bool bPasteSelection, RndStdIds nAnchorType, bool bIgnoreComments, SwPasteContext* pContext, PasteTableType ePasteTable ) { SwWait aWait( *rSh.GetView().GetDocShell(), false ); std::unique_ptr> pAction; SwModule* pMod = SW_MOD(); bool bRet = false; bool bCallAutoCaption = false; if( pPt ) { // external Drop if ((bPasteSelection ? !pMod->m_pXSelection : !pMod->m_pDragDrop) && // The following condition is used for tdf#156111 to prevent a selection from being // cleared by the default case of the nDestination switch. !(rSh.GetCursorCnt() == 1 && rSh.TestCurrPam(*pPt) && nDestination == SotExchangeDest::SWDOC_FREE_AREA && nFormat == SotClipboardFormatId::SONLK)) { switch( nDestination ) { case SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP: case SotExchangeDest::DOC_LNKD_GRAPHOBJ: case SotExchangeDest::DOC_GRAPH_W_IMAP: case SotExchangeDest::DOC_GRAPHOBJ: case SotExchangeDest::DOC_OLEOBJ: case SotExchangeDest::DOC_DRAWOBJ: case SotExchangeDest::DOC_URLBUTTON: case SotExchangeDest::DOC_GROUPOBJ: // select frames/objects SwTransferable::SetSelInShell( rSh, true, pPt ); break; default: SwTransferable::SetSelInShell( rSh, false, pPt ); break; } } } else if( ( !GetSwTransferable( rData ) || bIsPasteFormat ) && !rSh.IsTableMode() && rSh.HasSelection() ) { // then delete the selections //don't delete selected content // - at table-selection // - at ReRead of a graphic/DDEData // - at D&D, for the right selection was taken care of // in Drop-Handler bool bDelSel = false; switch( nDestination ) { case SotExchangeDest::DOC_TEXTFRAME: case SotExchangeDest::SWDOC_FREE_AREA: case SotExchangeDest::DOC_TEXTFRAME_WEB: case SotExchangeDest::SWDOC_FREE_AREA_WEB: bDelSel = true; break; default: break; } if( bDelSel ) // #i34830# pAction.reset(new SwTrnsfrActionAndUndo(&rSh, true, pContext)); } SwTransferable *pTrans=nullptr, *pTunneledTrans=GetSwTransferable( rData ); // check for private drop bool bPrivateDrop(pPt); if (bPrivateDrop) { if (bPasteSelection) pTrans = pMod->m_pXSelection; else pTrans = pMod->m_pDragDrop; bPrivateDrop = nullptr != pTrans; } bool bNeedToSelectBeforePaste(false); if(bPrivateDrop && DND_ACTION_LINK == nDropAction) { // internal drop on object, suppress bPrivateDrop to change internal fill bPrivateDrop = false; bNeedToSelectBeforePaste = true; } if(bPrivateDrop && pPt && DND_ACTION_MOVE == nDropAction) { // check if dragged over a useful target. If yes, use as content exchange // drop as if from external const SwFrameFormat* pSwFrameFormat = rSh.GetFormatFromObj(*pPt); if(dynamic_cast< const SwDrawFrameFormat* >(pSwFrameFormat)) { bPrivateDrop = false; bNeedToSelectBeforePaste = true; } } if(bPrivateDrop) { // then internal Drag & Drop or XSelection bRet = pTrans->PrivateDrop( rSh, *pPt, DND_ACTION_MOVE == nDropAction, bPasteSelection ); } else if( !pPt && pTunneledTrans && EXCHG_OUT_ACTION_INSERT_PRIVATE == nAction ) { // then internal paste bRet = pTunneledTrans->PrivatePaste(rSh, pContext, ePasteTable); } else if( EXCHG_INOUT_ACTION_NONE != nAction ) { if( !pAction ) { pAction.reset(new SwTrnsfrActionAndUndo( &rSh )); } // in Drag&Drop MessageBoxes must not be showed bool bMsg = nullptr == pPt; // delete selections switch( nAction ) { case EXCHG_OUT_ACTION_INSERT_PRIVATE: OSL_ENSURE( pPt, "EXCHG_OUT_ACTION_INSERT_PRIVATE: what should happen here?" ); break; case EXCHG_OUT_ACTION_MOVE_PRIVATE: OSL_ENSURE( pPt, "EXCHG_OUT_ACTION_MOVE_PRIVATE: what should happen here?" ); break; case EXCHG_IN_ACTION_MOVE: case EXCHG_IN_ACTION_COPY: case EXCHG_IN_ACTION_LINK: case EXCHG_OUT_ACTION_INSERT_HTML: case EXCHG_OUT_ACTION_INSERT_STRING: case EXCHG_OUT_ACTION_INSERT_IMAGEMAP: case EXCHG_OUT_ACTION_REPLACE_IMAGEMAP: // then we have to use the format switch( nFormat ) { case SotClipboardFormatId::DRAWING: bRet = SwTransferable::PasteSdrFormat( rData, rSh, SwPasteSdr::Insert, pPt, nActionFlags, bNeedToSelectBeforePaste); break; case SotClipboardFormatId::HTML: case SotClipboardFormatId::HTML_SIMPLE: case SotClipboardFormatId::HTML_NO_COMMENT: case SotClipboardFormatId::RTF: case SotClipboardFormatId::RICHTEXT: case SotClipboardFormatId::STRING: bRet = SwTransferable::PasteFileContent( rData, rSh, nFormat, bMsg, bIgnoreComments ); break; case SotClipboardFormatId::NETSCAPE_BOOKMARK: { INetBookmark aBkmk; if( rData.GetINetBookmark( nFormat, aBkmk ) ) { SwFormatINetFormat aFormat( aBkmk.GetURL(), OUString() ); rSh.InsertURL( aFormat, aBkmk.GetDescription() ); bRet = true; } } break; case SotClipboardFormatId::SD_OLE: bRet = SwTransferable::PasteOLE( rData, rSh, nFormat, nActionFlags, bMsg ); break; case SotClipboardFormatId::SVIM: bRet = SwTransferable::PasteImageMap( rData, rSh ); break; case SotClipboardFormatId::SVXB: case SotClipboardFormatId::BITMAP: case SotClipboardFormatId::PNG: case SotClipboardFormatId::GDIMETAFILE: bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, SwPasteSdr::Insert,pPt, nActionFlags, nDropAction, bNeedToSelectBeforePaste); break; case SotClipboardFormatId::XFORMS: case SotClipboardFormatId::SBA_FIELDDATAEXCHANGE: case SotClipboardFormatId::SBA_DATAEXCHANGE: case SotClipboardFormatId::SBA_CTRLDATAEXCHANGE: bRet = SwTransferable::PasteDBData( rData, rSh, nFormat, EXCHG_IN_ACTION_LINK == nAction, pPt, bMsg ); break; case SotClipboardFormatId::SIMPLE_FILE: bRet = SwTransferable::PasteFileName( rData, rSh, nFormat, ( EXCHG_IN_ACTION_MOVE == nAction ? SwPasteSdr::Replace : EXCHG_IN_ACTION_LINK == nAction ? SwPasteSdr::SetAttr : SwPasteSdr::Insert), pPt, nActionFlags, nullptr ); break; case SotClipboardFormatId::FILE_LIST: // then insert as graphics only bRet = SwTransferable::PasteFileList( rData, rSh, EXCHG_IN_ACTION_LINK == nAction, pPt, bMsg ); break; case SotClipboardFormatId::SONLK: if( pPt ) { NaviContentBookmark aBkmk; if (aBkmk.Paste(rData, rSh.GetSelText())) { if(bIsDefault) { switch(aBkmk.GetDefaultDragType()) { case RegionMode::NONE: nAction = EXCHG_IN_ACTION_COPY; break; case RegionMode::EMBEDDED: nAction = EXCHG_IN_ACTION_MOVE; break; case RegionMode::LINK: nAction = EXCHG_IN_ACTION_LINK; break; } } rSh.NavigatorPaste( aBkmk, nAction ); bRet = true; } } break; case SotClipboardFormatId::INET_IMAGE: case SotClipboardFormatId::NETSCAPE_IMAGE: bRet = SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::Insert, pPt, true ); break; default: OSL_ENSURE( pPt, "unknown format" ); } break; case EXCHG_OUT_ACTION_INSERT_FILE: { bool graphicInserted; bRet = SwTransferable::PasteFileName( rData, rSh, nFormat, SwPasteSdr::Insert, pPt, nActionFlags, &graphicInserted ); if( graphicInserted ) bCallAutoCaption = true; } break; case EXCHG_OUT_ACTION_INSERT_OLE: bRet = SwTransferable::PasteOLE( rData, rSh, nFormat, nActionFlags,bMsg ); break; case EXCHG_OUT_ACTION_INSERT_DDE: { bool bReRead = 0 != CNT_HasGrf( rSh.GetCntType() ); bRet = SwTransferable::PasteDDE( rData, rSh, bReRead, bMsg ); } break; case EXCHG_OUT_ACTION_INSERT_HYPERLINK: { OUString sURL, sDesc; if( SotClipboardFormatId::SIMPLE_FILE == nFormat ) { if( rData.GetString( nFormat, sURL ) && !sURL.isEmpty() ) { SwTransferable::CheckForURLOrLNKFile( rData, sURL, &sDesc ); if( sDesc.isEmpty() ) sDesc = sURL; bRet = true; } } else { INetBookmark aBkmk; if( rData.GetINetBookmark( nFormat, aBkmk ) ) { sURL = aBkmk.GetURL(); sDesc = aBkmk.GetDescription(); bRet = true; } } if( bRet ) { SwFormatINetFormat aFormat( sURL, OUString() ); rSh.InsertURL( aFormat, sDesc ); } } break; case EXCHG_OUT_ACTION_GET_ATTRIBUTES: switch( nFormat ) { case SotClipboardFormatId::DRAWING: bRet = SwTransferable::PasteSdrFormat( rData, rSh, SwPasteSdr::SetAttr, pPt, nActionFlags, bNeedToSelectBeforePaste); break; case SotClipboardFormatId::SVXB: case SotClipboardFormatId::GDIMETAFILE: case SotClipboardFormatId::BITMAP: case SotClipboardFormatId::PNG: case SotClipboardFormatId::NETSCAPE_BOOKMARK: case SotClipboardFormatId::SIMPLE_FILE: case SotClipboardFormatId::FILEGRPDESCRIPTOR: case SotClipboardFormatId::UNIFORMRESOURCELOCATOR: bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, SwPasteSdr::SetAttr, pPt, nActionFlags, nDropAction, bNeedToSelectBeforePaste); break; default: OSL_FAIL( "unknown format" ); } break; case EXCHG_OUT_ACTION_INSERT_DRAWOBJ: bRet = SwTransferable::PasteSdrFormat( rData, rSh, SwPasteSdr::Insert, pPt, nActionFlags, bNeedToSelectBeforePaste); break; case EXCHG_OUT_ACTION_INSERT_SVXB: case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE: case EXCHG_OUT_ACTION_INSERT_BITMAP: case EXCHG_OUT_ACTION_INSERT_GRAPH: bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, SwPasteSdr::Insert, pPt, nActionFlags, nDropAction, bNeedToSelectBeforePaste, nAnchorType ); break; case EXCHG_OUT_ACTION_REPLACE_DRAWOBJ: bRet = SwTransferable::PasteSdrFormat( rData, rSh, SwPasteSdr::Replace, pPt, nActionFlags, bNeedToSelectBeforePaste); break; case EXCHG_OUT_ACTION_REPLACE_SVXB: case EXCHG_OUT_ACTION_REPLACE_GDIMETAFILE: case EXCHG_OUT_ACTION_REPLACE_BITMAP: case EXCHG_OUT_ACTION_REPLACE_GRAPH: bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, SwPasteSdr::Replace,pPt, nActionFlags, nDropAction, bNeedToSelectBeforePaste); break; case EXCHG_OUT_ACTION_INSERT_INTERACTIVE: bRet = SwTransferable::PasteAsHyperlink( rData, rSh, nFormat ); break; default: OSL_FAIL("unknown action" ); } } if( !bPasteSelection && rSh.IsFrameSelected() ) { rSh.EnterSelFrameMode(); //force ::SelectShell rSh.GetView().StopShellTimer(); } pAction.reset(); if( bCallAutoCaption ) rSh.GetView().AutoCaption( GRAPHIC_CAP ); return bRet; } SotExchangeDest SwTransferable::GetSotDestination( const SwWrtShell& rSh ) { SotExchangeDest nRet = SotExchangeDest::NONE; ObjCntType eOType = rSh.GetObjCntTypeOfSelection(); switch( eOType ) { case OBJCNT_GRF: { bool bIMap, bLink; bIMap = nullptr != rSh.GetFlyFrameFormat()->GetURL().GetMap(); OUString aDummy; rSh.GetGrfNms( &aDummy, nullptr ); bLink = !aDummy.isEmpty(); if( bLink && bIMap ) nRet = SotExchangeDest::DOC_LNKD_GRAPH_W_IMAP; else if( bLink ) nRet = SotExchangeDest::DOC_LNKD_GRAPHOBJ; else if( bIMap ) nRet = SotExchangeDest::DOC_GRAPH_W_IMAP; else nRet = SotExchangeDest::DOC_GRAPHOBJ; } break; case OBJCNT_FLY: if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr ) nRet = SotExchangeDest::DOC_TEXTFRAME_WEB; else nRet = SotExchangeDest::DOC_TEXTFRAME; break; case OBJCNT_OLE: nRet = SotExchangeDest::DOC_OLEOBJ; break; case OBJCNT_CONTROL: /* no Action avail */ case OBJCNT_SIMPLE: nRet = SotExchangeDest::DOC_DRAWOBJ; break; case OBJCNT_URLBUTTON: nRet = SotExchangeDest::DOC_URLBUTTON; break; case OBJCNT_GROUPOBJ: nRet = SotExchangeDest::DOC_GROUPOBJ; break; // what do we do at multiple selections??? default: { if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr ) nRet = SotExchangeDest::SWDOC_FREE_AREA_WEB; else nRet = SotExchangeDest::SWDOC_FREE_AREA; } } return nRet; } bool SwTransferable::PasteFileContent( const TransferableDataHelper& rData, SwWrtShell& rSh, SotClipboardFormatId nFormat, bool bMsg, bool bIgnoreComments ) { TranslateId pResId = STR_CLPBRD_FORMAT_ERROR; bool bRet = false; MSE40HTMLClipFormatObj aMSE40ClpObj; tools::SvRef xStrm; SvStream* pStream = nullptr; Reader* pRead = nullptr; OUString sData; switch( nFormat ) { case SotClipboardFormatId::STRING: { pRead = ReadAscii; if( rData.GetString( nFormat, sData ) ) { pStream = new SvMemoryStream( const_cast(sData.getStr()), sData.getLength() * sizeof( sal_Unicode ), StreamMode::READ ); #ifdef OSL_BIGENDIAN pStream->SetEndian( SvStreamEndian::BIG ); #else pStream->SetEndian( SvStreamEndian::LITTLE ); #endif SwAsciiOptions aAOpt; aAOpt.SetCharSet( RTL_TEXTENCODING_UCS2 ); pRead->GetReaderOpt().SetASCIIOpts( aAOpt ); break; } } [[fallthrough]]; // because then test if we get a stream default: if( rData.GetSotStorageStream( nFormat, xStrm ) ) { if( ( SotClipboardFormatId::HTML_SIMPLE == nFormat ) || ( SotClipboardFormatId::HTML_NO_COMMENT == nFormat ) ) { pStream = aMSE40ClpObj.IsValid( *xStrm ); pRead = ReadHTML; pRead->SetReadUTF8( true ); bool bNoComments = ( nFormat == SotClipboardFormatId::HTML_NO_COMMENT ); pRead->SetIgnoreHTMLComments( bNoComments ); } else { pStream = xStrm.get(); if( SotClipboardFormatId::RTF == nFormat || SotClipboardFormatId::RICHTEXT == nFormat) pRead = SwReaderWriter::GetRtfReader(); else if( !pRead ) { pRead = ReadHTML; pRead->SetReadUTF8( true ); } } } break; } if( pStream && pRead ) { Link aOldLink( rSh.GetChgLnk() ); rSh.SetChgLnk( Link() ); const SwPosition& rInsPos = *rSh.GetCursor()->Start(); SwReader aReader(*pStream, OUString(), OUString(), *rSh.GetCursor()); rSh.SaveTableBoxContent( &rInsPos ); if (bIgnoreComments) pRead->SetIgnoreHTMLComments(true); if( aReader.Read( *pRead ).IsError() ) pResId = STR_ERROR_CLPBRD_READ; else { pResId = TranslateId(); bRet = true; } rSh.SetChgLnk( aOldLink ); if( bRet ) rSh.CallChgLnk(); } else pResId = STR_CLPBRD_FORMAT_ERROR; // Exist a SvMemoryStream? (data in the OUString and xStrm is empty) if( pStream && !xStrm.is() ) delete pStream; if (bMsg && pResId) { std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, VclMessageType::Info, VclButtonsType::Ok, SwResId(pResId))); xBox->run(); } return bRet; } bool SwTransferable::PasteOLE( TransferableDataHelper& rData, SwWrtShell& rSh, SotClipboardFormatId nFormat, SotExchangeActionFlags nActionFlags, bool bMsg ) { bool bRet = false; TransferableObjectDescriptor aObjDesc; uno::Reference < io::XInputStream > xStrm; uno::Reference < embed::XStorage > xStore; Reader* pRead = nullptr; // Get the preferred format SotClipboardFormatId nId; if( rData.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ) nId = SotClipboardFormatId::EMBEDDED_OBJ; else if( rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) && rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )) nId = SotClipboardFormatId::EMBED_SOURCE; else nId = SotClipboardFormatId::NONE; if (nId != SotClipboardFormatId::NONE) { SwDocShell* pDocSh = rSh.GetDoc()->GetDocShell(); xStrm = rData.GetInputStream(nId, SfxObjectShell::CreateShellID(pDocSh)); } if (xStrm.is()) { // if there is an embedded object, first try if it's a writer object // this will be inserted into the document by using a Reader try { xStore = comphelper::OStorageHelper::GetStorageFromInputStream( xStrm ); switch( SotStorage::GetFormatID( xStore ) ) { case SotClipboardFormatId::STARWRITER_60: case SotClipboardFormatId::STARWRITERWEB_60: case SotClipboardFormatId::STARWRITERGLOB_60: case SotClipboardFormatId::STARWRITER_8: case SotClipboardFormatId::STARWRITERWEB_8: case SotClipboardFormatId::STARWRITERGLOB_8: pRead = ReadXML; break; default: try { xStore->dispose(); xStore = nullptr; } catch (const uno::Exception&) { } break; } } catch (const uno::Exception&) { // it wasn't a storage, but maybe it's a useful stream } } if( pRead ) { SwPaM &rPAM = *rSh.GetCursor(); SwReader aReader(xStore, OUString(), rPAM); if( ! aReader.Read( *pRead ).IsError() ) bRet = true; else if( bMsg ) { std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, VclMessageType::Info, VclButtonsType::Ok, SwResId(STR_ERROR_CLPBRD_READ))); xBox->run(); } } else { // temporary storage until the object is inserted uno::Reference< embed::XStorage > xTmpStor; uno::Reference < embed::XEmbeddedObject > xObj; OUString aName; comphelper::EmbeddedObjectContainer aCnt; if ( xStrm.is() ) { if ( !rData.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) ) { OSL_ENSURE( !xStrm.is(), "An object without descriptor in clipboard!"); } } else { if( rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE ) && rData.GetTransferableObjectDescriptor( nFormat, aObjDesc ) ) { xStrm = rData.GetInputStream(SotClipboardFormatId::EMBED_SOURCE_OLE, OUString()); if (!xStrm.is()) xStrm = rData.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString()); if ( !xStrm.is() ) { // This is MSOLE object that should be created by direct using of system clipboard try { xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage(); uno::Reference < embed::XEmbedObjectClipboardCreator > xClipboardCreator = embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() ); embed::InsertedObjectInfo aInfo = xClipboardCreator->createInstanceInitFromClipboard( xTmpStor, "DummyName", uno::Sequence< beans::PropertyValue >() ); // TODO/LATER: in future InsertedObjectInfo will be used to get container related information // for example whether the object should be an iconified one xObj = aInfo.Object; } catch (const uno::Exception&) { } } } else if (rData.HasFormat(SotClipboardFormatId::SIMPLE_FILE)) { OUString sFile; if (rData.GetString(nFormat, sFile) && !sFile.isEmpty()) { // Copied from sd::View::DropInsertFileHdl uno::Sequence< beans::PropertyValue > aMedium{ comphelper::makePropertyValue( "URL", sFile) }; SwDocShell* pDocSh = rSh.GetDoc()->GetDocShell(); xObj = pDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject(aMedium, aName); } } } if ( xStrm.is() && !xObj.is() ) xObj = aCnt.InsertEmbeddedObject( xStrm, aName ); if( xObj.is() ) { svt::EmbeddedObjectRef xObjRef( xObj, aObjDesc.mnViewAspect ); // try to get the replacement image from the clipboard Graphic aGraphic; SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE; // limit the size of the preview metafile to 100000 actions GDIMetaFile aMetafile; if (rData.GetGDIMetaFile(SotClipboardFormatId::GDIMETAFILE, aMetafile, 100000)) { nGrFormat = SotClipboardFormatId::GDIMETAFILE; aGraphic = aMetafile; } // insert replacement image ( if there is one ) into the object helper if ( nGrFormat != SotClipboardFormatId::NONE ) { DataFlavor aDataFlavor; SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor ); xObjRef.SetGraphic( aGraphic, aDataFlavor.MimeType ); } else if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) { // it is important to have an icon, let an empty graphic be used // if no other graphic is provided // TODO/LATER: in future a default bitmap could be used MapMode aMapMode( MapUnit::Map100thMM ); aGraphic.SetPrefSize( Size( 2500, 2500 ) ); aGraphic.SetPrefMapMode( aMapMode ); xObjRef.SetGraphic( aGraphic, OUString() ); } //set size. This is a hack because of handing over, size should be //passed to the InsertOle!!!!!!!!!! Size aSize; if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) { if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) aSize = aObjDesc.maSize; else { MapMode aMapMode( MapUnit::Map100thMM ); aSize = xObjRef.GetSize( &aMapMode ); } } else if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) { aSize = aObjDesc.maSize; //always 100TH_MM MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( aObjDesc.mnViewAspect ) ); aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aUnit)); awt::Size aSz; try { aSz = xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); } catch (const embed::NoVisualAreaSizeException&) { // in this case the provided size is used } if ( aSz.Width != aSize.Width() || aSz.Height != aSize.Height() ) { aSz.Width = aSize.Width(); aSz.Height = aSize.Height(); xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); } } else { // the descriptor contains the wrong object size // the following call will let the MSOLE objects cache the size if it is possible // it should be done while the object is running try { xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); } catch (const uno::Exception&) { } } //End of Hack! rSh.InsertOleObject( xObjRef ); bRet = true; if( bRet && ( nActionFlags & SotExchangeActionFlags::InsertTargetUrl) ) SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::NONE, nullptr, false ); // let the object be unloaded if possible SwOLEObj::UnloadObject( xObj, rSh.GetDoc(), embed::Aspects::MSOLE_CONTENT ); } } return bRet; } bool SwTransferable::PasteTargetURL( const TransferableDataHelper& rData, SwWrtShell& rSh, SwPasteSdr nAction, const Point* pPt, bool bInsertGRF ) { bool bRet = false; INetImage aINetImg; if( ( rData.HasFormat( SotClipboardFormatId::INET_IMAGE ) && rData.GetINetImage( SotClipboardFormatId::INET_IMAGE, aINetImg )) || ( rData.HasFormat( SotClipboardFormatId::NETSCAPE_IMAGE ) && rData.GetINetImage( SotClipboardFormatId::NETSCAPE_IMAGE, aINetImg )) ) { if( !aINetImg.GetImageURL().isEmpty() && bInsertGRF ) { OUString sURL( aINetImg.GetImageURL() ); SwTransferable::CheckForURLOrLNKFile( rData, sURL ); //!!! check at FileSystem - only then it makes sense to test graphics !!! Graphic aGraphic; GraphicFilter &rFlt = GraphicFilter::GetGraphicFilter(); bRet = ERRCODE_NONE == GraphicFilter::LoadGraphic(sURL, OUString(), aGraphic, &rFlt); if( bRet ) { //Check and Perform rotation if needed lclCheckAndPerformRotation(aGraphic); switch( nAction ) { case SwPasteSdr::Insert: SwTransferable::SetSelInShell( rSh, false, pPt ); rSh.InsertGraphic(sURL, OUString(), aGraphic); break; case SwPasteSdr::Replace: if( rSh.IsObjSelected() ) { rSh.ReplaceSdrObj( sURL, &aGraphic ); Point aPt( pPt ? *pPt : rSh.GetCursorDocPos() ); SwTransferable::SetSelInShell( rSh, true, &aPt ); } else rSh.ReRead(sURL, OUString(), &aGraphic); break; case SwPasteSdr::SetAttr: if( rSh.IsObjSelected() ) rSh.Paste( aGraphic, OUString() ); else if( OBJCNT_GRF == rSh.GetObjCntTypeOfSelection() ) rSh.ReRead(sURL, OUString(), &aGraphic); else { SwTransferable::SetSelInShell( rSh, false, pPt ); rSh.InsertGraphic(sURL, OUString(), aGraphic); } break; default: bRet = false; } } } else bRet = true; } if( bRet ) { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL( aSet.Get( RES_URL ) ); if( aURL.GetURL() != aINetImg.GetTargetURL() || aURL.GetTargetFrameName() != aINetImg.GetTargetFrame() ) { aURL.SetURL( aINetImg.GetTargetURL(), false ); aURL.SetTargetFrameName( aINetImg.GetTargetFrame() ); aSet.Put( aURL ); rSh.SetFlyFrameAttr( aSet ); } } return bRet; } void SwTransferable::SetSelInShell( SwWrtShell& rSh, bool bSelectFrame, const Point* pPt ) { if( bSelectFrame ) { // select frames/objects if( pPt && !rSh.GetView().GetViewFrame().GetDispatcher()->IsLocked() ) { rSh.GetView().NoRotate(); if( rSh.SelectObj( *pPt )) { rSh.HideCursor(); rSh.EnterSelFrameMode( pPt ); g_bFrameDrag = true; } } } else { if( rSh.IsFrameSelected() || rSh.IsObjSelected() ) { rSh.UnSelectFrame(); rSh.LeaveSelFrameMode(); rSh.GetView().GetEditWin().StopInsFrame(); g_bFrameDrag = false; } else if( rSh.GetView().GetDrawFuncPtr() ) rSh.GetView().GetEditWin().StopInsFrame(); rSh.EnterStdMode(); if( pPt ) rSh.SwCursorShell::SetCursor( *pPt, true ); } } bool SwTransferable::PasteDDE( const TransferableDataHelper& rData, SwWrtShell& rWrtShell, bool bReReadGrf, bool bMsg ) { // data from Clipboardformat OUString aApp, aTopic, aItem; { tools::SvRef xStrm; if( !rData.GetSotStorageStream( SotClipboardFormatId::LINK, xStrm )) { OSL_ENSURE( false, "DDE Data not found." ); return false; } // report useful error!! rtl_TextEncoding eEncoding = osl_getThreadTextEncoding(); aApp = read_zeroTerminated_uInt8s_ToOUString(*xStrm, eEncoding); aTopic = read_zeroTerminated_uInt8s_ToOUString(*xStrm, eEncoding); aItem = read_zeroTerminated_uInt8s_ToOUString(*xStrm, eEncoding); } OUString aCmd; sfx2::MakeLnkName( aCmd, &aApp, aTopic, aItem ); // do we want to read in a graphic now? SotClipboardFormatId nFormat; if( !rData.HasFormat( SotClipboardFormatId::RTF ) && !rData.HasFormat( SotClipboardFormatId::RICHTEXT ) && !rData.HasFormat( SotClipboardFormatId::HTML ) && !rData.HasFormat( SotClipboardFormatId::STRING ) && (rData.HasFormat( nFormat = SotClipboardFormatId::GDIMETAFILE ) || rData.HasFormat( nFormat = SotClipboardFormatId::BITMAP )) ) { Graphic aGrf; bool bRet = rData.GetGraphic( nFormat, aGrf ); if( bRet ) { OUString sLnkTyp("DDE"); if ( bReReadGrf ) rWrtShell.ReRead( aCmd, sLnkTyp, &aGrf ); else rWrtShell.InsertGraphic( aCmd, sLnkTyp, aGrf ); } return bRet; } SwFieldType* pTyp = nullptr; size_t i = 1; size_t j; OUString aName; bool bDoublePaste = false; const size_t nSize = rWrtShell.GetFieldTypeCount(); const ::utl::TransliterationWrapper& rColl = ::GetAppCmpStrIgnore(); do { aName = aApp + OUString::number( i ); for( j = INIT_FLDTYPES; j < nSize; j++ ) { pTyp = rWrtShell.GetFieldType( j ); if( SwFieldIds::Dde == pTyp->Which() ) { if( rColl.isEqual( static_cast(pTyp)->GetCmd(), aCmd ) && SfxLinkUpdateMode::ALWAYS == static_cast(pTyp)->GetType() ) { aName = pTyp->GetName(); bDoublePaste = true; break; } else if( rColl.isEqual( aName, pTyp->GetName() ) ) break; } } if( j == nSize ) break; ++i; } while( !bDoublePaste ); if( !bDoublePaste ) { SwDDEFieldType aType( aName, aCmd, SfxLinkUpdateMode::ALWAYS ); pTyp = rWrtShell.InsertFieldType( aType ); } SwDDEFieldType* pDDETyp = static_cast(pTyp); OUString aExpand; if( rData.GetString( SotClipboardFormatId::STRING, aExpand )) { do { // middle checked loop const sal_Int32 nNewlines{comphelper::string::getTokenCount(aExpand, '\n')}; // When data comes from a spreadsheet, we add a DDE-table if( !aExpand.isEmpty() && ( rData.HasFormat( SotClipboardFormatId::SYLK ) || rData.HasFormat( SotClipboardFormatId::SYLK_BIGCAPS ) ) ) { sal_Int32 nRows = nNewlines ? nNewlines-1 : 0; if (!aExpand.endsWith("\n")) ++nRows; // last row has no newline, e.g. one single cell const sal_Int32 nCols = comphelper::string::getTokenCount(o3tl::getToken(aExpand, 0, '\n'), '\t'); // don't try to insert tables that are too large for writer if (nRows > SAL_MAX_UINT16 || nCols > SAL_MAX_UINT16) { if( bMsg ) { std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, VclMessageType::Info, VclButtonsType::Ok, SwResId(STR_TABLE_TOO_LARGE))); xBox->run(); } pDDETyp = nullptr; break; } // at least one column & row must be there if( !nRows || !nCols ) { if( bMsg ) { std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, VclMessageType::Info, VclButtonsType::Ok, SwResId(STR_NO_TABLE))); xBox->run(); } pDDETyp = nullptr; break; } rWrtShell.InsertDDETable( SwInsertTableOptions( SwInsertTableFlags::SplitLayout, 1 ), // TODO MULTIHEADER pDDETyp, nRows, nCols ); } else if( nNewlines > 1 ) { // multiple paragraphs -> insert a protected section if( rWrtShell.HasSelection() ) rWrtShell.DelRight(); SwSectionData aSect( SectionType::DdeLink, aName ); aSect.SetLinkFileName( aCmd ); aSect.SetProtectFlag(true); rWrtShell.InsertSection( aSect ); pDDETyp = nullptr; // remove FieldTypes again } else { // insert SwDDEField aSwDDEField( pDDETyp ); rWrtShell.InsertField2( aSwDDEField ); } } while( false ); } else pDDETyp = nullptr; // remove FieldTypes again if( !pDDETyp && !bDoublePaste ) { // remove FieldType again - error occurred! for( j = nSize; j >= INIT_FLDTYPES; --j ) if( pTyp == rWrtShell.GetFieldType( j ) ) { rWrtShell.RemoveFieldType( j ); break; } } return true; } bool SwTransferable::PasteSdrFormat( const TransferableDataHelper& rData, SwWrtShell& rSh, SwPasteSdr nAction, const Point* pPt, SotExchangeActionFlags nActionFlags, bool bNeedToSelectBeforePaste) { bool bRet = false; tools::SvRef xStrm; if( rData.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStrm )) { xStrm->SetVersion( SOFFICE_FILEFORMAT_50 ); if(bNeedToSelectBeforePaste && pPt) { // if this is an internal drag, need to set the target right (select it), else // still the source will be selected SwTransferable::SetSelInShell( rSh, true, pPt ); } rSh.Paste( *xStrm, nAction, pPt ); bRet = true; if( bRet && ( nActionFlags & SotExchangeActionFlags::InsertTargetUrl )) SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::NONE, nullptr, false ); } return bRet; } bool SwTransferable::PasteGrf( const TransferableDataHelper& rData, SwWrtShell& rSh, SotClipboardFormatId nFormat, SwPasteSdr nAction, const Point* pPt, SotExchangeActionFlags nActionFlags, sal_Int8 nDropAction, bool bNeedToSelectBeforePaste, RndStdIds nAnchorType ) { bool bRet = false; Graphic aGraphic; INetBookmark aBkmk; bool bCheckForGrf = false, bCheckForImageMap = false; switch( nFormat ) { case SotClipboardFormatId::BITMAP: case SotClipboardFormatId::PNG: case SotClipboardFormatId::GDIMETAFILE: bRet = rData.GetGraphic( nFormat, aGraphic ); break; case SotClipboardFormatId::SVXB: { tools::SvRef xStm; if(rData.GetSotStorageStream(SotClipboardFormatId::SVXB, xStm)) { TypeSerializer aSerializer(*xStm); aSerializer.readGraphic(aGraphic); bRet = (GraphicType::NONE != aGraphic.GetType() && GraphicType::Default != aGraphic.GetType()); } break; } case SotClipboardFormatId::NETSCAPE_BOOKMARK: case SotClipboardFormatId::FILEGRPDESCRIPTOR: case SotClipboardFormatId::UNIFORMRESOURCELOCATOR: bRet = rData.GetINetBookmark( nFormat, aBkmk ); if( bRet ) { if( SwPasteSdr::SetAttr == nAction ) nFormat = SotClipboardFormatId::NETSCAPE_BOOKMARK; else bCheckForGrf = true; } break; case SotClipboardFormatId::SIMPLE_FILE: { OUString sText; bRet = rData.GetString( nFormat, sText ); if( bRet ) { OUString sDesc; SwTransferable::CheckForURLOrLNKFile( rData, sText, &sDesc ); sText = URIHelper::SmartRel2Abs(INetURLObject(), sText, Link(), false); #ifdef _WIN32 // Now that the path could be modified after SwTransferable::CheckForURLOrLNKFile, // where it could have been converted to URL, and made sure it's actually converted // to URL in URIHelper::SmartRel2Abs, we can finally convert file: URL back to // system path to make sure we don't use short path. // It looks not optimal, when we could apply GetLongPathNameW right to the original // pasted filename. But I don't know if (1) all arriving strings are system paths; // and (2) if SwTransferable::CheckForURLOrLNKFile could result in a different short // path, so taking a safe route. if (sText.startsWithIgnoreAsciiCase("file:")) { // tdf#124500: Convert short path to long path which should be used in links OUString sSysPath; osl::FileBase::getSystemPathFromFileURL(sText, sSysPath); std::unique_ptr aBuf(new sal_Unicode[32767]); DWORD nCopied = GetLongPathNameW(o3tl::toW(sSysPath.getStr()), o3tl::toW(aBuf.get()), 32767); if (nCopied && nCopied < 32767) sText = URIHelper::SmartRel2Abs(INetURLObject(), OUString(aBuf.get()), Link(), false); } #endif aBkmk = INetBookmark(sText, sDesc); bCheckForGrf = true; bCheckForImageMap = SwPasteSdr::Replace == nAction; } } break; default: bRet = rData.GetGraphic( nFormat, aGraphic ); break; } if( bCheckForGrf ) { //!!! check at FileSystem - only then it makes sense to test the graphics !!! GraphicFilter &rFlt = GraphicFilter::GetGraphicFilter(); bRet = ERRCODE_NONE == GraphicFilter::LoadGraphic(aBkmk.GetURL(), OUString(), aGraphic, &rFlt ); if( !bRet && SwPasteSdr::SetAttr == nAction && SotClipboardFormatId::SIMPLE_FILE == nFormat && // only at frame selection rSh.IsFrameSelected() ) { // then set as hyperlink after the graphic nFormat = SotClipboardFormatId::NETSCAPE_BOOKMARK; bRet = true; } } if(pPt && bNeedToSelectBeforePaste) { // when using internal D&Ds, still the source object is selected and // this is necessary to get the correct source data which is also // dependent from selection. After receiving the drag data it is // now time to select the correct target object SwTransferable::SetSelInShell( rSh, true, pPt ); } if( bRet ) { //Check and Perform rotation if needed lclCheckAndPerformRotation(aGraphic); OUString sURL; if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr // #i123922# if link action is noted, also take URL || DND_ACTION_LINK == nDropAction) { sURL = aBkmk.GetURL(); } switch( nAction ) { case SwPasteSdr::Insert: { SwTransferable::SetSelInShell( rSh, false, pPt ); rSh.InsertGraphic(sURL, OUString(), aGraphic, nullptr, nAnchorType); break; } case SwPasteSdr::Replace: { if( rSh.IsObjSelected() ) { // #i123922# for D&D on draw objects, do for now the same for // SwPasteSdr::Replace (D&D) as for SwPasteSdr::SetAttr (D&D and // CTRL+SHIFT). The code below replaces the draw object with // a writer graphic; maybe this is an option later again if wanted rSh.Paste( aGraphic, sURL ); // rSh.ReplaceSdrObj(sURL, OUString(), &aGraphic); // Point aPt( pPt ? *pPt : rSh.GetCursorDocPos() ); // SwTransferable::SetSelInShell( rSh, true, &aPt ); } else { // set graphic at writer graphic without link rSh.ReRead(sURL, OUString(), &aGraphic); } break; } case SwPasteSdr::SetAttr: { if( SotClipboardFormatId::NETSCAPE_BOOKMARK == nFormat ) { if( rSh.IsFrameSelected() ) { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL( aSet.Get( RES_URL ) ); aURL.SetURL( aBkmk.GetURL(), false ); aSet.Put( aURL ); rSh.SetFlyFrameAttr( aSet ); } } else if( rSh.IsObjSelected() ) { // set as attribute at DrawObject rSh.Paste( aGraphic, sURL ); } else if( OBJCNT_GRF == rSh.GetObjCntTypeOfSelection() ) { // set as linked graphic at writer graphic frame rSh.ReRead(sURL, OUString(), &aGraphic); } else { SwTransferable::SetSelInShell( rSh, false, pPt ); rSh.InsertGraphic(aBkmk.GetURL(), OUString(), aGraphic); } break; } default: { bRet = false; break; } } } if( bRet ) { if( nActionFlags & ( SotExchangeActionFlags::InsertImageMap | SotExchangeActionFlags::ReplaceImageMap ) ) SwTransferable::PasteImageMap( rData, rSh ); if( nActionFlags & SotExchangeActionFlags::InsertTargetUrl ) SwTransferable::PasteTargetURL( rData, rSh, SwPasteSdr::NONE, nullptr, false ); } else if( bCheckForImageMap ) { // or should the file be an ImageMap-File? ImageMap aMap; SfxMedium aMed( INetURLObject(aBkmk.GetURL()).GetFull(), StreamMode::STD_READ ); SvStream* pStream = aMed.GetInStream(); if( pStream != nullptr && !pStream->GetError() && // mba: no BaseURL for clipboard functionality aMap.Read( *pStream, IMapFormat::Detect ) == IMAP_ERR_OK && aMap.GetIMapObjectCount() ) { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL( aSet.Get( RES_URL ) ); aURL.SetMap( &aMap ); aSet.Put( aURL ); rSh.SetFlyFrameAttr( aSet ); bRet = true; } } return bRet; } bool SwTransferable::PasteImageMap( const TransferableDataHelper& rData, SwWrtShell& rSh ) { bool bRet = false; if( rData.HasFormat( SotClipboardFormatId::SVIM )) { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL( aSet.Get( RES_URL ) ); const ImageMap* pOld = aURL.GetMap(); // set or replace, that is the question ImageMap aImageMap; if( rData.GetImageMap( SotClipboardFormatId::SVIM, aImageMap ) && ( !pOld || aImageMap != *pOld )) { aURL.SetMap( &aImageMap ); aSet.Put( aURL ); rSh.SetFlyFrameAttr( aSet ); } bRet = true; } return bRet; } bool SwTransferable::PasteAsHyperlink( const TransferableDataHelper& rData, SwWrtShell& rSh, SotClipboardFormatId nFormat ) { bool bRet = false; OUString sFile; if( rData.GetString( nFormat, sFile ) && !sFile.isEmpty() ) { OUString sDesc; SwTransferable::CheckForURLOrLNKFile( rData, sFile, &sDesc ); // first, make the URL absolute INetURLObject aURL; aURL.SetSmartProtocol( INetProtocol::File ); aURL.SetSmartURL( sFile ); sFile = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); switch( rSh.GetObjCntTypeOfSelection() ) { case OBJCNT_FLY: case OBJCNT_GRF: case OBJCNT_OLE: { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL2( aSet.Get( RES_URL ) ); aURL2.SetURL( sFile, false ); if( aURL2.GetName().isEmpty() ) aURL2.SetName( sFile ); aSet.Put( aURL2 ); rSh.SetFlyFrameAttr( aSet ); } break; default: { rSh.InsertURL( SwFormatINetFormat( sFile, OUString() ), sDesc.isEmpty() ? sFile : sDesc); } } bRet = true; } return bRet; } bool SwTransferable::PasteFileName( TransferableDataHelper& rData, SwWrtShell& rSh, SotClipboardFormatId nFormat, SwPasteSdr nAction, const Point* pPt, SotExchangeActionFlags nActionFlags, bool * graphicInserted) { bool bRet = SwTransferable::PasteGrf( rData, rSh, nFormat, nAction, pPt, nActionFlags, 0, false); if (graphicInserted != nullptr) { *graphicInserted = bRet; } if( !bRet ) { OUString sFile, sDesc; if( rData.GetString( nFormat, sFile ) && !sFile.isEmpty() ) { #if HAVE_FEATURE_AVMEDIA INetURLObject aMediaURL; aMediaURL.SetSmartURL( sFile ); const OUString aMediaURLStr( aMediaURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); if( ::avmedia::MediaWindow::isMediaURL( aMediaURLStr, ""/*TODO?*/ ) ) { const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, aMediaURLStr ); rSh.GetView().GetViewFrame().GetDispatcher()->ExecuteList( SID_INSERT_AVMEDIA, SfxCallMode::SYNCHRON, { &aMediaURLItem }); } #else if (false) { } #endif else { bool bIsURLFile = SwTransferable::CheckForURLOrLNKFile( rData, sFile, &sDesc ); //Own FileFormat? --> insert, not for StarWriter/Web OUString sFileURL = URIHelper::SmartRel2Abs(INetURLObject(), sFile, Link(), false ); std::shared_ptr pFlt = SwPasteSdr::SetAttr == nAction ? nullptr : SwIoSystem::GetFileFilter(sFileURL); if( pFlt && dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) == nullptr ) { // and then pull up the insert-region-dialog SwSectionData aSect( SectionType::FileLink, rSh.GetDoc()->GetUniqueSectionName() ); aSect.SetLinkFileName( sFileURL ); aSect.SetProtectFlag( true ); rSh.StartInsertRegionDialog( aSect ); // starts dialog asynchronously bRet = true; } else if (SwPasteSdr::Insert == nAction && rData.HasFormat(SotClipboardFormatId::SIMPLE_FILE)) { // insert file as OLE PasteOLE(rData, rSh, nFormat, nActionFlags, nullptr == pPt); } else if( SwPasteSdr::SetAttr == nAction || ( bIsURLFile && SwPasteSdr::Insert == nAction )) { //we can insert foreign files as links after all // first, make the URL absolute INetURLObject aURL; aURL.SetSmartProtocol( INetProtocol::File ); aURL.SetSmartURL( sFile ); sFile = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); switch( rSh.GetObjCntTypeOfSelection() ) { case OBJCNT_FLY: case OBJCNT_GRF: case OBJCNT_OLE: { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL2( aSet.Get( RES_URL ) ); aURL2.SetURL( sFile, false ); if( aURL2.GetName().isEmpty() ) aURL2.SetName( sFile ); aSet.Put( aURL2 ); rSh.SetFlyFrameAttr( aSet ); } break; default: { rSh.InsertURL( SwFormatINetFormat( sFile, OUString() ), sDesc.isEmpty() ? sFile : sDesc ); } } bRet = true; } } } } return bRet; } bool SwTransferable::PasteDBData( const TransferableDataHelper& rData, SwWrtShell& rSh, SotClipboardFormatId nFormat, bool bLink, const Point* pDragPt, bool bMsg ) { bool bRet = false; OUString sText; if( rData.GetString( nFormat, sText ) && !sText.isEmpty() ) { sal_uInt16 nWh = SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == nFormat ? 0 : SotClipboardFormatId::SBA_DATAEXCHANGE == nFormat ? (bLink ? FN_QRY_MERGE_FIELD : FN_QRY_INSERT) : (bLink ? 0 : FN_QRY_INSERT_FIELD ); const DataFlavorExVector& rVector = rData.GetDataFlavorExVector(); bool bHaveColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(rVector, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE); if ( SotClipboardFormatId::XFORMS == nFormat ) { rSh.MakeDrawView(); FmFormView* pFmView = dynamic_cast( rSh.GetDrawView() ); if (pFmView && pDragPt) { OXFormsDescriptor aDesc = OXFormsTransferable::extractDescriptor(rData); rtl::Reference pObj = pFmView->CreateXFormsControl(aDesc); if(pObj) { rSh.SwFEShell::InsertDrawObj( *pObj, *pDragPt ); } } } else if( nWh ) { std::unique_ptr pConnectionItem; std::unique_ptr pCursorItem; std::unique_ptr pColumnItem; std::unique_ptr pSourceItem; std::unique_ptr pCommandItem; std::unique_ptr pCommandTypeItem; std::unique_ptr pColumnNameItem; std::unique_ptr pSelectionItem; bool bDataAvailable = true; ODataAccessDescriptor aDesc; if(bHaveColumnDescriptor) aDesc = OColumnTransferable::extractColumnDescriptor(rData); else if(ODataAccessObjectTransferable::canExtractObjectDescriptor(rVector) ) aDesc = ODataAccessObjectTransferable::extractObjectDescriptor(rData); else bDataAvailable = false; if ( bDataAvailable ) { pConnectionItem.reset(new SfxUnoAnyItem(FN_DB_CONNECTION_ANY, aDesc[DataAccessDescriptorProperty::Connection])); pColumnItem.reset(new SfxUnoAnyItem(FN_DB_COLUMN_ANY, aDesc[DataAccessDescriptorProperty::ColumnObject])); pSourceItem.reset(new SfxUnoAnyItem(FN_DB_DATA_SOURCE_ANY, Any(aDesc.getDataSource()))); pCommandItem.reset(new SfxUnoAnyItem(FN_DB_DATA_COMMAND_ANY, aDesc[DataAccessDescriptorProperty::Command])); pCommandTypeItem.reset(new SfxUnoAnyItem(FN_DB_DATA_COMMAND_TYPE_ANY, aDesc[DataAccessDescriptorProperty::CommandType])); pColumnNameItem.reset(new SfxUnoAnyItem(FN_DB_DATA_COLUMN_NAME_ANY, aDesc[DataAccessDescriptorProperty::ColumnName])); pSelectionItem.reset(new SfxUnoAnyItem(FN_DB_DATA_SELECTION_ANY, aDesc[DataAccessDescriptorProperty::Selection])); pCursorItem.reset(new SfxUnoAnyItem(FN_DB_DATA_CURSOR_ANY, aDesc[DataAccessDescriptorProperty::Cursor])); } SwView& rView = rSh.GetView(); //force ::SelectShell rView.StopShellTimer(); SfxStringItem aDataDesc( nWh, sText ); rView.GetViewFrame().GetDispatcher()->ExecuteList( nWh, SfxCallMode::ASYNCHRON, { &aDataDesc, pConnectionItem.get(), pColumnItem.get(), pSourceItem.get(), pCommandItem.get(), pCommandTypeItem.get(), pColumnNameItem.get(), pSelectionItem.get(), pCursorItem.get() }); } else { rSh.MakeDrawView(); FmFormView* pFmView = dynamic_cast( rSh.GetDrawView() ); if (pFmView && bHaveColumnDescriptor && pDragPt) { rtl::Reference pObj = pFmView->CreateFieldControl( OColumnTransferable::extractColumnDescriptor(rData) ); if (pObj) rSh.SwFEShell::InsertDrawObj( *pObj, *pDragPt ); } } bRet = true; } else if( bMsg ) { std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, VclMessageType::Info, VclButtonsType::Ok, SwResId(STR_CLPBRD_FORMAT_ERROR))); xBox->run(); } return bRet; } bool SwTransferable::PasteFileList( const TransferableDataHelper& rData, SwWrtShell& rSh, bool bLink, const Point* pPt, bool bMsg ) { bool bRet = false; FileList aFileList; if( rData.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList ) && aFileList.Count() ) { SwPasteSdr nAct = bLink ? SwPasteSdr::SetAttr : SwPasteSdr::Insert; OUString sFlyNm; // iterate over the filelist for( sal_uLong n = 0, nEnd = aFileList.Count(); n < nEnd; ++n ) { rtl::Reference pHlp = new TransferDataContainer; pHlp->CopyString( SotClipboardFormatId::SIMPLE_FILE, aFileList.GetFile( n )); TransferableDataHelper aData( pHlp ); if( SwTransferable::PasteFileName( aData, rSh, SotClipboardFormatId::SIMPLE_FILE, nAct, pPt, SotExchangeActionFlags::NONE, nullptr )) { if( bLink ) { sFlyNm = rSh.GetFlyName(); SwTransferable::SetSelInShell( rSh, false, pPt ); } bRet = true; } } if( !sFlyNm.isEmpty() ) rSh.GotoFly( sFlyNm ); } else if( bMsg ) { std::unique_ptr xBox(Application::CreateMessageDialog(nullptr, VclMessageType::Info, VclButtonsType::Ok, SwResId(STR_CLPBRD_FORMAT_ERROR))); xBox->run(); } return bRet; } bool SwTransferable::CheckForURLOrLNKFile( const TransferableDataHelper& rData, OUString& rFileName, OUString* pTitle ) { bool bIsURLFile = false; INetBookmark aBkmk; if( rData.GetINetBookmark( SotClipboardFormatId::SOLK, aBkmk ) ) { rFileName = aBkmk.GetURL(); if( pTitle ) *pTitle = aBkmk.GetDescription(); bIsURLFile = true; } else { if( rFileName.getLength()>4 && rFileName.endsWithIgnoreAsciiCase(".url") ) { OSL_ENSURE( false, "how do we read today .URL - Files?" ); } } return bIsURLFile; } bool SwTransferable::IsPasteSpecial( const SwWrtShell& rWrtShell, const TransferableDataHelper& rData ) { // we can paste-special if there's an entry in the paste-special-format list SvxClipboardFormatItem aClipboardFormatItem(TypedWhichId(0)); FillClipFormatItem( rWrtShell, rData, aClipboardFormatItem); return aClipboardFormatItem.Count() > 0; } bool SwTransferable::IsPasteOwnFormat( const TransferableDataHelper& rData ) { return ( GetSwTransferable( rData ) != nullptr ); } bool SwTransferable::PasteFormat( SwWrtShell& rSh, TransferableDataHelper& rData, SotClipboardFormatId nFormat ) { SwWait aWait( *rSh.GetView().GetDocShell(), false ); bool bRet = false; SotClipboardFormatId nPrivateFormat = SotClipboardFormatId::PRIVATE; SwTransferable *pClipboard = GetSwTransferable( rData ); if( pClipboard && ((TransferBufferType::Document|TransferBufferType::Graphic|TransferBufferType::Ole) & pClipboard->m_eBufferType )) nPrivateFormat = SotClipboardFormatId::EMBED_SOURCE; if( pClipboard && nPrivateFormat == nFormat ) bRet = pClipboard->PrivatePaste( rSh ); else if( rData.HasFormat( nFormat ) ) { uno::Reference xTransferable( rData.GetXTransferable() ); sal_uInt8 nEventAction; SotExchangeDest nDestination = SwTransferable::GetSotDestination( rSh ); sal_uInt16 nSourceOptions = (( SotExchangeDest::DOC_TEXTFRAME == nDestination || SotExchangeDest::SWDOC_FREE_AREA == nDestination || SotExchangeDest::DOC_TEXTFRAME_WEB == nDestination || SotExchangeDest::SWDOC_FREE_AREA_WEB == nDestination ) ? EXCHG_IN_ACTION_COPY : EXCHG_IN_ACTION_MOVE); SotExchangeActionFlags nActionFlags; sal_uInt8 nAction = SotExchange::GetExchangeAction( rData.GetDataFlavorExVector(), nDestination, nSourceOptions, /* ?? */ EXCHG_IN_ACTION_DEFAULT, /* ?? */ nFormat, nEventAction, nFormat, lcl_getTransferPointer ( xTransferable ), &nActionFlags ); if( EXCHG_INOUT_ACTION_NONE != nAction ) bRet = SwTransferable::PasteData( rData, rSh, nAction, nActionFlags, nFormat, nDestination, true, false ); } return bRet; } bool SwTransferable::TestAllowedFormat( const TransferableDataHelper& rData, SotClipboardFormatId nFormat, SotExchangeDest nDestination ) { sal_uInt8 nAction = EXCHG_INOUT_ACTION_NONE; if( rData.HasFormat( nFormat )) { uno::Reference xTransferable( rData.GetXTransferable() ); sal_uInt8 nEventAction; nAction = SotExchange::GetExchangeAction( rData.GetDataFlavorExVector(), nDestination, EXCHG_IN_ACTION_COPY, EXCHG_IN_ACTION_COPY, nFormat, nEventAction, nFormat, lcl_getTransferPointer ( xTransferable ) ); } return EXCHG_INOUT_ACTION_NONE != nAction; } /** * the list of formats which will be offered to the user in the 'Paste * Special...' dialog and the paste button menu */ static SotClipboardFormatId aPasteSpecialIds[] = { SotClipboardFormatId::HTML, SotClipboardFormatId::HTML_SIMPLE, SotClipboardFormatId::HTML_NO_COMMENT, SotClipboardFormatId::RTF, SotClipboardFormatId::RICHTEXT, SotClipboardFormatId::STRING, SotClipboardFormatId::SONLK, SotClipboardFormatId::NETSCAPE_BOOKMARK, SotClipboardFormatId::DRAWING, SotClipboardFormatId::SVXB, SotClipboardFormatId::GDIMETAFILE, SotClipboardFormatId::BITMAP, SotClipboardFormatId::SVIM, SotClipboardFormatId::FILEGRPDESCRIPTOR, SotClipboardFormatId::NONE }; bool SwTransferable::PasteUnformatted( SwWrtShell& rSh, TransferableDataHelper& rData ) { // Plain text == unformatted return SwTransferable::PasteFormat( rSh, rData, SotClipboardFormatId::STRING ); } void SwTransferable::PrePasteSpecial( const SwWrtShell& rSh, TransferableDataHelper& rData, const VclPtr& pDlg ) { DataFlavorExVector aFormats( rData.GetDataFlavorExVector() ); TransferableObjectDescriptor aDesc; SotExchangeDest nDest = SwTransferable::GetSotDestination( rSh ); SwTransferable *pClipboard = GetSwTransferable( rData ); if( pClipboard ) { aDesc = pClipboard->m_aObjDesc; TranslateId pResId; if( pClipboard->m_eBufferType & TransferBufferType::Document ) pResId = STR_PRIVATETEXT; else if( pClipboard->m_eBufferType & TransferBufferType::Graphic ) pResId = STR_PRIVATEGRAPHIC; else if( pClipboard->m_eBufferType == TransferBufferType::Ole ) pResId = STR_PRIVATEOLE; if (pResId) { if (STR_PRIVATEOLE == pResId || STR_PRIVATEGRAPHIC == pResId) { // add SotClipboardFormatId::EMBED_SOURCE to the formats. This // format display then the private format name. DataFlavorEx aFlavorEx; aFlavorEx.mnSotId = SotClipboardFormatId::EMBED_SOURCE; aFormats.insert( aFormats.begin(), aFlavorEx ); } pDlg->SetObjName( pClipboard->m_aObjDesc.maClassName, SwResId(pResId) ); pDlg->Insert( SotClipboardFormatId::EMBED_SOURCE, OUString() ); } } else { if( rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) ) { (void)rData.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc ); } if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::EMBED_SOURCE, nDest )) pDlg->Insert( SotClipboardFormatId::EMBED_SOURCE, OUString() ); if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK_SOURCE, nDest )) pDlg->Insert( SotClipboardFormatId::LINK_SOURCE, OUString() ); } if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK, nDest )) pDlg->Insert( SotClipboardFormatId::LINK, SwResId(STR_DDEFORMAT) ); for( SotClipboardFormatId* pIds = aPasteSpecialIds; *pIds != SotClipboardFormatId::NONE; ++pIds ) if( SwTransferable::TestAllowedFormat( rData, *pIds, nDest )) pDlg->Insert( *pIds, OUString() ); } void SwTransferable::FillClipFormatItem( const SwWrtShell& rSh, const TransferableDataHelper& rData, SvxClipboardFormatItem & rToFill ) { SotExchangeDest nDest = SwTransferable::GetSotDestination( rSh ); SwTransferable *pClipboard = GetSwTransferable( rData ); if( pClipboard ) { TranslateId pResId; if( pClipboard->m_eBufferType & TransferBufferType::Document ) pResId = STR_PRIVATETEXT; else if( pClipboard->m_eBufferType & TransferBufferType::Graphic ) pResId = STR_PRIVATEGRAPHIC; else if( pClipboard->m_eBufferType == TransferBufferType::Ole ) pResId = STR_PRIVATEOLE; if (pResId) rToFill.AddClipbrdFormat(SotClipboardFormatId::EMBED_SOURCE, SwResId(pResId)); } else { TransferableObjectDescriptor aDesc; if (rData.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) { (void)rData.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aDesc); } if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::EMBED_SOURCE, nDest )) rToFill.AddClipbrdFormat( SotClipboardFormatId::EMBED_SOURCE, aDesc.maTypeName ); if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK_SOURCE, nDest )) rToFill.AddClipbrdFormat( SotClipboardFormatId::LINK_SOURCE ); SotClipboardFormatId nFormat; if ( rData.HasFormat(nFormat = SotClipboardFormatId::EMBED_SOURCE_OLE) || rData.HasFormat(nFormat = SotClipboardFormatId::EMBEDDED_OBJ_OLE) ) { OUString sName,sSource; if ( SvPasteObjectHelper::GetEmbeddedName(rData,sName,sSource,nFormat) ) rToFill.AddClipbrdFormat( nFormat, sName ); } } if( SwTransferable::TestAllowedFormat( rData, SotClipboardFormatId::LINK, nDest )) rToFill.AddClipbrdFormat( SotClipboardFormatId::LINK, SwResId(STR_DDEFORMAT) ); for( SotClipboardFormatId* pIds = aPasteSpecialIds; *pIds != SotClipboardFormatId::NONE; ++pIds ) if( SwTransferable::TestAllowedFormat( rData, *pIds, nDest )) rToFill.AddClipbrdFormat(*pIds, OUString()); } void SwTransferable::SetDataForDragAndDrop( const Point& rSttPos ) { if(!m_pWrtShell) return; OUString sGrfNm; const SelectionType nSelection = m_pWrtShell->GetSelectionType(); if( SelectionType::Graphic == nSelection) { AddFormat( SotClipboardFormatId::SVXB ); const Graphic* pGrf = m_pWrtShell->GetGraphic(); if ( pGrf && pGrf->IsSupportedGraphic() ) { AddFormat( SotClipboardFormatId::GDIMETAFILE ); AddFormat( SotClipboardFormatId::PNG ); AddFormat( SotClipboardFormatId::BITMAP ); } m_eBufferType = TransferBufferType::Graphic; m_pWrtShell->GetGrfNms( &sGrfNm, nullptr ); } else if( SelectionType::Ole == nSelection ) { AddFormat( SotClipboardFormatId::EMBED_SOURCE ); PrepareOLE( m_aObjDesc ); AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); AddFormat( SotClipboardFormatId::GDIMETAFILE ); m_eBufferType = TransferBufferType::Ole; } //Is there anything to provide anyway? else if ( m_pWrtShell->IsSelection() || m_pWrtShell->IsFrameSelected() || m_pWrtShell->IsObjSelected() ) { if( m_pWrtShell->IsObjSelected() ) m_eBufferType = TransferBufferType::Drawing; else { m_eBufferType = TransferBufferType::Document; if( SwWrtShell::NO_WORD != m_pWrtShell->IntelligentCut( nSelection, false )) m_eBufferType = TransferBufferType::DocumentWord | m_eBufferType; } if( nSelection & SelectionType::TableCell ) m_eBufferType = TransferBufferType::Table | m_eBufferType; AddFormat( SotClipboardFormatId::EMBED_SOURCE ); //put RTF ahead of the OLE's Metafile for less loss if( !m_pWrtShell->IsObjSelected() ) { AddFormat( SotClipboardFormatId::RTF ); AddFormat( SotClipboardFormatId::RICHTEXT ); AddFormat( SotClipboardFormatId::HTML ); } if( m_pWrtShell->IsSelection() ) AddFormat( SotClipboardFormatId::STRING ); if( nSelection & ( SelectionType::DrawObject | SelectionType::DbForm )) { AddFormat( SotClipboardFormatId::DRAWING ); if ( nSelection & SelectionType::DrawObject ) { AddFormat( SotClipboardFormatId::GDIMETAFILE ); AddFormat( SotClipboardFormatId::PNG ); AddFormat( SotClipboardFormatId::BITMAP ); } m_eBufferType = TransferBufferType::Graphic | m_eBufferType; // is it a URL-Button ? OUString sURL; OUString sDesc; if( m_pWrtShell->GetURLFromButton( sURL, sDesc ) ) { AddFormat( SotClipboardFormatId::STRING ); AddFormat( SotClipboardFormatId::SOLK ); AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); AddFormat( SotClipboardFormatId::FILECONTENT ); AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); m_eBufferType = TransferBufferType::InetField | m_eBufferType; } } //ObjectDescriptor was already filled from the old DocShell. //Now adjust it. Thus in GetData the first query can still //be answered with delayed rendering. m_aObjDesc.maDragStartPos = rSttPos; m_aObjDesc.maSize = constOleSize100mm; PrepareOLE( m_aObjDesc ); AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); } else if( nSelection & SelectionType::Text && !m_pWrtShell->HasMark() ) { // is only one field - selected? SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr ); Point aPos( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY()); if( m_pWrtShell->GetContentAtPos( aPos, aContentAtPos ) ) { AddFormat( SotClipboardFormatId::STRING ); AddFormat( SotClipboardFormatId::SOLK ); AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); AddFormat( SotClipboardFormatId::FILECONTENT ); AddFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ); AddFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ); m_eBufferType = TransferBufferType::InetField; } } if( !m_pWrtShell->IsFrameSelected() ) return; SfxItemSetFixed aSet( m_pWrtShell->GetAttrPool() ); m_pWrtShell->GetFlyFrameAttr( aSet ); const SwFormatURL& rURL = aSet.Get( RES_URL ); if( rURL.GetMap() ) { m_pImageMap.reset( new ImageMap( *rURL.GetMap() ) ); AddFormat( SotClipboardFormatId::SVIM ); } else if( !rURL.GetURL().isEmpty() ) { m_pTargetURL.reset(new INetImage( sGrfNm, rURL.GetURL(), rURL.GetTargetFrameName() )); AddFormat( SotClipboardFormatId::INET_IMAGE ); } } void SwTransferable::StartDrag( vcl::Window* pWin, const Point& rPos ) { if(!m_pWrtShell) return; m_bOldIdle = m_pWrtShell->GetViewOptions()->IsIdle(); m_bCleanUp = true; m_pWrtShell->GetViewOptions()->SetIdle( false ); if( m_pWrtShell->IsSelFrameMode() ) m_pWrtShell->ShowCursor(); SW_MOD()->m_pDragDrop = this; SetDataForDragAndDrop( rPos ); sal_Int8 nDragOptions = DND_ACTION_COPYMOVE | DND_ACTION_LINK; SwDocShell* pDShell = m_pWrtShell->GetView().GetDocShell(); if( ( pDShell && pDShell->IsReadOnly() ) || m_pWrtShell->HasReadonlySel() ) nDragOptions &= ~DND_ACTION_MOVE; TransferableHelper::StartDrag( pWin, nDragOptions ); } void SwTransferable::DragFinished( sal_Int8 nAction ) { //And the last finishing work so that all statuses are right if( DND_ACTION_MOVE == nAction ) { if( m_bCleanUp ) { //It was dropped outside of Writer. We still have to //delete. m_pWrtShell->StartAllAction(); m_pWrtShell->StartUndo( SwUndoId::UI_DRAG_AND_MOVE ); if ( m_pWrtShell->IsTableMode() ) m_pWrtShell->DeleteTableSel(); else { if ( !(m_pWrtShell->IsSelFrameMode() || m_pWrtShell->IsObjSelected()) ) //SmartCut, take one of the blanks along m_pWrtShell->IntelligentCut( m_pWrtShell->GetSelectionType() ); m_pWrtShell->DelRight(); } m_pWrtShell->EndUndo( SwUndoId::UI_DRAG_AND_MOVE ); m_pWrtShell->EndAllAction(); } else { const SelectionType nSelection = m_pWrtShell->GetSelectionType(); if( ( SelectionType::Frame | SelectionType::Graphic | SelectionType::Ole | SelectionType::DrawObject ) & nSelection ) { m_pWrtShell->EnterSelFrameMode(); } } } m_pWrtShell->GetView().GetEditWin().DragFinished(); if( m_pWrtShell->IsSelFrameMode() ) m_pWrtShell->HideCursor(); else m_pWrtShell->ShowCursor(); m_pWrtShell->GetViewOptions()->SetIdle( m_bOldIdle ); } namespace { bool lcl_checkClassification(SwDoc* pSourceDoc, SwDoc* pDestinationDoc) { if (!pSourceDoc || !pDestinationDoc) return true; SwDocShell* pSourceShell = pSourceDoc->GetDocShell(); SwDocShell* pDestinationShell = pDestinationDoc->GetDocShell(); if (!pSourceShell || !pDestinationShell) return true; SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSourceShell->getDocProperties(), pDestinationShell->getDocProperties()); return SfxClassificationHelper::ShowPasteInfo(eResult); } } bool SwTransferable::PrivatePaste(SwWrtShell& rShell, SwPasteContext* pContext, PasteTableType ePasteTable) { // first, ask for the SelectionType, then action-bracketing !!!! // (otherwise it's not pasted into a TableSelection!!!) OSL_ENSURE( !rShell.ActionPend(), "Paste must never have an ActionPend" ); if ( !m_pClpDocFac ) return false; // the return value of the SwFEShell::Paste also is bool! const SelectionType nSelection = rShell.GetSelectionType(); SwTrnsfrActionAndUndo aAction( &rShell ); bool bKillPaMs = false; //Delete selected content, not at table-selection and table in Clipboard, and don't delete hovering graphics. if( rShell.HasSelection() && !( nSelection & SelectionType::TableCell) && !( nSelection & SelectionType::DrawObject)) { if (!(nSelection & SelectionType::NumberList)) { bKillPaMs = true; rShell.SetRetainSelection( true ); } if (pContext) pContext->forget(); rShell.DelRight(); if (pContext) pContext->remember(); // when a Fly was selected, a valid cursor position has to be found now // (parked Cursor!) if( ( SelectionType::Frame | SelectionType::Graphic | SelectionType::Ole | SelectionType::DrawObject | SelectionType::DbForm ) & nSelection ) { // position the cursor again Point aPt( rShell.GetCharRect().Pos() ); rShell.SwCursorShell::SetCursor( aPt, true ); } if (!(nSelection & SelectionType::NumberList)) { rShell.SetRetainSelection( false ); } } if ( nSelection & SelectionType::DrawObject) //unselect hovering graphics { rShell.ResetSelect(nullptr,false); } bool bInWrd = false, bEndWrd = false, bSttWrd = false, bSmart(TransferBufferType::DocumentWord & m_eBufferType); if( bSmart ) { // Why not for other Scripts? If TransferBufferType::DocumentWord is set, we have a word // in the buffer, word in this context means 'something with spaces at beginning // and end'. In this case we definitely want these spaces to be inserted here. bInWrd = rShell.IsInWord(); bEndWrd = rShell.IsEndWrd(); bSmart = bInWrd || bEndWrd; if( bSmart ) { bSttWrd = rShell.IsStartWord(); if (!bSttWrd && (bInWrd || bEndWrd)) rShell.SwEditShell::Insert(' '); } } bool bRet = true; // m_pWrtShell is nullptr when the source document is closed already. if (!m_pWrtShell || lcl_checkClassification(m_pWrtShell->GetDoc(), rShell.GetDoc())) bRet = rShell.Paste(m_pClpDocFac->GetDoc(), ePasteTable == PasteTableType::PASTE_TABLE); if( bKillPaMs ) rShell.KillPams(); // If Smart Paste then insert blank if( bRet && bSmart && ((bInWrd && !bEndWrd )|| bSttWrd) ) rShell.SwEditShell::Insert(' '); return bRet; } bool SwTransferable::PrivateDrop( SwWrtShell& rSh, const Point& rDragPt, bool bMove, bool bIsXSelection ) { int cWord = 0; bool bInWrd = false; bool bEndWrd = false; bool bSttWrd = false; bool bSttPara = false; bool bTableSel = false; bool bTableMove = false; bool bFrameSel = false; SwWrtShell& rSrcSh = *GetShell(); rSh.UnSetVisibleCursor(); if( TransferBufferType::InetField == m_eBufferType ) { if( rSh.GetFormatFromObj( rDragPt ) ) { INetBookmark aTmp; if( (TransferBufferType::InetField & m_eBufferType) && m_oBookmark ) aTmp = *m_oBookmark; // select target graphic if( rSh.SelectObj( rDragPt ) ) { rSh.HideCursor(); rSh.EnterSelFrameMode( &rDragPt ); g_bFrameDrag = true; } const SelectionType nSelection = rSh.GetSelectionType(); // not yet consider Draw objects if( SelectionType::Graphic & nSelection ) { SfxItemSetFixed aSet( rSh.GetAttrPool() ); rSh.GetFlyFrameAttr( aSet ); SwFormatURL aURL( aSet.Get( RES_URL ) ); aURL.SetURL( aTmp.GetURL(), false ); aSet.Put( aURL ); rSh.SetFlyFrameAttr( aSet ); return true; } if( SelectionType::DrawObject & nSelection ) { rSh.LeaveSelFrameMode(); rSh.UnSelectFrame(); rSh.ShowCursor(); g_bFrameDrag = false; } } } if( &rSh != &rSrcSh && (SelectionType::Graphic & rSh.GetSelectionType()) && TransferBufferType::Graphic == m_eBufferType ) { // ReRead the graphic OUString sGrfNm; OUString sFltNm; rSrcSh.GetGrfNms( &sGrfNm, &sFltNm ); rSh.ReRead( sGrfNm, sFltNm, rSrcSh.GetGraphic() ); return true; } //not in selections or selected frames if( rSh.TestCurrPam( rDragPt ) || ( rSh.IsSelFrameMode() && rSh.IsInsideSelectedObj( rDragPt )) ) return false; if( rSrcSh.IsTableMode() ) { bTableSel = true; const SelectionType nSelection = rSrcSh.GetSelectionType(); // at enhanced table row/column selection or wholly selected tables, // paste rows above or columns before, and in the case of moving, remove the selection // (limit only to the single document case temporarily) if( rSrcSh.GetDoc() == rSh.GetDoc() && ( (( SelectionType::TableRow | SelectionType::TableCol) & nSelection ) || rSrcSh.HasWholeTabSelection() ) ) { bool bTableCol(SelectionType::TableCol & nSelection); ::sw::mark::IMark* pMarkMoveFrom = bMove ? rSh.SetBookmark( vcl::KeyCode(), OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK ) : nullptr; // row count and direction of the table selection: // up to down, if the cursor is there in its last table row const SwSelBoxes& rBoxes = rSrcSh.GetTableCursor()->GetSelectedBoxes(); const SwTableNode* pTableNd = rSh.IsCursorInTable(); if (!pTableNd) { SAL_WARN("sw", "presumably this case can't arise in practice"); return false; } const SwTableLines& rLines = pTableNd->GetTable().GetTabLines(); const SwStartNode& rDelPos = rBoxes.back() ? *rBoxes.front()->GetSttNd() : *pTableNd->GetStartNode(); // count selected rows or columns sal_Int32 nSelRowOrCols = 0; if ( rBoxes.back() ) { if ( bTableCol ) { // selected column count is the count of the cells // in the first row of the selection auto nLine = rLines.GetPos( rBoxes.front()->GetUpper() ); for (auto pBox : rBoxes) { // cell is in the next row if ( nLine != rLines.GetPos( pBox->GetUpper() ) ) break; ++nSelRowOrCols; } } else { // selected row count is the difference of the row number of the // first and the last cell of the selection nSelRowOrCols = rLines.GetPos( rBoxes.back()->GetUpper() ) - rLines.GetPos( rBoxes.front()->GetUpper() ) + 1; } } bool bSelUpToDown = rBoxes.back() && rBoxes.back()->GetUpper() == rSh.GetCursor()->GetPointNode().GetTableBox()->GetUpper(); SwUndoId eUndoId = bMove ? SwUndoId::UI_DRAG_AND_MOVE : SwUndoId::UI_DRAG_AND_COPY; SwRewriter aRewriter; aRewriter.AddRule(UndoArg1, rSrcSh.GetSelDescr()); if(rSrcSh.GetDoc() != rSh.GetDoc()) rSrcSh.StartUndo( eUndoId, &aRewriter ); rSh.StartUndo( eUndoId, &aRewriter ); rSh.StartAction(); rSrcSh.StartAction(); SfxDispatcher* pDispatch = rSrcSh.GetView().GetViewFrame().GetDispatcher(); pDispatch->Execute(SID_COPY, SfxCallMode::SYNCHRON); rSrcSh.Push(); // save selection for later restoration rSh.EnterStdMode(); rSh.SwCursorShell::SetCursor(rDragPt, false); bool bPasteIntoTable = rSh.GetCursor()->GetPointNode().GetTableBox() != nullptr; // store cursor ::sw::mark::IMark* pMark = rSh.SetBookmark( vcl::KeyCode(), OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK ); // paste rows above/columns before pDispatch->Execute(bTableCol ? FN_TABLE_PASTE_COL_BEFORE : FN_TABLE_PASTE_ROW_BEFORE, SfxCallMode::SYNCHRON); // go to the previously inserted table rows and set them to tracked insertion, if needed bool bNeedTrack = !bTableCol && rSh.getIDocumentRedlineAccess().IsRedlineOn(); // restore cursor position if (bNeedTrack && pMark != nullptr) rSh.GotoMark( pMark ); if ( !bNeedTrack && !bPasteIntoTable ) { rSrcSh.Pop(SwCursorShell::PopMode::DeleteCurrent); // restore selection... // delete source rows/columns if (bMove) pDispatch->Execute(bTableCol ? FN_TABLE_DELETE_COL : FN_TABLE_DELETE_ROW, SfxCallMode::SYNCHRON); } else { const SwTableBox* pBoxStt = rSh.GetCursor()->GetPointNode().GetTableBox(); SwTableLine* pLine = pBoxStt ? const_cast( pBoxStt->GetUpper()): nullptr; for (sal_Int32 nDeleted = 0; bNeedTrack && nDeleted < nSelRowOrCols;) { // move up text cursor (note: "true" is important for the layout level) if ( !rSh.Up(false) ) break; const SwTableBox* pBox = rSh.GetCursor()->GetPointNode().GetTableBox(); if ( !pBox ) break; // Up() reaches a new row if ( pBox->GetUpper() != pLine ) { //rSh.SelTableRow(); SvxPrintItem aTracked(RES_PRINT, false); rSh.GetDoc()->SetRowNotTracked( *rSh.GetCursor(), aTracked ); ++nDeleted; pLine = const_cast(pBox->GetUpper()); } } rSrcSh.Pop(SwCursorShell::PopMode::DeleteCurrent); // restore selection... // delete source rows/columns if (bMove) { // restore cursor position if (pMarkMoveFrom != nullptr) { rSh.GotoMark( pMarkMoveFrom ); rSh.getIDocumentMarkAccess()->deleteMark( pMarkMoveFrom ); } // tracked table row moving: set original rows as tracked deletion, // otherwise delete original rows/columns (tracking column deletion // and insertion is not supported yet) if ( !bTableCol && bNeedTrack ) { pLine = nullptr; for (sal_Int32 nDeleted = 0; nDeleted < nSelRowOrCols;) { const SwTableBox* pBox = rSh.GetCursor()->GetPointNode().GetTableBox(); if ( !pBox ) break; if ( pBox->GetUpper() != pLine ) { pLine = const_cast(pBox->GetUpper()); pDispatch->Execute(FN_TABLE_DELETE_ROW, SfxCallMode::SYNCHRON); ++nDeleted; } bool bMoved = false; if (bSelUpToDown) bMoved = rSh.Up(false); else bMoved = rSh.Down(false); if (!bMoved) break; } } else { // set cursor in the first cell of the original selection rSh.GetCursor()->DeleteMark(); rSh.GetCursor()->GetPoint()->Assign( rDelPos.GetIndex() + 1); for (sal_Int32 nDeleted = 0; nDeleted < nSelRowOrCols; ++nDeleted) { pDispatch->Execute(bTableCol ? FN_TABLE_DELETE_COL : FN_TABLE_DELETE_ROW, SfxCallMode::SYNCHRON); } } } } // restore cursor position if (pMark != nullptr) { rSh.GotoMark( pMark ); rSh.getIDocumentMarkAccess()->deleteMark( pMark ); } rSh.DestroyCursor(); rSh.EndUndo(); rSh.EndAction(); rSh.EndAction(); return true; } if ( bMove && rSrcSh.HasWholeTabSelection() ) bTableMove = true; } else if( rSrcSh.IsSelFrameMode() || rSrcSh.IsObjSelected() ) { // don't move position-protected objects! if( bMove && rSrcSh.IsSelObjProtected( FlyProtectFlags::Pos ) != FlyProtectFlags::NONE ) return false; bFrameSel = true; } const SelectionType nSel = rSrcSh.GetSelectionType(); SwUndoId eUndoId = bMove ? SwUndoId::UI_DRAG_AND_MOVE : SwUndoId::UI_DRAG_AND_COPY; SwRewriter aRewriter; aRewriter.AddRule(UndoArg1, rSrcSh.GetSelDescr()); if(rSrcSh.GetDoc() != rSh.GetDoc()) rSrcSh.StartUndo( eUndoId, &aRewriter ); rSh.StartUndo( eUndoId, &aRewriter ); rSh.StartAction(); rSrcSh.StartAction(); if( &rSrcSh != &rSh ) { rSh.EnterStdMode(); rSh.SwCursorShell::SetCursor( rDragPt, true ); cWord = rSrcSh.IntelligentCut( nSel, false ); } else if( !bTableSel && !bFrameSel ) { if( !rSh.IsAddMode() ) { // #i87233# if ( rSh.IsBlockMode() ) { // preserve order of cursors for block mode rSh.GoPrevCursor(); } rSh.SwCursorShell::CreateCursor(); } rSh.SwCursorShell::SetCursor( rDragPt, true, false ); rSh.GoPrevCursor(); cWord = rSh.IntelligentCut( rSh.GetSelectionType(), false ); rSh.GoNextCursor(); } bInWrd = rSh.IsInWord(); bEndWrd = rSh.IsEndWrd(); bSttWrd = !bEndWrd && rSh.IsStartWord(); bSttPara= rSh.IsSttPara(); Point aSttPt( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY() ); // at first, select InetFields! if( TransferBufferType::InetField == m_eBufferType ) { if( &rSrcSh == &rSh ) { rSh.GoPrevCursor(); rSh.SwCursorShell::SetCursor( aSttPt, true ); rSh.SelectTextAttr( RES_TXTATR_INETFMT ); if( rSh.TestCurrPam( rDragPt ) ) { // don't copy/move inside of yourself rSh.DestroyCursor(); rSh.EndUndo(); rSh.EndAction(); rSh.EndAction(); return false; } rSh.GoNextCursor(); } else { rSrcSh.SwCursorShell::SetCursor( aSttPt, true ); rSrcSh.SelectTextAttr( RES_TXTATR_INETFMT ); } // is there a URL attribute at the insert point? Then replace that, // so simply put up a selection? rSh.DelINetAttrWithText(); g_bDDINetAttr = true; } if ( rSrcSh.IsSelFrameMode() ) { //Hack: fool the special treatment aSttPt = rSrcSh.GetObjRect().Pos(); } bool bRet = rSrcSh.SwFEShell::Copy(rSh, aSttPt, rDragPt, bMove, !bIsXSelection); if( !bIsXSelection ) { rSrcSh.Push(); if ( bRet && bMove && !bFrameSel ) { if ( bTableSel ) { /* delete table contents not cells */ rSrcSh.Delete(false); } else { //SmartCut, take one of the blanks along. rSh.SwCursorShell::DestroyCursor(); if ( cWord == SwWrtShell::WORD_SPACE_BEFORE ) rSh.ExtendSelection( false ); else if ( cWord == SwWrtShell::WORD_SPACE_AFTER ) rSh.ExtendSelection(); rSrcSh.DelRight(); } } rSrcSh.KillPams(); rSrcSh.Pop(SwCursorShell::PopMode::DeleteCurrent); /* after dragging a table selection inside one shell set cursor to the drop position. */ if( &rSh == &rSrcSh && ( bTableSel || rSh.IsBlockMode() ) ) { rSrcSh.CalcLayout(); rSrcSh.SwCursorShell::SetCursor(rDragPt); rSrcSh.GetCursor()->SetMark(); } } if( bRet && !bTableSel && !bFrameSel ) { if( (bInWrd || bEndWrd) && (cWord == SwWrtShell::WORD_SPACE_AFTER || cWord == SwWrtShell::WORD_SPACE_BEFORE) ) { if ( bSttWrd || (bInWrd && !bEndWrd)) rSh.SwEditShell::Insert(' ', bIsXSelection); if ( !bSttWrd || (bInWrd && !bSttPara) ) { rSh.SwapPam(); if ( !bSttWrd ) rSh.SwEditShell::Insert(' ', bIsXSelection); rSh.SwapPam(); } } if( bIsXSelection ) { if( &rSrcSh == &rSh && !rSh.IsAddMode() ) { rSh.SwCursorShell::DestroyCursor(); rSh.GoPrevCursor(); } else { rSh.SwapPam(); rSh.SwCursorShell::ClearMark(); } } else { if( rSh.IsAddMode() ) rSh.SwCursorShell::CreateCursor(); else { // turn on selection mode rSh.SttSelect(); rSh.EndSelect(); } } } else if ( bRet && bTableMove ) { SfxDispatcher* pDispatch = rSrcSh.GetView().GetViewFrame().GetDispatcher(); pDispatch->Execute(FN_TABLE_DELETE_TABLE, SfxCallMode::SYNCHRON); } if( bRet && bMove && bFrameSel ) rSrcSh.LeaveSelFrameMode(); if( rSrcSh.GetDoc() != rSh.GetDoc() ) rSrcSh.EndUndo(); rSh.EndUndo(); // put the shell in the right state if( &rSrcSh != &rSh && ( rSh.IsFrameSelected() || rSh.IsObjSelected() )) rSh.EnterSelFrameMode(); rSrcSh.EndAction(); rSh.EndAction(); return true; } // Interfaces for Selection void SwTransferable::CreateSelection( SwWrtShell& rSh, const SwFrameShell * _pCreatorView ) { SwModule *pMod = SW_MOD(); rtl::Reference pNew = new SwTransferable( rSh ); pNew->m_pCreatorView = _pCreatorView; pMod->m_pXSelection = pNew.get(); pNew->CopyToPrimarySelection(); } void SwTransferable::ClearSelection( const SwWrtShell& rSh, const SwFrameShell * _pCreatorView) { SwModule *pMod = SW_MOD(); if( pMod->m_pXSelection && ((!pMod->m_pXSelection->m_pWrtShell) || (pMod->m_pXSelection->m_pWrtShell == &rSh)) && (!_pCreatorView || (pMod->m_pXSelection->m_pCreatorView == _pCreatorView)) ) { TransferableHelper::ClearPrimarySelection(); } } SwTransferable* SwTransferable::GetSwTransferable( const TransferableDataHelper& rData ) { return dynamic_cast(rData.GetTransferable().get()); } SwTransferDdeLink::SwTransferDdeLink( SwTransferable& rTrans, SwWrtShell& rSh ) : m_rTransfer(rTrans) , m_pDocShell(nullptr) , m_nOldTimeOut(0) , m_bDelBookmark(false) , m_bInDisconnect(false) { // we only end up here with table- or text selection if( SelectionType::TableCell & rSh.GetSelectionType() ) { SwFrameFormat* pFormat = rSh.GetTableFormat(); if( pFormat ) m_sName = pFormat->GetName(); } else { // creating a temp. bookmark without undo bool bUndo = rSh.DoesUndo(); rSh.DoUndo( false ); bool bIsModified = rSh.IsModified(); ::sw::mark::IMark* pMark = rSh.SetBookmark( vcl::KeyCode(), OUString(), IDocumentMarkAccess::MarkType::DDE_BOOKMARK); if(pMark) { m_sName = pMark->GetName(); m_bDelBookmark = true; if( !bIsModified ) rSh.ResetModified(); } else m_sName.clear(); rSh.DoUndo( bUndo ); } if( m_sName.isEmpty() || nullptr == ( m_pDocShell = rSh.GetDoc()->GetDocShell() )) return; // then we create our "server" and connect to it m_xRefObj = m_pDocShell->DdeCreateLinkSource( m_sName ); if( m_xRefObj.is() ) { m_xRefObj->AddConnectAdvise( this ); m_xRefObj->AddDataAdvise( this, OUString(), ADVISEMODE_NODATA | ADVISEMODE_ONLYONCE ); m_nOldTimeOut = m_xRefObj->GetUpdateTimeout(); m_xRefObj->SetUpdateTimeout( 0 ); } } SwTransferDdeLink::~SwTransferDdeLink() { if( m_xRefObj.is() ) Disconnect( true ); } ::sfx2::SvBaseLink::UpdateResult SwTransferDdeLink::DataChanged( const OUString& , const uno::Any& ) { // well, that's it with the link if( !m_bInDisconnect ) { if( FindDocShell() && m_pDocShell->GetView() ) m_rTransfer.RemoveDDELinkFormat( m_pDocShell->GetView()->GetEditWin() ); Disconnect( false ); } return SUCCESS; } bool SwTransferDdeLink::WriteData( SvStream& rStrm ) { if( !m_xRefObj.is() || !FindDocShell() ) return false; rtl_TextEncoding eEncoding = osl_getThreadTextEncoding(); const OString aAppNm(OUStringToOString( Application::GetAppName(), eEncoding)); const OString aTopic(OUStringToOString( m_pDocShell->GetTitle(SFX_TITLE_FULLNAME), eEncoding)); const OString aName(OUStringToOString(m_sName, eEncoding)); std::unique_ptr pMem(new char[ aAppNm.getLength() + aTopic.getLength() + aName.getLength() + 4 ]); sal_Int32 nLen = aAppNm.getLength(); memcpy( pMem.get(), aAppNm.getStr(), nLen ); pMem[ nLen++ ] = 0; memcpy( pMem.get() + nLen, aTopic.getStr(), aTopic.getLength() ); nLen = nLen + aTopic.getLength(); pMem[ nLen++ ] = 0; memcpy( pMem.get() + nLen, aName.getStr(), aName.getLength() ); nLen = nLen + aName.getLength(); pMem[ nLen++ ] = 0; pMem[ nLen++ ] = 0; rStrm.WriteBytes( pMem.get(), nLen ); pMem.reset(); IDocumentMarkAccess* const pMarkAccess = m_pDocShell->GetDoc()->getIDocumentMarkAccess(); IDocumentMarkAccess::const_iterator_t ppMark = pMarkAccess->findMark(m_sName); if(ppMark != pMarkAccess->getAllMarksEnd() && IDocumentMarkAccess::GetType(**ppMark) != IDocumentMarkAccess::MarkType::BOOKMARK) { // the mark is still a DdeBookmark // we replace it with a Bookmark, so it will get saved etc. ::sw::mark::IMark* const pMark = *ppMark; ::sfx2::SvLinkSource* p = m_xRefObj.get(); SwServerObject& rServerObject = dynamic_cast(*p); // collecting state of old mark SwPaM aPaM(pMark->GetMarkStart()); *aPaM.GetPoint() = pMark->GetMarkStart(); if(pMark->IsExpanded()) { aPaM.SetMark(); *aPaM.GetMark() = pMark->GetMarkEnd(); } OUString sMarkName = pMark->GetName(); // remove mark rServerObject.SetNoServer(); // this removes the connection between SwServerObject and mark // N.B. ppMark was not loaded from file and cannot have xml:id pMarkAccess->deleteMark(ppMark, false); // recreate as Bookmark ::sw::mark::IMark* const pNewMark = pMarkAccess->makeMark( aPaM, sMarkName, IDocumentMarkAccess::MarkType::BOOKMARK, ::sw::mark::InsertMode::New); rServerObject.SetDdeBookmark(*pNewMark); } m_bDelBookmark = false; return true; } void SwTransferDdeLink::Disconnect( bool bRemoveDataAdvise ) { // don't accept DataChanged anymore, when already in Disconnect! // (DTOR from Bookmark sends a DataChanged!) bool bOldDisconnect = m_bInDisconnect; m_bInDisconnect = true; // destroy the unused bookmark again (without Undo!)? if( m_bDelBookmark && m_xRefObj.is() && FindDocShell() ) { SwDoc* pDoc = m_pDocShell->GetDoc(); ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo()); // #i58448# Link aSavedOle2Link( pDoc->GetOle2Link() ); pDoc->SetOle2Link( Link() ); bool bIsModified = pDoc->getIDocumentState().IsModified(); IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess(); pMarkAccess->deleteMark(pMarkAccess->findMark(m_sName), false); if( !bIsModified ) pDoc->getIDocumentState().ResetModified(); // #i58448# pDoc->SetOle2Link( aSavedOle2Link ); m_bDelBookmark = false; } if( m_xRefObj.is() ) { m_xRefObj->SetUpdateTimeout( m_nOldTimeOut ); m_xRefObj->RemoveConnectAdvise( this ); if( bRemoveDataAdvise ) // in a DataChanged the SelectionObject must NEVER be deleted // is already handled by the base class // (ADVISEMODE_ONLYONCE!!!!) // but always in normal Disconnect! m_xRefObj->RemoveAllDataAdvise( this ); m_xRefObj.clear(); } m_bInDisconnect = bOldDisconnect; } bool SwTransferDdeLink::FindDocShell() { SfxObjectShell* pTmpSh = SfxObjectShell::GetFirst( checkSfxObjectShell ); while( pTmpSh ) { if( pTmpSh == m_pDocShell ) // that's what we want to have { if( m_pDocShell->GetDoc() ) return true; break; // the Doc is not there anymore, so leave! } pTmpSh = SfxObjectShell::GetNext( *pTmpSh, checkSfxObjectShell ); } m_pDocShell = nullptr; return false; } void SwTransferDdeLink::Closed() { if( !m_bInDisconnect && m_xRefObj.is() ) { m_xRefObj->RemoveAllDataAdvise( this ); m_xRefObj->RemoveConnectAdvise( this ); m_xRefObj.clear(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */