1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_editeng.hxx"
26
27 #include <vcl/wrkwin.hxx>
28 #include <vcl/dialog.hxx>
29 #include <vcl/msgbox.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/metaact.hxx>
32 #include <vcl/gdimtf.hxx>
33
34 #define _SVSTDARR_sal_uIt16S
35 #include <svl/svstdarr.hxx>
36
37 #include <vcl/wrkwin.hxx>
38 #include <editeng/adjitem.hxx>
39 #include <editeng/tstpitem.hxx>
40 #include <editeng/lspcitem.hxx>
41 #include <editeng/flditem.hxx>
42 #include <impedit.hxx>
43 #include <editeng/editeng.hxx>
44 #include <editeng/editview.hxx>
45 #include <editeng/txtrange.hxx>
46 #include <editeng/cscoitem.hxx>
47 #include <editeng/colritem.hxx>
48 #include <editeng/udlnitem.hxx>
49 #include <editeng/fhgtitem.hxx>
50 #include <editeng/kernitem.hxx>
51 #include <editeng/lrspitem.hxx>
52 #include <editeng/ulspitem.hxx>
53 #include <editeng/fontitem.hxx>
54 #include <editeng/wghtitem.hxx>
55 #include <editeng/postitem.hxx>
56 #include <editeng/langitem.hxx>
57 #include <editeng/scriptspaceitem.hxx>
58 #include <editeng/charscaleitem.hxx>
59 #include <editeng/numitem.hxx>
60
61 #include <svtools/colorcfg.hxx>
62 #include <svl/ctloptions.hxx>
63
64 #include <editeng/forbiddencharacterstable.hxx>
65
66 #include <unotools/localedatawrapper.hxx>
67
68 #include <editeng/unolingu.hxx>
69
70 #include <math.h>
71 #include <vcl/svapp.hxx>
72 #include <vcl/metric.hxx>
73 #include <com/sun/star/i18n/ScriptType.hpp>
74 #include <com/sun/star/text/CharacterCompressionType.hpp>
75 #include <vcl/pdfextoutdevdata.hxx>
76 #include <i18npool/mslangid.hxx>
77
78 #include <comphelper/processfactory.hxx>
79
80 using ::rtl::OUString;
81 using namespace ::com::sun::star;
82 using namespace ::com::sun::star::uno;
83 using namespace ::com::sun::star::beans;
84 using namespace ::com::sun::star::linguistic2;
85
86 SV_DECL_VARARR_SORT( SortedPositions, sal_uInt32, 16, 8 )
87 SV_IMPL_VARARR_SORT( SortedPositions, sal_uInt32 );
88
89 #define CH_HYPH '-'
90
91 #define RESDIFF 10
92
93 #define WRONG_SHOW_MIN 5
94 #define WRONG_SHOW_SMALL 11
95 #define WRONG_SHOW_MEDIUM 15
96
97 struct TabInfo
98 {
99 sal_Bool bValid;
100
101 SvxTabStop aTabStop;
102 xub_StrLen nCharPos;
103 sal_uInt16 nTabPortion;
104 long nStartPosX;
105 long nTabPos;
106
TabInfoTabInfo107 TabInfo() { bValid = sal_False; }
108 };
109
Rotate(const Point & rPoint,short nOrientation,const Point & rOrigin)110 Point Rotate( const Point& rPoint, short nOrientation, const Point& rOrigin )
111 {
112 double nRealOrientation = nOrientation*F_PI1800;
113 double nCos = cos( nRealOrientation );
114 double nSin = sin( nRealOrientation );
115
116 Point aRotatedPos;
117 Point aTranslatedPos( rPoint );
118
119 // Translation
120 aTranslatedPos -= rOrigin;
121
122 // Rotation...
123 aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
124 aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
125 aTranslatedPos = aRotatedPos;
126
127 // Translation...
128 aTranslatedPos += rOrigin;
129 return aTranslatedPos;
130 }
131
GetCharTypeForCompression(xub_Unicode cChar)132 sal_uInt8 GetCharTypeForCompression( xub_Unicode cChar )
133 {
134 switch ( cChar )
135 {
136 case 0x3008: case 0x300A: case 0x300C: case 0x300E:
137 case 0x3010: case 0x3014: case 0x3016: case 0x3018:
138 case 0x301A: case 0x301D:
139 {
140 return CHAR_PUNCTUATIONRIGHT;
141 }
142 case 0x3001: case 0x3002: case 0x3009: case 0x300B:
143 case 0x300D: case 0x300F: case 0x3011: case 0x3015:
144 case 0x3017: case 0x3019: case 0x301B: case 0x301E:
145 case 0x301F:
146 {
147 return CHAR_PUNCTUATIONLEFT;
148 }
149 default:
150 {
151 return ( ( 0x3040 <= cChar ) && ( 0x3100 > cChar ) ) ? CHAR_KANA : CHAR_NORMAL;
152 }
153 }
154 }
155
lcl_DrawRedLines(OutputDevice * pOutDev,long nFontHeight,const Point & rPnt,sal_uInt16 nIndex,sal_uInt16 nMaxEnd,const sal_Int32 * pDXArray,WrongList * pWrongs,short nOrientation,const Point & rOrigin,sal_Bool bVertical,sal_Bool bIsRightToLeft)156 void lcl_DrawRedLines(
157 OutputDevice* pOutDev,
158 long nFontHeight,
159 const Point& rPnt,
160 sal_uInt16 nIndex,
161 sal_uInt16 nMaxEnd,
162 const sal_Int32* pDXArray,
163 WrongList* pWrongs,
164 short nOrientation,
165 const Point& rOrigin,
166 sal_Bool bVertical,
167 sal_Bool bIsRightToLeft )
168 {
169 #ifndef SVX_LIGHT
170 // Aber nur, wenn Font nicht zu klein...
171 long nHght = pOutDev->LogicToPixel( Size( 0, nFontHeight ) ).Height();
172 if( WRONG_SHOW_MIN < nHght )
173 {
174 sal_uInt16 nStyle;
175 if( WRONG_SHOW_MEDIUM < nHght )
176 nStyle = WAVE_NORMAL;
177 else if( WRONG_SHOW_SMALL < nHght )
178 nStyle = WAVE_SMALL;
179 else
180 nStyle = WAVE_FLAT;
181
182 sal_uInt16 nEnd, nStart = nIndex;
183 sal_Bool bWrong = pWrongs->NextWrong( nStart, nEnd );
184 while ( bWrong )
185 {
186 if ( nStart >= nMaxEnd )
187 break;
188
189 if ( nStart < nIndex ) // Wurde korrigiert
190 nStart = nIndex;
191 if ( nEnd > nMaxEnd )
192 nEnd = nMaxEnd;
193 Point aPnt1( rPnt );
194 if ( bVertical && ( nStyle != WAVE_FLAT ) )
195 {
196 // VCL doesn't know that the text is vertical, and is manipulating
197 // the positions a little bit in y direction...
198 long nOnePixel = pOutDev->PixelToLogic( Size( 0, 1 ) ).Height();
199 long nCorrect = ( nStyle == WAVE_NORMAL ) ? 2*nOnePixel : nOnePixel;
200 aPnt1.Y() -= nCorrect;
201 aPnt1.X() -= nCorrect;
202 }
203 if ( nStart > nIndex )
204 {
205 if ( !bVertical )
206 {
207 // since for RTL portions rPnt is on the visual right end of the portion
208 // (i.e. at the start of the first RTL char) we need to subtract the offset
209 // for RTL portions...
210 aPnt1.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nStart - nIndex - 1 ];
211 }
212 else
213 aPnt1.Y() += pDXArray[ nStart - nIndex - 1 ];
214 }
215 Point aPnt2( rPnt );
216 DBG_ASSERT( nEnd > nIndex, "RedLine: aPnt2?" );
217 if ( !bVertical )
218 {
219 // since for RTL portions rPnt is on the visual right end of the portion
220 // (i.e. at the start of the first RTL char) we need to subtract the offset
221 // for RTL portions...
222 aPnt2.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nEnd - nIndex - 1 ];
223 }
224 else
225 aPnt2.Y() += pDXArray[ nEnd - nIndex - 1 ];
226 if ( nOrientation )
227 {
228 aPnt1 = Rotate( aPnt1, nOrientation, rOrigin );
229 aPnt2 = Rotate( aPnt2, nOrientation, rOrigin );
230 }
231
232 pOutDev->DrawWaveLine( aPnt1, aPnt2, nStyle );
233
234 nStart = nEnd+1;
235 if ( nEnd < nMaxEnd )
236 bWrong = pWrongs->NextWrong( nStart, nEnd );
237 else
238 bWrong = sal_False;
239 }
240 }
241 #endif // !SVX_LIGHT
242 }
243
lcl_ImplCalcRotatedPos(Point rPos,Point rOrigin,double nSin,double nCos)244 Point lcl_ImplCalcRotatedPos( Point rPos, Point rOrigin, double nSin, double nCos )
245 {
246 Point aRotatedPos;
247 // Translation...
248 Point aTranslatedPos( rPos);
249 aTranslatedPos -= rOrigin;
250
251 aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
252 aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
253 aTranslatedPos = aRotatedPos;
254 // Translation...
255 aTranslatedPos += rOrigin;
256
257 return aTranslatedPos;
258 }
259
lcl_IsLigature(xub_Unicode cCh,xub_Unicode cNextCh)260 sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh ) // For Kashidas from sw/source/core/text/porlay.txt
261 {
262 // Lam + Alef
263 return ( 0x644 == cCh && 0x627 == cNextCh ) ||
264 // Beh + Reh
265 ( 0x628 == cCh && 0x631 == cNextCh );
266 }
267
lcl_ConnectToPrev(xub_Unicode cCh,xub_Unicode cPrevCh)268 sal_Bool lcl_ConnectToPrev( xub_Unicode cCh, xub_Unicode cPrevCh ) // For Kashidas from sw/source/core/text/porlay.txt
269 {
270 // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
271 sal_Bool bRet = 0x627 != cPrevCh && 0x62F != cPrevCh && 0x630 != cPrevCh &&
272 0x631 != cPrevCh && 0x632 != cPrevCh && 0x648 != cPrevCh;
273
274 // check for ligatures cPrevChar + cChar
275 if ( bRet )
276 bRet = ! lcl_IsLigature( cPrevCh, cCh );
277
278 return bRet;
279 }
280
281
282 // ----------------------------------------------------------------------
283 // class ImpEditEngine
284 // ----------------------------------------------------------------------
UpdateViews(EditView * pCurView)285 void ImpEditEngine::UpdateViews( EditView* pCurView )
286 {
287 if ( !GetUpdateMode() || IsFormatting() || aInvalidRec.IsEmpty() )
288 return;
289
290 DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
291
292 for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
293 {
294 EditView* pView = aEditViews[nView];
295 DBG_CHKOBJ( pView, EditView, 0 );
296 pView->HideCursor();
297
298 Rectangle aClipRec( aInvalidRec );
299 Rectangle aVisArea( pView->GetVisArea() );
300 aClipRec.Intersection( aVisArea );
301
302 if ( !aClipRec.IsEmpty() )
303 {
304 // in Fensterkoordinaten umwandeln....
305 aClipRec = pView->pImpEditView->GetWindowPos( aClipRec );
306
307 if ( ( pView == pCurView ) )
308 Paint( pView->pImpEditView, aClipRec, 0, sal_True );
309 else
310 pView->GetWindow()->Invalidate( aClipRec );
311 }
312 }
313
314 if ( pCurView )
315 {
316 sal_Bool bGotoCursor = pCurView->pImpEditView->DoAutoScroll();
317 pCurView->ShowCursor( bGotoCursor );
318 }
319
320 aInvalidRec = Rectangle();
321 CallStatusHdl();
322 }
323
IMPL_LINK(ImpEditEngine,OnlineSpellHdl,Timer *,EMPTYARG)324 IMPL_LINK( ImpEditEngine, OnlineSpellHdl, Timer *, EMPTYARG )
325 {
326 if ( !Application::AnyInput( INPUT_KEYBOARD ) && GetUpdateMode() && IsFormatted() )
327 DoOnlineSpelling();
328 else
329 aOnlineSpellTimer.Start();
330
331 return 0;
332 }
333
IMPL_LINK_INLINE_START(ImpEditEngine,IdleFormatHdl,Timer *,EMPTYARG)334 IMPL_LINK_INLINE_START( ImpEditEngine, IdleFormatHdl, Timer *, EMPTYARG )
335 {
336 aIdleFormatter.ResetRestarts();
337
338 // #i97146# check if that view is still available
339 // else probably the idle format timer fired while we're already
340 // downing
341 EditView* pView = aIdleFormatter.GetView();
342 for( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
343 {
344 if( aEditViews[nView] == pView )
345 {
346 FormatAndUpdate( pView );
347 break;
348 }
349 }
350 return 0;
351 }
IMPL_LINK_INLINE_END(ImpEditEngine,IdleFormatHdl,Timer *,EMPTYARG)352 IMPL_LINK_INLINE_END( ImpEditEngine, IdleFormatHdl, Timer *, EMPTYARG )
353
354 void ImpEditEngine::CheckIdleFormatter()
355 {
356 aIdleFormatter.ForceTimeout();
357 // Falls kein Idle, aber trotzdem nicht formatiert:
358 if ( !IsFormatted() )
359 FormatDoc();
360 }
361
FormatFullDoc()362 void ImpEditEngine::FormatFullDoc()
363 {
364 for ( sal_uInt32 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
365 GetParaPortions()[nPortion]->MarkSelectionInvalid( 0, GetParaPortions()[nPortion]->GetNode()->Len() );
366 FormatDoc();
367 }
368
FormatDoc()369 void ImpEditEngine::FormatDoc()
370 {
371 if ( !GetUpdateMode() || IsFormatting() || !GetUpdateModeForAcc())
372 return;
373
374 EnterBlockNotifications();
375
376 bIsFormatting = sal_True;
377
378 // Dann kann ich auch den Spell-Timer starten...
379 if ( GetStatus().DoOnlineSpelling() )
380 StartOnlineSpellTimer();
381
382 long nY = 0;
383 sal_Bool bGrow = sal_False;
384
385 Font aOldFont( GetRefDevice()->GetFont() );
386
387 // Hier schon, damit nicht jedesmal in CreateLines...
388 sal_Bool bMapChanged = ImpCheckRefMapMode();
389
390 aInvalidRec = Rectangle(); // leermachen
391 for ( sal_uInt32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
392 {
393 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
394 if ( pParaPortion->MustRepaint() || ( pParaPortion->IsInvalid() && pParaPortion->IsVisible() ) )
395 {
396 if ( pParaPortion->IsInvalid() )
397 {
398 sal_Bool bChangedByDerivedClass = GetEditEnginePtr()->FormattingParagraph( nPara );
399 if ( bChangedByDerivedClass )
400 {
401 pParaPortion->GetTextPortions().Reset();
402 pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
403 }
404 }
405 // bei MustRepaint() sollte keine Formatierung noetig sein!
406 // 23.1.95: Evtl. ist sie durch eine andere Aktion aber doch
407 // ungueltig geworden!
408 // if ( pParaPortion->MustRepaint() || CreateLines( nPara ) )
409 if ( ( pParaPortion->MustRepaint() && !pParaPortion->IsInvalid() )
410 || CreateLines( nPara, nY ) )
411 {
412 if ( !bGrow && GetTextRanger() )
413 {
414 // Bei einer Aenderung der Hoehe muss alles weiter unten
415 // neu formatiert werden...
416 for ( sal_uInt32 n = nPara+1; n < GetParaPortions().Count(); n++ )
417 {
418 ParaPortion* pPP = GetParaPortions().GetObject( n );
419 pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
420 pPP->GetLines().Reset();
421 }
422 }
423 bGrow = sal_True;
424 if ( IsCallParaInsertedOrDeleted() )
425 GetEditEnginePtr()->ParagraphHeightChanged( nPara );
426 pParaPortion->SetMustRepaint( sal_False );
427 }
428
429 // InvalidRec nur einmal setzen...
430 if ( aInvalidRec.IsEmpty() )
431 {
432 // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
433 long nWidth = Max( (long)1, ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) );
434 Range aInvRange( GetInvalidYOffsets( pParaPortion ) );
435 aInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
436 Size( nWidth, aInvRange.Len() ) );
437 }
438 else
439 {
440 aInvalidRec.Bottom() = nY + pParaPortion->GetHeight();
441 }
442 }
443 else if ( bGrow )
444 {
445 aInvalidRec.Bottom() = nY + pParaPortion->GetHeight();
446 }
447 nY += pParaPortion->GetHeight();
448 }
449
450 // Man kann auch durch UpdateMode An=>AUS=>AN in die Formatierung gelangen...
451 // Optimierung erst nach Vobis-Auslieferung aktivieren...
452 // if ( !aInvalidRec.IsEmpty() )
453 {
454 sal_uInt32 nNewHeight = CalcTextHeight();
455 long nDiff = nNewHeight - nCurTextHeight;
456 if ( nDiff )
457 aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTHEIGHTCHANGED : EE_STAT_TEXTWIDTHCHANGED;
458 if ( nNewHeight < nCurTextHeight )
459 {
460 aInvalidRec.Bottom() = (long)Max( nNewHeight, nCurTextHeight );
461 if ( aInvalidRec.IsEmpty() )
462 {
463 aInvalidRec.Top() = 0;
464 // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
465 aInvalidRec.Left() = 0;
466 aInvalidRec.Right() = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
467 }
468 }
469
470 nCurTextHeight = nNewHeight;
471
472 if ( aStatus.AutoPageSize() )
473 CheckAutoPageSize();
474 else if ( nDiff )
475 {
476 for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
477 {
478 EditView* pView = aEditViews[nView];
479 ImpEditView* pImpView = pView->pImpEditView;
480 if ( pImpView->DoAutoHeight() )
481 {
482 Size aSz( pImpView->GetOutputArea().GetWidth(), nCurTextHeight );
483 if ( aSz.Height() > aMaxAutoPaperSize.Height() )
484 aSz.Height() = aMaxAutoPaperSize.Height();
485 else if ( aSz.Height() < aMinAutoPaperSize.Height() )
486 aSz.Height() = aMinAutoPaperSize.Height();
487 pImpView->ResetOutputArea( Rectangle(
488 pImpView->GetOutputArea().TopLeft(), aSz ) );
489 }
490 }
491 }
492 }
493
494 if ( aStatus.DoRestoreFont() )
495 GetRefDevice()->SetFont( aOldFont );
496 bIsFormatting = sal_False;
497 bFormatted = sal_True;
498
499 if ( bMapChanged )
500 GetRefDevice()->Pop();
501
502 CallStatusHdl(); // Falls Modified...
503
504 LeaveBlockNotifications();
505 }
506
ImpCheckRefMapMode()507 sal_Bool ImpEditEngine::ImpCheckRefMapMode()
508 {
509 sal_Bool bChange = sal_False;
510
511 if ( aStatus.DoFormat100() )
512 {
513 MapMode aMapMode( GetRefDevice()->GetMapMode() );
514 if ( aMapMode.GetScaleX().GetNumerator() != aMapMode.GetScaleX().GetDenominator() )
515 bChange = sal_True;
516 else if ( aMapMode.GetScaleY().GetNumerator() != aMapMode.GetScaleY().GetDenominator() )
517 bChange = sal_True;
518
519 if ( bChange )
520 {
521 Fraction Scale1( 1, 1 );
522 aMapMode.SetScaleX( Scale1 );
523 aMapMode.SetScaleY( Scale1 );
524 GetRefDevice()->Push();
525 GetRefDevice()->SetMapMode( aMapMode );
526 }
527 }
528
529 return bChange;
530 }
531
CheckAutoPageSize()532 void ImpEditEngine::CheckAutoPageSize()
533 {
534 Size aPrevPaperSize( GetPaperSize() );
535 if ( GetStatus().AutoPageWidth() )
536 aPaperSize.Width() = (long) !IsVertical() ? CalcTextWidth( sal_True ) : GetTextHeight();
537 if ( GetStatus().AutoPageHeight() )
538 aPaperSize.Height() = (long) !IsVertical() ? GetTextHeight() : CalcTextWidth( sal_True );
539
540 SetValidPaperSize( aPaperSize ); //Min, Max beruecksichtigen
541
542 if ( aPaperSize != aPrevPaperSize )
543 {
544 if ( ( !IsVertical() && ( aPaperSize.Width() != aPrevPaperSize.Width() ) )
545 || ( IsVertical() && ( aPaperSize.Height() != aPrevPaperSize.Height() ) ) )
546 {
547 // Falls davor zentriert/rechts oder Tabs...
548 aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTWIDTHCHANGED : EE_STAT_TEXTHEIGHTCHANGED;
549 for ( sal_uInt32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
550 {
551 // Es brauchen nur Absaetze neu formatiert werden,
552 // die nicht linksbuendig sind.
553 // Die Hoehe kann sich hier nicht mehr aendern.
554 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
555 ContentNode* pNode = pParaPortion->GetNode();
556 SvxAdjust eJustification = GetJustification( nPara );
557 if ( eJustification != SVX_ADJUST_LEFT )
558 {
559 pParaPortion->MarkSelectionInvalid( 0, pNode->Len() );
560 CreateLines( nPara, 0 ); // 0: Bei AutoPageSize kein TextRange!
561 }
562 }
563 }
564
565 Size aInvSize = aPaperSize;
566 if ( aPaperSize.Width() < aPrevPaperSize.Width() )
567 aInvSize.Width() = aPrevPaperSize.Width();
568 if ( aPaperSize.Height() < aPrevPaperSize.Height() )
569 aInvSize.Height() = aPrevPaperSize.Height();
570
571 Size aSz( aInvSize );
572 if ( IsVertical() )
573 {
574 aSz.Width() = aInvSize.Height();
575 aSz.Height() = aInvSize.Width();
576 }
577 aInvalidRec = Rectangle( Point(), aSz );
578
579
580 for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
581 {
582 EditView* pView = aEditViews[nView];
583 pView->pImpEditView->RecalcOutputArea();
584 }
585 }
586 }
587
ImplCalculateFontIndependentLineSpacing(const sal_Int32 nFontHeight)588 static sal_Int32 ImplCalculateFontIndependentLineSpacing( const sal_Int32 nFontHeight )
589 {
590 return ( nFontHeight * 12 ) / 10; // + 20%
591 }
592
CreateLines(sal_uInt32 nPara,sal_uInt32 nStartPosY)593 sal_Bool ImpEditEngine::CreateLines( sal_uInt32 nPara, sal_uInt32 nStartPosY )
594 {
595 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
596
597 // sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False
598 DBG_ASSERT( pParaPortion->GetNode(), "Portion ohne Node in CreateLines" );
599 DBG_ASSERT( pParaPortion->IsVisible(), "Unsichtbare Absaetze nicht formatieren!" );
600 DBG_ASSERT( pParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
601
602 sal_Bool bProcessingEmptyLine = ( pParaPortion->GetNode()->Len() == 0 );
603 sal_Bool bEmptyNodeWithPolygon = ( pParaPortion->GetNode()->Len() == 0 ) && GetTextRanger();
604
605 // ---------------------------------------------------------------
606 // Schnelle Sonderbehandlung fuer leere Absaetze...
607 // ---------------------------------------------------------------
608 if ( ( pParaPortion->GetNode()->Len() == 0 ) && !GetTextRanger() )
609 {
610 // schnelle Sonderbehandlung...
611 if ( pParaPortion->GetTextPortions().Count() )
612 pParaPortion->GetTextPortions().Reset();
613 if ( pParaPortion->GetLines().Count() )
614 pParaPortion->GetLines().Reset();
615 CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
616 return FinishCreateLines( pParaPortion );
617 }
618
619 // ---------------------------------------------------------------
620 // Initialisierung......
621 // ---------------------------------------------------------------
622
623 // Immer fuer 100% formatieren:
624 sal_Bool bMapChanged = ImpCheckRefMapMode();
625
626 if ( pParaPortion->GetLines().Count() == 0 )
627 {
628 EditLine* pL = new EditLine;
629 pParaPortion->GetLines().Insert( pL, 0 );
630 }
631
632 // ---------------------------------------------------------------
633 // Absatzattribute holen......
634 // ---------------------------------------------------------------
635 ContentNode* const pNode = pParaPortion->GetNode();
636
637 sal_Bool bRightToLeftPara = IsRightToLeft( nPara );
638
639 SvxAdjust eJustification = GetJustification( nPara );
640 sal_Bool bHyphenatePara = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HYPHENATE )).GetValue();
641 sal_Int32 nSpaceBefore = 0;
642 sal_Int32 nMinLabelWidth = 0;
643 sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pNode, &nSpaceBefore, &nMinLabelWidth );
644 const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pNode );
645 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&) pNode->GetContentAttribs().GetItem( EE_PARA_SBL );
646 const sal_Bool bScriptSpace = ((const SvxScriptSpaceItem&) pNode->GetContentAttribs().GetItem( EE_PARA_ASIANCJKSPACING )).GetValue();
647
648 // const sal_uInt16 nInvalidEnd = ( pParaPortion->GetInvalidDiff() > 0 )
649 // ? pParaPortion->GetInvalidPosStart() + pParaPortion->GetInvalidDiff()
650 // : pNode->Len();
651 const short nInvalidDiff = pParaPortion->GetInvalidDiff();
652 const sal_uInt16 nInvalidStart = pParaPortion->GetInvalidPosStart();
653 const sal_uInt16 nInvalidEnd = nInvalidStart + Abs( nInvalidDiff );
654
655 sal_Bool bQuickFormat = sal_False;
656 if ( !bEmptyNodeWithPolygon && !HasScriptType( nPara, i18n::ScriptType::COMPLEX ) )
657 {
658 if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff > 0 ) &&
659 ( pNode->Search( CH_FEATURE, nInvalidStart ) > nInvalidEnd ) )
660 {
661 bQuickFormat = sal_True;
662 }
663 else if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
664 {
665 // pruefen, ob loeschen ueber Portiongrenzen erfolgte...
666 sal_uInt16 nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!!
667 sal_uInt16 nEnd = nStart - nInvalidDiff; // neg.
668 bQuickFormat = sal_True;
669 sal_uInt16 nPos = 0;
670 sal_uInt16 nPortions = pParaPortion->GetTextPortions().Count();
671 for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
672 {
673 // Es darf kein Start/Ende im geloeschten Bereich liegen.
674 TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
675 nPos = nPos + pTP->GetLen();
676 if ( ( nPos > nStart ) && ( nPos < nEnd ) )
677 {
678 bQuickFormat = sal_False;
679 break;
680 }
681 }
682 }
683 }
684
685 // SW disables TEXT_LAYOUT_COMPLEX_DISABLED, so maybe I have to enable it...
686
687 // #114278# Saving both layout mode and language (since I'm
688 // potentially changing both)
689
690 GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
691
692 ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
693
694 sal_uInt16 nRealInvalidStart = nInvalidStart;
695
696 if ( bEmptyNodeWithPolygon )
697 {
698 TextPortion* pDummyPortion = new TextPortion( 0 );
699 pParaPortion->GetTextPortions().Reset();
700 pParaPortion->GetTextPortions().Insert( pDummyPortion, 0 );
701 }
702 else if ( bQuickFormat )
703 {
704 // schnellere Methode:
705 RecalcTextPortion( pParaPortion, nInvalidStart, nInvalidDiff );
706 }
707 else // nRealInvalidStart kann vor InvalidStart liegen, weil Portions geloescht....
708 {
709 CreateTextPortions( pParaPortion, nRealInvalidStart );
710 }
711
712
713 // ---------------------------------------------------------------
714 // Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
715 // Zeilen flaggen => nicht removen !
716 // ---------------------------------------------------------------
717
718 sal_uInt16 nLine = pParaPortion->GetLines().Count()-1;
719 for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
720 {
721 EditLine* pLine = pParaPortion->GetLines().GetObject( nL );
722 if ( pLine->GetEnd() > nRealInvalidStart ) // nicht nInvalidStart!
723 {
724 nLine = nL;
725 break;
726 }
727 pLine->SetValid();
728 }
729 // Eine Zeile davor beginnen...
730 // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
731 if ( nLine && ( !pParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->Len() ) || ( nInvalidDiff <= 0 ) ) )
732 nLine--;
733
734 EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
735
736 static Rectangle aZeroArea = Rectangle( Point(), Point() );
737 Rectangle aBulletArea( aZeroArea );
738 if ( !nLine )
739 {
740 aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
741 if ( aBulletArea.Right() > 0 )
742 pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
743 else
744 pParaPortion->SetBulletX( 0 ); // Falls Bullet falsch eingestellt.
745 }
746
747 // ---------------------------------------------------------------
748 // Ab hier alle Zeilen durchformatieren...
749 // ---------------------------------------------------------------
750 sal_uInt16 nDelFromLine = 0xFFFF;
751 sal_Bool bLineBreak = sal_False;
752
753 sal_uInt16 nIndex = pLine->GetStart();
754 EditLine aSaveLine( *pLine );
755 SvxFont aTmpFont( pNode->GetCharAttribs().GetDefFont() );
756
757 sal_Bool bCalcCharPositions = sal_True;
758 sal_Int32* pBuf = new sal_Int32[ pNode->Len() ];
759
760 sal_Bool bSameLineAgain = sal_False; // Fuer TextRanger, wenn sich die Hoehe aendert.
761 TabInfo aCurrentTab;
762
763 sal_Bool bForceOneRun = bEmptyNodeWithPolygon;
764 sal_Bool bCompressedChars = sal_False;
765
766 while ( ( nIndex < pNode->Len() ) || bForceOneRun )
767 {
768 bForceOneRun = sal_False;
769
770 sal_Bool bEOL = sal_False;
771 sal_Bool bEOC = sal_False;
772 sal_uInt16 nPortionStart = 0;
773 sal_uInt16 nPortionEnd = 0;
774
775 long nStartX = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
776 if ( nIndex == 0 )
777 {
778 long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
779 nStartX += nFI;
780
781 if ( !nLine && ( pParaPortion->GetBulletX() > nStartX ) )
782 {
783 // TL_NFLR nStartX += nFI; // Vielleicht reicht der LI?
784 // TL_NFLR if ( pParaPortion->GetBulletX() > nStartX )
785 nStartX = pParaPortion->GetBulletX();
786 }
787 }
788
789 long nMaxLineWidth;
790 if ( !IsVertical() )
791 nMaxLineWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : aPaperSize.Width();
792 else
793 nMaxLineWidth = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : aPaperSize.Height();
794
795 nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
796 nMaxLineWidth -= nStartX;
797
798 // Wenn PaperSize == long_max, kann ich keinen neg. Erstzeileneinzug
799 // abziehen (Overflow)
800 if ( ( nMaxLineWidth < 0 ) && ( nStartX < 0 ) )
801 nMaxLineWidth = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) - GetXValue( rLRItem.GetRight() );
802
803 // Wenn jetzt noch kleiner 0, kann es nur der rechte Rand sein.
804 if ( nMaxLineWidth <= 0 )
805 nMaxLineWidth = 1;
806
807 // Problem: Da eine Zeile _vor_ der ungueltigen Position mit der
808 // Formatierung begonnen wird, werden hier leider auch die Positionen
809 // neu bestimmt...
810 // Loesungsansatz:
811 // Die Zeile davor kann nur groesser werden, nicht kleiner
812 // => ...
813 if ( bCalcCharPositions )
814 pLine->GetCharPosArray().Remove( 0, pLine->GetCharPosArray().Count() );
815
816 sal_uInt16 nTmpPos = nIndex;
817 sal_uInt16 nTmpPortion = pLine->GetStartPortion();
818 long nTmpWidth = 0;
819 long nXWidth = nMaxLineWidth;
820 if ( nXWidth <= nTmpWidth ) // while muss 1x durchlaufen werden
821 nXWidth = nTmpWidth+1;
822
823 SvLongsPtr pTextRanges = 0;
824 long nTextExtraYOffset = 0;
825 long nTextXOffset = 0;
826 long nTextLineHeight = 0;
827 if ( GetTextRanger() )
828 {
829 GetTextRanger()->SetVertical( IsVertical() );
830
831 long nTextY = nStartPosY + GetEditCursor( pParaPortion, pLine->GetStart() ).Top();
832 if ( !bSameLineAgain )
833 {
834 SeekCursor( pNode, nTmpPos+1, aTmpFont );
835 aTmpFont.SetPhysFont( GetRefDevice() );
836 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
837
838 if ( IsFixedCellHeight() )
839 nTextLineHeight = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
840 else
841 nTextLineHeight = aTmpFont.GetPhysTxtSize( GetRefDevice(), String() ).Height();
842 // Metriken koennen groesser sein
843 FormatterFontMetric aTempFormatterMetrics;
844 RecalcFormatterFontMetrics( aTempFormatterMetrics, aTmpFont );
845 sal_uInt16 nLineHeight = aTempFormatterMetrics.GetHeight();
846 if ( nLineHeight > nTextLineHeight )
847 nTextLineHeight = nLineHeight;
848 }
849 else
850 nTextLineHeight = pLine->GetHeight();
851
852 nXWidth = 0;
853 while ( !nXWidth )
854 {
855 long nYOff = nTextY + nTextExtraYOffset;
856 long nYDiff = nTextLineHeight;
857 if ( IsVertical() )
858 {
859 long nMaxPolygonX = GetTextRanger()->GetBoundRect().Right();
860 nYOff = nMaxPolygonX-nYOff;
861 nYDiff = -nTextLineHeight;
862 }
863 pTextRanges = GetTextRanger()->GetTextRanges( Range( nYOff, nYOff + nYDiff ) );
864 DBG_ASSERT( pTextRanges, "GetTextRanges?!" );
865 long nMaxRangeWidth = 0;
866 // Den breitesten Bereich verwenden...
867 // Der breiteste Bereich koennte etwas verwirren, also
868 // generell den ersten. Am besten mal richtig mit Luecken.
869 // for ( sal_uInt16 n = 0; n < pTextRanges->Count(); )
870 if ( pTextRanges->Count() )
871 {
872 sal_uInt16 n = 0;
873 long nA = pTextRanges->GetObject( n++ );
874 long nB = pTextRanges->GetObject( n++ );
875 DBG_ASSERT( nA <= nB, "TextRange verdreht?" );
876 long nW = nB - nA;
877 if ( nW > nMaxRangeWidth )
878 {
879 nMaxRangeWidth = nW;
880 nTextXOffset = nA;
881 }
882 }
883 nXWidth = nMaxRangeWidth;
884 if ( nXWidth )
885 nMaxLineWidth = nXWidth - nStartX - GetXValue( rLRItem.GetRight() );
886 else
887 {
888 // Weiter unten im Polygon versuchen.
889 // Unterhalb des Polygons die Paperbreite verwenden.
890 nTextExtraYOffset += Max( (long)(nTextLineHeight / 10), (long)1 );
891 if ( ( nTextY + nTextExtraYOffset ) > GetTextRanger()->GetBoundRect().Bottom() )
892 {
893 nXWidth = !IsVertical() ? GetPaperSize().Width() : GetPaperSize().Height();
894 if ( !nXWidth ) // AutoPaperSize
895 nXWidth = 0x7FFFFFFF;
896 }
897 }
898 }
899 }
900
901 // Portion suchen, die nicht mehr in Zeile passt....
902 TextPortion* pPortion = 0;
903 sal_Bool bBrokenLine = sal_False;
904 bLineBreak = sal_False;
905 EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( pLine->GetStart() );
906 while ( ( nTmpWidth < nXWidth ) && !bEOL && ( nTmpPortion < pParaPortion->GetTextPortions().Count() ) )
907 {
908 nPortionStart = nTmpPos;
909 pPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
910 if ( pPortion->GetKind() == PORTIONKIND_HYPHENATOR )
911 {
912 // Portion wegschmeissen, ggf. die davor korrigieren, wenn
913 // die Hyph-Portion ein Zeichen geschluckt hat...
914 pParaPortion->GetTextPortions().Remove( nTmpPortion );
915 if ( nTmpPortion && pPortion->GetLen() )
916 {
917 nTmpPortion--;
918 TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
919 DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
920 nTmpWidth -= pPrev->GetSize().Width();
921 nTmpPos = nTmpPos - pPrev->GetLen();
922 pPrev->SetLen( pPrev->GetLen() + pPortion->GetLen() );
923 pPrev->GetSize().Width() = (-1);
924 }
925 delete pPortion;
926 DBG_ASSERT( nTmpPortion < pParaPortion->GetTextPortions().Count(), "Keine Portion mehr da!" );
927 pPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
928 }
929 DBG_ASSERT( pPortion->GetKind() != PORTIONKIND_HYPHENATOR, "CreateLines: Hyphenator-Portion!" );
930 DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Leere Portion in CreateLines ?!" );
931 if ( pNextFeature && ( pNextFeature->GetStart() == nTmpPos ) )
932 {
933 sal_uInt16 nWhich = pNextFeature->GetItem()->Which();
934 switch ( nWhich )
935 {
936 case EE_FEATURE_TAB:
937 {
938 long nOldTmpWidth = nTmpWidth;
939
940 // Tab-Pos suchen...
941 long nCurPos = nTmpWidth+nStartX;
942 // nCurPos -= rLRItem.GetTxtLeft(); // Tabs relativ zu LI
943 // Skalierung rausrechnen
944 if ( aStatus.DoStretch() && ( nStretchX != 100 ) )
945 nCurPos = nCurPos*100/nStretchX;
946
947 short nAllSpaceBeforeText = static_cast< short >(rLRItem.GetTxtLeft()/* + rLRItem.GetTxtLeft()*/ + nSpaceBeforeAndMinLabelWidth);
948 aCurrentTab.aTabStop = pNode->GetContentAttribs().FindTabStop( nCurPos - nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/, aEditDoc.GetDefTab() );
949 aCurrentTab.nTabPos = GetXValue( (long) ( aCurrentTab.aTabStop.GetTabPos() + nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/ ) );
950 aCurrentTab.bValid = sal_False;
951
952 // Switch direction in R2L para...
953 if ( bRightToLeftPara )
954 {
955 if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
956 aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_LEFT;
957 else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_LEFT )
958 aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_RIGHT;
959 }
960
961 if ( ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT ) ||
962 ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER ) ||
963 ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL ) )
964 {
965 // Bei LEFT/DEFAULT wird dieses Tab nicht mehr betrachtet.
966 aCurrentTab.bValid = sal_True;
967 aCurrentTab.nStartPosX = nTmpWidth;
968 aCurrentTab.nCharPos = nTmpPos;
969 aCurrentTab.nTabPortion = nTmpPortion;
970 }
971
972 pPortion->GetKind() = PORTIONKIND_TAB;
973 pPortion->SetExtraValue( aCurrentTab.aTabStop.GetFill() );
974 pPortion->GetSize().Width() = aCurrentTab.nTabPos - (nTmpWidth+nStartX);
975
976 // #90520# Height needed...
977 SeekCursor( pNode, nTmpPos+1, aTmpFont );
978 pPortion->GetSize().Height() = aTmpFont.QuickGetTextSize( GetRefDevice(), String(), 0, 0, NULL ).Height();
979
980 DBG_ASSERT( pPortion->GetSize().Width() >= 0, "Tab falsch berechnet!" );
981
982 nTmpWidth = aCurrentTab.nTabPos-nStartX;
983
984 // Wenn dies das erste Token in der Zeile ist,
985 // und nTmpWidth > aPaperSize.Width, habe ich eine
986 // Endlos-Schleife!
987 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
988 {
989 // Aber was jetzt ?
990 // Tab passend machen
991 pPortion->GetSize().Width() = nXWidth-nOldTmpWidth;
992 nTmpWidth = nXWidth-1;
993 bEOL = sal_True;
994 bBrokenLine = sal_True;
995 }
996 pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
997 bCompressedChars = sal_False;
998 }
999 break;
1000 case EE_FEATURE_LINEBR:
1001 {
1002 DBG_ASSERT( pPortion, "?!" );
1003 pPortion->GetSize().Width() = 0;
1004 bEOL = sal_True;
1005 bLineBreak = sal_True;
1006 pPortion->GetKind() = PORTIONKIND_LINEBREAK;
1007 bCompressedChars = sal_False;
1008 pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
1009 }
1010 break;
1011 case EE_FEATURE_FIELD:
1012 {
1013 // long nCurWidth = nTmpWidth;
1014 SeekCursor( pNode, nTmpPos+1, aTmpFont );
1015 sal_Unicode cChar = 0; // later: NBS?
1016 aTmpFont.SetPhysFont( GetRefDevice() );
1017 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
1018
1019 String aFieldValue = cChar ? String(cChar) : ((EditCharAttribField*)pNextFeature)->GetFieldValue();
1020 if ( bCalcCharPositions || !pPortion->HasValidSize() )
1021 {
1022 pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), aFieldValue, 0, aFieldValue.Len(), 0 );
1023 // Damit kein Scrollen bei ueberlangen Feldern
1024 if ( pPortion->GetSize().Width() > nXWidth )
1025 pPortion->GetSize().Width() = nXWidth;
1026 }
1027 nTmpWidth += pPortion->GetSize().Width();
1028 pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
1029 pPortion->GetKind() = cChar ? PORTIONKIND_TEXT : PORTIONKIND_FIELD;
1030 // Wenn dies das erste Token in der Zeile ist,
1031 // und nTmpWidth > aPaperSize.Width, habe ich eine
1032 // Endlos-Schleife!
1033 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
1034 {
1035 nTmpWidth = nXWidth-1;
1036 bEOL = sal_True;
1037 bBrokenLine = sal_True;
1038 }
1039 // Compression in Fields????
1040 // I think this could be a little bit difficult and is not very useful
1041 bCompressedChars = sal_False;
1042 }
1043 break;
1044 default: DBG_ERROR( "Was fuer ein Feature ?" );
1045 }
1046 pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
1047 }
1048 else
1049 {
1050 DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Empty Portion - Extra Space?!" );
1051 SeekCursor( pNode, nTmpPos+1, aTmpFont );
1052 aTmpFont.SetPhysFont( GetRefDevice() );
1053 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
1054
1055 if ( bCalcCharPositions || !pPortion->HasValidSize() )
1056 {
1057 pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), *pParaPortion->GetNode(), nTmpPos, pPortion->GetLen(), pBuf );
1058
1059 // #i9050# Do Kerning also behind portions...
1060 if ( ( aTmpFont.GetFixKerning() > 0 ) && ( ( nTmpPos + pPortion->GetLen() ) < pNode->Len() ) )
1061 pPortion->GetSize().Width() += aTmpFont.GetFixKerning();
1062 if ( IsFixedCellHeight() )
1063 pPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1064 }
1065 if ( bCalcCharPositions )
1066 {
1067 sal_uInt16 nLen = pPortion->GetLen();
1068 // Es wird am Anfang generell das Array geplaettet
1069 // => Immer einfach schnelles insert.
1070 sal_uInt16 nPos = nTmpPos - pLine->GetStart();
1071 pLine->GetCharPosArray().Insert( pBuf, nLen, nPos );
1072 }
1073
1074 // And now check for Compression:
1075 if ( pPortion->GetLen() && GetAsianCompressionMode() )
1076 bCompressedChars |= ImplCalcAsianCompression( pNode, pPortion, nTmpPos, (sal_Int32*)pLine->GetCharPosArray().GetData() + (nTmpPos-pLine->GetStart()), 10000, sal_False );
1077
1078 nTmpWidth += pPortion->GetSize().Width();
1079
1080 pPortion->SetRightToLeft( GetRightToLeft( nPara, nTmpPos+1 ) );
1081
1082 sal_uInt16 _nPortionEnd = nTmpPos + pPortion->GetLen();
1083 if( bScriptSpace && ( _nPortionEnd < pNode->Len() ) && ( nTmpWidth < nXWidth ) && IsScriptChange( EditPaM( pNode, _nPortionEnd ) ) )
1084 {
1085 sal_Bool bAllow = sal_False;
1086 sal_uInt16 nScriptTypeLeft = GetScriptType( EditPaM( pNode, _nPortionEnd ) );
1087 sal_uInt16 nScriptTypeRight = GetScriptType( EditPaM( pNode, _nPortionEnd+1 ) );
1088 if ( ( nScriptTypeLeft == i18n::ScriptType::ASIAN ) || ( nScriptTypeRight == i18n::ScriptType::ASIAN ) )
1089 bAllow = sal_True;
1090
1091 // No spacing within L2R/R2L nesting
1092 if ( bAllow )
1093 {
1094 long nExtraSpace = pPortion->GetSize().Height()/5;
1095 nExtraSpace = GetXValue( nExtraSpace );
1096 pPortion->GetSize().Width() += nExtraSpace;
1097 nTmpWidth += nExtraSpace;
1098 }
1099 }
1100 }
1101
1102 if ( aCurrentTab.bValid && ( nTmpPortion != aCurrentTab.nTabPortion ) )
1103 {
1104 long nWidthAfterTab = 0;
1105 for ( sal_uInt16 n = aCurrentTab.nTabPortion+1; n <= nTmpPortion; n++ )
1106 {
1107 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( n );
1108 nWidthAfterTab += pTP->GetSize().Width();
1109 }
1110 long nW = nWidthAfterTab; // Length before tab position
1111 if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
1112 {
1113 // nW = nWidthAfterTab;
1114 }
1115 else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER )
1116 {
1117 nW = nWidthAfterTab/2;
1118 }
1119 else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL )
1120 {
1121 // nW = nWidthAfterTab;
1122 String aText = GetSelected( EditSelection( EditPaM( pParaPortion->GetNode(), nTmpPos ),
1123 EditPaM( pParaPortion->GetNode(), nTmpPos + pPortion->GetLen() ) ) );
1124 sal_uInt16 nDecPos = aText.Search( aCurrentTab.aTabStop.GetDecimal() );
1125 if ( nDecPos != STRING_NOTFOUND )
1126 {
1127 nW -= pParaPortion->GetTextPortions().GetObject( nTmpPortion )->GetSize().Width();
1128 nW += aTmpFont.QuickGetTextSize( GetRefDevice(), *pParaPortion->GetNode(), nTmpPos, nDecPos, NULL ).Width();
1129 aCurrentTab.bValid = sal_False;
1130 }
1131 }
1132 else
1133 {
1134 DBG_ERROR( "CreateLines: Tab not handled!" );
1135 }
1136 long nMaxW = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nStartX;
1137 if ( nW >= nMaxW )
1138 {
1139 nW = nMaxW;
1140 aCurrentTab.bValid = sal_False;
1141 }
1142 TextPortion* pTabPortion = pParaPortion->GetTextPortions().GetObject( aCurrentTab.nTabPortion );
1143 pTabPortion->GetSize().Width() = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nW - nStartX;
1144 nTmpWidth = aCurrentTab.nStartPosX + pTabPortion->GetSize().Width() + nWidthAfterTab;
1145 }
1146
1147 nTmpPos = nTmpPos + pPortion->GetLen();
1148 nPortionEnd = nTmpPos;
1149 nTmpPortion++;
1150 if ( aStatus.OneCharPerLine() )
1151 bEOL = sal_True;
1152 }
1153
1154 DBG_ASSERT( pPortion, "no portion!?" );
1155
1156 aCurrentTab.bValid = sal_False;
1157
1158 // das war evtl. eine Portion zu weit:
1159 sal_Bool bFixedEnd = sal_False;
1160 if ( aStatus.OneCharPerLine() )
1161 {
1162 // Zustand vor Portion: ( bis auf nTmpWidth )
1163 nPortionEnd = nTmpPos;
1164 nTmpPos -= pPortion ? pPortion->GetLen() : 0;
1165 nPortionStart = nTmpPos;
1166 nTmpPortion--;
1167
1168 bEOL = sal_True;
1169 bEOC = sal_False;
1170
1171 // Und jetzt genau ein Zeichen:
1172 nTmpPos++;
1173 nTmpPortion++;
1174 nPortionEnd = nTmpPortion;
1175 // Eine Nicht-Feature-Portion muss gebrochen werden
1176 if ( pPortion->GetLen() > 1 )
1177 {
1178 DBG_ASSERT( pPortion && (pPortion->GetKind() == PORTIONKIND_TEXT), "Len>1, aber keine TextPortion?" );
1179 nTmpWidth -= pPortion ? pPortion->GetSize().Width() : 0;
1180 sal_uInt16 nP = SplitTextPortion( pParaPortion, nTmpPos, pLine );
1181 TextPortion* p = pParaPortion->GetTextPortions().GetObject( nP );
1182 DBG_ASSERT( p, "Portion ?!" );
1183 nTmpWidth += p->GetSize().Width();
1184 }
1185 }
1186 else if ( nTmpWidth >= nXWidth )
1187 {
1188 nPortionEnd = nTmpPos;
1189 nTmpPos -= pPortion ? pPortion->GetLen() : 0;
1190 nPortionStart = nTmpPos;
1191 nTmpPortion--;
1192 bEOL = sal_False;
1193 bEOC = sal_False;
1194 if( pPortion ) switch ( pPortion->GetKind() )
1195 {
1196 case PORTIONKIND_TEXT:
1197 {
1198 nTmpWidth -= pPortion->GetSize().Width();
1199 }
1200 break;
1201 case PORTIONKIND_FIELD:
1202 case PORTIONKIND_TAB:
1203 {
1204 nTmpWidth -= pPortion->GetSize().Width();
1205 bEOL = sal_True;
1206 bFixedEnd = sal_True;
1207 }
1208 break;
1209 default:
1210 {
1211 // Ein Feature wird nicht umgebrochen:
1212 DBG_ASSERT( ( pPortion->GetKind() == PORTIONKIND_LINEBREAK ), "Was fuer ein Feature ?" );
1213 bEOL = sal_True;
1214 bFixedEnd = sal_True;
1215 }
1216 }
1217 }
1218 else
1219 {
1220 bEOL = sal_True;
1221 bEOC = sal_True;
1222 pLine->SetEnd( nPortionEnd );
1223 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine TextPortions?" );
1224 pLine->SetEndPortion( (sal_uInt16)pParaPortion->GetTextPortions().Count() - 1 );
1225 }
1226
1227 if ( aStatus.OneCharPerLine() )
1228 {
1229 pLine->SetEnd( nPortionEnd );
1230 pLine->SetEndPortion( nTmpPortion-1 );
1231 }
1232 else if ( bFixedEnd )
1233 {
1234 pLine->SetEnd( nPortionStart );
1235 pLine->SetEndPortion( nTmpPortion-1 );
1236 }
1237 else if ( bLineBreak || bBrokenLine )
1238 {
1239 pLine->SetEnd( nPortionStart+1 );
1240 pLine->SetEndPortion( nTmpPortion-1 );
1241 bEOC = sal_False; // wurde oben gesetzt, vielleich mal die if's umstellen?
1242 }
1243 else if ( !bEOL )
1244 {
1245 DBG_ASSERT( pPortion && ((nPortionEnd-nPortionStart) == pPortion->GetLen()), "Doch eine andere Portion?!" );
1246 long nRemainingWidth = nMaxLineWidth - nTmpWidth;
1247 sal_Bool bCanHyphenate = ( aTmpFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL );
1248 if ( bCompressedChars && pPortion && ( pPortion->GetLen() > 1 ) && pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
1249 {
1250 // I need the manipulated DXArray for determining the break position...
1251 ImplCalcAsianCompression( pNode, pPortion, nPortionStart, const_cast<sal_Int32*>(( pLine->GetCharPosArray().GetData() + (nPortionStart-pLine->GetStart()) )), 10000, sal_True );
1252 }
1253 if( pPortion )
1254 ImpBreakLine( pParaPortion, pLine, pPortion, nPortionStart,
1255 nRemainingWidth, bCanHyphenate && bHyphenatePara );
1256 }
1257
1258 // ------------------------------------------------------------------
1259 // Zeile fertig => justieren
1260 // ------------------------------------------------------------------
1261
1262 // CalcTextSize sollte besser durch ein kontinuierliches
1263 // Registrieren ersetzt werden !
1264 Size aTextSize = pLine->CalcTextSize( *pParaPortion );
1265
1266 if ( aTextSize.Height() == 0 )
1267 {
1268 SeekCursor( pNode, pLine->GetStart()+1, aTmpFont );
1269 aTmpFont.SetPhysFont( pRefDev );
1270 ImplInitDigitMode( pRefDev, 0, 0, 0, aTmpFont.GetLanguage() );
1271
1272 if ( IsFixedCellHeight() )
1273 aTextSize.Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1274 else
1275 aTextSize.Height() = aTmpFont.GetPhysTxtSize( pRefDev, String() ).Height();
1276 pLine->SetHeight( (sal_uInt16)aTextSize.Height() );
1277 }
1278
1279 // Die Fontmetriken koennen nicht kontinuierlich berechnet werden,
1280 // wenn der Font sowieso eingestellt ist, weil ggf. ein grosser Font
1281 // erst nach dem Umbrechen ploetzlich in der naechsten Zeile landet
1282 // => Font-Metriken zu gross.
1283 FormatterFontMetric aFormatterMetrics;
1284 sal_uInt16 nTPos = pLine->GetStart();
1285 for ( sal_uInt16 nP = pLine->GetStartPortion(); nP <= pLine->GetEndPortion(); nP++ )
1286 {
1287 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( nP );
1288 // #95819# problem with hard font height attribute, when everything but the line break has this attribute
1289 if ( pTP->GetKind() != PORTIONKIND_LINEBREAK )
1290 {
1291 SeekCursor( pNode, nTPos+1, aTmpFont );
1292 aTmpFont.SetPhysFont( GetRefDevice() );
1293 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
1294 RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
1295 }
1296 nTPos = nTPos + pTP->GetLen();
1297 }
1298 sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
1299 if ( nLineHeight > pLine->GetHeight() )
1300 pLine->SetHeight( nLineHeight );
1301 pLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
1302
1303 bSameLineAgain = sal_False;
1304 if ( GetTextRanger() && ( pLine->GetHeight() > nTextLineHeight ) )
1305 {
1306 // Nochmal mit der anderen Groesse aufsetzen!
1307 bSameLineAgain = sal_True;
1308 }
1309
1310
1311 if ( !bSameLineAgain && !aStatus.IsOutliner() )
1312 {
1313 if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
1314 {
1315 sal_uInt16 nMinHeight = GetYValue( rLSItem.GetLineHeight() );
1316 sal_uInt16 nTxtHeight = pLine->GetHeight();
1317 if ( nTxtHeight < nMinHeight )
1318 {
1319 // Der Ascent muss um die Differenz angepasst werden:
1320 long nDiff = nMinHeight - nTxtHeight;
1321 pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff) );
1322 pLine->SetHeight( nMinHeight, nTxtHeight );
1323 }
1324 }
1325 else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
1326 {
1327 if ( nPara || IsFixedCellHeight() || pLine->GetStartPortion() ) // Nicht die aller erste Zeile
1328 {
1329 // #100508# There are documents with PropLineSpace 0, why?
1330 // (cmc: re above question :-) such documents can be seen by importing a .ppt
1331 if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
1332 {
1333 sal_uInt16 nTxtHeight = pLine->GetHeight();
1334 sal_Int32 nH = nTxtHeight;
1335 nH *= rLSItem.GetPropLineSpace();
1336 nH /= 100;
1337 // Der Ascent muss um die Differenz angepasst werden:
1338 long nDiff = pLine->GetHeight() - nH;
1339 if ( nDiff > pLine->GetMaxAscent() )
1340 nDiff = pLine->GetMaxAscent();
1341 pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() - nDiff) );
1342 pLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1343 }
1344 }
1345 }
1346 else if( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_FIX )
1347 {
1348 sal_uInt16 nTxtHeight = pLine->GetHeight();
1349 sal_uInt32 nH = rLSItem.GetLineHeight();
1350 if ( nH != nTxtHeight )
1351 {
1352 long nMaxAscent = pLine->GetMaxAscent() - nTxtHeight + nH;
1353 if ( nMaxAscent < 0 )
1354 nMaxAscent = 0 ;
1355 pLine->SetMaxAscent( (sal_uInt16)nMaxAscent );
1356 pLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1357 }
1358 }
1359 }
1360
1361
1362 // #80582# - Bullet should not influence line height
1363 // if ( !nLine )
1364 // {
1365 // long nBulletHeight = aBulletArea.GetHeight();
1366 // if ( nBulletHeight > (long)pLine->GetHeight() )
1367 // {
1368 // long nDiff = nBulletHeight - (long)pLine->GetHeight();
1369 // // nDiff auf oben und unten verteilen.
1370 // pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff/2) );
1371 // pLine->SetHeight( (sal_uInt16)nBulletHeight );
1372 // }
1373 // }
1374
1375 if ( ( !IsVertical() && aStatus.AutoPageWidth() ) ||
1376 ( IsVertical() && aStatus.AutoPageHeight() ) )
1377 {
1378 // Wenn die Zeile in die aktuelle Papierbreite passt, muss
1379 // diese Breite fuer die Ausrichting verwendet werden.
1380 // Wenn sie nicht passt oder sie die Papierbreite aendert,
1381 // wird bei Justification != LEFT sowieso noch mal formatiert.
1382 long nMaxLineWidthFix = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() )
1383 - GetXValue( rLRItem.GetRight() ) - nStartX;
1384 if ( aTextSize.Width() < nMaxLineWidthFix )
1385 nMaxLineWidth = nMaxLineWidthFix;
1386 }
1387
1388 if ( bCompressedChars )
1389 {
1390 long nRemainingWidth = nMaxLineWidth - aTextSize.Width();
1391 if ( nRemainingWidth > 0 )
1392 {
1393 ImplExpandCompressedPortions( pLine, pParaPortion, nRemainingWidth );
1394 aTextSize = pLine->CalcTextSize( *pParaPortion );
1395 }
1396 }
1397
1398 if ( pLine->IsHangingPunctuation() )
1399 {
1400 // Width from HangingPunctuation was set to 0 in ImpBreakLine,
1401 // check for rel width now, maybe create compression...
1402 long n = nMaxLineWidth - aTextSize.Width();
1403 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( pLine->GetEndPortion() );
1404 sal_uInt16 nPosInArray = pLine->GetEnd()-1-pLine->GetStart();
1405 long nNewValue = ( nPosInArray ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0 ) + n;
1406 pLine->GetCharPosArray()[ nPosInArray ] = nNewValue;
1407 pTP->GetSize().Width() += n;
1408 }
1409
1410 pLine->SetTextWidth( aTextSize.Width() );
1411 switch ( eJustification )
1412 {
1413 case SVX_ADJUST_CENTER:
1414 {
1415 long n = ( nMaxLineWidth - aTextSize.Width() ) / 2;
1416 n += nStartX; // Einrueckung bleibt erhalten.
1417 if ( n > 0 )
1418 pLine->SetStartPosX( (sal_uInt16)n );
1419 else
1420 pLine->SetStartPosX( 0 );
1421
1422 }
1423 break;
1424 case SVX_ADJUST_RIGHT:
1425 {
1426 // Bei automatisch umgebrochenen Zeilen, die ein Blank
1427 // am Ende enthalten, darf das Blank nicht ausgegeben werden!
1428
1429 long n = nMaxLineWidth - aTextSize.Width();
1430 n += nStartX; // Einrueckung bleibt erhalten.
1431 if ( n > 0 )
1432 pLine->SetStartPosX( (sal_uInt16)n );
1433 else
1434 pLine->SetStartPosX( 0 );
1435 }
1436 break;
1437 case SVX_ADJUST_BLOCK:
1438 {
1439 long nRemainingSpace = nMaxLineWidth - aTextSize.Width();
1440 pLine->SetStartPosX( (sal_uInt16)nStartX );
1441 if ( !bEOC && ( nRemainingSpace > 0 ) ) // nicht die letzte Zeile...
1442 ImpAdjustBlocks( pParaPortion, pLine, nRemainingSpace );
1443 }
1444 break;
1445 default:
1446 {
1447 pLine->SetStartPosX( (sal_uInt16)nStartX ); // FI, LI
1448 }
1449 break;
1450 }
1451
1452 // -----------------------------------------------------------------
1453 // pruefen, ob die Zeile neu ausgegeben werden muss...
1454 // -----------------------------------------------------------------
1455 pLine->SetInvalid();
1456
1457 // Wenn eine Portion umgebrochen wurde sind ggf. viel zu viele Positionen
1458 // im CharPosArray:
1459 if ( bCalcCharPositions )
1460 {
1461 sal_uInt16 nLen = pLine->GetLen();
1462 sal_uInt16 nCount = pLine->GetCharPosArray().Count();
1463 if ( nCount > nLen )
1464 pLine->GetCharPosArray().Remove( nLen, nCount-nLen );
1465 }
1466
1467 if ( GetTextRanger() )
1468 {
1469 if ( nTextXOffset )
1470 pLine->SetStartPosX( (sal_uInt16) ( pLine->GetStartPosX() + nTextXOffset ) );
1471 if ( nTextExtraYOffset )
1472 {
1473 pLine->SetHeight( (sal_uInt16) ( pLine->GetHeight() + nTextExtraYOffset ), 0, pLine->GetHeight() );
1474 pLine->SetMaxAscent( (sal_uInt16) ( pLine->GetMaxAscent() + nTextExtraYOffset ) );
1475 }
1476 }
1477
1478 // Fuer kleiner 0 noch ueberlegen!
1479 if ( pParaPortion->IsSimpleInvalid() /* && ( nInvalidDiff > 0 ) */ )
1480 {
1481 // Aenderung durch einfache Textaenderung...
1482 // Formatierung nicht abbrechen, da Portions evtl. wieder
1483 // gesplittet werden muessen!
1484 // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
1485 // Aber ggf. als Valid markieren, damit weniger Ausgabe...
1486 if ( pLine->GetEnd() < nInvalidStart )
1487 {
1488 if ( *pLine == aSaveLine )
1489 {
1490 pLine->SetValid();
1491 }
1492 }
1493 else
1494 {
1495 sal_uInt16 nStart = pLine->GetStart();
1496 sal_uInt16 nEnd = pLine->GetEnd();
1497
1498 if ( nStart > nInvalidEnd )
1499 {
1500 if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
1501 ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
1502 {
1503 pLine->SetValid();
1504 if ( bCalcCharPositions && bQuickFormat )
1505 {
1506 bCalcCharPositions = sal_False;
1507 bLineBreak = sal_False;
1508 pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
1509 break;
1510 }
1511 }
1512 }
1513 else if ( bCalcCharPositions && bQuickFormat && ( nEnd > nInvalidEnd) )
1514 {
1515 // Wenn die ungueltige Zeile so endet, dass die naechste an
1516 // der 'gleichen' Textstelle wie vorher beginnt, also nicht
1517 // anders umgebrochen wird, brauche ich dort auch nicht die
1518 // textbreiten neu bestimmen:
1519 if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
1520 {
1521 bCalcCharPositions = sal_False;
1522 bLineBreak = sal_False;
1523 pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
1524 break;
1525 }
1526 }
1527 }
1528 }
1529
1530 if ( !bSameLineAgain )
1531 {
1532 nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende
1533 // weil nEnd hinter das letzte Zeichen zeigt!
1534
1535 sal_uInt16 nEndPortion = pLine->GetEndPortion();
1536
1537 // Naechste Zeile oder ggf. neue Zeile....
1538 pLine = 0;
1539 if ( nLine < pParaPortion->GetLines().Count()-1 )
1540 pLine = pParaPortion->GetLines().GetObject( ++nLine );
1541 if ( pLine && ( nIndex >= pNode->Len() ) )
1542 {
1543 nDelFromLine = nLine;
1544 break;
1545 }
1546 if ( !pLine )
1547 {
1548 if ( nIndex < pNode->Len() )
1549 {
1550 pLine = new EditLine;
1551 pParaPortion->GetLines().Insert( pLine, ++nLine );
1552 }
1553 else if ( nIndex && bLineBreak && GetTextRanger() )
1554 {
1555 // normally CreateAndInsertEmptyLine would be called, but I want to use
1556 // CreateLines, so I need Polygon code only here...
1557 TextPortion* pDummyPortion = new TextPortion( 0 );
1558 pParaPortion->GetTextPortions().Insert( pDummyPortion, pParaPortion->GetTextPortions().Count() );
1559 pLine = new EditLine;
1560 pParaPortion->GetLines().Insert( pLine, ++nLine );
1561 bForceOneRun = sal_True;
1562 bProcessingEmptyLine = sal_True;
1563 }
1564 }
1565 if ( pLine )
1566 {
1567 aSaveLine = *pLine;
1568 pLine->SetStart( nIndex );
1569 pLine->SetEnd( nIndex );
1570 pLine->SetStartPortion( nEndPortion+1 );
1571 pLine->SetEndPortion( nEndPortion+1 );
1572 }
1573 }
1574 } // while ( Index < Len )
1575
1576 if ( nDelFromLine != 0xFFFF )
1577 pParaPortion->GetLines().DeleteFromLine( nDelFromLine );
1578
1579 DBG_ASSERT( pParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" );
1580
1581 if ( bLineBreak == sal_True )
1582 CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
1583
1584 delete[] pBuf;
1585
1586 sal_Bool bHeightChanged = FinishCreateLines( pParaPortion );
1587
1588 if ( bMapChanged )
1589 GetRefDevice()->Pop();
1590
1591 GetRefDevice()->Pop();
1592
1593 return bHeightChanged;
1594 }
1595
CreateAndInsertEmptyLine(ParaPortion * pParaPortion,sal_uInt32)1596 void ImpEditEngine::CreateAndInsertEmptyLine( ParaPortion* pParaPortion, sal_uInt32 )
1597 {
1598 DBG_ASSERT( !GetTextRanger(), "Don't use CreateAndInsertEmptyLine with a polygon!" );
1599
1600 EditLine* pTmpLine = new EditLine;
1601 pTmpLine->SetStart( pParaPortion->GetNode()->Len() );
1602 pTmpLine->SetEnd( pParaPortion->GetNode()->Len() );
1603 pParaPortion->GetLines().Insert( pTmpLine, pParaPortion->GetLines().Count() );
1604
1605 sal_Bool bLineBreak = pParaPortion->GetNode()->Len() ? sal_True : sal_False;
1606 sal_Int32 nSpaceBefore = 0;
1607 sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pParaPortion->GetNode(), &nSpaceBefore );
1608 const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pParaPortion->GetNode() );
1609 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pParaPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
1610 short nStartX = GetXValue( (short)(rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBefore));
1611
1612 Rectangle aBulletArea = Rectangle( Point(), Point() );
1613 if ( bLineBreak == sal_True )
1614 {
1615 nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
1616 }
1617 else
1618 {
1619 aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
1620 if ( aBulletArea.Right() > 0 )
1621 pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
1622 else
1623 pParaPortion->SetBulletX( 0 ); // Falls Bullet falsch eingestellt.
1624 if ( pParaPortion->GetBulletX() > nStartX )
1625 {
1626 nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
1627 if ( pParaPortion->GetBulletX() > nStartX )
1628 nStartX = pParaPortion->GetBulletX();
1629 }
1630 }
1631
1632 SvxFont aTmpFont;
1633 SeekCursor( pParaPortion->GetNode(), bLineBreak ? pParaPortion->GetNode()->Len() : 0, aTmpFont );
1634 aTmpFont.SetPhysFont( pRefDev );
1635
1636 TextPortion* pDummyPortion = new TextPortion( 0 );
1637 pDummyPortion->GetSize() = aTmpFont.GetPhysTxtSize( pRefDev, String() );
1638 if ( IsFixedCellHeight() )
1639 pDummyPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1640 pParaPortion->GetTextPortions().Insert( pDummyPortion, pParaPortion->GetTextPortions().Count() );
1641 FormatterFontMetric aFormatterMetrics;
1642 RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
1643 pTmpLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
1644 pTmpLine->SetHeight( (sal_uInt16) pDummyPortion->GetSize().Height() );
1645 sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
1646 if ( nLineHeight > pTmpLine->GetHeight() )
1647 pTmpLine->SetHeight( nLineHeight );
1648
1649 if ( !aStatus.IsOutliner() )
1650 {
1651 sal_uInt32 nPara = GetParaPortions().GetPos( pParaPortion );
1652 SvxAdjust eJustification = GetJustification( nPara );
1653 long nMaxLineWidth = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
1654 nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
1655 long nTextXOffset = 0;
1656 if ( nMaxLineWidth < 0 )
1657 nMaxLineWidth = 1;
1658 if ( eJustification == SVX_ADJUST_CENTER )
1659 nStartX = sal::static_int_cast< short >(nMaxLineWidth / 2);
1660 else if ( eJustification == SVX_ADJUST_RIGHT )
1661 nStartX = sal::static_int_cast< short >(nMaxLineWidth);
1662
1663 nStartX = sal::static_int_cast< short >(nStartX + nTextXOffset);
1664 }
1665
1666 pTmpLine->SetStartPosX( nStartX );
1667
1668 if ( !aStatus.IsOutliner() )
1669 {
1670 if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
1671 {
1672 sal_uInt16 nMinHeight = rLSItem.GetLineHeight();
1673 sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1674 if ( nTxtHeight < nMinHeight )
1675 {
1676 // Der Ascent muss um die Differenz angepasst werden:
1677 long nDiff = nMinHeight - nTxtHeight;
1678 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff) );
1679 pTmpLine->SetHeight( nMinHeight, nTxtHeight );
1680 }
1681 }
1682 else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
1683 {
1684 sal_uInt32 nPara = GetParaPortions().GetPos( pParaPortion );
1685 if ( nPara || IsFixedCellHeight() || pTmpLine->GetStartPortion() ) // Nicht die aller erste Zeile
1686 {
1687 // #100508# There are documents with PropLineSpace 0, why?
1688 // (cmc: re above question :-) such documents can be seen by importing a .ppt
1689 if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
1690 {
1691 sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1692 sal_Int32 nH = nTxtHeight;
1693 nH *= rLSItem.GetPropLineSpace();
1694 nH /= 100;
1695 // Der Ascent muss um die Differenz angepasst werden:
1696 long nDiff = pTmpLine->GetHeight() - nH;
1697 if ( nDiff > pTmpLine->GetMaxAscent() )
1698 nDiff = pTmpLine->GetMaxAscent();
1699 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() - nDiff) );
1700 pTmpLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1701 }
1702 }
1703 }
1704 else if( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_FIX )
1705 {
1706 sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1707 sal_uInt32 nH = rLSItem.GetLineHeight();
1708 if ( nH != nTxtHeight )
1709 {
1710 long nMaxAscent = pTmpLine->GetMaxAscent() - nTxtHeight + nH;
1711 if ( nMaxAscent < 0 )
1712 nMaxAscent = 0 ;
1713 pTmpLine->SetMaxAscent( (sal_uInt16)nMaxAscent );
1714 pTmpLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1715 }
1716 }
1717 }
1718
1719 if ( !bLineBreak )
1720 {
1721 long nMinHeight = aBulletArea.GetHeight();
1722 if ( nMinHeight > (long)pTmpLine->GetHeight() )
1723 {
1724 long nDiff = nMinHeight - (long)pTmpLine->GetHeight();
1725 // nDiff auf oben und unten verteilen.
1726 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff/2) );
1727 pTmpLine->SetHeight( (sal_uInt16)nMinHeight );
1728 }
1729 }
1730 else
1731 {
1732 // -2: Die neue ist bereits eingefuegt.
1733 #ifdef DBG_UTIL
1734 EditLine* pLastLine = pParaPortion->GetLines().GetObject( pParaPortion->GetLines().Count()-2 );
1735 DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" );
1736 DBG_ASSERT( pLastLine->GetEnd() == pParaPortion->GetNode()->Len(), "Doch anders?" );
1737 #endif
1738 // pTmpLine->SetStart( pLastLine->GetEnd() );
1739 // pTmpLine->SetEnd( pLastLine->GetEnd() );
1740 sal_uInt16 nPos = (sal_uInt16) pParaPortion->GetTextPortions().Count() - 1 ;
1741 pTmpLine->SetStartPortion( nPos );
1742 pTmpLine->SetEndPortion( nPos );
1743 }
1744 }
1745
FinishCreateLines(ParaPortion * pParaPortion)1746 sal_Bool ImpEditEngine::FinishCreateLines( ParaPortion* pParaPortion )
1747 {
1748 // CalcCharPositions( pParaPortion );
1749 pParaPortion->SetValid();
1750 long nOldHeight = pParaPortion->GetHeight();
1751 // sal_uInt32 nPos = GetParaPortions().GetPos( pParaPortion );
1752 // DBG_ASSERT( nPos != USHRT_MAX, "FinishCreateLines: Portion nicht in Liste!" );
1753 // ParaPortion* pPrev = nPos ? GetParaPortions().GetObject( nPos-1 ) : 0;
1754 CalcHeight( pParaPortion );
1755
1756 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "FinishCreateLines: Keine Text-Portion?" );
1757 sal_Bool bRet = ( pParaPortion->GetHeight() != nOldHeight );
1758 return bRet;
1759 }
1760
ImpBreakLine(ParaPortion * pParaPortion,EditLine * pLine,TextPortion * pPortion,sal_uInt16 nPortionStart,long nRemainingWidth,sal_Bool bCanHyphenate)1761 void ImpEditEngine::ImpBreakLine( ParaPortion* pParaPortion, EditLine* pLine, TextPortion* pPortion, sal_uInt16 nPortionStart, long nRemainingWidth, sal_Bool bCanHyphenate )
1762 {
1763 ContentNode* const pNode = pParaPortion->GetNode();
1764
1765 sal_uInt16 nBreakInLine = nPortionStart - pLine->GetStart();
1766 sal_uInt16 nMax = nBreakInLine + pPortion->GetLen();
1767 while ( ( nBreakInLine < nMax ) && ( pLine->GetCharPosArray()[nBreakInLine] < nRemainingWidth ) )
1768 nBreakInLine++;
1769
1770 sal_uInt16 nMaxBreakPos = nBreakInLine + pLine->GetStart();
1771 sal_uInt16 nBreakPos = 0xFFFF;
1772
1773 sal_Bool bCompressBlank = sal_False;
1774 sal_Bool bHyphenated = sal_False;
1775 sal_Bool bHangingPunctuation = sal_False;
1776 sal_Unicode cAlternateReplChar = 0;
1777 sal_Unicode cAlternateExtraChar = 0;
1778
1779 if ( ( nMaxBreakPos < ( nMax + pLine->GetStart() ) ) && ( pNode->GetChar( nMaxBreakPos ) == ' ' ) )
1780 {
1781 // Break behind the blank, blank will be compressed...
1782 nBreakPos = nMaxBreakPos + 1;
1783 bCompressBlank = sal_True;
1784 }
1785 else
1786 {
1787 sal_uInt16 nMinBreakPos = pLine->GetStart();
1788 sal_uInt16 nAttrs = pNode->GetCharAttribs().GetAttribs().Count();
1789 for ( sal_uInt16 nAttr = nAttrs; nAttr; )
1790 {
1791 EditCharAttrib* pAttr = pNode->GetCharAttribs().GetAttribs()[--nAttr];
1792 if ( pAttr->IsFeature() && ( pAttr->GetEnd() > nMinBreakPos ) && ( pAttr->GetEnd() <= nMaxBreakPos ) )
1793 {
1794 nMinBreakPos = pAttr->GetEnd();
1795 break;
1796 }
1797 }
1798
1799 lang::Locale aLocale = GetLocale( EditPaM( pNode, nMaxBreakPos ) );
1800
1801 Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1802 OUString aText( *pNode );
1803 Reference< XHyphenator > xHyph;
1804 if ( bCanHyphenate )
1805 xHyph = GetHyphenator();
1806 i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, Sequence< PropertyValue >(), 1 );
1807 i18n::LineBreakUserOptions aUserOptions;
1808
1809 const i18n::ForbiddenCharacters* pForbidden = GetForbiddenCharsTable()->GetForbiddenCharacters( SvxLocaleToLanguage( aLocale ), sal_True );
1810 aUserOptions.forbiddenBeginCharacters = pForbidden->beginLine;
1811 aUserOptions.forbiddenEndCharacters = pForbidden->endLine;
1812 aUserOptions.applyForbiddenRules = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_FORBIDDENRULES )).GetValue();
1813 aUserOptions.allowPunctuationOutsideMargin = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HANGINGPUNCTUATION )).GetValue();
1814 aUserOptions.allowHyphenateEnglish = sal_False;
1815
1816 i18n::LineBreakResults aLBR = _xBI->getLineBreak( *pNode, nMaxBreakPos, aLocale, nMinBreakPos, aHyphOptions, aUserOptions );
1817 nBreakPos = (sal_uInt16)aLBR.breakIndex;
1818
1819 // BUG in I18N - under special condition (break behind field, #87327#) breakIndex is < nMinBreakPos
1820 if ( nBreakPos < nMinBreakPos )
1821 {
1822 nBreakPos = nMinBreakPos;
1823 }
1824 else if ( ( nBreakPos > nMaxBreakPos ) && !aUserOptions.allowPunctuationOutsideMargin )
1825 {
1826 DBG_ERROR( "I18N: XBreakIterator::getLineBreak returns position > Max" );
1827 nBreakPos = nMaxBreakPos;
1828 }
1829
1830 // #101795# nBreakPos can never be outside the portion, even not with hangig punctuation
1831 if ( nBreakPos > nMaxBreakPos )
1832 nBreakPos = nMaxBreakPos;
1833
1834 // BUG in I18N - the japanese dot is in the next line!
1835 // !!! Testen!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1836 if ( (nBreakPos + ( aUserOptions.allowPunctuationOutsideMargin ? 0 : 1 ) ) <= nMaxBreakPos )
1837 {
1838 sal_Unicode cFirstInNextLine = ( (nBreakPos+1) < pNode->Len() ) ? pNode->GetChar( nBreakPos ) : 0;
1839 if ( cFirstInNextLine == 12290 )
1840 nBreakPos++;
1841 }
1842
1843 bHangingPunctuation = ( nBreakPos > nMaxBreakPos ) ? sal_True : sal_False;
1844 pLine->SetHangingPunctuation( bHangingPunctuation );
1845
1846 #ifndef SVX_LIGHT
1847 // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch
1848 // die Silbentrennung jagen...
1849 // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt,
1850 // nBreakPos ist der Wort-Anfang
1851 // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort
1852 // auf mehr als Zwei Zeilen gebrochen wird...
1853 if ( !bHangingPunctuation && bCanHyphenate && GetHyphenator().is() )
1854 {
1855 i18n::Boundary aBoundary = _xBI->getWordBoundary( *pNode, nBreakPos, GetLocale( EditPaM( pNode, nBreakPos ) ), ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True );
1856 // sal_uInt16 nWordStart = nBreakPos;
1857 // sal_uInt16 nBreakPos_OLD = nBreakPos;
1858 sal_uInt16 nWordStart = nBreakPos;
1859 sal_uInt16 nWordEnd = (sal_uInt16) aBoundary.endPos;
1860 DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
1861
1862 sal_uInt16 nWordLen = nWordEnd - nWordStart;
1863 if ( ( nWordEnd >= nMaxBreakPos ) && ( nWordLen > 3 ) )
1864 {
1865 // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
1866 // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
1867 String aWord( *pNode, nWordStart, nWordLen );
1868 sal_uInt16 nMinTrail = nWordEnd-nMaxBreakPos+1; //+1: Vor dem angeknacksten Buchstaben
1869 Reference< XHyphenatedWord > xHyphWord;
1870 if (xHyphenator.is())
1871 xHyphWord = xHyphenator->hyphenate( aWord, aLocale, aWord.Len() - nMinTrail, Sequence< PropertyValue >() );
1872 if (xHyphWord.is())
1873 {
1874 sal_Bool bAlternate = xHyphWord->isAlternativeSpelling();
1875 sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos();
1876
1877 if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= (pLine->GetStart() + 2 ) ) )
1878 {
1879 if ( !bAlternate )
1880 {
1881 bHyphenated = sal_True;
1882 nBreakPos = nWordStart + _nWordLen;
1883 }
1884 else
1885 {
1886 String aAlt( xHyphWord->getHyphenatedWord() );
1887
1888 // Wir gehen von zwei Faellen aus, die nun
1889 // vorliegen koennen:
1890 // 1) packen wird zu pak-ken
1891 // 2) Schiffahrt wird zu Schiff-fahrt
1892 // In Fall 1 muss ein Zeichen ersetzt werden,
1893 // in Fall 2 wird ein Zeichen hinzugefuegt.
1894 // Die Identifikation wird erschwert durch Worte wie
1895 // "Schiffahrtsbrennesseln", da der Hyphenator alle
1896 // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln"
1897 // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom
1898 // Index des AlternativWord auf aWord schliessen.
1899
1900 // Das ganze geraffel wird durch eine Funktion am
1901 // Hyphenator vereinfacht werden, sobald AMA sie einbaut...
1902 sal_uInt16 nAltStart = _nWordLen - 1;
1903 sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len());
1904 sal_uInt16 nTxtEnd = nTxtStart;
1905 sal_uInt16 nAltEnd = nAltStart;
1906
1907 // Die Bereiche zwischen den nStart und nEnd ist
1908 // die Differenz zwischen Alternativ- und OriginalString.
1909 while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() &&
1910 aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) )
1911 {
1912 ++nTxtEnd;
1913 ++nAltEnd;
1914 }
1915
1916 // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt:
1917 if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
1918 aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) )
1919 {
1920 ++nAltEnd;
1921 ++nTxtStart;
1922 ++nTxtEnd;
1923 }
1924
1925 DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" );
1926
1927 if ( nTxtEnd > nTxtStart )
1928 cAlternateReplChar = aAlt.GetChar( nAltStart );
1929 else
1930 cAlternateExtraChar = aAlt.GetChar( nAltStart );
1931
1932 bHyphenated = sal_True;
1933 nBreakPos = nWordStart + nTxtStart;
1934 if ( cAlternateReplChar )
1935 nBreakPos++;
1936 }
1937 }
1938 }
1939 }
1940 }
1941
1942 #endif // !SVX_LIGHT
1943
1944 if ( nBreakPos <= pLine->GetStart() )
1945 {
1946 // keine Trenner in Zeile => abhacken !
1947 nBreakPos = nMaxBreakPos;
1948 // MT: I18N nextCharacters !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1949 if ( nBreakPos <= pLine->GetStart() )
1950 nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife!
1951 }
1952 }
1953
1954 // die angeknackste Portion ist die End-Portion
1955 pLine->SetEnd( nBreakPos );
1956
1957 sal_uInt16 nEndPortion = SplitTextPortion( pParaPortion, nBreakPos, pLine );
1958
1959 if ( !bCompressBlank && !bHangingPunctuation )
1960 {
1961 // #96187# When justification is not SVX_ADJUST_LEFT, it's important to compress
1962 // the trailing space even if there is enough room for the space...
1963 // Don't check for SVX_ADJUST_LEFT, doesn't matter to compress in this case too...
1964 DBG_ASSERT( nBreakPos > pLine->GetStart(), "ImpBreakLines - BreakPos not expected!" );
1965 if ( pNode->GetChar( nBreakPos-1 ) == ' ' )
1966 bCompressBlank = sal_True;
1967 }
1968
1969 if ( bCompressBlank || bHangingPunctuation )
1970 {
1971 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( nEndPortion );
1972 DBG_ASSERT( pTP->GetKind() == PORTIONKIND_TEXT, "BlankRubber: Keine TextPortion!" );
1973 DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
1974 sal_uInt16 nPosInArray = nBreakPos - 1 - pLine->GetStart();
1975 pTP->GetSize().Width() = ( nPosInArray && ( pTP->GetLen() > 1 ) ) ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0;
1976 pLine->GetCharPosArray()[ nPosInArray ] = pTP->GetSize().Width();
1977 }
1978 else if ( bHyphenated )
1979 {
1980 // Eine Portion fuer den Trenner einbauen...
1981 TextPortion* pHyphPortion = new TextPortion( 0 );
1982 pHyphPortion->GetKind() = PORTIONKIND_HYPHENATOR;
1983 String aHyphText( CH_HYPH );
1984 if ( cAlternateReplChar )
1985 {
1986 TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nEndPortion );
1987 DBG_ASSERT( pPrev && pPrev->GetLen(), "Hyphenate: Prev portion?!" );
1988 pPrev->SetLen( pPrev->GetLen() - 1 );
1989 pHyphPortion->SetLen( 1 );
1990 pHyphPortion->SetExtraValue( cAlternateReplChar );
1991 // Breite der Portion davor korrigieren:
1992 pPrev->GetSize().Width() =
1993 pLine->GetCharPosArray()[ nBreakPos-1 - pLine->GetStart() - 1 ];
1994 }
1995 else if ( cAlternateExtraChar )
1996 {
1997 pHyphPortion->SetExtraValue( cAlternateExtraChar );
1998 aHyphText.Insert( cAlternateExtraChar, 0 );
1999 }
2000
2001 // Breite der Hyph-Portion ermitteln:
2002 SvxFont aFont;
2003 SeekCursor( pParaPortion->GetNode(), nBreakPos, aFont );
2004 aFont.SetPhysFont( GetRefDevice() );
2005 pHyphPortion->GetSize().Height() = GetRefDevice()->GetTextHeight();
2006 pHyphPortion->GetSize().Width() = GetRefDevice()->GetTextWidth( aHyphText );
2007
2008 pParaPortion->GetTextPortions().Insert( pHyphPortion, ++nEndPortion );
2009 }
2010 pLine->SetEndPortion( nEndPortion );
2011 }
2012
ImpAdjustBlocks(ParaPortion * pParaPortion,EditLine * pLine,long nRemainingSpace)2013 void ImpEditEngine::ImpAdjustBlocks( ParaPortion* pParaPortion, EditLine* pLine, long nRemainingSpace )
2014 {
2015 DBG_ASSERT( nRemainingSpace > 0, "AdjustBlocks: Etwas zuwenig..." );
2016 DBG_ASSERT( pLine, "AdjustBlocks: Zeile ?!" );
2017 if ( ( nRemainingSpace < 0 ) || pLine->IsEmpty() )
2018 return ;
2019
2020 const sal_uInt16 nFirstChar = pLine->GetStart();
2021 const sal_uInt16 nLastChar = pLine->GetEnd() -1; // Last zeigt dahinter
2022 ContentNode* pNode = pParaPortion->GetNode();
2023
2024 DBG_ASSERT( nLastChar < pNode->Len(), "AdjustBlocks: Out of range!" );
2025
2026 // Search blanks or Kashidas...
2027 SvUShorts aPositions;
2028 sal_uInt16 nChar;
2029 for ( nChar = nFirstChar; nChar <= nLastChar; nChar++ )
2030 {
2031 if ( pNode->GetChar(nChar) == ' ' )
2032 {
2033 // Don't use blank if language is arabic
2034 LanguageType eLang = GetLanguage( EditPaM( pNode, nChar ) );
2035 if ( MsLangId::getPrimaryLanguage( eLang) != LANGUAGE_ARABIC_PRIMARY_ONLY )
2036 aPositions.Insert( nChar, aPositions.Count() );
2037 }
2038 }
2039
2040 // Kashidas ?
2041 ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions );
2042
2043
2044 if ( !aPositions.Count() )
2045 return;
2046
2047 // Wenn das letzte Zeichen ein Blank ist, will ich es nicht haben!
2048 // Die Breite muss auf die Blocker davor verteilt werden...
2049 // Aber nicht, wenn es das einzige ist
2050 if ( ( pNode->GetChar( nLastChar ) == ' ' ) && ( aPositions.Count() > 1 ) && ( MsLangId::getPrimaryLanguage( GetLanguage( EditPaM( pNode, nLastChar ) ) ) != LANGUAGE_ARABIC_PRIMARY_ONLY ) )
2051 {
2052 aPositions.Remove( aPositions.Count()-1, 1 );
2053 sal_uInt16 nPortionStart, nPortion;
2054 nPortion = pParaPortion->GetTextPortions().FindPortion( nLastChar+1, nPortionStart );
2055 TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
2056 long nRealWidth = pLine->GetCharPosArray()[nLastChar-nFirstChar];
2057 long nBlankWidth = nRealWidth;
2058 if ( nLastChar > nPortionStart )
2059 nBlankWidth -= pLine->GetCharPosArray()[nLastChar-nFirstChar-1];
2060 // Evtl. ist das Blank schon in ImpBreakLine abgezogen worden:
2061 if ( nRealWidth == pLastPortion->GetSize().Width() )
2062 {
2063 // Beim letzten Zeichen muss die Portion hinter dem Blank aufhoeren
2064 // => Korrektur vereinfachen:
2065 DBG_ASSERT( ( nPortionStart + pLastPortion->GetLen() ) == ( nLastChar+1 ), "Blank doch nicht am Portion-Ende?!" );
2066 pLastPortion->GetSize().Width() -= nBlankWidth;
2067 nRemainingSpace += nBlankWidth;
2068 }
2069 pLine->GetCharPosArray()[nLastChar-nFirstChar] -= nBlankWidth;
2070 }
2071
2072 sal_uInt16 nGaps = aPositions.Count();
2073 const long nMore4Everyone = nRemainingSpace / nGaps;
2074 long nSomeExtraSpace = nRemainingSpace - nMore4Everyone*nGaps;
2075
2076 DBG_ASSERT( nSomeExtraSpace < (long)nGaps, "AdjustBlocks: ExtraSpace zu gross" );
2077 DBG_ASSERT( nSomeExtraSpace >= 0, "AdjustBlocks: ExtraSpace < 0 " );
2078
2079 // Die Positionen im Array und die Portion-Breiten korrigieren:
2080 // Letztes Zeichen wird schon nicht mehr beachtet...
2081 for ( sal_uInt16 n = 0; n < aPositions.Count(); n++ )
2082 {
2083 nChar = aPositions[n];
2084 if ( nChar < nLastChar )
2085 {
2086 sal_uInt16 nPortionStart, nPortion;
2087 nPortion = pParaPortion->GetTextPortions().FindPortion( nChar, nPortionStart );
2088 TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
2089
2090 // Die Breite der Portion:
2091 pLastPortion->GetSize().Width() += nMore4Everyone;
2092 if ( nSomeExtraSpace )
2093 pLastPortion->GetSize().Width()++;
2094
2095 // Correct positions in array
2096 // Even for kashidas just change positions, VCL will then draw the kashida automatically
2097 sal_uInt16 nPortionEnd = nPortionStart + pLastPortion->GetLen();
2098 for ( sal_uInt16 _n = nChar; _n < nPortionEnd; _n++ )
2099 {
2100 pLine->GetCharPosArray()[_n-nFirstChar] += nMore4Everyone;
2101 if ( nSomeExtraSpace )
2102 pLine->GetCharPosArray()[_n-nFirstChar]++;
2103 }
2104
2105 if ( nSomeExtraSpace )
2106 nSomeExtraSpace--;
2107 }
2108 }
2109
2110 // Now the text width contains the extra width...
2111 pLine->SetTextWidth( pLine->GetTextWidth() + nRemainingSpace );
2112 }
2113
ImpFindKashidas(ContentNode * pNode,sal_uInt16 nStart,sal_uInt16 nEnd,SvUShorts & rArray)2114 void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, sal_uInt16 nStart, sal_uInt16 nEnd, SvUShorts& rArray )
2115 {
2116 // the search has to be performed on a per word base
2117
2118 EditSelection aWordSel( EditPaM( pNode, nStart ) );
2119 aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2120 if ( aWordSel.Min().GetIndex() < nStart )
2121 aWordSel.Min().GetIndex() = nStart;
2122
2123 while ( ( aWordSel.Min().GetNode() == pNode ) && ( aWordSel.Min().GetIndex() < nEnd ) )
2124 {
2125 sal_uInt16 nSavPos = aWordSel.Max().GetIndex();
2126 if ( aWordSel.Max().GetIndex() > nEnd )
2127 aWordSel.Max().GetIndex() = nEnd;
2128
2129 String aWord = GetSelected( aWordSel );
2130
2131 // restore selection for proper iteration at the end of the function
2132 aWordSel.Max().GetIndex() = nSavPos;
2133
2134 xub_StrLen nIdx = 0;
2135 xub_StrLen nKashidaPos = STRING_LEN;
2136 xub_Unicode cCh;
2137 xub_Unicode cPrevCh = 0;
2138
2139 while ( nIdx < aWord.Len() )
2140 {
2141 cCh = aWord.GetChar( nIdx );
2142
2143 // 1. Priority:
2144 // after user inserted kashida
2145 if ( 0x640 == cCh )
2146 {
2147 nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
2148 break;
2149 }
2150
2151 // 2. Priority:
2152 // after a Seen or Sad
2153 if ( nIdx + 1 < aWord.Len() &&
2154 ( 0x633 == cCh || 0x635 == cCh ) )
2155 {
2156 nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
2157 break;
2158 }
2159
2160 // 3. Priority:
2161 // before final form of Teh Marbuta, Hah, Dal
2162 // 4. Priority:
2163 // before final form of Alef, Lam or Kaf
2164 if ( nIdx && nIdx + 1 == aWord.Len() &&
2165 ( 0x629 == cCh || 0x62D == cCh || 0x62F == cCh ||
2166 0x627 == cCh || 0x644 == cCh || 0x643 == cCh ) )
2167 {
2168 DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2169
2170 // check if character is connectable to previous character,
2171 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2172 {
2173 nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2174 break;
2175 }
2176 }
2177
2178 // 5. Priority:
2179 // before media Bah
2180 if ( nIdx && nIdx + 1 < aWord.Len() && 0x628 == cCh )
2181 {
2182 DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2183
2184 // check if next character is Reh, Yeh or Alef Maksura
2185 xub_Unicode cNextCh = aWord.GetChar( nIdx + 1 );
2186
2187 if ( 0x631 == cNextCh || 0x64A == cNextCh ||
2188 0x649 == cNextCh )
2189 {
2190 // check if character is connectable to previous character,
2191 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2192 nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2193 }
2194 }
2195
2196 // 6. Priority:
2197 // other connecting possibilities
2198 if ( nIdx && nIdx + 1 == aWord.Len() &&
2199 0x60C <= cCh && 0x6FE >= cCh )
2200 {
2201 DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2202
2203 // check if character is connectable to previous character,
2204 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2205 {
2206 // only choose this position if we did not find
2207 // a better one:
2208 if ( STRING_LEN == nKashidaPos )
2209 nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2210 break;
2211 }
2212 }
2213
2214 // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
2215 // Damma, Kasra, Shadda and Sukun when checking if
2216 // a character can be connected to previous character.
2217 if ( cCh < 0x64B || cCh > 0x652 )
2218 cPrevCh = cCh;
2219
2220 ++nIdx;
2221 } // end of current word
2222
2223 if ( STRING_LEN != nKashidaPos )
2224 rArray.Insert( nKashidaPos, rArray.Count() );
2225
2226 aWordSel = WordRight( aWordSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2227 aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2228 }
2229 }
2230
SplitTextPortion(ParaPortion * pPortion,sal_uInt16 nPos,EditLine * pCurLine)2231 sal_uInt16 ImpEditEngine::SplitTextPortion( ParaPortion* pPortion, sal_uInt16 nPos, EditLine* pCurLine )
2232 {
2233 DBG_ASSERT( pPortion, "SplitTextPortion: Welche ?" );
2234
2235 // Die Portion bei nPos wird geplittet, wenn bei nPos nicht
2236 // sowieso ein Wechsel ist
2237 if ( nPos == 0 )
2238 return 0;
2239
2240 sal_uInt16 nSplitPortion;
2241 sal_uInt16 nTmpPos = 0;
2242 TextPortion* pTextPortion = 0;
2243 sal_uInt16 nPortions = pPortion->GetTextPortions().Count();
2244 for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
2245 {
2246 TextPortion* pTP = pPortion->GetTextPortions().GetObject(nSplitPortion);
2247 nTmpPos = nTmpPos + pTP->GetLen();
2248 if ( nTmpPos >= nPos )
2249 {
2250 if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden
2251 {
2252 // Skip Portions with ExtraSpace
2253 // while ( ( (nSplitPortion+1) < nPortions ) && (pPortion->GetTextPortions().GetObject(nSplitPortion+1)->GetKind() == PORTIONKIND_EXTRASPACE ) )
2254 // nSplitPortion++;
2255
2256 return nSplitPortion;
2257 }
2258 pTextPortion = pTP;
2259 break;
2260 }
2261 }
2262
2263 DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
2264 DBG_ASSERT( pTextPortion->GetKind() == PORTIONKIND_TEXT, "SplitTextPortion: Keine TextPortion!" );
2265
2266 sal_uInt16 nOverlapp = nTmpPos - nPos;
2267 pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
2268 TextPortion* pNewPortion = new TextPortion( nOverlapp );
2269 pPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 );
2270 // Groessen setzen:
2271 if ( pCurLine )
2272 {
2273 // Kein neues GetTextSize, sondern Werte aus Array verwenden:
2274 DBG_ASSERT( nPos > pCurLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
2275 pTextPortion->GetSize().Width() = pCurLine->GetCharPosArray()[ nPos-pCurLine->GetStart()-1 ];
2276
2277 if ( pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed )
2278 {
2279 // We need the original size from the portion
2280 sal_uInt16 nTxtPortionStart = pPortion->GetTextPortions().GetStartPos( nSplitPortion );
2281 SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
2282 SeekCursor( pPortion->GetNode(), nTxtPortionStart+1, aTmpFont );
2283 aTmpFont.SetPhysFont( GetRefDevice() );
2284 GetRefDevice()->Push( PUSH_TEXTLANGUAGE );
2285 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
2286 Size aSz = aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nTxtPortionStart, pTextPortion->GetLen(), NULL );
2287 GetRefDevice()->Pop();
2288 pTextPortion->GetExtraInfos()->nOrgWidth = aSz.Width();
2289 }
2290 }
2291 else
2292 pTextPortion->GetSize().Width() = (-1);
2293
2294 return nSplitPortion;
2295 }
2296
CreateTextPortions(ParaPortion * pParaPortion,sal_uInt16 & rStart)2297 void ImpEditEngine::CreateTextPortions( ParaPortion* pParaPortion, sal_uInt16& rStart /* , sal_Bool bCreateBlockPortions */ )
2298 {
2299 sal_uInt16 nStartPos = rStart;
2300 ContentNode* pNode = pParaPortion->GetNode();
2301 DBG_ASSERT( pNode->Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
2302
2303 SortedPositions aPositions;
2304 aPositions.Insert( (sal_uInt32) 0 );
2305
2306 sal_uInt16 nAttr = 0;
2307 EditCharAttrib* pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
2308 while ( pAttrib )
2309 {
2310 // Start und Ende in das Array eintragen...
2311 // Die InsertMethode laesst keine doppelten Werte zu....
2312 aPositions.Insert( pAttrib->GetStart() );
2313 aPositions.Insert( pAttrib->GetEnd() );
2314 nAttr++;
2315 pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
2316 }
2317 aPositions.Insert( pNode->Len() );
2318
2319 if ( pParaPortion->aScriptInfos.empty() )
2320 ((ImpEditEngine*)this)->InitScriptTypes( GetParaPortions().GetPos( pParaPortion ) );
2321
2322 const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
2323 for ( size_t nT = 0; nT < rTypes.size(); nT++ )
2324 aPositions.Insert( rTypes[nT].nStartPos );
2325
2326 const WritingDirectionInfos& rWritingDirections = pParaPortion->aWritingDirectionInfos;
2327 for ( size_t nD = 0; nD < rWritingDirections.size(); nD++ )
2328 aPositions.Insert( rWritingDirections[nD].nStartPos );
2329
2330 if ( mpIMEInfos && mpIMEInfos->nLen && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) )
2331 {
2332 sal_uInt16 nLastAttr = 0xFFFF;
2333 for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
2334 {
2335 if ( mpIMEInfos->pAttribs[n] != nLastAttr )
2336 {
2337 aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n );
2338 nLastAttr = mpIMEInfos->pAttribs[n];
2339 }
2340 }
2341 aPositions.Insert( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen );
2342 }
2343
2344 // Ab ... loeschen:
2345 // Leider muss die Anzahl der TextPortions mit aPositions.Count()
2346 // nicht uebereinstimmen, da evtl. Zeilenumbrueche...
2347 sal_uInt16 nPortionStart = 0;
2348 sal_uInt16 nInvPortion = 0;
2349 sal_uInt16 nP;
2350 for ( nP = 0; nP < pParaPortion->GetTextPortions().Count(); nP++ )
2351 {
2352 TextPortion* pTmpPortion = pParaPortion->GetTextPortions().GetObject(nP);
2353 nPortionStart = nPortionStart + pTmpPortion->GetLen();
2354 if ( nPortionStart >= nStartPos )
2355 {
2356 nPortionStart = nPortionStart - pTmpPortion->GetLen();
2357 rStart = nPortionStart;
2358 nInvPortion = nP;
2359 break;
2360 }
2361 }
2362 DBG_ASSERT( nP < pParaPortion->GetTextPortions().Count() || !pParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" );
2363 if ( nInvPortion && ( nPortionStart+pParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) )
2364 {
2365 // lieber eine davor...
2366 // Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
2367 // die einzige in der Zeile davor !
2368 nInvPortion--;
2369 nPortionStart = nPortionStart - pParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen();
2370 }
2371 pParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
2372
2373 // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
2374 aPositions.Insert( nPortionStart );
2375
2376 sal_uInt16 nInvPos;
2377 #ifdef DBG_UTIL
2378 sal_Bool bFound =
2379 #endif
2380 aPositions.Seek_Entry( nPortionStart, &nInvPos );
2381
2382 DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" );
2383 for ( sal_uInt16 i = nInvPos+1; i < aPositions.Count(); i++ )
2384 {
2385 TextPortion* pNew = new TextPortion( (sal_uInt16)aPositions[i] - (sal_uInt16)aPositions[i-1] );
2386 pParaPortion->GetTextPortions().Insert( pNew, pParaPortion->GetTextPortions().Count());
2387 }
2388
2389 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine Portions?!" );
2390 #ifdef EDITDEBUG
2391 DBG_ASSERT( pParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
2392 #endif
2393 }
2394
RecalcTextPortion(ParaPortion * pParaPortion,sal_uInt16 nStartPos,short nNewChars)2395 void ImpEditEngine::RecalcTextPortion( ParaPortion* pParaPortion, sal_uInt16 nStartPos, short nNewChars )
2396 {
2397 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine Portions!" );
2398 DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" );
2399
2400 ContentNode* const pNode = pParaPortion->GetNode();
2401 if ( nNewChars > 0 )
2402 {
2403 // Wenn an nStartPos ein Attribut beginnt/endet, faengt eine neue Portion
2404 // an, ansonsten wird die Portion an nStartPos erweitert.
2405
2406 if ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) || IsScriptChange( EditPaM( pNode, nStartPos ) ) )
2407 {
2408 sal_uInt16 nNewPortionPos = 0;
2409 if ( nStartPos )
2410 nNewPortionPos = SplitTextPortion( pParaPortion, nStartPos ) + 1;
2411
2412 // Eine leere Portion kann hier stehen, wenn der Absatz leer war,
2413 // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
2414 if ( ( nNewPortionPos < pParaPortion->GetTextPortions().Count() ) &&
2415 !pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
2416 {
2417 DBG_ASSERT( pParaPortion->GetTextPortions()[nNewPortionPos]->GetKind() == PORTIONKIND_TEXT, "Leere Portion war keine TextPortion!" );
2418 sal_uInt16 & r =
2419 pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
2420 r = r + nNewChars;
2421 }
2422 else
2423 {
2424 TextPortion* pNewPortion = new TextPortion( nNewChars );
2425 pParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos );
2426 }
2427 }
2428 else
2429 {
2430 sal_uInt16 nPortionStart;
2431 const sal_uInt16 nTP = pParaPortion->GetTextPortions().
2432 FindPortion( nStartPos, nPortionStart );
2433 TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
2434 DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
2435 pTP->GetLen() = pTP->GetLen() + nNewChars;
2436 pTP->GetSize().Width() = (-1);
2437 }
2438 }
2439 else
2440 {
2441 // Portion schrumpfen oder ggf. entfernen.
2442 // Vor Aufruf dieser Methode muss sichergestellt sein, dass
2443 // keine Portions in dem geloeschten Bereich lagen!
2444
2445 // Es darf keine reinragende oder im Bereich startende Portion geben,
2446 // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
2447 sal_uInt16 nPortion = 0;
2448 sal_uInt16 nPos = 0;
2449 sal_uInt16 nEnd = nStartPos-nNewChars;
2450 sal_uInt16 nPortions = pParaPortion->GetTextPortions().Count();
2451 TextPortion* pTP = 0;
2452 for ( nPortion = 0; nPortion < nPortions; nPortion++ )
2453 {
2454 pTP = pParaPortion->GetTextPortions()[ nPortion ];
2455 if ( ( nPos+pTP->GetLen() ) > nStartPos )
2456 {
2457 DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
2458 DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
2459 break;
2460 }
2461 nPos = nPos + pTP->GetLen();
2462 }
2463 DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
2464 if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
2465 {
2466 // Portion entfernen;
2467 sal_uInt8 nType = pTP->GetKind();
2468 pParaPortion->GetTextPortions().Remove( nPortion );
2469 delete pTP;
2470 if ( nType == PORTIONKIND_LINEBREAK )
2471 {
2472 TextPortion* pNext = pParaPortion->GetTextPortions()[ nPortion ];
2473 if ( pNext && !pNext->GetLen() )
2474 {
2475 // Dummy-Portion entfernen
2476 pParaPortion->GetTextPortions().Remove( nPortion );
2477 delete pNext;
2478 }
2479 }
2480 }
2481 else
2482 {
2483 DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
2484 pTP->GetLen() = pTP->GetLen() + nNewChars;
2485 }
2486
2487 // ganz am Schluss darf keine HYPHENATOR-Portion stehen bleiben...
2488 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" );
2489 sal_uInt16 nLastPortion = pParaPortion->GetTextPortions().Count() - 1;
2490 pTP = pParaPortion->GetTextPortions().GetObject( nLastPortion );
2491 if ( pTP->GetKind() == PORTIONKIND_HYPHENATOR )
2492 {
2493 // Portion wegschmeissen, ggf. die davor korrigieren, wenn
2494 // die Hyph-Portion ein Zeichen geschluckt hat...
2495 pParaPortion->GetTextPortions().Remove( nLastPortion );
2496 if ( nLastPortion && pTP->GetLen() )
2497 {
2498 TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nLastPortion - 1 );
2499 DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
2500 pPrev->SetLen( pPrev->GetLen() + pTP->GetLen() );
2501 pPrev->GetSize().Width() = (-1);
2502 }
2503 delete pTP;
2504 }
2505 }
2506 #ifdef EDITDEBUG
2507 DBG_ASSERT( pParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
2508 #endif
2509 }
2510
SetTextRanger(TextRanger * pRanger)2511 void ImpEditEngine::SetTextRanger( TextRanger* pRanger )
2512 {
2513 if ( pTextRanger != pRanger )
2514 {
2515 delete pTextRanger;
2516 pTextRanger = pRanger;
2517
2518 for ( sal_uInt32 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
2519 {
2520 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
2521 pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
2522 pParaPortion->GetLines().Reset();
2523 }
2524
2525 FormatFullDoc();
2526 UpdateViews( GetActiveView() );
2527 if ( GetUpdateMode() && GetActiveView() )
2528 pActiveView->ShowCursor( sal_False, sal_False );
2529 }
2530 }
2531
SetVertical(sal_Bool bVertical)2532 void ImpEditEngine::SetVertical( sal_Bool bVertical )
2533 {
2534 if ( IsVertical() != bVertical )
2535 {
2536 GetEditDoc().SetVertical( bVertical );
2537 sal_Bool bUseCharAttribs = ( aStatus.GetControlWord() & EE_CNTRL_USECHARATTRIBS ) ? sal_True : sal_False;
2538 GetEditDoc().CreateDefFont( bUseCharAttribs );
2539 if ( IsFormatted() )
2540 {
2541 FormatFullDoc();
2542 UpdateViews( GetActiveView() );
2543 }
2544 }
2545 }
2546
SetFixedCellHeight(sal_Bool bUseFixedCellHeight)2547 void ImpEditEngine::SetFixedCellHeight( sal_Bool bUseFixedCellHeight )
2548 {
2549 if ( IsFixedCellHeight() != bUseFixedCellHeight )
2550 {
2551 GetEditDoc().SetFixedCellHeight( bUseFixedCellHeight );
2552 if ( IsFormatted() )
2553 {
2554 FormatFullDoc();
2555 UpdateViews( GetActiveView() );
2556 }
2557 }
2558 }
2559
SeekCursor(ContentNode * pNode,sal_uInt16 nPos,SvxFont & rFont,OutputDevice * pOut,sal_uInt16 nIgnoreWhich)2560 void ImpEditEngine::SeekCursor( ContentNode* pNode, sal_uInt16 nPos, SvxFont& rFont, OutputDevice* pOut, sal_uInt16 nIgnoreWhich )
2561 {
2562 // Es war mal geplant, SeekCursor( nStartPos, nEndPos, ... ), damit nur
2563 // ab der StartPosition neu gesucht wird.
2564 // Problem: Es mussten zwei Listen beruecksichtigt/gefuehrt werden:
2565 // OrderedByStart,OrderedByEnd.
2566
2567 if ( nPos > pNode->Len() )
2568 nPos = pNode->Len();
2569
2570 rFont = pNode->GetCharAttribs().GetDefFont();
2571
2572 short nScriptType = GetScriptType( EditPaM( pNode, nPos ) );
2573 if ( ( nScriptType == i18n::ScriptType::ASIAN ) || ( nScriptType == i18n::ScriptType::COMPLEX ) )
2574 {
2575 const SvxFontItem& rFontItem = (const SvxFontItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) );
2576 rFont.SetName( rFontItem.GetFamilyName() );
2577 rFont.SetFamily( rFontItem.GetFamily() );
2578 rFont.SetPitch( rFontItem.GetPitch() );
2579 rFont.SetCharSet( rFontItem.GetCharSet() );
2580 Size aSz( rFont.GetSize() );
2581 aSz.Height() = ((const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) ).GetHeight();
2582 rFont.SetSize( aSz );
2583 rFont.SetWeight( ((const SvxWeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ))).GetWeight() );
2584 rFont.SetItalic( ((const SvxPostureItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ))).GetPosture() );
2585 rFont.SetLanguage( ((const SvxLanguageItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ))).GetLanguage() );
2586 }
2587
2588 sal_uInt16 nRelWidth = ((const SvxCharScaleWidthItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH)).GetValue();
2589
2590 if ( pOut )
2591 {
2592 const SvxUnderlineItem& rTextLineColor = (const SvxUnderlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_UNDERLINE );
2593 if ( rTextLineColor.GetColor() != COL_TRANSPARENT )
2594 pOut->SetTextLineColor( rTextLineColor.GetColor() );
2595 else
2596 pOut->SetTextLineColor();
2597 }
2598
2599 if ( pOut )
2600 {
2601 const SvxOverlineItem& rOverlineColor = (const SvxOverlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_OVERLINE );
2602 if ( rOverlineColor.GetColor() != COL_TRANSPARENT )
2603 pOut->SetOverlineColor( rOverlineColor.GetColor() );
2604 else
2605 pOut->SetOverlineColor();
2606 }
2607
2608 const SvxLanguageItem* pCJKLanguageItem = NULL;
2609
2610 if ( aStatus.UseCharAttribs() )
2611 {
2612 const CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
2613 sal_uInt16 nAttr = 0;
2614 EditCharAttrib* pAttrib = GetAttrib( rAttribs, nAttr );
2615 while ( pAttrib && ( pAttrib->GetStart() <= nPos ) )
2616 {
2617 // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
2618 // Leere Attribute werden beruecksichtigt( verwendet), da diese
2619 // gerade eingestellt wurden.
2620 // 12.4.95: Doch keine Leeren Attribute verwenden:
2621 // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
2622 // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
2623 if ( ( pAttrib->Which() != nIgnoreWhich ) &&
2624 ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
2625 || ( !pNode->Len() ) ) )
2626 {
2627 DBG_ASSERT( ( pAttrib->Which() >= EE_CHAR_START ) && ( pAttrib->Which() <= EE_FEATURE_END ), "Unglueltiges Attribut in Seek() " );
2628 if ( IsScriptItemValid( pAttrib->Which(), nScriptType ) )
2629 {
2630 pAttrib->SetFont( rFont, pOut );
2631 // #i1550# hard color attrib should win over text color from field
2632 if ( pAttrib->Which() == EE_FEATURE_FIELD )
2633 {
2634 EditCharAttrib* pColorAttr = pNode->GetCharAttribs().FindAttrib( EE_CHAR_COLOR, nPos );
2635 if ( pColorAttr )
2636 pColorAttr->SetFont( rFont, pOut );
2637 }
2638 }
2639 if ( pAttrib->Which() == EE_CHAR_FONTWIDTH )
2640 nRelWidth = ((const SvxCharScaleWidthItem*)pAttrib->GetItem())->GetValue();
2641 if ( pAttrib->Which() == EE_CHAR_LANGUAGE_CJK )
2642 pCJKLanguageItem = (const SvxLanguageItem*) pAttrib->GetItem();
2643 }
2644 pAttrib = GetAttrib( rAttribs, ++nAttr );
2645 }
2646 }
2647
2648 if ( !pCJKLanguageItem )
2649 pCJKLanguageItem = (const SvxLanguageItem*) &pNode->GetContentAttribs().GetItem( EE_CHAR_LANGUAGE_CJK );
2650
2651 rFont.SetCJKContextLanguage( pCJKLanguageItem->GetLanguage() );
2652
2653 if ( rFont.GetKerning() && IsKernAsianPunctuation() && ( nScriptType == i18n::ScriptType::ASIAN ) )
2654 rFont.SetKerning( rFont.GetKerning() | KERNING_ASIAN );
2655
2656 if ( aStatus.DoNotUseColors() )
2657 {
2658 // Hack fuer DL,weil JOE staendig die Pooldefaults verbiegt!
2659 // const SvxColorItem& rColorItem = (const SvxColorItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_COLOR );
2660 rFont.SetColor( /* rColorItem.GetValue() */ COL_BLACK );
2661 }
2662
2663 if ( aStatus.DoStretch() || ( nRelWidth != 100 ) )
2664 {
2665 // Fuer das aktuelle Ausgabegeraet, weil es sonst bei einem
2666 // Drucker als RefDev auf dem Bildschirm #?!@' aussieht!
2667 OutputDevice* pDev = pOut ? pOut : GetRefDevice();
2668 rFont.SetPhysFont( pDev );
2669 FontMetric aMetric( pDev->GetFontMetric() );
2670 // Fuer die Hoehe nicht die Metriken nehmen, da das bei
2671 // Hoch-/Tiefgestellt schief geht.
2672 Size aRealSz( aMetric.GetSize().Width(), rFont.GetSize().Height() );
2673 if ( aStatus.DoStretch() )
2674 {
2675 if ( nStretchY != 100 )
2676 {
2677 aRealSz.Height() *= nStretchY;
2678 aRealSz.Height() /= 100;
2679 }
2680 if ( nStretchX != 100 )
2681 {
2682 aRealSz.Width() *= nStretchX;
2683 aRealSz.Width() /= 100;
2684
2685 // Auch das Kerning: (long wegen Zwischenergebnis)
2686 long nKerning = rFont.GetFixKerning();
2687 /*
2688 Die Ueberlegung war: Wenn neg. Kerning, aber StretchX = 200
2689 => Nicht das Kerning verdoppelt, also die Buchstaben weiter
2690 zusammenziehen
2691 ---------------------------
2692 Kern StretchX =>Kern
2693 ---------------------------
2694 >0 <100 < (Proportional)
2695 <0 <100 < (Proportional)
2696 >0 >100 > (Proportional)
2697 <0 >100 < (Der Betrag, also Antiprop)
2698 */
2699 if ( ( nKerning < 0 ) && ( nStretchX > 100 ) )
2700 {
2701 // Antiproportional
2702 nKerning *= 100;
2703 nKerning /= nStretchX;
2704 }
2705 else if ( nKerning )
2706 {
2707 // Proportional
2708 nKerning *= nStretchX;
2709 nKerning /= 100;
2710 }
2711 rFont.SetFixKerning( (short)nKerning );
2712 }
2713 }
2714 if ( nRelWidth != 100 )
2715 {
2716 aRealSz.Width() *= nRelWidth;
2717 aRealSz.Width() /= 100;
2718 }
2719 rFont.SetSize( aRealSz );
2720 // Font wird nicht restauriert...
2721 }
2722
2723 if ( ( ( rFont.GetColor() == COL_AUTO ) || ( IsForceAutoColor() ) ) && pOut )
2724 {
2725 // #i75566# Do not use AutoColor when printing OR Pdf export
2726 const bool bPrinting(OUTDEV_PRINTER == pOut->GetOutDevType());
2727 const bool bPDFExporting(0 != pOut->GetPDFWriter());
2728
2729 if ( IsAutoColorEnabled() && !bPrinting && !bPDFExporting)
2730 {
2731 // Never use WindowTextColor on the printer
2732 rFont.SetColor( GetAutoColor() );
2733 }
2734 else
2735 {
2736 if ( ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() )
2737 rFont.SetColor( COL_WHITE );
2738 else
2739 rFont.SetColor( COL_BLACK );
2740 }
2741 }
2742
2743 if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) &&
2744 ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
2745 {
2746 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
2747 if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
2748 rFont.SetUnderline( UNDERLINE_SINGLE );
2749 else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
2750 rFont.SetUnderline( UNDERLINE_BOLD );
2751 else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
2752 rFont.SetUnderline( UNDERLINE_DOTTED );
2753 else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
2754 rFont.SetUnderline( UNDERLINE_DOTTED );
2755 else if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
2756 rFont.SetColor( Color( COL_RED ) );
2757 else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
2758 rFont.SetColor( Color( COL_LIGHTGRAY ) );
2759 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
2760 {
2761 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2762 rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
2763 rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
2764 rFont.SetTransparent( sal_False );
2765 }
2766 else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
2767 {
2768 rFont.SetUnderline( UNDERLINE_WAVE );
2769 if( pOut )
2770 pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
2771 }
2772 }
2773 }
2774
RecalcFormatterFontMetrics(FormatterFontMetric & rCurMetrics,SvxFont & rFont)2775 void ImpEditEngine::RecalcFormatterFontMetrics( FormatterFontMetric& rCurMetrics, SvxFont& rFont )
2776 {
2777 // Fuer Zeilenhoehe bei Hoch/Tief erstmal ohne Propr!
2778 sal_uInt16 nPropr = rFont.GetPropr();
2779 DBG_ASSERT( ( nPropr == 100 ) || rFont.GetEscapement(), "Propr ohne Escape?!" );
2780 if ( nPropr != 100 )
2781 {
2782 rFont.SetPropr( 100 );
2783 rFont.SetPhysFont( pRefDev );
2784 }
2785 sal_uInt16 nAscent, nDescent;
2786
2787 FontMetric aMetric( pRefDev->GetFontMetric() );
2788 nAscent = (sal_uInt16)aMetric.GetAscent();
2789 if ( IsAddExtLeading() )
2790 nAscent = sal::static_int_cast< sal_uInt16 >(
2791 nAscent + aMetric.GetExtLeading() );
2792 nDescent = (sal_uInt16)aMetric.GetDescent();
2793
2794 if ( IsFixedCellHeight() )
2795 {
2796 /* creating correct proportional ascent and descent values lead to problems if different fonts are used
2797 in the same portion, it results in a bigger linespacing.
2798 sal_Int32 f = nAscent + nDescent;
2799 if ( f )
2800 {
2801 sal_Int32 nHeight = ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() );
2802 nAscent = (sal_Int16)(( nHeight * nAscent ) / f );
2803 nDescent = (sal_Int16)(nHeight - nAscent);
2804 }
2805 */
2806 nAscent = sal::static_int_cast< sal_uInt16 >( rFont.GetHeight() );
2807 nDescent= sal::static_int_cast< sal_uInt16 >( ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() ) - nAscent );
2808 }
2809 else
2810 {
2811 sal_uInt16 nIntLeading = ( aMetric.GetIntLeading() > 0 ) ? (sal_uInt16)aMetric.GetIntLeading() : 0;
2812 // Fonts ohne Leading bereiten Probleme
2813 if ( ( nIntLeading == 0 ) && ( pRefDev->GetOutDevType() == OUTDEV_PRINTER ) )
2814 {
2815 // Da schaun wir mal, was fuer eine Leading ich auf dem
2816 // Bildschirm erhalte
2817 VirtualDevice* pVDev = GetVirtualDevice( pRefDev->GetMapMode(), pRefDev->GetDrawMode() );
2818 rFont.SetPhysFont( pVDev );
2819 aMetric = pVDev->GetFontMetric();
2820
2821 // Damit sich die Leading nicht wieder rausrechnet,
2822 // wenn die ganze Zeile den Font hat, nTmpLeading.
2823
2824 // 4/96: Kommt bei HP Laserjet 4V auch nicht hin
2825 // => Werte komplett vom Bildschirm holen.
2826 // sal_uInt16 nTmpLeading = (sal_uInt16)aMetric.GetLeading();
2827 // nAscent += nTmpLeading;
2828 nAscent = (sal_uInt16)aMetric.GetAscent();
2829 nDescent = (sal_uInt16)aMetric.GetDescent();
2830 // nLeading = (sal_uInt16)aMetric.GetLeading();
2831 }
2832 }
2833 if ( nAscent > rCurMetrics.nMaxAscent )
2834 rCurMetrics.nMaxAscent = nAscent;
2835 if ( nDescent > rCurMetrics.nMaxDescent )
2836 rCurMetrics.nMaxDescent= nDescent;
2837 // Sonderbehandlung Hoch/Tief:
2838 if ( rFont.GetEscapement() )
2839 {
2840 // Jetzt unter Beruecksichtigung von Escape/Propr
2841 // Ascent oder Descent ggf vergroessern
2842 short nDiff = (short)(rFont.GetSize().Height()*rFont.GetEscapement()/100L);
2843 if ( rFont.GetEscapement() > 0 )
2844 {
2845 nAscent = (sal_uInt16) (((long)nAscent)*nPropr/100 + nDiff);
2846 if ( nAscent > rCurMetrics.nMaxAscent )
2847 rCurMetrics.nMaxAscent = nAscent;
2848 }
2849 else // muss < 0 sein
2850 {
2851 nDescent = (sal_uInt16) (((long)nDescent)*nPropr/100 - nDiff);
2852 if ( nDescent > rCurMetrics.nMaxDescent )
2853 rCurMetrics.nMaxDescent= nDescent;
2854 }
2855 }
2856 }
2857
Paint(OutputDevice * pOutDev,Rectangle aClipRec,Point aStartPos,sal_Bool bStripOnly,short nOrientation)2858 void ImpEditEngine::Paint( OutputDevice* pOutDev, Rectangle aClipRec, Point aStartPos, sal_Bool bStripOnly, short nOrientation )
2859 {
2860 if ( !GetUpdateMode() && !bStripOnly )
2861 return;
2862
2863 if ( !IsFormatted() )
2864 FormatDoc();
2865
2866 long nFirstVisXPos = - pOutDev->GetMapMode().GetOrigin().X();
2867 long nFirstVisYPos = - pOutDev->GetMapMode().GetOrigin().Y();
2868
2869 EditLine* pLine;
2870 Point aTmpPos;
2871 Point aRedLineTmpPos;
2872 DBG_ASSERT( GetParaPortions().Count(), "Keine ParaPortion?!" );
2873 SvxFont aTmpFont( GetParaPortions()[0]->GetNode()->GetCharAttribs().GetDefFont() );
2874 Font aOldFont( pOutDev->GetFont() );
2875 vcl::PDFExtOutDevData* pPDFExtOutDevData = PTR_CAST( vcl::PDFExtOutDevData, pOutDev->GetExtOutDevData() );
2876
2877 // Bei gedrehtem Text wird aStartPos als TopLeft angesehen, da andere
2878 // Informationen fehlen, und sowieso das ganze Object ungescrollt
2879 // dargestellt wird.
2880 // Das Rechteck ist unendlich gross.
2881 Point aOrigin( aStartPos );
2882 double nCos = 0.0, nSin = 0.0;
2883 if ( nOrientation )
2884 {
2885 double nRealOrientation = nOrientation*F_PI1800;
2886 nCos = cos( nRealOrientation );
2887 nSin = sin( nRealOrientation );
2888 }
2889
2890 // #110496# Added some more optional metafile comments. This
2891 // change: factored out some duplicated code.
2892 GDIMetaFile* pMtf = pOutDev->GetConnectMetaFile();
2893 const bool bMetafileValid( pMtf != NULL );
2894
2895 // Fuer OnlineSpelling:
2896 // EditPaM aCursorPos;
2897 // if( GetStatus().DoOnlineSpelling() && pActiveView )
2898 // aCurPos = pActiveView->pImpEditView->GetEditSelections().Max();
2899
2900 // --------------------------------------------------
2901 // Ueber alle Absaetze...
2902 // --------------------------------------------------
2903 for ( sal_uInt32 n = 0; n < GetParaPortions().Count(); n++ )
2904 {
2905 ParaPortion* pPortion = GetParaPortions().GetObject( n );
2906 DBG_ASSERT( pPortion, "NULL-Pointer in TokenList in Paint" );
2907 // falls beim Tippen Idle-Formatierung, asynchrones Paint.
2908 // Unsichtbare Portions koennen ungueltig sein.
2909 if ( pPortion->IsVisible() && pPortion->IsInvalid() )
2910 return;
2911
2912 if ( pPDFExtOutDevData )
2913 pPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
2914
2915 long nParaHeight = pPortion->GetHeight();
2916 sal_uInt16 nIndex = 0;
2917 if ( pPortion->IsVisible() && (
2918 ( !IsVertical() && ( ( aStartPos.Y() + nParaHeight ) > aClipRec.Top() ) ) ||
2919 ( IsVertical() && ( ( aStartPos.X() - nParaHeight ) < aClipRec.Right() ) ) ) )
2920
2921 {
2922 // --------------------------------------------------
2923 // Ueber die Zeilen des Absatzes...
2924 // --------------------------------------------------
2925 sal_uInt16 nLines = pPortion->GetLines().Count();
2926 sal_uInt16 nLastLine = nLines-1;
2927
2928 // #108052#
2929 bool bEndOfParagraphWritten(false);
2930
2931 if ( !IsVertical() )
2932 aStartPos.Y() += pPortion->GetFirstLineOffset();
2933 else
2934 aStartPos.X() -= pPortion->GetFirstLineOffset();
2935
2936 Point aParaStart( aStartPos );
2937
2938 const SvxLineSpacingItem& rLSItem = ((const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ));
2939 sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
2940 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
2941 bool bPaintBullet (false);
2942
2943 for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
2944 {
2945 pLine = pPortion->GetLines().GetObject(nLine);
2946 DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in UpdateViews" );
2947 aTmpPos = aStartPos;
2948 if ( !IsVertical() )
2949 {
2950 aTmpPos.X() += pLine->GetStartPosX();
2951 aTmpPos.Y() += pLine->GetMaxAscent();
2952 aStartPos.Y() += pLine->GetHeight();
2953 }
2954 else
2955 {
2956 aTmpPos.Y() += pLine->GetStartPosX();
2957 aTmpPos.X() -= pLine->GetMaxAscent();
2958 aStartPos.X() -= pLine->GetHeight();
2959 }
2960
2961 if ( ( !IsVertical() && ( aStartPos.Y() > aClipRec.Top() ) )
2962 || ( IsVertical() && aStartPos.X() < aClipRec.Right() ) )
2963 {
2964 bPaintBullet = false;
2965
2966 // Why not just also call when stripping portions? This will give the correct values
2967 // and needs no position corrections in OutlinerEditEng::DrawingText which tries to call
2968 // PaintBullet correctly; exactly what GetEditEnginePtr()->PaintingFirstLine
2969 // does, too. No change for not-layouting (painting).
2970 if(0 == nLine) // && !bStripOnly)
2971 {
2972 // VERT???
2973 GetEditEnginePtr()->PaintingFirstLine( n, aParaStart, aTmpPos.Y(), aOrigin, nOrientation, pOutDev );
2974
2975 // Remember whether a bullet was painted.
2976 const SfxBoolItem& rBulletState = static_cast<const SfxBoolItem&>(
2977 pEditEngine->GetParaAttrib(n, EE_PARA_BULLETSTATE));
2978 bPaintBullet = rBulletState.GetValue() ? true : false;
2979 }
2980
2981 // --------------------------------------------------
2982 // Ueber die Portions der Zeile...
2983 // --------------------------------------------------
2984 nIndex = pLine->GetStart();
2985
2986 for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
2987 {
2988 DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" );
2989 TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y );
2990 DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
2991
2992 long nPortionXOffset = GetPortionXOffset( pPortion, pLine, y );
2993 if ( !IsVertical() )
2994 {
2995 aTmpPos.X() = aStartPos.X() + nPortionXOffset;
2996 if ( aTmpPos.X() > aClipRec.Right() )
2997 break; // Keine weitere Ausgabe in Zeile noetig
2998 }
2999 else
3000 {
3001 aTmpPos.Y() = aStartPos.Y() + nPortionXOffset;
3002 if ( aTmpPos.Y() > aClipRec.Bottom() )
3003 break; // Keine weitere Ausgabe in Zeile noetig
3004 }
3005
3006 // R2L replaces with obove...
3007 // New position after processing R2L text...
3008 // R2L if ( nR2LWidth && !pTextPortion->GetRightToLeft() )
3009 // R2L {
3010 // R2L if ( !IsVertical() )
3011 // R2L aTmpPos.X() += nR2LWidth;
3012 // R2L else
3013 // R2L aTmpPos.Y() += nR2LWidth;
3014 // R2L
3015 // R2L nR2LWidth = 0;
3016 // R2L }
3017
3018 switch ( pTextPortion->GetKind() )
3019 {
3020 case PORTIONKIND_TEXT:
3021 case PORTIONKIND_FIELD:
3022 case PORTIONKIND_HYPHENATOR:
3023 {
3024 SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
3025
3026 sal_Bool bDrawFrame = sal_False;
3027
3028 if ( ( pTextPortion->GetKind() == PORTIONKIND_FIELD ) && !aTmpFont.IsTransparent() &&
3029 ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() &&
3030 ( IsAutoColorEnabled() && ( pOutDev->GetOutDevType() != OUTDEV_PRINTER ) ) )
3031 {
3032 aTmpFont.SetTransparent( sal_True );
3033 pOutDev->SetFillColor();
3034 pOutDev->SetLineColor( GetAutoColor() );
3035 bDrawFrame = sal_True;
3036 }
3037
3038 #ifdef EDITDEBUG
3039 if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
3040 {
3041 aTmpFont.SetFillColor( COL_LIGHTGRAY );
3042 aTmpFont.SetTransparent( sal_False );
3043 }
3044 if ( pTextPortion->GetRightToLeft() )
3045 {
3046 aTmpFont.SetFillColor( COL_LIGHTGRAY );
3047 aTmpFont.SetTransparent( sal_False );
3048 }
3049 else if ( GetScriptType( EditPaM( pPortion->GetNode(), nIndex+1 ) ) == i18n::ScriptType::COMPLEX )
3050 {
3051 aTmpFont.SetFillColor( COL_LIGHTCYAN );
3052 aTmpFont.SetTransparent( sal_False );
3053 }
3054 #endif
3055 aTmpFont.SetPhysFont( pOutDev );
3056
3057 // #114278# Saving both layout mode and language (since I'm
3058 // potentially changing both)
3059 pOutDev->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
3060 ImplInitLayoutMode( pOutDev, n, nIndex );
3061 ImplInitDigitMode( pOutDev, 0, 0, 0, aTmpFont.GetLanguage() );
3062
3063 XubString aText;
3064 sal_uInt16 nTextStart = 0;
3065 sal_uInt16 nTextLen = 0;
3066 const sal_Int32* pDXArray = 0;
3067 sal_Int32* pTmpDXArray = 0;
3068
3069 if ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
3070 {
3071 aText = *pPortion->GetNode();
3072 nTextStart = nIndex;
3073 nTextLen = pTextPortion->GetLen();
3074 pDXArray = pLine->GetCharPosArray().GetData()+( nIndex-pLine->GetStart() );
3075
3076 // --> FME 2005-10-18 #i55716# Paint control characters
3077 if ( aStatus.MarkFields() )
3078 {
3079 xub_StrLen nTmpIdx;
3080 const xub_StrLen nTmpEnd = nTextStart + pTextPortion->GetLen();
3081
3082 for ( nTmpIdx = nTextStart; nTmpIdx <= nTmpEnd ; ++nTmpIdx )
3083 {
3084 const sal_Unicode cChar = ( nTmpIdx != aText.Len() && ( nTmpIdx != nTextStart || 0 == nTextStart ) ) ?
3085 aText.GetChar( nTmpIdx ) :
3086 0;
3087
3088 if ( 0x200B == cChar || 0x2060 == cChar )
3089 {
3090 const String aBlank( ' ' );
3091 long nHalfBlankWidth = aTmpFont.QuickGetTextSize( pOutDev, aBlank, 0, 1, 0 ).Width() / 2;
3092
3093 const long nAdvanceX = ( nTmpIdx == nTmpEnd ?
3094 pTextPortion->GetSize().Width() :
3095 pDXArray[ nTmpIdx - nTextStart ] ) - nHalfBlankWidth;
3096 const long nAdvanceY = -pLine->GetMaxAscent();
3097
3098 Point aTopLeftRectPos( aTmpPos );
3099 if ( !IsVertical() )
3100 {
3101 aTopLeftRectPos.X() += nAdvanceX;
3102 aTopLeftRectPos.Y() += nAdvanceY;
3103 }
3104 else
3105 {
3106 aTopLeftRectPos.Y() += nAdvanceX;
3107 aTopLeftRectPos.X() -= nAdvanceY;
3108 }
3109
3110 Point aBottomRightRectPos( aTopLeftRectPos );
3111 if ( !IsVertical() )
3112 {
3113 aBottomRightRectPos.X() += 2 * nHalfBlankWidth;
3114 aBottomRightRectPos.Y() += pLine->GetHeight();
3115 }
3116 else
3117 {
3118 aBottomRightRectPos.X() -= pLine->GetHeight();
3119 aBottomRightRectPos.Y() += 2 * nHalfBlankWidth;
3120 }
3121
3122 pOutDev->Push( PUSH_FILLCOLOR );
3123 pOutDev->Push( PUSH_LINECOLOR );
3124 pOutDev->SetFillColor( COL_LIGHTGRAY );
3125 pOutDev->SetLineColor( COL_LIGHTGRAY );
3126
3127 const Rectangle aBackRect( aTopLeftRectPos, aBottomRightRectPos );
3128 pOutDev->DrawRect( aBackRect );
3129
3130 pOutDev->Pop();
3131 pOutDev->Pop();
3132
3133 if ( 0x200B == cChar )
3134 {
3135 const String aSlash( '/' );
3136 const short nOldEscapement = aTmpFont.GetEscapement();
3137 const sal_uInt8 nOldPropr = aTmpFont.GetPropr();
3138
3139 aTmpFont.SetEscapement( -20 );
3140 aTmpFont.SetPropr( 25 );
3141 aTmpFont.SetPhysFont( pOutDev );
3142
3143 const Size aSlashSize = aTmpFont.QuickGetTextSize( pOutDev, aSlash, 0, 1, 0 );
3144 Point aSlashPos( aTmpPos );
3145 const long nAddX = nHalfBlankWidth - aSlashSize.Width() / 2;
3146 if ( !IsVertical() )
3147 {
3148 aSlashPos.X() = aTopLeftRectPos.X() + nAddX;
3149 }
3150 else
3151 {
3152 aSlashPos.Y() = aTopLeftRectPos.Y() + nAddX;
3153 }
3154
3155 aTmpFont.QuickDrawText( pOutDev, aSlashPos, aSlash, 0, 1, 0 );
3156
3157 aTmpFont.SetEscapement( nOldEscapement );
3158 aTmpFont.SetPropr( nOldPropr );
3159 aTmpFont.SetPhysFont( pOutDev );
3160 }
3161 }
3162 }
3163 }
3164 // <--
3165 }
3166 else if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3167 {
3168 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
3169 DBG_ASSERT( pAttr, "Feld nicht gefunden" );
3170 DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Feld vom falschen Typ!" );
3171 aText = ((EditCharAttribField*)pAttr)->GetFieldValue();
3172 nTextStart = 0;
3173 nTextLen = aText.Len();
3174
3175 pTmpDXArray = new sal_Int32[ aText.Len() ];
3176 pDXArray = pTmpDXArray;
3177 Font _aOldFont( GetRefDevice()->GetFont() );
3178 aTmpFont.SetPhysFont( GetRefDevice() );
3179 aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.Len(), pTmpDXArray );
3180 if ( aStatus.DoRestoreFont() )
3181 GetRefDevice()->SetFont( _aOldFont );
3182
3183 // add a meta file comment if we record to a metafile
3184 if( bMetafileValid )
3185 {
3186 SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
3187 if( pFieldItem )
3188 {
3189 const SvxFieldData* pFieldData = pFieldItem->GetField();
3190 if( pFieldData )
3191 pMtf->AddAction( pFieldData->createBeginComment() );
3192 }
3193 }
3194
3195 }
3196 else if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
3197 {
3198 if ( pTextPortion->GetExtraValue() )
3199 aText = pTextPortion->GetExtraValue();
3200 aText += CH_HYPH;
3201 nTextStart = 0;
3202 nTextLen = aText.Len();
3203
3204 // #b6668980# crash when accessing 0 pointer in pDXArray
3205 pTmpDXArray = new sal_Int32[ aText.Len() ];
3206 pDXArray = pTmpDXArray;
3207 Font _aOldFont( GetRefDevice()->GetFont() );
3208 aTmpFont.SetPhysFont( GetRefDevice() );
3209 aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.Len(), pTmpDXArray );
3210 if ( aStatus.DoRestoreFont() )
3211 GetRefDevice()->SetFont( _aOldFont );
3212 }
3213
3214 long nTxtWidth = pTextPortion->GetSize().Width();
3215
3216 Point aOutPos( aTmpPos );
3217 aRedLineTmpPos = aTmpPos;
3218 // In RTL portions spell markup pos should be at the start of the
3219 // first chara as well. That is on the right end of the portion
3220 if (pTextPortion->IsRightToLeft())
3221 aRedLineTmpPos.X() += pTextPortion->GetSize().Width();
3222
3223 //L2R if ( pTextPortion->GetRightToLeft() )
3224 //L2R {
3225 //L2R sal_uInt16 nNextPortion = y+1;
3226 //L2R while ( nNextPortion <= pLine->GetEndPortion() )
3227 //L2R {
3228 //L2R TextPortion* pNextTextPortion = pPortion->GetTextPortions().GetObject( nNextPortion );
3229 //L2R if ( pNextTextPortion->GetRightToLeft() )
3230 //L2R {
3231 //L2R if ( !IsVertical() )
3232 //L2R aOutPos.X() += pNextTextPortion->GetSize().Width();
3233 //L2R else
3234 //L2R aOutPos.Y() += pNextTextPortion->GetSize().Width();
3235 //L2R }
3236 //L2R else
3237 //L2R break;
3238 //L2R nNextPortion++;
3239 //L2R }
3240 //L2R }
3241 if ( bStripOnly )
3242 {
3243 EEngineData::WrongSpellVector aWrongSpellVector;
3244
3245 if(GetStatus().DoOnlineSpelling() && pTextPortion->GetLen())
3246 {
3247 WrongList* pWrongs = pPortion->GetNode()->GetWrongList();
3248
3249 if(pWrongs && pWrongs->HasWrongs())
3250 {
3251 sal_uInt16 nStart(nIndex);
3252 sal_uInt16 nEnd(0);
3253 sal_Bool bWrong(pWrongs->NextWrong(nStart, nEnd));
3254 const sal_uInt16 nMaxEnd(nIndex + pTextPortion->GetLen());
3255
3256 while(bWrong)
3257 {
3258 if(nStart >= nMaxEnd)
3259 {
3260 break;
3261 }
3262
3263 if(nStart < nIndex)
3264 {
3265 nStart = nIndex;
3266 }
3267
3268 if(nEnd > nMaxEnd)
3269 {
3270 nEnd = nMaxEnd;
3271 }
3272
3273 // add to vector
3274 aWrongSpellVector.push_back(EEngineData::WrongSpellClass(nStart, nEnd));
3275
3276 // goto next index
3277 nStart = nEnd + 1;
3278
3279 if(nEnd < nMaxEnd)
3280 {
3281 bWrong = pWrongs->NextWrong(nStart, nEnd);
3282 }
3283 else
3284 {
3285 bWrong = sal_False;
3286 }
3287 }
3288 }
3289 }
3290
3291 const SvxFieldData* pFieldData = 0;
3292
3293 if(PORTIONKIND_FIELD == pTextPortion->GetKind())
3294 {
3295 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
3296 SvxFieldItem* pFieldItem = PTR_CAST(SvxFieldItem, pAttr->GetItem());
3297
3298 if(pFieldItem)
3299 {
3300 pFieldData = pFieldItem->GetField();
3301 }
3302 }
3303
3304 // support for EOC, EOW, EOS TEXT comments. To support that,
3305 // the locale is needed. With the locale and a XBreakIterator it is
3306 // possible to re-create the text marking info on primitive level
3307 const lang::Locale aLocale(GetLocale(EditPaM(pPortion->GetNode(), nIndex + 1)));
3308
3309 // create EOL and EOP bools
3310 const bool bEndOfLine(y == pLine->GetEndPortion());
3311 const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
3312
3313 // get Overline color (from ((const SvxOverlineItem*)GetItem())->GetColor() in
3314 // consequence, but also already set at pOutDev)
3315 const Color aOverlineColor(pOutDev->GetOverlineColor());
3316
3317 // get TextLine color (from ((const SvxUnderlineItem*)GetItem())->GetColor() in
3318 // consequence, but also already set at pOutDev)
3319 const Color aTextLineColor(pOutDev->GetTextLineColor());
3320
3321 // Unicode code points conversion according to ctl text numeral setting
3322 ImplInitDigitMode( 0, &aText, nTextStart, nTextLen, aTmpFont.GetLanguage() );
3323
3324 // StripPortions() data callback
3325 GetEditEnginePtr()->DrawingText( aOutPos, aText, nTextStart, nTextLen, pDXArray,
3326 aTmpFont, n, nIndex, pTextPortion->GetRightToLeft(),
3327 aWrongSpellVector.size() ? &aWrongSpellVector : 0,
3328 pFieldData,
3329 bEndOfLine, bEndOfParagraph, false, // support for EOL/EOP TEXT comments
3330 &aLocale,
3331 aOverlineColor,
3332 aTextLineColor);
3333
3334 // #108052# remember that EOP is written already for this ParaPortion
3335 if(bEndOfParagraph)
3336 {
3337 bEndOfParagraphWritten = true;
3338 }
3339 }
3340 else
3341 {
3342 short nEsc = aTmpFont.GetEscapement();
3343 if ( nOrientation )
3344 {
3345 // Bei Hoch/Tief selbst Hand anlegen:
3346 if ( aTmpFont.GetEscapement() )
3347 {
3348 long nDiff = aTmpFont.GetSize().Height() * aTmpFont.GetEscapement() / 100L;
3349 if ( !IsVertical() )
3350 aOutPos.Y() -= nDiff;
3351 else
3352 aOutPos.X() += nDiff;
3353 aRedLineTmpPos = aOutPos;
3354 aTmpFont.SetEscapement( 0 );
3355 }
3356
3357 aOutPos = lcl_ImplCalcRotatedPos( aOutPos, aOrigin, nSin, nCos );
3358 aTmpFont.SetOrientation( aTmpFont.GetOrientation()+nOrientation );
3359 aTmpFont.SetPhysFont( pOutDev );
3360
3361 }
3362 // nur ausgeben, was im sichtbaren Bereich beginnt:
3363 // Wichtig, weil Bug bei einigen Grafikkarten bei transparentem Font, Ausgabe bei neg.
3364 if ( nOrientation || ( !IsVertical() && ( ( aTmpPos.X() + nTxtWidth ) >= nFirstVisXPos ) )
3365 || ( IsVertical() && ( ( aTmpPos.Y() + nTxtWidth ) >= nFirstVisYPos ) ) )
3366 {
3367 if ( nEsc && ( ( aTmpFont.GetUnderline() != UNDERLINE_NONE ) ) )
3368 {
3369 // Das Hoch/Tief ohne Underline malen, das Underline
3370 // auf der BaseLine der Original-Fonthoehe ausgeben...
3371
3372 // Aber nur, wenn davor auch Unterstrichen!
3373 sal_Bool bSpecialUnderline = sal_False;
3374 EditCharAttrib* pPrev = pPortion->GetNode()->GetCharAttribs().FindAttrib( EE_CHAR_ESCAPEMENT, nIndex );
3375 if ( pPrev )
3376 {
3377 SvxFont aDummy;
3378 // Unterstreichung davor?
3379 if ( pPrev->GetStart() )
3380 {
3381 SeekCursor( pPortion->GetNode(), pPrev->GetStart(), aDummy );
3382 if ( aDummy.GetUnderline() != UNDERLINE_NONE )
3383 bSpecialUnderline = sal_True;
3384 }
3385 if ( !bSpecialUnderline && ( pPrev->GetEnd() < pPortion->GetNode()->Len() ) )
3386 {
3387 SeekCursor( pPortion->GetNode(), pPrev->GetEnd()+1, aDummy );
3388 if ( aDummy.GetUnderline() != UNDERLINE_NONE )
3389 bSpecialUnderline = sal_True;
3390 }
3391 }
3392 if ( bSpecialUnderline )
3393 {
3394 Size aSz = aTmpFont.GetPhysTxtSize( pOutDev, aText, nTextStart, nTextLen );
3395 sal_uInt8 nProp = aTmpFont.GetPropr();
3396 aTmpFont.SetEscapement( 0 );
3397 aTmpFont.SetPropr( 100 );
3398 aTmpFont.SetPhysFont( pOutDev );
3399 String aBlanks;
3400 aBlanks.Fill( nTextLen, ' ' );
3401 Point aUnderlinePos( aOutPos );
3402 if ( nOrientation )
3403 aUnderlinePos = lcl_ImplCalcRotatedPos( aTmpPos, aOrigin, nSin, nCos );
3404 pOutDev->DrawStretchText( aUnderlinePos, aSz.Width(), aBlanks, 0, nTextLen );
3405
3406 aTmpFont.SetUnderline( UNDERLINE_NONE );
3407 if ( !nOrientation )
3408 aTmpFont.SetEscapement( nEsc );
3409 aTmpFont.SetPropr( nProp );
3410 aTmpFont.SetPhysFont( pOutDev );
3411 }
3412 }
3413 Point aRealOutPos( aOutPos );
3414 if ( ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
3415 && pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed
3416 && pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation )
3417 {
3418 aRealOutPos.X() += pTextPortion->GetExtraInfos()->nPortionOffsetX;
3419 }
3420
3421 // --> FME 2005-06-17 #i37132# RTL portions with
3422 // compressed blank should not paint this blank:
3423 if ( pTextPortion->IsRightToLeft() && nTextLen >= 2 &&
3424 pDXArray[ nTextLen - 1 ] ==
3425 pDXArray[ nTextLen - 2 ] &&
3426 ' ' == aText.GetChar( nTextStart + nTextLen - 1 ) )
3427 --nTextLen;
3428 // <--
3429
3430 // output directly
3431 aTmpFont.QuickDrawText( pOutDev, aRealOutPos, aText, nTextStart, nTextLen, pDXArray );
3432
3433 if ( bDrawFrame )
3434 {
3435 Point aTopLeft( aTmpPos );
3436 aTopLeft.Y() -= pLine->GetMaxAscent();
3437 if ( nOrientation )
3438 aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
3439 Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
3440 pOutDev->DrawRect( aRect );
3441 }
3442
3443
3444 // PDF export:
3445 if ( pPDFExtOutDevData )
3446 {
3447 if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3448 {
3449 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
3450 SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
3451 if( pFieldItem )
3452 {
3453 const SvxFieldData* pFieldData = pFieldItem->GetField();
3454 if ( pFieldData->ISA( SvxURLField ) )
3455 {
3456 Point aTopLeft( aTmpPos );
3457 aTopLeft.Y() -= pLine->GetMaxAscent();
3458 // if ( nOrientation )
3459 // aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
3460
3461 Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
3462 vcl::PDFExtOutDevBookmarkEntry aBookmark;
3463 aBookmark.nLinkId = pPDFExtOutDevData->CreateLink( aRect );
3464 aBookmark.aBookmark = ((SvxURLField*)pFieldData)->GetURL();
3465 std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
3466 rBookmarks.push_back( aBookmark );
3467 }
3468 }
3469 }
3470 }
3471
3472 // comment
3473
3474
3475
3476
3477 }
3478
3479 #ifndef SVX_LIGHT
3480 if ( GetStatus().DoOnlineSpelling() && pPortion->GetNode()->GetWrongList()->HasWrongs() && pTextPortion->GetLen() )
3481 {
3482 {//#105750# adjust LinePos for superscript or subscript text
3483 short _nEsc = aTmpFont.GetEscapement();
3484 if( _nEsc )
3485 {
3486 long nShift = ((_nEsc*long(aTmpFont.GetSize().Height()))/ 100L);
3487 if( !IsVertical() )
3488 aRedLineTmpPos.Y() -= nShift;
3489 else
3490 aRedLineTmpPos.X() += nShift;
3491 }
3492 }
3493 Color aOldColor( pOutDev->GetLineColor() );
3494 pOutDev->SetLineColor( Color( GetColorConfig().GetColorValue( svtools::SPELL ).nColor ) );
3495 lcl_DrawRedLines( pOutDev, aTmpFont.GetSize().Height(), aRedLineTmpPos, nIndex, nIndex + pTextPortion->GetLen(), pDXArray, pPortion->GetNode()->GetWrongList(), nOrientation, aOrigin, IsVertical(), pTextPortion->IsRightToLeft() );
3496 pOutDev->SetLineColor( aOldColor );
3497 }
3498 #endif // !SVX_LIGHT
3499 }
3500
3501 pOutDev->Pop();
3502
3503 if ( pTmpDXArray )
3504 delete[] pTmpDXArray;
3505
3506 // R2L if ( !pTextPortion->GetRightToLeft() )
3507 // R2L {
3508 // R2L if ( !IsVertical() )
3509 // R2L aTmpPos.X() += nTxtWidth;
3510 // R2L else
3511 // R2L aTmpPos.Y() += nTxtWidth;
3512 // R2L }
3513 // R2L else
3514 // R2L {
3515 // R2L nR2LWidth += nTxtWidth;
3516 // R2L }
3517
3518 if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3519 {
3520 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
3521 DBG_ASSERT( pAttr, "Feld nicht gefunden" );
3522 DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Feld vom falschen Typ!" );
3523
3524 // add a meta file comment if we record to a metafile
3525 if( bMetafileValid )
3526 {
3527 SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
3528
3529 if( pFieldItem )
3530 {
3531 const SvxFieldData* pFieldData = pFieldItem->GetField();
3532 if( pFieldData )
3533 pMtf->AddAction( pFieldData->createEndComment() );
3534 }
3535 }
3536
3537 }
3538
3539 }
3540 break;
3541 // case PORTIONKIND_EXTRASPACE:
3542 case PORTIONKIND_TAB:
3543 {
3544 if ( pTextPortion->GetExtraValue() && ( pTextPortion->GetExtraValue() != ' ' ) )
3545 {
3546 SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
3547 aTmpFont.SetTransparent( sal_False );
3548 aTmpFont.SetEscapement( 0 );
3549 aTmpFont.SetPhysFont( pOutDev );
3550 long nCharWidth = aTmpFont.QuickGetTextSize( pOutDev, pTextPortion->GetExtraValue(), 0, 1, NULL ).Width();
3551 long nChars = 2;
3552 if( nCharWidth )
3553 nChars = pTextPortion->GetSize().Width() / nCharWidth;
3554 if ( nChars < 2 )
3555 nChars = 2; // wird durch DrawStretchText gestaucht.
3556 else if ( nChars == 2 )
3557 nChars = 3; // sieht besser aus
3558
3559 String aText;
3560 aText.Fill( (sal_uInt16)nChars, pTextPortion->GetExtraValue() );
3561
3562 if(bStripOnly)
3563 {
3564 // #71056# when converting to primitives, visualized TAB spaces need to be
3565 // visualized. Add tab#ed text here. Alternatively a primitive especially
3566 // representing this space and the single fill character would be possible, too.
3567 // For now, use what we have (the DrawingText callback)
3568 const lang::Locale aLocale(GetLocale(EditPaM(pPortion->GetNode(),nIndex + 1)));
3569
3570 // get Overline color (from ((const SvxOverlineItem*)GetItem())->GetColor() in
3571 // consequence, but also already set at pOutDev)
3572 const Color aOverlineColor(pOutDev->GetOverlineColor());
3573
3574 // get TextLine color (from ((const SvxUnderlineItem*)GetItem())->GetColor() in
3575 // consequence, but also already set at pOutDev)
3576 const Color aTextLineColor(pOutDev->GetTextLineColor());
3577
3578 // get AllWidth and together with nCharWidth create DXArray using per character
3579 // difference
3580 const sal_Int32 nAllWidth(pTextPortion->GetSize().Width());
3581 const double fSingleCharDiff((double(nAllWidth) / double(nChars)) - double(nCharWidth));
3582 sal_Int32* pTmpDXArray = 0;
3583
3584 if(fSingleCharDiff > 1.0)
3585 {
3586 // if more than one unit per character, create DXArray to create
3587 // something adequate to StretchText
3588 const double fAdvance(nCharWidth + fSingleCharDiff);
3589 const sal_uInt32 nCount(static_cast< sal_uInt32 >(nChars));
3590 pTmpDXArray = new sal_Int32[nCount];
3591 double fPos(0.0);
3592
3593 for(sal_uInt32 a(0); a < nCount; a++)
3594 {
3595 fPos += fAdvance;
3596 pTmpDXArray[a] = basegfx::fround(fPos);
3597 }
3598 }
3599
3600 // StripPortions() data callback
3601 GetEditEnginePtr()->DrawingText(
3602 aTmpPos,
3603 aText,
3604 0,
3605 nChars,
3606 pTmpDXArray,
3607 aTmpFont,
3608 n,
3609 nIndex,
3610 pTextPortion->GetRightToLeft(),
3611 0,
3612 0,
3613 false,
3614 false,
3615 false, // support for EOL/EOP TEXT comments
3616 &aLocale,
3617 aOverlineColor,
3618 aTextLineColor);
3619
3620 if(pTmpDXArray)
3621 {
3622 delete pTmpDXArray;
3623 }
3624 }
3625 else
3626 {
3627 pOutDev->DrawStretchText(aTmpPos,pTextPortion->GetSize().Width(),aText);
3628 }
3629 }
3630 }
3631 break;
3632 }
3633 nIndex = nIndex + pTextPortion->GetLen();
3634 }
3635 }
3636
3637 if ( ( nLine != nLastLine ) && !aStatus.IsOutliner() )
3638 {
3639 if ( !IsVertical() )
3640 aStartPos.Y() += nSBL;
3641 else
3642 aStartPos.X() -= nSBL;
3643 }
3644
3645 // keine sichtbaren Aktionen mehr?
3646 if ( !IsVertical() && ( aStartPos.Y() >= aClipRec.Bottom() ) )
3647 break;
3648 else if ( IsVertical() && ( aStartPos.X() <= aClipRec.Left() ) )
3649 break;
3650 }
3651
3652 if ( !aStatus.IsOutliner() )
3653 {
3654 const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3655 long nUL = GetYValue( rULItem.GetLower() );
3656 if ( !IsVertical() )
3657 aStartPos.Y() += nUL;
3658 else
3659 aStartPos.X() -= nUL;
3660 }
3661
3662 // #108052# Safer way for #i108052# and #i118881#: If for the current ParaPortion
3663 // EOP is not written, do it now. This will be safer than before. It has shown
3664 // that the reason for #i108052# was fixed/removed again, so this is a try to fix
3665 // the number of paragraphs (and counting empty ones) now independent from the
3666 // changes in EditEngine behaviour.
3667 if(!bEndOfParagraphWritten && !bPaintBullet && bStripOnly)
3668 {
3669 const Color aOverlineColor(pOutDev->GetOverlineColor());
3670 const Color aTextLineColor(pOutDev->GetTextLineColor());
3671
3672 GetEditEnginePtr()->DrawingText(
3673 aTmpPos, String(), 0, 0, 0,
3674 aTmpFont, n, nIndex, 0,
3675 0,
3676 0,
3677 false, true, false, // support for EOL/EOP TEXT comments
3678 0,
3679 aOverlineColor,
3680 aTextLineColor);
3681 }
3682 }
3683 else
3684 {
3685 if ( !IsVertical() )
3686 aStartPos.Y() += nParaHeight;
3687 else
3688 aStartPos.X() -= nParaHeight;
3689 }
3690
3691 if ( pPDFExtOutDevData )
3692 pPDFExtOutDevData->EndStructureElement();
3693
3694 // keine sichtbaren Aktionen mehr?
3695 if ( !IsVertical() && ( aStartPos.Y() > aClipRec.Bottom() ) )
3696 break;
3697 if ( IsVertical() && ( aStartPos.X() < aClipRec.Left() ) )
3698 break;
3699 }
3700 if ( aStatus.DoRestoreFont() )
3701 pOutDev->SetFont( aOldFont );
3702 }
3703
Paint(ImpEditView * pView,const Rectangle & rRec,OutputDevice * pTargetDevice,sal_Bool bUseVirtDev)3704 void ImpEditEngine::Paint( ImpEditView* pView, const Rectangle& rRec, OutputDevice* pTargetDevice, sal_Bool bUseVirtDev )
3705 {
3706 DBG_ASSERT( pView, "Keine View - Kein Paint!" );
3707 DBG_CHKOBJ( GetEditEnginePtr(), EditEngine, 0 );
3708
3709 if ( !GetUpdateMode() || IsInUndo() )
3710 return;
3711
3712 // Schnittmenge aus Paintbereich und OutputArea.
3713 Rectangle aClipRec( pView->GetOutputArea() );
3714 aClipRec.Intersection( rRec );
3715
3716 OutputDevice* pTarget = pTargetDevice ? pTargetDevice : pView->GetWindow();
3717
3718 if ( bUseVirtDev )
3719 {
3720 Rectangle aClipRecPixel( pTarget->LogicToPixel( aClipRec ) );
3721 if ( !IsVertical() )
3722 {
3723 // etwas mehr, falls abgerundet!
3724 aClipRecPixel.Right() += 1;
3725 aClipRecPixel.Bottom() += 1;
3726 }
3727 else
3728 {
3729 aClipRecPixel.Left() -= 1;
3730 aClipRecPixel.Bottom() += 1;
3731 }
3732
3733 // Wenn aClipRecPixel > XXXX, dann invalidieren ?!
3734
3735 VirtualDevice* pVDev = GetVirtualDevice( pTarget->GetMapMode(), pTarget->GetDrawMode() );
3736 pVDev->SetDigitLanguage( GetRefDevice()->GetDigitLanguage() );
3737
3738 {
3739 Color aBackgroundColor( pView->GetBackgroundColor() );
3740 // #i47161# Check if text is visible on background
3741 SvxFont aTmpFont;
3742 ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
3743 SeekCursor( pNode, 1, aTmpFont );
3744 Color aFontColor( aTmpFont.GetColor() );
3745 if( (aFontColor == COL_AUTO) || IsForceAutoColor() )
3746 aFontColor = GetAutoColor();
3747
3748 // #i69346# check for reverse color of input method attribute
3749 if( mpIMEInfos && (mpIMEInfos->aPos.GetNode() == pNode &&
3750 mpIMEInfos->pAttribs))
3751 {
3752 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ 0 ];
3753 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
3754 {
3755 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
3756 aFontColor = rStyleSettings.GetHighlightColor() ;
3757 }
3758 }
3759
3760 sal_uInt8 nColorDiff = aFontColor.GetColorError( aBackgroundColor );
3761 if( nColorDiff < 8 )
3762 aBackgroundColor = aFontColor.IsDark() ? COL_WHITE : COL_BLACK;
3763 pVDev->SetBackground( aBackgroundColor );
3764 }
3765
3766 sal_Bool bVDevValid = sal_True;
3767 Size aOutSz( pVDev->GetOutputSizePixel() );
3768 if ( ( aOutSz.Width() < aClipRecPixel.GetWidth() ) ||
3769 ( aOutSz.Height() < aClipRecPixel.GetHeight() ) )
3770 {
3771 bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3772 }
3773 else
3774 {
3775 // Das VirtDev kann bei einem Resize sehr gross werden =>
3776 // irgendwann mal kleiner machen!
3777 if ( ( aOutSz.Height() > ( aClipRecPixel.GetHeight() + RESDIFF ) ) ||
3778 ( aOutSz.Width() > ( aClipRecPixel.GetWidth() + RESDIFF ) ) )
3779 {
3780 bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3781 }
3782 else
3783 {
3784 pVDev->Erase();
3785 }
3786 }
3787 DBG_ASSERT( bVDevValid, "VirtualDevice failed to be enlarged!" );
3788 if ( !bVDevValid )
3789 {
3790 Paint( pView, rRec, NULL /* without VirtualDevice */ );
3791 return;
3792 }
3793
3794 // PaintRect fuer VDev nicht mit alignter Groesse,
3795 // da sonst die Zeile darunter auch ausgegeben werden muss:
3796 Rectangle aTmpRec( Point( 0, 0 ), aClipRec.GetSize() );
3797
3798 aClipRec = pTarget->PixelToLogic( aClipRecPixel );
3799 Point aStartPos;
3800 if ( !IsVertical() )
3801 {
3802 aStartPos = aClipRec.TopLeft();
3803 aStartPos = pView->GetDocPos( aStartPos );
3804 aStartPos.X() *= (-1);
3805 aStartPos.Y() *= (-1);
3806 }
3807 else
3808 {
3809 aStartPos = aClipRec.TopRight();
3810 Point aDocPos( pView->GetDocPos( aStartPos ) );
3811 aStartPos.X() = aClipRec.GetSize().Width() + aDocPos.Y();
3812 aStartPos.Y() = -aDocPos.X();
3813 }
3814
3815 Paint( pVDev, aTmpRec, aStartPos );
3816
3817 sal_Bool bClipRegion = sal_False;
3818 Region aOldRegion;
3819 MapMode aOldMapMode;
3820 if ( GetTextRanger() )
3821 {
3822 // Some problems here with push/pop, why?!
3823 // pTarget->Push( PUSH_CLIPREGION|PUSH_MAPMODE );
3824 bClipRegion = pTarget->IsClipRegion();
3825 aOldRegion = pTarget->GetClipRegion();
3826 // Wie bekomme ich das Polygon an die richtige Stelle????
3827 // Das Polygon bezieht sich auf die View, nicht auf das Window
3828 // => Origin umsetzen...
3829 aOldMapMode = pTarget->GetMapMode();
3830 Point aOrigin = aOldMapMode.GetOrigin();
3831 Point aViewPos = pView->GetOutputArea().TopLeft();
3832 aOrigin.Move( aViewPos.X(), aViewPos.Y() );
3833 aClipRec.Move( -aViewPos.X(), -aViewPos.Y() );
3834 MapMode aNewMapMode( aOldMapMode );
3835 aNewMapMode.SetOrigin( aOrigin );
3836 pTarget->SetMapMode( aNewMapMode );
3837 pTarget->SetClipRegion( Region( GetTextRanger()->GetPolyPolygon() ) );
3838 }
3839
3840 pTarget->DrawOutDev( aClipRec.TopLeft(), aClipRec.GetSize(),
3841 Point(0,0), aClipRec.GetSize(), *pVDev );
3842
3843 if ( GetTextRanger() )
3844 {
3845 // pTarget->Pop();
3846 if ( bClipRegion )
3847 pTarget->SetClipRegion( aOldRegion );
3848 else
3849 pTarget->SetClipRegion();
3850 pTarget->SetMapMode( aOldMapMode );
3851 }
3852
3853
3854 pView->DrawSelection(pView->GetEditSelection(), 0, pTarget);
3855 }
3856 else
3857 {
3858 Point aStartPos;
3859 if ( !IsVertical() )
3860 {
3861 aStartPos = pView->GetOutputArea().TopLeft();
3862 aStartPos.X() -= pView->GetVisDocLeft();
3863 aStartPos.Y() -= pView->GetVisDocTop();
3864 }
3865 else
3866 {
3867 aStartPos = pView->GetOutputArea().TopRight();
3868 aStartPos.X() += pView->GetVisDocTop();
3869 aStartPos.Y() -= pView->GetVisDocLeft();
3870 }
3871
3872 // Wenn Doc-Breite < OutputArea,Width, nicht umgebrochene Felder,
3873 // stehen die Felder sonst �ber, wenn > Zeile.
3874 // ( Oben nicht, da dort bereits Doc-Breite von Formatierung mit drin )
3875 if ( !IsVertical() && ( pView->GetOutputArea().GetWidth() > GetPaperSize().Width() ) )
3876 {
3877 long nMaxX = pView->GetOutputArea().Left() + GetPaperSize().Width();
3878 if ( aClipRec.Left() > nMaxX )
3879 return;
3880 if ( aClipRec.Right() > nMaxX )
3881 aClipRec.Right() = nMaxX;
3882 }
3883
3884 sal_Bool bClipRegion = pTarget->IsClipRegion();
3885 Region aOldRegion = pTarget->GetClipRegion();
3886 pTarget->IntersectClipRegion( aClipRec );
3887
3888 Paint( pTarget, aClipRec, aStartPos );
3889
3890 if ( bClipRegion )
3891 pTarget->SetClipRegion( aOldRegion );
3892 else
3893 pTarget->SetClipRegion();
3894
3895 pView->DrawSelection(pView->GetEditSelection(), 0, pTarget);
3896 }
3897
3898 }
3899
InsertContent(ContentNode * pNode,sal_uInt32 nPos)3900 void ImpEditEngine::InsertContent( ContentNode* pNode, sal_uInt32 nPos )
3901 {
3902 DBG_ASSERT( pNode, "NULL-Poointer in InsertContent! " );
3903 DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
3904 ParaPortion* pNew = new ParaPortion( pNode );
3905 GetParaPortions().Insert( pNew, nPos );
3906 aEditDoc.Insert( pNode, nPos );
3907 if ( IsCallParaInsertedOrDeleted() )
3908 GetEditEnginePtr()->ParagraphInserted( nPos );
3909 }
3910
SplitContent(sal_uInt32 nNode,sal_uInt16 nSepPos)3911 EditPaM ImpEditEngine::SplitContent( sal_uInt32 nNode, sal_uInt16 nSepPos )
3912 {
3913 ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
3914 DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
3915 DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
3916 DBG_ASSERT( nSepPos <= pNode->Len(), "Index im Wald: SplitContent" );
3917 EditPaM aPaM( pNode, nSepPos );
3918 return ImpInsertParaBreak( aPaM );
3919 }
3920
ConnectContents(sal_uInt32 nLeftNode,sal_Bool bBackward)3921 EditPaM ImpEditEngine::ConnectContents( sal_uInt32 nLeftNode, sal_Bool bBackward )
3922 {
3923 ContentNode* pLeftNode = aEditDoc.SaveGetObject( nLeftNode );
3924 ContentNode* pRightNode = aEditDoc.SaveGetObject( nLeftNode+1 );
3925 DBG_ASSERT( pLeftNode, "Ungueltiger linker Node in ConnectContents" );
3926 DBG_ASSERT( pRightNode, "Ungueltiger rechter Node in ConnectContents" );
3927 DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
3928 return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward );
3929 }
3930
SetUpdateModeForAcc(sal_Bool bUp)3931 void ImpEditEngine::SetUpdateModeForAcc( sal_Bool bUp)
3932 {
3933 bUpdateForAcc = bUp;
3934 }
3935
GetUpdateModeForAcc()3936 sal_Bool ImpEditEngine::GetUpdateModeForAcc()
3937 {
3938 return bUpdateForAcc;
3939 }
3940
SetUpdateMode(sal_Bool bUp,EditView * pCurView,sal_Bool bForceUpdate)3941 void ImpEditEngine::SetUpdateMode( sal_Bool bUp, EditView* pCurView, sal_Bool bForceUpdate )
3942 {
3943 sal_Bool bChanged = ( GetUpdateMode() != bUp );
3944
3945 // Beim Umschalten von sal_True auf sal_False waren alle Selektionen sichtbar,
3946 // => Wegmalen
3947 // Umgekehrt waren alle unsichtbar => malen
3948
3949 // DrawAllSelections(); sieht im Outliner schlecht aus !
3950 // EditView* pView = aEditViewList.First();
3951 // while ( pView )
3952 // {
3953 // DBG_CHKOBJ( pView, EditView, 0 );
3954 // pView->pImpEditView->DrawSelection();
3955 // pView = aEditViewList.Next();
3956 // }
3957
3958 // Wenn !bFormatted, also z.B. nach SetText, braucht bei UpdateMode sal_True
3959 // nicht sofort formatiert werden, weil warscheinlich noch Text kommt.
3960 // Spaetestens bei einem Paint / CalcTextWidth wird formatiert.
3961
3962 bUpdate = bUp;
3963 if ( bUpdate && ( bChanged || bForceUpdate ) )
3964 FormatAndUpdate( pCurView );
3965 }
3966
ShowParagraph(sal_uInt32 nParagraph,sal_Bool bShow)3967 void ImpEditEngine::ShowParagraph( sal_uInt32 nParagraph, sal_Bool bShow )
3968 {
3969 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3970 DBG_ASSERT( pPPortion, "ShowParagraph: Absatz existiert nicht!" );
3971 if ( pPPortion && ( pPPortion->IsVisible() != bShow ) )
3972 {
3973 pPPortion->SetVisible( bShow );
3974
3975 if ( !bShow )
3976 {
3977 // Als deleted kenzeichnen, damit keine Selektion auf diesem
3978 // Absatz beginnt oder endet...
3979 DeletedNodeInfo* pDelInfo = new DeletedNodeInfo( (sal_uIntPtr)pPPortion->GetNode(), nParagraph );
3980 aDeletedNodes.Insert( pDelInfo, aDeletedNodes.Count() );
3981 UpdateSelections();
3982 // Dann kriege ich den unteren Bereich nicht invalidiert,
3983 // wenn UpdateMode = sal_False!
3984 // Wenn doch, dann vor SetVisible auf sal_False merken!
3985 // nCurTextHeight -= pPPortion->GetHeight();
3986 }
3987
3988 if ( bShow && ( pPPortion->IsInvalid() || !pPPortion->nHeight ) )
3989 {
3990 if ( !GetTextRanger() )
3991 {
3992 if ( pPPortion->IsInvalid() )
3993 {
3994 Font aOldFont( GetRefDevice()->GetFont() );
3995 CreateLines( nParagraph, 0 ); // 0: Kein TextRanger
3996 if ( aStatus.DoRestoreFont() )
3997 GetRefDevice()->SetFont( aOldFont );
3998 }
3999 else
4000 {
4001 CalcHeight( pPPortion );
4002 }
4003 nCurTextHeight += pPPortion->GetHeight();
4004 }
4005 else
4006 {
4007 nCurTextHeight = 0x7fffffff;
4008 }
4009 }
4010
4011 pPPortion->SetMustRepaint( sal_True );
4012 if ( GetUpdateMode() && !IsInUndo() && !GetTextRanger() )
4013 {
4014 aInvalidRec = Rectangle( Point( 0, GetParaPortions().GetYOffset( pPPortion ) ),
4015 Point( GetPaperSize().Width(), nCurTextHeight ) );
4016 UpdateViews( GetActiveView() );
4017 }
4018 }
4019 }
4020
IsParagraphVisible(sal_uInt32 nParagraph)4021 sal_Bool ImpEditEngine::IsParagraphVisible( sal_uInt32 nParagraph )
4022 {
4023 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
4024 DBG_ASSERT( pPPortion, "IsParagraphVisible: Absatz existiert nicht!" );
4025 if ( pPPortion )
4026 return pPPortion->IsVisible();
4027 return sal_False;
4028 }
4029
MoveParagraphs(Range aOldPositions,sal_uInt32 nNewPos,EditView * pCurView)4030 EditSelection ImpEditEngine::MoveParagraphs( Range aOldPositions, sal_uInt32 nNewPos, EditView* pCurView )
4031 {
4032 DBG_ASSERT( GetParaPortions().Count() != 0, "Keine Absaetze gefunden: MoveParagraphs" );
4033 if ( GetParaPortions().Count() == 0 )
4034 return EditSelection();
4035 aOldPositions.Justify();
4036
4037 EditSelection aSel( ImpMoveParagraphs( aOldPositions, nNewPos ) );
4038
4039 if ( nNewPos >= GetParaPortions().Count() )
4040 nNewPos = GetParaPortions().Count() - 1;
4041
4042 // Dort, wo der Absatz eingefuegt wurde, muss richtig gepainted werden:
4043 // Dort, wo der Absatz entfernt wurde, muss richtig gepainted werden:
4044 // ( Und dazwischen entsprechend auch...)
4045 if ( pCurView && ( GetUpdateMode() == sal_True ) )
4046 {
4047 // in diesem Fall kann ich direkt neu malen, ohne die
4048 // Portions zu Invalidieren.
4049 sal_uInt32 nFirstPortion = Min( (sal_uInt32)aOldPositions.Min(), nNewPos );
4050 sal_uInt32 nLastPortion = Max( (sal_uInt32)aOldPositions.Max(), nNewPos );
4051
4052 ParaPortion* pUpperPortion = GetParaPortions().SaveGetObject( nFirstPortion );
4053 ParaPortion* pLowerPortion = GetParaPortions().SaveGetObject( nLastPortion );
4054
4055 aInvalidRec = Rectangle(); // leermachen
4056 aInvalidRec.Left() = 0;
4057 aInvalidRec.Right() = aPaperSize.Width();
4058 aInvalidRec.Top() = GetParaPortions().GetYOffset( pUpperPortion );
4059 aInvalidRec.Bottom() = GetParaPortions().GetYOffset( pLowerPortion ) + pLowerPortion->GetHeight();
4060
4061 UpdateViews( pCurView );
4062 }
4063 else
4064 {
4065 // aber der oberen ungueltigen Position neu painten...
4066 sal_uInt32 nFirstInvPara = Min( (sal_uInt32)aOldPositions.Min(), nNewPos );
4067 InvalidateFromParagraph( nFirstInvPara );
4068 }
4069 return aSel;
4070 }
4071
InvalidateFromParagraph(sal_uInt32 nFirstInvPara)4072 void ImpEditEngine::InvalidateFromParagraph( sal_uInt32 nFirstInvPara )
4073 {
4074 // Es werden nicht die folgenden Absaetze invalidiert,
4075 // da ResetHeight() => Groessenanderung => alles folgende wird
4076 // sowieso neu ausgegeben.
4077 ParaPortion* pTmpPortion;
4078 if ( nFirstInvPara != 0 )
4079 {
4080 pTmpPortion = GetParaPortions().GetObject( nFirstInvPara-1 );
4081 pTmpPortion->MarkInvalid( pTmpPortion->GetNode()->Len(), 0 );
4082 }
4083 else
4084 {
4085 pTmpPortion = GetParaPortions().GetObject( 0 );
4086 pTmpPortion->MarkSelectionInvalid( 0, pTmpPortion->GetNode()->Len() );
4087 }
4088 pTmpPortion->ResetHeight();
4089 }
4090
IMPL_LINK_INLINE_START(ImpEditEngine,StatusTimerHdl,Timer *,EMPTYARG)4091 IMPL_LINK_INLINE_START( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
4092 {
4093 CallStatusHdl();
4094 return 0;
4095 }
IMPL_LINK_INLINE_END(ImpEditEngine,StatusTimerHdl,Timer *,EMPTYARG)4096 IMPL_LINK_INLINE_END( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
4097
4098 void ImpEditEngine::CallStatusHdl()
4099 {
4100 if ( aStatusHdlLink.IsSet() && aStatus.GetStatusWord() )
4101 {
4102 // Der Status muss vor Call zurueckgesetzt werden,
4103 // da im Hdl evtl. weitere Fags gesetzt werden...
4104 EditStatus aTmpStatus( aStatus );
4105 aStatus.Clear();
4106 aStatusHdlLink.Call( &aTmpStatus );
4107 aStatusTimer.Stop(); // Falls von Hand gerufen...
4108 }
4109 }
4110
GetPrevVisNode(ContentNode * pCurNode)4111 ContentNode* ImpEditEngine::GetPrevVisNode( ContentNode* pCurNode )
4112 {
4113 ParaPortion* pPortion = FindParaPortion( pCurNode );
4114 DBG_ASSERT( pPortion, "GetPrevVisibleNode: Keine passende Portion!" );
4115 pPortion = GetPrevVisPortion( pPortion );
4116 if ( pPortion )
4117 return pPortion->GetNode();
4118 return 0;
4119 }
4120
GetNextVisNode(ContentNode * pCurNode)4121 ContentNode* ImpEditEngine::GetNextVisNode( ContentNode* pCurNode )
4122 {
4123 ParaPortion* pPortion = FindParaPortion( pCurNode );
4124 DBG_ASSERT( pPortion, "GetNextVisibleNode: Keine passende Portion!" );
4125 pPortion = GetNextVisPortion( pPortion );
4126 if ( pPortion )
4127 return pPortion->GetNode();
4128 return 0;
4129 }
4130
GetPrevVisPortion(ParaPortion * pCurPortion)4131 ParaPortion* ImpEditEngine::GetPrevVisPortion( ParaPortion* pCurPortion )
4132 {
4133 sal_uInt32 nPara = GetParaPortions().GetPos( pCurPortion );
4134 DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisPortion" );
4135 ParaPortion* pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4136 while ( pPortion && !pPortion->IsVisible() )
4137 pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4138
4139 return pPortion;
4140 }
4141
GetNextVisPortion(ParaPortion * pCurPortion)4142 ParaPortion* ImpEditEngine::GetNextVisPortion( ParaPortion* pCurPortion )
4143 {
4144 sal_uInt32 nPara = GetParaPortions().GetPos( pCurPortion );
4145 DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisNode" );
4146 ParaPortion* pPortion = GetParaPortions().SaveGetObject( ++nPara );
4147 while ( pPortion && !pPortion->IsVisible() )
4148 pPortion = GetParaPortions().SaveGetObject( ++nPara );
4149
4150 return pPortion;
4151 }
4152
InsertParagraph(sal_uInt32 nPara)4153 EditPaM ImpEditEngine::InsertParagraph( sal_uInt32 nPara )
4154 {
4155 EditPaM aPaM;
4156 if ( nPara != 0 )
4157 {
4158 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara-1 );
4159 if ( !pNode )
4160 pNode = GetEditDoc().SaveGetObject( GetEditDoc().Count() - 1 );
4161 DBG_ASSERT( pNode, "Kein einziger Absatz in InsertParagraph ?" );
4162 aPaM = EditPaM( pNode, pNode->Len() );
4163 }
4164 else
4165 {
4166 ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
4167 aPaM = EditPaM( pNode, 0 );
4168 }
4169
4170 return ImpInsertParaBreak( aPaM );
4171 }
4172
SelectParagraph(sal_uInt32 nPara)4173 EditSelection* ImpEditEngine::SelectParagraph( sal_uInt32 nPara )
4174 {
4175 EditSelection* pSel = 0;
4176 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
4177 DBG_ASSERTWARNING( pNode, "Absatz existiert nicht: SelectParagraph" );
4178 if ( pNode )
4179 pSel = new EditSelection( EditPaM( pNode, 0 ), EditPaM( pNode, pNode->Len() ) );
4180
4181 return pSel;
4182 }
4183
FormatAndUpdate(EditView * pCurView)4184 void ImpEditEngine::FormatAndUpdate( EditView* pCurView )
4185 {
4186 if ( bDowning )
4187 return ;
4188
4189 if ( IsInUndo() )
4190 IdleFormatAndUpdate( pCurView );
4191 else
4192 {
4193 FormatDoc();
4194 UpdateViews( pCurView );
4195 }
4196 }
4197
SetFlatMode(sal_Bool bFlat)4198 void ImpEditEngine::SetFlatMode( sal_Bool bFlat )
4199 {
4200 if ( bFlat != aStatus.UseCharAttribs() )
4201 return;
4202
4203 if ( !bFlat )
4204 aStatus.TurnOnFlags( EE_CNTRL_USECHARATTRIBS );
4205 else
4206 aStatus.TurnOffFlags( EE_CNTRL_USECHARATTRIBS );
4207
4208 aEditDoc.CreateDefFont( !bFlat );
4209
4210 FormatFullDoc();
4211 UpdateViews( (EditView*) 0);
4212 if ( pActiveView )
4213 pActiveView->ShowCursor();
4214 }
4215
SetCharStretching(sal_uInt16 nX,sal_uInt16 nY)4216 void ImpEditEngine::SetCharStretching( sal_uInt16 nX, sal_uInt16 nY )
4217 {
4218 if ( !IsVertical() )
4219 {
4220 nStretchX = nX;
4221 nStretchY = nY;
4222 }
4223 else
4224 {
4225 nStretchX = nY;
4226 nStretchY = nX;
4227 }
4228
4229 if ( aStatus.DoStretch() )
4230 {
4231 FormatFullDoc();
4232 UpdateViews( GetActiveView() );
4233 }
4234 }
4235
DoStretchChars(sal_uInt16 nX,sal_uInt16 nY)4236 void ImpEditEngine::DoStretchChars( sal_uInt16 nX, sal_uInt16 nY )
4237 {
4238 UndoActionStart( EDITUNDO_STRETCH );
4239 sal_uInt32 nParas = GetEditDoc().Count();
4240 for ( sal_uInt32 nPara = 0; nPara < nParas; nPara++ )
4241 {
4242 ContentNode* pNode = GetEditDoc()[nPara];
4243 SfxItemSet aTmpSet( pNode->GetContentAttribs().GetItems() );
4244
4245 if ( nX != 100 )
4246 {
4247 // Fontbreite
4248 SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*) pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH ).Clone();
4249 sal_uInt32 nProp = pNewWidth->GetValue(); // sal_uInt32, kann temporaer gross werden
4250 nProp *= nX;
4251 nProp /= 100;
4252 pNewWidth->SetValue( (sal_uInt16)nProp );
4253 aTmpSet.Put( *pNewWidth );
4254 delete pNewWidth;
4255
4256 // Kerning:
4257 const SvxKerningItem& rKerningItem =
4258 (const SvxKerningItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_KERNING );
4259 SvxKerningItem* pNewKerning = (SvxKerningItem*)rKerningItem.Clone();
4260 long nKerning = pNewKerning->GetValue();
4261 if ( nKerning > 0 )
4262 {
4263 nKerning *= nX;
4264 nKerning /= 100;
4265 }
4266 else if ( nKerning < 0 )
4267 {
4268 // Bei Negativen Werten:
4269 // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
4270 nKerning *= 100;
4271 nKerning /= nX;
4272 }
4273 pNewKerning->SetValue( (short)nKerning );
4274 aTmpSet.Put( *pNewKerning);
4275 delete pNewKerning;
4276 }
4277 else
4278 aTmpSet.ClearItem( EE_CHAR_FONTWIDTH );
4279
4280 if ( nY != 100 )
4281 {
4282 // Fonthoehe
4283 for ( int nItem = 0; nItem < 3; nItem++ )
4284 {
4285 sal_uInt16 nItemId = EE_CHAR_FONTHEIGHT;
4286 if ( nItem == 1 )
4287 nItemId = EE_CHAR_FONTHEIGHT_CJK;
4288 else if ( nItem == 2 )
4289 nItemId = EE_CHAR_FONTHEIGHT_CTL;
4290
4291 const SvxFontHeightItem& rHeightItem =
4292 (const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( nItemId );
4293 SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)rHeightItem.Clone();
4294 sal_uInt32 nHeight = pNewHeight->GetHeight();
4295 nHeight *= nY;
4296 nHeight /= 100;
4297 pNewHeight->SetHeightValue( nHeight );
4298 aTmpSet.Put( *pNewHeight );
4299 delete pNewHeight;
4300 }
4301
4302 // Absatzabstaende
4303 const SvxULSpaceItem& rULSpaceItem =
4304 (const SvxULSpaceItem&)pNode->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4305 SvxULSpaceItem* pNewUL = (SvxULSpaceItem*)rULSpaceItem.Clone();
4306 sal_uInt32 nUpper = pNewUL->GetUpper();
4307 nUpper *= nY;
4308 nUpper /= 100;
4309 pNewUL->SetUpper( (sal_uInt16)nUpper );
4310 sal_uInt32 nLower = pNewUL->GetLower();
4311 nLower *= nY;
4312 nLower /= 100;
4313 pNewUL->SetLower( (sal_uInt16)nLower );
4314 aTmpSet.Put( *pNewUL );
4315 delete pNewUL;
4316 }
4317 else
4318 aTmpSet.ClearItem( EE_CHAR_FONTHEIGHT );
4319
4320 SetParaAttribs( nPara, aTmpSet );
4321
4322 // harte Attribute:
4323 sal_uInt16 nLastEnd = 0; // damit nach entfernen und neu nicht nochmal
4324 CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
4325 sal_uInt16 nAttribs = rAttribs.Count();
4326 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
4327 {
4328 EditCharAttrib* pAttr = rAttribs[nAttr];
4329 if ( pAttr->GetStart() >= nLastEnd )
4330 {
4331 sal_uInt16 nWhich = pAttr->Which();
4332 SfxPoolItem* pNew = 0;
4333 if ( nWhich == EE_CHAR_FONTHEIGHT )
4334 {
4335 SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)pAttr->GetItem()->Clone();
4336 sal_uInt32 nHeight = pNewHeight->GetHeight();
4337 nHeight *= nY;
4338 nHeight /= 100;
4339 pNewHeight->SetHeightValue( nHeight );
4340 pNew = pNewHeight;
4341 }
4342 else if ( nWhich == EE_CHAR_FONTWIDTH )
4343 {
4344 SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*)pAttr->GetItem()->Clone();
4345 sal_uInt32 nProp = pNewWidth->GetValue();
4346 nProp *= nX;
4347 nProp /= 100;
4348 pNewWidth->SetValue( (sal_uInt16)nProp );
4349 pNew = pNewWidth;
4350 }
4351 else if ( nWhich == EE_CHAR_KERNING )
4352 {
4353 SvxKerningItem* pNewKerning = (SvxKerningItem*)pAttr->GetItem()->Clone();
4354 long nKerning = pNewKerning->GetValue();
4355 if ( nKerning > 0 )
4356 {
4357 nKerning *= nX;
4358 nKerning /= 100;
4359 }
4360 else if ( nKerning < 0 )
4361 {
4362 // Bei Negativen Werten:
4363 // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
4364 nKerning *= 100;
4365 nKerning /= nX;
4366 }
4367 pNewKerning->SetValue( (short)nKerning );
4368 pNew = pNewKerning;
4369 }
4370 if ( pNew )
4371 {
4372 SfxItemSet _aTmpSet( GetEmptyItemSet() );
4373 _aTmpSet.Put( *pNew );
4374 SetAttribs( EditSelection( EditPaM( pNode, pAttr->GetStart() ),
4375 EditPaM( pNode, pAttr->GetEnd() ) ), _aTmpSet );
4376
4377 nLastEnd = pAttr->GetEnd();
4378 delete pNew;
4379 }
4380 }
4381 }
4382 }
4383 UndoActionEnd( EDITUNDO_STRETCH );
4384 }
4385
GetNumberFormat(const ContentNode * pNode) const4386 const SvxNumberFormat* ImpEditEngine::GetNumberFormat( const ContentNode *pNode ) const
4387 {
4388 const SvxNumberFormat *pRes = 0;
4389
4390 if (pNode)
4391 {
4392 // get index of paragraph
4393 sal_uInt32 nPara = GetEditDoc().GetPos( const_cast< ContentNode * >(pNode) );
4394 DBG_ASSERT( nPara < EE_PARA_MAX, "node not found in array" );
4395 if (nPara < EE_PARA_MAX)
4396 {
4397 // the called function may be overloaded by an OutlinerEditEng object to provide
4398 // access to the SvxNumberFormat of the Outliner.
4399 // The EditEngine implementation will just return 0.
4400 pRes = pEditEngine->GetNumberFormat( nPara );
4401 }
4402 }
4403
4404 return pRes;
4405 }
4406
GetSpaceBeforeAndMinLabelWidth(const ContentNode * pNode,sal_Int32 * pnSpaceBefore,sal_Int32 * pnMinLabelWidth) const4407 sal_Int32 ImpEditEngine::GetSpaceBeforeAndMinLabelWidth(
4408 const ContentNode *pNode,
4409 sal_Int32 *pnSpaceBefore, sal_Int32 *pnMinLabelWidth ) const
4410 {
4411 // nSpaceBefore matches the ODF attribut text:space-before
4412 // nMinLabelWidth matches the ODF attribut text:min-label-width
4413
4414 const SvxNumberFormat *pNumFmt = GetNumberFormat( pNode );
4415
4416 // if no number format was found we have no Outliner or the numbering level
4417 // within the Outliner is -1 which means no number format should be applied.
4418 // Thus the default values to be returned are 0.
4419 sal_Int32 nSpaceBefore = 0;
4420 sal_Int32 nMinLabelWidth = 0;
4421
4422 if (pNumFmt)
4423 {
4424 nMinLabelWidth = -pNumFmt->GetFirstLineOffset();
4425 nSpaceBefore = pNumFmt->GetAbsLSpace() - nMinLabelWidth;
4426 DBG_ASSERT( nMinLabelWidth >= 0, "ImpEditEngine::GetSpaceBeforeAndMinLabelWidth: min-label-width < 0 encountered" );
4427 }
4428 if (pnSpaceBefore)
4429 *pnSpaceBefore = nSpaceBefore;
4430 if (pnMinLabelWidth)
4431 *pnMinLabelWidth = nMinLabelWidth;
4432
4433 return nSpaceBefore + nMinLabelWidth;
4434 }
4435
GetLRSpaceItem(ContentNode * pNode)4436 const SvxLRSpaceItem& ImpEditEngine::GetLRSpaceItem( ContentNode* pNode )
4437 {
4438 return (const SvxLRSpaceItem&)pNode->GetContentAttribs().GetItem( aStatus.IsOutliner() ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
4439 }
4440
4441 // Either sets the digit mode at the output device or
4442 // modifies the passed string according to the text numeral setting:
ImplInitDigitMode(OutputDevice * pOutDev,String * pString,xub_StrLen nStt,xub_StrLen nLen,LanguageType eCurLang)4443 void ImpEditEngine::ImplInitDigitMode( OutputDevice* pOutDev, String* pString, xub_StrLen nStt, xub_StrLen nLen, LanguageType eCurLang )
4444 {
4445 // #114278# Also setting up digit language from Svt options
4446 // (cannot reliably inherit the outdev's setting)
4447 if( !pCTLOptions )
4448 pCTLOptions = new SvtCTLOptions;
4449
4450 LanguageType eLang = eCurLang;
4451 const SvtCTLOptions::TextNumerals nCTLTextNumerals = pCTLOptions->GetCTLTextNumerals();
4452
4453 if ( SvtCTLOptions::NUMERALS_HINDI == nCTLTextNumerals )
4454 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4455 else if ( SvtCTLOptions::NUMERALS_ARABIC == nCTLTextNumerals )
4456 eLang = LANGUAGE_ENGLISH;
4457 else if ( SvtCTLOptions::NUMERALS_SYSTEM == nCTLTextNumerals )
4458 eLang = (LanguageType) Application::GetSettings().GetLanguage();
4459
4460 if(pOutDev)
4461 {
4462 pOutDev->SetDigitLanguage( eLang );
4463 }
4464 else if (pString)
4465 {
4466 // see sallayout.cxx in vcl
4467 int nOffset;
4468 switch( eLang & LANGUAGE_MASK_PRIMARY )
4469 {
4470 default:
4471 nOffset = 0;
4472 break;
4473 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
4474 nOffset = 0x0660 - '0'; // arabic-indic digits
4475 break;
4476 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
4477 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
4478 case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
4479 nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
4480 break;
4481 }
4482 if (nOffset)
4483 {
4484 const xub_StrLen nEnd = nStt + nLen;
4485 for( xub_StrLen nIdx = nStt; nIdx < nEnd; ++nIdx )
4486 {
4487 sal_Unicode nChar = pString->GetChar( nIdx );
4488 if( (nChar < '0') || ('9' < nChar) )
4489 continue;
4490 nChar = (sal_Unicode)(nChar + nOffset);
4491 pString->SetChar( nIdx, nChar );
4492 }
4493 }
4494 }
4495 }
4496
ImplInitLayoutMode(OutputDevice * pOutDev,sal_uInt32 nPara,sal_uInt16 nIndex)4497 void ImpEditEngine::ImplInitLayoutMode( OutputDevice* pOutDev, sal_uInt32 nPara, sal_uInt16 nIndex )
4498 {
4499 sal_Bool bCTL = sal_False;
4500 sal_uInt8 bR2L = sal_False;
4501 if ( nIndex == 0xFFFF )
4502 {
4503 bCTL = HasScriptType( nPara, i18n::ScriptType::COMPLEX );
4504 bR2L = IsRightToLeft( nPara );
4505 }
4506 else
4507 {
4508 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
4509 short nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
4510 bCTL = nScriptType == i18n::ScriptType::COMPLEX;
4511 bR2L = GetRightToLeft( nPara, nIndex + 1); // this change was discussed in issue 37190
4512 // it also works for issue 55927
4513 }
4514
4515 sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
4516
4517 // We always use the left position for DrawText()
4518 nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL);
4519
4520 if ( !bCTL && !bR2L)
4521 {
4522 // No CTL/Bidi checking necessary
4523 nLayoutMode |= ( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4524 }
4525 else
4526 {
4527 // CTL/Bidi checking necessary
4528 // Don't use BIDI_STRONG, VCL must do some checks.
4529 nLayoutMode &= ~( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4530
4531 if ( bR2L )
4532 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
4533 }
4534
4535 pOutDev->SetLayoutMode( nLayoutMode );
4536
4537 // #114278# Also setting up digit language from Svt options
4538 // (cannot reliably inherit the outdev's setting)
4539 LanguageType eLang;
4540
4541 if( !pCTLOptions )
4542 pCTLOptions = new SvtCTLOptions;
4543
4544 if ( SvtCTLOptions::NUMERALS_HINDI == pCTLOptions->GetCTLTextNumerals() )
4545 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4546 else if ( SvtCTLOptions::NUMERALS_ARABIC == pCTLOptions->GetCTLTextNumerals() )
4547 eLang = LANGUAGE_ENGLISH;
4548 else
4549 eLang = (LanguageType) Application::GetSettings().GetLanguage();
4550
4551 pOutDev->SetDigitLanguage( eLang );
4552 }
4553
ImplGetBreakIterator() const4554 Reference < i18n::XBreakIterator > ImpEditEngine::ImplGetBreakIterator() const
4555 {
4556 if ( !xBI.is() )
4557 {
4558 Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
4559 xBI.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
4560 }
4561 return xBI;
4562 }
4563
ImplGetInputSequenceChecker() const4564 Reference < i18n::XExtendedInputSequenceChecker > ImpEditEngine::ImplGetInputSequenceChecker() const
4565 {
4566 if ( !xISC.is() )
4567 {
4568 Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
4569 Reference < XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
4570 if ( xI.is() )
4571 {
4572 Any x = xI->queryInterface( ::getCppuType((const Reference< i18n::XExtendedInputSequenceChecker >*)0) );
4573 x >>= xISC;
4574 }
4575 }
4576 return xISC;
4577 }
4578
GetAutoColor() const4579 Color ImpEditEngine::GetAutoColor() const
4580 {
4581 Color aColor = const_cast<ImpEditEngine*>(this)->GetColorConfig().GetColorValue( svtools::FONTCOLOR ).nColor;
4582
4583 if ( GetBackgroundColor() != COL_AUTO )
4584 {
4585 if ( GetBackgroundColor().IsDark() && aColor.IsDark() )
4586 aColor = COL_WHITE;
4587 else if ( GetBackgroundColor().IsBright() && aColor.IsBright() )
4588 aColor = COL_BLACK;
4589 }
4590
4591 return aColor;
4592 }
4593
4594
ImplCalcAsianCompression(ContentNode * pNode,TextPortion * pTextPortion,sal_uInt16 nStartPos,sal_Int32 * pDXArray,sal_uInt16 n100thPercentFromMax,sal_Bool bManipulateDXArray)4595 sal_Bool ImpEditEngine::ImplCalcAsianCompression( ContentNode* pNode, TextPortion* pTextPortion, sal_uInt16 nStartPos, sal_Int32* pDXArray, sal_uInt16 n100thPercentFromMax, sal_Bool bManipulateDXArray )
4596 {
4597 DBG_ASSERT( GetAsianCompressionMode(), "ImplCalcAsianCompression - Why?" );
4598 DBG_ASSERT( pTextPortion->GetLen(), "ImplCalcAsianCompression - Empty Portion?" );
4599
4600 // Percent is 1/100 Percent...
4601
4602 if ( n100thPercentFromMax == 10000 )
4603 pTextPortion->SetExtraInfos( NULL );
4604
4605 sal_Bool bCompressed = sal_False;
4606
4607 if ( GetScriptType( EditPaM( pNode, nStartPos+1 ) ) == i18n::ScriptType::ASIAN )
4608 {
4609 long nNewPortionWidth = pTextPortion->GetSize().Width();
4610 sal_uInt16 nPortionLen = pTextPortion->GetLen();
4611 for ( sal_uInt16 n = 0; n < nPortionLen; n++ )
4612 {
4613 sal_uInt8 nType = GetCharTypeForCompression( pNode->GetChar( n+nStartPos ) );
4614
4615 sal_Bool bCompressPunctuation = ( nType == CHAR_PUNCTUATIONLEFT ) || ( nType == CHAR_PUNCTUATIONRIGHT );
4616 sal_Bool bCompressKana = ( nType == CHAR_KANA ) && ( GetAsianCompressionMode() == text::CharacterCompressionType::PUNCTUATION_AND_KANA );
4617
4618 // create Extra infos only if needed...
4619 if ( bCompressPunctuation || bCompressKana )
4620 {
4621 if ( !pTextPortion->GetExtraInfos() )
4622 {
4623 ExtraPortionInfo* pExtraInfos = new ExtraPortionInfo;
4624 pTextPortion->SetExtraInfos( pExtraInfos );
4625 pExtraInfos->nOrgWidth = pTextPortion->GetSize().Width();
4626 pExtraInfos->nAsianCompressionTypes = CHAR_NORMAL;
4627 }
4628 pTextPortion->GetExtraInfos()->nMaxCompression100thPercent = n100thPercentFromMax;
4629 pTextPortion->GetExtraInfos()->nAsianCompressionTypes |= nType;
4630 // pTextPortion->GetExtraInfos()->nCompressedChars++;
4631
4632 long nOldCharWidth;
4633 if ( (n+1) < nPortionLen )
4634 {
4635 nOldCharWidth = pDXArray[n];
4636 }
4637 else
4638 {
4639 if ( bManipulateDXArray )
4640 nOldCharWidth = nNewPortionWidth - pTextPortion->GetExtraInfos()->nPortionOffsetX;
4641 else
4642 nOldCharWidth = pTextPortion->GetExtraInfos()->nOrgWidth;
4643 }
4644 nOldCharWidth -= ( n ? pDXArray[n-1] : 0 );
4645
4646 long nCompress = 0;
4647
4648 if ( bCompressPunctuation )
4649 {
4650 // pTextPortion->GetExtraInfos()->nComressionWeight += 5;
4651 nCompress = nOldCharWidth / 2;
4652 }
4653 else // Kana
4654 {
4655 // pTextPortion->GetExtraInfos()->nComressionWeight += 1;
4656 nCompress = nOldCharWidth / 10;
4657 }
4658
4659 if ( n100thPercentFromMax != 10000 )
4660 {
4661 nCompress *= n100thPercentFromMax;
4662 nCompress /= 10000;
4663 }
4664
4665 if ( nCompress )
4666 {
4667 bCompressed = sal_True;
4668 nNewPortionWidth -= nCompress;
4669 pTextPortion->GetExtraInfos()->bCompressed = sal_True;
4670
4671
4672 // Special handling for rightpunctuation: For the 'compression' we must
4673 // start th eoutput before the normal char position....
4674 if ( bManipulateDXArray && ( pTextPortion->GetLen() > 1 ) )
4675 {
4676 if ( !pTextPortion->GetExtraInfos()->pOrgDXArray )
4677 pTextPortion->GetExtraInfos()->SaveOrgDXArray( pDXArray, pTextPortion->GetLen()-1 );
4678
4679 if ( nType == CHAR_PUNCTUATIONRIGHT )
4680 {
4681 // If it's the first char, I must handle it in Paint()...
4682 if ( n )
4683 {
4684 // -1: No entry for the last character
4685 for ( sal_uInt16 i = n-1; i < (nPortionLen-1); i++ )
4686 pDXArray[i] -= nCompress;
4687 }
4688 else
4689 {
4690 pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation = sal_True;
4691 pTextPortion->GetExtraInfos()->nPortionOffsetX = -nCompress;
4692 }
4693 }
4694 else
4695 {
4696 // -1: No entry for the last character
4697 for ( sal_uInt16 i = n; i < (nPortionLen-1); i++ )
4698 pDXArray[i] -= nCompress;
4699 }
4700 }
4701 }
4702 }
4703 }
4704
4705 if ( bCompressed && ( n100thPercentFromMax == 10000 ) )
4706 pTextPortion->GetExtraInfos()->nWidthFullCompression = nNewPortionWidth;
4707
4708 pTextPortion->GetSize().Width() = nNewPortionWidth;
4709
4710 if ( pTextPortion->GetExtraInfos() && ( n100thPercentFromMax != 10000 ) )
4711 {
4712 // Maybe rounding errors in nNewPortionWidth, assure that width not bigger than expected
4713 long nShrink = pTextPortion->GetExtraInfos()->nOrgWidth - pTextPortion->GetExtraInfos()->nWidthFullCompression;
4714 nShrink *= n100thPercentFromMax;
4715 nShrink /= 10000;
4716 long nNewWidth = pTextPortion->GetExtraInfos()->nOrgWidth - nShrink;
4717 if ( nNewWidth < pTextPortion->GetSize().Width() )
4718 pTextPortion->GetSize().Width() = nNewWidth;
4719 }
4720 }
4721 return bCompressed;
4722 }
4723
4724
ImplExpandCompressedPortions(EditLine * pLine,ParaPortion * pParaPortion,long nRemainingWidth)4725 void ImpEditEngine::ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, long nRemainingWidth )
4726 {
4727 sal_Bool bFoundCompressedPortion = sal_False;
4728 long nCompressed = 0;
4729 // long nCompressWeight = 0;
4730 TextPortionList aCompressedPortions;
4731
4732 sal_uInt16 nPortion = pLine->GetEndPortion();
4733 TextPortion* pTP = pParaPortion->GetTextPortions()[ nPortion ];
4734 while ( pTP && ( pTP->GetKind() == PORTIONKIND_TEXT ) )
4735 {
4736 if ( pTP->GetExtraInfos() && pTP->GetExtraInfos()->bCompressed )
4737 {
4738 bFoundCompressedPortion = sal_True;
4739 nCompressed += pTP->GetExtraInfos()->nOrgWidth - pTP->GetSize().Width();
4740 aCompressedPortions.Insert( pTP, aCompressedPortions.Count() );
4741 }
4742 pTP = ( nPortion > pLine->GetStartPortion() ) ? pParaPortion->GetTextPortions()[ --nPortion ] : NULL;
4743 }
4744
4745 if ( bFoundCompressedPortion )
4746 {
4747 long nCompressPercent = 0;
4748 if ( nCompressed > nRemainingWidth )
4749 {
4750 nCompressPercent = nCompressed - nRemainingWidth;
4751 DBG_ASSERT( nCompressPercent < 200000, "ImplExpandCompressedPortions - Overflow!" );
4752 nCompressPercent *= 10000;
4753 nCompressPercent /= nCompressed;
4754 }
4755
4756 for ( sal_uInt16 n = 0; n < aCompressedPortions.Count(); n++ )
4757 {
4758 pTP = aCompressedPortions[n];
4759 pTP->GetExtraInfos()->bCompressed = sal_False;
4760 pTP->GetSize().Width() = pTP->GetExtraInfos()->nOrgWidth;
4761 if ( nCompressPercent )
4762 {
4763 sal_uInt16 nTxtPortion = pParaPortion->GetTextPortions().GetPos( pTP );
4764 sal_uInt16 nTxtPortionStart = pParaPortion->GetTextPortions().GetStartPos( nTxtPortion );
4765 DBG_ASSERT( nTxtPortionStart >= pLine->GetStart(), "Portion doesn't belong to the line!!!" );
4766 sal_Int32* pDXArray = const_cast< sal_Int32* >( pLine->GetCharPosArray().GetData()+( nTxtPortionStart-pLine->GetStart() ) );
4767 if ( pTP->GetExtraInfos()->pOrgDXArray )
4768 memcpy( pDXArray, pTP->GetExtraInfos()->pOrgDXArray, (pTP->GetLen()-1)*sizeof(sal_Int32) );
4769 ImplCalcAsianCompression( pParaPortion->GetNode(), pTP, nTxtPortionStart, pDXArray, (sal_uInt16)nCompressPercent, sal_True );
4770 }
4771 }
4772 }
4773
4774 aCompressedPortions.Remove( 0, aCompressedPortions.Count() );
4775 }
4776
4777 // redesigned to work with TextMarkingVector
ImplFillTextMarkingVector(const lang::Locale & rLocale,EEngineData::TextMarkingVector & rTextMarkingVector,const String & rTxt,const sal_uInt16 nIdx,const sal_uInt16 nLen) const4778 void ImpEditEngine::ImplFillTextMarkingVector(const lang::Locale& rLocale, EEngineData::TextMarkingVector& rTextMarkingVector, const String& rTxt, const sal_uInt16 nIdx, const sal_uInt16 nLen) const
4779 {
4780 // determine relevant logical text elements for the just-rendered
4781 // string of characters.
4782 Reference< i18n::XBreakIterator > _xBI(ImplGetBreakIterator());
4783
4784 if(_xBI.is())
4785 {
4786 sal_Int32 nDone;
4787 sal_Int32 nNextCellBreak(_xBI->nextCharacters(rTxt, nIdx, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
4788 i18n::Boundary nNextWordBoundary(_xBI->getWordBoundary(rTxt, nIdx, rLocale, i18n::WordType::ANY_WORD, sal_True));
4789 sal_Int32 nNextSentenceBreak(_xBI->endOfSentence(rTxt, nIdx, rLocale));
4790
4791 const sal_Int32 nEndPos(nIdx + nLen);
4792 sal_Int32 i;
4793
4794 for(i = nIdx; i < nEndPos; i++)
4795 {
4796 // create the entries for the respective break positions
4797 if(i == nNextCellBreak)
4798 {
4799 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfCaracter, i - nIdx));
4800 nNextCellBreak = _xBI->nextCharacters(rTxt, i, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
4801 }
4802 if(i == nNextWordBoundary.endPos)
4803 {
4804 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfWord, i - nIdx));
4805 nNextWordBoundary = _xBI->getWordBoundary(rTxt, i + 1, rLocale, i18n::WordType::ANY_WORD, sal_True);
4806 }
4807 if(i == nNextSentenceBreak)
4808 {
4809 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfSentence, i - nIdx));
4810 nNextSentenceBreak = _xBI->endOfSentence(rTxt, i + 1, rLocale);
4811 }
4812 }
4813 }
4814 }
4815
4816 // eof
4817