mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-28 05:00:20 +00:00
764aa6c512
The way the starting time of a new plan was set was bonkers: 1) PlannerWidgets::planDive() invokes DivePlannerPointsModel:: createSimpleDive(). 2) createSimpleDive() calls DivePlannerPointsModel:: setupStartTime() 3) setupStartTime() emits a signal startTimeChanged() 4) startTimeChanged is caught by PlannerWidget and sets the UI field 5) change of the UI field emits a timeChanged() signal which is connected to DivePlannerPointsModel::setStartTime() 6) setStartTime() sets the time of the plan and displayed_dive and emits dataChanged() 7) dataChanged() replots the dive() 8) Back in DivePlannerPointsModel::createSimpleDive() the diveplan start time is overwritten with displayed_dive (the value are equal owing to 6) Wow! But it gets worse: 9) The initial dive plan is set up in createSimpleDive(). Since the profile is drawn in 7) after clearing the displayed_dive and before constructing the initial plan, the profile is shown on a dive without samples. It therefore generates a dummy profile. To make this somewhat less insane, remove the startTimeChanged() signal in 3), explicitly set the start time of plan and dive to the one calculated by setupStartTime() and explicitly set the UI filed in the plannerWidget. This still indirectly draws the profile via signals in a convoluted way, but at it straightens out things somewhat. Most importantly, the profile doesn't have to generate a fake DC. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
637 lines
27 KiB
C++
637 lines
27 KiB
C++
// SPDX-License-Identifier: GPL-2.0
|
||
#include "desktop-widgets/diveplanner.h"
|
||
#include "desktop-widgets/modeldelegates.h"
|
||
#include "desktop-widgets/mainwindow.h"
|
||
#include "core/planner.h"
|
||
#include "core/qthelper.h"
|
||
#include "core/units.h"
|
||
#include "core/settings/qPrefDivePlanner.h"
|
||
#include "core/subsurface-qt/divelistnotifier.h"
|
||
#include "core/gettextfromc.h"
|
||
#include "backend-shared/plannershared.h"
|
||
|
||
#include "qt-models/cylindermodel.h"
|
||
#include "qt-models/models.h"
|
||
#include "profile-widget/profilewidget2.h"
|
||
#include "qt-models/diveplannermodel.h"
|
||
|
||
#include <QShortcut>
|
||
#ifndef NO_PRINTING
|
||
#include <QPrintDialog>
|
||
#include <QPrinter>
|
||
#include <QBuffer>
|
||
#endif
|
||
|
||
DivePlannerWidget::DivePlannerWidget(QWidget *parent) : QWidget(parent, QFlag(0))
|
||
{
|
||
DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance();
|
||
CylindersModel *cylinders = DivePlannerPointsModel::instance()->cylindersModel();
|
||
|
||
ui.setupUi(this);
|
||
ui.dateEdit->setDisplayFormat(prefs.date_format);
|
||
ui.tableWidget->setTitle(tr("Dive planner points"));
|
||
ui.tableWidget->setModel(plannerModel);
|
||
connect(ui.tableWidget, &TableView::itemClicked, plannerModel, &DivePlannerPointsModel::remove);
|
||
plannerModel->setRecalc(true);
|
||
ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::GAS, new AirTypesDelegate(this));
|
||
ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::DIVEMODE, new DiveTypesDelegate(this));
|
||
ui.cylinderTableWidget->setTitle(tr("Available gases"));
|
||
ui.cylinderTableWidget->setBtnToolTip(tr("Add cylinder"));
|
||
ui.cylinderTableWidget->setModel(cylinders);
|
||
connect(ui.cylinderTableWidget, &TableView::itemClicked, cylinders, &CylindersModel::remove);
|
||
ui.waterType->setItemData(0, FRESHWATER_SALINITY);
|
||
ui.waterType->setItemData(1, SEAWATER_SALINITY);
|
||
ui.waterType->setItemData(2, EN13319_SALINITY);
|
||
waterTypeUpdateTexts();
|
||
|
||
QTableView *view = ui.cylinderTableWidget->view();
|
||
view->setColumnHidden(CylindersModel::START, true);
|
||
view->setColumnHidden(CylindersModel::END, true);
|
||
view->setColumnHidden(CylindersModel::DEPTH, false);
|
||
view->setItemDelegateForColumn(CylindersModel::TYPE, new TankInfoDelegate(this));
|
||
connect(ui.cylinderTableWidget, &TableView::addButtonClicked, plannerModel, &DivePlannerPointsModel::addCylinder_clicked);
|
||
|
||
// addStop actually accept a call with no parameters, due to default parameters, but the connect() syntax without SIGNAL/SLOT
|
||
// does not understand default parameters and causes errors to be thrown.
|
||
// Continue to use old syntax, to avoid problems.
|
||
connect(ui.tableWidget, SIGNAL(addButtonClicked()), plannerModel, SLOT(addStop()));
|
||
|
||
connect(cylinders, &CylindersModel::dataChanged, GasSelectionModel::instance(), &GasSelectionModel::repopulate);
|
||
connect(cylinders, &CylindersModel::rowsInserted, GasSelectionModel::instance(), &GasSelectionModel::repopulate);
|
||
connect(cylinders, &CylindersModel::rowsRemoved, GasSelectionModel::instance(), &GasSelectionModel::repopulate);
|
||
connect(cylinders, &CylindersModel::dataChanged, plannerModel, &DivePlannerPointsModel::emitDataChanged);
|
||
connect(cylinders, &CylindersModel::dataChanged, plannerModel, &DivePlannerPointsModel::cylinderModelEdited);
|
||
connect(cylinders, &CylindersModel::rowsInserted, plannerModel, &DivePlannerPointsModel::cylinderModelEdited);
|
||
connect(cylinders, &CylindersModel::rowsRemoved, plannerModel, &DivePlannerPointsModel::cylinderModelEdited);
|
||
|
||
|
||
ui.tableWidget->setBtnToolTip(tr("Add dive data point"));
|
||
connect(ui.startTime, &QDateEdit::timeChanged, plannerModel, &DivePlannerPointsModel::setStartTime);
|
||
connect(ui.dateEdit, &QDateEdit::dateChanged, plannerModel, &DivePlannerPointsModel::setStartDate);
|
||
connect(ui.ATMPressure, QOverload<int>::of(&QSpinBox::valueChanged), this, &DivePlannerWidget::atmPressureChanged);
|
||
connect(ui.atmHeight, QOverload<int>::of(&QSpinBox::valueChanged), this, &DivePlannerWidget::heightChanged);
|
||
connect(ui.waterType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &DivePlannerWidget::waterTypeChanged);
|
||
connect(ui.customSalinity, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &DivePlannerWidget::customSalinityChanged);
|
||
|
||
// Creating (and canceling) the plan
|
||
replanButton = ui.buttonBox->addButton(tr("Save new"), QDialogButtonBox::ActionRole);
|
||
connect(replanButton, &QAbstractButton::clicked, plannerModel, &DivePlannerPointsModel::saveDuplicatePlan);
|
||
connect(ui.buttonBox, &QDialogButtonBox::accepted, plannerModel, &DivePlannerPointsModel::savePlan);
|
||
connect(ui.buttonBox, &QDialogButtonBox::rejected, plannerModel, &DivePlannerPointsModel::cancelPlan);
|
||
QShortcut *closeKey = new QShortcut(QKeySequence(Qt::Key_Escape), this);
|
||
connect(closeKey, &QShortcut::activated, plannerModel, &DivePlannerPointsModel::cancelPlan);
|
||
|
||
// This makes shure the spinbox gets a setMinimum(0) on it so we can't have negative time or depth.
|
||
// Limit segments to a depth of 1000 m/3300 ft and a duration of 100 h. Setting the limit for
|
||
// the depth will be done in settingChanged() since this depends on the chosen units.
|
||
ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::RUNTIME, new SpinBoxDelegate(0, INT_MAX, 1, this));
|
||
ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::DURATION, new SpinBoxDelegate(0, 6000, 1, this));
|
||
ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::CCSETPOINT, new DoubleSpinBoxDelegate(0, 2, 0.1, this));
|
||
|
||
connect(&diveListNotifier, &DiveListNotifier::settingsChanged, this, &DivePlannerWidget::settingsChanged);
|
||
|
||
/* set defaults. */
|
||
ui.ATMPressure->setValue(1013);
|
||
ui.atmHeight->setValue(0);
|
||
|
||
settingsChanged();
|
||
|
||
setMinimumWidth(0);
|
||
setMinimumHeight(0);
|
||
}
|
||
|
||
void DivePlannerWidget::setReplanButton(bool replan)
|
||
{
|
||
replanButton->setVisible(replan);
|
||
}
|
||
|
||
void DivePlannerWidget::setupStartTime(QDateTime startTime)
|
||
{
|
||
ui.startTime->setTime(startTime.time());
|
||
ui.dateEdit->setDate(startTime.date());
|
||
}
|
||
|
||
void DivePlannerWidget::setSurfacePressure(int surface_pressure)
|
||
{
|
||
ui.ATMPressure->setValue(surface_pressure);
|
||
}
|
||
|
||
void PlannerSettingsWidget::setDiveMode(int mode)
|
||
{
|
||
ui.rebreathermode->setCurrentIndex(mode);
|
||
}
|
||
|
||
void DivePlannerWidget::setSalinity(int salinity)
|
||
{
|
||
bool mapped = false;
|
||
for (int i = 0; i < ui.waterType->count(); i++) {
|
||
if (salinity == ui.waterType->itemData(i).toInt()) {
|
||
mapped = true;
|
||
ui.waterType->setCurrentIndex(i);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!mapped) {
|
||
/* Assign to last element "custom" in combo box */
|
||
ui.waterType->setItemData(ui.waterType->count()-1, salinity);
|
||
ui.waterType->setCurrentIndex(ui.waterType->count()-1);
|
||
ui.customSalinity->setEnabled(true);
|
||
ui.customSalinity->setValue(salinity / 10000.0);
|
||
}
|
||
DivePlannerPointsModel::instance()->setSalinity(salinity);
|
||
}
|
||
|
||
void DivePlannerWidget::settingsChanged()
|
||
{
|
||
// Adopt units
|
||
int maxDepth;
|
||
if (get_units()->length == units::FEET) {
|
||
ui.atmHeight->setSuffix("ft");
|
||
ui.atmHeight->setMinimum(-300);
|
||
ui.atmHeight->setMaximum(10000);
|
||
maxDepth = 3300;
|
||
} else {
|
||
ui.atmHeight->setSuffix(("m"));
|
||
ui.atmHeight->setMinimum(-100);
|
||
ui.atmHeight->setMaximum(3000);
|
||
maxDepth = 1000;
|
||
}
|
||
ui.tableWidget->view()->setItemDelegateForColumn(DivePlannerPointsModel::DEPTH, new SpinBoxDelegate(0, maxDepth, 1, this));
|
||
ui.atmHeight->blockSignals(true);
|
||
ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(DivePlannerPointsModel::instance()->getSurfacePressure()), NULL,NULL));
|
||
ui.atmHeight->blockSignals(false);
|
||
}
|
||
|
||
void DivePlannerWidget::atmPressureChanged(const int pressure)
|
||
{
|
||
DivePlannerPointsModel::instance()->setSurfacePressure(pressure);
|
||
ui.atmHeight->blockSignals(true);
|
||
ui.atmHeight->setValue((int) get_depth_units((int) pressure_to_altitude(pressure), NULL,NULL));
|
||
ui.atmHeight->blockSignals(false);
|
||
}
|
||
|
||
void DivePlannerWidget::heightChanged(const int height)
|
||
{ // height is in ft or in meters
|
||
int pressure = (int) (altitude_to_pressure(units_to_depth((double) height).mm));
|
||
ui.ATMPressure->blockSignals(true);
|
||
ui.ATMPressure->setValue(pressure);
|
||
ui.ATMPressure->blockSignals(false);
|
||
DivePlannerPointsModel::instance()->setSurfacePressure(pressure);
|
||
}
|
||
|
||
void DivePlannerWidget::waterTypeUpdateTexts()
|
||
{
|
||
double density;
|
||
/* Do not set text in last/custom element */
|
||
for (int i = 0; i < ui.waterType->count()-1; i++) {
|
||
if (ui.waterType->itemData(i) != QVariant::Invalid) {
|
||
QString densityText = ui.waterType->itemText(i).split("(")[0].trimmed();
|
||
density = ui.waterType->itemData(i).toInt() / 10000.0;
|
||
densityText.append(QString(" (%L1%2)").arg(density, 0, 'f', 2).arg(tr("kg/ℓ")));
|
||
ui.waterType->setItemText(i, densityText);
|
||
}
|
||
}
|
||
}
|
||
|
||
void DivePlannerWidget::waterTypeChanged(const int index)
|
||
{
|
||
ui.customSalinity->setEnabled(index == ui.waterType->count() - 1);
|
||
ui.customSalinity->setValue(ui.waterType->itemData(index).toInt() / 10000.0);
|
||
DivePlannerPointsModel::instance()->setSalinity(ui.waterType->itemData(index).toInt());
|
||
}
|
||
|
||
void DivePlannerWidget::customSalinityChanged(double density)
|
||
{
|
||
if (ui.customSalinity->isEnabled()) {
|
||
int newSalinity = (int)(density * 10000.0);
|
||
ui.waterType->setItemData(ui.waterType->count() - 1, newSalinity);
|
||
DivePlannerPointsModel::instance()->setSalinity(newSalinity);
|
||
}
|
||
}
|
||
|
||
void PlannerSettingsWidget::disableDecoElements(int mode)
|
||
{
|
||
if (mode == RECREATIONAL) {
|
||
ui.label_gflow->setDisabled(false);
|
||
ui.label_gfhigh->setDisabled(false);
|
||
ui.gflow->setDisabled(false);
|
||
ui.gfhigh->setDisabled(false);
|
||
ui.lastStop->setDisabled(true);
|
||
ui.backgasBreaks->setDisabled(true);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(false);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
ui.bailout->setDisabled(true);
|
||
ui.bailout->blockSignals(true);
|
||
ui.bailout->setChecked(false);
|
||
ui.bailout->blockSignals(false);
|
||
ui.bottompo2->setDisabled(false);
|
||
ui.decopo2->setDisabled(true);
|
||
ui.safetystop->setDisabled(false);
|
||
ui.label_reserve_gas->setDisabled(false);
|
||
ui.reserve_gas->setDisabled(false);
|
||
ui.label_vpmb_conservatism->setDisabled(true);
|
||
ui.vpmb_conservatism->setDisabled(true);
|
||
ui.switch_at_req_stop->setDisabled(true);
|
||
ui.min_switch_duration->setDisabled(true);
|
||
ui.surface_segment->setDisabled(true);
|
||
ui.label_min_switch_duration->setDisabled(true);
|
||
ui.sacfactor->setDisabled(true);
|
||
ui.problemsolvingtime->setDisabled(true);
|
||
ui.sacfactor->blockSignals(true);
|
||
ui.problemsolvingtime->blockSignals(true);
|
||
ui.sacfactor->setValue(2.0);
|
||
ui.problemsolvingtime->setValue(0);
|
||
ui.sacfactor->blockSignals(false);
|
||
ui.problemsolvingtime->blockSignals(false);
|
||
ui.display_variations->setDisabled(true);
|
||
}
|
||
else if (mode == VPMB) {
|
||
ui.label_gflow->setDisabled(true);
|
||
ui.label_gfhigh->setDisabled(true);
|
||
ui.gflow->setDisabled(true);
|
||
ui.gfhigh->setDisabled(true);
|
||
ui.lastStop->setDisabled(false);
|
||
if (prefs.last_stop) {
|
||
ui.backgasBreaks->setDisabled(false);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(prefs.doo2breaks);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
} else {
|
||
ui.backgasBreaks->setDisabled(true);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(false);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
}
|
||
ui.bailout->setDisabled(!(displayed_dive.dc.divemode == CCR || displayed_dive.dc.divemode == PSCR));
|
||
ui.bottompo2->setDisabled(false);
|
||
ui.decopo2->setDisabled(false);
|
||
ui.safetystop->setDisabled(true);
|
||
ui.label_reserve_gas->setDisabled(true);
|
||
ui.reserve_gas->setDisabled(true);
|
||
ui.label_vpmb_conservatism->setDisabled(false);
|
||
ui.vpmb_conservatism->setDisabled(false);
|
||
ui.switch_at_req_stop->setDisabled(false);
|
||
ui.min_switch_duration->setDisabled(false);
|
||
ui.surface_segment->setDisabled(false);
|
||
ui.label_min_switch_duration->setDisabled(false);
|
||
ui.sacfactor->setDisabled(false);
|
||
ui.problemsolvingtime->setDisabled(false);
|
||
ui.sacfactor->setValue(PlannerShared::sacfactor());
|
||
ui.problemsolvingtime->setValue(prefs.problemsolvingtime);
|
||
ui.display_variations->setDisabled(false);
|
||
}
|
||
else if (mode == BUEHLMANN) {
|
||
ui.label_gflow->setDisabled(false);
|
||
ui.label_gfhigh->setDisabled(false);
|
||
ui.gflow->setDisabled(false);
|
||
ui.gfhigh->setDisabled(false);
|
||
ui.lastStop->setDisabled(false);
|
||
if (prefs.last_stop) {
|
||
ui.backgasBreaks->setDisabled(false);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(prefs.doo2breaks);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
} else {
|
||
ui.backgasBreaks->setDisabled(true);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(false);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
}
|
||
ui.bailout->setDisabled(!(displayed_dive.dc.divemode == CCR || displayed_dive.dc.divemode == PSCR));
|
||
ui.bottompo2->setDisabled(false);
|
||
ui.decopo2->setDisabled(false);
|
||
ui.safetystop->setDisabled(true);
|
||
ui.label_reserve_gas->setDisabled(true);
|
||
ui.reserve_gas->setDisabled(true);
|
||
ui.label_vpmb_conservatism->setDisabled(true);
|
||
ui.vpmb_conservatism->setDisabled(true);
|
||
ui.switch_at_req_stop->setDisabled(false);
|
||
ui.min_switch_duration->setDisabled(false);
|
||
ui.surface_segment->setDisabled(false);
|
||
ui.label_min_switch_duration->setDisabled(false);
|
||
ui.sacfactor->setDisabled(false);
|
||
ui.problemsolvingtime->setDisabled(false);
|
||
ui.sacfactor->setValue(PlannerShared::sacfactor());
|
||
ui.problemsolvingtime->setValue(prefs.problemsolvingtime);
|
||
ui.display_variations->setDisabled(false);
|
||
}
|
||
}
|
||
|
||
void PlannerSettingsWidget::disableBackgasBreaks(bool enabled)
|
||
{
|
||
if (prefs.planner_deco_mode == RECREATIONAL)
|
||
return;
|
||
|
||
if (enabled) {
|
||
ui.backgasBreaks->setDisabled(false);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(prefs.doo2breaks);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
} else {
|
||
ui.backgasBreaks->setDisabled(true);
|
||
ui.backgasBreaks->blockSignals(true);
|
||
ui.backgasBreaks->setChecked(false);
|
||
ui.backgasBreaks->blockSignals(false);
|
||
}
|
||
}
|
||
|
||
PlannerSettingsWidget::PlannerSettingsWidget(QWidget *parent) : QWidget(parent, QFlag(0))
|
||
{
|
||
ui.setupUi(this);
|
||
|
||
DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance();
|
||
plannerModel->getDiveplan().bottomsac = prefs.bottomsac;
|
||
plannerModel->getDiveplan().decosac = prefs.decosac;
|
||
|
||
updateUnitsUI();
|
||
ui.lastStop->setChecked(prefs.last_stop);
|
||
ui.verbatim_plan->setChecked(prefs.verbatim_plan);
|
||
ui.display_duration->setChecked(prefs.display_duration);
|
||
ui.display_runtime->setChecked(prefs.display_runtime);
|
||
ui.display_transitions->setChecked(prefs.display_transitions);
|
||
ui.display_variations->setChecked(prefs.display_variations);
|
||
ui.safetystop->setChecked(prefs.safetystop);
|
||
ui.sacfactor->setValue(PlannerShared::sacfactor());
|
||
ui.problemsolvingtime->setValue(prefs.problemsolvingtime);
|
||
ui.bottompo2->setValue(PlannerShared::bottompo2());
|
||
ui.decopo2->setValue(PlannerShared::decopo2());
|
||
ui.backgasBreaks->setChecked(prefs.doo2breaks);
|
||
PlannerShared::set_dobailout(false);
|
||
setBailoutVisibility(false);
|
||
ui.o2narcotic->setChecked(prefs.o2narcotic);
|
||
ui.drop_stone_mode->setChecked(prefs.drop_stone_mode);
|
||
ui.switch_at_req_stop->setChecked(prefs.switch_at_req_stop);
|
||
ui.min_switch_duration->setValue(PlannerShared::min_switch_duration());
|
||
ui.surface_segment->setValue(PlannerShared::surface_segment());
|
||
ui.recreational_deco->setChecked(prefs.planner_deco_mode == RECREATIONAL);
|
||
ui.buehlmann_deco->setChecked(prefs.planner_deco_mode == BUEHLMANN);
|
||
ui.vpmb_deco->setChecked(prefs.planner_deco_mode == VPMB);
|
||
disableDecoElements((int) prefs.planner_deco_mode);
|
||
|
||
// should be the same order as in dive_comp_type!
|
||
QStringList rebreather_modes = QStringList();
|
||
for (int i = 0; i < FREEDIVE; i++)
|
||
rebreather_modes.append(gettextFromC::tr(divemode_text_ui[i]));
|
||
ui.rebreathermode->insertItems(0, rebreather_modes);
|
||
|
||
connect(ui.recreational_deco, &QAbstractButton::clicked, [] { PlannerShared::set_planner_deco_mode(RECREATIONAL); });
|
||
connect(ui.buehlmann_deco, &QAbstractButton::clicked, [] { PlannerShared::set_planner_deco_mode(BUEHLMANN); });
|
||
connect(ui.vpmb_deco, &QAbstractButton::clicked, [] { PlannerShared::set_planner_deco_mode(VPMB); });
|
||
|
||
connect(ui.lastStop, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setLastStop6m);
|
||
connect(ui.lastStop, &QAbstractButton::toggled, this, &PlannerSettingsWidget::disableBackgasBreaks);
|
||
connect(ui.verbatim_plan, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setVerbatim);
|
||
connect(ui.display_duration, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setDisplayDuration);
|
||
connect(ui.display_runtime, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setDisplayRuntime);
|
||
connect(ui.display_transitions, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setDisplayTransitions);
|
||
connect(ui.display_variations, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setDisplayVariations);
|
||
connect(ui.safetystop, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setSafetyStop);
|
||
connect(ui.reserve_gas, QOverload<int>::of(&QSpinBox::valueChanged), &PlannerShared::set_reserve_gas);
|
||
connect(ui.ascRate75, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setAscrate75Display);
|
||
connect(ui.ascRate50, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setAscrate50Display);
|
||
connect(ui.ascRateStops, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setAscratestopsDisplay);
|
||
connect(ui.ascRateLast6m, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setAscratelast6mDisplay);
|
||
connect(ui.descRate, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setDescrateDisplay);
|
||
connect(ui.drop_stone_mode, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setDropStoneMode);
|
||
connect(ui.gfhigh, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setGFHigh);
|
||
connect(ui.gflow, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setGFLow);
|
||
connect(ui.vpmb_conservatism, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setVpmbConservatism);
|
||
connect(ui.backgasBreaks, &QAbstractButton::toggled, this, &PlannerSettingsWidget::setBackgasBreaks);
|
||
connect(ui.bailout, &QAbstractButton::toggled, &PlannerShared::set_dobailout);
|
||
connect(ui.o2narcotic, &QAbstractButton::toggled, &PlannerShared::set_o2narcotic);
|
||
connect(ui.switch_at_req_stop, &QAbstractButton::toggled, plannerModel, &DivePlannerPointsModel::setSwitchAtReqStop);
|
||
connect(ui.min_switch_duration, QOverload<int>::of(&QSpinBox::valueChanged), &PlannerShared::set_min_switch_duration);
|
||
connect(ui.surface_segment, QOverload<int>::of(&QSpinBox::valueChanged), &PlannerShared::set_surface_segment);
|
||
connect(ui.rebreathermode, QOverload<int>::of(&QComboBox::currentIndexChanged), plannerModel, &DivePlannerPointsModel::setRebreatherMode);
|
||
connect(ui.rebreathermode, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PlannerSettingsWidget::setBailoutVisibility);
|
||
|
||
connect(ui.recreational_deco, &QAbstractButton::clicked, [this] { disableDecoElements(RECREATIONAL); });
|
||
connect(ui.buehlmann_deco, &QAbstractButton::clicked, [this] { disableDecoElements(BUEHLMANN); });
|
||
connect(ui.vpmb_deco, &QAbstractButton::clicked, [this] { disableDecoElements(VPMB); });
|
||
|
||
connect(ui.sacfactor, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &PlannerShared::set_sacfactor);
|
||
connect(ui.problemsolvingtime, QOverload<int>::of(&QSpinBox::valueChanged), plannerModel, &DivePlannerPointsModel::setProblemSolvingTime);
|
||
connect(ui.bottompo2, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &PlannerShared::set_bottompo2);
|
||
connect(ui.decopo2, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &PlannerShared::set_decopo2);
|
||
connect(ui.bestmixEND, QOverload<int>::of(&QSpinBox::valueChanged), &PlannerShared::set_bestmixend);
|
||
connect(ui.bottomSAC, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &PlannerShared::set_bottomsac);
|
||
connect(ui.decoStopSAC, QOverload<double>::of(&QDoubleSpinBox::valueChanged), &PlannerShared::set_decosac);
|
||
connect(&diveListNotifier, &DiveListNotifier::settingsChanged, this, &PlannerSettingsWidget::settingsChanged);
|
||
|
||
settingsChanged();
|
||
ui.gflow->setValue(prefs.gflow);
|
||
ui.gfhigh->setValue(prefs.gfhigh);
|
||
ui.vpmb_conservatism->setValue(prefs.vpmb_conservatism);
|
||
|
||
ui.ascRate75->setKeyboardTracking(false);
|
||
ui.ascRate50->setKeyboardTracking(false);
|
||
ui.ascRateLast6m->setKeyboardTracking(false);
|
||
ui.ascRateStops->setKeyboardTracking(false);
|
||
ui.descRate->setKeyboardTracking(false);
|
||
ui.gfhigh->setKeyboardTracking(false);
|
||
ui.gflow->setKeyboardTracking(false);
|
||
|
||
setMinimumWidth(0);
|
||
setMinimumHeight(0);
|
||
}
|
||
|
||
void PlannerSettingsWidget::updateUnitsUI()
|
||
{
|
||
DivePlannerPointsModel *plannerModel = DivePlannerPointsModel::instance();
|
||
ui.ascRate75->setValue(plannerModel->ascrate75Display());
|
||
ui.ascRate50->setValue(plannerModel->ascrate50Display());
|
||
ui.ascRateStops->setValue(plannerModel->ascratestopsDisplay());
|
||
ui.ascRateLast6m->setValue(plannerModel->ascratelast6mDisplay());
|
||
ui.descRate->setValue(lrint(plannerModel->descrateDisplay()));
|
||
ui.bestmixEND->setValue(lrint(get_depth_units(prefs.bestmixend.mm, NULL, NULL)));
|
||
}
|
||
|
||
PlannerSettingsWidget::~PlannerSettingsWidget()
|
||
{
|
||
}
|
||
|
||
void PlannerSettingsWidget::settingsChanged()
|
||
{
|
||
QString vs;
|
||
// don't recurse into setting the value from the ui when setting the ui from the value
|
||
ui.bottomSAC->blockSignals(true);
|
||
ui.decoStopSAC->blockSignals(true);
|
||
if (get_units()->length == units::FEET) {
|
||
vs.append(tr("ft/min"));
|
||
ui.lastStop->setText(tr("Last stop at 20ft"));
|
||
ui.asc50to6->setText(tr("50% avg. depth to 20ft"));
|
||
ui.asc6toSurf->setText(tr("20ft to surface"));
|
||
ui.bestmixEND->setSuffix(tr("ft"));
|
||
} else {
|
||
vs.append(tr("m/min"));
|
||
ui.lastStop->setText(tr("Last stop at 6m"));
|
||
ui.asc50to6->setText(tr("50% avg. depth to 6m"));
|
||
ui.asc6toSurf->setText(tr("6m to surface"));
|
||
ui.bestmixEND->setSuffix(tr("m"));
|
||
}
|
||
if (get_units()->volume == units::CUFT) {
|
||
ui.bottomSAC->setSuffix(tr("cuft/min"));
|
||
ui.decoStopSAC->setSuffix(tr("cuft/min"));
|
||
ui.bottomSAC->setDecimals(2);
|
||
ui.bottomSAC->setSingleStep(0.1);
|
||
ui.decoStopSAC->setDecimals(2);
|
||
ui.decoStopSAC->setSingleStep(0.1);
|
||
ui.bottomSAC->setValue(PlannerShared::bottomsac());
|
||
ui.decoStopSAC->setValue(PlannerShared::decosac());
|
||
} else {
|
||
ui.bottomSAC->setSuffix(tr("ℓ/min"));
|
||
ui.decoStopSAC->setSuffix(tr("ℓ/min"));
|
||
ui.bottomSAC->setDecimals(0);
|
||
ui.bottomSAC->setSingleStep(1);
|
||
ui.decoStopSAC->setDecimals(0);
|
||
ui.decoStopSAC->setSingleStep(1);
|
||
ui.bottomSAC->setValue(PlannerShared::bottomsac());
|
||
ui.decoStopSAC->setValue(PlannerShared::decosac());
|
||
}
|
||
if (get_units()->pressure == units::BAR) {
|
||
ui.reserve_gas->setSuffix(tr("bar"));
|
||
ui.reserve_gas->setSingleStep(1);
|
||
ui.reserve_gas->setValue(prefs.reserve_gas / 1000);
|
||
ui.reserve_gas->setMaximum(300);
|
||
} else {
|
||
ui.reserve_gas->setSuffix(tr("psi"));
|
||
ui.reserve_gas->setSingleStep(10);
|
||
ui.reserve_gas->setMaximum(5000);
|
||
ui.reserve_gas->setValue(mbar_to_PSI(prefs.reserve_gas));
|
||
}
|
||
|
||
ui.bottomSAC->blockSignals(false);
|
||
ui.decoStopSAC->blockSignals(false);
|
||
updateUnitsUI();
|
||
ui.ascRate75->setSuffix(vs);
|
||
ui.ascRate50->setSuffix(vs);
|
||
ui.ascRateStops->setSuffix(vs);
|
||
ui.ascRateLast6m->setSuffix(vs);
|
||
ui.descRate->setSuffix(vs);
|
||
}
|
||
|
||
void PlannerSettingsWidget::setBackgasBreaks(bool dobreaks)
|
||
{
|
||
PlannerShared::set_doo2breaks(dobreaks);
|
||
}
|
||
|
||
void PlannerSettingsWidget::setBailoutVisibility(int mode)
|
||
{
|
||
ui.bailout->setDisabled(!(mode == CCR || mode == PSCR));
|
||
ui.sacFactor->setDisabled(mode == CCR);
|
||
}
|
||
|
||
PlannerDetails::PlannerDetails(QWidget *parent) : QWidget(parent)
|
||
{
|
||
ui.setupUi(this);
|
||
#ifdef NO_PRINTING
|
||
ui.printPlan->hide();
|
||
#endif
|
||
}
|
||
|
||
void PlannerDetails::setPlanNotes(QString plan)
|
||
{
|
||
ui.divePlanOutput->setHtml(plan);
|
||
}
|
||
|
||
PlannerWidgets::PlannerWidgets()
|
||
{
|
||
connect(plannerDetails.printPlan(), &QPushButton::pressed, this, &PlannerWidgets::printDecoPlan);
|
||
connect(DivePlannerPointsModel::instance(), &DivePlannerPointsModel::calculatedPlanNotes,
|
||
&plannerDetails, &PlannerDetails::setPlanNotes);
|
||
}
|
||
|
||
void PlannerWidgets::planDive()
|
||
{
|
||
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN);
|
||
|
||
MainWindow::instance()->graphics->setPlanState();
|
||
dc_number = 0;
|
||
|
||
// create a simple starting dive, using the first gas from the just copied cylinders
|
||
DivePlannerPointsModel::instance()->createSimpleDive();
|
||
|
||
// plan the dive in the same mode as the currently selected one
|
||
if (current_dive) {
|
||
plannerSettingsWidget.setDiveMode(current_dive->dc.divemode);
|
||
plannerSettingsWidget.setBailoutVisibility(current_dive->dc.divemode);
|
||
if (current_dive->salinity)
|
||
plannerWidget.setSalinity(current_dive->salinity);
|
||
else // No salinity means salt water
|
||
plannerWidget.setSalinity(SEAWATER_SALINITY);
|
||
}
|
||
plannerWidget.setReplanButton(false);
|
||
|
||
plannerWidget.setupStartTime(timestampToDateTime(displayed_dive.when)); // This will reload the profile!
|
||
}
|
||
|
||
void PlannerWidgets::replanDive()
|
||
{
|
||
DivePlannerPointsModel::instance()->clear();
|
||
DivePlannerPointsModel::instance()->setPlanMode(DivePlannerPointsModel::PLAN);
|
||
|
||
MainWindow::instance()->graphics->setPlanState();
|
||
|
||
plannerWidget.setReplanButton(true);
|
||
plannerWidget.setupStartTime(timestampToDateTime(current_dive->when));
|
||
if (current_dive->surface_pressure.mbar)
|
||
plannerWidget.setSurfacePressure(current_dive->surface_pressure.mbar);
|
||
if (current_dive->salinity)
|
||
plannerWidget.setSalinity(current_dive->salinity);
|
||
DivePlannerPointsModel::instance()->loadFromDive(current_dive);
|
||
reset_cylinders(&displayed_dive, true);
|
||
DivePlannerPointsModel::instance()->cylindersModel()->updateDive(&displayed_dive);
|
||
}
|
||
|
||
void PlannerWidgets::printDecoPlan()
|
||
{
|
||
#ifndef NO_PRINTING
|
||
char *disclaimer = get_planner_disclaimer_formatted();
|
||
// Prepend a logo and a disclaimer to the plan.
|
||
// Save the old plan so that it can be restored at the end of the function.
|
||
QString origPlan = plannerDetails.divePlanOutput()->toHtml();
|
||
QString diveplan = QStringLiteral("<img height=50 src=\":subsurface-icon\"> ") +
|
||
QString(disclaimer) + origPlan;
|
||
free(disclaimer);
|
||
|
||
QPrinter printer;
|
||
QPrintDialog *dialog = new QPrintDialog(&printer, MainWindow::instance());
|
||
dialog->setWindowTitle(tr("Print runtime table"));
|
||
if (dialog->exec() != QDialog::Accepted)
|
||
return;
|
||
|
||
/* render the profile as a pixmap that is inserted as base64 data into a HTML <img> tag
|
||
* make it fit a page width defined by 2 cm margins via QTextDocument->print() (cannot be changed?)
|
||
* the height of the profile is 40% of the page height.
|
||
*/
|
||
QSizeF renderSize = printer.pageRect(QPrinter::Inch).size();
|
||
const qreal marginsInch = 1.57480315; // = (2 x 2cm) / 2.45cm/inch
|
||
renderSize.setWidth((renderSize.width() - marginsInch) * printer.resolution());
|
||
renderSize.setHeight(((renderSize.height() - marginsInch) * printer.resolution()) / 2.5);
|
||
|
||
QPixmap pixmap(renderSize.toSize());
|
||
QPainter painter(&pixmap);
|
||
painter.setRenderHint(QPainter::Antialiasing);
|
||
painter.setRenderHint(QPainter::SmoothPixmapTransform);
|
||
|
||
ProfileWidget2 *profile = MainWindow::instance()->graphics;
|
||
QSize origSize = profile->size();
|
||
profile->resize(renderSize.toSize());
|
||
profile->setPrintMode(true);
|
||
profile->render(&painter);
|
||
profile->resize(origSize);
|
||
profile->setPrintMode(false);
|
||
|
||
QByteArray byteArray;
|
||
QBuffer buffer(&byteArray);
|
||
pixmap.save(&buffer, "PNG");
|
||
QString profileImage = QString("<img src=\"data:image/png;base64,") + byteArray.toBase64() + "\"/><br><br>";
|
||
diveplan = profileImage + diveplan;
|
||
|
||
plannerDetails.divePlanOutput()->setHtml(diveplan);
|
||
plannerDetails.divePlanOutput()->print(&printer);
|
||
plannerDetails.divePlanOutput()->setHtml(origPlan); // restore original plan
|
||
#endif
|
||
}
|