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