1 /* This file is part of the KDE project
2  * Copyright (C) 2010-2015 C. Boemann <cbo@boemann.dk>
3  * Copyright (C) 2006,2011 Sebastian Sauer <mail@dipe.org>
4  * Copyright (C) 2006-2007, 2010 Thomas Zander <zander@kde.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "KWRootAreaProvider.h"
23 #include "KWPageManager.h"
24 #include "KWDocument.h"
25 #include "KWView.h"
26 #include "KWPage.h"
27 #include "frames/KWCopyShape.h"
28 #include "frames/KWTextFrameSet.h"
29 #include "frames/KWFrameLayout.h"
30 
31 #include <KoTextLayoutRootArea.h>
32 #include <KoShape.h>
33 #include <KoShapeContainer.h>
34 #include <KoShapeFactoryBase.h>
35 #include <KoTextShapeData.h>
36 #include <KoTextDocumentLayout.h>
37 #include <KoTextLayoutObstruction.h>
38 #include <KoSelection.h>
39 #include <KoCanvasBase.h>
40 #include <KoShapeAnchor.h>
41 #include <KoColumns.h>
42 
43 #include <QTimer>
44 #include <WordsDebug.h>
45 
46 class KWTextLayoutRootArea : public KoTextLayoutRootArea
47 {
48     public:
KWTextLayoutRootArea(KoTextDocumentLayout * documentLayout,KWTextFrameSet * frameSet,int pageNumber)49         KWTextLayoutRootArea(KoTextDocumentLayout *documentLayout, KWTextFrameSet *frameSet, int pageNumber) : KoTextLayoutRootArea(documentLayout), m_frameSet(frameSet), m_pageNumber(pageNumber) {
50             //debugWords;
51         }
~KWTextLayoutRootArea()52         ~KWTextLayoutRootArea() override {
53             //debugWords;
54         }
layout(FrameIterator * cursor)55         virtual bool layout(FrameIterator *cursor) {
56             //debugWords << "pageNumber=" << m_pageNumber << "frameSetType=" << Words::frameSetTypeName(m_frameSet->textFrameSetType()) << "isDirty=" << isDirty();
57             bool ok = KoTextLayoutRootArea::layout(cursor);
58             return ok;
59         }
60         KWTextFrameSet *m_frameSet;
61         int m_pageNumber;
62 };
63 
KWRootAreaProvider(KWTextFrameSet * textFrameSet)64 KWRootAreaProvider::KWRootAreaProvider(KWTextFrameSet *textFrameSet)
65     : KWRootAreaProviderBase(textFrameSet)
66 {
67 }
68 
~KWRootAreaProvider()69 KWRootAreaProvider::~KWRootAreaProvider()
70 {
71     qDeleteAll(m_rootAreaCache);
72     m_rootAreaCache.clear();
73     qDeleteAll(m_pages);
74     m_pages.clear();
75 }
76 
clearPages(int pageNumber)77 void KWRootAreaProvider::clearPages(int pageNumber)
78 {
79     if (pageNumber > pages().count()) {
80         return;
81     }
82 
83     KoTextDocumentLayout *lay = dynamic_cast<KoTextDocumentLayout*>(frameSet()->document()->documentLayout());
84     Q_ASSERT(lay);
85     int prevPageIndex = pageNumber - 2;
86     do {
87         KWRootAreaPage *prevPage = prevPageIndex >= 0 && prevPageIndex < pages().count() ? pages()[prevPageIndex] : 0;
88         if (prevPage) {
89             if (prevPage->rootAreas.isEmpty()) {
90                 --prevPageIndex;
91                 continue; // this page doesn't have any root-areas so try the next previous page
92             }
93             QList<KoTextLayoutRootArea *> rootAreas = prevPage->rootAreas;
94             foreach(KoTextLayoutRootArea *area, rootAreas) {
95                 releaseAllAfter(area);
96                 lay->removeRootArea(area);
97             }
98         } else {
99             releaseAllAfter(0);
100             lay->removeRootArea(0);
101         }
102     } while(false);
103 }
104 
addDependentProvider(KWRootAreaProviderBase * provider,int pageNumber)105 void KWRootAreaProvider::addDependentProvider(KWRootAreaProviderBase *provider, int pageNumber)
106 {
107     debugWords;
108     KoTextDocumentLayout *lay = dynamic_cast<KoTextDocumentLayout*>(provider->frameSet()->document()->documentLayout());
109     Q_ASSERT(lay);
110     lay->setContinuousLayout(false); // to abort the current layout-loop
111     lay->setBlockLayout(true); // to prevent a new layout-loop from being started
112 
113     m_dependentProviders.append(QPair<KWRootAreaProviderBase *, int>(provider, pageNumber));
114 }
115 
setPageDirty(int pageNumber)116 void KWRootAreaProvider::setPageDirty(int pageNumber)
117 {
118     if (pageNumber - 1 < m_pages.count()) {
119         KWRootAreaPage *page = m_pages[pageNumber - 1];
120         foreach(KoTextLayoutRootArea *rootArea, page->rootAreas) {
121             rootArea->setDirty(); // be sure the root-areas from the page are relayouted
122         }
123     }
124 }
125 
handleDependentProviders(int pageNumber)126 void KWRootAreaProvider::handleDependentProviders(int pageNumber)
127 {
128     QList<KoTextDocumentLayout *> layouts;
129     for(int i = m_dependentProviders.count() - 1; i >= 0; --i) {
130         QPair<KWRootAreaProviderBase *, int> p = m_dependentProviders[i];
131         if (p.second > pageNumber) { // only handle providers which would continue layouting at the page we just processed
132             continue;
133         }
134         m_dependentProviders.removeAt(i); // this one is handled now
135         p.first->setPageDirty(pageNumber);
136         KoTextDocumentLayout *lay = dynamic_cast<KoTextDocumentLayout*>(p.first->frameSet()->document()->documentLayout());
137         Q_ASSERT(lay);
138         if (!layouts.contains(lay)) {
139             layouts.append(lay);
140         }
141     }
142 
143     foreach(KoTextDocumentLayout *lay, layouts) {
144         lay->setContinuousLayout(true); // to not abort the current layout-loop any longer
145         lay->setBlockLayout(false); // allow layouting again
146         lay->layout(); // continue layouting but don't schedule so we are sure it's done instantly
147     }
148 }
149 
provideNext(KoTextDocumentLayout * documentLayout,const RootAreaConstraint & constraints)150 KoTextLayoutRootArea* KWRootAreaProvider::provideNext(KoTextDocumentLayout *documentLayout, const RootAreaConstraint &constraints)
151 {
152     KWDocument *kwdoc = frameSet()->wordsDocument();
153     KWPageManager *pageManager = kwdoc->pageManager();
154     Q_ASSERT(pageManager);
155 
156     int pageNumber = 1;
157     KWRootAreaPage *rootAreaPage = m_pages.isEmpty() ? 0 : m_pages.last();
158     int requiredRootAreaCount = 1;
159     if (rootAreaPage && frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
160         Q_ASSERT(rootAreaPage->page.isValid());
161         Q_ASSERT(rootAreaPage->page.pageStyle().isValid());
162         requiredRootAreaCount = rootAreaPage->page.pageStyle().columns().count;
163         if (constraints.newPageForced) {
164             requiredRootAreaCount = 1;
165         }
166     }
167     if (rootAreaPage && rootAreaPage->rootAreas.count() < requiredRootAreaCount) {
168         pageNumber = m_pages.count(); // the root-area is still on the same page
169     } else {
170         pageNumber = m_pages.count() + 1; // the root-area is the first on a new page
171 
172         if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
173             // Create missing KWPage's (they will also create a KWFrame and TextShape per page)
174             for(int i = pageManager->pageCount(); i < pageNumber; ++i) {
175                 KWPage page = kwdoc->appendPage(constraints.masterPageName);
176                 Q_ASSERT(page.isValid());
177                 if (constraints.visiblePageNumber >= 0)
178                     page.setVisiblePageNumber(constraints.visiblePageNumber);
179             }
180         } else if (pageNumber > pageManager->pageCount()) {
181             if (Words::isHeaderFooter(frameSet())) {
182                 // Headers and footers are special in that they continue with every page what is why they depend on the mainframe.
183                 KWRootAreaProvider *provider = (KWRootAreaProvider *)kwdoc->frameLayout()->mainFrameSet()->rootAreaProvider();
184                 provider->addDependentProvider(this, pageNumber);
185             }
186             return 0; // not ready to layout this yet
187         }
188 
189         KWPage page = pageManager->page(pageNumber);
190         Q_ASSERT(page.isValid());
191         if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
192             if (constraints.visiblePageNumber >= 0)
193                 page.setVisiblePageNumber(constraints.visiblePageNumber);
194             else
195                 page.setVisiblePageNumber(0);
196         }
197         rootAreaPage = new KWRootAreaPage(page);
198         m_pages.append(rootAreaPage);
199     }
200 
201     debugWords << "pageNumber=" << pageNumber <<  "frameSet=" << Words::frameSetTypeName(frameSet()->textFrameSetType());
202     if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
203         handleDependentProviders(pageNumber);
204     }
205     // Determinate the frames that are on the page. Note that the KWFrameLayout only knows
206     // about header, footer and the mainframes but not about all other framesets.
207     QList<KoShape *> shapes;
208 
209     if (frameSet()->type() == Words::OtherFrameSet || frameSet()->textFrameSetType() == Words::OtherTextFrameSet) {
210         if (KoShape *s = frameSet()->shapes().value(pageNumber - 1))
211             shapes = QList<KoShape *>() << s;
212     } else {
213         shapes = kwdoc->frameLayout()->sequencedShapesOn(frameSet(), pageNumber);
214     }
215 
216     // position OtherFrameSet's which are anchored to this page
217     if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
218         foreach(KWFrameSet* fs, kwdoc->frameSets()) {
219             KWTextFrameSet *tfs = dynamic_cast<KWTextFrameSet*>(fs);
220             if (tfs && tfs->textFrameSetType() != Words::OtherTextFrameSet)
221                 continue;
222             foreach (KWFrame *frame, fs->frames()) {
223                 KoShape *shape = frame->shape();
224                 int anchoredPageNumber = shape->anchor() ? shape->anchor()->pageNumber() : -1;
225                 if (anchoredPageNumber == pageNumber) {
226                     qreal oldOffset = frame->anchoredFrameOffset();
227                     qreal newOffset = rootAreaPage->page.offsetInDocument();
228                     if (!qFuzzyCompare(1 + oldOffset, 1 + newOffset)) {
229                         frame->setAnchoredFrameOffset(newOffset);
230                         QPointF pos(shape->position().x(), newOffset - oldOffset + shape->position().y());
231                         shape->setPosition(pos);
232                     }
233 
234                     // During load we make page anchored shapes invisible, because otherwise
235                     // they leave empty rects in the text if there is run-around
236                     // now is the time to make them visible again
237                     shape->setVisible(true);
238 
239                     QPointF delta;
240                     KWFrameLayout::proposeShapeMove(shape, delta, rootAreaPage->page);
241                     shape->setPosition(shape->position() + delta);
242                }
243             }
244         }
245     } else {
246         if (!documentLayout->referencedLayout()) {
247             KoTextDocumentLayout *reflay = dynamic_cast<KoTextDocumentLayout*>(kwdoc->frameLayout()->mainFrameSet()->document()->documentLayout());
248             documentLayout->setReferencedLayout(reflay);
249         }
250     }
251 
252     KoShape *shape = rootAreaPage->rootAreas.count() < shapes.count() ? shapes[rootAreaPage->rootAreas.count()] : 0;
253 
254     KWTextLayoutRootArea *area = new KWTextLayoutRootArea(documentLayout, frameSet(), pageNumber);
255     if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
256         if (rootAreaPage->page.pageStyle().columns().count > 1) {
257             area->setAcceptsColumnBreak(true);
258         }
259         area->setAcceptsPageBreak(true);
260     }
261 
262     if (shape) { // Not every KoTextLayoutRootArea has a KoShape for display purposes.
263         //Q_ASSERT_X(pageNumber == pageManager->page(shape).pageNumber(), __FUNCTION__, QString("KWPageManager is out-of-sync, pageNumber=%1 vs pageNumber=%2 with offset=%3 vs offset=%4 on frameSetType=%5").arg(pageNumber).arg(pageManager->page(shape).pageNumber()).arg(shape->absolutePosition().y()).arg(pageManager->page(shape).offsetInDocument()).arg(Words::frameSetTypeName(frameSet()->textFrameSetType())).toLocal8Bit());
264         KoTextShapeData *data = qobject_cast<KoTextShapeData*>(shape->userData());
265         if (data) {
266             data->setRootArea(area);
267             area->setAssociatedShape(shape);
268         } else {
269             warnWords << "shape has no KoTextShapeData";
270         }
271         if ((!shape->anchor()) || shape->anchor()->anchorType() == KoShapeAnchor::AnchorPage) {
272             area->setPage(new KWPage(rootAreaPage->page));
273         }
274     }
275 
276     if (frameSet()->type() != Words::OtherFrameSet && frameSet()->textFrameSetType() != Words::OtherTextFrameSet) {
277         // Only header, footer and main-frames have an own KoTextPage. All other frames are embedded into them and inherit the KoTextPage from them.
278         area->setPage(new KWPage(rootAreaPage->page));
279         area->setLayoutEnvironmentResctictions(true, false);
280     } else {
281         area->setLayoutEnvironmentResctictions(true, true);
282     }
283 
284     m_pageHash[area] = rootAreaPage;
285     rootAreaPage->rootAreas.append(area);
286 
287     return area;
288 }
289 
provide(KoTextDocumentLayout * documentLayout,const RootAreaConstraint & constraints,int requestedPosition,bool * isNewArea)290 KoTextLayoutRootArea *KWRootAreaProvider::provide(KoTextDocumentLayout* documentLayout, const RootAreaConstraint& constraints, int requestedPosition, bool *isNewArea)
291 {
292     KWPageManager *pageManager = frameSet()->wordsDocument()->pageManager();
293     Q_ASSERT(pageManager);
294     if (pageManager->pageCount() == 0) // not ready yet (may happen e.g. on loading a document)
295         return 0;
296 
297     QString reallyNeededPageStyle = constraints.masterPageName;
298     int visiblePageNumber = constraints.visiblePageNumber;
299     bool newPageForced = constraints.newPageForced;
300     if (m_rootAreaCache.size() > requestedPosition)
301     {
302         KoTextLayoutRootArea *rootArea = m_rootAreaCache[requestedPosition];
303         Q_ASSERT(rootArea);
304 
305         if (frameSet()->textFrameSetType() != Words::MainTextFrameSet)
306         {
307             // No constraints except for the main frame set
308             *isNewArea = false;
309             return rootArea;
310         }
311 
312         KWRootAreaPage *rootAreaPage = m_pageHash.value(rootArea);
313         Q_ASSERT(rootAreaPage);
314 
315         if (constraints.visiblePageNumber >= 0)
316             rootAreaPage->page.setVisiblePageNumber(constraints.visiblePageNumber);
317         else
318             rootAreaPage->page.setVisiblePageNumber(0);
319 
320         QString reallyNeededPageStyle = constraints.masterPageName;
321         if (reallyNeededPageStyle.isNull())
322         {
323             // We must work using the previous pages...
324             if (requestedPosition == 0)
325             {
326                 reallyNeededPageStyle = pageManager->defaultPageStyle().name();
327             }
328             else
329             {
330                 KWRootAreaPage *previousAreaPage = m_pageHash.value(m_rootAreaCache[requestedPosition - 1]);
331                 reallyNeededPageStyle = previousAreaPage->page.pageStyle().nextStyleName();
332                 if (reallyNeededPageStyle.isNull())
333                     reallyNeededPageStyle = previousAreaPage->page.pageStyle().name();
334             }
335         }
336 
337         if (rootAreaPage->page.masterPageName() != reallyNeededPageStyle)
338         {
339             //TODO : recycle pages in order to save us a lot of effort and reduce risks of flickering, especially with very long documents
340             releaseAllAfter(rootArea);
341         }
342         else
343         {
344             *isNewArea = false;
345             return rootArea;
346         }
347     }
348 
349     // We are interested in the first KoTextLayoutRootArea that has a shape associated for display
350     // purposes. This can mean that multiple KoTextLayoutRootArea are created but only selected
351     // ones that should be layouted and displayed are passed on to the textlayout-library.
352     // This is only done for headers and footers cause they are continuous whereas for example
353     // Words::OtherFrameSet and Words::OtherTextFrameSet framesets may not have the correct position
354     // or not shape assigned at this point but later.
355     RootAreaConstraint realConstraints;
356     realConstraints.masterPageName = reallyNeededPageStyle;
357     realConstraints.visiblePageNumber = visiblePageNumber;
358     realConstraints.newPageForced = newPageForced;
359     KoTextLayoutRootArea *area = 0;
360     do {
361         area = provideNext(documentLayout, realConstraints);
362         if (m_rootAreaCache.size() <= requestedPosition)
363             m_rootAreaCache.append(area);
364     } while(Words::isHeaderFooter(frameSet()) && area && !area->associatedShape());
365 
366     Q_ASSERT(m_rootAreaCache.size() > requestedPosition);
367 
368     if (area == 0 && (frameSet()->textFrameSetType() != Words::MainTextFrameSet) && requestedPosition == 0)
369         m_rootAreaCache.clear();
370     *isNewArea = true;
371 
372     return area;
373 }
374 
375 // afterThis==nullptr means delete everything
releaseAllAfter(KoTextLayoutRootArea * afterThis)376 void KWRootAreaProvider::releaseAllAfter(KoTextLayoutRootArea *afterThis)
377 {
378     int afterIndex = -1;
379     if (afterThis) {
380         if (!m_pageHash.contains(afterThis))
381             return;
382         KWRootAreaPage *page = m_pageHash.value(afterThis);
383         afterIndex = m_pages.indexOf(page);
384         Q_ASSERT(afterIndex >= 0);
385 
386         int newSize = m_rootAreaCache.indexOf(afterThis) + 1;
387         while (m_rootAreaCache.size() != newSize)
388         {
389             KoTextLayoutRootArea *oldArea = m_rootAreaCache.takeLast();
390             delete(oldArea);
391         }
392     }
393 
394     debugWords << "afterPageNumber=" << afterIndex+1;
395 
396     bool atLeastOnePageRemoved = false;
397     KWPageManager *pageManager = frameSet()->wordsDocument()->pageManager();
398     if (afterIndex >= 0) {
399         for(int i = m_pages.count() - 1; i > afterIndex; --i) {
400             KWRootAreaPage *page = m_pages.takeLast();
401             foreach(KoTextLayoutRootArea *area, page->rootAreas)
402                 m_pageHash.remove(area);
403             delete page;
404 
405             if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
406                 pageManager->removePage(i+1);
407                 atLeastOnePageRemoved = true;
408             }
409         }
410 
411         // FIXME
412         for(int i = m_dependentProviders.count() - 1; i >= 0; --i) {
413             QPair<KWRootAreaProviderBase *, int> p = m_dependentProviders[i];
414             if (p.second >= afterIndex)
415                 m_dependentProviders.removeAt(i);
416         }
417     } else {
418         //atLeastOnePageRemoved = !m_pages.isEmpty();
419         qDeleteAll(m_pages);
420         qDeleteAll(m_rootAreaCache);
421         m_pages.clear();
422         m_pageHash.clear();
423         m_rootAreaCache.clear();
424 
425         /*FIXME that would result in flickering :-/
426         for(int i = pageManager->pageCount(); i >= 1; --i)
427             pageManager->removePage(i);
428         */
429 
430         /*FIXME
431         m_dependentProviders.clear();
432         */
433     }
434      if (atLeastOnePageRemoved)
435          frameSet()->wordsDocument()->firePageSetupChanged();
436 }
437 
doPostLayout(KoTextLayoutRootArea * rootArea,bool isNewRootArea)438 void KWRootAreaProvider::doPostLayout(KoTextLayoutRootArea *rootArea, bool isNewRootArea)
439 {
440     KWDocument *kwdoc = const_cast<KWDocument*>(frameSet()->wordsDocument());
441     KWPageManager *pageManager = kwdoc->pageManager();
442     Q_ASSERT(pageManager);
443 
444     if (frameSet()->textFrameSetType() != Words::MainTextFrameSet) {
445         if (m_pages.count() > pageManager->pageCount()) {
446             // we need to wait for the mainFrameSet to finish till we are able to continue
447             KWRootAreaProvider *provider = (KWRootAreaProvider *)kwdoc->frameLayout()->mainFrameSet()->rootAreaProvider();
448             provider->addDependentProvider(this, m_pages.count());
449         }
450     }
451 
452     KoShape *shape = rootArea->associatedShape();
453     if (!shape)
454         return;
455 
456     KWPage page = pageManager->page(shape);
457     Q_ASSERT(page.isValid());
458     KoTextShapeData *data = qobject_cast<KoTextShapeData*>(shape->userData());
459     Q_ASSERT(data);
460     bool isHeaderFooter = Words::isHeaderFooter(frameSet());
461 
462     debugWords << "pageNumber=" << page.pageNumber() << "frameSetType=" << Words::frameSetTypeName(frameSet()->textFrameSetType()) << "isNewRootArea=" << isNewRootArea << "rootArea=" << rootArea << "isDirty=" << rootArea->isDirty();
463 
464     QRectF updateRect = shape->outlineRect();
465 
466     QSizeF newSize = shape->size()
467                     - QSizeF(data->leftPadding() + data->rightPadding(),
468                              data->topPadding() + data->bottomPadding());
469 
470     KoBorder *border = shape->border();
471 
472     if (border) {
473         newSize -= QSizeF(border->borderWidth(KoBorder::LeftBorder) + border->borderWidth(KoBorder::RightBorder), border->borderWidth(KoBorder::TopBorder) + border->borderWidth(KoBorder::BottomBorder));
474     }
475 
476     if (isHeaderFooter
477         ||data->resizeMethod() == KoTextShapeData::AutoGrowWidthAndHeight
478         ||data->resizeMethod() == KoTextShapeData::AutoGrowHeight) {
479 
480         newSize.setHeight(rootArea->bottom() - rootArea->top());
481 
482         if (frameSet()->type() == Words::OtherFrameSet || frameSet()->textFrameSetType() == Words::OtherTextFrameSet) {
483             // adjust size to have at least the defined minimum height
484             Q_ASSERT(frameSet()->shapeCount() > 0);
485             KoShape *firstShape = frameSet()->shapes().first();
486             if (firstShape->minimumHeight() > newSize.height())
487                 newSize.setHeight(firstShape->minimumHeight());
488         }
489     }
490     if (data->resizeMethod() == KoTextShapeData::AutoGrowWidthAndHeight
491         ||data->resizeMethod() == KoTextShapeData::AutoGrowWidth) {
492         newSize.setWidth(rootArea->right() - rootArea->left());
493     }
494 
495     // To make sure footnotes always end up at the bottom of the main area we need to set this
496     if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
497         rootArea->setBottom(rootArea->top() + newSize.height());
498     }
499 
500     newSize += QSizeF(data->leftPadding() + data->rightPadding(),
501                       data->topPadding() + data->bottomPadding());
502     if (border) {
503         newSize += QSizeF(border->borderWidth(KoBorder::LeftBorder) + border->borderWidth(KoBorder::RightBorder), border->borderWidth(KoBorder::TopBorder) + border->borderWidth(KoBorder::BottomBorder));
504     }
505 
506     if (newSize != rootArea->associatedShape()->size()) {
507         rootArea->associatedShape()->setSize(newSize);
508 
509         // transfer the new size to the copy-shapes
510         foreach(KWCopyShape *cs, frameSet()->copyShapes()) {
511             cs->setSize(newSize);
512         }
513 
514         if (isHeaderFooter) {
515             // adjust the minimum shape height for headers and footer
516             Q_ASSERT(frameSet()->shapeCount() > 0);
517             KoShape *firstShape = frameSet()->shapes().first();
518             if (firstShape->minimumHeight() != newSize.height()) {
519                 firstShape->setMinimumHeight(newSize.height());
520 
521                 // transfer the new minimumFrameHeight to the copy-shapes too
522                 foreach(KWCopyShape *cs, frameSet()->copyShapes()) {
523                     cs->setMinimumHeight(newSize.height());
524                 }
525                 // cause the header/footer's height changed we have to relayout the whole page
526                 frameSet()->wordsDocument()->frameLayout()->layoutFramesOnPage(page.pageNumber());
527             }
528         }
529     }
530 
531 
532     updateRect |= rootArea->associatedShape()->outlineRect();
533     rootArea->associatedShape()->update(updateRect);
534 
535     if (frameSet()->textFrameSetType() == Words::MainTextFrameSet) {
536         handleDependentProviders(page.pageNumber());
537     }
538 
539 }
540