Commit 8ab38f4f authored by Pierre Ducroquet's avatar Pierre Ducroquet

Pass more data between layout and RootAreaProvider

This patch refactors a bit the KoTextLayoutRootAreaProvider.
It extends the provideNext call by adding a constraints structure.
This structure contains so far the masterPageName and the page
number of the requested new rootArea.
This way, the provider don't have to sneak in layout specific data.
It also adds a way for KoTextLayoutRootAreaProvider to help the
layout and fix inserting a new page (by adding lines to a paragraph
for instance) before a page break changing the page style.

Some more explanations regarding this bug :
- Page 1, style AAA
* page break, requesting style BBB
- Page 2, style BBB

If you add more data to the page 1, then dynamically the layout should
do :
- Page 1, style AAA
- Page 2, style AAA
* page break, requesting style BBB
- Page 3, style BBB

But the layout did recycle the page 2, leading to :
- Page 1, style AAA
- Page 2, style BBB
* page break, requesting style BBB
- Page 3, style BBB

Reloading the document leads to a correct layout.

This refactoring also simplifies the page style management since the
RootAreaProvider no longer has to mess with the document to guess the
proper page style, and removes some duplicated code in KoTextDocumentLayout.

Thanks to Camilla Boemann for all the reviews.

REVIEW: 120733
parent acb3e590
......@@ -46,9 +46,12 @@
#include <KoAnnotation.h>
#include <KoTextDocument.h>
#include <KoUnit.h>
#include <KoParagraphStyle.h>
#include <KoTableStyle.h>
#include <kdebug.h>
#include <QTextBlock>
#include <QTextTable>
#include <QTimer>
#include <QList>
......@@ -159,7 +162,6 @@ KoTextDocumentLayout::~KoTextDocumentLayout()
{
delete d->paintDevice;
delete d->layoutPosition;
qDeleteAll(d->rootAreaList);
qDeleteAll(d->freeObstructions);
qDeleteAll(d->anchoredObstructions);
qDeleteAll(d->textAnchors);
......@@ -347,7 +349,8 @@ void KoTextDocumentLayout::documentChanged(int position, int charsRemoved, int c
}
// Mark all selected root-areas as dirty so they are relayouted.
for(int i = startIndex; i <= endIndex; ++i) {
d->rootAreaList[i]->setDirty();
if (d->rootAreaList.size() > i && d->rootAreaList[i])
d->rootAreaList[i]->setDirty();
}
}
......@@ -697,6 +700,29 @@ void KoTextDocumentLayout::layout()
}
}
RootAreaConstraint constraintsForPosition (const QTextFrame::iterator &it) {
RootAreaConstraint constraints;
constraints.masterPageName = QString::null;
constraints.visiblePageNumber = -1;
QTextBlock firstBlock = it.currentBlock();
QTextTable *firstTable = qobject_cast<QTextTable*>(it.currentFrame());
if (firstBlock.isValid()) {
constraints.masterPageName = firstBlock.blockFormat().property(KoParagraphStyle::MasterPageName).toString();
bool ok;
int num = firstBlock.blockFormat().property(KoParagraphStyle::PageNumber).toInt(&ok);
if (ok)
constraints.visiblePageNumber = num;
}
if (firstTable) {
constraints.masterPageName = firstTable->frameFormat().property(KoTableStyle::MasterPageName).toString();
bool ok;
int num = firstTable->frameFormat().property(KoTableStyle::PageNumber).toInt(&ok);
if (ok)
constraints.visiblePageNumber = num;
}
return constraints;
}
bool KoTextDocumentLayout::doLayout()
{
delete d->layoutPosition;
......@@ -707,12 +733,28 @@ bool KoTextDocumentLayout::doLayout()
FrameIterator *transferedFootNoteCursor = 0;
KoInlineNote *transferedContinuedNote = 0;
int footNoteAutoCount = 0;
KoTextLayoutRootArea *rootArea = 0;
foreach (KoTextLayoutRootArea *rootArea, d->rootAreaList) {
d->rootAreaList.clear();
int currentAreaNumber = 0;
do {
if (d->restartLayout) {
return false; // Abort layouting to restart from the beginning.
}
// Build our request for our rootArea provider
RootAreaConstraint constraints = constraintsForPosition(d->layoutPosition->it);
// Request a new root-area. If NULL is returned then layouting is finished.
bool newRootArea = false;
rootArea = d->provider->provide(this, constraints, currentAreaNumber, &newRootArea);
if (!rootArea) {
// Out of space ? Nothing more to do
break;
}
d->rootAreaList.append(rootArea);
bool shouldLayout = false;
if (rootArea->top() != d->y) {
......@@ -724,6 +766,9 @@ bool KoTextDocumentLayout::doLayout()
else if (!rootArea->isStartingAt(d->layoutPosition)) {
shouldLayout = true;
}
else if (newRootArea) {
shouldLayout = true;
}
if (shouldLayout) {
QRectF rect = d->provider->suggestRect(rootArea);
......@@ -753,7 +798,6 @@ bool KoTextDocumentLayout::doLayout()
foreach (KoShapeAnchor *anchor, d->textAnchors) {
if (!d->foundAnchors.contains(anchor)) {
d->anchoredObstructions.remove(anchor->shape());
d->anchoringSoftBreak = qMin(d->anchoringSoftBreak, anchor->textLocation()->position());
}
}
......@@ -763,11 +807,10 @@ bool KoTextDocumentLayout::doLayout()
tmpPosition = new FrameIterator(d->layoutPosition);
finished = rootArea->layoutRoot(tmpPosition);
}
delete d->layoutPosition;
d->layoutPosition = tmpPosition;
d->provider->doPostLayout(rootArea, false);
d->provider->doPostLayout(rootArea, newRootArea);
updateProgress(rootArea->startTextFrameIterator());
if (finished && !rootArea->footNoteCursorToNext()) {
......@@ -780,10 +823,15 @@ bool KoTextDocumentLayout::doLayout()
return true; // Finished layouting
}
if (d->layoutPosition->it == document()->rootFrame()->end()) {
return true; // Finished layouting
}
if (!continuousLayout()) {
return false; // Let's take a break. We are not finished layouting yet.
}
} else {
// Drop following rootAreas
delete d->layoutPosition;
d->layoutPosition = new FrameIterator(rootArea->nextStartOfArea());
if (d->layoutPosition->it == document()->rootFrame()->end() && !rootArea->footNoteCursorToNext()) {
......@@ -802,75 +850,8 @@ bool KoTextDocumentLayout::doLayout()
d->y = rootArea->bottom() + qreal(50); // (post)Layout method(s) just set this
// 50 just to separate pages
}
while (transferedFootNoteCursor || d->layoutPosition->it != document()->rootFrame()->end()) {
if (d->restartLayout) {
return false; // Abort layouting to restart from the beginning.
}
// Request a new root-area. If NULL is returned then layouting is finished.
KoTextLayoutRootArea *rootArea = d->provider->provide(this);
if (rootArea) {
d->rootAreaList.append(rootArea);
QRectF rect = d->provider->suggestRect(rootArea);
d->freeObstructions = d->provider->relevantObstructions(rootArea);
rootArea->setReferenceRect(rect.left(), rect.right(), d->y + rect.top(), d->y + rect.bottom());
beginAnchorCollecting(rootArea);
// Layout all that can fit into that root area
FrameIterator *tmpPosition = 0;
do {
rootArea->setFootNoteCountInDoc(footNoteAutoCount);
rootArea->setFootNoteFromPrevious(transferedFootNoteCursor, transferedContinuedNote);
d->foundAnchors.clear();
delete tmpPosition;
tmpPosition = new FrameIterator(d->layoutPosition);
rootArea->layoutRoot(tmpPosition);
if (d->anAnchorIsPlaced) {
d->anAnchorIsPlaced = false;
} else {
++d->anchoringIndex;
}
} while (d->anchoringIndex < d->textAnchors.count());
foreach (KoShapeAnchor *anchor, d->textAnchors) {
if (!d->foundAnchors.contains(anchor)) {
d->anchoredObstructions.remove(anchor->shape());
d->anchoringSoftBreak = qMin(d->anchoringSoftBreak, anchor->textLocation()->position());
}
}
if (d->textAnchors.count() > 0) {
delete tmpPosition;
tmpPosition = new FrameIterator(d->layoutPosition);
rootArea->layoutRoot(tmpPosition);
}
delete d->layoutPosition;
d->layoutPosition = tmpPosition;
d->provider->doPostLayout(rootArea, true);
updateProgress(rootArea->startTextFrameIterator());
if (d->layoutPosition->it == document()->rootFrame()->end()) {
return true; // Finished layouting
}
if (!continuousLayout()) {
return false; // Let's take a break. We are not finished layouting yet.
}
} else {
break; // with no more space there is nothing else we can do
}
transferedFootNoteCursor = rootArea->footNoteCursorToNext();
transferedContinuedNote = rootArea->continuedNoteToNext();
footNoteAutoCount += rootArea->footNoteAutoCount();
d->y = rootArea->bottom() + qreal(50); // (post)Layout method(s) just set this
// 50 just to separate pages
}
currentAreaNumber++;
} while (transferedFootNoteCursor || d->layoutPosition->it != document()->rootFrame()->end());
return true; // Finished layouting
}
......
......@@ -23,12 +23,23 @@
#include "kotextlayout_export.h"
#include <QList>
#include <QString>
class KoTextLayoutRootArea;
class KoTextDocumentLayout;
class KoTextLayoutObstruction;
class QRectF;
/**
* Represents the contract that a root area requested by the layout system
* has to respect. For simple layout situations (like a single text shape),
* it's fine to ignore the contract since pages do not exist.
*/
struct RootAreaConstraint {
QString masterPageName;
int visiblePageNumber;
};
/**
* When laying out text we need an area where upon the text will be placed.
* A KoTextLayoutRootAreaProvider provides the layout process with such areas
......@@ -40,8 +51,15 @@ public:
explicit KoTextLayoutRootAreaProvider();
virtual ~KoTextLayoutRootAreaProvider();
/// Provides an new root area
virtual KoTextLayoutRootArea *provide(KoTextDocumentLayout *documentLayout) = 0;
/**
* Provides a new root area for the layout
*
* @param documentLayout the current document layouter
* @param constraints the rules the new area has to respect (page style, visible page number...)
* @param requestedPosition the position of the new area in the text flow
* @param isNewArea will contain a boolean to tell whether this is a new area or a recycled one
*/
virtual KoTextLayoutRootArea *provide(KoTextDocumentLayout *documentLayout, const RootAreaConstraint &constraints, int requestedPosition, bool *isNewArea) = 0;
/// Release all root areas that are after the "afterThis" root area
/// If afterThis == 0 all should be released
......@@ -62,7 +80,6 @@ public:
/// Return a list of obstructions intersecting root area
virtual QList<KoTextLayoutObstruction *> relevantObstructions(KoTextLayoutRootArea *rootArea) = 0;
};
#endif
......@@ -28,12 +28,14 @@ MockRootAreaProvider::MockRootAreaProvider()
{
}
KoTextLayoutRootArea *MockRootAreaProvider::provide(KoTextDocumentLayout *documentLayout)
KoTextLayoutRootArea *MockRootAreaProvider::provide(KoTextDocumentLayout *documentLayout, const RootAreaConstraint &, int, bool *isNewRootArea)
{
if(m_area == 0) {
m_area = new KoTextLayoutRootArea(documentLayout);
*isNewRootArea = true;
return m_area;
}
*isNewRootArea = false;
m_askedForMoreThenOneArea = true;
return 0;
}
......@@ -70,4 +72,3 @@ QList<KoTextLayoutObstruction *> MockRootAreaProvider::relevantObstructions(KoTe
QList<KoTextLayoutObstruction*> obstructions;
return obstructions;
}
......@@ -30,7 +30,7 @@ public:
MockRootAreaProvider();
/// reimplemented
virtual KoTextLayoutRootArea *provide(KoTextDocumentLayout *documentLayout);
virtual KoTextLayoutRootArea *provide(KoTextDocumentLayout *documentLayout, const RootAreaConstraint &constraints, int requestedPosition, bool *isNewArea);
virtual void releaseAllAfter(KoTextLayoutRootArea *afterThis);
virtual void doPostLayout(KoTextLayoutRootArea *rootArea, bool isNewRootArea);
virtual QRectF suggestRect(KoTextLayoutRootArea *rootArea);
......
......@@ -34,15 +34,17 @@ SimpleRootAreaProvider::SimpleRootAreaProvider(KoTextShapeData *data, TextShape
{
}
KoTextLayoutRootArea *SimpleRootAreaProvider::provide(KoTextDocumentLayout *documentLayout)
KoTextLayoutRootArea *SimpleRootAreaProvider::provide(KoTextDocumentLayout *documentLayout, const RootAreaConstraint &, int, bool *isNewRootArea)
{
if(m_area == 0) {
*isNewRootArea = true;
m_area = new KoTextLayoutRootArea(documentLayout);
m_area->setAssociatedShape(m_textShape);
m_textShapeData->setRootArea(m_area);
return m_area;
}
*isNewRootArea = false;
return 0;
}
......
......@@ -31,7 +31,7 @@ public:
SimpleRootAreaProvider(KoTextShapeData *data, TextShape *textshape);
/// reimplemented
virtual KoTextLayoutRootArea *provide(KoTextDocumentLayout *documentLayout);
virtual KoTextLayoutRootArea *provide(KoTextDocumentLayout *documentLayout, const RootAreaConstraint &constraints, int requestedPosition, bool *isNewRootArea);
virtual void releaseAllAfter(KoTextLayoutRootArea *afterThis);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment