2017-04-18 15:32:10 +00:00
|
|
|
#include "diveimportedmodel.h"
|
2018-06-03 20:15:19 +00:00
|
|
|
#include "core/qthelper.h"
|
2019-07-15 21:44:39 +00:00
|
|
|
#include "core/divelist.h"
|
2019-11-16 21:24:06 +00:00
|
|
|
#include "commands/command.h"
|
2017-04-18 15:32:10 +00:00
|
|
|
|
|
|
|
DiveImportedModel::DiveImportedModel(QObject *o) : QAbstractTableModel(o),
|
2020-01-09 05:25:02 +00:00
|
|
|
diveTable(empty_dive_table),
|
|
|
|
sitesTable(empty_dive_site_table)
|
2017-04-18 15:32:10 +00:00
|
|
|
{
|
2019-09-25 18:49:13 +00:00
|
|
|
connect(&thread, &QThread::finished, this, &DiveImportedModel::downloadThreadFinished);
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
|
2018-05-21 15:53:42 +00:00
|
|
|
int DiveImportedModel::columnCount(const QModelIndex&) const
|
2017-04-18 15:32:10 +00:00
|
|
|
{
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
2018-05-21 15:53:42 +00:00
|
|
|
int DiveImportedModel::rowCount(const QModelIndex&) const
|
2017-04-18 15:32:10 +00:00
|
|
|
{
|
2019-09-22 20:07:13 +00:00
|
|
|
return diveTable.nr;
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DiveImportedModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
if (orientation == Qt::Vertical)
|
|
|
|
return QVariant();
|
2017-05-26 15:53:25 +00:00
|
|
|
|
|
|
|
// widgets access the model via index.column(), qml via role.
|
|
|
|
int column = section;
|
|
|
|
if (role == DateTime || role == Duration || role == Depth) {
|
|
|
|
column = role - DateTime;
|
|
|
|
role = Qt::DisplayRole;
|
|
|
|
}
|
|
|
|
|
2017-04-18 15:32:10 +00:00
|
|
|
if (role == Qt::DisplayRole) {
|
2017-05-26 15:53:25 +00:00
|
|
|
switch (column) {
|
2017-04-18 15:32:10 +00:00
|
|
|
case 0:
|
|
|
|
return QVariant(tr("Date/time"));
|
|
|
|
case 1:
|
|
|
|
return QVariant(tr("Duration"));
|
|
|
|
case 2:
|
|
|
|
return QVariant(tr("Depth"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DiveImportedModel::data(const QModelIndex &index, int role) const
|
|
|
|
{
|
|
|
|
if (!index.isValid())
|
|
|
|
return QVariant();
|
|
|
|
|
2019-09-22 20:07:13 +00:00
|
|
|
if (index.row() >= diveTable.nr)
|
2017-04-18 15:32:10 +00:00
|
|
|
return QVariant();
|
|
|
|
|
2019-09-22 20:03:49 +00:00
|
|
|
struct dive *d = get_dive_from_table(index.row(), &diveTable);
|
2017-04-18 15:32:10 +00:00
|
|
|
if (!d)
|
|
|
|
return QVariant();
|
2017-05-26 15:53:25 +00:00
|
|
|
|
|
|
|
// widgets access the model via index.column(), qml via role.
|
|
|
|
int column = index.column();
|
2017-06-04 12:40:25 +00:00
|
|
|
if (role >= DateTime) {
|
2017-05-26 15:53:25 +00:00
|
|
|
column = role - DateTime;
|
|
|
|
role = Qt::DisplayRole;
|
|
|
|
}
|
|
|
|
|
2017-04-18 15:32:10 +00:00
|
|
|
if (role == Qt::DisplayRole) {
|
2017-05-26 15:53:25 +00:00
|
|
|
switch (column) {
|
2017-04-18 15:32:10 +00:00
|
|
|
case 0:
|
|
|
|
return QVariant(get_short_dive_date_string(d->when));
|
|
|
|
case 1:
|
2017-06-12 10:08:34 +00:00
|
|
|
return QVariant(get_dive_duration_string(d->duration.seconds, tr("h"), tr("min")));
|
2017-04-18 15:32:10 +00:00
|
|
|
case 2:
|
|
|
|
return QVariant(get_depth_string(d->maxdepth.mm, true, false));
|
2017-06-04 12:40:25 +00:00
|
|
|
case 3:
|
|
|
|
return checkStates[index.row()];
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (role == Qt::CheckStateRole) {
|
|
|
|
if (index.column() == 0)
|
|
|
|
return checkStates[index.row()] ? Qt::Checked : Qt::Unchecked;
|
|
|
|
}
|
|
|
|
return QVariant();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveImportedModel::changeSelected(QModelIndex clickedIndex)
|
|
|
|
{
|
|
|
|
checkStates[clickedIndex.row()] = !checkStates[clickedIndex.row()];
|
2017-06-04 12:40:25 +00:00
|
|
|
dataChanged(index(clickedIndex.row(), 0), index(clickedIndex.row(), 0), QVector<int>() << Qt::CheckStateRole << Selected);
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void DiveImportedModel::selectAll()
|
|
|
|
{
|
2018-12-09 18:10:55 +00:00
|
|
|
std::fill(checkStates.begin(), checkStates.end(), true);
|
2019-09-22 20:07:13 +00:00
|
|
|
dataChanged(index(0, 0), index(diveTable.nr - 1, 0), QVector<int>() << Qt::CheckStateRole << Selected);
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
|
2017-05-29 18:36:00 +00:00
|
|
|
void DiveImportedModel::selectRow(int row)
|
|
|
|
{
|
|
|
|
checkStates[row] = !checkStates[row];
|
2017-06-04 12:40:25 +00:00
|
|
|
dataChanged(index(row, 0), index(row, 0), QVector<int>() << Qt::CheckStateRole << Selected);
|
2017-05-29 18:36:00 +00:00
|
|
|
}
|
|
|
|
|
2017-04-18 15:32:10 +00:00
|
|
|
void DiveImportedModel::selectNone()
|
|
|
|
{
|
2018-12-09 18:10:55 +00:00
|
|
|
std::fill(checkStates.begin(), checkStates.end(), false);
|
2019-09-22 20:07:13 +00:00
|
|
|
dataChanged(index(0, 0), index(diveTable.nr - 1, 0 ), QVector<int>() << Qt::CheckStateRole << Selected);
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Qt::ItemFlags DiveImportedModel::flags(const QModelIndex &index) const
|
|
|
|
{
|
|
|
|
if (index.column() != 0)
|
|
|
|
return QAbstractTableModel::flags(index);
|
|
|
|
return QAbstractTableModel::flags(index) | Qt::ItemIsUserCheckable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveImportedModel::clearTable()
|
|
|
|
{
|
2019-09-22 20:07:13 +00:00
|
|
|
beginResetModel();
|
|
|
|
clear_dive_table(&diveTable);
|
|
|
|
clear_dive_site_table(&sitesTable);
|
|
|
|
endResetModel();
|
2017-04-18 15:32:10 +00:00
|
|
|
}
|
|
|
|
|
2019-09-25 18:49:13 +00:00
|
|
|
void DiveImportedModel::downloadThreadFinished()
|
2017-04-18 15:32:10 +00:00
|
|
|
{
|
2018-12-09 17:56:51 +00:00
|
|
|
beginResetModel();
|
|
|
|
|
2019-09-22 19:48:46 +00:00
|
|
|
// Move the table data from thread to model
|
|
|
|
move_dive_table(&thread.downloadTable, &diveTable);
|
|
|
|
move_dive_site_table(&thread.diveSiteTable, &sitesTable);
|
2020-10-19 17:19:32 +00:00
|
|
|
deviceTable = std::move(thread.deviceTable);
|
2019-09-22 19:48:46 +00:00
|
|
|
|
|
|
|
checkStates.resize(diveTable.nr);
|
2018-12-09 18:10:55 +00:00
|
|
|
std::fill(checkStates.begin(), checkStates.end(), true);
|
2017-05-26 15:53:25 +00:00
|
|
|
|
2018-12-09 17:56:51 +00:00
|
|
|
endResetModel();
|
2019-09-22 19:48:46 +00:00
|
|
|
|
|
|
|
emit downloadFinished();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveImportedModel::startDownload()
|
|
|
|
{
|
|
|
|
thread.start();
|
2017-05-26 15:53:25 +00:00
|
|
|
}
|
|
|
|
|
2020-11-23 16:17:17 +00:00
|
|
|
void DiveImportedModel::waitForDownload()
|
|
|
|
{
|
|
|
|
thread.wait();
|
2020-11-27 20:32:40 +00:00
|
|
|
downloadThreadFinished();
|
2020-11-23 16:17:17 +00:00
|
|
|
}
|
|
|
|
|
2020-10-17 14:07:39 +00:00
|
|
|
std::tuple<struct dive_table, struct dive_site_table, struct device_table> DiveImportedModel::consumeTables()
|
2019-09-22 18:23:37 +00:00
|
|
|
{
|
|
|
|
beginResetModel();
|
|
|
|
|
|
|
|
// Move tables to result
|
2020-01-09 05:25:02 +00:00
|
|
|
struct dive_table dives = empty_dive_table;
|
|
|
|
struct dive_site_table sites = empty_dive_site_table;
|
2020-10-17 14:07:39 +00:00
|
|
|
struct device_table devices;
|
2019-09-22 19:48:46 +00:00
|
|
|
move_dive_table(&diveTable, &dives);
|
|
|
|
move_dive_site_table(&sitesTable, &sites);
|
2020-10-17 14:07:39 +00:00
|
|
|
devices = std::move(deviceTable);
|
2019-09-22 18:23:37 +00:00
|
|
|
|
2020-03-11 10:30:51 +00:00
|
|
|
// Reset indices
|
2019-09-22 18:23:37 +00:00
|
|
|
checkStates.clear();
|
|
|
|
|
|
|
|
endResetModel();
|
|
|
|
|
2020-10-17 14:07:39 +00:00
|
|
|
return std::make_tuple(dives, sites, devices);
|
2019-09-22 18:23:37 +00:00
|
|
|
}
|
|
|
|
|
2019-09-22 19:00:15 +00:00
|
|
|
int DiveImportedModel::numDives() const
|
|
|
|
{
|
2019-09-22 19:48:46 +00:00
|
|
|
return diveTable.nr;
|
2019-09-22 19:00:15 +00:00
|
|
|
}
|
|
|
|
|
2019-09-22 16:50:10 +00:00
|
|
|
// Delete non-selected dives
|
|
|
|
void DiveImportedModel::deleteDeselected()
|
2017-05-28 18:48:30 +00:00
|
|
|
{
|
2019-09-22 19:48:46 +00:00
|
|
|
int total = diveTable.nr;
|
2018-09-28 08:21:23 +00:00
|
|
|
int j = 0;
|
|
|
|
for (int i = 0; i < total; i++) {
|
2019-09-22 16:50:10 +00:00
|
|
|
if (checkStates[i]) {
|
2018-09-28 08:21:23 +00:00
|
|
|
j++;
|
2019-09-22 16:50:10 +00:00
|
|
|
} else {
|
|
|
|
beginRemoveRows(QModelIndex(), j, j);
|
2019-09-22 19:48:46 +00:00
|
|
|
delete_dive_from_table(&diveTable, j);
|
2019-09-22 16:50:10 +00:00
|
|
|
endRemoveRows();
|
|
|
|
}
|
2017-05-28 18:48:30 +00:00
|
|
|
}
|
2019-09-22 19:48:46 +00:00
|
|
|
checkStates.resize(diveTable.nr);
|
2019-09-22 16:50:10 +00:00
|
|
|
std::fill(checkStates.begin(), checkStates.end(), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: this function is only used from mobile - perhaps move it there or unify.
|
2019-11-16 21:24:06 +00:00
|
|
|
void DiveImportedModel::recordDives(int flags)
|
2019-09-22 16:50:10 +00:00
|
|
|
{
|
2019-11-16 21:24:06 +00:00
|
|
|
// delete non-selected dives
|
2019-09-22 20:00:29 +00:00
|
|
|
deleteDeselected();
|
|
|
|
|
2019-11-16 21:24:06 +00:00
|
|
|
// TODO: use structured bindings once we go C++17
|
2020-10-17 14:07:39 +00:00
|
|
|
std::tuple<struct dive_table, struct dive_site_table, struct device_table> tables = consumeTables();
|
|
|
|
if (std::get<0>(tables).nr > 0) {
|
2019-11-16 21:24:06 +00:00
|
|
|
auto data = thread.data();
|
2020-10-17 14:07:39 +00:00
|
|
|
Command::importDives(&std::get<0>(tables), nullptr, &std::get<1>(tables),
|
|
|
|
&std::get<2>(tables), nullptr, flags, data->devName());
|
2019-11-16 21:24:06 +00:00
|
|
|
} else {
|
2020-10-17 14:07:39 +00:00
|
|
|
clear_dive_site_table(&std::get<1>(tables));
|
2019-11-16 21:24:06 +00:00
|
|
|
}
|
|
|
|
// The dives and dive sites have been consumed, but the arrays of the tables
|
|
|
|
// still exist. Free them.
|
2020-10-17 14:07:39 +00:00
|
|
|
free(std::get<0>(tables).dives);
|
|
|
|
free(std::get<1>(tables).dive_sites);
|
2017-05-28 18:48:30 +00:00
|
|
|
}
|
|
|
|
|
2017-05-26 15:53:25 +00:00
|
|
|
QHash<int, QByteArray> DiveImportedModel::roleNames() const {
|
|
|
|
static QHash<int, QByteArray> roles = {
|
|
|
|
{ DateTime, "datetime"},
|
|
|
|
{ Depth, "depth"},
|
2017-05-29 18:36:00 +00:00
|
|
|
{ Duration, "duration"},
|
2017-06-04 12:40:25 +00:00
|
|
|
{ Selected, "selected"}
|
2017-05-29 18:36:00 +00:00
|
|
|
};
|
2017-05-26 15:53:25 +00:00
|
|
|
return roles;
|
|
|
|
}
|