mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
Desktop: Fix Gas Editing for Manually Added Dives.
- show the correct gasmix in the profile; - make gases available for gas switches in the profile after they have been added; - persist gas changes; - add air as a default gas when adding a dive. This still has problems when undoing a gas switch - instead of completely removing the gas switch it is just moved to the next point in the profile. Signed-off-by: Michael Keller <github@ike.ch>
This commit is contained in:
parent
9243921cbb
commit
f65afaf5d2
13 changed files with 121 additions and 54 deletions
|
@ -100,6 +100,7 @@ enum class EditProfileType {
|
|||
ADD,
|
||||
REMOVE,
|
||||
MOVE,
|
||||
EDIT,
|
||||
};
|
||||
void replanDive(dive *d); // dive computer(s) and cylinder(s) of first argument will be consumed!
|
||||
void editProfile(const dive *d, int dcNr, EditProfileType type, int count);
|
||||
|
|
|
@ -879,6 +879,7 @@ QString editProfileTypeToString(EditProfileType type, int count)
|
|||
case EditProfileType::ADD: return Command::Base::tr("Add stop");
|
||||
case EditProfileType::REMOVE: return Command::Base::tr("Remove %n stop(s)", "", count);
|
||||
case EditProfileType::MOVE: return Command::Base::tr("Move %n stop(s)", "", count);
|
||||
case EditProfileType::EDIT: return Command::Base::tr("Edit stop");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -904,7 +905,7 @@ EditProfile::EditProfile(const dive *source, int dcNr, EditProfileType type, int
|
|||
copy_samples(sdc, &dc);
|
||||
copy_events(sdc, &dc);
|
||||
|
||||
setText(editProfileTypeToString(type, count) + diveNumberOrDate(d));
|
||||
setText(editProfileTypeToString(type, count) + " " + diveNumberOrDate(d));
|
||||
}
|
||||
|
||||
EditProfile::~EditProfile()
|
||||
|
@ -925,6 +926,7 @@ void EditProfile::undo()
|
|||
std::swap(sdc->samples, dc.samples);
|
||||
std::swap(sdc->alloc_samples, dc.alloc_samples);
|
||||
std::swap(sdc->sample, dc.sample);
|
||||
std::swap(sdc->events, dc.events);
|
||||
std::swap(sdc->maxdepth, dc.maxdepth);
|
||||
std::swap(d->maxdepth, maxdepth);
|
||||
std::swap(d->meandepth, meandepth);
|
||||
|
@ -1125,7 +1127,7 @@ AddCylinder::AddCylinder(bool currentDiveOnly) :
|
|||
setText(Command::Base::tr("Add cylinder"));
|
||||
else
|
||||
setText(Command::Base::tr("Add cylinder (%n dive(s))", "", dives.size()));
|
||||
cyl = create_new_cylinder(dives[0]);
|
||||
cyl = create_new_manual_cylinder(dives[0]);
|
||||
indexes.reserve(dives.size());
|
||||
}
|
||||
|
||||
|
|
|
@ -503,12 +503,38 @@ cylinder_t create_new_cylinder(const struct dive *d)
|
|||
cylinder_t cyl = empty_cylinder;
|
||||
fill_default_cylinder(d, &cyl);
|
||||
cyl.start = cyl.type.workingpressure;
|
||||
cyl.manually_added = true;
|
||||
cyl.cylinder_use = OC_GAS;
|
||||
return cyl;
|
||||
}
|
||||
|
||||
static bool show_cylinder(const struct dive *d, int i)
|
||||
cylinder_t create_new_manual_cylinder(const struct dive *d)
|
||||
{
|
||||
cylinder_t cyl = create_new_cylinder(d);
|
||||
cyl.manually_added = true;
|
||||
return cyl;
|
||||
}
|
||||
|
||||
void add_default_cylinder(struct dive *d)
|
||||
{
|
||||
// Only add if there are no cylinders yet
|
||||
if (d->cylinders.nr > 0)
|
||||
return;
|
||||
|
||||
cylinder_t cyl;
|
||||
if (!empty_string(prefs.default_cylinder)) {
|
||||
cyl = create_new_cylinder(d);
|
||||
} else {
|
||||
cyl = empty_cylinder;
|
||||
// roughly an AL80
|
||||
cyl.type.description = strdup(translate("gettextFromC", "unknown"));
|
||||
cyl.type.size.mliter = 11100;
|
||||
cyl.type.workingpressure.mbar = 207000;
|
||||
}
|
||||
add_cylinder(&d->cylinders, 0, cyl);
|
||||
reset_cylinders(d, false);
|
||||
}
|
||||
|
||||
static bool show_cylinder(const struct dive *d, int i)
|
||||
{
|
||||
if (is_cylinder_used(d, i))
|
||||
return true;
|
||||
|
|
|
@ -93,7 +93,8 @@ extern void reset_cylinders(struct dive *dive, bool track_gas);
|
|||
extern int gas_volume(const cylinder_t *cyl, pressure_t p); /* Volume in mliter of a cylinder at pressure 'p' */
|
||||
extern int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table *cylinders);
|
||||
extern void fill_default_cylinder(const struct dive *dive, cylinder_t *cyl); /* dive is needed to fill out MOD, which depends on salinity. */
|
||||
extern cylinder_t create_new_cylinder(const struct dive *dive); /* dive is needed to fill out MOD, which depends on salinity. */
|
||||
extern cylinder_t create_new_manual_cylinder(const struct dive *dive); /* dive is needed to fill out MOD, which depends on salinity. */
|
||||
extern void add_default_cylinder(struct dive *dive);
|
||||
extern int first_hidden_cylinder(const struct dive *d);
|
||||
#ifdef DEBUG_CYL
|
||||
extern void dump_cylinders(struct dive *dive, bool verbose);
|
||||
|
|
|
@ -711,6 +711,7 @@ void MainWindow::on_actionAddDive_triggered()
|
|||
d.dc.meandepth.mm = M_OR_FT(13, 39); // this creates a resonable looking safety stop
|
||||
make_manually_added_dive_dc(&d.dc);
|
||||
fake_dc(&d.dc);
|
||||
add_default_cylinder(&d);
|
||||
fixup_dive(&d);
|
||||
|
||||
Command::addDive(&d, divelog.autogroup, true);
|
||||
|
|
|
@ -52,7 +52,7 @@ void EmptyView::resizeEvent(QResizeEvent *)
|
|||
update();
|
||||
}
|
||||
|
||||
ProfileWidget::ProfileWidget() : d(nullptr), dc(0), originalDive(nullptr), placingCommand(false)
|
||||
ProfileWidget::ProfileWidget() : d(nullptr), dc(0), placingCommand(false)
|
||||
{
|
||||
ui.setupUi(this);
|
||||
|
||||
|
@ -122,9 +122,13 @@ ProfileWidget::ProfileWidget() : d(nullptr), dc(0), originalDive(nullptr), placi
|
|||
|
||||
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &ProfileWidget::divesChanged);
|
||||
connect(&diveListNotifier, &DiveListNotifier::settingsChanged, view.get(), &ProfileWidget2::settingsChanged);
|
||||
connect(&diveListNotifier, &DiveListNotifier::cylinderAdded, this, &ProfileWidget::cylindersChanged);
|
||||
connect(&diveListNotifier, &DiveListNotifier::cylinderRemoved, this, &ProfileWidget::cylindersChanged);
|
||||
connect(&diveListNotifier, &DiveListNotifier::cylinderEdited, this, &ProfileWidget::cylindersChanged);
|
||||
connect(view.get(), &ProfileWidget2::stopAdded, this, &ProfileWidget::stopAdded);
|
||||
connect(view.get(), &ProfileWidget2::stopRemoved, this, &ProfileWidget::stopRemoved);
|
||||
connect(view.get(), &ProfileWidget2::stopMoved, this, &ProfileWidget::stopMoved);
|
||||
connect(view.get(), &ProfileWidget2::stopEdited, this, &ProfileWidget::stopEdited);
|
||||
|
||||
ui.profCalcAllTissues->setChecked(qPrefTechnicalDetails::calcalltissues());
|
||||
ui.profCalcCeiling->setChecked(qPrefTechnicalDetails::calcceiling());
|
||||
|
@ -192,6 +196,10 @@ void ProfileWidget::plotCurrentDive()
|
|||
|
||||
void ProfileWidget::plotDive(dive *dIn, int dcIn)
|
||||
{
|
||||
bool endEditMode = false;
|
||||
if (editedDive && (dIn != d || dcIn != dc))
|
||||
endEditMode = true;
|
||||
|
||||
d = dIn;
|
||||
|
||||
if (dcIn >= 0)
|
||||
|
@ -202,7 +210,7 @@ void ProfileWidget::plotDive(dive *dIn, int dcIn)
|
|||
dc = std::min(dc, (int)number_of_computers(current_dive) - 1);
|
||||
|
||||
// Exit edit mode if the dive changed
|
||||
if (editedDive && (originalDive != d || editedDc != dc))
|
||||
if (endEditMode)
|
||||
exitEditMode();
|
||||
|
||||
// If this is a manually added dive and we are not in the planner
|
||||
|
@ -216,7 +224,7 @@ void ProfileWidget::plotDive(dive *dIn, int dcIn)
|
|||
|
||||
setEnabledToolbar(d != nullptr);
|
||||
if (editedDive) {
|
||||
view->plotDive(editedDive.get(), editedDc);
|
||||
view->plotDive(editedDive.get(), dc);
|
||||
setDive(editedDive.get(), dc);
|
||||
} else if (d) {
|
||||
view->setProfileState(d, dc);
|
||||
|
@ -256,22 +264,42 @@ void ProfileWidget::rotateDC(int dir)
|
|||
void ProfileWidget::divesChanged(const QVector<dive *> &dives, DiveField field)
|
||||
{
|
||||
// If the current dive is not in list of changed dives, do nothing.
|
||||
// Only if duration or depth changed, the profile needs to be replotted.
|
||||
// Also, if we are currently placing a command, don't do anything.
|
||||
// Note that we cannot use Command::placingCommand(), because placing
|
||||
// a depth or time change on the maintab requires an update.
|
||||
if (!d || !dives.contains(d) || !(field.duration || field.depth) || placingCommand)
|
||||
return;
|
||||
|
||||
// If were editing the current dive and not currently
|
||||
// If we're editing the current dive and not currently
|
||||
// placing command, we have to update the edited dive.
|
||||
if (editedDive) {
|
||||
copy_dive(d, editedDive.get());
|
||||
// TODO: Holy moly that function sends too many signals. Fix it!
|
||||
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), editedDc);
|
||||
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), dc);
|
||||
}
|
||||
|
||||
plotCurrentDive();
|
||||
// Only if duration or depth changed, the profile needs to be replotted.
|
||||
if (field.duration || field.depth)
|
||||
plotCurrentDive();
|
||||
}
|
||||
|
||||
void ProfileWidget::cylindersChanged(struct dive *changed, int pos)
|
||||
{
|
||||
// If the current dive is not in list of changed dives, do nothing.
|
||||
// Only if duration or depth changed, the profile needs to be replotted.
|
||||
// Also, if we are currently placing a command, don't do anything.
|
||||
// Note that we cannot use Command::placingCommand(), because placing
|
||||
// a depth or time change on the maintab requires an update.
|
||||
if (!d || changed != d || !editedDive)
|
||||
return;
|
||||
|
||||
// If we're editing the current dive we have to update the
|
||||
// cylinders of the edited dive.
|
||||
if (editedDive) {
|
||||
copy_cylinders(&d->cylinders, &editedDive.get()->cylinders);
|
||||
// TODO: Holy moly that function sends too many signals. Fix it!
|
||||
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), dc);
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileWidget::setPlanState(const struct dive *d, int dcNr)
|
||||
|
@ -297,22 +325,20 @@ void ProfileWidget::unsetProfTissues()
|
|||
void ProfileWidget::editDive()
|
||||
{
|
||||
editedDive.reset(alloc_dive());
|
||||
editedDc = dc;
|
||||
copy_dive(d, editedDive.get()); // Work on a copy of the dive
|
||||
originalDive = d;
|
||||
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD);
|
||||
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), editedDc);
|
||||
view->setEditState(editedDive.get(), editedDc);
|
||||
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::EDIT);
|
||||
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get(), dc);
|
||||
view->setEditState(editedDive.get(), dc);
|
||||
}
|
||||
|
||||
void ProfileWidget::exitEditMode()
|
||||
{
|
||||
if (!editedDive)
|
||||
return;
|
||||
|
||||
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::NOTHING);
|
||||
view->setProfileState(d, dc); // switch back to original dive before erasing the copy.
|
||||
editedDive.reset();
|
||||
originalDive = nullptr;
|
||||
}
|
||||
|
||||
// Update depths of edited dive
|
||||
|
@ -338,25 +364,36 @@ void ProfileWidget::stopAdded()
|
|||
{
|
||||
if (!editedDive)
|
||||
return;
|
||||
calcDepth(*editedDive, editedDc);
|
||||
calcDepth(*editedDive, dc);
|
||||
Setter s(placingCommand, true);
|
||||
Command::editProfile(editedDive.get(), editedDc, Command::EditProfileType::ADD, 0);
|
||||
Command::editProfile(editedDive.get(), dc, Command::EditProfileType::ADD, 0);
|
||||
}
|
||||
|
||||
void ProfileWidget::stopRemoved(int count)
|
||||
{
|
||||
if (!editedDive)
|
||||
return;
|
||||
calcDepth(*editedDive, editedDc);
|
||||
calcDepth(*editedDive, dc);
|
||||
Setter s(placingCommand, true);
|
||||
Command::editProfile(editedDive.get(), editedDc, Command::EditProfileType::REMOVE, count);
|
||||
Command::editProfile(editedDive.get(), dc, Command::EditProfileType::REMOVE, count);
|
||||
}
|
||||
|
||||
void ProfileWidget::stopMoved(int count)
|
||||
{
|
||||
if (!editedDive)
|
||||
return;
|
||||
calcDepth(*editedDive, editedDc);
|
||||
calcDepth(*editedDive, dc);
|
||||
Setter s(placingCommand, true);
|
||||
Command::editProfile(editedDive.get(), editedDc, Command::EditProfileType::MOVE, count);
|
||||
Command::editProfile(editedDive.get(), dc, Command::EditProfileType::MOVE, count);
|
||||
}
|
||||
|
||||
void ProfileWidget::stopEdited()
|
||||
{
|
||||
if (!editedDive)
|
||||
return;
|
||||
|
||||
copy_events(get_dive_dc(editedDive.get(), dc), get_dive_dc(d, dc));
|
||||
|
||||
Setter s(placingCommand, true);
|
||||
Command::editProfile(editedDive.get(), dc, Command::EditProfileType::EDIT, 0);
|
||||
}
|
||||
|
|
|
@ -34,11 +34,13 @@ public:
|
|||
private
|
||||
slots:
|
||||
void divesChanged(const QVector<dive *> &dives, DiveField field);
|
||||
void cylindersChanged(struct dive *changed, int pos);
|
||||
void unsetProfHR();
|
||||
void unsetProfTissues();
|
||||
void stopAdded();
|
||||
void stopRemoved(int count);
|
||||
void stopMoved(int count);
|
||||
void stopEdited();
|
||||
private:
|
||||
std::unique_ptr<EmptyView> emptyView;
|
||||
std::vector<QAction *> toolbarActions;
|
||||
|
@ -49,8 +51,6 @@ private:
|
|||
void exitEditMode();
|
||||
void rotateDC(int dir);
|
||||
OwningDivePtr editedDive;
|
||||
int editedDc;
|
||||
dive *originalDive;
|
||||
bool placingCommand;
|
||||
};
|
||||
|
||||
|
|
|
@ -62,10 +62,10 @@ void DiveHandler::selfRemove()
|
|||
|
||||
void DiveHandler::changeGas()
|
||||
{
|
||||
ProfileWidget2 *view = qobject_cast<ProfileWidget2 *>(scene()->views().first());
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance();
|
||||
QModelIndex index = plannerModel->index(parentIndex(), DivePlannerPointsModel::GAS);
|
||||
plannerModel->gasChange(index.sibling(index.row() + 1, index.column()), action->data().toInt());
|
||||
|
||||
view->changeGas(parentIndex(), action->data().toInt());
|
||||
}
|
||||
|
||||
void DiveHandler::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
||||
|
|
|
@ -565,7 +565,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
|
|||
for (int i = 0; i < d->cylinders.nr; i++) {
|
||||
const cylinder_t *cylinder = get_cylinder(d, i);
|
||||
QString label = printCylinderDescription(i, cylinder);
|
||||
gasChange->addAction(label, [this, i, eventTime] { changeGas(i, eventTime); });
|
||||
gasChange->addAction(label, [this, i, eventTime] { addGasSwitch(i, eventTime); });
|
||||
}
|
||||
} else if (d && d->cylinders.nr > 1) {
|
||||
// if we have more than one gas, offer to switch to another one
|
||||
|
@ -573,7 +573,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event)
|
|||
for (int i = 0; i < d->cylinders.nr; i++) {
|
||||
const cylinder_t *cylinder = get_cylinder(d, i);
|
||||
QString label = printCylinderDescription(i, cylinder);
|
||||
gasChange->addAction(label, [this, i, seconds] { changeGas(i, seconds); });
|
||||
gasChange->addAction(label, [this, i, seconds] { addGasSwitch(i, seconds); });
|
||||
}
|
||||
}
|
||||
m.addAction(tr("Add setpoint change"), [this, seconds]() { ProfileWidget2::addSetpointChange(seconds); });
|
||||
|
@ -759,16 +759,25 @@ void ProfileWidget2::splitDive(int seconds)
|
|||
Command::splitDives(mutable_dive(), duration_t{ seconds });
|
||||
}
|
||||
|
||||
void ProfileWidget2::changeGas(int tank, int seconds)
|
||||
void ProfileWidget2::addGasSwitch(int tank, int seconds)
|
||||
{
|
||||
if (!d || tank < 0 || tank >= d->cylinders.nr)
|
||||
return;
|
||||
|
||||
Command::addGasSwitch(mutable_dive(), dc, seconds, tank);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
void ProfileWidget2::changeGas(int index, int newCylinderId)
|
||||
{
|
||||
if ((currentState == PLAN || currentState == EDIT) && plannerModel) {
|
||||
QModelIndex modelIndex = plannerModel->index(index, DivePlannerPointsModel::GAS);
|
||||
plannerModel->gasChange(modelIndex.sibling(modelIndex.row() + 1, modelIndex.column()), newCylinderId);
|
||||
|
||||
if (currentState == EDIT)
|
||||
emit stopEdited();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileWidget2::editName(DiveEventItem *item)
|
||||
{
|
||||
struct event *event = item->getEventMutable();
|
||||
|
|
|
@ -68,6 +68,7 @@ signals:
|
|||
void stopAdded(); // only emitted in edit mode
|
||||
void stopRemoved(int count); // only emitted in edit mode
|
||||
void stopMoved(int count); // only emitted in edit mode
|
||||
void stopEdited(); // only emitted in edit mode
|
||||
|
||||
public
|
||||
slots: // Necessary to call from QAction's signals.
|
||||
|
@ -111,7 +112,8 @@ private:
|
|||
|
||||
void replot();
|
||||
void setZoom(int level);
|
||||
void changeGas(int tank, int seconds);
|
||||
void addGasSwitch(int tank, int seconds);
|
||||
void changeGas(int index, int newCylinderId);
|
||||
void setupSceneAndFlags();
|
||||
void addItemsToScene();
|
||||
void setupItemOnScene();
|
||||
|
|
|
@ -499,7 +499,7 @@ void CylindersModel::add()
|
|||
if (!d)
|
||||
return;
|
||||
int row = d->cylinders.nr;
|
||||
cylinder_t cyl = create_new_cylinder(d);
|
||||
cylinder_t cyl = create_new_manual_cylinder(d);
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
add_cylinder(&d->cylinders, row, cyl);
|
||||
++numRows;
|
||||
|
|
|
@ -206,20 +206,8 @@ void DivePlannerPointsModel::setupCylinders()
|
|||
return; // We have at least one cylinder
|
||||
}
|
||||
}
|
||||
if (!empty_string(prefs.default_cylinder)) {
|
||||
cylinder_t cyl = empty_cylinder;
|
||||
fill_default_cylinder(d, &cyl);
|
||||
cyl.start = cyl.type.workingpressure;
|
||||
add_cylinder(&d->cylinders, 0, cyl);
|
||||
} else {
|
||||
cylinder_t cyl = empty_cylinder;
|
||||
// roughly an AL80
|
||||
cyl.type.description = copy_qstring(tr("unknown"));
|
||||
cyl.type.size.mliter = 11100;
|
||||
cyl.type.workingpressure.mbar = 207000;
|
||||
add_cylinder(&d->cylinders, 0, cyl);
|
||||
}
|
||||
reset_cylinders(d, false);
|
||||
|
||||
add_default_cylinder(d);
|
||||
cylinders.updateDive(d, dcNr);
|
||||
}
|
||||
|
||||
|
@ -1288,7 +1276,7 @@ void DivePlannerPointsModel::computeVariationsDone(QString variations)
|
|||
emit calculatedPlanNotes(QString(d->notes));
|
||||
}
|
||||
|
||||
void DivePlannerPointsModel::createPlan(bool replanCopy)
|
||||
void DivePlannerPointsModel::createPlan(bool saveAsNew)
|
||||
{
|
||||
// Ok, so, here the diveplan creates a dive
|
||||
deco_state_cache cache;
|
||||
|
@ -1350,7 +1338,7 @@ void DivePlannerPointsModel::createPlan(bool replanCopy)
|
|||
#endif // !SUBSURFACE_TESTING
|
||||
} else {
|
||||
copy_events_until(current_dive, d, dcNr, preserved_until.seconds);
|
||||
if (replanCopy) {
|
||||
if (saveAsNew) {
|
||||
// we were planning an old dive and save as a new dive
|
||||
d->id = dive_getUniqID(); // Things will break horribly if we create dives with the same id.
|
||||
#if !defined(SUBSURFACE_TESTING)
|
||||
|
|
|
@ -23,12 +23,12 @@ public:
|
|||
GAS,
|
||||
CCSETPOINT,
|
||||
DIVEMODE,
|
||||
COLUMNS
|
||||
COLUMNS,
|
||||
};
|
||||
enum Mode {
|
||||
NOTHING,
|
||||
PLAN,
|
||||
ADD
|
||||
EDIT,
|
||||
};
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
|
Loading…
Add table
Reference in a new issue