2021-04-12 21:34:28 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
|
|
|
|
#include "profilewidget.h"
|
|
|
|
#include "profile-widget/profilewidget2.h"
|
2022-02-19 10:58:36 +00:00
|
|
|
#include "commands/command.h"
|
2021-08-03 07:51:37 +00:00
|
|
|
#include "core/color.h"
|
2021-04-12 21:34:28 +00:00
|
|
|
#include "core/settings/qPrefTechnicalDetails.h"
|
|
|
|
#include "core/settings/qPrefPartialPressureGas.h"
|
2022-02-19 10:58:36 +00:00
|
|
|
#include "core/subsurface-string.h"
|
2021-04-12 21:34:28 +00:00
|
|
|
#include "qt-models/diveplannermodel.h"
|
|
|
|
|
|
|
|
#include <QToolBar>
|
|
|
|
#include <QHBoxLayout>
|
2021-06-30 05:17:40 +00:00
|
|
|
#include <QStackedWidget>
|
|
|
|
#include <QLabel>
|
|
|
|
|
|
|
|
// A resizing display of the Subsurface logo when no dive is shown
|
|
|
|
class EmptyView : public QLabel {
|
|
|
|
public:
|
|
|
|
EmptyView(QWidget *parent = nullptr);
|
|
|
|
~EmptyView();
|
|
|
|
private:
|
|
|
|
QPixmap logo;
|
|
|
|
void update();
|
|
|
|
void resizeEvent(QResizeEvent *) override;
|
|
|
|
};
|
|
|
|
|
|
|
|
EmptyView::EmptyView(QWidget *parent) : QLabel(parent),
|
|
|
|
logo(":poster-icon")
|
|
|
|
{
|
|
|
|
QPalette pal;
|
|
|
|
pal.setColor(QPalette::Window, getColor(::BACKGROUND));
|
|
|
|
setAutoFillBackground(true);
|
|
|
|
setPalette(pal);
|
|
|
|
setMinimumSize(1,1);
|
|
|
|
setAlignment(Qt::AlignHCenter);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
EmptyView::~EmptyView()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void EmptyView::update()
|
|
|
|
{
|
|
|
|
setPixmap(logo.scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
|
|
|
}
|
|
|
|
|
|
|
|
void EmptyView::resizeEvent(QResizeEvent *)
|
|
|
|
{
|
|
|
|
update();
|
|
|
|
}
|
2021-04-12 21:34:28 +00:00
|
|
|
|
2022-03-02 19:50:50 +00:00
|
|
|
ProfileWidget::ProfileWidget() : originalDive(nullptr), placingCommand(false)
|
2021-04-12 21:34:28 +00:00
|
|
|
{
|
|
|
|
ui.setupUi(this);
|
|
|
|
|
|
|
|
// what is a sane order for those icons? we should have the ones the user is
|
|
|
|
// most likely to want towards the top so they are always visible
|
|
|
|
// and the ones that someone likely sets and then never touches again towards the bottom
|
|
|
|
toolbarActions = { ui.profCalcCeiling, ui.profCalcAllTissues, // start with various ceilings
|
|
|
|
ui.profIncrement3m, ui.profDcCeiling,
|
|
|
|
ui.profPhe, ui.profPn2, ui.profPO2, // partial pressure graphs
|
|
|
|
ui.profRuler, ui.profScaled, // measuring and scaling
|
|
|
|
ui.profTogglePicture, ui.profTankbar,
|
|
|
|
ui.profMod, ui.profDeco, ui.profNdl_tts, // various values that a user is either interested in or not
|
|
|
|
ui.profEad, ui.profSAC,
|
|
|
|
ui.profHR, // very few dive computers support this
|
|
|
|
ui.profTissues }; // maybe less frequently used
|
|
|
|
|
2021-06-30 05:17:40 +00:00
|
|
|
emptyView.reset(new EmptyView);
|
|
|
|
|
2021-05-31 20:29:00 +00:00
|
|
|
view.reset(new ProfileWidget2(DivePlannerPointsModel::instance(), 1.0, this));
|
2021-04-12 21:34:28 +00:00
|
|
|
QToolBar *toolBar = new QToolBar(this);
|
|
|
|
for (QAction *a: toolbarActions)
|
|
|
|
toolBar->addAction(a);
|
|
|
|
toolBar->setOrientation(Qt::Vertical);
|
|
|
|
toolBar->setIconSize(QSize(24, 24));
|
2021-06-30 05:17:40 +00:00
|
|
|
|
|
|
|
stack = new QStackedWidget(this);
|
|
|
|
stack->addWidget(emptyView.get());
|
|
|
|
stack->addWidget(view.get());
|
|
|
|
|
2021-04-12 21:34:28 +00:00
|
|
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
|
|
|
layout->setSpacing(0);
|
2022-02-10 01:00:48 +00:00
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
2021-04-12 21:34:28 +00:00
|
|
|
layout->setMargin(0);
|
2022-02-10 01:00:48 +00:00
|
|
|
#endif
|
2021-04-12 21:34:28 +00:00
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
layout->addWidget(toolBar);
|
2021-06-30 05:17:40 +00:00
|
|
|
layout->addWidget(stack);
|
2021-04-12 21:34:28 +00:00
|
|
|
setLayout(layout);
|
|
|
|
|
|
|
|
// Toolbar Connections related to the Profile Update
|
|
|
|
auto tec = qPrefTechnicalDetails::instance();
|
|
|
|
connect(ui.profCalcAllTissues, &QAction::triggered, tec, &qPrefTechnicalDetails::set_calcalltissues);
|
|
|
|
connect(ui.profCalcCeiling, &QAction::triggered, tec, &qPrefTechnicalDetails::set_calcceiling);
|
|
|
|
connect(ui.profDcCeiling, &QAction::triggered, tec, &qPrefTechnicalDetails::set_dcceiling);
|
|
|
|
connect(ui.profEad, &QAction::triggered, tec, &qPrefTechnicalDetails::set_ead);
|
|
|
|
connect(ui.profIncrement3m, &QAction::triggered, tec, &qPrefTechnicalDetails::set_calcceiling3m);
|
|
|
|
connect(ui.profMod, &QAction::triggered, tec, &qPrefTechnicalDetails::set_mod);
|
|
|
|
connect(ui.profNdl_tts, &QAction::triggered, tec, &qPrefTechnicalDetails::set_calcndltts);
|
|
|
|
connect(ui.profDeco, &QAction::triggered, tec, &qPrefTechnicalDetails::set_decoinfo);
|
|
|
|
connect(ui.profHR, &QAction::triggered, tec, &qPrefTechnicalDetails::set_hrgraph);
|
|
|
|
connect(ui.profRuler, &QAction::triggered, tec, &qPrefTechnicalDetails::set_rulergraph);
|
|
|
|
connect(ui.profSAC, &QAction::triggered, tec, &qPrefTechnicalDetails::set_show_sac);
|
|
|
|
connect(ui.profScaled, &QAction::triggered, tec, &qPrefTechnicalDetails::set_zoomed_plot);
|
|
|
|
connect(ui.profTogglePicture, &QAction::triggered, tec, &qPrefTechnicalDetails::set_show_pictures_in_profile);
|
|
|
|
connect(ui.profTankbar, &QAction::triggered, tec, &qPrefTechnicalDetails::set_tankbar);
|
|
|
|
connect(ui.profTissues, &QAction::triggered, tec, &qPrefTechnicalDetails::set_percentagegraph);
|
|
|
|
|
|
|
|
connect(ui.profTissues, &QAction::triggered, this, &ProfileWidget::unsetProfHR);
|
|
|
|
connect(ui.profHR, &QAction::triggered, this, &ProfileWidget::unsetProfTissues);
|
|
|
|
|
|
|
|
auto pp_gas = qPrefPartialPressureGas::instance();
|
|
|
|
connect(ui.profPhe, &QAction::triggered, pp_gas, &qPrefPartialPressureGas::set_phe);
|
|
|
|
connect(ui.profPn2, &QAction::triggered, pp_gas, &qPrefPartialPressureGas::set_pn2);
|
|
|
|
connect(ui.profPO2, &QAction::triggered, pp_gas, &qPrefPartialPressureGas::set_po2);
|
|
|
|
|
2022-03-02 19:50:50 +00:00
|
|
|
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &ProfileWidget::divesChanged);
|
2021-04-12 21:34:28 +00:00
|
|
|
connect(&diveListNotifier, &DiveListNotifier::settingsChanged, view.get(), &ProfileWidget2::settingsChanged);
|
2022-02-19 10:58:36 +00:00
|
|
|
connect(view.get(), &ProfileWidget2::stopAdded, this, &ProfileWidget::stopAdded);
|
|
|
|
connect(view.get(), &ProfileWidget2::stopRemoved, this, &ProfileWidget::stopRemoved);
|
|
|
|
connect(view.get(), &ProfileWidget2::stopMoved, this, &ProfileWidget::stopMoved);
|
2021-04-12 21:34:28 +00:00
|
|
|
|
|
|
|
ui.profCalcAllTissues->setChecked(qPrefTechnicalDetails::calcalltissues());
|
|
|
|
ui.profCalcCeiling->setChecked(qPrefTechnicalDetails::calcceiling());
|
|
|
|
ui.profDcCeiling->setChecked(qPrefTechnicalDetails::dcceiling());
|
|
|
|
ui.profEad->setChecked(qPrefTechnicalDetails::ead());
|
|
|
|
ui.profIncrement3m->setChecked(qPrefTechnicalDetails::calcceiling3m());
|
|
|
|
ui.profMod->setChecked(qPrefTechnicalDetails::mod());
|
|
|
|
ui.profNdl_tts->setChecked(qPrefTechnicalDetails::calcndltts());
|
|
|
|
ui.profDeco->setChecked(qPrefTechnicalDetails::decoinfo());
|
|
|
|
ui.profPhe->setChecked(pp_gas->phe());
|
|
|
|
ui.profPn2->setChecked(pp_gas->pn2());
|
|
|
|
ui.profPO2->setChecked(pp_gas->po2());
|
|
|
|
ui.profHR->setChecked(qPrefTechnicalDetails::hrgraph());
|
|
|
|
ui.profRuler->setChecked(qPrefTechnicalDetails::rulergraph());
|
|
|
|
ui.profSAC->setChecked(qPrefTechnicalDetails::show_sac());
|
|
|
|
ui.profTogglePicture->setChecked(qPrefTechnicalDetails::show_pictures_in_profile());
|
|
|
|
ui.profTankbar->setChecked(qPrefTechnicalDetails::tankbar());
|
|
|
|
ui.profTissues->setChecked(qPrefTechnicalDetails::percentagegraph());
|
|
|
|
ui.profScaled->setChecked(qPrefTechnicalDetails::zoomed_plot());
|
|
|
|
}
|
|
|
|
|
|
|
|
ProfileWidget::~ProfileWidget()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::setEnabledToolbar(bool enabled)
|
|
|
|
{
|
|
|
|
for (QAction *b: toolbarActions)
|
|
|
|
b->setEnabled(enabled);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::setDive(const struct dive *d)
|
|
|
|
{
|
2022-02-19 10:58:36 +00:00
|
|
|
// If the user was currently editing a dive, exit edit mode.
|
2021-04-12 21:34:28 +00:00
|
|
|
stack->setCurrentIndex(1); // show profile
|
|
|
|
|
|
|
|
bool freeDiveMode = d->dc.divemode == FREEDIVE;
|
|
|
|
ui.profCalcCeiling->setDisabled(freeDiveMode);
|
|
|
|
ui.profCalcCeiling->setDisabled(freeDiveMode);
|
|
|
|
ui.profCalcAllTissues ->setDisabled(freeDiveMode);
|
|
|
|
ui.profIncrement3m->setDisabled(freeDiveMode);
|
|
|
|
ui.profDcCeiling->setDisabled(freeDiveMode);
|
|
|
|
ui.profPhe->setDisabled(freeDiveMode);
|
|
|
|
ui.profPn2->setDisabled(freeDiveMode); //TODO is the same as scuba?
|
|
|
|
ui.profPO2->setDisabled(freeDiveMode); //TODO is the same as scuba?
|
|
|
|
ui.profTankbar->setDisabled(freeDiveMode);
|
|
|
|
ui.profMod->setDisabled(freeDiveMode);
|
|
|
|
ui.profNdl_tts->setDisabled(freeDiveMode);
|
|
|
|
ui.profDeco->setDisabled(freeDiveMode);
|
|
|
|
ui.profEad->setDisabled(freeDiveMode);
|
|
|
|
ui.profSAC->setDisabled(freeDiveMode);
|
|
|
|
ui.profTissues->setDisabled(freeDiveMode);
|
|
|
|
|
|
|
|
ui.profRuler->setDisabled(false);
|
|
|
|
ui.profScaled->setDisabled(false); // measuring and scaling
|
|
|
|
ui.profTogglePicture->setDisabled(false);
|
|
|
|
ui.profHR->setDisabled(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::plotCurrentDive()
|
|
|
|
{
|
2022-02-19 10:58:36 +00:00
|
|
|
// Exit edit mode if the dive changed
|
2022-03-11 20:45:46 +00:00
|
|
|
if (editedDive && (originalDive != current_dive || editedDc != dc_number))
|
2022-02-19 10:58:36 +00:00
|
|
|
exitEditMode();
|
|
|
|
|
2022-03-11 20:45:46 +00:00
|
|
|
// If this is a manually added dive and we are not in the planner
|
|
|
|
// or already editing the dive, switch to edit mode.
|
|
|
|
if (current_dive && !editedDive &&
|
|
|
|
DivePlannerPointsModel::instance()->currentMode() == DivePlannerPointsModel::NOTHING) {
|
|
|
|
struct divecomputer *dc = get_dive_dc(current_dive, dc_number);
|
2022-10-20 22:39:30 +00:00
|
|
|
if (dc && is_manually_added_dc(dc) && dc->samples)
|
2022-03-11 20:45:46 +00:00
|
|
|
editDive();
|
|
|
|
}
|
|
|
|
|
2021-04-12 21:34:28 +00:00
|
|
|
setEnabledToolbar(current_dive != nullptr);
|
2022-02-19 10:58:36 +00:00
|
|
|
if (editedDive) {
|
2022-03-13 19:31:36 +00:00
|
|
|
setDive(originalDive);
|
2022-02-19 10:58:36 +00:00
|
|
|
view->plotDive(editedDive.get(), editedDc);
|
|
|
|
} else if (current_dive) {
|
2022-02-13 13:21:41 +00:00
|
|
|
setDive(current_dive);
|
2021-08-08 13:22:15 +00:00
|
|
|
view->setProfileState(current_dive, dc_number);
|
2021-10-04 19:45:15 +00:00
|
|
|
view->resetZoom(); // when switching dive, reset the zoomLevel
|
2021-06-30 05:17:40 +00:00
|
|
|
view->plotDive(current_dive, dc_number);
|
|
|
|
} else {
|
2021-08-08 13:22:15 +00:00
|
|
|
view->clear();
|
2021-06-30 05:17:40 +00:00
|
|
|
stack->setCurrentIndex(0);
|
|
|
|
}
|
2021-04-12 21:34:28 +00:00
|
|
|
}
|
|
|
|
|
2022-03-02 19:50:50 +00:00
|
|
|
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 (!current_dive || !dives.contains(current_dive) || !(field.duration || field.depth) || placingCommand)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If were editing the current dive and not currently
|
|
|
|
// placing command, we have to update the edited dive.
|
|
|
|
if (editedDive) {
|
|
|
|
copy_dive(current_dive, editedDive.get());
|
|
|
|
// TODO: Holy moly that function sends too many signals. Fix it!
|
|
|
|
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
plotCurrentDive();
|
|
|
|
}
|
|
|
|
|
2021-04-12 21:34:28 +00:00
|
|
|
void ProfileWidget::setPlanState(const struct dive *d, int dc)
|
|
|
|
{
|
2022-02-19 10:58:36 +00:00
|
|
|
exitEditMode();
|
2021-04-12 21:34:28 +00:00
|
|
|
setDive(d);
|
2022-02-19 10:58:36 +00:00
|
|
|
view->setPlanState(d, dc);
|
2021-04-12 21:34:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::unsetProfHR()
|
|
|
|
{
|
|
|
|
ui.profHR->setChecked(false);
|
|
|
|
qPrefTechnicalDetails::set_hrgraph(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::unsetProfTissues()
|
|
|
|
{
|
|
|
|
ui.profTissues->setChecked(false);
|
|
|
|
qPrefTechnicalDetails::set_percentagegraph(false);
|
|
|
|
}
|
2022-02-19 10:58:36 +00:00
|
|
|
|
|
|
|
void ProfileWidget::editDive()
|
|
|
|
{
|
|
|
|
editedDive.reset(alloc_dive());
|
|
|
|
editedDc = dc_number;
|
|
|
|
copy_dive(current_dive, editedDive.get()); // Work on a copy of the dive
|
|
|
|
originalDive = current_dive;
|
|
|
|
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::ADD);
|
|
|
|
DivePlannerPointsModel::instance()->loadFromDive(editedDive.get());
|
2022-03-11 20:45:46 +00:00
|
|
|
view->setEditState(editedDive.get(), editedDc);
|
2022-02-19 10:58:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::exitEditMode()
|
|
|
|
{
|
|
|
|
if (!editedDive)
|
|
|
|
return;
|
|
|
|
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::NOTHING);
|
|
|
|
view->setProfileState(current_dive, dc_number); // switch back to original dive before erasing the copy.
|
|
|
|
editedDive.reset();
|
|
|
|
originalDive = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update depths of edited dive
|
|
|
|
static void calcDepth(dive &d, int dcNr)
|
|
|
|
{
|
|
|
|
d.maxdepth.mm = get_dive_dc(&d, dcNr)->maxdepth.mm = 0;
|
|
|
|
fixup_dive(&d);
|
|
|
|
}
|
|
|
|
|
2022-03-02 19:50:50 +00:00
|
|
|
// Silly RAII-variable setter class: reset variable when going out of scope.
|
|
|
|
template <typename T>
|
|
|
|
struct Setter {
|
|
|
|
T &var, old;
|
|
|
|
Setter(T &var, T value) : var(var), old(var) {
|
|
|
|
var = value;
|
|
|
|
}
|
|
|
|
~Setter() {
|
|
|
|
var = old;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-02-19 10:58:36 +00:00
|
|
|
void ProfileWidget::stopAdded()
|
|
|
|
{
|
|
|
|
if (!editedDive)
|
|
|
|
return;
|
|
|
|
calcDepth(*editedDive, editedDc);
|
2022-03-02 19:50:50 +00:00
|
|
|
Setter s(placingCommand, true);
|
2022-02-19 10:58:36 +00:00
|
|
|
Command::editProfile(editedDive.get(), Command::EditProfileType::ADD, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::stopRemoved(int count)
|
|
|
|
{
|
|
|
|
if (!editedDive)
|
|
|
|
return;
|
|
|
|
calcDepth(*editedDive, editedDc);
|
2022-03-02 19:50:50 +00:00
|
|
|
Setter s(placingCommand, true);
|
2022-02-19 10:58:36 +00:00
|
|
|
Command::editProfile(editedDive.get(), Command::EditProfileType::REMOVE, count);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileWidget::stopMoved(int count)
|
|
|
|
{
|
|
|
|
if (!editedDive)
|
|
|
|
return;
|
|
|
|
calcDepth(*editedDive, editedDc);
|
2022-03-02 19:50:50 +00:00
|
|
|
Setter s(placingCommand, true);
|
2022-02-19 10:58:36 +00:00
|
|
|
Command::editProfile(editedDive.get(), Command::EditProfileType::MOVE, count);
|
|
|
|
}
|