diff options
-rw-r--r-- | packaging/ios/Subsurface-mobile.pro | 4 | ||||
-rw-r--r-- | qt-models/CMakeLists.txt | 8 | ||||
-rw-r--r-- | qt-models/divetripmodel.cpp | 4 | ||||
-rw-r--r-- | qt-models/divetripmodel.h | 3 | ||||
-rw-r--r-- | qt-models/mobilelistmodel.cpp | 544 | ||||
-rw-r--r-- | qt-models/mobilelistmodel.h | 94 |
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 |