/* -*- 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 .
*/
/// Tracks the boundaries of pasted content and notifies listeners. class SwPasteContext
{ public:
SwPasteContext(SwWrtShell& rWrtShell);
~SwPasteContext();
// 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<SwDocShell*>(pObj);
pDocSh->DoClose();
}
m_aDocShellRef.Clear();
if (SwModule* pMod = SwModule::get())
{ if ( pMod->m_pDragDrop == this )
pMod->m_pDragDrop = nullptr; elseif ( pMod->m_pXSelection == this )
pMod->m_pXSelection = nullptr;
}
void SwTransferable::ObjectReleased()
{
SwModule* pMod = SwModule::get(); if (!pMod) return; if( this == pMod->m_pDragDrop )
pMod->m_pDragDrop = nullptr; elseif( this == pMod->m_pXSelection )
pMod->m_pXSelection = nullptr;
}
void SwTransferable::AddSupportedFormats()
{ // only need if we are the current XSelection Object if (this == SwModule::get()->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() );
}
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, bool bDeleteRedlines = true)
{ const SwDoc &rSrc = *rSrcWrtShell.GetDoc();
//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);
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)
{ returntrue; // Complex
}
}
}
FrameClientSortList_t vFrames;
::CollectFrameAtNode(rNd, vFrames, true); if (!vFrames.empty())
{ // There is an at-char anchored object to this node, that's complex. returntrue;
}
// 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 ) ) returnfalse;
// 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 constbool bPending(m_pWrtShell->ActionPend());
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 );
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;
switch( nObjectType )
{ case SWTRANSFER_OBJECTTYPE_DRAWMODEL:
{ // don't change the sequence of commands
SdrModel *pModel = static_cast<SdrModel*>(pObject);
rOStream.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.GetUserOrPoolDefaultItem(EE_CHAR_FONTHEIGHT);
// SW should have no MasterPages
OSL_ENSURE(0 == pModel->GetMasterPageCount(), "SW with MasterPages (!)");
case SWTRANSFER_OBJECTTYPE_HTML:
{ // LOK is interested in getting images embedded for copy/paste support.
GetHTMLWriter( comphelper::LibreOfficeKit::isActive() ? u"EmbedImages;NoPrettyPrint"_ustr : OUString(), OUString(), xWrt ); break;
}
case SWTRANSFER_OBJECTTYPE_RTF: case SWTRANSFER_OBJECTTYPE_RICHTEXT:
GetRTFWriter(std::u16string_view(), OUString(), xWrt); break;
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());
//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);
// 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 );
// --> 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);
// 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, bDeleteRedlines);
DeleteDDEAndReminderMarks(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 );
//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;
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 );
//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;
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;
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;
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'.
// 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> xTransferable( rData.GetXTransferable() );
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(std::u16string_view sText, sal_Int32 nIdx)
{
sal_Int32 nRet = 0; while ( nIdx-- > 0 && sText[nIdx] == '\t' )
{
nRet++;
} return nRet;
}
// 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 );
UIName 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 UIName 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);
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) returnfalse; if (ePasteTable == PasteTableType::PASTE_TABLE)
pDispatch->Execute(FN_PASTE_NESTED_TABLE, SfxCallMode::SYNCHRON); elseif (ePasteTable == PasteTableType::PASTE_ROW)
pDispatch->Execute(FN_TABLE_PASTE_ROW_BEFORE, SfxCallMode::SYNCHRON); elseif (ePasteTable == PasteTableType::PASTE_COLUMN)
pDispatch->Execute(FN_TABLE_PASTE_COL_BEFORE, SfxCallMode::SYNCHRON); else
pDispatch->Execute(SID_PASTE, SfxCallMode::SYNCHRON); returntrue;
} 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 elseif ( (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("<table")) > -1 )
{ // calculate table row/column count by analysing indentation of the HTML table extract
// calculate indentation level of <table>, which is the base of the next calculations // (tdf#148791 table alignment can enlarge it using first level <center>, <div> or <dl>)
sal_Int32 nTableLevel = lcl_getLevel(aExpand, nIdx); // table rows repeated heading use extra indentation, too: // <thead> is always used here, and the first table with <thead> 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("<thead")) == nTableLevel + 1; // calculate count of selected rows or columns
sal_Int32 nSelectedRowsOrCols = 0; const OUString sSearchRowOrCol = bRowMode ? u"</tr>"_ustr : u"<col "_ustr; while((nIdx = aExpand.indexOf(sSearchRowOrCol, nIdx)) > -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 <tr></tr>
!aExpand.match("<tr></tr>", 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::MarkBase* pMark = (!bRowMode || nSelectedRowsOrCols == 0) ? nullptr : rSh.SetBookmark(
vcl::KeyCode(),
SwMarkName(),
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 });
}
// 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;
} elseif( 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);
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;
//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;
}
// 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);
// 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::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::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()))
{
aWait.~SwWait(); // end the wait pointer, X11 only annoyance
rSh.NavigatorPaste(aBkmk);
bRet = true;
}
} break;
case SotClipboardFormatId::INET_IMAGE: case SotClipboardFormatId::NETSCAPE_IMAGE:
bRet = SwTransferable::PasteTargetURL( rData, rSh,
SwPasteSdr::Insert,
pPt, true ); 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;
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;
}
}
const SwPosition& rInsertPosition = *rSh.GetCursor()->Start(); if (CanSkipInvalidateNumRules(rInsertPosition))
{ // Insertion point is not a numbering and we paste plain text: then no need to // invalidate all numberings.
bSkipInvalidateNumRules = true;
}
SwAsciiOptions aAOpt;
aAOpt.SetCharSet( RTL_TEXTENCODING_UCS2 );
pRead->GetReaderOpt().SetASCIIOpts( aAOpt ); break;
}
}
[[fallthrough]]; // because then test if we get a stream
// Get the preferred format
SotClipboardFormatId nId; if( rData.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) )
nId = SotClipboardFormatId::EMBEDDED_OBJ; elseif( rData.HasFormat( SotClipboardFormatId::EMBED_SOURCE ) &&
rData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ))
nId = SotClipboardFormatId::EMBED_SOURCE; else
nId = SotClipboardFormatId::NONE;
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 ( 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() );
// 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&)
{
}
}
} elseif (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(
u"URL"_ustr, sFile) };
SwDocShell* pDocSh = rSh.GetDoc()->GetDocShell();
xObj = pDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject(aMedium, aName);
}
}
}
// 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 );
} elseif ( 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 );
}
} elseif( 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!
//!!! 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);
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<weld::MessageDialog> 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<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
VclMessageType::Info, VclButtonsType::Ok,
SwResId(STR_NO_TABLE)));
xBox->run();
}
pDDETyp = nullptr; break;
}
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 );
}
#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<sal_Unicode[]> aBuf(new sal_Unicode[EXTENDED_MAX_PATH]);
DWORD nCopied = GetLongPathNameW(o3tl::toW(sSysPath.getStr()),
o3tl::toW(aBuf.get()), EXTENDED_MAX_PATH); if (nCopied && nCopied < EXTENDED_MAX_PATH)
sText = URIHelper::SmartRel2Abs(INetURLObject(), OUString(aBuf.get()),
Link<OUString*, bool>(), false);
} #endif
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();
}
case SwPasteSdr::Replace:
{ if( rSh.GetSelectedObjCount() )
{ // #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);
}
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<SvxClipboardFormatItem>(0));
FillClipFormatItem( rWrtShell, rData, aClipboardFormatItem); return aClipboardFormatItem.Count() > 0;
}
/** * 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
};
//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 );
} elseif( nSelection & SelectionType::Text && !m_pWrtShell->HasMark() )
{ // is only one field - selected?
SwContentAtPos aContentAtPos( IsAttrAtPos::InetAttr );
Point aPos( SwEditWin::GetDDStartPosX(), SwEditWin::GetDDStartPosY());
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->GetSelectedObjCount()) ) //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();
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 ) returnfalse; // the return value of the SwFEShell::Paste also is bool!
//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, ScrollSizeMode::ScrollSizeDefault);
}
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(' ');
//not in selections or selected frames if( rSh.TestCurrPam( rDragPt ) ||
( rSh.IsSelFrameMode() && rSh.IsInsideSelectedObj( rDragPt )) ) returnfalse;
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);
// 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"); returnfalse;
} 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;
}
}
// 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 );
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;
// 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;
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);
// 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();
}
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 ); elseif ( 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();
}
}
IDocumentMarkAccess* const pMarkAccess = m_pDocShell->GetDoc()->getIDocumentMarkAccess(); auto ppMark = pMarkAccess->findMark(SwMarkName(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::MarkBase* const pMark = *ppMark;
::sfx2::SvLinkSource* p = m_xRefObj.get();
SwServerObject& rServerObject = dynamic_cast<SwServerObject&>(*p);
// collecting state of old mark
SwPaM aPaM(pMark->GetMarkStart());
*aPaM.GetPoint() = pMark->GetMarkStart(); if(pMark->IsExpanded())
{
aPaM.SetMark();
*aPaM.GetMark() = pMark->GetMarkEnd();
}
SwMarkName 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);
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<SwDocShell> ); while( pTmpSh )
{ if( pTmpSh == m_pDocShell ) // that's what we want to have
{ if( m_pDocShell->GetDoc() ) returntrue; break; // the Doc is not there anymore, so leave!
}
pTmpSh = SfxObjectShell::GetNext( *pTmpSh, checkSfxObjectShell<SwDocShell> );
}
m_pDocShell = nullptr; returnfalse;
}
void SwTransferDdeLink::Closed()
{ if( !m_bInDisconnect && m_xRefObj.is() )
{
m_xRefObj->RemoveAllDataAdvise( this );
m_xRefObj->RemoveConnectAdvise( this );
m_xRefObj.clear();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.