938 lines
35 KiB
C++
938 lines
35 KiB
C++
/* -*- 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 <scitems.hxx>
|
|
#include <editeng/justifyitem.hxx>
|
|
|
|
#include <com/sun/star/uno/Sequence.hxx>
|
|
#include <com/sun/star/embed/XTransactedObject.hpp>
|
|
|
|
#include <o3tl/unit_conversion.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <unotools/tempfile.hxx>
|
|
#include <unotools/ucbstreamhelper.hxx>
|
|
#include <unotools/streamwrap.hxx>
|
|
#include <comphelper/fileformat.h>
|
|
#include <comphelper/lok.hxx>
|
|
#include <comphelper/storagehelper.hxx>
|
|
#include <comphelper/servicehelper.hxx>
|
|
#include <sot/storage.hxx>
|
|
#include <utility>
|
|
#include <vcl/gdimtf.hxx>
|
|
#include <vcl/jobset.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/virdev.hxx>
|
|
#include <sfx2/docfile.hxx>
|
|
|
|
#include <transobj.hxx>
|
|
#include <patattr.hxx>
|
|
#include <cellvalue.hxx>
|
|
#include <cellform.hxx>
|
|
#include <document.hxx>
|
|
#include <viewopti.hxx>
|
|
#include <editutil.hxx>
|
|
#include <impex.hxx>
|
|
#include <formulacell.hxx>
|
|
#include <printfun.hxx>
|
|
#include <docfunc.hxx>
|
|
#include <scmod.hxx>
|
|
#include <dragdata.hxx>
|
|
#include <sortparam.hxx>
|
|
#include <tabvwsh.hxx>
|
|
|
|
#include <editeng/paperinf.hxx>
|
|
#include <editeng/sizeitem.hxx>
|
|
#include <formula/errorcodes.hxx>
|
|
#include <docsh.hxx>
|
|
#include <markdata.hxx>
|
|
#include <stlpool.hxx>
|
|
#include <viewdata.hxx>
|
|
#include <dociter.hxx>
|
|
#include <cellsuno.hxx>
|
|
#include <stringutil.hxx>
|
|
#include <formulaiter.hxx>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
constexpr sal_uInt32 SCTRANS_TYPE_IMPEX = 1;
|
|
constexpr sal_uInt32 SCTRANS_TYPE_EDIT_RTF = 2;
|
|
constexpr sal_uInt32 SCTRANS_TYPE_EDIT_BIN = 3;
|
|
constexpr sal_uInt32 SCTRANS_TYPE_EMBOBJ = 4;
|
|
constexpr sal_uInt32 SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT = 5;
|
|
|
|
void ScTransferObj::GetAreaSize( const ScDocument& rDoc, SCTAB nTab1, SCTAB nTab2, SCROW& nRow, SCCOL& nCol )
|
|
{
|
|
SCCOL nMaxCol = 0;
|
|
SCROW nMaxRow = 0;
|
|
for( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
|
|
{
|
|
SCCOL nLastCol = 0;
|
|
SCROW nLastRow = 0;
|
|
// GetPrintArea instead of GetCellArea - include drawing objects
|
|
if( rDoc.GetPrintArea( nTab, nLastCol, nLastRow ) )
|
|
{
|
|
if( nLastCol > nMaxCol )
|
|
nMaxCol = nLastCol;
|
|
if( nLastRow > nMaxRow )
|
|
nMaxRow = nLastRow;
|
|
}
|
|
}
|
|
nRow = nMaxRow;
|
|
nCol = nMaxCol;
|
|
}
|
|
|
|
void ScTransferObj::PaintToDev( OutputDevice* pDev, ScDocument& rDoc, double nPrintFactor,
|
|
const ScRange& rBlock )
|
|
{
|
|
tools::Rectangle aBound( Point(), pDev->GetOutputSize() ); //! use size from clip area?
|
|
|
|
ScViewData aViewData(rDoc);
|
|
|
|
aViewData.SetTabNo( rBlock.aEnd.Tab() );
|
|
aViewData.SetScreen( rBlock.aStart.Col(), rBlock.aStart.Row(),
|
|
rBlock.aEnd.Col(), rBlock.aEnd.Row() );
|
|
|
|
ScPrintFunc::DrawToDev( rDoc, pDev, nPrintFactor, aBound, &aViewData, false/*bMetaFile*/ );
|
|
}
|
|
|
|
ScTransferObj::ScTransferObj( const std::shared_ptr<ScDocument>& pClipDoc, TransferableObjectDescriptor aDesc ) :
|
|
m_pDoc( pClipDoc ),
|
|
m_nNonFiltered(0),
|
|
m_aObjDesc(std::move( aDesc )),
|
|
m_nDragHandleX( 0 ),
|
|
m_nDragHandleY( 0 ),
|
|
m_nSourceCursorX( m_pDoc->MaxCol() + 1 ),
|
|
m_nSourceCursorY( m_pDoc->MaxRow() + 1 ),
|
|
m_nDragSourceFlags( ScDragSrc::Undefined ),
|
|
m_bDragWasInternal( false ),
|
|
m_bUsedForLink( false ),
|
|
m_bUseInApi( false )
|
|
{
|
|
OSL_ENSURE(m_pDoc->IsClipboard(), "wrong document");
|
|
|
|
// get aBlock from clipboard doc
|
|
|
|
SCCOL nCol1;
|
|
SCROW nRow1;
|
|
SCCOL nCol2;
|
|
SCROW nRow2;
|
|
m_pDoc->GetClipStart( nCol1, nRow1 );
|
|
m_pDoc->GetClipArea( nCol2, nRow2, true ); // real source area - include filtered rows
|
|
nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nCol1 );
|
|
nRow2 = sal::static_int_cast<SCROW>( nRow2 + nRow1 );
|
|
|
|
SCCOL nDummy;
|
|
m_pDoc->GetClipArea( nDummy, m_nNonFiltered, false );
|
|
m_bHasFiltered = (m_nNonFiltered < (nRow2 - nRow1));
|
|
++m_nNonFiltered; // to get count instead of diff
|
|
|
|
SCTAB nTab1=0;
|
|
SCTAB nTab2=0;
|
|
bool bFirst = true;
|
|
for (SCTAB i=0; i< m_pDoc->GetTableCount(); i++)
|
|
if (m_pDoc->HasTable(i))
|
|
{
|
|
if (bFirst)
|
|
nTab1 = i;
|
|
nTab2 = i;
|
|
bFirst = false;
|
|
}
|
|
OSL_ENSURE(!bFirst, "no sheet selected");
|
|
|
|
// only limit to used cells if whole sheet was marked
|
|
// (so empty cell areas can be copied)
|
|
if ( nCol2>=m_pDoc->MaxCol() && nRow2>=m_pDoc->MaxRow() )
|
|
{
|
|
SCROW nMaxRow;
|
|
SCCOL nMaxCol;
|
|
GetAreaSize( *m_pDoc, nTab1, nTab2, nMaxRow, nMaxCol );
|
|
if( nMaxRow < nRow2 )
|
|
nRow2 = nMaxRow;
|
|
if( nMaxCol < nCol2 )
|
|
nCol2 = nMaxCol;
|
|
}
|
|
|
|
m_aBlock = ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
|
|
m_nVisibleTab = nTab1; // valid table as default
|
|
|
|
tools::Rectangle aMMRect = m_pDoc->GetMMRect( nCol1,nRow1, nCol2,nRow2, nTab1 );
|
|
m_aObjDesc.maSize = aMMRect.GetSize();
|
|
PrepareOLE( m_aObjDesc );
|
|
}
|
|
|
|
ScTransferObj::~ScTransferObj()
|
|
{
|
|
SolarMutexGuard aSolarGuard;
|
|
|
|
bool bIsDisposing = comphelper::LibreOfficeKit::isActive() && !ScTabViewShell::GetActiveViewShell();
|
|
ScModule* pScMod = ScModule::get();
|
|
if (pScMod && !bIsDisposing && pScMod->GetDragData().pCellTransfer == this)
|
|
{
|
|
OSL_FAIL("ScTransferObj wasn't released");
|
|
pScMod->ResetDragObject();
|
|
}
|
|
|
|
m_pDoc.reset(); // ScTransferObj is owner of clipboard document
|
|
|
|
m_aDocShellRef.clear(); // before releasing the mutex
|
|
|
|
m_aDrawPersistRef.clear(); // after the model
|
|
|
|
}
|
|
|
|
ScTransferObj* ScTransferObj::GetOwnClipboard(const uno::Reference<datatransfer::XTransferable2>& xTransferable)
|
|
{
|
|
return dynamic_cast<ScTransferObj*>(xTransferable.get());
|
|
}
|
|
|
|
void ScTransferObj::AddSupportedFormats()
|
|
{
|
|
// same formats as in ScSelectionTransferObj::AddSupportedFormats
|
|
AddFormat( SotClipboardFormatId::EMBED_SOURCE );
|
|
AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR );
|
|
AddFormat( SotClipboardFormatId::GDIMETAFILE );
|
|
AddFormat( SotClipboardFormatId::PNG );
|
|
AddFormat( SotClipboardFormatId::BITMAP );
|
|
|
|
// ScImportExport formats
|
|
AddFormat( SotClipboardFormatId::HTML );
|
|
AddFormat( SotClipboardFormatId::SYLK );
|
|
AddFormat( SotClipboardFormatId::LINK );
|
|
AddFormat( SotClipboardFormatId::DIF );
|
|
AddFormat( SotClipboardFormatId::STRING );
|
|
AddFormat( SotClipboardFormatId::STRING_TSVC );
|
|
|
|
AddFormat( SotClipboardFormatId::RTF );
|
|
AddFormat( SotClipboardFormatId::RICHTEXT );
|
|
if ( m_aBlock.aStart == m_aBlock.aEnd )
|
|
{
|
|
AddFormat( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT );
|
|
}
|
|
}
|
|
|
|
static ScRange lcl_reduceBlock(const ScDocument& rDoc, ScRange aReducedBlock, bool bIncludeVisual = false)
|
|
{
|
|
if ((aReducedBlock.aEnd.Col() == rDoc.MaxCol() || aReducedBlock.aEnd.Row() == rDoc.MaxRow()) &&
|
|
aReducedBlock.aStart.Tab() == aReducedBlock.aEnd.Tab())
|
|
{
|
|
// Shrink the block here so we don't waste time creating huge
|
|
// output when whole columns or rows are selected.
|
|
|
|
SCCOL nPrintAreaEndCol = 0;
|
|
SCROW nPrintAreaEndRow = 0;
|
|
if (bIncludeVisual)
|
|
rDoc.GetPrintArea( aReducedBlock.aStart.Tab(), nPrintAreaEndCol, nPrintAreaEndRow, true );
|
|
|
|
// Shrink the area to allow pasting to external applications.
|
|
// Shrink to real data area for HTML, RTF and RICHTEXT, but include
|
|
// all objects and top-left area for BITMAP and PNG.
|
|
SCCOL nStartCol = aReducedBlock.aStart.Col();
|
|
SCROW nStartRow = aReducedBlock.aStart.Row();
|
|
SCCOL nEndCol = aReducedBlock.aEnd.Col();
|
|
SCROW nEndRow = aReducedBlock.aEnd.Row();
|
|
|
|
if (bIncludeVisual)
|
|
{
|
|
ScDataAreaExtras aDataAreaExtras;
|
|
aDataAreaExtras.mbCellNotes = true;
|
|
aDataAreaExtras.mbCellDrawObjects = true;
|
|
bool bShrunk = false;
|
|
rDoc.ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow,
|
|
false, true /*bStickyTopRow*/, true /*bStickyLeftCol*/, &aDataAreaExtras);
|
|
aDataAreaExtras.GetOverallRange( nStartCol, nStartRow, nEndCol, nEndRow, ScDataAreaExtras::Clip::None);
|
|
}
|
|
else
|
|
{
|
|
bool bShrunk = false;
|
|
rDoc.ShrinkToUsedDataArea( bShrunk, aReducedBlock.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow,
|
|
false, false /*bStickyTopRow*/, false /*bStickyLeftCol*/);
|
|
}
|
|
|
|
if ( nPrintAreaEndRow > nEndRow )
|
|
nEndRow = nPrintAreaEndRow;
|
|
|
|
if ( nPrintAreaEndCol > nEndCol )
|
|
nEndCol = nPrintAreaEndCol;
|
|
|
|
aReducedBlock = ScRange(nStartCol, nStartRow, aReducedBlock.aStart.Tab(), nEndCol, nEndRow, aReducedBlock.aEnd.Tab());
|
|
}
|
|
return aReducedBlock;
|
|
}
|
|
|
|
bool ScTransferObj::GetData( const datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
|
|
{
|
|
SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
|
|
bool bOK = false;
|
|
|
|
if( HasFormat( nFormat ) )
|
|
{
|
|
ScRange aReducedBlock = m_aBlock;
|
|
|
|
bool bReduceBlockFormat =
|
|
nFormat == SotClipboardFormatId::HTML
|
|
|| nFormat == SotClipboardFormatId::RTF
|
|
|| nFormat == SotClipboardFormatId::RICHTEXT
|
|
|| nFormat == SotClipboardFormatId::BITMAP
|
|
|| nFormat == SotClipboardFormatId::PNG;
|
|
|
|
const bool bIncludeVisual = (nFormat == SotClipboardFormatId::BITMAP ||
|
|
nFormat == SotClipboardFormatId::PNG);
|
|
|
|
if (bReduceBlockFormat)
|
|
aReducedBlock = lcl_reduceBlock(*m_pDoc, m_aBlock, bIncludeVisual);
|
|
|
|
if ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR )
|
|
{
|
|
bOK = SetTransferableObjectDescriptor( m_aObjDesc );
|
|
}
|
|
else if ( ( nFormat == SotClipboardFormatId::RTF || nFormat == SotClipboardFormatId::RICHTEXT ||
|
|
nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ) && m_aBlock.aStart == m_aBlock.aEnd )
|
|
{
|
|
// RTF from a single cell is handled by EditEngine
|
|
|
|
SCCOL nCol = m_aBlock.aStart.Col();
|
|
SCROW nRow = m_aBlock.aStart.Row();
|
|
SCTAB nTab = m_aBlock.aStart.Tab();
|
|
ScAddress aPos(nCol, nRow, nTab);
|
|
|
|
const ScPatternAttr* pPattern = m_pDoc->GetPattern( nCol, nRow, nTab );
|
|
if (pPattern)
|
|
{
|
|
ScTabEditEngine aEngine(*pPattern, m_pDoc->GetEditPool(), m_pDoc.get());
|
|
ScRefCellValue aCell(*m_pDoc, aPos);
|
|
if (aCell.getType() == CELLTYPE_EDIT)
|
|
{
|
|
const EditTextObject* pObj = aCell.getEditText();
|
|
aEngine.SetTextCurrentDefaults(*pObj);
|
|
}
|
|
else
|
|
{
|
|
ScInterpreterContext& rContext = m_pDoc->GetNonThreadedContext();
|
|
sal_uInt32 nNumFmt = pPattern->GetNumberFormat(rContext);
|
|
const Color* pColor;
|
|
OUString aText
|
|
= ScCellFormat::GetString(aCell, nNumFmt, &pColor, &rContext, *m_pDoc);
|
|
if (!aText.isEmpty())
|
|
aEngine.SetTextCurrentDefaults(aText);
|
|
}
|
|
|
|
bOK = SetObject(&aEngine,
|
|
((nFormat == SotClipboardFormatId::RTF)
|
|
? SCTRANS_TYPE_EDIT_RTF
|
|
: ((nFormat == SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT)
|
|
? SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT
|
|
: SCTRANS_TYPE_EDIT_BIN)),
|
|
rFlavor);
|
|
}
|
|
}
|
|
else if ( ScImportExport::IsFormatSupported( nFormat ) || nFormat == SotClipboardFormatId::RTF
|
|
|| nFormat == SotClipboardFormatId::RICHTEXT )
|
|
{
|
|
// if this transfer object was used to create a DDE link, filtered rows
|
|
// have to be included for subsequent calls (to be consistent with link data)
|
|
if ( nFormat == SotClipboardFormatId::LINK )
|
|
m_bUsedForLink = true;
|
|
|
|
bool bIncludeFiltered = m_pDoc->IsCutMode() || m_bUsedForLink;
|
|
|
|
ScImportExport aObj( *m_pDoc, aReducedBlock );
|
|
// Plain text ("Unformatted text") may contain embedded tabs and
|
|
// line breaks but is not enclosed in quotes. Which makes it
|
|
// unsuitable for multiple cells, especially if one of them is
|
|
// multi-line, but otherwise is expected behavior for plain text.
|
|
// For multiple cells replace embedded line breaks (and tabs) with
|
|
// space character, otherwise pasting would yield odd results.
|
|
/* XXX: it's debatable whether this is actually expected, but
|
|
* there's no way to satisfy all possible requirements when
|
|
* copy/pasting unformatted text. */
|
|
const bool bPlainMulti = (nFormat == SotClipboardFormatId::STRING &&
|
|
aReducedBlock.aStart != aReducedBlock.aEnd);
|
|
// Add quotes only for STRING_TSVC.
|
|
/* TODO: a possible future STRING_TSV should not contain embedded
|
|
* line breaks nor tab (separator) characters and not be quoted.
|
|
* A possible STRING_CSV should. */
|
|
ScExportTextOptions aTextOptions( ScExportTextOptions::None, 0,
|
|
(nFormat == SotClipboardFormatId::STRING_TSVC));
|
|
if ( bPlainMulti || m_bUsedForLink )
|
|
{
|
|
// For a DDE link or plain text multiple cells, convert line
|
|
// breaks and separators to space.
|
|
aTextOptions.meNewlineConversion = ScExportTextOptions::ToSpace;
|
|
aTextOptions.mcSeparatorConvertTo = ' ';
|
|
aTextOptions.mbAddQuotes = false;
|
|
}
|
|
aObj.SetExportTextOptions(aTextOptions);
|
|
aObj.SetFormulas( m_pDoc->GetViewOptions().GetOption( VOPT_FORMULAS ) );
|
|
aObj.SetIncludeFiltered( bIncludeFiltered );
|
|
|
|
// DataType depends on format type:
|
|
|
|
if ( rFlavor.DataType.equals( ::cppu::UnoType<OUString>::get() ) )
|
|
{
|
|
OUString aString;
|
|
if ( aObj.ExportString( aString, nFormat ) )
|
|
bOK = SetString( aString );
|
|
}
|
|
else if ( rFlavor.DataType.equals( cppu::UnoType<uno::Sequence< sal_Int8 >>::get() ) )
|
|
{
|
|
// SetObject converts a stream into an Int8-Sequence
|
|
bOK = SetObject( &aObj, SCTRANS_TYPE_IMPEX, rFlavor );
|
|
}
|
|
else
|
|
{
|
|
OSL_FAIL("unknown DataType");
|
|
}
|
|
}
|
|
else if ( nFormat == SotClipboardFormatId::BITMAP || nFormat == SotClipboardFormatId::PNG )
|
|
{
|
|
tools::Rectangle aMMRect = m_pDoc->GetMMRect( aReducedBlock.aStart.Col(), aReducedBlock.aStart.Row(),
|
|
aReducedBlock.aEnd.Col(), aReducedBlock.aEnd.Row(),
|
|
aReducedBlock.aStart.Tab() );
|
|
ScopedVclPtrInstance< VirtualDevice > pVirtDev;
|
|
|
|
// tdf#160855 fix crash due to Skia's internal maximum pixel limit
|
|
// Somewhere in the tens of thousands of selected fill cells,
|
|
// the size of the VirtualDevice exceeds 1 GB of pixels. But
|
|
// Skia, at least on macOS, will fail to create a surface.
|
|
// Even if there is ample free memory, Skia/Raster will fail.
|
|
// The second problem is that even if you disable Skia, the
|
|
// crash is just delayed when a BitmapEx is created from the
|
|
// VirtualDevice and malloc() fails.
|
|
// Since this data flavor really triggers one or more system
|
|
// memory limits, lower the resolution of the bitmap by keeping
|
|
// the VirtualDevice pixel size within an arbitrary number of
|
|
// pixels.
|
|
// Note: the arbitrary "maximum number of pixels" limit that
|
|
// that Skia can handle may need to be raised or lowered for
|
|
// platforms other than macOS.
|
|
static constexpr tools::Long nCopyToImageMaxPixels = 8192 * 8192;
|
|
Fraction aScale(1.0);
|
|
Size aPixelSize = pVirtDev->LogicToPixel(aMMRect.GetSize(), MapMode(MapUnit::Map100thMM));
|
|
tools::Long nPixels(aPixelSize.Width() * aPixelSize.Height());
|
|
if (nPixels < 0 || nPixels > nCopyToImageMaxPixels)
|
|
{
|
|
aScale = Fraction(nCopyToImageMaxPixels, nPixels);
|
|
aPixelSize = pVirtDev->LogicToPixel(aMMRect.GetSize(), MapMode(MapUnit::Map100thMM, Point(), aScale, aScale));
|
|
nPixels = aPixelSize.Width() * aPixelSize.Height();
|
|
}
|
|
|
|
pVirtDev->SetOutputSizePixel(aPixelSize);
|
|
|
|
PaintToDev( pVirtDev, *m_pDoc, 1.0, aReducedBlock );
|
|
|
|
pVirtDev->SetMapMode( MapMode( MapUnit::MapPixel, Point(), aScale, aScale ) );
|
|
BitmapEx aBmp = pVirtDev->GetBitmapEx( Point(), pVirtDev->GetOutputSize() );
|
|
bOK = SetBitmapEx( aBmp, rFlavor );
|
|
}
|
|
else if ( nFormat == SotClipboardFormatId::GDIMETAFILE )
|
|
{
|
|
// #i123405# Do not limit visual size calculation for metafile creation.
|
|
// It seems unlikely that removing the limitation causes problems since
|
|
// metafile creation means that no real pixel device in the needed size is
|
|
// created.
|
|
InitDocShell(false);
|
|
|
|
SfxObjectShell* pEmbObj = m_aDocShellRef.get();
|
|
|
|
// like SvEmbeddedTransfer::GetData:
|
|
GDIMetaFile aMtf;
|
|
ScopedVclPtrInstance< VirtualDevice > pVDev;
|
|
MapMode aMapMode( pEmbObj->GetMapUnit() );
|
|
tools::Rectangle aVisArea( pEmbObj->GetVisArea( ASPECT_CONTENT ) );
|
|
|
|
pVDev->EnableOutput( false );
|
|
pVDev->SetMapMode( aMapMode );
|
|
aMtf.SetPrefSize( aVisArea.GetSize() );
|
|
aMtf.SetPrefMapMode( aMapMode );
|
|
aMtf.Record( pVDev );
|
|
|
|
pEmbObj->DoDraw( pVDev, Point(), aVisArea.GetSize(), JobSetup() );
|
|
|
|
aMtf.Stop();
|
|
aMtf.WindStart();
|
|
|
|
bOK = SetGDIMetaFile( aMtf );
|
|
}
|
|
else if ( nFormat == SotClipboardFormatId::EMBED_SOURCE )
|
|
{
|
|
//TODO/LATER: differentiate between formats?!
|
|
// #i123405# Do limit visual size calculation to PageSize
|
|
InitDocShell(true); // set aDocShellRef
|
|
|
|
SfxObjectShell* pEmbObj = m_aDocShellRef.get();
|
|
bOK = SetObject( pEmbObj, SCTRANS_TYPE_EMBOBJ, rFlavor );
|
|
}
|
|
}
|
|
return bOK;
|
|
}
|
|
|
|
bool ScTransferObj::WriteObject( SvStream& rOStm, void* pUserObject, sal_uInt32 nUserObjectId,
|
|
const datatransfer::DataFlavor& rFlavor )
|
|
{
|
|
// called from SetObject, put data into stream
|
|
|
|
bool bRet = false;
|
|
switch (nUserObjectId)
|
|
{
|
|
case SCTRANS_TYPE_IMPEX:
|
|
{
|
|
ScImportExport* pImpEx = static_cast<ScImportExport*>(pUserObject);
|
|
|
|
SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
|
|
// mba: no BaseURL for data exchange
|
|
if ( pImpEx->ExportStream( rOStm, OUString(), nFormat ) )
|
|
bRet = ( rOStm.GetError() == ERRCODE_NONE );
|
|
}
|
|
break;
|
|
|
|
case SCTRANS_TYPE_EDIT_RTF:
|
|
case SCTRANS_TYPE_EDIT_BIN:
|
|
{
|
|
ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject);
|
|
if ( nUserObjectId == SCTRANS_TYPE_EDIT_RTF )
|
|
{
|
|
pEngine->Write( rOStm, EETextFormat::Rtf );
|
|
bRet = ( rOStm.GetError() == ERRCODE_NONE );
|
|
}
|
|
else
|
|
{
|
|
// can't use Write for EditEngine format because that would
|
|
// write old format without support for unicode characters.
|
|
// Get the data from the EditEngine's transferable instead.
|
|
|
|
sal_Int32 nParCnt = pEngine->GetParagraphCount();
|
|
if ( nParCnt == 0 )
|
|
nParCnt = 1;
|
|
ESelection aSel( 0, 0, nParCnt-1, pEngine->GetTextLen(nParCnt-1) );
|
|
|
|
uno::Reference<datatransfer::XTransferable> xEditTrans = pEngine->CreateTransferable( aSel );
|
|
TransferableDataHelper aEditHelper( xEditTrans );
|
|
|
|
std::unique_ptr<SvStream> xStrm = aEditHelper.GetSotStorageStream( rFlavor );
|
|
bRet = bool(xStrm);
|
|
if (bRet)
|
|
rOStm.WriteStream(*xStrm);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SCTRANS_TYPE_EDIT_ODF_TEXT_FLAT:
|
|
{
|
|
ScTabEditEngine* pEngine = static_cast<ScTabEditEngine*>(pUserObject);
|
|
pEngine->Write(rOStm, EETextFormat::Xml);
|
|
bRet = (rOStm.GetError() == ERRCODE_NONE);
|
|
}
|
|
break;
|
|
|
|
case SCTRANS_TYPE_EMBOBJ:
|
|
{
|
|
// TODO/MBA: testing
|
|
SfxObjectShell* pEmbObj = static_cast<SfxObjectShell*>(pUserObject);
|
|
::utl::TempFileFast aTempFile;
|
|
SvStream* pTempStream = aTempFile.GetStream(StreamMode::READWRITE);
|
|
uno::Reference< embed::XStorage > xWorkStore =
|
|
::comphelper::OStorageHelper::GetStorageFromStream( new utl::OStreamWrapper(*pTempStream) );
|
|
|
|
// write document storage
|
|
pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false );
|
|
|
|
// mba: no relative URLs for clipboard!
|
|
SfxMedium aMedium( xWorkStore, OUString() );
|
|
pEmbObj->DoSaveObjectAs( aMedium, false );
|
|
pEmbObj->DoSaveCompleted();
|
|
|
|
uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY );
|
|
if ( xTransact.is() )
|
|
xTransact->commit();
|
|
|
|
rOStm.SetBufferSize( 0xff00 );
|
|
rOStm.WriteStream( *pTempStream );
|
|
|
|
bRet = true;
|
|
|
|
xWorkStore->dispose();
|
|
xWorkStore.clear();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
OSL_FAIL("unknown object id");
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
sal_Bool SAL_CALL ScTransferObj::isComplex()
|
|
{
|
|
ScRange aReduced = lcl_reduceBlock(*m_pDoc, m_aBlock);
|
|
size_t nCells = (aReduced.aEnd.Col() - aReduced.aStart.Col() + 1) *
|
|
(aReduced.aEnd.Row() - aReduced.aStart.Row() + 1) *
|
|
(aReduced.aEnd.Tab() - aReduced.aStart.Tab() + 1);
|
|
return nCells > 1000;
|
|
}
|
|
|
|
void ScTransferObj::DragFinished( sal_Int8 nDropAction )
|
|
{
|
|
if ( nDropAction == DND_ACTION_MOVE && !m_bDragWasInternal && !(m_nDragSourceFlags & ScDragSrc::Navigator) )
|
|
{
|
|
// move: delete source data
|
|
ScDocShell* pSourceSh = GetSourceDocShell();
|
|
if (pSourceSh)
|
|
{
|
|
ScMarkData aMarkData = GetSourceMarkData();
|
|
// external drag&drop doesn't copy objects, so they also aren't deleted:
|
|
// bApi=TRUE, don't show error messages from drag&drop
|
|
pSourceSh->GetDocFunc().DeleteContents( aMarkData, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, true, true );
|
|
}
|
|
}
|
|
|
|
ScModule* pScMod = ScModule::get();
|
|
if ( pScMod && pScMod->GetDragData().pCellTransfer == this )
|
|
pScMod->ResetDragObject();
|
|
|
|
m_xDragSourceRanges = nullptr; // don't keep source after dropping
|
|
|
|
TransferDataContainer::DragFinished( nDropAction );
|
|
}
|
|
|
|
void ScTransferObj::SetDragHandlePos( SCCOL nX, SCROW nY )
|
|
{
|
|
m_nDragHandleX = nX;
|
|
m_nDragHandleY = nY;
|
|
}
|
|
|
|
void ScTransferObj::SetSourceCursorPos( SCCOL nX, SCROW nY )
|
|
{
|
|
m_nSourceCursorX = nX;
|
|
m_nSourceCursorY = nY;
|
|
}
|
|
|
|
bool ScTransferObj::WasSourceCursorInSelection() const
|
|
{
|
|
return
|
|
m_nSourceCursorX >= m_aBlock.aStart.Col() && m_nSourceCursorX <= m_aBlock.aEnd.Col() &&
|
|
m_nSourceCursorY >= m_aBlock.aStart.Row() && m_nSourceCursorY <= m_aBlock.aEnd.Row();
|
|
}
|
|
|
|
void ScTransferObj::SetVisibleTab( SCTAB nNew )
|
|
{
|
|
m_nVisibleTab = nNew;
|
|
}
|
|
|
|
void ScTransferObj::SetDrawPersist( const SfxObjectShellRef& rRef )
|
|
{
|
|
m_aDrawPersistRef = rRef;
|
|
}
|
|
|
|
void ScTransferObj::SetDragSource( ScDocShell* pSourceShell, const ScMarkData& rMark )
|
|
{
|
|
ScRangeList aRanges;
|
|
rMark.FillRangeListWithMarks( &aRanges, false );
|
|
m_xDragSourceRanges = new ScCellRangesObj( pSourceShell, aRanges );
|
|
}
|
|
|
|
void ScTransferObj::SetDragSourceFlags(ScDragSrc nFlags)
|
|
{
|
|
m_nDragSourceFlags = nFlags;
|
|
}
|
|
|
|
void ScTransferObj::SetDragWasInternal()
|
|
{
|
|
m_bDragWasInternal = true;
|
|
}
|
|
|
|
void ScTransferObj::SetUseInApi( bool bSet )
|
|
{
|
|
m_bUseInApi = bSet;
|
|
}
|
|
|
|
ScDocument* ScTransferObj::GetSourceDocument()
|
|
{
|
|
ScDocShell* pSourceDocSh = GetSourceDocShell();
|
|
if (pSourceDocSh)
|
|
return &pSourceDocSh->GetDocument();
|
|
return nullptr;
|
|
}
|
|
|
|
ScDocShell* ScTransferObj::GetSourceDocShell()
|
|
{
|
|
if (m_xDragSourceRanges)
|
|
return m_xDragSourceRanges->GetDocShell();
|
|
|
|
return nullptr; // none set
|
|
}
|
|
|
|
ScMarkData ScTransferObj::GetSourceMarkData() const
|
|
{
|
|
ScMarkData aMarkData(m_pDoc->GetSheetLimits());
|
|
if (m_xDragSourceRanges)
|
|
{
|
|
const ScRangeList& rRanges = m_xDragSourceRanges->GetRangeList();
|
|
aMarkData.MarkFromRangeList( rRanges, false );
|
|
}
|
|
return aMarkData;
|
|
}
|
|
|
|
// initialize aDocShellRef with a live document from the ClipDoc
|
|
|
|
// #i123405# added parameter to allow size calculation without limitation
|
|
// to PageSize, e.g. used for Metafile creation for clipboard.
|
|
|
|
void ScTransferObj::InitDocShell(bool bLimitToPageSize)
|
|
{
|
|
if ( m_aDocShellRef.is() )
|
|
return;
|
|
|
|
m_aDocShellRef = new ScDocShell; // ref must be there before InitNew
|
|
|
|
m_aDocShellRef->DoInitNew();
|
|
|
|
ScDocument& rDestDoc = m_aDocShellRef->GetDocument();
|
|
ScMarkData aDestMark(rDestDoc.GetSheetLimits());
|
|
aDestMark.SelectTable( 0, true );
|
|
|
|
rDestDoc.SetDocOptions( m_pDoc->GetDocOptions() ); // #i42666#
|
|
|
|
OUString aTabName;
|
|
m_pDoc->GetName( m_aBlock.aStart.Tab(), aTabName );
|
|
rDestDoc.RenameTab( 0, aTabName );
|
|
|
|
m_aDocShellRef->MakeDrawLayer();
|
|
|
|
rDestDoc.CopyStdStylesFrom(*m_pDoc);
|
|
|
|
SCCOL nStartX = m_aBlock.aStart.Col();
|
|
SCROW nStartY = m_aBlock.aStart.Row();
|
|
SCCOL nEndX = m_aBlock.aEnd.Col();
|
|
SCROW nEndY = m_aBlock.aEnd.Row();
|
|
|
|
// widths / heights
|
|
// (must be copied before CopyFromClip, for drawing objects)
|
|
|
|
SCCOL nCol;
|
|
SCTAB nSrcTab = m_aBlock.aStart.Tab();
|
|
rDestDoc.SetLayoutRTL(0, m_pDoc->IsLayoutRTL(nSrcTab));
|
|
for (nCol=nStartX; nCol<=nEndX; nCol++)
|
|
if ( m_pDoc->ColHidden(nCol, nSrcTab) )
|
|
rDestDoc.ShowCol( nCol, 0, false );
|
|
else
|
|
rDestDoc.SetColWidth( nCol, 0, m_pDoc->GetColWidth( nCol, nSrcTab ) );
|
|
|
|
if (nStartY > 0)
|
|
{
|
|
// Set manual height for all previous rows so we can ensure
|
|
// that visible area will not change due to autoheight
|
|
rDestDoc.SetManualHeight(0, nStartY - 1, 0, true);
|
|
}
|
|
for (SCROW nRow = nStartY; nRow <= nEndY; ++nRow)
|
|
{
|
|
if ( m_pDoc->RowHidden(nRow, nSrcTab) )
|
|
rDestDoc.ShowRow( nRow, 0, false );
|
|
else
|
|
{
|
|
rDestDoc.SetRowHeight( nRow, 0, m_pDoc->GetOriginalHeight( nRow, nSrcTab ) );
|
|
|
|
// if height was set manually, that flag has to be copied, too
|
|
bool bManual = m_pDoc->IsManualRowHeight(nRow, nSrcTab);
|
|
rDestDoc.SetManualHeight(nRow, nRow, 0, bManual);
|
|
}
|
|
}
|
|
|
|
// cell range is copied to the original position, but on the first sheet
|
|
// -> bCutMode must be set
|
|
// pDoc is always a Clipboard-document
|
|
|
|
ScRange aDestRange( nStartX,nStartY,0, nEndX,nEndY,0 );
|
|
bool bWasCut = m_pDoc->IsCutMode();
|
|
if (!bWasCut)
|
|
m_pDoc->SetClipArea( aDestRange, true ); // Cut
|
|
rDestDoc.CopyFromClip( aDestRange, aDestMark, InsertDeleteFlags::ALL, nullptr, m_pDoc.get(), false );
|
|
m_pDoc->SetClipArea( aDestRange, bWasCut );
|
|
|
|
StripRefs(*m_pDoc, nStartX,nStartY, nEndX,nEndY, rDestDoc);
|
|
|
|
ScRange aMergeRange = aDestRange;
|
|
rDestDoc.ExtendMerge( aMergeRange, true );
|
|
|
|
m_pDoc->CopyDdeLinks( rDestDoc ); // copy values of DDE Links
|
|
|
|
// page format (grid etc) and page size (maximum size for ole object)
|
|
|
|
Size aPaperSize = SvxPaperInfo::GetPaperSize( PAPER_A4 ); // Twips
|
|
ScStyleSheetPool* pStylePool = m_pDoc->GetStyleSheetPool();
|
|
OUString aStyleName = m_pDoc->GetPageStyle( m_aBlock.aStart.Tab() );
|
|
SfxStyleSheetBase* pStyleSheet = pStylePool->Find( aStyleName, SfxStyleFamily::Page );
|
|
if (pStyleSheet)
|
|
{
|
|
const SfxItemSet& rSourceSet = pStyleSheet->GetItemSet();
|
|
aPaperSize = rSourceSet.Get(ATTR_PAGE_SIZE).GetSize();
|
|
|
|
// CopyStyleFrom copies SetItems with correct pool
|
|
ScStyleSheetPool* pDestPool = rDestDoc.GetStyleSheetPool();
|
|
pDestPool->CopyStyleFrom( pStylePool, aStyleName, SfxStyleFamily::Page );
|
|
}
|
|
|
|
ScViewData aViewData(*m_aDocShellRef, nullptr);
|
|
aViewData.SetScreen( nStartX,nStartY, nEndX,nEndY );
|
|
aViewData.SetCurX( nStartX );
|
|
aViewData.SetCurY( nStartY );
|
|
|
|
rDestDoc.SetViewOptions( m_pDoc->GetViewOptions() );
|
|
|
|
// Size
|
|
//! get while copying sizes
|
|
|
|
tools::Long nPosX = 0;
|
|
tools::Long nPosY = 0;
|
|
|
|
for (nCol=0; nCol<nStartX; nCol++)
|
|
nPosX += rDestDoc.GetColWidth( nCol, 0 );
|
|
nPosY += rDestDoc.GetRowHeight( 0, nStartY-1, 0 );
|
|
nPosX = o3tl::convert(nPosX, o3tl::Length::twip, o3tl::Length::mm100);
|
|
nPosY = o3tl::convert(nPosY, o3tl::Length::twip, o3tl::Length::mm100);
|
|
|
|
aPaperSize.setWidth( aPaperSize.Width() * 2 ); // limit OLE object to double of page size
|
|
aPaperSize.setHeight( aPaperSize.Height() * 2 );
|
|
|
|
tools::Long nSizeX = 0;
|
|
tools::Long nSizeY = 0;
|
|
for (nCol=nStartX; nCol<=nEndX; nCol++)
|
|
{
|
|
tools::Long nAdd = rDestDoc.GetColWidth( nCol, 0 );
|
|
if ( bLimitToPageSize && nSizeX+nAdd > aPaperSize.Width() && nSizeX ) // above limit?
|
|
break;
|
|
nSizeX += nAdd;
|
|
}
|
|
for (SCROW nRow=nStartY; nRow<=nEndY; nRow++)
|
|
{
|
|
tools::Long nAdd = rDestDoc.GetRowHeight( nRow, 0 );
|
|
if ( bLimitToPageSize && nSizeY+nAdd > aPaperSize.Height() && nSizeY ) // above limit?
|
|
break;
|
|
nSizeY += nAdd;
|
|
}
|
|
nSizeX = o3tl::convert(nSizeX, o3tl::Length::twip, o3tl::Length::mm100);
|
|
nSizeY = o3tl::convert(nSizeY, o3tl::Length::twip, o3tl::Length::mm100);
|
|
|
|
// m_aDocShellRef->SetVisAreaSize( Size(nSizeX,nSizeY) );
|
|
|
|
tools::Rectangle aNewArea( Point(nPosX,nPosY), Size(nSizeX,nSizeY) );
|
|
//TODO/LATER: why twice?!
|
|
//m_aDocShellRef->SvInPlaceObject::SetVisArea( aNewArea );
|
|
m_aDocShellRef->SetVisArea(aNewArea);
|
|
|
|
m_aDocShellRef->UpdateOle(aViewData, true);
|
|
|
|
//! SetDocumentModified?
|
|
if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() )
|
|
rDestDoc.UpdateChartListenerCollection();
|
|
}
|
|
|
|
SfxObjectShell* ScTransferObj::SetDrawClipDoc( bool bAnyOle, const std::shared_ptr<ScDocument>& pDoc )
|
|
{
|
|
// update ScGlobal::xDrawClipDocShellRef
|
|
|
|
ScGlobal::xDrawClipDocShellRef.clear();
|
|
if (bAnyOle)
|
|
{
|
|
ScGlobal::xDrawClipDocShellRef = new ScDocShell(SfxModelFlags::EMBEDDED_OBJECT | SfxModelFlags::DISABLE_EMBEDDED_SCRIPTS, pDoc); // there must be a ref
|
|
ScGlobal::xDrawClipDocShellRef->DoInitNew();
|
|
}
|
|
|
|
return ScGlobal::xDrawClipDocShellRef.get();
|
|
}
|
|
|
|
void ScTransferObj::StripRefs( ScDocument& rDoc,
|
|
SCCOL nStartX, SCROW nStartY, SCCOL nEndX, SCROW nEndY,
|
|
ScDocument& rDestDoc )
|
|
{
|
|
// In a clipboard doc the data don't have to be on the first sheet
|
|
|
|
SCTAB nSrcTab = 0;
|
|
while (nSrcTab < rDoc.GetTableCount() && !rDoc.HasTable(nSrcTab))
|
|
++nSrcTab;
|
|
SCTAB nDestTab = 0;
|
|
while (nDestTab < rDestDoc.GetTableCount() && !rDestDoc.HasTable(nDestTab))
|
|
++nDestTab;
|
|
|
|
if (!rDoc.HasTable(nSrcTab) || !rDestDoc.HasTable(nDestTab))
|
|
{
|
|
OSL_FAIL("Sheet not found in ScTransferObj::StripRefs");
|
|
return;
|
|
}
|
|
|
|
ScRange aRef;
|
|
|
|
ScCellIterator aIter( rDoc, ScRange(nStartX, nStartY, nSrcTab, nEndX, nEndY, nSrcTab) );
|
|
for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
|
|
{
|
|
if (aIter.getType() != CELLTYPE_FORMULA)
|
|
continue;
|
|
|
|
ScFormulaCell* pFCell = aIter.getFormulaCell();
|
|
bool bOut = false;
|
|
ScDetectiveRefIter aRefIter( rDoc, pFCell );
|
|
while ( !bOut && aRefIter.GetNextRef( aRef ) )
|
|
{
|
|
if ( aRef.aStart.Tab() != nSrcTab || aRef.aEnd.Tab() != nSrcTab ||
|
|
aRef.aStart.Col() < nStartX || aRef.aEnd.Col() > nEndX ||
|
|
aRef.aStart.Row() < nStartY || aRef.aEnd.Row() > nEndY )
|
|
bOut = true;
|
|
}
|
|
if (bOut)
|
|
{
|
|
SCCOL nCol = aIter.GetPos().Col();
|
|
SCROW nRow = aIter.GetPos().Row();
|
|
|
|
FormulaError nErrCode = pFCell->GetErrCode();
|
|
ScAddress aPos(nCol, nRow, nDestTab);
|
|
if (nErrCode != FormulaError::NONE)
|
|
{
|
|
if ( rDestDoc.GetAttr( nCol,nRow,nDestTab, ATTR_HOR_JUSTIFY)->GetValue() ==
|
|
SvxCellHorJustify::Standard )
|
|
rDestDoc.ApplyAttr( nCol,nRow,nDestTab,
|
|
SvxHorJustifyItem(SvxCellHorJustify::Right, ATTR_HOR_JUSTIFY) );
|
|
|
|
ScSetStringParam aParam;
|
|
aParam.setTextInput();
|
|
rDestDoc.SetString(aPos, ScGlobal::GetErrorString(nErrCode), &aParam);
|
|
}
|
|
else if (pFCell->IsValue())
|
|
{
|
|
rDestDoc.SetValue(aPos, pFCell->GetValue());
|
|
}
|
|
else
|
|
{
|
|
OUString aStr = pFCell->GetString().getString();
|
|
if ( pFCell->IsMultilineResult() )
|
|
{
|
|
ScFieldEditEngine& rEngine = rDestDoc.GetEditEngine();
|
|
rEngine.SetTextCurrentDefaults(aStr);
|
|
rDestDoc.SetEditText(ScAddress(nCol,nRow,nDestTab), rEngine.CreateTextObject());
|
|
}
|
|
else
|
|
{
|
|
ScSetStringParam aParam;
|
|
aParam.setTextInput();
|
|
rDestDoc.SetString(aPos, aStr, &aParam);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|