xref: /trunk/main/editeng/source/editeng/edtspell.cxx (revision 1e6c8db2)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_editeng.hxx"
26 
27 #include <vcl/wrkwin.hxx>
28 #include <vcl/dialog.hxx>
29 #include <vcl/msgbox.hxx>
30 #include <vcl/svapp.hxx>
31 
32 #include <impedit.hxx>
33 #include <editeng/editview.hxx>
34 #include <editeng/editeng.hxx>
35 #include <edtspell.hxx>
36 #include <editeng/flditem.hxx>
37 #include <editeng/fontitem.hxx>
38 #include <svl/intitem.hxx>
39 #include <svl/eitem.hxx>
40 #include <editeng/unolingu.hxx>
41 #include <linguistic/lngprops.hxx>
42 #include <com/sun/star/beans/XPropertySet.hpp>
43 
44 using ::rtl::OUString;
45 using namespace com::sun::star::uno;
46 using namespace com::sun::star::beans;
47 using namespace com::sun::star::linguistic2;
48 
49 
EditSpellWrapper(Window * _pWin,Reference<XSpellChecker1> & xChecker,sal_Bool bIsStart,sal_Bool bIsAllRight,EditView * pView)50 EditSpellWrapper::EditSpellWrapper( Window* _pWin,
51 		Reference< XSpellChecker1 >  &xChecker,
52 		sal_Bool bIsStart, sal_Bool bIsAllRight, EditView* pView ) :
53 	SvxSpellWrapper( _pWin, xChecker, bIsStart, bIsAllRight )
54 {
55 	DBG_ASSERT( pView, "Es muss eine View uebergeben werden!" );
56 	// IgnoreList behalten, ReplaceList loeschen...
57 	if (SvxGetChangeAllList().is())
58 		SvxGetChangeAllList()->clear();
59 	pEditView = pView;
60 }
61 
SpellStart(SvxSpellArea eArea)62 void __EXPORT EditSpellWrapper::SpellStart( SvxSpellArea eArea )
63 {
64 	ImpEditEngine* pImpEE = pEditView->GetImpEditEngine();
65 	SpellInfo* pSpellInfo = pImpEE->GetSpellInfo();
66 
67 	if ( eArea == SVX_SPELL_BODY_START )
68 	{
69 		// Wird gerufen, wenn
70 		// a) Spell-Forwad ist am Ende angekomment und soll von vorne beginnen
71 		// IsEndDone() liefert auch sal_True, wenn Rueckwaerts-Spelling am Ende gestartet wird!
72         if ( IsEndDone() )
73 		{
74 			pSpellInfo->bSpellToEnd = sal_False;
75 			pSpellInfo->aSpellTo = pSpellInfo->aSpellStart;
76 			pEditView->GetImpEditView()->SetEditSelection(
77 					pImpEE->GetEditDoc().GetStartPaM() );
78 		}
79 		else
80 		{
81 			pSpellInfo->bSpellToEnd = sal_True;
82 			pSpellInfo->aSpellTo = pImpEE->CreateEPaM(
83 					pImpEE->GetEditDoc().GetStartPaM() );
84 		}
85 	}
86 	else if ( eArea == SVX_SPELL_BODY_END )
87 	{
88 		// Wird gerufen, wenn
89 		// a) Spell-Forwad wird gestartet
90 		// IsStartDone() liefert auch sal_True, wenn Vorwaerts-Spelling am Anfang gestartet wird!
91         if ( !IsStartDone() )
92 		{
93 			pSpellInfo->bSpellToEnd = sal_True;
94 			pSpellInfo->aSpellTo = pImpEE->CreateEPaM(
95 					pImpEE->GetEditDoc().GetEndPaM() );
96 		}
97 		else
98 		{
99 			pSpellInfo->bSpellToEnd = sal_False;
100 			pSpellInfo->aSpellTo = pSpellInfo->aSpellStart;
101 			pEditView->GetImpEditView()->SetEditSelection(
102 					pImpEE->GetEditDoc().GetEndPaM() );
103 		}
104 	}
105 	else if ( eArea == SVX_SPELL_BODY )
106 	{
107 		;	// Wird ueber SpellNextDocument von App gehandelt
108 
109 		// pSpellInfo->bSpellToEnd = sal_True;
110 		// pSpellInfo->aSpellTo = pImpEE->CreateEPaM( pImpEE->GetEditDoc().GetEndPaM() );
111 	}
112 	else
113 	{
114 		DBG_ERROR( "SpellStart: Unknown Area!" );
115 	}
116 }
117 
SpellContinue()118 sal_Bool EditSpellWrapper::SpellContinue()
119 {
120 	SetLast( pEditView->GetImpEditEngine()->ImpSpell( pEditView ) );
121 	return GetLast().is();
122 }
123 
SpellEnd()124 void __EXPORT EditSpellWrapper::SpellEnd()
125 {
126 	// Base class will show language errors...
127 	SvxSpellWrapper::SpellEnd();
128 }
129 
HasOtherCnt()130 sal_Bool __EXPORT EditSpellWrapper::HasOtherCnt()
131 {
132 	return sal_False;
133 }
134 
SpellMore()135 sal_Bool __EXPORT EditSpellWrapper::SpellMore()
136 {
137 	ImpEditEngine* pImpEE = pEditView->GetImpEditEngine();
138 	SpellInfo* pSpellInfo = pImpEE->GetSpellInfo();
139 	sal_Bool bMore = sal_False;
140 	if ( pSpellInfo->bMultipleDoc )
141 	{
142 		bMore = pImpEE->GetEditEnginePtr()->SpellNextDocument();
143 		SetCurTextObj( pImpEE->GetEditEnginePtr()->GetCurTextObj() );
144 		if ( bMore )
145 		{
146 			// Der Text wurde in diese Engine getreten, bei Rueckwaerts
147 			// muss die Selektion hinten sein.
148 			Reference< XPropertySet >  xProp( SvxGetLinguPropertySet() );
149             pEditView->GetImpEditView()->SetEditSelection(
150 						pImpEE->GetEditDoc().GetStartPaM() );
151 		}
152 	}
153 	return bMore;
154 }
155 
ScrollArea()156 void __EXPORT EditSpellWrapper::ScrollArea()
157 {
158 	// Keine weitere Aktion noetig...
159 	// Es sei denn, der Bereich soll in die Mitte gescrollt werden,
160 	// und nicht irgendwo stehen.
161 }
162 
ReplaceAll(const String & rNewText,sal_Int16)163 void __EXPORT EditSpellWrapper::ReplaceAll( const String &rNewText,
164 			sal_Int16 )
165 {
166 	// Wird gerufen, wenn Wort in ReplaceList des SpellCheckers
167 	pEditView->InsertText( rNewText );
168 	CheckSpellTo();
169 }
170 
ChangeWord(const String & rNewWord,const sal_uInt16)171 void __EXPORT EditSpellWrapper::ChangeWord( const String& rNewWord,
172 			const sal_uInt16 )
173 {
174 	// Wird gerufen, wenn Wort Button Change
175 	// bzw. intern von mir bei ChangeAll
176 
177 	// Wenn Punkt hinterm Wort, wird dieser nicht mitgegeben.
178 	// Falls '"' => PreStripped.
179 	String aNewWord( rNewWord );
180 	pEditView->InsertText( aNewWord );
181 	CheckSpellTo();
182 }
183 
ChangeThesWord(const String & rNewWord)184 void __EXPORT EditSpellWrapper::ChangeThesWord( const String& rNewWord )
185 {
186 	pEditView->InsertText( rNewWord );
187 	CheckSpellTo();
188 }
189 
AutoCorrect(const String &,const String &)190 void __EXPORT EditSpellWrapper::AutoCorrect( const String&, const String& )
191 {
192 }
193 
CheckSpellTo()194 void EditSpellWrapper::CheckSpellTo()
195 {
196 	ImpEditEngine* pImpEE = pEditView->GetImpEditEngine();
197 	SpellInfo* pSpellInfo = pImpEE->GetSpellInfo();
198 	EditPaM aPaM( pEditView->GetImpEditView()->GetEditSelection().Max() );
199 	EPaM aEPaM = pImpEE->CreateEPaM( aPaM );
200 	if ( aEPaM.nPara == pSpellInfo->aSpellTo.nPara )
201 	{
202 		// prueffen, ob SpellToEnd noch gueltiger Index, falls in dem Absatz
203 		// ersetzt wurde.
204 		if ( pSpellInfo->aSpellTo.nIndex > aPaM.GetNode()->Len() )
205 			pSpellInfo->aSpellTo.nIndex = aPaM.GetNode()->Len();
206 	}
207 }
208 
209 //////////////////////////////////////////////////////////////////////
210 
211 SV_IMPL_VARARR( WrongRanges, WrongRange );
212 
WrongList()213 WrongList::WrongList()
214 {
215 	nInvalidStart = 0;
216 	nInvalidEnd = 0xFFFF;
217 }
218 
~WrongList()219 WrongList::~WrongList()
220 {
221 }
222 
MarkInvalid(sal_uInt16 nS,sal_uInt16 nE)223 void WrongList::MarkInvalid( sal_uInt16 nS, sal_uInt16 nE )
224 {
225 	if ( ( nInvalidStart == NOT_INVALID ) || ( nInvalidStart > nS ) )
226 		nInvalidStart = nS;
227 	if ( nInvalidEnd < nE )
228 		nInvalidEnd = nE;
229 }
230 
TextInserted(sal_uInt16 nPos,sal_uInt16 nNew,sal_Bool bPosIsSep)231 void WrongList::TextInserted( sal_uInt16 nPos, sal_uInt16 nNew, sal_Bool bPosIsSep )
232 {
233 	if ( !IsInvalid() )
234 	{
235 		nInvalidStart = nPos;
236 		nInvalidEnd = nPos+nNew;
237 	}
238 	else
239 	{
240 		if ( nInvalidStart > nPos )
241 			nInvalidStart = nPos;
242 		if ( nInvalidEnd >= nPos )
243 			nInvalidEnd = nInvalidEnd + nNew;
244 		else
245 			nInvalidEnd = nPos+nNew;
246 	}
247 
248 	for ( sal_uInt16 n = 0; n < Count(); n++ )
249 	{
250 		WrongRange& rWrong = GetObject( n );
251 		sal_Bool bRefIsValid = sal_True;
252 		if ( rWrong.nEnd >= nPos )
253 		{
254 			// Alle Wrongs hinter der Einfuegeposition verschieben...
255 			if ( rWrong.nStart > nPos )
256 			{
257 				rWrong.nStart = rWrong.nStart + nNew;
258 				rWrong.nEnd = rWrong.nEnd + nNew;
259 			}
260 			// 1: Startet davor, geht bis nPos...
261 			else if ( rWrong.nEnd == nPos )
262 			{
263 				// Sollte bei einem Blank unterbunden werden!
264 				if ( !bPosIsSep )
265 					rWrong.nEnd = rWrong.nEnd + nNew;
266 			}
267 			// 2: Startet davor, geht hinter Pos...
268 			else if ( ( rWrong.nStart < nPos ) && ( rWrong.nEnd > nPos ) )
269 			{
270 				rWrong.nEnd = rWrong.nEnd + nNew;
271 				// Bei einem Trenner das Wrong entfernen und neu pruefen
272 				if ( bPosIsSep )
273 				{
274 					// Wrong aufteilen...
275 					WrongRange aNewWrong( rWrong.nStart, nPos );
276 					rWrong.nStart = nPos+1;
277 					Insert( aNewWrong, n );
278 					bRefIsValid = sal_False;	// Referenz nach Insert nicht mehr gueltig, der andere wurde davor an dessen Position eingefuegt
279 					n++; // Diesen nicht nochmal...
280 				}
281 			}
282 			// 3: Attribut startet auf Pos...
283 			else if ( rWrong.nStart == nPos )
284 			{
285 				rWrong.nEnd = rWrong.nEnd + nNew;
286 				if ( bPosIsSep )
287 					rWrong.nStart++;
288 			}
289 		}
290 		DBG_ASSERT( !bRefIsValid || ( rWrong.nStart < rWrong.nEnd ),
291 				"TextInserted, WrongRange: Start >= End?!" );
292 	}
293 
294 	DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" );
295 }
296 
TextDeleted(sal_uInt16 nPos,sal_uInt16 nDeleted)297 void WrongList::TextDeleted( sal_uInt16 nPos, sal_uInt16 nDeleted )
298 {
299 	sal_uInt16 nEndChanges = nPos+nDeleted;
300 	if ( !IsInvalid() )
301 	{
302         sal_uInt16 nNewInvalidStart = nPos ? nPos - 1 : 0;
303         nInvalidStart = nNewInvalidStart;
304         nInvalidEnd = nNewInvalidStart + 1;
305     }
306 	else
307 	{
308 		if ( nInvalidStart > nPos )
309 			nInvalidStart = nPos;
310 		if ( nInvalidEnd > nPos )
311 		{
312 			if ( nInvalidEnd > nEndChanges )
313 				nInvalidEnd = nInvalidEnd - nDeleted;
314 			else
315 				nInvalidEnd = nPos+1;
316 		}
317 	}
318 
319 	for ( sal_uInt16 n = 0; n < Count(); n++ )
320 	{
321 		WrongRange& rWrong = GetObject( n );
322 		sal_Bool bDelWrong = sal_False;
323 		if ( rWrong.nEnd >= nPos )
324 		{
325 			// Alles Wrongs hinter der Einfuegeposition verschieben...
326 			if ( rWrong.nStart >= nEndChanges )
327 			{
328 				rWrong.nStart = rWrong.nStart - nDeleted;
329 				rWrong.nEnd = rWrong.nEnd - nDeleted;
330 			}
331 			// 1. Innenliegende Wrongs loeschen...
332 			else if ( ( rWrong.nStart >= nPos ) && ( rWrong.nEnd <= nEndChanges ) )
333 			{
334 				bDelWrong = sal_True;
335 			}
336 			// 2. Wrong beginnt davor, endet drinnen oder dahinter...
337 			else if ( ( rWrong.nStart <= nPos ) && ( rWrong.nEnd > nPos ) )
338 			{
339 				if ( rWrong.nEnd <= nEndChanges )	// endet drinnen
340 					rWrong.nEnd = nPos;
341 				else
342 					rWrong.nEnd = rWrong.nEnd - nDeleted; // endet dahinter
343 			}
344 			// 3. Wrong beginnt drinnen, endet dahinter...
345 			else if ( ( rWrong.nStart >= nPos ) && ( rWrong.nEnd > nEndChanges ) )
346 			{
347 				rWrong.nStart = nEndChanges;
348 				rWrong.nStart = rWrong.nStart - nDeleted;
349 				rWrong.nEnd = rWrong.nEnd - nDeleted;
350 			}
351 		}
352 		DBG_ASSERT( rWrong.nStart < rWrong.nEnd,
353 				"TextInserted, WrongRange: Start >= End?!" );
354 		if ( bDelWrong )
355 		{
356 			Remove( n, 1 );
357 			n--;
358 		}
359 	}
360 
361 	DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" );
362 }
363 
NextWrong(sal_uInt16 & rnStart,sal_uInt16 & rnEnd) const364 sal_Bool WrongList::NextWrong( sal_uInt16& rnStart, sal_uInt16& rnEnd ) const
365 {
366 	/*
367 		rnStart enthaelt die Startposition, wird ggf. auf Wrong-Start korrigiert
368 		rnEnd braucht nicht inizialisiert sein.
369 	*/
370 	for ( sal_uInt16 n = 0; n < Count(); n++ )
371 	{
372 		WrongRange& rWrong = GetObject( n );
373 		if ( rWrong.nEnd > rnStart )
374 		{
375 			rnStart = rWrong.nStart;
376 			rnEnd = rWrong.nEnd;
377 			return sal_True;
378 		}
379 	}
380 	return sal_False;
381 }
382 
HasWrong(sal_uInt16 nStart,sal_uInt16 nEnd) const383 sal_Bool WrongList::HasWrong( sal_uInt16 nStart, sal_uInt16 nEnd ) const
384 {
385 	for ( sal_uInt16 n = 0; n < Count(); n++ )
386 	{
387 		WrongRange& rWrong = GetObject( n );
388 		if ( ( rWrong.nStart == nStart ) && ( rWrong.nEnd == nEnd ) )
389 			return sal_True;
390 		else if ( rWrong.nStart >= nStart )
391 			break;
392 	}
393 	return sal_False;
394 }
395 
HasAnyWrong(sal_uInt16 nStart,sal_uInt16 nEnd) const396 sal_Bool WrongList::HasAnyWrong( sal_uInt16 nStart, sal_uInt16 nEnd ) const
397 {
398 	for ( sal_uInt16 n = 0; n < Count(); n++ )
399 	{
400 		WrongRange& rWrong = GetObject( n );
401 		if ( ( rWrong.nEnd >= nStart ) && ( rWrong.nStart < nEnd ) )
402 			return sal_True;
403 		else if ( rWrong.nStart >= nEnd )
404 			break;
405 	}
406 	return sal_False;
407 }
408 
ClearWrongs(sal_uInt16 nStart,sal_uInt16 nEnd,const ContentNode * pNode)409 void WrongList::ClearWrongs( sal_uInt16 nStart, sal_uInt16 nEnd,
410 			const ContentNode* pNode )
411 {
412 	for ( sal_uInt16 n = 0; n < Count(); n++ )
413 	{
414 		WrongRange& rWrong = GetObject( n );
415 		if ( ( rWrong.nEnd > nStart ) && ( rWrong.nStart < nEnd ) )
416 		{
417 			if ( rWrong.nEnd > nEnd )	// // Laeuft raus
418 			{
419 				rWrong.nStart = nEnd;
420 				// Blanks?
421 				while ( ( rWrong.nStart < pNode->Len() ) &&
422 							( ( pNode->GetChar( rWrong.nStart ) == ' ' ) ||
423 							  ( pNode->IsFeature( rWrong.nStart ) ) ) )
424 				{
425 					rWrong.nStart++;
426 				}
427 			}
428 			else
429 			{
430 				Remove( n, 1 );
431 				n--;
432 			}
433 		}
434 	}
435 
436 	DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" );
437 }
438 
InsertWrong(sal_uInt16 nStart,sal_uInt16 nEnd,sal_Bool bClearRange)439 void WrongList::InsertWrong( sal_uInt16 nStart, sal_uInt16 nEnd,
440 			sal_Bool bClearRange )
441 {
442 	sal_uInt16 nPos = Count();
443 	for ( sal_uInt16 n = 0; n < Count(); n++ )
444 	{
445 		WrongRange& rWrong = GetObject( n );
446 		if ( rWrong.nStart >= nStart )
447 		{
448 			nPos = n;
449 			if ( bClearRange )
450 			{
451 				// Es kann eigentlich nur Passieren, dass der Wrong genau
452 				// hier beginnt und weiter rauslauft, aber nicht, dass hier
453 				// mehrere im Bereich liegen...
454 				// Genau im Bereich darf keiner liegen, sonst darf diese Methode
455 				// garnicht erst gerufen werden!
456 				DBG_ASSERT( ( ( rWrong.nStart == nStart ) && ( rWrong.nEnd > nEnd ) )
457 								|| ( rWrong.nStart > nEnd ), "InsertWrong: RangeMismatch!" );
458 				if ( ( rWrong.nStart == nStart ) && ( rWrong.nEnd > nEnd ) )
459 					rWrong.nStart = nEnd+1;
460 			}
461 			break;
462 		}
463 	}
464 	Insert( WrongRange( nStart, nEnd ), nPos );
465 
466 	DBG_ASSERT( !DbgIsBuggy(), "InsertWrong: WrongList kaputt!" );
467 }
468 
MarkWrongsInvalid()469 void WrongList::MarkWrongsInvalid()
470 {
471 	if ( Count() )
472 		MarkInvalid( GetObject( 0 ).nStart, GetObject( Count()-1 ).nEnd );
473 }
474 
Clone() const475 WrongList*	WrongList::Clone() const
476 {
477 	WrongList* pNew = new WrongList;
478 	for ( sal_uInt16 n = 0; n < Count(); n++ )
479 	{
480 		WrongRange& rWrong = GetObject( n );
481 		pNew->Insert( rWrong, pNew->Count() );
482 	}
483 
484 	return pNew;
485 }
486 
487 // #i102062#
operator ==(const WrongList & rCompare) const488 bool WrongList::operator==(const WrongList& rCompare) const
489 {
490     // cleck direct members
491     if(GetInvalidStart() != rCompare.GetInvalidStart()
492         || GetInvalidEnd() != rCompare.GetInvalidEnd()
493         || Count() != rCompare.Count())
494     {
495         return false;
496     }
497 
498     for(sal_uInt16 a(0); a < Count(); a++)
499     {
500         const WrongRange& rCandA(GetObject(a));
501         const WrongRange& rCandB(rCompare.GetObject(a));
502 
503         if(rCandA.nStart != rCandB.nStart
504             || rCandA.nEnd != rCandB.nEnd)
505         {
506             return false;
507         }
508     }
509 
510     return true;
511 }
512 
513 #ifdef DBG_UTIL
DbgIsBuggy() const514 sal_Bool WrongList::DbgIsBuggy() const
515 {
516 	// Pruefen, ob sich Bereiche ueberlappen
517 	sal_Bool bError = sal_False;
518 	for ( sal_uInt16 _nA = 0; !bError && ( _nA < Count() ); _nA++ )
519 	{
520 		WrongRange& rWrong = GetObject( _nA );
521 		for ( sal_uInt16 nB = _nA+1; !bError && ( nB < Count() ); nB++ )
522 		{
523 			WrongRange& rNextWrong = GetObject( nB );
524 			// 1) Start davor, End hinterm anderen Start
525 			if (   ( rWrong.nStart <= rNextWrong.nStart )
526 				&& ( rWrong.nEnd >= rNextWrong.nStart ) )
527 				bError = sal_True;
528 			// 2) Start hinter anderen Start, aber noch vorm anderen End
529 			else if (   ( rWrong.nStart >= rNextWrong.nStart)
530 					 && ( rWrong.nStart <= rNextWrong.nEnd ) )
531 				bError = sal_True;
532 		}
533 	}
534 	return bError;
535 }
536 #endif
537 
538 //////////////////////////////////////////////////////////////////////
539 
EdtAutoCorrDoc(ImpEditEngine * pE,ContentNode * pN,sal_uInt16 nCrsr,xub_Unicode cIns)540 EdtAutoCorrDoc::EdtAutoCorrDoc( ImpEditEngine* pE, ContentNode* pN,
541 			sal_uInt16 nCrsr, xub_Unicode cIns )
542 {
543 	pImpEE = pE;
544 	pCurNode = pN;
545 	nCursor = nCrsr;
546 
547 	bUndoAction = sal_False;
548 	bAllowUndoAction = cIns ? sal_True : sal_False;
549 }
550 
~EdtAutoCorrDoc()551 EdtAutoCorrDoc::~EdtAutoCorrDoc()
552 {
553 	if ( bUndoAction )
554 		pImpEE->UndoActionEnd( EDITUNDO_INSERT );
555 }
556 
Delete(sal_uInt16 nStt,sal_uInt16 nEnd)557 sal_Bool EdtAutoCorrDoc::Delete( sal_uInt16 nStt, sal_uInt16 nEnd )
558 {
559 	EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) );
560 	pImpEE->ImpDeleteSelection( aSel );
561 	DBG_ASSERT( nCursor >= nEnd, "Cursor mitten im Geschehen ?!" );
562 	nCursor -= ( nEnd-nStt );
563 	bAllowUndoAction = sal_False;
564 	return sal_True;
565 }
566 
Insert(sal_uInt16 nPos,const String & rTxt)567 sal_Bool EdtAutoCorrDoc::Insert( sal_uInt16 nPos, const String& rTxt )
568 {
569 	EditSelection aSel = EditPaM( pCurNode, nPos );
570 	pImpEE->ImpInsertText( aSel, rTxt );
571 	DBG_ASSERT( nCursor >= nPos, "Cursor mitten im Geschehen ?!" );
572 	nCursor = nCursor + rTxt.Len();
573 
574 	if ( bAllowUndoAction && ( rTxt.Len() == 1 ) )
575 		ImplStartUndoAction();
576 	bAllowUndoAction = sal_False;
577 
578 	return sal_True;
579 }
580 
Replace(sal_uInt16 nPos,const String & rTxt)581 sal_Bool EdtAutoCorrDoc::Replace( sal_uInt16 nPos, const String& rTxt )
582 {
583     return ReplaceRange( nPos, rTxt.Len(), rTxt );
584 }
585 
ReplaceRange(xub_StrLen nPos,xub_StrLen nSourceLength,const String & rTxt)586 sal_Bool EdtAutoCorrDoc::ReplaceRange( xub_StrLen nPos, xub_StrLen nSourceLength, const String& rTxt )
587 {
588 	// Eigentlich ein Replace einfuehren => Entspr. UNDO
589 	sal_uInt16 nEnd = nPos+nSourceLength;
590 	if ( nEnd > pCurNode->Len() )
591 		nEnd = pCurNode->Len();
592 
593     // #i5925# First insert new text behind to be deleted text, for keeping attributes.
594 	pImpEE->ImpInsertText( EditSelection( EditPaM( pCurNode, nEnd ) ), rTxt );
595     pImpEE->ImpDeleteSelection( EditSelection( EditPaM( pCurNode, nPos ), EditPaM( pCurNode, nEnd ) ) );
596 
597     if ( nPos == nCursor )
598 		nCursor = nCursor + rTxt.Len();
599 
600 	if ( bAllowUndoAction && ( rTxt.Len() == 1 ) )
601 		ImplStartUndoAction();
602 
603     bAllowUndoAction = sal_False;
604 
605 	return sal_True;
606 }
607 
SetAttr(sal_uInt16 nStt,sal_uInt16 nEnd,sal_uInt16 nSlotId,SfxPoolItem & rItem)608 sal_Bool EdtAutoCorrDoc::SetAttr( sal_uInt16 nStt, sal_uInt16 nEnd,
609 			sal_uInt16 nSlotId, SfxPoolItem& rItem )
610 {
611 	SfxItemPool* pPool = &pImpEE->GetEditDoc().GetItemPool();
612 	while ( pPool->GetSecondaryPool() &&
613 			!pPool->GetName().EqualsAscii( "EditEngineItemPool" ) )
614 	{
615 		pPool = pPool->GetSecondaryPool();
616 
617 	}
618 	sal_uInt16 nWhich = pPool->GetWhich( nSlotId );
619 	if ( nWhich )
620 	{
621 		rItem.SetWhich( nWhich );
622 
623 		SfxItemSet aSet( pImpEE->GetEmptyItemSet() );
624 		aSet.Put( rItem );
625 
626 		EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) );
627 		aSel.Max().SetIndex( nEnd );	// ???
628 		pImpEE->SetAttribs( aSel, aSet, ATTRSPECIAL_EDGE );
629 		bAllowUndoAction = sal_False;
630 	}
631 	return sal_True;
632 }
633 
SetINetAttr(sal_uInt16 nStt,sal_uInt16 nEnd,const String & rURL)634 sal_Bool EdtAutoCorrDoc::SetINetAttr( sal_uInt16 nStt, sal_uInt16 nEnd,
635 			const String& rURL )
636 {
637 	// Aus dem Text ein Feldbefehl machen...
638 	EditSelection aSel( EditPaM( pCurNode, nStt ), EditPaM( pCurNode, nEnd ) );
639 	String aText = pImpEE->GetSelected( aSel );
640 	aSel = pImpEE->ImpDeleteSelection( aSel );
641 	DBG_ASSERT( nCursor >= nEnd, "Cursor mitten im Geschehen ?!" );
642 	nCursor -= ( nEnd-nStt );
643 	SvxFieldItem aField( SvxURLField( rURL, aText, SVXURLFORMAT_REPR ),
644 									  EE_FEATURE_FIELD  );
645 	pImpEE->InsertField( aSel, aField );
646 	nCursor++;
647 	pImpEE->UpdateFields();
648 	bAllowUndoAction = sal_False;
649 	return sal_True;
650 }
651 
HasSymbolChars(sal_uInt16 nStt,sal_uInt16 nEnd)652 sal_Bool EdtAutoCorrDoc::HasSymbolChars( sal_uInt16 nStt, sal_uInt16 nEnd )
653 {
654 	sal_uInt16 nScriptType = pImpEE->GetScriptType( EditPaM( pCurNode, nStt ) );
655 	sal_uInt16 nScriptFontInfoItemId = GetScriptItemId( EE_CHAR_FONTINFO, nScriptType );
656 
657 	CharAttribArray& rAttribs = pCurNode->GetCharAttribs().GetAttribs();
658 	sal_uInt16 nAttrs = rAttribs.Count();
659 	for ( sal_uInt16 n = 0; n < nAttrs; n++ )
660 	{
661 		EditCharAttrib* pAttr = rAttribs.GetObject( n );
662 		if ( pAttr->GetStart() >= nEnd )
663 			return sal_False;
664 
665 		if ( ( pAttr->Which() == nScriptFontInfoItemId ) &&
666 				( ((SvxFontItem*)pAttr->GetItem())->GetCharSet() == RTL_TEXTENCODING_SYMBOL ) )
667 		{
668 			// Pruefen, ob das Attribt im Bereich liegt...
669 			if ( pAttr->GetEnd() >= nStt )
670 				return sal_True;
671 		}
672 	}
673 	return sal_False;
674 }
675 
GetPrevPara(sal_Bool)676 const String* EdtAutoCorrDoc::GetPrevPara( sal_Bool )
677 {
678 	// Vorherigen Absatz zurueck geben, damit ermittel werden kann,
679 	// ob es sich beim aktuellen Wort um einen Satzanfang handelt.
680 
681 	bAllowUndoAction = sal_False;	// Jetzt nicht mehr...
682 
683 	ContentList& rNodes = pImpEE->GetEditDoc();
684 	sal_uInt32 nPos = rNodes.GetPos( pCurNode );
685 
686 	// Sonderbehandlung: Bullet => Absatzanfang => einfach NULL returnen...
687 	const SfxBoolItem& rBulletState = (const SfxBoolItem&)
688 			pImpEE->GetParaAttrib( nPos, EE_PARA_BULLETSTATE );
689 	sal_Bool bBullet = rBulletState.GetValue() ? sal_True : sal_False;
690 	if ( !bBullet && ( pImpEE->aStatus.GetControlWord() & EE_CNTRL_OUTLINER ) )
691 	{
692 		// Der Outliner hat im Gliederungsmodus auf Ebene 0 immer ein Bullet.
693 		const SfxInt16Item& rLevel = (const SfxInt16Item&)
694 				pImpEE->GetParaAttrib( nPos, EE_PARA_OUTLLEVEL );
695 		if ( rLevel.GetValue() == 0 )
696 			bBullet = sal_True;
697 	}
698 	if ( bBullet )
699 		return NULL;
700 
701 	for ( sal_uInt32 n = nPos; n; )
702 	{
703 		n--;
704 		ContentNode* pNode = rNodes[n];
705 		if ( pNode->Len() )
706 			return pNode;
707 	}
708 	return NULL;
709 
710 }
711 
ChgAutoCorrWord(sal_uInt16 & rSttPos,sal_uInt16 nEndPos,SvxAutoCorrect & rACorrect,const String ** ppPara)712 sal_Bool EdtAutoCorrDoc::ChgAutoCorrWord( sal_uInt16& rSttPos,
713 			sal_uInt16 nEndPos, SvxAutoCorrect& rACorrect,
714 			const String** ppPara )
715 {
716 	// Absatz-Anfang oder ein Blank gefunden, suche nach dem Wort
717 	// Kuerzel im Auto
718 
719 	bAllowUndoAction = sal_False;	// Jetzt nicht mehr...
720 
721 	String aShort( pCurNode->Copy( rSttPos, nEndPos - rSttPos ) );
722 	sal_Bool bRet = sal_False;
723 
724 	if( !aShort.Len() )
725 		return bRet;
726 
727 	LanguageType eLang = pImpEE->GetLanguage( EditPaM( pCurNode, rSttPos+1 ) );
728 	const SvxAutocorrWord* pFnd = rACorrect.SearchWordsInList( *pCurNode, rSttPos, nEndPos, *this, eLang );
729 	if( pFnd && pFnd->IsTextOnly() )
730 	{
731 		// dann mal ersetzen
732 		EditSelection aSel( EditPaM( pCurNode, rSttPos ),
733 							EditPaM( pCurNode, nEndPos ) );
734 		aSel = pImpEE->ImpDeleteSelection( aSel );
735 		DBG_ASSERT( nCursor >= nEndPos, "Cursor mitten im Geschehen ?!" );
736 		nCursor -= ( nEndPos-rSttPos );
737 		pImpEE->ImpInsertText( aSel, pFnd->GetLong() );
738 		nCursor = nCursor + pFnd->GetLong().Len();
739 		if( ppPara )
740 			*ppPara = pCurNode;
741 		bRet = sal_True;
742 	}
743 
744 	return bRet;
745 }
746 
GetLanguage(sal_uInt16 nPos,sal_Bool) const747 LanguageType EdtAutoCorrDoc::GetLanguage( sal_uInt16 nPos, sal_Bool ) const
748 {
749 	return pImpEE->GetLanguage( EditPaM( pCurNode, nPos+1 ) );
750 }
751 
ImplStartUndoAction()752 void EdtAutoCorrDoc::ImplStartUndoAction()
753 {
754 	sal_uInt32 nPara = pImpEE->GetEditDoc().GetPos( pCurNode );
755 	ESelection aSel( nPara, nCursor, nPara, nCursor );
756 	pImpEE->UndoActionStart( EDITUNDO_INSERT, aSel );
757 	bUndoAction = sal_True;
758 	bAllowUndoAction = sal_False;
759 }
760 
761