summaryrefslogtreecommitdiffstats
path: root/sw/source/core/tox
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/tox
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/tox')
-rw-r--r--sw/source/core/tox/ToxLinkProcessor.cxx90
-rw-r--r--sw/source/core/tox/ToxTabStopTokenHandler.cxx128
-rw-r--r--sw/source/core/tox/ToxTextGenerator.cxx468
-rw-r--r--sw/source/core/tox/ToxWhitespaceStripper.cxx63
-rw-r--r--sw/source/core/tox/tox.cxx949
-rw-r--r--sw/source/core/tox/toxhlp.cxx119
-rw-r--r--sw/source/core/tox/txmsrt.cxx989
7 files changed, 2806 insertions, 0 deletions
diff --git a/sw/source/core/tox/ToxLinkProcessor.cxx b/sw/source/core/tox/ToxLinkProcessor.cxx
new file mode 100644
index 000000000..731a7c8a8
--- /dev/null
+++ b/sw/source/core/tox/ToxLinkProcessor.cxx
@@ -0,0 +1,90 @@
+/* -*- 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/.
+ */
+
+#include <memory>
+#include <ToxLinkProcessor.hxx>
+
+#include <SwStyleNameMapper.hxx>
+#include <ndtxt.hxx>
+#include <sal/log.hxx>
+#include <rtl/uri.hxx>
+
+namespace sw {
+
+void
+ToxLinkProcessor::StartNewLink(sal_Int32 startPosition, const OUString& characterStyle)
+{
+ SAL_INFO_IF(m_pStartedLink, "sw.core", "ToxLinkProcessor: LS without LE");
+ m_pStartedLink = std::make_unique<StartedLink>(
+ startPosition, characterStyle);
+}
+
+void ToxLinkProcessor::CloseLink(sal_Int32 endPosition, const OUString& url, bool bRelative)
+{
+ if (m_pStartedLink == nullptr)
+ {
+ SAL_INFO("sw.core", "ToxLinkProcessor: LE without LS");
+ return;
+ }
+
+ if (url.isEmpty()) {
+ return;
+ }
+
+ OUString uri;
+ if (bRelative)
+ {
+ // url contains '|' which must be encoded; also in some cases contains
+ // arbitrary strings that need to be encoded
+ assert(url[0] == '#'); // all links are internal
+ uri = "#"
+ + rtl::Uri::encode(url.copy(1), rtl_UriCharClassUricNoSlash,
+ rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ uri = url;
+ }
+
+ std::unique_ptr<ClosedLink> pClosedLink(
+ new ClosedLink(uri, m_pStartedLink->mStartPosition, endPosition));
+
+ const OUString& characterStyle = m_pStartedLink->mCharacterStyle;
+ sal_uInt16 poolId = ObtainPoolId(characterStyle);
+ pClosedLink->mINetFormat.SetVisitedFormatAndId(characterStyle, poolId);
+ pClosedLink->mINetFormat.SetINetFormatAndId(characterStyle, poolId);
+
+ m_ClosedLinks.push_back(std::move(pClosedLink));
+ m_pStartedLink.reset();
+}
+
+sal_uInt16
+ToxLinkProcessor::ObtainPoolId(const OUString& characterStyle) const
+{
+ if (characterStyle.isEmpty()) {
+ return USHRT_MAX;
+ }
+ else {
+ return SwStyleNameMapper::GetPoolIdFromUIName(characterStyle, SwGetPoolIdFromName::ChrFmt);
+ }
+}
+
+
+void
+ToxLinkProcessor::InsertLinkAttributes(SwTextNode& node)
+{
+ for (auto const& clink : m_ClosedLinks)
+ {
+ node.InsertItem(clink->mINetFormat, clink->mStartTextPos, clink->mEndTextPos);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxTabStopTokenHandler.cxx b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
new file mode 100644
index 000000000..a89e9ad27
--- /dev/null
+++ b/sw/source/core/tox/ToxTabStopTokenHandler.cxx
@@ -0,0 +1,128 @@
+/* -*- 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/.
+ */
+
+#include <ToxTabStopTokenHandler.hxx>
+
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/boxitem.hxx>
+
+#include <cntfrm.hxx>
+#include <fmtfsize.hxx>
+#include <fmtpdsc.hxx>
+#include <frmfmt.hxx>
+#include <frmatr.hxx>
+#include <ndtxt.hxx>
+#include <pagedesc.hxx>
+#include <pagefrm.hxx>
+#include <swrect.hxx>
+#include <tox.hxx>
+
+namespace sw {
+
+DefaultToxTabStopTokenHandler::DefaultToxTabStopTokenHandler(SwNodeOffset indexOfSectionNode,
+ const SwPageDesc& defaultPageDescription,
+ bool tabPositionIsRelativeToParagraphIndent,
+ TabStopReferencePolicy referencePolicy)
+: mIndexOfSectionNode(indexOfSectionNode),
+ mDefaultPageDescription(defaultPageDescription),
+ mTabPositionIsRelativeToParagraphIndent(tabPositionIsRelativeToParagraphIndent),
+ mTabStopReferencePolicy(referencePolicy)
+{
+}
+
+
+ToxTabStopTokenHandler::HandledTabStopToken
+DefaultToxTabStopTokenHandler::HandleTabStopToken(
+ const SwFormToken& aToken, const SwTextNode& targetNode, const SwRootFrame *currentLayout) const
+{
+ HandledTabStopToken result;
+
+ if (aToken.bWithTab) { // #i21237#
+ result.text = "\t";
+ }
+
+ // check whether a tab adjustment has been specified.
+ if (SvxTabAdjust::End > aToken.eTabAlign) {
+ const SvxLRSpaceItem& rLR = targetNode.SwContentNode::GetAttr(RES_LR_SPACE);
+
+ tools::Long nTabPosition = aToken.nTabStopPosition;
+ if (!mTabPositionIsRelativeToParagraphIndent && rLR.GetTextLeft()) {
+ nTabPosition -= rLR.GetTextLeft();
+ }
+ result.tabStop = SvxTabStop(nTabPosition, aToken.eTabAlign, cDfltDecimalChar, aToken.cTabFillChar);
+ return result;
+ }
+
+ SwRect aNdRect;
+ if (CanUseLayoutRectangle(targetNode, currentLayout)) {
+ aNdRect = targetNode.FindLayoutRect(true);
+ }
+ tools::Long nRightMargin;
+ if (aNdRect.IsEmpty()) {
+ nRightMargin = CalculatePageMarginFromPageDescription(targetNode);
+ } else {
+ nRightMargin = aNdRect.Width();
+ }
+ //#i24363# tab stops relative to indent
+ if (mTabStopReferencePolicy == TABSTOPS_RELATIVE_TO_INDENT) {
+ // left margin of paragraph style
+ const SvxLRSpaceItem& rLRSpace = targetNode.GetTextColl()->GetLRSpace();
+ nRightMargin -= rLRSpace.GetLeft();
+ nRightMargin -= rLRSpace.GetTextFirstLineOffset();
+ }
+
+ result.tabStop = SvxTabStop(nRightMargin, SvxTabAdjust::Right, cDfltDecimalChar, aToken.cTabFillChar);
+ return result;
+}
+
+tools::Long
+DefaultToxTabStopTokenHandler::CalculatePageMarginFromPageDescription(const SwTextNode& targetNode) const
+{
+ SwNodeOffset nPgDescNdIdx = targetNode.GetIndex() + 1;
+ const SwPageDesc *pPageDesc = targetNode.FindPageDesc(&nPgDescNdIdx);
+ if (!pPageDesc || nPgDescNdIdx < mIndexOfSectionNode) {
+ // Use default page description, if none is found or the found one is given by a Node before the
+ // table-of-content section.
+ pPageDesc = &mDefaultPageDescription;
+ }
+ const SwFrameFormat& rPgDscFormat = pPageDesc->GetMaster();
+ tools::Long result = rPgDscFormat.GetFrameSize().GetWidth() - rPgDscFormat.GetLRSpace().GetLeft()
+ - rPgDscFormat.GetLRSpace().GetRight();
+ // Also consider borders
+ const SvxBoxItem& rBox = rPgDscFormat.GetBox();
+ result -= rBox.CalcLineSpace(SvxBoxItemLine::LEFT) + rBox.CalcLineSpace(SvxBoxItemLine::RIGHT);
+ return result;
+}
+
+
+/*static*/ bool
+DefaultToxTabStopTokenHandler::CanUseLayoutRectangle(const SwTextNode& targetNode, const SwRootFrame *currentLayout)
+{
+ const SwPageDesc* pageDescription =
+ targetNode.SwContentNode::GetAttr(RES_PAGEDESC).GetPageDesc();
+
+ if (!pageDescription) {
+ return false;
+ }
+ const SwFrame* pFrame = targetNode.getLayoutFrame(currentLayout);
+ if (!pFrame) {
+ return false;
+ }
+ pFrame = pFrame->FindPageFrame();
+ if (!pFrame) {
+ return false;
+ }
+ const SwPageFrame* pageFrame = static_cast<const SwPageFrame*>(pFrame);
+ return pageDescription == pageFrame->GetPageDesc();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxTextGenerator.cxx b/sw/source/core/tox/ToxTextGenerator.cxx
new file mode 100644
index 000000000..82adcbca6
--- /dev/null
+++ b/sw/source/core/tox/ToxTextGenerator.cxx
@@ -0,0 +1,468 @@
+/* -*- 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 <ToxTextGenerator.hxx>
+
+#include <chpfld.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <ndindex.hxx>
+#include <fchrfmt.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <ndtxt.hxx>
+#include <tox.hxx>
+#include <txmsrt.hxx>
+#include <fmtautofmt.hxx>
+#include <swatrset.hxx>
+#include <ToxWhitespaceStripper.hxx>
+#include <ToxLinkProcessor.hxx>
+#include <ToxTabStopTokenHandler.hxx>
+#include <txatbase.hxx>
+#include <modeltoviewhelper.hxx>
+#include <strings.hrc>
+
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <svl/itemiter.hxx>
+
+#include <cassert>
+#include <memory>
+
+namespace {
+
+bool sortTabHasNoToxSourcesOrFirstToxSourceHasNoNode(const SwTOXSortTabBase& sortTab)
+{
+ if (sortTab.aTOXSources.empty()) {
+ return true;
+ }
+ if (sortTab.aTOXSources.at(0).pNd == nullptr) {
+ return true;
+ }
+ return false;
+}
+
+} // end anonymous namespace
+
+namespace sw {
+
+OUString
+ToxTextGenerator::GetNumStringOfFirstNode(const SwTOXSortTabBase& rBase,
+ bool bUsePrefix, sal_uInt8 nLevel,
+ SwRootFrame const*const pLayout)
+{
+ if (sortTabHasNoToxSourcesOrFirstToxSourceHasNoNode(rBase)) {
+ return OUString();
+ }
+
+ OUString sRet;
+ if (rBase.pTextMark) { // only if it's not a Mark
+ return sRet;
+ }
+
+ const SwTextNode* pNd = rBase.aTOXSources[0].pNd->GetTextNode();
+ if (!pNd) {
+ return sRet;
+ }
+ if (pLayout && pLayout->HasMergedParas())
+ { // note: pNd could be any node, since it could be Sequence etc.
+ pNd = sw::GetParaPropsNode(*pLayout, SwNodeIndex(*pNd));
+ }
+
+ const SwNumRule* pRule = pNd->GetNumRule();
+ if (!pRule) {
+ return sRet;
+ }
+
+ if (pNd->GetActualListLevel() < MAXLEVEL) {
+ sRet = pNd->GetNumString(bUsePrefix, nLevel, pLayout);
+ }
+
+ if (!sRet.isEmpty()) {
+ sRet += " ";// Makes sure spacing is done only when there is outline numbering
+ }
+
+ return sRet;
+}
+
+
+ToxTextGenerator::ToxTextGenerator(const SwForm& toxForm,
+ std::shared_ptr<ToxTabStopTokenHandler> const & tabStopHandler)
+: mToxForm(toxForm),
+ mLinkProcessor(std::make_shared<ToxLinkProcessor>()),
+ mTabStopTokenHandler(tabStopHandler)
+{}
+
+ToxTextGenerator::~ToxTextGenerator()
+{}
+
+OUString
+ToxTextGenerator::HandleChapterToken(const SwTOXSortTabBase& rBase,
+ const SwFormToken& aToken, SwRootFrame const*const pLayout) const
+{
+ if (sortTabHasNoToxSourcesOrFirstToxSourceHasNoNode(rBase)) {
+ return OUString();
+ }
+
+ // A bit tricky: Find a random Frame
+ const SwContentNode* contentNode = rBase.aTOXSources.at(0).pNd->GetContentNode();
+ if (!contentNode) {
+ return OUString();
+ }
+
+ // #i53420#
+ const SwContentFrame* contentFrame = contentNode->getLayoutFrame(pLayout);
+ if (!contentFrame) {
+ return OUString();
+ }
+
+ return GenerateTextForChapterToken(aToken, contentFrame, contentNode, pLayout);
+}
+
+OUString
+ToxTextGenerator::GenerateTextForChapterToken(const SwFormToken& chapterToken, const SwContentFrame* contentFrame,
+ const SwContentNode *contentNode,
+ SwRootFrame const*const pLayout) const
+{
+ OUString retval;
+
+ SwChapterFieldType chapterFieldType;
+ SwChapterField aField = ObtainChapterField(&chapterFieldType, &chapterToken, contentFrame, contentNode);
+
+ //---> #i89791#
+ // continue to support CF_NUMBER and CF_NUM_TITLE in order to handle ODF 1.0/1.1 written by OOo 3.x
+ // in the same way as OOo 2.x would handle them.
+ if (CF_NUM_NOPREPST_TITLE == chapterToken.nChapterFormat || CF_NUMBER == chapterToken.nChapterFormat) {
+ retval += aField.GetNumber(pLayout); // get the string number without pre/postfix
+ }
+ else if (CF_NUMBER_NOPREPST == chapterToken.nChapterFormat || CF_NUM_TITLE == chapterToken.nChapterFormat) {
+ retval += aField.GetNumber(pLayout) + " " + aField.GetTitle(pLayout);
+ } else if (CF_TITLE == chapterToken.nChapterFormat) {
+ retval += aField.GetTitle(pLayout);
+ }
+ return retval;
+}
+
+// Add parameter <_TOXSectNdIdx> and <_pDefaultPageDesc> in order to control,
+// which page description is used, no appropriate one is found.
+void
+ToxTextGenerator::GenerateText(SwDoc* pDoc,
+ std::unordered_map<OUString, int> & rMarkURLs,
+ const std::vector<std::unique_ptr<SwTOXSortTabBase>> &entries,
+ sal_uInt16 indexOfEntryToProcess, sal_uInt16 numberOfEntriesToProcess,
+ SwRootFrame const*const pLayout)
+{
+ // pTOXNd is only set at the first mark
+ SwTextNode* pTOXNd = const_cast<SwTextNode*>(entries.at(indexOfEntryToProcess)->pTOXNd);
+ // FIXME this operates directly on the node text
+ OUString & rText = const_cast<OUString&>(pTOXNd->GetText());
+ rText.clear();
+ for(sal_uInt16 nIndex = indexOfEntryToProcess; nIndex < indexOfEntryToProcess + numberOfEntriesToProcess; nIndex++)
+ {
+ if(nIndex > indexOfEntryToProcess)
+ rText += ", "; // comma separation
+ // Initialize String with the Pattern from the form
+ const SwTOXSortTabBase& rBase = *entries.at(nIndex);
+ sal_uInt16 nLvl = rBase.GetLevel();
+ OSL_ENSURE( nLvl < mToxForm.GetFormMax(), "invalid FORM_LEVEL");
+
+ SvxTabStopItem aTStops( 0, 0, SvxTabAdjust::Default, RES_PARATR_TABSTOP );
+ // create an enumerator
+ // #i21237#
+ SwFormTokens aPattern = mToxForm.GetPattern(nLvl);
+ // remove text from node
+ for(const auto& aToken : aPattern) // #i21237#
+ {
+ sal_Int32 nStartCharStyle = rText.getLength();
+ OUString aCharStyleName = aToken.sCharStyleName;
+ switch( aToken.eTokenType )
+ {
+ case TOKEN_ENTRY_NO:
+ // for TOC numbering
+ rText += GetNumStringOfFirstNode(rBase,
+ aToken.nChapterFormat == CF_NUMBER,
+ static_cast<sal_uInt8>(aToken.nOutlineLevel - 1), pLayout);
+ break;
+
+ case TOKEN_ENTRY_TEXT: {
+ HandledTextToken htt = HandleTextToken(rBase, pDoc->GetAttrPool(), pLayout);
+ ApplyHandledTextToken(htt, *pTOXNd);
+ }
+ break;
+
+ case TOKEN_ENTRY:
+ {
+ // for TOC numbering
+ rText += GetNumStringOfFirstNode(rBase, true, MAXLEVEL, pLayout);
+ HandledTextToken htt = HandleTextToken(rBase, pDoc->GetAttrPool(), pLayout);
+ ApplyHandledTextToken(htt, *pTOXNd);
+ }
+ break;
+
+ case TOKEN_TAB_STOP: {
+ ToxTabStopTokenHandler::HandledTabStopToken htst =
+ mTabStopTokenHandler->HandleTabStopToken(aToken, *pTOXNd, pDoc->getIDocumentLayoutAccess().GetCurrentLayout());
+ rText += htst.text;
+ aTStops.Insert(htst.tabStop);
+ break;
+ }
+
+ case TOKEN_TEXT:
+ rText += aToken.sText;
+ break;
+
+ case TOKEN_PAGE_NUMS:
+ rText += ConstructPageNumberPlaceholder(rBase.aTOXSources.size());
+ break;
+
+ case TOKEN_CHAPTER_INFO:
+ rText += HandleChapterToken(rBase, aToken, pLayout);
+ break;
+
+ case TOKEN_LINK_START:
+ mLinkProcessor->StartNewLink(rText.getLength(), aToken.sCharStyleName);
+ break;
+
+ case TOKEN_LINK_END:
+ {
+ auto [url, isMark] = rBase.GetURL(pLayout);
+ if (isMark)
+ {
+ auto [iter, _] = rMarkURLs.emplace(url, 0);
+ (void) _; // sigh... ignore it more explicitly
+ ++iter->second;
+ url = "#" + OUString::number(iter->second) + url;
+ }
+ mLinkProcessor->CloseLink(rText.getLength(), url, /*bRelative=*/true);
+ }
+ break;
+
+ case TOKEN_AUTHORITY:
+ {
+ ToxAuthorityField eField = static_cast<ToxAuthorityField>(aToken.nAuthorityField);
+ SwIndex aIdx( pTOXNd, rText.getLength() );
+ if (eField == ToxAuthorityField::AUTH_FIELD_URL)
+ {
+ aCharStyleName = SwResId(STR_POOLCHR_INET_NORMAL);
+ mLinkProcessor->StartNewLink(rText.getLength(), aCharStyleName);
+ }
+ rBase.FillText( *pTOXNd, aIdx, o3tl::narrowing<sal_uInt16>(eField), pLayout );
+ if (eField == ToxAuthorityField::AUTH_FIELD_URL)
+ {
+ // Get the absolute URL, the text may be a relative one.
+ const auto& rAuthority = static_cast<const SwTOXAuthority&>(rBase);
+ OUString aURL = SwTOXAuthority::GetSourceURL(
+ rAuthority.GetText(AUTH_FIELD_URL, pLayout));
+
+ mLinkProcessor->CloseLink(rText.getLength(), aURL, /*bRelative=*/false);
+ }
+ }
+ break;
+ case TOKEN_END: break;
+ }
+
+ if (!aCharStyleName.isEmpty())
+ {
+ SwCharFormat* pCharFormat;
+ if( USHRT_MAX != aToken.nPoolId )
+ pCharFormat = pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool( aToken.nPoolId );
+ else
+ pCharFormat = pDoc->FindCharFormatByName(aCharStyleName);
+
+ if (pCharFormat)
+ {
+ SwFormatCharFormat aFormat( pCharFormat );
+ pTOXNd->InsertItem( aFormat, nStartCharStyle,
+ rText.getLength(), SetAttrMode::DONTEXPAND );
+ }
+ }
+ }
+
+ pTOXNd->SetAttr( aTStops );
+ }
+ mLinkProcessor->InsertLinkAttributes(*pTOXNd);
+}
+
+/*static*/ std::shared_ptr<SfxItemSet>
+ToxTextGenerator::CollectAttributesForTox(const SwTextAttr& hint, SwAttrPool& pool)
+{
+ auto retval = std::make_shared<SfxItemSet>(pool);
+ if (hint.Which() != RES_TXTATR_AUTOFMT) {
+ return retval;
+ }
+ const SwFormatAutoFormat& afmt = hint.GetAutoFormat();
+ SfxItemIter aIter( *afmt.GetStyleHandle());
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ do
+ {
+ if (pItem->Which() == RES_CHRATR_ESCAPEMENT ||
+ pItem->Which() == RES_CHRATR_POSTURE ||
+ pItem->Which() == RES_CHRATR_CJK_POSTURE ||
+ pItem->Which() == RES_CHRATR_CTL_POSTURE)
+ {
+ retval->Put(std::unique_ptr<SfxPoolItem>(pItem->Clone()));
+ }
+ pItem = aIter.NextItem();
+ } while (pItem);
+ return retval;
+}
+
+void ToxTextGenerator::GetAttributesForNode(
+ ToxTextGenerator::HandledTextToken & rResult,
+ sal_Int32 & rOffset,
+ SwTextNode const& rNode,
+ ToxWhitespaceStripper const& rStripper,
+ SwAttrPool & rPool,
+ SwRootFrame const*const pLayout)
+{
+ // note: this *must* use the same flags as SwTextNode::GetExpandText()
+ // or indexes will be off!
+ ExpandMode eMode = ExpandMode::ExpandFields | ExpandMode::HideFieldmarkCommands;
+ if (pLayout && pLayout->IsHideRedlines())
+ {
+ eMode |= ExpandMode::HideDeletions;
+ }
+ ModelToViewHelper aConversionMap(rNode, pLayout, eMode);
+ if (SwpHints const*const pHints = rNode.GetpSwpHints())
+ {
+ for (size_t i = 0; i < pHints->Count(); ++i)
+ {
+ const SwTextAttr* pHint = pHints->Get(i);
+ std::shared_ptr<SfxItemSet> attributesToClone =
+ CollectAttributesForTox(*pHint, rPool);
+ if (attributesToClone->Count() <= 0) {
+ continue;
+ }
+
+ // sw_redlinehide: due to the ... interesting ... multi-level index
+ // mapping going on here, can't use the usual merged attr iterators :(
+
+ sal_Int32 const nStart(aConversionMap.ConvertToViewPosition(pHint->GetStart()));
+ sal_Int32 const nEnd(aConversionMap.ConvertToViewPosition(pHint->GetAnyEnd()));
+ if (nStart != nEnd) // might be in delete redline, and useless anyway
+ {
+ std::unique_ptr<SwFormatAutoFormat> pClone(pHint->GetAutoFormat().Clone());
+ pClone->SetStyleHandle(attributesToClone);
+ rResult.autoFormats.push_back(std::move(pClone));
+ // note the rStripper is on the whole merged text, so need rOffset
+ rResult.startPositions.push_back(
+ rStripper.GetPositionInStrippedString(rOffset + nStart));
+ rResult.endPositions.push_back(
+ rStripper.GetPositionInStrippedString(rOffset + nEnd));
+ }
+ }
+ }
+ rOffset += aConversionMap.getViewText().getLength();
+}
+
+ToxTextGenerator::HandledTextToken
+ToxTextGenerator::HandleTextToken(const SwTOXSortTabBase& source,
+ SwAttrPool& pool, SwRootFrame const*const pLayout)
+{
+ HandledTextToken result;
+ ToxWhitespaceStripper stripper(source.GetText().sText);
+ result.text = stripper.GetStrippedString();
+
+ // FIXME: there is a pre-existing problem that the index mapping of the
+ // attributes only works if the paragraph is fully selected
+ if (!source.IsFullPara() || source.aTOXSources.empty())
+ return result;
+
+ const SwTextNode* pSrc = source.aTOXSources.front().pNd->GetTextNode();
+ if (!pSrc)
+ {
+ return result;
+ }
+
+ sal_Int32 nOffset(0);
+ GetAttributesForNode(result, nOffset, *pSrc, stripper, pool, pLayout);
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pSrc->getLayoutFrame(pLayout)))
+ {
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ // pSrc already copied above
+ assert(pSrc == pMerged->pParaPropsNode);
+ for (SwNodeOffset i = pSrc->GetIndex() + 1;
+ i <= pMerged->pLastNode->GetIndex(); ++i)
+ {
+ SwNode *const pTmp(pSrc->GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+ GetAttributesForNode(result, nOffset,
+ *pTmp->GetTextNode(), stripper, pool, pLayout);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+/*static*/ void
+ToxTextGenerator::ApplyHandledTextToken(const HandledTextToken& htt, SwTextNode& targetNode)
+{
+ sal_Int32 offset = targetNode.GetText().getLength();
+ SwIndex aIdx(&targetNode, offset);
+ targetNode.InsertText(htt.text, aIdx);
+ for (size_t i=0; i < htt.autoFormats.size(); ++i) {
+ targetNode.InsertItem(*htt.autoFormats.at(i),
+ htt.startPositions.at(i) + offset,
+ htt.endPositions.at(i) + offset);
+ }
+}
+
+/*static*/ OUString
+ToxTextGenerator::ConstructPageNumberPlaceholder(size_t numberOfToxSources)
+{
+ if (numberOfToxSources == 0) {
+ return OUString();
+ }
+ OUStringBuffer retval;
+ // Place holder for the PageNumber; we only respect the first one
+ retval.append(C_NUM_REPL);
+ for (size_t i = 1; i < numberOfToxSources; ++i) {
+ retval.append(SwTOXMark::S_PAGE_DELI);
+ retval.append(C_NUM_REPL);
+ }
+ retval.append(C_END_PAGE_NUM);
+ return retval.makeStringAndClear();
+}
+
+/*virtual*/ SwChapterField
+ToxTextGenerator::ObtainChapterField(SwChapterFieldType* chapterFieldType,
+ const SwFormToken* chapterToken, const SwContentFrame* contentFrame,
+ const SwContentNode* contentNode) const
+{
+ assert(chapterToken);
+ assert(chapterToken->nOutlineLevel >= 1);
+
+ SwChapterField retval(chapterFieldType, chapterToken->nChapterFormat);
+ retval.SetLevel(static_cast<sal_uInt8>(chapterToken->nOutlineLevel - 1));
+ // #i53420#
+ retval.ChangeExpansion(*contentFrame, contentNode, true);
+ return retval;
+}
+} // end namespace sw
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/ToxWhitespaceStripper.cxx b/sw/source/core/tox/ToxWhitespaceStripper.cxx
new file mode 100644
index 000000000..28c9056ce
--- /dev/null
+++ b/sw/source/core/tox/ToxWhitespaceStripper.cxx
@@ -0,0 +1,63 @@
+/* -*- 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/.
+ */
+
+#include <ToxWhitespaceStripper.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+
+
+namespace sw {
+
+ToxWhitespaceStripper::ToxWhitespaceStripper(const OUString& inputString)
+{
+ OUStringBuffer buffer;
+
+ bool lastCharacterWasWhitespace = false;
+ for (sal_Int32 pos = 0; pos < inputString.getLength(); ++pos) {
+ sal_Unicode cur = inputString[pos];
+
+ if (cur == ' ' || cur == '\n' || cur == '\t') {
+ // merge consecutive whitespaces (and translate them to spaces)
+ if (!lastCharacterWasWhitespace) {
+ buffer.append(' ');
+ }
+ lastCharacterWasWhitespace = true;
+ }
+ else {
+ buffer.append(cur);
+ lastCharacterWasWhitespace = false;
+ }
+ mNewPositions.push_back(buffer.getLength()-1);
+ }
+ // strip the last whitespace (if there was one)
+ if (lastCharacterWasWhitespace) {
+ buffer.truncate(buffer.getLength() - 1);
+ }
+ mNewPositions.push_back(buffer.getLength());
+ mStripped = buffer.makeStringAndClear();
+}
+
+
+sal_Int32
+ToxWhitespaceStripper::GetPositionInStrippedString(sal_Int32 pos) const
+{
+ assert(0 <= pos);
+ if (o3tl::make_unsigned(pos) >= mNewPositions.size()) {
+ // TODO probably this should assert, not just warn?
+ SAL_WARN("sw.core", "Requested position of TOX entry text which does not exist. "
+ "Maybe the formatting hint is corrupt?");
+ return mNewPositions.back();
+ }
+ return mNewPositions.at(pos);
+}
+
+
+}
diff --git a/sw/source/core/tox/tox.cxx b/sw/source/core/tox/tox.cxx
new file mode 100644
index 000000000..53fc36d38
--- /dev/null
+++ b/sw/source/core/tox/tox.cxx
@@ -0,0 +1,949 @@
+/* -*- 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 <calbck.hxx>
+#include <doc.hxx>
+#include <docary.hxx>
+#include <editeng/tstpitem.hxx>
+#include <hintids.hxx>
+#include <hints.hxx>
+#include <ndtxt.hxx>
+#include <paratr.hxx>
+#include <rootfrm.hxx>
+#include <scriptinfo.hxx>
+#include <strings.hrc>
+#include <swtypes.hxx>
+#include <tox.hxx>
+#include <txtfrm.hxx>
+#include <txttxmrk.hxx>
+
+#include <optional>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+#include <string_view>
+
+
+const sal_Unicode C_NUM_REPL = '@';
+const sal_Unicode C_END_PAGE_NUM = '~';
+
+namespace
+{
+void lcl_FillAuthPattern(SwFormTokens &rAuthTokens, sal_uInt16 nTypeId)
+{
+ rAuthTokens.reserve(9); // Worst case: Start+Sep1+Auth+3*(Sep2+Auth)
+
+ SwFormToken aStartToken( TOKEN_AUTHORITY );
+ aStartToken.nAuthorityField = AUTH_FIELD_IDENTIFIER;
+ rAuthTokens.push_back( aStartToken );
+ SwFormToken aSeparatorToken( TOKEN_TEXT );
+ aSeparatorToken.sText = ": ";
+ rAuthTokens.push_back( aSeparatorToken );
+
+ --nTypeId; // compensate +1 offset introduced by caller
+
+ SwFormToken aTextToken( TOKEN_TEXT );
+ aTextToken.sText = ", ";
+
+ const ToxAuthorityField nVals[4] = {
+ AUTH_FIELD_AUTHOR,
+ AUTH_FIELD_TITLE,
+ AUTH_FIELD_YEAR,
+ nTypeId == AUTH_TYPE_WWW ? AUTH_FIELD_URL : AUTH_FIELD_END
+ };
+
+ for(size_t i = 0; i < SAL_N_ELEMENTS(nVals); ++i)
+ {
+ if(nVals[i] == AUTH_FIELD_END)
+ break;
+ if( i > 0 )
+ rAuthTokens.push_back( aTextToken );
+
+ // -> #i21237#
+ SwFormToken aToken(TOKEN_AUTHORITY);
+
+ aToken.nAuthorityField = nVals[i];
+ rAuthTokens.push_back(aToken);
+ // <- #i21237#
+ }
+}
+}
+
+/// pool default constructor
+SwTOXMark::SwTOXMark()
+ : SfxPoolItem(RES_TXTATR_TOXMARK)
+ , m_pType(nullptr)
+ , m_pTextAttr(nullptr)
+ , m_nLevel(0)
+ , m_bAutoGenerated(false)
+ , m_bMainEntry(false)
+{
+}
+
+SwTOXMark::SwTOXMark(const SwTOXType* pType)
+ : SfxPoolItem(RES_TXTATR_TOXMARK)
+ , m_pType(pType)
+ , m_pTextAttr(nullptr)
+ , m_nLevel(0)
+ , m_bAutoGenerated(false)
+ , m_bMainEntry(false)
+{
+ StartListening(const_cast<SwTOXType*>(m_pType)->GetNotifier());
+}
+
+SwTOXMark::SwTOXMark(const SwTOXMark& rCopy)
+ : SfxPoolItem(RES_TXTATR_TOXMARK)
+ , SvtListener()
+ , m_pType(rCopy.m_pType)
+ , m_aPrimaryKey(rCopy.m_aPrimaryKey)
+ , m_aSecondaryKey(rCopy.m_aSecondaryKey)
+ , m_aTextReading(rCopy.m_aTextReading)
+ , m_aPrimaryKeyReading(rCopy.m_aPrimaryKeyReading)
+ , m_aSecondaryKeyReading(rCopy.m_aSecondaryKeyReading)
+ , m_pTextAttr(nullptr)
+ , m_nLevel(rCopy.m_nLevel)
+ , m_bAutoGenerated(rCopy.m_bAutoGenerated)
+ , m_bMainEntry(rCopy.m_bMainEntry)
+{
+ if(m_pType)
+ StartListening(const_cast<SwTOXType*>(m_pType)->GetNotifier());
+ // Copy AlternativString
+ m_aAltText = rCopy.m_aAltText;
+}
+
+SwTOXMark::~SwTOXMark()
+{
+}
+
+void SwTOXMark::RegisterToTOXType(SwTOXType& rType)
+{
+ SvtListener::EndListeningAll();
+ m_pType = &rType;
+ StartListening(rType.GetNotifier());
+}
+
+bool SwTOXMark::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ return m_pType == static_cast<const SwTOXMark&>(rAttr).m_pType;
+}
+
+SwTOXMark* SwTOXMark::Clone( SfxItemPool* ) const
+{
+ return new SwTOXMark( *this );
+}
+
+void SwTOXMark::Notify(const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::SwLegacyModify)
+ {
+ auto pLegacyHint = static_cast<const sw::LegacyModifyHint*>(&rHint);
+ CallSwClientNotify(rHint);
+ if (pLegacyHint->m_pOld && (RES_REMOVE_UNO_OBJECT == pLegacyHint->m_pOld->Which()))
+ SetXTOXMark(css::uno::Reference<css::text::XDocumentIndexMark>(nullptr));
+ } else if (auto pCollectHint = dynamic_cast<const sw::CollectTextMarksHint*>(&rHint))
+ {
+ if(GetTextTOXMark())
+ pCollectHint->m_rMarks.push_back(this);
+ } else if (auto pCollectLayoutHint = dynamic_cast<const sw::CollectTextTOXMarksForLayoutHint*>(&rHint))
+ {
+ if(!GetTextTOXMark())
+ return;
+ auto& rTextMark = *GetTextTOXMark();
+ auto& rNode = rTextMark.GetTextNode();
+ auto pLayout = pCollectLayoutHint->m_pLayout;
+ // Check basic sanity and that it is part of our layout and not in undo
+ if(!rNode.GetNodes().IsDocNodes() || !rNode.GetText().getLength() || !rNode.HasWriterListeners() || !rNode.getLayoutFrame(pLayout))
+ return;
+ // Check for being hidden
+ if(rNode.IsHiddenByParaField() || SwScriptInfo::IsInHiddenRange(rNode, rTextMark.GetStart()))
+ return;
+ // Check for being hidden by hidden redlines
+ if (pLayout && pLayout->HasMergedParas() && sw::IsMarkHintHidden(*pLayout, rNode, rTextMark))
+ return;
+ pCollectLayoutHint->m_rMarks.push_back(rTextMark);
+ }
+}
+
+void SwTOXMark::InvalidateTOXMark()
+{
+ const SwPtrMsgPoolItem aMsgHint(RES_REMOVE_UNO_OBJECT, &static_cast<sw::BroadcastingModify&>(*this));
+ CallSwClientNotify(sw::LegacyModifyHint(&aMsgHint, &aMsgHint));
+}
+
+OUString SwTOXMark::GetText(SwRootFrame const*const pLayout) const
+{
+ if( !m_aAltText.isEmpty() )
+ return m_aAltText;
+
+ if( m_pTextAttr && m_pTextAttr->GetpTextNd() )
+ {
+ const sal_Int32* pEndIdx = m_pTextAttr->GetEnd();
+ OSL_ENSURE( pEndIdx, "TOXMark without mark!");
+ if( pEndIdx )
+ {
+ const sal_Int32 nStt = m_pTextAttr->GetStart();
+ return m_pTextAttr->GetpTextNd()->GetExpandText(pLayout, nStt, *pEndIdx-nStt);
+ }
+ }
+
+ return OUString();
+}
+
+// Manage types of TOX
+SwTOXType::SwTOXType(SwDoc& rDoc, TOXTypes eTyp, const OUString& rName)
+ : m_rDoc(rDoc)
+ , m_aName(rName)
+ , m_eType(eTyp)
+{
+}
+
+SwTOXType::SwTOXType(const SwTOXType& rCopy)
+ : m_rDoc(rCopy.m_rDoc)
+ , m_aName(rCopy.m_aName)
+ , m_eType(rCopy.m_eType)
+{
+ if (auto pRegisteredIn = const_cast<SwTOXType&>(rCopy).GetRegisteredIn())
+ pRegisteredIn->Add(this);
+}
+
+const TranslateId STR_POOLCOLL_TOX_ARY[] =
+{
+ // Subcategory Index-Directories
+ STR_POOLCOLL_TOX_IDXH,
+ STR_POOLCOLL_TOX_IDX1,
+ STR_POOLCOLL_TOX_IDX2,
+ STR_POOLCOLL_TOX_IDX3,
+ STR_POOLCOLL_TOX_IDXBREAK
+};
+
+const TranslateId STR_POOLCOLL_TOX_CNTNT_ARY[] =
+{
+ // Subcategory Tables of Contents
+ STR_POOLCOLL_TOX_CNTNTH,
+ STR_POOLCOLL_TOX_CNTNT1,
+ STR_POOLCOLL_TOX_CNTNT2,
+ STR_POOLCOLL_TOX_CNTNT3,
+ STR_POOLCOLL_TOX_CNTNT4,
+ STR_POOLCOLL_TOX_CNTNT5
+};
+
+const TranslateId STR_POOLCOLL_TOX_CNTNT_EXTRA_ARY[] =
+{
+ // Subcategory Table of Contents more Levels 5 - 10
+ STR_POOLCOLL_TOX_CNTNT6,
+ STR_POOLCOLL_TOX_CNTNT7,
+ STR_POOLCOLL_TOX_CNTNT8,
+ STR_POOLCOLL_TOX_CNTNT9,
+ STR_POOLCOLL_TOX_CNTNT10
+};
+
+const TranslateId STR_POOLCOLL_TOX_USER_ARY[] =
+{
+ // Subcategory User-Directories:
+ STR_POOLCOLL_TOX_USERH,
+ STR_POOLCOLL_TOX_USER1,
+ STR_POOLCOLL_TOX_USER2,
+ STR_POOLCOLL_TOX_USER3,
+ STR_POOLCOLL_TOX_USER4,
+ STR_POOLCOLL_TOX_USER5
+};
+
+const TranslateId STR_POOLCOLL_TOX_USER_EXTRA_ARY[] =
+{
+ // Subcategory User-Directories more Levels 5 - 10
+ STR_POOLCOLL_TOX_USER6,
+ STR_POOLCOLL_TOX_USER7,
+ STR_POOLCOLL_TOX_USER8,
+ STR_POOLCOLL_TOX_USER9,
+ STR_POOLCOLL_TOX_USER10
+};
+
+const TranslateId STR_POOLCOLL_TOX_ILLUS_ARY[] =
+{
+ // Illustrations Index
+ STR_POOLCOLL_TOX_ILLUSH,
+ STR_POOLCOLL_TOX_ILLUS1
+};
+
+const TranslateId STR_POOLCOLL_TOX_OBJECT_ARY[] =
+{
+ // Object Index
+ STR_POOLCOLL_TOX_OBJECTH,
+ STR_POOLCOLL_TOX_OBJECT1
+};
+
+const TranslateId STR_POOLCOLL_TOX_TABLES_ARY[] =
+{
+ // Tables Index
+ STR_POOLCOLL_TOX_TABLESH,
+ STR_POOLCOLL_TOX_TABLES1
+};
+
+const TranslateId STR_POOLCOLL_TOX_AUTHORITIES_ARY[] =
+{
+ // Index of Authorities
+ STR_POOLCOLL_TOX_AUTHORITIESH,
+ STR_POOLCOLL_TOX_AUTHORITIES1
+};
+
+const TranslateId STR_POOLCOLL_TOX_CITATION_ARY[] =
+{
+ STR_POOLCOLL_TOX_CITATION
+};
+
+// Edit forms
+SwForm::SwForm( TOXTypes eTyp ) // #i21237#
+ : m_eType( eTyp ), m_nFormMaxLevel( SwForm::GetFormMaxLevel( eTyp )),
+// nFirstTabPos( lNumberIndent ),
+ m_bCommaSeparated(false)
+{
+ //bHasFirstTabPos =
+ m_bIsRelTabPos = true;
+
+ // The table of contents has a certain number of headlines + headings
+ // The user has 10 levels + headings
+ // Keyword has 3 levels + headings+ separator
+ // Indexes of tables, object illustrations and authorities consist of a heading and one level
+
+ const TranslateId* pPoolId;
+ switch( m_eType )
+ {
+ case TOX_INDEX: pPoolId = STR_POOLCOLL_TOX_ARY; break;
+ case TOX_USER: pPoolId = STR_POOLCOLL_TOX_USER_ARY; break;
+ case TOX_CONTENT: pPoolId = STR_POOLCOLL_TOX_CNTNT_ARY; break;
+ case TOX_ILLUSTRATIONS: pPoolId = STR_POOLCOLL_TOX_ILLUS_ARY; break;
+ case TOX_OBJECTS : pPoolId = STR_POOLCOLL_TOX_OBJECT_ARY; break;
+ case TOX_TABLES : pPoolId = STR_POOLCOLL_TOX_TABLES_ARY; break;
+ case TOX_AUTHORITIES : pPoolId = STR_POOLCOLL_TOX_AUTHORITIES_ARY; break;
+ case TOX_CITATION : pPoolId = STR_POOLCOLL_TOX_CITATION_ARY; break;
+ default:
+ OSL_ENSURE( false, "invalid TOXTyp");
+ return ;
+ }
+
+ SwFormTokens aTokens;
+ if (TOX_CONTENT == m_eType || TOX_ILLUSTRATIONS == m_eType )
+ {
+ SwFormToken aLinkStt (TOKEN_LINK_START);
+ aLinkStt.sCharStyleName = SwResId(STR_POOLCHR_TOXJUMP);
+ aTokens.push_back(aLinkStt);
+ }
+
+ if (TOX_CONTENT == m_eType)
+ {
+ aTokens.emplace_back(TOKEN_ENTRY_NO);
+ aTokens.emplace_back(TOKEN_ENTRY_TEXT);
+ }
+ else
+ aTokens.emplace_back(TOKEN_ENTRY);
+
+ if (TOX_AUTHORITIES != m_eType)
+ {
+ SwFormToken aToken(TOKEN_TAB_STOP);
+ aToken.nTabStopPosition = 0;
+
+ // #i36870# right aligned tab for all
+ aToken.cTabFillChar = '.';
+ aToken.eTabAlign = SvxTabAdjust::End;
+
+ aTokens.push_back(aToken);
+ aTokens.emplace_back(TOKEN_PAGE_NUMS);
+ }
+
+ if (TOX_CONTENT == m_eType || TOX_ILLUSTRATIONS == m_eType)
+ aTokens.emplace_back(TOKEN_LINK_END);
+
+ SetTemplate(0, SwResId(*pPoolId++));
+
+ if(TOX_INDEX == m_eType)
+ {
+ for( sal_uInt16 i = 1; i < 5; ++i )
+ {
+ if(1 == i)
+ {
+ SwFormTokens aTmpTokens;
+ SwFormToken aTmpToken(TOKEN_ENTRY);
+ aTmpTokens.push_back(aTmpToken);
+
+ SetPattern( i, std::move(aTmpTokens) );
+ SetTemplate(i, SwResId(STR_POOLCOLL_TOX_IDXBREAK));
+ }
+ else
+ {
+ SetPattern( i, std::vector(aTokens) );
+ SetTemplate(i, SwResId(STR_POOLCOLL_TOX_ARY[i - 1]));
+ }
+ }
+ }
+ else
+ {
+ for (sal_uInt16 i = 1; i < GetFormMax(); ++i, ++pPoolId) // Number 0 is the title
+ {
+ if (TOX_AUTHORITIES == m_eType)
+ {
+ SwFormTokens aAuthTokens;
+ lcl_FillAuthPattern(aAuthTokens, i);
+ SetPattern(i, std::move(aAuthTokens));
+ }
+ else
+ SetPattern( i, std::vector(aTokens) );
+
+ if( TOX_CONTENT == m_eType && 6 == i )
+ pPoolId = STR_POOLCOLL_TOX_CNTNT_EXTRA_ARY;
+ else if( TOX_USER == m_eType && 6 == i )
+ pPoolId = STR_POOLCOLL_TOX_USER_EXTRA_ARY;
+ else if( TOX_AUTHORITIES == m_eType ) //reuse the same STR_POOLCOLL_TOX_AUTHORITIES1 id each time
+ pPoolId = STR_POOLCOLL_TOX_AUTHORITIES_ARY + 1;
+ SetTemplate(i, SwResId(*pPoolId));
+ }
+ }
+}
+
+SwForm::SwForm(const SwForm& rForm)
+ : m_eType( rForm.m_eType )
+{
+ *this = rForm;
+}
+
+SwForm& SwForm::operator=(const SwForm& rForm)
+{
+ m_eType = rForm.m_eType;
+ m_nFormMaxLevel = rForm.m_nFormMaxLevel;
+// nFirstTabPos = rForm.nFirstTabPos;
+// bHasFirstTabPos = rForm.bHasFirstTabPos;
+ m_bIsRelTabPos = rForm.m_bIsRelTabPos;
+ m_bCommaSeparated = rForm.m_bCommaSeparated;
+ for(sal_uInt16 i=0; i < m_nFormMaxLevel; ++i)
+ {
+ m_aPattern[i] = rForm.m_aPattern[i];
+ m_aTemplate[i] = rForm.m_aTemplate[i];
+ }
+ return *this;
+}
+
+sal_uInt16 SwForm::GetFormMaxLevel( TOXTypes eTOXType )
+{
+ switch( eTOXType )
+ {
+ case TOX_INDEX:
+ return 5;
+ case TOX_USER:
+ case TOX_CONTENT:
+ return MAXLEVEL + 1;
+ case TOX_ILLUSTRATIONS:
+ case TOX_OBJECTS:
+ case TOX_TABLES:
+ return 2;
+ case TOX_BIBLIOGRAPHY:
+ case TOX_CITATION:
+ case TOX_AUTHORITIES:
+ return AUTH_TYPE_END + 1;
+ }
+ return 0;
+}
+
+void SwForm::AdjustTabStops( SwDoc const & rDoc ) // #i21237#
+{
+ const sal_uInt16 nFormMax = GetFormMax();
+ for ( sal_uInt16 nLevel = 1; nLevel < nFormMax; ++nLevel )
+ {
+ SwTextFormatColl* pColl = rDoc.FindTextFormatCollByName( GetTemplate(nLevel) );
+ if( pColl == nullptr )
+ {
+ // Paragraph Style for this level has not been created.
+ // --> No need to propagate default values
+ continue;
+ }
+
+ const SvxTabStopItem& rTabStops = pColl->GetTabStops(false);
+ const sal_uInt16 nTabCount = rTabStops.Count();
+ if (nTabCount != 0)
+ {
+ SwFormTokens aCurrentPattern = GetPattern(nLevel);
+ SwFormTokens::iterator aIt = aCurrentPattern.begin();
+
+ bool bChanged = false;
+ for(sal_uInt16 nTab = 0; nTab < nTabCount; ++nTab)
+ {
+ const SvxTabStop& rTab = rTabStops[nTab];
+
+ if ( rTab.GetAdjustment() == SvxTabAdjust::Default )
+ continue; // ignore the default tab stop
+
+ aIt = find_if( aIt, aCurrentPattern.end(), SwFormTokenEqualToFormTokenType(TOKEN_TAB_STOP) );
+ if ( aIt != aCurrentPattern.end() )
+ {
+ bChanged = true;
+ aIt->nTabStopPosition = rTab.GetTabPos();
+ aIt->eTabAlign =
+ ( nTab == nTabCount - 1
+ && rTab.GetAdjustment() == SvxTabAdjust::Right )
+ ? SvxTabAdjust::End
+ : rTab.GetAdjustment();
+ aIt->cTabFillChar = rTab.GetFill();
+ ++aIt;
+ }
+ else
+ break; // no more tokens to replace
+ }
+
+ if ( bChanged )
+ SetPattern( nLevel, std::move(aCurrentPattern) );
+ }
+ }
+}
+
+OUString SwForm::GetFormEntry() {return "<E>";}
+OUString SwForm::GetFormTab() {return "<T>";}
+OUString SwForm::GetFormPageNums() {return "<#>";}
+OUString SwForm::GetFormLinkStt() {return "<LS>";}
+OUString SwForm::GetFormLinkEnd() {return "<LE>";}
+OUString SwForm::GetFormEntryNum() {return "<E#>";}
+OUString SwForm::GetFormEntryText() {return "<ET>";}
+OUString SwForm::GetFormChapterMark() {return "<C>";}
+OUString SwForm::GetFormText() {return "<X>";}
+OUString SwForm::GetFormAuth() {return "<A>";}
+
+SwTOXBase::SwTOXBase(const SwTOXType* pTyp, const SwForm& rForm,
+ SwTOXElement nCreaType, const OUString& rTitle )
+ : SwClient(const_cast<sw::BroadcastingModify*>(static_cast<sw::BroadcastingModify const *>(pTyp)))
+ , m_aForm(rForm)
+ , m_aTitle(rTitle)
+ , m_eLanguage(::GetAppLanguage())
+ , m_nCreateType(nCreaType)
+ , m_nOLEOptions(SwTOOElements::NONE)
+ , m_eCaptionDisplay(CAPTION_COMPLETE)
+ , m_bProtected( true )
+ , m_bFromChapter(false)
+ , m_bFromObjectNames(false)
+ , m_bLevelFromChapter(false)
+ , maMSTOCExpression()
+ , mbKeepExpression(true)
+{
+ m_aData.nOptions = SwTOIOptions::NONE;
+}
+
+SwTOXBase::SwTOXBase( const SwTOXBase& rSource, SwDoc* pDoc )
+ : SwClient( rSource.GetRegisteredInNonConst() )
+ , mbKeepExpression(true)
+{
+ CopyTOXBase( pDoc, rSource );
+}
+
+void SwTOXBase::RegisterToTOXType( SwTOXType& rType )
+{
+ rType.Add( this );
+}
+
+void SwTOXBase::CopyTOXBase( SwDoc* pDoc, const SwTOXBase& rSource )
+{
+ maMSTOCExpression = rSource.maMSTOCExpression;
+ SwTOXType* pType = const_cast<SwTOXType*>(rSource.GetTOXType());
+ if( pDoc &&
+ std::find_if(pDoc->GetTOXTypes().begin(), pDoc->GetTOXTypes().end(),
+ [=](const std::unique_ptr<SwTOXType> & p) { return p.get() == pType; })
+ == pDoc->GetTOXTypes().end())
+ {
+ // type not in pDoc, so create it now
+ const SwTOXTypes& rTypes = pDoc->GetTOXTypes();
+ bool bFound = false;
+ for( size_t n = rTypes.size(); n; )
+ {
+ const SwTOXType* pCmp = rTypes[ --n ].get();
+ if( pCmp->GetType() == pType->GetType() &&
+ pCmp->GetTypeName() == pType->GetTypeName() )
+ {
+ pType = const_cast<SwTOXType*>(pCmp);
+ bFound = true;
+ break;
+ }
+ }
+
+ if( !bFound )
+ pType = const_cast<SwTOXType*>(pDoc->InsertTOXType( *pType ));
+ }
+ pType->Add( this );
+
+ m_nCreateType = rSource.m_nCreateType;
+ m_aTitle = rSource.m_aTitle;
+ m_aForm = rSource.m_aForm;
+ m_aBookmarkName = rSource.m_aBookmarkName;
+ m_bProtected = rSource.m_bProtected;
+ m_bFromChapter = rSource.m_bFromChapter;
+ m_bFromObjectNames = rSource.m_bFromObjectNames;
+ m_sMainEntryCharStyle = rSource.m_sMainEntryCharStyle;
+ m_sSequenceName = rSource.m_sSequenceName;
+ m_eCaptionDisplay = rSource.m_eCaptionDisplay;
+ m_nOLEOptions = rSource.m_nOLEOptions;
+ m_eLanguage = rSource.m_eLanguage;
+ m_sSortAlgorithm = rSource.m_sSortAlgorithm;
+ m_bLevelFromChapter = rSource.m_bLevelFromChapter;
+
+ for( sal_uInt16 i = 0; i < MAXLEVEL; ++i )
+ m_aStyleNames[i] = rSource.m_aStyleNames[i];
+
+ // it's the same data type!
+ m_aData.nOptions = rSource.m_aData.nOptions;
+
+ if( !pDoc || pDoc->IsCopyIsMove() )
+ m_aName = rSource.GetTOXName();
+ else
+ m_aName = pDoc->GetUniqueTOXBaseName( *pType, rSource.GetTOXName() );
+}
+
+// TOX specific functions
+SwTOXBase::~SwTOXBase()
+{
+// if( GetTOXType()->GetType() == TOX_USER )
+// delete aData.pTemplateName;
+}
+
+void SwTOXBase::SetTitle(const OUString& rTitle)
+ { m_aTitle = rTitle; }
+
+void SwTOXBase::SetBookmarkName(const OUString& bName)
+{
+ m_aBookmarkName = bName;
+}
+
+SwTOXBase & SwTOXBase::operator = (const SwTOXBase & rSource)
+{
+ m_aForm = rSource.m_aForm;
+ m_aName = rSource.m_aName;
+ m_aTitle = rSource.m_aTitle;
+ m_aBookmarkName = rSource.m_aBookmarkName;
+ m_sMainEntryCharStyle = rSource.m_sMainEntryCharStyle;
+ for(sal_uInt16 nLevel = 0; nLevel < MAXLEVEL; nLevel++)
+ m_aStyleNames[nLevel] = rSource.m_aStyleNames[nLevel];
+ m_sSequenceName = rSource.m_sSequenceName;
+ m_eLanguage = rSource.m_eLanguage;
+ m_sSortAlgorithm = rSource.m_sSortAlgorithm;
+ m_aData = rSource.m_aData;
+ m_nCreateType = rSource.m_nCreateType;
+ m_nOLEOptions = rSource.m_nOLEOptions;
+ m_eCaptionDisplay = rSource.m_eCaptionDisplay;
+ m_bProtected = rSource.m_bProtected;
+ m_bFromChapter = rSource.m_bFromChapter;
+ m_bFromObjectNames = rSource.m_bFromObjectNames;
+ m_bLevelFromChapter = rSource.m_bLevelFromChapter;
+
+ if (rSource.GetAttrSet())
+ SetAttrSet(*rSource.GetAttrSet());
+
+ return *this;
+}
+
+OUString SwFormToken::GetString() const
+{
+ OUString sToken;
+
+ switch( eTokenType )
+ {
+ case TOKEN_ENTRY_NO:
+ sToken = SwForm::GetFormEntryNum();
+ break;
+ case TOKEN_ENTRY_TEXT:
+ sToken = SwForm::GetFormEntryText();
+ break;
+ case TOKEN_ENTRY:
+ sToken = SwForm::GetFormEntry();
+ break;
+ case TOKEN_TAB_STOP:
+ sToken = SwForm::GetFormTab();
+ break;
+ case TOKEN_TEXT:
+ // Return a Token only if Text is not empty!
+ if( sText.isEmpty() )
+ {
+ return OUString();
+ }
+ sToken = SwForm::GetFormText();
+ break;
+ case TOKEN_PAGE_NUMS:
+ sToken = SwForm::GetFormPageNums();
+ break;
+ case TOKEN_CHAPTER_INFO:
+ sToken = SwForm::GetFormChapterMark();
+ break;
+ case TOKEN_LINK_START:
+ sToken = SwForm::GetFormLinkStt();
+ break;
+ case TOKEN_LINK_END:
+ sToken = SwForm::GetFormLinkEnd();
+ break;
+ case TOKEN_AUTHORITY:
+ {
+ sToken = SwForm::GetFormAuth();
+ }
+ break;
+ case TOKEN_END:
+ break;
+ }
+
+ OUString sData = " " + sCharStyleName + "," + OUString::number( nPoolId ) + ",";
+
+ // TabStopPosition and TabAlign or ChapterInfoFormat
+ switch (eTokenType)
+ {
+ case TOKEN_TAB_STOP:
+ sData += OUString::number( nTabStopPosition ) + ","
+ + OUString::number( static_cast< sal_Int32 >(eTabAlign) ) + ","
+ + OUStringChar(cTabFillChar) + ","
+ + OUString::number( bWithTab ? 1 : 0 );
+ break;
+ case TOKEN_CHAPTER_INFO:
+ case TOKEN_ENTRY_NO:
+ // add also maximum permitted level
+ sData += OUString::number( nChapterFormat ) + ","
+ + OUString::number( nOutlineLevel );
+ break;
+ case TOKEN_TEXT:
+ sData += OUStringChar(TOX_STYLE_DELIMITER)
+ + sText.replaceAll(OUStringChar(TOX_STYLE_DELIMITER), "")
+ + OUStringChar(TOX_STYLE_DELIMITER);
+ break;
+ case TOKEN_AUTHORITY:
+ if (nAuthorityField<10)
+ {
+ sData = "0" + OUString::number( nAuthorityField ) + sData;
+ }
+ else
+ {
+ sData = OUString::number( nAuthorityField ) + sData;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return sToken.subView(0, sToken.getLength()-1) + sData + sToken.subView(sToken.getLength()-1);
+}
+
+// -> #i21237#
+
+/**
+ Returns the type of a token.
+
+ @param sToken the string representation of the token
+ @param rTokenLen return parameter the length of the head of the token
+
+ @return the type of the token
+*/
+static FormTokenType lcl_GetTokenType(std::u16string_view sToken,
+ sal_Int32 & rTokenLen)
+{
+ static struct
+ {
+ OUString sTokenStart;
+ sal_Int16 nTokenLength;
+ FormTokenType eTokenType;
+ } const aTokenArr[] = {
+ { SwForm::GetFormTab().copy(0, 2), 3, TOKEN_TAB_STOP },
+ { SwForm::GetFormPageNums().copy(0, 2), 3, TOKEN_PAGE_NUMS },
+ { SwForm::GetFormLinkStt().copy(0, 3), 4, TOKEN_LINK_START },
+ { SwForm::GetFormLinkEnd().copy(0, 3), 4, TOKEN_LINK_END },
+ { SwForm::GetFormEntryNum().copy(0, 3), 4, TOKEN_ENTRY_NO },
+ { SwForm::GetFormEntryText().copy(0, 3), 4, TOKEN_ENTRY_TEXT },
+ { SwForm::GetFormChapterMark().copy(0, 2), 3, TOKEN_CHAPTER_INFO },
+ { SwForm::GetFormText().copy(0, 2), 3, TOKEN_TEXT },
+ { SwForm::GetFormEntry().copy(0, 2), 3, TOKEN_ENTRY },
+ { SwForm::GetFormAuth().copy(0, 2), 5, TOKEN_AUTHORITY }
+ };
+
+ for(const auto & i : aTokenArr)
+ {
+ if( o3tl::starts_with( sToken, i.sTokenStart ) )
+ {
+ rTokenLen = i.nTokenLength;
+ return i.eTokenType;
+ }
+ }
+
+ SAL_WARN("sw.core", "SwFormTokensHelper: invalid token");
+ return TOKEN_END;
+}
+
+/**
+ Returns the string of a token.
+
+ @param sPattern the whole pattern
+ @param nStt starting position of the token
+
+ @return the string representation of the token
+*/
+static OUString
+lcl_SearchNextToken(const OUString & sPattern, sal_Int32 const nStt)
+{
+ sal_Int32 nEnd = sPattern.indexOf( '>', nStt );
+ if (nEnd >= 0)
+ {
+ // apparently the TOX_STYLE_DELIMITER act as a bracketing for
+ // TOKEN_TEXT tokens so that the user can have '>' inside the text...
+ const sal_Int32 nTextSeparatorFirst = sPattern.indexOf( TOX_STYLE_DELIMITER, nStt );
+ if ( nTextSeparatorFirst >= 0
+ && nTextSeparatorFirst + 1 < sPattern.getLength()
+ && nTextSeparatorFirst < nEnd)
+ {
+ const sal_Int32 nTextSeparatorSecond = sPattern.indexOf( TOX_STYLE_DELIMITER,
+ nTextSeparatorFirst + 1 );
+ // Since nEnd>=0 we don't need to check if nTextSeparatorSecond<0!
+ if( nEnd < nTextSeparatorSecond )
+ nEnd = sPattern.indexOf( '>', nTextSeparatorSecond );
+ // FIXME: No check to verify that nEnd is still >=0?
+ assert(nEnd >= 0);
+ }
+
+ ++nEnd;
+
+ return sPattern.copy( nStt, nEnd - nStt );
+ }
+
+ return OUString();
+}
+
+/**
+ Builds a token from its string representation.
+
+ @sPattern the whole pattern
+ @nCurPatternPos starting position of the token
+
+ @return the token
+ */
+static std::optional<SwFormToken>
+lcl_BuildToken(const OUString & sPattern, sal_Int32 & nCurPatternPos)
+{
+ OUString sToken( lcl_SearchNextToken(sPattern, nCurPatternPos) );
+ nCurPatternPos += sToken.getLength();
+ sal_Int32 nTokenLen = 0;
+ FormTokenType const eTokenType = lcl_GetTokenType(sToken, nTokenLen);
+ if (TOKEN_END == eTokenType) // invalid input? skip it
+ {
+ nCurPatternPos = sPattern.getLength();
+ return std::optional<SwFormToken>();
+ }
+
+ // at this point sPattern contains the
+ // character style name, the PoolId, tab stop position, tab stop alignment, chapter info format
+ // the form is: CharStyleName, PoolId[, TabStopPosition|ChapterInfoFormat[, TabStopAlignment[, TabFillChar]]]
+ // in text tokens the form differs from the others: CharStyleName, PoolId[,\0xffinserted text\0xff]
+ SwFormToken eRet( eTokenType );
+ const OUString sAuthFieldEnum = sToken.copy( 2, 2 );
+ sToken = sToken.copy( nTokenLen, sToken.getLength() - nTokenLen - 1);
+
+ sal_Int32 nIdx{ 0 };
+ eRet.sCharStyleName = sToken.getToken( 0, ',', nIdx );
+ std::u16string_view sTmp( o3tl::getToken(sToken, 0, ',', nIdx ));
+ if( !sTmp.empty() )
+ eRet.nPoolId = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sTmp));
+
+ switch( eTokenType )
+ {
+//i53420
+ case TOKEN_CHAPTER_INFO:
+//i53420
+ case TOKEN_ENTRY_NO:
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 2
+ if( !sTmp.empty() )
+ eRet.nChapterFormat = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sTmp));
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 3
+ if( !sTmp.empty() )
+ eRet.nOutlineLevel = o3tl::narrowing<sal_uInt16>(o3tl::toInt32(sTmp)); //the maximum outline level to examine
+ break;
+
+ case TOKEN_TEXT:
+ {
+ const sal_Int32 nStartText = sToken.indexOf( TOX_STYLE_DELIMITER );
+ if( nStartText>=0 && nStartText+1<sToken.getLength())
+ {
+ const sal_Int32 nEndText = sToken.indexOf( TOX_STYLE_DELIMITER,
+ nStartText + 1);
+ if( nEndText>=0 )
+ {
+ eRet.sText = sToken.copy( nStartText + 1,
+ nEndText - nStartText - 1);
+ }
+ }
+ }
+ break;
+
+ case TOKEN_TAB_STOP:
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 2
+ if( !sTmp.empty() )
+ eRet.nTabStopPosition = o3tl::toInt32(sTmp);
+
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 3
+ if( !sTmp.empty() )
+ eRet.eTabAlign = static_cast<SvxTabAdjust>(o3tl::toInt32(sTmp));
+
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 4
+ if( !sTmp.empty() )
+ eRet.cTabFillChar = sTmp[0];
+
+ sTmp = o3tl::getToken(sToken, 0, ',', nIdx ); // token 5
+ if( !sTmp.empty() )
+ eRet.bWithTab = 0 != o3tl::toInt32(sTmp);
+ break;
+
+ case TOKEN_AUTHORITY:
+ eRet.nAuthorityField = o3tl::narrowing<sal_uInt16>(sAuthFieldEnum.toInt32());
+ break;
+ default: break;
+ }
+ return eRet;
+}
+
+SwFormTokensHelper::SwFormTokensHelper(const OUString & rPattern)
+{
+ sal_Int32 nCurPatternPos = 0;
+
+ while (nCurPatternPos < rPattern.getLength())
+ {
+ std::optional<SwFormToken> const oToken(
+ lcl_BuildToken(rPattern, nCurPatternPos));
+ if (oToken)
+ m_Tokens.push_back(*oToken);
+ }
+}
+
+// <- #i21237#
+
+void SwForm::SetPattern(sal_uInt16 nLevel, SwFormTokens&& rTokens)
+{
+ OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX");
+ m_aPattern[nLevel] = std::move(rTokens);
+}
+
+void SwForm::SetPattern(sal_uInt16 nLevel, const OUString & rStr)
+{
+ OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX");
+
+ SwFormTokensHelper aHelper(rStr);
+ m_aPattern[nLevel] = aHelper.GetTokens();
+}
+
+const SwFormTokens& SwForm::GetPattern(sal_uInt16 nLevel) const
+{
+ OSL_ENSURE(nLevel < GetFormMax(), "Index >= FORM_MAX");
+ return m_aPattern[nLevel];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/toxhlp.cxx b/sw/source/core/tox/toxhlp.cxx
new file mode 100644
index 000000000..6acff7399
--- /dev/null
+++ b/sw/source/core/tox/toxhlp.cxx
@@ -0,0 +1,119 @@
+/* -*- 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 <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/IndexEntrySupplier.hpp>
+#include <toxwrap.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+IndexEntrySupplierWrapper::IndexEntrySupplierWrapper()
+{
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ try {
+ m_xIES = i18n::IndexEntrySupplier::create(xContext);
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "IndexEntrySupplierWrapper" );
+ }
+}
+
+IndexEntrySupplierWrapper::~IndexEntrySupplierWrapper()
+{
+}
+
+OUString IndexEntrySupplierWrapper::GetIndexKey( const OUString& rText,
+ const OUString& rTextReading,
+ const css::lang::Locale& rLocale ) const
+{
+ OUString sRet;
+ try {
+ sRet = m_xIES->getIndexKey( rText, rTextReading, rLocale );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "getIndexKey" );
+ }
+ return sRet;
+}
+
+OUString IndexEntrySupplierWrapper::GetFollowingText( bool bMorePages ) const
+{
+ OUString sRet;
+ try {
+ sRet = m_xIES->getIndexFollowPageWord( bMorePages, m_aLcl );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "getIndexFollowPageWord" );
+ }
+ return sRet;
+}
+
+css::uno::Sequence< OUString > IndexEntrySupplierWrapper::GetAlgorithmList( const css::lang::Locale& rLcl ) const
+{
+ uno::Sequence< OUString > sRet;
+
+ try {
+ sRet = m_xIES->getAlgorithmList( rLcl );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "getAlgorithmList" );
+ }
+ return sRet;
+}
+
+bool IndexEntrySupplierWrapper::LoadAlgorithm(
+ const css::lang::Locale& rLcl,
+ const OUString& sSortAlgorithm, tools::Long nOptions ) const
+{
+ bool bRet = false;
+ try {
+ bRet = m_xIES->loadAlgorithm( rLcl, sSortAlgorithm, nOptions );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "loadAlgorithm" );
+ }
+ return bRet;
+}
+
+sal_Int16 IndexEntrySupplierWrapper::CompareIndexEntry(
+ const OUString& rText1, const OUString& rTextReading1,
+ const css::lang::Locale& rLocale1,
+ const OUString& rText2, const OUString& rTextReading2,
+ const css::lang::Locale& rLocale2 ) const
+{
+ sal_Int16 nRet = 0;
+ try {
+ nRet = m_xIES->compareIndexEntry( rText1, rTextReading1, rLocale1,
+ rText2, rTextReading2, rLocale2 );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "sw.core", "compareIndexEntry" );
+ }
+ return nRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/tox/txmsrt.cxx b/sw/source/core/tox/txmsrt.cxx
new file mode 100644
index 000000000..8e2272b1f
--- /dev/null
+++ b/sw/source/core/tox/txmsrt.cxx
@@ -0,0 +1,989 @@
+/* -*- 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 <com/sun/star/uri/UriReferenceFactory.hpp>
+
+#include <unotools/charclass.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <comphelper/processfactory.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <txtfld.hxx>
+#include <doc.hxx>
+#include <IDocumentLayoutAccess.hxx>
+#include <IDocumentMarkAccess.hxx>
+#include <cntfrm.hxx>
+#include <txtfrm.hxx>
+#include <rootfrm.hxx>
+#include <modeltoviewhelper.hxx>
+#include <node.hxx>
+#include <pam.hxx>
+#include <txttxmrk.hxx>
+#include <frmfmt.hxx>
+#include <fmtfld.hxx>
+#include <txmsrt.hxx>
+#include <ndtxt.hxx>
+#include <swtable.hxx>
+#include <expfld.hxx>
+#include <authfld.hxx>
+#include <toxwrap.hxx>
+
+#include <strings.hrc>
+#include <reffld.hxx>
+#include <docsh.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+// Initialize strings
+SwTOIOptions SwTOXSortTabBase::nOpt = SwTOIOptions::NONE;
+
+SwTOXInternational::SwTOXInternational( LanguageType nLang, SwTOIOptions nOpt,
+ const OUString& rSortAlgorithm ) :
+ m_eLang( nLang ),
+ m_sSortAlgorithm(rSortAlgorithm),
+ m_nOptions( nOpt )
+{
+ Init();
+}
+
+SwTOXInternational::SwTOXInternational( const SwTOXInternational& rIntl ) :
+ m_eLang( rIntl.m_eLang ),
+ m_sSortAlgorithm(rIntl.m_sSortAlgorithm),
+ m_nOptions( rIntl.m_nOptions )
+{
+ Init();
+}
+
+void SwTOXInternational::Init()
+{
+ m_pIndexWrapper.reset( new IndexEntrySupplierWrapper() );
+
+ const lang::Locale aLcl( LanguageTag::convertToLocale( m_eLang ) );
+ m_pIndexWrapper->SetLocale( aLcl );
+
+ if(m_sSortAlgorithm.isEmpty())
+ {
+ Sequence < OUString > aSeq( m_pIndexWrapper->GetAlgorithmList( aLcl ));
+ if(aSeq.hasElements())
+ m_sSortAlgorithm = aSeq.getConstArray()[0];
+ }
+
+ if ( m_nOptions & SwTOIOptions::CaseSensitive )
+ m_pIndexWrapper->LoadAlgorithm( aLcl, m_sSortAlgorithm, 0 );
+ else
+ m_pIndexWrapper->LoadAlgorithm( aLcl, m_sSortAlgorithm, SW_COLLATOR_IGNORES );
+
+ m_pCharClass.reset( new CharClass( LanguageTag( aLcl )) );
+
+}
+
+SwTOXInternational::~SwTOXInternational()
+{
+ m_pCharClass.reset();
+ m_pIndexWrapper.reset();
+}
+
+OUString SwTOXInternational::ToUpper( const OUString& rStr, sal_Int32 nPos ) const
+{
+ return m_pCharClass->uppercase( rStr, nPos, 1 );
+}
+
+inline bool SwTOXInternational::IsNumeric( const OUString& rStr ) const
+{
+ return m_pCharClass->isNumeric( rStr );
+}
+
+sal_Int32 SwTOXInternational::Compare( const TextAndReading& rTaR1,
+ const lang::Locale& rLocale1,
+ const TextAndReading& rTaR2,
+ const lang::Locale& rLocale2 ) const
+{
+ return m_pIndexWrapper->CompareIndexEntry( rTaR1.sText, rTaR1.sReading, rLocale1,
+ rTaR2.sText, rTaR2.sReading, rLocale2 );
+}
+
+OUString SwTOXInternational::GetIndexKey( const TextAndReading& rTaR,
+ const lang::Locale& rLocale ) const
+{
+ return m_pIndexWrapper->GetIndexKey( rTaR.sText, rTaR.sReading, rLocale );
+}
+
+OUString SwTOXInternational::GetFollowingText( bool bMorePages ) const
+{
+ return m_pIndexWrapper->GetFollowingText( bMorePages );
+}
+
+// SortElement for TOX entries
+SwTOXSortTabBase::SwTOXSortTabBase( TOXSortType nTyp, const SwContentNode* pNd,
+ const SwTextTOXMark* pMark,
+ const SwTOXInternational* pInter,
+ const lang::Locale* pLocale )
+ : pTOXNd( nullptr ), pTextMark( pMark ), pTOXIntl( pInter ),
+ nPos( 0 ), nCntPos( 0 ), nType( o3tl::narrowing<sal_uInt16>(nTyp) )
+ , m_bValidText( false )
+{
+ if ( pLocale )
+ aLocale = *pLocale;
+
+ if( !pNd )
+ return;
+
+ sal_Int32 n = 0;
+ if( pTextMark )
+ n = pTextMark->GetStart();
+ SwTOXSource aTmp( pNd, n, pTextMark && pTextMark->GetTOXMark().IsMainEntry() );
+ aTOXSources.push_back(aTmp);
+
+ nPos = pNd->GetIndex();
+
+ switch( nTyp )
+ {
+ case TOX_SORT_CONTENT:
+ case TOX_SORT_PARA:
+ case TOX_SORT_TABLE:
+ // If they are in a special areas, we should get the position at the
+ // body
+ if( nPos < pNd->GetNodes().GetEndOfExtras().GetIndex() )
+ {
+ // Then get the 'anchor' (body) position
+ Point aPt;
+ std::pair<Point, bool> tmp(aPt, false);
+ const SwContentFrame *const pFrame = pNd->getLayoutFrame(
+ pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
+ nullptr, &tmp);
+ if( pFrame )
+ {
+ SwPosition aPos( *pNd );
+ const SwDoc& rDoc = pNd->GetDoc();
+ bool const bResult = GetBodyTextNode( rDoc, aPos, *pFrame );
+ OSL_ENSURE(bResult, "where is the text node");
+ nPos = aPos.nNode.GetIndex();
+ nCntPos = aPos.nContent.GetIndex();
+ }
+ }
+ else
+ nCntPos = n;
+ break;
+ default: break;
+ }
+}
+
+std::pair<OUString, bool> SwTOXSortTabBase::GetURL(SwRootFrame const*const pLayout) const
+{
+ OUString typeName;
+ SwTOXType const& rType(*pTextMark->GetTOXMark().GetTOXType());
+ switch (rType.GetType())
+ {
+ case TOX_INDEX:
+ typeName = "A";
+ break;
+ case TOX_CONTENT:
+ typeName = "C";
+ break;
+ case TOX_USER:
+ typeName = "U" + rType.GetTypeName();
+ break;
+ default:
+ assert(false); // other tox can't have toxmarks as source
+ break;
+ }
+ OUString const decodedUrl( // counter will be added by caller!
+ OUStringChar(toxMarkSeparator) + pTextMark->GetTOXMark().GetText(pLayout)
+ + OUStringChar(toxMarkSeparator) + typeName
+ + OUStringChar(cMarkSeparator) + "toxmark" );
+
+ return std::make_pair(decodedUrl, true);
+}
+
+bool SwTOXSortTabBase::IsFullPara() const
+{
+ return false;
+}
+
+void SwTOXSortTabBase::FillText( SwTextNode& rNd, const SwIndex& rInsPos,
+ sal_uInt16, SwRootFrame const*const) const
+{
+ rNd.InsertText( GetText().sText, rInsPos );
+}
+
+bool SwTOXSortTabBase::equivalent(const SwTOXSortTabBase& rCmp)
+{
+ bool bRet = nPos == rCmp.nPos && nCntPos == rCmp.nCntPos &&
+ (!aTOXSources[0].pNd || !rCmp.aTOXSources[0].pNd ||
+ aTOXSources[0].pNd == rCmp.aTOXSources[0].pNd );
+
+ if( TOX_SORT_CONTENT == nType )
+ {
+ bRet = bRet && pTextMark && rCmp.pTextMark &&
+ pTextMark->GetStart() == rCmp.pTextMark->GetStart();
+
+ if( bRet )
+ {
+ // Both pointers exist -> compare text
+ // else -> compare AlternativeText
+ const sal_Int32 *pEnd = pTextMark->End();
+ const sal_Int32 *pEndCmp = rCmp.pTextMark->End();
+
+ bRet = ( ( pEnd && pEndCmp ) || ( !pEnd && !pEndCmp ) ) &&
+ pTOXIntl->IsEqual( GetText(), GetLocale(),
+ rCmp.GetText(), rCmp.GetLocale() );
+ }
+ }
+ return bRet;
+}
+
+bool SwTOXSortTabBase::sort_lt(const SwTOXSortTabBase& rCmp)
+{
+ if( nPos < rCmp.nPos )
+ return true;
+
+ if( nPos == rCmp.nPos )
+ {
+ if( nCntPos < rCmp.nCntPos )
+ return true;
+
+ if( nCntPos == rCmp.nCntPos )
+ {
+ const SwNode* pFirst = aTOXSources[0].pNd;
+ const SwNode* pNext = rCmp.aTOXSources[0].pNd;
+
+ if( pFirst && pFirst == pNext )
+ {
+ if( TOX_SORT_CONTENT == nType && pTextMark && rCmp.pTextMark )
+ {
+ if( pTextMark->GetStart() < rCmp.pTextMark->GetStart() )
+ return true;
+
+ if( pTextMark->GetStart() == rCmp.pTextMark->GetStart() )
+ {
+ const sal_Int32 *pEnd = pTextMark->End();
+ const sal_Int32 *pEndCmp = rCmp.pTextMark->End();
+
+ // Both pointers exist -> compare text
+ // else -> compare AlternativeText
+ if( ( pEnd && pEndCmp ) || ( !pEnd && !pEndCmp ) )
+ {
+ return pTOXIntl->IsLess( GetText(), GetLocale(),
+ rCmp.GetText(), rCmp.GetLocale() );
+ }
+ if( pEnd && !pEndCmp )
+ return true;
+ }
+ }
+ }
+ else if( pFirst && pFirst->IsTextNode() &&
+ pNext && pNext->IsTextNode() )
+ return ::IsFrameBehind( *static_cast<const SwTextNode*>(pNext), nCntPos,
+ *static_cast<const SwTextNode*>(pFirst), nCntPos );
+ }
+ }
+ return false;
+}
+
+// Sorted keyword entry
+SwTOXIndex::SwTOXIndex( const SwTextNode& rNd,
+ const SwTextTOXMark* pMark, SwTOIOptions nOptions,
+ sal_uInt8 nKyLevel,
+ const SwTOXInternational& rIntl,
+ const lang::Locale& rLocale )
+ : SwTOXSortTabBase( TOX_SORT_INDEX, &rNd, pMark, &rIntl, &rLocale ),
+ nKeyLevel(nKyLevel)
+{
+ nPos = rNd.GetIndex();
+ nOpt = nOptions;
+}
+
+// Compare keywords. Only relates to the text.
+
+bool SwTOXIndex::equivalent(const SwTOXSortTabBase& rCmpBase)
+{
+ const SwTOXIndex& rCmp = static_cast<const SwTOXIndex&>(rCmpBase);
+
+ // Respect case taking dependencies into account
+ if(GetLevel() != rCmp.GetLevel() || nKeyLevel != rCmp.nKeyLevel)
+ return false;
+
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+
+ bool bRet = pTOXIntl->IsEqual( GetText(), GetLocale(),
+ rCmp.GetText(), rCmp.GetLocale() );
+
+ // If we don't summarize we need to evaluate the Pos
+ if(bRet && !(GetOptions() & SwTOIOptions::SameEntry))
+ bRet = nPos == rCmp.nPos;
+
+ return bRet;
+}
+
+// operator, only depends on the text
+
+bool SwTOXIndex::sort_lt(const SwTOXSortTabBase& rCmpBase)
+{
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+
+ const TextAndReading aMyTaR(GetText());
+ const TextAndReading aOtherTaR(rCmpBase.GetText());
+
+ bool bRet = GetLevel() == rCmpBase.GetLevel() &&
+ pTOXIntl->IsLess( aMyTaR, GetLocale(),
+ aOtherTaR, rCmpBase.GetLocale() );
+
+ // If we don't summarize we need to evaluate the Pos
+ if( !bRet && !(GetOptions() & SwTOIOptions::SameEntry) )
+ {
+ bRet = pTOXIntl->IsEqual( aMyTaR, GetLocale(),
+ aOtherTaR, rCmpBase.GetLocale() ) &&
+ nPos < rCmpBase.nPos;
+ }
+
+ return bRet;
+}
+
+// The keyword itself
+
+TextAndReading SwTOXIndex::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+ const SwTOXMark& rTOXMark = pTextMark->GetTOXMark();
+
+ TextAndReading aRet;
+ switch(nKeyLevel)
+ {
+ case FORM_PRIMARY_KEY :
+ {
+ aRet.sText = rTOXMark.GetPrimaryKey();
+ aRet.sReading = rTOXMark.GetPrimaryKeyReading();
+ }
+ break;
+ case FORM_SECONDARY_KEY :
+ {
+ aRet.sText = rTOXMark.GetSecondaryKey();
+ aRet.sReading = rTOXMark.GetSecondaryKeyReading();
+ }
+ break;
+ case FORM_ENTRY :
+ {
+ aRet.sText = rTOXMark.GetText(pLayout);
+ aRet.sReading = rTOXMark.GetTextReading();
+ }
+ break;
+ }
+ // if SwTOIOptions::InitialCaps is set, first character is to be capitalized
+ if( SwTOIOptions::InitialCaps & nOpt && pTOXIntl && !aRet.sText.isEmpty())
+ {
+ aRet.sText = pTOXIntl->ToUpper( aRet.sText, 0 ) + aRet.sText.subView(1);
+ }
+
+ return aRet;
+}
+
+void SwTOXIndex::FillText( SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!"sw_redlinehide: this is dead code, Bibliography only has SwTOXAuthority");
+ const sal_Int32* pEnd = pTextMark->End();
+
+ TextAndReading aRet;
+ if( pEnd && !pTextMark->GetTOXMark().IsAlternativeText() &&
+ !(GetOptions() & SwTOIOptions::KeyAsEntry))
+ {
+ aRet.sText = static_cast<const SwTextNode*>(aTOXSources[0].pNd)->GetExpandText(
+ pLayout,
+ pTextMark->GetStart(),
+ *pEnd - pTextMark->GetStart(),
+ false, false, false,
+ ExpandMode::ExpandFootnote
+ | (pLayout && pLayout->IsHideRedlines()
+ ? ExpandMode::HideDeletions
+ : ExpandMode(0)));
+ if(SwTOIOptions::InitialCaps & nOpt && pTOXIntl && !aRet.sText.isEmpty())
+ {
+ aRet.sText = pTOXIntl->ToUpper( aRet.sText, 0 ) + aRet.sText.subView(1);
+ }
+ }
+ else
+ aRet = GetText();
+
+ rNd.InsertText( aRet.sText, rInsPos );
+}
+
+sal_uInt16 SwTOXIndex::GetLevel() const
+{
+ OSL_ENSURE(pTextMark, "pTextMark == 0, No keyword");
+
+ sal_uInt16 nForm = FORM_PRIMARY_KEY;
+
+ if( !(GetOptions() & SwTOIOptions::KeyAsEntry)&&
+ !pTextMark->GetTOXMark().GetPrimaryKey().isEmpty() )
+ {
+ nForm = FORM_SECONDARY_KEY;
+ if( !pTextMark->GetTOXMark().GetSecondaryKey().isEmpty() )
+ nForm = FORM_ENTRY;
+ }
+ return nForm;
+}
+
+// Key and separator
+SwTOXCustom::SwTOXCustom(const TextAndReading& rKey,
+ sal_uInt16 nLevel,
+ const SwTOXInternational& rIntl,
+ const lang::Locale& rLocale )
+ : SwTOXSortTabBase( TOX_SORT_CUSTOM, nullptr, nullptr, &rIntl, &rLocale ),
+ m_aKey(rKey), nLev(nLevel)
+{
+}
+
+bool SwTOXCustom::equivalent(const SwTOXSortTabBase& rCmpBase)
+{
+ return GetLevel() == rCmpBase.GetLevel() &&
+ pTOXIntl->IsEqual( GetText(), GetLocale(),
+ rCmpBase.GetText(), rCmpBase.GetLocale() );
+}
+
+bool SwTOXCustom::sort_lt(const SwTOXSortTabBase& rCmpBase)
+{
+ return GetLevel() <= rCmpBase.GetLevel() &&
+ pTOXIntl->IsLess( GetText(), GetLocale(),
+ rCmpBase.GetText(), rCmpBase.GetLocale() );
+}
+
+sal_uInt16 SwTOXCustom::GetLevel() const
+{
+ return nLev;
+}
+
+TextAndReading SwTOXCustom::GetText_Impl(SwRootFrame const*const) const
+{
+ return m_aKey;
+}
+
+// Sorts the TOX entries
+SwTOXContent::SwTOXContent( const SwTextNode& rNd, const SwTextTOXMark* pMark,
+ const SwTOXInternational& rIntl)
+ : SwTOXSortTabBase( TOX_SORT_CONTENT, &rNd, pMark, &rIntl )
+{
+}
+
+// The content's text
+
+TextAndReading SwTOXContent::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ const sal_Int32* pEnd = pTextMark->End();
+ if( pEnd && !pTextMark->GetTOXMark().IsAlternativeText() )
+ {
+ return TextAndReading(
+ static_cast<const SwTextNode*>(aTOXSources[0].pNd)->GetExpandText(
+ pLayout,
+ pTextMark->GetStart(),
+ *pEnd - pTextMark->GetStart(),
+ false, false, false,
+ ExpandMode::ExpandFootnote
+ | (pLayout && pLayout->IsHideRedlines()
+ ? ExpandMode::HideDeletions
+ : ExpandMode(0))),
+ pTextMark->GetTOXMark().GetTextReading());
+ }
+
+ return TextAndReading(pTextMark->GetTOXMark().GetAlternativeText(), OUString());
+}
+
+void SwTOXContent::FillText(SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!"sw_redlinehide: this is dead code, Bibliography only has SwTOXAuthority");
+ const sal_Int32* pEnd = pTextMark->End();
+ if( pEnd && !pTextMark->GetTOXMark().IsAlternativeText() )
+ // sw_redlinehide: this probably won't HideDeletions
+ static_cast<const SwTextNode*>(aTOXSources[0].pNd)->CopyExpandText(
+ rNd, &rInsPos, pTextMark->GetStart(),
+ *pEnd - pTextMark->GetStart(), pLayout);
+ else
+ {
+ rNd.InsertText( GetText().sText, rInsPos );
+ }
+}
+
+// The level for displaying it
+
+sal_uInt16 SwTOXContent::GetLevel() const
+{
+ return pTextMark->GetTOXMark().GetLevel();
+}
+
+// TOX assembled from paragraphs
+// Watch out for OLE/graphics when sorting!
+// The position must not come from the document, but from the "anchor"!
+SwTOXPara::SwTOXPara(SwContentNode& rNd, SwTOXElement eT, sal_uInt16 nLevel, const OUString& sSeqName)
+ : SwTOXSortTabBase( TOX_SORT_PARA, &rNd, nullptr, nullptr ),
+ eType( eT ),
+ m_nLevel(nLevel),
+ nStartIndex(0),
+ nEndIndex(-1),
+ m_sSequenceName( sSeqName )
+{
+ // tdf#123313 create any missing bookmarks *before* generating ToX nodes!
+ switch (eType)
+ {
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ assert(rNd.IsTextNode());
+ rNd.GetDoc().getIDocumentMarkAccess()->getMarkForTextNode(
+ *rNd.GetTextNode(), IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
+ break;
+ default:
+ break;
+ }
+}
+
+TextAndReading SwTOXPara::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ const SwContentNode* pNd = aTOXSources[0].pNd;
+ switch( eType )
+ {
+ case SwTOXElement::Sequence:
+ if (nStartIndex != 0 || nEndIndex != -1)
+ {
+ // sw_redlinehide: "captions" are a rather fuzzily defined concept anyway
+ return TextAndReading(static_cast<const SwTextNode*>(pNd)->GetExpandText(
+ pLayout,
+ nStartIndex,
+ nEndIndex == -1 ? -1 : nEndIndex - nStartIndex,
+ false, false, false,
+ pLayout && pLayout->IsHideRedlines()
+ ? ExpandMode::HideDeletions
+ : ExpandMode(0)),
+ OUString());
+ }
+ [[fallthrough]];
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ {
+ assert(nStartIndex == 0);
+ assert(nEndIndex == -1);
+ return TextAndReading(sw::GetExpandTextMerged(
+ pLayout, *static_cast<const SwTextNode*>(pNd),
+ false, false, ExpandMode::HideInvisible | ExpandMode::HideDeletions),
+ OUString());
+ }
+ break;
+
+ case SwTOXElement::Ole:
+ case SwTOXElement::Graphic:
+ case SwTOXElement::Frame:
+ {
+ // Find the FlyFormat; the object/graphic name is there
+ SwFrameFormat* pFly = pNd->GetFlyFormat();
+ if( pFly )
+ return TextAndReading(pFly->GetName(), OUString());
+
+ OSL_ENSURE( false, "Graphic/object without name" );
+ TranslateId pId = SwTOXElement::Ole == eType
+ ? STR_OBJECT_DEFNAME
+ : SwTOXElement::Graphic == eType
+ ? STR_GRAPHIC_DEFNAME
+ : STR_FRAME_DEFNAME;
+ return TextAndReading(SwResId(pId), OUString());
+ }
+ break;
+ default: break;
+ }
+ return TextAndReading();
+}
+
+void SwTOXPara::FillText( SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16,
+ SwRootFrame const*const pLayout) const
+{
+ assert(!"sw_redlinehide: this is dead code, Bibliography only has SwTOXAuthority");
+ if( SwTOXElement::Template == eType || SwTOXElement::Sequence == eType || SwTOXElement::OutlineLevel == eType)
+ {
+ const SwTextNode* pSrc = static_cast<const SwTextNode*>(aTOXSources[0].pNd);
+ if (SwTOXElement::Sequence == eType
+ && (nStartIndex != 0 || nEndIndex != -1))
+ {
+ pSrc->CopyExpandText( rNd, &rInsPos, nStartIndex,
+ nEndIndex == -1 ? -1 : nEndIndex - nStartIndex,
+ pLayout, false, false, true );
+ }
+ else
+ {
+ assert(nStartIndex == 0);
+ assert(nEndIndex == -1);
+ // sw_redlinehide: this probably won't HideDeletions
+ pSrc->CopyExpandText( rNd, &rInsPos, 0, -1,
+ pLayout, false, false, true );
+ if (pLayout && pLayout->HasMergedParas())
+ {
+ if (SwTextFrame const*const pFrame = static_cast<SwTextFrame*>(pSrc->getLayoutFrame(pLayout)))
+ {
+ if (sw::MergedPara const*const pMerged = pFrame->GetMergedPara())
+ {
+ // pSrc already copied above
+ assert(pSrc == pMerged->pParaPropsNode);
+ for (SwNodeOffset i = pSrc->GetIndex() + 1;
+ i <= pMerged->pLastNode->GetIndex(); ++i)
+ {
+ SwNode *const pTmp(pSrc->GetNodes()[i]);
+ if (pTmp->GetRedlineMergeFlag() == SwNode::Merge::NonFirst)
+ {
+
+ pTmp->GetTextNode()->CopyExpandText(
+ rNd, &rInsPos, 0, -1,
+ pLayout, false, false, false );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ rNd.InsertText( GetText().sText.replace('\t', ' '), rInsPos );
+ }
+}
+
+sal_uInt16 SwTOXPara::GetLevel() const
+{
+ sal_uInt16 nRet = m_nLevel;
+ const SwContentNode* pNd = aTOXSources[0].pNd;
+
+ if( SwTOXElement::OutlineLevel == eType && pNd->GetTextNode() )
+ {
+ const int nTmp = static_cast<const SwTextNode*>(pNd)->GetAttrOutlineLevel();
+ if(nTmp != 0 )
+ nRet = o3tl::narrowing<sal_uInt16>(nTmp);
+ }
+ return nRet;
+}
+
+std::pair<OUString, bool> SwTOXPara::GetURL(SwRootFrame const*const) const
+{
+ OUString aText;
+ const SwContentNode* pNd = aTOXSources[0].pNd;
+ switch( eType )
+ {
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ {
+ const SwTextNode * pTextNd = pNd->GetTextNode();
+
+ SwDoc& rDoc = const_cast<SwDoc&>( pTextNd->GetDoc() );
+ // tdf#123313: this *must not* create a bookmark, its Undo would
+ // be screwed! create it as preparatory step, in ctor!
+ ::sw::mark::IMark const * const pMark = rDoc.getIDocumentMarkAccess()->getMarkForTextNode(
+ *pTextNd,
+ IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK);
+ aText = "#" + pMark->GetName();
+ }
+ break;
+
+ case SwTOXElement::Ole:
+ case SwTOXElement::Graphic:
+ case SwTOXElement::Frame:
+ {
+ // Find the FlyFormat; the object/graphic name is there
+ SwFrameFormat* pFly = pNd->GetFlyFormat();
+ if( pFly )
+ {
+ aText = "#" + pFly->GetName() + OUStringChar(cMarkSeparator);
+ const char* pStr;
+ switch( eType )
+ {
+ case SwTOXElement::Ole: pStr = "ole"; break;
+ case SwTOXElement::Graphic: pStr = "graphic"; break;
+ case SwTOXElement::Frame: pStr = "frame"; break;
+ default: pStr = nullptr;
+ }
+ if( pStr )
+ aText += OUString::createFromAscii( pStr );
+ }
+ }
+ break;
+ case SwTOXElement::Sequence:
+ {
+ aText = "#" + m_sSequenceName + OUStringChar(cMarkSeparator)
+ + "sequence";
+ }
+ break;
+ default: break;
+ }
+ return std::make_pair(aText, false);
+}
+
+bool SwTOXPara::IsFullPara() const
+{
+ switch (eType)
+ {
+ case SwTOXElement::Sequence:
+ case SwTOXElement::Template:
+ case SwTOXElement::OutlineLevel:
+ return nStartIndex == 0 && nEndIndex == -1;
+ default:
+ return false;
+ }
+}
+
+// Table
+SwTOXTable::SwTOXTable( const SwContentNode& rNd )
+ : SwTOXSortTabBase( TOX_SORT_TABLE, &rNd, nullptr, nullptr ),
+ nLevel(FORM_ALPHA_DELIMITER)
+{
+}
+
+TextAndReading SwTOXTable::GetText_Impl(SwRootFrame const*const) const
+{
+ const SwNode* pNd = aTOXSources[0].pNd;
+ if( pNd )
+ {
+ const SwTableNode* pTableNd =
+ pNd->FindTableNode();
+ if (pTableNd)
+ {
+ return TextAndReading(pTableNd->GetTable().GetFrameFormat()->GetName(), OUString());
+ }
+ }
+
+ OSL_ENSURE( false, "Where's my table?" );
+ return TextAndReading(SwResId( STR_TABLE_DEFNAME ), OUString());
+}
+
+sal_uInt16 SwTOXTable::GetLevel() const
+{
+ return nLevel;
+}
+
+std::pair<OUString, bool> SwTOXTable::GetURL(SwRootFrame const*const) const
+{
+ const SwNode* pNd = aTOXSources[0].pNd;
+ if (!pNd)
+ return std::make_pair(OUString(), false);
+
+ pNd = pNd->FindTableNode();
+ if (!pNd)
+ return std::make_pair(OUString(), false);
+
+ const OUString sName = static_cast<const SwTableNode*>(pNd)->GetTable().GetFrameFormat()->GetName();
+ if ( sName.isEmpty() )
+ return std::make_pair(OUString(), false);
+
+ return std::make_pair("#" + sName + OUStringChar(cMarkSeparator) + "table", false);
+}
+
+SwTOXAuthority::SwTOXAuthority( const SwContentNode& rNd,
+ SwFormatField& rField, const SwTOXInternational& rIntl ) :
+ SwTOXSortTabBase( TOX_SORT_AUTHORITY, &rNd, nullptr, &rIntl ),
+ m_rField(rField)
+{
+ if(rField.GetTextField())
+ nCntPos = rField.GetTextField()->GetStart();
+}
+
+sal_uInt16 SwTOXAuthority::GetLevel() const
+{
+ OUString sText(static_cast<SwAuthorityField*>(m_rField.GetField())->GetFieldText(AUTH_FIELD_AUTHORITY_TYPE));
+ //#i18655# the level '0' is the heading level therefore the values are incremented here
+ sal_uInt16 nRet = 1;
+ if( pTOXIntl->IsNumeric( sText ) )
+ {
+ nRet = sText.toUInt32();
+ nRet++;
+ }
+ //illegal values are also set to 'ARTICLE' as non-numeric values are
+ if(nRet > AUTH_TYPE_END)
+ nRet = 1;
+ return nRet;
+}
+
+static OUString lcl_GetText(SwFormatField const& rField, SwRootFrame const*const pLayout)
+{
+ return rField.GetField()->ExpandField(true, pLayout);
+}
+
+TextAndReading SwTOXAuthority::GetText_Impl(SwRootFrame const*const pLayout) const
+{
+ return TextAndReading(lcl_GetText(m_rField, pLayout), OUString());
+}
+
+OUString SwTOXAuthority::GetText(sal_uInt16 nAuthField, const SwRootFrame* pLayout) const
+{
+ SwAuthorityField* pField = static_cast<SwAuthorityField*>(m_rField.GetField());
+ OUString sText;
+ if(AUTH_FIELD_IDENTIFIER == nAuthField)
+ {
+ sText = lcl_GetText(m_rField, pLayout);
+ const SwAuthorityFieldType* pType = static_cast<const SwAuthorityFieldType*>(pField->GetTyp());
+ sal_Unicode cChar = pType->GetPrefix();
+ if(cChar && cChar != ' ')
+ sText = sText.copy(1);
+ cChar = pType->GetSuffix();
+ if(cChar && cChar != ' ')
+ sText = sText.copy(0, sText.getLength() - 1);
+ }
+ else if(AUTH_FIELD_AUTHORITY_TYPE == nAuthField)
+ {
+ sal_uInt16 nLevel = GetLevel();
+ if(nLevel)
+ sText = SwAuthorityFieldType::GetAuthTypeName(static_cast<ToxAuthorityType>(--nLevel));
+ }
+ else
+ sText = pField->GetFieldText(static_cast<ToxAuthorityField>(nAuthField));
+ return sText;
+}
+
+OUString SwTOXAuthority::GetSourceURL(const OUString& rText)
+{
+ OUString aText = rText;
+
+ uno::Reference<uri::XUriReferenceFactory> xUriReferenceFactory
+ = uri::UriReferenceFactory::create(comphelper::getProcessComponentContext());
+ uno::Reference<uri::XUriReference> xUriRef;
+ try
+ {
+ xUriRef = xUriReferenceFactory->parse(aText);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("sw.core",
+ "SwTOXAuthority::GetSourceURL: failed to parse url: " << rException.Message);
+ }
+ if (xUriRef.is() && xUriRef->getFragment().startsWith("page="))
+ {
+ xUriRef->clearFragment();
+ aText = xUriRef->getUriReference();
+ }
+
+ return aText;
+}
+
+void SwTOXAuthority::FillText(SwTextNode& rNd, const SwIndex& rInsPos, sal_uInt16 nAuthField,
+ SwRootFrame const* const pLayout) const
+{
+ OUString aText = GetText(nAuthField, pLayout);
+ if (nAuthField == AUTH_FIELD_URL)
+ {
+ aText = GetSourceURL(aText);
+
+ // Convert URL to a relative one if requested.
+ SwDoc* pDoc = static_cast<SwAuthorityFieldType*>(m_rField.GetField()->GetTyp())->GetDoc();
+ SwDocShell* pDocShell = pDoc->GetDocShell();
+ const OUString aBaseURL = pDocShell->getDocumentBaseURL();
+ std::u16string_view aBaseURIScheme;
+ sal_Int32 nSep = aBaseURL.indexOf(':');
+ if (nSep != -1)
+ {
+ aBaseURIScheme = aBaseURL.subView(0, nSep);
+ }
+
+ uno::Reference<uri::XUriReferenceFactory> xUriReferenceFactory
+ = uri::UriReferenceFactory::create(comphelper::getProcessComponentContext());
+ uno::Reference<uri::XUriReference> xUriRef;
+ try
+ {
+ xUriRef = xUriReferenceFactory->parse(aText);
+ }
+ catch (const uno::Exception& rException)
+ {
+ SAL_WARN("sw.core",
+ "SwTOXAuthority::FillText: failed to parse url: " << rException.Message);
+ }
+
+ bool bSaveRelFSys = officecfg::Office::Common::Save::URL::FileSystem::get();
+ if (xUriRef.is() && bSaveRelFSys && xUriRef->getScheme() == aBaseURIScheme)
+ {
+ aText = INetURLObject::GetRelURL(aBaseURL, aText);
+ }
+ }
+
+ rNd.InsertText(aText, rInsPos);
+}
+
+bool SwTOXAuthority::equivalent(const SwTOXSortTabBase& rCmp)
+{
+ if (nType != rCmp.nType)
+ {
+ return false;
+ }
+
+ // Compare our SwAuthEntry and rCmp's SwAuthEntry, but the URL is considered equivalent, as long
+ // as it only differs in a page number, as that's still the same source.
+ const SwAuthEntry* pThis = static_cast<SwAuthorityField*>(m_rField.GetField())->GetAuthEntry();
+ const SwAuthEntry* pOther = static_cast<SwAuthorityField*>(
+ static_cast<const SwTOXAuthority&>(rCmp).m_rField.GetField())
+ ->GetAuthEntry();
+ if (pThis == pOther)
+ {
+ return true;
+ }
+
+ for (int i = 0; i < AUTH_FIELD_END; ++i)
+ {
+ auto eField = static_cast<ToxAuthorityField>(i);
+ if (eField == AUTH_FIELD_URL)
+ {
+ if (GetSourceURL(pThis->GetAuthorField(AUTH_FIELD_URL))
+ != GetSourceURL(pOther->GetAuthorField(AUTH_FIELD_URL)))
+ {
+ return false;
+ }
+ continue;
+ }
+
+ if (pThis->GetAuthorField(eField) != pOther->GetAuthorField(eField))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SwTOXAuthority::sort_lt(const SwTOXSortTabBase& rBase)
+{
+ bool bRet = false;
+ SwAuthorityField* pField = static_cast<SwAuthorityField*>(m_rField.GetField());
+ SwAuthorityFieldType* pType = static_cast<SwAuthorityFieldType*>(
+ pField->GetTyp());
+ if(pType->IsSortByDocument())
+ bRet = SwTOXSortTabBase::sort_lt(rBase);
+ else
+ {
+ SwAuthorityField* pCmpField =
+ static_cast<SwAuthorityField*>(static_cast<const SwTOXAuthority&>(rBase).m_rField.GetField());
+
+ for(sal_uInt16 i = 0; i < pType->GetSortKeyCount(); i++)
+ {
+ const SwTOXSortKey* pKey = pType->GetSortKey(i);
+ const TextAndReading aMy(pField->GetFieldText(pKey->eField), OUString());
+ const TextAndReading aOther(pCmpField->GetFieldText(pKey->eField), OUString());
+
+ sal_Int32 nComp = pTOXIntl->Compare( aMy, GetLocale(),
+ aOther, rBase.GetLocale() );
+
+ if( nComp )
+ {
+ bRet = (-1 == nComp) == pKey->bSortAscending;
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */