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_uInt16 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_uInt16 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_uInt16 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_uInt16 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_uInt16 nPara,sal_uInt32 nStartPosY)593 sal_Bool ImpEditEngine::CreateLines( sal_uInt16 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 usefull
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 postion...
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 everthing 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 // normaly 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_uInt16 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_uInt16 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_uInt16 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 automaticly
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_uInt16 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_uInt16 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 pOutDev->DrawStretchText( aTmpPos, pTextPortion->GetSize().Width(), aText );
3562 }
3563 }
3564 break;
3565 }
3566 nIndex = nIndex + pTextPortion->GetLen();
3567 }
3568 }
3569
3570 if ( ( nLine != nLastLine ) && !aStatus.IsOutliner() )
3571 {
3572 if ( !IsVertical() )
3573 aStartPos.Y() += nSBL;
3574 else
3575 aStartPos.X() -= nSBL;
3576 }
3577
3578 // keine sichtbaren Aktionen mehr?
3579 if ( !IsVertical() && ( aStartPos.Y() >= aClipRec.Bottom() ) )
3580 break;
3581 else if ( IsVertical() && ( aStartPos.X() <= aClipRec.Left() ) )
3582 break;
3583 }
3584
3585 if ( !aStatus.IsOutliner() )
3586 {
3587 const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3588 long nUL = GetYValue( rULItem.GetLower() );
3589 if ( !IsVertical() )
3590 aStartPos.Y() += nUL;
3591 else
3592 aStartPos.X() -= nUL;
3593 }
3594
3595 // #108052# Safer way for #i108052# and #i118881#: If for the current ParaPortion
3596 // EOP is not written, do it now. This will be safer than before. It has shown
3597 // that the reason for #i108052# was fixed/removed again, so this is a try to fix
3598 // the number of paragraphs (and counting empty ones) now independent from the
3599 // changes in EditEngine behaviour.
3600 if(!bEndOfParagraphWritten && !bPaintBullet && bStripOnly)
3601 {
3602 const Color aOverlineColor(pOutDev->GetOverlineColor());
3603 const Color aTextLineColor(pOutDev->GetTextLineColor());
3604
3605 GetEditEnginePtr()->DrawingText(
3606 aTmpPos, String(), 0, 0, 0,
3607 aTmpFont, n, nIndex, 0,
3608 0,
3609 0,
3610 false, true, false, // support for EOL/EOP TEXT comments
3611 0,
3612 aOverlineColor,
3613 aTextLineColor);
3614 }
3615 }
3616 else
3617 {
3618 if ( !IsVertical() )
3619 aStartPos.Y() += nParaHeight;
3620 else
3621 aStartPos.X() -= nParaHeight;
3622 }
3623
3624 if ( pPDFExtOutDevData )
3625 pPDFExtOutDevData->EndStructureElement();
3626
3627 // keine sichtbaren Aktionen mehr?
3628 if ( !IsVertical() && ( aStartPos.Y() > aClipRec.Bottom() ) )
3629 break;
3630 if ( IsVertical() && ( aStartPos.X() < aClipRec.Left() ) )
3631 break;
3632 }
3633 if ( aStatus.DoRestoreFont() )
3634 pOutDev->SetFont( aOldFont );
3635 }
3636
Paint(ImpEditView * pView,const Rectangle & rRec,OutputDevice * pTargetDevice,sal_Bool bUseVirtDev)3637 void ImpEditEngine::Paint( ImpEditView* pView, const Rectangle& rRec, OutputDevice* pTargetDevice, sal_Bool bUseVirtDev )
3638 {
3639 DBG_ASSERT( pView, "Keine View - Kein Paint!" );
3640 DBG_CHKOBJ( GetEditEnginePtr(), EditEngine, 0 );
3641
3642 if ( !GetUpdateMode() || IsInUndo() )
3643 return;
3644
3645 // Schnittmenge aus Paintbereich und OutputArea.
3646 Rectangle aClipRec( pView->GetOutputArea() );
3647 aClipRec.Intersection( rRec );
3648
3649 OutputDevice* pTarget = pTargetDevice ? pTargetDevice : pView->GetWindow();
3650
3651 if ( bUseVirtDev )
3652 {
3653 Rectangle aClipRecPixel( pTarget->LogicToPixel( aClipRec ) );
3654 if ( !IsVertical() )
3655 {
3656 // etwas mehr, falls abgerundet!
3657 aClipRecPixel.Right() += 1;
3658 aClipRecPixel.Bottom() += 1;
3659 }
3660 else
3661 {
3662 aClipRecPixel.Left() -= 1;
3663 aClipRecPixel.Bottom() += 1;
3664 }
3665
3666 // Wenn aClipRecPixel > XXXX, dann invalidieren ?!
3667
3668 VirtualDevice* pVDev = GetVirtualDevice( pTarget->GetMapMode(), pTarget->GetDrawMode() );
3669 pVDev->SetDigitLanguage( GetRefDevice()->GetDigitLanguage() );
3670
3671 {
3672 Color aBackgroundColor( pView->GetBackgroundColor() );
3673 // #i47161# Check if text is visible on background
3674 SvxFont aTmpFont;
3675 ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
3676 SeekCursor( pNode, 1, aTmpFont );
3677 Color aFontColor( aTmpFont.GetColor() );
3678 if( (aFontColor == COL_AUTO) || IsForceAutoColor() )
3679 aFontColor = GetAutoColor();
3680
3681 // #i69346# check for reverse color of input method attribute
3682 if( mpIMEInfos && (mpIMEInfos->aPos.GetNode() == pNode &&
3683 mpIMEInfos->pAttribs))
3684 {
3685 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ 0 ];
3686 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
3687 {
3688 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
3689 aFontColor = rStyleSettings.GetHighlightColor() ;
3690 }
3691 }
3692
3693 sal_uInt8 nColorDiff = aFontColor.GetColorError( aBackgroundColor );
3694 if( nColorDiff < 8 )
3695 aBackgroundColor = aFontColor.IsDark() ? COL_WHITE : COL_BLACK;
3696 pVDev->SetBackground( aBackgroundColor );
3697 }
3698
3699 sal_Bool bVDevValid = sal_True;
3700 Size aOutSz( pVDev->GetOutputSizePixel() );
3701 if ( ( aOutSz.Width() < aClipRecPixel.GetWidth() ) ||
3702 ( aOutSz.Height() < aClipRecPixel.GetHeight() ) )
3703 {
3704 bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3705 }
3706 else
3707 {
3708 // Das VirtDev kann bei einem Resize sehr gross werden =>
3709 // irgendwann mal kleiner machen!
3710 if ( ( aOutSz.Height() > ( aClipRecPixel.GetHeight() + RESDIFF ) ) ||
3711 ( aOutSz.Width() > ( aClipRecPixel.GetWidth() + RESDIFF ) ) )
3712 {
3713 bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3714 }
3715 else
3716 {
3717 pVDev->Erase();
3718 }
3719 }
3720 DBG_ASSERT( bVDevValid, "VirtualDevice failed to be enlarged!" );
3721 if ( !bVDevValid )
3722 {
3723 Paint( pView, rRec, NULL /* without VirtualDevice */ );
3724 return;
3725 }
3726
3727 // PaintRect fuer VDev nicht mit alignter Groesse,
3728 // da sonst die Zeile darunter auch ausgegeben werden muss:
3729 Rectangle aTmpRec( Point( 0, 0 ), aClipRec.GetSize() );
3730
3731 aClipRec = pTarget->PixelToLogic( aClipRecPixel );
3732 Point aStartPos;
3733 if ( !IsVertical() )
3734 {
3735 aStartPos = aClipRec.TopLeft();
3736 aStartPos = pView->GetDocPos( aStartPos );
3737 aStartPos.X() *= (-1);
3738 aStartPos.Y() *= (-1);
3739 }
3740 else
3741 {
3742 aStartPos = aClipRec.TopRight();
3743 Point aDocPos( pView->GetDocPos( aStartPos ) );
3744 aStartPos.X() = aClipRec.GetSize().Width() + aDocPos.Y();
3745 aStartPos.Y() = -aDocPos.X();
3746 }
3747
3748 Paint( pVDev, aTmpRec, aStartPos );
3749
3750 sal_Bool bClipRegion = sal_False;
3751 Region aOldRegion;
3752 MapMode aOldMapMode;
3753 if ( GetTextRanger() )
3754 {
3755 // Some problems here with push/pop, why?!
3756 // pTarget->Push( PUSH_CLIPREGION|PUSH_MAPMODE );
3757 bClipRegion = pTarget->IsClipRegion();
3758 aOldRegion = pTarget->GetClipRegion();
3759 // Wie bekomme ich das Polygon an die richtige Stelle????
3760 // Das Polygon bezieht sich auf die View, nicht auf das Window
3761 // => Origin umsetzen...
3762 aOldMapMode = pTarget->GetMapMode();
3763 Point aOrigin = aOldMapMode.GetOrigin();
3764 Point aViewPos = pView->GetOutputArea().TopLeft();
3765 aOrigin.Move( aViewPos.X(), aViewPos.Y() );
3766 aClipRec.Move( -aViewPos.X(), -aViewPos.Y() );
3767 MapMode aNewMapMode( aOldMapMode );
3768 aNewMapMode.SetOrigin( aOrigin );
3769 pTarget->SetMapMode( aNewMapMode );
3770 pTarget->SetClipRegion( Region( GetTextRanger()->GetPolyPolygon() ) );
3771 }
3772
3773 pTarget->DrawOutDev( aClipRec.TopLeft(), aClipRec.GetSize(),
3774 Point(0,0), aClipRec.GetSize(), *pVDev );
3775
3776 if ( GetTextRanger() )
3777 {
3778 // pTarget->Pop();
3779 if ( bClipRegion )
3780 pTarget->SetClipRegion( aOldRegion );
3781 else
3782 pTarget->SetClipRegion();
3783 pTarget->SetMapMode( aOldMapMode );
3784 }
3785
3786
3787 pView->DrawSelection(pView->GetEditSelection(), 0, pTarget);
3788 }
3789 else
3790 {
3791 Point aStartPos;
3792 if ( !IsVertical() )
3793 {
3794 aStartPos = pView->GetOutputArea().TopLeft();
3795 aStartPos.X() -= pView->GetVisDocLeft();
3796 aStartPos.Y() -= pView->GetVisDocTop();
3797 }
3798 else
3799 {
3800 aStartPos = pView->GetOutputArea().TopRight();
3801 aStartPos.X() += pView->GetVisDocTop();
3802 aStartPos.Y() -= pView->GetVisDocLeft();
3803 }
3804
3805 // Wenn Doc-Breite < OutputArea,Width, nicht umgebrochene Felder,
3806 // stehen die Felder sonst �ber, wenn > Zeile.
3807 // ( Oben nicht, da dort bereits Doc-Breite von Formatierung mit drin )
3808 if ( !IsVertical() && ( pView->GetOutputArea().GetWidth() > GetPaperSize().Width() ) )
3809 {
3810 long nMaxX = pView->GetOutputArea().Left() + GetPaperSize().Width();
3811 if ( aClipRec.Left() > nMaxX )
3812 return;
3813 if ( aClipRec.Right() > nMaxX )
3814 aClipRec.Right() = nMaxX;
3815 }
3816
3817 sal_Bool bClipRegion = pTarget->IsClipRegion();
3818 Region aOldRegion = pTarget->GetClipRegion();
3819 pTarget->IntersectClipRegion( aClipRec );
3820
3821 Paint( pTarget, aClipRec, aStartPos );
3822
3823 if ( bClipRegion )
3824 pTarget->SetClipRegion( aOldRegion );
3825 else
3826 pTarget->SetClipRegion();
3827
3828 pView->DrawSelection(pView->GetEditSelection(), 0, pTarget);
3829 }
3830
3831 }
3832
InsertContent(ContentNode * pNode,sal_uInt16 nPos)3833 void ImpEditEngine::InsertContent( ContentNode* pNode, sal_uInt16 nPos )
3834 {
3835 DBG_ASSERT( pNode, "NULL-Poointer in InsertContent! " );
3836 DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
3837 ParaPortion* pNew = new ParaPortion( pNode );
3838 GetParaPortions().Insert( pNew, nPos );
3839 aEditDoc.Insert( pNode, nPos );
3840 if ( IsCallParaInsertedOrDeleted() )
3841 GetEditEnginePtr()->ParagraphInserted( nPos );
3842 }
3843
SplitContent(sal_uInt16 nNode,sal_uInt16 nSepPos)3844 EditPaM ImpEditEngine::SplitContent( sal_uInt16 nNode, sal_uInt16 nSepPos )
3845 {
3846 ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
3847 DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
3848 DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
3849 DBG_ASSERT( nSepPos <= pNode->Len(), "Index im Wald: SplitContent" );
3850 EditPaM aPaM( pNode, nSepPos );
3851 return ImpInsertParaBreak( aPaM );
3852 }
3853
ConnectContents(sal_uInt16 nLeftNode,sal_Bool bBackward)3854 EditPaM ImpEditEngine::ConnectContents( sal_uInt16 nLeftNode, sal_Bool bBackward )
3855 {
3856 ContentNode* pLeftNode = aEditDoc.SaveGetObject( nLeftNode );
3857 ContentNode* pRightNode = aEditDoc.SaveGetObject( nLeftNode+1 );
3858 DBG_ASSERT( pLeftNode, "Ungueltiger linker Node in ConnectContents" );
3859 DBG_ASSERT( pRightNode, "Ungueltiger rechter Node in ConnectContents" );
3860 DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
3861 return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward );
3862 }
3863
SetUpdateModeForAcc(sal_Bool bUp)3864 void ImpEditEngine::SetUpdateModeForAcc( sal_Bool bUp)
3865 {
3866 bUpdateForAcc = bUp;
3867 }
3868
GetUpdateModeForAcc()3869 sal_Bool ImpEditEngine::GetUpdateModeForAcc()
3870 {
3871 return bUpdateForAcc;
3872 }
3873
SetUpdateMode(sal_Bool bUp,EditView * pCurView,sal_Bool bForceUpdate)3874 void ImpEditEngine::SetUpdateMode( sal_Bool bUp, EditView* pCurView, sal_Bool bForceUpdate )
3875 {
3876 sal_Bool bChanged = ( GetUpdateMode() != bUp );
3877
3878 // Beim Umschalten von sal_True auf sal_False waren alle Selektionen sichtbar,
3879 // => Wegmalen
3880 // Umgekehrt waren alle unsichtbar => malen
3881
3882 // DrawAllSelections(); sieht im Outliner schlecht aus !
3883 // EditView* pView = aEditViewList.First();
3884 // while ( pView )
3885 // {
3886 // DBG_CHKOBJ( pView, EditView, 0 );
3887 // pView->pImpEditView->DrawSelection();
3888 // pView = aEditViewList.Next();
3889 // }
3890
3891 // Wenn !bFormatted, also z.B. nach SetText, braucht bei UpdateMode sal_True
3892 // nicht sofort formatiert werden, weil warscheinlich noch Text kommt.
3893 // Spaetestens bei einem Paint / CalcTextWidth wird formatiert.
3894
3895 bUpdate = bUp;
3896 if ( bUpdate && ( bChanged || bForceUpdate ) )
3897 FormatAndUpdate( pCurView );
3898 }
3899
ShowParagraph(sal_uInt16 nParagraph,sal_Bool bShow)3900 void ImpEditEngine::ShowParagraph( sal_uInt16 nParagraph, sal_Bool bShow )
3901 {
3902 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3903 DBG_ASSERT( pPPortion, "ShowParagraph: Absatz existiert nicht!" );
3904 if ( pPPortion && ( pPPortion->IsVisible() != bShow ) )
3905 {
3906 pPPortion->SetVisible( bShow );
3907
3908 if ( !bShow )
3909 {
3910 // Als deleted kenzeichnen, damit keine Selektion auf diesem
3911 // Absatz beginnt oder endet...
3912 DeletedNodeInfo* pDelInfo = new DeletedNodeInfo( (sal_uIntPtr)pPPortion->GetNode(), nParagraph );
3913 aDeletedNodes.Insert( pDelInfo, aDeletedNodes.Count() );
3914 UpdateSelections();
3915 // Dann kriege ich den unteren Bereich nicht invalidiert,
3916 // wenn UpdateMode = sal_False!
3917 // Wenn doch, dann vor SetVisible auf sal_False merken!
3918 // nCurTextHeight -= pPPortion->GetHeight();
3919 }
3920
3921 if ( bShow && ( pPPortion->IsInvalid() || !pPPortion->nHeight ) )
3922 {
3923 if ( !GetTextRanger() )
3924 {
3925 if ( pPPortion->IsInvalid() )
3926 {
3927 Font aOldFont( GetRefDevice()->GetFont() );
3928 CreateLines( nParagraph, 0 ); // 0: Kein TextRanger
3929 if ( aStatus.DoRestoreFont() )
3930 GetRefDevice()->SetFont( aOldFont );
3931 }
3932 else
3933 {
3934 CalcHeight( pPPortion );
3935 }
3936 nCurTextHeight += pPPortion->GetHeight();
3937 }
3938 else
3939 {
3940 nCurTextHeight = 0x7fffffff;
3941 }
3942 }
3943
3944 pPPortion->SetMustRepaint( sal_True );
3945 if ( GetUpdateMode() && !IsInUndo() && !GetTextRanger() )
3946 {
3947 aInvalidRec = Rectangle( Point( 0, GetParaPortions().GetYOffset( pPPortion ) ),
3948 Point( GetPaperSize().Width(), nCurTextHeight ) );
3949 UpdateViews( GetActiveView() );
3950 }
3951 }
3952 }
3953
IsParagraphVisible(sal_uInt16 nParagraph)3954 sal_Bool ImpEditEngine::IsParagraphVisible( sal_uInt16 nParagraph )
3955 {
3956 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3957 DBG_ASSERT( pPPortion, "IsParagraphVisible: Absatz existiert nicht!" );
3958 if ( pPPortion )
3959 return pPPortion->IsVisible();
3960 return sal_False;
3961 }
3962
MoveParagraphs(Range aOldPositions,sal_uInt16 nNewPos,EditView * pCurView)3963 EditSelection ImpEditEngine::MoveParagraphs( Range aOldPositions, sal_uInt16 nNewPos, EditView* pCurView )
3964 {
3965 DBG_ASSERT( GetParaPortions().Count() != 0, "Keine Absaetze gefunden: MoveParagraphs" );
3966 if ( GetParaPortions().Count() == 0 )
3967 return EditSelection();
3968 aOldPositions.Justify();
3969
3970 EditSelection aSel( ImpMoveParagraphs( aOldPositions, nNewPos ) );
3971
3972 if ( nNewPos >= GetParaPortions().Count() )
3973 nNewPos = GetParaPortions().Count() - 1;
3974
3975 // Dort, wo der Absatz eingefuegt wurde, muss richtig gepainted werden:
3976 // Dort, wo der Absatz entfernt wurde, muss richtig gepainted werden:
3977 // ( Und dazwischen entsprechend auch...)
3978 if ( pCurView && ( GetUpdateMode() == sal_True ) )
3979 {
3980 // in diesem Fall kann ich direkt neu malen, ohne die
3981 // Portions zu Invalidieren.
3982 sal_uInt16 nFirstPortion = Min( (sal_uInt16)aOldPositions.Min(), nNewPos );
3983 sal_uInt16 nLastPortion = Max( (sal_uInt16)aOldPositions.Max(), nNewPos );
3984
3985 ParaPortion* pUpperPortion = GetParaPortions().SaveGetObject( nFirstPortion );
3986 ParaPortion* pLowerPortion = GetParaPortions().SaveGetObject( nLastPortion );
3987
3988 aInvalidRec = Rectangle(); // leermachen
3989 aInvalidRec.Left() = 0;
3990 aInvalidRec.Right() = aPaperSize.Width();
3991 aInvalidRec.Top() = GetParaPortions().GetYOffset( pUpperPortion );
3992 aInvalidRec.Bottom() = GetParaPortions().GetYOffset( pLowerPortion ) + pLowerPortion->GetHeight();
3993
3994 UpdateViews( pCurView );
3995 }
3996 else
3997 {
3998 // aber der oberen ungueltigen Position neu painten...
3999 sal_uInt16 nFirstInvPara = Min( (sal_uInt16)aOldPositions.Min(), nNewPos );
4000 InvalidateFromParagraph( nFirstInvPara );
4001 }
4002 return aSel;
4003 }
4004
InvalidateFromParagraph(sal_uInt16 nFirstInvPara)4005 void ImpEditEngine::InvalidateFromParagraph( sal_uInt16 nFirstInvPara )
4006 {
4007 // Es werden nicht die folgenden Absaetze invalidiert,
4008 // da ResetHeight() => Groessenanderung => alles folgende wird
4009 // sowieso neu ausgegeben.
4010 ParaPortion* pTmpPortion;
4011 if ( nFirstInvPara != 0 )
4012 {
4013 pTmpPortion = GetParaPortions().GetObject( nFirstInvPara-1 );
4014 pTmpPortion->MarkInvalid( pTmpPortion->GetNode()->Len(), 0 );
4015 }
4016 else
4017 {
4018 pTmpPortion = GetParaPortions().GetObject( 0 );
4019 pTmpPortion->MarkSelectionInvalid( 0, pTmpPortion->GetNode()->Len() );
4020 }
4021 pTmpPortion->ResetHeight();
4022 }
4023
IMPL_LINK_INLINE_START(ImpEditEngine,StatusTimerHdl,Timer *,EMPTYARG)4024 IMPL_LINK_INLINE_START( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
4025 {
4026 CallStatusHdl();
4027 return 0;
4028 }
IMPL_LINK_INLINE_END(ImpEditEngine,StatusTimerHdl,Timer *,EMPTYARG)4029 IMPL_LINK_INLINE_END( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
4030
4031 void ImpEditEngine::CallStatusHdl()
4032 {
4033 if ( aStatusHdlLink.IsSet() && aStatus.GetStatusWord() )
4034 {
4035 // Der Status muss vor Call zurueckgesetzt werden,
4036 // da im Hdl evtl. weitere Fags gesetzt werden...
4037 EditStatus aTmpStatus( aStatus );
4038 aStatus.Clear();
4039 aStatusHdlLink.Call( &aTmpStatus );
4040 aStatusTimer.Stop(); // Falls von Hand gerufen...
4041 }
4042 }
4043
GetPrevVisNode(ContentNode * pCurNode)4044 ContentNode* ImpEditEngine::GetPrevVisNode( ContentNode* pCurNode )
4045 {
4046 ParaPortion* pPortion = FindParaPortion( pCurNode );
4047 DBG_ASSERT( pPortion, "GetPrevVisibleNode: Keine passende Portion!" );
4048 pPortion = GetPrevVisPortion( pPortion );
4049 if ( pPortion )
4050 return pPortion->GetNode();
4051 return 0;
4052 }
4053
GetNextVisNode(ContentNode * pCurNode)4054 ContentNode* ImpEditEngine::GetNextVisNode( ContentNode* pCurNode )
4055 {
4056 ParaPortion* pPortion = FindParaPortion( pCurNode );
4057 DBG_ASSERT( pPortion, "GetNextVisibleNode: Keine passende Portion!" );
4058 pPortion = GetNextVisPortion( pPortion );
4059 if ( pPortion )
4060 return pPortion->GetNode();
4061 return 0;
4062 }
4063
GetPrevVisPortion(ParaPortion * pCurPortion)4064 ParaPortion* ImpEditEngine::GetPrevVisPortion( ParaPortion* pCurPortion )
4065 {
4066 sal_uInt16 nPara = GetParaPortions().GetPos( pCurPortion );
4067 DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisPortion" );
4068 ParaPortion* pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4069 while ( pPortion && !pPortion->IsVisible() )
4070 pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4071
4072 return pPortion;
4073 }
4074
GetNextVisPortion(ParaPortion * pCurPortion)4075 ParaPortion* ImpEditEngine::GetNextVisPortion( ParaPortion* pCurPortion )
4076 {
4077 sal_uInt16 nPara = GetParaPortions().GetPos( pCurPortion );
4078 DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisNode" );
4079 ParaPortion* pPortion = GetParaPortions().SaveGetObject( ++nPara );
4080 while ( pPortion && !pPortion->IsVisible() )
4081 pPortion = GetParaPortions().SaveGetObject( ++nPara );
4082
4083 return pPortion;
4084 }
4085
InsertParagraph(sal_uInt16 nPara)4086 EditPaM ImpEditEngine::InsertParagraph( sal_uInt16 nPara )
4087 {
4088 EditPaM aPaM;
4089 if ( nPara != 0 )
4090 {
4091 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara-1 );
4092 if ( !pNode )
4093 pNode = GetEditDoc().SaveGetObject( GetEditDoc().Count() - 1 );
4094 DBG_ASSERT( pNode, "Kein einziger Absatz in InsertParagraph ?" );
4095 aPaM = EditPaM( pNode, pNode->Len() );
4096 }
4097 else
4098 {
4099 ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
4100 aPaM = EditPaM( pNode, 0 );
4101 }
4102
4103 return ImpInsertParaBreak( aPaM );
4104 }
4105
SelectParagraph(sal_uInt16 nPara)4106 EditSelection* ImpEditEngine::SelectParagraph( sal_uInt16 nPara )
4107 {
4108 EditSelection* pSel = 0;
4109 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
4110 DBG_ASSERTWARNING( pNode, "Absatz existiert nicht: SelectParagraph" );
4111 if ( pNode )
4112 pSel = new EditSelection( EditPaM( pNode, 0 ), EditPaM( pNode, pNode->Len() ) );
4113
4114 return pSel;
4115 }
4116
FormatAndUpdate(EditView * pCurView)4117 void ImpEditEngine::FormatAndUpdate( EditView* pCurView )
4118 {
4119 if ( bDowning )
4120 return ;
4121
4122 if ( IsInUndo() )
4123 IdleFormatAndUpdate( pCurView );
4124 else
4125 {
4126 FormatDoc();
4127 UpdateViews( pCurView );
4128 }
4129 }
4130
SetFlatMode(sal_Bool bFlat)4131 void ImpEditEngine::SetFlatMode( sal_Bool bFlat )
4132 {
4133 if ( bFlat != aStatus.UseCharAttribs() )
4134 return;
4135
4136 if ( !bFlat )
4137 aStatus.TurnOnFlags( EE_CNTRL_USECHARATTRIBS );
4138 else
4139 aStatus.TurnOffFlags( EE_CNTRL_USECHARATTRIBS );
4140
4141 aEditDoc.CreateDefFont( !bFlat );
4142
4143 FormatFullDoc();
4144 UpdateViews( (EditView*) 0);
4145 if ( pActiveView )
4146 pActiveView->ShowCursor();
4147 }
4148
SetCharStretching(sal_uInt16 nX,sal_uInt16 nY)4149 void ImpEditEngine::SetCharStretching( sal_uInt16 nX, sal_uInt16 nY )
4150 {
4151 if ( !IsVertical() )
4152 {
4153 nStretchX = nX;
4154 nStretchY = nY;
4155 }
4156 else
4157 {
4158 nStretchX = nY;
4159 nStretchY = nX;
4160 }
4161
4162 if ( aStatus.DoStretch() )
4163 {
4164 FormatFullDoc();
4165 UpdateViews( GetActiveView() );
4166 }
4167 }
4168
DoStretchChars(sal_uInt16 nX,sal_uInt16 nY)4169 void ImpEditEngine::DoStretchChars( sal_uInt16 nX, sal_uInt16 nY )
4170 {
4171 UndoActionStart( EDITUNDO_STRETCH );
4172 sal_uInt16 nParas = GetEditDoc().Count();
4173 for ( sal_uInt16 nPara = 0; nPara < nParas; nPara++ )
4174 {
4175 ContentNode* pNode = GetEditDoc()[nPara];
4176 SfxItemSet aTmpSet( pNode->GetContentAttribs().GetItems() );
4177
4178 if ( nX != 100 )
4179 {
4180 // Fontbreite
4181 SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*) pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH ).Clone();
4182 sal_uInt32 nProp = pNewWidth->GetValue(); // sal_uInt32, kann temporaer gross werden
4183 nProp *= nX;
4184 nProp /= 100;
4185 pNewWidth->SetValue( (sal_uInt16)nProp );
4186 aTmpSet.Put( *pNewWidth );
4187 delete pNewWidth;
4188
4189 // Kerning:
4190 const SvxKerningItem& rKerningItem =
4191 (const SvxKerningItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_KERNING );
4192 SvxKerningItem* pNewKerning = (SvxKerningItem*)rKerningItem.Clone();
4193 long nKerning = pNewKerning->GetValue();
4194 if ( nKerning > 0 )
4195 {
4196 nKerning *= nX;
4197 nKerning /= 100;
4198 }
4199 else if ( nKerning < 0 )
4200 {
4201 // Bei Negativen Werten:
4202 // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
4203 nKerning *= 100;
4204 nKerning /= nX;
4205 }
4206 pNewKerning->SetValue( (short)nKerning );
4207 aTmpSet.Put( *pNewKerning);
4208 delete pNewKerning;
4209 }
4210 else
4211 aTmpSet.ClearItem( EE_CHAR_FONTWIDTH );
4212
4213 if ( nY != 100 )
4214 {
4215 // Fonthoehe
4216 for ( int nItem = 0; nItem < 3; nItem++ )
4217 {
4218 sal_uInt16 nItemId = EE_CHAR_FONTHEIGHT;
4219 if ( nItem == 1 )
4220 nItemId = EE_CHAR_FONTHEIGHT_CJK;
4221 else if ( nItem == 2 )
4222 nItemId = EE_CHAR_FONTHEIGHT_CTL;
4223
4224 const SvxFontHeightItem& rHeightItem =
4225 (const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( nItemId );
4226 SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)rHeightItem.Clone();
4227 sal_uInt32 nHeight = pNewHeight->GetHeight();
4228 nHeight *= nY;
4229 nHeight /= 100;
4230 pNewHeight->SetHeightValue( nHeight );
4231 aTmpSet.Put( *pNewHeight );
4232 delete pNewHeight;
4233 }
4234
4235 // Absatzabstaende
4236 const SvxULSpaceItem& rULSpaceItem =
4237 (const SvxULSpaceItem&)pNode->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4238 SvxULSpaceItem* pNewUL = (SvxULSpaceItem*)rULSpaceItem.Clone();
4239 sal_uInt32 nUpper = pNewUL->GetUpper();
4240 nUpper *= nY;
4241 nUpper /= 100;
4242 pNewUL->SetUpper( (sal_uInt16)nUpper );
4243 sal_uInt32 nLower = pNewUL->GetLower();
4244 nLower *= nY;
4245 nLower /= 100;
4246 pNewUL->SetLower( (sal_uInt16)nLower );
4247 aTmpSet.Put( *pNewUL );
4248 delete pNewUL;
4249 }
4250 else
4251 aTmpSet.ClearItem( EE_CHAR_FONTHEIGHT );
4252
4253 SetParaAttribs( nPara, aTmpSet );
4254
4255 // harte Attribute:
4256 sal_uInt16 nLastEnd = 0; // damit nach entfernen und neu nicht nochmal
4257 CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
4258 sal_uInt16 nAttribs = rAttribs.Count();
4259 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
4260 {
4261 EditCharAttrib* pAttr = rAttribs[nAttr];
4262 if ( pAttr->GetStart() >= nLastEnd )
4263 {
4264 sal_uInt16 nWhich = pAttr->Which();
4265 SfxPoolItem* pNew = 0;
4266 if ( nWhich == EE_CHAR_FONTHEIGHT )
4267 {
4268 SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)pAttr->GetItem()->Clone();
4269 sal_uInt32 nHeight = pNewHeight->GetHeight();
4270 nHeight *= nY;
4271 nHeight /= 100;
4272 pNewHeight->SetHeightValue( nHeight );
4273 pNew = pNewHeight;
4274 }
4275 else if ( nWhich == EE_CHAR_FONTWIDTH )
4276 {
4277 SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*)pAttr->GetItem()->Clone();
4278 sal_uInt32 nProp = pNewWidth->GetValue();
4279 nProp *= nX;
4280 nProp /= 100;
4281 pNewWidth->SetValue( (sal_uInt16)nProp );
4282 pNew = pNewWidth;
4283 }
4284 else if ( nWhich == EE_CHAR_KERNING )
4285 {
4286 SvxKerningItem* pNewKerning = (SvxKerningItem*)pAttr->GetItem()->Clone();
4287 long nKerning = pNewKerning->GetValue();
4288 if ( nKerning > 0 )
4289 {
4290 nKerning *= nX;
4291 nKerning /= 100;
4292 }
4293 else if ( nKerning < 0 )
4294 {
4295 // Bei Negativen Werten:
4296 // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
4297 nKerning *= 100;
4298 nKerning /= nX;
4299 }
4300 pNewKerning->SetValue( (short)nKerning );
4301 pNew = pNewKerning;
4302 }
4303 if ( pNew )
4304 {
4305 SfxItemSet _aTmpSet( GetEmptyItemSet() );
4306 _aTmpSet.Put( *pNew );
4307 SetAttribs( EditSelection( EditPaM( pNode, pAttr->GetStart() ),
4308 EditPaM( pNode, pAttr->GetEnd() ) ), _aTmpSet );
4309
4310 nLastEnd = pAttr->GetEnd();
4311 delete pNew;
4312 }
4313 }
4314 }
4315 }
4316 UndoActionEnd( EDITUNDO_STRETCH );
4317 }
4318
GetNumberFormat(const ContentNode * pNode) const4319 const SvxNumberFormat* ImpEditEngine::GetNumberFormat( const ContentNode *pNode ) const
4320 {
4321 const SvxNumberFormat *pRes = 0;
4322
4323 if (pNode)
4324 {
4325 // get index of paragraph
4326 sal_uInt16 nPara = GetEditDoc().GetPos( const_cast< ContentNode * >(pNode) );
4327 DBG_ASSERT( nPara < USHRT_MAX, "node not found in array" );
4328 if (nPara < USHRT_MAX)
4329 {
4330 // the called function may be overloaded by an OutlinerEditEng object to provide
4331 // access to the SvxNumberFormat of the Outliner.
4332 // The EditEngine implementation will just return 0.
4333 pRes = pEditEngine->GetNumberFormat( nPara );
4334 }
4335 }
4336
4337 return pRes;
4338 }
4339
GetSpaceBeforeAndMinLabelWidth(const ContentNode * pNode,sal_Int32 * pnSpaceBefore,sal_Int32 * pnMinLabelWidth) const4340 sal_Int32 ImpEditEngine::GetSpaceBeforeAndMinLabelWidth(
4341 const ContentNode *pNode,
4342 sal_Int32 *pnSpaceBefore, sal_Int32 *pnMinLabelWidth ) const
4343 {
4344 // nSpaceBefore matches the ODF attribut text:space-before
4345 // nMinLabelWidth matches the ODF attribut text:min-label-width
4346
4347 const SvxNumberFormat *pNumFmt = GetNumberFormat( pNode );
4348
4349 // if no number format was found we have no Outliner or the numbering level
4350 // within the Outliner is -1 which means no number format should be applied.
4351 // Thus the default values to be returned are 0.
4352 sal_Int32 nSpaceBefore = 0;
4353 sal_Int32 nMinLabelWidth = 0;
4354
4355 if (pNumFmt)
4356 {
4357 nMinLabelWidth = -pNumFmt->GetFirstLineOffset();
4358 nSpaceBefore = pNumFmt->GetAbsLSpace() - nMinLabelWidth;
4359 DBG_ASSERT( nMinLabelWidth >= 0, "ImpEditEngine::GetSpaceBeforeAndMinLabelWidth: min-label-width < 0 encountered" );
4360 }
4361 if (pnSpaceBefore)
4362 *pnSpaceBefore = nSpaceBefore;
4363 if (pnMinLabelWidth)
4364 *pnMinLabelWidth = nMinLabelWidth;
4365
4366 return nSpaceBefore + nMinLabelWidth;
4367 }
4368
GetLRSpaceItem(ContentNode * pNode)4369 const SvxLRSpaceItem& ImpEditEngine::GetLRSpaceItem( ContentNode* pNode )
4370 {
4371 return (const SvxLRSpaceItem&)pNode->GetContentAttribs().GetItem( aStatus.IsOutliner() ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
4372 }
4373
4374 // Either sets the digit mode at the output device or
4375 // modifies the passed string according to the text numeral setting:
ImplInitDigitMode(OutputDevice * pOutDev,String * pString,xub_StrLen nStt,xub_StrLen nLen,LanguageType eCurLang)4376 void ImpEditEngine::ImplInitDigitMode( OutputDevice* pOutDev, String* pString, xub_StrLen nStt, xub_StrLen nLen, LanguageType eCurLang )
4377 {
4378 // #114278# Also setting up digit language from Svt options
4379 // (cannot reliably inherit the outdev's setting)
4380 if( !pCTLOptions )
4381 pCTLOptions = new SvtCTLOptions;
4382
4383 LanguageType eLang = eCurLang;
4384 const SvtCTLOptions::TextNumerals nCTLTextNumerals = pCTLOptions->GetCTLTextNumerals();
4385
4386 if ( SvtCTLOptions::NUMERALS_HINDI == nCTLTextNumerals )
4387 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4388 else if ( SvtCTLOptions::NUMERALS_ARABIC == nCTLTextNumerals )
4389 eLang = LANGUAGE_ENGLISH;
4390 else if ( SvtCTLOptions::NUMERALS_SYSTEM == nCTLTextNumerals )
4391 eLang = (LanguageType) Application::GetSettings().GetLanguage();
4392
4393 if(pOutDev)
4394 {
4395 pOutDev->SetDigitLanguage( eLang );
4396 }
4397 else if (pString)
4398 {
4399 // see sallayout.cxx in vcl
4400 int nOffset;
4401 switch( eLang & LANGUAGE_MASK_PRIMARY )
4402 {
4403 default:
4404 nOffset = 0;
4405 break;
4406 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
4407 nOffset = 0x0660 - '0'; // arabic-indic digits
4408 break;
4409 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
4410 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
4411 case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
4412 nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
4413 break;
4414 }
4415 if (nOffset)
4416 {
4417 const xub_StrLen nEnd = nStt + nLen;
4418 for( xub_StrLen nIdx = nStt; nIdx < nEnd; ++nIdx )
4419 {
4420 sal_Unicode nChar = pString->GetChar( nIdx );
4421 if( (nChar < '0') || ('9' < nChar) )
4422 continue;
4423 nChar = (sal_Unicode)(nChar + nOffset);
4424 pString->SetChar( nIdx, nChar );
4425 }
4426 }
4427 }
4428 }
4429
ImplInitLayoutMode(OutputDevice * pOutDev,sal_uInt16 nPara,sal_uInt16 nIndex)4430 void ImpEditEngine::ImplInitLayoutMode( OutputDevice* pOutDev, sal_uInt16 nPara, sal_uInt16 nIndex )
4431 {
4432 sal_Bool bCTL = sal_False;
4433 sal_uInt8 bR2L = sal_False;
4434 if ( nIndex == 0xFFFF )
4435 {
4436 bCTL = HasScriptType( nPara, i18n::ScriptType::COMPLEX );
4437 bR2L = IsRightToLeft( nPara );
4438 }
4439 else
4440 {
4441 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
4442 short nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
4443 bCTL = nScriptType == i18n::ScriptType::COMPLEX;
4444 bR2L = GetRightToLeft( nPara, nIndex + 1); // this change was discussed in issue 37190
4445 // it also works for issue 55927
4446 }
4447
4448 sal_uLong nLayoutMode = pOutDev->GetLayoutMode();
4449
4450 // We always use the left postion for DrawText()
4451 nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL);
4452
4453 if ( !bCTL && !bR2L)
4454 {
4455 // No CTL/Bidi checking neccessary
4456 nLayoutMode |= ( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4457 }
4458 else
4459 {
4460 // CTL/Bidi checking neccessary
4461 // Don't use BIDI_STRONG, VCL must do some checks.
4462 nLayoutMode &= ~( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4463
4464 if ( bR2L )
4465 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
4466 }
4467
4468 pOutDev->SetLayoutMode( nLayoutMode );
4469
4470 // #114278# Also setting up digit language from Svt options
4471 // (cannot reliably inherit the outdev's setting)
4472 LanguageType eLang;
4473
4474 if( !pCTLOptions )
4475 pCTLOptions = new SvtCTLOptions;
4476
4477 if ( SvtCTLOptions::NUMERALS_HINDI == pCTLOptions->GetCTLTextNumerals() )
4478 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4479 else if ( SvtCTLOptions::NUMERALS_ARABIC == pCTLOptions->GetCTLTextNumerals() )
4480 eLang = LANGUAGE_ENGLISH;
4481 else
4482 eLang = (LanguageType) Application::GetSettings().GetLanguage();
4483
4484 pOutDev->SetDigitLanguage( eLang );
4485 }
4486
ImplGetBreakIterator() const4487 Reference < i18n::XBreakIterator > ImpEditEngine::ImplGetBreakIterator() const
4488 {
4489 if ( !xBI.is() )
4490 {
4491 Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
4492 xBI.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
4493 }
4494 return xBI;
4495 }
4496
ImplGetInputSequenceChecker() const4497 Reference < i18n::XExtendedInputSequenceChecker > ImpEditEngine::ImplGetInputSequenceChecker() const
4498 {
4499 if ( !xISC.is() )
4500 {
4501 Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
4502 Reference < XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
4503 if ( xI.is() )
4504 {
4505 Any x = xI->queryInterface( ::getCppuType((const Reference< i18n::XExtendedInputSequenceChecker >*)0) );
4506 x >>= xISC;
4507 }
4508 }
4509 return xISC;
4510 }
4511
GetAutoColor() const4512 Color ImpEditEngine::GetAutoColor() const
4513 {
4514 Color aColor = const_cast<ImpEditEngine*>(this)->GetColorConfig().GetColorValue( svtools::FONTCOLOR ).nColor;
4515
4516 if ( GetBackgroundColor() != COL_AUTO )
4517 {
4518 if ( GetBackgroundColor().IsDark() && aColor.IsDark() )
4519 aColor = COL_WHITE;
4520 else if ( GetBackgroundColor().IsBright() && aColor.IsBright() )
4521 aColor = COL_BLACK;
4522 }
4523
4524 return aColor;
4525 }
4526
4527
ImplCalcAsianCompression(ContentNode * pNode,TextPortion * pTextPortion,sal_uInt16 nStartPos,sal_Int32 * pDXArray,sal_uInt16 n100thPercentFromMax,sal_Bool bManipulateDXArray)4528 sal_Bool ImpEditEngine::ImplCalcAsianCompression( ContentNode* pNode, TextPortion* pTextPortion, sal_uInt16 nStartPos, sal_Int32* pDXArray, sal_uInt16 n100thPercentFromMax, sal_Bool bManipulateDXArray )
4529 {
4530 DBG_ASSERT( GetAsianCompressionMode(), "ImplCalcAsianCompression - Why?" );
4531 DBG_ASSERT( pTextPortion->GetLen(), "ImplCalcAsianCompression - Empty Portion?" );
4532
4533 // Percent is 1/100 Percent...
4534
4535 if ( n100thPercentFromMax == 10000 )
4536 pTextPortion->SetExtraInfos( NULL );
4537
4538 sal_Bool bCompressed = sal_False;
4539
4540 if ( GetScriptType( EditPaM( pNode, nStartPos+1 ) ) == i18n::ScriptType::ASIAN )
4541 {
4542 long nNewPortionWidth = pTextPortion->GetSize().Width();
4543 sal_uInt16 nPortionLen = pTextPortion->GetLen();
4544 for ( sal_uInt16 n = 0; n < nPortionLen; n++ )
4545 {
4546 sal_uInt8 nType = GetCharTypeForCompression( pNode->GetChar( n+nStartPos ) );
4547
4548 sal_Bool bCompressPunctuation = ( nType == CHAR_PUNCTUATIONLEFT ) || ( nType == CHAR_PUNCTUATIONRIGHT );
4549 sal_Bool bCompressKana = ( nType == CHAR_KANA ) && ( GetAsianCompressionMode() == text::CharacterCompressionType::PUNCTUATION_AND_KANA );
4550
4551 // create Extra infos only if needed...
4552 if ( bCompressPunctuation || bCompressKana )
4553 {
4554 if ( !pTextPortion->GetExtraInfos() )
4555 {
4556 ExtraPortionInfo* pExtraInfos = new ExtraPortionInfo;
4557 pTextPortion->SetExtraInfos( pExtraInfos );
4558 pExtraInfos->nOrgWidth = pTextPortion->GetSize().Width();
4559 pExtraInfos->nAsianCompressionTypes = CHAR_NORMAL;
4560 }
4561 pTextPortion->GetExtraInfos()->nMaxCompression100thPercent = n100thPercentFromMax;
4562 pTextPortion->GetExtraInfos()->nAsianCompressionTypes |= nType;
4563 // pTextPortion->GetExtraInfos()->nCompressedChars++;
4564
4565 long nOldCharWidth;
4566 if ( (n+1) < nPortionLen )
4567 {
4568 nOldCharWidth = pDXArray[n];
4569 }
4570 else
4571 {
4572 if ( bManipulateDXArray )
4573 nOldCharWidth = nNewPortionWidth - pTextPortion->GetExtraInfos()->nPortionOffsetX;
4574 else
4575 nOldCharWidth = pTextPortion->GetExtraInfos()->nOrgWidth;
4576 }
4577 nOldCharWidth -= ( n ? pDXArray[n-1] : 0 );
4578
4579 long nCompress = 0;
4580
4581 if ( bCompressPunctuation )
4582 {
4583 // pTextPortion->GetExtraInfos()->nComressionWeight += 5;
4584 nCompress = nOldCharWidth / 2;
4585 }
4586 else // Kana
4587 {
4588 // pTextPortion->GetExtraInfos()->nComressionWeight += 1;
4589 nCompress = nOldCharWidth / 10;
4590 }
4591
4592 if ( n100thPercentFromMax != 10000 )
4593 {
4594 nCompress *= n100thPercentFromMax;
4595 nCompress /= 10000;
4596 }
4597
4598 if ( nCompress )
4599 {
4600 bCompressed = sal_True;
4601 nNewPortionWidth -= nCompress;
4602 pTextPortion->GetExtraInfos()->bCompressed = sal_True;
4603
4604
4605 // Special handling for rightpunctuation: For the 'compression' we must
4606 // start th eoutput before the normal char position....
4607 if ( bManipulateDXArray && ( pTextPortion->GetLen() > 1 ) )
4608 {
4609 if ( !pTextPortion->GetExtraInfos()->pOrgDXArray )
4610 pTextPortion->GetExtraInfos()->SaveOrgDXArray( pDXArray, pTextPortion->GetLen()-1 );
4611
4612 if ( nType == CHAR_PUNCTUATIONRIGHT )
4613 {
4614 // If it's the first char, I must handle it in Paint()...
4615 if ( n )
4616 {
4617 // -1: No entry for the last character
4618 for ( sal_uInt16 i = n-1; i < (nPortionLen-1); i++ )
4619 pDXArray[i] -= nCompress;
4620 }
4621 else
4622 {
4623 pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation = sal_True;
4624 pTextPortion->GetExtraInfos()->nPortionOffsetX = -nCompress;
4625 }
4626 }
4627 else
4628 {
4629 // -1: No entry for the last character
4630 for ( sal_uInt16 i = n; i < (nPortionLen-1); i++ )
4631 pDXArray[i] -= nCompress;
4632 }
4633 }
4634 }
4635 }
4636 }
4637
4638 if ( bCompressed && ( n100thPercentFromMax == 10000 ) )
4639 pTextPortion->GetExtraInfos()->nWidthFullCompression = nNewPortionWidth;
4640
4641 pTextPortion->GetSize().Width() = nNewPortionWidth;
4642
4643 if ( pTextPortion->GetExtraInfos() && ( n100thPercentFromMax != 10000 ) )
4644 {
4645 // Maybe rounding errors in nNewPortionWidth, assure that width not bigger than expected
4646 long nShrink = pTextPortion->GetExtraInfos()->nOrgWidth - pTextPortion->GetExtraInfos()->nWidthFullCompression;
4647 nShrink *= n100thPercentFromMax;
4648 nShrink /= 10000;
4649 long nNewWidth = pTextPortion->GetExtraInfos()->nOrgWidth - nShrink;
4650 if ( nNewWidth < pTextPortion->GetSize().Width() )
4651 pTextPortion->GetSize().Width() = nNewWidth;
4652 }
4653 }
4654 return bCompressed;
4655 }
4656
4657
ImplExpandCompressedPortions(EditLine * pLine,ParaPortion * pParaPortion,long nRemainingWidth)4658 void ImpEditEngine::ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, long nRemainingWidth )
4659 {
4660 sal_Bool bFoundCompressedPortion = sal_False;
4661 long nCompressed = 0;
4662 // long nCompressWeight = 0;
4663 TextPortionList aCompressedPortions;
4664
4665 sal_uInt16 nPortion = pLine->GetEndPortion();
4666 TextPortion* pTP = pParaPortion->GetTextPortions()[ nPortion ];
4667 while ( pTP && ( pTP->GetKind() == PORTIONKIND_TEXT ) )
4668 {
4669 if ( pTP->GetExtraInfos() && pTP->GetExtraInfos()->bCompressed )
4670 {
4671 bFoundCompressedPortion = sal_True;
4672 nCompressed += pTP->GetExtraInfos()->nOrgWidth - pTP->GetSize().Width();
4673 aCompressedPortions.Insert( pTP, aCompressedPortions.Count() );
4674 }
4675 pTP = ( nPortion > pLine->GetStartPortion() ) ? pParaPortion->GetTextPortions()[ --nPortion ] : NULL;
4676 }
4677
4678 if ( bFoundCompressedPortion )
4679 {
4680 long nCompressPercent = 0;
4681 if ( nCompressed > nRemainingWidth )
4682 {
4683 nCompressPercent = nCompressed - nRemainingWidth;
4684 DBG_ASSERT( nCompressPercent < 200000, "ImplExpandCompressedPortions - Overflow!" );
4685 nCompressPercent *= 10000;
4686 nCompressPercent /= nCompressed;
4687 }
4688
4689 for ( sal_uInt16 n = 0; n < aCompressedPortions.Count(); n++ )
4690 {
4691 pTP = aCompressedPortions[n];
4692 pTP->GetExtraInfos()->bCompressed = sal_False;
4693 pTP->GetSize().Width() = pTP->GetExtraInfos()->nOrgWidth;
4694 if ( nCompressPercent )
4695 {
4696 sal_uInt16 nTxtPortion = pParaPortion->GetTextPortions().GetPos( pTP );
4697 sal_uInt16 nTxtPortionStart = pParaPortion->GetTextPortions().GetStartPos( nTxtPortion );
4698 DBG_ASSERT( nTxtPortionStart >= pLine->GetStart(), "Portion doesn't belong to the line!!!" );
4699 sal_Int32* pDXArray = const_cast< sal_Int32* >( pLine->GetCharPosArray().GetData()+( nTxtPortionStart-pLine->GetStart() ) );
4700 if ( pTP->GetExtraInfos()->pOrgDXArray )
4701 memcpy( pDXArray, pTP->GetExtraInfos()->pOrgDXArray, (pTP->GetLen()-1)*sizeof(sal_Int32) );
4702 ImplCalcAsianCompression( pParaPortion->GetNode(), pTP, nTxtPortionStart, pDXArray, (sal_uInt16)nCompressPercent, sal_True );
4703 }
4704 }
4705 }
4706
4707 aCompressedPortions.Remove( 0, aCompressedPortions.Count() );
4708 }
4709
4710 // redesigned to work with TextMarkingVector
ImplFillTextMarkingVector(const lang::Locale & rLocale,EEngineData::TextMarkingVector & rTextMarkingVector,const String & rTxt,const sal_uInt16 nIdx,const sal_uInt16 nLen) const4711 void ImpEditEngine::ImplFillTextMarkingVector(const lang::Locale& rLocale, EEngineData::TextMarkingVector& rTextMarkingVector, const String& rTxt, const sal_uInt16 nIdx, const sal_uInt16 nLen) const
4712 {
4713 // determine relevant logical text elements for the just-rendered
4714 // string of characters.
4715 Reference< i18n::XBreakIterator > _xBI(ImplGetBreakIterator());
4716
4717 if(_xBI.is())
4718 {
4719 sal_Int32 nDone;
4720 sal_Int32 nNextCellBreak(_xBI->nextCharacters(rTxt, nIdx, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
4721 i18n::Boundary nNextWordBoundary(_xBI->getWordBoundary(rTxt, nIdx, rLocale, i18n::WordType::ANY_WORD, sal_True));
4722 sal_Int32 nNextSentenceBreak(_xBI->endOfSentence(rTxt, nIdx, rLocale));
4723
4724 const sal_Int32 nEndPos(nIdx + nLen);
4725 sal_Int32 i;
4726
4727 for(i = nIdx; i < nEndPos; i++)
4728 {
4729 // create the entries for the respective break positions
4730 if(i == nNextCellBreak)
4731 {
4732 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfCaracter, i - nIdx));
4733 nNextCellBreak = _xBI->nextCharacters(rTxt, i, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
4734 }
4735 if(i == nNextWordBoundary.endPos)
4736 {
4737 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfWord, i - nIdx));
4738 nNextWordBoundary = _xBI->getWordBoundary(rTxt, i + 1, rLocale, i18n::WordType::ANY_WORD, sal_True);
4739 }
4740 if(i == nNextSentenceBreak)
4741 {
4742 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfSentence, i - nIdx));
4743 nNextSentenceBreak = _xBI->endOfSentence(rTxt, i + 1, rLocale);
4744 }
4745 }
4746 }
4747 }
4748
4749 // eof
4750