update credits
[LibreOffice.git] / sw / source / core / layout / laycache.cxx
blobebd9e722aa2e993d20ee05a0ff12f86b70dabd65
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
21 #include <editeng/formatbreakitem.hxx>
22 #include <tools/stream.hxx>
23 #include <doc.hxx>
24 #include <docstat.hxx>
25 #include <docary.hxx>
26 #include <fmtpdsc.hxx>
27 #include <laycache.hxx>
28 #include <layhelp.hxx>
29 #include <pagefrm.hxx>
30 #include <rootfrm.hxx>
31 #include <txtfrm.hxx>
32 #include <ndtxt.hxx>
33 #include <swtable.hxx>
34 #include <tabfrm.hxx>
35 #include <rowfrm.hxx>
36 #include <colfrm.hxx>
37 #include <bodyfrm.hxx>
38 #include <ndindex.hxx>
39 #include <sectfrm.hxx>
40 #include <frmfmt.hxx>
41 #include <fmtcntnt.hxx>
42 #include <pagedesc.hxx>
43 #include <frmtool.hxx>
44 #include <dflyobj.hxx>
45 #include <dcontact.hxx>
46 #include "viewopt.hxx"
47 #include "viewsh.hxx"
48 #include <flyfrm.hxx>
49 // OD 2004-05-24 #i28701#
50 #include <sortedobjs.hxx>
52 #include <pam.hxx>
53 #include <docsh.hxx>
54 #include <poolfmt.hxx>
56 #include <set>
59 using namespace ::com::sun::star;
62 * Reading and writing of the layout cache.
63 * The layout cache is not necessary, but it improves
64 * the performance and reduces the text flow during
65 * the formatting.
66 * The layout cache contains the index of the paragraphs/tables
67 * at the top of every page, so it's possible to create
68 * the right count of pages and to distribute the document content
69 * to this pages before the formatting starts.
72 void SwLayoutCache::Read( SvStream &rStream )
74 if( !pImpl )
76 pImpl = new SwLayCacheImpl;
77 if( !pImpl->Read( rStream ) )
79 delete pImpl;
80 pImpl = 0;
85 //-----------------------------------------------------------------------------
87 void SwLayCacheImpl::Insert( sal_uInt16 nType, sal_uLong nIndex, xub_StrLen nOffset )
89 aType.push_back( nType );
90 std::vector<sal_uLong>::push_back( nIndex );
91 aOffset.push_back( nOffset );
94 bool SwLayCacheImpl::Read( SvStream& rStream )
96 SwLayCacheIoImpl aIo( rStream, false );
97 if( aIo.GetMajorVersion() > SW_LAYCACHE_IO_VERSION_MAJOR )
98 return false;
100 // Due to an evil bug in the layout cache (#102759#), we cannot trust the
101 // sizes of fly frames which have been written using the "old" layout cache.
102 // This flag should indicate that we do not want to trust the width and
103 // height of fly frames
104 bUseFlyCache = aIo.GetMinorVersion() >= 1;
106 aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
107 aIo.OpenFlagRec();
108 aIo.CloseFlagRec();
109 while( aIo.BytesLeft() && !aIo.HasError() )
111 sal_uInt32 nIndex(0), nOffset(0);
113 switch( aIo.Peek() )
115 case SW_LAYCACHE_IO_REC_PARA:
117 aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
118 sal_uInt8 cFlags = aIo.OpenFlagRec();
119 aIo.GetStream() >> nIndex;
120 if( (cFlags & 0x01) != 0 )
121 aIo.GetStream() >> nOffset;
122 else
123 nOffset = STRING_LEN;
124 aIo.CloseFlagRec();
125 Insert( SW_LAYCACHE_IO_REC_PARA, nIndex, (xub_StrLen)nOffset );
126 aIo.CloseRec( SW_LAYCACHE_IO_REC_PARA );
127 break;
129 case SW_LAYCACHE_IO_REC_TABLE:
130 aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
131 aIo.OpenFlagRec();
132 aIo.GetStream() >> nIndex
133 >> nOffset;
134 Insert( SW_LAYCACHE_IO_REC_TABLE, nIndex, (xub_StrLen)nOffset );
135 aIo.CloseFlagRec();
136 aIo.CloseRec( SW_LAYCACHE_IO_REC_TABLE );
137 break;
138 case SW_LAYCACHE_IO_REC_FLY:
140 aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
141 aIo.OpenFlagRec();
142 aIo.CloseFlagRec();
143 sal_Int32 nX(0), nY(0), nW(0), nH(0);
144 sal_uInt16 nPgNum(0);
145 aIo.GetStream() >> nPgNum >> nIndex
146 >> nX >> nY >> nW >> nH;
147 SwFlyCache* pFly = new SwFlyCache( nPgNum, nIndex, nX, nY, nW, nH );
148 aFlyCache.push_back( pFly );
149 aIo.CloseRec( SW_LAYCACHE_IO_REC_FLY );
150 break;
152 default:
153 aIo.SkipRec();
154 break;
157 aIo.CloseRec( SW_LAYCACHE_IO_REC_PAGES );
159 return !aIo.HasError();
163 * SwLayoutCache::Write(..)
164 * writes the index (more precise: the difference between
165 * the index and the first index of the document content)
166 * of the first paragraph/table at the top of every page.
167 * If at the top of a page is the rest of a paragraph/table
168 * from the bottom of the previous page, the character/row
169 * number is stored, too.
170 * The position, size and page number of the text frames
171 * are stored, too
174 void SwLayoutCache::Write( SvStream &rStream, const SwDoc& rDoc )
176 if( rDoc.GetCurrentLayout() ) // the layout itself .. //swmod 080218
178 SwLayCacheIoImpl aIo( rStream, true );
179 // We want to save the relative index, so we need the index
180 // of the first content
181 sal_uLong nStartOfContent = rDoc.GetNodes().GetEndOfContent().
182 StartOfSectionNode()->GetIndex();
183 // The first page..
184 SwPageFrm* pPage = (SwPageFrm*)rDoc.GetCurrentLayout()->Lower(); //swmod 080218
186 aIo.OpenRec( SW_LAYCACHE_IO_REC_PAGES );
187 aIo.OpenFlagRec( 0, 0 );
188 aIo.CloseFlagRec();
189 while( pPage )
191 if( pPage->GetPrev() )
193 SwLayoutFrm* pLay = pPage->FindBodyCont();
194 SwFrm* pTmp = pLay ? pLay->ContainsAny() : NULL;
195 // We are only interested in paragraph or table frames,
196 // a section frames contains paragraphs/tables.
197 if( pTmp && pTmp->IsSctFrm() )
198 pTmp = ((SwSectionFrm*)pTmp)->ContainsAny();
200 if( pTmp ) // any content
202 if( pTmp->IsTxtFrm() )
204 sal_uLong nNdIdx = ((SwTxtFrm*)pTmp)->GetNode()->GetIndex();
205 if( nNdIdx > nStartOfContent )
207 /* Open Paragraph Record */
208 aIo.OpenRec( SW_LAYCACHE_IO_REC_PARA );
209 sal_Bool bFollow = ((SwTxtFrm*)pTmp)->IsFollow();
210 aIo.OpenFlagRec( bFollow ? 0x01 : 0x00,
211 bFollow ? 8 : 4 );
212 nNdIdx -= nStartOfContent;
213 aIo.GetStream() << static_cast<sal_uInt32>(nNdIdx);
214 if( bFollow )
215 aIo.GetStream() << static_cast<sal_uInt32>(((SwTxtFrm*)pTmp)->GetOfst());
216 aIo.CloseFlagRec();
217 /* Close Paragraph Record */
218 aIo.CloseRec( SW_LAYCACHE_IO_REC_PARA );
221 else if( pTmp->IsTabFrm() )
223 SwTabFrm* pTab = (SwTabFrm*)pTmp;
224 sal_uLong nOfst = STRING_LEN;
225 if( pTab->IsFollow() )
227 // If the table is a follow, we have to look for the
228 // master and to count all rows to get the row number
229 nOfst = 0;
230 if( pTab->IsFollow() )
231 pTab = pTab->FindMaster( true );
232 while( pTab != pTmp )
234 SwFrm* pSub = pTab->Lower();
235 while( pSub )
237 ++nOfst;
238 pSub = pSub->GetNext();
240 pTab = pTab->GetFollow();
241 OSL_ENSURE( pTab, "Table follow without master" );
246 sal_uLong nNdIdx =
247 pTab->GetTable()->GetTableNode()->GetIndex();
248 if( nNdIdx > nStartOfContent )
250 /* Open Table Record */
251 aIo.OpenRec( SW_LAYCACHE_IO_REC_TABLE );
252 aIo.OpenFlagRec( 0, 8 );
253 nNdIdx -= nStartOfContent;
254 aIo.GetStream() << static_cast<sal_uInt32>(nNdIdx)
255 << static_cast<sal_uInt32>(nOfst);
256 aIo.CloseFlagRec();
257 /* Close Table Record */
258 aIo.CloseRec( SW_LAYCACHE_IO_REC_TABLE );
260 // If the table has a follow on the next page,
261 // we know already the row number and store this
262 // immediately.
263 if( pTab->GetFollow() )
265 if( nOfst == STRING_LEN )
266 nOfst = 0;
269 SwFrm* pSub = pTab->Lower();
270 while( pSub )
272 ++nOfst;
273 pSub = pSub->GetNext();
275 pTab = pTab->GetFollow();
276 SwPageFrm *pTabPage = pTab->FindPageFrm();
277 if( pTabPage != pPage )
279 OSL_ENSURE( pPage->GetPhyPageNum() <
280 pTabPage->GetPhyPageNum(),
281 "Looping Tableframes" );
282 pPage = pTabPage;
283 break;
285 } while ( pTab->GetFollow() );
287 else
288 break;
289 } while( pTab );
293 if( pPage->GetSortedObjs() )
295 SwSortedObjs &rObjs = *pPage->GetSortedObjs();
296 for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
298 SwAnchoredObject* pAnchoredObj = rObjs[i];
299 if ( pAnchoredObj->ISA(SwFlyFrm) )
301 SwFlyFrm *pFly = static_cast<SwFlyFrm*>(pAnchoredObj);
302 if( pFly->Frm().Left() != FAR_AWAY &&
303 !pFly->GetAnchorFrm()->FindFooterOrHeader() )
305 const SwContact *pC =
306 ::GetUserCall(pAnchoredObj->GetDrawObj());
307 if( pC )
309 sal_uInt32 nOrdNum = pAnchoredObj->GetDrawObj()->GetOrdNum();
310 sal_uInt16 nPageNum = pPage->GetPhyPageNum();
311 /* Open Fly Record */
312 aIo.OpenRec( SW_LAYCACHE_IO_REC_FLY );
313 aIo.OpenFlagRec( 0, 0 );
314 aIo.CloseFlagRec();
315 SwRect &rRct = pFly->Frm();
316 sal_Int32 nX = rRct.Left() - pPage->Frm().Left();
317 sal_Int32 nY = rRct.Top() - pPage->Frm().Top();
318 aIo.GetStream() << nPageNum << nOrdNum
319 << nX << nY
320 << static_cast<sal_Int32>(rRct.Width())
321 << static_cast<sal_Int32>(rRct.Height());
322 /* Close Fly Record */
323 aIo.CloseRec( SW_LAYCACHE_IO_REC_FLY );
329 pPage = (SwPageFrm*)pPage->GetNext();
331 aIo.CloseRec( SW_LAYCACHE_IO_REC_PAGES );
335 #ifdef DBG_UTIL
336 sal_Bool SwLayoutCache::CompareLayout( const SwDoc& rDoc ) const
338 if( !pImpl )
339 return sal_True;
340 const SwRootFrm *pRootFrm = rDoc.GetCurrentLayout();
341 if( pRootFrm )
343 sal_uInt16 nIndex = 0;
344 sal_uLong nStartOfContent = rDoc.GetNodes().GetEndOfContent().
345 StartOfSectionNode()->GetIndex();
346 SwPageFrm* pPage = (SwPageFrm*)pRootFrm->Lower();
347 if( pPage )
348 pPage = (SwPageFrm*)pPage->GetNext();
349 while( pPage )
351 if( nIndex >= pImpl->size() )
352 return sal_False;
354 SwLayoutFrm* pLay = pPage->FindBodyCont();
355 SwFrm* pTmp = pLay ? pLay->ContainsAny() : NULL;
356 if( pTmp && pTmp->IsSctFrm() )
357 pTmp = ((SwSectionFrm*)pTmp)->ContainsAny();
358 if( pTmp )
360 if( pTmp->IsTxtFrm() )
362 sal_uLong nNdIdx = ((SwTxtFrm*)pTmp)->GetNode()->GetIndex();
363 if( nNdIdx > nStartOfContent )
365 sal_Bool bFollow = ((SwTxtFrm*)pTmp)->IsFollow();
366 nNdIdx -= nStartOfContent;
367 if( pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
368 SW_LAYCACHE_IO_REC_PARA !=
369 pImpl->GetBreakType( nIndex ) ||
370 ( bFollow ? ((SwTxtFrm*)pTmp)->GetOfst()
371 : STRING_LEN ) != pImpl->GetBreakOfst( nIndex ) )
373 return sal_False;
375 ++nIndex;
378 else if( pTmp->IsTabFrm() )
380 SwTabFrm* pTab = (SwTabFrm*)pTmp;
381 sal_uLong nOfst = STRING_LEN;
382 if( pTab->IsFollow() )
384 nOfst = 0;
385 if( pTab->IsFollow() )
386 pTab = pTab->FindMaster( true );
387 while( pTab != pTmp )
389 SwFrm* pSub = pTab->Lower();
390 while( pSub )
392 ++nOfst;
393 pSub = pSub->GetNext();
395 pTab = pTab->GetFollow();
400 sal_uLong nNdIdx =
401 pTab->GetTable()->GetTableNode()->GetIndex();
402 if( nNdIdx > nStartOfContent )
404 nNdIdx -= nStartOfContent;
405 if( pImpl->GetBreakIndex( nIndex ) != nNdIdx ||
406 SW_LAYCACHE_IO_REC_TABLE !=
407 pImpl->GetBreakType( nIndex ) ||
408 nOfst != pImpl->GetBreakOfst( nIndex ) )
410 return sal_False;
412 ++nIndex;
414 if( pTab->GetFollow() )
416 if( nOfst == STRING_LEN )
417 nOfst = 0;
420 SwFrm* pSub = pTab->Lower();
421 while( pSub )
423 ++nOfst;
424 pSub = pSub->GetNext();
426 pTab = pTab->GetFollow();
427 SwPageFrm *pTabPage = pTab->FindPageFrm();
428 if( pTabPage != pPage )
430 pPage = pTabPage;
431 break;
433 } while ( pTab->GetFollow() );
435 else
436 break;
437 } while( pTab );
440 pPage = (SwPageFrm*)pPage->GetNext();
443 return sal_True;
445 #endif
447 void SwLayoutCache::ClearImpl()
449 if( !IsLocked() )
451 delete pImpl;
452 pImpl = 0;
457 SwLayoutCache::~SwLayoutCache()
459 OSL_ENSURE( !nLockCount, "Deleting a locked SwLayoutCache!?" );
460 delete pImpl;
464 * SwActualSection,
465 * a help class to create not nested section frames
466 * for nested sections.
469 SwActualSection::SwActualSection( SwActualSection *pUp,
470 SwSectionFrm *pSect,
471 SwSectionNode *pNd ) :
472 pUpper( pUp ),
473 pSectFrm( pSect ),
474 pSectNode( pNd )
476 if ( !pSectNode )
478 const SwNodeIndex *pIndex = pSect->GetFmt()->GetCntnt().GetCntntIdx();
479 pSectNode = pIndex->GetNode().FindSectionNode();
484 * SwLayHelper
485 * is the helper class, which utilizes the layout cache information
486 * to distribute the document content to the rigth pages.
487 * It's used by the _InsertCnt(..)-function.
488 * If there's no layout cache, the distibution to the pages is more
489 * a guess, but a guess with statistical background.
492 SwLayHelper::SwLayHelper( SwDoc *pD, SwFrm* &rpF, SwFrm* &rpP, SwPageFrm* &rpPg,
493 SwLayoutFrm* &rpL, SwActualSection* &rpA, sal_Bool &rB,
494 sal_uLong nNodeIndex, bool bCache )
495 : rpFrm( rpF ), rpPrv( rpP ), rpPage( rpPg ), rpLay( rpL ),
496 rpActualSection( rpA ), rbBreakAfter(rB), pDoc(pD), nMaxParaPerPage( 25 ),
497 nParagraphCnt( bCache ? 0 : USHRT_MAX ), bFirst( bCache )
499 pImpl = pDoc->GetLayoutCache() ? pDoc->GetLayoutCache()->LockImpl() : NULL;
500 if( pImpl )
502 nMaxParaPerPage = 1000;
503 nStartOfContent = pDoc->GetNodes().GetEndOfContent().StartOfSectionNode()
504 ->GetIndex();
505 nNodeIndex -= nStartOfContent;
506 nIndex = 0;
507 nFlyIdx = 0;
508 while( nIndex < pImpl->size() && (*pImpl)[ nIndex ] < nNodeIndex )
509 ++nIndex;
510 if( nIndex >= pImpl->size() )
512 pDoc->GetLayoutCache()->UnlockImpl();
513 pImpl = NULL;
516 else
518 nIndex = USHRT_MAX;
519 nStartOfContent = ULONG_MAX;
523 SwLayHelper::~SwLayHelper()
525 if( pImpl )
527 OSL_ENSURE( pDoc && pDoc->GetLayoutCache(), "Missing layoutcache" );
528 pDoc->GetLayoutCache()->UnlockImpl();
533 * SwLayHelper::CalcPageCount() does not really calculate the page count,
534 * it returns the page count value from the layout cache, if available,
535 * otherwise it estimates the page count.
538 sal_uLong SwLayHelper::CalcPageCount()
540 sal_uLong nPgCount;
541 SwLayCacheImpl *pCache = pDoc->GetLayoutCache() ?
542 pDoc->GetLayoutCache()->LockImpl() : NULL;
543 if( pCache )
545 nPgCount = pCache->size() + 1;
546 pDoc->GetLayoutCache()->UnlockImpl();
548 else
550 nPgCount = pDoc->GetDocStat().nPage;
551 if ( nPgCount <= 10 ) // no page insertion for less than 10 pages
552 nPgCount = 0;
553 sal_uLong nNdCount = pDoc->GetDocStat().nPara;
554 if ( nNdCount <= 1 )
556 //Estimates the number of paragraphs.
557 sal_uLong nTmp = pDoc->GetNodes().GetEndOfContent().GetIndex() -
558 pDoc->GetNodes().GetEndOfExtras().GetIndex();
559 //Tables have a little overhead..
560 nTmp -= pDoc->GetTblFrmFmts()->size() * 25;
561 //Fly frames, too ..
562 nTmp -= (pDoc->GetNodes().GetEndOfAutotext().GetIndex() -
563 pDoc->GetNodes().GetEndOfInserts().GetIndex()) / 3 * 5;
564 if ( nTmp > 0 )
565 nNdCount = nTmp;
567 if ( nNdCount > 100 ) // no estimation below this value
569 if ( nPgCount > 0 )
570 nMaxParaPerPage = nNdCount / nPgCount;
571 else
573 nMaxParaPerPage = std::max( sal_uLong(20),
574 sal_uLong(20 + nNdCount / 1000 * 3) );
575 const sal_uLong nMax = 53;
576 nMaxParaPerPage = std::min( nMaxParaPerPage, nMax );
577 nPgCount = nNdCount / nMaxParaPerPage;
579 if ( nNdCount < 1000 )
580 nPgCount = 0;// no progress bar for small documents
581 ViewShell *pSh = 0;
582 if( rpLay && rpLay->getRootFrm() )
583 pSh = rpLay->getRootFrm()->GetCurrShell();
584 if( pSh && pSh->GetViewOptions()->getBrowseMode() )
585 nMaxParaPerPage *= 6;
588 return nPgCount;
592 * SwLayHelper::CheckInsertPage()
593 * inserts a page and return true, if
594 * - the break after flag is set
595 * - the actual content wants a break before
596 * - the maximum count of paragraph/rows is reached
598 * The break after flag is set, if the actual content
599 * wants a break after.
602 bool SwLayHelper::CheckInsertPage()
604 bool bEnd = 0 == rpPage->GetNext();
605 const SwAttrSet* pAttr = rpFrm->GetAttrSet();
606 const SvxFmtBreakItem& rBrk = pAttr->GetBreak();
607 const SwFmtPageDesc& rDesc = pAttr->GetPageDesc();
608 // #118195# Do not evaluate page description if frame
609 // is a follow frame!
610 const SwPageDesc* pDesc = rpFrm->IsFlowFrm() &&
611 SwFlowFrm::CastFlowFrm( rpFrm )->IsFollow() ?
613 rDesc.GetPageDesc();
615 bool bBrk = nParagraphCnt > nMaxParaPerPage || rbBreakAfter;
616 rbBreakAfter = rBrk.GetBreak() == SVX_BREAK_PAGE_AFTER ||
617 rBrk.GetBreak() == SVX_BREAK_PAGE_BOTH;
618 if ( !bBrk )
619 bBrk = rBrk.GetBreak() == SVX_BREAK_PAGE_BEFORE ||
620 rBrk.GetBreak() == SVX_BREAK_PAGE_BOTH;
622 if ( bBrk || pDesc )
624 sal_uInt16 nPgNum = 0;
625 if ( !pDesc )
626 pDesc = rpPage->GetPageDesc()->GetFollow();
627 else
629 if ( 0 != (nPgNum = rDesc.GetNumOffset()) )
630 ((SwRootFrm*)rpPage->GetUpper())->SetVirtPageNum(sal_True);
632 bool bNextPageOdd = !rpPage->OnRightPage();
633 bool bInsertEmpty = false;
634 if( nPgNum && bNextPageOdd != ( ( nPgNum % 2 ) != 0 ) )
636 bNextPageOdd = !bNextPageOdd;
637 bInsertEmpty = true;
639 // If the page style is changing, we'll have a first page.
640 bool bNextPageFirst = pDesc != rpPage->GetPageDesc();
641 ::InsertNewPage( (SwPageDesc&)*pDesc, rpPage->GetUpper(),
642 bNextPageOdd, bNextPageFirst, bInsertEmpty, sal_False, rpPage->GetNext() );
643 if ( bEnd )
645 OSL_ENSURE( rpPage->GetNext(), "No new page?" );
647 { rpPage = (SwPageFrm*)rpPage->GetNext();
648 } while ( rpPage->GetNext() );
650 else
652 OSL_ENSURE( rpPage->GetNext(), "No new page?" );
653 rpPage = (SwPageFrm*)rpPage->GetNext();
654 if ( rpPage->IsEmptyPage() )
656 OSL_ENSURE( rpPage->GetNext(), "No new page?" );
657 rpPage = (SwPageFrm*)rpPage->GetNext();
660 rpLay = rpPage->FindBodyCont();
661 while( rpLay->Lower() )
662 rpLay = (SwLayoutFrm*)rpLay->Lower();
663 return true;
665 return false;
669 * SwLayHelper::CheckInsert
670 * is the entry point for the _InsertCnt-function.
671 * The document content index is checked either it is
672 * in the layout cache either it's time to insert a page
673 * cause the maximal estimation of content per page is reached.
674 * A really big table or long paragraph may contains more than
675 * one page, in this case the needed count of pages will inserted.
678 bool SwLayHelper::CheckInsert( sal_uLong nNodeIndex )
680 bool bRet = false;
681 bool bLongTab = false;
682 sal_uLong nMaxRowPerPage( 0 );
683 nNodeIndex -= nStartOfContent;
684 sal_uInt16 nRows( 0 );
685 if( rpFrm->IsTabFrm() )
687 //Inside a table counts every row as a paragraph
688 SwFrm *pLow = ((SwTabFrm*)rpFrm)->Lower();
689 nRows = 0;
692 ++nRows;
693 pLow = pLow->GetNext();
694 } while ( pLow );
695 nParagraphCnt += nRows;
696 if( !pImpl && nParagraphCnt > nMaxParaPerPage + 10 )
698 // OD 09.04.2003 #108698# - improve heuristics:
699 // Assume that a table, which has more than three times the quantity
700 // of maximal paragraphs per page rows, consists of rows, which have
701 // the height of a normal paragraph. Thus, allow as much rows per page
702 // as much paragraphs are allowed.
703 if ( nRows > ( 3*nMaxParaPerPage ) )
705 nMaxRowPerPage = nMaxParaPerPage;
707 else
709 SwFrm *pTmp = ((SwTabFrm*)rpFrm)->Lower();
710 if( pTmp->GetNext() )
711 pTmp = pTmp->GetNext();
712 pTmp = ((SwRowFrm*)pTmp)->Lower();
713 sal_uInt16 nCnt = 0;
716 ++nCnt;
717 pTmp = pTmp->GetNext();
718 } while( pTmp );
719 nMaxRowPerPage = std::max( sal_uLong(2), nMaxParaPerPage / nCnt );
721 bLongTab = true;
724 else
725 ++nParagraphCnt;
726 if( bFirst && pImpl && nIndex < pImpl->size() &&
727 pImpl->GetBreakIndex( nIndex ) == nNodeIndex &&
728 ( pImpl->GetBreakOfst( nIndex ) < STRING_LEN ||
729 ( ++nIndex < pImpl->size() &&
730 pImpl->GetBreakIndex( nIndex ) == nNodeIndex ) ) )
731 bFirst = false;
732 #if OSL_DEBUG_LEVEL > 1
733 sal_uLong nBreakIndex = ( pImpl && nIndex < pImpl->size() ) ?
734 pImpl->GetBreakIndex(nIndex) : 0xffff;
735 (void)nBreakIndex;
736 #endif
737 // OD 09.04.2003 #108698# - always split a big tables.
738 if ( !bFirst ||
739 ( rpFrm->IsTabFrm() && bLongTab )
742 sal_uLong nRowCount = 0;
745 if( pImpl || bLongTab )
747 #if OSL_DEBUG_LEVEL > 1
748 sal_uLong nBrkIndex = ( pImpl && nIndex < pImpl->size() ) ?
749 pImpl->GetBreakIndex(nIndex) : 0xffff;
750 (void)nBrkIndex;
751 #endif
752 xub_StrLen nOfst = STRING_LEN;
753 sal_uInt16 nType = SW_LAYCACHE_IO_REC_PAGES;
754 if( bLongTab )
756 rbBreakAfter = sal_True;
757 nOfst = static_cast<xub_StrLen>(nRowCount + nMaxRowPerPage);
759 else
761 while( nIndex < pImpl->size() &&
762 pImpl->GetBreakIndex(nIndex) < nNodeIndex)
763 ++nIndex;
764 if( nIndex < pImpl->size() &&
765 pImpl->GetBreakIndex(nIndex) == nNodeIndex )
767 nType = pImpl->GetBreakType( nIndex );
768 nOfst = pImpl->GetBreakOfst( nIndex++ );
769 rbBreakAfter = sal_True;
773 if( nOfst < STRING_LEN )
775 bool bSplit = false;
776 sal_uInt16 nRepeat( 0 );
777 if( !bLongTab && rpFrm->IsTxtFrm() &&
778 SW_LAYCACHE_IO_REC_PARA == nType &&
779 nOfst<((SwTxtFrm*)rpFrm)->GetTxtNode()->GetTxt().getLength())
780 bSplit = true;
781 else if( rpFrm->IsTabFrm() && nRowCount < nOfst &&
782 ( bLongTab || SW_LAYCACHE_IO_REC_TABLE == nType ) )
784 nRepeat = ((SwTabFrm*)rpFrm)->
785 GetTable()->GetRowsToRepeat();
786 bSplit = nOfst < nRows && nRowCount + nRepeat < nOfst;
787 bLongTab = bLongTab && bSplit;
789 if( bSplit )
791 rpFrm->InsertBehind( rpLay, rpPrv );
792 rpFrm->Frm().Pos() = rpLay->Frm().Pos();
793 rpFrm->Frm().Pos().Y() += 1;
794 rpPrv = rpFrm;
795 if( rpFrm->IsTabFrm() )
797 SwTabFrm* pTab = (SwTabFrm*)rpFrm;
798 // #i33629#, #i29955#
799 ::RegistFlys( pTab->FindPageFrm(), pTab );
800 SwFrm *pRow = pTab->Lower();
801 SwTabFrm *pFoll = new SwTabFrm( *pTab );
803 SwFrm *pPrv;
804 if( nRepeat > 0 )
806 bDontCreateObjects = true; //frmtool
808 // Insert new headlines:
809 sal_uInt16 nRowIdx = 0;
810 SwRowFrm* pHeadline = 0;
811 while( nRowIdx < nRepeat )
813 OSL_ENSURE( pTab->GetTable()->GetTabLines()[ nRowIdx ], "Table ohne Zeilen?" );
814 pHeadline =
815 new SwRowFrm( *pTab->GetTable()->GetTabLines()[ nRowIdx ], pTab );
816 pHeadline->SetRepeatedHeadline( true );
817 pHeadline->InsertBefore( pFoll, 0 );
818 pHeadline->RegistFlys();
820 ++nRowIdx;
823 bDontCreateObjects = false;
824 pPrv = pHeadline;
825 nRows = nRows + nRepeat;
827 else
828 pPrv = 0;
829 while( pRow && nRowCount < nOfst )
831 pRow = pRow->GetNext();
832 ++nRowCount;
834 while ( pRow )
836 SwFrm* pNxt = pRow->GetNext();
837 pRow->Remove();
838 pRow->InsertBehind( pFoll, pPrv );
839 pPrv = pRow;
840 pRow = pNxt;
842 rpFrm = pFoll;
844 else
846 SwTxtFrm *const pNew = static_cast<SwTxtFrm*>(
847 static_cast<SwTxtFrm*>(rpFrm)
848 ->GetTxtNode()->MakeFrm(rpFrm));
849 pNew->ManipOfst( nOfst );
850 pNew->SetFollow( ((SwTxtFrm*)rpFrm)->GetFollow() );
851 ((SwTxtFrm*)rpFrm)->SetFollow( pNew );
852 rpFrm = pNew;
858 SwPageFrm* pLastPage = rpPage;
859 if( CheckInsertPage() )
861 _CheckFlyCache( pLastPage );
862 if( rpPrv && rpPrv->IsTxtFrm() && !rpPrv->GetValidSizeFlag() )
863 rpPrv->Frm().Height( rpPrv->GetUpper()->Prt().Height() );
865 bRet = true;
866 rpPrv = 0;
867 nParagraphCnt = 0;
869 if ( rpActualSection )
871 //Did the SectionFrm even have a content? If not, we can
872 //directly put it somewhere else
873 SwSectionFrm *pSct;
874 bool bInit = false;
875 if ( !rpActualSection->GetSectionFrm()->ContainsCntnt())
877 pSct = rpActualSection->GetSectionFrm();
878 pSct->Remove();
880 else
882 pSct = new SwSectionFrm(
883 *rpActualSection->GetSectionFrm(), sal_False );
884 rpActualSection->GetSectionFrm()->SimpleFormat();
885 bInit = true;
887 rpActualSection->SetSectionFrm( pSct );
888 pSct->InsertBehind( rpLay, 0 );
889 if( bInit )
890 pSct->Init();
891 pSct->Frm().Pos() = rpLay->Frm().Pos();
892 pSct->Frm().Pos().Y() += 1; //because of the notifications
894 rpLay = pSct;
895 if ( rpLay->Lower() && rpLay->Lower()->IsLayoutFrm() )
896 rpLay = rpLay->GetNextLayoutLeaf();
899 } while( bLongTab || ( pImpl && nIndex < pImpl->size() &&
900 (*pImpl)[ nIndex ] == nNodeIndex ) );
902 bFirst = false;
903 return bRet;
906 struct SdrObjectCompare
908 bool operator()( const SdrObject* pF1, const SdrObject* pF2 ) const
910 return pF1->GetOrdNum() < pF2->GetOrdNum();
914 struct FlyCacheCompare
916 bool operator()( const SwFlyCache* pC1, const SwFlyCache* pC2 ) const
918 return pC1->nOrdNum < pC2->nOrdNum;
923 * SwLayHelper::_CheckFlyCache(..)
924 * If a new page is inserted, the last page is analysed.
925 * If there are text frames with default position, the fly cache
926 * is checked, if these frames are stored in the cache.
929 void SwLayHelper::_CheckFlyCache( SwPageFrm* pPage )
931 if( !pImpl || !pPage )
932 return;
933 sal_uInt16 nFlyCount = pImpl->GetFlyCount();
934 // Any text frames at the page, fly cache avaiable?
935 if( pPage->GetSortedObjs() && nFlyIdx < nFlyCount )
937 SwSortedObjs &rObjs = *pPage->GetSortedObjs();
938 sal_uInt16 nPgNum = pPage->GetPhyPageNum();
941 // NOTE: Here we do not use the absolute ordnums but
942 // relative ordnums for the objects on this page.
944 // skip fly frames from pages before the current page
945 SwFlyCache* pFlyC;
946 while( nFlyIdx < nFlyCount && ( pFlyC = pImpl->
947 GetFlyCache(nFlyIdx) )->nPageNum < nPgNum)
948 ++nFlyIdx;
950 // sort cached objects on this page by ordnum
951 std::set< const SwFlyCache*, FlyCacheCompare > aFlyCacheSet;
952 sal_uInt16 nIdx = nFlyIdx;
954 while( nIdx < nFlyCount && ( pFlyC = pImpl->
955 GetFlyCache( nIdx ) )->nPageNum == nPgNum )
957 aFlyCacheSet.insert( pFlyC );
958 ++nIdx;
961 // sort objects on this page by ordnum
962 std::set< const SdrObject*, SdrObjectCompare > aFlySet;
963 for ( sal_uInt16 i = 0; i < rObjs.Count(); ++i )
965 SwAnchoredObject* pAnchoredObj = rObjs[i];
966 if ( pAnchoredObj->ISA(SwFlyFrm) ) // a text frame?
968 SwFlyFrm *pFly = static_cast<SwFlyFrm*>(pAnchoredObj);
969 if( pFly->GetAnchorFrm() &&
970 !pFly->GetAnchorFrm()->FindFooterOrHeader() )
972 const SwContact *pC = ::GetUserCall( pAnchoredObj->GetDrawObj() );
973 if( pC )
975 aFlySet.insert( pAnchoredObj->GetDrawObj() );
981 if ( aFlyCacheSet.size() == aFlySet.size() )
983 std::set< const SwFlyCache*, FlyCacheCompare >::iterator aFlyCacheSetIt =
984 aFlyCacheSet.begin();
985 std::set< const SdrObject*, SdrObjectCompare >::iterator aFlySetIt =
986 aFlySet.begin();
988 while ( aFlyCacheSetIt != aFlyCacheSet.end() )
990 const SwFlyCache* pFlyCache = *aFlyCacheSetIt;
991 SwFlyFrm* pFly = ((SwVirtFlyDrawObj*)*aFlySetIt)->GetFlyFrm();
993 if ( pFly->Frm().Left() == FAR_AWAY )
995 // we get the stored information
996 pFly->Frm().Pos().X() = pFlyCache->Left() +
997 pPage->Frm().Left();
998 pFly->Frm().Pos().Y() = pFlyCache->Top() +
999 pPage->Frm().Top();
1000 if ( pImpl->IsUseFlyCache() )
1002 pFly->Frm().Width( pFlyCache->Width() );
1003 pFly->Frm().Height( pFlyCache->Height() );
1007 ++aFlyCacheSetIt;
1008 ++aFlySetIt;
1015 * SwLayHelper::CheckPageFlyCache(..)
1016 * looks for the given text frame in the fly cache and sets
1017 * the position and size, if possible.
1018 * The fly cache is sorted by pages and we start searching with the given page.
1019 * If we found the page number in the fly cache, we set
1020 * the rpPage parameter to the right page, if possible.
1023 bool SwLayHelper::CheckPageFlyCache( SwPageFrm* &rpPage, SwFlyFrm* pFly )
1025 if( !pFly->GetAnchorFrm() || !pFly->GetVirtDrawObj() ||
1026 pFly->GetAnchorFrm()->FindFooterOrHeader() )
1027 return false;
1028 bool bRet = false;
1029 SwDoc* pDoc = rpPage->GetFmt()->GetDoc();
1030 SwLayCacheImpl *pCache = pDoc->GetLayoutCache() ?
1031 pDoc->GetLayoutCache()->LockImpl() : NULL;
1032 if( pCache )
1034 sal_uInt16 nPgNum = rpPage->GetPhyPageNum();
1035 sal_uInt16 nIdx = 0;
1036 sal_uInt16 nCnt = pCache->GetFlyCount();
1037 sal_uLong nOrdNum = pFly->GetVirtDrawObj()->GetOrdNum();
1038 SwFlyCache* pFlyC = 0;
1040 // skip fly frames from pages before the current page
1041 while( nIdx < nCnt &&
1042 nPgNum > (pFlyC = pCache->GetFlyCache( nIdx ))->nPageNum )
1043 ++nIdx;
1045 while( nIdx < nCnt &&
1046 nOrdNum != (pFlyC = pCache->GetFlyCache( nIdx ))->nOrdNum )
1047 ++nIdx;
1048 if( nIdx < nCnt )
1050 SwPageFrm *pPage = rpPage;
1051 while( pPage && pPage->GetPhyPageNum() < pFlyC->nPageNum )
1052 pPage = (SwPageFrm*)pPage->GetNext();
1053 // #i43266# - if the found page is an empty page,
1054 // take the previous one (take next one, if previous one doesn't exists)
1055 if ( pPage && pPage->IsEmptyPage() )
1057 pPage = static_cast<SwPageFrm*>( pPage->GetPrev()
1058 ? pPage->GetPrev()
1059 : pPage->GetNext() );
1061 if( pPage )
1063 rpPage = pPage;
1064 pFly->Frm().Pos().X() = pFlyC->Left() + pPage->Frm().Left();
1065 pFly->Frm().Pos().Y() = pFlyC->Top() + pPage->Frm().Top();
1066 if ( pCache->IsUseFlyCache() )
1068 pFly->Frm().Width( pFlyC->Width() );
1069 pFly->Frm().Height( pFlyC->Height() );
1071 bRet = true;
1074 pDoc->GetLayoutCache()->UnlockImpl();
1076 return bRet;
1079 // -----------------------------------------------------------------------------
1081 SwLayCacheIoImpl::SwLayCacheIoImpl( SvStream& rStrm, bool bWrtMd ) :
1082 pStream( &rStrm ),
1083 nMajorVersion(SW_LAYCACHE_IO_VERSION_MAJOR),
1084 nMinorVersion(SW_LAYCACHE_IO_VERSION_MINOR),
1085 bWriteMode( bWrtMd ),
1086 bError( false )
1088 if( bWriteMode )
1089 *pStream << nMajorVersion
1090 << nMinorVersion;
1092 else
1093 *pStream >> nMajorVersion
1094 >> nMinorVersion;
1097 bool SwLayCacheIoImpl::OpenRec( sal_uInt8 cType )
1099 bool bRes = true;
1100 sal_uInt32 nPos = pStream->Tell();
1101 if( bWriteMode )
1103 aRecords.push_back( RecTypeSize(cType, nPos) );
1104 *pStream << (sal_uInt32) 0;
1106 else
1108 sal_uInt32 nVal(0);
1109 *pStream >> nVal;
1110 sal_uInt8 cRecTyp = (sal_uInt8)nVal;
1111 if( !nVal || cRecTyp != cType ||
1112 pStream->GetErrorCode() != SVSTREAM_OK || pStream->IsEof() )
1114 OSL_ENSURE( nVal, "OpenRec: Record-Header is 0" );
1115 OSL_ENSURE( cRecTyp == cType, "OpenRec: Wrong Record Type" );
1116 aRecords.push_back( RecTypeSize(0, pStream->Tell()) );
1117 bRes = false;
1118 bError = true;
1120 else
1122 sal_uInt32 nSize = nVal >> 8;
1123 aRecords.push_back( RecTypeSize(cRecTyp, nPos+nSize) );
1126 return bRes;
1129 // Close record
1131 bool SwLayCacheIoImpl::CloseRec( sal_uInt8 )
1133 bool bRes = true;
1134 OSL_ENSURE( !aRecords.empty(), "CloseRec: no levels" );
1135 if( !aRecords.empty() )
1137 sal_uInt32 nPos = pStream->Tell();
1138 if( bWriteMode )
1140 sal_uInt32 nBgn = aRecords.back().size;
1141 pStream->Seek( nBgn );
1142 sal_uInt32 nSize = nPos - nBgn;
1143 sal_uInt32 nVal = ( nSize << 8 ) | aRecords.back().type;
1144 *pStream << nVal;
1145 pStream->Seek( nPos );
1146 if( pStream->GetError() != SVSTREAM_OK )
1147 bRes = false;
1149 else
1151 sal_uInt32 n = aRecords.back().size;
1152 OSL_ENSURE( n >= nPos, "CloseRec: to much data read" );
1153 if( n != nPos )
1155 pStream->Seek( n );
1156 if( n < nPos )
1157 bRes = false;
1159 if( pStream->GetErrorCode() != SVSTREAM_OK )
1160 bRes = false;
1162 aRecords.pop_back();
1165 if( !bRes )
1166 bError = true;
1168 return bRes;
1171 sal_uInt32 SwLayCacheIoImpl::BytesLeft()
1173 sal_uInt32 n = 0;
1174 if( !bError && !aRecords.empty() )
1176 sal_uInt32 nEndPos = aRecords.back().size;
1177 sal_uInt32 nPos = pStream->Tell();
1178 if( nEndPos > nPos )
1179 n = nEndPos - nPos;
1181 return n;
1184 sal_uInt8 SwLayCacheIoImpl::Peek()
1186 sal_uInt8 c(0);
1187 if( !bError )
1189 sal_uInt32 nPos = pStream->Tell();
1190 *pStream >> c;
1191 pStream->Seek( nPos );
1192 if( pStream->GetErrorCode() != SVSTREAM_OK )
1194 c = 0;
1195 bError = true;
1198 return c;
1201 void SwLayCacheIoImpl::SkipRec()
1203 sal_uInt8 c = Peek();
1204 OpenRec( c );
1205 pStream->Seek( aRecords.back().size );
1206 CloseRec( c );
1209 sal_uInt8 SwLayCacheIoImpl::OpenFlagRec()
1211 OSL_ENSURE( !bWriteMode, "OpenFlagRec illegal in write mode" );
1212 sal_uInt8 cFlags(0);
1213 *pStream >> cFlags;
1214 nFlagRecEnd = pStream->Tell() + ( cFlags & 0x0F );
1215 return (cFlags >> 4);
1218 void SwLayCacheIoImpl::OpenFlagRec( sal_uInt8 nFlags, sal_uInt8 nLen )
1220 OSL_ENSURE( bWriteMode, "OpenFlagRec illegal in read mode" );
1221 OSL_ENSURE( (nFlags & 0xF0) == 0, "illegal flags set" );
1222 OSL_ENSURE( nLen < 16, "wrong flag record length" );
1223 sal_uInt8 cFlags = (nFlags << 4) + nLen;
1224 *pStream << cFlags;
1225 nFlagRecEnd = pStream->Tell() + nLen;
1228 void SwLayCacheIoImpl::CloseFlagRec()
1230 if( bWriteMode )
1232 OSL_ENSURE( pStream->Tell() == nFlagRecEnd, "Wrong amount of data written" );
1234 else
1236 OSL_ENSURE( pStream->Tell() <= nFlagRecEnd, "To many data read" );
1237 if( pStream->Tell() != nFlagRecEnd )
1238 pStream->Seek( nFlagRecEnd );
1242 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */