Impress Remote 1.0.5, tag sdremote-1.0.5
[LibreOffice.git] / sw / source / core / text / itrcrsr.cxx
blob3127ae863634a6b0de39c3e93d848c77011d063b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "hintids.hxx"
22 #include "ndtxt.hxx"
23 #include "frmfmt.hxx"
24 #include "paratr.hxx"
25 #include "flyfrm.hxx"
26 #include "pam.hxx"
27 #include "swselectionlist.hxx"
28 #include <sortedobjs.hxx>
29 #include <editeng/protitem.hxx>
30 #include <editeng/adjitem.hxx>
31 #include <editeng/lspcitem.hxx>
32 #include <editeng/lrspitem.hxx>
33 #include <frmatr.hxx>
34 #include <pagedesc.hxx> // SwPageDesc
35 #include <tgrditem.hxx>
36 #include <IDocumentSettingAccess.hxx>
37 #include <pagefrm.hxx>
39 #include "itrtxt.hxx"
40 #include "txtfrm.hxx"
41 #include "flyfrms.hxx"
42 #include "porglue.hxx" // SwFlyCnt
43 #include "porfld.hxx" // SwFldPortion::IsFollow()
44 #include "porfly.hxx" // GetFlyCrsrOfst()
45 #include "pordrop.hxx"
46 #include "crstate.hxx" // SwCrsrMoveState
47 #include <pormulti.hxx> // SwMultiPortion
48 // #i111284#
49 #include <numrule.hxx>
51 // Nicht reentrant !!!
52 // wird in GetCharRect gesetzt und im UnitUp/Down ausgewertet.
53 sal_Bool SwTxtCursor::bRightMargin = sal_False;
56 /*************************************************************************
57 * lcl_GetCharRectInsideField
59 * After calculating the position of a character during GetCharRect
60 * this function allows to find the coordinates of a position (defined
61 * in pCMS->pSpecialPos) inside a special portion (e.g., a field)
62 *************************************************************************/
63 static void lcl_GetCharRectInsideField( SwTxtSizeInfo& rInf, SwRect& rOrig,
64 const SwCrsrMoveState& rCMS,
65 const SwLinePortion& rPor )
67 OSL_ENSURE( rCMS.pSpecialPos, "Information about special pos missing" );
69 if ( rPor.InFldGrp() && ((SwFldPortion&)rPor).GetExp().Len() )
71 const sal_uInt16 nCharOfst = rCMS.pSpecialPos->nCharOfst;
72 sal_uInt16 nFldIdx = 0;
73 sal_uInt16 nFldLen = 0;
75 const XubString* pString = 0;
76 const SwLinePortion* pPor = &rPor;
79 if ( pPor->InFldGrp() )
81 pString = &((SwFldPortion*)pPor)->GetExp();
82 nFldLen = pString->Len();
84 else
86 pString = 0;
87 nFldLen = 0;
90 if ( ! pPor->GetPortion() || nFldIdx + nFldLen > nCharOfst )
91 break;
93 nFldIdx = nFldIdx + nFldLen;
94 rOrig.Pos().X() += pPor->Width();
95 pPor = pPor->GetPortion();
97 } while ( sal_True );
99 OSL_ENSURE( nCharOfst >= nFldIdx, "Request of position inside field failed" );
100 sal_uInt16 nLen = nCharOfst - nFldIdx + 1;
102 if ( pString )
104 // get script for field portion
105 rInf.GetFont()->SetActual( SwScriptInfo::WhichFont( 0, pString, 0 ) );
107 xub_StrLen nOldLen = pPor->GetLen();
108 ((SwLinePortion*)pPor)->SetLen( nLen - 1 );
109 const SwTwips nX1 = pPor->GetLen() ?
110 pPor->GetTxtSize( rInf ).Width() :
113 SwTwips nX2 = 0;
114 if ( rCMS.bRealWidth )
116 ((SwLinePortion*)pPor)->SetLen( nLen );
117 nX2 = pPor->GetTxtSize( rInf ).Width();
120 ((SwLinePortion*)pPor)->SetLen( nOldLen );
122 rOrig.Pos().X() += nX1;
123 rOrig.Width( ( nX2 > nX1 ) ?
124 ( nX2 - nX1 ) :
125 1 );
128 else
130 // special cases: no common fields, e.g., graphic number portion,
131 // FlyInCntPortions, Notes
132 rOrig.Width( rCMS.bRealWidth && rPor.Width() ? rPor.Width() : 1 );
136 // #i111284#
137 namespace {
138 bool AreListLevelIndentsApplicableAndLabelAlignmentActive( const SwTxtNode& rTxtNode )
140 bool bRet( false );
142 if ( rTxtNode.AreListLevelIndentsApplicable() )
144 const SwNumFmt& rNumFmt =
145 rTxtNode.GetNumRule()->Get( static_cast<sal_uInt16>(rTxtNode.GetActualListLevel()) );
146 if ( rNumFmt.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
148 bRet = true;
152 return bRet;
154 } // end of anonymous namespace
156 /*************************************************************************
157 * SwTxtMargin::CtorInitTxtMargin()
158 *************************************************************************/
159 void SwTxtMargin::CtorInitTxtMargin( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf )
161 CtorInitTxtIter( pNewFrm, pNewInf );
163 pInf = pNewInf;
164 GetInfo().SetFont( GetFnt() );
165 const SwTxtNode *pNode = pFrm->GetTxtNode();
167 const SvxLRSpaceItem &rSpace = pFrm->GetTxtNode()->GetSwAttrSet().GetLRSpace();
168 // #i95907#
169 // #i111284#
170 const bool bListLevelIndentsApplicableAndLabelAlignmentActive(
171 AreListLevelIndentsApplicableAndLabelAlignmentActive( *(pFrm->GetTxtNode()) ) );
174 // Carefully adjust the text formatting ranges.
176 // This whole area desperately needs some rework. There are
177 // quite a couple of values that need to be considered:
178 // 1. paragraph indent
179 // 2. paragraph first line indent
180 // 3. numbering indent
181 // 4. numbering spacing to text
182 // 5. paragraph border
183 // Note: These values have already been used during calculation
184 // of the printing area of the paragraph.
185 const int nLMWithNum = pNode->GetLeftMarginWithNum( sal_True );
186 if ( pFrm->IsRightToLeft() )
188 // this calculation is identical this the calculation for L2R layout - see below
189 nLeft = pFrm->Frm().Left() +
190 pFrm->Prt().Left() +
191 nLMWithNum -
192 pNode->GetLeftMarginWithNum( sal_False ) -
193 // #i95907#
194 // #i111284#
195 // rSpace.GetLeft() +
196 // rSpace.GetTxtLeft();
197 ( bListLevelIndentsApplicableAndLabelAlignmentActive
199 : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) );
201 else
203 // #i95907#
204 // #i111284#
205 if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
206 !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
208 // this calculation is identical this the calculation for R2L layout - see above
209 nLeft = pFrm->Frm().Left() +
210 pFrm->Prt().Left() +
211 nLMWithNum -
212 pNode->GetLeftMarginWithNum( sal_False ) -
213 // #i95907#
214 // #i111284#
215 ( bListLevelIndentsApplicableAndLabelAlignmentActive
217 : ( rSpace.GetLeft() - rSpace.GetTxtLeft() ) );
219 else
221 nLeft = pFrm->Frm().Left() +
222 Max( long( rSpace.GetTxtLeft() + nLMWithNum ),
223 pFrm->Prt().Left() );
227 nRight = pFrm->Frm().Left() + pFrm->Prt().Left() + pFrm->Prt().Width();
229 if( nLeft >= nRight &&
230 // #i53066# Omit adjustment of nLeft for numbered
231 // paras inside cells inside new documents:
232 ( pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
233 !pFrm->IsInTab() ||
234 !nLMWithNum ) )
236 nLeft = pFrm->Prt().Left() + pFrm->Frm().Left();
237 if( nLeft >= nRight ) // z.B. bei grossen Absatzeinzuegen in schmalen Tabellenspalten
238 nRight = nLeft + 1; // einen goennen wir uns immer
241 if( pFrm->IsFollow() && pFrm->GetOfst() )
242 nFirst = nLeft;
243 else
245 short nFLOfst = 0;
246 long nFirstLineOfs = 0;
247 if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
248 rSpace.IsAutoFirst() )
250 nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
251 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing();
252 if( pSpace )
254 switch( pSpace->GetLineSpaceRule() )
256 case SVX_LINE_SPACE_AUTO:
257 break;
258 case SVX_LINE_SPACE_MIN:
260 if( nFirstLineOfs < KSHORT( pSpace->GetLineHeight() ) )
261 nFirstLineOfs = pSpace->GetLineHeight();
262 break;
264 case SVX_LINE_SPACE_FIX:
265 nFirstLineOfs = pSpace->GetLineHeight();
266 break;
267 default: OSL_FAIL( ": unknown LineSpaceRule" );
269 switch( pSpace->GetInterLineSpaceRule() )
271 case SVX_INTER_LINE_SPACE_OFF:
272 break;
273 case SVX_INTER_LINE_SPACE_PROP:
275 long nTmp = pSpace->GetPropLineSpace();
276 // 50% ist das Minimum, bei 0% schalten wir auf
277 // den Defaultwert 100% um ...
278 if( nTmp < 50 )
279 nTmp = nTmp ? 50 : 100;
281 nTmp *= nFirstLineOfs;
282 nTmp /= 100;
283 if( !nTmp )
284 ++nTmp;
285 nFirstLineOfs = (KSHORT)nTmp;
286 break;
288 case SVX_INTER_LINE_SPACE_FIX:
290 nFirstLineOfs += pSpace->GetInterLineSpace();
291 break;
293 default: OSL_FAIL( ": unknown InterLineSpaceRule" );
297 else
298 nFirstLineOfs = nFLOfst;
300 // #i95907#
301 // #i111284#
302 if ( pFrm->IsRightToLeft() ||
303 bListLevelIndentsApplicableAndLabelAlignmentActive ||
304 !pNode->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
306 nFirst = nLeft + nFirstLineOfs;
308 else
310 nFirst = pFrm->Frm().Left() +
311 Max( rSpace.GetTxtLeft() + nLMWithNum+ nFirstLineOfs,
312 pFrm->Prt().Left() );
315 // Note: <SwTxtFrm::GetAdditionalFirstLineOffset()> returns a negative
316 // value for the new list label postion and space mode LABEL_ALIGNMENT
317 // and label alignment CENTER and RIGHT in L2R layout respectively
318 // label alignment LEFT and CENTER in R2L layout
319 nFirst += pFrm->GetAdditionalFirstLineOffset();
321 if( nFirst >= nRight )
322 nFirst = nRight - 1;
324 const SvxAdjustItem& rAdjust = pFrm->GetTxtNode()->GetSwAttrSet().GetAdjust();
325 nAdjust = static_cast<sal_uInt16>(rAdjust.GetAdjust());
327 // left is left and right is right
328 if ( pFrm->IsRightToLeft() )
330 if ( SVX_ADJUST_LEFT == nAdjust )
331 nAdjust = SVX_ADJUST_RIGHT;
332 else if ( SVX_ADJUST_RIGHT == nAdjust )
333 nAdjust = SVX_ADJUST_LEFT;
336 bOneBlock = rAdjust.GetOneWord() == SVX_ADJUST_BLOCK;
337 bLastBlock = rAdjust.GetLastBlock() == SVX_ADJUST_BLOCK;
338 bLastCenter = rAdjust.GetLastBlock() == SVX_ADJUST_CENTER;
340 // #i91133#
341 mnTabLeft = pNode->GetLeftMarginForTabCalculation();
343 #if OSL_DEBUG_LEVEL > 1
344 static sal_Bool bOne = sal_False;
345 static sal_Bool bLast = sal_False;
346 static sal_Bool bCenter = sal_False;
347 bOneBlock |= bOne;
348 bLastBlock |= bLast;
349 bLastCenter |= bCenter;
350 #endif
351 DropInit();
354 /*************************************************************************
355 * SwTxtMargin::DropInit()
356 *************************************************************************/
357 void SwTxtMargin::DropInit()
359 nDropLeft = nDropLines = nDropHeight = nDropDescent = 0;
360 const SwParaPortion *pPara = GetInfo().GetParaPortion();
361 if( pPara )
363 const SwDropPortion *pPorDrop = pPara->FindDropPortion();
364 if ( pPorDrop )
366 nDropLeft = pPorDrop->GetDropLeft();
367 nDropLines = pPorDrop->GetLines();
368 nDropHeight = pPorDrop->GetDropHeight();
369 nDropDescent = pPorDrop->GetDropDescent();
374 /*************************************************************************
375 * SwTxtMargin::GetLineStart()
376 *************************************************************************/
378 // Unter Beruecksichtigung des Erstzeileneinzuges und der angebenen Breite.
379 SwTwips SwTxtMargin::GetLineStart() const
381 SwTwips nRet = GetLeftMargin();
382 if( GetAdjust() != SVX_ADJUST_LEFT &&
383 !pCurr->GetFirstPortion()->IsMarginPortion() )
385 // Wenn die erste Portion ein Margin ist, dann wird das
386 // Adjustment durch die Portions ausgedrueckt.
387 if( GetAdjust() == SVX_ADJUST_RIGHT )
388 nRet = Right() - CurrWidth();
389 else if( GetAdjust() == SVX_ADJUST_CENTER )
390 nRet += (GetLineWidth() - CurrWidth()) / 2;
392 return nRet;
395 /*************************************************************************
396 * SwTxtCursor::CtorInitTxtCursor()
397 *************************************************************************/
398 void SwTxtCursor::CtorInitTxtCursor( SwTxtFrm *pNewFrm, SwTxtSizeInfo *pNewInf )
400 CtorInitTxtMargin( pNewFrm, pNewInf );
401 // 6096: Vorsicht, die Iteratoren sind abgeleitet!
402 // GetInfo().SetOut( GetInfo().GetWin() );
405 /*************************************************************************
406 * SwTxtCursor::GetEndCharRect()
407 *************************************************************************/
409 // 1170: Antikbug: Shift-Ende vergisst das letzte Zeichen ...
411 sal_Bool SwTxtCursor::GetEndCharRect( SwRect* pOrig, const xub_StrLen nOfst,
412 SwCrsrMoveState* pCMS, const long nMax )
414 // 1170: Mehrdeutigkeit von Dokumentpositionen
415 bRightMargin = sal_True;
416 CharCrsrToLine(nOfst);
418 // Etwas verdreht: nOfst bezeichnet die Position hinter dem letzten
419 // Zeichen der letzten Zeile == Position vor dem ersten Zeichen der
420 // Zeile in der wir gerade stehen:
421 if( nOfst != GetStart() || !pCurr->GetLen() )
423 // 8810: Masterzeile RightMargin, danach LeftMargin
424 const sal_Bool bRet = GetCharRect( pOrig, nOfst, pCMS, nMax );
425 bRightMargin = nOfst >= GetEnd() && nOfst < GetInfo().GetTxt().Len();
426 return bRet;
429 if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
430 return GetCharRect( pOrig, nOfst, pCMS, nMax );
432 // Adjustierung ggf. nachholen
433 GetAdjusted();
435 KSHORT nX = 0;
436 KSHORT nLast = 0;
437 SwLinePortion *pPor = pCurr->GetFirstPortion();
439 KSHORT nTmpHeight, nTmpAscent;
440 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
441 KSHORT nPorHeight = nTmpHeight;
442 KSHORT nPorAscent = nTmpAscent;
444 // Die letzte Text/EndPortion der Zeile suchen
445 while( pPor )
447 nX = nX + pPor->Width();
448 if( pPor->InTxtGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
449 && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
451 nLast = nX;
452 nPorHeight = pPor->Height();
453 nPorAscent = pPor->GetAscent();
455 pPor = pPor->GetPortion();
458 const Size aCharSize( 1, nTmpHeight );
459 pOrig->Pos( GetTopLeft() );
460 pOrig->SSize( aCharSize );
461 pOrig->Pos().X() += nLast;
462 const SwTwips nTmpRight = Right() - 1;
463 if( pOrig->Left() > nTmpRight )
464 pOrig->Pos().X() = nTmpRight;
466 if ( pCMS && pCMS->bRealHeight )
468 if ( nTmpAscent > nPorAscent )
469 pCMS->aRealHeight.X() = nTmpAscent - nPorAscent;
470 else
471 pCMS->aRealHeight.X() = 0;
472 OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
473 pCMS->aRealHeight.Y() = nPorHeight;
476 return sal_True;
479 /*************************************************************************
480 * void SwTxtCursor::_GetCharRect(..)
481 * internal function, called by SwTxtCursor::GetCharRect() to calculate
482 * the relative character position in the current line.
483 * pOrig referes to x and y coordinates, width and height of the cursor
484 * pCMS is used for restricting the cursor, if there are different font
485 * heights in one line ( first value = offset to y of pOrig, second
486 * value = real height of (shortened) cursor
487 *************************************************************************/
489 void SwTxtCursor::_GetCharRect( SwRect* pOrig, const xub_StrLen nOfst,
490 SwCrsrMoveState* pCMS )
492 const XubString &rText = GetInfo().GetTxt();
493 SwTxtSizeInfo aInf( GetInfo(), rText, nStart );
494 if( GetPropFont() )
495 aInf.GetFont()->SetProportion( GetPropFont() );
496 KSHORT nTmpAscent, nTmpHeight; // Zeilenhoehe
497 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
498 const Size aCharSize( 1, nTmpHeight );
499 const Point aCharPos;
500 pOrig->Pos( aCharPos );
501 pOrig->SSize( aCharSize );
503 // If we are looking for a position inside a field which covers
504 // more than one line we may not skip any "empty portions" at the
505 // beginning of a line
506 const sal_Bool bInsideFirstField = pCMS && pCMS->pSpecialPos &&
507 ( pCMS->pSpecialPos->nLineOfst ||
508 SP_EXTEND_RANGE_BEFORE ==
509 pCMS->pSpecialPos->nExtendRange );
511 sal_Bool bWidth = pCMS && pCMS->bRealWidth;
512 if( !pCurr->GetLen() && !pCurr->Width() )
514 if ( pCMS && pCMS->bRealHeight )
516 pCMS->aRealHeight.X() = 0;
517 pCMS->aRealHeight.Y() = nTmpHeight;
520 else
522 KSHORT nPorHeight = nTmpHeight;
523 KSHORT nPorAscent = nTmpAscent;
524 SwTwips nX = 0;
525 SwTwips nTmpFirst = 0;
526 SwLinePortion *pPor = pCurr->GetFirstPortion();
527 SwBidiPortion* pLastBidiPor = 0;
528 SwTwips nLastBidiPorWidth = 0;
529 std::deque<sal_uInt16>* pKanaComp = pCurr->GetpKanaComp();
530 MSHORT nSpaceIdx = 0;
531 size_t nKanaIdx = 0;
532 long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;
534 sal_Bool bNoTxt = sal_True;
536 // Zuerst werden alle Portions ohne Len am Zeilenanfang uebersprungen.
537 // Ausnahme bilden die fiesen Spezialportions aus WhichFirstPortion:
538 // Num, ErgoSum, FtnNum, FeldReste
539 // 8477: aber auch die einzige Textportion einer leeren Zeile mit
540 // Right/Center-Adjustment! Also nicht nur pPor->GetExpandPortion() ...
541 while( pPor && !pPor->GetLen() && ! bInsideFirstField )
543 nX += pPor->Width();
544 if ( pPor->InSpaceGrp() && nSpaceAdd )
545 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
546 if( bNoTxt )
547 nTmpFirst = nX;
548 // 8670: EndPortions zaehlen hier einmal als TxtPortions.
549 // if( pPor->InTxtGrp() || pPor->IsBreakPortion() )
550 if( pPor->InTxtGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
552 bNoTxt = sal_False;
553 nTmpFirst = nX;
555 if( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
557 if ( pCurr->IsSpaceAdd() )
559 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
560 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
561 else
562 nSpaceAdd = 0;
565 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
566 ++nKanaIdx;
568 if( pPor->InFixMargGrp() )
570 if( pPor->IsMarginPortion() )
571 bNoTxt = sal_False;
572 else
574 // fix margin portion => next SpaceAdd, KanaComp value
575 if ( pCurr->IsSpaceAdd() )
577 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
578 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
579 else
580 nSpaceAdd = 0;
583 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
584 ++nKanaIdx;
587 pPor = pPor->GetPortion();
590 if( !pPor )
592 // Es sind nur Spezialportions unterwegs.
593 nX = nTmpFirst;
595 else
597 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
598 (!pPor->InFldGrp() || pPor->GetAscent() ) )
600 nPorHeight = pPor->Height();
601 nPorAscent = pPor->GetAscent();
603 while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
604 ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
606 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
607 (!pPor->InFldGrp() || pPor->GetAscent() ) )
609 nPorHeight = pPor->Height();
610 nPorAscent = pPor->GetAscent();
613 // If we are behind the portion, we add the portion width to
614 // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
615 // For common portions (including BidiPortions) we want to add
616 // the portion width to nX. For MultiPortions, nExtra = 0,
617 // therefore we go to the 'else' branch and start a recursion.
618 const sal_uInt8 nExtra = pPor->IsMultiPortion() &&
619 ! ((SwMultiPortion*)pPor)->IsBidi() &&
620 ! bWidth ? 0 : 1;
621 if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
623 if ( pPor->InSpaceGrp() && nSpaceAdd )
624 nX += pPor->PrtWidth() +
625 pPor->CalcSpacing( nSpaceAdd, aInf );
626 else
628 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
630 // update to current SpaceAdd, KanaComp values
631 if ( pCurr->IsSpaceAdd() )
633 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
634 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
635 else
636 nSpaceAdd = 0;
639 if ( pKanaComp &&
640 ( nKanaIdx + 1 ) < pKanaComp->size()
642 ++nKanaIdx;
644 if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
645 !pPor->GetPortion()->IsMarginPortion() ) )
646 nX += pPor->PrtWidth();
648 if( pPor->IsMultiPortion() )
650 if ( ((SwMultiPortion*)pPor)->HasTabulator() )
652 if ( pCurr->IsSpaceAdd() )
654 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
655 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
656 else
657 nSpaceAdd = 0;
660 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
661 ++nKanaIdx;
664 // if we are right behind a BidiPortion, we have to
665 // hold a pointer to the BidiPortion in order to
666 // find the correct cursor position, depending on the
667 // cursor level
668 if ( ((SwMultiPortion*)pPor)->IsBidi() &&
669 aInf.GetIdx() + pPor->GetLen() == nOfst )
671 pLastBidiPor = (SwBidiPortion*)pPor;
672 nLastBidiPorWidth = pLastBidiPor->Width() +
673 pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );;
677 aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
678 pPor = pPor->GetPortion();
680 else
682 if( pPor->IsMultiPortion() )
684 nTmpAscent = AdjustBaseLine( *pCurr, pPor );
685 GetInfo().SetMulti( sal_True );
686 pOrig->Pos().Y() += nTmpAscent - nPorAscent;
688 if( pCMS && pCMS->b2Lines )
690 sal_Bool bRecursion = sal_True;
691 if ( ! pCMS->p2Lines )
693 pCMS->p2Lines = new Sw2LinesPos;
694 pCMS->p2Lines->aLine = SwRect(aCharPos, aCharSize);
695 bRecursion = sal_False;
698 if( ((SwMultiPortion*)pPor)->HasRotation() )
700 if( ((SwMultiPortion*)pPor)->IsRevers() )
701 pCMS->p2Lines->nMultiType = MT_ROT_270;
702 else
703 pCMS->p2Lines->nMultiType = MT_ROT_90;
705 else if( ((SwMultiPortion*)pPor)->IsDouble() )
706 pCMS->p2Lines->nMultiType = MT_TWOLINE;
707 else if( ((SwMultiPortion*)pPor)->IsBidi() )
708 pCMS->p2Lines->nMultiType = MT_BIDI;
709 else
710 pCMS->p2Lines->nMultiType = MT_RUBY;
712 SwTwips nTmpWidth = pPor->Width();
713 if( nSpaceAdd )
714 nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
716 SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
717 Size( nTmpWidth, pPor->Height() ) );
719 if ( ! bRecursion )
720 pCMS->p2Lines->aPortion = aRect;
721 else
722 pCMS->p2Lines->aPortion2 = aRect;
725 // In a multi-portion we use GetCharRect()-function
726 // recursively and must add the x-position
727 // of the multi-portion.
728 xub_StrLen nOldStart = nStart;
729 SwTwips nOldY = nY;
730 sal_uInt8 nOldProp = GetPropFont();
731 nStart = aInf.GetIdx();
732 SwLineLayout* pOldCurr = pCurr;
733 pCurr = &((SwMultiPortion*)pPor)->GetRoot();
734 if( ((SwMultiPortion*)pPor)->IsDouble() )
735 SetPropFont( 50 );
737 GETGRID( GetTxtFrm()->FindPageFrm() )
738 const sal_Bool bHasGrid = pGrid && GetInfo().SnapToGrid();
739 const sal_uInt16 nRubyHeight = bHasGrid ?
740 pGrid->GetRubyHeight() : 0;
742 if( nStart + pCurr->GetLen() <= nOfst && GetNext() &&
743 ( ! ((SwMultiPortion*)pPor)->IsRuby() ||
744 ((SwMultiPortion*)pPor)->OnTop() ) )
746 sal_uInt16 nOffset;
747 // in grid mode we may only add the height of the
748 // ruby line if ruby line is on top
749 if ( bHasGrid &&
750 ((SwMultiPortion*)pPor)->IsRuby() &&
751 ((SwMultiPortion*)pPor)->OnTop() )
752 nOffset = nRubyHeight;
753 else
754 nOffset = GetLineHeight();
756 pOrig->Pos().Y() += nOffset;
757 Next();
760 sal_Bool bSpaceChg = ((SwMultiPortion*)pPor)->
761 ChgSpaceAdd( pCurr, nSpaceAdd );
762 Point aOldPos = pOrig->Pos();
764 // Ok, for ruby portions in grid mode we have to
765 // temporarily set the inner line height to the
766 // outer line height because that value is needed
767 // for the adjustment inside the recursion
768 const sal_uInt16 nOldRubyHeight = pCurr->Height();
769 const sal_uInt16 nOldRubyRealHeight = pCurr->GetRealHeight();
770 const sal_Bool bChgHeight =
771 ((SwMultiPortion*)pPor)->IsRuby() && bHasGrid;
773 if ( bChgHeight )
775 pCurr->Height( pOldCurr->Height() - nRubyHeight );
776 pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
777 nRubyHeight );
780 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
781 if ( ((SwMultiPortion*)pPor)->IsBidi() )
783 aLayoutModeModifier.Modify(
784 ((SwBidiPortion*)pPor)->GetLevel() % 2 );
787 _GetCharRect( pOrig, nOfst, pCMS );
789 if ( bChgHeight )
791 pCurr->Height( nOldRubyHeight );
792 pCurr->SetRealHeight( nOldRubyRealHeight );
795 // if we are still in the first row of
796 // our 2 line multiportion, we use the FirstMulti flag
797 // to indicate this
798 if ( ((SwMultiPortion*)pPor)->IsDouble() )
800 // the recursion may have damaged our font size
801 SetPropFont( nOldProp );
802 if ( !nOldProp )
803 nOldProp = 100;
804 GetInfo().GetFont()->SetProportion( 100 );
806 if ( pCurr == &((SwMultiPortion*)pPor)->GetRoot() )
808 GetInfo().SetFirstMulti( sal_True );
810 // we want to treat a double line portion like a
811 // single line portion, if there is no text in
812 // the second line
813 if ( !pCurr->GetNext() ||
814 !pCurr->GetNext()->GetLen() )
815 GetInfo().SetMulti( sal_False );
818 // ruby portions are treated like single line portions
819 else if( ((SwMultiPortion*)pPor)->IsRuby() ||
820 ((SwMultiPortion*)pPor)->IsBidi() )
821 GetInfo().SetMulti( sal_False );
823 // calculate cursor values
824 if( ((SwMultiPortion*)pPor)->HasRotation() )
826 GetInfo().SetMulti( sal_False );
827 long nTmp = pOrig->Width();
828 pOrig->Width( pOrig->Height() );
829 pOrig->Height( nTmp );
830 nTmp = pOrig->Left() - aOldPos.X();
832 // if we travel into our rotated portion from
833 // a line below, we have to take care, that the
834 // y coord in pOrig is less than line height:
835 if ( nTmp )
836 nTmp--;
838 pOrig->Pos().X() = nX + aOldPos.X();
839 if( ((SwMultiPortion*)pPor)->IsRevers() )
840 pOrig->Pos().Y() = aOldPos.Y() + nTmp;
841 else
842 pOrig->Pos().Y() = aOldPos.Y()
843 + pPor->Height() - nTmp - pOrig->Height();
844 if ( pCMS && pCMS->bRealHeight )
846 pCMS->aRealHeight.Y() = -pCMS->aRealHeight.Y();
847 // result for rotated multi portion is not
848 // correct for reverse (270 degree) portions
849 if( ((SwMultiPortion*)pPor)->IsRevers() )
851 if ( SvxParaVertAlignItem::AUTOMATIC ==
852 GetLineInfo().GetVertAlign() )
853 // if vertical alignment is set to auto,
854 // we switch from base line alignment
855 // to centered alignment
856 pCMS->aRealHeight.X() =
857 ( pOrig->Width() +
858 pCMS->aRealHeight.Y() ) / 2;
859 else
860 pCMS->aRealHeight.X() =
861 ( pOrig->Width() -
862 pCMS->aRealHeight.X() +
863 pCMS->aRealHeight.Y() );
867 else
869 pOrig->Pos().Y() += aOldPos.Y();
870 if ( ((SwMultiPortion*)pPor)->IsBidi() )
872 const SwTwips nPorWidth = pPor->Width() +
873 pPor->CalcSpacing( nSpaceAdd, aInf );
874 const SwTwips nInsideOfst = pOrig->Pos().X();
875 pOrig->Pos().X() = nX + nPorWidth -
876 nInsideOfst - pOrig->Width();
878 else
879 pOrig->Pos().X() += nX;
881 if( ((SwMultiPortion*)pPor)->HasBrackets() )
882 pOrig->Pos().X() +=
883 ((SwDoubleLinePortion*)pPor)->PreWidth();
886 if( bSpaceChg )
887 SwDoubleLinePortion::ResetSpaceAdd( pCurr );
889 pCurr = pOldCurr;
890 nStart = nOldStart;
891 nY = nOldY;
892 bPrev = sal_False;
894 return;
896 if ( pPor->PrtWidth() )
898 xub_StrLen nOldLen = pPor->GetLen();
899 pPor->SetLen( nOfst - aInf.GetIdx() );
900 aInf.SetLen( pPor->GetLen() );
901 if( nX || !pPor->InNumberGrp() )
903 SeekAndChg( aInf );
904 const sal_Bool bOldOnWin = aInf.OnWin();
905 aInf.SetOnWin( sal_False ); // keine BULLETs!
906 SwTwips nTmp = nX;
907 aInf.SetKanaComp( pKanaComp );
908 aInf.SetKanaIdx( nKanaIdx );
909 nX += pPor->GetTxtSize( aInf ).Width();
910 aInf.SetOnWin( bOldOnWin );
911 if ( pPor->InSpaceGrp() && nSpaceAdd )
912 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
913 if( bWidth )
915 pPor->SetLen( pPor->GetLen() + 1 );
916 aInf.SetLen( pPor->GetLen() );
917 aInf.SetOnWin( sal_False ); // keine BULLETs!
918 nTmp += pPor->GetTxtSize( aInf ).Width();
919 aInf.SetOnWin( bOldOnWin );
920 if ( pPor->InSpaceGrp() && nSpaceAdd )
921 nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
922 pOrig->Width( nTmp - nX );
925 pPor->SetLen( nOldLen );
927 bWidth = sal_False;
928 break;
933 if( pPor )
935 OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
936 sal_Bool bEmptyFld = sal_False;
937 if( pPor->InFldGrp() && pPor->GetLen() )
939 SwFldPortion *pTmp = (SwFldPortion*)pPor;
940 while( pTmp->HasFollow() && !pTmp->GetExp().Len() )
942 KSHORT nAddX = pTmp->Width();
943 SwLinePortion *pNext = pTmp->GetPortion();
944 while( pNext && !pNext->InFldGrp() )
946 OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
947 nAddX = nAddX + pNext->Width();
948 pNext = pNext->GetPortion();
950 if( !pNext )
951 break;
952 pTmp = (SwFldPortion*)pNext;
953 nPorHeight = pTmp->Height();
954 nPorAscent = pTmp->GetAscent();
955 nX += nAddX;
956 bEmptyFld = sal_True;
959 // 8513: Felder im Blocksatz, ueberspringen
960 while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
961 ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
962 pPor->IsBlankPortion() || pPor->InTabGrp() ||
963 ( !bEmptyFld && pPor->InFldGrp() ) ) )
965 if ( pPor->InSpaceGrp() && nSpaceAdd )
966 nX += pPor->PrtWidth() +
967 pPor->CalcSpacing( nSpaceAdd, aInf );
968 else
970 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
972 if ( pCurr->IsSpaceAdd() )
974 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
975 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
976 else
977 nSpaceAdd = 0;
980 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
981 ++nKanaIdx;
983 if ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
984 !pPor->GetPortion()->IsMarginPortion() ) )
985 nX += pPor->PrtWidth();
987 if( pPor->IsMultiPortion() &&
988 ((SwMultiPortion*)pPor)->HasTabulator() )
990 if ( pCurr->IsSpaceAdd() )
992 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
993 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
994 else
995 nSpaceAdd = 0;
998 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
999 ++nKanaIdx;
1001 if( !pPor->IsFlyPortion() )
1003 nPorHeight = pPor->Height();
1004 nPorAscent = pPor->GetAscent();
1006 pPor = pPor->GetPortion();
1009 if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
1010 pPor->GetPortion() && pPor->GetPortion()->InFixGrp() )
1012 // Alle Sonderportions muessen uebersprungen werden
1013 // Beispiel: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
1014 // Ohne den Ausgleich landen wir vor '-' mit dem
1015 // Ausgleich vor 's'.
1016 while( pPor && !pPor->GetLen() )
1018 nX += pPor->Width();
1019 if( !pPor->IsMarginPortion() )
1021 nPorHeight = pPor->Height();
1022 nPorAscent = pPor->GetAscent();
1024 pPor = pPor->GetPortion();
1027 if( pPor && pCMS )
1029 if( pCMS->bFieldInfo && pPor->InFldGrp() && pPor->Width() )
1030 pOrig->Width( pPor->Width() );
1031 if( pPor->IsDropPortion() )
1033 nPorAscent = ((SwDropPortion*)pPor)->GetDropHeight();
1034 // The drop height is only calculated, if we have more than
1035 // one line. Otherwise it is 0.
1036 if ( ! nPorAscent)
1037 nPorAscent = pPor->Height();
1038 nPorHeight = nPorAscent;
1039 pOrig->Height( nPorHeight +
1040 ((SwDropPortion*)pPor)->GetDropDescent() );
1041 if( nTmpHeight < pOrig->Height() )
1043 nTmpAscent = nPorAscent;
1044 nTmpHeight = sal_uInt16( pOrig->Height() );
1047 if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
1048 aInf.GetIdx() == nOfst )
1050 if( !pPor->IsFlyPortion() && pPor->Height() &&
1051 pPor->GetAscent() )
1053 nPorHeight = pPor->Height();
1054 nPorAscent = pPor->GetAscent();
1056 SwTwips nTmp;
1057 if( 2 > pPor->GetLen() )
1059 nTmp = pPor->Width();
1060 if ( pPor->InSpaceGrp() && nSpaceAdd )
1061 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1063 else
1065 const sal_Bool bOldOnWin = aInf.OnWin();
1066 xub_StrLen nOldLen = pPor->GetLen();
1067 pPor->SetLen( 1 );
1068 aInf.SetLen( pPor->GetLen() );
1069 SeekAndChg( aInf );
1070 aInf.SetOnWin( sal_False ); // keine BULLETs!
1071 aInf.SetKanaComp( pKanaComp );
1072 aInf.SetKanaIdx( nKanaIdx );
1073 nTmp = pPor->GetTxtSize( aInf ).Width();
1074 aInf.SetOnWin( bOldOnWin );
1075 if ( pPor->InSpaceGrp() && nSpaceAdd )
1076 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1077 pPor->SetLen( nOldLen );
1079 pOrig->Width( nTmp );
1082 // travel inside field portion?
1083 if ( pCMS->pSpecialPos )
1085 // apply attributes to font
1086 Seek( nOfst );
1087 lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
1092 // special case: We are at the beginning of a BidiPortion or
1093 // directly behind a BidiPortion
1094 if ( pCMS &&
1095 ( pLastBidiPor ||
1096 ( pPor &&
1097 pPor->IsMultiPortion() &&
1098 ((SwMultiPortion*)pPor)->IsBidi() ) ) )
1100 // we determine if the cursor has to blink before or behind
1101 // the bidi portion
1102 if ( pLastBidiPor )
1104 const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
1106 if ( pCMS->nCursorBidiLevel >= nPortionLevel )
1108 // we came from inside the bidi portion, we want to blink
1109 // behind the portion
1110 pOrig->Pos().X() -= nLastBidiPorWidth;
1112 // Again, there is a special case: logically behind
1113 // the portion can actually mean that the cursor is inside
1114 // the portion. This can happen is the last portion
1115 // inside the bidi portion is a nested bidi portion
1116 SwLineLayout& rLineLayout =
1117 ((SwMultiPortion*)pLastBidiPor)->GetRoot();
1119 const SwLinePortion *pLast = rLineLayout.FindLastPortion();
1120 if ( pLast->IsMultiPortion() )
1122 OSL_ENSURE( ((SwMultiPortion*)pLast)->IsBidi(),
1123 "Non-BidiPortion inside BidiPortion" );
1124 pOrig->Pos().X() += pLast->Width() +
1125 pLast->CalcSpacing( nSpaceAdd, aInf );
1129 else
1131 const sal_uInt8 nPortionLevel = ((SwBidiPortion*)pPor)->GetLevel();
1133 if ( pCMS->nCursorBidiLevel >= nPortionLevel )
1135 // we came from inside the bidi portion, we want to blink
1136 // behind the portion
1137 pOrig->Pos().X() += pPor->Width() +
1138 pPor->CalcSpacing( nSpaceAdd, aInf );
1143 pOrig->Pos().X() += nX;
1145 if ( pCMS && pCMS->bRealHeight )
1147 nTmpAscent = AdjustBaseLine( *pCurr, 0, nPorHeight, nPorAscent );
1148 if ( nTmpAscent > nPorAscent )
1149 pCMS->aRealHeight.X() = nTmpAscent - nPorAscent;
1150 else
1151 pCMS->aRealHeight.X() = 0;
1152 OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
1153 if ( nTmpHeight > nPorHeight )
1154 pCMS->aRealHeight.Y() = nPorHeight;
1155 else
1156 pCMS->aRealHeight.Y() = nTmpHeight;
1161 /*************************************************************************
1162 * SwTxtCursor::GetCharRect()
1163 *************************************************************************/
1165 sal_Bool SwTxtCursor::GetCharRect( SwRect* pOrig, const xub_StrLen nOfst,
1166 SwCrsrMoveState* pCMS, const long nMax )
1168 CharCrsrToLine(nOfst);
1170 // Indicates that a position inside a special portion (field, number portion)
1171 // is requested.
1172 const sal_Bool bSpecialPos = pCMS && pCMS->pSpecialPos;
1173 xub_StrLen nFindOfst = nOfst;
1175 if ( bSpecialPos )
1177 const sal_uInt8 nExtendRange = pCMS->pSpecialPos->nExtendRange;
1179 OSL_ENSURE( ! pCMS->pSpecialPos->nLineOfst || SP_EXTEND_RANGE_BEFORE != nExtendRange,
1180 "LineOffset AND Number Portion?" );
1182 // portions which are behind the string
1183 if ( SP_EXTEND_RANGE_BEHIND == nExtendRange )
1184 ++nFindOfst;
1186 // skip lines for fields which cover more than one line
1187 for ( sal_uInt16 i = 0; i < pCMS->pSpecialPos->nLineOfst; i++ )
1188 Next();
1191 // Adjustierung ggf. nachholen
1192 GetAdjusted();
1194 const Point aCharPos( GetTopLeft() );
1195 sal_Bool bRet = sal_True;
1197 _GetCharRect( pOrig, nFindOfst, pCMS );
1199 const SwTwips nTmpRight = Right() - 12;
1201 pOrig->Pos().X() += aCharPos.X();
1202 pOrig->Pos().Y() += aCharPos.Y();
1204 if( pCMS && pCMS->b2Lines && pCMS->p2Lines )
1206 pCMS->p2Lines->aLine.Pos().X() += aCharPos.X();
1207 pCMS->p2Lines->aLine.Pos().Y() += aCharPos.Y();
1208 pCMS->p2Lines->aPortion.Pos().X() += aCharPos.X();
1209 pCMS->p2Lines->aPortion.Pos().Y() += aCharPos.Y();
1212 if( pOrig->Left() > nTmpRight )
1213 pOrig->Pos().X() = nTmpRight;
1215 if( nMax )
1217 if( pOrig->Top() + pOrig->Height() > nMax )
1219 if( pOrig->Top() > nMax )
1220 pOrig->Top( nMax );
1221 pOrig->Height( nMax - pOrig->Top() );
1223 if ( pCMS && pCMS->bRealHeight && pCMS->aRealHeight.Y() >= 0 )
1225 long nTmp = pCMS->aRealHeight.X() + pOrig->Top();
1226 if( nTmp >= nMax )
1228 pCMS->aRealHeight.X() = nMax - pOrig->Top();
1229 pCMS->aRealHeight.Y() = 0;
1231 else if( nTmp + pCMS->aRealHeight.Y() > nMax )
1232 pCMS->aRealHeight.Y() = nMax - nTmp;
1235 long nOut = pOrig->Right() - GetTxtFrm()->Frm().Right();
1236 if( nOut > 0 )
1238 if( GetTxtFrm()->Frm().Width() < GetTxtFrm()->Prt().Left()
1239 + GetTxtFrm()->Prt().Width() )
1240 nOut += GetTxtFrm()->Frm().Width() - GetTxtFrm()->Prt().Left()
1241 - GetTxtFrm()->Prt().Width();
1242 if( nOut > 0 )
1243 pOrig->Pos().X() -= nOut + 10;
1245 return bRet;
1248 /*************************************************************************
1249 * SwTxtCursor::GetCrsrOfst()
1251 * Return: Offset im String
1252 *************************************************************************/
1253 xub_StrLen SwTxtCursor::GetCrsrOfst( SwPosition *pPos, const Point &rPoint,
1254 const MSHORT nChgNode, SwCrsrMoveState* pCMS ) const
1256 // Adjustierung ggf. nachholen
1257 GetAdjusted();
1259 const XubString &rText = GetInfo().GetTxt();
1260 xub_StrLen nOffset = 0;
1262 // x ist der horizontale Offset innerhalb der Zeile.
1263 SwTwips x = rPoint.X();
1264 const SwTwips nLeftMargin = GetLineStart();
1265 SwTwips nRightMargin = GetLineEnd();
1266 if( nRightMargin == nLeftMargin )
1267 nRightMargin += 30;
1269 const sal_Bool bLeftOver = x < nLeftMargin;
1270 if( bLeftOver )
1271 x = nLeftMargin;
1272 const sal_Bool bRightOver = x > nRightMargin;
1273 if( bRightOver )
1274 x = nRightMargin;
1276 sal_Bool bRightAllowed = pCMS && ( pCMS->eState == MV_NONE );
1278 // Bis hierher in Dokumentkoordinaten.
1279 x -= nLeftMargin;
1281 KSHORT nX = KSHORT( x );
1283 // Wenn es in der Zeile Attributwechsel gibt, den Abschnitt
1284 // suchen, in dem nX liegt.
1285 SwLinePortion *pPor = pCurr->GetFirstPortion();
1286 xub_StrLen nCurrStart = nStart;
1287 sal_Bool bHolePortion = sal_False;
1288 sal_Bool bLastHyph = sal_False;
1290 std::deque<sal_uInt16> *pKanaComp = pCurr->GetpKanaComp();
1291 xub_StrLen nOldIdx = GetInfo().GetIdx();
1292 MSHORT nSpaceIdx = 0;
1293 size_t nKanaIdx = 0;
1294 long nSpaceAdd = pCurr->IsSpaceAdd() ? pCurr->GetLLSpaceAdd( 0 ) : 0;
1295 short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
1297 // nWidth ist die Breite der Zeile, oder die Breite des
1298 // Abschnitts mit dem Fontwechsel, in dem nX liegt.
1300 KSHORT nWidth = pPor->Width();
1301 if ( pCurr->IsSpaceAdd() || pKanaComp )
1303 if ( pPor->InSpaceGrp() && nSpaceAdd )
1305 ((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart );
1306 nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1308 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1309 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
1312 if ( pCurr->IsSpaceAdd() )
1314 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
1315 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
1316 else
1317 nSpaceAdd = 0;
1320 if( pKanaComp )
1322 if ( nKanaIdx + 1 < pKanaComp->size() )
1323 nKanaComp = (*pKanaComp)[++nKanaIdx];
1324 else
1325 nKanaComp = 0;
1330 KSHORT nWidth30;
1331 if ( pPor->IsPostItsPortion() )
1332 nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
1333 else
1334 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ?
1335 30 :
1336 nWidth;
1338 while( pPor->GetPortion() && nWidth30 < nX && !pPor->IsBreakPortion() )
1340 nX = nX - nWidth;
1341 nCurrStart = nCurrStart + pPor->GetLen();
1342 bHolePortion = pPor->IsHolePortion();
1343 pPor = pPor->GetPortion();
1344 nWidth = pPor->Width();
1345 if ( pCurr->IsSpaceAdd() || pKanaComp )
1347 if ( pPor->InSpaceGrp() && nSpaceAdd )
1349 ((SwTxtSizeInfo&)GetInfo()).SetIdx( nCurrStart );
1350 nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1353 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1354 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->HasTabulator() )
1357 if ( pCurr->IsSpaceAdd() )
1359 if ( ++nSpaceIdx < pCurr->GetLLSpaceAddCount() )
1360 nSpaceAdd = pCurr->GetLLSpaceAdd( nSpaceIdx );
1361 else
1362 nSpaceAdd = 0;
1365 if ( pKanaComp )
1367 if( nKanaIdx + 1 < pKanaComp->size() )
1368 nKanaComp = (*pKanaComp)[++nKanaIdx];
1369 else
1370 nKanaComp = 0;
1375 if ( pPor->IsPostItsPortion() )
1376 nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
1377 else
1378 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFldGrp() ?
1379 30 :
1380 nWidth;
1381 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1382 bLastHyph = pPor->InHyphGrp();
1385 const sal_Bool bLastPortion = (0 == pPor->GetPortion());
1387 if( nX==nWidth )
1389 SwLinePortion *pNextPor = pPor->GetPortion();
1390 while( pNextPor && pNextPor->InFldGrp() && !pNextPor->Width() )
1392 nCurrStart = nCurrStart + pPor->GetLen();
1393 pPor = pNextPor;
1394 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1395 bLastHyph = pPor->InHyphGrp();
1396 pNextPor = pPor->GetPortion();
1400 ((SwTxtSizeInfo&)GetInfo()).SetIdx( nOldIdx );
1402 xub_StrLen nLength = pPor->GetLen();
1404 sal_Bool bFieldInfo = pCMS && pCMS->bFieldInfo;
1406 if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
1407 ( pPor->InNumberGrp() && !pPor->IsFtnNumPortion() ) ||
1408 ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
1409 ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
1412 // #i27615#
1413 if (pCMS)
1415 if( pCMS->bInFrontOfLabel)
1417 if (! (2 * nX < nWidth && pPor->InNumberGrp() &&
1418 !pPor->IsFtnNumPortion()))
1419 pCMS->bInFrontOfLabel = sal_False;
1423 // 7684: Wir sind genau auf der HyphPortion angelangt und muessen dafuer
1424 // sorgen, dass wir in dem String landen.
1425 // 7993: Wenn die Laenge 0 ist muessen wir raus...
1426 if( !nLength )
1428 if( pCMS )
1430 if( pPor->IsFlyPortion() && bFieldInfo )
1431 ((SwCrsrMoveState*)pCMS)->bPosCorr = sal_True;
1433 if (!bRightOver && nX)
1435 if( pPor->IsFtnNumPortion())
1436 ((SwCrsrMoveState*)pCMS)->bFtnNoInfo = sal_True;
1437 else if (pPor->InNumberGrp() ) // #i23726#
1439 ((SwCrsrMoveState*)pCMS)->nInNumPostionOffset = nX;
1440 ((SwCrsrMoveState*)pCMS)->bInNumPortion = sal_True;
1444 if( !nCurrStart )
1445 return 0;
1447 // 7849, 7816: auf pPor->GetHyphPortion kann nicht verzichtet werden!
1448 if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
1449 ( pPor->IsMarginPortion() && !pPor->GetPortion() &&
1450 // 46598: In der letzten Zeile eines zentrierten Absatzes wollen
1451 // wir auch mal hinter dem letzten Zeichen landen.
1452 nCurrStart < rText.Len() ) )
1453 --nCurrStart;
1454 else if( pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()
1455 && nWidth > nX )
1457 if( bFieldInfo )
1458 --nCurrStart;
1459 else
1461 KSHORT nHeight = pPor->Height();
1462 if ( !nHeight || nHeight > nWidth )
1463 nHeight = nWidth;
1464 if( nChgNode && nWidth - nHeight/2 > nX )
1465 --nCurrStart;
1468 return nCurrStart;
1470 if ( 1 == nLength )
1472 if ( nWidth )
1474 // Sonst kommen wir nicht mehr in zeichengeb. Rahmen hinein...
1475 if( !( nChgNode && pPos && pPor->IsFlyCntPortion() ) )
1477 if ( pPor->InFldGrp() ||
1478 ( pPor->IsMultiPortion() &&
1479 ((SwMultiPortion*)pPor)->IsBidi() ) )
1481 KSHORT nHeight = 0;
1482 if( !bFieldInfo )
1484 nHeight = pPor->Height();
1485 if ( !nHeight || nHeight > nWidth )
1486 nHeight = nWidth;
1489 if( nWidth - nHeight/2 <= nX &&
1490 ( ! pPor->InFldGrp() ||
1491 !((SwFldPortion*)pPor)->HasFollow() ) )
1492 ++nCurrStart;
1494 else if ( ( !pPor->IsFlyPortion() || ( pPor->GetPortion() &&
1495 !pPor->GetPortion()->IsMarginPortion() &&
1496 !pPor->GetPortion()->IsHolePortion() ) )
1497 && ( nWidth/2 < nX ) &&
1498 ( !bFieldInfo ||
1499 ( pPor->GetPortion() &&
1500 pPor->GetPortion()->IsPostItsPortion() ) )
1501 && ( bRightAllowed || !bLastHyph ))
1502 ++nCurrStart;
1504 // if we want to get the position inside the field, we should not return
1505 if ( !pCMS || !pCMS->pSpecialPos )
1506 return nCurrStart;
1509 else
1511 if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
1512 pPor->InToxRefGrp() )
1513 return nCurrStart;
1514 if ( pPor->InFldGrp() )
1516 if( bRightOver && !((SwFldPortion*)pPor)->HasFollow() )
1517 ++nCurrStart;
1518 return nCurrStart;
1523 if( bLastPortion && (pCurr->GetNext() || pFrm->GetFollow() ) )
1524 --nLength;
1526 if( nWidth > nX ||
1527 ( nWidth == nX && pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsDouble() ) )
1529 if( pPor->IsMultiPortion() )
1531 // In a multi-portion we use GetCrsrOfst()-function recursively
1532 SwTwips nTmpY = rPoint.Y() - pCurr->GetAscent() + pPor->GetAscent();
1533 // if we are in the first line of a double line portion, we have
1534 // to add a value to nTmpY for not staying in this line
1535 // we also want to skip the first line, if we are inside ruby
1536 if ( ( ((SwTxtSizeInfo*)pInf)->IsMulti() &&
1537 ((SwTxtSizeInfo*)pInf)->IsFirstMulti() ) ||
1538 ( ((SwMultiPortion*)pPor)->IsRuby() &&
1539 ((SwMultiPortion*)pPor)->OnTop() ) )
1540 nTmpY += ((SwMultiPortion*)pPor)->Height();
1542 // Important for cursor traveling in ruby portions:
1543 // We have to set nTmpY to 0 in order to stay in the first row
1544 // if the phonetic line is the second row
1545 if ( ((SwMultiPortion*)pPor)->IsRuby() &&
1546 ! ((SwMultiPortion*)pPor)->OnTop() )
1547 nTmpY = 0;
1549 SwTxtCursorSave aSave( (SwTxtCursor*)this, (SwMultiPortion*)pPor,
1550 nTmpY, nX, nCurrStart, nSpaceAdd );
1552 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1553 if ( ((SwMultiPortion*)pPor)->IsBidi() )
1555 const sal_uInt8 nBidiLevel = ((SwBidiPortion*)pPor)->GetLevel();
1556 aLayoutModeModifier.Modify( nBidiLevel % 2 );
1559 if( ((SwMultiPortion*)pPor)->HasRotation() )
1561 nTmpY -= nY;
1562 if( !((SwMultiPortion*)pPor)->IsRevers() )
1563 nTmpY = pPor->Height() - nTmpY;
1564 if( nTmpY < 0 )
1565 nTmpY = 0;
1566 nX = (KSHORT)nTmpY;
1569 if( ((SwMultiPortion*)pPor)->HasBrackets() )
1571 sal_uInt16 nPreWidth = ((SwDoubleLinePortion*)pPor)->PreWidth();
1572 if ( nX > nPreWidth )
1573 nX = nX - nPreWidth;
1574 else
1575 nX = 0;
1578 return GetCrsrOfst( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
1579 nChgNode, pCMS );
1581 if( pPor->InTxtGrp() )
1583 sal_uInt8 nOldProp;
1584 if( GetPropFont() )
1586 ((SwFont*)GetFnt())->SetProportion( GetPropFont() );
1587 nOldProp = GetFnt()->GetPropr();
1589 else
1590 nOldProp = 0;
1592 SwTxtSizeInfo aSizeInf( GetInfo(), rText, nCurrStart );
1593 ((SwTxtCursor*)this)->SeekAndChg( aSizeInf );
1594 SwTxtSlot aDiffTxt( &aSizeInf, ((SwTxtPortion*)pPor), false, false );
1595 SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
1596 ((SwDropPortion*)pPor)->GetFnt() : NULL );
1598 SwParaPortion* pPara = (SwParaPortion*)GetInfo().GetParaPortion();
1599 OSL_ENSURE( pPara, "No paragraph!" );
1601 SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
1602 *aSizeInf.GetOut(),
1603 &pPara->GetScriptInfo(),
1604 aSizeInf.GetTxt(),
1605 aSizeInf.GetIdx(),
1606 pPor->GetLen() );
1607 aDrawInf.SetOfst( nX );
1609 if ( nSpaceAdd )
1611 xub_StrLen nCharCnt;
1612 // #i41860# Thai justified alignemt needs some
1613 // additional information:
1614 aDrawInf.SetNumberOfBlanks( pPor->InTxtGrp() ?
1615 static_cast<const SwTxtPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
1616 0 );
1619 if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
1620 aDrawInf.SetLen( STRING_LEN ); // SMARTTAGS
1622 aDrawInf.SetSpace( nSpaceAdd );
1623 aDrawInf.SetFont( aSizeInf.GetFont() );
1624 aDrawInf.SetFrm( pFrm );
1625 aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
1626 aDrawInf.SetPosMatchesBounds( pCMS && pCMS->bPosMatchesBounds );
1628 if ( SW_CJK == aSizeInf.GetFont()->GetActual() &&
1629 pPara->GetScriptInfo().CountCompChg() &&
1630 ! pPor->InFldGrp() )
1631 aDrawInf.SetKanaComp( nKanaComp );
1633 nLength = aSizeInf.GetFont()->_GetCrsrOfst( aDrawInf );
1635 // get position inside field portion?
1636 if ( pPor->InFldGrp() && pCMS && pCMS->pSpecialPos )
1638 pCMS->pSpecialPos->nCharOfst = nLength;
1639 nLength = 0; // SMARTTAGS
1642 // set cursor bidi level
1643 if ( pCMS )
1644 ((SwCrsrMoveState*)pCMS)->nCursorBidiLevel =
1645 aDrawInf.GetCursorBidiLevel();
1647 if( bFieldInfo && nLength == pPor->GetLen() &&
1648 ( ! pPor->GetPortion() ||
1649 ! pPor->GetPortion()->IsPostItsPortion() ) )
1650 --nLength;
1652 if( nOldProp )
1653 ((SwFont*)GetFnt())->SetProportion( nOldProp );
1655 else
1657 if( nChgNode && pPos && pPor->IsFlyCntPortion()
1658 && !( (SwFlyCntPortion*)pPor )->IsDraw() )
1660 // JP 24.11.94: liegt die Pos nicht im Fly, dann
1661 // darf nicht mit STRING_LEN returnt werden!
1662 // (BugId: 9692 + Aenderung in feshview)
1663 SwFlyInCntFrm *pTmp = ( (SwFlyCntPortion*)pPor )->GetFlyFrm();
1664 sal_Bool bChgNode = 1 < nChgNode;
1665 if( !bChgNode )
1667 SwFrm* pLower = pTmp->GetLower();
1668 if( pLower && (pLower->IsTxtFrm() || pLower->IsLayoutFrm()) )
1669 bChgNode = sal_True;
1671 Point aTmpPoint( rPoint );
1673 if ( pFrm->IsRightToLeft() )
1674 pFrm->SwitchLTRtoRTL( aTmpPoint );
1676 if ( pFrm->IsVertical() )
1677 pFrm->SwitchHorizontalToVertical( aTmpPoint );
1679 if( bChgNode && pTmp->Frm().IsInside( aTmpPoint ) &&
1680 !( pTmp->IsProtected() ) )
1682 nLength = ((SwFlyCntPortion*)pPor)->
1683 GetFlyCrsrOfst( nX, aTmpPoint, pPos, pCMS );
1684 // Sobald der Frame gewechselt wird, muessen wir aufpassen, dass
1685 // unser Font wieder im OutputDevice steht.
1686 // vgl. Paint und new SwFlyCntPortion !
1687 ((SwTxtSizeInfo*)pInf)->SelectFont();
1689 // 6776: Das pIter->GetCrsrOfst returnt
1690 // aus einer Verschachtelung mit STRING_LEN.
1691 return STRING_LEN;
1694 else
1695 nLength = pPor->GetCrsrOfst( nX );
1698 nOffset = nCurrStart + nLength;
1700 // 7684: Wir sind vor der HyphPortion angelangt und muessen dafuer
1701 // sorgen, dass wir in dem String landen.
1702 // Bei Zeilenenden vor FlyFrms muessen ebenso behandelt werden.
1704 if( nOffset && pPor->GetLen() == nLength && pPor->GetPortion() &&
1705 !pPor->GetPortion()->GetLen() && pPor->GetPortion()->InHyphGrp() )
1706 --nOffset;
1708 return nOffset;
1711 /** Looks for text portions which are inside the given rectangle
1713 For a rectangular text selection every text portions which is inside the given
1714 rectangle has to be put into the SwSelectionList as SwPaM
1715 From these SwPaM the SwCursors will be created.
1717 @param rSelList
1718 The container for the overlapped text portions
1720 @param rRect
1721 A rectangle in document coordinates, text inside this rectangle has to be
1722 selected.
1724 @return [ true, false ]
1725 true if any overlapping text portion has been found and put into list
1726 false if no portion overlaps, the list has been unchanged
1728 bool SwTxtFrm::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
1730 bool bRet = false;
1731 // PaintArea() instead Frm() for negative indents
1732 SwRect aTmpFrm( PaintArea() );
1733 if( !rRect.IsOver( aTmpFrm ) )
1734 return false;
1735 if( rSelList.checkContext( this ) )
1737 SwRect aRect( aTmpFrm );
1738 aRect.Intersection( rRect );
1739 // rNode without const to create SwPaMs
1740 SwCntntNode &rNode = const_cast<SwCntntNode&>( *GetNode() );
1741 SwNodeIndex aIdx( rNode );
1742 SwPosition aPosL( aIdx, SwIndex( &rNode, 0 ) );
1743 if( IsEmpty() )
1745 SwPaM *pPam = new SwPaM( aPosL, aPosL );
1746 rSelList.insertPaM( pPam );
1748 else if( aRect.HasArea() )
1750 xub_StrLen nOld = STRING_LEN;
1751 SwPosition aPosR( aPosL );
1752 Point aPoint;
1753 SwTxtInfo aInf( const_cast<SwTxtFrm*>(this) );
1754 SwTxtIter aLine( const_cast<SwTxtFrm*>(this), &aInf );
1755 // We have to care for top-to-bottom layout, where right becomes top etc.
1756 SWRECTFN( this )
1757 SwTwips nTop = (aRect.*fnRect->fnGetTop)();
1758 SwTwips nBottom = (aRect.*fnRect->fnGetBottom)();
1759 SwTwips nLeft = (aRect.*fnRect->fnGetLeft)();
1760 SwTwips nRight = (aRect.*fnRect->fnGetRight)();
1761 SwTwips nY = aLine.Y(); // Top position of the first line
1762 SwTwips nLastY = nY;
1763 while( nY < nTop && aLine.Next() ) // line above rectangle
1765 nLastY = nY;
1766 nY = aLine.Y();
1768 bool bLastLine = false;
1769 if( nY < nTop && !aLine.GetNext() )
1771 bLastLine = true;
1772 nY += aLine.GetLineHeight();
1774 do // check the lines for overlapping
1776 if( nLastY < nTop ) // if the last line was above rectangle
1777 nLastY = nTop;
1778 if( nY > nBottom ) // if the current line leaves the rectangle
1779 nY = nBottom;
1780 if( nY >= nLastY ) // gotcha: overlapping
1782 nLastY += nY;
1783 nLastY /= 2;
1784 if( bVert )
1786 aPoint.X() = nLastY;
1787 aPoint.Y() = nLeft;
1789 else
1791 aPoint.X() = nLeft;
1792 aPoint.Y() = nLastY;
1794 // Looking for the position of the left border of the rectangle
1795 // in this text line
1796 SwCrsrMoveState aState( MV_UPDOWN );
1797 if( GetCrsrOfst( &aPosL, aPoint, &aState ) )
1799 if( bVert )
1801 aPoint.X() = nLastY;
1802 aPoint.Y() = nRight;
1804 else
1806 aPoint.X() = nRight;
1807 aPoint.Y() = nLastY;
1809 // If we get a right position and if the left position
1810 // is not the same like the left position of the line before
1811 // which cound happen e.g. for field portions or fly frames
1812 // a SwPaM will be inserted with these positions
1813 if( GetCrsrOfst( &aPosR, aPoint, &aState ) &&
1814 nOld != aPosL.nContent.GetIndex() )
1816 SwPaM *pPam = new SwPaM( aPosL, aPosR );
1817 rSelList.insertPaM( pPam );
1818 nOld = aPosL.nContent.GetIndex();
1822 if( aLine.Next() )
1824 nLastY = nY;
1825 nY = aLine.Y();
1827 else if( !bLastLine )
1829 bLastLine = true;
1830 nLastY = nY;
1831 nY += aLine.GetLineHeight();
1833 else
1834 break;
1835 }while( nLastY < nBottom );
1838 if( GetDrawObjs() )
1840 const SwSortedObjs &rObjs = *GetDrawObjs();
1841 for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
1843 const SwAnchoredObject* pAnchoredObj = rObjs[i];
1844 if( !pAnchoredObj->ISA(SwFlyFrm) )
1845 continue;
1846 const SwFlyFrm* pFly = static_cast<const SwFlyFrm*>(pAnchoredObj);
1847 if( pFly->IsFlyInCntFrm() && pFly->FillSelection( rSelList, rRect ) )
1848 bRet = true;
1851 return bRet;
1854 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */