mirror of
https://github.com/subsurface/subsurface.git
synced 2025-02-19 22:16:15 +00:00
This tries to encapsulate the management of the current dive and divecomputer in the selection code. The current dive is alreay set by setSelection(). Add a new parameter to also set the current divecomputer. If -1 is passed, then the current computer number is remained. This will allow us to audit the code. Because for now, the whole "current dive computer" thing seems to be ill-defined. This fixes a bug: the dive-computer number wasn't validated when making a new dive the current dive. The new code has some drawbacks though: when selecting a whole trip, the validation will be called for all dives in the trip and thus the dive computer number will depend on the dive with the lowest amount of dive computers in the trip. This will need to be fixed. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
319 lines
11 KiB
C++
319 lines
11 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
#include "profilewidget.h"
|
|
#include "profile-widget/profilewidget2.h"
|
|
#include "commands/command.h"
|
|
#include "core/color.h"
|
|
#include "core/selection.h"
|
|
#include "core/settings/qPrefTechnicalDetails.h"
|
|
#include "core/settings/qPrefPartialPressureGas.h"
|
|
#include "core/subsurface-string.h"
|
|
#include "qt-models/diveplannermodel.h"
|
|
|
|
#include <QToolBar>
|
|
#include <QHBoxLayout>
|
|
#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();
|
|
}
|
|
|
|
ProfileWidget::ProfileWidget() : originalDive(nullptr), placingCommand(false)
|
|
{
|
|
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
|
|
|
|
emptyView.reset(new EmptyView);
|
|
|
|
view.reset(new ProfileWidget2(DivePlannerPointsModel::instance(), 1.0, this));
|
|
QToolBar *toolBar = new QToolBar(this);
|
|
for (QAction *a: toolbarActions)
|
|
toolBar->addAction(a);
|
|
toolBar->setOrientation(Qt::Vertical);
|
|
toolBar->setIconSize(QSize(24, 24));
|
|
|
|
stack = new QStackedWidget(this);
|
|
stack->addWidget(emptyView.get());
|
|
stack->addWidget(view.get());
|
|
|
|
QHBoxLayout *layout = new QHBoxLayout(this);
|
|
layout->setSpacing(0);
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
layout->setMargin(0);
|
|
#endif
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
layout->addWidget(toolBar);
|
|
layout->addWidget(stack);
|
|
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);
|
|
|
|
connect(&diveListNotifier, &DiveListNotifier::divesChanged, this, &ProfileWidget::divesChanged);
|
|
connect(&diveListNotifier, &DiveListNotifier::settingsChanged, view.get(), &ProfileWidget2::settingsChanged);
|
|
connect(view.get(), &ProfileWidget2::stopAdded, this, &ProfileWidget::stopAdded);
|
|
connect(view.get(), &ProfileWidget2::stopRemoved, this, &ProfileWidget::stopRemoved);
|
|
connect(view.get(), &ProfileWidget2::stopMoved, this, &ProfileWidget::stopMoved);
|
|
|
|
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)
|
|
{
|
|
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()
|
|
{
|
|
// Exit edit mode if the dive changed
|
|
if (editedDive && (originalDive != current_dive || editedDc != dc_number))
|
|
exitEditMode();
|
|
|
|
// 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);
|
|
if (dc && is_manually_added_dc(dc) && dc->samples)
|
|
editDive();
|
|
}
|
|
|
|
setEnabledToolbar(current_dive != nullptr);
|
|
if (editedDive) {
|
|
view->plotDive(editedDive.get(), editedDc);
|
|
setDive(editedDive.get());
|
|
} else if (current_dive) {
|
|
view->setProfileState(current_dive, dc_number);
|
|
view->resetZoom(); // when switching dive, reset the zoomLevel
|
|
view->plotDive(current_dive, dc_number);
|
|
setDive(current_dive);
|
|
} else {
|
|
view->clear();
|
|
stack->setCurrentIndex(0);
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
void ProfileWidget::setPlanState(const struct dive *d, int dc)
|
|
{
|
|
exitEditMode();
|
|
view->setPlanState(d, dc);
|
|
setDive(d);
|
|
}
|
|
|
|
void ProfileWidget::unsetProfHR()
|
|
{
|
|
ui.profHR->setChecked(false);
|
|
qPrefTechnicalDetails::set_hrgraph(false);
|
|
}
|
|
|
|
void ProfileWidget::unsetProfTissues()
|
|
{
|
|
ui.profTissues->setChecked(false);
|
|
qPrefTechnicalDetails::set_percentagegraph(false);
|
|
}
|
|
|
|
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());
|
|
view->setEditState(editedDive.get(), editedDc);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
};
|
|
|
|
void ProfileWidget::stopAdded()
|
|
{
|
|
if (!editedDive)
|
|
return;
|
|
calcDepth(*editedDive, editedDc);
|
|
Setter s(placingCommand, true);
|
|
Command::editProfile(editedDive.get(), Command::EditProfileType::ADD, 0);
|
|
}
|
|
|
|
void ProfileWidget::stopRemoved(int count)
|
|
{
|
|
if (!editedDive)
|
|
return;
|
|
calcDepth(*editedDive, editedDc);
|
|
Setter s(placingCommand, true);
|
|
Command::editProfile(editedDive.get(), Command::EditProfileType::REMOVE, count);
|
|
}
|
|
|
|
void ProfileWidget::stopMoved(int count)
|
|
{
|
|
if (!editedDive)
|
|
return;
|
|
calcDepth(*editedDive, editedDc);
|
|
Setter s(placingCommand, true);
|
|
Command::editProfile(editedDive.get(), Command::EditProfileType::MOVE, count);
|
|
}
|