Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit ffd625c6 authored by C. Boemann's avatar C. Boemann

Change KoTextAnchor into three new classes

 KoShapeAnchor - which in theory should be moved to flake later
 KoAnchorInlineObject - which is the kotext part responsible for being an inline (asChar) object
 KoAnchorTextRange - which is the kotext part resposible for toChar and toParagraph anchoring

Now the user no longer have to contend with invisible characters acting as anchors. These kinds
of anchors are now placed at a cursor position instead (using KoTextRange)

This also has the benefit that we can support toParagraph anchoring a bit better, as it's no
longer possible to move the anchor character away from the paragraphs first position.

REVIEW: 108459
parent 05e417b0
......@@ -41,7 +41,9 @@ set(kotext_LIB_SRCS
KoSection.cpp
KoTextLocator.cpp
KoTextReference.cpp
KoTextAnchor.cpp
KoShapeAnchor.cpp
KoAnchorInlineObject.cpp
KoAnchorTextRange.cpp
KoTextShapeSavingContext.cpp
KoAnnotation.cpp
KoAnnotationManager.cpp
......@@ -167,7 +169,9 @@ install(
KoBookmarkManager.h
KoAnnotationManager.h
KoInlineTextObjectManager.h
KoTextAnchor.h
KoAnchorInlineObject.h
KoAnchorTextRange.h
KoShapeAnchor.h
KoTextBlockBorderData.h
KoTextBlockData.h
KoTextDocument.h
......
/* This file is part of the KDE project
* Copyright (C) 2007, 2009-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2010 Ko Gmbh <cbo@kogmbh.com>
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoAnchorInlineObject.h"
#include "KoInlineObject_p.h"
#include "KoStyleStack.h"
#include "KoOdfLoadingContext.h"
#include "KoShapeAnchor.h"
#include <KoShapeContainer.h>
#include <KoXmlWriter.h>
#include <KoXmlReader.h>
#include <KoXmlNS.h>
#include <KoShapeSavingContext.h>
#include <KoShapeLoadingContext.h>
#include <KoUnit.h>
#include <QTextInlineObject>
#include <QFontMetricsF>
#include <QPainter>
#include <QRectF>
#include <KDebug>
#include "changetracker/KoChangeTracker.h"
#include "changetracker/KoChangeTrackerElement.h"
#include "styles/KoCharacterStyle.h"
#include "KoTextDocument.h"
#include <KoGenChanges.h>
// #define DEBUG_PAINTING
class KoAnchorInlineObjectPrivate : public KoInlineObjectPrivate
{
public:
KoAnchorInlineObjectPrivate(KoShapeAnchor *p)
: parent(p)
, document(0)
, position(-1)
, inlineObjectAscent(0)
, inlineObjectDescent(0)
{
}
KoShapeAnchor *parent;
const QTextDocument *document;
int position;
QTextCharFormat format;
qreal inlineObjectAscent;
qreal inlineObjectDescent;
};
KoAnchorInlineObject::KoAnchorInlineObject(KoShapeAnchor *parent)
: KoInlineObject(*(new KoAnchorInlineObjectPrivate(parent)), false)
{
Q_ASSERT(parent);
parent->setTextLocation(this);
}
KoAnchorInlineObject::~KoAnchorInlineObject()
{
}
KoShapeAnchor *KoAnchorInlineObject::anchor() const
{
Q_D(const KoAnchorInlineObject);
return d->parent;
}
void KoAnchorInlineObject::updatePosition(const QTextDocument *document, int posInDocument, const QTextCharFormat &format)
{
Q_D(KoAnchorInlineObject);
d->document = document;
d->position = posInDocument;
d->format = format;
if (d->parent->placementStrategy() != 0) {
d->parent->placementStrategy()->updateContainerModel();
}
}
void KoAnchorInlineObject::resize(const QTextDocument *document, QTextInlineObject object, int posInDocument, const QTextCharFormat &format, QPaintDevice *pd)
{
Q_UNUSED(document);
Q_UNUSED(posInDocument);
Q_D(KoAnchorInlineObject);
if (!d->parent->shape()->isVisible()) {
// Per default the shape this anchor presents is hidden and we only make it visible once an explicit resize-request
// was made. This prevents shapes that are anchored at e.g. hidden textboxes to not become visible as long as they
// are not asked to resize.
d->parent->shape()->setVisible(true);
}
// important detail; top of anchored shape is at the baseline.
QFontMetricsF fm(format.font(), pd);
if (d->parent->anchorType() == KoShapeAnchor::AnchorAsCharacter) {
QPointF offset = d->parent->offset();
offset.setX(0);
d->parent->setOffset(offset);
object.setWidth(d->parent->shape()->size().width());
if (d->parent->verticalRel() == KoShapeAnchor::VBaseline) {
// baseline implies special meaning of the position attribute:
switch (d->parent->verticalPos()) {
case KoShapeAnchor::VFromTop:
object.setAscent(qMax((qreal) 0, -offset.y()));
object.setDescent(qMax((qreal) 0, d->parent->shape()->size().height() + offset.y()));
break;
case KoShapeAnchor::VTop:
object.setAscent(d->parent->shape()->size().height());
object.setDescent(0);
break;
case KoShapeAnchor::VMiddle:
object.setAscent(d->parent->shape()->size().height()/2);
object.setDescent(d->parent->shape()->size().height()/2);
break;
case KoShapeAnchor::VBottom:
object.setAscent(0);
object.setDescent(d->parent->shape()->size().height());
break;
default:
break;
}
} else {
qreal boundTop = fm.ascent();
switch (d->parent->verticalPos()) {
case KoShapeAnchor::VFromTop:
object.setAscent(qMax((qreal) 0, -offset.y()));
object.setDescent(qMax((qreal) 0, d->parent->shape()->size().height() + offset.y()));
break;
case KoShapeAnchor::VTop:
object.setAscent(boundTop);
object.setDescent(qMax((qreal) 0, d->parent->shape()->size().height() - boundTop));
break;
case KoShapeAnchor::VMiddle:
object.setAscent(d->parent->shape()->size().height()/2);
object.setDescent(d->parent->shape()->size().height()/2);
break;
case KoShapeAnchor::VBottom:
object.setAscent(0);
object.setDescent(d->parent->shape()->size().height());
break;
default:
break;
}
}
d->inlineObjectAscent = object.ascent();
d->inlineObjectDescent = object.descent();
} else {
object.setWidth(0);
object.setAscent(0);
object.setDescent(0);
}
}
void KoAnchorInlineObject::paint(QPainter &, QPaintDevice *, const QTextDocument *, const QRectF &, QTextInlineObject , int , const QTextCharFormat &)
{
}
int KoAnchorInlineObject::position() const
{
Q_D(const KoAnchorInlineObject);
return d->position;
}
const QTextDocument *KoAnchorInlineObject::document() const
{
Q_D(const KoAnchorInlineObject);
return d->document;
}
qreal KoAnchorInlineObject::inlineObjectAscent() const
{
Q_D(const KoAnchorInlineObject);
return d->inlineObjectAscent;
}
qreal KoAnchorInlineObject::inlineObjectDescent() const
{
Q_D(const KoAnchorInlineObject);
return d->inlineObjectDescent;
}
bool KoAnchorInlineObject::loadOdf(const KoXmlElement &, KoShapeLoadingContext &)
{
// do nothing
return true;
}
void KoAnchorInlineObject::saveOdf(KoShapeSavingContext &context)
{
Q_D(KoAnchorInlineObject);
d->parent->saveOdf(context);
}
/* 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>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOANCHORINLINEOBJECT_H
#define KOANCHORINLINEOBJECT_H
#include "KoInlineObject.h"
#include "KoShapeAnchor.h"
#include "kotext_export.h"
class KoShape;
class KoAnchorInlineObjectPrivate;
class KoXmlElement;
class KoShapeLoadingContext;
class KoShapeContainer;
/**
* This class connects KoShapeAnchor to an inline character in the text document.
*
* This class is used when the shape anchor is of type: as-char
*
* It has to be registered to the inlineobjectmanager and thus forms the connection between the text
* and the KoShapeAnchor and by extension the so called 'anchored-shape' (any kind of shape)
*
* The KoAnchorInlineObject is placed as a character in text. As such it will move and be
* editable like any other character, including deletion.
*
* Since this is a real character it will be positioned by the textlayout engine and anything that
* will change the position of the text will thus also change the KoAnchorInlineObject character.
*
* The anchored-shape can be repositioned on the canvas if the text is relayouted (for example after
* editing the text. This is dependent on how the text layout is implemented.
*
* Steps to use a KoAnchorInlineObject are; <ol>
* <li> Create KoShapeAnchor *anchor = new KoShapeAnchor(shape);
* <li> Use anchor->loadOdf() to load additional attributes like the "text:anchor-type"
* <li> if type is as-char create KoAnchorInlineObject *anchorObj = new KoAnchorInlineObject(anchor);
*/
class KOTEXT_EXPORT KoAnchorInlineObject : public KoInlineObject, public KoShapeAnchor::TextLocation
{
Q_OBJECT
public:
/**
* Constructor for an as-char anchor.
* @param parent the shapeanchor.
*/
KoAnchorInlineObject(KoShapeAnchor *parent);
virtual ~KoAnchorInlineObject();
/// returns the parent anchor
KoShapeAnchor *anchor() const;
/// returns the cursor position in the document where this anchor is positioned.
int position() const;
/// returns the document that this anchor is associated with.
const QTextDocument *document() const;
/// reimplemented from KoInlineObject
virtual void updatePosition(const QTextDocument *document,
int posInDocument, const QTextCharFormat &format);
/// reimplemented from KoInlineObject
virtual void resize(const QTextDocument *document, QTextInlineObject object,
int posInDocument, const QTextCharFormat &format, QPaintDevice *pd);
/// reimplemented from KoInlineObject
virtual void paint(QPainter &painter, QPaintDevice *pd, const QTextDocument *document,
const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextCharFormat &format);
qreal inlineObjectAscent() const;
qreal inlineObjectDescent() const;
/// reimplemented from KoInlineObject - should not do anything
bool loadOdf(const KoXmlElement &, KoShapeLoadingContext &);
/// reimplemented from KoInlineObject
void saveOdf(KoShapeSavingContext &context);
private:
Q_DECLARE_PRIVATE(KoAnchorInlineObject)
};
#endif
/* This file is part of the KDE project
* Copyright (C) 2007, 2009-2010 Thomas Zander <zander@kde.org>
* Copyright (C) 2010 Ko Gmbh <cbo@kogmbh.com>
* Copyright (C) 2011 Matus Hanzes <matus.hanzes@ixonos.com>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KoAnchorTextRange.h"
#include "KoStyleStack.h"
#include "KoOdfLoadingContext.h"
#include "KoShapeAnchor.h"
#include <KoShapeContainer.h>
#include <KoXmlWriter.h>
#include <KoXmlReader.h>
#include <KoXmlNS.h>
#include <KoShapeSavingContext.h>
#include <KoShapeLoadingContext.h>
#include <KoUnit.h>
#include <KDebug>
#include "KoTextDocument.h"
class KoAnchorTextRangePrivate
{
public:
KoAnchorTextRangePrivate(KoShapeAnchor *p)
: parent(p)
{
}
KoShapeAnchor *parent;
};
KoAnchorTextRange::KoAnchorTextRange(KoShapeAnchor *parent, const QTextCursor &cursor)
: KoTextRange(cursor)
, d_ptr(new KoAnchorTextRangePrivate(parent))
{
Q_ASSERT(parent);
parent->setTextLocation(this);
}
KoAnchorTextRange::~KoAnchorTextRange()
{
delete d_ptr;
}
KoShapeAnchor *KoAnchorTextRange::anchor() const
{
Q_D(const KoAnchorTextRange);
return d->parent;
}
const QTextDocument *KoAnchorTextRange::document() const
{
return KoTextRange::document();
}
int KoAnchorTextRange::position() const
{
return rangeStart();
}
void KoAnchorTextRange::updateContainerModel()
{
Q_D(KoAnchorTextRange);
if (!d->parent->shape()->isVisible()) {
// Per default the shape this anchor presents is hidden and we only make it visible once an
// explicit placement is made. This prevents shapes that are anchored at e.g. hidden
// textboxes to not become visible.
d->parent->shape()->setVisible(true);
}
if (d->parent->placementStrategy() != 0) {
d->parent->placementStrategy()->updateContainerModel();
}
}
bool KoAnchorTextRange::loadOdf(const KoXmlElement &, KoShapeLoadingContext &)
{
return true;
}
void KoAnchorTextRange::saveOdf(KoShapeSavingContext &context, int position, KoTextRange::TagType tagType) const
{
Q_UNUSED(position);
Q_UNUSED(tagType);
Q_D(const KoAnchorTextRange);
if (tagType == KoTextRange::StartTag) {
d->parent->saveOdf(context);
}
}
/* 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>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOANCHORTEXTRANGE_H
#define KOANCHORTEXTRANGE_H
#include "KoTextRange.h"
#include "KoShapeAnchor.h"
#include "kotext_export.h"
#include <QPointF>
class KoShape;
class KoAnchorTextRangePrivate;
class KoXmlElement;
class KoShapeLoadingContext;
class KoShapeContainer;
class QTextCursor;
/**
* This class connects KoShapeAnchor to a position in the text document.
*
* This class is used when the shape anchor is of type: char or paragraph
*
* It has to be registered to the textrange manager and thus forms the connection between the text
* and the KoShapeAnchor and by extension the so called 'anchored-shape' (any kind of shape)
*
* The KoAnchorTextRange is placed at a position in text. As with all KoTextRange it will change
* it's position in the text when the user edits text before it. The user is also able to delete it if
* deleting the text where it is positioned.
*
* The anchored-shape can be repositioned on the canvas if the text is relayouted (for example after
* editing the text. This is dependent on how the text layout is implemented.
*
* Steps to use a KoAnchorTextRange are; <ol>
* <li> Create KoShapeAnchor *anchor = new KoShapeAnchor(shape);
* <li> Use anchor->loadOdf() to load additional attributes like the "text:anchor-type"
* <li> if type is char or paragraph create KoAnchorTextRange *anchorRange = new KoAnchorTextRange(anchor);
*/
class KOTEXT_EXPORT KoAnchorTextRange : public KoTextRange, public KoShapeAnchor::TextLocation
{
Q_OBJECT
public:
/**
* Constructor for a char or paragraph anchor.
* @param parent the shapeanchor.
*/
KoAnchorTextRange(KoShapeAnchor *parent, const QTextCursor &cursor);
virtual ~KoAnchorTextRange();
/// returns the parent anchor
KoShapeAnchor *anchor() const;
/// reimplemented from KoShapeAnchor::TextLocation
const QTextDocument *document() const;
/// reimplemented from KoShapeAnchor::TextLocation
int position() const;
void updateContainerModel();
/// reimplemented from KoTextRange - should not do anything
bool loadOdf(const KoXmlElement &, KoShapeLoadingContext &);
/// reimplemented from KoTextRange
void saveOdf(KoShapeSavingContext &context, int position, KoTextRange::TagType tagType) const;
private:
KoAnchorTextRangePrivate *d_ptr;
Q_DECLARE_PRIVATE(KoAnchorTextRange)
};
#endif
/* 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>
* Copyright (C) 2013 C. Boemann <cbo@boemann.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
......@@ -17,33 +18,21 @@
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef KOTEXTANCHOR_H
#define KOTEXTANCHOR_H
#include "KoInlineObject.h"
#ifndef KOSHAPEANCHOR_H
#define KOSHAPEANCHOR_H
#include "kotext_export.h"
#include <QPointF>
class KoShape;
class KoTextAnchorPrivate;
class KoXmlElement;
class KoShapeLoadingContext;
class KoShapeSavingContext;
class KoShapeContainer;
class KoShapeAnchorPrivate;
class QTextDocument;
/**
* This class is an interface that positions the shape linked to text anchor
*/
class KOTEXT_EXPORT KoAnchorStrategy {
public:
KoAnchorStrategy(){};
virtual ~KoAnchorStrategy(){};
virtual void detachFromModel() = 0;
virtual void updatePosition(KoShape *shape, const QTextDocument *document, int position) = 0;
};
/**
* This class is the object that explains how a shape is anchored to something.
......@@ -51,37 +40,55 @@ public:
* The anchored shape will be positioned (in supporting applications) based on the properties
* defined in this class.
*
* This class can be used in two different ways:
* This class can be used in three different ways:
* -page anchor
* -as-char, char, paragraph anchor
* -as-char
* -char, paragraph anchor
*
* If it's a page anchor it just provide the info about how the shape relates to a page number.
*
* For the other types of anchoring it has to be inlined in the QTextDocument in which case the
* anchor is the connection between the text and the so called 'anchored-shape', where the anchored
* shape can be any kind of shape. This textanchor then connects the anchored-shape to the text
* flow so the anchored shape can be repositioned on the canvas if new text is inserted or removed
* before the anchor character.
* For the other types of anchoring it has to have a TextLocation in a QTextDocument. This TextLocation
* can either be an inline character (type as-char) or a position (type char or paragraph) The
* KoShapeAnchor and TextLocation connects the anchored-shape to the text flow so the anchored shape
* can be repositioned on the canvas if new text is inserted or removed before the anchor character.
*
* The KoTextAnchor object is inserted in text, and is represented to the user as one invisible
* character in the text flow. Since this is a real character it will be positioned by the text
* -layout engine and * anything that will change the position of the text will thus also change
* the KoTextAnchor character.
* In such a case where the KoTextAnchor character is repositioned the position of the anchored-shape
* should also be reconsidered.
* For as-char, char and paragraph use cases:
* @see KoAnchorInlineObject
* @see KoAnchorTextRange
* which are both implemented as subclasses of TextLocation
*
* Steps to use a KoTextAnchor are; <ol>
* <li> Create a new instance with e.g. new KoTextAnchor(myshape);
* <li> Use loadOdf() to load additional attributes like the "text:anchor-type"
* <li> Position the anchor with updatePosition() what will attach the KoTextAnchor-instance to
* the TextShape's \a KoTextShapeContainerModel . </ol>
* The position of the shape relative to the anchor is called the offset. It's loaded by loadOdf().
* @see AnchorStrategy for more information about the layout of anchors/shapes in Words.
* @see PlacementStrategy for more information about the layout of anchors/shapes in Words.
*/
class KOTEXT_EXPORT KoTextAnchor : public KoInlineObject
class KOTEXT_EXPORT KoShapeAnchor
{
Q_OBJECT
public:
/**
* This class is an interface that positions the shape linked to text anchor
*/
class PlacementStrategy {
public:
PlacementStrategy(){};
virtual ~PlacementStrategy(){};
virtual void detachFromModel() = 0;
/**
* Reparent the anchored shape under an appropriate shape container (and model)
*
* If needed, it changes the parent KoShapeContainerModel and KoShapeContainer of the anchored
* shape.
*/
virtual void updateContainerModel() = 0;
};
class TextLocation {
public:
TextLocation(){};
virtual ~TextLocation(){};
virtual const QTextDocument *document() const = 0;
virtual int position() const = 0;
};
enum HorizontalPos {
HCenter,
HFromInside,
......@@ -140,8 +147,8 @@ public:
* Constructor for an in-place anchor.
* @param shape the anchored shape that this anchor links to.
*/
explicit KoTextAnchor(KoShape *shape);
virtual ~KoTextAnchor();
explicit KoShapeAnchor(KoShape *shape);
virtual ~KoShapeAnchor();
/**
* Return the shape that is linked to from the text anchor.
......@@ -164,6 +171,7 @@ public:
* - frame
* The parent text box that the current drawing shape element is