xref: /core/sw/source/core/text/itrtxt.cxx (revision f55a9b77)
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 <flyfrm.hxx>
22 #include <paratr.hxx>
23 #include <vcl/outdev.hxx>
24 #include <editeng/paravertalignitem.hxx>
25 
26 #include "pormulti.hxx"
27 #include <pagefrm.hxx>
28 #include <pagedesc.hxx>
29 #include <tgrditem.hxx>
30 #include "porfld.hxx"
31 
32 #include "itrtxt.hxx"
33 #include <txtfrm.hxx>
34 #include "porfly.hxx"
35 
36 void SwTextIter::CtorInitTextIter( SwTextFrame *pNewFrame, SwTextInfo *pNewInf )
37 {
38     SwTextNode *pNode = pNewFrame->GetTextNode();
39 
40     assert(pNewFrame->GetPara());
41 
42     CtorInitAttrIter( *pNode, pNewFrame->GetPara()->GetScriptInfo(), pNewFrame );
43 
44     m_pFrame = pNewFrame;
45     m_pInf = pNewInf;
46     m_aLineInf.CtorInitLineInfo( pNode->GetSwAttrSet(), *pNode );
47     m_nFrameStart = m_pFrame->getFrameArea().Pos().Y() + m_pFrame->getFramePrintArea().Pos().Y();
48     SwTextIter::Init();
49 
50     // Order is important: only execute FillRegister if GetValue!=0
51     m_bRegisterOn = pNode->GetSwAttrSet().GetRegister().GetValue()
52         && m_pFrame->FillRegister( m_nRegStart, m_nRegDiff );
53 }
54 
55 void SwTextIter::Init()
56 {
57     m_pCurr = m_pInf->GetParaPortion();
58     m_nStart = m_pInf->GetTextStart();
59     m_nY = m_nFrameStart;
60     m_bPrev = true;
61     m_pPrev = nullptr;
62     m_nLineNr = 1;
63 }
64 
65 void SwTextIter::CalcAscentAndHeight( sal_uInt16 &rAscent, sal_uInt16 &rHeight ) const
66 {
67     rHeight = GetLineHeight();
68     rAscent = m_pCurr->GetAscent() + rHeight - m_pCurr->Height();
69 }
70 
71 SwLineLayout *SwTextIter::GetPrev_()
72 {
73     m_pPrev = nullptr;
74     m_bPrev = true;
75     SwLineLayout *pLay = m_pInf->GetParaPortion();
76     if( m_pCurr == pLay )
77         return nullptr;
78     while( pLay->GetNext() != m_pCurr )
79         pLay = pLay->GetNext();
80     return m_pPrev = pLay;
81 }
82 
83 const SwLineLayout *SwTextIter::GetPrev()
84 {
85     if(! m_bPrev)
86         GetPrev_();
87     return m_pPrev;
88 }
89 
90 const SwLineLayout *SwTextIter::Prev()
91 {
92     if( !m_bPrev )
93         GetPrev_();
94     if( m_pPrev )
95     {
96         m_bPrev = false;
97         m_pCurr = m_pPrev;
98         m_nStart = m_nStart - m_pCurr->GetLen();
99         m_nY = m_nY - GetLineHeight();
100         if( !m_pCurr->IsDummy() && !(--m_nLineNr) )
101             ++m_nLineNr;
102         return m_pCurr;
103     }
104     else
105         return nullptr;
106 }
107 
108 const SwLineLayout *SwTextIter::Next()
109 {
110     if(m_pCurr->GetNext())
111     {
112         m_pPrev = m_pCurr;
113         m_bPrev = true;
114         m_nStart = m_nStart + m_pCurr->GetLen();
115         m_nY += GetLineHeight();
116         if( m_pCurr->GetLen() || ( m_nLineNr>1 && !m_pCurr->IsDummy() ) )
117             ++m_nLineNr;
118         return m_pCurr = m_pCurr->GetNext();
119     }
120     else
121         return nullptr;
122 }
123 
124 const SwLineLayout *SwTextIter::NextLine()
125 {
126     const SwLineLayout *pNext = Next();
127     while( pNext && pNext->IsDummy() && pNext->GetNext() )
128     {
129         pNext = Next();
130     }
131     return pNext;
132 }
133 
134 const SwLineLayout *SwTextIter::GetNextLine() const
135 {
136     const SwLineLayout *pNext = m_pCurr->GetNext();
137     while( pNext && pNext->IsDummy() && pNext->GetNext() )
138     {
139         pNext = pNext->GetNext();
140     }
141     return pNext;
142 }
143 
144 const SwLineLayout *SwTextIter::GetPrevLine()
145 {
146     const SwLineLayout *pRoot = m_pInf->GetParaPortion();
147     if( pRoot == m_pCurr )
148         return nullptr;
149     const SwLineLayout *pLay = pRoot;
150 
151     while( pLay->GetNext() != m_pCurr )
152         pLay = pLay->GetNext();
153 
154     if( pLay->IsDummy() )
155     {
156         const SwLineLayout *pTmp = pRoot;
157         pLay = pRoot->IsDummy() ? nullptr : pRoot;
158         while( pTmp->GetNext() != m_pCurr )
159         {
160             if( !pTmp->IsDummy() )
161                 pLay = pTmp;
162             pTmp = pTmp->GetNext();
163         }
164     }
165 
166     // If nothing has changed, then there are only dummy's
167     return pLay;
168 }
169 
170 const SwLineLayout *SwTextIter::PrevLine()
171 {
172     const SwLineLayout *pMyPrev = Prev();
173     if( !pMyPrev )
174         return nullptr;
175 
176     const SwLineLayout *pLast = pMyPrev;
177     while( pMyPrev && pMyPrev->IsDummy() )
178     {
179         pLast = pMyPrev;
180         pMyPrev = Prev();
181     }
182     return pMyPrev ? pMyPrev : pLast;
183 }
184 
185 void SwTextIter::Bottom()
186 {
187     while( Next() )
188     {
189         // nothing
190     }
191 }
192 
193 void SwTextIter::CharToLine(const sal_Int32 nChar)
194 {
195     while( m_nStart + m_pCurr->GetLen() <= nChar && Next() )
196         ;
197     while( m_nStart > nChar && Prev() )
198         ;
199 }
200 
201 // 1170: takes into account ambiguities:
202 const SwLineLayout *SwTextCursor::CharCursorToLine( const sal_Int32 nPosition )
203 {
204     CharToLine( nPosition );
205     if( nPosition != m_nStart )
206         bRightMargin = false;
207     bool bPrevious = bRightMargin && m_pCurr->GetLen() && GetPrev() &&
208         GetPrev()->GetLen();
209     if( bPrevious && nPosition && CH_BREAK == GetInfo().GetChar( nPosition-1 ) )
210         bPrevious = false;
211     return bPrevious ? PrevLine() : m_pCurr;
212 }
213 
214 sal_uInt16 SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine,
215                                     const SwLinePortion* pPor,
216                                     sal_uInt16 nPorHeight, sal_uInt16 nPorAscent,
217                                     const bool bAutoToCentered ) const
218 {
219     if ( pPor )
220     {
221         nPorHeight = pPor->Height();
222         nPorAscent = pPor->GetAscent();
223     }
224 
225     sal_uInt16 nOfst = rLine.GetRealHeight() - rLine.Height();
226 
227     SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
228 
229     if ( pGrid && GetInfo().SnapToGrid() && pGrid->IsSquaredMode() )
230     {
231         const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight();
232         const bool bRubyTop = ! pGrid->GetRubyTextBelow();
233 
234         if ( GetInfo().IsMulti() )
235             // we are inside the GetCharRect recursion for multi portions
236             // we center the portion in its surrounding line
237             nOfst = ( m_pCurr->Height() - nPorHeight ) / 2 + nPorAscent;
238         else
239         {
240             // We have to take care for ruby portions.
241             // The ruby portion is NOT centered
242             nOfst = nOfst + nPorAscent;
243 
244             if ( ! pPor || ! pPor->IsMultiPortion() ||
245                  ! static_cast<const SwMultiPortion*>(pPor)->IsRuby() )
246             {
247                 // Portions which are bigger than on grid distance are
248                 // centered inside the whole line.
249 
250                 //for text refactor
251                 const sal_uInt16 nLineNet =  rLine.Height() - nRubyHeight;
252                 //const sal_uInt16 nLineNet = ( nPorHeight > nGridWidth ) ?
253                  //                           rLine.Height() - nRubyHeight :
254                  //                           nGridWidth;
255                 nOfst += ( nLineNet - nPorHeight ) / 2;
256                 if ( bRubyTop )
257                     nOfst += nRubyHeight;
258             }
259         }
260     }
261     else
262     {
263         switch ( GetLineInfo().GetVertAlign() ) {
264             case SvxParaVertAlignItem::Align::Top :
265                 nOfst = nOfst + nPorAscent;
266                 break;
267             case SvxParaVertAlignItem::Align::Center :
268                 OSL_ENSURE( rLine.Height() >= nPorHeight, "Portion height > Line height");
269                 nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent;
270                 break;
271             case SvxParaVertAlignItem::Align::Bottom :
272                 nOfst += rLine.Height() - nPorHeight + nPorAscent;
273                 break;
274             case SvxParaVertAlignItem::Align::Automatic :
275                 if ( bAutoToCentered || GetInfo().GetTextFrame()->IsVertical() )
276                 {
277                     if( GetInfo().GetTextFrame()->IsVertLR() )
278                             nOfst += rLine.Height() - ( rLine.Height() - nPorHeight ) / 2 - nPorAscent;
279                     else
280                             nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent;
281                     break;
282                 }
283                 SAL_FALLTHROUGH;
284             case SvxParaVertAlignItem::Align::Baseline :
285                 // base line
286                 nOfst = nOfst + rLine.GetAscent();
287                 break;
288         }
289     }
290 
291     return nOfst;
292 }
293 
294 void SwTextIter::TwipsToLine( const SwTwips y)
295 {
296     while( m_nY + GetLineHeight() <= y && Next() )
297         ;
298     while( m_nY > y && Prev() )
299         ;
300 }
301 
302 // Local helper function to check, if pCurr needs a field rest portion:
303 static bool lcl_NeedsFieldRest( const SwLineLayout* pCurr )
304 {
305     const SwLinePortion *pPor = pCurr->GetPortion();
306     bool bRet = false;
307     while( pPor && !bRet )
308     {
309         bRet = pPor->InFieldGrp() && static_cast<const SwFieldPortion*>(pPor)->HasFollow();
310         if( !pPor->GetPortion() || !pPor->GetPortion()->InFieldGrp() )
311             break;
312         pPor = pPor->GetPortion();
313     }
314     return bRet;
315 }
316 
317 void SwTextIter::TruncLines( bool bNoteFollow )
318 {
319     SwLineLayout *pDel = m_pCurr->GetNext();
320     const sal_Int32 nEnd = m_nStart + m_pCurr->GetLen();
321 
322     if( pDel )
323     {
324         m_pCurr->SetNext( nullptr );
325         if( GetHints() && bNoteFollow )
326         {
327             GetInfo().GetParaPortion()->SetFollowField( pDel->IsRest() ||
328                                                         lcl_NeedsFieldRest( m_pCurr ) );
329 
330             // bug 88534: wrong positioning of flys
331             SwTextFrame* pFollow = GetTextFrame()->GetFollow();
332             if ( pFollow && ! pFollow->IsLocked() &&
333                  nEnd == pFollow->GetOfst() )
334             {
335                 sal_Int32 nRangeEnd = nEnd;
336                 SwLineLayout* pLine = pDel;
337 
338                 // determine range to be searched for flys anchored as characters
339                 while ( pLine )
340                 {
341                     nRangeEnd = nRangeEnd + pLine->GetLen();
342                     pLine = pLine->GetNext();
343                 }
344 
345                 SwpHints* pTmpHints = GetTextFrame()->GetTextNode()->GetpSwpHints();
346 
347                 // examine hints in range nEnd - (nEnd + nRangeChar)
348                 for( size_t i = 0; i < pTmpHints->Count(); ++i )
349                 {
350                     const SwTextAttr* pHt = pTmpHints->Get( i );
351                     if( RES_TXTATR_FLYCNT == pHt->Which() )
352                     {
353                         // check, if hint is in our range
354                         const sal_uInt16 nTmpPos = pHt->GetStart();
355                         if ( nEnd <= nTmpPos && nTmpPos < nRangeEnd )
356                             pFollow->InvalidateRange_(
357                                 SwCharRange( nTmpPos, nTmpPos ) );
358                     }
359                 }
360             }
361         }
362         delete pDel;
363     }
364     if( m_pCurr->IsDummy() &&
365         !m_pCurr->GetLen() &&
366          m_nStart < GetTextFrame()->GetText().getLength() )
367         m_pCurr->SetRealHeight( 1 );
368     if( GetHints() )
369         m_pFrame->RemoveFootnote( nEnd );
370 }
371 
372 void SwTextIter::CntHyphens( sal_uInt8 &nEndCnt, sal_uInt8 &nMidCnt) const
373 {
374     nEndCnt = 0;
375     nMidCnt = 0;
376     if ( m_bPrev && m_pPrev && !m_pPrev->IsEndHyph() && !m_pPrev->IsMidHyph() )
377          return;
378     SwLineLayout *pLay = m_pInf->GetParaPortion();
379     if( m_pCurr == pLay )
380         return;
381     while( pLay != m_pCurr )
382     {
383         if ( pLay->IsEndHyph() )
384             nEndCnt++;
385         else
386             nEndCnt = 0;
387         if ( pLay->IsMidHyph() )
388             nMidCnt++;
389         else
390             nMidCnt = 0;
391         pLay = pLay->GetNext();
392     }
393 }
394 
395 // Change current output device to formatting device, this has to be done before
396 // formatting.
397 SwHookOut::SwHookOut( SwTextSizeInfo& rInfo ) :
398      pInf( &rInfo ),
399      pOut( rInfo.GetOut() ),
400      bOnWin( rInfo.OnWin() )
401 {
402     OSL_ENSURE( rInfo.GetRefDev(), "No reference device for text formatting" );
403 
404     // set new values
405     rInfo.SetOut( rInfo.GetRefDev() );
406     rInfo.SetOnWin( false );
407 }
408 
409 SwHookOut::~SwHookOut()
410 {
411     pInf->SetOut( pOut );
412     pInf->SetOnWin( bOnWin );
413 }
414 
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
416