1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <hintids.hxx>
21 #include <ndtxt.hxx>
22 #include <doc.hxx>
23 #include <frmfmt.hxx>
24 #include <paratr.hxx>
25 #include <flyfrm.hxx>
26 #include <pam.hxx>
27 #include <swselectionlist.hxx>
28 #include <sortedobjs.hxx>
29 #include <editeng/protitem.hxx>
30 #include <editeng/adjustitem.hxx>
31 #include <editeng/lspcitem.hxx>
32 #include <editeng/lrspitem.hxx>
33 #include <editeng/borderline.hxx>
34 #include <frmatr.hxx>
35 #include <pagedesc.hxx>
36 #include <tgrditem.hxx>
37 #include <IDocumentSettingAccess.hxx>
38 #include <pagefrm.hxx>
39 
40 #include "itrtxt.hxx"
41 #include <txtfrm.hxx>
42 #include <flyfrms.hxx>
43 #include "porglue.hxx"
44 #include "porfld.hxx"
45 #include "porfly.hxx"
46 #include "pordrop.hxx"
47 #include <crstate.hxx>
48 #include "pormulti.hxx"
49 #include <numrule.hxx>
50 #include <com/sun/star/i18n/ScriptType.hpp>
51 
52 // Not reentrant !!!
53 // is set in GetCharRect and is interpreted in UnitUp/Down.
54 bool SwTextCursor::bRightMargin = false;
55 
56 // After calculating the position of a character during GetCharRect
57 // this function allows to find the coordinates of a position (defined
58 // in pCMS->pSpecialPos) inside a special portion (e.g., a field)
lcl_GetCharRectInsideField(SwTextSizeInfo & rInf,SwRect & rOrig,const SwCursorMoveState & rCMS,const SwLinePortion & rPor)59 static void lcl_GetCharRectInsideField( SwTextSizeInfo& rInf, SwRect& rOrig,
60                                  const SwCursorMoveState& rCMS,
61                                  const SwLinePortion& rPor )
62 {
63     OSL_ENSURE( rCMS.m_pSpecialPos, "Information about special pos missing" );
64 
65     if ( rPor.InFieldGrp() && !static_cast<const SwFieldPortion&>(rPor).GetExp().isEmpty() )
66     {
67         const sal_Int32 nCharOfst = rCMS.m_pSpecialPos->nCharOfst;
68         sal_Int32 nFieldIdx = 0;
69         sal_Int32 nFieldLen = 0;
70 
71         OUString sString;
72         const OUString* pString = nullptr;
73         const SwLinePortion* pPor = &rPor;
74         do
75         {
76             if ( pPor->InFieldGrp() )
77             {
78                 sString = static_cast<const SwFieldPortion*>(pPor)->GetExp();
79                 pString = &sString;
80                 nFieldLen = pString->getLength();
81             }
82             else
83             {
84                 pString = nullptr;
85                 nFieldLen = 0;
86             }
87 
88             if ( ! pPor->GetNextPortion() || nFieldIdx + nFieldLen > nCharOfst )
89                 break;
90 
91             nFieldIdx = nFieldIdx + nFieldLen;
92             rOrig.Pos().AdjustX(pPor->Width() );
93             pPor = pPor->GetNextPortion();
94 
95         } while ( true );
96 
97         OSL_ENSURE( nCharOfst >= nFieldIdx, "Request of position inside field failed" );
98         sal_Int32 nLen = nCharOfst - nFieldIdx + 1;
99 
100         if ( pString )
101         {
102             // get script for field portion
103             rInf.GetFont()->SetActual( SwScriptInfo::WhichFont(0, *pString) );
104 
105             TextFrameIndex const nOldLen = pPor->GetLen();
106             const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen - 1));
107             const SwTwips nX1 = pPor->GetLen() ?
108                                 pPor->GetTextSize( rInf ).Width() :
109                                 0;
110 
111             SwTwips nX2 = 0;
112             if ( rCMS.m_bRealWidth )
113             {
114                 const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen));
115                 nX2 = pPor->GetTextSize( rInf ).Width();
116             }
117 
118             const_cast<SwLinePortion*>(pPor)->SetLen( nOldLen );
119 
120             rOrig.Pos().AdjustX(nX1 );
121             rOrig.Width( ( nX2 > nX1 ) ?
122                          ( nX2 - nX1 ) :
123                            1 );
124         }
125     }
126     else
127     {
128         // special cases: no common fields, e.g., graphic number portion,
129         // FlyInCntPortions, Notes
130         rOrig.Width( rCMS.m_bRealWidth && rPor.Width() ? rPor.Width() : 1 );
131     }
132 }
133 
134 // #i111284#
135 namespace {
IsLabelAlignmentActive(const SwTextNode & rTextNode)136     bool IsLabelAlignmentActive( const SwTextNode& rTextNode )
137     {
138         bool bRet( false );
139 
140         if ( rTextNode.GetNumRule() )
141         {
142             int nListLevel = rTextNode.GetActualListLevel();
143 
144             if (nListLevel < 0)
145                 nListLevel = 0;
146 
147             if (nListLevel >= MAXLEVEL)
148                 nListLevel = MAXLEVEL - 1;
149 
150             const SwNumFormat& rNumFormat =
151                     rTextNode.GetNumRule()->Get( static_cast<sal_uInt16>(nListLevel) );
152             if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
153             {
154                 bRet = true;
155             }
156         }
157 
158         return bRet;
159     }
160 } // end of anonymous namespace
161 
CtorInitTextMargin(SwTextFrame * pNewFrame,SwTextSizeInfo * pNewInf)162 void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
163 {
164     CtorInitTextIter( pNewFrame, pNewInf );
165 
166     m_pInf = pNewInf;
167     GetInfo().SetFont( GetFnt() );
168     const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
169 
170     const SvxLRSpaceItem &rSpace = pNode->GetSwAttrSet().GetLRSpace();
171     // #i95907#
172     // #i111284#
173     const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
174     const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
175     const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable();
176     const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;
177 
178     // Carefully adjust the text formatting ranges.
179 
180     // This whole area desperately needs some rework. There are
181     // quite a couple of values that need to be considered:
182     // 1. paragraph indent
183     // 2. paragraph first line indent
184     // 3. numbering indent
185     // 4. numbering spacing to text
186     // 5. paragraph border
187     // Note: These values have already been used during calculation
188     // of the printing area of the paragraph.
189     const int nLMWithNum = pNode->GetLeftMarginWithNum( true );
190     if ( m_pFrame->IsRightToLeft() )
191     {
192         // this calculation is identical this the calculation for L2R layout - see below
193         nLeft = m_pFrame->getFrameArea().Left() +
194                 m_pFrame->getFramePrintArea().Left() +
195                 nLMWithNum -
196                 pNode->GetLeftMarginWithNum() -
197                 // #i95907#
198                 // #i111284#
199                 // rSpace.GetLeft() + rSpace.GetTextLeft();
200                 ( bListLevelIndentsApplicableAndLabelAlignmentActive
201                   ? 0
202                   : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
203     }
204     else
205     {
206         // #i95907#
207         // #i111284#
208         if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
209              !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
210         {
211             // this calculation is identical this the calculation for R2L layout - see above
212             nLeft = m_pFrame->getFrameArea().Left() +
213                     m_pFrame->getFramePrintArea().Left() +
214                     nLMWithNum -
215                     pNode->GetLeftMarginWithNum() -
216                     // #i95907#
217                     // #i111284#
218                     ( bListLevelIndentsApplicableAndLabelAlignmentActive
219                       ? 0
220                       : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
221         }
222         else
223         {
224             nLeft = m_pFrame->getFrameArea().Left() +
225                     std::max( long( rSpace.GetTextLeft() + nLMWithNum ),
226                          m_pFrame->getFramePrintArea().Left() );
227         }
228     }
229 
230     nRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();
231 
232     if( nLeft >= nRight &&
233          // #i53066# Omit adjustment of nLeft for numbered
234          // paras inside cells inside new documents:
235         ( pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
236           !m_pFrame->IsInTab() ||
237           ( !nLMWithNum && !(bLabelAlignmentActive && !bListLevelIndentsApplicable) ) ) )
238     {
239         nLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
240         if( nLeft >= nRight )   // e.g. with large paragraph indentations in slim table columns
241             nRight = nLeft + 1; // einen goennen wir uns immer
242     }
243 
244     if( m_pFrame->IsFollow() && m_pFrame->GetOfst() )
245         nFirst = nLeft;
246     else
247     {
248         short nFLOfst = 0;
249         long nFirstLineOfs = 0;
250         if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
251             rSpace.IsAutoFirst() )
252         {
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;
258 
259             const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
260             if( pSpace )
261             {
262                 switch( pSpace->GetLineSpaceRule() )
263                 {
264                     case SvxLineSpaceRule::Auto:
265                     break;
266                     case SvxLineSpaceRule::Min:
267                     {
268                         if( nFirstLineOfs < pSpace->GetLineHeight() )
269                             nFirstLineOfs = pSpace->GetLineHeight();
270                         break;
271                     }
272                     case SvxLineSpaceRule::Fix:
273                         nFirstLineOfs = pSpace->GetLineHeight();
274                     break;
275                     default: OSL_FAIL( ": unknown LineSpaceRule" );
276                 }
277                 switch( pSpace->GetInterLineSpaceRule() )
278                 {
279                     case SvxInterLineSpaceRule::Off:
280                     break;
281                     case SvxInterLineSpaceRule::Prop:
282                     {
283                         long nTmp = pSpace->GetPropLineSpace();
284                         // 50% is the minimum, at 0% we switch to
285                         // the default value 100%...
286                         if( nTmp < 50 )
287                             nTmp = nTmp ? 50 : 100;
288 
289                         nTmp *= nFirstLineOfs;
290                         nTmp /= 100;
291                         if( !nTmp )
292                             ++nTmp;
293                         nFirstLineOfs = nTmp;
294                         break;
295                     }
296                     case SvxInterLineSpaceRule::Fix:
297                     {
298                         nFirstLineOfs += pSpace->GetInterLineSpace();
299                         break;
300                     }
301                     default: OSL_FAIL( ": unknown InterLineSpaceRule" );
302                 }
303             }
304         }
305         else
306             nFirstLineOfs = nFLOfst;
307 
308         // #i95907#
309         // #i111284#
310         if ( m_pFrame->IsRightToLeft() ||
311              bListLevelIndentsApplicableAndLabelAlignmentActive ||
312              !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
313         {
314             nFirst = nLeft + nFirstLineOfs;
315         }
316         else
317         {
318               nFirst = m_pFrame->getFrameArea().Left() +
319                      std::max( rSpace.GetTextLeft() + nLMWithNum+ nFirstLineOfs,
320                           m_pFrame->getFramePrintArea().Left() );
321         }
322 
323         // Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
324         //       value for the new list label position and space mode LABEL_ALIGNMENT
325         //       and label alignment CENTER and RIGHT in L2R layout respectively
326         //       label alignment LEFT and CENTER in R2L layout
327         nFirst += m_pFrame->GetAdditionalFirstLineOffset();
328 
329         if( nFirst >= nRight )
330             nFirst = nRight - 1;
331     }
332     const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
333     nAdjust = rAdjust.GetAdjust();
334 
335     // left is left and right is right
336     if ( m_pFrame->IsRightToLeft() )
337     {
338         if ( SvxAdjust::Left == nAdjust )
339             nAdjust = SvxAdjust::Right;
340         else if ( SvxAdjust::Right == nAdjust )
341             nAdjust = SvxAdjust::Left;
342     }
343 
344     m_bOneBlock = rAdjust.GetOneWord() == SvxAdjust::Block;
345     m_bLastBlock = rAdjust.GetLastBlock() == SvxAdjust::Block;
346     m_bLastCenter = rAdjust.GetLastBlock() == SvxAdjust::Center;
347 
348     // #i91133#
349     mnTabLeft = pNode->GetLeftMarginForTabCalculation();
350 
351     DropInit();
352 }
353 
DropInit()354 void SwTextMargin::DropInit()
355 {
356     nDropLeft = nDropLines = nDropHeight = nDropDescent = 0;
357     const SwParaPortion *pPara = GetInfo().GetParaPortion();
358     if( pPara )
359     {
360         const SwDropPortion *pPorDrop = pPara->FindDropPortion();
361         if ( pPorDrop )
362         {
363             nDropLeft = pPorDrop->GetDropLeft();
364             nDropLines = pPorDrop->GetLines();
365             nDropHeight = pPorDrop->GetDropHeight();
366             nDropDescent = pPorDrop->GetDropDescent();
367         }
368     }
369 }
370 
371 // The function is interpreting / observing / evaluating / keeping / respecting the first line indention and the specified width.
GetLineStart() const372 SwTwips SwTextMargin::GetLineStart() const
373 {
374     SwTwips nRet = GetLeftMargin();
375     if( GetAdjust() != SvxAdjust::Left &&
376         !m_pCurr->GetFirstPortion()->IsMarginPortion() )
377     {
378         // If the first portion is a Margin, then the
379         // adjustment is expressed by the portions.
380         if( GetAdjust() == SvxAdjust::Right )
381             nRet = Right() - CurrWidth();
382         else if( GetAdjust() == SvxAdjust::Center )
383             nRet += (GetLineWidth() - CurrWidth()) / 2;
384     }
385     return nRet;
386 }
387 
CtorInitTextCursor(SwTextFrame * pNewFrame,SwTextSizeInfo * pNewInf)388 void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
389 {
390     CtorInitTextMargin( pNewFrame, pNewInf );
391     // 6096: Attention, the iterators are derived!
392     // GetInfo().SetOut( GetInfo().GetWin() );
393 }
394 
395 // 1170: Ancient bug: Shift-End forgets the last character ...
GetEndCharRect(SwRect * pOrig,const TextFrameIndex nOfst,SwCursorMoveState * pCMS,const long nMax)396 void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
397                                   SwCursorMoveState* pCMS, const long nMax )
398 {
399     // 1170: Ambiguity of document positions
400     bRightMargin = true;
401     CharCursorToLine(nOfst);
402 
403     // Somehow twisted: nOfst names the position behind the last
404     // character of the last line == This is the position in front of the first character
405     // of the line, in which we are situated:
406     if( nOfst != GetStart() || !m_pCurr->GetLen() )
407     {
408         // 8810: Master line RightMargin, after that LeftMargin
409         GetCharRect( pOrig, nOfst, pCMS, nMax );
410         bRightMargin = nOfst >= GetEnd() && nOfst < TextFrameIndex(GetInfo().GetText().getLength());
411         return;
412     }
413 
414     if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
415     {
416         GetCharRect( pOrig, nOfst, pCMS, nMax );
417         return;
418     }
419 
420     // If necessary, as catch up, do the adjustment
421     GetAdjusted();
422 
423     long nX = 0;
424     long nLast = 0;
425     SwLinePortion *pPor = m_pCurr->GetFirstPortion();
426 
427     sal_uInt16 nTmpHeight, nTmpAscent;
428     CalcAscentAndHeight( nTmpAscent, nTmpHeight );
429     sal_uInt16 nPorHeight = nTmpHeight;
430     sal_uInt16 nPorAscent = nTmpAscent;
431 
432     // Search for the last Text/EndPortion of the line
433     while( pPor )
434     {
435         nX = nX + pPor->Width();
436         if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
437             && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
438         {
439             nLast = nX;
440             nPorHeight = pPor->Height();
441             nPorAscent = pPor->GetAscent();
442         }
443         pPor = pPor->GetNextPortion();
444     }
445 
446     const Size aCharSize( 1, nTmpHeight );
447     pOrig->Pos( GetTopLeft() );
448     pOrig->SSize( aCharSize );
449     pOrig->Pos().AdjustX(nLast );
450     const SwTwips nTmpRight = Right() - 1;
451     if( pOrig->Left() > nTmpRight )
452         pOrig->Pos().setX( nTmpRight );
453 
454     if ( pCMS && pCMS->m_bRealHeight )
455     {
456         if ( nTmpAscent > nPorAscent )
457             pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
458         else
459             pCMS->m_aRealHeight.setX( 0 );
460         OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
461         pCMS->m_aRealHeight.setY( nPorHeight );
462     }
463 }
464 
465 // internal function, called by SwTextCursor::GetCharRect() to calculate
466 // the relative character position in the current line.
467 // pOrig refers to x and y coordinates, width and height of the cursor
468 // pCMS is used for restricting the cursor, if there are different font
469 // heights in one line ( first value = offset to y of pOrig, second
470 // value = real height of (shortened) cursor
GetCharRect_(SwRect * pOrig,TextFrameIndex const nOfst,SwCursorMoveState * pCMS)471 void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
472     SwCursorMoveState* pCMS )
473 {
474     const OUString aText = GetInfo().GetText();
475     SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
476     if( GetPropFont() )
477         aInf.GetFont()->SetProportion( GetPropFont() );
478     sal_uInt16 nTmpAscent, nTmpHeight;  // Line height
479     CalcAscentAndHeight( nTmpAscent, nTmpHeight );
480     const Size  aCharSize( 1, nTmpHeight );
481     const Point aCharPos;
482     pOrig->Pos( aCharPos );
483     pOrig->SSize( aCharSize );
484 
485     // If we are looking for a position inside a field which covers
486     // more than one line we may not skip any "empty portions" at the
487     // beginning of a line
488     const bool bInsideFirstField = pCMS && pCMS->m_pSpecialPos &&
489                                     ( pCMS->m_pSpecialPos->nLineOfst ||
490                                       SwSPExtendRange::BEFORE ==
491                                       pCMS->m_pSpecialPos->nExtendRange );
492 
493     bool bWidth = pCMS && pCMS->m_bRealWidth;
494     if( !m_pCurr->GetLen() && !m_pCurr->Width() )
495     {
496         if ( pCMS && pCMS->m_bRealHeight )
497         {
498             pCMS->m_aRealHeight.setX( 0 );
499             pCMS->m_aRealHeight.setY( nTmpHeight );
500         }
501     }
502     else
503     {
504         sal_uInt16 nPorHeight = nTmpHeight;
505         sal_uInt16 nPorAscent = nTmpAscent;
506         SwTwips nX = 0;
507         SwTwips nTmpFirst = 0;
508         SwLinePortion *pPor = m_pCurr->GetFirstPortion();
509         SwBidiPortion* pLastBidiPor = nullptr;
510         TextFrameIndex nLastBidiIdx(-1);
511         SwTwips nLastBidiPorWidth = 0;
512         std::deque<sal_uInt16>* pKanaComp = m_pCurr->GetpKanaComp();
513         sal_uInt16 nSpaceIdx = 0;
514         size_t nKanaIdx = 0;
515         long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
516 
517         bool bNoText = true;
518 
519         // First all portions without Len at beginning of line are skipped.
520         // Exceptions are the mean special portions from WhichFirstPortion:
521         // Num, ErgoSum, FootnoteNum, FieldRests
522         // 8477: but also the only Textportion of an empty line with
523         // Right/Center-Adjustment! So not just pPor->GetExpandPortion() ...
524         while( pPor && !pPor->GetLen() && ! bInsideFirstField )
525         {
526             nX += pPor->Width();
527             if ( pPor->InSpaceGrp() && nSpaceAdd )
528                 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
529             if( bNoText )
530                 nTmpFirst = nX;
531             // 8670: EndPortions count once as TextPortions.
532             // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
533             if( pPor->InTextGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
534             {
535                 bNoText = false;
536                 nTmpFirst = nX;
537             }
538             if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
539             {
540                 if ( m_pCurr->IsSpaceAdd() )
541                 {
542                     if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
543                         nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
544                     else
545                         nSpaceAdd = 0;
546                 }
547 
548                 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
549                     ++nKanaIdx;
550             }
551             if( pPor->InFixMargGrp() )
552             {
553                 if( pPor->IsMarginPortion() )
554                     bNoText = false;
555                 else
556                 {
557                     // fix margin portion => next SpaceAdd, KanaComp value
558                     if ( m_pCurr->IsSpaceAdd() )
559                     {
560                         if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
561                             nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
562                         else
563                             nSpaceAdd = 0;
564                     }
565 
566                     if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
567                         ++nKanaIdx;
568                 }
569             }
570             pPor = pPor->GetNextPortion();
571         }
572 
573         if( !pPor )
574         {
575             // There's just Spezialportions.
576             nX = nTmpFirst;
577         }
578         else
579         {
580             if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
581                 (!pPor->InFieldGrp() || pPor->GetAscent() ) )
582             {
583                 nPorHeight = pPor->Height();
584                 nPorAscent = pPor->GetAscent();
585             }
586             while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
587                    ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
588             {
589                 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
590                     (!pPor->InFieldGrp() || pPor->GetAscent() ) )
591                 {
592                     nPorHeight = pPor->Height();
593                     nPorAscent = pPor->GetAscent();
594                 }
595 
596                 // If we are behind the portion, we add the portion width to
597                 // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
598                 // For common portions (including BidiPortions) we want to add
599                 // the portion width to nX. For MultiPortions, nExtra = 0,
600                 // therefore we go to the 'else' branch and start a recursion.
601                 const TextFrameIndex nExtra( (pPor->IsMultiPortion()
602                              && !static_cast<SwMultiPortion*>(pPor)->IsBidi()
603                              && !bWidth)
604                         ? 0 : 1 );
605                 if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
606                 {
607                     if ( pPor->InSpaceGrp() && nSpaceAdd )
608                         nX += pPor->PrtWidth() +
609                               pPor->CalcSpacing( nSpaceAdd, aInf );
610                     else
611                     {
612                         if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
613                         {
614                             // update to current SpaceAdd, KanaComp values
615                             if ( m_pCurr->IsSpaceAdd() )
616                             {
617                                 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
618                                     nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
619                                 else
620                                     nSpaceAdd = 0;
621                             }
622 
623                             if ( pKanaComp &&
624                                 ( nKanaIdx + 1 ) < pKanaComp->size()
625                                 )
626                                 ++nKanaIdx;
627                         }
628                         if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
629                                 !pPor->GetNextPortion()->IsMarginPortion() ) )
630                             nX += pPor->PrtWidth();
631                     }
632                     if( pPor->IsMultiPortion() )
633                     {
634                         if ( static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
635                         {
636                             if ( m_pCurr->IsSpaceAdd() )
637                             {
638                                 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
639                                     nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
640                                 else
641                                     nSpaceAdd = 0;
642                             }
643 
644                             if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
645                                 ++nKanaIdx;
646                         }
647 
648                         // if we are right behind a BidiPortion, we have to
649                         // hold a pointer to the BidiPortion in order to
650                         // find the correct cursor position, depending on the
651                         // cursor level
652                         if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() &&
653                              aInf.GetIdx() + pPor->GetLen() == nOfst )
654                         {
655                              pLastBidiPor = static_cast<SwBidiPortion*>(pPor);
656                              nLastBidiIdx = aInf.GetIdx();
657                              nLastBidiPorWidth = pLastBidiPor->Width() +
658                                                  pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );
659                         }
660                     }
661 
662                     aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
663                     pPor = pPor->GetNextPortion();
664                 }
665                 else
666                 {
667                     if( pPor->IsMultiPortion() )
668                     {
669                         nTmpAscent = AdjustBaseLine( *m_pCurr, pPor );
670                         GetInfo().SetMulti( true );
671                         pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );
672 
673                         if( pCMS && pCMS->m_b2Lines )
674                         {
675                             const bool bRecursion (pCMS->m_p2Lines);
676                             if ( !bRecursion )
677                             {
678                                 pCMS->m_p2Lines.reset(new Sw2LinesPos);
679                                 pCMS->m_p2Lines->aLine = SwRect(aCharPos, aCharSize);
680                             }
681 
682                             if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
683                             {
684                                 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
685                                     pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_270;
686                                 else
687                                     pCMS->m_p2Lines->nMultiType = MultiPortionType::ROT_90;
688                             }
689                             else if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
690                                 pCMS->m_p2Lines->nMultiType = MultiPortionType::TWOLINE;
691                             else if( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
692                                 pCMS->m_p2Lines->nMultiType = MultiPortionType::BIDI;
693                             else
694                                 pCMS->m_p2Lines->nMultiType = MultiPortionType::RUBY;
695 
696                             SwTwips nTmpWidth = pPor->Width();
697                             if( nSpaceAdd )
698                                 nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
699 
700                             SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
701                                           Size( nTmpWidth, pPor->Height() ) );
702 
703                             if ( ! bRecursion )
704                                 pCMS->m_p2Lines->aPortion = aRect;
705                             else
706                                 pCMS->m_p2Lines->aPortion2 = aRect;
707                         }
708 
709                         // In a multi-portion we use GetCharRect()-function
710                         // recursively and must add the x-position
711                         // of the multi-portion.
712                         TextFrameIndex const nOldStart = m_nStart;
713                         SwTwips nOldY = m_nY;
714                         sal_uInt8 nOldProp = GetPropFont();
715                         m_nStart = aInf.GetIdx();
716                         SwLineLayout* pOldCurr = m_pCurr;
717                         m_pCurr = &static_cast<SwMultiPortion*>(pPor)->GetRoot();
718                         if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
719                             SetPropFont( 50 );
720 
721                         SwTextGridItem const*const pGrid(
722                                 GetGridItem(GetTextFrame()->FindPageFrame()));
723                         const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
724                         const sal_uInt16 nRubyHeight = bHasGrid ?
725                                                    pGrid->GetRubyHeight() : 0;
726 
727                         if( m_nStart + m_pCurr->GetLen() <= nOfst && GetNext() &&
728                             ( ! static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
729                                 static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
730                         {
731                             sal_uInt16 nOffset;
732                             // in grid mode we may only add the height of the
733                             // ruby line if ruby line is on top
734                             if ( bHasGrid &&
735                                 static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
736                                 static_cast<SwMultiPortion*>(pPor)->OnTop() )
737                                 nOffset = nRubyHeight;
738                             else
739                                 nOffset = GetLineHeight();
740 
741                             pOrig->Pos().AdjustY(nOffset );
742                             Next();
743                         }
744 
745                         const bool bSpaceChg = static_cast<SwMultiPortion*>(pPor)->
746                                                 ChgSpaceAdd( m_pCurr, nSpaceAdd );
747                         Point aOldPos = pOrig->Pos();
748 
749                         // Ok, for ruby portions in grid mode we have to
750                         // temporarily set the inner line height to the
751                         // outer line height because that value is needed
752                         // for the adjustment inside the recursion
753                         const sal_uInt16 nOldRubyHeight = m_pCurr->Height();
754                         const sal_uInt16 nOldRubyRealHeight = m_pCurr->GetRealHeight();
755                         const bool bChgHeight =
756                                 static_cast<SwMultiPortion*>(pPor)->IsRuby() && bHasGrid;
757 
758                         if ( bChgHeight )
759                         {
760                             m_pCurr->Height( pOldCurr->Height() - nRubyHeight );
761                             m_pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
762                                                   nRubyHeight );
763                         }
764 
765                         SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
766                         if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
767                         {
768                             aLayoutModeModifier.Modify(
769                                 static_cast<SwBidiPortion*>(pPor)->GetLevel() % 2 );
770                         }
771 
772                         GetCharRect_( pOrig, nOfst, pCMS );
773 
774                         if ( bChgHeight )
775                         {
776                             m_pCurr->Height( nOldRubyHeight );
777                             m_pCurr->SetRealHeight( nOldRubyRealHeight );
778                         }
779 
780                         // if we are still in the first row of
781                         // our 2 line multiportion, we use the FirstMulti flag
782                         // to indicate this
783                         if ( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
784                         {
785                             // the recursion may have damaged our font size
786                             SetPropFont( nOldProp );
787                             GetInfo().GetFont()->SetProportion( 100 );
788 
789                             if ( m_pCurr == &static_cast<SwMultiPortion*>(pPor)->GetRoot() )
790                             {
791                                 GetInfo().SetFirstMulti( true );
792 
793                                 // we want to treat a double line portion like a
794                                 // single line portion, if there is no text in
795                                 // the second line
796                                 if ( !m_pCurr->GetNext() ||
797                                      !m_pCurr->GetNext()->GetLen() )
798                                     GetInfo().SetMulti( false );
799                             }
800                         }
801                         // ruby portions are treated like single line portions
802                         else if( static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
803                                  static_cast<SwMultiPortion*>(pPor)->IsBidi() )
804                             GetInfo().SetMulti( false );
805 
806                         // calculate cursor values
807                         if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
808                         {
809                             GetInfo().SetMulti( false );
810                             long nTmp = pOrig->Width();
811                             pOrig->Width( pOrig->Height() );
812                             pOrig->Height( nTmp );
813                             nTmp = pOrig->Left() - aOldPos.X();
814 
815                             // if we travel into our rotated portion from
816                             // a line below, we have to take care, that the
817                             // y coord in pOrig is less than line height:
818                             if ( nTmp )
819                                 nTmp--;
820 
821                             pOrig->Pos().setX( nX + aOldPos.X() );
822                             if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
823                                 pOrig->Pos().setY( aOldPos.Y() + nTmp );
824                             else
825                                 pOrig->Pos().setY( aOldPos.Y()
826                                     + pPor->Height() - nTmp - pOrig->Height() );
827                             if ( pCMS && pCMS->m_bRealHeight )
828                             {
829                                 pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
830                                 // result for rotated multi portion is not
831                                 // correct for reverse (270 degree) portions
832                                 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
833                                 {
834                                     if ( SvxParaVertAlignItem::Align::Automatic ==
835                                          GetLineInfo().GetVertAlign() )
836                                         // if vertical alignment is set to auto,
837                                         // we switch from base line alignment
838                                         // to centered alignment
839                                         pCMS->m_aRealHeight.setX(
840                                             ( pOrig->Width() +
841                                               pCMS->m_aRealHeight.Y() ) / 2 );
842                                     else
843                                         pCMS->m_aRealHeight.setX(
844                                             pOrig->Width() -
845                                             pCMS->m_aRealHeight.X() +
846                                             pCMS->m_aRealHeight.Y() );
847                                 }
848                             }
849                         }
850                         else
851                         {
852                             pOrig->Pos().AdjustY(aOldPos.Y() );
853                             if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
854                             {
855                                 const SwTwips nPorWidth = pPor->Width() +
856                                                          pPor->CalcSpacing( nSpaceAdd, aInf );
857                                 const SwTwips nInsideOfst = pOrig->Pos().X();
858                                 pOrig->Pos().setX( nX + nPorWidth -
859                                                    nInsideOfst - pOrig->Width() );
860                             }
861                             else
862                                 pOrig->Pos().AdjustX(nX );
863 
864                             if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
865                                 pOrig->Pos().AdjustX(
866                                     static_cast<SwDoubleLinePortion*>(pPor)->PreWidth() );
867                         }
868 
869                         if( bSpaceChg )
870                             SwDoubleLinePortion::ResetSpaceAdd( m_pCurr );
871 
872                         m_pCurr = pOldCurr;
873                         m_nStart = nOldStart;
874                         m_nY = nOldY;
875                         m_bPrev = false;
876 
877                         return;
878                     }
879                     if ( pPor->PrtWidth() )
880                     {
881                         TextFrameIndex const nOldLen = pPor->GetLen();
882                         pPor->SetLen( nOfst - aInf.GetIdx() );
883                         aInf.SetLen( pPor->GetLen() );
884                         if( nX || !pPor->InNumberGrp() )
885                         {
886                             SeekAndChg( aInf );
887                             const bool bOldOnWin = aInf.OnWin();
888                             aInf.SetOnWin( false ); // no BULLETs!
889                             SwTwips nTmp = nX;
890                             aInf.SetKanaComp( pKanaComp );
891                             aInf.SetKanaIdx( nKanaIdx );
892                             nX += pPor->GetTextSize( aInf ).Width();
893                             aInf.SetOnWin( bOldOnWin );
894                             if ( pPor->InSpaceGrp() && nSpaceAdd )
895                                 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
896                             if( bWidth )
897                             {
898                                 pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
899                                 aInf.SetLen( pPor->GetLen() );
900                                 aInf.SetOnWin( false ); // no BULLETs!
901                                 nTmp += pPor->GetTextSize( aInf ).Width();
902                                 aInf.SetOnWin( bOldOnWin );
903                                 if ( pPor->InSpaceGrp() && nSpaceAdd )
904                                     nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
905                                 pOrig->Width( nTmp - nX );
906                             }
907                         }
908                         pPor->SetLen( nOldLen );
909 
910                         // Shift the cursor with the right border width
911                         // Note: nX remains positive because GetTextSize() also include the width of the right border
912                         if( aInf.GetIdx() < nOfst && nOfst < aInf.GetIdx() + pPor->GetLen() )
913                         {
914                             // Find the current drop portion part and use its right border
915                             if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
916                             {
917                                 SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
918                                 const SwDropPortionPart* pCurrPart = pDrop->GetPart();
919                                 TextFrameIndex nSumLength(0);
920                                 while( pCurrPart && (nSumLength += pCurrPart->GetLen()) < nOfst - aInf.GetIdx() )
921                                 {
922                                     pCurrPart = pCurrPart->GetFollow();
923                                 }
924                                 if( pCurrPart && nSumLength != nOfst - aInf.GetIdx() &&
925                                     pCurrPart->GetFont().GetRightBorder() && !pCurrPart->GetJoinBorderWithNext() )
926                                 {
927                                     nX -= pCurrPart->GetFont().GetRightBorderSpace();
928                                 }
929                             }
930                             else if( GetInfo().GetFont()->GetRightBorder() && !pPor->GetJoinBorderWithNext())
931                             {
932                                 nX -= GetInfo().GetFont()->GetRightBorderSpace();
933                             }
934                          }
935                     }
936                     bWidth = false;
937                     break;
938                 }
939             }
940         }
941 
942         if( pPor )
943         {
944             OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
945             bool bEmptyField = false;
946             if( pPor->InFieldGrp() && pPor->GetLen() )
947             {
948                 SwFieldPortion *pTmp = static_cast<SwFieldPortion*>(pPor);
949                 while( pTmp->HasFollow() && pTmp->GetExp().isEmpty() )
950                 {
951                     sal_uInt16 nAddX = pTmp->Width();
952                     SwLinePortion *pNext = pTmp->GetNextPortion();
953                     while( pNext && !pNext->InFieldGrp() )
954                     {
955                         OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
956                         nAddX = nAddX + pNext->Width();
957                         pNext = pNext->GetNextPortion();
958                     }
959                     if( !pNext )
960                         break;
961                     pTmp = static_cast<SwFieldPortion*>(pNext);
962                     nPorHeight = pTmp->Height();
963                     nPorAscent = pTmp->GetAscent();
964                     nX += nAddX;
965                     bEmptyField = true;
966                 }
967             }
968             // 8513: Fields in justified text, skipped
969             while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
970                    ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
971                      pPor->IsBlankPortion() || pPor->InTabGrp() ||
972                      ( !bEmptyField && pPor->InFieldGrp() ) ) )
973             {
974                 if ( pPor->InSpaceGrp() && nSpaceAdd )
975                     nX += pPor->PrtWidth() +
976                           pPor->CalcSpacing( nSpaceAdd, aInf );
977                 else
978                 {
979                     if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
980                     {
981                         if ( m_pCurr->IsSpaceAdd() )
982                         {
983                             if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
984                                 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
985                             else
986                                 nSpaceAdd = 0;
987                         }
988 
989                         if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
990                             ++nKanaIdx;
991                     }
992                     if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
993                             !pPor->GetNextPortion()->IsMarginPortion() ) )
994                         nX += pPor->PrtWidth();
995                 }
996                 if( pPor->IsMultiPortion() &&
997                     static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
998                 {
999                     if ( m_pCurr->IsSpaceAdd() )
1000                     {
1001                         if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1002                             nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1003                         else
1004                             nSpaceAdd = 0;
1005                     }
1006 
1007                     if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
1008                         ++nKanaIdx;
1009                 }
1010                 if( !pPor->IsFlyPortion() )
1011                 {
1012                     nPorHeight = pPor->Height();
1013                     nPorAscent = pPor->GetAscent();
1014                 }
1015                 pPor = pPor->GetNextPortion();
1016             }
1017 
1018             if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
1019                 pPor->GetNextPortion() && pPor->GetNextPortion()->InFixGrp() )
1020             {
1021                 // All special portions have to be skipped
1022                 // Taking the German word "zusammen" as example: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
1023                 // Without the adjustment we end up in front of '-', with the
1024                 // adjustment in front of the 's'.
1025                 while( pPor && !pPor->GetLen() )
1026                 {
1027                     nX += pPor->Width();
1028                     if( !pPor->IsMarginPortion() )
1029                     {
1030                         nPorHeight = pPor->Height();
1031                         nPorAscent = pPor->GetAscent();
1032                     }
1033                     pPor = pPor->GetNextPortion();
1034                 }
1035             }
1036             if( pPor && pCMS )
1037             {
1038                 if( pCMS->m_bFieldInfo && pPor->InFieldGrp() && pPor->Width() )
1039                     pOrig->Width( pPor->Width() );
1040                 if( pPor->IsDropPortion() )
1041                 {
1042                     nPorAscent = static_cast<SwDropPortion*>(pPor)->GetDropHeight();
1043                     // The drop height is only calculated, if we have more than
1044                     // one line. Otherwise it is 0.
1045                     if ( ! nPorAscent)
1046                         nPorAscent = pPor->Height();
1047                     nPorHeight = nPorAscent;
1048                     pOrig->Height( nPorHeight +
1049                         static_cast<SwDropPortion*>(pPor)->GetDropDescent() );
1050                     if( nTmpHeight < pOrig->Height() )
1051                     {
1052                         nTmpAscent = nPorAscent;
1053                         nTmpHeight = sal_uInt16( pOrig->Height() );
1054                     }
1055                 }
1056                 if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
1057                     aInf.GetIdx() == nOfst )
1058                 {
1059                     if( !pPor->IsFlyPortion() && pPor->Height() &&
1060                         pPor->GetAscent() )
1061                     {
1062                         nPorHeight = pPor->Height();
1063                         nPorAscent = pPor->GetAscent();
1064                     }
1065                     SwTwips nTmp;
1066                     if (TextFrameIndex(2) > pPor->GetLen())
1067                     {
1068                         nTmp = pPor->Width();
1069                         if ( pPor->InSpaceGrp() && nSpaceAdd )
1070                             nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1071                     }
1072                     else
1073                     {
1074                         const bool bOldOnWin = aInf.OnWin();
1075                         TextFrameIndex const nOldLen = pPor->GetLen();
1076                         pPor->SetLen( TextFrameIndex(1) );
1077                         aInf.SetLen( pPor->GetLen() );
1078                         SeekAndChg( aInf );
1079                         aInf.SetOnWin( false ); // no BULLETs!
1080                         aInf.SetKanaComp( pKanaComp );
1081                         aInf.SetKanaIdx( nKanaIdx );
1082                         nTmp = pPor->GetTextSize( aInf ).Width();
1083                         aInf.SetOnWin( bOldOnWin );
1084                         if ( pPor->InSpaceGrp() && nSpaceAdd )
1085                             nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1086                         pPor->SetLen( nOldLen );
1087                     }
1088                     pOrig->Width( nTmp );
1089                 }
1090 
1091                 // travel inside field portion?
1092                 if ( pCMS->m_pSpecialPos )
1093                 {
1094                     // apply attributes to font
1095                     Seek( nOfst );
1096                     lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
1097                 }
1098             }
1099         }
1100 
1101         // special case: We are at the beginning of a BidiPortion or
1102         // directly behind a BidiPortion
1103         if ( pCMS &&
1104                 ( pLastBidiPor ||
1105                 ( pPor &&
1106                   pPor->IsMultiPortion() &&
1107                   static_cast<SwMultiPortion*>(pPor)->IsBidi() ) ) )
1108         {
1109             // we determine if the cursor has to blink before or behind
1110             // the bidi portion
1111             if ( pLastBidiPor )
1112             {
1113                 const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
1114 
1115                 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1116                 {
1117                     // we came from inside the bidi portion, we want to blink
1118                     // behind the portion
1119                     pOrig->Pos().AdjustX( -nLastBidiPorWidth );
1120 
1121                     // Again, there is a special case: logically behind
1122                     // the portion can actually mean that the cursor is inside
1123                     // the portion. This can happen is the last portion
1124                     // inside the bidi portion is a nested bidi portion
1125                     SwLineLayout& rLineLayout =
1126                             static_cast<SwMultiPortion*>(pLastBidiPor)->GetRoot();
1127 
1128                     const SwLinePortion *pLast = rLineLayout.FindLastPortion();
1129                     if ( pLast->IsMultiPortion() )
1130                     {
1131                         OSL_ENSURE( static_cast<const SwMultiPortion*>(pLast)->IsBidi(),
1132                                  "Non-BidiPortion inside BidiPortion" );
1133                         TextFrameIndex const nIdx = aInf.GetIdx();
1134                         // correct the index before using CalcSpacing.
1135                         aInf.SetIdx(nLastBidiIdx);
1136                         pOrig->Pos().AdjustX(pLast->Width() +
1137                                             pLast->CalcSpacing( nSpaceAdd, aInf ) );
1138                         aInf.SetIdx(nIdx);
1139                     }
1140                 }
1141             }
1142             else
1143             {
1144                 const sal_uInt8 nPortionLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1145 
1146                 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1147                 {
1148                     // we came from inside the bidi portion, we want to blink
1149                     // behind the portion
1150                     pOrig->Pos().AdjustX(pPor->Width() +
1151                                         pPor->CalcSpacing( nSpaceAdd, aInf ) );
1152                 }
1153             }
1154         }
1155 
1156         pOrig->Pos().AdjustX(nX );
1157 
1158         if ( pCMS && pCMS->m_bRealHeight )
1159         {
1160             nTmpAscent = AdjustBaseLine( *m_pCurr, nullptr, nPorHeight, nPorAscent );
1161             if ( nTmpAscent > nPorAscent )
1162                 pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
1163             else
1164                 pCMS->m_aRealHeight.setX( 0 );
1165             OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
1166             if ( nTmpHeight > nPorHeight )
1167                 pCMS->m_aRealHeight.setY( nPorHeight );
1168             else
1169                 pCMS->m_aRealHeight.setY( nTmpHeight );
1170         }
1171     }
1172 }
1173 
GetCharRect(SwRect * pOrig,TextFrameIndex const nOfst,SwCursorMoveState * pCMS,const long nMax)1174 void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
1175                                SwCursorMoveState* pCMS, const long nMax )
1176 {
1177     CharCursorToLine(nOfst);
1178 
1179     // Indicates that a position inside a special portion (field, number portion)
1180     // is requested.
1181     const bool bSpecialPos = pCMS && pCMS->m_pSpecialPos;
1182     TextFrameIndex nFindOfst = nOfst;
1183 
1184     if ( bSpecialPos )
1185     {
1186         const SwSPExtendRange nExtendRange = pCMS->m_pSpecialPos->nExtendRange;
1187 
1188         OSL_ENSURE( ! pCMS->m_pSpecialPos->nLineOfst || SwSPExtendRange::BEFORE != nExtendRange,
1189                 "LineOffset AND Number Portion?" );
1190 
1191         // portions which are behind the string
1192         if ( SwSPExtendRange::BEHIND == nExtendRange )
1193             ++nFindOfst;
1194 
1195         // skip lines for fields which cover more than one line
1196         for ( sal_uInt16 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
1197             Next();
1198     }
1199 
1200     // If necessary, as catch up, do the adjustment
1201     GetAdjusted();
1202 
1203     const Point aCharPos( GetTopLeft() );
1204 
1205     GetCharRect_( pOrig, nFindOfst, pCMS );
1206 
1207     // This actually would have to be "-1 LogicToPixel", but that seems too
1208     // expensive, so it's a value (-12), that should hopefully be OK.
1209     const SwTwips nTmpRight = Right() - 12;
1210 
1211     pOrig->Pos().AdjustX(aCharPos.X() );
1212     pOrig->Pos().AdjustY(aCharPos.Y() );
1213 
1214     if( pCMS && pCMS->m_b2Lines && pCMS->m_p2Lines )
1215     {
1216         pCMS->m_p2Lines->aLine.Pos().AdjustX(aCharPos.X() );
1217         pCMS->m_p2Lines->aLine.Pos().AdjustY(aCharPos.Y() );
1218         pCMS->m_p2Lines->aPortion.Pos().AdjustX(aCharPos.X() );
1219         pCMS->m_p2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
1220     }
1221 
1222     const bool bTabOverMargin = GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_OVER_MARGIN);
1223     // Make sure the cursor respects the right margin, unless in compat mode, where the tab size has priority over the margin size.
1224     if( pOrig->Left() > nTmpRight && !bTabOverMargin)
1225         pOrig->Pos().setX( nTmpRight );
1226 
1227     if( nMax )
1228     {
1229         if( pOrig->Top() + pOrig->Height() > nMax )
1230         {
1231             if( pOrig->Top() > nMax )
1232                 pOrig->Top( nMax );
1233             pOrig->Height( nMax - pOrig->Top() );
1234         }
1235         if ( pCMS && pCMS->m_bRealHeight && pCMS->m_aRealHeight.Y() >= 0 )
1236         {
1237             long nTmp = pCMS->m_aRealHeight.X() + pOrig->Top();
1238             if( nTmp >= nMax )
1239             {
1240                 pCMS->m_aRealHeight.setX( nMax - pOrig->Top() );
1241                 pCMS->m_aRealHeight.setY( 0 );
1242             }
1243             else if( nTmp + pCMS->m_aRealHeight.Y() > nMax )
1244                 pCMS->m_aRealHeight.setY( nMax - nTmp );
1245         }
1246     }
1247     long nOut = pOrig->Right() - GetTextFrame()->getFrameArea().Right();
1248     if( nOut > 0 )
1249     {
1250         if( GetTextFrame()->getFrameArea().Width() < GetTextFrame()->getFramePrintArea().Left()
1251                                    + GetTextFrame()->getFramePrintArea().Width() )
1252             nOut += GetTextFrame()->getFrameArea().Width() - GetTextFrame()->getFramePrintArea().Left()
1253                     - GetTextFrame()->getFramePrintArea().Width();
1254         if( nOut > 0 )
1255             pOrig->Pos().AdjustX( -(nOut + 10) );
1256     }
1257 }
1258 
1259 /**
1260  * Determines if SwTextCursor::GetCursorOfst() should consider the next portion when calculating the
1261  * doc model position from a Point.
1262  */
ConsiderNextPortionForCursorOffset(const SwLinePortion * pPor,sal_uInt16 nWidth30,sal_uInt16 nX)1263 static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, sal_uInt16 nWidth30, sal_uInt16 nX)
1264 {
1265     if (!pPor->GetNextPortion())
1266     {
1267         return false;
1268     }
1269 
1270     // If we're past the target position, stop the iteration in general.
1271     // Exception: don't stop the iteration between as-char fly portions and their comments.
1272     if (nWidth30 >= nX && (!pPor->IsFlyCntPortion() || !pPor->GetNextPortion()->IsPostItsPortion()))
1273     {
1274         return false;
1275     }
1276 
1277     return !pPor->IsBreakPortion();
1278 }
1279 
1280 // Return: Offset in String
GetCursorOfst(SwPosition * pPos,const Point & rPoint,bool bChgNode,SwCursorMoveState * pCMS) const1281 TextFrameIndex SwTextCursor::GetCursorOfst( SwPosition *pPos, const Point &rPoint,
1282                                     bool bChgNode, SwCursorMoveState* pCMS ) const
1283 {
1284     // If necessary, as catch up, do the adjustment
1285     GetAdjusted();
1286 
1287     const OUString &rText = GetInfo().GetText();
1288     TextFrameIndex nOffset(0);
1289 
1290     // x is the horizontal offset within the line.
1291     SwTwips x = rPoint.X();
1292     const SwTwips nLeftMargin  = GetLineStart();
1293     SwTwips nRightMargin = GetLineEnd() +
1294         ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 );
1295     if( nRightMargin == nLeftMargin )
1296         nRightMargin += 30;
1297 
1298     const bool bLeftOver = x < nLeftMargin;
1299     if( bLeftOver )
1300         x = nLeftMargin;
1301     const bool bRightOver = x > nRightMargin;
1302     if( bRightOver )
1303         x = nRightMargin;
1304 
1305     const bool bRightAllowed = pCMS && ( pCMS->m_eState == MV_NONE );
1306 
1307     // Until here everything in document coordinates.
1308     x -= nLeftMargin;
1309 
1310     sal_uInt16 nX = sal_uInt16( x );
1311 
1312     // If there are attribute changes in the line, search for the paragraph,
1313     // in which nX is situated.
1314     SwLinePortion *pPor = m_pCurr->GetFirstPortion();
1315     TextFrameIndex nCurrStart = m_nStart;
1316     bool bHolePortion = false;
1317     bool bLastHyph = false;
1318 
1319     std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
1320     TextFrameIndex const nOldIdx = GetInfo().GetIdx();
1321     sal_uInt16 nSpaceIdx = 0;
1322     size_t nKanaIdx = 0;
1323     long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
1324     short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
1325 
1326     // nWidth is the width of the line, or the width of
1327     // the paragraph with the font change, in which nX is situated.
1328 
1329     sal_uInt16 nWidth = pPor->Width();
1330     if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1331     {
1332         if ( pPor->InSpaceGrp() && nSpaceAdd )
1333         {
1334             const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1335             nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1336         }
1337         if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1338             ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1339           )
1340         {
1341             if ( m_pCurr->IsSpaceAdd() )
1342             {
1343                 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1344                     nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1345                 else
1346                     nSpaceAdd = 0;
1347             }
1348 
1349             if( pKanaComp )
1350             {
1351                 if ( nKanaIdx + 1 < pKanaComp->size() )
1352                     nKanaComp = (*pKanaComp)[++nKanaIdx];
1353                 else
1354                     nKanaComp = 0;
1355             }
1356         }
1357     }
1358 
1359     sal_uInt16 nWidth30;
1360     if ( pPor->IsPostItsPortion() )
1361         nWidth30 = 30 + pPor->GetViewWidth( GetInfo() ) / 2;
1362     else
1363         nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1364                      30 :
1365                      nWidth;
1366 
1367     while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
1368     {
1369         nX = nX - nWidth;
1370         nCurrStart = nCurrStart + pPor->GetLen();
1371         bHolePortion = pPor->IsHolePortion();
1372         pPor = pPor->GetNextPortion();
1373         nWidth = pPor->Width();
1374         if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1375         {
1376             if ( pPor->InSpaceGrp() && nSpaceAdd )
1377             {
1378                 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1379                 nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1380             }
1381 
1382             if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1383                 ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1384               )
1385             {
1386                 if ( m_pCurr->IsSpaceAdd() )
1387                 {
1388                     if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1389                         nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1390                     else
1391                         nSpaceAdd = 0;
1392                 }
1393 
1394                 if ( pKanaComp )
1395                 {
1396                     if( nKanaIdx + 1 < pKanaComp->size() )
1397                         nKanaComp = (*pKanaComp)[++nKanaIdx];
1398                     else
1399                         nKanaComp = 0;
1400                 }
1401             }
1402         }
1403 
1404         if ( pPor->IsPostItsPortion() )
1405             nWidth30 = 30 +  pPor->GetViewWidth( GetInfo() ) / 2;
1406         else
1407             nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1408                          30 :
1409                          nWidth;
1410         if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1411             bLastHyph = pPor->InHyphGrp();
1412     }
1413 
1414     const bool bLastPortion = (nullptr == pPor->GetNextPortion());
1415 
1416     if( nX==nWidth )
1417     {
1418         SwLinePortion *pNextPor = pPor->GetNextPortion();
1419         while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
1420         {
1421             nCurrStart = nCurrStart + pPor->GetLen();
1422             pPor = pNextPor;
1423             if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1424                 bLastHyph = pPor->InHyphGrp();
1425             pNextPor = pPor->GetNextPortion();
1426         }
1427     }
1428 
1429     const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nOldIdx );
1430 
1431     TextFrameIndex nLength = pPor->GetLen();
1432 
1433     const bool bFieldInfo = pCMS && pCMS->m_bFieldInfo;
1434 
1435     if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
1436         ( pPor->InNumberGrp() && !pPor->IsFootnoteNumPortion() ) ||
1437         ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
1438         pCMS->m_bPosCorr = true;
1439 
1440     // #i27615#
1441     if (pCMS && pCMS->m_bInFrontOfLabel)
1442     {
1443         if (! (2 * nX < nWidth && pPor->InNumberGrp() &&
1444                !pPor->IsFootnoteNumPortion()))
1445             pCMS->m_bInFrontOfLabel = false;
1446     }
1447 
1448     // 7684: We are exactly ended up at their HyphPortion. It is our task to
1449     // provide, that we end up in the String.
1450     // 7993: If length = 0, then we must exit...
1451     if( !nLength )
1452     {
1453         if( pCMS )
1454         {
1455             if( pPor->IsFlyPortion() && bFieldInfo )
1456                 pCMS->m_bPosCorr = true;
1457 
1458             if (!bRightOver && nX)
1459             {
1460                 if( pPor->IsFootnoteNumPortion())
1461                     pCMS->m_bFootnoteNoInfo = true;
1462                 else if (pPor->InNumberGrp() ) // #i23726#
1463                 {
1464                     pCMS->m_nInNumPortionOffset = nX;
1465                     pCMS->m_bInNumPortion = true;
1466                 }
1467             }
1468         }
1469         if( !nCurrStart )
1470             return TextFrameIndex(0);
1471 
1472         // 7849, 7816: pPor->GetHyphPortion is mandatory!
1473         if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
1474             ( pPor->IsMarginPortion() && !pPor->GetNextPortion() &&
1475               // 46598: Consider the situation: We might end up behind the last character,
1476               // in the last line of a centered paragraph
1477               nCurrStart < TextFrameIndex(rText.getLength())))
1478             --nCurrStart;
1479         else if( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->IsFollow()
1480                  && nWidth > nX )
1481         {
1482             if( bFieldInfo )
1483                 --nCurrStart;
1484             else
1485             {
1486                 sal_uInt16 nHeight = pPor->Height();
1487                 if ( !nHeight || nHeight > nWidth )
1488                     nHeight = nWidth;
1489                 if( bChgNode && nWidth - nHeight/2 > nX )
1490                     --nCurrStart;
1491             }
1492         }
1493         return nCurrStart;
1494     }
1495     if (TextFrameIndex(1) == nLength)
1496     {
1497         if ( nWidth )
1498         {
1499             // no quick return for as-character frames, we want to peek inside
1500             if (!(bChgNode && pPos && pPor->IsFlyCntPortion())
1501             // if we want to get the position inside the field, we should not return
1502                 && (!pCMS || !pCMS->m_pSpecialPos))
1503             {
1504                 if ( pPor->InFieldGrp() ||
1505                      ( pPor->IsMultiPortion() &&
1506                        static_cast<SwMultiPortion*>(pPor)->IsBidi()  ) )
1507                 {
1508                     sal_uInt16 nHeight = 0;
1509                     if( !bFieldInfo )
1510                     {
1511                         nHeight = pPor->Height();
1512                         if ( !nHeight || nHeight > nWidth )
1513                             nHeight = nWidth;
1514                     }
1515 
1516                     if( nWidth - nHeight/2 <= nX &&
1517                         ( ! pPor->InFieldGrp() ||
1518                           !static_cast<SwFieldPortion*>(pPor)->HasFollow() ) )
1519                         ++nCurrStart;
1520                 }
1521                 else if ( ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1522                     !pPor->GetNextPortion()->IsMarginPortion() &&
1523                     !pPor->GetNextPortion()->IsHolePortion() ) )
1524                          && ( nWidth/2 < nX ) &&
1525                          ( !bFieldInfo ||
1526                             ( pPor->GetNextPortion() &&
1527                               pPor->GetNextPortion()->IsPostItsPortion() ) )
1528                          && ( bRightAllowed || !bLastHyph ))
1529                     ++nCurrStart;
1530 
1531                 return nCurrStart;
1532             }
1533         }
1534         else
1535         {
1536             if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
1537                  pPor->InToxRefGrp() )
1538             {
1539                 if (pPor->IsPostItsPortion())
1540                 {
1541                     // Offset would be nCurrStart + nLength below, do the same for post-it portions.
1542                     nCurrStart += pPor->GetLen();
1543                 }
1544                 return nCurrStart;
1545             }
1546             if ( pPor->InFieldGrp() )
1547             {
1548                 if( bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow() )
1549                     ++nCurrStart;
1550                 return nCurrStart;
1551             }
1552         }
1553     }
1554 
1555     // Skip space at the end of the line
1556     if( bLastPortion && (m_pCurr->GetNext() || m_pFrame->GetFollow() )
1557         && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ' )
1558         --nLength;
1559 
1560     if( nWidth > nX ||
1561       ( nWidth == nX && pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsDouble() ) )
1562     {
1563         if( pPor->IsMultiPortion() )
1564         {
1565             // In a multi-portion we use GetCursorOfst()-function recursively
1566             SwTwips nTmpY = rPoint.Y() - m_pCurr->GetAscent() + pPor->GetAscent();
1567             // if we are in the first line of a double line portion, we have
1568             // to add a value to nTmpY for not staying in this line
1569             // we also want to skip the first line, if we are inside ruby
1570             if ( ( static_cast<SwTextSizeInfo*>(m_pInf)->IsMulti() &&
1571                    static_cast<SwTextSizeInfo*>(m_pInf)->IsFirstMulti() ) ||
1572                  ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1573                    static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
1574                 nTmpY += static_cast<SwMultiPortion*>(pPor)->Height();
1575 
1576             // Important for cursor traveling in ruby portions:
1577             // We have to set nTmpY to 0 in order to stay in the first row
1578             // if the phonetic line is the second row
1579             if (   static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1580                  ! static_cast<SwMultiPortion*>(pPor)->OnTop() )
1581                 nTmpY = 0;
1582 
1583             SwTextCursorSave aSave( const_cast<SwTextCursor*>(this), static_cast<SwMultiPortion*>(pPor),
1584                  nTmpY, nX, nCurrStart, nSpaceAdd );
1585 
1586             SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1587             if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
1588             {
1589                 const sal_uInt8 nBidiLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1590                 aLayoutModeModifier.Modify( nBidiLevel % 2 );
1591             }
1592 
1593             if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
1594             {
1595                 nTmpY -= m_nY;
1596                 if( !static_cast<SwMultiPortion*>(pPor)->IsRevers() )
1597                     nTmpY = pPor->Height() - nTmpY;
1598                 if( nTmpY < 0 )
1599                     nTmpY = 0;
1600                 nX = static_cast<sal_uInt16>(nTmpY);
1601             }
1602 
1603             if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
1604             {
1605                 const sal_uInt16 nPreWidth = static_cast<SwDoubleLinePortion*>(pPor)->PreWidth();
1606                 if ( nX > nPreWidth )
1607                     nX = nX - nPreWidth;
1608                 else
1609                     nX = 0;
1610             }
1611 
1612             return GetCursorOfst( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
1613                                 bChgNode, pCMS );
1614         }
1615         if( pPor->InTextGrp() )
1616         {
1617             sal_uInt8 nOldProp;
1618             if( GetPropFont() )
1619             {
1620                 const_cast<SwFont*>(GetFnt())->SetProportion( GetPropFont() );
1621                 nOldProp = GetFnt()->GetPropr();
1622             }
1623             else
1624                 nOldProp = 0;
1625             {
1626                 SwTextSizeInfo aSizeInf( GetInfo(), &rText, nCurrStart );
1627                 const_cast<SwTextCursor*>(this)->SeekAndChg( aSizeInf );
1628                 SwTextSlot aDiffText( &aSizeInf, static_cast<SwTextPortion*>(pPor), false, false );
1629                 SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
1630                         static_cast<SwDropPortion*>(pPor)->GetFnt() : nullptr );
1631 
1632                 SwParaPortion* pPara = const_cast<SwParaPortion*>(GetInfo().GetParaPortion());
1633                 OSL_ENSURE( pPara, "No paragraph!" );
1634 
1635                 SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
1636                                          *aSizeInf.GetOut(),
1637                                          &pPara->GetScriptInfo(),
1638                                          aSizeInf.GetText(),
1639                                          aSizeInf.GetIdx(),
1640                                          pPor->GetLen() );
1641 
1642                 // Drop portion works like a multi portion, just its parts are not portions
1643                 if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
1644                 {
1645                     SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
1646                     const SwDropPortionPart* pCurrPart = pDrop->GetPart();
1647                     sal_uInt16 nSumWidth = 0;
1648                     sal_uInt16 nSumBorderWidth = 0;
1649                     // Shift offset with the right and left border of previous parts and left border of actual one
1650                     while (pCurrPart && nSumWidth <= nX - sal_Int32(nCurrStart))
1651                     {
1652                         nSumWidth += pCurrPart->GetWidth();
1653                         if( pCurrPart->GetFont().GetLeftBorder() && !pCurrPart->GetJoinBorderWithPrev() )
1654                         {
1655                             nSumBorderWidth += pCurrPart->GetFont().GetLeftBorderSpace();
1656                         }
1657                         if (nSumWidth <= nX - sal_Int32(nCurrStart) && pCurrPart->GetFont().GetRightBorder() &&
1658                             !pCurrPart->GetJoinBorderWithNext() )
1659                         {
1660                             nSumBorderWidth += pCurrPart->GetFont().GetRightBorderSpace();
1661                         }
1662                         pCurrPart = pCurrPart->GetFollow();
1663                     }
1664                     nX = std::max(0, nX - nSumBorderWidth);
1665                 }
1666                 // Shift the offset with the left border width
1667                 else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() )
1668                 {
1669                     nX = std::max(0, nX - GetInfo().GetFont()->GetLeftBorderSpace());
1670                 }
1671 
1672                 aDrawInf.SetOfst( nX );
1673 
1674                 if ( nSpaceAdd )
1675                 {
1676                     TextFrameIndex nCharCnt(0);
1677                     // #i41860# Thai justified alignment needs some
1678                     // additional information:
1679                     aDrawInf.SetNumberOfBlanks( pPor->InTextGrp() ?
1680                                                 static_cast<const SwTextPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
1681                                                 TextFrameIndex(0) );
1682                 }
1683 
1684                 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1685                     aDrawInf.SetLen( TextFrameIndex(COMPLETE_STRING) );
1686 
1687                 aDrawInf.SetSpace( nSpaceAdd );
1688                 aDrawInf.SetFont( aSizeInf.GetFont() );
1689                 aDrawInf.SetFrame( m_pFrame );
1690                 aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
1691                 aDrawInf.SetPosMatchesBounds( pCMS && pCMS->m_bPosMatchesBounds );
1692 
1693                 if ( SwFontScript::CJK == aSizeInf.GetFont()->GetActual() &&
1694                      pPara->GetScriptInfo().CountCompChg() &&
1695                     ! pPor->InFieldGrp() )
1696                     aDrawInf.SetKanaComp( nKanaComp );
1697 
1698                 nLength = aSizeInf.GetFont()->GetCursorOfst_( aDrawInf );
1699 
1700                 // get position inside field portion?
1701                 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1702                 {
1703                     pCMS->m_pSpecialPos->nCharOfst = sal_Int32(nLength);
1704                     nLength = TextFrameIndex(0);
1705                 }
1706 
1707                 // set cursor bidi level
1708                 if ( pCMS )
1709                     pCMS->m_nCursorBidiLevel =
1710                         aDrawInf.GetCursorBidiLevel();
1711 
1712                 if( bFieldInfo && nLength == pPor->GetLen() &&
1713                     ( ! pPor->GetNextPortion() ||
1714                       ! pPor->GetNextPortion()->IsPostItsPortion() ) )
1715                     --nLength;
1716             }
1717             if( nOldProp )
1718                 const_cast<SwFont*>(GetFnt())->SetProportion( nOldProp );
1719         }
1720         else
1721         {
1722             sw::FlyContentPortion* pFlyPor(nullptr);
1723             if(bChgNode && pPos && (pFlyPor = dynamic_cast<sw::FlyContentPortion*>(pPor)))
1724             {
1725                 // JP 24.11.94: if the Position is not in Fly, then
1726                 //              we many not return with COMPLETE_STRING as value!
1727                 //              (BugId: 9692 + Change in feshview)
1728                 SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
1729                 SwFrame* pLower = pTmp->GetLower();
1730                 bool bChgNodeInner = pLower
1731                     && (pLower->IsTextFrame() || pLower->IsLayoutFrame());
1732                 Point aTmpPoint( rPoint );
1733 
1734                 if ( m_pFrame->IsRightToLeft() )
1735                     m_pFrame->SwitchLTRtoRTL( aTmpPoint );
1736 
1737                 if ( m_pFrame->IsVertical() )
1738                     m_pFrame->SwitchHorizontalToVertical( aTmpPoint );
1739 
1740                 if( bChgNodeInner && pTmp->getFrameArea().IsInside( aTmpPoint ) &&
1741                     !( pTmp->IsProtected() ) )
1742                 {
1743                     pFlyPor->GetFlyCursorOfst(aTmpPoint, *pPos, pCMS);
1744                     // After a change of the frame, our font must be still
1745                     // available for/in the OutputDevice.
1746                     // For comparison: Paint and new SwFlyCntPortion !
1747                     static_cast<SwTextSizeInfo*>(m_pInf)->SelectFont();
1748 
1749                     // 6776: The pIter->GetCursorOfst is returning here
1750                     // from a nesting with COMPLETE_STRING.
1751                     return TextFrameIndex(COMPLETE_STRING);
1752                 }
1753             }
1754             else
1755                 nLength = pPor->GetCursorOfst( nX );
1756         }
1757     }
1758     nOffset = nCurrStart + nLength;
1759 
1760     // 7684: We end up in front of the HyphPortion. We must assure
1761     // that we end up in the string.
1762     // If we are at end of line in front of FlyFrames, we must proceed the same way.
1763     if( nOffset && pPor->GetLen() == nLength && pPor->GetNextPortion() &&
1764         !pPor->GetNextPortion()->GetLen() && pPor->GetNextPortion()->InHyphGrp() )
1765         --nOffset;
1766 
1767     return nOffset;
1768 }
1769 
1770 /** Looks for text portions which are inside the given rectangle
1771 
1772     For a rectangular text selection every text portions which is inside the given
1773     rectangle has to be put into the SwSelectionList as SwPaM
1774     From these SwPaM the SwCursors will be created.
1775 
1776     @param rSelList
1777     The container for the overlapped text portions
1778 
1779     @param rRect
1780     A rectangle in document coordinates, text inside this rectangle has to be
1781     selected.
1782 
1783     @return [ true, false ]
1784     true if any overlapping text portion has been found and put into list
1785     false if no portion overlaps, the list has been unchanged
1786 */
FillSelection(SwSelectionList & rSelList,const SwRect & rRect) const1787 bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
1788 {
1789     bool bRet = false;
1790     // GetPaintArea() instead getFrameArea() for negative indents
1791     SwRect aTmpFrame( GetPaintArea() );
1792     if( !rRect.IsOver( aTmpFrame ) )
1793         return false;
1794     if( rSelList.checkContext( this ) )
1795     {
1796         SwRect aRect( aTmpFrame );
1797         aRect.Intersection( rRect );
1798         SwPosition aPosL( MapViewToModelPos(TextFrameIndex(0)) );
1799         if( IsEmpty() )
1800         {
1801             SwPaM *pPam = new SwPaM( aPosL, aPosL );
1802             rSelList.insertPaM( pPam );
1803         }
1804         else if( aRect.HasArea() )
1805         {
1806             SwPosition aOld(aPosL.nNode.GetNodes().GetEndOfContent());
1807             SwPosition aPosR( aPosL );
1808             Point aPoint;
1809             SwTextInfo aInf( const_cast<SwTextFrame*>(this) );
1810             SwTextIter aLine( const_cast<SwTextFrame*>(this), &aInf );
1811             // We have to care for top-to-bottom layout, where right becomes top etc.
1812             SwRectFnSet aRectFnSet(this);
1813             SwTwips nTop = aRectFnSet.GetTop(aRect);
1814             SwTwips nBottom = aRectFnSet.GetBottom(aRect);
1815             SwTwips nLeft = aRectFnSet.GetLeft(aRect);
1816             SwTwips nRight = aRectFnSet.GetRight(aRect);
1817             SwTwips nY = aLine.Y(); // Top position of the first line
1818             SwTwips nLastY = nY;
1819             while( nY < nTop && aLine.Next() ) // line above rectangle
1820             {
1821                 nLastY = nY;
1822                 nY = aLine.Y();
1823             }
1824             bool bLastLine = false;
1825             if( nY < nTop && !aLine.GetNext() )
1826             {
1827                 bLastLine = true;
1828                 nY += aLine.GetLineHeight();
1829             }
1830             do // check the lines for overlapping
1831             {
1832                 if( nLastY < nTop ) // if the last line was above rectangle
1833                     nLastY = nTop;
1834                 if( nY > nBottom ) // if the current line leaves the rectangle
1835                     nY = nBottom;
1836                 if( nY >= nLastY ) // gotcha: overlapping
1837                 {
1838                     nLastY += nY;
1839                     nLastY /= 2;
1840                     if( aRectFnSet.IsVert() )
1841                     {
1842                         aPoint.setX( nLastY );
1843                         aPoint.setY( nLeft );
1844                     }
1845                     else
1846                     {
1847                         aPoint.setX( nLeft );
1848                         aPoint.setY( nLastY );
1849                     }
1850                     // Looking for the position of the left border of the rectangle
1851                     // in this text line
1852                     SwCursorMoveState aState( MV_UPDOWN );
1853                     if( GetCursorOfst( &aPosL, aPoint, &aState ) )
1854                     {
1855                         if( aRectFnSet.IsVert() )
1856                         {
1857                             aPoint.setX( nLastY );
1858                             aPoint.setY( nRight );
1859                         }
1860                         else
1861                         {
1862                             aPoint.setX( nRight );
1863                             aPoint.setY( nLastY );
1864                         }
1865                         // If we get a right position and if the left position
1866                         // is not the same like the left position of the line before
1867                         // which could happen e.g. for field portions or fly frames
1868                         // a SwPaM will be inserted with these positions
1869                         if( GetCursorOfst( &aPosR, aPoint, &aState ) &&
1870                             aOld != aPosL)
1871                         {
1872                             SwPaM *pPam = new SwPaM( aPosL, aPosR );
1873                             rSelList.insertPaM( pPam );
1874                             aOld = aPosL;
1875                         }
1876                     }
1877                 }
1878                 if( aLine.Next() )
1879                 {
1880                     nLastY = nY;
1881                     nY = aLine.Y();
1882                 }
1883                 else if( !bLastLine )
1884                 {
1885                     bLastLine = true;
1886                     nLastY = nY;
1887                     nY += aLine.GetLineHeight();
1888                 }
1889                 else
1890                     break;
1891             }while( nLastY < nBottom );
1892         }
1893     }
1894     if( GetDrawObjs() )
1895     {
1896         const SwSortedObjs &rObjs = *GetDrawObjs();
1897         for (SwAnchoredObject* pAnchoredObj : rObjs)
1898         {
1899             if( dynamic_cast< const SwFlyFrame *>( pAnchoredObj ) ==  nullptr )
1900                 continue;
1901             const SwFlyFrame* pFly = static_cast<const SwFlyFrame*>(pAnchoredObj);
1902             if( pFly->IsFlyInContentFrame() && pFly->FillSelection( rSelList, rRect ) )
1903                 bRet = true;
1904         }
1905     }
1906     return bRet;
1907 }
1908 
1909 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1910