From 2789bb05b133a7cf54081d58d4f5c51c8977e951 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Thu, 28 Jan 2021 14:24:38 +0100 Subject: [PATCH] profile: display arbitrary dive So far the profile operated on the global displayed_dive. Instead, take the dive to be displayed as a parameter to the plotDive() functions. This is necessary if we want to have multiple concurrent profile objects. Think for example for printing or for mobile where multiple dive objects are active at the same time. Signed-off-by: Berthold Stoeger --- desktop-widgets/divelogexportdialog.cpp | 4 +- desktop-widgets/diveplanner.cpp | 4 +- desktop-widgets/mainwindow.cpp | 12 +- desktop-widgets/printer.cpp | 4 +- desktop-widgets/tab-widgets/maintab.cpp | 5 - profile-widget/profilewidget2.cpp | 197 +++++++++++------------- profile-widget/profilewidget2.h | 17 +- profile-widget/qmlprofile.cpp | 7 +- qt-models/divepicturemodel.cpp | 2 - 9 files changed, 121 insertions(+), 131 deletions(-) diff --git a/desktop-widgets/divelogexportdialog.cpp b/desktop-widgets/divelogexportdialog.cpp index e9a4beb4e..30e389dde 100644 --- a/desktop-widgets/divelogexportdialog.cpp +++ b/desktop-widgets/divelogexportdialog.cpp @@ -229,7 +229,7 @@ void exportProfile(const struct dive *dive, const QString filename) profile->setPrintMode(true); double scale = profile->getFontPrintScale(); profile->setFontPrintScale(4 * scale); - profile->plotDive(dive, true, false, true); + profile->plotDive(dive, 0, true, false, true); QImage image = QImage(profile->size() * 4, QImage::Format_RGB32); QPainter paint; paint.begin(&image); @@ -238,5 +238,5 @@ void exportProfile(const struct dive *dive, const QString filename) profile->setToolTipVisibile(true); profile->setFontPrintScale(scale); profile->setPrintMode(false); - profile->plotDive(dive, true); + profile->plotDive(dive, 0, true); // TODO: Shouldn't this plot the current dive? } diff --git a/desktop-widgets/diveplanner.cpp b/desktop-widgets/diveplanner.cpp index 94912cab6..e237097c5 100644 --- a/desktop-widgets/diveplanner.cpp +++ b/desktop-widgets/diveplanner.cpp @@ -541,7 +541,7 @@ void PlannerWidgets::planDive() { DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN); - MainWindow::instance()->graphics->setPlanState(); + MainWindow::instance()->graphics->setPlanState(&displayed_dive, 0); dc_number = 0; // create a simple starting dive, using the first gas from the just copied cylinders @@ -569,7 +569,7 @@ void PlannerWidgets::replanDive() DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN); DivePlannerPointsModel::instance()->loadFromDive(&displayed_dive); - MainWindow::instance()->graphics->setPlanState(); + MainWindow::instance()->graphics->setPlanState(&displayed_dive, 0); plannerWidget.setReplanButton(true); plannerWidget.setupStartTime(timestampToDateTime(displayed_dive.when)); diff --git a/desktop-widgets/mainwindow.cpp b/desktop-widgets/mainwindow.cpp index 425aaa30f..b6cae1e96 100644 --- a/desktop-widgets/mainwindow.cpp +++ b/desktop-widgets/mainwindow.cpp @@ -434,7 +434,7 @@ void MainWindow::selectionChanged() configureToolbar(); enableDisableOtherDCsActions(); } - graphics->plotDive(current_dive, false); + graphics->plotDive(current_dive, dc_number, false); MapWidget::instance()->selectionChanged(); } @@ -685,7 +685,7 @@ void MainWindow::enableShortcuts() void MainWindow::showProfile() { enableShortcuts(); - graphics->setProfileState(); + graphics->setProfileState(current_dive, dc_number); setApplicationState(ApplicationState::Default); } @@ -738,7 +738,7 @@ void MainWindow::refreshProfile() { showProfile(); configureToolbar(); - graphics->plotDive(current_dive, true); + graphics->plotDive(current_dive, dc_number, true); } void MainWindow::planCanceled() @@ -919,7 +919,7 @@ void MainWindow::on_actionPreviousDC_triggered() unsigned nrdc = number_of_computers(current_dive); dc_number = (dc_number + nrdc - 1) % nrdc; configureToolbar(); - graphics->plotDive(current_dive, false); + graphics->plotDive(current_dive, dc_number, false); mainTab->updateDiveInfo(); } @@ -928,7 +928,7 @@ void MainWindow::on_actionNextDC_triggered() unsigned nrdc = number_of_computers(current_dive); dc_number = (dc_number + 1) % nrdc; configureToolbar(); - graphics->plotDive(current_dive, false); + graphics->plotDive(current_dive, dc_number, false); mainTab->updateDiveInfo(); } @@ -1516,7 +1516,7 @@ void MainWindow::editCurrentDive() disableShortcuts(); copy_dive(current_dive, &displayed_dive); // Work on a copy of the dive DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD); - graphics->setAddState(); + graphics->setAddState(&displayed_dive, 0); setApplicationState(ApplicationState::EditDive); DivePlannerPointsModel::instance()->loadFromDive(&displayed_dive); mainTab->enableEdition(); diff --git a/desktop-widgets/printer.cpp b/desktop-widgets/printer.cpp index 49135d79c..022db1a07 100644 --- a/desktop-widgets/printer.cpp +++ b/desktop-widgets/printer.cpp @@ -38,7 +38,7 @@ void Printer::putProfileImage(const QRect &profilePlaceholder, const QRect &view int y = profilePlaceholder.y() - viewPort.y(); // use the placeHolder and the viewPort position to calculate the relative position of the dive profile. QRect pos(x, y, profilePlaceholder.width(), profilePlaceholder.height()); - profile->plotDive(dive, true, true); + profile->plotDive(dive, 0, true, true); if (!printOptions.color_selected) { QImage image(pos.width(), pos.height(), QImage::Format_ARGB32); @@ -193,7 +193,7 @@ void Printer::render(int pages) qPrefDisplay::set_animation_speed(animationOriginal); //replot the dive after returning the settings - profile->plotDive(current_dive, true, true); + profile->plotDive(current_dive, dc_number, true, true); } //value: ranges from 0 : 100 and shows the progress of the templating engine diff --git a/desktop-widgets/tab-widgets/maintab.cpp b/desktop-widgets/tab-widgets/maintab.cpp index 0170cdb40..78784de46 100644 --- a/desktop-widgets/tab-widgets/maintab.cpp +++ b/desktop-widgets/tab-widgets/maintab.cpp @@ -541,11 +541,6 @@ void MainTab::rejectChanges() // no harm done to call cancelPlan even if we were not PLAN mode... DivePlannerPointsModel::instance()->cancelPlan(); - // now make sure that the correct dive is displayed - if (current_dive) - copy_dive(current_dive, &displayed_dive); - else - clear_dive(&displayed_dive); updateDiveInfo(); // show the profile and dive info diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index 7113894a3..bb9f1c6c7 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -360,7 +360,7 @@ void ProfileWidget2::setupItemOnScene() void ProfileWidget2::replot() { - plotDive(current_dive, true, false); + plotDive(d, dc, true, false); } PartialPressureGasItem *ProfileWidget2::createPPGas(int column, color_index_t color, color_index_t colorAlert, @@ -521,29 +521,21 @@ void ProfileWidget2::resetZoom() } // Currently just one dive, but the plan is to enable All of the selected dives. -void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPictures, bool instant) +void ProfileWidget2::plotDive(const struct dive *dIn, int dcIn, bool force, bool doClearPictures, bool instant) { + d = dIn; + dc = dcIn; + if (!d) { + setEmptyState(); + return; + } + QElapsedTimer measureDuration; // let's measure how long this takes us (maybe we'll turn of TTL calculation later measureDuration.start(); #ifdef SUBSURFACE_MOBILE Q_UNUSED(doClearPictures); #endif if ((currentState != ADD && currentState != PLAN) || !plannerModel) { - if (!d) { - setEmptyState(); - return; - } - - // No need to do this again if we are already showing the same dive - // computer of the same dive, so we check the unique id of the dive - // and the selected dive computer number against the ones we are - // showing (can't compare the dive pointers as those might change). - if (d->id == displayed_dive.id && dc_number == dataModel->dcShown() && !force) - return; - - // this copies the dive and makes copies of all the relevant additional data - copy_dive(d, &displayed_dive); - if (decoMode(false) == VPMB) decoModelParameters->setText(QString("VPM-B +%1").arg(prefs.vpmb_conservatism)); else @@ -563,7 +555,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict #endif } - struct divecomputer *currentdc = select_dc(&displayed_dive); + const struct divecomputer *currentdc = get_dive_dc_const(d, dc); if (!currentdc || !currentdc->samples) return setEmptyState(); @@ -581,7 +573,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict if (currentState == EMPTY) setProfileState(); - bool setpointflag = (currentdc->divemode == CCR) && prefs.pp_graphs.po2 && current_dive; + bool setpointflag = (currentdc->divemode == CCR) && prefs.pp_graphs.po2; bool sensorflag = setpointflag && prefs.show_ccr_sensors; o2SetpointGasItem->setVisible(setpointflag && prefs.show_ccr_setpoint); ccrsensor1GasItem->setVisible(sensorflag); @@ -602,9 +594,9 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict #ifndef SUBSURFACE_MOBILE // A non-null planner_ds signals to create_plot_info_new that the dive is currently planned. struct deco_state *planner_ds = currentState == PLAN && plannerModel ? &plannerModel->final_deco_state : nullptr; - create_plot_info_new(&displayed_dive, currentdc, &plotInfo, !shouldCalculateMaxDepth, planner_ds); + create_plot_info_new(d, get_dive_dc_const(d, dc), &plotInfo, !shouldCalculateMaxDepth, planner_ds); #else - create_plot_info_new(&displayed_dive, currentdc, &plotInfo, !shouldCalculateMaxDepth, nullptr); + create_plot_info_new(d, get_dive_dc_const(d, dc), &plotInfo, !shouldCalculateMaxDepth, nullptr); #endif int newMaxtime = get_maxtime(&plotInfo); if (shouldCalculateMaxTime || newMaxtime > maxtime) @@ -682,7 +674,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict cylinderPressureAxis->setMinimum(plotInfo.minpressure); cylinderPressureAxis->setMaximum(plotInfo.maxpressure); #ifndef SUBSURFACE_MOBILE - rulerItem->setPlotInfo(&displayed_dive, plotInfo); + rulerItem->setPlotInfo(d, plotInfo); #endif #ifdef SUBSURFACE_MOBILE @@ -722,13 +714,13 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict ocpo2GasItem->setVisible(false); } #endif - tankItem->setData(&plotInfo, &displayed_dive); + tankItem->setData(&plotInfo, d); gasYAxis->update(); // Replot dive items for (AbstractProfilePolygonItem *item: profileItems) - item->replot(&displayed_dive, currentState == PLAN); + item->replot(d, currentState == PLAN); // The event items are a bit special since we don't know how many events are going to // exist on a dive, so I cant create cache items for that. that's why they are here @@ -736,7 +728,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict qDeleteAll(eventItems); eventItems.clear(); struct event *event = currentdc->events; - struct gasmix lastgasmix = get_gasmix_at_time(&displayed_dive, current_dc, duration_t{1}); + struct gasmix lastgasmix = get_gasmix_at_time(d, get_dive_dc_const(d, dc), duration_t{1}); while (event) { #ifndef SUBSURFACE_MOBILE @@ -759,7 +751,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict item->setHorizontalAxis(timeAxis); item->setVerticalAxis(profileYAxis, qPrefDisplay::animation_speed()); item->setModel(dataModel); - item->setEvent(&displayed_dive, event, lastgasmix); + item->setEvent(d, event, lastgasmix); item->setZValue(2); #ifndef SUBSURFACE_MOBILE item->setScale(printMode ? 4 :1); @@ -767,7 +759,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict scene()->addItem(item); eventItems.push_back(item); if (event_is_gaschange(event)) - lastgasmix = get_gasmix_from_event(&displayed_dive, event); + lastgasmix = get_gasmix_from_event(d, event); event = event->next; } @@ -784,8 +776,8 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict dcText = tr("Unknown dive computer"); #ifndef SUBSURFACE_MOBILE int nr; - if ((nr = number_of_computers(&displayed_dive)) > 1) - dcText += tr(" (#%1 of %2)").arg(dc_number + 1).arg(nr); + if ((nr = number_of_computers(d)) > 1) + dcText += tr(" (#%1 of %2)").arg(dc + 1).arg(nr); #endif diveComputerText->setText(dcText); @@ -799,7 +791,7 @@ void ProfileWidget2::plotDive(const struct dive *d, bool force, bool doClearPict else plotPicturesInternal(d, instant); - toolTipItem->refresh(&displayed_dive, mapToScene(mapFromGlobal(QCursor::pos())), currentState == PLAN); + toolTipItem->refresh(d, mapToScene(mapFromGlobal(QCursor::pos())), currentState == PLAN); #endif // OK, how long did this take us? Anything above the second is way too long, @@ -1025,7 +1017,7 @@ void ProfileWidget2::scrollViewTo(const QPoint &pos) void ProfileWidget2::mouseMoveEvent(QMouseEvent *event) { QPointF pos = mapToScene(event->pos()); - toolTipItem->refresh(&displayed_dive, mapToScene(mapFromGlobal(QCursor::pos())), currentState == PLAN); + toolTipItem->refresh(d, mapToScene(mapFromGlobal(QCursor::pos())), currentState == PLAN); if (zoomLevel == 0) { QGraphicsView::mouseMoveEvent(event); @@ -1123,6 +1115,14 @@ void ProfileWidget2::setEmptyState() hideAll(gases); } +void ProfileWidget2::setProfileState(const dive *dIn, int dcIn) +{ + d = dIn; + dc = dcIn; + + setProfileState(); +} + void ProfileWidget2::setProfileState() { // Then starting Empty State, move the background up. @@ -1207,13 +1207,14 @@ void ProfileWidget2::setProfileState() po2GasItem->setVisible(prefs.pp_graphs.po2); pheGasItem->setVisible(prefs.pp_graphs.phe); - bool setpointflag = current_dive && (current_dc->divemode == CCR) && prefs.pp_graphs.po2; + const struct divecomputer *currentdc = d ? get_dive_dc_const(d, dc) : nullptr; + bool setpointflag = currentdc && currentdc->divemode == CCR && prefs.pp_graphs.po2; bool sensorflag = setpointflag && prefs.show_ccr_sensors; o2SetpointGasItem->setVisible(setpointflag && prefs.show_ccr_setpoint); ccrsensor1GasItem->setVisible(sensorflag); - ccrsensor2GasItem->setVisible(sensorflag && (current_dc->no_o2sensors > 1)); - ccrsensor3GasItem->setVisible(sensorflag && (current_dc->no_o2sensors > 2)); - ocpo2GasItem->setVisible(current_dive && (current_dc->divemode == PSCR) && prefs.show_scr_ocpo2); + ccrsensor2GasItem->setVisible(currentdc && sensorflag && currentdc->no_o2sensors > 1); + ccrsensor3GasItem->setVisible(currentdc && sensorflag && currentdc->no_o2sensors > 2); + ocpo2GasItem->setVisible(currentdc && currentdc->divemode == PSCR && prefs.show_scr_ocpo2); heartBeatItem->setVisible(prefs.hrgraph); #endif @@ -1272,12 +1273,12 @@ void ProfileWidget2::connectPlannerModel() connect(plannerModel, &DivePlannerPointsModel::rowsMoved, this, &ProfileWidget2::pointsMoved); } -void ProfileWidget2::setAddState() +void ProfileWidget2::setAddState(const dive *d, int dc) { if (currentState == ADD) return; - setProfileState(); + setProfileState(d, dc); mouseFollowerHorizontal->setVisible(true); mouseFollowerVertical->setVisible(true); mouseFollowerHorizontal->setLine(timeAxis->line()); @@ -1300,12 +1301,12 @@ void ProfileWidget2::setAddState() setBackgroundBrush(QColor("#A7DCFF")); } -void ProfileWidget2::setPlanState() +void ProfileWidget2::setPlanState(const dive *d, int dc) { if (currentState == PLAN) return; - setProfileState(); + setProfileState(d, dc); mouseFollowerHorizontal->setVisible(true); mouseFollowerVertical->setVisible(true); mouseFollowerHorizontal->setLine(timeAxis->line()); @@ -1369,7 +1370,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) } QMenu m; bool isDCName = false; - if (!current_dive) + if (!d) return; // figure out if we are ontop of the dive computer name in the profile QGraphicsItem *sceneItem = itemAt(mapFromGlobal(event->globalPos())); @@ -1383,13 +1384,13 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) parentItem = parentItem->parentItem(); } if (isDCName) { - if (dc_number == 0 && number_of_computers(current_dive) == 1) + if (dc == 0 && number_of_computers(d) == 1) // nothing to do, can't delete or reorder return; // create menu to show when right clicking on dive computer name - if (dc_number > 0) + if (dc > 0) m.addAction(tr("Make first dive computer"), this, &ProfileWidget2::makeFirstDC); - if (number_of_computers(current_dive) > 1) { + if (number_of_computers(d) > 1) { m.addAction(tr("Delete this dive computer"), this, &ProfileWidget2::deleteCurrentDC); m.addAction(tr("Split this dive computer into own dive"), this, &ProfileWidget2::splitCurrentDC); } @@ -1405,19 +1406,19 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) DiveEventItem *item = dynamic_cast(sceneItem); // Add or edit Gas Change - if (current_dive && item && event_is_gaschange(item->getEvent())) { + if (d && item && event_is_gaschange(item->getEvent())) { int eventTime = item->getEvent()->time.seconds; QMenu *gasChange = m.addMenu(tr("Edit Gas Change")); - for (int i = 0; i < current_dive->cylinders.nr; i++) { - const cylinder_t *cylinder = get_cylinder(current_dive, i); + 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); }); } - } else if (current_dive && current_dive->cylinders.nr > 1) { + } else if (d && d->cylinders.nr > 1) { // if we have more than one gas, offer to switch to another one QMenu *gasChange = m.addMenu(tr("Add gas change")); - for (int i = 0; i < current_dive->cylinders.nr; i++) { - const cylinder_t *cylinder = get_cylinder(current_dive, i); + 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); }); } @@ -1428,7 +1429,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) const struct event *ev = NULL; enum divemode_t divemode = UNDEF_COMP_TYPE; - get_current_divemode(current_dc, seconds, &ev, &divemode); + get_current_divemode(get_dive_dc_const(d, dc), seconds, &ev, &divemode); QMenu *changeMode = m.addMenu(tr("Change divemode")); if (divemode != OC) changeMode->addAction(gettextFromC::tr(divemode_text_ui[OC]), @@ -1440,7 +1441,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) changeMode->addAction(gettextFromC::tr(divemode_text_ui[PSCR]), [this, seconds](){ addDivemodeSwitch(seconds, PSCR); }); - if (same_string(current_dc->model, "manually added dive")) + if (same_string(get_dive_dc_const(d, dc)->model, "manually added dive")) m.addAction(tr("Edit the profile"), this, &ProfileWidget2::editCurrentDive); if (DiveEventItem *item = dynamic_cast(sceneItem)) { @@ -1471,7 +1472,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) int newGasIdx = gasChangeIdx + 1; const struct plot_data &newGasEntry = plotInfo.entry[newGasIdx]; qDebug() << "after gas change at " << newGasEntry->sec << ": sensor pressure" << newGasEntry->pressure[0] << "interpolated" << newGasEntry->pressure[1]; - if (get_plot_sensor_pressure(&plotInfo, gasChangeIdx) == 0 || get_cylinder(&displayed_dive, gasChangeEntry->sensor[0])->sample_start.mbar == 0) { + if (get_plot_sensor_pressure(&plotInfo, gasChangeIdx) == 0 || get_cylinder(d, gasChangeEntry->sensor[0])->sample_start.mbar == 0) { // if we have no sensorpressure or if we have no pressure from samples we can assume that // we only have interpolated pressure (the pressure in the entry may be stored in the sensor // pressure field if this is the first or last entry for this tank... see details in gaspressures.c @@ -1480,7 +1481,7 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) QAction *adjustOldPressure = m.addAction(tr("Adjust pressure of cyl. %1 (currently interpolated as %2)") .arg(gasChangeEntry->sensor[0] + 1).arg(get_pressure_string(pressure))); } - if (get_plot_sensor_pressure(&plotInfo, newGasIdx) == 0 || get_cylinder(&displayed_dive, newGasEntry->sensor[0])->sample_start.mbar == 0) { + if (get_plot_sensor_pressure(&plotInfo, newGasIdx) == 0 || get_cylinder(d, newGasEntry->sensor[0])->sample_start.mbar == 0) { // we only have interpolated press -- see commend above pressure_t pressure; pressure.mbar = get_plot_interpolated_pressure(&plotInfo, newGasIdx) ? : get_plot_sensor_pressure(&plotInfo, newGasIdx); @@ -1505,17 +1506,20 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) void ProfileWidget2::deleteCurrentDC() { - Command::deleteDiveComputer(current_dive, dc_number); + if (d) + Command::deleteDiveComputer(mutable_dive(), dc); } void ProfileWidget2::splitCurrentDC() { - Command::splitDiveComputer(current_dive, dc_number); + if (d) + Command::splitDiveComputer(mutable_dive(), dc); } void ProfileWidget2::makeFirstDC() { - Command::moveDiveComputerToFront(current_dive, dc_number); + if (d) + Command::moveDiveComputerToFront(mutable_dive(), dc); } void ProfileWidget2::hideEvents(DiveEventItem *item) @@ -1551,68 +1555,54 @@ void ProfileWidget2::unhideEvents() item->show(); } -// The profile displays a copy of the current_dive, namely displayed_dive. -// Therefore, the events we get are likewise copies. This function finds -// the original event. TODO: Remove function once the profile can display -// arbitrary dives. -static event *find_event(const struct event *ev) -{ - struct divecomputer *dc = current_dc; - if (!dc) - return nullptr; - for (struct event *act = current_dc->events; act; act = act->next) { - if (same_event(act, ev)) - return act; - } - return nullptr; -} - void ProfileWidget2::removeEvent(DiveEventItem *item) { - struct event *event = find_event(item->getEvent()); - if (!event) + struct event *event = item->getEvent(); + if (!event || !d) return; if (QMessageBox::question(this, TITLE_OR_TEXT( tr("Remove the selected event?"), tr("%1 @ %2:%3").arg(event->name).arg(event->time.seconds / 60).arg(event->time.seconds % 60, 2, 10, QChar('0'))), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) - Command::removeEvent(current_dive, dc_number, event); + Command::removeEvent(mutable_dive(), dc, event); } void ProfileWidget2::addBookmark(int seconds) { - Command::addEventBookmark(current_dive, dc_number, seconds); + if (d) + Command::addEventBookmark(mutable_dive(), dc, seconds); } void ProfileWidget2::addDivemodeSwitch(int seconds, int divemode) { - Command::addEventDivemodeSwitch(current_dive, dc_number, seconds, divemode); + if (d) + Command::addEventDivemodeSwitch(mutable_dive(), dc, seconds, divemode); } void ProfileWidget2::addSetpointChange(int seconds) { - SetpointDialog dialog(current_dive, dc_number, seconds); + if (!d) + return; + SetpointDialog dialog(mutable_dive(), dc, seconds); dialog.exec(); } void ProfileWidget2::splitDive(int seconds) { #ifndef SUBSURFACE_MOBILE - // Make sure that this is an actual dive and we're not in add mode - dive *d = get_dive_by_uniq_id(displayed_dive.id); if (!d) return; - Command::splitDives(d, duration_t{ seconds }); + Command::splitDives(mutable_dive(), duration_t{ seconds }); #endif } void ProfileWidget2::changeGas(int tank, int seconds) { - if (!current_dive || tank < 0 || tank >= current_dive->cylinders.nr) + if (!d || tank < 0 || tank >= d->cylinders.nr) return; - Command::addGasSwitch(current_dive, dc_number, seconds, tank); + Command::addGasSwitch(mutable_dive(), dc, seconds, tank); } #endif @@ -1660,8 +1650,8 @@ double ProfileWidget2::getFontPrintScale() const #ifndef SUBSURFACE_MOBILE void ProfileWidget2::editName(DiveEventItem *item) { - struct event *event = find_event(item->getEvent()); - if (!event) + struct event *event = item->getEvent(); + if (!event || !d) return; bool ok; QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"), @@ -1674,7 +1664,7 @@ void ProfileWidget2::editName(DiveEventItem *item) lengthWarning.exec(); return; } - Command::renameEvent(current_dive, dc_number, event, qPrintable(newName)); + Command::renameEvent(mutable_dive(), dc, event, qPrintable(newName)); } } #endif @@ -1710,7 +1700,7 @@ int ProfileWidget2::handleIndex(const DiveHandler *h) const DiveHandler *ProfileWidget2::createHandle() { - DiveHandler *item = new DiveHandler(&displayed_dive); + DiveHandler *item = new DiveHandler(d); scene()->addItem(item); connect(item, &DiveHandler::moved, this, &ProfileWidget2::divePlannerHandlerMoved); connect(item, &DiveHandler::clicked, this, &ProfileWidget2::divePlannerHandlerClicked); @@ -1792,8 +1782,8 @@ void ProfileWidget2::repositionDiveHandlers() QLineF line(p1, p2); QPointF pos = line.pointAt(0.5); gases[i]->setPos(pos); - if (datapoint.cylinderid >= 0 && datapoint.cylinderid < displayed_dive.cylinders.nr) - gases[i]->setText(get_gas_string(get_cylinder(&displayed_dive, datapoint.cylinderid)->gasmix)); + if (datapoint.cylinderid >= 0 && datapoint.cylinderid < d->cylinders.nr) + gases[i]->setText(get_gas_string(get_cylinder(d, datapoint.cylinderid)->gasmix)); else gases[i]->setText(QString()); gases[i]->setVisible(datapoint.entered && @@ -2067,7 +2057,7 @@ void ProfileWidget2::updateThumbnailXPos(PictureEntry &e) // This function resets the picture thumbnails of the current dive. void ProfileWidget2::plotPictures() { - plotPicturesInternal(current_dive, false); + plotPicturesInternal(d, false); } void ProfileWidget2::plotPicturesInternal(const struct dive *d, bool synchronous) @@ -2099,8 +2089,6 @@ void ProfileWidget2::plotPicturesInternal(const struct dive *d, bool synchronous // Remove the pictures with the given filenames from the profile plot. void ProfileWidget2::picturesRemoved(dive *d, QVector fileUrls) { - if (d->id != displayed_dive.id) - return; // To remove the pictures, we use the std::remove_if() algorithm. // std::remove_if() does not actually delete the elements, but moves // them to the end of the given range. It returns an iterator to the @@ -2116,9 +2104,6 @@ void ProfileWidget2::picturesRemoved(dive *d, QVector fileUrls) void ProfileWidget2::picturesAdded(dive *d, QVector pics) { - if (d->id != displayed_dive.id) - return; - for (const PictureObj &pic: pics) { if (pic.offset.seconds > 0 && pic.offset.seconds <= d->duration.seconds) { pictures.emplace_back(pic.offset, QString::fromStdString(pic.filename), this, false); @@ -2135,14 +2120,13 @@ void ProfileWidget2::picturesAdded(dive *d, QVector pics) void ProfileWidget2::removePicture(const QString &fileUrl) { - struct dive *d = get_dive_by_uniq_id(displayed_dive.id); if (d) - Command::removePictures({ { d, { fileUrl.toStdString() } } }); + Command::removePictures({ { mutable_dive(), { fileUrl.toStdString() } } }); } -void ProfileWidget2::profileChanged(dive *d) +void ProfileWidget2::profileChanged(dive *dive) { - if (!d || d->id != displayed_dive.id) + if (dive != d) return; // Cylinders of a differnt dive than the shown one changed. replot(); } @@ -2152,7 +2136,7 @@ void ProfileWidget2::profileChanged(dive *d) void ProfileWidget2::dropEvent(QDropEvent *event) { #ifndef SUBSURFACE_MOBILE - if (event->mimeData()->hasFormat("application/x-subsurfaceimagedrop")) { + if (event->mimeData()->hasFormat("application/x-subsurfaceimagedrop") && d) { QByteArray itemData = event->mimeData()->data("application/x-subsurfaceimagedrop"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); @@ -2160,7 +2144,7 @@ void ProfileWidget2::dropEvent(QDropEvent *event) dataStream >> filename; QPointF mappedPos = mapToScene(event->pos()); offset_t offset { (int32_t)lrint(timeAxis->valueAt(mappedPos)) }; - Command::setPictureOffset(current_dive, filename, offset); + Command::setPictureOffset(mutable_dive(), filename, offset); if (event->source() == this) { event->setDropAction(Qt::MoveAction); @@ -2175,13 +2159,13 @@ void ProfileWidget2::dropEvent(QDropEvent *event) } #ifndef SUBSURFACE_MOBILE -void ProfileWidget2::pictureOffsetChanged(dive *d, QString filename, offset_t offset) +void ProfileWidget2::pictureOffsetChanged(dive *dIn, QString filename, offset_t offset) { - if (d->id != displayed_dive.id) + if (dIn != d) return; // Picture of a different dive than the one shown changed. // Calculate time in dive where picture was dropped and whether the new position is during the dive. - bool duringDive = current_dive && offset.seconds > 0 && offset.seconds < current_dive->duration.seconds; + bool duringDive = d && offset.seconds > 0 && offset.seconds < d->duration.seconds; // A picture was drag&dropped onto the profile: We have four cases to consider: // 1a) The image was already shown on the profile and is moved to a different position on the profile. @@ -2266,3 +2250,8 @@ void ProfileWidget2::dragMoveEvent(QDragMoveEvent *event) event->ignore(); } } + +struct dive *ProfileWidget2::mutable_dive() const +{ + return const_cast(d); +} diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index db9949d7b..5065dcb3c 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -80,7 +80,10 @@ public: ~ProfileWidget2(); void resetZoom(); void scale(qreal sx, qreal sy); - void plotDive(const struct dive *d, bool force = false, bool clearPictures = false, bool instant = false); + void plotDive(const struct dive *d, int dc, bool force = false, bool clearPictures = false, bool instant = false); + void setProfileState(const struct dive *d, int dc); + void setPlanState(const struct dive *d, int dc); + void setAddState(const struct dive *d, int dc); void setPrintMode(bool mode, bool grayscale = false); bool getPrintMode() const; bool isPointOutOfBoundaries(const QPointF &point) const; @@ -107,13 +110,10 @@ slots: // Necessary to call from QAction's signals. void actionRequestedReplot(bool triggered); void divesChanged(const QVector &dives, DiveField field); void setEmptyState(); - void setProfileState(); #ifndef SUBSURFACE_MOBILE void plotPictures(); void picturesRemoved(dive *d, QVector filenames); void picturesAdded(dive *d, QVector pics); - void setPlanState(); - void setAddState(); void pointsReset(); void pointInserted(const QModelIndex &parent, int start, int end); void pointsRemoved(const QModelIndex &, int start, int end); @@ -139,6 +139,7 @@ slots: // Necessary to call from QAction's signals. #endif private: + void setProfileState(); // keep currently displayed dive void resizeEvent(QResizeEvent *event) override; #ifndef SUBSURFACE_MOBILE void wheelEvent(QWheelEvent *event) override; @@ -194,6 +195,8 @@ private: // All those here should probably be merged into one structure, // So it's esyer to replicate for more dives later. // In the meantime, keep it here. + const struct dive *d; + int dc; struct plot_info plotInfo; DepthAxis *profileYAxis; PartialGasPressureAxis *gasYAxis; @@ -272,6 +275,12 @@ private: int maxtime; int maxdepth; double fontPrintScale; + + // We store a const pointer to the shown dive. However, the undo commands want + // (understandably) a non-const pointer. Since the profile has a context-menu + // with actions, it needs such a non-const pointer. This function turns the + // currently shown dive into such a pointer. Ugly, yes. + struct dive *mutable_dive() const; }; #endif // PROFILEWIDGET2_H diff --git a/profile-widget/qmlprofile.cpp b/profile-widget/qmlprofile.cpp index 12d0165b5..388b1e731 100644 --- a/profile-widget/qmlprofile.cpp +++ b/profile-widget/qmlprofile.cpp @@ -20,7 +20,7 @@ QMLProfile::QMLProfile(QQuickItem *parent) : { setAntialiasing(true); setFlags(QQuickItem::ItemClipsChildrenToShape | QQuickItem::ItemHasContents ); - m_profileWidget->setProfileState(); + m_profileWidget->setProfileState(nullptr, 0); m_profileWidget->setPrintMode(true); m_profileWidget->setFontPrintScale(fontScale); connect(QMLManager::instance(), &QMLManager::sendScreenChanged, this, &QMLProfile::screenChanged); @@ -135,7 +135,7 @@ void QMLProfile::updateProfile() return; if (verbose) qDebug() << "update profile for dive #" << d->number << "offeset" << QString::number(m_xOffset, 'f', 1) << "/" << QString::number(m_yOffset, 'f', 1); - m_profileWidget->plotDive(d, true); + m_profileWidget->plotDive(d, dc_number, true); } void QMLProfile::setDiveId(int diveId) @@ -189,10 +189,9 @@ void QMLProfile::divesChanged(const QVector &dives, DiveField) for (struct dive *d: dives) { if (d->id == m_diveId) { qDebug() << "dive #" << d->number << "changed, trigger profile update"; - m_profileWidget->plotDive(d, true); + m_profileWidget->plotDive(d, dc_number, true); triggerUpdate(); return; } } - } diff --git a/qt-models/divepicturemodel.cpp b/qt-models/divepicturemodel.cpp index e73377d32..af2813e78 100644 --- a/qt-models/divepicturemodel.cpp +++ b/qt-models/divepicturemodel.cpp @@ -193,7 +193,6 @@ void DivePictureModel::picturesRemoved(dive *d, QVector filenamesIn) endRemoveRows(); toIdx -= j - i; } - copy_dive(current_dive, &displayed_dive); // TODO: Remove once displayed_dive is moved to the planner } // Assumes that pics is sorted! @@ -306,7 +305,6 @@ void DivePictureModel::pictureOffsetChanged(dive *d, const QString filenameIn, o // Update the offset here and in the backend oldPos->offsetSeconds = offset.seconds; - copy_dive(current_dive, &displayed_dive); // TODO: remove once profile can display arbitrary dives // Henceforth we will work with indices instead of iterators int oldIndex = oldPos - pictures.begin();