mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
edab566105
Amend the DiveItem class to avoid float in favour of int. Add getters which return display friendly QStrings reflecting user preferences for (e.g.) depth. Modify DiveTripModel to support controlled alignment by column; right align for depth and duration. Fix problems with utf8 encoding on rating stars, degree symbols and O2 subscript. Signed-off-by: Amit Chaudhuri <amit.k.chaudhuri@gmail.com> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
564 lines
11 KiB
C++
564 lines
11 KiB
C++
/*
|
|
* models.cpp
|
|
*
|
|
* classes for the equipment models of Subsurface
|
|
*
|
|
*/
|
|
#include "models.h"
|
|
#include "../dive.h"
|
|
#include "../divelist.h"
|
|
|
|
#include <QtDebug>
|
|
|
|
extern struct tank_info tank_info[100];
|
|
|
|
CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent)
|
|
{
|
|
}
|
|
|
|
QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
QVariant ret;
|
|
if (orientation == Qt::Vertical) {
|
|
return ret;
|
|
}
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
switch(section) {
|
|
case TYPE:
|
|
ret = tr("Type");
|
|
break;
|
|
case SIZE:
|
|
ret = tr("Size");
|
|
break;
|
|
case MAXPRESS:
|
|
ret = tr("MaxPress");
|
|
break;
|
|
case START:
|
|
ret = tr("Start");
|
|
break;
|
|
case END:
|
|
ret = tr("End");
|
|
break;
|
|
case O2:
|
|
ret = tr("O2%");
|
|
break;
|
|
case HE:
|
|
ret = tr("He%");
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int CylindersModel::columnCount(const QModelIndex& parent) const
|
|
{
|
|
return 7;
|
|
}
|
|
|
|
QVariant CylindersModel::data(const QModelIndex& index, int role) const
|
|
{
|
|
QVariant ret;
|
|
if (!index.isValid() || index.row() >= MAX_CYLINDERS) {
|
|
return ret;
|
|
}
|
|
|
|
dive *d = get_dive(selected_dive);
|
|
cylinder_t& cyl = d->cylinder[index.row()];
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
switch(index.column()) {
|
|
case TYPE:
|
|
ret = QString(cyl.type.description);
|
|
break;
|
|
case SIZE:
|
|
ret = cyl.type.size.mliter;
|
|
break;
|
|
case MAXPRESS:
|
|
ret = cyl.type.workingpressure.mbar;
|
|
break;
|
|
case START:
|
|
ret = cyl.start.mbar;
|
|
break;
|
|
case END:
|
|
ret = cyl.end.mbar;
|
|
break;
|
|
case O2:
|
|
ret = cyl.gasmix.o2.permille;
|
|
break;
|
|
case HE:
|
|
ret = cyl.gasmix.he.permille;
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int CylindersModel::rowCount(const QModelIndex& parent) const
|
|
{
|
|
return usedRows[currentDive];
|
|
}
|
|
|
|
void CylindersModel::add(cylinder_t* cyl)
|
|
{
|
|
if (usedRows[currentDive] >= MAX_CYLINDERS) {
|
|
free(cyl);
|
|
}
|
|
|
|
int row = usedRows[currentDive];
|
|
|
|
cylinder_t& cylinder = currentDive->cylinder[row];
|
|
|
|
cylinder.end.mbar = cyl->end.mbar;
|
|
cylinder.start.mbar = cyl->start.mbar;
|
|
|
|
beginInsertRows(QModelIndex(), row, row);
|
|
usedRows[currentDive]++;
|
|
endInsertRows();
|
|
}
|
|
|
|
void CylindersModel::update()
|
|
{
|
|
if (usedRows[currentDive] > 0) {
|
|
beginRemoveRows(QModelIndex(), 0, usedRows[currentDive]-1);
|
|
endRemoveRows();
|
|
}
|
|
|
|
currentDive = get_dive(selected_dive);
|
|
if (usedRows[currentDive] > 0) {
|
|
beginInsertRows(QModelIndex(), 0, usedRows[currentDive]-1);
|
|
endInsertRows();
|
|
}
|
|
}
|
|
|
|
void CylindersModel::clear()
|
|
{
|
|
if (usedRows[currentDive] > 0) {
|
|
beginRemoveRows(QModelIndex(), 0, usedRows[currentDive]-1);
|
|
usedRows[currentDive] = 0;
|
|
endRemoveRows();
|
|
}
|
|
}
|
|
|
|
void WeightModel::clear()
|
|
{
|
|
}
|
|
|
|
int WeightModel::columnCount(const QModelIndex& parent) const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
QVariant WeightModel::data(const QModelIndex& index, int role) const
|
|
{
|
|
return QVariant();
|
|
}
|
|
|
|
int WeightModel::rowCount(const QModelIndex& parent) const
|
|
{
|
|
return rows;
|
|
}
|
|
|
|
QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
QVariant ret;
|
|
if (orientation == Qt::Vertical) {
|
|
return ret;
|
|
}
|
|
|
|
switch(section) {
|
|
case TYPE:
|
|
ret = tr("Type");
|
|
break;
|
|
case WEIGHT:
|
|
ret = tr("Weight");
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void WeightModel::add(weight_t* weight)
|
|
{
|
|
}
|
|
|
|
void WeightModel::update()
|
|
{
|
|
}
|
|
|
|
void TankInfoModel::add(const QString& description)
|
|
{
|
|
// When the user `creates` a new one on the combobox.
|
|
// for now, empty till dirk cleans the GTK code.
|
|
}
|
|
|
|
void TankInfoModel::clear()
|
|
{
|
|
}
|
|
|
|
int TankInfoModel::columnCount(const QModelIndex& parent) const
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
QVariant TankInfoModel::data(const QModelIndex& index, int role) const
|
|
{
|
|
QVariant ret;
|
|
if (!index.isValid()) {
|
|
return ret;
|
|
}
|
|
struct tank_info *info = &tank_info[index.row()];
|
|
|
|
int ml = info->ml;
|
|
|
|
int bar = ((info->psi) ? psi_to_bar(info->psi) : info->bar) * 1000 + 0.5;
|
|
|
|
if (info->cuft) {
|
|
double airvolume = cuft_to_l(info->cuft) * 1000.0;
|
|
ml = airvolume / bar_to_atm(bar) + 0.5;
|
|
}
|
|
if (role == Qt::DisplayRole) {
|
|
switch(index.column()) {
|
|
case BAR: ret = bar;
|
|
break;
|
|
case ML: ret = ml;
|
|
break;
|
|
case DESCRIPTION:
|
|
ret = QString(info->name);
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
QVariant TankInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
QVariant ret;
|
|
|
|
if (orientation != Qt::Horizontal)
|
|
return ret;
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
switch(section) {
|
|
case BAR:
|
|
ret = tr("Bar");
|
|
break;
|
|
case ML:
|
|
ret = tr("Ml");
|
|
break;
|
|
case DESCRIPTION:
|
|
ret = tr("Description");
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int TankInfoModel::rowCount(const QModelIndex& parent) const
|
|
{
|
|
return rows+1;
|
|
}
|
|
|
|
TankInfoModel::TankInfoModel() : QAbstractTableModel(), rows(-1)
|
|
{
|
|
struct tank_info *info = tank_info;
|
|
for (info = tank_info ; info->name; info++, rows++);
|
|
|
|
if (rows > -1) {
|
|
beginInsertRows(QModelIndex(), 0, rows);
|
|
endInsertRows();
|
|
}
|
|
}
|
|
|
|
void TankInfoModel::update()
|
|
{
|
|
if(rows > -1) {
|
|
beginRemoveRows(QModelIndex(), 0, rows);
|
|
endRemoveRows();
|
|
}
|
|
struct tank_info *info = tank_info;
|
|
for (info = tank_info ; info->name; info++, rows++);
|
|
|
|
if (rows > -1) {
|
|
beginInsertRows(QModelIndex(), 0, rows);
|
|
endInsertRows();
|
|
}
|
|
}
|
|
|
|
/*! A DiveItem for use with a DiveTripModel
|
|
*
|
|
* A simple class which wraps basic stats for a dive (e.g. duration, depth) and
|
|
* tidies up after it's children. This is done manually as we don't inherit from
|
|
* QObject.
|
|
*
|
|
*/
|
|
class DiveItem
|
|
{
|
|
public:
|
|
explicit DiveItem(): number(0), dateTime(QString()), seconds(0), mm(0), location(QString()) { parentItem = 0; }
|
|
explicit DiveItem(int num, QString dt, int, int, QString loc, DiveItem *parent = 0);
|
|
~DiveItem() { qDeleteAll(childlist); }
|
|
|
|
int diveNumber() const { return number; }
|
|
const QString& diveDateTime() const { return dateTime; }
|
|
int diveDuration() const { return seconds; }
|
|
int diveDepth() const { return mm; }
|
|
QString displayDuration() const;
|
|
QString displayDepth() const;
|
|
const QString& diveLocation() const { return location; }
|
|
DiveItem *parent() const { return parentItem; }
|
|
const QList<DiveItem *>& children() const { return childlist; }
|
|
|
|
void addChild(DiveItem* item) {
|
|
item->parentItem = this;
|
|
childlist.push_back(item);
|
|
} /* parent = self */
|
|
|
|
private:
|
|
int number;
|
|
QString dateTime;
|
|
int seconds;
|
|
int mm;
|
|
QString location;
|
|
|
|
DiveItem *parentItem;
|
|
QList <DiveItem*> childlist;
|
|
|
|
};
|
|
|
|
DiveItem::DiveItem(int num, QString dt, int dur, int dep, QString loc, DiveItem *p):
|
|
number(num), dateTime(dt), seconds(dur), mm(dep), location(loc), parentItem(p)
|
|
{
|
|
if (parentItem)
|
|
parentItem->addChild(this);
|
|
}
|
|
|
|
QString DiveItem::displayDepth() const
|
|
{
|
|
const int scale = 1000;
|
|
QString fract, str;
|
|
if (get_units()->length == units::METERS) {
|
|
fract = QString::number((unsigned)(mm % scale) / 10);
|
|
str = QString("%1.%2").arg((unsigned)(mm / scale)).arg(fract);
|
|
}
|
|
if (get_units()->length == units::FEET) {
|
|
str = QString::number(mm_to_feet(mm),'f',2);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
QString DiveItem::displayDuration() const
|
|
{
|
|
int hrs, mins, secs, val;
|
|
const int minutes_hour = 60;
|
|
const int seconds_minute= 60;
|
|
|
|
val = seconds;
|
|
secs = seconds % seconds_minute;
|
|
val /= seconds_minute;
|
|
mins = val % seconds_minute;
|
|
val /= minutes_hour;
|
|
hrs = val % minutes_hour;
|
|
|
|
QString displayTime;
|
|
if (hrs > 0)
|
|
displayTime = QString("%1:%2:%3").arg(hrs).arg(mins).arg(secs);
|
|
else
|
|
displayTime = QString("%1:%2").arg(mins).arg(secs);
|
|
|
|
return displayTime;
|
|
}
|
|
|
|
DiveTripModel::DiveTripModel(QObject *parent) : QAbstractItemModel(parent)
|
|
{
|
|
rootItem = new DiveItem;
|
|
int i;
|
|
struct dive *d;
|
|
|
|
for_each_dive(i, d) {
|
|
struct tm tm;
|
|
char *buffer;
|
|
utc_mkdate(d->when, &tm);
|
|
buffer = get_dive_date_string(&tm);
|
|
new DiveItem(d->number,
|
|
buffer,
|
|
d->duration.seconds,
|
|
d->maxdepth.mm,
|
|
d->location,
|
|
rootItem);
|
|
free(buffer);
|
|
}
|
|
}
|
|
|
|
|
|
Qt::ItemFlags DiveTripModel::flags(const QModelIndex &index) const
|
|
{
|
|
Qt::ItemFlags diveFlags = QAbstractItemModel::flags(index);
|
|
if (index.isValid()) {
|
|
diveFlags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled;
|
|
}
|
|
return diveFlags;
|
|
}
|
|
|
|
|
|
QVariant DiveTripModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid())
|
|
return QVariant();
|
|
|
|
DiveItem *item = static_cast<DiveItem*>(index.internalPointer());
|
|
|
|
QVariant retVal;
|
|
if (role == Qt::TextAlignmentRole) {
|
|
switch (index.column()) {
|
|
case DURATION: /* fall through */
|
|
case DEPTH:
|
|
retVal = Qt::AlignRight;
|
|
break;
|
|
default:
|
|
retVal = Qt::AlignLeft;
|
|
}
|
|
}
|
|
if (role == Qt::DisplayRole) {
|
|
switch (index.column()) {
|
|
case NR:
|
|
retVal = item->diveNumber();
|
|
break;
|
|
case DATE:
|
|
retVal = item->diveDateTime();
|
|
break;
|
|
case DURATION:
|
|
retVal = item->displayDuration();
|
|
//retVal = item->diveDuration();
|
|
break;
|
|
case DEPTH:
|
|
retVal = item->displayDepth();
|
|
//retVal = item->diveDepth();
|
|
break;
|
|
case LOCATION:
|
|
retVal = item->diveLocation();
|
|
break;
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
|
|
QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
QVariant ret;
|
|
if (orientation != Qt::Horizontal)
|
|
return ret;
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
switch(section) {
|
|
case NR:
|
|
ret = tr("#");
|
|
break;
|
|
case DATE:
|
|
ret = tr("Date");
|
|
break;
|
|
case RATING:
|
|
ret = QString::fromUtf8(UTF8_BLACKSTAR);
|
|
break;
|
|
case DEPTH:
|
|
if (get_units()->length == units::METERS)
|
|
ret = tr("Depth (m)");
|
|
else
|
|
ret = tr("Depth (ft)");
|
|
break;
|
|
case DURATION:
|
|
ret = tr("Duration (h:mm:ss)");
|
|
break;
|
|
case TEMPERATURE:
|
|
if (get_units()->temperature == units::CELSIUS)
|
|
ret = QString("%1%2").arg(QString::fromUtf8(UTF8_DEGREE)).arg("C");
|
|
else
|
|
ret = QString("%1%2").arg(QString::fromUtf8(UTF8_DEGREE)).arg("F");
|
|
break;
|
|
case TOTALWEIGHT:
|
|
if (get_units()->weight == units::KG)
|
|
ret = tr("Weight (kg)");
|
|
else
|
|
ret = tr("Weight (lbs)");
|
|
break;
|
|
case SUIT:
|
|
ret = tr("Suit");
|
|
break;
|
|
case CYLINDER:
|
|
ret = tr("Cyl");
|
|
break;
|
|
case NITROX:
|
|
ret = QString("O%1%").arg(QString::fromUtf8(UTF8_SUBSCRIPT_2));
|
|
break;
|
|
case SAC:
|
|
ret = tr("SAC");
|
|
break;
|
|
case OTU:
|
|
ret = tr("OTU");
|
|
break;
|
|
case MAXCNS:
|
|
ret = tr("maxCNS");
|
|
break;
|
|
case LOCATION:
|
|
ret = tr("Location");
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int DiveTripModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
/* only allow kids in column 0 */
|
|
if (parent.isValid() && parent.column() > 0)
|
|
return 0;
|
|
DiveItem *item = itemForIndex(parent);
|
|
return item ? item->children().count() : 0;
|
|
}
|
|
|
|
|
|
|
|
int DiveTripModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
return parent.isValid() && parent.column() != 0 ? 0 : COLUMNS;
|
|
}
|
|
|
|
|
|
QModelIndex DiveTripModel::index(int row, int column, const QModelIndex &parent) const
|
|
{
|
|
|
|
if (!rootItem || row < 0 || column < 0 || column >= COLUMNS ||
|
|
(parent.isValid() && parent.column() != 0))
|
|
return QModelIndex();
|
|
|
|
DiveItem *parentItem = itemForIndex(parent);
|
|
Q_ASSERT(parentItem);
|
|
if (DiveItem *item = parentItem->children().at(row))
|
|
return createIndex(row, column, item);
|
|
return QModelIndex();
|
|
}
|
|
|
|
|
|
QModelIndex DiveTripModel::parent(const QModelIndex &childIndex) const
|
|
{
|
|
if (!childIndex.isValid())
|
|
return QModelIndex();
|
|
|
|
DiveItem *child = static_cast<DiveItem*>(childIndex.internalPointer());
|
|
DiveItem *parent = child->parent();
|
|
|
|
if (parent == rootItem)
|
|
return QModelIndex();
|
|
|
|
return createIndex(parent->children().indexOf(child), 0, parent);
|
|
}
|
|
|
|
|
|
DiveItem* DiveTripModel::itemForIndex(const QModelIndex &index) const
|
|
{
|
|
if (index.isValid()) {
|
|
DiveItem *item = static_cast<DiveItem*>(index.internalPointer());
|
|
return item;
|
|
}
|
|
return rootItem;
|
|
}
|