kword

KWFrameLayout.cpp
1/* This file is part of the KOffice project
2 * Copyright (C) 2002-2005 David Faure <faure@kde.org>
3 * Copyright (C) 2005 Thomas Zander <zander@kde.org>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; version 2.
8
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19#include "KWFrameLayout.h"
20#include "KWFrameList.h"
21#include "KWPageManager.h"
22#include "KWPage.h"
23#include "KWTextFrameSet.h"
24#include "KWDocument.h"
25#include <tqtimer.h>
26
27// #define DEBUG_FRAMELAYOUT
28
29#ifdef NDEBUG
30#undef DEBUG_FRAMELAYOUT
31#endif
32
33KWFrameLayout::HeaderFooterFrameset::HeaderFooterFrameset( KWTextFrameSet* fs, int start, int end,
34 double spacing, OddEvenAll oea )
35 : m_frameset(fs), m_startAtPage(start), m_endAtPage(end), m_oddEvenAll(oea),
36 m_spacing(spacing), m_minY( 0 ), m_positioned( false )
37{
38 if ( fs->frameCount() > 0 )
39 m_height = fs->frame(0)->height();
40 else
41 m_height = 20; // whatever. The text layout will resize it.
42 Q_ASSERT( m_height > 0 );
43}
44
45
46void KWFrameLayout::HeaderFooterFrameset::debug()
47{
48#ifdef DEBUG_FRAMELAYOUT
49 HeaderFooterFrameset* hff = this;
50 kdDebug(32002) << " * " << hff->m_frameset->name()
51 << " pages:" << hff->m_startAtPage << "-" << (hff->m_endAtPage==-1?TQString("(all)"):TQString::number(hff->m_endAtPage))
52 << " page-selection:" << (hff->m_oddEvenAll==HeaderFooterFrameset::Odd ? "Odd" :
53 hff->m_oddEvenAll==HeaderFooterFrameset::Even ? "Even" : "All")
54 << " frames:" << hff->m_frameset->frameCount()
55 << " height:" << hff->m_height
56 << " spacing:" << hff->m_spacing << endl;
57#endif
58}
59
60bool KWFrameLayout::HeaderFooterFrameset::deleteFramesAfterLast( int lastPage )
61{
62 int lastFrame = lastFrameNumber( lastPage );
63#ifdef DEBUG_FRAMELAYOUT
64 //kdDebug(32002) << " Final cleanup: frameset " << m_frameset->name() << ": lastFrame=" << lastFrame << endl;
65#endif
66
67 KWTextFrameSet* fs = m_frameset;
68
69 // Special case for odd/even headers: keep at least one frame even if it doesn't appear,
70 // otherwise the frame properties are lost.
71 if ( fs->isHeaderOrFooter() && lastFrame == -1 ) {
72 fs->setVisible( false );
73 lastFrame = 0;
74 }
75
76 bool deleted = false;
77 while ( (int)fs->frameCount() - 1 > lastFrame ) {
78#ifdef DEBUG_FRAMELAYOUT
79 kdDebug(32002) << " Final cleanup: deleting frame " << fs->frameCount() - 1 << " of " << fs->name() << endl;
80#endif
81 fs->deleteFrame( fs->frameCount() - 1 );
82 deleted = true;
83 }
84 return deleted;
85}
86
87int KWFrameLayout::HeaderFooterFrameset::lastFrameNumber( int lastPage ) const {
88 if ( lastPage < m_startAtPage )
89 return -1; // we need none
90 int pg = lastPage;
91 if ( m_endAtPage > -1 )
92 pg = TQMIN( m_endAtPage, pg );
93 pg -= m_startAtPage; // always >=0
94 Q_ASSERT( pg >= 0 );
95 switch (m_oddEvenAll) {
96 case Odd:
97 case Even:
98 return pg / 2; // page 0 and 1 -> 0. page 2 and 3 -> 1.
99 case All:
100 return pg; // page 0 -> 0 etc. ;)
101 default:
102 return -1;
103 }
104}
105
106int KWFrameLayout::HeaderFooterFrameset::frameNumberForPage( int page ) const
107{
108 if ( page < m_startAtPage || ( m_endAtPage != -1 && page > m_endAtPage ) )
109 return -1;
110 int pg = page - m_startAtPage; // always >=0
111 switch (m_oddEvenAll) {
112 case Odd:
113 // we test the absolute page number for odd/even, not pg!
114 if ( page % 2 )
115 return pg / 2; // page start+(0 or 1) -> frame 0, page start+(2 or 3) -> frame 1
116 else
117 return -1;
118 case Even:
119 if ( page % 2 == 0 )
120 return pg / 2; // page start+(0 or 1) -> frame 0, page start+(2 or 3) -> frame 1
121 else
122 return -1;
123 case All:
124 return pg; // page 0[+start] -> frame 0, etc.
125 default:
126 return -1;
127 }
128}
129
131
132void KWFrameLayout::layout( KWFrameSet* mainTextFrameSet, int numColumns,
133 int fromPage, int toPage, uint flags )
134{
135 //kdDebug(32002) << "KWFrameLayout::layout " << kdBacktrace() << endl;
136 // Just debug stuff
137#ifdef DEBUG_FRAMELAYOUT
138 kdDebug(32002) << "KWFrameLayout::layout " << fromPage << " to " << toPage << endl;
139 Q_ASSERT( toPage >= fromPage );
140 TQPtrListIterator<HeaderFooterFrameset> itdbg( m_headersFooters );
141 for ( ; itdbg.current() ; ++itdbg )
142 itdbg.current()->debug();
143 TQPtrListIterator<HeaderFooterFrameset> itdbg2( m_footnotes );
144 for ( ; itdbg2.current() ; ++itdbg2 )
145 itdbg2.current()->debug();
146 TQPtrListIterator<HeaderFooterFrameset> itdbg3( m_endnotes );
147 for ( ; itdbg3.current() ; ++itdbg3 )
148 itdbg3.current()->debug();
149#endif
150#if 0 // old code
151 // Necessary for end notes: calculate where the text goes down to
152 Q_ASSERT( mainTextFrameSet->type() == FT_TEXT );
153 double textBottom = 0.0;
154 if ( mainTextFrameSet->hasFramesInPageArray() )
155 {
156 KoPoint textBottomPoint;
157 KoTextParag * lastParag = static_cast<KWTextFrameSet *>(mainTextFrameSet)->textDocument()->lastParag();
158 if ( lastParag->isValid() )
159 {
160 TQRect rect = lastParag->rect();
161 int bottom = rect.top() + rect.height() + 2; // cf kwtextframeset
162#ifdef DEBUG_FRAMELAYOUT
163 kdDebug(32002) << "bottom LU=" << bottom << endl;
164#endif
165
166 if ( static_cast<KWTextFrameSet *>(mainTextFrameSet)->internalToDocument( TQPoint(rect.left(), bottom), textBottomPoint ) )
167 textBottom = textBottomPoint.y();
168 }
169 }
170#ifdef DEBUG_FRAMELAYOUT
171 kdDebug(32002) << "textBottom = " << textBottom << "pt" << endl;
172#endif
173#endif
174 m_framesetsToUpdate.clear();
175 // Necessary for end notes: find out the last frame of the main textframeset
176 KWFrame* lastMainFrame = mainTextFrameSet->frameIterator().getLast();
177 double lastMainFrameBottom = lastMainFrame->bottom(); // before we change it below!
178
179 double ptColumnWidth = m_doc->ptColumnWidth();
180 int mainTextFrameResized = -1; // contains the page number of the first resized main textframe
181
182 // The main loop is: "for each page". We lay out each page separately.
183 for ( int pageNum = fromPage ; pageNum <= toPage ; ++pageNum )
184 {
185 KWPage *page = m_doc->pageManager()->page(pageNum);
186 double top = page->offsetInDocument() + page->topMargin();
187 double bottom = page->offsetInDocument() + page->height() - page->bottomMargin();
188 double left = page->leftMargin();
189 double right = page->width() - page->rightMargin();
190 Q_ASSERT( left < right );
191 KoRect oldColumnRect = firstColumnRect( mainTextFrameSet, pageNum, numColumns );
192#ifdef DEBUG_FRAMELAYOUT
193 kdDebug(32002) << " Page " << pageNum << endl;
194#endif
195
196 // For each header/footer....
197 for ( TQPtrListIterator<HeaderFooterFrameset> it( m_headersFooters ); it.current() ; ++it )
198 {
199 int frameNum = it.current()->frameNumberForPage( pageNum );
200 if ( frameNum != -1 )
201 {
202 it.current()->m_positioned = true;
203 KWTextFrameSet* fs = it.current()->m_frameset;
204#ifdef DEBUG_FRAMELAYOUT
205 kdDebug(32002) << " Page " << pageNum << ": adding frame " << frameNum << " from " << fs->name() << endl;
206#endif
207 KoRect rect;
208 if ( fs->isAHeader() ) // add on top
209 {
210 rect.setRect( left, top, right - left, it.current()->m_height );
211 top += it.current()->m_height + it.current()->m_spacing;
212 } else // footer, add at bottom
213 {
214 double frameHeight = it.current()->m_height;
215 double frameTop = bottom - frameHeight;
216 rect.setRect( left, frameTop, right - left, frameHeight );
217 bottom -= frameHeight + it.current()->m_spacing;
218 }
219 Q_ASSERT( bottom > 0 );
220 Q_ASSERT( top < bottom );
221#ifdef DEBUG_FRAMELAYOUT
222 kdDebug(32002) << " rect:" << rect << " - new_top:" << top << " new_bottom:" << bottom << endl;
223#endif
224 resizeOrCreateHeaderFooter( fs, frameNum, rect );
225 }
226 }
227
228 // All headers/footers for this page have been done,
229 // now resize the frame from the main textframeset (if any)
230 // the first time _before_ doing the footnotes.
231 resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, bottom, NoFootNote );
232
233 // Recalc footnote pages
234 checkFootNotes();
235
236 bool firstFootNote = true;
237
243 double totalFootNotesHeight = 0;
244 for ( TQPtrListIterator<HeaderFooterFrameset> it( m_footnotes ); it.current() ; ++it )
245 {
246 int frameNum = it.current()->frameNumberForPage( pageNum );
247 if ( frameNum != -1 )
248 totalFootNotesHeight += it.current()->m_height;
249 }
250
251 // For each footnote (caller sorted them from bottom to top)
252 for ( TQPtrListIterator<HeaderFooterFrameset> it( m_footnotes ); it.current() ; ++it )
253 {
254 int frameNum = it.current()->frameNumberForPage( pageNum );
255 if ( frameNum != -1 )
256 {
257 it.current()->m_positioned = true;
258 totalFootNotesHeight -= it.current()->m_height; // as discussed above
259 KWTextFrameSet* fs = it.current()->m_frameset;
260#ifdef DEBUG_FRAMELAYOUT
261 kdDebug(32002) << " Page " << pageNum << ": adding footnote frame " << frameNum << " from " << fs->name() << endl;
262#endif
263 KoRect rect;
264
265 // When two footnotes are in the same page there should be 0 spacing between them
266 // Yeah, write a generic frame layouter and then realize it's not flexible enough :(
267 if ( fs->isFootEndNote() && !firstFootNote )
268 {
269 // Undo "bottom -= spacing" (done below). This assumes equal spacing for all footnotes
270 bottom += it.current()->m_spacing;
271 bottom -= 1; // keep them one pixel apart though
272 }
273 double frameTop = bottom - it.current()->m_height;
274 double frameHeight = it.current()->m_height;
275
276 Q_ASSERT ( fs->isFootNote() );
277
278 // This is where we add the "total height of the footnotes on top of this one".
279 // The footnote variable can't be behind them....
280
281 double minY = it.current()->m_minY + totalFootNotesHeight;
282#ifdef DEBUG_FRAMELAYOUT
283 kdDebug(32002) << " footnote: frameHeight=" << frameHeight << " frameTop (" << frameTop << ") <? minY (" << minY << ")" << endl;
284#endif
285 if ( frameTop < minY )
286 {
287 // Ok, this is the complex case of a footnote var too far down in the page,
288 // and its footnote text is too big, so both won't fit.
289 // We do like other WPs: we create a frame on the next page
290 it.current()->m_endAtPage++; // this will do so
291
292 // In the current page we stop at minY
293 frameTop = minY;
294 frameHeight = bottom - frameTop;
295#ifdef DEBUG_FRAMELAYOUT
296 kdDebug(32002) << " footnote: new top=" << frameTop << " new height=" << frameHeight << " remaining height=" << it.current()->m_height - frameHeight << endl;
297#endif
298 Q_ASSERT( frameHeight < it.current()->m_height );
299 it.current()->m_height -= frameHeight; // calculate what remains to be done in the next frame
300 //fnFrameBehavior = KWFrame::Ignore;
301
302 // Make sure there'll actually be a next page
303 if ( pageNum == m_doc->pageCount()-1 ) {
304#ifdef DEBUG_FRAMELAYOUT
305 kdDebug(32002) << "Adding a page for the footnote overflow." << endl;
306#endif
307 m_doc->appendPage();
308 m_doc->updateAllFrames();
309 toPage = m_doc->pageCount()-1;
310 }
311 }
312
313 rect.setRect( left, frameTop, right - left, frameHeight );
314 bottom -= frameHeight + it.current()->m_spacing;
315 Q_ASSERT( bottom > 0 );
316 Q_ASSERT( top < bottom );
317#ifdef DEBUG_FRAMELAYOUT
318 kdDebug(32002) << " footnote rect:" << rect << " - new_top:" << top << " new_bottom:" << bottom << endl;
319#endif
320 resizeOrCreateHeaderFooter( fs, frameNum, rect );
321 firstFootNote = false;
322
323 // We added a footnote, update main text frame size
324#ifdef DEBUG_FRAMELAYOUT
325 kdDebug(32002) << " Laid out a footnote -> call resizeMainTextFrame/checkFootNotes again" << endl;
326#endif
327 resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, bottom, WithFootNotes );
328 checkFootNotes();
329 }
330 } // for all footnotes
331
332 // Check for endnotes, on the last page of main text
333 // and on any end-notes-only page, i.e. after the last page of main text
334 if ( pageNum >= m_lastMainFramePage && m_doc->hasEndNotes() ) {
335 bool pageHasMainText = ( pageNum == m_lastMainFramePage );
336 if ( pageHasMainText )
337 lastMainFrame->setDrawFootNoteLine( true );
338 double textBottom = pageHasMainText ? lastMainFrameBottom : top;
339 // Leave some space on top of the endnotes, for the horizontal line
340 double endNoteTop = textBottom + m_doc->ptFootnoteBodySpacing();
341#ifdef DEBUG_FRAMELAYOUT
342 kdDebug(32002) << " Endnotes: textBottom=" << textBottom << "pt, endNoteTop=" << endNoteTop << "pt, bottom=" << bottom << "pt" << endl;
343#endif
344 bool firstEndNote = true;
345 for ( TQPtrListIterator<HeaderFooterFrameset> it( m_endnotes ); it.current() ; ++it )
346 {
347 if ( ! it.current()->m_positioned )
348 {
349 KWTextFrameSet* fs = it.current()->m_frameset;
350#ifdef DEBUG_FRAMELAYOUT
351 kdDebug(32002) << " Page " << pageNum << ": adding endnote frame from " << fs->name() << endl;
352#endif
353 double frameHeight = it.current()->m_height;
354 if ( it.current()->m_startAtPage < 0 ) // not set yet
355 it.current()->m_startAtPage = pageNum;
356
357 // Check if the endnote is bigger than the available space
358 if ( endNoteTop + frameHeight > bottom )
359 {
360 // In the current page we stop at bottom
361 frameHeight = bottom - endNoteTop;
362
363 if ( frameHeight > 1E-10 ) // means, if frameHeight > 0
364 {
365#ifdef DEBUG_FRAMELAYOUT
366 kdDebug(32002) << " endnote: new height=" << frameHeight << " remaining height=" << it.current()->m_height - frameHeight << endl;
367#endif
368 Q_ASSERT( frameHeight < it.current()->m_height );
369 it.current()->m_height -= frameHeight; // calculate what remains to be done in the next frame
370 } else {
371 // No room at all on this page. Schedule for next page.
372 it.current()->m_startAtPage++;
373 break;
374 }
375 // Make sure there'll actually be a next page
376 if ( pageNum == m_doc->pageCount()-1 ) {
377#ifdef DEBUG_FRAMELAYOUT
378 kdDebug(32002) << "Adding a page for the endnote overflow." << endl;
379#endif
380 m_doc->appendPage();
381 m_doc->updateAllFrames();
382 toPage = m_doc->pageCount()-1;
383 }
384 }
385 else // It'll all fit in this page
386 {
387 it.current()->m_positioned = true;
388 }
389 KoRect rect( left, endNoteTop, right - left, frameHeight );
390 endNoteTop += frameHeight + 1; // not + it.current()->m_spacing;
391 Q_ASSERT( bottom > 0 );
392#ifdef DEBUG_FRAMELAYOUT
393 kdDebug(32002) << " rect:" << rect << " - new_top:" << endNoteTop << " new_bottom:" << bottom << endl;
394#endif
395 int frameNum = pageNum - it.current()->m_startAtPage;
396 resizeOrCreateHeaderFooter( fs, frameNum, rect );
397
398#if 0 // Disabled. The main frame is resized by KWTextFrameSet::slotAfterFormatting already.
399 if ( pageHasMainText && firstEndNote )
400 {
401 // We positionned the first endnote, update main text frame size
402#ifdef DEBUG_FRAMELAYOUT
403 kdDebug(32002) << " Laid out an endnote and the page has a maintextframe too -> call resizeMainTextFrame/checkFootNotes again top=" << top << " textBottom=" << textBottom << endl;
404#endif
405 resizeMainTextFrame( mainTextFrameSet, pageNum, numColumns, ptColumnWidth, m_doc->ptColumnSpacing(), left, top, textBottom, NoChange );
406 }
407#endif
408 } // if not positionned yet
409 firstEndNote = false; // yes, out of the if
410 } // for all endnotes
411 } // if page can have endnotes
412
413 if ( mainTextFrameResized == -1 ) {
414 // Test if the main text frame for this page was really resized or not.
415 KoRect newColumnRect = firstColumnRect( mainTextFrameSet, pageNum, numColumns );
416#ifdef DEBUG_FRAMELAYOUT
417 kdDebug(32002) << " Comparing old=" << oldColumnRect << " and new=" << newColumnRect << endl;
418#endif
419 if ( oldColumnRect != newColumnRect ) {
420 mainTextFrameResized = pageNum;
421#ifdef DEBUG_FRAMELAYOUT
422 kdDebug(32002) << " changed -> mainTextFrameResized=" << mainTextFrameResized << endl;
423#endif
424 }
425 }
426
427 } // for all pages
428 m_lastMainFramePage = lastMainFrame->pageNumber();
429#ifdef DEBUG_FRAMELAYOUT
430 kdDebug(32002) << "m_lastMainFramePage = " << m_lastMainFramePage << " lastMainFrameBottom=" << lastMainFrameBottom << endl;
431#endif
432
433 if ( ! ( flags & DontRemovePages ) )
434 {
435 m_doc->updateAllFrames( KWFrameSet::UpdateFramesInPage );
436 // Check if the last page is now empty (e.g. this can happen when removing
437 // some text above an endnote, so the endnote moves up)
438 (void)m_doc->tryRemovingPages();
439 }
440
441 const int lastPage = m_doc->lastPage();
442 // Final cleanup: delete all frames after lastFrameNumber in each frameset
443 TQPtrListIterator<HeaderFooterFrameset> it( m_headersFooters );
444 for ( ; it.current() ; ++it )
445 if ( it.current()->deleteFramesAfterLast( lastPage ) )
446 m_framesetsToUpdate.insert( it.current()->m_frameset, true );
447 TQPtrListIterator<HeaderFooterFrameset> it2( m_footnotes );
448 for ( ; it2.current() ; ++it2 )
449 if ( it2.current()->deleteFramesAfterLast( lastPage ) )
450 m_framesetsToUpdate.insert( it2.current()->m_frameset, true );
451 if ( mainTextFrameSet ) {
452 // For the last main text frameset, we use m_lastMainFramePage, so that
453 // there's no frame on the "end notes only" page(s).
454 int lastFrame = m_lastMainFramePage * numColumns + (numColumns-1);
455#ifdef DEBUG_FRAMELAYOUT
456 kdDebug(32002) << "lastFrame: " << lastFrame << " due to " << m_lastMainFramePage << endl;
457#endif
458 bool deleted = false;
459 while ( (int)mainTextFrameSet->frameCount() - 1 > lastFrame ) {
460#ifdef DEBUG_FRAMELAYOUT
461 kdDebug(32002) << " Final cleanup: deleting frame " << mainTextFrameSet->frameCount() - 1 << " of main textframeset (lastFrame=" << lastFrame << ")" << endl;
462#endif
463 mainTextFrameSet->deleteFrame( mainTextFrameSet->frameCount() - 1, true, false /*do not updateFrames!*/ );
464 deleted = true;
465 }
466 if ( deleted )
467 m_framesetsToUpdate.insert( mainTextFrameSet, true );
468 // The last frame before the first endnote, is in auto-extend mode
469 if ( m_doc->hasEndNotes() ) {
470 KWFrame* lastMainFrame = mainTextFrameSet->frameIterator().getLast();
471 if ( lastMainFrame->frameBehavior() != KWFrame::AutoExtendFrame )
472 {
473 lastMainFrame->setFrameBehavior( KWFrame::AutoExtendFrame );
474 // make sure it gets resized
475 if ( mainTextFrameResized == -1 )
476 mainTextFrameResized = lastMainFrame->pageNumber();
477 }
478 }
479 }
480
481 TQMap<KWFrameSet*, bool>::iterator fsit = m_framesetsToUpdate.begin();
482 for ( ; fsit != m_framesetsToUpdate.end() ; ++fsit )
483 fsit.key()->updateFrames();
484
485 // ## TODO: only if something changed? (resizing, new frames, or deleted frames...)
486 KWFrameList::recalcFrames(m_doc, fromPage, toPage);
487
488 if ( mainTextFrameResized != -1 && mainTextFrameSet->type() == FT_TEXT ) {
489#ifdef DEBUG_FRAMELAYOUT
490 kdDebug(32002) << " Done. First maintextframe resized: " << mainTextFrameResized << endl;
491#endif
492 KWTextFrameSet* fs = static_cast<KWTextFrameSet *>(mainTextFrameSet);
493
494 // Not right now, this could be called during formatting...
495 //m_doc->invalidate();
496 // ### This means the layout will be done during painting. Not good.
497 // What about mainTextFrameSet->invalidate() or even layout()?
498 //TQTimer::singleShot( 0, m_doc, TQ_SLOT( invalidate() ) );
499
500 // Invalidate main textframeset only, and from top of page only.
501 // Otherwise loading a long document (with headers/footers) takes ages,
502 // if we redo it all from the beginning at each new page!
503 int topLU, bottomLU;
504 if ( fs->minMaxInternalOnPage( mainTextFrameResized, topLU, bottomLU ) )
505 {
506 // Find parag at topLU
507 KoTextParag* parag = fs->paragAtLUPos( topLU );
508 if ( parag ) {
509#ifdef DEBUG_FRAMELAYOUT
510 kdDebug(32002) << " Invalidating from parag " << parag->paragId() << endl;
511#endif
512 fs->textObject()->setLastFormattedParag( parag );
513 fs->textObject()->formatMore( 2 );
514 }
515 }
516 }
517}
518
519void KWFrameLayout::resizeOrCreateHeaderFooter( KWTextFrameSet* headerFooter, uint frameNumber, const KoRect& rect )
520{
521 if ( frameNumber < headerFooter->frameCount() ) {
522 KWFrame* frame = headerFooter->frame( frameNumber );
523 if ( *frame == rect )
524 return;
525 frame->setRect( rect );
526#ifdef DEBUG_FRAMELAYOUT
527 kdDebug(32002) << "KWFrameLayout::resizeOrCreateHeaderFooter frame " << headerFooter->name() << " " << frame << " resized to " << rect << " pagenum=" << frame->pageNumber() << endl;
528#endif
529 }
530 else
531 {
532#ifdef DEBUG_FRAMELAYOUT
533 kdDebug(32002) << "KWFrameLayout::resizeOrCreateHeaderFooter creating frame for " << headerFooter->name() << endl;
534#endif
535 KWFrame* frame = new KWFrame( headerFooter, rect.x(), rect.y(), rect.width(), rect.height() );
536 frame->setFrameBehavior( KWFrame::AutoExtendFrame );
537 if ( headerFooter->isHeaderOrFooter() ) // not for footnotes!
538 {
539 frame->setNewFrameBehavior( KWFrame::Copy );
540 frame->setCopy( true );
541 }
542 else
543 frame->setNewFrameBehavior( KWFrame::NoFollowup );
544 headerFooter->addFrame( frame, false /*no recalc*/ );
545 }
546 // This updates e.g. availableHeight. Very important in the case
547 // of the footnote frameset with 2 frames.
548 headerFooter->updateFrames( 0 /*fast one*/ );
549 m_framesetsToUpdate.insert( headerFooter, true );
550}
551
552// Called at beginning and end of the layout for a given page,
553// to determine if the main-text-frame layout really changed or not.
554// Testing in resizeMainTextFrame doesn't work, we call it several times,
555// once for each footnote, so it can't detect the "no change" case.
556KoRect KWFrameLayout::firstColumnRect( KWFrameSet* mainTextFrameSet, int pageNum, int numColumns ) const
557{
558 uint frameNum = pageNum * numColumns /*+ col 0 here*/;
559 if ( mainTextFrameSet && frameNum < mainTextFrameSet->frameCount() )
560 return * mainTextFrameSet->frame( frameNum );
561 else
562 return KoRect();
563}
564
565bool KWFrameLayout::resizeMainTextFrame( KWFrameSet* mainTextFrameSet, int pageNum, int numColumns, double ptColumnWidth, double ptColumnSpacing, double left, double top, double bottom, HasFootNotes hasFootNotes )
566{
567 if ( !mainTextFrameSet )
568 return false;
569 bool mainTextFrameResized = false;
570 for ( int col = 0; col < numColumns; col++ ) {
571 Q_ASSERT( bottom > top );
572 // Calculate wanted rect for this frame
573 KoRect rect( left + col * ( ptColumnWidth + ptColumnSpacing ),
574 top, ptColumnWidth, bottom - top );
575 uint frameNum = (pageNum - m_doc->startPage()) * numColumns + col;
576 KWFrame* frame;
577 if ( frameNum < mainTextFrameSet->frameCount() ) {
578 // Resize existing frame
579 frame = mainTextFrameSet->frame( frameNum );
580 // Special case for last-frame-before-endnotes: don't resize its bottom
581 if ( m_doc->hasEndNotes() && pageNum >= m_lastMainFramePage )
582 rect.setBottom( frame->bottom() );
583 bool resized = (rect != *frame);
584 if ( resized ) {
585#ifdef DEBUG_FRAMELAYOUT
586 kdDebug(32002) << " Page " << pageNum << ": resizing main text frame " << frameNum << "(" << frame << ") to " << rect << endl;
587#endif
588 frame->setRect( rect );
589 frame->updateRulerHandles();
590 mainTextFrameResized = true;
591 mainTextFrameSet->updateFrames( 0xff - KWFrameSet::SortFrames ); // Don't sort frames yet!
592 }
593 } else {
594 // Create new frame
595 frame = new KWFrame( mainTextFrameSet, rect.x(), rect.y(), rect.width(), rect.height() );
596#ifdef DEBUG_FRAMELAYOUT
597 kdDebug(32002) << " Page " << pageNum << ": creating new main text frame " << frameNum << "(" << frame << ") to " << rect << endl;
598#endif
599 mainTextFrameSet->addFrame( frame );
600 Q_ASSERT( frameNum == mainTextFrameSet->frameCount()-1 );
601 mainTextFrameResized = true;
602 mainTextFrameSet->updateFrames( 0xff - KWFrameSet::SortFrames ); // Don't sort frames yet!
603 }
604 if ( hasFootNotes == NoFootNote )
605 frame->setDrawFootNoteLine( false );
606 else if ( hasFootNotes == WithFootNotes )
607 frame->setDrawFootNoteLine( true );
608 // unchanged in the other cases
609 // By default, all main-text frames are in "auto-create new frames" mode
610 frame->setFrameBehavior( KWFrame::AutoCreateNewFrame );
611 }
612 return mainTextFrameResized;
613}
614
615void KWFrameLayout::checkFootNotes()
616{
617 // We recalculate all footnotes pages, but we return true
618 // if those on pageNum have changed.
619 TQPtrListIterator<HeaderFooterFrameset> it( m_footnotes );
620 for ( ; it.current() ; ++it )
621 {
622 HeaderFooterFrameset* hff = it.current();
623 if ( ! hff->m_positioned )
624 {
625 Q_ASSERT ( hff->m_frameset->isFootEndNote() );
626 KWFootNoteFrameSet* fnfs = static_cast<KWFootNoteFrameSet *>( hff->m_frameset );
627 KWFootNoteVariable* fnvar = fnfs->footNoteVariable();
628 //necessary to test paragraph because when we delete mutli
629 //footnote, first footnote who delete call setDelete(true)
630 //and force recalc, but with multi footnote deleted
631 //paragraph is null before we apply attribute to
632 //kotextcustom.
633 if ( !fnvar || !fnvar->paragraph() )
634 continue;
635 double varY = fnvar->varY();
636 if ( varY == 0 ) // not able to calculate it yet
637 continue;
638 hff->m_minY = varY + /*2 * */ hff->m_spacing + 2 /* some spacing */;
639 int pageNum = m_doc->pageManager()->pageNumber(varY);
640 if ( pageNum != hff->m_startAtPage ) {
641#ifdef DEBUG_FRAMELAYOUT
642 kdDebug(32002) << " checkFootNotes: found minY=" << hff->m_minY << " start/end=" << pageNum << " for footnote " << fnvar->text() << endl;
643#endif
644 hff->m_startAtPage = pageNum;
645 hff->m_endAtPage = pageNum;
646 }
647 }
648 }
649}
static void recalcFrames(KWDocument *doc, int pageFrom, int pageTo)
Update the z-ordering for all frames in the page section of the whole document.
virtual void deleteFrame(unsigned int num, bool remove=true, bool recalc=true)
Delete a frame from the set of frames this frameSet has.
virtual FrameSetType type() const
The type of frameset.
Definition KWFrameSet.h:76
@ SortFrames
kwtextframeset only
Definition KWFrameSet.h:297
const TQPtrList< KWFrame > & frameIterator() const
Iterator over the child frames.
Definition KWFrameSet.h:159
unsigned int frameCount() const
Get number of child frames.
Definition KWFrameSet.h:163
virtual void updateFrames(int flags=0xff)
Called when our frames change, or when another frameset's frames change.
KWFrame * frame(unsigned int _num) const
get a frame by number
TQString name() const
get/set frameset name. For tables in particular, this must be unique
Definition KWFrameSet.h:428
bool hasFramesInPageArray() const
Allows to detect that updateFrames() hasn't been called yet (e.g.
Definition KWFrameSet.h:311
void setDrawFootNoteLine(bool b)
Only applicable to frames of the main text frameset.
Definition KWFrame.h:297
int pageNumber() const
The page on which this frame is.
Definition KWFrame.cpp:116
double height() const
return the height of this page (in pt)
Definition KWPage.cpp:44
double leftMargin() const
return the width of the margin at left (in pt);
Definition KWPage.cpp:97
double topMargin() const
return the height of the margin at top (in pt);
Definition KWPage.cpp:87
double bottomMargin() const
return the height of the margin at bottom (in pt);
Definition KWPage.cpp:92
double width() const
return the width of this page (in pt)
Definition KWPage.cpp:38
double rightMargin() const
return the width of the margin at left (in pt);
Definition KWPage.cpp:108
double offsetInDocument() const
All Pages are in a document-wide coordinate system that simply puts every page under the next page an...
Definition KWPage.cpp:128
Class: KWTextFrameSet Contains text (KoTextObject) and frames to display that text.
KoTextObject * textObject() const
Return the contained text object.
virtual void updateFrames(int flags=0xff)
reshuffle frames so text is always displayed from top-left down and then right.
virtual void deleteFrame(unsigned int num, bool remove=true, bool recalc=true)
Delete a frame from the set of frames this frameSet has.
KoTextParag * paragAtLUPos(int yLU) const
Find the parag at the given Y position (in LU).
bool minMaxInternalOnPage(int pageNum, int &topLU, int &bottomLU) const
Return the min and max LU coordinates for the text in the given page, if the frameset has any frames ...
virtual void setVisible(bool v)
set the visibility of the frameset.