update dev300-m58
[ooovba.git] / svx / source / editeng / impedit3.cxx
blob435721da6b6edc09151502eea4ca58606059f6b0
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: impedit3.cxx,v $
10 * $Revision: 1.124.82.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include <eeng_pch.hxx>
35 #include <vcl/metaact.hxx>
36 #include <vcl/gdimtf.hxx>
38 #define _SVSTDARR_USHORTS
39 #include <svtools/svstdarr.hxx>
41 #ifndef _WRKWIN_HXX //autogen
42 #include <vcl/wrkwin.hxx>
43 #endif
45 #include <svx/adjitem.hxx>
46 #include <svx/tstpitem.hxx>
47 #include <svx/lspcitem.hxx>
49 #include <svx/flditem.hxx>
51 #include <impedit.hxx>
52 #include <svx/editeng.hxx>
53 #include <svx/editview.hxx>
54 #include <txtrange.hxx>
55 #include <svx/cscoitem.hxx>
56 #include <svx/colritem.hxx>
57 #include <svx/udlnitem.hxx>
58 #include <svx/fhgtitem.hxx>
59 #include <svx/kernitem.hxx>
60 #include <svx/lrspitem.hxx>
61 #include <svx/ulspitem.hxx>
62 #include <fontitem.hxx>
63 #include <svx/wghtitem.hxx>
64 #include <svx/postitem.hxx>
65 #include <svx/langitem.hxx>
66 #include <svx/scriptspaceitem.hxx>
67 #include <svx/charscaleitem.hxx>
68 #include <svx/numitem.hxx>
70 #include <svtools/colorcfg.hxx>
71 #include <svtools/ctloptions.hxx>
73 #include <forbiddencharacterstable.hxx>
75 #include <unotools/localedatawrapper.hxx>
77 #include <unolingu.hxx>
79 #include <math.h>
80 #include <vcl/svapp.hxx>
81 #include <sfx2/sfxuno.hxx>
82 #include <vcl/metric.hxx>
83 #include <com/sun/star/i18n/ScriptType.hpp>
84 #include <com/sun/star/text/CharacterCompressionType.hpp>
85 #include <vcl/pdfextoutdevdata.hxx>
86 #include <i18npool/mslangid.hxx>
88 #include <comphelper/processfactory.hxx>
90 using ::rtl::OUString;
91 using namespace ::com::sun::star;
92 using namespace ::com::sun::star::uno;
93 using namespace ::com::sun::star::beans;
94 using namespace ::com::sun::star::linguistic2;
96 SV_DECL_VARARR_SORT( SortedPositions, sal_uInt32, 16, 8 )
97 SV_IMPL_VARARR_SORT( SortedPositions, sal_uInt32 );
99 #define CH_HYPH '-'
101 #define RESDIFF 10
103 #define WRONG_SHOW_MIN 5
104 #define WRONG_SHOW_SMALL 11
105 #define WRONG_SHOW_MEDIUM 15
107 struct TabInfo
109 BOOL bValid;
111 SvxTabStop aTabStop;
112 xub_StrLen nCharPos;
113 USHORT nTabPortion;
114 long nStartPosX;
115 long nTabPos;
117 TabInfo() { bValid = FALSE; }
120 Point Rotate( const Point& rPoint, short nOrientation, const Point& rOrigin )
122 double nRealOrientation = nOrientation*F_PI1800;
123 double nCos = cos( nRealOrientation );
124 double nSin = sin( nRealOrientation );
126 Point aRotatedPos;
127 Point aTranslatedPos( rPoint );
129 // Translation
130 aTranslatedPos -= rOrigin;
132 // Rotation...
133 aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
134 aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
135 aTranslatedPos = aRotatedPos;
137 // Translation...
138 aTranslatedPos += rOrigin;
139 return aTranslatedPos;
142 BYTE GetCharTypeForCompression( xub_Unicode cChar )
144 switch ( cChar )
146 case 0x3008: case 0x300A: case 0x300C: case 0x300E:
147 case 0x3010: case 0x3014: case 0x3016: case 0x3018:
148 case 0x301A: case 0x301D:
150 return CHAR_PUNCTUATIONRIGHT;
152 case 0x3001: case 0x3002: case 0x3009: case 0x300B:
153 case 0x300D: case 0x300F: case 0x3011: case 0x3015:
154 case 0x3017: case 0x3019: case 0x301B: case 0x301E:
155 case 0x301F:
157 return CHAR_PUNCTUATIONLEFT;
159 default:
161 return ( ( 0x3040 <= cChar ) && ( 0x3100 > cChar ) ) ? CHAR_KANA : CHAR_NORMAL;
166 void lcl_DrawRedLines(
167 OutputDevice* pOutDev,
168 long nFontHeight,
169 const Point& rPnt,
170 sal_uInt16 nIndex,
171 sal_uInt16 nMaxEnd,
172 const sal_Int32* pDXArray,
173 WrongList* pWrongs,
174 short nOrientation,
175 const Point& rOrigin,
176 BOOL bVertical,
177 BOOL bIsRightToLeft )
179 #ifndef SVX_LIGHT
180 // Aber nur, wenn Font nicht zu klein...
181 long nHght = pOutDev->LogicToPixel( Size( 0, nFontHeight ) ).Height();
182 if( WRONG_SHOW_MIN < nHght )
184 sal_uInt16 nStyle;
185 if( WRONG_SHOW_MEDIUM < nHght )
186 nStyle = WAVE_NORMAL;
187 else if( WRONG_SHOW_SMALL < nHght )
188 nStyle = WAVE_SMALL;
189 else
190 nStyle = WAVE_FLAT;
192 sal_uInt16 nEnd, nStart = nIndex;
193 sal_Bool bWrong = pWrongs->NextWrong( nStart, nEnd );
194 while ( bWrong )
196 if ( nStart >= nMaxEnd )
197 break;
199 if ( nStart < nIndex ) // Wurde korrigiert
200 nStart = nIndex;
201 if ( nEnd > nMaxEnd )
202 nEnd = nMaxEnd;
203 Point aPnt1( rPnt );
204 if ( bVertical && ( nStyle != WAVE_FLAT ) )
206 // VCL doesn't know that the text is vertical, and is manipulating
207 // the positions a little bit in y direction...
208 long nOnePixel = pOutDev->PixelToLogic( Size( 0, 1 ) ).Height();
209 long nCorrect = ( nStyle == WAVE_NORMAL ) ? 2*nOnePixel : nOnePixel;
210 aPnt1.Y() -= nCorrect;
211 aPnt1.X() -= nCorrect;
213 if ( nStart > nIndex )
215 if ( !bVertical )
217 // since for RTL portions rPnt is on the visual right end of the portion
218 // (i.e. at the start of the first RTL char) we need to subtract the offset
219 // for RTL portions...
220 aPnt1.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nStart - nIndex - 1 ];
222 else
223 aPnt1.Y() += pDXArray[ nStart - nIndex - 1 ];
225 Point aPnt2( rPnt );
226 DBG_ASSERT( nEnd > nIndex, "RedLine: aPnt2?" );
227 if ( !bVertical )
229 // since for RTL portions rPnt is on the visual right end of the portion
230 // (i.e. at the start of the first RTL char) we need to subtract the offset
231 // for RTL portions...
232 aPnt2.X() += (bIsRightToLeft ? -1 : 1) * pDXArray[ nEnd - nIndex - 1 ];
234 else
235 aPnt2.Y() += pDXArray[ nEnd - nIndex - 1 ];
236 if ( nOrientation )
238 aPnt1 = Rotate( aPnt1, nOrientation, rOrigin );
239 aPnt2 = Rotate( aPnt2, nOrientation, rOrigin );
242 pOutDev->DrawWaveLine( aPnt1, aPnt2, nStyle );
244 nStart = nEnd+1;
245 if ( nEnd < nMaxEnd )
246 bWrong = pWrongs->NextWrong( nStart, nEnd );
247 else
248 bWrong = sal_False;
251 #endif // !SVX_LIGHT
254 Point lcl_ImplCalcRotatedPos( Point rPos, Point rOrigin, double nSin, double nCos )
256 Point aRotatedPos;
257 // Translation...
258 Point aTranslatedPos( rPos);
259 aTranslatedPos -= rOrigin;
261 aRotatedPos.X() = (long) ( nCos*aTranslatedPos.X() + nSin*aTranslatedPos.Y() );
262 aRotatedPos.Y() = (long) - ( nSin*aTranslatedPos.X() - nCos*aTranslatedPos.Y() );
263 aTranslatedPos = aRotatedPos;
264 // Translation...
265 aTranslatedPos += rOrigin;
267 return aTranslatedPos;
270 sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh ) // For Kashidas from sw/source/core/text/porlay.txt
272 // Lam + Alef
273 return ( 0x644 == cCh && 0x627 == cNextCh ) ||
274 // Beh + Reh
275 ( 0x628 == cCh && 0x631 == cNextCh );
278 sal_Bool lcl_ConnectToPrev( xub_Unicode cCh, xub_Unicode cPrevCh ) // For Kashidas from sw/source/core/text/porlay.txt
280 // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
281 sal_Bool bRet = 0x627 != cPrevCh && 0x62F != cPrevCh && 0x630 != cPrevCh &&
282 0x631 != cPrevCh && 0x632 != cPrevCh && 0x648 != cPrevCh;
284 // check for ligatures cPrevChar + cChar
285 if ( bRet )
286 bRet = ! lcl_IsLigature( cPrevCh, cCh );
288 return bRet;
292 // ----------------------------------------------------------------------
293 // class ImpEditEngine
294 // ----------------------------------------------------------------------
295 void ImpEditEngine::UpdateViews( EditView* pCurView )
297 if ( !GetUpdateMode() || IsFormatting() || aInvalidRec.IsEmpty() )
298 return;
300 DBG_ASSERT( IsFormatted(), "UpdateViews: Doc nicht formatiert!" );
302 for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
304 EditView* pView = aEditViews[nView];
305 DBG_CHKOBJ( pView, EditView, 0 );
306 pView->HideCursor();
308 Rectangle aClipRec( aInvalidRec );
309 Rectangle aVisArea( pView->GetVisArea() );
310 aClipRec.Intersection( aVisArea );
312 if ( !aClipRec.IsEmpty() )
314 // in Fensterkoordinaten umwandeln....
315 aClipRec = pView->pImpEditView->GetWindowPos( aClipRec );
317 if ( ( pView == pCurView ) )
318 Paint( pView->pImpEditView, aClipRec, sal_True );
319 else
320 pView->GetWindow()->Invalidate( aClipRec );
324 if ( pCurView )
326 sal_Bool bGotoCursor = pCurView->pImpEditView->DoAutoScroll();
327 pCurView->ShowCursor( bGotoCursor );
330 aInvalidRec = Rectangle();
331 CallStatusHdl();
334 IMPL_LINK( ImpEditEngine, OnlineSpellHdl, Timer *, EMPTYARG )
336 if ( !Application::AnyInput( INPUT_KEYBOARD ) && GetUpdateMode() && IsFormatted() )
337 DoOnlineSpelling();
338 else
339 aOnlineSpellTimer.Start();
341 return 0;
344 IMPL_LINK_INLINE_START( ImpEditEngine, IdleFormatHdl, Timer *, EMPTYARG )
346 aIdleFormatter.ResetRestarts();
348 // #i97146# check if that view is still available
349 // else probably the idle format timer fired while we're already
350 // downing
351 EditView* pView = aIdleFormatter.GetView();
352 for( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
354 if( aEditViews[nView] == pView )
356 FormatAndUpdate( pView );
357 break;
360 return 0;
362 IMPL_LINK_INLINE_END( ImpEditEngine, IdleFormatHdl, Timer *, EMPTYARG )
364 void ImpEditEngine::CheckIdleFormatter()
366 aIdleFormatter.ForceTimeout();
367 // Falls kein Idle, aber trotzdem nicht formatiert:
368 if ( !IsFormatted() )
369 FormatDoc();
372 void ImpEditEngine::FormatFullDoc()
374 for ( sal_uInt16 nPortion = 0; nPortion < GetParaPortions().Count(); nPortion++ )
375 GetParaPortions()[nPortion]->MarkSelectionInvalid( 0, GetParaPortions()[nPortion]->GetNode()->Len() );
376 FormatDoc();
379 void ImpEditEngine::FormatDoc()
381 if ( !GetUpdateMode() || IsFormatting() )
382 return;
384 EnterBlockNotifications();
386 bIsFormatting = sal_True;
388 // Dann kann ich auch den Spell-Timer starten...
389 if ( GetStatus().DoOnlineSpelling() )
390 StartOnlineSpellTimer();
392 long nY = 0;
393 sal_Bool bGrow = sal_False;
395 Font aOldFont( GetRefDevice()->GetFont() );
397 // Hier schon, damit nicht jedesmal in CreateLines...
398 sal_Bool bMapChanged = ImpCheckRefMapMode();
400 aInvalidRec = Rectangle(); // leermachen
401 for ( sal_uInt16 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
403 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
404 if ( pParaPortion->MustRepaint() || ( pParaPortion->IsInvalid() && pParaPortion->IsVisible() ) )
406 if ( pParaPortion->IsInvalid() )
408 sal_Bool bChangedByDerivedClass = GetEditEnginePtr()->FormattingParagraph( nPara );
409 if ( bChangedByDerivedClass )
411 pParaPortion->GetTextPortions().Reset();
412 pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
415 // bei MustRepaint() sollte keine Formatierung noetig sein!
416 // 23.1.95: Evtl. ist sie durch eine andere Aktion aber doch
417 // ungueltig geworden!
418 // if ( pParaPortion->MustRepaint() || CreateLines( nPara ) )
419 if ( ( pParaPortion->MustRepaint() && !pParaPortion->IsInvalid() )
420 || CreateLines( nPara, nY ) )
422 if ( !bGrow && GetTextRanger() )
424 // Bei einer Aenderung der Hoehe muss alles weiter unten
425 // neu formatiert werden...
426 for ( sal_uInt16 n = nPara+1; n < GetParaPortions().Count(); n++ )
428 ParaPortion* pPP = GetParaPortions().GetObject( n );
429 pPP->MarkSelectionInvalid( 0, pPP->GetNode()->Len() );
430 pPP->GetLines().Reset();
433 bGrow = sal_True;
434 if ( IsCallParaInsertedOrDeleted() )
435 GetEditEnginePtr()->ParagraphHeightChanged( nPara );
436 pParaPortion->SetMustRepaint( sal_False );
439 // InvalidRec nur einmal setzen...
440 if ( aInvalidRec.IsEmpty() )
442 // Bei Paperwidth 0 (AutoPageSize) bleibt es sonst Empty()...
443 long nWidth = Max( (long)1, ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) );
444 Range aInvRange( GetInvalidYOffsets( pParaPortion ) );
445 aInvalidRec = Rectangle( Point( 0, nY+aInvRange.Min() ),
446 Size( nWidth, aInvRange.Len() ) );
448 else
450 aInvalidRec.Bottom() = nY + pParaPortion->GetHeight();
453 else if ( bGrow )
455 aInvalidRec.Bottom() = nY + pParaPortion->GetHeight();
457 nY += pParaPortion->GetHeight();
460 // Man kann auch durch UpdateMode An=>AUS=>AN in die Formatierung gelangen...
461 // Optimierung erst nach Vobis-Auslieferung aktivieren...
462 // if ( !aInvalidRec.IsEmpty() )
464 sal_uInt32 nNewHeight = CalcTextHeight();
465 long nDiff = nNewHeight - nCurTextHeight;
466 if ( nDiff )
467 aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTHEIGHTCHANGED : EE_STAT_TEXTWIDTHCHANGED;
468 if ( nNewHeight < nCurTextHeight )
470 aInvalidRec.Bottom() = (long)Max( nNewHeight, nCurTextHeight );
471 if ( aInvalidRec.IsEmpty() )
473 aInvalidRec.Top() = 0;
474 // Left und Right werden nicht ausgewertet, aber wegen IsEmpty gesetzt.
475 aInvalidRec.Left() = 0;
476 aInvalidRec.Right() = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
480 nCurTextHeight = nNewHeight;
482 if ( aStatus.AutoPageSize() )
483 CheckAutoPageSize();
484 else if ( nDiff )
486 for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
488 EditView* pView = aEditViews[nView];
489 ImpEditView* pImpView = pView->pImpEditView;
490 if ( pImpView->DoAutoHeight() )
492 Size aSz( pImpView->GetOutputArea().GetWidth(), nCurTextHeight );
493 if ( aSz.Height() > aMaxAutoPaperSize.Height() )
494 aSz.Height() = aMaxAutoPaperSize.Height();
495 else if ( aSz.Height() < aMinAutoPaperSize.Height() )
496 aSz.Height() = aMinAutoPaperSize.Height();
497 pImpView->ResetOutputArea( Rectangle(
498 pImpView->GetOutputArea().TopLeft(), aSz ) );
504 if ( aStatus.DoRestoreFont() )
505 GetRefDevice()->SetFont( aOldFont );
506 bIsFormatting = sal_False;
507 bFormatted = sal_True;
509 if ( bMapChanged )
510 GetRefDevice()->Pop();
512 CallStatusHdl(); // Falls Modified...
514 LeaveBlockNotifications();
517 sal_Bool ImpEditEngine::ImpCheckRefMapMode()
519 sal_Bool bChange = sal_False;
521 if ( aStatus.DoFormat100() )
523 MapMode aMapMode( GetRefDevice()->GetMapMode() );
524 if ( aMapMode.GetScaleX().GetNumerator() != aMapMode.GetScaleX().GetDenominator() )
525 bChange = sal_True;
526 else if ( aMapMode.GetScaleY().GetNumerator() != aMapMode.GetScaleY().GetDenominator() )
527 bChange = sal_True;
529 if ( bChange )
531 Fraction Scale1( 1, 1 );
532 aMapMode.SetScaleX( Scale1 );
533 aMapMode.SetScaleY( Scale1 );
534 GetRefDevice()->Push();
535 GetRefDevice()->SetMapMode( aMapMode );
539 return bChange;
542 void ImpEditEngine::CheckAutoPageSize()
544 Size aPrevPaperSize( GetPaperSize() );
545 if ( GetStatus().AutoPageWidth() )
546 aPaperSize.Width() = (long) !IsVertical() ? CalcTextWidth( TRUE ) : GetTextHeight();
547 if ( GetStatus().AutoPageHeight() )
548 aPaperSize.Height() = (long) !IsVertical() ? GetTextHeight() : CalcTextWidth( TRUE );
550 SetValidPaperSize( aPaperSize ); //Min, Max beruecksichtigen
552 if ( aPaperSize != aPrevPaperSize )
554 if ( ( !IsVertical() && ( aPaperSize.Width() != aPrevPaperSize.Width() ) )
555 || ( IsVertical() && ( aPaperSize.Height() != aPrevPaperSize.Height() ) ) )
557 // Falls davor zentriert/rechts oder Tabs...
558 aStatus.GetStatusWord() |= !IsVertical() ? EE_STAT_TEXTWIDTHCHANGED : EE_STAT_TEXTHEIGHTCHANGED;
559 for ( sal_uInt16 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
561 // Es brauchen nur Absaetze neu formatiert werden,
562 // die nicht linksbuendig sind.
563 // Die Hoehe kann sich hier nicht mehr aendern.
564 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
565 ContentNode* pNode = pParaPortion->GetNode();
566 SvxAdjust eJustification = GetJustification( nPara );
567 if ( eJustification != SVX_ADJUST_LEFT )
569 pParaPortion->MarkSelectionInvalid( 0, pNode->Len() );
570 CreateLines( nPara, 0 ); // 0: Bei AutoPageSize kein TextRange!
575 Size aInvSize = aPaperSize;
576 if ( aPaperSize.Width() < aPrevPaperSize.Width() )
577 aInvSize.Width() = aPrevPaperSize.Width();
578 if ( aPaperSize.Height() < aPrevPaperSize.Height() )
579 aInvSize.Height() = aPrevPaperSize.Height();
581 Size aSz( aInvSize );
582 if ( IsVertical() )
584 aSz.Width() = aInvSize.Height();
585 aSz.Height() = aInvSize.Width();
587 aInvalidRec = Rectangle( Point(), aSz );
590 for ( sal_uInt16 nView = 0; nView < aEditViews.Count(); nView++ )
592 EditView* pView = aEditViews[nView];
593 pView->pImpEditView->RecalcOutputArea();
598 static sal_Int32 ImplCalculateFontIndependentLineSpacing( const sal_Int32 nFontHeight )
600 return ( nFontHeight * 12 ) / 10; // + 20%
603 sal_Bool ImpEditEngine::CreateLines( USHORT nPara, sal_uInt32 nStartPosY )
605 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
607 // sal_Bool: Aenderung der Hoehe des Absatzes Ja/Nein - sal_True/sal_False
608 DBG_ASSERT( pParaPortion->GetNode(), "Portion ohne Node in CreateLines" );
609 DBG_ASSERT( pParaPortion->IsVisible(), "Unsichtbare Absaetze nicht formatieren!" );
610 DBG_ASSERT( pParaPortion->IsInvalid(), "CreateLines: Portion nicht invalid!" );
612 BOOL bProcessingEmptyLine = ( pParaPortion->GetNode()->Len() == 0 );
613 BOOL bEmptyNodeWithPolygon = ( pParaPortion->GetNode()->Len() == 0 ) && GetTextRanger();
615 // ---------------------------------------------------------------
616 // Schnelle Sonderbehandlung fuer leere Absaetze...
617 // ---------------------------------------------------------------
618 if ( ( pParaPortion->GetNode()->Len() == 0 ) && !GetTextRanger() )
620 // schnelle Sonderbehandlung...
621 if ( pParaPortion->GetTextPortions().Count() )
622 pParaPortion->GetTextPortions().Reset();
623 if ( pParaPortion->GetLines().Count() )
624 pParaPortion->GetLines().Reset();
625 CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
626 return FinishCreateLines( pParaPortion );
629 // ---------------------------------------------------------------
630 // Initialisierung......
631 // ---------------------------------------------------------------
633 // Immer fuer 100% formatieren:
634 sal_Bool bMapChanged = ImpCheckRefMapMode();
636 if ( pParaPortion->GetLines().Count() == 0 )
638 EditLine* pL = new EditLine;
639 pParaPortion->GetLines().Insert( pL, 0 );
642 // ---------------------------------------------------------------
643 // Absatzattribute holen......
644 // ---------------------------------------------------------------
645 ContentNode* const pNode = pParaPortion->GetNode();
647 BOOL bRightToLeftPara = IsRightToLeft( nPara );
649 SvxAdjust eJustification = GetJustification( nPara );
650 sal_Bool bHyphenatePara = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HYPHENATE )).GetValue();
651 sal_Int32 nSpaceBefore = 0;
652 sal_Int32 nMinLabelWidth = 0;
653 sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pNode, &nSpaceBefore, &nMinLabelWidth );
654 const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pNode );
655 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&) pNode->GetContentAttribs().GetItem( EE_PARA_SBL );
656 const BOOL bScriptSpace = ((const SvxScriptSpaceItem&) pNode->GetContentAttribs().GetItem( EE_PARA_ASIANCJKSPACING )).GetValue();
658 // const sal_uInt16 nInvalidEnd = ( pParaPortion->GetInvalidDiff() > 0 )
659 // ? pParaPortion->GetInvalidPosStart() + pParaPortion->GetInvalidDiff()
660 // : pNode->Len();
661 const short nInvalidDiff = pParaPortion->GetInvalidDiff();
662 const sal_uInt16 nInvalidStart = pParaPortion->GetInvalidPosStart();
663 const sal_uInt16 nInvalidEnd = nInvalidStart + Abs( nInvalidDiff );
665 sal_Bool bQuickFormat = sal_False;
666 if ( !bEmptyNodeWithPolygon && !HasScriptType( nPara, i18n::ScriptType::COMPLEX ) )
668 if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff > 0 ) &&
669 ( pNode->Search( CH_FEATURE, nInvalidStart ) > nInvalidEnd ) )
671 bQuickFormat = sal_True;
673 else if ( ( pParaPortion->IsSimpleInvalid() ) && ( nInvalidDiff < 0 ) )
675 // pruefen, ob loeschen ueber Portiongrenzen erfolgte...
676 sal_uInt16 nStart = nInvalidStart; // DOPPELT !!!!!!!!!!!!!!!
677 sal_uInt16 nEnd = nStart - nInvalidDiff; // neg.
678 bQuickFormat = sal_True;
679 sal_uInt16 nPos = 0;
680 sal_uInt16 nPortions = pParaPortion->GetTextPortions().Count();
681 for ( sal_uInt16 nTP = 0; nTP < nPortions; nTP++ )
683 // Es darf kein Start/Ende im geloeschten Bereich liegen.
684 TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
685 nPos = nPos + pTP->GetLen();
686 if ( ( nPos > nStart ) && ( nPos < nEnd ) )
688 bQuickFormat = sal_False;
689 break;
695 // SW disables TEXT_LAYOUT_COMPLEX_DISABLED, so maybe I have to enable it...
697 // #114278# Saving both layout mode and language (since I'm
698 // potentially changing both)
700 GetRefDevice()->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
702 ImplInitLayoutMode( GetRefDevice(), nPara, 0xFFFF );
704 sal_uInt16 nRealInvalidStart = nInvalidStart;
706 if ( bEmptyNodeWithPolygon )
708 TextPortion* pDummyPortion = new TextPortion( 0 );
709 pParaPortion->GetTextPortions().Reset();
710 pParaPortion->GetTextPortions().Insert( pDummyPortion, 0 );
712 else if ( bQuickFormat )
714 // schnellere Methode:
715 RecalcTextPortion( pParaPortion, nInvalidStart, nInvalidDiff );
717 else // nRealInvalidStart kann vor InvalidStart liegen, weil Portions geloescht....
719 CreateTextPortions( pParaPortion, nRealInvalidStart );
723 // ---------------------------------------------------------------
724 // Zeile mit InvalidPos suchen, eine Zeile davor beginnen...
725 // Zeilen flaggen => nicht removen !
726 // ---------------------------------------------------------------
728 sal_uInt16 nLine = pParaPortion->GetLines().Count()-1;
729 for ( sal_uInt16 nL = 0; nL <= nLine; nL++ )
731 EditLine* pLine = pParaPortion->GetLines().GetObject( nL );
732 if ( pLine->GetEnd() > nRealInvalidStart ) // nicht nInvalidStart!
734 nLine = nL;
735 break;
737 pLine->SetValid();
739 // Eine Zeile davor beginnen...
740 // Wenn ganz hinten getippt wird, kann sich die Zeile davor nicht aendern.
741 if ( nLine && ( !pParaPortion->IsSimpleInvalid() || ( nInvalidEnd < pNode->Len() ) || ( nInvalidDiff <= 0 ) ) )
742 nLine--;
744 EditLine* pLine = pParaPortion->GetLines().GetObject( nLine );
746 static Rectangle aZeroArea = Rectangle( Point(), Point() );
747 Rectangle aBulletArea( aZeroArea );
748 if ( !nLine )
750 aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
751 if ( aBulletArea.Right() > 0 )
752 pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
753 else
754 pParaPortion->SetBulletX( 0 ); // Falls Bullet falsch eingestellt.
757 // ---------------------------------------------------------------
758 // Ab hier alle Zeilen durchformatieren...
759 // ---------------------------------------------------------------
760 sal_uInt16 nDelFromLine = 0xFFFF;
761 sal_Bool bLineBreak = sal_False;
763 sal_uInt16 nIndex = pLine->GetStart();
764 EditLine aSaveLine( *pLine );
765 SvxFont aTmpFont( pNode->GetCharAttribs().GetDefFont() );
767 sal_Bool bCalcCharPositions = sal_True;
768 sal_Int32* pBuf = new sal_Int32[ pNode->Len() ];
770 sal_Bool bSameLineAgain = sal_False; // Fuer TextRanger, wenn sich die Hoehe aendert.
771 TabInfo aCurrentTab;
773 BOOL bForceOneRun = bEmptyNodeWithPolygon;
774 BOOL bCompressedChars = FALSE;
776 while ( ( nIndex < pNode->Len() ) || bForceOneRun )
778 bForceOneRun = FALSE;
780 sal_Bool bEOL = sal_False;
781 sal_Bool bEOC = sal_False;
782 sal_uInt16 nPortionStart = 0;
783 sal_uInt16 nPortionEnd = 0;
785 long nStartX = GetXValue( rLRItem.GetTxtLeft() + nSpaceBeforeAndMinLabelWidth );
786 if ( nIndex == 0 )
788 long nFI = GetXValue( rLRItem.GetTxtFirstLineOfst() );
789 nStartX += nFI;
791 if ( !nLine && ( pParaPortion->GetBulletX() > nStartX ) )
793 // TL_NFLR nStartX += nFI; // Vielleicht reicht der LI?
794 // TL_NFLR if ( pParaPortion->GetBulletX() > nStartX )
795 nStartX = pParaPortion->GetBulletX();
799 long nMaxLineWidth;
800 if ( !IsVertical() )
801 nMaxLineWidth = aStatus.AutoPageWidth() ? aMaxAutoPaperSize.Width() : aPaperSize.Width();
802 else
803 nMaxLineWidth = aStatus.AutoPageHeight() ? aMaxAutoPaperSize.Height() : aPaperSize.Height();
805 nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
806 nMaxLineWidth -= nStartX;
808 // Wenn PaperSize == long_max, kann ich keinen neg. Erstzeileneinzug
809 // abziehen (Overflow)
810 if ( ( nMaxLineWidth < 0 ) && ( nStartX < 0 ) )
811 nMaxLineWidth = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() ) - GetXValue( rLRItem.GetRight() );
813 // Wenn jetzt noch kleiner 0, kann es nur der rechte Rand sein.
814 if ( nMaxLineWidth <= 0 )
815 nMaxLineWidth = 1;
817 // Problem: Da eine Zeile _vor_ der ungueltigen Position mit der
818 // Formatierung begonnen wird, werden hier leider auch die Positionen
819 // neu bestimmt...
820 // Loesungsansatz:
821 // Die Zeile davor kann nur groesser werden, nicht kleiner
822 // => ...
823 if ( bCalcCharPositions )
824 pLine->GetCharPosArray().Remove( 0, pLine->GetCharPosArray().Count() );
826 sal_uInt16 nTmpPos = nIndex;
827 sal_uInt16 nTmpPortion = pLine->GetStartPortion();
828 long nTmpWidth = 0;
829 long nXWidth = nMaxLineWidth;
830 if ( nXWidth <= nTmpWidth ) // while muss 1x durchlaufen werden
831 nXWidth = nTmpWidth+1;
833 SvLongsPtr pTextRanges = 0;
834 long nTextExtraYOffset = 0;
835 long nTextXOffset = 0;
836 long nTextLineHeight = 0;
837 if ( GetTextRanger() )
839 GetTextRanger()->SetVertical( IsVertical() );
841 long nTextY = nStartPosY + GetEditCursor( pParaPortion, pLine->GetStart() ).Top();
842 if ( !bSameLineAgain )
844 SeekCursor( pNode, nTmpPos+1, aTmpFont );
845 aTmpFont.SetPhysFont( GetRefDevice() );
846 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
848 if ( IsFixedCellHeight() )
849 nTextLineHeight = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
850 else
851 nTextLineHeight = aTmpFont.GetPhysTxtSize( GetRefDevice(), String() ).Height();
852 // Metriken koennen groesser sein
853 FormatterFontMetric aTempFormatterMetrics;
854 RecalcFormatterFontMetrics( aTempFormatterMetrics, aTmpFont );
855 sal_uInt16 nLineHeight = aTempFormatterMetrics.GetHeight();
856 if ( nLineHeight > nTextLineHeight )
857 nTextLineHeight = nLineHeight;
859 else
860 nTextLineHeight = pLine->GetHeight();
862 nXWidth = 0;
863 while ( !nXWidth )
865 long nYOff = nTextY + nTextExtraYOffset;
866 long nYDiff = nTextLineHeight;
867 if ( IsVertical() )
869 long nMaxPolygonX = GetTextRanger()->GetBoundRect().Right();
870 nYOff = nMaxPolygonX-nYOff;
871 nYDiff = -nTextLineHeight;
873 pTextRanges = GetTextRanger()->GetTextRanges( Range( nYOff, nYOff + nYDiff ) );
874 DBG_ASSERT( pTextRanges, "GetTextRanges?!" );
875 long nMaxRangeWidth = 0;
876 // Den breitesten Bereich verwenden...
877 // Der breiteste Bereich koennte etwas verwirren, also
878 // generell den ersten. Am besten mal richtig mit Luecken.
879 // for ( sal_uInt16 n = 0; n < pTextRanges->Count(); )
880 if ( pTextRanges->Count() )
882 sal_uInt16 n = 0;
883 long nA = pTextRanges->GetObject( n++ );
884 long nB = pTextRanges->GetObject( n++ );
885 DBG_ASSERT( nA <= nB, "TextRange verdreht?" );
886 long nW = nB - nA;
887 if ( nW > nMaxRangeWidth )
889 nMaxRangeWidth = nW;
890 nTextXOffset = nA;
893 nXWidth = nMaxRangeWidth;
894 if ( nXWidth )
895 nMaxLineWidth = nXWidth - nStartX - GetXValue( rLRItem.GetRight() );
896 else
898 // Weiter unten im Polygon versuchen.
899 // Unterhalb des Polygons die Paperbreite verwenden.
900 nTextExtraYOffset += Max( (long)(nTextLineHeight / 10), (long)1 );
901 if ( ( nTextY + nTextExtraYOffset ) > GetTextRanger()->GetBoundRect().Bottom() )
903 nXWidth = !IsVertical() ? GetPaperSize().Width() : GetPaperSize().Height();
904 if ( !nXWidth ) // AutoPaperSize
905 nXWidth = 0x7FFFFFFF;
911 // Portion suchen, die nicht mehr in Zeile passt....
912 TextPortion* pPortion = 0;
913 sal_Bool bBrokenLine = sal_False;
914 bLineBreak = sal_False;
915 EditCharAttrib* pNextFeature = pNode->GetCharAttribs().FindFeature( pLine->GetStart() );
916 while ( ( nTmpWidth < nXWidth ) && !bEOL && ( nTmpPortion < pParaPortion->GetTextPortions().Count() ) )
918 nPortionStart = nTmpPos;
919 pPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
920 if ( pPortion->GetKind() == PORTIONKIND_HYPHENATOR )
922 // Portion wegschmeissen, ggf. die davor korrigieren, wenn
923 // die Hyph-Portion ein Zeichen geschluckt hat...
924 pParaPortion->GetTextPortions().Remove( nTmpPortion );
925 if ( nTmpPortion && pPortion->GetLen() )
927 nTmpPortion--;
928 TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
929 DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
930 nTmpWidth -= pPrev->GetSize().Width();
931 nTmpPos = nTmpPos - pPrev->GetLen();
932 pPrev->SetLen( pPrev->GetLen() + pPortion->GetLen() );
933 pPrev->GetSize().Width() = (-1);
935 delete pPortion;
936 DBG_ASSERT( nTmpPortion < pParaPortion->GetTextPortions().Count(), "Keine Portion mehr da!" );
937 pPortion = pParaPortion->GetTextPortions().GetObject( nTmpPortion );
939 DBG_ASSERT( pPortion->GetKind() != PORTIONKIND_HYPHENATOR, "CreateLines: Hyphenator-Portion!" );
940 DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Leere Portion in CreateLines ?!" );
941 if ( pNextFeature && ( pNextFeature->GetStart() == nTmpPos ) )
943 sal_uInt16 nWhich = pNextFeature->GetItem()->Which();
944 switch ( nWhich )
946 case EE_FEATURE_TAB:
948 long nOldTmpWidth = nTmpWidth;
950 // Tab-Pos suchen...
951 long nCurPos = nTmpWidth+nStartX;
952 // nCurPos -= rLRItem.GetTxtLeft(); // Tabs relativ zu LI
953 // Skalierung rausrechnen
954 if ( aStatus.DoStretch() && ( nStretchX != 100 ) )
955 nCurPos = nCurPos*100/std::max(static_cast<sal_Int32>(nStretchX), static_cast<sal_Int32>(1));
957 short nAllSpaceBeforeText = static_cast< short >(rLRItem.GetTxtLeft()/* + rLRItem.GetTxtLeft()*/ + nSpaceBeforeAndMinLabelWidth);
958 aCurrentTab.aTabStop = pNode->GetContentAttribs().FindTabStop( nCurPos - nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/, aEditDoc.GetDefTab() );
959 aCurrentTab.nTabPos = GetXValue( (long) ( aCurrentTab.aTabStop.GetTabPos() + nAllSpaceBeforeText /*rLRItem.GetTxtLeft()*/ ) );
960 aCurrentTab.bValid = FALSE;
962 // Switch direction in R2L para...
963 if ( bRightToLeftPara )
965 if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
966 aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_LEFT;
967 else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_LEFT )
968 aCurrentTab.aTabStop.GetAdjustment() = SVX_TAB_ADJUST_RIGHT;
971 if ( ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT ) ||
972 ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER ) ||
973 ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL ) )
975 // Bei LEFT/DEFAULT wird dieses Tab nicht mehr betrachtet.
976 aCurrentTab.bValid = TRUE;
977 aCurrentTab.nStartPosX = nTmpWidth;
978 aCurrentTab.nCharPos = nTmpPos;
979 aCurrentTab.nTabPortion = nTmpPortion;
982 pPortion->GetKind() = PORTIONKIND_TAB;
983 pPortion->SetExtraValue( aCurrentTab.aTabStop.GetFill() );
984 pPortion->GetSize().Width() = aCurrentTab.nTabPos - (nTmpWidth+nStartX);
986 // #90520# Height needed...
987 SeekCursor( pNode, nTmpPos+1, aTmpFont );
988 pPortion->GetSize().Height() = aTmpFont.QuickGetTextSize( GetRefDevice(), String(), 0, 0, NULL ).Height();
990 DBG_ASSERT( pPortion->GetSize().Width() >= 0, "Tab falsch berechnet!" );
992 nTmpWidth = aCurrentTab.nTabPos-nStartX;
994 // Wenn dies das erste Token in der Zeile ist,
995 // und nTmpWidth > aPaperSize.Width, habe ich eine
996 // Endlos-Schleife!
997 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
999 // Aber was jetzt ?
1000 // Tab passend machen
1001 pPortion->GetSize().Width() = nXWidth-nOldTmpWidth;
1002 nTmpWidth = nXWidth-1;
1003 bEOL = sal_True;
1004 bBrokenLine = sal_True;
1006 pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
1007 bCompressedChars = FALSE;
1009 break;
1010 case EE_FEATURE_LINEBR:
1012 DBG_ASSERT( pPortion, "?!" );
1013 pPortion->GetSize().Width() = 0;
1014 bEOL = sal_True;
1015 bLineBreak = sal_True;
1016 pPortion->GetKind() = PORTIONKIND_LINEBREAK;
1017 bCompressedChars = FALSE;
1018 pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
1020 break;
1021 case EE_FEATURE_FIELD:
1023 // long nCurWidth = nTmpWidth;
1024 SeekCursor( pNode, nTmpPos+1, aTmpFont );
1025 sal_Unicode cChar = 0; // later: NBS?
1026 aTmpFont.SetPhysFont( GetRefDevice() );
1027 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
1029 String aFieldValue = cChar ? String(cChar) : ((EditCharAttribField*)pNextFeature)->GetFieldValue();
1030 if ( bCalcCharPositions || !pPortion->HasValidSize() )
1032 pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), aFieldValue, 0, aFieldValue.Len(), 0 );
1033 // Damit kein Scrollen bei ueberlangen Feldern
1034 if ( pPortion->GetSize().Width() > nXWidth )
1035 pPortion->GetSize().Width() = nXWidth;
1037 nTmpWidth += pPortion->GetSize().Width();
1038 pLine->GetCharPosArray().Insert( pPortion->GetSize().Width(), nTmpPos-pLine->GetStart() );
1039 pPortion->GetKind() = cChar ? PORTIONKIND_TEXT : PORTIONKIND_FIELD;
1040 // Wenn dies das erste Token in der Zeile ist,
1041 // und nTmpWidth > aPaperSize.Width, habe ich eine
1042 // Endlos-Schleife!
1043 if ( ( nTmpWidth >= nXWidth ) && ( nTmpPortion == pLine->GetStartPortion() ) )
1045 nTmpWidth = nXWidth-1;
1046 bEOL = sal_True;
1047 bBrokenLine = sal_True;
1049 // Compression in Fields????
1050 // I think this could be a little bit difficult and is not very usefull
1051 bCompressedChars = FALSE;
1053 break;
1054 default: DBG_ERROR( "Was fuer ein Feature ?" );
1056 pNextFeature = pNode->GetCharAttribs().FindFeature( pNextFeature->GetStart() + 1 );
1058 else
1060 DBG_ASSERT( pPortion->GetLen() || bProcessingEmptyLine, "Empty Portion - Extra Space?!" );
1061 SeekCursor( pNode, nTmpPos+1, aTmpFont );
1062 aTmpFont.SetPhysFont( GetRefDevice() );
1063 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
1065 if ( bCalcCharPositions || !pPortion->HasValidSize() )
1067 pPortion->GetSize() = aTmpFont.QuickGetTextSize( GetRefDevice(), *pParaPortion->GetNode(), nTmpPos, pPortion->GetLen(), pBuf );
1069 // #i9050# Do Kerning also behind portions...
1070 if ( ( aTmpFont.GetFixKerning() > 0 ) && ( ( nTmpPos + pPortion->GetLen() ) < pNode->Len() ) )
1071 pPortion->GetSize().Width() += aTmpFont.GetFixKerning();
1072 if ( IsFixedCellHeight() )
1073 pPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1075 if ( bCalcCharPositions )
1077 sal_uInt16 nLen = pPortion->GetLen();
1078 // Es wird am Anfang generell das Array geplaettet
1079 // => Immer einfach schnelles insert.
1080 sal_uInt16 nPos = nTmpPos - pLine->GetStart();
1081 pLine->GetCharPosArray().Insert( pBuf, nLen, nPos );
1084 // And now check for Compression:
1085 if ( pPortion->GetLen() && GetAsianCompressionMode() )
1086 bCompressedChars |= ImplCalcAsianCompression( pNode, pPortion, nTmpPos, (sal_Int32*)pLine->GetCharPosArray().GetData() + (nTmpPos-pLine->GetStart()), 10000, FALSE );
1088 nTmpWidth += pPortion->GetSize().Width();
1090 pPortion->SetRightToLeft( GetRightToLeft( nPara, nTmpPos+1 ) );
1092 USHORT _nPortionEnd = nTmpPos + pPortion->GetLen();
1093 if( bScriptSpace && ( _nPortionEnd < pNode->Len() ) && ( nTmpWidth < nXWidth ) && IsScriptChange( EditPaM( pNode, _nPortionEnd ) ) )
1095 BOOL bAllow = FALSE;
1096 USHORT nScriptTypeLeft = GetScriptType( EditPaM( pNode, _nPortionEnd ) );
1097 USHORT nScriptTypeRight = GetScriptType( EditPaM( pNode, _nPortionEnd+1 ) );
1098 if ( ( nScriptTypeLeft == i18n::ScriptType::ASIAN ) || ( nScriptTypeRight == i18n::ScriptType::ASIAN ) )
1099 bAllow = TRUE;
1101 // No spacing within L2R/R2L nesting
1102 if ( bAllow )
1104 long nExtraSpace = pPortion->GetSize().Height()/5;
1105 nExtraSpace = GetXValue( nExtraSpace );
1106 pPortion->GetSize().Width() += nExtraSpace;
1107 nTmpWidth += nExtraSpace;
1112 if ( aCurrentTab.bValid && ( nTmpPortion != aCurrentTab.nTabPortion ) )
1114 long nWidthAfterTab = 0;
1115 for ( USHORT n = aCurrentTab.nTabPortion+1; n <= nTmpPortion; n++ )
1117 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( n );
1118 nWidthAfterTab += pTP->GetSize().Width();
1120 long nW = nWidthAfterTab; // Length before tab position
1121 if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_RIGHT )
1123 // nW = nWidthAfterTab;
1125 else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_CENTER )
1127 nW = nWidthAfterTab/2;
1129 else if ( aCurrentTab.aTabStop.GetAdjustment() == SVX_TAB_ADJUST_DECIMAL )
1131 // nW = nWidthAfterTab;
1132 String aText = GetSelected( EditSelection( EditPaM( pParaPortion->GetNode(), nTmpPos ),
1133 EditPaM( pParaPortion->GetNode(), nTmpPos + pPortion->GetLen() ) ) );
1134 USHORT nDecPos = aText.Search( aCurrentTab.aTabStop.GetDecimal() );
1135 if ( nDecPos != STRING_NOTFOUND )
1137 nW -= pParaPortion->GetTextPortions().GetObject( nTmpPortion )->GetSize().Width();
1138 nW += aTmpFont.QuickGetTextSize( GetRefDevice(), *pParaPortion->GetNode(), nTmpPos, nDecPos, NULL ).Width();
1139 aCurrentTab.bValid = FALSE;
1142 else
1144 DBG_ERROR( "CreateLines: Tab not handled!" );
1146 long nMaxW = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nStartX;
1147 if ( nW >= nMaxW )
1149 nW = nMaxW;
1150 aCurrentTab.bValid = FALSE;
1152 TextPortion* pTabPortion = pParaPortion->GetTextPortions().GetObject( aCurrentTab.nTabPortion );
1153 pTabPortion->GetSize().Width() = aCurrentTab.nTabPos - aCurrentTab.nStartPosX - nW - nStartX;
1154 nTmpWidth = aCurrentTab.nStartPosX + pTabPortion->GetSize().Width() + nWidthAfterTab;
1157 nTmpPos = nTmpPos + pPortion->GetLen();
1158 nPortionEnd = nTmpPos;
1159 nTmpPortion++;
1160 if ( aStatus.OneCharPerLine() )
1161 bEOL = sal_True;
1164 DBG_ASSERT( pPortion, "no portion!?" );
1166 aCurrentTab.bValid = FALSE;
1168 // das war evtl. eine Portion zu weit:
1169 sal_Bool bFixedEnd = sal_False;
1170 if ( aStatus.OneCharPerLine() )
1172 // Zustand vor Portion: ( bis auf nTmpWidth )
1173 nPortionEnd = nTmpPos;
1174 nTmpPos -= pPortion ? pPortion->GetLen() : 0;
1175 nPortionStart = nTmpPos;
1176 nTmpPortion--;
1178 bEOL = sal_True;
1179 bEOC = sal_False;
1181 // Und jetzt genau ein Zeichen:
1182 nTmpPos++;
1183 nTmpPortion++;
1184 nPortionEnd = nTmpPortion;
1185 // Eine Nicht-Feature-Portion muss gebrochen werden
1186 if ( pPortion->GetLen() > 1 )
1188 DBG_ASSERT( pPortion && (pPortion->GetKind() == PORTIONKIND_TEXT), "Len>1, aber keine TextPortion?" );
1189 nTmpWidth -= pPortion ? pPortion->GetSize().Width() : 0;
1190 sal_uInt16 nP = SplitTextPortion( pParaPortion, nTmpPos, pLine );
1191 TextPortion* p = pParaPortion->GetTextPortions().GetObject( nP );
1192 DBG_ASSERT( p, "Portion ?!" );
1193 nTmpWidth += p->GetSize().Width();
1196 else if ( nTmpWidth >= nXWidth )
1198 nPortionEnd = nTmpPos;
1199 nTmpPos -= pPortion ? pPortion->GetLen() : 0;
1200 nPortionStart = nTmpPos;
1201 nTmpPortion--;
1202 bEOL = sal_False;
1203 bEOC = sal_False;
1204 if( pPortion ) switch ( pPortion->GetKind() )
1206 case PORTIONKIND_TEXT:
1208 nTmpWidth -= pPortion->GetSize().Width();
1210 break;
1211 case PORTIONKIND_FIELD:
1212 case PORTIONKIND_TAB:
1214 nTmpWidth -= pPortion->GetSize().Width();
1215 bEOL = sal_True;
1216 bFixedEnd = sal_True;
1218 break;
1219 default:
1221 // Ein Feature wird nicht umgebrochen:
1222 DBG_ASSERT( ( pPortion->GetKind() == PORTIONKIND_LINEBREAK ), "Was fuer ein Feature ?" );
1223 bEOL = sal_True;
1224 bFixedEnd = sal_True;
1228 else
1230 bEOL = sal_True;
1231 bEOC = sal_True;
1232 pLine->SetEnd( nPortionEnd );
1233 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine TextPortions?" );
1234 pLine->SetEndPortion( (sal_uInt16)pParaPortion->GetTextPortions().Count() - 1 );
1237 if ( aStatus.OneCharPerLine() )
1239 pLine->SetEnd( nPortionEnd );
1240 pLine->SetEndPortion( nTmpPortion-1 );
1242 else if ( bFixedEnd )
1244 pLine->SetEnd( nPortionStart );
1245 pLine->SetEndPortion( nTmpPortion-1 );
1247 else if ( bLineBreak || bBrokenLine )
1249 pLine->SetEnd( nPortionStart+1 );
1250 pLine->SetEndPortion( nTmpPortion-1 );
1251 bEOC = sal_False; // wurde oben gesetzt, vielleich mal die if's umstellen?
1253 else if ( !bEOL )
1255 DBG_ASSERT( pPortion && ((nPortionEnd-nPortionStart) == pPortion->GetLen()), "Doch eine andere Portion?!" );
1256 long nRemainingWidth = nMaxLineWidth - nTmpWidth;
1257 sal_Bool bCanHyphenate = ( aTmpFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL );
1258 if ( bCompressedChars && pPortion && ( pPortion->GetLen() > 1 ) && pPortion->GetExtraInfos() && pPortion->GetExtraInfos()->bCompressed )
1260 // I need the manipulated DXArray for determining the break postion...
1261 ImplCalcAsianCompression( pNode, pPortion, nPortionStart, const_cast<sal_Int32*>(( pLine->GetCharPosArray().GetData() + (nPortionStart-pLine->GetStart()) )), 10000, TRUE );
1263 if( pPortion )
1264 ImpBreakLine( pParaPortion, pLine, pPortion, nPortionStart,
1265 nRemainingWidth, bCanHyphenate && bHyphenatePara );
1268 // ------------------------------------------------------------------
1269 // Zeile fertig => justieren
1270 // ------------------------------------------------------------------
1272 // CalcTextSize sollte besser durch ein kontinuierliches
1273 // Registrieren ersetzt werden !
1274 Size aTextSize = pLine->CalcTextSize( *pParaPortion );
1276 if ( aTextSize.Height() == 0 )
1278 SeekCursor( pNode, pLine->GetStart()+1, aTmpFont );
1279 aTmpFont.SetPhysFont( pRefDev );
1280 ImplInitDigitMode( pRefDev, 0, 0, 0, aTmpFont.GetLanguage() );
1282 if ( IsFixedCellHeight() )
1283 aTextSize.Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1284 else
1285 aTextSize.Height() = aTmpFont.GetPhysTxtSize( pRefDev, String() ).Height();
1286 pLine->SetHeight( (sal_uInt16)aTextSize.Height() );
1289 // Die Fontmetriken koennen nicht kontinuierlich berechnet werden,
1290 // wenn der Font sowieso eingestellt ist, weil ggf. ein grosser Font
1291 // erst nach dem Umbrechen ploetzlich in der naechsten Zeile landet
1292 // => Font-Metriken zu gross.
1293 FormatterFontMetric aFormatterMetrics;
1294 sal_uInt16 nTPos = pLine->GetStart();
1295 for ( sal_uInt16 nP = pLine->GetStartPortion(); nP <= pLine->GetEndPortion(); nP++ )
1297 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( nP );
1298 // #95819# problem with hard font height attribute, when everthing but the line break has this attribute
1299 if ( pTP->GetKind() != PORTIONKIND_LINEBREAK )
1301 SeekCursor( pNode, nTPos+1, aTmpFont );
1302 aTmpFont.SetPhysFont( GetRefDevice() );
1303 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
1304 RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
1306 nTPos = nTPos + pTP->GetLen();
1308 sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
1309 if ( nLineHeight > pLine->GetHeight() )
1310 pLine->SetHeight( nLineHeight );
1311 pLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
1313 bSameLineAgain = sal_False;
1314 if ( GetTextRanger() && ( pLine->GetHeight() > nTextLineHeight ) )
1316 // Nochmal mit der anderen Groesse aufsetzen!
1317 bSameLineAgain = sal_True;
1321 if ( !bSameLineAgain && !aStatus.IsOutliner() )
1323 if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
1325 sal_uInt16 nMinHeight = GetYValue( rLSItem.GetLineHeight() );
1326 sal_uInt16 nTxtHeight = pLine->GetHeight();
1327 if ( nTxtHeight < nMinHeight )
1329 // Der Ascent muss um die Differenz angepasst werden:
1330 long nDiff = nMinHeight - nTxtHeight;
1331 pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff) );
1332 pLine->SetHeight( nMinHeight, nTxtHeight );
1335 else if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_FIX )
1337 sal_uInt16 nFixHeight = GetYValue( rLSItem.GetLineHeight() );
1338 sal_uInt16 nTxtHeight = pLine->GetHeight();
1339 pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + ( nFixHeight - nTxtHeight ) ) );
1340 pLine->SetHeight( nFixHeight, nTxtHeight );
1342 else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
1344 if ( nPara || IsFixedCellHeight() || pLine->GetStartPortion() ) // Nicht die aller erste Zeile
1346 // #100508# There are documents with PropLineSpace 0, why?
1347 // (cmc: re above question :-) such documents can be seen by importing a .ppt
1348 if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
1350 sal_uInt16 nTxtHeight = pLine->GetHeight();
1351 sal_Int32 nH = nTxtHeight;
1352 nH *= rLSItem.GetPropLineSpace();
1353 nH /= 100;
1354 // Der Ascent muss um die Differenz angepasst werden:
1355 long nDiff = pLine->GetHeight() - nH;
1356 if ( nDiff > pLine->GetMaxAscent() )
1357 nDiff = pLine->GetMaxAscent();
1358 pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() - nDiff) );
1359 pLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1366 // #80582# - Bullet should not influence line height
1367 // if ( !nLine )
1368 // {
1369 // long nBulletHeight = aBulletArea.GetHeight();
1370 // if ( nBulletHeight > (long)pLine->GetHeight() )
1371 // {
1372 // long nDiff = nBulletHeight - (long)pLine->GetHeight();
1373 // // nDiff auf oben und unten verteilen.
1374 // pLine->SetMaxAscent( (sal_uInt16)(pLine->GetMaxAscent() + nDiff/2) );
1375 // pLine->SetHeight( (sal_uInt16)nBulletHeight );
1376 // }
1377 // }
1379 if ( ( !IsVertical() && aStatus.AutoPageWidth() ) ||
1380 ( IsVertical() && aStatus.AutoPageHeight() ) )
1382 // Wenn die Zeile in die aktuelle Papierbreite passt, muss
1383 // diese Breite fuer die Ausrichting verwendet werden.
1384 // Wenn sie nicht passt oder sie die Papierbreite aendert,
1385 // wird bei Justification != LEFT sowieso noch mal formatiert.
1386 long nMaxLineWidthFix = ( !IsVertical() ? aPaperSize.Width() : aPaperSize.Height() )
1387 - GetXValue( rLRItem.GetRight() ) - nStartX;
1388 if ( aTextSize.Width() < nMaxLineWidthFix )
1389 nMaxLineWidth = nMaxLineWidthFix;
1392 if ( bCompressedChars )
1394 long nRemainingWidth = nMaxLineWidth - aTextSize.Width();
1395 if ( nRemainingWidth > 0 )
1397 ImplExpandCompressedPortions( pLine, pParaPortion, nRemainingWidth );
1398 aTextSize = pLine->CalcTextSize( *pParaPortion );
1402 if ( pLine->IsHangingPunctuation() )
1404 // Width from HangingPunctuation was set to 0 in ImpBreakLine,
1405 // check for rel width now, maybe create compression...
1406 long n = nMaxLineWidth - aTextSize.Width();
1407 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( pLine->GetEndPortion() );
1408 sal_uInt16 nPosInArray = pLine->GetEnd()-1-pLine->GetStart();
1409 long nNewValue = ( nPosInArray ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0 ) + n;
1410 pLine->GetCharPosArray()[ nPosInArray ] = nNewValue;
1411 pTP->GetSize().Width() += n;
1414 pLine->SetTextWidth( aTextSize.Width() );
1415 switch ( eJustification )
1417 case SVX_ADJUST_CENTER:
1419 long n = ( nMaxLineWidth - aTextSize.Width() ) / 2;
1420 n += nStartX; // Einrueckung bleibt erhalten.
1421 if ( n > 0 )
1422 pLine->SetStartPosX( (sal_uInt16)n );
1423 else
1424 pLine->SetStartPosX( 0 );
1427 break;
1428 case SVX_ADJUST_RIGHT:
1430 // Bei automatisch umgebrochenen Zeilen, die ein Blank
1431 // am Ende enthalten, darf das Blank nicht ausgegeben werden!
1433 long n = nMaxLineWidth - aTextSize.Width();
1434 n += nStartX; // Einrueckung bleibt erhalten.
1435 if ( n > 0 )
1436 pLine->SetStartPosX( (sal_uInt16)n );
1437 else
1438 pLine->SetStartPosX( 0 );
1440 break;
1441 case SVX_ADJUST_BLOCK:
1443 long nRemainingSpace = nMaxLineWidth - aTextSize.Width();
1444 pLine->SetStartPosX( (sal_uInt16)nStartX );
1445 if ( !bEOC && ( nRemainingSpace > 0 ) ) // nicht die letzte Zeile...
1446 ImpAdjustBlocks( pParaPortion, pLine, nRemainingSpace );
1448 break;
1449 default:
1451 pLine->SetStartPosX( (sal_uInt16)nStartX ); // FI, LI
1453 break;
1456 // -----------------------------------------------------------------
1457 // pruefen, ob die Zeile neu ausgegeben werden muss...
1458 // -----------------------------------------------------------------
1459 pLine->SetInvalid();
1461 // Wenn eine Portion umgebrochen wurde sind ggf. viel zu viele Positionen
1462 // im CharPosArray:
1463 if ( bCalcCharPositions )
1465 sal_uInt16 nLen = pLine->GetLen();
1466 sal_uInt16 nCount = pLine->GetCharPosArray().Count();
1467 if ( nCount > nLen )
1468 pLine->GetCharPosArray().Remove( nLen, nCount-nLen );
1471 if ( GetTextRanger() )
1473 if ( nTextXOffset )
1474 pLine->SetStartPosX( (sal_uInt16) ( pLine->GetStartPosX() + nTextXOffset ) );
1475 if ( nTextExtraYOffset )
1477 pLine->SetHeight( (sal_uInt16) ( pLine->GetHeight() + nTextExtraYOffset ), 0, pLine->GetHeight() );
1478 pLine->SetMaxAscent( (sal_uInt16) ( pLine->GetMaxAscent() + nTextExtraYOffset ) );
1482 // Fuer kleiner 0 noch ueberlegen!
1483 if ( pParaPortion->IsSimpleInvalid() /* && ( nInvalidDiff > 0 ) */ )
1485 // Aenderung durch einfache Textaenderung...
1486 // Formatierung nicht abbrechen, da Portions evtl. wieder
1487 // gesplittet werden muessen!
1488 // Wenn irgendwann mal abbrechbar, dann fogende Zeilen Validieren!
1489 // Aber ggf. als Valid markieren, damit weniger Ausgabe...
1490 if ( pLine->GetEnd() < nInvalidStart )
1492 if ( *pLine == aSaveLine )
1494 pLine->SetValid();
1497 else
1499 sal_uInt16 nStart = pLine->GetStart();
1500 sal_uInt16 nEnd = pLine->GetEnd();
1502 if ( nStart > nInvalidEnd )
1504 if ( ( ( nStart-nInvalidDiff ) == aSaveLine.GetStart() ) &&
1505 ( ( nEnd-nInvalidDiff ) == aSaveLine.GetEnd() ) )
1507 pLine->SetValid();
1508 if ( bCalcCharPositions && bQuickFormat )
1510 bCalcCharPositions = sal_False;
1511 bLineBreak = sal_False;
1512 pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
1513 break;
1517 else if ( bCalcCharPositions && bQuickFormat && ( nEnd > nInvalidEnd) )
1519 // Wenn die ungueltige Zeile so endet, dass die naechste an
1520 // der 'gleichen' Textstelle wie vorher beginnt, also nicht
1521 // anders umgebrochen wird, brauche ich dort auch nicht die
1522 // textbreiten neu bestimmen:
1523 if ( nEnd == ( aSaveLine.GetEnd() + nInvalidDiff ) )
1525 bCalcCharPositions = sal_False;
1526 bLineBreak = sal_False;
1527 pParaPortion->CorrectValuesBehindLastFormattedLine( nLine );
1528 break;
1534 if ( !bSameLineAgain )
1536 nIndex = pLine->GetEnd(); // naechste Zeile Start = letzte Zeile Ende
1537 // weil nEnd hinter das letzte Zeichen zeigt!
1539 sal_uInt16 nEndPortion = pLine->GetEndPortion();
1541 // Naechste Zeile oder ggf. neue Zeile....
1542 pLine = 0;
1543 if ( nLine < pParaPortion->GetLines().Count()-1 )
1544 pLine = pParaPortion->GetLines().GetObject( ++nLine );
1545 if ( pLine && ( nIndex >= pNode->Len() ) )
1547 nDelFromLine = nLine;
1548 break;
1550 if ( !pLine )
1552 if ( nIndex < pNode->Len() )
1554 pLine = new EditLine;
1555 pParaPortion->GetLines().Insert( pLine, ++nLine );
1557 else if ( nIndex && bLineBreak && GetTextRanger() )
1559 // normaly CreateAndInsertEmptyLine would be called, but I want to use
1560 // CreateLines, so I need Polygon code only here...
1561 TextPortion* pDummyPortion = new TextPortion( 0 );
1562 pParaPortion->GetTextPortions().Insert( pDummyPortion, pParaPortion->GetTextPortions().Count() );
1563 pLine = new EditLine;
1564 pParaPortion->GetLines().Insert( pLine, ++nLine );
1565 bForceOneRun = TRUE;
1566 bProcessingEmptyLine = TRUE;
1569 if ( pLine )
1571 aSaveLine = *pLine;
1572 pLine->SetStart( nIndex );
1573 pLine->SetEnd( nIndex );
1574 pLine->SetStartPortion( nEndPortion+1 );
1575 pLine->SetEndPortion( nEndPortion+1 );
1578 } // while ( Index < Len )
1580 if ( nDelFromLine != 0xFFFF )
1581 pParaPortion->GetLines().DeleteFromLine( nDelFromLine );
1583 DBG_ASSERT( pParaPortion->GetLines().Count(), "Keine Zeile nach CreateLines!" );
1585 if ( bLineBreak == sal_True )
1586 CreateAndInsertEmptyLine( pParaPortion, nStartPosY );
1588 delete[] pBuf;
1590 sal_Bool bHeightChanged = FinishCreateLines( pParaPortion );
1592 if ( bMapChanged )
1593 GetRefDevice()->Pop();
1595 GetRefDevice()->Pop();
1597 return bHeightChanged;
1600 void ImpEditEngine::CreateAndInsertEmptyLine( ParaPortion* pParaPortion, sal_uInt32 )
1602 DBG_ASSERT( !GetTextRanger(), "Don't use CreateAndInsertEmptyLine with a polygon!" );
1604 EditLine* pTmpLine = new EditLine;
1605 pTmpLine->SetStart( pParaPortion->GetNode()->Len() );
1606 pTmpLine->SetEnd( pParaPortion->GetNode()->Len() );
1607 pParaPortion->GetLines().Insert( pTmpLine, pParaPortion->GetLines().Count() );
1609 sal_Bool bLineBreak = pParaPortion->GetNode()->Len() ? sal_True : sal_False;
1610 sal_Int32 nSpaceBefore = 0;
1611 sal_Int32 nSpaceBeforeAndMinLabelWidth = GetSpaceBeforeAndMinLabelWidth( pParaPortion->GetNode(), &nSpaceBefore );
1612 const SvxLRSpaceItem& rLRItem = GetLRSpaceItem( pParaPortion->GetNode() );
1613 const SvxLineSpacingItem& rLSItem = (const SvxLineSpacingItem&)pParaPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL );
1614 short nStartX = GetXValue( (short)(rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBefore));
1616 Rectangle aBulletArea = Rectangle( Point(), Point() );
1617 if ( bLineBreak == sal_True )
1619 nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
1621 else
1623 aBulletArea = GetEditEnginePtr()->GetBulletArea( GetParaPortions().GetPos( pParaPortion ) );
1624 if ( aBulletArea.Right() > 0 )
1625 pParaPortion->SetBulletX( (sal_uInt16) GetXValue( aBulletArea.Right() ) );
1626 else
1627 pParaPortion->SetBulletX( 0 ); // Falls Bullet falsch eingestellt.
1628 if ( pParaPortion->GetBulletX() > nStartX )
1630 nStartX = (short)GetXValue( rLRItem.GetTxtLeft() + rLRItem.GetTxtFirstLineOfst() + nSpaceBeforeAndMinLabelWidth );
1631 if ( pParaPortion->GetBulletX() > nStartX )
1632 nStartX = pParaPortion->GetBulletX();
1636 SvxFont aTmpFont;
1637 SeekCursor( pParaPortion->GetNode(), bLineBreak ? pParaPortion->GetNode()->Len() : 0, aTmpFont );
1638 aTmpFont.SetPhysFont( pRefDev );
1640 TextPortion* pDummyPortion = new TextPortion( 0 );
1641 pDummyPortion->GetSize() = aTmpFont.GetPhysTxtSize( pRefDev, String() );
1642 if ( IsFixedCellHeight() )
1643 pDummyPortion->GetSize().Height() = ImplCalculateFontIndependentLineSpacing( aTmpFont.GetHeight() );
1644 pParaPortion->GetTextPortions().Insert( pDummyPortion, pParaPortion->GetTextPortions().Count() );
1645 FormatterFontMetric aFormatterMetrics;
1646 RecalcFormatterFontMetrics( aFormatterMetrics, aTmpFont );
1647 pTmpLine->SetMaxAscent( aFormatterMetrics.nMaxAscent );
1648 pTmpLine->SetHeight( (sal_uInt16) pDummyPortion->GetSize().Height() );
1649 sal_uInt16 nLineHeight = aFormatterMetrics.GetHeight();
1650 if ( nLineHeight > pTmpLine->GetHeight() )
1651 pTmpLine->SetHeight( nLineHeight );
1653 if ( !aStatus.IsOutliner() )
1655 USHORT nPara = GetParaPortions().GetPos( pParaPortion );
1656 SvxAdjust eJustification = GetJustification( nPara );
1657 long nMaxLineWidth = !IsVertical() ? aPaperSize.Width() : aPaperSize.Height();
1658 nMaxLineWidth -= GetXValue( rLRItem.GetRight() );
1659 long nTextXOffset = 0;
1660 if ( nMaxLineWidth < 0 )
1661 nMaxLineWidth = 1;
1662 if ( eJustification == SVX_ADJUST_CENTER )
1663 nStartX = sal::static_int_cast< short >(nMaxLineWidth / 2);
1664 else if ( eJustification == SVX_ADJUST_RIGHT )
1665 nStartX = sal::static_int_cast< short >(nMaxLineWidth);
1667 nStartX = sal::static_int_cast< short >(nStartX + nTextXOffset);
1670 pTmpLine->SetStartPosX( nStartX );
1672 if ( !aStatus.IsOutliner() )
1674 if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN )
1676 sal_uInt16 nMinHeight = rLSItem.GetLineHeight();
1677 sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1678 if ( nTxtHeight < nMinHeight )
1680 // Der Ascent muss um die Differenz angepasst werden:
1681 long nDiff = nMinHeight - nTxtHeight;
1682 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff) );
1683 pTmpLine->SetHeight( nMinHeight, nTxtHeight );
1686 else if ( rLSItem.GetLineSpaceRule() == SVX_LINE_SPACE_FIX )
1688 sal_uInt16 nFixHeight = rLSItem.GetLineHeight();
1689 sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1691 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + ( nFixHeight - nTxtHeight ) ) );
1692 pTmpLine->SetHeight( nFixHeight, nTxtHeight );
1694 else if ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_PROP )
1696 USHORT nPara = GetParaPortions().GetPos( pParaPortion );
1697 if ( nPara || IsFixedCellHeight() || pTmpLine->GetStartPortion() ) // Nicht die aller erste Zeile
1699 // #100508# There are documents with PropLineSpace 0, why?
1700 // (cmc: re above question :-) such documents can be seen by importing a .ppt
1701 if ( rLSItem.GetPropLineSpace() && ( rLSItem.GetPropLineSpace() != 100 ) )
1703 sal_uInt16 nTxtHeight = pTmpLine->GetHeight();
1704 sal_Int32 nH = nTxtHeight;
1705 nH *= rLSItem.GetPropLineSpace();
1706 nH /= 100;
1707 // Der Ascent muss um die Differenz angepasst werden:
1708 long nDiff = pTmpLine->GetHeight() - nH;
1709 if ( nDiff > pTmpLine->GetMaxAscent() )
1710 nDiff = pTmpLine->GetMaxAscent();
1711 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() - nDiff) );
1712 pTmpLine->SetHeight( (sal_uInt16)nH, nTxtHeight );
1718 if ( !bLineBreak )
1720 long nMinHeight = aBulletArea.GetHeight();
1721 if ( nMinHeight > (long)pTmpLine->GetHeight() )
1723 long nDiff = nMinHeight - (long)pTmpLine->GetHeight();
1724 // nDiff auf oben und unten verteilen.
1725 pTmpLine->SetMaxAscent( (sal_uInt16)(pTmpLine->GetMaxAscent() + nDiff/2) );
1726 pTmpLine->SetHeight( (sal_uInt16)nMinHeight );
1729 else
1731 // -2: Die neue ist bereits eingefuegt.
1732 #ifdef DBG_UTIL
1733 EditLine* pLastLine = pParaPortion->GetLines().GetObject( pParaPortion->GetLines().Count()-2 );
1734 DBG_ASSERT( pLastLine, "Weicher Umbruch, keine Zeile ?!" );
1735 DBG_ASSERT( pLastLine->GetEnd() == pParaPortion->GetNode()->Len(), "Doch anders?" );
1736 #endif
1737 // pTmpLine->SetStart( pLastLine->GetEnd() );
1738 // pTmpLine->SetEnd( pLastLine->GetEnd() );
1739 sal_uInt16 nPos = (sal_uInt16) pParaPortion->GetTextPortions().Count() - 1 ;
1740 pTmpLine->SetStartPortion( nPos );
1741 pTmpLine->SetEndPortion( nPos );
1745 sal_Bool ImpEditEngine::FinishCreateLines( ParaPortion* pParaPortion )
1747 // CalcCharPositions( pParaPortion );
1748 pParaPortion->SetValid();
1749 long nOldHeight = pParaPortion->GetHeight();
1750 // sal_uInt16 nPos = GetParaPortions().GetPos( pParaPortion );
1751 // DBG_ASSERT( nPos != USHRT_MAX, "FinishCreateLines: Portion nicht in Liste!" );
1752 // ParaPortion* pPrev = nPos ? GetParaPortions().GetObject( nPos-1 ) : 0;
1753 CalcHeight( pParaPortion );
1755 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "FinishCreateLines: Keine Text-Portion?" );
1756 sal_Bool bRet = ( pParaPortion->GetHeight() != nOldHeight );
1757 return bRet;
1760 void ImpEditEngine::ImpBreakLine( ParaPortion* pParaPortion, EditLine* pLine, TextPortion* pPortion, sal_uInt16 nPortionStart, long nRemainingWidth, sal_Bool bCanHyphenate )
1762 ContentNode* const pNode = pParaPortion->GetNode();
1764 sal_uInt16 nBreakInLine = nPortionStart - pLine->GetStart();
1765 sal_uInt16 nMax = nBreakInLine + pPortion->GetLen();
1766 while ( ( nBreakInLine < nMax ) && ( pLine->GetCharPosArray()[nBreakInLine] < nRemainingWidth ) )
1767 nBreakInLine++;
1769 sal_uInt16 nMaxBreakPos = nBreakInLine + pLine->GetStart();
1770 sal_uInt16 nBreakPos = 0xFFFF;
1772 sal_Bool bCompressBlank = sal_False;
1773 sal_Bool bHyphenated = sal_False;
1774 sal_Bool bHangingPunctuation = sal_False;
1775 sal_Unicode cAlternateReplChar = 0;
1776 sal_Unicode cAlternateExtraChar = 0;
1778 if ( ( nMaxBreakPos < ( nMax + pLine->GetStart() ) ) && ( pNode->GetChar( nMaxBreakPos ) == ' ' ) )
1780 // Break behind the blank, blank will be compressed...
1781 nBreakPos = nMaxBreakPos + 1;
1782 bCompressBlank = sal_True;
1784 else
1786 sal_uInt16 nMinBreakPos = pLine->GetStart();
1787 USHORT nAttrs = pNode->GetCharAttribs().GetAttribs().Count();
1788 for ( USHORT nAttr = nAttrs; nAttr; )
1790 EditCharAttrib* pAttr = pNode->GetCharAttribs().GetAttribs()[--nAttr];
1791 if ( pAttr->IsFeature() && ( pAttr->GetEnd() > nMinBreakPos ) && ( pAttr->GetEnd() <= nMaxBreakPos ) )
1793 nMinBreakPos = pAttr->GetEnd();
1794 break;
1798 lang::Locale aLocale = GetLocale( EditPaM( pNode, nMaxBreakPos ) );
1800 Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
1801 OUString aText( *pNode );
1802 Reference< XHyphenator > xHyph;
1803 if ( bCanHyphenate )
1804 xHyph = GetHyphenator();
1805 i18n::LineBreakHyphenationOptions aHyphOptions( xHyph, Sequence< PropertyValue >(), 1 );
1806 i18n::LineBreakUserOptions aUserOptions;
1808 const i18n::ForbiddenCharacters* pForbidden = GetForbiddenCharsTable()->GetForbiddenCharacters( SvxLocaleToLanguage( aLocale ), TRUE );
1809 aUserOptions.forbiddenBeginCharacters = pForbidden->beginLine;
1810 aUserOptions.forbiddenEndCharacters = pForbidden->endLine;
1811 aUserOptions.applyForbiddenRules = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_FORBIDDENRULES )).GetValue();
1812 aUserOptions.allowPunctuationOutsideMargin = ((const SfxBoolItem&)pNode->GetContentAttribs().GetItem( EE_PARA_HANGINGPUNCTUATION )).GetValue();
1813 aUserOptions.allowHyphenateEnglish = FALSE;
1815 i18n::LineBreakResults aLBR = _xBI->getLineBreak( *pNode, nMaxBreakPos, aLocale, nMinBreakPos, aHyphOptions, aUserOptions );
1816 nBreakPos = (USHORT)aLBR.breakIndex;
1818 // BUG in I18N - under special condition (break behind field, #87327#) breakIndex is < nMinBreakPos
1819 if ( nBreakPos < nMinBreakPos )
1821 nBreakPos = nMinBreakPos;
1823 else if ( ( nBreakPos > nMaxBreakPos ) && !aUserOptions.allowPunctuationOutsideMargin )
1825 DBG_ERROR( "I18N: XBreakIterator::getLineBreak returns position > Max" );
1826 nBreakPos = nMaxBreakPos;
1829 // #101795# nBreakPos can never be outside the portion, even not with hangig punctuation
1830 if ( nBreakPos > nMaxBreakPos )
1831 nBreakPos = nMaxBreakPos;
1833 // BUG in I18N - the japanese dot is in the next line!
1834 // !!! Testen!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1835 if ( (nBreakPos + ( aUserOptions.allowPunctuationOutsideMargin ? 0 : 1 ) ) <= nMaxBreakPos )
1837 sal_Unicode cFirstInNextLine = ( (nBreakPos+1) < pNode->Len() ) ? pNode->GetChar( nBreakPos ) : 0;
1838 if ( cFirstInNextLine == 12290 )
1839 nBreakPos++;
1842 bHangingPunctuation = ( nBreakPos > nMaxBreakPos ) ? sal_True : sal_False;
1843 pLine->SetHangingPunctuation( bHangingPunctuation );
1845 #ifndef SVX_LIGHT
1846 // Egal ob Trenner oder nicht: Das Wort nach dem Trenner durch
1847 // die Silbentrennung jagen...
1848 // nMaxBreakPos ist das letzte Zeichen was in die Zeile passt,
1849 // nBreakPos ist der Wort-Anfang
1850 // Ein Problem gibt es, wenn das Dok so schmal ist, dass ein Wort
1851 // auf mehr als Zwei Zeilen gebrochen wird...
1852 if ( !bHangingPunctuation && bCanHyphenate && GetHyphenator().is() )
1854 i18n::Boundary aBoundary = _xBI->getWordBoundary( *pNode, nBreakPos, GetLocale( EditPaM( pNode, nBreakPos ) ), ::com::sun::star::i18n::WordType::DICTIONARY_WORD, sal_True );
1855 // sal_uInt16 nWordStart = nBreakPos;
1856 // sal_uInt16 nBreakPos_OLD = nBreakPos;
1857 sal_uInt16 nWordStart = nBreakPos;
1858 sal_uInt16 nWordEnd = (USHORT) aBoundary.endPos;
1859 DBG_ASSERT( nWordEnd > nWordStart, "ImpBreakLine: Start >= End?" );
1861 USHORT nWordLen = nWordEnd - nWordStart;
1862 if ( ( nWordEnd >= nMaxBreakPos ) && ( nWordLen > 3 ) )
1864 // #104415# May happen, because getLineBreak may differ from getWordBoudary with DICTIONARY_WORD
1865 // DBG_ASSERT( nWordEnd >= nMaxBreakPos, "Hyph: Break?" );
1866 String aWord( *pNode, nWordStart, nWordLen );
1867 sal_uInt16 nMinTrail = nWordEnd-nMaxBreakPos+1; //+1: Vor dem angeknacksten Buchstaben
1868 Reference< XHyphenatedWord > xHyphWord;
1869 if (xHyphenator.is())
1870 xHyphWord = xHyphenator->hyphenate( aWord, aLocale, aWord.Len() - nMinTrail, Sequence< PropertyValue >() );
1871 if (xHyphWord.is())
1873 sal_Bool bAlternate = xHyphWord->isAlternativeSpelling();
1874 sal_uInt16 _nWordLen = 1 + xHyphWord->getHyphenPos();
1876 if ( ( _nWordLen >= 2 ) && ( (nWordStart+_nWordLen) >= (pLine->GetStart() + 2 ) ) )
1878 if ( !bAlternate )
1880 bHyphenated = sal_True;
1881 nBreakPos = nWordStart + _nWordLen;
1883 else
1885 String aAlt( xHyphWord->getHyphenatedWord() );
1887 // Wir gehen von zwei Faellen aus, die nun
1888 // vorliegen koennen:
1889 // 1) packen wird zu pak-ken
1890 // 2) Schiffahrt wird zu Schiff-fahrt
1891 // In Fall 1 muss ein Zeichen ersetzt werden,
1892 // in Fall 2 wird ein Zeichen hinzugefuegt.
1893 // Die Identifikation wird erschwert durch Worte wie
1894 // "Schiffahrtsbrennesseln", da der Hyphenator alle
1895 // Position des Wortes auftrennt und "Schifffahrtsbrennnesseln"
1896 // ermittelt. Wir koennen also eigentlich nicht unmittelbar vom
1897 // Index des AlternativWord auf aWord schliessen.
1899 // Das ganze geraffel wird durch eine Funktion am
1900 // Hyphenator vereinfacht werden, sobald AMA sie einbaut...
1901 sal_uInt16 nAltStart = _nWordLen - 1;
1902 sal_uInt16 nTxtStart = nAltStart - (aAlt.Len() - aWord.Len());
1903 sal_uInt16 nTxtEnd = nTxtStart;
1904 sal_uInt16 nAltEnd = nAltStart;
1906 // Die Bereiche zwischen den nStart und nEnd ist
1907 // die Differenz zwischen Alternativ- und OriginalString.
1908 while( nTxtEnd < aWord.Len() && nAltEnd < aAlt.Len() &&
1909 aWord.GetChar(nTxtEnd) != aAlt.GetChar(nAltEnd) )
1911 ++nTxtEnd;
1912 ++nAltEnd;
1915 // Wenn ein Zeichen hinzugekommen ist, dann bemerken wir es jetzt:
1916 if( nAltEnd > nTxtEnd && nAltStart == nAltEnd &&
1917 aWord.GetChar( nTxtEnd ) == aAlt.GetChar(nAltEnd) )
1919 ++nAltEnd;
1920 ++nTxtStart;
1921 ++nTxtEnd;
1924 DBG_ASSERT( ( nAltEnd - nAltStart ) == 1, "Alternate: Falsche Annahme!" );
1926 if ( nTxtEnd > nTxtStart )
1927 cAlternateReplChar = aAlt.GetChar( nAltStart );
1928 else
1929 cAlternateExtraChar = aAlt.GetChar( nAltStart );
1931 bHyphenated = sal_True;
1932 nBreakPos = nWordStart + nTxtStart;
1933 if ( cAlternateReplChar )
1934 nBreakPos++;
1941 #endif // !SVX_LIGHT
1943 if ( nBreakPos <= pLine->GetStart() )
1945 // keine Trenner in Zeile => abhacken !
1946 nBreakPos = nMaxBreakPos;
1947 // MT: I18N nextCharacters !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1948 if ( nBreakPos <= pLine->GetStart() )
1949 nBreakPos = pLine->GetStart() + 1; // Sonst Endlosschleife!
1953 // die angeknackste Portion ist die End-Portion
1954 pLine->SetEnd( nBreakPos );
1956 sal_uInt16 nEndPortion = SplitTextPortion( pParaPortion, nBreakPos, pLine );
1958 if ( !bCompressBlank && !bHangingPunctuation )
1960 // #96187# When justification is not SVX_ADJUST_LEFT, it's important to compress
1961 // the trailing space even if there is enough room for the space...
1962 // Don't check for SVX_ADJUST_LEFT, doesn't matter to compress in this case too...
1963 DBG_ASSERT( nBreakPos > pLine->GetStart(), "ImpBreakLines - BreakPos not expected!" );
1964 if ( pNode->GetChar( nBreakPos-1 ) == ' ' )
1965 bCompressBlank = sal_True;
1968 if ( bCompressBlank || bHangingPunctuation )
1970 TextPortion* pTP = pParaPortion->GetTextPortions().GetObject( nEndPortion );
1971 DBG_ASSERT( pTP->GetKind() == PORTIONKIND_TEXT, "BlankRubber: Keine TextPortion!" );
1972 DBG_ASSERT( nBreakPos > pLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
1973 sal_uInt16 nPosInArray = nBreakPos - 1 - pLine->GetStart();
1974 pTP->GetSize().Width() = ( nPosInArray && ( pTP->GetLen() > 1 ) ) ? pLine->GetCharPosArray()[ nPosInArray-1 ] : 0;
1975 pLine->GetCharPosArray()[ nPosInArray ] = pTP->GetSize().Width();
1977 else if ( bHyphenated )
1979 // Eine Portion fuer den Trenner einbauen...
1980 TextPortion* pHyphPortion = new TextPortion( 0 );
1981 pHyphPortion->GetKind() = PORTIONKIND_HYPHENATOR;
1982 String aHyphText( CH_HYPH );
1983 if ( cAlternateReplChar )
1985 TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nEndPortion );
1986 DBG_ASSERT( pPrev && pPrev->GetLen(), "Hyphenate: Prev portion?!" );
1987 pPrev->SetLen( pPrev->GetLen() - 1 );
1988 pHyphPortion->SetLen( 1 );
1989 pHyphPortion->SetExtraValue( cAlternateReplChar );
1990 // Breite der Portion davor korrigieren:
1991 pPrev->GetSize().Width() =
1992 pLine->GetCharPosArray()[ nBreakPos-1 - pLine->GetStart() - 1 ];
1994 else if ( cAlternateExtraChar )
1996 pHyphPortion->SetExtraValue( cAlternateExtraChar );
1997 aHyphText.Insert( cAlternateExtraChar, 0 );
2000 // Breite der Hyph-Portion ermitteln:
2001 SvxFont aFont;
2002 SeekCursor( pParaPortion->GetNode(), nBreakPos, aFont );
2003 aFont.SetPhysFont( GetRefDevice() );
2004 pHyphPortion->GetSize().Height() = GetRefDevice()->GetTextHeight();
2005 pHyphPortion->GetSize().Width() = GetRefDevice()->GetTextWidth( aHyphText );
2007 pParaPortion->GetTextPortions().Insert( pHyphPortion, ++nEndPortion );
2009 pLine->SetEndPortion( nEndPortion );
2012 void ImpEditEngine::ImpAdjustBlocks( ParaPortion* pParaPortion, EditLine* pLine, long nRemainingSpace )
2014 DBG_ASSERT( nRemainingSpace > 0, "AdjustBlocks: Etwas zuwenig..." );
2015 DBG_ASSERT( pLine, "AdjustBlocks: Zeile ?!" );
2016 if ( ( nRemainingSpace < 0 ) || pLine->IsEmpty() )
2017 return ;
2019 const USHORT nFirstChar = pLine->GetStart();
2020 const USHORT nLastChar = pLine->GetEnd() -1; // Last zeigt dahinter
2021 ContentNode* pNode = pParaPortion->GetNode();
2023 DBG_ASSERT( nLastChar < pNode->Len(), "AdjustBlocks: Out of range!" );
2025 // Search blanks or Kashidas...
2026 SvUShorts aPositions;
2027 USHORT nChar;
2028 for ( nChar = nFirstChar; nChar <= nLastChar; nChar++ )
2030 if ( pNode->GetChar(nChar) == ' ' )
2032 // Don't use blank if language is arabic
2033 LanguageType eLang = GetLanguage( EditPaM( pNode, nChar ) );
2034 if ( MsLangId::getPrimaryLanguage( eLang) != LANGUAGE_ARABIC_PRIMARY_ONLY )
2035 aPositions.Insert( nChar, aPositions.Count() );
2039 // Kashidas ?
2040 ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions );
2043 if ( !aPositions.Count() )
2044 return;
2046 // Wenn das letzte Zeichen ein Blank ist, will ich es nicht haben!
2047 // Die Breite muss auf die Blocker davor verteilt werden...
2048 // Aber nicht, wenn es das einzige ist
2049 if ( ( pNode->GetChar( nLastChar ) == ' ' ) && ( aPositions.Count() > 1 ) && ( MsLangId::getPrimaryLanguage( GetLanguage( EditPaM( pNode, nLastChar ) ) ) != LANGUAGE_ARABIC_PRIMARY_ONLY ) )
2051 aPositions.Remove( aPositions.Count()-1, 1 );
2052 USHORT nPortionStart, nPortion;
2053 nPortion = pParaPortion->GetTextPortions().FindPortion( nLastChar+1, nPortionStart );
2054 TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
2055 long nRealWidth = pLine->GetCharPosArray()[nLastChar-nFirstChar];
2056 long nBlankWidth = nRealWidth;
2057 if ( nLastChar > nPortionStart )
2058 nBlankWidth -= pLine->GetCharPosArray()[nLastChar-nFirstChar-1];
2059 // Evtl. ist das Blank schon in ImpBreakLine abgezogen worden:
2060 if ( nRealWidth == pLastPortion->GetSize().Width() )
2062 // Beim letzten Zeichen muss die Portion hinter dem Blank aufhoeren
2063 // => Korrektur vereinfachen:
2064 DBG_ASSERT( ( nPortionStart + pLastPortion->GetLen() ) == ( nLastChar+1 ), "Blank doch nicht am Portion-Ende?!" );
2065 pLastPortion->GetSize().Width() -= nBlankWidth;
2066 nRemainingSpace += nBlankWidth;
2068 pLine->GetCharPosArray()[nLastChar-nFirstChar] -= nBlankWidth;
2071 USHORT nGaps = aPositions.Count();
2072 const long nMore4Everyone = nRemainingSpace / nGaps;
2073 long nSomeExtraSpace = nRemainingSpace - nMore4Everyone*nGaps;
2075 DBG_ASSERT( nSomeExtraSpace < (long)nGaps, "AdjustBlocks: ExtraSpace zu gross" );
2076 DBG_ASSERT( nSomeExtraSpace >= 0, "AdjustBlocks: ExtraSpace < 0 " );
2078 // Die Positionen im Array und die Portion-Breiten korrigieren:
2079 // Letztes Zeichen wird schon nicht mehr beachtet...
2080 for ( USHORT n = 0; n < aPositions.Count(); n++ )
2082 nChar = aPositions[n];
2083 if ( nChar < nLastChar )
2085 USHORT nPortionStart, nPortion;
2086 nPortion = pParaPortion->GetTextPortions().FindPortion( nChar, nPortionStart );
2087 TextPortion* pLastPortion = pParaPortion->GetTextPortions()[ nPortion ];
2089 // Die Breite der Portion:
2090 pLastPortion->GetSize().Width() += nMore4Everyone;
2091 if ( nSomeExtraSpace )
2092 pLastPortion->GetSize().Width()++;
2094 // Correct positions in array
2095 // Even for kashidas just change positions, VCL will then draw the kashida automaticly
2096 USHORT nPortionEnd = nPortionStart + pLastPortion->GetLen();
2097 for ( USHORT _n = nChar; _n < nPortionEnd; _n++ )
2099 pLine->GetCharPosArray()[_n-nFirstChar] += nMore4Everyone;
2100 if ( nSomeExtraSpace )
2101 pLine->GetCharPosArray()[_n-nFirstChar]++;
2104 if ( nSomeExtraSpace )
2105 nSomeExtraSpace--;
2109 // Now the text width contains the extra width...
2110 pLine->SetTextWidth( pLine->GetTextWidth() + nRemainingSpace );
2113 void ImpEditEngine::ImpFindKashidas( ContentNode* pNode, USHORT nStart, USHORT nEnd, SvUShorts& rArray )
2115 // the search has to be performed on a per word base
2117 EditSelection aWordSel( EditPaM( pNode, nStart ) );
2118 aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2119 if ( aWordSel.Min().GetIndex() < nStart )
2120 aWordSel.Min().GetIndex() = nStart;
2122 while ( ( aWordSel.Min().GetNode() == pNode ) && ( aWordSel.Min().GetIndex() < nEnd ) )
2124 USHORT nSavPos = aWordSel.Max().GetIndex();
2125 if ( aWordSel.Max().GetIndex() > nEnd )
2126 aWordSel.Max().GetIndex() = nEnd;
2128 String aWord = GetSelected( aWordSel );
2130 // restore selection for proper iteration at the end of the function
2131 aWordSel.Max().GetIndex() = nSavPos;
2133 xub_StrLen nIdx = 0;
2134 xub_StrLen nKashidaPos = STRING_LEN;
2135 xub_Unicode cCh;
2136 xub_Unicode cPrevCh = 0;
2138 while ( nIdx < aWord.Len() )
2140 cCh = aWord.GetChar( nIdx );
2142 // 1. Priority:
2143 // after user inserted kashida
2144 if ( 0x640 == cCh )
2146 nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
2147 break;
2150 // 2. Priority:
2151 // after a Seen or Sad
2152 if ( nIdx + 1 < aWord.Len() &&
2153 ( 0x633 == cCh || 0x635 == cCh ) )
2155 nKashidaPos = aWordSel.Min().GetIndex() + nIdx;
2156 break;
2159 // 3. Priority:
2160 // before final form of Teh Marbuta, Hah, Dal
2161 // 4. Priority:
2162 // before final form of Alef, Lam or Kaf
2163 if ( nIdx && nIdx + 1 == aWord.Len() &&
2164 ( 0x629 == cCh || 0x62D == cCh || 0x62F == cCh ||
2165 0x627 == cCh || 0x644 == cCh || 0x643 == cCh ) )
2167 DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2169 // check if character is connectable to previous character,
2170 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2172 nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2173 break;
2177 // 5. Priority:
2178 // before media Bah
2179 if ( nIdx && nIdx + 1 < aWord.Len() && 0x628 == cCh )
2181 DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2183 // check if next character is Reh, Yeh or Alef Maksura
2184 xub_Unicode cNextCh = aWord.GetChar( nIdx + 1 );
2186 if ( 0x631 == cNextCh || 0x64A == cNextCh ||
2187 0x649 == cNextCh )
2189 // check if character is connectable to previous character,
2190 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2191 nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2195 // 6. Priority:
2196 // other connecting possibilities
2197 if ( nIdx && nIdx + 1 == aWord.Len() &&
2198 0x60C <= cCh && 0x6FE >= cCh )
2200 DBG_ASSERT( 0 != cPrevCh, "No previous character" );
2202 // check if character is connectable to previous character,
2203 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
2205 // only choose this position if we did not find
2206 // a better one:
2207 if ( STRING_LEN == nKashidaPos )
2208 nKashidaPos = aWordSel.Min().GetIndex() + nIdx - 1;
2209 break;
2213 // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
2214 // Damma, Kasra, Shadda and Sukun when checking if
2215 // a character can be connected to previous character.
2216 if ( cCh < 0x64B || cCh > 0x652 )
2217 cPrevCh = cCh;
2219 ++nIdx;
2220 } // end of current word
2222 if ( STRING_LEN != nKashidaPos )
2223 rArray.Insert( nKashidaPos, rArray.Count() );
2225 aWordSel = WordRight( aWordSel.Max(), ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2226 aWordSel = SelectWord( aWordSel, ::com::sun::star::i18n::WordType::DICTIONARY_WORD );
2230 sal_uInt16 ImpEditEngine::SplitTextPortion( ParaPortion* pPortion, sal_uInt16 nPos, EditLine* pCurLine )
2232 DBG_ASSERT( pPortion, "SplitTextPortion: Welche ?" );
2234 // Die Portion bei nPos wird geplittet, wenn bei nPos nicht
2235 // sowieso ein Wechsel ist
2236 if ( nPos == 0 )
2237 return 0;
2239 sal_uInt16 nSplitPortion;
2240 sal_uInt16 nTmpPos = 0;
2241 TextPortion* pTextPortion = 0;
2242 sal_uInt16 nPortions = pPortion->GetTextPortions().Count();
2243 for ( nSplitPortion = 0; nSplitPortion < nPortions; nSplitPortion++ )
2245 TextPortion* pTP = pPortion->GetTextPortions().GetObject(nSplitPortion);
2246 nTmpPos = nTmpPos + pTP->GetLen();
2247 if ( nTmpPos >= nPos )
2249 if ( nTmpPos == nPos ) // dann braucht nichts geteilt werden
2251 // Skip Portions with ExtraSpace
2252 // while ( ( (nSplitPortion+1) < nPortions ) && (pPortion->GetTextPortions().GetObject(nSplitPortion+1)->GetKind() == PORTIONKIND_EXTRASPACE ) )
2253 // nSplitPortion++;
2255 return nSplitPortion;
2257 pTextPortion = pTP;
2258 break;
2262 DBG_ASSERT( pTextPortion, "Position ausserhalb des Bereichs!" );
2263 DBG_ASSERT( pTextPortion->GetKind() == PORTIONKIND_TEXT, "SplitTextPortion: Keine TextPortion!" );
2265 sal_uInt16 nOverlapp = nTmpPos - nPos;
2266 pTextPortion->GetLen() = pTextPortion->GetLen() - nOverlapp;
2267 TextPortion* pNewPortion = new TextPortion( nOverlapp );
2268 pPortion->GetTextPortions().Insert( pNewPortion, nSplitPortion+1 );
2269 // Groessen setzen:
2270 if ( pCurLine )
2272 // Kein neues GetTextSize, sondern Werte aus Array verwenden:
2273 DBG_ASSERT( nPos > pCurLine->GetStart(), "SplitTextPortion am Anfang der Zeile?" );
2274 pTextPortion->GetSize().Width() = pCurLine->GetCharPosArray()[ nPos-pCurLine->GetStart()-1 ];
2276 if ( pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed )
2278 // We need the original size from the portion
2279 USHORT nTxtPortionStart = pPortion->GetTextPortions().GetStartPos( nSplitPortion );
2280 SvxFont aTmpFont( pPortion->GetNode()->GetCharAttribs().GetDefFont() );
2281 SeekCursor( pPortion->GetNode(), nTxtPortionStart+1, aTmpFont );
2282 aTmpFont.SetPhysFont( GetRefDevice() );
2283 GetRefDevice()->Push( PUSH_TEXTLANGUAGE );
2284 ImplInitDigitMode( GetRefDevice(), 0, 0, 0, aTmpFont.GetLanguage() );
2285 Size aSz = aTmpFont.QuickGetTextSize( GetRefDevice(), *pPortion->GetNode(), nTxtPortionStart, pTextPortion->GetLen(), NULL );
2286 GetRefDevice()->Pop();
2287 pTextPortion->GetExtraInfos()->nOrgWidth = aSz.Width();
2290 else
2291 pTextPortion->GetSize().Width() = (-1);
2293 return nSplitPortion;
2296 void ImpEditEngine::CreateTextPortions( ParaPortion* pParaPortion, sal_uInt16& rStart /* , sal_Bool bCreateBlockPortions */ )
2298 sal_uInt16 nStartPos = rStart;
2299 ContentNode* pNode = pParaPortion->GetNode();
2300 DBG_ASSERT( pNode->Len(), "CreateTextPortions sollte nicht fuer leere Absaetze verwendet werden!" );
2302 SortedPositions aPositions;
2303 aPositions.Insert( (sal_uInt32) 0 );
2305 sal_uInt16 nAttr = 0;
2306 EditCharAttrib* pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
2307 while ( pAttrib )
2309 // Start und Ende in das Array eintragen...
2310 // Die InsertMethode laesst keine doppelten Werte zu....
2311 aPositions.Insert( pAttrib->GetStart() );
2312 aPositions.Insert( pAttrib->GetEnd() );
2313 nAttr++;
2314 pAttrib = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr );
2316 aPositions.Insert( pNode->Len() );
2318 if ( !pParaPortion->aScriptInfos.Count() )
2319 ((ImpEditEngine*)this)->InitScriptTypes( GetParaPortions().GetPos( pParaPortion ) );
2321 const ScriptTypePosInfos& rTypes = pParaPortion->aScriptInfos;
2322 for ( USHORT nT = 0; nT < rTypes.Count(); nT++ )
2323 aPositions.Insert( rTypes[nT].nStartPos );
2325 const WritingDirectionInfos& rWritingDirections = pParaPortion->aWritingDirectionInfos;
2326 for ( USHORT nD = 0; nD < rWritingDirections.Count(); nD++ )
2327 aPositions.Insert( rWritingDirections[nD].nStartPos );
2329 if ( mpIMEInfos && mpIMEInfos->nLen && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) )
2331 sal_uInt16 nLastAttr = 0xFFFF;
2332 for( sal_uInt16 n = 0; n < mpIMEInfos->nLen; n++ )
2334 if ( mpIMEInfos->pAttribs[n] != nLastAttr )
2336 aPositions.Insert( mpIMEInfos->aPos.GetIndex() + n );
2337 nLastAttr = mpIMEInfos->pAttribs[n];
2340 aPositions.Insert( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen );
2343 // Ab ... loeschen:
2344 // Leider muss die Anzahl der TextPortions mit aPositions.Count()
2345 // nicht uebereinstimmen, da evtl. Zeilenumbrueche...
2346 sal_uInt16 nPortionStart = 0;
2347 sal_uInt16 nInvPortion = 0;
2348 sal_uInt16 nP;
2349 for ( nP = 0; nP < pParaPortion->GetTextPortions().Count(); nP++ )
2351 TextPortion* pTmpPortion = pParaPortion->GetTextPortions().GetObject(nP);
2352 nPortionStart = nPortionStart + pTmpPortion->GetLen();
2353 if ( nPortionStart >= nStartPos )
2355 nPortionStart = nPortionStart - pTmpPortion->GetLen();
2356 rStart = nPortionStart;
2357 nInvPortion = nP;
2358 break;
2361 DBG_ASSERT( nP < pParaPortion->GetTextPortions().Count() || !pParaPortion->GetTextPortions().Count(), "Nichts zum loeschen: CreateTextPortions" );
2362 if ( nInvPortion && ( nPortionStart+pParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen() > nStartPos ) )
2364 // lieber eine davor...
2365 // Aber nur wenn es mitten in der Portion war, sonst ist es evtl.
2366 // die einzige in der Zeile davor !
2367 nInvPortion--;
2368 nPortionStart = nPortionStart - pParaPortion->GetTextPortions().GetObject(nInvPortion)->GetLen();
2370 pParaPortion->GetTextPortions().DeleteFromPortion( nInvPortion );
2372 // Eine Portion kann auch durch einen Zeilenumbruch entstanden sein:
2373 aPositions.Insert( nPortionStart );
2375 sal_uInt16 nInvPos;
2376 #ifdef DBG_UTIL
2377 sal_Bool bFound =
2378 #endif
2379 aPositions.Seek_Entry( nPortionStart, &nInvPos );
2381 DBG_ASSERT( bFound && ( nInvPos < (aPositions.Count()-1) ), "InvPos ?!" );
2382 for ( sal_uInt16 i = nInvPos+1; i < aPositions.Count(); i++ )
2384 TextPortion* pNew = new TextPortion( (sal_uInt16)aPositions[i] - (sal_uInt16)aPositions[i-1] );
2385 pParaPortion->GetTextPortions().Insert( pNew, pParaPortion->GetTextPortions().Count());
2388 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine Portions?!" );
2389 #ifdef EDITDEBUG
2390 DBG_ASSERT( pParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
2391 #endif
2394 void ImpEditEngine::RecalcTextPortion( ParaPortion* pParaPortion, sal_uInt16 nStartPos, short nNewChars )
2396 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "Keine Portions!" );
2397 DBG_ASSERT( nNewChars, "RecalcTextPortion mit Diff == 0" );
2399 ContentNode* const pNode = pParaPortion->GetNode();
2400 if ( nNewChars > 0 )
2402 // Wenn an nStartPos ein Attribut beginnt/endet, faengt eine neue Portion
2403 // an, ansonsten wird die Portion an nStartPos erweitert.
2405 if ( pNode->GetCharAttribs().HasBoundingAttrib( nStartPos ) || IsScriptChange( EditPaM( pNode, nStartPos ) ) )
2407 sal_uInt16 nNewPortionPos = 0;
2408 if ( nStartPos )
2409 nNewPortionPos = SplitTextPortion( pParaPortion, nStartPos ) + 1;
2411 // Eine leere Portion kann hier stehen, wenn der Absatz leer war,
2412 // oder eine Zeile durch einen harten Zeilenumbruch entstanden ist.
2413 if ( ( nNewPortionPos < pParaPortion->GetTextPortions().Count() ) &&
2414 !pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen() )
2416 DBG_ASSERT( pParaPortion->GetTextPortions()[nNewPortionPos]->GetKind() == PORTIONKIND_TEXT, "Leere Portion war keine TextPortion!" );
2417 USHORT & r =
2418 pParaPortion->GetTextPortions()[nNewPortionPos]->GetLen();
2419 r = r + nNewChars;
2421 else
2423 TextPortion* pNewPortion = new TextPortion( nNewChars );
2424 pParaPortion->GetTextPortions().Insert( pNewPortion, nNewPortionPos );
2427 else
2429 sal_uInt16 nPortionStart;
2430 const sal_uInt16 nTP = pParaPortion->GetTextPortions().
2431 FindPortion( nStartPos, nPortionStart );
2432 TextPortion* const pTP = pParaPortion->GetTextPortions()[ nTP ];
2433 DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
2434 pTP->GetLen() = pTP->GetLen() + nNewChars;
2435 pTP->GetSize().Width() = (-1);
2438 else
2440 // Portion schrumpfen oder ggf. entfernen.
2441 // Vor Aufruf dieser Methode muss sichergestellt sein, dass
2442 // keine Portions in dem geloeschten Bereich lagen!
2444 // Es darf keine reinragende oder im Bereich startende Portion geben,
2445 // also muss nStartPos <= nPos <= nStartPos - nNewChars(neg.) sein
2446 sal_uInt16 nPortion = 0;
2447 sal_uInt16 nPos = 0;
2448 sal_uInt16 nEnd = nStartPos-nNewChars;
2449 sal_uInt16 nPortions = pParaPortion->GetTextPortions().Count();
2450 TextPortion* pTP = 0;
2451 for ( nPortion = 0; nPortion < nPortions; nPortion++ )
2453 pTP = pParaPortion->GetTextPortions()[ nPortion ];
2454 if ( ( nPos+pTP->GetLen() ) > nStartPos )
2456 DBG_ASSERT( nPos <= nStartPos, "Start falsch!" );
2457 DBG_ASSERT( nPos+pTP->GetLen() >= nEnd, "End falsch!" );
2458 break;
2460 nPos = nPos + pTP->GetLen();
2462 DBG_ASSERT( pTP, "RecalcTextPortion: Portion nicht gefunden" );
2463 if ( ( nPos == nStartPos ) && ( (nPos+pTP->GetLen()) == nEnd ) )
2465 // Portion entfernen;
2466 BYTE nType = pTP->GetKind();
2467 pParaPortion->GetTextPortions().Remove( nPortion );
2468 delete pTP;
2469 if ( nType == PORTIONKIND_LINEBREAK )
2471 TextPortion* pNext = pParaPortion->GetTextPortions()[ nPortion ];
2472 if ( pNext && !pNext->GetLen() )
2474 // Dummy-Portion entfernen
2475 pParaPortion->GetTextPortions().Remove( nPortion );
2476 delete pNext;
2480 else
2482 DBG_ASSERT( pTP->GetLen() > (-nNewChars), "Portion zu klein zum schrumpfen!" );
2483 pTP->GetLen() = pTP->GetLen() + nNewChars;
2486 // ganz am Schluss darf keine HYPHENATOR-Portion stehen bleiben...
2487 DBG_ASSERT( pParaPortion->GetTextPortions().Count(), "RecalcTextPortions: Keine mehr da!" );
2488 sal_uInt16 nLastPortion = pParaPortion->GetTextPortions().Count() - 1;
2489 pTP = pParaPortion->GetTextPortions().GetObject( nLastPortion );
2490 if ( pTP->GetKind() == PORTIONKIND_HYPHENATOR )
2492 // Portion wegschmeissen, ggf. die davor korrigieren, wenn
2493 // die Hyph-Portion ein Zeichen geschluckt hat...
2494 pParaPortion->GetTextPortions().Remove( nLastPortion );
2495 if ( nLastPortion && pTP->GetLen() )
2497 TextPortion* pPrev = pParaPortion->GetTextPortions().GetObject( nLastPortion - 1 );
2498 DBG_ASSERT( pPrev->GetKind() == PORTIONKIND_TEXT, "Portion?!" );
2499 pPrev->SetLen( pPrev->GetLen() + pTP->GetLen() );
2500 pPrev->GetSize().Width() = (-1);
2502 delete pTP;
2505 #ifdef EDITDEBUG
2506 DBG_ASSERT( pParaPortion->DbgCheckTextPortions(), "Portions kaputt?" );
2507 #endif
2510 void ImpEditEngine::SetTextRanger( TextRanger* pRanger )
2512 if ( pTextRanger != pRanger )
2514 delete pTextRanger;
2515 pTextRanger = pRanger;
2517 for ( sal_uInt16 nPara = 0; nPara < GetParaPortions().Count(); nPara++ )
2519 ParaPortion* pParaPortion = GetParaPortions().GetObject( nPara );
2520 pParaPortion->MarkSelectionInvalid( 0, pParaPortion->GetNode()->Len() );
2521 pParaPortion->GetLines().Reset();
2524 FormatFullDoc();
2525 UpdateViews( GetActiveView() );
2526 if ( GetUpdateMode() && GetActiveView() )
2527 pActiveView->ShowCursor( sal_False, sal_False );
2531 void ImpEditEngine::SetVertical( BOOL bVertical )
2533 if ( IsVertical() != bVertical )
2535 GetEditDoc().SetVertical( bVertical );
2536 sal_Bool bUseCharAttribs = ( aStatus.GetControlWord() & EE_CNTRL_USECHARATTRIBS ) ? sal_True : sal_False;
2537 GetEditDoc().CreateDefFont( bUseCharAttribs );
2538 if ( IsFormatted() )
2540 FormatFullDoc();
2541 UpdateViews( GetActiveView() );
2546 void ImpEditEngine::SetFixedCellHeight( BOOL bUseFixedCellHeight )
2548 if ( IsFixedCellHeight() != bUseFixedCellHeight )
2550 GetEditDoc().SetFixedCellHeight( bUseFixedCellHeight );
2551 if ( IsFormatted() )
2553 FormatFullDoc();
2554 UpdateViews( GetActiveView() );
2559 void ImpEditEngine::SeekCursor( ContentNode* pNode, sal_uInt16 nPos, SvxFont& rFont, OutputDevice* pOut, sal_uInt16 nIgnoreWhich )
2561 // Es war mal geplant, SeekCursor( nStartPos, nEndPos, ... ), damit nur
2562 // ab der StartPosition neu gesucht wird.
2563 // Problem: Es mussten zwei Listen beruecksichtigt/gefuehrt werden:
2564 // OrderedByStart,OrderedByEnd.
2566 if ( nPos > pNode->Len() )
2567 nPos = pNode->Len();
2569 rFont = pNode->GetCharAttribs().GetDefFont();
2571 short nScriptType = GetScriptType( EditPaM( pNode, nPos ) );
2572 if ( ( nScriptType == i18n::ScriptType::ASIAN ) || ( nScriptType == i18n::ScriptType::COMPLEX ) )
2574 const SvxFontItem& rFontItem = (const SvxFontItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ) );
2575 rFont.SetName( rFontItem.GetFamilyName() );
2576 rFont.SetFamily( rFontItem.GetFamily() );
2577 rFont.SetPitch( rFontItem.GetPitch() );
2578 rFont.SetCharSet( rFontItem.GetCharSet() );
2579 Size aSz( rFont.GetSize() );
2580 aSz.Height() = ((const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ) ) ).GetHeight();
2581 rFont.SetSize( aSz );
2582 rFont.SetWeight( ((const SvxWeightItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ))).GetWeight() );
2583 rFont.SetItalic( ((const SvxPostureItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_ITALIC, nScriptType ))).GetPosture() );
2584 rFont.SetLanguage( ((const SvxLanguageItem&)pNode->GetContentAttribs().GetItem( GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ))).GetLanguage() );
2587 sal_uInt16 nRelWidth = ((const SvxCharScaleWidthItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH)).GetValue();
2589 if ( pOut )
2591 const SvxUnderlineItem& rTextLineColor = (const SvxUnderlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_UNDERLINE );
2592 if ( rTextLineColor.GetColor() != COL_TRANSPARENT )
2593 pOut->SetTextLineColor( rTextLineColor.GetColor() );
2594 else
2595 pOut->SetTextLineColor();
2598 if ( pOut )
2600 const SvxOverlineItem& rOverlineColor = (const SvxOverlineItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_OVERLINE );
2601 if ( rOverlineColor.GetColor() != COL_TRANSPARENT )
2602 pOut->SetOverlineColor( rOverlineColor.GetColor() );
2603 else
2604 pOut->SetOverlineColor();
2607 const SvxLanguageItem* pCJKLanguageItem = NULL;
2609 if ( aStatus.UseCharAttribs() )
2611 const CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
2612 sal_uInt16 nAttr = 0;
2613 EditCharAttrib* pAttrib = GetAttrib( rAttribs, nAttr );
2614 while ( pAttrib && ( pAttrib->GetStart() <= nPos ) )
2616 // Beim Seeken nicht die Attr beruecksichtigen, die dort beginnen!
2617 // Leere Attribute werden beruecksichtigt( verwendet), da diese
2618 // gerade eingestellt wurden.
2619 // 12.4.95: Doch keine Leeren Attribute verwenden:
2620 // - Wenn gerade eingestellt und leer => keine Auswirkung auf Font
2621 // In einem leeren Absatz eingestellte Zeichen werden sofort wirksam.
2622 if ( ( pAttrib->Which() != nIgnoreWhich ) &&
2623 ( ( ( pAttrib->GetStart() < nPos ) && ( pAttrib->GetEnd() >= nPos ) )
2624 || ( !pNode->Len() ) ) )
2626 DBG_ASSERT( ( pAttrib->Which() >= EE_CHAR_START ) && ( pAttrib->Which() <= EE_FEATURE_END ), "Unglueltiges Attribut in Seek() " );
2627 if ( IsScriptItemValid( pAttrib->Which(), nScriptType ) )
2629 pAttrib->SetFont( rFont, pOut );
2630 // #i1550# hard color attrib should win over text color from field
2631 if ( pAttrib->Which() == EE_FEATURE_FIELD )
2633 EditCharAttrib* pColorAttr = pNode->GetCharAttribs().FindAttrib( EE_CHAR_COLOR, nPos );
2634 if ( pColorAttr )
2635 pColorAttr->SetFont( rFont, pOut );
2638 if ( pAttrib->Which() == EE_CHAR_FONTWIDTH )
2639 nRelWidth = ((const SvxCharScaleWidthItem*)pAttrib->GetItem())->GetValue();
2640 if ( pAttrib->Which() == EE_CHAR_LANGUAGE_CJK )
2641 pCJKLanguageItem = (const SvxLanguageItem*) pAttrib->GetItem();
2643 pAttrib = GetAttrib( rAttribs, ++nAttr );
2647 if ( !pCJKLanguageItem )
2648 pCJKLanguageItem = (const SvxLanguageItem*) &pNode->GetContentAttribs().GetItem( EE_CHAR_LANGUAGE_CJK );
2650 rFont.SetCJKContextLanguage( pCJKLanguageItem->GetLanguage() );
2652 if ( rFont.GetKerning() && IsKernAsianPunctuation() && ( nScriptType == i18n::ScriptType::ASIAN ) )
2653 rFont.SetKerning( rFont.GetKerning() | KERNING_ASIAN );
2655 if ( aStatus.DoNotUseColors() )
2657 // Hack fuer DL,weil JOE staendig die Pooldefaults verbiegt!
2658 // const SvxColorItem& rColorItem = (const SvxColorItem&)aEditDoc.GetItemPool().GetDefaultItem( EE_CHAR_COLOR );
2659 rFont.SetColor( /* rColorItem.GetValue() */ COL_BLACK );
2662 if ( aStatus.DoStretch() || ( nRelWidth != 100 ) )
2664 // Fuer das aktuelle Ausgabegeraet, weil es sonst bei einem
2665 // Drucker als RefDev auf dem Bildschirm #?!@' aussieht!
2666 OutputDevice* pDev = pOut ? pOut : GetRefDevice();
2667 rFont.SetPhysFont( pDev );
2668 FontMetric aMetric( pDev->GetFontMetric() );
2669 // Fuer die Hoehe nicht die Metriken nehmen, da das bei
2670 // Hoch-/Tiefgestellt schief geht.
2671 Size aRealSz( aMetric.GetSize().Width(), rFont.GetSize().Height() );
2672 if ( aStatus.DoStretch() )
2674 if ( nStretchY != 100 )
2676 aRealSz.Height() *= nStretchY;
2677 aRealSz.Height() /= 100;
2679 if ( nStretchX != 100 )
2681 if ( nStretchX == nStretchY &&
2682 nRelWidth == 100 )
2684 aRealSz.Width() = 0;
2686 else
2688 aRealSz.Width() *= nStretchX;
2689 aRealSz.Width() /= 100;
2691 // Auch das Kerning: (long wegen Zwischenergebnis)
2692 long nKerning = rFont.GetFixKerning();
2694 Die Ueberlegung war: Wenn neg. Kerning, aber StretchX = 200
2695 => Nicht das Kerning verdoppelt, also die Buchstaben weiter
2696 zusammenziehen
2697 ---------------------------
2698 Kern StretchX =>Kern
2699 ---------------------------
2700 >0 <100 < (Proportional)
2701 <0 <100 < (Proportional)
2702 >0 >100 > (Proportional)
2703 <0 >100 < (Der Betrag, also Antiprop)
2705 if ( ( nKerning < 0 ) && ( nStretchX > 100 ) )
2707 // Antiproportional
2708 nKerning *= 100;
2709 nKerning /= nStretchX;
2711 else if ( nKerning )
2713 // Proportional
2714 nKerning *= nStretchX;
2715 nKerning /= 100;
2717 rFont.SetFixKerning( (short)nKerning );
2721 if ( nRelWidth != 100 )
2723 aRealSz.Width() *= nRelWidth;
2724 aRealSz.Width() /= 100;
2726 rFont.SetSize( aRealSz );
2727 // Font wird nicht restauriert...
2730 if ( ( ( rFont.GetColor() == COL_AUTO ) || ( IsForceAutoColor() ) ) && pOut )
2732 // #i75566# Do not use AutoColor when printing OR Pdf export
2733 const bool bPrinting(OUTDEV_PRINTER == pOut->GetOutDevType());
2734 const bool bPDFExporting(0 != pOut->GetPDFWriter());
2736 if ( IsAutoColorEnabled() && !bPrinting && !bPDFExporting)
2738 // Never use WindowTextColor on the printer
2739 rFont.SetColor( GetAutoColor() );
2741 else
2743 if ( ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() )
2744 rFont.SetColor( COL_WHITE );
2745 else
2746 rFont.SetColor( COL_BLACK );
2750 if ( mpIMEInfos && mpIMEInfos->pAttribs && ( mpIMEInfos->aPos.GetNode() == pNode ) &&
2751 ( nPos > mpIMEInfos->aPos.GetIndex() ) && ( nPos <= ( mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen ) ) )
2753 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ nPos - mpIMEInfos->aPos.GetIndex() - 1 ];
2754 if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
2755 rFont.SetUnderline( UNDERLINE_SINGLE );
2756 else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
2757 rFont.SetUnderline( UNDERLINE_BOLD );
2758 else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
2759 rFont.SetUnderline( UNDERLINE_DOTTED );
2760 else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
2761 rFont.SetUnderline( UNDERLINE_DOTTED );
2762 else if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
2763 rFont.SetColor( Color( COL_RED ) );
2764 else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
2765 rFont.SetColor( Color( COL_LIGHTGRAY ) );
2766 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
2768 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
2769 rFont.SetColor( rStyleSettings.GetHighlightTextColor() );
2770 rFont.SetFillColor( rStyleSettings.GetHighlightColor() );
2771 rFont.SetTransparent( FALSE );
2773 else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
2775 rFont.SetUnderline( UNDERLINE_WAVE );
2776 if( pOut )
2777 pOut->SetTextLineColor( Color( COL_LIGHTGRAY ) );
2782 void ImpEditEngine::RecalcFormatterFontMetrics( FormatterFontMetric& rCurMetrics, SvxFont& rFont )
2784 // Fuer Zeilenhoehe bei Hoch/Tief erstmal ohne Propr!
2785 sal_uInt16 nPropr = rFont.GetPropr();
2786 DBG_ASSERT( ( nPropr == 100 ) || rFont.GetEscapement(), "Propr ohne Escape?!" );
2787 if ( nPropr != 100 )
2789 rFont.SetPropr( 100 );
2790 rFont.SetPhysFont( pRefDev );
2792 sal_uInt16 nAscent, nDescent;
2794 FontMetric aMetric( pRefDev->GetFontMetric() );
2795 nAscent = (sal_uInt16)aMetric.GetAscent();
2796 if ( IsAddExtLeading() )
2797 nAscent = sal::static_int_cast< sal_uInt16 >(
2798 nAscent + aMetric.GetExtLeading() );
2799 nDescent = (sal_uInt16)aMetric.GetDescent();
2801 if ( IsFixedCellHeight() )
2803 /* creating correct proportional ascent and descent values lead to problems if different fonts are used
2804 in the same portion, it results in a bigger linespacing.
2805 sal_Int32 f = nAscent + nDescent;
2806 if ( f )
2808 sal_Int32 nHeight = ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() );
2809 nAscent = (sal_Int16)(( nHeight * nAscent ) / f );
2810 nDescent = (sal_Int16)(nHeight - nAscent);
2813 nAscent = sal::static_int_cast< sal_uInt16 >( rFont.GetHeight() );
2814 nDescent= sal::static_int_cast< sal_uInt16 >( ImplCalculateFontIndependentLineSpacing( rFont.GetHeight() ) - nAscent );
2816 else
2818 sal_uInt16 nIntLeading = ( aMetric.GetIntLeading() > 0 ) ? (sal_uInt16)aMetric.GetIntLeading() : 0;
2819 // Fonts ohne Leading bereiten Probleme
2820 if ( ( nIntLeading == 0 ) && ( pRefDev->GetOutDevType() == OUTDEV_PRINTER ) )
2822 // Da schaun wir mal, was fuer eine Leading ich auf dem
2823 // Bildschirm erhalte
2824 VirtualDevice* pVDev = GetVirtualDevice( pRefDev->GetMapMode(), pRefDev->GetDrawMode() );
2825 rFont.SetPhysFont( pVDev );
2826 aMetric = pVDev->GetFontMetric();
2828 // Damit sich die Leading nicht wieder rausrechnet,
2829 // wenn die ganze Zeile den Font hat, nTmpLeading.
2831 // 4/96: Kommt bei HP Laserjet 4V auch nicht hin
2832 // => Werte komplett vom Bildschirm holen.
2833 // sal_uInt16 nTmpLeading = (sal_uInt16)aMetric.GetLeading();
2834 // nAscent += nTmpLeading;
2835 nAscent = (sal_uInt16)aMetric.GetAscent();
2836 nDescent = (sal_uInt16)aMetric.GetDescent();
2837 // nLeading = (sal_uInt16)aMetric.GetLeading();
2840 if ( nAscent > rCurMetrics.nMaxAscent )
2841 rCurMetrics.nMaxAscent = nAscent;
2842 if ( nDescent > rCurMetrics.nMaxDescent )
2843 rCurMetrics.nMaxDescent= nDescent;
2844 // Sonderbehandlung Hoch/Tief:
2845 if ( rFont.GetEscapement() )
2847 // Jetzt unter Beruecksichtigung von Escape/Propr
2848 // Ascent oder Descent ggf vergroessern
2849 short nDiff = (short)(rFont.GetSize().Height()*rFont.GetEscapement()/100L);
2850 if ( rFont.GetEscapement() > 0 )
2852 nAscent = (sal_uInt16) (((long)nAscent)*nPropr/100 + nDiff);
2853 if ( nAscent > rCurMetrics.nMaxAscent )
2854 rCurMetrics.nMaxAscent = nAscent;
2856 else // muss < 0 sein
2858 nDescent = (sal_uInt16) (((long)nDescent)*nPropr/100 - nDiff);
2859 if ( nDescent > rCurMetrics.nMaxDescent )
2860 rCurMetrics.nMaxDescent= nDescent;
2865 void ImpEditEngine::Paint( OutputDevice* pOutDev, Rectangle aClipRec, Point aStartPos, sal_Bool bStripOnly, short nOrientation )
2867 if ( !GetUpdateMode() && !bStripOnly )
2868 return;
2870 if ( !IsFormatted() )
2871 FormatDoc();
2873 long nFirstVisXPos = - pOutDev->GetMapMode().GetOrigin().X();
2874 long nFirstVisYPos = - pOutDev->GetMapMode().GetOrigin().Y();
2876 EditLine* pLine;
2877 Point aTmpPos;
2878 Point aRedLineTmpPos;
2879 DBG_ASSERT( GetParaPortions().Count(), "Keine ParaPortion?!" );
2880 SvxFont aTmpFont( GetParaPortions()[0]->GetNode()->GetCharAttribs().GetDefFont() );
2881 Font aOldFont( pOutDev->GetFont() );
2882 vcl::PDFExtOutDevData* pPDFExtOutDevData = PTR_CAST( vcl::PDFExtOutDevData, pOutDev->GetExtOutDevData() );
2884 // Bei gedrehtem Text wird aStartPos als TopLeft angesehen, da andere
2885 // Informationen fehlen, und sowieso das ganze Object ungescrollt
2886 // dargestellt wird.
2887 // Das Rechteck ist unendlich gross.
2888 Point aOrigin( aStartPos );
2889 double nCos = 0.0, nSin = 0.0;
2890 if ( nOrientation )
2892 double nRealOrientation = nOrientation*F_PI1800;
2893 nCos = cos( nRealOrientation );
2894 nSin = sin( nRealOrientation );
2897 // #110496# Added some more optional metafile comments. This
2898 // change: factored out some duplicated code.
2899 GDIMetaFile* pMtf = pOutDev->GetConnectMetaFile();
2900 const bool bMetafileValid( pMtf != NULL );
2902 // Fuer OnlineSpelling:
2903 // EditPaM aCursorPos;
2904 // if( GetStatus().DoOnlineSpelling() && pActiveView )
2905 // aCurPos = pActiveView->pImpEditView->GetEditSelections().Max();
2907 // --------------------------------------------------
2908 // Ueber alle Absaetze...
2909 // --------------------------------------------------
2910 for ( sal_uInt16 n = 0; n < GetParaPortions().Count(); n++ )
2912 ParaPortion* pPortion = GetParaPortions().GetObject( n );
2913 DBG_ASSERT( pPortion, "NULL-Pointer in TokenList in Paint" );
2914 // falls beim Tippen Idle-Formatierung, asynchrones Paint.
2915 // Unsichtbare Portions koennen ungueltig sein.
2916 if ( pPortion->IsVisible() && pPortion->IsInvalid() )
2917 return;
2919 if ( pPDFExtOutDevData )
2920 pPDFExtOutDevData->BeginStructureElement( vcl::PDFWriter::Paragraph );
2922 long nParaHeight = pPortion->GetHeight();
2923 sal_uInt16 nIndex = 0;
2924 if ( pPortion->IsVisible() && (
2925 ( !IsVertical() && ( ( aStartPos.Y() + nParaHeight ) > aClipRec.Top() ) ) ||
2926 ( IsVertical() && ( ( aStartPos.X() - nParaHeight ) < aClipRec.Right() ) ) ) )
2929 // --------------------------------------------------
2930 // Ueber die Zeilen des Absatzes...
2931 // --------------------------------------------------
2932 sal_uInt16 nLines = pPortion->GetLines().Count();
2933 sal_uInt16 nLastLine = nLines-1;
2935 if ( !IsVertical() )
2936 aStartPos.Y() += pPortion->GetFirstLineOffset();
2937 else
2938 aStartPos.X() -= pPortion->GetFirstLineOffset();
2940 Point aParaStart( aStartPos );
2942 const SvxLineSpacingItem& rLSItem = ((const SvxLineSpacingItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_SBL ));
2943 sal_uInt16 nSBL = ( rLSItem.GetInterLineSpaceRule() == SVX_INTER_LINE_SPACE_FIX )
2944 ? GetYValue( rLSItem.GetInterLineSpace() ) : 0;
2945 for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
2947 pLine = pPortion->GetLines().GetObject(nLine);
2948 DBG_ASSERT( pLine, "NULL-Pointer im Zeileniterator in UpdateViews" );
2949 aTmpPos = aStartPos;
2950 if ( !IsVertical() )
2952 aTmpPos.X() += pLine->GetStartPosX();
2953 aTmpPos.Y() += pLine->GetMaxAscent();
2954 aStartPos.Y() += pLine->GetHeight();
2956 else
2958 aTmpPos.Y() += pLine->GetStartPosX();
2959 aTmpPos.X() -= pLine->GetMaxAscent();
2960 aStartPos.X() -= pLine->GetHeight();
2963 if ( ( !IsVertical() && ( aStartPos.Y() > aClipRec.Top() ) )
2964 || ( IsVertical() && aStartPos.X() < aClipRec.Right() ) )
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)
2972 // VERT???
2973 GetEditEnginePtr()->PaintingFirstLine( n, aParaStart, aTmpPos.Y(), aOrigin, nOrientation, pOutDev );
2976 // --------------------------------------------------
2977 // Ueber die Portions der Zeile...
2978 // --------------------------------------------------
2979 nIndex = pLine->GetStart();
2980 for ( sal_uInt16 y = pLine->GetStartPortion(); y <= pLine->GetEndPortion(); y++ )
2982 DBG_ASSERT( pPortion->GetTextPortions().Count(), "Zeile ohne Textportion im Paint!" );
2983 TextPortion* pTextPortion = pPortion->GetTextPortions().GetObject( y );
2984 DBG_ASSERT( pTextPortion, "NULL-Pointer im Portioniterator in UpdateViews" );
2986 long nPortionXOffset = GetPortionXOffset( pPortion, pLine, y );
2987 if ( !IsVertical() )
2989 aTmpPos.X() = aStartPos.X() + nPortionXOffset;
2990 if ( aTmpPos.X() > aClipRec.Right() )
2991 break; // Keine weitere Ausgabe in Zeile noetig
2993 else
2995 aTmpPos.Y() = aStartPos.Y() + nPortionXOffset;
2996 if ( aTmpPos.Y() > aClipRec.Bottom() )
2997 break; // Keine weitere Ausgabe in Zeile noetig
3000 // R2L replaces with obove...
3001 // New position after processing R2L text...
3002 // R2L if ( nR2LWidth && !pTextPortion->GetRightToLeft() )
3003 // R2L {
3004 // R2L if ( !IsVertical() )
3005 // R2L aTmpPos.X() += nR2LWidth;
3006 // R2L else
3007 // R2L aTmpPos.Y() += nR2LWidth;
3008 // R2L
3009 // R2L nR2LWidth = 0;
3010 // R2L }
3012 switch ( pTextPortion->GetKind() )
3014 case PORTIONKIND_TEXT:
3015 case PORTIONKIND_FIELD:
3016 case PORTIONKIND_HYPHENATOR:
3018 SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
3020 BOOL bDrawFrame = FALSE;
3022 if ( ( pTextPortion->GetKind() == PORTIONKIND_FIELD ) && !aTmpFont.IsTransparent() &&
3023 ( GetBackgroundColor() != COL_AUTO ) && GetBackgroundColor().IsDark() &&
3024 ( IsAutoColorEnabled() && ( pOutDev->GetOutDevType() != OUTDEV_PRINTER ) ) )
3026 aTmpFont.SetTransparent( TRUE );
3027 pOutDev->SetFillColor();
3028 pOutDev->SetLineColor( GetAutoColor() );
3029 bDrawFrame = TRUE;
3032 #ifdef EDITDEBUG
3033 if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
3035 aTmpFont.SetFillColor( COL_LIGHTGRAY );
3036 aTmpFont.SetTransparent( sal_False );
3038 if ( pTextPortion->GetRightToLeft() )
3040 aTmpFont.SetFillColor( COL_LIGHTGRAY );
3041 aTmpFont.SetTransparent( sal_False );
3043 else if ( GetScriptType( EditPaM( pPortion->GetNode(), nIndex+1 ) ) == i18n::ScriptType::COMPLEX )
3045 aTmpFont.SetFillColor( COL_LIGHTCYAN );
3046 aTmpFont.SetTransparent( sal_False );
3048 #endif
3049 aTmpFont.SetPhysFont( pOutDev );
3051 // #114278# Saving both layout mode and language (since I'm
3052 // potentially changing both)
3053 pOutDev->Push( PUSH_TEXTLAYOUTMODE|PUSH_TEXTLANGUAGE );
3054 ImplInitLayoutMode( pOutDev, n, nIndex );
3055 ImplInitDigitMode( pOutDev, 0, 0, 0, aTmpFont.GetLanguage() );
3057 XubString aText;
3058 USHORT nTextStart = 0;
3059 USHORT nTextLen = 0;
3060 const sal_Int32* pDXArray = 0;
3061 sal_Int32* pTmpDXArray = 0;
3063 if ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
3065 aText = *pPortion->GetNode();
3066 nTextStart = nIndex;
3067 nTextLen = pTextPortion->GetLen();
3068 pDXArray = pLine->GetCharPosArray().GetData()+( nIndex-pLine->GetStart() );
3070 // --> FME 2005-10-18 #i55716# Paint control characters
3071 if ( aStatus.MarkFields() )
3073 xub_StrLen nTmpIdx;
3074 const xub_StrLen nTmpEnd = nTextStart + pTextPortion->GetLen();
3076 for ( nTmpIdx = nTextStart; nTmpIdx <= nTmpEnd ; ++nTmpIdx )
3078 const sal_Unicode cChar = ( nTmpIdx != aText.Len() && ( nTmpIdx != nTextStart || 0 == nTextStart ) ) ?
3079 aText.GetChar( nTmpIdx ) :
3082 if ( 0x200B == cChar || 0x2060 == cChar )
3084 const String aBlank( ' ' );
3085 long nHalfBlankWidth = aTmpFont.QuickGetTextSize( pOutDev, aBlank, 0, 1, 0 ).Width() / 2;
3087 const long nAdvanceX = ( nTmpIdx == nTmpEnd ?
3088 pTextPortion->GetSize().Width() :
3089 pDXArray[ nTmpIdx - nTextStart ] ) - nHalfBlankWidth;
3090 const long nAdvanceY = -pLine->GetMaxAscent();
3092 Point aTopLeftRectPos( aTmpPos );
3093 if ( !IsVertical() )
3095 aTopLeftRectPos.X() += nAdvanceX;
3096 aTopLeftRectPos.Y() += nAdvanceY;
3098 else
3100 aTopLeftRectPos.Y() += nAdvanceX;
3101 aTopLeftRectPos.X() -= nAdvanceY;
3104 Point aBottomRightRectPos( aTopLeftRectPos );
3105 if ( !IsVertical() )
3107 aBottomRightRectPos.X() += 2 * nHalfBlankWidth;
3108 aBottomRightRectPos.Y() += pLine->GetHeight();
3110 else
3112 aBottomRightRectPos.X() -= pLine->GetHeight();
3113 aBottomRightRectPos.Y() += 2 * nHalfBlankWidth;
3116 pOutDev->Push( PUSH_FILLCOLOR );
3117 pOutDev->Push( PUSH_LINECOLOR );
3118 pOutDev->SetFillColor( COL_LIGHTGRAY );
3119 pOutDev->SetLineColor( COL_LIGHTGRAY );
3121 const Rectangle aBackRect( aTopLeftRectPos, aBottomRightRectPos );
3122 pOutDev->DrawRect( aBackRect );
3124 pOutDev->Pop();
3125 pOutDev->Pop();
3127 if ( 0x200B == cChar )
3129 const String aSlash( '/' );
3130 const short nOldEscapement = aTmpFont.GetEscapement();
3131 const BYTE nOldPropr = aTmpFont.GetPropr();
3133 aTmpFont.SetEscapement( -20 );
3134 aTmpFont.SetPropr( 25 );
3135 aTmpFont.SetPhysFont( pOutDev );
3137 const Size aSlashSize = aTmpFont.QuickGetTextSize( pOutDev, aSlash, 0, 1, 0 );
3138 Point aSlashPos( aTmpPos );
3139 const long nAddX = nHalfBlankWidth - aSlashSize.Width() / 2;
3140 if ( !IsVertical() )
3142 aSlashPos.X() = aTopLeftRectPos.X() + nAddX;
3144 else
3146 aSlashPos.Y() = aTopLeftRectPos.Y() + nAddX;
3149 aTmpFont.QuickDrawText( pOutDev, aSlashPos, aSlash, 0, 1, 0 );
3151 aTmpFont.SetEscapement( nOldEscapement );
3152 aTmpFont.SetPropr( nOldPropr );
3153 aTmpFont.SetPhysFont( pOutDev );
3158 // <--
3160 else if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3162 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
3163 DBG_ASSERT( pAttr, "Feld nicht gefunden" );
3164 DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Feld vom falschen Typ!" );
3165 aText = ((EditCharAttribField*)pAttr)->GetFieldValue();
3166 nTextStart = 0;
3167 nTextLen = aText.Len();
3169 pTmpDXArray = new sal_Int32[ aText.Len() ];
3170 pDXArray = pTmpDXArray;
3171 Font _aOldFont( GetRefDevice()->GetFont() );
3172 aTmpFont.SetPhysFont( GetRefDevice() );
3173 aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.Len(), pTmpDXArray );
3174 if ( aStatus.DoRestoreFont() )
3175 GetRefDevice()->SetFont( _aOldFont );
3177 // add a meta file comment if we record to a metafile
3178 if( bMetafileValid )
3180 SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
3181 if( pFieldItem )
3183 const SvxFieldData* pFieldData = pFieldItem->GetField();
3184 if( pFieldData )
3185 pMtf->AddAction( pFieldData->createBeginComment() );
3190 else if ( pTextPortion->GetKind() == PORTIONKIND_HYPHENATOR )
3192 if ( pTextPortion->GetExtraValue() )
3193 aText = pTextPortion->GetExtraValue();
3194 aText += CH_HYPH;
3195 nTextStart = 0;
3196 nTextLen = aText.Len();
3198 // #b6668980# crash when accessing 0 pointer in pDXArray
3199 pTmpDXArray = new sal_Int32[ aText.Len() ];
3200 pDXArray = pTmpDXArray;
3201 Font _aOldFont( GetRefDevice()->GetFont() );
3202 aTmpFont.SetPhysFont( GetRefDevice() );
3203 aTmpFont.QuickGetTextSize( GetRefDevice(), aText, 0, aText.Len(), pTmpDXArray );
3204 if ( aStatus.DoRestoreFont() )
3205 GetRefDevice()->SetFont( _aOldFont );
3208 long nTxtWidth = pTextPortion->GetSize().Width();
3210 Point aOutPos( aTmpPos );
3211 aRedLineTmpPos = aTmpPos;
3212 // In RTL portions spell markup pos should be at the start of the
3213 // first chara as well. That is on the right end of the portion
3214 if (pTextPortion->IsRightToLeft())
3215 aRedLineTmpPos.X() += pTextPortion->GetSize().Width();
3217 //L2R if ( pTextPortion->GetRightToLeft() )
3218 //L2R {
3219 //L2R sal_uInt16 nNextPortion = y+1;
3220 //L2R while ( nNextPortion <= pLine->GetEndPortion() )
3221 //L2R {
3222 //L2R TextPortion* pNextTextPortion = pPortion->GetTextPortions().GetObject( nNextPortion );
3223 //L2R if ( pNextTextPortion->GetRightToLeft() )
3224 //L2R {
3225 //L2R if ( !IsVertical() )
3226 //L2R aOutPos.X() += pNextTextPortion->GetSize().Width();
3227 //L2R else
3228 //L2R aOutPos.Y() += pNextTextPortion->GetSize().Width();
3229 //L2R }
3230 //L2R else
3231 //L2R break;
3232 //L2R nNextPortion++;
3233 //L2R }
3234 //L2R }
3235 if ( bStripOnly )
3237 EEngineData::WrongSpellVector aWrongSpellVector;
3239 if(GetStatus().DoOnlineSpelling() && pTextPortion->GetLen())
3241 WrongList* pWrongs = pPortion->GetNode()->GetWrongList();
3243 if(pWrongs && pWrongs->HasWrongs())
3245 sal_uInt16 nStart(nIndex);
3246 sal_uInt16 nEnd(0);
3247 sal_Bool bWrong(pWrongs->NextWrong(nStart, nEnd));
3248 const sal_uInt16 nMaxEnd(nIndex + pTextPortion->GetLen());
3250 while(bWrong)
3252 if(nStart >= nMaxEnd)
3254 break;
3257 if(nStart < nIndex)
3259 nStart = nIndex;
3262 if(nEnd > nMaxEnd)
3264 nEnd = nMaxEnd;
3267 // add to vector
3268 aWrongSpellVector.push_back(EEngineData::WrongSpellClass(nStart, nEnd));
3270 // goto next index
3271 nStart = nEnd + 1;
3273 if(nEnd < nMaxEnd)
3275 bWrong = pWrongs->NextWrong(nStart, nEnd);
3277 else
3279 bWrong = sal_False;
3285 const SvxFieldData* pFieldData = 0;
3287 if(PORTIONKIND_FIELD == pTextPortion->GetKind())
3289 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature(nIndex);
3290 SvxFieldItem* pFieldItem = PTR_CAST(SvxFieldItem, pAttr->GetItem());
3292 if(pFieldItem)
3294 pFieldData = pFieldItem->GetField();
3298 // support for EOC, EOW, EOS TEXT comments. To support that,
3299 // the locale is needed. With the locale and a XBreakIterator it is
3300 // possible to re-create the text marking info on primitive level
3301 const lang::Locale aLocale(GetLocale(EditPaM(pPortion->GetNode(), nIndex + 1)));
3303 // create EOL and EOP bools
3304 const bool bEndOfLine(y == pLine->GetEndPortion());
3305 const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines);
3307 // get Overline color (from ((const SvxOverlineItem*)GetItem())->GetColor() in
3308 // consequence, but also already set at pOutDev)
3309 const Color aOverlineColor(pOutDev->GetOverlineColor());
3311 // get TextLine color (from ((const SvxUnderlineItem*)GetItem())->GetColor() in
3312 // consequence, but also already set at pOutDev)
3313 const Color aTextLineColor(pOutDev->GetTextLineColor());
3315 // Unicode code points conversion according to ctl text numeral setting
3316 ImplInitDigitMode( 0, &aText, nTextStart, nTextLen, aTmpFont.GetLanguage() );
3318 // StripPortions() data callback
3319 GetEditEnginePtr()->DrawingText( aOutPos, aText, nTextStart, nTextLen, pDXArray,
3320 aTmpFont, n, nIndex, pTextPortion->GetRightToLeft(),
3321 aWrongSpellVector.size() ? &aWrongSpellVector : 0,
3322 pFieldData,
3323 bEndOfLine, bEndOfParagraph, false, // support for EOL/EOP TEXT comments
3324 &aLocale,
3325 aOverlineColor,
3326 aTextLineColor);
3328 else
3330 short nEsc = aTmpFont.GetEscapement();
3331 if ( nOrientation )
3333 // Bei Hoch/Tief selbst Hand anlegen:
3334 if ( aTmpFont.GetEscapement() )
3336 long nDiff = aTmpFont.GetSize().Height() * aTmpFont.GetEscapement() / 100L;
3337 if ( !IsVertical() )
3338 aOutPos.Y() -= nDiff;
3339 else
3340 aOutPos.X() += nDiff;
3341 aRedLineTmpPos = aOutPos;
3342 aTmpFont.SetEscapement( 0 );
3345 aOutPos = lcl_ImplCalcRotatedPos( aOutPos, aOrigin, nSin, nCos );
3346 aTmpFont.SetOrientation( aTmpFont.GetOrientation()+nOrientation );
3347 aTmpFont.SetPhysFont( pOutDev );
3350 // nur ausgeben, was im sichtbaren Bereich beginnt:
3351 // Wichtig, weil Bug bei einigen Grafikkarten bei transparentem Font, Ausgabe bei neg.
3352 if ( nOrientation || ( !IsVertical() && ( ( aTmpPos.X() + nTxtWidth ) >= nFirstVisXPos ) )
3353 || ( IsVertical() && ( ( aTmpPos.Y() + nTxtWidth ) >= nFirstVisYPos ) ) )
3355 if ( nEsc && ( ( aTmpFont.GetUnderline() != UNDERLINE_NONE ) ) )
3357 // Das Hoch/Tief ohne Underline malen, das Underline
3358 // auf der BaseLine der Original-Fonthoehe ausgeben...
3360 // Aber nur, wenn davor auch Unterstrichen!
3361 sal_Bool bSpecialUnderline = sal_False;
3362 EditCharAttrib* pPrev = pPortion->GetNode()->GetCharAttribs().FindAttrib( EE_CHAR_ESCAPEMENT, nIndex );
3363 if ( pPrev )
3365 SvxFont aDummy;
3366 // Unterstreichung davor?
3367 if ( pPrev->GetStart() )
3369 SeekCursor( pPortion->GetNode(), pPrev->GetStart(), aDummy );
3370 if ( aDummy.GetUnderline() != UNDERLINE_NONE )
3371 bSpecialUnderline = sal_True;
3373 if ( !bSpecialUnderline && ( pPrev->GetEnd() < pPortion->GetNode()->Len() ) )
3375 SeekCursor( pPortion->GetNode(), pPrev->GetEnd()+1, aDummy );
3376 if ( aDummy.GetUnderline() != UNDERLINE_NONE )
3377 bSpecialUnderline = sal_True;
3380 if ( bSpecialUnderline )
3382 Size aSz = aTmpFont.GetPhysTxtSize( pOutDev, aText, nTextStart, nTextLen );
3383 BYTE nProp = aTmpFont.GetPropr();
3384 aTmpFont.SetEscapement( 0 );
3385 aTmpFont.SetPropr( 100 );
3386 aTmpFont.SetPhysFont( pOutDev );
3387 String aBlanks;
3388 aBlanks.Fill( nTextLen, ' ' );
3389 Point aUnderlinePos( aOutPos );
3390 if ( nOrientation )
3391 aUnderlinePos = lcl_ImplCalcRotatedPos( aTmpPos, aOrigin, nSin, nCos );
3392 pOutDev->DrawStretchText( aUnderlinePos, aSz.Width(), aBlanks, 0, nTextLen );
3394 aTmpFont.SetUnderline( UNDERLINE_NONE );
3395 if ( !nOrientation )
3396 aTmpFont.SetEscapement( nEsc );
3397 aTmpFont.SetPropr( nProp );
3398 aTmpFont.SetPhysFont( pOutDev );
3401 Point aRealOutPos( aOutPos );
3402 if ( ( pTextPortion->GetKind() == PORTIONKIND_TEXT )
3403 && pTextPortion->GetExtraInfos() && pTextPortion->GetExtraInfos()->bCompressed
3404 && pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation )
3406 aRealOutPos.X() += pTextPortion->GetExtraInfos()->nPortionOffsetX;
3409 // --> FME 2005-06-17 #i37132# RTL portions with
3410 // compressed blank should not paint this blank:
3411 if ( pTextPortion->IsRightToLeft() && nTextLen >= 2 &&
3412 pDXArray[ nTextLen - 1 ] ==
3413 pDXArray[ nTextLen - 2 ] &&
3414 ' ' == aText.GetChar( nTextStart + nTextLen - 1 ) )
3415 --nTextLen;
3416 // <--
3418 // output directly
3419 aTmpFont.QuickDrawText( pOutDev, aRealOutPos, aText, nTextStart, nTextLen, pDXArray );
3421 if ( bDrawFrame )
3423 Point aTopLeft( aTmpPos );
3424 aTopLeft.Y() -= pLine->GetMaxAscent();
3425 if ( nOrientation )
3426 aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
3427 Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
3428 pOutDev->DrawRect( aRect );
3432 // PDF export:
3433 if ( pPDFExtOutDevData )
3435 if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3437 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
3438 SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
3439 if( pFieldItem )
3441 const SvxFieldData* pFieldData = pFieldItem->GetField();
3442 if ( pFieldData->ISA( SvxURLField ) )
3444 Point aTopLeft( aTmpPos );
3445 aTopLeft.Y() -= pLine->GetMaxAscent();
3446 // if ( nOrientation )
3447 // aTopLeft = lcl_ImplCalcRotatedPos( aTopLeft, aOrigin, nSin, nCos );
3449 Rectangle aRect( aTopLeft, pTextPortion->GetSize() );
3450 vcl::PDFExtOutDevBookmarkEntry aBookmark;
3451 aBookmark.nLinkId = pPDFExtOutDevData->CreateLink( aRect );
3452 aBookmark.aBookmark = ((SvxURLField*)pFieldData)->GetURL();
3453 std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks();
3454 rBookmarks.push_back( aBookmark );
3460 // comment
3467 #ifndef SVX_LIGHT
3468 if ( GetStatus().DoOnlineSpelling() && pPortion->GetNode()->GetWrongList()->HasWrongs() && pTextPortion->GetLen() )
3470 {//#105750# adjust LinePos for superscript or subscript text
3471 short _nEsc = aTmpFont.GetEscapement();
3472 if( _nEsc )
3474 long nShift = ((_nEsc*long(aTmpFont.GetSize().Height()))/ 100L);
3475 if( !IsVertical() )
3476 aRedLineTmpPos.Y() -= nShift;
3477 else
3478 aRedLineTmpPos.X() += nShift;
3481 Color aOldColor( pOutDev->GetLineColor() );
3482 pOutDev->SetLineColor( Color( GetColorConfig().GetColorValue( svtools::SPELL ).nColor ) );
3483 lcl_DrawRedLines( pOutDev, aTmpFont.GetSize().Height(), aRedLineTmpPos, nIndex, nIndex + pTextPortion->GetLen(), pDXArray, pPortion->GetNode()->GetWrongList(), nOrientation, aOrigin, IsVertical(), pTextPortion->IsRightToLeft() );
3484 pOutDev->SetLineColor( aOldColor );
3486 #endif // !SVX_LIGHT
3489 pOutDev->Pop();
3491 if ( pTmpDXArray )
3492 delete[] pTmpDXArray;
3494 // R2L if ( !pTextPortion->GetRightToLeft() )
3495 // R2L {
3496 // R2L if ( !IsVertical() )
3497 // R2L aTmpPos.X() += nTxtWidth;
3498 // R2L else
3499 // R2L aTmpPos.Y() += nTxtWidth;
3500 // R2L }
3501 // R2L else
3502 // R2L {
3503 // R2L nR2LWidth += nTxtWidth;
3504 // R2L }
3506 if ( pTextPortion->GetKind() == PORTIONKIND_FIELD )
3508 EditCharAttrib* pAttr = pPortion->GetNode()->GetCharAttribs().FindFeature( nIndex );
3509 DBG_ASSERT( pAttr, "Feld nicht gefunden" );
3510 DBG_ASSERT( pAttr && pAttr->GetItem()->ISA( SvxFieldItem ), "Feld vom falschen Typ!" );
3512 // add a meta file comment if we record to a metafile
3513 if( bMetafileValid )
3515 SvxFieldItem* pFieldItem = PTR_CAST( SvxFieldItem, pAttr->GetItem() );
3517 if( pFieldItem )
3519 const SvxFieldData* pFieldData = pFieldItem->GetField();
3520 if( pFieldData )
3521 pMtf->AddAction( pFieldData->createEndComment() );
3528 break;
3529 // case PORTIONKIND_EXTRASPACE:
3530 case PORTIONKIND_TAB:
3532 if ( pTextPortion->GetExtraValue() && ( pTextPortion->GetExtraValue() != ' ' ) )
3534 SeekCursor( pPortion->GetNode(), nIndex+1, aTmpFont, pOutDev );
3535 aTmpFont.SetTransparent( sal_False );
3536 aTmpFont.SetEscapement( 0 );
3537 aTmpFont.SetPhysFont( pOutDev );
3538 long nCharWidth = aTmpFont.QuickGetTextSize( pOutDev, pTextPortion->GetExtraValue(), 0, 1, NULL ).Width();
3539 long nChars = 2;
3540 if( nCharWidth )
3541 nChars = pTextPortion->GetSize().Width() / nCharWidth;
3542 if ( nChars < 2 )
3543 nChars = 2; // wird durch DrawStretchText gestaucht.
3544 else if ( nChars == 2 )
3545 nChars = 3; // sieht besser aus
3547 String aText;
3548 aText.Fill( (USHORT)nChars, pTextPortion->GetExtraValue() );
3549 pOutDev->DrawStretchText( aTmpPos, pTextPortion->GetSize().Width(), aText );
3552 break;
3554 nIndex = nIndex + pTextPortion->GetLen();
3558 if ( ( nLine != nLastLine ) && !aStatus.IsOutliner() )
3560 if ( !IsVertical() )
3561 aStartPos.Y() += nSBL;
3562 else
3563 aStartPos.X() -= nSBL;
3566 // keine sichtbaren Aktionen mehr?
3567 if ( !IsVertical() && ( aStartPos.Y() >= aClipRec.Bottom() ) )
3568 break;
3569 else if ( IsVertical() && ( aStartPos.X() <= aClipRec.Left() ) )
3570 break;
3573 if ( !aStatus.IsOutliner() )
3575 const SvxULSpaceItem& rULItem = (const SvxULSpaceItem&)pPortion->GetNode()->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
3576 long nUL = GetYValue( rULItem.GetLower() );
3577 if ( !IsVertical() )
3578 aStartPos.Y() += nUL;
3579 else
3580 aStartPos.X() -= nUL;
3583 else
3585 if ( !IsVertical() )
3586 aStartPos.Y() += nParaHeight;
3587 else
3588 aStartPos.X() -= nParaHeight;
3591 if ( pPDFExtOutDevData )
3592 pPDFExtOutDevData->EndStructureElement();
3594 // keine sichtbaren Aktionen mehr?
3595 if ( !IsVertical() && ( aStartPos.Y() > aClipRec.Bottom() ) )
3596 break;
3597 if ( IsVertical() && ( aStartPos.X() < aClipRec.Left() ) )
3598 break;
3600 if ( aStatus.DoRestoreFont() )
3601 pOutDev->SetFont( aOldFont );
3604 void ImpEditEngine::Paint( ImpEditView* pView, const Rectangle& rRec, sal_Bool bUseVirtDev )
3606 DBG_ASSERT( pView, "Keine View - Kein Paint!" );
3607 DBG_CHKOBJ( GetEditEnginePtr(), EditEngine, 0 );
3609 if ( !GetUpdateMode() || IsInUndo() )
3610 return;
3612 // Schnittmenge aus Paintbereich und OutputArea.
3613 Rectangle aClipRec( pView->GetOutputArea() );
3614 aClipRec.Intersection( rRec );
3616 Window* pOutWin = pView->GetWindow();
3618 if ( bUseVirtDev )
3620 Rectangle aClipRecPixel( pOutWin->LogicToPixel( aClipRec ) );
3621 if ( !IsVertical() )
3623 // etwas mehr, falls abgerundet!
3624 aClipRecPixel.Right() += 1;
3625 aClipRecPixel.Bottom() += 1;
3627 else
3629 aClipRecPixel.Left() -= 1;
3630 aClipRecPixel.Bottom() += 1;
3633 // Wenn aClipRecPixel > XXXX, dann invalidieren ?!
3635 VirtualDevice* pVDev = GetVirtualDevice( pOutWin->GetMapMode(), pOutWin->GetDrawMode() );
3636 pVDev->SetDigitLanguage( GetRefDevice()->GetDigitLanguage() );
3639 Color aBackgroundColor( pView->GetBackgroundColor() );
3640 // #i47161# Check if text is visible on background
3641 SvxFont aTmpFont;
3642 ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
3643 SeekCursor( pNode, 1, aTmpFont );
3644 Color aFontColor( aTmpFont.GetColor() );
3645 if( aFontColor == COL_AUTO )
3646 aFontColor = GetAutoColor();
3648 // #i69346# check for reverse color of input method attribute
3649 if( mpIMEInfos && (mpIMEInfos->aPos.GetNode() == pNode &&
3650 mpIMEInfos->pAttribs))
3652 sal_uInt16 nAttr = mpIMEInfos->pAttribs[ 0 ];
3653 if ( nAttr & EXTTEXTINPUT_ATTR_HIGHLIGHT )
3655 const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
3656 aFontColor = rStyleSettings.GetHighlightColor() ;
3660 UINT8 nColorDiff = aFontColor.GetColorError( aBackgroundColor );
3661 if( nColorDiff < 8 )
3662 aBackgroundColor = aFontColor.IsDark() ? COL_WHITE : COL_BLACK;
3663 pVDev->SetBackground( aBackgroundColor );
3666 sal_Bool bVDevValid = sal_True;
3667 Size aOutSz( pVDev->GetOutputSizePixel() );
3668 if ( ( aOutSz.Width() < aClipRecPixel.GetWidth() ) ||
3669 ( aOutSz.Height() < aClipRecPixel.GetHeight() ) )
3671 bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3673 else
3675 // Das VirtDev kann bei einem Resize sehr gross werden =>
3676 // irgendwann mal kleiner machen!
3677 if ( ( aOutSz.Height() > ( aClipRecPixel.GetHeight() + RESDIFF ) ) ||
3678 ( aOutSz.Width() > ( aClipRecPixel.GetWidth() + RESDIFF ) ) )
3680 bVDevValid = pVDev->SetOutputSizePixel( aClipRecPixel.GetSize() );
3682 else
3684 pVDev->Erase();
3687 DBG_ASSERT( bVDevValid, "VDef konnte nicht vergroessert werden!" );
3688 if ( !bVDevValid )
3690 Paint( pView, rRec, sal_False /* ohne VDev */ );
3691 return;
3694 // PaintRect fuer VDev nicht mit alignter Groesse,
3695 // da sonst die Zeile darunter auch ausgegeben werden muss:
3696 Rectangle aTmpRec( Point( 0, 0 ), aClipRec.GetSize() );
3698 aClipRec = pOutWin->PixelToLogic( aClipRecPixel );
3699 Point aStartPos;
3700 if ( !IsVertical() )
3702 aStartPos = aClipRec.TopLeft();
3703 aStartPos = pView->GetDocPos( aStartPos );
3704 aStartPos.X() *= (-1);
3705 aStartPos.Y() *= (-1);
3707 else
3709 aStartPos = aClipRec.TopRight();
3710 Point aDocPos( pView->GetDocPos( aStartPos ) );
3711 aStartPos.X() = aClipRec.GetSize().Width() + aDocPos.Y();
3712 aStartPos.Y() = -aDocPos.X();
3715 Paint( pVDev, aTmpRec, aStartPos );
3717 sal_Bool bClipRegion = sal_False;
3718 Region aOldRegion;
3719 MapMode aOldMapMode;
3720 if ( GetTextRanger() )
3722 // Some problems here with push/pop, why?!
3723 // pOutWin->Push( PUSH_CLIPREGION|PUSH_MAPMODE );
3724 bClipRegion = pOutWin->IsClipRegion();
3725 aOldRegion = pOutWin->GetClipRegion();
3726 // Wie bekomme ich das Polygon an die richtige Stelle????
3727 // Das Polygon bezieht sich auf die View, nicht auf das Window
3728 // => Origin umsetzen...
3729 aOldMapMode = pOutWin->GetMapMode();
3730 Point aOrigin = aOldMapMode.GetOrigin();
3731 Point aViewPos = pView->GetOutputArea().TopLeft();
3732 aOrigin.Move( aViewPos.X(), aViewPos.Y() );
3733 aClipRec.Move( -aViewPos.X(), -aViewPos.Y() );
3734 MapMode aNewMapMode( aOldMapMode );
3735 aNewMapMode.SetOrigin( aOrigin );
3736 pOutWin->SetMapMode( aNewMapMode );
3737 pOutWin->SetClipRegion( Region( GetTextRanger()->GetPolyPolygon() ) );
3740 pOutWin->DrawOutDev( aClipRec.TopLeft(), aClipRec.GetSize(),
3741 Point(0,0), aClipRec.GetSize(), *pVDev );
3743 if ( GetTextRanger() )
3745 // pOutWin->Pop();
3746 if ( bClipRegion )
3747 pOutWin->SetClipRegion( aOldRegion );
3748 else
3749 pOutWin->SetClipRegion();
3750 pOutWin->SetMapMode( aOldMapMode );
3754 pView->DrawSelection();
3756 else
3758 Point aStartPos;
3759 if ( !IsVertical() )
3761 aStartPos = pView->GetOutputArea().TopLeft();
3762 aStartPos.X() -= pView->GetVisDocLeft();
3763 aStartPos.Y() -= pView->GetVisDocTop();
3765 else
3767 aStartPos = pView->GetOutputArea().TopRight();
3768 aStartPos.X() += pView->GetVisDocTop();
3769 aStartPos.Y() -= pView->GetVisDocLeft();
3772 // Wenn Doc-Breite < OutputArea,Width, nicht umgebrochene Felder,
3773 // stehen die Felder sonst �ber, wenn > Zeile.
3774 // ( Oben nicht, da dort bereits Doc-Breite von Formatierung mit drin )
3775 if ( !IsVertical() && ( pView->GetOutputArea().GetWidth() > GetPaperSize().Width() ) )
3777 long nMaxX = pView->GetOutputArea().Left() + GetPaperSize().Width();
3778 if ( aClipRec.Left() > nMaxX )
3779 return;
3780 if ( aClipRec.Right() > nMaxX )
3781 aClipRec.Right() = nMaxX;
3784 sal_Bool bClipRegion = pOutWin->IsClipRegion();
3785 Region aOldRegion = pOutWin->GetClipRegion();
3786 pOutWin->IntersectClipRegion( aClipRec );
3788 Paint( pOutWin, aClipRec, aStartPos );
3790 if ( bClipRegion )
3791 pOutWin->SetClipRegion( aOldRegion );
3792 else
3793 pOutWin->SetClipRegion();
3795 pView->DrawSelection();
3800 void ImpEditEngine::InsertContent( ContentNode* pNode, sal_uInt16 nPos )
3802 DBG_ASSERT( pNode, "NULL-Poointer in InsertContent! " );
3803 DBG_ASSERT( IsInUndo(), "InsertContent nur fuer Undo()!" );
3804 ParaPortion* pNew = new ParaPortion( pNode );
3805 GetParaPortions().Insert( pNew, nPos );
3806 aEditDoc.Insert( pNode, nPos );
3807 if ( IsCallParaInsertedOrDeleted() )
3808 GetEditEnginePtr()->ParagraphInserted( nPos );
3811 EditPaM ImpEditEngine::SplitContent( sal_uInt16 nNode, sal_uInt16 nSepPos )
3813 ContentNode* pNode = aEditDoc.SaveGetObject( nNode );
3814 DBG_ASSERT( pNode, "Ungueltiger Node in SplitContent" );
3815 DBG_ASSERT( IsInUndo(), "SplitContent nur fuer Undo()!" );
3816 DBG_ASSERT( nSepPos <= pNode->Len(), "Index im Wald: SplitContent" );
3817 EditPaM aPaM( pNode, nSepPos );
3818 return ImpInsertParaBreak( aPaM );
3821 EditPaM ImpEditEngine::ConnectContents( sal_uInt16 nLeftNode, sal_Bool bBackward )
3823 ContentNode* pLeftNode = aEditDoc.SaveGetObject( nLeftNode );
3824 ContentNode* pRightNode = aEditDoc.SaveGetObject( nLeftNode+1 );
3825 DBG_ASSERT( pLeftNode, "Ungueltiger linker Node in ConnectContents" );
3826 DBG_ASSERT( pRightNode, "Ungueltiger rechter Node in ConnectContents" );
3827 DBG_ASSERT( IsInUndo(), "ConnectContent nur fuer Undo()!" );
3828 return ImpConnectParagraphs( pLeftNode, pRightNode, bBackward );
3831 void ImpEditEngine::SetUpdateMode( sal_Bool bUp, EditView* pCurView, sal_Bool bForceUpdate )
3833 sal_Bool bChanged = ( GetUpdateMode() != bUp );
3835 // Beim Umschalten von sal_True auf sal_False waren alle Selektionen sichtbar,
3836 // => Wegmalen
3837 // Umgekehrt waren alle unsichtbar => malen
3839 // DrawAllSelections(); sieht im Outliner schlecht aus !
3840 // EditView* pView = aEditViewList.First();
3841 // while ( pView )
3842 // {
3843 // DBG_CHKOBJ( pView, EditView, 0 );
3844 // pView->pImpEditView->DrawSelection();
3845 // pView = aEditViewList.Next();
3846 // }
3848 // Wenn !bFormatted, also z.B. nach SetText, braucht bei UpdateMode sal_True
3849 // nicht sofort formatiert werden, weil warscheinlich noch Text kommt.
3850 // Spaetestens bei einem Paint / CalcTextWidth wird formatiert.
3852 bUpdate = bUp;
3853 if ( bUpdate && ( bChanged || bForceUpdate ) )
3854 FormatAndUpdate( pCurView );
3857 void ImpEditEngine::ShowParagraph( sal_uInt16 nParagraph, sal_Bool bShow )
3859 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3860 DBG_ASSERT( pPPortion, "ShowParagraph: Absatz existiert nicht!" );
3861 if ( pPPortion && ( pPPortion->IsVisible() != bShow ) )
3863 pPPortion->SetVisible( bShow );
3865 if ( !bShow )
3867 // Als deleted kenzeichnen, damit keine Selektion auf diesem
3868 // Absatz beginnt oder endet...
3869 DeletedNodeInfo* pDelInfo = new DeletedNodeInfo( (sal_uIntPtr)pPPortion->GetNode(), nParagraph );
3870 aDeletedNodes.Insert( pDelInfo, aDeletedNodes.Count() );
3871 UpdateSelections();
3872 // Dann kriege ich den unteren Bereich nicht invalidiert,
3873 // wenn UpdateMode = sal_False!
3874 // Wenn doch, dann vor SetVisible auf sal_False merken!
3875 // nCurTextHeight -= pPPortion->GetHeight();
3878 if ( bShow && ( pPPortion->IsInvalid() || !pPPortion->nHeight ) )
3880 if ( !GetTextRanger() )
3882 if ( pPPortion->IsInvalid() )
3884 Font aOldFont( GetRefDevice()->GetFont() );
3885 CreateLines( nParagraph, 0 ); // 0: Kein TextRanger
3886 if ( aStatus.DoRestoreFont() )
3887 GetRefDevice()->SetFont( aOldFont );
3889 else
3891 CalcHeight( pPPortion );
3893 nCurTextHeight += pPPortion->GetHeight();
3895 else
3897 nCurTextHeight = 0x7fffffff;
3901 pPPortion->SetMustRepaint( sal_True );
3902 if ( GetUpdateMode() && !IsInUndo() && !GetTextRanger() )
3904 aInvalidRec = Rectangle( Point( 0, GetParaPortions().GetYOffset( pPPortion ) ),
3905 Point( GetPaperSize().Width(), nCurTextHeight ) );
3906 UpdateViews( GetActiveView() );
3911 sal_Bool ImpEditEngine::IsParagraphVisible( sal_uInt16 nParagraph )
3913 ParaPortion* pPPortion = GetParaPortions().SaveGetObject( nParagraph );
3914 DBG_ASSERT( pPPortion, "IsParagraphVisible: Absatz existiert nicht!" );
3915 if ( pPPortion )
3916 return pPPortion->IsVisible();
3917 return sal_False;
3920 EditSelection ImpEditEngine::MoveParagraphs( Range aOldPositions, sal_uInt16 nNewPos, EditView* pCurView )
3922 DBG_ASSERT( GetParaPortions().Count() != 0, "Keine Absaetze gefunden: MoveParagraphs" );
3923 if ( GetParaPortions().Count() == 0 )
3924 return EditSelection();
3925 aOldPositions.Justify();
3927 EditSelection aSel( ImpMoveParagraphs( aOldPositions, nNewPos ) );
3929 if ( nNewPos >= GetParaPortions().Count() )
3930 nNewPos = GetParaPortions().Count() - 1;
3932 // Dort, wo der Absatz eingefuegt wurde, muss richtig gepainted werden:
3933 // Dort, wo der Absatz entfernt wurde, muss richtig gepainted werden:
3934 // ( Und dazwischen entsprechend auch...)
3935 if ( pCurView && ( GetUpdateMode() == sal_True ) )
3937 // in diesem Fall kann ich direkt neu malen, ohne die
3938 // Portions zu Invalidieren.
3939 sal_uInt16 nFirstPortion = Min( (sal_uInt16)aOldPositions.Min(), nNewPos );
3940 sal_uInt16 nLastPortion = Max( (sal_uInt16)aOldPositions.Max(), nNewPos );
3942 ParaPortion* pUpperPortion = GetParaPortions().SaveGetObject( nFirstPortion );
3943 ParaPortion* pLowerPortion = GetParaPortions().SaveGetObject( nLastPortion );
3945 aInvalidRec = Rectangle(); // leermachen
3946 aInvalidRec.Left() = 0;
3947 aInvalidRec.Right() = aPaperSize.Width();
3948 aInvalidRec.Top() = GetParaPortions().GetYOffset( pUpperPortion );
3949 aInvalidRec.Bottom() = GetParaPortions().GetYOffset( pLowerPortion ) + pLowerPortion->GetHeight();
3951 UpdateViews( pCurView );
3953 else
3955 // aber der oberen ungueltigen Position neu painten...
3956 sal_uInt16 nFirstInvPara = Min( (sal_uInt16)aOldPositions.Min(), nNewPos );
3957 InvalidateFromParagraph( nFirstInvPara );
3959 return aSel;
3962 void ImpEditEngine::InvalidateFromParagraph( sal_uInt16 nFirstInvPara )
3964 // Es werden nicht die folgenden Absaetze invalidiert,
3965 // da ResetHeight() => Groessenanderung => alles folgende wird
3966 // sowieso neu ausgegeben.
3967 ParaPortion* pTmpPortion;
3968 if ( nFirstInvPara != 0 )
3970 pTmpPortion = GetParaPortions().GetObject( nFirstInvPara-1 );
3971 pTmpPortion->MarkInvalid( pTmpPortion->GetNode()->Len(), 0 );
3973 else
3975 pTmpPortion = GetParaPortions().GetObject( 0 );
3976 pTmpPortion->MarkSelectionInvalid( 0, pTmpPortion->GetNode()->Len() );
3978 pTmpPortion->ResetHeight();
3981 IMPL_LINK_INLINE_START( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
3983 CallStatusHdl();
3984 return 0;
3986 IMPL_LINK_INLINE_END( ImpEditEngine, StatusTimerHdl, Timer *, EMPTYARG )
3988 void ImpEditEngine::CallStatusHdl()
3990 if ( aStatusHdlLink.IsSet() && aStatus.GetStatusWord() )
3992 // Der Status muss vor Call zurueckgesetzt werden,
3993 // da im Hdl evtl. weitere Fags gesetzt werden...
3994 EditStatus aTmpStatus( aStatus );
3995 aStatus.Clear();
3996 aStatusHdlLink.Call( &aTmpStatus );
3997 aStatusTimer.Stop(); // Falls von Hand gerufen...
4001 ContentNode* ImpEditEngine::GetPrevVisNode( ContentNode* pCurNode )
4003 ParaPortion* pPortion = FindParaPortion( pCurNode );
4004 DBG_ASSERT( pPortion, "GetPrevVisibleNode: Keine passende Portion!" );
4005 pPortion = GetPrevVisPortion( pPortion );
4006 if ( pPortion )
4007 return pPortion->GetNode();
4008 return 0;
4011 ContentNode* ImpEditEngine::GetNextVisNode( ContentNode* pCurNode )
4013 ParaPortion* pPortion = FindParaPortion( pCurNode );
4014 DBG_ASSERT( pPortion, "GetNextVisibleNode: Keine passende Portion!" );
4015 pPortion = GetNextVisPortion( pPortion );
4016 if ( pPortion )
4017 return pPortion->GetNode();
4018 return 0;
4021 ParaPortion* ImpEditEngine::GetPrevVisPortion( ParaPortion* pCurPortion )
4023 sal_uInt16 nPara = GetParaPortions().GetPos( pCurPortion );
4024 DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisPortion" );
4025 ParaPortion* pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4026 while ( pPortion && !pPortion->IsVisible() )
4027 pPortion = nPara ? GetParaPortions()[--nPara] : 0;
4029 return pPortion;
4032 ParaPortion* ImpEditEngine::GetNextVisPortion( ParaPortion* pCurPortion )
4034 sal_uInt16 nPara = GetParaPortions().GetPos( pCurPortion );
4035 DBG_ASSERT( nPara < GetParaPortions().Count() , "Portion nicht gefunden: GetPrevVisNode" );
4036 ParaPortion* pPortion = GetParaPortions().SaveGetObject( ++nPara );
4037 while ( pPortion && !pPortion->IsVisible() )
4038 pPortion = GetParaPortions().SaveGetObject( ++nPara );
4040 return pPortion;
4043 EditPaM ImpEditEngine::InsertParagraph( sal_uInt16 nPara )
4045 EditPaM aPaM;
4046 if ( nPara != 0 )
4048 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara-1 );
4049 if ( !pNode )
4050 pNode = GetEditDoc().SaveGetObject( GetEditDoc().Count() - 1 );
4051 DBG_ASSERT( pNode, "Kein einziger Absatz in InsertParagraph ?" );
4052 aPaM = EditPaM( pNode, pNode->Len() );
4054 else
4056 ContentNode* pNode = GetEditDoc().SaveGetObject( 0 );
4057 aPaM = EditPaM( pNode, 0 );
4060 return ImpInsertParaBreak( aPaM );
4063 EditSelection* ImpEditEngine::SelectParagraph( sal_uInt16 nPara )
4065 EditSelection* pSel = 0;
4066 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
4067 DBG_ASSERTWARNING( pNode, "Absatz existiert nicht: SelectParagraph" );
4068 if ( pNode )
4069 pSel = new EditSelection( EditPaM( pNode, 0 ), EditPaM( pNode, pNode->Len() ) );
4071 return pSel;
4074 void ImpEditEngine::FormatAndUpdate( EditView* pCurView )
4076 if ( bDowning )
4077 return ;
4079 if ( IsInUndo() )
4080 IdleFormatAndUpdate( pCurView );
4081 else
4083 FormatDoc();
4084 UpdateViews( pCurView );
4088 void ImpEditEngine::SetFlatMode( sal_Bool bFlat )
4090 if ( bFlat != aStatus.UseCharAttribs() )
4091 return;
4093 if ( !bFlat )
4094 aStatus.TurnOnFlags( EE_CNTRL_USECHARATTRIBS );
4095 else
4096 aStatus.TurnOffFlags( EE_CNTRL_USECHARATTRIBS );
4098 aEditDoc.CreateDefFont( !bFlat );
4100 FormatFullDoc();
4101 UpdateViews( (EditView*) 0);
4102 if ( pActiveView )
4103 pActiveView->ShowCursor();
4106 void ImpEditEngine::SetCharStretching( sal_uInt16 nX, sal_uInt16 nY )
4108 bool bChanged(false);
4109 if ( !IsVertical() )
4111 bChanged = nStretchX!=nX || nStretchY!=nY;
4112 nStretchX = nX;
4113 nStretchY = nY;
4115 else
4117 bChanged = nStretchX!=nY || nStretchY!=nX;
4118 nStretchX = nY;
4119 nStretchY = nX;
4122 if (bChanged && aStatus.DoStretch())
4124 FormatFullDoc();
4125 // (potentially) need everything redrawn
4126 aInvalidRec=Rectangle(0,0,1000000,1000000);
4127 UpdateViews( GetActiveView() );
4131 void ImpEditEngine::DoStretchChars( sal_uInt16 nX, sal_uInt16 nY )
4133 UndoActionStart( EDITUNDO_STRETCH );
4134 sal_uInt16 nParas = GetEditDoc().Count();
4135 for ( sal_uInt16 nPara = 0; nPara < nParas; nPara++ )
4137 ContentNode* pNode = GetEditDoc()[nPara];
4138 SfxItemSet aTmpSet( pNode->GetContentAttribs().GetItems() );
4140 if ( nX != 100 )
4142 // Fontbreite
4143 SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*) pNode->GetContentAttribs().GetItem( EE_CHAR_FONTWIDTH ).Clone();
4144 sal_uInt32 nProp = pNewWidth->GetValue(); // sal_uInt32, kann temporaer gross werden
4145 nProp *= nX;
4146 nProp /= 100;
4147 pNewWidth->SetValue( (sal_uInt16)nProp );
4148 aTmpSet.Put( *pNewWidth );
4149 delete pNewWidth;
4151 // Kerning:
4152 const SvxKerningItem& rKerningItem =
4153 (const SvxKerningItem&)pNode->GetContentAttribs().GetItem( EE_CHAR_KERNING );
4154 SvxKerningItem* pNewKerning = (SvxKerningItem*)rKerningItem.Clone();
4155 long nKerning = pNewKerning->GetValue();
4156 if ( nKerning > 0 )
4158 nKerning *= nX;
4159 nKerning /= 100;
4161 else if ( nKerning < 0 )
4163 // Bei Negativen Werten:
4164 // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
4165 nKerning *= 100;
4166 nKerning /= nX;
4168 pNewKerning->SetValue( (short)nKerning );
4169 aTmpSet.Put( *pNewKerning);
4170 delete pNewKerning;
4172 else
4173 aTmpSet.ClearItem( EE_CHAR_FONTWIDTH );
4175 if ( nY != 100 )
4177 // Fonthoehe
4178 for ( int nItem = 0; nItem < 3; nItem++ )
4180 USHORT nItemId = EE_CHAR_FONTHEIGHT;
4181 if ( nItem == 1 )
4182 nItemId = EE_CHAR_FONTHEIGHT_CJK;
4183 else if ( nItem == 2 )
4184 nItemId = EE_CHAR_FONTHEIGHT_CTL;
4186 const SvxFontHeightItem& rHeightItem =
4187 (const SvxFontHeightItem&)pNode->GetContentAttribs().GetItem( nItemId );
4188 SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)rHeightItem.Clone();
4189 sal_uInt32 nHeight = pNewHeight->GetHeight();
4190 nHeight *= nY;
4191 nHeight /= 100;
4192 pNewHeight->SetHeightValue( nHeight );
4193 aTmpSet.Put( *pNewHeight );
4194 delete pNewHeight;
4197 // Absatzabstaende
4198 const SvxULSpaceItem& rULSpaceItem =
4199 (const SvxULSpaceItem&)pNode->GetContentAttribs().GetItem( EE_PARA_ULSPACE );
4200 SvxULSpaceItem* pNewUL = (SvxULSpaceItem*)rULSpaceItem.Clone();
4201 sal_uInt32 nUpper = pNewUL->GetUpper();
4202 nUpper *= nY;
4203 nUpper /= 100;
4204 pNewUL->SetUpper( (sal_uInt16)nUpper );
4205 sal_uInt32 nLower = pNewUL->GetLower();
4206 nLower *= nY;
4207 nLower /= 100;
4208 pNewUL->SetLower( (sal_uInt16)nLower );
4209 aTmpSet.Put( *pNewUL );
4210 delete pNewUL;
4212 else
4213 aTmpSet.ClearItem( EE_CHAR_FONTHEIGHT );
4215 SetParaAttribs( nPara, aTmpSet );
4217 // harte Attribute:
4218 sal_uInt16 nLastEnd = 0; // damit nach entfernen und neu nicht nochmal
4219 CharAttribArray& rAttribs = pNode->GetCharAttribs().GetAttribs();
4220 sal_uInt16 nAttribs = rAttribs.Count();
4221 for ( sal_uInt16 nAttr = 0; nAttr < nAttribs; nAttr++ )
4223 EditCharAttrib* pAttr = rAttribs[nAttr];
4224 if ( pAttr->GetStart() >= nLastEnd )
4226 sal_uInt16 nWhich = pAttr->Which();
4227 SfxPoolItem* pNew = 0;
4228 if ( nWhich == EE_CHAR_FONTHEIGHT )
4230 SvxFontHeightItem* pNewHeight = (SvxFontHeightItem*)pAttr->GetItem()->Clone();
4231 sal_uInt32 nHeight = pNewHeight->GetHeight();
4232 nHeight *= nY;
4233 nHeight /= 100;
4234 pNewHeight->SetHeightValue( nHeight );
4235 pNew = pNewHeight;
4237 else if ( nWhich == EE_CHAR_FONTWIDTH )
4239 SvxCharScaleWidthItem* pNewWidth = (SvxCharScaleWidthItem*)pAttr->GetItem()->Clone();
4240 sal_uInt32 nProp = pNewWidth->GetValue();
4241 nProp *= nX;
4242 nProp /= 100;
4243 pNewWidth->SetValue( (sal_uInt16)nProp );
4244 pNew = pNewWidth;
4246 else if ( nWhich == EE_CHAR_KERNING )
4248 SvxKerningItem* pNewKerning = (SvxKerningItem*)pAttr->GetItem()->Clone();
4249 long nKerning = pNewKerning->GetValue();
4250 if ( nKerning > 0 )
4252 nKerning *= nX;
4253 nKerning /= 100;
4255 else if ( nKerning < 0 )
4257 // Bei Negativen Werten:
4258 // Bei Stretching > 100 muessen die Werte kleiner werden und umgekehrt.
4259 nKerning *= 100;
4260 nKerning /= nX;
4262 pNewKerning->SetValue( (short)nKerning );
4263 pNew = pNewKerning;
4265 if ( pNew )
4267 SfxItemSet _aTmpSet( GetEmptyItemSet() );
4268 _aTmpSet.Put( *pNew );
4269 SetAttribs( EditSelection( EditPaM( pNode, pAttr->GetStart() ),
4270 EditPaM( pNode, pAttr->GetEnd() ) ), _aTmpSet );
4272 nLastEnd = pAttr->GetEnd();
4273 delete pNew;
4278 UndoActionEnd( EDITUNDO_STRETCH );
4281 const SvxNumberFormat* ImpEditEngine::GetNumberFormat( const ContentNode *pNode ) const
4283 const SvxNumberFormat *pRes = 0;
4285 if (pNode)
4287 // get index of paragraph
4288 USHORT nPara = GetEditDoc().GetPos( const_cast< ContentNode * >(pNode) );
4289 DBG_ASSERT( nPara < USHRT_MAX, "node not found in array" );
4290 if (nPara < USHRT_MAX)
4292 // the called function may be overloaded by an OutlinerEditEng object to provide
4293 // access to the SvxNumberFormat of the Outliner.
4294 // The EditEngine implementation will just return 0.
4295 pRes = pEditEngine->GetNumberFormat( nPara );
4299 return pRes;
4302 sal_Int32 ImpEditEngine::GetSpaceBeforeAndMinLabelWidth(
4303 const ContentNode *pNode,
4304 sal_Int32 *pnSpaceBefore, sal_Int32 *pnMinLabelWidth ) const
4306 // nSpaceBefore matches the ODF attribut text:space-before
4307 // nMinLabelWidth matches the ODF attribut text:min-label-width
4309 const SvxNumberFormat *pNumFmt = GetNumberFormat( pNode );
4311 // if no number format was found we have no Outliner or the numbering level
4312 // within the Outliner is -1 which means no number format should be applied.
4313 // Thus the default values to be returned are 0.
4314 sal_Int32 nSpaceBefore = 0;
4315 sal_Int32 nMinLabelWidth = 0;
4317 if (pNumFmt)
4319 nMinLabelWidth = -pNumFmt->GetFirstLineOffset();
4320 nSpaceBefore = pNumFmt->GetAbsLSpace() - nMinLabelWidth;
4321 DBG_ASSERT( nMinLabelWidth >= 0, "ImpEditEngine::GetSpaceBeforeAndMinLabelWidth: min-label-width < 0 encountered" );
4323 if (pnSpaceBefore)
4324 *pnSpaceBefore = nSpaceBefore;
4325 if (pnMinLabelWidth)
4326 *pnMinLabelWidth = nMinLabelWidth;
4328 return nSpaceBefore + nMinLabelWidth;
4331 const SvxLRSpaceItem& ImpEditEngine::GetLRSpaceItem( ContentNode* pNode )
4333 return (const SvxLRSpaceItem&)pNode->GetContentAttribs().GetItem( aStatus.IsOutliner() ? EE_PARA_OUTLLRSPACE : EE_PARA_LRSPACE );
4336 // Either sets the digit mode at the output device or
4337 // modifies the passed string according to the text numeral setting:
4338 void ImpEditEngine::ImplInitDigitMode( OutputDevice* pOutDev, String* pString, xub_StrLen nStt, xub_StrLen nLen, LanguageType eCurLang )
4340 // #114278# Also setting up digit language from Svt options
4341 // (cannot reliably inherit the outdev's setting)
4342 if( !pCTLOptions )
4343 pCTLOptions = new SvtCTLOptions;
4345 LanguageType eLang = eCurLang;
4346 const SvtCTLOptions::TextNumerals nCTLTextNumerals = pCTLOptions->GetCTLTextNumerals();
4348 if ( SvtCTLOptions::NUMERALS_HINDI == nCTLTextNumerals )
4349 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4350 else if ( SvtCTLOptions::NUMERALS_ARABIC == nCTLTextNumerals )
4351 eLang = LANGUAGE_ENGLISH;
4352 else if ( SvtCTLOptions::NUMERALS_SYSTEM == nCTLTextNumerals )
4353 eLang = (LanguageType) Application::GetSettings().GetLanguage();
4355 if(pOutDev)
4357 pOutDev->SetDigitLanguage( eLang );
4359 else if (pString)
4361 // see sallayout.cxx in vcl
4362 int nOffset;
4363 switch( eLang & LANGUAGE_MASK_PRIMARY )
4365 default:
4366 nOffset = 0;
4367 break;
4368 case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
4369 nOffset = 0x0660 - '0'; // arabic-indic digits
4370 break;
4371 case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
4372 case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
4373 case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
4374 nOffset = 0x06F0 - '0'; // eastern arabic-indic digits
4375 break;
4377 if (nOffset)
4379 const xub_StrLen nEnd = nStt + nLen;
4380 for( xub_StrLen nIdx = nStt; nIdx < nEnd; ++nIdx )
4382 sal_Unicode nChar = pString->GetChar( nIdx );
4383 if( (nChar < '0') || ('9' < nChar) )
4384 continue;
4385 nChar = (sal_Unicode)(nChar + nOffset);
4386 pString->SetChar( nIdx, nChar );
4392 void ImpEditEngine::ImplInitLayoutMode( OutputDevice* pOutDev, USHORT nPara, USHORT nIndex )
4394 BOOL bCTL = FALSE;
4395 BYTE bR2L = FALSE;
4396 if ( nIndex == 0xFFFF )
4398 bCTL = HasScriptType( nPara, i18n::ScriptType::COMPLEX );
4399 bR2L = IsRightToLeft( nPara );
4401 else
4403 ContentNode* pNode = GetEditDoc().SaveGetObject( nPara );
4404 short nScriptType = GetScriptType( EditPaM( pNode, nIndex+1 ) );
4405 bCTL = nScriptType == i18n::ScriptType::COMPLEX;
4406 bR2L = GetRightToLeft( nPara, nIndex + 1); // this change was discussed in issue 37190
4407 // it also works for issue 55927
4410 ULONG nLayoutMode = pOutDev->GetLayoutMode();
4412 // We always use the left postion for DrawText()
4413 nLayoutMode &= ~(TEXT_LAYOUT_BIDI_RTL);
4415 if ( !bCTL && !bR2L)
4417 // No CTL/Bidi checking neccessary
4418 nLayoutMode |= ( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4420 else
4422 // CTL/Bidi checking neccessary
4423 // Don't use BIDI_STRONG, VCL must do some checks.
4424 nLayoutMode &= ~( TEXT_LAYOUT_COMPLEX_DISABLED | TEXT_LAYOUT_BIDI_STRONG );
4426 if ( bR2L )
4427 nLayoutMode |= TEXT_LAYOUT_BIDI_RTL|TEXT_LAYOUT_TEXTORIGIN_LEFT;
4430 pOutDev->SetLayoutMode( nLayoutMode );
4432 // #114278# Also setting up digit language from Svt options
4433 // (cannot reliably inherit the outdev's setting)
4434 LanguageType eLang;
4436 if( !pCTLOptions )
4437 pCTLOptions = new SvtCTLOptions;
4439 if ( SvtCTLOptions::NUMERALS_HINDI == pCTLOptions->GetCTLTextNumerals() )
4440 eLang = LANGUAGE_ARABIC_SAUDI_ARABIA;
4441 else if ( SvtCTLOptions::NUMERALS_ARABIC == pCTLOptions->GetCTLTextNumerals() )
4442 eLang = LANGUAGE_ENGLISH;
4443 else
4444 eLang = (LanguageType) Application::GetSettings().GetLanguage();
4446 pOutDev->SetDigitLanguage( eLang );
4449 Reference < i18n::XBreakIterator > ImpEditEngine::ImplGetBreakIterator() const
4451 if ( !xBI.is() )
4453 Reference< lang::XMultiServiceFactory > xMSF( ::comphelper::getProcessServiceFactory() );
4454 xBI.set( xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) ), UNO_QUERY );
4456 return xBI;
4459 Reference < i18n::XExtendedInputSequenceChecker > ImpEditEngine::ImplGetInputSequenceChecker() const
4461 if ( !xISC.is() )
4463 Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
4464 Reference < XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
4465 if ( xI.is() )
4467 Any x = xI->queryInterface( ::getCppuType((const Reference< i18n::XExtendedInputSequenceChecker >*)0) );
4468 x >>= xISC;
4471 return xISC;
4474 Color ImpEditEngine::GetAutoColor() const
4476 Color aColor = const_cast<ImpEditEngine*>(this)->GetColorConfig().GetColorValue( svtools::FONTCOLOR ).nColor;
4478 if ( GetBackgroundColor() != COL_AUTO )
4480 if ( GetBackgroundColor().IsDark() && aColor.IsDark() )
4481 aColor = COL_WHITE;
4482 else if ( GetBackgroundColor().IsBright() && aColor.IsBright() )
4483 aColor = COL_BLACK;
4486 return aColor;
4490 BOOL ImpEditEngine::ImplCalcAsianCompression( ContentNode* pNode, TextPortion* pTextPortion, USHORT nStartPos, sal_Int32* pDXArray, USHORT n100thPercentFromMax, BOOL bManipulateDXArray )
4492 DBG_ASSERT( GetAsianCompressionMode(), "ImplCalcAsianCompression - Why?" );
4493 DBG_ASSERT( pTextPortion->GetLen(), "ImplCalcAsianCompression - Empty Portion?" );
4495 // Percent is 1/100 Percent...
4497 if ( n100thPercentFromMax == 10000 )
4498 pTextPortion->SetExtraInfos( NULL );
4500 BOOL bCompressed = FALSE;
4502 if ( GetScriptType( EditPaM( pNode, nStartPos+1 ) ) == i18n::ScriptType::ASIAN )
4504 long nNewPortionWidth = pTextPortion->GetSize().Width();
4505 USHORT nPortionLen = pTextPortion->GetLen();
4506 for ( USHORT n = 0; n < nPortionLen; n++ )
4508 BYTE nType = GetCharTypeForCompression( pNode->GetChar( n+nStartPos ) );
4510 BOOL bCompressPunctuation = ( nType == CHAR_PUNCTUATIONLEFT ) || ( nType == CHAR_PUNCTUATIONRIGHT );
4511 BOOL bCompressKana = ( nType == CHAR_KANA ) && ( GetAsianCompressionMode() == text::CharacterCompressionType::PUNCTUATION_AND_KANA );
4513 // create Extra infos only if needed...
4514 if ( bCompressPunctuation || bCompressKana )
4516 if ( !pTextPortion->GetExtraInfos() )
4518 ExtraPortionInfo* pExtraInfos = new ExtraPortionInfo;
4519 pTextPortion->SetExtraInfos( pExtraInfos );
4520 pExtraInfos->nOrgWidth = pTextPortion->GetSize().Width();
4521 pExtraInfos->nAsianCompressionTypes = CHAR_NORMAL;
4523 pTextPortion->GetExtraInfos()->nMaxCompression100thPercent = n100thPercentFromMax;
4524 pTextPortion->GetExtraInfos()->nAsianCompressionTypes |= nType;
4525 // pTextPortion->GetExtraInfos()->nCompressedChars++;
4527 long nOldCharWidth;
4528 if ( (n+1) < nPortionLen )
4530 nOldCharWidth = pDXArray[n];
4532 else
4534 if ( bManipulateDXArray )
4535 nOldCharWidth = nNewPortionWidth - pTextPortion->GetExtraInfos()->nPortionOffsetX;
4536 else
4537 nOldCharWidth = pTextPortion->GetExtraInfos()->nOrgWidth;
4539 nOldCharWidth -= ( n ? pDXArray[n-1] : 0 );
4541 long nCompress = 0;
4543 if ( bCompressPunctuation )
4545 // pTextPortion->GetExtraInfos()->nComressionWeight += 5;
4546 nCompress = nOldCharWidth / 2;
4548 else // Kana
4550 // pTextPortion->GetExtraInfos()->nComressionWeight += 1;
4551 nCompress = nOldCharWidth / 10;
4554 if ( n100thPercentFromMax != 10000 )
4556 nCompress *= n100thPercentFromMax;
4557 nCompress /= 10000;
4560 if ( nCompress )
4562 bCompressed = TRUE;
4563 nNewPortionWidth -= nCompress;
4564 pTextPortion->GetExtraInfos()->bCompressed = TRUE;
4567 // Special handling for rightpunctuation: For the 'compression' we must
4568 // start th eoutput before the normal char position....
4569 if ( bManipulateDXArray && ( pTextPortion->GetLen() > 1 ) )
4571 if ( !pTextPortion->GetExtraInfos()->pOrgDXArray )
4572 pTextPortion->GetExtraInfos()->SaveOrgDXArray( pDXArray, pTextPortion->GetLen()-1 );
4574 if ( nType == CHAR_PUNCTUATIONRIGHT )
4576 // If it's the first char, I must handle it in Paint()...
4577 if ( n )
4579 // -1: No entry for the last character
4580 for ( USHORT i = n-1; i < (nPortionLen-1); i++ )
4581 pDXArray[i] -= nCompress;
4583 else
4585 pTextPortion->GetExtraInfos()->bFirstCharIsRightPunktuation = TRUE;
4586 pTextPortion->GetExtraInfos()->nPortionOffsetX = -nCompress;
4589 else
4591 // -1: No entry for the last character
4592 for ( USHORT i = n; i < (nPortionLen-1); i++ )
4593 pDXArray[i] -= nCompress;
4600 if ( bCompressed && ( n100thPercentFromMax == 10000 ) )
4601 pTextPortion->GetExtraInfos()->nWidthFullCompression = nNewPortionWidth;
4603 pTextPortion->GetSize().Width() = nNewPortionWidth;
4605 if ( pTextPortion->GetExtraInfos() && ( n100thPercentFromMax != 10000 ) )
4607 // Maybe rounding errors in nNewPortionWidth, assure that width not bigger than expected
4608 long nShrink = pTextPortion->GetExtraInfos()->nOrgWidth - pTextPortion->GetExtraInfos()->nWidthFullCompression;
4609 nShrink *= n100thPercentFromMax;
4610 nShrink /= 10000;
4611 long nNewWidth = pTextPortion->GetExtraInfos()->nOrgWidth - nShrink;
4612 if ( nNewWidth < pTextPortion->GetSize().Width() )
4613 pTextPortion->GetSize().Width() = nNewWidth;
4616 return bCompressed;
4620 void ImpEditEngine::ImplExpandCompressedPortions( EditLine* pLine, ParaPortion* pParaPortion, long nRemainingWidth )
4622 BOOL bFoundCompressedPortion = FALSE;
4623 long nCompressed = 0;
4624 // long nCompressWeight = 0;
4625 TextPortionList aCompressedPortions;
4627 USHORT nPortion = pLine->GetEndPortion();
4628 TextPortion* pTP = pParaPortion->GetTextPortions()[ nPortion ];
4629 while ( pTP && ( pTP->GetKind() == PORTIONKIND_TEXT ) )
4631 if ( pTP->GetExtraInfos() && pTP->GetExtraInfos()->bCompressed )
4633 bFoundCompressedPortion = TRUE;
4634 nCompressed += pTP->GetExtraInfos()->nOrgWidth - pTP->GetSize().Width();
4635 aCompressedPortions.Insert( pTP, aCompressedPortions.Count() );
4637 pTP = ( nPortion > pLine->GetStartPortion() ) ? pParaPortion->GetTextPortions()[ --nPortion ] : NULL;
4640 if ( bFoundCompressedPortion )
4642 long nCompressPercent = 0;
4643 if ( nCompressed > nRemainingWidth )
4645 nCompressPercent = nCompressed - nRemainingWidth;
4646 DBG_ASSERT( nCompressPercent < 200000, "ImplExpandCompressedPortions - Overflow!" );
4647 nCompressPercent *= 10000;
4648 nCompressPercent /= nCompressed;
4651 for ( USHORT n = 0; n < aCompressedPortions.Count(); n++ )
4653 pTP = aCompressedPortions[n];
4654 pTP->GetExtraInfos()->bCompressed = FALSE;
4655 pTP->GetSize().Width() = pTP->GetExtraInfos()->nOrgWidth;
4656 if ( nCompressPercent )
4658 USHORT nTxtPortion = pParaPortion->GetTextPortions().GetPos( pTP );
4659 USHORT nTxtPortionStart = pParaPortion->GetTextPortions().GetStartPos( nTxtPortion );
4660 DBG_ASSERT( nTxtPortionStart >= pLine->GetStart(), "Portion doesn't belong to the line!!!" );
4661 sal_Int32* pDXArray = const_cast< sal_Int32* >( pLine->GetCharPosArray().GetData()+( nTxtPortionStart-pLine->GetStart() ) );
4662 if ( pTP->GetExtraInfos()->pOrgDXArray )
4663 memcpy( pDXArray, pTP->GetExtraInfos()->pOrgDXArray, (pTP->GetLen()-1)*sizeof(sal_Int32) );
4664 ImplCalcAsianCompression( pParaPortion->GetNode(), pTP, nTxtPortionStart, pDXArray, (USHORT)nCompressPercent, TRUE );
4669 aCompressedPortions.Remove( 0, aCompressedPortions.Count() );
4672 // redesigned to work with TextMarkingVector
4673 void ImpEditEngine::ImplFillTextMarkingVector(const lang::Locale& rLocale, EEngineData::TextMarkingVector& rTextMarkingVector, const String& rTxt, const USHORT nIdx, const USHORT nLen) const
4675 // determine relevant logical text elements for the just-rendered
4676 // string of characters.
4677 Reference< i18n::XBreakIterator > _xBI(ImplGetBreakIterator());
4679 if(_xBI.is())
4681 sal_Int32 nDone;
4682 sal_Int32 nNextCellBreak(_xBI->nextCharacters(rTxt, nIdx, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone));
4683 i18n::Boundary nNextWordBoundary(_xBI->getWordBoundary(rTxt, nIdx, rLocale, i18n::WordType::ANY_WORD, sal_True));
4684 sal_Int32 nNextSentenceBreak(_xBI->endOfSentence(rTxt, nIdx, rLocale));
4686 const sal_Int32 nEndPos(nIdx + nLen);
4687 sal_Int32 i;
4689 for(i = nIdx; i < nEndPos; i++)
4691 // create the entries for the respective break positions
4692 if(i == nNextCellBreak)
4694 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfCaracter, i - nIdx));
4695 nNextCellBreak = _xBI->nextCharacters(rTxt, i, rLocale, i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
4697 if(i == nNextWordBoundary.endPos)
4699 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfWord, i - nIdx));
4700 nNextWordBoundary = _xBI->getWordBoundary(rTxt, i + 1, rLocale, i18n::WordType::ANY_WORD, sal_True);
4702 if(i == nNextSentenceBreak)
4704 rTextMarkingVector.push_back(EEngineData::TextMarkingClass(EEngineData::EndOfSentence, i - nIdx));
4705 nNextSentenceBreak = _xBI->endOfSentence(rTxt, i + 1, rLocale);
4711 // eof