sc: factor out some more code
[LibreOffice.git] / sw / source / core / text / itrcrsr.cxx
blobddb54f8eb0d69b36e1a3a3022f258bc4c6583b9f
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 assert(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 += 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 auto stMetrics = GetFnt()->GetFontUnitMetrics();
166 SvxFirstLineIndentItem const& rFirstLine(pNode->GetSwAttrSet().GetFirstLineIndent());
167 SvxTextLeftMarginItem const& rTextLeftMargin(pNode->GetSwAttrSet().GetTextLeftMargin());
168 SvxRightMarginItem const& rRightMargin(pNode->GetSwAttrSet().GetRightMargin());
170 // #i95907#
171 // #i111284#
172 const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
173 const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
174 const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable() != ::sw::ListLevelIndents::No;
175 const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;
177 // Carefully adjust the text formatting ranges.
179 // This whole area desperately needs some rework. There are
180 // quite a couple of values that need to be considered:
181 // 1. paragraph indent
182 // 2. paragraph first line indent
183 // 3. numbering indent
184 // 4. numbering spacing to text
185 // 5. paragraph border
186 // Note: These values have already been used during calculation
187 // of the printing area of the paragraph.
188 const int nLMWithNum = pNode->GetLeftMarginWithNum( true );
189 if ( m_pFrame->IsRightToLeft() )
191 // this calculation is identical this the calculation for L2R layout - see below
192 mnLeft = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + nLMWithNum
193 - pNode->GetLeftMarginWithNum() -
194 // #i95907#
195 // #i111284#
196 // rSpace.GetLeft() + rSpace.GetTextLeft();
197 (rTextLeftMargin.ResolveLeft(rFirstLine, stMetrics)
198 - rTextLeftMargin.ResolveTextLeft(stMetrics));
200 else
202 // #i95907#
203 // #i111284#
204 if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
205 !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
207 // this calculation is identical this the calculation for R2L layout - see above
208 mnLeft = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left()
209 + nLMWithNum - pNode->GetLeftMarginWithNum() -
210 // #i95907#
211 // #i111284#
212 (rTextLeftMargin.ResolveLeft(rFirstLine, stMetrics)
213 - rTextLeftMargin.ResolveTextLeft(stMetrics));
215 else
217 mnLeft
218 = m_pFrame->getFrameArea().Left()
219 + std::max(tools::Long(rTextLeftMargin.ResolveTextLeft(stMetrics) + nLMWithNum),
220 m_pFrame->getFramePrintArea().Left());
224 mnRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();
226 // tdf#163913: Apply font-relative adjustment to the margins
227 mnLeft += rTextLeftMargin.ResolveLeftVariablePart(rFirstLine, stMetrics);
228 mnRight -= rRightMargin.ResolveRightVariablePart(stMetrics);
230 if (mnLeft >= mnRight &&
231 // #i53066# Omit adjustment of nLeft for numbered
232 // paras inside cells inside new documents:
233 (pNode->getIDocumentSettingAccess()->get(
234 DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING)
235 || !m_pFrame->IsInTab()
236 || (bListLevelIndentsApplicable
237 && nLMWithNum == rTextLeftMargin.ResolveTextLeft(stMetrics))
238 || (!bLabelAlignmentActive && nLMWithNum == 0)))
240 mnLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
241 if( mnLeft >= mnRight ) // e.g. with large paragraph indentations in slim table columns
242 mnRight = mnLeft + 1; // einen goennen wir uns immer
245 if( m_pFrame->IsFollow() && m_pFrame->GetOffset() )
246 mnFirst = mnLeft;
247 else
249 short nFLOfst = 0;
250 tools::Long nFirstLineOfs = 0;
251 if (!pNode->GetFirstLineOfsWithNum(nFLOfst, stMetrics) && rFirstLine.IsAutoFirst())
253 nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
254 LanguageType const aLang = m_pFrame->GetLangOfChar(
255 TextFrameIndex(0), css::i18n::ScriptType::ASIAN);
256 if (aLang != LANGUAGE_KOREAN && aLang != LANGUAGE_JAPANESE)
257 nFirstLineOfs<<=1;
259 // tdf#129448: Auto first-line indent should not be effected by line space.
260 // Below is for compatibility with old documents.
261 if (!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE))
263 const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
264 if( pSpace )
266 switch( pSpace->GetLineSpaceRule() )
268 case SvxLineSpaceRule::Auto:
269 break;
270 case SvxLineSpaceRule::Min:
272 if( nFirstLineOfs < pSpace->GetLineHeight() )
273 nFirstLineOfs = pSpace->GetLineHeight();
274 break;
276 case SvxLineSpaceRule::Fix:
277 nFirstLineOfs = pSpace->GetLineHeight();
278 break;
279 default: OSL_FAIL( ": unknown LineSpaceRule" );
281 switch( pSpace->GetInterLineSpaceRule() )
283 case SvxInterLineSpaceRule::Off:
284 break;
285 case SvxInterLineSpaceRule::Prop:
287 tools::Long nTmp = pSpace->GetPropLineSpace();
288 // 50% is the minimum, at 0% we switch to
289 // the default value 100%...
290 if( nTmp < 50 )
291 nTmp = nTmp ? 50 : 100;
293 nTmp *= nFirstLineOfs;
294 nTmp /= 100;
295 if( !nTmp )
296 ++nTmp;
297 nFirstLineOfs = nTmp;
298 break;
300 case SvxInterLineSpaceRule::Fix:
302 nFirstLineOfs += pSpace->GetInterLineSpace();
303 break;
305 default: OSL_FAIL( ": unknown InterLineSpaceRule" );
310 else
311 nFirstLineOfs = nFLOfst;
313 // #i95907#
314 // #i111284#
315 if ( m_pFrame->IsRightToLeft() ||
316 bListLevelIndentsApplicableAndLabelAlignmentActive ||
317 !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
319 if ( nFirstLineOfs < 0 && m_pFrame->IsInTab() &&
320 mnLeft == m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left() &&
321 !m_pFrame->IsRightToLeft() &&
322 !bListLevelIndentsApplicableAndLabelAlignmentActive )
324 // tdf#130218 always show hanging indent in narrow table cells
325 // to avoid hiding the text content of the first line
326 mnLeft -= nFirstLineOfs;
329 mnFirst = mnLeft + nFirstLineOfs;
331 else
333 mnFirst = m_pFrame->getFrameArea().Left()
334 + std::max(rTextLeftMargin.ResolveTextLeft(stMetrics) + nLMWithNum
335 + nFirstLineOfs,
336 m_pFrame->getFramePrintArea().Left());
339 // Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
340 // value for the new list label position and space mode LABEL_ALIGNMENT
341 // and label alignment CENTER and RIGHT in L2R layout respectively
342 // label alignment LEFT and CENTER in R2L layout
343 mnFirst += m_pFrame->GetAdditionalFirstLineOffset();
345 if( mnFirst >= mnRight )
346 mnFirst = mnRight - 1;
348 const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
349 mnAdjust = rAdjust.GetAdjust();
351 // left is left and right is right
352 if ( m_pFrame->IsRightToLeft() )
354 if ( SvxAdjust::Left == mnAdjust )
355 mnAdjust = SvxAdjust::Right;
356 else if ( SvxAdjust::Right == mnAdjust )
357 mnAdjust = SvxAdjust::Left;
360 m_bOneBlock = rAdjust.GetOneWord() == SvxAdjust::Block;
361 m_bLastBlock = rAdjust.GetLastBlock() == SvxAdjust::Block;
362 m_bLastCenter = rAdjust.GetLastBlock() == SvxAdjust::Center;
364 // #i91133#
365 mnTabLeft = pNode->GetLeftMarginForTabCalculation();
367 DropInit();
370 void SwTextMargin::DropInit()
372 mnDropLeft = mnDropLines = mnDropHeight = mnDropDescent = 0;
373 const SwParaPortion *pPara = GetInfo().GetParaPortion();
374 if( pPara )
376 const SwDropPortion *pPorDrop = pPara->FindDropPortion();
377 if ( pPorDrop )
379 mnDropLeft = pPorDrop->GetDropLeft();
380 mnDropLines = pPorDrop->GetLines();
381 mnDropHeight = pPorDrop->GetDropHeight();
382 mnDropDescent = pPorDrop->GetDropDescent();
387 // The function is interpreting / observing / evaluating / keeping / respecting the first line indention and the specified width.
388 SwTwips SwTextMargin::GetLineStart() const
390 SwTwips nRet = GetLeftMargin();
391 if( GetAdjust() != SvxAdjust::Left &&
392 !m_pCurr->GetFirstPortion()->IsMarginPortion() )
394 // If the first portion is a Margin, then the
395 // adjustment is expressed by the portions.
396 if( GetAdjust() == SvxAdjust::Right )
397 nRet = Right() - CurrWidth();
398 else if( GetAdjust() == SvxAdjust::Center )
399 nRet += (GetLineWidth() - CurrWidth()) / 2;
401 return nRet;
404 void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
406 CtorInitTextMargin( pNewFrame, pNewInf );
407 // 6096: Attention, the iterators are derived!
408 // GetInfo().SetOut( GetInfo().GetWin() );
411 static bool isTrailingDecoration(SwLinePortion* p)
413 // Optional no-width portion, followed only by no-width portions and/or terminating portions?
414 for (; p; p = p->GetNextPortion())
416 if (p->IsMarginPortion() || p->IsBreakPortion())
417 return true;
418 if (p->Width())
419 return false;
421 return true; // no more portions
424 // tdf#120715 tdf#43100: Make width for some HolePortions, so cursor will be able to move into it.
425 // It should not change the layout, so this should be called after the layout is calculated.
426 void SwTextCursor::AddExtraBlankWidth()
428 SwLinePortion* pPos = m_pCurr->GetNextPortion();
429 while (pPos)
431 SwLinePortion* pNextPos = pPos->GetNextPortion();
432 // Do it only if it is the last portion that able to handle the cursor,
433 // else the next portion would miscalculate the cursor position
434 if (pPos->ExtraBlankWidth() && isTrailingDecoration(pNextPos))
436 pPos->Width(pPos->Width() + pPos->ExtraBlankWidth());
437 pPos->ExtraBlankWidth(0);
439 pPos = pNextPos;
443 // 1170: Ancient bug: Shift-End forgets the last character ...
444 void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
445 SwCursorMoveState* pCMS, const tools::Long nMax )
447 // 1170: Ambiguity of document positions
448 s_bRightMargin = true;
449 CharCursorToLine(nOfst);
451 // Somehow twisted: nOfst names the position behind the last
452 // character of the last line == This is the position in front of the first character
453 // of the line, in which we are situated:
454 if( nOfst != GetStart() || !m_pCurr->GetLen() )
456 // 8810: Master line RightMargin, after that LeftMargin
457 GetCharRect( pOrig, nOfst, pCMS, nMax );
458 s_bRightMargin = nOfst >= GetEnd() && nOfst < TextFrameIndex(GetInfo().GetText().getLength());
459 return;
462 if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
464 GetCharRect( pOrig, nOfst, pCMS, nMax );
465 return;
468 // If necessary, as catch up, do the adjustment
469 GetAdjusted();
471 tools::Long nX = 0;
472 tools::Long nLast = 0;
473 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
475 SwTwips nTmpHeight, nTmpAscent;
476 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
477 sal_uInt16 nPorHeight = nTmpHeight;
478 sal_uInt16 nPorAscent = nTmpAscent;
480 // Search for the last Text/EndPortion of the line
481 while( pPor )
483 nX += pPor->Width();
484 if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
485 && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
487 nLast = nX;
488 nPorHeight = pPor->Height();
489 nPorAscent = pPor->GetAscent();
491 pPor = pPor->GetNextPortion();
494 const Size aCharSize( 1, nTmpHeight );
495 pOrig->Pos( GetTopLeft() );
496 pOrig->SSize( aCharSize );
497 pOrig->Pos().AdjustX(nLast );
498 const SwTwips nTmpRight = Right() - 1;
499 if( pOrig->Left() > nTmpRight )
500 pOrig->Pos().setX( nTmpRight );
502 if ( pCMS && pCMS->m_bRealHeight )
504 if ( nTmpAscent > nPorAscent )
505 pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
506 else
507 pCMS->m_aRealHeight.setX( 0 );
508 OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
509 pCMS->m_aRealHeight.setY( nPorHeight );
513 // internal function, called by SwTextCursor::GetCharRect() to calculate
514 // the relative character position in the current line.
515 // pOrig refers to x and y coordinates, width and height of the cursor
516 // pCMS is used for restricting the cursor, if there are different font
517 // heights in one line ( first value = offset to y of pOrig, second
518 // value = real height of (shortened) cursor
519 void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
520 SwCursorMoveState* pCMS )
522 const OUString aText = GetInfo().GetText();
523 SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
524 if( GetPropFont() )
525 aInf.GetFont()->SetProportion( GetPropFont() );
526 SwTwips nTmpAscent, nTmpHeight; // Line height
527 CalcAscentAndHeight( nTmpAscent, nTmpHeight );
528 const Size aCharSize( 1, nTmpHeight );
529 const Point aCharPos;
530 pOrig->Pos( aCharPos );
531 pOrig->SSize( aCharSize );
533 // If we are looking for a position inside a field which covers
534 // more than one line we may not skip any "empty portions" at the
535 // beginning of a line
536 const bool bInsideFirstField = pCMS && pCMS->m_pSpecialPos &&
537 ( pCMS->m_pSpecialPos->nLineOfst ||
538 SwSPExtendRange::BEFORE ==
539 pCMS->m_pSpecialPos->nExtendRange );
541 bool bWidth = pCMS && pCMS->m_bRealWidth;
542 if( !m_pCurr->GetLen() && !m_pCurr->Width() )
544 if ( pCMS && pCMS->m_bRealHeight )
546 pCMS->m_aRealHeight.setX( 0 );
547 pCMS->m_aRealHeight.setY( nTmpHeight );
550 else
552 SwTwips nPorHeight = nTmpHeight;
553 SwTwips nPorAscent = nTmpAscent;
554 SwTwips nX = 0;
555 SwTwips nTmpFirst = 0;
556 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
557 SwBidiPortion* pLastBidiPor = nullptr;
558 TextFrameIndex nLastBidiIdx(-1);
559 SwTwips nLastBidiPorWidth = 0;
560 std::deque<sal_uInt16>* pKanaComp = m_pCurr->GetpKanaComp();
561 sal_uInt16 nSpaceIdx = 0;
562 size_t nKanaIdx = 0;
563 tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
565 bool bNoText = true;
567 // First all portions without Len at beginning of line are skipped.
568 // Exceptions are the mean special portions from WhichFirstPortion:
569 // Num, ErgoSum, FootnoteNum, FieldRests
570 // 8477: but also the only Textportion of an empty line with
571 // Right/Center-Adjustment! So not just pPor->GetExpandPortion() ...
572 while( pPor && !pPor->GetLen() && ! bInsideFirstField )
574 nX += pPor->Width();
575 if ( pPor->InSpaceGrp() && nSpaceAdd )
576 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
577 if( bNoText )
578 nTmpFirst = nX;
579 // 8670: EndPortions count once as TextPortions.
580 // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
581 if( pPor->InTextGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
583 bNoText = false;
584 nTmpFirst = nX;
586 if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
588 if ( m_pCurr->IsSpaceAdd() )
590 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
591 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
592 else
593 nSpaceAdd = 0;
596 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
597 ++nKanaIdx;
599 if( pPor->InFixMargGrp() )
601 if( pPor->IsMarginPortion() )
602 bNoText = false;
603 else
605 // fix margin portion => next SpaceAdd, KanaComp value
606 if ( m_pCurr->IsSpaceAdd() )
608 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
609 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
610 else
611 nSpaceAdd = 0;
614 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
615 ++nKanaIdx;
618 pPor = pPor->GetNextPortion();
621 if( !pPor )
623 // There's just Spezialportions.
624 nX = nTmpFirst;
626 else
628 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
629 (!pPor->InFieldGrp() || pPor->GetAscent() ) )
631 nPorHeight = pPor->Height();
632 nPorAscent = pPor->GetAscent();
634 while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
635 ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
637 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
638 (!pPor->InFieldGrp() || pPor->GetAscent() ) )
640 nPorHeight = pPor->Height();
641 nPorAscent = pPor->GetAscent();
644 // If we are behind the portion, we add the portion width to
645 // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
646 // For common portions (including BidiPortions) we want to add
647 // the portion width to nX. For MultiPortions, nExtra = 0,
648 // therefore we go to the 'else' branch and start a recursion.
649 const TextFrameIndex nExtra( (pPor->IsMultiPortion()
650 && !static_cast<SwMultiPortion*>(pPor)->IsBidi()
651 && !bWidth)
652 ? 0 : 1 );
653 if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
655 if ( pPor->InSpaceGrp() && nSpaceAdd )
656 // tdf#163042 In the case of shrunk lines with a single portion,
657 // adjust the line width to show the cursor in the correct position
658 nX += ( ( std::abs( m_pCurr->Width() - pPor->PrtWidth() ) <= 1 &&
659 m_pCurr->ExtraShrunkWidth() > 0 )
660 ? m_pCurr->ExtraShrunkWidth()
661 : pPor->PrtWidth() ) +
662 pPor->CalcSpacing( nSpaceAdd, aInf );
663 else
665 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
667 // update to current SpaceAdd, KanaComp values
668 if ( m_pCurr->IsSpaceAdd() )
670 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
671 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
672 else
673 nSpaceAdd = 0;
676 if ( pKanaComp &&
677 ( nKanaIdx + 1 ) < pKanaComp->size()
679 ++nKanaIdx;
681 if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
682 !pPor->GetNextPortion()->IsMarginPortion() ) )
683 nX += pPor->PrtWidth();
685 if( pPor->IsMultiPortion() )
687 if ( static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
689 if ( m_pCurr->IsSpaceAdd() )
691 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
692 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
693 else
694 nSpaceAdd = 0;
697 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
698 ++nKanaIdx;
701 // if we are right behind a BidiPortion, we have to
702 // hold a pointer to the BidiPortion in order to
703 // find the correct cursor position, depending on the
704 // cursor level
705 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() &&
706 aInf.GetIdx() + pPor->GetLen() == nOfst )
708 pLastBidiPor = static_cast<SwBidiPortion*>(pPor);
709 nLastBidiIdx = aInf.GetIdx();
710 nLastBidiPorWidth = pLastBidiPor->Width() +
711 pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );
715 aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
716 pPor = pPor->GetNextPortion();
718 else
720 if( pPor->IsMultiPortion() )
722 GetInfo().SetMulti( true );
723 pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );
725 if( pCMS && pCMS->m_b2Lines )
727 const bool bRecursion (pCMS->m_p2Lines);
728 if ( !bRecursion )
730 pCMS->m_p2Lines.reset(new Sw2LinesPos);
731 pCMS->m_p2Lines->aLine = SwRect(aCharPos, aCharSize);
734 if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
736 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
737 pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_270;
738 else
739 pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_90;
741 else if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
742 pCMS->m_p2Lines->nMultiType = MultiPortionType::TWOLINE;
743 else if( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
744 pCMS->m_p2Lines->nMultiType = MultiPortionType::BIDI;
745 else
746 pCMS->m_p2Lines->nMultiType = MultiPortionType::RUBY;
748 SwTwips nTmpWidth = pPor->Width();
749 if( nSpaceAdd )
750 nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
752 SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
753 Size( nTmpWidth, pPor->Height() ) );
755 if ( ! bRecursion )
756 pCMS->m_p2Lines->aPortion = aRect;
757 else
758 pCMS->m_p2Lines->aPortion2 = aRect;
761 // In a multi-portion we use GetCharRect()-function
762 // recursively and must add the x-position
763 // of the multi-portion.
764 TextFrameIndex const nOldStart = m_nStart;
765 SwTwips nOldY = m_nY;
766 sal_uInt8 nOldProp = GetPropFont();
767 m_nStart = aInf.GetIdx();
768 SwLineLayout* pOldCurr = m_pCurr;
769 m_pCurr = &static_cast<SwMultiPortion*>(pPor)->GetRoot();
770 if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
771 SetPropFont( 50 );
773 SwTextGridItem const*const pGrid(
774 GetGridItem(GetTextFrame()->FindPageFrame()));
775 const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
776 const sal_uInt16 nRubyHeight = bHasGrid ?
777 pGrid->GetRubyHeight() : 0;
779 if( m_nStart + m_pCurr->GetLen() <= nOfst && GetNext() &&
780 ( ! static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
781 static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
783 sal_uInt16 nOffset;
784 // in grid mode we may only add the height of the
785 // ruby line if ruby line is on top
786 if ( bHasGrid &&
787 static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
788 static_cast<SwMultiPortion*>(pPor)->OnTop() )
789 nOffset = nRubyHeight;
790 else
791 nOffset = GetLineHeight();
793 pOrig->Pos().AdjustY(nOffset );
794 Next();
797 const bool bSpaceChg = static_cast<SwMultiPortion*>(pPor)->
798 ChgSpaceAdd( m_pCurr, nSpaceAdd );
799 Point aOldPos = pOrig->Pos();
801 // Ok, for ruby portions in grid mode we have to
802 // temporarily set the inner line height to the
803 // outer line height because that value is needed
804 // for the adjustment inside the recursion
805 const sal_uInt16 nOldRubyHeight = m_pCurr->Height();
806 const sal_uInt16 nOldRubyRealHeight = m_pCurr->GetRealHeight();
807 const bool bChgHeight =
808 static_cast<SwMultiPortion*>(pPor)->IsRuby() && bHasGrid;
810 if ( bChgHeight )
812 m_pCurr->Height( pOldCurr->Height() - nRubyHeight );
813 m_pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
814 nRubyHeight );
817 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
818 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
820 aLayoutModeModifier.Modify(
821 static_cast<SwBidiPortion*>(pPor)->GetLevel() % 2 );
824 GetCharRect_( pOrig, nOfst, pCMS );
826 if ( bChgHeight )
828 m_pCurr->Height( nOldRubyHeight );
829 m_pCurr->SetRealHeight( nOldRubyRealHeight );
832 // if we are still in the first row of
833 // our 2 line multiportion, we use the FirstMulti flag
834 // to indicate this
835 if ( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
837 // the recursion may have damaged our font size
838 SetPropFont( nOldProp );
839 GetInfo().GetFont()->SetProportion( 100 );
841 if ( m_pCurr == &static_cast<SwMultiPortion*>(pPor)->GetRoot() )
843 GetInfo().SetFirstMulti( true );
845 // we want to treat a double line portion like a
846 // single line portion, if there is no text in
847 // the second line
848 if ( !m_pCurr->GetNext() ||
849 !m_pCurr->GetNext()->GetLen() )
850 GetInfo().SetMulti( false );
853 // ruby portions are treated like single line portions
854 else if( static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
855 static_cast<SwMultiPortion*>(pPor)->IsBidi() )
856 GetInfo().SetMulti( false );
858 // calculate cursor values
859 if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
861 GetInfo().SetMulti( false );
862 tools::Long nTmp = pOrig->Width();
863 pOrig->Width( pOrig->Height() );
864 pOrig->Height( nTmp );
865 nTmp = pOrig->Left() - aOldPos.X();
867 // if we travel into our rotated portion from
868 // a line below, we have to take care, that the
869 // y coord in pOrig is less than line height:
870 if ( nTmp )
871 nTmp--;
873 pOrig->Pos().setX( nX + aOldPos.X() );
874 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
875 pOrig->Pos().setY( aOldPos.Y() + nTmp );
876 else
877 pOrig->Pos().setY( aOldPos.Y()
878 + pPor->Height() - nTmp - pOrig->Height() );
879 if ( pCMS && pCMS->m_bRealHeight )
881 pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
882 // result for rotated multi portion is not
883 // correct for reverse (270 degree) portions
884 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
886 if ( SvxParaVertAlignItem::Align::Automatic ==
887 GetLineInfo().GetVertAlign() )
888 // if vertical alignment is set to auto,
889 // we switch from base line alignment
890 // to centered alignment
891 pCMS->m_aRealHeight.setX(
892 ( pOrig->Width() +
893 pCMS->m_aRealHeight.Y() ) / 2 );
894 else
895 pCMS->m_aRealHeight.setX(
896 pOrig->Width() -
897 pCMS->m_aRealHeight.X() +
898 pCMS->m_aRealHeight.Y() );
902 else
904 pOrig->Pos().AdjustY(aOldPos.Y() );
905 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
907 const SwTwips nPorWidth = pPor->Width() +
908 pPor->CalcSpacing( nSpaceAdd, aInf );
909 const SwTwips nInsideOfst = pOrig->Pos().X();
910 pOrig->Pos().setX( nX + nPorWidth -
911 nInsideOfst - pOrig->Width() );
913 else
914 pOrig->Pos().AdjustX(nX );
916 if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
917 pOrig->Pos().AdjustX(
918 static_cast<SwDoubleLinePortion*>(pPor)->PreWidth() );
921 if( bSpaceChg )
922 SwDoubleLinePortion::ResetSpaceAdd( m_pCurr );
924 m_pCurr = pOldCurr;
925 m_nStart = nOldStart;
926 m_nY = nOldY;
927 m_bPrev = false;
929 return;
931 if ( pPor->PrtWidth() )
933 // tdf#30731: To get the correct nOfst width, we need
934 // to send the whole portion string to GetTextSize()
935 // and ask it to return the width of nOfst by calling
936 // SetMeasureLen(). Cutting the string at nOfst can
937 // give the wrong width if nOfst is in e.g. the middle
938 // of a ligature. See SwFntObj::DrawText().
939 TextFrameIndex const nOldLen = pPor->GetLen();
940 TextFrameIndex nMaxLen = TextFrameIndex(aInf.GetText().getLength()) - aInf.GetIdx();
941 aInf.SetLen( std::min(nMaxLen, pPor->GetLen()) );
942 pPor->SetLen( nOfst - aInf.GetIdx() );
943 aInf.SetMeasureLen(pPor->GetLen());
944 if (aInf.GetLen() < aInf.GetMeasureLen())
946 pPor->SetLen(aInf.GetMeasureLen());
947 aInf.SetLen(pPor->GetLen());
949 if( nX || !pPor->InNumberGrp() )
951 SeekAndChg( aInf );
952 const bool bOldOnWin = aInf.OnWin();
953 aInf.SetOnWin( false ); // no BULLETs!
954 SwTwips nTmp = nX;
955 aInf.SetKanaComp( pKanaComp );
956 aInf.SetKanaIdx( nKanaIdx );
957 nX += pPor->GetTextSize( aInf ).Width();
958 aInf.SetOnWin( bOldOnWin );
959 if ( pPor->InSpaceGrp() && nSpaceAdd )
960 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
961 if( bWidth )
963 pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
964 aInf.SetMeasureLen(pPor->GetLen());
965 if (aInf.GetLen() < aInf.GetMeasureLen())
967 pPor->SetLen(aInf.GetMeasureLen());
968 aInf.SetLen(pPor->GetLen());
970 aInf.SetOnWin( false ); // no BULLETs!
971 nTmp += pPor->GetTextSize( aInf ).Width();
972 aInf.SetOnWin( bOldOnWin );
973 if ( pPor->InSpaceGrp() && nSpaceAdd )
974 nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
975 pOrig->Width( nTmp - nX );
978 pPor->SetLen( nOldLen );
980 // Shift the cursor with the right border width
981 // Note: nX remains positive because GetTextSize() also include the width of the right border
982 if( aInf.GetIdx() < nOfst && nOfst < aInf.GetIdx() + pPor->GetLen() )
984 // Find the current drop portion part and use its right border
985 if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
987 SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
988 const SwDropPortionPart* pCurrPart = pDrop->GetPart();
989 TextFrameIndex nSumLength(0);
990 while( pCurrPart && (nSumLength += pCurrPart->GetLen()) < nOfst - aInf.GetIdx() )
992 pCurrPart = pCurrPart->GetFollow();
994 if( pCurrPart && nSumLength != nOfst - aInf.GetIdx() &&
995 pCurrPart->GetFont().GetRightBorder() && !pCurrPart->GetJoinBorderWithNext() )
997 nX -= pCurrPart->GetFont().GetRightBorderSpace();
1000 else if( GetInfo().GetFont()->GetRightBorder() && !pPor->GetJoinBorderWithNext())
1002 nX -= GetInfo().GetFont()->GetRightBorderSpace();
1006 bWidth = false;
1007 break;
1012 if( pPor )
1014 OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
1015 bool bEmptyField = false;
1016 if( pPor->InFieldGrp() && pPor->GetLen() )
1018 SwFieldPortion *pTmp = static_cast<SwFieldPortion*>(pPor);
1019 while( pTmp->HasFollow() && pTmp->GetExp().isEmpty() )
1021 sal_uInt16 nAddX = pTmp->Width();
1022 SwLinePortion *pNext = pTmp->GetNextPortion();
1023 while( pNext && !pNext->InFieldGrp() )
1025 OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
1026 nAddX += pNext->Width();
1027 pNext = pNext->GetNextPortion();
1029 if( !pNext )
1030 break;
1031 pTmp = static_cast<SwFieldPortion*>(pNext);
1032 nPorHeight = pTmp->Height();
1033 nPorAscent = pTmp->GetAscent();
1034 nX += nAddX;
1035 bEmptyField = true;
1038 // 8513: Fields in justified text, skipped
1039 while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
1040 ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
1041 pPor->IsBlankPortion() || pPor->InTabGrp() ||
1042 ( !bEmptyField && pPor->InFieldGrp() ) ) )
1044 if ( pPor->InSpaceGrp() && nSpaceAdd )
1045 nX += pPor->PrtWidth() +
1046 pPor->CalcSpacing( nSpaceAdd, aInf );
1047 else
1049 if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
1051 if ( m_pCurr->IsSpaceAdd() )
1053 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1054 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1055 else
1056 nSpaceAdd = 0;
1059 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
1060 ++nKanaIdx;
1062 if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1063 !pPor->GetNextPortion()->IsMarginPortion() ) )
1064 nX += pPor->PrtWidth();
1066 if( pPor->IsMultiPortion() &&
1067 static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1069 if ( m_pCurr->IsSpaceAdd() )
1071 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1072 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1073 else
1074 nSpaceAdd = 0;
1077 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
1078 ++nKanaIdx;
1080 if( !pPor->IsFlyPortion() )
1082 nPorHeight = pPor->Height();
1083 nPorAscent = pPor->GetAscent();
1085 pPor = pPor->GetNextPortion();
1088 if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
1089 pPor->GetNextPortion() && pPor->GetNextPortion()->InFixGrp() )
1091 // All special portions have to be skipped
1092 // Taking the German word "zusammen" as example: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
1093 // Without the adjustment we end up in front of '-', with the
1094 // adjustment in front of the 's'.
1095 while( pPor && !pPor->GetLen() )
1097 nX += pPor->Width();
1098 if( !pPor->IsMarginPortion() )
1100 nPorHeight = pPor->Height();
1101 nPorAscent = pPor->GetAscent();
1103 pPor = pPor->GetNextPortion();
1106 if( pPor && pCMS )
1108 if( pCMS->m_bFieldInfo && pPor->InFieldGrp() && pPor->Width() )
1109 pOrig->Width( pPor->Width() );
1110 if( pPor->IsDropPortion() )
1112 nPorAscent = static_cast<SwDropPortion*>(pPor)->GetDropHeight();
1113 // The drop height is only calculated, if we have more than
1114 // one line. Otherwise it is 0.
1115 if ( ! nPorAscent)
1116 nPorAscent = pPor->Height();
1117 nPorHeight = nPorAscent;
1118 pOrig->Height( nPorHeight +
1119 static_cast<SwDropPortion*>(pPor)->GetDropDescent() );
1120 if( nTmpHeight < pOrig->Height() )
1122 nTmpAscent = nPorAscent;
1123 nTmpHeight = sal_uInt16( pOrig->Height() );
1126 if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
1127 aInf.GetIdx() == nOfst )
1129 if( !pPor->IsFlyPortion() && pPor->Height() &&
1130 pPor->GetAscent() )
1132 nPorHeight = pPor->Height();
1133 nPorAscent = pPor->GetAscent();
1135 SwTwips nTmp;
1136 if (TextFrameIndex(2) > pPor->GetLen())
1138 nTmp = pPor->Width();
1139 if ( pPor->InSpaceGrp() && nSpaceAdd )
1140 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1142 else
1144 const bool bOldOnWin = aInf.OnWin();
1145 TextFrameIndex const nOldLen = pPor->GetLen();
1146 aInf.SetLen( pPor->GetLen() );
1147 pPor->SetLen( TextFrameIndex(1) );
1148 aInf.SetMeasureLen(pPor->GetLen());
1149 if (aInf.GetLen() < aInf.GetMeasureLen())
1151 pPor->SetLen(aInf.GetMeasureLen());
1152 aInf.SetLen(pPor->GetLen());
1154 SeekAndChg( aInf );
1155 aInf.SetOnWin( false ); // no BULLETs!
1156 aInf.SetKanaComp( pKanaComp );
1157 aInf.SetKanaIdx( nKanaIdx );
1158 nTmp = pPor->GetTextSize( aInf ).Width();
1159 aInf.SetOnWin( bOldOnWin );
1160 if ( pPor->InSpaceGrp() && nSpaceAdd )
1161 nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1162 pPor->SetLen( nOldLen );
1164 pOrig->Width( nTmp );
1167 // travel inside field portion?
1168 if ( pCMS->m_pSpecialPos )
1170 // apply attributes to font
1171 Seek( nOfst );
1172 lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
1177 // special case: We are at the beginning of a BidiPortion or
1178 // directly behind a BidiPortion
1179 if ( pCMS &&
1180 ( pLastBidiPor ||
1181 ( pPor &&
1182 pPor->IsMultiPortion() &&
1183 static_cast<SwMultiPortion*>(pPor)->IsBidi() ) ) )
1185 // we determine if the cursor has to blink before or behind
1186 // the bidi portion
1187 if ( pLastBidiPor )
1189 const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
1191 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1193 // we came from inside the bidi portion, we want to blink
1194 // behind the portion
1195 pOrig->Pos().AdjustX( -nLastBidiPorWidth );
1197 // Again, there is a special case: logically behind
1198 // the portion can actually mean that the cursor is inside
1199 // the portion. This can happen is the last portion
1200 // inside the bidi portion is a nested bidi portion
1201 SwLineLayout& rLineLayout =
1202 static_cast<SwMultiPortion*>(pLastBidiPor)->GetRoot();
1204 const SwLinePortion *pLast = rLineLayout.FindLastPortion();
1205 if ( pLast->IsMultiPortion() )
1207 OSL_ENSURE( static_cast<const SwMultiPortion*>(pLast)->IsBidi(),
1208 "Non-BidiPortion inside BidiPortion" );
1209 TextFrameIndex const nIdx = aInf.GetIdx();
1210 // correct the index before using CalcSpacing.
1211 aInf.SetIdx(nLastBidiIdx);
1212 pOrig->Pos().AdjustX(pLast->Width() +
1213 pLast->CalcSpacing( nSpaceAdd, aInf ) );
1214 aInf.SetIdx(nIdx);
1218 else
1220 const sal_uInt8 nPortionLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1222 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1224 // we came from inside the bidi portion, we want to blink
1225 // behind the portion
1226 pOrig->Pos().AdjustX(pPor->Width() +
1227 pPor->CalcSpacing( nSpaceAdd, aInf ) );
1232 pOrig->Pos().AdjustX(nX );
1234 if ( pCMS && pCMS->m_bRealHeight )
1236 nTmpAscent = AdjustBaseLine( *m_pCurr, nullptr, nPorHeight, nPorAscent );
1237 if ( nTmpAscent > nPorAscent )
1238 pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
1239 else
1240 pCMS->m_aRealHeight.setX( 0 );
1241 OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
1242 if ( nTmpHeight > nPorHeight )
1243 pCMS->m_aRealHeight.setY( nPorHeight );
1244 else
1245 pCMS->m_aRealHeight.setY( nTmpHeight );
1250 void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
1251 SwCursorMoveState* pCMS, const tools::Long nMax )
1253 CharCursorToLine(nOfst);
1255 // Indicates that a position inside a special portion (field, number portion)
1256 // is requested.
1257 const bool bSpecialPos = pCMS && pCMS->m_pSpecialPos;
1258 TextFrameIndex nFindOfst = nOfst;
1260 if ( bSpecialPos )
1262 const SwSPExtendRange nExtendRange = pCMS->m_pSpecialPos->nExtendRange;
1264 OSL_ENSURE( ! pCMS->m_pSpecialPos->nLineOfst || SwSPExtendRange::BEFORE != nExtendRange,
1265 "LineOffset AND Number Portion?" );
1267 // portions which are behind the string
1268 if ( SwSPExtendRange::BEHIND == nExtendRange )
1269 ++nFindOfst;
1271 // skip lines for fields which cover more than one line
1272 for ( sal_Int32 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
1273 Next();
1276 // If necessary, as catch up, do the adjustment
1277 GetAdjusted();
1278 AddExtraBlankWidth();
1280 const Point aCharPos( GetTopLeft() );
1282 GetCharRect_( pOrig, nFindOfst, pCMS );
1284 pOrig->Pos().AdjustX(aCharPos.X() );
1285 pOrig->Pos().AdjustY(aCharPos.Y() );
1287 if( pCMS && pCMS->m_b2Lines && pCMS->m_p2Lines )
1289 pCMS->m_p2Lines->aLine.Pos().AdjustX(aCharPos.X() );
1290 pCMS->m_p2Lines->aLine.Pos().AdjustY(aCharPos.Y() );
1291 pCMS->m_p2Lines->aPortion.Pos().AdjustX(aCharPos.X() );
1292 pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
1295 if( nMax )
1297 if( pOrig->Top() + pOrig->Height() > nMax )
1299 if( pOrig->Top() > nMax )
1300 pOrig->Top( nMax );
1301 pOrig->Height( nMax - pOrig->Top() );
1303 if ( pCMS && pCMS->m_bRealHeight && pCMS->m_aRealHeight.Y() >= 0 )
1305 tools::Long nTmp = pCMS->m_aRealHeight.X() + pOrig->Top();
1306 if( nTmp >= nMax )
1308 pCMS->m_aRealHeight.setX( nMax - pOrig->Top() );
1309 pCMS->m_aRealHeight.setY( 0 );
1311 else if( nTmp + pCMS->m_aRealHeight.Y() > nMax )
1312 pCMS->m_aRealHeight.setY( nMax - nTmp );
1318 * Determines if SwTextCursor::GetModelPositionForViewPoint() should consider the next portion when calculating the
1319 * doc model position from a Point.
1321 static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, SwTwips nWidth30, sal_uInt16 nX)
1323 if (!pPor->GetNextPortion() || pPor->IsBreakPortion())
1325 return false;
1328 // tdf#138592: consider all following zero-width text portions of current text portion,
1329 // like combining characters.
1330 if (nWidth30 == nX && pPor->IsTextPortion() && pPor->GetNextPortion()->IsTextPortion()
1331 && pPor->GetNextPortion()->Width() == 0)
1332 return true;
1334 // If we're past the target position, stop the iteration in general.
1335 // Exception: don't stop the iteration between as-char fly portions and their comments.
1336 if (nWidth30 >= nX && (!pPor->IsFlyCntPortion() || !pPor->GetNextPortion()->IsPostItsPortion()))
1338 // Normally returns false.
1340 // Another exception: If the cursor is at the very end of the portion, and the next portion is a comment,
1341 // then place the cursor after the zero-width comment. This is primarily to benefit the very end of a line.
1342 return nWidth30 == nX && pPor->GetNextPortion()->IsPostItsPortion();
1345 return true;
1348 static auto SearchLine(SwLineLayout const*const pLineOfFoundPor,
1349 SwLinePortion const*const pFoundPor,
1350 int & rLines, std::vector<SwFieldPortion const*> & rPortions,
1351 SwLineLayout const*const pLine) -> bool
1353 for (SwLinePortion const* pLP = pLine; pLP; pLP = pLP->GetNextPortion())
1355 if (pLP == pFoundPor)
1357 return true;
1359 if (pLP->InFieldGrp())
1361 SwFieldPortion const* pField(static_cast<SwFieldPortion const*>(pLP));
1362 if (!pField->IsFollow())
1364 rLines = 0;
1365 rPortions.clear();
1367 if (pLine == pLineOfFoundPor)
1369 rPortions.emplace_back(pField);
1372 else if (pLP->IsMultiPortion())
1374 SwMultiPortion const*const pMulti(static_cast<SwMultiPortion const*>(pLP));
1375 for (SwLineLayout const* pMLine = &pMulti->GetRoot();
1376 pMLine; pMLine = pMLine->GetNext())
1378 if (SearchLine(pLineOfFoundPor, pFoundPor, rLines, rPortions, pMLine))
1380 return true;
1385 return (pLine == pLineOfFoundPor);
1388 // Return: Offset in String
1389 TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
1390 bool bChgNode, SwCursorMoveState* pCMS ) const
1392 // If necessary, as catch up, do the adjustment
1393 GetAdjusted();
1395 const OUString &rText = GetInfo().GetText();
1396 TextFrameIndex nOffset(0);
1398 // x is the horizontal offset within the line.
1399 SwTwips x = rPoint.X();
1400 const SwTwips nLeftMargin = GetLineStart();
1401 SwTwips nRightMargin = GetLineEnd() +
1402 ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 );
1403 if( nRightMargin == nLeftMargin )
1404 nRightMargin += 30;
1406 const bool bLeftOver = x < nLeftMargin;
1407 if( bLeftOver )
1408 x = nLeftMargin;
1409 const bool bRightOver = x > nRightMargin;
1410 const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );
1412 // Until here everything in document coordinates.
1413 x -= nLeftMargin;
1415 SwTwips nX = x;
1417 // If there are attribute changes in the line, search for the paragraph,
1418 // in which nX is situated.
1419 SwLinePortion *pPor = m_pCurr->GetFirstPortion();
1420 TextFrameIndex nCurrStart = m_nStart;
1421 bool bLastHyph = false;
1423 std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
1424 TextFrameIndex const nOldIdx = GetInfo().GetIdx();
1425 sal_uInt16 nSpaceIdx = 0;
1426 size_t nKanaIdx = 0;
1427 tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
1428 short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
1430 // nWidth is the width of the line, or the width of
1431 // the paragraph with the font change, in which nX is situated.
1432 // tdf#16342 In the case of shrunk lines with a single portion,
1433 // adjust the line width to move the cursor to the click position
1434 SwTwips nWidth =
1435 ( std::abs( m_pCurr->Width() - pPor->Width() ) <= 1 && m_pCurr->ExtraShrunkWidth() > 0 )
1436 ? m_pCurr->ExtraShrunkWidth()
1437 : pPor->Width();
1438 if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1440 if ( pPor->InSpaceGrp() && nSpaceAdd )
1442 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1443 nWidth += pPor->CalcSpacing( nSpaceAdd, GetInfo() );
1445 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1446 ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1449 if ( m_pCurr->IsSpaceAdd() )
1451 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1452 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1453 else
1454 nSpaceAdd = 0;
1457 if( pKanaComp )
1459 if ( nKanaIdx + 1 < pKanaComp->size() )
1460 nKanaComp = (*pKanaComp)[++nKanaIdx];
1461 else
1462 nKanaComp = 0;
1467 SwTwips nWidth30;
1468 if ( pPor->IsPostItsPortion() )
1469 nWidth30 = 0;
1470 else
1471 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1472 30 :
1473 nWidth;
1475 while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
1477 nX -= nWidth;
1478 nCurrStart += pPor->GetLen();
1479 pPor = pPor->GetNextPortion();
1480 nWidth = pPor->Width();
1481 if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1483 if ( pPor->InSpaceGrp() && nSpaceAdd )
1485 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1486 nWidth += pPor->CalcSpacing( nSpaceAdd, GetInfo() );
1489 if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1490 ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1493 if ( m_pCurr->IsSpaceAdd() )
1495 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1496 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1497 else
1498 nSpaceAdd = 0;
1501 if ( pKanaComp )
1503 if( nKanaIdx + 1 < pKanaComp->size() )
1504 nKanaComp = (*pKanaComp)[++nKanaIdx];
1505 else
1506 nKanaComp = 0;
1511 if ( pPor->IsPostItsPortion() )
1512 nWidth30 = 0;
1513 else
1514 nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1515 30 :
1516 nWidth;
1517 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1518 bLastHyph = pPor->InHyphGrp();
1521 const bool bLastPortion = (nullptr == pPor->GetNextPortion());
1523 if( nX==nWidth )
1525 SwLinePortion *pNextPor = pPor->GetNextPortion();
1526 while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
1528 nCurrStart += pPor->GetLen();
1529 pPor = pNextPor;
1530 if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1531 bLastHyph = pPor->InHyphGrp();
1532 pNextPor = pPor->GetNextPortion();
1536 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nOldIdx );
1538 TextFrameIndex nLength = pPor->GetLen();
1540 const bool bFieldInfo = pCMS && pCMS->m_bFieldInfo;
1542 if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
1543 ( pPor->InNumberGrp() && !pPor->IsFootnoteNumPortion() ) ||
1544 ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
1545 pCMS->m_bPosCorr = true;
1547 // #i27615#
1548 if (pCMS && pCMS->m_bInFrontOfLabel)
1550 if (2 * nX >= nWidth || !pPor->InNumberGrp() || pPor->IsFootnoteNumPortion())
1551 pCMS->m_bInFrontOfLabel = false;
1554 // 7684: We are exactly ended up at their HyphPortion. It is our task to
1555 // provide, that we end up in the String.
1556 // 7993: If length = 0, then we must exit...
1557 if( !nLength )
1559 if( pCMS )
1561 if( pPor->IsFlyPortion() && bFieldInfo )
1562 pCMS->m_bPosCorr = true;
1564 if (!bRightOver && nX)
1566 if( pPor->IsFootnoteNumPortion())
1567 pCMS->m_bFootnoteNoInfo = true;
1568 else if (pPor->InNumberGrp() ) // #i23726#
1570 pCMS->m_nInNumPortionOffset = nX;
1571 pCMS->m_bInNumPortion = true;
1575 if( !nCurrStart )
1576 return TextFrameIndex(0);
1578 // 7849, 7816: pPor->GetHyphPortion is mandatory!
1579 if( ( !bRightAllowed && bLastHyph ) ||
1580 ( pPor->IsMarginPortion() && !pPor->GetNextPortion() &&
1581 // 46598: Consider the situation: We might end up behind the last character,
1582 // in the last line of a centered paragraph
1583 nCurrStart < TextFrameIndex(rText.getLength())))
1584 --nCurrStart;
1585 else if( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->IsFollow()
1586 && nWidth > nX )
1588 if( bFieldInfo )
1589 --nCurrStart;
1590 else
1592 sal_uInt16 nHeight = pPor->Height();
1593 if ( !nHeight || nHeight > nWidth )
1594 nHeight = nWidth;
1595 if( bChgNode && nWidth - nHeight/2 > nX )
1596 --nCurrStart;
1599 if (!pPor->InFieldGrp() || !static_cast<SwFieldPortion const*>(pPor)->IsFollow()
1600 || !pCMS || !pCMS->m_pSpecialPos)
1602 return nCurrStart;
1605 if (TextFrameIndex(1) == nLength || pPor->InFieldGrp())
1607 if ( nWidth )
1609 // no quick return for as-character frames, we want to peek inside
1610 if (!(bChgNode && pPos && pPor->IsFlyCntPortion())
1611 // if we want to get the position inside the field, we should not return
1612 && (!pCMS || !pCMS->m_pSpecialPos))
1614 if ( pPor->InFieldGrp() ||
1615 ( pPor->IsMultiPortion() &&
1616 static_cast<SwMultiPortion*>(pPor)->IsBidi() ) )
1618 sal_uInt16 nHeight = 0;
1619 if( !bFieldInfo )
1621 nHeight = pPor->Height();
1622 if ( !nHeight || nHeight > nWidth )
1623 nHeight = nWidth;
1626 if( nWidth - nHeight/2 <= nX &&
1627 ( ! pPor->InFieldGrp() ||
1628 !static_cast<SwFieldPortion*>(pPor)->HasFollow() ) )
1630 if (pPor->InFieldGrp())
1632 nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
1634 else
1636 ++nCurrStart;
1640 else if ( ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1641 !pPor->GetNextPortion()->IsMarginPortion() &&
1642 !pPor->GetNextPortion()->IsHolePortion() ) )
1643 && ( nWidth/2 < nX ) &&
1644 ( !bFieldInfo ||
1645 ( pPor->GetNextPortion() &&
1646 pPor->GetNextPortion()->IsPostItsPortion() ) )
1647 && ( bRightAllowed || !bLastHyph ))
1648 ++nCurrStart;
1650 return nCurrStart;
1653 else
1655 if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
1656 pPor->InToxRefGrp() )
1658 SwPostItsPortion* pPostItsPortion = pPor->IsPostItsPortion() ? dynamic_cast<SwPostItsPortion*>(pPor) : nullptr;
1659 if (pPostItsPortion)
1661 if (!pPostItsPortion->IsScript()) // tdf#141079
1663 // Offset would be nCurrStart + nLength below, do the same for post-it portions.
1664 nCurrStart += pPor->GetLen();
1667 return nCurrStart;
1669 if ( pPor->InFieldGrp() )
1671 if( bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow() )
1673 nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
1675 return nCurrStart;
1680 // Skip space at the end of the line
1681 if( bLastPortion && (m_pCurr->GetNext() || m_pFrame->GetFollow() )
1682 && sal_Int32(nLength) != 0
1683 && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ')
1685 --nLength;
1688 if( nWidth > nX ||
1689 ( nWidth == nX && pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsDouble() ) )
1691 if( pPor->IsMultiPortion() )
1693 // In a multi-portion we use GetModelPositionForViewPoint()-function recursively
1694 SwTwips nTmpY = rPoint.Y() - m_pCurr->GetAscent() + pPor->GetAscent();
1695 // if we are in the first line of a double line portion, we have
1696 // to add a value to nTmpY for not staying in this line
1697 // we also want to skip the first line, if we are inside ruby
1698 if ( ( static_cast<SwTextSizeInfo*>(m_pInf)->IsMulti() &&
1699 static_cast<SwTextSizeInfo*>(m_pInf)->IsFirstMulti() ) ||
1700 ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1701 static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
1702 nTmpY += static_cast<SwMultiPortion*>(pPor)->Height();
1704 // Important for cursor traveling in ruby portions:
1705 // We have to set nTmpY to 0 in order to stay in the first row
1706 // if the phonetic line is the second row
1707 if ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1708 ! static_cast<SwMultiPortion*>(pPor)->OnTop() )
1709 nTmpY = 0;
1711 SwTextCursorSave aSave( const_cast<SwTextCursor*>(this), static_cast<SwMultiPortion*>(pPor),
1712 nTmpY, nX, nCurrStart, nSpaceAdd );
1714 SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1715 if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
1717 const sal_uInt8 nBidiLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1718 aLayoutModeModifier.Modify( nBidiLevel % 2 );
1721 if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
1723 nTmpY -= m_nY;
1724 if( !static_cast<SwMultiPortion*>(pPor)->IsRevers() )
1725 nTmpY = pPor->Height() - nTmpY;
1726 if( nTmpY < 0 )
1727 nTmpY = 0;
1728 nX = o3tl::narrowing<sal_uInt16>(nTmpY);
1731 if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
1733 const sal_uInt16 nPreWidth = static_cast<SwDoubleLinePortion*>(pPor)->PreWidth();
1734 if ( nX > nPreWidth )
1735 nX -= nPreWidth;
1736 else
1737 nX = 0;
1740 return GetModelPositionForViewPoint( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
1741 bChgNode, pCMS );
1743 if( pPor->InTextGrp() || pPor->IsHolePortion() )
1745 sal_uInt8 nOldProp;
1746 if( GetPropFont() )
1748 const_cast<SwFont*>(GetFnt())->SetProportion( GetPropFont() );
1749 nOldProp = GetFnt()->GetPropr();
1751 else
1752 nOldProp = 0;
1754 SwTextSizeInfo aSizeInf( GetInfo(), &rText, nCurrStart );
1755 const_cast<SwTextCursor*>(this)->SeekAndChg( aSizeInf );
1756 SwTextSlot aDiffText( &aSizeInf, pPor, false, false );
1757 SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
1758 static_cast<SwDropPortion*>(pPor)->GetFnt() : nullptr );
1760 SwParaPortion* pPara = const_cast<SwParaPortion*>(GetInfo().GetParaPortion());
1761 OSL_ENSURE( pPara, "No paragraph!" );
1763 // protect against bugs elsewhere
1764 SAL_WARN_IF( aSizeInf.GetIdx().get() + pPor->GetLen().get() > aSizeInf.GetText().getLength(), "sw", "portion and text are out of sync" );
1765 TextFrameIndex nSafeLen( std::min(pPor->GetLen().get(), aSizeInf.GetText().getLength() - aSizeInf.GetIdx().get()) );
1767 SwDrawTextInfo aDrawInf(aSizeInf.GetVsh(), *aSizeInf.GetOut(),
1768 &pPara->GetScriptInfo(), aSizeInf.GetText(),
1769 aSizeInf.GetIdx(), nSafeLen, aSizeInf.GetLayoutContext());
1771 // Drop portion works like a multi portion, just its parts are not portions
1772 if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
1774 SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
1775 const SwDropPortionPart* pCurrPart = pDrop->GetPart();
1776 sal_uInt16 nSumWidth = 0;
1777 sal_uInt16 nSumBorderWidth = 0;
1778 // Shift offset with the right and left border of previous parts and left border of actual one
1779 while (pCurrPart && nSumWidth <= nX - sal_Int32(nCurrStart))
1781 nSumWidth += pCurrPart->GetWidth();
1782 if( pCurrPart->GetFont().GetLeftBorder() && !pCurrPart->GetJoinBorderWithPrev() )
1784 nSumBorderWidth += pCurrPart->GetFont().GetLeftBorderSpace();
1786 if (nSumWidth <= nX - sal_Int32(nCurrStart) && pCurrPart->GetFont().GetRightBorder() &&
1787 !pCurrPart->GetJoinBorderWithNext() )
1789 nSumBorderWidth += pCurrPart->GetFont().GetRightBorderSpace();
1791 pCurrPart = pCurrPart->GetFollow();
1793 nX = std::max(static_cast<SwTwips>(0), nX - nSumBorderWidth);
1795 // Shift the offset with the left border width
1796 else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() )
1798 nX = std::max(static_cast<SwTwips>(0), nX - GetInfo().GetFont()->GetLeftBorderSpace());
1801 aDrawInf.SetOffset( nX );
1803 if ( nSpaceAdd )
1805 TextFrameIndex nCharCnt(0);
1806 // #i41860# Thai justified alignment needs some
1807 // additional information:
1808 aDrawInf.SetNumberOfBlanks( pPor->InTextGrp() ?
1809 static_cast<const SwTextPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
1810 TextFrameIndex(0) );
1813 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1814 aDrawInf.SetLen( TextFrameIndex(COMPLETE_STRING) );
1816 aDrawInf.SetSpace( nSpaceAdd );
1817 aDrawInf.SetFont( aSizeInf.GetFont() );
1818 aDrawInf.SetFrame( m_pFrame );
1819 aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
1820 aDrawInf.SetPosMatchesBounds( pCMS && pCMS->m_bPosMatchesBounds );
1822 if ( SwFontScript::CJK == aSizeInf.GetFont()->GetActual() &&
1823 pPara->GetScriptInfo().CountCompChg() &&
1824 ! pPor->InFieldGrp() )
1825 aDrawInf.SetKanaComp( nKanaComp );
1827 nLength = aSizeInf.GetFont()->GetModelPositionForViewPoint_( aDrawInf );
1829 // get position inside field portion?
1830 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1832 pCMS->m_pSpecialPos->nCharOfst = sal_Int32(nLength);
1833 // follow portions: need to add the length of all previous
1834 // portions for the same field
1835 if (static_cast<SwFieldPortion const*>(pPor)->IsFollow())
1837 int nLines(0);
1838 std::vector<SwFieldPortion const*> portions;
1839 for (SwLineLayout const* pLine = GetInfo().GetParaPortion();
1840 true; pLine = pLine->GetNext())
1842 if (SearchLine(m_pCurr, pPor, nLines, portions, pLine))
1844 break;
1846 ++nLines;
1848 for (SwFieldPortion const* pField : portions)
1850 pCMS->m_pSpecialPos->nCharOfst += pField->GetExp().getLength();
1852 pCMS->m_pSpecialPos->nLineOfst = nLines;
1854 nLength = TextFrameIndex(0);
1857 // set cursor bidi level
1858 if ( pCMS )
1859 pCMS->m_nCursorBidiLevel =
1860 aDrawInf.GetCursorBidiLevel();
1862 if( nOldProp )
1863 const_cast<SwFont*>(GetFnt())->SetProportion( nOldProp );
1865 else
1867 sw::FlyContentPortion* pFlyPor(nullptr);
1868 if(bChgNode && pPos && (pFlyPor = dynamic_cast<sw::FlyContentPortion*>(pPor)))
1870 // JP 24.11.94: if the Position is not in Fly, then
1871 // we many not return with COMPLETE_STRING as value!
1872 // (BugId: 9692 + Change in feshview)
1873 SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
1874 SwFrame* pLower = pTmp->GetLower();
1875 // Allow non-text-frames to get SwGrfNode for as-char anchored images into pPos
1876 // instead of the closest SwTextNode, to be consistent with at-char behavior.
1877 bool bChgNodeInner = pLower
1878 && (pLower->IsTextFrame() || pLower->IsLayoutFrame() || pLower->IsNoTextFrame());
1879 Point aTmpPoint( rPoint );
1881 if ( m_pFrame->IsRightToLeft() )
1882 m_pFrame->SwitchLTRtoRTL( aTmpPoint );
1884 if ( m_pFrame->IsVertical() )
1885 m_pFrame->SwitchHorizontalToVertical( aTmpPoint );
1887 if( bChgNodeInner && pTmp->getFrameArea().Contains( aTmpPoint ) &&
1888 !( pTmp->IsProtected() ) )
1890 pFlyPor->GetFlyCursorOfst(aTmpPoint, *pPos, pCMS);
1891 // After a change of the frame, our font must be still
1892 // available for/in the OutputDevice.
1893 // For comparison: Paint and new SwFlyCntPortion !
1894 static_cast<SwTextSizeInfo*>(m_pInf)->SelectFont();
1896 // 6776: The pIter->GetModelPositionForViewPoint is returning here
1897 // from a nesting with COMPLETE_STRING.
1898 return TextFrameIndex(COMPLETE_STRING);
1901 else
1902 nLength = pPor->GetModelPositionForViewPoint( nX );
1905 nOffset = nCurrStart + nLength;
1907 // 7684: We end up in front of the HyphPortion. We must assure
1908 // that we end up in the string.
1909 // If we are at end of line in front of FlyFrames, we must proceed the same way.
1910 if( nOffset && pPor->GetLen() == nLength && pPor->GetNextPortion() &&
1911 !pPor->GetNextPortion()->GetLen() && pPor->GetNextPortion()->InHyphGrp() )
1912 --nOffset;
1914 return nOffset;
1917 /** Looks for text portions which are inside the given rectangle
1919 For a rectangular text selection every text portions which is inside the given
1920 rectangle has to be put into the SwSelectionList as SwPaM
1921 From these SwPaM the SwCursors will be created.
1923 @param rSelList
1924 The container for the overlapped text portions
1926 @param rRect
1927 A rectangle in document coordinates, text inside this rectangle has to be
1928 selected.
1930 @return [ true, false ]
1931 true if any overlapping text portion has been found and put into list
1932 false if no portion overlaps, the list has been unchanged
1934 bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
1936 bool bRet = false;
1937 // GetPaintArea() instead getFrameArea() for negative indents
1938 SwRect aTmpFrame( GetPaintArea() );
1939 if( !rRect.Overlaps( aTmpFrame ) )
1940 return false;
1941 if( rSelList.checkContext( this ) )
1943 SwRect aRect( aTmpFrame );
1944 aRect.Intersection( rRect );
1945 SwPosition aPosL( MapViewToModelPos(TextFrameIndex(0)) );
1946 if( IsEmpty() )
1948 SwPaM *pPam = new SwPaM( aPosL, aPosL );
1949 rSelList.insertPaM( pPam );
1951 else if( aRect.HasArea() )
1953 SwPosition aOld(aPosL.GetNodes().GetEndOfContent());
1954 SwPosition aPosR( aPosL );
1955 Point aPoint;
1956 SwTextInfo aInf( const_cast<SwTextFrame*>(this) );
1957 SwTextIter aLine( const_cast<SwTextFrame*>(this), &aInf );
1958 // We have to care for top-to-bottom layout, where right becomes top etc.
1959 SwRectFnSet aRectFnSet(this);
1960 SwTwips nTop = aRectFnSet.GetTop(aRect);
1961 SwTwips nBottom = aRectFnSet.GetBottom(aRect);
1962 SwTwips nLeft = aRectFnSet.GetLeft(aRect);
1963 SwTwips nRight = aRectFnSet.GetRight(aRect);
1964 SwTwips nY = aLine.Y(); // Top position of the first line
1965 SwTwips nLastY = nY;
1966 while( nY < nTop && aLine.Next() ) // line above rectangle
1968 nLastY = nY;
1969 nY = aLine.Y();
1971 bool bLastLine = false;
1972 if( nY < nTop && !aLine.GetNext() )
1974 bLastLine = true;
1975 nY += aLine.GetLineHeight();
1977 do // check the lines for overlapping
1979 if( nLastY < nTop ) // if the last line was above rectangle
1980 nLastY = nTop;
1981 if( nY > nBottom ) // if the current line leaves the rectangle
1982 nY = nBottom;
1983 if( nY >= nLastY ) // gotcha: overlapping
1985 nLastY += nY;
1986 nLastY /= 2;
1987 if( aRectFnSet.IsVert() )
1989 aPoint.setX( nLastY );
1990 aPoint.setY( nLeft );
1992 else
1994 aPoint.setX( nLeft );
1995 aPoint.setY( nLastY );
1997 // Looking for the position of the left border of the rectangle
1998 // in this text line
1999 SwCursorMoveState aState( CursorMoveState::UpDown );
2000 if( GetModelPositionForViewPoint( &aPosL, aPoint, &aState ) )
2002 if( aRectFnSet.IsVert() )
2004 aPoint.setX( nLastY );
2005 aPoint.setY( nRight );
2007 else
2009 aPoint.setX( nRight );
2010 aPoint.setY( nLastY );
2012 // If we get a right position and if the left position
2013 // is not the same like the left position of the line before
2014 // which could happen e.g. for field portions or fly frames
2015 // a SwPaM will be inserted with these positions
2016 if( GetModelPositionForViewPoint( &aPosR, aPoint, &aState ) &&
2017 aOld != aPosL)
2019 SwPaM *pPam = new SwPaM( aPosL, aPosR );
2020 rSelList.insertPaM( pPam );
2021 aOld = aPosL;
2025 if( aLine.Next() )
2027 nLastY = nY;
2028 nY = aLine.Y();
2030 else if( !bLastLine )
2032 bLastLine = true;
2033 nLastY = nY;
2034 nY += aLine.GetLineHeight();
2036 else
2037 break;
2038 }while( nLastY < nBottom );
2041 if( GetDrawObjs() )
2043 const SwSortedObjs &rObjs = *GetDrawObjs();
2044 for (SwAnchoredObject* pAnchoredObj : rObjs)
2046 const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
2047 if( !pFly )
2048 continue;
2049 if( pFly->IsFlyInContentFrame() && pFly->FillSelection( rSelList, rRect ) )
2050 bRet = true;
2053 return bRet;
2056 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */