xref: /AOO41X/main/sw/source/core/edit/acorrect.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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