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#
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 
1251 	sal_Bool bUsePortionInfo = sal_False;
1252 //	sal_Bool bFields = sal_False;
1253 	XParaPortionList* pPortionInfo = rTextObject.GetPortionInfo();
1254 
1255 	if ( pPortionInfo && ( (long)pPortionInfo->GetPaperWidth() == aPaperSize.Width() )
1256 			&& ( pPortionInfo->GetRefMapMode() == GetRefDevice()->GetMapMode() ) )
1257 	{
1258 		if ( ( pPortionInfo->GetRefDevPtr() == (sal_uIntPtr)GetRefDevice() ) ||
1259 			 ( ( pPortionInfo->GetRefDevType() == OUTDEV_VIRDEV ) &&
1260 			   ( GetRefDevice()->GetOutDevType() == OUTDEV_VIRDEV ) ) )
1261 		bUsePortionInfo = sal_True;
1262 	}
1263 
1264 	sal_Bool bConvertItems = sal_False;
1265 	MapUnit eSourceUnit = MapUnit(), eDestUnit = MapUnit();
1266 	if ( rTextObject.HasMetric() )
1267 	{
1268 		eSourceUnit = (MapUnit)rTextObject.GetMetric();
1269 		eDestUnit = (MapUnit)aEditDoc.GetItemPool().GetMetric( DEF_METRIC );
1270 		if ( eSourceUnit != eDestUnit )
1271 			bConvertItems = sal_True;
1272 	}
1273 
1274 	sal_uInt16 nContents = rTextObject.GetContents().Count();
1275 	sal_uInt16 nPara = aEditDoc.GetPos( aPaM.GetNode() );
1276 
1277 	for ( sal_uInt16 n = 0; n < nContents; n++, nPara++ )
1278 	{
1279 		ContentInfo* pC = rTextObject.GetContents().GetObject( n );
1280 
1281 		if ( bIsPasting ) 	//#115580#
1282 		{
1283 			if ( !n )
1284 				aPaM = aStart2PaM;
1285 			//init node
1286 			aPaM.GetNode()->SetStyleSheet( aStart1PaM.GetNode()->GetStyleSheet(), sal_False );
1287 			aPaM.GetNode()->GetContentAttribs().GetItems().ClearItem();
1288 			aPaM.GetNode()->GetCharAttribs().Clear();
1289 		}
1290 
1291 		sal_Bool bNewContent = aPaM.GetNode()->Len() ? sal_False: sal_True;
1292 		sal_uInt16 nStartPos = aPaM.GetIndex();
1293 
1294 		aPaM = ImpFastInsertText( aPaM, pC->GetText() );
1295 
1296 		ParaPortion* pPortion = FindParaPortion( aPaM.GetNode() );
1297 		DBG_ASSERT( pPortion, "Blinde Portion in FastInsertText" );
1298 		pPortion->MarkInvalid( nStartPos, pC->GetText().Len() );
1299 
1300 		// Zeicheattribute...
1301 		sal_Bool bAllreadyHasAttribs = aPaM.GetNode()->GetCharAttribs().Count() ? sal_True : sal_False;
1302 		sal_uInt16 nNewAttribs = pC->GetAttribs().Count();
1303 		if ( nNewAttribs )
1304 		{
1305             sal_Bool bUpdateFields = sal_False;
1306 			for ( sal_uInt16 nAttr = 0; nAttr < nNewAttribs; nAttr++ )
1307 			{
1308 				XEditAttribute* pX = pC->GetAttribs().GetObject( nAttr );
1309 				// Kann passieren wenn Absaetze >16K entstehen, dann wird einfach umgebrochen.
1310 				if ( pX->GetEnd() <= aPaM.GetNode()->Len() )
1311 				{
1312 					if ( !bAllreadyHasAttribs || pX->IsFeature() )
1313 					{
1314 						// Normale Attribute gehen dann schneller...
1315 						// Features duerfen nicht ueber EditDoc::InsertAttrib
1316 						// eingefuegt werden, sie sind bei FastInsertText schon im TextFluss
1317 						DBG_ASSERT( pX->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut zu gross!" );
1318 						EditCharAttrib* pAttr;
1319 						if ( !bConvertItems )
1320 							pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *(pX->GetItem()), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
1321 						else
1322 						{
1323 							SfxPoolItem* pNew = pX->GetItem()->Clone();
1324 							ConvertItem( *pNew, eSourceUnit, eDestUnit );
1325 							pAttr = MakeCharAttrib( aEditDoc.GetItemPool(), *pNew, pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos );
1326 							delete pNew;
1327 						}
1328 						DBG_ASSERT( pAttr->GetEnd() <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (1)" );
1329 						aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttr );
1330 						if ( pAttr->Which() == EE_FEATURE_FIELD )
1331                             bUpdateFields = sal_True;
1332 					}
1333 					else
1334 					{
1335 						DBG_ASSERT( pX->GetEnd()+nStartPos <= aPaM.GetNode()->Len(), "InsertBinTextObject: Attribut passt nicht! (2)" );
1336 						// Tabs und andere Features koennen nicht ueber InsertAttrib eingefuegt werden:
1337 						aEditDoc.InsertAttrib( aPaM.GetNode(), pX->GetStart()+nStartPos, pX->GetEnd()+nStartPos, *pX->GetItem() );
1338 					}
1339 				}
1340 			}
1341             if ( bUpdateFields )
1342                 UpdateFields();
1343 
1344             // Sonst QuickFormat => Keine Attribute!
1345 			pPortion->MarkSelectionInvalid( nStartPos, pC->GetText().Len() );
1346 		}
1347 
1348 		DBG_ASSERT( CheckOrderedList( aPaM.GetNode()->GetCharAttribs().GetAttribs(), sal_True ), "InsertBinTextObject: Start-Liste verdreht" );
1349 
1350 		sal_Bool bParaAttribs = sal_False;
1351 		if ( bNewContent || ( ( n > 0 ) && ( n < (nContents-1) ) ) )
1352 		{
1353             bParaAttribs = sal_False;
1354             // #101512# Don't overwrite level/style from existing paragraph in OutlineView
1355             // MT 10/2002: Removed because of #103874#, handled in Outliner::EndPasteOrDropHdl now.
1356 //            if ( !aStatus.IsOutliner() || n )
1357             {
1358 			    // nur dann Style und ParaAttribs, wenn neuer Absatz, oder
1359 			    // komplett inneliegender...
1360 			    bParaAttribs = pC->GetParaAttribs().Count() ? sal_True : sal_False;
1361 
1362 			    if ( bIsPasting )	//#115580#
1363 			    {
1364 				    nPara = aEditDoc.GetPos( aPaM.GetNode() );
1365 				    if ( GetStyleSheetPool() && pC->GetStyle().Len() )
1366 				    {
1367 					SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() );
1368 					DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
1369 					SetStyleSheet( nPara, pStyle );
1370 				    }
1371 			    }
1372 			    else
1373 			    if ( GetStyleSheetPool() && pC->GetStyle().Len() )
1374 			    {
1375 				    SfxStyleSheet* pStyle = (SfxStyleSheet*)GetStyleSheetPool()->Find( pC->GetStyle(), pC->GetFamily() );
1376 				    DBG_ASSERT( pStyle, "InsertBinTextObject - Style not found!" );
1377 				    SetStyleSheet( nPara, pStyle );
1378 			    }
1379 			    if ( !bConvertItems )
1380 				    SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), pC->GetParaAttribs() );
1381 			    else
1382 			    {
1383 				    SfxItemSet aAttribs( GetEmptyItemSet() );
1384 				    ConvertAndPutItems( aAttribs, pC->GetParaAttribs(), &eSourceUnit, &eDestUnit );
1385 				    SetParaAttribs( aEditDoc.GetPos( aPaM.GetNode() ), aAttribs );
1386 			    }
1387             }
1388 			if ( bNewContent && bUsePortionInfo )
1389 			{
1390 				XParaPortion* pXP = pPortionInfo->GetObject( n );
1391 				DBG_ASSERT( pXP, "InsertBinTextObject: PortionInfo?" );
1392 				ParaPortion* pParaPortion = GetParaPortions()[ nPara ];
1393 				DBG_ASSERT( pParaPortion, "InsertBinTextObject: ParaPortion?" );
1394 				pParaPortion->nHeight = pXP->nHeight;
1395 				pParaPortion->nFirstLineOffset = pXP->nFirstLineOffset;
1396 				pParaPortion->bForceRepaint = sal_True;
1397 				pParaPortion->SetValid();	// Nicht formatieren
1398 
1399 				// Die TextPortions
1400 				pParaPortion->GetTextPortions().Reset();
1401 				sal_uInt16 nCount = pXP->aTextPortions.Count();
1402 				for ( sal_uInt16 _n = 0; _n < nCount; _n++ )
1403 				{
1404 					TextPortion* pTextPortion = pXP->aTextPortions[_n];
1405 					TextPortion* pNew = new TextPortion( *pTextPortion );
1406 					pParaPortion->GetTextPortions().Insert( pNew, _n );
1407 				}
1408 
1409 				// Die Zeilen
1410 				pParaPortion->GetLines().Reset();
1411 				nCount = pXP->aLines.Count();
1412 				for ( sal_uInt16 m = 0; m < nCount; m++ )
1413 				{
1414 					EditLine* pLine = pXP->aLines[m];
1415 					EditLine* pNew = pLine->Clone();
1416 					pNew->SetInvalid();	// neu Painten!
1417 					pParaPortion->GetLines().Insert( pNew, m );
1418 				}
1419 #ifdef DBG_UTIL
1420 				sal_uInt16 nTest;
1421                 int nTPLen = 0, nTxtLen = 0;
1422 				for ( nTest = pParaPortion->GetTextPortions().Count(); nTest; )
1423 					nTPLen += pParaPortion->GetTextPortions().GetObject( --nTest )->GetLen();
1424 				for ( nTest = pParaPortion->GetLines().Count(); nTest; )
1425 					nTxtLen += pParaPortion->GetLines().GetObject( --nTest )->GetLen();
1426 				DBG_ASSERT( ( nTPLen == pParaPortion->GetNode()->Len() ) && ( nTxtLen == pParaPortion->GetNode()->Len() ), "InsertBinTextObject: ParaPortion not completely formatted!" );
1427 #endif
1428 			}
1429 		}
1430 		if ( !bParaAttribs ) // DefFont wird bei FastInsertParagraph nicht berechnet
1431 		{
1432 			aPaM.GetNode()->GetCharAttribs().GetDefFont() = aEditDoc.GetDefFont();
1433 			if ( aStatus.UseCharAttribs() )
1434 				aPaM.GetNode()->CreateDefFont();
1435 		}
1436 
1437 #ifndef SVX_LIGHT
1438 		if ( bNewContent && GetStatus().DoOnlineSpelling() && pC->GetWrongList() )
1439 		{
1440 			aPaM.GetNode()->DestroyWrongList();	// otherwise MLK, if list exists...
1441 			aPaM.GetNode()->SetWrongList( pC->GetWrongList()->Clone() );
1442 		}
1443 #endif // !SVX_LIGHT
1444 
1445 		if ( bIsPasting )	//#115580#
1446 		{
1447 			AdjustParaAttribsByStyleSheet( aPaM.GetNode() );
1448 			ParaAttribsToCharAttribs( aPaM.GetNode() );
1449 		}
1450 
1451 		// Zeilenumbruch, wenn weitere folgen...
1452 		if ( n < ( nContents-1) )
1453 		{
1454 			if ( bNewContent )
1455 				aPaM = ImpFastInsertParagraph( nPara+1 );
1456 			else
1457 				aPaM = ImpInsertParaBreak( aPaM, sal_False );
1458 		}
1459 	}
1460 
1461 	/* aSel.Max() = aPaM; */	//#115580#
1462 
1463 	if ( bIsPasting )
1464 	{
1465 		EditPaM aEnd2PaM( aPaM );
1466 
1467 		sal_Bool bSpecialBackward = aStart1PaM.GetNode()->Len() ? sal_False : sal_True;
1468 
1469 		aSel.Min() = ImpConnectParagraphs( aStart1PaM.GetNode(), aStart2PaM.GetNode(), bSpecialBackward );
1470 		bSpecialBackward = aEnd1PaM.GetNode()->Len() ? sal_True : sal_False;
1471 
1472 		aSel.Max() = ImpConnectParagraphs( ( ( nContents == 1 ) ? aStart1PaM.GetNode() : aEnd2PaM.GetNode() ),
1473 																		aEnd1PaM.GetNode(), bSpecialBackward );
1474 	}
1475 	else
1476 		aSel.Max() = aPaM;
1477 
1478 	DBG_ASSERT( !aSel.DbgIsBuggy( aEditDoc ), "InsertBibTextObject: Selektion kaput!(1)" );
1479 	return aSel;
1480 }
1481 
1482 LanguageType ImpEditEngine::GetLanguage( const EditPaM& rPaM, sal_uInt16* pEndPos ) const
1483 {
1484 	short nScriptType = GetScriptType( rPaM, pEndPos );	// pEndPos will be valid now, pointing to ScriptChange or NodeLen
1485 	sal_uInt16 nLangId = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType );
1486 	const SvxLanguageItem* pLangItem = &(const SvxLanguageItem&)rPaM.GetNode()->GetContentAttribs().GetItem( nLangId );
1487 	EditCharAttrib*	pAttr = rPaM.GetNode()->GetCharAttribs().FindAttrib( nLangId, rPaM.GetIndex() );
1488 	if ( pAttr )
1489 		pLangItem = (const SvxLanguageItem*)pAttr->GetItem();
1490 
1491 	if ( pEndPos && pAttr && ( pAttr->GetEnd() < *pEndPos ) )
1492 		*pEndPos = pAttr->GetEnd();
1493 
1494 	return pLangItem->GetLanguage();
1495 }
1496 
1497 ::com::sun::star::lang::Locale ImpEditEngine::GetLocale( const EditPaM& rPaM ) const
1498 {
1499 	return SvxCreateLocale( GetLanguage( rPaM ) );
1500 }
1501 
1502 Reference< XSpellChecker1 > ImpEditEngine::GetSpeller()
1503 {
1504 #ifndef SVX_LIGHT
1505 	if ( !xSpeller.is() )
1506 		xSpeller = SvxGetSpellChecker();
1507 #endif
1508 	return xSpeller;
1509 }
1510 
1511 
1512 SpellInfo * ImpEditEngine::CreateSpellInfo( const EditSelection &rSel, bool bMultipleDocs )
1513 {
1514     if (!pSpellInfo)
1515 	    pSpellInfo = new SpellInfo;
1516     else
1517         *pSpellInfo = SpellInfo();  // reset to default values
1518 
1519 	pSpellInfo->bMultipleDoc = bMultipleDocs;
1520     EditSelection aSentenceSel( SelectSentence( rSel ) );
1521 //    pSpellInfo->aSpellStart = CreateEPaM( aSentenceSel.Min() );
1522 //    pSpellInfo->aSpellTo    = CreateEPaM( rSel.HasRange()? aSentenceSel.Max() : aSentenceSel.Min() );
1523     // always spell draw objects completely, startting at the top.
1524     // (spelling in only a selection or not starting with the top requires
1525     // further changes elsewehe to work properly)
1526 	pSpellInfo->aSpellStart = EPaM();
1527     pSpellInfo->aSpellTo    = EPaM( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND );
1528     return pSpellInfo;
1529 }
1530 
1531 
1532 EESpellState ImpEditEngine::Spell( EditView* pEditView, sal_Bool bMultipleDoc )
1533 {
1534 #ifdef SVX_LIGHT
1535 	return EE_SPELL_NOSPELLER;
1536 #else
1537 
1538 	DBG_ASSERTWARNING( xSpeller.is(), "Kein Speller gesetzt!" );
1539 
1540 	if ( !xSpeller.is() )
1541 		return EE_SPELL_NOSPELLER;
1542 
1543 	aOnlineSpellTimer.Stop();
1544 
1545 	// Bei MultipleDoc immer von vorne/hinten...
1546 	if ( bMultipleDoc )
1547 	{
1548         pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
1549 	}
1550 
1551 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
1552 	pSpellInfo = CreateSpellInfo( aCurSel, bMultipleDoc );
1553 
1554 	sal_Bool bIsStart = sal_False;
1555 	if ( bMultipleDoc )
1556 		bIsStart = sal_True;	// Immer von Vorne bzw. von hinten...
1557     else if ( ( CreateEPaM( aEditDoc.GetStartPaM() ) == pSpellInfo->aSpellStart ) )
1558 		bIsStart = sal_True;
1559 
1560 	EditSpellWrapper* pWrp = new EditSpellWrapper( Application::GetDefDialogParent(),
1561 			xSpeller, bIsStart, sal_False, pEditView );
1562 	pWrp->SpellDocument();
1563 	delete pWrp;
1564 
1565 	if ( !bMultipleDoc )
1566 	{
1567 		pEditView->pImpEditView->DrawSelection();
1568 		if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
1569 			aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
1570 		aCurSel.Min() = aCurSel.Max();
1571 		pEditView->pImpEditView->SetEditSelection( aCurSel );
1572 		pEditView->pImpEditView->DrawSelection();
1573 		pEditView->ShowCursor( sal_True, sal_False );
1574 	}
1575 	EESpellState eState = pSpellInfo->eState;
1576 	delete pSpellInfo;
1577 	pSpellInfo = 0;
1578 	return eState;
1579 #endif
1580 }
1581 
1582 
1583 sal_Bool ImpEditEngine::HasConvertibleTextPortion( LanguageType nSrcLang )
1584 {
1585 #ifdef SVX_LIGHT
1586     return sal_False;
1587 #else
1588     sal_Bool    bHasConvTxt = sal_False;
1589 
1590     sal_uInt16 nParas = pEditEngine->GetParagraphCount();
1591     for (sal_uInt16 k = 0;  k < nParas;  ++k)
1592     {
1593         SvUShorts aPortions;
1594         pEditEngine->GetPortions( k, aPortions );
1595         for ( sal_uInt16 nPos = 0; nPos < aPortions.Count(); ++nPos )
1596         {
1597             sal_uInt16 nEnd   = aPortions.GetObject( nPos );
1598             sal_uInt16 nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;
1599 
1600 			// if the paragraph is not empty we need to increase the index
1601 			// by one since the attribute of the character left to the
1602 			// specified position is evaluated.
1603 			if (nEnd > nStart)	// empty para?
1604 				++nStart;
1605             LanguageType nLangFound = pEditEngine->GetLanguage( k, nStart );
1606 #ifdef DEBUG
1607             lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
1608 #endif
1609             bHasConvTxt =   (nSrcLang == nLangFound) ||
1610                             (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
1611                              editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
1612             if (bHasConvTxt)
1613                 return bHasConvTxt;
1614        }
1615     }
1616 
1617 #endif
1618     return bHasConvTxt;
1619 }
1620 
1621 
1622 void ImpEditEngine::Convert( EditView* pEditView,
1623         LanguageType nSrcLang, LanguageType nDestLang, const Font *pDestFont,
1624         sal_Int32 nOptions, sal_Bool bIsInteractive, sal_Bool bMultipleDoc )
1625 {
1626     // modified version of ImpEditEngine::Spell
1627 
1628 #ifdef SVX_LIGHT
1629 #else
1630 
1631     // Bei MultipleDoc immer von vorne/hinten...
1632     if ( bMultipleDoc )
1633         pEditView->pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
1634 
1635 	//
1636 	// initialize pConvInfo
1637 	//
1638     EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
1639     aCurSel.Adjust( aEditDoc );
1640     pConvInfo = new ConvInfo;
1641     pConvInfo->bMultipleDoc = bMultipleDoc;
1642     pConvInfo->aConvStart = CreateEPaM( aCurSel.Min() );
1643 	//
1644 	// if it is not just a selection and we are about to begin
1645 	// with the current conversion for the very first time
1646 	// we need to find the start of the current (initial)
1647 	// convertible unit in order for the text conversion to give
1648 	// the correct result for that. Since it is easier to obtain
1649 	// the start of the word we use that though.
1650 	if (!aCurSel.HasRange() && ImplGetBreakIterator().is())
1651 	{
1652 		EditPaM aWordStartPaM(	SelectWord( aCurSel, i18n::WordType::DICTIONARY_WORD ).Min() );
1653 
1654 		// since #118246 / #117803 still occurs if the cursor is placed
1655 		// between the two chinese characters to be converted (because both
1656 		// of them are words on their own!) using the word boundary here does
1657 		// not work. Thus since chinese conversion is not interactive we start
1658 		// at the begin of the paragraph to solve the problem, i.e. have the
1659         // TextConversion service get those characters together in the same call.
1660 		sal_uInt16 nStartIdx = ( editeng::HangulHanjaConversion::IsChinese( nSrcLang ) ) ?
1661 								0 : aWordStartPaM.GetIndex();
1662 		pConvInfo->aConvStart.nIndex = nStartIdx;
1663 	}
1664 	//
1665     pConvInfo->aConvContinue = pConvInfo->aConvStart;
1666 
1667     sal_Bool bIsStart = sal_False;
1668     if ( bMultipleDoc )
1669         bIsStart = sal_True;    // Immer von Vorne bzw. von hinten...
1670     else if ( CreateEPaM( aEditDoc.GetStartPaM() ) == pConvInfo->aConvStart )
1671         bIsStart = sal_True;
1672 
1673     bImpConvertFirstCall = sal_True;    // next ImpConvert call is the very first in this conversion turn
1674 
1675     Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
1676     TextConvWrapper aWrp( Application::GetDefDialogParent(), xMSF,
1677                           SvxCreateLocale( nSrcLang ), SvxCreateLocale( nDestLang ),
1678                           pDestFont,
1679                           nOptions, bIsInteractive,
1680                           bIsStart, pEditView );
1681 
1682 	//
1683 	//!! optimization does not work since when update mode is false
1684     //!! the object is 'lying' about it portions, paragraphs,
1685 	//!! EndPaM... later on.
1686     //!! Should not be a great problem since text boxes or cells in
1687 	//!! Calc usually have only a rather short text.
1688 	//
1689 	// disallow formatting, updating the view, ... while
1690 	// non-interactively converting the document. (saves time)
1691 	//if (!bIsInteractive)
1692 	//	SetUpdateMode( sal_False );
1693 
1694 	aWrp.Convert();
1695 
1696 	//if (!bIsInteractive)
1697 	//SetUpdateMode( sal_True, 0, sal_True );
1698 
1699     if ( !bMultipleDoc )
1700     {
1701         pEditView->pImpEditView->DrawSelection();
1702         if ( aCurSel.Max().GetIndex() > aCurSel.Max().GetNode()->Len() )
1703             aCurSel.Max().GetIndex() = aCurSel.Max().GetNode()->Len();
1704         aCurSel.Min() = aCurSel.Max();
1705         pEditView->pImpEditView->SetEditSelection( aCurSel );
1706         pEditView->pImpEditView->DrawSelection();
1707         pEditView->ShowCursor( sal_True, sal_False );
1708     }
1709     delete pConvInfo;
1710     pConvInfo = 0;
1711 #endif
1712 }
1713 
1714 
1715 void ImpEditEngine::SetLanguageAndFont(
1716     const ESelection &rESel,
1717     LanguageType nLang, sal_uInt16 nLangWhichId,
1718     const Font *pFont,  sal_uInt16 nFontWhichId )
1719 {
1720     ESelection aOldSel = pActiveView->GetSelection();
1721     pActiveView->SetSelection( rESel );
1722 
1723     // set new language attribute
1724     SfxItemSet aNewSet( pActiveView->GetEmptyItemSet() );
1725     aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
1726 
1727     // new font to be set?
1728     DBG_ASSERT( pFont, "target font missing?" );
1729     if (pFont)
1730     {
1731         // set new font attribute
1732         SvxFontItem aFontItem = (SvxFontItem&) aNewSet.Get( nFontWhichId );
1733         aFontItem.SetFamilyName( pFont->GetName());
1734         aFontItem.SetFamily( pFont->GetFamily());
1735         aFontItem.SetStyleName( pFont->GetStyleName());
1736         aFontItem.SetPitch( pFont->GetPitch());
1737         aFontItem.SetCharSet( pFont->GetCharSet() );
1738         aNewSet.Put( aFontItem );
1739     }
1740 
1741     // apply new attributes
1742     pActiveView->SetAttribs( aNewSet );
1743 
1744     pActiveView->SetSelection( aOldSel );
1745 }
1746 
1747 
1748 void ImpEditEngine::ImpConvert( rtl::OUString &rConvTxt, LanguageType &rConvTxtLang,
1749         EditView* pEditView, LanguageType nSrcLang, const ESelection &rConvRange,
1750         sal_Bool bAllowImplicitChangesForNotConvertibleText,
1751         LanguageType nTargetLang, const Font *pTargetFont  )
1752 {
1753     // modified version of ImpEditEngine::ImpSpell
1754 
1755     // looks for next convertible text portion to be passed on to the wrapper
1756 
1757     String aRes;
1758     LanguageType nResLang = LANGUAGE_NONE;
1759 
1760 #ifdef SVX_LIGHT
1761     rConvTxt = rtl::OUString();
1762     rConvTxtLang = LANGUAGE_NONE;
1763 #else
1764 
1765     /* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( aEditDoc.Count()-1 );
1766 
1767     EditPaM aPos( CreateEditPaM( pConvInfo->aConvContinue ) );
1768 	EditSelection aCurSel = EditSelection( aPos, aPos );
1769 
1770     String aWord;
1771 
1772     while (!aRes.Len())
1773     {
1774         // empty paragraph found that needs to have language and font set?
1775 		if (bAllowImplicitChangesForNotConvertibleText &&
1776             !pEditEngine->GetText( pConvInfo->aConvContinue.nPara ).Len())
1777 		{
1778 			sal_uInt16 nPara = pConvInfo->aConvContinue.nPara;
1779             ESelection aESel( nPara, 0, nPara, 0 );
1780             // see comment for below same function call
1781             SetLanguageAndFont( aESel,
1782                     nTargetLang, EE_CHAR_LANGUAGE_CJK,
1783                     pTargetFont, EE_CHAR_FONTINFO_CJK );
1784 		}
1785 
1786 
1787 		if (pConvInfo->aConvContinue.nPara  == pConvInfo->aConvTo.nPara &&
1788 			pConvInfo->aConvContinue.nIndex >= pConvInfo->aConvTo.nIndex)
1789 			break;
1790 
1791 /*
1792         // Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
1793         // Current bei jeder Ersetzung korrigiert werden, sonst passt
1794         // das Ende evtl. nicht mehr genau...
1795         if ( pConvInfo->bConvToEnd || pConvInfo->bMultipleDoc )
1796         {
1797             if ( aCurSel.Max().GetNode() == pLastNode &&
1798                  aCurSel.Max().GetIndex() >= pLastNode->Len() )
1799                 break;
1800         }
1801 */
1802 
1803 		sal_uInt16 nAttribStart	= USHRT_MAX;
1804 		sal_uInt16 nAttribEnd	= USHRT_MAX;
1805 		sal_uInt16 nCurPos		= USHRT_MAX;
1806 		EPaM aCurStart = CreateEPaM( aCurSel.Min() );
1807 		SvUShorts aPortions;
1808 		pEditEngine->GetPortions( (sal_uInt16)aCurStart.nPara, aPortions );
1809 		for ( sal_uInt16 nPos = 0; nPos < aPortions.Count(); ++nPos )
1810 		{
1811 			sal_uInt16 nEnd	  = aPortions.GetObject( nPos );
1812 			sal_uInt16 nStart = nPos > 0 ? aPortions.GetObject( nPos - 1 ) : 0;
1813 
1814             // the language attribute is obtained from the left character
1815             // (like usually all other attributes)
1816             // thus we usually have to add 1 in order to get the language
1817             // of the text right to the cursor position
1818             sal_uInt16 nLangIdx = nEnd > nStart ? nStart + 1 : nStart;
1819             LanguageType nLangFound = pEditEngine->GetLanguage( aCurStart.nPara, nLangIdx );
1820 #ifdef DEBUG
1821             lang::Locale aLocale( SvxCreateLocale( nLangFound ) );
1822 #endif
1823             sal_Bool bLangOk =  (nLangFound == nSrcLang) ||
1824                                 (editeng::HangulHanjaConversion::IsChinese( nLangFound ) &&
1825                                  editeng::HangulHanjaConversion::IsChinese( nSrcLang ));
1826 
1827             if (nAttribEnd != USHRT_MAX) // start already found?
1828 			{
1829 				DBG_ASSERT(nEnd >= aCurStart.nIndex, "error while scanning attributes (a)" );
1830 				DBG_ASSERT(nEnd >= nAttribEnd, "error while scanning attributes (b)" );
1831 				if (/*nEnd >= aCurStart.nIndex &&*/ nLangFound == nResLang)
1832 					nAttribEnd = nEnd;
1833 				else  // language attrib has changed
1834 					break;
1835 			}
1836 			if (nAttribStart == USHRT_MAX && // start not yet found?
1837 				nEnd > aCurStart.nIndex && bLangOk)
1838 			{
1839 				nAttribStart = nStart;
1840 				nAttribEnd   = nEnd;
1841 				nResLang = nLangFound;
1842 			}
1843 			//! the list of portions may have changed compared to the previous
1844 			//! call to this function (because of possibly changed language
1845 			//! attribute!)
1846 			//! But since we don't want to start in the already processed part
1847 			//! we clip the start accordingly.
1848 			if (nAttribStart < aCurStart.nIndex)
1849 			{
1850 				nAttribStart = aCurStart.nIndex;
1851 			}
1852 
1853             // check script type to the right of the start of the current portion
1854             EditPaM aPaM( CreateEditPaM( EPaM(aCurStart.nPara, nLangIdx) ) );
1855             sal_Bool bIsAsianScript = (i18n::ScriptType::ASIAN == GetScriptType( aPaM ));
1856             // not yet processed text part with for conversion
1857             // not suitable language found that needs to be changed?
1858             if (bAllowImplicitChangesForNotConvertibleText &&
1859                 !bLangOk && !bIsAsianScript && nEnd > aCurStart.nIndex)
1860 			{
1861                 ESelection aESel( aCurStart.nPara, nStart, aCurStart.nPara, nEnd );
1862 				// set language and font to target language and font of conversion
1863 				//! Now this especially includes all non convertible text e.g.
1864 				//! spaces, empty paragraphs and western text.
1865 				// This is in order for every *new* text entered at *any* position to
1866 				// have the correct language and font attributes set.
1867                 SetLanguageAndFont( aESel,
1868                         nTargetLang, EE_CHAR_LANGUAGE_CJK,
1869                         pTargetFont, EE_CHAR_FONTINFO_CJK );
1870 			}
1871 
1872 			nCurPos = nEnd;
1873 		}
1874 
1875 		if (nAttribStart != USHRT_MAX  &&  nAttribEnd != USHRT_MAX)
1876 		{
1877 			aCurSel.Min().SetIndex( nAttribStart );
1878 			aCurSel.Max().SetIndex( nAttribEnd );
1879 		}
1880 		else if (nCurPos != USHRT_MAX)
1881 		{
1882 			// set selection to end of scanned text
1883 			// (used to set the position where to continue from later on)
1884 			aCurSel.Min().SetIndex( nCurPos );
1885 			aCurSel.Max().SetIndex( nCurPos );
1886 		}
1887 
1888 		if ( !pConvInfo->bConvToEnd )
1889         {
1890             EPaM aEPaM( CreateEPaM( aCurSel.Min() ) );
1891             if ( !( aEPaM < pConvInfo->aConvTo ) )
1892                 break;
1893         }
1894 
1895 		// clip selected word to the converted area
1896 		// (main use when conversion starts/ends **within** a word)
1897 		EditPaM aPaM( CreateEditPaM( pConvInfo->aConvStart ) );
1898 		if (pConvInfo->bConvToEnd &&
1899 			aCurSel.Min().GetNode() == aPaM.GetNode() &&
1900 			aCurSel.Min().GetIndex() < aPaM.GetIndex())
1901 				aCurSel.Min().SetIndex( aPaM.GetIndex() );
1902 		aPaM = CreateEditPaM( pConvInfo->aConvContinue );
1903 		if (aCurSel.Min().GetNode() == aPaM.GetNode() &&
1904 			aCurSel.Min().GetIndex() < aPaM.GetIndex())
1905 				aCurSel.Min().SetIndex( aPaM.GetIndex() );
1906 		aPaM = CreateEditPaM( pConvInfo->aConvTo );
1907 		if ((!pConvInfo->bConvToEnd || rConvRange.HasRange())&&
1908 			aCurSel.Max().GetNode() == aPaM.GetNode() &&
1909 			aCurSel.Max().GetIndex() > aPaM.GetIndex())
1910 				aCurSel.Max().SetIndex( aPaM.GetIndex() );
1911 
1912 		aWord = GetSelected( aCurSel );
1913 
1914         if ( aWord.Len() > 0 /* && bLangOk */)
1915             aRes = aWord;
1916 
1917 		// move to next word/paragraph if necessary
1918         if ( !aRes.Len() )
1919             aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1920 
1921         pConvInfo->aConvContinue = CreateEPaM( aCurSel.Max() );
1922     }
1923 
1924     pEditView->pImpEditView->DrawSelection();
1925     pEditView->pImpEditView->SetEditSelection( aCurSel );
1926     pEditView->pImpEditView->DrawSelection();
1927     pEditView->ShowCursor( sal_True, sal_False );
1928 
1929     rConvTxt = aRes;
1930     if (rConvTxt.getLength())
1931         rConvTxtLang = nResLang;
1932 #endif
1933 }
1934 
1935 
1936 Reference< XSpellAlternatives > ImpEditEngine::ImpSpell( EditView* pEditView )
1937 {
1938 #ifdef SVX_LIGHT
1939 	return Reference< XSpellAlternatives >();
1940 #else
1941 
1942 	DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
1943 
1944     ContentNode* pLastNode = aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
1945 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
1946     aCurSel.Min() = aCurSel.Max();
1947 
1948 	String aWord;
1949 	Reference< XSpellAlternatives > xSpellAlt;
1950 	Sequence< PropertyValue > aEmptySeq;
1951 	while (!xSpellAlt.is())
1952 	{
1953 
1954 		// Bekannter (wahrscheinlicher) Bug: Wenn SpellToCurrent, muss
1955 		// Current bei jeder Ersetzung korrigiert werden, sonst passt
1956 		// das Ende evtl. nicht mehr genau...
1957 		if ( pSpellInfo->bSpellToEnd || pSpellInfo->bMultipleDoc )
1958 		{
1959 			if ( aCurSel.Max().GetNode() == pLastNode )
1960 			{
1961                 if ( ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
1962 					break;
1963 			}
1964 		}
1965 		else if ( !pSpellInfo->bSpellToEnd )
1966 		{
1967 			EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
1968             if ( !( aEPaM < pSpellInfo->aSpellTo ) )
1969 				break;
1970 		}
1971 
1972 		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1973 		aWord = GetSelected( aCurSel );
1974 
1975 		// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
1976 		// Falls Abkuerzung...
1977 		if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
1978 		{
1979 			sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
1980 			if ( cNext == '.' )
1981 			{
1982 				aCurSel.Max().GetIndex()++;
1983 				aWord += cNext;
1984 			}
1985 		}
1986 
1987 		if ( aWord.Len() > 0 )
1988 		{
1989 			LanguageType eLang = GetLanguage( aCurSel.Max() );
1990 			SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
1991 			xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
1992 		}
1993 
1994 		if ( !xSpellAlt.is() )
1995 			aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
1996 		else
1997 			pSpellInfo->eState = EE_SPELL_ERRORFOUND;
1998 	}
1999 
2000 	pEditView->pImpEditView->DrawSelection();
2001 	pEditView->pImpEditView->SetEditSelection( aCurSel );
2002 	pEditView->pImpEditView->DrawSelection();
2003 	pEditView->ShowCursor( sal_True, sal_False );
2004 	return xSpellAlt;
2005 #endif
2006 }
2007 /*-- 13.10.2003 16:43:27---------------------------------------------------
2008 
2009   -----------------------------------------------------------------------*/
2010 void ImpEditEngine::EndSpelling()
2011 {
2012     DELETEZ(pSpellInfo);
2013 }
2014 /*-- 13.10.2003 16:43:27---------------------------------------------------
2015 
2016   -----------------------------------------------------------------------*/
2017 void ImpEditEngine::StartSpelling(EditView& rEditView, sal_Bool bMultipleDoc)
2018 {
2019     DBG_ASSERT(!pSpellInfo, "pSpellInfo already set?");
2020     rEditView.pImpEditView->SetEditSelection( aEditDoc.GetStartPaM() );
2021     EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
2022     pSpellInfo = CreateSpellInfo( aCurSel, bMultipleDoc );
2023 }
2024 /*-- 13.10.2003 16:43:27---------------------------------------------------
2025     Search for the next wrong word within the given selection
2026   -----------------------------------------------------------------------*/
2027 Reference< XSpellAlternatives > ImpEditEngine::ImpFindNextError(EditSelection& rSelection)
2028 {
2029     /* ContentNode* pLastNode = */ aEditDoc.SaveGetObject( (aEditDoc.Count()-1) );
2030     EditSelection aCurSel( rSelection.Min() );
2031 
2032     String aWord;
2033     Reference< XSpellAlternatives > xSpellAlt;
2034     Sequence< PropertyValue > aEmptySeq;
2035     while (!xSpellAlt.is())
2036     {
2037         //check if the end of the selection has been reached
2038         {
2039             EPaM aEPaM( CreateEPaM( aCurSel.Max() ) );
2040             if ( !( aEPaM < CreateEPaM( rSelection.Max()) ) )
2041                 break;
2042         }
2043 
2044         aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2045         aWord = GetSelected( aCurSel );
2046 
2047         // Wenn Punkt dahinter, muss dieser mit uebergeben werden !
2048         // Falls Abkuerzung...
2049         if ( aWord.Len() && ( aCurSel.Max().GetIndex() < aCurSel.Max().GetNode()->Len() ) )
2050         {
2051             sal_Unicode cNext = aCurSel.Max().GetNode()->GetChar( aCurSel.Max().GetIndex() );
2052             if ( cNext == '.' )
2053             {
2054                 aCurSel.Max().GetIndex()++;
2055                 aWord += cNext;
2056             }
2057         }
2058 
2059         if ( aWord.Len() > 0 )
2060             xSpellAlt = xSpeller->spell( aWord, GetLanguage( aCurSel.Max() ), aEmptySeq );
2061 
2062         if ( !xSpellAlt.is() )
2063             aCurSel = WordRight( aCurSel.Min(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2064         else
2065 		{
2066             pSpellInfo->eState = EE_SPELL_ERRORFOUND;
2067 			rSelection = aCurSel;
2068 		}
2069     }
2070     return xSpellAlt;
2071 }
2072 /*-- 13.10.2003 16:43:27---------------------------------------------------
2073 
2074   -----------------------------------------------------------------------*/
2075 bool ImpEditEngine::SpellSentence(EditView& rEditView,
2076     ::svx::SpellPortions& rToFill,
2077     bool /*bIsGrammarChecking*/ )
2078 {
2079 #ifdef SVX_LIGHT
2080 #else
2081     bool bRet = false;
2082     EditSelection aCurSel( rEditView.pImpEditView->GetEditSelection() );
2083     if(!pSpellInfo)
2084 		pSpellInfo = CreateSpellInfo( aCurSel, true );
2085     pSpellInfo->aCurSentenceStart = aCurSel.Min();
2086     DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
2087     pSpellInfo->aLastSpellPortions.clear();
2088     pSpellInfo->aLastSpellContentSelections.clear();
2089     rToFill.clear();
2090     //if no selection previously exists the range is extended to the end of the object
2091     if(aCurSel.Min() == aCurSel.Max())
2092     {
2093         ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count()-1);
2094         aCurSel.Max() = EditPaM(pLastNode, pLastNode->Len());
2095     }
2096     // check for next error in aCurSel and set aCurSel to that one if any was found
2097     Reference< XSpellAlternatives > xAlt = ImpFindNextError(aCurSel);
2098     if (xAlt.is())
2099     {
2100         bRet = true;
2101 		//find the sentence boundaries
2102         EditSelection aSentencePaM = SelectSentence(aCurSel);
2103         //make sure that the sentence is never smaller than the error range!
2104         if(aSentencePaM.Max().GetIndex() < aCurSel.Max().GetIndex())
2105             aSentencePaM.Max() = aCurSel.Max();
2106         //add the portion preceeding the error
2107         EditSelection aStartSelection(aSentencePaM.Min(), aCurSel.Min());
2108         if(aStartSelection.HasRange())
2109 			AddPortionIterated(rEditView, aStartSelection, 0, rToFill);
2110         //add the error portion
2111         AddPortionIterated(rEditView, aCurSel, xAlt, rToFill);
2112         //find the end of the sentence
2113         //search for all errors in the rest of the sentence and add all the portions
2114         do
2115         {
2116             EditSelection aNextSel = EditSelection(aCurSel.Max(), aSentencePaM.Max());
2117             xAlt = ImpFindNextError(aNextSel);
2118             if(xAlt.is())
2119             {
2120                 //add the part between the previous and the current error
2121 				AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aNextSel.Min()), 0, rToFill);
2122                 //add the current error
2123 				AddPortionIterated(rEditView, aNextSel, xAlt, rToFill);
2124             }
2125             else
2126                 AddPortionIterated(rEditView, EditSelection(aCurSel.Max(), aSentencePaM.Max()), xAlt, rToFill);
2127 			aCurSel = aNextSel;
2128         }
2129         while( xAlt.is() );
2130 
2131 		//set the selection to the end of the current sentence
2132 		rEditView.pImpEditView->SetEditSelection(aSentencePaM.Max());
2133     }
2134 #endif
2135     return bRet;
2136 }
2137 
2138 /*-- 15.10.2003 16:09:12---------------------------------------------------
2139     adds one portion to the SpellPortions
2140   -----------------------------------------------------------------------*/
2141 void ImpEditEngine::AddPortion(
2142                             const EditSelection rSel,
2143                             uno::Reference< XSpellAlternatives > xAlt,
2144                                 ::svx::SpellPortions& rToFill,
2145                                 bool bIsField)
2146 {
2147 #ifdef SVX_LIGHT
2148 #else
2149     if(rSel.HasRange())
2150 	{
2151 		svx::SpellPortion aPortion;
2152 		aPortion.sText = GetSelected( rSel );
2153 		aPortion.eLanguage = GetLanguage( rSel.Min() );
2154 		aPortion.xAlternatives = xAlt;
2155         aPortion.bIsField = bIsField;
2156         rToFill.push_back(aPortion);
2157 
2158 		//save the spelled portions for later use
2159 		pSpellInfo->aLastSpellPortions.push_back(aPortion);
2160 		pSpellInfo->aLastSpellContentSelections.push_back(rSel);
2161 
2162 	}
2163 #endif
2164 }
2165 
2166 /*-- 15.10.2003 16:07:47---------------------------------------------------
2167     adds one or more portions of text to the SpellPortions depending on language changes
2168   -----------------------------------------------------------------------*/
2169 void ImpEditEngine::AddPortionIterated(
2170                             EditView& rEditView,
2171                             const EditSelection rSel,
2172                             Reference< XSpellAlternatives > xAlt,
2173                                 ::svx::SpellPortions& rToFill)
2174 {
2175 #ifdef SVX_LIGHT
2176 #else
2177     if(rSel.Min() != rSel.Max())
2178     {
2179         if(xAlt.is())
2180         {
2181             AddPortion(rSel, xAlt, rToFill, false);
2182         }
2183         else
2184         {
2185             //iterate and search for language attribute changes
2186             //save the start and end positions
2187             bool bTest = rSel.Min().GetIndex() <= rSel.Max().GetIndex();
2188             EditPaM aStart(bTest ? rSel.Min() : rSel.Max());
2189             EditPaM aEnd(bTest ? rSel.Max() : rSel.Min());
2190             //iterate over the text to find changes in language
2191             //set the mark equal to the point
2192             EditPaM aCursor(aStart);
2193             rEditView.pImpEditView->SetEditSelection( aCursor );
2194             LanguageType eStartLanguage = GetLanguage( aCursor );
2195             //search for a field attribute at the beginning - only the end position
2196             //of this field is kept to end a portion at that position
2197             const EditCharAttrib* pFieldAttr = aCursor.GetNode()->GetCharAttribs().
2198                                                     FindFeature( aCursor.GetIndex() );
2199             bool bIsField = pFieldAttr &&
2200                     pFieldAttr->GetStart() == aCursor.GetIndex() &&
2201                     pFieldAttr->GetStart() != pFieldAttr->GetEnd() &&
2202                     pFieldAttr->Which() == EE_FEATURE_FIELD;
2203             sal_uInt16 nEndField = bIsField ? pFieldAttr->GetEnd() : USHRT_MAX;
2204 			bool bIsEndField = false;
2205             do
2206             {
2207                 aCursor = CursorRight( aCursor);
2208                 //determine whether a field and has been reached
2209 				bIsEndField = nEndField == aCursor.GetIndex();
2210                 //search for a new field attribute
2211                 EditCharAttrib* _pFieldAttr = aCursor.GetNode()->GetCharAttribs().
2212                                                         FindFeature( aCursor.GetIndex() );
2213                 bIsField = _pFieldAttr &&
2214                         _pFieldAttr->GetStart() == aCursor.GetIndex() &&
2215                         _pFieldAttr->GetStart() != _pFieldAttr->GetEnd() &&
2216                         _pFieldAttr->Which() == EE_FEATURE_FIELD;
2217                 //on every new field move the end position
2218                 if(bIsField)
2219                     nEndField = bIsField ? _pFieldAttr->GetEnd() : USHRT_MAX;
2220 
2221                 LanguageType eCurLanguage = GetLanguage( aCursor );
2222                 if(eCurLanguage != eStartLanguage || bIsField || bIsEndField)
2223                 {
2224                     eStartLanguage = eCurLanguage;
2225                     //go one step back - the cursor currently selects the first character
2226                     //with a different language
2227                     //create a selection from start to the current Cursor
2228                     EditSelection aSelection(aStart, aCursor);
2229                     AddPortion(aSelection, xAlt, rToFill, bIsEndField);
2230                     aStart = aCursor;
2231                 }
2232             }
2233             while(aCursor.GetIndex() < aEnd.GetIndex());
2234             EditSelection aSelection(aStart, aCursor);
2235             AddPortion(aSelection, xAlt, rToFill, bIsField);
2236         }
2237     }
2238 #endif
2239 }
2240 
2241 /*-- 13.10.2003 16:43:33---------------------------------------------------
2242 
2243   -----------------------------------------------------------------------*/
2244 void ImpEditEngine::ApplyChangedSentence(EditView& rEditView,
2245     const ::svx::SpellPortions& rNewPortions,
2246     bool bRecheck )
2247 {
2248 #ifdef SVX_LIGHT
2249 #else
2250     // Note: rNewPortions.size() == 0 is valid and happens when the whole
2251     // sentence got removed in the dialog
2252 
2253     DBG_ASSERT(pSpellInfo, "pSpellInfo not initialized");
2254     if (pSpellInfo &&
2255         pSpellInfo->aLastSpellPortions.size() > 0)  // no portions -> no text to be changed
2256     {
2257         // get current paragraph length to calculate later on how the sentence length changed,
2258         // in order to place the cursor at the end of the sentence again
2259         EditSelection aOldSel( rEditView.pImpEditView->GetEditSelection() );
2260         xub_StrLen nOldLen = aOldSel.Max().GetNode()->Len();
2261 
2262         UndoActionStart( EDITUNDO_INSERT );
2263         if(pSpellInfo->aLastSpellPortions.size() == rNewPortions.size())
2264         {
2265             DBG_ASSERT( rNewPortions.size() > 0, "rNewPortions should not be empty here" );
2266             DBG_ASSERT( pSpellInfo->aLastSpellPortions.size() == pSpellInfo->aLastSpellContentSelections.size(),
2267                     "aLastSpellPortions and aLastSpellContentSelections size mismatch" );
2268 
2269             //the simple case: the same number of elements on both sides
2270             //each changed element has to be applied to the corresponding source element
2271             svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.end();
2272             svx::SpellPortions::const_iterator aCurrentOldPortion = pSpellInfo->aLastSpellPortions.end();
2273             SpellContentSelections::const_iterator aCurrentOldPosition = pSpellInfo->aLastSpellContentSelections.end();
2274             bool bSetToEnd = false;
2275 			do
2276             {
2277                 --aCurrentNewPortion;
2278                 --aCurrentOldPortion;
2279                 --aCurrentOldPosition;
2280 				//set the cursor to the end of the sentence - necessary to
2281 				//resume there at the next step
2282 				if(!bSetToEnd)
2283 				{
2284 					bSetToEnd = true;
2285 					rEditView.pImpEditView->SetEditSelection( aCurrentOldPosition->Max() );
2286 				}
2287 
2288                 sal_uInt16 nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
2289 //                LanguageType eTextLanguage = GetLanguage( aCurrentOldPosition->Min() );
2290 
2291                 sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
2292                 switch(nScriptType)
2293                 {
2294                     case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
2295                     case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
2296                 }
2297                 if(aCurrentNewPortion->sText != aCurrentOldPortion->sText)
2298                 {
2299                     //change text and apply language
2300                     SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
2301                     aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
2302                     SetAttribs( *aCurrentOldPosition, aSet );
2303                     ImpInsertText( *aCurrentOldPosition, aCurrentNewPortion->sText );
2304                 }
2305                 else if(aCurrentNewPortion->eLanguage != aCurrentOldPortion->eLanguage)
2306                 {
2307                     //apply language
2308                     SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
2309                     aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
2310                     SetAttribs( *aCurrentOldPosition, aSet );
2311                 }
2312                 if(aCurrentNewPortion == rNewPortions.begin())
2313                     break;
2314             }
2315             while(aCurrentNewPortion != rNewPortions.begin());
2316         }
2317         else
2318         {
2319             DBG_ASSERT( pSpellInfo->aLastSpellContentSelections.size() > 0, "aLastSpellContentSelections should not be empty here" );
2320 
2321             //select the complete sentence
2322             SpellContentSelections::const_iterator aCurrentEndPosition = pSpellInfo->aLastSpellContentSelections.end();
2323             --aCurrentEndPosition;
2324             SpellContentSelections::const_iterator aCurrentStartPosition = pSpellInfo->aLastSpellContentSelections.begin();
2325             EditSelection aAllSentence(aCurrentStartPosition->Min(), aCurrentEndPosition->Max());
2326 
2327             //delete the sentence completely
2328             ImpDeleteSelection( aAllSentence );
2329             svx::SpellPortions::const_iterator aCurrentNewPortion = rNewPortions.begin();
2330             EditPaM aCurrentPaM = aAllSentence.Min();
2331             while(aCurrentNewPortion != rNewPortions.end())
2332             {
2333                 //set the language attribute
2334                 LanguageType eCurLanguage = GetLanguage( aCurrentPaM );
2335                 if(eCurLanguage != aCurrentNewPortion->eLanguage)
2336                 {
2337                     sal_uInt16 nScriptType = GetI18NScriptTypeOfLanguage( aCurrentNewPortion->eLanguage );
2338                     sal_uInt16 nLangWhichId = EE_CHAR_LANGUAGE;
2339                     switch(nScriptType)
2340                     {
2341                         case SCRIPTTYPE_ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
2342                         case SCRIPTTYPE_COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
2343                     }
2344                     SfxItemSet aSet( aEditDoc.GetItemPool(), nLangWhichId, nLangWhichId);
2345                     aSet.Put(SvxLanguageItem(aCurrentNewPortion->eLanguage, nLangWhichId));
2346                     SetAttribs( aCurrentPaM, aSet );
2347                 }
2348                 //insert the new string and set the cursor to the end of the inserted string
2349                 aCurrentPaM = ImpInsertText( aCurrentPaM , aCurrentNewPortion->sText );
2350                 ++aCurrentNewPortion;
2351             }
2352         }
2353         UndoActionEnd( EDITUNDO_INSERT );
2354 
2355         EditPaM aNext;
2356         if (bRecheck)
2357             aNext = pSpellInfo->aCurSentenceStart;
2358         else
2359         {
2360             // restore cursor position to the end of the modified sentence.
2361             // (This will define the continuation position for spell/grammar checking)
2362             // First: check if the sentence/para length changed
2363             sal_Int32 nDelta = rEditView.pImpEditView->GetEditSelection().Max().GetNode()->Len() - nOldLen;
2364             xub_StrLen nEndOfSentence = aOldSel.Max().GetIndex() + nDelta;
2365             aNext = EditPaM( aOldSel.Max().GetNode(), nEndOfSentence );
2366         }
2367         rEditView.pImpEditView->SetEditSelection( aNext );
2368 
2369         FormatAndUpdate();
2370         aEditDoc.SetModified(sal_True);
2371     }
2372 #endif
2373 }
2374 /*-- 08.09.2008 11:33:02---------------------------------------------------
2375 
2376   -----------------------------------------------------------------------*/
2377 void ImpEditEngine::PutSpellingToSentenceStart( EditView& rEditView )
2378 {
2379 #ifdef SVX_LIGHT
2380 #else
2381     if( pSpellInfo && pSpellInfo->aLastSpellContentSelections.size() )
2382     {
2383         rEditView.pImpEditView->SetEditSelection( pSpellInfo->aLastSpellContentSelections.begin()->Min() );
2384     }
2385 
2386 #endif
2387 }
2388 
2389 
2390 void ImpEditEngine::DoOnlineSpelling( ContentNode* pThisNodeOnly, sal_Bool bSpellAtCursorPos, sal_Bool bInteruptable )
2391 {
2392 #ifndef SVX_LIGHT
2393 	/*
2394 	 Er wird ueber alle Absaetze iteriert, nur Absaetze mit invalidierter
2395 	 WrongList werden geprueft...
2396 
2397 	 Es werden alle Woerter im invalidierten Bereich geprueft.
2398 	 Ist ein Wort falsch, aber noch nicht in der WrongList, oder umgekehrt,
2399 	 wird der Bereich des Wortes invalidiert
2400 	  (	kein Invalidate, sondern wenn nur Uebergaenge von richtig=>falsch,
2401 		einfaches Paint, bei Uebergaengen von falsch=>richtig mit VDev
2402 		ueberplaetten )
2403 	*/
2404 
2405  	if ( !xSpeller.is() )
2406 		return;
2407 
2408 	EditPaM aCursorPos;
2409 	if( pActiveView && !bSpellAtCursorPos )
2410 	{
2411 		DBG_CHKOBJ( pActiveView, EditView, 0 );
2412 		aCursorPos = pActiveView->pImpEditView->GetEditSelection().Max();
2413 	}
2414 	sal_Bool bRestartTimer = sal_False;
2415 
2416 	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
2417 	sal_uInt16 nNodes = GetEditDoc().Count();
2418 	sal_uInt16 nInvalids = 0;
2419 	Sequence< PropertyValue > aEmptySeq;
2420 	for ( sal_uInt16 n = 0; n < nNodes; n++ )
2421 	{
2422 		ContentNode* pNode = GetEditDoc().GetObject( n );
2423 		if ( pThisNodeOnly )
2424 			pNode = pThisNodeOnly;
2425 
2426 		if ( pNode->GetWrongList()->IsInvalid() )
2427 		{
2428 			WrongList* pWrongList = pNode->GetWrongList();
2429 			sal_uInt16 nInvStart = pWrongList->GetInvalidStart();
2430 			sal_uInt16 nInvEnd = pWrongList->GetInvalidEnd();
2431 
2432 			sal_uInt16 nWrongs = 0;	// Auch im Absatz mal die Kontrolle abgeben...
2433 //			sal_Bool bStop = sal_False;
2434 
2435 			sal_uInt16 nPaintFrom = 0xFFFF, nPaintTo = 0;
2436 			sal_Bool bSimpleRepaint = sal_True;
2437 
2438 			pWrongList->SetValid();
2439 
2440 			EditPaM aPaM( pNode, nInvStart );
2441 			EditSelection aSel( aPaM, aPaM );
2442 			while ( ( aSel.Max().GetNode() == pNode ) /* && !bStop */ )
2443 			{
2444 				if ( ( aSel.Min().GetIndex() > nInvEnd )
2445 						|| ( ( aSel.Max().GetNode() == pLastNode ) && ( aSel.Max().GetIndex() >= pLastNode->Len() ) ) )
2446 					break;	// Dokument- oder Ungueltigkeitsbereich-Ende
2447 
2448 				aSel = SelectWord( aSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2449 				String aWord( GetSelected( aSel ) );
2450 				// Wenn Punkt dahinter, muss dieser mit uebergeben werden !
2451 				// Falls Abkuerzung...
2452 				sal_Bool bDottAdded = sal_False;
2453 				if ( aSel.Max().GetIndex() < aSel.Max().GetNode()->Len() )
2454 				{
2455 					sal_Unicode cNext = aSel.Max().GetNode()->GetChar( aSel.Max().GetIndex() );
2456 					if ( cNext == '.' )
2457 					{
2458 						aSel.Max().GetIndex()++;
2459 						aWord += cNext;
2460 						bDottAdded = sal_True;
2461 					}
2462 				}
2463 
2464 
2465 				sal_Bool bChanged = sal_False;
2466 				if ( aWord.Len() > 0 )
2467 				{
2468 					sal_uInt16 nWStart = aSel.Min().GetIndex();
2469 					sal_uInt16 nWEnd= aSel.Max().GetIndex();
2470 					if ( !xSpeller->isValid( aWord, GetLanguage( EditPaM( aSel.Min().GetNode(), nWStart+1 ) ), aEmptySeq ) )
2471 					{
2472 						// Pruefen, ob schon richtig markiert...
2473 						nWrongs++;
2474 						// Nur bei SimpleRepaint stoppen, sonst zu oft VDev
2475 	//						if ( ( nWrongs > 8 ) && bSimpleRepaint )
2476 	//						{
2477 	//							bStop = sal_True;
2478 	// 							pWrongList->MarkInvalid( aSel.Max().GetIndex(), nInvEnd );
2479 	//						}
2480 						sal_uInt16 nXEnd = bDottAdded ? nWEnd -1 : nWEnd;
2481 						if ( !pWrongList->HasWrong( nWStart, nXEnd ) )
2482 						{
2483 							// Wort als falsch markieren...
2484 							// Aber nur, wenn nicht an Cursor-Position...
2485 							sal_Bool bCursorPos = sal_False;
2486 							if ( aCursorPos.GetNode() == pNode )
2487 							{
2488 								if ( ( nWStart <= aCursorPos.GetIndex() ) && nWEnd >= aCursorPos.GetIndex() )
2489 									bCursorPos = sal_True;
2490 							}
2491 							if ( bCursorPos )
2492 							{
2493 								// Dann weiter als ungueltig markieren...
2494 								pWrongList->GetInvalidStart() = nWStart;
2495 								pWrongList->GetInvalidEnd() = nWEnd;
2496 								bRestartTimer = sal_True;
2497 							}
2498 							else
2499 							{
2500 								// Es kann sein, dass die Wrongs in der Liste nicht
2501 								// genau ueber Woerter aufgespannt sind, weil die
2502 								// WordDelimiters beim Expandieren nicht ausgewrtet werden.
2503 								pWrongList->InsertWrong( nWStart, nXEnd, sal_True );
2504 								bChanged = sal_True;
2505 							}
2506 						}
2507 					}
2508 					else
2509 					{
2510 						// Pruefen, ob nicht als als falsch markiert....
2511 						if ( pWrongList->HasAnyWrong( nWStart, nWEnd ) )
2512 						{
2513 							pWrongList->ClearWrongs( nWStart, nWEnd, pNode );
2514 							bSimpleRepaint = sal_False;
2515 							bChanged = sal_True;
2516 						}
2517 					}
2518 					if ( bChanged  )
2519 					{
2520 						if ( nPaintFrom == 0xFFFF )
2521 							nPaintFrom = nWStart;
2522 						nPaintTo = nWEnd;
2523 					}
2524 				}
2525 
2526 				EditPaM aLastEnd( aSel.Max() );
2527 				aSel = WordRight( aSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2528 				if ( bChanged && ( aSel.Min().GetNode() == pNode ) &&
2529 						( ( aSel.Min().GetIndex()-aLastEnd.GetIndex() > 1 ) ) )
2530 				{
2531 					// Wenn zwei Worte durch mehr Zeichen als ein Blank getrennt
2532 					// sind, kann es passieren, dass beim Aufsplitten eines Wrongs
2533 					// der Start den zweiten Wortes vor dem tatsaechlich Wort liegt
2534 					pWrongList->ClearWrongs( aLastEnd.GetIndex(), aSel.Min().GetIndex(), pNode );
2535 				}
2536 			}
2537 
2538 			// Invalidieren?
2539             if ( ( nPaintFrom != 0xFFFF ) )
2540 			{
2541 				aStatus.GetStatusWord() |= EE_STAT_WRONGWORDCHANGED;
2542 				CallStatusHdl();
2543 
2544 				if ( aEditViews.Count() )
2545 				{
2546 					// Bei SimpleRepaint wuerde ein uebermalen ohne VDev reichen,
2547 					// aber dann muesste ich ueber alle Views, Intersecten,
2548 					// Clippen, ...
2549 					// Lohnt wahrscheinlich nicht.
2550 					EditPaM aStartPaM( pNode, nPaintFrom );
2551 					EditPaM aEndPaM( pNode, nPaintTo );
2552 					Rectangle aStartCursor( PaMtoEditCursor( aStartPaM ) );
2553 					Rectangle aEndCursor( PaMtoEditCursor( aEndPaM ) );
2554 					DBG_ASSERT( aInvalidRec.IsEmpty(), "InvalidRect gesetzt!" );
2555 					aInvalidRec.Left() = 0;
2556 					aInvalidRec.Right() = GetPaperSize().Width();
2557 					aInvalidRec.Top() = aStartCursor.Top();
2558 					aInvalidRec.Bottom() = aEndCursor.Bottom();
2559 					if ( pActiveView && pActiveView->HasSelection() )
2560 					{
2561 						// Dann darf nicht ueber VDev ausgegeben werden
2562 						UpdateViews( NULL );
2563 					}
2564 					else if ( bSimpleRepaint )
2565 					{
2566 						for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
2567 						{
2568 							EditView* pView = aEditViews[nView];
2569 							Rectangle aClipRec( aInvalidRec );
2570 							aClipRec.Intersection( pView->GetVisArea() );
2571 							if ( !aClipRec.IsEmpty() )
2572 							{
2573 								// in Fensterkoordinaten umwandeln....
2574 								aClipRec.SetPos( pView->pImpEditView->GetWindowPos( aClipRec.TopLeft() ) );
2575 								// Wenn Selektion, dann VDev...
2576 								Paint( pView->pImpEditView, aClipRec, pView->HasSelection() );
2577 							}
2578 						}
2579 					}
2580 					else
2581 					{
2582 						UpdateViews( pActiveView );
2583 					}
2584 					aInvalidRec = Rectangle();
2585 				}
2586 			}
2587 			// Nach zwei korrigierten Nodes die Kontrolle abgeben...
2588 			nInvalids++;
2589 			if ( bInteruptable && ( nInvalids >= 2 ) )
2590 			{
2591 				bRestartTimer = sal_True;
2592 				break;
2593 			}
2594 		}
2595 
2596 		if ( pThisNodeOnly )
2597 			break;
2598 	}
2599 	if ( bRestartTimer )
2600 		aOnlineSpellTimer.Start();
2601 #endif // !SVX_LIGHT
2602 }
2603 
2604 
2605 EESpellState ImpEditEngine::HasSpellErrors()
2606 {
2607 	DBG_ASSERT( xSpeller.is(), "Kein Speller gesetzt!" );
2608 
2609 #ifndef SVX_LIGHT
2610 	ContentNode* pLastNode = aEditDoc.SaveGetObject( aEditDoc.Count() - 1 );
2611 	EditSelection aCurSel( aEditDoc.GetStartPaM() );
2612 
2613 	String aWord;
2614 	Reference< XSpellAlternatives > xSpellAlt;
2615 	Sequence< PropertyValue > aEmptySeq;
2616 	while ( !xSpellAlt.is() )
2617 	{
2618 		if ( ( aCurSel.Max().GetNode() == pLastNode ) &&
2619 			 ( aCurSel.Max().GetIndex() >= pLastNode->Len() ) )
2620 		{
2621 			return EE_SPELL_OK;
2622 		}
2623 
2624 		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2625 		aWord = GetSelected( aCurSel );
2626 		if ( aWord.Len() > 0 )
2627 		{
2628 			LanguageType eLang = GetLanguage( aCurSel.Max() );
2629 			SvxSpellWrapper::CheckSpellLang( xSpeller, eLang );
2630 			xSpellAlt = xSpeller->spell( aWord, eLang, aEmptySeq );
2631 		}
2632 		aCurSel = WordRight( aCurSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2633 	}
2634 #endif
2635 
2636 	return EE_SPELL_ERRORFOUND;
2637 }
2638 
2639 EESpellState ImpEditEngine::StartThesaurus( EditView* pEditView )
2640 {
2641 #ifndef SVX_LIGHT
2642 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
2643 	if ( !aCurSel.HasRange() )
2644 		aCurSel = SelectWord( aCurSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2645 	String aWord( GetSelected( aCurSel ) );
2646 
2647 	Reference< XThesaurus > xThes( SvxGetThesaurus() );
2648 	if (!xThes.is())
2649 		return EE_SPELL_ERRORFOUND;
2650 
2651 	EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
2652 	AbstractThesaurusDialog* pDlg = pFact->CreateThesaurusDialog( pEditView->GetWindow(), xThes, aWord, GetLanguage( aCurSel.Max() ) );
2653 	if ( pDlg->Execute() == RET_OK )
2654 	{
2655 		// Wort ersetzen...
2656 		pEditView->pImpEditView->DrawSelection();
2657 		pEditView->pImpEditView->SetEditSelection( aCurSel );
2658 		pEditView->pImpEditView->DrawSelection();
2659 		pEditView->InsertText( pDlg->GetWord() );
2660 		pEditView->ShowCursor( sal_True, sal_False );
2661 	}
2662 
2663 	delete pDlg;
2664 	return EE_SPELL_OK;
2665 #else
2666 	return EE_SPELL_NOSPELLER;
2667 #endif
2668 }
2669 
2670 sal_uInt16 ImpEditEngine::StartSearchAndReplace( EditView* pEditView, const SvxSearchItem& rSearchItem )
2671 {
2672 	sal_uInt16 nFound = 0;
2673 
2674 #ifndef SVX_LIGHT
2675 	EditSelection aCurSel( pEditView->pImpEditView->GetEditSelection() );
2676 
2677 	// FIND_ALL ohne Mehrfachselektion nicht moeglich.
2678 	if ( ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND ) ||
2679 		 ( rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND_ALL ) )
2680 	{
2681 		if ( Search( rSearchItem, pEditView ) )
2682 			nFound++;
2683 	}
2684 	else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE )
2685 	{
2686 		// Das Wort ist selektiert, wenn der Anwender die Selektion
2687 		// nicht zwischendurch manipuliert:
2688 		if ( aCurSel.HasRange() )
2689 		{
2690 			pEditView->InsertText( rSearchItem.GetReplaceString() );
2691 			nFound = 1;
2692 		}
2693 		else
2694 			if( Search( rSearchItem, pEditView ) )
2695 				nFound = 1;
2696 	}
2697 	else if ( rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL )
2698 	{
2699 		// Der Writer ersetzt alle, vorn Anfang bis Ende...
2700 		SvxSearchItem aTmpItem( rSearchItem );
2701 		aTmpItem.SetBackward( sal_False );
2702 
2703 		pEditView->pImpEditView->DrawSelection();
2704 
2705         aCurSel.Adjust( aEditDoc );
2706 		EditPaM aStartPaM = aTmpItem.GetSelection() ? aCurSel.Min() : aEditDoc.GetStartPaM();
2707 		EditSelection aFoundSel( aCurSel.Max() );
2708 		sal_Bool bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
2709 		if ( bFound )
2710 			UndoActionStart( EDITUNDO_REPLACEALL );
2711 		while ( bFound )
2712 		{
2713 			nFound++;
2714 			aStartPaM = ImpInsertText( aFoundSel, rSearchItem.GetReplaceString() );
2715 			bFound = ImpSearch( aTmpItem, aCurSel, aStartPaM, aFoundSel );
2716 		}
2717 		if ( nFound )
2718 		{
2719 			EditPaM aNewPaM( aFoundSel.Max() );
2720 			if ( aNewPaM.GetIndex() > aNewPaM.GetNode()->Len() )
2721 				aNewPaM.GetIndex() =  aNewPaM.GetNode()->Len();
2722 			pEditView->pImpEditView->SetEditSelection( aNewPaM );
2723 			FormatAndUpdate( pEditView );
2724 			UndoActionEnd( EDITUNDO_REPLACEALL );
2725 		}
2726 		else
2727 		{
2728 			pEditView->pImpEditView->DrawSelection();
2729 			pEditView->ShowCursor( sal_True, sal_False );
2730 		}
2731 	}
2732 #endif // !SVX_LIGHT
2733 	return nFound;
2734 }
2735 
2736 sal_Bool ImpEditEngine::Search( const SvxSearchItem& rSearchItem, EditView* pEditView )
2737 {
2738 	EditSelection aSel( pEditView->pImpEditView->GetEditSelection() );
2739 	aSel.Adjust( aEditDoc );
2740 	EditPaM aStartPaM( aSel.Max() );
2741 	if ( rSearchItem.GetSelection() && !rSearchItem.GetBackward() )
2742 		aStartPaM = aSel.Min();
2743 
2744 	EditSelection aFoundSel;
2745 	sal_Bool bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
2746 	if ( bFound && ( aFoundSel == aSel ) )	// Bei Rueckwaetssuche
2747 	{
2748 		aStartPaM = aSel.Min();
2749 		bFound = ImpSearch( rSearchItem, aSel, aStartPaM, aFoundSel );
2750 	}
2751 
2752 	pEditView->pImpEditView->DrawSelection();
2753 	if ( bFound )
2754 	{
2755 		// Erstmal das Min einstellen, damit das ganze Wort in den sichtbaren Bereich kommt.
2756 		pEditView->pImpEditView->SetEditSelection( aFoundSel.Min() );
2757 		pEditView->ShowCursor( sal_True, sal_False );
2758 		pEditView->pImpEditView->SetEditSelection( aFoundSel );
2759 	}
2760 	else
2761 		pEditView->pImpEditView->SetEditSelection( aSel.Max() );
2762 
2763 	pEditView->pImpEditView->DrawSelection();
2764 	pEditView->ShowCursor( sal_True, sal_False );
2765 	return bFound;
2766 }
2767 
2768 sal_Bool ImpEditEngine::ImpSearch( const SvxSearchItem& rSearchItem,
2769 	const EditSelection& rSearchSelection, const EditPaM& rStartPos, EditSelection& rFoundSel )
2770 {
2771 #ifndef SVX_LIGHT
2772 	util::SearchOptions aSearchOptions( rSearchItem.GetSearchOptions() );
2773 	aSearchOptions.Locale = GetLocale( rStartPos );
2774 
2775 	sal_Bool bBack = rSearchItem.GetBackward();
2776 	sal_Bool bSearchInSelection = rSearchItem.GetSelection();
2777 	sal_uInt16 nStartNode = aEditDoc.GetPos( rStartPos.GetNode() );
2778 	sal_uInt16 nEndNode;
2779 	if ( bSearchInSelection )
2780 	{
2781 		nEndNode = aEditDoc.GetPos( bBack ? rSearchSelection.Min().GetNode() : rSearchSelection.Max().GetNode() );
2782 	}
2783 	else
2784 	{
2785 		nEndNode = bBack ? 0 : aEditDoc.Count()-1;
2786 	}
2787 
2788 	utl::TextSearch aSearcher( aSearchOptions );
2789 
2790 	// ueber die Absaetze iterieren...
2791 	for ( sal_uInt16 nNode = nStartNode;
2792 			bBack ? ( nNode >= nEndNode ) : ( nNode <= nEndNode) ;
2793 			bBack ? nNode-- : nNode++ )
2794 	{
2795 		// Bei rueckwaertsuche, wenn nEndNode = 0:
2796 		if ( nNode >= 0xFFFF )
2797 			return sal_False;
2798 
2799 		ContentNode* pNode = aEditDoc.GetObject( nNode );
2800 
2801 		sal_uInt16 nStartPos = 0;
2802 		sal_uInt16 nEndPos = pNode->Len();
2803 		if ( nNode == nStartNode )
2804 		{
2805 			if ( bBack )
2806 				nEndPos = rStartPos.GetIndex();
2807 			else
2808 				nStartPos = rStartPos.GetIndex();
2809 		}
2810 		if ( ( nNode == nEndNode ) && bSearchInSelection )
2811 		{
2812 			if ( bBack )
2813 				nStartPos = rSearchSelection.Min().GetIndex();
2814 			else
2815 				nEndPos = rSearchSelection.Max().GetIndex();
2816 		}
2817 
2818 		// Suchen...
2819 		XubString aParaStr( GetEditDoc().GetParaAsString( pNode ) );
2820 		bool bFound = false;
2821 		if ( bBack )
2822 		{
2823 			Swapsal_uIt16s( nStartPos, nEndPos );
2824 			bFound = aSearcher.SearchBkwrd( aParaStr, &nStartPos, &nEndPos);
2825 		}
2826 		else
2827 			bFound = aSearcher.SearchFrwrd( aParaStr, &nStartPos, &nEndPos);
2828 
2829 		if ( bFound )
2830 		{
2831 			rFoundSel.Min().SetNode( pNode );
2832 			rFoundSel.Min().SetIndex( nStartPos );
2833 			rFoundSel.Max().SetNode( pNode );
2834 			rFoundSel.Max().SetIndex( nEndPos );
2835 			return sal_True;
2836 		}
2837 	}
2838 #endif // !SVX_LIGHT
2839 	return sal_False;
2840 }
2841 
2842 sal_Bool ImpEditEngine::HasText( const SvxSearchItem& rSearchItem )
2843 {
2844 #ifndef SVX_LIGHT
2845 	SvxSearchItem aTmpItem( rSearchItem );
2846 	aTmpItem.SetBackward( sal_False );
2847 	aTmpItem.SetSelection( sal_False );
2848 
2849 	EditPaM aStartPaM( aEditDoc.GetStartPaM() );
2850 	EditSelection aDummySel( aStartPaM );
2851 	EditSelection aFoundSel;
2852 	return ImpSearch( aTmpItem, aDummySel, aStartPaM, aFoundSel );
2853 #else
2854 	return sal_False;
2855 #endif
2856 }
2857 
2858 void ImpEditEngine::SetAutoCompleteText( const String& rStr, sal_Bool bClearTipWindow )
2859 {
2860 #ifndef SVX_LIGHT
2861 	aAutoCompleteText = rStr;
2862 	if ( bClearTipWindow && pActiveView )
2863 		Help::ShowQuickHelp( pActiveView->GetWindow(), Rectangle(), String(), 0 );
2864 #endif // !SVX_LIGHT
2865 }
2866 
2867 
2868 struct TransliterationChgData
2869 {
2870     sal_uInt16                      nStart;
2871     xub_StrLen                  nLen;
2872     EditSelection               aSelection;
2873     String                      aNewText;
2874     uno::Sequence< sal_Int32 >  aOffsets;
2875 };
2876 
2877 
2878 EditSelection ImpEditEngine::TransliterateText( const EditSelection& rSelection, sal_Int32 nTransliterationMode )
2879 {
2880     uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
2881     if (!_xBI.is())
2882         return rSelection;
2883 
2884 	EditSelection aSel( rSelection );
2885 	aSel.Adjust( aEditDoc );
2886 
2887 	if ( !aSel.HasRange() )
2888 		aSel = SelectWord( aSel );
2889 
2890     EditSelection aNewSel( aSel );
2891 
2892 	const sal_uInt16 nStartNode = aEditDoc.GetPos( aSel.Min().GetNode() );
2893 	const sal_uInt16 nEndNode = aEditDoc.GetPos( aSel.Max().GetNode() );
2894 
2895 	sal_Bool bChanges = sal_False;
2896     sal_Bool bLenChanged = sal_False;
2897 	EditUndoTransliteration* pUndo = NULL;
2898 
2899 	utl::TransliterationWrapper aTranslitarationWrapper( ::comphelper::getProcessServiceFactory(), nTransliterationMode );
2900 	sal_Bool bConsiderLanguage = aTranslitarationWrapper.needLanguageForTheMode();
2901 
2902 	for ( sal_uInt16 nNode = nStartNode; nNode <= nEndNode; nNode++	)
2903 	{
2904 		ContentNode* pNode = aEditDoc.GetObject( nNode );
2905 		xub_StrLen nStartPos = 0;
2906 		xub_StrLen nEndPos = pNode->Len();
2907 		if ( nNode == nStartNode )
2908 			nStartPos = aSel.Min().GetIndex();
2909 		if ( nNode == nEndNode ) // kann auch == nStart sein!
2910 			nEndPos = aSel.Max().GetIndex();
2911 
2912 		sal_uInt16 nCurrentStart = nStartPos;
2913 		sal_uInt16 nCurrentEnd = nEndPos;
2914 		sal_uInt16 nLanguage = LANGUAGE_SYSTEM;
2915 
2916         // since we don't use Hiragana/Katakana or half-width/full-width transliterations here
2917         // it is fine to use ANYWORD_IGNOREWHITESPACES. (ANY_WORD btw is broken and will
2918         // occasionaly miss words in consecutive sentences). Also with ANYWORD_IGNOREWHITESPACES
2919         // text like 'just-in-time' will be converted to 'Just-In-Time' which seems to be the
2920         // proper thing to do.
2921         const sal_Int16 nWordType = i18n::WordType::ANYWORD_IGNOREWHITESPACES;
2922 
2923         //! In order to have less trouble with changing text size, e.g. because
2924         //! of ligatures or � (German small sz) being resolved, we need to process
2925         //! the text replacements from end to start.
2926         //! This way the offsets for the yet to be changed words will be
2927         //! left unchanged by the already replaced text.
2928         //! For this we temporarily save the changes to be done in this vector
2929         std::vector< TransliterationChgData >   aChanges;
2930         TransliterationChgData                  aChgData;
2931 
2932         if (nTransliterationMode == i18n::TransliterationModulesExtra::TITLE_CASE)
2933         {
2934             // for 'capitalize every word' we need to iterate over each word
2935 
2936             i18n::Boundary aSttBndry;
2937             i18n::Boundary aEndBndry;
2938             aSttBndry = _xBI->getWordBoundary(
2939                         *pNode, nStartPos,
2940                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nStartPos + 1 ) ) ),
2941                         nWordType, sal_True /*prefer forward direction*/);
2942             aEndBndry = _xBI->getWordBoundary(
2943                         *pNode, nEndPos,
2944                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nEndPos + 1 ) ) ),
2945                         nWordType, sal_False /*prefer backward direction*/);
2946 
2947             // prevent backtracking to the previous word if selection is at word boundary
2948             if (aSttBndry.endPos <= nStartPos)
2949             {
2950                 aSttBndry = _xBI->nextWord(
2951                         *pNode, aSttBndry.endPos,
2952                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aSttBndry.endPos + 1 ) ) ),
2953                         nWordType);
2954             }
2955             // prevent advancing to the next word if selection is at word boundary
2956             if (aEndBndry.startPos >= nEndPos)
2957             {
2958                 aEndBndry = _xBI->previousWord(
2959                         *pNode, aEndBndry.startPos,
2960                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aEndBndry.startPos + 1 ) ) ),
2961                         nWordType);
2962             }
2963 
2964             i18n::Boundary aCurWordBndry( aSttBndry );
2965             while (aCurWordBndry.startPos <= aEndBndry.startPos)
2966             {
2967                 nCurrentStart = (xub_StrLen)aCurWordBndry.startPos;
2968                 nCurrentEnd   = (xub_StrLen)aCurWordBndry.endPos;
2969                 sal_Int32 nLen = nCurrentEnd - nCurrentStart;
2970                 DBG_ASSERT( nLen > 0, "invalid word length of 0" );
2971 #if OSL_DEBUG_LEVEL > 1
2972                 String aText( pNode->Copy( nCurrentStart, nLen ) );
2973 #endif
2974 
2975 	            Sequence< sal_Int32 > aOffsets;
2976                 String aNewText( aTranslitarationWrapper.transliterate( *pNode,
2977                         GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ),
2978                         nCurrentStart, nLen, &aOffsets ));
2979 
2980                 if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
2981                 {
2982                     aChgData.nStart     = nCurrentStart;
2983                     aChgData.nLen       = nLen;
2984                     aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
2985                     aChgData.aNewText   = aNewText;
2986                     aChgData.aOffsets   = aOffsets;
2987                     aChanges.push_back( aChgData );
2988                 }
2989 #if OSL_DEBUG_LEVEL > 1
2990                 String aSelTxt ( GetSelected( aChgData.aSelection ) );
2991                 (void) aSelTxt;
2992 #endif
2993 
2994                 aCurWordBndry = _xBI->nextWord( *pNode, nCurrentEnd,
2995                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
2996                         nWordType);
2997             }
2998             DBG_ASSERT( nCurrentEnd >= aEndBndry.endPos, "failed to reach end of transliteration" );
2999         }
3000         else if (nTransliterationMode == i18n::TransliterationModulesExtra::SENTENCE_CASE)
3001         {
3002             // for 'sentence case' we need to iterate sentence by sentence
3003 
3004             sal_Int32 nLastStart = _xBI->beginOfSentence(
3005                     *pNode, nEndPos,
3006                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nEndPos + 1 ) ) ) );
3007             sal_Int32 nLastEnd = _xBI->endOfSentence(
3008                     *pNode, nLastStart,
3009                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nLastStart + 1 ) ) ) );
3010 
3011             // extend nCurrentStart, nCurrentEnd to the current sentence boundaries
3012             nCurrentStart = _xBI->beginOfSentence(
3013                     *pNode, nStartPos,
3014                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nStartPos + 1 ) ) ) );
3015             nCurrentEnd = _xBI->endOfSentence(
3016                     *pNode, nCurrentStart,
3017                     SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
3018 
3019             // prevent backtracking to the previous sentence if selection starts at end of a sentence
3020             if (nCurrentEnd <= nStartPos)
3021             {
3022                 // now nCurrentStart is probably located on a non-letter word. (unless we
3023                 // are in Asian text with no spaces...)
3024                 // Thus to get the real sentence start we should locate the next real word,
3025                 // that is one found by DICTIONARY_WORD
3026                 i18n::Boundary aBndry = _xBI->nextWord( *pNode, nCurrentEnd,
3027                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
3028                         i18n::WordType::DICTIONARY_WORD);
3029 
3030                 // now get new current sentence boundaries
3031                 nCurrentStart = _xBI->beginOfSentence(
3032                         *pNode, aBndry.startPos,
3033                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aBndry.startPos + 1 ) ) ) );
3034                 nCurrentEnd = _xBI->endOfSentence(
3035                         *pNode, nCurrentStart,
3036                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
3037             }
3038             // prevent advancing to the next sentence if selection ends at start of a sentence
3039             if (nLastStart >= nEndPos)
3040             {
3041                 // now nCurrentStart is probably located on a non-letter word. (unless we
3042                 // are in Asian text with no spaces...)
3043                 // Thus to get the real sentence start we should locate the previous real word,
3044                 // that is one found by DICTIONARY_WORD
3045                 i18n::Boundary aBndry = _xBI->previousWord( *pNode, nLastStart,
3046                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nLastStart + 1 ) ) ),
3047                         i18n::WordType::DICTIONARY_WORD);
3048                 nLastEnd = _xBI->endOfSentence(
3049                         *pNode, aBndry.startPos,
3050                         SvxCreateLocale( GetLanguage( EditPaM( pNode, aBndry.startPos + 1 ) ) ) );
3051                 if (nCurrentEnd > nLastEnd)
3052                     nCurrentEnd = nLastEnd;
3053             }
3054 
3055             while (nCurrentStart < nLastEnd)
3056             {
3057                 sal_Int32 nLen = nCurrentEnd - nCurrentStart;
3058                 DBG_ASSERT( nLen > 0, "invalid word length of 0" );
3059 #if OSL_DEBUG_LEVEL > 1
3060                 String aText( pNode->Copy( nCurrentStart, nLen ) );
3061 #endif
3062 
3063 	            Sequence< sal_Int32 > aOffsets;
3064                 String aNewText( aTranslitarationWrapper.transliterate( *pNode,
3065                         GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ),
3066                         nCurrentStart, nLen, &aOffsets ));
3067 
3068                 if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
3069                 {
3070                     aChgData.nStart     = nCurrentStart;
3071                     aChgData.nLen       = nLen;
3072                     aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
3073                     aChgData.aNewText   = aNewText;
3074                     aChgData.aOffsets   = aOffsets;
3075                     aChanges.push_back( aChgData );
3076                 }
3077 
3078                 i18n::Boundary aFirstWordBndry;
3079                 aFirstWordBndry = _xBI->nextWord(
3080                         *pNode, nCurrentEnd,
3081                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentEnd + 1 ) ) ),
3082                         nWordType);
3083                 nCurrentStart = aFirstWordBndry.startPos;
3084                 nCurrentEnd = _xBI->endOfSentence(
3085                         *pNode, nCurrentStart,
3086                         SvxCreateLocale( GetLanguage( EditPaM( pNode, nCurrentStart + 1 ) ) ) );
3087             }
3088             DBG_ASSERT( nCurrentEnd >= nLastEnd, "failed to reach end of transliteration" );
3089         }
3090         else
3091         {
3092             do
3093             {
3094 		        if ( bConsiderLanguage )
3095 		        {
3096 			        nLanguage = GetLanguage( EditPaM( pNode, nCurrentStart+1 ), &nCurrentEnd );
3097 			        if ( nCurrentEnd > nEndPos )
3098 				        nCurrentEnd = nEndPos;
3099 		        }
3100 
3101 		        xub_StrLen nLen = nCurrentEnd - nCurrentStart;
3102 
3103 		        Sequence< sal_Int32 > aOffsets;
3104 		        String aNewText( aTranslitarationWrapper.transliterate( *pNode, nLanguage, nCurrentStart, nLen, &aOffsets ) );
3105 
3106                 if (!pNode->Equals( aNewText, nCurrentStart, nLen ))
3107                 {
3108                     aChgData.nStart     = nCurrentStart;
3109                     aChgData.nLen       = nLen;
3110                     aChgData.aSelection = EditSelection( EditPaM( pNode, nCurrentStart ), EditPaM( pNode, nCurrentEnd ) );
3111                     aChgData.aNewText   = aNewText;
3112                     aChgData.aOffsets   = aOffsets;
3113                     aChanges.push_back( aChgData );
3114                 }
3115 
3116 		        nCurrentStart = nCurrentEnd;
3117 		    } while( nCurrentEnd < nEndPos );
3118         }
3119 
3120         if (aChanges.size() > 0)
3121         {
3122 #ifndef SVX_LIGHT
3123             // Create a single UndoAction on Demand for all the changes ...
3124             if ( !pUndo && IsUndoEnabled() && !IsInUndo() )
3125             {
3126                 // adjust selection to include all changes
3127                 for (size_t i = 0; i < aChanges.size(); ++i)
3128                 {
3129                     const EditSelection &rSel = aChanges[i].aSelection;
3130                     if (aSel.Min().GetNode() == rSel.Min().GetNode() &&
3131                         aSel.Min().GetIndex() > rSel.Min().GetIndex())
3132                         aSel.Min().SetIndex( rSel.Min().GetIndex() );
3133                     if (aSel.Max().GetNode() == rSel.Max().GetNode() &&
3134                         aSel.Max().GetIndex() < rSel.Max().GetIndex())
3135                         aSel.Max().SetIndex( rSel.Max().GetIndex() );
3136                 }
3137                 aNewSel = aSel;
3138 
3139                 ESelection aESel( CreateESel( aSel ) );
3140                 pUndo = new EditUndoTransliteration( this, aESel, nTransliterationMode );
3141 
3142                 const bool bSingleNode = aSel.Min().GetNode()== aSel.Max().GetNode();
3143                 const bool bHasAttribs = aSel.Min().GetNode()->GetCharAttribs().HasAttrib( aSel.Min().GetIndex(), aSel.Max().GetIndex() );
3144                 if (bSingleNode && !bHasAttribs)
3145 	                pUndo->SetText( aSel.Min().GetNode()->Copy( aSel.Min().GetIndex(), aSel.Max().GetIndex()-aSel.Min().GetIndex() ) );
3146                 else
3147 	                pUndo->SetText( CreateBinTextObject( aSel, NULL ) );
3148             }
3149 #endif
3150 
3151             // now apply the changes from end to start to leave the offsets of the
3152             // yet unchanged text parts remain the same.
3153             for (size_t i = 0; i < aChanges.size(); ++i)
3154             {
3155                 const TransliterationChgData &rData = aChanges[ aChanges.size() - 1 - i ];
3156 
3157 			    bChanges = sal_True;
3158                 if (rData.nLen != rData.aNewText.Len())
3159                     bLenChanged = sal_True;
3160 
3161                 // Change text without loosing the attributes
3162                 sal_uInt16 nDiffs = ReplaceTextOnly( rData.aSelection.Min().GetNode(),
3163                         rData.nStart, rData.nLen, rData.aNewText, rData.aOffsets );
3164 
3165                 // adjust selection in end node to possibly changed size
3166                 if (aSel.Max().GetNode() == rData.aSelection.Max().GetNode())
3167                     aNewSel.Max().GetIndex() = aNewSel.Max().GetIndex() + nDiffs;
3168 
3169                 sal_uInt16 nSelNode = aEditDoc.GetPos( rData.aSelection.Min().GetNode() );
3170                 ParaPortion* pParaPortion = GetParaPortions()[nSelNode];
3171 			    pParaPortion->MarkSelectionInvalid( rData.nStart,
3172                         std::max< sal_uInt16 >( rData.nStart + rData.nLen,
3173                                             rData.nStart + rData.aNewText.Len() ) );
3174 		    }
3175         } // if (aChanges.size() > 0)
3176     }
3177 
3178 #ifndef SVX_LIGHT
3179 	if ( pUndo )
3180 	{
3181 		ESelection aESel( CreateESel( aNewSel ) );
3182 		pUndo->SetNewSelection( aESel );
3183 		InsertUndo( pUndo );
3184 	}
3185 #endif
3186 
3187 	if ( bChanges )
3188     {
3189         TextModified();
3190         SetModifyFlag( sal_True );
3191         if ( bLenChanged )
3192             UpdateSelections();
3193 		FormatAndUpdate();
3194     }
3195 
3196     return aNewSel;
3197 }
3198 
3199 
3200 short ImpEditEngine::ReplaceTextOnly(
3201     ContentNode* pNode,
3202     sal_uInt16 nCurrentStart, xub_StrLen nLen,
3203 	const String& rNewText,
3204     const uno::Sequence< sal_Int32 >& rOffsets )
3205 {
3206     (void)  nLen;
3207 
3208     // Change text without loosing the attributes
3209     sal_uInt16 nCharsAfterTransliteration =
3210         sal::static_int_cast< sal_uInt16 >(rOffsets.getLength());
3211     const sal_Int32* pOffsets = rOffsets.getConstArray();
3212     short nDiffs = 0;
3213     for ( sal_uInt16 n = 0; n < nCharsAfterTransliteration; n++ )
3214     {
3215         sal_uInt16 nCurrentPos = nCurrentStart+n;
3216         sal_Int32 nDiff = (nCurrentPos-nDiffs) - pOffsets[n];
3217 
3218         if ( !nDiff )
3219         {
3220             DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
3221             pNode->SetChar( nCurrentPos, rNewText.GetChar(n) );
3222         }
3223         else if ( nDiff < 0 )
3224         {
3225             // Replace first char, delete the rest...
3226             DBG_ASSERT( nCurrentPos < pNode->Len(), "TransliterateText - String smaller than expected!" );
3227             pNode->SetChar( nCurrentPos, rNewText.GetChar(n) );
3228 
3229             DBG_ASSERT( (nCurrentPos+1) < pNode->Len(), "TransliterateText - String smaller than expected!" );
3230             GetEditDoc().RemoveChars( EditPaM( pNode, nCurrentPos+1 ), sal::static_int_cast< sal_uInt16 >(-nDiff) );
3231         }
3232         else
3233         {
3234             DBG_ASSERT( nDiff == 1, "TransliterateText - Diff other than expected! But should work..." );
3235             GetEditDoc().InsertText( EditPaM( pNode, nCurrentPos ), rNewText.GetChar(n) );
3236 
3237         }
3238         nDiffs = sal::static_int_cast< short >(nDiffs + nDiff);
3239     }
3240 
3241     return nDiffs;
3242 }
3243 
3244 
3245 void ImpEditEngine::SetAsianCompressionMode( sal_uInt16 n )
3246 {
3247     if ( n != nAsianCompressionMode )
3248     {
3249         nAsianCompressionMode = n;
3250         if ( ImplHasText() )
3251         {
3252             FormatFullDoc();
3253             UpdateViews();
3254         }
3255     }
3256 }
3257 
3258 void ImpEditEngine::SetKernAsianPunctuation( sal_Bool b )
3259 {
3260     if ( b != bKernAsianPunctuation )
3261     {
3262         bKernAsianPunctuation = b;
3263         if ( ImplHasText() )
3264         {
3265             FormatFullDoc();
3266             UpdateViews();
3267         }
3268     }
3269 }
3270 
3271 void ImpEditEngine::SetAddExtLeading( sal_Bool bExtLeading )
3272 {
3273     if ( IsAddExtLeading() != bExtLeading )
3274     {
3275         bAddExtLeading = bExtLeading;
3276         if ( ImplHasText() )
3277         {
3278             FormatFullDoc();
3279             UpdateViews();
3280         }
3281     }
3282 };
3283 
3284 
3285 
3286 sal_Bool ImpEditEngine::ImplHasText() const
3287 {
3288     return ( ( GetEditDoc().Count() > 1 ) || GetEditDoc().GetObject(0)->Len() );
3289 }
3290 
3291 long ImpEditEngine::LogicToTwips( long n )
3292 {
3293 	Size aSz( n, 0 );
3294 	MapMode aTwipsMode( MAP_TWIP );
3295 	aSz = pRefDev->LogicToLogic( aSz, NULL, &aTwipsMode );
3296 	return aSz.Width();
3297 }
3298 
3299 
3300