summaryrefslogtreecommitdiffstats
path: root/qt-models/cylindermodel.cpp
diff options
context:
space:
mode:
authorGravatar Tomaz Canabrava <tomaz.canabrava@intel.com>2015-05-28 16:23:49 -0300
committerGravatar Dirk Hohndel <dirk@hohndel.org>2015-05-29 14:09:13 -0700
commitf432b764e78ac3d66f5ab1bfc7c18fbdb75624e5 (patch)
treec2ec5c81660d22bf986968808bd4ecb7256623ae /qt-models/cylindermodel.cpp
parent6e4aa7d044a344527e61f17c2254851ba799c4bd (diff)
downloadsubsurface-f432b764e78ac3d66f5ab1bfc7c18fbdb75624e5.tar.gz
Move DivePlannerModel and CylinderModel to qt-models
Still trying to make it easier for the Mobile Port: This patch is a bit bigger than I hopped, but it was the smallest that I could get. A lot of TODO items where added where I broke the code because the current implementation would break the QML implementtion on the designer. I'll most probably fix those myself when I finish the transition to the models to the new folder. I only moved both models at once because there's an interdependency between them (seems inevitable, tough, but I'll take a better look at it later). Signed-off-by: Tomaz Canabrava <tomaz.canabrava@intel.com> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'qt-models/cylindermodel.cpp')
-rw-r--r--qt-models/cylindermodel.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp
new file mode 100644
index 000000000..70b4f5f30
--- /dev/null
+++ b/qt-models/cylindermodel.cpp
@@ -0,0 +1,370 @@
+#include "cylindermodel.h"
+#include "models.h"
+#include "helpers.h"
+#include "dive.h"
+#include "color.h"
+#include "diveplannermodel.h"
+#include "gettextfromc.h"
+
+CylindersModel::CylindersModel(QObject *parent) : changed(false),
+ rows(0)
+{
+ // enum {REMOVE, TYPE, SIZE, WORKINGPRESS, START, END, O2, HE, DEPTH};
+ setHeaderDataStrings(QStringList() << "" << tr("Type") << tr("Size") << tr("Work press.") << tr("Start press.") << tr("End press.") << tr("O₂%") << tr("He%")
+ << tr("Switch at") << tr("Use"));
+
+}
+
+CylindersModel *CylindersModel::instance()
+{
+
+ static QScopedPointer<CylindersModel> self(new CylindersModel());
+ return self.data();
+}
+
+static QVariant percent_string(fraction_t fraction)
+{
+ int permille = fraction.permille;
+
+ if (!permille)
+ return QVariant();
+ return QString("%1%").arg(permille / 10.0, 0, 'f', 1);
+}
+
+QVariant CylindersModel::data(const QModelIndex &index, int role) const
+{
+ QVariant ret;
+
+ if (!index.isValid() || index.row() >= MAX_CYLINDERS)
+ return ret;
+
+ cylinder_t *cyl = &displayed_dive.cylinder[index.row()];
+ switch (role) {
+ case Qt::BackgroundRole: {
+ switch (index.column()) {
+ // mark the cylinder start / end pressure in red if the values
+ // seem implausible
+ case START:
+ case END:
+ if ((cyl->start.mbar && !cyl->end.mbar) ||
+ (cyl->end.mbar && cyl->start.mbar <= cyl->end.mbar))
+ ret = REDORANGE1_HIGH_TRANS;
+ break;
+ }
+ break;
+ }
+ case Qt::FontRole: {
+ QFont font = defaultModelFont();
+ switch (index.column()) {
+ case START:
+ font.setItalic(!cyl->start.mbar);
+ break;
+ case END:
+ font.setItalic(!cyl->end.mbar);
+ break;
+ }
+ ret = font;
+ break;
+ }
+ case Qt::TextAlignmentRole:
+ ret = Qt::AlignCenter;
+ break;
+ case Qt::DisplayRole:
+ case Qt::EditRole:
+ switch (index.column()) {
+ case TYPE:
+ ret = QString(cyl->type.description);
+ break;
+ case SIZE:
+ if (cyl->type.size.mliter)
+ ret = get_volume_string(cyl->type.size, true, cyl->type.workingpressure.mbar);
+ break;
+ case WORKINGPRESS:
+ if (cyl->type.workingpressure.mbar)
+ ret = get_pressure_string(cyl->type.workingpressure, true);
+ break;
+ case START:
+ if (cyl->start.mbar)
+ ret = get_pressure_string(cyl->start, true);
+ else if (cyl->sample_start.mbar)
+ ret = get_pressure_string(cyl->sample_start, true);
+ break;
+ case END:
+ if (cyl->end.mbar)
+ ret = get_pressure_string(cyl->end, true);
+ else if (cyl->sample_end.mbar)
+ ret = get_pressure_string(cyl->sample_end, true);
+ break;
+ case O2:
+ ret = percent_string(cyl->gasmix.o2);
+ break;
+ case HE:
+ ret = percent_string(cyl->gasmix.he);
+ break;
+ case DEPTH:
+ ret = get_depth_string(cyl->depth, true);
+ break;
+ case USE:
+ ret = gettextFromC::instance()->trGettext(cylinderuse_text[cyl->cylinder_use]);
+ break;
+ }
+ break;
+ case Qt::DecorationRole:
+ if (index.column() == REMOVE)
+ ret = trashIcon();
+ break;
+ case Qt::SizeHintRole:
+ if (index.column() == REMOVE)
+ ret = trashIcon().size();
+ break;
+
+ case Qt::ToolTipRole:
+ if (index.column() == REMOVE)
+ ret = tr("Clicking here will remove this cylinder.");
+ break;
+ }
+
+ return ret;
+}
+
+cylinder_t *CylindersModel::cylinderAt(const QModelIndex &index)
+{
+ return &displayed_dive.cylinder[index.row()];
+}
+
+// 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 = cylinderAt(index);
+ switch (index.column()) {
+ case SIZE:
+ if (cyl->type.size.mliter != value.toInt()) {
+ cyl->type.size.mliter = value.toInt();
+ dataChanged(index, index);
+ }
+ break;
+ case WORKINGPRESS:
+ if (cyl->type.workingpressure.mbar != value.toInt()) {
+ cyl->type.workingpressure.mbar = value.toInt();
+ dataChanged(index, index);
+ }
+ break;
+ }
+}
+
+bool CylindersModel::setData(const QModelIndex &index, const QVariant &value, int role)
+{
+ QString vString;
+ bool addDiveMode = DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING;
+ if (addDiveMode)
+ DivePlannerPointsModel::instance()->rememberTanks();
+
+ cylinder_t *cyl = cylinderAt(index);
+ switch (index.column()) {
+ case TYPE:
+ if (!value.isNull()) {
+ QByteArray ba = value.toByteArray();
+ const char *text = ba.constData();
+ if (!cyl->type.description || strcmp(cyl->type.description, text)) {
+ cyl->type.description = strdup(text);
+ changed = true;
+ }
+ }
+ break;
+ case SIZE:
+ if (CHANGED()) {
+ TankInfoModel *tanks = TankInfoModel::instance();
+ QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description);
+
+ cyl->type.size = string_to_volume(vString.toUtf8().data(), cyl->type.workingpressure);
+ mark_divelist_changed(true);
+ if (!matches.isEmpty())
+ tanks->setData(tanks->index(matches.first().row(), TankInfoModel::ML), cyl->type.size.mliter);
+ changed = true;
+ }
+ break;
+ case WORKINGPRESS:
+ if (CHANGED()) {
+ TankInfoModel *tanks = TankInfoModel::instance();
+ QModelIndexList matches = tanks->match(tanks->index(0, 0), Qt::DisplayRole, cyl->type.description);
+ cyl->type.workingpressure = string_to_pressure(vString.toUtf8().data());
+ if (!matches.isEmpty())
+ tanks->setData(tanks->index(matches.first().row(), TankInfoModel::BAR), cyl->type.workingpressure.mbar / 1000.0);
+ changed = true;
+ }
+ break;
+ case START:
+ if (CHANGED()) {
+ cyl->start = string_to_pressure(vString.toUtf8().data());
+ changed = true;
+ }
+ break;
+ case END:
+ if (CHANGED()) {
+ //&& (!cyl->start.mbar || string_to_pressure(vString.toUtf8().data()).mbar <= cyl->start.mbar)) {
+ cyl->end = string_to_pressure(vString.toUtf8().data());
+ changed = true;
+ }
+ break;
+ case O2:
+ if (CHANGED()) {
+ cyl->gasmix.o2 = string_to_fraction(vString.toUtf8().data());
+ pressure_t modpO2;
+ if (displayed_dive.dc.divemode == PSCR)
+ modpO2.mbar = prefs.decopo2 + (1000 - get_o2(&cyl->gasmix)) * SURFACE_PRESSURE *
+ prefs.o2consumption / prefs.decosac / prefs.pscr_ratio;
+ else
+ modpO2.mbar = prefs.decopo2;
+ cyl->depth = gas_mod(&cyl->gasmix, modpO2, M_OR_FT(3, 10));
+ changed = true;
+ }
+ break;
+ case HE:
+ if (CHANGED()) {
+ cyl->gasmix.he = string_to_fraction(vString.toUtf8().data());
+ changed = true;
+ }
+ break;
+ case DEPTH:
+ if (CHANGED()) {
+ cyl->depth = string_to_depth(vString.toUtf8().data());
+ changed = true;
+ }
+ break;
+ case USE:
+ if (CHANGED()) {
+ cyl->cylinder_use = (enum cylinderuse)vString.toInt();
+ changed = true;
+ }
+ break;
+ }
+ if (addDiveMode)
+ DivePlannerPointsModel::instance()->tanksUpdated();
+ dataChanged(index, index);
+ return true;
+}
+
+int CylindersModel::rowCount(const QModelIndex &parent) const
+{
+ return rows;
+}
+
+void CylindersModel::add()
+{
+ if (rows >= MAX_CYLINDERS) {
+ return;
+ }
+
+ int row = rows;
+ fill_default_cylinder(&displayed_dive.cylinder[row]);
+ displayed_dive.cylinder[row].manually_added = true;
+ beginInsertRows(QModelIndex(), row, row);
+ rows++;
+ changed = true;
+ endInsertRows();
+}
+
+void CylindersModel::clear()
+{
+ if (rows > 0) {
+ beginRemoveRows(QModelIndex(), 0, rows - 1);
+ endRemoveRows();
+ }
+}
+
+void CylindersModel::updateDive()
+{
+ clear();
+ rows = 0;
+ for (int i = 0; i < MAX_CYLINDERS; i++) {
+ if (!cylinder_none(&displayed_dive.cylinder[i]) &&
+ (prefs.display_unused_tanks ||
+ is_cylinder_used(&displayed_dive, i) ||
+ displayed_dive.cylinder[i].manually_added))
+ rows = i + 1;
+ }
+ if (rows > 0) {
+ beginInsertRows(QModelIndex(), 0, rows - 1);
+ endInsertRows();
+ }
+}
+
+void CylindersModel::copyFromDive(dive *d)
+{
+ if (!d)
+ return;
+ rows = 0;
+ for (int i = 0; i < MAX_CYLINDERS; i++) {
+ if (!cylinder_none(&d->cylinder[i]) &&
+ (is_cylinder_used(d, i) || prefs.display_unused_tanks)) {
+ rows = i + 1;
+ }
+ }
+ if (rows > 0) {
+ beginInsertRows(QModelIndex(), 0, rows - 1);
+ endInsertRows();
+ }
+}
+
+Qt::ItemFlags CylindersModel::flags(const QModelIndex &index) const
+{
+ if (index.column() == REMOVE)
+ return Qt::ItemIsEnabled;
+ return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
+}
+
+void CylindersModel::remove(const QModelIndex &index)
+{
+ int mapping[MAX_CYLINDERS];
+ if (index.column() != REMOVE) {
+ return;
+ }
+ int same_gas = -1;
+ cylinder_t *cyl = &displayed_dive.cylinder[index.row()];
+ struct gasmix *mygas = &cyl->gasmix;
+ for (int i = 0; i < MAX_CYLINDERS; i++) {
+ mapping[i] = i;
+ if (i == index.row() || cylinder_none(&displayed_dive.cylinder[i]))
+ continue;
+ struct gasmix *gas2 = &displayed_dive.cylinder[i].gasmix;
+ if (gasmix_distance(mygas, gas2) == 0)
+ same_gas = i;
+ }
+ if (same_gas == -1 &&
+ ((DivePlannerPointsModel::instance()->currentMode() != DivePlannerPointsModel::NOTHING &&
+ DivePlannerPointsModel::instance()->tankInUse(cyl->gasmix)) ||
+ (DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING &&
+ is_cylinder_used(&displayed_dive, index.row())))) {
+ emit warningMessage(TITLE_OR_TEXT(
+ tr("Cylinder cannot be removed"),
+ tr("This gas is in use. Only cylinders that are not used in the dive can be removed.")));
+ return;
+ }
+ beginRemoveRows(QModelIndex(), index.row(), index.row()); // yah, know, ugly.
+ rows--;
+ if (index.row() == 0) {
+ // first gas - we need to make sure that the same gas ends up
+ // as first gas
+ memmove(cyl, &displayed_dive.cylinder[same_gas], sizeof(*cyl));
+ remove_cylinder(&displayed_dive, same_gas);
+ mapping[same_gas] = 0;
+ for (int i = same_gas + 1; i < MAX_CYLINDERS; i++)
+ mapping[i] = i - 1;
+ } else {
+ remove_cylinder(&displayed_dive, index.row());
+ if (same_gas > index.row())
+ same_gas--;
+ mapping[index.row()] = same_gas;
+ for (int i = index.row() + 1; i < MAX_CYLINDERS; i++)
+ mapping[i] = i - 1;
+ }
+ changed = true;
+ endRemoveRows();
+ struct divecomputer *dc = &displayed_dive.dc;
+ while (dc) {
+ dc_cylinder_renumber(&displayed_dive, dc, mapping);
+ dc = dc->next;
+ }
+}