1 /*
2  *  Copyright (c) 2016 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "KoShapeResizeCommand.h"
20 
21 #include <KoShape.h>
22 #include "kis_command_ids.h"
23 #include "kis_assert.h"
24 
25 
26 struct Q_DECL_HIDDEN KoShapeResizeCommand::Private
27 {
28     QList<KoShape *> shapes;
29     qreal scaleX;
30     qreal scaleY;
31     QPointF absoluteStillPoint;
32     bool useGlobalMode;
33     bool usePostScaling;
34     QTransform postScalingCoveringTransform;
35 
36     QList<QSizeF> oldSizes;
37     QList<QTransform> oldTransforms;
38 };
39 
40 
KoShapeResizeCommand(const QList<KoShape * > & shapes,qreal scaleX,qreal scaleY,const QPointF & absoluteStillPoint,bool useGLobalMode,bool usePostScaling,const QTransform & postScalingCoveringTransform,KUndo2Command * parent)41 KoShapeResizeCommand::KoShapeResizeCommand(const QList<KoShape*> &shapes,
42                                            qreal scaleX, qreal scaleY,
43                                            const QPointF &absoluteStillPoint,
44                                            bool useGLobalMode,
45                                            bool usePostScaling,
46                                            const QTransform &postScalingCoveringTransform,
47                                            KUndo2Command *parent)
48     : SkipFirstRedoBase(false, kundo2_i18n("Resize"), parent),
49       m_d(new Private)
50 {
51     m_d->shapes = shapes;
52     m_d->scaleX = scaleX;
53     m_d->scaleY = scaleY;
54     m_d->absoluteStillPoint = absoluteStillPoint;
55     m_d->useGlobalMode = useGLobalMode;
56     m_d->usePostScaling = usePostScaling;
57     m_d->postScalingCoveringTransform = postScalingCoveringTransform;
58 
59     Q_FOREACH (KoShape *shape, m_d->shapes) {
60         m_d->oldSizes << shape->size();
61         m_d->oldTransforms << shape->transformation();
62     }
63 }
64 
~KoShapeResizeCommand()65 KoShapeResizeCommand::~KoShapeResizeCommand()
66 {
67 }
68 
redoImpl()69 void KoShapeResizeCommand::redoImpl()
70 {
71     QMap<KoShape*, QRectF> updates = redoNoUpdate();
72 
73     for (auto it = updates.begin(); it != updates.end(); ++it) {
74         it.key()->updateAbsolute(it.value());
75     }
76 }
77 
undoImpl()78 void KoShapeResizeCommand::undoImpl()
79 {
80     QMap<KoShape*, QRectF> updates = undoNoUpdate();
81 
82     for (auto it = updates.begin(); it != updates.end(); ++it) {
83         it.key()->updateAbsolute(it.value());
84     }
85 }
86 
redoNoUpdate()87 QMap<KoShape*, QRectF> KoShapeResizeCommand::redoNoUpdate()
88 {
89     QMap<KoShape*,QRectF> updates;
90 
91     Q_FOREACH (KoShape *shape, m_d->shapes) {
92         const QRectF oldDirtyRect = shape->boundingRect();
93 
94         KoFlake::resizeShapeCommon(shape,
95                              m_d->scaleX, m_d->scaleY,
96                              m_d->absoluteStillPoint,
97                              m_d->useGlobalMode,
98                              m_d->usePostScaling,
99                              m_d->postScalingCoveringTransform);
100 
101         updates[shape] = oldDirtyRect | shape->boundingRect();
102     }
103 
104     return updates;
105 }
106 
undoNoUpdate()107 QMap<KoShape*, QRectF> KoShapeResizeCommand::undoNoUpdate()
108 {
109     QMap<KoShape*,QRectF> updates;
110 
111     for (int i = 0; i < m_d->shapes.size(); i++) {
112         KoShape *shape = m_d->shapes[i];
113 
114         const QRectF oldDirtyRect = shape->boundingRect();
115         shape->setSize(m_d->oldSizes[i]);
116         shape->setTransformation(m_d->oldTransforms[i]);
117 
118         updates[shape] = oldDirtyRect | shape->boundingRect();
119     }
120 
121     return updates;
122 }
123 
id() const124 int KoShapeResizeCommand::id() const
125 {
126     return KisCommandUtils::ResizeShapeId;
127 }
128 
mergeWith(const KUndo2Command * command)129 bool KoShapeResizeCommand::mergeWith(const KUndo2Command *command)
130 {
131     const KoShapeResizeCommand *other = dynamic_cast<const KoShapeResizeCommand*>(command);
132 
133     if (!other ||
134         other->m_d->absoluteStillPoint != m_d->absoluteStillPoint ||
135         other->m_d->shapes != m_d->shapes ||
136         other->m_d->useGlobalMode != m_d->useGlobalMode ||
137         other->m_d->usePostScaling != m_d->usePostScaling) {
138 
139         return false;
140     }
141 
142     // check if the significant orientations coincide
143     if (m_d->useGlobalMode && !m_d->usePostScaling) {
144         Qt::Orientation our = KoFlake::significantScaleOrientation(m_d->scaleX, m_d->scaleY);
145         Qt::Orientation their = KoFlake::significantScaleOrientation(other->m_d->scaleX, other->m_d->scaleY);
146 
147         if (our != their) {
148             return false;
149         }
150     }
151 
152     m_d->scaleX *= other->m_d->scaleX;
153     m_d->scaleY *= other->m_d->scaleY;
154     return true;
155 }
156 
replaceResizeAction(qreal scaleX,qreal scaleY,const QPointF & absoluteStillPoint)157 void KoShapeResizeCommand::replaceResizeAction(qreal scaleX, qreal scaleY, const QPointF &absoluteStillPoint)
158 {
159     const QMap<KoShape*, QRectF> undoUpdates = undoNoUpdate();
160 
161     m_d->scaleX = scaleX;
162     m_d->scaleY = scaleY;
163     m_d->absoluteStillPoint = absoluteStillPoint;
164 
165     const QMap<KoShape*, QRectF> redoUpdates = redoNoUpdate();
166 
167     KIS_SAFE_ASSERT_RECOVER_NOOP(undoUpdates.size() == redoUpdates.size());
168 
169     for (auto it = undoUpdates.begin(); it != undoUpdates.end(); ++it) {
170         KIS_SAFE_ASSERT_RECOVER_NOOP(redoUpdates.contains(it.key()));
171         it.key()->updateAbsolute(it.value() | redoUpdates[it.key()]);
172     }
173 }
174