/* -*- 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 <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/flstitem.hxx>
#include <editeng/unolingu.hxx>
#include <rtl/ustrbuf.hxx>
#include <svl/listener.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/fcontnr.hxx>
#include <sfx2/linkmgr.hxx>
#include <svtools/ctrltool.hxx>
#include <vcl/svapp.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 <IDocumentMarkAccess.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 <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 <poolfmt.hxx>
#include <edimp.hxx>
#include <fchrfmt.hxx>
#include <cntfrm.hxx>
#include <pagefrm.hxx>
#include <doctxm.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 <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 <com/sun/star/lang/IndexOutOfBoundsException.hpp>
#include <unoframe.hxx>
#include <fmthdft.hxx>
#include <fmtflcnt.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>
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() COVERITY_NOEXCEPT_FALSE
{
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->EndListeningAll();
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->EndListeningAll();
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 SvtListener
{
public:
const SfxItemPropertySet& m_rPropSet;
const enum RangePosition m_eRangePosition;
SwDoc& m_rDoc;
uno::Reference<text::XText> m_xParentText;
const SwFrameFormat* m_pTableFormat;
const ::sw::mark::IMark* m_pMark;
Impl(SwDoc& rDoc, const enum RangePosition eRange,
SwFrameFormat* const pTableFormat,
const uno::Reference<text::XText>& xParent = nullptr)
: m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
, m_eRangePosition(eRange)
, m_rDoc(rDoc)
, m_xParentText(xParent)
, m_pTableFormat(pTableFormat)
, m_pMark(nullptr)
{
m_pTableFormat && StartListening(pTableFormat->GetNotifier());
}
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;
}
m_pTableFormat = nullptr;
EndListeningAll();
}
const ::sw::mark::IMark* GetBookmark() const { return m_pMark; }
void SetMark(::sw::mark::IMark& rMark)
{
EndListeningAll();
m_pTableFormat = nullptr;
m_pMark = &rMark;
StartListening(rMark.GetNotifier());
}
protected:
virtual void Notify(const SfxHint&) override;
};
void SwXTextRange::Impl::Notify(const SfxHint& rHint)
{
if(rHint.GetId() == SfxHintId::Dying)
{
EndListeningAll();
m_pTableFormat = nullptr;
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();
auto pMark = pMA->makeMark(rPam, OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK, sw::mark::InsertMode::New);
m_pImpl->SetMark(*pMark);
}
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_pTableFormat)
{
SwTable const*const pTable = SwTable::FindTable( m_pImpl->m_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(
bHeader ? sal_uInt16(RES_HEADER) : sal_uInt16(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 (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 Clients
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: */
↑ V560 A part of conditional expression is always true.
↑ V560 A part of conditional expression is always true: (CursorType::TableText != m_eCursorType).
↑ V560 A part of conditional expression is always true: (CursorType::TableText != m_eCursorType).