profle: port event manipulation to QtQuick
UI change: the context menu is opened when left-clicking on the event. This is probably more compatible with mobile UI. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
8580364481
commit
376da40e43
@ -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<std::pair<QString, QPixmap>> 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;
|
||||
}
|
||||
|
||||
@ -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<std::pair<QString, QPixmap>> eventsAt(QPointF pos) const;
|
||||
DiveEventItem *eventAtPosition(QPointF pos) const; // null if no event icon at position.
|
||||
|
||||
const struct dive *d;
|
||||
int dc;
|
||||
|
||||
@ -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 <QMenu>
|
||||
#include <QInputDialog>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
|
||||
static const QColor mouseFollowerColor = QColor(Qt::red).lighter();
|
||||
@ -383,22 +388,38 @@ void ProfileView::wheelEvent(QWheelEvent *event)
|
||||
struct MenuEntry {
|
||||
QString text;
|
||||
std::function<void()> action;
|
||||
std::vector<MenuEntry> subitems;
|
||||
MenuEntry(QString text, std::function<void()> action)
|
||||
: text(text), action(std::move(action))
|
||||
{
|
||||
}
|
||||
MenuEntry(QString text, std::vector<MenuEntry> subitems)
|
||||
: text(text), subitems(std::move(subitems))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef SUBSURFACE_MOBILE
|
||||
static void makeMenu(QMenu &m, const std::vector<MenuEntry> &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<MenuEntry> &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<MenuEntry> m;
|
||||
const struct event *ev = item->getEvent();
|
||||
if (event_is_gaschange(ev)) {
|
||||
std::vector<MenuEntry> 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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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<DiveEventItem *>(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
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user