You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
650 lines
29 KiB
650 lines
29 KiB
/* This file is part of the KOffice project
|
|
* Copyright (C) 2002-2005 David Faure <faure@kde.org>
|
|
* Copyright (C) 2005 Thomas Zander <zander@kde.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; version 2.
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
#include "KWFrameLayout.h"
|
|
#include "KWFrameList.h"
|
|
#include "KWPageManager.h"
|
|
#include "KWPage.h"
|
|
#include "KWTextFrameSet.h"
|
|
#include "KWDocument.h"
|
|
#include <tqtimer.h>
|
|
|
|
// #define DEBUG_FRAMELAYOUT
|
|
|
|
#ifdef NDEBUG
|
|
#undef DEBUG_FRAMELAYOUT
|
|
#endif
|
|
|
|
KWFrameLayout::HeaderFooterFrameset::HeaderFooterFrameset( KWTextFrameSet* fs, int start, int end,
|
|
double spacing, OddEvenAll oea )
|
|
: m_frameset(fs), m_startAtPage(start), m_endAtPage(end), m_oddEvenAll(oea),
|
|
m_spacing(spacing), m_minY( 0 ), m_positioned( false )
|
|
{
|
|
if ( fs->frameCount() > 0 )
|
|
m_height = fs->frame(0)->height();
|
|
else
|
|
m_height = 20; // whatever. The text layout will resize it.
|
|
Q_ASSERT( m_height > 0 );
|
|
}
|
|
|
|
|
|
void KWFrameLayout::HeaderFooterFrameset::debug()
|
|
{
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
HeaderFooterFrameset* hff = this;
|
|
kdDebug(32002) << " * " << hff->m_frameset->name()
|
|
<< " pages:" << hff->m_startAtPage << "-" << (hff->m_endAtPage==-1?TQString("(all)"):TQString::number(hff->m_endAtPage))
|
|
<< " page-selection:" << (hff->m_oddEvenAll==HeaderFooterFrameset::Odd ? "Odd" :
|
|
hff->m_oddEvenAll==HeaderFooterFrameset::Even ? "Even" : "All")
|
|
<< " frames:" << hff->m_frameset->frameCount()
|
|
<< " height:" << hff->m_height
|
|
<< " spacing:" << hff->m_spacing << endl;
|
|
#endif
|
|
}
|
|
|
|
bool KWFrameLayout::HeaderFooterFrameset::deleteFramesAfterLast( int lastPage )
|
|
{
|
|
int lastFrame = lastFrameNumber( lastPage );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
//kdDebug(32002) << " Final cleanup: frameset " << m_frameset->name() << ": lastFrame=" << lastFrame << endl;
|
|
#endif
|
|
|
|
KWTextFrameSet* fs = m_frameset;
|
|
|
|
// Special case for odd/even headers: keep at least one frame even if it doesn't appear,
|
|
// otherwise the frame properties are lost.
|
|
if ( fs->isHeaderOrFooter() && lastFrame == -1 ) {
|
|
fs->setVisible( false );
|
|
lastFrame = 0;
|
|
}
|
|
|
|
bool deleted = false;
|
|
while ( (int)fs->frameCount() - 1 > lastFrame ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Final cleanup: deleting frame " << fs->frameCount() - 1 << " of " << fs->name() << endl;
|
|
#endif
|
|
fs->deleteFrame( fs->frameCount() - 1 );
|
|
deleted = true;
|
|
}
|
|
return deleted;
|
|
}
|
|
|
|
int KWFrameLayout::HeaderFooterFrameset::lastFrameNumber( int lastPage ) const {
|
|
if ( lastPage < m_startAtPage )
|
|
return -1; // we need none
|
|
int pg = lastPage;
|
|
if ( m_endAtPage > -1 )
|
|
pg = TQMIN( m_endAtPage, pg );
|
|
pg -= m_startAtPage; // always >=0
|
|
Q_ASSERT( pg >= 0 );
|
|
switch (m_oddEvenAll) {
|
|
case Odd:
|
|
case Even:
|
|
return pg / 2; // page 0 and 1 -> 0. page 2 and 3 -> 1.
|
|
case All:
|
|
return pg; // page 0 -> 0 etc. ;)
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int KWFrameLayout::HeaderFooterFrameset::frameNumberForPage( int page ) const
|
|
{
|
|
if ( page < m_startAtPage || ( m_endAtPage != -1 && page > m_endAtPage ) )
|
|
return -1;
|
|
int pg = page - m_startAtPage; // always >=0
|
|
switch (m_oddEvenAll) {
|
|
case Odd:
|
|
// we test the absolute page number for odd/even, not pg!
|
|
if ( page % 2 )
|
|
return pg / 2; // page start+(0 or 1) -> frame 0, page start+(2 or 3) -> frame 1
|
|
else
|
|
return -1;
|
|
case Even:
|
|
if ( page % 2 == 0 )
|
|
return pg / 2; // page start+(0 or 1) -> frame 0, page start+(2 or 3) -> frame 1
|
|
else
|
|
return -1;
|
|
case All:
|
|
return pg; // page 0[+start] -> frame 0, etc.
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/////
|
|
|
|
void KWFrameLayout::layout( KWFrameSet* mainTextFrameSet, int numColumns,
|
|
int fromPage, int toPage, uint flags )
|
|
{
|
|
//kdDebug(32002) << "KWFrameLayout::layout " << kdBacktrace() << endl;
|
|
// Just debug stuff
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "KWFrameLayout::layout " << fromPage << " to " << toPage << endl;
|
|
Q_ASSERT( toPage >= fromPage );
|
|
TQPtrListIterator<HeaderFooterFrameset> itdbg( m_headersFooters );
|
|
for ( ; itdbg.current() ; ++itdbg )
|
|
itdbg.current()->debug();
|
|
TQPtrListIterator<HeaderFooterFrameset> itdbg2( m_footnotes );
|
|
for ( ; itdbg2.current() ; ++itdbg2 )
|
|
itdbg2.current()->debug();
|
|
TQPtrListIterator<HeaderFooterFrameset> itdbg3( m_endnotes );
|
|
for ( ; itdbg3.current() ; ++itdbg3 )
|
|
itdbg3.current()->debug();
|
|
#endif
|
|
#if 0 // old code
|
|
// Necessary for end notes: calculate where the text goes down to
|
|
Q_ASSERT( mainTextFrameSet->type() == FT_TEXT );
|
|
double textBottom = 0.0;
|
|
if ( mainTextFrameSet->hasFramesInPageArray() )
|
|
{
|
|
KoPoint textBottomPoint;
|
|
KoTextParag * lastParag = static_cast<KWTextFrameSet *>(mainTextFrameSet)->textDocument()->lastParag();
|
|
if ( lastParag->isValid() )
|
|
{
|
|
TQRect rect = lastParag->rect();
|
|
int bottom = rect.top() + rect.height() + 2; // cf kwtextframeset
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "bottom LU=" << bottom << endl;
|
|
#endif
|
|
|
|
if ( static_cast<KWTextFrameSet *>(mainTextFrameSet)->internalToDocument( TQPoint(rect.left(), bottom), textBottomPoint ) )
|
|
textBottom = textBottomPoint.y();
|
|
}
|
|
}
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "textBottom = " << textBottom << "pt" << endl;
|
|
#endif
|
|
#endif
|
|
m_framesetsToUpdate.clear();
|
|
// Necessary for end notes: find out the last frame of the main textframeset
|
|
KWFrame* lastMainFrame = mainTextFrameSet->frameIterator().getLast();
|
|
double lastMainFrameBottom = lastMainFrame->bottom(); // before we change it below!
|
|
|
|
double ptColumnWidth = m_doc->ptColumnWidth();
|
|
int mainTextFrameResized = -1; // contains the page number of the first resized main textframe
|
|
|
|
// The main loop is: "for each page". We lay out each page separately.
|
|
for ( int pageNum = fromPage ; pageNum <= toPage ; ++pageNum )
|
|
{
|
|
KWPage *page = m_doc->pageManager()->page(pageNum);
|
|
double top = page->offsetInDocument() + page->topMargin();
|
|
double bottom = page->offsetInDocument() + page->height() - page->bottomMargin();
|
|
double left = page->leftMargin();
|
|
double right = page->width() - page->rightMargin();
|
|
Q_ASSERT( left < right );
|
|
KoRect oldColumnRect = firstColumnRect( mainTextFrameSet, pageNum, numColumns );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Page " << pageNum << endl;
|
|
#endif
|
|
|
|
// For each header/footer....
|
|
for ( TQPtrListIterator<HeaderFooterFrameset> it( m_headersFooters ); it.current() ; ++it )
|
|
{
|
|
int frameNum = it.current()->frameNumberForPage( pageNum );
|
|
if ( frameNum != -1 )
|
|
{
|
|
it.current()->m_positioned = true;
|
|
KWTextFrameSet* fs = it.current()->m_frameset;
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Page " << pageNum << ": adding frame " << frameNum << " from " << fs->name() << endl;
|
|
#endif
|
|
KoRect rect;
|
|
if ( fs->isAHeader() ) // add on top
|
|
{
|
|
rect.setRect( left, top, right - left, it.current()->m_height );
|
|
top += it.current()->m_height + it.current()->m_spacing;
|
|
} else // footer, add at bottom
|
|
{
|
|
double frameHeight = it.current()->m_height;
|
|
double frameTop = bottom - frameHeight;
|
|
rect.setRect( left, frameTop, right - left, frameHeight );
|
|
bottom -= frameHeight + it.current()->m_spacing;
|
|
}
|
|
Q_ASSERT( bottom > 0 );
|
|
Q_ASSERT( top < bottom );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " rect:" << rect << " - new_top:" << top << " new_bottom:" << bottom << endl;
|
|
#endif
|
|
resizeOrCreateHeaderFooter( fs, frameNum, rect );
|
|
}
|
|
}
|
|
|
|
// All headers/footers for this page have been done,
|
|
// now resize the frame from the main textframeset (if any)
|
|
// the first time _before_ doing the footnotes.
|
|
resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, bottom, NoFootNote );
|
|
|
|
// Recalc footnote pages
|
|
checkFootNotes();
|
|
|
|
bool firstFootNote = true;
|
|
|
|
//// Stay seated... We need to know if there are any footnotes on top of us, although we're going
|
|
//// to lay them out _AFTER_. But we need their total height for the minY stuff.
|
|
//// So we first iterate over all footnotes of the page, to get their total height.
|
|
//// Then we'll reduce this height after every footnote being positionned, so it's always
|
|
//// the "height on top of us".
|
|
double totalFootNotesHeight = 0;
|
|
for ( TQPtrListIterator<HeaderFooterFrameset> it( m_footnotes ); it.current() ; ++it )
|
|
{
|
|
int frameNum = it.current()->frameNumberForPage( pageNum );
|
|
if ( frameNum != -1 )
|
|
totalFootNotesHeight += it.current()->m_height;
|
|
}
|
|
|
|
// For each footnote (caller sorted them from bottom to top)
|
|
for ( TQPtrListIterator<HeaderFooterFrameset> it( m_footnotes ); it.current() ; ++it )
|
|
{
|
|
int frameNum = it.current()->frameNumberForPage( pageNum );
|
|
if ( frameNum != -1 )
|
|
{
|
|
it.current()->m_positioned = true;
|
|
totalFootNotesHeight -= it.current()->m_height; // as discussed above
|
|
KWTextFrameSet* fs = it.current()->m_frameset;
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Page " << pageNum << ": adding footnote frame " << frameNum << " from " << fs->name() << endl;
|
|
#endif
|
|
KoRect rect;
|
|
|
|
// When two footnotes are in the same page there should be 0 spacing between them
|
|
// Yeah, write a generic frame layouter and then realize it's not flexible enough :(
|
|
if ( fs->isFootEndNote() && !firstFootNote )
|
|
{
|
|
// Undo "bottom -= spacing" (done below). This assumes equal spacing for all footnotes
|
|
bottom += it.current()->m_spacing;
|
|
bottom -= 1; // keep them one pixel apart though
|
|
}
|
|
double frameTop = bottom - it.current()->m_height;
|
|
double frameHeight = it.current()->m_height;
|
|
|
|
Q_ASSERT ( fs->isFootNote() );
|
|
|
|
// This is where we add the "total height of the footnotes on top of this one".
|
|
// The footnote variable can't be behind them....
|
|
|
|
double minY = it.current()->m_minY + totalFootNotesHeight;
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " footnote: frameHeight=" << frameHeight << " frameTop (" << frameTop << ") <? minY (" << minY << ")" << endl;
|
|
#endif
|
|
if ( frameTop < minY )
|
|
{
|
|
// Ok, this is the complex case of a footnote var too far down in the page,
|
|
// and its footnote text is too big, so both won't fit.
|
|
// We do like other WPs: we create a frame on the next page
|
|
it.current()->m_endAtPage++; // this will do so
|
|
|
|
// In the current page we stop at minY
|
|
frameTop = minY;
|
|
frameHeight = bottom - frameTop;
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " footnote: new top=" << frameTop << " new height=" << frameHeight << " remaining height=" << it.current()->m_height - frameHeight << endl;
|
|
#endif
|
|
Q_ASSERT( frameHeight < it.current()->m_height );
|
|
it.current()->m_height -= frameHeight; // calculate what remains to be done in the next frame
|
|
//fnFrameBehavior = KWFrame::Ignore;
|
|
|
|
// Make sure there'll actually be a next page
|
|
if ( pageNum == m_doc->pageCount()-1 ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "Adding a page for the footnote overflow." << endl;
|
|
#endif
|
|
m_doc->appendPage();
|
|
m_doc->updateAllFrames();
|
|
toPage = m_doc->pageCount()-1;
|
|
}
|
|
}
|
|
|
|
rect.setRect( left, frameTop, right - left, frameHeight );
|
|
bottom -= frameHeight + it.current()->m_spacing;
|
|
Q_ASSERT( bottom > 0 );
|
|
Q_ASSERT( top < bottom );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " footnote rect:" << rect << " - new_top:" << top << " new_bottom:" << bottom << endl;
|
|
#endif
|
|
resizeOrCreateHeaderFooter( fs, frameNum, rect );
|
|
firstFootNote = false;
|
|
|
|
// We added a footnote, update main text frame size
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Laid out a footnote -> call resizeMainTextFrame/checkFootNotes again" << endl;
|
|
#endif
|
|
resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, bottom, WithFootNotes );
|
|
checkFootNotes();
|
|
}
|
|
} // for all footnotes
|
|
|
|
// Check for endnotes, on the last page of main text
|
|
// and on any end-notes-only page, i.e. after the last page of main text
|
|
if ( pageNum >= m_lastMainFramePage && m_doc->hasEndNotes() ) {
|
|
bool pageHasMainText = ( pageNum == m_lastMainFramePage );
|
|
if ( pageHasMainText )
|
|
lastMainFrame->setDrawFootNoteLine( true );
|
|
double textBottom = pageHasMainText ? lastMainFrameBottom : top;
|
|
// Leave some space on top of the endnotes, for the horizontal line
|
|
double endNoteTop = textBottom + m_doc->ptFootnoteBodySpacing();
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Endnotes: textBottom=" << textBottom << "pt, endNoteTop=" << endNoteTop << "pt, bottom=" << bottom << "pt" << endl;
|
|
#endif
|
|
bool firstEndNote = true;
|
|
for ( TQPtrListIterator<HeaderFooterFrameset> it( m_endnotes ); it.current() ; ++it )
|
|
{
|
|
if ( ! it.current()->m_positioned )
|
|
{
|
|
KWTextFrameSet* fs = it.current()->m_frameset;
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Page " << pageNum << ": adding endnote frame from " << fs->name() << endl;
|
|
#endif
|
|
double frameHeight = it.current()->m_height;
|
|
if ( it.current()->m_startAtPage < 0 ) // not set yet
|
|
it.current()->m_startAtPage = pageNum;
|
|
|
|
// Check if the endnote is bigger than the available space
|
|
if ( endNoteTop + frameHeight > bottom )
|
|
{
|
|
// In the current page we stop at bottom
|
|
frameHeight = bottom - endNoteTop;
|
|
|
|
if ( frameHeight > 1E-10 ) // means, if frameHeight > 0
|
|
{
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " endnote: new height=" << frameHeight << " remaining height=" << it.current()->m_height - frameHeight << endl;
|
|
#endif
|
|
Q_ASSERT( frameHeight < it.current()->m_height );
|
|
it.current()->m_height -= frameHeight; // calculate what remains to be done in the next frame
|
|
} else {
|
|
// No room at all on this page. Schedule for next page.
|
|
it.current()->m_startAtPage++;
|
|
break;
|
|
}
|
|
// Make sure there'll actually be a next page
|
|
if ( pageNum == m_doc->pageCount()-1 ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "Adding a page for the endnote overflow." << endl;
|
|
#endif
|
|
m_doc->appendPage();
|
|
m_doc->updateAllFrames();
|
|
toPage = m_doc->pageCount()-1;
|
|
}
|
|
}
|
|
else // It'll all fit in this page
|
|
{
|
|
it.current()->m_positioned = true;
|
|
}
|
|
KoRect rect( left, endNoteTop, right - left, frameHeight );
|
|
endNoteTop += frameHeight + 1; // not + it.current()->m_spacing;
|
|
Q_ASSERT( bottom > 0 );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " rect:" << rect << " - new_top:" << endNoteTop << " new_bottom:" << bottom << endl;
|
|
#endif
|
|
int frameNum = pageNum - it.current()->m_startAtPage;
|
|
resizeOrCreateHeaderFooter( fs, frameNum, rect );
|
|
|
|
#if 0 // Disabled. The main frame is resized by KWTextFrameSet::slotAfterFormatting already.
|
|
if ( pageHasMainText && firstEndNote )
|
|
{
|
|
// We positionned the first endnote, update main text frame size
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Laid out an endnote and the page has a maintextframe too -> call resizeMainTextFrame/checkFootNotes again top=" << top << " textBottom=" << textBottom << endl;
|
|
#endif
|
|
resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, textBottom, NoChange );
|
|
}
|
|
#endif
|
|
} // if not positionned yet
|
|
firstEndNote = false; // yes, out of the if
|
|
} // for all endnotes
|
|
} // if page can have endnotes
|
|
|
|
if ( mainTextFrameResized == -1 ) {
|
|
// Test if the main text frame for this page was really resized or not.
|
|
KoRect newColumnRect = firstColumnRect( mainTextFrameSet, pageNum, numColumns );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Comparing old=" << oldColumnRect << " and new=" << newColumnRect << endl;
|
|
#endif
|
|
if ( oldColumnRect != newColumnRect ) {
|
|
mainTextFrameResized = pageNum;
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " changed -> mainTextFrameResized=" << mainTextFrameResized << endl;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
} // for all pages
|
|
m_lastMainFramePage = lastMainFrame->pageNumber();
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "m_lastMainFramePage = " << m_lastMainFramePage << " lastMainFrameBottom=" << lastMainFrameBottom << endl;
|
|
#endif
|
|
|
|
if ( ! ( flags & DontRemovePages ) )
|
|
{
|
|
m_doc->updateAllFrames( KWFrameSet::UpdateFramesInPage );
|
|
// Check if the last page is now empty (e.g. this can happen when removing
|
|
// some text above an endnote, so the endnote moves up)
|
|
(void)m_doc->tryRemovingPages();
|
|
}
|
|
|
|
const int lastPage = m_doc->lastPage();
|
|
// Final cleanup: delete all frames after lastFrameNumber in each frameset
|
|
TQPtrListIterator<HeaderFooterFrameset> it( m_headersFooters );
|
|
for ( ; it.current() ; ++it )
|
|
if ( it.current()->deleteFramesAfterLast( lastPage ) )
|
|
m_framesetsToUpdate.insert( it.current()->m_frameset, true );
|
|
TQPtrListIterator<HeaderFooterFrameset> it2( m_footnotes );
|
|
for ( ; it2.current() ; ++it2 )
|
|
if ( it2.current()->deleteFramesAfterLast( lastPage ) )
|
|
m_framesetsToUpdate.insert( it2.current()->m_frameset, true );
|
|
if ( mainTextFrameSet ) {
|
|
// For the last main text frameset, we use m_lastMainFramePage, so that
|
|
// there's no frame on the "end notes only" page(s).
|
|
int lastFrame = m_lastMainFramePage * numColumns + (numColumns-1);
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "lastFrame: " << lastFrame << " due to " << m_lastMainFramePage << endl;
|
|
#endif
|
|
bool deleted = false;
|
|
while ( (int)mainTextFrameSet->frameCount() - 1 > lastFrame ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Final cleanup: deleting frame " << mainTextFrameSet->frameCount() - 1 << " of main textframeset (lastFrame=" << lastFrame << ")" << endl;
|
|
#endif
|
|
mainTextFrameSet->deleteFrame( mainTextFrameSet->frameCount() - 1, true, false /*do not updateFrames!*/ );
|
|
deleted = true;
|
|
}
|
|
if ( deleted )
|
|
m_framesetsToUpdate.insert( mainTextFrameSet, true );
|
|
// The last frame before the first endnote, is in auto-extend mode
|
|
if ( m_doc->hasEndNotes() ) {
|
|
KWFrame* lastMainFrame = mainTextFrameSet->frameIterator().getLast();
|
|
if ( lastMainFrame->frameBehavior() != KWFrame::AutoExtendFrame )
|
|
{
|
|
lastMainFrame->setFrameBehavior( KWFrame::AutoExtendFrame );
|
|
// make sure it gets resized
|
|
if ( mainTextFrameResized == -1 )
|
|
mainTextFrameResized = lastMainFrame->pageNumber();
|
|
}
|
|
}
|
|
}
|
|
|
|
TQMap<KWFrameSet*, bool>::iterator fsit = m_framesetsToUpdate.begin();
|
|
for ( ; fsit != m_framesetsToUpdate.end() ; ++fsit )
|
|
fsit.key()->updateFrames();
|
|
|
|
// ## TODO: only if something changed? (resizing, new frames, or deleted frames...)
|
|
KWFrameList::recalcFrames(m_doc, fromPage, toPage);
|
|
|
|
if ( mainTextFrameResized != -1 && mainTextFrameSet->type() == FT_TEXT ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Done. First maintextframe resized: " << mainTextFrameResized << endl;
|
|
#endif
|
|
KWTextFrameSet* fs = static_cast<KWTextFrameSet *>(mainTextFrameSet);
|
|
|
|
// Not right now, this could be called during formatting...
|
|
//m_doc->invalidate();
|
|
// ### This means the layout will be done during painting. Not good.
|
|
// What about mainTextFrameSet->invalidate() or even layout()?
|
|
//TQTimer::singleShot( 0, m_doc, TQT_SLOT( invalidate() ) );
|
|
|
|
// Invalidate main textframeset only, and from top of page only.
|
|
// Otherwise loading a long document (with headers/footers) takes ages,
|
|
// if we redo it all from the beginning at each new page!
|
|
int topLU, bottomLU;
|
|
if ( fs->minMaxInternalOnPage( mainTextFrameResized, topLU, bottomLU ) )
|
|
{
|
|
// Find parag at topLU
|
|
KoTextParag* parag = fs->paragAtLUPos( topLU );
|
|
if ( parag ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Invalidating from parag " << parag->paragId() << endl;
|
|
#endif
|
|
fs->textObject()->setLastFormattedParag( parag );
|
|
fs->textObject()->formatMore( 2 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void KWFrameLayout::resizeOrCreateHeaderFooter( KWTextFrameSet* headerFooter, uint frameNumber, const KoRect& rect )
|
|
{
|
|
if ( frameNumber < headerFooter->frameCount() ) {
|
|
KWFrame* frame = headerFooter->frame( frameNumber );
|
|
if ( *frame == rect )
|
|
return;
|
|
frame->setRect( rect );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "KWFrameLayout::resizeOrCreateHeaderFooter frame " << headerFooter->name() << " " << frame << " resized to " << rect << " pagenum=" << frame->pageNumber() << endl;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << "KWFrameLayout::resizeOrCreateHeaderFooter creating frame for " << headerFooter->name() << endl;
|
|
#endif
|
|
KWFrame* frame = new KWFrame( headerFooter, rect.x(), rect.y(), rect.width(), rect.height() );
|
|
frame->setFrameBehavior( KWFrame::AutoExtendFrame );
|
|
if ( headerFooter->isHeaderOrFooter() ) // not for footnotes!
|
|
{
|
|
frame->setNewFrameBehavior( KWFrame::Copy );
|
|
frame->setCopy( true );
|
|
}
|
|
else
|
|
frame->setNewFrameBehavior( KWFrame::NoFollowup );
|
|
headerFooter->addFrame( frame, false /*no recalc*/ );
|
|
}
|
|
// This updates e.g. availableHeight. Very important in the case
|
|
// of the footnote frameset with 2 frames.
|
|
headerFooter->updateFrames( 0 /*fast one*/ );
|
|
m_framesetsToUpdate.insert( headerFooter, true );
|
|
}
|
|
|
|
// Called at beginning and end of the layout for a given page,
|
|
// to determine if the main-text-frame layout really changed or not.
|
|
// Testing in resizeMainTextFrame doesn't work, we call it several times,
|
|
// once for each footnote, so it can't detect the "no change" case.
|
|
KoRect KWFrameLayout::firstColumnRect( KWFrameSet* mainTextFrameSet, int pageNum, int numColumns ) const
|
|
{
|
|
uint frameNum = pageNum * numColumns /*+ col 0 here*/;
|
|
if ( mainTextFrameSet && frameNum < mainTextFrameSet->frameCount() )
|
|
return * mainTextFrameSet->frame( frameNum );
|
|
else
|
|
return KoRect();
|
|
}
|
|
|
|
bool KWFrameLayout::resizeMainTextFrame( KWFrameSet* mainTextFrameSet, int pageNum, int numColumns, double ptColumnWidth, double ptColumnSpacing, double left, double top, double bottom, HasFootNotes hasFootNotes )
|
|
{
|
|
if ( !mainTextFrameSet )
|
|
return false;
|
|
bool mainTextFrameResized = false;
|
|
for ( int col = 0; col < numColumns; col++ ) {
|
|
Q_ASSERT( bottom > top );
|
|
// Calculate wanted rect for this frame
|
|
KoRect rect( left + col * ( ptColumnWidth + ptColumnSpacing ),
|
|
top, ptColumnWidth, bottom - top );
|
|
uint frameNum = (pageNum - m_doc->startPage()) * numColumns + col;
|
|
KWFrame* frame;
|
|
if ( frameNum < mainTextFrameSet->frameCount() ) {
|
|
// Resize existing frame
|
|
frame = mainTextFrameSet->frame( frameNum );
|
|
// Special case for last-frame-before-endnotes: don't resize its bottom
|
|
if ( m_doc->hasEndNotes() && pageNum >= m_lastMainFramePage )
|
|
rect.setBottom( frame->bottom() );
|
|
bool resized = (rect != *frame);
|
|
if ( resized ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Page " << pageNum << ": resizing main text frame " << frameNum << "(" << frame << ") to " << rect << endl;
|
|
#endif
|
|
frame->setRect( rect );
|
|
frame->updateRulerHandles();
|
|
mainTextFrameResized = true;
|
|
mainTextFrameSet->updateFrames( 0xff - KWFrameSet::SortFrames ); // Don't sort frames yet!
|
|
}
|
|
} else {
|
|
// Create new frame
|
|
frame = new KWFrame( mainTextFrameSet, rect.x(), rect.y(), rect.width(), rect.height() );
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " Page " << pageNum << ": creating new main text frame " << frameNum << "(" << frame << ") to " << rect << endl;
|
|
#endif
|
|
mainTextFrameSet->addFrame( frame );
|
|
Q_ASSERT( frameNum == mainTextFrameSet->frameCount()-1 );
|
|
mainTextFrameResized = true;
|
|
mainTextFrameSet->updateFrames( 0xff - KWFrameSet::SortFrames ); // Don't sort frames yet!
|
|
}
|
|
if ( hasFootNotes == NoFootNote )
|
|
frame->setDrawFootNoteLine( false );
|
|
else if ( hasFootNotes == WithFootNotes )
|
|
frame->setDrawFootNoteLine( true );
|
|
// unchanged in the other cases
|
|
// By default, all main-text frames are in "auto-create new frames" mode
|
|
frame->setFrameBehavior( KWFrame::AutoCreateNewFrame );
|
|
}
|
|
return mainTextFrameResized;
|
|
}
|
|
|
|
void KWFrameLayout::checkFootNotes()
|
|
{
|
|
// We recalculate all footnotes pages, but we return true
|
|
// if those on pageNum have changed.
|
|
TQPtrListIterator<HeaderFooterFrameset> it( m_footnotes );
|
|
for ( ; it.current() ; ++it )
|
|
{
|
|
HeaderFooterFrameset* hff = it.current();
|
|
if ( ! hff->m_positioned )
|
|
{
|
|
Q_ASSERT ( hff->m_frameset->isFootEndNote() );
|
|
KWFootNoteFrameSet* fnfs = static_cast<KWFootNoteFrameSet *>( hff->m_frameset );
|
|
KWFootNoteVariable* fnvar = fnfs->footNoteVariable();
|
|
//necessary to test paragraph because when we delete mutli
|
|
//footnote, first footnote who delete call setDelete(true)
|
|
//and force recalc, but with multi footnote deleted
|
|
//paragraph is null before we apply attribute to
|
|
//kotextcustom.
|
|
if ( !fnvar || !fnvar->paragraph() )
|
|
continue;
|
|
double varY = fnvar->varY();
|
|
if ( varY == 0 ) // not able to calculate it yet
|
|
continue;
|
|
hff->m_minY = varY + /*2 * */ hff->m_spacing + 2 /* some spacing */;
|
|
int pageNum = m_doc->pageManager()->pageNumber(varY);
|
|
if ( pageNum != hff->m_startAtPage ) {
|
|
#ifdef DEBUG_FRAMELAYOUT
|
|
kdDebug(32002) << " checkFootNotes: found minY=" << hff->m_minY << " start/end=" << pageNum << " for footnote " << fnvar->text() << endl;
|
|
#endif
|
|
hff->m_startAtPage = pageNum;
|
|
hff->m_endAtPage = pageNum;
|
|
}
|
|
}
|
|
}
|
|
}
|