2013-04-14 03:44:02 +00:00
|
|
|
/*
|
|
|
|
* models.cpp
|
|
|
|
*
|
|
|
|
* classes for the equipment models of Subsurface
|
|
|
|
*
|
|
|
|
*/
|
2013-04-13 13:17:59 +00:00
|
|
|
#include "models.h"
|
2013-05-21 19:03:05 +00:00
|
|
|
#include "../helpers.h"
|
2013-05-22 17:02:28 +00:00
|
|
|
#include "../dive.h"
|
2013-06-07 14:43:45 +00:00
|
|
|
#include "../device.h"
|
2013-05-02 02:51:34 +00:00
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QDebug>
|
2013-05-02 21:19:15 +00:00
|
|
|
#include <QColor>
|
|
|
|
#include <QBrush>
|
2013-05-19 13:46:49 +00:00
|
|
|
#include <QFont>
|
2013-05-22 14:00:20 +00:00
|
|
|
#include <QIcon>
|
2013-04-24 15:57:30 +00:00
|
|
|
|
2013-05-21 12:33:55 +00:00
|
|
|
CylindersModel::CylindersModel(QObject* parent): QAbstractTableModel(parent), current(0), rows(0)
|
2013-04-13 13:17:59 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant CylindersModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
2013-05-26 03:04:31 +00:00
|
|
|
QFont font;
|
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
if (orientation == Qt::Vertical)
|
2013-04-13 13:17:59 +00:00
|
|
|
return ret;
|
|
|
|
|
2013-05-26 03:04:31 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::FontRole:
|
|
|
|
font.setPointSizeF(font.pointSizeF() * 0.8);
|
|
|
|
return font;
|
|
|
|
case Qt::DisplayRole:
|
2013-04-13 13:17:59 +00:00
|
|
|
switch(section) {
|
2013-05-29 20:43:14 +00:00
|
|
|
case TYPE: ret = tr("Type"); break;
|
|
|
|
case SIZE: ret = tr("Size"); break;
|
|
|
|
case WORKINGPRESS: ret = tr("WorkPress"); break;
|
|
|
|
case START: ret = tr("StartPress"); break;
|
|
|
|
case END: ret = tr("EndPress "); break;
|
|
|
|
case O2: ret = tr("O2% "); break;
|
|
|
|
case HE: ret = tr("He% "); break;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CylindersModel::columnCount(const QModelIndex& parent) const
|
|
|
|
{
|
2013-05-22 14:00:20 +00:00
|
|
|
return COLUMNS;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant CylindersModel::data(const QModelIndex& index, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
2013-05-26 03:04:31 +00:00
|
|
|
QFont font;
|
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
if (!index.isValid() || index.row() >= MAX_CYLINDERS)
|
2013-04-13 13:17:59 +00:00
|
|
|
return ret;
|
2013-05-02 05:00:08 +00:00
|
|
|
|
2013-05-21 19:03:05 +00:00
|
|
|
cylinder_t *cyl = ¤t->cylinder[index.row()];
|
2013-05-26 03:04:31 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::FontRole:
|
|
|
|
font.setPointSizeF(font.pointSizeF() * 0.80);
|
|
|
|
ret = font;
|
|
|
|
break;
|
|
|
|
case Qt::TextAlignmentRole:
|
|
|
|
ret = Qt::AlignRight;
|
|
|
|
break;
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
case Qt::EditRole:
|
2013-04-13 13:17:59 +00:00
|
|
|
switch(index.column()) {
|
|
|
|
case TYPE:
|
2013-05-21 19:03:05 +00:00
|
|
|
ret = QString(cyl->type.description);
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
|
|
|
case SIZE:
|
2013-05-21 19:03:05 +00:00
|
|
|
// we can't use get_volume_string because the idiotic imperial tank
|
|
|
|
// sizes take working pressure into account...
|
|
|
|
if (cyl->type.size.mliter) {
|
|
|
|
if (prefs.units.volume == prefs.units.CUFT) {
|
2013-05-22 20:29:17 +00:00
|
|
|
int cuft = 0.5 + ml_to_cuft(gas_volume(cyl, cyl->type.workingpressure));
|
2013-05-21 19:03:05 +00:00
|
|
|
ret = QString("%1cuft").arg(cuft);
|
|
|
|
} else {
|
|
|
|
ret = QString("%1l").arg(cyl->type.size.mliter / 1000.0, 0, 'f', 1);
|
|
|
|
}
|
|
|
|
}
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
2013-05-22 20:29:17 +00:00
|
|
|
case WORKINGPRESS:
|
2013-05-21 19:03:05 +00:00
|
|
|
if (cyl->type.workingpressure.mbar)
|
|
|
|
ret = get_pressure_string(cyl->type.workingpressure, TRUE);
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
|
|
|
case START:
|
2013-05-21 19:03:05 +00:00
|
|
|
if (cyl->start.mbar)
|
|
|
|
ret = get_pressure_string(cyl->start, TRUE);
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
|
|
|
case END:
|
2013-05-21 19:03:05 +00:00
|
|
|
if (cyl->end.mbar)
|
|
|
|
ret = get_pressure_string(cyl->end, TRUE );
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
|
|
|
case O2:
|
2013-05-21 19:03:05 +00:00
|
|
|
ret = QString("%1%").arg((cyl->gasmix.o2.permille + 5) / 10);
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
|
|
|
case HE:
|
2013-05-21 19:03:05 +00:00
|
|
|
ret = QString("%1%").arg((cyl->gasmix.he.permille + 5) / 10);
|
2013-04-13 13:17:59 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-05-26 03:04:31 +00:00
|
|
|
break;
|
|
|
|
case Qt::DecorationRole:
|
2013-05-23 04:25:05 +00:00
|
|
|
if (index.column() == REMOVE)
|
2013-05-22 14:00:20 +00:00
|
|
|
ret = QIcon(":trash");
|
2013-05-26 03:04:31 +00:00
|
|
|
break;
|
2013-05-22 14:00:20 +00:00
|
|
|
}
|
2013-04-13 13:17:59 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-05-23 20:31:46 +00:00
|
|
|
// this is our magic 'pass data in' function that allows the delegate to get
|
|
|
|
// the data here without silly unit conversions;
|
|
|
|
// so we only implement the two columns we care about
|
|
|
|
void CylindersModel::passInData(const QModelIndex& index, const QVariant& value)
|
|
|
|
{
|
|
|
|
cylinder_t *cyl = ¤t->cylinder[index.row()];
|
|
|
|
switch(index.column()) {
|
|
|
|
case SIZE:
|
|
|
|
if (cyl->type.size.mliter != value.toInt()) {
|
|
|
|
cyl->type.size.mliter = value.toInt();
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WORKINGPRESS:
|
|
|
|
if (cyl->type.workingpressure.mbar != value.toInt()) {
|
|
|
|
cyl->type.workingpressure.mbar = value.toInt();
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 20:29:17 +00:00
|
|
|
#define CHANGED(_t,_u1,_u2) value._t() != data(index, role).toString().replace(_u1,"").replace(_u2,"")._t()
|
|
|
|
|
2013-05-22 15:20:00 +00:00
|
|
|
bool CylindersModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
|
|
{
|
|
|
|
cylinder_t *cyl = ¤t->cylinder[index.row()];
|
2013-05-22 20:29:17 +00:00
|
|
|
switch(index.column()) {
|
|
|
|
case TYPE:
|
|
|
|
if (!value.isNull()) {
|
Fix usage of temporary QByteArrays
This commit fixes three different things:
- a memory leak in WeightModel::setData()
- getSetting() calling strdup() on a QByteArray
- a possible usage of memory after deallocation
Here's an explanation of the last issue (taken from the mailing list, slightly
adapted):
toByteArray(), as well as others "toSomething()" methods, returns
a new object which the compiler allocates on the stack. The compiler
will consider it a temporary data, and destroy it on the next line. So,
when one does
char *text= value.toByteArray().data(); // line 1
if (strcmp(description, text)) { // line 2
the compiler creates a QByteArray on line 1, calls ::data() on it, which
returns a valid char *, and assigns its value to "text". So far, so
good. But before jumping to line 2, the compiler destroys the temporary
QByteArray, and this will in turn invoke the QByteArray destructor,
which will destroy the internal data. The result is that on line 2,
"text" will point to some memory which has already been freed.
One solution is to store a copy of the temporary QByteArray into a local
variable: the compiler will still destroy the temporary QByteArray it created,
but (thanks to the reference-counted data sharing built in QByteArray) now the
destructor will see that the data is referenced by another instance of
QByteArray (the local variable "ba") and will not free the internal data.
In this way, the internal data will be available until the local variable is
destroyed, which will happen at the end of the {} block where it is defined.
Please note that when one uses the data in the same line, one doesn't need to
worry about this issue. In fact,
text = strdup(value.toString().toUtf8().data());
works just fine.
Signed-off-by: Alberto Mardegan <mardy@users.sourceforge.net>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-05-24 11:39:37 +00:00
|
|
|
QByteArray ba = value.toByteArray();
|
|
|
|
const char *text = ba.constData();
|
2013-05-22 23:22:21 +00:00
|
|
|
if (!cyl->type.description || strcmp(cyl->type.description, text)) {
|
2013-05-22 20:29:17 +00:00
|
|
|
cyl->type.description = strdup(text);
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
2013-05-22 15:20:00 +00:00
|
|
|
}
|
2013-05-22 20:29:17 +00:00
|
|
|
break;
|
|
|
|
case SIZE:
|
|
|
|
if (CHANGED(toDouble, "cuft", "l")) {
|
|
|
|
// if units are CUFT then this value is meaningless until we have working pressure
|
|
|
|
if (value.toDouble() != 0.0) {
|
2013-05-24 05:56:12 +00:00
|
|
|
TankInfoModel *tanks = TankInfoModel::instance();
|
|
|
|
QModelIndexList matches = tanks->match(tanks->index(0,0), Qt::DisplayRole, cyl->type.description);
|
2013-05-22 15:20:00 +00:00
|
|
|
if (prefs.units.volume == prefs.units.CUFT) {
|
2013-05-24 05:56:12 +00:00
|
|
|
if (cyl->type.workingpressure.mbar == 0) {
|
2013-05-22 20:29:17 +00:00
|
|
|
// this is a hack as we can't store a wet size
|
|
|
|
// without working pressure in cuft mode
|
|
|
|
// so we assume it's an aluminum tank at 3000psi
|
|
|
|
cyl->type.workingpressure.mbar = psi_to_mbar(3000);
|
2013-05-24 05:56:12 +00:00
|
|
|
if (!matches.isEmpty())
|
|
|
|
tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl->type.workingpressure.mbar / 1000.0);
|
|
|
|
}
|
2013-05-22 20:29:17 +00:00
|
|
|
if (cyl->type.size.mliter != wet_volume(value.toDouble(), cyl->type.workingpressure)) {
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
cyl->type.size.mliter = wet_volume(value.toDouble(), cyl->type.workingpressure);
|
2013-05-24 05:56:12 +00:00
|
|
|
if (!matches.isEmpty())
|
|
|
|
tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter);
|
2013-05-22 20:29:17 +00:00
|
|
|
}
|
2013-05-22 15:20:00 +00:00
|
|
|
} else {
|
2013-05-22 20:29:17 +00:00
|
|
|
if (cyl->type.size.mliter != value.toDouble() * 1000.0) {
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
cyl->type.size.mliter = value.toDouble() * 1000.0;
|
2013-05-24 05:56:12 +00:00
|
|
|
if (!matches.isEmpty())
|
|
|
|
tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter);
|
2013-05-22 20:29:17 +00:00
|
|
|
}
|
2013-05-22 15:20:00 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-22 20:29:17 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case WORKINGPRESS:
|
|
|
|
if (CHANGED(toDouble, "psi", "bar")) {
|
|
|
|
if (value.toDouble() != 0.0) {
|
2013-05-24 05:56:12 +00:00
|
|
|
TankInfoModel *tanks = TankInfoModel::instance();
|
|
|
|
QModelIndexList matches = tanks->match(tanks->index(0,0), Qt::DisplayRole, cyl->type.description);
|
2013-05-22 20:29:17 +00:00
|
|
|
if (prefs.units.pressure == prefs.units.PSI)
|
|
|
|
cyl->type.workingpressure.mbar = psi_to_mbar(value.toDouble());
|
|
|
|
else
|
|
|
|
cyl->type.workingpressure.mbar = value.toDouble() * 1000;
|
2013-05-24 05:56:12 +00:00
|
|
|
if (!matches.isEmpty())
|
|
|
|
tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl->type.workingpressure.mbar / 1000.0);
|
2013-05-22 20:29:17 +00:00
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case START:
|
|
|
|
if (CHANGED(toDouble, "psi", "bar")) {
|
|
|
|
if (value.toDouble() != 0.0) {
|
|
|
|
if (prefs.units.pressure == prefs.units.PSI)
|
|
|
|
cyl->start.mbar = psi_to_mbar(value.toDouble());
|
|
|
|
else
|
|
|
|
cyl->start.mbar = value.toDouble() * 1000;
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case END:
|
|
|
|
if (CHANGED(toDouble, "psi", "bar")) {
|
|
|
|
if (value.toDouble() != 0.0) {
|
|
|
|
if (prefs.units.pressure == prefs.units.PSI)
|
|
|
|
cyl->end.mbar = psi_to_mbar(value.toDouble());
|
|
|
|
else
|
|
|
|
cyl->end.mbar = value.toDouble() * 1000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case O2:
|
|
|
|
if (CHANGED(toInt, "%", "%")) {
|
2013-05-22 15:20:00 +00:00
|
|
|
cyl->gasmix.o2.permille = value.toInt() * 10 - 5;
|
2013-05-22 20:29:17 +00:00
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HE:
|
|
|
|
if (CHANGED(toInt, "%", "%")) {
|
2013-05-22 15:20:00 +00:00
|
|
|
cyl->gasmix.he.permille = value.toInt() * 10 - 5;
|
2013-05-22 20:29:17 +00:00
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
break;
|
2013-05-22 15:20:00 +00:00
|
|
|
}
|
2013-05-22 20:29:17 +00:00
|
|
|
return QAbstractItemModel::setData(index, value, role);
|
2013-05-22 15:20:00 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 13:17:59 +00:00
|
|
|
int CylindersModel::rowCount(const QModelIndex& parent) const
|
|
|
|
{
|
2013-05-21 12:33:55 +00:00
|
|
|
return rows;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
2013-05-22 17:52:38 +00:00
|
|
|
void CylindersModel::add()
|
2013-04-13 13:17:59 +00:00
|
|
|
{
|
2013-05-21 12:33:55 +00:00
|
|
|
if (rows >= MAX_CYLINDERS) {
|
2013-05-01 21:49:17 +00:00
|
|
|
return;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
2013-05-21 12:33:55 +00:00
|
|
|
int row = rows;
|
2013-04-13 13:17:59 +00:00
|
|
|
|
|
|
|
beginInsertRows(QModelIndex(), row, row);
|
2013-05-21 12:33:55 +00:00
|
|
|
rows++;
|
2013-04-13 13:17:59 +00:00
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CylindersModel::update()
|
|
|
|
{
|
2013-05-21 12:33:55 +00:00
|
|
|
setDive(current);
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CylindersModel::clear()
|
|
|
|
{
|
2013-05-21 12:33:55 +00:00
|
|
|
if (rows > 0) {
|
|
|
|
beginRemoveRows(QModelIndex(), 0, rows-1);
|
2013-04-13 13:17:59 +00:00
|
|
|
endRemoveRows();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-21 12:33:55 +00:00
|
|
|
void CylindersModel::setDive(dive* d)
|
|
|
|
{
|
|
|
|
if (current)
|
|
|
|
clear();
|
|
|
|
|
2013-05-21 18:29:21 +00:00
|
|
|
int amount = MAX_CYLINDERS;
|
2013-05-23 04:25:05 +00:00
|
|
|
for(int i = 0; i < MAX_CYLINDERS; i++) {
|
2013-05-21 18:29:21 +00:00
|
|
|
cylinder_t *cylinder = &d->cylinder[i];
|
|
|
|
if (cylinder_none(cylinder)) {
|
2013-05-21 12:33:55 +00:00
|
|
|
amount = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
beginInsertRows(QModelIndex(), 0, amount-1);
|
|
|
|
rows = amount;
|
|
|
|
current = d;
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
|
2013-05-22 14:00:20 +00:00
|
|
|
Qt::ItemFlags CylindersModel::flags(const QModelIndex& index) const
|
|
|
|
{
|
|
|
|
if (index.column() == REMOVE)
|
|
|
|
return Qt::ItemIsEnabled;
|
2013-05-22 15:20:00 +00:00
|
|
|
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
2013-05-22 14:00:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CylindersModel::remove(const QModelIndex& index)
|
|
|
|
{
|
2013-05-23 04:25:05 +00:00
|
|
|
if (index.column() != REMOVE) {
|
2013-05-22 14:00:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-05-22 14:40:57 +00:00
|
|
|
beginRemoveRows(QModelIndex(), index.row(), index.row()); // yah, know, ugly.
|
2013-05-22 17:02:28 +00:00
|
|
|
rows--;
|
|
|
|
remove_cylinder(current, index.row());
|
|
|
|
mark_divelist_changed(TRUE);
|
2013-05-22 14:40:57 +00:00
|
|
|
endRemoveRows();
|
2013-05-22 14:00:20 +00:00
|
|
|
}
|
|
|
|
|
2013-05-24 01:40:16 +00:00
|
|
|
WeightModel::WeightModel(QObject* parent): QAbstractTableModel(parent), current(0), rows(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-05-22 14:00:20 +00:00
|
|
|
void WeightModel::remove(const QModelIndex& index)
|
|
|
|
{
|
2013-05-23 04:25:05 +00:00
|
|
|
if (index.column() != REMOVE) {
|
2013-05-22 14:00:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-05-22 14:40:57 +00:00
|
|
|
beginRemoveRows(QModelIndex(), index.row(), index.row()); // yah, know, ugly.
|
2013-05-22 17:02:28 +00:00
|
|
|
rows--;
|
|
|
|
remove_weightsystem(current, index.row());
|
|
|
|
mark_divelist_changed(TRUE);
|
2013-05-22 14:40:57 +00:00
|
|
|
endRemoveRows();
|
2013-05-22 14:00:20 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 13:17:59 +00:00
|
|
|
void WeightModel::clear()
|
|
|
|
{
|
2013-05-21 12:59:41 +00:00
|
|
|
if (rows > 0) {
|
|
|
|
beginRemoveRows(QModelIndex(), 0, rows-1);
|
2013-05-01 16:04:14 +00:00
|
|
|
endRemoveRows();
|
|
|
|
}
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int WeightModel::columnCount(const QModelIndex& parent) const
|
|
|
|
{
|
2013-05-22 14:00:20 +00:00
|
|
|
return COLUMNS;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant WeightModel::data(const QModelIndex& index, int role) const
|
|
|
|
{
|
2013-05-01 17:11:46 +00:00
|
|
|
QVariant ret;
|
2013-05-26 03:04:31 +00:00
|
|
|
QFont font;
|
2013-05-02 05:00:08 +00:00
|
|
|
if (!index.isValid() || index.row() >= MAX_WEIGHTSYSTEMS)
|
2013-05-01 17:11:46 +00:00
|
|
|
return ret;
|
2013-05-02 05:00:08 +00:00
|
|
|
|
2013-05-24 01:40:16 +00:00
|
|
|
weightsystem_t *ws = ¤t->weightsystem[index.row()];
|
2013-05-01 17:11:46 +00:00
|
|
|
|
2013-05-26 03:04:31 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::FontRole:
|
|
|
|
font.setPointSizeF(font.pointSizeF() * 0.80);
|
|
|
|
ret = font;
|
|
|
|
break;
|
|
|
|
case Qt::TextAlignmentRole:
|
|
|
|
ret = Qt::AlignRight;
|
|
|
|
break;
|
|
|
|
case Qt::DisplayRole:
|
|
|
|
case Qt::EditRole:
|
2013-05-01 17:11:46 +00:00
|
|
|
switch(index.column()) {
|
|
|
|
case TYPE:
|
|
|
|
ret = QString(ws->description);
|
|
|
|
break;
|
|
|
|
case WEIGHT:
|
2013-05-21 19:03:05 +00:00
|
|
|
ret = get_weight_string(ws->weight, TRUE);
|
2013-05-01 17:11:46 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-05-26 03:04:31 +00:00
|
|
|
break;
|
|
|
|
case Qt::DecorationRole:
|
2013-05-23 04:25:05 +00:00
|
|
|
if (index.column() == REMOVE)
|
2013-05-22 14:00:20 +00:00
|
|
|
ret = QIcon(":trash");
|
2013-05-26 03:04:31 +00:00
|
|
|
break;
|
2013-05-22 14:00:20 +00:00
|
|
|
}
|
2013-05-01 17:11:46 +00:00
|
|
|
return ret;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
2013-05-24 01:40:16 +00:00
|
|
|
// this is our magic 'pass data in' function that allows the delegate to get
|
|
|
|
// the data here without silly unit conversions;
|
|
|
|
// so we only implement the two columns we care about
|
|
|
|
void WeightModel::passInData(const QModelIndex& index, const QVariant& value)
|
|
|
|
{
|
|
|
|
weightsystem_t *ws = ¤t->weightsystem[index.row()];
|
|
|
|
if (index.column() == WEIGHT) {
|
|
|
|
if (ws->weight.grams != value.toInt()) {
|
|
|
|
ws->weight.grams = value.toInt();
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-22 15:20:00 +00:00
|
|
|
bool WeightModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
|
|
{
|
2013-05-24 01:40:16 +00:00
|
|
|
weightsystem_t *ws = ¤t->weightsystem[index.row()];
|
2013-05-22 15:20:00 +00:00
|
|
|
switch(index.column()) {
|
2013-05-24 01:40:16 +00:00
|
|
|
case TYPE:
|
|
|
|
if (!value.isNull()) {
|
Fix usage of temporary QByteArrays
This commit fixes three different things:
- a memory leak in WeightModel::setData()
- getSetting() calling strdup() on a QByteArray
- a possible usage of memory after deallocation
Here's an explanation of the last issue (taken from the mailing list, slightly
adapted):
toByteArray(), as well as others "toSomething()" methods, returns
a new object which the compiler allocates on the stack. The compiler
will consider it a temporary data, and destroy it on the next line. So,
when one does
char *text= value.toByteArray().data(); // line 1
if (strcmp(description, text)) { // line 2
the compiler creates a QByteArray on line 1, calls ::data() on it, which
returns a valid char *, and assigns its value to "text". So far, so
good. But before jumping to line 2, the compiler destroys the temporary
QByteArray, and this will in turn invoke the QByteArray destructor,
which will destroy the internal data. The result is that on line 2,
"text" will point to some memory which has already been freed.
One solution is to store a copy of the temporary QByteArray into a local
variable: the compiler will still destroy the temporary QByteArray it created,
but (thanks to the reference-counted data sharing built in QByteArray) now the
destructor will see that the data is referenced by another instance of
QByteArray (the local variable "ba") and will not free the internal data.
In this way, the internal data will be available until the local variable is
destroyed, which will happen at the end of the {} block where it is defined.
Please note that when one uses the data in the same line, one doesn't need to
worry about this issue. In fact,
text = strdup(value.toString().toUtf8().data());
works just fine.
Signed-off-by: Alberto Mardegan <mardy@users.sourceforge.net>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-05-24 11:39:37 +00:00
|
|
|
QByteArray ba = value.toString().toUtf8();
|
|
|
|
const char *text = ba.constData();
|
2013-05-24 01:40:16 +00:00
|
|
|
if (!ws->description || strcmp(ws->description, text)) {
|
|
|
|
ws->description = strdup(text);
|
|
|
|
mark_divelist_changed(TRUE);
|
|
|
|
}
|
|
|
|
}
|
2013-05-22 15:20:00 +00:00
|
|
|
break;
|
|
|
|
case WEIGHT:
|
2013-05-24 01:40:16 +00:00
|
|
|
if (CHANGED(toDouble, "kg", "lbs")) {
|
|
|
|
if (prefs.units.weight == prefs.units.LBS)
|
|
|
|
ws->weight.grams = lbs_to_grams(value.toDouble());
|
|
|
|
else
|
|
|
|
ws->weight.grams = value.toDouble() * 1000.0;
|
2013-05-24 05:56:12 +00:00
|
|
|
// now update the ws_info
|
|
|
|
WSInfoModel *wsim = WSInfoModel::instance();
|
|
|
|
QModelIndexList matches = wsim->match(wsim->index(0,0), Qt::DisplayRole, ws->description);
|
|
|
|
if (!matches.isEmpty())
|
|
|
|
wsim->setData(wsim->index(matches.first().row(), WSInfoModel::GR), ws->weight.grams);
|
2013-05-24 01:40:16 +00:00
|
|
|
}
|
2013-05-22 15:20:00 +00:00
|
|
|
break;
|
|
|
|
}
|
2013-05-24 01:40:16 +00:00
|
|
|
return QAbstractItemModel::setData(index, value, role);
|
2013-05-22 15:20:00 +00:00
|
|
|
}
|
|
|
|
|
2013-05-22 14:00:20 +00:00
|
|
|
Qt::ItemFlags WeightModel::flags(const QModelIndex& index) const
|
|
|
|
{
|
|
|
|
if (index.column() == REMOVE)
|
|
|
|
return Qt::ItemIsEnabled;
|
2013-05-22 15:20:00 +00:00
|
|
|
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
|
2013-05-22 14:00:20 +00:00
|
|
|
}
|
|
|
|
|
2013-04-13 13:17:59 +00:00
|
|
|
int WeightModel::rowCount(const QModelIndex& parent) const
|
|
|
|
{
|
2013-05-21 12:59:41 +00:00
|
|
|
return rows;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QVariant WeightModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
2013-05-26 03:04:31 +00:00
|
|
|
QFont font;
|
2013-05-02 05:00:08 +00:00
|
|
|
if (orientation == Qt::Vertical)
|
2013-04-13 13:17:59 +00:00
|
|
|
return ret;
|
|
|
|
|
2013-05-26 03:04:31 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::FontRole:
|
|
|
|
font.setPointSizeF(font.pointSizeF() * 0.8);
|
|
|
|
ret = font;
|
|
|
|
break;
|
|
|
|
case Qt::DisplayRole:
|
2013-05-01 21:30:34 +00:00
|
|
|
switch(section) {
|
|
|
|
case TYPE:
|
|
|
|
ret = tr("Type");
|
|
|
|
break;
|
|
|
|
case WEIGHT:
|
|
|
|
ret = tr("Weight");
|
|
|
|
break;
|
|
|
|
}
|
2013-05-26 03:04:31 +00:00
|
|
|
break;
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-05-22 17:52:38 +00:00
|
|
|
void WeightModel::add()
|
2013-04-13 13:17:59 +00:00
|
|
|
{
|
2013-05-21 18:29:21 +00:00
|
|
|
if (rows >= MAX_WEIGHTSYSTEMS)
|
2013-05-01 21:49:17 +00:00
|
|
|
return;
|
2013-05-01 17:11:46 +00:00
|
|
|
|
2013-05-21 12:59:41 +00:00
|
|
|
int row = rows;
|
2013-05-01 17:11:46 +00:00
|
|
|
beginInsertRows(QModelIndex(), row, row);
|
2013-05-21 12:59:41 +00:00
|
|
|
rows++;
|
2013-05-01 17:11:46 +00:00
|
|
|
endInsertRows();
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void WeightModel::update()
|
|
|
|
{
|
2013-05-21 12:59:41 +00:00
|
|
|
setDive(current);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WeightModel::setDive(dive* d)
|
|
|
|
{
|
|
|
|
if (current)
|
|
|
|
clear();
|
|
|
|
|
2013-05-21 18:29:21 +00:00
|
|
|
int amount = MAX_WEIGHTSYSTEMS;
|
|
|
|
for(int i = 0; i < MAX_WEIGHTSYSTEMS; i++) {
|
|
|
|
weightsystem_t *weightsystem = &d->weightsystem[i];
|
|
|
|
if (weightsystem_none(weightsystem)) {
|
2013-05-21 12:59:41 +00:00
|
|
|
amount = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
beginInsertRows(QModelIndex(), 0, amount-1);
|
|
|
|
rows = amount;
|
|
|
|
current = d;
|
|
|
|
endInsertRows();
|
2013-04-13 13:17:59 +00:00
|
|
|
}
|
2013-04-15 18:04:35 +00:00
|
|
|
|
2013-05-24 01:40:16 +00:00
|
|
|
WSInfoModel* WSInfoModel::instance()
|
|
|
|
{
|
|
|
|
static WSInfoModel *self = new WSInfoModel();
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WSInfoModel::insertRows(int row, int count, const QModelIndex& parent)
|
|
|
|
{
|
|
|
|
beginInsertRows(parent, rowCount(), rowCount());
|
|
|
|
rows += count;
|
|
|
|
endInsertRows();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WSInfoModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
|
|
{
|
|
|
|
struct ws_info *info = &ws_info[index.row()];
|
2013-05-24 05:56:12 +00:00
|
|
|
switch(index.column()) {
|
|
|
|
case DESCRIPTION:
|
|
|
|
info->name = strdup(value.toByteArray().data());
|
|
|
|
break;
|
|
|
|
case GR:
|
|
|
|
info->grams = value.toInt();
|
|
|
|
break;
|
|
|
|
}
|
2013-05-24 01:40:16 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WSInfoModel::clear()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int WSInfoModel::columnCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant WSInfoModel::data(const QModelIndex& index, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
if (!index.isValid()) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
struct ws_info *info = &ws_info[index.row()];
|
|
|
|
|
|
|
|
int gr = info->grams;
|
|
|
|
|
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
|
|
|
switch(index.column()) {
|
|
|
|
case GR:
|
|
|
|
ret = gr;
|
|
|
|
break;
|
|
|
|
case DESCRIPTION:
|
|
|
|
ret = QString(info->name);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant WSInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
|
|
|
|
if (orientation != Qt::Horizontal)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
switch(section) {
|
|
|
|
case GR:
|
|
|
|
ret = tr("kg");
|
|
|
|
break;
|
|
|
|
case DESCRIPTION:
|
|
|
|
ret = tr("Description");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WSInfoModel::rowCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
return rows+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
WSInfoModel::WSInfoModel() : QAbstractTableModel(), rows(-1)
|
|
|
|
{
|
|
|
|
struct ws_info *info = ws_info;
|
|
|
|
for (info = ws_info; info->name; info++, rows++);
|
|
|
|
|
|
|
|
if (rows > -1) {
|
|
|
|
beginInsertRows(QModelIndex(), 0, rows);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WSInfoModel::update()
|
|
|
|
{
|
|
|
|
if (rows > -1) {
|
|
|
|
beginRemoveRows(QModelIndex(), 0, rows);
|
|
|
|
endRemoveRows();
|
2013-05-24 05:56:12 +00:00
|
|
|
rows = -1;
|
2013-05-24 01:40:16 +00:00
|
|
|
}
|
|
|
|
struct ws_info *info = ws_info;
|
|
|
|
for (info = ws_info; info->name; info++, rows++);
|
|
|
|
|
|
|
|
if (rows > -1) {
|
|
|
|
beginInsertRows(QModelIndex(), 0, rows);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-23 18:33:20 +00:00
|
|
|
TankInfoModel* TankInfoModel::instance()
|
2013-04-15 18:04:35 +00:00
|
|
|
{
|
2013-05-23 18:33:20 +00:00
|
|
|
static TankInfoModel *self = new TankInfoModel();
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TankInfoModel::insertRows(int row, int count, const QModelIndex& parent)
|
|
|
|
{
|
|
|
|
beginInsertRows(parent, rowCount(), rowCount());
|
|
|
|
rows += count;
|
|
|
|
endInsertRows();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TankInfoModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
|
|
{
|
|
|
|
struct tank_info *info = &tank_info[index.row()];
|
2013-05-24 05:56:12 +00:00
|
|
|
switch(index.column()) {
|
|
|
|
case DESCRIPTION:
|
|
|
|
info->name = strdup(value.toByteArray().data());
|
|
|
|
break;
|
|
|
|
case ML:
|
|
|
|
info->ml = value.toInt();
|
|
|
|
break;
|
|
|
|
case BAR:
|
|
|
|
info->bar = value.toInt();
|
|
|
|
break;
|
|
|
|
}
|
2013-05-24 01:40:16 +00:00
|
|
|
return TRUE;
|
2013-04-15 18:04:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2013-05-22 23:22:21 +00:00
|
|
|
if (info->cuft && info->psi) {
|
|
|
|
pressure_t p;
|
|
|
|
p.mbar = psi_to_mbar(info->psi);
|
|
|
|
ml = wet_volume(info->cuft, p);
|
2013-04-15 18:04:35 +00:00
|
|
|
}
|
2013-05-23 18:33:20 +00:00
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
2013-04-15 18:04:35 +00:00
|
|
|
switch(index.column()) {
|
2013-05-02 05:00:08 +00:00
|
|
|
case BAR:
|
|
|
|
ret = bar;
|
2013-04-15 18:04:35 +00:00
|
|
|
break;
|
2013-05-02 05:00:08 +00:00
|
|
|
case ML:
|
|
|
|
ret = ml;
|
2013-04-15 18:04:35 +00:00
|
|
|
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;
|
2013-05-02 05:00:08 +00:00
|
|
|
for (info = tank_info; info->name; info++, rows++);
|
2013-04-15 18:04:35 +00:00
|
|
|
if (rows > -1) {
|
|
|
|
beginInsertRows(QModelIndex(), 0, rows);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TankInfoModel::update()
|
|
|
|
{
|
2013-05-23 04:25:05 +00:00
|
|
|
if (rows > -1) {
|
2013-04-15 18:04:35 +00:00
|
|
|
beginRemoveRows(QModelIndex(), 0, rows);
|
|
|
|
endRemoveRows();
|
2013-05-24 05:56:12 +00:00
|
|
|
rows = -1;
|
2013-04-15 18:04:35 +00:00
|
|
|
}
|
|
|
|
struct tank_info *info = tank_info;
|
2013-05-02 05:00:08 +00:00
|
|
|
for (info = tank_info; info->name; info++, rows++);
|
2013-04-15 18:04:35 +00:00
|
|
|
|
|
|
|
if (rows > -1) {
|
|
|
|
beginInsertRows(QModelIndex(), 0, rows);
|
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
}
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-04-27 15:27:27 +00:00
|
|
|
|
2013-04-22 01:12:36 +00:00
|
|
|
/*! 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.
|
|
|
|
*
|
|
|
|
*/
|
2013-05-02 02:51:34 +00:00
|
|
|
|
|
|
|
TreeItemDT::~TreeItemDT()
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-14 07:28:30 +00:00
|
|
|
qDeleteAll(children);
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
2013-04-25 07:50:01 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
int TreeItemDT::row() const
|
|
|
|
{
|
|
|
|
if (parent)
|
2013-05-14 07:28:30 +00:00
|
|
|
return parent->children.indexOf(const_cast<TreeItemDT*>(this));
|
2013-04-25 07:50:01 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
QVariant TreeItemDT::data(int column, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
switch (column) {
|
2013-05-29 20:43:14 +00:00
|
|
|
case NR: ret = tr("#"); break;
|
|
|
|
case DATE: ret = tr("Date"); break;
|
|
|
|
case RATING: ret = UTF8_BLACKSTAR; break;
|
|
|
|
case DEPTH: ret = (get_units()->length == units::METERS) ? tr("m") : tr("ft"); break;
|
|
|
|
case DURATION: ret = tr("min"); break;
|
|
|
|
case TEMPERATURE: ret = QString("%1%2").arg(UTF8_DEGREE).arg((get_units()->temperature == units::CELSIUS) ? "C" : "F"); break;
|
|
|
|
case TOTALWEIGHT: ret = (get_units()->weight == units::KG) ? tr("kg") : tr("lbs"); break;
|
|
|
|
case SUIT: ret = tr("Suit"); break;
|
|
|
|
case CYLINDER: ret = tr("Cyl"); break;
|
|
|
|
case NITROX: ret = QString("O%1%").arg(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;
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct TripItem : public TreeItemDT {
|
|
|
|
virtual QVariant data(int column, int role) const;
|
|
|
|
dive_trip_t* trip;
|
|
|
|
};
|
|
|
|
|
|
|
|
QVariant TripItem::data(int column, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
2013-04-25 14:04:41 +00:00
|
|
|
|
2013-05-29 05:54:39 +00:00
|
|
|
if (role == SORT_ROLE)
|
|
|
|
return (qulonglong)trip->when;
|
|
|
|
|
2013-05-02 16:33:51 +00:00
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
switch (column) {
|
|
|
|
case LOCATION:
|
|
|
|
ret = QString(trip->location);
|
|
|
|
break;
|
|
|
|
case DATE:
|
|
|
|
ret = QString(get_trip_date_string(trip->when, trip->nrdives));
|
|
|
|
break;
|
|
|
|
}
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct DiveItem : public TreeItemDT {
|
|
|
|
virtual QVariant data(int column, int role) const;
|
|
|
|
struct dive* dive;
|
2013-04-27 15:27:27 +00:00
|
|
|
|
2013-04-24 15:57:30 +00:00
|
|
|
QString displayDuration() const;
|
|
|
|
QString displayDepth() const;
|
2013-04-25 06:21:57 +00:00
|
|
|
QString displayTemperature() const;
|
|
|
|
QString displayWeight() const;
|
|
|
|
QString displaySac() const;
|
2013-05-02 02:51:34 +00:00
|
|
|
int weight() const;
|
|
|
|
};
|
2013-04-27 15:27:27 +00:00
|
|
|
|
2013-05-29 05:54:39 +00:00
|
|
|
static int nitrox_sort_value(struct dive *dive)
|
|
|
|
{
|
|
|
|
int o2, he, o2low;
|
|
|
|
get_dive_gas(dive, &o2, &he, &o2low);
|
|
|
|
return he*1000 + o2;
|
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
QVariant DiveItem::data(int column, int role) const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
QVariant retVal;
|
|
|
|
|
2013-05-02 21:05:53 +00:00
|
|
|
switch (role) {
|
|
|
|
case Qt::TextAlignmentRole:
|
2013-05-02 02:51:34 +00:00
|
|
|
switch (column) {
|
|
|
|
case DATE: /* fall through */
|
|
|
|
case SUIT: /* fall through */
|
|
|
|
case LOCATION:
|
|
|
|
retVal = Qt::AlignLeft;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
retVal = Qt::AlignRight;
|
|
|
|
break;
|
|
|
|
}
|
2013-05-02 21:05:53 +00:00
|
|
|
break;
|
2013-05-29 05:54:39 +00:00
|
|
|
case SORT_ROLE:
|
|
|
|
switch (column) {
|
2013-05-29 20:43:14 +00:00
|
|
|
case NR: retVal = dive->number; break;
|
|
|
|
case DATE: retVal = (qulonglong) dive->when; break;
|
2013-05-29 20:53:50 +00:00
|
|
|
case RATING: retVal = dive->rating; break;
|
2013-05-29 20:43:14 +00:00
|
|
|
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 NITROX: 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 LOCATION: retVal = QString(dive->location); break;
|
2013-05-29 05:54:39 +00:00
|
|
|
}
|
|
|
|
break;
|
2013-05-02 21:05:53 +00:00
|
|
|
case Qt::DisplayRole:
|
2013-05-02 02:51:34 +00:00
|
|
|
switch (column) {
|
2013-05-29 20:43:14 +00:00
|
|
|
case NR: retVal = dive->number; break;
|
|
|
|
case DATE: retVal = QString(get_dive_date_string(dive->when)); 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 NITROX: retVal = QString(get_nitrox_string(dive)); break;
|
|
|
|
case SAC: retVal = displaySac(); break;
|
|
|
|
case OTU: retVal = dive->otu; break;
|
|
|
|
case MAXCNS: retVal = dive->maxcns; break;
|
|
|
|
case LOCATION: retVal = QString(dive->location); break;
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
2013-05-02 21:05:53 +00:00
|
|
|
break;
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
2013-05-02 23:32:57 +00:00
|
|
|
|
2013-05-21 18:29:21 +00:00
|
|
|
if (role == STAR_ROLE)
|
2013-05-02 23:32:57 +00:00
|
|
|
retVal = dive->rating;
|
|
|
|
|
2013-05-21 18:29:21 +00:00
|
|
|
if (role == DIVE_ROLE)
|
2013-05-02 23:32:57 +00:00
|
|
|
retVal = QVariant::fromValue<void*>(dive);
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
return retVal;
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
|
|
|
|
2013-04-24 15:57:30 +00:00
|
|
|
QString DiveItem::displayDepth() const
|
|
|
|
{
|
|
|
|
const int scale = 1000;
|
|
|
|
QString fract, str;
|
|
|
|
if (get_units()->length == units::METERS) {
|
2013-04-25 14:04:41 +00:00
|
|
|
fract = QString::number((unsigned)(dive->maxdepth.mm % scale) / 10);
|
|
|
|
str = QString("%1.%2").arg((unsigned)(dive->maxdepth.mm / scale)).arg(fract, 2, QChar('0'));
|
2013-04-24 15:57:30 +00:00
|
|
|
}
|
|
|
|
if (get_units()->length == units::FEET) {
|
2013-04-25 14:04:41 +00:00
|
|
|
str = QString::number(mm_to_feet(dive->maxdepth.mm),'f',0);
|
2013-04-24 15:57:30 +00:00
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString DiveItem::displayDuration() const
|
|
|
|
{
|
2013-04-24 23:03:14 +00:00
|
|
|
int hrs, mins, secs;
|
2013-04-25 14:04:41 +00:00
|
|
|
secs = dive->duration.seconds % 60;
|
|
|
|
mins = dive->duration.seconds / 60;
|
2013-04-24 23:03:14 +00:00
|
|
|
hrs = mins / 60;
|
|
|
|
mins -= hrs * 60;
|
2013-04-24 15:57:30 +00:00
|
|
|
|
|
|
|
QString displayTime;
|
2013-04-24 23:03:14 +00:00
|
|
|
if (hrs)
|
|
|
|
displayTime = QString("%1:%2:").arg(hrs).arg(mins, 2, 10, QChar('0'));
|
2013-04-24 15:57:30 +00:00
|
|
|
else
|
2013-04-24 23:03:14 +00:00
|
|
|
displayTime = QString("%1:").arg(mins);
|
|
|
|
displayTime += QString("%1").arg(secs, 2, 10, QChar('0'));
|
2013-04-24 15:57:30 +00:00
|
|
|
return displayTime;
|
|
|
|
}
|
|
|
|
|
2013-04-25 06:21:57 +00:00
|
|
|
QString DiveItem::displayTemperature() const
|
|
|
|
{
|
|
|
|
QString str;
|
|
|
|
|
2013-05-14 07:45:01 +00:00
|
|
|
if (!dive->watertemp.mkelvin)
|
|
|
|
return str;
|
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
if (get_units()->temperature == units::CELSIUS)
|
2013-04-25 14:04:41 +00:00
|
|
|
str = QString::number(mkelvin_to_C(dive->watertemp.mkelvin), 'f', 1);
|
2013-05-02 05:00:08 +00:00
|
|
|
else
|
2013-04-25 14:04:41 +00:00
|
|
|
str = QString::number(mkelvin_to_F(dive->watertemp.mkelvin), 'f', 1);
|
2013-05-02 05:00:08 +00:00
|
|
|
|
2013-04-25 06:21:57 +00:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString DiveItem::displaySac() const
|
|
|
|
{
|
|
|
|
QString str;
|
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
if (get_units()->volume == units::LITER)
|
2013-04-25 14:04:41 +00:00
|
|
|
str = QString::number(dive->sac / 1000, 'f', 1);
|
2013-05-02 05:00:08 +00:00
|
|
|
else
|
2013-04-25 14:04:41 +00:00
|
|
|
str = QString::number(ml_to_cuft(dive->sac), 'f', 2);
|
2013-05-02 05:00:08 +00:00
|
|
|
|
2013-04-25 06:21:57 +00:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString DiveItem::displayWeight() const
|
|
|
|
{
|
|
|
|
QString str;
|
|
|
|
|
|
|
|
if (get_units()->weight == units::KG) {
|
2013-05-02 02:51:34 +00:00
|
|
|
int gr = weight() % 1000;
|
|
|
|
int kg = weight() / 1000;
|
2013-05-14 07:45:01 +00:00
|
|
|
str = QString("%1.%2").arg(kg).arg((unsigned)(gr) / 100);
|
2013-04-25 06:21:57 +00:00
|
|
|
} else {
|
2013-05-14 07:45:01 +00:00
|
|
|
str = QString("%1").arg((unsigned)(grams_to_lbs(weight())));
|
2013-04-25 06:21:57 +00:00
|
|
|
}
|
2013-05-02 02:51:34 +00:00
|
|
|
|
2013-04-25 06:21:57 +00:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
int DiveItem::weight() const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
weight_t tw = { total_weight(dive) };
|
|
|
|
return tw.grams;
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
DiveTripModel::DiveTripModel(QObject* parent) :
|
|
|
|
QAbstractItemModel(parent)
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
rootItem = new TreeItemDT();
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
DiveTripModel::~DiveTripModel()
|
|
|
|
{
|
|
|
|
delete rootItem;
|
|
|
|
}
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
int DiveTripModel::columnCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
if (parent.isValid())
|
|
|
|
return static_cast<TreeItemDT*>(parent.internalPointer())->columnCount();
|
|
|
|
else
|
|
|
|
return rootItem->columnCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DiveTripModel::data(const QModelIndex& index, int role) const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
|
|
|
if (!index.isValid())
|
|
|
|
return QVariant();
|
|
|
|
|
2013-05-23 04:25:05 +00:00
|
|
|
if (role == Qt::FontRole) {
|
2013-05-19 13:46:49 +00:00
|
|
|
QFont font;
|
|
|
|
font.setPointSizeF(font.pointSizeF() * 0.7);
|
|
|
|
return font;
|
|
|
|
}
|
2013-05-02 02:51:34 +00:00
|
|
|
TreeItemDT* item = static_cast<TreeItemDT*>(index.internalPointer());
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
return item->data(index.column(), role);
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
Qt::ItemFlags DiveTripModel::flags(const QModelIndex& index) const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
if (!index.isValid())
|
2013-04-22 01:12:36 +00:00
|
|
|
return 0;
|
2013-05-02 02:51:34 +00:00
|
|
|
|
|
|
|
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
QVariant DiveTripModel::headerData(int section, Qt::Orientation orientation,
|
2013-05-21 18:29:21 +00:00
|
|
|
int role) const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
|
|
|
return rootItem->data(section, role);
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
return QVariant();
|
|
|
|
}
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
QModelIndex DiveTripModel::index(int row, int column, const QModelIndex& parent)
|
|
|
|
const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
if (!hasIndex(row, column, parent))
|
2013-04-22 01:12:36 +00:00
|
|
|
return QModelIndex();
|
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
TreeItemDT* parentItem = (!parent.isValid()) ? rootItem : static_cast<TreeItemDT*>(parent.internalPointer());
|
2013-05-02 02:51:34 +00:00
|
|
|
|
2013-05-14 07:28:30 +00:00
|
|
|
TreeItemDT* childItem = parentItem->children[row];
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 05:00:08 +00:00
|
|
|
return (childItem) ? createIndex(row, column, childItem) : QModelIndex();
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
QModelIndex DiveTripModel::parent(const QModelIndex& index) const
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
if (!index.isValid())
|
2013-04-22 01:12:36 +00:00
|
|
|
return QModelIndex();
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
TreeItemDT* childItem = static_cast<TreeItemDT*>(index.internalPointer());
|
|
|
|
TreeItemDT* parentItem = childItem->parent;
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-11 10:38:24 +00:00
|
|
|
if (parentItem == rootItem || !parentItem)
|
2013-04-22 01:12:36 +00:00
|
|
|
return QModelIndex();
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
return createIndex(parentItem->row(), 0, parentItem);
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
int DiveTripModel::rowCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
TreeItemDT* parentItem;
|
|
|
|
|
|
|
|
if (!parent.isValid())
|
|
|
|
parentItem = rootItem;
|
|
|
|
else
|
|
|
|
parentItem = static_cast<TreeItemDT*>(parent.internalPointer());
|
|
|
|
|
2013-05-14 07:28:30 +00:00
|
|
|
int amount = parentItem->children.count();
|
2013-05-14 01:14:59 +00:00
|
|
|
|
|
|
|
return amount;
|
2013-05-02 02:51:34 +00:00
|
|
|
}
|
2013-04-22 01:12:36 +00:00
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
void DiveTripModel::setupModelData()
|
2013-04-22 01:12:36 +00:00
|
|
|
{
|
2013-05-02 02:51:34 +00:00
|
|
|
int i = dive_table.nr;
|
|
|
|
|
2013-05-28 19:56:58 +00:00
|
|
|
if (rowCount()){
|
|
|
|
beginRemoveRows(QModelIndex(), 0, rowCount()-1);
|
|
|
|
endRemoveRows();
|
|
|
|
}
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
while (--i >= 0) {
|
|
|
|
struct dive* dive = get_dive(i);
|
2013-05-07 03:36:37 +00:00
|
|
|
update_cylinder_related_info(dive);
|
2013-05-02 02:51:34 +00:00
|
|
|
dive_trip_t* trip = dive->divetrip;
|
|
|
|
|
|
|
|
DiveItem* diveItem = new DiveItem();
|
|
|
|
diveItem->dive = dive;
|
|
|
|
|
2013-05-28 19:56:58 +00:00
|
|
|
if (!trip || currentLayout == LIST) {
|
2013-05-02 02:51:34 +00:00
|
|
|
diveItem->parent = rootItem;
|
2013-05-14 07:28:30 +00:00
|
|
|
rootItem->children.push_back(diveItem);
|
2013-05-02 02:51:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
2013-05-28 19:56:58 +00:00
|
|
|
if (currentLayout == LIST)
|
|
|
|
continue;
|
|
|
|
|
2013-05-02 02:51:34 +00:00
|
|
|
if (!trips.keys().contains(trip)) {
|
|
|
|
TripItem* tripItem = new TripItem();
|
|
|
|
tripItem->trip = trip;
|
|
|
|
tripItem->parent = rootItem;
|
2013-05-14 07:28:30 +00:00
|
|
|
tripItem->children.push_back(diveItem);
|
2013-05-02 02:51:34 +00:00
|
|
|
trips[trip] = tripItem;
|
2013-05-14 07:28:30 +00:00
|
|
|
rootItem->children.push_back(tripItem);
|
2013-05-02 02:51:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
TripItem* tripItem = trips[trip];
|
2013-05-14 07:28:30 +00:00
|
|
|
tripItem->children.push_back(diveItem);
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
2013-05-28 19:56:58 +00:00
|
|
|
|
|
|
|
if (rowCount()){
|
2013-05-29 20:43:14 +00:00
|
|
|
beginInsertRows(QModelIndex(), 0, rowCount() - 1);
|
2013-05-28 19:56:58 +00:00
|
|
|
endInsertRows();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DiveTripModel::Layout DiveTripModel::layout() const
|
|
|
|
{
|
|
|
|
return currentLayout;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveTripModel::setLayout(DiveTripModel::Layout layout)
|
|
|
|
{
|
|
|
|
currentLayout = layout;
|
|
|
|
setupModelData();
|
2013-04-22 01:12:36 +00:00
|
|
|
}
|
2013-06-07 14:43:45 +00:00
|
|
|
|
|
|
|
/*####################################################################
|
|
|
|
*
|
|
|
|
* Dive Computer Model
|
|
|
|
*
|
|
|
|
*####################################################################
|
|
|
|
*/
|
|
|
|
|
|
|
|
DiveComputerModel::DiveComputerModel(QObject* parent): QAbstractTableModel(parent)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int DiveComputerModel::columnCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
return COLUMNS;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DiveComputerModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
if (role != Qt::DisplayRole || orientation != Qt::Horizontal){
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
switch(section){
|
|
|
|
case ID : ret = tr("Device ID"); break;
|
|
|
|
case MODEL : ret = tr("Model"); break;
|
|
|
|
case NICKNAME : ret = tr("Nickname"); break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant DiveComputerModel::data(const QModelIndex& index, int role) const
|
|
|
|
{
|
|
|
|
struct device_info *device = head_of_device_info_list();
|
|
|
|
for(int i = 0; i < index.row(); i++){
|
|
|
|
device = device->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant ret;
|
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole){
|
|
|
|
switch(index.column()){
|
|
|
|
case ID : ret = device->deviceid; break;
|
|
|
|
case MODEL : ret = device->model; break;
|
|
|
|
case NICKNAME : ret = device->nickname; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (role == Qt::DecorationRole && index.column() == REMOVE){
|
|
|
|
ret = QIcon(":trash");
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int DiveComputerModel::rowCount(const QModelIndex& parent) const
|
|
|
|
{
|
|
|
|
return numRows;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiveComputerModel::update()
|
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
struct device_info *nnl = head_of_device_info_list();
|
|
|
|
while (nnl) {
|
|
|
|
nnl = nnl->next;
|
|
|
|
count++;
|
|
|
|
}
|
2013-06-07 15:57:35 +00:00
|
|
|
|
2013-06-07 14:43:45 +00:00
|
|
|
if(numRows){
|
|
|
|
beginRemoveRows(QModelIndex(), 0, numRows-1);
|
|
|
|
numRows = 0;
|
|
|
|
endRemoveRows();
|
|
|
|
}
|
2013-06-07 15:57:35 +00:00
|
|
|
|
2013-06-07 14:43:45 +00:00
|
|
|
if (count){
|
|
|
|
beginInsertRows(QModelIndex(), 0, count-1);
|
|
|
|
numRows = count;
|
|
|
|
endInsertRows();
|
|
|
|
}
|
2013-06-07 15:57:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Qt::ItemFlags DiveComputerModel::flags(const QModelIndex& index) const
|
|
|
|
{
|
|
|
|
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
|
|
|
|
if (index.column() == NICKNAME)
|
|
|
|
flags |= Qt::ItemIsEditable;
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DiveComputerModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
|
|
|
{
|
|
|
|
struct device_info *nnl = head_of_device_info_list();
|
|
|
|
for(int i = 0; i < index.row(); i++){
|
|
|
|
nnl = nnl->next;
|
|
|
|
}
|
2013-06-07 14:43:45 +00:00
|
|
|
|
2013-06-07 15:57:35 +00:00
|
|
|
QByteArray v = value.toByteArray();
|
|
|
|
nnl->nickname = strdup(v.data()); // how should I free this before setting a new one?
|
2013-06-07 14:43:45 +00:00
|
|
|
}
|