2945 lines
108 KiB
C++
2945 lines
108 KiB
C++
/* -*- 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 <contentindex.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>
|
|
#include <utility>
|
|
|
|
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::optional<CharClass> m_oCharClass; // 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_oCharClass || eLang != m_eCharClassLang )
|
|
{
|
|
m_oCharClass.emplace( LanguageTag( eLang ) );
|
|
m_eCharClassLang = eLang;
|
|
}
|
|
return *m_oCharClass;
|
|
}
|
|
|
|
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( std::u16string_view aStr );
|
|
static sal_Int32 GetTrailingBlanks( std::u16string_view aStr );
|
|
|
|
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 aFlags,
|
|
SwNode const * pSttNd = nullptr, SwNode 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:
|
|
case STR_AUTOFMTREDL_ITALIC:
|
|
case STR_AUTOFMTREDL_STRIKETHROUGH:
|
|
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();
|
|
bool bIsShortBullet = rText == "* " || rText == "- ";
|
|
sal_uInt16 nMinLen = bIsShortBullet ? 1 : 2;
|
|
TextFrameIndex nBlanks(GetLeadingBlanks(rText));
|
|
const TextFrameIndex nLen = TextFrameIndex(rText.getLength()) - nBlanks;
|
|
if( !nLen )
|
|
return false;
|
|
|
|
// -, +, * separated by blank ??
|
|
if (TextFrameIndex(nMinLen) < 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
|
|
|
|
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()->GetNode() );
|
|
m_aDelPam.Move( fnMoveForward );
|
|
m_pDoc->InsertTable( SwInsertTableOptions( SwInsertTableFlags::All , 1 ),
|
|
*m_aDelPam.GetPoint(), 1, nColCnt, eHori,
|
|
nullptr, &aPosArr );
|
|
m_aDelPam.GetPoint()->Assign(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( std::u16string_view aStr )
|
|
{
|
|
size_t nL;
|
|
size_t n;
|
|
|
|
for( nL = aStr.size(), n = 0; n < nL && IsSpace( aStr[ n ] ); ++n )
|
|
;
|
|
return n;
|
|
}
|
|
|
|
sal_Int32 SwAutoFormat::GetTrailingBlanks( std::u16string_view aStr )
|
|
{
|
|
size_t nL = aStr.size(), n = nL;
|
|
if( !nL )
|
|
return 0;
|
|
|
|
while( --n && IsSpace( aStr[ 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()->Assign( *m_pCurTextFrame->GetTextNodeForParaProps() );
|
|
|
|
// 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()->GetNode().GetTextNode()->HasSwAttrSet())
|
|
{
|
|
aSet.Put(*m_aDelPam.GetPoint()->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()->GetNode();
|
|
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()->Assign( m_pCurTextFrame->GetTextNodeFirst()->GetIndex() - 1 );
|
|
SwTextNode* pTNd = m_aDelPam.GetMarkNode().GetTextNode();
|
|
if( pTNd )
|
|
// first use the previous text node
|
|
m_aDelPam.GetMark()->SetContent(pTNd->GetText().getLength());
|
|
else if( bTstNextPara )
|
|
{
|
|
// then try the next (at the beginning of a Doc, table cells, frames, ...)
|
|
const SwTextNode* pNext = m_pCurTextFrame->GetMergedPara()
|
|
? m_pCurTextFrame->GetMergedPara()->pLastNode
|
|
: m_pCurTextNd;
|
|
m_aDelPam.GetMark()->Assign(pNext->GetIndex() + 1);
|
|
pTNd = m_aDelPam.GetMarkNode().GetTextNode();
|
|
if( pTNd )
|
|
{
|
|
*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.GetPointNode().IsTextNode());
|
|
assert(!m_aDelPam.HasMark());
|
|
m_aDelPam.SetMark(); // mark remains at join position
|
|
m_pCurTextFrame = GetFrame(*m_aDelPam.GetPointNode().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, u""_ustr, 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()->Assign( *m_pCurTextFrame->GetTextNodeFirst() );
|
|
m_aDelPam.SetMark();
|
|
|
|
m_aDelPam.GetPoint()->Adjust(SwNodeOffset(-1));
|
|
SwTextNode* pTNd = m_aDelPam.GetPointNode().GetTextNode();
|
|
if( pTNd )
|
|
{
|
|
// use the previous text node first
|
|
m_aDelPam.GetPoint()->SetContent(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);
|
|
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()
|
|
.GetTextLeftMargin()
|
|
.ResolveLeft(m_pCurTextFrame->GetTextNodeForParaProps()
|
|
->GetSwAttrSet()
|
|
.GetFirstLineIndent(),
|
|
/*metrics*/ {});
|
|
}
|
|
|
|
if( m_bMoreLines )
|
|
DelMoreLinesBlanks();
|
|
else
|
|
bBreak = !IsFastFullLine(*m_pCurTextFrame)
|
|
|| IsBlanksInString(*m_pCurTextFrame)
|
|
|| IsSentenceAtEnd(*m_pCurTextFrame);
|
|
bool bRTL = m_pEditShell->IsInRightToLeftText();
|
|
|
|
const OUString sStrWithTrailingBlanks = DelLeadingBlanks(m_pCurTextFrame->GetText());
|
|
bool bIsShortBullet = sStrWithTrailingBlanks == "* " || sStrWithTrailingBlanks == "- ";
|
|
sal_uInt16 nMinLen = bIsShortBullet ? 1 : 2;
|
|
|
|
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 = bIsShortBullet ? sStrWithTrailingBlanks : m_pCurTextFrame->GetText();
|
|
TextFrameIndex nTextStt(0);
|
|
const sal_Unicode* pFndBulletChr = nullptr;
|
|
if (m_aFlags.bChgEnumNum && nMinLen < 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.SetListFormat(u""_ustr, u""_ustr, n);
|
|
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 ) );
|
|
const OUString sPrefix = aPrefix.getToken(0, u'\x0001', nPrefixIdx);
|
|
aFormat.SetStart( o3tl::narrowing<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aPrefix, 0, u'\x0001', nPrefixIdx ))));
|
|
aFormat.SetListFormat(sPrefix, aPostfix.getToken(0, u'\x0001'), nLvl);
|
|
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 ) );
|
|
|
|
const OUString sPrefix = n ? u""_ustr : aPrefix.getToken(0, u'\x0001', nPrefixIdx);
|
|
aFormat.SetStart( o3tl::narrowing<sal_uInt16>(o3tl::toInt32(o3tl::getToken(aPrefix, 0, u'\x0001', nPrefixIdx )) ));
|
|
aFormat.SetListFormat(sPrefix, aPostfix.getToken(0, u'\x0001', nPostfixIdx), n);
|
|
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()->Assign( *m_pCurTextFrame->GetTextNodeForParaProps() );
|
|
|
|
if( m_aFlags.bSetNumRule )
|
|
{
|
|
if( m_aFlags.bAFormatByInput )
|
|
{
|
|
m_aDelPam.SetMark();
|
|
SwTextFrame const*const pNextFrame = GetNextNode(false);
|
|
if (pNextFrame)
|
|
{
|
|
m_aDelPam.GetMark()->Assign( *pNextFrame->GetTextNodeForParaProps() );
|
|
m_aDelPam.GetMarkNode().GetTextNode()->SetAttrListLevel( nLvl );
|
|
}
|
|
}
|
|
|
|
const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAttrListLevel(nLvl);
|
|
|
|
// start new list
|
|
m_pDoc->SetNumRule(m_aDelPam, aRule, SwDoc::SetNumRuleMode::CreateNewList, 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(bIsShortBullet ? 1 : 2);
|
|
|
|
while (!bIsShortBullet && 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()->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()->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()->Assign( *pNextFrame->GetTextNodeForParaProps() );
|
|
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()->SetContent( m_aDelPam.GetMark()->GetContentIndex() + 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()->GetNode();
|
|
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()->SetContent( m_aDelPam.GetMark()->GetContentIndex() + 1 );
|
|
m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
|
|
|
|
if( m_aFlags.bWithRedlining )
|
|
{
|
|
m_aNdIdx = m_aDelPam.GetPoint()->GetNode();
|
|
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()->GetNode();
|
|
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);
|
|
bBreak++;
|
|
}
|
|
// 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 (sal_Int32 nUpdatedPos = pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext); nUpdatedPos >= 0)
|
|
{
|
|
nPos = TextFrameIndex(nUpdatedPos);
|
|
break;
|
|
}
|
|
}
|
|
[[fallthrough]];
|
|
case '-':
|
|
if (m_aFlags.bChgWeightUnderl)
|
|
{
|
|
// consider Symbolfonts!
|
|
if (!aFInfo.GetFrame())
|
|
aFInfo.SetFrame(GetFrame(*m_pCurTextNd));
|
|
if (!aFInfo.IsBullet(nPos))
|
|
{
|
|
SetRedlineText('/' == cChar ? STR_AUTOFMTREDL_ITALIC : STR_AUTOFMTREDL_STRIKETHROUGH);
|
|
|
|
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()->GetNode();
|
|
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 // or -- are still contained in pText
|
|
if (m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() & RedlineFlags::ShowDelete)
|
|
{
|
|
nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint())
|
|
- TextFrameIndex(1);
|
|
bBreak++;
|
|
}
|
|
// Was a character deleted before starting?
|
|
if (cBlank && cBlank != (*pText)[sal_Int32(nSttPos) - 1])
|
|
--nSttPos;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '.':
|
|
case '!':
|
|
case '?':
|
|
if( m_aFlags.bCapitalStartSentence )
|
|
bFirstSent = true;
|
|
[[fallthrough]];
|
|
default:
|
|
if (!(rAppCC.isBase(*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()->GetNode();
|
|
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()->GetNode();
|
|
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 );
|
|
if (sal_Int32 nUpdatedPos = pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext); nUpdatedPos >= 0)
|
|
nPos = TextFrameIndex(nUpdatedPos);
|
|
}
|
|
|
|
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)) ||
|
|
( m_aFlags.bSetDOIAttr &&
|
|
(nPos == TextFrameIndex(pText->getLength()) || IsSpace((*pText)[sal_Int32(nPos)])) &&
|
|
SetRedlineText( STR_AUTOFMTREDL_DETECT_DOI ) &&
|
|
pATst->FnSetDOIAttr(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()->GetNode();
|
|
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 aFlags,
|
|
SwNode const * pSttNd, SwNode const * pEndNd )
|
|
: m_aFlags(std::move( aFlags )),
|
|
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 )
|
|
{
|
|
assert(pEndNd);
|
|
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 )
|
|
{
|
|
::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, TST_ENUMERIC, 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:
|
|
assert(m_pCurTextFrame);
|
|
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:
|
|
assert(m_pCurTextFrame);
|
|
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;
|
|
}
|
|
|
|
const OUString& rStr = m_pCurTextFrame->GetText();
|
|
SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
|
|
SvxSwAutoFormatFlags& rFlags = pACorr->GetSwFlags();
|
|
if (rFlags.bChgEnumNum && (rStr == "- " || rStr == "* "))
|
|
{
|
|
eStat = TST_ENUMERIC;
|
|
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:
|
|
{
|
|
assert(m_pCurTextFrame);
|
|
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();
|
|
// auto correct paragraphs that fail to enter state HAS_FMTCOLL
|
|
AutoCorrect();
|
|
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;
|
|
}
|
|
|
|
// replace custom styles with text body
|
|
if ( IsPoolUserFormat( nPoolId ) && m_aFlags.bChgUserColl )
|
|
{
|
|
SetColl( RES_POOLCOLL_TEXT, true );
|
|
}
|
|
|
|
// check for left margin set by the style
|
|
if( IsPoolUserFormat( nPoolId ) ||
|
|
RES_POOLCOLL_STANDARD == nPoolId )
|
|
{
|
|
SvxFirstLineIndentItem const*const pFirstLineIndent(
|
|
m_pCurTextFrame->GetTextNodeForParaProps()
|
|
->GetSwAttrSet().GetItemIfSet(RES_MARGIN_FIRSTLINE));
|
|
SvxTextLeftMarginItem const*const pTextLeftMargin(
|
|
m_pCurTextFrame->GetTextNodeForParaProps()
|
|
->GetSwAttrSet().GetItemIfSet(RES_MARGIN_TEXTLEFT));
|
|
|
|
// Unit conversion is not needed here: check the sign only
|
|
double dIndentValue = 0.0;
|
|
if (pFirstLineIndent)
|
|
dIndentValue = pFirstLineIndent->GetTextFirstLineOffset().m_dValue;
|
|
|
|
if (0.0 != dIndentValue
|
|
|| (pTextLeftMargin && (0.0 != pTextLeftMargin->GetTextLeft().m_dValue)))
|
|
{
|
|
// 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.0 < dIndentValue) // positive 1st line indentation
|
|
BuildIndent();
|
|
else if (0.0 > dIndentValue) // negative 1st line indentation
|
|
BuildNegIndent( aFInfo.GetLineStart() );
|
|
else if (pTextLeftMargin
|
|
&& (pTextLeftMargin->GetTextLeft().m_dValue
|
|
!= 0.0)) // 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;
|
|
|
|
assert(m_pCurTextFrame);
|
|
|
|
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;
|
|
assert(m_pCurTextFrame);
|
|
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())
|
|
{
|
|
SvxFirstLineIndentItem const*const pFirstLineIndent(
|
|
m_pCurTextFrame->GetTextNodeForParaProps()
|
|
->GetSwAttrSet().GetItemIfSet(RES_MARGIN_FIRSTLINE, false));
|
|
SvxTextLeftMarginItem const*const pTextLeftMargin(
|
|
m_pCurTextFrame->GetTextNodeForParaProps()
|
|
->GetSwAttrSet().GetItemIfSet(RES_MARGIN_TEXTLEFT, false));
|
|
|
|
// Unit conversion is not needed here: check the sign only
|
|
double dIndentValue = 0.0;
|
|
if (pFirstLineIndent)
|
|
dIndentValue = pFirstLineIndent->GetTextFirstLineOffset().m_dValue;
|
|
|
|
if (bReplaceStyles
|
|
&& (0.0 != dIndentValue
|
|
|| (pTextLeftMargin
|
|
&& (0.0 != pTextLeftMargin->GetTextLeft().m_dValue))))
|
|
{
|
|
// then use one of our templates
|
|
if (0.0 < dIndentValue) // positive 1st line indentation
|
|
BuildIndent();
|
|
else if (0.0 > dIndentValue) // negative 1st line indentation
|
|
{
|
|
BuildNegIndent( aFInfo.GetLineStart() );
|
|
}
|
|
else if (pTextLeftMargin
|
|
&& (0.0
|
|
!= pTextLeftMargin->GetTextLeft().m_dValue)) // is indentation
|
|
BuildTextIndent();
|
|
else
|
|
BuildText();
|
|
}
|
|
}
|
|
// force auto correct
|
|
AutoCorrect();
|
|
}
|
|
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, bool bCurrentParagraphOnly )
|
|
{
|
|
std::optional<SwWait> oWait;
|
|
|
|
CurrShell aCurr( this );
|
|
StartAllAction();
|
|
StartUndo( SwUndoId::AUTOFORMAT );
|
|
|
|
SvxSwAutoFormatFlags aAFFlags; // use default values or add params?
|
|
if( pAFlags )
|
|
{
|
|
aAFFlags = *pAFlags;
|
|
if( !aAFFlags.bAFormatByInput )
|
|
oWait.emplace( *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( this, aAFFlags, &rPaM.Start()->GetNode(),
|
|
&rPaM.End()->GetNode() );
|
|
}
|
|
}
|
|
}
|
|
else if (bCurrentParagraphOnly)
|
|
{
|
|
pCursor->SetMark();
|
|
SwAutoFormat aFormat( this, std::move(aAFFlags), &pCursor->GetMark()->GetNode(),
|
|
&pCursor->GetPoint()->GetNode() );
|
|
}
|
|
else
|
|
{
|
|
SwAutoFormat( this, std::move(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();
|
|
SwPosition* pMarkPos = pCursor->GetMark();
|
|
if( pMarkPos->GetContentIndex() )
|
|
{
|
|
pMarkPos->SetContent(0);
|
|
bRange = true;
|
|
}
|
|
else
|
|
{
|
|
// then go one node backwards
|
|
SwNodeIndex aNdIdx(pCursor->GetMark()->GetNode());
|
|
sw::GotoPrevLayoutTextFrame(aNdIdx, GetLayout());
|
|
SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
|
|
if (pTextNd && !pTextNd->GetText().isEmpty())
|
|
{
|
|
pCursor->GetMark()->Assign( aNdIdx );
|
|
bRange = true;
|
|
}
|
|
}
|
|
|
|
if( bRange )
|
|
{
|
|
Push(); // save cursor
|
|
|
|
SvxSwAutoFormatFlags aAFFlags = *GetAutoFormatFlags(); // use default values so far
|
|
|
|
SwAutoFormat aFormat( this, std::move(aAFFlags), &pCursor->GetMark()->GetNode(),
|
|
&pCursor->GetPoint()->GetNode() );
|
|
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 |
|
|
ACFlags::SetDOIAttr ))
|
|
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: */
|