Krita Source Code Documentation
Loading...
Searching...
No Matches
KoShapeReorderCommand.cpp
Go to the documentation of this file.
1/* This file is part of the KDE project
2 * SPDX-FileCopyrightText: 2006 Thomas Zander <zander@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
8#include "KoShape.h"
9#include "KoShape_p.h"
10#include "KoShapeManager.h"
11#include "KoShapeContainer.h"
12
13#include <klocalizedstring.h>
14#include <FlakeDebug.h>
15#include <limits.h>
16
20
22 : zIndex(_shape->zIndex()), shape(_shape)
23{
24}
25
30
44
46 : KUndo2Command(parent),
47 d(new KoShapeReorderCommandPrivate(shapes, newIndexes))
48{
49 Q_ASSERT(shapes.count() == newIndexes.count());
50 foreach (KoShape *shape, shapes)
51 d->previousIndexes.append(shape->zIndex());
52
53 setText(kundo2_i18n("Reorder shapes"));
54}
55
57 : KUndo2Command(parent),
59{
60 Q_FOREACH (const IndexedShape &index, shapes) {
61 d->shapes.append(index.shape);
62 d->newIndexes.append(index.zIndex);
63 d->previousIndexes.append(index.shape->zIndex());
64 }
65
66 setText(kundo2_i18n("Reorder shapes"));
67}
68
73
75{
77 for (int i = 0; i < d->shapes.count(); i++) {
78 // z-index cannot change the bounding rect of the shape, so
79 // no united updates needed
80 d->shapes.at(i)->setZIndex(d->newIndexes.at(i));
81 d->shapes.at(i)->update();
82 }
83}
84
86{
88 for (int i = 0; i < d->shapes.count(); i++) {
89 // z-index cannot change the bounding rect of the shape, so
90 // no united updates needed
91 d->shapes.at(i)->setZIndex(d->previousIndexes.at(i));
92 d->shapes.at(i)->update();
93 }
94}
95
97{
98 KoShapeContainer *parent = s->parent();
99 QMap<KoShape*, QList<KoShape*> >::iterator it(newOrder.find(parent));
100 if (it == newOrder.end()) {
101 QList<KoShape*> children;
102 if (parent != 0) {
103 children = parent->shapes();
104 }
105 else {
106 // get all toplevel shapes
107 children = manager->topLevelShapes();
108 }
109 std::sort(children.begin(), children.end(), KoShape::compareShapeZIndex);
110 // the append and prepend are needed so that the raise/lower of all shapes works as expected.
111 children.append(0);
112 children.prepend(0);
113 it = newOrder.insert(parent, children);
114 }
115 QList<KoShape *> & shapes(newOrder[parent]);
116 int index = shapes.indexOf(s);
117 if (index != -1) {
118 shapes.removeAt(index);
119 switch (move) {
121 index = shapes.size();
122 break;
124 if (index < shapes.size()) {
125 ++index;
126 }
127 break;
129 if (index > 0) {
130 --index;
131 }
132 break;
134 index = 0;
135 break;
136 }
137 shapes.insert(index,s);
138 }
139}
140
141// static
143{
149 QList<int> newIndexes;
150 QList<KoShape*> changedShapes;
151 QMap<KoShape*, QList<KoShape*> > newOrder;
152 QList<KoShape*> sortedShapes(shapes);
153 std::sort(sortedShapes.begin(), sortedShapes.end(), KoShape::compareShapeZIndex);
154 if (move == BringToFront || move == LowerShape) {
155 for (int i = 0; i < sortedShapes.size(); ++i) {
156 prepare(sortedShapes.at(i), newOrder, manager, move);
157 }
158 }
159 else {
160 for (int i = sortedShapes.size() - 1; i >= 0; --i) {
161 prepare(sortedShapes.at(i), newOrder, manager, move);
162 }
163 }
164
165 QMap<KoShape*, QList<KoShape*> >::iterator newIt(newOrder.begin());
166 for (; newIt != newOrder.end(); ++newIt) {
167 QList<KoShape*> order(newIt.value());
168 order.removeAll(nullptr);
169 int index = -KoShape::maxZIndex - 1; // set minimum zIndex
170 int pos = 0;
171 for (; pos < order.size(); ++pos) {
172 if (order[pos]->zIndex() > index) {
173 index = order[pos]->zIndex();
174 }
175 else {
176 break;
177 }
178 }
179
180 if (pos == order.size()) {
181 //nothing needs to be done
182 continue;
183 }
184 else if (pos <= order.size() / 2) {
185 // new index for the front
186 int startIndex = order[pos]->zIndex() - pos;
187 for (int i = 0; i < pos; ++i) {
188 changedShapes.append(order[i]);
189 newIndexes.append(startIndex++);
190 }
191 }
192 else {
193 //new index for the end
194 for (int i = pos; i < order.size(); ++i) {
195 changedShapes.append(order[i]);
196 newIndexes.append(++index);
197 }
198 }
199 }
200 Q_ASSERT(changedShapes.count() == newIndexes.count());
201 return changedShapes.isEmpty() ? 0: new KoShapeReorderCommand(changedShapes, newIndexes, parent);
202}
203
205{
206 std::sort(shapes.begin(), shapes.end(), KoShape::compareShapeZIndex);
207
208 QList<KoShape*> reindexedShapes;
209 QList<int> reindexedIndexes;
210
211 const int originalShapeZIndex = newShape->zIndex();
212 int newShapeZIndex = originalShapeZIndex;
213 int lastOccupiedShapeZIndex = originalShapeZIndex + 1;
214
215 Q_FOREACH (KoShape *shape, shapes) {
216 if (shape == newShape) continue;
217
218 const int zIndex = shape->zIndex();
219
220 if (newShapeZIndex == originalShapeZIndex) {
221 if (zIndex == originalShapeZIndex) {
222 newShapeZIndex = originalShapeZIndex + 1;
223 lastOccupiedShapeZIndex = newShapeZIndex;
224
225 reindexedShapes << newShape;
226 reindexedIndexes << newShapeZIndex;
227 }
228 } else {
229 if (zIndex >= newShapeZIndex &&
230 zIndex <= lastOccupiedShapeZIndex) {
231
232 lastOccupiedShapeZIndex = zIndex + 1;
233 reindexedShapes << shape;
234 reindexedIndexes << lastOccupiedShapeZIndex;
235 }
236 }
237 }
238
239 return !reindexedShapes.isEmpty() ? new KoShapeReorderCommand(reindexedShapes, reindexedIndexes, parent) : 0;
240}
241
244{
245 if (shapes.isEmpty()) return shapes;
246
247 // the shapes are expected to be sorted, we just need to adjust the indexes
248
249 int lastIndex = shapes.begin()->zIndex;
250
251 auto it = shapes.begin() + 1;
252 while (it != shapes.end()) {
253 if (it->zIndex <= lastIndex) {
254 it->zIndex = lastIndex + 1;
255 }
256 lastIndex = it->zIndex;
257 ++it;
258 }
259
260 const int overflowSize = shapes.last().zIndex - int(std::numeric_limits<qint16>::max());
261
262 if (overflowSize > 0) {
263 if (shapes.first().zIndex - overflowSize > int(std::numeric_limits<qint16>::min())) {
264 for (auto it = shapes.begin(); it != shapes.end(); ++it) {
265 it->zIndex -= overflowSize;
266 }
267 } else {
268 int index = shapes.size() < int(std::numeric_limits<qint16>::max()) ?
269 0 :
270 int(std::numeric_limits<qint16>::max()) - shapes.size();
271
272 for (auto it = shapes.begin(); it != shapes.end(); ++it) {
273 it->zIndex = index;
274 index++;
275 }
276 }
277 }
278
279 return shapes;
280}
281
284{
285 shapes = homogenizeZIndexes(shapes);
286 // remove shapes that didn't change
287 for (auto it = shapes.begin(); it != shapes.end();) {
288 if (it->zIndex == it->shape->zIndex()) {
289 it = shapes.erase(it);
290 } else {
291 ++it;
292 }
293 }
294
295 return shapes;
296}
297
300{
301 std::sort(shapesBelow.begin(), shapesBelow.end(), KoShape::compareShapeZIndex);
302 std::sort(shapesAbove.begin(), shapesAbove.end(), KoShape::compareShapeZIndex);
303
304 QList<IndexedShape> shapes;
305 Q_FOREACH (KoShape *shape, shapesBelow) {
306 shapes.append(IndexedShape(shape));
307 }
308
309 Q_FOREACH (KoShape *shape, shapesAbove) {
310 shapes.append(IndexedShape(shape));
311 }
312
313 return homogenizeZIndexesLazy(shapes);
314}
315
316QDebug operator<<(QDebug dbg, const KoShapeReorderCommand::IndexedShape &indexedShape)
317{
318 dbg.nospace() << "IndexedShape (" << indexedShape.shape << ", " << indexedShape.zIndex << ")";
319 return dbg.space();
320}
static void prepare(KoShape *s, QMap< KoShape *, QList< KoShape * > > &newOrder, KoShapeManager *manager, KoShapeReorderCommand::MoveShapeType move)
QDebug operator<<(QDebug dbg, const KoShapeReorderCommand::IndexedShape &indexedShape)
virtual void undo()
void setText(const KUndo2MagicString &text)
virtual void redo()
QList< KoShape * > topLevelShapes() const
KoShapeReorderCommandPrivate(const QList< KoShape * > &s, QList< int > &ni)
This command allows you to change the zIndex of a number of shapes.
void undo() override
revert the actions done in redo
static KoShapeReorderCommand * mergeInShape(QList< KoShape * > shapes, KoShape *newShape, KUndo2Command *parent=0)
mergeInShape adjust zIndex of all the shapes and newShape to avoid collisions between shapes and newS...
KoShapeReorderCommand(const QList< KoShape * > &shapes, QList< int > &newIndexes, KUndo2Command *parent=0)
static QList< KoShapeReorderCommand::IndexedShape > homogenizeZIndexes(QList< IndexedShape > shapes)
static QList< IndexedShape > mergeDownShapes(QList< KoShape * > shapesBelow, QList< KoShape * > shapesAbove)
KoShapeReorderCommandPrivate *const d
void redo() override
redo the command
MoveShapeType
An enum for defining what kind of reordering to use.
@ RaiseShape
raise the selected shape to the level that it is above the shape that is on top of it.
@ SendToBack
Lower the selected shape to be below all other shapes.
@ LowerShape
Lower the selected shape to the level that it is below the shape that is below it.
@ BringToFront
Raise the selected shape to be on top of all shapes.
static QList< KoShapeReorderCommand::IndexedShape > homogenizeZIndexesLazy(QList< IndexedShape > shapes)
static KoShapeReorderCommand * createCommand(const QList< KoShape * > &shapes, KoShapeManager *manager, MoveShapeType move, KUndo2Command *parent=0)
static bool compareShapeZIndex(KoShape *s1, KoShape *s2)
Definition KoShape.cpp:434
KoShapeContainer * parent() const
Definition KoShape.cpp:1039
qint16 zIndex() const
Definition KoShape.cpp:600
static const qint16 maxZIndex
Definition KoShape.h:502
KUndo2MagicString kundo2_i18n(const char *text)
bool operator<(const IndexedShape &rhs) const