xref: /trunk/main/sw/source/core/edit/acorrect.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 
32 #define _STD_VAR_ARRAYS
33 #include <hintids.hxx>
34 
35 #include <svx/svxids.hrc>
36 #include <editeng/langitem.hxx>
37 #include <fmtinfmt.hxx>
38 #include <txtatr.hxx>
39 #include <txtinet.hxx>
40 #include <editsh.hxx>
41 #include <doc.hxx>
42 #include <pam.hxx>
43 #include <ndtxt.hxx>
44 #include <acorrect.hxx>
45 #include <shellio.hxx>
46 #include <swundo.hxx>
47 #include <viscrs.hxx>
48 
49 #include <editeng/acorrcfg.hxx>
50 
51 using namespace ::com::sun::star;
52 
53 
54 class _PaMIntoCrsrShellRing
55 {
56     SwCrsrShell& rSh;
57     SwPaM &rDelPam, &rCrsr;
58     Ring *pPrevDelPam, *pPrevCrsr;
59 
60     void RemoveFromRing( SwPaM& rPam, Ring* pPrev );
61 public:
62     _PaMIntoCrsrShellRing( SwCrsrShell& rSh, SwPaM& rCrsr, SwPaM& rPam );
63     ~_PaMIntoCrsrShellRing();
64 };
65 
66 _PaMIntoCrsrShellRing::_PaMIntoCrsrShellRing( SwCrsrShell& rCSh,
67                                             SwPaM& rShCrsr, SwPaM& rPam )
68     : rSh( rCSh ), rDelPam( rPam ), rCrsr( rShCrsr )
69 {
70     SwPaM* pShCrsr = rSh._GetCrsr();
71 
72     pPrevDelPam = rDelPam.GetPrev();
73     pPrevCrsr = rCrsr.GetPrev();
74 
75     rDelPam.MoveRingTo( pShCrsr );
76     rCrsr.MoveRingTo( pShCrsr );
77 }
78 _PaMIntoCrsrShellRing::~_PaMIntoCrsrShellRing()
79 {
80     // und den Pam wieder herausnehmen:
81     RemoveFromRing( rDelPam, pPrevDelPam );
82     RemoveFromRing( rCrsr, pPrevCrsr );
83 }
84 void _PaMIntoCrsrShellRing::RemoveFromRing( SwPaM& rPam, Ring* pPrev )
85 {
86     Ring *p, *pNext = (Ring*)&rPam;
87     do {
88         p = pNext;
89         pNext = p->GetNext();
90         p->MoveTo( &rPam );
91     } while( p != pPrev );
92 }
93 
94 
95 SwAutoCorrDoc::SwAutoCorrDoc( SwEditShell& rEditShell, SwPaM& rPam,
96                                 sal_Unicode cIns )
97     : rEditSh( rEditShell ), rCrsr( rPam ), pIdx( 0 )
98     , m_nEndUndoCounter(0)
99     , bUndoIdInitialized( cIns ? false : true )
100 {
101 }
102 
103 
104 SwAutoCorrDoc::~SwAutoCorrDoc()
105 {
106     for (int i = 0; i < m_nEndUndoCounter; ++i)
107     {
108         rEditSh.EndUndo();
109     }
110     delete pIdx;
111 }
112 
113 void SwAutoCorrDoc::DeleteSel( SwPaM& rDelPam )
114 {
115     SwDoc* pDoc = rEditSh.GetDoc();
116     if( pDoc->IsAutoFmtRedline() )
117     {
118         // damit der DelPam auch verschoben wird, in den Shell-Cursr-Ring
119         // mit aufnehmen !!
120         _PaMIntoCrsrShellRing aTmp( rEditSh, rCrsr, rDelPam );
121         pDoc->DeleteAndJoin( rDelPam );
122     }
123     else
124     {
125         pDoc->DeleteRange( rDelPam );
126     }
127 }
128 
129 sal_Bool SwAutoCorrDoc::Delete( xub_StrLen nStt, xub_StrLen nEnd )
130 {
131     const SwNodeIndex& rNd = rCrsr.GetPoint()->nNode;
132     SwPaM aSel( rNd, nStt, rNd, nEnd );
133     DeleteSel( aSel );
134 
135     if( bUndoIdInitialized )
136         bUndoIdInitialized = true;
137     return sal_True;
138 }
139 
140 
141 sal_Bool SwAutoCorrDoc::Insert( xub_StrLen nPos, const String& rTxt )
142 {
143     SwPaM aPam( rCrsr.GetPoint()->nNode.GetNode(), nPos );
144     rEditSh.GetDoc()->InsertString( aPam, rTxt );
145     if( !bUndoIdInitialized )
146     {
147         bUndoIdInitialized = true;
148         if( 1 == rTxt.Len() )
149         {
150             rEditSh.StartUndo( UNDO_AUTOCORRECT );
151             ++m_nEndUndoCounter;
152         }
153     }
154     return sal_True;
155 }
156 
157 
158 sal_Bool SwAutoCorrDoc::Replace( xub_StrLen nPos, const String& rTxt )
159 {
160     return ReplaceRange( nPos, rTxt.Len(), rTxt );
161 }
162 sal_Bool SwAutoCorrDoc::ReplaceRange( xub_StrLen nPos, xub_StrLen nSourceLength, const String& rTxt )
163 {
164     SwPaM* pPam = &rCrsr;
165     if( pPam->GetPoint()->nContent.GetIndex() != nPos )
166     {
167         pPam = new SwPaM( *rCrsr.GetPoint() );
168         pPam->GetPoint()->nContent = nPos;
169     }
170 
171     SwTxtNode * const pNd = pPam->GetNode()->GetTxtNode();
172     if ( !pNd )
173     {
174         return sal_False;
175     }
176 
177     // text attributes with dummy characters must not be replaced!
178     bool bDoReplace = true;
179     xub_StrLen const nLen = rTxt.Len();
180     for ( xub_StrLen n = 0; n < nLen; ++n )
181     {
182         sal_Unicode const Char = pNd->GetTxt().GetChar( n + nPos );
183         if ( ( CH_TXTATR_BREAKWORD == Char || CH_TXTATR_INWORD == Char )
184              && pNd->GetTxtAttrForCharAt( n + nPos ) )
185         {
186             bDoReplace = false;
187             break;
188         }
189     }
190 
191     if ( bDoReplace )
192     {
193         SwDoc* pDoc = rEditSh.GetDoc();
194 
195 //      if( !pDoc->IsAutoFmtRedline() &&
196 //          pPam != &rCrsr )    // nur an akt. Position das Redline sichern
197 //          pDoc->SetRedlineMode_intern( eOld | REDLINE_IGNORE );
198 
199         if( pDoc->IsAutoFmtRedline() )
200         {
201             if( nPos == pNd->GetTxt().Len() )       // am Ende erfolgt ein Insert
202             {
203                 pDoc->InsertString( *pPam, rTxt );
204             }
205             else
206             {
207                 _PaMIntoCrsrShellRing aTmp( rEditSh, rCrsr, *pPam );
208 
209                 pPam->SetMark();
210                 pPam->GetPoint()->nContent = Min( pNd->GetTxt().Len(),
211                                               xub_StrLen( nPos + nSourceLength ));
212                 pDoc->ReplaceRange( *pPam, rTxt, false );
213                 pPam->Exchange();
214                 pPam->DeleteMark();
215             }
216         }
217         else
218         {
219             if( nSourceLength != rTxt.Len() )
220             {
221                 pPam->SetMark();
222                 pPam->GetPoint()->nContent = Min( pNd->GetTxt().Len(),
223                                               xub_StrLen( nPos + nSourceLength ));
224                 pDoc->ReplaceRange( *pPam, rTxt, false );
225                 pPam->Exchange();
226                 pPam->DeleteMark();
227             }
228             else
229                 pDoc->Overwrite( *pPam, rTxt );
230         }
231 
232 //      pDoc->SetRedlineMode_intern( eOld );
233         if( bUndoIdInitialized )
234         {
235             bUndoIdInitialized = true;
236             if( 1 == rTxt.Len() )
237             {
238                 rEditSh.StartUndo( UNDO_AUTOCORRECT );
239                 ++m_nEndUndoCounter;
240             }
241         }
242     }
243 
244     if( pPam != &rCrsr )
245         delete pPam;
246 
247     return sal_True;
248 }
249 
250 
251 
252 sal_Bool SwAutoCorrDoc::SetAttr( xub_StrLen nStt, xub_StrLen nEnd, sal_uInt16 nSlotId,
253                                         SfxPoolItem& rItem )
254 {
255     const SwNodeIndex& rNd = rCrsr.GetPoint()->nNode;
256     SwPaM aPam( rNd, nStt, rNd, nEnd );
257 
258     SfxItemPool& rPool = rEditSh.GetDoc()->GetAttrPool();
259     sal_uInt16 nWhich = rPool.GetWhich( nSlotId, sal_False );
260     if( nWhich )
261     {
262         rItem.SetWhich( nWhich );
263 
264         SfxItemSet aSet( rPool, aCharFmtSetRange );
265         SetAllScriptItem( aSet, rItem );
266 
267         rEditSh.GetDoc()->SetFmtItemByAutoFmt( aPam, aSet );
268 
269         if( bUndoIdInitialized )
270             bUndoIdInitialized = true;
271     }
272     return 0 != nWhich;
273 }
274 
275 
276 
277 sal_Bool SwAutoCorrDoc::SetINetAttr( xub_StrLen nStt, xub_StrLen nEnd, const String& rURL )
278 {
279     const SwNodeIndex& rNd = rCrsr.GetPoint()->nNode;
280     SwPaM aPam( rNd, nStt, rNd, nEnd );
281 
282     SfxItemSet aSet( rEditSh.GetDoc()->GetAttrPool(),
283                         RES_TXTATR_INETFMT, RES_TXTATR_INETFMT );
284     aSet.Put( SwFmtINetFmt( rURL, aEmptyStr ));
285     rEditSh.GetDoc()->SetFmtItemByAutoFmt( aPam, aSet );
286     if( bUndoIdInitialized )
287         bUndoIdInitialized = true;
288     return sal_True;
289 }
290 
291     // returne den Text eines vorherigen Absatzes.
292     // Dieser darf nicht leer sein!
293     // Gibt es diesen nicht oder gibt es davor nur Leere, dann returne 0
294     // Das Flag gibt an:
295     //      sal_True: den, vor der normalen Einfuegeposition (sal_True)
296     //      sal_False: den, in den das korrigierte Wort eingfuegt wurde.
297     //              (Muss nicht der gleiche Absatz sein!!!!)
298 const String* SwAutoCorrDoc::GetPrevPara( sal_Bool bAtNormalPos )
299 {
300     const String* pStr = 0;
301 
302     if( bAtNormalPos || !pIdx )
303         pIdx = new SwNodeIndex( rCrsr.GetPoint()->nNode, -1 );
304     else
305         (*pIdx)--;
306 
307     SwTxtNode* pTNd = pIdx->GetNode().GetTxtNode();
308     while( pTNd && !pTNd->GetTxt().Len() )
309     {
310         (*pIdx)--;
311         pTNd = pIdx->GetNode().GetTxtNode();
312     }
313     //if( pTNd && NO_NUMBERING == pTNd->GetTxtColl()->GetOutlineLevel() )
314     if( pTNd && 0 == pTNd->GetAttrOutlineLevel() )//#outline level,zhaojianwei
315         pStr = &pTNd->GetTxt();
316 
317     if( bUndoIdInitialized )
318         bUndoIdInitialized = true;
319     return pStr;
320 }
321 
322 
323 sal_Bool SwAutoCorrDoc::ChgAutoCorrWord( xub_StrLen & rSttPos, xub_StrLen nEndPos,
324                                             SvxAutoCorrect& rACorrect,
325                                             const String** ppPara )
326 {
327     if( bUndoIdInitialized )
328         bUndoIdInitialized = true;
329 
330     // Absatz-Anfang oder ein Blank gefunden, suche nach dem Wort
331     // Kuerzel im Auto
332     SwTxtNode* pTxtNd = rCrsr.GetNode()->GetTxtNode();
333     ASSERT( pTxtNd, "wo ist denn der TextNode?" );
334 
335     sal_Bool bRet = sal_False;
336     if( nEndPos == rSttPos )
337         return bRet;
338 
339     LanguageType eLang = GetLanguage(nEndPos, sal_False);
340     if(LANGUAGE_SYSTEM == eLang)
341         eLang = (LanguageType)GetAppLanguage();
342 
343     //JP 22.04.99: Bug 63883 - Sonderbehandlung fuer Punkte.
344     sal_Bool bLastCharIsPoint = nEndPos < pTxtNd->GetTxt().Len() &&
345                             '.' == pTxtNd->GetTxt().GetChar( nEndPos );
346 
347     const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList(
348                                 pTxtNd->GetTxt(), rSttPos, nEndPos, *this, eLang );
349     SwDoc* pDoc = rEditSh.GetDoc();
350     if( pFnd )
351     {
352         const SwNodeIndex& rNd = rCrsr.GetPoint()->nNode;
353         SwPaM aPam( rNd, rSttPos, rNd, nEndPos );
354 
355         if( pFnd->IsTextOnly() )
356         {
357             //JP 22.04.99: Bug 63883 - Sonderbehandlung fuer Punkte.
358             if( !bLastCharIsPoint || !pFnd->GetLong().Len() ||
359                 '.' != pFnd->GetLong().GetChar( pFnd->GetLong().Len() - 1 ) )
360             {
361                 // replace the selection
362                 pDoc->ReplaceRange( aPam, pFnd->GetLong(), false);
363                 bRet = sal_True;
364             }
365         }
366         else
367         {
368             SwTextBlocks aTBlks( rACorrect.GetAutoCorrFileName( eLang, sal_False, sal_True ));
369             sal_uInt16 nPos = aTBlks.GetIndex( pFnd->GetShort() );
370             if( USHRT_MAX != nPos && aTBlks.BeginGetDoc( nPos ) )
371             {
372                 DeleteSel( aPam );
373                 pDoc->DontExpandFmt( *aPam.GetPoint() );
374 
375                 if( ppPara )
376                 {
377                     ASSERT( !pIdx, "wer hat seinen Index nicht geloescht?" );
378                     pIdx = new SwNodeIndex( rCrsr.GetPoint()->nNode, -1 );
379                 }
380 
381                 //
382                 SwDoc* pAutoDoc = aTBlks.GetDoc();
383                 SwNodeIndex aSttIdx( pAutoDoc->GetNodes().GetEndOfExtras(), 1 );
384                 SwCntntNode* pCntntNd = pAutoDoc->GetNodes().GoNext( &aSttIdx );
385                 SwPaM aCpyPam( aSttIdx );
386 
387                 const SwTableNode* pTblNd = pCntntNd->FindTableNode();
388                 if( pTblNd )
389                 {
390                     aCpyPam.GetPoint()->nContent.Assign( 0, 0 );
391                     aCpyPam.GetPoint()->nNode = *pTblNd;
392                 }
393                 aCpyPam.SetMark();
394 
395                 // dann bis zum Ende vom Nodes Array
396                 aCpyPam.GetPoint()->nNode.Assign( pAutoDoc->GetNodes().GetEndOfContent(), -1 );
397                 pCntntNd = aCpyPam.GetCntntNode();
398                 aCpyPam.GetPoint()->nContent.Assign( pCntntNd, pCntntNd->Len() );
399 
400                 SwDontExpandItem aExpItem;
401                 aExpItem.SaveDontExpandItems( *aPam.GetPoint() );
402 
403                 pAutoDoc->CopyRange( aCpyPam, *aPam.GetPoint(), false );
404 
405                 aExpItem.RestoreDontExpandItems( *aPam.GetPoint() );
406 
407                 if( ppPara )
408                 {
409                     (*pIdx)++;
410                     pTxtNd = pIdx->GetNode().GetTxtNode();
411                 }
412                 bRet = sal_True;
413             }
414             aTBlks.EndGetDoc();
415         }
416     }
417 
418     if( bRet && ppPara && pTxtNd )
419         *ppPara = &pTxtNd->GetTxt();
420 
421     return bRet;
422 }
423 
424 
425     // wird nach dem austauschen der Zeichen von den Funktionen
426     //  - FnCptlSttWrd
427     //  - FnCptlSttSntnc
428     // gerufen. Dann koennen die Worte ggfs. in die Ausnahmelisten
429     // aufgenommen werden.
430 void SwAutoCorrDoc::SaveCpltSttWord( sal_uLong nFlag, xub_StrLen nPos,
431                                             const String& rExceptWord,
432                                             sal_Unicode cChar )
433 {
434     sal_uLong nNode = pIdx ? pIdx->GetIndex() : rCrsr.GetPoint()->nNode.GetIndex();
435     LanguageType eLang = GetLanguage(nPos, sal_False);
436     rEditSh.GetDoc()->SetAutoCorrExceptWord( new SwAutoCorrExceptWord( nFlag,
437                                         nNode, nPos, rExceptWord, cChar, eLang ));
438 }
439 
440 LanguageType SwAutoCorrDoc::GetLanguage( xub_StrLen nPos, sal_Bool bPrevPara ) const
441 {
442     LanguageType eRet = LANGUAGE_SYSTEM;
443 
444     SwTxtNode* pNd = (( bPrevPara && pIdx )
445                             ? *pIdx
446                             : rCrsr.GetPoint()->nNode ).GetNode().GetTxtNode();
447 
448     if( pNd )
449         eRet = pNd->GetLang( nPos, 0 );
450     if(LANGUAGE_SYSTEM == eRet)
451         eRet = (LanguageType)GetAppLanguage();
452     return eRet;
453 }
454 
455 void SwAutoCorrExceptWord::CheckChar( const SwPosition& rPos, sal_Unicode cChr )
456 {
457     // nur testen ob es eine Verbesserung ist. Wenn ja, dann das Wort
458     // in die Ausnahmeliste aufnehmen.
459     if( cChar == cChr && rPos.nNode.GetIndex() == nNode &&
460         rPos.nContent.GetIndex() == nCntnt )
461     {
462         // die akt. Autokorrektur besorgen:
463         SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get()->GetAutoCorrect();
464 
465         // dann in die Liste aufnehmen:
466         if( CptlSttWrd & nFlags )
467             pACorr->AddWrtSttException( sWord, eLanguage );
468         else if( CptlSttSntnc & nFlags )
469             pACorr->AddCplSttException( sWord, eLanguage );
470     }
471 }
472 
473 
474 sal_Bool SwAutoCorrExceptWord::CheckDelChar( const SwPosition& rPos )
475 {
476     sal_Bool bRet = sal_False;
477     if( !bDeleted && rPos.nNode.GetIndex() == nNode &&
478         rPos.nContent.GetIndex() == nCntnt )
479         bDeleted = bRet = sal_True;
480     return bRet;
481 }
482 
483 SwDontExpandItem::~SwDontExpandItem()
484 {
485     delete pDontExpItems;
486 }
487 
488 void SwDontExpandItem::SaveDontExpandItems( const SwPosition& rPos )
489 {
490     const SwTxtNode* pTxtNd = rPos.nNode.GetNode().GetTxtNode();
491     if( pTxtNd )
492     {
493         pDontExpItems = new SfxItemSet( ((SwDoc*)pTxtNd->GetDoc())->GetAttrPool(),
494                                             aCharFmtSetRange );
495         xub_StrLen n = rPos.nContent.GetIndex();
496         if( !pTxtNd->GetAttr( *pDontExpItems, n, n,
497                                 n != pTxtNd->GetTxt().Len() ))
498             delete pDontExpItems, pDontExpItems = 0;
499     }
500 }
501 
502 void SwDontExpandItem::RestoreDontExpandItems( const SwPosition& rPos )
503 {
504     SwTxtNode* pTxtNd = rPos.nNode.GetNode().GetTxtNode();
505     if( pTxtNd )
506     {
507         xub_StrLen nStart = rPos.nContent.GetIndex();
508         if( nStart == pTxtNd->GetTxt().Len() )
509             pTxtNd->FmtToTxtAttr( pTxtNd );
510 
511         if( pTxtNd->GetpSwpHints() && pTxtNd->GetpSwpHints()->Count() )
512         {
513             const sal_uInt16 nSize = pTxtNd->GetpSwpHints()->Count();
514             sal_uInt16 n;
515             xub_StrLen nAttrStart;
516             const xub_StrLen* pAttrEnd;
517 
518             for( n = 0; n < nSize; ++n )
519             {
520                 SwTxtAttr* pHt = pTxtNd->GetpSwpHints()->GetTextHint( n );
521                 nAttrStart = *pHt->GetStart();
522                 if( nAttrStart > nStart )       // ueber den Bereich hinaus
523                     break;
524 
525                 if( 0 != ( pAttrEnd = pHt->GetEnd() ) &&
526                     ( ( nAttrStart < nStart &&
527                         ( pHt->DontExpand() ? nStart < *pAttrEnd
528                                             : nStart <= *pAttrEnd )) ||
529                       ( nStart == nAttrStart &&
530                         ( nAttrStart == *pAttrEnd || !nStart ))) )
531                 {
532                     const SfxPoolItem* pItem;
533                     if( !pDontExpItems || SFX_ITEM_SET != pDontExpItems->
534                         GetItemState( pHt->Which(), sal_False, &pItem ) ||
535                         *pItem != pHt->GetAttr() )
536                     {
537                         // das Attribut war vorher nicht in dieser Form im Absatz
538                         // gesetzt, also kann es nur durchs einfuegen/kopieren erzeugt
539                         // worden sein. Damit ist es ein Kandiadat fuers DontExpand
540                         pHt->SetDontExpand( sal_True );
541                     }
542                 }
543             }
544         }
545     }
546 }
547 
548 
549