android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / text / itrcrsr.cxx
blob80900061b66f858c05ada0a0c156091021281ed9
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 .
20 #include <ndtxt.hxx>
21 #include <doc.hxx>
22 #include <paratr.hxx>
23 #include <flyfrm.hxx>
24 #include <pam.hxx>
25 #include <swselectionlist.hxx>
26 #include <sortedobjs.hxx>
27 #include <editeng/adjustitem.hxx>
28 #include <editeng/lspcitem.hxx>
29 #include <editeng/lrspitem.hxx>
30 #include <frmatr.hxx>
31 #include <tgrditem.hxx>
32 #include <IDocumentSettingAccess.hxx>
33 #include <pagefrm.hxx>
35 #include "itrtxt.hxx"
36 #include <txtfrm.hxx>
37 #include <flyfrms.hxx>
38 #include "porfld.hxx"
39 #include "porfly.hxx"
40 #include "pordrop.hxx"
41 #include <crstate.hxx>
42 #include "pormulti.hxx"
43 #include <numrule.hxx>
44 #include <com/sun/star/i18n/ScriptType.hpp>
46 // Not reentrant !!!
47 // is set in GetCharRect and is interpreted in UnitUp/Down.
48 bool SwTextCursor::s_bRightMargin = false;
50 // After calculating the position of a character during GetCharRect
51 // this function allows to find the coordinates of a position (defined
52 // in pCMS->pSpecialPos) inside a special portion (e.g., a field)
53 static void lcl_GetCharRectInsideField( SwTextSizeInfo& rInf, SwRect& rOrig,
54 const SwCursorMoveState& rCMS,
55 const SwLinePortion& rPor )
57 OSL_ENSURE( rCMS.m_pSpecialPos, "Information about special pos missing" );
59 if ( rPor.InFieldGrp() && !static_cast<const SwFieldPortion&>(rPor).GetExp().isEmpty() )
61 const sal_Int32 nCharOfst = rCMS.m_pSpecialPos->nCharOfst;
62 sal_Int32 nFieldIdx = 0;
63 sal_Int32 nFieldLen = 0;
65 OUString sString;
66 const OUString* pString = nullptr;
67 const SwLinePortion* pPor = &rPor;
70 if ( pPor->InFieldGrp() )
72 sString = static_cast<const SwFieldPortion*>(pPor)->GetExp();
73 pString = &sString;
74 nFieldLen = pString->getLength();
76 else
78 pString = nullptr;
79 nFieldLen = 0;
82 if ( ! pPor->GetNextPortion() || nFieldIdx + nFieldLen > nCharOfst )
83 break;
85 nFieldIdx = nFieldIdx + nFieldLen;
86 rOrig.Pos().AdjustX(pPor->Width() );
87 pPor = pPor->GetNextPortion();
89 } while ( true );
91 OSL_ENSURE( nCharOfst >= nFieldIdx, "Request of position inside field failed" );
92 sal_Int32 nLen = nCharOfst - nFieldIdx + 1;
94 if ( pString )
96 // get script for field portion
97 rInf.GetFont()->SetActual( SwScriptInfo::WhichFont(0, *pString) );
99 TextFrameIndex const nOldLen = pPor->GetLen();
100 const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen - 1));
101 const SwTwips nX1 = pPor->GetLen() ?
102 pPor->GetTextSize( rInf ).Width() :
105 SwTwips nX2 = 0;
106 if ( rCMS.m_bRealWidth )
108 const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen));
109 nX2 = pPor->GetTextSize( rInf ).Width();
112 const_cast<SwLinePortion*>(pPor)->SetLen( nOldLen );
114 rOrig.Pos().AdjustX(nX1 );
115 rOrig.Width( ( nX2 > nX1 ) ?
116 ( nX2 - nX1 ) :
117 1 );
120 else
122 // special cases: no common fields, e.g., graphic number portion,
123 // FlyInCntPortions, Notes
124 rOrig.Width( rCMS.m_bRealWidth && rPor.Width() ? rPor.Width() : 1 );
128 // #i111284#
129 namespace {
130 bool IsLabelAlignmentActive( const SwTextNode& rTextNode )
132 bool bRet( false );
134 if ( rTextNode.GetNumRule() )
136 int nListLevel = rTextNode.GetActualListLevel();
138 if (nListLevel < 0)
139 nListLevel = 0;
141 if (nListLevel >= MAXLEVEL)
142 nListLevel = MAXLEVEL - 1;
144 const SwNumFormat& rNumFormat =
145 rTextNode.GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
146 if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
148 bRet = true;
152 return bRet;
154 } // end of anonymous namespace
156 void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
158 CtorInitTextIter( pNewFrame, pNewInf );
160 m_pInf = pNewInf;
161 GetInfo().SetFont( GetFnt() );
162 const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
164 SvxFirstLineIndentItem const& rFirstLine(pNode->GetSwAttrSet().GetFirstLineIndent());
165 SvxTextLeftMarginItem const& rTextLeftMargin(pNode->GetSwAttrSet().GetTextLeftMargin());
166 // #i95907#
167 // #i111284#
168 const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
169 const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
170 const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable() != ::sw::ListLevelIndents::No;
171 const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;
173 // Carefully adjust the text formatting ranges.
175 // This whole area desperately needs some rework. There are
176 // quite a couple of values that need to be considered:
177 // 1. paragraph indent
178 // 2. paragraph first line indent
179 // 3. numbering indent
180 // 4. numbering spacing to text
181 // 5. paragraph border
182 // Note: These values have already been used during calculation
183 // of the printing area of the paragraph.
184 const int nLMWithNum = pNode->GetLeftMarginWithNum( true );
185 if ( m_pFrame->IsRightToLeft() )
187 // this calculation is identical this the calculation for L2R layout - see below
188 mnLeft = m_pFrame->getFrameArea().Left() +
189 m_pFrame->getFramePrintArea().Left() +
190 nLMWithNum -
191 pNode->GetLeftMarginWithNum() -
192 // #i95907#
193 // #i111284#
194 // rSpace.GetLeft() + rSpace.GetTextLeft();
195 (rTextLeftMargin.GetLeft(rFirstLine) - rTextLeftMargin.GetTextLeft());
197 else
199 // #i95907#
200 // #i111284#
201 if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
202 !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
204 // this calculation is identical this the calculation for R2L layout - see above
205 mnLeft = m_pFrame->getFrameArea().Left() +
206 m_pFrame->getFramePrintArea().Left() +
207 nLMWithNum -
208 pNode->GetLeftMarginWithNum() -
209 // #i95907#
210 // #i111284#
211 (rTextLeftMargin.GetLeft(rFirstLine) - rTextLeftMargin.GetTextLeft());
213 else
215 mnLeft = m_pFrame->getFrameArea().Left() +
216 std::max(tools::Long(rTextLeftMargin.GetTextLeft() + nLMWithNum),
217 m_pFrame->getFramePrintArea().Left() );
221 mnRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();
223 if( mnLeft >= mnRight &&
224 // #i53066# Omit adjustment of nLeft for numbered
225 // paras inside cells inside new documents:
226 ( pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
227 !m_pFrame->IsInTab() ||
228 (bListLevelIndentsApplicable && nLMWithNum == rTextLeftMargin.GetTextLeft())
229 || (!bLabelAlignmentActive && nLMWithNum == 0)))
231 mnLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
232 if( mnLeft >= mnRight ) // e.g. with large paragraph indentations in slim table columns
233 mnRight = mnLeft + 1; // einen goennen wir uns immer
236 if( m_pFrame->IsFollow() && m_pFrame->GetOffset() )
237 mnFirst = mnLeft;
238 else
240 short nFLOfst = 0;
241 tools::Long nFirstLineOfs = 0;
242 if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
243 rFirstLine.IsAutoFirst())
245 nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
246 LanguageType const aLang = m_pFrame->GetLangOfChar(
247 TextFrameIndex(0), css::i18n::ScriptType::ASIAN);
248 if (aLang != LANGUAGE_KOREAN && aLang != LANGUAGE_JAPANESE)
249 nFirstLineOfs<<=1;
251 // tdf#129448: Auto first-line indent should not be effected by line space.
252 // Below is for compatibility with old documents.
253 if (!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE))
255 const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
256 if( pSpace )
258 switch( pSpace->GetLineSpaceRule() )
260 case SvxLineSpaceRule::Auto:
261 break;
262 case SvxLineSpaceRule::Min:
264 if( nFirstLineOfs < pSpace->GetLineHeight() )
265 nFirstLineOfs = pSpace->GetLineHeight();
266 break;
268 case SvxLineSpaceRule::Fix:
269 nFirstLineOfs = pSpace->GetLineHeight();
270 break;
271 default: OSL_FAIL( ": unknown LineSpaceRule" );
273 switch( pSpace->GetInterLineSpaceRule() )
275 case SvxInterLineSpaceRule::Off:
276 break;
277 case SvxInterLineSpaceRule::Prop:
279 tools::Long nTmp = pSpace->GetPropLineSpace();
280 // 50% is the minimum, at 0% we switch to
281 // the default value 100%...
282 if( nTmp < 50 )
283 nTmp = nTmp ? 50 : 100;
285 nTmp *= nFirstLineOfs;
286 nTmp /= 100;
287 if( !nTmp )
288 ++nTmp;
289 nFirstLineOfs = nTmp;
290 break;
292 case SvxInterLineSpaceRule::Fix:
294 nFirstLineOfs += pSpace->GetInterLineSpace();
295 break;
297 default: OSL_FAIL( ": unknown InterLineSpaceRule" );
302 else
303 nFirstLineOfs = nFLOfst;
305 // #i95907#
306 // #i111284#
307 if ( m_pFrame->IsRightToLeft() ||
308 bListLevelIndentsApplicableAndLabelAlignmentActive ||
309 !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
311 if ( nFirstLineOfs < 0 && m_pFrame->IsInTab() &&
312 mnLeft == m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left() &&
313 !m_pFrame->IsRightToLeft() &&
314 !bListLevelIndentsApplicableAndLabelAlignmentActive )
316 // tdf#130218 always show hanging indent in narrow table cells
317 // to avoid hiding the text content of the first line
318 mnLeft -= nFirstLineOfs;
321 mnFirst = mnLeft + nFirstLineOfs;
323 else
325 mnFirst = m_pFrame->getFrameArea().Left() +
326 std::max(rTextLeftMargin.GetTextLeft() + nLMWithNum + nFirstLineOfs,
327 m_pFrame->getFramePrintArea().Left() );
330 // Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
331 // value for the new list label position and space mode LABEL_ALIGNMENT
332 // and label alignment CENTER and RIGHT in L2R layout respectively
333 // label alignment LEFT and CENTER in R2L layout
334 mnFirst += m_pFrame->GetAdditionalFirstLineOffset();
336 if( mnFirst >= mnRight )
337 mnFirst = mnRight - 1;
339 const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
340 mnAdjust = rAdjust.GetAdjust();
342 // left is left and right is right
343 if ( m_pFrame->IsRightToLeft() )
345 if ( SvxAdjust::Left == mnAdjust )
346 mnAdjust = SvxAdjust::Right;
347 else if ( SvxAdjust::Right == mnAdjust )
348 mnAdjust = SvxAdjust::Left;
351 m_bOneBlock = rAdjust.GetOneWord() == SvxAdjust::Block;
352 m_bLastBlock = rAdjust.GetLastBlock() == SvxAdjust::Block;
353 m_bLastCenter = rAdjust.GetLastBlock() == SvxAdjust::Center;
355 // #i91133#
356 mnTabLeft = pNode->GetLeftMarginForTabCalculation();
358 DropInit();
361 void SwTextMargin::DropInit()
363 mnDropLeft = mnDropLines = mnDropHeight = mnDropDescent = 0;
364 const SwParaPortion *pPara = GetInfo().GetParaPortion();
365 if( pPara )
367 const SwDropPortion *pPorDrop = pPara->FindDropPortion();
368 if ( pPorDrop )
370 mnDropLeft = pPorDrop->GetDropLeft();
371 mnDropLines = pPorDrop->GetLines();
372 mnDropHeight = pPorDrop->GetDropHeight();
373 mnDropDescent = pPorDrop->GetDropDescent();
378 // The function is interpreting / observing / evaluating / keeping / respecting the first line indention and the specified width.
379 SwTwips SwTextMargin::GetLineStart() const
381 SwTwips nRet = GetLeftMargin();
382 if( GetAdjust() != SvxAdjust::Left &&
383 !m_pCurr->GetFirstPortion()->IsMarginPortion() )
385 // If the first portion is a Margin, then the
386 // adjustment is expressed by the portions.
387 if( GetAdjust() == SvxAdjust::Right )
388 nRet = Right() - CurrWidth();
389 else if( GetAdjust() == SvxAdjust::Center )
390 nRet += (GetLineWidth() - CurrWidth()) / 2;
392 return nRet;
395 void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
397 CtorInitTextMargin( pNewFrame, pNewInf );
398 // 6096: Attention, the iterators are derived!
399 // GetInfo().SetOut( GetInfo().GetWin() );
402 // tdf#120715 tdf#43100: Make width for some HolePortions, so cursor will be able to move into it.
403 // It should not change the layout, so this should be called after the layout is calculated.
404 void SwTextCursor::AddExtraBlankWidth()
406 SwLinePortion* pPos = m_pCurr->GetNextPortion();
407 SwLinePortion* pNextPos;
408 while (pPos)
410 pNextPos = pPos->GetNextPortion();
411 // Do it only if it is the last portion that able to handle the cursor,
412 // else the next portion would miscalculate the cursor position
413 if (pPos->ExtraBlankWidth() && (!pNextPos || pNextPos->IsMarginPortion()))
415 pPos->Width(pPos->Width() + pPos->ExtraBlankWidth());
416 pPos->ExtraBlankWidth(0);
418 pPos = pNextPos;
422 // 1170: Ancient bug: Shift-End forgets the last character ...
423 void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
424 SwCursorMoveState* pCMS, const tools::Long nMax )
426 // 1170: Ambiguity of document positions
427 s_bRightMargin = true;
428 CharCursorToLine(nOfst);
430 // Somehow twisted: nOfst names the position behind the last
431 // character of the last line == This is the position in front of the first character
432 // of the line, in which we are situated:
433 if( nOfst != GetStart() || !m_pCurr->GetLen() )
435 // 8810: Master line RightMargin, after that LeftMargin
436 GetCharRect( pOrig, nOfst, pCMS, nMax );
437 s_bRightMargin = nOfst >= GetEnd() && nOfst < TextFrameIndex(GetInfo().GetText().getLength());
438 return;
441 if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
443 GetCharRect( pOrig, nOfst, pCMS, nMax );
444 return;
447 // If necessary, as catch up, do the adjustment
448 GetAdjusted();
450 tools::Long nX = 0;
451 tools::Long nLast = 0;
452 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
454 SwTwips nTmpHeight, nTmpAscent;
455 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
456 sal_uInt16 nPorHeight = nTmpHeight;
457 sal_uInt16 nPorAscent = nTmpAscent;
459 // Search for the last Text/EndPortion of the line
460 while( pPor )
462 nX = nX + pPor->Width();
463 if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
464 && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
466 nLast = nX;
467 nPorHeight = pPor->Height();
468 nPorAscent = pPor->GetAscent();
470 pPor = pPor->GetNextPortion();
473 const Size aCharSize( 1, nTmpHeight );
474 pOrig->Pos( GetTopLeft() );
475 pOrig->SSize( aCharSize );
476 pOrig->Pos().AdjustX(nLast );
477 const SwTwips nTmpRight = Right() - 1;
478 if( pOrig->Left() > nTmpRight )
479 pOrig->Pos().setX( nTmpRight );
481 if ( pCMS && pCMS->m_bRealHeight )
483 if ( nTmpAscent > nPorAscent )
484 pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
485 else
486 pCMS->m_aRealHeight.setX( 0 );
487 OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
488 pCMS->m_aRealHeight.setY( nPorHeight );
492 // internal function, called by SwTextCursor::GetCharRect() to calculate
493 // the relative character position in the current line.
494 // pOrig refers to x and y coordinates, width and height of the cursor
495 // pCMS is used for restricting the cursor, if there are different font
496 // heights in one line ( first value = offset to y of pOrig, second
497 // value = real height of (shortened) cursor
498 void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
499 SwCursorMoveState* pCMS )
501 const OUString aText = GetInfo().GetText();
502 SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
503 if( GetPropFont() )
504 aInf.GetFont()->SetProportion( GetPropFont() );
505 SwTwips nTmpAscent, nTmpHeight; // Line height
506 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
507 const Size aCharSize( 1, nTmpHeight );
508 const Point aCharPos;
509 pOrig->Pos( aCharPos );
510 pOrig->SSize( aCharSize );
512 // If we are looking for a position inside a field which covers
513 // more than one line we may not skip any "empty portions" at the
514 // beginning of a line
515 const bool bInsideFirstField = pCMS && pCMS->m_pSpecialPos &&
516 ( pCMS->m_pSpecialPos->nLineOfst ||
517 SwSPExtendRange::BEFORE ==
518 pCMS->m_pSpecialPos->nExtendRange );
520 bool bWidth = pCMS && pCMS->m_bRealWidth;
521 if( !m_pCurr->GetLen() && !m_pCurr->Width() )
523 if ( pCMS && pCMS->m_bRealHeight )
525 pCMS->m_aRealHeight.setX( 0 );
526 pCMS->m_aRealHeight.setY( nTmpHeight );
529 else
531 SwTwips nPorHeight = nTmpHeight;
532 SwTwips nPorAscent = nTmpAscent;
533 SwTwips nX = 0;
534 SwTwips nTmpFirst = 0;
535 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
536 SwBidiPortion* pLastBidiPor = nullptr;
537 TextFrameIndex nLastBidiIdx(-1);
538 SwTwips nLastBidiPorWidth = 0;
539 std::deque<sal_uInt16>* pKanaComp = m_pCurr->GetpKanaComp();
540 sal_uInt16 nSpaceIdx = 0;
541 size_t nKanaIdx = 0;
542 tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
544 bool bNoText = true;
546 // First all portions without Len at beginning of line are skipped.
547 // Exceptions are the mean special portions from WhichFirstPortion:
548 // Num, ErgoSum, FootnoteNum, FieldRests
549 // 8477: but also the only Textportion of an empty line with
550 // Right/Center-Adjustment! So not just pPor->GetExpandPortion() ...
551 while( pPor && !pPor->GetLen() && ! bInsideFirstField )
553 nX += pPor->Width();
554 if ( pPor->InSpaceGrp() && nSpaceAdd )
555 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
556 if( bNoText )
557 nTmpFirst = nX;
558 // 8670: EndPortions count once as TextPortions.
559 // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
560 if( pPor->InTextGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
562 bNoText = false;
563 nTmpFirst = nX;
565 if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
567 if ( m_pCurr->IsSpaceAdd() )
569 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
570 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
571 else
572 nSpaceAdd = 0;
575 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
576 ++nKanaIdx;
578 if( pPor->InFixMargGrp() )
580 if( pPor->IsMarginPortion() )
581 bNoText = false;
582 else
584 // fix margin portion => next SpaceAdd, KanaComp value
585 if ( m_pCurr->IsSpaceAdd() )
587 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
588 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
589 else
590 nSpaceAdd = 0;
593 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
594 ++nKanaIdx;
597 pPor = pPor->GetNextPortion();
600 if( !pPor )
602 // There's just Spezialportions.
603 nX = nTmpFirst;
605 else
607 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
608 (!pPor->InFieldGrp() || pPor->GetAscent() ) )
610 nPorHeight = pPor->Height();
611 nPorAscent = pPor->GetAscent();
613 while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
614 ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
616 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
617 (!pPor->InFieldGrp() || pPor->GetAscent() ) )
619 nPorHeight = pPor->Height();
620 nPorAscent = pPor->GetAscent();
623 // If we are behind the portion, we add the portion width to
624 // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
625 // For common portions (including BidiPortions) we want to add
626 // the portion width to nX. For MultiPortions, nExtra = 0,
627 // therefore we go to the 'else' branch and start a recursion.
628 const TextFrameIndex nExtra( (pPor->IsMultiPortion()
629 && !static_cast<SwMultiPortion*>(pPor)->IsBidi()
630 && !bWidth)
631 ? 0 : 1 );
632 if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
634 if ( pPor->InSpaceGrp() && nSpaceAdd )
635 nX += pPor->PrtWidth() +
636 pPor->CalcSpacing( nSpaceAdd, aInf );
637 else
639 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
641 // update to current SpaceAdd, KanaComp values
642 if ( m_pCurr->IsSpaceAdd() )
644 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
645 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
646 else
647 nSpaceAdd = 0;
650 if ( pKanaComp &&
651 ( nKanaIdx + 1 ) < pKanaComp->size()
653 ++nKanaIdx;
655 if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
656 !pPor->GetNextPortion()->IsMarginPortion() ) )
657 nX += pPor->PrtWidth();
659 if( pPor->IsMultiPortion() )
661 if ( static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
663 if ( m_pCurr->IsSpaceAdd() )
665 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
666 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
667 else
668 nSpaceAdd = 0;
671 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
672 ++nKanaIdx;
675 // if we are right behind a BidiPortion, we have to
676 // hold a pointer to the BidiPortion in order to
677 // find the correct cursor position, depending on the
678 // cursor level
679 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() &&
680 aInf.GetIdx() + pPor->GetLen() == nOfst )
682 pLastBidiPor = static_cast<SwBidiPortion*>(pPor);
683 nLastBidiIdx = aInf.GetIdx();
684 nLastBidiPorWidth = pLastBidiPor->Width() +
685 pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );
689 aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
690 pPor = pPor->GetNextPortion();
692 else
694 if( pPor->IsMultiPortion() )
696 nTmpAscent = AdjustBaseLine( *m_pCurr, pPor );
697 GetInfo().SetMulti( true );
698 pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );
700 if( pCMS && pCMS->m_b2Lines )
702 const bool bRecursion (pCMS->m_p2Lines);
703 if ( !bRecursion )
705 pCMS->m_p2Lines.reset(new Sw2LinesPos);
706 pCMS->m_p2Lines->aLine = SwRect(aCharPos, aCharSize);
709 if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
711 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
712 pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_270;
713 else
714 pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_90;
716 else if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
717 pCMS->m_p2Lines->nMultiType = MultiPortionType::TWOLINE;
718 else if( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
719 pCMS->m_p2Lines->nMultiType = MultiPortionType::BIDI;
720 else
721 pCMS->m_p2Lines->nMultiType = MultiPortionType::RUBY;
723 SwTwips nTmpWidth = pPor->Width();
724 if( nSpaceAdd )
725 nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
727 SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
728 Size( nTmpWidth, pPor->Height() ) );
730 if ( ! bRecursion )
731 pCMS->m_p2Lines->aPortion = aRect;
732 else
733 pCMS->m_p2Lines->aPortion2 = aRect;
736 // In a multi-portion we use GetCharRect()-function
737 // recursively and must add the x-position
738 // of the multi-portion.
739 TextFrameIndex const nOldStart = m_nStart;
740 SwTwips nOldY = m_nY;
741 sal_uInt8 nOldProp = GetPropFont();
742 m_nStart = aInf.GetIdx();
743 SwLineLayout* pOldCurr = m_pCurr;
744 m_pCurr = &static_cast<SwMultiPortion*>(pPor)->GetRoot();
745 if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
746 SetPropFont( 50 );
748 SwTextGridItem const*const pGrid(
749 GetGridItem(GetTextFrame()->FindPageFrame()));
750 const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
751 const sal_uInt16 nRubyHeight = bHasGrid ?
752 pGrid->GetRubyHeight() : 0;
754 if( m_nStart + m_pCurr->GetLen() <= nOfst && GetNext() &&
755 ( ! static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
756 static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
758 sal_uInt16 nOffset;
759 // in grid mode we may only add the height of the
760 // ruby line if ruby line is on top
761 if ( bHasGrid &&
762 static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
763 static_cast<SwMultiPortion*>(pPor)->OnTop() )
764 nOffset = nRubyHeight;
765 else
766 nOffset = GetLineHeight();
768 pOrig->Pos().AdjustY(nOffset );
769 Next();
772 const bool bSpaceChg = static_cast<SwMultiPortion*>(pPor)->
773 ChgSpaceAdd( m_pCurr, nSpaceAdd );
774 Point aOldPos = pOrig->Pos();
776 // Ok, for ruby portions in grid mode we have to
777 // temporarily set the inner line height to the
778 // outer line height because that value is needed
779 // for the adjustment inside the recursion
780 const sal_uInt16 nOldRubyHeight = m_pCurr->Height();
781 const sal_uInt16 nOldRubyRealHeight = m_pCurr->GetRealHeight();
782 const bool bChgHeight =
783 static_cast<SwMultiPortion*>(pPor)->IsRuby() && bHasGrid;
785 if ( bChgHeight )
787 m_pCurr->Height( pOldCurr->Height() - nRubyHeight );
788 m_pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
789 nRubyHeight );
792 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
793 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
795 aLayoutModeModifier.Modify(
796 static_cast<SwBidiPortion*>(pPor)->GetLevel() % 2 );
799 GetCharRect_( pOrig, nOfst, pCMS );
801 if ( bChgHeight )
803 m_pCurr->Height( nOldRubyHeight );
804 m_pCurr->SetRealHeight( nOldRubyRealHeight );
807 // if we are still in the first row of
808 // our 2 line multiportion, we use the FirstMulti flag
809 // to indicate this
810 if ( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
812 // the recursion may have damaged our font size
813 SetPropFont( nOldProp );
814 GetInfo().GetFont()->SetProportion( 100 );
816 if ( m_pCurr == &static_cast<SwMultiPortion*>(pPor)->GetRoot() )
818 GetInfo().SetFirstMulti( true );
820 // we want to treat a double line portion like a
821 // single line portion, if there is no text in
822 // the second line
823 if ( !m_pCurr->GetNext() ||
824 !m_pCurr->GetNext()->GetLen() )
825 GetInfo().SetMulti( false );
828 // ruby portions are treated like single line portions
829 else if( static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
830 static_cast<SwMultiPortion*>(pPor)->IsBidi() )
831 GetInfo().SetMulti( false );
833 // calculate cursor values
834 if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
836 GetInfo().SetMulti( false );
837 tools::Long nTmp = pOrig->Width();
838 pOrig->Width( pOrig->Height() );
839 pOrig->Height( nTmp );
840 nTmp = pOrig->Left() - aOldPos.X();
842 // if we travel into our rotated portion from
843 // a line below, we have to take care, that the
844 // y coord in pOrig is less than line height:
845 if ( nTmp )
846 nTmp--;
848 pOrig->Pos().setX( nX + aOldPos.X() );
849 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
850 pOrig->Pos().setY( aOldPos.Y() + nTmp );
851 else
852 pOrig->Pos().setY( aOldPos.Y()
853 + pPor->Height() - nTmp - pOrig->Height() );
854 if ( pCMS && pCMS->m_bRealHeight )
856 pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
857 // result for rotated multi portion is not
858 // correct for reverse (270 degree) portions
859 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
861 if ( SvxParaVertAlignItem::Align::Automatic ==
862 GetLineInfo().GetVertAlign() )
863 // if vertical alignment is set to auto,
864 // we switch from base line alignment
865 // to centered alignment
866 pCMS->m_aRealHeight.setX(
867 ( pOrig->Width() +
868 pCMS->m_aRealHeight.Y() ) / 2 );
869 else
870 pCMS->m_aRealHeight.setX(
871 pOrig->Width() -
872 pCMS->m_aRealHeight.X() +
873 pCMS->m_aRealHeight.Y() );
877 else
879 pOrig->Pos().AdjustY(aOldPos.Y() );
880 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
882 const SwTwips nPorWidth = pPor->Width() +
883 pPor->CalcSpacing( nSpaceAdd, aInf );
884 const SwTwips nInsideOfst = pOrig->Pos().X();
885 pOrig->Pos().setX( nX + nPorWidth -
886 nInsideOfst - pOrig->Width() );
888 else
889 pOrig->Pos().AdjustX(nX );
891 if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
892 pOrig->Pos().AdjustX(
893 static_cast<SwDoubleLinePortion*>(pPor)->PreWidth() );
896 if( bSpaceChg )
897 SwDoubleLinePortion::ResetSpaceAdd( m_pCurr );
899 m_pCurr = pOldCurr;
900 m_nStart = nOldStart;
901 m_nY = nOldY;
902 m_bPrev = false;
904 return;
906 if ( pPor->PrtWidth() )
908 // tdf#30731: To get the correct nOfst width, we need
909 // to send the whole portion string to GetTextSize()
910 // and ask it to return the width of nOfst by calling
911 // SetMeasureLen(). Cutting the string at nOfst can
912 // give the wrong width if nOfst is in e.g. the middle
913 // of a ligature. See SwFntObj::DrawText().
914 TextFrameIndex const nOldLen = pPor->GetLen();
915 aInf.SetLen( pPor->GetLen() );
916 pPor->SetLen( nOfst - aInf.GetIdx() );
917 aInf.SetMeasureLen(pPor->GetLen());
918 if (aInf.GetLen() < aInf.GetMeasureLen())
920 pPor->SetLen(aInf.GetMeasureLen());
921 aInf.SetLen(pPor->GetLen());
923 if( nX || !pPor->InNumberGrp() )
925 SeekAndChg( aInf );
926 const bool bOldOnWin = aInf.OnWin();
927 aInf.SetOnWin( false ); // no BULLETs!
928 SwTwips nTmp = nX;
929 aInf.SetKanaComp( pKanaComp );
930 aInf.SetKanaIdx( nKanaIdx );
931 nX += pPor->GetTextSize( aInf ).Width();
932 aInf.SetOnWin( bOldOnWin );
933 if ( pPor->InSpaceGrp() && nSpaceAdd )
934 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
935 if( bWidth )
937 pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
938 aInf.SetMeasureLen(pPor->GetLen());
939 if (aInf.GetLen() < aInf.GetMeasureLen())
941 pPor->SetLen(aInf.GetMeasureLen());
942 aInf.SetLen(pPor->GetLen());
944 aInf.SetOnWin( false ); // no BULLETs!
945 nTmp += pPor->GetTextSize( aInf ).Width();
946 aInf.SetOnWin( bOldOnWin );
947 if ( pPor->InSpaceGrp() && nSpaceAdd )
948 nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
949 pOrig->Width( nTmp - nX );
952 pPor->SetLen( nOldLen );
954 // Shift the cursor with the right border width
955 // Note: nX remains positive because GetTextSize() also include the width of the right border
956 if( aInf.GetIdx() < nOfst && nOfst < aInf.GetIdx() + pPor->GetLen() )
958 // Find the current drop portion part and use its right border
959 if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
961 SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
962 const SwDropPortionPart* pCurrPart = pDrop->GetPart();
963 TextFrameIndex nSumLength(0);
964 while( pCurrPart && (nSumLength += pCurrPart->GetLen()) < nOfst - aInf.GetIdx() )
966 pCurrPart = pCurrPart->GetFollow();
968 if( pCurrPart && nSumLength != nOfst - aInf.GetIdx() &&
969 pCurrPart->GetFont().GetRightBorder() && !pCurrPart->GetJoinBorderWithNext() )
971 nX -= pCurrPart->GetFont().GetRightBorderSpace();
974 else if( GetInfo().GetFont()->GetRightBorder() && !pPor->GetJoinBorderWithNext())
976 nX -= GetInfo().GetFont()->GetRightBorderSpace();
980 bWidth = false;
981 break;
986 if( pPor )
988 OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
989 bool bEmptyField = false;
990 if( pPor->InFieldGrp() && pPor->GetLen() )
992 SwFieldPortion *pTmp = static_cast<SwFieldPortion*>(pPor);
993 while( pTmp->HasFollow() && pTmp->GetExp().isEmpty() )
995 sal_uInt16 nAddX = pTmp->Width();
996 SwLinePortion *pNext = pTmp->GetNextPortion();
997 while( pNext && !pNext->InFieldGrp() )
999 OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
1000 nAddX = nAddX + pNext->Width();
1001 pNext = pNext->GetNextPortion();
1003 if( !pNext )
1004 break;
1005 pTmp = static_cast<SwFieldPortion*>(pNext);
1006 nPorHeight = pTmp->Height();
1007 nPorAscent = pTmp->GetAscent();
1008 nX += nAddX;
1009 bEmptyField = true;
1012 // 8513: Fields in justified text, skipped
1013 while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
1014 ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
1015 pPor->IsBlankPortion() || pPor->InTabGrp() ||
1016 ( !bEmptyField && pPor->InFieldGrp() ) ) )
1018 if ( pPor->InSpaceGrp() && nSpaceAdd )
1019 nX += pPor->PrtWidth() +
1020 pPor->CalcSpacing( nSpaceAdd, aInf );
1021 else
1023 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
1025 if ( m_pCurr->IsSpaceAdd() )
1027 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1028 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1029 else
1030 nSpaceAdd = 0;
1033 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
1034 ++nKanaIdx;
1036 if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1037 !pPor->GetNextPortion()->IsMarginPortion() ) )
1038 nX += pPor->PrtWidth();
1040 if( pPor->IsMultiPortion() &&
1041 static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1043 if ( m_pCurr->IsSpaceAdd() )
1045 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1046 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1047 else
1048 nSpaceAdd = 0;
1051 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
1052 ++nKanaIdx;
1054 if( !pPor->IsFlyPortion() )
1056 nPorHeight = pPor->Height();
1057 nPorAscent = pPor->GetAscent();
1059 pPor = pPor->GetNextPortion();
1062 if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
1063 pPor->GetNextPortion() && pPor->GetNextPortion()->InFixGrp() )
1065 // All special portions have to be skipped
1066 // Taking the German word "zusammen" as example: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
1067 // Without the adjustment we end up in front of '-', with the
1068 // adjustment in front of the 's'.
1069 while( pPor && !pPor->GetLen() )
1071 nX += pPor->Width();
1072 if( !pPor->IsMarginPortion() )
1074 nPorHeight = pPor->Height();
1075 nPorAscent = pPor->GetAscent();
1077 pPor = pPor->GetNextPortion();
1080 if( pPor && pCMS )
1082 if( pCMS->m_bFieldInfo && pPor->InFieldGrp() && pPor->Width() )
1083 pOrig->Width( pPor->Width() );
1084 if( pPor->IsDropPortion() )
1086 nPorAscent = static_cast<SwDropPortion*>(pPor)->GetDropHeight();
1087 // The drop height is only calculated, if we have more than
1088 // one line. Otherwise it is 0.
1089 if ( ! nPorAscent)
1090 nPorAscent = pPor->Height();
1091 nPorHeight = nPorAscent;
1092 pOrig->Height( nPorHeight +
1093 static_cast<SwDropPortion*>(pPor)->GetDropDescent() );
1094 if( nTmpHeight < pOrig->Height() )
1096 nTmpAscent = nPorAscent;
1097 nTmpHeight = sal_uInt16( pOrig->Height() );
1100 if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
1101 aInf.GetIdx() == nOfst )
1103 if( !pPor->IsFlyPortion() && pPor->Height() &&
1104 pPor->GetAscent() )
1106 nPorHeight = pPor->Height();
1107 nPorAscent = pPor->GetAscent();
1109 SwTwips nTmp;
1110 if (TextFrameIndex(2) > pPor->GetLen())
1112 nTmp = pPor->Width();
1113 if ( pPor->InSpaceGrp() && nSpaceAdd )
1114 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1116 else
1118 const bool bOldOnWin = aInf.OnWin();
1119 TextFrameIndex const nOldLen = pPor->GetLen();
1120 aInf.SetLen( pPor->GetLen() );
1121 pPor->SetLen( TextFrameIndex(1) );
1122 aInf.SetMeasureLen(pPor->GetLen());
1123 if (aInf.GetLen() < aInf.GetMeasureLen())
1125 pPor->SetLen(aInf.GetMeasureLen());
1126 aInf.SetLen(pPor->GetLen());
1128 SeekAndChg( aInf );
1129 aInf.SetOnWin( false ); // no BULLETs!
1130 aInf.SetKanaComp( pKanaComp );
1131 aInf.SetKanaIdx( nKanaIdx );
1132 nTmp = pPor->GetTextSize( aInf ).Width();
1133 aInf.SetOnWin( bOldOnWin );
1134 if ( pPor->InSpaceGrp() && nSpaceAdd )
1135 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1136 pPor->SetLen( nOldLen );
1138 pOrig->Width( nTmp );
1141 // travel inside field portion?
1142 if ( pCMS->m_pSpecialPos )
1144 // apply attributes to font
1145 Seek( nOfst );
1146 lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
1151 // special case: We are at the beginning of a BidiPortion or
1152 // directly behind a BidiPortion
1153 if ( pCMS &&
1154 ( pLastBidiPor ||
1155 ( pPor &&
1156 pPor->IsMultiPortion() &&
1157 static_cast<SwMultiPortion*>(pPor)->IsBidi() ) ) )
1159 // we determine if the cursor has to blink before or behind
1160 // the bidi portion
1161 if ( pLastBidiPor )
1163 const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
1165 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1167 // we came from inside the bidi portion, we want to blink
1168 // behind the portion
1169 pOrig->Pos().AdjustX( -nLastBidiPorWidth );
1171 // Again, there is a special case: logically behind
1172 // the portion can actually mean that the cursor is inside
1173 // the portion. This can happen is the last portion
1174 // inside the bidi portion is a nested bidi portion
1175 SwLineLayout& rLineLayout =
1176 static_cast<SwMultiPortion*>(pLastBidiPor)->GetRoot();
1178 const SwLinePortion *pLast = rLineLayout.FindLastPortion();
1179 if ( pLast->IsMultiPortion() )
1181 OSL_ENSURE( static_cast<const SwMultiPortion*>(pLast)->IsBidi(),
1182 "Non-BidiPortion inside BidiPortion" );
1183 TextFrameIndex const nIdx = aInf.GetIdx();
1184 // correct the index before using CalcSpacing.
1185 aInf.SetIdx(nLastBidiIdx);
1186 pOrig->Pos().AdjustX(pLast->Width() +
1187 pLast->CalcSpacing( nSpaceAdd, aInf ) );
1188 aInf.SetIdx(nIdx);
1192 else
1194 const sal_uInt8 nPortionLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1196 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1198 // we came from inside the bidi portion, we want to blink
1199 // behind the portion
1200 pOrig->Pos().AdjustX(pPor->Width() +
1201 pPor->CalcSpacing( nSpaceAdd, aInf ) );
1206 pOrig->Pos().AdjustX(nX );
1208 if ( pCMS && pCMS->m_bRealHeight )
1210 nTmpAscent = AdjustBaseLine( *m_pCurr, nullptr, nPorHeight, nPorAscent );
1211 if ( nTmpAscent > nPorAscent )
1212 pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
1213 else
1214 pCMS->m_aRealHeight.setX( 0 );
1215 OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
1216 if ( nTmpHeight > nPorHeight )
1217 pCMS->m_aRealHeight.setY( nPorHeight );
1218 else
1219 pCMS->m_aRealHeight.setY( nTmpHeight );
1224 void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
1225 SwCursorMoveState* pCMS, const tools::Long nMax )
1227 CharCursorToLine(nOfst);
1229 // Indicates that a position inside a special portion (field, number portion)
1230 // is requested.
1231 const bool bSpecialPos = pCMS && pCMS->m_pSpecialPos;
1232 TextFrameIndex nFindOfst = nOfst;
1234 if ( bSpecialPos )
1236 const SwSPExtendRange nExtendRange = pCMS->m_pSpecialPos->nExtendRange;
1238 OSL_ENSURE( ! pCMS->m_pSpecialPos->nLineOfst || SwSPExtendRange::BEFORE != nExtendRange,
1239 "LineOffset AND Number Portion?" );
1241 // portions which are behind the string
1242 if ( SwSPExtendRange::BEHIND == nExtendRange )
1243 ++nFindOfst;
1245 // skip lines for fields which cover more than one line
1246 for ( sal_Int32 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
1247 Next();
1250 // If necessary, as catch up, do the adjustment
1251 GetAdjusted();
1253 const Point aCharPos( GetTopLeft() );
1255 GetCharRect_( pOrig, nFindOfst, pCMS );
1257 pOrig->Pos().AdjustX(aCharPos.X() );
1258 pOrig->Pos().AdjustY(aCharPos.Y() );
1260 if( pCMS && pCMS->m_b2Lines && pCMS->m_p2Lines )
1262 pCMS->m_p2Lines->aLine.Pos().AdjustX(aCharPos.X() );
1263 pCMS->m_p2Lines->aLine.Pos().AdjustY(aCharPos.Y() );
1264 pCMS->m_p2Lines->aPortion.Pos().AdjustX(aCharPos.X() );
1265 pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
1268 if( nMax )
1270 if( pOrig->Top() + pOrig->Height() > nMax )
1272 if( pOrig->Top() > nMax )
1273 pOrig->Top( nMax );
1274 pOrig->Height( nMax - pOrig->Top() );
1276 if ( pCMS && pCMS->m_bRealHeight && pCMS->m_aRealHeight.Y() >= 0 )
1278 tools::Long nTmp = pCMS->m_aRealHeight.X() + pOrig->Top();
1279 if( nTmp >= nMax )
1281 pCMS->m_aRealHeight.setX( nMax - pOrig->Top() );
1282 pCMS->m_aRealHeight.setY( 0 );
1284 else if( nTmp + pCMS->m_aRealHeight.Y() > nMax )
1285 pCMS->m_aRealHeight.setY( nMax - nTmp );
1291 * Determines if SwTextCursor::GetModelPositionForViewPoint() should consider the next portion when calculating the
1292 * doc model position from a Point.
1294 static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, SwTwips nWidth30, sal_uInt16 nX)
1296 if (!pPor->GetNextPortion() || pPor->IsBreakPortion())
1298 return false;
1301 // tdf#138592: consider all following zero-width text portions of current text portion,
1302 // like combining characters.
1303 if (nWidth30 == nX && pPor->IsTextPortion() && pPor->GetNextPortion()->IsTextPortion()
1304 && pPor->GetNextPortion()->Width() == 0)
1305 return true;
1307 // If we're past the target position, stop the iteration in general.
1308 // Exception: don't stop the iteration between as-char fly portions and their comments.
1309 if (nWidth30 >= nX && (!pPor->IsFlyCntPortion() || !pPor->GetNextPortion()->IsPostItsPortion()))
1311 // Normally returns false.
1313 // Another exception: If the cursor is at the very end of the portion, and the next portion is a comment,
1314 // then place the cursor after the zero-width comment. This is primarily to benefit the very end of a line.
1315 return nWidth30 == nX && pPor->GetNextPortion()->IsPostItsPortion();
1318 return true;
1321 // Return: Offset in String
1322 TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
1323 bool bChgNode, SwCursorMoveState* pCMS ) const
1325 // If necessary, as catch up, do the adjustment
1326 GetAdjusted();
1328 const OUString &rText = GetInfo().GetText();
1329 TextFrameIndex nOffset(0);
1331 // x is the horizontal offset within the line.
1332 SwTwips x = rPoint.X();
1333 const SwTwips nLeftMargin = GetLineStart();
1334 SwTwips nRightMargin = GetLineEnd() +
1335 ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 );
1336 if( nRightMargin == nLeftMargin )
1337 nRightMargin += 30;
1339 const bool bLeftOver = x < nLeftMargin;
1340 if( bLeftOver )
1341 x = nLeftMargin;
1342 const bool bRightOver = x > nRightMargin;
1343 const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );
1345 // Until here everything in document coordinates.
1346 x -= nLeftMargin;
1348 SwTwips nX = x;
1350 // If there are attribute changes in the line, search for the paragraph,
1351 // in which nX is situated.
1352 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
1353 TextFrameIndex nCurrStart = m_nStart;
1354 bool bHolePortion = false;
1355 bool bLastHyph = false;
1357 std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
1358 TextFrameIndex const nOldIdx = GetInfo().GetIdx();
1359 sal_uInt16 nSpaceIdx = 0;
1360 size_t nKanaIdx = 0;
1361 tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
1362 short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
1364 // nWidth is the width of the line, or the width of
1365 // the paragraph with the font change, in which nX is situated.
1367 SwTwips nWidth = pPor->Width();
1368 if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1370 if ( pPor->InSpaceGrp() && nSpaceAdd )
1372 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1373 nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1375 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1376 ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1379 if ( m_pCurr->IsSpaceAdd() )
1381 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1382 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1383 else
1384 nSpaceAdd = 0;
1387 if( pKanaComp )
1389 if ( nKanaIdx + 1 < pKanaComp->size() )
1390 nKanaComp = (*pKanaComp)[++nKanaIdx];
1391 else
1392 nKanaComp = 0;
1397 SwTwips nWidth30;
1398 if ( pPor->IsPostItsPortion() )
1399 nWidth30 = 0;
1400 else
1401 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1402 30 :
1403 nWidth;
1405 while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
1407 nX = nX - nWidth;
1408 nCurrStart = nCurrStart + pPor->GetLen();
1409 bHolePortion = pPor->IsHolePortion();
1410 pPor = pPor->GetNextPortion();
1411 nWidth = pPor->Width();
1412 if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1414 if ( pPor->InSpaceGrp() && nSpaceAdd )
1416 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1417 nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1420 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1421 ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1424 if ( m_pCurr->IsSpaceAdd() )
1426 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1427 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1428 else
1429 nSpaceAdd = 0;
1432 if ( pKanaComp )
1434 if( nKanaIdx + 1 < pKanaComp->size() )
1435 nKanaComp = (*pKanaComp)[++nKanaIdx];
1436 else
1437 nKanaComp = 0;
1442 if ( pPor->IsPostItsPortion() )
1443 nWidth30 = 0;
1444 else
1445 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1446 30 :
1447 nWidth;
1448 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1449 bLastHyph = pPor->InHyphGrp();
1452 const bool bLastPortion = (nullptr == pPor->GetNextPortion());
1454 if( nX==nWidth )
1456 SwLinePortion *pNextPor = pPor->GetNextPortion();
1457 while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
1459 nCurrStart = nCurrStart + pPor->GetLen();
1460 pPor = pNextPor;
1461 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1462 bLastHyph = pPor->InHyphGrp();
1463 pNextPor = pPor->GetNextPortion();
1467 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nOldIdx );
1469 TextFrameIndex nLength = pPor->GetLen();
1471 const bool bFieldInfo = pCMS && pCMS->m_bFieldInfo;
1473 if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
1474 ( pPor->InNumberGrp() && !pPor->IsFootnoteNumPortion() ) ||
1475 ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
1476 pCMS->m_bPosCorr = true;
1478 // #i27615#
1479 if (pCMS && pCMS->m_bInFrontOfLabel)
1481 if (2 * nX >= nWidth || !pPor->InNumberGrp() || pPor->IsFootnoteNumPortion())
1482 pCMS->m_bInFrontOfLabel = false;
1485 // 7684: We are exactly ended up at their HyphPortion. It is our task to
1486 // provide, that we end up in the String.
1487 // 7993: If length = 0, then we must exit...
1488 if( !nLength )
1490 if( pCMS )
1492 if( pPor->IsFlyPortion() && bFieldInfo )
1493 pCMS->m_bPosCorr = true;
1495 if (!bRightOver && nX)
1497 if( pPor->IsFootnoteNumPortion())
1498 pCMS->m_bFootnoteNoInfo = true;
1499 else if (pPor->InNumberGrp() ) // #i23726#
1501 pCMS->m_nInNumPortionOffset = nX;
1502 pCMS->m_bInNumPortion = true;
1506 if( !nCurrStart )
1507 return TextFrameIndex(0);
1509 // 7849, 7816: pPor->GetHyphPortion is mandatory!
1510 if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
1511 ( pPor->IsMarginPortion() && !pPor->GetNextPortion() &&
1512 // 46598: Consider the situation: We might end up behind the last character,
1513 // in the last line of a centered paragraph
1514 nCurrStart < TextFrameIndex(rText.getLength())))
1515 --nCurrStart;
1516 else if( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->IsFollow()
1517 && nWidth > nX )
1519 if( bFieldInfo )
1520 --nCurrStart;
1521 else
1523 sal_uInt16 nHeight = pPor->Height();
1524 if ( !nHeight || nHeight > nWidth )
1525 nHeight = nWidth;
1526 if( bChgNode && nWidth - nHeight/2 > nX )
1527 --nCurrStart;
1530 return nCurrStart;
1532 if (TextFrameIndex(1) == nLength || pPor->InFieldGrp())
1534 if ( nWidth )
1536 // no quick return for as-character frames, we want to peek inside
1537 if (!(bChgNode && pPos && pPor->IsFlyCntPortion())
1538 // if we want to get the position inside the field, we should not return
1539 && (!pCMS || !pCMS->m_pSpecialPos))
1541 if ( pPor->InFieldGrp() ||
1542 ( pPor->IsMultiPortion() &&
1543 static_cast<SwMultiPortion*>(pPor)->IsBidi() ) )
1545 sal_uInt16 nHeight = 0;
1546 if( !bFieldInfo )
1548 nHeight = pPor->Height();
1549 if ( !nHeight || nHeight > nWidth )
1550 nHeight = nWidth;
1553 if( nWidth - nHeight/2 <= nX &&
1554 ( ! pPor->InFieldGrp() ||
1555 !static_cast<SwFieldPortion*>(pPor)->HasFollow() ) )
1557 if (pPor->InFieldGrp())
1559 nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
1561 else
1563 ++nCurrStart;
1567 else if ( ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1568 !pPor->GetNextPortion()->IsMarginPortion() &&
1569 !pPor->GetNextPortion()->IsHolePortion() ) )
1570 && ( nWidth/2 < nX ) &&
1571 ( !bFieldInfo ||
1572 ( pPor->GetNextPortion() &&
1573 pPor->GetNextPortion()->IsPostItsPortion() ) )
1574 && ( bRightAllowed || !bLastHyph ))
1575 ++nCurrStart;
1577 return nCurrStart;
1580 else
1582 if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
1583 pPor->InToxRefGrp() )
1585 SwPostItsPortion* pPostItsPortion = pPor->IsPostItsPortion() ? dynamic_cast<SwPostItsPortion*>(pPor) : nullptr;
1586 if (pPostItsPortion)
1588 if (!pPostItsPortion->IsScript()) // tdf#141079
1590 // Offset would be nCurrStart + nLength below, do the same for post-it portions.
1591 nCurrStart += pPor->GetLen();
1594 return nCurrStart;
1596 if ( pPor->InFieldGrp() )
1598 if( bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow() )
1600 nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
1602 return nCurrStart;
1607 // Skip space at the end of the line
1608 if( bLastPortion && (m_pCurr->GetNext() || m_pFrame->GetFollow() )
1609 && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ' )
1610 --nLength;
1612 if( nWidth > nX ||
1613 ( nWidth == nX && pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsDouble() ) )
1615 if( pPor->IsMultiPortion() )
1617 // In a multi-portion we use GetModelPositionForViewPoint()-function recursively
1618 SwTwips nTmpY = rPoint.Y() - m_pCurr->GetAscent() + pPor->GetAscent();
1619 // if we are in the first line of a double line portion, we have
1620 // to add a value to nTmpY for not staying in this line
1621 // we also want to skip the first line, if we are inside ruby
1622 if ( ( static_cast<SwTextSizeInfo*>(m_pInf)->IsMulti() &&
1623 static_cast<SwTextSizeInfo*>(m_pInf)->IsFirstMulti() ) ||
1624 ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1625 static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
1626 nTmpY += static_cast<SwMultiPortion*>(pPor)->Height();
1628 // Important for cursor traveling in ruby portions:
1629 // We have to set nTmpY to 0 in order to stay in the first row
1630 // if the phonetic line is the second row
1631 if ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1632 ! static_cast<SwMultiPortion*>(pPor)->OnTop() )
1633 nTmpY = 0;
1635 SwTextCursorSave aSave( const_cast<SwTextCursor*>(this), static_cast<SwMultiPortion*>(pPor),
1636 nTmpY, nX, nCurrStart, nSpaceAdd );
1638 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1639 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
1641 const sal_uInt8 nBidiLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1642 aLayoutModeModifier.Modify( nBidiLevel % 2 );
1645 if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
1647 nTmpY -= m_nY;
1648 if( !static_cast<SwMultiPortion*>(pPor)->IsRevers() )
1649 nTmpY = pPor->Height() - nTmpY;
1650 if( nTmpY < 0 )
1651 nTmpY = 0;
1652 nX = o3tl::narrowing<sal_uInt16>(nTmpY);
1655 if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
1657 const sal_uInt16 nPreWidth = static_cast<SwDoubleLinePortion*>(pPor)->PreWidth();
1658 if ( nX > nPreWidth )
1659 nX = nX - nPreWidth;
1660 else
1661 nX = 0;
1664 return GetModelPositionForViewPoint( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
1665 bChgNode, pCMS );
1667 if( pPor->InTextGrp() || pPor->IsHolePortion() )
1669 sal_uInt8 nOldProp;
1670 if( GetPropFont() )
1672 const_cast<SwFont*>(GetFnt())->SetProportion( GetPropFont() );
1673 nOldProp = GetFnt()->GetPropr();
1675 else
1676 nOldProp = 0;
1678 SwTextSizeInfo aSizeInf( GetInfo(), &rText, nCurrStart );
1679 const_cast<SwTextCursor*>(this)->SeekAndChg( aSizeInf );
1680 SwTextSlot aDiffText( &aSizeInf, pPor, false, false );
1681 SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
1682 static_cast<SwDropPortion*>(pPor)->GetFnt() : nullptr );
1684 SwParaPortion* pPara = const_cast<SwParaPortion*>(GetInfo().GetParaPortion());
1685 OSL_ENSURE( pPara, "No paragraph!" );
1687 // protect against bugs elsewhere
1688 SAL_WARN_IF( aSizeInf.GetIdx().get() + pPor->GetLen().get() > aSizeInf.GetText().getLength(), "sw", "portion and text are out of sync" );
1689 TextFrameIndex nSafeLen( std::min(pPor->GetLen().get(), aSizeInf.GetText().getLength() - aSizeInf.GetIdx().get()) );
1691 SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
1692 *aSizeInf.GetOut(),
1693 &pPara->GetScriptInfo(),
1694 aSizeInf.GetText(),
1695 aSizeInf.GetIdx(),
1696 nSafeLen );
1698 // Drop portion works like a multi portion, just its parts are not portions
1699 if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
1701 SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
1702 const SwDropPortionPart* pCurrPart = pDrop->GetPart();
1703 sal_uInt16 nSumWidth = 0;
1704 sal_uInt16 nSumBorderWidth = 0;
1705 // Shift offset with the right and left border of previous parts and left border of actual one
1706 while (pCurrPart && nSumWidth <= nX - sal_Int32(nCurrStart))
1708 nSumWidth += pCurrPart->GetWidth();
1709 if( pCurrPart->GetFont().GetLeftBorder() && !pCurrPart->GetJoinBorderWithPrev() )
1711 nSumBorderWidth += pCurrPart->GetFont().GetLeftBorderSpace();
1713 if (nSumWidth <= nX - sal_Int32(nCurrStart) && pCurrPart->GetFont().GetRightBorder() &&
1714 !pCurrPart->GetJoinBorderWithNext() )
1716 nSumBorderWidth += pCurrPart->GetFont().GetRightBorderSpace();
1718 pCurrPart = pCurrPart->GetFollow();
1720 nX = std::max(static_cast<SwTwips>(0), nX - nSumBorderWidth);
1722 // Shift the offset with the left border width
1723 else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() )
1725 nX = std::max(static_cast<SwTwips>(0), nX - GetInfo().GetFont()->GetLeftBorderSpace());
1728 aDrawInf.SetOffset( nX );
1730 if ( nSpaceAdd )
1732 TextFrameIndex nCharCnt(0);
1733 // #i41860# Thai justified alignment needs some
1734 // additional information:
1735 aDrawInf.SetNumberOfBlanks( pPor->InTextGrp() ?
1736 static_cast<const SwTextPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
1737 TextFrameIndex(0) );
1740 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1741 aDrawInf.SetLen( TextFrameIndex(COMPLETE_STRING) );
1743 aDrawInf.SetSpace( nSpaceAdd );
1744 aDrawInf.SetFont( aSizeInf.GetFont() );
1745 aDrawInf.SetFrame( m_pFrame );
1746 aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
1747 aDrawInf.SetPosMatchesBounds( pCMS && pCMS->m_bPosMatchesBounds );
1749 if ( SwFontScript::CJK == aSizeInf.GetFont()->GetActual() &&
1750 pPara->GetScriptInfo().CountCompChg() &&
1751 ! pPor->InFieldGrp() )
1752 aDrawInf.SetKanaComp( nKanaComp );
1754 nLength = aSizeInf.GetFont()->GetModelPositionForViewPoint_( aDrawInf );
1756 // get position inside field portion?
1757 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1759 pCMS->m_pSpecialPos->nCharOfst = sal_Int32(nLength);
1760 nLength = TextFrameIndex(0);
1763 // set cursor bidi level
1764 if ( pCMS )
1765 pCMS->m_nCursorBidiLevel =
1766 aDrawInf.GetCursorBidiLevel();
1768 if( bFieldInfo && nLength == pPor->GetLen() &&
1769 ( ! pPor->GetNextPortion() ||
1770 ! pPor->GetNextPortion()->IsPostItsPortion() ) )
1771 --nLength;
1773 if( nOldProp )
1774 const_cast<SwFont*>(GetFnt())->SetProportion( nOldProp );
1776 else
1778 sw::FlyContentPortion* pFlyPor(nullptr);
1779 if(bChgNode && pPos && (pFlyPor = dynamic_cast<sw::FlyContentPortion*>(pPor)))
1781 // JP 24.11.94: if the Position is not in Fly, then
1782 // we many not return with COMPLETE_STRING as value!
1783 // (BugId: 9692 + Change in feshview)
1784 SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
1785 SwFrame* pLower = pTmp->GetLower();
1786 // Allow non-text-frames to get SwGrfNode for as-char anchored images into pPos
1787 // instead of the closest SwTextNode, to be consistent with at-char behavior.
1788 bool bChgNodeInner = pLower
1789 && (pLower->IsTextFrame() || pLower->IsLayoutFrame() || pLower->IsNoTextFrame());
1790 Point aTmpPoint( rPoint );
1792 if ( m_pFrame->IsRightToLeft() )
1793 m_pFrame->SwitchLTRtoRTL( aTmpPoint );
1795 if ( m_pFrame->IsVertical() )
1796 m_pFrame->SwitchHorizontalToVertical( aTmpPoint );
1798 if( bChgNodeInner && pTmp->getFrameArea().Contains( aTmpPoint ) &&
1799 !( pTmp->IsProtected() ) )
1801 pFlyPor->GetFlyCursorOfst(aTmpPoint, *pPos, pCMS);
1802 // After a change of the frame, our font must be still
1803 // available for/in the OutputDevice.
1804 // For comparison: Paint and new SwFlyCntPortion !
1805 static_cast<SwTextSizeInfo*>(m_pInf)->SelectFont();
1807 // 6776: The pIter->GetModelPositionForViewPoint is returning here
1808 // from a nesting with COMPLETE_STRING.
1809 return TextFrameIndex(COMPLETE_STRING);
1812 else
1813 nLength = pPor->GetModelPositionForViewPoint( nX );
1816 nOffset = nCurrStart + nLength;
1818 // 7684: We end up in front of the HyphPortion. We must assure
1819 // that we end up in the string.
1820 // If we are at end of line in front of FlyFrames, we must proceed the same way.
1821 if( nOffset && pPor->GetLen() == nLength && pPor->GetNextPortion() &&
1822 !pPor->GetNextPortion()->GetLen() && pPor->GetNextPortion()->InHyphGrp() )
1823 --nOffset;
1825 return nOffset;
1828 /** Looks for text portions which are inside the given rectangle
1830 For a rectangular text selection every text portions which is inside the given
1831 rectangle has to be put into the SwSelectionList as SwPaM
1832 From these SwPaM the SwCursors will be created.
1834 @param rSelList
1835 The container for the overlapped text portions
1837 @param rRect
1838 A rectangle in document coordinates, text inside this rectangle has to be
1839 selected.
1841 @return [ true, false ]
1842 true if any overlapping text portion has been found and put into list
1843 false if no portion overlaps, the list has been unchanged
1845 bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
1847 bool bRet = false;
1848 // GetPaintArea() instead getFrameArea() for negative indents
1849 SwRect aTmpFrame( GetPaintArea() );
1850 if( !rRect.Overlaps( aTmpFrame ) )
1851 return false;
1852 if( rSelList.checkContext( this ) )
1854 SwRect aRect( aTmpFrame );
1855 aRect.Intersection( rRect );
1856 SwPosition aPosL( MapViewToModelPos(TextFrameIndex(0)) );
1857 if( IsEmpty() )
1859 SwPaM *pPam = new SwPaM( aPosL, aPosL );
1860 rSelList.insertPaM( pPam );
1862 else if( aRect.HasArea() )
1864 SwPosition aOld(aPosL.GetNodes().GetEndOfContent());
1865 SwPosition aPosR( aPosL );
1866 Point aPoint;
1867 SwTextInfo aInf( const_cast<SwTextFrame*>(this) );
1868 SwTextIter aLine( const_cast<SwTextFrame*>(this), &aInf );
1869 // We have to care for top-to-bottom layout, where right becomes top etc.
1870 SwRectFnSet aRectFnSet(this);
1871 SwTwips nTop = aRectFnSet.GetTop(aRect);
1872 SwTwips nBottom = aRectFnSet.GetBottom(aRect);
1873 SwTwips nLeft = aRectFnSet.GetLeft(aRect);
1874 SwTwips nRight = aRectFnSet.GetRight(aRect);
1875 SwTwips nY = aLine.Y(); // Top position of the first line
1876 SwTwips nLastY = nY;
1877 while( nY < nTop && aLine.Next() ) // line above rectangle
1879 nLastY = nY;
1880 nY = aLine.Y();
1882 bool bLastLine = false;
1883 if( nY < nTop && !aLine.GetNext() )
1885 bLastLine = true;
1886 nY += aLine.GetLineHeight();
1888 do // check the lines for overlapping
1890 if( nLastY < nTop ) // if the last line was above rectangle
1891 nLastY = nTop;
1892 if( nY > nBottom ) // if the current line leaves the rectangle
1893 nY = nBottom;
1894 if( nY >= nLastY ) // gotcha: overlapping
1896 nLastY += nY;
1897 nLastY /= 2;
1898 if( aRectFnSet.IsVert() )
1900 aPoint.setX( nLastY );
1901 aPoint.setY( nLeft );
1903 else
1905 aPoint.setX( nLeft );
1906 aPoint.setY( nLastY );
1908 // Looking for the position of the left border of the rectangle
1909 // in this text line
1910 SwCursorMoveState aState( CursorMoveState::UpDown );
1911 if( GetModelPositionForViewPoint( &aPosL, aPoint, &aState ) )
1913 if( aRectFnSet.IsVert() )
1915 aPoint.setX( nLastY );
1916 aPoint.setY( nRight );
1918 else
1920 aPoint.setX( nRight );
1921 aPoint.setY( nLastY );
1923 // If we get a right position and if the left position
1924 // is not the same like the left position of the line before
1925 // which could happen e.g. for field portions or fly frames
1926 // a SwPaM will be inserted with these positions
1927 if( GetModelPositionForViewPoint( &aPosR, aPoint, &aState ) &&
1928 aOld != aPosL)
1930 SwPaM *pPam = new SwPaM( aPosL, aPosR );
1931 rSelList.insertPaM( pPam );
1932 aOld = aPosL;
1936 if( aLine.Next() )
1938 nLastY = nY;
1939 nY = aLine.Y();
1941 else if( !bLastLine )
1943 bLastLine = true;
1944 nLastY = nY;
1945 nY += aLine.GetLineHeight();
1947 else
1948 break;
1949 }while( nLastY < nBottom );
1952 if( GetDrawObjs() )
1954 const SwSortedObjs &rObjs = *GetDrawObjs();
1955 for (SwAnchoredObject* pAnchoredObj : rObjs)
1957 const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
1958 if( !pFly )
1959 continue;
1960 if( pFly->IsFlyInContentFrame() && pFly->FillSelection( rSelList, rRect ) )
1961 bRet = true;
1964 return bRet;
1967 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */