update dev300-m58
[ooovba.git] / sw / source / core / text / itrcrsr.cxx
blob7fbb6bae554bb97e365514e3b8c5d2e517c2840f
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: itrcrsr.cxx,v $
10 * $Revision: 1.81.40.1 $
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_sw.hxx"
34 #include "hintids.hxx"
35 #include "errhdl.hxx"
36 #include "ndtxt.hxx"
37 #include "frmfmt.hxx"
38 #include "paratr.hxx"
39 #include "flyfrm.hxx"
40 #include "pam.hxx"
41 #include "swselectionlist.hxx"
42 #include <sortedobjs.hxx>
43 #include <svx/protitem.hxx>
44 #include <svx/adjitem.hxx>
45 #include <svx/lspcitem.hxx>
46 #include <svx/lrspitem.hxx>
47 #include <frmatr.hxx>
48 #include <pagedesc.hxx> // SwPageDesc
49 #include <tgrditem.hxx>
50 #include <IDocumentSettingAccess.hxx>
51 #include <pagefrm.hxx>
53 #include "txtcfg.hxx"
54 #include "itrtxt.hxx"
55 #include "txtfrm.hxx"
56 #include "flyfrms.hxx"
57 #include "porglue.hxx" // SwFlyCnt
58 #include "porfld.hxx" // SwFldPortion::IsFollow()
59 #include "porfly.hxx" // GetFlyCrsrOfst()
60 #include "pordrop.hxx"
61 #include "crstate.hxx" // SwCrsrMoveState
62 #include <pormulti.hxx> // SwMultiPortion
64 // Nicht reentrant !!!
65 // wird in GetCharRect gesetzt und im UnitUp/Down ausgewertet.
66 sal_Bool SwTxtCursor::bRightMargin = sal_False;
69 /*************************************************************************
70 * lcl_GetCharRectInsideField
72 * After calculating the position of a character during GetCharRect
73 * this function allows to find the coordinates of a position (defined
74 * in pCMS->pSpecialPos) inside a special portion (e.g., a field)
75 *************************************************************************/
76 void lcl_GetCharRectInsideField( SwTxtSizeInfo& rInf, SwRect& rOrig,
77 const SwCrsrMoveState& rCMS,
78 const SwLinePortion& rPor )
80 ASSERT( rCMS.pSpecialPos, "Information about special pos missing" )
82 if ( rPor.InFldGrp() && ((SwFldPortion&)rPor).GetExp().Len() )
84 const USHORT nCharOfst = rCMS.pSpecialPos->nCharOfst;
85 USHORT nFldIdx = 0;
86 USHORT nFldLen = 0;
88 const XubString* pString = 0;
89 const SwLinePortion* pPor = &rPor;
92 if ( pPor->InFldGrp() )
94 pString = &((SwFldPortion*)pPor)->GetExp();
95 nFldLen = pString->Len();
97 else
99 pString = 0;
100 nFldLen = 0;
103 if ( ! pPor->GetPortion() || nFldIdx + nFldLen > nCharOfst )
104 break;
106 nFldIdx = nFldIdx + nFldLen;
107 rOrig.Pos().X() += pPor->Width();
108 pPor = pPor->GetPortion();
110 } while ( TRUE );
112 ASSERT( nCharOfst >= nFldIdx, "Request of position inside field failed" )
113 USHORT nLen = nCharOfst - nFldIdx + 1;
115 if ( pString )
117 // get script for field portion
118 rInf.GetFont()->SetActual( SwScriptInfo::WhichFont( 0, pString, 0 ) );
120 xub_StrLen nOldLen = pPor->GetLen();
121 ((SwLinePortion*)pPor)->SetLen( nLen - 1 );
122 const SwTwips nX1 = pPor->GetLen() ?
123 pPor->GetTxtSize( rInf ).Width() :
126 SwTwips nX2 = 0;
127 if ( rCMS.bRealWidth )
129 ((SwLinePortion*)pPor)->SetLen( nLen );
130 nX2 = pPor->GetTxtSize( rInf ).Width();
133 ((SwLinePortion*)pPor)->SetLen( nOldLen );
135 rOrig.Pos().X() += nX1;
136 rOrig.Width( ( nX2 > nX1 ) ?
137 ( nX2 - nX1 ) :
138 1 );
141 else
143 // special cases: no common fields, e.g., graphic number portion,
144 // FlyInCntPortions, Notes
145 rOrig.Width( rCMS.bRealWidth && rPor.Width() ? rPor.Width() : 1 );
149 /*************************************************************************
150 * SwTxtMargin::CtorInitTxtMargin()
151 *************************************************************************/
152 void SwTxtMargin::CtorInitTxtMargin( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf )
154 CtorInitTxtIter( pNewFrm, pNewInf );
156 pInf = pNewInf;
157 GetInfo().SetFont( GetFnt() );
158 const SwTxtNode *pNode = pFrm->GetTxtNode();
160 const SvxLRSpaceItem &rSpace = pFrm->GetTxtNode()->GetSwAttrSet().GetLRSpace();
161 // --> OD 2009-09-02 #i95907#
162 const bool bListLevelIndentsApplicable = pFrm->GetTxtNode()->AreListLevelIndentsApplicable();
163 // <--
166 // Carefully adjust the text formatting ranges.
168 // This whole area desperately needs some rework. There are
169 // quite a couple of values that need to be considered:
170 // 1. paragraph indent
171 // 2. paragraph first line indent
172 // 3. numbering indent
173 // 4. numbering spacing to text
174 // 5. paragraph border
175 // Note: These values have already been used during calculation
176 // of the printing area of the paragraph.
177 const int nLMWithNum = pNode->GetLeftMarginWithNum( sal_True );
178 if ( pFrm->IsRightToLeft() )
180 // --> OD 2008-01-23 #newlistlevelattrs#
181 // this calculation is identical this the calculation for L2R layout - see below
182 nLeft = pFrm->Frm().Left() +
183 pFrm->Prt().Left() +
184 nLMWithNum -
185 pNode->GetLeftMarginWithNum( sal_False ) -
186 // --> OD 2009-09-02 #i95907#
187 // rSpace.GetLeft() +
188 // rSpace.GetTxtLeft();
189 ( bListLevelIndentsApplicable
191 : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) );
192 // <--
194 else
196 if ( !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
198 // this calculation is identical this the calculation for R2L layout - see above
199 nLeft = pFrm->Frm().Left() +
200 pFrm->Prt().Left() +
201 nLMWithNum -
202 pNode->GetLeftMarginWithNum( sal_False ) -
203 // --> OD 2009-09-02 #i95907#
204 // rSpace.GetLeft() +
205 // rSpace.GetTxtLeft();
206 ( bListLevelIndentsApplicable
208 : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) );
209 // <--
211 else
213 nLeft = pFrm->Frm().Left() +
214 Max( long( rSpace.GetTxtLeft() + nLMWithNum ),
215 pFrm->Prt().Left() );
219 nRight = pFrm->Frm().Left() + pFrm->Prt().Left() + pFrm->Prt().Width();
221 if( nLeft >= nRight &&
222 // --> FME 2005-08-10 #i53066# Omit adjustment of nLeft for numbered
223 // paras inside cells inside new documents:
224 ( pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
225 !pFrm->IsInTab() ||
226 !nLMWithNum) )
227 // <--
229 nLeft = pFrm->Prt().Left() + pFrm->Frm().Left();
230 if( nLeft >= nRight ) // z.B. bei grossen Absatzeinzuegen in schmalen Tabellenspalten
231 nRight = nLeft + 1; // einen goennen wir uns immer
234 if( pFrm->IsFollow() && pFrm->GetOfst() )
235 nFirst = nLeft;
236 else
238 short nFLOfst;
239 long nFirstLineOfs;
240 if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
241 rSpace.IsAutoFirst() )
243 nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
244 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
245 if( pSpace )
247 switch( pSpace->GetLineSpaceRule() )
249 case SVX_LINE_SPACE_AUTO:
250 break;
251 case SVX_LINE_SPACE_MIN:
253 if( nFirstLineOfs < KSHORT( pSpace->GetLineHeight() ) )
254 nFirstLineOfs = pSpace->GetLineHeight();
255 break;
257 case SVX_LINE_SPACE_FIX:
258 nFirstLineOfs = pSpace->GetLineHeight();
259 break;
260 default: ASSERT( sal_False, ": unknown LineSpaceRule" );
262 switch( pSpace->GetInterLineSpaceRule() )
264 case SVX_INTER_LINE_SPACE_OFF:
265 break;
266 case SVX_INTER_LINE_SPACE_PROP:
268 long nTmp = pSpace->GetPropLineSpace();
269 // 50% ist das Minimum, bei 0% schalten wir auf
270 // den Defaultwert 100% um ...
271 if( nTmp < 50 )
272 nTmp = nTmp ? 50 : 100;
274 nTmp *= nFirstLineOfs;
275 nTmp /= 100;
276 if( !nTmp )
277 ++nTmp;
278 nFirstLineOfs = (KSHORT)nTmp;
279 break;
281 case SVX_INTER_LINE_SPACE_FIX:
283 nFirstLineOfs += pSpace->GetInterLineSpace();
284 break;
286 default: ASSERT( sal_False, ": unknown InterLineSpaceRule" );
290 else
291 nFirstLineOfs = nFLOfst;
293 if ( pFrm->IsRightToLeft() ||
294 !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
296 nFirst = nLeft + nFirstLineOfs;
298 else
300 nFirst = pFrm->Frm().Left() +
301 Max( rSpace.GetTxtLeft() + nLMWithNum+ nFirstLineOfs,
302 pFrm->Prt().Left() );
305 // --> OD 2008-01-31 #newlistlevelattrs#
306 // Note: <SwTxtFrm::GetAdditionalFirstLineOffset()> returns a negative
307 // value for the new list label postion and space mode LABEL_ALIGNMENT
308 // and label alignment CENTER and RIGHT in L2R layout respectively
309 // label alignment LEFT and CENTER in R2L layout
310 nFirst += pFrm->GetAdditionalFirstLineOffset();
311 // <--
313 if( nFirst >= nRight )
314 nFirst = nRight - 1;
316 const SvxAdjustItem& rAdjust = pFrm->GetTxtNode()->GetSwAttrSet().GetAdjust();
317 nAdjust = static_cast<USHORT>(rAdjust.GetAdjust());
319 // left is left and right is right
320 if ( pFrm->IsRightToLeft() )
322 if ( SVX_ADJUST_LEFT == nAdjust )
323 nAdjust = SVX_ADJUST_RIGHT;
324 else if ( SVX_ADJUST_RIGHT == nAdjust )
325 nAdjust = SVX_ADJUST_LEFT;
328 bOneBlock = rAdjust.GetOneWord() == SVX_ADJUST_BLOCK;
329 bLastBlock = rAdjust.GetLastBlock() == SVX_ADJUST_BLOCK;
330 bLastCenter = rAdjust.GetLastBlock() == SVX_ADJUST_CENTER;
332 // --> OD 2008-07-01 #i91133#
333 mnTabLeft = pNode->GetLeftMarginForTabCalculation();
334 // <--
336 #if OSL_DEBUG_LEVEL > 1
337 static sal_Bool bOne = sal_False;
338 static sal_Bool bLast = sal_False;
339 static sal_Bool bCenter = sal_False;
340 bOneBlock |= bOne;
341 bLastBlock |= bLast;
342 bLastCenter |= bCenter;
343 #endif
344 DropInit();
347 /*************************************************************************
348 * SwTxtMargin::DropInit()
349 *************************************************************************/
350 void SwTxtMargin::DropInit()
352 nDropLeft = nDropLines = nDropHeight = nDropDescent = 0;
353 const SwParaPortion *pPara = GetInfo().GetParaPortion();
354 if( pPara )
356 const SwDropPortion *pPorDrop = pPara->FindDropPortion();
357 if ( pPorDrop )
359 nDropLeft = pPorDrop->GetDropLeft();
360 nDropLines = pPorDrop->GetLines();
361 nDropHeight = pPorDrop->GetDropHeight();
362 nDropDescent = pPorDrop->GetDropDescent();
367 /*************************************************************************
368 * SwTxtMargin::GetLineStart()
369 *************************************************************************/
371 // Unter Beruecksichtigung des Erstzeileneinzuges und der angebenen Breite.
372 SwTwips SwTxtMargin::GetLineStart() const
374 SwTwips nRet = GetLeftMargin();
375 if( GetAdjust() != SVX_ADJUST_LEFT &&
376 !pCurr->GetFirstPortion()->IsMarginPortion() )
378 // Wenn die erste Portion ein Margin ist, dann wird das
379 // Adjustment durch die Portions ausgedrueckt.
380 if( GetAdjust() == SVX_ADJUST_RIGHT )
381 nRet = Right() - CurrWidth();
382 else if( GetAdjust() == SVX_ADJUST_CENTER )
383 nRet += (GetLineWidth() - CurrWidth()) / 2;
385 return nRet;
388 /*************************************************************************
389 * SwTxtCursor::CtorInitTxtCursor()
390 *************************************************************************/
391 void SwTxtCursor::CtorInitTxtCursor( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf )
393 CtorInitTxtMargin( pNewFrm, pNewInf );
394 // 6096: Vorsicht, die Iteratoren sind abgeleitet!
395 // GetInfo().SetOut( GetInfo().GetWin() );
398 /*************************************************************************
399 * SwTxtCursor::GetEndCharRect()
400 *************************************************************************/
402 // 1170: Antikbug: Shift-Ende vergisst das letzte Zeichen ...
404 sal_Bool SwTxtCursor::GetEndCharRect( SwRect* pOrig, const xub_StrLen nOfst,
405 SwCrsrMoveState* pCMS, const long nMax )
407 // 1170: Mehrdeutigkeit von Dokumentpositionen
408 bRightMargin = sal_True;
409 CharCrsrToLine(nOfst);
411 // Etwas verdreht: nOfst bezeichnet die Position hinter dem letzten
412 // Zeichen der letzten Zeile == Position vor dem ersten Zeichen der
413 // Zeile in der wir gerade stehen:
414 if( nOfst != GetStart() || !pCurr->GetLen() )
416 // 8810: Masterzeile RightMargin, danach LeftMargin
417 const sal_Bool bRet = GetCharRect( pOrig, nOfst, pCMS, nMax );
418 bRightMargin = nOfst >= GetEnd() && nOfst < GetInfo().GetTxt().Len();
419 return bRet;
422 if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
423 return GetCharRect( pOrig, nOfst, pCMS, nMax );
425 // Adjustierung ggf. nachholen
426 GetAdjusted();
428 KSHORT nX = 0;
429 KSHORT nLast = 0;
430 SwLinePortion *pPor = pCurr->GetFirstPortion();
432 KSHORT nTmpHeight, nTmpAscent;
433 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
434 KSHORT nPorHeight = nTmpHeight;
435 KSHORT nPorAscent = nTmpAscent;
437 // Die letzte Text/EndPortion der Zeile suchen
438 while( pPor )
440 nX = nX + pPor->Width();
441 if( pPor->InTxtGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
442 && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
444 nLast = nX;
445 nPorHeight = pPor->Height();
446 nPorAscent = pPor->GetAscent();
448 pPor = pPor->GetPortion();
451 const Size aCharSize( 1, nTmpHeight );
452 pOrig->Pos( GetTopLeft() );
453 pOrig->SSize( aCharSize );
454 pOrig->Pos().X() += nLast;
455 const SwTwips nTmpRight = Right() - 1;
456 if( pOrig->Left() > nTmpRight )
457 pOrig->Pos().X() = nTmpRight;
459 if ( pCMS && pCMS->bRealHeight )
461 if ( nTmpAscent > nPorAscent )
462 pCMS->aRealHeight.X() = nTmpAscent - nPorAscent;
463 else
464 pCMS->aRealHeight.X() = 0;
465 ASSERT( nPorHeight, "GetCharRect: Missing Portion-Height" );
466 pCMS->aRealHeight.Y() = nPorHeight;
469 return sal_True;
472 /*************************************************************************
473 * void SwTxtCursor::_GetCharRect(..)
474 * internal function, called by SwTxtCursor::GetCharRect() to calculate
475 * the relative character position in the current line.
476 * pOrig referes to x and y coordinates, width and height of the cursor
477 * pCMS is used for restricting the cursor, if there are different font
478 * heights in one line ( first value = offset to y of pOrig, second
479 * value = real height of (shortened) cursor
480 *************************************************************************/
482 void SwTxtCursor::_GetCharRect( SwRect* pOrig, const xub_StrLen nOfst,
483 SwCrsrMoveState* pCMS )
485 const XubString &rText = GetInfo().GetTxt();
486 SwTxtSizeInfo aInf( GetInfo(), rText, nStart );
487 if( GetPropFont() )
488 aInf.GetFont()->SetProportion( GetPropFont() );
489 KSHORT nTmpAscent, nTmpHeight; // Zeilenhoehe
490 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
491 const Size aCharSize( 1, nTmpHeight );
492 const Point aCharPos;
493 pOrig->Pos( aCharPos );
494 pOrig->SSize( aCharSize );
496 // If we are looking for a position inside a field which covers
497 // more than one line we may not skip any "empty portions" at the
498 // beginning of a line
499 const sal_Bool bInsideFirstField = pCMS && pCMS->pSpecialPos &&
500 ( pCMS->pSpecialPos->nLineOfst ||
501 SP_EXTEND_RANGE_BEFORE ==
502 pCMS->pSpecialPos->nExtendRange );
504 sal_Bool bWidth = pCMS && pCMS->bRealWidth;
505 if( !pCurr->GetLen() && !pCurr->Width() )
507 if ( pCMS && pCMS->bRealHeight )
509 pCMS->aRealHeight.X() = 0;
510 pCMS->aRealHeight.Y() = nTmpHeight;
513 else
515 KSHORT nPorHeight = nTmpHeight;
516 KSHORT nPorAscent = nTmpAscent;
517 SwTwips nX = 0;
518 SwTwips nTmpFirst = 0;
519 SwLinePortion *pPor = pCurr->GetFirstPortion();
520 SwBidiPortion* pLastBidiPor = 0;
521 SwTwips nLastBidiPorWidth = 0;
522 SvUShorts* pKanaComp = pCurr->GetpKanaComp();
523 MSHORT nSpaceIdx = 0;
524 MSHORT nKanaIdx = 0;
525 long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;
527 sal_Bool bNoTxt = sal_True;
529 // Zuerst werden alle Portions ohne Len am Zeilenanfang uebersprungen.
530 // Ausnahme bilden die fiesen Spezialportions aus WhichFirstPortion:
531 // Num, ErgoSum, FtnNum, FeldReste
532 // 8477: aber auch die einzige Textportion einer leeren Zeile mit
533 // Right/Center-Adjustment! Also nicht nur pPor->GetExpandPortion() ...
534 while( pPor && !pPor->GetLen() && ! bInsideFirstField )
536 nX += pPor->Width();
537 if ( pPor->InSpaceGrp() && nSpaceAdd )
538 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
539 if( bNoTxt )
540 nTmpFirst = nX;
541 // 8670: EndPortions zaehlen hier einmal als TxtPortions.
542 // --> OD 2008-01-28 #newlistlevelattrs#
543 // if( pPor->InTxtGrp() || pPor->IsBreakPortion() )
544 if( pPor->InTxtGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
545 // <--
547 bNoTxt = sal_False;
548 nTmpFirst = nX;
550 if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
552 if ( pCurr->IsSpaceAdd() )
554 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
555 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
556 else
557 nSpaceAdd = 0;
560 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
561 ++nKanaIdx;
563 if( pPor->InFixMargGrp() )
565 if( pPor->IsMarginPortion() )
566 bNoTxt = sal_False;
567 else
569 // fix margin portion => next SpaceAdd, KanaComp value
570 if ( pCurr->IsSpaceAdd() )
572 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
573 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
574 else
575 nSpaceAdd = 0;
578 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
579 ++nKanaIdx;
582 pPor = pPor->GetPortion();
585 if( !pPor )
587 // Es sind nur Spezialportions unterwegs.
588 nX = nTmpFirst;
590 else
592 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
593 (!pPor->InFldGrp() || pPor->GetAscent() ) )
595 nPorHeight = pPor->Height();
596 nPorAscent = pPor->GetAscent();
598 while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
599 ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
601 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
602 (!pPor->InFldGrp() || pPor->GetAscent() ) )
604 nPorHeight = pPor->Height();
605 nPorAscent = pPor->GetAscent();
608 // If we are behind the portion, we add the portion width to
609 // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
610 // For common portions (including BidiPortions) we want to add
611 // the portion width to nX. For MultiPortions, nExtra = 0,
612 // therefore we go to the 'else' branch and start a recursion.
613 const BYTE nExtra = pPor->IsMultiPortion() &&
614 ! ((SwMultiPortion*)pPor)->IsBidi() &&
615 ! bWidth ? 0 : 1;
616 if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
618 if ( pPor->InSpaceGrp() && nSpaceAdd )
619 nX += pPor->PrtWidth() +
620 pPor->CalcSpacing( nSpaceAdd, aInf );
621 else
623 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
625 // update to current SpaceAdd, KanaComp values
626 if ( pCurr->IsSpaceAdd() )
628 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
629 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
630 else
631 nSpaceAdd = 0;
634 if ( pKanaComp &&
635 ( nKanaIdx + 1 ) < pKanaComp->Count()
637 ++nKanaIdx;
639 if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
640 !pPor->GetPortion()->IsMarginPortion() ) )
641 nX += pPor->PrtWidth();
643 if( pPor->IsMultiPortion() )
645 if ( ((SwMultiPortion*)pPor)->HasTabulator() )
647 if ( pCurr->IsSpaceAdd() )
649 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
650 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
651 else
652 nSpaceAdd = 0;
655 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
656 ++nKanaIdx;
659 // if we are right behind a BidiPortion, we have to
660 // hold a pointer to the BidiPortion in order to
661 // find the correct cursor position, depending on the
662 // cursor level
663 if ( ((SwMultiPortion*)pPor)->IsBidi() &&
664 aInf.GetIdx() + pPor->GetLen() == nOfst )
666 pLastBidiPor = (SwBidiPortion*)pPor;
667 nLastBidiPorWidth = pLastBidiPor->Width() +
668 pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );;
672 aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
673 pPor = pPor->GetPortion();
675 else
677 if( pPor->IsMultiPortion() )
679 nTmpAscent = AdjustBaseLine( *pCurr, pPor );
680 GetInfo().SetMulti( sal_True );
681 pOrig->Pos().Y() += nTmpAscent - nPorAscent;
683 if( pCMS && pCMS->b2Lines )
685 sal_Bool bRecursion = sal_True;
686 if ( ! pCMS->p2Lines )
688 pCMS->p2Lines = new Sw2LinesPos;
689 pCMS->p2Lines->aLine = SwRect(aCharPos, aCharSize);
690 bRecursion = sal_False;
693 if( ((SwMultiPortion*)pPor)->HasRotation() )
695 if( ((SwMultiPortion*)pPor)->IsRevers() )
696 pCMS->p2Lines->nMultiType = MT_ROT_270;
697 else
698 pCMS->p2Lines->nMultiType = MT_ROT_90;
700 else if( ((SwMultiPortion*)pPor)->IsDouble() )
701 pCMS->p2Lines->nMultiType = MT_TWOLINE;
702 else if( ((SwMultiPortion*)pPor)->IsBidi() )
703 pCMS->p2Lines->nMultiType = MT_BIDI;
704 else
705 pCMS->p2Lines->nMultiType = MT_RUBY;
707 SwTwips nTmpWidth = pPor->Width();
708 if( nSpaceAdd )
709 nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
711 SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
712 Size( nTmpWidth, pPor->Height() ) );
714 if ( ! bRecursion )
715 pCMS->p2Lines->aPortion = aRect;
716 else
717 pCMS->p2Lines->aPortion2 = aRect;
720 // In a multi-portion we use GetCharRect()-function
721 // recursively and must add the x-position
722 // of the multi-portion.
723 xub_StrLen nOldStart = nStart;
724 SwTwips nOldY = nY;
725 BYTE nOldProp = GetPropFont();
726 nStart = aInf.GetIdx();
727 SwLineLayout* pOldCurr = pCurr;
728 pCurr = &((SwMultiPortion*)pPor)->GetRoot();
729 if( ((SwMultiPortion*)pPor)->IsDouble() )
730 SetPropFont( 50 );
732 GETGRID( GetTxtFrm()->FindPageFrm() )
733 const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid();
734 const USHORT nRubyHeight = bHasGrid ?
735 pGrid->GetRubyHeight() : 0;
737 if( nStart + pCurr->GetLen() <= nOfst && GetNext() &&
738 ( ! ((SwMultiPortion*)pPor)->IsRuby() ||
739 ((SwMultiPortion*)pPor)->OnTop() ) )
741 USHORT nOffset;
742 // in grid mode we may only add the height of the
743 // ruby line if ruby line is on top
744 if ( bHasGrid &&
745 ((SwMultiPortion*)pPor)->IsRuby() &&
746 ((SwMultiPortion*)pPor)->OnTop() )
747 nOffset = nRubyHeight;
748 else
749 nOffset = GetLineHeight();
751 pOrig->Pos().Y() += nOffset;
752 Next();
755 sal_Bool bSpaceChg = ((SwMultiPortion*)pPor)->
756 ChgSpaceAdd( pCurr, nSpaceAdd );
757 Point aOldPos = pOrig->Pos();
759 // Ok, for ruby portions in grid mode we have to
760 // temporarily set the inner line height to the
761 // outer line height because that value is needed
762 // for the adjustment inside the recursion
763 const USHORT nOldRubyHeight = pCurr->Height();
764 const USHORT nOldRubyRealHeight = pCurr->GetRealHeight();
765 const sal_Bool bChgHeight =
766 ((SwMultiPortion*)pPor)->IsRuby() && bHasGrid;
768 if ( bChgHeight )
770 pCurr->Height( pOldCurr->Height() - nRubyHeight );
771 pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
772 nRubyHeight );
775 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
776 if ( ((SwMultiPortion*)pPor)->IsBidi() )
778 aLayoutModeModifier.Modify(
779 ((SwBidiPortion*)pPor)->GetLevel() % 2 );
782 _GetCharRect( pOrig, nOfst, pCMS );
784 if ( bChgHeight )
786 pCurr->Height( nOldRubyHeight );
787 pCurr->SetRealHeight( nOldRubyRealHeight );
790 // if we are still in the first row of
791 // our 2 line multiportion, we use the FirstMulti flag
792 // to indicate this
793 if ( ((SwMultiPortion*)pPor)->IsDouble() )
795 // the recursion may have damaged our font size
796 SetPropFont( nOldProp );
797 if ( !nOldProp )
798 nOldProp = 100;
799 GetInfo().GetFont()->SetProportion( 100 );
801 if ( pCurr == &((SwMultiPortion*)pPor)->GetRoot() )
803 GetInfo().SetFirstMulti( sal_True );
805 // we want to treat a double line portion like a
806 // single line portion, if there is no text in
807 // the second line
808 if ( !pCurr->GetNext() ||
809 !pCurr->GetNext()->GetLen() )
810 GetInfo().SetMulti( sal_False );
813 // ruby portions are treated like single line portions
814 else if( ((SwMultiPortion*)pPor)->IsRuby() ||
815 ((SwMultiPortion*)pPor)->IsBidi() )
816 GetInfo().SetMulti( sal_False );
818 // calculate cursor values
819 if( ((SwMultiPortion*)pPor)->HasRotation() )
821 GetInfo().SetMulti( sal_False );
822 long nTmp = pOrig->Width();
823 pOrig->Width( pOrig->Height() );
824 pOrig->Height( nTmp );
825 nTmp = pOrig->Left() - aOldPos.X();
827 // if we travel into our rotated portion from
828 // a line below, we have to take care, that the
829 // y coord in pOrig is less than line height:
830 if ( nTmp )
831 nTmp--;
833 pOrig->Pos().X() = nX + aOldPos.X();
834 if( ((SwMultiPortion*)pPor)->IsRevers() )
835 pOrig->Pos().Y() = aOldPos.Y() + nTmp;
836 else
837 pOrig->Pos().Y() = aOldPos.Y()
838 + pPor->Height() - nTmp - pOrig->Height();
839 if ( pCMS && pCMS->bRealHeight )
841 pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y();
842 // result for rotated multi portion is not
843 // correct for reverse (270 degree) portions
844 if( ((SwMultiPortion*)pPor)->IsRevers() )
846 if ( SvxParaVertAlignItem::AUTOMATIC ==
847 GetLineInfo().GetVertAlign() )
848 // if vertical alignment is set to auto,
849 // we switch from base line alignment
850 // to centered alignment
851 pCMS->aRealHeight.X() =
852 ( pOrig->Width() +
853 pCMS->aRealHeight.Y() ) / 2;
854 else
855 pCMS->aRealHeight.X() =
856 ( pOrig->Width() -
857 pCMS->aRealHeight.X() +
858 pCMS->aRealHeight.Y() );
862 else
864 pOrig->Pos().Y() += aOldPos.Y();
865 if ( ((SwMultiPortion*)pPor)->IsBidi() )
867 const SwTwips nPorWidth = pPor->Width() +
868 pPor->CalcSpacing( nSpaceAdd, aInf );
869 const SwTwips nInsideOfst = pOrig->Pos().X();
870 pOrig->Pos().X() = nX + nPorWidth -
871 nInsideOfst - pOrig->Width();
873 else
874 pOrig->Pos().X() += nX;
876 if( ((SwMultiPortion*)pPor)->HasBrackets() )
877 pOrig->Pos().X() +=
878 ((SwDoubleLinePortion*)pPor)->PreWidth();
881 if( bSpaceChg )
882 SwDoubleLinePortion::ResetSpaceAdd( pCurr );
884 pCurr = pOldCurr;
885 nStart = nOldStart;
886 nY = nOldY;
887 bPrev = sal_False;
889 return;
891 if ( pPor->PrtWidth() )
893 xub_StrLen nOldLen = pPor->GetLen();
894 pPor->SetLen( nOfst - aInf.GetIdx() );
895 aInf.SetLen( pPor->GetLen() );
896 if( nX || !pPor->InNumberGrp() )
898 SeekAndChg( aInf );
899 const sal_Bool bOldOnWin = aInf.OnWin();
900 aInf.SetOnWin( sal_False ); // keine BULLETs!
901 SwTwips nTmp = nX;
902 aInf.SetKanaComp( pKanaComp );
903 aInf.SetKanaIdx( nKanaIdx );
904 nX += pPor->GetTxtSize( aInf ).Width();
905 aInf.SetOnWin( bOldOnWin );
906 if ( pPor->InSpaceGrp() && nSpaceAdd )
907 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
908 if( bWidth )
910 pPor->SetLen( pPor->GetLen() + 1 );
911 aInf.SetLen( pPor->GetLen() );
912 aInf.SetOnWin( sal_False ); // keine BULLETs!
913 nTmp += pPor->GetTxtSize( aInf ).Width();
914 aInf.SetOnWin( bOldOnWin );
915 if ( pPor->InSpaceGrp() && nSpaceAdd )
916 nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
917 pOrig->Width( nTmp - nX );
920 pPor->SetLen( nOldLen );
922 bWidth = sal_False;
923 break;
928 if( pPor )
930 ASSERT( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
931 sal_Bool bEmptyFld = sal_False;
932 if( pPor->InFldGrp() && pPor->GetLen() )
934 SwFldPortion *pTmp = (SwFldPortion*)pPor;
935 while( pTmp->HasFollow() && !pTmp->GetExp().Len() )
937 KSHORT nAddX = pTmp->Width();
938 SwLinePortion *pNext = pTmp->GetPortion();
939 while( pNext && !pNext->InFldGrp() )
941 ASSERT( !pNext->GetLen(), "Where's my field follow?" );
942 nAddX = nAddX + pNext->Width();
943 pNext = pNext->GetPortion();
945 if( !pNext )
946 break;
947 pTmp = (SwFldPortion*)pNext;
948 nPorHeight = pTmp->Height();
949 nPorAscent = pTmp->GetAscent();
950 nX += nAddX;
951 bEmptyFld = sal_True;
954 // 8513: Felder im Blocksatz, ueberspringen
955 while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
956 ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
957 pPor->IsBlankPortion() || pPor->InTabGrp() ||
958 ( !bEmptyFld && pPor->InFldGrp() ) ) )
960 if ( pPor->InSpaceGrp() && nSpaceAdd )
961 nX += pPor->PrtWidth() +
962 pPor->CalcSpacing( nSpaceAdd, aInf );
963 else
965 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
967 if ( pCurr->IsSpaceAdd() )
969 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
970 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
971 else
972 nSpaceAdd = 0;
975 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
976 ++nKanaIdx;
978 if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
979 !pPor->GetPortion()->IsMarginPortion() ) )
980 nX += pPor->PrtWidth();
982 if( pPor->IsMultiPortion() &&
983 ((SwMultiPortion*)pPor)->HasTabulator() )
985 if ( pCurr->IsSpaceAdd() )
987 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
988 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
989 else
990 nSpaceAdd = 0;
993 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->Count() )
994 ++nKanaIdx;
996 if( !pPor->IsFlyPortion() )
998 nPorHeight = pPor->Height();
999 nPorAscent = pPor->GetAscent();
1001 pPor = pPor->GetPortion();
1004 if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
1005 pPor->GetPortion() && pPor->GetPortion()->InFixGrp() )
1007 // Alle Sonderportions muessen uebersprungen werden
1008 // Beispiel: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
1009 // Ohne den Ausgleich landen wir vor '-' mit dem
1010 // Ausgleich vor 's'.
1011 while( pPor && !pPor->GetLen() )
1013 DBG_LOOP;
1014 nX += pPor->Width();
1015 if( !pPor->IsMarginPortion() )
1017 nPorHeight = pPor->Height();
1018 nPorAscent = pPor->GetAscent();
1020 pPor = pPor->GetPortion();
1023 if( pPor && pCMS )
1025 if( pCMS->bFieldInfo && pPor->InFldGrp() && pPor->Width() )
1026 pOrig->Width( pPor->Width() );
1027 if( pPor->IsDropPortion() )
1029 nPorAscent = ((SwDropPortion*)pPor)->GetDropHeight();
1030 // The drop height is only calculated, if we have more than
1031 // one line. Otherwise it is 0.
1032 if ( ! nPorAscent)
1033 nPorAscent = pPor->Height();
1034 nPorHeight = nPorAscent;
1035 pOrig->Height( nPorHeight +
1036 ((SwDropPortion*)pPor)->GetDropDescent() );
1037 if( nTmpHeight < pOrig->Height() )
1039 nTmpAscent = nPorAscent;
1040 nTmpHeight = USHORT( pOrig->Height() );
1043 if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
1044 aInf.GetIdx() == nOfst )
1046 if( !pPor->IsFlyPortion() && pPor->Height() &&
1047 pPor->GetAscent() )
1049 nPorHeight = pPor->Height();
1050 nPorAscent = pPor->GetAscent();
1052 SwTwips nTmp;
1053 if( 2 > pPor->GetLen() )
1055 nTmp = pPor->Width();
1056 if ( pPor->InSpaceGrp() && nSpaceAdd )
1057 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1059 else
1061 const sal_Bool bOldOnWin = aInf.OnWin();
1062 xub_StrLen nOldLen = pPor->GetLen();
1063 pPor->SetLen( 1 );
1064 aInf.SetLen( pPor->GetLen() );
1065 SeekAndChg( aInf );
1066 aInf.SetOnWin( sal_False ); // keine BULLETs!
1067 aInf.SetKanaComp( pKanaComp );
1068 aInf.SetKanaIdx( nKanaIdx );
1069 nTmp = pPor->GetTxtSize( aInf ).Width();
1070 aInf.SetOnWin( bOldOnWin );
1071 if ( pPor->InSpaceGrp() && nSpaceAdd )
1072 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1073 pPor->SetLen( nOldLen );
1075 pOrig->Width( nTmp );
1078 // travel inside field portion?
1079 if ( pCMS->pSpecialPos )
1081 // apply attributes to font
1082 Seek( nOfst );
1083 lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
1088 // special case: We are at the beginning of a BidiPortion or
1089 // directly behind a BidiPortion
1090 if ( pCMS &&
1091 ( pLastBidiPor ||
1092 ( pPor &&
1093 pPor->IsMultiPortion() &&
1094 ((SwMultiPortion*)pPor)->IsBidi() ) ) )
1096 // we determine if the cursor has to blink before or behind
1097 // the bidi portion
1098 if ( pLastBidiPor )
1100 const BYTE nPortionLevel = pLastBidiPor->GetLevel();
1102 if ( pCMS->nCursorBidiLevel >= nPortionLevel )
1104 // we came from inside the bidi portion, we want to blink
1105 // behind the portion
1106 pOrig->Pos().X() -= nLastBidiPorWidth;
1108 // Again, there is a special case: logically behind
1109 // the portion can actually mean that the cursor is inside
1110 // the portion. This can happen is the last portion
1111 // inside the bidi portion is a nested bidi portion
1112 SwLineLayout& rLineLayout =
1113 ((SwMultiPortion*)pLastBidiPor)->GetRoot();
1115 const SwLinePortion *pLast = rLineLayout.FindLastPortion();
1116 if ( pLast->IsMultiPortion() )
1118 ASSERT( ((SwMultiPortion*)pLast)->IsBidi(),
1119 "Non-BidiPortion inside BidiPortion" )
1120 pOrig->Pos().X() += pLast->Width() +
1121 pLast->CalcSpacing( nSpaceAdd, aInf );
1125 else
1127 const BYTE nPortionLevel = ((SwBidiPortion*)pPor)->GetLevel();
1129 if ( pCMS->nCursorBidiLevel >= nPortionLevel )
1131 // we came from inside the bidi portion, we want to blink
1132 // behind the portion
1133 pOrig->Pos().X() += pPor->Width() +
1134 pPor->CalcSpacing( nSpaceAdd, aInf );
1139 pOrig->Pos().X() += nX;
1141 if ( pCMS && pCMS->bRealHeight )
1143 nTmpAscent = AdjustBaseLine( *pCurr, 0, nPorHeight, nPorAscent );
1144 if ( nTmpAscent > nPorAscent )
1145 pCMS->aRealHeight.X() = nTmpAscent - nPorAscent;
1146 else
1147 pCMS->aRealHeight.X() = 0;
1148 ASSERT( nPorHeight, "GetCharRect: Missing Portion-Height" );
1149 if ( nTmpHeight > nPorHeight )
1150 pCMS->aRealHeight.Y() = nPorHeight;
1151 else
1152 pCMS->aRealHeight.Y() = nTmpHeight;
1157 /*************************************************************************
1158 * SwTxtCursor::GetCharRect()
1159 *************************************************************************/
1161 sal_Bool SwTxtCursor::GetCharRect( SwRect* pOrig, const xub_StrLen nOfst,
1162 SwCrsrMoveState* pCMS, const long nMax )
1164 CharCrsrToLine(nOfst);
1166 // Indicates that a position inside a special portion (field, number portion)
1167 // is requested.
1168 const sal_Bool bSpecialPos = pCMS && pCMS->pSpecialPos;
1169 xub_StrLen nFindOfst = nOfst;
1171 if ( bSpecialPos )
1173 const BYTE nExtendRange = pCMS->pSpecialPos->nExtendRange;
1175 ASSERT( ! pCMS->pSpecialPos->nLineOfst || SP_EXTEND_RANGE_BEFORE != nExtendRange,
1176 "LineOffset AND Number Portion?" )
1178 // portions which are behind the string
1179 if ( SP_EXTEND_RANGE_BEHIND == nExtendRange )
1180 ++nFindOfst;
1182 // skip lines for fields which cover more than one line
1183 for ( USHORT i = 0; i < pCMS->pSpecialPos->nLineOfst; i++ )
1184 Next();
1187 // Adjustierung ggf. nachholen
1188 GetAdjusted();
1190 const Point aCharPos( GetTopLeft() );
1191 sal_Bool bRet = sal_True;
1193 _GetCharRect( pOrig, nFindOfst, pCMS );
1195 const SwTwips nTmpRight = Right() - 12;
1197 pOrig->Pos().X() += aCharPos.X();
1198 pOrig->Pos().Y() += aCharPos.Y();
1200 if( pCMS && pCMS->b2Lines && pCMS->p2Lines )
1202 pCMS->p2Lines->aLine.Pos().X() += aCharPos.X();
1203 pCMS->p2Lines->aLine.Pos().Y() += aCharPos.Y();
1204 pCMS->p2Lines->aPortion.Pos().X() += aCharPos.X();
1205 pCMS->p2Lines->aPortion.Pos().Y() += aCharPos.Y();
1208 if( pOrig->Left() > nTmpRight )
1209 pOrig->Pos().X() = nTmpRight;
1211 if( nMax )
1213 if( pOrig->Top() + pOrig->Height() > nMax )
1215 if( pOrig->Top() > nMax )
1216 pOrig->Top( nMax );
1217 pOrig->Height( nMax - pOrig->Top() );
1219 if ( pCMS && pCMS->bRealHeight && pCMS->aRealHeight.Y() >= 0 )
1221 long nTmp = pCMS->aRealHeight.X() + pOrig->Top();
1222 if( nTmp >= nMax )
1224 pCMS->aRealHeight.X() = nMax - pOrig->Top();
1225 pCMS->aRealHeight.Y() = 0;
1227 else if( nTmp + pCMS->aRealHeight.Y() > nMax )
1228 pCMS->aRealHeight.Y() = nMax - nTmp;
1231 long nOut = pOrig->Right() - GetTxtFrm()->Frm().Right();
1232 if( nOut > 0 )
1234 if( GetTxtFrm()->Frm().Width() < GetTxtFrm()->Prt().Left()
1235 + GetTxtFrm()->Prt().Width() )
1236 nOut += GetTxtFrm()->Frm().Width() - GetTxtFrm()->Prt().Left()
1237 - GetTxtFrm()->Prt().Width();
1238 if( nOut > 0 )
1239 pOrig->Pos().X() -= nOut + 10;
1241 return bRet;
1244 /*************************************************************************
1245 * SwTxtCursor::GetCrsrOfst()
1247 * Return: Offset im String
1248 *************************************************************************/
1249 xub_StrLen SwTxtCursor::GetCrsrOfst( SwPosition *pPos, const Point &rPoint,
1250 const MSHORT nChgNode, SwCrsrMoveState* pCMS ) const
1252 // Adjustierung ggf. nachholen
1253 GetAdjusted();
1255 const XubString &rText = GetInfo().GetTxt();
1256 xub_StrLen nOffset = 0;
1258 // x ist der horizontale Offset innerhalb der Zeile.
1259 SwTwips x = rPoint.X();
1260 CONST SwTwips nLeftMargin = GetLineStart();
1261 SwTwips nRightMargin = GetLineEnd();
1262 if( nRightMargin == nLeftMargin )
1263 nRightMargin += 30;
1265 const sal_Bool bLeftOver = x < nLeftMargin;
1266 if( bLeftOver )
1267 x = nLeftMargin;
1268 const sal_Bool bRightOver = x > nRightMargin;
1269 if( bRightOver )
1270 x = nRightMargin;
1272 sal_Bool bRightAllowed = pCMS && ( pCMS->eState == MV_NONE );
1274 // Bis hierher in Dokumentkoordinaten.
1275 x -= nLeftMargin;
1277 KSHORT nX = KSHORT( x );
1279 // Wenn es in der Zeile Attributwechsel gibt, den Abschnitt
1280 // suchen, in dem nX liegt.
1281 SwLinePortion *pPor = pCurr->GetFirstPortion();
1282 xub_StrLen nCurrStart = nStart;
1283 sal_Bool bHolePortion = sal_False;
1284 sal_Bool bLastHyph = sal_False;
1286 SvUShorts *pKanaComp = pCurr->GetpKanaComp();
1287 xub_StrLen nOldIdx = GetInfo().GetIdx();
1288 MSHORT nSpaceIdx = 0;
1289 MSHORT nKanaIdx = 0;
1290 long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;
1291 short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
1293 // nWidth ist die Breite der Zeile, oder die Breite des
1294 // Abschnitts mit dem Fontwechsel, in dem nX liegt.
1296 KSHORT nWidth = pPor->Width();
1297 if ( pCurr->IsSpaceAdd() || pKanaComp )
1299 if ( pPor->InSpaceGrp() && nSpaceAdd )
1301 ((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart );
1302 nWidth = nWidth + USHORT( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1304 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1305 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
1308 if ( pCurr->IsSpaceAdd() )
1310 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
1311 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
1312 else
1313 nSpaceAdd = 0;
1316 if( pKanaComp )
1318 if ( nKanaIdx + 1 < pKanaComp->Count() )
1319 nKanaComp = (*pKanaComp)[++nKanaIdx];
1320 else
1321 nKanaComp = 0;
1326 KSHORT nWidth30;
1327 if ( pPor->IsPostItsPortion() )
1328 nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
1329 else
1330 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ?
1331 30 :
1332 nWidth;
1334 while( pPor->GetPortion() && nWidth30 < nX && !pPor->IsBreakPortion() )
1336 nX = nX - nWidth;
1337 nCurrStart = nCurrStart + pPor->GetLen();
1338 bHolePortion = pPor->IsHolePortion();
1339 pPor = pPor->GetPortion();
1340 nWidth = pPor->Width();
1341 if ( pCurr->IsSpaceAdd() || pKanaComp )
1343 if ( pPor->InSpaceGrp() && nSpaceAdd )
1345 ((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart );
1346 nWidth = nWidth + USHORT( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1349 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1350 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
1353 if ( pCurr->IsSpaceAdd() )
1355 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
1356 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
1357 else
1358 nSpaceAdd = 0;
1361 if ( pKanaComp )
1363 if( nKanaIdx + 1 < pKanaComp->Count() )
1364 nKanaComp = (*pKanaComp)[++nKanaIdx];
1365 else
1366 nKanaComp = 0;
1371 if ( pPor->IsPostItsPortion() )
1372 nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
1373 else
1374 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ?
1375 30 :
1376 nWidth;
1377 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1378 bLastHyph = pPor->InHyphGrp();
1381 const sal_Bool bLastPortion = (0 == pPor->GetPortion());
1383 if( nX==nWidth )
1385 SwLinePortion *pNextPor = pPor->GetPortion();
1386 while( pNextPor && pNextPor->InFldGrp() && !pNextPor->Width() )
1388 nCurrStart = nCurrStart + pPor->GetLen();
1389 pPor = pNextPor;
1390 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1391 bLastHyph = pPor->InHyphGrp();
1392 pNextPor = pPor->GetPortion();
1396 ((SwTxtSizeInfo&)GetInfo()).SetIdx( nOldIdx );
1398 xub_StrLen nLength = pPor->GetLen();
1400 sal_Bool bFieldInfo = pCMS && pCMS->bFieldInfo;
1402 if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
1403 ( pPor->InNumberGrp() && !pPor->IsFtnNumPortion() ) ||
1404 ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
1405 ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
1408 // #i27615#
1409 if (pCMS)
1411 if( pCMS->bInFrontOfLabel)
1413 if (! (2 * nX < nWidth && pPor->InNumberGrp() &&
1414 !pPor->IsFtnNumPortion()))
1415 pCMS->bInFrontOfLabel = sal_False;
1419 // 7684: Wir sind genau auf der HyphPortion angelangt und muessen dafuer
1420 // sorgen, dass wir in dem String landen.
1421 // 7993: Wenn die Laenge 0 ist muessen wir raus...
1422 if( !nLength )
1424 if( pCMS )
1426 if( pPor->IsFlyPortion() && bFieldInfo )
1427 ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
1429 if (!bRightOver && nX)
1431 if( pPor->IsFtnNumPortion())
1432 ((SwCrsrMoveState*)pCMS)->bFtnNoInfo = sal_True;
1433 else if (pPor->InNumberGrp() ) // #i23726#
1435 ((SwCrsrMoveState*)pCMS)->nInNumPostionOffset = nX;
1436 ((SwCrsrMoveState*)pCMS)->bInNumPortion = sal_True;
1440 if( !nCurrStart )
1441 return 0;
1443 // 7849, 7816: auf pPor->GetHyphPortion kann nicht verzichtet werden!
1444 if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
1445 ( pPor->IsMarginPortion() && !pPor->GetPortion() &&
1446 // 46598: In der letzten Zeile eines zentrierten Absatzes wollen
1447 // wir auch mal hinter dem letzten Zeichen landen.
1448 nCurrStart < rText.Len() ) )
1449 --nCurrStart;
1450 else if( pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()
1451 && nWidth > nX )
1453 if( bFieldInfo )
1454 --nCurrStart;
1455 else
1457 KSHORT nHeight = pPor->Height();
1458 if ( !nHeight || nHeight > nWidth )
1459 nHeight = nWidth;
1460 if( nChgNode && nWidth - nHeight/2 > nX )
1461 --nCurrStart;
1464 return nCurrStart;
1466 if ( 1 == nLength )
1468 if ( nWidth )
1470 // Sonst kommen wir nicht mehr in zeichengeb. Rahmen hinein...
1471 if( !( nChgNode && pPos && pPor->IsFlyCntPortion() ) )
1473 if ( pPor->InFldGrp() ||
1474 ( pPor->IsMultiPortion() &&
1475 ((SwMultiPortion*)pPor)->IsBidi() ) )
1477 KSHORT nHeight = 0;
1478 if( !bFieldInfo )
1480 nHeight = pPor->Height();
1481 if ( !nHeight || nHeight > nWidth )
1482 nHeight = nWidth;
1485 if( nWidth - nHeight/2 <= nX &&
1486 ( ! pPor->InFldGrp() ||
1487 !((SwFldPortion*)pPor)->HasFollow() ) )
1488 ++nCurrStart;
1490 else if ( ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
1491 !pPor->GetPortion()->IsMarginPortion() &&
1492 !pPor->GetPortion()->IsHolePortion() ) )
1493 && ( nWidth/2 < nX ) &&
1494 ( !bFieldInfo ||
1495 ( pPor->GetPortion() &&
1496 pPor->GetPortion()->IsPostItsPortion() ) )
1497 && ( bRightAllowed || !bLastHyph ))
1498 ++nCurrStart;
1500 // if we want to get the position inside the field, we should not return
1501 if ( !pCMS || !pCMS->pSpecialPos )
1502 return nCurrStart;
1505 else
1507 if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
1508 pPor->InToxRefGrp() )
1509 return nCurrStart;
1510 if ( pPor->InFldGrp() )
1512 if( bRightOver && !((SwFldPortion*)pPor)->HasFollow() )
1513 ++nCurrStart;
1514 return nCurrStart;
1519 if( bLastPortion && (pCurr->GetNext() || pFrm->GetFollow() ) )
1520 --nLength;
1522 if( nWidth > nX ||
1523 ( nWidth == nX && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsDouble() ) )
1525 if( pPor->IsMultiPortion() )
1527 // In a multi-portion we use GetCrsrOfst()-function recursively
1528 SwTwips nTmpY = rPoint.Y() - pCurr->GetAscent() + pPor->GetAscent();
1529 // if we are in the first line of a double line portion, we have
1530 // to add a value to nTmpY for not staying in this line
1531 // we also want to skip the first line, if we are inside ruby
1532 if ( ( ((SwTxtSizeInfo*)pInf)->IsMulti() &&
1533 ((SwTxtSizeInfo*)pInf)->IsFirstMulti() ) ||
1534 ( ((SwMultiPortion*)pPor)->IsRuby() &&
1535 ((SwMultiPortion*)pPor)->OnTop() ) )
1536 nTmpY += ((SwMultiPortion*)pPor)->Height();
1538 // Important for cursor traveling in ruby portions:
1539 // We have to set nTmpY to 0 in order to stay in the first row
1540 // if the phonetic line is the second row
1541 if ( ((SwMultiPortion*)pPor)->IsRuby() &&
1542 ! ((SwMultiPortion*)pPor)->OnTop() )
1543 nTmpY = 0;
1545 SwTxtCursorSave aSave( (SwTxtCursor*)this, (SwMultiPortion*)pPor,
1546 nTmpY, nX, nCurrStart, nSpaceAdd );
1548 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1549 if ( ((SwMultiPortion*)pPor)->IsBidi() )
1551 const BYTE nBidiLevel = ((SwBidiPortion*)pPor)->GetLevel();
1552 aLayoutModeModifier.Modify( nBidiLevel % 2 );
1555 if( ((SwMultiPortion*)pPor)->HasRotation() )
1557 nTmpY -= nY;
1558 if( !((SwMultiPortion*)pPor)->IsRevers() )
1559 nTmpY = pPor->Height() - nTmpY;
1560 if( nTmpY < 0 )
1561 nTmpY = 0;
1562 nX = (KSHORT)nTmpY;
1565 if( ((SwMultiPortion*)pPor)->HasBrackets() )
1567 USHORT nPreWidth = ((SwDoubleLinePortion*)pPor)->PreWidth();
1568 if ( nX > nPreWidth )
1569 nX = nX - nPreWidth;
1570 else
1571 nX = 0;
1574 return GetCrsrOfst( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
1575 nChgNode, pCMS );
1577 if( pPor->InTxtGrp() )
1579 BYTE nOldProp;
1580 if( GetPropFont() )
1582 ((SwFont*)GetFnt())->SetProportion( GetPropFont() );
1583 nOldProp = GetFnt()->GetPropr();
1585 else
1586 nOldProp = 0;
1588 SwTxtSizeInfo aSizeInf( GetInfo(), rText, nCurrStart );
1589 ((SwTxtCursor*)this)->SeekAndChg( aSizeInf );
1590 SwTxtSlot aDiffTxt( &aSizeInf, ((SwTxtPortion*)pPor), false, false );
1591 SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
1592 ((SwDropPortion*)pPor)->GetFnt() : NULL );
1594 SwParaPortion* pPara = (SwParaPortion*)GetInfo().GetParaPortion();
1595 ASSERT( pPara, "No paragraph!" );
1597 SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
1598 *aSizeInf.GetOut(),
1599 &pPara->GetScriptInfo(),
1600 aSizeInf.GetTxt(),
1601 aSizeInf.GetIdx(),
1602 pPor->GetLen() );
1603 aDrawInf.SetOfst( nX );
1605 if ( nSpaceAdd )
1607 xub_StrLen nCharCnt;
1608 // --> FME 2005-04-04 #i41860# Thai justified alignemt needs some
1609 // additional information:
1610 aDrawInf.SetNumberOfBlanks( pPor->InTxtGrp() ?
1611 static_cast<const SwTxtPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
1612 0 );
1613 // <--
1616 if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
1617 aDrawInf.SetLen( STRING_LEN ); // SMARTTAGS
1619 aDrawInf.SetSpace( nSpaceAdd );
1620 aDrawInf.SetFont( aSizeInf.GetFont() );
1621 aDrawInf.SetFrm( pFrm );
1622 aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
1623 aDrawInf.SetPosMatchesBounds( pCMS && pCMS->bPosMatchesBounds );
1625 if ( SW_CJK == aSizeInf.GetFont()->GetActual() &&
1626 pPara->GetScriptInfo().CountCompChg() &&
1627 ! pPor->InFldGrp() )
1628 aDrawInf.SetKanaComp( nKanaComp );
1630 nLength = aSizeInf.GetFont()->_GetCrsrOfst( aDrawInf );
1632 // get position inside field portion?
1633 if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
1635 pCMS->pSpecialPos->nCharOfst = nLength;
1636 nLength = 0; // SMARTTAGS
1639 // set cursor bidi level
1640 if ( pCMS )
1641 ((SwCrsrMoveState*)pCMS)->nCursorBidiLevel =
1642 aDrawInf.GetCursorBidiLevel();
1644 if( bFieldInfo && nLength == pPor->GetLen() &&
1645 ( ! pPor->GetPortion() ||
1646 ! pPor->GetPortion()->IsPostItsPortion() ) )
1647 --nLength;
1649 if( nOldProp )
1650 ((SwFont*)GetFnt())->SetProportion( nOldProp );
1652 else
1654 if( nChgNode && pPos && pPor->IsFlyCntPortion()
1655 && !( (SwFlyCntPortion*)pPor )->IsDraw() )
1657 // JP 24.11.94: liegt die Pos nicht im Fly, dann
1658 // darf nicht mit STRING_LEN returnt werden!
1659 // (BugId: 9692 + Aenderung in feshview)
1660 SwFlyInCntFrm *pTmp = ( (SwFlyCntPortion*)pPor )->GetFlyFrm();
1661 sal_Bool bChgNode = 1 < nChgNode;
1662 if( !bChgNode )
1664 SwFrm* pLower = pTmp->GetLower();
1665 if( pLower && (pLower->IsTxtFrm() || pLower->IsLayoutFrm()) )
1666 bChgNode = sal_True;
1668 Point aTmpPoint( rPoint );
1670 if ( pFrm->IsRightToLeft() )
1671 pFrm->SwitchLTRtoRTL( aTmpPoint );
1673 if ( pFrm->IsVertical() )
1674 pFrm->SwitchHorizontalToVertical( aTmpPoint );
1676 if( bChgNode && pTmp->Frm().IsInside( aTmpPoint ) &&
1677 !( pTmp->IsProtected() ) )
1679 nLength = ((SwFlyCntPortion*)pPor)->
1680 GetFlyCrsrOfst( nX, aTmpPoint, pPos, pCMS );
1681 // Sobald der Frame gewechselt wird, muessen wir aufpassen, dass
1682 // unser Font wieder im OutputDevice steht.
1683 // vgl. Paint und new SwFlyCntPortion !
1684 ((SwTxtSizeInfo*)pInf)->SelectFont();
1686 // 6776: Das pIter->GetCrsrOfst returnt
1687 // aus einer Verschachtelung mit STRING_LEN.
1688 return STRING_LEN;
1691 else
1692 nLength = pPor->GetCrsrOfst( nX );
1695 nOffset = nCurrStart + nLength;
1697 // 7684: Wir sind vor der HyphPortion angelangt und muessen dafuer
1698 // sorgen, dass wir in dem String landen.
1699 // Bei Zeilenenden vor FlyFrms muessen ebenso behandelt werden.
1701 if( nOffset && pPor->GetLen() == nLength && pPor->GetPortion() &&
1702 !pPor->GetPortion()->GetLen() && pPor->GetPortion()->InHyphGrp() )
1703 --nOffset;
1705 return nOffset;
1708 /** Looks for text portions which are inside the given rectangle
1710 For a rectangular text selection every text portions which is inside the given
1711 rectangle has to be put into the SwSelectionList as SwPaM
1712 From these SwPaM the SwCursors will be created.
1714 @param rSelList
1715 The container for the overlapped text portions
1717 @param rRect
1718 A rectangle in document coordinates, text inside this rectangle has to be
1719 selected.
1721 @return [ true, false ]
1722 true if any overlapping text portion has been found and put into list
1723 false if no portion overlaps, the list has been unchanged
1725 bool SwTxtFrm::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
1727 bool bRet = false;
1728 // PaintArea() instead Frm() for negative indents
1729 SwRect aTmpFrm( PaintArea() );
1730 if( !rRect.IsOver( aTmpFrm ) )
1731 return false;
1732 if( rSelList.checkContext( this ) )
1734 SwRect aRect( aTmpFrm );
1735 aRect.Intersection( rRect );
1736 // rNode without const to create SwPaMs
1737 SwCntntNode &rNode = const_cast<SwCntntNode&>( *GetNode() );
1738 SwNodeIndex aIdx( rNode );
1739 SwPosition aPosL( aIdx, SwIndex( &rNode, 0 ) );
1740 if( IsEmpty() )
1742 SwPaM *pPam = new SwPaM( aPosL, aPosL );
1743 rSelList.insertPaM( pPam );
1745 else if( aRect.HasArea() )
1747 xub_StrLen nOld = STRING_LEN;
1748 SwPosition aPosR( aPosL );
1749 Point aPoint;
1750 SwTxtInfo aInf( const_cast<SwTxtFrm*>(this) );
1751 SwTxtIter aLine( const_cast<SwTxtFrm*>(this), &aInf );
1752 // We have to care for top-to-bottom layout, where right becomes top etc.
1753 SWRECTFN( this )
1754 SwTwips nTop = (aRect.*fnRect->fnGetTop)();
1755 SwTwips nBottom = (aRect.*fnRect->fnGetBottom)();
1756 SwTwips nLeft = (aRect.*fnRect->fnGetLeft)();
1757 SwTwips nRight = (aRect.*fnRect->fnGetRight)();
1758 SwTwips nY = aLine.Y(); // Top position of the first line
1759 SwTwips nLastY = nY;
1760 while( nY < nTop && aLine.Next() ) // line above rectangle
1762 nLastY = nY;
1763 nY = aLine.Y();
1765 bool bLastLine = false;
1766 if( nY < nTop && !aLine.GetNext() )
1768 bLastLine = true;
1769 nY += aLine.GetLineHeight();
1771 do // check the lines for overlapping
1773 if( nLastY < nTop ) // if the last line was above rectangle
1774 nLastY = nTop;
1775 if( nY > nBottom ) // if the current line leaves the rectangle
1776 nY = nBottom;
1777 if( nY >= nLastY ) // gotcha: overlapping
1779 nLastY += nY;
1780 nLastY /= 2;
1781 if( bVert )
1783 aPoint.X() = nLastY;
1784 aPoint.Y() = nLeft;
1786 else
1788 aPoint.X() = nLeft;
1789 aPoint.Y() = nLastY;
1791 // Looking for the position of the left border of the rectangle
1792 // in this text line
1793 SwCrsrMoveState aState( MV_UPDOWN );
1794 if( GetCrsrOfst( &aPosL, aPoint, &aState ) )
1796 if( bVert )
1798 aPoint.X() = nLastY;
1799 aPoint.Y() = nRight;
1801 else
1803 aPoint.X() = nRight;
1804 aPoint.Y() = nLastY;
1806 // If we get a right position and if the left position
1807 // is not the same like the left position of the line before
1808 // which cound happen e.g. for field portions or fly frames
1809 // a SwPaM will be inserted with these positions
1810 if( GetCrsrOfst( &aPosR, aPoint, &aState ) &&
1811 nOld != aPosL.nContent.GetIndex() )
1813 SwPaM *pPam = new SwPaM( aPosL, aPosR );
1814 rSelList.insertPaM( pPam );
1815 nOld = aPosL.nContent.GetIndex();
1819 if( aLine.Next() )
1821 nLastY = nY;
1822 nY = aLine.Y();
1824 else if( !bLastLine )
1826 bLastLine = true;
1827 nLastY = nY;
1828 nY += aLine.GetLineHeight();
1830 else
1831 break;
1832 }while( nLastY < nBottom );
1835 if( GetDrawObjs() )
1837 const SwSortedObjs &rObjs = *GetDrawObjs();
1838 for ( USHORT i = 0; i < rObjs.Count(); ++i )
1840 const SwAnchoredObject* pAnchoredObj = rObjs[i];
1841 if( !pAnchoredObj->ISA(SwFlyFrm) )
1842 continue;
1843 const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pAnchoredObj);
1844 if( pFly->IsFlyInCntFrm() && pFly->FillSelection( rSelList, rRect ) )
1845 bRet = true;
1848 return bRet;