/* -*- 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 #include #include #include #include using namespace com::sun::star; bool ScViewFunc::PasteDataFormat( SotClipboardFormatId nFormatId, const uno::Reference& rxTransferable, SCCOL nPosX, SCROW nPosY, const Point* pLogicPos, bool bLink, bool bAllowDialogs ) { ScDocument& rDoc = GetViewData().GetDocument(); rDoc.SetPastingDrawFromOtherDoc( true ); Point aPos; // inserting position (1/100 mm) if (pLogicPos) aPos = *pLogicPos; else { // inserting position isn't needed for text formats bool bIsTextFormat = ( ScImportExport::IsFormatSupported( nFormatId ) || nFormatId == SotClipboardFormatId::RTF ); if ( !bIsTextFormat ) { // Window MapMode isn't drawing MapMode if DrawingLayer hasn't been created yet SCTAB nTab = GetViewData().GetTabNo(); tools::Long nXT = 0; for (SCCOL i=0; i xStm; TransferableObjectDescriptor aObjDesc; if (aDataHelper.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc)) xStm = aDataHelper.GetInputStream(nFormatId, OUString()); if (xStm.is()) { if ( aObjDesc.maClassName == SvGlobalName( SO3_SC_CLASSID_60 ) ) { uno::Reference < embed::XStorage > xStore = ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm ); // mba: BaseURL doesn't make sense for clipboard // #i43716# Medium must be allocated with "new". // DoLoad stores the pointer and deletes it with the SfxObjectShell. SfxMedium* pMedium = new SfxMedium( xStore, OUString() ); // TODO/LATER: is it a problem that we don't support binary formats here? ScDocShellRef xDocShRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT); if (xDocShRef->DoLoad(pMedium)) { ScDocument& rSrcDoc = xDocShRef->GetDocument(); SCTAB nSrcTab = rSrcDoc.GetVisibleTab(); if (!rSrcDoc.HasTable(nSrcTab)) nSrcTab = 0; ScMarkData aSrcMark(rSrcDoc.GetSheetLimits()); aSrcMark.SelectOneTable( nSrcTab ); // for CopyToClip ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); SCCOL nFirstCol, nLastCol; SCROW nFirstRow, nLastRow; if ( rSrcDoc.GetDataStart( nSrcTab, nFirstCol, nFirstRow ) ) { rSrcDoc.GetCellArea( nSrcTab, nLastCol, nLastRow ); if (nLastCol < nFirstCol) nLastCol = nFirstCol; if (nLastRow < nFirstRow) nLastRow = nFirstRow; } else { nFirstCol = nLastCol = 0; nFirstRow = nLastRow = 0; } bool bIncludeObjects = false; // include drawing layer objects in CopyToClip ? if (nFormatId == SotClipboardFormatId::EMBED_SOURCE) { const ScDrawLayer* pDraw = rSrcDoc.GetDrawLayer(); SCCOL nPrintEndCol = nFirstCol; SCROW nPrintEndRow = nFirstRow; bool bHasObjects = pDraw && pDraw->HasObjects(); // Extend the range to include the drawing layer objects. if (bHasObjects && rSrcDoc.GetPrintArea(nSrcTab, nPrintEndCol, nPrintEndRow, true)) { nLastCol = std::max(nLastCol, nPrintEndCol); nLastRow = std::max(nLastRow, nPrintEndRow); } bIncludeObjects = bHasObjects; } ScClipParam aClipParam(ScRange(nFirstCol, nFirstRow, nSrcTab, nLastCol, nLastRow, nSrcTab), false); rSrcDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSrcMark, false, bIncludeObjects); ScGlobal::SetClipDocName( xDocShRef->GetTitle( SFX_TITLE_FULLNAME ) ); SetCursor( nPosX, nPosY ); Unmark(); PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(), ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, bAllowDialogs ); bRet = true; } xDocShRef->DoClose(); xDocShRef.clear(); } else { OUString aName; uno::Reference < embed::XEmbeddedObject > xObj = GetViewData().GetViewShell()->GetObjectShell()-> GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName ); if ( xObj.is() ) { // 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 (aDataHelper.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 ) { datatransfer::DataFlavor aDataFlavor; SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor ); PasteObject( aPos, xObj, &aObjDesc.maSize, &aGraphic, aDataFlavor.MimeType, aObjDesc.mnViewAspect ); } else PasteObject( aPos, xObj, &aObjDesc.maSize ); bRet = true; } else { OSL_FAIL("Error in CreateAndLoad"); } } } else { if ( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE, aObjDesc ) ) { OUString aName; uno::Reference < embed::XEmbeddedObject > xObj; xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBED_SOURCE_OLE, OUString()); if (!xStm.is()) aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString()); if (xStm.is()) { xObj = GetViewData().GetDocShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName ); } else { try { uno::Reference< embed::XStorage > 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; if ( xObj.is() ) GetViewData().GetDocShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName ); } catch( uno::Exception& ) {} } if ( xObj.is() ) { // try to get the replacement image from the clipboard Graphic aGraphic; SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE; // (for Selection Manager in Trusted Solaris) #ifndef __sun if( aDataHelper.GetGraphic( SotClipboardFormatId::SVXB, aGraphic ) ) nGrFormat = SotClipboardFormatId::SVXB; else if( aDataHelper.GetGraphic( SotClipboardFormatId::GDIMETAFILE, aGraphic ) ) nGrFormat = SotClipboardFormatId::GDIMETAFILE; else if( aDataHelper.GetGraphic( SotClipboardFormatId::BITMAP, aGraphic ) ) nGrFormat = SotClipboardFormatId::BITMAP; #endif // insert replacement image ( if there is one ) into the object helper if ( nGrFormat != SotClipboardFormatId::NONE ) { datatransfer::DataFlavor aDataFlavor; SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor ); PasteObject( aPos, xObj, &aObjDesc.maSize, &aGraphic, aDataFlavor.MimeType, aObjDesc.mnViewAspect ); } else PasteObject( aPos, xObj, &aObjDesc.maSize ); // let object stay in loaded state after insertion SdrOle2Obj::Unload( xObj, embed::Aspects::MSOLE_CONTENT ); bRet = true; } else { OSL_FAIL("Error creating external OLE object"); } } //TODO/LATER: if format is not available, create picture } } else if ( nFormatId == SotClipboardFormatId::LINK ) // LINK is also in ScImportExport { bRet = PasteLink( rxTransferable ); } else if ( ScImportExport::IsFormatSupported( nFormatId ) || nFormatId == SotClipboardFormatId::RTF || nFormatId == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) { if ( nFormatId == SotClipboardFormatId::RTF && ( aDataHelper.HasFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) ) ) { // use EditView's PasteSpecial / Drop PasteRTF( nPosX, nPosY, rxTransferable ); bRet = true; } else { ScAddress aCellPos( nPosX, nPosY, GetViewData().GetTabNo() ); auto pObj = std::make_shared(GetViewData().GetDocument(), aCellPos); pObj->SetOverwriting( true ); auto pStrBuffer = std::make_shared(); tools::SvRef xStream; if ( aDataHelper.GetSotStorageStream( nFormatId, xStream ) && xStream.is() ) { // Static variables for per-session storage. This could be // changed to longer-term storage in future. static bool bHaveSavedPreferences = false; static LanguageType eSavedLanguage; static bool bSavedDateConversion; if (nFormatId == SotClipboardFormatId::HTML && !comphelper::LibreOfficeKit::isActive()) { if (bHaveSavedPreferences) { ScAsciiOptions aOptions; aOptions.SetLanguage(eSavedLanguage); aOptions.SetDetectSpecialNumber(bSavedDateConversion); pObj->SetExtOptions(aOptions); } else { // Launch the text import options dialog. For now, we do // this for html pasting only, but in the future it may // make sense to do it for other data types too. ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); vcl::Window* pParent = GetActiveWin(); ScopedVclPtr pDlg( pFact->CreateScTextImportOptionsDlg(pParent ? pParent->GetFrameWeld() : nullptr)); if (pDlg->Execute() == RET_OK) { ScAsciiOptions aOptions; aOptions.SetLanguage(pDlg->GetLanguageType()); aOptions.SetDetectSpecialNumber(pDlg->IsDateConversionSet()); if (!pDlg->IsKeepAskingSet()) { bHaveSavedPreferences = true; eSavedLanguage = pDlg->GetLanguageType(); bSavedDateConversion = pDlg->IsDateConversionSet(); } pObj->SetExtOptions(aOptions); } else { // prevent error dialog for user cancel action bRet = true; } } } if(!bRet) bRet = pObj->ImportStream( *xStream, OUString(), nFormatId ); // mba: clipboard always must contain absolute URLs (could be from alien source) } else if ((nFormatId == SotClipboardFormatId::STRING || nFormatId == SotClipboardFormatId::STRING_TSVC) && aDataHelper.GetString( nFormatId, *pStrBuffer )) { // Do CSV dialog if more than one line. But not if invoked from Automation. const SfxViewShell* pViewShell = SfxViewShell::Current(); sal_Int32 nDelim = pStrBuffer->indexOf('\n'); if (!(pViewShell && pViewShell->isLOKMobilePhone()) && !comphelper::Automation::AutomationInvokedZone::isActive() && nDelim >= 0 && nDelim != pStrBuffer->getLength () - 1) { vcl::Window* pParent = GetActiveWin(); auto pStrm = std::make_shared(*pStrBuffer); ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); VclPtr pDlg( pFact->CreateScImportAsciiDlg(pParent ? pParent->GetFrameWeld() : nullptr, OUString(), pStrm.get(), SC_PASTETEXT)); bAllowDialogs = bAllowDialogs && !SC_MOD()->IsInExecuteDrop(); pDlg->StartExecuteAsync([this, pDlg, &rDoc, pStrm, nFormatId, pStrBuffer, pObj, bAllowDialogs](sal_Int32 nResult){ bool bShowErrorDialog = bAllowDialogs; if (RET_OK == nResult) { ScAsciiOptions aOptions; pDlg->GetOptions( aOptions ); pDlg->SaveParameters(); pObj->SetExtOptions( aOptions ); pObj->ImportString( *pStrBuffer, nFormatId ); // TODO: what if (aObj.IsOverflow()) // Content was partially pasted, which can be undone by // the user though. bShowErrorDialog = bShowErrorDialog && pObj->IsOverflow(); } else { bShowErrorDialog = false; // Yes, no failure, don't raise a "couldn't paste" // dialog if user cancelled. } InvalidateAttribs(); GetViewData().UpdateInputHandler(); rDoc.SetPastingDrawFromOtherDoc( false ); if (bShowErrorDialog) ErrorMessage(STR_PASTE_ERROR); pDlg->disposeOnce(); }); return true; } else bRet = pObj->ImportString( *pStrBuffer, nFormatId ); } else if ((nFormatId != SotClipboardFormatId::STRING && nFormatId != SotClipboardFormatId::STRING_TSVC) && aDataHelper.GetString( nFormatId, *pStrBuffer )) bRet = pObj->ImportString( *pStrBuffer, nFormatId ); InvalidateAttribs(); GetViewData().UpdateInputHandler(); } } else if (nFormatId == SotClipboardFormatId::SBA_DATAEXCHANGE) { // import of database data into table const DataFlavorExVector& rVector = aDataHelper.GetDataFlavorExVector(); if ( svx::ODataAccessObjectTransferable::canExtractObjectDescriptor(rVector) ) { // transport the whole ODataAccessDescriptor as slot parameter svx::ODataAccessDescriptor aDesc = svx::ODataAccessObjectTransferable::extractObjectDescriptor(aDataHelper); uno::Any aDescAny; uno::Sequence aProperties = aDesc.createPropertyValueSequence(); aDescAny <<= aProperties; SfxUnoAnyItem aDataDesc(SID_SBA_IMPORT, aDescAny); ScDocShell* pDocSh = GetViewData().GetDocShell(); SCTAB nTab = GetViewData().GetTabNo(); ClickCursor(nPosX, nPosY, false); // set cursor position // Creation of database area "Import1" isn't here, but in the DocShell // slot execute, so it can be added to the undo action ScDBData* pDBData = pDocSh->GetDBData( ScRange(nPosX,nPosY,nTab), SC_DB_OLD, ScGetDBSelection::Keep ); OUString sTarget; if (pDBData) sTarget = pDBData->GetName(); else { ScAddress aCellPos( nPosX,nPosY,nTab ); sTarget = aCellPos.Format(ScRefFlags::ADDR_ABS_3D, &rDoc, rDoc.GetAddressConvention()); } SfxStringItem aTarget(FN_PARAM_1, sTarget); bool bAreaIsNew = !pDBData; SfxBoolItem aAreaNew(FN_PARAM_2, bAreaIsNew); // asynchronous, to avoid doing the whole import in drop handler SfxDispatcher& rDisp = GetViewData().GetDispatcher(); rDisp.ExecuteList(SID_SBA_IMPORT, SfxCallMode::ASYNCHRON, { &aDataDesc, &aTarget, &aAreaNew }); bRet = true; } } else if (nFormatId == SotClipboardFormatId::SBA_FIELDDATAEXCHANGE) { // insert database field control if ( svx::OColumnTransferable::canExtractColumnDescriptor( aDataHelper.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE ) ) { MakeDrawLayer(); ScDrawView* pScDrawView = GetScDrawView(); SdrObjectUniquePtr pObj = pScDrawView->CreateFieldControl( svx::OColumnTransferable::extractColumnDescriptor( aDataHelper ) ); if (pObj) { Point aInsPos = aPos; tools::Rectangle aRect(pObj->GetLogicRect()); aInsPos.AdjustX( -(aRect.GetSize().Width() / 2) ); aInsPos.AdjustY( -(aRect.GetSize().Height() / 2) ); if ( aInsPos.X() < 0 ) aInsPos.setX( 0 ); if ( aInsPos.Y() < 0 ) aInsPos.setY( 0 ); aRect.SetPos(aInsPos); pObj->SetLogicRect(aRect); if ( dynamic_cast( pObj.get() ) != nullptr ) pObj->NbcSetLayer(SC_LAYER_CONTROLS); else pObj->NbcSetLayer(SC_LAYER_FRONT); if (dynamic_cast( pObj.get() ) != nullptr) { SdrObjListIter aIter( *pObj, SdrIterMode::DeepWithGroups ); SdrObject* pSubObj = aIter.Next(); while (pSubObj) { if ( dynamic_cast( pSubObj) != nullptr ) pSubObj->NbcSetLayer(SC_LAYER_CONTROLS); else pSubObj->NbcSetLayer(SC_LAYER_FRONT); pSubObj = aIter.Next(); } } pScDrawView->InsertObjectSafe(pObj.release(), *pScDrawView->GetSdrPageView()); GetViewData().GetViewShell()->SetDrawShell( true ); bRet = true; } } } else if (nFormatId == SotClipboardFormatId::BITMAP || nFormatId == SotClipboardFormatId::PNG || nFormatId == SotClipboardFormatId::JPEG) { BitmapEx aBmpEx; if( aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) ) bRet = PasteBitmapEx( aPos, aBmpEx ); } else if (nFormatId == SotClipboardFormatId::GDIMETAFILE) { GDIMetaFile aMtf; if( aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) ) bRet = PasteMetaFile( aPos, aMtf ); } else if (nFormatId == SotClipboardFormatId::SVXB) { tools::SvRef xStm; if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) ) { Graphic aGraphic; TypeSerializer aSerializer(*xStm); aSerializer.readGraphic(aGraphic); bRet = PasteGraphic( aPos, aGraphic, OUString() ); } } else if ( nFormatId == SotClipboardFormatId::DRAWING ) { tools::SvRef xStm; if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStm ) ) { MakeDrawLayer(); // before loading model, so 3D factory has been created ScDocShellRef aDragShellRef( new ScDocShell ); aDragShellRef->DoInitNew(); std::unique_ptr pModel( new FmFormModel( nullptr, aDragShellRef.get())); pModel->GetItemPool().FreezeIdRanges(); xStm->Seek(0); css::uno::Reference< css::io::XInputStream > xInputStream( new utl::OInputStreamWrapper( *xStm ) ); SvxDrawingLayerImport( pModel.get(), xInputStream ); // set everything to right layer: size_t nObjCount = 0; sal_uInt16 nPages = pModel->GetPageCount(); for (sal_uInt16 i=0; iGetPage(i); SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); SdrObject* pObject = aIter.Next(); while (pObject) { if ( dynamic_cast( pObject) != nullptr ) pObject->NbcSetLayer(SC_LAYER_CONTROLS); else pObject->NbcSetLayer(SC_LAYER_FRONT); pObject = aIter.Next(); } nObjCount += pPage->GetObjCount(); // count group object only once } PasteDraw(aPos, pModel.get(), (nObjCount > 1), u"A", u"B"); // grouped if more than 1 object pModel.reset(); aDragShellRef->DoClose(); bRet = true; } } else if ( (nFormatId == SotClipboardFormatId::BIFF_5) || (nFormatId == SotClipboardFormatId::BIFF_8) ) { // do excel import into a clipboard document //TODO/MBA: testing uno::Reference xStm = aDataHelper.GetInputStream(nFormatId, OUString()); if (xStm.is()) { ScDocument aInsDoc( SCDOCMODE_CLIP ); SCTAB nSrcTab = 0; // Biff5 in clipboard: always sheet 0 aInsDoc.ResetClip( &rDoc, nSrcTab ); SfxMedium aMed; aMed.GetItemSet()->Put( SfxUnoAnyItem( SID_INPUTSTREAM, uno::Any( xStm ) ) ); ErrCode eErr = ScFormatFilter::Get().ScImportExcel( aMed, &aInsDoc, EIF_AUTO ); if ( eErr == ERRCODE_NONE ) { ScRange aSource; const ScExtDocOptions* pExtOpt = aInsDoc.GetExtDocOptions(); const ScExtTabSettings* pTabSett = pExtOpt ? pExtOpt->GetTabSettings( nSrcTab ) : nullptr; if( pTabSett && pTabSett->maUsedArea.IsValid() ) { aSource = pTabSett->maUsedArea; // ensure correct sheet indexes aSource.aStart.SetTab( nSrcTab ); aSource.aEnd.SetTab( nSrcTab ); // don't use selection area: if cursor is moved in Excel after Copy, selection // represents the new cursor position and not the copied area } else { OSL_FAIL("no dimension"); //! possible? SCCOL nFirstCol, nLastCol; SCROW nFirstRow, nLastRow; if ( aInsDoc.GetDataStart( nSrcTab, nFirstCol, nFirstRow ) ) aInsDoc.GetCellArea( nSrcTab, nLastCol, nLastRow ); else { nFirstCol = nLastCol = 0; nFirstRow = nLastRow = 0; } aSource = ScRange( nFirstCol, nFirstRow, nSrcTab, nLastCol, nLastRow, nSrcTab ); } if ( pLogicPos ) { // position specified (Drag&Drop) - change selection MoveCursorAbs( nPosX, nPosY, SC_FOLLOW_NONE, false, false ); Unmark(); } aInsDoc.SetClipArea( aSource ); PasteFromClip( InsertDeleteFlags::ALL, &aInsDoc, ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, bAllowDialogs ); bRet = true; } } } else if ( nFormatId == SotClipboardFormatId::SIMPLE_FILE ) { OUString aFile; if ( aDataHelper.GetString( nFormatId, aFile ) ) bRet = PasteFile( aPos, aFile, bLink ); } else if ( nFormatId == SotClipboardFormatId::FILE_LIST ) { FileList aFileList; if ( aDataHelper.GetFileList( nFormatId, aFileList ) ) { sal_uLong nCount = aFileList.Count(); for( sal_uLong i = 0; i < nCount ; i++ ) { OUString aFile = aFileList.GetFile( i ); PasteFile( aPos, aFile, bLink ); aPos.AdjustX(400 ); aPos.AdjustY(400 ); } bRet = true; } } else if ( nFormatId == SotClipboardFormatId::SOLK || nFormatId == SotClipboardFormatId::UNIFORMRESOURCELOCATOR || nFormatId == SotClipboardFormatId::NETSCAPE_BOOKMARK || nFormatId == SotClipboardFormatId::FILEGRPDESCRIPTOR ) { bRet = PasteBookmark( nFormatId, rxTransferable, nPosX, nPosY ); } rDoc.SetPastingDrawFromOtherDoc( false ); return bRet; } bool ScViewFunc::PasteLink( const uno::Reference& rxTransferable ) { TransferableDataHelper aDataHelper( rxTransferable ); // get link data from transferable before string data, // so the source knows it will be used for a link uno::Sequence aSequence = aDataHelper.GetSequence(SotClipboardFormatId::LINK, OUString()); if (!aSequence.hasElements()) { OSL_FAIL("DDE Data not found."); return false; } // check size (only if string is available in transferable) sal_uInt16 nCols = 1; sal_uInt16 nRows = 1; if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) { OUString aDataStr; if ( aDataHelper.GetString( SotClipboardFormatId::STRING, aDataStr ) ) { // get size from string the same way as in ScDdeLink::DataChanged aDataStr = convertLineEnd(aDataStr, LINEEND_LF); sal_Int32 nLen = aDataStr.getLength(); if (nLen && aDataStr[nLen-1] == '\n') aDataStr = aDataStr.copy(0, nLen-1); if (!aDataStr.isEmpty()) { nRows = comphelper::string::getTokenCount(aDataStr, '\n'); std::u16string_view aLine = o3tl::getToken(aDataStr, 0, '\n' ); if (!aLine.empty()) nCols = comphelper::string::getTokenCount(aLine, '\t'); } } } // create formula sal_Int32 nSeqLen = aSequence.getLength(); const char* p = reinterpret_cast(aSequence.getConstArray()); rtl_TextEncoding eSysEnc = osl_getThreadTextEncoding(); // char array delimited by \0. // app \0 topic \0 item \0 (extra \0) where the extra is optional. ::std::vector aStrs; const char* pStart = p; sal_Int32 nStart = 0; for (sal_Int32 i = 0; i < nSeqLen; ++i, ++p) { if (*p == '\0') { sal_Int32 nLen = i - nStart; aStrs.emplace_back(pStart, nLen, eSysEnc); nStart = ++i; pStart = ++p; } } if (aStrs.size() < 3) return false; const OUString& pApp = aStrs[0]; const OUString& pTopic = aStrs[1]; const OUString& pItem = aStrs[2]; const OUString* pExtra = nullptr; if (aStrs.size() > 3) pExtra = &aStrs[3]; if ( pExtra && *pExtra == "calc:extref" ) { // Paste this as an external reference. Note that paste link always // uses Calc A1 syntax even when another formula syntax is specified // in the UI. EnterMatrix("='" + ScGlobal::GetAbsDocName(pTopic, GetViewData().GetDocument().GetDocumentShell()) + "'#" + pItem , ::formula::FormulaGrammar::GRAM_NATIVE); return true; } else { // DDE in all other cases. // TODO: we could define ocQuote for " EnterMatrix("=" + ScCompiler::GetNativeSymbol(ocDde) + ScCompiler::GetNativeSymbol(ocOpen) + "\"" + pApp + "\"" + ScCompiler::GetNativeSymbol(ocSep) + "\"" + pTopic + "\"" + ScCompiler::GetNativeSymbol(ocSep) + "\"" + pItem + "\"" + ScCompiler::GetNativeSymbol(ocClose) , ::formula::FormulaGrammar::GRAM_NATIVE); } // mark range SCTAB nTab = GetViewData().GetTabNo(); SCCOL nCurX = GetViewData().GetCurX(); SCROW nCurY = GetViewData().GetCurY(); HideAllCursors(); DoneBlockMode(); InitBlockMode( nCurX, nCurY, nTab ); MarkCursor( nCurX+static_cast(nCols)-1, nCurY+static_cast(nRows)-1, nTab ); ShowAllCursors(); CursorPosChanged(); return true; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */