File indexing completed on 2025-03-09 04:03:40

0001 /* This file is part of the KDE project
0002  * SPDX-FileCopyrightText: 2006 Thomas Zander <zander@kde.org>
0003  * SPDX-FileCopyrightText: 2006 Jan Hambrecht <jaham@gmx.net>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "KoShapeDistributeCommand.h"
0009 
0010 #include "commands/KoShapeMoveCommand.h"
0011 #include "KoShape.h"
0012 #include <QMap>
0013 
0014 #include <klocalizedstring.h>
0015 
0016 class Q_DECL_HIDDEN KoShapeDistributeCommand::Private
0017 {
0018 public:
0019     Private() : command(0) {}
0020     ~Private() {
0021         delete command;
0022     }
0023 
0024     qreal getAvailableSpace(KoShape *first, KoShape *last, qreal extent, const QRectF &boundingRect);
0025 
0026     Distribute distribute;
0027     KoShapeMoveCommand *command;
0028 };
0029 
0030 KoShapeDistributeCommand::KoShapeDistributeCommand(const QList<KoShape*> &shapes, Distribute distribute, const QRectF &boundingRect, KUndo2Command *parent)
0031         : KUndo2Command(parent),
0032         d(new Private())
0033 {
0034     d->distribute = distribute;
0035     QMap<qreal, KoShape*> sortedPos;
0036     QRectF bRect;
0037     qreal extent = 0.0;
0038     // sort by position and calculate sum of objects width/height
0039     Q_FOREACH (KoShape *shape, shapes) {
0040         bRect = shape->absoluteOutlineRect();
0041         switch (d->distribute) {
0042         case HorizontalCenterDistribution:
0043             sortedPos[bRect.center().x()] = shape;
0044             break;
0045         case HorizontalGapsDistribution:
0046         case HorizontalLeftDistribution:
0047             sortedPos[bRect.left()] = shape;
0048             extent += bRect.width();
0049             break;
0050         case HorizontalRightDistribution:
0051             sortedPos[bRect.right()] = shape;
0052             break;
0053         case VerticalCenterDistribution:
0054             sortedPos[bRect.center().y()] = shape;
0055             break;
0056         case VerticalGapsDistribution:
0057         case VerticalBottomDistribution:
0058             sortedPos[bRect.bottom()] = shape;
0059             extent += bRect.height();
0060             break;
0061         case VerticalTopDistribution:
0062             sortedPos[bRect.top()] = shape;
0063             break;
0064         }
0065     }
0066     KoShape* first = sortedPos.begin().value();
0067     KoShape* last = (--sortedPos.end()).value();
0068 
0069     // determine the available space to distribute
0070     qreal space = d->getAvailableSpace(first, last, extent, boundingRect);
0071     qreal pos = 0.0, step = space / qreal(shapes.count() - 1);
0072 
0073     QList<QPointF> previousPositions;
0074     QList<QPointF> newPositions;
0075     QPointF position;
0076     QPointF delta;
0077     QMapIterator<qreal, KoShape*> it(sortedPos);
0078     while (it.hasNext()) {
0079         it.next();
0080         position = it.value()->absolutePosition();
0081         previousPositions  << position;
0082 
0083         bRect = it.value()->absoluteOutlineRect();
0084         switch (d->distribute)        {
0085         case HorizontalCenterDistribution:
0086             delta = QPointF(boundingRect.x() + first->absoluteOutlineRect().width() / 2 + pos - bRect.width() / 2, bRect.y()) - bRect.topLeft();
0087             break;
0088         case HorizontalGapsDistribution:
0089             delta = QPointF(boundingRect.left() + pos, bRect.y()) - bRect.topLeft();
0090             pos += bRect.width();
0091             break;
0092         case HorizontalLeftDistribution:
0093             delta = QPointF(boundingRect.left() + pos, bRect.y()) - bRect.topLeft();
0094             break;
0095         case HorizontalRightDistribution:
0096             delta = QPointF(boundingRect.left() + first->absoluteOutlineRect().width() + pos - bRect.width(), bRect.y()) - bRect.topLeft();
0097             break;
0098         case VerticalCenterDistribution:
0099             delta = QPointF(bRect.x(), boundingRect.y() + first->absoluteOutlineRect().height() / 2 + pos - bRect.height() / 2) - bRect.topLeft();
0100             break;
0101         case VerticalGapsDistribution:
0102             delta = QPointF(bRect.x(), boundingRect.top() + pos) - bRect.topLeft();
0103             pos += bRect.height();
0104             break;
0105         case VerticalBottomDistribution:
0106             delta = QPointF(bRect.x(), boundingRect.top() + first->absoluteOutlineRect().height() + pos - bRect.height()) - bRect.topLeft();
0107             break;
0108         case VerticalTopDistribution:
0109             delta = QPointF(bRect.x(), boundingRect.top() + pos) - bRect.topLeft();
0110             break;
0111         };
0112         newPositions  << position + delta;
0113         pos += step;
0114     }
0115     d->command = new KoShapeMoveCommand(sortedPos.values(), previousPositions, newPositions);
0116 
0117     setText(kundo2_i18n("Distribute shapes"));
0118 }
0119 
0120 KoShapeDistributeCommand::~KoShapeDistributeCommand()
0121 {
0122     delete d;
0123 }
0124 
0125 void KoShapeDistributeCommand::redo()
0126 {
0127     KUndo2Command::redo();
0128     d->command->redo();
0129 }
0130 
0131 void KoShapeDistributeCommand::undo()
0132 {
0133     KUndo2Command::undo();
0134     d->command->undo();
0135 }
0136 
0137 qreal KoShapeDistributeCommand::Private::getAvailableSpace(KoShape *first, KoShape *last, qreal extent, const QRectF &boundingRect)
0138 {
0139     switch (distribute) {
0140     case HorizontalCenterDistribution:
0141         return boundingRect.width() - last->absoluteOutlineRect().width() / 2 - first->absoluteOutlineRect().width() / 2;
0142         break;
0143     case HorizontalGapsDistribution:
0144         return boundingRect.width() - extent;
0145         break;
0146     case HorizontalLeftDistribution:
0147         return boundingRect.width() - last->absoluteOutlineRect().width();
0148         break;
0149     case HorizontalRightDistribution:
0150         return boundingRect.width() - first->absoluteOutlineRect().width();
0151         break;
0152     case VerticalCenterDistribution:
0153         return boundingRect.height() - last->absoluteOutlineRect().height() / 2 - first->absoluteOutlineRect().height() / 2;
0154         break;
0155     case VerticalGapsDistribution:
0156         return boundingRect.height() - extent;
0157         break;
0158     case VerticalBottomDistribution:
0159         return boundingRect.height() - first->absoluteOutlineRect().height();
0160         break;
0161     case VerticalTopDistribution:
0162         return boundingRect.height() - last->absoluteOutlineRect().height();
0163         break;
0164     }
0165     return 0.0;
0166 }