1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
25 #include <swselectionlist.hxx>
26 #include <sortedobjs.hxx>
27 #include <editeng/adjustitem.hxx>
28 #include <editeng/lspcitem.hxx>
29 #include <editeng/lrspitem.hxx>
31 #include <tgrditem.hxx>
32 #include <IDocumentSettingAccess.hxx>
33 #include <pagefrm.hxx>
37 #include <flyfrms.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>
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;
66 const OUString
* pString
= nullptr;
67 const SwLinePortion
* pPor
= &rPor
;
70 if ( pPor
->InFieldGrp() )
72 sString
= static_cast<const SwFieldPortion
*>(pPor
)->GetExp();
74 nFieldLen
= pString
->getLength();
82 if ( ! pPor
->GetNextPortion() || nFieldIdx
+ nFieldLen
> nCharOfst
)
85 nFieldIdx
+= nFieldLen
;
86 rOrig
.Pos().AdjustX(pPor
->Width() );
87 pPor
= pPor
->GetNextPortion();
91 OSL_ENSURE( nCharOfst
>= nFieldIdx
, "Request of position inside field failed" );
92 sal_Int32 nLen
= nCharOfst
- nFieldIdx
+ 1;
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() :
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
) ?
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 );
130 bool IsLabelAlignmentActive( const SwTextNode
& rTextNode
)
134 if ( rTextNode
.GetNumRule() )
136 int nListLevel
= rTextNode
.GetActualListLevel();
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
)
154 } // end of anonymous namespace
156 void SwTextMargin::CtorInitTextMargin( SwTextFrame
*pNewFrame
, SwTextSizeInfo
*pNewInf
)
158 CtorInitTextIter( pNewFrame
, 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());
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() -
196 // rSpace.GetLeft() + rSpace.GetTextLeft();
197 (rTextLeftMargin
.ResolveLeft(rFirstLine
, stMetrics
)
198 - rTextLeftMargin
.ResolveTextLeft(stMetrics
));
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() -
212 (rTextLeftMargin
.ResolveLeft(rFirstLine
, stMetrics
)
213 - rTextLeftMargin
.ResolveTextLeft(stMetrics
));
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() )
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
)
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();
266 switch( pSpace
->GetLineSpaceRule() )
268 case SvxLineSpaceRule::Auto
:
270 case SvxLineSpaceRule::Min
:
272 if( nFirstLineOfs
< pSpace
->GetLineHeight() )
273 nFirstLineOfs
= pSpace
->GetLineHeight();
276 case SvxLineSpaceRule::Fix
:
277 nFirstLineOfs
= pSpace
->GetLineHeight();
279 default: OSL_FAIL( ": unknown LineSpaceRule" );
281 switch( pSpace
->GetInterLineSpaceRule() )
283 case SvxInterLineSpaceRule::Off
:
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%...
291 nTmp
= nTmp
? 50 : 100;
293 nTmp
*= nFirstLineOfs
;
297 nFirstLineOfs
= nTmp
;
300 case SvxInterLineSpaceRule::Fix
:
302 nFirstLineOfs
+= pSpace
->GetInterLineSpace();
305 default: OSL_FAIL( ": unknown InterLineSpaceRule" );
311 nFirstLineOfs
= nFLOfst
;
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
;
333 mnFirst
= m_pFrame
->getFrameArea().Left()
334 + std::max(rTextLeftMargin
.ResolveTextLeft(stMetrics
) + nLMWithNum
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
;
365 mnTabLeft
= pNode
->GetLeftMarginForTabCalculation();
370 void SwTextMargin::DropInit()
372 mnDropLeft
= mnDropLines
= mnDropHeight
= mnDropDescent
= 0;
373 const SwParaPortion
*pPara
= GetInfo().GetParaPortion();
376 const SwDropPortion
*pPorDrop
= pPara
->FindDropPortion();
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;
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())
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();
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);
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());
462 if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
464 GetCharRect( pOrig
, nOfst
, pCMS
, nMax
);
468 // If necessary, as catch up, do the adjustment
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
484 if( pPor
->InTextGrp() || ( pPor
->GetLen() && !pPor
->IsFlyPortion()
485 && !pPor
->IsHolePortion() ) || pPor
->IsBreakPortion() )
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
);
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
);
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
);
552 SwTwips nPorHeight
= nTmpHeight
;
553 SwTwips nPorAscent
= nTmpAscent
;
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;
563 tools::Long nSpaceAdd
= m_pCurr
->IsSpaceAdd() ? m_pCurr
->GetLLSpaceAdd( 0 ) : 0;
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
)
575 if ( pPor
->InSpaceGrp() && nSpaceAdd
)
576 nX
+= pPor
->CalcSpacing( nSpaceAdd
, aInf
);
579 // 8670: EndPortions count once as TextPortions.
580 // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
581 if( pPor
->InTextGrp() || pPor
->IsBreakPortion() || pPor
->InTabGrp() )
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
);
596 if( pKanaComp
&& ( nKanaIdx
+ 1 ) < pKanaComp
->size() )
599 if( pPor
->InFixMargGrp() )
601 if( pPor
->IsMarginPortion() )
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
);
614 if( pKanaComp
&& ( nKanaIdx
+ 1 ) < pKanaComp
->size() )
618 pPor
= pPor
->GetNextPortion();
623 // There's just Spezialportions.
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()
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
);
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
);
677 ( nKanaIdx
+ 1 ) < pKanaComp
->size()
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
);
697 if( pKanaComp
&& ( nKanaIdx
+ 1 ) < pKanaComp
->size() )
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
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();
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
);
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
;
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
;
746 pCMS
->m_p2Lines
->nMultiType
= MultiPortionType::RUBY
;
748 SwTwips nTmpWidth
= pPor
->Width();
750 nTmpWidth
+= pPor
->CalcSpacing(nSpaceAdd
, aInf
);
752 SwRect
aRect( Point(aCharPos
.X() + nX
, pOrig
->Top() ),
753 Size( nTmpWidth
, pPor
->Height() ) );
756 pCMS
->m_p2Lines
->aPortion
= aRect
;
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() )
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() ) )
784 // in grid mode we may only add the height of the
785 // ruby line if ruby line is on top
787 static_cast<SwMultiPortion
*>(pPor
)->IsRuby() &&
788 static_cast<SwMultiPortion
*>(pPor
)->OnTop() )
789 nOffset
= nRubyHeight
;
791 nOffset
= GetLineHeight();
793 pOrig
->Pos().AdjustY(nOffset
);
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
;
812 m_pCurr
->Height( pOldCurr
->Height() - nRubyHeight
);
813 m_pCurr
->SetRealHeight( pOldCurr
->GetRealHeight() -
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
);
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
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
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:
873 pOrig
->Pos().setX( nX
+ aOldPos
.X() );
874 if( static_cast<SwMultiPortion
*>(pPor
)->IsRevers() )
875 pOrig
->Pos().setY( aOldPos
.Y() + nTmp
);
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(
893 pCMS
->m_aRealHeight
.Y() ) / 2 );
895 pCMS
->m_aRealHeight
.setX(
897 pCMS
->m_aRealHeight
.X() +
898 pCMS
->m_aRealHeight
.Y() );
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() );
914 pOrig
->Pos().AdjustX(nX
);
916 if( static_cast<SwMultiPortion
*>(pPor
)->HasBrackets() )
917 pOrig
->Pos().AdjustX(
918 static_cast<SwDoubleLinePortion
*>(pPor
)->PreWidth() );
922 SwDoubleLinePortion::ResetSpaceAdd( m_pCurr
);
925 m_nStart
= nOldStart
;
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() )
952 const bool bOldOnWin
= aInf
.OnWin();
953 aInf
.SetOnWin( false ); // no BULLETs!
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
);
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();
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();
1031 pTmp
= static_cast<SwFieldPortion
*>(pNext
);
1032 nPorHeight
= pTmp
->Height();
1033 nPorAscent
= pTmp
->GetAscent();
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
);
1049 if( pPor
->InFixMargGrp() && ! pPor
->IsMarginPortion() )
1051 if ( m_pCurr
->IsSpaceAdd() )
1053 if ( ++nSpaceIdx
< m_pCurr
->GetLLSpaceAddCount() )
1054 nSpaceAdd
= m_pCurr
->GetLLSpaceAdd( nSpaceIdx
);
1059 if( pKanaComp
&& ( nKanaIdx
+ 1 ) < pKanaComp
->size() )
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
);
1077 if( pKanaComp
&& ( nKanaIdx
+ 1 ) < pKanaComp
->size() )
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();
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.
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() &&
1132 nPorHeight
= pPor
->Height();
1133 nPorAscent
= pPor
->GetAscent();
1136 if (TextFrameIndex(2) > pPor
->GetLen())
1138 nTmp
= pPor
->Width();
1139 if ( pPor
->InSpaceGrp() && nSpaceAdd
)
1140 nTmp
+= pPor
->CalcSpacing( nSpaceAdd
, aInf
);
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());
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
1172 lcl_GetCharRectInsideField( aInf
, *pOrig
, *pCMS
, *pPor
);
1177 // special case: We are at the beginning of a BidiPortion or
1178 // directly behind a BidiPortion
1182 pPor
->IsMultiPortion() &&
1183 static_cast<SwMultiPortion
*>(pPor
)->IsBidi() ) ) )
1185 // we determine if the cursor has to blink before or behind
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
) );
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
);
1240 pCMS
->m_aRealHeight
.setX( 0 );
1241 OSL_ENSURE( nPorHeight
, "GetCharRect: Missing Portion-Height" );
1242 if ( nTmpHeight
> nPorHeight
)
1243 pCMS
->m_aRealHeight
.setY( nPorHeight
);
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)
1257 const bool bSpecialPos
= pCMS
&& pCMS
->m_pSpecialPos
;
1258 TextFrameIndex nFindOfst
= nOfst
;
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
)
1271 // skip lines for fields which cover more than one line
1272 for ( sal_Int32 i
= 0; i
< pCMS
->m_pSpecialPos
->nLineOfst
; i
++ )
1276 // If necessary, as catch up, do the adjustment
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() );
1297 if( pOrig
->Top() + pOrig
->Height() > nMax
)
1299 if( 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();
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())
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)
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();
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
)
1359 if (pLP
->InFieldGrp())
1361 SwFieldPortion
const* pField(static_cast<SwFieldPortion
const*>(pLP
));
1362 if (!pField
->IsFollow())
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
))
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
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
)
1406 const bool bLeftOver
= 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.
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
1435 ( std::abs( m_pCurr
->Width() - pPor
->Width() ) <= 1 && m_pCurr
->ExtraShrunkWidth() > 0 )
1436 ? m_pCurr
->ExtraShrunkWidth()
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
);
1459 if ( nKanaIdx
+ 1 < pKanaComp
->size() )
1460 nKanaComp
= (*pKanaComp
)[++nKanaIdx
];
1468 if ( pPor
->IsPostItsPortion() )
1471 nWidth30
= ! nWidth
&& pPor
->GetLen() && pPor
->InToxRefOrFieldGrp() ?
1475 while (ConsiderNextPortionForCursorOffset(pPor
, nWidth30
, nX
))
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
);
1503 if( nKanaIdx
+ 1 < pKanaComp
->size() )
1504 nKanaComp
= (*pKanaComp
)[++nKanaIdx
];
1511 if ( pPor
->IsPostItsPortion() )
1514 nWidth30
= ! nWidth
&& pPor
->GetLen() && pPor
->InToxRefOrFieldGrp() ?
1517 if( !pPor
->IsFlyPortion() && !pPor
->IsMarginPortion() )
1518 bLastHyph
= pPor
->InHyphGrp();
1521 const bool bLastPortion
= (nullptr == pPor
->GetNextPortion());
1525 SwLinePortion
*pNextPor
= pPor
->GetNextPortion();
1526 while( pNextPor
&& pNextPor
->InFieldGrp() && !pNextPor
->Width() )
1528 nCurrStart
+= pPor
->GetLen();
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;
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...
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;
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())))
1585 else if( pPor
->InFieldGrp() && static_cast<SwFieldPortion
*>(pPor
)->IsFollow()
1592 sal_uInt16 nHeight
= pPor
->Height();
1593 if ( !nHeight
|| nHeight
> nWidth
)
1595 if( bChgNode
&& nWidth
- nHeight
/2 > nX
)
1599 if (!pPor
->InFieldGrp() || !static_cast<SwFieldPortion
const*>(pPor
)->IsFollow()
1600 || !pCMS
|| !pCMS
->m_pSpecialPos
)
1605 if (TextFrameIndex(1) == nLength
|| pPor
->InFieldGrp())
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;
1621 nHeight
= pPor
->Height();
1622 if ( !nHeight
|| 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();
1640 else if ( ( !pPor
->IsFlyPortion() || ( pPor
->GetNextPortion() &&
1641 !pPor
->GetNextPortion()->IsMarginPortion() &&
1642 !pPor
->GetNextPortion()->IsHolePortion() ) )
1643 && ( nWidth
/2 < nX
) &&
1645 ( pPor
->GetNextPortion() &&
1646 pPor
->GetNextPortion()->IsPostItsPortion() ) )
1647 && ( bRightAllowed
|| !bLastHyph
))
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();
1669 if ( pPor
->InFieldGrp() )
1671 if( bRightOver
&& !static_cast<SwFieldPortion
*>(pPor
)->HasFollow() )
1673 nCurrStart
+= static_cast<SwFieldPortion
*>(pPor
)->GetFieldLen();
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] == ' ')
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() )
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() )
1724 if( !static_cast<SwMultiPortion
*>(pPor
)->IsRevers() )
1725 nTmpY
= pPor
->Height() - nTmpY
;
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
)
1740 return GetModelPositionForViewPoint( pPos
, Point( GetLineStart() + nX
, rPoint
.Y() ),
1743 if( pPor
->InTextGrp() || pPor
->IsHolePortion() )
1748 const_cast<SwFont
*>(GetFnt())->SetProportion( GetPropFont() );
1749 nOldProp
= GetFnt()->GetPropr();
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
);
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())
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
))
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
1859 pCMS
->m_nCursorBidiLevel
=
1860 aDrawInf
.GetCursorBidiLevel();
1863 const_cast<SwFont
*>(GetFnt())->SetProportion( nOldProp
);
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
);
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() )
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.
1924 The container for the overlapped text portions
1927 A rectangle in document coordinates, text inside this rectangle has to be
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
1937 // GetPaintArea() instead getFrameArea() for negative indents
1938 SwRect
aTmpFrame( GetPaintArea() );
1939 if( !rRect
.Overlaps( aTmpFrame
) )
1941 if( rSelList
.checkContext( this ) )
1943 SwRect
aRect( aTmpFrame
);
1944 aRect
.Intersection( rRect
);
1945 SwPosition
aPosL( MapViewToModelPos(TextFrameIndex(0)) );
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
);
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
1971 bool bLastLine
= false;
1972 if( nY
< nTop
&& !aLine
.GetNext() )
1975 nY
+= aLine
.GetLineHeight();
1977 do // check the lines for overlapping
1979 if( nLastY
< nTop
) // if the last line was above rectangle
1981 if( nY
> nBottom
) // if the current line leaves the rectangle
1983 if( nY
>= nLastY
) // gotcha: overlapping
1987 if( aRectFnSet
.IsVert() )
1989 aPoint
.setX( nLastY
);
1990 aPoint
.setY( nLeft
);
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
);
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
) &&
2019 SwPaM
*pPam
= new SwPaM( aPosL
, aPosR
);
2020 rSelList
.insertPaM( pPam
);
2030 else if( !bLastLine
)
2034 nY
+= aLine
.GetLineHeight();
2038 }while( nLastY
< nBottom
);
2043 const SwSortedObjs
&rObjs
= *GetDrawObjs();
2044 for (SwAnchoredObject
* pAnchoredObj
: rObjs
)
2046 const SwFlyFrame
* pFly
= pAnchoredObj
->DynCastFlyFrame();
2049 if( pFly
->IsFlyInContentFrame() && pFly
->FillSelection( rSelList
, rRect
) )
2056 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */