# HG changeset patch
# User Santeri Piippo
# Date 1520371780 -7200
# Node ID 900f1dfae46bfa06a27d90f76481a16c86655d7b
# Parent bf0ac547b9349ae522e13ddf4f00e55d53018c57
Implemented row moving in the model and replaced swapping with it
diff -r bf0ac547b934 -r 900f1dfae46b CMakeLists.txt
--- a/CMakeLists.txt Mon Mar 05 23:59:47 2018 +0200
+++ b/CMakeLists.txt Tue Mar 06 23:29:40 2018 +0200
@@ -138,6 +138,8 @@
src/editmodes/magicWandMode.h
src/editmodes/rectangleMode.h
src/editmodes/selectMode.h
+ src/generics/migrate.h
+ src/generics/range.h
src/generics/reverse.h
src/geometry/linesegment.h
src/linetypes/comment.h
diff -r bf0ac547b934 -r 900f1dfae46b src/basics.h
--- a/src/basics.h Mon Mar 05 23:59:47 2018 +0200
+++ b/src/basics.h Tue Mar 06 23:29:40 2018 +0200
@@ -329,6 +329,15 @@
return x / qAbs(x);
}
+template<>
+inline int sign(int x)
+{
+ if (x == 0)
+ return 0;
+ else
+ return x / qAbs(x);
+}
+
/*
* Returns the maximum of a single parameter (the parameter itself).
*/
diff -r bf0ac547b934 -r 900f1dfae46b src/editHistory.cpp
--- a/src/editHistory.cpp Mon Mar 05 23:59:47 2018 +0200
+++ b/src/editHistory.cpp Tue Mar 06 23:29:40 2018 +0200
@@ -188,19 +188,31 @@
parent()->document()->setObjectAt(row, newState);
}
-SwapHistoryEntry::SwapHistoryEntry (const QModelIndex& index_1, const QModelIndex& index_2, EditHistory* parent) :
+MoveHistoryEntry::MoveHistoryEntry(int top, int bottom, int destination, EditHistory* parent) :
AbstractHistoryEntry {parent},
- row_1 (index_1.row()),
- row_2 (index_2.row()) {}
+ top {top},
+ bottom {bottom},
+ destination {destination} {}
-
-void SwapHistoryEntry::undo()
+void MoveHistoryEntry::undo()
{
- Model* model = parent()->document();
- model->swapObjects(model->index(row_1), model->index(row_2));
+ bool downwards = (destination < top);
+ parent()->document()->moveRows(
+ {},
+ downwards ? (destination) : (destination - (bottom - top) - 1),
+ bottom - top + 1,
+ {},
+ downwards ? (bottom + 1) : top
+ );
}
-void SwapHistoryEntry::redo()
+void MoveHistoryEntry::redo()
{
- undo();
+ parent()->document()->moveRows(
+ {},
+ top,
+ bottom - top + 1,
+ {},
+ destination
+ );
}
diff -r bf0ac547b934 -r 900f1dfae46b src/editHistory.h
--- a/src/editHistory.h Mon Mar 05 23:59:47 2018 +0200
+++ b/src/editHistory.h Tue Mar 06 23:29:40 2018 +0200
@@ -116,14 +116,15 @@
Serializer::Archive newState;
};
-class SwapHistoryEntry : public AbstractHistoryEntry
+class MoveHistoryEntry : public AbstractHistoryEntry
{
public:
- SwapHistoryEntry (const QModelIndex& index_1, const QModelIndex& index_2, EditHistory* parent);
+ MoveHistoryEntry(int top, int bottom, int destination, EditHistory* parent);
void undo() override;
void redo() override;
private:
- int row_1;
- int row_2;
+ int top;
+ int bottom;
+ int destination;
};
diff -r bf0ac547b934 -r 900f1dfae46b src/generics/migrate.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/generics/migrate.h Tue Mar 06 23:29:40 2018 +0200
@@ -0,0 +1,45 @@
+/*
+ * LDForge: LDraw parts authoring CAD
+ * Copyright (C) 2013 - 2017 Teemu Piippo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "range.h"
+
+template
+void migrate(T& vector, int first, int last, int destination)
+{
+ if (destination < first)
+ {
+ int n = first - destination;
+ for (int i : range(first, first + 1, last))
+ {
+ for (int step : range(i - 1, i - 2, i - n))
+ std::swap(vector[step + 1], vector[step]);
+ }
+ }
+ else if (destination > last)
+ {
+ int n = destination - last - 1;
+
+ for (int i : range(last, last - 1, first))
+ {
+ for (int step : range(i + 1, i + 2, i + n))
+ std::swap(vector[step - 1], vector[step]);
+ }
+ }
+}
diff -r bf0ac547b934 -r 900f1dfae46b src/generics/range.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/generics/range.h Tue Mar 06 23:29:40 2018 +0200
@@ -0,0 +1,161 @@
+/*
+ * LDForge: LDraw parts authoring CAD
+ * Copyright (C) 2013 - 2017 Teemu Piippo
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+#pragma once
+
+/*
+ * Range
+ *
+ * This class models a range of values (by default integers but anything with a
+ * total order qualifies) The value type must be constructible with 0 and 1 for
+ * the sake of default values.
+ *
+ * The range may be iterated, in which case the first value yielded will be the
+ * lower bound. Then, the iterator's value is incremented by a certain step
+ * value, yielding the next value. This is continued until the iterator would
+ * yield a value larger than upper bound.
+ *
+ * A range can be constructed in a number of ways:
+ * - Range<>()
+ * default-construction, lower and upper bounds will be set to 0.
+ * - Range<>(first, end)
+ * the range contains the values [first, end - 1].
+ * Iteration of this range yields: first, first + 1, ..., end - 1.
+ * - Range<>(first, second, end)
+ * the range contains the values [first, end - 1].
+ * Iteration of this range yields:
+ * - first
+ * - first + (second - first)
+ * - first + 2*(second - first)
+ * - end - 1 (assuming that (end - 1) can be represented with
+ * (first + n*(second - first)) with some n
+ *
+ * The function range() is provided to avoid the angle brackets for the common
+ * use case where T = int.
+ */
+template
+class Range
+{
+ struct Iterator
+ {
+ const T baseValue;
+ const T stepValue;
+ int stepCount = 0;
+
+ Iterator() :
+ baseValue {0},
+ stepValue {1},
+ stepCount {0} {}
+
+ Iterator(T value, T stepValue, T stepCount) :
+ baseValue {value},
+ stepValue {stepValue},
+ stepCount {stepCount} {}
+
+ T operator*() const
+ {
+ return baseValue + stepCount * stepValue;
+ }
+
+ bool operator!=(const Iterator& other) const
+ {
+ return stepCount != other.stepCount;
+ }
+
+ Iterator& operator++()
+ {
+ stepCount += 1;
+ return *this;
+ }
+
+ Iterator& operator--()
+ {
+ stepCount -= 1;
+ return *this;
+ }
+ };
+
+public:
+ Range() :
+ beginValue {0},
+ endValue {0},
+ step {1} {}
+
+ Range(T first, T second, T last) :
+ beginValue {first},
+ endValue {last + (second - first)},
+ step {second - first} {}
+
+ Iterator begin() const
+ {
+ return {beginValue, step, 0};
+ }
+
+ bool contains(T value) const
+ {
+ return value >= beginValue and value < endValue;
+ }
+
+ Iterator end() const
+ {
+ return {beginValue, step, (endValue - beginValue) / step};
+ }
+
+ bool overlaps(const Range& other) const
+ {
+ return contains(other.beginValue) or contains(other.endValue - 1);
+ }
+
+ bool operator==(const Range& other) const
+ {
+ return beginValue == other.beginValue
+ and endValue == other.endValue
+ and step == other.step;
+ }
+
+ bool operator!=(Range const& other) const
+ {
+ return not operator==(other);
+ }
+
+private:
+ T beginValue;
+ T endValue;
+ T step;
+};
+
+/*
+ * Returns a range from [first, second, ..., last]
+ */
+template
+Range range(T first, T second, T last)
+{
+ return {first, second, last};
+}
+
+/*
+ * Returns a range from [first, first + 1, ..., end - 1]
+ */
+/*
+template
+Range range(T end)
+{
+ return range(0, 1, end - 1);
+}
+*/
diff -r bf0ac547b934 -r 900f1dfae46b src/lddocument.cpp
--- a/src/lddocument.cpp Mon Mar 05 23:59:47 2018 +0200
+++ b/src/lddocument.cpp Tue Mar 06 23:29:40 2018 +0200
@@ -44,10 +44,10 @@
connect(
this,
- &Model::objectsSwapped,
- [&](const QModelIndex& index_1, const QModelIndex& index_2)
+ &Model::rowsMoved,
+ [&](const QModelIndex&, int start, int end, const QModelIndex&, int row)
{
- history()->add(index_1, index_2);
+ history()->add(start, end, row);
}
);
}
diff -r bf0ac547b934 -r 900f1dfae46b src/main.h
--- a/src/main.h Mon Mar 05 23:59:47 2018 +0200
+++ b/src/main.h Tue Mar 06 23:29:40 2018 +0200
@@ -32,5 +32,6 @@
#include "version.h"
#include "format.h"
#include "configuration.h"
+#include "generics/range.h"
extern Configuration* config;
diff -r bf0ac547b934 -r 900f1dfae46b src/model.cpp
--- a/src/model.cpp Mon Mar 05 23:59:47 2018 +0200
+++ b/src/model.cpp Tue Mar 06 23:29:40 2018 +0200
@@ -19,6 +19,7 @@
#include "model.h"
#include "linetypes/modelobject.h"
#include "documentmanager.h"
+#include "generics/migrate.h"
#include "linetypes/comment.h"
#include "linetypes/conditionaledge.h"
#include "linetypes/edgeline.h"
@@ -79,17 +80,21 @@
installObject(row, object);
}
-/*
- * Swaps one object with another, assuming they both are in this model.
- */
-bool Model::swapObjects(const QModelIndex& index_1, const QModelIndex& index_2)
-{
- if (index_1.isValid() and index_2.isValid() and index_1 != index_2)
- {
- qSwap(_objects[index_1.row()], _objects[index_2.row()]);
- emit objectsSwapped(index_1, index_2);
- emit dataChanged(index_1, index_1);
- emit dataChanged(index_2, index_2);
+bool Model::moveRows(
+ const QModelIndex&,
+ int sourceRow,
+ int count,
+ const QModelIndex&,
+ int destinationRow
+) {
+ int sourceRowLast = sourceRow + count - 1;
+
+ if (range(0, 1, size() - count).contains(sourceRow)
+ and range(0, 1, size()).contains(destinationRow)
+ ) {
+ beginMoveRows({}, sourceRow, sourceRowLast, {}, destinationRow);
+ migrate(_objects, sourceRow, sourceRowLast, destinationRow);
+ endMoveRows();
return true;
}
else
diff -r bf0ac547b934 -r 900f1dfae46b src/model.h
--- a/src/model.h Mon Mar 05 23:59:47 2018 +0200
+++ b/src/model.h Tue Mar 06 23:29:40 2018 +0200
@@ -89,7 +89,6 @@
void insertCopy(int position, LDObject* object);
void insertFromArchive(int row, Serializer::Archive& archive);
- bool swapObjects(const QModelIndex& index_1, const QModelIndex& index_2);
bool setObjectAt(int idx, Serializer::Archive& archive);
template T* emplace(Args&& ...args);
template T* emplaceAt(int position, Args&& ...args);
@@ -116,6 +115,14 @@
LDObject* lookup(const QModelIndex& index) const;
QModelIndex indexFromId(qint32 id) const;
+ bool moveRows(
+ const QModelIndex& sourceParent,
+ int sourceRow,
+ int count,
+ const QModelIndex& destinationParent,
+ int destinationChild
+ ) override;
+
int rowCount(const QModelIndex& parent) const override;
QVariant data(const QModelIndex& index, int role) const override;
@@ -123,7 +130,6 @@
void objectAdded(const QModelIndex& object);
void aboutToRemoveObject(const QModelIndex& index);
void objectModified(LDObject* object);
- void objectsSwapped(const QModelIndex& index_1, const QModelIndex& index_2);
protected:
template T* constructObject(Args&& ...args);
diff -r bf0ac547b934 -r 900f1dfae46b src/toolsets/movetoolset.cpp
--- a/src/toolsets/movetoolset.cpp Mon Mar 05 23:59:47 2018 +0200
+++ b/src/toolsets/movetoolset.cpp Tue Mar 06 23:29:40 2018 +0200
@@ -30,33 +30,15 @@
void MoveToolset::moveSelection (bool up)
{
- // TODO: order these!
- QVector objs = selectedObjects().toList().toVector();
-
- if (objs.isEmpty())
- return;
-
- // If we move down, we need to iterate the array in reverse order.
- int start = up ? 0 : (countof(objs) - 1);
- int end = up ? countof(objs) : -1;
- int increment = up ? 1 : -1;
-
- for (int i = start; i != end; i += increment)
+ for (const QItemSelectionRange& selectionRange : m_window->currentSelectionModel()->selection())
{
- LDObject* obj = objs[i];
-
- QModelIndex index = currentDocument()->indexOf(obj);
- int target = index.row() + (up ? -1 : 1);
-
- if ((up and index.row() == 0) or (not up and index.row() == countof(currentDocument()->objects()) - 1))
- {
- // One of the objects hit the extrema. If this happens, this should be the first
- // object to be iterated on. Thus, nothing has changed yet and it's safe to just
- // abort the entire operation.
- return;
- }
-
- currentDocument()->swapObjects(index, currentDocument()->index(target));
+ currentDocument()->moveRows(
+ {},
+ selectionRange.top(),
+ selectionRange.height(),
+ {},
+ up ? (selectionRange.top() - 1) : (selectionRange.bottom() + 2)
+ );
}
}