Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include <tools/stream.hxx>
30 : :
31 : : #include <vcl/texteng.hxx>
32 : : #include <vcl/textview.hxx>
33 : : #include <textdoc.hxx>
34 : : #include <textdat2.hxx>
35 : : #include <textundo.hxx>
36 : : #include <textund2.hxx>
37 : : #include <svl/ctloptions.hxx>
38 : : #include <vcl/window.hxx>
39 : :
40 : : #include <vcl/edit.hxx>
41 : : #include <com/sun/star/lang/XMultiServiceFactory.hpp>
42 : : #include <com/sun/star/beans/PropertyValues.hpp>
43 : :
44 : : #include <com/sun/star/i18n/XBreakIterator.hpp>
45 : :
46 : : #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
47 : :
48 : : #include <com/sun/star/i18n/WordType.hpp>
49 : :
50 : : #include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp>
51 : : #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
52 : : #include <com/sun/star/i18n/ScriptType.hpp>
53 : :
54 : : #include <comphelper/processfactory.hxx>
55 : :
56 : : #include <unotools/localedatawrapper.hxx>
57 : : #include <vcl/unohelp.hxx>
58 : :
59 : : #include <vcl/svapp.hxx>
60 : : #include <vcl/metric.hxx>
61 : :
62 : : #include <unicode/ubidi.h>
63 : :
64 : : #include <set>
65 : : #include <vector>
66 : : #include <boost/foreach.hpp>
67 : :
68 : : using namespace ::com::sun::star;
69 : : using namespace ::com::sun::star::uno;
70 : : using namespace ::rtl;
71 : :
72 : :
73 : : // -------------------------------------------------------------------------
74 : : // (-) class TextEngine
75 : : // -------------------------------------------------------------------------
76 [ + - ][ + - ]: 90 : TextEngine::TextEngine()
77 : : {
78 : 90 : mpDoc = 0;
79 : 90 : mpTEParaPortions = 0;
80 : :
81 [ + - ][ + - ]: 90 : mpViews = new TextViews;
82 : 90 : mpActiveView = NULL;
83 : :
84 : 90 : mbIsFormatting = sal_False;
85 : 90 : mbFormatted = sal_False;
86 : 90 : mbUpdate = sal_True;
87 : 90 : mbModified = sal_False;
88 : 90 : mbUndoEnabled = sal_False;
89 : 90 : mbIsInUndo = sal_False;
90 : 90 : mbDowning = sal_False;
91 : 90 : mbRightToLeft = sal_False;
92 : 90 : mbHasMultiLineParas = sal_False;
93 : :
94 : 90 : meAlign = TXTALIGN_LEFT;
95 : :
96 : 90 : mnMaxTextWidth = 0;
97 : 90 : mnMaxTextLen = 0;
98 : 90 : mnCurTextWidth = 0xFFFFFFFF;
99 : 90 : mnCurTextHeight = 0;
100 : :
101 : 90 : mpUndoManager = NULL;
102 : 90 : mpIMEInfos = NULL;
103 : 90 : mpLocaleDataWrapper = NULL;
104 : :
105 [ + - ][ + - ]: 90 : mpIdleFormatter = new IdleFormatter;
106 [ + - ]: 90 : mpIdleFormatter->SetTimeoutHdl( LINK( this, TextEngine, IdleFormatHdl ) );
107 : :
108 [ + - ][ + - ]: 90 : mpRefDev = new VirtualDevice;
109 : :
110 [ + - ]: 90 : ImpInitLayoutMode( mpRefDev );
111 : :
112 [ + - ]: 90 : ImpInitDoc();
113 : :
114 : 90 : maTextColor = COL_BLACK;
115 [ + - ]: 90 : Font aFont;
116 [ + - ]: 90 : aFont.SetTransparent( sal_False );
117 [ + - ]: 90 : Color aFillColor( aFont.GetFillColor() );
118 [ + - ]: 90 : aFillColor.SetTransparency( 0 );
119 [ + - ]: 90 : aFont.SetFillColor( aFillColor );
120 [ + - ][ + - ]: 90 : SetFont( aFont );
121 : 90 : }
122 : :
123 [ + - ]: 90 : TextEngine::~TextEngine()
124 : : {
125 : 90 : mbDowning = sal_True;
126 : :
127 [ + - ][ + - ]: 90 : delete mpIdleFormatter;
128 [ + - ][ + - ]: 90 : delete mpDoc;
129 [ + - ][ + - ]: 90 : delete mpTEParaPortions;
130 [ + - ]: 90 : delete mpViews; // nur die Liste, nicht die Vies
131 [ + - ][ + - ]: 90 : delete mpRefDev;
132 [ - + ][ # # ]: 90 : delete mpUndoManager;
133 [ - + ][ # # ]: 90 : delete mpIMEInfos;
134 [ - + ][ # # ]: 90 : delete mpLocaleDataWrapper;
135 [ - + ]: 90 : }
136 : :
137 : 90 : void TextEngine::InsertView( TextView* pTextView )
138 : : {
139 : 90 : mpViews->push_back( pTextView );
140 [ + - ]: 90 : pTextView->SetSelection( TextSelection() );
141 : :
142 [ + - ]: 90 : if ( !GetActiveView() )
143 : 90 : SetActiveView( pTextView );
144 : 90 : }
145 : :
146 : 0 : void TextEngine::RemoveView( TextView* pTextView )
147 : : {
148 [ # # ]: 0 : TextViews::iterator it = std::find( mpViews->begin(), mpViews->end(), pTextView );
149 [ # # ][ # # ]: 0 : if( it != mpViews->end() )
150 : : {
151 [ # # ]: 0 : pTextView->HideCursor();
152 [ # # ]: 0 : mpViews->erase( it );
153 [ # # ][ # # ]: 0 : if ( pTextView == GetActiveView() )
154 [ # # ]: 0 : SetActiveView( 0 );
155 : : }
156 : 0 : }
157 : :
158 : 0 : sal_uInt16 TextEngine::GetViewCount() const
159 : : {
160 : 0 : return mpViews->size();
161 : : }
162 : :
163 : 0 : TextView* TextEngine::GetView( sal_uInt16 nView ) const
164 : : {
165 : 0 : return (*mpViews)[ nView ];
166 : : }
167 : :
168 : 90 : TextView* TextEngine::GetActiveView() const
169 : : {
170 : 90 : return mpActiveView;
171 : : }
172 : :
173 : 90 : void TextEngine::SetActiveView( TextView* pTextView )
174 : : {
175 [ + - ]: 90 : if ( pTextView != mpActiveView )
176 : : {
177 [ - + ]: 90 : if ( mpActiveView )
178 : 0 : mpActiveView->HideSelection();
179 : :
180 : 90 : mpActiveView = pTextView;
181 : :
182 [ + - ]: 90 : if ( mpActiveView )
183 : 90 : mpActiveView->ShowSelection();
184 : : }
185 : 90 : }
186 : :
187 : 432 : void TextEngine::SetFont( const Font& rFont )
188 : : {
189 [ + - ]: 432 : if ( rFont != maFont )
190 : : {
191 [ + - ]: 432 : maFont = rFont;
192 : : // #i40221# As the font's color now defaults to transparent (since i35764)
193 : : // we have to choose a useful textcolor in this case.
194 : : // Otherwise maTextColor and maFont.GetColor() are both transparent....
195 [ + - ][ + + ]: 432 : if( rFont.GetColor() == COL_TRANSPARENT )
196 : 90 : maTextColor = COL_BLACK;
197 : : else
198 [ + - ]: 342 : maTextColor = rFont.GetColor();
199 : :
200 : : // Wegen Selektion keinen Transparenten Font zulassen...
201 : : // (Sonst spaeter in ImplPaint den Hintergrund anders loeschen...)
202 [ + - ]: 432 : maFont.SetTransparent( sal_False );
203 : : // Tell VCL not to use the font color, use text color from OutputDevice
204 [ + - ]: 432 : maFont.SetColor( COL_TRANSPARENT );
205 [ + - ]: 432 : Color aFillColor( maFont.GetFillColor() );
206 [ + - ]: 432 : aFillColor.SetTransparency( 0 );
207 [ + - ]: 432 : maFont.SetFillColor( aFillColor );
208 : :
209 [ + - ]: 432 : maFont.SetAlign( ALIGN_TOP );
210 [ + - ]: 432 : mpRefDev->SetFont( maFont);
211 : 432 : Size aTextSize;
212 [ + - ][ + - ]: 432 : aTextSize.Width() = mpRefDev->GetTextWidth(rtl::OUString(" "));
[ + - ]
213 [ + - ]: 432 : aTextSize.Height() = mpRefDev->GetTextHeight();
214 [ - + ]: 432 : if ( !aTextSize.Width() )
215 [ # # ][ # # ]: 0 : aTextSize.Width() = mpRefDev->GetTextWidth(rtl::OUString("XXXX"));
[ # # ]
216 : :
217 : 432 : mnDefTab = (sal_uInt16)aTextSize.Width();
218 [ - + ]: 432 : if ( !mnDefTab )
219 : 0 : mnDefTab = 1;
220 : 432 : mnCharHeight = (sal_uInt16)aTextSize.Height();
221 : 432 : mnFixCharWidth100 = 0;
222 : :
223 [ + - ]: 432 : FormatFullDoc();
224 [ + - ]: 432 : UpdateViews();
225 : :
226 [ + + ]: 774 : for ( sal_uInt16 nView = mpViews->size(); nView; )
227 : : {
228 [ + - ]: 342 : TextView* pView = (*mpViews)[ --nView ];
229 [ + - ][ + + ]: 342 : pView->GetWindow()->SetInputContext( InputContext( GetFont(), !pView->IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
[ + - ][ + - ]
[ + - ][ + - ]
230 : : }
231 : : }
232 : 432 : }
233 : :
234 : 184 : void TextEngine::SetMaxTextLen( sal_uLong nLen )
235 : : {
236 : 184 : mnMaxTextLen = nLen;
237 : 184 : }
238 : :
239 : 76 : void TextEngine::SetMaxTextWidth( sal_uLong nMaxWidth )
240 : : {
241 [ + + ]: 76 : if ( nMaxWidth != mnMaxTextWidth )
242 : : {
243 : 16 : mnMaxTextWidth = Min( nMaxWidth, (sal_uLong)0x7FFFFFFF );
244 : 16 : FormatFullDoc();
245 : 16 : UpdateViews();
246 : : }
247 : 76 : }
248 : :
249 : : static sal_Unicode static_aLFText[] = { '\n', 0 };
250 : : static sal_Unicode static_aCRText[] = { '\r', 0 };
251 : : static sal_Unicode static_aCRLFText[] = { '\r', '\n', 0 };
252 : :
253 : 0 : static inline const sal_Unicode* static_getLineEndText( LineEnd aLineEnd )
254 : : {
255 : 0 : const sal_Unicode* pRet = NULL;
256 : :
257 [ # # # # ]: 0 : switch( aLineEnd )
258 : : {
259 : 0 : case LINEEND_LF: pRet = static_aLFText;break;
260 : 0 : case LINEEND_CR: pRet = static_aCRText;break;
261 : 0 : case LINEEND_CRLF: pRet = static_aCRLFText;break;
262 : : }
263 : 0 : return pRet;
264 : : }
265 : :
266 : 0 : void TextEngine::ReplaceText(const TextSelection& rSel, const String& rText)
267 : : {
268 : 0 : ImpInsertText( rSel, rText );
269 : 0 : }
270 : :
271 : 0 : String TextEngine::GetText( LineEnd aSeparator ) const
272 : : {
273 : 0 : return mpDoc->GetText( static_getLineEndText( aSeparator ) );
274 : : }
275 : :
276 : 0 : String TextEngine::GetTextLines( LineEnd aSeparator ) const
277 : : {
278 : 0 : String aText;
279 : 0 : sal_uLong nParas = mpTEParaPortions->Count();
280 : 0 : const sal_Unicode* pSep = static_getLineEndText( aSeparator );
281 [ # # ]: 0 : for ( sal_uLong nP = 0; nP < nParas; nP++ )
282 : : {
283 [ # # ]: 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nP );
284 : :
285 : 0 : sal_uInt16 nLines = pTEParaPortion->GetLines().size();
286 [ # # ]: 0 : for ( sal_uInt16 nL = 0; nL < nLines; nL++ )
287 : : {
288 : 0 : TextLine* pLine = pTEParaPortion->GetLines()[nL];
289 [ # # ][ # # ]: 0 : aText += pTEParaPortion->GetNode()->GetText().Copy( pLine->GetStart(), pLine->GetEnd() - pLine->GetStart() );
[ # # ]
290 [ # # ][ # # ]: 0 : if ( pSep && ( ( (nP+1) < nParas ) || ( (nL+1) < nLines ) ) )
[ # # ]
291 [ # # ]: 0 : aText += pSep;
292 : : }
293 : : }
294 : 0 : return aText;
295 : : }
296 : :
297 : 0 : String TextEngine::GetText( sal_uLong nPara ) const
298 : : {
299 : 0 : return mpDoc->GetText( nPara );
300 : : }
301 : :
302 : 0 : sal_uLong TextEngine::GetTextLen( LineEnd aSeparator ) const
303 : : {
304 : 0 : return mpDoc->GetTextLen( static_getLineEndText( aSeparator ) );
305 : : }
306 : :
307 : 0 : sal_uLong TextEngine::GetTextLen( const TextSelection& rSel, LineEnd aSeparator ) const
308 : : {
309 : 0 : TextSelection aSel( rSel );
310 [ # # ]: 0 : aSel.Justify();
311 [ # # ]: 0 : ValidateSelection( aSel );
312 [ # # ]: 0 : return mpDoc->GetTextLen( static_getLineEndText( aSeparator ), &aSel );
313 : : }
314 : :
315 : 436 : sal_uInt16 TextEngine::GetTextLen( sal_uLong nPara ) const
316 : : {
317 : 436 : return mpDoc->GetNodes().GetObject( nPara )->GetText().Len();
318 : : }
319 : :
320 : 0 : void TextEngine::SetUpdateMode( sal_Bool bUpdate )
321 : : {
322 [ # # ]: 0 : if ( bUpdate != mbUpdate )
323 : : {
324 : 0 : mbUpdate = bUpdate;
325 [ # # ]: 0 : if ( mbUpdate )
326 : : {
327 : 0 : FormatAndUpdate( GetActiveView() );
328 [ # # ]: 0 : if ( GetActiveView() )
329 : 0 : GetActiveView()->ShowCursor();
330 : : }
331 : : }
332 : 0 : }
333 : :
334 : 0 : sal_Bool TextEngine::DoesKeyChangeText( const KeyEvent& rKeyEvent )
335 : : {
336 : 0 : sal_Bool bDoesChange = sal_False;
337 : :
338 : 0 : KeyFuncType eFunc = rKeyEvent.GetKeyCode().GetFunction();
339 [ # # ]: 0 : if ( eFunc != KEYFUNC_DONTKNOW )
340 : : {
341 [ # # ]: 0 : switch ( eFunc )
342 : : {
343 : : case KEYFUNC_UNDO:
344 : : case KEYFUNC_REDO:
345 : : case KEYFUNC_CUT:
346 : 0 : case KEYFUNC_PASTE: bDoesChange = sal_True;
347 : 0 : break;
348 : : default: // wird dann evtl. unten bearbeitet.
349 : 0 : eFunc = KEYFUNC_DONTKNOW;
350 : : }
351 : : }
352 [ # # ]: 0 : if ( eFunc == KEYFUNC_DONTKNOW )
353 : : {
354 [ # # # ]: 0 : switch ( rKeyEvent.GetKeyCode().GetCode() )
355 : : {
356 : : case KEY_DELETE:
357 : : case KEY_BACKSPACE:
358 : : {
359 [ # # ]: 0 : if ( !rKeyEvent.GetKeyCode().IsMod2() )
360 : 0 : bDoesChange = sal_True;
361 : : }
362 : 0 : break;
363 : : case KEY_RETURN:
364 : : case KEY_TAB:
365 : : {
366 [ # # ][ # # ]: 0 : if ( !rKeyEvent.GetKeyCode().IsMod1() && !rKeyEvent.GetKeyCode().IsMod2() )
[ # # ]
367 : 0 : bDoesChange = sal_True;
368 : : }
369 : 0 : break;
370 : : default:
371 : : {
372 : 0 : bDoesChange = TextEngine::IsSimpleCharInput( rKeyEvent );
373 : : }
374 : : }
375 : : }
376 : 0 : return bDoesChange;
377 : : }
378 : :
379 : 0 : sal_Bool TextEngine::IsSimpleCharInput( const KeyEvent& rKeyEvent )
380 : : {
381 [ # # ][ # # : 0 : if( rKeyEvent.GetCharCode() >= 32 && rKeyEvent.GetCharCode() != 127 &&
# # # # ]
[ # # ]
382 : 0 : KEY_MOD1 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) && // (ssa) #i45714#:
383 : 0 : KEY_MOD2 != (rKeyEvent.GetKeyCode().GetModifier() & ~KEY_SHIFT) ) // check for Ctrl and Alt separately
384 : : {
385 : 0 : return sal_True;
386 : : }
387 : 0 : return sal_False;
388 : : }
389 : :
390 : 218 : void TextEngine::ImpInitDoc()
391 : : {
392 [ + + ]: 218 : if ( mpDoc )
393 : 128 : mpDoc->Clear();
394 : : else
395 [ + - ]: 90 : mpDoc = new TextDoc;
396 : :
397 [ + + ]: 218 : delete mpTEParaPortions;
398 [ + - ]: 218 : mpTEParaPortions = new TEParaPortions;
399 : :
400 [ + - ][ + - ]: 218 : TextNode* pNode = new TextNode( String() );
401 : 218 : mpDoc->GetNodes().Insert( pNode, 0 );
402 : :
403 [ + - ]: 218 : TEParaPortion* pIniPortion = new TEParaPortion( pNode );
404 : 218 : mpTEParaPortions->Insert( pIniPortion, (sal_uLong)0 );
405 : :
406 : 218 : mbFormatted = sal_False;
407 : :
408 : 218 : ImpParagraphRemoved( TEXT_PARA_ALL );
409 : 218 : ImpParagraphInserted( 0 );
410 : 218 : }
411 : :
412 : 0 : String TextEngine::GetText( const TextSelection& rSel, LineEnd aSeparator ) const
413 : : {
414 [ # # ]: 0 : String aText;
415 : :
416 [ # # ]: 0 : if ( !rSel.HasRange() )
417 : : return aText;
418 : :
419 : 0 : TextSelection aSel( rSel );
420 [ # # ]: 0 : aSel.Justify();
421 : :
422 : 0 : sal_uLong nStartPara = aSel.GetStart().GetPara();
423 : 0 : sal_uLong nEndPara = aSel.GetEnd().GetPara();
424 : 0 : const sal_Unicode* pSep = static_getLineEndText( aSeparator );
425 [ # # ]: 0 : for ( sal_uLong nNode = aSel.GetStart().GetPara(); nNode <= nEndPara; nNode++ )
426 : : {
427 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
428 : :
429 : 0 : sal_uInt16 nStartPos = 0;
430 : 0 : sal_uInt16 nEndPos = pNode->GetText().Len();
431 [ # # ]: 0 : if ( nNode == nStartPara )
432 : 0 : nStartPos = aSel.GetStart().GetIndex();
433 [ # # ]: 0 : if ( nNode == nEndPara ) // kann auch == nStart sein!
434 : 0 : nEndPos = aSel.GetEnd().GetIndex();
435 : :
436 [ # # ][ # # ]: 0 : aText += pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
[ # # ]
437 [ # # ]: 0 : if ( nNode < nEndPara )
438 [ # # ]: 0 : aText += pSep;
439 : : }
440 : 0 : return aText;
441 : : }
442 : :
443 : 128 : void TextEngine::ImpRemoveText()
444 : : {
445 [ + - ]: 128 : ImpInitDoc();
446 : :
447 : 128 : TextPaM aStartPaM( 0, 0 );
448 [ + - ]: 128 : TextSelection aEmptySel( aStartPaM, aStartPaM );
449 [ + + ]: 256 : for ( sal_uInt16 nView = 0; nView < mpViews->size(); nView++ )
450 : : {
451 [ + - ]: 128 : TextView* pView = (*mpViews)[ nView ];
452 [ + - ]: 128 : pView->ImpSetSelection( aEmptySel );
453 : : }
454 [ + - ]: 128 : ResetUndo();
455 : 128 : }
456 : :
457 : 128 : void TextEngine::SetText( const XubString& rText )
458 : : {
459 [ + - ]: 128 : ImpRemoveText();
460 : :
461 : 128 : sal_Bool bUndoCurrentlyEnabled = IsUndoEnabled();
462 : : // Der von Hand reingesteckte Text kann nicht vom Anwender rueckgaengig gemacht werden.
463 [ + - ]: 128 : EnableUndo( sal_False );
464 : :
465 : 128 : TextPaM aStartPaM( 0, 0 );
466 [ + - ]: 128 : TextSelection aEmptySel( aStartPaM, aStartPaM );
467 : :
468 : 128 : TextPaM aPaM = aStartPaM;
469 [ + + ]: 128 : if ( rText.Len() )
470 [ + - ]: 52 : aPaM = ImpInsertText( aEmptySel, rText );
471 : :
472 [ + + ]: 256 : for ( sal_uInt16 nView = 0; nView < mpViews->size(); nView++ )
473 : : {
474 [ + - ]: 128 : TextView* pView = (*mpViews)[ nView ];
475 [ + - ]: 128 : pView->ImpSetSelection( aEmptySel );
476 : :
477 : : // Wenn kein Text, dann auch Kein Format&Update
478 : : // => Der Text bleibt stehen.
479 [ + + ][ + - ]: 128 : if ( !rText.Len() && GetUpdateMode() )
[ + + ]
480 [ + - ]: 76 : pView->Invalidate();
481 : : }
482 : :
483 [ + + ]: 128 : if( !rText.Len() ) // sonst muss spaeter noch invalidiert werden, !bFormatted reicht.
484 : 76 : mnCurTextHeight = 0;
485 : :
486 [ + - ]: 128 : FormatAndUpdate();
487 : :
488 [ + - ]: 128 : EnableUndo( bUndoCurrentlyEnabled );
489 : : DBG_ASSERT( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo nach SetText?" );
490 : 128 : }
491 : :
492 : :
493 : 218 : void TextEngine::CursorMoved( sal_uLong nNode )
494 : : {
495 : : // Leere Attribute loeschen, aber nur, wenn Absatz nicht leer!
496 : 218 : TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
497 [ - + ][ # # ]: 218 : if ( pNode && pNode->GetCharAttribs().HasEmptyAttribs() && pNode->GetText().Len() )
[ - + ][ + - ]
498 : 0 : pNode->GetCharAttribs().DeleteEmptyAttribs();
499 : 218 : }
500 : :
501 : 0 : void TextEngine::ImpRemoveChars( const TextPaM& rPaM, sal_uInt16 nChars, SfxUndoAction* )
502 : : {
503 : : DBG_ASSERT( nChars, "ImpRemoveChars - 0 Chars?!" );
504 [ # # ][ # # ]: 0 : if ( IsUndoEnabled() && !IsInUndo() )
[ # # ]
505 : : {
506 : : // Attribute muessen hier vorm RemoveChars fuer UNDO gesichert werden!
507 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
508 [ # # ]: 0 : XubString aStr( pNode->GetText().Copy( rPaM.GetIndex(), nChars ) );
509 : :
510 : : // Pruefen, ob Attribute geloescht oder geaendert werden:
511 : 0 : sal_uInt16 nStart = rPaM.GetIndex();
512 : 0 : sal_uInt16 nEnd = nStart + nChars;
513 [ # # ]: 0 : for ( sal_uInt16 nAttr = pNode->GetCharAttribs().Count(); nAttr; )
514 : : {
515 [ # # ]: 0 : TextCharAttrib* pAttr = pNode->GetCharAttribs().GetAttrib( --nAttr );
516 [ # # ][ # # ]: 0 : if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetStart() < nEnd ) )
[ # # ]
517 : : {
518 : 0 : break; // for
519 : : }
520 : : }
521 [ # # ][ # # ]: 0 : InsertUndo( new TextUndoRemoveChars( this, rPaM, aStr ) );
[ # # ][ # # ]
522 : : }
523 : :
524 : 0 : mpDoc->RemoveChars( rPaM, nChars );
525 : 0 : ImpCharsRemoved( rPaM.GetPara(), rPaM.GetIndex(), nChars );
526 : 0 : }
527 : :
528 : 0 : TextPaM TextEngine::ImpConnectParagraphs( sal_uLong nLeft, sal_uLong nRight )
529 : : {
530 : : DBG_ASSERT( nLeft != nRight, "Den gleichen Absatz zusammenfuegen ?" );
531 : :
532 : 0 : TextNode* pLeft = mpDoc->GetNodes().GetObject( nLeft );
533 : 0 : TextNode* pRight = mpDoc->GetNodes().GetObject( nRight );
534 : :
535 [ # # ][ # # ]: 0 : if ( IsUndoEnabled() && !IsInUndo() )
[ # # ]
536 [ # # ]: 0 : InsertUndo( new TextUndoConnectParas( this, nLeft, pLeft->GetText().Len() ) );
537 : :
538 : : // Erstmal Portions suchen, da pRight nach ConnectParagraphs weg.
539 : 0 : TEParaPortion* pLeftPortion = mpTEParaPortions->GetObject( nLeft );
540 : 0 : TEParaPortion* pRightPortion = mpTEParaPortions->GetObject( nRight );
541 : : DBG_ASSERT( pLeft && pLeftPortion, "Blinde Portion in ImpConnectParagraphs(1)" );
542 : : DBG_ASSERT( pRight && pRightPortion, "Blinde Portion in ImpConnectParagraphs(2)" );
543 : :
544 : 0 : TextPaM aPaM = mpDoc->ConnectParagraphs( pLeft, pRight );
545 : 0 : ImpParagraphRemoved( nRight );
546 : :
547 : 0 : pLeftPortion->MarkSelectionInvalid( aPaM.GetIndex(), pLeft->GetText().Len() );
548 : :
549 : 0 : mpTEParaPortions->Remove( nRight );
550 [ # # ]: 0 : delete pRightPortion;
551 : : // der rechte Node wird von EditDoc::ConnectParagraphs() geloescht.
552 : :
553 : 0 : return aPaM;
554 : : }
555 : :
556 : 0 : TextPaM TextEngine::ImpDeleteText( const TextSelection& rSel )
557 : : {
558 [ # # ]: 0 : if ( !rSel.HasRange() )
559 : 0 : return rSel.GetStart();
560 : :
561 : 0 : TextSelection aSel( rSel );
562 [ # # ]: 0 : aSel.Justify();
563 : 0 : TextPaM aStartPaM( aSel.GetStart() );
564 : 0 : TextPaM aEndPaM( aSel.GetEnd() );
565 : :
566 [ # # ]: 0 : CursorMoved( aStartPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden...
567 [ # # ]: 0 : CursorMoved( aEndPaM.GetPara() ); // nur damit neu eingestellte Attribute verschwinden...
568 : :
569 : : DBG_ASSERT( mpDoc->IsValidPaM( aStartPaM ), "Index im Wald in ImpDeleteText" );
570 : : DBG_ASSERT( mpDoc->IsValidPaM( aEndPaM ), "Index im Wald in ImpDeleteText" );
571 : :
572 : 0 : sal_uLong nStartNode = aStartPaM.GetPara();
573 : 0 : sal_uLong nEndNode = aEndPaM.GetPara();
574 : :
575 : : // Alle Nodes dazwischen entfernen....
576 [ # # ]: 0 : for ( sal_uLong z = nStartNode+1; z < nEndNode; z++ )
577 : : {
578 : : // Immer nStartNode+1, wegen Remove()!
579 [ # # ]: 0 : ImpRemoveParagraph( nStartNode+1 );
580 : : }
581 : :
582 [ # # ]: 0 : if ( nStartNode != nEndNode )
583 : : {
584 : : // Den Rest des StartNodes...
585 [ # # ]: 0 : TextNode* pLeft = mpDoc->GetNodes().GetObject( nStartNode );
586 : 0 : sal_uInt16 nChars = pLeft->GetText().Len() - aStartPaM.GetIndex();
587 [ # # ]: 0 : if ( nChars )
588 : : {
589 [ # # ]: 0 : ImpRemoveChars( aStartPaM, nChars );
590 [ # # ]: 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
591 : : DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(3)" );
592 [ # # ]: 0 : pPortion->MarkSelectionInvalid( aStartPaM.GetIndex(), pLeft->GetText().Len() );
593 : : }
594 : :
595 : : // Den Anfang des EndNodes....
596 : 0 : nEndNode = nStartNode+1; // Die anderen Absaetze wurden geloescht
597 : 0 : nChars = aEndPaM.GetIndex();
598 [ # # ]: 0 : if ( nChars )
599 : : {
600 : 0 : aEndPaM.GetPara() = nEndNode;
601 : 0 : aEndPaM.GetIndex() = 0;
602 [ # # ]: 0 : ImpRemoveChars( aEndPaM, nChars );
603 [ # # ]: 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nEndNode );
604 : : DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(4)" );
605 [ # # ]: 0 : pPortion->MarkSelectionInvalid( 0, pPortion->GetNode()->GetText().Len() );
606 : : }
607 : :
608 : : // Zusammenfuegen....
609 [ # # ]: 0 : aStartPaM = ImpConnectParagraphs( nStartNode, nEndNode );
610 : : }
611 : : else
612 : : {
613 : : sal_uInt16 nChars;
614 : 0 : nChars = aEndPaM.GetIndex() - aStartPaM.GetIndex();
615 [ # # ]: 0 : ImpRemoveChars( aStartPaM, nChars );
616 [ # # ]: 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nStartNode );
617 : : DBG_ASSERT( pPortion, "Blinde Portion in ImpDeleteText(5)" );
618 [ # # ]: 0 : pPortion->MarkInvalid( aEndPaM.GetIndex(), aStartPaM.GetIndex() - aEndPaM.GetIndex() );
619 : : }
620 : :
621 : : // UpdateSelections();
622 [ # # ]: 0 : TextModified();
623 : 0 : return aStartPaM;
624 : : }
625 : :
626 : 0 : void TextEngine::ImpRemoveParagraph( sal_uLong nPara )
627 : : {
628 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
629 : 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
630 : :
631 : : // Der Node wird vom Undo verwaltet und ggf. zerstoert!
632 : 0 : /* delete */ mpDoc->GetNodes().Remove( nPara );
633 [ # # ][ # # ]: 0 : if ( IsUndoEnabled() && !IsInUndo() )
[ # # ]
634 [ # # ]: 0 : InsertUndo( new TextUndoDelPara( this, pNode, nPara ) );
635 : : else
636 [ # # ]: 0 : delete pNode;
637 : :
638 : 0 : mpTEParaPortions->Remove( nPara );
639 [ # # ]: 0 : delete pPortion;
640 : :
641 : 0 : ImpParagraphRemoved( nPara );
642 : 0 : }
643 : :
644 : 0 : uno::Reference < i18n::XExtendedInputSequenceChecker > TextEngine::GetInputSequenceChecker() const
645 : : {
646 : 0 : uno::Reference < i18n::XExtendedInputSequenceChecker > xISC;
647 : : // if ( !xISC.is() )
648 : : {
649 [ # # ]: 0 : uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
650 [ # # ][ # # ]: 0 : uno::Reference< uno::XInterface > xI = xMSF->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.InputSequenceChecker" )) );
[ # # ]
651 [ # # ]: 0 : if ( xI.is() )
652 : : {
653 [ # # ][ # # ]: 0 : Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XExtendedInputSequenceChecker >*)0) );
[ # # ]
654 [ # # ]: 0 : x >>= xISC;
655 : 0 : }
656 : : }
657 : 0 : return xISC;
658 : : }
659 : :
660 : 0 : sal_Bool TextEngine::IsInputSequenceCheckingRequired( sal_Unicode c, const TextSelection& rCurSel ) const
661 : : {
662 [ # # ]: 0 : uno::Reference< i18n::XBreakIterator > xBI = ((TextEngine *) this)->GetBreakIterator();
663 [ # # ]: 0 : SvtCTLOptions aCTLOptions;
664 : :
665 : : // get the index that really is first
666 : 0 : sal_uInt16 nFirstPos = rCurSel.GetStart().GetIndex();
667 : 0 : sal_uInt16 nMaxPos = rCurSel.GetEnd().GetIndex();
668 [ # # ]: 0 : if (nMaxPos < nFirstPos)
669 : 0 : nFirstPos = nMaxPos;
670 : :
671 : : sal_Bool bIsSequenceChecking =
672 [ # # ]: 0 : aCTLOptions.IsCTLFontEnabled() &&
673 [ # # ]: 0 : aCTLOptions.IsCTLSequenceChecking() &&
674 : : nFirstPos != 0 && /* first char needs not to be checked */
675 [ # # ][ # # ]: 0 : xBI.is() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rtl::OUString( c ), 0 );
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
676 : :
677 [ # # ]: 0 : return bIsSequenceChecking;
678 : : }
679 : :
680 : 0 : TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, sal_Unicode c, sal_Bool bOverwrite )
681 : : {
682 : 0 : return ImpInsertText( c, rCurSel, bOverwrite, sal_False );
683 : : }
684 : :
685 : 0 : TextPaM TextEngine::ImpInsertText( sal_Unicode c, const TextSelection& rCurSel, sal_Bool bOverwrite, sal_Bool bIsUserInput )
686 : : {
687 : : DBG_ASSERT( c != '\n', "Zeilenumbruch bei InsertText ?" );
688 : : DBG_ASSERT( c != '\r', "Zeilenumbruch bei InsertText ?" );
689 : :
690 : 0 : TextPaM aPaM( rCurSel.GetStart() );
691 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
692 : :
693 [ # # ]: 0 : if ( pNode->GetText().Len() < STRING_MAXLEN )
694 : : {
695 : : sal_Bool bDoOverwrite = ( bOverwrite &&
696 [ # # ][ # # ]: 0 : ( aPaM.GetIndex() < pNode->GetText().Len() ) ) ? sal_True : sal_False;
697 : :
698 [ # # ][ # # ]: 0 : sal_Bool bUndoAction = ( rCurSel.HasRange() || bDoOverwrite );
699 : :
700 [ # # ]: 0 : if ( bUndoAction )
701 [ # # ]: 0 : UndoActionStart();
702 : :
703 [ # # ]: 0 : if ( rCurSel.HasRange() )
704 : : {
705 [ # # ]: 0 : aPaM = ImpDeleteText( rCurSel );
706 : : }
707 [ # # ]: 0 : else if ( bDoOverwrite )
708 : : {
709 : : // Wenn Selektion, dann kein Zeichen ueberschreiben
710 [ # # ]: 0 : TextSelection aTmpSel( aPaM );
711 : 0 : aTmpSel.GetEnd().GetIndex()++;
712 [ # # ]: 0 : ImpDeleteText( aTmpSel );
713 : : }
714 : :
715 [ # # ][ # # ]: 0 : if (bIsUserInput && IsInputSequenceCheckingRequired( c, rCurSel ))
[ # # ][ # # ]
716 : : {
717 [ # # ]: 0 : uno::Reference < i18n::XExtendedInputSequenceChecker > xISC = GetInputSequenceChecker();
718 [ # # ]: 0 : SvtCTLOptions aCTLOptions;
719 : :
720 [ # # ]: 0 : if (xISC.is())
721 : : {
722 : 0 : xub_StrLen nTmpPos = aPaM.GetIndex();
723 [ # # ]: 0 : sal_Int16 nCheckMode = aCTLOptions.IsCTLSequenceCheckingRestricted() ?
724 [ # # ]: 0 : i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;
725 : :
726 : : // the text that needs to be checked is only the one
727 : : // before the current cursor position
728 [ # # ][ # # ]: 0 : rtl::OUString aOldText( mpDoc->GetText( aPaM.GetPara() ).Copy(0, nTmpPos) );
[ # # ][ # # ]
[ # # ]
729 : 0 : rtl::OUString aNewText( aOldText );
730 [ # # ][ # # ]: 0 : if (aCTLOptions.IsCTLSequenceCheckingTypeAndReplace())
731 : : {
732 [ # # ][ # # ]: 0 : xISC->correctInputSequence( aNewText, nTmpPos - 1, c, nCheckMode );
733 : :
734 : : // find position of first character that has changed
735 : 0 : sal_Int32 nOldLen = aOldText.getLength();
736 : 0 : sal_Int32 nNewLen = aNewText.getLength();
737 : 0 : const sal_Unicode *pOldTxt = aOldText.getStr();
738 : 0 : const sal_Unicode *pNewTxt = aNewText.getStr();
739 : 0 : sal_Int32 nChgPos = 0;
740 [ # # ][ # # ]: 0 : while ( nChgPos < nOldLen && nChgPos < nNewLen &&
[ # # ][ # # ]
741 : 0 : pOldTxt[nChgPos] == pNewTxt[nChgPos] )
742 : 0 : ++nChgPos;
743 : :
744 [ # # ]: 0 : String aChgText( aNewText.copy( nChgPos ) );
745 : :
746 : : // select text from first pos to be changed to current pos
747 [ # # ]: 0 : TextSelection aSel( TextPaM( aPaM.GetPara(), (sal_uInt16) nChgPos ), aPaM );
748 : :
749 [ # # ]: 0 : if (aChgText.Len())
750 : : // ImpInsertText implicitly handles undo...
751 [ # # ]: 0 : return ImpInsertText( aSel, aChgText );
752 : : else
753 [ # # ]: 0 : return aPaM;
754 : : }
755 : : else
756 : : {
757 : : // should the character be ignored (i.e. not get inserted) ?
758 [ # # ][ # # ]: 0 : if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, c, nCheckMode ))
[ # # ]
759 : 0 : return aPaM; // nothing to be done -> no need for undo
760 [ # # ][ # # ]: 0 : }
761 [ # # ][ # # ]: 0 : }
[ # # ]
762 : :
763 : : // at this point now we will insert the character 'normally' some lines below...
764 : : }
765 : :
766 : :
767 [ # # ][ # # ]: 0 : if ( IsUndoEnabled() && !IsInUndo() )
[ # # ]
768 : : {
769 [ # # ][ # # ]: 0 : TextUndoInsertChars* pNewUndo = new TextUndoInsertChars( this, aPaM, rtl::OUString(c) );
[ # # ][ # # ]
770 [ # # ][ # # ]: 0 : sal_Bool bTryMerge = ( !bDoOverwrite && ( c != ' ' ) ) ? sal_True : sal_False;
771 [ # # ]: 0 : InsertUndo( pNewUndo, bTryMerge );
772 : : }
773 : :
774 [ # # ]: 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
775 [ # # ]: 0 : pPortion->MarkInvalid( aPaM.GetIndex(), 1 );
776 [ # # ]: 0 : if ( c == '\t' )
777 : 0 : pPortion->SetNotSimpleInvalid();
778 [ # # ]: 0 : aPaM = mpDoc->InsertText( aPaM, c );
779 [ # # ]: 0 : ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-1, 1 );
780 : :
781 [ # # ]: 0 : TextModified();
782 : :
783 [ # # ]: 0 : if ( bUndoAction )
784 [ # # ]: 0 : UndoActionEnd();
785 : : }
786 : :
787 : 0 : return aPaM;
788 : : }
789 : :
790 : :
791 : 52 : TextPaM TextEngine::ImpInsertText( const TextSelection& rCurSel, const XubString& rStr )
792 : : {
793 [ + - ]: 52 : UndoActionStart();
794 : :
795 : 52 : TextPaM aPaM;
796 : :
797 [ - + ]: 52 : if ( rCurSel.HasRange() )
798 [ # # ]: 0 : aPaM = ImpDeleteText( rCurSel );
799 : : else
800 : 52 : aPaM = rCurSel.GetEnd();
801 : :
802 [ + - ][ + - ]: 52 : XubString aText(convertLineEnd(rStr, LINEEND_LF));
[ + - ]
803 : :
804 : 52 : sal_uInt16 nStart = 0;
805 [ + + ]: 104 : while ( nStart < aText.Len() )
806 : : {
807 [ + - ]: 52 : sal_uInt16 nEnd = aText.Search( LINE_SEP, nStart );
808 [ + - ]: 52 : if ( nEnd == STRING_NOTFOUND )
809 : 52 : nEnd = aText.Len(); // nicht dereferenzieren!
810 : :
811 : : // Start == End => Leerzeile
812 [ + - ]: 52 : if ( nEnd > nStart )
813 : : {
814 : 52 : sal_uLong nL = aPaM.GetIndex();
815 : 52 : nL += ( nEnd-nStart );
816 [ - + ]: 52 : if ( nL > STRING_MAXLEN )
817 : : {
818 : 0 : sal_uInt16 nDiff = (sal_uInt16) (nL-STRING_MAXLEN);
819 : 0 : nEnd = nEnd - nDiff;
820 : : }
821 : :
822 [ + - ]: 52 : XubString aLine( aText, nStart, nEnd-nStart );
823 [ - + ][ # # ]: 52 : if ( IsUndoEnabled() && !IsInUndo() )
[ - + ]
824 [ # # ][ # # ]: 0 : InsertUndo( new TextUndoInsertChars( this, aPaM, aLine ) );
[ # # ]
825 : :
826 [ + - ]: 52 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( aPaM.GetPara() );
827 [ + - ]: 52 : pPortion->MarkInvalid( aPaM.GetIndex(), aLine.Len() );
828 [ + - ][ - + ]: 52 : if ( aLine.Search( '\t' ) != STRING_NOTFOUND )
829 : 0 : pPortion->SetNotSimpleInvalid();
830 : :
831 [ + - ]: 52 : aPaM = mpDoc->InsertText( aPaM, aLine );
832 [ + - ][ + - ]: 52 : ImpCharsInserted( aPaM.GetPara(), aPaM.GetIndex()-aLine.Len(), aLine.Len() );
833 : :
834 : : }
835 [ - + ]: 52 : if ( nEnd < aText.Len() )
836 [ # # ]: 0 : aPaM = ImpInsertParaBreak( aPaM );
837 : :
838 : 52 : nStart = nEnd+1;
839 : :
840 [ - + ]: 52 : if ( nStart < nEnd ) // #108611# overflow
841 : 0 : break;
842 : : }
843 : :
844 [ + - ]: 52 : UndoActionEnd();
845 : :
846 [ + - ]: 52 : TextModified();
847 [ + - ]: 52 : return aPaM;
848 : : }
849 : :
850 : 0 : TextPaM TextEngine::ImpInsertParaBreak( const TextSelection& rCurSel, sal_Bool bKeepEndingAttribs )
851 : : {
852 : 0 : TextPaM aPaM;
853 [ # # ]: 0 : if ( rCurSel.HasRange() )
854 [ # # ]: 0 : aPaM = ImpDeleteText( rCurSel );
855 : : else
856 : 0 : aPaM = rCurSel.GetEnd();
857 : :
858 [ # # ]: 0 : return ImpInsertParaBreak( aPaM, bKeepEndingAttribs );
859 : : }
860 : :
861 : 0 : TextPaM TextEngine::ImpInsertParaBreak( const TextPaM& rPaM, sal_Bool bKeepEndingAttribs )
862 : : {
863 [ # # ][ # # ]: 0 : if ( IsUndoEnabled() && !IsInUndo() )
[ # # ]
864 [ # # ]: 0 : InsertUndo( new TextUndoSplitPara( this, rPaM.GetPara(), rPaM.GetIndex() ) );
865 : :
866 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
867 : 0 : sal_Bool bFirstParaContentChanged = rPaM.GetIndex() < pNode->GetText().Len();
868 : :
869 : 0 : TextPaM aPaM( mpDoc->InsertParaBreak( rPaM, bKeepEndingAttribs ) );
870 : :
871 : 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
872 : : DBG_ASSERT( pPortion, "Blinde Portion in ImpInsertParaBreak" );
873 : 0 : pPortion->MarkInvalid( rPaM.GetIndex(), 0 );
874 : :
875 : 0 : TextNode* pNewNode = mpDoc->GetNodes().GetObject( aPaM.GetPara() );
876 [ # # ]: 0 : TEParaPortion* pNewPortion = new TEParaPortion( pNewNode );
877 : 0 : mpTEParaPortions->Insert( pNewPortion, aPaM.GetPara() );
878 : 0 : ImpParagraphInserted( aPaM.GetPara() );
879 : :
880 : 0 : CursorMoved( rPaM.GetPara() ); // falls leeres Attribut entstanden.
881 : 0 : TextModified();
882 : :
883 [ # # ]: 0 : if ( bFirstParaContentChanged )
884 [ # # ]: 0 : Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, rPaM.GetPara() ) );
885 : :
886 : 0 : return aPaM;
887 : : }
888 : :
889 : 308 : Rectangle TextEngine::PaMtoEditCursor( const TextPaM& rPaM, sal_Bool bSpecial )
890 : : {
891 : : DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: PaMtoEditCursor" );
892 : :
893 : 308 : Rectangle aEditCursor;
894 : 308 : long nY = 0;
895 : :
896 [ + - ]: 308 : if ( !mbHasMultiLineParas )
897 : : {
898 : 308 : nY = rPaM.GetPara() * mnCharHeight;
899 : : }
900 : : else
901 : : {
902 [ # # ]: 0 : for ( sal_uLong nPortion = 0; nPortion < rPaM.GetPara(); nPortion++ )
903 : : {
904 : 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject(nPortion);
905 : 0 : nY += pPortion->GetLines().size() * mnCharHeight;
906 : : }
907 : : }
908 : :
909 : 308 : aEditCursor = GetEditCursor( rPaM, bSpecial );
910 : 308 : aEditCursor.Top() += nY;
911 : 308 : aEditCursor.Bottom() += nY;
912 : 308 : return aEditCursor;
913 : : }
914 : :
915 : 308 : Rectangle TextEngine::GetEditCursor( const TextPaM& rPaM, sal_Bool bSpecial, sal_Bool bPreferPortionStart )
916 : : {
917 [ - + ][ # # ]: 308 : if ( !IsFormatted() && !IsFormatting() )
[ - + ]
918 : 0 : FormatAndUpdate();
919 : :
920 : 308 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( rPaM.GetPara() );
921 : : //TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
922 : :
923 : : /*
924 : : bSpecial: Wenn hinter dem letzten Zeichen einer umgebrochenen Zeile,
925 : : am Ende der Zeile bleiben, nicht am Anfang der naechsten.
926 : : Zweck: - END => wirklich hinter das letzte Zeichen
927 : : - Selektion....
928 : : bSpecial: If behind the last character of a made up line, stay at the
929 : : end of the line, not at the start of the next line.
930 : : Purpose: - really END = > behind the last character
931 : : - to selection...
932 : :
933 : : */
934 : :
935 : 308 : long nY = 0;
936 : 308 : sal_uInt16 nCurIndex = 0;
937 : 308 : TextLine* pLine = 0;
938 [ + - ]: 308 : for ( sal_uInt16 nLine = 0; nLine < pPortion->GetLines().size(); nLine++ )
939 : : {
940 : 308 : TextLine* pTmpLine = pPortion->GetLines()[ nLine ];
941 [ # # ][ + - ]: 308 : if ( ( pTmpLine->GetStart() == rPaM.GetIndex() ) || ( pTmpLine->IsIn( rPaM.GetIndex(), bSpecial ) ) )
[ - + ]
942 : : {
943 : 308 : pLine = pTmpLine;
944 : 308 : break;
945 : : }
946 : :
947 : 0 : nCurIndex = nCurIndex + pTmpLine->GetLen();
948 : 0 : nY += mnCharHeight;
949 : : }
950 [ - + ]: 308 : if ( !pLine )
951 : : {
952 : : // Cursor am Ende des Absatzes.
953 : : DBG_ASSERT( rPaM.GetIndex() == nCurIndex, "Index voll daneben in GetEditCursor!" );
954 : :
955 : 0 : pLine = pPortion->GetLines().back();
956 : 0 : nY -= mnCharHeight;
957 : 0 : nCurIndex = nCurIndex - pLine->GetLen();
958 : : }
959 : :
960 : 308 : Rectangle aEditCursor;
961 : :
962 : 308 : aEditCursor.Top() = nY;
963 : 308 : nY += mnCharHeight;
964 : 308 : aEditCursor.Bottom() = nY-1;
965 : :
966 : : // innerhalb der Zeile suchen....
967 : 308 : long nX = ImpGetXPos( rPaM.GetPara(), pLine, rPaM.GetIndex(), bPreferPortionStart );
968 : 308 : aEditCursor.Left() = aEditCursor.Right() = nX;
969 : 308 : return aEditCursor;
970 : : }
971 : :
972 : 406 : long TextEngine::ImpGetXPos( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_Bool bPreferPortionStart )
973 : : {
974 : : DBG_ASSERT( ( nIndex >= pLine->GetStart() ) && ( nIndex <= pLine->GetEnd() ) , "ImpGetXPos muss richtig gerufen werden!" );
975 : :
976 : 406 : sal_Bool bDoPreferPortionStart = bPreferPortionStart;
977 : : // Assure that the portion belongs to this line:
978 [ + + ]: 406 : if ( nIndex == pLine->GetStart() )
979 : 357 : bDoPreferPortionStart = sal_True;
980 [ + - ]: 49 : else if ( nIndex == pLine->GetEnd() )
981 : 49 : bDoPreferPortionStart = sal_False;
982 : :
983 [ + - ]: 406 : TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
984 : :
985 : 406 : sal_uInt16 nTextPortionStart = 0;
986 [ + - ]: 406 : size_t nTextPortion = pParaPortion->GetTextPortions().FindPortion( nIndex, nTextPortionStart, bDoPreferPortionStart );
987 : :
988 : : DBG_ASSERT( ( nTextPortion >= pLine->GetStartPortion() ) && ( nTextPortion <= pLine->GetEndPortion() ), "GetXPos: Portion not in current line! " );
989 : :
990 : 406 : TETextPortion* pPortion = pParaPortion->GetTextPortions()[ nTextPortion ];
991 : :
992 [ + - ]: 406 : long nX = ImpGetPortionXOffset( nPara, pLine, nTextPortion );
993 : :
994 : 406 : long nPortionTextWidth = pPortion->GetWidth();
995 : :
996 [ + + ]: 406 : if ( nTextPortionStart != nIndex )
997 : : {
998 : : // Search within portion...
999 [ + - ]: 49 : if ( nIndex == ( nTextPortionStart + pPortion->GetLen() ) )
1000 : : {
1001 : : // End of Portion
1002 [ + - + - : 147 : if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) ||
- + # # #
# ][ + - ]
1003 : 98 : ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
1004 : 0 : ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1005 : : {
1006 : 49 : nX += nPortionTextWidth;
1007 [ - + ][ # # ]: 49 : if ( ( pPortion->GetKind() == PORTIONKIND_TAB ) && ( (nTextPortion+1) < pParaPortion->GetTextPortions().size() ) )
[ - + ]
1008 : : {
1009 : 0 : TETextPortion* pNextPortion = pParaPortion->GetTextPortions()[ nTextPortion+1 ];
1010 [ # # ][ # # : 0 : if ( ( pNextPortion->GetKind() != PORTIONKIND_TAB ) && (
# # # # #
# # # ]
1011 : 0 : ( !IsRightToLeft() && pNextPortion->IsRightToLeft() ) ||
1012 : 0 : ( IsRightToLeft() && !pNextPortion->IsRightToLeft() ) ) )
1013 : : {
1014 : : // nX += pNextPortion->GetWidth();
1015 : : // End of the tab portion, use start of next for cursor pos
1016 : : DBG_ASSERT( !bPreferPortionStart, "ImpGetXPos - How can we this tab portion here???" );
1017 [ # # ]: 0 : nX = ImpGetXPos( nPara, pLine, nIndex, sal_True );
1018 : : }
1019 : :
1020 : : }
1021 : : }
1022 : : }
1023 [ # # ]: 0 : else if ( pPortion->GetKind() == PORTIONKIND_TEXT )
1024 : : {
1025 : : DBG_ASSERT( nIndex != pLine->GetStart(), "Strange behavior in new ImpGetXPos()" );
1026 : :
1027 [ # # ]: 0 : long nPosInPortion = (long)CalcTextWidth( nPara, nTextPortionStart, nIndex-nTextPortionStart );
1028 : :
1029 [ # # ][ # # : 0 : if ( ( !IsRightToLeft() && !pPortion->IsRightToLeft() ) ||
# # # # ]
[ # # ]
1030 : 0 : ( IsRightToLeft() && pPortion->IsRightToLeft() ) )
1031 : : {
1032 : 0 : nX += nPosInPortion;
1033 : : }
1034 : : else
1035 : : {
1036 : 0 : nX += nPortionTextWidth - nPosInPortion;
1037 : : }
1038 : : }
1039 : : }
1040 : : else // if ( nIndex == pLine->GetStart() )
1041 : : {
1042 [ + - + - : 1428 : if ( ( pPortion->GetKind() != PORTIONKIND_TAB ) &&
+ - - + #
# ][ - + ]
1043 : 714 : ( ( !IsRightToLeft() && pPortion->IsRightToLeft() ) ||
1044 : 357 : ( IsRightToLeft() && !pPortion->IsRightToLeft() ) ) )
1045 : : {
1046 : 0 : nX += nPortionTextWidth;
1047 : : }
1048 : : }
1049 : :
1050 : 406 : return nX;
1051 : : }
1052 : :
1053 : 0 : const TextAttrib* TextEngine::FindAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1054 : : {
1055 : 0 : const TextAttrib* pAttr = NULL;
1056 : 0 : const TextCharAttrib* pCharAttr = FindCharAttrib( rPaM, nWhich );
1057 [ # # ]: 0 : if ( pCharAttr )
1058 : 0 : pAttr = &pCharAttr->GetAttr();
1059 : 0 : return pAttr;
1060 : : }
1061 : :
1062 : 0 : const TextCharAttrib* TextEngine::FindCharAttrib( const TextPaM& rPaM, sal_uInt16 nWhich ) const
1063 : : {
1064 : 0 : const TextCharAttrib* pAttr = NULL;
1065 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( rPaM.GetPara() );
1066 [ # # ][ # # ]: 0 : if ( pNode && ( rPaM.GetIndex() < pNode->GetText().Len() ) )
[ # # ]
1067 : 0 : pAttr = pNode->GetCharAttribs().FindAttrib( nWhich, rPaM.GetIndex() );
1068 : 0 : return pAttr;
1069 : : }
1070 : :
1071 : 0 : sal_Bool TextEngine::HasAttrib( sal_uInt16 nWhich ) const
1072 : : {
1073 : 0 : sal_Bool bAttr = sal_False;
1074 [ # # ][ # # ]: 0 : for ( sal_uLong n = mpDoc->GetNodes().Count(); --n && !bAttr; )
[ # # ]
1075 : : {
1076 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( n );
1077 : 0 : bAttr = pNode->GetCharAttribs().HasAttrib( nWhich );
1078 : : }
1079 : 0 : return bAttr;
1080 : : }
1081 : :
1082 : 0 : TextPaM TextEngine::GetPaM( const Point& rDocPos, sal_Bool bSmart )
1083 : : {
1084 : : DBG_ASSERT( GetUpdateMode(), "Darf bei Update=sal_False nicht erreicht werden: GetPaM" );
1085 : :
1086 : 0 : long nY = 0;
1087 [ # # ]: 0 : for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1088 : : {
1089 : 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1090 : 0 : long nTmpHeight = pPortion->GetLines().size() * mnCharHeight;
1091 : 0 : nY += nTmpHeight;
1092 [ # # ]: 0 : if ( nY > rDocPos.Y() )
1093 : : {
1094 : 0 : nY -= nTmpHeight;
1095 : 0 : Point aPosInPara( rDocPos );
1096 : 0 : aPosInPara.Y() -= nY;
1097 : :
1098 : 0 : TextPaM aPaM( nPortion, 0 );
1099 [ # # ]: 0 : aPaM.GetIndex() = ImpFindIndex( nPortion, aPosInPara, bSmart );
1100 : 0 : return aPaM;
1101 : : }
1102 : : }
1103 : :
1104 : : // Nicht gefunden - Dann den letzten sichtbare...
1105 : 0 : sal_uLong nLastNode = mpDoc->GetNodes().Count() - 1;
1106 : 0 : TextNode* pLast = mpDoc->GetNodes().GetObject( nLastNode );
1107 : 0 : return TextPaM( nLastNode, pLast->GetText().Len() );
1108 : : }
1109 : :
1110 : 0 : sal_uInt16 TextEngine::ImpFindIndex( sal_uLong nPortion, const Point& rPosInPara, sal_Bool bSmart )
1111 : : {
1112 : : DBG_ASSERT( IsFormatted(), "GetPaM: Nicht formatiert" );
1113 : 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1114 : :
1115 : 0 : sal_uInt16 nCurIndex = 0;
1116 : :
1117 : 0 : long nY = 0;
1118 : 0 : TextLine* pLine = 0;
1119 : : sal_uInt16 nLine;
1120 [ # # ]: 0 : for ( nLine = 0; nLine < pPortion->GetLines().size(); nLine++ )
1121 : : {
1122 : 0 : TextLine* pTmpLine = pPortion->GetLines()[ nLine ];
1123 : 0 : nY += mnCharHeight;
1124 [ # # ]: 0 : if ( nY > rPosInPara.Y() ) // das war 'se
1125 : : {
1126 : 0 : pLine = pTmpLine;
1127 : 0 : break; // richtige Y-Position intressiert nicht
1128 : : }
1129 : : }
1130 : : DBG_ASSERT( pLine, "ImpFindIndex: pLine ?" );
1131 : :
1132 : 0 : nCurIndex = GetCharPos( nPortion, nLine, rPosInPara.X(), bSmart );
1133 : :
1134 [ # # # # ]: 0 : if ( nCurIndex && ( nCurIndex == pLine->GetEnd() ) &&
[ # # ][ # # ]
1135 : 0 : ( pLine != pPortion->GetLines().back() ) )
1136 : : {
1137 [ # # ]: 0 : uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1138 : 0 : sal_Int32 nCount = 1;
1139 [ # # ][ # # ]: 0 : nCurIndex = (sal_uInt16)xBI->previousCharacters( pPortion->GetNode()->GetText(), nCurIndex, GetLocale(), i18n::CharacterIteratorMode::SKIPCELL, nCount, nCount );
[ # # ][ # # ]
1140 : : }
1141 : 0 : return nCurIndex;
1142 : : }
1143 : :
1144 : 0 : sal_uInt16 TextEngine::GetCharPos( sal_uLong nPortion, sal_uInt16 nLine, long nXPos, sal_Bool )
1145 : : {
1146 : :
1147 : 0 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPortion );
1148 : 0 : TextLine* pLine = pPortion->GetLines()[ nLine ];
1149 : :
1150 : 0 : sal_uInt16 nCurIndex = pLine->GetStart();
1151 : :
1152 : 0 : long nTmpX = pLine->GetStartX();
1153 [ # # ]: 0 : if ( nXPos <= nTmpX )
1154 : 0 : return nCurIndex;
1155 : :
1156 [ # # ]: 0 : for ( sal_uInt16 i = pLine->GetStartPortion(); i <= pLine->GetEndPortion(); i++ )
1157 : : {
1158 : 0 : TETextPortion* pTextPortion = pPortion->GetTextPortions()[ i ];
1159 : 0 : nTmpX += pTextPortion->GetWidth();
1160 : :
1161 [ # # ]: 0 : if ( nTmpX > nXPos )
1162 : : {
1163 [ # # ]: 0 : if( pTextPortion->GetLen() > 1 )
1164 : : {
1165 : 0 : nTmpX -= pTextPortion->GetWidth(); // vor die Portion stellen
1166 : : // Optimieren: Kein GetTextBreak, wenn feste Fontbreite...
1167 [ # # ]: 0 : Font aFont;
1168 [ # # ]: 0 : SeekCursor( nPortion, nCurIndex+1, aFont, NULL );
1169 [ # # ]: 0 : mpRefDev->SetFont( aFont);
1170 : 0 : long nPosInPortion = nXPos-nTmpX;
1171 [ # # ]: 0 : if ( IsRightToLeft() != pTextPortion->IsRightToLeft() )
1172 : 0 : nPosInPortion = pTextPortion->GetWidth() - nPosInPortion;
1173 [ # # ][ # # ]: 0 : nCurIndex = mpRefDev->GetTextBreak( pPortion->GetNode()->GetText(), nPosInPortion, nCurIndex );
1174 : : // MT: GetTextBreak should assure that we are not withing a CTL cell...
1175 : : }
1176 : 0 : return nCurIndex;
1177 : : }
1178 : 0 : nCurIndex = nCurIndex + pTextPortion->GetLen();
1179 : : }
1180 : 0 : return nCurIndex;
1181 : : }
1182 : :
1183 : :
1184 : 260 : sal_uLong TextEngine::GetTextHeight() const
1185 : : {
1186 : : DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
1187 : :
1188 [ - + ][ # # ]: 260 : if ( !IsFormatted() && !IsFormatting() )
[ - + ]
1189 : 0 : ((TextEngine*)this)->FormatAndUpdate();
1190 : :
1191 : 260 : return mnCurTextHeight;
1192 : : }
1193 : :
1194 : 0 : sal_uLong TextEngine::GetTextHeight( sal_uLong nParagraph ) const
1195 : : {
1196 : : DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: GetTextHeight" );
1197 : :
1198 [ # # ][ # # ]: 0 : if ( !IsFormatted() && !IsFormatting() )
[ # # ]
1199 : 0 : ((TextEngine*)this)->FormatAndUpdate();
1200 : :
1201 : 0 : return CalcParaHeight( nParagraph );
1202 : : }
1203 : :
1204 : 612 : sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara )
1205 : : {
1206 : 612 : sal_uLong nParaWidth = 0;
1207 : 612 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
1208 [ + + ]: 1118 : for ( sal_uInt16 nLine = pPortion->GetLines().size(); nLine; )
1209 : : {
1210 : 506 : sal_uLong nLineWidth = 0;
1211 : 506 : TextLine* pLine = pPortion->GetLines()[ --nLine ];
1212 [ + + ]: 1012 : for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
1213 : : {
1214 : 506 : TETextPortion* pTextPortion = pPortion->GetTextPortions()[ nTP ];
1215 : 506 : nLineWidth += pTextPortion->GetWidth();
1216 : : }
1217 [ + + ]: 506 : if ( nLineWidth > nParaWidth )
1218 : 112 : nParaWidth = nLineWidth;
1219 : : }
1220 : 612 : return nParaWidth;
1221 : : }
1222 : :
1223 : 262 : sal_uLong TextEngine::CalcTextWidth()
1224 : : {
1225 [ - + ][ # # ]: 262 : if ( !IsFormatted() && !IsFormatting() )
[ - + ]
1226 : 0 : FormatAndUpdate();
1227 : :
1228 [ + + ]: 262 : if ( mnCurTextWidth == 0xFFFFFFFF )
1229 : : {
1230 : 28 : mnCurTextWidth = 0;
1231 [ + + ]: 56 : for ( sal_uLong nPara = mpTEParaPortions->Count(); nPara; )
1232 : : {
1233 : 28 : sal_uLong nParaWidth = CalcTextWidth( --nPara );
1234 [ + + ]: 28 : if ( nParaWidth > mnCurTextWidth )
1235 : 4 : mnCurTextWidth = nParaWidth;
1236 : : }
1237 : : }
1238 : 262 : return mnCurTextWidth+1;// Ein breiter, da in CreateLines bei >= umgebrochen wird.
1239 : : }
1240 : :
1241 : 596 : sal_uLong TextEngine::CalcTextHeight()
1242 : : {
1243 : : DBG_ASSERT( GetUpdateMode(), "Sollte bei Update=sal_False nicht verwendet werden: CalcTextHeight" );
1244 : :
1245 : 596 : sal_uLong nY = 0;
1246 [ + + ]: 1192 : for ( sal_uLong nPortion = mpTEParaPortions->Count(); nPortion; )
1247 : 596 : nY += CalcParaHeight( --nPortion );
1248 : 596 : return nY;
1249 : : }
1250 : :
1251 : 92 : sal_uLong TextEngine::CalcTextWidth( sal_uLong nPara, sal_uInt16 nPortionStart, sal_uInt16 nLen, const Font* pFont )
1252 : : {
1253 : : // Innerhalb des Textes darf es keinen Portionwechsel (Attribut/Tab) geben!
1254 : : DBG_ASSERT( mpDoc->GetNodes().GetObject( nPara )->GetText().Search( '\t', nPortionStart ) >= (nPortionStart+nLen), "CalcTextWidth: Tab!" );
1255 : :
1256 : : sal_uLong nWidth;
1257 [ - + ]: 92 : if ( mnFixCharWidth100 )
1258 : : {
1259 : 0 : nWidth = (sal_uLong)nLen*mnFixCharWidth100/100;
1260 : : }
1261 : : else
1262 : : {
1263 [ - + ]: 92 : if ( pFont )
1264 : : {
1265 [ # # ]: 0 : if ( !mpRefDev->GetFont().IsSameInstance( *pFont ) )
1266 : 0 : mpRefDev->SetFont( *pFont );
1267 : : }
1268 : : else
1269 : : {
1270 [ + - ]: 92 : Font aFont;
1271 [ + - ]: 92 : SeekCursor( nPara, nPortionStart+1, aFont, NULL );
1272 [ + - ][ + - ]: 92 : mpRefDev->SetFont( aFont );
1273 : : }
1274 : 92 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1275 : 92 : nWidth = (sal_uLong)mpRefDev->GetTextWidth( pNode->GetText(), nPortionStart, nLen );
1276 : :
1277 : : }
1278 : 92 : return nWidth;
1279 : : }
1280 : :
1281 : :
1282 : 0 : sal_uInt16 TextEngine::GetLineCount( sal_uLong nParagraph ) const
1283 : : {
1284 : : DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1285 : :
1286 : 0 : TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1287 [ # # ]: 0 : if ( pPPortion )
1288 : 0 : return pPPortion->GetLines().size();
1289 : :
1290 : 0 : return 0xFFFF;
1291 : : }
1292 : :
1293 : 0 : sal_uInt16 TextEngine::GetLineLen( sal_uLong nParagraph, sal_uInt16 nLine ) const
1294 : : {
1295 : : DBG_ASSERT( nParagraph < mpTEParaPortions->Count(), "GetLineCount: Out of range" );
1296 : :
1297 : 0 : TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1298 [ # # ][ # # ]: 0 : if ( pPPortion && ( nLine < pPPortion->GetLines().size() ) )
[ # # ]
1299 : : {
1300 : 0 : TextLine* pLine = pPPortion->GetLines()[ nLine ];
1301 : 0 : return pLine->GetLen();
1302 : : }
1303 : :
1304 : 0 : return 0xFFFF;
1305 : : }
1306 : :
1307 : 1281 : sal_uLong TextEngine::CalcParaHeight( sal_uLong nParagraph ) const
1308 : : {
1309 : 1281 : sal_uLong nHeight = 0;
1310 : :
1311 : 1281 : TEParaPortion* pPPortion = mpTEParaPortions->GetObject( nParagraph );
1312 : : DBG_ASSERT( pPPortion, "Absatz nicht gefunden: GetParaHeight" );
1313 [ + - ]: 1281 : if ( pPPortion )
1314 : 1281 : nHeight = pPPortion->GetLines().size() * mnCharHeight;
1315 : :
1316 : 1281 : return nHeight;
1317 : : }
1318 : :
1319 : 0 : void TextEngine::UpdateSelections()
1320 : : {
1321 : 0 : }
1322 : :
1323 : 596 : Range TextEngine::GetInvalidYOffsets( sal_uLong nPortion )
1324 : : {
1325 : 596 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion );
1326 : 596 : sal_uInt16 nLines = pTEParaPortion->GetLines().size();
1327 : 596 : sal_uInt16 nLastInvalid, nFirstInvalid = 0;
1328 : : sal_uInt16 nLine;
1329 [ + - ]: 596 : for ( nLine = 0; nLine < nLines; nLine++ )
1330 : : {
1331 : 596 : TextLine* pL = pTEParaPortion->GetLines()[ nLine ];
1332 [ + - ]: 596 : if ( pL->IsInvalid() )
1333 : : {
1334 : 596 : nFirstInvalid = nLine;
1335 : 596 : break;
1336 : : }
1337 : : }
1338 : :
1339 [ + + ]: 1192 : for ( nLastInvalid = nFirstInvalid; nLastInvalid < nLines; nLastInvalid++ )
1340 : : {
1341 : 596 : TextLine* pL = pTEParaPortion->GetLines()[ nLine ];
1342 [ - + ]: 596 : if ( pL->IsValid() )
1343 : 0 : break;
1344 : : }
1345 : :
1346 [ + - ]: 596 : if ( nLastInvalid >= nLines )
1347 : 596 : nLastInvalid = nLines-1;
1348 : :
1349 : 596 : return Range( nFirstInvalid*mnCharHeight, ((nLastInvalid+1)*mnCharHeight)-1 );
1350 : : }
1351 : :
1352 : 0 : sal_uLong TextEngine::GetParagraphCount() const
1353 : : {
1354 : 0 : return mpDoc->GetNodes().Count();
1355 : : }
1356 : :
1357 : 346 : void TextEngine::EnableUndo( sal_Bool bEnable )
1358 : : {
1359 : : // Beim Umschalten des Modus Liste loeschen:
1360 [ + - ]: 346 : if ( bEnable != IsUndoEnabled() )
1361 : 346 : ResetUndo();
1362 : :
1363 : 346 : mbUndoEnabled = bEnable;
1364 : 346 : }
1365 : :
1366 : 0 : ::svl::IUndoManager& TextEngine::GetUndoManager()
1367 : : {
1368 [ # # ]: 0 : if ( !mpUndoManager )
1369 [ # # ]: 0 : mpUndoManager = new TextUndoManager( this );
1370 : 0 : return *mpUndoManager;
1371 : : }
1372 : :
1373 : 52 : void TextEngine::UndoActionStart( sal_uInt16 nId )
1374 : : {
1375 [ - + ][ # # ]: 52 : if ( IsUndoEnabled() && !IsInUndo() )
[ - + ]
1376 : : {
1377 [ # # ]: 0 : String aComment;
1378 : : // ...
1379 [ # # ][ # # ]: 0 : GetUndoManager().EnterListAction( aComment, XubString(), nId );
[ # # ][ # # ]
[ # # ]
1380 : : }
1381 : 52 : }
1382 : :
1383 : 52 : void TextEngine::UndoActionEnd()
1384 : : {
1385 [ - + ][ # # ]: 52 : if ( IsUndoEnabled() && !IsInUndo() )
[ - + ]
1386 : 0 : GetUndoManager().LeaveListAction();
1387 : 52 : }
1388 : :
1389 : 0 : void TextEngine::InsertUndo( TextUndo* pUndo, sal_Bool bTryMerge )
1390 : : {
1391 : : DBG_ASSERT( !IsInUndo(), "InsertUndo im Undomodus!" );
1392 : 0 : GetUndoManager().AddUndoAction( pUndo, bTryMerge );
1393 : 0 : }
1394 : :
1395 : 474 : void TextEngine::ResetUndo()
1396 : : {
1397 [ - + ]: 474 : if ( mpUndoManager )
1398 : 0 : mpUndoManager->Clear();
1399 : 474 : }
1400 : :
1401 : 0 : void TextEngine::InsertContent( TextNode* pNode, sal_uLong nPara )
1402 : : {
1403 : : DBG_ASSERT( pNode, "NULL-Pointer in InsertContent! " );
1404 : : DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
1405 [ # # ]: 0 : TEParaPortion* pNew = new TEParaPortion( pNode );
1406 : 0 : mpTEParaPortions->Insert( pNew, nPara );
1407 : 0 : mpDoc->GetNodes().Insert( pNode, nPara );
1408 : 0 : ImpParagraphInserted( nPara );
1409 : 0 : }
1410 : :
1411 : 0 : TextPaM TextEngine::SplitContent( sal_uLong nNode, sal_uInt16 nSepPos )
1412 : : {
1413 : : #ifdef DBG_UTIL
1414 : : TextNode* pNode = mpDoc->GetNodes().GetObject( nNode );
1415 : : DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
1416 : : DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
1417 : : DBG_ASSERT( nSepPos <= pNode->GetText().Len(), "Index im Wald: SplitContent" );
1418 : : #endif
1419 : 0 : TextPaM aPaM( nNode, nSepPos );
1420 [ # # ]: 0 : return ImpInsertParaBreak( aPaM );
1421 : : }
1422 : :
1423 : 0 : TextPaM TextEngine::ConnectContents( sal_uLong nLeftNode )
1424 : : {
1425 : : DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
1426 : 0 : return ImpConnectParagraphs( nLeftNode, nLeftNode+1 );
1427 : : }
1428 : :
1429 : 181 : void TextEngine::SeekCursor( sal_uLong nPara, sal_uInt16 nPos, Font& rFont, OutputDevice* pOutDev )
1430 : : {
1431 : 181 : rFont = maFont;
1432 [ + + ]: 181 : if ( pOutDev )
1433 : 89 : pOutDev->SetTextColor( maTextColor );
1434 : :
1435 : 181 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1436 : 181 : sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1437 [ - + ]: 181 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1438 : : {
1439 : 0 : TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1440 [ # # ]: 0 : if ( pAttrib->GetStart() > nPos )
1441 : 0 : break;
1442 : :
1443 : : // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
1444 : : // Leere Attribute werden beruecksichtigt( verwendet), da diese
1445 : : // gerade eingestellt wurden.
1446 : : // 12.4.95: Doch keine Leeren Attribute verwenden:
1447 : : // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
1448 : : // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
1449 [ # # ]: 0 : if ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
[ # # # # ]
[ # # ]
1450 : 0 : || !pNode->GetText().Len() )
1451 : : {
1452 [ # # ]: 0 : if ( pAttrib->Which() != TEXTATTR_FONTCOLOR )
1453 : : {
1454 : 0 : pAttrib->GetAttr().SetFont(rFont);
1455 : : }
1456 : : else
1457 : : {
1458 [ # # ]: 0 : if ( pOutDev )
1459 : 0 : pOutDev->SetTextColor( ((TextAttribFontColor&)pAttrib->GetAttr()).GetColor() );
1460 : : }
1461 : : }
1462 : : }
1463 : :
1464 [ - + ][ # # ]: 181 : if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) &&
[ # # # #
# # ][ - + ]
1465 : 0 : ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
1466 : : {
1467 : 0 : sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
1468 [ # # ]: 0 : if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
1469 : 0 : rFont.SetUnderline( UNDERLINE_SINGLE );
1470 [ # # ]: 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
1471 : 0 : rFont.SetUnderline( UNDERLINE_BOLD );
1472 [ # # ]: 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
1473 : 0 : rFont.SetUnderline( UNDERLINE_DOTTED );
1474 [ # # ]: 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
1475 : 0 : rFont.SetUnderline( UNDERLINE_DOTTED );
1476 [ # # ]: 0 : if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
1477 [ # # ]: 0 : rFont.SetColor( Color( COL_RED ) );
1478 [ # # ]: 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
1479 [ # # ]: 0 : rFont.SetColor( Color( COL_LIGHTGRAY ) );
1480 [ # # ]: 0 : if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
1481 : : {
1482 : 0 : const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
1483 : 0 : rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
1484 : 0 : rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
1485 : 0 : rFont.SetTransparent( sal_False );
1486 : : }
1487 [ # # ]: 0 : else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
1488 : : {
1489 : 0 : rFont.SetUnderline( UNDERLINE_WAVE );
1490 : : // if( pOut )
1491 : : // pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
1492 : : }
1493 : : }
1494 : 181 : }
1495 : :
1496 : 128 : void TextEngine::FormatAndUpdate( TextView* pCurView )
1497 : : {
1498 [ - + ]: 128 : if ( mbDowning )
1499 : 128 : return ;
1500 : :
1501 [ - + ]: 128 : if ( IsInUndo() )
1502 : 0 : IdleFormatAndUpdate( pCurView );
1503 : : else
1504 : : {
1505 : 128 : FormatDoc();
1506 : 128 : UpdateViews( pCurView );
1507 : : }
1508 : : }
1509 : :
1510 : :
1511 : 0 : void TextEngine::IdleFormatAndUpdate( TextView* pCurView, sal_uInt16 nMaxTimerRestarts )
1512 : : {
1513 : 0 : mpIdleFormatter->DoIdleFormat( pCurView, nMaxTimerRestarts );
1514 : 0 : }
1515 : :
1516 : 52 : void TextEngine::TextModified()
1517 : : {
1518 : 52 : mbFormatted = sal_False;
1519 : 52 : mbModified = sal_True;
1520 : 52 : }
1521 : :
1522 : 596 : void TextEngine::UpdateViews( TextView* pCurView )
1523 : : {
1524 [ + - ][ + - ]: 596 : if ( !GetUpdateMode() || IsFormatting() || maInvalidRec.IsEmpty() )
[ - + ][ - + ]
1525 : 596 : return;
1526 : :
1527 : : DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
1528 : :
1529 [ + + ]: 1102 : for ( sal_uInt16 nView = 0; nView < mpViews->size(); nView++ )
1530 : : {
1531 [ + - ]: 506 : TextView* pView = (*mpViews)[ nView ];
1532 [ + - ]: 506 : pView->HideCursor();
1533 : :
1534 : 506 : Rectangle aClipRec( maInvalidRec );
1535 [ + - ]: 506 : Size aOutSz = pView->GetWindow()->GetOutputSizePixel();
1536 [ + - ][ + - ]: 506 : Rectangle aVisArea( pView->GetStartDocPos(), aOutSz );
1537 [ + - ]: 506 : aClipRec.Intersection( aVisArea );
1538 [ + - ][ + + ]: 506 : if ( !aClipRec.IsEmpty() )
1539 : : {
1540 : : // in Fensterkoordinaten umwandeln....
1541 [ + - ]: 296 : Point aNewPos = pView->GetWindowPos( aClipRec.TopLeft() );
1542 [ - + ]: 296 : if ( IsRightToLeft() )
1543 : 0 : aNewPos.X() -= aOutSz.Width() - 1;
1544 : 296 : aClipRec.SetPos( aNewPos );
1545 : :
1546 [ - + ]: 296 : if ( pView == pCurView )
1547 [ # # ][ # # ]: 0 : pView->ImpPaint( aClipRec, !pView->GetWindow()->IsPaintTransparent() );
[ # # ]
1548 : : else
1549 [ + - ][ + - ]: 296 : pView->GetWindow()->Invalidate( aClipRec );
1550 : : }
1551 : : }
1552 : :
1553 [ - + ]: 596 : if ( pCurView )
1554 : : {
1555 : 0 : pCurView->ShowCursor( pCurView->IsAutoScroll() );
1556 : : }
1557 : :
1558 : 596 : maInvalidRec = Rectangle();
1559 : : }
1560 : :
1561 : 0 : IMPL_LINK_NOARG(TextEngine, IdleFormatHdl)
1562 : : {
1563 : 0 : FormatAndUpdate( mpIdleFormatter->GetView() );
1564 : 0 : return 0;
1565 : : }
1566 : :
1567 : 526 : void TextEngine::CheckIdleFormatter()
1568 : : {
1569 : 526 : mpIdleFormatter->ForceTimeout();
1570 : 526 : }
1571 : :
1572 : 468 : void TextEngine::FormatFullDoc()
1573 : : {
1574 [ + + ]: 936 : for ( sal_uLong nPortion = 0; nPortion < mpTEParaPortions->Count(); nPortion++ )
1575 : : {
1576 : 468 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPortion ); sal_uInt16 nLen = pTEParaPortion->GetNode()->GetText().Len();
1577 : 468 : pTEParaPortion->MarkSelectionInvalid( 0, nLen );
1578 : : }
1579 : 468 : mbFormatted = sal_False;
1580 : 468 : FormatDoc();
1581 : 468 : }
1582 : :
1583 : 596 : void TextEngine::FormatDoc()
1584 : : {
1585 [ + - ][ + - ]: 596 : if ( IsFormatted() || !GetUpdateMode() || IsFormatting() )
[ - + ][ - + ]
1586 : 596 : return;
1587 : :
1588 : 596 : mbIsFormatting = sal_True;
1589 : 596 : mbHasMultiLineParas = sal_False;
1590 : :
1591 : 596 : long nY = 0;
1592 : 596 : sal_Bool bGrow = sal_False;
1593 : :
1594 : 596 : maInvalidRec = Rectangle(); // leermachen
1595 [ + + ]: 1192 : for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
1596 : : {
1597 : 596 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1598 [ + - ]: 596 : if ( pTEParaPortion->IsInvalid() )
1599 : : {
1600 : 596 : sal_uLong nOldParaWidth = 0xFFFFFFFF;
1601 [ + + ]: 596 : if ( mnCurTextWidth != 0xFFFFFFFF )
1602 : 292 : nOldParaWidth = CalcTextWidth( nPara );
1603 : :
1604 : 596 : ImpFormattingParagraph( nPara );
1605 : :
1606 [ + + ]: 596 : if ( CreateLines( nPara ) )
1607 : 218 : bGrow = sal_True;
1608 : :
1609 : : // InvalidRec nur einmal setzen...
1610 [ + - ]: 596 : if ( maInvalidRec.IsEmpty() )
1611 : : {
1612 : : // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
1613 : 596 : long nWidth = (long)mnMaxTextWidth;
1614 [ + + ]: 596 : if ( !nWidth )
1615 : 384 : nWidth = 0x7FFFFFFF;
1616 [ + - ]: 596 : Range aInvRange( GetInvalidYOffsets( nPara ) );
1617 : 596 : maInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
1618 [ + - ]: 1192 : Size( nWidth, aInvRange.Len() ) );
1619 : : }
1620 : : else
1621 : : {
1622 : 0 : maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
1623 : : }
1624 : :
1625 [ + + ]: 596 : if ( mnCurTextWidth != 0xFFFFFFFF )
1626 : : {
1627 : 292 : sal_uLong nNewParaWidth = CalcTextWidth( nPara );
1628 [ + + ]: 292 : if ( nNewParaWidth >= mnCurTextWidth )
1629 : 258 : mnCurTextWidth = nNewParaWidth;
1630 [ + - ][ - + ]: 34 : else if ( ( nOldParaWidth != 0xFFFFFFFF ) && ( nOldParaWidth >= mnCurTextWidth ) )
1631 : 0 : mnCurTextWidth = 0xFFFFFFFF;
1632 : : }
1633 : : }
1634 [ # # ]: 0 : else if ( bGrow )
1635 : : {
1636 : 0 : maInvalidRec.Bottom() = nY + CalcParaHeight( nPara );
1637 : : }
1638 : 596 : nY += CalcParaHeight( nPara );
1639 [ - + ][ - + ]: 596 : if ( !mbHasMultiLineParas && pTEParaPortion->GetLines().size() > 1 )
[ + - ]
1640 : 0 : mbHasMultiLineParas = sal_True;
1641 : : }
1642 : :
1643 [ + - ]: 596 : if ( !maInvalidRec.IsEmpty() )
1644 : : {
1645 : 596 : sal_uLong nNewHeight = CalcTextHeight();
1646 : 596 : long nDiff = nNewHeight - mnCurTextHeight;
1647 [ + + ]: 596 : if ( nNewHeight < mnCurTextHeight )
1648 : : {
1649 : 110 : maInvalidRec.Bottom() = (long)Max( nNewHeight, mnCurTextHeight );
1650 [ - + ]: 110 : if ( maInvalidRec.IsEmpty() )
1651 : : {
1652 : 0 : maInvalidRec.Top() = 0;
1653 : : // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
1654 : 0 : maInvalidRec.Left() = 0;
1655 : 0 : maInvalidRec.Right() = mnMaxTextWidth;
1656 : : }
1657 : : }
1658 : :
1659 : 596 : mnCurTextHeight = nNewHeight;
1660 [ + + ]: 596 : if ( nDiff )
1661 : : {
1662 : 300 : mbFormatted = sal_True;
1663 : 300 : ImpTextHeightChanged();
1664 : : }
1665 : : }
1666 : :
1667 : 596 : mbIsFormatting = sal_False;
1668 : 596 : mbFormatted = sal_True;
1669 : :
1670 : 596 : ImpTextFormatted();
1671 : : }
1672 : :
1673 : 504 : void TextEngine::CreateAndInsertEmptyLine( sal_uLong nPara )
1674 : : {
1675 [ + - ]: 504 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1676 [ + - ]: 504 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1677 : :
1678 [ + - ]: 504 : TextLine* pTmpLine = new TextLine;
1679 : 504 : pTmpLine->SetStart( pNode->GetText().Len() );
1680 : 504 : pTmpLine->SetEnd( pTmpLine->GetStart() );
1681 [ + - ]: 504 : pTEParaPortion->GetLines().push_back( pTmpLine );
1682 : :
1683 [ + - ][ + + ]: 504 : if ( ImpGetAlign() == TXTALIGN_CENTER )
1684 : 144 : pTmpLine->SetStartX( (short)(mnMaxTextWidth / 2) );
1685 [ + - ][ - + ]: 360 : else if ( ImpGetAlign() == TXTALIGN_RIGHT )
1686 : 0 : pTmpLine->SetStartX( (short)mnMaxTextWidth );
1687 : : else
1688 : 360 : pTmpLine->SetStartX( mpDoc->GetLeftMargin() );
1689 : :
1690 [ - + ]: 504 : sal_Bool bLineBreak = pNode->GetText().Len() ? sal_True : sal_False;
1691 : :
1692 [ + - ]: 504 : TETextPortion* pDummyPortion = new TETextPortion( 0 );
1693 : 504 : pDummyPortion->GetWidth() = 0;
1694 [ + - ]: 504 : pTEParaPortion->GetTextPortions().push_back( pDummyPortion );
1695 : :
1696 [ - + ]: 504 : if ( bLineBreak == sal_True )
1697 : : {
1698 : : // -2: The new one is already inserted.
1699 : : OSL_ENSURE(
1700 : : pTEParaPortion->GetLines()[pTEParaPortion->GetLines().size()-2],
1701 : : "Soft Break, no Line?!");
1702 : 0 : sal_uInt16 nPos = (sal_uInt16) pTEParaPortion->GetTextPortions().size() - 1 ;
1703 : 0 : pTmpLine->SetStartPortion( nPos );
1704 : 0 : pTmpLine->SetEndPortion( nPos );
1705 : : }
1706 : 504 : }
1707 : :
1708 : 0 : void TextEngine::ImpBreakLine( sal_uLong nPara, TextLine* pLine, TETextPortion*, sal_uInt16 nPortionStart, long nRemainingWidth )
1709 : : {
1710 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
1711 : :
1712 : : // Font sollte noch eingestellt sein.
1713 [ # # ]: 0 : sal_uInt16 nMaxBreakPos = mpRefDev->GetTextBreak( pNode->GetText(), nRemainingWidth, nPortionStart );
1714 : :
1715 : : DBG_ASSERT( nMaxBreakPos < pNode->GetText().Len(), "Break?!" );
1716 : :
1717 [ # # ]: 0 : if ( nMaxBreakPos == STRING_LEN ) // GetTextBreak() ist anderer Auffassung als GetTextSize()
1718 : 0 : nMaxBreakPos = pNode->GetText().Len() - 1;
1719 : :
1720 [ # # ]: 0 : uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
1721 [ # # ][ # # ]: 0 : i18n::LineBreakHyphenationOptions aHyphOptions( NULL, uno::Sequence< beans::PropertyValue >(), 1 );
[ # # ][ # # ]
1722 : :
1723 : 0 : i18n::LineBreakUserOptions aUserOptions;
1724 [ # # ][ # # ]: 0 : aUserOptions.forbiddenBeginCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().beginLine;
1725 [ # # ][ # # ]: 0 : aUserOptions.forbiddenEndCharacters = ImpGetLocaleDataWrapper()->getForbiddenCharacters().endLine;
1726 : 0 : aUserOptions.applyForbiddenRules = sal_True;
1727 : 0 : aUserOptions.allowPunctuationOutsideMargin = sal_False;
1728 : 0 : aUserOptions.allowHyphenateEnglish = sal_False;
1729 : :
1730 [ # # ][ # # ]: 0 : static const com::sun::star::lang::Locale aDefLocale;
1731 [ # # ][ # # ]: 0 : i18n::LineBreakResults aLBR = xBI->getLineBreak( pNode->GetText(), nMaxBreakPos, aDefLocale, pLine->GetStart(), aHyphOptions, aUserOptions );
[ # # ]
1732 : 0 : sal_uInt16 nBreakPos = (sal_uInt16)aLBR.breakIndex;
1733 [ # # ]: 0 : if ( nBreakPos <= pLine->GetStart() )
1734 : : {
1735 : 0 : nBreakPos = nMaxBreakPos;
1736 [ # # ]: 0 : if ( nBreakPos <= pLine->GetStart() )
1737 : 0 : nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife!
1738 : : }
1739 : :
1740 : :
1741 : : // die angeknackste Portion ist die End-Portion
1742 : 0 : pLine->SetEnd( nBreakPos );
1743 [ # # ]: 0 : sal_uInt16 nEndPortion = SplitTextPortion( nPara, nBreakPos );
1744 : :
1745 : 0 : sal_Bool bBlankSeparator = ( ( nBreakPos >= pLine->GetStart() ) &&
1746 [ # # ][ # # ]: 0 : ( pNode->GetText().GetChar( nBreakPos ) == ' ' ) ) ? sal_True : sal_False;
1747 [ # # ]: 0 : if ( bBlankSeparator )
1748 : : {
1749 : : // Blanks am Zeilenende generell unterdruecken...
1750 [ # # ]: 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1751 : 0 : TETextPortion* pTP = pTEParaPortion->GetTextPortions()[ nEndPortion ];
1752 : : DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
1753 [ # # ]: 0 : pTP->GetWidth() = (long)CalcTextWidth( nPara, nBreakPos-pTP->GetLen(), pTP->GetLen()-1 );
1754 : : }
1755 [ # # ][ # # ]: 0 : pLine->SetEndPortion( nEndPortion );
1756 : 0 : }
1757 : :
1758 : 0 : sal_uInt16 TextEngine::SplitTextPortion( sal_uLong nPara, sal_uInt16 nPos )
1759 : : {
1760 : :
1761 : : // Die Portion bei nPos wird geplittet, wenn bei nPos nicht
1762 : : // sowieso ein Wechsel ist
1763 [ # # ]: 0 : if ( nPos == 0 )
1764 : 0 : return 0;
1765 : :
1766 : : sal_uInt16 nSplitPortion;
1767 : 0 : sal_uInt16 nTmpPos = 0;
1768 : 0 : TETextPortion* pTextPortion = 0;
1769 [ # # ]: 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1770 : 0 : sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().size();
1771 [ # # ]: 0 : for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
1772 : : {
1773 : 0 : TETextPortion* pTP = pTEParaPortion->GetTextPortions()[nSplitPortion];
1774 : 0 : nTmpPos = nTmpPos + pTP->GetLen();
1775 [ # # ]: 0 : if ( nTmpPos >= nPos )
1776 : : {
1777 [ # # ]: 0 : if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden
1778 : 0 : return nSplitPortion;
1779 : 0 : pTextPortion = pTP;
1780 : 0 : break;
1781 : : }
1782 : : }
1783 : :
1784 : : DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
1785 : :
1786 : 0 : sal_uInt16 nOverlapp = nTmpPos - nPos;
1787 : 0 : pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
1788 [ # # ]: 0 : TETextPortion* pNewPortion = new TETextPortion( nOverlapp );
1789 [ # # ][ # # ]: 0 : pTEParaPortion->GetTextPortions().insert( pTEParaPortion->GetTextPortions().begin() + nSplitPortion + 1, pNewPortion );
[ # # ]
1790 [ # # ]: 0 : pTextPortion->GetWidth() = (long)CalcTextWidth( nPara, nPos-pTextPortion->GetLen(), pTextPortion->GetLen() );
1791 : :
1792 : 0 : return nSplitPortion;
1793 : : }
1794 : :
1795 : 92 : void TextEngine::CreateTextPortions( sal_uLong nPara, sal_uInt16 nStartPos )
1796 : : {
1797 [ + - ]: 92 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1798 : 92 : TextNode* pNode = pTEParaPortion->GetNode();
1799 : : DBG_ASSERT( pNode->GetText().Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
1800 : :
1801 [ + - ]: 92 : std::set<sal_uInt16> aPositions;
1802 [ + - ]: 92 : std::set<sal_uInt16>::iterator aPositionsIt;
1803 [ + - ]: 92 : aPositions.insert(0);
1804 : :
1805 : 92 : sal_uInt16 nAttribs = pNode->GetCharAttribs().Count();
1806 [ - + ]: 92 : for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
1807 : : {
1808 [ # # ]: 0 : TextCharAttrib* pAttrib = pNode->GetCharAttribs().GetAttrib( nAttr );
1809 : :
1810 [ # # ]: 0 : aPositions.insert( pAttrib->GetStart() );
1811 [ # # ]: 0 : aPositions.insert( pAttrib->GetEnd() );
1812 : : }
1813 [ + - ]: 92 : aPositions.insert( pNode->GetText().Len() );
1814 : :
1815 : 92 : const std::vector<TEWritingDirectionInfo>& rWritingDirections = pTEParaPortion->GetWritingDirectionInfos();
1816 [ + - ][ + + ]: 184 : for ( std::vector<TEWritingDirectionInfo>::const_iterator it = rWritingDirections.begin(); it != rWritingDirections.end(); ++it )
1817 [ + - ]: 92 : aPositions.insert( (*it).nStartPos );
1818 : :
1819 [ - + ][ # # ]: 92 : if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetPara() == nPara ) )
[ # # ][ - + ]
1820 : : {
1821 : 0 : sal_uInt16 nLastAttr = 0xFFFF;
1822 [ # # ]: 0 : for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
1823 : : {
1824 [ # # ]: 0 : if ( mpIMEInfos->pAttribs[n] != nLastAttr )
1825 : : {
1826 [ # # ]: 0 : aPositions.insert( mpIMEInfos->aPos.GetIndex() + n );
1827 : 0 : nLastAttr = mpIMEInfos->pAttribs[n];
1828 : : }
1829 : : }
1830 : : }
1831 : :
1832 [ + - ]: 92 : sal_uInt16 nTabPos = pNode->GetText().Search( '\t', 0 );
1833 [ - + ]: 92 : while ( nTabPos != STRING_NOTFOUND )
1834 : : {
1835 [ # # ]: 0 : aPositions.insert( nTabPos );
1836 [ # # ]: 0 : aPositions.insert( nTabPos + 1 );
1837 [ # # ]: 0 : nTabPos = pNode->GetText().Search( '\t', nTabPos+1 );
1838 : : }
1839 : :
1840 : : // Ab ... loeschen:
1841 : : // Leider muss die Anzahl der TextPortions mit aPositions.Count()
1842 : : // nicht uebereinstimmen, da evtl. Zeilenumbrueche...
1843 : 92 : sal_uInt16 nPortionStart = 0;
1844 : 92 : sal_uInt16 nInvPortion = 0;
1845 : : sal_uInt16 nP;
1846 [ + + ]: 92 : for ( nP = 0; nP < pTEParaPortion->GetTextPortions().size(); nP++ )
1847 : : {
1848 : 40 : TETextPortion* pTmpPortion = pTEParaPortion->GetTextPortions()[nP];
1849 : 40 : nPortionStart = nPortionStart + pTmpPortion->GetLen();
1850 [ + - ]: 40 : if ( nPortionStart >= nStartPos )
1851 : : {
1852 : 40 : nPortionStart = nPortionStart - pTmpPortion->GetLen();
1853 : 40 : nInvPortion = nP;
1854 : 40 : break;
1855 : : }
1856 : : }
1857 : : OSL_ENSURE(nP < pTEParaPortion->GetTextPortions().size()
1858 : : || pTEParaPortion->GetTextPortions().empty(),
1859 : : "Nothing to delete: CreateTextPortions");
1860 [ - + ][ # # ]: 92 : if ( nInvPortion && ( nPortionStart+pTEParaPortion->GetTextPortions()[nInvPortion]->GetLen() > nStartPos ) )
[ - + ]
1861 : : {
1862 : : // lieber eine davor...
1863 : : // Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
1864 : : // die einzige in der Zeile davor !
1865 : 0 : nInvPortion--;
1866 : 0 : nPortionStart = nPortionStart - pTEParaPortion->GetTextPortions()[nInvPortion]->GetLen();
1867 : : }
1868 [ + - ]: 92 : pTEParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
1869 : :
1870 : : // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
1871 [ + - ]: 92 : aPositions.insert( nPortionStart );
1872 : :
1873 [ + - ]: 92 : aPositionsIt = aPositions.find( nPortionStart );
1874 : : DBG_ASSERT( aPositionsIt != aPositions.end(), "nPortionStart not found" );
1875 : :
1876 [ + - ][ + - ]: 92 : if ( aPositionsIt != aPositions.end() )
1877 : : {
1878 : 92 : std::set<sal_uInt16>::iterator nextIt = aPositionsIt;
1879 [ + - ][ + - ]: 184 : for ( ++nextIt; nextIt != aPositions.end(); ++aPositionsIt, ++nextIt )
[ + - ][ + - ]
[ + + ]
1880 : : {
1881 [ + - ][ + - ]: 92 : TETextPortion* pNew = new TETextPortion( *nextIt - *aPositionsIt );
[ + - ]
1882 [ + - ]: 92 : pTEParaPortion->GetTextPortions().push_back( pNew );
1883 : : }
1884 : : }
1885 : 92 : OSL_ENSURE(pTEParaPortion->GetTextPortions().size(), "No Portions?!");
1886 : 92 : }
1887 : :
1888 : 0 : void TextEngine::RecalcTextPortion( sal_uLong nPara, sal_uInt16 nStartPos, short nNewChars )
1889 : : {
1890 : 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
1891 : : OSL_ENSURE(pTEParaPortion->GetTextPortions().size(), "no Portions!");
1892 : : OSL_ENSURE(nNewChars, "RecalcTextPortion with Diff == 0");
1893 : :
1894 : 0 : TextNode* const pNode = pTEParaPortion->GetNode();
1895 [ # # ]: 0 : if ( nNewChars > 0 )
1896 : : {
1897 : : // Wenn an nStartPos ein Attribut beginnt/endet, oder vor nStartPos
1898 : : // ein Tab steht, faengt eine neue Portion an,
1899 : : // ansonsten wird die Portion an nStartPos erweitert.
1900 : : // Oder wenn ganz vorne ( StartPos 0 ) und dann ein Tab
1901 : :
1902 [ # # ]: 0 : if ( ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) ) ||
[ # # # # ]
[ # # # #
# # ][ # # ]
1903 : 0 : ( nStartPos && ( pNode->GetText().GetChar( nStartPos - 1 ) == '\t' ) ) ||
1904 : 0 : ( ( !nStartPos && ( nNewChars < pNode->GetText().Len() ) && pNode->GetText().GetChar( nNewChars ) == '\t' ) ) )
1905 : : {
1906 : 0 : sal_uInt16 nNewPortionPos = 0;
1907 [ # # ]: 0 : if ( nStartPos )
1908 : 0 : nNewPortionPos = SplitTextPortion( nPara, nStartPos ) + 1;
1909 : :
1910 : : // Eine leere Portion kann hier stehen, wenn der Absatz leer war,
1911 : : // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
1912 [ # # # # ]: 0 : if ( ( nNewPortionPos < pTEParaPortion->GetTextPortions().size() ) &&
[ # # ]
1913 : 0 : !pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
1914 : : {
1915 : : // Dann die leere Portion verwenden.
1916 : : sal_uInt16 & r =
1917 : 0 : pTEParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
1918 : 0 : r = r + nNewChars;
1919 : : }
1920 : : else
1921 : : {
1922 [ # # ]: 0 : TETextPortion* pNewPortion = new TETextPortion( nNewChars );
1923 [ # # ][ # # ]: 0 : pTEParaPortion->GetTextPortions().insert( pTEParaPortion->GetTextPortions().begin() + nNewPortionPos, pNewPortion );
1924 : : }
1925 : : }
1926 : : else
1927 : : {
1928 : : sal_uInt16 nPortionStart;
1929 : 0 : const sal_uInt16 nTP = pTEParaPortion->GetTextPortions().
1930 [ # # ]: 0 : FindPortion( nStartPos, nPortionStart );
1931 : 0 : TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
1932 : : DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
1933 : 0 : pTP->GetLen() = pTP->GetLen() + nNewChars;
1934 : 0 : pTP->GetWidth() = (-1);
1935 : : }
1936 : : }
1937 : : else
1938 : : {
1939 : : // Portion schrumpfen oder ggf. entfernen.
1940 : : // Vor Aufruf dieser Methode muss sichergestellt sein, dass
1941 : : // keine Portions in dem geloeschten Bereich lagen!
1942 : :
1943 : : // Es darf keine reinragende oder im Bereich startende Portion geben,
1944 : : // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
1945 : 0 : sal_uInt16 nPortion = 0;
1946 : 0 : sal_uInt16 nPos = 0;
1947 : 0 : sal_uInt16 nEnd = nStartPos-nNewChars;
1948 : 0 : sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().size();
1949 : 0 : TETextPortion* pTP = 0;
1950 [ # # ]: 0 : for ( nPortion = 0; nPortion < nPortions; nPortion++ )
1951 : : {
1952 : 0 : pTP = pTEParaPortion->GetTextPortions()[ nPortion ];
1953 [ # # ]: 0 : if ( ( nPos+pTP->GetLen() ) > nStartPos )
1954 : : {
1955 : : DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
1956 : : DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
1957 : 0 : break;
1958 : : }
1959 : 0 : nPos = nPos + pTP->GetLen();
1960 : : }
1961 : : DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
1962 [ # # ][ # # ]: 0 : if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
[ # # ]
1963 : : {
1964 : : // Portion entfernen;
1965 [ # # ][ # # ]: 0 : pTEParaPortion->GetTextPortions().erase( pTEParaPortion->GetTextPortions().begin() + nPortion );
1966 : 0 : delete pTP;
1967 : : }
1968 : : else
1969 : : {
1970 : : DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
1971 : 0 : pTP->GetLen() = pTP->GetLen() + nNewChars;
1972 : : }
1973 : : OSL_ENSURE( pTEParaPortion->GetTextPortions().size(),
1974 : : "RecalcTextPortions: none are left!" );
1975 : : }
1976 : 0 : }
1977 : :
1978 : 89 : void TextEngine::ImpPaint( OutputDevice* pOutDev, const Point& rStartPos, Rectangle const* pPaintArea, TextSelection const* pPaintRange, TextSelection const* pSelection )
1979 : : {
1980 [ - + ]: 89 : if ( !GetUpdateMode() )
1981 : 0 : return;
1982 : :
1983 [ - + ]: 89 : if ( !IsFormatted() )
1984 : 0 : FormatDoc();
1985 : :
1986 : 89 : bool bTransparent = false;
1987 [ - + ]: 89 : Window* pOutWin = dynamic_cast<Window*>(pOutDev);
1988 [ + - ][ + + ]: 89 : bTransparent = (pOutWin && pOutWin->IsPaintTransparent());
1989 : :
1990 : 89 : long nY = rStartPos.Y();
1991 : :
1992 : 89 : TextPaM const* pSelStart = 0;
1993 : 89 : TextPaM const* pSelEnd = 0;
1994 [ # # ][ - + ]: 89 : if ( pSelection && pSelection->HasRange() )
[ - + ]
1995 : : {
1996 : 0 : sal_Bool bInvers = pSelection->GetEnd() < pSelection->GetStart();
1997 [ # # ]: 0 : pSelStart = !bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
1998 [ # # ]: 0 : pSelEnd = bInvers ? &pSelection->GetStart() : &pSelection->GetEnd();
1999 : : }
2000 : : DBG_ASSERT( !pPaintRange || ( pPaintRange->GetStart() < pPaintRange->GetEnd() ), "ImpPaint: Paint-Range?!" );
2001 : :
2002 : 89 : const StyleSettings& rStyleSettings = pOutDev->GetSettings().GetStyleSettings();
2003 : :
2004 : : // --------------------------------------------------
2005 : : // Ueber alle Absaetze...
2006 : : // --------------------------------------------------
2007 [ + + ]: 178 : for ( sal_uLong nPara = 0; nPara < mpTEParaPortions->Count(); nPara++ )
2008 : : {
2009 : 89 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
2010 : : // falls beim Tippen Idle-Formatierung, asynchrones Paint.
2011 [ - + ]: 89 : if ( pPortion->IsInvalid() )
2012 : 0 : return;
2013 : :
2014 : 89 : sal_uLong nParaHeight = CalcParaHeight( nPara );
2015 [ + - ][ - + : 89 : if ( ( !pPaintArea || ( ( nY + (long)nParaHeight ) > pPaintArea->Top() ) )
# # # # ]
[ + - ][ + - ]
2016 : 0 : && ( !pPaintRange || ( ( nPara >= pPaintRange->GetStart().GetPara() ) && ( nPara <= pPaintRange->GetEnd().GetPara() ) ) ) )
2017 : : {
2018 : : // --------------------------------------------------
2019 : : // Ueber die Zeilen des Absatzes...
2020 : : // --------------------------------------------------
2021 : 89 : sal_uInt16 nLines = pPortion->GetLines().size();
2022 : 89 : sal_uInt16 nIndex = 0;
2023 [ + + ]: 178 : for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
2024 : : {
2025 : 89 : TextLine* pLine = pPortion->GetLines()[nLine];
2026 : 89 : Point aTmpPos( rStartPos.X() + pLine->GetStartX(), nY );
2027 : :
2028 [ + - ][ - + : 178 : if ( ( !pPaintArea || ( ( nY + mnCharHeight ) > pPaintArea->Top() ) )
# # # # ]
[ + - ][ + - ]
2029 : : && ( !pPaintRange || (
2030 [ - + ]: 89 : ( TextPaM( nPara, pLine->GetStart() ) < pPaintRange->GetEnd() ) &&
2031 [ - + ]: 89 : ( TextPaM( nPara, pLine->GetEnd() ) > pPaintRange->GetStart() ) ) ) )
2032 : : {
2033 : : // --------------------------------------------------
2034 : : // Ueber die Portions der Zeile...
2035 : : // --------------------------------------------------
2036 : 89 : nIndex = pLine->GetStart();
2037 [ + + ]: 178 : for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
2038 : : {
2039 : : OSL_ENSURE(pPortion->GetTextPortions().size(),
2040 : : "Line without Textportion in Paint!");
2041 : 89 : TETextPortion* pTextPortion = pPortion->GetTextPortions()[ y ];
2042 : : DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
2043 : :
2044 [ + - ]: 89 : ImpInitLayoutMode( pOutDev /*, pTextPortion->IsRightToLeft() */);
2045 : :
2046 : 89 : long nTxtWidth = pTextPortion->GetWidth();
2047 [ + - ]: 89 : aTmpPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nIndex, nIndex );
2048 : :
2049 : : // nur ausgeben, was im sichtbaren Bereich beginnt:
2050 [ + - ][ - + : 178 : if ( ( ( aTmpPos.X() + nTxtWidth ) >= 0 )
# # # # ]
[ + - ]
2051 : : && ( !pPaintRange || (
2052 [ - + ]: 89 : ( TextPaM( nPara, nIndex ) < pPaintRange->GetEnd() ) &&
2053 [ - + ]: 89 : ( TextPaM( nPara, nIndex + pTextPortion->GetLen() ) > pPaintRange->GetStart() ) ) ) )
2054 : : {
2055 [ + - - ]: 89 : switch ( pTextPortion->GetKind() )
2056 : : {
2057 : : case PORTIONKIND_TEXT:
2058 : : {
2059 : : {
2060 [ + - ]: 89 : Font aFont;
2061 [ + - ]: 89 : SeekCursor( nPara, nIndex+1, aFont, pOutDev );
2062 [ + + ]: 89 : if( bTransparent )
2063 [ + - ]: 48 : aFont.SetTransparent( sal_True );
2064 [ - + ]: 41 : else if ( pSelection )
2065 [ # # ]: 0 : aFont.SetTransparent( sal_False );
2066 [ + - ]: 89 : pOutDev->SetFont( aFont );
2067 : :
2068 : 89 : sal_uInt16 nTmpIndex = nIndex;
2069 : 89 : sal_uInt16 nEnd = nTmpIndex + pTextPortion->GetLen();
2070 : 89 : Point aPos = aTmpPos;
2071 [ - + ]: 89 : if ( pPaintRange )
2072 : : {
2073 : : // evtl soll nicht alles ausgegeben werden...
2074 [ # # # # ]: 0 : if ( ( pPaintRange->GetStart().GetPara() == nPara )
[ # # ]
2075 : 0 : && ( nTmpIndex < pPaintRange->GetStart().GetIndex() ) )
2076 : : {
2077 : 0 : nTmpIndex = pPaintRange->GetStart().GetIndex();
2078 : : }
2079 [ # # # # ]: 0 : if ( ( pPaintRange->GetEnd().GetPara() == nPara )
[ # # ]
2080 : 0 : && ( nEnd > pPaintRange->GetEnd().GetIndex() ) )
2081 : : {
2082 : 0 : nEnd = pPaintRange->GetEnd().GetIndex();
2083 : : }
2084 : : }
2085 : :
2086 : 89 : sal_Bool bDone = sal_False;
2087 [ - + ]: 89 : if ( pSelStart )
2088 : : {
2089 : : // liegt ein Teil in der Selektion???
2090 : 0 : TextPaM aTextStart( nPara, nTmpIndex );
2091 : 0 : TextPaM aTextEnd( nPara, nEnd );
2092 [ # # ][ # # ]: 0 : if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
[ # # ]
2093 : : {
2094 : : sal_uInt16 nL;
2095 : :
2096 : : // 1) Bereich vor Selektion
2097 [ # # ]: 0 : if ( aTextStart < *pSelStart )
2098 : : {
2099 : 0 : nL = pSelStart->GetIndex() - nTmpIndex;
2100 [ # # ]: 0 : pOutDev->SetFont( aFont);
2101 [ # # ]: 0 : aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2102 [ # # ]: 0 : pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2103 : 0 : nTmpIndex = nTmpIndex + nL;
2104 : :
2105 : : }
2106 : : // 2) Bereich mit Selektion
2107 : 0 : nL = nEnd-nTmpIndex;
2108 [ # # ]: 0 : if ( aTextEnd > *pSelEnd )
2109 : 0 : nL = pSelEnd->GetIndex() - nTmpIndex;
2110 [ # # ]: 0 : if ( nL )
2111 : : {
2112 : 0 : Color aOldTextColor = pOutDev->GetTextColor();
2113 [ # # ]: 0 : pOutDev->SetTextColor( rStyleSettings.GetHighlightTextColor() );
2114 [ # # ]: 0 : pOutDev->SetTextFillColor( rStyleSettings.GetHighlightColor() );
2115 [ # # ]: 0 : aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2116 [ # # ]: 0 : pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nL );
2117 [ # # ]: 0 : pOutDev->SetTextColor( aOldTextColor );
2118 [ # # ]: 0 : pOutDev->SetTextFillColor();
2119 : 0 : nTmpIndex = nTmpIndex + nL;
2120 : : }
2121 : :
2122 : : // 3) Bereich nach Selektion
2123 [ # # ]: 0 : if ( nTmpIndex < nEnd )
2124 : : {
2125 : 0 : nL = nEnd-nTmpIndex;
2126 [ # # ]: 0 : aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nTmpIndex+nL );
2127 [ # # ]: 0 : pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2128 : : }
2129 : 0 : bDone = sal_True;
2130 : : }
2131 : : }
2132 [ + - ]: 89 : if ( !bDone )
2133 : : {
2134 [ + - ]: 89 : aPos.X() = rStartPos.X() + ImpGetOutputOffset( nPara, pLine, nTmpIndex, nEnd );
2135 [ + - ]: 89 : pOutDev->DrawText( aPos, pPortion->GetNode()->GetText(), nTmpIndex, nEnd-nTmpIndex );
2136 [ + - ]: 89 : }
2137 : : }
2138 : :
2139 : : }
2140 : 89 : break;
2141 : : case PORTIONKIND_TAB:
2142 : : {
2143 : : // Bei HideSelection() nur Range, pSelection = 0.
2144 [ # # ][ # # ]: 0 : if ( pSelStart || pPaintRange )
2145 : : {
2146 [ # # ]: 0 : Rectangle aTabArea( aTmpPos, Point( aTmpPos.X()+nTxtWidth, aTmpPos.Y()+mnCharHeight-1 ) );
2147 : 0 : sal_Bool bDone = sal_False;
2148 [ # # ]: 0 : if ( pSelStart )
2149 : : {
2150 : : // liegt der Tab in der Selektion???
2151 : 0 : TextPaM aTextStart( nPara, nIndex );
2152 : 0 : TextPaM aTextEnd( nPara, nIndex+1 );
2153 [ # # ][ # # ]: 0 : if ( ( aTextStart < *pSelEnd ) && ( aTextEnd > *pSelStart ) )
[ # # ]
2154 : : {
2155 : 0 : Color aOldColor = pOutDev->GetFillColor();
2156 [ # # ]: 0 : pOutDev->SetFillColor( rStyleSettings.GetHighlightColor() );
2157 [ # # ]: 0 : pOutDev->DrawRect( aTabArea );
2158 [ # # ]: 0 : pOutDev->SetFillColor( aOldColor );
2159 : 0 : bDone = sal_True;
2160 : : }
2161 : : }
2162 [ # # ]: 0 : if ( !bDone )
2163 : : {
2164 [ # # ]: 0 : pOutDev->Erase( aTabArea );
2165 : : }
2166 : : }
2167 : : }
2168 : 89 : break;
2169 : : default: OSL_FAIL( "ImpPaint: Unknown Portion-Type !" );
2170 : : }
2171 : : }
2172 : :
2173 : 89 : nIndex = nIndex + pTextPortion->GetLen();
2174 : : }
2175 : : }
2176 : :
2177 : 89 : nY += mnCharHeight;
2178 : :
2179 [ + - ][ + + ]: 89 : if ( pPaintArea && ( nY >= pPaintArea->Bottom() ) )
[ + + ]
2180 : : break; // keine sichtbaren Aktionen mehr...
2181 : : }
2182 : : }
2183 : : else
2184 : : {
2185 : 0 : nY += nParaHeight;
2186 : : }
2187 : :
2188 [ + - ][ + + ]: 89 : if ( pPaintArea && ( nY > pPaintArea->Bottom() ) )
[ + + ]
2189 : 6 : break; // keine sichtbaren Aktionen mehr...
2190 : : }
2191 : : }
2192 : :
2193 : 596 : sal_Bool TextEngine::CreateLines( sal_uLong nPara )
2194 : : {
2195 : : // sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False
2196 : :
2197 [ + - ]: 596 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2198 [ + - ]: 596 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2199 : : DBG_ASSERT( pTEParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
2200 : :
2201 : 596 : sal_uInt16 nOldLineCount = pTEParaPortion->GetLines().size();
2202 : :
2203 : : // ---------------------------------------------------------------
2204 : : // Schnelle Sonderbehandlung fuer leere Absaetze...
2205 : : // ---------------------------------------------------------------
2206 [ + + ]: 596 : if ( pTEParaPortion->GetNode()->GetText().Len() == 0 )
2207 : : {
2208 : : // schnelle Sonderbehandlung...
2209 [ + + ]: 504 : if ( !pTEParaPortion->GetTextPortions().empty() )
2210 [ + - ]: 338 : pTEParaPortion->GetTextPortions().Reset();
2211 [ + + ]: 504 : if ( !pTEParaPortion->GetLines().empty() )
2212 : : {
2213 [ + - ][ + - ]: 1014 : BOOST_FOREACH(TextLine* pLine, pTEParaPortion->GetLines())
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ]
2214 : 338 : delete pLine;
2215 : 338 : pTEParaPortion->GetLines().clear();
2216 : : }
2217 [ + - ]: 504 : CreateAndInsertEmptyLine( nPara );
2218 : 504 : pTEParaPortion->SetValid();
2219 : 504 : return nOldLineCount != pTEParaPortion->GetLines().size();
2220 : : }
2221 : :
2222 : : // ---------------------------------------------------------------
2223 : : // Initialisierung......
2224 : : // ---------------------------------------------------------------
2225 : :
2226 [ + + ]: 92 : if ( pTEParaPortion->GetLines().empty() )
2227 : : {
2228 [ + - ]: 52 : TextLine* pL = new TextLine;
2229 [ + - ]: 52 : pTEParaPortion->GetLines().push_back( pL );
2230 : : }
2231 : :
2232 : 92 : const short nInvalidDiff = pTEParaPortion->GetInvalidDiff();
2233 : 92 : const sal_uInt16 nInvalidStart = pTEParaPortion->GetInvalidPosStart();
2234 : 92 : const sal_uInt16 nInvalidEnd = nInvalidStart + Abs( nInvalidDiff );
2235 : 92 : sal_Bool bQuickFormat = sal_False;
2236 : :
2237 [ + - ]: 92 : if ( pTEParaPortion->GetWritingDirectionInfos().empty() )
2238 [ + - ]: 92 : ImpInitWritingDirections( nPara );
2239 : :
2240 [ + - ]: 92 : if ( pTEParaPortion->GetWritingDirectionInfos().size() == 1 )
2241 : : {
2242 [ - + ][ # # ]: 92 : if ( pTEParaPortion->IsSimpleInvalid() && ( nInvalidDiff > 0 ) )
[ - + ]
2243 : : {
2244 : 0 : bQuickFormat = sal_True;
2245 : : }
2246 [ - + ][ # # ]: 92 : else if ( ( pTEParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
[ - + ]
2247 : : {
2248 : : // pruefen, ob loeschen ueber Portiongrenzen erfolgte...
2249 : 0 : sal_uInt16 nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!!
2250 : 0 : sal_uInt16 nEnd = nStart - nInvalidDiff; // neg.
2251 : 0 : bQuickFormat = sal_True;
2252 : 0 : sal_uInt16 nPos = 0;
2253 : 0 : sal_uInt16 nPortions = pTEParaPortion->GetTextPortions().size();
2254 [ # # ]: 0 : for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
2255 : : {
2256 : : // Es darf kein Start/Ende im geloeschten Bereich liegen.
2257 : 0 : TETextPortion* const pTP = pTEParaPortion->GetTextPortions()[ nTP ];
2258 : 0 : nPos = nPos + pTP->GetLen();
2259 [ # # ][ # # ]: 0 : if ( ( nPos > nStart ) && ( nPos < nEnd ) )
2260 : : {
2261 : 0 : bQuickFormat = sal_False;
2262 : 0 : break;
2263 : : }
2264 : : }
2265 : : }
2266 : : }
2267 : :
2268 [ - + ]: 92 : if ( bQuickFormat )
2269 [ # # ]: 0 : RecalcTextPortion( nPara, nInvalidStart, nInvalidDiff );
2270 : : else
2271 [ + - ]: 92 : CreateTextPortions( nPara, nInvalidStart );
2272 : :
2273 : : // ---------------------------------------------------------------
2274 : : // Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
2275 : : // Zeilen flaggen => nicht removen !
2276 : : // ---------------------------------------------------------------
2277 : :
2278 : 92 : sal_uInt16 nLine = pTEParaPortion->GetLines().size()-1;
2279 [ + + ]: 144 : for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
2280 : : {
2281 : 92 : TextLine* pLine = pTEParaPortion->GetLines()[ nL ];
2282 [ + + ]: 92 : if ( pLine->GetEnd() > nInvalidStart )
2283 : : {
2284 : 40 : nLine = nL;
2285 : 40 : break;
2286 : : }
2287 : 52 : pLine->SetValid();
2288 : : }
2289 : : // Eine Zeile davor beginnen...
2290 : : // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
2291 [ - + ][ # # ]: 92 : if ( nLine && ( !pTEParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->GetText().Len() ) || ( nInvalidDiff <= 0 ) ) )
[ # # ][ # # ]
[ - + ]
2292 : 0 : nLine--;
2293 : :
2294 : 92 : TextLine* pLine = pTEParaPortion->GetLines()[ nLine ];
2295 : :
2296 : : // ---------------------------------------------------------------
2297 : : // Ab hier alle Zeilen durchformatieren...
2298 : : // ---------------------------------------------------------------
2299 : 92 : size_t nDelFromLine = std::numeric_limits<size_t>::max();
2300 : 92 : sal_Bool bLineBreak = sal_False;
2301 : :
2302 : 92 : sal_uInt16 nIndex = pLine->GetStart();
2303 : 92 : TextLine aSaveLine( *pLine );
2304 : :
2305 [ + - ]: 92 : Font aFont;
2306 : :
2307 : 92 : sal_Bool bCalcPortion = sal_True;
2308 : :
2309 [ + + ]: 184 : while ( nIndex < pNode->GetText().Len() )
2310 : : {
2311 : 92 : sal_Bool bEOL = sal_False;
2312 : 92 : sal_uInt16 nPortionStart = 0;
2313 : 92 : sal_uInt16 nPortionEnd = 0;
2314 : :
2315 : 92 : sal_uInt16 nTmpPos = nIndex;
2316 : 92 : sal_uInt16 nTmpPortion = pLine->GetStartPortion();
2317 : 92 : long nTmpWidth = mpDoc->GetLeftMargin();
2318 : : // long nXWidth = mnMaxTextWidth ? ( mnMaxTextWidth - mpDoc->GetLeftMargin() ) : 0x7FFFFFFF;
2319 : : // Margin nicht abziehen, ist schon in TmpWidth enthalten.
2320 [ + + ]: 92 : long nXWidth = mnMaxTextWidth ? mnMaxTextWidth : 0x7FFFFFFF;
2321 [ - + ]: 92 : if ( nXWidth < nTmpWidth )
2322 : 0 : nXWidth = nTmpWidth;
2323 : :
2324 : : // Portion suchen, die nicht mehr in Zeile passt....
2325 : 92 : TETextPortion* pPortion = 0;
2326 : 92 : sal_Bool bBrokenLine = sal_False;
2327 : 92 : bLineBreak = sal_False;
2328 : :
2329 [ + - ][ + - ]: 184 : while ( ( nTmpWidth <= nXWidth ) && !bEOL && ( nTmpPortion < pTEParaPortion->GetTextPortions().size() ) )
[ + + ][ + + ]
2330 : : {
2331 : 92 : nPortionStart = nTmpPos;
2332 : 92 : pPortion = pTEParaPortion->GetTextPortions()[ nTmpPortion ];
2333 : : DBG_ASSERT( pPortion->GetLen(), "Leere Portion in CreateLines ?!" );
2334 [ - + ]: 92 : if ( pNode->GetText().GetChar( nTmpPos ) == '\t' )
2335 : : {
2336 : 0 : long nCurPos = nTmpWidth-mpDoc->GetLeftMargin();
2337 : 0 : nTmpWidth = ((nCurPos/mnDefTab)+1)*mnDefTab+mpDoc->GetLeftMargin();
2338 : 0 : pPortion->GetWidth() = nTmpWidth - nCurPos - mpDoc->GetLeftMargin();
2339 : : // Wenn dies das erste Token in der Zeile ist, und
2340 : : // nTmpWidth > aPaperSize.Width, habe ich eine Endlos-Schleife!
2341 [ # # ][ # # ]: 0 : if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
[ # # ]
2342 : : {
2343 : : // Aber was jetzt ? Tab passend machen!
2344 : 0 : pPortion->GetWidth() = nXWidth-1;
2345 : 0 : nTmpWidth = pPortion->GetWidth();
2346 : 0 : bEOL = sal_True;
2347 : 0 : bBrokenLine = sal_True;
2348 : : }
2349 : 0 : pPortion->GetKind() = PORTIONKIND_TAB;
2350 : : }
2351 : : else
2352 : : {
2353 : :
2354 [ - + ][ # # ]: 92 : if ( bCalcPortion || !pPortion->HasValidSize() )
[ + - ]
2355 [ + - ]: 92 : pPortion->GetWidth() = (long)CalcTextWidth( nPara, nTmpPos, pPortion->GetLen() );
2356 : 92 : nTmpWidth += pPortion->GetWidth();
2357 : :
2358 [ + - ]: 92 : pPortion->GetRightToLeft() = ImpGetRightToLeft( nPara, nTmpPos+1 );
2359 : 92 : pPortion->GetKind() = PORTIONKIND_TEXT;
2360 : : }
2361 : :
2362 : 92 : nTmpPos = nTmpPos + pPortion->GetLen();
2363 : 92 : nPortionEnd = nTmpPos;
2364 : 92 : nTmpPortion++;
2365 : : }
2366 : :
2367 : : // das war evtl. eine Portion zu weit:
2368 : 92 : sal_Bool bFixedEnd = sal_False;
2369 [ - + ]: 92 : if ( nTmpWidth > nXWidth )
2370 : : {
2371 : 0 : nPortionEnd = nTmpPos;
2372 : 0 : nTmpPos = nTmpPos - pPortion->GetLen();
2373 : 0 : nPortionStart = nTmpPos;
2374 : 0 : nTmpPortion--;
2375 : 0 : bEOL = sal_False;
2376 : :
2377 : 0 : nTmpWidth -= pPortion->GetWidth();
2378 [ # # ]: 0 : if ( pPortion->GetKind() == PORTIONKIND_TAB )
2379 : : {
2380 : 0 : bEOL = sal_True;
2381 : 0 : bFixedEnd = sal_True;
2382 : : }
2383 : : }
2384 : : else
2385 : : {
2386 : 92 : bEOL = sal_True;
2387 : 92 : pLine->SetEnd( nPortionEnd );
2388 : : OSL_ENSURE(pTEParaPortion->GetTextPortions().size(),
2389 : : "No TextPortions?");
2390 : 92 : pLine->SetEndPortion( (sal_uInt16)pTEParaPortion->GetTextPortions().size() - 1 );
2391 : : }
2392 : :
2393 [ - + ]: 92 : if ( bFixedEnd )
2394 : : {
2395 : 0 : pLine->SetEnd( nPortionStart );
2396 : 0 : pLine->SetEndPortion( nTmpPortion-1 );
2397 : : }
2398 [ + - ][ - + ]: 92 : else if ( bLineBreak || bBrokenLine )
2399 : : {
2400 : 0 : pLine->SetEnd( nPortionStart+1 );
2401 : 0 : pLine->SetEndPortion( nTmpPortion-1 );
2402 : : }
2403 [ - + ]: 92 : else if ( !bEOL )
2404 : : {
2405 : : DBG_ASSERT( (nPortionEnd-nPortionStart) == pPortion->GetLen(), "Doch eine andere Portion?!" );
2406 : 0 : long nRemainingWidth = mnMaxTextWidth - nTmpWidth;
2407 [ # # ]: 0 : ImpBreakLine( nPara, pLine, pPortion, nPortionStart, nRemainingWidth );
2408 : : }
2409 : :
2410 [ + - ][ + + ]: 92 : if ( ( ImpGetAlign() == TXTALIGN_CENTER ) || ( ImpGetAlign() == TXTALIGN_RIGHT ) )
[ + - ][ - + ]
[ + + ]
2411 : : {
2412 : : // Ausrichten...
2413 : 32 : long nTextWidth = 0;
2414 [ + + ]: 64 : for ( sal_uInt16 nTP = pLine->GetStartPortion(); nTP <= pLine->GetEndPortion(); nTP++ )
2415 : : {
2416 : 32 : TETextPortion* pTextPortion = pTEParaPortion->GetTextPortions()[ nTP ];
2417 : 32 : nTextWidth += pTextPortion->GetWidth();
2418 : : }
2419 : 32 : long nSpace = mnMaxTextWidth - nTextWidth;
2420 [ + + ]: 32 : if ( nSpace > 0 )
2421 : : {
2422 [ + - ][ + - ]: 6 : if ( ImpGetAlign() == TXTALIGN_CENTER )
2423 : 6 : pLine->SetStartX( (sal_uInt16)(nSpace / 2) );
2424 : : else // TXTALIGN_RIGHT
2425 : 0 : pLine->SetStartX( (sal_uInt16)nSpace );
2426 : : }
2427 : : }
2428 : : else
2429 : : {
2430 : 60 : pLine->SetStartX( mpDoc->GetLeftMargin() );
2431 : : }
2432 : :
2433 : : // -----------------------------------------------------------------
2434 : : // pruefen, ob die Zeile neu ausgegeben werden muss...
2435 : : // -----------------------------------------------------------------
2436 : 92 : pLine->SetInvalid();
2437 : :
2438 [ - + ]: 92 : if ( pTEParaPortion->IsSimpleInvalid() )
2439 : : {
2440 : : // Aenderung durch einfache Textaenderung...
2441 : : // Formatierung nicht abbrechen, da Portions evtl. wieder
2442 : : // gesplittet werden muessen!
2443 : : // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
2444 : : // Aber ggf. als Valid markieren, damit weniger Ausgabe...
2445 [ # # ]: 0 : if ( pLine->GetEnd() < nInvalidStart )
2446 : : {
2447 [ # # ]: 0 : if ( *pLine == aSaveLine )
2448 : : {
2449 : 0 : pLine->SetValid();
2450 : : }
2451 : : }
2452 : : else
2453 : : {
2454 : 0 : sal_uInt16 nStart = pLine->GetStart();
2455 : 0 : sal_uInt16 nEnd = pLine->GetEnd();
2456 : :
2457 [ # # ]: 0 : if ( nStart > nInvalidEnd )
2458 : : {
2459 [ # # # # ]: 0 : if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
[ # # ]
2460 : 0 : ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
2461 : : {
2462 : 0 : pLine->SetValid();
2463 [ # # ][ # # ]: 0 : if ( bCalcPortion && bQuickFormat )
2464 : : {
2465 : 0 : bCalcPortion = sal_False;
2466 [ # # ]: 0 : pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2467 : 0 : break;
2468 : : }
2469 : : }
2470 : : }
2471 [ # # ][ # # ]: 0 : else if ( bQuickFormat && ( nEnd > nInvalidEnd) )
2472 : : {
2473 : : // Wenn die ungueltige Zeile so endet, dass die naechste an
2474 : : // der 'gleichen' Textstelle wie vorher beginnt, also nicht
2475 : : // anders umgebrochen wird, brauche ich dort auch nicht die
2476 : : // textbreiten neu bestimmen:
2477 [ # # ]: 0 : if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
2478 : : {
2479 : 0 : bCalcPortion = sal_False;
2480 [ # # ]: 0 : pTEParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
2481 : 0 : break;
2482 : : }
2483 : : }
2484 : : }
2485 : : }
2486 : :
2487 : 92 : nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende
2488 : : // weil nEnd hinter das letzte Zeichen zeigt!
2489 : :
2490 : 92 : sal_uInt16 nEndPortion = pLine->GetEndPortion();
2491 : :
2492 : : // Naechste Zeile oder ggf. neue Zeile....
2493 : 92 : pLine = 0;
2494 [ - + ]: 92 : if ( nLine < pTEParaPortion->GetLines().size()-1 )
2495 : 0 : pLine = pTEParaPortion->GetLines()[ ++nLine ];
2496 [ - + ][ # # ]: 92 : if ( pLine && ( nIndex >= pNode->GetText().Len() ) )
[ - + ]
2497 : : {
2498 : 0 : nDelFromLine = nLine;
2499 : 0 : break;
2500 : : }
2501 [ + - ][ - + ]: 92 : if ( !pLine && ( nIndex < pNode->GetText().Len() ) )
[ - + ]
2502 : : {
2503 [ # # ]: 0 : pLine = new TextLine;
2504 [ # # ][ # # ]: 0 : pTEParaPortion->GetLines().insert( pTEParaPortion->GetLines().begin() + ++nLine, pLine );
2505 : : }
2506 [ - + ]: 92 : if ( pLine )
2507 : : {
2508 : 0 : aSaveLine = *pLine;
2509 : 0 : pLine->SetStart( nIndex );
2510 : 0 : pLine->SetEnd( nIndex );
2511 : 0 : pLine->SetStartPortion( nEndPortion+1 );
2512 : 0 : pLine->SetEndPortion( nEndPortion+1 );
2513 : : }
2514 : : } // while ( Index < Len )
2515 : :
2516 [ - + ]: 92 : if (nDelFromLine != std::numeric_limits<size_t>::max())
2517 : : {
2518 [ # # ][ # # ]: 0 : for( TextLines::iterator it = pTEParaPortion->GetLines().begin() + nDelFromLine;
[ # # ]
2519 : 0 : it != pTEParaPortion->GetLines().end(); ++it )
2520 : : {
2521 : 0 : delete *it;
2522 : : }
2523 : 0 : pTEParaPortion->GetLines().erase( pTEParaPortion->GetLines().begin() + nDelFromLine,
2524 [ # # # # ]: 0 : pTEParaPortion->GetLines().end() );
2525 : : }
2526 : :
2527 : : DBG_ASSERT( pTEParaPortion->GetLines().size(), "Keine Zeile nach CreateLines!" );
2528 : :
2529 [ - + ]: 92 : if ( bLineBreak == sal_True )
2530 [ # # ]: 0 : CreateAndInsertEmptyLine( nPara );
2531 : :
2532 : 92 : pTEParaPortion->SetValid();
2533 : :
2534 [ + - ]: 596 : return nOldLineCount != pTEParaPortion->GetLines().size();
2535 : : }
2536 : :
2537 : 0 : String TextEngine::GetWord( const TextPaM& rCursorPos, TextPaM* pStartOfWord )
2538 : : {
2539 : 0 : String aWord;
2540 [ # # ]: 0 : if ( rCursorPos.GetPara() < mpDoc->GetNodes().Count() )
2541 : : {
2542 [ # # ]: 0 : TextSelection aSel( rCursorPos );
2543 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( rCursorPos.GetPara() );
2544 [ # # ]: 0 : uno::Reference < i18n::XBreakIterator > xBI = GetBreakIterator();
2545 [ # # ][ # # ]: 0 : i18n::Boundary aBoundary = xBI->getWordBoundary( pNode->GetText(), rCursorPos.GetIndex(), GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
[ # # ][ # # ]
2546 : 0 : aSel.GetStart().GetIndex() = (sal_uInt16)aBoundary.startPos;
2547 : 0 : aSel.GetEnd().GetIndex() = (sal_uInt16)aBoundary.endPos;
2548 [ # # ][ # # ]: 0 : aWord = pNode->GetText().Copy( aSel.GetStart().GetIndex(), aSel.GetEnd().GetIndex() - aSel.GetStart().GetIndex() );
[ # # ]
2549 [ # # ]: 0 : if ( pStartOfWord )
2550 : 0 : *pStartOfWord = aSel.GetStart();
2551 : : }
2552 : 0 : return aWord;
2553 : : }
2554 : :
2555 : 0 : sal_Bool TextEngine::Read( SvStream& rInput, const TextSelection* pSel )
2556 : : {
2557 : 0 : sal_Bool bUpdate = GetUpdateMode();
2558 [ # # ]: 0 : SetUpdateMode( sal_False );
2559 : :
2560 [ # # ]: 0 : UndoActionStart();
2561 [ # # ]: 0 : TextSelection aSel;
2562 [ # # ]: 0 : if ( pSel )
2563 : 0 : aSel = *pSel;
2564 : : else
2565 : : {
2566 : 0 : sal_uLong nParas = mpDoc->GetNodes().Count();
2567 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2568 [ # # ]: 0 : aSel = TextPaM( nParas-1 , pNode->GetText().Len() );
2569 : : }
2570 : :
2571 [ # # ]: 0 : if ( aSel.HasRange() )
2572 [ # # ][ # # ]: 0 : aSel = ImpDeleteText( aSel );
2573 : :
2574 : 0 : rtl::OString aLine;
2575 [ # # ]: 0 : sal_Bool bDone = rInput.ReadLine( aLine );
2576 [ # # ]: 0 : rtl::OUString aTmpStr(rtl::OStringToOUString(aLine, rInput.GetStreamCharSet())), aStr;
2577 [ # # ]: 0 : while ( bDone )
2578 : : {
2579 [ # # ][ # # ]: 0 : aSel = ImpInsertText( aSel, aTmpStr );
[ # # ][ # # ]
2580 [ # # ]: 0 : bDone = rInput.ReadLine( aLine );
2581 [ # # ]: 0 : aTmpStr = rtl::OStringToOUString(aLine, rInput.GetStreamCharSet());
2582 [ # # ]: 0 : if ( bDone )
2583 [ # # ][ # # ]: 0 : aSel = ImpInsertParaBreak( aSel.GetEnd() );
2584 : : }
2585 : :
2586 [ # # ]: 0 : UndoActionEnd();
2587 : :
2588 [ # # ]: 0 : TextSelection aNewSel( aSel.GetEnd(), aSel.GetEnd() );
2589 : :
2590 : : // Damit bei FormatAndUpdate nicht auf die ungueltige Selektion zugegriffen wird.
2591 [ # # ][ # # ]: 0 : if ( GetActiveView() )
2592 [ # # ][ # # ]: 0 : GetActiveView()->ImpSetSelection( aNewSel );
2593 : :
2594 [ # # ]: 0 : SetUpdateMode( bUpdate );
2595 [ # # ][ # # ]: 0 : FormatAndUpdate( GetActiveView() );
2596 : :
2597 : 0 : return rInput.GetError() ? sal_False : sal_True;
2598 : : }
2599 : :
2600 : 0 : sal_Bool TextEngine::Write( SvStream& rOutput, const TextSelection* pSel, sal_Bool bHTML )
2601 : : {
2602 [ # # ]: 0 : TextSelection aSel;
2603 [ # # ]: 0 : if ( pSel )
2604 : 0 : aSel = *pSel;
2605 : : else
2606 : : {
2607 : 0 : sal_uLong nParas = mpDoc->GetNodes().Count();
2608 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nParas - 1 );
2609 : 0 : aSel.GetStart() = TextPaM( 0, 0 );
2610 : 0 : aSel.GetEnd() = TextPaM( nParas-1, pNode->GetText().Len() );
2611 : : }
2612 : :
2613 [ # # ]: 0 : if ( bHTML )
2614 : : {
2615 [ # # ]: 0 : rOutput.WriteLine( "<HTML>" );
2616 [ # # ]: 0 : rOutput.WriteLine( "<BODY>" );
2617 : : }
2618 : :
2619 [ # # ]: 0 : for ( sal_uLong nPara = aSel.GetStart().GetPara(); nPara <= aSel.GetEnd().GetPara(); nPara++ )
2620 : : {
2621 [ # # ]: 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2622 : :
2623 : 0 : sal_uInt16 nStartPos = 0;
2624 : 0 : sal_uInt16 nEndPos = pNode->GetText().Len();
2625 [ # # ]: 0 : if ( nPara == aSel.GetStart().GetPara() )
2626 : 0 : nStartPos = aSel.GetStart().GetIndex();
2627 [ # # ]: 0 : if ( nPara == aSel.GetEnd().GetPara() )
2628 : 0 : nEndPos = aSel.GetEnd().GetIndex();
2629 : :
2630 [ # # ]: 0 : String aText;
2631 [ # # ]: 0 : if ( !bHTML )
2632 : : {
2633 [ # # ][ # # ]: 0 : aText = pNode->GetText().Copy( nStartPos, nEndPos-nStartPos );
[ # # ]
2634 : : }
2635 : : else
2636 : : {
2637 [ # # ]: 0 : aText.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "<P STYLE=\"margin-bottom: 0cm\">" ) );
2638 : :
2639 [ # # ]: 0 : if ( nStartPos == nEndPos )
2640 : : {
2641 : : // Leerzeilen werden von Writer wegoptimiert
2642 [ # # ]: 0 : aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<BR>" ) );
2643 : : }
2644 : : else
2645 : : {
2646 : 0 : sal_uInt16 nTmpStart = nStartPos;
2647 : 0 : sal_uInt16 nTmpEnd = nEndPos;
2648 [ # # ]: 0 : do
2649 : : {
2650 [ # # ]: 0 : TextCharAttrib* pAttr = pNode->GetCharAttribs().FindNextAttrib( TEXTATTR_HYPERLINK, nTmpStart, nEndPos );
2651 [ # # ]: 0 : nTmpEnd = pAttr ? pAttr->GetStart() : nEndPos;
2652 : :
2653 : : // Text vor dem Attribut
2654 [ # # ][ # # ]: 0 : aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
[ # # ]
2655 : :
2656 [ # # ]: 0 : if ( pAttr )
2657 : : {
2658 : 0 : nTmpEnd = Min( pAttr->GetEnd(), nEndPos );
2659 : :
2660 : : // z.B. <A HREF="http://www.mopo.de/">Morgenpost</A>
2661 [ # # ]: 0 : aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "<A HREF=\"" ) );
2662 [ # # ]: 0 : aText += ((const TextAttribHyperLink&) pAttr->GetAttr() ).GetURL();
2663 [ # # ]: 0 : aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "\">" ) );
2664 : 0 : nTmpStart = pAttr->GetStart();
2665 [ # # ][ # # ]: 0 : aText += pNode->GetText().Copy( nTmpStart, nTmpEnd-nTmpStart );
[ # # ]
2666 [ # # ]: 0 : aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</A>" ) );
2667 : :
2668 : 0 : nTmpStart = pAttr->GetEnd();
2669 : : }
2670 : : } while ( nTmpEnd < nEndPos );
2671 : : }
2672 : :
2673 [ # # ]: 0 : aText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "</P>" ) );
2674 : : }
2675 : : rOutput.WriteLine(rtl::OUStringToOString(aText,
2676 [ # # ][ # # ]: 0 : rOutput.GetStreamCharSet()));
[ # # ]
2677 [ # # ]: 0 : }
2678 : :
2679 [ # # ]: 0 : if ( bHTML )
2680 : : {
2681 [ # # ]: 0 : rOutput.WriteLine( "</BODY>" );
2682 [ # # ]: 0 : rOutput.WriteLine( "</HTML>" );
2683 : : }
2684 : :
2685 : 0 : return rOutput.GetError() ? sal_False : sal_True;
2686 : : }
2687 : :
2688 : 0 : void TextEngine::RemoveAttribs( sal_uLong nPara, sal_Bool bIdleFormatAndUpdate )
2689 : : {
2690 [ # # ]: 0 : if ( nPara < mpDoc->GetNodes().Count() )
2691 : : {
2692 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2693 [ # # ]: 0 : if ( pNode->GetCharAttribs().Count() )
2694 : : {
2695 : 0 : pNode->GetCharAttribs().Clear( sal_True );
2696 : :
2697 : 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2698 : 0 : pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2699 : :
2700 : 0 : mbFormatted = sal_False;
2701 : :
2702 [ # # ]: 0 : if ( bIdleFormatAndUpdate )
2703 : 0 : IdleFormatAndUpdate( NULL, 0xFFFF );
2704 : : else
2705 : 0 : FormatAndUpdate( NULL );
2706 : : }
2707 : : }
2708 : 0 : }
2709 : 0 : void TextEngine::RemoveAttribs( sal_uLong nPara, sal_uInt16 nWhich, sal_Bool bIdleFormatAndUpdate )
2710 : : {
2711 [ # # ]: 0 : if ( nPara < mpDoc->GetNodes().Count() )
2712 : : {
2713 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2714 [ # # ]: 0 : if ( pNode->GetCharAttribs().Count() )
2715 : : {
2716 : 0 : TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2717 : 0 : sal_uInt16 nAttrCount = rAttribs.Count();
2718 [ # # ]: 0 : for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2719 : : {
2720 [ # # ]: 0 : if(rAttribs.GetAttrib( nAttr - 1 )->Which() == nWhich)
2721 : 0 : rAttribs.RemoveAttrib( nAttr -1 );
2722 : : }
2723 : 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2724 : 0 : pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2725 : 0 : mbFormatted = sal_False;
2726 [ # # ]: 0 : if(bIdleFormatAndUpdate)
2727 : 0 : IdleFormatAndUpdate( NULL, 0xFFFF );
2728 : : else
2729 : 0 : FormatAndUpdate( NULL );
2730 : : }
2731 : : }
2732 : 0 : }
2733 : 0 : void TextEngine::RemoveAttrib( sal_uLong nPara, const TextCharAttrib& rAttrib )
2734 : : {
2735 [ # # ]: 0 : if ( nPara < mpDoc->GetNodes().Count() )
2736 : : {
2737 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2738 [ # # ]: 0 : if ( pNode->GetCharAttribs().Count() )
2739 : : {
2740 : 0 : TextCharAttribList& rAttribs = pNode->GetCharAttribs();
2741 : 0 : sal_uInt16 nAttrCount = rAttribs.Count();
2742 [ # # ]: 0 : for(sal_uInt16 nAttr = nAttrCount; nAttr; --nAttr)
2743 : : {
2744 [ # # ]: 0 : if(rAttribs.GetAttrib( nAttr - 1 ) == &rAttrib)
2745 : : {
2746 : 0 : rAttribs.RemoveAttrib( nAttr -1 );
2747 : 0 : break;
2748 : : }
2749 : : }
2750 : 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2751 : 0 : pTEParaPortion->MarkSelectionInvalid( 0, pNode->GetText().Len() );
2752 : 0 : mbFormatted = sal_False;
2753 : 0 : FormatAndUpdate( NULL );
2754 : : }
2755 : : }
2756 : 0 : }
2757 : :
2758 : 0 : void TextEngine::SetAttrib( const TextAttrib& rAttr, sal_uLong nPara, sal_uInt16 nStart, sal_uInt16 nEnd, sal_Bool bIdleFormatAndUpdate )
2759 : : {
2760 : : // Es wird hier erstmal nicht geprueft, ob sich Attribute ueberlappen!
2761 : : // Diese Methode ist erstmal nur fuer einen Editor, der fuer eine Zeile
2762 : : // _schnell_ das Syntax-Highlight einstellen will.
2763 : :
2764 : : // Da die TextEngine z.Zt fuer Editoren gedacht ist gibt es auch kein
2765 : : // Undo fuer Attribute!
2766 : :
2767 [ # # ]: 0 : if ( nPara < mpDoc->GetNodes().Count() )
2768 : : {
2769 : 0 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
2770 : 0 : TEParaPortion* pTEParaPortion = mpTEParaPortions->GetObject( nPara );
2771 : :
2772 : 0 : sal_uInt16 nMax = pNode->GetText().Len();
2773 [ # # ]: 0 : if ( nStart > nMax )
2774 : 0 : nStart = nMax;
2775 [ # # ]: 0 : if ( nEnd > nMax )
2776 : 0 : nEnd = nMax;
2777 : :
2778 [ # # ]: 0 : pNode->GetCharAttribs().InsertAttrib( new TextCharAttrib( rAttr, nStart, nEnd ) );
2779 : 0 : pTEParaPortion->MarkSelectionInvalid( nStart, nEnd );
2780 : :
2781 : 0 : mbFormatted = sal_False;
2782 [ # # ]: 0 : if ( bIdleFormatAndUpdate )
2783 : 0 : IdleFormatAndUpdate( NULL, 0xFFFF );
2784 : : else
2785 : 0 : FormatAndUpdate( NULL );
2786 : : }
2787 : 0 : }
2788 : :
2789 : 272 : void TextEngine::SetTextAlign( TxtAlign eAlign )
2790 : : {
2791 [ + + ]: 272 : if ( eAlign != meAlign )
2792 : : {
2793 : 20 : meAlign = eAlign;
2794 : 20 : FormatFullDoc();
2795 : 20 : UpdateViews();
2796 : : }
2797 : 272 : }
2798 : :
2799 : :
2800 : 218 : void TextEngine::ValidateSelection( TextSelection& rSel ) const
2801 : : {
2802 : 218 : ValidatePaM( rSel.GetStart() );
2803 : 218 : ValidatePaM( rSel.GetEnd() );
2804 : 218 : }
2805 : :
2806 : 436 : void TextEngine::ValidatePaM( TextPaM& rPaM ) const
2807 : : {
2808 : 436 : sal_uLong nMaxPara = mpDoc->GetNodes().Count() - 1;
2809 [ - + ]: 436 : if ( rPaM.GetPara() > nMaxPara )
2810 : : {
2811 : 0 : rPaM.GetPara() = nMaxPara;
2812 : 0 : rPaM.GetIndex() = 0xFFFF;
2813 : : }
2814 : :
2815 : 436 : sal_uInt16 nMaxIndex = GetTextLen( rPaM.GetPara() );
2816 [ - + ]: 436 : if ( rPaM.GetIndex() > nMaxIndex )
2817 : 0 : rPaM.GetIndex() = nMaxIndex;
2818 : 436 : }
2819 : :
2820 : :
2821 : : // Status & Selektionsanpassung
2822 : :
2823 : 218 : void TextEngine::ImpParagraphInserted( sal_uLong nPara )
2824 : : {
2825 : : // Die aktive View braucht nicht angepasst werden, aber bei allen
2826 : : // passiven muss die Selektion angepasst werden:
2827 [ - + ]: 218 : if ( mpViews->size() > 1 )
2828 : : {
2829 [ # # ]: 0 : for ( sal_uInt16 nView = mpViews->size(); nView; )
2830 : : {
2831 : 0 : TextView* pView = (*mpViews)[ --nView ];
2832 [ # # ]: 0 : if ( pView != GetActiveView() )
2833 : : {
2834 [ # # ]: 0 : for ( int n = 0; n <= 1; n++ )
2835 : : {
2836 [ # # ]: 0 : TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2837 [ # # ]: 0 : if ( rPaM.GetPara() >= nPara )
2838 : 0 : rPaM.GetPara()++;
2839 : : }
2840 : : }
2841 : : }
2842 : : }
2843 [ + - ]: 218 : Broadcast( TextHint( TEXT_HINT_PARAINSERTED, nPara ) );
2844 : 218 : }
2845 : :
2846 : 218 : void TextEngine::ImpParagraphRemoved( sal_uLong nPara )
2847 : : {
2848 [ - + ]: 218 : if ( mpViews->size() > 1 )
2849 : : {
2850 [ # # ]: 0 : for ( sal_uInt16 nView = mpViews->size(); nView; )
2851 : : {
2852 : 0 : TextView* pView = (*mpViews)[ --nView ];
2853 [ # # ]: 0 : if ( pView != GetActiveView() )
2854 : : {
2855 : 0 : sal_uLong nParas = mpDoc->GetNodes().Count();
2856 [ # # ]: 0 : for ( int n = 0; n <= 1; n++ )
2857 : : {
2858 [ # # ]: 0 : TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2859 [ # # ]: 0 : if ( rPaM.GetPara() > nPara )
2860 : 0 : rPaM.GetPara()--;
2861 [ # # ]: 0 : else if ( rPaM.GetPara() == nPara )
2862 : : {
2863 : 0 : rPaM.GetIndex() = 0;
2864 [ # # ]: 0 : if ( rPaM.GetPara() >= nParas )
2865 : 0 : rPaM.GetPara()--;
2866 : : }
2867 : : }
2868 : : }
2869 : : }
2870 : : }
2871 [ + - ]: 218 : Broadcast( TextHint( TEXT_HINT_PARAREMOVED, nPara ) );
2872 : 218 : }
2873 : :
2874 : 0 : void TextEngine::ImpCharsRemoved( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2875 : : {
2876 [ # # ]: 0 : if ( mpViews->size() > 1 )
2877 : : {
2878 [ # # ]: 0 : for ( sal_uInt16 nView = mpViews->size(); nView; )
2879 : : {
2880 : 0 : TextView* pView = (*mpViews)[ --nView ];
2881 [ # # ]: 0 : if ( pView != GetActiveView() )
2882 : : {
2883 : 0 : sal_uInt16 nEnd = nPos+nChars;
2884 [ # # ]: 0 : for ( int n = 0; n <= 1; n++ )
2885 : : {
2886 [ # # ]: 0 : TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2887 [ # # ]: 0 : if ( rPaM.GetPara() == nPara )
2888 : : {
2889 [ # # ]: 0 : if ( rPaM.GetIndex() > nEnd )
2890 : 0 : rPaM.GetIndex() = rPaM.GetIndex() - nChars;
2891 [ # # ]: 0 : else if ( rPaM.GetIndex() > nPos )
2892 : 0 : rPaM.GetIndex() = nPos;
2893 : : }
2894 : : }
2895 : : }
2896 : : }
2897 : : }
2898 [ # # ]: 0 : Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
2899 : 0 : }
2900 : :
2901 : 52 : void TextEngine::ImpCharsInserted( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16 nChars )
2902 : : {
2903 [ - + ]: 52 : if ( mpViews->size() > 1 )
2904 : : {
2905 [ # # ]: 0 : for ( sal_uInt16 nView = mpViews->size(); nView; )
2906 : : {
2907 : 0 : TextView* pView = (*mpViews)[ --nView ];
2908 [ # # ]: 0 : if ( pView != GetActiveView() )
2909 : : {
2910 [ # # ]: 0 : for ( int n = 0; n <= 1; n++ )
2911 : : {
2912 [ # # ]: 0 : TextPaM& rPaM = n ? pView->GetSelection().GetStart(): pView->GetSelection().GetEnd();
2913 [ # # ]: 0 : if ( rPaM.GetPara() == nPara )
2914 : : {
2915 [ # # ]: 0 : if ( rPaM.GetIndex() >= nPos )
2916 : 0 : rPaM.GetIndex() = rPaM.GetIndex() + nChars;
2917 : : }
2918 : : }
2919 : : }
2920 : : }
2921 : : }
2922 [ + - ]: 52 : Broadcast( TextHint( TEXT_HINT_PARACONTENTCHANGED, nPara ) );
2923 : 52 : }
2924 : :
2925 : 596 : void TextEngine::ImpFormattingParagraph( sal_uLong nPara )
2926 : : {
2927 [ + - ]: 596 : Broadcast( TextHint( TEXT_HINT_FORMATPARA, nPara ) );
2928 : 596 : }
2929 : :
2930 : 300 : void TextEngine::ImpTextHeightChanged()
2931 : : {
2932 [ + - ]: 300 : Broadcast( TextHint( TEXT_HINT_TEXTHEIGHTCHANGED ) );
2933 : 300 : }
2934 : :
2935 : 596 : void TextEngine::ImpTextFormatted()
2936 : : {
2937 [ + - ]: 596 : Broadcast( TextHint( TEXT_HINT_TEXTFORMATTED ) );
2938 : 596 : }
2939 : :
2940 : 0 : void TextEngine::Draw( OutputDevice* pDev, const Point& rPos )
2941 : : {
2942 : 0 : ImpPaint( pDev, rPos, NULL );
2943 : 0 : }
2944 : :
2945 : 22 : void TextEngine::SetLeftMargin( sal_uInt16 n )
2946 : : {
2947 : 22 : mpDoc->SetLeftMargin( n );
2948 : 22 : }
2949 : :
2950 : 0 : sal_uInt16 TextEngine::GetLeftMargin() const
2951 : : {
2952 : 0 : return mpDoc->GetLeftMargin();
2953 : : }
2954 : :
2955 : 0 : uno::Reference< i18n::XBreakIterator > TextEngine::GetBreakIterator()
2956 : : {
2957 [ # # ]: 0 : if ( !mxBreakIterator.is() )
2958 [ # # ]: 0 : mxBreakIterator = vcl::unohelper::CreateBreakIterator();
2959 : : DBG_ASSERT( mxBreakIterator.is(), "Could not create BreakIterator" );
2960 : 0 : return mxBreakIterator;
2961 : : }
2962 : :
2963 : 90 : void TextEngine::SetLocale( const ::com::sun::star::lang::Locale& rLocale )
2964 : : {
2965 : 90 : maLocale = rLocale;
2966 [ - + ]: 90 : delete mpLocaleDataWrapper;
2967 : 90 : mpLocaleDataWrapper = NULL;
2968 : 90 : }
2969 : :
2970 : 0 : ::com::sun::star::lang::Locale TextEngine::GetLocale()
2971 : : {
2972 [ # # ]: 0 : if ( maLocale.Language.isEmpty() )
2973 : : {
2974 : 0 : maLocale = Application::GetSettings().GetUILocale();
2975 : : }
2976 : 0 : return maLocale;
2977 : : }
2978 : :
2979 : 0 : LocaleDataWrapper* TextEngine::ImpGetLocaleDataWrapper()
2980 : : {
2981 [ # # ]: 0 : if ( !mpLocaleDataWrapper )
2982 [ # # ][ # # ]: 0 : mpLocaleDataWrapper = new LocaleDataWrapper( vcl::unohelper::GetMultiServiceFactory(), GetLocale() );
[ # # ]
2983 : :
2984 : 0 : return mpLocaleDataWrapper;
2985 : : }
2986 : :
2987 : 272 : void TextEngine::SetRightToLeft( sal_Bool bR2L )
2988 : : {
2989 [ - + ]: 272 : if ( mbRightToLeft != bR2L )
2990 : : {
2991 : 0 : mbRightToLeft = bR2L;
2992 [ # # ]: 0 : meAlign = bR2L ? TXTALIGN_RIGHT : TXTALIGN_LEFT;
2993 : 0 : FormatFullDoc();
2994 : 0 : UpdateViews();
2995 : : }
2996 : 272 : }
2997 : :
2998 : 92 : void TextEngine::ImpInitWritingDirections( sal_uLong nPara )
2999 : : {
3000 : 92 : TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3001 : 92 : std::vector<TEWritingDirectionInfo>& rInfos = pParaPortion->GetWritingDirectionInfos();
3002 : 92 : rInfos.clear();
3003 : :
3004 [ + - ]: 92 : if ( pParaPortion->GetNode()->GetText().Len() )
3005 : : {
3006 [ - + ]: 92 : const UBiDiLevel nBidiLevel = IsRightToLeft() ? 1 /*RTL*/ : 0 /*LTR*/;
3007 [ + - ]: 92 : String aText( pParaPortion->GetNode()->GetText() );
3008 : :
3009 : : //
3010 : : // Bidi functions from icu 2.0
3011 : : //
3012 : 92 : UErrorCode nError = U_ZERO_ERROR;
3013 [ + - ]: 92 : UBiDi* pBidi = ubidi_openSized( aText.Len(), 0, &nError );
3014 : 92 : nError = U_ZERO_ERROR;
3015 : :
3016 [ + - ]: 92 : ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aText.GetBuffer()), aText.Len(), nBidiLevel, NULL, &nError ); // UChar != sal_Unicode in MinGW
3017 : 92 : nError = U_ZERO_ERROR;
3018 : :
3019 [ + - ]: 92 : long nCount = ubidi_countRuns( pBidi, &nError );
3020 : :
3021 : 92 : int32_t nStart = 0;
3022 : : int32_t nEnd;
3023 : : UBiDiLevel nCurrDir;
3024 : :
3025 [ + + ]: 184 : for ( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
3026 : : {
3027 [ + - ]: 92 : ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
3028 [ + - ]: 92 : rInfos.push_back( TEWritingDirectionInfo( nCurrDir, (sal_uInt16)nStart, (sal_uInt16)nEnd ) );
3029 : 92 : nStart = nEnd;
3030 : : }
3031 : :
3032 [ + - ][ + - ]: 92 : ubidi_close( pBidi );
3033 : : }
3034 : :
3035 : : // No infos mean no CTL and default dir is L2R...
3036 [ - + ]: 92 : if ( rInfos.empty() )
3037 [ # # ]: 0 : rInfos.push_back( TEWritingDirectionInfo( 0, 0, (sal_uInt16)pParaPortion->GetNode()->GetText().Len() ) );
3038 : :
3039 : 92 : }
3040 : :
3041 : 92 : sal_uInt8 TextEngine::ImpGetRightToLeft( sal_uLong nPara, sal_uInt16 nPos, sal_uInt16* pStart, sal_uInt16* pEnd )
3042 : : {
3043 : 92 : sal_uInt8 nRightToLeft = 0;
3044 : :
3045 : 92 : TextNode* pNode = mpDoc->GetNodes().GetObject( nPara );
3046 [ + - ][ + - ]: 92 : if ( pNode && pNode->GetText().Len() )
[ + - ]
3047 : : {
3048 : 92 : TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3049 [ - + ]: 92 : if ( pParaPortion->GetWritingDirectionInfos().empty() )
3050 : 0 : ImpInitWritingDirections( nPara );
3051 : :
3052 : 92 : std::vector<TEWritingDirectionInfo>& rDirInfos = pParaPortion->GetWritingDirectionInfos();
3053 [ + - ][ + - ]: 184 : for ( std::vector<TEWritingDirectionInfo>::const_iterator rDirInfosIt = rDirInfos.begin(); rDirInfosIt != rDirInfos.end(); ++rDirInfosIt )
[ + - ]
3054 : : {
3055 [ + - ][ + - ]: 92 : if ( ( (*rDirInfosIt).nStartPos <= nPos ) && ( (*rDirInfosIt).nEndPos >= nPos ) )
[ + - ]
3056 : : {
3057 : 92 : nRightToLeft = (*rDirInfosIt).nType;
3058 [ - + ]: 92 : if ( pStart )
3059 : 0 : *pStart = (*rDirInfosIt).nStartPos;
3060 [ - + ]: 92 : if ( pEnd )
3061 : 0 : *pEnd = (*rDirInfosIt).nEndPos;
3062 : 92 : break;
3063 : : }
3064 : : }
3065 : : }
3066 : 92 : return nRightToLeft;
3067 : : }
3068 : :
3069 : 535 : long TextEngine::ImpGetPortionXOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nTextPortion )
3070 : : {
3071 : 535 : long nX = pLine->GetStartX();
3072 : :
3073 : 535 : TEParaPortion* pParaPortion = mpTEParaPortions->GetObject( nPara );
3074 : :
3075 [ - + ]: 535 : for ( sal_uInt16 i = pLine->GetStartPortion(); i < nTextPortion; i++ )
3076 : : {
3077 : 0 : TETextPortion* pPortion = pParaPortion->GetTextPortions()[ i ];
3078 : 0 : nX += pPortion->GetWidth();
3079 : : }
3080 : :
3081 : 535 : TETextPortion* pDestPortion = pParaPortion->GetTextPortions()[ nTextPortion ];
3082 [ + - ]: 535 : if ( pDestPortion->GetKind() != PORTIONKIND_TAB )
3083 : : {
3084 [ + - ][ - + ]: 535 : if ( !IsRightToLeft() && pDestPortion->GetRightToLeft() )
[ - + ]
3085 : : {
3086 : : // Portions behind must be added, visual before this portion
3087 : 0 : sal_uInt16 nTmpPortion = nTextPortion+1;
3088 [ # # ]: 0 : while ( nTmpPortion <= pLine->GetEndPortion() )
3089 : : {
3090 : 0 : TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3091 [ # # ][ # # ]: 0 : if ( pNextTextPortion->GetRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
[ # # ]
3092 : 0 : nX += pNextTextPortion->GetWidth();
3093 : : else
3094 : 0 : break;
3095 : 0 : nTmpPortion++;
3096 : : }
3097 : : // Portions before must be removed, visual behind this portion
3098 : 0 : nTmpPortion = nTextPortion;
3099 [ # # ]: 0 : while ( nTmpPortion > pLine->GetStartPortion() )
3100 : : {
3101 : 0 : --nTmpPortion;
3102 : 0 : TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3103 [ # # ][ # # ]: 0 : if ( pPrevTextPortion->GetRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
[ # # ]
3104 : 0 : nX -= pPrevTextPortion->GetWidth();
3105 : : else
3106 : 0 : break;
3107 : : }
3108 : : }
3109 [ - + ][ # # ]: 535 : else if ( IsRightToLeft() && !pDestPortion->IsRightToLeft() )
[ - + ]
3110 : : {
3111 : : // Portions behind must be removed, visual behind this portion
3112 : 0 : sal_uInt16 nTmpPortion = nTextPortion+1;
3113 [ # # ]: 0 : while ( nTmpPortion <= pLine->GetEndPortion() )
3114 : : {
3115 : 0 : TETextPortion* pNextTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3116 [ # # ][ # # ]: 0 : if ( !pNextTextPortion->IsRightToLeft() && ( pNextTextPortion->GetKind() != PORTIONKIND_TAB ) )
[ # # ]
3117 : 0 : nX += pNextTextPortion->GetWidth();
3118 : : else
3119 : 0 : break;
3120 : 0 : nTmpPortion++;
3121 : : }
3122 : : // Portions before must be added, visual before this portion
3123 : 0 : nTmpPortion = nTextPortion;
3124 [ # # ]: 0 : while ( nTmpPortion > pLine->GetStartPortion() )
3125 : : {
3126 : 0 : --nTmpPortion;
3127 : 0 : TETextPortion* pPrevTextPortion = pParaPortion->GetTextPortions()[ nTmpPortion ];
3128 [ # # ][ # # ]: 0 : if ( !pPrevTextPortion->IsRightToLeft() && ( pPrevTextPortion->GetKind() != PORTIONKIND_TAB ) )
[ # # ]
3129 : 0 : nX -= pPrevTextPortion->GetWidth();
3130 : : else
3131 : 0 : break;
3132 : : }
3133 : : }
3134 : : }
3135 : :
3136 : 535 : return nX;
3137 : : }
3138 : :
3139 : 179 : void TextEngine::ImpInitLayoutMode( OutputDevice* pOutDev, sal_Bool bDrawingR2LPortion )
3140 : : {
3141 : 179 : sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
3142 : :
3143 : 179 : nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
3144 [ - + ]: 179 : if ( bDrawingR2LPortion )
3145 : 0 : nLayoutMode |= TEXT_LAYOUT_BIDI_RTL;
3146 : :
3147 : 179 : pOutDev->SetLayoutMode( nLayoutMode );
3148 : 179 : }
3149 : :
3150 : 1022 : TxtAlign TextEngine::ImpGetAlign() const
3151 : : {
3152 : 1022 : TxtAlign eAlign = meAlign;
3153 [ - + ]: 1022 : if ( IsRightToLeft() )
3154 : : {
3155 [ # # ]: 0 : if ( eAlign == TXTALIGN_LEFT )
3156 : 0 : eAlign = TXTALIGN_RIGHT;
3157 [ # # ]: 0 : else if ( eAlign == TXTALIGN_RIGHT )
3158 : 0 : eAlign = TXTALIGN_LEFT;
3159 : : }
3160 : 1022 : return eAlign;
3161 : : }
3162 : :
3163 : 178 : long TextEngine::ImpGetOutputOffset( sal_uLong nPara, TextLine* pLine, sal_uInt16 nIndex, sal_uInt16 nIndex2 )
3164 : : {
3165 [ + - ]: 178 : TEParaPortion* pPortion = mpTEParaPortions->GetObject( nPara );
3166 : :
3167 : : sal_uInt16 nPortionStart;
3168 [ + - ]: 178 : sal_uInt16 nPortion = pPortion->GetTextPortions().FindPortion( nIndex, nPortionStart, sal_True );
3169 : :
3170 : 178 : TETextPortion* pTextPortion = pPortion->GetTextPortions()[ nPortion ];
3171 : :
3172 : : long nX;
3173 : :
3174 [ + + ][ + - ]: 178 : if ( ( nIndex == nPortionStart ) && ( nIndex == nIndex2 ) )
3175 : : {
3176 : : // Output of full portion, so we need portion x offset.
3177 : : // Use ImpGetPortionXOffset, because GetXPos may deliver left or right position from portioon, depending on R2L, L2R
3178 [ + - ]: 129 : nX = ImpGetPortionXOffset( nPara, pLine, nPortion );
3179 [ - + ]: 129 : if ( IsRightToLeft() )
3180 : : {
3181 : 0 : nX = -nX -pTextPortion->GetWidth();
3182 : : }
3183 : : }
3184 : : else
3185 : : {
3186 [ + - ]: 49 : nX = ImpGetXPos( nPara, pLine, nIndex, nIndex == nPortionStart );
3187 [ + - ]: 49 : if ( nIndex2 != nIndex )
3188 : : {
3189 [ + - ]: 49 : long nX2 = ImpGetXPos( nPara, pLine, nIndex2, sal_False );
3190 [ + - ]: 98 : if ( ( !IsRightToLeft() && ( nX2 < nX ) ) ||
[ + - - + ]
[ # # ][ - + ]
3191 : 49 : ( IsRightToLeft() && ( nX2 > nX ) ) )
3192 : : {
3193 : 0 : nX = nX2;
3194 : : }
3195 : : }
3196 [ - + ]: 49 : if ( IsRightToLeft() )
3197 : : {
3198 : 0 : nX = -nX;
3199 : : }
3200 : : }
3201 : :
3202 : 178 : return nX;
3203 : : }
3204 : :
3205 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|