diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/tox | |
parent | Initial commit. (diff) | |
download | libreoffice-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.cxx | 90 | ||||
-rw-r--r-- | sw/source/core/tox/ToxTabStopTokenHandler.cxx | 128 | ||||
-rw-r--r-- | sw/source/core/tox/ToxTextGenerator.cxx | 468 | ||||
-rw-r--r-- | sw/source/core/tox/ToxWhitespaceStripper.cxx | 63 | ||||
-rw-r--r-- | sw/source/core/tox/tox.cxx | 949 | ||||
-rw-r--r-- | sw/source/core/tox/toxhlp.cxx | 119 | ||||
-rw-r--r-- | sw/source/core/tox/txmsrt.cxx | 989 |
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: */ |