Commit 06992f97 authored by Hanzes Matus's avatar Hanzes Matus

Moving anchor strategy into text shape

parent b6f1b1b3
......@@ -775,7 +775,6 @@ KoShape::TextRunAroundSide KoShape::textRunAroundSide() const
void KoShape::setTextRunAroundSide(TextRunAroundSide side, Through runThrought)
{
Q_D(KoShape);
d->textRunAroundSide = side;
if (side == RunThrough) {
if (runThrought == Background) {
......@@ -786,6 +785,14 @@ void KoShape::setTextRunAroundSide(TextRunAroundSide side, Through runThrought)
} else {
setRunThrough(0);
}
if ( d->textRunAroundSide == side) {
return;
}
d->textRunAroundSide = side;
notifyChanged();
d->shapeChanged(TextRunAroundChanged);
}
qreal KoShape::textRunAroundDistance() const
......
......@@ -123,6 +123,7 @@ public:
ShadowChanged, ///< the shapes shadow has changed
ParameterChanged, ///< the shapes parameter has changed (KoParameterShape only)
ContentChanged, ///< the content of the shape changed e.g. a new image inside a pixmap/text change inside a textshape
TextRunAroundChanged, ///< used after a setTextRunAroundSide()
ChildChanged ///< a child of a container was changed/removed. This is propagated to all parents
};
......
/* This file is part of the KDE project
* Copyright (C) 2007, 2009-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2010 Ko Gmbh <casper.boemann@kogmbh.com>
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -37,6 +38,7 @@
#include <QTextInlineObject>
#include <QFontMetricsF>
#include <QPainter>
#include <QRectF>
#include <KDebug>
#include "changetracker/KoChangeTracker.h"
......@@ -61,24 +63,15 @@ public:
verticalRel(KoTextAnchor::VLine),
horizontalPos(KoTextAnchor::HLeft),
horizontalRel(KoTextAnchor::HChar),
anchorType("char")
anchorType("char"),
pageRect(0,0,10,10),
pageContentRect(0,0,10,10),
pageNumber(0),
anchorStrategy(0)
{
Q_ASSERT(shape);
}
void relayout()
{
if (document && shape->parent()) {
KoTextShapeData *data = qobject_cast<KoTextShapeData*>(shape->parent()->userData());
Q_ASSERT(data);
data->foul();
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(document->documentLayout());
if (lay)
lay->interruptLayout();
data->fireResizeEvent();
}
}
/// as multiple shapes can hold 1 text flow; the anchored shape can be moved between containers and thus models
void setContainer(KoShapeContainer *container)
{
......@@ -129,6 +122,10 @@ public:
KoTextAnchor::HorizontalRel horizontalRel;
QString anchorType;
bool fakeAsChar;
QRectF pageRect;
QRectF pageContentRect;
int pageNumber;
KoAnchorStrategy *anchorStrategy;
};
KoTextAnchor::KoTextAnchor(KoShape *shape)
......@@ -143,6 +140,9 @@ KoTextAnchor::~KoTextAnchor()
Q_D(KoTextAnchor);
if (d->model)
d->model->removeAnchor(this);
if (d->anchorStrategy != 0) {
delete d->anchorStrategy;
}
}
void KoTextAnchor::fakeAsChar()
......@@ -208,7 +208,6 @@ KoTextAnchor::VerticalRel KoTextAnchor::verticalRel()
void KoTextAnchor::updatePosition(const QTextDocument *document, QTextInlineObject object, int posInDocument, const QTextCharFormat &format)
{
Q_UNUSED(object);
Q_UNUSED(format);
Q_D(KoTextAnchor);
d->document = document;
d->position = posInDocument;
......@@ -352,10 +351,7 @@ const QPointF &KoTextAnchor::offset() const
void KoTextAnchor::setOffset(const QPointF &offset)
{
Q_D(KoTextAnchor);
if (d->distance == offset)
return;
d->distance = offset;
d->relayout();
}
void KoTextAnchor::saveOdf(KoShapeSavingContext &context)
......@@ -520,9 +516,6 @@ bool KoTextAnchor::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &c
return false;
d->anchorType = shape()->additionalAttribute("text:anchor-type");
if (d->anchorType == "as-char")
d->behaveAsCharacter = true;
// load settings from graphic style
KoStyleStack &styleStack = context.odfLoadingContext().styleStack();
styleStack.save();
......@@ -680,6 +673,13 @@ bool KoTextAnchor::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &c
d->distance = QPointF();
}
if (d->anchorType == "as-char") {
d->behaveAsCharacter = true;
d->horizontalRel = HChar;
d->horizontalPos = HLeft;
}
return true;
}
......@@ -687,6 +687,10 @@ void KoTextAnchor::setBehavesAsCharacter(bool aschar)
{
Q_D(KoTextAnchor);
d->behaveAsCharacter = aschar;
if (aschar == true) {
d->horizontalRel = HChar;
d->horizontalPos = HLeft;
}
}
bool KoTextAnchor::behavesAsCharacter() const
......@@ -702,3 +706,56 @@ void KoTextAnchor::detachFromModel()
Q_D(KoTextAnchor);
d->model = 0;
}
QRectF KoTextAnchor::pageRect()
{
Q_D(KoTextAnchor);
return d->pageRect;
}
void KoTextAnchor::setPageRect(QRectF &pageRect)
{
Q_D(KoTextAnchor);
d->pageRect = pageRect;
}
QRectF KoTextAnchor::pageContentRect()
{
Q_D(KoTextAnchor);
return d->pageContentRect;
}
void KoTextAnchor::setPageContentRect(QRectF &pageContentRect)
{
Q_D(KoTextAnchor);
d->pageContentRect = pageContentRect;
}
int KoTextAnchor::pageNumber()
{
Q_D(KoTextAnchor);
return d->pageNumber;
}
void KoTextAnchor::setPageNumber(int pageNumber)
{
Q_D(KoTextAnchor);
d->pageNumber = pageNumber;
}
KoAnchorStrategy * KoTextAnchor::anchorStrategy()
{
Q_D(KoTextAnchor);
return d->anchorStrategy;
}
void KoTextAnchor::setAnchorStrategy(KoAnchorStrategy * anchorStrategy)
{
Q_D(KoTextAnchor);
if (anchorStrategy != d->anchorStrategy) {
if (d->anchorStrategy != 0) {
delete d->anchorStrategy;
}
d->anchorStrategy = anchorStrategy;
}
}
/* This file is part of the KDE project
* Copyright (C) 2007, 2009 Thomas Zander <zander@kde.org>
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -20,6 +21,7 @@
#define KOTEXTANCHOR_H
#include "KoInlineObject.h"
#include "KoTextDocumentLayout.h"
#include "kotext_export.h"
......@@ -30,6 +32,25 @@ class KoTextAnchorPrivate;
class KoXmlElement;
class KoShapeLoadingContext;
/**
* This class is an interface that positions the shape linked to text anchor
*/
class KOTEXT_EXPORT KoAnchorStrategy{
public:
KoAnchorStrategy(){};
virtual ~KoAnchorStrategy(){};
virtual bool positionShape(KoTextDocumentLayout::LayoutState *state) = 0;
virtual bool isPositioned() = 0;
virtual void reset() = 0;
virtual bool isRelayoutNeeded() = 0;
virtual QPointF relayoutPosition() = 0;
};
/**
* This class is the object that is positioned in the text to be an anchor for a shape.
* An anchor is the connection between the text-shape and the so called 'anchored-shape', where the
......@@ -104,6 +125,7 @@ public:
VParagraphContent,
VText
};
/**
* Constructor for an in-place anchor.
* @param shape the anchored shape that this anchor links to.
......@@ -185,6 +207,31 @@ public:
/// \internal make sure that the anchor has no KoTextShapeContainerModel references anymore.
void detachFromModel();
// get page rectangle coordinates to which this text anchor is anchored (needed for HPage)
QRectF pageRect();
// set page rectangle coordinates to which this text anchor is anchored (needed for HPage)
void setPageRect(QRectF &pageRect);
// get content rectangle coordinates to which this text anchor is anchored (needed for HPageContent)
QRectF pageContentRect();
// set content rectangle coordinates to which this text anchor is anchored (needed for HPageContent)
void setPageContentRect(QRectF &marginRect);
// get number of page to which this text anchor is anchored (needed for HOutside,HInside,HFromInside)
int pageNumber();
// set number of page to which this text anchor is anchored (needed for HOutside,HInside,HFromInside)
void setPageNumber(int pageNumber);
// get anchor strategy which is used to position shape linked to text anchor
KoAnchorStrategy * anchorStrategy();
// set anchor strategy which is used to position shape linked to text anchor
void setAnchorStrategy(KoAnchorStrategy * anchorStrategy);
private:
Q_DECLARE_PRIVATE(KoTextAnchor)
};
......
......@@ -111,6 +111,18 @@ public:
void fitLineForRunAround(bool resetHorizontalPosition) {
Q_UNUSED(resetHorizontalPosition);
}
virtual void insertInlineObject(KoTextAnchor * textAnchor) {
Q_UNUSED(textAnchor);
}
virtual void resetInlineObject(int resetPosition) {
Q_UNUSED(resetPosition);
}
virtual void removeInlineObject(KoTextAnchor * textAnchor) {
Q_UNUSED(textAnchor);
}
};
class KoTextDocumentLayout::Private
......@@ -433,6 +445,7 @@ void KoTextDocumentLayout::documentChanged(int position, int charsRemoved, int c
// found our (first) shape to re-layout
data->foul();
m_state->reset();
resetInlineObject(position);
scheduleLayout();
return;
}
......@@ -565,6 +578,11 @@ QList<KoShape*> KoTextDocumentLayout::shapes() const
return d->shapes;
}
void KoTextDocumentLayout::resetInlineObject(int resetPosition)
{
m_state->resetInlineObject(resetPosition);
}
KoShape* KoTextDocumentLayout::shapeForPosition(int position) const
{
foreach(KoShape *shape, shapes()) {
......
......@@ -38,6 +38,7 @@ class QTextLayout;
class KoInlineTextObjectManager;
class KoViewConverter;
class KoImageCollection;
class KoTextAnchor;
/**
* KWords text layouter that allows text to flow in multiple frames and around
......@@ -138,6 +139,9 @@ public:
/// return the list of shapes that will be used to run all the text into.
virtual QList<KoShape*> shapes() const;
// reset all inline object which document position is bigger or equal to resetPosition
virtual void resetInlineObject(int resetPosition);
/**
* This inner class is an interface that allows the KoTextDocumentLayout to do rough layout
* while the LayoutState implementation can do all the boring details.
......@@ -215,6 +219,12 @@ public:
virtual QTextLine createLine() = 0;
/// Fiddle with the width of the current textline until it fits
virtual void fitLineForRunAround(bool resetHorizontalPosition) = 0;
// add inline object
virtual void insertInlineObject(KoTextAnchor * textAnchor) = 0;
// reset all inline object which document position is bigger or equal to resetPosition
virtual void resetInlineObject(int resetPosition) = 0;
// remove inline object
virtual void removeInlineObject(KoTextAnchor * textAnchor) = 0;
/// the index in the list of shapes (or frameset) of the shape we are currently layouting.
int shapeNumber;
/// the shape that is currently being laid out
......
......@@ -142,17 +142,11 @@ void KoTextShapeContainerModel::containerChanged(KoShapeContainer *container, Ko
void KoTextShapeContainerModel::childChanged(KoShape *child, KoShape::ChangeType type)
{
if (type == KoShape::RotationChanged || type == KoShape::ScaleChanged ||
type == KoShape::ShearChanged || type == KoShape::SizeChanged) {
if (((type == KoShape::RotationChanged || type == KoShape::ScaleChanged || type == KoShape::ShearChanged ||
type == KoShape::SizeChanged) && child->textRunAroundSide() != KoShape::RunThrough) ||
type == KoShape::TextRunAroundChanged) {
KoTextShapeData *data = qobject_cast<KoTextShapeData*>(child->parent()->userData());
Q_ASSERT(data);
data->foul();
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(data->document()->documentLayout());
if (lay)
lay->interruptLayout();
data->fireResizeEvent();
relayoutInlineObject(child);
}
KoShapeContainerModel::childChanged( child, type );
}
......@@ -195,6 +189,7 @@ void KoTextShapeContainerModel::proposeMove(KoShape *child, QPointF &move)
QTextLine tl = layout->lineForTextPosition(anchorPosInParag);
relation.anchor->setOffset(QPointF(newPosition.x() - tl.cursorToX(anchorPosInParag)
+ tl.x(), 0));
relayoutInlineObject(child);
// the rest of the code uses the shape baseline, at this time the bottom. So adjust
newPosition.setY(newPosition.y() + child->size().height());
......@@ -209,11 +204,13 @@ void KoTextShapeContainerModel::proposeMove(KoShape *child, QPointF &move)
QTextLine tl = layout->lineForTextPosition(anchorPosInParag);
qreal y = tl.y() - data->documentOffset() - newPosition.y() + child->size().height();
relation.anchor->setOffset(QPointF(relation.anchor->offset().x(), -y));
relayoutInlineObject(child);
}
} else {
//TODO pavolk: handle position type change: absolute to realtive, etc ..
child->setPosition(newPosition);
relation.anchor->setOffset(relation.anchor->offset() + move);
relayoutInlineObject(child);
}
move.setX(0); // let the text layout move it.
......@@ -224,3 +221,27 @@ bool KoTextShapeContainerModel::isChildLocked(const KoShape *child) const
{
return child->isGeometryProtected();
}
void KoTextShapeContainerModel::relayoutInlineObject(KoShape *child)
{
if (child == 0) {
return;
}
KoTextShapeData *data = qobject_cast<KoTextShapeData*>(child->parent()->userData());
Q_ASSERT(data);
data->foul();
KoTextDocumentLayout *lay = qobject_cast<KoTextDocumentLayout*>(data->document()->documentLayout());
if (lay) {
lay->interruptLayout();
if (d->children.contains(child)) {
Relation relation = d->children.value(child);
if (relation.anchor) {
lay->resetInlineObject(relation.anchor->positionInDocument());
}
}
}
data->fireResizeEvent();
}
......@@ -69,6 +69,9 @@ public:
void removeAnchor(KoTextAnchor *anchor);
private:
// reset child position and relayout shape to which this shape is linked
void relayoutInlineObject(KoShape *child);
class Private;
Private * const d;
};
......
......@@ -35,6 +35,8 @@ SET ( textshape_SRCS
TableLayout.cpp
TableLayoutData.cpp
TextAnchorStrategy.cpp
dialogs/StylesWidget.cpp
dialogs/SimpleCharacterWidget.cpp
......
......@@ -30,6 +30,7 @@
#include "ListItemsHelper.h"
#include "TextShape.h"
#include "ToCGenerator.h"
#include "TextAnchorStrategy.h"
#include <KoTextDocumentLayout.h>
#include <KoTextShapeData.h>
......@@ -91,12 +92,18 @@ Layout::Layout(KoTextDocumentLayout *parent)
m_allTimeMinimumLeft(0),
m_allTimeMaximumRight(0),
m_maxLineHeight(0),
m_scaleFactor(1.0)
m_scaleFactor(1.0),
m_textAnchorIndex(0)
{
m_frameStack.reserve(5); // avoid reallocs
setTabSpacing(MM_TO_POINT(23)); // use same default as open office
}
Layout::~Layout()
{
unregisterAllRunAroundShapes();
}
bool Layout::start()
{
m_styleManager = KoTextDocument(m_parent->document()).styleManager();
......@@ -115,13 +122,13 @@ void Layout::end()
if (layout)
layout->endLayout();
layout = 0;
unregisterAllRunAroundShapes();
}
QTextLine Layout::createLine()
{
m_textLine.createLine(this);
m_textLine.setOutlines(m_outlines);
refreshCurrentPageOutlines();
m_textLine.setOutlines(m_currentLineOutlines);
return m_textLine.line;
}
......@@ -411,6 +418,19 @@ bool Layout::addLine()
}
}
// position inline objects
bool possibleRelayoutNeeded = positionInlineObjects();
if (possibleRelayoutNeeded) {
if (m_textAnchors[m_textAnchorIndex - 1]->anchorStrategy()->isRelayoutNeeded()) {
if (moveLayoutPosition(m_textAnchors[m_textAnchorIndex - 1]) == true) {
return false;
}
}
}
// call update to repaint the new line
QRectF repaintRect = line.rect();
if (layout->lineCount() > 1) { // cover big linespacing.
......@@ -2296,18 +2316,130 @@ void Layout::registerRunAroundShape(KoShape *s)
matrix = matrix * shape->absoluteTransformation(0).inverted();
matrix.translate(0, documentOffsetInShape());
Outline *outline = new Outline(s, matrix);
m_outlines.append(outline);
m_outlines.insert(s,outline);
}
void Layout::updateRunAroundShape(KoShape *s)
{
foreach (Outline *outline, m_outlines) {
if (outline->shape() == s) {
QTransform matrix = s->absoluteTransformation(0);
matrix = matrix * shape->absoluteTransformation(0).inverted();
matrix.translate(0, documentOffsetInShape());
outline->changeMatrix(matrix);
m_textLine.updateOutline(outline);
if (m_outlines.contains(s)) {
Outline *outline = m_outlines.value(s);
QTransform matrix = s->absoluteTransformation(0);
matrix = matrix * shape->absoluteTransformation(0).inverted();
matrix.translate(0, documentOffsetInShape());
outline->changeMatrix(matrix);
m_textLine.updateOutline(outline);
return;
}
// if no outline was found then create one
registerRunAroundShape(s);
}
void Layout::unregisterAllRunAroundShapes()
{
qDeleteAll(m_outlines);
m_outlines.clear();
}
void Layout::insertInlineObject(KoTextAnchor * textAnchor)
{
if (textAnchor != 0) {
textAnchor->setAnchorStrategy(new TextAnchorStrategy(textAnchor));
m_textAnchors.append(textAnchor);
}
}
void Layout::resetInlineObject(int resetPosition)
{
bool resetAllOthers = false;
for (int index = 0; index < m_textAnchors.size(); index++) {
if (resetAllOthers == false) {
if (m_textAnchors[index]->positionInDocument() >= resetPosition) {
if (m_textAnchors[index]->anchorStrategy()->isPositioned()) {
m_textAnchorIndex = index;
resetAllOthers = true;
}
else {
break;
}
}
}
if (resetAllOthers == true) {
KoTextAnchor * textAnchorToReset = m_textAnchors[index];
textAnchorToReset->anchorStrategy()->reset();
// delete outline
if (m_outlines.contains(textAnchorToReset->shape())) {
Outline *outline = m_outlines.value(textAnchorToReset->shape());
m_outlines.remove(textAnchorToReset->shape());
m_textLine.updateOutline(outline);
refreshCurrentPageOutlines();
delete outline;
}
}
}
}
void Layout::removeInlineObject(KoTextAnchor * textAnchor)
{
Q_UNUSED(textAnchor);
}
bool Layout::positionInlineObjects()
{
while (m_textAnchorIndex < m_textAnchors.size()) {
KoTextAnchor *textAnchor = m_textAnchors[m_textAnchorIndex];
if (textAnchor->anchorStrategy()->positionShape(this) == false) {
break;
}
// move the index to next not positioned shape
m_textAnchorIndex++;
// create outline if the shape is positioned inside text
if (textAnchor->shape()->textRunAroundSide() != KoShape::RunThrough) {
updateRunAroundShape(textAnchor->shape());
return true;
}
}
return false;
}
bool Layout::moveLayoutPosition(KoTextAnchor *textAnchor)
{
int oldPosition = y();
QPointF relayoutPos = textAnchor->anchorStrategy()->relayoutPosition();
qreal recalcFrom = relayoutPos.y() + m_data->documentOffset();
// move position layout over the textAnchor linked shape
do {
if (recalcFrom >= y()) {
break;
}
} while (previousParag());
if (oldPosition != y()) {
return true;
}
return false;
}
void Layout::refreshCurrentPageOutlines()
{
m_currentLineOutlines.clear();
TextShape *textShape = dynamic_cast<TextShape*>(shape);
if (textShape == 0) {
return;
}
// add current page children outlines to m_currentLineOutlines
foreach(KoShape *childShape, textShape->shapes()) {
if (m_outlines.contains(childShape)) {
m_currentLineOutlines.append(m_outlines.value(childShape));
}
}
}
......@@ -52,6 +52,8 @@ class Layout : public KoTextDocumentLayout::LayoutState
{