Warning, file /office/calligra/libs/flake/KoAnnotationLayoutManager.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) 2013 Mojtaba Shahi Senobari <mojtaba.shahi3000@gmail.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Library General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Library General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Library General Public License
0015  * along with this library; see the file COPYING.LIB.  If not, write to
0016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018  */
0019 #include <algorithm>
0020 
0021 #include "KoAnnotationLayoutManager.h"
0022 
0023 #include <KoViewConverter.h>
0024 #include <KoShapeManager.h>
0025 #include <KoCanvasBase.h>
0026 #include <KoShape.h>
0027 #include <QWidget>
0028 #include <QList>
0029 #include <QHash>
0030 #include <QMap>
0031 #include <QtAlgorithms>
0032 #include <QPainter>
0033 #include <QPen>
0034 #include <QLine>
0035 #include <QLineF>
0036 
0037 #include <FlakeDebug.h>
0038 
0039 int compare(const QPair < QPointF, KoShape * > &a, const QPair < QPointF, KoShape * > &b)
0040 {
0041     if (a.first.y() == b.first.y()) {
0042         return a.first.x() < b.first.x();
0043     }
0044     return a.first.y() < b.first.y();
0045 }
0046 
0047 class Q_DECL_HIDDEN KoAnnotationLayoutManager::Private
0048 {
0049 public:
0050     Private()
0051         : shapeManager(0),
0052           canvas(0)
0053     {}
0054 
0055     qreal x; // The x point of annotation shapes position.
0056     QList< QPair < QPointF, KoShape * > > annotationShapePositions;
0057     KoShapeManager *shapeManager;
0058     KoCanvasBase *canvas;
0059 };
0060 
0061 KoAnnotationLayoutManager::KoAnnotationLayoutManager(QObject *parent)
0062     : QObject(parent)
0063     , d(new Private())
0064 {
0065 }
0066 
0067 KoAnnotationLayoutManager::~KoAnnotationLayoutManager()
0068 {
0069 }
0070 
0071 void KoAnnotationLayoutManager::setShapeManager(KoShapeManager *shapeManager)
0072 {
0073     if (d->shapeManager) {
0074         disconnect(d->shapeManager, SIGNAL(shapeChanged(KoShape*)), this, SLOT(updateLayout(KoShape*)));
0075     }
0076     d->shapeManager = shapeManager;
0077     connect(d->shapeManager, SIGNAL(shapeChanged(KoShape*)), this, SLOT(updateLayout(KoShape*)));
0078 }
0079 
0080 
0081 void KoAnnotationLayoutManager::setCanvasBase(KoCanvasBase *canvas)
0082 {
0083     d->canvas = canvas;
0084 }
0085 
0086 void KoAnnotationLayoutManager::setViewContentWidth(qreal width)
0087 {
0088     d->x = width;
0089 }
0090 
0091 void KoAnnotationLayoutManager::paintConnections(QPainter &painter)
0092 {
0093     painter.save();
0094 
0095     QPen pen(QColor(230, 216, 87)); // Color of lines.
0096     pen.setStyle(Qt::DashLine);
0097     pen.setWidth(2);
0098     pen.setJoinStyle(Qt::RoundJoin);
0099     pen.setCapStyle(Qt::RoundCap);
0100 
0101     painter.setPen(pen);
0102 
0103     QList< QPair < QPointF, KoShape * > >::const_iterator it = d->annotationShapePositions.constBegin();
0104     while (it != d->annotationShapePositions.constEnd() && !d->annotationShapePositions.isEmpty()) {
0105         KoShape *shape = it->second;
0106 
0107         // To make it more beautiful start line from shape a little downer of top-left of annotation shape (shape->position().y() + 20.0).
0108         QPointF shapePosition(d->canvas->viewConverter()->documentToView(QPointF(shape->position().x(), (shape->position().y() + 20.0))));
0109         QPointF refTextPosition(d->canvas->viewConverter()->documentToView(QPointF(it->first.x(), (it->first.y()))));
0110         QPointF connectionPoint(d->canvas->viewConverter()->documentToView(QPointF((shape->position().x() - connectionPointLines), (it->first.y()))));
0111         QPointF pointLine(d->canvas->viewConverter()->documentToView(QPointF(it->first.x(), (it->first.y() + 5))));
0112 
0113 
0114         //Draw first line, from shape to connection Point.
0115         painter.drawLine(shapePosition, connectionPoint);
0116         // Draw second line, from connection point to reftext position.
0117         painter.drawLine(connectionPoint, refTextPosition);
0118         // Draw pointer.
0119         painter.drawLine(refTextPosition, pointLine);
0120 
0121         it++;
0122     }
0123     painter.restore();
0124 }
0125 
0126 bool KoAnnotationLayoutManager::isAnnotationShape(KoShape *shape) const
0127 {
0128     QList< QPair < QPointF, KoShape * > >::ConstIterator it = d->annotationShapePositions.constBegin();
0129     while (it != d->annotationShapePositions.constEnd()) {
0130         if (shape == it->second) {
0131             return true;
0132         }
0133         ++it;
0134     }
0135     return false;
0136 }
0137 
0138 void KoAnnotationLayoutManager::registerAnnotationRefPosition(KoShape *annotationShape, const QPointF &pos)
0139 {
0140     QList< QPair < QPointF, KoShape * > >::iterator it = d->annotationShapePositions.begin();
0141     bool yPositionChanged = false;
0142     while (it != d->annotationShapePositions.end()) {
0143         KoShape *shape = it->second;
0144         if (shape == annotationShape) {
0145             if (it->first.y() != pos.y()) {
0146                 yPositionChanged = true;
0147             }
0148             d->annotationShapePositions.erase(it);
0149             break;
0150         }
0151         ++it;
0152     }
0153     if (d->annotationShapePositions.isEmpty()) {
0154         emit hasAnnotationsChanged(true);
0155     }
0156     d->annotationShapePositions.append(QPair< QPointF, KoShape * >(pos, annotationShape));
0157     layoutAnnotationShapes();
0158     if (d->canvas && yPositionChanged) {
0159         d->canvas->canvasWidget()->update();
0160     }
0161 }
0162 
0163 void KoAnnotationLayoutManager::removeAnnotationShape(KoShape *annotationShape)
0164 {
0165     QList< QPair < QPointF, KoShape * > >::iterator it = d->annotationShapePositions.begin();
0166     while (it != d->annotationShapePositions.end()) {
0167         if (it->second == annotationShape) {
0168             d->annotationShapePositions.erase(it);
0169             break;
0170         }
0171         ++it;
0172     }
0173     layoutAnnotationShapes();
0174     if (d->annotationShapePositions.isEmpty()) {
0175         emit hasAnnotationsChanged(false);
0176     }
0177     //Should update canvas.
0178     d->canvas->canvasWidget()->update();
0179 }
0180 
0181 void KoAnnotationLayoutManager::updateLayout(KoShape *shape)
0182 {
0183     QList< QPair < QPointF, KoShape * > >::const_iterator it = d->annotationShapePositions.constBegin();
0184     while (it != d->annotationShapePositions.constEnd() && !d->annotationShapePositions.isEmpty()) {
0185         if (it->second == shape) {
0186             layoutAnnotationShapes();
0187             break;
0188         }
0189         ++it;
0190     }
0191 }
0192 
0193 void KoAnnotationLayoutManager::layoutAnnotationShapes()
0194 {
0195     qreal currentY = 0.0;
0196     std::stable_sort(d->annotationShapePositions.begin(), d->annotationShapePositions.end(), compare);
0197 
0198     QList< QPair < QPointF, KoShape * > >::const_iterator it = d->annotationShapePositions.constBegin();
0199     while (it != d->annotationShapePositions.constEnd()) {
0200         KoShape *shape = it->second;
0201         qreal refPosition = it->first.y();
0202         if (refPosition > currentY) {
0203             currentY = refPosition;
0204         }
0205         shape->update();
0206         shape->setSize(QSize(shapeWidth, shape->size().height()));
0207         shape->setPosition(QPointF(d->x, currentY));
0208 
0209         // This makes the shape visible. It was set to invisible when it was created.
0210         shape->setVisible(true);
0211         shape->update();
0212         currentY += shape->size().height() + shapeSpace;
0213 
0214         ++it;
0215     }
0216 }
0217 
0218 // only static const integral data members can be initialized within a class
0219 const qreal KoAnnotationLayoutManager::shapeSpace = 10.0; // Distance between annotation shapes.
0220 const qreal KoAnnotationLayoutManager::shapeWidth = 200.0; // Annotation shapes width.
0221 const qreal KoAnnotationLayoutManager::connectionPointLines = 50.0; //Connection point of lines from shape to this point and from this point to refText position.