kword

KWFrameLayout.cpp

00001 /* This file is part of the KOffice project
00002  * Copyright (C) 2002-2005 David Faure <faure@kde.org>
00003  * Copyright (C) 2005 Thomas Zander <zander@kde.org>
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Library General Public
00007  * License as published by the Free Software Foundation; version 2.
00008 
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this library; see the file COPYING.LIB.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  */
00019 #include "KWFrameLayout.h"
00020 #include "KWFrameList.h"
00021 #include "KWPageManager.h"
00022 #include "KWPage.h"
00023 #include "KWTextFrameSet.h"
00024 #include "KWDocument.h"
00025 #include <qtimer.h>
00026 
00027 // #define DEBUG_FRAMELAYOUT
00028 
00029 #ifdef NDEBUG
00030 #undef DEBUG_FRAMELAYOUT
00031 #endif
00032 
00033 KWFrameLayout::HeaderFooterFrameset::HeaderFooterFrameset( KWTextFrameSet* fs, int start, int end,
00034                                                            double spacing, OddEvenAll oea )
00035     : m_frameset(fs), m_startAtPage(start), m_endAtPage(end), m_oddEvenAll(oea),
00036       m_spacing(spacing), m_minY( 0 ), m_positioned( false )
00037 {
00038     if ( fs->frameCount() > 0 )
00039         m_height = fs->frame(0)->height();
00040     else
00041         m_height = 20; // whatever. The text layout will resize it.
00042     Q_ASSERT( m_height > 0 );
00043 }
00044 
00045 
00046 void KWFrameLayout::HeaderFooterFrameset::debug()
00047 {
00048 #ifdef DEBUG_FRAMELAYOUT
00049     HeaderFooterFrameset* hff = this;
00050     kdDebug(32002) << " * " << hff->m_frameset->name()
00051                    << " pages:" << hff->m_startAtPage << "-" << (hff->m_endAtPage==-1?QString("(all)"):QString::number(hff->m_endAtPage))
00052                    << " page-selection:" << (hff->m_oddEvenAll==HeaderFooterFrameset::Odd ? "Odd" :
00053                                              hff->m_oddEvenAll==HeaderFooterFrameset::Even ? "Even" : "All")
00054                    << " frames:" << hff->m_frameset->frameCount()
00055                    << " height:" << hff->m_height
00056                    << " spacing:" << hff->m_spacing << endl;
00057 #endif
00058 }
00059 
00060 bool KWFrameLayout::HeaderFooterFrameset::deleteFramesAfterLast( int lastPage )
00061 {
00062     int lastFrame = lastFrameNumber( lastPage );
00063 #ifdef DEBUG_FRAMELAYOUT
00064     //kdDebug(32002) << " Final cleanup: frameset " << m_frameset->name() << ": lastFrame=" << lastFrame << endl;
00065 #endif
00066 
00067     KWTextFrameSet* fs = m_frameset;
00068 
00069     // Special case for odd/even headers: keep at least one frame even if it doesn't appear,
00070     // otherwise the frame properties are lost.
00071     if ( fs->isHeaderOrFooter() && lastFrame == -1 ) {
00072         fs->setVisible( false );
00073         lastFrame = 0;
00074     }
00075 
00076     bool deleted = false;
00077     while ( (int)fs->frameCount() - 1 > lastFrame ) {
00078 #ifdef DEBUG_FRAMELAYOUT
00079         kdDebug(32002) << "  Final cleanup: deleting frame " << fs->frameCount() - 1 << " of " << fs->name() << endl;
00080 #endif
00081         fs->deleteFrame( fs->frameCount() - 1 );
00082         deleted = true;
00083     }
00084     return deleted;
00085 }
00086 
00087 int KWFrameLayout::HeaderFooterFrameset::lastFrameNumber( int lastPage ) const {
00088     if ( lastPage < m_startAtPage )
00089         return -1; // we need none
00090     int pg = lastPage;
00091     if ( m_endAtPage > -1 )
00092         pg = QMIN( m_endAtPage, pg );
00093     pg -= m_startAtPage; // always >=0
00094     Q_ASSERT( pg >= 0 );
00095     switch (m_oddEvenAll) {
00096         case Odd:
00097         case Even:
00098             return pg / 2; // page 0 and 1 -> 0. page 2 and 3 -> 1.
00099         case All:
00100             return pg; // page 0 -> 0 etc. ;)
00101         default:
00102             return -1;
00103     }
00104 }
00105 
00106 int KWFrameLayout::HeaderFooterFrameset::frameNumberForPage( int page ) const
00107 {
00108     if ( page < m_startAtPage || ( m_endAtPage != -1 && page > m_endAtPage ) )
00109         return -1;
00110     int pg = page - m_startAtPage; // always >=0
00111     switch (m_oddEvenAll) {
00112     case Odd:
00113         // we test the absolute page number for odd/even, not pg!
00114         if ( page % 2 )
00115             return pg / 2; // page start+(0 or 1) -> frame 0, page start+(2 or 3) -> frame 1
00116         else
00117             return -1;
00118     case Even:
00119         if ( page % 2 == 0 )
00120             return pg / 2; // page start+(0 or 1) -> frame 0, page start+(2 or 3) -> frame 1
00121         else
00122             return -1;
00123     case All:
00124         return pg; // page 0[+start] -> frame 0, etc.
00125     default:
00126         return -1;
00127     }
00128 }
00129 
00131 
00132 void KWFrameLayout::layout( KWFrameSet* mainTextFrameSet, int numColumns,
00133                             int fromPage, int toPage, uint flags )
00134 {
00135     //kdDebug(32002) << "KWFrameLayout::layout " << kdBacktrace() << endl;
00136     // Just debug stuff
00137 #ifdef DEBUG_FRAMELAYOUT
00138     kdDebug(32002) << "KWFrameLayout::layout " << fromPage << " to " << toPage << endl;
00139     Q_ASSERT( toPage >= fromPage );
00140     QPtrListIterator<HeaderFooterFrameset> itdbg( m_headersFooters );
00141     for ( ; itdbg.current() ; ++itdbg )
00142         itdbg.current()->debug();
00143     QPtrListIterator<HeaderFooterFrameset> itdbg2( m_footnotes );
00144     for ( ; itdbg2.current() ; ++itdbg2 )
00145         itdbg2.current()->debug();
00146     QPtrListIterator<HeaderFooterFrameset> itdbg3( m_endnotes );
00147     for ( ; itdbg3.current() ; ++itdbg3 )
00148         itdbg3.current()->debug();
00149 #endif
00150 #if 0 // old code
00151     // Necessary for end notes: calculate where the text goes down to
00152     Q_ASSERT( mainTextFrameSet->type() == FT_TEXT );
00153     double textBottom = 0.0;
00154     if ( mainTextFrameSet->hasFramesInPageArray() )
00155     {
00156         KoPoint textBottomPoint;
00157         KoTextParag * lastParag = static_cast<KWTextFrameSet *>(mainTextFrameSet)->textDocument()->lastParag();
00158         if ( lastParag->isValid() )
00159         {
00160             QRect rect = lastParag->rect();
00161             int bottom = rect.top() + rect.height() + 2; // cf kwtextframeset
00162 #ifdef DEBUG_FRAMELAYOUT
00163             kdDebug(32002) << "bottom LU=" << bottom << endl;
00164 #endif
00165 
00166             if ( static_cast<KWTextFrameSet *>(mainTextFrameSet)->internalToDocument( QPoint(rect.left(), bottom), textBottomPoint ) )
00167                 textBottom = textBottomPoint.y();
00168         }
00169     }
00170 #ifdef DEBUG_FRAMELAYOUT
00171     kdDebug(32002) << "textBottom = " << textBottom << "pt" << endl;
00172 #endif
00173 #endif
00174     m_framesetsToUpdate.clear();
00175     // Necessary for end notes: find out the last frame of the main textframeset
00176     KWFrame* lastMainFrame = mainTextFrameSet->frameIterator().getLast();
00177     double lastMainFrameBottom = lastMainFrame->bottom(); // before we change it below!
00178 
00179     double ptColumnWidth = m_doc->ptColumnWidth();
00180     int mainTextFrameResized = -1; // contains the page number of the first resized main textframe
00181 
00182     // The main loop is: "for each page". We lay out each page separately.
00183     for ( int pageNum = fromPage ; pageNum <= toPage ; ++pageNum )
00184     {
00185         KWPage *page = m_doc->pageManager()->page(pageNum);
00186         double top = page->offsetInDocument() + page->topMargin();
00187         double bottom = page->offsetInDocument() + page->height() - page->bottomMargin();
00188         double left = page->leftMargin();
00189         double right = page->width() - page->rightMargin();
00190         Q_ASSERT( left < right );
00191         KoRect oldColumnRect = firstColumnRect( mainTextFrameSet, pageNum, numColumns );
00192 #ifdef DEBUG_FRAMELAYOUT
00193         kdDebug(32002) << " Page " << pageNum << endl;
00194 #endif
00195 
00196         // For each header/footer....
00197         for ( QPtrListIterator<HeaderFooterFrameset> it( m_headersFooters ); it.current() ; ++it )
00198         {
00199             int frameNum = it.current()->frameNumberForPage( pageNum );
00200             if ( frameNum != -1 )
00201             {
00202                 it.current()->m_positioned = true;
00203                 KWTextFrameSet* fs = it.current()->m_frameset;
00204 #ifdef DEBUG_FRAMELAYOUT
00205                 kdDebug(32002) << " Page " << pageNum << ": adding frame " << frameNum << " from " << fs->name() << endl;
00206 #endif
00207                 KoRect rect;
00208                 if ( fs->isAHeader() ) // add on top
00209                 {
00210                     rect.setRect( left, top, right - left, it.current()->m_height );
00211                     top += it.current()->m_height + it.current()->m_spacing;
00212                 } else // footer, add at bottom
00213                 {
00214                     double frameHeight = it.current()->m_height;
00215                     double frameTop = bottom - frameHeight;
00216                     rect.setRect( left, frameTop, right - left, frameHeight );
00217                     bottom -= frameHeight + it.current()->m_spacing;
00218                 }
00219                 Q_ASSERT( bottom > 0 );
00220                 Q_ASSERT( top < bottom );
00221 #ifdef DEBUG_FRAMELAYOUT
00222                 kdDebug(32002) << "     rect:" << rect << "   - new_top:" << top << " new_bottom:" << bottom << endl;
00223 #endif
00224                 resizeOrCreateHeaderFooter( fs, frameNum, rect );
00225             }
00226         }
00227 
00228         // All headers/footers for this page have been done,
00229         // now resize the frame from the main textframeset (if any)
00230         // the first time _before_ doing the footnotes.
00231         resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, bottom, NoFootNote );
00232 
00233         // Recalc footnote pages
00234         checkFootNotes();
00235 
00236         bool firstFootNote = true;
00237 
00243         double totalFootNotesHeight = 0;
00244         for ( QPtrListIterator<HeaderFooterFrameset> it( m_footnotes ); it.current() ; ++it )
00245         {
00246             int frameNum = it.current()->frameNumberForPage( pageNum );
00247             if ( frameNum != -1 )
00248                 totalFootNotesHeight += it.current()->m_height;
00249         }
00250 
00251         // For each footnote (caller sorted them from bottom to top)
00252         for ( QPtrListIterator<HeaderFooterFrameset> it( m_footnotes ); it.current() ; ++it )
00253         {
00254             int frameNum = it.current()->frameNumberForPage( pageNum );
00255             if ( frameNum != -1 )
00256             {
00257                 it.current()->m_positioned = true;
00258                 totalFootNotesHeight -= it.current()->m_height; // as discussed above
00259                 KWTextFrameSet* fs = it.current()->m_frameset;
00260 #ifdef DEBUG_FRAMELAYOUT
00261                 kdDebug(32002) << " Page " << pageNum << ": adding footnote frame " << frameNum << " from " << fs->name() << endl;
00262 #endif
00263                 KoRect rect;
00264 
00265                 // When two footnotes are in the same page there should be 0 spacing between them
00266                 // Yeah, write a generic frame layouter and then realize it's not flexible enough :(
00267                 if ( fs->isFootEndNote() && !firstFootNote )
00268                 {
00269                     // Undo "bottom -= spacing" (done below). This assumes equal spacing for all footnotes
00270                     bottom += it.current()->m_spacing;
00271                     bottom -= 1; // keep them one pixel apart though
00272                 }
00273                 double frameTop = bottom - it.current()->m_height;
00274                 double frameHeight = it.current()->m_height;
00275 
00276                 Q_ASSERT ( fs->isFootNote() );
00277 
00278                 // This is where we add the "total height of the footnotes on top of this one".
00279                 // The footnote variable can't be behind them....
00280 
00281                 double minY = it.current()->m_minY + totalFootNotesHeight;
00282 #ifdef DEBUG_FRAMELAYOUT
00283                 kdDebug(32002) << "   footnote: frameHeight=" << frameHeight << " frameTop (" << frameTop << ") <? minY (" << minY << ")" << endl;
00284 #endif
00285                 if ( frameTop < minY )
00286                 {
00287                     // Ok, this is the complex case of a footnote var too far down in the page,
00288                     // and its footnote text is too big, so both won't fit.
00289                     // We do like other WPs: we create a frame on the next page
00290                     it.current()->m_endAtPage++; // this will do so
00291 
00292                     // In the current page we stop at minY
00293                     frameTop = minY;
00294                     frameHeight = bottom - frameTop;
00295 #ifdef DEBUG_FRAMELAYOUT
00296                     kdDebug(32002) << "   footnote: new top=" << frameTop << " new height=" << frameHeight << " remaining height=" << it.current()->m_height - frameHeight << endl;
00297 #endif
00298                     Q_ASSERT( frameHeight < it.current()->m_height );
00299                     it.current()->m_height -= frameHeight; // calculate what remains to be done in the next frame
00300                     //fnFrameBehavior = KWFrame::Ignore;
00301 
00302                     // Make sure there'll actually be a next page
00303                     if ( pageNum == m_doc->pageCount()-1 ) {
00304 #ifdef DEBUG_FRAMELAYOUT
00305                         kdDebug(32002) << "Adding a page for the footnote overflow." << endl;
00306 #endif
00307                         m_doc->appendPage();
00308                         m_doc->updateAllFrames();
00309                         toPage = m_doc->pageCount()-1;
00310                     }
00311                 }
00312 
00313                 rect.setRect( left, frameTop, right - left, frameHeight );
00314                 bottom -= frameHeight + it.current()->m_spacing;
00315                 Q_ASSERT( bottom > 0 );
00316                 Q_ASSERT( top < bottom );
00317 #ifdef DEBUG_FRAMELAYOUT
00318                 kdDebug(32002) << "     footnote rect:" << rect << "   - new_top:" << top << " new_bottom:" << bottom << endl;
00319 #endif
00320                 resizeOrCreateHeaderFooter( fs, frameNum, rect );
00321                 firstFootNote = false;
00322 
00323                 // We added a footnote, update main text frame size
00324 #ifdef DEBUG_FRAMELAYOUT
00325                 kdDebug(32002) << "   Laid out a footnote -> call resizeMainTextFrame/checkFootNotes again" << endl;
00326 #endif
00327                 resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, bottom, WithFootNotes );
00328                 checkFootNotes();
00329             }
00330         } // for all footnotes
00331 
00332         // Check for endnotes, on the last page of main text
00333         // and on any end-notes-only page, i.e. after the last page of main text
00334         if ( pageNum >= m_lastMainFramePage && m_doc->hasEndNotes() ) {
00335             bool pageHasMainText = ( pageNum == m_lastMainFramePage );
00336             if ( pageHasMainText )
00337                 lastMainFrame->setDrawFootNoteLine( true );
00338             double textBottom = pageHasMainText ? lastMainFrameBottom : top;
00339             // Leave some space on top of the endnotes, for the horizontal line
00340             double endNoteTop = textBottom + m_doc->ptFootnoteBodySpacing();
00341 #ifdef DEBUG_FRAMELAYOUT
00342             kdDebug(32002) << "  Endnotes: textBottom=" << textBottom << "pt, endNoteTop=" << endNoteTop << "pt, bottom=" << bottom << "pt" << endl;
00343 #endif
00344             bool firstEndNote = true;
00345             for ( QPtrListIterator<HeaderFooterFrameset> it( m_endnotes ); it.current() ; ++it )
00346             {
00347                 if ( ! it.current()->m_positioned )
00348                 {
00349                     KWTextFrameSet* fs = it.current()->m_frameset;
00350 #ifdef DEBUG_FRAMELAYOUT
00351                     kdDebug(32002) << " Page " << pageNum << ": adding endnote frame from " << fs->name() << endl;
00352 #endif
00353                     double frameHeight = it.current()->m_height;
00354                     if ( it.current()->m_startAtPage < 0 ) // not set yet
00355                         it.current()->m_startAtPage = pageNum;
00356 
00357                     // Check if the endnote is bigger than the available space
00358                     if ( endNoteTop + frameHeight > bottom )
00359                     {
00360                         // In the current page we stop at bottom
00361                         frameHeight = bottom - endNoteTop;
00362 
00363                         if ( frameHeight > 1E-10 ) // means, if frameHeight > 0
00364                         {
00365 #ifdef DEBUG_FRAMELAYOUT
00366                             kdDebug(32002) << "   endnote: new height=" << frameHeight << " remaining height=" << it.current()->m_height - frameHeight << endl;
00367 #endif
00368                             Q_ASSERT( frameHeight < it.current()->m_height );
00369                             it.current()->m_height -= frameHeight; // calculate what remains to be done in the next frame
00370                         } else {
00371                             // No room at all on this page. Schedule for next page.
00372                             it.current()->m_startAtPage++;
00373                             break;
00374                         }
00375                         // Make sure there'll actually be a next page
00376                         if ( pageNum == m_doc->pageCount()-1 ) {
00377 #ifdef DEBUG_FRAMELAYOUT
00378                             kdDebug(32002) << "Adding a page for the endnote overflow." << endl;
00379 #endif
00380                             m_doc->appendPage();
00381                             m_doc->updateAllFrames();
00382                             toPage = m_doc->pageCount()-1;
00383                         }
00384                     }
00385                     else // It'll all fit in this page
00386                     {
00387                         it.current()->m_positioned = true;
00388                     }
00389                     KoRect rect( left, endNoteTop, right - left, frameHeight );
00390                     endNoteTop += frameHeight + 1; // not + it.current()->m_spacing;
00391                     Q_ASSERT( bottom > 0 );
00392 #ifdef DEBUG_FRAMELAYOUT
00393                     kdDebug(32002) << "     rect:" << rect << "   - new_top:" << endNoteTop << " new_bottom:" << bottom << endl;
00394 #endif
00395                     int frameNum = pageNum - it.current()->m_startAtPage;
00396                     resizeOrCreateHeaderFooter( fs, frameNum, rect );
00397 
00398 #if 0  // Disabled. The main frame is resized by KWTextFrameSet::slotAfterFormatting already.
00399                     if ( pageHasMainText && firstEndNote )
00400                     {
00401                         // We positionned the first endnote, update main text frame size
00402 #ifdef DEBUG_FRAMELAYOUT
00403                         kdDebug(32002) << "   Laid out an endnote and the page has a maintextframe too -> call resizeMainTextFrame/checkFootNotes again top=" << top << " textBottom=" << textBottom << endl;
00404 #endif
00405                         resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, textBottom, NoChange );
00406                     }
00407 #endif
00408                 } // if not positionned yet
00409                 firstEndNote = false; // yes, out of the if
00410             } // for all endnotes
00411         } // if page can have endnotes
00412 
00413         if ( mainTextFrameResized == -1 ) {
00414             // Test if the main text frame for this page was really resized or not.
00415             KoRect newColumnRect = firstColumnRect( mainTextFrameSet, pageNum, numColumns );
00416 #ifdef DEBUG_FRAMELAYOUT
00417             kdDebug(32002) << "  Comparing old=" << oldColumnRect << " and new=" << newColumnRect << endl;
00418 #endif
00419             if ( oldColumnRect != newColumnRect ) {
00420                 mainTextFrameResized = pageNum;
00421 #ifdef DEBUG_FRAMELAYOUT
00422                 kdDebug(32002) << "  changed -> mainTextFrameResized=" << mainTextFrameResized << endl;
00423 #endif
00424             }
00425         }
00426 
00427     } // for all pages
00428     m_lastMainFramePage = lastMainFrame->pageNumber();
00429 #ifdef DEBUG_FRAMELAYOUT
00430     kdDebug(32002) << "m_lastMainFramePage = " << m_lastMainFramePage << " lastMainFrameBottom=" << lastMainFrameBottom << endl;
00431 #endif
00432 
00433     if ( ! ( flags & DontRemovePages ) )
00434     {
00435         m_doc->updateAllFrames( KWFrameSet::UpdateFramesInPage );
00436         // Check if the last page is now empty (e.g. this can happen when removing
00437         // some text above an endnote, so the endnote moves up)
00438         (void)m_doc->tryRemovingPages();
00439     }
00440 
00441     const int lastPage = m_doc->lastPage();
00442     // Final cleanup: delete all frames after lastFrameNumber in each frameset
00443     QPtrListIterator<HeaderFooterFrameset> it( m_headersFooters );
00444     for ( ; it.current() ; ++it )
00445         if ( it.current()->deleteFramesAfterLast( lastPage ) )
00446             m_framesetsToUpdate.insert( it.current()->m_frameset, true );
00447     QPtrListIterator<HeaderFooterFrameset> it2( m_footnotes );
00448     for ( ; it2.current() ; ++it2 )
00449         if ( it2.current()->deleteFramesAfterLast( lastPage ) )
00450             m_framesetsToUpdate.insert( it2.current()->m_frameset, true );
00451     if ( mainTextFrameSet ) {
00452         // For the last main text frameset, we use m_lastMainFramePage, so that
00453         // there's no frame on the "end notes only" page(s).
00454         int lastFrame = m_lastMainFramePage * numColumns + (numColumns-1);
00455 #ifdef DEBUG_FRAMELAYOUT
00456         kdDebug(32002) << "lastFrame: " << lastFrame << " due to " << m_lastMainFramePage << endl;
00457 #endif
00458         bool deleted = false;
00459         while ( (int)mainTextFrameSet->frameCount() - 1 > lastFrame ) {
00460 #ifdef DEBUG_FRAMELAYOUT
00461             kdDebug(32002) << "  Final cleanup: deleting frame " << mainTextFrameSet->frameCount() - 1 << " of main textframeset (lastFrame=" << lastFrame << ")" << endl;
00462 #endif
00463             mainTextFrameSet->deleteFrame( mainTextFrameSet->frameCount() - 1, true, false /*do not updateFrames!*/ );
00464             deleted = true;
00465         }
00466         if ( deleted )
00467             m_framesetsToUpdate.insert( mainTextFrameSet, true );
00468         // The last frame before the first endnote, is in auto-extend mode
00469         if ( m_doc->hasEndNotes() ) {
00470             KWFrame* lastMainFrame = mainTextFrameSet->frameIterator().getLast();
00471             if ( lastMainFrame->frameBehavior() != KWFrame::AutoExtendFrame )
00472             {
00473                 lastMainFrame->setFrameBehavior( KWFrame::AutoExtendFrame );
00474                 // make sure it gets resized
00475                 if ( mainTextFrameResized == -1 )
00476                     mainTextFrameResized = lastMainFrame->pageNumber();
00477             }
00478         }
00479     }
00480 
00481     QMap<KWFrameSet*, bool>::iterator fsit = m_framesetsToUpdate.begin();
00482     for ( ; fsit != m_framesetsToUpdate.end() ; ++fsit )
00483         fsit.key()->updateFrames();
00484 
00485     // ## TODO: only if something changed? (resizing, new frames, or deleted frames...)
00486     KWFrameList::recalcFrames(m_doc, fromPage, toPage);
00487 
00488     if ( mainTextFrameResized != -1 && mainTextFrameSet->type() == FT_TEXT ) {
00489 #ifdef DEBUG_FRAMELAYOUT
00490         kdDebug(32002) << " Done. First maintextframe resized: " << mainTextFrameResized << endl;
00491 #endif
00492         KWTextFrameSet* fs = static_cast<KWTextFrameSet *>(mainTextFrameSet);
00493 
00494         // Not right now, this could be called during formatting...
00495         //m_doc->invalidate();
00496         // ### This means the layout will be done during painting. Not good.
00497         // What about mainTextFrameSet->invalidate() or even layout()?
00498         //QTimer::singleShot( 0, m_doc, SLOT( invalidate() ) );
00499 
00500         // Invalidate main textframeset only, and from top of page only.
00501         // Otherwise loading a long document (with headers/footers) takes ages,
00502         // if we redo it all from the beginning at each new page!
00503         int topLU, bottomLU;
00504         if ( fs->minMaxInternalOnPage( mainTextFrameResized, topLU, bottomLU ) )
00505         {
00506             // Find parag at topLU
00507             KoTextParag* parag = fs->paragAtLUPos( topLU );
00508             if ( parag ) {
00509 #ifdef DEBUG_FRAMELAYOUT
00510                 kdDebug(32002) << " Invalidating from parag " << parag->paragId() << endl;
00511 #endif
00512                 fs->textObject()->setLastFormattedParag( parag );
00513                 fs->textObject()->formatMore( 2 );
00514             }
00515         }
00516     }
00517 }
00518 
00519 void KWFrameLayout::resizeOrCreateHeaderFooter( KWTextFrameSet* headerFooter, uint frameNumber, const KoRect& rect )
00520 {
00521     if ( frameNumber < headerFooter->frameCount() ) {
00522         KWFrame* frame = headerFooter->frame( frameNumber );
00523         if ( *frame == rect )
00524             return;
00525         frame->setRect( rect );
00526 #ifdef DEBUG_FRAMELAYOUT
00527         kdDebug(32002) << "KWFrameLayout::resizeOrCreateHeaderFooter frame " << headerFooter->name() << " " << frame << " resized to " << rect << " pagenum=" << frame->pageNumber() << endl;
00528 #endif
00529     }
00530     else
00531     {
00532 #ifdef DEBUG_FRAMELAYOUT
00533         kdDebug(32002) << "KWFrameLayout::resizeOrCreateHeaderFooter creating frame for " << headerFooter->name() << endl;
00534 #endif
00535         KWFrame* frame = new KWFrame( headerFooter, rect.x(), rect.y(), rect.width(), rect.height() );
00536         frame->setFrameBehavior( KWFrame::AutoExtendFrame );
00537         if ( headerFooter->isHeaderOrFooter() ) // not for footnotes!
00538         {
00539             frame->setNewFrameBehavior( KWFrame::Copy );
00540             frame->setCopy( true );
00541         }
00542         else
00543             frame->setNewFrameBehavior( KWFrame::NoFollowup );
00544         headerFooter->addFrame( frame, false /*no recalc*/ );
00545     }
00546     // This updates e.g. availableHeight. Very important in the case
00547     // of the footnote frameset with 2 frames.
00548     headerFooter->updateFrames( 0 /*fast one*/ );
00549     m_framesetsToUpdate.insert( headerFooter, true );
00550 }
00551 
00552 // Called at beginning and end of the layout for a given page,
00553 // to determine if the main-text-frame layout really changed or not.
00554 // Testing in resizeMainTextFrame doesn't work, we call it several times,
00555 // once for each footnote, so it can't detect the "no change" case.
00556 KoRect KWFrameLayout::firstColumnRect( KWFrameSet* mainTextFrameSet, int pageNum, int numColumns ) const
00557 {
00558     uint frameNum = pageNum * numColumns /*+ col  0 here*/;
00559     if ( mainTextFrameSet && frameNum < mainTextFrameSet->frameCount() )
00560         return * mainTextFrameSet->frame( frameNum );
00561     else
00562         return KoRect();
00563 }
00564 
00565 bool KWFrameLayout::resizeMainTextFrame( KWFrameSet* mainTextFrameSet, int pageNum, int numColumns, double ptColumnWidth, double ptColumnSpacing, double left, double top, double bottom, HasFootNotes hasFootNotes )
00566 {
00567     if ( !mainTextFrameSet )
00568         return false;
00569     bool mainTextFrameResized = false;
00570     for ( int col = 0; col < numColumns; col++ ) {
00571         Q_ASSERT( bottom > top );
00572         // Calculate wanted rect for this frame
00573         KoRect rect( left + col * ( ptColumnWidth + ptColumnSpacing ),
00574                      top, ptColumnWidth, bottom - top );
00575         uint frameNum = (pageNum - m_doc->startPage()) * numColumns + col;
00576         KWFrame* frame;
00577         if ( frameNum < mainTextFrameSet->frameCount() ) {
00578             // Resize existing frame
00579             frame = mainTextFrameSet->frame( frameNum );
00580             // Special case for last-frame-before-endnotes: don't resize its bottom
00581             if ( m_doc->hasEndNotes() && pageNum >= m_lastMainFramePage )
00582                 rect.setBottom( frame->bottom() );
00583             bool resized = (rect != *frame);
00584             if ( resized ) {
00585 #ifdef DEBUG_FRAMELAYOUT
00586                 kdDebug(32002) << " Page " << pageNum << ": resizing main text frame " << frameNum << "(" << frame << ") to " << rect << endl;
00587 #endif
00588                 frame->setRect( rect );
00589                 frame->updateRulerHandles();
00590                 mainTextFrameResized = true;
00591                 mainTextFrameSet->updateFrames( 0xff - KWFrameSet::SortFrames ); // Don't sort frames yet!
00592             }
00593         } else {
00594             // Create new frame
00595             frame = new KWFrame( mainTextFrameSet, rect.x(), rect.y(), rect.width(), rect.height() );
00596 #ifdef DEBUG_FRAMELAYOUT
00597             kdDebug(32002) << " Page " << pageNum << ": creating new main text frame " << frameNum << "(" << frame << ") to " << rect << endl;
00598 #endif
00599             mainTextFrameSet->addFrame( frame );
00600             Q_ASSERT( frameNum == mainTextFrameSet->frameCount()-1 );
00601             mainTextFrameResized = true;
00602             mainTextFrameSet->updateFrames( 0xff - KWFrameSet::SortFrames ); // Don't sort frames yet!
00603         }
00604         if ( hasFootNotes == NoFootNote )
00605             frame->setDrawFootNoteLine( false );
00606         else if ( hasFootNotes == WithFootNotes )
00607             frame->setDrawFootNoteLine( true );
00608         // unchanged in the other cases
00609         // By default, all main-text frames are in "auto-create new frames" mode
00610         frame->setFrameBehavior( KWFrame::AutoCreateNewFrame );
00611     }
00612     return mainTextFrameResized;
00613 }
00614 
00615 void KWFrameLayout::checkFootNotes()
00616 {
00617     // We recalculate all footnotes pages, but we return true
00618     // if those on pageNum have changed.
00619     QPtrListIterator<HeaderFooterFrameset> it( m_footnotes );
00620     for ( ; it.current() ; ++it )
00621     {
00622         HeaderFooterFrameset* hff = it.current();
00623         if ( ! hff->m_positioned )
00624         {
00625             Q_ASSERT ( hff->m_frameset->isFootEndNote() );
00626             KWFootNoteFrameSet* fnfs = static_cast<KWFootNoteFrameSet *>( hff->m_frameset );
00627             KWFootNoteVariable* fnvar = fnfs->footNoteVariable();
00628             //necessary to test paragraph because when we delete mutli
00629             //footnote, first footnote who delete call setDelete(true)
00630             //and force recalc, but with multi footnote deleted
00631             //paragraph is null before we apply attribute to
00632             //kotextcustom.
00633             if ( !fnvar || !fnvar->paragraph() )
00634                 continue;
00635             double varY = fnvar->varY();
00636             if ( varY == 0 ) // not able to calculate it yet
00637                 continue;
00638             hff->m_minY = varY + /*2 * */ hff->m_spacing + 2 /* some spacing */;
00639             int pageNum = m_doc->pageManager()->pageNumber(varY);
00640             if ( pageNum != hff->m_startAtPage ) {
00641 #ifdef DEBUG_FRAMELAYOUT
00642                 kdDebug(32002) << " checkFootNotes: found minY=" << hff->m_minY << " start/end=" << pageNum << " for footnote " << fnvar->text() << endl;
00643 #endif
00644                 hff->m_startAtPage = pageNum;
00645                 hff->m_endAtPage = pageNum;
00646             }
00647         }
00648     }
00649 }
KDE Home | KDE Accessibility Home | Description of Access Keys