summaryrefslogtreecommitdiffstats
path: root/qt-models/divetripmodel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qt-models/divetripmodel.cpp')
-rw-r--r--qt-models/divetripmodel.cpp725
1 files changed, 437 insertions, 288 deletions
diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp
index 7a76ffbdb..46f0b712e 100644
--- a/qt-models/divetripmodel.cpp
+++ b/qt-models/divetripmodel.cpp
@@ -9,6 +9,10 @@
#include "core/subsurface-qt/DiveListNotifier.h"
#include <QIcon>
#include <QDebug>
+#include <memory>
+#include <algorithm>
+
+// 1) Base functions
static int nitrox_sort_value(const struct dive *dive)
{
@@ -20,33 +24,33 @@ static int nitrox_sort_value(const struct dive *dive)
static QVariant dive_table_alignment(int column)
{
switch (column) {
- case DiveTripModel::DEPTH:
- case DiveTripModel::DURATION:
- case DiveTripModel::TEMPERATURE:
- case DiveTripModel::TOTALWEIGHT:
- case DiveTripModel::SAC:
- case DiveTripModel::OTU:
- case DiveTripModel::MAXCNS:
+ case DiveTripModelBase::DEPTH:
+ case DiveTripModelBase::DURATION:
+ case DiveTripModelBase::TEMPERATURE:
+ case DiveTripModelBase::TOTALWEIGHT:
+ case DiveTripModelBase::SAC:
+ case DiveTripModelBase::OTU:
+ case DiveTripModelBase::MAXCNS:
// Right align numeric columns
return int(Qt::AlignRight | Qt::AlignVCenter);
// NR needs to be left aligned because its the indent marker for trips too
- case DiveTripModel::NR:
- case DiveTripModel::DATE:
- case DiveTripModel::RATING:
- case DiveTripModel::SUIT:
- case DiveTripModel::CYLINDER:
- case DiveTripModel::GAS:
- case DiveTripModel::TAGS:
- case DiveTripModel::PHOTOS:
- case DiveTripModel::COUNTRY:
- case DiveTripModel::BUDDIES:
- case DiveTripModel::LOCATION:
+ case DiveTripModelBase::NR:
+ case DiveTripModelBase::DATE:
+ case DiveTripModelBase::RATING:
+ case DiveTripModelBase::SUIT:
+ case DiveTripModelBase::CYLINDER:
+ case DiveTripModelBase::GAS:
+ case DiveTripModelBase::TAGS:
+ case DiveTripModelBase::PHOTOS:
+ case DiveTripModelBase::COUNTRY:
+ case DiveTripModelBase::BUDDIES:
+ case DiveTripModelBase::LOCATION:
return int(Qt::AlignLeft | Qt::AlignVCenter);
}
return QVariant();
}
-QVariant DiveTripModel::tripData(const dive_trip *trip, int column, int role)
+QVariant DiveTripModelBase::tripData(const dive_trip *trip, int column, int role)
{
if (role == TRIP_ROLE)
@@ -54,7 +58,7 @@ QVariant DiveTripModel::tripData(const dive_trip *trip, int column, int role)
if (role == Qt::DisplayRole) {
switch (column) {
- case DiveTripModel::NR:
+ case DiveTripModelBase::NR:
QString shownText;
bool oneDayTrip = trip_is_single_day(trip);
int countShown = trip_shown_dives(trip);
@@ -128,7 +132,7 @@ static QString displayWeight(const struct dive *d, bool units)
return s + gettextFromC::tr("lbs");
}
-QVariant DiveTripModel::diveData(const struct dive *d, int column, int role)
+QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role)
{
switch (role) {
case Qt::TextAlignmentRole:
@@ -251,89 +255,7 @@ QVariant DiveTripModel::diveData(const struct dive *d, int column, int role)
return QVariant();
}
-DiveTripModel *DiveTripModel::instance()
-{
- static DiveTripModel self;
- return &self;
-}
-
-DiveTripModel::DiveTripModel(QObject *parent) :
- QAbstractItemModel(parent),
- currentLayout(TREE)
-{
- // Stay informed of changes to the divelist
- connect(&diveListNotifier, &DiveListNotifier::divesAdded, this, &DiveTripModel::divesAdded);
- connect(&diveListNotifier, &DiveListNotifier::divesDeleted, this, &DiveTripModel::divesDeleted);
- connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &DiveTripModel::divesChanged);
- connect(&diveListNotifier, &DiveListNotifier::divesMovedBetweenTrips, this, &DiveTripModel::divesMovedBetweenTrips);
- connect(&diveListNotifier, &DiveListNotifier::divesTimeChanged, this, &DiveTripModel::divesTimeChanged);
- connect(&diveListNotifier, &DiveListNotifier::divesSelected, this, &DiveTripModel::divesSelected);
- connect(&diveListNotifier, &DiveListNotifier::divesDeselected, this, &DiveTripModel::divesDeselected);
- connect(&diveListNotifier, &DiveListNotifier::currentDiveChanged, this, &DiveTripModel::currentDiveChanged);
-}
-
-int DiveTripModel::columnCount(const QModelIndex&) const
-{
- return COLUMNS;
-}
-
-int DiveTripModel::rowCount(const QModelIndex &parent) const
-{
- // No parent means top level - return the number of top-level items
- if (!parent.isValid())
- return items.size();
-
- // If the parent has a parent, this is a dive -> no entries
- if (parent.parent().isValid())
- return 0;
-
- // If this is outside of our top-level list -> no entries
- int row = parent.row();
- if (row < 0 || row >= (int)items.size())
- return 0;
-
- // Only trips have items
- const Item &entry = items[parent.row()];
- return entry.d_or_t.trip ? entry.dives.size() : 0;
-}
-
-static const quintptr noParent = ~(quintptr)0; // This is the "internalId" marker for top-level item
-
-QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) const
-{
- if (!hasIndex(row, column, parent))
- return QModelIndex();
-
- // In the "internalId", we store either ~0 no top-level items or the
- // index of the parent item. A top-level item has an invalid parent.
- return createIndex(row, column, parent.isValid() ? parent.row() : noParent);
-}
-
-QModelIndex DiveTripModel::parent(const QModelIndex &index) const
-{
- if (!index.isValid())
- return QModelIndex();
-
- // In the "internalId", we store either ~0 for top-level items
- // or the index of the parent item.
- quintptr id = index.internalId();
- if (id == noParent)
- return QModelIndex();
-
- // Parent must be top-level item
- return createIndex(id, 0, noParent);
-}
-
-Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const
-{
- dive *d = diveOrNull(index);
- Qt::ItemFlags base = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
-
- // Only dives have editable fields and only the number is editable
- return d && index.column() == NR ? base | Qt::ItemIsEditable : base;
-}
-
-QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const
+QVariant DiveTripModelBase::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Vertical)
return QVariant();
@@ -431,33 +353,75 @@ QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int
return QVariant();
}
-DiveTripModel::Item::Item(dive_trip *t, const QVector<dive *> &divesIn) : d_or_t{nullptr, t},
- dives(divesIn.toStdVector())
+static std::unique_ptr<DiveTripModelBase> currentModel;
+DiveTripModelBase *DiveTripModelBase::instance()
{
+ if (!currentModel)
+ resetModel(TREE);
+ return currentModel.get();
}
-DiveTripModel::Item::Item(dive_trip *t, dive *d) : d_or_t{nullptr, t},
- dives({ d })
+void DiveTripModelBase::resetModel(DiveTripModelBase::Layout layout)
{
+ if (layout == TREE)
+ currentModel.reset(new DiveTripModelTree);
+ else
+ currentModel.reset(new DiveTripModelList);
}
-DiveTripModel::Item::Item(dive *d) : d_or_t{d, nullptr}
+DiveTripModelBase::DiveTripModelBase(QObject *parent) : QAbstractItemModel(parent)
{
}
-bool DiveTripModel::Item::isDive(const dive *d) const
+int DiveTripModelBase::columnCount(const QModelIndex&) const
{
- return d_or_t.dive == d;
+ return COLUMNS;
}
-dive *DiveTripModel::Item::getDive() const
+Qt::ItemFlags DiveTripModelBase::flags(const QModelIndex &index) const
{
- return d_or_t.dive;
+ dive *d = diveOrNull(index);
+ Qt::ItemFlags base = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
+
+ // Only dives have editable fields and only the number is editable
+ return d && index.column() == NR ? base | Qt::ItemIsEditable : base;
}
-timestamp_t DiveTripModel::Item::when() const
+bool DiveTripModelBase::setData(const QModelIndex &index, const QVariant &value, int role)
{
- return d_or_t.trip ? trip_date(d_or_t.trip) : d_or_t.dive->when;
+ // We only support setting of data for dives and there, only the number.
+ dive *d = diveOrNull(index);
+ if (!d)
+ return false;
+ if (role != Qt::EditRole)
+ return false;
+ if (index.column() != NR)
+ return false;
+
+ int v = value.toInt();
+ if (v == 0)
+ return false;
+
+ // Only accept numbers that are not already in use by other dives.
+ int i;
+ struct dive *dive;
+ for_each_dive (i, dive) {
+ if (dive->number == v)
+ return false;
+ }
+ d->number = v;
+ mark_divelist_changed(true);
+ return true;
+}
+
+void DiveTripModelBase::divesSelected(dive_trip *trip, const QVector<dive *> &dives)
+{
+ changeDiveSelection(trip, dives, true);
+}
+
+void DiveTripModelBase::divesDeselected(dive_trip *trip, const QVector<dive *> &dives)
+{
+ changeDiveSelection(trip, dives, false);
}
// Find a range of matching elements in a vector.
@@ -547,19 +511,64 @@ void processRangesZip(Vector1 &items1, Vector2 &items2, Predicate cond, Action a
});
}
-void DiveTripModel::setupModelData()
+// Add items from vector "v2" to vector "v1" in batches of contiguous objects.
+// The items are inserted at places according to a sort order determined by "comp".
+// "v1" and "v2" are supposed to be ordered accordingly.
+// TODO: We might use binary search with std::lower_bound(), but not sure if it's worth it.
+// Input parameters:
+// - v1: destination vector
+// - v2: source vector
+// - comp: compare-function, which is fed elements from v2 and v1. returns true for "insert here".
+// - adder: performs the insertion. Perameters: v1, v2, insertion index, from, to range in v2.
+template <typename Vector1, typename Vector2, typename Comparator, typename Inserter>
+void addInBatches(Vector1 &v1, const Vector2 &v2, Comparator comp, Inserter insert)
{
- beginResetModel();
+ int idx = 0; // Index where dives will be inserted
+ int i, j; // Begin and end of range to insert
+ for (i = 0; i < (int)v2.size(); i = j) {
+ for (; idx < (int)v1.size() && !comp(v2[i], v1[idx]); ++idx)
+ ; // Pass
+
+ // We found the index of the first item to add.
+ // Now search how many items we should insert there.
+ if (idx == (int)v1.size()) {
+ // We were at end -> insert the remaining items
+ j = v2.size();
+ } else {
+ for (j = i + 1; j < (int)v2.size() && comp(v2[j], v1[idx]); ++j)
+ ; // Pass
+ }
- items.clear();
+ // Now add the batch
+ insert(v1, v2, idx, i, j);
+
+ // Skip over inserted dives for searching the new insertion position plus one.
+ // If we added at the end, the loop will end anyway.
+ idx += j - i + 1;
+ }
+}
+// 2) TreeModel functions
+
+DiveTripModelTree::DiveTripModelTree(QObject *parent) : DiveTripModelBase(parent)
+{
+ // Stay informed of changes to the divelist
+ connect(&diveListNotifier, &DiveListNotifier::divesAdded, this, &DiveTripModelTree::divesAdded);
+ connect(&diveListNotifier, &DiveListNotifier::divesDeleted, this, &DiveTripModelTree::divesDeleted);
+ connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &DiveTripModelTree::divesChanged);
+ connect(&diveListNotifier, &DiveListNotifier::divesMovedBetweenTrips, this, &DiveTripModelTree::divesMovedBetweenTrips);
+ connect(&diveListNotifier, &DiveListNotifier::divesTimeChanged, this, &DiveTripModelTree::divesTimeChanged);
+ connect(&diveListNotifier, &DiveListNotifier::divesSelected, this, &DiveTripModelTree::divesSelected);
+ connect(&diveListNotifier, &DiveListNotifier::divesDeselected, this, &DiveTripModelTree::divesDeselected);
+ connect(&diveListNotifier, &DiveListNotifier::currentDiveChanged, this, &DiveTripModelTree::currentDiveChanged);
+
+ // Fill model
for (int i = 0; i < dive_table.nr ; ++i) {
dive *d = get_dive(i);
update_cylinder_related_info(d);
dive_trip_t *trip = d->divetrip;
- // If this dive doesn't have a trip or we are in list-mode, add
- // as top-level item.
- if (!trip || currentLayout == LIST) {
+ // If this dive doesn't have a trip, add as top-level item.
+ if (!trip) {
items.emplace_back(d);
continue;
}
@@ -576,17 +585,85 @@ void DiveTripModel::setupModelData()
it->dives.push_back(d);
}
}
+}
+
+int DiveTripModelTree::rowCount(const QModelIndex &parent) const
+{
+ // No parent means top level - return the number of top-level items
+ if (!parent.isValid())
+ return items.size();
+
+ // If the parent has a parent, this is a dive -> no entries
+ if (parent.parent().isValid())
+ return 0;
+
+ // If this is outside of our top-level list -> no entries
+ int row = parent.row();
+ if (row < 0 || row >= (int)items.size())
+ return 0;
- endResetModel();
+ // Only trips have items
+ const Item &entry = items[parent.row()];
+ return entry.d_or_t.trip ? entry.dives.size() : 0;
+}
+
+static const quintptr noParent = ~(quintptr)0; // This is the "internalId" marker for top-level item
+
+QModelIndex DiveTripModelTree::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ // In the "internalId", we store either ~0 for top-level items or the
+ // index of the parent item. A top-level item has an invalid parent.
+ return createIndex(row, column, parent.isValid() ? parent.row() : noParent);
+}
+
+QModelIndex DiveTripModelTree::parent(const QModelIndex &index) const
+{
+ if (!index.isValid())
+ return QModelIndex();
+
+ // In the "internalId", we store either ~0 for top-level items
+ // or the index of the parent item.
+ quintptr id = index.internalId();
+ if (id == noParent)
+ return QModelIndex();
+
+ // Parent must be top-level item
+ return createIndex(id, 0, noParent);
+}
+
+DiveTripModelTree::Item::Item(dive_trip *t, const QVector<dive *> &divesIn) : d_or_t{nullptr, t},
+ dives(divesIn.toStdVector())
+{
+}
+
+DiveTripModelTree::Item::Item(dive_trip *t, dive *d) : d_or_t{nullptr, t},
+ dives({ d })
+{
+}
+
+DiveTripModelTree::Item::Item(dive *d) : d_or_t{d, nullptr}
+{
+}
+
+bool DiveTripModelTree::Item::isDive(const dive *d) const
+{
+ return d_or_t.dive == d;
}
-void DiveTripModel::setLayout(DiveTripModel::Layout layout)
+dive *DiveTripModelTree::Item::getDive() const
{
- currentLayout = layout;
- setupModelData();
+ return d_or_t.dive;
}
-dive_or_trip DiveTripModel::tripOrDive(const QModelIndex &index) const
+timestamp_t DiveTripModelTree::Item::when() const
+{
+ return d_or_t.trip ? trip_date(d_or_t.trip) : d_or_t.dive->when;
+}
+
+dive_or_trip DiveTripModelTree::tripOrDive(const QModelIndex &index) const
{
if (!index.isValid())
return { nullptr, nullptr };
@@ -600,12 +677,12 @@ dive_or_trip DiveTripModel::tripOrDive(const QModelIndex &index) const
return { items[parent.row()].dives[index.row()], nullptr };
}
-dive *DiveTripModel::diveOrNull(const QModelIndex &index) const
+dive *DiveTripModelTree::diveOrNull(const QModelIndex &index) const
{
return tripOrDive(index).dive;
}
-QVariant DiveTripModel::data(const QModelIndex &index, int role) const
+QVariant DiveTripModelTree::data(const QModelIndex &index, int role) const
{
// Set the font for all items alike
if (role == Qt::FontRole)
@@ -620,71 +697,9 @@ QVariant DiveTripModel::data(const QModelIndex &index, int role) const
return QVariant();
}
-bool DiveTripModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- // We only support setting of data for dives and there, only the number.
- dive *d = diveOrNull(index);
- if (!d)
- return false;
- if (role != Qt::EditRole)
- return false;
- if (index.column() != NR)
- return false;
-
- int v = value.toInt();
- if (v == 0)
- return false;
-
- // Only accept numbers that are not already in use by other dives.
- int i;
- struct dive *dive;
- for_each_dive (i, dive) {
- if (dive->number == v)
- return false;
- }
- d->number = v;
- mark_divelist_changed(true);
- return true;
-}
-
-int DiveTripModel::findTripIdx(const dive_trip *trip) const
-{
- for (int i = 0; i < (int)items.size(); ++i)
- if (items[i].d_or_t.trip == trip)
- return i;
- return -1;
-}
-
-int DiveTripModel::findDiveIdx(const dive *d) const
-{
- for (int i = 0; i < (int)items.size(); ++i)
- if (items[i].isDive(d))
- return i;
- return -1;
-}
-
-int DiveTripModel::findDiveInTrip(int tripIdx, const dive *d) const
-{
- const Item &item = items[tripIdx];
- for (int i = 0; i < (int)item.dives.size(); ++i)
- if (item.dives[i] == d)
- return i;
- return -1;
-}
-
-int DiveTripModel::findInsertionIndex(const dive_trip *trip) const
-{
- dive_or_trip d_or_t{ nullptr, (dive_trip *)trip };
- for (int i = 0; i < (int)items.size(); ++i) {
- if (dive_or_trip_less_than(d_or_t, items[i].d_or_t))
- return i;
- }
- return items.size();
-}
-
-// After a top-level item changed (notably a trip), it might
-// need to be reordered. Move the item and send a "data-changed" signal.
-void DiveTripModel::topLevelChanged(int idx)
+// After a trip changed, the top level might need to be reordered.
+// Move the item and send a "data-changed" signal.
+void DiveTripModelTree::topLevelChanged(int idx)
{
if (idx < 0 || idx >= (int)items.size())
return;
@@ -713,44 +728,7 @@ void DiveTripModel::topLevelChanged(int idx)
dataChanged(tripIdx, tripIdx);
}
-// Add items from vector "v2" to vector "v1" in batches of contiguous objects.
-// The items are inserted at places according to a sort order determined by "comp".
-// "v1" and "v2" are supposed to be ordered accordingly.
-// TODO: We might use binary search with std::lower_bound(), but not sure if it's worth it.
-// Input parameters:
-// - v1: destination vector
-// - v2: source vector
-// - comp: compare-function, which is fed elements from v2 and v1. returns true for "insert here".
-// - adder: performs the insertion. Perameters: v1, v2, insertion index, from, to range in v2.
-template <typename Vector1, typename Vector2, typename Comparator, typename Inserter>
-void addInBatches(Vector1 &v1, const Vector2 &v2, Comparator comp, Inserter insert)
-{
- int idx = 0; // Index where dives will be inserted
- int i, j; // Begin and end of range to insert
- for (i = 0; i < (int)v2.size(); i = j) {
- for (; idx < (int)v1.size() && !comp(v2[i], v1[idx]); ++idx)
- ; // Pass
-
- // We found the index of the first item to add.
- // Now search how many items we should insert there.
- if (idx == (int)v1.size()) {
- // We were at end -> insert the remaining items
- j = v2.size();
- } else {
- for (j = i + 1; j < (int)v2.size() && comp(v2[j], v1[idx]); ++j)
- ; // Pass
- }
-
- // Now add the batch
- insert(v1, v2, idx, i, j);
-
- // Skip over inserted dives for searching the new insertion position plus one.
- // If we added at the end, the loop will end anyway.
- idx += j - i + 1;
- }
-}
-
-void DiveTripModel::addDivesToTrip(int trip, const QVector<dive *> &dives)
+void DiveTripModelTree::addDivesToTrip(int trip, const QVector<dive *> &dives)
{
// Construct the parent index, ie. the index of the trip.
QModelIndex parent = createIndex(trip, 0, noParent);
@@ -769,22 +747,56 @@ void DiveTripModel::addDivesToTrip(int trip, const QVector<dive *> &dives)
topLevelChanged(trip);
}
+int DiveTripModelTree::findTripIdx(const dive_trip *trip) const
+{
+ for (int i = 0; i < (int)items.size(); ++i)
+ if (items[i].d_or_t.trip == trip)
+ return i;
+ return -1;
+}
+
+int DiveTripModelTree::findDiveIdx(const dive *d) const
+{
+ for (int i = 0; i < (int)items.size(); ++i)
+ if (items[i].isDive(d))
+ return i;
+ return -1;
+}
+
+int DiveTripModelTree::findDiveInTrip(int tripIdx, const dive *d) const
+{
+ const Item &item = items[tripIdx];
+ for (int i = 0; i < (int)item.dives.size(); ++i)
+ if (item.dives[i] == d)
+ return i;
+ return -1;
+}
+
+int DiveTripModelTree::findInsertionIndex(const dive_trip *trip) const
+{
+ dive_or_trip d_or_t{ nullptr, (dive_trip *)trip };
+ for (int i = 0; i < (int)items.size(); ++i) {
+ if (dive_or_trip_less_than(d_or_t, items[i].d_or_t))
+ return i;
+ }
+ return items.size();
+}
+
// This function is used to compare a dive to an arbitrary entry (dive or trip).
// For comparing two dives, use the core function dive_less_than_entry, which
// effectively sorts by timestamp.
// If comparing to a trip, the policy for equal-times is to place the dives
// before the trip in the case of equal timestamps.
-bool DiveTripModel::dive_before_entry(const dive *d, const Item &entry)
+bool DiveTripModelTree::dive_before_entry(const dive *d, const Item &entry)
{
dive_or_trip d_or_t { (dive *)d, nullptr };
return dive_or_trip_less_than(d_or_t, entry.d_or_t);
}
-void DiveTripModel::divesAdded(dive_trip *trip, bool addTrip, const QVector<dive *> &dives)
+void DiveTripModelTree::divesAdded(dive_trip *trip, bool addTrip, const QVector<dive *> &dives)
{
- if (!trip || currentLayout == LIST) {
- // Either this is outside of a trip or we're in list mode.
- // Thus, add dives at the top-level in batches
+ if (!trip) {
+ // This is outside of a trip. Add dives at the top-level in batches.
addInBatches(items, dives,
&dive_before_entry, // comp
[&](std::vector<Item> &items, const QVector<dive *> &dives, int idx, int from, int to) { // inserter
@@ -805,7 +817,7 @@ void DiveTripModel::divesAdded(dive_trip *trip, bool addTrip, const QVector<dive
if (idx < 0) {
// We don't know the trip - this shouldn't happen. We seem to have
// missed some signals!
- qWarning() << "DiveTripModel::divesAdded(): unknown trip";
+ qWarning() << "DiveTripModelTree::divesAdded(): unknown trip";
return;
}
@@ -814,11 +826,10 @@ void DiveTripModel::divesAdded(dive_trip *trip, bool addTrip, const QVector<dive
}
}
-void DiveTripModel::divesDeleted(dive_trip *trip, bool deleteTrip, const QVector<dive *> &dives)
+void DiveTripModelTree::divesDeleted(dive_trip *trip, bool deleteTrip, const QVector<dive *> &dives)
{
- if (!trip || currentLayout == LIST) {
- // Either this is outside of a trip or we're in list mode.
- // Thus, delete top-level dives. We do this range-wise.
+ if (!trip) {
+ // This is outside of a trip. Delete top-level dives in batches.
processRangesZip(items, dives,
[](const Item &e, dive *d) { return e.getDive() == d; }, // Condition
[&](std::vector<Item> &items, const QVector<dive *> &, int from, int to, int) -> int { // Action
@@ -833,7 +844,7 @@ void DiveTripModel::divesDeleted(dive_trip *trip, bool deleteTrip, const QVector
if (idx < 0) {
// We don't know the trip - this shouldn't happen. We seem to have
// missed some signals!
- qWarning() << "DiveTripModel::divesDeleted(): unknown trip";
+ qWarning() << "DiveTripModelTree::divesDeleted(): unknown trip";
return;
}
@@ -863,11 +874,10 @@ void DiveTripModel::divesDeleted(dive_trip *trip, bool deleteTrip, const QVector
}
}
-void DiveTripModel::divesChanged(dive_trip *trip, const QVector<dive *> &dives)
+void DiveTripModelTree::divesChanged(dive_trip *trip, const QVector<dive *> &dives)
{
- if (!trip || currentLayout == LIST) {
- // Either this is outside of a trip or we're in list mode.
- // Thus, these are top-level dives. We do this range-wise.
+ if (!trip) {
+ // This is outside of a trip. Process top-level items range-wise.
// Since we know that the dive list is sorted, we will only ever search for the first element
// in dives as this must be the first that we encounter. Once we find a range, increase the
@@ -885,13 +895,13 @@ void DiveTripModel::divesChanged(dive_trip *trip, const QVector<dive *> &dives)
if (idx < 0) {
// We don't know the trip - this shouldn't happen. We seem to have
// missed some signals!
- qWarning() << "DiveTripModel::divesChanged(): unknown trip";
+ qWarning() << "DiveTripModelTree::divesChanged(): unknown trip";
return;
}
// Change the dives in the trip. We do this range-wise.
processRangesZip(items[idx].dives, dives,
- [](dive *d1, dive *d2) { return d1 == d2; }, // Condition
+ [](const dive *d1, const dive *d2) { return d1 == d2; }, // Condition (std::equal_to only in C++14)
[&](const std::vector<dive *> &, const QVector<dive *> &, int from, int to, int) -> int { // Action
// TODO: We might be smarter about which columns changed!
dataChanged(createIndex(from, 0, idx), createIndex(to - 1, COLUMNS - 1, idx));
@@ -903,7 +913,7 @@ void DiveTripModel::divesChanged(dive_trip *trip, const QVector<dive *> &dives)
}
}
-QVector<dive *> filterSelectedDives(const QVector<dive *> &dives)
+static QVector<dive *> filterSelectedDives(const QVector<dive *> &dives)
{
QVector<dive *> res;
res.reserve(dives.size());
@@ -913,7 +923,7 @@ QVector<dive *> filterSelectedDives(const QVector<dive *> &dives)
return res;
}
-void DiveTripModel::divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool deleteFrom, bool createTo, const QVector<dive *> &dives)
+void DiveTripModelTree::divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool deleteFrom, bool createTo, const QVector<dive *> &dives)
{
// Move dives between trips. This is an "interesting" problem, as we might
// move from trip to trip, from trip to top-level or from top-level to trip.
@@ -922,9 +932,8 @@ void DiveTripModel::divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool
// functions. This *is* cheating. But let's just try this and see how graceful
// this is handled by Qt and if it gives some ugly UI behavior!
- // But first let's just rule out the trivial cases: same-to-same trip move
- // and list view (in which case we don't care).
- if (from == to || currentLayout == LIST)
+ // But first let's just rule out the trivial case: same-to-same trip move.
+ if (from == to)
return;
// Cheating!
@@ -936,13 +945,13 @@ void DiveTripModel::divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool
divesSelected(to, selectedDives);
}
-void DiveTripModel::divesTimeChanged(dive_trip *trip, timestamp_t delta, const QVector<dive *> &dives)
+void DiveTripModelTree::divesTimeChanged(dive_trip *trip, timestamp_t delta, const QVector<dive *> &dives)
{
// As in the case of divesMovedBetweenTrips(), this is a tricky, but solvable, problem.
// We have to consider the direction (delta < 0 or delta >0) and that dives at their destination
// position have different contiguous batches than at their original position. For now,
// cheat and simply do a remove/add pair. Note that for this to work it is crucial the the
- // order of the dives don't change. This is indeed the case, as all starting-times where
+ // order of the dives don't change. This is indeed the case, as all starting-times were
// moved by the same delta.
// Cheating!
@@ -954,25 +963,15 @@ void DiveTripModel::divesTimeChanged(dive_trip *trip, timestamp_t delta, const Q
divesSelected(trip, selectedDives);
}
-void DiveTripModel::divesSelected(dive_trip *trip, const QVector<dive *> &dives)
-{
- changeDiveSelection(trip, dives, true);
-}
-
-void DiveTripModel::divesDeselected(dive_trip *trip, const QVector<dive *> &dives)
-{
- changeDiveSelection(trip, dives, false);
-}
-
-void DiveTripModel::changeDiveSelection(dive_trip *trip, const QVector<dive *> &dives, bool select)
+void DiveTripModelTree::changeDiveSelection(dive_trip *trip, const QVector<dive *> &dives, bool select)
{
// We got a number of dives that have been selected. Turn this into QModelIndexes and
// emit a signal, so that views can change the selection.
QVector<QModelIndex> indexes;
indexes.reserve(dives.count());
- if (!trip || currentLayout == LIST) {
- // Either this is outside of a trip or we're in list mode.
+ if (!trip) {
+ // This is at the top level.
// Since both lists are sorted, we can do this linearly. Perhaps a binary search
// would be better?
int j = 0; // Index in items array
@@ -989,7 +988,7 @@ void DiveTripModel::changeDiveSelection(dive_trip *trip, const QVector<dive *> &
if (idx < 0) {
// We don't know the trip - this shouldn't happen. We seem to have
// missed some signals!
- qWarning() << "DiveTripModel::divesSelected(): unknown trip";
+ qWarning() << "DiveTripModelTree::changeDiveSelection(): unknown trip";
return;
}
// Locate the indices inside the trip.
@@ -1009,7 +1008,7 @@ void DiveTripModel::changeDiveSelection(dive_trip *trip, const QVector<dive *> &
emit selectionChanged(indexes, select);
}
-void DiveTripModel::currentDiveChanged()
+void DiveTripModelTree::currentDiveChanged()
{
// The current dive has changed. Transform the current dive into an index and pass it on to the view.
if (!current_dive) {
@@ -1018,12 +1017,12 @@ void DiveTripModel::currentDiveChanged()
}
dive_trip *trip = current_dive->divetrip;
- if (!trip || currentLayout == LIST) {
- // Either this is outside of a trip or we're in list mode.
+ if (!trip) {
+ // Outside of a trip - search top-level.
int idx = findDiveIdx(current_dive);
if (idx < 0) {
// We don't know this dive. Something is wrong. Warn and bail.
- qWarning() << "DiveTripModel::currentDiveChanged(): unknown top-level dive";
+ qWarning() << "DiveTripModelTree::currentDiveChanged(): unknown top-level dive";
emit newCurrentDive(QModelIndex());
return;
}
@@ -1032,14 +1031,14 @@ void DiveTripModel::currentDiveChanged()
int idx = findTripIdx(trip);
if (idx < 0) {
// We don't know the trip - this shouldn't happen. Warn and bail.
- qWarning() << "DiveTripModel::currentDiveChanged(): unknown trip";
+ qWarning() << "DiveTripModelTree::currentDiveChanged(): unknown trip";
emit newCurrentDive(QModelIndex());
return;
}
int diveIdx = findDiveInTrip(idx, current_dive);
if (diveIdx < 0) {
// We don't know this dive. Something is wrong. Warn and bail.
- qWarning() << "DiveTripModel::currentDiveChanged(): unknown top-level dive";
+ qWarning() << "DiveTripModelTree::currentDiveChanged(): unknown top-level dive";
emit newCurrentDive(QModelIndex());
return;
}
@@ -1047,18 +1046,175 @@ void DiveTripModel::currentDiveChanged()
}
}
-void DiveTripModel::filterFinished()
+void DiveTripModelTree::filterFinished()
{
// If the filter finished, update all trip items to show the correct number of displayed dives
// in each trip. Without doing this, only trip headers of expanded trips were updated.
- if (currentLayout == LIST)
- return; // No trips in list mode
for (int idx = 0; idx < (int)items.size(); ++idx) {
QModelIndex tripIndex = createIndex(idx, 0, noParent);
dataChanged(tripIndex, tripIndex);
}
}
+bool DiveTripModelTree::lessThan(const QModelIndex &i1, const QModelIndex &i2) const
+{
+ // In tree mode we don't support any sorting!
+ // Simply keep the original position.
+ return i1.row() < i2.row();
+}
+
+// 3) ListModel functions
+
+DiveTripModelList::DiveTripModelList(QObject *parent) : DiveTripModelBase(parent)
+{
+ // Stay informed of changes to the divelist
+ connect(&diveListNotifier, &DiveListNotifier::divesAdded, this, &DiveTripModelList::divesAdded);
+ connect(&diveListNotifier, &DiveListNotifier::divesDeleted, this, &DiveTripModelList::divesDeleted);
+ connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &DiveTripModelList::divesChanged);
+ // Does nothing in list-view
+ //connect(&diveListNotifier, &DiveListNotifier::divesMovedBetweenTrips, this, &DiveTripModelList::divesMovedBetweenTrips);
+ connect(&diveListNotifier, &DiveListNotifier::divesTimeChanged, this, &DiveTripModelList::divesTimeChanged);
+ connect(&diveListNotifier, &DiveListNotifier::divesSelected, this, &DiveTripModelList::divesSelected);
+ connect(&diveListNotifier, &DiveListNotifier::divesDeselected, this, &DiveTripModelList::divesDeselected);
+ connect(&diveListNotifier, &DiveListNotifier::currentDiveChanged, this, &DiveTripModelList::currentDiveChanged);
+
+ // Fill model
+ items.reserve(dive_table.nr);
+ for (int i = 0; i < dive_table.nr ; ++i)
+ items.push_back(get_dive(i));
+}
+
+int DiveTripModelList::rowCount(const QModelIndex &parent) const
+{
+ // In list-mode there is only one level, i.e only top-level
+ // (=invalid parent) has items.
+ return parent.isValid() ? 0 : items.size();
+}
+
+QModelIndex DiveTripModelList::index(int row, int column, const QModelIndex &parent) const
+{
+ if (!hasIndex(row, column, parent))
+ return QModelIndex();
+
+ return createIndex(row, column);
+}
+
+QModelIndex DiveTripModelList::parent(const QModelIndex &index) const
+{
+ // In list-mode there is only one level, i.e. no parent
+ return QModelIndex();
+}
+
+dive *DiveTripModelList::diveOrNull(const QModelIndex &index) const
+{
+ int row = index.row();
+ if (row < 0 || row > (int)items.size())
+ return nullptr;
+ return items[row];
+}
+
+QVariant DiveTripModelList::data(const QModelIndex &index, int role) const
+{
+ // Set the font for all items alike
+ if (role == Qt::FontRole)
+ return defaultModelFont();
+
+ dive *d = diveOrNull(index);
+ return d ? diveData(d, index.column(), role) : QVariant();
+}
+
+void DiveTripModelList::divesAdded(dive_trip *, bool, const QVector<dive *> &dives)
+{
+ addInBatches(items, dives,
+ &dive_less_than, // comp
+ [&](std::vector<dive *> &items, const QVector<dive *> &dives, int idx, int from, int to) { // inserter
+ beginInsertRows(QModelIndex(), idx, idx + to - from - 1);
+ items.insert(items.begin() + idx, dives.begin() + from, dives.begin() + to);
+ endInsertRows();
+ });
+}
+
+void DiveTripModelList::divesDeleted(dive_trip *trip, bool deleteTrip, const QVector<dive *> &dives)
+{
+ processRangesZip(items, dives,
+ [](const dive *d1, const dive *d2) { return d1 == d2; }, // Condition (std::equal_to only in C++14)
+ [&](std::vector<dive *> &items, const QVector<dive *> &, int from, int to, int) -> int { // Action
+ beginRemoveRows(QModelIndex(), from, to - 1);
+ items.erase(items.begin() + from, items.begin() + to);
+ endRemoveRows();
+ return from - to; // Delta: negate the number of items deleted
+ });
+}
+
+void DiveTripModelList::divesChanged(dive_trip *trip, const QVector<dive *> &dives)
+{
+ // Since we know that the dive list is sorted, we will only ever search for the first element
+ // in dives as this must be the first that we encounter. Once we find a range, increase the
+ // index accordingly.
+ processRangesZip(items, dives,
+ [](const dive *d1, const dive *d2) { return d1 == d2; }, // Condition (std::equal_to only in C++14)
+ [&](const std::vector<dive *> &, const QVector<dive *> &, int from, int to, int) -> int { // Action
+ // TODO: We might be smarter about which columns changed!
+ dataChanged(createIndex(from, 0, noParent), createIndex(to - 1, COLUMNS - 1, noParent));
+ return 0; // No items added or deleted
+ });
+}
+
+void DiveTripModelList::divesTimeChanged(dive_trip *trip, timestamp_t delta, const QVector<dive *> &dives)
+{
+ // See comment for DiveTripModelTree::divesTimeChanged above.
+ QVector<dive *> selectedDives = filterSelectedDives(dives);
+ divesDeleted(trip, false, dives);
+ divesAdded(trip, false, dives);
+ divesSelected(trip, selectedDives);
+}
+
+void DiveTripModelList::changeDiveSelection(dive_trip *trip, const QVector<dive *> &dives, bool select)
+{
+ // We got a number of dives that have been selected. Turn this into QModelIndexes and
+ // emit a signal, so that views can change the selection.
+ QVector<QModelIndex> indexes;
+ indexes.reserve(dives.count());
+
+ // Since both lists are sorted, we can do this linearly. Perhaps a binary search
+ // would be better?
+ int j = 0; // Index in items array
+ for (int i = 0; i < dives.size(); ++i) {
+ while (j < (int)items.size() && items[j] != dives[i])
+ ++j;
+ if (j >= (int)items.size())
+ break;
+ indexes.append(createIndex(j, 0, noParent));
+ }
+
+ emit selectionChanged(indexes, select);
+}
+
+void DiveTripModelList::currentDiveChanged()
+{
+ // The current dive has changed. Transform the current dive into an index and pass it on to the view.
+ if (!current_dive) {
+ emit newCurrentDive(QModelIndex()); // No current dive -> tell view to clear current index with an invalid index
+ return;
+ }
+
+ // Either this is outside of a trip or we're in list mode.
+ auto it = std::find(items.begin(), items.end(), current_dive);
+ if (it == items.end()) {
+ // We don't know this dive. Something is wrong. Warn and bail.
+ qWarning() << "DiveTripModelList::currentDiveChanged(): unknown top-level dive";
+ emit newCurrentDive(QModelIndex());
+ return;
+ }
+ emit newCurrentDive(createIndex(it - items.begin(), 0));
+}
+
+void DiveTripModelList::filterFinished()
+{
+ // In list mode, we don't have to change anything after filter finished.
+}
+
+
// Simple sorting helper for sorting against a criterium and if
// that is undefined against a different criterium.
// Return true if diff1 < 0, false if diff1 > 0.
@@ -1077,22 +1233,15 @@ static int strCmp(const char *s1, const char *s2)
return QString::localeAwareCompare(QString(s1), QString(s2)); // TODO: avoid copy
}
-bool DiveTripModel::lessThan(const QModelIndex &i1, const QModelIndex &i2) const
+bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) const
{
- if (currentLayout != LIST) {
- // In tree mode we don't support any sorting!
- // Simply keep the original position.
- return i1.row() < i2.row();
- }
-
// We assume that i1.column() == i2.column().
- // We are in list mode, so we know that we only have dives.
int row1 = i1.row();
int row2 = i2.row();
if (row1 < 0 || row1 >= (int)items.size() || row2 < 0 || row2 >= (int)items.size())
return false;
- const dive *d1 = items[i1.row()].d_or_t.dive;
- const dive *d2 = items[i2.row()].d_or_t.dive;
+ const dive *d1 = items[row1];
+ const dive *d2 = items[row2];
// This is used as a second sort criterion: For equal values, sorting is chronologically *descending*.
int row_diff = row2 - row1;
switch (i1.column()) {