652 lines
22 KiB
C++
652 lines
22 KiB
C++
/*
|
|
* ModelRailroadTimetablePlanner
|
|
* Copyright 2016-2023, Filippo Gentile
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "editstopdialog.h"
|
|
#include "ui_editstopdialog.h"
|
|
|
|
#include "model/stopmodel.h"
|
|
#include "stopeditinghelper.h"
|
|
|
|
#include "app/session.h"
|
|
|
|
#include "utils/jobcategorystrings.h"
|
|
|
|
#include "rscoupledialog.h"
|
|
#include "model/rscouplinginterface.h"
|
|
|
|
#include "model/stopcouplingmodel.h"
|
|
#include "model/trainassetmodel.h"
|
|
#include "model/jobpassingsmodel.h"
|
|
#include "jobs/jobsmanager/model/jobshelper.h"
|
|
#include "jobs/jobsmanager/model/jobmatchmodel.h"
|
|
|
|
#include "utils/delegates/sql/modelpageswitcher.h"
|
|
#include "utils/delegates/sql/customcompletionlineedit.h"
|
|
#include "utils/delegates/sql/chooseitemdlg.h"
|
|
|
|
#include <QtMath>
|
|
|
|
#include "utils/owningqpointer.h"
|
|
#include <QMenu>
|
|
#include <QMessageBox>
|
|
|
|
#include "app/scopedebug.h"
|
|
|
|
EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
|
|
QDialog(parent),
|
|
ui(new Ui::EditStopDialog),
|
|
stopModel(m),
|
|
readOnly(false)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
// Stop
|
|
helper = new StopEditingHelper(Session->m_Db, stopModel, ui->outGateTrackSpin,
|
|
ui->arrivalTimeEdit, ui->departureTimeEdit, this);
|
|
connect(helper, &StopEditingHelper::nextSegmentChosen, this,
|
|
&EditStopDialog::updateAdditionalNotes);
|
|
connect(helper, &StopEditingHelper::stationTrackChosen, this,
|
|
&EditStopDialog::updateAdditionalNotes);
|
|
|
|
CustomCompletionLineEdit *mStationEdit = helper->getStationEdit();
|
|
CustomCompletionLineEdit *mStTrackEdit = helper->getStTrackEdit();
|
|
CustomCompletionLineEdit *mOutGateEdit = helper->getOutGateEdit();
|
|
|
|
ui->curStopLay->setWidget(0, QFormLayout::FieldRole, mStationEdit);
|
|
ui->curStopLay->setWidget(2, QFormLayout::FieldRole, mStTrackEdit);
|
|
ui->curStopLay->setWidget(3, QFormLayout::FieldRole, mOutGateEdit);
|
|
|
|
// Coupling
|
|
couplingMgr = new RSCouplingInterface(Session->m_Db, this);
|
|
|
|
coupledModel = new StopCouplingModel(Session->m_Db, this);
|
|
auto ps = new ModelPageSwitcher(true, this);
|
|
ps->setModel(coupledModel);
|
|
ui->coupledView->setModel(coupledModel);
|
|
ui->coupledLayout->insertWidget(1, ps);
|
|
|
|
uncoupledModel = new StopCouplingModel(Session->m_Db, this);
|
|
ps = new ModelPageSwitcher(true, this);
|
|
ps->setModel(uncoupledModel);
|
|
ui->uncoupledView->setModel(uncoupledModel);
|
|
ui->uncoupledLayout->insertWidget(1, ps);
|
|
|
|
connect(ui->importJobRsBut, &QPushButton::clicked, this, &EditStopDialog::importJobRS);
|
|
connect(ui->editCoupledBut, &QPushButton::clicked, this, &EditStopDialog::editCoupled);
|
|
connect(ui->editUncoupledBut, &QPushButton::clicked, this, &EditStopDialog::editUncoupled);
|
|
|
|
ui->coupledView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
ui->uncoupledView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->coupledView, &QAbstractItemView::customContextMenuRequested, this,
|
|
&EditStopDialog::couplingCustomContextMenuRequested);
|
|
connect(ui->uncoupledView, &QAbstractItemView::customContextMenuRequested, this,
|
|
&EditStopDialog::couplingCustomContextMenuRequested);
|
|
|
|
// Setup train asset models
|
|
trainAssetModelBefore = new TrainAssetModel(Session->m_Db, this);
|
|
ps = new ModelPageSwitcher(true, this);
|
|
ps->setModel(trainAssetModelBefore);
|
|
ui->assetBeforeView->setModel(trainAssetModelBefore);
|
|
ui->trainAssetGridLayout->addWidget(ps, 2, 0);
|
|
|
|
trainAssetModelAfter = new TrainAssetModel(Session->m_Db, this);
|
|
ps = new ModelPageSwitcher(true, this);
|
|
ps->setModel(trainAssetModelAfter);
|
|
ui->assetAfterView->setModel(trainAssetModelAfter);
|
|
ui->trainAssetGridLayout->addWidget(ps, 2, 1);
|
|
|
|
ui->assetBeforeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
ui->assetAfterView->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(ui->assetBeforeView, &QAbstractItemView::customContextMenuRequested, this,
|
|
&EditStopDialog::couplingCustomContextMenuRequested);
|
|
connect(ui->assetAfterView, &QAbstractItemView::customContextMenuRequested, this,
|
|
&EditStopDialog::couplingCustomContextMenuRequested);
|
|
|
|
// Setup Crossings/Passings
|
|
passingsModel = new JobPassingsModel(this);
|
|
ui->passingsView->setModel(passingsModel);
|
|
|
|
crossingsModel = new JobPassingsModel(this);
|
|
ui->crossingsView->setModel(crossingsModel);
|
|
|
|
connect(ui->calcPassingsBut, &QPushButton::clicked, this, &EditStopDialog::calcPassings);
|
|
|
|
// BIG TODO: temporarily disable option to Cancel dialog
|
|
// This is because at the moment it doesn't seem Coupling are canceled
|
|
// So you get a mixed state: Arrival/Departure/Descriptio ecc changes are canceled but Coupling
|
|
// changes are still applied
|
|
ui->buttonBox->setStandardButtons(QDialogButtonBox::Ok);
|
|
|
|
ui->buttonBox->button(QDialogButtonBox::Ok)
|
|
->setToolTip(tr("Press SHIFT modifier and click to save changes"
|
|
" without recalculating travel times."));
|
|
|
|
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
|
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
|
|
|
setReadOnly(true);
|
|
}
|
|
|
|
EditStopDialog::~EditStopDialog()
|
|
{
|
|
delete helper;
|
|
helper = nullptr;
|
|
|
|
delete ui;
|
|
}
|
|
|
|
void EditStopDialog::clearUi()
|
|
{
|
|
helper->stopOutTrackTimer();
|
|
|
|
stopIdx = QModelIndex();
|
|
|
|
m_jobId = 0;
|
|
m_jobCat = JobCategory::FREIGHT;
|
|
|
|
trainAssetModelBefore->setStop(0, QTime(), TrainAssetModel::BeforeStop);
|
|
trainAssetModelAfter->setStop(0, QTime(), TrainAssetModel::AfterStop);
|
|
|
|
// TODO: clear UI properly
|
|
}
|
|
|
|
void EditStopDialog::showBeforeAsset(bool val)
|
|
{
|
|
ui->assetBeforeView->setVisible(val);
|
|
ui->assetBeforeLabel->setVisible(val);
|
|
}
|
|
|
|
void EditStopDialog::showAfterAsset(bool val)
|
|
{
|
|
ui->assetAfterView->setVisible(val);
|
|
ui->assetAfterLabel->setVisible(val);
|
|
}
|
|
|
|
void EditStopDialog::setStop(const QModelIndex &idx)
|
|
{
|
|
DEBUG_ENTRY;
|
|
|
|
if (!idx.isValid())
|
|
{
|
|
clearUi();
|
|
return;
|
|
}
|
|
|
|
m_jobId = stopModel->getJobId();
|
|
m_jobCat = stopModel->getCategory();
|
|
|
|
stopIdx = idx;
|
|
|
|
const StopItem &curStop = stopModel->getItemAt(idx.row());
|
|
StopItem prevStop;
|
|
if (idx.row() == 0)
|
|
prevStop = StopItem(); // First stop has no previous stop
|
|
else
|
|
prevStop = stopModel->getItemAt(idx.row() - 1);
|
|
|
|
helper->setStop(curStop, prevStop);
|
|
|
|
// Setup Train Asset
|
|
trainAssetModelBefore->setStop(m_jobId, curStop.arrival, TrainAssetModel::BeforeStop);
|
|
trainAssetModelAfter->setStop(m_jobId, curStop.arrival, TrainAssetModel::AfterStop);
|
|
|
|
// Hide train asset before stop on First stop
|
|
showBeforeAsset(curStop.type != StopType::First);
|
|
|
|
// Hide train asset after stop on Last stop
|
|
showAfterAsset(curStop.type != StopType::Last);
|
|
|
|
// Coupling operations
|
|
coupledModel->setStop(curStop.stopId, RsOp::Coupled);
|
|
uncoupledModel->setStop(curStop.stopId, RsOp::Uncoupled);
|
|
|
|
// Update UI
|
|
updateInfo();
|
|
|
|
// Calc passings
|
|
calcPassings();
|
|
|
|
// Update Title
|
|
const QString jobName = JobCategoryName::jobName(m_jobId, m_jobCat);
|
|
setWindowTitle(jobName);
|
|
}
|
|
|
|
void EditStopDialog::updateInfo()
|
|
{
|
|
const StopItem &curStop = helper->getCurItem();
|
|
const StopItem &prevStop = helper->getPrevItem();
|
|
|
|
const QString inGateStr =
|
|
helper->getGateString(curStop.fromGate.gateId, prevStop.nextSegment.reversed);
|
|
ui->inGateEdit->setText(inGateStr);
|
|
|
|
if (curStop.type == StopType::First)
|
|
{
|
|
// Hide box of previous stop
|
|
ui->prevStopBox->setVisible(false);
|
|
ui->curStopBox->setTitle(tr("First Stop"));
|
|
}
|
|
else
|
|
{
|
|
// Show box of previous stop
|
|
ui->prevStopBox->setVisible(true);
|
|
ui->curStopBox->setTitle(curStop.type == StopType::Last ? tr("Last Stop")
|
|
: tr("Current Stop"));
|
|
|
|
QString prevStName;
|
|
if (prevStop.stationId)
|
|
{
|
|
query q(Session->m_Db, "SELECT name FROM stations WHERE id=?");
|
|
q.bind(1, prevStop.stationId);
|
|
q.step();
|
|
prevStName = q.getRows().get<QString>(0);
|
|
}
|
|
ui->prevStEdit->setText(prevStName);
|
|
|
|
const QString outGateStr =
|
|
helper->getGateString(prevStop.toGate.gateId, prevStop.nextSegment.reversed);
|
|
ui->prevOutGateEdit->setText(outGateStr);
|
|
}
|
|
|
|
const QString descr = stopModel->getDescription(curStop);
|
|
ui->descriptionEdit->setPlainText(descr);
|
|
|
|
if (curStop.type == StopType::Transit)
|
|
{
|
|
// On transit you cannot couple/uncouple rollingstock
|
|
ui->editCoupledBut->setEnabled(false);
|
|
ui->editUncoupledBut->setEnabled(false);
|
|
}
|
|
|
|
couplingMgr->loadCouplings(stopModel, curStop.stopId, m_jobId, curStop.arrival);
|
|
|
|
updateAdditionalNotes();
|
|
}
|
|
|
|
void EditStopDialog::saveDataToModel()
|
|
{
|
|
DEBUG_ENTRY;
|
|
|
|
const StopItem &curStop = helper->getCurItem();
|
|
const StopItem &prevStop = helper->getPrevItem();
|
|
|
|
if (ui->descriptionEdit->document()->isModified())
|
|
{
|
|
stopModel->setDescription(stopIdx, ui->descriptionEdit->toPlainText());
|
|
}
|
|
|
|
bool avoidTimeRecalc = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
|
stopModel->setStopInfo(stopIdx, curStop, prevStop.nextSegment, avoidTimeRecalc);
|
|
}
|
|
|
|
void EditStopDialog::importJobRS()
|
|
{
|
|
const StopItem &curStop = helper->getCurItem();
|
|
if (!curStop.stationId)
|
|
{
|
|
QMessageBox::warning(this, tr("Import Error"),
|
|
tr("In order to import rollingstock from other<br>"
|
|
"Jobs stopping in the <b>same station</b> as current one, "
|
|
"you must first set a valid station for this stop."));
|
|
return;
|
|
}
|
|
|
|
JobMatchModel jobsMatch(Session->m_Db);
|
|
jobsMatch.setDefaultId(JobMatchModel::StopId);
|
|
jobsMatch.setFilter(m_jobId, curStop.stationId, curStop.departure);
|
|
|
|
QString stName = helper->getStationEdit()->text();
|
|
|
|
OwningQPointer<ChooseItemDlg> dlg = new ChooseItemDlg(&jobsMatch, this);
|
|
dlg->setDescription(
|
|
tr("Please choose a Job among the ones stopping at <b>%1</b> before <b>%2</b>.<br>"
|
|
"All rollingstock uncoupled by selected Job at current station<br>"
|
|
"will be coupled to current Job.")
|
|
.arg(stName, curStop.departure.toString("HH:mm")));
|
|
dlg->setPlaceholder(tr("Job number without category"));
|
|
|
|
// Select model
|
|
int ret = dlg->exec();
|
|
if (ret != QDialog::Accepted || !dlg)
|
|
return;
|
|
|
|
db_id otherJobStopId = dlg->getItemId();
|
|
if (!otherJobStopId)
|
|
return;
|
|
|
|
// Import rollingstock
|
|
int count = couplingMgr->importRSFromJob(otherJobStopId);
|
|
|
|
// Refresh views
|
|
coupledModel->refreshData(true);
|
|
trainAssetModelAfter->refreshData(true);
|
|
|
|
// Tell user it's completed
|
|
QMessageBox::information(
|
|
this, tr("Importation Finished"),
|
|
tr("<b>%1</b> rollingstock items were successfully imported").arg(count));
|
|
}
|
|
|
|
void EditStopDialog::editCoupled()
|
|
{
|
|
const StopItem &curStop = helper->getCurItem();
|
|
|
|
coupledModel->clearCache();
|
|
trainAssetModelAfter->clearCache();
|
|
|
|
OwningQPointer<RSCoupleDialog> dlg = new RSCoupleDialog(couplingMgr, RsOp::Coupled, this);
|
|
dlg->setWindowTitle(tr("Couple"));
|
|
dlg->loadProxyModels(Session->m_Db, m_jobId, curStop.stopId, curStop.stationId,
|
|
curStop.arrival);
|
|
|
|
dlg->exec();
|
|
|
|
coupledModel->refreshData(true);
|
|
trainAssetModelAfter->refreshData(true);
|
|
}
|
|
|
|
void EditStopDialog::editUncoupled()
|
|
{
|
|
const StopItem &curStop = helper->getCurItem();
|
|
|
|
uncoupledModel->clearCache();
|
|
trainAssetModelAfter->clearCache();
|
|
|
|
OwningQPointer<RSCoupleDialog> dlg = new RSCoupleDialog(couplingMgr, RsOp::Uncoupled, this);
|
|
dlg->setWindowTitle(tr("Uncouple"));
|
|
dlg->loadProxyModels(Session->m_Db, m_jobId, curStop.stopId, curStop.stationId,
|
|
curStop.arrival);
|
|
|
|
dlg->exec();
|
|
|
|
uncoupledModel->refreshData(true);
|
|
trainAssetModelAfter->refreshData(true);
|
|
}
|
|
|
|
bool EditStopDialog::hasEngineAfterStop()
|
|
{
|
|
DEBUG_ENTRY;
|
|
return couplingMgr->hasEngineAfterStop();
|
|
}
|
|
|
|
void EditStopDialog::calcPassings()
|
|
{
|
|
DEBUG_ENTRY;
|
|
|
|
const StopItem &curStop = helper->getCurItem();
|
|
|
|
JobStopDirectionHelper dirHelper(Session->m_Db);
|
|
utils::Side myDirection = dirHelper.getStopOutSide(curStop.stopId);
|
|
|
|
query q(Session->m_Db,
|
|
"SELECT s.id, s.job_id, jobs.category, s.arrival, s.departure,"
|
|
"t1.name,t2.name"
|
|
" FROM stops s"
|
|
" JOIN jobs ON jobs.id=s.job_id"
|
|
" LEFT JOIN station_gate_connections g1 ON g1.id=s.in_gate_conn"
|
|
" LEFT JOIN station_gate_connections g2 ON g2.id=s.out_gate_conn"
|
|
" LEFT JOIN station_tracks t1 ON t1.id=g1.track_id"
|
|
" LEFT JOIN station_tracks t2 ON t2.id=g2.track_id"
|
|
" WHERE s.station_id=? AND s.departure >=? AND s.arrival<=? AND s.job_id <> ?");
|
|
|
|
q.bind(1, curStop.stationId);
|
|
q.bind(2, curStop.arrival);
|
|
q.bind(3, curStop.arrival);
|
|
q.bind(4, m_jobId);
|
|
|
|
QVector<JobPassingsModel::Entry> passings, crossings;
|
|
|
|
for (auto r : q)
|
|
{
|
|
JobPassingsModel::Entry e;
|
|
|
|
db_id otherStopId = r.get<db_id>(0);
|
|
e.jobId = r.get<db_id>(1);
|
|
e.category = JobCategory(r.get<int>(2));
|
|
e.arrival = r.get<QTime>(3);
|
|
e.departure = r.get<QTime>(4);
|
|
e.platform = r.get<int>(5);
|
|
|
|
e.platform = r.get<QString>(6);
|
|
if (e.platform.isEmpty())
|
|
e.platform = r.get<QString>(7); // Use out gate to get track name
|
|
|
|
utils::Side otherDir = dirHelper.getStopOutSide(otherStopId);
|
|
|
|
if (myDirection == otherDir)
|
|
passings.append(e); // Same direction -> Passing
|
|
else
|
|
crossings.append(e); // Opposite direction -> Crossing
|
|
}
|
|
|
|
q.reset();
|
|
|
|
passingsModel->setJobs(passings);
|
|
crossingsModel->setJobs(crossings);
|
|
|
|
ui->passingsView->resizeColumnsToContents();
|
|
ui->crossingsView->resizeColumnsToContents();
|
|
}
|
|
|
|
void EditStopDialog::couplingCustomContextMenuRequested(const QPoint &pos)
|
|
{
|
|
OwningQPointer<QMenu> menu = new QMenu(this);
|
|
QAction *act = menu->addAction(tr("Refresh"));
|
|
|
|
// HACK: could be ui->coupledView or ui->uncoupledView or ui->assetBeforeView or
|
|
// ui->assetAfterView
|
|
QAbstractItemView *view = qobject_cast<QAbstractItemView *>(sender());
|
|
if (!view)
|
|
return; // Error: not called by the view?
|
|
|
|
if (menu->exec(view->viewport()->mapToGlobal(pos)) != act)
|
|
return; // User didn't select 'Refresh' action
|
|
|
|
// Refresh data
|
|
coupledModel->refreshData(true);
|
|
uncoupledModel->refreshData(true);
|
|
trainAssetModelBefore->refreshData(true);
|
|
trainAssetModelAfter->refreshData(true);
|
|
}
|
|
|
|
int EditStopDialog::getTrainSpeedKmH(bool afterStop)
|
|
{
|
|
const StopItem &curStop = helper->getCurItem();
|
|
|
|
query q(Session->m_Db, "SELECT MIN(rs_models.max_speed), rs_id FROM("
|
|
"SELECT coupling.rs_id AS rs_id, MAX(stops.arrival)"
|
|
" FROM stops"
|
|
" JOIN coupling ON coupling.stop_id=stops.id"
|
|
" WHERE stops.job_id=? AND stops.arrival<?"
|
|
" GROUP BY coupling.rs_id"
|
|
" HAVING coupling.operation=1)"
|
|
" JOIN rs_list ON rs_list.id=rs_id"
|
|
" JOIN rs_models ON rs_models.id=rs_list.model_id");
|
|
q.bind(1, m_jobId); // TODO: maybe move to model
|
|
|
|
// HACK: 1 minute is the min interval between stops,
|
|
// by adding 1 minute we include the current stop but leave out the next one
|
|
if (afterStop)
|
|
q.bind(2, curStop.arrival.addSecs(60));
|
|
else
|
|
q.bind(2, curStop.arrival);
|
|
|
|
q.step();
|
|
return q.getRows().get<int>(0);
|
|
}
|
|
|
|
void EditStopDialog::updateAdditionalNotes()
|
|
{
|
|
const StopItem &curStop = helper->getCurItem();
|
|
|
|
QString msg;
|
|
|
|
// Check direction
|
|
if (curStop.fromGate.gateConnId && curStop.toGate.gateConnId && curStop.type != StopType::First
|
|
&& curStop.type != StopType::Last)
|
|
{
|
|
// Ignore First and Last stop (sometimes they have fake in/out gates set which might trigger
|
|
// this message) Both entry and exit path are set, check direction
|
|
if (curStop.fromGate.stationTrackSide == curStop.toGate.stationTrackSide)
|
|
{
|
|
// Train leaves station track from same side of entrance
|
|
msg = tr("Train reverses direction.");
|
|
}
|
|
}
|
|
|
|
// Check line traction
|
|
if (curStop.type != StopType::Last && curStop.nextSegment.segmentId)
|
|
{
|
|
// Last has no next segment so do not show traction type
|
|
|
|
bool nextSegmentElectrified = stopModel->isRailwayElectrifiedAfterRow(stopIdx.row());
|
|
bool prevSegmentElectrified = !nextSegmentElectrified; // Trigger change on First stop
|
|
|
|
if (curStop.type != StopType::First && stopIdx.row() >= 0)
|
|
{
|
|
// Get real previous railway type
|
|
prevSegmentElectrified = stopModel->isRailwayElectrifiedAfterRow(stopIdx.row() - 1);
|
|
}
|
|
|
|
if (!msg.isEmpty())
|
|
msg.append("\n\n"); // Separate from previous message
|
|
|
|
if (nextSegmentElectrified)
|
|
msg.append(tr("Electric traction is ALLOWED."));
|
|
else
|
|
msg.append(tr("Electric traction is NOT ALLOWED."));
|
|
|
|
if (nextSegmentElectrified != prevSegmentElectrified && curStop.type != StopType::First)
|
|
{
|
|
// Railway type changed
|
|
msg.append('\n');
|
|
msg.append(tr("(Different traction then previous line!)"));
|
|
}
|
|
}
|
|
|
|
ui->additionalNotesLabel->setText(msg);
|
|
ui->additionalNotesBox->setVisible(!msg.isEmpty());
|
|
}
|
|
|
|
void EditStopDialog::setReadOnly(bool value)
|
|
{
|
|
readOnly = value;
|
|
|
|
CustomCompletionLineEdit *mStationEdit = helper->getStationEdit();
|
|
CustomCompletionLineEdit *mStTrackEdit = helper->getStTrackEdit();
|
|
CustomCompletionLineEdit *mOutGateEdit = helper->getOutGateEdit();
|
|
|
|
mStationEdit->setReadOnly(readOnly);
|
|
mStTrackEdit->setReadOnly(readOnly);
|
|
mOutGateEdit->setReadOnly(readOnly);
|
|
|
|
ui->arrivalTimeEdit->setReadOnly(readOnly);
|
|
ui->departureTimeEdit->setReadOnly(readOnly);
|
|
|
|
ui->descriptionEdit->setReadOnly(readOnly);
|
|
|
|
ui->editCoupledBut->setEnabled(!readOnly);
|
|
ui->editUncoupledBut->setEnabled(!readOnly);
|
|
}
|
|
|
|
void EditStopDialog::done(int val)
|
|
{
|
|
if (val == QDialog::Accepted)
|
|
{
|
|
if (stopIdx.row() < stopModel->rowCount() - 2)
|
|
{
|
|
// We are not last stop
|
|
|
|
// Check if train has at least one engine after this stop
|
|
// But not if we are Last stop (size - 1 - AddHere)
|
|
// because the train doesn't have to leave the station
|
|
bool electricOnNonElectrifiedLine = false;
|
|
if (!couplingMgr->hasEngineAfterStop(&electricOnNonElectrifiedLine)
|
|
|| electricOnNonElectrifiedLine)
|
|
{
|
|
int ret = QMessageBox::warning(
|
|
this, tr("No Engine Left"),
|
|
electricOnNonElectrifiedLine
|
|
? tr("It seems you have uncoupled all job engines except for electric ones "
|
|
"but the line is not electrified\n"
|
|
"(The train isn't able to move)\n"
|
|
"Do you want to couple a non electric engine?")
|
|
: tr("It seems you have uncoupled all job engines\n"
|
|
"(The train isn't able to move)\n"
|
|
"Do you want to couple an engine?"),
|
|
QMessageBox::Yes | QMessageBox::No);
|
|
|
|
if (ret == QMessageBox::Yes)
|
|
{
|
|
return; // Second chance to edit couplings
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_AUTO_TIME_RECALC
|
|
if (originalSpeedAfterStop != newSpeedAfterStop)
|
|
{
|
|
int speedBefore = originalSpeedAfterStop;
|
|
int speedAfter = newSpeedAfterStop;
|
|
|
|
LinesModel *linesModel = stopModel->getLinesModel();
|
|
db_id lineId = curLine ? curLine : stopIdx.data(NEXT_LINE_ROLE).toLongLong();
|
|
int lineSpeed = linesModel->getLineSpeed(lineId);
|
|
if (speedBefore == 0)
|
|
{
|
|
// If speed is null (likely because there weren't RS coupled before)
|
|
// Fall back to line max speed
|
|
speedBefore = lineSpeed;
|
|
}
|
|
if (speedAfter == 0)
|
|
{
|
|
// If speed is null (likely because there isn't RS coupled after this stop)
|
|
// Fall back to line max speed
|
|
speedAfter = lineSpeed;
|
|
}
|
|
int ret = QMessageBox::question(
|
|
this, tr("Train Speed Changed"),
|
|
tr("Train speed after this stop has changed from a value of %1 km/h to <b>%2 "
|
|
"km/h</b><br>"
|
|
"Do you want to rebase travel times to this new speed?<br>"
|
|
"NOTE: this doesn't affect stop times but you will lose manual adjustments to "
|
|
"travel times")
|
|
.arg(speedBefore)
|
|
.arg(speedAfter),
|
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
|
|
|
|
if (ret == QMessageBox::Cancel)
|
|
{
|
|
return; // Second chance to edit couplings
|
|
}
|
|
|
|
if (ret == QMessageBox::Yes)
|
|
{
|
|
stopModel->rebaseTimesToSpeed(stopIdx.row(), ui->arrivalTimeEdit->time(),
|
|
ui->departureTimeEdit->time());
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
saveDataToModel();
|
|
}
|
|
|
|
QDialog::done(val);
|
|
}
|