nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / core / edit / autofmt.cxx
blob060e8b2889b482f117885fe52d47f83e278ea297
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
22 #include <unotools/charclass.hxx>
24 #include <editeng/boxitem.hxx>
25 #include <editeng/lrspitem.hxx>
26 #include <editeng/formatbreakitem.hxx>
27 #include <editeng/adjustitem.hxx>
28 #include <editeng/tstpitem.hxx>
29 #include <editeng/fontitem.hxx>
30 #include <editeng/langitem.hxx>
31 #include <editeng/acorrcfg.hxx>
33 #include <swwait.hxx>
34 #include <fmtpdsc.hxx>
35 #include <doc.hxx>
36 #include <IDocumentUndoRedo.hxx>
37 #include <DocumentRedlineManager.hxx>
38 #include <IDocumentStylePoolAccess.hxx>
39 #include <redline.hxx>
40 #include <unocrsr.hxx>
41 #include <docary.hxx>
42 #include <editsh.hxx>
43 #include <index.hxx>
44 #include <pam.hxx>
45 #include <swundo.hxx>
46 #include <poolfmt.hxx>
47 #include <ndtxt.hxx>
48 #include <rootfrm.hxx>
49 #include <txtfrm.hxx>
50 #include <frminf.hxx>
51 #include <pagedesc.hxx>
52 #include <paratr.hxx>
53 #include <acorrect.hxx>
54 #include <shellres.hxx>
55 #include <section.hxx>
56 #include <frmatr.hxx>
57 #include <charatr.hxx>
58 #include <mdiexp.hxx>
59 #include <strings.hrc>
60 #include <comcore.hxx>
61 #include <numrule.hxx>
62 #include <itabenum.hxx>
64 #include <memory>
66 using namespace ::com::sun::star;
68 //JP 16.12.99: definition:
69 // from pos cPosEnDash to cPosEmDash all chars changed to em dashes,
70 // from pos cPosEmDash to cPosEnd all chars changed to em dashes
71 // all other chars are changed to the user configuration
73 const sal_Unicode pBulletChar[6] = { '+', '*', '-', 0x2013, 0x2014, 0 };
74 const int cnPosEnDash = 2, cnPosEmDash = 4;
76 const sal_Unicode cStarSymbolEnDash = 0x2013;
77 const sal_Unicode cStarSymbolEmDash = 0x2014;
79 SvxSwAutoFormatFlags* SwEditShell::s_pAutoFormatFlags = nullptr;
81 // Number of num-/bullet-paragraph templates. MAXLEVEL will soon be raised
82 // to x, but not the number of templates. (Artifact from <= 4.0)
83 const sal_uInt16 cnNumBullColls = 4;
85 class SwAutoFormat
87 SvxSwAutoFormatFlags m_aFlags;
88 SwPaM m_aDelPam; // a Pam that can be used
89 SwNodeIndex m_aNdIdx; // the index on the current TextNode
90 SwNodeIndex m_aEndNdIdx; // index on the end of the area
92 SwEditShell* m_pEditShell;
93 SwDoc* m_pDoc;
94 SwTextNode* m_pCurTextNd; // the current TextNode
95 SwTextFrame* m_pCurTextFrame; // frame of the current TextNode
96 sal_uLong m_nEndNdIdx; // for the percentage-display
97 mutable std::unique_ptr<CharClass> m_pCharClass; // Character classification
98 mutable LanguageType m_eCharClassLang;
100 sal_uInt16 m_nRedlAutoFormatSeqId;
102 enum
104 NONE = 0,
105 DELIM = 1,
106 DIGIT = 2,
107 CHG = 4,
108 LOWER_ALPHA = 8,
109 UPPER_ALPHA = 16,
110 LOWER_ROMAN = 32,
111 UPPER_ROMAN = 64,
112 NO_DELIM = (DIGIT|LOWER_ALPHA|UPPER_ALPHA|LOWER_ROMAN|UPPER_ROMAN)
115 bool m_bEnd : 1;
116 bool m_bMoreLines : 1;
118 CharClass& GetCharClass( LanguageType eLang ) const
120 if( !m_pCharClass || eLang != m_eCharClassLang )
122 m_pCharClass.reset( new CharClass( LanguageTag( eLang ) ) );
123 m_eCharClassLang = eLang;
125 return *m_pCharClass;
128 static bool IsSpace( const sal_Unicode c )
129 { return (' ' == c || '\t' == c || 0x0a == c|| 0x3000 == c /* Jap. space */); }
131 void SetColl( sal_uInt16 nId, bool bHdLineOrText = false );
132 void GoNextPara();
133 static bool HasObjects(const SwTextFrame &);
135 // TextNode methods
136 const SwTextFrame * GetNextNode(bool isCheckEnd = true) const;
137 static bool IsEmptyLine(const SwTextFrame & rFrame)
139 return rFrame.GetText().isEmpty()
140 || rFrame.GetText().getLength() == GetLeadingBlanks(rFrame.GetText());
143 bool IsOneLine(const SwTextFrame &) const;
144 bool IsFastFullLine(const SwTextFrame &) const;
145 bool IsNoAlphaLine(const SwTextFrame &) const;
146 bool IsEnumericChar(const SwTextFrame &) const;
147 static bool IsBlanksInString(const SwTextFrame&);
148 sal_uInt16 CalcLevel(const SwTextFrame&, sal_uInt16 *pDigitLvl = nullptr) const;
149 sal_Int32 GetBigIndent(TextFrameIndex & rCurrentSpacePos) const;
151 static OUString DelLeadingBlanks(const OUString& rStr);
152 static OUString DelTrailingBlanks( const OUString& rStr );
153 static sal_Int32 GetLeadingBlanks( const OUString& rStr );
154 static sal_Int32 GetTrailingBlanks( const OUString& rStr );
156 bool IsFirstCharCapital(const SwTextFrame & rNd) const;
157 sal_uInt16 GetDigitLevel(const SwTextFrame& rFrame, TextFrameIndex& rPos,
158 OUString* pPrefix = nullptr, OUString* pPostfix = nullptr,
159 OUString* pNumTypes = nullptr ) const;
160 /// get the FORMATTED TextFrame
161 SwTextFrame* GetFrame( const SwTextNode& rTextNd ) const;
162 SwTextFrame * EnsureFormatted(SwTextFrame const&) const;
164 void BuildIndent();
165 void BuildText();
166 void BuildTextIndent();
167 void BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel );
168 void BuildNegIndent( SwTwips nSpaces );
169 void BuildHeadLine( sal_uInt16 nLvl );
171 static bool HasBreakAttr(const SwTextFrame &);
172 void DeleteSel( SwPaM& rPam );
173 void DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect);
174 bool DeleteJoinCurNextPara(SwTextFrame const* pNextFrame, bool bIgnoreLeadingBlanks = false);
175 /// delete in the node start and/or end
176 void DeleteLeadingTrailingBlanks( bool bStart = true, bool bEnd = true );
177 void DelEmptyLine( bool bTstNextPara = true );
178 /// when using multiline paragraphs delete the "left" and/or
179 /// "right" margins
180 void DelMoreLinesBlanks( bool bWithLineBreaks = false );
181 /// join with the previous paragraph
182 void JoinPrevPara();
183 /// execute AutoCorrect on current TextNode
184 void AutoCorrect(TextFrameIndex nSttPos = TextFrameIndex(0));
186 bool CanJoin(const SwTextFrame * pNextFrame) const
188 return !m_bEnd && pNextFrame
189 && !IsEmptyLine(*pNextFrame)
190 && !IsNoAlphaLine(*pNextFrame)
191 && !IsEnumericChar(*pNextFrame)
192 // check the last / first nodes here...
193 && ((COMPLETE_STRING - 50 - pNextFrame->GetTextNodeFirst()->GetText().getLength())
194 > (m_pCurTextFrame->GetMergedPara()
195 ? m_pCurTextFrame->GetMergedPara()->pLastNode
196 : m_pCurTextNd)->GetText().getLength())
197 && !HasBreakAttr(*pNextFrame);
200 /// is a dot at the end ??
201 static bool IsSentenceAtEnd(const SwTextFrame & rTextFrame);
203 bool DoUnderline();
204 bool DoTable();
206 void SetRedlineText_( sal_uInt16 nId );
207 bool SetRedlineText( sal_uInt16 nId ) {
208 if( m_aFlags.bWithRedlining )
209 SetRedlineText_( nId );
210 return true;
212 void ClearRedlineText() {
213 if( m_aFlags.bWithRedlining )
214 m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment(nullptr);
217 public:
218 SwAutoFormat( SwEditShell* pEdShell, SvxSwAutoFormatFlags const & rFlags,
219 SwNodeIndex const * pSttNd = nullptr, SwNodeIndex const * pEndNd = nullptr );
222 static const sal_Unicode* StrChr( const sal_Unicode* pSrc, sal_Unicode c )
224 while( *pSrc && *pSrc != c )
225 ++pSrc;
226 return *pSrc ? pSrc : nullptr;
229 SwTextFrame* SwAutoFormat::GetFrame( const SwTextNode& rTextNd ) const
231 // get the Frame
232 const SwContentFrame *pFrame = rTextNd.getLayoutFrame( m_pEditShell->GetLayout() );
233 assert(pFrame && "For Autoformat a Layout is needed");
234 return EnsureFormatted(*static_cast<SwTextFrame const*>(pFrame));
237 SwTextFrame * SwAutoFormat::EnsureFormatted(SwTextFrame const& rFrame) const
239 SwTextFrame *const pFrame(const_cast<SwTextFrame*>(&rFrame));
240 if( m_aFlags.bAFormatByInput && !pFrame->isFrameAreaDefinitionValid() )
242 DisableCallbackAction a(*pFrame->getRootFrame());
243 SwRect aTmpFrame( pFrame->getFrameArea() );
244 SwRect aTmpPrt( pFrame->getFramePrintArea() );
245 pFrame->Calc(pFrame->getRootFrame()->GetCurrShell()->GetOut());
247 if( pFrame->getFrameArea() != aTmpFrame || pFrame->getFramePrintArea() != aTmpPrt ||
248 !pFrame->GetPaintSwRect().IsEmpty())
250 pFrame->SetCompletePaint();
254 return pFrame->GetFormatted();
257 void SwAutoFormat::SetRedlineText_( sal_uInt16 nActionId )
259 OUString sText;
260 sal_uInt16 nSeqNo = 0;
261 if( STR_AUTOFMTREDL_END > nActionId )
263 sText = SwViewShell::GetShellRes()->GetAutoFormatNameLst()[ nActionId ];
264 switch( nActionId )
266 case STR_AUTOFMTREDL_SET_NUMBER_BULLET:
267 case STR_AUTOFMTREDL_DEL_MORELINES:
269 // AutoCorrect actions
270 case STR_AUTOFMTREDL_USE_REPLACE:
271 case STR_AUTOFMTREDL_CPTL_STT_WORD:
272 case STR_AUTOFMTREDL_CPTL_STT_SENT:
273 case STR_AUTOFMTREDL_TYPO:
274 case STR_AUTOFMTREDL_UNDER:
275 case STR_AUTOFMTREDL_BOLD:
276 case STR_AUTOFMTREDL_FRACTION:
277 case STR_AUTOFMTREDL_DASH:
278 case STR_AUTOFMTREDL_ORDINAL:
279 case STR_AUTOFMTREDL_NON_BREAK_SPACE:
280 case STR_AUTOFMTREDL_TRANSLITERATE_RTL:
281 nSeqNo = ++m_nRedlAutoFormatSeqId;
282 break;
285 #if OSL_DEBUG_LEVEL > 0
286 else
287 sText = "Action text is missing";
288 #endif
290 m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment( &sText, nSeqNo );
293 void SwAutoFormat::GoNextPara()
295 SwNode* pNewNd = nullptr;
296 do {
297 // has to be checked twice before and after incrementation
298 if( m_aNdIdx.GetIndex() >= m_aEndNdIdx.GetIndex() )
300 m_bEnd = true;
301 return;
304 sw::GotoNextLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
305 if( m_aNdIdx.GetIndex() >= m_aEndNdIdx.GetIndex() )
307 m_bEnd = true;
308 return;
310 else
311 pNewNd = &m_aNdIdx.GetNode();
313 // not a TextNode ->
314 // TableNode : skip table
315 // NoTextNode : skip nodes
316 // EndNode : at the end, terminate
317 if( pNewNd->IsEndNode() )
319 m_bEnd = true;
320 return;
322 else if( pNewNd->IsTableNode() )
323 m_aNdIdx = *pNewNd->EndOfSectionNode();
324 else if( pNewNd->IsSectionNode() )
326 const SwSection& rSect = pNewNd->GetSectionNode()->GetSection();
327 if( rSect.IsHiddenFlag() || rSect.IsProtectFlag() )
328 m_aNdIdx = *pNewNd->EndOfSectionNode();
330 } while( !pNewNd->IsTextNode() );
332 if( !m_aFlags.bAFormatByInput )
333 ::SetProgressState( m_aNdIdx.GetIndex() + m_nEndNdIdx - m_aEndNdIdx.GetIndex(),
334 m_pDoc->GetDocShell() );
336 m_pCurTextNd = static_cast<SwTextNode*>(pNewNd);
337 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
340 bool SwAutoFormat::HasObjects(const SwTextFrame & rFrame)
342 // Is there something bound to the paragraph in the paragraph
343 // like Frames, DrawObjects, ...
344 SwNodeIndex node(*rFrame.GetTextNodeFirst());
347 if (node.GetNode().GetAnchoredFlys() != nullptr)
349 assert(!node.GetNode().GetAnchoredFlys()->empty());
350 return true;
352 ++node;
354 while (sw::FrameContainsNode(rFrame, node.GetIndex()));
355 return false;
358 const SwTextFrame* SwAutoFormat::GetNextNode(bool const isCheckEnd) const
360 SwNodeIndex tmp(m_aNdIdx);
361 sw::GotoNextLayoutTextFrame(tmp, m_pEditShell->GetLayout());
362 if ((isCheckEnd && m_aEndNdIdx <= tmp) || !tmp.GetNode().IsTextNode())
363 return nullptr;
364 // note: the returned frame is not necessarily formatted, have to call
365 // EnsureFormatted for that
366 return static_cast<SwTextFrame*>(tmp.GetNode().GetTextNode()->getLayoutFrame(m_pEditShell->GetLayout()));
369 bool SwAutoFormat::IsOneLine(const SwTextFrame & rFrame) const
371 SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
372 return aFInfo.IsOneLine();
375 bool SwAutoFormat::IsFastFullLine(const SwTextFrame & rFrame) const
377 bool bRet = m_aFlags.bRightMargin;
378 if( bRet )
380 SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
381 bRet = aFInfo.IsFilled( m_aFlags.nRightMargin );
383 return bRet;
386 bool SwAutoFormat::IsEnumericChar(const SwTextFrame& rFrame) const
388 const OUString& rText = rFrame.GetText();
389 TextFrameIndex nBlanks(GetLeadingBlanks(rText));
390 const TextFrameIndex nLen = TextFrameIndex(rText.getLength()) - nBlanks;
391 if( !nLen )
392 return false;
394 // -, +, * separated by blank ??
395 if (TextFrameIndex(2) < nLen && IsSpace(rText[sal_Int32(nBlanks) + 1]))
397 if (StrChr(pBulletChar, rText[sal_Int32(nBlanks)]))
398 return true;
399 // Should there be a symbol font at the position?
400 SwTextFrameInfo aFInfo( EnsureFormatted(rFrame) );
401 if (aFInfo.IsBullet(nBlanks))
402 return true;
405 // 1.) / 1. / 1.1.1 / (1). / (1) / ...
406 return USHRT_MAX != GetDigitLevel(rFrame, nBlanks);
409 bool SwAutoFormat::IsBlanksInString(const SwTextFrame& rFrame)
411 // Search more than 5 consecutive blanks/tabs in the string.
412 OUString sTmp( DelLeadingBlanks(rFrame.GetText()) );
413 const sal_Int32 nLen = sTmp.getLength();
414 sal_Int32 nIdx = 0;
415 while (nIdx < nLen)
417 // Skip non-blanks
418 while (nIdx < nLen && !IsSpace(sTmp[nIdx])) ++nIdx;
419 if (nIdx == nLen)
420 return false;
421 // Then count consecutive blanks
422 const sal_Int32 nFirst = nIdx;
423 while (nIdx < nLen && IsSpace(sTmp[nIdx])) ++nIdx;
424 // And exit if enough consecutive blanks were found
425 if (nIdx-nFirst > 5)
426 return true;
428 return false;
431 sal_uInt16 SwAutoFormat::CalcLevel(const SwTextFrame & rFrame,
432 sal_uInt16 *const pDigitLvl) const
434 sal_uInt16 nLvl = 0, nBlnk = 0;
435 const OUString& rText = rFrame.GetText();
436 if( pDigitLvl )
437 *pDigitLvl = USHRT_MAX;
439 if (RES_POOLCOLL_TEXT_MOVE == rFrame.GetTextNodeForParaProps()->GetTextColl()->GetPoolFormatId())
441 if( m_aFlags.bAFormatByInput )
443 // this is very non-obvious: on the *first* invocation of
444 // AutoFormat, the node will have the tabs (any number) converted
445 // to a fixed indent in BuildTextIndent(), and the number of tabs
446 // is stored in the node;
447 // on the *second* invocation of AutoFormat, CalcLevel() will
448 // retrieve the stored number, and it will be used by
449 // BuildHeadLine() to select the corresponding heading style.
450 nLvl = rFrame.GetTextNodeForParaProps()->GetAutoFormatLvl();
451 const_cast<SwTextNode *>(rFrame.GetTextNodeForParaProps())->SetAutoFormatLvl(0);
452 if( nLvl )
453 return nLvl;
455 ++nLvl;
458 for (TextFrameIndex n(0),
459 nEnd(rText.getLength()); n < nEnd; ++n)
461 switch (rText[sal_Int32(n)])
463 case ' ': if( 3 == ++nBlnk )
465 ++nLvl;
466 nBlnk = 0;
468 break;
469 case '\t': ++nLvl;
470 nBlnk = 0;
471 break;
472 default:
473 if( pDigitLvl )
474 // test 1.) / 1. / 1.1.1 / (1). / (1) / ...
475 *pDigitLvl = GetDigitLevel(rFrame, n);
476 return nLvl;
479 return nLvl;
482 sal_Int32 SwAutoFormat::GetBigIndent(TextFrameIndex & rCurrentSpacePos) const
484 SwTextFrameInfo aFInfo( m_pCurTextFrame );
485 const SwTextFrame* pNextFrame = nullptr;
487 if( !m_bMoreLines )
489 pNextFrame = GetNextNode();
490 if (!CanJoin(pNextFrame) || !IsOneLine(*pNextFrame))
491 return 0;
493 pNextFrame = EnsureFormatted(*pNextFrame);
496 return aFInfo.GetBigIndent( rCurrentSpacePos, pNextFrame );
499 bool SwAutoFormat::IsNoAlphaLine(const SwTextFrame & rFrame) const
501 const OUString& rStr = rFrame.GetText();
502 if( rStr.isEmpty() )
503 return false;
504 // or better: determine via number of AlphaNum and !AlphaNum characters
505 sal_Int32 nANChar = 0, nBlnk = 0;
507 for (TextFrameIndex n(0),
508 nEnd(rStr.getLength()); n < nEnd; ++n)
509 if (IsSpace(rStr[sal_Int32(n)]))
510 ++nBlnk;
511 else
513 auto const pair = rFrame.MapViewToModel(n);
514 CharClass& rCC = GetCharClass(pair.first->GetSwAttrSet().GetLanguage().GetLanguage());
515 if (rCC.isLetterNumeric(rStr, sal_Int32(n)))
516 ++nANChar;
519 // If there are 75% of non-alphanumeric characters, then true
520 sal_uLong nLen = rStr.getLength() - nBlnk;
521 nLen = ( nLen * 3 ) / 4; // long overflow, if the strlen > sal_uInt16
522 return sal_Int32(nLen) < (rStr.getLength() - nANChar - nBlnk);
525 bool SwAutoFormat::DoUnderline()
527 if( !m_aFlags.bSetBorder )
528 return false;
530 OUString const& rText(m_pCurTextFrame->GetText());
531 int eState = 0;
532 sal_Int32 nCnt = 0;
533 while (nCnt < rText.getLength())
535 int eTmp = 0;
536 switch (rText[nCnt])
538 case '-': eTmp = 1; break;
539 case '_': eTmp = 2; break;
540 case '=': eTmp = 3; break;
541 case '*': eTmp = 4; break;
542 case '~': eTmp = 5; break;
543 case '#': eTmp = 6; break;
544 default:
545 return false;
547 if( 0 == eState )
548 eState = eTmp;
549 else if( eState != eTmp )
550 return false;
551 ++nCnt;
554 if( 2 < nCnt )
556 // then underline the previous paragraph if one exists
557 DelEmptyLine( false ); // -> point will be on end of current paragraph
558 // WARNING: rText may be deleted now, m_pCurTextFrame may be nullptr
559 m_aDelPam.SetMark();
560 // apply to last node & rely on InsertItemSet to apply it to props-node
561 m_aDelPam.GetMark()->nContent = 0;
563 editeng::SvxBorderLine aLine;
564 switch( eState )
566 case 1: // single, 0.05 pt
567 aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
568 aLine.SetWidth( DEF_LINE_WIDTH_0 );
569 break;
570 case 2: // single, 1.0 pt
571 aLine.SetBorderLineStyle(SvxBorderLineStyle::SOLID);
572 aLine.SetWidth( DEF_LINE_WIDTH_1 );
573 break;
574 case 3: // double, 1.0 pt
575 aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
576 aLine.SetWidth( DEF_LINE_WIDTH_1 );
577 break;
578 case 4: // double (thick/thin), 4.0 pt
579 aLine.SetBorderLineStyle(SvxBorderLineStyle::THICKTHIN_SMALLGAP);
580 aLine.SetWidth( DEF_LINE_WIDTH_3 );
581 break;
582 case 5: // double (thin/thick), 4.0 pt
583 aLine.SetBorderLineStyle(SvxBorderLineStyle::THINTHICK_SMALLGAP);
584 aLine.SetWidth( DEF_LINE_WIDTH_3 );
585 break;
586 case 6: // double, 2.5 pt
587 aLine.SetBorderLineStyle(SvxBorderLineStyle::DOUBLE);
588 aLine.SetWidth( DEF_LINE_WIDTH_2 );
589 break;
591 SfxItemSet aSet(m_pDoc->GetAttrPool(),
592 svl::Items<RES_PARATR_CONNECT_BORDER, RES_PARATR_CONNECT_BORDER,
593 RES_BOX, RES_BOX>{});
594 aSet.Put( SwParaConnectBorderItem( false ) );
595 SvxBoxItem aBox( RES_BOX );
596 aBox.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
597 aBox.SetDistance(42, SvxBoxItemLine::BOTTOM ); // ~0,75 mm
598 aSet.Put(aBox);
599 m_pDoc->getIDocumentContentOperations().InsertItemSet(m_aDelPam, aSet,
600 SetAttrMode::DEFAULT, m_pEditShell->GetLayout());
602 m_aDelPam.DeleteMark();
604 return 2 < nCnt;
607 bool SwAutoFormat::DoTable()
609 if( !m_aFlags.bCreateTable || !m_aFlags.bAFormatByInput ||
610 m_pCurTextNd->FindTableNode() )
611 return false;
613 const OUString& rTmp = m_pCurTextFrame->GetText();
614 TextFrameIndex nSttPlus(GetLeadingBlanks(rTmp));
615 TextFrameIndex nEndPlus(GetTrailingBlanks(rTmp));
616 sal_Unicode cChar;
618 if (TextFrameIndex(2) > nEndPlus - nSttPlus
619 || ('+' != (cChar = rTmp[sal_Int32(nSttPlus)]) && '|' != cChar)
620 || ('+' != (cChar = rTmp[sal_Int32(nEndPlus) - 1]) && '|' != cChar))
621 return false;
623 SwTextFrameInfo aInfo( m_pCurTextFrame );
625 TextFrameIndex n = nSttPlus;
626 std::vector<sal_uInt16> aPosArr;
628 while (n < TextFrameIndex(rTmp.getLength()))
630 switch (rTmp[sal_Int32(n)])
632 case '-':
633 case '_':
634 case '=':
635 case ' ':
636 case '\t':
637 break;
639 case '+':
640 case '|':
641 aPosArr.push_back( static_cast<sal_uInt16>(aInfo.GetCharPos(n)) );
642 break;
644 default:
645 return false;
647 if( ++n == nEndPlus )
648 break;
651 if( 1 < aPosArr.size() )
653 // get the text node's alignment
654 sal_uInt16 nColCnt = aPosArr.size() - 1;
655 SwTwips nSttPos = aPosArr[ 0 ];
656 sal_Int16 eHori;
657 switch (m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust().GetAdjust())
659 case SvxAdjust::Center: eHori = text::HoriOrientation::CENTER; break;
660 case SvxAdjust::Right: eHori = text::HoriOrientation::RIGHT; break;
662 default:
663 if( nSttPos )
665 eHori = text::HoriOrientation::NONE;
666 // then - as last - we need to add the current frame width into the array
667 aPosArr.push_back( static_cast<sal_uInt16>(m_pCurTextFrame->getFrameArea().Width()) );
669 else
670 eHori = text::HoriOrientation::LEFT;
671 break;
674 // then create a table that matches the character
675 DelEmptyLine();
676 // WARNING: rTmp may be deleted now, m_pCurTextFrame may be nullptr
677 SwNodeIndex aIdx( m_aDelPam.GetPoint()->nNode );
678 m_aDelPam.Move( fnMoveForward );
679 m_pDoc->InsertTable( SwInsertTableOptions( SwInsertTableFlags::All , 1 ),
680 *m_aDelPam.GetPoint(), 1, nColCnt, eHori,
681 nullptr, &aPosArr );
682 m_aDelPam.GetPoint()->nNode = aIdx;
684 return 1 < aPosArr.size();
687 OUString SwAutoFormat::DelLeadingBlanks( const OUString& rStr )
689 sal_Int32 nL, n;
690 for( nL = rStr.getLength(), n = 0; n < nL && IsSpace( rStr[n] ); ++n )
692 if( n ) // no Spaces
693 return rStr.copy(n);
694 return rStr;
697 OUString SwAutoFormat::DelTrailingBlanks( const OUString& rStr )
699 sal_Int32 nL = rStr.getLength(), n = nL;
700 if( !nL )
701 return rStr;
703 while( --n && IsSpace( rStr[ n ] ) )
705 if( n+1 != nL ) // no Spaces
706 return rStr.copy( 0, n+1 );
707 return rStr;
710 sal_Int32 SwAutoFormat::GetLeadingBlanks( const OUString& rStr )
712 sal_Int32 nL;
713 sal_Int32 n;
715 for( nL = rStr.getLength(), n = 0; n < nL && IsSpace( rStr[ n ] ); ++n )
717 return n;
720 sal_Int32 SwAutoFormat::GetTrailingBlanks( const OUString& rStr )
722 sal_Int32 nL = rStr.getLength(), n = nL;
723 if( !nL )
724 return 0;
726 while( --n && IsSpace( rStr[ n ] ) )
728 return ++n;
731 bool SwAutoFormat::IsFirstCharCapital(const SwTextFrame& rFrame) const
733 const OUString& rText = rFrame.GetText();
734 for (TextFrameIndex n(0),
735 nEnd(rText.getLength()); n < nEnd; ++n)
736 if (!IsSpace(rText[sal_Int32(n)]))
738 auto const pair = rFrame.MapViewToModel(n);
739 CharClass& rCC = GetCharClass( pair.first->GetSwAttrSet().
740 GetLanguage().GetLanguage() );
741 sal_Int32 nCharType = rCC.getCharacterType(rText, sal_Int32(n));
742 return CharClass::isLetterType( nCharType ) &&
743 0 != ( i18n::KCharacterType::UPPER &
744 nCharType );
746 return false;
749 sal_uInt16
750 SwAutoFormat::GetDigitLevel(const SwTextFrame& rFrame, TextFrameIndex& rPos,
751 OUString* pPrefix, OUString* pPostfix, OUString* pNumTypes ) const
754 // check for 1.) / 1. / 1.1.1 / (1). / (1) / ...
755 const OUString& rText = rFrame.GetText();
756 sal_Int32 nPos(rPos);
757 int eScan = NONE;
759 sal_uInt16 nStart = 0;
760 sal_uInt8 nDigitLvl = 0, nDigitCnt = 0;
761 // count number of parenthesis to assure a sensible order is found
762 sal_uInt16 nOpeningParentheses = 0;
763 sal_uInt16 nClosingParentheses = 0;
765 while (nPos < rText.getLength() && nDigitLvl < MAXLEVEL - 1)
767 auto const pair = rFrame.MapViewToModel(TextFrameIndex(nPos));
768 CharClass& rCC = GetCharClass(pair.first->GetSwAttrSet().GetLanguage().GetLanguage());
769 const sal_Unicode cCurrentChar = rText[nPos];
770 if( ('0' <= cCurrentChar && '9' >= cCurrentChar) ||
771 (0xff10 <= cCurrentChar && 0xff19 >= cCurrentChar) )
773 if( eScan & DELIM )
775 if( eScan & CHG ) // not if it starts with a number
777 ++nDigitLvl;
778 if( pPostfix )
779 *pPostfix += "\x01";
782 if( pNumTypes )
783 *pNumTypes += OUStringChar(sal_Unicode('0' + SVX_NUM_ARABIC));
785 eScan = eScan | CHG;
787 else if( pNumTypes && !(eScan & DIGIT) )
788 *pNumTypes += OUStringChar(sal_Unicode('0' + SVX_NUM_ARABIC));
790 eScan &= ~DELIM; // remove Delim
791 if( 0 != (eScan & ~CHG) && DIGIT != (eScan & ~CHG))
792 return USHRT_MAX;
794 eScan |= DIGIT; // add Digit
795 if( 3 == ++nDigitCnt ) // more than 2 numbers are not an enum anymore
796 return USHRT_MAX;
798 nStart *= 10;
799 nStart += cCurrentChar <= '9' ? cCurrentChar - '0' : cCurrentChar - 0xff10;
801 else if( rCC.isAlpha( rText, nPos ) )
803 bool bIsUpper =
804 0 != ( i18n::KCharacterType::UPPER &
805 rCC.getCharacterType( rText, nPos ));
806 sal_Unicode cLow = rCC.lowercase(rText, nPos, 1)[0], cNumTyp;
807 int eTmpScan;
809 // Roman numbers are "mdclxvi". Since we want to start numbering with c or d more often,
810 // convert first to characters and later to roman numbers if needed.
811 if( 256 > cLow && strchr( "mdclxvi", cLow ) )
813 if( bIsUpper )
815 cNumTyp = '0' + SVX_NUM_ROMAN_UPPER;
816 eTmpScan = UPPER_ROMAN;
818 else
820 cNumTyp = '0' + SVX_NUM_ROMAN_LOWER;
821 eTmpScan = LOWER_ROMAN;
824 else if( bIsUpper )
826 cNumTyp = '0' + SVX_NUM_CHARS_UPPER_LETTER;
827 eTmpScan = UPPER_ALPHA;
829 else
831 cNumTyp = '0' + SVX_NUM_CHARS_LOWER_LETTER;
832 eTmpScan = LOWER_ALPHA;
835 // Switch to roman numbers (only for c/d!)
836 if( 1 == nDigitCnt && ( eScan & (UPPER_ALPHA|LOWER_ALPHA) ) &&
837 ( 3 == nStart || 4 == nStart) && 256 > cLow &&
838 strchr( "mdclxvi", cLow ) &&
839 (( eScan & UPPER_ALPHA ) ? (eTmpScan & (UPPER_ALPHA|UPPER_ROMAN))
840 : (eTmpScan & (LOWER_ALPHA|LOWER_ROMAN))) )
842 sal_Unicode c = '0';
843 nStart = 3 == nStart ? 100 : 500;
844 if( UPPER_ALPHA == eTmpScan )
846 eTmpScan = UPPER_ROMAN;
847 c += SVX_NUM_ROMAN_UPPER;
849 else
851 eTmpScan = LOWER_ROMAN;
852 c += SVX_NUM_ROMAN_LOWER;
855 eScan = (eScan & ~(UPPER_ALPHA|LOWER_ALPHA)) | eTmpScan;
856 if( pNumTypes )
857 (*pNumTypes) = pNumTypes->replaceAt( pNumTypes->getLength() - 1, 1, OUString(c) );
860 if( eScan & DELIM )
862 if( eScan & CHG ) // not if it starts with a number
864 ++nDigitLvl;
865 if( pPostfix )
866 *pPostfix += "\x01";
869 if( pNumTypes )
870 *pNumTypes += OUStringChar(cNumTyp);
871 eScan = eScan | CHG;
873 else if( pNumTypes && !(eScan & eTmpScan) )
874 *pNumTypes += OUStringChar(cNumTyp);
876 eScan &= ~DELIM; // remove Delim
878 // if another type is set, stop here
879 if( 0 != ( eScan & ~CHG ) && eTmpScan != ( eScan & ~CHG ))
880 return USHRT_MAX;
882 if( eTmpScan & (UPPER_ALPHA | LOWER_ALPHA) )
884 // allow characters only if they appear once
885 return USHRT_MAX;
887 else
889 // roman numbers, check if valid characters
890 sal_uInt16 nVal;
891 bool bError = false;
892 switch( cLow )
894 case 'm': nVal = 1000; goto CHECK_ROMAN_1;
895 case 'd': nVal = 500; goto CHECK_ROMAN_5;
896 case 'c': nVal = 100; goto CHECK_ROMAN_1;
897 case 'l': nVal = 50; goto CHECK_ROMAN_5;
898 case 'x': nVal = 10; goto CHECK_ROMAN_1;
899 case 'v': nVal = 5; goto CHECK_ROMAN_5;
901 CHECK_ROMAN_1:
903 int nMod5 = nStart % (nVal * 5);
904 int nLast = nStart % nVal;
905 int n10 = nVal / 10;
907 if( nMod5 == ((3 * nVal) + n10 ) ||
908 nMod5 == ((4 * nVal) + n10 ) ||
909 nLast == n10 )
910 nStart = static_cast<sal_uInt16>(nStart + (n10 * 8));
911 else if( nMod5 == 0 ||
912 nMod5 == (1 * nVal) ||
913 nMod5 == (2 * nVal) )
914 nStart = nStart + nVal;
915 else
916 bError = true;
918 break;
920 CHECK_ROMAN_5:
922 if( ( nStart / nVal ) & 1 )
923 bError = true;
924 else
926 int nMod = nStart % nVal;
927 int n10 = nVal / 5;
928 if( n10 == nMod )
929 nStart = static_cast<sal_uInt16>(nStart + (3 * n10));
930 else if( 0 == nMod )
931 nStart = nStart + nVal;
932 else
933 bError = true;
936 break;
938 case 'i':
939 if( nStart % 5 >= 3 )
940 bError = true;
941 else
942 nStart += 1;
943 break;
945 default:
946 bError = true;
949 if( bError )
950 return USHRT_MAX;
952 eScan |= eTmpScan; // add Digit
953 ++nDigitCnt;
955 else if( (256 > cCurrentChar &&
956 strchr( ".)(", cCurrentChar )) ||
957 0x3002 == cCurrentChar /* Chinese trad. dot */||
958 0xff0e == cCurrentChar /* Japanese dot */||
959 0xFF08 == cCurrentChar /* opening bracket Chin./Jap.*/||
960 0xFF09 == cCurrentChar )/* closing bracket Chin./Jap. */
962 if(cCurrentChar == '(' || cCurrentChar == 0xFF09)
963 nOpeningParentheses++;
964 else if(cCurrentChar == ')'|| cCurrentChar == 0xFF08)
965 nClosingParentheses++;
966 // only if no numbers were read until here
967 if( pPrefix && !( eScan & ( NO_DELIM | CHG )) )
968 *pPrefix += OUStringChar(rText[nPos]);
969 else if( pPostfix )
970 *pPostfix += OUStringChar(rText[nPos]);
972 if( NO_DELIM & eScan )
974 eScan |= CHG;
975 if( pPrefix )
976 *pPrefix += "\x01" + OUString::number( nStart );
978 eScan &= ~NO_DELIM; // remove Delim
979 eScan |= DELIM; // add Digit
980 nDigitCnt = 0;
981 nStart = 0;
983 else
984 break;
985 ++nPos;
987 if (!( CHG & eScan ) || rPos == TextFrameIndex(nPos) ||
988 nPos == rText.getLength() || !IsSpace(rText[nPos]) ||
989 (nOpeningParentheses > nClosingParentheses))
990 return USHRT_MAX;
992 if( (NO_DELIM & eScan) && pPrefix ) // do not forget the last one
993 *pPrefix += "\x01" + OUString::number( nStart );
995 rPos = TextFrameIndex(nPos);
996 return nDigitLvl; // 0 .. 9 (MAXLEVEL - 1)
999 void SwAutoFormat::SetColl( sal_uInt16 nId, bool bHdLineOrText )
1001 m_aDelPam.DeleteMark();
1002 m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeForParaProps();
1003 m_aDelPam.GetPoint()->nContent.Assign(m_aDelPam.GetPoint()->nNode.GetNode().GetContentNode(), 0);
1005 // keep hard tabs, alignment, language, hyphenation, DropCaps and nearly all frame attributes
1006 SfxItemSet aSet(
1007 m_pDoc->GetAttrPool(),
1008 svl::Items<
1009 RES_CHRATR_LANGUAGE, RES_CHRATR_LANGUAGE,
1010 RES_PARATR_ADJUST, RES_PARATR_ADJUST,
1011 RES_PARATR_TABSTOP, RES_PARATR_DROP,
1012 RES_BACKGROUND, RES_SHADOW>{});
1014 if (m_aDelPam.GetPoint()->nNode.GetNode().GetTextNode()->HasSwAttrSet())
1016 aSet.Put(*m_aDelPam.GetPoint()->nNode.GetNode().GetTextNode()->GetpSwAttrSet());
1017 // take HeaderLine/TextBody only if centered or right aligned, otherwise only justification
1018 SvxAdjustItem const * pAdj;
1019 if( SfxItemState::SET == aSet.GetItemState( RES_PARATR_ADJUST,
1020 false, reinterpret_cast<const SfxPoolItem**>(&pAdj) ))
1022 SvxAdjust eAdj = pAdj->GetAdjust();
1023 if( bHdLineOrText ? (SvxAdjust::Right != eAdj &&
1024 SvxAdjust::Center != eAdj)
1025 : SvxAdjust::Block != eAdj )
1026 aSet.ClearItem( RES_PARATR_ADJUST );
1030 m_pDoc->SetTextFormatCollByAutoFormat( *m_aDelPam.GetPoint(), nId, &aSet );
1033 static bool HasSelBlanks(
1034 SwTextFrame const*const pStartFrame, TextFrameIndex & rStartIndex,
1035 SwTextFrame const*const pEndFrame, TextFrameIndex & rEndIndex)
1037 if (TextFrameIndex(0) < rEndIndex
1038 && rEndIndex < TextFrameIndex(pEndFrame->GetText().getLength())
1039 && ' ' == pEndFrame->GetText()[sal_Int32(rEndIndex) - 1])
1041 --rEndIndex;
1042 return true;
1044 if (rStartIndex < TextFrameIndex(pStartFrame->GetText().getLength())
1045 && ' ' == pStartFrame->GetText()[sal_Int32(rStartIndex)])
1047 ++rStartIndex;
1048 return true;
1050 return false;
1053 bool SwAutoFormat::HasBreakAttr(const SwTextFrame& rTextFrame)
1055 const SfxItemSet *const pSet = rTextFrame.GetTextNodeFirst()->GetpSwAttrSet();
1056 if( !pSet )
1057 return false;
1059 const SfxPoolItem* pItem;
1060 if( SfxItemState::SET == pSet->GetItemState( RES_BREAK, false, &pItem )
1061 && SvxBreak::NONE != static_cast<const SvxFormatBreakItem*>(pItem)->GetBreak() )
1062 return true;
1064 if( SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, false, &pItem )
1065 && static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc()
1066 && UseOnPage::NONE != static_cast<const SwFormatPageDesc*>(pItem)->GetPageDesc()->GetUseOn() )
1067 return true;
1068 return false;
1071 /// Is there a dot at the end?
1072 bool SwAutoFormat::IsSentenceAtEnd(const SwTextFrame & rTextFrame)
1074 const OUString& rStr = rTextFrame.GetText();
1075 sal_Int32 n = rStr.getLength();
1076 if( !n )
1077 return true;
1079 while( --n && IsSpace( rStr[ n ] ) )
1081 return '.' == rStr[ n ];
1084 /// Delete beginning and/or end in a node
1085 void SwAutoFormat::DeleteLeadingTrailingBlanks(bool bStart, bool bEnd)
1087 if( !(m_aFlags.bAFormatByInput
1088 ? m_aFlags.bAFormatByInpDelSpacesAtSttEnd
1089 : m_aFlags.bAFormatDelSpacesAtSttEnd) )
1090 return;
1092 // delete blanks at the end of the current and at the beginning of the next one
1093 m_aDelPam.DeleteMark();
1094 TextFrameIndex nPos(GetLeadingBlanks(m_pCurTextFrame->GetText()));
1095 if (bStart && TextFrameIndex(0) != nPos)
1097 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
1098 m_aDelPam.SetMark();
1099 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
1100 DeleteSel( m_aDelPam );
1101 m_aDelPam.DeleteMark();
1103 nPos = TextFrameIndex(GetTrailingBlanks(m_pCurTextFrame->GetText()));
1104 if (bEnd && TextFrameIndex(m_pCurTextFrame->GetText().getLength()) != nPos)
1106 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
1107 TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
1108 m_aDelPam.SetMark();
1109 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
1110 DeleteSel( m_aDelPam );
1111 m_aDelPam.DeleteMark();
1115 namespace sw {
1117 bool GetRanges(std::vector<std::shared_ptr<SwUnoCursor>> & rRanges,
1118 SwDoc & rDoc, SwPaM const& rDelPam)
1120 bool isNoRedline(true);
1121 SwRedlineTable::size_type tmp;
1122 IDocumentRedlineAccess const& rIDRA(rDoc.getIDocumentRedlineAccess());
1123 if (!(rIDRA.GetRedlineFlags() & RedlineFlags::ShowDelete))
1125 return isNoRedline;
1127 rIDRA.GetRedline(*rDelPam.Start(), &tmp);
1128 SwPosition const* pCurrent(rDelPam.Start());
1129 for ( ; tmp < rIDRA.GetRedlineTable().size(); ++tmp)
1131 SwRangeRedline const*const pRedline(rIDRA.GetRedlineTable()[tmp]);
1132 if (*rDelPam.End() <= *pRedline->Start())
1134 break;
1136 if (*pRedline->End() <= *rDelPam.Start())
1138 continue;
1140 if (pRedline->GetType() == RedlineType::Delete)
1142 assert(*pRedline->Start() != *pRedline->End());
1143 isNoRedline = false;
1144 if (*pCurrent < *pRedline->Start())
1146 rRanges.push_back(rDoc.CreateUnoCursor(*pCurrent));
1147 rRanges.back()->SetMark();
1148 *rRanges.back()->GetPoint() = *pRedline->Start();
1150 pCurrent = pRedline->End();
1153 if (!isNoRedline && *pCurrent < *rDelPam.End())
1155 rRanges.push_back(rDoc.CreateUnoCursor(*pCurrent));
1156 rRanges.back()->SetMark();
1157 *rRanges.back()->GetPoint() = *rDelPam.End();
1159 return isNoRedline;
1162 } // namespace sw
1164 void SwAutoFormat::DeleteSel(SwPaM & rDelPam)
1166 std::vector<std::shared_ptr<SwUnoCursor>> ranges; // need correcting cursor
1167 if (GetRanges(ranges, *m_pDoc, rDelPam))
1169 DeleteSelImpl(rDelPam, rDelPam);
1171 else
1173 for (auto const& pCursor : ranges)
1175 DeleteSelImpl(*pCursor, rDelPam);
1180 void SwAutoFormat::DeleteSelImpl(SwPaM & rDelPam, SwPaM & rPamToCorrect)
1182 if (m_aFlags.bWithRedlining || &rDelPam != &rPamToCorrect)
1184 // Add to Shell-Cursor-Ring so that DelPam will be moved as well!
1185 SwPaM* pShCursor = m_pEditShell->GetCursor_();
1186 SwPaM aTmp( *m_pCurTextNd, 0, pShCursor );
1188 SwPaM* pPrev = rPamToCorrect.GetPrev();
1189 rPamToCorrect.GetRingContainer().merge( pShCursor->GetRingContainer() );
1191 m_pEditShell->DeleteSel( rDelPam );
1193 // and remove Pam again:
1194 SwPaM* p;
1195 SwPaM* pNext = &rPamToCorrect;
1196 do {
1197 p = pNext;
1198 pNext = p->GetNext();
1199 p->MoveTo( &rPamToCorrect );
1200 } while( p != pPrev );
1202 m_aNdIdx = aTmp.GetPoint()->nNode;
1203 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
1204 m_pCurTextFrame = GetFrame(*m_pCurTextNd); // keep it up to date
1206 else
1207 m_pEditShell->DeleteSel( rDelPam );
1210 bool SwAutoFormat::DeleteJoinCurNextPara(SwTextFrame const*const pNextFrame,
1211 bool const bIgnoreLeadingBlanks)
1213 // delete blanks at the end of the current and at the beginning of the next one
1214 m_aDelPam.DeleteMark();
1215 TextFrameIndex nTrailingPos(GetTrailingBlanks(m_pCurTextFrame->GetText()));
1217 SwTextFrame const*const pEndFrame(pNextFrame ? pNextFrame : m_pCurTextFrame);
1218 TextFrameIndex nLeadingPos(0);
1219 if (pNextFrame)
1221 nLeadingPos = TextFrameIndex(
1222 bIgnoreLeadingBlanks ? 0 : GetLeadingBlanks(pNextFrame->GetText()));
1224 else
1226 nLeadingPos = TextFrameIndex(m_pCurTextFrame->GetText().getLength());
1229 // Is there a Blank at the beginning or end?
1230 // Do not delete it, it will be inserted again.
1231 bool bHasBlnks = HasSelBlanks(m_pCurTextFrame, nTrailingPos, pEndFrame, nLeadingPos);
1233 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nTrailingPos);
1234 m_aDelPam.SetMark();
1235 *m_aDelPam.GetPoint() = pEndFrame->MapViewToModelPos(nLeadingPos);
1237 if( *m_aDelPam.GetPoint() != *m_aDelPam.GetMark() )
1238 DeleteSel( m_aDelPam );
1239 m_aDelPam.DeleteMark();
1240 // note: keep m_aDelPam point at insert pos. for clients
1242 return !bHasBlnks;
1245 void SwAutoFormat::DelEmptyLine( bool bTstNextPara )
1247 SetRedlineText( STR_AUTOFMTREDL_DEL_EMPTY_PARA );
1248 // delete blanks in empty paragraph
1249 m_aDelPam.DeleteMark();
1250 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
1251 TextFrameIndex(0));
1252 m_aDelPam.SetMark();
1254 m_aDelPam.GetMark()->nNode = m_pCurTextFrame->GetTextNodeFirst()->GetIndex() - 1;
1255 SwTextNode* pTNd = m_aDelPam.GetNode( false ).GetTextNode();
1256 if( pTNd )
1257 // first use the previous text node
1258 m_aDelPam.GetMark()->nContent.Assign(pTNd, pTNd->GetText().getLength());
1259 else if( bTstNextPara )
1261 // then try the next (at the beginning of a Doc, table cells, frames, ...)
1262 m_aDelPam.GetMark()->nNode = (m_pCurTextFrame->GetMergedPara()
1263 ? m_pCurTextFrame->GetMergedPara()->pLastNode
1264 : m_pCurTextNd
1265 )->GetIndex() + 1;
1266 pTNd = m_aDelPam.GetNode( false ).GetTextNode();
1267 if( pTNd )
1269 m_aDelPam.GetMark()->nContent.Assign( pTNd, 0 );
1270 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
1271 TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
1274 if( pTNd )
1275 { // join with previous or next paragraph
1276 DeleteSel(m_aDelPam);
1278 assert(m_aDelPam.GetNode().IsTextNode());
1279 assert(!m_aDelPam.HasMark());
1280 m_aDelPam.SetMark(); // mark remains at join position
1281 m_pCurTextFrame = GetFrame(*m_aDelPam.GetNode().GetTextNode());
1282 // replace until the end of the merged paragraph
1283 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
1284 TextFrameIndex(m_pCurTextFrame->GetText().getLength()));
1285 if (*m_aDelPam.GetPoint() != *m_aDelPam.GetMark())
1286 { // tdf#137245 replace (not delete) to preserve any flys
1287 m_pDoc->getIDocumentContentOperations().ReplaceRange(m_aDelPam, "", false);
1290 m_aDelPam.DeleteMark();
1291 ClearRedlineText();
1292 // note: this likely has deleted m_pCurTextFrame - update it...
1293 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
1294 m_pCurTextFrame = m_pCurTextNd ? GetFrame( *m_pCurTextNd ) : nullptr;
1297 void SwAutoFormat::DelMoreLinesBlanks( bool bWithLineBreaks )
1299 if( !(m_aFlags.bAFormatByInput
1300 ? m_aFlags.bAFormatByInpDelSpacesBetweenLines
1301 : m_aFlags.bAFormatDelSpacesBetweenLines) )
1302 return;
1304 // delete all blanks on the left and right of the indentation
1305 m_aDelPam.DeleteMark();
1307 SwTextFrameInfo aFInfo( m_pCurTextFrame );
1308 std::vector<std::pair<TextFrameIndex, TextFrameIndex>> spaces;
1309 aFInfo.GetSpaces(spaces, !m_aFlags.bAFormatByInput || bWithLineBreaks);
1311 // tdf#123285 iterate backwards - delete invalidates following indexes
1312 for (auto iter = spaces.rbegin(); iter != spaces.rend(); ++iter)
1314 auto & rSpaceRange(*iter);
1315 assert(rSpaceRange.first != rSpaceRange.second);
1316 bool const bHasBlanks = HasSelBlanks(
1317 m_pCurTextFrame, rSpaceRange.first,
1318 m_pCurTextFrame, rSpaceRange.second);
1319 if (rSpaceRange.first != rSpaceRange.second)
1321 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(rSpaceRange.first);
1322 m_aDelPam.SetMark();
1323 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(rSpaceRange.second);
1324 DeleteSel(m_aDelPam);
1325 if (!bHasBlanks)
1327 m_pDoc->getIDocumentContentOperations().InsertString(m_aDelPam, OUString(' '));
1329 m_aDelPam.DeleteMark();
1334 void SwAutoFormat::JoinPrevPara()
1336 m_aDelPam.DeleteMark();
1337 m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeFirst();
1338 m_aDelPam.GetPoint()->nContent.Assign(m_pCurTextFrame->GetTextNodeFirst(), 0);
1339 m_aDelPam.SetMark();
1341 --m_aDelPam.GetPoint()->nNode;
1342 SwTextNode* pTNd = m_aDelPam.GetNode().GetTextNode();
1343 if( pTNd )
1345 // use the previous text node first
1346 m_aDelPam.GetPoint()->nContent.Assign(pTNd, pTNd->GetText().getLength());
1347 DeleteSel( m_aDelPam );
1349 m_aDelPam.DeleteMark();
1352 void SwAutoFormat::BuildIndent()
1354 SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_INDENT );
1356 // read all succeeding paragraphs that belong to this indentation
1357 bool bBreak = true;
1358 if( m_bMoreLines )
1359 DelMoreLinesBlanks( true );
1360 else
1361 bBreak = !IsFastFullLine(*m_pCurTextFrame)
1362 || IsBlanksInString(*m_pCurTextFrame)
1363 || IsSentenceAtEnd(*m_pCurTextFrame);
1364 SetColl( RES_POOLCOLL_TEXT_IDENT );
1365 if( !bBreak )
1367 SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
1368 const SwTextFrame * pNextFrame = GetNextNode();
1369 if (pNextFrame && !m_bEnd)
1371 do {
1372 bBreak = !IsFastFullLine(*pNextFrame)
1373 || IsBlanksInString(*pNextFrame)
1374 || IsSentenceAtEnd(*pNextFrame);
1375 if (DeleteJoinCurNextPara(pNextFrame))
1377 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
1379 if( bBreak )
1380 break;
1381 pNextFrame = GetNextNode();
1383 while (CanJoin(pNextFrame)
1384 && !CalcLevel(*pNextFrame));
1387 DeleteLeadingTrailingBlanks();
1388 AutoCorrect();
1391 void SwAutoFormat::BuildTextIndent()
1393 SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_TEXT_INDENT);
1394 // read all succeeding paragraphs that belong to this indentation
1395 bool bBreak = true;
1396 if( m_bMoreLines )
1397 DelMoreLinesBlanks( true );
1398 else
1399 bBreak = !IsFastFullLine(*m_pCurTextFrame)
1400 || IsBlanksInString(*m_pCurTextFrame)
1401 || IsSentenceAtEnd(*m_pCurTextFrame);
1403 if( m_aFlags.bAFormatByInput )
1405 const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAutoFormatLvl(
1406 static_cast<sal_uInt8>(CalcLevel(*m_pCurTextFrame)));
1409 SetColl( RES_POOLCOLL_TEXT_MOVE );
1410 if( !bBreak )
1412 SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
1413 const SwTextFrame * pNextFrame = GetNextNode();
1414 while (CanJoin(pNextFrame) &&
1415 CalcLevel(*pNextFrame))
1417 bBreak = !IsFastFullLine(*pNextFrame)
1418 || IsBlanksInString(*pNextFrame)
1419 || IsSentenceAtEnd(*pNextFrame);
1420 if (DeleteJoinCurNextPara(pNextFrame))
1422 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
1424 if( bBreak )
1425 break;
1426 pNextFrame = GetNextNode();
1429 DeleteLeadingTrailingBlanks();
1430 AutoCorrect();
1433 void SwAutoFormat::BuildText()
1435 SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_TEXT );
1436 // read all succeeding paragraphs that belong to this text without indentation
1437 bool bBreak = true;
1438 if( m_bMoreLines )
1439 DelMoreLinesBlanks();
1440 else
1441 bBreak = !IsFastFullLine(*m_pCurTextFrame)
1442 || IsBlanksInString(*m_pCurTextFrame)
1443 || IsSentenceAtEnd(*m_pCurTextFrame);
1444 SetColl( RES_POOLCOLL_TEXT, true );
1445 if( !bBreak )
1447 SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
1448 const SwTextFrame * pNextFrame = GetNextNode();
1449 while (CanJoin(pNextFrame) &&
1450 !CalcLevel(*pNextFrame))
1452 bBreak = !IsFastFullLine(*pNextFrame)
1453 || IsBlanksInString(*pNextFrame)
1454 || IsSentenceAtEnd(*pNextFrame);
1455 if (DeleteJoinCurNextPara(pNextFrame))
1457 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
1459 if( bBreak )
1460 break;
1461 const SwTextFrame *const pCurrNode = pNextFrame;
1462 pNextFrame = GetNextNode();
1463 if (!pNextFrame || pCurrNode == pNextFrame)
1464 break;
1467 DeleteLeadingTrailingBlanks();
1468 AutoCorrect();
1471 void SwAutoFormat::BuildEnum( sal_uInt16 nLvl, sal_uInt16 nDigitLevel )
1473 SetRedlineText( STR_AUTOFMTREDL_SET_NUMBER_BULLET );
1475 bool bBreak = true;
1477 // first, determine current indentation and frame width
1478 SwTwips nFrameWidth = m_pCurTextFrame->getFramePrintArea().Width();
1479 SwTwips nLeftTextPos;
1481 TextFrameIndex nPos(0);
1482 while (nPos < TextFrameIndex(m_pCurTextFrame->GetText().getLength())
1483 && IsSpace(m_pCurTextFrame->GetText()[sal_Int32(nPos)]))
1485 ++nPos;
1488 SwTextFrameInfo aInfo( m_pCurTextFrame );
1489 nLeftTextPos = aInfo.GetCharPos(nPos);
1490 nLeftTextPos -= m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetLRSpace().GetLeft();
1493 if( m_bMoreLines )
1494 DelMoreLinesBlanks();
1495 else
1496 bBreak = !IsFastFullLine(*m_pCurTextFrame)
1497 || IsBlanksInString(*m_pCurTextFrame)
1498 || IsSentenceAtEnd(*m_pCurTextFrame);
1499 bool bRTL = m_pEditShell->IsInRightToLeftText();
1500 DeleteLeadingTrailingBlanks();
1502 bool bChgBullet = false, bChgEnum = false;
1503 TextFrameIndex nAutoCorrPos(0);
1505 // if numbering is set, get the current one
1506 SwNumRule aRule( m_pDoc->GetUniqueNumRuleName(),
1507 // #i89178#
1508 numfunc::GetDefaultPositionAndSpaceMode() );
1510 const SwNumRule* pCur = nullptr;
1511 if (m_aFlags.bSetNumRule)
1513 pCur = m_pCurTextFrame->GetTextNodeForParaProps()->GetNumRule();
1514 if (pCur)
1516 aRule = *pCur;
1520 // replace bullet character with defined one
1521 const OUString& rStr = m_pCurTextFrame->GetText();
1522 TextFrameIndex nTextStt(0);
1523 const sal_Unicode* pFndBulletChr = nullptr;
1524 if (m_aFlags.bChgEnumNum && 2 < rStr.getLength())
1525 pFndBulletChr = StrChr(pBulletChar, rStr[sal_Int32(nTextStt)]);
1526 if (nullptr != pFndBulletChr && IsSpace(rStr[sal_Int32(nTextStt) + 1]))
1528 if( m_aFlags.bAFormatByInput )
1530 if( m_aFlags.bSetNumRule )
1532 SwCharFormat* pCFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
1533 RES_POOLCHR_BULLET_LEVEL );
1534 bChgBullet = true;
1535 // Was the format already somewhere adjusted?
1536 if( !aRule.GetNumFormat( nLvl ) )
1538 int nBulletPos = pFndBulletChr - pBulletChar;
1539 sal_UCS4 cBullChar;
1540 const vcl::Font* pBullFnt( nullptr );
1541 if( nBulletPos < cnPosEnDash )
1543 cBullChar = m_aFlags.cBullet;
1544 pBullFnt = &m_aFlags.aBulletFont;
1546 else
1548 cBullChar = nBulletPos < cnPosEmDash
1549 ? cStarSymbolEnDash
1550 : cStarSymbolEmDash;
1551 // #i63395#
1552 // Only apply user defined default bullet font
1553 if ( numfunc::IsDefBulletFontUserDefined() )
1555 pBullFnt = &numfunc::GetDefBulletFont();
1559 sal_Int32 nAbsPos = lBulletIndent;
1560 SwTwips nSpaceSteps = nLvl
1561 ? nLeftTextPos / nLvl
1562 : lBulletIndent;
1563 for( sal_uInt8 n = 0; n < MAXLEVEL; ++n, nAbsPos = nAbsPos + nSpaceSteps )
1565 SwNumFormat aFormat( aRule.Get( n ) );
1566 aFormat.SetBulletFont( pBullFnt );
1567 aFormat.SetBulletChar( cBullChar );
1568 aFormat.SetNumberingType(SVX_NUM_CHAR_SPECIAL);
1569 // #i93908# clear suffix for bullet lists
1570 aFormat.SetPrefix(OUString());
1571 aFormat.SetSuffix(OUString());
1572 aFormat.SetFirstLineOffset( lBulletFirstLineOffset );
1573 aFormat.SetAbsLSpace( nAbsPos );
1574 if( !aFormat.GetCharFormat() )
1575 aFormat.SetCharFormat( pCFormat );
1576 if( bRTL )
1577 aFormat.SetNumAdjust( SvxAdjust::Right );
1579 aRule.Set( n, aFormat );
1581 if( n == nLvl &&
1582 nFrameWidth < ( nSpaceSteps * MAXLEVEL ) )
1583 nSpaceSteps = ( nFrameWidth - nLeftTextPos ) /
1584 ( MAXLEVEL - nLvl );
1589 else
1591 bChgBullet = true;
1592 SetColl( static_cast<sal_uInt16>(RES_POOLCOLL_BULLET_LEVEL1 + ( std::min( nLvl, cnNumBullColls ) * 4 )) );
1595 else
1597 // Then it is a numbering
1599 //JP 21.11.97: The NumLevel is either the DigitLevel or, if the latter is not existent or 0,
1600 // it is determined by the indentation level.
1602 OUString aPostfix, aPrefix, aNumTypes;
1603 nDigitLevel = GetDigitLevel(*m_pCurTextFrame, nTextStt,
1604 &aPrefix, &aPostfix, &aNumTypes);
1605 if (USHRT_MAX != nDigitLevel)
1607 bChgEnum = true;
1609 // Level 0 and Indentation, determine level by left indentation and default NumIndent
1610 if( !nDigitLevel && nLeftTextPos )
1611 nLvl = std::min( sal_uInt16( nLeftTextPos / lNumberIndent ),
1612 sal_uInt16( MAXLEVEL - 1 ) );
1613 else
1614 nLvl = nDigitLevel;
1617 if( bChgEnum && m_aFlags.bSetNumRule )
1619 if( !pCur ) // adjust NumRule if it is new
1621 SwCharFormat* pCFormat = m_pDoc->getIDocumentStylePoolAccess().GetCharFormatFromPool(
1622 RES_POOLCHR_NUM_LEVEL );
1624 sal_Int32 nPrefixIdx{ 0 };
1625 if( !nDigitLevel )
1627 SwNumFormat aFormat( aRule.Get( nLvl ) );
1628 aFormat.SetPrefix( aPrefix.getToken( 0, u'\x0001', nPrefixIdx ));
1629 aFormat.SetStart( static_cast<sal_uInt16>(aPrefix.getToken( 0, u'\x0001', nPrefixIdx ).toInt32()));
1630 aFormat.SetSuffix( aPostfix.getToken( 0, u'\x0001' ));
1631 aFormat.SetIncludeUpperLevels( 0 );
1633 if( !aFormat.GetCharFormat() )
1634 aFormat.SetCharFormat( pCFormat );
1636 if( !aNumTypes.isEmpty() )
1637 aFormat.SetNumberingType(static_cast<SvxNumType>(aNumTypes[ 0 ] - '0'));
1639 if( bRTL )
1640 aFormat.SetNumAdjust( SvxAdjust::Right );
1641 aRule.Set( nLvl, aFormat );
1643 else
1645 auto const nSpaceSteps = nLvl ? nLeftTextPos / nLvl : 0;
1646 sal_uInt16 n;
1647 sal_Int32 nPostfixIdx{ 0 };
1648 for( n = 0; n <= nLvl; ++n )
1650 SwNumFormat aFormat( aRule.Get( n ) );
1652 if( !n )
1653 aFormat.SetPrefix( aPrefix.getToken( 0, u'\x0001', nPrefixIdx )); // token 0, read only on first loop
1654 aFormat.SetStart( static_cast<sal_uInt16>(aPrefix.getToken( 0, u'\x0001', nPrefixIdx ).toInt32() ));
1655 aFormat.SetSuffix( aPostfix.getToken( 0, u'\x0001', nPostfixIdx ));
1656 aFormat.SetIncludeUpperLevels( MAXLEVEL );
1657 if( n < aNumTypes.getLength() )
1658 aFormat.SetNumberingType(static_cast<SvxNumType>(aNumTypes[ n ] - '0'));
1660 aFormat.SetAbsLSpace( nSpaceSteps * n
1661 + lNumberIndent );
1663 if( !aFormat.GetCharFormat() )
1664 aFormat.SetCharFormat( pCFormat );
1665 if( bRTL )
1666 aFormat.SetNumAdjust( SvxAdjust::Right );
1668 aRule.Set( n, aFormat );
1671 // Does it fit completely into the frame?
1672 bool bDefStep = nFrameWidth < (nSpaceSteps * MAXLEVEL);
1673 for( ; n < MAXLEVEL; ++n )
1675 SwNumFormat aFormat( aRule.Get( n ) );
1676 aFormat.SetIncludeUpperLevels( MAXLEVEL );
1677 if( bDefStep )
1678 aFormat.SetAbsLSpace( nLeftTextPos +
1679 SwNumRule::GetNumIndent(static_cast<sal_uInt8>(n-nLvl)));
1680 else
1681 aFormat.SetAbsLSpace( nSpaceSteps * n
1682 + lNumberIndent );
1683 aRule.Set( n, aFormat );
1688 else if( !m_aFlags.bAFormatByInput )
1689 SetColl( static_cast<sal_uInt16>(RES_POOLCOLL_NUM_LEVEL1 + ( std::min( nLvl, cnNumBullColls ) * 4 ) ));
1690 else
1691 bChgEnum = false;
1694 if ( bChgEnum || bChgBullet )
1696 m_aDelPam.DeleteMark();
1697 m_aDelPam.GetPoint()->nNode = *m_pCurTextFrame->GetTextNodeForParaProps();
1699 if( m_aFlags.bSetNumRule )
1701 if( m_aFlags.bAFormatByInput )
1703 m_aDelPam.SetMark();
1704 SwTextFrame const*const pNextFrame = GetNextNode(false);
1705 assert(pNextFrame);
1706 m_aDelPam.GetMark()->nNode = *pNextFrame->GetTextNodeForParaProps();
1707 m_aDelPam.GetNode(false).GetTextNode()->SetAttrListLevel( nLvl );
1710 const_cast<SwTextNode*>(m_pCurTextFrame->GetTextNodeForParaProps())->SetAttrListLevel(nLvl);
1712 // start new list
1713 m_pDoc->SetNumRule(m_aDelPam, aRule, true, m_pEditShell->GetLayout());
1714 m_aDelPam.DeleteMark();
1716 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
1718 else
1720 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(
1721 bChgEnum ? nTextStt : TextFrameIndex(0));
1723 m_aDelPam.SetMark();
1725 if ( bChgBullet )
1726 nTextStt += TextFrameIndex(2);
1728 while (nTextStt < TextFrameIndex(rStr.getLength()) && IsSpace(rStr[sal_Int32(nTextStt)]))
1729 nTextStt++;
1731 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nTextStt);
1732 DeleteSel( m_aDelPam );
1734 if( !m_aFlags.bSetNumRule )
1736 OUString sChgStr('\t');
1737 if( bChgBullet )
1738 sChgStr = OUString(&m_aFlags.cBullet, 1) + sChgStr;
1739 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, sChgStr );
1741 SfxItemSet aSet( m_pDoc->GetAttrPool(), aTextNodeSetRange );
1742 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
1743 assert(&m_aDelPam.GetPoint()->nNode.GetNode() == m_pCurTextFrame->GetTextNodeForParaProps());
1744 if( bChgBullet )
1746 m_aDelPam.SetMark();
1747 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(1));
1748 SetAllScriptItem( aSet,
1749 SvxFontItem( m_aFlags.aBulletFont.GetFamilyType(),
1750 m_aFlags.aBulletFont.GetFamilyName(),
1751 m_aFlags.aBulletFont.GetStyleName(),
1752 m_aFlags.aBulletFont.GetPitch(),
1753 m_aFlags.aBulletFont.GetCharSet(),
1754 RES_CHRATR_FONT ) );
1755 m_pDoc->SetFormatItemByAutoFormat( m_aDelPam, aSet );
1756 m_aDelPam.DeleteMark();
1757 nAutoCorrPos = TextFrameIndex(2);
1758 aSet.ClearItem();
1760 SvxTabStopItem aTStops( RES_PARATR_TABSTOP );
1761 aTStops.Insert( SvxTabStop( 0 ) );
1762 aSet.Put( aTStops );
1763 assert(&m_aDelPam.GetPoint()->nNode.GetNode() == m_pCurTextFrame->GetTextNodeForParaProps());
1764 m_pDoc->SetFormatItemByAutoFormat( m_aDelPam, aSet );
1768 if( bBreak )
1770 AutoCorrect( nAutoCorrPos ); /* Offset due to Bullet + Tab */
1771 return;
1774 const SwTextFrame * pNextFrame = GetNextNode();
1775 while (CanJoin(pNextFrame)
1776 && nLvl == CalcLevel(*pNextFrame))
1778 SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
1779 bBreak = !IsFastFullLine(*pNextFrame)
1780 || IsBlanksInString(*pNextFrame)
1781 || IsSentenceAtEnd(*pNextFrame);
1782 if (DeleteJoinCurNextPara(pNextFrame))
1784 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
1786 if( bBreak )
1787 break;
1788 const SwTextFrame *const pCurrNode = pNextFrame;
1789 pNextFrame = GetNextNode();
1790 if (!pNextFrame || pCurrNode == pNextFrame)
1791 break;
1793 DeleteLeadingTrailingBlanks( false );
1794 AutoCorrect( nAutoCorrPos );
1797 void SwAutoFormat::BuildNegIndent( SwTwips nSpaces )
1799 SetRedlineText( STR_AUTOFMTREDL_SET_TMPL_NEG_INDENT );
1800 // Test of contraposition (n words, divided by spaces/tabs, with same indentation in 2nd line)
1802 // read all succeeding paragraphs that belong to this enumeration
1803 bool bBreak = true;
1804 TextFrameIndex nSpacePos(0);
1805 const sal_Int32 nTextPos = GetBigIndent( nSpacePos );
1806 if( m_bMoreLines )
1807 DelMoreLinesBlanks( true );
1808 else
1809 bBreak = !IsFastFullLine(*m_pCurTextFrame)
1810 || (!nTextPos && IsBlanksInString(*m_pCurTextFrame))
1811 || IsSentenceAtEnd(*m_pCurTextFrame);
1813 SetColl( static_cast<sal_uInt16>( nTextPos
1814 ? RES_POOLCOLL_CONFRONTATION
1815 : RES_POOLCOLL_TEXT_NEGIDENT ) );
1817 if( nTextPos )
1819 const OUString& rStr = m_pCurTextFrame->GetText();
1820 bool bInsTab = true;
1822 if ('\t' == rStr[sal_Int32(nSpacePos) + 1]) // leave tab alone
1824 --nSpacePos;
1825 bInsTab = false;
1828 TextFrameIndex nSpaceStt = nSpacePos;
1829 while (nSpaceStt && IsSpace(rStr[sal_Int32(--nSpaceStt)]))
1831 ++nSpaceStt;
1833 if (bInsTab && '\t' == rStr[sal_Int32(nSpaceStt)]) // leave tab alone
1835 ++nSpaceStt;
1836 bInsTab = false;
1839 m_aDelPam.DeleteMark();
1840 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nSpacePos);
1842 // delete old Spaces, etc.
1843 if( nSpaceStt < nSpacePos )
1845 m_aDelPam.SetMark();
1846 *m_aDelPam.GetMark() = m_pCurTextFrame->MapViewToModelPos(nSpaceStt);
1847 DeleteSel( m_aDelPam );
1848 if( bInsTab )
1850 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString('\t') );
1855 if( !bBreak )
1857 SetRedlineText( STR_AUTOFMTREDL_DEL_MORELINES );
1858 SwTextFrameInfo aFInfo( m_pCurTextFrame );
1859 const SwTextFrame * pNextFrame = GetNextNode();
1860 while (CanJoin(pNextFrame) &&
1861 20 < std::abs( static_cast<tools::Long>(nSpaces - aFInfo.SetFrame(
1862 EnsureFormatted(*pNextFrame)).GetLineStart()) )
1865 bBreak = !IsFastFullLine(*pNextFrame)
1866 || IsBlanksInString(*pNextFrame)
1867 || IsSentenceAtEnd(*pNextFrame);
1868 if (DeleteJoinCurNextPara(pNextFrame))
1870 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(' ') );
1872 if( bBreak )
1873 break;
1874 pNextFrame = GetNextNode();
1877 DeleteLeadingTrailingBlanks();
1878 AutoCorrect();
1881 void SwAutoFormat::BuildHeadLine( sal_uInt16 nLvl )
1883 if( m_aFlags.bWithRedlining )
1885 OUString sText(SwViewShell::GetShellRes()->GetAutoFormatNameLst()[
1886 STR_AUTOFMTREDL_SET_TMPL_HEADLINE ] );
1887 sText = sText.replaceAll( "$(ARG1)", OUString::number( nLvl + 1 ) );
1888 m_pDoc->GetDocumentRedlineManager().SetAutoFormatRedlineComment( &sText );
1891 SetColl( static_cast<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + nLvl ), true );
1892 if( m_aFlags.bAFormatByInput )
1894 SwTextFormatColl& rNxtColl = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetNextTextFormatColl();
1896 JoinPrevPara();
1898 DeleteLeadingTrailingBlanks( true, false );
1899 const SwTextFrame* pNextFrame = GetNextNode(false);
1900 if (pNextFrame->GetNext())
1902 (void)DeleteJoinCurNextPara(pNextFrame, true);
1903 pNextFrame = GetNextNode(false);
1905 m_aDelPam.DeleteMark();
1906 m_aDelPam.GetPoint()->nNode = *pNextFrame->GetTextNodeForParaProps();
1907 m_aDelPam.GetPoint()->nContent.Assign( m_aDelPam.GetContentNode(), 0 );
1908 m_pDoc->SetTextFormatColl( m_aDelPam, &rNxtColl );
1910 else
1912 DeleteLeadingTrailingBlanks();
1913 AutoCorrect();
1917 /// Start autocorrection for the current TextNode
1918 void SwAutoFormat::AutoCorrect(TextFrameIndex nPos)
1920 SvxAutoCorrect* pATst = SvxAutoCorrCfg::Get().GetAutoCorrect();
1921 ACFlags aSvxFlags = pATst->GetFlags( );
1922 bool bReplaceQuote( aSvxFlags & ACFlags::ChgQuotes );
1923 bool bReplaceSglQuote( aSvxFlags & ACFlags::ChgSglQuotes );
1925 if( m_aFlags.bAFormatByInput ||
1926 (!m_aFlags.bAutoCorrect && !bReplaceQuote && !bReplaceSglQuote &&
1927 !m_aFlags.bCapitalStartSentence && !m_aFlags.bCapitalStartWord &&
1928 !m_aFlags.bChgOrdinalNumber && !m_aFlags.bTransliterateRTL &&
1929 !m_aFlags.bChgToEnEmDash && !m_aFlags.bSetINetAttr &&
1930 !m_aFlags.bChgWeightUnderl && !m_aFlags.bAddNonBrkSpace) )
1931 return;
1933 const OUString* pText = &m_pCurTextFrame->GetText();
1934 if (TextFrameIndex(pText->getLength()) <= nPos)
1935 return;
1937 bool bGetLanguage = m_aFlags.bChgOrdinalNumber || m_aFlags.bTransliterateRTL ||
1938 m_aFlags.bChgToEnEmDash || m_aFlags.bSetINetAttr ||
1939 m_aFlags.bCapitalStartWord || m_aFlags.bCapitalStartSentence ||
1940 m_aFlags.bAddNonBrkSpace;
1942 m_aDelPam.DeleteMark();
1943 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(TextFrameIndex(0));
1945 SwAutoCorrDoc aACorrDoc( *m_pEditShell, m_aDelPam );
1947 SwTextFrameInfo aFInfo( nullptr );
1949 TextFrameIndex nSttPos, nLastBlank = nPos;
1950 bool bFirst = m_aFlags.bCapitalStartSentence, bFirstSent = bFirst;
1951 sal_Unicode cChar = 0;
1952 bool bNbspRunNext = false;
1954 CharClass& rAppCC = GetAppCharClass();
1956 do {
1957 while (nPos < TextFrameIndex(pText->getLength())
1958 && IsSpace(cChar = (*pText)[sal_Int32(nPos)]))
1959 ++nPos;
1960 if (nPos == TextFrameIndex(pText->getLength()))
1961 break; // that's it
1963 if( ( ( bReplaceQuote && '\"' == cChar ) ||
1964 ( bReplaceSglQuote && '\'' == cChar ) ) &&
1965 (!nPos || ' ' == (*pText)[sal_Int32(nPos)-1]))
1968 // note: special case symbol fonts !!!
1969 if( !aFInfo.GetFrame() )
1970 aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
1971 if( !aFInfo.IsBullet( nPos ))
1973 SetRedlineText( STR_AUTOFMTREDL_TYPO );
1974 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
1975 bool bSetHardBlank = false;
1977 OUString sReplace( pATst->GetQuote( aACorrDoc,
1978 sal_Int32(nPos), cChar, true ));
1980 m_aDelPam.SetMark();
1981 m_aDelPam.GetPoint()->nContent = m_aDelPam.GetMark()->nContent.GetIndex() + 1;
1982 if( 2 == sReplace.getLength() && ' ' == sReplace[ 1 ])
1984 sReplace = sReplace.copy( 0, 1 );
1985 bSetHardBlank = true;
1987 m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
1989 if( m_aFlags.bWithRedlining )
1991 m_aNdIdx = m_aDelPam.GetPoint()->nNode;
1992 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
1993 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
1994 pText = &m_pCurTextFrame->GetText();
1995 m_aDelPam.SetMark();
1996 aFInfo.SetFrame( nullptr );
1999 nPos += TextFrameIndex(sReplace.getLength() - 1);
2000 m_aDelPam.DeleteMark();
2001 if( bSetHardBlank )
2003 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(CHAR_HARDBLANK) );
2004 ++nPos;
2009 bool bCallACorr = false;
2010 int bBreak = 0;
2011 if (nPos && IsSpace((*pText)[sal_Int32(nPos) - 1]))
2012 nLastBlank = nPos;
2013 for (nSttPos = nPos; !bBreak && nPos < TextFrameIndex(pText->getLength()); ++nPos)
2015 cChar = (*pText)[sal_Int32(nPos)];
2016 switch (cChar)
2018 case '\"':
2019 case '\'':
2020 if( ( cChar == '\"' && bReplaceQuote ) || ( cChar == '\'' && bReplaceSglQuote ) )
2022 // consider Symbolfonts!
2023 if( !aFInfo.GetFrame() )
2024 aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
2025 if( !aFInfo.IsBullet( nPos ))
2027 SetRedlineText( STR_AUTOFMTREDL_TYPO );
2028 bool bSetHardBlank = false;
2029 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
2030 OUString sReplace( pATst->GetQuote( aACorrDoc,
2031 sal_Int32(nPos), cChar, false) );
2033 if( 2 == sReplace.getLength() && ' ' == sReplace[ 0 ])
2035 sReplace = sReplace.copy( 1 );
2036 bSetHardBlank = true;
2039 m_aDelPam.SetMark();
2040 m_aDelPam.GetPoint()->nContent = m_aDelPam.GetMark()->nContent.GetIndex() + 1;
2041 m_pDoc->getIDocumentContentOperations().ReplaceRange( m_aDelPam, sReplace, false );
2043 if( m_aFlags.bWithRedlining )
2045 m_aNdIdx = m_aDelPam.GetPoint()->nNode;
2046 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
2047 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
2048 pText = &m_pCurTextFrame->GetText();
2049 m_aDelPam.SetMark();
2050 m_aDelPam.DeleteMark();
2051 aFInfo.SetFrame( nullptr );
2054 nPos += TextFrameIndex(sReplace.getLength() - 1);
2055 m_aDelPam.DeleteMark();
2057 if( bSetHardBlank )
2059 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
2060 m_pDoc->getIDocumentContentOperations().InsertString( m_aDelPam, OUString(CHAR_HARDBLANK) );
2061 ++nPos;
2062 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
2066 break;
2067 case '*':
2068 case '_':
2069 if( m_aFlags.bChgWeightUnderl )
2071 // consider Symbolfonts!
2072 if( !aFInfo.GetFrame() )
2073 aFInfo.SetFrame( GetFrame( *m_pCurTextNd ) );
2074 if( !aFInfo.IsBullet( nPos ))
2076 SetRedlineText( '*' == cChar
2077 ? STR_AUTOFMTREDL_BOLD
2078 : STR_AUTOFMTREDL_UNDER );
2080 sal_Unicode cBlank = nSttPos ? (*pText)[sal_Int32(nSttPos) - 1] : 0;
2081 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
2083 if (pATst->FnChgWeightUnderl(aACorrDoc, *pText, sal_Int32(nPos)))
2085 if( m_aFlags.bWithRedlining )
2087 m_aNdIdx = m_aDelPam.GetPoint()->nNode;
2088 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
2089 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
2090 pText = &m_pCurTextFrame->GetText();
2091 m_aDelPam.SetMark();
2092 m_aDelPam.DeleteMark();
2093 aFInfo.SetFrame( nullptr );
2095 //#125102# in case of the mode RedlineFlags::ShowDelete the ** are still contained in pText
2096 if(!(m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags() & RedlineFlags::ShowDelete))
2097 nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint()) - TextFrameIndex(1);
2098 // Was a character deleted before starting?
2099 if (cBlank && cBlank != (*pText)[sal_Int32(nSttPos) - 1])
2100 --nSttPos;
2104 break;
2105 case '/':
2106 if ( m_aFlags.bAddNonBrkSpace )
2108 LanguageType eLang = bGetLanguage
2109 ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true)
2110 : LANGUAGE_SYSTEM;
2112 SetRedlineText( STR_AUTOFMTREDL_NON_BREAK_SPACE );
2113 if (pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext))
2114 --nPos;
2116 break;
2118 case '.':
2119 case '!':
2120 case '?':
2121 if( m_aFlags.bCapitalStartSentence )
2122 bFirstSent = true;
2123 [[fallthrough]];
2124 default:
2125 if (!(rAppCC.isLetterNumeric(*pText, sal_Int32(nPos))
2126 || '/' == cChar )) // '/' should not be a word separator (e.g. '1/2' needs to be handled as one word for replacement)
2128 --nPos; // revert ++nPos which was decremented in for loop
2129 ++bBreak;
2131 break;
2135 if( nPos == nSttPos )
2137 if (++nPos == TextFrameIndex(pText->getLength()))
2138 bCallACorr = true;
2140 else
2141 bCallACorr = true;
2143 if( bCallACorr )
2145 *m_aDelPam.GetPoint() = m_pCurTextFrame->MapViewToModelPos(nPos);
2146 SetRedlineText( STR_AUTOFMTREDL_USE_REPLACE );
2147 if( m_aFlags.bAutoCorrect &&
2148 aACorrDoc.ChgAutoCorrWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos), *pATst, nullptr))
2150 nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
2152 if( m_aFlags.bWithRedlining )
2154 m_aNdIdx = m_aDelPam.GetPoint()->nNode;
2155 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
2156 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
2157 pText = &m_pCurTextFrame->GetText();
2158 m_aDelPam.SetMark();
2159 m_aDelPam.DeleteMark();
2162 continue; // do not check further
2165 LanguageType eLang = bGetLanguage
2166 ? m_pCurTextFrame->GetLangOfChar(nSttPos, 0, true)
2167 : LANGUAGE_SYSTEM;
2169 if( m_aFlags.bTransliterateRTL && eLang == LANGUAGE_HUNGARIAN &&
2170 SetRedlineText( STR_AUTOFMTREDL_TRANSLITERATE_RTL ) &&
2171 aACorrDoc.TransliterateRTLWord(reinterpret_cast<sal_Int32&>(nSttPos), sal_Int32(nPos)))
2173 nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
2174 if( m_aFlags.bWithRedlining )
2176 m_aNdIdx = m_aDelPam.GetPoint()->nNode;
2177 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
2178 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
2179 pText = &m_pCurTextFrame->GetText();
2180 m_aDelPam.SetMark();
2181 m_aDelPam.DeleteMark();
2184 continue; // do not check further
2187 if ( m_aFlags.bAddNonBrkSpace && nPos < TextFrameIndex(pText->getLength()) )
2189 SetRedlineText( STR_AUTOFMTREDL_NON_BREAK_SPACE );
2190 pATst->FnAddNonBrkSpace(aACorrDoc, *pText, sal_Int32(nPos), eLang, bNbspRunNext);
2193 if( ( m_aFlags.bChgOrdinalNumber &&
2194 SetRedlineText( STR_AUTOFMTREDL_ORDINAL ) &&
2195 pATst->FnChgOrdinalNumber(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang)) ||
2196 ( m_aFlags.bChgToEnEmDash &&
2197 SetRedlineText( STR_AUTOFMTREDL_DASH ) &&
2198 pATst->FnChgToEnEmDash(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang)) ||
2199 ( m_aFlags.bSetINetAttr &&
2200 (nPos == TextFrameIndex(pText->getLength()) || IsSpace((*pText)[sal_Int32(nPos)])) &&
2201 SetRedlineText( STR_AUTOFMTREDL_DETECT_URL ) &&
2202 pATst->FnSetINetAttr(aACorrDoc, *pText, sal_Int32(nLastBlank), sal_Int32(nPos), eLang)))
2204 nPos = m_pCurTextFrame->MapModelToViewPos(*m_aDelPam.GetPoint());
2206 else
2208 // two capital letters at the beginning of a word?
2209 if( m_aFlags.bCapitalStartWord )
2211 SetRedlineText( STR_AUTOFMTREDL_CPTL_STT_WORD );
2212 pATst->FnCapitalStartWord(aACorrDoc, *pText, sal_Int32(nSttPos), sal_Int32(nPos), eLang);
2214 // capital letter at the beginning of a sentence?
2215 if( m_aFlags.bCapitalStartSentence && bFirst )
2217 SetRedlineText( STR_AUTOFMTREDL_CPTL_STT_SENT );
2218 pATst->FnCapitalStartSentence(aACorrDoc, *pText, true, sal_Int32(nSttPos), sal_Int32(nPos), eLang);
2221 bFirst = bFirstSent;
2222 bFirstSent = false;
2224 if( m_aFlags.bWithRedlining )
2226 m_aNdIdx = m_aDelPam.GetPoint()->nNode;
2227 m_pCurTextNd = m_aNdIdx.GetNode().GetTextNode();
2228 m_pCurTextFrame = GetFrame( *m_pCurTextNd );
2229 pText = &m_pCurTextFrame->GetText();
2230 m_aDelPam.SetMark();
2231 m_aDelPam.DeleteMark();
2236 while (nPos < TextFrameIndex(pText->getLength()));
2237 ClearRedlineText();
2240 SwAutoFormat::SwAutoFormat( SwEditShell* pEdShell, SvxSwAutoFormatFlags const & rFlags,
2241 SwNodeIndex const * pSttNd, SwNodeIndex const * pEndNd )
2242 : m_aFlags( rFlags ),
2243 m_aDelPam( pEdShell->GetDoc()->GetNodes().GetEndOfExtras() ),
2244 m_aNdIdx( pEdShell->GetDoc()->GetNodes().GetEndOfExtras(), +1 ),
2245 m_aEndNdIdx( pEdShell->GetDoc()->GetNodes().GetEndOfContent() ),
2246 m_pEditShell( pEdShell ),
2247 m_pDoc( pEdShell->GetDoc() ),
2248 m_pCurTextNd( nullptr ), m_pCurTextFrame( nullptr ),
2249 m_nRedlAutoFormatSeqId( 0 )
2251 OSL_ENSURE( (pSttNd && pEndNd) || (!pSttNd && !pEndNd),
2252 "Got no area" );
2254 if( m_aFlags.bSetNumRule && !m_aFlags.bAFormatByInput )
2255 m_aFlags.bSetNumRule = false;
2257 bool bReplaceStyles = !m_aFlags.bAFormatByInput || m_aFlags.bReplaceStyles;
2259 const SwTextFrame * pNextFrame = nullptr;
2260 bool bNxtEmpty = false;
2261 bool bNxtAlpha = false;
2262 sal_uInt16 nNxtLevel = 0;
2263 bool bEmptyLine;
2265 // set area for autoformatting
2266 if( pSttNd )
2268 m_aNdIdx = *pSttNd;
2269 // for GoNextPara, one paragraph prior to that
2270 sw::GotoPrevLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
2271 m_aEndNdIdx = *pEndNd;
2272 sw::GotoNextLayoutTextFrame(m_aEndNdIdx, m_pEditShell->GetLayout());
2274 // check the previous TextNode
2275 SwTextFrame const*const pPrevFrame = m_aNdIdx.GetNode().GetTextNode()
2276 ? static_cast<SwTextFrame const*>(m_aNdIdx.GetNode().GetTextNode()->getLayoutFrame(m_pEditShell->GetLayout()))
2277 : nullptr;
2278 bEmptyLine = !pPrevFrame
2279 || IsEmptyLine(*pPrevFrame)
2280 || IsNoAlphaLine(*pPrevFrame);
2282 else
2283 bEmptyLine = true; // at document beginning
2285 m_bEnd = false;
2287 // set value for percentage display
2288 m_nEndNdIdx = m_aEndNdIdx.GetIndex();
2290 if( !m_aFlags.bAFormatByInput )
2292 m_nEndNdIdx = m_aEndNdIdx.GetIndex();
2293 ::StartProgress( STR_STATSTR_AUTOFORMAT, m_aNdIdx.GetIndex(),
2294 m_nEndNdIdx,
2295 m_pDoc->GetDocShell() );
2298 RedlineFlags eRedlMode = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(), eOldMode = eRedlMode;
2299 if( m_aFlags.bWithRedlining )
2301 m_pDoc->SetAutoFormatRedline( true );
2302 eRedlMode = RedlineFlags::On | (eOldMode & RedlineFlags::ShowMask);
2304 else
2305 eRedlMode = RedlineFlags::Ignore | (eOldMode & RedlineFlags::ShowMask);
2306 m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eRedlMode );
2308 // save undo state (might be turned off)
2309 bool const bUndoState = m_pDoc->GetIDocumentUndoRedo().DoesUndo();
2311 // If multiple lines, then do not merge with next paragraph
2312 m_bMoreLines = false;
2314 sal_uInt16 nLastCalcHeadLvl = 0;
2315 sal_uInt16 nLastHeadLvl = USHRT_MAX;
2316 sal_uInt16 nLevel = 0;
2317 sal_uInt16 nDigitLvl = 0;
2319 // set defaults
2320 SwTextFrameInfo aFInfo( nullptr );
2322 enum Format_Status
2324 READ_NEXT_PARA, // -> ISEND, TST_EMPTY_LINE
2325 TST_EMPTY_LINE, // -> READ_NEXT_PARA, TST_ALPHA_LINE
2326 TST_ALPHA_LINE, // -> READ_NEXT_PARA, GET_ALL_INFO, IS_END
2327 GET_ALL_INFO, // -> READ_NEXT_PARA, IS_ONE_LINE, TST_ENUMERIC, HAS_FMTCOLL
2328 IS_ONE_LINE, // -> READ_NEXT_PARA, TST_ENUMERIC
2329 TST_ENUMERIC, // -> READ_NEXT_PARA, TST_IDENT, TST_NEG_IDENT
2330 TST_IDENT, // -> READ_NEXT_PARA, TST_TXT_BODY
2331 TST_NEG_IDENT, // -> READ_NEXT_PARA, TST_TXT_BODY
2332 TST_TXT_BODY, // -> READ_NEXT_PARA
2333 HAS_FMTCOLL, // -> READ_NEXT_PARA
2334 IS_END
2335 } eStat;
2337 // This is the automat for autoformatting
2338 eStat = READ_NEXT_PARA;
2339 while( !m_bEnd )
2341 switch( eStat )
2343 case READ_NEXT_PARA:
2345 GoNextPara();
2346 eStat = m_bEnd ? IS_END : TST_EMPTY_LINE;
2348 break;
2350 case TST_EMPTY_LINE:
2351 if (IsEmptyLine(*m_pCurTextFrame))
2353 if (m_aFlags.bDelEmptyNode && !HasObjects(*m_pCurTextFrame))
2355 bEmptyLine = true;
2356 sal_uLong nOldCnt = m_pDoc->GetNodes().Count();
2357 DelEmptyLine();
2358 // Was there really a deletion of a node?
2359 if( nOldCnt != m_pDoc->GetNodes().Count() )
2361 // do not skip the next paragraph
2362 sw::GotoPrevLayoutTextFrame(m_aNdIdx, m_pEditShell->GetLayout());
2365 eStat = READ_NEXT_PARA;
2367 else
2368 eStat = TST_ALPHA_LINE;
2369 break;
2371 case TST_ALPHA_LINE:
2372 if (IsNoAlphaLine(*m_pCurTextFrame))
2374 // recognize a table definition +---+---+
2375 if( m_aFlags.bAFormatByInput && m_aFlags.bCreateTable && DoTable() )
2377 //JP 30.09.96: DoTable() builds on PopCursor and MoveCursor after AutoFormat!
2378 pEdShell->Pop(SwCursorShell::PopMode::DeleteCurrent);
2379 *pEdShell->GetCursor() = m_aDelPam;
2380 pEdShell->Push();
2382 eStat = IS_END;
2383 break;
2386 // Check for 3 "---" or "===". In this case, the previous paragraph should be
2387 // underlined and the current be deleted!
2388 if( !DoUnderline() && bReplaceStyles )
2390 SetColl( RES_POOLCOLL_STANDARD, true );
2391 bEmptyLine = true;
2393 eStat = READ_NEXT_PARA;
2395 else
2396 eStat = GET_ALL_INFO;
2397 break;
2399 case GET_ALL_INFO:
2401 if (m_pCurTextFrame->GetTextNodeForParaProps()->GetNumRule())
2403 // do nothing in numbering, go to next
2404 bEmptyLine = false;
2405 eStat = READ_NEXT_PARA;
2406 // delete all blanks at beginning/end and in between
2407 //JP 29.04.98: first only "all in between"
2408 DelMoreLinesBlanks();
2409 break;
2412 aFInfo.SetFrame( m_pCurTextFrame );
2414 // so far: if there were templates assigned, keep these and go to next node
2415 sal_uInt16 nPoolId = m_pCurTextFrame->GetTextNodeForParaProps()->GetTextColl()->GetPoolFormatId();
2416 if( IsPoolUserFormat( nPoolId )
2417 ? !m_aFlags.bChgUserColl
2418 : ( RES_POOLCOLL_STANDARD != nPoolId &&
2419 ( !m_aFlags.bAFormatByInput ||
2420 (RES_POOLCOLL_TEXT_MOVE != nPoolId &&
2421 RES_POOLCOLL_TEXT != nPoolId )) ))
2423 eStat = HAS_FMTCOLL;
2424 break;
2427 // check for hard spaces or LRSpaces set by the template
2428 if( IsPoolUserFormat( nPoolId ) ||
2429 RES_POOLCOLL_STANDARD == nPoolId )
2431 short nSz;
2432 SvxLRSpaceItem const * pLRSpace;
2433 if (SfxItemState::SET == m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().
2434 GetItemState( RES_LR_SPACE, true,
2435 reinterpret_cast<const SfxPoolItem**>(&pLRSpace) ) &&
2436 ( 0 != (nSz = pLRSpace->GetTextFirstLineOffset()) ||
2437 0 != pLRSpace->GetTextLeft() ) )
2439 // exception: numbering/enumeration can have an indentation
2440 if (IsEnumericChar(*m_pCurTextFrame))
2442 nLevel = CalcLevel(*m_pCurTextFrame, &nDigitLvl);
2443 if( nLevel >= MAXLEVEL )
2444 nLevel = MAXLEVEL-1;
2445 BuildEnum( nLevel, nDigitLvl );
2446 eStat = READ_NEXT_PARA;
2447 break;
2450 // never merge (maybe only indent as exception)
2451 m_bMoreLines = true;
2453 if( bReplaceStyles )
2455 // then use one of our templates
2456 if( 0 < nSz ) // positive 1st line indentation
2457 BuildIndent();
2458 else if( 0 > nSz ) // negative 1st line indentation
2459 BuildNegIndent( aFInfo.GetLineStart() );
2460 else if( pLRSpace->GetTextLeft() ) // is indentation
2461 BuildTextIndent();
2463 eStat = READ_NEXT_PARA;
2464 break;
2468 nLevel = CalcLevel( *m_pCurTextFrame, &nDigitLvl );
2469 m_bMoreLines = !IsOneLine(*m_pCurTextFrame);
2470 // note: every use of pNextFrame in following states, until the
2471 // next READ_NEXT_PARA, relies on this update
2472 pNextFrame = GetNextNode();
2473 if (pNextFrame)
2475 bNxtEmpty = IsEmptyLine(*pNextFrame);
2476 bNxtAlpha = IsNoAlphaLine(*pNextFrame);
2477 nNxtLevel = CalcLevel(*pNextFrame);
2479 if (!bEmptyLine && HasBreakAttr(*m_pCurTextFrame))
2480 bEmptyLine = true;
2481 if (!bNxtEmpty && HasBreakAttr(*pNextFrame))
2482 bNxtEmpty = true;
2485 else
2487 bNxtEmpty = false;
2488 bNxtAlpha = false;
2489 nNxtLevel = 0;
2491 eStat = !m_bMoreLines ? IS_ONE_LINE : TST_ENUMERIC;
2493 break;
2495 case IS_ONE_LINE:
2497 eStat = TST_ENUMERIC;
2498 if( !bReplaceStyles )
2499 break;
2501 const OUString sClrStr( DelLeadingBlanks(m_pCurTextFrame->GetText()) );
2503 if( sClrStr.isEmpty() )
2505 bEmptyLine = true;
2506 eStat = READ_NEXT_PARA;
2507 break; // read next paragraph
2510 // check if headline
2511 if (!bEmptyLine || !IsFirstCharCapital(*m_pCurTextFrame)
2512 || IsBlanksInString(*m_pCurTextFrame))
2513 break;
2515 bEmptyLine = false;
2516 const OUString sEndClrStr( DelTrailingBlanks(sClrStr) );
2517 const sal_Unicode cLast = sEndClrStr[sEndClrStr.getLength() - 1];
2519 // not, then check if headline
2520 if( ':' == cLast )
2522 BuildHeadLine( 2 );
2523 eStat = READ_NEXT_PARA;
2524 break;
2526 else if( 256 <= cLast || !strchr( ",.;", cLast ) )
2528 if( bNxtEmpty || bNxtAlpha
2529 || (pNextFrame && IsEnumericChar(*pNextFrame)))
2532 // one level below?
2533 if( nLevel >= MAXLEVEL )
2534 nLevel = MAXLEVEL-1;
2536 if( USHRT_MAX == nLastHeadLvl )
2537 nLastHeadLvl = 0;
2538 else if( nLastCalcHeadLvl < nLevel )
2540 if( nLastHeadLvl+1 < MAXLEVEL )
2541 ++nLastHeadLvl;
2543 // one level above?
2544 else if( nLastCalcHeadLvl > nLevel )
2546 if( nLastHeadLvl )
2547 --nLastHeadLvl;
2549 nLastCalcHeadLvl = nLevel;
2551 if( m_aFlags.bAFormatByInput )
2552 BuildHeadLine( nLevel );
2553 else
2554 BuildHeadLine( nLastHeadLvl );
2555 eStat = READ_NEXT_PARA;
2556 break;
2560 break;
2562 case TST_ENUMERIC:
2564 bEmptyLine = false;
2565 if (IsEnumericChar(*m_pCurTextFrame))
2567 if( nLevel >= MAXLEVEL )
2568 nLevel = MAXLEVEL-1;
2569 BuildEnum( nLevel, nDigitLvl );
2570 eStat = READ_NEXT_PARA;
2572 else if( bReplaceStyles )
2573 eStat = nLevel ? TST_IDENT : TST_NEG_IDENT;
2574 else
2575 eStat = READ_NEXT_PARA;
2577 break;
2579 case TST_IDENT:
2580 // Spaces at the beginning, check again for indentation
2581 if( m_bMoreLines && nLevel )
2583 SwTwips nSz = aFInfo.GetFirstIndent();
2584 if( 0 < nSz ) // positive 1st line indentation
2585 BuildIndent();
2586 else if( 0 > nSz ) // negative 1st line indentation
2587 BuildNegIndent( aFInfo.GetLineStart() );
2588 else // is indentation
2589 BuildTextIndent();
2590 eStat = READ_NEXT_PARA;
2592 else if (nLevel && pNextFrame &&
2593 !bNxtEmpty && !bNxtAlpha && !nNxtLevel &&
2594 !IsEnumericChar(*pNextFrame))
2596 // is an indentation
2597 BuildIndent();
2598 eStat = READ_NEXT_PARA;
2600 else
2601 eStat = TST_TXT_BODY;
2602 break;
2604 case TST_NEG_IDENT:
2605 // no spaces at the beginning, check again for negative indentation
2607 if( m_bMoreLines && !nLevel )
2609 SwTwips nSz = aFInfo.GetFirstIndent();
2610 if( 0 < nSz ) // positive 1st line indentation
2611 BuildIndent();
2612 else if( 0 > nSz ) // negative 1st line indentation
2613 BuildNegIndent( aFInfo.GetLineStart() );
2614 else // is _no_ indentation
2615 BuildText();
2616 eStat = READ_NEXT_PARA;
2618 else if (!nLevel && pNextFrame &&
2619 !bNxtEmpty && !bNxtAlpha && nNxtLevel &&
2620 !IsEnumericChar(*pNextFrame))
2622 // is a negative indentation
2623 BuildNegIndent( aFInfo.GetLineStart() );
2624 eStat = READ_NEXT_PARA;
2626 else
2627 eStat = TST_TXT_BODY;
2629 break;
2631 case TST_TXT_BODY:
2633 if( m_bMoreLines )
2635 SwTwips nSz = aFInfo.GetFirstIndent();
2636 if( 0 < nSz ) // positive 1st line indentation
2637 BuildIndent();
2638 else if( 0 > nSz ) // negative 1st line indentation
2639 BuildNegIndent( aFInfo.GetLineStart() );
2640 else if( nLevel ) // is indentation
2641 BuildTextIndent();
2642 else
2643 BuildText();
2645 else if( nLevel )
2646 BuildTextIndent();
2647 else
2648 BuildText();
2649 eStat = READ_NEXT_PARA;
2651 break;
2653 case HAS_FMTCOLL:
2655 // so far: if there were templates assigned, keep these and go to next node
2656 bEmptyLine = false;
2657 eStat = READ_NEXT_PARA;
2658 // delete all blanks at beginning/end and in between
2659 //JP 29.04.98: first only "all in between"
2660 DelMoreLinesBlanks();
2662 // handle hard attributes
2663 if (m_pCurTextFrame->GetTextNodeForParaProps()->HasSwAttrSet())
2665 short nSz;
2666 SvxLRSpaceItem const * pLRSpace;
2667 if( bReplaceStyles &&
2668 SfxItemState::SET == m_pCurTextFrame->GetTextNodeForParaProps()->GetSwAttrSet().
2669 GetItemState( RES_LR_SPACE, false,
2670 reinterpret_cast<const SfxPoolItem**>(&pLRSpace) ) &&
2671 ( 0 != (nSz = pLRSpace->GetTextFirstLineOffset()) ||
2672 0 != pLRSpace->GetTextLeft() ) )
2674 // then use one of our templates
2675 if( 0 < nSz ) // positive 1st line indentation
2676 BuildIndent();
2677 else if( 0 > nSz ) // negative 1st line indentation
2679 BuildNegIndent( aFInfo.GetLineStart() );
2681 else if( pLRSpace->GetTextLeft() ) // is indentation
2682 BuildTextIndent();
2683 else
2684 BuildText();
2688 break;
2690 case IS_END:
2691 m_bEnd = true;
2692 break;
2696 if( m_aFlags.bWithRedlining )
2697 m_pDoc->SetAutoFormatRedline( false );
2698 m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( eOldMode );
2700 // restore undo (in case it has been changed)
2701 m_pDoc->GetIDocumentUndoRedo().DoUndo(bUndoState);
2703 // disable display of percentage again
2704 if( !m_aFlags.bAFormatByInput )
2705 ::EndProgress( m_pDoc->GetDocShell() );
2708 void SwEditShell::AutoFormat( const SvxSwAutoFormatFlags* pAFlags )
2710 std::unique_ptr<SwWait> pWait;
2712 CurrShell aCurr( this );
2713 StartAllAction();
2714 StartUndo( SwUndoId::AUTOFORMAT );
2716 SvxSwAutoFormatFlags aAFFlags; // use default values or add params?
2717 if( pAFlags )
2719 aAFFlags = *pAFlags;
2720 if( !aAFFlags.bAFormatByInput )
2721 pWait.reset(new SwWait( *GetDoc()->GetDocShell(), true ));
2724 SwPaM* pCursor = GetCursor();
2725 // There are more than one or a selection is open
2726 if( pCursor->GetNext() != pCursor || pCursor->HasMark() )
2728 for(SwPaM& rPaM : GetCursor()->GetRingContainer())
2730 if( rPaM.HasMark() )
2732 SwAutoFormat aFormat( this, aAFFlags, &(rPaM.Start()->nNode),
2733 &(rPaM.End()->nNode) );
2737 else
2739 SwAutoFormat aFormat( this, aAFFlags );
2742 EndUndo( SwUndoId::AUTOFORMAT );
2743 EndAllAction();
2746 void SwEditShell::AutoFormatBySplitNode()
2748 CurrShell aCurr( this );
2749 SwPaM* pCursor = GetCursor();
2750 if( pCursor->IsMultiSelection() || !pCursor->Move( fnMoveBackward, GoInNode ) )
2751 return;
2753 StartAllAction();
2754 StartUndo( SwUndoId::AUTOFORMAT );
2756 bool bRange = false;
2757 pCursor->SetMark();
2758 SwIndex* pContent = &pCursor->GetMark()->nContent;
2759 if( pContent->GetIndex() )
2761 *pContent = 0;
2762 bRange = true;
2764 else
2766 // then go one node backwards
2767 SwNodeIndex aNdIdx(pCursor->GetMark()->nNode);
2768 sw::GotoPrevLayoutTextFrame(aNdIdx, GetLayout());
2769 SwTextNode* pTextNd = aNdIdx.GetNode().GetTextNode();
2770 if (pTextNd && !pTextNd->GetText().isEmpty())
2772 pContent->Assign( pTextNd, 0 );
2773 pCursor->GetMark()->nNode = aNdIdx;
2774 bRange = true;
2778 if( bRange )
2780 Push(); // save cursor
2782 SvxSwAutoFormatFlags aAFFlags = *GetAutoFormatFlags(); // use default values so far
2784 SwAutoFormat aFormat( this, aAFFlags, &pCursor->GetMark()->nNode,
2785 &pCursor->GetPoint()->nNode );
2786 SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
2787 if( pACorr && !pACorr->IsAutoCorrFlag( ACFlags::CapitalStartSentence | ACFlags::CapitalStartWord |
2788 ACFlags::AddNonBrkSpace | ACFlags::ChgOrdinalNumber | ACFlags::TransliterateRTL |
2789 ACFlags::ChgToEnEmDash | ACFlags::SetINetAttr | ACFlags::Autocorrect ))
2790 pACorr = nullptr;
2792 if( pACorr )
2793 AutoCorrect( *pACorr,false, u'\0' );
2795 //JP 30.09.96: DoTable() builds on PopCursor and MoveCursor!
2796 Pop(PopMode::DeleteCurrent);
2797 pCursor = GetCursor();
2799 pCursor->DeleteMark();
2800 pCursor->Move( fnMoveForward, GoInNode );
2802 EndUndo( SwUndoId::AUTOFORMAT );
2803 EndAllAction();
2807 SvxSwAutoFormatFlags* SwEditShell::GetAutoFormatFlags()
2809 if (!s_pAutoFormatFlags)
2810 s_pAutoFormatFlags = new SvxSwAutoFormatFlags;
2812 return s_pAutoFormatFlags;
2815 void SwEditShell::SetAutoFormatFlags(SvxSwAutoFormatFlags const * pFlags)
2817 SvxSwAutoFormatFlags* pEditFlags = GetAutoFormatFlags();
2819 pEditFlags->bSetNumRule = pFlags->bSetNumRule;
2820 pEditFlags->bChgEnumNum = pFlags->bChgEnumNum;
2821 pEditFlags->bSetBorder = pFlags->bSetBorder;
2822 pEditFlags->bCreateTable = pFlags->bCreateTable;
2823 pEditFlags->bReplaceStyles = pFlags->bReplaceStyles;
2824 pEditFlags->bAFormatByInpDelSpacesAtSttEnd =
2825 pFlags->bAFormatByInpDelSpacesAtSttEnd;
2826 pEditFlags->bAFormatByInpDelSpacesBetweenLines =
2827 pFlags->bAFormatByInpDelSpacesBetweenLines;
2829 //JP 15.12.98: copy BulletChar and Font into "normal" ones
2830 // because AutoFormat can only work with the latter!
2831 pEditFlags->cBullet = pFlags->cByInputBullet;
2832 pEditFlags->aBulletFont = pFlags->aByInputBulletFont;
2833 pEditFlags->cByInputBullet = pFlags->cByInputBullet;
2834 pEditFlags->aByInputBulletFont = pFlags->aByInputBulletFont;
2837 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */