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_editeng.hxx"
30 
31 #include <vcl/wrkwin.hxx>
32 #include <vcl/dialog.hxx>
33 #include <vcl/msgbox.hxx>
34 #include <vcl/svapp.hxx>
35 
36 #include <svl/srchitem.hxx>
37 #include <editeng/lspcitem.hxx>
38 #include <editeng/adjitem.hxx>
39 #include <editeng/tstpitem.hxx>
40 
41 #include <eertfpar.hxx>
42 #include <editeng/editeng.hxx>
43 #include <impedit.hxx>
44 #include <editeng/editview.hxx>
45 #include <eehtml.hxx>
46 #include <editobj2.hxx>
47 #include <i18npool/lang.h>
48 
49 #include "editxml.hxx"
50 
51 #include <editeng/akrnitem.hxx>
52 #include <editeng/cntritem.hxx>
53 #include <editeng/colritem.hxx>
54 #include <editeng/crsditem.hxx>
55 #include <editeng/escpitem.hxx>
56 #include <editeng/fhgtitem.hxx>
57 #include <editeng/fontitem.hxx>
58 #include <editeng/kernitem.hxx>
59 #include <editeng/lrspitem.hxx>
60 #include <editeng/postitem.hxx>
61 #include <editeng/shdditem.hxx>
62 #include <editeng/udlnitem.hxx>
63 #include <editeng/ulspitem.hxx>
64 #include <editeng/wghtitem.hxx>
65 #include <editeng/langitem.hxx>
66 #include <editeng/charreliefitem.hxx>
67 #include <editeng/frmdiritem.hxx>
68 #include <editeng/emphitem.hxx>
69 #include <textconv.hxx>
70 #include <rtl/tencinfo.h>
71 #include <svtools/rtfout.hxx>
72 #include <edtspell.hxx>
73 #include <editeng/scripttypeitem.hxx>
74 #include <editeng/unolingu.hxx>
75 #include <linguistic/lngprops.hxx>
76 #include <com/sun/star/linguistic2/XThesaurus.hpp>
77 #include <com/sun/star/linguistic2/XMeaning.hpp>
78 #include <com/sun/star/i18n/ScriptType.hpp>
79 #include <com/sun/star/i18n/WordType.hpp>
80 #include <com/sun/star/i18n/TransliterationModules.hpp>
81 #include <com/sun/star/i18n/TransliterationModulesExtra.hpp>
82 #include <unotools/transliterationwrapper.hxx>
83 #include <unotools/textsearch.hxx>
84 #include <comphelper/processfactory.hxx>
85 #include <vcl/help.hxx>
86 #include <svtools/rtfkeywd.hxx>
87 #include <editeng/edtdlg.hxx>
88 
89 #include <vector>
90 
91 using namespace ::com::sun::star;
92 using namespace ::com::sun::star::uno;
93 using namespace ::com::sun::star::beans;
94 using namespace ::com::sun::star::linguistic2;
95 
96 void Swapsal_uIt16s( sal_uInt16& rX, sal_uInt16& rY )
97 {
98 	sal_uInt16 n = rX;
99 	rX = rY;
100 	rY = n;
101 }
102 
103 EditPaM ImpEditEngine::Read( SvStream& rInput, const String& rBaseURL, EETextFormat eFormat, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
104 {
105 	sal_Bool _bUpdate = GetUpdateMode();
106 	SetUpdateMode( sal_False );
107 	EditPaM aPaM;
108 	if ( eFormat == EE_FORMAT_TEXT )
109 		aPaM = ReadText( rInput, aSel );
110 	else if ( eFormat == EE_FORMAT_RTF )
111 		aPaM = ReadRTF( rInput, aSel );
112 	else if ( eFormat == EE_FORMAT_XML )
113 		aPaM = ReadXML( rInput, aSel );
114 	else if ( eFormat == EE_FORMAT_HTML )
115         aPaM = ReadHTML( rInput, rBaseURL, aSel, pHTTPHeaderAttrs );
116 	else if ( eFormat == EE_FORMAT_BIN)
117 		aPaM = ReadBin( rInput, aSel );
118 	else
119 	{
120 		DBG_ERROR( "Read: Unbekanntes Format" );
121 	}
122 
123 	FormatFullDoc();		// reicht vielleicht auch ein einfaches Format?
124 	SetUpdateMode( _bUpdate );
125 
126 	return aPaM;
127 }
128 
129 EditPaM ImpEditEngine::ReadText( SvStream& rInput, EditSelection aSel )
130 {
131 	if ( aSel.HasRange() )
132 		aSel = ImpDeleteSelection( aSel );
133 	EditPaM aPaM = aSel.Max();
134 
135 	XubString aTmpStr, aStr;
136 	sal_Bool bDone = rInput.ReadByteStringLine( aTmpStr );
137 	while ( bDone )
138 	{
139 		aTmpStr.Erase( MAXCHARSINPARA );
140 		aPaM = ImpInsertText( EditSelection( aPaM, aPaM ), aTmpStr );
141 		aPaM = ImpInsertParaBreak( aPaM );
142 		bDone = rInput.ReadByteStringLine( aTmpStr );
143 	}
144 	return aPaM;
145 }
146 
147 EditPaM ImpEditEngine::ReadXML( SvStream& rInput, EditSelection aSel )
148 {
149 #ifndef SVX_LIGHT
150 	if ( aSel.HasRange() )
151 		aSel = ImpDeleteSelection( aSel );
152 
153     ESelection aESel = CreateESel( aSel );
154 
155     ::SvxReadXML( *GetEditEnginePtr(), rInput, aESel );
156 
157     return aSel.Max();
158 #else
159 	return EditPaM();
160 #endif
161 }
162 
163 EditPaM ImpEditEngine::ReadRTF( SvStream& rInput, EditSelection aSel )
164 {
165 #ifndef SVX_LIGHT
166 
167 #if defined (EDITDEBUG) && !defined( UNX )
168 	SvFileStream aRTFOut( String( RTL_CONSTASCII_USTRINGPARAM ( "d:\\rtf_in.rtf" ) ), STREAM_WRITE );
169 	aRTFOut << rInput;
170 	aRTFOut.Close();
171 	rInput.Seek( 0 );
172 #endif
173 	if ( aSel.HasRange() )
174 		aSel = ImpDeleteSelection( aSel );
175 
176 //	sal_Bool bCharsBeforeInsertPos = ( aSel.Min().GetIndex() ) ? sal_True : sal_False;
177 //	sal_Bool bCharsBehindInsertPos = ( aSel.Min().GetIndex() < aSel.Min().GetNode()->Len() ) ? sal_True : sal_False;
178 
179 	// Der SvRTF-Parser erwartet, dass das Which-Mapping am uebergebenen Pool,
180 	// nicht an einem Secondary haengt.
181 	SfxItemPool* pPool = &aEditDoc.GetItemPool();
182 	while ( pPool->GetSecondaryPool() && !pPool->GetName().EqualsAscii( "EditEngineItemPool" ) )
183 	{
184 		pPool = pPool->GetSecondaryPool();
185 
186 	}
187 	DBG_ASSERT( pPool && pPool->GetName().EqualsAscii( "EditEngineItemPool" ), "ReadRTF: Kein EditEnginePool!" );
188 
189 	EditRTFParserRef xPrsr = new EditRTFParser( rInput, aSel, *pPool, this );
190 	SvParserState eState = xPrsr->CallParser();
191 	if ( ( eState != SVPAR_ACCEPTED ) && ( !rInput.GetError() ) )
192 	{
193 		rInput.SetError( EE_READWRITE_WRONGFORMAT );
194 		return aSel.Min();
195 	}
196 	return xPrsr->GetCurPaM();
197 #else
198 	return EditPaM();
199 #endif
200 }
201 
202 EditPaM ImpEditEngine::ReadHTML( SvStream& rInput, const String& rBaseURL, EditSelection aSel, SvKeyValueIterator* pHTTPHeaderAttrs )
203 {
204 #ifndef SVX_LIGHT
205 
206 	if ( aSel.HasRange() )
207 		aSel = ImpDeleteSelection( aSel );
208 
209 //	sal_Bool bCharsBeforeInsertPos = ( aSel.Min().GetIndex() ) ? sal_True : sal_False;
210 //	sal_Bool bCharsBehindInsertPos = ( aSel.Min().GetIndex() < aSel.Min().GetNode()->Len() ) ? sal_True : sal_False;
211 
212     EditHTMLParserRef xPrsr = new EditHTMLParser( rInput, rBaseURL, pHTTPHeaderAttrs );
213 	SvParserState eState = xPrsr->CallParser( this, aSel.Max() );
214 	if ( ( eState != SVPAR_ACCEPTED ) && ( !rInput.GetError() ) )
215 	{
216 		rInput.SetError( EE_READWRITE_WRONGFORMAT );
217 		return aSel.Min();
218 	}
219 	return xPrsr->GetCurSelection().Max();
220 #else
221 	return EditPaM();
222 #endif
223 }
224 
225 EditPaM ImpEditEngine::ReadBin( SvStream& rInput, EditSelection aSel )
226 {
227 	// Einfach ein temporaeres TextObject missbrauchen...
228 	EditTextObject* pObj = EditTextObject::Create( rInput, NULL );
229 
230 	EditPaM aLastPaM = aSel.Max();
231 	if ( pObj )
232 		aLastPaM = InsertText( *pObj, aSel ).Max();
233 
234 	delete pObj;
235 	return aLastPaM;
236 }
237 
238 #ifndef SVX_LIGHT
239 void ImpEditEngine::Write( SvStream& rOutput, EETextFormat eFormat, EditSelection aSel )
240 {
241 	if ( !rOutput.IsWritable() )
242 		rOutput.SetError( SVSTREAM_WRITE_ERROR );
243 
244 	if ( !rOutput.GetError() )
245 	{
246 		if ( eFormat == EE_FORMAT_TEXT )
247 			WriteText( rOutput, aSel );
248 		else if ( eFormat == EE_FORMAT_RTF )
249 			WriteRTF( rOutput, aSel );
250 		else if ( eFormat == EE_FORMAT_XML )
251 			WriteXML( rOutput, aSel );
252 		else if ( eFormat == EE_FORMAT_HTML )
253 			WriteHTML( rOutput, aSel );
254 		else if ( eFormat == EE_FORMAT_BIN)
255 			WriteBin( rOutput, aSel );
256 		else
257 		{
258 			DBG_ERROR( "Write: Unbekanntes Format" );
259 		}
260 	}
261 }
262 #endif
263 
264 sal_uInt32 ImpEditEngine::WriteText( SvStream& rOutput, EditSelection aSel )
265 {
266 	sal_uInt16 nStartNode, nEndNode;
267 	sal_Bool bRange = aSel.HasRange();
268 	if ( bRange )
269 	{
270 		aSel.Adjust( aEditDoc );
271 		nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
272 		nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
273 	}
274 	else
275 	{
276 		nStartNode = 0;
277 		nEndNode = aEditDoc.Count()-1;
278 	}
279 
280 	// ueber die Absaetze iterieren...
281 	for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++  )
282 	{
283 		ContentNode* pNode = aEditDoc.GetObject( nNode );
284 		DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );
285 
286 		sal_uInt16 nStartPos = 0;
287 		sal_uInt16 nEndPos = pNode->Len();
288 		if ( bRange )
289 		{
290 			if ( nNode == nStartNode )
291 				nStartPos = aSel.Min().GetIndex();
292 			if ( nNode == nEndNode ) // kann auch == nStart sein!
293 				nEndPos = aSel.Max().GetIndex();
294 		}
295 		XubString aTmpStr = aEditDoc.GetParaAsString( pNode, nStartPos, nEndPos );
296 		rOutput.WriteByteStringLine( aTmpStr );
297 	}
298 
299 	return rOutput.GetError();
300 }
301 
302 sal_Bool ImpEditEngine::WriteItemListAsRTF( ItemList& rLst, SvStream& rOutput, sal_uInt16 nPara, sal_uInt16 nPos,
303 						SvxFontTable& rFontTable, SvxColorList& rColorList )
304 {
305 	const SfxPoolItem* pAttrItem = rLst.First();
306 	while ( pAttrItem )
307 	{
308 		WriteItemAsRTF( *pAttrItem, rOutput, nPara, nPos,rFontTable, rColorList );
309 		pAttrItem = rLst.Next();
310 	}
311 	return ( rLst.Count() ? sal_True : sal_False );
312 }
313 
314 void lcl_FindValidAttribs( ItemList& rLst, ContentNode* pNode, sal_uInt16 nIndex, sal_uInt16 nScriptType )
315 {
316 	sal_uInt16 nAttr = 0;
317 	EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
318 	while ( pAttr && ( pAttr->GetStart() <= nIndex ) )
319 	{
320 		// Start wird in While ueberprueft...
321 		if ( pAttr->GetEnd() > nIndex )
322         {
323             if ( IsScriptItemValid( pAttr->GetItem()->Which(), nScriptType ) )
324 			    rLst.Insert( pAttr->GetItem(), LIST_APPEND );
325 		}
326 		nAttr++;
327 		pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
328 	}
329 }
330 
331 sal_uInt32 ImpEditEngine::WriteBin( SvStream& rOutput, EditSelection aSel, sal_Bool bStoreUnicodeStrings ) const
332 {
333 	BinTextObject* pObj = (BinTextObject*)CreateBinTextObject( aSel, NULL );
334 	pObj->StoreUnicodeStrings( bStoreUnicodeStrings );
335 	pObj->Store( rOutput );
336 	delete pObj;
337 	return 0;
338 }
339 
340 #ifndef SVX_LIGHT
341 sal_uInt32 ImpEditEngine::WriteXML( SvStream& rOutput, EditSelection aSel )
342 {
343     ESelection aESel = CreateESel( aSel );
344 
345     SvxWriteXML( *GetEditEnginePtr(), rOutput, aESel );
346 
347     return 0;
348 }
349 #endif
350 
351 static sal_uInt16 getStylePos( const SfxStyles& rStyles, SfxStyleSheet* pSheet )
352 {
353 	sal_uInt16 nNumber = 0;
354 	SfxStyles::const_iterator iter( rStyles.begin() );
355 	while( iter != rStyles.end() )
356 	{
357 		if( (*iter++).get() == pSheet )
358 			return nNumber;
359 		++nNumber;
360 	}
361 	return 0;
362 }
363 
364 sal_uInt32 ImpEditEngine::WriteRTF( SvStream& rOutput, EditSelection aSel )
365 {
366 #ifndef SVX_LIGHT
367 	DBG_ASSERT( GetUpdateMode(), "WriteRTF bei UpdateMode = sal_False!" );
368 	CheckIdleFormatter();
369 	if ( !IsFormatted() )
370 		FormatDoc();
371 
372 	sal_uInt16 nStartNode, nEndNode;
373 	aSel.Adjust( aEditDoc );
374 
375 	nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
376 	nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
377 
378 	// RTF-Vorspann...
379 	rOutput << '{' ;
380 
381 	rOutput << OOO_STRING_SVTOOLS_RTF_RTF;
382 
383 	rOutput << OOO_STRING_SVTOOLS_RTF_ANSI;
384 	rtl_TextEncoding eDestEnc = RTL_TEXTENCODING_MS_1252;
385 
386 	// Fonttabelle erzeugen und rausschreiben...
387 	SvxFontTable aFontTable;
388 	// DefaultFont muss ganz vorne stehen, damit DEF-Font im RTF
389 	aFontTable.Insert( 0, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO ) ) );
390 	aFontTable.Insert( 1, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CJK ) ) );
391 	aFontTable.Insert( 2, new SvxFontItem( (const SvxFontItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_FONTINFO_CTL ) ) );
392     for ( sal_uInt16 nScriptType = 0; nScriptType < 3; nScriptType++ )
393     {
394         sal_uInt16 nWhich = EE_CHAR_FONTINFO;
395         if ( nScriptType == 1 )
396             nWhich = EE_CHAR_FONTINFO_CJK;
397         else if ( nScriptType == 2 )
398             nWhich = EE_CHAR_FONTINFO_CTL;
399 
400         sal_uInt32 i = 0;
401 	    SvxFontItem* pFontItem = (SvxFontItem*)aEditDoc.GetItemPool().GetItem2( nWhich, i );
402 	    while ( pFontItem )
403 	    {
404             bool bAlreadyExist = false;
405             sal_uLong nTestMax = nScriptType ? aFontTable.Count() : 1;
406             for ( sal_uLong nTest = 0; !bAlreadyExist && ( nTest < nTestMax ); nTest++ )
407             {
408                 bAlreadyExist = *aFontTable.Get( nTest ) == *pFontItem;
409             }
410 
411             if ( !bAlreadyExist )
412 		        aFontTable.Insert( aFontTable.Count(), new SvxFontItem( *pFontItem ) );
413 
414             pFontItem = (SvxFontItem*)aEditDoc.GetItemPool().GetItem2( nWhich, ++i );
415 	    }
416     }
417 
418 	rOutput << endl << '{' << OOO_STRING_SVTOOLS_RTF_FONTTBL;
419 	sal_uInt16 j;
420 	for ( j = 0; j < aFontTable.Count(); j++ )
421 	{
422 		SvxFontItem* pFontItem = aFontTable.Get( j );
423 		rOutput << '{';
424 		rOutput << OOO_STRING_SVTOOLS_RTF_F;
425 		rOutput.WriteNumber( j );
426 		switch ( pFontItem->GetFamily()  )
427 		{
428 			case FAMILY_DONTKNOW:		rOutput << OOO_STRING_SVTOOLS_RTF_FNIL;
429 										break;
430 			case FAMILY_DECORATIVE:		rOutput << OOO_STRING_SVTOOLS_RTF_FDECOR;
431 										break;
432 			case FAMILY_MODERN:			rOutput << OOO_STRING_SVTOOLS_RTF_FMODERN;
433 										break;
434 			case FAMILY_ROMAN:			rOutput << OOO_STRING_SVTOOLS_RTF_FROMAN;
435 										break;
436 			case FAMILY_SCRIPT:			rOutput << OOO_STRING_SVTOOLS_RTF_FSCRIPT;
437 										break;
438 			case FAMILY_SWISS:			rOutput << OOO_STRING_SVTOOLS_RTF_FSWISS;
439 										break;
440 			default:
441 				break;
442 		}
443 		rOutput << OOO_STRING_SVTOOLS_RTF_FPRQ;
444 		sal_uInt16 nVal = 0;
445 		switch( pFontItem->GetPitch() )
446 		{
447 			case PITCH_FIXED:		nVal = 1;		break;
448 			case PITCH_VARIABLE:	nVal = 2;		break;
449 			default:
450 				break;
451 		}
452 		rOutput.WriteNumber( nVal );
453 
454 		CharSet eChrSet = pFontItem->GetCharSet();
455 		DBG_ASSERT( eChrSet != 9, "SystemCharSet?!" );
456 		if( RTL_TEXTENCODING_DONTKNOW == eChrSet )
457 			eChrSet = gsl_getSystemTextEncoding();
458 		rOutput << OOO_STRING_SVTOOLS_RTF_FCHARSET;
459 		rOutput.WriteNumber( rtl_getBestWindowsCharsetFromTextEncoding( eChrSet ) );
460 
461 		rOutput << ' ';
462         RTFOutFuncs::Out_String( rOutput, pFontItem->GetFamilyName(), eDestEnc );
463 		rOutput << ";}";
464 	}
465 	rOutput << '}';
466 	rOutput << endl;
467 
468 	// ColorList rausschreiben...
469 	SvxColorList aColorList;
470 	sal_uInt32 i = 0;
471 	SvxColorItem* pColorItem = (SvxColorItem*)aEditDoc.GetItemPool().GetItem2( EE_CHAR_COLOR, i );
472 	while ( pColorItem )
473 	{
474 		sal_uInt32 nPos = i;
475 		if ( pColorItem->GetValue() == COL_AUTO )
476 			nPos = 0;
477 		aColorList.Insert( new SvxColorItem( *pColorItem ), nPos );
478 		pColorItem = (SvxColorItem*)aEditDoc.GetItemPool().GetItem2( EE_CHAR_COLOR, ++i );
479 	}
480 	aColorList.Insert( new SvxColorItem( (const SvxColorItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_COLOR) ), i );
481 
482 	rOutput << '{' << OOO_STRING_SVTOOLS_RTF_COLORTBL;
483 	for ( j = 0; j < aColorList.Count(); j++ )
484 	{
485 		pColorItem = aColorList.GetObject( j );
486 		if ( !j || ( pColorItem->GetValue() != COL_AUTO ) )
487 		{
488 			rOutput << OOO_STRING_SVTOOLS_RTF_RED;
489 			rOutput.WriteNumber( pColorItem->GetValue().GetRed() );
490 			rOutput << OOO_STRING_SVTOOLS_RTF_GREEN;
491 			rOutput.WriteNumber( pColorItem->GetValue().GetGreen() );
492 			rOutput << OOO_STRING_SVTOOLS_RTF_BLUE;
493 			rOutput.WriteNumber( pColorItem->GetValue().GetBlue() );
494 		}
495 		rOutput << ';';
496 	}
497 	rOutput << '}';
498 	rOutput << endl;
499 
500 	// StyleSheets...
501 	if ( GetStyleSheetPool() )
502 	{
503 		sal_uInt16 nStyles = (sal_uInt16)GetStyleSheetPool()->GetStyles().size();
504 		if ( nStyles )
505 		{
506 			rOutput << '{' << OOO_STRING_SVTOOLS_RTF_STYLESHEET;
507 
508 			for ( sal_uInt16 nStyle = 0; nStyle < nStyles; nStyle++ )
509 			{
510 
511 				SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->GetStyles()[ nStyle ].get();
512 
513 				rOutput << endl << '{' << OOO_STRING_SVTOOLS_RTF_S;
514 				sal_uInt16 nNumber = (sal_uInt16) (nStyle + 1);
515 				rOutput.WriteNumber( nNumber );
516 
517 				// Attribute, auch aus Parent!
518 				for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
519 				{
520 					if ( pStyle->GetItemSet().GetItemState( nParAttr ) == SFX_ITEM_ON )
521 					{
522 						const SfxPoolItem& rItem = pStyle->GetItemSet().Get( nParAttr );
523 						WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
524 					}
525 				}
526 
527 				// Parent...(nur wenn noetig)
528 				if ( pStyle->GetParent().Len() && ( pStyle->GetParent() != pStyle->GetName() ) )
529 				{
530 					SfxStyleSheet* pParent = (SfxStyleSheet*)GetStyleSheetPool()->Find( pStyle->GetParent(), pStyle->GetFamily() );
531 					DBG_ASSERT( pParent, "Parent nicht gefunden!" );
532 					rOutput << OOO_STRING_SVTOOLS_RTF_SBASEDON;
533 					nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pParent ) + 1;
534 					rOutput.WriteNumber( nNumber );
535 				}
536 
537 				// Folgevorlage...(immer)
538 				SfxStyleSheet* pNext = pStyle;
539 				if ( pStyle->GetFollow().Len() && ( pStyle->GetFollow() != pStyle->GetName() ) )
540 					pNext = (SfxStyleSheet*)GetStyleSheetPool()->Find( pStyle->GetFollow(), pStyle->GetFamily() );
541 
542 				DBG_ASSERT( pNext, "Naechsten nicht gefunden!" );
543 				rOutput << OOO_STRING_SVTOOLS_RTF_SNEXT;
544 				nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pNext ) + 1;
545 				rOutput.WriteNumber( nNumber );
546 
547 				// Namen der Vorlage...
548 				rOutput << " " << ByteString( pStyle->GetName(), eDestEnc ).GetBuffer();
549 				rOutput << ";}";
550 			}
551 			rOutput << '}';
552 			rOutput << endl;
553 		}
554 	}
555 
556 	// Die Pool-Defaults vorweg schreiben...
557 	rOutput << '{' << OOO_STRING_SVTOOLS_RTF_IGNORE << "\\EditEnginePoolDefaults";
558 	for ( sal_uInt16 nPoolDefItem = EE_PARA_START; nPoolDefItem <= EE_CHAR_END; nPoolDefItem++)
559 	{
560 		const SfxPoolItem& rItem = aEditDoc.GetItemPool().GetDefaultItem( nPoolDefItem );
561 		WriteItemAsRTF( rItem, rOutput, 0, 0, aFontTable, aColorList );
562 	}
563 	rOutput << '}' << endl;
564 
565 	// Def-Hoehe vorweg, da sonst 12Pt
566 	// Doch nicht, onst in jedem Absatz hart!
567 	// SfxItemSet aTmpSet( GetEmptyItemSet() );
568 	// const SvxFontHeightItem& rDefFontHeight = (const SvxFontHeightItem&)aTmpSet.Get( EE_CHAR_FONTHEIGHT );
569 	// WriteItemAsRTF( rDefFontHeight, rOutput, aFontTable, aColorList );
570 	// rOutput << '{' << OOO_STRING_SVTOOLS_RTF_IGNORE << "\\EditEnginePoolDefaultHeight}" << endl;
571 
572 	// DefTab:
573 	MapMode aTwpMode( MAP_TWIP );
574 	sal_uInt16 nDefTabTwps = (sal_uInt16) GetRefDevice()->LogicToLogic(
575 										Point( aEditDoc.GetDefTab(), 0 ),
576 										&GetRefMapMode(), &aTwpMode ).X();
577 	rOutput << OOO_STRING_SVTOOLS_RTF_DEFTAB;
578 	rOutput.WriteNumber( nDefTabTwps );
579 	rOutput << endl;
580 
581 	// ueber die Absaetze iterieren...
582 	rOutput << '{' << endl;
583 	for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++  )
584 	{
585 		ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
586 		DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );
587 
588 		// Die Absatzattribute vorweg...
589 		sal_Bool bAttr = sal_False;
590 
591 		// Vorlage ?
592 		if ( pNode->GetStyleSheet() )
593 		{
594 			// Nummer der Vorlage
595 			rOutput << OOO_STRING_SVTOOLS_RTF_S;
596 			sal_uInt16 nNumber = (sal_uInt16) getStylePos( GetStyleSheetPool()->GetStyles(), pNode->GetStyleSheet() ) + 1;
597 			rOutput.WriteNumber( nNumber );
598 
599 			// Alle Attribute
600 			// Attribute, auch aus Parent!
601 			for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
602 			{
603 				if ( pNode->GetStyleSheet()->GetItemSet().GetItemState( nParAttr ) == SFX_ITEM_ON )
604 				{
605 					const SfxPoolItem& rItem = pNode->GetStyleSheet()->GetItemSet().Get( nParAttr );
606 					WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
607 					bAttr = sal_True;
608 				}
609 			}
610 		}
611 
612 		for ( sal_uInt16 nParAttr = EE_PARA_START; nParAttr <= EE_CHAR_END; nParAttr++ )
613 		{
614 //			const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItem( nParAttr );
615 			// Jetzt, wo StyleSheet-Verarbeitung, nur noch harte Absatzattribute!
616 			if ( pNode->GetContentAttribs().GetItems().GetItemState( nParAttr ) == SFX_ITEM_ON )
617 			{
618 				const SfxPoolItem& rItem = pNode->GetContentAttribs().GetItems().Get( nParAttr );
619 				WriteItemAsRTF( rItem, rOutput, nNode, 0, aFontTable, aColorList );
620 				bAttr = sal_True;
621 			}
622 		}
623 		if ( bAttr )
624 			rOutput << ' ';	// Separator
625 
626 		ItemList aAttribItems;
627 		ParaPortion* pParaPortion = FindParaPortion( pNode );
628 		DBG_ASSERT( pParaPortion, "Portion nicht gefunden: WriteRTF" );
629 
630 		sal_uInt16 nIndex = 0;
631 		sal_uInt16 nStartPos = 0;
632 		sal_uInt16 nEndPos = pNode->Len();
633 		sal_uInt16 nStartPortion = 0;
634 		sal_uInt16 nEndPortion = (sal_uInt16)pParaPortion->GetTextPortions().Count() - 1;
635 		sal_Bool bFinishPortion = sal_False;
636 		sal_uInt16 nPortionStart;
637 
638 		if ( nNode == nStartNode )
639 		{
640 			nStartPos = aSel.Min().GetIndex();
641 			nStartPortion = pParaPortion->GetTextPortions().FindPortion( nStartPos, nPortionStart );
642 			if ( nStartPos != 0 )
643 			{
644 				aAttribItems.Clear();
645                 lcl_FindValidAttribs( aAttribItems, pNode, nStartPos, GetScriptType( EditPaM( pNode, 0 ) ) );
646 				if ( aAttribItems.Count() )
647 				{
648 					// Diese Attribute duerfen nicht fuer den gesamten
649 					// Absatz gelten:
650 					rOutput << '{';
651 					WriteItemListAsRTF( aAttribItems, rOutput, nNode, nStartPos, aFontTable, aColorList );
652 					bFinishPortion = sal_True;
653 				}
654 				aAttribItems.Clear();
655 			}
656 		}
657 		if ( nNode == nEndNode ) // kann auch == nStart sein!
658 		{
659 			nEndPos = aSel.Max().GetIndex();
660 			nEndPortion = pParaPortion->GetTextPortions().FindPortion( nEndPos, nPortionStart );
661 		}
662 
663 		EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( nIndex );
664 		// Bei 0 anfangen, damit der Index richtig ist...
665 
666 		for ( sal_uInt16 n = 0; n <= nEndPortion; n++ )
667 		{
668 			TextPortion* pTextPortion = pParaPortion->GetTextPortions().GetObject(n);
669 			if ( n < nStartPortion )
670 			{
671 				nIndex = nIndex + pTextPortion->GetLen();
672 				continue;
673 			}
674 
675 			if ( pNextFeature && ( pNextFeature->GetStart() == nIndex ) && ( pNextFeature->GetItem()->Which() != EE_FEATURE_FIELD ) )
676 			{
677 				WriteItemAsRTF( *pNextFeature->GetItem(), rOutput, nNode, nIndex, aFontTable, aColorList );
678 				pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
679 			}
680 			else
681 			{
682 				aAttribItems.Clear();
683                 sal_uInt16 nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
684                 if ( !n || IsScriptChange( EditPaM( pNode, nIndex ) ) )
685                 {
686                     SfxItemSet aAttribs = GetAttribs( nNode, nIndex+1, nIndex+1 );
687                     aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) ), LIST_APPEND );
688                     aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ), LIST_APPEND );
689                     aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ) ), LIST_APPEND );
690                     aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ) ), LIST_APPEND );
691                     aAttribItems.Insert( &aAttribs.Get( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ) ), LIST_APPEND );
692                 }
693                 // #96298# Insert hard attribs AFTER CJK attribs...
694 				lcl_FindValidAttribs( aAttribItems, pNode, nIndex, nScriptType );
695 
696 				rOutput << '{';
697 				if ( WriteItemListAsRTF( aAttribItems, rOutput, nNode, nIndex, aFontTable, aColorList ) )
698 					rOutput << ' ';
699 
700 				sal_uInt16 nS = nIndex;
701 				sal_uInt16 nE = nIndex + pTextPortion->GetLen();
702 				if ( n == nStartPortion )
703 					nS = nStartPos;
704 				if ( n == nEndPortion )
705 					nE = nEndPos;
706 
707 				XubString aRTFStr = aEditDoc.GetParaAsString( pNode, nS, nE);
708 				RTFOutFuncs::Out_String( rOutput, aRTFStr, eDestEnc );
709 				rOutput << '}';
710 			}
711 			if ( bFinishPortion )
712 			{
713 				rOutput << '}';
714 				bFinishPortion = sal_False;
715 			}
716 
717 			nIndex = nIndex + pTextPortion->GetLen();
718 		}
719 
720 		rOutput << OOO_STRING_SVTOOLS_RTF_PAR << OOO_STRING_SVTOOLS_RTF_PARD << OOO_STRING_SVTOOLS_RTF_PLAIN;;
721 		rOutput << endl;
722 	}
723 	// RTF-Nachspann...
724 	rOutput << "}}";	// 1xKlammerung Absaetze, 1x Klammerung RTF-Dokument
725 	rOutput.Flush();
726 
727 #if defined (EDITDEBUG) && !defined( UNX )
728     {
729         SvFileStream aStream( String( RTL_CONSTASCII_USTRINGPARAM ( "d:\\rtf_out.rtf" ) ), STREAM_WRITE|STREAM_TRUNC );
730         sal_uLong nP = rOutput.Tell();
731         rOutput.Seek( 0 );
732         aStream << rOutput;
733         rOutput.Seek( nP );
734     }
735 #endif
736 
737 	return rOutput.GetError();
738 #else
739 	return 0;
740 #endif
741 }
742 
743 
744 void ImpEditEngine::WriteItemAsRTF( const SfxPoolItem& rItem, SvStream& rOutput, sal_uInt16 nPara, sal_uInt16 nPos,
745 							SvxFontTable& rFontTable, SvxColorList& rColorList )
746 {
747 	sal_uInt16 nWhich = rItem.Which();
748 	switch ( nWhich )
749 	{
750         case EE_PARA_WRITINGDIR:
751         {
752             const SvxFrameDirectionItem& rWritingMode = (const SvxFrameDirectionItem&)rItem;
753             if ( rWritingMode.GetValue() == FRMDIR_HORI_RIGHT_TOP )
754                 rOutput << "\\rtlpar";
755             else
756                 rOutput << "\\ltrpar";
757         }
758         break;
759 		case EE_PARA_OUTLLEVEL:
760 		{
761 			sal_Int16 nLevel = ((const SfxInt16Item&)rItem).GetValue();
762 			if( nLevel >= 0 )
763 			{
764 				rOutput << "\\level";
765 				rOutput.WriteNumber( nLevel );
766 			}
767 		}
768 		break;
769 		case EE_PARA_OUTLLRSPACE:
770 		case EE_PARA_LRSPACE:
771 		{
772 //            const ContentNode *pNode = aEditDoc.GetObject( nPara );
773 
774 			rOutput << OOO_STRING_SVTOOLS_RTF_FI;
775 			short nTxtFirst = ((const SvxLRSpaceItem&)rItem).GetTxtFirstLineOfst();
776 			nTxtFirst = (short)LogicToTwips( nTxtFirst );
777 			rOutput.WriteNumber( nTxtFirst );
778 			rOutput << OOO_STRING_SVTOOLS_RTF_LI;
779             sal_uInt16 nTxtLeft = static_cast< sal_uInt16 >(((const SvxLRSpaceItem&)rItem).GetTxtLeft());
780 			nTxtLeft = (sal_uInt16)LogicToTwips( nTxtLeft );
781 			rOutput.WriteNumber( nTxtLeft );
782 			rOutput << OOO_STRING_SVTOOLS_RTF_RI;
783 			sal_uInt32 nTxtRight = ((const SvxLRSpaceItem&)rItem).GetRight();
784 			nTxtRight = LogicToTwips( nTxtRight);
785 			rOutput.WriteNumber( nTxtRight );
786 		}
787 		break;
788 		case EE_PARA_ULSPACE:
789 		{
790 			rOutput << OOO_STRING_SVTOOLS_RTF_SB;
791 			sal_uInt16 nUpper = ((const SvxULSpaceItem&)rItem).GetUpper();
792 			nUpper = (sal_uInt16)LogicToTwips( nUpper );
793 			rOutput.WriteNumber( nUpper );
794 			rOutput << OOO_STRING_SVTOOLS_RTF_SA;
795 			sal_uInt16 nLower = ((const SvxULSpaceItem&)rItem).GetLower();
796 			nLower = (sal_uInt16)LogicToTwips( nLower );
797 			rOutput.WriteNumber( nLower );
798 		}
799 		break;
800 		case EE_PARA_SBL:
801 		{
802 			rOutput << OOO_STRING_SVTOOLS_RTF_SL;
803 			long nVal = ((const SvxLineSpacingItem&)rItem).GetLineHeight();
804             char cMult = '0';
805 			if ( ((const SvxLineSpacingItem&)rItem).GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
806 			{
807 				// Woher kriege ich jetzt den Wert?
808 				// Der SwRTF-Parser geht von einem 240er Font aus!
809 				nVal = ((const SvxLineSpacingItem&)rItem).GetPropLineSpace();
810 				nVal *= 240;
811 				nVal /= 100;
812                 cMult = '1';
813 			}
814 			rOutput.WriteNumber( nVal );
815             rOutput << OOO_STRING_SVTOOLS_RTF_SLMULT << cMult;
816 		}
817 		break;
818 		case EE_PARA_JUST:
819 		{
820 			SvxAdjust eJustification = ((const SvxAdjustItem&)rItem).GetAdjust();
821 			switch ( eJustification )
822 			{
823 				case SVX_ADJUST_CENTER:	rOutput << OOO_STRING_SVTOOLS_RTF_QC;
824 										break;
825 				case SVX_ADJUST_RIGHT:	rOutput << OOO_STRING_SVTOOLS_RTF_QR;
826 										break;
827 				default:				rOutput << OOO_STRING_SVTOOLS_RTF_QL;
828 										break;
829 			}
830 		}
831 		break;
832 		case EE_PARA_TABS:
833 		{
834 			const SvxTabStopItem& rTabs = (const SvxTabStopItem&) rItem;
835 			for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
836 			{
837 				const SvxTabStop& rTab = rTabs[i];
838 				rOutput << OOO_STRING_SVTOOLS_RTF_TX;
839 				rOutput.WriteNumber( LogicToTwips( rTab.GetTabPos() ) );
840 			}
841 		}
842 		break;
843 		case EE_CHAR_COLOR:
844 		{
845 			sal_uInt32 n = rColorList.GetId( (const SvxColorItem&)rItem );
846 			rOutput << OOO_STRING_SVTOOLS_RTF_CF;
847 			rOutput.WriteNumber( n );
848 		}
849 		break;
850 		case EE_CHAR_FONTINFO:
851 		case EE_CHAR_FONTINFO_CJK:
852 		case EE_CHAR_FONTINFO_CTL:
853 		{
854 			sal_uInt32 n = rFontTable.GetId( (const SvxFontItem&)rItem );
855 			rOutput << OOO_STRING_SVTOOLS_RTF_F;
856 			rOutput.WriteNumber( n );
857 		}
858 		break;
859 		case EE_CHAR_FONTHEIGHT:
860 		case EE_CHAR_FONTHEIGHT_CJK:
861 		case EE_CHAR_FONTHEIGHT_CTL:
862 		{
863 			rOutput << OOO_STRING_SVTOOLS_RTF_FS;
864 			long nHeight = ((const SvxFontHeightItem&)rItem).GetHeight();
865 			nHeight = LogicToTwips( nHeight );
866 			// Twips => HalfPoints
867 			nHeight /= 10;
868 			rOutput.WriteNumber( nHeight );
869 		}
870 		break;
871 		case EE_CHAR_WEIGHT:
872 		case EE_CHAR_WEIGHT_CJK:
873 		case EE_CHAR_WEIGHT_CTL:
874 		{
875 			FontWeight e = ((const SvxWeightItem&)rItem).GetWeight();
876 			switch ( e )
877 			{
878 				case WEIGHT_BOLD:	rOutput << OOO_STRING_SVTOOLS_RTF_B;				break;
879 				default:			rOutput << OOO_STRING_SVTOOLS_RTF_B << '0';		break;
880 			}
881 		}
882 		break;
883 		case EE_CHAR_UNDERLINE:
884 		{
885 			// muesste bei WordLineMode ggf. ulw werden,
886 			// aber die Information fehlt hier
887 			FontUnderline e = ((const SvxUnderlineItem&)rItem).GetLineStyle();
888 			switch ( e )
889 			{
890 				case UNDERLINE_NONE:	rOutput << OOO_STRING_SVTOOLS_RTF_ULNONE;		break;
891 				case UNDERLINE_SINGLE:	rOutput << OOO_STRING_SVTOOLS_RTF_UL; 		break;
892 				case UNDERLINE_DOUBLE:	rOutput << OOO_STRING_SVTOOLS_RTF_ULDB;		break;
893 				case UNDERLINE_DOTTED:	rOutput << OOO_STRING_SVTOOLS_RTF_ULD;		break;
894 				default:
895 					break;
896 			}
897 		}
898 		break;
899 		case EE_CHAR_OVERLINE:
900 		{
901 			FontUnderline e = ((const SvxOverlineItem&)rItem).GetLineStyle();
902 			switch ( e )
903 			{
904 				case UNDERLINE_NONE:	rOutput << OOO_STRING_SVTOOLS_RTF_OLNONE;		break;
905 				case UNDERLINE_SINGLE:	rOutput << OOO_STRING_SVTOOLS_RTF_OL; 		break;
906 				case UNDERLINE_DOUBLE:	rOutput << OOO_STRING_SVTOOLS_RTF_OLDB;		break;
907 				case UNDERLINE_DOTTED:	rOutput << OOO_STRING_SVTOOLS_RTF_OLD;		break;
908 				default:
909 					break;
910 			}
911 		}
912 		break;
913 		case EE_CHAR_STRIKEOUT:
914 		{
915 			FontStrikeout e = ((const SvxCrossedOutItem&)rItem).GetStrikeout();
916 			switch ( e )
917 			{
918 				case STRIKEOUT_SINGLE:
919 				case STRIKEOUT_DOUBLE:	rOutput << OOO_STRING_SVTOOLS_RTF_STRIKE; 		break;
920 				case STRIKEOUT_NONE:	rOutput << OOO_STRING_SVTOOLS_RTF_STRIKE << '0';	break;
921 				default:
922 					break;
923 			}
924 		}
925 		break;
926 		case EE_CHAR_ITALIC:
927 		case EE_CHAR_ITALIC_CJK:
928 		case EE_CHAR_ITALIC_CTL:
929 		{
930 			FontItalic e = ((const SvxPostureItem&)rItem).GetPosture();
931 			switch ( e )
932 			{
933 				case ITALIC_OBLIQUE:
934 				case ITALIC_NORMAL:	rOutput << OOO_STRING_SVTOOLS_RTF_I; 		break;
935 				case ITALIC_NONE:	rOutput << OOO_STRING_SVTOOLS_RTF_I << '0';	break;
936 				default:
937 					break;
938 			}
939 		}
940 		break;
941 		case EE_CHAR_OUTLINE:
942 		{
943 			rOutput << OOO_STRING_SVTOOLS_RTF_OUTL;
944 			if ( ((const SvxContourItem&)rItem).GetValue() == 0 )
945 				rOutput << '0';
946 		}
947 		break;
948 		case EE_CHAR_RELIEF:
949 		{
950 			sal_uInt16 nRelief = ((const SvxCharReliefItem&)rItem).GetValue();
951 			if ( nRelief == RELIEF_EMBOSSED )
952 				rOutput << OOO_STRING_SVTOOLS_RTF_EMBO;
953 			if ( nRelief == RELIEF_ENGRAVED )
954 				rOutput << OOO_STRING_SVTOOLS_RTF_IMPR;
955 		}
956 		break;
957 		case EE_CHAR_EMPHASISMARK:
958 		{
959 			sal_uInt16 nMark = ((const SvxEmphasisMarkItem&)rItem).GetValue();
960 			if ( nMark == EMPHASISMARK_NONE )
961 				rOutput << OOO_STRING_SVTOOLS_RTF_ACCNONE;
962 			else if ( nMark == EMPHASISMARK_SIDE_DOTS )
963 				rOutput << OOO_STRING_SVTOOLS_RTF_ACCCOMMA;
964 			else
965 				rOutput << OOO_STRING_SVTOOLS_RTF_ACCDOT;
966 		}
967 		break;
968 		case EE_CHAR_SHADOW:
969 		{
970 			rOutput << OOO_STRING_SVTOOLS_RTF_SHAD;
971 			if ( ((const SvxShadowedItem&)rItem).GetValue() == 0 )
972 				rOutput << '0';
973 		}
974 		break;
975 		case EE_FEATURE_TAB:
976 		{
977 			rOutput << OOO_STRING_SVTOOLS_RTF_TAB;
978 		}
979 		break;
980 		case EE_FEATURE_LINEBR:
981 		{
982 			rOutput << OOO_STRING_SVTOOLS_RTF_SL;
983 		}
984 		break;
985 		case EE_CHAR_KERNING:
986 		{
987 			rOutput << OOO_STRING_SVTOOLS_RTF_EXPNDTW;
988 			rOutput.WriteNumber( LogicToTwips(
989 				((const SvxKerningItem&)rItem).GetValue() ) );
990 		}
991 		break;
992 		case EE_CHAR_PAIRKERNING:
993 		{
994 			rOutput << OOO_STRING_SVTOOLS_RTF_KERNING;
995 			rOutput.WriteNumber( ((const SvxAutoKernItem&)rItem).GetValue() ? 1 : 0 );
996 		}
997 		break;
998 		case EE_CHAR_ESCAPEMENT:
999 		{
1000 			SvxFont aFont;
1001 			ContentNode* pNode = aEditDoc.GetObject( nPara );
1002 			SeekCursor( pNode, nPos, aFont );
1003 			MapMode aPntMode( MAP_POINT );
1004 			long nFontHeight = GetRefDevice()->LogicToLogic(
1005 					aFont.GetSize(), &GetRefMapMode(), &aPntMode ).Height();
1006 			nFontHeight *=2;	// HalfPoints
1007 			sal_uInt16 nProp = ((const SvxEscapementItem&)rItem).GetProp();
1008 			sal_uInt16 nProp100 = nProp*100;	// Fuer SWG-Token Prop in 100tel Prozent.
1009 			short nEsc = ((const SvxEscapementItem&)rItem).GetEsc();
1010 			if ( nEsc == DFLT_ESC_AUTO_SUPER )
1011 			{
1012 				nEsc = 100 - nProp;
1013 				nProp100++;	// Eine 1 hinten bedeutet 'automatisch'.
1014 			}
1015 			else if ( nEsc == DFLT_ESC_AUTO_SUB )
1016 			{
1017 				nEsc = sal::static_int_cast< short >( -( 100 - nProp ) );
1018 				nProp100++;
1019 			}
1020 			// SWG:
1021 			if ( nEsc )
1022 				rOutput << "{\\*\\updnprop" << ByteString::CreateFromInt32( nProp100 ).GetBuffer() << '}';
1023 			long nUpDown = nFontHeight * Abs( nEsc ) / 100;
1024 			ByteString aUpDown = ByteString::CreateFromInt32( nUpDown );
1025 			if ( nEsc < 0 )
1026 				rOutput << OOO_STRING_SVTOOLS_RTF_DN << aUpDown.GetBuffer();
1027 			else if ( nEsc > 0 )
1028 				rOutput << OOO_STRING_SVTOOLS_RTF_UP << aUpDown.GetBuffer();
1029 		}
1030 		break;
1031 	}
1032 }
1033 
1034 sal_uInt32 ImpEditEngine::WriteHTML( SvStream&, EditSelection )
1035 {
1036 	return 0;
1037 }
1038 
1039 
1040 EditTextObject*	ImpEditEngine::CreateTextObject()
1041 {
1042 	EditSelection aCompleteSelection;
1043 	aCompleteSelection.Min() = aEditDoc.GetStartPaM();
1044 	aCompleteSelection.Max() = aEditDoc.GetEndPaM();
1045 
1046 	return CreateTextObject( aCompleteSelection );
1047 }
1048 
1049 EditTextObject*	ImpEditEngine::CreateTextObject( EditSelection aSel )
1050 {
1051 	return CreateBinTextObject( aSel, GetEditTextObjectPool(), aStatus.AllowBigObjects(), nBigTextObjectStart );
1052 }
1053 
1054 EditTextObject*	ImpEditEngine::CreateBinTextObject( EditSelection aSel, SfxItemPool* pPool, sal_Bool bAllowBigObjects, sal_uInt16 nBigObjectStart ) const
1055 {
1056 	BinTextObject* pTxtObj = new BinTextObject( pPool );
1057 	pTxtObj->SetVertical( IsVertical() );
1058 	MapUnit eMapUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
1059 	pTxtObj->SetMetric( (sal_uInt16) eMapUnit );
1060 	if ( pTxtObj->IsOwnerOfPool() )
1061 		pTxtObj->GetPool()->SetDefaultMetric( (SfxMapUnit) eMapUnit );
1062 
1063 	sal_uInt16 nStartNode, nEndNode;
1064 	sal_uInt32 nTextPortions = 0;
1065 
1066 	aSel.Adjust( aEditDoc );
1067 	nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
1068 	nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
1069 
1070 	sal_Bool bOnlyFullParagraphs = ( aSel.Min().GetIndex() ||
1071 		( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() ) ) ?
1072 			sal_False : sal_True;
1073 
1074 	// Vorlagen werden nicht gespeichert!
1075 	// ( Nur Name und Familie, Vorlage selbst muss in App stehen! )
1076 
1077 	pTxtObj->SetScriptType( GetScriptType( aSel ) );
1078 
1079 	// ueber die Absaetze iterieren...
1080 	sal_uInt16 nNode;
1081 	for ( nNode = nStartNode; nNode <= nEndNode; nNode++  )
1082 	{
1083 		ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
1084 		DBG_ASSERT( pNode, "Node nicht gefunden: Search&Replace" );
1085 
1086 		if ( bOnlyFullParagraphs )
1087 		{
1088 			ParaPortion* pParaPortion = GetParaPortions()[nNode];
1089 			nTextPortions += pParaPortion->GetTextPortions().Count();
1090 		}
1091 
1092 		sal_uInt16 nStartPos = 0;
1093 		sal_uInt16 nEndPos = pNode->Len();
1094 
1095 		sal_Bool bEmptyPara = nEndPos ? sal_False : sal_True;
1096 
1097 		if ( ( nNode == nStartNode ) && !bOnlyFullParagraphs )
1098 			nStartPos = aSel.Min().GetIndex();
1099 		if ( ( nNode == nEndNode ) && !bOnlyFullParagraphs )
1100 			nEndPos = aSel.Max().GetIndex();
1101 
1102 
1103 		ContentInfo* pC = pTxtObj->CreateAndInsertContent();
1104 
1105 		// Die Absatzattribute...
1106 		pC->GetParaAttribs().Set( pNode->GetContentAttribs().GetItems() );
1107 
1108 		// Das StyleSheet...
1109 		if ( pNode->GetStyleSheet() )
1110 		{
1111 			pC->GetStyle() = pNode->GetStyleSheet()->GetName();
1112 			pC->GetFamily() = pNode->GetStyleSheet()->GetFamily();
1113 		}
1114 
1115 		// Der Text...
1116 		pC->GetText() = pNode->Copy( nStartPos, nEndPos-nStartPos );
1117 
1118 		// und die Attribute...
1119 		sal_uInt16 nAttr = 0;
1120 		EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
1121 		while ( pAttr )
1122 		{
1123 			// In einem leeren Absatz die Attribute behalten!
1124 			if ( bEmptyPara ||
1125 				 ( ( pAttr->GetEnd() > nStartPos ) && ( pAttr->GetStart() < nEndPos ) ) )
1126 			{
1127 				XEditAttribute* pX = pTxtObj->CreateAttrib( *pAttr->GetItem(), pAttr->GetStart(), pAttr->GetEnd() );
1128 				// Evtl. korrigieren...
1129 				if ( ( nNode == nStartNode ) && ( nStartPos != 0 ) )
1130 				{
1131 					pX->GetStart() = ( pX->GetStart() > nStartPos ) ? pX->GetStart()-nStartPos : 0;
1132 					pX->GetEnd() = pX->GetEnd() - nStartPos;
1133 
1134 				}
1135 				if ( nNode == nEndNode )
1136 				{
1137 					if ( pX->GetEnd() > (nEndPos-nStartPos) )
1138 						pX->GetEnd() = nEndPos-nStartPos;
1139 				}
1140 				DBG_ASSERT( pX->GetEnd() <= (nEndPos-nStartPos), "CreateBinTextObject: Attribut zu lang!" );
1141 				if ( !pX->GetLen() && !bEmptyPara )
1142 					pTxtObj->DestroyAttrib( pX );
1143 				else
1144 					pC->GetAttribs().Insert( pX, pC->GetAttribs().Count() );
1145 			}
1146 			nAttr++;
1147 			pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
1148 		}
1149 
1150 #ifndef SVX_LIGHT
1151 		// ggf. Online-Spelling
1152 		if ( bAllowBigObjects && bOnlyFullParagraphs && pNode->GetWrongList() )
1153 			pC->SetWrongList( pNode->GetWrongList()->Clone() );
1154 #endif // !SVX_LIGHT
1155 
1156 	}
1157 
1158 	// Bei grossen Textobjekten die PortionInfos merken:
1159 	// Schwelle rauf setzen, wenn Olli die Absaetze nicht mehr zerhackt!
1160 	if ( bAllowBigObjects && bOnlyFullParagraphs && IsFormatted() && GetUpdateMode() && ( nTextPortions >= nBigObjectStart ) )
1161 	{
1162 		XParaPortionList* pXList = new XParaPortionList( GetRefDevice(), aPaperSize.Width() );
1163 		pTxtObj->SetPortionInfo( pXList );
1164 		for ( nNode = nStartNode; nNode <= nEndNode; nNode++  )
1165 		{
1166 			ParaPortion* pParaPortion = GetParaPortions()[nNode];
1167 			XParaPortion* pX = new XParaPortion;
1168 			pXList->Insert( pX, pXList->Count() );
1169 
1170 			pX->nHeight = pParaPortion->GetHeight();
1171 			pX->nFirstLineOffset = pParaPortion->GetFirstLineOffset();
1172 
1173 			// Die TextPortions
1174 			sal_uInt16 nCount = pParaPortion->GetTextPortions().Count();
1175 			sal_uInt16 n;
1176 			for ( n = 0; n < nCount; n++ )
1177 			{
1178 				TextPortion* pTextPortion = pParaPortion->GetTextPortions()[n];
1179 				TextPortion* pNew = new TextPortion( *pTextPortion );
1180 				pX->aTextPortions.Insert( pNew, pX->aTextPortions.Count() );
1181 			}
1182 
1183 			// Die Zeilen
1184 			nCount = pParaPortion->GetLines().Count();
1185 			for ( n = 0; n < nCount; n++ )
1186 			{
1187 				EditLine* pLine = pParaPortion->GetLines()[n];
1188 				EditLine* pNew = pLine->Clone();
1189 				pX->aLines.Insert( pNew, pX->aLines.Count() );
1190 			}
1191 #ifdef DBG_UTIL
1192 			sal_uInt16 nTest;
1193             int nTPLen = 0, nTxtLen = 0;
1194 			for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
1195 				nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
1196 			for ( nTest = pParaPortion->GetLines().Count(); nTest; )
1197 				nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
1198 			DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "CreateBinTextObject: ParaPortion not completely formatted!" );
1199 #endif
1200 		}
1201 	}
1202 	return pTxtObj;
1203 }
1204 
1205 void ImpEditEngine::SetText( const EditTextObject& rTextObject )
1206 {
1207 	// Da Setzen eines TextObject ist nicht Undo-faehig!
1208 	ResetUndoManager();
1209 	sal_Bool _bUpdate = GetUpdateMode();
1210 	sal_Bool _bUndo = IsUndoEnabled();
1211 
1212 	SetText( XubString() );
1213 	EditPaM aPaM = aEditDoc.GetStartPaM();
1214 
1215 	SetUpdateMode( sal_False );
1216 	EnableUndo( sal_False );
1217 
1218 	InsertText( rTextObject, EditSelection( aPaM, aPaM ) );
1219 	SetVertical( rTextObject.IsVertical() );
1220 
1221 #ifndef SVX_LIGHT
1222 	DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Woher kommt das Undo in SetText ?!" );
1223 #endif
1224 	SetUpdateMode( _bUpdate );
1225 	EnableUndo( _bUndo );
1226 }
1227 
1228 EditSelection ImpEditEngine::InsertText( const EditTextObject& rTextObject, EditSelection aSel )
1229 {
1230     EnterBlockNotifications();
1231 	aSel.Adjust( aEditDoc );
1232 	if ( aSel.HasRange() )
1233 		aSel = ImpDeleteSelection( aSel );
1234 	EditSelection aNewSel = InsertBinTextObject( (BinTextObject&)rTextObject, aSel.Max() );
1235     LeaveBlockNotifications();
1236     return aNewSel;
1237 
1238 	// MT 05/00: InsertBinTextObject direkt hier machen...
1239 }
1240 
1241 EditSelection ImpEditEngine::InsertBinTextObject( BinTextObject& rTextObject, EditPaM aPaM )
1242 {
1243 	// Optimieren:
1244 	// Kein GetPos undFindParaportion, sondern Index berechnen!
1245 	EditSelection aSel( aPaM, aPaM );
1246 	DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );
1247 
1248 	sal_Bool bUsePortionInfo = sal_False;
1249 //	sal_Bool bFields = sal_False;
1250 	XParaPortionList* pPortionInfo = rTextObject.GetPortionInfo();
1251 
1252 	if ( pPortionInfo && ( (long)pPortionInfo->GetPaperWidth() == aPaperSize.Width() )
1253 			&& ( pPortionInfo->GetRefMapMode() == GetRefDevice()->GetMapMode() ) )
1254 	{
1255 		if ( ( pPortionInfo->GetRefDevPtr() == (sal_uIntPtr)GetRefDevice() ) ||
1256 			 ( ( pPortionInfo->GetRefDevType() == OUTDEV_VIRDEV ) &&
1257 			   ( GetRefDevice()->GetOutDevType() == OUTDEV_VIRDEV ) ) )
1258 		bUsePortionInfo = sal_True;
1259 	}
1260 
1261 	sal_Bool bConvertItems = sal_False;
1262 	MapUnit eSourceUnit = MapUnit(), eDestUnit = MapUnit();
1263 	if ( rTextObject.HasMetric() )
1264 	{
1265 		eSourceUnit = (MapUnit)rTextObject.GetMetric();
1266 		eDestUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
1267 		if ( eSourceUnit != eDestUnit )
1268 			bConvertItems = sal_True;
1269 	}
1270 
1271 	sal_uInt16 nContents = rTextObject.GetContents().Count();
1272 	sal_uInt16 nPara = aEditDoc.GetPos( aPaM.GetNode() );
1273 
1274 	for ( sal_uInt16 n = 0; n < nContents; n++, nPara++ )
1275 	{
1276 		ContentInfo* pC = rTextObject.GetContents().GetObject( n );
1277 		sal_Bool bNewContent = aPaM.GetNode()->Len() ? sal_False: sal_True;
1278 		sal_uInt16 nStartPos = aPaM.GetIndex();
1279 
1280 		aPaM = ImpFastInsertText( aPaM, pC->GetText() );
1281 
1282 		ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
1283 		DBG_ASSERT( pPortion, "Blinde Portion in FastInsertText" );
1284 		pPortion->MarkInvalid( nStartPos, pC->GetText().Len() );
1285 
1286 		// Zeicheattribute...
1287 		sal_Bool bAllreadyHasAttribs = aPaM.GetNode()->GetCharAttribs().Count() ? sal_True : sal_False;
1288 		sal_uInt16 nNewAttribs = pC->GetAttribs().Count();
1289 		if ( nNewAttribs )
1290 		{
1291             sal_Bool bUpdateFields = sal_False;
1292 			for ( sal_uInt16 nAttr = 0; nAttr < nNewAttribs; nAttr++ )
1293 			{
1294 				XEditAttribute* pX = pC->GetAttribs().GetObject( nAttr );
1295 				// Kann passieren wenn Absaetze >16K entstehen, dann wird einfach umgebrochen.
1296 				if ( pX->GetEnd() <= aPaM.GetNode()->Len() )
1297 				{
1298 					if ( !bAllreadyHasAttribs || pX->IsFeature() )
1299 					{
1300 						// Normale Attribute gehen dann schneller...
1301 						// Features duerfen nicht ueber EditDoc::InsertAttrib
1302 						// eingefuegt werden, sie sind bei FastInsertText schon im TextFluss
1303 						DBG_ASSERT( pX->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut zu gross!" );
1304 						EditCharAttrib* pAttr;
1305 						if ( !bConvertItems )
1306 							pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *(pX->GetItem()), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
1307 						else
1308 						{
1309 							SfxPoolItem* pNew = pX->GetItem()->Clone();
1310 							ConvertItem( *pNew, eSourceUnit, eDestUnit );
1311 							pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *pNew, pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
1312 							delete pNew;
1313 						}
1314 						DBG_ASSERT( pAttr->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (1)" );
1315 						aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttr );
1316 						if ( pAttr->Which() == EE_FEATURE_FIELD )
1317                             bUpdateFields = sal_True;
1318 					}
1319 					else
1320 					{
1321 						DBG_ASSERT( pX->GetEnd()+nStartPos <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (2)" );
1322 						// Tabs und andere Features koennen nicht ueber InsertAttrib eingefuegt werden:
1323 						aEditDoc.InsertAttrib( aPaM.GetNode(), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos, *pX->GetItem() );
1324 					}
1325 				}
1326 			}
1327             if ( bUpdateFields )
1328                 UpdateFields();
1329 
1330             // Sonst QuickFormat => Keine Attribute!
1331 			pPortion->MarkSelectionInvalid( nStartPos, pC->GetText().Len() );
1332 		}
1333 
1334 		DBG_ASSERT( CheckOrderedList( aPaM.GetNode()->GetCharAttribs().GetAttribs(), sal_True ), "InsertBinTextObject: Start-Liste verdreht" );
1335 
1336 		sal_Bool bParaAttribs = sal_False;
1337 		if ( bNewContent || ( ( n > 0 ) && ( n < (nContents-1) ) ) )
1338 		{
1339             bParaAttribs = sal_False;
1340             // #101512# Don't overwrite level/style from existing paragraph in OutlineView
1341             // MT 10/2002: Removed because of #103874#, handled in Outliner::EndPasteOrDropHdl now.
1342 //            if ( !aStatus.IsOutliner() || n )
1343             {
1344 			    // nur dann Style und ParaAttribs, wenn neuer Absatz, oder
1345 			    // komplett inneliegender...
1346 			    bParaAttribs = pC->GetParaAttribs().Count() ? sal_True : sal_False;
1347 			    if ( GetStyleSheetPool() && pC->GetStyle().Len() )
1348 			    {
1349 				    SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() );
1350 				    DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
1351 				    SetStyleSheet( nPara, pStyle );
1352 			    }
1353 			    if ( !bConvertItems )
1354 				    SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), pC->GetParaAttribs() );
1355 			    else
1356 			    {
1357 				    SfxItemSet aAttribs( GetEmptyItemSet() );
1358 				    ConvertAndPutItems( aAttribs, pC->GetParaAttribs(), &eSourceUnit, &eDestUnit );
1359 				    SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), aAttribs );
1360 			    }
1361             }
1362 			if ( bNewContent && bUsePortionInfo )
1363 			{
1364 				XParaPortion* pXP = pPortionInfo->GetObject( n );
1365 				DBG_ASSERT( pXP, "InsertBinTextObject: PortionInfo?" );
1366 				ParaPortion* pParaPortion = GetParaPortions()[ nPara ];
1367 				DBG_ASSERT( pParaPortion, "InsertBinTextObject: ParaPortion?" );
1368 				pParaPortion->nHeight = pXP->nHeight;
1369 				pParaPortion->nFirstLineOffset = pXP->nFirstLineOffset;
1370 				pParaPortion->bForceRepaint = sal_True;
1371 				pParaPortion->SetValid();	// Nicht formatieren
1372 
1373 				// Die TextPortions
1374 				pParaPortion->GetTextPortions().Reset();
1375 				sal_uInt16 nCount = pXP->aTextPortions.Count();
1376 				for ( sal_uInt16 _n = 0; _n < nCount; _n++ )
1377 				{
1378 					TextPortion* pTextPortion = pXP->aTextPortions[_n];
1379 					TextPortion* pNew = new TextPortion( *pTextPortion );
1380 					pParaPortion->GetTextPortions().Insert( pNew, _n );
1381 				}
1382 
1383 				// Die Zeilen
1384 				pParaPortion->GetLines().Reset();
1385 				nCount = pXP->aLines.Count();
1386 				for ( sal_uInt16 m = 0; m < nCount; m++ )
1387 				{
1388 					EditLine* pLine = pXP->aLines[m];
1389 					EditLine* pNew = pLine->Clone();
1390 					pNew->SetInvalid();	// neu Painten!
1391 					pParaPortion->GetLines().Insert( pNew, m );
1392 				}
1393 #ifdef DBG_UTIL
1394 				sal_uInt16 nTest;
1395                 int nTPLen = 0, nTxtLen = 0;
1396 				for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
1397 					nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
1398 				for ( nTest = pParaPortion->GetLines().Count(); nTest; )
1399 					nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
1400 				DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "InsertBinTextObject: ParaPortion not completely formatted!" );
1401 #endif
1402 			}
1403 		}
1404 		if ( !bParaAttribs ) // DefFont wird bei FastInsertParagraph nicht berechnet
1405 		{
1406 			aPaM.GetNode()->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
1407 			if ( aStatus.UseCharAttribs() )
1408 				aPaM.GetNode()->CreateDefFont();
1409 		}
1410 
1411 #ifndef SVX_LIGHT
1412 		if ( bNewContent && GetStatus().DoOnlineSpelling() && pC->GetWrongList() )
1413 		{
1414 			aPaM.GetNode()->DestroyWrongList();	// otherwise MLK, if list exists...
1415 			aPaM.GetNode()->SetWrongList( pC->GetWrongList()->Clone() );
1416 		}
1417 #endif // !SVX_LIGHT
1418 
1419 		// Zeilenumbruch, wenn weitere folgen...
1420 		if ( n < ( nContents-1) )
1421 		{
1422 			if ( bNewContent )
1423 				aPaM = ImpFastInsertParagraph( nPara+1 );
1424 			else
1425 				aPaM = ImpInsertParaBreak( aPaM, sal_False );
1426 		}
1427 	}
1428 
1429 	aSel.Max() = aPaM;
1430 	DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );
1431 	return aSel;
1432 }
1433 
1434 LanguageType ImpEditEngine::GetLanguage( const EditPaM& rPaM, sal_uInt16* pEndPos ) const
1435 {
1436 	short nScriptType = GetScriptType( rPaM, pEndPos );	// pEndPos will be valid now, pointing to ScriptChange or NodeLen
1437 	sal_uInt16 nLangId = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
1438 	const SvxLanguageItem* pLangItem = &(const SvxLanguageItem&)rPaM.GetNode()->GetContentAttribs().GetItem( nLangId );
1439 	EditCharAttrib*	pAttr = rPaM.GetNode()->GetCharAttribs().FindAttrib( nLangId, rPaM.GetIndex() );
1440 	if ( pAttr )
1441 		pLangItem = (const SvxLanguageItem*)pAttr->GetItem();
1442 
1443 	if ( pEndPos && pAttr && ( pAttr->GetEnd() < *pEndPos ) )
1444 		*pEndPos = pAttr->GetEnd();
1445 
1446 	return pLangItem->GetLanguage();
1447 }
1448 
1449 ::com::sun::star::lang::Locale ImpEditEngine::GetLocale( const EditPaM& rPaM ) const
1450 {
1451 	return SvxCreateLocale( GetLanguage( rPaM ) );
1452 }
1453 
1454 Reference< XSpellChecker1 > ImpEditEngine::GetSpeller()
1455 {
1456 #ifndef SVX_LIGHT
1457 	if ( !xSpeller.is() )
1458 		xSpeller = SvxGetSpellChecker();
1459 #endif
1460 	return xSpeller;
1461 }
1462 
1463 
1464 SpellInfo * ImpEditEngine::CreateSpellInfo( const EditSelection &rSel, bool bMultipleDocs )
1465 {
1466     if (!pSpellInfo)
1467 	    pSpellInfo = new SpellInfo;
1468     else
1469         *pSpellInfo = SpellInfo();  // reset to default values
1470 
1471 	pSpellInfo->bMultipleDoc = bMultipleDocs;
1472     EditSelection aSentenceSel( SelectSentence( rSel ) );
1473 //    pSpellInfo->aSpellStart = CreateEPaM( aSentenceSel.Min() );
1474 //    pSpellInfo->aSpellTo    = CreateEPaM( rSel.HasRange()? aSentenceSel.Max() : aSentenceSel.Min() );
1475     // always spell draw objects completely, startting at the top.
1476     // (spelling in only a selection or not starting with the top requires
1477     // further changes elsewehe to work properly)
1478 	pSpellInfo->aSpellStart = EPaM();
1479     pSpellInfo->aSpellTo    = EPaM( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND );
1480     return pSpellInfo;
1481 }
1482 
1483 
1484 EESpellState ImpEditEngine::Spell( EditView* pEditView, sal_Bool bMultipleDoc )
1485 {
1486 #ifdef SVX_LIGHT
1487 	return EE_SPELL_NOSPELLER;
1488 #else
1489 
1490 	DBG_ASSERTWARNING( xSpeller.is(), "Kein Speller gesetzt!" );
1491 
1492 	if ( !xSpeller.is() )
1493 		return EE_SPELL_NOSPELLER;
1494 
1495 	aOnlineSpellTimer.Stop();
1496 
1497 	// Bei MultipleDoc immer von vorne/hinten...
1498 	if ( bMultipleDoc )
1499 	{
1500         pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
1501 	}
1502 
1503 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
1504 	pSpellInfo = CreateSpellInfo( aCurSel, bMultipleDoc );
1505 
1506 	sal_Bool bIsStart = sal_False;
1507 	if ( bMultipleDoc )
1508 		bIsStart = sal_True;	// Immer von Vorne bzw. von hinten...
1509     else if ( ( CreateEPaM( aEditDoc.GetStartPaM() ) == pSpellInfo->aSpellStart ) )
1510 		bIsStart = sal_True;
1511 
1512 	EditSpellWrapper* pWrp = new EditSpellWrapper( Application::GetDefDialogParent(),
1513 			xSpeller, bIsStart, sal_False, pEditView );
1514 	pWrp->SpellDocument();
1515 	delete pWrp;
1516 
1517 	if ( !bMultipleDoc )
1518 	{
1519 		pEditView->pImpEditView->DrawSelection();
1520 		if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
1521 			aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
1522 		aCurSel.Min() = aCurSel.Max();
1523 		pEditView->pImpEditView->SetEditSelection( aCurSel );
1524 		pEditView->pImpEditView->DrawSelection();
1525 		pEditView->ShowCursor( sal_True, sal_False );
1526 	}
1527 	EESpellState eState = pSpellInfo->eState;
1528 	delete pSpellInfo;
1529 	pSpellInfo = 0;
1530 	return eState;
1531 #endif
1532 }
1533 
1534 
1535 sal_Bool ImpEditEngine::HasConvertibleTextPortion( LanguageType nSrcLang )
1536 {
1537 #ifdef SVX_LIGHT
1538     return sal_False;
1539 #else
1540     sal_Bool    bHasConvTxt = sal_False;
1541 
1542     sal_uInt16 nParas = pEditEngine->GetParagraphCount();
1543     for (sal_uInt16 k = 0;  k < nParas;  ++k)
1544     {
1545         SvUShorts aPortions;
1546         pEditEngine->GetPortions( k, aPortions );
1547         for ( sal_uInt16 nPos = 0; nPos < aPortions.Count(); ++nPos )
1548         {
1549             sal_uInt16 nEnd   = aPortions.GetObject( nPos );
1550             sal_uInt16 nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;
1551 
1552 			// if the paragraph is not empty we need to increase the index
1553 			// by one since the attribute of the character left to the
1554 			// specified position is evaluated.
1555 			if (nEnd > nStart)	// empty para?
1556 				++nStart;
1557             LanguageType nLangFound = pEditEngine->GetLanguage( k, nStart );
1558 #ifdef DEBUG
1559             lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
1560 #endif
1561             bHasConvTxt =   (nSrcLang == nLangFound) ||
1562                             (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
1563                              editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
1564             if (bHasConvTxt)
1565                 return bHasConvTxt;
1566        }
1567     }
1568 
1569 #endif
1570     return bHasConvTxt;
1571 }
1572 
1573 
1574 void ImpEditEngine::Convert( EditView* pEditView,
1575         LanguageType nSrcLang, LanguageType nDestLang, const Font *pDestFont,
1576         sal_Int32 nOptions, sal_Bool bIsInteractive, sal_Bool bMultipleDoc )
1577 {
1578     // modified version of ImpEditEngine::Spell
1579 
1580 #ifdef SVX_LIGHT
1581 #else
1582 
1583     // Bei MultipleDoc immer von vorne/hinten...
1584     if ( bMultipleDoc )
1585         pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
1586 
1587 	//
1588 	// initialize pConvInfo
1589 	//
1590     EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
1591     aCurSel.Adjust( aEditDoc );
1592     pConvInfo = new ConvInfo;
1593     pConvInfo->bMultipleDoc = bMultipleDoc;
1594     pConvInfo->aConvStart = CreateEPaM( aCurSel.Min() );
1595 	//
1596 	// if it is not just a selection and we are about to begin
1597 	// with the current conversion for the very first time
1598 	// we need to find the start of the current (initial)
1599 	// convertible unit in order for the text conversion to give
1600 	// the correct result for that. Since it is easier to obtain
1601 	// the start of the word we use that though.
1602 	if (!aCurSel.HasRange() && ImplGetBreakIterator().is())
1603 	{
1604 		EditPaM aWordStartPaM(	SelectWord( aCurSel, i18n::WordType::DICTIONARY_WORD ).Min() );
1605 
1606 		// since #118246 / #117803 still occurs if the cursor is placed
1607 		// between the two chinese characters to be converted (because both
1608 		// of them are words on their own!) using the word boundary here does
1609 		// not work. Thus since chinese conversion is not interactive we start
1610 		// at the begin of the paragraph to solve the problem, i.e. have the
1611         // TextConversion service get those characters together in the same call.
1612 		sal_uInt16 nStartIdx = ( editeng::HangulHanjaConversion::IsChinese( nSrcLang ) ) ?
1613 								0 : aWordStartPaM.GetIndex();
1614 		pConvInfo->aConvStart.nIndex = nStartIdx;
1615 	}
1616 	//
1617     pConvInfo->aConvContinue = pConvInfo->aConvStart;
1618 
1619     sal_Bool bIsStart = sal_False;
1620     if ( bMultipleDoc )
1621         bIsStart = sal_True;    // Immer von Vorne bzw. von hinten...
1622     else if ( CreateEPaM( aEditDoc.GetStartPaM() ) == pConvInfo->aConvStart )
1623         bIsStart = sal_True;
1624 
1625     bImpConvertFirstCall = sal_True;    // next ImpConvert call is the very first in this conversion turn
1626 
1627     Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
1628     TextConvWrapper aWrp( Application::GetDefDialogParent(), xMSF,
1629                           SvxCreateLocale( nSrcLang ), SvxCreateLocale( nDestLang ),
1630                           pDestFont,
1631                           nOptions, bIsInteractive,
1632                           bIsStart, pEditView );
1633 
1634 	//
1635 	//!! optimization does not work since when update mode is false
1636     //!! the object is 'lying' about it portions, paragraphs,
1637 	//!! EndPaM... later on.
1638     //!! Should not be a great problem since text boxes or cells in
1639 	//!! Calc usually have only a rather short text.
1640 	//
1641 	// disallow formatting, updating the view, ... while
1642 	// non-interactively converting the document. (saves time)
1643 	//if (!bIsInteractive)
1644 	//	SetUpdateMode( sal_False );
1645 
1646 	aWrp.Convert();
1647 
1648 	//if (!bIsInteractive)
1649 	//SetUpdateMode( sal_True, 0, sal_True );
1650 
1651     if ( !bMultipleDoc )
1652     {
1653         pEditView->pImpEditView->DrawSelection();
1654         if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
1655             aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
1656         aCurSel.Min() = aCurSel.Max();
1657         pEditView->pImpEditView->SetEditSelection( aCurSel );
1658         pEditView->pImpEditView->DrawSelection();
1659         pEditView->ShowCursor( sal_True, sal_False );
1660     }
1661     delete pConvInfo;
1662     pConvInfo = 0;
1663 #endif
1664 }
1665 
1666 
1667 void ImpEditEngine::SetLanguageAndFont(
1668     const ESelection &rESel,
1669     LanguageType nLang, sal_uInt16 nLangWhichId,
1670     const Font *pFont,  sal_uInt16 nFontWhichId )
1671 {
1672     ESelection aOldSel = pActiveView->GetSelection();
1673     pActiveView->SetSelection( rESel );
1674 
1675     // set new language attribute
1676     SfxItemSet aNewSet( pActiveView->GetEmptyItemSet() );
1677     aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
1678 
1679     // new font to be set?
1680     DBG_ASSERT( pFont, "target font missing?" );
1681     if (pFont)
1682     {
1683         // set new font attribute
1684         SvxFontItem aFontItem = (SvxFontItem&) aNewSet.Get( nFontWhichId );
1685         aFontItem.SetFamilyName( pFont->GetName());
1686         aFontItem.SetFamily( pFont->GetFamily());
1687         aFontItem.SetStyleName( pFont->GetStyleName());
1688         aFontItem.SetPitch( pFont->GetPitch());
1689         aFontItem.SetCharSet( pFont->GetCharSet() );
1690         aNewSet.Put( aFontItem );
1691     }
1692 
1693     // apply new attributes
1694     pActiveView->SetAttribs( aNewSet );
1695 
1696     pActiveView->SetSelection( aOldSel );
1697 }
1698 
1699 
1700 void ImpEditEngine::ImpConvert( rtl::OUString &rConvTxt, LanguageType &rConvTxtLang,
1701         EditView* pEditView, LanguageType nSrcLang, const ESelection &rConvRange,
1702         sal_Bool bAllowImplicitChangesForNotConvertibleText,
1703         LanguageType nTargetLang, const Font *pTargetFont  )
1704 {
1705     // modified version of ImpEditEngine::ImpSpell
1706 
1707     // looks for next convertible text portion to be passed on to the wrapper
1708 
1709     String aRes;
1710     LanguageType nResLang = LANGUAGE_NONE;
1711 
1712 #ifdef SVX_LIGHT
1713     rConvTxt = rtl::OUString();
1714     rConvTxtLang = LANGUAGE_NONE;
1715 #else
1716 
1717     /* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
1718 
1719     EditPaM aPos( CreateEditPaM( pConvInfo->aConvContinue ) );
1720 	EditSelection aCurSel = EditSelection( aPos, aPos );
1721 
1722     String aWord;
1723 
1724     while (!aRes.Len())
1725     {
1726         // empty paragraph found that needs to have language and font set?
1727 		if (bAllowImplicitChangesForNotConvertibleText &&
1728             !pEditEngine->GetText( pConvInfo->aConvContinue.nPara ).Len())
1729 		{
1730 			sal_uInt16 nPara = pConvInfo->aConvContinue.nPara;
1731             ESelection aESel( nPara, 0, nPara, 0 );
1732             // see comment for below same function call
1733             SetLanguageAndFont( aESel,
1734                     nTargetLang, EE_CHAR_LANGUAGE_CJK,
1735                     pTargetFont, EE_CHAR_FONTINFO_CJK );
1736 		}
1737 
1738 
1739 		if (pConvInfo->aConvContinue.nPara  == pConvInfo->aConvTo.nPara &&
1740 			pConvInfo->aConvContinue.nIndex >= pConvInfo->aConvTo.nIndex)
1741 			break;
1742 
1743 /*
1744         // Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
1745         // Current bei jeder Ersetzung korrigiert werden, sonst passt
1746         // das Ende evtl. nicht mehr genau...
1747         if ( pConvInfo->bConvToEnd || pConvInfo->bMultipleDoc )
1748         {
1749             if ( aCurSel.Max().GetNode() == pLastNode &&
1750                  aCurSel.Max().GetIndex() >= pLastNode->Len() )
1751                 break;
1752         }
1753 */
1754 
1755 		sal_uInt16 nAttribStart	= USHRT_MAX;
1756 		sal_uInt16 nAttribEnd	= USHRT_MAX;
1757 		sal_uInt16 nCurPos		= USHRT_MAX;
1758 		EPaM aCurStart = CreateEPaM( aCurSel.Min() );
1759 		SvUShorts aPortions;
1760 		pEditEngine->GetPortions( (sal_uInt16)aCurStart.nPara, aPortions );
1761 		for ( sal_uInt16 nPos = 0; nPos < aPortions.Count(); ++nPos )
1762 		{
1763 			sal_uInt16 nEnd	  = aPortions.GetObject( nPos );
1764 			sal_uInt16 nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;
1765 
1766             // the language attribute is obtained from the left character
1767             // (like usually all other attributes)
1768             // thus we usually have to add 1 in order to get the language
1769             // of the text right to the cursor position
1770             sal_uInt16 nLangIdx = nEnd > nStart ? nStart + 1 : nStart;
1771             LanguageType nLangFound = pEditEngine->GetLanguage( aCurStart.nPara, nLangIdx );
1772 #ifdef DEBUG
1773             lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
1774 #endif
1775             sal_Bool bLangOk =  (nLangFound == nSrcLang) ||
1776                                 (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
1777                                  editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
1778 
1779             if (nAttribEnd != USHRT_MAX) // start already found?
1780 			{
1781 				DBG_ASSERT(nEnd >= aCurStart.nIndex, "error while scanning attributes (a)" );
1782 				DBG_ASSERT(nEnd >= nAttribEnd, "error while scanning attributes (b)" );
1783 				if (/*nEnd >= aCurStart.nIndex &&*/ nLangFound == nResLang)
1784 					nAttribEnd = nEnd;
1785 				else  // language attrib has changed
1786 					break;
1787 			}
1788 			if (nAttribStart == USHRT_MAX && // start not yet found?
1789 				nEnd > aCurStart.nIndex && bLangOk)
1790 			{
1791 				nAttribStart = nStart;
1792 				nAttribEnd   = nEnd;
1793 				nResLang = nLangFound;
1794 			}
1795 			//! the list of portions may have changed compared to the previous
1796 			//! call to this function (because of possibly changed language
1797 			//! attribute!)
1798 			//! But since we don't want to start in the already processed part
1799 			//! we clip the start accordingly.
1800 			if (nAttribStart < aCurStart.nIndex)
1801 			{
1802 				nAttribStart = aCurStart.nIndex;
1803 			}
1804 
1805             // check script type to the right of the start of the current portion
1806             EditPaM aPaM( CreateEditPaM( EPaM(aCurStart.nPara, nLangIdx) ) );
1807             sal_Bool bIsAsianScript = (i18n::ScriptType::ASIAN == GetScriptType( aPaM ));
1808             // not yet processed text part with for conversion
1809             // not suitable language found that needs to be changed?
1810             if (bAllowImplicitChangesForNotConvertibleText &&
1811                 !bLangOk && !bIsAsianScript && nEnd > aCurStart.nIndex)
1812 			{
1813                 ESelection aESel( aCurStart.nPara, nStart, aCurStart.nPara, nEnd );
1814 				// set language and font to target language and font of conversion
1815 				//! Now this especially includes all non convertible text e.g.
1816 				//! spaces, empty paragraphs and western text.
1817 				// This is in order for every *new* text entered at *any* position to
1818 				// have the correct language and font attributes set.
1819                 SetLanguageAndFont( aESel,
1820                         nTargetLang, EE_CHAR_LANGUAGE_CJK,
1821                         pTargetFont, EE_CHAR_FONTINFO_CJK );
1822 			}
1823 
1824 			nCurPos = nEnd;
1825 		}
1826 
1827 		if (nAttribStart != USHRT_MAX  &&  nAttribEnd != USHRT_MAX)
1828 		{
1829 			aCurSel.Min().SetIndex( nAttribStart );
1830 			aCurSel.Max().SetIndex( nAttribEnd );
1831 		}
1832 		else if (nCurPos != USHRT_MAX)
1833 		{
1834 			// set selection to end of scanned text
1835 			// (used to set the position where to continue from later on)
1836 			aCurSel.Min().SetIndex( nCurPos );
1837 			aCurSel.Max().SetIndex( nCurPos );
1838 		}
1839 
1840 		if ( !pConvInfo->bConvToEnd )
1841         {
1842             EPaM aEPaM( CreateEPaM( aCurSel.Min() ) );
1843             if ( !( aEPaM < pConvInfo->aConvTo ) )
1844                 break;
1845         }
1846 
1847 		// clip selected word to the converted area
1848 		// (main use when conversion starts/ends **within** a word)
1849 		EditPaM aPaM( CreateEditPaM( pConvInfo->aConvStart ) );
1850 		if (pConvInfo->bConvToEnd &&
1851 			aCurSel.Min().GetNode() == aPaM.GetNode() &&
1852 			aCurSel.Min().GetIndex() < aPaM.GetIndex())
1853 				aCurSel.Min().SetIndex( aPaM.GetIndex() );
1854 		aPaM = CreateEditPaM( pConvInfo->aConvContinue );
1855 		if (aCurSel.Min().GetNode() == aPaM.GetNode() &&
1856 			aCurSel.Min().GetIndex() < aPaM.GetIndex())
1857 				aCurSel.Min().SetIndex( aPaM.GetIndex() );
1858 		aPaM = CreateEditPaM( pConvInfo->aConvTo );
1859 		if ((!pConvInfo->bConvToEnd || rConvRange.HasRange())&&
1860 			aCurSel.Max().GetNode() == aPaM.GetNode() &&
1861 			aCurSel.Max().GetIndex() > aPaM.GetIndex())
1862 				aCurSel.Max().SetIndex( aPaM.GetIndex() );
1863 
1864 		aWord = GetSelected( aCurSel );
1865 
1866         if ( aWord.Len() > 0 /* && bLangOk */)
1867             aRes = aWord;
1868 
1869 		// move to next word/paragraph if necessary
1870         if ( !aRes.Len() )
1871             aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1872 
1873         pConvInfo->aConvContinue = CreateEPaM( aCurSel.Max() );
1874     }
1875 
1876     pEditView->pImpEditView->DrawSelection();
1877     pEditView->pImpEditView->SetEditSelection( aCurSel );
1878     pEditView->pImpEditView->DrawSelection();
1879     pEditView->ShowCursor( sal_True, sal_False );
1880 
1881     rConvTxt = aRes;
1882     if (rConvTxt.getLength())
1883         rConvTxtLang = nResLang;
1884 #endif
1885 }
1886 
1887 
1888 Reference< XSpellAlternatives > ImpEditEngine::ImpSpell( EditView* pEditView )
1889 {
1890 #ifdef SVX_LIGHT
1891 	return Reference< XSpellAlternatives >();
1892 #else
1893 
1894 	DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
1895 
1896     ContentNode* pLastNode = aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
1897 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
1898     aCurSel.Min() = aCurSel.Max();
1899 
1900 	String aWord;
1901 	Reference< XSpellAlternatives > xSpellAlt;
1902 	Sequence< PropertyValue > aEmptySeq;
1903 	while (!xSpellAlt.is())
1904 	{
1905 
1906 		// Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
1907 		// Current bei jeder Ersetzung korrigiert werden, sonst passt
1908 		// das Ende evtl. nicht mehr genau...
1909 		if ( pSpellInfo->bSpellToEnd || pSpellInfo->bMultipleDoc )
1910 		{
1911 			if ( aCurSel.Max().GetNode() == pLastNode )
1912 			{
1913                 if ( ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
1914 					break;
1915 			}
1916 		}
1917 		else if ( !pSpellInfo->bSpellToEnd )
1918 		{
1919 			EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
1920             if ( !( aEPaM < pSpellInfo->aSpellTo ) )
1921 				break;
1922 		}
1923 
1924 		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1925 		aWord = GetSelected( aCurSel );
1926 
1927 		// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
1928 		// Falls Abkuerzung...
1929 		if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
1930 		{
1931 			sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
1932 			if ( cNext == '.' )
1933 			{
1934 				aCurSel.Max().GetIndex()++;
1935 				aWord += cNext;
1936 			}
1937 		}
1938 
1939 		if ( aWord.Len() > 0 )
1940 		{
1941 			LanguageType eLang = GetLanguage( aCurSel.Max() );
1942 			SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
1943 			xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
1944 		}
1945 
1946 		if ( !xSpellAlt.is() )
1947 			aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1948 		else
1949 			pSpellInfo->eState = EE_SPELL_ERRORFOUND;
1950 	}
1951 
1952 	pEditView->pImpEditView->DrawSelection();
1953 	pEditView->pImpEditView->SetEditSelection( aCurSel );
1954 	pEditView->pImpEditView->DrawSelection();
1955 	pEditView->ShowCursor( sal_True, sal_False );
1956 	return xSpellAlt;
1957 #endif
1958 }
1959 /*-- 13.10.2003 16:43:27---------------------------------------------------
1960 
1961   -----------------------------------------------------------------------*/
1962 void ImpEditEngine::EndSpelling()
1963 {
1964     DELETEZ(pSpellInfo);
1965 }
1966 /*-- 13.10.2003 16:43:27---------------------------------------------------
1967 
1968   -----------------------------------------------------------------------*/
1969 void ImpEditEngine::StartSpelling(EditView& rEditView, sal_Bool bMultipleDoc)
1970 {
1971     DBG_ASSERT(!pSpellInfo, "pSpellInfo already set?");
1972     rEditView.pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
1973     EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
1974     pSpellInfo = CreateSpellInfo( aCurSel, bMultipleDoc );
1975 }
1976 /*-- 13.10.2003 16:43:27---------------------------------------------------
1977     Search for the next wrong word within the given selection
1978   -----------------------------------------------------------------------*/
1979 Reference< XSpellAlternatives > ImpEditEngine::ImpFindNextError(EditSelection& rSelection)
1980 {
1981     /* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
1982     EditSelection aCurSel( rSelection.Min() );
1983 
1984     String aWord;
1985     Reference< XSpellAlternatives > xSpellAlt;
1986     Sequence< PropertyValue > aEmptySeq;
1987     while (!xSpellAlt.is())
1988     {
1989         //check if the end of the selection has been reached
1990         {
1991             EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
1992             if ( !( aEPaM < CreateEPaM( rSelection.Max()) ) )
1993                 break;
1994         }
1995 
1996         aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1997         aWord = GetSelected( aCurSel );
1998 
1999         // Wenn Punkt dahinter, muss dieser mit uebergeben werden !
2000         // Falls Abkuerzung...
2001         if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
2002         {
2003             sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
2004             if ( cNext == '.' )
2005             {
2006                 aCurSel.Max().GetIndex()++;
2007                 aWord += cNext;
2008             }
2009         }
2010 
2011         if ( aWord.Len() > 0 )
2012             xSpellAlt = xSpeller->spell( aWord, GetLanguage( aCurSel.Max() ), aEmptySeq );
2013 
2014         if ( !xSpellAlt.is() )
2015             aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2016         else
2017 		{
2018             pSpellInfo->eState = EE_SPELL_ERRORFOUND;
2019 			rSelection = aCurSel;
2020 		}
2021     }
2022     return xSpellAlt;
2023 }
2024 /*-- 13.10.2003 16:43:27---------------------------------------------------
2025 
2026   -----------------------------------------------------------------------*/
2027 bool ImpEditEngine::SpellSentence(EditView& rEditView,
2028     ::svx::SpellPortions& rToFill,
2029     bool /*bIsGrammarChecking*/ )
2030 {
2031 #ifdef SVX_LIGHT
2032 #else
2033     bool bRet = false;
2034     EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
2035     if(!pSpellInfo)
2036 		pSpellInfo = CreateSpellInfo( aCurSel, true );
2037     pSpellInfo->aCurSentenceStart = aCurSel.Min();
2038     DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
2039     pSpellInfo->aLastSpellPortions.clear();
2040     pSpellInfo->aLastSpellContentSelections.clear();
2041     rToFill.clear();
2042     //if no selection previously exists the range is extended to the end of the object
2043     if(aCurSel.Min() == aCurSel.Max())
2044     {
2045         ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1);
2046         aCurSel.Max() = EditPaM(pLastNode, pLastNode->Len());
2047     }
2048     // check for next error in aCurSel and set aCurSel to that one if any was found
2049     Reference< XSpellAlternatives > xAlt = ImpFindNextError(aCurSel);
2050     if (xAlt.is())
2051     {
2052         bRet = true;
2053 		//find the sentence boundaries
2054         EditSelection aSentencePaM = SelectSentence(aCurSel);
2055         //make sure that the sentence is never smaller than the error range!
2056         if(aSentencePaM.Max().GetIndex() < aCurSel.Max().GetIndex())
2057             aSentencePaM.Max() = aCurSel.Max();
2058         //add the portion preceeding the error
2059         EditSelection aStartSelection(aSentencePaM.Min(), aCurSel.Min());
2060         if(aStartSelection.HasRange())
2061 			AddPortionIterated(rEditView, aStartSelection, 0, rToFill);
2062         //add the error portion
2063         AddPortionIterated(rEditView, aCurSel, xAlt, rToFill);
2064         //find the end of the sentence
2065         //search for all errors in the rest of the sentence and add all the portions
2066         do
2067         {
2068             EditSelection aNextSel = EditSelection(aCurSel.Max(), aSentencePaM.Max());
2069             xAlt = ImpFindNextError(aNextSel);
2070             if(xAlt.is())
2071             {
2072                 //add the part between the previous and the current error
2073 				AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aNextSel.Min()), 0, rToFill);
2074                 //add the current error
2075 				AddPortionIterated(rEditView, aNextSel, xAlt, rToFill);
2076             }
2077             else
2078                 AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aSentencePaM.Max()), xAlt, rToFill);
2079 			aCurSel = aNextSel;
2080         }
2081         while( xAlt.is() );
2082 
2083 		//set the selection to the end of the current sentence
2084 		rEditView.pImpEditView->SetEditSelection(aSentencePaM.Max());
2085     }
2086 #endif
2087     return bRet;
2088 }
2089 
2090 /*-- 15.10.2003 16:09:12---------------------------------------------------
2091     adds one portion to the SpellPortions
2092   -----------------------------------------------------------------------*/
2093 void ImpEditEngine::AddPortion(
2094                             const EditSelection rSel,
2095                             uno::Reference< XSpellAlternatives > xAlt,
2096                                 ::svx::SpellPortions& rToFill,
2097                                 bool bIsField)
2098 {
2099 #ifdef SVX_LIGHT
2100 #else
2101     if(rSel.HasRange())
2102 	{
2103 		svx::SpellPortion aPortion;
2104 		aPortion.sText = GetSelected( rSel );
2105 		aPortion.eLanguage = GetLanguage( rSel.Min() );
2106 		aPortion.xAlternatives = xAlt;
2107         aPortion.bIsField = bIsField;
2108         rToFill.push_back(aPortion);
2109 
2110 		//save the spelled portions for later use
2111 		pSpellInfo->aLastSpellPortions.push_back(aPortion);
2112 		pSpellInfo->aLastSpellContentSelections.push_back(rSel);
2113 
2114 	}
2115 #endif
2116 }
2117 
2118 /*-- 15.10.2003 16:07:47---------------------------------------------------
2119     adds one or more portions of text to the SpellPortions depending on language changes
2120   -----------------------------------------------------------------------*/
2121 void ImpEditEngine::AddPortionIterated(
2122                             EditView& rEditView,
2123                             const EditSelection rSel,
2124                             Reference< XSpellAlternatives > xAlt,
2125                                 ::svx::SpellPortions& rToFill)
2126 {
2127 #ifdef SVX_LIGHT
2128 #else
2129     if(rSel.Min() != rSel.Max())
2130     {
2131         if(xAlt.is())
2132         {
2133             AddPortion(rSel, xAlt, rToFill, false);
2134         }
2135         else
2136         {
2137             //iterate and search for language attribute changes
2138             //save the start and end positions
2139             bool bTest = rSel.Min().GetIndex() <= rSel.Max().GetIndex();
2140             EditPaM aStart(bTest ? rSel.Min() : rSel.Max());
2141             EditPaM aEnd(bTest ? rSel.Max() : rSel.Min());
2142             //iterate over the text to find changes in language
2143             //set the mark equal to the point
2144             EditPaM aCursor(aStart);
2145             rEditView.pImpEditView->SetEditSelection( aCursor );
2146             LanguageType eStartLanguage = GetLanguage( aCursor );
2147             //search for a field attribute at the beginning - only the end position
2148             //of this field is kept to end a portion at that position
2149             const EditCharAttrib* pFieldAttr = aCursor.GetNode()->GetCharAttribs().
2150                                                     FindFeature( aCursor.GetIndex() );
2151             bool bIsField = pFieldAttr &&
2152                     pFieldAttr->GetStart() == aCursor.GetIndex() &&
2153                     pFieldAttr->GetStart() != pFieldAttr->GetEnd() &&
2154                     pFieldAttr->Which() == EE_FEATURE_FIELD;
2155             sal_uInt16 nEndField = bIsField ? pFieldAttr->GetEnd() : USHRT_MAX;
2156 			bool bIsEndField = false;
2157             do
2158             {
2159                 aCursor = CursorRight( aCursor);
2160                 //determine whether a field and has been reached
2161 				bIsEndField = nEndField == aCursor.GetIndex();
2162                 //search for a new field attribute
2163                 EditCharAttrib* _pFieldAttr = aCursor.GetNode()->GetCharAttribs().
2164                                                         FindFeature( aCursor.GetIndex() );
2165                 bIsField = _pFieldAttr &&
2166                         _pFieldAttr->GetStart() == aCursor.GetIndex() &&
2167                         _pFieldAttr->GetStart() != _pFieldAttr->GetEnd() &&
2168                         _pFieldAttr->Which() == EE_FEATURE_FIELD;
2169                 //on every new field move the end position
2170                 if(bIsField)
2171                     nEndField = bIsField ? _pFieldAttr->GetEnd() : USHRT_MAX;
2172 
2173                 LanguageType eCurLanguage = GetLanguage( aCursor );
2174                 if(eCurLanguage != eStartLanguage || bIsField || bIsEndField)
2175                 {
2176                     eStartLanguage = eCurLanguage;
2177                     //go one step back - the cursor currently selects the first character
2178                     //with a different language
2179                     //create a selection from start to the current Cursor
2180                     EditSelection aSelection(aStart, aCursor);
2181                     AddPortion(aSelection, xAlt, rToFill, bIsEndField);
2182                     aStart = aCursor;
2183                 }
2184             }
2185             while(aCursor.GetIndex() < aEnd.GetIndex());
2186             EditSelection aSelection(aStart, aCursor);
2187             AddPortion(aSelection, xAlt, rToFill, bIsField);
2188         }
2189     }
2190 #endif
2191 }
2192 
2193 /*-- 13.10.2003 16:43:33---------------------------------------------------
2194 
2195   -----------------------------------------------------------------------*/
2196 void ImpEditEngine::ApplyChangedSentence(EditView& rEditView,
2197     const ::svx::SpellPortions& rNewPortions,
2198     bool bRecheck )
2199 {
2200 #ifdef SVX_LIGHT
2201 #else
2202     // Note: rNewPortions.size() == 0 is valid and happens when the whole
2203     // sentence got removed in the dialog
2204 
2205     DBG_ASSERT(pSpellInfo, "pSpellInfo not initialized");
2206     if (pSpellInfo &&
2207         pSpellInfo->aLastSpellPortions.size() > 0)  // no portions -> no text to be changed
2208     {
2209         // get current paragraph length to calculate later on how the sentence length changed,
2210         // in order to place the cursor at the end of the sentence again
2211         EditSelection aOldSel( rEditView.pImpEditView->GetEditSelection() );
2212         xub_StrLen nOldLen = aOldSel.Max().GetNode()->Len();
2213 
2214         UndoActionStart( EDITUNDO_INSERT );
2215         if(pSpellInfo->aLastSpellPortions.size() == rNewPortions.size())
2216         {
2217             DBG_ASSERT( rNewPortions.size() > 0, "rNewPortions should not be empty here" );
2218             DBG_ASSERT( pSpellInfo->aLastSpellPortions.size() == pSpellInfo->aLastSpellContentSelections.size(),
2219                     "aLastSpellPortions and aLastSpellContentSelections size mismatch" );
2220 
2221             //the simple case: the same number of elements on both sides
2222             //each changed element has to be applied to the corresponding source element
2223             svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
2224             svx::SpellPortions::const_iterator aCurrentOldPortion = pSpellInfo->aLastSpellPortions.end();
2225             SpellContentSelections::const_iterator aCurrentOldPosition = pSpellInfo->aLastSpellContentSelections.end();
2226             bool bSetToEnd = false;
2227 			do
2228             {
2229                 --aCurrentNewPortion;
2230                 --aCurrentOldPortion;
2231                 --aCurrentOldPosition;
2232 				//set the cursor to the end of the sentence - necessary to
2233 				//resume there at the next step
2234 				if(!bSetToEnd)
2235 				{
2236 					bSetToEnd = true;
2237 					rEditView.pImpEditView->SetEditSelection( aCurrentOldPosition->Max() );
2238 				}
2239 
2240                 sal_uInt16 nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
2241 //                LanguageType eTextLanguage = GetLanguage( aCurrentOldPosition->Min() );
2242 
2243                 sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
2244                 switch(nScriptType)
2245                 {
2246                     case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
2247                     case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
2248                 }
2249                 if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
2250                 {
2251                     //change text and apply language
2252                     SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
2253                     aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
2254                     SetAttribs( *aCurrentOldPosition, aSet );
2255                     ImpInsertText( *aCurrentOldPosition, aCurrentNewPortion->sText );
2256                 }
2257                 else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
2258                 {
2259                     //apply language
2260                     SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
2261                     aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
2262                     SetAttribs( *aCurrentOldPosition, aSet );
2263                 }
2264                 if(aCurrentNewPortion == rNewPortions.begin())
2265                     break;
2266             }
2267             while(aCurrentNewPortion != rNewPortions.begin());
2268         }
2269         else
2270         {
2271             DBG_ASSERT( pSpellInfo->aLastSpellContentSelections.size() > 0, "aLastSpellContentSelections should not be empty here" );
2272 
2273             //select the complete sentence
2274             SpellContentSelections::const_iterator aCurrentEndPosition = pSpellInfo->aLastSpellContentSelections.end();
2275             --aCurrentEndPosition;
2276             SpellContentSelections::const_iterator aCurrentStartPosition = pSpellInfo->aLastSpellContentSelections.begin();
2277             EditSelection aAllSentence(aCurrentStartPosition->Min(), aCurrentEndPosition->Max());
2278 
2279             //delete the sentence completely
2280             ImpDeleteSelection( aAllSentence );
2281             svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.begin();
2282             EditPaM aCurrentPaM = aAllSentence.Min();
2283             while(aCurrentNewPortion != rNewPortions.end())
2284             {
2285                 //set the language attribute
2286                 LanguageType eCurLanguage = GetLanguage( aCurrentPaM );
2287                 if(eCurLanguage != aCurrentNewPortion->eLanguage)
2288                 {
2289                     sal_uInt16 nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
2290                     sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
2291                     switch(nScriptType)
2292                     {
2293                         case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
2294                         case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
2295                     }
2296                     SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
2297                     aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
2298                     SetAttribs( aCurrentPaM, aSet );
2299                 }
2300                 //insert the new string and set the cursor to the end of the inserted string
2301                 aCurrentPaM = ImpInsertText( aCurrentPaM , aCurrentNewPortion->sText );
2302                 ++aCurrentNewPortion;
2303             }
2304         }
2305         UndoActionEnd( EDITUNDO_INSERT );
2306 
2307         EditPaM aNext;
2308         if (bRecheck)
2309             aNext = pSpellInfo->aCurSentenceStart;
2310         else
2311         {
2312             // restore cursor position to the end of the modified sentence.
2313             // (This will define the continuation position for spell/grammar checking)
2314             // First: check if the sentence/para length changed
2315             sal_Int32 nDelta = rEditView.pImpEditView->GetEditSelection().Max().GetNode()->Len() - nOldLen;
2316             xub_StrLen nEndOfSentence = aOldSel.Max().GetIndex() + nDelta;
2317             aNext = EditPaM( aOldSel.Max().GetNode(), nEndOfSentence );
2318         }
2319         rEditView.pImpEditView->SetEditSelection( aNext );
2320 
2321         FormatAndUpdate();
2322         aEditDoc.SetModified(sal_True);
2323     }
2324 #endif
2325 }
2326 /*-- 08.09.2008 11:33:02---------------------------------------------------
2327 
2328   -----------------------------------------------------------------------*/
2329 void ImpEditEngine::PutSpellingToSentenceStart( EditView& rEditView )
2330 {
2331 #ifdef SVX_LIGHT
2332 #else
2333     if( pSpellInfo && pSpellInfo->aLastSpellContentSelections.size() )
2334     {
2335         rEditView.pImpEditView->SetEditSelection( pSpellInfo->aLastSpellContentSelections.begin()->Min() );
2336     }
2337 
2338 #endif
2339 }
2340 
2341 
2342 void ImpEditEngine::DoOnlineSpelling( ContentNode* pThisNodeOnly, sal_Bool bSpellAtCursorPos, sal_Bool bInteruptable )
2343 {
2344 #ifndef SVX_LIGHT
2345 	/*
2346 	 Er wird ueber alle Absaetze iteriert, nur Absaetze mit invalidierter
2347 	 WrongList werden geprueft...
2348 
2349 	 Es werden alle Woerter im invalidierten Bereich geprueft.
2350 	 Ist ein Wort falsch, aber noch nicht in der WrongList, oder umgekehrt,
2351 	 wird der Bereich des Wortes invalidiert
2352 	  (	kein Invalidate, sondern wenn nur Uebergaenge von richtig=>falsch,
2353 		einfaches Paint, bei Uebergaengen von falsch=>richtig mit VDev
2354 		ueberplaetten )
2355 	*/
2356 
2357  	if ( !xSpeller.is() )
2358 		return;
2359 
2360 	EditPaM aCursorPos;
2361 	if( pActiveView && !bSpellAtCursorPos )
2362 	{
2363 		DBG_CHKOBJ( pActiveView, EditView, 0 );
2364 		aCursorPos = pActiveView->pImpEditView->GetEditSelection().Max();
2365 	}
2366 	sal_Bool bRestartTimer = sal_False;
2367 
2368 	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
2369 	sal_uInt16 nNodes = GetEditDoc().Count();
2370 	sal_uInt16 nInvalids = 0;
2371 	Sequence< PropertyValue > aEmptySeq;
2372 	for ( sal_uInt16 n = 0; n < nNodes; n++ )
2373 	{
2374 		ContentNode* pNode = GetEditDoc().GetObject( n );
2375 		if ( pThisNodeOnly )
2376 			pNode = pThisNodeOnly;
2377 
2378 		if ( pNode->GetWrongList()->IsInvalid() )
2379 		{
2380 			WrongList* pWrongList = pNode->GetWrongList();
2381 			sal_uInt16 nInvStart = pWrongList->GetInvalidStart();
2382 			sal_uInt16 nInvEnd = pWrongList->GetInvalidEnd();
2383 
2384 			sal_uInt16 nWrongs = 0;	// Auch im Absatz mal die Kontrolle abgeben...
2385 //			sal_Bool bStop = sal_False;
2386 
2387 			sal_uInt16 nPaintFrom = 0xFFFF, nPaintTo = 0;
2388 			sal_Bool bSimpleRepaint = sal_True;
2389 
2390 			pWrongList->SetValid();
2391 
2392 			EditPaM aPaM( pNode, nInvStart );
2393 			EditSelection aSel( aPaM, aPaM );
2394 			while ( ( aSel.Max().GetNode() == pNode ) /* && !bStop */ )
2395 			{
2396 				if ( ( aSel.Min().GetIndex() > nInvEnd )
2397 						|| ( ( aSel.Max().GetNode() == pLastNode ) && ( aSel.Max().GetIndex() >= pLastNode->Len() ) ) )
2398 					break;	// Dokument- oder Ungueltigkeitsbereich-Ende
2399 
2400 				aSel = SelectWord( aSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2401 				String aWord( GetSelected( aSel ) );
2402 				// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
2403 				// Falls Abkuerzung...
2404 				sal_Bool bDottAdded = sal_False;
2405 				if ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() )
2406 				{
2407 					sal_Unicode cNext = aSel.Max().GetNode()->GetChar( aSel.Max().GetIndex() );
2408 					if ( cNext == '.' )
2409 					{
2410 						aSel.Max().GetIndex()++;
2411 						aWord += cNext;
2412 						bDottAdded = sal_True;
2413 					}
2414 				}
2415 
2416 
2417 				sal_Bool bChanged = sal_False;
2418 				if ( aWord.Len() > 0 )
2419 				{
2420 					sal_uInt16 nWStart = aSel.Min().GetIndex();
2421 					sal_uInt16 nWEnd= aSel.Max().GetIndex();
2422 					if ( !xSpeller->isValid( aWord, GetLanguage( EditPaM( aSel.Min().GetNode(), nWStart+1 ) ), aEmptySeq ) )
2423 					{
2424 						// Pruefen, ob schon richtig markiert...
2425 						nWrongs++;
2426 						// Nur bei SimpleRepaint stoppen, sonst zu oft VDev
2427 	//						if ( ( nWrongs > 8 ) && bSimpleRepaint )
2428 	//						{
2429 	//							bStop = sal_True;
2430 	// 							pWrongList->MarkInvalid( aSel.Max().GetIndex(), nInvEnd );
2431 	//						}
2432 						sal_uInt16 nXEnd = bDottAdded ? nWEnd -1 : nWEnd;
2433 						if ( !pWrongList->HasWrong( nWStart, nXEnd ) )
2434 						{
2435 							// Wort als falsch markieren...
2436 							// Aber nur, wenn nicht an Cursor-Position...
2437 							sal_Bool bCursorPos = sal_False;
2438 							if ( aCursorPos.GetNode() == pNode )
2439 							{
2440 								if ( ( nWStart <= aCursorPos.GetIndex() ) && nWEnd >= aCursorPos.GetIndex() )
2441 									bCursorPos = sal_True;
2442 							}
2443 							if ( bCursorPos )
2444 							{
2445 								// Dann weiter als ungueltig markieren...
2446 								pWrongList->GetInvalidStart() = nWStart;
2447 								pWrongList->GetInvalidEnd() = nWEnd;
2448 								bRestartTimer = sal_True;
2449 							}
2450 							else
2451 							{
2452 								// Es kann sein, dass die Wrongs in der Liste nicht
2453 								// genau ueber Woerter aufgespannt sind, weil die
2454 								// WordDelimiters beim Expandieren nicht ausgewrtet werden.
2455 								pWrongList->InsertWrong( nWStart, nXEnd, sal_True );
2456 								bChanged = sal_True;
2457 							}
2458 						}
2459 					}
2460 					else
2461 					{
2462 						// Pruefen, ob nicht als als falsch markiert....
2463 						if ( pWrongList->HasAnyWrong( nWStart, nWEnd ) )
2464 						{
2465 							pWrongList->ClearWrongs( nWStart, nWEnd, pNode );
2466 							bSimpleRepaint = sal_False;
2467 							bChanged = sal_True;
2468 						}
2469 					}
2470 					if ( bChanged  )
2471 					{
2472 						if ( nPaintFrom == 0xFFFF )
2473 							nPaintFrom = nWStart;
2474 						nPaintTo = nWEnd;
2475 					}
2476 				}
2477 
2478 				EditPaM aLastEnd( aSel.Max() );
2479 				aSel = WordRight( aSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2480 				if ( bChanged && ( aSel.Min().GetNode() == pNode ) &&
2481 						( ( aSel.Min().GetIndex()-aLastEnd.GetIndex() > 1 ) ) )
2482 				{
2483 					// Wenn zwei Worte durch mehr Zeichen als ein Blank getrennt
2484 					// sind, kann es passieren, dass beim Aufsplitten eines Wrongs
2485 					// der Start den zweiten Wortes vor dem tatsaechlich Wort liegt
2486 					pWrongList->ClearWrongs( aLastEnd.GetIndex(), aSel.Min().GetIndex(), pNode );
2487 				}
2488 			}
2489 
2490 			// Invalidieren?
2491             if ( ( nPaintFrom != 0xFFFF ) )
2492 			{
2493 				aStatus.GetStatusWord() |= EE_STAT_WRONGWORDCHANGED;
2494 				CallStatusHdl();
2495 
2496 				if ( aEditViews.Count() )
2497 				{
2498 					// Bei SimpleRepaint wuerde ein uebermalen ohne VDev reichen,
2499 					// aber dann muesste ich ueber alle Views, Intersecten,
2500 					// Clippen, ...
2501 					// Lohnt wahrscheinlich nicht.
2502 					EditPaM aStartPaM( pNode, nPaintFrom );
2503 					EditPaM aEndPaM( pNode, nPaintTo );
2504 					Rectangle aStartCursor( PaMtoEditCursor( aStartPaM ) );
2505 					Rectangle aEndCursor( PaMtoEditCursor( aEndPaM ) );
2506 					DBG_ASSERT( aInvalidRec.IsEmpty(), "InvalidRect gesetzt!" );
2507 					aInvalidRec.Left() = 0;
2508 					aInvalidRec.Right() = GetPaperSize().Width();
2509 					aInvalidRec.Top() = aStartCursor.Top();
2510 					aInvalidRec.Bottom() = aEndCursor.Bottom();
2511 					if ( pActiveView && pActiveView->HasSelection() )
2512 					{
2513 						// Dann darf nicht ueber VDev ausgegeben werden
2514 						UpdateViews( NULL );
2515 					}
2516 					else if ( bSimpleRepaint )
2517 					{
2518 						for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
2519 						{
2520 							EditView* pView = aEditViews[nView];
2521 							Rectangle aClipRec( aInvalidRec );
2522 							aClipRec.Intersection( pView->GetVisArea() );
2523 							if ( !aClipRec.IsEmpty() )
2524 							{
2525 								// in Fensterkoordinaten umwandeln....
2526 								aClipRec.SetPos( pView->pImpEditView->GetWindowPos( aClipRec.TopLeft() ) );
2527 								// Wenn Selektion, dann VDev...
2528 								Paint( pView->pImpEditView, aClipRec, pView->HasSelection() );
2529 							}
2530 						}
2531 					}
2532 					else
2533 					{
2534 						UpdateViews( pActiveView );
2535 					}
2536 					aInvalidRec = Rectangle();
2537 				}
2538 			}
2539 			// Nach zwei korrigierten Nodes die Kontrolle abgeben...
2540 			nInvalids++;
2541 			if ( bInteruptable && ( nInvalids >= 2 ) )
2542 			{
2543 				bRestartTimer = sal_True;
2544 				break;
2545 			}
2546 		}
2547 
2548 		if ( pThisNodeOnly )
2549 			break;
2550 	}
2551 	if ( bRestartTimer )
2552 		aOnlineSpellTimer.Start();
2553 #endif // !SVX_LIGHT
2554 }
2555 
2556 
2557 EESpellState ImpEditEngine::HasSpellErrors()
2558 {
2559 	DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
2560 
2561 #ifndef SVX_LIGHT
2562 	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
2563 	EditSelection aCurSel( aEditDoc.GetStartPaM() );
2564 
2565 	String aWord;
2566 	Reference< XSpellAlternatives > xSpellAlt;
2567 	Sequence< PropertyValue > aEmptySeq;
2568 	while ( !xSpellAlt.is() )
2569 	{
2570 		if ( ( aCurSel.Max().GetNode() == pLastNode ) &&
2571 			 ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
2572 		{
2573 			return EE_SPELL_OK;
2574 		}
2575 
2576 		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2577 		aWord = GetSelected( aCurSel );
2578 		if ( aWord.Len() > 0 )
2579 		{
2580 			LanguageType eLang = GetLanguage( aCurSel.Max() );
2581 			SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
2582 			xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
2583 		}
2584 		aCurSel = WordRight( aCurSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2585 	}
2586 #endif
2587 
2588 	return EE_SPELL_ERRORFOUND;
2589 }
2590 
2591 EESpellState ImpEditEngine::StartThesaurus( EditView* pEditView )
2592 {
2593 #ifndef SVX_LIGHT
2594 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
2595 	if ( !aCurSel.HasRange() )
2596 		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2597 	String aWord( GetSelected( aCurSel ) );
2598 
2599 	Reference< XThesaurus > xThes( SvxGetThesaurus() );
2600 	if (!xThes.is())
2601 		return EE_SPELL_ERRORFOUND;
2602 
2603 	EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
2604 	AbstractThesaurusDialog* pDlg = pFact->CreateThesaurusDialog( pEditView->GetWindow(), xThes, aWord, GetLanguage( aCurSel.Max() ) );
2605 	if ( pDlg->Execute() == RET_OK )
2606 	{
2607 		// Wort ersetzen...
2608 		pEditView->pImpEditView->DrawSelection();
2609 		pEditView->pImpEditView->SetEditSelection( aCurSel );
2610 		pEditView->pImpEditView->DrawSelection();
2611 		pEditView->InsertText( pDlg->GetWord() );
2612 		pEditView->ShowCursor( sal_True, sal_False );
2613 	}
2614 
2615 	delete pDlg;
2616 	return EE_SPELL_OK;
2617 #else
2618 	return EE_SPELL_NOSPELLER;
2619 #endif
2620 }
2621 
2622 sal_uInt16 ImpEditEngine::StartSearchAndReplace( EditView* pEditView, const SvxSearchItem& rSearchItem )
2623 {
2624 	sal_uInt16 nFound = 0;
2625 
2626 #ifndef SVX_LIGHT
2627 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
2628 
2629 	// FIND_ALL ohne Mehrfachselektion nicht moeglich.
2630 	if ( ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND ) ||
2631 		 ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND_ALL ) )
2632 	{
2633 		if ( Search( rSearchItem, pEditView ) )
2634 			nFound++;
2635 	}
2636 	else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE )
2637 	{
2638 		// Das Wort ist selektiert, wenn der Anwender die Selektion
2639 		// nicht zwischendurch manipuliert:
2640 		if ( aCurSel.HasRange() )
2641 		{
2642 			pEditView->InsertText( rSearchItem.GetReplaceString() );
2643 			nFound = 1;
2644 		}
2645 		else
2646 			if( Search( rSearchItem, pEditView ) )
2647 				nFound = 1;
2648 	}
2649 	else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL )
2650 	{
2651 		// Der Writer ersetzt alle, vorn Anfang bis Ende...
2652 		SvxSearchItem aTmpItem( rSearchItem );
2653 		aTmpItem.SetBackward( sal_False );
2654 
2655 		pEditView->pImpEditView->DrawSelection();
2656 
2657         aCurSel.Adjust( aEditDoc );
2658 		EditPaM aStartPaM = aTmpItem.GetSelection() ? aCurSel.Min() : aEditDoc.GetStartPaM();
2659 		EditSelection aFoundSel( aCurSel.Max() );
2660 		sal_Bool bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
2661 		if ( bFound )
2662 			UndoActionStart( EDITUNDO_REPLACEALL );
2663 		while ( bFound )
2664 		{
2665 			nFound++;
2666 			aStartPaM = ImpInsertText( aFoundSel, rSearchItem.GetReplaceString() );
2667 			bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
2668 		}
2669 		if ( nFound )
2670 		{
2671 			EditPaM aNewPaM( aFoundSel.Max() );
2672 			if ( aNewPaM.GetIndex() > aNewPaM.GetNode()->Len() )
2673 				aNewPaM.GetIndex() =  aNewPaM.GetNode()->Len();
2674 			pEditView->pImpEditView->SetEditSelection( aNewPaM );
2675 			FormatAndUpdate( pEditView );
2676 			UndoActionEnd( EDITUNDO_REPLACEALL );
2677 		}
2678 		else
2679 		{
2680 			pEditView->pImpEditView->DrawSelection();
2681 			pEditView->ShowCursor( sal_True, sal_False );
2682 		}
2683 	}
2684 #endif // !SVX_LIGHT
2685 	return nFound;
2686 }
2687 
2688 sal_Bool ImpEditEngine::Search( const SvxSearchItem& rSearchItem, EditView* pEditView )
2689 {
2690 	EditSelection aSel( pEditView->pImpEditView->GetEditSelection() );
2691 	aSel.Adjust( aEditDoc );
2692 	EditPaM aStartPaM( aSel.Max() );
2693 	if ( rSearchItem.GetSelection() && !rSearchItem.GetBackward() )
2694 		aStartPaM = aSel.Min();
2695 
2696 	EditSelection aFoundSel;
2697 	sal_Bool bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
2698 	if ( bFound && ( aFoundSel == aSel ) )	// Bei Rueckwaetssuche
2699 	{
2700 		aStartPaM = aSel.Min();
2701 		bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
2702 	}
2703 
2704 	pEditView->pImpEditView->DrawSelection();
2705 	if ( bFound )
2706 	{
2707 		// Erstmal das Min einstellen, damit das ganze Wort in den sichtbaren Bereich kommt.
2708 		pEditView->pImpEditView->SetEditSelection( aFoundSel.Min() );
2709 		pEditView->ShowCursor( sal_True, sal_False );
2710 		pEditView->pImpEditView->SetEditSelection( aFoundSel );
2711 	}
2712 	else
2713 		pEditView->pImpEditView->SetEditSelection( aSel.Max() );
2714 
2715 	pEditView->pImpEditView->DrawSelection();
2716 	pEditView->ShowCursor( sal_True, sal_False );
2717 	return bFound;
2718 }
2719 
2720 sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
2721 	const EditSelection& rSearchSelection, const EditPaM& rStartPos, EditSelection& rFoundSel )
2722 {
2723 #ifndef SVX_LIGHT
2724 	util::SearchOptions aSearchOptions( rSearchItem.GetSearchOptions() );
2725 	aSearchOptions.Locale = GetLocale( rStartPos );
2726 
2727 	sal_Bool bBack = rSearchItem.GetBackward();
2728 	sal_Bool bSearchInSelection = rSearchItem.GetSelection();
2729 	sal_uInt16 nStartNode = aEditDoc.GetPos( rStartPos.GetNode() );
2730 	sal_uInt16 nEndNode;
2731 	if ( bSearchInSelection )
2732 	{
2733 		nEndNode = aEditDoc.GetPos( bBack ? rSearchSelection.Min().GetNode() : rSearchSelection.Max().GetNode() );
2734 	}
2735 	else
2736 	{
2737 		nEndNode = bBack ? 0 : aEditDoc.Count()-1;
2738 	}
2739 
2740 	utl::TextSearch aSearcher( aSearchOptions );
2741 
2742 	// ueber die Absaetze iterieren...
2743 	for ( sal_uInt16 nNode = nStartNode;
2744 			bBack ? ( nNode >= nEndNode ) : ( nNode <= nEndNode) ;
2745 			bBack ? nNode-- : nNode++ )
2746 	{
2747 		// Bei rueckwaertsuche, wenn nEndNode = 0:
2748 		if ( nNode >= 0xFFFF )
2749 			return sal_False;
2750 
2751 		ContentNode* pNode = aEditDoc.GetObject( nNode );
2752 
2753 		sal_uInt16 nStartPos = 0;
2754 		sal_uInt16 nEndPos = pNode->Len();
2755 		if ( nNode == nStartNode )
2756 		{
2757 			if ( bBack )
2758 				nEndPos = rStartPos.GetIndex();
2759 			else
2760 				nStartPos = rStartPos.GetIndex();
2761 		}
2762 		if ( ( nNode == nEndNode ) && bSearchInSelection )
2763 		{
2764 			if ( bBack )
2765 				nStartPos = rSearchSelection.Min().GetIndex();
2766 			else
2767 				nEndPos = rSearchSelection.Max().GetIndex();
2768 		}
2769 
2770 		// Suchen...
2771 		XubString aParaStr( GetEditDoc().GetParaAsString( pNode ) );
2772 		bool bFound = false;
2773 		if ( bBack )
2774 		{
2775 			Swapsal_uIt16s( nStartPos, nEndPos );
2776 			bFound = aSearcher.SearchBkwrd( aParaStr, &nStartPos, &nEndPos);
2777 		}
2778 		else
2779 			bFound = aSearcher.SearchFrwrd( aParaStr, &nStartPos, &nEndPos);
2780 
2781 		if ( bFound )
2782 		{
2783 			rFoundSel.Min().SetNode( pNode );
2784 			rFoundSel.Min().SetIndex( nStartPos );
2785 			rFoundSel.Max().SetNode( pNode );
2786 			rFoundSel.Max().SetIndex( nEndPos );
2787 			return sal_True;
2788 		}
2789 	}
2790 #endif // !SVX_LIGHT
2791 	return sal_False;
2792 }
2793 
2794 sal_Bool ImpEditEngine::HasText( const SvxSearchItem& rSearchItem )
2795 {
2796 #ifndef SVX_LIGHT
2797 	SvxSearchItem aTmpItem( rSearchItem );
2798 	aTmpItem.SetBackward( sal_False );
2799 	aTmpItem.SetSelection( sal_False );
2800 
2801 	EditPaM aStartPaM( aEditDoc.GetStartPaM() );
2802 	EditSelection aDummySel( aStartPaM );
2803 	EditSelection aFoundSel;
2804 	return ImpSearch( aTmpItem, aDummySel, aStartPaM, aFoundSel );
2805 #else
2806 	return sal_False;
2807 #endif
2808 }
2809 
2810 void ImpEditEngine::SetAutoCompleteText( const String& rStr, sal_Bool bClearTipWindow )
2811 {
2812 #ifndef SVX_LIGHT
2813 	aAutoCompleteText = rStr;
2814 	if ( bClearTipWindow && pActiveView )
2815 		Help::ShowQuickHelp( pActiveView->GetWindow(), Rectangle(), String(), 0 );
2816 #endif // !SVX_LIGHT
2817 }
2818 
2819 
2820 struct TransliterationChgData
2821 {
2822     sal_uInt16                      nStart;
2823     xub_StrLen                  nLen;
2824     EditSelection               aSelection;
2825     String                      aNewText;
2826     uno::Sequence< sal_Int32 >  aOffsets;
2827 };
2828 
2829 
2830 EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, sal_Int32 nTransliterationMode )
2831 {
2832     uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
2833     if (!_xBI.is())
2834         return rSelection;
2835 
2836 	EditSelection aSel( rSelection );
2837 	aSel.Adjust( aEditDoc );
2838 
2839 	if ( !aSel.HasRange() )
2840 		aSel = SelectWord( aSel );
2841 
2842     EditSelection aNewSel( aSel );
2843 
2844 	const sal_uInt16 nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
2845 	const sal_uInt16 nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
2846 
2847 	sal_Bool bChanges = sal_False;
2848     sal_Bool bLenChanged = sal_False;
2849 	EditUndoTransliteration* pUndo = NULL;
2850 
2851 	utl::TransliterationWrapper aTranslitarationWrapper( ::comphelper::getProcessServiceFactory(), nTransliterationMode );
2852 	sal_Bool bConsiderLanguage = aTranslitarationWrapper.needLanguageForTheMode();
2853 
2854 	for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++	)
2855 	{
2856 		ContentNode* pNode = aEditDoc.GetObject( nNode );
2857 		xub_StrLen nStartPos = 0;
2858 		xub_StrLen nEndPos = pNode->Len();
2859 		if ( nNode == nStartNode )
2860 			nStartPos = aSel.Min().GetIndex();
2861 		if ( nNode == nEndNode ) // kann auch == nStart sein!
2862 			nEndPos = aSel.Max().GetIndex();
2863 
2864 		sal_uInt16 nCurrentStart = nStartPos;
2865 		sal_uInt16 nCurrentEnd = nEndPos;
2866 		sal_uInt16 nLanguage = LANGUAGE_SYSTEM;
2867 
2868         // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
2869         // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
2870         // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
2871         // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
2872         // proper thing to do.
2873         const sal_Int16 nWordType = i18n::WordType::ANYWORD_IGNOREWHITESPACES;
2874 
2875         //! In order to have less trouble with changing text size, e.g. because
2876         //! of ligatures or � (German small sz) being resolved, we need to process
2877         //! the text replacements from end to start.
2878         //! This way the offsets for the yet to be changed words will be
2879         //! left unchanged by the already replaced text.
2880         //! For this we temporarily save the changes to be done in this vector
2881         std::vector< TransliterationChgData >   aChanges;
2882         TransliterationChgData                  aChgData;
2883 
2884         if (nTransliterationMode == i18n::TransliterationModulesExtra::TITLE_CASE)
2885         {
2886             // for 'capitalize every word' we need to iterate over each word
2887 
2888             i18n::Boundary aSttBndry;
2889             i18n::Boundary aEndBndry;
2890             aSttBndry = _xBI->getWordBoundary(
2891                         *pNode, nStartPos,
2892                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nStartPos + 1 ) ) ),
2893                         nWordType, sal_True /*prefer forward direction*/);
2894             aEndBndry = _xBI->getWordBoundary(
2895                         *pNode, nEndPos,
2896                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nEndPos + 1 ) ) ),
2897                         nWordType, sal_False /*prefer backward direction*/);
2898 
2899             // prevent backtracking to the previous word if selection is at word boundary
2900             if (aSttBndry.endPos <= nStartPos)
2901             {
2902                 aSttBndry = _xBI->nextWord(
2903                         *pNode, aSttBndry.endPos,
2904                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aSttBndry.endPos + 1 ) ) ),
2905                         nWordType);
2906             }
2907             // prevent advancing to the next word if selection is at word boundary
2908             if (aEndBndry.startPos >= nEndPos)
2909             {
2910                 aEndBndry = _xBI->previousWord(
2911                         *pNode, aEndBndry.startPos,
2912                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aEndBndry.startPos + 1 ) ) ),
2913                         nWordType);
2914             }
2915 
2916             i18n::Boundary aCurWordBndry( aSttBndry );
2917             while (aCurWordBndry.startPos <= aEndBndry.startPos)
2918             {
2919                 nCurrentStart = (xub_StrLen)aCurWordBndry.startPos;
2920                 nCurrentEnd   = (xub_StrLen)aCurWordBndry.endPos;
2921                 sal_Int32 nLen = nCurrentEnd - nCurrentStart;
2922                 DBG_ASSERT( nLen > 0, "invalid word length of 0" );
2923 #if OSL_DEBUG_LEVEL > 1
2924                 String aText( pNode->Copy( nCurrentStart, nLen ) );
2925 #endif
2926 
2927 	            Sequence< sal_Int32 > aOffsets;
2928                 String aNewText( aTranslitarationWrapper.transliterate( *pNode,
2929                         GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ),
2930                         nCurrentStart, nLen, &aOffsets ));
2931 
2932                 if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
2933                 {
2934                     aChgData.nStart     = nCurrentStart;
2935                     aChgData.nLen       = nLen;
2936                     aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
2937                     aChgData.aNewText   = aNewText;
2938                     aChgData.aOffsets   = aOffsets;
2939                     aChanges.push_back( aChgData );
2940                 }
2941 #if OSL_DEBUG_LEVEL > 1
2942                 String aSelTxt ( GetSelected( aChgData.aSelection ) );
2943                 (void) aSelTxt;
2944 #endif
2945 
2946                 aCurWordBndry = _xBI->nextWord( *pNode, nCurrentEnd,
2947                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
2948                         nWordType);
2949             }
2950             DBG_ASSERT( nCurrentEnd >= aEndBndry.endPos, "failed to reach end of transliteration" );
2951         }
2952         else if (nTransliterationMode == i18n::TransliterationModulesExtra::SENTENCE_CASE)
2953         {
2954             // for 'sentence case' we need to iterate sentence by sentence
2955 
2956             sal_Int32 nLastStart = _xBI->beginOfSentence(
2957                     *pNode, nEndPos,
2958                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nEndPos + 1 ) ) ) );
2959             sal_Int32 nLastEnd = _xBI->endOfSentence(
2960                     *pNode, nLastStart,
2961                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nLastStart + 1 ) ) ) );
2962 
2963             // extend nCurrentStart, nCurrentEnd to the current sentence boundaries
2964             nCurrentStart = _xBI->beginOfSentence(
2965                     *pNode, nStartPos,
2966                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nStartPos + 1 ) ) ) );
2967             nCurrentEnd = _xBI->endOfSentence(
2968                     *pNode, nCurrentStart,
2969                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
2970 
2971             // prevent backtracking to the previous sentence if selection starts at end of a sentence
2972             if (nCurrentEnd <= nStartPos)
2973             {
2974                 // now nCurrentStart is probably located on a non-letter word. (unless we
2975                 // are in Asian text with no spaces...)
2976                 // Thus to get the real sentence start we should locate the next real word,
2977                 // that is one found by DICTIONARY_WORD
2978                 i18n::Boundary aBndry = _xBI->nextWord( *pNode, nCurrentEnd,
2979                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
2980                         i18n::WordType::DICTIONARY_WORD);
2981 
2982                 // now get new current sentence boundaries
2983                 nCurrentStart = _xBI->beginOfSentence(
2984                         *pNode, aBndry.startPos,
2985                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aBndry.startPos + 1 ) ) ) );
2986                 nCurrentEnd = _xBI->endOfSentence(
2987                         *pNode, nCurrentStart,
2988                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
2989             }
2990             // prevent advancing to the next sentence if selection ends at start of a sentence
2991             if (nLastStart >= nEndPos)
2992             {
2993                 // now nCurrentStart is probably located on a non-letter word. (unless we
2994                 // are in Asian text with no spaces...)
2995                 // Thus to get the real sentence start we should locate the previous real word,
2996                 // that is one found by DICTIONARY_WORD
2997                 i18n::Boundary aBndry = _xBI->previousWord( *pNode, nLastStart,
2998                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nLastStart + 1 ) ) ),
2999                         i18n::WordType::DICTIONARY_WORD);
3000                 nLastEnd = _xBI->endOfSentence(
3001                         *pNode, aBndry.startPos,
3002                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aBndry.startPos + 1 ) ) ) );
3003                 if (nCurrentEnd > nLastEnd)
3004                     nCurrentEnd = nLastEnd;
3005             }
3006 
3007             while (nCurrentStart < nLastEnd)
3008             {
3009                 sal_Int32 nLen = nCurrentEnd - nCurrentStart;
3010                 DBG_ASSERT( nLen > 0, "invalid word length of 0" );
3011 #if OSL_DEBUG_LEVEL > 1
3012                 String aText( pNode->Copy( nCurrentStart, nLen ) );
3013 #endif
3014 
3015 	            Sequence< sal_Int32 > aOffsets;
3016                 String aNewText( aTranslitarationWrapper.transliterate( *pNode,
3017                         GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ),
3018                         nCurrentStart, nLen, &aOffsets ));
3019 
3020                 if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
3021                 {
3022                     aChgData.nStart     = nCurrentStart;
3023                     aChgData.nLen       = nLen;
3024                     aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
3025                     aChgData.aNewText   = aNewText;
3026                     aChgData.aOffsets   = aOffsets;
3027                     aChanges.push_back( aChgData );
3028                 }
3029 
3030                 i18n::Boundary aFirstWordBndry;
3031                 aFirstWordBndry = _xBI->nextWord(
3032                         *pNode, nCurrentEnd,
3033                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
3034                         nWordType);
3035                 nCurrentStart = aFirstWordBndry.startPos;
3036                 nCurrentEnd = _xBI->endOfSentence(
3037                         *pNode, nCurrentStart,
3038                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
3039             }
3040             DBG_ASSERT( nCurrentEnd >= nLastEnd, "failed to reach end of transliteration" );
3041         }
3042         else
3043         {
3044             do
3045             {
3046 		        if ( bConsiderLanguage )
3047 		        {
3048 			        nLanguage = GetLanguage( EditPaM( pNode, nCurrentStart+1 ), &nCurrentEnd );
3049 			        if ( nCurrentEnd > nEndPos )
3050 				        nCurrentEnd = nEndPos;
3051 		        }
3052 
3053 		        xub_StrLen nLen = nCurrentEnd - nCurrentStart;
3054 
3055 		        Sequence< sal_Int32 > aOffsets;
3056 		        String aNewText( aTranslitarationWrapper.transliterate( *pNode, nLanguage, nCurrentStart, nLen, &aOffsets ) );
3057 
3058                 if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
3059                 {
3060                     aChgData.nStart     = nCurrentStart;
3061                     aChgData.nLen       = nLen;
3062                     aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
3063                     aChgData.aNewText   = aNewText;
3064                     aChgData.aOffsets   = aOffsets;
3065                     aChanges.push_back( aChgData );
3066                 }
3067 
3068 		        nCurrentStart = nCurrentEnd;
3069 		    } while( nCurrentEnd < nEndPos );
3070         }
3071 
3072         if (aChanges.size() > 0)
3073         {
3074 #ifndef SVX_LIGHT
3075             // Create a single UndoAction on Demand for all the changes ...
3076             if ( !pUndo && IsUndoEnabled() && !IsInUndo() )
3077             {
3078                 // adjust selection to include all changes
3079                 for (size_t i = 0; i < aChanges.size(); ++i)
3080                 {
3081                     const EditSelection &rSel = aChanges[i].aSelection;
3082                     if (aSel.Min().GetNode() == rSel.Min().GetNode() &&
3083                         aSel.Min().GetIndex() > rSel.Min().GetIndex())
3084                         aSel.Min().SetIndex( rSel.Min().GetIndex() );
3085                     if (aSel.Max().GetNode() == rSel.Max().GetNode() &&
3086                         aSel.Max().GetIndex() < rSel.Max().GetIndex())
3087                         aSel.Max().SetIndex( rSel.Max().GetIndex() );
3088                 }
3089                 aNewSel = aSel;
3090 
3091                 ESelection aESel( CreateESel( aSel ) );
3092                 pUndo = new EditUndoTransliteration( this, aESel, nTransliterationMode );
3093 
3094                 const bool bSingleNode = aSel.Min().GetNode()== aSel.Max().GetNode();
3095                 const bool bHasAttribs = aSel.Min().GetNode()->GetCharAttribs().HasAttrib( aSel.Min().GetIndex(), aSel.Max().GetIndex() );
3096                 if (bSingleNode && !bHasAttribs)
3097 	                pUndo->SetText( aSel.Min().GetNode()->Copy( aSel.Min().GetIndex(), aSel.Max().GetIndex()-aSel.Min().GetIndex() ) );
3098                 else
3099 	                pUndo->SetText( CreateBinTextObject( aSel, NULL ) );
3100             }
3101 #endif
3102 
3103             // now apply the changes from end to start to leave the offsets of the
3104             // yet unchanged text parts remain the same.
3105             for (size_t i = 0; i < aChanges.size(); ++i)
3106             {
3107                 const TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ];
3108 
3109 			    bChanges = sal_True;
3110                 if (rData.nLen != rData.aNewText.Len())
3111                     bLenChanged = sal_True;
3112 
3113                 // Change text without loosing the attributes
3114                 sal_uInt16 nDiffs = ReplaceTextOnly( rData.aSelection.Min().GetNode(),
3115                         rData.nStart, rData.nLen, rData.aNewText, rData.aOffsets );
3116 
3117                 // adjust selection in end node to possibly changed size
3118                 if (aSel.Max().GetNode() == rData.aSelection.Max().GetNode())
3119                     aNewSel.Max().GetIndex() = aNewSel.Max().GetIndex() + nDiffs;
3120 
3121                 sal_uInt16 nSelNode = aEditDoc.GetPos( rData.aSelection.Min().GetNode() );
3122                 ParaPortion* pParaPortion = GetParaPortions()[nSelNode];
3123 			    pParaPortion->MarkSelectionInvalid( rData.nStart,
3124                         std::max< sal_uInt16 >( rData.nStart + rData.nLen,
3125                                             rData.nStart + rData.aNewText.Len() ) );
3126 		    }
3127         } // if (aChanges.size() > 0)
3128     }
3129 
3130 #ifndef SVX_LIGHT
3131 	if ( pUndo )
3132 	{
3133 		ESelection aESel( CreateESel( aNewSel ) );
3134 		pUndo->SetNewSelection( aESel );
3135 		InsertUndo( pUndo );
3136 	}
3137 #endif
3138 
3139 	if ( bChanges )
3140     {
3141         TextModified();
3142         SetModifyFlag( sal_True );
3143         if ( bLenChanged )
3144             UpdateSelections();
3145 		FormatAndUpdate();
3146     }
3147 
3148     return aNewSel;
3149 }
3150 
3151 
3152 short ImpEditEngine::ReplaceTextOnly(
3153     ContentNode* pNode,
3154     sal_uInt16 nCurrentStart, xub_StrLen nLen,
3155 	const String& rNewText,
3156     const uno::Sequence< sal_Int32 >& rOffsets )
3157 {
3158     (void)  nLen;
3159 
3160     // Change text without loosing the attributes
3161     sal_uInt16 nCharsAfterTransliteration =
3162         sal::static_int_cast< sal_uInt16 >(rOffsets.getLength());
3163     const sal_Int32* pOffsets = rOffsets.getConstArray();
3164     short nDiffs = 0;
3165     for ( sal_uInt16 n = 0; n < nCharsAfterTransliteration; n++ )
3166     {
3167         sal_uInt16 nCurrentPos = nCurrentStart+n;
3168         sal_Int32 nDiff = (nCurrentPos-nDiffs) - pOffsets[n];
3169 
3170         if ( !nDiff )
3171         {
3172             DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
3173             pNode->SetChar( nCurrentPos, rNewText.GetChar(n) );
3174         }
3175         else if ( nDiff < 0 )
3176         {
3177             // Replace first char, delete the rest...
3178             DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
3179             pNode->SetChar( nCurrentPos, rNewText.GetChar(n) );
3180 
3181             DBG_ASSERT( (nCurrentPos+1) < pNode->Len(), "TransliterateText - String smaller than expected!" );
3182             GetEditDoc().RemoveChars( EditPaM( pNode, nCurrentPos+1 ), sal::static_int_cast< sal_uInt16 >(-nDiff) );
3183         }
3184         else
3185         {
3186             DBG_ASSERT( nDiff == 1, "TransliterateText - Diff other than expected! But should work..." );
3187             GetEditDoc().InsertText( EditPaM( pNode, nCurrentPos ), rNewText.GetChar(n) );
3188 
3189         }
3190         nDiffs = sal::static_int_cast< short >(nDiffs + nDiff);
3191     }
3192 
3193     return nDiffs;
3194 }
3195 
3196 
3197 void ImpEditEngine::SetAsianCompressionMode( sal_uInt16 n )
3198 {
3199     if ( n != nAsianCompressionMode )
3200     {
3201         nAsianCompressionMode = n;
3202         if ( ImplHasText() )
3203         {
3204             FormatFullDoc();
3205             UpdateViews();
3206         }
3207     }
3208 }
3209 
3210 void ImpEditEngine::SetKernAsianPunctuation( sal_Bool b )
3211 {
3212     if ( b != bKernAsianPunctuation )
3213     {
3214         bKernAsianPunctuation = b;
3215         if ( ImplHasText() )
3216         {
3217             FormatFullDoc();
3218             UpdateViews();
3219         }
3220     }
3221 }
3222 
3223 void ImpEditEngine::SetAddExtLeading( sal_Bool bExtLeading )
3224 {
3225     if ( IsAddExtLeading() != bExtLeading )
3226     {
3227         bAddExtLeading = bExtLeading;
3228         if ( ImplHasText() )
3229         {
3230             FormatFullDoc();
3231             UpdateViews();
3232         }
3233     }
3234 };
3235 
3236 
3237 
3238 sal_Bool ImpEditEngine::ImplHasText() const
3239 {
3240     return ( ( GetEditDoc().Count() > 1 ) || GetEditDoc().GetObject(0)->Len() );
3241 }
3242 
3243 long ImpEditEngine::LogicToTwips( long n )
3244 {
3245 	Size aSz( n, 0 );
3246 	MapMode aTwipsMode( MAP_TWIP );
3247 	aSz = pRefDev->LogicToLogic( aSz, NULL, &aTwipsMode );
3248 	return aSz.Width();
3249 }
3250 
3251 
3252