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