aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packaging/ios/Subsurface-mobile.pro4
-rw-r--r--qt-models/CMakeLists.txt8
-rw-r--r--qt-models/divetripmodel.cpp4
-rw-r--r--qt-models/divetripmodel.h3
-rw-r--r--qt-models/mobilelistmodel.cpp544
-rw-r--r--qt-models/mobilelistmodel.h94
6 files changed, 656 insertions, 1 deletions
diff --git a/packaging/ios/Subsurface-mobile.pro b/packaging/ios/Subsurface-mobile.pro
index 7c6277c85..9739f8ec2 100644
--- a/packaging/ios/Subsurface-mobile.pro
+++ b/packaging/ios/Subsurface-mobile.pro
@@ -128,6 +128,8 @@ SOURCES += ../../subsurface-mobile-main.cpp \
../../qt-models/diveimportedmodel.cpp \
../../qt-models/messagehandlermodel.cpp \
../../qt-models/diveplannermodel.cpp \
+ ../../qt-models/divetripmodel.cpp \
+ ../../qt-models/mobilelistmodel.cpp \
../../qt-models/cylindermodel.cpp \
../../qt-models/cleanertablemodel.cpp \
../../qt-models/tankinfomodel.cpp \
@@ -267,6 +269,8 @@ HEADERS += \
../../qt-models/diveimportedmodel.h \
../../qt-models/messagehandlermodel.h \
../../qt-models/diveplannermodel.h \
+ ../../qt-models/divetripmodel.h \
+ ../../qt-models/mobilelistmodel.h \
../../qt-models/cylindermodel.h \
../../qt-models/cleanertablemodel.h \
../../qt-models/tankinfomodel.h \
diff --git a/qt-models/CMakeLists.txt b/qt-models/CMakeLists.txt
index 8cdeecd25..aa3aa1886 100644
--- a/qt-models/CMakeLists.txt
+++ b/qt-models/CMakeLists.txt
@@ -17,6 +17,8 @@ set(SUBSURFACE_GENERIC_MODELS_LIB_SRCS
diveplannermodel.h
diveplotdatamodel.cpp
diveplotdatamodel.h
+ divetripmodel.cpp
+ divetripmodel.h
maplocationmodel.cpp
maplocationmodel.h
models.cpp
@@ -39,6 +41,10 @@ set(SUBSURFACE_DESKTOP_MODELS_LIB_SRCS
divetripmodel.h
filtermodels.cpp
filtermodels.h
+ models.cpp
+ models.h
+ tankinfomodel.cpp
+ tankinfomodel.h
treemodel.cpp
treemodel.h
weightmodel.cpp
@@ -59,6 +65,8 @@ set(SUBSURFACE_MOBILE_MODELS_LIB_SRCS
gpslistmodel.h
messagehandlermodel.cpp
messagehandlermodel.h
+ mobilelistmodel.cpp
+ mobilelistmodel.h
)
if (SUBSURFACE_TARGET_EXECUTABLE MATCHES "DesktopExecutable")
diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp
index 52bfcf7f5..db3fde694 100644
--- a/qt-models/divetripmodel.cpp
+++ b/qt-models/divetripmodel.cpp
@@ -433,7 +433,11 @@ bool DiveTripModelBase::setData(const QModelIndex &index, const QVariant &value,
if (dive->number == v)
return false;
}
+#if defined(SUBSURFACE_MOBILE)
+ d->number = v;
+#else
Command::editNumber(v, d);
+#endif
return true;
}
diff --git a/qt-models/divetripmodel.h b/qt-models/divetripmodel.h
index a8688baa1..a10bacc15 100644
--- a/qt-models/divetripmodel.h
+++ b/qt-models/divetripmodel.h
@@ -51,7 +51,8 @@ public:
DIVE_IDX,
SELECTED_ROLE,
CURRENT_ROLE,
- TRIP_HAS_CURRENT_ROLE // Returns true if this is a trip and it contains the current dive
+ TRIP_HAS_CURRENT_ROLE, // Returns true if this is a trip and it contains the current dive
+ LAST_ROLE
};
enum Layout {
TREE,
diff --git a/qt-models/mobilelistmodel.cpp b/qt-models/mobilelistmodel.cpp
new file mode 100644
index 000000000..79d4dcdaf
--- /dev/null
+++ b/qt-models/mobilelistmodel.cpp
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "mobilelistmodel.h"
+#include "core/divelist.h" // for shown_dives
+
+MobileListModel::MobileListModel(DiveTripModelBase *sourceIn) : source(sourceIn),
+ expandedRow(-1)
+{
+ connectSignals();
+}
+
+MobileListModel *MobileListModel::instance()
+{
+ static DiveTripModelTree source;
+ static MobileListModel self(&source);
+ return &self;
+}
+
+void MobileListModel::connectSignals()
+{
+ connect(source, &DiveTripModelBase::modelAboutToBeReset, this, &MobileListModel::beginResetModel);
+ connect(source, &DiveTripModelBase::modelReset, this, &MobileListModel::endResetModel);
+ connect(source, &DiveTripModelBase::rowsAboutToBeRemoved, this, &MobileListModel::prepareRemove);
+ connect(source, &DiveTripModelBase::rowsRemoved, this, &MobileListModel::doneRemove);
+ connect(source, &DiveTripModelBase::rowsAboutToBeInserted, this, &MobileListModel::prepareInsert);
+ connect(source, &DiveTripModelBase::rowsInserted, this, &MobileListModel::doneInsert);
+ connect(source, &DiveTripModelBase::rowsAboutToBeMoved, this, &MobileListModel::prepareMove);
+ connect(source, &DiveTripModelBase::rowsMoved, this, &MobileListModel::doneMove);
+ connect(source, &DiveTripModelBase::dataChanged, this, &MobileListModel::changed);
+ connect(&diveListNotifier, &DiveListNotifier::numShownChanged, this, &MobileListModel::shownChanged);
+}
+
+QHash<int, QByteArray> MobileListModel::roleNames() const
+{
+ QHash<int, QByteArray> roles;
+ roles[DiveTripModelBase::IS_TRIP_ROLE] = "isTrip";
+ roles[DiveTripModelBase::CURRENT_ROLE] = "current";
+ roles[IsTopLevelRole] = "isTopLevel";
+ roles[DiveDateRole] = "date";
+ roles[TripIdRole] = "tripId";
+ roles[TripNrDivesRole] = "tripNrDives";
+ roles[DateTimeRole] = "dateTime";
+ roles[IdRole] = "id";
+ roles[NumberRole] = "number";
+ roles[LocationRole] = "location";
+ roles[DepthRole] = "depth";
+ roles[DurationRole] = "duration";
+ roles[DepthDurationRole] = "depthDuration";
+ roles[RatingRole] = "rating";
+ roles[VizRole] = "viz";
+ roles[SuitRole] = "suit";
+ roles[AirTempRole] = "airTemp";
+ roles[WaterTempRole] = "waterTemp";
+ roles[SacRole] = "sac";
+ roles[SumWeightRole] = "sumWeight";
+ roles[DiveMasterRole] = "diveMaster";
+ roles[BuddyRole] = "buddy";
+ roles[NotesRole]= "notes";
+ roles[GpsRole] = "gps";
+ roles[GpsDecimalRole] = "gpsDecimal";
+ roles[NoDiveRole] = "noDive";
+ roles[DiveSiteRole] = "diveSite";
+ roles[CylinderRole] = "cylinder";
+ roles[GetCylinderRole] = "getCylinder";
+ roles[CylinderListRole] = "cylinderList";
+ roles[SingleWeightRole] = "singleWeight";
+ roles[StartPressureRole] = "startPressure";
+ roles[EndPressureRole] = "endPressure";
+ roles[FirstGasRole] = "firstGas";
+ roles[SelectedRole] = "selected";
+ return roles;
+}
+
+int MobileListModel::shown() const
+{
+ return shown_dives;
+}
+
+// We want to show the newest dives first. Therefore, we have to invert
+// the indexes with respect to the source model. To avoid mental gymnastics
+// in the rest of the code, we do this right before sending to the
+// source model and just after recieving from the source model, respectively.
+QModelIndex MobileListModel::sourceIndex(int row, int col, int parentRow) const
+{
+ if (row < 0 || col < 0)
+ return QModelIndex();
+ QModelIndex parent;
+ if (parentRow >= 0) {
+ int numTop = source->rowCount(QModelIndex());
+ parent = source->index(numTop - 1 - parentRow, 0);
+ }
+ int numItems = source->rowCount(parent);
+ return source->index(numItems - 1 - row, col, parent);
+}
+
+int MobileListModel::numSubItems() const
+{
+ if (expandedRow < 0)
+ return 0;
+ return source->rowCount(sourceIndex(expandedRow, 0));
+}
+
+int MobileListModel::invertRow(const QModelIndex &parent, int row) const
+{
+ int numItems = source->rowCount(parent);
+ return numItems - 1 - row;
+}
+
+int MobileListModel::mapRowFromSourceTopLevel(int row) const
+{
+ // This is a top-level item. If it is after the expanded row,
+ // we have to add the items of the expanded row.
+ row = invertRow(QModelIndex(), row);
+ return expandedRow >= 0 && row > expandedRow ? row + numSubItems() : row;
+}
+
+int MobileListModel::mapRowFromSourceTopLevelForInsert(int row) const
+{
+ // This is a top-level item. If it is after the expanded row,
+ // we have to add the items of the expanded row.
+ row = invertRow(QModelIndex(), row) + 1;
+ return expandedRow >= 0 && row > expandedRow ? row + numSubItems() : row;
+}
+
+// The parentRow parameter is the row of the expanded trip converted into
+// local "coordinates" as a premature optimization.
+int MobileListModel::mapRowFromSourceTrip(const QModelIndex &parent, int parentRow, int row) const
+{
+ row = invertRow(parent, row);
+ if (parentRow != expandedRow) {
+ qWarning("MobileListModel::mapRowFromSourceTrip() called on non-extended row");
+ return -1;
+ }
+ return expandedRow + 1 + row; // expandedRow + 1 is the row of the first subitem
+}
+
+int MobileListModel::mapRowFromSource(const QModelIndex &parent, int row) const
+{
+ if (row < 0)
+ return -1;
+
+ if (!parent.isValid()) {
+ return mapRowFromSourceTopLevel(row);
+ } else {
+ int parentRow = invertRow(QModelIndex(), parent.row());
+ return mapRowFromSourceTrip(parent, parentRow, row);
+ }
+}
+
+MobileListModel::IndexRange MobileListModel::mapRangeFromSource(const QModelIndex &parent, int first, int last) const
+{
+ int num = last - first; // Actually, that is num - 1 owing to Qt's bizarre range semantics!
+ // Since we invert the direction, the last will be the first.
+ if (!parent.isValid()) {
+ first = mapRowFromSourceTopLevel(last);
+ // If this includes the extended row, we have to add the subitems
+ if (first <= expandedRow && first + num >= expandedRow)
+ num += numSubItems();
+ return { true, first, first + num };
+ } else {
+ int parentRow = invertRow(QModelIndex(), parent.row());
+ if (parentRow == expandedRow) {
+ first = mapRowFromSourceTrip(parent, parentRow, last);
+ return { true, first, first + num };
+ } else {
+ return { false, -1, -1 };
+ }
+ }
+}
+
+// This is fun: when inserting, we point to the item *before* which we
+// want to insert. But by inverting the direction we turn that into the item
+// *after* which we want to insert. Thus, we have to add one to the range.
+// Moreover, here we have to use the first item. TODO: We can remove this
+// function once the core-model is sorted appropriately.
+MobileListModel::IndexRange MobileListModel::mapRangeFromSourceForInsert(const QModelIndex &parent, int first, int last) const
+{
+ int num = last - first;
+ if (!parent.isValid()) {
+ first = mapRowFromSourceTopLevelForInsert(first);
+ return { true, first, first + num };
+ } else {
+ int parentRow = invertRow(QModelIndex(), parent.row());
+ if (parentRow == expandedRow) {
+ first = mapRowFromSourceTrip(parent, parentRow, first);
+ return { true, first + 1, first + 1 + num };
+ } else {
+ return { false, -1, -1 };
+ }
+ }
+}
+
+QModelIndex MobileListModel::mapFromSource(const QModelIndex &idx) const
+{
+ return createIndex(mapRowFromSource(idx.parent(), idx.row()), idx.column());
+}
+
+QModelIndex MobileListModel::mapToSource(const QModelIndex &idx) const
+{
+ if (!idx.isValid())
+ return idx;
+ int row = idx.row();
+ int col = idx.column();
+ if (expandedRow < 0 || row <= expandedRow)
+ return sourceIndex(row, col);
+
+ int numSub = numSubItems();
+ if (row > expandedRow + numSub)
+ return sourceIndex(row - numSub, col);
+
+ return sourceIndex(row - expandedRow - 1, col, expandedRow);
+}
+
+QModelIndex MobileListModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ return createIndex(row, column);
+}
+
+QModelIndex MobileListModel::parent(const QModelIndex &index) const
+{
+ // This is a flat model - there is no parent
+ return QModelIndex();
+}
+
+int MobileListModel::rowCount(const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ return 0; // There is no parent
+ return source->rowCount() + numSubItems();
+}
+
+int MobileListModel::columnCount(const QModelIndex &parent) const
+{
+ return source->columnCount(parent);
+}
+
+QVariant MobileListModel::data(const QModelIndex &index, int role) const
+{
+ if (role == IsTopLevelRole)
+ return index.row() <= expandedRow || index.row() > expandedRow + numSubItems();
+
+ return source->data(mapToSource(index), role);
+}
+
+void MobileListModel::resetModel()
+{
+ beginResetModel();
+ source->reset();
+ connectSignals();
+ endResetModel();
+}
+
+// Trivial helper to return and erase the last element of a stack
+template<typename T>
+static T pop(std::vector<T> &v)
+{
+ T res = v.back();
+ v.pop_back();
+ return res;
+}
+
+void MobileListModel::prepareRemove(const QModelIndex &parent, int first, int last)
+{
+ IndexRange range = mapRangeFromSource(parent, first, last);
+
+ // Check whether we remove a dive from an expanded trip
+ if (range.visible && parent.isValid()) {
+ for (int i = first; i <= last; ++i) {
+ QModelIndex index = source->index(i, 0, parent);
+ if (source->data(index, DiveTripModelBase::CURRENT_ROLE).value<bool>()) {
+ // Hack alert: we remove the currently selected dive from a visible trip.
+ // Therefore, simply collapse the expanded trip. This is done in prepareRemove(),
+ // i.e. before the base model has actually done any removal and thus things should
+ // be consistent. Then, we can simply pretend that the range was invisible all along,
+ // i.e. the removal is a no-op.
+ unexpand();
+ range.visible = false;
+ break;
+ }
+ }
+ }
+
+ rangeStack.push_back(range);
+ if (range.visible)
+ beginRemoveRows(QModelIndex(), range.first, range.last);
+}
+
+
+void MobileListModel::updateRowAfterRemove(const IndexRange &range, int &row)
+{
+ if (row < 0)
+ return;
+ else if (range.first <= row && range.last >= row)
+ row = -1;
+ else if (range.first <= row)
+ row -= range.last - range.first + 1;
+}
+
+void MobileListModel::doneRemove(const QModelIndex &parent, int first, int last)
+{
+ IndexRange range = pop(rangeStack);
+ if (range.visible) {
+ // Check if we have to move or remove the expanded or current item
+ updateRowAfterRemove(range, expandedRow);
+
+ endRemoveRows();
+ }
+}
+
+void MobileListModel::prepareInsert(const QModelIndex &parent, int first, int last)
+{
+ IndexRange range = mapRangeFromSourceForInsert(parent, first, last);
+ rangeStack.push_back(range);
+ if (range.visible)
+ beginInsertRows(QModelIndex(), range.first, range.last);
+}
+
+void MobileListModel::doneInsert(const QModelIndex &parent, int first, int last)
+{
+ IndexRange range = pop(rangeStack);
+ if (range.visible) {
+ // Check if we have to move the expanded item
+ if (!parent.isValid() && expandedRow >= 0 && range.first <= expandedRow)
+ expandedRow += last - first + 1;
+ endInsertRows();
+ } else {
+ // The range was not visible, thus we inserted into a non-expanded trip.
+ // However, we might have inserted the current item. This means that we
+ // have to expand that trip.
+ // If we inserted a dive that is the current item
+ QModelIndex index = source->index(parent.row(), 0, QModelIndex());
+ if (source->data(index, DiveTripModelBase::TRIP_HAS_CURRENT_ROLE).value<bool>()) {
+ int row = mapRowFromSourceTopLevel(parent.row());
+ expand(row);
+ }
+ }
+
+ if (!parent.isValid()) {
+ // If we inserted a trip that contains the current item, expand that trip
+ for (int i = first; i <= last; ++i) {
+ // Accessing data via the model/view API is annoying.
+ // Perhaps we should simply add a tripHasCurrent(int row) function?
+ QModelIndex index = source->index(i, 0, QModelIndex());
+ if (source->data(index, DiveTripModelBase::TRIP_HAS_CURRENT_ROLE).value<bool>()) {
+ int row = mapRowFromSourceTopLevel(i);
+ expand(row);
+ break;
+ }
+ }
+ }
+}
+
+// Moving rows is annoying, as there are numerous cases to be considered.
+// Some of them degrade to removing or inserting rows.
+void MobileListModel::prepareMove(const QModelIndex &parent, int first, int last, const QModelIndex &dest, int destRow)
+{
+ IndexRange range = mapRangeFromSource(parent, first, last);
+ IndexRange rangeDest = mapRangeFromSourceForInsert(dest, destRow, destRow);
+ rangeStack.push_back(range);
+ rangeStack.push_back(rangeDest);
+ if (!range.visible && !rangeDest.visible)
+ return;
+ if (range.visible && !rangeDest.visible)
+ return prepareRemove(parent, first, last);
+ if (!range.visible && rangeDest.visible)
+ return prepareInsert(parent, first, last);
+ beginMoveRows(QModelIndex(), range.first, range.last, QModelIndex(), rangeDest.first);
+}
+
+void MobileListModel::updateRowAfterMove(const IndexRange &range, const IndexRange &rangeDest, int &row)
+{
+ if (row >= 0 && (rangeDest.first < range.first || rangeDest.first > range.last + 1)) {
+ if (range.first <= row && range.last >= row) {
+ // Case 1: the expanded row is in the moved range
+ if (rangeDest.first <= range.first)
+ row -= range.first - rangeDest.first;
+ else if (rangeDest.first > range.last + 1)
+ row += rangeDest.first - (range.last + 1);
+ } else if (range.first > row && rangeDest.first <= row) {
+ // Case 2: moving things from behind to before the expanded row
+ row += range.last - range.first + 1;
+ } else if (range.first < row && rangeDest.first > row) {
+ // Case 3: moving things from before to behind the expanded row
+ row -= range.last - range.first + 1;
+ }
+ }
+}
+
+void MobileListModel::doneMove(const QModelIndex &parent, int first, int last, const QModelIndex &dest, int destRow)
+{
+ IndexRange rangeDest = pop(rangeStack);
+ IndexRange range = pop(rangeStack);
+ if (!range.visible && !rangeDest.visible)
+ return;
+ if (range.visible && !rangeDest.visible)
+ return doneRemove(parent, first, last);
+ if (!range.visible && rangeDest.visible)
+ return doneInsert(parent, first, last);
+ if (expandedRow >= 0 && (rangeDest.first < range.first || rangeDest.first > range.last + 1)) {
+ if (!parent.isValid() && range.first <= expandedRow && range.last >= expandedRow) {
+ // Case 1: the expanded row is in the moved range
+ // Since we don't support sub-trips, this means that we can't move into another trip
+ if (dest.isValid())
+ qWarning("MobileListModel::doneMove(): moving trips into a subtrip");
+ else if (rangeDest.first <= range.first)
+ expandedRow -= range.first - rangeDest.first;
+ else if (rangeDest.first > range.last + 1)
+ expandedRow += rangeDest.first - (range.last + 1);
+ } else if (range.first > expandedRow && rangeDest.first <= expandedRow) {
+ // Case 2: moving things from behind to before the expanded row
+ expandedRow += range.last - range.first + 1;
+ } else if (range.first < expandedRow && rangeDest.first > expandedRow) {
+ // Case 3: moving things from before to behind the expanded row
+ expandedRow -= range.last - range.first + 1;
+ }
+ }
+ updateRowAfterMove(range, rangeDest, expandedRow);
+ endMoveRows();
+}
+
+void MobileListModel::expand(int row)
+{
+ // First, let us treat the trivial cases: expand an invalid row
+ // or the row is already expanded.
+ if (row < 0) {
+ unexpand();
+ return;
+ }
+ if (row == expandedRow)
+ return;
+
+ // Collapse the old expanded row, if any.
+ if (expandedRow >= 0) {
+ int numSub = numSubItems();
+ if (row > expandedRow) {
+ if (row <= expandedRow + numSub) {
+ qWarning("MobileListModel::expand(): trying to expand row in trip");
+ return;
+ }
+ row -= numSub;
+ }
+ unexpand();
+ }
+
+ int first = row + 1;
+ QModelIndex tripIdx = sourceIndex(row, 0);
+ int numRow = source->rowCount(tripIdx);
+ int last = first + numRow - 1;
+ if (last < first) {
+ // Amazingly, Qt's model API doesn't properly handle empty ranges!
+ expandedRow = row;
+ return;
+ }
+ beginInsertRows(QModelIndex(), first, last);
+ expandedRow = row;
+ endInsertRows();
+}
+
+void MobileListModel::changed(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
+{
+ // We don't support changes beyond levels, sorry.
+ if (topLeft.parent().isValid() != bottomRight.parent().isValid()) {
+ qWarning("MobileListModel::changed(): changes across different levels. Ignoring.");
+ return;
+ }
+
+ // Special case CURRENT_ROLE: if a dive in a collapsed trip becomes current, expand that trip
+ // and if a dive outside of a trip becomes current, collapse any expanded trip.
+ // Note: changes to current must not be combined with other changes, therefore we can
+ // assume that roles.size() == 1.
+ if (roles.size() == 1 && roles[0] == DiveTripModelBase::CURRENT_ROLE &&
+ source->data(topLeft, DiveTripModelBase::CURRENT_ROLE).value<bool>()) {
+ if (topLeft.parent().isValid()) {
+ int parentRow = mapRowFromSourceTopLevel(topLeft.parent().row());
+ if (parentRow != expandedRow) {
+ expand(parentRow);
+ return;
+ }
+ } else {
+ unexpand();
+ }
+ }
+
+ if (topLeft.parent().isValid()) {
+ // This is a range in a trip. First do a sanity check.
+ if (topLeft.parent().row() != bottomRight.parent().row()) {
+ qWarning("MobileListModel::changed(): changes inside different trips. Ignoring.");
+ return;
+ }
+
+ // Now check whether this is expanded
+ IndexRange range = mapRangeFromSource(topLeft.parent(), topLeft.row(), bottomRight.row());
+ if (!range.visible)
+ return;
+
+ dataChanged(createIndex(range.first, topLeft.column()), createIndex(range.last, bottomRight.column()), roles);
+ } else {
+ // This is a top-level range.
+ IndexRange range = mapRangeFromSource(topLeft.parent(), topLeft.row(), bottomRight.row());
+
+ // If the expanded row is outside the region to be updated
+ // or the last entry in the region to be updated, we can simply
+ // forward the signal.
+ if (expandedRow < 0 || expandedRow < range.first || expandedRow >= range.last) {
+ dataChanged(createIndex(range.first, topLeft.column()), createIndex(range.last, bottomRight.column()), roles);
+ return;
+ }
+
+ // We have to split this in two parts: before and including the expanded row
+ // and everything after the expanded row.
+ int numSub = numSubItems();
+ dataChanged(createIndex(range.first, topLeft.column()), createIndex(expandedRow, bottomRight.column()), roles);
+ dataChanged(createIndex(expandedRow + 1 + numSub, topLeft.column()), createIndex(range.last, bottomRight.column()), roles);
+ }
+}
+
+void MobileListModel::unexpand()
+{
+ if (expandedRow < 0)
+ return;
+ int first = expandedRow + 1;
+ int numRows = numSubItems();
+ int last = first + numRows - 1;
+ if (last < first) {
+ // Amazingly, Qt's model API doesn't properly handle empty ranges!
+ expandedRow = -1;
+ return;
+ }
+ beginRemoveRows(QModelIndex(), first, last);
+ expandedRow = -1;
+ endRemoveRows();
+}
+
+void MobileListModel::toggle(int row)
+{
+ if (row < 0)
+ return;
+ else if (row == expandedRow)
+ unexpand();
+ else
+ expand(row);
+}
diff --git a/qt-models/mobilelistmodel.h b/qt-models/mobilelistmodel.h
new file mode 100644
index 000000000..92a12960d
--- /dev/null
+++ b/qt-models/mobilelistmodel.h
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef MOBILELISTMODEL_H
+#define MOBILELISTMODEL_H
+
+#include "divetripmodel.h"
+
+class MobileListModel : public QAbstractItemModel {
+ Q_OBJECT
+public:
+ enum Roles {
+ IsTopLevelRole = DiveTripModelBase::LAST_ROLE + 1,
+ DiveDateRole,
+ TripIdRole,
+ TripNrDivesRole,
+ DateTimeRole,
+ IdRole,
+ NumberRole,
+ LocationRole,
+ DepthRole,
+ DurationRole,
+ DepthDurationRole,
+ RatingRole,
+ VizRole,
+ SuitRole,
+ AirTempRole,
+ WaterTempRole,
+ SacRole,
+ SumWeightRole,
+ DiveMasterRole,
+ BuddyRole,
+ NotesRole,
+ GpsDecimalRole,
+ GpsRole,
+ NoDiveRole,
+ DiveSiteRole,
+ CylinderRole,
+ GetCylinderRole,
+ CylinderListRole,
+ SingleWeightRole,
+ StartPressureRole,
+ EndPressureRole,
+ FirstGasRole,
+ SelectedRole,
+ };
+ MobileListModel(DiveTripModelBase *source);
+ static MobileListModel *instance();
+ void resetModel();
+ void expand(int row);
+ void unexpand();
+ void toggle(int row);
+ Q_PROPERTY(int shown READ shown NOTIFY shownChanged);
+signals:
+ void shownChanged();
+private:
+ struct IndexRange {
+ bool visible;
+ int first, last;
+ };
+ void connectSignals();
+ std::vector<IndexRange> rangeStack;
+ QModelIndex sourceIndex(int row, int col, int parentRow = -1) const;
+ int numSubItems() const;
+ int mapRowFromSourceTopLevel(int row) const;
+ int mapRowFromSourceTopLevelForInsert(int row) const;
+ int mapRowFromSourceTrip(const QModelIndex &parent, int parentRow, int row) const;
+ int mapRowFromSource(const QModelIndex &parent, int row) const;
+ int invertRow(const QModelIndex &parent, int row) const;
+ IndexRange mapRangeFromSource(const QModelIndex &parent, int first, int last) const;
+ IndexRange mapRangeFromSourceForInsert(const QModelIndex &parent, int first, int last) const;
+ QModelIndex mapFromSource(const QModelIndex &idx) const;
+ QModelIndex mapToSource(const QModelIndex &idx) const;
+ static void updateRowAfterRemove(const IndexRange &range, int &row);
+ static void updateRowAfterMove(const IndexRange &range, const IndexRange &dest, int &row);
+ QVariant data(const QModelIndex &index, int role) const override;
+ QModelIndex index(int row, int column, const QModelIndex &parent) const override;
+ QModelIndex parent(const QModelIndex &index) const override;
+ int rowCount(const QModelIndex &parent) const override;
+ int columnCount(const QModelIndex &parent) const override;
+ QHash<int, QByteArray> roleNames() const override;
+ int shown() const;
+
+ DiveTripModelBase *source;
+ int expandedRow;
+private slots:
+ void prepareRemove(const QModelIndex &parent, int first, int last);
+ void doneRemove(const QModelIndex &parent, int first, int last);
+ void prepareInsert(const QModelIndex &parent, int first, int last);
+ void doneInsert(const QModelIndex &parent, int first, int last);
+ void prepareMove(const QModelIndex &parent, int first, int last, const QModelIndex &dest, int destRow);
+ void doneMove(const QModelIndex &parent, int first, int last, const QModelIndex &dest, int destRow);
+ void changed(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles);
+};
+
+#endif