/* -*- 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 .
*/
// return a position at the begin of rEnd, if it is a ContentNode // else set it to the begin of the Node after rEnd, if there is one // else set it to the end of the node before rStt // else set it to the ContentNode of the Pos outside the Range void lcl_FindExpelPosition(
std::optional<SwPosition>& rFoundPos, const SwNode& rStt, const SwNode& rEnd, const SwPosition& rOtherPosition)
{ const SwContentNode * pNode = rEnd.GetContentNode(); bool bPosAtEndOfNode = false; if ( pNode == nullptr)
{
SwNodeIndex aEnd(rEnd);
pNode = SwNodes::GoNext(&aEnd);
bPosAtEndOfNode = false;
} if ( pNode == nullptr )
{
SwNodeIndex aStt(rStt);
pNode = SwNodes::GoPrevious(&aStt);
bPosAtEndOfNode = true;
} if ( pNode != nullptr )
{
lcl_PositionFromContentNode( rFoundPos, pNode, bPosAtEndOfNode ); return;
}
template<class MarkT>
MarkT* lcl_getMarkBefore(const std::vector<MarkT*>& rMarks, const SwPosition& rPos, bool bLoop)
{ // candidates from which to choose the mark before
std::vector<MarkT*> vCandidates; // no need to consider marks starting after rPos autoconst pCandidatesEnd = upper_bound(
rMarks.begin(),
rMarks.end(),
rPos,
CompareIMarkStartsAfter<MarkT>());
vCandidates.reserve(pCandidatesEnd - rMarks.begin()); // only marks ending before are candidates
remove_copy_if(
rMarks.begin(),
pCandidatesEnd,
back_inserter(vCandidates),
[&rPos] (const MarkT *const pMark) { return !(pMark->GetMarkEnd() < rPos); } ); // no candidate left => we are in front of the first mark or there are none if(vCandidates.empty())
{ if (bLoop && rMarks.begin() != rMarks.end()) return *(rMarks.end() - 1);
return nullptr;
} // return the highest (last) candidate using mark end ordering return *max_element(vCandidates.begin(), vCandidates.end(), &lcl_MarkOrderingByEnd<MarkT>);
}
bool lcl_FixCorrectedMark( constbool bChangedPos, constbool bChangedOPos,
MarkBase* io_pMark )
{ if ( IsAnnotationMark(*io_pMark) )
{ // annotation marks are allowed to span a table cell range. // but trigger sorting to be save returntrue;
}
template<class MarkT> typename std::vector<MarkT*>::const_iterator lcl_FindMark(
std::vector<MarkT*>& rMarks, const MarkT *const pMarkToFind)
{ auto ppCurrentMark = lower_bound(
rMarks.begin(), rMarks.end(),
pMarkToFind, &lcl_MarkOrderingByStart<MarkT>); // since there are usually not too many marks on the same start // position, we are not doing a bisect search for the upper bound // but instead start to iterate from pMarkLow directly while (ppCurrentMark != rMarks.end() && lcl_MarkEqualByStart<MarkT>(*ppCurrentMark, pMarkToFind))
{ if(*ppCurrentMark == pMarkToFind)
{ return ppCurrentMark;
}
++ppCurrentMark;
} // reached a mark starting on a later start pos or the end of the // vector => not found return rMarks.end();
};
template<class MarkT> typename std::vector<MarkT*>::const_iterator lcl_FindMarkAtPos(
std::vector<MarkT*>& rMarks, const SwPosition& rPos, const IDocumentMarkAccess::MarkType eType)
{ for (auto ppCurrentMark = lower_bound(
rMarks.begin(), rMarks.end(),
rPos,
CompareIMarkStartsBefore<MarkT>());
ppCurrentMark != rMarks.end();
++ppCurrentMark)
{ // Once we reach a mark starting after the target pos // we do not need to continue if((*ppCurrentMark)->GetMarkStart() > rPos) break; if(IDocumentMarkAccess::GetType(**ppCurrentMark) == eType)
{ return ppCurrentMark;
}
} // reached a mark starting on a later start pos or the end of the // vector => not found return rMarks.end();
};
// In the mode where the names are not checked, we need to avoid a case where there was a // bookmark, and a file is inserted at an earlier point, with the same-name bookmark, causing // a rename of the pre-existing bookmark. usedNames and uncheckedNameMarks are used for that. // usedNames is pre-populated with the existing names (at the moment when the mode started); // and uncheckedNameMarks stores only the marks needing the checks.
class UniqueNameChecksGuard_impl : public MarkManager::UniqueNameChecksGuard
{ public:
UniqueNameChecksGuard_impl(MarkManager& aParent)
: parent(aParent)
{
assert(parent.m_pUniqueNameChecksGuard == nullptr);
parent.m_pUniqueNameChecksGuard = this;
// Populate the pre-existing already deduplicated names for (auto& pMark : parent.m_vAllMarks)
usedNames.insert(pMark->GetName().toString());
}
// for performance reasons, we trust UnoMarks to have a (generated) unique name if (eType != IDocumentMarkAccess::MarkType::UNO_BOOKMARK)
{ if (!m_pUniqueNameChecksGuard)
{
pMark->SetName(getUniqueMarkName(
pMark->GetName(), [this](const SwMarkName& n)
{ return lcl_FindMarkByName(n, m_vAllMarks) == m_vAllMarks.end(); }));
} else
{
m_pUniqueNameChecksGuard->add(pMark.get());
}
}
// insert any dummy chars before inserting into sorted vectors
pMark->InitDoc(m_rDoc, eMode, pSepPos);
// register mark
lcl_InsertMarkSorted(m_vAllMarks, pMark.get()); switch(eType)
{ case IDocumentMarkAccess::MarkType::BOOKMARK: case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK:
lcl_InsertMarkSorted(m_vBookmarks, static_cast<Bookmark*>(pMark.get())); break; case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
lcl_InsertMarkSorted(m_vFieldmarks, static_cast<Fieldmark*>(pMark.get())); break; case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
lcl_InsertMarkSorted( m_vAnnotationMarks, static_cast<AnnotationMark*>(pMark.get()) ); break; case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: // no special array for these break;
} if (eMode == InsertMode::New
&& (eType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
|| eType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
{ // due to sw::InsertText notifications everything is visible now - tell // layout to hide as appropriate // note: we don't know how many layouts there are and which // parts they hide, so just notify the entire fieldmark, it // should give the right result if not in the most efficient way // note2: can't be done in InitDoc() because it requires the mark // to be inserted in the vectors.
SwPaM const tmp(pMark->GetMarkPos(), pMark->GetOtherMarkPos());
sw::UpdateFramesForAddDeleteRedline(m_rDoc, tmp);
}
// Disable undo, because we handle it using SwUndoInsTextFieldmark bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
if (bUndoIsEnabled)
{
m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled); if (pFieldMark)
m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsTextFieldmark>(*pFieldMark));
}
return pFieldMark;
}
::sw::mark::Fieldmark* MarkManager::makeNoTextFieldBookmark( const SwPaM& rPaM, const SwMarkName& rName, const OUString& rType)
{ // Disable undo, because we handle it using SwUndoInsNoTextFieldmark bool bUndoIsEnabled = m_rDoc.GetIDocumentUndoRedo().DoesUndo();
m_rDoc.GetIDocumentUndoRedo().DoUndo(false);
if (bUndoIsEnabled)
{
m_rDoc.GetIDocumentUndoRedo().DoUndo(bUndoIsEnabled); if (pFieldMark)
m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoInsNoTextFieldmark>(*pFieldMark));
}
void MarkManager::repositionMark(
::sw::mark::MarkBase* const io_pMark, const SwPaM& rPaM)
{
assert(&io_pMark->GetMarkPos().GetDoc() == &m_rDoc && "" " - Mark is not in my doc.");
MarkBase* const pMarkBase = io_pMark; if (!pMarkBase) return;
staticbool isDeleteMark(
::sw::mark::MarkBase const*const pMark, boolconst isReplace,
SwNode const& rStt,
SwNode const& rEnd,
std::optional<sal_Int32> oStartContentIdx,
std::optional<sal_Int32> oEndContentIdx, bool & rbIsPosInRange, bool & rbIsOtherPosInRange)
{
assert(pMark); // navigator marks should not be moved // TODO: Check if this might make them invalid if (IsNavigatorReminder(*pMark))
{ returnfalse;
}
// on position ??
rbIsPosInRange = lcl_GreaterThan(pMark->GetMarkPos(), rStt, oStartContentIdx)
&& lcl_Lower(pMark->GetMarkPos(), rEnd, oEndContentIdx);
rbIsOtherPosInRange = pMark->IsExpanded()
&& lcl_GreaterThan(pMark->GetOtherMarkPos(), rStt, oStartContentIdx)
&& lcl_Lower(pMark->GetOtherMarkPos(), rEnd, oEndContentIdx); // special case: completely in range, touching the end? if ( oEndContentIdx.has_value()
&& !(isReplace && IDocumentMarkAccess::GetType(*pMark)
== IDocumentMarkAccess::MarkType::BOOKMARK)
&& ( ( rbIsOtherPosInRange
&& pMark->GetMarkPos().GetNode() == rEnd
&& pMark->GetMarkPos().GetContentIndex() == *oEndContentIdx )
|| ( rbIsPosInRange
&& pMark->IsExpanded()
&& pMark->GetOtherMarkPos().GetNode() == rEnd
&& pMark->GetOtherMarkPos().GetContentIndex() == *oEndContentIdx ) ) )
{
rbIsPosInRange = true;
rbIsOtherPosInRange = true;
}
if (rbIsPosInRange
&& (rbIsOtherPosInRange
|| !pMark->IsExpanded()))
{ // completely in range
bool bDeleteMark = true;
{ switch ( IDocumentMarkAccess::GetType( *pMark ) )
{ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: // no delete of cross-reference bookmarks, if range is inside one paragraph
bDeleteMark = &rStt != &rEnd; break; case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: // no delete of UNO mark, if it is not expanded and only touches the start of the range
bDeleteMark = rbIsOtherPosInRange
|| pMark->IsExpanded()
|| !oStartContentIdx.has_value()
|| pMark->GetMarkPos().GetNode() != rStt
|| pMark->GetMarkPos().GetContentIndex() != *oStartContentIdx; break; default: break;
}
} return bDeleteMark;
} returnfalse;
}
// boolean indicating, if at least one mark has been moved while collecting marks for deletion bool bMarksMoved = false; // have marks in the range been skipped instead of deleted bool bMarksSkipDeletion = false;
// copy all bookmarks in the move area to a vector storing all position data as offset // reassignment is performed after the move for (auto ppMark = m_vAllMarks.begin();
ppMark != m_vAllMarks.end();
++ppMark)
{
::sw::mark::MarkBase *const pMark = *ppMark; bool bIsPosInRange(false); bool bIsOtherPosInRange(false); boolconst bDeleteMark = isDeleteMark(pMark, isReplace, rStt, rEnd,
oStartContentIdx, oEndContentIdx, bIsPosInRange, bIsOtherPosInRange);
if ( bIsPosInRange
&& ( bIsOtherPosInRange
|| !pMark->IsExpanded() ) )
{ if ( bDeleteMark )
{ if ( pSaveBkmk )
{
pSaveBkmk->push_back( SaveBookmark( *pMark, rStt, oStartContentIdx ) );
}
vMarksToDelete.emplace_back(ppMark);
} else
{
bMarksSkipDeletion = true;
}
} elseif ( bIsPosInRange != bIsOtherPosInRange )
{ // the bookmark is partially in the range // move position of that is in the range out of it
bool bMoveMark = true;
{ switch ( IDocumentMarkAccess::GetType( *pMark ) )
{ case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK: // no move of cross-reference bookmarks, if move occurs inside a certain node
bMoveMark = pMark->GetMarkPos().GetNode() != oNewPos->GetNode(); break; case IDocumentMarkAccess::MarkType::ANNOTATIONMARK: // no move of annotation marks, if method is called to collect deleted marks
bMoveMark = pSaveBkmk == nullptr; break; default:
bMoveMark = true; break;
}
} if ( bMoveMark )
{ if ( bIsPosInRange )
pMark->SetMarkPos(*oNewPos); else
pMark->SetOtherMarkPos(*oNewPos);
bMarksMoved = true;
// illegal selection? collapse the mark and restore sorting later
bIsSortingNeeded |= lcl_FixCorrectedMark( bIsPosInRange, bIsOtherPosInRange, pMark );
}
}
}
{ // fdo#61016 delay the deletion of the fieldmark characters // to prevent that from deleting the marks on that position // which would invalidate the iterators in vMarksToDelete
std::vector< std::unique_ptr<ILazyDeleter> > vDelay;
vDelay.reserve(vMarksToDelete.size());
// If needed, sort mark containers containing subsets of the marks // in order to assure sorting. The sorting is critical for the // deletion of a mark as it is searched in these container for // deletion. if ( !vMarksToDelete.empty() && bMarksMoved )
{
sortSubsetMarks();
} // we just remembered the iterators to delete, so we do not need to search // for the shared_ptr<> (the entry in m_vAllMarks) again // reverse iteration, since erasing an entry invalidates iterators // behind it (the iterators in vMarksToDelete are sorted) for ( std::vector< const_iterator >::reverse_iterator pppMark = vMarksToDelete.rbegin();
pppMark != vMarksToDelete.rend();
++pppMark )
{
vDelay.push_back(deleteMark(*pppMark, pSaveBkmk != nullptr));
}
} // scope to kill vDelay
// also need to sort if both marks were moved and not-deleted because // the not-deleted marks could be in wrong order vs. the moved ones if (bIsSortingNeeded || (bMarksMoved && bMarksSkipDeletion))
{
sortMarks();
}
struct LazyFieldmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
{
std::unique_ptr<Fieldmark> m_pFieldmark;
SwDoc& m_rDoc; boolconst m_isMoveNodes;
LazyFieldmarkDeleter(Fieldmark *const pMark, SwDoc& rDoc, boolconst isMoveNodes)
: m_pFieldmark(pMark), m_rDoc(rDoc), m_isMoveNodes(isMoveNodes)
{
assert(m_pFieldmark);
} virtual ~LazyFieldmarkDeleter() override
{ // note: because of the call chain from SwUndoDelete, the field // command *cannot* be deleted here as it would create a separate // SwUndoDelete that's interleaved with the SwHistory of the outer // one - only delete the CH_TXT_ATR_FIELD*! if (!m_isMoveNodes)
{
m_pFieldmark->ReleaseDoc(m_rDoc);
}
}
};
// Call DeregisterFromDoc() lazily, because it can call selection change listeners, which // may mutate the marks container struct LazyDdeBookmarkDeleter : public IDocumentMarkAccess::ILazyDeleter
{
std::unique_ptr<DdeBookmark> m_pDdeBookmark;
SwDoc& m_rDoc;
LazyDdeBookmarkDeleter(DdeBookmark *const pDdeBookmark, SwDoc& rDoc)
: m_pDdeBookmark(pDdeBookmark), m_rDoc(rDoc)
{
assert(pDdeBookmark);
} virtual ~LazyDdeBookmarkDeleter() override
{
m_pDdeBookmark->DeregisterFromDoc(m_rDoc);
}
};
m_vBookmarks.erase(ppBookmark);
} else
{
assert(false && " - Bookmark not found in Bookmark container.");
}
} break; case IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK: case IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK:
{ autoconst ppBookmark = lcl_FindMark(m_vBookmarks, static_cast<Bookmark*>(pMark)); if ( ppBookmark != m_vBookmarks.end() )
{
m_vBookmarks.erase(ppBookmark);
} else
{
assert(false && " - Bookmark not found in Bookmark container.");
}
} break;
case IDocumentMarkAccess::MarkType::TEXT_FIELDMARK: case IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK: case IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK: case IDocumentMarkAccess::MarkType::DATE_FIELDMARK:
{ autoconst ppFieldmark = lcl_FindMark(m_vFieldmarks, static_cast<Fieldmark*>(pMark)); if ( ppFieldmark != m_vFieldmarks.end() )
{ if(m_pLastActiveFieldmark == *ppFieldmark)
ClearFieldActivation();
m_vFieldmarks.erase(ppFieldmark);
ret.reset(new LazyFieldmarkDeleter(static_cast<Fieldmark*>(pMark), m_rDoc, isMoveNodes));
pMark = nullptr;
} else
{
assert(false && " - Fieldmark not found in Fieldmark container.");
}
} break;
case IDocumentMarkAccess::MarkType::ANNOTATIONMARK:
{ autoconst ppAnnotationMark = lcl_FindMark(m_vAnnotationMarks, static_cast<AnnotationMark*>(pMark));
assert(ppAnnotationMark != m_vAnnotationMarks.end() && " - Annotation Mark not found in Annotation Mark container.");
m_vAnnotationMarks.erase(ppAnnotationMark);
} break;
case IDocumentMarkAccess::MarkType::DDE_BOOKMARK: case IDocumentMarkAccess::MarkType::NAVIGATOR_REMINDER: case IDocumentMarkAccess::MarkType::UNO_BOOKMARK: // no special marks container break;
} //Effective STL Item 27, get a non-const iterator aI at the same //position as const iterator ppMark was auto aI = m_vAllMarks.begin();
std::advance(aI, std::distance<container_t::const_iterator>(aI, ppMark));
DdeBookmark* const pDdeBookmark = dynamic_cast<DdeBookmark*>(pMark); if (pDdeBookmark)
{
ret.reset(new LazyDdeBookmarkDeleter(pDdeBookmark, m_rDoc));
pMark = nullptr;
}
m_vAllMarks.erase(aI); // delete after we remove from the list, because the destructor can // recursively call into this method. delete pMark; // If we have a lazy deleter, pMark was null-ed
return ret;
}
void MarkManager::deleteMark(const MarkBase* const pMark)
{
assert(&pMark->GetMarkPos().GetDoc() == &m_rDoc && "" " - Mark is not in my doc."); // finds the last Mark that is starting before pMark // (pMarkLow < pMark) auto [it, endIt] = equal_range(
m_vAllMarks.begin(),
m_vAllMarks.end(),
pMark->GetMarkStart(),
CompareIMarkStartsBefore<MarkBase>()); for ( ; it != endIt; ++it) if (*it == pMark)
{
deleteMark(it, false); break;
}
}
void MarkManager::clearAllMarks()
{
ClearFieldActivation();
m_vFieldmarks.clear();
m_vBookmarks.clear();
m_vAnnotationMarks.clear(); for (constauto & p : m_vAllMarks) delete p;
m_vAllMarks.clear();
}
// find the first Bookmark that does not start before
std::vector<sw::mark::Bookmark*>::const_iterator MarkManager::findFirstBookmarkNotStartsBefore(const SwPosition& rPos) const
{ return std::lower_bound(
m_vBookmarks.begin(),
m_vBookmarks.end(),
rPos,
CompareIMarkStartsBefore<Bookmark>());
}
// finds the first that is starting after
std::vector<sw::mark::Bookmark*>::const_iterator MarkManager::findFirstBookmarkStartsAfter(const SwPosition& rPos) const
{ return std::upper_bound(
m_vBookmarks.begin(),
m_vBookmarks.end(),
rPos,
CompareIMarkStartsAfter<sw::mark::Bookmark>());
}
Fieldmark* MarkManager::getInnerFieldmarkFor(const SwPosition& rPos) const
{ // find the first mark starting on or before the position in reverse order // (as we are reverse searching, this is the one closest to the position) // m_vFieldmarks should be ordered by mark start, so we can bisect with lower_bound auto itEnd = m_vFieldmarks.rend(); auto itStart = lower_bound(
m_vFieldmarks.rbegin(),
itEnd,
rPos,
CompareIMarkStartsAfterReverse()); // now continue a linear search for the first (still in reverse order) ending behind the position auto itCurrent = find_if(
itStart,
itEnd,
[&rPos](const sw::mark::MarkBase* const pMark) { return rPos < pMark->GetMarkEnd(); }); // if we reached the end (in reverse order) there is no match if(itCurrent == itEnd) return nullptr; // we found our first candidate covering the position ... auto pMark = *itCurrent; auto aMarkStartEndPair = pMark->GetMarkStartEnd(); const SwPosition* pMarkStart = &aMarkStartEndPair.first; const SwPosition* pMarkEnd = &aMarkStartEndPair.second; // ... however we still need to check if there is a smaller/'more inner' one with the same start position for(++itCurrent; itCurrent != itEnd; ++itCurrent)
{ if((*itCurrent)->GetMarkStart() < *pMarkStart) // any following mark (in reverse order) will have an earlier // start and thus can not be more 'inner' than our previous // match, so we are done. break; const SwPosition& rCurrentMarkEnd = (*itCurrent)->GetMarkEnd(); if(rPos < rCurrentMarkEnd && rCurrentMarkEnd <= *pMarkEnd)
{ // both covering the position and more inner/smaller => use this one instead
pMark = *itCurrent;
pMarkEnd = &rCurrentMarkEnd;
}
} return pMark;
}
sw::mark::Bookmark* MarkManager::getOneInnermostBookmarkFor(const SwPosition& rPos) const
{ auto it = std::find_if(m_vBookmarks.begin(), m_vBookmarks.end(),
[&rPos](const sw::mark::Bookmark* pMark)
{ return pMark->IsCoveringPosition(rPos); }); if (it == m_vBookmarks.end())
{ return nullptr;
}
sw::mark::Bookmark* pBookmark = *it;
// See if any bookmarks after the first hit are closer to rPos.
++it;
for (; it != m_vBookmarks.end(); ++it)
{ // Find the innermost bookmark. auto [/*const SwPosition&*/ rMarkStart, rMarkEnd] = (*it)->GetMarkStartEnd(); if (rMarkStart > rPos) break; if (rPos < rMarkEnd
&& (pBookmark->GetMarkStart() < rMarkStart
|| rMarkEnd < pBookmark->GetMarkEnd()))
{
pBookmark = *it;
}
} return pBookmark;
}
void MarkManager::deleteFieldmarkAt(const SwPosition& rPos)
{
Fieldmark* const pFieldmark = getFieldmarkAt(rPos);
assert(pFieldmark); // currently all callers require it to be there
::sw::mark::Fieldmark* MarkManager::changeFormFieldmarkType(::sw::mark::Fieldmark* pFieldmark, const OUString& rNewType)
{ bool bActualChange = false; if(rNewType == ODF_FORMDROPDOWN)
{ if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark))
bActualChange = true; if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown return nullptr;
} elseif(rNewType == ODF_FORMCHECKBOX)
{ if (!dynamic_cast<::sw::mark::CheckboxFieldmark*>(pFieldmark))
bActualChange = true; if (!dynamic_cast<::sw::mark::DropDownFieldmark*>(pFieldmark)) // only allowed converting between checkbox <-> dropdown return nullptr;
} elseif(rNewType == ODF_FORMDATE)
{ if (!dynamic_cast<::sw::mark::DateFieldmark*>(pFieldmark))
bActualChange = true; if (!dynamic_cast<::sw::mark::TextFieldmark*>(pFieldmark)) // only allowed converting between date field <-> text field return nullptr;
}
if (!bActualChange) return nullptr;
// Store attributes needed to create the new fieldmark
SwMarkName sName = pFieldmark->GetName();
SwPaM const aPaM(pFieldmark->GetMarkStart());
// Remove the old fieldmark and create a new one with the new type if (rNewType == ODF_FORMDROPDOWN || rNewType == ODF_FORMCHECKBOX)
{
SwPosition aNewPos (*aPaM.GetPoint());
deleteFieldmarkAt(aNewPos); return makeNoTextFieldBookmark(aPaM, sName, rNewType);
} elseif(rNewType == ODF_FORMDATE)
{
SwPosition aPos (*aPaM.GetPoint());
SwPaM aNewPaM(pFieldmark->GetMarkStart(), pFieldmark->GetMarkEnd());
deleteFieldmarkAt(aPos); // HACK: hard-code the separator position here at the start because // writerfilter put it in the wrong place (at the end) on attach()
SwPosition const sepPos(*aNewPaM.Start()); return makeFieldBookmark(aNewPaM, sName, rNewType, &sepPos);
} return nullptr;
}
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.