summaryrefslogtreecommitdiffstats
path: root/sw/source/core/edit/autofmt.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/edit/autofmt.cxx')
-rw-r--r--sw/source/core/edit/autofmt.cxx2831
1 files changed, 2831 insertions, 0 deletions
diff --git a/sw/source/core/edit/autofmt.cxx b/sw/source/core/edit/autofmt.cxx
new file mode 100644
index 000000000..a4e0260f3
--- /dev/null
+++ b/sw/source/core/edit/autofmt.cxx
@@ -0,0 +1,2831 @@
+/* -*- 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 <hintids.hxx>
+
+#include <osl/diagnose.h>
+#include <unotools/charclass.hxx>
+
+#include <editeng/boxitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/formatbreakitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/acorrcfg.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <swwait.hxx>
+#include <fmtpdsc.hxx>
+#include <doc.hxx>
+#include <IDocumentUndoRedo.hxx>
+#include <DocumentRedlineManager.hxx>
+#include <IDocumentStylePoolAccess.hxx>
+#include <redline.hxx>
+#include <unocrsr.hxx>
+#include <docary.hxx>
+#include <editsh.hxx>
+#include <index.hxx>
+#include <pam.hxx>
+#include <swundo.hxx>
+#include <poolfmt.hxx>
+#include <ndtxt.hxx>
+#include <rootfrm.hxx>
+#include <txtfrm.hxx>
+#include <frminf.hxx>
+#include <pagedesc.hxx>
+#include <paratr.hxx>
+#include <acorrect.hxx>
+#include <shellres.hxx>
+#include <section.hxx>
+#include <frmatr.hxx>
+#include <charatr.hxx>
+#include <mdiexp.hxx>
+#include <strings.hrc>
+#include <comcore.hxx>
+#include <numrule.hxx>
+#include <itabenum.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+
+//JP 16.12.99: definition:
+// from pos cPosEnDash to cPosEmDash all chars changed to em dashes,
+// from pos cPosEmDash to cPosEnd all chars changed to em dashes
+// all other chars are changed to the user configuration
+
+const sal_Unicode pBulletChar[6] = { '+', '*', '-', 0x2013, 0x2014, 0 };
+const int cnPosEnDash = 2, cnPosEmDash = 4;
+
+const sal_Unicode cStarSymbolEnDash = 0x2013;
+const sal_Unicode cStarSymbolEmDash = 0x2014;
+
+SvxSwAutoFormatFlags* SwEditShell::s_pAutoFormatFlags = nullptr;
+
+// Number of num-/bullet-paragraph templates. MAXLEVEL will soon be raised
+// to x, but not the number of templates. (Artifact from <= 4.0)
+const sal_uInt16 cnNumBullColls = 4;
+
+class SwAutoFormat
+{
+ SvxSwAutoFormatFlags m_aFlags;
+ SwPaM m_aDelPam; // a Pam that can be used
+ SwNodeIndex m_aNdIdx; // the index on the current TextNode
+ SwNodeIndex m_aEndNdIdx; // index on the end of the area
+
+ SwEditShell* m_pEditShell;
+ SwDoc* m_pDoc;
+ SwTextNode* m_pCurTextNd; // the current TextNode
+ SwTextFrame* m_pCurTextFrame; // frame of the current TextNode
+ bool m_bIsRightToLeft; // text direction of the current frame
+ SwNodeOffset m_nEndNdIdx; // for the percentage-display
+ mutable std::unique_ptr<CharClass> m_pCharClass; // Character classification
+ mutable LanguageType m_eCharClassLang;
+
+ sal_uInt16 m_nRedlAutoFormatSeqId;
+
+ enum
+ {
+ NONE = 0,
+ DELIM = 1,
+ DIGIT = 2,
+ CHG = 4,
+ LOWER_ALPHA = 8,
+ UPPER_ALPHA = 16,
+ LOWER_ROMAN = 32,
+ UPPER_ROMAN = 64,
+ NO_DELIM = (DIGIT|LOWER_ALPHA|UPPER_ALPHA|LOWER_ROMAN|UPPER_ROMAN)
+ };
+
+ bool m_bEnd : 1;
+ bool m_bMoreLines : 1;
+
+ CharClass& GetCharClass( LanguageType eLang ) const
+ {
+ if( !m_pCharClass || eLang != m_eCharClassLang )
+ {
+ m_pCharClass.reset( new CharClass( LanguageTag( eLang ) ) );
+ m_eCharClassLang = eLang;
+ }
+ return *m_pCharClass;
+ }
+
+ static bool IsSpace( const sal_Unicode c )
+ { return (' ' == c || '\t' == c || 0x0a == c|| 0x3000 == c /* Jap. space */); }
+
+ void SetColl( sal_uInt16 nId, bool bHdLineOrText = false );
+ void GoNextPara();
+ static bool HasObjects(const SwTextFrame &);
+
+ // TextNode methods
+ const SwTextFrame * GetNextNode(bool isCheckEnd = true) const;
+ static bool IsEmptyLine(const SwTextFrame & rFrame)
+ {
+ return rFrame.GetText().isEmpty()
+ || rFrame.GetText().getLength() == GetLeadingBlanks(rFrame.GetText());
+ }
+
+ bool IsOneLine(const SwTextFrame &) const;
+ bool IsFastFullLine(const SwTextFrame &) const;
+ bool IsNoAlphaLine(const SwTextFrame &) const;
+ bool IsEnumericChar(const SwTextFrame &) const;
+ static bool IsBlanksInString(const SwTextFrame&);
+ sal_uInt16 CalcLevel(const SwTextFrame&, sal_uInt16 *pDigitLvl = nullptr) const;
+ sal_Int32 GetBigIndent(TextFrameIndex & rCurrentSpacePos) const;
+
+ static OUString DelLeadingBlanks(const OUString& rStr);
+ static OUString DelTrailingBlanks( const OUString& rStr );
+ static sal_Int32 GetLeadingBlanks( const OUString& rStr );
+ static sal_Int32 GetTrailingBlanks( const OUString& rStr );
+
+ bool IsFirstCharCapital(const SwTextFrame & rNd) const;
+ sal_uInt16 GetDigitLevel(const SwTextFrame& rFrame, TextFrameIndex& rPos,
+ OUString* pPrefix = nullptr, OUString* pPostfix = nullptr,
+ OUString* pNumTypes = nullptr ) const;
+ /// get the FORMATTED TextFrame
+ SwTextFrame* GetFrame( const SwTextNode& rTextNd ) const;
+ SwTextFrame * EnsureFormatted(SwTextFrame const&) const;
+
+ void BuildIndent();
+ void BuildText();
+ void BuildTextIndent();
+ void BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel );
+ void BuildNegIndent( SwTwips nSpaces );
+ void BuildHeadLine( sal_uInt16 nLvl );
+
+ static bool HasBreakAttr(const SwTextFrame &);
+ void DeleteSel( SwPaM& rPam );
+ void DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect);
+ bool DeleteJoinCurNextPara(SwTextFrame const* pNextFrame, bool bIgnoreLeadingBlanks = false);
+ /// delete in the node start and/or end
+ void DeleteLeadingTrailingBlanks( bool bStart = true, bool bEnd = true );
+ void DelEmptyLine( bool bTstNextPara = true );
+ /// when using multiline paragraphs delete the "left" and/or
+ /// "right" margins
+ void DelMoreLinesBlanks( bool bWithLineBreaks = false );
+ /// join with the previous paragraph
+ void JoinPrevPara();
+ /// execute AutoCorrect on current TextNode
+ void AutoCorrect(TextFrameIndex nSttPos = TextFrameIndex(0));
+
+ bool CanJoin(const SwTextFrame * pNextFrame) const
+ {
+ return !m_bEnd && pNextFrame
+ && !IsEmptyLine(*pNextFrame)
+ && !IsNoAlphaLine(*pNextFrame)
+ && !IsEnumericChar(*pNextFrame)
+ // check the last / first nodes here...
+ && ((COMPLETE_STRING - 50 - pNextFrame->GetTextNodeFirst()->GetText().getLength())
+ > (m_pCurTextFrame->GetMergedPara()
+ ? m_pCurTextFrame->GetMergedPara()->pLastNode
+ : m_pCurTextNd)->GetText().getLength())
+ && !HasBreakAttr(*pNextFrame);
+ }
+
+ /// is a dot at the end ??
+ static bool IsSentenceAtEnd(const SwTextFrame & rTextFrame);
+
+ bool DoUnderline();
+ bool DoTable();
+
+ void SetRedlineText_( sal_uInt16 nId );
+ bool SetRedlineText( sal_uInt16 nId ) {
+ if( m_aFlags.bWithRedlining )
+ SetRedlineText_( nId );
+ return true;
+ }
+ void ClearRedlineText() {
+ if( m_aFlags.bWithRedlining )
+ m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment(nullptr);
+ }
+
+public:
+ SwAutoFormat( SwEditShell* pEdShell, SvxSwAutoFormatFlags const & rFlags,
+ SwNodeIndex const * pSttNd = nullptr, SwNodeIndex const * pEndNd = nullptr );
+};
+
+static const sal_Unicode* StrChr( const sal_Unicode* pSrc, sal_Unicode c )
+{
+ while( *pSrc && *pSrc != c )
+ ++pSrc;
+ return *pSrc ? pSrc : nullptr;
+}
+
+SwTextFrame* SwAutoFormat::GetFrame( const SwTextNode& rTextNd ) const
+{
+ // get the Frame
+ const SwContentFrame *pFrame = rTextNd.getLayoutFrame( m_pEditShell->GetLayout() );
+ assert(pFrame && "For Autoformat a Layout is needed");
+ return EnsureFormatted(*static_cast<SwTextFrame const*>(pFrame));
+}
+
+SwTextFrame * SwAutoFormat::EnsureFormatted(SwTextFrame const& rFrame) const
+{
+ SwTextFrame *const pFrame(const_cast<SwTextFrame*>(&rFrame));
+ if( m_aFlags.bAFormatByInput && !pFrame->isFrameAreaDefinitionValid() )
+ {
+ DisableCallbackAction a(*pFrame->getRootFrame());
+ SwRect aTmpFrame( pFrame->getFrameArea() );
+ SwRect aTmpPrt( pFrame->getFramePrintArea() );
+ pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
+
+ if( pFrame->getFrameArea() != aTmpFrame || pFrame->getFramePrintArea() != aTmpPrt ||
+ !pFrame->GetPaintSwRect().IsEmpty())
+ {
+ pFrame->SetCompletePaint();
+ }
+ }
+
+ return pFrame->GetFormatted();
+}
+
+void SwAutoFormat::SetRedlineText_( sal_uInt16 nActionId )
+{
+ OUString sText;
+ sal_uInt16 nSeqNo = 0;
+ if( STR_AUTOFMTREDL_END > nActionId )
+ {
+ sText = SwViewShell::GetShellRes()->GetAutoFormatNameLst()[ nActionId ];
+ switch( nActionId )
+ {
+ case STR_AUTOFMTREDL_SET_NUMBER_BULLET:
+ case STR_AUTOFMTREDL_DEL_MORELINES:
+
+ // AutoCorrect actions
+ case STR_AUTOFMTREDL_USE_REPLACE:
+ case STR_AUTOFMTREDL_CPTL_STT_WORD:
+ case STR_AUTOFMTREDL_CPTL_STT_SENT:
+ case STR_AUTOFMTREDL_TYPO:
+ case STR_AUTOFMTREDL_UNDER:
+ case STR_AUTOFMTREDL_BOLD:
+ case STR_AUTOFMTREDL_FRACTION:
+ case STR_AUTOFMTREDL_DASH:
+ case STR_AUTOFMTREDL_ORDINAL:
+ case STR_AUTOFMTREDL_NON_BREAK_SPACE:
+ case STR_AUTOFMTREDL_TRANSLITERATE_RTL:
+ nSeqNo = ++m_nRedlAutoFormatSeqId;
+ break;
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ sText = "Action text is missing";
+#endif
+
+ m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment( &sText, nSeqNo );
+}
+
+void SwAutoFormat::GoNextPara()
+{
+ SwNode* pNewNd = nullptr;
+ do {
+ // has to be checked twice before and after incrementation
+ if( m_aNdIdx.GetIndex() >= m_aEndNdIdx.GetIndex() )
+ {
+ m_bEnd = true;
+ return;
+ }
+
+ sw::GotoNextLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
+ if( m_aNdIdx.GetIndex() >= m_aEndNdIdx.GetIndex() )
+ {
+ m_bEnd = true;
+ return;
+ }
+ else
+ pNewNd = &m_aNdIdx.GetNode();
+
+ // not a TextNode ->
+ // TableNode : skip table
+ // NoTextNode : skip nodes
+ // EndNode : at the end, terminate
+ if( pNewNd->IsEndNode() )
+ {
+ m_bEnd = true;
+ return;
+ }
+ else if( pNewNd->IsTableNode() )
+ m_aNdIdx = *pNewNd->EndOfSectionNode();
+ else if( pNewNd->IsSectionNode() )
+ {
+ const SwSection& rSect = pNewNd->GetSectionNode()->GetSection();
+ if( rSect.IsHiddenFlag() || rSect.IsProtectFlag() )
+ m_aNdIdx = *pNewNd->EndOfSectionNode();
+ }
+ } while( !pNewNd->IsTextNode() );
+
+ if( !m_aFlags.bAFormatByInput )
+ ::SetProgressState( sal_Int32(m_aNdIdx.GetIndex() + m_nEndNdIdx - m_aEndNdIdx.GetIndex()),
+ m_pDoc->GetDocShell() );
+
+ m_pCurTextNd = static_cast<SwTextNode*>(pNewNd);
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ m_bIsRightToLeft = m_pCurTextFrame->IsRightToLeft();
+}
+
+bool SwAutoFormat::HasObjects(const SwTextFrame & rFrame)
+{
+ // Is there something bound to the paragraph in the paragraph
+ // like Frames, DrawObjects, ...
+ SwNodeIndex node(*rFrame.GetTextNodeFirst());
+ do
+ {
+ if (!node.GetNode().GetAnchoredFlys().empty())
+ return true;
+ ++node;
+ }
+ while (sw::FrameContainsNode(rFrame, node.GetIndex()));
+ return false;
+}
+
+const SwTextFrame* SwAutoFormat::GetNextNode(bool const isCheckEnd) const
+{
+ SwNodeIndex tmp(m_aNdIdx);
+ sw::GotoNextLayoutTextFrame(tmp, m_pEditShell->GetLayout());
+ if ((isCheckEnd && m_aEndNdIdx <= tmp) || !tmp.GetNode().IsTextNode())
+ return nullptr;
+ // note: the returned frame is not necessarily formatted, have to call
+ // EnsureFormatted for that
+ return static_cast<SwTextFrame*>(tmp.GetNode().GetTextNode()->getLayoutFrame(m_pEditShell->GetLayout()));
+}
+
+bool SwAutoFormat::IsOneLine(const SwTextFrame & rFrame) const
+{
+ SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
+ return aFInfo.IsOneLine();
+}
+
+bool SwAutoFormat::IsFastFullLine(const SwTextFrame & rFrame) const
+{
+ bool bRet = m_aFlags.bRightMargin;
+ if( bRet )
+ {
+ SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
+ bRet = aFInfo.IsFilled( m_aFlags.nRightMargin );
+ }
+ return bRet;
+}
+
+bool SwAutoFormat::IsEnumericChar(const SwTextFrame& rFrame) const
+{
+ const OUString& rText = rFrame.GetText();
+ TextFrameIndex nBlanks(GetLeadingBlanks(rText));
+ const TextFrameIndex nLen = TextFrameIndex(rText.getLength()) - nBlanks;
+ if( !nLen )
+ return false;
+
+ // -, +, * separated by blank ??
+ if (TextFrameIndex(2) < nLen && IsSpace(rText[sal_Int32(nBlanks) + 1]))
+ {
+ if (StrChr(pBulletChar, rText[sal_Int32(nBlanks)]))
+ return true;
+ // Should there be a symbol font at the position?
+ SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
+ if (aFInfo.IsBullet(nBlanks))
+ return true;
+ }
+
+ // 1.) / 1. / 1.1.1 / (1). / (1) / ...
+ return USHRT_MAX != GetDigitLevel(rFrame, nBlanks);
+}
+
+bool SwAutoFormat::IsBlanksInString(const SwTextFrame& rFrame)
+{
+ // Search more than 5 consecutive blanks/tabs in the string.
+ OUString sTmp( DelLeadingBlanks(rFrame.GetText()) );
+ const sal_Int32 nLen = sTmp.getLength();
+ sal_Int32 nIdx = 0;
+ while (nIdx < nLen)
+ {
+ // Skip non-blanks
+ while (nIdx < nLen && !IsSpace(sTmp[nIdx])) ++nIdx;
+ if (nIdx == nLen)
+ return false;
+ // Then count consecutive blanks
+ const sal_Int32 nFirst = nIdx;
+ while (nIdx < nLen && IsSpace(sTmp[nIdx])) ++nIdx;
+ // And exit if enough consecutive blanks were found
+ if (nIdx-nFirst > 5)
+ return true;
+ }
+ return false;
+}
+
+sal_uInt16 SwAutoFormat::CalcLevel(const SwTextFrame & rFrame,
+ sal_uInt16 *const pDigitLvl) const
+{
+ sal_uInt16 nLvl = 0, nBlnk = 0;
+ const OUString& rText = rFrame.GetText();
+ if( pDigitLvl )
+ *pDigitLvl = USHRT_MAX;
+
+ if (RES_POOLCOLL_TEXT_MOVE == rFrame.GetTextNodeForParaProps()->GetTextColl()->GetPoolFormatId())
+ {
+ if( m_aFlags.bAFormatByInput )
+ {
+ // this is very non-obvious: on the *first* invocation of
+ // AutoFormat, the node will have the tabs (any number) converted
+ // to a fixed indent in BuildTextIndent(), and the number of tabs
+ // is stored in the node;
+ // on the *second* invocation of AutoFormat, CalcLevel() will
+ // retrieve the stored number, and it will be used by
+ // BuildHeadLine() to select the corresponding heading style.
+ nLvl = rFrame.GetTextNodeForParaProps()->GetAutoFormatLvl();
+ const_cast<SwTextNode *>(rFrame.GetTextNodeForParaProps())->SetAutoFormatLvl(0);
+ if( nLvl )
+ return nLvl;
+ }
+ ++nLvl;
+ }
+
+ for (TextFrameIndex n(0),
+ nEnd(rText.getLength()); n < nEnd; ++n)
+ {
+ switch (rText[sal_Int32(n)])
+ {
+ case ' ': if( 3 == ++nBlnk )
+ {
+ ++nLvl;
+ nBlnk = 0;
+ }
+ break;
+ case '\t': ++nLvl;
+ nBlnk = 0;
+ break;
+ default:
+ if( pDigitLvl )
+ // test 1.) / 1. / 1.1.1 / (1). / (1) / ...
+ *pDigitLvl = GetDigitLevel(rFrame, n);
+ return nLvl;
+ }
+ }
+ return nLvl;
+}
+
+sal_Int32 SwAutoFormat::GetBigIndent(TextFrameIndex & rCurrentSpacePos) const
+{
+ SwTextFrameInfo aFInfo( m_pCurTextFrame );
+ const SwTextFrame* pNextFrame = nullptr;
+
+ if( !m_bMoreLines )
+ {
+ pNextFrame = GetNextNode();
+ if (!CanJoin(pNextFrame) || !IsOneLine(*pNextFrame))
+ return 0;
+
+ pNextFrame = EnsureFormatted(*pNextFrame);
+ }
+
+ return aFInfo.GetBigIndent( rCurrentSpacePos, pNextFrame );
+}
+
+bool SwAutoFormat::IsNoAlphaLine(const SwTextFrame & rFrame) const
+{
+ const OUString& rStr = rFrame.GetText();
+ if( rStr.isEmpty() )
+ return false;
+ // or better: determine via number of AlphaNum and !AlphaNum characters
+ sal_Int32 nANChar = 0, nBlnk = 0;
+
+ for (TextFrameIndex n(0),
+ nEnd(rStr.getLength()); n < nEnd; ++n)
+ if (IsSpace(rStr[sal_Int32(n)]))
+ ++nBlnk;
+ else
+ {
+ auto const pair = rFrame.MapViewToModel(n);
+ CharClass& rCC = GetCharClass(pair.first->GetSwAttrSet().GetLanguage().GetLanguage());
+ if (rCC.isLetterNumeric(rStr, sal_Int32(n)))
+ ++nANChar;
+ }
+
+ // If there are 75% of non-alphanumeric characters, then true
+ sal_uLong nLen = rStr.getLength() - nBlnk;
+ nLen = ( nLen * 3 ) / 4; // long overflow, if the strlen > sal_uInt16
+ return sal_Int32(nLen) < (rStr.getLength() - nANChar - nBlnk);
+}
+
+bool SwAutoFormat::DoUnderline()
+{
+ if( !m_aFlags.bSetBorder )
+ return false;
+
+ OUString const& rText(m_pCurTextFrame->GetText());
+ int eState = 0;
+ sal_Int32 nCnt = 0;
+ while (nCnt < rText.getLength())
+ {
+ int eTmp = 0;
+ switch (rText[nCnt])
+ {
+ case '-': eTmp = 1; break;
+ case '_': eTmp = 2; break;
+ case '=': eTmp = 3; break;
+ case '*': eTmp = 4; break;
+ case '~': eTmp = 5; break;
+ case '#': eTmp = 6; break;
+ default:
+ return false;
+ }
+ if( 0 == eState )
+ eState = eTmp;
+ else if( eState != eTmp )
+ return false;
+ ++nCnt;
+ }
+
+ if( 2 < nCnt )
+ {
+ // then underline the previous paragraph if one exists
+ DelEmptyLine( false ); // -> point will be on end of current paragraph
+ // WARNING: rText may be deleted now, m_pCurTextFrame may be nullptr
+ m_aDelPam.SetMark();
+ // apply to last node & rely on InsertItemSet to apply it to props-node
+ m_aDelPam.GetMark()->nContent = 0;
+
+ editeng::SvxBorderLine aLine;
+ switch( eState )
+ {
+ case 1: // single, hairline
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
+ aLine.SetWidth( SvxBorderLineWidth::Hairline );
+ break;
+ case 2: // single, thin
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
+ aLine.SetWidth( SvxBorderLineWidth::Thin );
+ break;
+ case 3: // double, thin
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ aLine.SetWidth( SvxBorderLineWidth::Thin );
+ break;
+ case 4: // double, thick/thin
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_SMALLGAP);
+ aLine.SetWidth( SvxBorderLineWidth::Thick );
+ break;
+ case 5: // double, thin/thick
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_SMALLGAP);
+ aLine.SetWidth( SvxBorderLineWidth::Thick );
+ break;
+ case 6: // double, medium
+ aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
+ aLine.SetWidth( SvxBorderLineWidth::Medium );
+ break;
+ }
+ SfxItemSetFixed<RES_PARATR_CONNECT_BORDER, RES_PARATR_CONNECT_BORDER,
+ RES_BOX, RES_BOX> aSet(m_pDoc->GetAttrPool());
+ aSet.Put( SwParaConnectBorderItem( false ) );
+ SvxBoxItem aBox( RES_BOX );
+ aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
+ aBox.SetDistance(42, SvxBoxItemLine::BOTTOM ); // ~0,75 mm
+ aSet.Put(aBox);
+ m_pDoc->getIDocumentContentOperations().InsertItemSet(m_aDelPam, aSet,
+ SetAttrMode::DEFAULT, m_pEditShell->GetLayout());
+
+ m_aDelPam.DeleteMark();
+ }
+ return 2 < nCnt;
+}
+
+bool SwAutoFormat::DoTable()
+{
+ if( !m_aFlags.bCreateTable || !m_aFlags.bAFormatByInput ||
+ m_pCurTextNd->FindTableNode() )
+ return false;
+
+ const OUString& rTmp = m_pCurTextFrame->GetText();
+ TextFrameIndex nSttPlus(GetLeadingBlanks(rTmp));
+ TextFrameIndex nEndPlus(GetTrailingBlanks(rTmp));
+ sal_Unicode cChar;
+
+ if (TextFrameIndex(2) > nEndPlus - nSttPlus
+ || ('+' != (cChar = rTmp[sal_Int32(nSttPlus)]) && '|' != cChar)
+ || ('+' != (cChar = rTmp[sal_Int32(nEndPlus) - 1]) && '|' != cChar))
+ return false;
+
+ SwTextFrameInfo aInfo( m_pCurTextFrame );
+
+ TextFrameIndex n = nSttPlus;
+ std::vector<sal_uInt16> aPosArr;
+
+ while (n < TextFrameIndex(rTmp.getLength()))
+ {
+ switch (rTmp[sal_Int32(n)])
+ {
+ case '-':
+ case '_':
+ case '=':
+ case ' ':
+ case '\t':
+ break;
+
+ case '+':
+ case '|':
+ aPosArr.push_back( o3tl::narrowing<sal_uInt16>(aInfo.GetCharPos(n)) );
+ break;
+
+ default:
+ return false;
+ }
+ if( ++n == nEndPlus )
+ break;
+ }
+
+ if( 1 < aPosArr.size() )
+ {
+ // get the text node's alignment
+ sal_uInt16 nColCnt = aPosArr.size() - 1;
+ SwTwips nSttPos = aPosArr[ 0 ];
+ sal_Int16 eHori;
+ switch (m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust())
+ {
+ case SvxAdjust::Center: eHori = text::HoriOrientation::CENTER; break;
+ case SvxAdjust::Right: eHori = text::HoriOrientation::RIGHT; break;
+
+ default:
+ if( nSttPos )
+ {
+ eHori = text::HoriOrientation::NONE;
+ // then - as last - we need to add the current frame width into the array
+ aPosArr.push_back( o3tl::narrowing<sal_uInt16>(m_pCurTextFrame->getFrameArea().Width()) );
+ }
+ else
+ eHori = text::HoriOrientation::LEFT;
+ break;
+ }
+
+ // then create a table that matches the character
+ DelEmptyLine();
+ // WARNING: rTmp may be deleted now, m_pCurTextFrame may be nullptr
+ SwNodeIndex aIdx( m_aDelPam.GetPoint()->nNode );
+ m_aDelPam.Move( fnMoveForward );
+ m_pDoc->InsertTable( SwInsertTableOptions( SwInsertTableFlags::All , 1 ),
+ *m_aDelPam.GetPoint(), 1, nColCnt, eHori,
+ nullptr, &aPosArr );
+ m_aDelPam.GetPoint()->nNode = aIdx;
+ }
+ return 1 < aPosArr.size();
+}
+
+OUString SwAutoFormat::DelLeadingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL, n;
+ for( nL = rStr.getLength(), n = 0; n < nL && IsSpace( rStr[n] ); ++n )
+ ;
+ if( n ) // no Spaces
+ return rStr.copy(n);
+ return rStr;
+}
+
+OUString SwAutoFormat::DelTrailingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL = rStr.getLength(), n = nL;
+ if( !nL )
+ return rStr;
+
+ while( --n && IsSpace( rStr[ n ] ) )
+ ;
+ if( n+1 != nL ) // no Spaces
+ return rStr.copy( 0, n+1 );
+ return rStr;
+}
+
+sal_Int32 SwAutoFormat::GetLeadingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL;
+ sal_Int32 n;
+
+ for( nL = rStr.getLength(), n = 0; n < nL && IsSpace( rStr[ n ] ); ++n )
+ ;
+ return n;
+}
+
+sal_Int32 SwAutoFormat::GetTrailingBlanks( const OUString& rStr )
+{
+ sal_Int32 nL = rStr.getLength(), n = nL;
+ if( !nL )
+ return 0;
+
+ while( --n && IsSpace( rStr[ n ] ) )
+ ;
+ return ++n;
+}
+
+bool SwAutoFormat::IsFirstCharCapital(const SwTextFrame& rFrame) const
+{
+ const OUString& rText = rFrame.GetText();
+ for (TextFrameIndex n(0),
+ nEnd(rText.getLength()); n < nEnd; ++n)
+ if (!IsSpace(rText[sal_Int32(n)]))
+ {
+ auto const pair = rFrame.MapViewToModel(n);
+ CharClass& rCC = GetCharClass( pair.first->GetSwAttrSet().
+ GetLanguage().GetLanguage() );
+ sal_Int32 nCharType = rCC.getCharacterType(rText, sal_Int32(n));
+ return CharClass::isLetterType( nCharType ) &&
+ 0 != ( i18n::KCharacterType::UPPER &
+ nCharType );
+ }
+ return false;
+}
+
+sal_uInt16
+SwAutoFormat::GetDigitLevel(const SwTextFrame& rFrame, TextFrameIndex& rPos,
+ OUString* pPrefix, OUString* pPostfix, OUString* pNumTypes ) const
+{
+
+ // check for 1.) / 1. / 1.1.1 / (1). / (1) / ...
+ const OUString& rText = rFrame.GetText();
+ sal_Int32 nPos(rPos);
+ int eScan = NONE;
+
+ sal_uInt16 nStart = 0;
+ sal_uInt8 nDigitLvl = 0, nDigitCnt = 0;
+ // count number of parenthesis to assure a sensible order is found
+ sal_uInt16 nOpeningParentheses = 0;
+ sal_uInt16 nClosingParentheses = 0;
+
+ while (nPos < rText.getLength() && nDigitLvl < MAXLEVEL - 1)
+ {
+ auto const pair = rFrame.MapViewToModel(TextFrameIndex(nPos));
+ CharClass& rCC = GetCharClass(pair.first->GetSwAttrSet().GetLanguage().GetLanguage());
+ const sal_Unicode cCurrentChar = rText[nPos];
+ if( ('0' <= cCurrentChar && '9' >= cCurrentChar) ||
+ (0xff10 <= cCurrentChar && 0xff19 >= cCurrentChar) )
+ {
+ if( eScan & DELIM )
+ {
+ if( eScan & CHG ) // not if it starts with a number
+ {
+ ++nDigitLvl;
+ if( pPostfix )
+ *pPostfix += "\x01";
+ }
+
+ if( pNumTypes )
+ *pNumTypes += OUStringChar(sal_Unicode('0' + SVX_NUM_ARABIC));
+
+ eScan = eScan | CHG;
+ }
+ else if( pNumTypes && !(eScan & DIGIT) )
+ *pNumTypes += OUStringChar(sal_Unicode('0' + SVX_NUM_ARABIC));
+
+ eScan &= ~DELIM; // remove Delim
+ if( 0 != (eScan & ~CHG) && DIGIT != (eScan & ~CHG))
+ return USHRT_MAX;
+
+ eScan |= DIGIT; // add Digit
+ if( 3 == ++nDigitCnt ) // more than 2 numbers are not an enum anymore
+ return USHRT_MAX;
+
+ nStart *= 10;
+ nStart += cCurrentChar <= '9' ? cCurrentChar - '0' : cCurrentChar - 0xff10;
+ }
+ else if( rCC.isAlpha( rText, nPos ) )
+ {
+ bool bIsUpper =
+ 0 != ( i18n::KCharacterType::UPPER &
+ rCC.getCharacterType( rText, nPos ));
+ sal_Unicode cLow = rCC.lowercase(rText, nPos, 1)[0], cNumTyp;
+ int eTmpScan;
+
+ // Roman numbers are "mdclxvi". Since we want to start numbering with c or d more often,
+ // convert first to characters and later to roman numbers if needed.
+ if( 256 > cLow && strchr( "mdclxvi", cLow ) )
+ {
+ if( bIsUpper )
+ {
+ cNumTyp = '0' + SVX_NUM_ROMAN_UPPER;
+ eTmpScan = UPPER_ROMAN;
+ }
+ else
+ {
+ cNumTyp = '0' + SVX_NUM_ROMAN_LOWER;
+ eTmpScan = LOWER_ROMAN;
+ }
+ }
+ else if( bIsUpper )
+ {
+ cNumTyp = '0' + SVX_NUM_CHARS_UPPER_LETTER;
+ eTmpScan = UPPER_ALPHA;
+ }
+ else
+ {
+ cNumTyp = '0' + SVX_NUM_CHARS_LOWER_LETTER;
+ eTmpScan = LOWER_ALPHA;
+ }
+
+ // Switch to roman numbers (only for c/d!)
+ if( 1 == nDigitCnt && ( eScan & (UPPER_ALPHA|LOWER_ALPHA) ) &&
+ ( 3 == nStart || 4 == nStart) && 256 > cLow &&
+ strchr( "mdclxvi", cLow ) &&
+ (( eScan & UPPER_ALPHA ) ? (eTmpScan & (UPPER_ALPHA|UPPER_ROMAN))
+ : (eTmpScan & (LOWER_ALPHA|LOWER_ROMAN))) )
+ {
+ sal_Unicode c = '0';
+ nStart = 3 == nStart ? 100 : 500;
+ if( UPPER_ALPHA == eTmpScan )
+ {
+ eTmpScan = UPPER_ROMAN;
+ c += SVX_NUM_ROMAN_UPPER;
+ }
+ else
+ {
+ eTmpScan = LOWER_ROMAN;
+ c += SVX_NUM_ROMAN_LOWER;
+ }
+
+ eScan = (eScan & ~(UPPER_ALPHA|LOWER_ALPHA)) | eTmpScan;
+ if( pNumTypes )
+ (*pNumTypes) = pNumTypes->replaceAt( pNumTypes->getLength() - 1, 1, rtl::OUStringChar(c) );
+ }
+
+ if( eScan & DELIM )
+ {
+ if( eScan & CHG ) // not if it starts with a number
+ {
+ ++nDigitLvl;
+ if( pPostfix )
+ *pPostfix += "\x01";
+ }
+
+ if( pNumTypes )
+ *pNumTypes += OUStringChar(cNumTyp);
+ eScan = eScan | CHG;
+ }
+ else if( pNumTypes && !(eScan & eTmpScan) )
+ *pNumTypes += OUStringChar(cNumTyp);
+
+ eScan &= ~DELIM; // remove Delim
+
+ // if another type is set, stop here
+ if( 0 != ( eScan & ~CHG ) && eTmpScan != ( eScan & ~CHG ))
+ return USHRT_MAX;
+
+ if( eTmpScan & (UPPER_ALPHA | LOWER_ALPHA) )
+ {
+ // allow characters only if they appear once
+ return USHRT_MAX;
+ }
+ else
+ {
+ // roman numbers, check if valid characters
+ sal_uInt16 nVal;
+ bool bError = false;
+ switch( cLow )
+ {
+ case 'm': nVal = 1000; goto CHECK_ROMAN_1;
+ case 'd': nVal = 500; goto CHECK_ROMAN_5;
+ case 'c': nVal = 100; goto CHECK_ROMAN_1;
+ case 'l': nVal = 50; goto CHECK_ROMAN_5;
+ case 'x': nVal = 10; goto CHECK_ROMAN_1;
+ case 'v': nVal = 5; goto CHECK_ROMAN_5;
+
+CHECK_ROMAN_1:
+ {
+ int nMod5 = nStart % (nVal * 5);
+ int nLast = nStart % nVal;
+ int n10 = nVal / 10;
+
+ if( nMod5 == ((3 * nVal) + n10 ) ||
+ nMod5 == ((4 * nVal) + n10 ) ||
+ nLast == n10 )
+ nStart = o3tl::narrowing<sal_uInt16>(nStart + (n10 * 8));
+ else if( nMod5 == 0 ||
+ nMod5 == (1 * nVal) ||
+ nMod5 == (2 * nVal) )
+ nStart = nStart + nVal;
+ else
+ bError = true;
+ }
+ break;
+
+CHECK_ROMAN_5:
+ {
+ if( ( nStart / nVal ) & 1 )
+ bError = true;
+ else
+ {
+ int nMod = nStart % nVal;
+ int n10 = nVal / 5;
+ if( n10 == nMod )
+ nStart = o3tl::narrowing<sal_uInt16>(nStart + (3 * n10));
+ else if( 0 == nMod )
+ nStart = nStart + nVal;
+ else
+ bError = true;
+ }
+ }
+ break;
+
+ case 'i':
+ if( nStart % 5 >= 3 )
+ bError = true;
+ else
+ nStart += 1;
+ break;
+
+ default:
+ bError = true;
+ }
+
+ if( bError )
+ return USHRT_MAX;
+ }
+ eScan |= eTmpScan; // add Digit
+ ++nDigitCnt;
+ }
+ else if( (256 > cCurrentChar &&
+ strchr( ".)(", cCurrentChar )) ||
+ 0x3002 == cCurrentChar /* Chinese trad. dot */||
+ 0xff0e == cCurrentChar /* Japanese dot */||
+ 0xFF08 == cCurrentChar /* opening bracket Chin./Jap.*/||
+ 0xFF09 == cCurrentChar )/* closing bracket Chin./Jap. */
+ {
+ if(cCurrentChar == '(' || cCurrentChar == 0xFF09)
+ nOpeningParentheses++;
+ else if(cCurrentChar == ')'|| cCurrentChar == 0xFF08)
+ nClosingParentheses++;
+ // only if no numbers were read until here
+ if( pPrefix && !( eScan & ( NO_DELIM | CHG )) )
+ *pPrefix += OUStringChar(rText[nPos]);
+ else if( pPostfix )
+ *pPostfix += OUStringChar(rText[nPos]);
+
+ if( NO_DELIM & eScan )
+ {
+ eScan |= CHG;
+ if( pPrefix )
+ *pPrefix += "\x01" + OUString::number( nStart );
+ }
+ eScan &= ~NO_DELIM; // remove Delim
+ eScan |= DELIM; // add Digit
+ nDigitCnt = 0;
+ nStart = 0;
+ }
+ else
+ break;
+ ++nPos;
+ }
+ if (!( CHG & eScan ) || rPos == TextFrameIndex(nPos) ||
+ nPos == rText.getLength() || !IsSpace(rText[nPos]) ||
+ (nOpeningParentheses > nClosingParentheses))
+ return USHRT_MAX;
+
+ if( (NO_DELIM & eScan) && pPrefix ) // do not forget the last one
+ *pPrefix += "\x01" + OUString::number( nStart );
+
+ rPos = TextFrameIndex(nPos);
+ return nDigitLvl; // 0 .. 9 (MAXLEVEL - 1)
+}
+
+void SwAutoFormat::SetColl( sal_uInt16 nId, bool bHdLineOrText )
+{
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeForParaProps();
+ m_aDelPam.GetPoint()->nContent.Assign(m_aDelPam.GetPoint()->nNode.GetNode().GetContentNode(), 0);
+
+ // keep hard tabs, alignment, language, hyphenation, DropCaps and nearly all frame attributes
+ SfxItemSetFixed<
+ RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE,
+ RES_PARATR_ADJUST, RES_PARATR_ADJUST,
+ RES_PARATR_TABSTOP, RES_PARATR_DROP,
+ RES_BACKGROUND, RES_SHADOW> aSet(m_pDoc->GetAttrPool());
+
+ if (m_aDelPam.GetPoint()->nNode.GetNode().GetTextNode()->HasSwAttrSet())
+ {
+ aSet.Put(*m_aDelPam.GetPoint()->nNode.GetNode().GetTextNode()->GetpSwAttrSet());
+ // take HeaderLine/TextBody only if centered or right aligned, otherwise only justification
+ if( SvxAdjustItem const * pAdj = aSet.GetItemIfSet( RES_PARATR_ADJUST, false) )
+ {
+ SvxAdjust eAdj = pAdj->GetAdjust();
+ if( bHdLineOrText ? (SvxAdjust::Right != eAdj &&
+ SvxAdjust::Center != eAdj)
+ : SvxAdjust::Block != eAdj )
+ aSet.ClearItem( RES_PARATR_ADJUST );
+ }
+ }
+
+ m_pDoc->SetTextFormatCollByAutoFormat( *m_aDelPam.GetPoint(), nId, &aSet );
+}
+
+static bool HasSelBlanks(
+ SwTextFrame const*const pStartFrame, TextFrameIndex & rStartIndex,
+ SwTextFrame const*const pEndFrame, TextFrameIndex & rEndIndex)
+{
+ if (TextFrameIndex(0) < rEndIndex
+ && rEndIndex < TextFrameIndex(pEndFrame->GetText().getLength())
+ && ' ' == pEndFrame->GetText()[sal_Int32(rEndIndex) - 1])
+ {
+ --rEndIndex;
+ return true;
+ }
+ if (rStartIndex < TextFrameIndex(pStartFrame->GetText().getLength())
+ && ' ' == pStartFrame->GetText()[sal_Int32(rStartIndex)])
+ {
+ ++rStartIndex;
+ return true;
+ }
+ return false;
+}
+
+bool SwAutoFormat::HasBreakAttr(const SwTextFrame& rTextFrame)
+{
+ const SfxItemSet *const pSet = rTextFrame.GetTextNodeFirst()->GetpSwAttrSet();
+ if( !pSet )
+ return false;
+
+ const SvxFormatBreakItem* pBreakItem = pSet->GetItemIfSet( RES_BREAK, false );
+ if( pBreakItem && SvxBreak::NONE != pBreakItem->GetBreak() )
+ return true;
+
+ const SwFormatPageDesc* pItem = pSet->GetItemIfSet( RES_PAGEDESC, false );
+ if( pItem && pItem->GetPageDesc()
+ && UseOnPage::NONE != pItem->GetPageDesc()->GetUseOn() )
+ return true;
+ return false;
+}
+
+/// Is there a dot at the end?
+bool SwAutoFormat::IsSentenceAtEnd(const SwTextFrame & rTextFrame)
+{
+ const OUString& rStr = rTextFrame.GetText();
+ sal_Int32 n = rStr.getLength();
+ if( !n )
+ return true;
+
+ while( --n && IsSpace( rStr[ n ] ) )
+ ;
+ return '.' == rStr[ n ];
+}
+
+/// Delete beginning and/or end in a node
+void SwAutoFormat::DeleteLeadingTrailingBlanks(bool bStart, bool bEnd)
+{
+ if( !(m_aFlags.bAFormatByInput
+ ? m_aFlags.bAFormatByInpDelSpacesAtSttEnd
+ : m_aFlags.bAFormatDelSpacesAtSttEnd) )
+ return;
+
+ // delete blanks at the end of the current and at the beginning of the next one
+ m_aDelPam.DeleteMark();
+ TextFrameIndex nPos(GetLeadingBlanks(m_pCurTextFrame->GetText()));
+ if (bStart && TextFrameIndex(0) != nPos)
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ DeleteSel( m_aDelPam );
+ m_aDelPam.DeleteMark();
+ }
+ nPos = TextFrameIndex(GetTrailingBlanks(m_pCurTextFrame->GetText()));
+ if (bEnd && TextFrameIndex(m_pCurTextFrame->GetText().getLength()) != nPos)
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ DeleteSel( m_aDelPam );
+ m_aDelPam.DeleteMark();
+ }
+}
+
+namespace sw {
+
+bool GetRanges(std::vector<std::shared_ptr<SwUnoCursor>> & rRanges,
+ SwDoc & rDoc, SwPaM const& rDelPam)
+{
+ bool isNoRedline(true);
+ SwRedlineTable::size_type tmp;
+ IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
+ if (!(rIDRA.GetRedlineFlags() & RedlineFlags::ShowDelete))
+ {
+ return isNoRedline;
+ }
+ rIDRA.GetRedline(*rDelPam.Start(), &tmp);
+ SwPosition const* pCurrent(rDelPam.Start());
+ for ( ; tmp < rIDRA.GetRedlineTable().size(); ++tmp)
+ {
+ SwRangeRedline const*const pRedline(rIDRA.GetRedlineTable()[tmp]);
+ if (*rDelPam.End() <= *pRedline->Start())
+ {
+ break;
+ }
+ if (*pRedline->End() <= *rDelPam.Start())
+ {
+ continue;
+ }
+ if (pRedline->GetType() == RedlineType::Delete)
+ {
+ assert(*pRedline->Start() != *pRedline->End());
+ isNoRedline = false;
+ if (*pCurrent < *pRedline->Start())
+ {
+ rRanges.push_back(rDoc.CreateUnoCursor(*pCurrent));
+ rRanges.back()->SetMark();
+ *rRanges.back()->GetPoint() = *pRedline->Start();
+ }
+ pCurrent = pRedline->End();
+ }
+ }
+ if (!isNoRedline && *pCurrent < *rDelPam.End())
+ {
+ rRanges.push_back(rDoc.CreateUnoCursor(*pCurrent));
+ rRanges.back()->SetMark();
+ *rRanges.back()->GetPoint() = *rDelPam.End();
+ }
+ return isNoRedline;
+}
+
+} // namespace sw
+
+void SwAutoFormat::DeleteSel(SwPaM & rDelPam)
+{
+ std::vector<std::shared_ptr<SwUnoCursor>> ranges; // need correcting cursor
+ if (GetRanges(ranges, *m_pDoc, rDelPam))
+ {
+ DeleteSelImpl(rDelPam, rDelPam);
+ }
+ else
+ {
+ for (auto const& pCursor : ranges)
+ {
+ DeleteSelImpl(*pCursor, rDelPam);
+ }
+ }
+}
+
+void SwAutoFormat::DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect)
+{
+ if (m_aFlags.bWithRedlining || &rDelPam != &rPamToCorrect)
+ {
+ // Add to Shell-Cursor-Ring so that DelPam will be moved as well!
+ SwPaM* pShCursor = m_pEditShell->GetCursor_();
+ SwPaM aTmp( *m_pCurTextNd, 0, pShCursor );
+
+ SwPaM* pPrev = rPamToCorrect.GetPrev();
+ rPamToCorrect.GetRingContainer().merge( pShCursor->GetRingContainer() );
+
+ m_pEditShell->DeleteSel(rDelPam, true);
+
+ // and remove Pam again:
+ SwPaM* p;
+ SwPaM* pNext = &rPamToCorrect;
+ do {
+ p = pNext;
+ pNext = p->GetNext();
+ p->MoveTo( &rPamToCorrect );
+ } while( p != pPrev );
+
+ m_aNdIdx = aTmp.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame(*m_pCurTextNd); // keep it up to date
+ }
+ else
+ m_pEditShell->DeleteSel(rDelPam, true);
+}
+
+bool SwAutoFormat::DeleteJoinCurNextPara(SwTextFrame const*const pNextFrame,
+ bool const bIgnoreLeadingBlanks)
+{
+ // delete blanks at the end of the current and at the beginning of the next one
+ m_aDelPam.DeleteMark();
+ TextFrameIndex nTrailingPos(GetTrailingBlanks(m_pCurTextFrame->GetText()));
+
+ SwTextFrame const*const pEndFrame(pNextFrame ? pNextFrame : m_pCurTextFrame);
+ TextFrameIndex nLeadingPos(0);
+ if (pNextFrame)
+ {
+ nLeadingPos = TextFrameIndex(
+ bIgnoreLeadingBlanks ? 0 : GetLeadingBlanks(pNextFrame->GetText()));
+ }
+ else
+ {
+ nLeadingPos = TextFrameIndex(m_pCurTextFrame->GetText().getLength());
+ }
+
+ // Is there a Blank at the beginning or end?
+ // Do not delete it, it will be inserted again.
+ bool bHasBlnks = HasSelBlanks(m_pCurTextFrame, nTrailingPos, pEndFrame, nLeadingPos);
+
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nTrailingPos);
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = pEndFrame->MapViewToModelPos(nLeadingPos);
+
+ if( *m_aDelPam.GetPoint() != *m_aDelPam.GetMark() )
+ DeleteSel( m_aDelPam );
+ m_aDelPam.DeleteMark();
+ // note: keep m_aDelPam point at insert pos. for clients
+
+ return !bHasBlnks;
+}
+
+void SwAutoFormat::DelEmptyLine( bool bTstNextPara )
+{
+ SetRedlineText( STR_AUTOFMTREDL_DEL_EMPTY_PARA );
+ // delete blanks in empty paragraph
+ m_aDelPam.DeleteMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(0));
+ m_aDelPam.SetMark();
+
+ m_aDelPam.GetMark()->nNode = m_pCurTextFrame->GetTextNodeFirst()->GetIndex() - 1;
+ SwTextNode* pTNd = m_aDelPam.GetNode( false ).GetTextNode();
+ if( pTNd )
+ // first use the previous text node
+ m_aDelPam.GetMark()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+ else if( bTstNextPara )
+ {
+ // then try the next (at the beginning of a Doc, table cells, frames, ...)
+ m_aDelPam.GetMark()->nNode = (m_pCurTextFrame->GetMergedPara()
+ ? m_pCurTextFrame->GetMergedPara()->pLastNode
+ : m_pCurTextNd
+ )->GetIndex() + 1;
+ pTNd = m_aDelPam.GetNode( false ).GetTextNode();
+ if( pTNd )
+ {
+ m_aDelPam.GetMark()->nContent.Assign( pTNd, 0 );
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
+ }
+ }
+ if( pTNd )
+ { // join with previous or next paragraph
+ DeleteSel(m_aDelPam);
+ }
+ assert(m_aDelPam.GetNode().IsTextNode());
+ assert(!m_aDelPam.HasMark());
+ m_aDelPam.SetMark(); // mark remains at join position
+ m_pCurTextFrame = GetFrame(*m_aDelPam.GetNode().GetTextNode());
+ // replace until the end of the merged paragraph
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
+ if (*m_aDelPam.GetPoint() != *m_aDelPam.GetMark())
+ { // tdf#137245 replace (not delete) to preserve any flys
+ m_pDoc->getIDocumentContentOperations().ReplaceRange(m_aDelPam, "", false);
+ }
+
+ m_aDelPam.DeleteMark();
+ ClearRedlineText();
+ // note: this likely has deleted m_pCurTextFrame - update it...
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = m_pCurTextNd ? GetFrame( *m_pCurTextNd ) : nullptr;
+}
+
+void SwAutoFormat::DelMoreLinesBlanks( bool bWithLineBreaks )
+{
+ if( !(m_aFlags.bAFormatByInput
+ ? m_aFlags.bAFormatByInpDelSpacesBetweenLines
+ : m_aFlags.bAFormatDelSpacesBetweenLines) )
+ return;
+
+ // delete all blanks on the left and right of the indentation
+ m_aDelPam.DeleteMark();
+
+ SwTextFrameInfo aFInfo( m_pCurTextFrame );
+ std::vector<std::pair<TextFrameIndex, TextFrameIndex>> spaces;
+ aFInfo.GetSpaces(spaces, !m_aFlags.bAFormatByInput || bWithLineBreaks);
+
+ // tdf#123285 iterate backwards - delete invalidates following indexes
+ for (auto iter = spaces.rbegin(); iter != spaces.rend(); ++iter)
+ {
+ auto & rSpaceRange(*iter);
+ assert(rSpaceRange.first != rSpaceRange.second);
+ bool const bHasBlanks = HasSelBlanks(
+ m_pCurTextFrame, rSpaceRange.first,
+ m_pCurTextFrame, rSpaceRange.second);
+ if (rSpaceRange.first != rSpaceRange.second)
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(rSpaceRange.first);
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(rSpaceRange.second);
+ DeleteSel(m_aDelPam);
+ if (!bHasBlanks)
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString(m_aDelPam, OUString(' '));
+ }
+ m_aDelPam.DeleteMark();
+ }
+ }
+}
+
+void SwAutoFormat::JoinPrevPara()
+{
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeFirst();
+ m_aDelPam.GetPoint()->nContent.Assign(m_pCurTextFrame->GetTextNodeFirst(), 0);
+ m_aDelPam.SetMark();
+
+ --m_aDelPam.GetPoint()->nNode;
+ SwTextNode* pTNd = m_aDelPam.GetNode().GetTextNode();
+ if( pTNd )
+ {
+ // use the previous text node first
+ m_aDelPam.GetPoint()->nContent.Assign(pTNd, pTNd->GetText().getLength());
+ DeleteSel( m_aDelPam );
+ }
+ m_aDelPam.DeleteMark();
+}
+
+void SwAutoFormat::BuildIndent()
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_INDENT );
+
+ // read all succeeding paragraphs that belong to this indentation
+ bool bBreak = true;
+ if( m_bMoreLines )
+ DelMoreLinesBlanks( true );
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+ SetColl( RES_POOLCOLL_TEXT_IDENT );
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ if (pNextFrame && !m_bEnd)
+ {
+ do {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ pNextFrame = GetNextNode();
+ }
+ while (CanJoin(pNextFrame)
+ && !CalcLevel(*pNextFrame));
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildTextIndent()
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_TEXT_INDENT);
+ // read all succeeding paragraphs that belong to this indentation
+ bool bBreak = true;
+ if( m_bMoreLines )
+ DelMoreLinesBlanks( true );
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+
+ if( m_aFlags.bAFormatByInput )
+ {
+ const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAutoFormatLvl(
+ static_cast<sal_uInt8>(CalcLevel(*m_pCurTextFrame)));
+ }
+
+ SetColl( RES_POOLCOLL_TEXT_MOVE );
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame) &&
+ CalcLevel(*pNextFrame))
+ {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ pNextFrame = GetNextNode();
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildText()
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_TEXT );
+ // read all succeeding paragraphs that belong to this text without indentation
+ bool bBreak = true;
+ if( m_bMoreLines )
+ DelMoreLinesBlanks();
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+ SetColl( RES_POOLCOLL_TEXT, true );
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame) &&
+ !CalcLevel(*pNextFrame))
+ {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ const SwTextFrame *const pCurrNode = pNextFrame;
+ pNextFrame = GetNextNode();
+ if (!pNextFrame || pCurrNode == pNextFrame)
+ break;
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel )
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_NUMBER_BULLET );
+
+ bool bBreak = true;
+
+ // first, determine current indentation and frame width
+ SwTwips nFrameWidth = m_pCurTextFrame->getFramePrintArea().Width();
+ SwTwips nLeftTextPos;
+ {
+ TextFrameIndex nPos(0);
+ while (nPos < TextFrameIndex(m_pCurTextFrame->GetText().getLength())
+ && IsSpace(m_pCurTextFrame->GetText()[sal_Int32(nPos)]))
+ {
+ ++nPos;
+ }
+
+ SwTextFrameInfo aInfo( m_pCurTextFrame );
+ nLeftTextPos = aInfo.GetCharPos(nPos);
+ nLeftTextPos -= m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace().GetLeft();
+ }
+
+ if( m_bMoreLines )
+ DelMoreLinesBlanks();
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame)
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+ bool bRTL = m_pEditShell->IsInRightToLeftText();
+ DeleteLeadingTrailingBlanks();
+
+ bool bChgBullet = false, bChgEnum = false;
+ TextFrameIndex nAutoCorrPos(0);
+
+ // if numbering is set, get the current one
+ SwNumRule aRule( m_pDoc->GetUniqueNumRuleName(),
+ // #i89178#
+ numfunc::GetDefaultPositionAndSpaceMode() );
+
+ const SwNumRule* pCur = nullptr;
+ if (m_aFlags.bSetNumRule)
+ {
+ pCur = m_pCurTextFrame->GetTextNodeForParaProps()->GetNumRule();
+ if (pCur)
+ {
+ aRule = *pCur;
+ }
+ }
+
+ // replace bullet character with defined one
+ const OUString& rStr = m_pCurTextFrame->GetText();
+ TextFrameIndex nTextStt(0);
+ const sal_Unicode* pFndBulletChr = nullptr;
+ if (m_aFlags.bChgEnumNum && 2 < rStr.getLength())
+ pFndBulletChr = StrChr(pBulletChar, rStr[sal_Int32(nTextStt)]);
+ if (nullptr != pFndBulletChr && IsSpace(rStr[sal_Int32(nTextStt) + 1]))
+ {
+ if( m_aFlags.bAFormatByInput )
+ {
+ if( m_aFlags.bSetNumRule )
+ {
+ SwCharFormat* pCFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_BULLET_LEVEL );
+ bChgBullet = true;
+ // Was the format already somewhere adjusted?
+ if( !aRule.GetNumFormat( nLvl ) )
+ {
+ int nBulletPos = pFndBulletChr - pBulletChar;
+ sal_UCS4 cBullChar;
+ const vcl::Font* pBullFnt( nullptr );
+ if( nBulletPos < cnPosEnDash )
+ {
+ cBullChar = m_aFlags.cBullet;
+ pBullFnt = &m_aFlags.aBulletFont;
+ }
+ else
+ {
+ cBullChar = nBulletPos < cnPosEmDash
+ ? cStarSymbolEnDash
+ : cStarSymbolEmDash;
+ // #i63395#
+ // Only apply user defined default bullet font
+ if ( numfunc::IsDefBulletFontUserDefined() )
+ {
+ pBullFnt = &numfunc::GetDefBulletFont();
+ }
+ }
+
+ sal_Int32 nAbsPos = lBulletIndent;
+ SwTwips nSpaceSteps = nLvl
+ ? nLeftTextPos / nLvl
+ : lBulletIndent;
+ for( sal_uInt8 n = 0; n < MAXLEVEL; ++n, nAbsPos = nAbsPos + nSpaceSteps )
+ {
+ SwNumFormat aFormat( aRule.Get( n ) );
+ aFormat.SetBulletFont( pBullFnt );
+ aFormat.SetBulletChar( cBullChar );
+ aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
+ // #i93908# clear suffix for bullet lists
+ aFormat.SetPrefix(OUString());
+ aFormat.SetSuffix(OUString());
+ aFormat.SetFirstLineOffset( lBulletFirstLineOffset );
+ aFormat.SetAbsLSpace( nAbsPos );
+ if( !aFormat.GetCharFormat() )
+ aFormat.SetCharFormat( pCFormat );
+ if( bRTL )
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+
+ aRule.Set( n, aFormat );
+
+ if( n == nLvl &&
+ nFrameWidth < ( nSpaceSteps * MAXLEVEL ) )
+ nSpaceSteps = ( nFrameWidth - nLeftTextPos ) /
+ ( MAXLEVEL - nLvl );
+ }
+ }
+ }
+ }
+ else
+ {
+ bChgBullet = true;
+ SetColl( o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_BULLET_LEVEL1 + ( std::min( nLvl, cnNumBullColls ) * 4 )) );
+ }
+ }
+ else
+ {
+ // Then it is a numbering
+
+ //JP 21.11.97: The NumLevel is either the DigitLevel or, if the latter is not existent or 0,
+ // it is determined by the indentation level.
+
+ OUString aPostfix, aPrefix, aNumTypes;
+ nDigitLevel = GetDigitLevel(*m_pCurTextFrame, nTextStt,
+ &aPrefix, &aPostfix, &aNumTypes);
+ if (USHRT_MAX != nDigitLevel)
+ {
+ bChgEnum = true;
+
+ // Level 0 and Indentation, determine level by left indentation and default NumIndent
+ if( !nDigitLevel && nLeftTextPos )
+ nLvl = std::min( sal_uInt16( nLeftTextPos / lNumberIndent ),
+ sal_uInt16( MAXLEVEL - 1 ) );
+ else
+ nLvl = nDigitLevel;
+ }
+
+ if( bChgEnum && m_aFlags.bSetNumRule )
+ {
+ if( !pCur ) // adjust NumRule if it is new
+ {
+ SwCharFormat* pCFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
+ RES_POOLCHR_NUM_LEVEL );
+
+ sal_Int32 nPrefixIdx{ 0 };
+ if( !nDigitLevel )
+ {
+ SwNumFormat aFormat( aRule.Get( nLvl ) );
+ aFormat.SetPrefix( aPrefix.getToken( 0, u'\x0001', nPrefixIdx ));
+ aFormat.SetStart( o3tl::narrowing<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aPrefix, 0, u'\x0001', nPrefixIdx ))));
+ aFormat.SetSuffix( aPostfix.getToken( 0, u'\x0001' ));
+ aFormat.SetIncludeUpperLevels( 0 );
+
+ if( !aFormat.GetCharFormat() )
+ aFormat.SetCharFormat( pCFormat );
+
+ if( !aNumTypes.isEmpty() )
+ aFormat.SetNumberingType(static_cast<SvxNumType>(aNumTypes[ 0 ] - '0'));
+
+ if( bRTL )
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+ aRule.Set( nLvl, aFormat );
+ }
+ else
+ {
+ auto const nSpaceSteps = nLvl ? nLeftTextPos / nLvl : 0;
+ sal_uInt16 n;
+ sal_Int32 nPostfixIdx{ 0 };
+ for( n = 0; n <= nLvl; ++n )
+ {
+ SwNumFormat aFormat( aRule.Get( n ) );
+
+ if( !n )
+ aFormat.SetPrefix( aPrefix.getToken( 0, u'\x0001', nPrefixIdx )); // token 0, read only on first loop
+ aFormat.SetStart( o3tl::narrowing<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aPrefix, 0, u'\x0001', nPrefixIdx )) ));
+ aFormat.SetSuffix( aPostfix.getToken( 0, u'\x0001', nPostfixIdx ));
+ aFormat.SetIncludeUpperLevels( MAXLEVEL );
+ if( n < aNumTypes.getLength() )
+ aFormat.SetNumberingType(static_cast<SvxNumType>(aNumTypes[ n ] - '0'));
+
+ aFormat.SetAbsLSpace( nSpaceSteps * n
+ + lNumberIndent );
+
+ if( !aFormat.GetCharFormat() )
+ aFormat.SetCharFormat( pCFormat );
+ if( bRTL )
+ aFormat.SetNumAdjust( SvxAdjust::Right );
+
+ aRule.Set( n, aFormat );
+ }
+
+ // Does it fit completely into the frame?
+ bool bDefStep = nFrameWidth < (nSpaceSteps * MAXLEVEL);
+ for( ; n < MAXLEVEL; ++n )
+ {
+ SwNumFormat aFormat( aRule.Get( n ) );
+ aFormat.SetIncludeUpperLevels( MAXLEVEL );
+ if( bDefStep )
+ aFormat.SetAbsLSpace( nLeftTextPos +
+ SwNumRule::GetNumIndent(static_cast<sal_uInt8>(n-nLvl)));
+ else
+ aFormat.SetAbsLSpace( nSpaceSteps * n
+ + lNumberIndent );
+ aRule.Set( n, aFormat );
+ }
+ }
+ }
+ }
+ else if( !m_aFlags.bAFormatByInput )
+ SetColl( o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_NUM_LEVEL1 + ( std::min( nLvl, cnNumBullColls ) * 4 ) ));
+ else
+ bChgEnum = false;
+ }
+
+ if ( bChgEnum || bChgBullet )
+ {
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeForParaProps();
+
+ if( m_aFlags.bSetNumRule )
+ {
+ if( m_aFlags.bAFormatByInput )
+ {
+ m_aDelPam.SetMark();
+ SwTextFrame const*const pNextFrame = GetNextNode(false);
+ assert(pNextFrame);
+ m_aDelPam.GetMark()->nNode = *pNextFrame->GetTextNodeForParaProps();
+ m_aDelPam.GetNode(false).GetTextNode()->SetAttrListLevel( nLvl );
+ }
+
+ const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAttrListLevel(nLvl);
+
+ // start new list
+ m_pDoc->SetNumRule(m_aDelPam, aRule, true, m_pEditShell->GetLayout());
+ m_aDelPam.DeleteMark();
+
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ }
+ else
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
+ bChgEnum ? nTextStt : TextFrameIndex(0));
+ }
+ m_aDelPam.SetMark();
+
+ if ( bChgBullet )
+ nTextStt += TextFrameIndex(2);
+
+ while (nTextStt < TextFrameIndex(rStr.getLength()) && IsSpace(rStr[sal_Int32(nTextStt)]))
+ nTextStt++;
+
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nTextStt);
+ DeleteSel( m_aDelPam );
+
+ if( !m_aFlags.bSetNumRule )
+ {
+ OUString sChgStr('\t');
+ if( bChgBullet )
+ sChgStr = OUString(&m_aFlags.cBullet, 1) + sChgStr;
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, sChgStr );
+
+ SfxItemSet aSet( m_pDoc->GetAttrPool(), aTextNodeSetRange );
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+ assert(&m_aDelPam.GetPoint()->nNode.GetNode() == m_pCurTextFrame->GetTextNodeForParaProps());
+ if( bChgBullet )
+ {
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(1));
+ SetAllScriptItem( aSet,
+ SvxFontItem( m_aFlags.aBulletFont.GetFamilyType(),
+ m_aFlags.aBulletFont.GetFamilyName(),
+ m_aFlags.aBulletFont.GetStyleName(),
+ m_aFlags.aBulletFont.GetPitch(),
+ m_aFlags.aBulletFont.GetCharSet(),
+ RES_CHRATR_FONT ) );
+ m_pDoc->SetFormatItemByAutoFormat( m_aDelPam, aSet );
+ m_aDelPam.DeleteMark();
+ nAutoCorrPos = TextFrameIndex(2);
+ aSet.ClearItem();
+ }
+ SvxTabStopItem aTStops( RES_PARATR_TABSTOP );
+ aTStops.Insert( SvxTabStop( 0 ) );
+ aSet.Put( aTStops );
+ assert(&m_aDelPam.GetPoint()->nNode.GetNode() == m_pCurTextFrame->GetTextNodeForParaProps());
+ m_pDoc->SetFormatItemByAutoFormat( m_aDelPam, aSet );
+ }
+ }
+
+ if( bBreak )
+ {
+ AutoCorrect( nAutoCorrPos ); /* Offset due to Bullet + Tab */
+ return;
+ }
+
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame)
+ && nLvl == CalcLevel(*pNextFrame))
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ const SwTextFrame *const pCurrNode = pNextFrame;
+ pNextFrame = GetNextNode();
+ if (!pNextFrame || pCurrNode == pNextFrame)
+ break;
+ }
+ DeleteLeadingTrailingBlanks( false );
+ AutoCorrect( nAutoCorrPos );
+}
+
+void SwAutoFormat::BuildNegIndent( SwTwips nSpaces )
+{
+ SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_NEG_INDENT );
+ // Test of contraposition (n words, divided by spaces/tabs, with same indentation in 2nd line)
+
+ // read all succeeding paragraphs that belong to this enumeration
+ bool bBreak = true;
+ TextFrameIndex nSpacePos(0);
+ const sal_Int32 nTextPos = GetBigIndent( nSpacePos );
+ if( m_bMoreLines )
+ DelMoreLinesBlanks( true );
+ else
+ bBreak = !IsFastFullLine(*m_pCurTextFrame)
+ || (!nTextPos && IsBlanksInString(*m_pCurTextFrame))
+ || IsSentenceAtEnd(*m_pCurTextFrame);
+
+ SetColl( o3tl::narrowing<sal_uInt16>( nTextPos
+ ? RES_POOLCOLL_CONFRONTATION
+ : RES_POOLCOLL_TEXT_NEGIDENT ) );
+
+ if( nTextPos )
+ {
+ const OUString& rStr = m_pCurTextFrame->GetText();
+ bool bInsTab = true;
+
+ if ('\t' == rStr[sal_Int32(nSpacePos) + 1]) // leave tab alone
+ {
+ --nSpacePos;
+ bInsTab = false;
+ }
+
+ TextFrameIndex nSpaceStt = nSpacePos;
+ while (nSpaceStt && IsSpace(rStr[sal_Int32(--nSpaceStt)]))
+ ;
+ ++nSpaceStt;
+
+ if (bInsTab && '\t' == rStr[sal_Int32(nSpaceStt)]) // leave tab alone
+ {
+ ++nSpaceStt;
+ bInsTab = false;
+ }
+
+ m_aDelPam.DeleteMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nSpacePos);
+
+ // delete old Spaces, etc.
+ if( nSpaceStt < nSpacePos )
+ {
+ m_aDelPam.SetMark();
+ *m_aDelPam.GetMark() = m_pCurTextFrame->MapViewToModelPos(nSpaceStt);
+ DeleteSel( m_aDelPam );
+ if( bInsTab )
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString('\t') );
+ }
+ }
+ }
+
+ if( !bBreak )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
+ SwTextFrameInfo aFInfo( m_pCurTextFrame );
+ const SwTextFrame * pNextFrame = GetNextNode();
+ while (CanJoin(pNextFrame) &&
+ 20 < std::abs( static_cast<tools::Long>(nSpaces - aFInfo.SetFrame(
+ EnsureFormatted(*pNextFrame)).GetLineStart()) )
+ )
+ {
+ bBreak = !IsFastFullLine(*pNextFrame)
+ || IsBlanksInString(*pNextFrame)
+ || IsSentenceAtEnd(*pNextFrame);
+ if (DeleteJoinCurNextPara(pNextFrame))
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
+ }
+ if( bBreak )
+ break;
+ pNextFrame = GetNextNode();
+ }
+ }
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+}
+
+void SwAutoFormat::BuildHeadLine( sal_uInt16 nLvl )
+{
+ if( m_aFlags.bWithRedlining )
+ {
+ OUString sText(SwViewShell::GetShellRes()->GetAutoFormatNameLst()[
+ STR_AUTOFMTREDL_SET_TMPL_HEADLINE ] );
+ sText = sText.replaceAll( "$(ARG1)", OUString::number( nLvl + 1 ) );
+ m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment( &sText );
+ }
+
+ SetColl( o3tl::narrowing<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + nLvl ), true );
+ if( m_aFlags.bAFormatByInput )
+ {
+ SwTextFormatColl& rNxtColl = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetNextTextFormatColl();
+
+ JoinPrevPara();
+
+ DeleteLeadingTrailingBlanks( true, false );
+ const SwTextFrame* pNextFrame = GetNextNode(false);
+ if (pNextFrame->GetNext())
+ {
+ (void)DeleteJoinCurNextPara(pNextFrame, true);
+ pNextFrame = GetNextNode(false);
+ }
+ m_aDelPam.DeleteMark();
+ m_aDelPam.GetPoint()->nNode = *pNextFrame->GetTextNodeForParaProps();
+ m_aDelPam.GetPoint()->nContent.Assign( m_aDelPam.GetContentNode(), 0 );
+ m_pDoc->SetTextFormatColl( m_aDelPam, &rNxtColl );
+ }
+ else
+ {
+ DeleteLeadingTrailingBlanks();
+ AutoCorrect();
+ }
+}
+
+/// Start autocorrection for the current TextNode
+void SwAutoFormat::AutoCorrect(TextFrameIndex nPos)
+{
+ SvxAutoCorrect* pATst = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ ACFlags aSvxFlags = pATst->GetFlags( );
+ bool bReplaceQuote( aSvxFlags & ACFlags::ChgQuotes );
+ bool bReplaceSglQuote( aSvxFlags & ACFlags::ChgSglQuotes );
+
+ if( m_aFlags.bAFormatByInput ||
+ (!m_aFlags.bAutoCorrect && !bReplaceQuote && !bReplaceSglQuote &&
+ !m_aFlags.bCapitalStartSentence && !m_aFlags.bCapitalStartWord &&
+ !m_aFlags.bChgOrdinalNumber && !m_aFlags.bTransliterateRTL &&
+ !m_aFlags.bChgToEnEmDash && !m_aFlags.bSetINetAttr &&
+ !m_aFlags.bChgWeightUnderl && !m_aFlags.bAddNonBrkSpace) )
+ return;
+
+ const OUString* pText = &m_pCurTextFrame->GetText();
+ if (TextFrameIndex(pText->getLength()) <= nPos)
+ return;
+
+ bool bGetLanguage = m_aFlags.bChgOrdinalNumber || m_aFlags.bTransliterateRTL ||
+ m_aFlags.bChgToEnEmDash || m_aFlags.bSetINetAttr ||
+ m_aFlags.bCapitalStartWord || m_aFlags.bCapitalStartSentence ||
+ m_aFlags.bAddNonBrkSpace;
+
+ m_aDelPam.DeleteMark();
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
+
+ SwAutoCorrDoc aACorrDoc( *m_pEditShell, m_aDelPam );
+
+ SwTextFrameInfo aFInfo( nullptr );
+
+ TextFrameIndex nSttPos, nLastBlank = nPos;
+ bool bFirst = m_aFlags.bCapitalStartSentence, bFirstSent = bFirst;
+ sal_Unicode cChar = 0;
+ bool bNbspRunNext = false;
+
+ CharClass& rAppCC = GetAppCharClass();
+
+ do {
+ while (nPos < TextFrameIndex(pText->getLength())
+ && IsSpace(cChar = (*pText)[sal_Int32(nPos)]))
+ ++nPos;
+ if (nPos == TextFrameIndex(pText->getLength()))
+ break; // that's it
+
+ if( ( ( bReplaceQuote && '\"' == cChar ) ||
+ ( bReplaceSglQuote && '\'' == cChar ) ) &&
+ (!nPos || ' ' == (*pText)[sal_Int32(nPos)-1]))
+ {
+
+ // note: special case symbol fonts !!!
+ if( !aFInfo.GetFrame() )
+ aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
+ if( !aFInfo.IsBullet( nPos ))
+ {
+ SetRedlineText( STR_AUTOFMTREDL_TYPO );
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ bool bSetHardBlank = false;
+
+ OUString sReplace( pATst->GetQuote( aACorrDoc,
+ sal_Int32(nPos), cChar, true ));
+
+ m_aDelPam.SetMark();
+ m_aDelPam.GetPoint()->nContent = m_aDelPam.GetMark()->nContent.GetIndex() + 1;
+ if( 2 == sReplace.getLength() && ' ' == sReplace[ 1 ])
+ {
+ sReplace = sReplace.copy( 0, 1 );
+ bSetHardBlank = true;
+ }
+ m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ aFInfo.SetFrame( nullptr );
+ }
+
+ nPos += TextFrameIndex(sReplace.getLength() - 1);
+ m_aDelPam.DeleteMark();
+ if( bSetHardBlank )
+ {
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(CHAR_HARDBLANK) );
+ ++nPos;
+ }
+ }
+ }
+
+ bool bCallACorr = false;
+ int bBreak = 0;
+ if (nPos && IsSpace((*pText)[sal_Int32(nPos) - 1]))
+ nLastBlank = nPos;
+ for (nSttPos = nPos; !bBreak && nPos < TextFrameIndex(pText->getLength()); ++nPos)
+ {
+ cChar = (*pText)[sal_Int32(nPos)];
+ switch (cChar)
+ {
+ case '\"':
+ case '\'':
+ if( ( cChar == '\"' && bReplaceQuote ) || ( cChar == '\'' && bReplaceSglQuote ) )
+ {
+ // consider Symbolfonts!
+ if( !aFInfo.GetFrame() )
+ aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
+ if( !aFInfo.IsBullet( nPos ))
+ {
+ SetRedlineText( STR_AUTOFMTREDL_TYPO );
+ bool bSetHardBlank = false;
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ OUString sReplace( pATst->GetQuote( aACorrDoc,
+ sal_Int32(nPos), cChar, false) );
+
+ if( 2 == sReplace.getLength() && ' ' == sReplace[ 0 ])
+ {
+ sReplace = sReplace.copy( 1 );
+ bSetHardBlank = true;
+ }
+
+ m_aDelPam.SetMark();
+ m_aDelPam.GetPoint()->nContent = m_aDelPam.GetMark()->nContent.GetIndex() + 1;
+ m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ aFInfo.SetFrame( nullptr );
+ }
+
+ nPos += TextFrameIndex(sReplace.getLength() - 1);
+ m_aDelPam.DeleteMark();
+
+ if( bSetHardBlank )
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(CHAR_HARDBLANK) );
+ ++nPos;
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ }
+ }
+ }
+ break;
+ case '*':
+ case '_':
+ if( m_aFlags.bChgWeightUnderl )
+ {
+ // consider Symbolfonts!
+ if( !aFInfo.GetFrame() )
+ aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
+ if( !aFInfo.IsBullet( nPos ))
+ {
+ SetRedlineText( '*' == cChar
+ ? STR_AUTOFMTREDL_BOLD
+ : STR_AUTOFMTREDL_UNDER );
+
+ sal_Unicode cBlank = nSttPos ? (*pText)[sal_Int32(nSttPos) - 1] : 0;
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+
+ if (pATst->FnChgWeightUnderl(aACorrDoc, *pText, sal_Int32(nPos)))
+ {
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ aFInfo.SetFrame( nullptr );
+ }
+ //#125102# in case of the mode RedlineFlags::ShowDelete the ** are still contained in pText
+ if(!(m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() & RedlineFlags::ShowDelete))
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint()) - TextFrameIndex(1);
+ // Was a character deleted before starting?
+ if (cBlank && cBlank != (*pText)[sal_Int32(nSttPos) - 1])
+ --nSttPos;
+ }
+ }
+ }
+ break;
+ case '/':
+ if ( m_aFlags.bAddNonBrkSpace )
+ {
+ LanguageType eLang = bGetLanguage
+ ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true)
+ : LANGUAGE_SYSTEM;
+
+ SetRedlineText( STR_AUTOFMTREDL_NON_BREAK_SPACE );
+ if (pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext))
+ --nPos;
+ }
+ break;
+
+ case '.':
+ case '!':
+ case '?':
+ if( m_aFlags.bCapitalStartSentence )
+ bFirstSent = true;
+ [[fallthrough]];
+ default:
+ if (!(rAppCC.isLetterNumeric(*pText, sal_Int32(nPos))
+ || '/' == cChar )) // '/' should not be a word separator (e.g. '1/2' needs to be handled as one word for replacement)
+ {
+ --nPos; // revert ++nPos which was decremented in for loop
+ ++bBreak;
+ }
+ break;
+ }
+ }
+
+ if( nPos == nSttPos )
+ {
+ if (++nPos == TextFrameIndex(pText->getLength()))
+ bCallACorr = true;
+ }
+ else
+ bCallACorr = true;
+
+ if( bCallACorr )
+ {
+ *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
+ SetRedlineText( STR_AUTOFMTREDL_USE_REPLACE );
+
+ LanguageType eLang = bGetLanguage
+ ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true)
+ : LANGUAGE_SYSTEM;
+
+ if( m_bIsRightToLeft && m_aFlags.bTransliterateRTL && eLang == LANGUAGE_HUNGARIAN &&
+ SetRedlineText( STR_AUTOFMTREDL_TRANSLITERATE_RTL ) &&
+ aACorrDoc.TransliterateRTLWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos), /*bApply=*/true))
+ {
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ }
+
+ continue; // do not check further
+ }
+
+ if( m_aFlags.bAutoCorrect &&
+ aACorrDoc.ChgAutoCorrWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos), *pATst, nullptr) )
+ {
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ }
+
+ continue; // do not check further
+ }
+
+ if ( m_aFlags.bAddNonBrkSpace && nPos < TextFrameIndex(pText->getLength()) )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_NON_BREAK_SPACE );
+ pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext);
+ }
+
+ if( ( m_aFlags.bChgOrdinalNumber &&
+ SetRedlineText( STR_AUTOFMTREDL_ORDINAL ) &&
+ pATst->FnChgOrdinalNumber(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang)) ||
+ ( m_aFlags.bChgToEnEmDash &&
+ SetRedlineText( STR_AUTOFMTREDL_DASH ) &&
+ pATst->FnChgToEnEmDash(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang)) ||
+ ( m_aFlags.bSetINetAttr &&
+ (nPos == TextFrameIndex(pText->getLength()) || IsSpace((*pText)[sal_Int32(nPos)])) &&
+ SetRedlineText( STR_AUTOFMTREDL_DETECT_URL ) &&
+ pATst->FnSetINetAttr(aACorrDoc, *pText, sal_Int32(nLastBlank), sal_Int32(nPos), eLang)))
+ {
+ nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
+ }
+ else
+ {
+ // two capital letters at the beginning of a word?
+ if( m_aFlags.bCapitalStartWord )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_CPTL_STT_WORD );
+ pATst->FnCapitalStartWord(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang);
+ }
+ // capital letter at the beginning of a sentence?
+ if( m_aFlags.bCapitalStartSentence && bFirst )
+ {
+ SetRedlineText( STR_AUTOFMTREDL_CPTL_STT_SENT );
+ pATst->FnCapitalStartSentence(aACorrDoc, *pText, true, sal_Int32(nSttPos), sal_Int32(nPos), eLang);
+ }
+
+ bFirst = bFirstSent;
+ bFirstSent = false;
+
+ if( m_aFlags.bWithRedlining )
+ {
+ m_aNdIdx = m_aDelPam.GetPoint()->nNode;
+ m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
+ m_pCurTextFrame = GetFrame( *m_pCurTextNd );
+ pText = &m_pCurTextFrame->GetText();
+ m_aDelPam.SetMark();
+ m_aDelPam.DeleteMark();
+ }
+ }
+ }
+ }
+ while (nPos < TextFrameIndex(pText->getLength()));
+ ClearRedlineText();
+}
+
+SwAutoFormat::SwAutoFormat( SwEditShell* pEdShell, SvxSwAutoFormatFlags const & rFlags,
+ SwNodeIndex const * pSttNd, SwNodeIndex const * pEndNd )
+ : m_aFlags( rFlags ),
+ m_aDelPam( pEdShell->GetDoc()->GetNodes().GetEndOfExtras() ),
+ m_aNdIdx( pEdShell->GetDoc()->GetNodes().GetEndOfExtras(), SwNodeOffset(+1) ),
+ m_aEndNdIdx( pEdShell->GetDoc()->GetNodes().GetEndOfContent() ),
+ m_pEditShell( pEdShell ),
+ m_pDoc( pEdShell->GetDoc() ),
+ m_pCurTextNd( nullptr ), m_pCurTextFrame( nullptr ),
+ m_nRedlAutoFormatSeqId( 0 )
+{
+ OSL_ENSURE( (pSttNd && pEndNd) || (!pSttNd && !pEndNd),
+ "Got no area" );
+
+ if( m_aFlags.bSetNumRule && !m_aFlags.bAFormatByInput )
+ m_aFlags.bSetNumRule = false;
+
+ bool bReplaceStyles = !m_aFlags.bAFormatByInput || m_aFlags.bReplaceStyles;
+
+ const SwTextFrame * pNextFrame = nullptr;
+ bool bNxtEmpty = false;
+ bool bNxtAlpha = false;
+ sal_uInt16 nNxtLevel = 0;
+ bool bEmptyLine;
+
+ // set area for autoformatting
+ if( pSttNd )
+ {
+ m_aNdIdx = *pSttNd;
+ // for GoNextPara, one paragraph prior to that
+ sw::GotoPrevLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
+ m_aEndNdIdx = *pEndNd;
+ sw::GotoNextLayoutTextFrame(m_aEndNdIdx, m_pEditShell->GetLayout());
+
+ // check the previous TextNode
+ SwTextFrame const*const pPrevFrame = m_aNdIdx.GetNode().GetTextNode()
+ ? static_cast<SwTextFrame const*>(m_aNdIdx.GetNode().GetTextNode()->getLayoutFrame(m_pEditShell->GetLayout()))
+ : nullptr;
+ bEmptyLine = !pPrevFrame
+ || IsEmptyLine(*pPrevFrame)
+ || IsNoAlphaLine(*pPrevFrame);
+ }
+ else
+ bEmptyLine = true; // at document beginning
+
+ m_bEnd = false;
+
+ // set value for percentage display
+ m_nEndNdIdx = m_aEndNdIdx.GetIndex();
+
+ if( !m_aFlags.bAFormatByInput )
+ {
+ m_nEndNdIdx = m_aEndNdIdx.GetIndex();
+ ::StartProgress( STR_STATSTR_AUTOFORMAT, sal_Int32(m_aNdIdx.GetIndex()),
+ sal_Int32(m_nEndNdIdx),
+ m_pDoc->GetDocShell() );
+ }
+
+ RedlineFlags eRedlMode = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode;
+ if( m_aFlags.bWithRedlining )
+ {
+ m_pDoc->SetAutoFormatRedline( true );
+ eRedlMode = RedlineFlags::On | (eOldMode & RedlineFlags::ShowMask);
+ }
+ else
+ eRedlMode = RedlineFlags::Ignore | (eOldMode & RedlineFlags::ShowMask);
+ m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode );
+
+ // save undo state (might be turned off)
+ bool const bUndoState = m_pDoc->GetIDocumentUndoRedo().DoesUndo();
+
+ // If multiple lines, then do not merge with next paragraph
+ m_bMoreLines = false;
+
+ sal_uInt16 nLastCalcHeadLvl = 0;
+ sal_uInt16 nLastHeadLvl = USHRT_MAX;
+ sal_uInt16 nLevel = 0;
+ sal_uInt16 nDigitLvl = 0;
+
+ // set defaults
+ SwTextFrameInfo aFInfo( nullptr );
+
+ enum Format_Status
+ {
+ READ_NEXT_PARA, // -> ISEND, TST_EMPTY_LINE
+ TST_EMPTY_LINE, // -> READ_NEXT_PARA, TST_ALPHA_LINE
+ TST_ALPHA_LINE, // -> READ_NEXT_PARA, GET_ALL_INFO, IS_END
+ GET_ALL_INFO, // -> READ_NEXT_PARA, IS_ONE_LINE, TST_ENUMERIC, HAS_FMTCOLL
+ IS_ONE_LINE, // -> READ_NEXT_PARA, TST_ENUMERIC
+ TST_ENUMERIC, // -> READ_NEXT_PARA, TST_IDENT, TST_NEG_IDENT
+ TST_IDENT, // -> READ_NEXT_PARA, TST_TXT_BODY
+ TST_NEG_IDENT, // -> READ_NEXT_PARA, TST_TXT_BODY
+ TST_TXT_BODY, // -> READ_NEXT_PARA
+ HAS_FMTCOLL, // -> READ_NEXT_PARA
+ IS_END
+ } eStat;
+
+ // This is the automat for autoformatting
+ eStat = READ_NEXT_PARA;
+ while( !m_bEnd )
+ {
+ switch( eStat )
+ {
+ case READ_NEXT_PARA:
+ {
+ GoNextPara();
+ eStat = m_bEnd ? IS_END : TST_EMPTY_LINE;
+ }
+ break;
+
+ case TST_EMPTY_LINE:
+ if (IsEmptyLine(*m_pCurTextFrame))
+ {
+ if (m_aFlags.bDelEmptyNode && !HasObjects(*m_pCurTextFrame))
+ {
+ bEmptyLine = true;
+ SwNodeOffset nOldCnt = m_pDoc->GetNodes().Count();
+ DelEmptyLine();
+ // Was there really a deletion of a node?
+ if( nOldCnt != m_pDoc->GetNodes().Count() )
+ {
+ // do not skip the next paragraph
+ sw::GotoPrevLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
+ }
+ }
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = TST_ALPHA_LINE;
+ break;
+
+ case TST_ALPHA_LINE:
+ if (IsNoAlphaLine(*m_pCurTextFrame))
+ {
+ // recognize a table definition +---+---+
+ if( m_aFlags.bAFormatByInput && m_aFlags.bCreateTable && DoTable() )
+ {
+ //JP 30.09.96: DoTable() builds on PopCursor and MoveCursor after AutoFormat!
+ pEdShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
+ static_cast<SwPaM&>(*pEdShell->GetCursor()) = m_aDelPam;
+ pEdShell->Push();
+
+ eStat = IS_END;
+ break;
+ }
+
+ // Check for 3 "---" or "===". In this case, the previous paragraph should be
+ // underlined and the current be deleted!
+ if( !DoUnderline() && bReplaceStyles )
+ {
+ SetColl( RES_POOLCOLL_STANDARD, true );
+ bEmptyLine = true;
+ }
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = GET_ALL_INFO;
+ break;
+
+ case GET_ALL_INFO:
+ {
+ if (m_pCurTextFrame->GetTextNodeForParaProps()->GetNumRule())
+ {
+ // do nothing in numbering, go to next
+ bEmptyLine = false;
+ eStat = READ_NEXT_PARA;
+ // delete all blanks at beginning/end and in between
+ //JP 29.04.98: first only "all in between"
+ DelMoreLinesBlanks();
+ break;
+ }
+
+ aFInfo.SetFrame( m_pCurTextFrame );
+
+ // so far: if there were templates assigned, keep these and go to next node
+ sal_uInt16 nPoolId = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetPoolFormatId();
+ if( IsPoolUserFormat( nPoolId )
+ ? !m_aFlags.bChgUserColl
+ : ( RES_POOLCOLL_STANDARD != nPoolId &&
+ ( !m_aFlags.bAFormatByInput ||
+ (RES_POOLCOLL_TEXT_MOVE != nPoolId &&
+ RES_POOLCOLL_TEXT != nPoolId )) ))
+ {
+ eStat = HAS_FMTCOLL;
+ break;
+ }
+
+ // check for hard spaces or LRSpaces set by the template
+ if( IsPoolUserFormat( nPoolId ) ||
+ RES_POOLCOLL_STANDARD == nPoolId )
+ {
+ short nSz;
+ SvxLRSpaceItem const * pLRSpace = m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().
+ GetItemIfSet( RES_LR_SPACE );
+ if (pLRSpace &&
+ ( 0 != (nSz = pLRSpace->GetTextFirstLineOffset()) ||
+ 0 != pLRSpace->GetTextLeft() ) )
+ {
+ // exception: numbering/enumeration can have an indentation
+ if (IsEnumericChar(*m_pCurTextFrame))
+ {
+ nLevel = CalcLevel(*m_pCurTextFrame, &nDigitLvl);
+ if( nLevel >= MAXLEVEL )
+ nLevel = MAXLEVEL-1;
+ BuildEnum( nLevel, nDigitLvl );
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+
+ // never merge (maybe only indent as exception)
+ m_bMoreLines = true;
+
+ if( bReplaceStyles )
+ {
+ // then use one of our templates
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else if( pLRSpace->GetTextLeft() ) // is indentation
+ BuildTextIndent();
+ }
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+ }
+
+ nLevel = CalcLevel( *m_pCurTextFrame, &nDigitLvl );
+ m_bMoreLines = !IsOneLine(*m_pCurTextFrame);
+ // note: every use of pNextFrame in following states, until the
+ // next READ_NEXT_PARA, relies on this update
+ pNextFrame = GetNextNode();
+ if (pNextFrame)
+ {
+ bNxtEmpty = IsEmptyLine(*pNextFrame);
+ bNxtAlpha = IsNoAlphaLine(*pNextFrame);
+ nNxtLevel = CalcLevel(*pNextFrame);
+
+ if (!bEmptyLine && HasBreakAttr(*m_pCurTextFrame))
+ bEmptyLine = true;
+ if (!bNxtEmpty && HasBreakAttr(*pNextFrame))
+ bNxtEmpty = true;
+
+ }
+ else
+ {
+ bNxtEmpty = false;
+ bNxtAlpha = false;
+ nNxtLevel = 0;
+ }
+ eStat = !m_bMoreLines ? IS_ONE_LINE : TST_ENUMERIC;
+ }
+ break;
+
+ case IS_ONE_LINE:
+ {
+ eStat = TST_ENUMERIC;
+ if( !bReplaceStyles )
+ break;
+
+ const OUString sClrStr( DelLeadingBlanks(m_pCurTextFrame->GetText()) );
+
+ if( sClrStr.isEmpty() )
+ {
+ bEmptyLine = true;
+ eStat = READ_NEXT_PARA;
+ break; // read next paragraph
+ }
+
+ // check if headline
+ if (!bEmptyLine || !IsFirstCharCapital(*m_pCurTextFrame)
+ || IsBlanksInString(*m_pCurTextFrame))
+ break;
+
+ bEmptyLine = false;
+ const OUString sEndClrStr( DelTrailingBlanks(sClrStr) );
+ const sal_Unicode cLast = sEndClrStr[sEndClrStr.getLength() - 1];
+
+ // not, then check if headline
+ if( ':' == cLast )
+ {
+ BuildHeadLine( 2 );
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+ else if( 256 <= cLast || !strchr( ",.;", cLast ) )
+ {
+ if( bNxtEmpty || bNxtAlpha
+ || (pNextFrame && IsEnumericChar(*pNextFrame)))
+ {
+
+ // one level below?
+ if( nLevel >= MAXLEVEL )
+ nLevel = MAXLEVEL-1;
+
+ if( USHRT_MAX == nLastHeadLvl )
+ nLastHeadLvl = 0;
+ else if( nLastCalcHeadLvl < nLevel )
+ {
+ if( nLastHeadLvl+1 < MAXLEVEL )
+ ++nLastHeadLvl;
+ }
+ // one level above?
+ else if( nLastCalcHeadLvl > nLevel )
+ {
+ if( nLastHeadLvl )
+ --nLastHeadLvl;
+ }
+ nLastCalcHeadLvl = nLevel;
+
+ if( m_aFlags.bAFormatByInput )
+ BuildHeadLine( nLevel );
+ else
+ BuildHeadLine( nLastHeadLvl );
+ eStat = READ_NEXT_PARA;
+ break;
+ }
+ }
+ }
+ break;
+
+ case TST_ENUMERIC:
+ {
+ bEmptyLine = false;
+ if (IsEnumericChar(*m_pCurTextFrame))
+ {
+ if( nLevel >= MAXLEVEL )
+ nLevel = MAXLEVEL-1;
+ BuildEnum( nLevel, nDigitLvl );
+ eStat = READ_NEXT_PARA;
+ }
+ else if( bReplaceStyles )
+ eStat = nLevel ? TST_IDENT : TST_NEG_IDENT;
+ else
+ eStat = READ_NEXT_PARA;
+ }
+ break;
+
+ case TST_IDENT:
+ // Spaces at the beginning, check again for indentation
+ if( m_bMoreLines && nLevel )
+ {
+ SwTwips nSz = aFInfo.GetFirstIndent();
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else // is indentation
+ BuildTextIndent();
+ eStat = READ_NEXT_PARA;
+ }
+ else if (nLevel && pNextFrame &&
+ !bNxtEmpty && !bNxtAlpha && !nNxtLevel &&
+ !IsEnumericChar(*pNextFrame))
+ {
+ // is an indentation
+ BuildIndent();
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = TST_TXT_BODY;
+ break;
+
+ case TST_NEG_IDENT:
+ // no spaces at the beginning, check again for negative indentation
+ {
+ if( m_bMoreLines && !nLevel )
+ {
+ SwTwips nSz = aFInfo.GetFirstIndent();
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else // is _no_ indentation
+ BuildText();
+ eStat = READ_NEXT_PARA;
+ }
+ else if (!nLevel && pNextFrame &&
+ !bNxtEmpty && !bNxtAlpha && nNxtLevel &&
+ !IsEnumericChar(*pNextFrame))
+ {
+ // is a negative indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ eStat = READ_NEXT_PARA;
+ }
+ else
+ eStat = TST_TXT_BODY;
+ }
+ break;
+
+ case TST_TXT_BODY:
+ {
+ if( m_bMoreLines )
+ {
+ SwTwips nSz = aFInfo.GetFirstIndent();
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ BuildNegIndent( aFInfo.GetLineStart() );
+ else if( nLevel ) // is indentation
+ BuildTextIndent();
+ else
+ BuildText();
+ }
+ else if( nLevel )
+ BuildTextIndent();
+ else
+ BuildText();
+ eStat = READ_NEXT_PARA;
+ }
+ break;
+
+ case HAS_FMTCOLL:
+ {
+ // so far: if there were templates assigned, keep these and go to next node
+ bEmptyLine = false;
+ eStat = READ_NEXT_PARA;
+ // delete all blanks at beginning/end and in between
+ //JP 29.04.98: first only "all in between"
+ DelMoreLinesBlanks();
+
+ // handle hard attributes
+ if (m_pCurTextFrame->GetTextNodeForParaProps()->HasSwAttrSet())
+ {
+ short nSz;
+ SvxLRSpaceItem const * pLRSpace;
+ if( bReplaceStyles &&
+ (pLRSpace = m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().
+ GetItemIfSet( RES_LR_SPACE, false )) &&
+ ( 0 != (nSz = pLRSpace->GetTextFirstLineOffset()) ||
+ 0 != pLRSpace->GetTextLeft() ) )
+ {
+ // then use one of our templates
+ if( 0 < nSz ) // positive 1st line indentation
+ BuildIndent();
+ else if( 0 > nSz ) // negative 1st line indentation
+ {
+ BuildNegIndent( aFInfo.GetLineStart() );
+ }
+ else if( pLRSpace->GetTextLeft() ) // is indentation
+ BuildTextIndent();
+ else
+ BuildText();
+ }
+ }
+ }
+ break;
+
+ case IS_END:
+ m_bEnd = true;
+ break;
+ }
+ }
+
+ if( m_aFlags.bWithRedlining )
+ m_pDoc->SetAutoFormatRedline( false );
+ m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode );
+
+ // restore undo (in case it has been changed)
+ m_pDoc->GetIDocumentUndoRedo().DoUndo(bUndoState);
+
+ // disable display of percentage again
+ if( !m_aFlags.bAFormatByInput )
+ ::EndProgress( m_pDoc->GetDocShell() );
+}
+
+void SwEditShell::AutoFormat( const SvxSwAutoFormatFlags* pAFlags )
+{
+ std::unique_ptr<SwWait> pWait;
+
+ CurrShell aCurr( this );
+ StartAllAction();
+ StartUndo( SwUndoId::AUTOFORMAT );
+
+ SvxSwAutoFormatFlags aAFFlags; // use default values or add params?
+ if( pAFlags )
+ {
+ aAFFlags = *pAFlags;
+ if( !aAFFlags.bAFormatByInput )
+ pWait.reset(new SwWait( *GetDoc()->GetDocShell(), true ));
+ }
+
+ SwPaM* pCursor = GetCursor();
+ // There are more than one or a selection is open
+ if( pCursor->GetNext() != pCursor || pCursor->HasMark() )
+ {
+ for(SwPaM& rPaM : GetCursor()->GetRingContainer())
+ {
+ if( rPaM.HasMark() )
+ {
+ SwAutoFormat aFormat( this, aAFFlags, &(rPaM.Start()->nNode),
+ &(rPaM.End()->nNode) );
+ }
+ }
+ }
+ else
+ {
+ SwAutoFormat aFormat( this, aAFFlags );
+ }
+
+ EndUndo( SwUndoId::AUTOFORMAT );
+ EndAllAction();
+}
+
+void SwEditShell::AutoFormatBySplitNode()
+{
+ CurrShell aCurr( this );
+ SwPaM* pCursor = GetCursor();
+ if( pCursor->IsMultiSelection() || !pCursor->Move( fnMoveBackward, GoInNode ) )
+ return;
+
+ StartAllAction();
+ StartUndo( SwUndoId::AUTOFORMAT );
+
+ bool bRange = false;
+ pCursor->SetMark();
+ SwIndex* pContent = &pCursor->GetMark()->nContent;
+ if( pContent->GetIndex() )
+ {
+ *pContent = 0;
+ bRange = true;
+ }
+ else
+ {
+ // then go one node backwards
+ SwNodeIndex aNdIdx(pCursor->GetMark()->nNode);
+ sw::GotoPrevLayoutTextFrame(aNdIdx, GetLayout());
+ SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
+ if (pTextNd && !pTextNd->GetText().isEmpty())
+ {
+ pContent->Assign( pTextNd, 0 );
+ pCursor->GetMark()->nNode = aNdIdx;
+ bRange = true;
+ }
+ }
+
+ if( bRange )
+ {
+ Push(); // save cursor
+
+ SvxSwAutoFormatFlags aAFFlags = *GetAutoFormatFlags(); // use default values so far
+
+ SwAutoFormat aFormat( this, aAFFlags, &pCursor->GetMark()->nNode,
+ &pCursor->GetPoint()->nNode );
+ SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
+ if( pACorr && !pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord |
+ ACFlags::AddNonBrkSpace | ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL |
+ ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | ACFlags::Autocorrect ))
+ pACorr = nullptr;
+
+ if( pACorr )
+ AutoCorrect( *pACorr,false, u'\0' );
+
+ //JP 30.09.96: DoTable() builds on PopCursor and MoveCursor!
+ Pop(PopMode::DeleteCurrent);
+ pCursor = GetCursor();
+ }
+ pCursor->DeleteMark();
+ pCursor->Move( fnMoveForward, GoInNode );
+
+ EndUndo( SwUndoId::AUTOFORMAT );
+ EndAllAction();
+
+}
+
+SvxSwAutoFormatFlags* SwEditShell::GetAutoFormatFlags()
+{
+ if (!s_pAutoFormatFlags)
+ s_pAutoFormatFlags = new SvxSwAutoFormatFlags;
+
+ return s_pAutoFormatFlags;
+}
+
+void SwEditShell::SetAutoFormatFlags(SvxSwAutoFormatFlags const * pFlags)
+{
+ SvxSwAutoFormatFlags* pEditFlags = GetAutoFormatFlags();
+
+ pEditFlags->bSetNumRule = pFlags->bSetNumRule;
+ pEditFlags->bChgEnumNum = pFlags->bChgEnumNum;
+ pEditFlags->bSetBorder = pFlags->bSetBorder;
+ pEditFlags->bCreateTable = pFlags->bCreateTable;
+ pEditFlags->bReplaceStyles = pFlags->bReplaceStyles;
+ pEditFlags->bAFormatByInpDelSpacesAtSttEnd =
+ pFlags->bAFormatByInpDelSpacesAtSttEnd;
+ pEditFlags->bAFormatByInpDelSpacesBetweenLines =
+ pFlags->bAFormatByInpDelSpacesBetweenLines;
+
+ //JP 15.12.98: copy BulletChar and Font into "normal" ones
+ // because AutoFormat can only work with the latter!
+ pEditFlags->cBullet = pFlags->cByInputBullet;
+ pEditFlags->aBulletFont = pFlags->aByInputBulletFont;
+ pEditFlags->cByInputBullet = pFlags->cByInputBullet;
+ pEditFlags->aByInputBulletFont = pFlags->aByInputBulletFont;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */