subsurface/qt-models/divetripmodel.cpp
Dirk Hohndel 92d24a2912 Consistently show dive duration based on preferences
We now respect the settings in the preferences and also only show
the duration as minutes and seconds if the dive is a free dive.

Fixes #361
Fixes #362

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2017-05-06 17:43:32 -07:00

587 lines
13 KiB
C++

// SPDX-License-Identifier: GPL-2.0
#include "qt-models/divetripmodel.h"
#include "core/gettextfromc.h"
#include "core/metrics.h"
#include "core/divelist.h"
#include "core/helpers.h"
#include <QIcon>
static int nitrox_sort_value(struct dive *dive)
{
int o2, he, o2max;
get_dive_gas(dive, &o2, &he, &o2max);
return he * 1000 + o2;
}
static QVariant dive_table_alignment(int column)
{
QVariant retVal;
switch (column) {
case DiveTripModel::DEPTH:
case DiveTripModel::DURATION:
case DiveTripModel::TEMPERATURE:
case DiveTripModel::TOTALWEIGHT:
case DiveTripModel::SAC:
case DiveTripModel::OTU:
case DiveTripModel::MAXCNS:
// Right align numeric columns
retVal = int(Qt::AlignRight | Qt::AlignVCenter);
break;
// NR needs to be left aligned becase 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::PHOTOS:
case DiveTripModel::LOCATION:
retVal = int(Qt::AlignLeft | Qt::AlignVCenter);
break;
}
return retVal;
}
QVariant TripItem::data(int column, int role) const
{
QVariant ret;
bool oneDayTrip=true;
if (role == DiveTripModel::TRIP_ROLE)
return QVariant::fromValue<void *>(trip);
if (role == DiveTripModel::SORT_ROLE)
return (qulonglong)trip->when;
if (role == Qt::DisplayRole) {
switch (column) {
case DiveTripModel::NR:
QString shownText;
struct dive *d = trip->dives;
int countShown = 0;
while (d) {
if (!d->hidden_by_filter)
countShown++;
oneDayTrip &= is_same_day (trip->when, d->when);
d = d->next;
}
if (countShown < trip->nrdives)
shownText = tr("(%1 shown)").arg(countShown);
if (trip->location && *trip->location)
ret = QString(trip->location) + ", " + get_trip_date_string(trip->when, trip->nrdives, oneDayTrip) + " "+ shownText;
else
ret = get_trip_date_string(trip->when, trip->nrdives, oneDayTrip) + shownText;
break;
}
}
return ret;
}
QVariant DiveItem::data(int column, int role) const
{
QVariant retVal;
QString icon_names[4] = {":zero",":duringPhoto", ":outsidePhoto", ":inAndOutPhoto" };
struct dive *dive = get_dive_by_uniq_id(diveId);
if (!dive)
return QVariant();
switch (role) {
case Qt::TextAlignmentRole:
retVal = dive_table_alignment(column);
break;
case DiveTripModel::SORT_ROLE:
Q_ASSERT(dive != NULL);
switch (column) {
case NR:
retVal = (qlonglong)dive->when;
break;
case DATE:
retVal = (qlonglong)dive->when;
break;
case RATING:
retVal = dive->rating;
break;
case DEPTH:
retVal = dive->maxdepth.mm;
break;
case DURATION:
retVal = dive->duration.seconds;
break;
case TEMPERATURE:
retVal = dive->watertemp.mkelvin;
break;
case TOTALWEIGHT:
retVal = total_weight(dive);
break;
case SUIT:
retVal = QString(dive->suit);
break;
case CYLINDER:
retVal = QString(dive->cylinder[0].type.description);
break;
case GAS:
retVal = nitrox_sort_value(dive);
break;
case SAC:
retVal = dive->sac;
break;
case OTU:
retVal = dive->otu;
break;
case MAXCNS:
retVal = dive->maxcns;
break;
case PHOTOS:
retVal = countPhotos(dive);
break;
case LOCATION:
retVal = QString(get_dive_location(dive));
break;
}
break;
case Qt::DisplayRole:
Q_ASSERT(dive != NULL);
switch (column) {
case NR:
retVal = dive->number;
break;
case DATE:
retVal = displayDate();
break;
case DEPTH:
retVal = displayDepth();
break;
case DURATION:
retVal = displayDuration();
break;
case TEMPERATURE:
retVal = displayTemperature();
break;
case TOTALWEIGHT:
retVal = displayWeight();
break;
case SUIT:
retVal = QString(dive->suit);
break;
case CYLINDER:
retVal = QString(dive->cylinder[0].type.description);
break;
case SAC:
retVal = displaySac();
break;
case OTU:
retVal = dive->otu;
break;
case MAXCNS:
retVal = dive->maxcns;
break;
case PHOTOS:
break;
case LOCATION:
retVal = QString(get_dive_location(dive));
break;
case GAS:
const char *gas_string = get_dive_gas_string(dive);
retVal = QString(gas_string);
free((void*)gas_string);
break;
}
break;
case Qt::DecorationRole:
switch (column) {
case LOCATION:
if (dive_has_gps_location(dive)) {
IconMetrics im = defaultIconMetrics();
retVal = QIcon(":globe-icon").pixmap(im.sz_small, im.sz_small);
}
break;
case PHOTOS:
if (dive->picture_list)
{
IconMetrics im = defaultIconMetrics();
retVal = QIcon(icon_names[countPhotos(dive)]).pixmap(im.sz_small, im.sz_small);
} // If there are photos, show one of the three photo icons: fish= photos during dive;
break; // sun=photos before/after dive; sun+fish=photos during dive as well as before/after
}
break;
case Qt::ToolTipRole:
switch (column) {
case NR:
retVal = tr("#");
break;
case DATE:
retVal = tr("Date");
break;
case RATING:
retVal = tr("Rating");
break;
case DEPTH:
retVal = tr("Depth(%1)").arg((get_units()->length == units::METERS) ? tr("m") : tr("ft"));
break;
case DURATION:
retVal = tr("Duration");
break;
case TEMPERATURE:
retVal = tr("Temp.(%1%2)").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F");
break;
case TOTALWEIGHT:
retVal = tr("Weight(%1)").arg((get_units()->weight == units::KG) ? tr("kg") : tr("lbs"));
break;
case SUIT:
retVal = tr("Suit");
break;
case CYLINDER:
retVal = tr("Cylinder");
break;
case GAS:
retVal = tr("Gas");
break;
case SAC:
const char *unit;
get_volume_units(0, NULL, &unit);
retVal = tr("SAC(%1)").arg(QString(unit).append(tr("/min")));
break;
case OTU:
retVal = tr("OTU");
break;
case MAXCNS:
retVal = tr("Max. CNS");
break;
case PHOTOS:
retVal = tr("Photos before/during/after dive");
break;
case LOCATION:
retVal = tr("Location");
break;
}
break;
}
if (role == DiveTripModel::STAR_ROLE) {
Q_ASSERT(dive != NULL);
retVal = dive->rating;
}
if (role == DiveTripModel::DIVE_ROLE) {
retVal = QVariant::fromValue<void *>(dive);
}
if (role == DiveTripModel::DIVE_IDX) {
Q_ASSERT(dive != NULL);
retVal = get_divenr(dive);
}
return retVal;
}
Qt::ItemFlags DiveItem::flags(const QModelIndex &index) const
{
if (index.column() == NR) {
return TreeItem::flags(index) | Qt::ItemIsEditable;
}
return TreeItem::flags(index);
}
bool DiveItem::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole)
return false;
if (index.column() != NR)
return false;
int v = value.toInt();
if (v == 0)
return false;
int i;
struct dive *d;
for_each_dive (i, d) {
if (d->number == v)
return false;
}
d = get_dive_by_uniq_id(diveId);
d->number = value.toInt();
mark_divelist_changed(true);
return true;
}
QString DiveItem::displayDate() const
{
struct dive *dive = get_dive_by_uniq_id(diveId);
return get_dive_date_string(dive->when);
}
QString DiveItem::displayDepth() const
{
struct dive *dive = get_dive_by_uniq_id(diveId);
return get_depth_string(dive->maxdepth);
}
QString DiveItem::displayDepthWithUnit() const
{
struct dive *dive = get_dive_by_uniq_id(diveId);
return get_depth_string(dive->maxdepth, true);
}
int DiveItem::countPhotos(dive *dive) const
{ // Determine whether dive has pictures, and whether they were taken during or before/after dive.
const int bufperiod = 120; // A 2-min buffer period. Photos within 2 min of dive are assumed as
int diveDuration = dive->duration.seconds; // taken during the dive, not before/after.
int pic_offset, icon_index = 0;
FOR_EACH_PICTURE (dive) { // Step through each of the pictures for this dive:
if (!picture) break; // if there are no pictures for this dive, return 0
pic_offset = picture->offset.seconds;
if ((pic_offset < -bufperiod) | (pic_offset > diveDuration+bufperiod)) {
icon_index |= 0x02; // If picture is before/after the dive
} // then set the appropriate bit ...
else {
icon_index |= 0x01; // else set the bit for picture during the dive
}
}
return icon_index; // return value: 0=no pictures; 1=pictures during dive;
} // 2=pictures before/after; 3=pictures during as well as before/after
QString DiveItem::displayDuration() const
{
struct dive *dive = get_dive_by_uniq_id(diveId);
return get_dive_duration_string(dive->duration.seconds, ":", "m", "s", dive->dc.divemode == FREEDIVE);
}
QString DiveItem::displayTemperature() const
{
QString str;
struct dive *dive = get_dive_by_uniq_id(diveId);
if (!dive->watertemp.mkelvin)
return str;
if (get_units()->temperature == units::CELSIUS)
str = QString::number(mkelvin_to_C(dive->watertemp.mkelvin), 'f', 1);
else
str = QString::number(mkelvin_to_F(dive->watertemp.mkelvin), 'f', 1);
return str;
}
QString DiveItem::displaySac() const
{
QString str;
struct dive *dive = get_dive_by_uniq_id(diveId);
if (dive->sac) {
const char *unit;
int decimal;
double value = get_volume_units(dive->sac, &decimal, &unit);
return QString::number(value, 'f', decimal);
}
return QString("");
}
QString DiveItem::displayWeight() const
{
QString str = weight_string(weight());
return str;
}
int DiveItem::weight() const
{
struct dive *dive = get_dive_by_uniq_id(diveId);
weight_t tw = { total_weight(dive) };
return tw.grams;
}
DiveTripModel::DiveTripModel(QObject *parent) :
TreeModel(parent),
currentLayout(TREE)
{
columns = COLUMNS;
}
Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
TripItem *item = static_cast<TripItem *>(index.internalPointer());
return item->flags(index);
}
QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const
{
QVariant ret;
if (orientation == Qt::Vertical)
return ret;
switch (role) {
case Qt::TextAlignmentRole:
ret = dive_table_alignment(section);
break;
case Qt::FontRole:
ret = defaultModelFont();
break;
case Qt::DisplayRole:
switch (section) {
case NR:
ret = tr("#");
break;
case DATE:
ret = tr("Date");
break;
case RATING:
ret = tr("Rating");
break;
case DEPTH:
ret = tr("Depth");
break;
case DURATION:
ret = tr("Duration");
break;
case TEMPERATURE:
ret = tr("Temp.");
break;
case TOTALWEIGHT:
ret = tr("Weight");
break;
case SUIT:
ret = tr("Suit");
break;
case CYLINDER:
ret = tr("Cylinder");
break;
case GAS:
ret = tr("Gas");
break;
case SAC:
ret = tr("SAC");
break;
case OTU:
ret = tr("OTU");
break;
case MAXCNS:
ret = tr("Max CNS");
break;
case PHOTOS:
ret = tr("Photos");
break;
case LOCATION:
ret = tr("Location");
break;
}
break;
case Qt::ToolTipRole:
switch (section) {
case NR:
ret = tr("#");
break;
case DATE:
ret = tr("Date");
break;
case RATING:
ret = tr("Rating");
break;
case DEPTH:
ret = tr("Depth(%1)").arg((get_units()->length == units::METERS) ? tr("m") : tr("ft"));
break;
case DURATION:
ret = tr("Duration");
break;
case TEMPERATURE:
ret = tr("Temp.(%1%2)").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F");
break;
case TOTALWEIGHT:
ret = tr("Weight(%1)").arg((get_units()->weight == units::KG) ? tr("kg") : tr("lbs"));
break;
case SUIT:
ret = tr("Suit");
break;
case CYLINDER:
ret = tr("Cylinder");
break;
case GAS:
ret = tr("Gas");
break;
case SAC:
const char *unit;
get_volume_units(0, NULL, &unit);
ret = tr("SAC(%1)").arg(QString(unit).append(tr("/min")));
break;
case OTU:
ret = tr("OTU");
break;
case MAXCNS:
ret = tr("Max CNS");
break;
case PHOTOS:
ret = tr("Photos before/during/after dive");
break;
case LOCATION:
ret = tr("Location");
break;
}
break;
}
return ret;
}
void DiveTripModel::setupModelData()
{
int i = dive_table.nr;
if (rowCount()) {
beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
endRemoveRows();
}
if (autogroup)
autogroup_dives();
dive_table.preexisting = dive_table.nr;
while (--i >= 0) {
struct dive *dive = get_dive(i);
update_cylinder_related_info(dive);
dive_trip_t *trip = dive->divetrip;
DiveItem *diveItem = new DiveItem();
diveItem->diveId = dive->id;
if (!trip || currentLayout == LIST) {
diveItem->parent = rootItem;
rootItem->children.push_back(diveItem);
continue;
}
if (currentLayout == LIST)
continue;
if (!trips.keys().contains(trip)) {
TripItem *tripItem = new TripItem();
tripItem->trip = trip;
tripItem->parent = rootItem;
tripItem->children.push_back(diveItem);
trips[trip] = tripItem;
rootItem->children.push_back(tripItem);
continue;
}
TripItem *tripItem = trips[trip];
tripItem->children.push_back(diveItem);
}
if (rowCount()) {
beginInsertRows(QModelIndex(), 0, rowCount() - 1);
endInsertRows();
}
}
DiveTripModel::Layout DiveTripModel::layout() const
{
return currentLayout;
}
void DiveTripModel::setLayout(DiveTripModel::Layout layout)
{
currentLayout = layout;
setupModelData();
}
bool DiveTripModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
DiveItem *diveItem = dynamic_cast<DiveItem *>(item);
if (!diveItem)
return false;
return diveItem->setData(index, value, role);
}