Bug Summary

File:home/maarten/src/libreoffice/core/sw/source/core/text/itrcrsr.cxx
Warning:line 1056, column 25
Value stored to 'nTmpAscent' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

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