File indexing completed on 2025-02-23 04:05:46

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KoShapeResizeCommand.h"
0008 
0009 #include <KoShape.h>
0010 #include "kis_command_ids.h"
0011 #include "kis_assert.h"
0012 
0013 
0014 struct Q_DECL_HIDDEN KoShapeResizeCommand::Private
0015 {
0016     QList<KoShape *> shapes;
0017     qreal scaleX;
0018     qreal scaleY;
0019     QPointF absoluteStillPoint;
0020     bool useGlobalMode;
0021     bool usePostScaling;
0022     QTransform postScalingCoveringTransform;
0023 
0024     QList<QSizeF> oldSizes;
0025     QList<QTransform> oldTransforms;
0026 };
0027 
0028 
0029 KoShapeResizeCommand::KoShapeResizeCommand(const QList<KoShape*> &shapes,
0030                                            qreal scaleX, qreal scaleY,
0031                                            const QPointF &absoluteStillPoint,
0032                                            bool useGLobalMode,
0033                                            bool usePostScaling,
0034                                            const QTransform &postScalingCoveringTransform,
0035                                            KUndo2Command *parent)
0036     : SkipFirstRedoBase(false, kundo2_i18n("Resize"), parent),
0037       m_d(new Private)
0038 {
0039     m_d->shapes = shapes;
0040     m_d->scaleX = scaleX;
0041     m_d->scaleY = scaleY;
0042     m_d->absoluteStillPoint = absoluteStillPoint;
0043     m_d->useGlobalMode = useGLobalMode;
0044     m_d->usePostScaling = usePostScaling;
0045     m_d->postScalingCoveringTransform = postScalingCoveringTransform;
0046 
0047     Q_FOREACH (KoShape *shape, m_d->shapes) {
0048         m_d->oldSizes << shape->size();
0049         m_d->oldTransforms << shape->transformation();
0050     }
0051 }
0052 
0053 KoShapeResizeCommand::~KoShapeResizeCommand()
0054 {
0055 }
0056 
0057 void KoShapeResizeCommand::redoImpl()
0058 {
0059     QMap<KoShape*, QRectF> updates = redoNoUpdate();
0060 
0061     for (auto it = updates.begin(); it != updates.end(); ++it) {
0062         it.key()->updateAbsolute(it.value());
0063     }
0064 }
0065 
0066 void KoShapeResizeCommand::undoImpl()
0067 {
0068     QMap<KoShape*, QRectF> updates = undoNoUpdate();
0069 
0070     for (auto it = updates.begin(); it != updates.end(); ++it) {
0071         it.key()->updateAbsolute(it.value());
0072     }
0073 }
0074 
0075 QMap<KoShape*, QRectF> KoShapeResizeCommand::redoNoUpdate()
0076 {
0077     QMap<KoShape*,QRectF> updates;
0078 
0079     Q_FOREACH (KoShape *shape, m_d->shapes) {
0080         const QRectF oldDirtyRect = shape->boundingRect();
0081 
0082         KoFlake::resizeShapeCommon(shape,
0083                              m_d->scaleX, m_d->scaleY,
0084                              m_d->absoluteStillPoint,
0085                              m_d->useGlobalMode,
0086                              m_d->usePostScaling,
0087                              m_d->postScalingCoveringTransform);
0088 
0089         updates[shape] = oldDirtyRect | shape->boundingRect();
0090     }
0091 
0092     return updates;
0093 }
0094 
0095 QMap<KoShape*, QRectF> KoShapeResizeCommand::undoNoUpdate()
0096 {
0097     QMap<KoShape*,QRectF> updates;
0098 
0099     for (int i = 0; i < m_d->shapes.size(); i++) {
0100         KoShape *shape = m_d->shapes[i];
0101 
0102         const QRectF oldDirtyRect = shape->boundingRect();
0103         shape->setSize(m_d->oldSizes[i]);
0104         shape->setTransformation(m_d->oldTransforms[i]);
0105 
0106         updates[shape] = oldDirtyRect | shape->boundingRect();
0107     }
0108 
0109     return updates;
0110 }
0111 
0112 int KoShapeResizeCommand::id() const
0113 {
0114     return KisCommandUtils::ResizeShapeId;
0115 }
0116 
0117 bool KoShapeResizeCommand::mergeWith(const KUndo2Command *command)
0118 {
0119     const KoShapeResizeCommand *other = dynamic_cast<const KoShapeResizeCommand*>(command);
0120 
0121     if (!other ||
0122         other->m_d->absoluteStillPoint != m_d->absoluteStillPoint ||
0123         other->m_d->shapes != m_d->shapes ||
0124         other->m_d->useGlobalMode != m_d->useGlobalMode ||
0125         other->m_d->usePostScaling != m_d->usePostScaling) {
0126 
0127         return false;
0128     }
0129 
0130     // check if the significant orientations coincide
0131     if (m_d->useGlobalMode && !m_d->usePostScaling) {
0132         Qt::Orientation our = KoFlake::significantScaleOrientation(m_d->scaleX, m_d->scaleY);
0133         Qt::Orientation their = KoFlake::significantScaleOrientation(other->m_d->scaleX, other->m_d->scaleY);
0134 
0135         if (our != their) {
0136             return false;
0137         }
0138     }
0139 
0140     m_d->scaleX *= other->m_d->scaleX;
0141     m_d->scaleY *= other->m_d->scaleY;
0142     return true;
0143 }
0144 
0145 void KoShapeResizeCommand::replaceResizeAction(qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint)
0146 {
0147     const QMap<KoShape*, QRectF> undoUpdates = undoNoUpdate();
0148 
0149     m_d->scaleX = scaleX;
0150     m_d->scaleY = scaleY;
0151     m_d->absoluteStillPoint = absoluteStillPoint;
0152 
0153     const QMap<KoShape*, QRectF> redoUpdates = redoNoUpdate();
0154 
0155     KIS_SAFE_ASSERT_RECOVER_NOOP(undoUpdates.size() == redoUpdates.size());
0156 
0157     for (auto it = undoUpdates.begin(); it != undoUpdates.end(); ++it) {
0158         KIS_SAFE_ASSERT_RECOVER_NOOP(redoUpdates.contains(it.key()));
0159         it.key()->updateAbsolute(it.value() | redoUpdates[it.key()]);
0160     }
0161 }