Files
libreoffice/sw/source/core/unocore/unoobj2.cxx
Michael Stahl 421a23bb36 tdf#112679 sw: fix copying of fieldmarks
Aha, now we know that the reason for the defensive programming
in lcl_AssureFieldMarksSet() was that there are actually 2
different use-cases for it: usually a new mark is inserted,
so there are no dummy characters and they must be inserted.

However when copying text, the dummy characters are copied too,
so they must not be inserted, or we get duplicate fieldmarks.

This also reverts commit d4036d3a89
which fixed the problem only for CHECKBOX_FIELDMARK in a
different way.

(regression from bb069fe7b8)

Change-Id: I3c99b8c6d720951655198e682018794337859373
2017-09-27 16:37:46 +02:00

1737 lines
54 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 <sal/config.h>
#include <utility>
#include <rtl/ustrbuf.hxx>
#include <swtypes.hxx>
#include <hintids.hxx>
#include <cmdid.h>
#include <hints.hxx>
#include <IMark.hxx>
#include <bookmrk.hxx>
#include <frmfmt.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <textboxhelper.hxx>
#include <ndtxt.hxx>
#include <ndnotxt.hxx>
#include <unocrsr.hxx>
#include <swundo.hxx>
#include <rootfrm.hxx>
#include <flyfrm.hxx>
#include <ftnidx.hxx>
#include <sfx2/linkmgr.hxx>
#include <docary.hxx>
#include <paratr.hxx>
#include <pam.hxx>
#include <shellio.hxx>
#include <swerror.h>
#include <swtblfmt.hxx>
#include <docsh.hxx>
#include <docstyle.hxx>
#include <charfmt.hxx>
#include <txtfld.hxx>
#include <fmtfld.hxx>
#include <fmtpdsc.hxx>
#include <pagedesc.hxx>
#include <strings.hrc>
#include <poolfmt.hxx>
#include <edimp.hxx>
#include <fchrfmt.hxx>
#include <cntfrm.hxx>
#include <pagefrm.hxx>
#include <doctxm.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/fcontnr.hxx>
#include <fmtrfmrk.hxx>
#include <txtrfmrk.hxx>
#include <unoparaframeenum.hxx>
#include <unofootnote.hxx>
#include <unotextbodyhf.hxx>
#include <unotextrange.hxx>
#include <unoparagraph.hxx>
#include <unomap.hxx>
#include <unoport.hxx>
#include <unocrsrhelper.hxx>
#include <unosett.hxx>
#include <unoprnms.hxx>
#include <unotbl.hxx>
#include <unodraw.hxx>
#include <unocoll.hxx>
#include <unostyle.hxx>
#include <fmtanchr.hxx>
#include <editeng/flstitem.hxx>
#include <editeng/unolingu.hxx>
#include <svtools/ctrltool.hxx>
#include <flypos.hxx>
#include <txtftn.hxx>
#include <fmtftn.hxx>
#include <fmtcntnt.hxx>
#include <com/sun/star/text/WrapTextMode.hpp>
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/style/PageStyleLayout.hpp>
#include <com/sun/star/text/XTextDocument.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <unoframe.hxx>
#include <fmthdft.hxx>
#include <vcl/svapp.hxx>
#include <fmtflcnt.hxx>
#include <editeng/brushitem.hxx>
#include <fmtclds.hxx>
#include <dcontact.hxx>
#include <dflyobj.hxx>
#include <vector>
#include <sortedobjs.hxx>
#include <sortopt.hxx>
#include <algorithm>
#include <iterator>
#include <calbck.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
using namespace ::com::sun::star;
namespace sw {
uno::Sequence< OUString >
GetSupportedServiceNamesImpl(
size_t const nServices, char const*const pServices[])
{
uno::Sequence< OUString > ret(nServices);
for (size_t i = 0; i < nServices; ++i)
{
ret[i] = OUString::createFromAscii(pServices[i]);
}
return ret;
}
} // namespace sw
namespace sw {
void DeepCopyPaM(SwPaM const & rSource, SwPaM & rTarget)
{
rTarget = rSource;
if (rSource.GetNext() != &rSource)
{
SwPaM *pPam = const_cast<SwPaM*>(rSource.GetNext());
do
{
// create new PaM
SwPaM *const pNew = new SwPaM(*pPam, nullptr);
// insert into ring
pNew->MoveTo(&rTarget);
pPam = pPam->GetNext();
}
while (pPam != &rSource);
}
}
} // namespace sw
struct FrameClientSortListLess
{
bool operator() (FrameClientSortListEntry const& r1,
FrameClientSortListEntry const& r2) const
{
return (r1.nIndex < r2.nIndex)
|| ((r1.nIndex == r2.nIndex) && (r1.nOrder < r2.nOrder));
}
};
namespace
{
void lcl_CollectFrameAtNodeWithLayout(const SwContentFrame* pCFrame,
FrameClientSortList_t& rFrames,
const RndStdIds nAnchorType)
{
auto pObjs = pCFrame->GetDrawObjs();
if(!pObjs)
return;
for(const auto pAnchoredObj : *pObjs)
{
SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
// Filter out textboxes, which are not interesting at an UNO level.
if(SwTextBoxHelper::isTextBox(&rFormat, RES_FLYFRMFMT))
continue;
if(rFormat.GetAnchor().GetAnchorId() == nAnchorType)
{
const auto nIdx =
rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex();
const auto nOrder = rFormat.GetAnchor().GetOrder();
FrameClientSortListEntry entry(nIdx, nOrder, new sw::FrameClient(&rFormat));
rFrames.push_back(entry);
}
}
}
}
void CollectFrameAtNode( const SwNodeIndex& rIdx,
FrameClientSortList_t& rFrames,
const bool bAtCharAnchoredObjs )
{
// _bAtCharAnchoredObjs:
// <true>: at-character anchored objects are collected
// <false>: at-paragraph anchored objects are collected
// search all borders, images, and OLEs that are connected to the paragraph
SwDoc* pDoc = rIdx.GetNode().GetDoc();
const auto nChkType = bAtCharAnchoredObjs ? RndStdIds::FLY_AT_CHAR : RndStdIds::FLY_AT_PARA;
const SwContentFrame* pCFrame;
const SwContentNode* pCNd;
if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() &&
nullptr != (pCNd = rIdx.GetNode().GetContentNode()) &&
nullptr != (pCFrame = pCNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout())) )
{
lcl_CollectFrameAtNodeWithLayout(pCFrame, rFrames, nChkType);
}
else
{
const SwFrameFormats& rFormats = *pDoc->GetSpzFrameFormats();
const size_t nSize = rFormats.size();
for ( size_t i = 0; i < nSize; i++)
{
const SwFrameFormat* pFormat = rFormats[ i ];
const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
const SwPosition* pAnchorPos;
if( rAnchor.GetAnchorId() == nChkType &&
nullptr != (pAnchorPos = rAnchor.GetContentAnchor()) &&
pAnchorPos->nNode == rIdx )
{
// OD 2004-05-07 #i28701# - determine insert position for
// sorted <rFrameArr>
const sal_Int32 nIndex = pAnchorPos->nContent.GetIndex();
sal_uInt32 nOrder = rAnchor.GetOrder();
FrameClientSortListEntry entry(nIndex, nOrder, new sw::FrameClient(const_cast<SwFrameFormat*>(pFormat)));
rFrames.push_back(entry);
}
}
std::sort(rFrames.begin(), rFrames.end(), FrameClientSortListLess());
}
}
UnoActionContext::UnoActionContext(SwDoc *const pDoc)
: m_pDoc(pDoc)
{
SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
if (pRootFrame)
{
pRootFrame->StartAllAction();
}
}
UnoActionContext::~UnoActionContext() COVERITY_NOEXCEPT_FALSE
{
// Doc may already have been removed here
if (m_pDoc)
{
SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
if (pRootFrame)
{
pRootFrame->EndAllAction();
}
}
}
static void lcl_RemoveImpl(SwDoc *const pDoc)
{
assert(pDoc);
SwRootFrame *const pRootFrame = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
if (pRootFrame)
{
pRootFrame->UnoRemoveAllActions();
}
}
UnoActionRemoveContext::UnoActionRemoveContext(SwDoc *const pDoc)
: m_pDoc(pDoc)
{
lcl_RemoveImpl(m_pDoc);
}
static SwDoc * lcl_IsNewStyleTable(SwUnoTableCursor const& rCursor)
{
SwTableNode *const pTableNode = rCursor.GetNode().FindTableNode();
return (pTableNode && !pTableNode->GetTable().IsNewModel())
? rCursor.GetDoc()
: nullptr;
}
UnoActionRemoveContext::UnoActionRemoveContext(SwUnoTableCursor const& rCursor)
: m_pDoc(lcl_IsNewStyleTable(rCursor))
{
// this insanity is only necessary for old-style tables
// because SwRootFrame::MakeTableCursors() creates the table cursor for these
if (m_pDoc)
{
lcl_RemoveImpl(m_pDoc);
}
}
UnoActionRemoveContext::~UnoActionRemoveContext()
{
if (m_pDoc)
{
SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
if (pRootFrame)
{
pRootFrame->UnoRestoreAllActions();
}
}
}
void ClientModify(SwClient* pClient, const SfxPoolItem *pOld, const SfxPoolItem *pNew)
{
switch( pOld ? pOld->Which() : 0 )
{
case RES_REMOVE_UNO_OBJECT:
case RES_OBJECTDYING:
if( static_cast<void*>(pClient->GetRegisteredIn()) == static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject )
pClient->GetRegisteredIn()->Remove(pClient);
break;
case RES_FMT_CHG:
// Is the move to the new one finished and will the old one be deleted?
if( static_cast<const SwFormatChg*>(pNew)->pChangedFormat == pClient->GetRegisteredIn() &&
static_cast<const SwFormatChg*>(pOld)->pChangedFormat->IsFormatInDTOR() )
pClient->GetRegisteredIn()->Remove(pClient);
break;
}
}
void SwUnoCursorHelper::SetCursorAttr(SwPaM & rPam,
const SfxItemSet& rSet,
const SetAttrMode nAttrMode, const bool bTableMode)
{
const SetAttrMode nFlags = nAttrMode | SetAttrMode::APICALL;
SwDoc* pDoc = rPam.GetDoc();
//StartEndAction
UnoActionContext aAction(pDoc);
if (rPam.GetNext() != &rPam) // Ring of Cursors
{
pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSATTR, nullptr);
for(SwPaM& rCurrent : rPam.GetRingContainer())
{
if (rCurrent.HasMark() &&
( bTableMode ||
(*rCurrent.GetPoint() != *rCurrent.GetMark()) ))
{
pDoc->getIDocumentContentOperations().InsertItemSet(rCurrent, rSet, nFlags);
}
}
pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr);
}
else
{
pDoc->getIDocumentContentOperations().InsertItemSet( rPam, rSet, nFlags );
}
if( rSet.GetItemState( RES_PARATR_OUTLINELEVEL, false ) >= SfxItemState::DEFAULT )
{
SwTextNode * pTmpNode = rPam.GetNode().GetTextNode();
if ( pTmpNode )
{
rPam.GetDoc()->GetNodes().UpdateOutlineNode( *pTmpNode );
}
}
}
// #i63870#
// split third parameter <bCurrentAttrOnly> into new parameters <bOnlyTextAttr>
// and <bGetFromChrFormat> to get better control about resulting <SfxItemSet>
void SwUnoCursorHelper::GetCursorAttr(SwPaM & rPam,
SfxItemSet & rSet, const bool bOnlyTextAttr, const bool bGetFromChrFormat)
{
static const sal_uLong nMaxLookup = 1000;
SfxItemSet aSet( *rSet.GetPool(), rSet.GetRanges() );
SfxItemSet *pSet = &rSet;
for(SwPaM& rCurrent : rPam.GetRingContainer())
{
SwPosition const & rStart( *rCurrent.Start() );
SwPosition const & rEnd( *rCurrent.End() );
const sal_uLong nSttNd = rStart.nNode.GetIndex();
const sal_uLong nEndNd = rEnd .nNode.GetIndex();
if (nEndNd - nSttNd >= nMaxLookup)
{
rSet.ClearItem();
rSet.InvalidateAllItems();
return;// uno::Any();
}
// the first node inserts the values into the get set
// all other nodes merge their values into the get set
for (sal_uLong n = nSttNd; n <= nEndNd; ++n)
{
SwNode *const pNd = rPam.GetDoc()->GetNodes()[ n ];
switch (pNd->GetNodeType())
{
case SwNodeType::Text:
{
const sal_Int32 nStart = (n == nSttNd)
? rStart.nContent.GetIndex() : 0;
const sal_Int32 nEnd = (n == nEndNd)
? rEnd.nContent.GetIndex()
: pNd->GetTextNode()->GetText().getLength();
pNd->GetTextNode()->GetAttr(*pSet, nStart, nEnd, bOnlyTextAttr, bGetFromChrFormat);
}
break;
case SwNodeType::Grf:
case SwNodeType::Ole:
static_cast<SwContentNode*>(pNd)->GetAttr( *pSet );
break;
default:
continue; // skip this node
}
if (pSet != &rSet)
{
rSet.MergeValues( aSet );
}
else
{
pSet = &aSet;
}
if (aSet.Count())
{
aSet.ClearItem();
}
}
}
}
struct SwXParagraphEnumerationImpl final : public SwXParagraphEnumeration
{
uno::Reference< text::XText > const m_xParentText;
const CursorType m_eCursorType;
/// Start node of the cell _or_ table the enumeration belongs to.
/// Used to restrict the movement of the UNO cursor to the cell and its
/// embedded tables.
SwStartNode const*const m_pOwnStartNode;
SwTable const*const m_pOwnTable;
const sal_uLong m_nEndIndex;
sal_Int32 m_nFirstParaStart;
sal_Int32 m_nLastParaEnd;
bool m_bFirstParagraph;
uno::Reference< text::XTextContent > m_xNextPara;
sw::UnoCursorPointer m_pCursor;
SwXParagraphEnumerationImpl(
uno::Reference< text::XText > const& xParent,
const std::shared_ptr<SwUnoCursor>& pCursor,
const CursorType eType,
SwStartNode const*const pStartNode, SwTable const*const pTable)
: m_xParentText( xParent )
, m_eCursorType( eType )
// remember table and start node for later travelling
// (used in export of tables in tables)
, m_pOwnStartNode( pStartNode )
// for import of tables in tables we have to remember the actual
// table and start node of the current position in the enumeration.
, m_pOwnTable( pTable )
, m_nEndIndex( pCursor->End()->nNode.GetIndex() )
, m_nFirstParaStart( -1 )
, m_nLastParaEnd( -1 )
, m_bFirstParagraph( true )
, m_pCursor(pCursor)
{
OSL_ENSURE(m_xParentText.is(), "SwXParagraphEnumeration: no parent?");
OSL_ENSURE( !((CursorType::SelectionInTable == eType) ||
(CursorType::TableText == eType))
|| (m_pOwnTable && m_pOwnStartNode),
"SwXParagraphEnumeration: table type but no start node or table?");
if ((CursorType::Selection == m_eCursorType) ||
(CursorType::SelectionInTable == m_eCursorType))
{
SwUnoCursor & rCursor = GetCursor();
rCursor.Normalize();
m_nFirstParaStart = rCursor.GetPoint()->nContent.GetIndex();
m_nLastParaEnd = rCursor.GetMark()->nContent.GetIndex();
rCursor.DeleteMark();
}
}
virtual ~SwXParagraphEnumerationImpl() override
{ m_pCursor.reset(nullptr); }
virtual void SAL_CALL release() throw () override
{
SolarMutexGuard g;
OWeakObject::release();
}
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override
{ return OUString("SwXParagraphEnumeration"); }
virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName) override
{ return cppu::supportsService(this, rServiceName); };
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
{ return {"com.sun.star.text.ParagraphEnumeration"}; };
// XEnumeration
virtual sal_Bool SAL_CALL hasMoreElements() override;
virtual css::uno::Any SAL_CALL nextElement() override;
SwUnoCursor& GetCursor()
{ return *m_pCursor; }
/// @throws container::NoSuchElementException
/// @throws lang::WrappedTargetException
/// @throws uno::RuntimeException
uno::Reference< text::XTextContent > NextElement_Impl();
};
SwXParagraphEnumeration* SwXParagraphEnumeration::Create(
uno::Reference< text::XText > const& xParent,
const std::shared_ptr<SwUnoCursor>& pCursor,
const CursorType eType,
SwStartNode const*const pStartNode,
SwTable const*const pTable)
{
return new SwXParagraphEnumerationImpl(xParent, pCursor, eType, pStartNode, pTable);
}
sal_Bool SAL_CALL
SwXParagraphEnumerationImpl::hasMoreElements()
{
SolarMutexGuard aGuard;
return m_bFirstParagraph || m_xNextPara.is();
}
//!! compare to SwShellTableCursor::FillRects() in viscrs.cxx
static SwTableNode *
lcl_FindTopLevelTable(
SwTableNode *const pTableNode, SwTable const*const pOwnTable)
{
// find top-most table in current context (section) level
SwTableNode * pLast = pTableNode;
for (SwTableNode* pTmp = pLast;
pTmp != nullptr && &pTmp->GetTable() != pOwnTable; /* we must not go up higher than the own table! */
pTmp = pTmp->StartOfSectionNode()->FindTableNode() )
{
pLast = pTmp;
}
return pLast;
}
static bool
lcl_CursorIsInSection(
SwUnoCursor const*const pUnoCursor, SwStartNode const*const pOwnStartNode)
{
// returns true if the cursor is in the section (or in a sub section!)
// represented by pOwnStartNode
bool bRes = true;
if (pUnoCursor && pOwnStartNode)
{
const SwEndNode * pOwnEndNode = pOwnStartNode->EndOfSectionNode();
bRes = pOwnStartNode->GetIndex() <= pUnoCursor->Start()->nNode.GetIndex() &&
pUnoCursor->End()->nNode.GetIndex() <= pOwnEndNode->GetIndex();
}
return bRes;
}
uno::Reference< text::XTextContent >
SwXParagraphEnumerationImpl::NextElement_Impl()
{
SwUnoCursor& rUnoCursor = GetCursor();
// check for exceeding selections
if (!m_bFirstParagraph &&
((CursorType::Selection == m_eCursorType) ||
(CursorType::SelectionInTable == m_eCursorType)))
{
SwPosition* pStart = rUnoCursor.Start();
auto aNewCursor(rUnoCursor.GetDoc()->CreateUnoCursor(*pStart));
// one may also go into tables here
if ((CursorType::TableText != m_eCursorType) &&
(CursorType::SelectionInTable != m_eCursorType))
{
aNewCursor->SetRemainInSection( false );
}
// os 2005-01-14: This part is only necessary to detect movements out
// of a selection; if there is no selection we don't have to care
SwTableNode *const pTableNode = aNewCursor->GetNode().FindTableNode();
if (((CursorType::TableText != m_eCursorType) &&
(CursorType::SelectionInTable != m_eCursorType)) && pTableNode)
{
aNewCursor->GetPoint()->nNode = pTableNode->EndOfSectionIndex();
aNewCursor->Move(fnMoveForward, GoInNode);
}
else
{
aNewCursor->MovePara(GoNextPara, fnParaStart);
}
if (m_nEndIndex < aNewCursor->Start()->nNode.GetIndex())
{
return nullptr;
}
}
bool bInTable = false;
if (!m_bFirstParagraph)
{
rUnoCursor.SetRemainInSection( false );
// what to do if already in a table?
SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
pTableNode = lcl_FindTopLevelTable( pTableNode, m_pOwnTable );
if (pTableNode && (&pTableNode->GetTable() != m_pOwnTable))
{
// this is a foreign table: go to end
rUnoCursor.GetPoint()->nNode = pTableNode->EndOfSectionIndex();
if (!rUnoCursor.Move(fnMoveForward, GoInNode))
{
return nullptr;
}
bInTable = true;
}
}
uno::Reference< text::XTextContent > xRef;
// the cursor must remain in the current section or a subsection
// before AND after the movement...
if (lcl_CursorIsInSection( &rUnoCursor, m_pOwnStartNode ) &&
(m_bFirstParagraph || bInTable ||
(rUnoCursor.MovePara(GoNextPara, fnParaStart) &&
lcl_CursorIsInSection( &rUnoCursor, m_pOwnStartNode ))))
{
SwPosition* pStart = rUnoCursor.Start();
const sal_Int32 nFirstContent =
(m_bFirstParagraph) ? m_nFirstParaStart : -1;
const sal_Int32 nLastContent =
(m_nEndIndex == pStart->nNode.GetIndex()) ? m_nLastParaEnd : -1;
// position in a table, or in a simple paragraph?
SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
pTableNode = lcl_FindTopLevelTable( pTableNode, m_pOwnTable );
if (/*CursorType::TableText != eCursorType && CursorType::SelectionInTable != eCursorType && */
pTableNode && (&pTableNode->GetTable() != m_pOwnTable))
{
// this is a foreign table
SwFrameFormat* pTableFormat =
static_cast<SwFrameFormat*>(pTableNode->GetTable().GetFrameFormat());
xRef = SwXTextTable::CreateXTextTable(pTableFormat);
}
else
{
text::XText *const pText = m_xParentText.get();
xRef = SwXParagraph::CreateXParagraph(*rUnoCursor.GetDoc(),
pStart->nNode.GetNode().GetTextNode(),
static_cast<SwXText*>(pText), nFirstContent, nLastContent);
}
}
return xRef;
}
uno::Any SAL_CALL SwXParagraphEnumerationImpl::nextElement()
{
SolarMutexGuard aGuard;
if (m_bFirstParagraph)
{
m_xNextPara = NextElement_Impl();
m_bFirstParagraph = false;
}
const uno::Reference< text::XTextContent > xRef = m_xNextPara;
if (!xRef.is())
{
throw container::NoSuchElementException();
}
m_xNextPara = NextElement_Impl();
uno::Any aRet;
aRet <<= xRef;
return aRet;
}
class SwXTextRange::Impl
: public SwClient
{
public:
const SfxItemPropertySet & m_rPropSet;
const enum RangePosition m_eRangePosition;
SwDoc & m_rDoc;
uno::Reference<text::XText> m_xParentText;
SwDepend m_ObjectDepend; // register at format of table or frame
::sw::mark::IMark * m_pMark;
Impl( SwDoc & rDoc, const enum RangePosition eRange,
SwFrameFormat *const pTableFormat,
const uno::Reference< text::XText > & xParent = nullptr)
: SwClient()
, m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
, m_eRangePosition(eRange)
, m_rDoc(rDoc)
, m_xParentText(xParent)
, m_ObjectDepend(this, pTableFormat)
, m_pMark(nullptr)
{
}
virtual ~Impl() override
{
// Impl owns the bookmark; delete it here: SolarMutex is locked
Invalidate();
}
void Invalidate()
{
if (m_pMark)
{
m_rDoc.getIDocumentMarkAccess()->deleteMark(m_pMark);
m_pMark = nullptr;
}
}
const ::sw::mark::IMark * GetBookmark() const { return m_pMark; }
protected:
// SwClient
virtual void Modify(const SfxPoolItem *pOld, const SfxPoolItem *pNew) override;
};
void SwXTextRange::Impl::Modify(const SfxPoolItem *pOld, const SfxPoolItem *pNew)
{
const bool bAlreadyRegistered = nullptr != GetRegisteredIn();
ClientModify(this, pOld, pNew);
if (m_ObjectDepend.GetRegisteredIn())
{
ClientModify(&m_ObjectDepend, pOld, pNew);
// if the depend was removed then the range must be removed too
if (!m_ObjectDepend.GetRegisteredIn() && GetRegisteredIn())
{
GetRegisteredIn()->Remove(this);
}
// or if the range has been removed but the depend is still
// connected then the depend must be removed
else if (bAlreadyRegistered && !GetRegisteredIn() &&
m_ObjectDepend.GetRegisteredIn())
{
m_ObjectDepend.GetRegisteredIn()
->Remove(& m_ObjectDepend);
}
}
if (!GetRegisteredIn())
{
m_pMark = nullptr;
}
}
SwXTextRange::SwXTextRange(SwPaM const & rPam,
const uno::Reference< text::XText > & xParent,
const enum RangePosition eRange)
: m_pImpl( new SwXTextRange::Impl(*rPam.GetDoc(), eRange, nullptr, xParent) )
{
SetPositions(rPam);
}
SwXTextRange::SwXTextRange(SwFrameFormat& rTableFormat)
: m_pImpl(
new SwXTextRange::Impl(*rTableFormat.GetDoc(), RANGE_IS_TABLE, &rTableFormat) )
{
SwTable *const pTable = SwTable::FindTable( &rTableFormat );
SwTableNode *const pTableNode = pTable->GetTableNode();
SwPosition aPosition( *pTableNode );
SwPaM aPam( aPosition );
SetPositions( aPam );
}
SwXTextRange::~SwXTextRange()
{
}
const SwDoc& SwXTextRange::GetDoc() const
{
return m_pImpl->m_rDoc;
}
SwDoc& SwXTextRange::GetDoc()
{
return m_pImpl->m_rDoc;
}
void SwXTextRange::Invalidate()
{
m_pImpl->Invalidate();
}
void SwXTextRange::SetPositions(const SwPaM& rPam)
{
m_pImpl->Invalidate();
IDocumentMarkAccess* const pMA = m_pImpl->m_rDoc.getIDocumentMarkAccess();
m_pImpl->m_pMark = pMA->makeMark(rPam, OUString(),
IDocumentMarkAccess::MarkType::UNO_BOOKMARK, sw::mark::InsertMode::New);
m_pImpl->m_pMark->Add(m_pImpl.get());
}
void SwXTextRange::DeleteAndInsert(
const OUString& rText, const bool bForceExpandHints)
{
if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
{
// setString on table not allowed
throw uno::RuntimeException();
}
const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
SwCursor aCursor(aPos, nullptr);
if (GetPositions(aCursor))
{
UnoActionContext aAction(& m_pImpl->m_rDoc);
m_pImpl->m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
if (aCursor.HasMark())
{
m_pImpl->m_rDoc.getIDocumentContentOperations().DeleteAndJoin(aCursor);
}
if (!rText.isEmpty())
{
SwUnoCursorHelper::DocInsertStringSplitCR(
m_pImpl->m_rDoc, aCursor, rText, bForceExpandHints);
SwUnoCursorHelper::SelectPam(aCursor, true);
aCursor.Left(rText.getLength());
}
SetPositions(aCursor);
m_pImpl->m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
}
}
namespace
{
class theSwXTextRangeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextRangeUnoTunnelId > {};
}
const uno::Sequence< sal_Int8 > & SwXTextRange::getUnoTunnelId()
{
return theSwXTextRangeUnoTunnelId::get().getSeq();
}
// XUnoTunnel
sal_Int64 SAL_CALL
SwXTextRange::getSomething(const uno::Sequence< sal_Int8 >& rId)
{
return ::sw::UnoTunnelImpl<SwXTextRange>(rId, this);
}
OUString SAL_CALL
SwXTextRange::getImplementationName()
{
return OUString("SwXTextRange");
}
static char const*const g_ServicesTextRange[] =
{
"com.sun.star.text.TextRange",
"com.sun.star.style.CharacterProperties",
"com.sun.star.style.CharacterPropertiesAsian",
"com.sun.star.style.CharacterPropertiesComplex",
"com.sun.star.style.ParagraphProperties",
"com.sun.star.style.ParagraphPropertiesAsian",
"com.sun.star.style.ParagraphPropertiesComplex",
};
static const size_t g_nServicesTextRange(SAL_N_ELEMENTS(g_ServicesTextRange));
sal_Bool SAL_CALL SwXTextRange::supportsService(const OUString& rServiceName)
{
return cppu::supportsService(this, rServiceName);
}
uno::Sequence< OUString > SAL_CALL
SwXTextRange::getSupportedServiceNames()
{
return ::sw::GetSupportedServiceNamesImpl(
g_nServicesTextRange, g_ServicesTextRange);
}
uno::Reference< text::XText > SAL_CALL
SwXTextRange::getText()
{
SolarMutexGuard aGuard;
if (!m_pImpl->m_xParentText.is())
{
if (m_pImpl->m_eRangePosition == RANGE_IS_TABLE &&
m_pImpl->m_ObjectDepend.GetRegisteredIn())
{
SwFrameFormat const*const pTableFormat = static_cast<SwFrameFormat const*>(
m_pImpl->m_ObjectDepend.GetRegisteredIn());
SwTable const*const pTable = SwTable::FindTable( pTableFormat );
SwTableNode const*const pTableNode = pTable->GetTableNode();
const SwPosition aPosition( *pTableNode );
m_pImpl->m_xParentText =
::sw::CreateParentXText(m_pImpl->m_rDoc, aPosition);
}
}
OSL_ENSURE(m_pImpl->m_xParentText.is(), "SwXTextRange::getText: no text");
return m_pImpl->m_xParentText;
}
uno::Reference< text::XTextRange > SAL_CALL
SwXTextRange::getStart()
{
SolarMutexGuard aGuard;
uno::Reference< text::XTextRange > xRet;
::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
if (!m_pImpl->m_xParentText.is())
{
getText();
}
if(pBkmk)
{
SwPaM aPam(pBkmk->GetMarkStart());
xRet = new SwXTextRange(aPam, m_pImpl->m_xParentText);
}
else if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
{
// start and end are this, if it's a table
xRet = this;
}
else
{
throw uno::RuntimeException();
}
return xRet;
}
uno::Reference< text::XTextRange > SAL_CALL
SwXTextRange::getEnd()
{
SolarMutexGuard aGuard;
uno::Reference< text::XTextRange > xRet;
::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
if (!m_pImpl->m_xParentText.is())
{
getText();
}
if(pBkmk)
{
SwPaM aPam(pBkmk->GetMarkEnd());
xRet = new SwXTextRange(aPam, m_pImpl->m_xParentText);
}
else if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
{
// start and end are this, if it's a table
xRet = this;
}
else
{
throw uno::RuntimeException();
}
return xRet;
}
OUString SAL_CALL SwXTextRange::getString()
{
SolarMutexGuard aGuard;
OUString sRet;
// for tables there is no bookmark, thus also no text
// one could export the table as ASCII here maybe?
SwPaM aPaM(GetDoc().GetNodes());
if (GetPositions(aPaM) && aPaM.HasMark())
{
SwUnoCursorHelper::GetTextFromPam(aPaM, sRet);
}
return sRet;
}
void SAL_CALL SwXTextRange::setString(const OUString& rString)
{
SolarMutexGuard aGuard;
DeleteAndInsert(rString, false);
}
bool SwXTextRange::GetPositions(SwPaM& rToFill) const
{
::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
if(pBkmk)
{
*rToFill.GetPoint() = pBkmk->GetMarkPos();
if(pBkmk->IsExpanded())
{
rToFill.SetMark();
*rToFill.GetMark() = pBkmk->GetOtherMarkPos();
}
else
{
rToFill.DeleteMark();
}
return true;
}
return false;
}
namespace sw {
bool XTextRangeToSwPaM( SwUnoInternalPaM & rToFill,
const uno::Reference< text::XTextRange > & xTextRange)
{
bool bRet = false;
uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
SwXTextRange* pRange = nullptr;
OTextCursorHelper* pCursor = nullptr;
SwXTextPortion* pPortion = nullptr;
SwXText* pText = nullptr;
SwXParagraph* pPara = nullptr;
if(xRangeTunnel.is())
{
pRange = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel);
pCursor =
::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel);
pPortion=
::sw::UnoTunnelGetImplementation<SwXTextPortion>(xRangeTunnel);
pText = ::sw::UnoTunnelGetImplementation<SwXText>(xRangeTunnel);
pPara = ::sw::UnoTunnelGetImplementation<SwXParagraph>(xRangeTunnel);
}
// if it's a text then create a temporary cursor there and re-use
// the pCursor variable
// #i108489#: Reference in outside scope to keep cursor alive
uno::Reference< text::XTextCursor > xTextCursor;
if (pText)
{
xTextCursor.set( pText->CreateCursor() );
xTextCursor->gotoEnd(true);
const uno::Reference<lang::XUnoTunnel> xCursorTunnel(
xTextCursor, uno::UNO_QUERY);
pCursor =
::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xCursorTunnel);
}
if(pRange && &pRange->GetDoc() == rToFill.GetDoc())
{
bRet = pRange->GetPositions(rToFill);
}
else
{
if (pPara)
{
bRet = pPara->SelectPaM(rToFill);
}
else
{
SwDoc* const pDoc = (pCursor) ? pCursor->GetDoc()
: ((pPortion) ? pPortion->GetCursor().GetDoc() : nullptr);
const SwPaM* const pUnoCursor = (pCursor) ? pCursor->GetPaM()
: ((pPortion) ? &pPortion->GetCursor() : nullptr);
if (pUnoCursor && pDoc == rToFill.GetDoc())
{
OSL_ENSURE(!pUnoCursor->IsMultiSelection(),
"what to do about rings?");
bRet = true;
*rToFill.GetPoint() = *pUnoCursor->GetPoint();
if (pUnoCursor->HasMark())
{
rToFill.SetMark();
*rToFill.GetMark() = *pUnoCursor->GetMark();
}
else
rToFill.DeleteMark();
}
}
}
return bRet;
}
static bool
lcl_IsStartNodeInFormat(const bool bHeader, SwStartNode const *const pSttNode,
SwFrameFormat const*const pFrameFormat, SwFrameFormat*& rpFormat)
{
bool bRet = false;
const SfxItemSet& rSet = pFrameFormat->GetAttrSet();
const SfxPoolItem* pItem;
if (SfxItemState::SET == rSet.GetItemState(
static_cast<sal_uInt16>(bHeader ? RES_HEADER : RES_FOOTER),
true, &pItem))
{
SfxPoolItem *const pItemNonConst(const_cast<SfxPoolItem *>(pItem));
SwFrameFormat *const pHeadFootFormat = (bHeader) ?
static_cast<SwFormatHeader*>(pItemNonConst)->GetHeaderFormat() :
static_cast<SwFormatFooter*>(pItemNonConst)->GetFooterFormat();
if (pHeadFootFormat)
{
const SwFormatContent& rFlyContent = pHeadFootFormat->GetContent();
const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
SwStartNode const*const pCurSttNode = rNode.FindSttNodeByType(
(bHeader) ? SwHeaderStartNode : SwFooterStartNode);
if (pCurSttNode && (pCurSttNode == pSttNode))
{
rpFormat = pHeadFootFormat;
bRet = true;
}
}
}
return bRet;
}
} // namespace sw
uno::Reference< text::XTextRange >
SwXTextRange::CreateXTextRange(
SwDoc & rDoc, const SwPosition& rPos, const SwPosition *const pMark)
{
const uno::Reference<text::XText> xParentText(
::sw::CreateParentXText(rDoc, rPos));
const auto pNewCursor(rDoc.CreateUnoCursor(rPos));
if(pMark)
{
pNewCursor->SetMark();
*pNewCursor->GetMark() = *pMark;
}
const bool isCell( dynamic_cast<SwXCell*>(xParentText.get()) );
const uno::Reference< text::XTextRange > xRet(
new SwXTextRange(*pNewCursor, xParentText,
isCell ? RANGE_IN_CELL : RANGE_IN_TEXT) );
return xRet;
}
namespace sw {
uno::Reference< text::XText >
CreateParentXText(SwDoc & rDoc, const SwPosition& rPos)
{
uno::Reference< text::XText > xParentText;
SwStartNode* pSttNode = rPos.nNode.GetNode().StartOfSectionNode();
while(pSttNode && pSttNode->IsSectionNode())
{
pSttNode = pSttNode->StartOfSectionNode();
}
SwStartNodeType eType = pSttNode ? pSttNode->GetStartNodeType() : SwNormalStartNode;
switch(eType)
{
case SwTableBoxStartNode:
{
SwTableNode const*const pTableNode = pSttNode->FindTableNode();
SwFrameFormat *const pTableFormat =
static_cast<SwFrameFormat*>(pTableNode->GetTable().GetFrameFormat());
SwTableBox *const pBox = pSttNode->GetTableBox();
xParentText = (pBox)
? SwXCell::CreateXCell( pTableFormat, pBox )
: new SwXCell( pTableFormat, *pSttNode );
}
break;
case SwFlyStartNode:
{
SwFrameFormat *const pFormat = pSttNode->GetFlyFormat();
if (nullptr != pFormat)
{
xParentText.set(SwXTextFrame::CreateXTextFrame(rDoc, pFormat),
uno::UNO_QUERY);
}
}
break;
case SwHeaderStartNode:
case SwFooterStartNode:
{
const bool bHeader = (SwHeaderStartNode == eType);
const size_t nPDescCount = rDoc.GetPageDescCnt();
for(size_t i = 0; i < nPDescCount; i++)
{
const SwPageDesc& rDesc = rDoc.GetPageDesc( i );
const SwFrameFormat* pFrameFormatMaster = &rDesc.GetMaster();
const SwFrameFormat* pFrameFormatLeft = &rDesc.GetLeft();
SwFrameFormat* pHeadFootFormat = nullptr;
if (!lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatMaster,
pHeadFootFormat))
{
lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatLeft,
pHeadFootFormat);
}
if (pHeadFootFormat)
{
xParentText = SwXHeadFootText::CreateXHeadFootText(
*pHeadFootFormat, bHeader);
}
}
}
break;
case SwFootnoteStartNode:
{
const size_t nFootnoteCnt = rDoc.GetFootnoteIdxs().size();
for (size_t n = 0; n < nFootnoteCnt; ++n )
{
const SwTextFootnote* pTextFootnote = rDoc.GetFootnoteIdxs()[ n ];
const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
pTextFootnote = rFootnote.GetTextFootnote();
#if OSL_DEBUG_LEVEL > 1
const SwStartNode* pTmpSttNode =
pTextFootnote->GetStartNode()->GetNode().
FindSttNodeByType(SwFootnoteStartNode);
(void)pTmpSttNode;
#endif
if (pSttNode == pTextFootnote->GetStartNode()->GetNode().
FindSttNodeByType(SwFootnoteStartNode))
{
xParentText.set(SwXFootnote::CreateXFootnote(rDoc,
&const_cast<SwFormatFootnote&>(rFootnote)), uno::UNO_QUERY);
break;
}
}
}
break;
default:
{
// then it is the body text
const uno::Reference<frame::XModel> xModel =
rDoc.GetDocShell()->GetBaseModel();
const uno::Reference< text::XTextDocument > xDoc(
xModel, uno::UNO_QUERY);
xParentText = xDoc->getText();
}
}
OSL_ENSURE(xParentText.is(), "no parent text?");
return xParentText;
}
} // namespace sw
uno::Reference< container::XEnumeration > SAL_CALL
SwXTextRange::createContentEnumeration(const OUString& rServiceName)
{
SolarMutexGuard g;
if ( rServiceName != "com.sun.star.text.TextContent" )
{
throw uno::RuntimeException();
}
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
const auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos));
if (!GetPositions(*pNewCursor))
{
throw uno::RuntimeException();
}
return SwXParaFrameEnumeration::Create(*pNewCursor, PARAFRAME_PORTION_TEXTRANGE);
}
uno::Reference< container::XEnumeration > SAL_CALL
SwXTextRange::createEnumeration()
{
SolarMutexGuard g;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos));
if (!GetPositions(*pNewCursor))
{
throw uno::RuntimeException();
}
if (!m_pImpl->m_xParentText.is())
{
getText();
}
const CursorType eSetType = (RANGE_IN_CELL == m_pImpl->m_eRangePosition)
? CursorType::SelectionInTable : CursorType::Selection;
return SwXParagraphEnumeration::Create(m_pImpl->m_xParentText, pNewCursor, eSetType);
}
uno::Type SAL_CALL SwXTextRange::getElementType()
{
return cppu::UnoType<text::XTextRange>::get();
}
sal_Bool SAL_CALL SwXTextRange::hasElements()
{
return true;
}
uno::Sequence< OUString > SAL_CALL
SwXTextRange::getAvailableServiceNames()
{
uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
return aRet;
}
uno::Reference< beans::XPropertySetInfo > SAL_CALL
SwXTextRange::getPropertySetInfo()
{
SolarMutexGuard aGuard;
static uno::Reference< beans::XPropertySetInfo > xRef =
m_pImpl->m_rPropSet.getPropertySetInfo();
return xRef;
}
void SAL_CALL
SwXTextRange::setPropertyValue(
const OUString& rPropertyName, const uno::Any& rValue)
{
SolarMutexGuard aGuard;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
GetPositions(aPaM);
SwUnoCursorHelper::SetPropertyValue(aPaM, m_pImpl->m_rPropSet,
rPropertyName, rValue);
}
uno::Any SAL_CALL
SwXTextRange::getPropertyValue(const OUString& rPropertyName)
{
SolarMutexGuard aGuard;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
GetPositions(aPaM);
return SwUnoCursorHelper::GetPropertyValue(aPaM, m_pImpl->m_rPropSet,
rPropertyName);
}
void SAL_CALL
SwXTextRange::addPropertyChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
OSL_FAIL("SwXTextRange::addPropertyChangeListener(): not implemented");
}
void SAL_CALL
SwXTextRange::removePropertyChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
{
OSL_FAIL("SwXTextRange::removePropertyChangeListener(): not implemented");
}
void SAL_CALL
SwXTextRange::addVetoableChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
OSL_FAIL("SwXTextRange::addVetoableChangeListener(): not implemented");
}
void SAL_CALL
SwXTextRange::removeVetoableChangeListener(
const OUString& /*rPropertyName*/,
const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
{
OSL_FAIL("SwXTextRange::removeVetoableChangeListener(): not implemented");
}
beans::PropertyState SAL_CALL
SwXTextRange::getPropertyState(const OUString& rPropertyName)
{
SolarMutexGuard aGuard;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
GetPositions(aPaM);
return SwUnoCursorHelper::GetPropertyState(aPaM, m_pImpl->m_rPropSet,
rPropertyName);
}
uno::Sequence< beans::PropertyState > SAL_CALL
SwXTextRange::getPropertyStates(const uno::Sequence< OUString >& rPropertyName)
{
SolarMutexGuard g;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
GetPositions(aPaM);
return SwUnoCursorHelper::GetPropertyStates(aPaM, m_pImpl->m_rPropSet,
rPropertyName);
}
void SAL_CALL SwXTextRange::setPropertyToDefault(const OUString& rPropertyName)
{
SolarMutexGuard aGuard;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
GetPositions(aPaM);
SwUnoCursorHelper::SetPropertyToDefault(aPaM, m_pImpl->m_rPropSet,
rPropertyName);
}
uno::Any SAL_CALL
SwXTextRange::getPropertyDefault(const OUString& rPropertyName)
{
SolarMutexGuard aGuard;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
GetPositions(aPaM);
return SwUnoCursorHelper::GetPropertyDefault(aPaM, m_pImpl->m_rPropSet,
rPropertyName);
}
void SAL_CALL
SwXTextRange::makeRedline(
const OUString& rRedlineType,
const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
{
SolarMutexGuard aGuard;
if (!m_pImpl->GetBookmark())
{
throw uno::RuntimeException();
}
SwPaM aPaM(GetDoc().GetNodes());
SwXTextRange::GetPositions(aPaM);
SwUnoCursorHelper::makeRedline( aPaM, rRedlineType, rRedlineProperties );
}
struct SwXTextRangesImpl final : public SwXTextRanges
{
// XUnoTunnel
virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override
{ return OUString("SwXTextRanges"); };
virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName) override
{ return cppu::supportsService(this, rServiceName); };
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
{ return { "com.sun.star.text.TextRanges" }; };
// XElementAccess
virtual css::uno::Type SAL_CALL getElementType() override
{ return cppu::UnoType<text::XTextRange>::get(); };
virtual sal_Bool SAL_CALL hasElements() override
{ return getCount() > 0; };
// XIndexAccess
virtual sal_Int32 SAL_CALL getCount() override;
virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override;
explicit SwXTextRangesImpl(SwPaM *const pPaM)
{
if (pPaM)
{
m_pUnoCursor.reset(pPaM->GetDoc()->CreateUnoCursor(*pPaM->GetPoint()));
::sw::DeepCopyPaM(*pPaM, *GetCursor());
}
MakeRanges();
}
virtual void SAL_CALL release() throw () override
{
SolarMutexGuard g;
OWeakObject::release();
}
virtual SwUnoCursor* GetCursor() override
{ return &(*m_pUnoCursor); };
void MakeRanges();
std::vector< uno::Reference< text::XTextRange > > m_Ranges;
sw::UnoCursorPointer m_pUnoCursor;
};
void SwXTextRangesImpl::MakeRanges()
{
if (GetCursor())
{
for(SwPaM& rTmpCursor : GetCursor()->GetRingContainer())
{
const uno::Reference< text::XTextRange > xRange(
SwXTextRange::CreateXTextRange(
*rTmpCursor.GetDoc(),
*rTmpCursor.GetPoint(), rTmpCursor.GetMark()));
if (xRange.is())
{
m_Ranges.push_back(xRange);
}
}
}
}
SwXTextRanges* SwXTextRanges::Create(SwPaM *const pPaM)
{ return new SwXTextRangesImpl(pPaM); }
namespace
{
class theSwXTextRangesUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextRangesUnoTunnelId > {};
}
const uno::Sequence< sal_Int8 > & SwXTextRanges::getUnoTunnelId()
{ return theSwXTextRangesUnoTunnelId::get().getSeq(); }
sal_Int64 SAL_CALL
SwXTextRangesImpl::getSomething(const uno::Sequence< sal_Int8 >& rId)
{
return ::sw::UnoTunnelImpl<SwXTextRanges>(rId, this);
}
/*
* Text positions
* Up to the first access to a text position, only a SwCursor is stored.
* Afterwards, an array with uno::Reference<XTextPosition> will be created.
*/
sal_Int32 SAL_CALL SwXTextRangesImpl::getCount()
{
SolarMutexGuard aGuard;
return static_cast<sal_Int32>(m_Ranges.size());
}
uno::Any SAL_CALL SwXTextRangesImpl::getByIndex(sal_Int32 nIndex)
{
SolarMutexGuard aGuard;
if ((nIndex < 0) || (static_cast<size_t>(nIndex) >= m_Ranges.size()))
throw lang::IndexOutOfBoundsException();
uno::Any ret;
ret <<= (m_Ranges.at(nIndex));
return ret;
}
void SwUnoCursorHelper::SetString(SwCursor & rCursor, const OUString& rString)
{
// Start/EndAction
SwDoc *const pDoc = rCursor.GetDoc();
UnoActionContext aAction(pDoc);
pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
if (rCursor.HasMark())
{
pDoc->getIDocumentContentOperations().DeleteAndJoin(rCursor);
}
if (!rString.isEmpty())
{
const bool bSuccess( SwUnoCursorHelper::DocInsertStringSplitCR(
*pDoc, rCursor, rString, false ) );
OSL_ENSURE( bSuccess, "DocInsertStringSplitCR" );
SwUnoCursorHelper::SelectPam(rCursor, true);
rCursor.Left(rString.getLength());
}
pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
}
struct SwXParaFrameEnumerationImpl final : public SwXParaFrameEnumeration
{
// XServiceInfo
virtual OUString SAL_CALL getImplementationName() override
{ return OUString("SwXParaFrameEnumeration"); };
virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override
{ return cppu::supportsService(this, rServiceName); };
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
{ return {"com.sun.star.util.ContentEnumeration"}; };
// XEnumeration
virtual sal_Bool SAL_CALL hasMoreElements() override;
virtual css::uno::Any SAL_CALL nextElement() override;
SwXParaFrameEnumerationImpl(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat);
virtual void SAL_CALL release() throw () override
{
SolarMutexGuard g;
OWeakObject::release();
}
SwUnoCursor& GetCursor()
{ return *m_pUnoCursor; }
void PurgeFrameClients()
{
if(!m_pUnoCursor)
{
m_vFrames.clear();
m_xNextObject = nullptr;
}
else
{
// removing orphaned SwDepends
const auto iter = std::remove_if(m_vFrames.begin(), m_vFrames.end(),
[] (std::shared_ptr<sw::FrameClient>& rEntry) -> bool { return !rEntry->GetRegisteredIn(); });
m_vFrames.erase(iter, m_vFrames.end());
}
}
void FillFrame();
bool CreateNextObject();
uno::Reference< text::XTextContent > m_xNextObject;
FrameClientList_t m_vFrames;
::sw::UnoCursorPointer m_pUnoCursor;
};
SwXParaFrameEnumeration* SwXParaFrameEnumeration::Create(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat)
{ return new SwXParaFrameEnumerationImpl(rPaM, eParaFrameMode, pFormat); }
SwXParaFrameEnumerationImpl::SwXParaFrameEnumerationImpl(
const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode,
SwFrameFormat* const pFormat)
: m_pUnoCursor(rPaM.GetDoc()->CreateUnoCursor(*rPaM.GetPoint()))
{
if (rPaM.HasMark())
{
GetCursor().SetMark();
*GetCursor().GetMark() = *rPaM.GetMark();
}
if (PARAFRAME_PORTION_PARAGRAPH == eParaFrameMode)
{
FrameClientSortList_t vFrames;
::CollectFrameAtNode(rPaM.GetPoint()->nNode, vFrames, false);
std::transform(vFrames.begin(), vFrames.end(),
std::back_inserter(m_vFrames),
[] (const FrameClientSortListEntry& rEntry) { return rEntry.pFrameClient; });
}
else if (pFormat)
{
m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFormat));
}
else if ((PARAFRAME_PORTION_CHAR == eParaFrameMode) ||
(PARAFRAME_PORTION_TEXTRANGE == eParaFrameMode))
{
if (PARAFRAME_PORTION_TEXTRANGE == eParaFrameMode)
{
//get all frames that are bound at paragraph or at character
for(const auto& pFlyFrame : rPaM.GetDoc()->GetAllFlyFormats(&GetCursor(), false, true))
{
const auto pFrameFormat = const_cast<SwFrameFormat*>(&pFlyFrame->GetFormat());
m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFrameFormat));
}
}
FillFrame();
}
}
// Search for a FLYCNT text attribute at the cursor point and fill the frame
// into the array
void SwXParaFrameEnumerationImpl::FillFrame()
{
if(!m_pUnoCursor->GetNode().IsTextNode())
return;
// search for objects at the cursor - anchored at/as char
const auto pTextAttr = m_pUnoCursor->GetNode().GetTextNode()->GetTextAttrForCharAt(
m_pUnoCursor->GetPoint()->nContent.GetIndex(), RES_TXTATR_FLYCNT);
if(!pTextAttr)
return;
const SwFormatFlyCnt& rFlyCnt = pTextAttr->GetFlyCnt();
SwFrameFormat* const pFrameFormat = rFlyCnt.GetFrameFormat();
m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFrameFormat));
}
bool SwXParaFrameEnumerationImpl::CreateNextObject()
{
if (m_vFrames.empty())
return false;
SwFrameFormat* const pFormat = static_cast<SwFrameFormat*>(
m_vFrames.front()->GetRegisteredIn());
m_vFrames.pop_front();
// the format should be valid here, otherwise the client
// would have been removed by PurgeFrameClients
// check for a shape first
if(pFormat->Which() == RES_DRAWFRMFMT)
{
SdrObject* pObject(nullptr);
pFormat->CallSwClientNotify(sw::FindSdrObjectHint(pObject));
if(pObject)
m_xNextObject.set(pObject->getUnoShape(), uno::UNO_QUERY);
}
else
{
const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
OSL_ENSURE(pIdx, "where is the index?");
SwNode const*const pNd =
m_pUnoCursor->GetDoc()->GetNodes()[ pIdx->GetIndex() + 1 ];
if (!pNd->IsNoTextNode())
{
m_xNextObject.set(SwXTextFrame::CreateXTextFrame(
*pFormat->GetDoc(), pFormat));
}
else if (pNd->IsGrfNode())
{
m_xNextObject.set(SwXTextGraphicObject::CreateXTextGraphicObject(
*pFormat->GetDoc(), pFormat));
}
else
{
assert(pNd->IsOLENode());
m_xNextObject.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
*pFormat->GetDoc(), pFormat));
}
}
return m_xNextObject.is();
}
sal_Bool SAL_CALL
SwXParaFrameEnumerationImpl::hasMoreElements()
{
SolarMutexGuard aGuard;
PurgeFrameClients();
return m_xNextObject.is() || CreateNextObject();
}
uno::Any SAL_CALL SwXParaFrameEnumerationImpl::nextElement()
{
SolarMutexGuard aGuard;
PurgeFrameClients();
if (!m_xNextObject.is() && !m_vFrames.empty())
CreateNextObject();
if (!m_xNextObject.is())
throw container::NoSuchElementException();
uno::Any aRet;
aRet <<= m_xNextObject;
m_xNextObject = nullptr;
return aRet;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */