2019-11-11 20:16:48 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2019-12-11 09:55:45 +00:00
|
|
|
// This header files declares two linear models used by the mobile UI.
|
|
|
|
//
|
|
|
|
// MobileListModel presents a list of trips and optionally the dives of
|
|
|
|
// one expanded trip. It is used for quick navigation through trips.
|
|
|
|
//
|
2020-03-06 11:48:47 +00:00
|
|
|
// MobileSwipeModel gives a linearized view of all dives, sorted by
|
2019-12-11 09:55:45 +00:00
|
|
|
// trip. Even if there is temporal overlap of trips, all dives of
|
|
|
|
// a trip are listed in a contiguous block. This model is used for
|
|
|
|
// swiping through dives.
|
2019-11-11 20:16:48 +00:00
|
|
|
#ifndef MOBILELISTMODEL_H
|
|
|
|
#define MOBILELISTMODEL_H
|
|
|
|
|
|
|
|
#include "divetripmodel.h"
|
|
|
|
|
2019-12-11 09:55:45 +00:00
|
|
|
// This is the base class of the mobile-list model. All it does
|
|
|
|
// is exporting the various dive fields as roles.
|
|
|
|
class MobileListModelBase : public QAbstractItemModel {
|
2019-11-11 20:16:48 +00:00
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
enum Roles {
|
|
|
|
IsTopLevelRole = DiveTripModelBase::LAST_ROLE + 1,
|
|
|
|
DiveDateRole,
|
|
|
|
TripIdRole,
|
|
|
|
TripNrDivesRole,
|
2019-11-12 05:31:56 +00:00
|
|
|
TripShortDateRole,
|
|
|
|
TripTitleRole,
|
2019-11-11 20:16:48 +00:00
|
|
|
DateTimeRole,
|
|
|
|
IdRole,
|
|
|
|
NumberRole,
|
|
|
|
LocationRole,
|
|
|
|
DepthRole,
|
|
|
|
DurationRole,
|
|
|
|
DepthDurationRole,
|
|
|
|
RatingRole,
|
|
|
|
VizRole,
|
|
|
|
SuitRole,
|
|
|
|
AirTempRole,
|
|
|
|
WaterTempRole,
|
|
|
|
SacRole,
|
|
|
|
SumWeightRole,
|
2022-02-12 13:03:18 +00:00
|
|
|
DiveGuideRole,
|
2019-11-11 20:16:48 +00:00
|
|
|
BuddyRole,
|
2020-05-11 14:13:01 +00:00
|
|
|
TagsRole,
|
2019-11-11 20:16:48 +00:00
|
|
|
NotesRole,
|
|
|
|
GpsDecimalRole,
|
|
|
|
GpsRole,
|
|
|
|
NoDiveRole,
|
|
|
|
DiveSiteRole,
|
|
|
|
CylinderRole,
|
|
|
|
GetCylinderRole,
|
|
|
|
CylinderListRole,
|
|
|
|
SingleWeightRole,
|
|
|
|
StartPressureRole,
|
|
|
|
EndPressureRole,
|
|
|
|
FirstGasRole,
|
|
|
|
SelectedRole,
|
2020-02-19 22:05:09 +00:00
|
|
|
DiveInTripRole,
|
2020-02-20 21:22:57 +00:00
|
|
|
TripAbove,
|
|
|
|
TripBelow,
|
2020-02-22 05:31:31 +00:00
|
|
|
TripLocationRole,
|
|
|
|
TripNotesRole,
|
2020-03-20 22:31:46 +00:00
|
|
|
IsInvalidRole
|
2019-11-11 20:16:48 +00:00
|
|
|
};
|
2019-12-11 09:55:45 +00:00
|
|
|
QHash<int, QByteArray> roleNames() const override;
|
|
|
|
protected:
|
|
|
|
DiveTripModelBase *source;
|
|
|
|
MobileListModelBase(DiveTripModelBase *source);
|
|
|
|
private:
|
|
|
|
int columnCount(const QModelIndex &parent) const override;
|
|
|
|
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
|
|
|
|
QModelIndex parent(const QModelIndex &index) const override;
|
|
|
|
};
|
|
|
|
|
|
|
|
class MobileListModel : public MobileListModelBase {
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
2019-11-11 20:16:48 +00:00
|
|
|
MobileListModel(DiveTripModelBase *source);
|
|
|
|
void expand(int row);
|
|
|
|
void unexpand();
|
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
|
|
|
void invalidate();
|
2020-03-06 17:06:31 +00:00
|
|
|
Q_INVOKABLE void toggle(int row);
|
2019-11-11 20:16:48 +00:00
|
|
|
Q_PROPERTY(int shown READ shown NOTIFY shownChanged);
|
|
|
|
signals:
|
|
|
|
void shownChanged();
|
|
|
|
private:
|
|
|
|
struct IndexRange {
|
|
|
|
bool visible;
|
|
|
|
int first, last;
|
|
|
|
};
|
|
|
|
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;
|
|
|
|
int rowCount(const QModelIndex &parent) const override;
|
|
|
|
int shown() const;
|
|
|
|
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2020-03-06 11:48:47 +00:00
|
|
|
class MobileSwipeModel : public MobileListModelBase {
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
MobileSwipeModel(DiveTripModelBase *source);
|
|
|
|
static MobileSwipeModel *instance();
|
|
|
|
void resetModel(DiveTripModelBase::Layout layout); // Switch between tree and list view
|
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
|
|
|
void invalidate();
|
2020-03-06 11:48:47 +00:00
|
|
|
private:
|
|
|
|
struct IndexRange {
|
|
|
|
int first, last;
|
|
|
|
};
|
|
|
|
std::vector<IndexRange> rangeStack;
|
|
|
|
std::vector<int> firstElement; // First element of top level item.
|
|
|
|
int rows;
|
|
|
|
QVariant data(const QModelIndex &index, int role) const override;
|
|
|
|
int rowCount(const QModelIndex &parent) const override;
|
|
|
|
|
|
|
|
// Since accesses to data come in bursts, we cache map-to-source lookup.
|
|
|
|
// Note that this is not thread safe. We suppose that the model is only ever accessed from the UI thread.
|
|
|
|
mutable int cachedRow = -1;
|
|
|
|
mutable QModelIndex cacheSourceParent;
|
|
|
|
mutable int cacheSourceRow = -1;
|
|
|
|
|
2020-03-11 10:30:51 +00:00
|
|
|
// Translate indices from/to source
|
2020-03-06 11:48:47 +00:00
|
|
|
int topLevelRowCountInSource(int sourceRow) const;
|
|
|
|
QModelIndex mapToSource(const QModelIndex &index) const;
|
|
|
|
int mapTopLevelFromSource(int row) const;
|
|
|
|
int mapTopLevelFromSourceForInsert(int row) const;
|
|
|
|
int elementCountInTopLevel(int row) const;
|
|
|
|
int mapRowFromSource(const QModelIndex &parent, int row) const;
|
|
|
|
int mapRowFromSource(const QModelIndex &parent) const;
|
|
|
|
int mapRowFromSourceForInsert(const QModelIndex &parent, int row) const;
|
|
|
|
IndexRange mapRangeFromSource(const QModelIndex &parent, int first, int last) const;
|
|
|
|
void invalidateSourceRowCache() const;
|
|
|
|
void updateSourceRowCache(int row) const;
|
|
|
|
|
|
|
|
// Update elements
|
|
|
|
void initData();
|
|
|
|
int removeTopLevel(int begin, int end);
|
|
|
|
void addTopLevel(int row, std::vector<int> items);
|
|
|
|
void updateTopLevel(int row, int delta);
|
|
|
|
signals:
|
|
|
|
void currentDiveChanged(QModelIndex index);
|
|
|
|
private slots:
|
|
|
|
void doneReset();
|
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2019-12-11 09:37:50 +00:00
|
|
|
// This convenience class provides access to the two mobile models.
|
|
|
|
// Moreover, it provides an interface to the source trip-model.
|
|
|
|
class MobileModels {
|
|
|
|
public:
|
|
|
|
static MobileModels *instance();
|
|
|
|
MobileListModel *listModel();
|
2020-03-06 11:48:47 +00:00
|
|
|
MobileSwipeModel *swipeModel();
|
cleanup: invert control-flow when resetting the core structures
To reset the core data structures, the mobile and desktop UIs
were calling into the dive-list models, which then reset the
core data structures, themselves and the unrelated
locationinformation model. The UI code then reset various other
things, such as the TankInformation model or the map. . This was
unsatisfying from a control-flow perspective, as the models should
display the core data, not act on it. Moreover, this meant lots
of intricate intermodule-dependencies.
Thus, straighten up the control flow: give the C core the
possibility to send a "all data reset" event. And do that
in those functions that reset the core data structures.
Let each module react to this event by itself. This removes
inter-module dependencies. For example, the MainWindow now
doesn't have to reset the TankInfoModel or the MapWidget.
Then, to reset the core data structures, let the UI code
simply directly call the respective core functions.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2020-05-04 22:12:36 +00:00
|
|
|
void invalidate(); // Invalidate all entries to force a re-render.
|
2019-12-11 09:37:50 +00:00
|
|
|
private:
|
|
|
|
MobileModels();
|
|
|
|
DiveTripModelTree source;
|
|
|
|
MobileListModel lm;
|
2020-03-06 11:48:47 +00:00
|
|
|
MobileSwipeModel sm;
|
2019-12-11 09:37:50 +00:00
|
|
|
};
|
2020-03-05 19:00:31 +00:00
|
|
|
|
2019-11-11 20:16:48 +00:00
|
|
|
#endif
|