diff --git a/profile-widget/profilescene.cpp b/profile-widget/profilescene.cpp index bae1630fe..0d93aaabf 100644 --- a/profile-widget/profilescene.cpp +++ b/profile-widget/profilescene.cpp @@ -523,10 +523,11 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, int animSpeed, boo percentageItem->replot(d, currentdc, plotInfo); eventItems.clear(); - struct event *event = currentdc->events; struct gasmix lastgasmix = get_gasmix_at_time(d, currentdc, duration_t{1}); - while (event) { + for (struct event *event = currentdc->events; event; event = event->next) { + if (event->hidden) + continue; // if print mode is selected only draw headings, SP change, gas events or bookmark event if (printMode) { if (empty_string(event->name) || @@ -534,7 +535,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, int animSpeed, boo (same_string(event->name, "SP change") && event->time.seconds == 0) || event_is_gaschange(event) || event->type == SAMPLE_EVENT_BOOKMARK)) { - event = event->next; continue; } } @@ -547,7 +547,6 @@ void ProfileScene::plotDive(const struct dive *dIn, int dcIn, int animSpeed, boo } if (event_is_gaschange(event)) lastgasmix = get_gasmix_from_event(d, event); - event = event->next; } QString dcText = get_dc_nickname(currentdc); @@ -667,3 +666,12 @@ std::vector> ProfileScene::eventsAt(QPointF pos) con } return res; } + +DiveEventItem *ProfileScene::eventAtPosition(QPointF pos) const +{ + for (const auto &item: eventItems) { + if (item->contains(item->mapFromScene(pos))) + return item.get(); + } + return nullptr; +} diff --git a/profile-widget/profilescene.h b/profile-widget/profilescene.h index 915c05742..ff07f4382 100644 --- a/profile-widget/profilescene.h +++ b/profile-widget/profilescene.h @@ -60,6 +60,7 @@ public: double posAtDepth(double depth) const; double yToScreen(double y) const; // For pictures: depth given in fration of displayed range. std::vector> eventsAt(QPointF pos) const; + DiveEventItem *eventAtPosition(QPointF pos) const; // null if no event icon at position. const struct dive *d; int dc; diff --git a/profile-widget/profileview.cpp b/profile-widget/profileview.cpp index 57c7669f6..d550fd06c 100644 --- a/profile-widget/profileview.cpp +++ b/profile-widget/profileview.cpp @@ -2,16 +2,20 @@ #include "profileview.h" #include "pictureitem.h" #include "profilescene.h" +#include "diveeventitem.h" #include "handleitem.h" #include "ruleritem.h" #include "tooltipitem.h" #include "zvalues.h" #include "commands/command.h" #include "core/errorhelper.h" +#include "core/event.h" +#include "core/eventtype.h" #include "core/imagedownloader.h" #include "core/pref.h" #include "core/qthelper.h" // for localFilePath() #include "core/range.h" +#include "core/subsurface-string.h" // for empty_string() #include "core/settings/qPrefDisplay.h" #include "core/settings/qPrefPartialPressureGas.h" #include "core/settings/qPrefTechnicalDetails.h" @@ -27,8 +31,9 @@ #ifndef SUBSURFACE_MOBILE #include "core/device.h" -#include #include +#include +#include #endif static const QColor mouseFollowerColor = QColor(Qt::red).lighter(); @@ -383,22 +388,38 @@ void ProfileView::wheelEvent(QWheelEvent *event) struct MenuEntry { QString text; std::function action; + std::vector subitems; MenuEntry(QString text, std::function action) : text(text), action(std::move(action)) { } + MenuEntry(QString text, std::vector subitems) + : text(text), subitems(std::move(subitems)) + { + } }; +#ifndef SUBSURFACE_MOBILE +static void makeMenu(QMenu &m, const std::vector &entries) +{ + for (const MenuEntry &e: entries) { + if (!e.subitems.empty()) + makeMenu(*m.addMenu(e.text), e.subitems); + else if(e.action) + m.addAction(e.text, [f = e.action] { f(); }); // Dang. Qt doesn't support std::function! + // This is too many indirections for my taste. :( + } +} +#endif + static void execMenu(const std::vector &entries, QPoint pos) { if (entries.empty()) return; + #ifndef SUBSURFACE_MOBILE QMenu m; - for (const MenuEntry &e: entries) { - // Dang. Qt doesn't support std::function! This is too many indirections for my taste. :( - m.addAction(e.text, [f = e.action] { f(); }); - } + makeMenu(m, entries); m.exec(pos); #endif } @@ -420,6 +441,39 @@ void ProfileView::renameCurrentDC() #endif } +// TODO: How should that work on mobile? +void ProfileView::editEventName(struct event *event) +{ +#ifndef SUBSURFACE_MOBILE + bool ok; + // TODO: center on window by passing a QWidget as first argument! + QString newName = QInputDialog::getText(nullptr, tr("Edit name of bookmark"), + tr("Custom name:"), QLineEdit::Normal, + event->name, &ok); + if (ok && !newName.isEmpty()) { + if (newName.length() > 22) { //longer names will display as garbage. + QMessageBox lengthWarning; + lengthWarning.setText(tr("Name is too long!")); + lengthWarning.exec(); + return; + } + Command::renameEvent(mutable_dive(), dc, event, qPrintable(newName)); + } +#endif +} + +// Formats cylinder information for display. +// eg : "Cyl 1 (AL80 EAN32)" +static QString formatCylinderDescription(int i, const cylinder_t *cylinder) +{ + QString label = gettextFromC::tr("Cyl") + QString(" %1").arg(i+1); + if( cylinder != NULL ) { + QString mix = get_gas_string(cylinder->gasmix); + label += QString(" (%2 %3)").arg(QString(cylinder->type.description), mix); + } + return label; +} + void ProfileView::mousePressEvent(QMouseEvent *event) { // Handle dragging of items @@ -449,8 +503,45 @@ void ProfileView::mousePressEvent(QMouseEvent *event) } if (currentdc->deviceid) m.emplace_back(tr("Rename this dive computer"), [this] { renameCurrentDC(); }); - execMenu(m, event->globalPos()); - return; + return execMenu(m, event->globalPos()); + } + + DiveEventItem *item = profileScene->eventAtPosition(event->pos()); + if (d && item) { + std::vector m; + const struct event *ev = item->getEvent(); + if (event_is_gaschange(ev)) { + std::vector gas_menu; + for (int i = 0; i < d->cylinders.nr; i++) { + const cylinder_t *cylinder = get_cylinder(d, i); + QString label = formatCylinderDescription(i, cylinder); + gas_menu.emplace_back(label, [this, i, time = ev->time.seconds] { + Command::addGasSwitch(mutable_dive(), dc, time, i); + }); + } + m.emplace_back(tr("Edit Gas Change"), std::move(gas_menu)); + } + + m.emplace_back(tr("Remove event"), [this, item] { + Command::removeEvent(mutable_dive(), dc, item->getEventMutable()); + }); + m.emplace_back(tr("Hide event"), [this, item] { + item->getEventMutable()->hidden = true; + replot(); + }); + if (!empty_string(item->getEvent()->name)) { + m.emplace_back(tr("Hide events of type '%1'").arg(event_type_name(ev)), [this, item] { + hide_event_type(item->getEvent()); + replot(); + }); + } + if (ev->type == SAMPLE_EVENT_BOOKMARK) { + m.emplace_back(tr("Edit name"), [this, item] { + editEventName(item->getEventMutable()); + }); + } + + return execMenu(m, event->globalPos()); } // Check if current picture is clicked diff --git a/profile-widget/profileview.h b/profile-widget/profileview.h index 7d0b30d4d..6c58bb579 100644 --- a/profile-widget/profileview.h +++ b/profile-widget/profileview.h @@ -136,6 +136,9 @@ private: // DC related void renameCurrentDC(); + // Event related + void editEventName(struct event *event); + // The list of pictures in this plot. The pictures are sorted by offset in seconds. // For the same offset, sort by filename. // Pictures that are outside of the dive time are not shown. diff --git a/profile-widget/profilewidget2.cpp b/profile-widget/profilewidget2.cpp index a32334743..c215d63dd 100644 --- a/profile-widget/profilewidget2.cpp +++ b/profile-widget/profilewidget2.cpp @@ -335,17 +335,6 @@ struct int ProfileWidget2::getEntryFromPos(QPointF pos) #endif #ifndef SUBSURFACE_MOBILE -/// Prints cylinder information for display. -/// eg : "Cyl 1 (AL80 EAN32)" -static QString printCylinderDescription(int i, const cylinder_t *cylinder) -{ - QString label = gettextFromC::tr("Cyl") + QString(" %1").arg(i+1); - if( cylinder != NULL ) { - QString mix = get_gas_string(cylinder->gasmix); - label += QString(" (%2 %3)").arg(cylinder->type.description).arg(mix); - } - return label; -} static bool isDiveTextItem(const QGraphicsItem *item, const DiveTextItem *textItem) { @@ -375,13 +364,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) // Add or edit Gas Change 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 < 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 (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")); @@ -409,56 +391,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) changeMode->addAction(gettextFromC::tr(divemode_text_ui[PSCR]), [this, seconds](){ addDivemodeSwitch(seconds, PSCR); }); - if (DiveEventItem *item = dynamic_cast(sceneItem)) { - const struct event *dcEvent = item->getEvent(); - m.addAction(tr("Remove event"), [this,item] { removeEvent(item); }); - m.addAction(tr("Hide event"), [this, item] { hideEvent(item); }); - m.addAction(tr("Hide events of type '%1'").arg(event_type_name(dcEvent)), - [this, item] { hideEventType(item); }); - if (dcEvent->type == SAMPLE_EVENT_BOOKMARK) - m.addAction(tr("Edit name"), [this, item] { editName(item); }); -#if 0 // TODO::: FINISH OR DISABLE - QPointF scenePos = mapToScene(event->pos()); - int idx = getEntryFromPos(scenePos); - // this shows how to figure out if we should ask the user if they want adjust interpolated pressures - // at either side of a gas change - if (dcEvent->type == SAMPLE_EVENT_GASCHANGE || dcEvent->type == SAMPLE_EVENT_GASCHANGE2) { - qDebug() << "figure out if there are interpolated pressures"; - int gasChangeIdx = idx; - while (gasChangeIdx > 0) { - --gasChangeIdx; - if (plotInfo.entry[gasChangeIdx].sec <= dcEvent->time.seconds) - break; - } - const struct plot_data &gasChangeEntry = plotInfo.entry[newGasIdx]; - qDebug() << "at gas change at" << gasChangeEntry->sec << ": sensor pressure" << get_plot_sensor_pressure(&plotInfo, newGasIdx) - << "interpolated" << ;get_plot_sensor_pressure(&plotInfo, newGasIdx); - // now gasChangeEntry points at the gas change, that entry has the final pressure of - // the old tank, the next entry has the starting pressure of the next tank - if (gasChangeIdx < plotInfo.nr - 1) { - 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(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 - pressure_t pressure; - pressure.mbar = get_plot_interpolated_pressure(&plotInfo, gasChangeIdx) ? : get_plot_sensor_pressure(&plotInfo, gasChangeIdx); - 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(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); - QAction *adjustOldPressure = m.addAction(tr("Adjust pressure of cyl. %1 (currently interpolated as %2)") - .arg(newGasEntry->sensor[0] + 1).arg(get_pressure_string(pressure))); - } - } - } -#endif - } if (any_event_types_hidden()) { QMenu *m2 = m.addMenu(tr("Unhide event type")); for (int i: hidden_event_types()) { @@ -475,23 +407,6 @@ void ProfileWidget2::contextMenuEvent(QContextMenuEvent *event) m.exec(event->globalPos()); } -void ProfileWidget2::hideEvent(DiveEventItem *item) -{ - item->getEventMutable()->hidden = true; - item->hide(); -} - -void ProfileWidget2::hideEventType(DiveEventItem *item) -{ - const struct event *event = item->getEvent(); - - if (!empty_string(event->name)) { - hide_event_type(event); - - replot(); - } -} - void ProfileWidget2::unhideEvents() { for (DiveEventItem *item: profileScene->eventItems) { @@ -507,19 +422,6 @@ void ProfileWidget2::unhideEventTypes() replot(); } -void ProfileWidget2::removeEvent(DiveEventItem *item) -{ - struct event *event = item->getEventMutable(); - 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(mutable_dive(), dc, event); -} - void ProfileWidget2::addBookmark(int seconds) { if (d) @@ -547,35 +449,6 @@ void ProfileWidget2::splitDive(int seconds) Command::splitDives(mutable_dive(), duration_t{ seconds }); } -void ProfileWidget2::changeGas(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::editName(DiveEventItem *item) -{ - struct event *event = item->getEventMutable(); - if (!event || !d) - return; - bool ok; - QString newName = QInputDialog::getText(this, tr("Edit name of bookmark"), - tr("Custom name:"), QLineEdit::Normal, - event->name, &ok); - if (ok && !newName.isEmpty()) { - if (newName.length() > 22) { //longer names will display as garbage. - QMessageBox lengthWarning; - lengthWarning.setText(tr("Name is too long!")); - lengthWarning.exec(); - return; - } - Command::renameEvent(mutable_dive(), dc, event, qPrintable(newName)); - } -} #endif #ifndef SUBSURFACE_MOBILE diff --git a/profile-widget/profilewidget2.h b/profile-widget/profilewidget2.h index d9138fbe7..4134ac44c 100644 --- a/profile-widget/profilewidget2.h +++ b/profile-widget/profilewidget2.h @@ -107,8 +107,6 @@ private: void splitDive(int seconds); void addSetpointChange(int seconds); void removeEvent(DiveEventItem *item); - void hideEvent(DiveEventItem *item); - void hideEventType(DiveEventItem *item); void editName(DiveEventItem *item); void unhideEvents(); void unhideEventTypes();