1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <com/sun/star/frame/XModel.hpp>
21 #include <com/sun/star/linguistic2/ProofreadingResult.hpp>
22 #include <com/sun/star/linguistic2/XProofreadingIterator.hpp>
23 #include <com/sun/star/linguistic2/XHyphenatedWord.hpp>
24 #include <com/sun/star/linguistic2/XLinguProperties.hpp>
25 #include <com/sun/star/text/XFlatParagraph.hpp>
26 #include <com/sun/star/i18n/ScriptType.hpp>
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <o3tl/any.hxx>
29
30 #include <unoflatpara.hxx>
31
32 #include <strings.hrc>
33 #include <hintids.hxx>
34 #include <osl/diagnose.h>
35 #include <unotools/linguprops.hxx>
36 #include <linguistic/lngprops.hxx>
37 #include <editeng/langitem.hxx>
38 #include <editeng/SpellPortions.hxx>
39 #include <svl/languageoptions.hxx>
40 #include <editsh.hxx>
41 #include <doc.hxx>
42 #include <IDocumentUndoRedo.hxx>
43 #include <IDocumentRedlineAccess.hxx>
44 #include <rootfrm.hxx>
45 #include <pam.hxx>
46 #include <swundo.hxx>
47 #include <ndtxt.hxx>
48 #include <viewopt.hxx>
49 #include <SwGrammarMarkUp.hxx>
50 #include <mdiexp.hxx>
51 #include <cntfrm.hxx>
52 #include <splargs.hxx>
53 #include <redline.hxx>
54 #include <docary.hxx>
55 #include <docsh.hxx>
56 #include <txatbase.hxx>
57 #include <txtfrm.hxx>
58
59 using namespace ::svx;
60 using namespace ::com::sun::star;
61 using namespace ::com::sun::star::uno;
62 using namespace ::com::sun::star::beans;
63 using namespace ::com::sun::star::linguistic2;
64
65 namespace {
66
67 class SwLinguIter
68 {
69 SwEditShell* m_pSh;
70 std::unique_ptr<SwPosition> m_pStart;
71 std::unique_ptr<SwPosition> m_pEnd;
72 std::unique_ptr<SwPosition> m_pCurr;
73 std::unique_ptr<SwPosition> m_pCurrX;
74 sal_uInt16 m_nCursorCount;
75
76 public:
77 SwLinguIter();
78
GetSh()79 SwEditShell* GetSh() { return m_pSh; }
80
GetEnd() const81 const SwPosition *GetEnd() const { return m_pEnd.get(); }
SetEnd(SwPosition * pNew)82 void SetEnd(SwPosition* pNew) { m_pEnd.reset(pNew); }
83
GetStart() const84 const SwPosition *GetStart() const { return m_pStart.get(); }
SetStart(SwPosition * pNew)85 void SetStart(SwPosition* pNew) { m_pStart.reset(pNew); }
86
GetCurr() const87 const SwPosition *GetCurr() const { return m_pCurr.get(); }
SetCurr(SwPosition * pNew)88 void SetCurr(SwPosition* pNew) { m_pCurr.reset(pNew); }
89
GetCurrX() const90 const SwPosition *GetCurrX() const { return m_pCurrX.get(); }
SetCurrX(SwPosition * pNew)91 void SetCurrX(SwPosition* pNew) { m_pCurrX.reset(pNew); }
92
GetCursorCnt()93 sal_uInt16& GetCursorCnt() { return m_nCursorCount; }
94
95 // for the UI:
96 void Start_( SwEditShell *pSh, SwDocPositions eStart,
97 SwDocPositions eEnd );
98 void End_(bool bRestoreSelection = true);
99 };
100
101 // #i18881# to be able to identify the positions of the changed words
102 // the content positions of each portion need to be saved
103 struct SpellContentPosition
104 {
105 sal_Int32 nLeft;
106 sal_Int32 nRight;
107 };
108
109 }
110
111 typedef std::vector<SpellContentPosition> SpellContentPositions;
112
113 namespace {
114
115 class SwSpellIter : public SwLinguIter
116 {
117 uno::Reference<XSpellChecker1> m_xSpeller;
118 svx::SpellPortions m_aLastPortions;
119
120 SpellContentPositions m_aLastPositions;
121 bool m_bBackToStartOfSentence;
122
123 void CreatePortion(uno::Reference< XSpellAlternatives > const & xAlt,
124 linguistic2::ProofreadingResult* pGrammarResult,
125 bool bIsField, bool bIsHidden);
126
127 void AddPortion(uno::Reference< XSpellAlternatives > const & xAlt,
128 linguistic2::ProofreadingResult* pGrammarResult,
129 const SpellContentPositions& rDeletedRedlines);
130 public:
SwSpellIter()131 SwSpellIter()
132 : m_bBackToStartOfSentence(false)
133 {
134 }
135
136 void Start( SwEditShell *pSh, SwDocPositions eStart, SwDocPositions eEnd );
137
138 uno::Any Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt );
139
140 bool SpellSentence(svx::SpellPortions& rPortions, bool bIsGrammarCheck);
141 void ToSentenceStart();
GetLastPortions() const142 const svx::SpellPortions& GetLastPortions() const { return m_aLastPortions; }
GetLastPositions() const143 const SpellContentPositions& GetLastPositions() const { return m_aLastPositions; }
144 };
145
146 /// used for text conversion
147 class SwConvIter : public SwLinguIter
148 {
149 SwConversionArgs& m_rArgs;
150
151 public:
SwConvIter(SwConversionArgs & rConvArgs)152 explicit SwConvIter(SwConversionArgs& rConvArgs)
153 : m_rArgs(rConvArgs)
154 {
155 }
156
157 void Start( SwEditShell *pSh, SwDocPositions eStart, SwDocPositions eEnd );
158
159 uno::Any Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt );
160 };
161
162 class SwHyphIter : public SwLinguIter
163 {
164 // With that we save a GetFrame() in Hyphenate //TODO: does it actually matter?
165 const SwTextNode *m_pLastNode;
166 SwTextFrame *m_pLastFrame;
167 friend SwTextFrame * sw::SwHyphIterCacheLastTextFrame(SwTextNode const * pNode, const sw::Creator& rCreator);
168
169 bool m_bOldIdle;
170 static void DelSoftHyph( SwPaM &rPam );
171
172 public:
SwHyphIter()173 SwHyphIter()
174 : m_pLastNode(nullptr)
175 , m_pLastFrame(nullptr)
176 , m_bOldIdle(false)
177 {
178 }
179
180 void Start( SwEditShell *pSh, SwDocPositions eStart, SwDocPositions eEnd );
181 void End();
182
183 void Ignore();
184
185 uno::Any Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt );
186
187 static bool IsAuto();
188 void InsertSoftHyph( const sal_Int32 nHyphPos );
189 void ShowSelection();
190 };
191
192 }
193
194 static SwSpellIter* g_pSpellIter = nullptr;
195 static SwConvIter* g_pConvIter = nullptr;
196 static SwHyphIter* g_pHyphIter = nullptr;
197
SwLinguIter()198 SwLinguIter::SwLinguIter()
199 : m_pSh(nullptr)
200 , m_nCursorCount(0)
201 {
202 // TODO missing: ensurance of re-entrance, OSL_ENSURE( etc.
203 }
204
Start_(SwEditShell * pShell,SwDocPositions eStart,SwDocPositions eEnd)205 void SwLinguIter::Start_( SwEditShell *pShell, SwDocPositions eStart,
206 SwDocPositions eEnd )
207 {
208 // TODO missing: ensurance of re-entrance, locking
209 if (m_pSh)
210 return;
211
212 bool bSetCurr;
213
214 m_pSh = pShell;
215
216 CurrShell aCurr(m_pSh);
217
218 OSL_ENSURE(!m_pEnd, "SwLinguIter::Start_ without End?");
219
220 SwPaM* pCursor = m_pSh->GetCursor();
221
222 if( pShell->HasSelection() || pCursor != pCursor->GetNext() )
223 {
224 bSetCurr = nullptr != GetCurr();
225 m_nCursorCount = m_pSh->GetCursorCnt();
226 if (m_pSh->IsTableMode())
227 m_pSh->TableCursorToCursor();
228
229 m_pSh->Push();
230 sal_uInt16 n;
231 for (n = 0; n < m_nCursorCount; ++n)
232 {
233 m_pSh->Push();
234 m_pSh->DestroyCursor();
235 }
236 m_pSh->Pop(SwCursorShell::PopMode::DeleteCurrent);
237 }
238 else
239 {
240 bSetCurr = false;
241 m_nCursorCount = 1;
242 m_pSh->Push();
243 m_pSh->SetLinguRange(eStart, eEnd);
244 }
245
246 pCursor = m_pSh->GetCursor();
247 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
248 pCursor->Exchange();
249
250 m_pStart.reset(new SwPosition(*pCursor->GetPoint()));
251 m_pEnd.reset(new SwPosition(*pCursor->GetMark()));
252 if( bSetCurr )
253 {
254 SwPosition* pNew = new SwPosition( *GetStart() );
255 SetCurr( pNew );
256 pNew = new SwPosition( *pNew );
257 SetCurrX( pNew );
258 }
259
260 pCursor->SetMark();
261 }
262
End_(bool bRestoreSelection)263 void SwLinguIter::End_(bool bRestoreSelection)
264 {
265 if (!m_pSh)
266 return;
267
268 OSL_ENSURE(m_pEnd, "SwLinguIter::End_ without end?");
269 if(bRestoreSelection)
270 {
271 while (m_nCursorCount--)
272 m_pSh->Pop(SwCursorShell::PopMode::DeleteCurrent);
273
274 m_pSh->KillPams();
275 m_pSh->ClearMark();
276 }
277 m_pStart.reset();
278 m_pEnd.reset();
279 m_pCurr.reset();
280 m_pCurrX.reset();
281
282 m_pSh = nullptr;
283 }
284
Start(SwEditShell * pShell,SwDocPositions eStart,SwDocPositions eEnd)285 void SwSpellIter::Start( SwEditShell *pShell, SwDocPositions eStart,
286 SwDocPositions eEnd )
287 {
288 if( GetSh() )
289 return;
290
291 m_xSpeller = ::GetSpellChecker();
292 if (m_xSpeller.is())
293 Start_( pShell, eStart, eEnd );
294 m_aLastPortions.clear();
295 m_aLastPositions.clear();
296 }
297
298 // This method is the origin of SwEditShell::SpellContinue()
Continue(sal_uInt16 * pPageCnt,sal_uInt16 * pPageSt)299 uno::Any SwSpellIter::Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
300 {
301 //!!
302 //!! Please check SwConvIter also when modifying this
303 //!!
304
305 uno::Any aSpellRet;
306 SwEditShell *pMySh = GetSh();
307 if( !pMySh )
308 return aSpellRet;
309
310 OSL_ENSURE( GetEnd(), "SwSpellIter::Continue without start?");
311
312 uno::Reference< uno::XInterface > xSpellRet;
313 bool bGoOn = true;
314 do {
315 SwPaM *pCursor = pMySh->GetCursor();
316 if ( !pCursor->HasMark() )
317 pCursor->SetMark();
318
319 *pMySh->GetCursor()->GetPoint() = *GetCurr();
320 *pMySh->GetCursor()->GetMark() = *GetEnd();
321 pMySh->GetDoc()->Spell(*pMySh->GetCursor(), m_xSpeller, pPageCnt, pPageSt, false,
322 pMySh->GetLayout())
323 >>= xSpellRet;
324 bGoOn = GetCursorCnt() > 1;
325 if( xSpellRet.is() )
326 {
327 bGoOn = false;
328 SwPosition* pNewPoint = new SwPosition( *pCursor->GetPoint() );
329 SwPosition* pNewMark = new SwPosition( *pCursor->GetMark() );
330 SetCurr( pNewPoint );
331 SetCurrX( pNewMark );
332 }
333 if( bGoOn )
334 {
335 pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
336 pCursor = pMySh->GetCursor();
337 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
338 pCursor->Exchange();
339 SwPosition* pNew = new SwPosition( *pCursor->GetPoint() );
340 SetStart( pNew );
341 pNew = new SwPosition( *pCursor->GetMark() );
342 SetEnd( pNew );
343 pNew = new SwPosition( *GetStart() );
344 SetCurr( pNew );
345 pNew = new SwPosition( *pNew );
346 SetCurrX( pNew );
347 pCursor->SetMark();
348 --GetCursorCnt();
349 }
350 }while ( bGoOn );
351 aSpellRet <<= xSpellRet;
352 return aSpellRet;
353 }
354
Start(SwEditShell * pShell,SwDocPositions eStart,SwDocPositions eEnd)355 void SwConvIter::Start( SwEditShell *pShell, SwDocPositions eStart,
356 SwDocPositions eEnd )
357 {
358 if( GetSh() )
359 return;
360 Start_( pShell, eStart, eEnd );
361 }
362
Continue(sal_uInt16 * pPageCnt,sal_uInt16 * pPageSt)363 uno::Any SwConvIter::Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
364 {
365 //!!
366 //!! Please check SwSpellIter also when modifying this
367 //!!
368
369 uno::Any aConvRet( makeAny( OUString() ) );
370 SwEditShell *pMySh = GetSh();
371 if( !pMySh )
372 return aConvRet;
373
374 OSL_ENSURE( GetEnd(), "SwConvIter::Continue() without Start?");
375
376 OUString aConvText;
377 bool bGoOn = true;
378 do {
379 SwPaM *pCursor = pMySh->GetCursor();
380 if ( !pCursor->HasMark() )
381 pCursor->SetMark();
382
383 *pMySh->GetCursor()->GetPoint() = *GetCurr();
384 *pMySh->GetCursor()->GetMark() = *GetEnd();
385
386 // call function to find next text portion to be converted
387 uno::Reference< linguistic2::XSpellChecker1 > xEmpty;
388 pMySh->GetDoc()->Spell(*pMySh->GetCursor(), xEmpty, pPageCnt, pPageSt, false,
389 pMySh->GetLayout(), &m_rArgs)
390 >>= aConvText;
391
392 bGoOn = GetCursorCnt() > 1;
393 if( !aConvText.isEmpty() )
394 {
395 bGoOn = false;
396 SwPosition* pNewPoint = new SwPosition( *pCursor->GetPoint() );
397 SwPosition* pNewMark = new SwPosition( *pCursor->GetMark() );
398
399 SetCurr( pNewPoint );
400 SetCurrX( pNewMark );
401 }
402 if( bGoOn )
403 {
404 pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
405 pCursor = pMySh->GetCursor();
406 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
407 pCursor->Exchange();
408 SwPosition* pNew = new SwPosition( *pCursor->GetPoint() );
409 SetStart( pNew );
410 pNew = new SwPosition( *pCursor->GetMark() );
411 SetEnd( pNew );
412 pNew = new SwPosition( *GetStart() );
413 SetCurr( pNew );
414 pNew = new SwPosition( *pNew );
415 SetCurrX( pNew );
416 pCursor->SetMark();
417 --GetCursorCnt();
418 }
419 }while ( bGoOn );
420 return makeAny( aConvText );
421 }
422
IsAuto()423 bool SwHyphIter::IsAuto()
424 {
425 uno::Reference< beans::XPropertySet > xProp( ::GetLinguPropertySet() );
426 return xProp.is() && *o3tl::doAccess<bool>(xProp->getPropertyValue(
427 UPN_IS_HYPH_AUTO ));
428 }
429
ShowSelection()430 void SwHyphIter::ShowSelection()
431 {
432 SwEditShell *pMySh = GetSh();
433 if( pMySh )
434 {
435 pMySh->StartAction();
436 // Caution! Due to EndAction() formatting is started which can lead to the fact that new
437 // words are added to/set in the Hyphenator. Thus: save!
438 pMySh->EndAction();
439 }
440 }
441
Start(SwEditShell * pShell,SwDocPositions eStart,SwDocPositions eEnd)442 void SwHyphIter::Start( SwEditShell *pShell, SwDocPositions eStart, SwDocPositions eEnd )
443 {
444 // robust
445 if( GetSh() || GetEnd() )
446 {
447 OSL_ENSURE( !GetSh(), "SwHyphIter::Start: missing HyphEnd()" );
448 return;
449 }
450
451 // nothing to do (at least not in the way as in the "else" part)
452 m_bOldIdle = pShell->GetViewOptions()->IsIdle();
453 pShell->GetViewOptions()->SetIdle( false );
454 Start_( pShell, eStart, eEnd );
455 }
456
457 // restore selections
End()458 void SwHyphIter::End()
459 {
460 if( !GetSh() )
461 return;
462 GetSh()->GetViewOptions()->SetIdle(m_bOldIdle);
463 End_();
464 }
465
Continue(sal_uInt16 * pPageCnt,sal_uInt16 * pPageSt)466 uno::Any SwHyphIter::Continue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
467 {
468 uno::Any aHyphRet;
469 SwEditShell *pMySh = GetSh();
470 if( !pMySh )
471 return aHyphRet;
472
473 const bool bAuto = IsAuto();
474 uno::Reference< XHyphenatedWord > xHyphWord;
475 bool bGoOn = false;
476 do {
477 SwPaM *pCursor;
478 do {
479 OSL_ENSURE( GetEnd(), "SwHyphIter::Continue without Start?" );
480 pCursor = pMySh->GetCursor();
481 if ( !pCursor->HasMark() )
482 pCursor->SetMark();
483 if ( *pCursor->GetPoint() < *pCursor->GetMark() )
484 {
485 pCursor->Exchange();
486 pCursor->SetMark();
487 }
488
489 if ( *pCursor->End() <= *GetEnd() )
490 {
491 *pCursor->GetMark() = *GetEnd();
492
493 // Do we need to break the word at the current cursor position?
494 const Point aCursorPos( pMySh->GetCharRect().Pos() );
495 xHyphWord = pMySh->GetDoc()->Hyphenate( pCursor, aCursorPos,
496 pPageCnt, pPageSt );
497 }
498
499 if( bAuto && xHyphWord.is() )
500 {
501 SwEditShell::InsertSoftHyph( xHyphWord->getHyphenationPos() + 1);
502 }
503 } while( bAuto && xHyphWord.is() ); //end of do-while
504 bGoOn = !xHyphWord.is() && GetCursorCnt() > 1;
505
506 if( bGoOn )
507 {
508 pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
509 pCursor = pMySh->GetCursor();
510 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
511 pCursor->Exchange();
512 SwPosition* pNew = new SwPosition(*pCursor->End());
513 SetEnd( pNew );
514 pCursor->SetMark();
515 --GetCursorCnt();
516 }
517 } while ( bGoOn );
518 aHyphRet <<= xHyphWord;
519 return aHyphRet;
520 }
521
522 /// ignore hyphenation
Ignore()523 void SwHyphIter::Ignore()
524 {
525 SwEditShell *pMySh = GetSh();
526 SwPaM *pCursor = pMySh->GetCursor();
527
528 // delete old SoftHyphen
529 DelSoftHyph( *pCursor );
530
531 // and continue
532 pCursor->Start()->nContent = pCursor->End()->nContent;
533 pCursor->SetMark();
534 }
535
DelSoftHyph(SwPaM & rPam)536 void SwHyphIter::DelSoftHyph( SwPaM &rPam )
537 {
538 const SwPosition* pStt = rPam.Start();
539 const sal_Int32 nStart = pStt->nContent.GetIndex();
540 const sal_Int32 nEnd = rPam.End()->nContent.GetIndex();
541 SwTextNode *pNode = pStt->nNode.GetNode().GetTextNode();
542 pNode->DelSoftHyph( nStart, nEnd );
543 }
544
InsertSoftHyph(const sal_Int32 nHyphPos)545 void SwHyphIter::InsertSoftHyph( const sal_Int32 nHyphPos )
546 {
547 SwEditShell *pMySh = GetSh();
548 OSL_ENSURE( pMySh, "SwHyphIter::InsertSoftHyph: missing HyphStart()");
549 if( !pMySh )
550 return;
551
552 SwPaM *pCursor = pMySh->GetCursor();
553 SwPosition* pSttPos = pCursor->Start();
554 SwPosition* pEndPos = pCursor->End();
555
556 const sal_Int32 nLastHyphLen = GetEnd()->nContent.GetIndex() -
557 pSttPos->nContent.GetIndex();
558
559 if( pSttPos->nNode != pEndPos->nNode || !nLastHyphLen )
560 {
561 OSL_ENSURE( pSttPos->nNode == pEndPos->nNode,
562 "SwHyphIter::InsertSoftHyph: node warp during hyphenation" );
563 OSL_ENSURE(nLastHyphLen, "SwHyphIter::InsertSoftHyph: missing HyphContinue()");
564 *pSttPos = *pEndPos;
565 return;
566 }
567
568 pMySh->StartAction();
569 {
570 SwDoc *pDoc = pMySh->GetDoc();
571 DelSoftHyph( *pCursor );
572 pSttPos->nContent += nHyphPos;
573 SwPaM aRg( *pSttPos );
574 pDoc->getIDocumentContentOperations().InsertString( aRg, OUString(CHAR_SOFTHYPHEN) );
575 }
576 // revoke selection
577 pCursor->DeleteMark();
578 pMySh->EndAction();
579 pCursor->SetMark();
580 }
581
582 namespace sw {
583
584 SwTextFrame *
SwHyphIterCacheLastTextFrame(SwTextNode const * pNode,const sw::Creator & create)585 SwHyphIterCacheLastTextFrame(SwTextNode const * pNode, const sw::Creator& create)
586 {
587 assert(g_pHyphIter);
588 if (pNode != g_pHyphIter->m_pLastNode || !g_pHyphIter->m_pLastFrame)
589 {
590 g_pHyphIter->m_pLastNode = pNode;
591 g_pHyphIter->m_pLastFrame = create();
592 }
593 return g_pHyphIter->m_pLastFrame;
594 }
595
596 }
597
HasLastSentenceGotGrammarChecked()598 bool SwEditShell::HasLastSentenceGotGrammarChecked()
599 {
600 bool bTextWasGrammarChecked = false;
601 if (g_pSpellIter)
602 {
603 svx::SpellPortions aLastPortions( g_pSpellIter->GetLastPortions() );
604 for (size_t i = 0; i < aLastPortions.size() && !bTextWasGrammarChecked; ++i)
605 {
606 // bIsGrammarError is also true if the text was only checked but no
607 // grammar error was found. (That is if a ProofreadingResult was obtained in
608 // SwDoc::Spell and in turn bIsGrammarError was set in SwSpellIter::CreatePortion)
609 if (aLastPortions[i].bIsGrammarError)
610 bTextWasGrammarChecked = true;
611 }
612 }
613 return bTextWasGrammarChecked;
614 }
615
HasConvIter()616 bool SwEditShell::HasConvIter()
617 {
618 return nullptr != g_pConvIter;
619 }
620
HasHyphIter()621 bool SwEditShell::HasHyphIter()
622 {
623 return nullptr != g_pHyphIter;
624 }
625
SetLinguRange(SwDocPositions eStart,SwDocPositions eEnd)626 void SwEditShell::SetLinguRange( SwDocPositions eStart, SwDocPositions eEnd )
627 {
628 SwPaM *pCursor = GetCursor();
629 MakeFindRange( eStart, eEnd, pCursor );
630 if( *pCursor->GetPoint() > *pCursor->GetMark() )
631 pCursor->Exchange();
632 }
633
SpellStart(SwDocPositions eStart,SwDocPositions eEnd,SwDocPositions eCurr,SwConversionArgs * pConvArgs)634 void SwEditShell::SpellStart(
635 SwDocPositions eStart, SwDocPositions eEnd, SwDocPositions eCurr,
636 SwConversionArgs *pConvArgs )
637 {
638 SwLinguIter *pLinguIter = nullptr;
639
640 // do not spell if interactive spelling is active elsewhere
641 if (!pConvArgs && !g_pSpellIter)
642 {
643 g_pSpellIter = new SwSpellIter;
644 pLinguIter = g_pSpellIter;
645 }
646 // do not do text conversion if it is active elsewhere
647 if (pConvArgs && !g_pConvIter)
648 {
649 g_pConvIter = new SwConvIter( *pConvArgs );
650 pLinguIter = g_pConvIter;
651 }
652
653 if (pLinguIter)
654 {
655 SwCursor* pSwCursor = GetSwCursor();
656
657 SwPosition *pTmp = new SwPosition( *pSwCursor->GetPoint() );
658 pSwCursor->FillFindPos( eCurr, *pTmp );
659 pLinguIter->SetCurr( pTmp );
660
661 pTmp = new SwPosition( *pTmp );
662 pLinguIter->SetCurrX( pTmp );
663 }
664
665 if (!pConvArgs && g_pSpellIter)
666 g_pSpellIter->Start( this, eStart, eEnd );
667 if (pConvArgs && g_pConvIter)
668 g_pConvIter->Start( this, eStart, eEnd );
669 }
670
SpellEnd(SwConversionArgs const * pConvArgs,bool bRestoreSelection)671 void SwEditShell::SpellEnd( SwConversionArgs const *pConvArgs, bool bRestoreSelection )
672 {
673 if (!pConvArgs && g_pSpellIter && g_pSpellIter->GetSh() == this)
674 {
675 g_pSpellIter->End_(bRestoreSelection);
676 delete g_pSpellIter;
677 g_pSpellIter = nullptr;
678 }
679 if (pConvArgs && g_pConvIter && g_pConvIter->GetSh() == this)
680 {
681 g_pConvIter->End_();
682 delete g_pConvIter;
683 g_pConvIter = nullptr;
684 }
685 }
686
687 /// @returns SPL_ return values as in splchk.hxx
SpellContinue(sal_uInt16 * pPageCnt,sal_uInt16 * pPageSt,SwConversionArgs const * pConvArgs)688 uno::Any SwEditShell::SpellContinue(
689 sal_uInt16* pPageCnt, sal_uInt16* pPageSt,
690 SwConversionArgs const *pConvArgs )
691 {
692 uno::Any aRes;
693
694 if ((!pConvArgs && g_pSpellIter->GetSh() != this) ||
695 ( pConvArgs && g_pConvIter->GetSh() != this))
696 return aRes;
697
698 if( pPageCnt && !*pPageCnt )
699 {
700 sal_uInt16 nEndPage = GetLayout()->GetPageNum();
701 nEndPage += nEndPage * 10 / 100;
702 *pPageCnt = nEndPage;
703 if( nEndPage )
704 ::StartProgress( STR_STATSTR_SPELL, 0, nEndPage, GetDoc()->GetDocShell() );
705 }
706
707 OSL_ENSURE( pConvArgs || g_pSpellIter, "SpellIter missing" );
708 OSL_ENSURE( !pConvArgs || g_pConvIter, "ConvIter missing" );
709 //JP 18.07.95: prevent displaying selection on error messages. NO StartAction so that all
710 // Paints are also disabled.
711 ++mnStartAction;
712 OUString aRet;
713 uno::Reference< uno::XInterface > xRet;
714 if (pConvArgs)
715 {
716 g_pConvIter->Continue( pPageCnt, pPageSt ) >>= aRet;
717 aRes <<= aRet;
718 }
719 else
720 {
721 g_pSpellIter->Continue( pPageCnt, pPageSt ) >>= xRet;
722 aRes <<= xRet;
723 }
724 --mnStartAction;
725
726 if( !aRet.isEmpty() || xRet.is() )
727 {
728 // then make awt::Selection again visible
729 StartAction();
730 EndAction();
731 }
732 return aRes;
733 }
734
735 /* Interactive Hyphenation (BP 10.03.93)
736 *
737 * 1) HyphStart
738 * - Revoke all Selections
739 * - Save current Cursor
740 * - if no selections existent:
741 * - create new selection reaching until document end
742 * 2) HyphContinue
743 * - add nLastHyphLen onto SelectionStart
744 * - iterate over all selected areas
745 * - pDoc->Hyphenate() iterates over all Nodes of a selection
746 * - pTextNode->Hyphenate() calls SwTextFrame::Hyphenate of the EditShell
747 * - SwTextFrame:Hyphenate() iterates over all rows of the Pam
748 * - LineIter::Hyphenate() sets the Hyphenator and the Pam based on
749 * the to be separated word.
750 * - Returns true if there is a hyphenation and false if the Pam is processed.
751 * - If true, show the selected word and set nLastHyphLen.
752 * - If false, delete current selection and select next one. Returns HYPH_OK if no more.
753 * 3) InsertSoftHyph (might be called by UI if needed)
754 * - Place current cursor and add attribute.
755 * 4) HyphEnd
756 * - Restore old cursor, EndAction
757 */
HyphStart(SwDocPositions eStart,SwDocPositions eEnd)758 void SwEditShell::HyphStart( SwDocPositions eStart, SwDocPositions eEnd )
759 {
760 // do not hyphenate if interactive hyphenation is active elsewhere
761 if (!g_pHyphIter)
762 {
763 g_pHyphIter = new SwHyphIter;
764 g_pHyphIter->Start( this, eStart, eEnd );
765 }
766 }
767
768 /// restore selections
HyphEnd()769 void SwEditShell::HyphEnd()
770 {
771 assert(g_pHyphIter);
772 if (g_pHyphIter->GetSh() == this)
773 {
774 g_pHyphIter->End();
775 delete g_pHyphIter;
776 g_pHyphIter = nullptr;
777 }
778 }
779
780 /// @returns HYPH_CONTINUE if hyphenation, HYPH_OK if selected area was processed.
781 uno::Reference< uno::XInterface >
HyphContinue(sal_uInt16 * pPageCnt,sal_uInt16 * pPageSt)782 SwEditShell::HyphContinue( sal_uInt16* pPageCnt, sal_uInt16* pPageSt )
783 {
784 assert(g_pHyphIter);
785 if (g_pHyphIter->GetSh() != this)
786 return nullptr;
787
788 if( pPageCnt && !*pPageCnt && !*pPageSt )
789 {
790 sal_uInt16 nEndPage = GetLayout()->GetPageNum();
791 nEndPage += nEndPage * 10 / 100;
792 if( nEndPage > 14 )
793 {
794 *pPageCnt = nEndPage;
795 ::StartProgress( STR_STATSTR_HYPHEN, 0, nEndPage, GetDoc()->GetDocShell());
796 }
797 else // here we once and for all suppress StatLineStartPercent
798 *pPageSt = 1;
799 }
800
801 //JP 18.07.95: prevent displaying selection on error messages. NO StartAction so that all
802 // Paints are also disabled.
803 ++mnStartAction;
804 uno::Reference< uno::XInterface > xRet;
805 g_pHyphIter->Continue( pPageCnt, pPageSt ) >>= xRet;
806 --mnStartAction;
807
808 if( xRet.is() )
809 g_pHyphIter->ShowSelection();
810
811 return xRet;
812 }
813
814 /** Insert soft hyphen
815 *
816 * @param nHyphPos Offset in the to be separated word
817 */
InsertSoftHyph(const sal_Int32 nHyphPos)818 void SwEditShell::InsertSoftHyph( const sal_Int32 nHyphPos )
819 {
820 assert(g_pHyphIter);
821 g_pHyphIter->InsertSoftHyph( nHyphPos );
822 }
823
824 /// ignore hyphenation
HyphIgnore()825 void SwEditShell::HyphIgnore()
826 {
827 assert(g_pHyphIter);
828 //JP 18.07.95: prevent displaying selection on error messages. NO StartAction so that all
829 // Paints are also disabled.
830 ++mnStartAction;
831 g_pHyphIter->Ignore();
832 --mnStartAction;
833
834 g_pHyphIter->ShowSelection();
835 }
836
HandleCorrectionError(const OUString & aText,SwPosition aPos,sal_Int32 nBegin,sal_Int32 nLen,const Point * pPt,SwRect & rSelectRect)837 void SwEditShell::HandleCorrectionError(const OUString& aText, SwPosition aPos, sal_Int32 nBegin,
838 sal_Int32 nLen, const Point* pPt,
839 SwRect& rSelectRect)
840 {
841 // save the start and end positions of the line and the starting point
842 Push();
843 LeftMargin();
844 const sal_Int32 nLineStart = GetCursor()->GetPoint()->nContent.GetIndex();
845 RightMargin();
846 const sal_Int32 nLineEnd = GetCursor()->GetPoint()->nContent.GetIndex();
847 Pop(PopMode::DeleteCurrent);
848
849 // make sure the selection build later from the data below does
850 // not "in word" character to the left and right in order to
851 // preserve those. Therefore count those "in words" in order to
852 // modify the selection accordingly.
853 const sal_Unicode* pChar = aText.getStr();
854 sal_Int32 nLeft = 0;
855 while (*pChar++ == CH_TXTATR_INWORD)
856 ++nLeft;
857 pChar = aText.getLength() ? aText.getStr() + aText.getLength() - 1 : nullptr;
858 sal_Int32 nRight = 0;
859 while (pChar && *pChar-- == CH_TXTATR_INWORD)
860 ++nRight;
861
862 aPos.nContent = nBegin + nLeft;
863 SwPaM* pCursor = GetCursor();
864 *pCursor->GetPoint() = aPos;
865 pCursor->SetMark();
866 ExtendSelection( true, nLen - nLeft - nRight );
867 // don't determine the rectangle in the current line
868 const sal_Int32 nWordStart = (nBegin + nLeft) < nLineStart ? nLineStart : nBegin + nLeft;
869 // take one less than the line end - otherwise the next line would be calculated
870 const sal_Int32 nWordEnd = (nBegin + nLen - nLeft - nRight) > nLineEnd
871 ? nLineEnd : (nBegin + nLen - nLeft - nRight);
872 Push();
873 pCursor->DeleteMark();
874 SwIndex& rContent = GetCursor()->GetPoint()->nContent;
875 rContent = nWordStart;
876 SwRect aStartRect;
877 SwCursorMoveState aState;
878 aState.m_bRealWidth = true;
879 SwContentNode* pContentNode = pCursor->GetContentNode();
880 std::pair<Point, bool> tmp;
881 if (pPt)
882 {
883 tmp.first = *pPt;
884 tmp.second = false;
885 }
886 SwContentFrame *const pContentFrame = pContentNode->getLayoutFrame(GetLayout(), pCursor->GetPoint(), pPt ? &tmp : nullptr);
887
888 pContentFrame->GetCharRect( aStartRect, *pCursor->GetPoint(), &aState );
889 rContent = nWordEnd - 1;
890 SwRect aEndRect;
891 pContentFrame->GetCharRect( aEndRect, *pCursor->GetPoint(),&aState );
892 rSelectRect = aStartRect.Union( aEndRect );
893 Pop(PopMode::DeleteCurrent);
894 }
895
896 /** Get a list of potential corrections for misspelled word.
897 *
898 * If empty, word is unknown but there are no corrections available.
899 * If NULL then the word is not misspelled but correct.
900 *
901 * @brief SwEditShell::GetCorrection
902 * @return list or NULL pointer
903 */
904 uno::Reference< XSpellAlternatives >
GetCorrection(const Point * pPt,SwRect & rSelectRect)905 SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect )
906 {
907 uno::Reference< XSpellAlternatives > xSpellAlt;
908
909 if( IsTableMode() )
910 return nullptr;
911 SwPaM* pCursor = GetCursor();
912 SwPosition aPos( *pCursor->GetPoint() );
913 SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
914 SwTextNode *pNode = nullptr;
915 SwWrongList *pWrong = nullptr;
916 if (pPt && GetLayout()->GetModelPositionForViewPoint( &aPos, *const_cast<Point*>(pPt), &eTmpState ))
917 pNode = aPos.nNode.GetNode().GetTextNode();
918 if (nullptr == pNode)
919 pNode = pCursor->GetNode().GetTextNode();
920 if (nullptr != pNode)
921 pWrong = pNode->GetWrong();
922 if (nullptr != pWrong && !pNode->IsInProtectSect())
923 {
924 sal_Int32 nBegin = aPos.nContent.GetIndex();
925 sal_Int32 nLen = 1;
926 if (pWrong->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin))
927 {
928 const OUString aText(pNode->GetText().copy(nBegin, nLen));
929 OUString aWord = aText.replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "")
930 .replaceAll(OUStringChar(CH_TXTATR_INWORD), "");
931
932 uno::Reference< XSpellChecker1 > xSpell( ::GetSpellChecker() );
933 if( xSpell.is() )
934 {
935 LanguageType eActLang = pNode->GetLang( nBegin, nLen );
936 if( xSpell->hasLanguage( static_cast<sal_uInt16>(eActLang) ))
937 {
938 // restrict the maximal number of suggestions displayed
939 // in the context menu.
940 // Note: That could of course be done by clipping the
941 // resulting sequence but the current third party
942 // implementations result differs greatly if the number of
943 // suggestions to be returned gets changed. Statistically
944 // it gets much better if told to return e.g. only 7 strings
945 // than returning e.g. 16 suggestions and using only the
946 // first 7. Thus we hand down the value to use to that
947 // implementation here by providing an additional parameter.
948 Sequence< PropertyValue > aPropVals(1);
949 PropertyValue &rVal = aPropVals.getArray()[0];
950 rVal.Name = UPN_MAX_NUMBER_OF_SUGGESTIONS;
951 rVal.Value <<= sal_Int16(7);
952
953 xSpellAlt = xSpell->spell( aWord, static_cast<sal_uInt16>(eActLang), aPropVals );
954 }
955 }
956
957 if ( xSpellAlt.is() ) // error found?
958 {
959 HandleCorrectionError( aText, aPos, nBegin, nLen, pPt, rSelectRect );
960 }
961 }
962 }
963 return xSpellAlt;
964 }
965
GetGrammarCorrection(linguistic2::ProofreadingResult & rResult,sal_Int32 & rErrorPosInText,sal_Int32 & rErrorIndexInResult,uno::Sequence<OUString> & rSuggestions,const Point * pPt,SwRect & rSelectRect)966 bool SwEditShell::GetGrammarCorrection(
967 linguistic2::ProofreadingResult /*out*/ &rResult, // the complete result
968 sal_Int32 /*out*/ &rErrorPosInText, // offset of error position in string that was grammar checked...
969 sal_Int32 /*out*/ &rErrorIndexInResult, // index of error in rResult.aGrammarErrors
970 uno::Sequence< OUString > /*out*/ &rSuggestions, // suggestions to be used for the error found
971 const Point *pPt, SwRect &rSelectRect )
972 {
973 bool bRes = false;
974
975 if( IsTableMode() )
976 return bRes;
977
978 SwPaM* pCursor = GetCursor();
979 SwPosition aPos( *pCursor->GetPoint() );
980 SwCursorMoveState eTmpState( CursorMoveState::SetOnlyText );
981 SwTextNode *pNode = nullptr;
982 SwGrammarMarkUp *pWrong = nullptr;
983 if (pPt && GetLayout()->GetModelPositionForViewPoint( &aPos, *const_cast<Point*>(pPt), &eTmpState ))
984 pNode = aPos.nNode.GetNode().GetTextNode();
985 if (nullptr == pNode)
986 pNode = pCursor->GetNode().GetTextNode();
987 if (nullptr != pNode)
988 pWrong = pNode->GetGrammarCheck();
989 if (nullptr != pWrong && !pNode->IsInProtectSect())
990 {
991 sal_Int32 nBegin = aPos.nContent.GetIndex();
992 sal_Int32 nLen = 1;
993 if (pWrong->InWrongWord(nBegin, nLen))
994 {
995 const OUString aText(pNode->GetText().copy(nBegin, nLen));
996
997 uno::Reference< linguistic2::XProofreadingIterator > xGCIterator( mxDoc->GetGCIterator() );
998 if (xGCIterator.is())
999 {
1000 uno::Reference< lang::XComponent > xDoc = mxDoc->GetDocShell()->GetBaseModel();
1001
1002 // Expand the string:
1003 const ModelToViewHelper aConversionMap(*pNode, GetLayout());
1004 const OUString& aExpandText = aConversionMap.getViewText();
1005 // get XFlatParagraph to use...
1006 uno::Reference< text::XFlatParagraph > xFlatPara = new SwXFlatParagraph( *pNode, aExpandText, aConversionMap );
1007
1008 // get error position of cursor in XFlatParagraph
1009 rErrorPosInText = aConversionMap.ConvertToViewPosition( nBegin );
1010
1011 const sal_Int32 nStartOfSentence = aConversionMap.ConvertToViewPosition( pWrong->getSentenceStart( nBegin ) );
1012 const sal_Int32 nEndOfSentence = aConversionMap.ConvertToViewPosition( pWrong->getSentenceEnd( nBegin ) );
1013
1014 rResult = xGCIterator->checkSentenceAtPosition(
1015 xDoc, xFlatPara, aExpandText, lang::Locale(), nStartOfSentence,
1016 nEndOfSentence == COMPLETE_STRING ? aExpandText.getLength() : nEndOfSentence,
1017 rErrorPosInText );
1018 bRes = true;
1019
1020 // get suggestions to use for the specific error position
1021 rSuggestions.realloc( 0 );
1022 // return suggestions for first error that includes the given error position
1023 auto pError = std::find_if(rResult.aErrors.begin(), rResult.aErrors.end(),
1024 [rErrorPosInText, nLen](const linguistic2::SingleProofreadingError &rError) {
1025 return rError.nErrorStart <= rErrorPosInText
1026 && rErrorPosInText + nLen <= rError.nErrorStart + rError.nErrorLength; });
1027 if (pError != rResult.aErrors.end())
1028 {
1029 rSuggestions = pError->aSuggestions;
1030 rErrorIndexInResult = static_cast<sal_Int32>(std::distance(rResult.aErrors.begin(), pError));
1031 }
1032 }
1033
1034 if (rResult.aErrors.hasElements()) // error found?
1035 {
1036 HandleCorrectionError( aText, aPos, nBegin, nLen, pPt, rSelectRect );
1037 }
1038 }
1039 }
1040
1041 return bRes;
1042 }
1043
SpellSentence(svx::SpellPortions & rPortions,bool bIsGrammarCheck)1044 bool SwEditShell::SpellSentence(svx::SpellPortions& rPortions, bool bIsGrammarCheck)
1045 {
1046 OSL_ENSURE( g_pSpellIter, "SpellIter missing" );
1047 if (!g_pSpellIter)
1048 return false;
1049 bool bRet = g_pSpellIter->SpellSentence(rPortions, bIsGrammarCheck);
1050
1051 // make Selection visible - this should simply move the
1052 // cursor to the end of the sentence
1053 StartAction();
1054 EndAction();
1055 return bRet;
1056 }
1057
1058 ///make SpellIter start with the current sentence when called next time
PutSpellingToSentenceStart()1059 void SwEditShell::PutSpellingToSentenceStart()
1060 {
1061 OSL_ENSURE( g_pSpellIter, "SpellIter missing" );
1062 if (!g_pSpellIter)
1063 return;
1064 g_pSpellIter->ToSentenceStart();
1065 }
1066
lcl_CountRedlines(const svx::SpellPortions & rLastPortions)1067 static sal_uInt32 lcl_CountRedlines(const svx::SpellPortions& rLastPortions)
1068 {
1069 return static_cast<sal_uInt32>(std::count_if(rLastPortions.begin(), rLastPortions.end(),
1070 [](const svx::SpellPortion& rPortion) { return rPortion.bIsHidden; }));
1071 }
1072
MoveContinuationPosToEndOfCheckedSentence()1073 void SwEditShell::MoveContinuationPosToEndOfCheckedSentence()
1074 {
1075 // give hint that continuation position for spell/grammar checking is
1076 // at the end of this sentence
1077 if (g_pSpellIter)
1078 {
1079 g_pSpellIter->SetCurr( new SwPosition( *g_pSpellIter->GetCurrX() ) );
1080 }
1081 }
1082
ApplyChangedSentence(const svx::SpellPortions & rNewPortions,bool bRecheck)1083 void SwEditShell::ApplyChangedSentence(const svx::SpellPortions& rNewPortions, bool bRecheck)
1084 {
1085 // Note: rNewPortions.size() == 0 is valid and happens when the whole
1086 // sentence got removed in the dialog
1087
1088 OSL_ENSURE( g_pSpellIter, "SpellIter missing" );
1089 if (!g_pSpellIter ||
1090 g_pSpellIter->GetLastPortions().empty()) // no portions -> no text to be changed
1091 return;
1092
1093 const SpellPortions& rLastPortions = g_pSpellIter->GetLastPortions();
1094 const SpellContentPositions rLastPositions = g_pSpellIter->GetLastPositions();
1095 OSL_ENSURE(!rLastPortions.empty() &&
1096 rLastPortions.size() == rLastPositions.size(),
1097 "last vectors of spelling results are not set or not equal");
1098
1099 // iterate over the new portions, beginning at the end to take advantage of the previously
1100 // saved content positions
1101
1102 mxDoc->GetIDocumentUndoRedo().StartUndo( SwUndoId::UI_TEXT_CORRECTION, nullptr );
1103 StartAction();
1104
1105 SwPaM *pCursor = GetCursor();
1106 // save cursor position (which should be at the end of the current sentence)
1107 // for later restoration
1108 Push();
1109
1110 sal_uInt32 nRedlinePortions = lcl_CountRedlines(rLastPortions);
1111 if((rLastPortions.size() - nRedlinePortions) == rNewPortions.size())
1112 {
1113 OSL_ENSURE( !rNewPortions.empty(), "rNewPortions should not be empty here" );
1114 OSL_ENSURE( !rLastPortions.empty(), "rLastPortions should not be empty here" );
1115 OSL_ENSURE( !rLastPositions.empty(), "rLastPositions should not be empty here" );
1116
1117 // the simple case: the same number of elements on both sides
1118 // each changed element has to be applied to the corresponding source element
1119 svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
1120 SpellPortions::const_iterator aCurrentOldPortion = rLastPortions.end();
1121 SpellContentPositions::const_iterator aCurrentOldPosition = rLastPositions.end();
1122 do
1123 {
1124 --aCurrentNewPortion;
1125 --aCurrentOldPortion;
1126 --aCurrentOldPosition;
1127 //jump over redline portions
1128 while(aCurrentOldPortion->bIsHidden)
1129 {
1130 if (aCurrentOldPortion != rLastPortions.begin() &&
1131 aCurrentOldPosition != rLastPositions.begin())
1132 {
1133 --aCurrentOldPortion;
1134 --aCurrentOldPosition;
1135 }
1136 else
1137 {
1138 OSL_FAIL("ApplyChangedSentence: iterator positions broken" );
1139 break;
1140 }
1141 }
1142 if ( !pCursor->HasMark() )
1143 pCursor->SetMark();
1144 pCursor->GetPoint()->nContent = aCurrentOldPosition->nLeft;
1145 pCursor->GetMark()->nContent = aCurrentOldPosition->nRight;
1146 sal_uInt16 nScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
1147 sal_uInt16 nLangWhichId = RES_CHRATR_LANGUAGE;
1148 switch(nScriptType)
1149 {
1150 case css::i18n::ScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
1151 case css::i18n::ScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
1152 }
1153 if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
1154 {
1155 // change text ...
1156 // ... and apply language if necessary
1157 if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
1158 SetAttrItem( SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId) );
1159 mxDoc->getIDocumentContentOperations().ReplaceRange(*pCursor, aCurrentNewPortion->sText, false);
1160 }
1161 else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
1162 {
1163 // apply language
1164 SetAttrItem( SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId) );
1165 }
1166 else if( aCurrentNewPortion->bIgnoreThisError )
1167 {
1168 // add the 'ignore' markup to the TextNode's grammar ignore markup list
1169 IgnoreGrammarErrorAt( *pCursor );
1170 OSL_FAIL("TODO: add ignore mark to text node");
1171 }
1172 }
1173 while(aCurrentNewPortion != rNewPortions.begin());
1174 }
1175 else
1176 {
1177 OSL_ENSURE( !rLastPositions.empty(), "rLastPositions should not be empty here" );
1178
1179 // select the complete sentence
1180 SpellContentPositions::const_iterator aCurrentEndPosition = rLastPositions.end();
1181 --aCurrentEndPosition;
1182 SpellContentPositions::const_iterator aCurrentStartPosition = rLastPositions.begin();
1183 pCursor->GetPoint()->nContent = aCurrentStartPosition->nLeft;
1184 pCursor->GetMark()->nContent = aCurrentEndPosition->nRight;
1185
1186 // delete the sentence completely
1187 mxDoc->getIDocumentContentOperations().DeleteAndJoin(*pCursor);
1188 for(const auto& rCurrentNewPortion : rNewPortions)
1189 {
1190 // set the language attribute
1191 SvtScriptType nScriptType = GetScriptType();
1192 sal_uInt16 nLangWhichId = RES_CHRATR_LANGUAGE;
1193 switch(nScriptType)
1194 {
1195 case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
1196 case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
1197 default: break;
1198 }
1199 SfxItemSet aSet(GetAttrPool(), {{nLangWhichId, nLangWhichId}});
1200 GetCurAttr( aSet );
1201 const SvxLanguageItem& rLang = static_cast<const SvxLanguageItem& >(aSet.Get(nLangWhichId));
1202 if(rLang.GetLanguage() != rCurrentNewPortion.eLanguage)
1203 SetAttrItem( SvxLanguageItem(rCurrentNewPortion.eLanguage, nLangWhichId) );
1204 // insert the new string
1205 mxDoc->getIDocumentContentOperations().InsertString(*pCursor, rCurrentNewPortion.sText);
1206
1207 // set the cursor to the end of the inserted string
1208 *pCursor->Start() = *pCursor->End();
1209 }
1210 }
1211
1212 // restore cursor to the end of the sentence
1213 // (will work also if the sentence length has changed,
1214 // since cursors get updated automatically!)
1215 Pop(PopMode::DeleteCurrent);
1216
1217 // collapse cursor to the end of the modified sentence
1218 *pCursor->Start() = *pCursor->End();
1219 if (bRecheck)
1220 {
1221 // in grammar check the current sentence has to be checked again
1222 GoStartSentence();
1223 }
1224 // set continuation position for spell/grammar checking to the end of this sentence
1225 g_pSpellIter->SetCurr( new SwPosition(*pCursor->Start()) );
1226
1227 mxDoc->GetIDocumentUndoRedo().EndUndo( SwUndoId::UI_TEXT_CORRECTION, nullptr );
1228 EndAction();
1229
1230 }
1231 /** Collect all deleted redlines of the current text node
1232 * beginning at the start of the cursor position
1233 */
lcl_CollectDeletedRedlines(SwEditShell const * pSh)1234 static SpellContentPositions lcl_CollectDeletedRedlines(SwEditShell const * pSh)
1235 {
1236 SpellContentPositions aRedlines;
1237 SwDoc* pDoc = pSh->GetDoc();
1238 const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( pDoc->getIDocumentRedlineAccess().GetRedlineFlags() );
1239 if ( bShowChg )
1240 {
1241 SwPaM *pCursor = pSh->GetCursor();
1242 const SwPosition* pStartPos = pCursor->Start();
1243 const SwTextNode* pTextNode = pCursor->GetNode().GetTextNode();
1244
1245 SwRedlineTable::size_type nAct = pDoc->getIDocumentRedlineAccess().GetRedlinePos( *pTextNode, RedlineType::Any );
1246 const sal_Int32 nStartIndex = pStartPos->nContent.GetIndex();
1247 for ( ; nAct < pDoc->getIDocumentRedlineAccess().GetRedlineTable().size(); nAct++ )
1248 {
1249 const SwRangeRedline* pRed = pDoc->getIDocumentRedlineAccess().GetRedlineTable()[ nAct ];
1250
1251 if ( pRed->Start()->nNode > pTextNode->GetIndex() )
1252 break;
1253
1254 if( RedlineType::Delete == pRed->GetType() )
1255 {
1256 sal_Int32 nStart_, nEnd_;
1257 pRed->CalcStartEnd( pTextNode->GetIndex(), nStart_, nEnd_ );
1258 sal_Int32 nStart = nStart_;
1259 sal_Int32 nEnd = nEnd_;
1260 if(nStart >= nStartIndex || nEnd >= nStartIndex)
1261 {
1262 SpellContentPosition aAdd;
1263 aAdd.nLeft = nStart;
1264 aAdd.nRight = nEnd;
1265 aRedlines.push_back(aAdd);
1266 }
1267 }
1268 }
1269 }
1270 return aRedlines;
1271 }
1272
1273 /// remove the redline positions after the current selection
lcl_CutRedlines(SpellContentPositions & aDeletedRedlines,SwEditShell const * pSh)1274 static void lcl_CutRedlines( SpellContentPositions& aDeletedRedlines, SwEditShell const * pSh )
1275 {
1276 if(!aDeletedRedlines.empty())
1277 {
1278 SwPaM *pCursor = pSh->GetCursor();
1279 const SwPosition* pEndPos = pCursor->End();
1280 const sal_Int32 nEnd = pEndPos->nContent.GetIndex();
1281 while(!aDeletedRedlines.empty() &&
1282 aDeletedRedlines.back().nLeft > nEnd)
1283 {
1284 aDeletedRedlines.pop_back();
1285 }
1286 }
1287 }
1288
lcl_FindNextDeletedRedline(const SpellContentPositions & rDeletedRedlines,sal_Int32 nSearchFrom)1289 static SpellContentPosition lcl_FindNextDeletedRedline(
1290 const SpellContentPositions& rDeletedRedlines,
1291 sal_Int32 nSearchFrom )
1292 {
1293 SpellContentPosition aRet;
1294 aRet.nLeft = aRet.nRight = SAL_MAX_INT32;
1295 if(!rDeletedRedlines.empty())
1296 {
1297 auto aIter = std::find_if_not(rDeletedRedlines.begin(), rDeletedRedlines.end(),
1298 [nSearchFrom](const SpellContentPosition& rPos) { return rPos.nLeft < nSearchFrom; });
1299 if (aIter != rDeletedRedlines.end())
1300 aRet = *aIter;
1301 }
1302 return aRet;
1303 }
1304
SpellSentence(svx::SpellPortions & rPortions,bool bIsGrammarCheck)1305 bool SwSpellIter::SpellSentence(svx::SpellPortions& rPortions, bool bIsGrammarCheck)
1306 {
1307 bool bRet = false;
1308 m_aLastPortions.clear();
1309 m_aLastPositions.clear();
1310
1311 SwEditShell *pMySh = GetSh();
1312 if( !pMySh )
1313 return false;
1314
1315 OSL_ENSURE( GetEnd(), "SwSpellIter::SpellSentence without Start?");
1316
1317 uno::Reference< XSpellAlternatives > xSpellRet;
1318 linguistic2::ProofreadingResult aGrammarResult;
1319 bool bGoOn = true;
1320 bool bGrammarErrorFound = false;
1321 do {
1322 SwPaM *pCursor = pMySh->GetCursor();
1323 if ( !pCursor->HasMark() )
1324 pCursor->SetMark();
1325
1326 *pCursor->GetPoint() = *GetCurr();
1327 *pCursor->GetMark() = *GetEnd();
1328
1329 if (m_bBackToStartOfSentence)
1330 {
1331 pMySh->GoStartSentence();
1332 m_bBackToStartOfSentence = false;
1333 }
1334 uno::Any aSpellRet = pMySh->GetDoc()->Spell(*pCursor, m_xSpeller, nullptr, nullptr,
1335 bIsGrammarCheck, pMySh->GetLayout());
1336 aSpellRet >>= xSpellRet;
1337 aSpellRet >>= aGrammarResult;
1338 bGoOn = GetCursorCnt() > 1;
1339 bGrammarErrorFound = aGrammarResult.aErrors.hasElements();
1340 if( xSpellRet.is() || bGrammarErrorFound )
1341 {
1342 bGoOn = false;
1343 SwPosition* pNewPoint = new SwPosition( *pCursor->GetPoint() );
1344 SwPosition* pNewMark = new SwPosition( *pCursor->GetMark() );
1345
1346 SetCurr( pNewPoint );
1347 SetCurrX( pNewMark );
1348 }
1349 if( bGoOn )
1350 {
1351 pMySh->Pop(SwCursorShell::PopMode::DeleteCurrent);
1352 pCursor = pMySh->GetCursor();
1353 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
1354 pCursor->Exchange();
1355 SwPosition* pNew = new SwPosition( *pCursor->GetPoint() );
1356 SetStart( pNew );
1357 pNew = new SwPosition( *pCursor->GetMark() );
1358 SetEnd( pNew );
1359 pNew = new SwPosition( *GetStart() );
1360 SetCurr( pNew );
1361 pNew = new SwPosition( *pNew );
1362 SetCurrX( pNew );
1363 pCursor->SetMark();
1364 --GetCursorCnt();
1365 }
1366 } while ( bGoOn );
1367
1368 if(xSpellRet.is() || bGrammarErrorFound)
1369 {
1370 // an error has been found
1371 // To fill the spell portions the beginning of the sentence has to be found
1372 SwPaM *pCursor = pMySh->GetCursor();
1373 // set the mark to the right if necessary
1374 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
1375 pCursor->Exchange();
1376 // the cursor has to be collapsed on the left to go to the start of the sentence - if sentence ends inside of the error
1377 pCursor->DeleteMark();
1378 pCursor->SetMark();
1379 bool bStartSent = pMySh->GoStartSentence();
1380 SpellContentPositions aDeletedRedlines = lcl_CollectDeletedRedlines(pMySh);
1381 if(bStartSent)
1382 {
1383 // create a portion from the start part
1384 AddPortion(nullptr, nullptr, aDeletedRedlines);
1385 }
1386 // Set the cursor to the error already found
1387 *pCursor->GetPoint() = *GetCurrX();
1388 *pCursor->GetMark() = *GetCurr();
1389 AddPortion(xSpellRet, &aGrammarResult, aDeletedRedlines);
1390
1391 // save the end position of the error to continue from here
1392 SwPosition aSaveStartPos = *pCursor->End();
1393 // determine the end of the current sentence
1394 if ( *pCursor->GetPoint() < *pCursor->GetMark() )
1395 pCursor->Exchange();
1396 // again collapse to start marking after the end of the error
1397 pCursor->DeleteMark();
1398 pCursor->SetMark();
1399
1400 pMySh->GoEndSentence();
1401 if( bGrammarErrorFound )
1402 {
1403 const ModelToViewHelper aConversionMap(static_cast<SwTextNode&>(pCursor->GetNode()), pMySh->GetLayout());
1404 const OUString& aExpandText = aConversionMap.getViewText();
1405 sal_Int32 nSentenceEnd =
1406 aConversionMap.ConvertToViewPosition( aGrammarResult.nBehindEndOfSentencePosition );
1407 // remove trailing space
1408 if( aExpandText[nSentenceEnd - 1] == ' ' )
1409 --nSentenceEnd;
1410 if( pCursor->End()->nContent.GetIndex() < nSentenceEnd )
1411 {
1412 pCursor->End()->nContent.Assign(
1413 pCursor->End()->nNode.GetNode().GetContentNode(), nSentenceEnd);
1414 }
1415 }
1416
1417 lcl_CutRedlines( aDeletedRedlines, pMySh );
1418 // save the 'global' end of the spellchecking
1419 const SwPosition aSaveEndPos = *GetEnd();
1420 // set the sentence end as 'local' end
1421 SetEnd( new SwPosition( *pCursor->End() ));
1422
1423 *pCursor->GetPoint() = aSaveStartPos;
1424 *pCursor->GetMark() = *GetEnd();
1425 // now the rest of the sentence has to be searched for errors
1426 // for each error the non-error text between the current and the last error has
1427 // to be added to the portions - if necessary broken into same-language-portions
1428 if( !bGrammarErrorFound ) //in grammar check there's only one error returned
1429 {
1430 do
1431 {
1432 xSpellRet = nullptr;
1433 // don't search for grammar errors here anymore!
1434 pMySh->GetDoc()->Spell(*pCursor, m_xSpeller, nullptr, nullptr, false,
1435 pMySh->GetLayout())
1436 >>= xSpellRet;
1437 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
1438 pCursor->Exchange();
1439 SetCurr( new SwPosition( *pCursor->GetPoint() ));
1440 SetCurrX( new SwPosition( *pCursor->GetMark() ));
1441
1442 // if an error has been found go back to the text preceding the error
1443 if(xSpellRet.is())
1444 {
1445 *pCursor->GetPoint() = aSaveStartPos;
1446 *pCursor->GetMark() = *GetCurr();
1447 }
1448 // add the portion
1449 AddPortion(nullptr, nullptr, aDeletedRedlines);
1450
1451 if(xSpellRet.is())
1452 {
1453 *pCursor->GetPoint() = *GetCurr();
1454 *pCursor->GetMark() = *GetCurrX();
1455 AddPortion(xSpellRet, nullptr, aDeletedRedlines);
1456 // move the cursor to the end of the error string
1457 *pCursor->GetPoint() = *GetCurrX();
1458 // and save the end of the error as new start position
1459 aSaveStartPos = *GetCurrX();
1460 // and the end of the sentence
1461 *pCursor->GetMark() = *GetEnd();
1462 }
1463 // if the end of the sentence has already been reached then break here
1464 if(*GetCurrX() >= *GetEnd())
1465 break;
1466 }
1467 while(xSpellRet.is());
1468 }
1469 else
1470 {
1471 // go to the end of sentence as the grammar check returned it
1472 // at this time the Point is behind the grammar error
1473 // and the mark points to the sentence end as
1474 if ( *pCursor->GetPoint() < *pCursor->GetMark() )
1475 pCursor->Exchange();
1476 }
1477
1478 // the part between the last error and the end of the sentence has to be added
1479 *pMySh->GetCursor()->GetPoint() = *GetEnd();
1480 if(*GetCurrX() < *GetEnd())
1481 {
1482 AddPortion(nullptr, nullptr, aDeletedRedlines);
1483 }
1484 // set the shell cursor to the end of the sentence to prevent a visible selection
1485 *pCursor->GetMark() = *GetEnd();
1486 if( !bIsGrammarCheck )
1487 {
1488 // set the current position to the end of the sentence
1489 SetCurr( new SwPosition(*GetEnd()) );
1490 }
1491 // restore the 'global' end
1492 SetEnd( new SwPosition(aSaveEndPos) );
1493 rPortions = m_aLastPortions;
1494 bRet = true;
1495 }
1496 else
1497 {
1498 // if no error could be found the selection has to be corrected - at least if it's not in the body
1499 *pMySh->GetCursor()->GetPoint() = *GetEnd();
1500 pMySh->GetCursor()->DeleteMark();
1501 }
1502
1503 return bRet;
1504 }
1505
ToSentenceStart()1506 void SwSpellIter::ToSentenceStart() { m_bBackToStartOfSentence = true; }
1507
lcl_GetLanguage(SwEditShell & rSh)1508 static LanguageType lcl_GetLanguage(SwEditShell& rSh)
1509 {
1510 SvtScriptType nScriptType = rSh.GetScriptType();
1511 sal_uInt16 nLangWhichId = RES_CHRATR_LANGUAGE;
1512
1513 switch(nScriptType)
1514 {
1515 case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
1516 case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
1517 default: break;
1518 }
1519 SfxItemSet aSet(rSh.GetAttrPool(), {{nLangWhichId, nLangWhichId}});
1520 rSh.GetCurAttr( aSet );
1521 const SvxLanguageItem& rLang = static_cast<const SvxLanguageItem& >(aSet.Get(nLangWhichId));
1522 return rLang.GetLanguage();
1523 }
1524
1525 /// create a text portion at the given position
CreatePortion(uno::Reference<XSpellAlternatives> const & xAlt,linguistic2::ProofreadingResult * pGrammarResult,bool bIsField,bool bIsHidden)1526 void SwSpellIter::CreatePortion(uno::Reference< XSpellAlternatives > const & xAlt,
1527 linguistic2::ProofreadingResult* pGrammarResult,
1528 bool bIsField, bool bIsHidden)
1529 {
1530 svx::SpellPortion aPortion;
1531 OUString sText;
1532 GetSh()->GetSelectedText( sText );
1533 if(sText.isEmpty())
1534 return;
1535
1536 // in case of redlined deletions the selection of an error is not the same as the _real_ word
1537 if(xAlt.is())
1538 aPortion.sText = xAlt->getWord();
1539 else if(pGrammarResult)
1540 {
1541 aPortion.bIsGrammarError = true;
1542 if(pGrammarResult->aErrors.hasElements())
1543 {
1544 aPortion.aGrammarError = pGrammarResult->aErrors[0];
1545 aPortion.sText = pGrammarResult->aText.copy( aPortion.aGrammarError.nErrorStart, aPortion.aGrammarError.nErrorLength );
1546 aPortion.xGrammarChecker = pGrammarResult->xProofreader;
1547 auto pProperty = std::find_if(pGrammarResult->aProperties.begin(), pGrammarResult->aProperties.end(),
1548 [](const beans::PropertyValue& rProperty) { return rProperty.Name == "DialogTitle"; });
1549 if (pProperty != pGrammarResult->aProperties.end())
1550 pProperty->Value >>= aPortion.sDialogTitle;
1551 }
1552 }
1553 else
1554 aPortion.sText = sText;
1555 aPortion.eLanguage = lcl_GetLanguage(*GetSh());
1556 aPortion.bIsField = bIsField;
1557 aPortion.bIsHidden = bIsHidden;
1558 aPortion.xAlternatives = xAlt;
1559 SpellContentPosition aPosition;
1560 SwPaM *pCursor = GetSh()->GetCursor();
1561 aPosition.nLeft = pCursor->Start()->nContent.GetIndex();
1562 aPosition.nRight = pCursor->End()->nContent.GetIndex();
1563 m_aLastPortions.push_back(aPortion);
1564 m_aLastPositions.push_back(aPosition);
1565 }
1566
AddPortion(uno::Reference<XSpellAlternatives> const & xAlt,linguistic2::ProofreadingResult * pGrammarResult,const SpellContentPositions & rDeletedRedlines)1567 void SwSpellIter::AddPortion(uno::Reference< XSpellAlternatives > const & xAlt,
1568 linguistic2::ProofreadingResult* pGrammarResult,
1569 const SpellContentPositions& rDeletedRedlines)
1570 {
1571 SwEditShell *pMySh = GetSh();
1572 OUString sText;
1573 pMySh->GetSelectedText( sText );
1574 if(sText.isEmpty())
1575 return;
1576
1577 if(xAlt.is() || pGrammarResult != nullptr)
1578 {
1579 CreatePortion(xAlt, pGrammarResult, false, false);
1580 }
1581 else
1582 {
1583 SwPaM *pCursor = GetSh()->GetCursor();
1584 if ( *pCursor->GetPoint() > *pCursor->GetMark() )
1585 pCursor->Exchange();
1586 // save the start and end positions
1587 SwPosition aStart(*pCursor->GetPoint());
1588 SwPosition aEnd(*pCursor->GetMark());
1589 // iterate over the text to find changes in language
1590 // set the mark equal to the point
1591 *pCursor->GetMark() = aStart;
1592 SwTextNode* pTextNode = pCursor->GetNode().GetTextNode();
1593 LanguageType eStartLanguage = lcl_GetLanguage(*GetSh());
1594 SpellContentPosition aNextRedline = lcl_FindNextDeletedRedline(
1595 rDeletedRedlines, aStart.nContent.GetIndex() );
1596 if( aNextRedline.nLeft == aStart.nContent.GetIndex() )
1597 {
1598 // select until the end of the current redline
1599 const sal_Int32 nEnd = aEnd.nContent.GetIndex() < aNextRedline.nRight ?
1600 aEnd.nContent.GetIndex() : aNextRedline.nRight;
1601 pCursor->GetPoint()->nContent.Assign( pTextNode, nEnd );
1602 CreatePortion(xAlt, pGrammarResult, false, true);
1603 aStart = *pCursor->End();
1604 // search for next redline
1605 aNextRedline = lcl_FindNextDeletedRedline(
1606 rDeletedRedlines, aStart.nContent.GetIndex() );
1607 }
1608 while(*pCursor->GetPoint() < aEnd)
1609 {
1610 // #125786 in table cell with fixed row height the cursor might not move forward
1611 if(!GetSh()->Right(1, CRSR_SKIP_CELLS))
1612 break;
1613
1614 bool bField = false;
1615 // read the character at the current position to check if it's a field
1616 sal_Unicode const cChar =
1617 pTextNode->GetText()[pCursor->GetMark()->nContent.GetIndex()];
1618 if( CH_TXTATR_BREAKWORD == cChar || CH_TXTATR_INWORD == cChar)
1619 {
1620 const SwTextAttr* pTextAttr = pTextNode->GetTextAttrForCharAt(
1621 pCursor->GetMark()->nContent.GetIndex() );
1622 const sal_uInt16 nWhich = pTextAttr
1623 ? pTextAttr->Which()
1624 : RES_TXTATR_END;
1625 switch (nWhich)
1626 {
1627 case RES_TXTATR_FIELD:
1628 case RES_TXTATR_ANNOTATION:
1629 case RES_TXTATR_FTN:
1630 case RES_TXTATR_FLYCNT:
1631 bField = true;
1632 break;
1633 }
1634 }
1635 else if (cChar == CH_TXT_ATR_FORMELEMENT)
1636 {
1637 SwPosition aPos(*pCursor->GetMark());
1638 bField = pMySh->GetDoc()->getIDocumentMarkAccess()->getDropDownFor(aPos);
1639 }
1640
1641 LanguageType eCurLanguage = lcl_GetLanguage(*GetSh());
1642 bool bRedline = aNextRedline.nLeft == pCursor->GetPoint()->nContent.GetIndex();
1643 // create a portion if the next character
1644 // - is a field,
1645 // - is at the beginning of a deleted redline
1646 // - has a different language
1647 if(bField || bRedline || eCurLanguage != eStartLanguage)
1648 {
1649 eStartLanguage = eCurLanguage;
1650 // go one step back - the cursor currently selects the first character
1651 // with a different language
1652 // in the case of redlining it's different
1653 if(eCurLanguage != eStartLanguage || bField)
1654 *pCursor->GetPoint() = *pCursor->GetMark();
1655 // set to the last start
1656 *pCursor->GetMark() = aStart;
1657 // create portion should only be called if a selection exists
1658 // there's no selection if there's a field at the beginning
1659 if(*pCursor->Start() != *pCursor->End())
1660 CreatePortion(xAlt, pGrammarResult, false, false);
1661 aStart = *pCursor->End();
1662 // now export the field - if there is any
1663 if(bField)
1664 {
1665 *pCursor->GetMark() = *pCursor->GetPoint();
1666 GetSh()->Right(1, CRSR_SKIP_CELLS);
1667 CreatePortion(xAlt, pGrammarResult, true, false);
1668 aStart = *pCursor->End();
1669 }
1670 }
1671 // if a redline start then create a portion for it
1672 if(bRedline)
1673 {
1674 *pCursor->GetMark() = *pCursor->GetPoint();
1675 // select until the end of the current redline
1676 const sal_Int32 nEnd = aEnd.nContent.GetIndex() < aNextRedline.nRight ?
1677 aEnd.nContent.GetIndex() : aNextRedline.nRight;
1678 pCursor->GetPoint()->nContent.Assign( pTextNode, nEnd );
1679 CreatePortion(xAlt, pGrammarResult, false, true);
1680 aStart = *pCursor->End();
1681 // search for next redline
1682 aNextRedline = lcl_FindNextDeletedRedline(
1683 rDeletedRedlines, aStart.nContent.GetIndex() );
1684 }
1685 *pCursor->GetMark() = *pCursor->GetPoint();
1686 }
1687 pCursor->SetMark();
1688 *pCursor->GetMark() = aStart;
1689 CreatePortion(xAlt, pGrammarResult, false, false);
1690 }
1691 }
1692
IgnoreGrammarErrorAt(SwPaM & rErrorPosition)1693 void SwEditShell::IgnoreGrammarErrorAt( SwPaM& rErrorPosition )
1694 {
1695 SwTextNode *pNode;
1696 SwWrongList *pWrong;
1697 SwNodeIndex aIdx = rErrorPosition.Start()->nNode;
1698 SwNodeIndex aEndIdx = rErrorPosition.Start()->nNode;
1699 sal_Int32 nStart = rErrorPosition.Start()->nContent.GetIndex();
1700 sal_Int32 nEnd = COMPLETE_STRING;
1701 while( aIdx <= aEndIdx )
1702 {
1703 pNode = aIdx.GetNode().GetTextNode();
1704 if( pNode ) {
1705 if( aIdx == aEndIdx )
1706 nEnd = rErrorPosition.End()->nContent.GetIndex();
1707 pWrong = pNode->GetGrammarCheck();
1708 if( pWrong )
1709 pWrong->RemoveEntry( nStart, nEnd );
1710 pWrong = pNode->GetWrong();
1711 if( pWrong )
1712 pWrong->RemoveEntry( nStart, nEnd );
1713 SwTextFrame::repaintTextFrames( *pNode );
1714 }
1715 ++aIdx;
1716 nStart = 0;
1717 }
1718 }
1719
1720 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1721