Warning, file /office/calligra/libs/textlayout/KoTextShapeContainerModel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  * Copyright (C) 2007,2009,2010 Thomas Zander <zander@kde.org>
0003  * Copyright (C) 2010 C. Boemann <cbo@kogmbh.com>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "KoTextShapeContainerModel.h"
0022 
0023 #include "KoAnchorInlineObject.h"
0024 #include "KoTextShapeData.h"
0025 #include "KoShapeContainer.h"
0026 
0027 #include <QTextBlock>
0028 #include <QTextLayout>
0029 #include <QTextLine>
0030 #include <QTextDocument>
0031 
0032 #include <TextLayoutDebug.h>
0033 
0034 struct Relation
0035 {
0036     Relation(KoShape *shape = 0)
0037         : child(shape),
0038         anchor(0),
0039         nested(false),
0040         inheritsTransform(false)
0041     {
0042     }
0043     KoShape *child;
0044     KoShapeAnchor *anchor;
0045     uint nested : 1;
0046     uint inheritsTransform :1;
0047 };
0048 
0049 class Q_DECL_HIDDEN KoTextShapeContainerModel::Private
0050 {
0051 public:
0052     QHash<const KoShape*, Relation> children;
0053     QList<KoShapeAnchor *> shapeRemovedAnchors;
0054 };
0055 
0056 KoTextShapeContainerModel::KoTextShapeContainerModel()
0057         : d(new Private())
0058 {
0059 }
0060 
0061 KoTextShapeContainerModel::~KoTextShapeContainerModel()
0062 {
0063     delete d;
0064 }
0065 
0066 void KoTextShapeContainerModel::add(KoShape *child)
0067 {
0068     if (d->children.contains(child))
0069         return;
0070     Relation relation(child);
0071     d->children.insert(child, relation);
0072 
0073     KoShapeAnchor *toBeAddedAnchor = 0;
0074     foreach (KoShapeAnchor *anchor, d->shapeRemovedAnchors) {
0075         if (child == anchor->shape()) {
0076             toBeAddedAnchor = anchor;
0077             break;
0078         }
0079     }
0080 
0081     if (toBeAddedAnchor) {
0082         addAnchor(toBeAddedAnchor);
0083         d->shapeRemovedAnchors.removeAll(toBeAddedAnchor);
0084     }
0085 }
0086 
0087 void KoTextShapeContainerModel::remove(KoShape *child)
0088 {
0089     Relation relation = d->children.value(child);
0090     d->children.remove(child);
0091     if (relation.anchor) {
0092         relation.anchor->placementStrategy()->detachFromModel();
0093         d->shapeRemovedAnchors.append(relation.anchor);
0094     }
0095 }
0096 
0097 void KoTextShapeContainerModel::setClipped(const KoShape *child, bool clipping)
0098 {
0099     Q_ASSERT(d->children.contains(child));
0100     d->children[child].nested = clipping;
0101 }
0102 
0103 bool KoTextShapeContainerModel::isClipped(const KoShape *child) const
0104 {
0105     Q_ASSERT(d->children.contains(child));
0106     return d->children[child].nested;
0107 }
0108 
0109 void KoTextShapeContainerModel::setInheritsTransform(const KoShape *shape, bool inherit)
0110 {
0111     Q_ASSERT(d->children.contains(shape));
0112     d->children[shape].inheritsTransform = inherit;
0113 }
0114 
0115 bool KoTextShapeContainerModel::inheritsTransform(const KoShape *shape) const
0116 {
0117     Q_ASSERT(d->children.contains(shape));
0118     return d->children[shape].inheritsTransform;
0119 }
0120 
0121 
0122 int KoTextShapeContainerModel::count() const
0123 {
0124     return d->children.count();
0125 }
0126 
0127 QList<KoShape*> KoTextShapeContainerModel::shapes() const
0128 {
0129     QList<KoShape*> answer;
0130     answer.reserve(d->children.count());
0131     foreach (const Relation &relation, d->children) {
0132         answer << relation.child;
0133     }
0134     return answer;
0135 }
0136 
0137 void KoTextShapeContainerModel::containerChanged(KoShapeContainer *container, KoShape::ChangeType type)
0138 {
0139     Q_UNUSED(container);
0140     Q_UNUSED(type);
0141 }
0142 
0143 void KoTextShapeContainerModel::childChanged(KoShape *child, KoShape::ChangeType type)
0144 {
0145     if (((type == KoShape::RotationChanged ||
0146           type == KoShape::ScaleChanged ||
0147           type == KoShape::ShearChanged ||
0148           type == KoShape::ClipPathChanged ||
0149           type == KoShape::PositionChanged ||
0150           type == KoShape::SizeChanged) && child->textRunAroundSide() != KoShape::RunThrough) ||
0151           type == KoShape::TextRunAroundChanged) {
0152 
0153         relayoutInlineObject(child);
0154     }
0155     KoShapeContainerModel::childChanged( child, type );
0156 }
0157 
0158 void KoTextShapeContainerModel::addAnchor(KoShapeAnchor *anchor)
0159 {
0160     Q_ASSERT(anchor);
0161     Q_ASSERT(anchor->shape());
0162     Q_ASSERT(d->children.contains(anchor->shape()));
0163     d->children[anchor->shape()].anchor = anchor;
0164 }
0165 
0166 void KoTextShapeContainerModel::removeAnchor(KoShapeAnchor *anchor)
0167 {
0168     if (d->children.contains(anchor->shape())) {
0169         d->children[anchor->shape()].anchor = 0;
0170         d->shapeRemovedAnchors.removeAll(anchor);
0171     }
0172 }
0173 
0174 void KoTextShapeContainerModel::proposeMove(KoShape *child, QPointF &move)
0175 {
0176     if (!d->children.contains(child))
0177         return;
0178     Relation relation = d->children.value(child);
0179     if (relation.anchor == 0)
0180         return;
0181 
0182     QPointF newPosition = child->position() + move/* + relation.anchor->offset()*/;
0183 //warnTextLayout <<"proposeMove:" /*<< move <<" |"*/ << newPosition <<" |" << parentShapeRect;
0184 
0185     QTextLayout *layout = 0;
0186     int anchorPosInParag = -1;
0187 
0188     if (relation.anchor->anchorType() == KoShapeAnchor::AnchorAsCharacter) {
0189         int posInDocument = relation.anchor->textLocation()->position();
0190         const QTextDocument *document = relation.anchor->textLocation()->document();
0191         QTextBlock block = document->findBlock(posInDocument);
0192         layout = block.layout();
0193         anchorPosInParag = posInDocument - block.position();
0194         if (layout) {
0195             QTextLine tl = layout->lineForTextPosition(anchorPosInParag);
0196             Q_ASSERT(tl.isValid());
0197             relation.anchor->setOffset(QPointF(newPosition.x() - tl.cursorToX(anchorPosInParag)
0198                 + tl.x(), 0));
0199             relayoutInlineObject(child);
0200         }
0201 
0202         // the rest of the code uses the shape baseline, at this time the bottom. So adjust
0203         newPosition.setY(newPosition.y() + child->size().height());
0204         if (layout == 0) {
0205             QTextBlock block = document->findBlock(posInDocument);
0206             layout = block.layout();
0207             anchorPosInParag = posInDocument - block.position();
0208         }
0209         if (layout->lineCount() > 0) {
0210             KoTextShapeData *data = qobject_cast<KoTextShapeData*>(child->parent()->userData());
0211             Q_ASSERT(data);
0212             QTextLine tl = layout->lineForTextPosition(anchorPosInParag);
0213             Q_ASSERT(tl.isValid());
0214             qreal y = tl.y() - data->documentOffset() - newPosition.y() + child->size().height();
0215             relation.anchor->setOffset(QPointF(relation.anchor->offset().x(), -y));
0216             relayoutInlineObject(child);
0217         }
0218     } else {
0219         //TODO pavolk: handle position type change: absolute to realtive, etc ..
0220         child->setPosition(newPosition);
0221         relation.anchor->setOffset(relation.anchor->offset() + move);
0222         relayoutInlineObject(child);
0223     }
0224 
0225     move.setX(0); // let the text layout move it.
0226     move.setY(0);
0227 }
0228 
0229 bool KoTextShapeContainerModel::isChildLocked(const KoShape *child) const
0230 {
0231     return child->isGeometryProtected();
0232 }
0233 
0234 void KoTextShapeContainerModel::relayoutInlineObject(KoShape *child)
0235 {
0236     if (child == 0) {
0237         return;
0238     }
0239     KoTextShapeData *data  = qobject_cast<KoTextShapeData*>(child->parent()->userData());
0240     Q_ASSERT(data);
0241     data->setDirty();
0242 }
0243