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