diff --git a/commands/command.h b/commands/command.h index 7010162f5..e633d3a18 100644 --- a/commands/command.h +++ b/commands/command.h @@ -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); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index d8caf5408..7f090eacf 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -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()); } diff --git a/core/equipment.c b/core/equipment.c index d3daf50fc..96bfa73f7 100644 --- a/core/equipment.c +++ b/core/equipment.c @@ -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; diff --git a/core/equipment.h b/core/equipment.h index 43de4dcf4..4df799b89 100644 --- a/core/equipment.h +++ b/core/equipment.h @@ -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); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 8aea21e1d..a38c69b7b 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -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); diff --git a/desktop-widgets/profilewidget.cpp b/desktop-widgets/profilewidget.cpp index f58c585f7..f4763cc90 100644 --- a/desktop-widgets/profilewidget.cpp +++ b/desktop-widgets/profilewidget.cpp @@ -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 &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); } diff --git a/desktop-widgets/profilewidget.h b/desktop-widgets/profilewidget.h index 84d03a5a5..b55e0e6a9 100644 --- a/desktop-widgets/profilewidget.h +++ b/desktop-widgets/profilewidget.h @@ -34,11 +34,13 @@ public: private slots: void divesChanged(const QVector &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; std::vector toolbarActions; @@ -49,8 +51,6 @@ private: void exitEditMode(); void rotateDC(int dir); OwningDivePtr editedDive; - int editedDc; - dive *originalDive; bool placingCommand; }; diff --git a/profile-widget/divehandler.cpp b/profile-widget/divehandler.cpp index d9edf342a..86a35d30d 100644 --- a/profile-widget/divehandler.cpp +++ b/profile-widget/divehandler.cpp @@ -62,10 +62,10 @@ void DiveHandler::selfRemove() void DiveHandler::changeGas() { + ProfileWidget2 *view = qobject_cast(scene()->views().first()); QAction *action = qobject_cast(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) diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 08194a57b..268b80203 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -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(); diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index b20eb5cef..7b727b040 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -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(); diff --git a/qt-models/cylindermodel.cpp b/qt-models/cylindermodel.cpp index ce6972744..caefc82a0 100644 --- a/qt-models/cylindermodel.cpp +++ b/qt-models/cylindermodel.cpp @@ -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; diff --git a/qt-models/diveplannermodel.cpp b/qt-models/diveplannermodel.cpp index 80ad3d2e6..80900468f 100644 --- a/qt-models/diveplannermodel.cpp +++ b/qt-models/diveplannermodel.cpp @@ -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) diff --git a/qt-models/diveplannermodel.h b/qt-models/diveplannermodel.h index 2af8fad0d..86c283022 100644 --- a/qt-models/diveplannermodel.h +++ b/qt-models/diveplannermodel.h @@ -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;