Add .clang-format file

Run clang-format 16
This commit is contained in:
Filippo Gentile 2023-06-25 13:12:52 +02:00
parent 42c486f5cf
commit 9f14ee1c64
383 changed files with 14150 additions and 13200 deletions

55
.clang-format Normal file
View File

@ -0,0 +1,55 @@
---
BasedOnStyle: Microsoft
AlignEscapedNewlines: Left
IndentWidth: 4
ColumnLimit: 100
# We want a space between the type and the star for pointer types.
PointerBindsToType: false
# We want to break before the operators, but not before a '='.
BreakBeforeBinaryOperators: NonAssignment
# Braces are usually attached, but not after functions or class declarations.
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterCaseLabel: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
BeforeLambdaBody: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
# Indent width for line continuations.
ContinuationIndentWidth: 2
# Allow indentation for preprocessing directives (if/ifdef/endif). https://reviews.llvm.org/rL312125
IndentPPDirectives: AfterHash
# Do not indent public/private/protected
IndentAccessModifiers: false
# This is needed because IndentAccessModifiers doesn't seem to work
AccessModifierOffset: -4
BreakConstructorInitializers: AfterColon
PackConstructorInitializers: Never
# Horizontally align arguments after an open bracket.
AlignAfterOpenBracket: true
SortIncludes: false
InsertNewlineAtEOF: true
AlignConsecutiveMacros: AcrossEmptyLines
AlignConsecutiveAssignments: AcrossEmptyLines

View File

@ -46,23 +46,29 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS
QMutexLocker lock(&logMutex);
QString str;
//const QString fmt = QStringLiteral("%1: %2 (%3:%4, %5)\n");
// const QString fmt = QStringLiteral("%1: %2 (%3:%4, %5)\n");
static const QString fmt = QStringLiteral("%1: %2\n");
switch (type) {
switch (type)
{
case QtDebugMsg:
str = fmt.arg(QStringLiteral("Debug"), msg); //.arg(context.file).arg(context.line).arg(context.function);
str = fmt.arg(QStringLiteral("Debug"),
msg); //.arg(context.file).arg(context.line).arg(context.function);
break;
case QtInfoMsg:
str = fmt.arg(QStringLiteral("Info"), msg); //.arg(context.file).arg(context.line).arg(context.function);
str = fmt.arg(QStringLiteral("Info"),
msg); //.arg(context.file).arg(context.line).arg(context.function);
break;
case QtWarningMsg:
str = fmt.arg(QStringLiteral("Warning"), msg); //.arg(context.file).arg(context.line).arg(context.function);
str = fmt.arg(QStringLiteral("Warning"),
msg); //.arg(context.file).arg(context.line).arg(context.function);
break;
case QtCriticalMsg:
str = fmt.arg(QStringLiteral("Critical"), msg); //.arg(context.file).arg(context.line).arg(context.function);
str = fmt.arg(QStringLiteral("Critical"),
msg); //.arg(context.file).arg(context.line).arg(context.function);
break;
case QtFatalMsg:
str = fmt.arg(QStringLiteral("Fatal"), msg); //.arg(context.file).arg(context.line).arg(context.function);
str = fmt.arg(QStringLiteral("Fatal"),
msg); //.arg(context.file).arg(context.line).arg(context.function);
}
QTextStream s(gLogFile());
@ -73,114 +79,124 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS
void setupLogger()
{
//const QString path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
// const QString path = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
QString path = MeetingSession::appDataPath;
if(qApp->arguments().contains("--test"))
path = qApp->applicationDirPath(); //If testing use exe folder instead of AppData: see MeetingSession
if (qApp->arguments().contains("--test"))
path = qApp->applicationDirPath(); // If testing use exe folder instead of AppData:
// see MeetingSession
QFile *logFile = gLogFile();
logFile->setFileName(path + QStringLiteral("/logs/mrtp_log.log"));
logFile->open(QFile::WriteOnly | QFile::Append | QFile::Text);
if(!logFile->isOpen()) //FIXME: if logFile gets too big, ask user to truncate it
if (!logFile->isOpen()) // FIXME: if logFile gets too big, ask user to truncate it
{
QDir dir(path);
dir.mkdir("logs");
logFile->open(QFile::WriteOnly | QFile::Append | QFile::Text);
}
if(logFile->isOpen())
if (logFile->isOpen())
{
defaultHandler = qInstallMessageHandler(myMessageOutput);
}
else {
qDebug() << "Cannot open Log file:" << logFile->fileName() << "Error:" << logFile->errorString();
else
{
qDebug() << "Cannot open Log file:" << logFile->fileName()
<< "Error:" << logFile->errorString();
}
}
int main(int argc, char *argv[])
{
#ifdef GLOBAL_TRY_CATCH
try{
try
{
#endif
QApplication app(argc, argv);
QApplication::setOrganizationName(AppCompany);
//QApplication::setApplicationName(AppProduct);
QApplication::setApplicationDisplayName(AppDisplayName);
QApplication::setApplicationVersion(AppVersion);
QApplication app(argc, argv);
QApplication::setOrganizationName(AppCompany);
// QApplication::setApplicationName(AppProduct);
QApplication::setApplicationDisplayName(AppDisplayName);
QApplication::setApplicationVersion(AppVersion);
MeetingSession::locateAppdata();
MeetingSession::locateAppdata();
setupLogger();
setupLogger();
qDebug() << QApplication::applicationDisplayName()
<< "Version:" << QApplication::applicationVersion()
<< "Built:" << AppBuildDate
<< "Website: " << AppProjectWebSite;
qDebug() << "Qt:" << QT_VERSION_STR;
qDebug() << "Sqlite:" << sqlite3_libversion() << " DB Format: V" << FormatVersion;
qDebug() << QDateTime::currentDateTime().toString("dd/MM/yyyy HH:mm");
qDebug() << QApplication::applicationDisplayName()
<< "Version:" << QApplication::applicationVersion() << "Built:" << AppBuildDate
<< "Website: " << AppProjectWebSite;
qDebug() << "Qt:" << QT_VERSION_STR;
qDebug() << "Sqlite:" << sqlite3_libversion() << " DB Format: V" << FormatVersion;
qDebug() << QDateTime::currentDateTime().toString("dd/MM/yyyy HH:mm");
//Check SQLite thread safety
int val = sqlite3_threadsafe();
if(val != 1)
{
//Not thread safe
qWarning() << "SQLite Library was not compiled with SQLITE_THREADSAFE=1. This may cause crashes of this application.";
}
MeetingSession meetingSession;
utils::language::loadTranslationsFromSettings();
MainWindow w;
w.showNormal();
w.resize(800, 600);
w.showMaximized();
if(argc > 1) //FIXME: better handling if there are extra arguments
{
QString fileName = app.arguments().at(1);
qDebug() << "Trying to load:" << fileName;
if(QFile(fileName).exists())
// Check SQLite thread safety
int val = sqlite3_threadsafe();
if (val != 1)
{
w.loadFile(app.arguments().at(1));
// Not thread safe
qWarning() << "SQLite Library was not compiled with SQLITE_THREADSAFE=1. This may "
"cause crashes of this application.";
}
}
qDebug() << "Running...";
MeetingSession meetingSession;
utils::language::loadTranslationsFromSettings();
int ret = app.exec();
QThreadPool::globalInstance()->waitForDone(1000);
DB_Error err = Session->closeDB();
MainWindow w;
w.showNormal();
w.resize(800, 600);
w.showMaximized();
if(err == DB_Error::DbBusyWhenClosing || QThreadPool::globalInstance()->activeThreadCount() > 0)
{
qWarning() << "Error: Application closing while threadpool still running or database busy!";
QThreadPool::globalInstance()->waitForDone(10000);
Session->closeDB();
}
if (argc > 1) // FIXME: better handling if there are extra arguments
{
QString fileName = app.arguments().at(1);
qDebug() << "Trying to load:" << fileName;
if (QFile(fileName).exists())
{
w.loadFile(app.arguments().at(1));
}
}
return ret;
qDebug() << "Running...";
int ret = app.exec();
QThreadPool::globalInstance()->waitForDone(1000);
DB_Error err = Session->closeDB();
if (err == DB_Error::DbBusyWhenClosing
|| QThreadPool::globalInstance()->activeThreadCount() > 0)
{
qWarning()
<< "Error: Application closing while threadpool still running or database busy!";
QThreadPool::globalInstance()->waitForDone(10000);
Session->closeDB();
}
return ret;
#ifdef GLOBAL_TRY_CATCH
}catch(const char* str)
}
catch (const char *str)
{
qDebug() << "Exception:" << str;
throw;
}catch(const std::string& str)
}
catch (const std::string &str)
{
qDebug() << "Exception:" << str.c_str();
throw;
}catch(sqlite3pp::database_error& e)
}
catch (sqlite3pp::database_error &e)
{
qDebug() << "Exception:" << e.what();
throw;
}catch(std::exception& e)
}
catch (std::exception &e)
{
qDebug() << "Exception:" << e.what();
throw;
}catch(...)
}
catch (...)
{
qDebug() << "Caught generic exception";
throw;

View File

@ -24,7 +24,6 @@
#include "viewmanager/viewmanager.h"
#include "jobs/jobeditor/jobpatheditor.h"
#include <QDockWidget>
@ -52,7 +51,7 @@
#include "printing/wizard/printwizard.h"
#ifdef ENABLE_USER_QUERY
#include "sqlconsole/sqlconsole.h"
# include "sqlconsole/sqlconsole.h"
#endif
#include <QActionGroup>
@ -61,10 +60,10 @@
#include "searchbox/searchresultmodel.h"
#ifdef ENABLE_BACKGROUND_MANAGER
#include "backgroundmanager/backgroundmanager.h"
#include "backgroundmanager/backgroundresultpanel.h"
#include "jobs/jobs_checker/crossing/jobcrossingchecker.h"
#include "rollingstock/rs_checker/rscheckermanager.h"
# include "backgroundmanager/backgroundmanager.h"
# include "backgroundmanager/backgroundresultpanel.h"
# include "jobs/jobs_checker/crossing/jobcrossingchecker.h"
# include "rollingstock/rs_checker/rscheckermanager.h"
#endif // ENABLE_BACKGROUND_MANAGER
#include "propertiesdialog.h"
@ -100,47 +99,47 @@ MainWindow::MainWindow(QWidget *parent) :
ui->setupUi(this);
ui->actionAbout->setText(tr("About %1").arg(qApp->applicationDisplayName()));
auto viewMgr = Session->getViewManager();
auto viewMgr = Session->getViewManager();
viewMgr->m_mainWidget = this;
auto graphMgr = viewMgr->getLineGraphMgr();
auto graphMgr = viewMgr->getLineGraphMgr();
connect(graphMgr, &LineGraphManager::jobSelected, this, &MainWindow::onJobSelected);
//view = graphMgr->getView();
// view = graphMgr->getView();
view = new LineGraphWidget(this);
//Welcome label
// Welcome label
welcomeLabel = new QLabel(this);
welcomeLabel->setTextFormat(Qt::RichText);
welcomeLabel->setAlignment(Qt::AlignCenter);
welcomeLabel->setFont(QFont("Arial", 15));
welcomeLabel->setObjectName("WelcomeLabel");
//JobPathEditor dock
jobEditor = new JobPathEditor(this);
// JobPathEditor dock
jobEditor = new JobPathEditor(this);
viewMgr->jobEditor = jobEditor;
jobDock = new QDockWidget(tr("Job Editor"), this);
jobDock = new QDockWidget(tr("Job Editor"), this);
jobDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
jobDock->setWidget(jobEditor);
jobDock->installEventFilter(this); //NOTE: see MainWindow::eventFilter() below
jobDock->installEventFilter(this); // NOTE: see MainWindow::eventFilter() below
addDockWidget(Qt::RightDockWidgetArea, jobDock);
ui->menuView->addAction(jobDock->toggleViewAction());
connect(jobDock->toggleViewAction(), &QAction::triggered, jobEditor, &JobPathEditor::show);
#ifdef ENABLE_BACKGROUND_MANAGER
//Background Errors dock
// Background Errors dock
BackgroundResultPanel *resPanel = new BackgroundResultPanel(this);
resPanelDock = new QDockWidget(tr("Errors"), this);
resPanelDock = new QDockWidget(tr("Errors"), this);
resPanelDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
resPanelDock->setWidget(resPanel);
resPanelDock->installEventFilter(this); //NOTE: see eventFilter() below
resPanelDock->installEventFilter(this); // NOTE: see eventFilter() below
addDockWidget(Qt::BottomDockWidgetArea, resPanelDock);
ui->menuView->addAction(resPanelDock->toggleViewAction());
ui->mainToolBar->addAction(resPanelDock->toggleViewAction());
//Add checkers FIXME: move to session?
// Add checkers FIXME: move to session?
JobCrossingChecker *jobCrossingChecker = new JobCrossingChecker(Session->m_Db, this);
Session->getBackgroundManager()->addChecker(jobCrossingChecker);
@ -148,22 +147,24 @@ MainWindow::MainWindow(QWidget *parent) :
Session->getBackgroundManager()->addChecker(rsChecker);
#endif // ENABLE_BACKGROUND_MANAGER
//Allow JobPathEditor to use all vertical space when RsErrorWidget dock is at bottom
// Allow JobPathEditor to use all vertical space when RsErrorWidget dock is at bottom
setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
//Search Box
// Search Box
SearchResultModel *searchModel = new SearchResultModel(Session->m_Db, this);
searchEdit = new CustomCompletionLineEdit(searchModel, this);
searchEdit = new CustomCompletionLineEdit(searchModel, this);
searchEdit->setMinimumWidth(300);
searchEdit->setMinimumHeight(25);
searchEdit->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
searchEdit->setPlaceholderText(tr("Find"));
searchEdit->setClearButtonEnabled(true);
connect(searchEdit, &CustomCompletionLineEdit::completionDone, this, &MainWindow::onJobSearchItemSelected);
connect(searchModel, &SearchResultModel::resultsReady, this, &MainWindow::onJobSearchResultsReady);
connect(searchEdit, &CustomCompletionLineEdit::completionDone, this,
&MainWindow::onJobSearchItemSelected);
connect(searchModel, &SearchResultModel::resultsReady, this,
&MainWindow::onJobSearchResultsReady);
QWidget* spacer = new QWidget();
QWidget *spacer = new QWidget();
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
ui->mainToolBar->addWidget(spacer);
ui->mainToolBar->addWidget(searchEdit);
@ -172,12 +173,11 @@ MainWindow::MainWindow(QWidget *parent) :
setCentralWidgetMode(CentralWidgetMode::StartPageMode);
QMenu *recentFilesMenu = new QMenu(this);
for(int i = 0; i < MaxRecentFiles; i++)
for (int i = 0; i < MaxRecentFiles; i++)
{
recentFileActs[i] = new QAction(this);
recentFileActs[i]->setVisible(false);
connect(recentFileActs[i], &QAction::triggered,
this, &MainWindow::onOpenRecent);
connect(recentFileActs[i], &QAction::triggered, this, &MainWindow::onOpenRecent);
recentFilesMenu->addAction(recentFileActs[i]);
}
@ -186,7 +186,7 @@ MainWindow::MainWindow(QWidget *parent) :
ui->actionOpen_Recent->setMenu(recentFilesMenu);
//Listen to changes to display welcomeLabel or view
// Listen to changes to display welcomeLabel or view
connect(Session, &MeetingSession::segmentAdded, this, &MainWindow::checkLineNumber);
connect(Session, &MeetingSession::segmentRemoved, this, &MainWindow::checkLineNumber);
connect(Session, &MeetingSession::lineAdded, this, &MainWindow::checkLineNumber);
@ -240,11 +240,13 @@ void MainWindow::setup_actions()
connect(ui->actionProperties, &QAction::triggered, this, &MainWindow::onProperties);
connect(ui->actionStations, &QAction::triggered, this, &MainWindow::onStationManager);
connect(ui->actionRollingstockManager, &QAction::triggered, this, &MainWindow::onRollingStockManager);
connect(ui->actionRollingstockManager, &QAction::triggered, this,
&MainWindow::onRollingStockManager);
connect(ui->actionJob_Shifts, &QAction::triggered, this, &MainWindow::onShiftManager);
connect(ui->action_JobsMgr, &QAction::triggered, this, &MainWindow::onJobsManager);
connect(ui->actionRS_Session_Viewer, &QAction::triggered, this, &MainWindow::onSessionRSViewer);
connect(ui->actionMeeting_Information, &QAction::triggered, this, &MainWindow::onMeetingInformation);
connect(ui->actionMeeting_Information, &QAction::triggered, this,
&MainWindow::onMeetingInformation);
connect(ui->actionAddJob, &QAction::triggered, this, &MainWindow::onAddJob);
connect(ui->actionRemoveJob, &QAction::triggered, this, &MainWindow::onRemoveJob);
@ -263,16 +265,22 @@ void MainWindow::setup_actions()
connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
ui->actionNext_Job_Segment->setToolTip(tr("Hold shift and click to go to <b>last</b> job stop."));
ui->actionPrev_Job_Segment->setToolTip(tr("Hold shift and click to go to <b>first</b> job stop."));
connect(ui->actionNext_Job_Segment, &QAction::triggered, this, []()
ui->actionNext_Job_Segment->setToolTip(
tr("Hold shift and click to go to <b>last</b> job stop."));
ui->actionPrev_Job_Segment->setToolTip(
tr("Hold shift and click to go to <b>first</b> job stop."));
connect(ui->actionNext_Job_Segment, &QAction::triggered, this,
[]()
{
bool shiftPressed = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool shiftPressed =
QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
Session->getViewManager()->requestJobShowPrevNextSegment(false, shiftPressed);
});
connect(ui->actionPrev_Job_Segment, &QAction::triggered, this, []()
connect(ui->actionPrev_Job_Segment, &QAction::triggered, this,
[]()
{
bool shiftPressed = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
bool shiftPressed =
QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
Session->getViewManager()->requestJobShowPrevNextSegment(true, shiftPressed);
});
}
@ -284,15 +292,14 @@ void MainWindow::about()
msgBox->setWindowTitle(tr("About %1").arg(qApp->applicationDisplayName()));
const QString translatedText =
tr(
"<h3>%1</h3>"
"<p>This program makes it easier to deal with timetables and trains.</p>"
"<p>Version: <b>%2</b></p>"
"<p>Built: %3</p>"
"<p>Website: <a href='%4'>%4</a></p>")
.arg(qApp->applicationDisplayName(), qApp->applicationVersion(),
QDate::fromString(AppBuildDate, QLatin1String("MMM dd yyyy")).toString("dd/MM/yyyy"),
AppProjectWebSite);
tr("<h3>%1</h3>"
"<p>This program makes it easier to deal with timetables and trains.</p>"
"<p>Version: <b>%2</b></p>"
"<p>Built: %3</p>"
"<p>Website: <a href='%4'>%4</a></p>")
.arg(qApp->applicationDisplayName(), qApp->applicationVersion(),
QDate::fromString(AppBuildDate, QLatin1String("MMM dd yyyy")).toString("dd/MM/yyyy"),
AppProjectWebSite);
msgBox->setTextFormat(Qt::RichText);
msgBox->setText(translatedText);
@ -309,15 +316,14 @@ void MainWindow::onOpen()
#endif
#ifdef ENABLE_BACKGROUND_MANAGER
if(Session->getBackgroundManager()->isRunning())
if (Session->getBackgroundManager()->isRunning())
{
int ret = QMessageBox::warning(this,
tr("Backgroung Task"),
tr("Background task for checking rollingstock errors is still running.\n"
"Do you want to cancel it?"),
QMessageBox::Yes, QMessageBox::No,
QMessageBox::Yes);
if(ret == QMessageBox::Yes)
int ret = QMessageBox::warning(
this, tr("Backgroung Task"),
tr("Background task for checking rollingstock errors is still running.\n"
"Do you want to cancel it?"),
QMessageBox::Yes, QMessageBox::No, QMessageBox::Yes);
if (ret == QMessageBox::Yes)
Session->getBackgroundManager()->abortAllTasks();
else
return;
@ -335,22 +341,21 @@ void MainWindow::onOpen()
filters << FileFormats::tr(FileFormats::allFiles);
dlg->setNameFilters(filters);
if(dlg->exec() != QDialog::Accepted || !dlg)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
QString fileName = dlg->selectedUrls().value(0).toLocalFile();
if(fileName.isEmpty())
if (fileName.isEmpty())
return;
RecentDirStore::setPath(directory_key::session, fileName);
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
if(!QThreadPool::globalInstance()->waitForDone(2000))
if (!QThreadPool::globalInstance()->waitForDone(2000))
{
QMessageBox::warning(this,
tr("Background Tasks"),
QMessageBox::warning(this, tr("Background Tasks"),
tr("Some background tasks are still running.\n"
"The file was not opened. Try again."));
QApplication::restoreOverrideCursor();
@ -362,10 +367,10 @@ void MainWindow::onOpen()
loadFile(fileName);
}
void MainWindow::loadFile(const QString& fileName)
void MainWindow::loadFile(const QString &fileName)
{
DEBUG_ENTRY;
if(fileName.isEmpty())
if (fileName.isEmpty())
return;
qDebug() << "Loading:" << fileName;
@ -376,86 +381,91 @@ void MainWindow::loadFile(const QString& fileName)
QApplication::restoreOverrideCursor();
if(err == DB_Error::FormatTooOld)
if (err == DB_Error::FormatTooOld)
{
int but = QMessageBox::warning(this, tr("Version is old"),
tr("This file was created by an older version of %1.\n"
"Opening it without conversion might not work and even crash the application.\n"
"Do you want to open it anyway?").arg(qApp->applicationDisplayName()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if(but == QMessageBox::Yes)
int but = QMessageBox::warning(
this, tr("Version is old"),
tr("This file was created by an older version of %1.\n"
"Opening it without conversion might not work and even crash the application.\n"
"Do you want to open it anyway?")
.arg(qApp->applicationDisplayName()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (but == QMessageBox::Yes)
err = Session->openDB(fileName, true);
}
else if(err == DB_Error::FormatTooNew)
else if (err == DB_Error::FormatTooNew)
{
if(err == DB_Error::FormatTooOld)
if (err == DB_Error::FormatTooOld)
{
int but = QMessageBox::warning(this, tr("Version is too new"),
tr("This file was created by a newer version of %1.\n"
"You should update the application first. Opening this file might not work or even crash.\n"
"Do you want to open it anyway?").arg(qApp->applicationDisplayName()),
"You should update the application first. Opening "
"this file might not work or even crash.\n"
"Do you want to open it anyway?")
.arg(qApp->applicationDisplayName()),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if(but == QMessageBox::Yes)
if (but == QMessageBox::Yes)
err = Session->openDB(fileName, true);
}
}
if(err == DB_Error::DbBusyWhenClosing)
if (err == DB_Error::DbBusyWhenClosing)
showCloseWarning();
if(err != DB_Error::NoError)
if (err != DB_Error::NoError)
return;
setCurrentFile(fileName);
//Fake we are coming from Start Page
//Otherwise we cannot show the first line
// Fake we are coming from Start Page
// Otherwise we cannot show the first line
m_mode = CentralWidgetMode::StartPageMode;
checkLineNumber();
if(!Session->checkImportRSTablesEmpty())
if (!Session->checkImportRSTablesEmpty())
{
//Probably the application crashed before finishing RS importation
//Give user choice to resume it or discard
// Probably the application crashed before finishing RS importation
// Give user choice to resume it or discard
OwningQPointer<QMessageBox> msgBox = new QMessageBox(
QMessageBox::Warning,
tr("RS Import"),
tr("There is some rollingstock import data left in this file. "
"Probably the application has crashed!<br>"
"Before deleting it would you like to resume importation?<br>"
"<i>(Sorry for the crash, would you like to contact me and share information about it?)</i>"),
QMessageBox::NoButton, this);
OwningQPointer<QMessageBox> msgBox =
new QMessageBox(QMessageBox::Warning, tr("RS Import"),
tr("There is some rollingstock import data left in this file. "
"Probably the application has crashed!<br>"
"Before deleting it would you like to resume importation?<br>"
"<i>(Sorry for the crash, would you like to contact me and share "
"information about it?)</i>"),
QMessageBox::NoButton, this);
auto resumeBut = msgBox->addButton(tr("Resume importation"), QMessageBox::YesRole);
msgBox->addButton(tr("Just delete it"), QMessageBox::NoRole);
msgBox->setDefaultButton(resumeBut);
msgBox->setTextFormat(Qt::RichText);
msgBox->exec();
if(!msgBox)
if (!msgBox)
return;
if(msgBox->clickedButton() == resumeBut)
if (msgBox->clickedButton() == resumeBut)
{
Session->getViewManager()->resumeRSImportation();
}else{
}
else
{
Session->clearImportRSTables();
}
}
}
void MainWindow::setCurrentFile(const QString& fileName)
void MainWindow::setCurrentFile(const QString &fileName)
{
DEBUG_ENTRY;
if(fileName.isEmpty())
if (fileName.isEmpty())
{
setWindowFilePath(QString()); //Reset title bar
setWindowFilePath(QString()); // Reset title bar
return;
}
//Qt automatically takes care of showing stripped filename in window title
// Qt automatically takes care of showing stripped filename in window title
setWindowFilePath(fileName);
QStringList files = AppSettings.getRecentFiles();
@ -472,22 +482,23 @@ void MainWindow::setCurrentFile(const QString& fileName)
QString MainWindow::strippedName(const QString &fullFileName, bool *ok)
{
QFileInfo fi(fullFileName);
if(ok) *ok = fi.exists();
if (ok)
*ok = fi.exists();
return fi.fileName();
}
void MainWindow::updateRecentFileActions()
{
DEBUG_ENTRY;
QStringList files = AppSettings.getRecentFiles();
QStringList files = AppSettings.getRecentFiles();
int numRecentFiles = qMin(files.size(), int(MaxRecentFiles));
for (int i = 0; i < numRecentFiles; i++)
{
bool ok = true;
bool ok = true;
QString name = strippedName(files[i], &ok);
if(name.isEmpty() || !ok)
if (name.isEmpty() || !ok)
{
files.removeAt(i);
i--;
@ -511,8 +522,8 @@ void MainWindow::updateRecentFileActions()
void MainWindow::onOpenRecent()
{
DEBUG_ENTRY;
QAction *act = qobject_cast<QAction*>(sender());
if(!act)
QAction *act = qobject_cast<QAction *>(sender());
if (!act)
return;
loadFile(act->data().toString());
@ -527,15 +538,14 @@ void MainWindow::onNew()
#endif
#ifdef ENABLE_BACKGROUND_MANAGER
if(Session->getBackgroundManager()->isRunning())
if (Session->getBackgroundManager()->isRunning())
{
int ret = QMessageBox::warning(this,
tr("Backgroung Task"),
tr("Background task for checking rollingstock errors is still running.\n"
"Do you want to cancel it?"),
QMessageBox::Yes, QMessageBox::No,
QMessageBox::Yes);
if(ret == QMessageBox::Yes)
int ret = QMessageBox::warning(
this, tr("Backgroung Task"),
tr("Background task for checking rollingstock errors is still running.\n"
"Do you want to cancel it?"),
QMessageBox::Yes, QMessageBox::No, QMessageBox::Yes);
if (ret == QMessageBox::Yes)
Session->getBackgroundManager()->abortAllTasks();
else
return;
@ -553,22 +563,21 @@ void MainWindow::onNew()
filters << FileFormats::tr(FileFormats::allFiles);
dlg->setNameFilters(filters);
if(dlg->exec() != QDialog::Accepted || !dlg)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
QString fileName = dlg->selectedUrls().value(0).toLocalFile();
if(fileName.isEmpty())
if (fileName.isEmpty())
return;
RecentDirStore::setPath(directory_key::session, fileName);
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
if(!QThreadPool::globalInstance()->waitForDone(2000))
if (!QThreadPool::globalInstance()->waitForDone(2000))
{
QMessageBox::warning(this,
tr("Background Tasks"),
QMessageBox::warning(this, tr("Background Tasks"),
tr("Some background tasks are still running.\n"
"The new file was not created. Try again."));
QApplication::restoreOverrideCursor();
@ -576,17 +585,17 @@ void MainWindow::onNew()
}
QFile f(fileName);
if(f.exists())
if (f.exists())
f.remove();
DB_Error err = Session->createNewDB(fileName);
QApplication::restoreOverrideCursor();
if(err == DB_Error::DbBusyWhenClosing)
if (err == DB_Error::DbBusyWhenClosing)
showCloseWarning();
if(err != DB_Error::NoError)
if (err != DB_Error::NoError)
return;
setCurrentFile(fileName);
@ -595,7 +604,7 @@ void MainWindow::onNew()
void MainWindow::onSave()
{
if(!Session->getViewManager()->closeEditors())
if (!Session->getViewManager()->closeEditors())
return;
Session->releaseAllSavepoints();
@ -605,7 +614,7 @@ void MainWindow::onSaveCopyAs()
{
DEBUG_ENTRY;
if(!Session->getViewManager()->closeEditors())
if (!Session->getViewManager()->closeEditors())
return;
OwningQPointer<QFileDialog> dlg = new QFileDialog(this, tr("Save Session Copy"));
@ -619,29 +628,30 @@ void MainWindow::onSaveCopyAs()
filters << FileFormats::tr(FileFormats::allFiles);
dlg->setNameFilters(filters);
if(dlg->exec() != QDialog::Accepted || !dlg)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
QString fileName = dlg->selectedUrls().value(0).toLocalFile();
if(fileName.isEmpty())
if (fileName.isEmpty())
return;
RecentDirStore::setPath(directory_key::session, fileName);
QFile f(fileName);
if(f.exists())
if (f.exists())
f.remove();
database backupDB(fileName.toUtf8(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
int rc = Session->m_Db.backup(backupDB, [](int pageCount, int remaining, int res)
int rc = Session->m_Db.backup(backupDB,
[](int pageCount, int remaining, int res)
{
Q_UNUSED(res)
qDebug() << pageCount << "/" << remaining;
});
if(rc != SQLITE_OK && rc != SQLITE_DONE)
if (rc != SQLITE_OK && rc != SQLITE_DONE)
{
QString errMsg = Session->m_Db.error_msg();
qDebug() << Session->m_Db.error_code() << errMsg;
@ -651,7 +661,7 @@ void MainWindow::onSaveCopyAs()
void MainWindow::closeEvent(QCloseEvent *e)
{
if(closeSession())
if (closeSession())
e->accept();
else
e->ignore();
@ -659,15 +669,14 @@ void MainWindow::closeEvent(QCloseEvent *e)
void MainWindow::showCloseWarning()
{
QMessageBox::warning(this,
tr("Error while Closing"),
QMessageBox::warning(this, tr("Error while Closing"),
tr("There was an error while closing the database.\n"
"Make sure there aren't any background tasks running and try again."));
}
void MainWindow::stopCloseTimer()
{
if(closeTimerId)
if (closeTimerId)
{
killTimer(closeTimerId);
closeTimerId = 0;
@ -701,36 +710,36 @@ void MainWindow::setCentralWidgetMode(MainWindow::CentralWidgetMode mode)
#endif // ENABLE_BACKGROUND_MANAGER
welcomeLabel->setText(
tr("<p><b>There are no lines in this session</b></p>"
"<p>"
"<table align=\"center\">"
"<tr>"
"<td>Start by creating the railway layout for this session:</td>"
"</tr>"
"<tr>"
"<td>"
"<table>"
"<tr>"
"<td>1.</td>"
"<td>Create stations (<b>Edit</b> > <b>Stations</b>)</td>"
"</tr>"
"<tr>"
"<td>2.</td>"
"<td>Create railway lines (<b>Edit</b> > <b>Stations</b> > <b>Lines Tab</b>)</td>"
"</tr>"
"<tr>"
"<td>3.</td>"
"<td>Add stations to railway lines</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td>(<b>Edit</b> > <b>Stations</b> > <b>Lines Tab</b> > <b>Edit Line</b>)</td>"
"</tr>"
"</table>"
"</td>"
"</tr>"
"</table>"
"</p>"));
tr("<p><b>There are no lines in this session</b></p>"
"<p>"
"<table align=\"center\">"
"<tr>"
"<td>Start by creating the railway layout for this session:</td>"
"</tr>"
"<tr>"
"<td>"
"<table>"
"<tr>"
"<td>1.</td>"
"<td>Create stations (<b>Edit</b> > <b>Stations</b>)</td>"
"</tr>"
"<tr>"
"<td>2.</td>"
"<td>Create railway lines (<b>Edit</b> > <b>Stations</b> > <b>Lines Tab</b>)</td>"
"</tr>"
"<tr>"
"<td>3.</td>"
"<td>Add stations to railway lines</td>"
"</tr>"
"<tr>"
"<td></td>"
"<td>(<b>Edit</b> > <b>Stations</b> > <b>Lines Tab</b> > <b>Edit Line</b>)</td>"
"</tr>"
"</table>"
"</td>"
"</tr>"
"</table>"
"</p>"));
break;
}
case CentralWidgetMode::ViewSessionMode:
@ -748,37 +757,39 @@ void MainWindow::setCentralWidgetMode(MainWindow::CentralWidgetMode mode)
enableDBActions(mode != CentralWidgetMode::StartPageMode);
if(mode == CentralWidgetMode::ViewSessionMode)
if (mode == CentralWidgetMode::ViewSessionMode)
{
if(centralWidget() != view)
if (centralWidget() != view)
{
takeCentralWidget(); //Remove ownership from welcomeLabel
takeCentralWidget(); // Remove ownership from welcomeLabel
setCentralWidget(view);
view->show();
welcomeLabel->hide();
}
//Enable Job Creation
// Enable Job Creation
ui->actionAddJob->setEnabled(true);
ui->actionAddJob->setToolTip(tr("Add train job"));
//Update actions based on Job selection
JobStopEntry selectedJob = Session->getViewManager()->getLineGraphMgr()->getCurrentSelectedJob();
// Update actions based on Job selection
JobStopEntry selectedJob =
Session->getViewManager()->getLineGraphMgr()->getCurrentSelectedJob();
onJobSelected(selectedJob.jobId);
}
else
{
if(centralWidget() != welcomeLabel)
if (centralWidget() != welcomeLabel)
{
takeCentralWidget(); //Remove ownership from LineGraphWidget
takeCentralWidget(); // Remove ownership from LineGraphWidget
setCentralWidget(welcomeLabel);
view->hide();
welcomeLabel->show();
}
//If there aren't lines prevent from creating jobs
// If there aren't lines prevent from creating jobs
ui->actionAddJob->setEnabled(false);
ui->actionAddJob->setToolTip(tr("You must create at least one railway segment before adding job to this session"));
ui->actionAddJob->setToolTip(
tr("You must create at least one railway segment before adding job to this session"));
ui->actionRemoveJob->setEnabled(false);
}
@ -799,8 +810,8 @@ void MainWindow::onProperties()
void MainWindow::onMeetingInformation()
{
OwningQPointer<MeetingInformationDialog> dlg = new MeetingInformationDialog(this);
int ret = dlg->exec();
if(dlg && ret == QDialog::Accepted)
int ret = dlg->exec();
if (dlg && ret == QDialog::Accepted)
dlg->saveData();
}
@ -808,11 +819,11 @@ bool MainWindow::closeSession()
{
DB_Error err = Session->closeDB();
if(err == DB_Error::DbBusyWhenClosing)
if (err == DB_Error::DbBusyWhenClosing)
{
if(closeTimerId)
if (closeTimerId)
{
//We already tried again
// We already tried again
stopCloseTimer();
@ -820,19 +831,19 @@ bool MainWindow::closeSession()
return false;
}
//Start a timer to try again
// Start a timer to try again
closeTimerId = startTimer(1500);
return false;
}
stopCloseTimer();
if(err != DB_Error::NoError && err != DB_Error::DbNotOpen)
if (err != DB_Error::NoError && err != DB_Error::DbNotOpen)
return false;
setCentralWidgetMode(CentralWidgetMode::StartPageMode);
//Reset filePath to refresh title
// Reset filePath to refresh title
setCurrentFile(QString());
return true;
@ -842,7 +853,7 @@ void MainWindow::enableDBActions(bool enable)
{
databaseActionGroup->setEnabled(enable);
searchEdit->setEnabled(enable);
if(!enable)
if (!enable)
jobEditor->setEnabled(false);
#ifdef ENABLE_BACKGROUND_MANAGER
@ -924,30 +935,30 @@ void MainWindow::checkLineNumber()
{
RailwaySegmentHelper helper(Session->m_Db);
bool isLine = false;
bool isLine = false;
db_id graphObjId = 0;
if(!helper.findFirstLineOrSegment(graphObjId, isLine))
if (!helper.findFirstLineOrSegment(graphObjId, isLine))
graphObjId = 0;
if(graphObjId && m_mode != CentralWidgetMode::ViewSessionMode)
if (graphObjId && m_mode != CentralWidgetMode::ViewSessionMode)
{
//First line was added or newly opened file -> Session has at least one line
// First line was added or newly opened file -> Session has at least one line
setCentralWidgetMode(CentralWidgetMode::ViewSessionMode);
//Load first line or segment
// Load first line or segment
view->tryLoadGraph(graphObjId,
isLine ? LineGraphType::RailwayLine : LineGraphType::RailwaySegment);
}
else if(graphObjId == 0 && m_mode != CentralWidgetMode::NoLinesWarningMode)
else if (graphObjId == 0 && m_mode != CentralWidgetMode::NoLinesWarningMode)
{
//Last line removed -> Session has no line
// Last line removed -> Session has no line
setCentralWidgetMode(CentralWidgetMode::NoLinesWarningMode);
}
}
void MainWindow::timerEvent(QTimerEvent *e)
{
if(e->timerId() == closeTimerId)
if (e->timerId() == closeTimerId)
{
closeSession();
return;
@ -964,38 +975,32 @@ void MainWindow::onJobSelected(db_id jobId)
ui->actionRemoveJob->setEnabled(selected);
QString removeJobTooltip;
if(selected)
if (selected)
removeJobTooltip = tr("Remove selected Job");
else
removeJobTooltip = tr("First select a Job by double click on graph or type in search box");
ui->actionRemoveJob->setToolTip(removeJobTooltip);
}
//QT-BUG 69922: If user closes a floating dock widget, when shown again it cannot dock anymore
//HACK: intercept dock close event and manually re-dock and hide so next time is shown it's docked
//NOTE: calling directly 'QDockWidget::setFloating(false)' from inside 'eventFinter()' causes CRASH
// so queue it. Cannot use 'QMetaObject::invokeMethod()' because it's not a slot.
// QT-BUG 69922: If user closes a floating dock widget, when shown again it cannot dock anymore
// HACK: intercept dock close event and manually re-dock and hide so next time is shown it's docked
// NOTE: calling directly 'QDockWidget::setFloating(false)' from inside 'eventFinter()' causes CRASH
// so queue it. Cannot use 'QMetaObject::invokeMethod()' because it's not a slot.
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if(watched == jobDock && event->type() == QEvent::Close)
if (watched == jobDock && event->type() == QEvent::Close)
{
if(jobDock->isFloating())
if (jobDock->isFloating())
{
QTimer::singleShot(0, jobDock, [this]()
{
jobDock->setFloating(false);
});
QTimer::singleShot(0, jobDock, [this]() { jobDock->setFloating(false); });
}
}
#ifdef ENABLE_BACKGROUND_MANAGER
else if(watched == resPanelDock && event->type() == QEvent::Close)
else if (watched == resPanelDock && event->type() == QEvent::Close)
{
if(resPanelDock->isFloating())
if (resPanelDock->isFloating())
{
QTimer::singleShot(0, resPanelDock, [this]()
{
resPanelDock->setFloating(false);
});
QTimer::singleShot(0, resPanelDock, [this]() { resPanelDock->setFloating(false); });
}
}
#endif // ENABLE_BACKGROUND_MANAGER
@ -1012,10 +1017,10 @@ void MainWindow::onJobSearchItemSelected()
{
db_id jobId = 0;
QString tmp;
if(!searchEdit->getData(jobId, tmp))
if (!searchEdit->getData(jobId, tmp))
return;
searchEdit->clear(); //Clear text
searchEdit->clear(); // Clear text
Session->getViewManager()->requestJobSelection(jobId, true, true);
}

View File

@ -57,7 +57,7 @@ public:
void loadFile(const QString &fileName);
bool closeSession();
private slots:
void onStationManager();
@ -140,8 +140,11 @@ private:
QActionGroup *databaseActionGroup;
enum { MaxRecentFiles = 5 };
QAction* recentFileActs[MaxRecentFiles];
enum
{
MaxRecentFiles = 5
};
QAction *recentFileActs[MaxRecentFiles];
CentralWidgetMode m_mode;

View File

@ -47,7 +47,7 @@ PropertiesDialog::PropertiesDialog(QWidget *parent) :
pathReadOnlyEdit->setPlaceholderText(tr("No opened file"));
pathReadOnlyEdit->setReadOnly(true);
//TODO: make pretty and maybe add other informations like metadata versions
// TODO: make pretty and maybe add other informations like metadata versions
setMinimumSize(200, 200);
}

View File

@ -28,19 +28,20 @@ Scope::Scope(const char *fn, const char *s, const char *e) :
start(s),
end(e)
{
qDebug().nospace().noquote()
<< start << QByteArray(" ").repeated(stackLevel) << ">>> " << func << end;
qDebug().nospace().noquote() << start << QByteArray(" ").repeated(stackLevel) << ">>> " << func
<< end;
stackLevel++;
}
Scope::~Scope()
{
stackLevel--;
qDebug().nospace().noquote()
<< start << QByteArray(" ").repeated(stackLevel) << "<<< " << func << end;
qDebug().nospace().noquote() << start << QByteArray(" ").repeated(stackLevel) << "<<< " << func
<< end;
}
ScopeTimer::ScopeTimer(const char *fn, const char *s, const char *e) :Scope(fn, s, e)
ScopeTimer::ScopeTimer(const char *fn, const char *s, const char *e) :
Scope(fn, s, e)
{
timer.start();
}

View File

@ -20,57 +20,52 @@
#ifndef SCOPEDEBUG_H
#define SCOPEDEBUG_H
#define SHELL_RESET "\033[0m"
#define SHELL_RESET "\033[0m"
#define SHELL_RED "\033[031m"
#define SHELL_GREEN "\033[032m"
#define SHELL_YELLOW "\033[033m"
#define SHELL_BLUE "\033[034m"
#define SHELL_RED "\033[031m"
#define SHELL_GREEN "\033[032m"
#define SHELL_YELLOW "\033[033m"
#define SHELL_BLUE "\033[034m"
#include <QDebug>
#include <QString>
#include <QElapsedTimer>
//#define NO_DEBUG_CALL_TRACE
// #define NO_DEBUG_CALL_TRACE
#ifndef NO_DEBUG_CALL_TRACE
class Scope
{
public:
Scope(const char *fn, const char *s="", const char* e="");
Scope(const char *fn, const char *s = "", const char *e = "");
~Scope();
const char *func, *start, *end;
};
class ScopeTimer : Scope
{
public:
ScopeTimer(const char *fn, const char *s="", const char* e="");
ScopeTimer(const char *fn, const char *s = "", const char *e = "");
~ScopeTimer();
QElapsedTimer timer;
};
# define DEBUG_ENTRY_NAME(name) Scope DBG(name)
# define DEBUG_ENTRY DEBUG_ENTRY_NAME(__PRETTY_FUNCTION__)
# define DEBUG_COLOR_ENTRY(color) Scope DBG(__PRETTY_FUNCTION__, color, SHELL_RESET)
# define DEBUG_IMPORTANT_ENTRY DEBUG_COLOR_ENTRY(SHELL_GREEN)
# define DEBUG_ENTRY_NAME(name) Scope DBG(name)
# define DEBUG_ENTRY DEBUG_ENTRY_NAME(__PRETTY_FUNCTION__)
# define DEBUG_COLOR_ENTRY(color) Scope DBG(__PRETTY_FUNCTION__, color, SHELL_RESET)
# define DEBUG_IMPORTANT_ENTRY DEBUG_COLOR_ENTRY(SHELL_GREEN)
# define DEBUG_TIME_ENTRY ScopeTimer DBG(__PRETTY_FUNCTION__)
# define DEBUG_TIME_ENTRY ScopeTimer DBG(__PRETTY_FUNCTION__)
#else
# define DEBUG_ENTRY_NAME(name)
# define DEBUG_ENTRY
# define DEBUG_COLOR_ENTRY(color)
# define DEBUG_IMPORTANT_ENTRY
# define DEBUG_TIME_ENTRY
# define DEBUG_ENTRY_NAME(name)
# define DEBUG_ENTRY
# define DEBUG_COLOR_ENTRY(color)
# define DEBUG_IMPORTANT_ENTRY
# define DEBUG_TIME_ENTRY
#endif // NO_DEBUG_CALLTRACE
#endif // SCOPEDEBUG_H

View File

@ -29,7 +29,7 @@
#include "db_metadata/metadatamanager.h"
#ifdef ENABLE_BACKGROUND_MANAGER
#include "backgroundmanager/backgroundmanager.h"
# include "backgroundmanager/backgroundmanager.h"
#endif // ENABLE_BACKGROUND_MANAGER
#include <QStandardPaths>
@ -37,7 +37,7 @@
#include <QTranslator>
MeetingSession* MeetingSession::session;
MeetingSession *MeetingSession::session;
QString MeetingSession::appDataPath;
const QLocale MeetingSession::embeddedLocale = QLocale(QLocale::English, QLocale::UnitedStates);
@ -54,13 +54,14 @@ MeetingSession::MeetingSession() :
m_Db(nullptr),
sheetExportTranslator(nullptr)
{
session = this; //Global singleton pointer
session = this; // Global singleton pointer
QString settings_file;
if(qApp->arguments().contains("test"))
if (qApp->arguments().contains("test"))
{
//If testing use exe folder instead of AppData
settings_file = QCoreApplication::applicationDirPath() + QStringLiteral("/mrtp_settings.ini");
// If testing use exe folder instead of AppData
settings_file =
QCoreApplication::applicationDirPath() + QStringLiteral("/mrtp_settings.ini");
}
loadSettings(settings_file);
@ -76,7 +77,7 @@ MeetingSession::MeetingSession() :
MeetingSession::~MeetingSession()
{
//Delete sheet export translator
// Delete sheet export translator
setSheetExportTranslator(nullptr, sheetExportLocale);
}
@ -90,29 +91,29 @@ DB_Error MeetingSession::openDB(const QString &str, bool ignoreVersion)
DEBUG_ENTRY;
DB_Error err = closeDB();
if(err != DB_Error::NoError && err != DB_Error::DbNotOpen)
if (err != DB_Error::NoError && err != DB_Error::DbNotOpen)
{
return err;
}
//try{
if(m_Db.connect(str.toUtf8(), SQLITE_OPEN_READWRITE) != SQLITE_OK)
// try{
if (m_Db.connect(str.toUtf8(), SQLITE_OPEN_READWRITE) != SQLITE_OK)
{
//throw database_error(m_Db);
// throw database_error(m_Db);
qWarning() << "DB:" << m_Db.error_msg();
return DB_Error::GenericError;
}
if(!ignoreVersion)
if (!ignoreVersion)
{
qint64 version = 0;
switch (metaDataMgr->getInt64(version, MetaDataKey::FormatVersionKey))
{
case MetaDataKey::Result::ValueFound:
{
if(version < FormatVersion)
if (version < FormatVersion)
return DB_Error::FormatTooOld;
else if(version > FormatVersion)
else if (version > FormatVersion)
return DB_Error::FormatTooNew;
break;
}
@ -123,38 +124,38 @@ DB_Error MeetingSession::openDB(const QString &str, bool ignoreVersion)
fileName = str;
//Enable foreign keys to ensure no invalid operation is allowed
// Enable foreign keys to ensure no invalid operation is allowed
m_Db.enable_foreign_keys(true);
m_Db.enable_extended_result_codes(true);
// }catch(const char *msg)
// {
// QMessageBox::warning(nullptr,
// QObject::tr("Error"),
// QObject::tr("Error while opening file:\n%1\n'%2'")
// .arg(str)
// .arg(msg));
// throw;
// return false;
// }
// catch(std::exception& e)
// {
// QMessageBox::warning(nullptr,
// QObject::tr("Error"),
// QObject::tr("Error while opening file:\n%1\n'%2'")
// .arg(str)
// .arg(e.what()));
// throw;
// return false;
// }
// catch(...)
// {
// QMessageBox::warning(nullptr,
// QObject::tr("Error"),
// QObject::tr("Unknown error while opening file:\n%1").arg(str));
// throw;
// return false;
// }
// }catch(const char *msg)
// {
// QMessageBox::warning(nullptr,
// QObject::tr("Error"),
// QObject::tr("Error while opening file:\n%1\n'%2'")
// .arg(str)
// .arg(msg));
// throw;
// return false;
// }
// catch(std::exception& e)
// {
// QMessageBox::warning(nullptr,
// QObject::tr("Error"),
// QObject::tr("Error while opening file:\n%1\n'%2'")
// .arg(str)
// .arg(e.what()));
// throw;
// return false;
// }
// catch(...)
// {
// QMessageBox::warning(nullptr,
// QObject::tr("Error"),
// QObject::tr("Unknown error while opening file:\n%1").arg(str));
// throw;
// return false;
// }
#ifdef ENABLE_BACKGROUND_MANAGER
backgroundManager->handleSessionLoaded();
@ -167,7 +168,7 @@ DB_Error MeetingSession::closeDB()
{
DEBUG_ENTRY;
if(!m_Db.db())
if (!m_Db.db())
return DB_Error::DbNotOpen;
#ifdef SEARCHBOX_MODE_ASYNC
@ -178,27 +179,27 @@ DB_Error MeetingSession::closeDB()
backgroundManager->abortAllTasks();
#endif
if(!viewManager->closeEditors())
return DB_Error::EditorsStillOpened; //User wants to continue editing
if (!viewManager->closeEditors())
return DB_Error::EditorsStillOpened; // User wants to continue editing
//Close all graphs
// Close all graphs
viewManager->clearAllLineGraphs();
releaseAllSavepoints();
//Calls sqlite3_close(), not forcing closing db like sqlite3_close_v2
//So in case the database is still used by some background task (returns SQLITE_BUSY)
//we abort closing and return. It's like nevere having closed, database is 100% working
// Calls sqlite3_close(), not forcing closing db like sqlite3_close_v2
// So in case the database is still used by some background task (returns SQLITE_BUSY)
// we abort closing and return. It's like nevere having closed, database is 100% working
int rc = m_Db.disconnect();
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
qWarning() << "Err: closing db" << m_Db.error_code() << m_Db.error_msg();
if(rc == SQLITE_BUSY)
if (rc == SQLITE_BUSY)
{
return DB_Error::DbBusyWhenClosing;
}
//return false;
// return false;
}
#ifdef ENABLE_BACKGROUND_MANAGER
@ -210,22 +211,24 @@ DB_Error MeetingSession::closeDB()
return DB_Error::NoError;
}
DB_Error MeetingSession::createNewDB(const QString& file)
DB_Error MeetingSession::createNewDB(const QString &file)
{
DEBUG_ENTRY;
DB_Error err = closeDB();
if(err != DB_Error::NoError && err != DB_Error::DbNotOpen)
if (err != DB_Error::NoError && err != DB_Error::DbNotOpen)
{
return err;
}
int result = SQLITE_OK;
#define CHECK(code) if((code) != SQLITE_OK) qWarning() << __LINE__ << (code) << m_Db.error_code() << m_Db.error_msg()
#define CHECK(code) \
if ((code) != SQLITE_OK) \
qWarning() << __LINE__ << (code) << m_Db.error_code() << m_Db.error_msg()
result = m_Db.connect(file.toUtf8(), SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
CHECK(result);
//See 'openDB()'
// See 'openDB()'
m_Db.enable_foreign_keys(true);
m_Db.enable_extended_result_codes(true);
@ -237,7 +240,7 @@ DB_Error MeetingSession::createNewDB(const QString& file)
* if not explicitly set, it will trigger SQLITE_CONSTRAINT_FOREIGNKEY error.
*/
//Tables
// Tables
result = m_Db.execute("CREATE TABLE rs_models ("
"id INTEGER,"
"name TEXT,"
@ -278,81 +281,87 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"CHECK(length(name)>0) )");
CHECK(result);
result = m_Db.execute("CREATE TABLE station_tracks ("
"id INTEGER PRIMARY KEY,"
"station_id INTEGER NOT NULL,"
"pos INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"track_length_cm INTEGER NOT NULL,"
"platf_length_cm INTEGET NOT NULL,"
"freight_length_cm INTEGER NOT NULL,"
"max_axes INTEGER NOT NULL,"
"color_rgb INTEGER,"
"name TEXT NOT NULL,"
"CHECK("
" length(name)>0 AND max_axes>=2 AND track_length_cm>0"
" AND (platf_length_cm BETWEEN 0 AND track_length_cm)"
" AND (freight_length_cm BETWEEN 0 AND track_length_cm)"
"),"
"UNIQUE(station_id, pos),"
"UNIQUE(station_id, name),"
"FOREIGN KEY (station_id) REFERENCES stations(id) ON UPDATE CASCADE ON DELETE CASCADE )");
result = m_Db.execute(
"CREATE TABLE station_tracks ("
"id INTEGER PRIMARY KEY,"
"station_id INTEGER NOT NULL,"
"pos INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"track_length_cm INTEGER NOT NULL,"
"platf_length_cm INTEGET NOT NULL,"
"freight_length_cm INTEGER NOT NULL,"
"max_axes INTEGER NOT NULL,"
"color_rgb INTEGER,"
"name TEXT NOT NULL,"
"CHECK("
" length(name)>0 AND max_axes>=2 AND track_length_cm>0"
" AND (platf_length_cm BETWEEN 0 AND track_length_cm)"
" AND (freight_length_cm BETWEEN 0 AND track_length_cm)"
"),"
"UNIQUE(station_id, pos),"
"UNIQUE(station_id, name),"
"FOREIGN KEY (station_id) REFERENCES stations(id) ON UPDATE CASCADE ON DELETE CASCADE )");
CHECK(result);
result = m_Db.execute("CREATE TABLE station_gates ("
"id INTEGER PRIMARY KEY,"
"station_id INTEGER NOT NULL,"
"out_track_count INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"def_in_platf_id INTEGER,"
"name TEXT NOT NULL,"
"side INTEGER NOT NULL,"
"CHECK("
" out_track_count>0 AND (type&(1<<0) OR type&(1<<1))"
" AND (length(name)=1 AND name BETWEEN 'A' AND 'Z')"
")," //NOTE: see utils::GateType
"FOREIGN KEY (station_id) REFERENCES stations(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY(def_in_platf_id) REFERENCES station_tracks(id) ON UPDATE CASCADE ON DELETE SET NULL,"
"UNIQUE(station_id,name) )");
result = m_Db.execute(
"CREATE TABLE station_gates ("
"id INTEGER PRIMARY KEY,"
"station_id INTEGER NOT NULL,"
"out_track_count INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"def_in_platf_id INTEGER,"
"name TEXT NOT NULL,"
"side INTEGER NOT NULL,"
"CHECK("
" out_track_count>0 AND (type&(1<<0) OR type&(1<<1))"
" AND (length(name)=1 AND name BETWEEN 'A' AND 'Z')"
")," // NOTE: see utils::GateType
"FOREIGN KEY (station_id) REFERENCES stations(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY(def_in_platf_id) REFERENCES station_tracks(id) ON UPDATE CASCADE ON DELETE SET "
"NULL,"
"UNIQUE(station_id,name) )");
CHECK(result);
result = m_Db.execute("CREATE TABLE station_gate_connections ("
"id INTEGER PRIMARY KEY,"
"track_id INTEGER NOT NULL,"
"track_side INTEGER NOT NULL,"
"gate_id INTEGER NOT NULL,"
"gate_track INTEGER NOT NULL,"
"UNIQUE(gate_id,track_id,track_side,gate_track),"
"FOREIGN KEY (track_id) REFERENCES station_tracks(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY (gate_id) REFERENCES station_gates(id) ON UPDATE CASCADE ON DELETE CASCADE )");
result = m_Db.execute(
"CREATE TABLE station_gate_connections ("
"id INTEGER PRIMARY KEY,"
"track_id INTEGER NOT NULL,"
"track_side INTEGER NOT NULL,"
"gate_id INTEGER NOT NULL,"
"gate_track INTEGER NOT NULL,"
"UNIQUE(gate_id,track_id,track_side,gate_track),"
"FOREIGN KEY (track_id) REFERENCES station_tracks(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY (gate_id) REFERENCES station_gates(id) ON UPDATE CASCADE ON DELETE CASCADE )");
CHECK(result);
result = m_Db.execute("CREATE TABLE railway_segments ("
"id INTEGER PRIMARY KEY,"
"in_gate_id INTEGER NOT NULL,"
"out_gate_id INTEGER NOT NULL,"
"name TEXT,"
"max_speed_kmh INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"distance_meters INTEGER NOT NULL,"
"UNIQUE(in_gate_id),"
"UNIQUE(out_gate_id),"
"FOREIGN KEY(in_gate_id) REFERENCES station_gates(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY(out_gate_id) REFERENCES station_gates(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"CHECK(in_gate_id<>out_gate_id AND"
" max_speed_kmh>=10 AND"
" distance_meters>=100 AND"
" length(name)>0) )");
result = m_Db.execute(
"CREATE TABLE railway_segments ("
"id INTEGER PRIMARY KEY,"
"in_gate_id INTEGER NOT NULL,"
"out_gate_id INTEGER NOT NULL,"
"name TEXT,"
"max_speed_kmh INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"distance_meters INTEGER NOT NULL,"
"UNIQUE(in_gate_id),"
"UNIQUE(out_gate_id),"
"FOREIGN KEY(in_gate_id) REFERENCES station_gates(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY(out_gate_id) REFERENCES station_gates(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"CHECK(in_gate_id<>out_gate_id AND"
" max_speed_kmh>=10 AND"
" distance_meters>=100 AND"
" length(name)>0) )");
CHECK(result);
result = m_Db.execute("CREATE TABLE railway_connections ("
"id INTEGER PRIMARY KEY,"
"seg_id INTEGER NOT NULL,"
"in_track INTEGER NOT NULL,"
"out_track INTEGER NOT NULL,"
"UNIQUE(seg_id,in_track),"
"UNIQUE(seg_id,out_track),"
"FOREIGN KEY(seg_id) REFERENCES railway_segments(id) ON UPDATE CASCADE ON DELETE RESTRICT )");
result = m_Db.execute(
"CREATE TABLE railway_connections ("
"id INTEGER PRIMARY KEY,"
"seg_id INTEGER NOT NULL,"
"in_track INTEGER NOT NULL,"
"out_track INTEGER NOT NULL,"
"UNIQUE(seg_id,in_track),"
"UNIQUE(seg_id,out_track),"
"FOREIGN KEY(seg_id) REFERENCES railway_segments(id) ON UPDATE CASCADE ON DELETE RESTRICT )");
CHECK(result);
result = m_Db.execute("CREATE TABLE lines ("
@ -361,17 +370,18 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"start_meters INTEGER NOT NULL DEFAULT 0 )");
CHECK(result);
result = m_Db.execute("CREATE TABLE line_segments ("
"id INTEGER PRIMARY KEY,"
"line_id INTEGER NOT NULL,"
"seg_id INTEGER NOT NULL,"
"direction INTEGER NOT NULL,"
"pos INTEGER NOT NULL,"
"FOREIGN KEY(line_id) REFERENCES lines(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY(seg_id) REFERENCES railway_segments(id) ON UPDATE CASCADE ON DELETE RESTRICT,"
"UNIQUE(line_id, seg_id)"
"UNIQUE(line_id, pos)"
"CHECK(pos<100) )"); //Allow up to 100 segments for each line
result = m_Db.execute(
"CREATE TABLE line_segments ("
"id INTEGER PRIMARY KEY,"
"line_id INTEGER NOT NULL,"
"seg_id INTEGER NOT NULL,"
"direction INTEGER NOT NULL,"
"pos INTEGER NOT NULL,"
"FOREIGN KEY(line_id) REFERENCES lines(id) ON UPDATE CASCADE ON DELETE CASCADE,"
"FOREIGN KEY(seg_id) REFERENCES railway_segments(id) ON UPDATE CASCADE ON DELETE RESTRICT,"
"UNIQUE(line_id, seg_id)"
"UNIQUE(line_id, pos)"
"CHECK(pos<100) )"); // Allow up to 100 segments for each line
CHECK(result);
result = m_Db.execute("CREATE TABLE jobshifts ("
@ -379,34 +389,39 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"name TEXT UNIQUE NOT NULL)");
CHECK(result);
result = m_Db.execute("CREATE TABLE jobs ("
"id INTEGER PRIMARY KEY,"
"category INTEGER NOT NULL DEFAULT 0,"
"shift_id INTEGER,"
"FOREIGN KEY(shift_id) REFERENCES jobshifts(id) ON UPDATE CASCADE ON DELETE RESTRICT)");
result = m_Db.execute(
"CREATE TABLE jobs ("
"id INTEGER PRIMARY KEY,"
"category INTEGER NOT NULL DEFAULT 0,"
"shift_id INTEGER,"
"FOREIGN KEY(shift_id) REFERENCES jobshifts(id) ON UPDATE CASCADE ON DELETE RESTRICT)");
CHECK(result);
result = m_Db.execute("CREATE TABLE stops ("
"id INTEGER PRIMARY KEY,"
"job_id INTEGER NOT NULL,"
"station_id INTEGER,"
"arrival INTEGER NOT NULL,"
"departure INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"description TEXT,"
result = m_Db.execute(
"CREATE TABLE stops ("
"id INTEGER PRIMARY KEY,"
"job_id INTEGER NOT NULL,"
"station_id INTEGER,"
"arrival INTEGER NOT NULL,"
"departure INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"description TEXT,"
"in_gate_conn INTEGER,"
"out_gate_conn INTEGER,"
"next_segment_conn_id INTEGER,"
"in_gate_conn INTEGER,"
"out_gate_conn INTEGER,"
"next_segment_conn_id INTEGER,"
"CHECK(arrival<=departure),"
"UNIQUE(job_id,arrival),"
"UNIQUE(job_id,departure),"
"FOREIGN KEY(job_id) REFERENCES jobs(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(station_id) REFERENCES stations(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(in_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON DELETE RESTRICT,"
"FOREIGN KEY(out_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON DELETE RESTRICT,"
"FOREIGN KEY(next_segment_conn_id) REFERENCES railway_connections(id) ON UPDATE CASCADE ON DELETE RESTRICT )");
"CHECK(arrival<=departure),"
"UNIQUE(job_id,arrival),"
"UNIQUE(job_id,departure),"
"FOREIGN KEY(job_id) REFERENCES jobs(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(station_id) REFERENCES stations(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(in_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON "
"DELETE RESTRICT,"
"FOREIGN KEY(out_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON "
"DELETE RESTRICT,"
"FOREIGN KEY(next_segment_conn_id) REFERENCES railway_connections(id) ON UPDATE CASCADE ON "
"DELETE RESTRICT )");
CHECK(result);
result = m_Db.execute("CREATE TABLE coupling ("
@ -420,41 +435,47 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"UNIQUE(stop_id,rs_id))");
CHECK(result);
//Create also backup tables to save old stops and couplings before editing a job and restore them if user cancels the edits.
//NOTE: the structure of the table must be the same, remember to update theese if updating stops or couplings
// Create also backup tables to save old stops and couplings before editing a job and restore
// them if user cancels the edits. NOTE: the structure of the table must be the same, remember
// to update theese if updating stops or couplings
result = m_Db.execute("CREATE TABLE old_stops ("
"id INTEGER PRIMARY KEY,"
"job_id INTEGER NOT NULL,"
"station_id INTEGER,"
"arrival INTEGER NOT NULL,"
"departure INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"description TEXT,"
result = m_Db.execute(
"CREATE TABLE old_stops ("
"id INTEGER PRIMARY KEY,"
"job_id INTEGER NOT NULL,"
"station_id INTEGER,"
"arrival INTEGER NOT NULL,"
"departure INTEGER NOT NULL,"
"type INTEGER NOT NULL,"
"description TEXT,"
"in_gate_conn INTEGER,"
"out_gate_conn INTEGER,"
"next_segment_conn_id INTEGER,"
"in_gate_conn INTEGER,"
"out_gate_conn INTEGER,"
"next_segment_conn_id INTEGER,"
"CHECK(arrival<=departure),"
"UNIQUE(job_id,arrival),"
"UNIQUE(job_id,departure),"
"FOREIGN KEY(job_id) REFERENCES jobs(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(station_id) REFERENCES stations(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(in_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON DELETE RESTRICT,"
"FOREIGN KEY(out_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON DELETE RESTRICT,"
"FOREIGN KEY(next_segment_conn_id) REFERENCES railway_connections(id) ON UPDATE CASCADE ON DELETE RESTRICT )");
"CHECK(arrival<=departure),"
"UNIQUE(job_id,arrival),"
"UNIQUE(job_id,departure),"
"FOREIGN KEY(job_id) REFERENCES jobs(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(station_id) REFERENCES stations(id) ON DELETE RESTRICT ON UPDATE CASCADE,"
"FOREIGN KEY(in_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON "
"DELETE RESTRICT,"
"FOREIGN KEY(out_gate_conn) REFERENCES station_gate_connections(id) ON UPDATE CASCADE ON "
"DELETE RESTRICT,"
"FOREIGN KEY(next_segment_conn_id) REFERENCES railway_connections(id) ON UPDATE CASCADE ON "
"DELETE RESTRICT )");
CHECK(result);
result = m_Db.execute("CREATE TABLE old_coupling ("
"id INTEGER PRIMARY KEY,"
"stop_id INTEGER,"
"rs_id INTEGER,"
"operation INTEGER NOT NULL DEFAULT 0,"
result =
m_Db.execute("CREATE TABLE old_coupling ("
"id INTEGER PRIMARY KEY,"
"stop_id INTEGER,"
"rs_id INTEGER,"
"operation INTEGER NOT NULL DEFAULT 0,"
"FOREIGN KEY(stop_id) REFERENCES old_stops(id) ON DELETE CASCADE," //Old stops
"FOREIGN KEY(rs_id) REFERENCES rs_list(id) ON DELETE RESTRICT,"
"UNIQUE(stop_id,rs_id))");
"FOREIGN KEY(stop_id) REFERENCES old_stops(id) ON DELETE CASCADE," // Old stops
"FOREIGN KEY(rs_id) REFERENCES rs_list(id) ON DELETE RESTRICT,"
"UNIQUE(stop_id,rs_id))");
CHECK(result);
result = m_Db.execute("CREATE TABLE imported_rs_owners ("
@ -465,7 +486,8 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"match_existing_id INTEGER,"
"sheet_idx INTEGER,"
"PRIMARY KEY(id),"
"FOREIGN KEY(match_existing_id) REFERENCES rs_owners(id) ON UPDATE RESTRICT ON DELETE RESTRICT)");
"FOREIGN KEY(match_existing_id) REFERENCES rs_owners(id) ON UPDATE "
"RESTRICT ON DELETE RESTRICT)");
CHECK(result);
result = m_Db.execute("CREATE TABLE imported_rs_models ("
@ -480,7 +502,8 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"type INTEGER,"
"sub_type INTEGER,"
"PRIMARY KEY(id),"
"FOREIGN KEY(match_existing_id) REFERENCES rs_models(id) ON UPDATE RESTRICT ON DELETE RESTRICT)");
"FOREIGN KEY(match_existing_id) REFERENCES rs_models(id) ON UPDATE "
"RESTRICT ON DELETE RESTRICT)");
CHECK(result);
result = m_Db.execute("CREATE TABLE imported_rs_list ("
@ -491,8 +514,10 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"number INTEGER,"
"new_number INTEGER,"
"PRIMARY KEY(id),"
"FOREIGN KEY(model_id) REFERENCES imported_rs_models(id) ON UPDATE RESTRICT ON DELETE RESTRICT,"
"FOREIGN KEY(owner_id) REFERENCES imported_rs_owners(id) ON UPDATE RESTRICT ON DELETE RESTRICT)");
"FOREIGN KEY(model_id) REFERENCES imported_rs_models(id) ON UPDATE "
"RESTRICT ON DELETE RESTRICT,"
"FOREIGN KEY(owner_id) REFERENCES imported_rs_owners(id) ON UPDATE "
"RESTRICT ON DELETE RESTRICT)");
CHECK(result);
result = m_Db.execute("CREATE TABLE metadata ("
@ -500,50 +525,56 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"val BLOB)");
CHECK(result);
//Triggers
// Triggers
//Prevent multiple segments on same station gate
result = m_Db.execute("CREATE TRIGGER multiple_gate_segments\n"
"BEFORE INSERT ON railway_segments\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect same gate twice') FROM railway_segments WHERE in_gate_id=NEW.out_gate_id OR out_gate_id=NEW.in_gate_id;"
"END");
// Prevent multiple segments on same station gate
result =
m_Db.execute("CREATE TRIGGER multiple_gate_segments\n"
"BEFORE INSERT ON railway_segments\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect same gate twice') FROM railway_segments "
"WHERE in_gate_id=NEW.out_gate_id OR out_gate_id=NEW.in_gate_id;"
"END");
CHECK(result);
result = m_Db.execute("CREATE TRIGGER multiple_gate_segments_update_in\n"
"BEFORE UPDATE OF in_gate_id ON railway_segments\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect same gate twice') FROM railway_segments WHERE out_gate_id=NEW.in_gate_id;"
"SELECT RAISE(ABORT, 'Cannot connect same gate twice') FROM "
"railway_segments WHERE out_gate_id=NEW.in_gate_id;"
"END");
CHECK(result);
result = m_Db.execute("CREATE TRIGGER multiple_gate_segments_update_out\n"
"BEFORE UPDATE OF out_gate_id ON railway_segments\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect same gate twice') FROM railway_segments WHERE in_gate_id=NEW.out_gate_id;"
"SELECT RAISE(ABORT, 'Cannot connect same gate twice') FROM "
"railway_segments WHERE in_gate_id=NEW.out_gate_id;"
"END");
CHECK(result);
//Prevent connecting a track to a gate of a different station
result = m_Db.execute("CREATE TRIGGER gate_conn_different_station\n"
"BEFORE INSERT ON station_gate_connections\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect platform of a different station') FROM station_tracks t"
" JOIN station_gates g ON g.id=NEW.gate_id"
" WHERE t.id=NEW.track_id AND t.station_id<>g.station_id;"
"END");
// Prevent connecting a track to a gate of a different station
result = m_Db.execute(
"CREATE TRIGGER gate_conn_different_station\n"
"BEFORE INSERT ON station_gate_connections\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect platform of a different station') FROM station_tracks t"
" JOIN station_gates g ON g.id=NEW.gate_id"
" WHERE t.id=NEW.track_id AND t.station_id<>g.station_id;"
"END");
CHECK(result);
result = m_Db.execute("CREATE TRIGGER gate_conn_different_station_update\n"
"BEFORE UPDATE OF track_id,gate_id ON station_gate_connections\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect platform of a different station') FROM station_tracks t"
" JOIN station_gates g ON g.id=NEW.gate_id"
" WHERE t.id=NEW.track_id AND t.station_id<>g.station_id;"
"END");
result = m_Db.execute(
"CREATE TRIGGER gate_conn_different_station_update\n"
"BEFORE UPDATE OF track_id,gate_id ON station_gate_connections\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot connect platform of a different station') FROM station_tracks t"
" JOIN station_gates g ON g.id=NEW.gate_id"
" WHERE t.id=NEW.track_id AND t.station_id<>g.station_id;"
"END");
CHECK(result);
//FIXME: Remote possibility of updating 'station_id' of track or gate.
// FIXME: Remote possibility of updating 'station_id' of track or gate.
//Prevent connecting gate track out of bound
// Prevent connecting gate track out of bound
result = m_Db.execute("CREATE TRIGGER gate_conn_gate_track_bound\n"
"BEFORE INSERT ON station_gate_connections\n"
"BEGIN\n"
@ -563,18 +594,20 @@ DB_Error MeetingSession::createNewDB(const QString& file)
result = m_Db.execute("CREATE TRIGGER gate_out_track_bound_update\n"
"BEFORE UPDATE OF out_track_count ON station_gates\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Cannot remove gate tracks. Platforms connected.') FROM station_gate_connections c"
"SELECT RAISE(ABORT, 'Cannot remove gate tracks. Platforms connected.') "
"FROM station_gate_connections c"
" WHERE c.gate_id=NEW.id AND NEW.out_track_count<=c.gate_track;"
"END");
CHECK(result);
//Prevent setting gate default track to a track which is not connected to it
// Prevent setting gate default track to a track which is not connected to it
result = m_Db.execute("CREATE TRIGGER gate_def_platf_not_connected\n"
"BEFORE INSERT ON station_gates\n"
"BEGIN\n"
"SELECT RAISE(ABORT, 'Platform not connected to this gate') WHERE"
" NEW.def_in_platf_id NOT NULL AND NOT EXISTS ("
" SELECT 1 FROM station_gate_connections WHERE track_id=NEW.def_in_platf_id AND gate_id=NEW.id"
" SELECT 1 FROM station_gate_connections WHERE "
"track_id=NEW.def_in_platf_id AND gate_id=NEW.id"
");"
"END");
CHECK(result);
@ -584,13 +617,13 @@ DB_Error MeetingSession::createNewDB(const QString& file)
"BEGIN\n"
"SELECT RAISE(ABORT, 'Platform not connected to this gate') WHERE"
" NEW.def_in_platf_id NOT NULL AND NOT EXISTS ("
" SELECT 1 FROM station_gate_connections WHERE track_id=NEW.def_in_platf_id AND gate_id=NEW.id"
" SELECT 1 FROM station_gate_connections WHERE "
"track_id=NEW.def_in_platf_id AND gate_id=NEW.id"
");"
"END");
CHECK(result);
//FIXME: if setting default gate track but then delete track connection -> invalid state
// FIXME: if setting default gate track but then delete track connection -> invalid state
#undef CHECK
@ -605,25 +638,25 @@ DB_Error MeetingSession::createNewDB(const QString& file)
* Theese tables are used during RS importation and are cleared when the process
* completes or gets canceled by the user
* If they are not empty it might be because the application crashed before clearing theese tables
*/
*/
bool MeetingSession::checkImportRSTablesEmpty()
{
query q(m_Db, "SELECT COUNT(1) FROM imported_rs_list");
q.step();
int count = q.getRows().get<int>(0);
if(count)
if (count)
return false;
q.prepare("SELECT COUNT(1) FROM imported_rs_models");
q.step();
count = q.getRows().get<int>(0);
if(count)
if (count)
return false;
q.prepare("SELECT COUNT(1) FROM imported_rs_owners");
q.step();
count = q.getRows().get<int>(0);
if(count)
if (count)
return false;
return true;
}
@ -631,15 +664,15 @@ bool MeetingSession::checkImportRSTablesEmpty()
bool MeetingSession::clearImportRSTables()
{
command cmd(m_Db, "DELETE FROM imported_rs_list");
if(cmd.execute() != SQLITE_OK)
if (cmd.execute() != SQLITE_OK)
return false;
cmd.prepare("DELETE FROM imported_rs_models");
if(cmd.execute() != SQLITE_OK)
if (cmd.execute() != SQLITE_OK)
return false;
cmd.prepare("DELETE FROM imported_rs_owners");
if(cmd.execute() != SQLITE_OK)
if (cmd.execute() != SQLITE_OK)
return false;
return true;
@ -647,15 +680,15 @@ bool MeetingSession::clearImportRSTables()
bool MeetingSession::setSavepoint(const QString &pointname)
{
if(!m_Db.db())
if (!m_Db.db())
return false;
if(savepointList.contains(pointname))
if (savepointList.contains(pointname))
return true;
QString sql = QStringLiteral("SAVEPOINT %1;");
if(m_Db.execute(sql.arg(pointname).toUtf8()) != SQLITE_OK)
if (m_Db.execute(sql.arg(pointname).toUtf8()) != SQLITE_OK)
{
qDebug() << m_Db.error_msg();
return false;
@ -667,15 +700,15 @@ bool MeetingSession::setSavepoint(const QString &pointname)
bool MeetingSession::releaseSavepoint(const QString &pointname)
{
if(!m_Db.db())
if (!m_Db.db())
return false;
if(!savepointList.contains(pointname))
if (!savepointList.contains(pointname))
return true;
QString sql = QStringLiteral("RELEASE %1;");
if(m_Db.execute(sql.arg(pointname).toUtf8()) != SQLITE_OK)
if (m_Db.execute(sql.arg(pointname).toUtf8()) != SQLITE_OK)
{
qDebug() << m_Db.error_msg();
return false;
@ -689,15 +722,15 @@ bool MeetingSession::releaseSavepoint(const QString &pointname)
bool MeetingSession::revertToSavepoint(const QString &pointname)
{
if(!m_Db.db())
if (!m_Db.db())
return false;
if(!savepointList.contains(pointname))
if (!savepointList.contains(pointname))
return false;
QString sql = QStringLiteral("ROLLBACK TO SAVEPOINT %1;");
if(m_Db.execute(sql.arg(pointname).toUtf8()) != SQLITE_OK)
if (m_Db.execute(sql.arg(pointname).toUtf8()) != SQLITE_OK)
{
qDebug() << m_Db.error_msg();
return false;
@ -711,17 +744,17 @@ bool MeetingSession::revertToSavepoint(const QString &pointname)
bool MeetingSession::releaseAllSavepoints()
{
if(!m_Db.db())
if (!m_Db.db())
return false;
for(const QString& point : qAsConst(savepointList))
for (const QString &point : qAsConst(savepointList))
{
if(!releaseSavepoint(point))
if (!releaseSavepoint(point))
return false;
}
// When still in a transaction, commit that too
if(sqlite3_get_autocommit(m_Db.db()) == 0)
if (sqlite3_get_autocommit(m_Db.db()) == 0)
m_Db.execute("COMMIT;");
return true;
@ -729,9 +762,9 @@ bool MeetingSession::releaseAllSavepoints()
bool MeetingSession::revertAll()
{
for(const QString& point : qAsConst(savepointList))
for (const QString &point : qAsConst(savepointList))
{
if(!revertToSavepoint(point))
if (!revertToSavepoint(point))
return false;
}
return true;
@ -739,59 +772,59 @@ bool MeetingSession::revertAll()
QColor MeetingSession::colorForCat(JobCategory cat)
{
QColor col = settings.getCategoryColor(int(cat)); //TODO: maybe session-specific
if(col.isValid())
QColor col = settings.getCategoryColor(int(cat)); // TODO: maybe session-specific
if (col.isValid())
return col;
return QColor(Qt::gray); //Error
return QColor(Qt::gray); // Error
}
void MeetingSession::locateAppdata()
{
appDataPath = QStringLiteral("%1/%2/%3")
.arg(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation),
AppCompany, AppProductShort);
.arg(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation),
AppCompany, AppProductShort);
appDataPath = QDir::cleanPath(appDataPath);
qDebug() << appDataPath;
}
void MeetingSession::setSheetExportTranslator(QTranslator *translator, const QLocale &loc)
{
//NOTE: if Sheet Language is of Application Language then set a nullptr translator
//If translator is valid use it
//If translator is nullptr and language is default English locale, use hardcoded strings
//If translator is nullptr and language is different from English, use default translations
// NOTE: if Sheet Language is of Application Language then set a nullptr translator
// If translator is valid use it
// If translator is nullptr and language is default English locale, use hardcoded strings
// If translator is nullptr and language is different from English, use default translations
if(sheetExportTranslator && sheetExportTranslator != translator)
if (sheetExportTranslator && sheetExportTranslator != translator)
delete sheetExportTranslator;
sheetExportTranslator = translator;
sheetExportLocale = loc;
sheetExportLocale = loc;
}
void MeetingSession::loadSettings(const QString& settings_file)
void MeetingSession::loadSettings(const QString &settings_file)
{
DEBUG_ENTRY;
if(settings_file.isEmpty())
if (settings_file.isEmpty())
settings.loadSettings(appDataPath + QStringLiteral("/mrtp_settings.ini"));
else
settings.loadSettings(settings_file);
hourOffset = settings.getHourOffset();
stationOffset = settings.getStationOffset();
horizOffset = settings.getHorizontalOffset();
vertOffset = settings.getVerticalOffset();
platformOffset = settings.getPlatformOffset();
hourOffset = settings.getHourOffset();
stationOffset = settings.getStationOffset();
horizOffset = settings.getHorizontalOffset();
vertOffset = settings.getVerticalOffset();
platformOffset = settings.getPlatformOffset();
jobLineWidth = settings.getJobLineWidth();
jobLineWidth = settings.getJobLineWidth();
originalAppLocale = settings.getLanguage();
//Default initialize to same value of application language
// Default initialize to same value of application language
setSheetExportTranslator(nullptr, originalAppLocale);
}
#ifdef ENABLE_BACKGROUND_MANAGER
BackgroundManager* MeetingSession::getBackgroundManager() const
BackgroundManager *MeetingSession::getBackgroundManager() const
{
return backgroundManager.get();
}

View File

@ -33,7 +33,6 @@ using namespace sqlite3pp;
#include <settings/appsettings.h>
class ViewManager;
class MetaDataManager;
@ -65,53 +64,60 @@ class MeetingSession : public QObject
Q_OBJECT
private:
static MeetingSession* session;
static MeetingSession *session;
public:
MeetingSession();
~MeetingSession();
static MeetingSession* Get();
static MeetingSession *Get();
inline ViewManager* getViewManager() { return viewManager.get(); }
inline ViewManager *getViewManager()
{
return viewManager.get();
}
inline MetaDataManager* getMetaDataManager() { return metaDataMgr.get(); }
inline MetaDataManager *getMetaDataManager()
{
return metaDataMgr.get();
}
#ifdef ENABLE_BACKGROUND_MANAGER
BackgroundManager *getBackgroundManager() const;
#endif
signals:
//Shifts
// Shifts
void shiftAdded(db_id shiftId);
void shiftRemoved(db_id shiftId);
void shiftNameChanged(db_id shiftId);
//A job was added/removed/modified belonging to this shift
// A job was added/removed/modified belonging to this shift
void shiftJobsChanged(db_id shiftId, db_id jobId);
//Rollingstock
// Rollingstock
void rollingstockRemoved(db_id rsId);
void rollingStockPlanChanged(QSet<db_id> rsIds);
void rollingStockModified(db_id rsId);
//Jobs
// Jobs
void jobAdded(db_id jobId);
void jobChanged(db_id jobId, db_id oldJobId); //Updated id/category/stops
void jobChanged(db_id jobId, db_id oldJobId); // Updated id/category/stops
void jobRemoved(db_id jobId);
//Stations
// Stations
void stationNameChanged(db_id stationId);
void stationJobsPlanChanged(const QSet<db_id>& stationIds);
void stationTrackPlanChanged(const QSet<db_id>& stationIds);
void stationJobsPlanChanged(const QSet<db_id> &stationIds);
void stationTrackPlanChanged(const QSet<db_id> &stationIds);
void stationRemoved(db_id stationId);
//Segments
// Segments
void segmentAdded(db_id segmentId);
void segmentNameChanged(db_id segmentId);
void segmentStationsChanged(db_id segmentId);
void segmentRemoved(db_id segmentId);
//Lines
// Lines
void lineAdded(db_id lineId);
void lineNameChanged(db_id lineId);
void lineSegmentsChanged(db_id lineId);
@ -126,45 +132,48 @@ private:
std::unique_ptr<BackgroundManager> backgroundManager;
#endif
//Settings TODO: remove
// Settings TODO: remove
public:
void loadSettings(const QString &settings_file);
MRTPSettings settings;
int hourOffset;
int stationOffset;
qreal platformOffset;
int hourOffset;
int stationOffset;
qreal platformOffset;
int horizOffset;
int vertOffset;
int horizOffset;
int vertOffset;
int jobLineWidth;
//Database
// Database
public:
database m_Db;
//Job Categories:
// Job Categories:
public:
QColor colorForCat(JobCategory cat);
//Savepoints TODO: seem unused
// Savepoints TODO: seem unused
public:
inline bool getDBDirty() { return !savepointList.isEmpty(); }
inline bool getDBDirty()
{
return !savepointList.isEmpty();
}
bool setSavepoint(const QString& pointname = "RESTOREPOINT");
bool releaseSavepoint(const QString& pointname = "RESTOREPOINT");
bool revertToSavepoint(const QString& pointname = "RESTOREPOINT");
bool setSavepoint(const QString &pointname = "RESTOREPOINT");
bool releaseSavepoint(const QString &pointname = "RESTOREPOINT");
bool revertToSavepoint(const QString &pointname = "RESTOREPOINT");
bool releaseAllSavepoints();
bool revertAll();
QStringList savepointList;
//DB
// DB
public:
DB_Error createNewDB(const QString &file);
DB_Error openDB(const QString& str, bool ignoreVersion);
DB_Error openDB(const QString &str, bool ignoreVersion);
DB_Error closeDB();
bool checkImportRSTablesEmpty();
@ -172,7 +181,7 @@ public:
QString fileName;
//AppData
// AppData
public:
static void locateAppdata();
static QString appDataPath;
@ -228,14 +237,25 @@ public:
*/
static const QLocale embeddedLocale;
void setSheetExportTranslator(QTranslator *translator, const QLocale& loc);
void setSheetExportTranslator(QTranslator *translator, const QLocale &loc);
inline QTranslator *getSheetExportTranslator() const { return sheetExportTranslator; }
inline QLocale getSheetExportLocale() const { return sheetExportLocale; }
inline QLocale getAppLanguage() const { return originalAppLocale; }
inline QTranslator *getSheetExportTranslator() const
{
return sheetExportTranslator;
}
inline QLocale getSheetExportLocale() const
{
return sheetExportLocale;
}
inline QLocale getAppLanguage() const
{
return originalAppLocale;
}
};
#define Session MeetingSession::Get()
#define Session MeetingSession::Get()
#define AppSettings Session->settings

View File

@ -20,25 +20,23 @@
#include "backgroundmanager.h"
#ifdef ENABLE_BACKGROUND_MANAGER
#include "backgroundmanager/ibackgroundchecker.h"
# include "backgroundmanager/ibackgroundchecker.h"
#include <QThreadPool>
#include <QSet>
# include <QThreadPool>
# include <QSet>
BackgroundManager::BackgroundManager(QObject *parent) :
QObject(parent)
{
}
BackgroundManager::~BackgroundManager()
{
}
void BackgroundManager::handleSessionLoaded()
{
for(IBackgroundChecker *mgr : qAsConst(checkers))
for (IBackgroundChecker *mgr : qAsConst(checkers))
mgr->startWorker();
}
@ -46,19 +44,19 @@ void BackgroundManager::abortAllTasks()
{
emit abortTrivialTasks();
for(IBackgroundChecker *mgr : qAsConst(checkers))
for (IBackgroundChecker *mgr : qAsConst(checkers))
mgr->abortTasks();
}
bool BackgroundManager::isRunning()
{
bool running = QThreadPool::globalInstance()->activeThreadCount() > 0;
if(running)
if (running)
return true;
for(IBackgroundChecker *mgr : qAsConst(checkers))
for (IBackgroundChecker *mgr : qAsConst(checkers))
{
if(mgr->isRunning())
if (mgr->isRunning())
return true;
}
@ -69,10 +67,8 @@ void BackgroundManager::addChecker(IBackgroundChecker *mgr)
{
checkers.append(mgr);
connect(mgr, &IBackgroundChecker::destroyed, this, [this](QObject *self)
{
removeChecker(static_cast<IBackgroundChecker *>(self));
});
connect(mgr, &IBackgroundChecker::destroyed, this,
[this](QObject *self) { removeChecker(static_cast<IBackgroundChecker *>(self)); });
emit checkerAdded(mgr);
}
@ -86,7 +82,7 @@ void BackgroundManager::removeChecker(IBackgroundChecker *mgr)
void BackgroundManager::clearResults()
{
for(IBackgroundChecker *mgr : qAsConst(checkers))
for (IBackgroundChecker *mgr : qAsConst(checkers))
{
mgr->clearModel();
}

View File

@ -22,12 +22,12 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include <QObject>
#include <QVector>
# include <QObject>
# include <QVector>
class IBackgroundChecker;
//TODO: show a progress bar for all task like Qt Creator does
// TODO: show a progress bar for all task like Qt Creator does
class BackgroundManager : public QObject
{
Q_OBJECT

View File

@ -19,13 +19,13 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include "backgroundresultpanel.h"
# include "backgroundresultpanel.h"
#include "backgroundmanager/ibackgroundchecker.h"
#include "backgroundresultwidget.h"
# include "backgroundmanager/ibackgroundchecker.h"
# include "backgroundresultwidget.h"
#include "app/session.h"
#include "backgroundmanager.h"
# include "app/session.h"
# include "backgroundmanager.h"
BackgroundResultPanel::BackgroundResultPanel(QWidget *parent) :
QTabWidget(parent)
@ -34,7 +34,7 @@ BackgroundResultPanel::BackgroundResultPanel(QWidget *parent) :
connect(bkMgr, &BackgroundManager::checkerAdded, this, &BackgroundResultPanel::addChecker);
connect(bkMgr, &BackgroundManager::checkerRemoved, this, &BackgroundResultPanel::removeChecker);
for(auto mgr : bkMgr->checkers)
for (auto mgr : bkMgr->checkers)
addChecker(mgr);
}
@ -46,10 +46,10 @@ void BackgroundResultPanel::addChecker(IBackgroundChecker *mgr)
void BackgroundResultPanel::removeChecker(IBackgroundChecker *mgr)
{
for(int i = 0; i < count(); i++)
for (int i = 0; i < count(); i++)
{
BackgroundResultWidget *w = static_cast<BackgroundResultWidget *>(widget(i));
if(w->mgr == mgr)
if (w->mgr == mgr)
{
removeTab(i);
delete w;

View File

@ -22,7 +22,7 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include <QTabWidget>
# include <QTabWidget>
class IBackgroundChecker;

View File

@ -19,21 +19,20 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include "ibackgroundchecker.h"
#include "backgroundresultwidget.h"
# include "ibackgroundchecker.h"
# include "backgroundresultwidget.h"
#include <QTreeView>
#include <QHeaderView>
#include <QProgressBar>
#include <QPushButton>
# include <QTreeView>
# include <QHeaderView>
# include <QProgressBar>
# include <QPushButton>
#include <QGridLayout>
# include <QGridLayout>
#include <QMenu>
#include <QAction>
#include <QTimerEvent>
# include <QMenu>
# include <QAction>
# include <QTimerEvent>
BackgroundResultWidget::BackgroundResultWidget(IBackgroundChecker *mgr_, QWidget *parent) :
QWidget(parent),
@ -45,8 +44,8 @@ BackgroundResultWidget::BackgroundResultWidget(IBackgroundChecker *mgr_, QWidget
view->setSelectionBehavior(QTreeView::SelectRows);
progressBar = new QProgressBar;
startBut = new QPushButton(tr("Start"));
stopBut = new QPushButton(tr("Stop"));
startBut = new QPushButton(tr("Start"));
stopBut = new QPushButton(tr("Stop"));
startBut->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
stopBut->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
@ -68,7 +67,8 @@ BackgroundResultWidget::BackgroundResultWidget(IBackgroundChecker *mgr_, QWidget
connect(stopBut, &QPushButton::clicked, this, &BackgroundResultWidget::stopTask);
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, &QTreeView::customContextMenuRequested, this, &BackgroundResultWidget::showContextMenu);
connect(view, &QTreeView::customContextMenuRequested, this,
&BackgroundResultWidget::showContextMenu);
setWindowTitle(tr("Error Results"));
@ -79,11 +79,11 @@ void BackgroundResultWidget::startTask()
{
progressBar->setValue(0);
if(mgr->startWorker())
if (mgr->startWorker())
{
if(timerId)
if (timerId)
{
killTimer(timerId); //Stop progressBar from hiding in 1 second
killTimer(timerId); // Stop progressBar from hiding in 1 second
timerId = 0;
}
}
@ -105,14 +105,14 @@ void BackgroundResultWidget::taskFinished()
{
progressBar->setValue(progressBar->maximum());
if(timerId)
if (timerId)
killTimer(timerId);
timerId = startTimer(1000); //Hide progressBar after 1 second
timerId = startTimer(1000); // Hide progressBar after 1 second
}
void BackgroundResultWidget::timerEvent(QTimerEvent *e)
{
if(e->timerId() == timerId)
if (e->timerId() == timerId)
{
killTimer(timerId);
timerId = 0;
@ -120,10 +120,10 @@ void BackgroundResultWidget::timerEvent(QTimerEvent *e)
}
}
void BackgroundResultWidget::showContextMenu(const QPoint& pos)
void BackgroundResultWidget::showContextMenu(const QPoint &pos)
{
QModelIndex idx = view->indexAt(pos);
if(!idx.isValid())
if (!idx.isValid())
return;
mgr->showContextMenu(this, view->viewport()->mapToGlobal(pos), idx);

View File

@ -22,7 +22,7 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include <QWidget>
# include <QWidget>
class QTreeView;
class QProgressBar;

View File

@ -19,25 +19,23 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include "ibackgroundchecker.h"
# include "ibackgroundchecker.h"
#include <QThreadPool>
#include "utils/thread/iquittabletask.h"
#include "utils/thread/taskprogressevent.h"
#include "sqlite3pp/sqlite3pp.h"
# include <QThreadPool>
# include "utils/thread/iquittabletask.h"
# include "utils/thread/taskprogressevent.h"
# include "sqlite3pp/sqlite3pp.h"
IBackgroundChecker::IBackgroundChecker(sqlite3pp::database &db, QObject *parent) :
QObject(parent),
mDb(db)
{
}
bool IBackgroundChecker::event(QEvent *e)
{
if(e->type() == TaskProgressEvent::_Type)
if (e->type() == TaskProgressEvent::_Type)
{
e->setAccepted(true);
@ -46,14 +44,14 @@ bool IBackgroundChecker::event(QEvent *e)
return true;
}
else if(e->type() == eventType)
else if (e->type() == eventType)
{
e->setAccepted(true);
GenericTaskEvent *ev = static_cast<GenericTaskEvent *>(e);
if(m_mainWorker && ev->task == m_mainWorker)
if (m_mainWorker && ev->task == m_mainWorker)
{
if(!m_mainWorker->wasStopped())
if (!m_mainWorker->wasStopped())
{
setErrors(ev, false);
}
@ -66,10 +64,10 @@ bool IBackgroundChecker::event(QEvent *e)
else
{
int idx = m_workers.indexOf(ev->task);
if(idx != -1)
if (idx != -1)
{
m_workers.removeAt(idx);
if(!ev->task->wasStopped())
if (!ev->task->wasStopped())
setErrors(ev, true);
delete ev->task;
@ -84,19 +82,19 @@ bool IBackgroundChecker::event(QEvent *e)
bool IBackgroundChecker::startWorker()
{
if(m_mainWorker)
if (m_mainWorker)
return false;
if(!mDb.db())
if (!mDb.db())
return false;
m_mainWorker = createMainWorker();
QThreadPool::globalInstance()->start(m_mainWorker);
for(auto task = m_workers.begin(); task != m_workers.end(); task++)
for (auto task = m_workers.begin(); task != m_workers.end(); task++)
{
if(QThreadPool::globalInstance()->tryTake(*task))
if (QThreadPool::globalInstance()->tryTake(*task))
{
IQuittableTask *ptr = *task;
m_workers.erase(task);
@ -113,12 +111,12 @@ bool IBackgroundChecker::startWorker()
void IBackgroundChecker::abortTasks()
{
if(m_mainWorker)
if (m_mainWorker)
{
m_mainWorker->stop();
}
for(IQuittableTask *task : qAsConst(m_workers))
for (IQuittableTask *task : qAsConst(m_workers))
{
task->stop();
}
@ -126,7 +124,7 @@ void IBackgroundChecker::abortTasks()
void IBackgroundChecker::sessionLoadedHandler()
{
//no-op
// no-op
}
void IBackgroundChecker::addSubTask(IQuittableTask *task)

View File

@ -22,8 +22,8 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include <QObject>
#include <QVector>
# include <QObject>
# include <QVector>
class QAbstractItemModel;
class QModelIndex;
@ -47,13 +47,20 @@ public:
bool startWorker();
void abortTasks();
inline bool isRunning() const { return m_mainWorker || m_workers.size() > 0; }
inline bool isRunning() const
{
return m_mainWorker || m_workers.size() > 0;
}
inline QAbstractItemModel *getModel() const { return errorsModel; };
inline QAbstractItemModel *getModel() const
{
return errorsModel;
};
virtual QString getName() const = 0;
virtual void clearModel() = 0;
virtual void showContextMenu(QWidget *panel, const QPoint& globalPos, const QModelIndex& idx) const = 0;
virtual QString getName() const = 0;
virtual void clearModel() = 0;
virtual void showContextMenu(QWidget *panel, const QPoint &globalPos,
const QModelIndex &idx) const = 0;
virtual void sessionLoadedHandler();
@ -64,13 +71,13 @@ signals:
protected:
void addSubTask(IQuittableTask *task);
virtual IQuittableTask *createMainWorker() = 0;
virtual IQuittableTask *createMainWorker() = 0;
virtual void setErrors(QEvent *e, bool merge) = 0;
protected:
sqlite3pp::database &mDb;
QAbstractItemModel *errorsModel = nullptr;
int eventType = 0;
int eventType = 0;
private:
IQuittableTask *m_mainWorker = nullptr;

View File

@ -23,8 +23,7 @@
#include <QDebug>
namespace ImageMetaData
{
namespace ImageMetaData {
constexpr char sql_get_key_id[] = "SELECT rowid FROM metadata WHERE name=? AND val NOT NULL";
@ -35,7 +34,6 @@ ImageBlobDevice::ImageBlobDevice(sqlite3 *db, QObject *parent) :
mDb(db),
mBlob(nullptr)
{
}
ImageBlobDevice::~ImageBlobDevice()
@ -45,40 +43,40 @@ ImageBlobDevice::~ImageBlobDevice()
void ImageBlobDevice::setBlobInfo(const QByteArray &table, const QByteArray &column, qint64 rowId)
{
mRowId = rowId;
mTable = table;
mRowId = rowId;
mTable = table;
mColumn = column;
}
bool ImageBlobDevice::reserveSizeAndReset(qint64 len)
{
//NOTE: this will discard any previous content
// NOTE: this will discard any previous content
//Close previous BLOB handle
if(mBlob)
// Close previous BLOB handle
if (mBlob)
close();
//Create SQL statement
QByteArray sql = "UPDATE " + mTable + " SET " + mColumn + "=? WHERE rowId=?";
// Create SQL statement
QByteArray sql = "UPDATE " + mTable + " SET " + mColumn + "=? WHERE rowId=?";
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(mDb, sql.constData(), sql.size(), &stmt, nullptr);
if(rc != SQLITE_OK)
int rc = sqlite3_prepare_v2(mDb, sql.constData(), sql.size(), &stmt, nullptr);
if (rc != SQLITE_OK)
{
qWarning() << "ImageBlobDevice::reserveSizeAndReset cannot prepare:" << sqlite3_errmsg(mDb);
setErrorString(tr("Cannot query database"));
return false;
}
//Reserve BLOB memory
// Reserve BLOB memory
rc = sqlite3_bind_zeroblob64(stmt, 1, len);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return false;
}
rc = sqlite3_bind_int64(stmt, 2, mRowId);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return false;
@ -88,30 +86,30 @@ bool ImageBlobDevice::reserveSizeAndReset(qint64 len)
sqlite3_finalize(stmt);
if(rc != SQLITE_OK && rc != SQLITE_DONE)
if (rc != SQLITE_OK && rc != SQLITE_DONE)
{
qWarning() << "ImageBlobDevice::reserveSizeAndReset cannot step:" << sqlite3_errmsg(mDb);
setErrorString(tr("Cannot create BLOB"));
return false;
}
//Open new BLOB handle
// Open new BLOB handle
return open(QIODevice::ReadWrite);
}
bool ImageBlobDevice::open(QIODevice::OpenMode mode)
{
if(isOpen())
if (isOpen())
{
qWarning().nospace() << "ImageBlobDevice::open Device already open "
<< '(' << mTable << '.' << mColumn << ')';
qWarning().nospace() << "ImageBlobDevice::open Device already open " << '(' << mTable << '.'
<< mColumn << ')';
return false;
}
mode |= QIODevice::ReadOnly; //Always enable reading
int rc = sqlite3_blob_open(mDb, "main", mTable.constData(), mColumn.constData(),
mRowId, (mode & QIODevice::WriteOnly) != 0, &mBlob);
if(rc != SQLITE_OK || !mBlob)
mode |= QIODevice::ReadOnly; // Always enable reading
int rc = sqlite3_blob_open(mDb, "main", mTable.constData(), mColumn.constData(), mRowId,
(mode & QIODevice::WriteOnly) != 0, &mBlob);
if (rc != SQLITE_OK || !mBlob)
{
mBlob = nullptr;
setErrorString(sqlite3_errmsg(mDb));
@ -127,7 +125,7 @@ bool ImageBlobDevice::open(QIODevice::OpenMode mode)
void ImageBlobDevice::close()
{
if(mBlob)
if (mBlob)
{
sqlite3_blob_close(mBlob);
mBlob = nullptr;
@ -144,21 +142,21 @@ qint64 ImageBlobDevice::size() const
qint64 ImageBlobDevice::writeData(const char *data, qint64 len)
{
if(!mBlob)
if (!mBlob)
return -1;
int offset = int(pos());
if(len + offset >= mSize)
if (len + offset >= mSize)
len = mSize - offset;
if(!len)
if (!len)
return -1;
int rc = sqlite3_blob_write(mBlob, data, int(len), offset);
if(rc == SQLITE_OK)
if (rc == SQLITE_OK)
return len;
if(rc == SQLITE_READONLY)
if (rc == SQLITE_READONLY)
return -1;
setErrorString(sqlite3_errmsg(mDb));
@ -167,45 +165,46 @@ qint64 ImageBlobDevice::writeData(const char *data, qint64 len)
qint64 ImageBlobDevice::readData(char *data, qint64 maxlen)
{
if(!mBlob)
if (!mBlob)
return -1;
int offset = int(pos());
if(maxlen + offset >= mSize)
if (maxlen + offset >= mSize)
maxlen = mSize - offset;
if(!maxlen)
if (!maxlen)
return -1;
int rc = sqlite3_blob_read(mBlob, data, int(maxlen), offset);
if(rc == SQLITE_OK)
if (rc == SQLITE_OK)
return maxlen;
setErrorString(sqlite3_errmsg(mDb));
return -1;
}
ImageBlobDevice* getImage(sqlite3pp::database& db, const MetaDataManager::Key &key)
ImageBlobDevice *getImage(sqlite3pp::database &db, const MetaDataManager::Key &key)
{
if(!db.db())
if (!db.db())
return nullptr;
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(db.db(), sql_get_key_id, sizeof (sql_get_key_id) - 1, &stmt, nullptr);
if(rc != SQLITE_OK)
int rc =
sqlite3_prepare_v2(db.db(), sql_get_key_id, sizeof(sql_get_key_id) - 1, &stmt, nullptr);
if (rc != SQLITE_OK)
return nullptr;
rc = sqlite3_bind_text(stmt, 1, key.str, key.len, SQLITE_STATIC);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return nullptr;
}
rc = sqlite3_step(stmt);
rc = sqlite3_step(stmt);
qint64 rowId = 0;
if(rc != SQLITE_ROW)
if (rc != SQLITE_ROW)
{
sqlite3_finalize(stmt);
return nullptr;
@ -214,7 +213,7 @@ ImageBlobDevice* getImage(sqlite3pp::database& db, const MetaDataManager::Key &k
rowId = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
if(!rowId)
if (!rowId)
return nullptr;
ImageBlobDevice *dev = new ImageBlobDevice(db.db());
@ -222,11 +221,11 @@ ImageBlobDevice* getImage(sqlite3pp::database& db, const MetaDataManager::Key &k
return dev;
}
void setImage(sqlite3pp::database& db, const MetaDataManager::Key &key, const void *data, int size)
void setImage(sqlite3pp::database &db, const MetaDataManager::Key &key, const void *data, int size)
{
sqlite3pp::command cmd(db, "REPLACE INTO metadata(name, val) VALUES(?, ?)");
sqlite3_bind_text(cmd.stmt(), 1, key.str, key.len, SQLITE_STATIC);
if(data)
if (data)
sqlite3_bind_blob(cmd.stmt(), 2, data, size, SQLITE_STATIC);
else
sqlite3_bind_null(cmd.stmt(), 2);

View File

@ -27,10 +27,9 @@
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_blob sqlite3_blob;
namespace ImageMetaData
{
namespace ImageMetaData {
//TODO: move to utils
// TODO: move to utils
class ImageBlobDevice : public QIODevice
{
Q_OBJECT
@ -38,7 +37,7 @@ public:
ImageBlobDevice(sqlite3 *db, QObject *parent = nullptr);
~ImageBlobDevice() override;
void setBlobInfo(const QByteArray& table, const QByteArray& column, qint64 rowId);
void setBlobInfo(const QByteArray &table, const QByteArray &column, qint64 rowId);
bool reserveSizeAndReset(qint64 len);
@ -61,8 +60,8 @@ private:
QByteArray mColumn;
};
ImageBlobDevice *getImage(sqlite3pp::database& db, const MetaDataManager::Key& key);
void setImage(sqlite3pp::database& db, const MetaDataManager::Key &key, const void *data, int size);
ImageBlobDevice *getImage(sqlite3pp::database &db, const MetaDataManager::Key &key);
void setImage(sqlite3pp::database &db, const MetaDataManager::Key &key, const void *data, int size);
} // namespace ImageMetaData

View File

@ -47,28 +47,34 @@ MeetingInformationDialog::MeetingInformationDialog(QWidget *parent) :
ui->setupUi(this);
connect(ui->viewPictureBut, &QPushButton::clicked, this, &MeetingInformationDialog::showImage);
connect(ui->importPictureBut, &QPushButton::clicked, this, &MeetingInformationDialog::importImage);
connect(ui->removePictureBut, &QPushButton::clicked, this, &MeetingInformationDialog::removeImage);
connect(ui->resetHeaderBut, &QPushButton::clicked, this, &MeetingInformationDialog::toggleHeader);
connect(ui->resetFooterBut, &QPushButton::clicked, this, &MeetingInformationDialog::toggleFooter);
connect(ui->startDate, &QDateEdit::dateChanged, this, &MeetingInformationDialog::updateMinumumDate);
connect(ui->importPictureBut, &QPushButton::clicked, this,
&MeetingInformationDialog::importImage);
connect(ui->removePictureBut, &QPushButton::clicked, this,
&MeetingInformationDialog::removeImage);
connect(ui->resetHeaderBut, &QPushButton::clicked, this,
&MeetingInformationDialog::toggleHeader);
connect(ui->resetFooterBut, &QPushButton::clicked, this,
&MeetingInformationDialog::toggleFooter);
connect(ui->startDate, &QDateEdit::dateChanged, this,
&MeetingInformationDialog::updateMinumumDate);
QSizePolicy sp = ui->headerEdit->sizePolicy();
sp.setRetainSizeWhenHidden(true);
ui->headerEdit->setSizePolicy(sp);
ui->footerEdit->setSizePolicy(sp);
//Use similar font to the actual font used in sheet export
// Use similar font to the actual font used in sheet export
QFont font;
font.setBold(true);
font.setPointSize(18);
ui->descrEdit->document()->setDefaultFont(font);
if(!loadData())
if (!loadData())
{
QMessageBox::warning(this, tr("Database Error"),
tr("This database doesn't support metadata.\n"
"Make sure it was created by a recent version of the application and was not manipulated."));
"Make sure it was created by a recent version of the application "
"and was not manipulated."));
setDisabled(true);
}
}
@ -82,7 +88,7 @@ bool MeetingInformationDialog::loadData()
{
MetaDataManager *meta = Session->getMetaDataManager();
qint64 tmp = 0;
qint64 tmp = 0;
QDate date;
switch (meta->getInt64(tmp, MetaDataKey::MeetingStartDate))
@ -93,7 +99,7 @@ bool MeetingInformationDialog::loadData()
break;
}
case MetaDataKey::Result::NoMetaDataTable:
return false; //Database has no well-formed metadata
return false; // Database has no well-formed metadata
default:
date = QDate::currentDate();
}
@ -126,7 +132,7 @@ bool MeetingInformationDialog::loadData()
text.clear();
meta->getString(text, MetaDataKey::MeetingDescription);
ui->descrEdit->setPlainText(text);
//Align all text to center
// Align all text to center
QTextCursor c = ui->descrEdit->textCursor();
c.select(QTextCursor::Document);
QTextBlockFormat fmt;
@ -145,11 +151,12 @@ bool MeetingInformationDialog::loadData()
return true;
}
void MeetingInformationDialog::setSheetText(QLineEdit *lineEdit, QPushButton *but, const QString& text, bool isNull)
void MeetingInformationDialog::setSheetText(QLineEdit *lineEdit, QPushButton *but,
const QString &text, bool isNull)
{
lineEdit->setVisible(!isNull);
if(isNull)
if (isNull)
{
but->setText(tr("Set custom text"));
lineEdit->setText(QString());
@ -170,15 +177,18 @@ void MeetingInformationDialog::saveData()
meta->setInt64(ui->showDatesBox->isChecked() ? 1 : 0, false, MetaDataKey::MeetingShowDates);
meta->setString(ui->locationEdit->text().simplified(), false, MetaDataKey::MeetingLocation);
meta->setString(ui->associationEdit->text().simplified(), false, MetaDataKey::MeetingHostAssociation);
meta->setString(ui->associationEdit->text().simplified(), false,
MetaDataKey::MeetingHostAssociation);
meta->setString(ui->descrEdit->toPlainText(), false, MetaDataKey::MeetingDescription);
meta->setString(ui->headerEdit->text().simplified(), headerIsNull, MetaDataKey::SheetHeaderText);
meta->setString(ui->footerEdit->text().simplified(), footerIsNull, MetaDataKey::SheetFooterText);
meta->setString(ui->headerEdit->text().simplified(), headerIsNull,
MetaDataKey::SheetHeaderText);
meta->setString(ui->footerEdit->text().simplified(), footerIsNull,
MetaDataKey::SheetFooterText);
if(needsToSaveImg)
if (needsToSaveImg)
{
if(img.isNull())
if (img.isNull())
{
ImageMetaData::setImage(Session->m_Db, MetaDataKey::MeetingLogoPicture, nullptr, 0);
}
@ -189,10 +199,13 @@ void MeetingInformationDialog::saveData()
buf.open(QIODevice::WriteOnly);
QImageWriter writer(&buf, "PNG");
if(writer.canWrite() && writer.write(img))
if (writer.canWrite() && writer.write(img))
{
ImageMetaData::setImage(Session->m_Db, MetaDataKey::MeetingLogoPicture, arr.data(),
arr.size());
}
else
{
ImageMetaData::setImage(Session->m_Db, MetaDataKey::MeetingLogoPicture, arr.data(), arr.size());
}else{
qDebug() << "MeetingInformationDialog: error saving image," << writer.errorString();
}
}
@ -203,25 +216,28 @@ void MeetingInformationDialog::showImage()
{
OwningQPointer<ImageViewer> dlg = new ImageViewer(this);
if(img.isNull() && !needsToSaveImg)
if (img.isNull() && !needsToSaveImg)
{
std::unique_ptr<ImageMetaData::ImageBlobDevice> imageIO;
imageIO.reset(ImageMetaData::getImage(Session->m_Db, MetaDataKey::MeetingLogoPicture));
if(imageIO && imageIO->open(QIODevice::ReadOnly))
if (imageIO && imageIO->open(QIODevice::ReadOnly))
{
QImageReader reader(imageIO.get());
if(reader.canRead())
if (reader.canRead())
{
img = reader.read(); //ERRORMSG: handle errors, show to user
img = reader.read(); // ERRORMSG: handle errors, show to user
}
if(img.isNull())
if (img.isNull())
{
qDebug() << "MeetingInformationDialog: error loading image," << reader.errorString();
qDebug() << "MeetingInformationDialog: error loading image,"
<< reader.errorString();
}
imageIO->close();
}else{
}
else
{
qDebug() << "MeetingInformationDialog: error query image," << Session->m_Db.error_msg();
}
}
@ -230,15 +246,15 @@ void MeetingInformationDialog::showImage()
dlg->exec();
if(!needsToSaveImg)
img = QImage(); //Cleanup to free memory
if (!needsToSaveImg)
img = QImage(); // Cleanup to free memory
}
void MeetingInformationDialog::importImage()
{
const QLatin1String meeting_image_key = QLatin1String("meeting_image_key");
OwningQPointer<QFileDialog> dlg = new QFileDialog(this, tr("Import image"));
OwningQPointer<QFileDialog> dlg = new QFileDialog(this, tr("Import image"));
dlg->setFileMode(QFileDialog::ExistingFile);
dlg->setAcceptMode(QFileDialog::AcceptOpen);
dlg->setDirectory(RecentDirStore::getDir(meeting_image_key, RecentDirStore::Images));
@ -246,28 +262,28 @@ void MeetingInformationDialog::importImage()
QList<QByteArray> mimes = QImageReader::supportedMimeTypes();
QStringList filters;
filters.reserve(mimes.size() + 1);
for(const QByteArray &ba : mimes)
for (const QByteArray &ba : mimes)
filters.append(QString::fromUtf8(ba));
filters << "application/octet-stream"; // will show "All files (*)"
dlg->setMimeTypeFilters(filters);
if(dlg->exec() != QDialog::Accepted || !dlg)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
QString fileName = dlg->selectedUrls().value(0).toLocalFile();
if(fileName.isEmpty())
if (fileName.isEmpty())
return;
RecentDirStore::setPath(meeting_image_key, fileName);
QImageReader reader(fileName);
reader.setQuality(100);
if(reader.canRead())
if (reader.canRead())
{
QImage image = reader.read();
if(image.isNull())
if (image.isNull())
{
QMessageBox::warning(this, tr("Importing error"),
tr("The image format is not supported or the file is corrupted."));
@ -275,7 +291,7 @@ void MeetingInformationDialog::importImage()
return;
}
img = image;
img = image;
needsToSaveImg = true;
}
}
@ -284,10 +300,10 @@ void MeetingInformationDialog::removeImage()
{
int ret = QMessageBox::question(this, tr("Remove image?"),
tr("Are you sure to remove the image logo?"));
if(ret != QMessageBox::Yes)
if (ret != QMessageBox::Yes)
return;
img = QImage(); //Cleanup to free memory
img = QImage(); // Cleanup to free memory
needsToSaveImg = true;
}

View File

@ -21,29 +21,29 @@
#include <sqlite3pp/sqlite3pp.h>
constexpr char sql_get_metadata[] = "SELECT val FROM metadata WHERE name=?";
constexpr char sql_get_metadata[] = "SELECT val FROM metadata WHERE name=?";
constexpr char sql_has_metadata_key[] = "SELECT 1 FROM metadata WHERE name=? AND val NOT NULL";
constexpr char sql_set_metadata[] = "REPLACE INTO metadata(name, val) VALUES(?, ?)";
constexpr char sql_set_metadata[] = "REPLACE INTO metadata(name, val) VALUES(?, ?)";
MetaDataManager::MetaDataManager(sqlite3pp::database &db) :
mDb(db)
{
}
MetaDataKey::Result MetaDataManager::hasKey(const MetaDataManager::Key &key)
{
MetaDataKey::Result result = MetaDataKey::Result::NoMetaDataTable;
if(!mDb.db())
if (!mDb.db())
return result;
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(mDb.db(), sql_has_metadata_key, sizeof (sql_has_metadata_key) - 1, &stmt, nullptr);
if(rc != SQLITE_OK)
int rc = sqlite3_prepare_v2(mDb.db(), sql_has_metadata_key, sizeof(sql_has_metadata_key) - 1,
&stmt, nullptr);
if (rc != SQLITE_OK)
return result;
rc = sqlite3_bind_text(stmt, 1, key.str, key.len, SQLITE_STATIC);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
@ -51,11 +51,11 @@ MetaDataKey::Result MetaDataManager::hasKey(const MetaDataManager::Key &key)
rc = sqlite3_step(stmt);
if(rc == SQLITE_ROW)
if (rc == SQLITE_ROW)
{
result = MetaDataKey::Result::ValueFound;
}
else if(rc == SQLITE_OK || rc == SQLITE_DONE)
else if (rc == SQLITE_OK || rc == SQLITE_DONE)
{
result = MetaDataKey::Result::ValueNotFound;
}
@ -68,19 +68,20 @@ MetaDataKey::Result MetaDataManager::hasKey(const MetaDataManager::Key &key)
return result;
}
MetaDataKey::Result MetaDataManager::getInt64(qint64 &out, const Key& key)
MetaDataKey::Result MetaDataManager::getInt64(qint64 &out, const Key &key)
{
MetaDataKey::Result result = MetaDataKey::Result::NoMetaDataTable;
if(!mDb.db())
if (!mDb.db())
return result;
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(mDb.db(), sql_get_metadata, sizeof (sql_get_metadata) - 1, &stmt, nullptr);
if(rc != SQLITE_OK)
int rc =
sqlite3_prepare_v2(mDb.db(), sql_get_metadata, sizeof(sql_get_metadata) - 1, &stmt, nullptr);
if (rc != SQLITE_OK)
return result;
rc = sqlite3_bind_text(stmt, 1, key.str, key.len, SQLITE_STATIC);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
@ -88,19 +89,19 @@ MetaDataKey::Result MetaDataManager::getInt64(qint64 &out, const Key& key)
rc = sqlite3_step(stmt);
if(rc == SQLITE_ROW)
if (rc == SQLITE_ROW)
{
if(sqlite3_column_type(stmt, 0) == SQLITE_NULL)
if (sqlite3_column_type(stmt, 0) == SQLITE_NULL)
{
result = MetaDataKey::Result::ValueIsNull;
}
else
{
result = MetaDataKey::Result::ValueFound;
out = sqlite3_column_int64(stmt, 0);
out = sqlite3_column_int64(stmt, 0);
}
}
else if(rc == SQLITE_OK || rc == SQLITE_DONE)
else if (rc == SQLITE_OK || rc == SQLITE_DONE)
{
result = MetaDataKey::Result::ValueNotFound;
}
@ -113,29 +114,30 @@ MetaDataKey::Result MetaDataManager::getInt64(qint64 &out, const Key& key)
return result;
}
MetaDataKey::Result MetaDataManager::setInt64(qint64 in, bool setToNull, const Key& key)
MetaDataKey::Result MetaDataManager::setInt64(qint64 in, bool setToNull, const Key &key)
{
MetaDataKey::Result result = MetaDataKey::Result::NoMetaDataTable;
if(!mDb.db())
if (!mDb.db())
return result;
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(mDb.db(), sql_set_metadata, sizeof (sql_set_metadata) - 1, &stmt, nullptr);
if(rc != SQLITE_OK)
int rc =
sqlite3_prepare_v2(mDb.db(), sql_set_metadata, sizeof(sql_set_metadata) - 1, &stmt, nullptr);
if (rc != SQLITE_OK)
return result;
rc = sqlite3_bind_text(stmt, 1, key.str, key.len, SQLITE_STATIC);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
}
if(setToNull)
if (setToNull)
rc = sqlite3_bind_null(stmt, 2);
else
rc = sqlite3_bind_int64(stmt, 2, in);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
@ -143,7 +145,7 @@ MetaDataKey::Result MetaDataManager::setInt64(qint64 in, bool setToNull, const K
rc = sqlite3_step(stmt);
if(rc == SQLITE_OK || rc == SQLITE_DONE)
if (rc == SQLITE_OK || rc == SQLITE_DONE)
{
result = MetaDataKey::Result::ValueFound;
}
@ -156,19 +158,20 @@ MetaDataKey::Result MetaDataManager::setInt64(qint64 in, bool setToNull, const K
return result;
}
MetaDataKey::Result MetaDataManager::getString(QString &out, const Key& key)
MetaDataKey::Result MetaDataManager::getString(QString &out, const Key &key)
{
MetaDataKey::Result result = MetaDataKey::Result::NoMetaDataTable;
if(!mDb.db())
if (!mDb.db())
return result;
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(mDb.db(), sql_get_metadata, sizeof (sql_get_metadata) - 1, &stmt, nullptr);
if(rc != SQLITE_OK)
int rc =
sqlite3_prepare_v2(mDb.db(), sql_get_metadata, sizeof(sql_get_metadata) - 1, &stmt, nullptr);
if (rc != SQLITE_OK)
return result;
rc = sqlite3_bind_text(stmt, 1, key.str, key.len, SQLITE_STATIC);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
@ -176,21 +179,21 @@ MetaDataKey::Result MetaDataManager::getString(QString &out, const Key& key)
rc = sqlite3_step(stmt);
if(rc == SQLITE_ROW)
if (rc == SQLITE_ROW)
{
if(sqlite3_column_type(stmt, 0) == SQLITE_NULL)
if (sqlite3_column_type(stmt, 0) == SQLITE_NULL)
{
result = MetaDataKey::Result::ValueIsNull;
}
else
{
result = MetaDataKey::Result::ValueFound;
const int len = sqlite3_column_bytes(stmt, 0);
const char *text = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 0));
out = QString::fromUtf8(text, len);
result = MetaDataKey::Result::ValueFound;
const int len = sqlite3_column_bytes(stmt, 0);
const char *text = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 0));
out = QString::fromUtf8(text, len);
}
}
else if(rc == SQLITE_OK || rc == SQLITE_DONE)
else if (rc == SQLITE_OK || rc == SQLITE_DONE)
{
result = MetaDataKey::Result::ValueNotFound;
}
@ -203,19 +206,20 @@ MetaDataKey::Result MetaDataManager::getString(QString &out, const Key& key)
return result;
}
MetaDataKey::Result MetaDataManager::setString(const QString& in, bool setToNull, const Key& key)
MetaDataKey::Result MetaDataManager::setString(const QString &in, bool setToNull, const Key &key)
{
MetaDataKey::Result result = MetaDataKey::Result::NoMetaDataTable;
if(!mDb.db())
if (!mDb.db())
return result;
sqlite3_stmt *stmt = nullptr;
int rc = sqlite3_prepare_v2(mDb.db(), sql_set_metadata, sizeof (sql_set_metadata) - 1, &stmt, nullptr);
if(rc != SQLITE_OK)
int rc =
sqlite3_prepare_v2(mDb.db(), sql_set_metadata, sizeof(sql_set_metadata) - 1, &stmt, nullptr);
if (rc != SQLITE_OK)
return result;
rc = sqlite3_bind_text(stmt, 1, key.str, key.len, SQLITE_STATIC);
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
@ -223,13 +227,13 @@ MetaDataKey::Result MetaDataManager::setString(const QString& in, bool setToNull
QByteArray arr = in.toUtf8();
if(setToNull)
if (setToNull)
rc = sqlite3_bind_null(stmt, 2);
else
{
rc = sqlite3_bind_text(stmt, 2, arr.data(), arr.size(), SQLITE_STATIC);
}
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return result;
@ -237,7 +241,7 @@ MetaDataKey::Result MetaDataManager::setString(const QString& in, bool setToNull
rc = sqlite3_step(stmt);
if(rc == SQLITE_OK || rc == SQLITE_DONE)
if (rc == SQLITE_OK || rc == SQLITE_DONE)
{
result = MetaDataKey::Result::ValueFound;
}

View File

@ -27,42 +27,46 @@ namespace sqlite3pp {
class database;
}
namespace MetaDataKey
{
namespace MetaDataKey {
enum Result
{
ValueFound = 0,
ValueIsNull,
ValueNotFound,
NoMetaDataTable, //Format is too old, 'metadata' table is not present
NoMetaDataTable, // Format is too old, 'metadata' table is not present
NResults
};
//BEGING Key constants TODO: maybe make static or extern to avoid duplication
// BEGING Key constants TODO: maybe make static or extern to avoid duplication
//Database
constexpr char FormatVersionKey[] = "format_version"; //INTEGER: version, NOTE: FormatVersion is aleady used by info.h constants
constexpr char ApplicationString[] = "application_str"; //STRING: application version string 'maj.min.patch'
// Database
constexpr char FormatVersionKey[] =
"format_version"; // INTEGER: version, NOTE: FormatVersion is aleady used by info.h constants
constexpr char ApplicationString[] =
"application_str"; // STRING: application version string 'maj.min.patch'
//Meeting
constexpr char MeetingShowDates[] = "meeting_show_dates"; //INTEGER: 1 shows dates, 0 hides them
constexpr char MeetingStartDate[] = "meeting_start_date"; //INTEGER: Start date in Julian Day integer
constexpr char MeetingEndDate[] = "meeting_end_date"; //INTEGER: End date in Juliand Day integer
constexpr char MeetingLocation[] = "meeting_location"; //STRING: city name
constexpr char MeetingDescription[] = "meeting_descr"; //STRING: brief description of the meeting
constexpr char MeetingHostAssociation[] = "meeting_host"; //STRING: name of association that is hosting the meeting
constexpr char MeetingLogoPicture[] = "meeting_logo"; //BLOB: PNG alpha image, usually hosting association logo
// Meeting
constexpr char MeetingShowDates[] = "meeting_show_dates"; // INTEGER: 1 shows dates, 0 hides them
constexpr char MeetingStartDate[] =
"meeting_start_date"; // INTEGER: Start date in Julian Day integer
constexpr char MeetingEndDate[] = "meeting_end_date"; // INTEGER: End date in Juliand Day integer
constexpr char MeetingLocation[] = "meeting_location"; // STRING: city name
constexpr char MeetingDescription[] = "meeting_descr"; // STRING: brief description of the meeting
constexpr char MeetingHostAssociation[] =
"meeting_host"; // STRING: name of association that is hosting the meeting
constexpr char MeetingLogoPicture[] =
"meeting_logo"; // BLOB: PNG alpha image, usually hosting association logo
//ODT Export Sheet
constexpr char SheetHeaderText[] = "sheet_header"; //STRING: sheet header text
constexpr char SheetFooterText[] = "sheet_footer"; //STRING: sheet footer text
// ODT Export Sheet
constexpr char SheetHeaderText[] = "sheet_header"; // STRING: sheet header text
constexpr char SheetFooterText[] = "sheet_footer"; // STRING: sheet footer text
//Jobs
#define METADATA_MAKE_RS_KEY(category) ("job_default_stop_" ## #category)
// Jobs
#define METADATA_MAKE_RS_KEY(category) ("job_default_stop_"## #category)
//END Key constants
}
// END Key constants
} // namespace MetaDataKey
class MetaDataManager
{
@ -71,16 +75,20 @@ public:
struct Key
{
template<int N>
constexpr inline Key(const char (&val)[N]) :str(val), len(N - 1) {}
template <int N>
constexpr inline Key(const char (&val)[N]) :
str(val),
len(N - 1)
{
}
const char *str;
const int len;
};
MetaDataKey::Result hasKey(const Key& key);
MetaDataKey::Result hasKey(const Key &key);
MetaDataKey::Result getInt64(qint64 &out, const Key& key);
MetaDataKey::Result getInt64(qint64 &out, const Key &key);
MetaDataKey::Result setInt64(qint64 in, bool setToNull, const Key &key);
MetaDataKey::Result getString(QString &out, const Key &key);

View File

@ -1,9 +1,4 @@
add_subdirectory(model)
add_subdirectory(view)
add_subdirectory(model) add_subdirectory(view)
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
graph/linegraphtypes.h
graph/linegraphtypes.cpp
PARENT_SCOPE
)
set(MR_TIMETABLE_PLANNER_SOURCES ${MR_TIMETABLE_PLANNER_SOURCES} graph / linegraphtypes.h graph
/ linegraphtypes.cpp PARENT_SCOPE)

View File

@ -28,16 +28,14 @@ public:
static const char *texts[];
};
const char *LineGraphTypeNames::texts[] = {
QT_TRANSLATE_NOOP("LineGraphTypeNames", "No Graph"),
QT_TRANSLATE_NOOP("LineGraphTypeNames", "Station"),
QT_TRANSLATE_NOOP("LineGraphTypeNames", "Segment"),
QT_TRANSLATE_NOOP("LineGraphTypeNames", "Line")
};
const char *LineGraphTypeNames::texts[] = {QT_TRANSLATE_NOOP("LineGraphTypeNames", "No Graph"),
QT_TRANSLATE_NOOP("LineGraphTypeNames", "Station"),
QT_TRANSLATE_NOOP("LineGraphTypeNames", "Segment"),
QT_TRANSLATE_NOOP("LineGraphTypeNames", "Line")};
QString utils::getLineGraphTypeName(LineGraphType type)
{
if(type >= LineGraphType::NTypes)
if (type >= LineGraphType::NTypes)
return QString();
return LineGraphTypeNames::tr(LineGraphTypeNames::texts[int(type)]);
}

View File

@ -27,10 +27,10 @@
*/
enum class LineGraphType
{
NoGraph = 0, //!< No content displayed
SingleStation, //!< Show a single station
NoGraph = 0, //!< No content displayed
SingleStation, //!< Show a single station
RailwaySegment, //!< Show two adjacent stations and the segment in between
RailwayLine, //!< Show a complete railway line (multiple adjacent segments)
RailwayLine, //!< Show a complete railway line (multiple adjacent segments)
NTypes
};

View File

@ -41,34 +41,41 @@ LineGraphManager::LineGraphManager(QObject *parent) :
m_hasScheduledUpdate(false)
{
auto session = Session;
//Stations
connect(session, &MeetingSession::stationNameChanged, this, &LineGraphManager::onStationNameChanged);
connect(session, &MeetingSession::stationJobsPlanChanged, this, &LineGraphManager::onStationJobPlanChanged);
connect(session, &MeetingSession::stationTrackPlanChanged, this, &LineGraphManager::onStationTrackPlanChanged);
// Stations
connect(session, &MeetingSession::stationNameChanged, this,
&LineGraphManager::onStationNameChanged);
connect(session, &MeetingSession::stationJobsPlanChanged, this,
&LineGraphManager::onStationJobPlanChanged);
connect(session, &MeetingSession::stationTrackPlanChanged, this,
&LineGraphManager::onStationTrackPlanChanged);
connect(session, &MeetingSession::stationRemoved, this, &LineGraphManager::onStationRemoved);
//Segments
connect(session, &MeetingSession::segmentNameChanged, this, &LineGraphManager::onSegmentNameChanged);
connect(session, &MeetingSession::segmentStationsChanged, this, &LineGraphManager::onSegmentStationsChanged);
// Segments
connect(session, &MeetingSession::segmentNameChanged, this,
&LineGraphManager::onSegmentNameChanged);
connect(session, &MeetingSession::segmentStationsChanged, this,
&LineGraphManager::onSegmentStationsChanged);
connect(session, &MeetingSession::segmentRemoved, this, &LineGraphManager::onSegmentRemoved);
//Lines
// Lines
connect(session, &MeetingSession::lineNameChanged, this, &LineGraphManager::onLineNameChanged);
connect(session, &MeetingSession::lineSegmentsChanged, this, &LineGraphManager::onLineSegmentsChanged);
connect(session, &MeetingSession::lineSegmentsChanged, this,
&LineGraphManager::onLineSegmentsChanged);
connect(session, &MeetingSession::lineRemoved, this, &LineGraphManager::onLineRemoved);
//Jobs
// Jobs
connect(session, &MeetingSession::jobChanged, this, &LineGraphManager::onJobChanged);
connect(session, &MeetingSession::jobRemoved, this, &LineGraphManager::onJobRemoved);
//Settings
connect(&AppSettings, &MRTPSettings::jobGraphOptionsChanged, this, &LineGraphManager::updateGraphOptions);
// Settings
connect(&AppSettings, &MRTPSettings::jobGraphOptionsChanged, this,
&LineGraphManager::updateGraphOptions);
m_followJobOnGraphChange = AppSettings.getFollowSelectionOnGraphChange();
}
bool LineGraphManager::event(QEvent *ev)
{
if(ev->type() == QEvent::Type(CustomEvents::LineGraphManagerUpdate))
if (ev->type() == QEvent::Type(CustomEvents::LineGraphManagerUpdate))
{
ev->accept();
processPendingUpdates();
@ -88,13 +95,13 @@ void LineGraphManager::registerScene(LineGraphScene *scene)
connect(scene, &LineGraphScene::sceneActivated, this, &LineGraphManager::setActiveScene);
connect(scene, &LineGraphScene::jobSelected, this, &LineGraphManager::onJobSelected);
if(m_followJobOnGraphChange)
if (m_followJobOnGraphChange)
connect(scene, &LineGraphScene::graphChanged, this, &LineGraphManager::onGraphChanged);
if(scenes.count() == 1)
if (scenes.count() == 1)
{
//This is the first scene registered
//activate it so we have an active scene even if user does't activate one
// This is the first scene registered
// activate it so we have an active scene even if user does't activate one
setActiveScene(scene);
}
}
@ -109,17 +116,17 @@ void LineGraphManager::unregisterScene(LineGraphScene *scene)
disconnect(scene, &LineGraphScene::sceneActivated, this, &LineGraphManager::setActiveScene);
disconnect(scene, &LineGraphScene::jobSelected, this, &LineGraphManager::onJobSelected);
if(m_followJobOnGraphChange)
if (m_followJobOnGraphChange)
disconnect(scene, &LineGraphScene::graphChanged, this, &LineGraphManager::onGraphChanged);
//Reset active scene if it is unregistered
if(activeScene == scene)
// Reset active scene if it is unregistered
if (activeScene == scene)
setActiveScene(nullptr);
}
void LineGraphManager::clearAllGraphs()
{
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
scene->loadGraph(0, LineGraphType::NoGraph, true);
}
@ -127,9 +134,9 @@ void LineGraphManager::clearAllGraphs()
void LineGraphManager::clearGraphsOfObject(db_id objectId, LineGraphType type)
{
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->getGraphObjectId() == objectId && scene->getGraphType() == type)
if (scene->getGraphObjectId() == objectId && scene->getGraphType() == type)
scene->loadGraph(0, LineGraphType::NoGraph);
}
}
@ -137,61 +144,60 @@ void LineGraphManager::clearGraphsOfObject(db_id objectId, LineGraphType type)
JobStopEntry LineGraphManager::getCurrentSelectedJob() const
{
JobStopEntry selectedJob;
if(activeScene)
if (activeScene)
selectedJob = activeScene->getSelectedJob();
return selectedJob;
}
void LineGraphManager::scheduleUpdate()
{
if(m_hasScheduledUpdate)
return; //Already scheduled
if (m_hasScheduledUpdate)
return; // Already scheduled
//Mark as scheduled and post event to ourself
// Mark as scheduled and post event to ourself
m_hasScheduledUpdate = true;
QCoreApplication::postEvent(this,
new QEvent(QEvent::Type(CustomEvents::LineGraphManagerUpdate)),
Qt::HighEventPriority);
QCoreApplication::postEvent(
this, new QEvent(QEvent::Type(CustomEvents::LineGraphManagerUpdate)), Qt::HighEventPriority);
}
void LineGraphManager::processPendingUpdates()
{
constexpr int MAX_UPDATE_TIME_MS = 1000;
//Clear update flag before updating in case one operation triggers update
// Clear update flag before updating in case one operation triggers update
m_hasScheduledUpdate = false;
QElapsedTimer timer;
timer.start();
for(LineGraphScene *scene : qAsConst(scenes))
{
if(timer.elapsed() > MAX_UPDATE_TIME_MS)
for (LineGraphScene *scene : qAsConst(scenes))
{
if (timer.elapsed() > MAX_UPDATE_TIME_MS)
{
//It's taking to long, schedule a second update batch to finish
// It's taking to long, schedule a second update batch to finish
scheduleUpdate();
break;
}
if(scene->pendingUpdate.testFlag(PendingUpdate::NothingToDo))
continue; //Skip
if (scene->pendingUpdate.testFlag(PendingUpdate::NothingToDo))
continue; // Skip
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
{
scene->reload();
}
else
{
if(scene->pendingUpdate.testFlag(PendingUpdate::ReloadJobs))
if (scene->pendingUpdate.testFlag(PendingUpdate::ReloadJobs))
{
scene->reloadJobs();
}
if(scene->pendingUpdate.testFlag(PendingUpdate::ReloadStationNames))
if (scene->pendingUpdate.testFlag(PendingUpdate::ReloadStationNames))
{
scene->updateStationNames();
}
//Manually cleare pending update and trigger redraw
// Manually cleare pending update and trigger redraw
scene->pendingUpdate = PendingUpdate::NothingToDo;
emit scene->redrawGraph();
}
@ -202,28 +208,28 @@ void LineGraphManager::setActiveScene(IGraphScene *scene)
{
LineGraphScene *lineScene = qobject_cast<LineGraphScene *>(scene);
if(lineScene)
if (lineScene)
{
if(activeScene == lineScene)
if (activeScene == lineScene)
return;
//NOTE: Only registere scenes can become active
//Otherwise we cannot track if scene got destroyed and reset active scene.
if(!scenes.contains(lineScene))
// NOTE: Only registere scenes can become active
// Otherwise we cannot track if scene got destroyed and reset active scene.
if (!scenes.contains(lineScene))
return;
}
else if(!scenes.isEmpty())
else if (!scenes.isEmpty())
{
//Activate first registered scene because previous one was unregistered
// Activate first registered scene because previous one was unregistered
lineScene = scenes.first();
}
activeScene = lineScene;
emit activeSceneChanged(activeScene);
//Triegger selection update or clear it
// Triegger selection update or clear it
JobStopEntry selectedJob;
if(activeScene)
if (activeScene)
{
selectedJob = activeScene->getSelectedJob();
}
@ -239,58 +245,60 @@ void LineGraphManager::onSceneDestroyed(QObject *obj)
void LineGraphManager::onGraphChanged(int /*graphType_*/, db_id graphObjId, LineGraphScene *scene)
{
if(!m_followJobOnGraphChange || !scenes.contains(scene))
if (!m_followJobOnGraphChange || !scenes.contains(scene))
{
qWarning() << "LineGraphManager: should not receive graph change for scene" << scene;
return;
}
if(!graphObjId || scene->getGraphType() == LineGraphType::NoGraph)
return; //No graph selected
if (!graphObjId || scene->getGraphType() == LineGraphType::NoGraph)
return; // No graph selected
//Graph has changed, ensure selected job is still visible (if possible)
// Graph has changed, ensure selected job is still visible (if possible)
JobStopEntry selectedJob = scene->getSelectedJob();
if(!selectedJob.jobId)
return; //No job selected, nothing to do
if (!selectedJob.jobId)
return; // No job selected, nothing to do
LineGraphSelectionHelper helper(Session->m_Db);
LineGraphSelectionHelper::SegmentInfo info;
if(!helper.tryFindJobStopInGraph(scene, selectedJob.jobId, info))
return; //Cannot find job in current graph, give up
if (!helper.tryFindJobStopInGraph(scene, selectedJob.jobId, info))
return; // Cannot find job in current graph, give up
//Ensure job is visible
scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart, info.departure);
// Ensure job is visible
scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}
void LineGraphManager::onJobSelected(db_id jobId, int category, db_id stopId)
{
JobCategory cat = JobCategory(category);
if(lastSelectedJob.jobId == jobId && lastSelectedJob.category == cat && lastSelectedJob.stopId == stopId)
return; //Selection did not change
if (lastSelectedJob.jobId == jobId && lastSelectedJob.category == cat
&& lastSelectedJob.stopId == stopId)
return; // Selection did not change
lastSelectedJob.jobId = jobId;
lastSelectedJob.jobId = jobId;
lastSelectedJob.category = cat;
lastSelectedJob.stopId = stopId;
lastSelectedJob.stopId = stopId;
if(jobId)
if (jobId)
Session->getViewManager()->requestJobEditor(jobId);
else
Session->getViewManager()->requestClearJob();
if(AppSettings.getSyncSelectionOnAllGraphs())
if (AppSettings.getSyncSelectionOnAllGraphs())
{
//Sync selection among all registered scenes
for(LineGraphScene *scene : qAsConst(scenes))
// Sync selection among all registered scenes
for (LineGraphScene *scene : qAsConst(scenes))
{
scene->setSelectedJob(lastSelectedJob);
}
}
if(activeScene)
if (activeScene)
{
const JobStopEntry selectedJob = activeScene->getSelectedJob();
if(selectedJob.jobId == lastSelectedJob.jobId)
if (selectedJob.jobId == lastSelectedJob.jobId)
{
emit jobSelected(lastSelectedJob.jobId, int(lastSelectedJob.category),
lastSelectedJob.stopId);
@ -302,23 +310,23 @@ void LineGraphManager::onStationNameChanged(db_id stationId)
{
bool found = false;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; //Already flagged
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; // Already flagged
if(scene->stations.contains(stationId))
if (scene->stations.contains(stationId))
{
scene->pendingUpdate.setFlag(PendingUpdate::ReloadStationNames);
found = true;
}
}
if(found)
if (found)
scheduleUpdate();
}
void LineGraphManager::onStationJobPlanChanged(const QSet<db_id>& stationIds)
void LineGraphManager::onStationJobPlanChanged(const QSet<db_id> &stationIds)
{
onStationPlanChanged_internal(stationIds, int(PendingUpdate::ReloadJobs));
}
@ -330,9 +338,9 @@ void LineGraphManager::onStationTrackPlanChanged(const QSet<db_id> &stationIds)
void LineGraphManager::onStationRemoved(db_id stationId)
{
//A station can be removed only when not connected and no jobs pass through it.
//So there is no need to update other scenes because no line will contain
//The removed station
// A station can be removed only when not connected and no jobs pass through it.
// So there is no need to update other scenes because no line will contain
// The removed station
clearGraphsOfObject(stationId, LineGraphType::SingleStation);
}
@ -340,25 +348,25 @@ void LineGraphManager::onSegmentNameChanged(db_id segmentId)
{
QString segName;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; //Already flagged
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; // Already flagged
if(scene->graphType == LineGraphType::RailwaySegment && scene->graphObjectId == segmentId)
if (scene->graphType == LineGraphType::RailwaySegment && scene->graphObjectId == segmentId)
{
if(segName.isEmpty())
if (segName.isEmpty())
{
//Fetch new name and cache it
// Fetch new name and cache it
sqlite3pp::query q(scene->mDb, "SELECT name FROM railway_segments WHERE id=?");
q.bind(1, segmentId);
if(q.step() != SQLITE_ROW)
if (q.step() != SQLITE_ROW)
{
qWarning() << "Graph: invalid segment ID" << segmentId;
return;
}
//Store segment name
// Store segment name
segName = q.getRows().get<QString>(0);
}
@ -372,16 +380,16 @@ void LineGraphManager::onSegmentStationsChanged(db_id segmentId)
{
bool found = false;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; //Already flagged
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; // Already flagged
if(scene->graphType == LineGraphType::RailwayLine)
if (scene->graphType == LineGraphType::RailwayLine)
{
for(const auto& stPos : qAsConst(scene->stationPositions))
for (const auto &stPos : qAsConst(scene->stationPositions))
{
if(stPos.segmentId == segmentId)
if (stPos.segmentId == segmentId)
{
scene->pendingUpdate.setFlag(PendingUpdate::FullReload);
found = true;
@ -389,23 +397,24 @@ void LineGraphManager::onSegmentStationsChanged(db_id segmentId)
}
}
}
else if(scene->graphType == LineGraphType::RailwaySegment && scene->graphObjectId == segmentId)
else if (scene->graphType == LineGraphType::RailwaySegment
&& scene->graphObjectId == segmentId)
{
scene->pendingUpdate.setFlag(PendingUpdate::FullReload);
found = true;
}
}
if(found)
if (found)
scheduleUpdate();
}
void LineGraphManager::onSegmentRemoved(db_id segmentId)
{
//A segment can be removed only when is not on any line
//And when no jobs pass through it.
//So there is no need to update other line scenes because no line will contain
//The removed segment
// A segment can be removed only when is not on any line
// And when no jobs pass through it.
// So there is no need to update other line scenes because no line will contain
// The removed segment
clearGraphsOfObject(segmentId, LineGraphType::RailwaySegment);
}
@ -413,25 +422,25 @@ void LineGraphManager::onLineNameChanged(db_id lineId)
{
QString lineName;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; //Already flagged
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; // Already flagged
if(scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
if (scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
{
if(lineName.isEmpty())
if (lineName.isEmpty())
{
//Fetch new name and cache it
// Fetch new name and cache it
sqlite3pp::query q(scene->mDb, "SELECT name FROM lines WHERE id=?");
q.bind(1, lineId);
if(q.step() != SQLITE_ROW)
if (q.step() != SQLITE_ROW)
{
qWarning() << "Graph: invalid line ID" << lineId;
return;
}
//Store line name
// Store line name
lineName = q.getRows().get<QString>(0);
}
@ -445,60 +454,61 @@ void LineGraphManager::onLineSegmentsChanged(db_id lineId)
{
bool found = false;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; //Already flagged
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; // Already flagged
if(scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
if (scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
{
scene->pendingUpdate.setFlag(PendingUpdate::FullReload);
found = true;
}
}
if(found)
if (found)
scheduleUpdate();
}
void LineGraphManager::onLineRemoved(db_id lineId)
{
//Lines do not affect segments and stations
//So no other scene needs updating
// Lines do not affect segments and stations
// So no other scene needs updating
clearGraphsOfObject(lineId, LineGraphType::RailwayLine);
}
void LineGraphManager::onJobChanged(db_id jobId, db_id oldJobId)
{
//If job changed ID or category, update selection on all scenes which had it selected
//There is no need to reload jobs because it is already done.
//In fact when a job changes ID, all station interested by this job get informed, and scenes reloaded
// If job changed ID or category, update selection on all scenes which had it selected
// There is no need to reload jobs because it is already done.
// In fact when a job changes ID, all station interested by this job get informed, and scenes
// reloaded
JobStopEntry selectedJob;
selectedJob.jobId = jobId;
LineGraphScene::updateJobSelection(Session->m_Db, selectedJob);
if(!selectedJob.jobId)
return; //Invalid job ID
if (!selectedJob.jobId)
return; // Invalid job ID
if(activeScene && AppSettings.getSyncSelectionOnAllGraphs())
if (activeScene && AppSettings.getSyncSelectionOnAllGraphs())
{
//Update active scene before others in case selection is synced
//This way all scenes get updated selection
// Update active scene before others in case selection is synced
// This way all scenes get updated selection
JobStopEntry oldSelectedJob = activeScene->getSelectedJob();
if(oldSelectedJob.jobId == oldJobId)
if (oldSelectedJob.jobId == oldJobId)
{
activeScene->setSelectedJob(selectedJob);
}
}
else
{
//Manually update all scenes
for(LineGraphScene *scene : qAsConst(scenes))
// Manually update all scenes
for (LineGraphScene *scene : qAsConst(scenes))
{
JobStopEntry oldSelectedJob = scene->getSelectedJob();
if(oldSelectedJob.jobId == oldJobId)
if (oldSelectedJob.jobId == oldJobId)
{
scene->setSelectedJob(selectedJob);
}
@ -508,64 +518,66 @@ void LineGraphManager::onJobChanged(db_id jobId, db_id oldJobId)
void LineGraphManager::onJobRemoved(db_id jobId)
{
//We already catch normal job removal with other signals
if(jobId)
// We already catch normal job removal with other signals
if (jobId)
return;
//If jobId is zero, it means all jobs have been deleted
//Reload all scenes
// If jobId is zero, it means all jobs have been deleted
// Reload all scenes
bool found = false;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload)
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload)
|| scene->pendingUpdate.testFlag(PendingUpdate(PendingUpdate::ReloadJobs)))
continue; //Already flagged
continue; // Already flagged
scene->pendingUpdate.setFlag(PendingUpdate(PendingUpdate::ReloadJobs));
found = true;
}
if(found)
if (found)
scheduleUpdate();
}
void LineGraphManager::updateGraphOptions()
{
//TODO: maybe get rid of theese variables in MeetingSession and always use AppSettings?
int hourOffset = AppSettings.getHourOffset();
Session->hourOffset = hourOffset;
// TODO: maybe get rid of theese variables in MeetingSession and always use AppSettings?
int hourOffset = AppSettings.getHourOffset();
Session->hourOffset = hourOffset;
int horizOffset = AppSettings.getHorizontalOffset();
Session->horizOffset = horizOffset;
int horizOffset = AppSettings.getHorizontalOffset();
Session->horizOffset = horizOffset;
int vertOffset = AppSettings.getVerticalOffset();
Session->vertOffset = vertOffset;
int vertOffset = AppSettings.getVerticalOffset();
Session->vertOffset = vertOffset;
Session->stationOffset = AppSettings.getStationOffset();
Session->stationOffset = AppSettings.getStationOffset();
Session->platformOffset = AppSettings.getPlatformOffset();
Session->jobLineWidth = AppSettings.getJobLineWidth();
Session->jobLineWidth = AppSettings.getJobLineWidth();
//Reload all graphs
for(LineGraphScene *scene : qAsConst(scenes))
// Reload all graphs
for (LineGraphScene *scene : qAsConst(scenes))
{
scene->reload();
}
const bool oldVal = m_followJobOnGraphChange;
const bool oldVal = m_followJobOnGraphChange;
m_followJobOnGraphChange = AppSettings.getFollowSelectionOnGraphChange();
if(m_followJobOnGraphChange != oldVal)
if (m_followJobOnGraphChange != oldVal)
{
//Update connections
for(LineGraphScene *scene : qAsConst(scenes))
// Update connections
for (LineGraphScene *scene : qAsConst(scenes))
{
if(m_followJobOnGraphChange)
connect(scene, &LineGraphScene::graphChanged, this, &LineGraphManager::onGraphChanged);
if (m_followJobOnGraphChange)
connect(scene, &LineGraphScene::graphChanged, this,
&LineGraphManager::onGraphChanged);
else
disconnect(scene, &LineGraphScene::graphChanged, this, &LineGraphManager::onGraphChanged);
disconnect(scene, &LineGraphScene::graphChanged, this,
&LineGraphManager::onGraphChanged);
}
}
}
@ -574,15 +586,15 @@ void LineGraphManager::onStationPlanChanged_internal(const QSet<db_id> &stationI
{
bool found = false;
for(LineGraphScene *scene : qAsConst(scenes))
for (LineGraphScene *scene : qAsConst(scenes))
{
if(scene->pendingUpdate.testFlag(PendingUpdate::FullReload)
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload)
|| scene->pendingUpdate.testFlag(PendingUpdate(flag)))
continue; //Already flagged
continue; // Already flagged
for(db_id stationId : stationIds)
for (db_id stationId : stationIds)
{
if(scene->stations.contains(stationId))
if (scene->stations.contains(stationId))
{
scene->pendingUpdate.setFlag(PendingUpdate(flag));
found = true;
@ -591,6 +603,6 @@ void LineGraphManager::onStationPlanChanged_internal(const QSet<db_id> &stationI
}
}
if(found)
if (found)
scheduleUpdate();
}

View File

@ -82,7 +82,10 @@ public:
* \brief get active scene
* \return Scene instance or nullptr if no scene is active
*/
inline LineGraphScene *getActiveScene() const { return activeScene; }
inline LineGraphScene *getActiveScene() const
{
return activeScene;
}
/*!
* \brief get current selected job
@ -151,32 +154,32 @@ public slots:
void setActiveScene(IGraphScene *scene);
private slots:
//Scenes
// Scenes
void onSceneDestroyed(QObject *obj);
void onGraphChanged(int graphType_, db_id graphObjId, LineGraphScene *scene);
void onJobSelected(db_id jobId, int category, db_id stopId);
//Stations
// Stations
void onStationNameChanged(db_id stationId);
void onStationJobPlanChanged(const QSet<db_id> &stationIds);
void onStationTrackPlanChanged(const QSet<db_id> &stationIds);
void onStationRemoved(db_id stationId);
//Segments
// Segments
void onSegmentNameChanged(db_id segmentId);
void onSegmentStationsChanged(db_id segmentId);
void onSegmentRemoved(db_id segmentId);
//Lines
// Lines
void onLineNameChanged(db_id lineId);
void onLineSegmentsChanged(db_id lineId);
void onLineRemoved(db_id lineId);
//Jobs
// Jobs
void onJobChanged(db_id jobId, db_id oldJobId);
void onJobRemoved(db_id jobId);
//Settings
// Settings
void updateGraphOptions();
private:

File diff suppressed because it is too large Load Diff

View File

@ -57,18 +57,18 @@ public:
*/
enum class PendingUpdate
{
NothingToDo = 0x0, //!< No content needs updating
ReloadJobs = 0x1, //!< Only Jobs need to be reloaded
NothingToDo = 0x0, //!< No content needs updating
ReloadJobs = 0x1, //!< Only Jobs need to be reloaded
ReloadStationNames = 0x2, //!< Only Station Names but not Station Plan has changed
FullReload = 0x4 //!< Do a full reload
FullReload = 0x4 //!< Do a full reload
};
Q_DECLARE_FLAGS(PendingUpdateFlags, PendingUpdate)
LineGraphScene(sqlite3pp::database &db, QObject *parent = nullptr);
void renderContents(QPainter *painter, const QRectF& sceneRect) override;
void renderHeader(QPainter *painter, const QRectF& sceneRect,
Qt::Orientation orient, double scroll) override;
void renderContents(QPainter *painter, const QRectF &sceneRect) override;
void renderHeader(QPainter *painter, const QRectF &sceneRect, Qt::Orientation orient,
double scroll) override;
/*!
* \brief Load graph contents
@ -109,7 +109,7 @@ public:
* \param pos Point in scene coordinates
* \param tolerance A tolerance if mouse doesn't exactly click on job item
*/
JobStopEntry getJobAt(const QPointF& pos, const double tolerance);
JobStopEntry getJobAt(const QPointF &pos, const double tolerance);
inline LineGraphType getGraphType() const
{
@ -154,7 +154,10 @@ public:
*
* \sa setDrawSelection()
*/
inline bool getDrawSelection() { return m_drawSelection; }
inline bool getDrawSelection()
{
return m_drawSelection;
}
/*!
* \brief setDrawSelection
@ -165,7 +168,10 @@ public:
*
* \sa getDrawSelection()
*/
inline void setDrawSelection(bool val) { m_drawSelection = val; }
inline void setDrawSelection(bool val)
{
m_drawSelection = val;
}
/*!
* \brief requestShowZone
@ -208,7 +214,6 @@ public slots:
void reload();
private:
/*!
* \brief Graph of the job while is moving
*
@ -308,7 +313,8 @@ private:
* Load job segments and stores them in 'from' station
* \sa loadStationJobStops()
*/
bool loadSegmentJobs(StationPosEntry &stPos, const StationGraphObject &fromSt, const StationGraphObject &toSt);
bool loadSegmentJobs(StationPosEntry &stPos, const StationGraphObject &fromSt,
const StationGraphObject &toSt);
/*!
* \brief Update job selection category
@ -324,7 +330,7 @@ private:
friend class BackgroundHelper;
friend class LineGraphManager;
sqlite3pp::database& mDb;
sqlite3pp::database &mDb;
/*!
* \brief Graph Object ID

View File

@ -27,10 +27,10 @@ using namespace sqlite3pp;
LineGraphSelectionHelper::LineGraphSelectionHelper(sqlite3pp::database &db) :
mDb(db)
{
}
bool LineGraphSelectionHelper::tryFindJobStopInGraph(LineGraphScene *scene, db_id jobId, SegmentInfo &info)
bool LineGraphSelectionHelper::tryFindJobStopInGraph(LineGraphScene *scene, db_id jobId,
SegmentInfo &info)
{
query q(mDb);
@ -72,7 +72,7 @@ bool LineGraphSelectionHelper::tryFindJobStopInGraph(LineGraphScene *scene, db_i
case LineGraphType::NoGraph:
case LineGraphType::NTypes:
{
//We need to load a new graph, give up
// We need to load a new graph, give up
return false;
}
}
@ -80,18 +80,18 @@ bool LineGraphSelectionHelper::tryFindJobStopInGraph(LineGraphScene *scene, db_i
q.bind(1, jobId);
q.bind(2, scene->getGraphObjectId());
if(q.step() != SQLITE_ROW || q.getRows().column_type(0) == SQLITE_NULL)
return false; //We didn't find a stop in current graph, give up
if (q.step() != SQLITE_ROW || q.getRows().column_type(0) == SQLITE_NULL)
return false; // We didn't find a stop in current graph, give up
//Get stop info
auto stop = q.getRows();
info.firstStopId = stop.get<db_id>(0);
info.segmentId = stop.get<db_id>(1);
// Get stop info
auto stop = q.getRows();
info.firstStopId = stop.get<db_id>(0);
info.segmentId = stop.get<db_id>(1);
info.arrivalAndStart = stop.get<QTime>(2);
info.departure = stop.get<QTime>(3);
info.departure = stop.get<QTime>(3);
//If graph is SingleStation we already know the station ID
if(scene->getGraphType() == LineGraphType::SingleStation)
// If graph is SingleStation we already know the station ID
if (scene->getGraphType() == LineGraphType::SingleStation)
info.firstStationId = scene->getGraphObjectId();
else
info.firstStationId = stop.get<db_id>(4);
@ -99,96 +99,98 @@ bool LineGraphSelectionHelper::tryFindJobStopInGraph(LineGraphScene *scene, db_i
return true;
}
bool LineGraphSelectionHelper::tryFindJobStopsAfter(db_id jobId, SegmentInfo& info)
bool LineGraphSelectionHelper::tryFindJobStopsAfter(db_id jobId, SegmentInfo &info)
{
query q(mDb, "SELECT stops.id, MIN(stops.arrival), stops.departure, stops.station_id, c.seg_id"
" FROM stops"
" LEFT JOIN railway_connections c ON c.id=stops.next_segment_conn_id"
" WHERE stops.job_id=? AND stops.arrival>=?");
//Find first job stop after or equal to startTime
// Find first job stop after or equal to startTime
q.bind(1, jobId);
q.bind(2, info.arrivalAndStart);
if(q.step() != SQLITE_ROW || q.getRows().column_type(0) == SQLITE_NULL)
if (q.step() != SQLITE_ROW || q.getRows().column_type(0) == SQLITE_NULL)
return false;
//Get first stop info
auto stop = q.getRows();
info.firstStopId = stop.get<db_id>(0);
// Get first stop info
auto stop = q.getRows();
info.firstStopId = stop.get<db_id>(0);
info.arrivalAndStart = stop.get<QTime>(1);
info.departure = stop.get<QTime>(2);
info.firstStationId = stop.get<db_id>(3);
info.segmentId = stop.get<db_id>(4);
info.departure = stop.get<QTime>(2);
info.firstStationId = stop.get<db_id>(3);
info.segmentId = stop.get<db_id>(4);
q.reset();
//Try get a second stop after the first departure
//NOTE: minimum 60 seconds of travel between 2 consecutive stops
// Try get a second stop after the first departure
// NOTE: minimum 60 seconds of travel between 2 consecutive stops
q.bind(1, jobId);
q.bind(2, info.departure.addSecs(60));
if(q.step() != SQLITE_ROW)
if (q.step() != SQLITE_ROW)
{
//We found only 1 stop, return that
// We found only 1 stop, return that
info.secondStationId = 0;
return true;
}
//Get first stop info
// Get first stop info
stop = q.getRows();
//db_id secondStopId = stop.get<db_id>(0);
//QTime secondArrival = stop.get<QTime>(1);
info.departure = stop.get<QTime>(2); //Overwrite departure
// db_id secondStopId = stop.get<db_id>(0);
// QTime secondArrival = stop.get<QTime>(1);
info.departure = stop.get<QTime>(2); // Overwrite departure
info.secondStationId = stop.get<db_id>(3);
return true;
}
bool LineGraphSelectionHelper::tryFindNewGraphForJob(db_id jobId, SegmentInfo& info,
db_id &outGraphObjId, LineGraphType &outGraphType)
bool LineGraphSelectionHelper::tryFindNewGraphForJob(db_id jobId, SegmentInfo &info,
db_id &outGraphObjId,
LineGraphType &outGraphType)
{
if(!tryFindJobStopsAfter(jobId, info))
return false; //No stops found
if (!tryFindJobStopsAfter(jobId, info))
return false; // No stops found
if(!info.secondStationId || !info.segmentId)
if (!info.secondStationId || !info.segmentId)
{
//We found only 1 stop, select first station
// We found only 1 stop, select first station
outGraphObjId = info.firstStationId;
outGraphType = LineGraphType::SingleStation;
outGraphType = LineGraphType::SingleStation;
return true;
}
//Try to find a railway line which contains this segment
//FIXME: better criteria to choose a line (name?, number of segments?, prompt user?)
// Try to find a railway line which contains this segment
// FIXME: better criteria to choose a line (name?, number of segments?, prompt user?)
query q(mDb, "SELECT ls.line_id"
" FROM line_segments ls"
" WHERE ls.seg_id=?");
q.bind(1, info.segmentId);
if(q.step() == SQLITE_ROW)
if (q.step() == SQLITE_ROW)
{
//Found a line
// Found a line
outGraphObjId = q.getRows().get<db_id>(0);
outGraphType = LineGraphType::RailwayLine;
outGraphType = LineGraphType::RailwayLine;
return true;
}
//No lines found, use the railway segment
// No lines found, use the railway segment
outGraphObjId = info.segmentId;
outGraphType = LineGraphType::RailwaySegment;
outGraphType = LineGraphType::RailwaySegment;
return true;
}
bool LineGraphSelectionHelper::requestJobSelection(LineGraphScene *scene, db_id jobId, bool select, bool ensureVisible)
bool LineGraphSelectionHelper::requestJobSelection(LineGraphScene *scene, db_id jobId, bool select,
bool ensureVisible)
{
//Check jobId is valid and get category
// Check jobId is valid and get category
query q(mDb, "SELECT category FROM jobs WHERE id=?");
q.bind(1, jobId);
if(q.step() != SQLITE_ROW)
return false; //Job doen't exist
if (q.step() != SQLITE_ROW)
return false; // Job doen't exist
JobStopEntry selectedJob;
selectedJob.jobId = jobId;
selectedJob.jobId = jobId;
selectedJob.category = JobCategory(q.getRows().get<int>(0));
SegmentInfo info;
@ -196,65 +198,66 @@ bool LineGraphSelectionHelper::requestJobSelection(LineGraphScene *scene, db_id
// Try to select earliest stop of this job in current graph, if any
const bool found = tryFindJobStopInGraph(scene, selectedJob.jobId, info);
if(!found)
if (!found)
{
//Find a NEW line graph or segment or station with this job
// Find a NEW line graph or segment or station with this job
//Find first 2 stops of the job
// Find first 2 stops of the job
db_id graphObjId = 0;
db_id graphObjId = 0;
LineGraphType graphType = LineGraphType::NoGraph;
if(!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
if (!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
{
//Could not find a suitable graph, abort
// Could not find a suitable graph, abort
return false;
}
//NOTE: clear selection to avoid LineGraphManager trying to follow selection
//do not emit change because selection might be synced between all scenes
//and because it's restored soon after
// NOTE: clear selection to avoid LineGraphManager trying to follow selection
// do not emit change because selection might be synced between all scenes
// and because it's restored soon after
const JobStopEntry oldSelection = scene->getSelectedJob();
scene->setSelectedJob(JobStopEntry{}, false); //Clear selection
scene->setSelectedJob(JobStopEntry{}, false); // Clear selection
//Select the graph
// Select the graph
scene->loadGraph(graphObjId, graphType);
//Restore previous selection
// Restore previous selection
scene->setSelectedJob(oldSelection, false);
}
//Extract the info
// Extract the info
selectedJob.stopId = info.firstStopId;
if(!selectedJob.stopId)
return false; //No stop found, abort
if (!selectedJob.stopId)
return false; // No stop found, abort
//Select job
if(select)
// Select job
if (select)
scene->setSelectedJob(selectedJob);
if(ensureVisible)
if (ensureVisible)
{
return scene->requestShowZone(info.firstStationId, info.segmentId,
info.arrivalAndStart, info.departure);
return scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}
return true;
}
bool LineGraphSelectionHelper::requestCurrentJobPrevSegmentVisible(LineGraphScene *scene, bool goToStart)
bool LineGraphSelectionHelper::requestCurrentJobPrevSegmentVisible(LineGraphScene *scene,
bool goToStart)
{
JobStopEntry selectedJob = scene->getSelectedJob();
if(!selectedJob.jobId)
return false; //No job selected, nothing to do
if (!selectedJob.jobId)
return false; // No job selected, nothing to do
query q(mDb);
SegmentInfo info;
if(selectedJob.stopId && !goToStart)
if (selectedJob.stopId && !goToStart)
{
//Start from current stop and get previous stop
// Start from current stop and get previous stop
q.prepare("SELECT s2.job_id, s1.id, MAX(s1.arrival), s1.station_id, c.seg_id,"
"s2.departure, s2.station_id"
" FROM stops s2"
@ -263,71 +266,72 @@ bool LineGraphSelectionHelper::requestCurrentJobPrevSegmentVisible(LineGraphScen
" WHERE s2.id=? AND s1.arrival < s2.arrival");
q.bind(1, selectedJob.stopId);
if(q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
if (q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
{
auto stop = q.getRows();
auto stop = q.getRows();
db_id jobId = stop.get<db_id>(0);
if(jobId == selectedJob.jobId)
if (jobId == selectedJob.jobId)
{
//Found stop and belongs to requested job
info.firstStopId = stop.get<db_id>(1);
// Found stop and belongs to requested job
info.firstStopId = stop.get<db_id>(1);
info.arrivalAndStart = stop.get<QTime>(2);
info.firstStationId = stop.get<db_id>(3);
info.segmentId = stop.get<db_id>(4);
info.departure = stop.get<QTime>(5);
info.firstStationId = stop.get<db_id>(3);
info.segmentId = stop.get<db_id>(4);
info.departure = stop.get<QTime>(5);
info.secondStationId = stop.get<db_id>(6);
}
}
}
if(!info.firstStopId)
if (!info.firstStopId)
{
//goToStart or failed to get previous stop so go to start anyway
// goToStart or failed to get previous stop so go to start anyway
if(!tryFindJobStopsAfter(selectedJob.jobId, info))
return false; //No stops found, give up
if (!tryFindJobStopsAfter(selectedJob.jobId, info))
return false; // No stops found, give up
}
db_id graphObjId = 0;
db_id graphObjId = 0;
LineGraphType graphType = LineGraphType::NoGraph;
if(!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
if (!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
{
//Could not find a suitable graph, abort
// Could not find a suitable graph, abort
return false;
}
//NOTE: clear selection to avoid LineGraphManager trying to follow selection
//do not emit change because selection might be synced between all scenes
//and because it's restored soon after
scene->setSelectedJob(JobStopEntry{}, false); //Clear selection
// NOTE: clear selection to avoid LineGraphManager trying to follow selection
// do not emit change because selection might be synced between all scenes
// and because it's restored soon after
scene->setSelectedJob(JobStopEntry{}, false); // Clear selection
//Select the graph
// Select the graph
scene->loadGraph(graphObjId, graphType);
//Restore selection
// Restore selection
selectedJob.stopId = info.firstStopId;
scene->setSelectedJob(selectedJob); //This time emit
scene->setSelectedJob(selectedJob); // This time emit
return scene->requestShowZone(info.firstStationId, info.segmentId,
info.arrivalAndStart, info.departure);
return scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}
bool LineGraphSelectionHelper::requestCurrentJobNextSegmentVisible(LineGraphScene *scene, bool goToEnd)
bool LineGraphSelectionHelper::requestCurrentJobNextSegmentVisible(LineGraphScene *scene,
bool goToEnd)
{
//TODO: maybe go to first segment AFTER last segment in current view.
//So it may not be 'next' but maybe 2 or 3 segments after current.
// TODO: maybe go to first segment AFTER last segment in current view.
// So it may not be 'next' but maybe 2 or 3 segments after current.
JobStopEntry selectedJob = scene->getSelectedJob();
if(!selectedJob.jobId)
return false; //No job selected, nothing to do
if (!selectedJob.jobId)
return false; // No job selected, nothing to do
query q(mDb);
SegmentInfo info;
if(selectedJob.stopId && !goToEnd)
if (selectedJob.stopId && !goToEnd)
{
//Start from current stop and get next stop
// Start from current stop and get next stop
q.prepare("SELECT s2.job_id, s1.id, MIN(s1.arrival), s1.departure, s1.station_id, c.seg_id"
" FROM stops s2"
" JOIN stops s1 ON s1.job_id=s2.job_id"
@ -335,64 +339,64 @@ bool LineGraphSelectionHelper::requestCurrentJobNextSegmentVisible(LineGraphScen
" WHERE s2.id=? AND s1.arrival > s2.arrival");
q.bind(1, selectedJob.stopId);
if(q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
if (q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
{
auto stop = q.getRows();
auto stop = q.getRows();
db_id jobId = stop.get<db_id>(0);
if(jobId == selectedJob.jobId)
if (jobId == selectedJob.jobId)
{
//Found stop and belongs to requested job
info.firstStopId = stop.get<db_id>(1);
// Found stop and belongs to requested job
info.firstStopId = stop.get<db_id>(1);
info.arrivalAndStart = stop.get<QTime>(2);
info.departure = stop.get<QTime>(3);
info.firstStationId = stop.get<db_id>(4);
info.segmentId = stop.get<db_id>(5);
info.departure = stop.get<QTime>(3);
info.firstStationId = stop.get<db_id>(4);
info.segmentId = stop.get<db_id>(5);
}
}
}
if(!info.firstStopId)
if (!info.firstStopId)
{
//goToEnd or failed to get next stop so go to end anyway
// goToEnd or failed to get next stop so go to end anyway
//Select last station, no segment
// Select last station, no segment
q.prepare("SELECT s1.id, MAX(s1.arrival), s1.departure, s1.station_id"
" FROM stops s1"
" WHERE s1.job_id=?");
q.bind(1, selectedJob.jobId);
if(q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
if (q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
{
//Found stop and belongs to requested job
auto stop = q.getRows();
info.firstStopId = stop.get<db_id>(0);
// Found stop and belongs to requested job
auto stop = q.getRows();
info.firstStopId = stop.get<db_id>(0);
info.arrivalAndStart = stop.get<QTime>(1);
info.departure = stop.get<QTime>(2);
info.firstStationId = stop.get<db_id>(3);
info.departure = stop.get<QTime>(2);
info.firstStationId = stop.get<db_id>(3);
}
}
db_id graphObjId = 0;
db_id graphObjId = 0;
LineGraphType graphType = LineGraphType::NoGraph;
if(!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
if (!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
{
//Could not find a suitable graph, abort
// Could not find a suitable graph, abort
return false;
}
//NOTE: clear selection to avoid LineGraphManager trying to follow selection
//do not emit change because selection might be synced between all scenes
//and because it's restored soon after
scene->setSelectedJob(JobStopEntry{}, false); //Clear selection
// NOTE: clear selection to avoid LineGraphManager trying to follow selection
// do not emit change because selection might be synced between all scenes
// and because it's restored soon after
scene->setSelectedJob(JobStopEntry{}, false); // Clear selection
//Select the graph
// Select the graph
scene->loadGraph(graphObjId, graphType);
//Restore selection
// Restore selection
selectedJob.stopId = info.firstStopId;
scene->setSelectedJob(selectedJob); //This time emit
scene->setSelectedJob(selectedJob); // This time emit
return scene->requestShowZone(info.firstStationId, info.segmentId,
info.arrivalAndStart, info.departure);
return scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}

View File

@ -33,16 +33,15 @@ class database;
class LineGraphSelectionHelper
{
public:
/*!
* \brief The SegmentInfo struct
*/
struct SegmentInfo
{
db_id segmentId = 0;
db_id firstStationId = 0;
db_id segmentId = 0;
db_id firstStationId = 0;
db_id secondStationId = 0;
db_id firstStopId = 0;
db_id firstStopId = 0;
/*!
* \brief arrival and start
@ -56,7 +55,7 @@ public:
LineGraphSelectionHelper(sqlite3pp::database &db);
//Low level API
// Low level API
/*!
* \brief find job in current graph
@ -69,7 +68,7 @@ public:
* If scene has NoGraph or does not contain requested job then returns false
* SegmentInfo::secondStId is always left empty
*/
bool tryFindJobStopInGraph(LineGraphScene *scene, db_id jobId, SegmentInfo& info);
bool tryFindJobStopInGraph(LineGraphScene *scene, db_id jobId, SegmentInfo &info);
/*!
* \brief find 2 job stops after requested hour
@ -93,10 +92,11 @@ public:
* Try to find a railway line containing the job. If not found try with a railway segment.
* If neither line nor segment are found try with first stop station.
*/
bool tryFindNewGraphForJob(db_id jobId, SegmentInfo &info, db_id &outGraphObjId, LineGraphType &outGraphType);
bool tryFindNewGraphForJob(db_id jobId, SegmentInfo &info, db_id &outGraphObjId,
LineGraphType &outGraphType);
public:
//High level API
// High level API
/*!
* \brief request job selection

View File

@ -21,5 +21,4 @@
StationGraphObject::StationGraphObject()
{
}

View File

@ -21,7 +21,7 @@
#include "app/session.h"
#include "graph/model/linegraphscene.h"
#include "graph/model/linegraphscene.h"
#include "utils/jobcategorystrings.h"
#include <QPainter>
@ -31,9 +31,9 @@
#include <QDebug>
void BackgroundHelper::drawHourPanel(QPainter *painter, const QRectF& rect)
void BackgroundHelper::drawHourPanel(QPainter *painter, const QRectF &rect)
{
//TODO: settings
// TODO: settings
QFont hourTextFont;
setFontPointSizeDPI(hourTextFont, 15, painter);
@ -45,14 +45,14 @@ void BackgroundHelper::drawHourPanel(QPainter *painter, const QRectF& rect)
painter->setFont(hourTextFont);
painter->setPen(hourTextPen);
//qDebug() << "Drawing hours..." << rect << scroll;
// qDebug() << "Drawing hours..." << rect << scroll;
const QString fmt(QStringLiteral("%1:00"));
const qreal top = rect.top() - vertOffset;
const qreal top = rect.top() - vertOffset;
const qreal bottom = rect.bottom();
int h = qFloor(top / hourOffset);
if(h < 0)
int h = qFloor(top / hourOffset);
if (h < 0)
h = 0;
QRectF labelRect = rect;
@ -60,9 +60,9 @@ void BackgroundHelper::drawHourPanel(QPainter *painter, const QRectF& rect)
labelRect.setHeight(hourOffset);
labelRect.moveTop(h * hourOffset + vertOffset - hourOffset / 2);
for(; h <= 24 && labelRect.top() <= bottom; h++)
for (; h <= 24 && labelRect.top() <= bottom; h++)
{
//qDebug() << "Y:" << y << fmt.arg(h);
// qDebug() << "Y:" << y << fmt.arg(h);
painter->drawText(labelRect, fmt.arg(h), QTextOption(Qt::AlignVCenter | Qt::AlignRight));
labelRect.moveTop(labelRect.top() + hourOffset);
}
@ -71,39 +71,38 @@ void BackgroundHelper::drawHourPanel(QPainter *painter, const QRectF& rect)
void BackgroundHelper::drawBackgroundHourLines(QPainter *painter, const QRectF &rect)
{
const double horizOffset = Session->horizOffset;
const double vertOffset = Session->vertOffset;
const double hourOffset = Session->hourOffset;
const double vertOffset = Session->vertOffset;
const double hourOffset = Session->hourOffset;
QPen hourLinePen(AppSettings.getHourLineColor(), AppSettings.getHourLineWidth());
const qreal x1 = qMax(qreal(horizOffset), rect.left());
const qreal x2 = rect.right();
const qreal t = qMax(rect.top(), vertOffset);
const qreal b = rect.bottom();
const qreal t = qMax(rect.top(), vertOffset);
const qreal b = rect.bottom();
if(x1 > x2 || b < vertOffset || t > b)
if (x1 > x2 || b < vertOffset || t > b)
return;
int firstH = qCeil((t - vertOffset) / hourOffset);
int lastH = qFloor((b - vertOffset) / hourOffset);
int lastH = qFloor((b - vertOffset) / hourOffset);
if(firstH > 24 || lastH < 0)
if (firstH > 24 || lastH < 0)
return;
if(firstH < 0)
if (firstH < 0)
firstH = 0;
if(lastH > 24)
if (lastH > 24)
lastH = 24;
const int n = lastH - firstH + 1;
if(n <= 0)
if (n <= 0)
return;
qreal y = vertOffset + firstH * hourOffset;
qreal y = vertOffset + firstH * hourOffset;
QLineF *arr = new QLineF[n];
for(int i = 0; i < n; i++)
for (int i = 0; i < n; i++)
{
arr[i] = QLineF(x1, y, x2, y);
y += hourOffset;
@ -111,10 +110,11 @@ void BackgroundHelper::drawBackgroundHourLines(QPainter *painter, const QRectF &
painter->setPen(hourLinePen);
painter->drawLines(arr, n);
delete [] arr;
delete[] arr;
}
void BackgroundHelper::drawStationHeader(QPainter *painter, LineGraphScene *scene, const QRectF &rect)
void BackgroundHelper::drawStationHeader(QPainter *painter, LineGraphScene *scene,
const QRectF &rect)
{
QFont stationFont;
stationFont.setBold(true);
@ -132,23 +132,23 @@ void BackgroundHelper::drawStationHeader(QPainter *painter, LineGraphScene *scen
QPen nonElectricPlatfPen(Qt::black);
const qreal platformOffset = Session->platformOffset;
const int stationOffset = Session->stationOffset;
const int stationOffset = Session->stationOffset;
//On left go back by half station offset to center station label
//and center platform label by going a back of half platformOffset
const int leftOffset = -stationOffset/2 - platformOffset /2;
// On left go back by half station offset to center station label
// and center platform label by going a back of half platformOffset
const int leftOffset = -stationOffset / 2 - platformOffset / 2;
const double margin = stationOffset * 0.1;
const double margin = stationOffset * 0.1;
QRectF r = rect;
QRectF r = rect;
for(auto st : qAsConst(scene->stations))
for (auto st : qAsConst(scene->stations))
{
const double left = st.xPos + leftOffset;
const double left = st.xPos + leftOffset;
const double right = left + st.platforms.count() * platformOffset + stationOffset;
if(right < r.left() || left >= r.right())
continue; //Skip station, it's not visible
if (right < r.left() || left >= r.right())
continue; // Skip station, it's not visible
QRectF labelRect = r;
labelRect.setLeft(left + margin);
@ -160,20 +160,20 @@ void BackgroundHelper::drawStationHeader(QPainter *painter, LineGraphScene *scen
painter->drawText(labelRect, Qt::AlignVCenter | Qt::AlignCenter, st.stationName);
labelRect = r;
labelRect.setTop(r.top() + r.height() * 2/3);
labelRect.setTop(r.top() + r.height() * 2 / 3);
//Go to start of station (first platform)
//We need to compensate the half stationOffset used to center station label
double xPos = left + stationOffset/2;
// Go to start of station (first platform)
// We need to compensate the half stationOffset used to center station label
double xPos = left + stationOffset / 2;
labelRect.setWidth(platformOffset);
for(const StationGraphObject::PlatformGraph& platf : qAsConst(st.platforms))
for (const StationGraphObject::PlatformGraph &platf : qAsConst(st.platforms))
{
if(platf.platformType.testFlag(utils::StationTrackType::Electrified))
if (platf.platformType.testFlag(utils::StationTrackType::Electrified))
painter->setPen(electricPlatfPen);
else
painter->setPen(nonElectricPlatfPen);
if(platf.platformType.testFlag(utils::StationTrackType::Through))
if (platf.platformType.testFlag(utils::StationTrackType::Through))
painter->setFont(platfBoldFont);
else
painter->setFont(platfNormalFont);
@ -191,33 +191,33 @@ void BackgroundHelper::drawStations(QPainter *painter, LineGraphScene *scene, co
{
const QRgb white = qRgb(255, 255, 255);
//const int horizOffset = Session->horizOffset;
// const int horizOffset = Session->horizOffset;
const int vertOffset = Session->vertOffset;
//const int stationOffset = Session->stationOffset;
const double platfOffset = Session->platformOffset;
const int lastY = vertOffset + Session->hourOffset * 24 + 10;
// const int stationOffset = Session->stationOffset;
const double platfOffset = Session->platformOffset;
const int lastY = vertOffset + Session->hourOffset * 24 + 10;
const int width = AppSettings.getPlatformLineWidth();
const int width = AppSettings.getPlatformLineWidth();
const QColor mainPlatfColor = AppSettings.getMainPlatfColor();
QPen platfPen (mainPlatfColor, width);
QPen platfPen(mainPlatfColor, width);
QPointF top(0, vertOffset);
QPointF bottom(0, lastY);
for(const StationGraphObject &st : qAsConst(scene->stations))
for (const StationGraphObject &st : qAsConst(scene->stations))
{
const double left = st.xPos;
const double left = st.xPos;
const double right = left + st.platforms.count() * platfOffset;
if(left > rect.right() || right < rect.left())
continue; //Skip station, it's not visible
if (left > rect.right() || right < rect.left())
continue; // Skip station, it's not visible
top.rx() = bottom.rx() = st.xPos;
for(const StationGraphObject::PlatformGraph& platf : st.platforms)
for (const StationGraphObject::PlatformGraph &platf : st.platforms)
{
if(platf.color == white)
if (platf.color == white)
platfPen.setColor(mainPlatfColor);
else
platfPen.setColor(platf.color);
@ -232,9 +232,10 @@ void BackgroundHelper::drawStations(QPainter *painter, LineGraphScene *scene, co
}
}
void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF &rect, bool drawSelection)
void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF &rect,
bool drawSelection)
{
const double platfOffset = Session->platformOffset;
const double platfOffset = Session->platformOffset;
const double stationOffset = Session->stationOffset;
QFont jobNameFont;
@ -249,7 +250,7 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co
QPen selectedJobPen;
const JobStopEntry selectedJob = scene->getSelectedJob();
if(drawSelection && selectedJob.jobId)
if (drawSelection && selectedJob.jobId)
{
selectedJobPen.setWidthF(jobPen.widthF() * SelectedJobWidthFactor);
selectedJobPen.setCapStyle(Qt::RoundCap);
@ -266,48 +267,48 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co
JobCategory lastJobCategory = JobCategory::NCategories;
QTextOption textOption(Qt::AlignTop | Qt::AlignLeft);
for(const StationGraphObject &st : qAsConst(scene->stations))
for (const StationGraphObject &st : qAsConst(scene->stations))
{
const double left = st.xPos;
const double left = st.xPos;
const double right = left + st.platforms.count() * platfOffset;
//Set a maximum right edge to Job labels
//This allows to determine if they have to be drawn
// Set a maximum right edge to Job labels
// This allows to determine if they have to be drawn
const double maxJobLabelX = right + stationOffset;
if(left > rect.right() || maxJobLabelX < rect.left())
continue; //Skip station, it's not visible
if (left > rect.right() || maxJobLabelX < rect.left())
continue; // Skip station, it's not visible
top.rx() = bottom.rx() = st.xPos;
for(const StationGraphObject::PlatformGraph& platf : st.platforms)
for (const StationGraphObject::PlatformGraph &platf : st.platforms)
{
for(const StationGraphObject::JobStopGraph& jobStop : platf.jobStops)
for (const StationGraphObject::JobStopGraph &jobStop : platf.jobStops)
{
//NOTE: departure comes AFTER arrival in time, opposite than job segment
if(jobStop.arrivalY > rect.bottom() || jobStop.departureY < rect.top())
continue; //Skip, job not visible
// NOTE: departure comes AFTER arrival in time, opposite than job segment
if (jobStop.arrivalY > rect.bottom() || jobStop.departureY < rect.top())
continue; // Skip, job not visible
top.setY(jobStop.arrivalY);
bottom.setY(jobStop.departureY);
const bool nullStopDuration = qFuzzyCompare(top.y(), bottom.y());
if(drawSelection && selectedJob.jobId == jobStop.stop.jobId)
if (drawSelection && selectedJob.jobId == jobStop.stop.jobId)
{
//Draw selection around segment
// Draw selection around segment
painter->setPen(selectedJobPen);
if(nullStopDuration)
if (nullStopDuration)
painter->drawPoint(top);
else
painter->drawLine(top, bottom);
//Reset pen
// Reset pen
painter->setPen(jobPen);
}
if(lastJobCategory != jobStop.stop.category)
if (lastJobCategory != jobStop.stop.category)
{
QColor color = Session->colorForCat(jobStop.stop.category);
jobPen.setColor(color);
@ -315,17 +316,18 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co
lastJobCategory = jobStop.stop.category;
}
if(nullStopDuration)
if (nullStopDuration)
painter->drawPoint(top);
else
painter->drawLine(top, bottom);
if(jobStop.drawLabel)
if (jobStop.drawLabel)
{
const QString jobName = JobCategoryName::jobName(jobStop.stop.jobId, jobStop.stop.category);
const QString jobName =
JobCategoryName::jobName(jobStop.stop.jobId, jobStop.stop.category);
//Put label a bit to the left in respect to the stop arrival point
//Calculate width so it doesn't go after maxJobLabelX
// Put label a bit to the left in respect to the stop arrival point
// Calculate width so it doesn't go after maxJobLabelX
const qreal topWithMargin = top.x() + platfOffset / 2;
QRectF r(topWithMargin, top.y(), maxJobLabelX - topWithMargin, 25);
painter->drawText(r, jobName, textOption);
@ -338,7 +340,8 @@ void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, co
}
}
void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect, bool drawSelection)
void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect,
bool drawSelection)
{
const double stationOffset = Session->stationOffset;
@ -357,7 +360,7 @@ void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene,
QPen selectedJobPen;
const JobStopEntry selectedJob = scene->getSelectedJob();
if(drawSelection && selectedJob.jobId)
if (drawSelection && selectedJob.jobId)
{
selectedJobPen.setWidthF(jobPen.widthF() * SelectedJobWidthFactor);
selectedJobPen.setCapStyle(Qt::RoundCap);
@ -371,47 +374,47 @@ void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene,
JobCategory lastJobCategory = JobCategory::NCategories;
QTextOption textOption(Qt::AlignCenter);
//Iterate until one but last
//This way we can always acces next station
for(int i = 0; i < scene->stationPositions.size() - 1; i++)
// Iterate until one but last
// This way we can always acces next station
for (int i = 0; i < scene->stationPositions.size() - 1; i++)
{
const LineGraphScene::StationPosEntry& stPos = scene->stationPositions.at(i);
const LineGraphScene::StationPosEntry &stPos = scene->stationPositions.at(i);
const double left = stPos.xPos;
double right = 0;
const double left = stPos.xPos;
double right = 0;
if(i < scene->stationPositions.size() - 2)
if (i < scene->stationPositions.size() - 2)
{
const LineGraphScene::StationPosEntry& afterNextPos = scene->stationPositions.at(i + 2);
right = afterNextPos.xPos - stationOffset;
const LineGraphScene::StationPosEntry &afterNextPos = scene->stationPositions.at(i + 2);
right = afterNextPos.xPos - stationOffset;
}
else
{
right = rect.right(); //Last station, use all space on right side
right = rect.right(); // Last station, use all space on right side
}
if(left > rect.right() || right < rect.left())
continue; //Skip station, it's not visible
if (left > rect.right() || right < rect.left())
continue; // Skip station, it's not visible
for(const LineGraphScene::JobSegmentGraph& job : stPos.nextSegmentJobGraphs)
for (const LineGraphScene::JobSegmentGraph &job : stPos.nextSegmentJobGraphs)
{
//NOTE: departure comes BEFORE arrival in time, opposite than job stop
if(job.fromDeparture.y() > rect.bottom() || job.toArrival.y() < rect.top())
continue; //Skip, job not visible
// NOTE: departure comes BEFORE arrival in time, opposite than job stop
if (job.fromDeparture.y() > rect.bottom() || job.toArrival.y() < rect.top())
continue; // Skip, job not visible
const QLineF line(job.fromDeparture, job.toArrival);
if(drawSelection && selectedJob.jobId == job.jobId)
if (drawSelection && selectedJob.jobId == job.jobId)
{
//Draw selection around segment
// Draw selection around segment
painter->setPen(selectedJobPen);
painter->drawLine(line);
//Reset pen
// Reset pen
painter->setPen(jobPen);
}
if(lastJobCategory != job.category)
if (lastJobCategory != job.category)
{
QColor color = Session->colorForCat(job.category);
jobPen.setColor(color);
@ -423,35 +426,35 @@ void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene,
const QString jobName = JobCategoryName::jobName(job.jobId, job.category);
//Save old transformation to reset it after drawing text
// Save old transformation to reset it after drawing text
const QTransform oldTransf = painter->transform();
//Move to line center, it will be rotation pivot
// Move to line center, it will be rotation pivot
painter->translate(line.center());
//Rotate by line angle
// Rotate by line angle
qreal angle = line.angle();
if(job.fromDeparture.x() > job.toArrival.x())
angle += 180.0; //Prevent flipping text
if (job.fromDeparture.x() > job.toArrival.x())
angle += 180.0; // Prevent flipping text
painter->rotate(-angle); //minus because QPainter wants clockwise angle
painter->rotate(-angle); // minus because QPainter wants clockwise angle
const double lineLength = line.length();
QRectF textRect(-lineLength / 2, -30, lineLength, 25);
//Try to avoid overlapping text of crossing jobs, move text towards arrival
if(job.toArrival.x() > job.fromDeparture.x())
// Try to avoid overlapping text of crossing jobs, move text towards arrival
if (job.toArrival.x() > job.fromDeparture.x())
textRect.moveLeft(textRect.left() + lineLength / 5);
else
textRect.moveLeft(textRect.left() - lineLength / 5);
textRect = painter->boundingRect(textRect, jobName, textOption);
//Draw a semi transparent background to ease text reading
// Draw a semi transparent background to ease text reading
painter->fillRect(textRect, textBackground);
painter->drawText(textRect, jobName, textOption);
//Reset to old transformation
// Reset to old transformation
painter->setTransform(oldTransf);
}
}

View File

@ -36,21 +36,23 @@ class LineGraphScene;
class BackgroundHelper
{
public:
static void drawHourPanel(QPainter *painter, const QRectF& rect);
static void drawHourPanel(QPainter *painter, const QRectF &rect);
static void drawBackgroundHourLines(QPainter *painter, const QRectF& rect);
static void drawBackgroundHourLines(QPainter *painter, const QRectF &rect);
static void drawStationHeader(QPainter *painter, LineGraphScene *scene, const QRectF& rect);
static void drawStationHeader(QPainter *painter, LineGraphScene *scene, const QRectF &rect);
static void drawStations(QPainter *painter, LineGraphScene *scene, const QRectF& rect);
static void drawStations(QPainter *painter, LineGraphScene *scene, const QRectF &rect);
static void drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF& rect, bool drawSelection);
static void drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF &rect,
bool drawSelection);
static void drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect, bool drawSelection);
static void drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect,
bool drawSelection);
public:
static constexpr double SelectedJobWidthFactor = 3.0;
static constexpr int SelectedJobAlphaFactor = 127;
static constexpr int SelectedJobAlphaFactor = 127;
};
#endif // BACKGROUNDHELPER_H

View File

@ -49,20 +49,22 @@ LineGraphSelectionWidget::LineGraphSelectionWidget(QWidget *parent) :
QStringList items;
items.reserve(int(LineGraphType::NTypes));
for(int i = 0; i < int(LineGraphType::NTypes); i++)
for (int i = 0; i < int(LineGraphType::NTypes); i++)
items.append(utils::getLineGraphTypeName(LineGraphType(i)));
graphTypeCombo->addItems(items);
graphTypeCombo->setCurrentIndex(0);
connect(graphTypeCombo, qOverload<int>(&QComboBox::activated), this, &LineGraphSelectionWidget::onTypeComboActivated);
connect(objectCombo, &CustomCompletionLineEdit::completionDone, this, &LineGraphSelectionWidget::onCompletionDone);
connect(graphTypeCombo, qOverload<int>(&QComboBox::activated), this,
&LineGraphSelectionWidget::onTypeComboActivated);
connect(objectCombo, &CustomCompletionLineEdit::completionDone, this,
&LineGraphSelectionWidget::onCompletionDone);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
}
LineGraphSelectionWidget::~LineGraphSelectionWidget()
{
if(matchModel)
if (matchModel)
{
objectCombo->setModel(nullptr);
delete matchModel;
@ -77,7 +79,7 @@ LineGraphType LineGraphSelectionWidget::getGraphType() const
void LineGraphSelectionWidget::setGraphType(LineGraphType type)
{
if(getGraphType() == type)
if (getGraphType() == type)
return;
graphTypeCombo->setCurrentIndex(int(type));
@ -98,16 +100,16 @@ const QString &LineGraphSelectionWidget::getObjectName() const
void LineGraphSelectionWidget::setObjectId(db_id objectId, const QString &name)
{
if(m_graphType == LineGraphType::NoGraph)
return; //Object ID must be null
if (m_graphType == LineGraphType::NoGraph)
return; // Object ID must be null
m_name = name;
if(!objectId)
if (!objectId)
m_name.clear();
objectCombo->setData(objectId, name);
if(m_objectId != objectId)
if (m_objectId != objectId)
emit graphChanged(int(m_graphType), m_objectId);
}
@ -119,7 +121,7 @@ void LineGraphSelectionWidget::onTypeComboActivated(int index)
void LineGraphSelectionWidget::onCompletionDone()
{
if(!objectCombo->getData(m_objectId, m_name))
if (!objectCombo->getData(m_objectId, m_name))
return;
emit graphChanged(int(m_graphType), m_objectId);
@ -127,17 +129,17 @@ void LineGraphSelectionWidget::onCompletionDone()
void LineGraphSelectionWidget::setupModel(LineGraphType type)
{
if(type != m_graphType)
if (type != m_graphType)
{
//Clear old model
if(matchModel)
// Clear old model
if (matchModel)
{
objectCombo->setModel(nullptr);
delete matchModel;
matchModel = nullptr;
}
//Manually clear line edit
// Manually clear line edit
m_objectId = 0;
m_name.clear();
objectCombo->setData(m_objectId, m_name);
@ -147,7 +149,7 @@ void LineGraphSelectionWidget::setupModel(LineGraphType type)
case LineGraphType::NoGraph:
default:
{
//Prevent recursion on loadGraph() calling back to us
// Prevent recursion on loadGraph() calling back to us
type = LineGraphType::NoGraph;
break;
}
@ -168,14 +170,13 @@ void LineGraphSelectionWidget::setupModel(LineGraphType type)
case LineGraphType::RailwayLine:
{
LinesMatchModel *m = new LinesMatchModel(Session->m_Db, true, this);
matchModel = m;
matchModel = m;
break;
}
}
if(matchModel)
if (matchModel)
objectCombo->setModel(matchModel);
}
m_graphType = type;
}

View File

@ -51,7 +51,7 @@ public:
db_id getObjectId() const;
const QString &getObjectName() const;
void setObjectId(db_id objectId, const QString& name);
void setObjectId(db_id objectId, const QString &name);
void setName(const QString &newName);

View File

@ -49,7 +49,8 @@ LineGraphToolbar::LineGraphToolbar(QWidget *parent) :
lay->setContentsMargins(0, 0, 0, 0);
selectionWidget = new LineGraphSelectionWidget;
connect(selectionWidget, &LineGraphSelectionWidget::graphChanged, this, &LineGraphToolbar::onWidgetGraphChanged);
connect(selectionWidget, &LineGraphSelectionWidget::graphChanged, this,
&LineGraphToolbar::onWidgetGraphChanged);
lay->addWidget(selectionWidget);
redrawBut = new QPushButton(tr("Redraw"));
@ -70,57 +71,59 @@ LineGraphToolbar::LineGraphToolbar(QWidget *parent) :
zoomSpinBox->setRange(25, 400);
zoomSpinBox->setValue(mZoom);
zoomSpinBox->setSuffix(QChar('%'));
connect(zoomSpinBox, qOverload<int>(&QSpinBox::valueChanged), this, &LineGraphToolbar::updateZoomLevel);
connect(zoomSpinBox, qOverload<int>(&QSpinBox::valueChanged), this,
&LineGraphToolbar::updateZoomLevel);
lay->addWidget(zoomSpinBox);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
//Accept focus events by click
// Accept focus events by click
setFocusPolicy(Qt::ClickFocus);
//Install event filter to catch focus events on children widgets
for(QObject *child : selectionWidget->children())
// Install event filter to catch focus events on children widgets
for (QObject *child : selectionWidget->children())
{
if(child->isWidgetType())
if (child->isWidgetType())
child->installEventFilter(this);
}
//Install event filter on Zoom Slider to catch double click
// Install event filter on Zoom Slider to catch double click
zoomSlider->installEventFilter(this);
}
LineGraphToolbar::~LineGraphToolbar()
{
}
void LineGraphToolbar::setScene(LineGraphScene *scene)
{
if(m_scene)
if (m_scene)
{
disconnect(m_scene, &LineGraphScene::graphChanged, this, &LineGraphToolbar::onSceneGraphChanged);
disconnect(m_scene, &LineGraphScene::graphChanged, this,
&LineGraphToolbar::onSceneGraphChanged);
disconnect(m_scene, &QObject::destroyed, this, &LineGraphToolbar::onSceneDestroyed);
}
m_scene = scene;
if(m_scene)
if (m_scene)
{
connect(m_scene, &LineGraphScene::graphChanged, this, &LineGraphToolbar::onSceneGraphChanged);
connect(m_scene, &LineGraphScene::graphChanged, this,
&LineGraphToolbar::onSceneGraphChanged);
connect(m_scene, &QObject::destroyed, this, &LineGraphToolbar::onSceneDestroyed);
}
}
bool LineGraphToolbar::eventFilter(QObject *watched, QEvent *ev)
{
if(ev->type() == QEvent::FocusIn)
if (ev->type() == QEvent::FocusIn)
{
//If any of our child widgets receives focus, activate our scene
if(m_scene)
// If any of our child widgets receives focus, activate our scene
if (m_scene)
m_scene->activateScene();
}
if(watched == zoomSlider && ev->type() == QEvent::MouseButtonDblClick)
if (watched == zoomSlider && ev->type() == QEvent::MouseButtonDblClick)
{
//Zoom Slider was double clicked, reset zoom level to 100
// Zoom Slider was double clicked, reset zoom level to 100
updateZoomLevel(100);
}
@ -130,14 +133,14 @@ bool LineGraphToolbar::eventFilter(QObject *watched, QEvent *ev)
void LineGraphToolbar::resetToolbarToScene()
{
LineGraphType type = LineGraphType::NoGraph;
db_id objectId = 0;
db_id objectId = 0;
QString name;
if(m_scene)
if (m_scene)
{
type = m_scene->getGraphType();
type = m_scene->getGraphType();
objectId = m_scene->getGraphObjectId();
name = m_scene->getGraphObjectName();
name = m_scene->getGraphObjectName();
}
selectionWidget->setGraphType(type);
@ -146,7 +149,7 @@ void LineGraphToolbar::resetToolbarToScene()
void LineGraphToolbar::updateZoomLevel(int zoom)
{
if(mZoom == zoom)
if (mZoom == zoom)
return;
mZoom = zoom;
@ -160,13 +163,13 @@ void LineGraphToolbar::updateZoomLevel(int zoom)
void LineGraphToolbar::onWidgetGraphChanged(int type, db_id objectId)
{
LineGraphType graphType = LineGraphType(type);
if(graphType == LineGraphType::NoGraph)
if (graphType == LineGraphType::NoGraph)
objectId = 0;
if(graphType != LineGraphType::NoGraph && !objectId)
return; //User is still selecting an object
if (graphType != LineGraphType::NoGraph && !objectId)
return; // User is still selecting an object
if(m_scene)
if (m_scene)
m_scene->loadGraph(objectId, graphType);
}
@ -175,7 +178,7 @@ void LineGraphToolbar::onSceneGraphChanged(int type, db_id objectId)
selectionWidget->setGraphType(LineGraphType(type));
QString name;
if(m_scene && m_scene->getGraphObjectId() == objectId)
if (m_scene && m_scene->getGraphObjectId() == objectId)
name = m_scene->getGraphObjectName();
selectionWidget->setObjectId(objectId, name);
}
@ -183,12 +186,12 @@ void LineGraphToolbar::onSceneGraphChanged(int type, db_id objectId)
void LineGraphToolbar::onSceneDestroyed()
{
m_scene = nullptr;
resetToolbarToScene(); //Clear UI
resetToolbarToScene(); // Clear UI
}
void LineGraphToolbar::focusInEvent(QFocusEvent *e)
{
if(m_scene)
if (m_scene)
m_scene->activateScene();
QWidget::focusInEvent(e);

View File

@ -31,27 +31,28 @@
LineGraphView::LineGraphView(QWidget *parent) :
BasicGraphView(parent)
{
}
bool LineGraphView::viewportEvent(QEvent *e)
{
LineGraphScene *lineScene = qobject_cast<LineGraphScene *>(scene());
if(e->type() == QEvent::ToolTip && lineScene && lineScene->getGraphType() != LineGraphType::NoGraph)
if (e->type() == QEvent::ToolTip && lineScene
&& lineScene->getGraphType() != LineGraphType::NoGraph)
{
QHelpEvent *ev = static_cast<QHelpEvent *>(e);
QHelpEvent *ev = static_cast<QHelpEvent *>(e);
const QPointF scenePos = mapToScene(ev->pos());
JobStopEntry job = lineScene->getJobAt(scenePos, Session->platformOffset / 2);
JobStopEntry job = lineScene->getJobAt(scenePos, Session->platformOffset / 2);
if(job.jobId)
if (job.jobId)
{
QToolTip::showText(ev->globalPos(),
JobCategoryName::jobName(job.jobId, job.category),
QToolTip::showText(ev->globalPos(), JobCategoryName::jobName(job.jobId, job.category),
viewport());
}else{
}
else
{
QToolTip::hideText();
}
@ -71,11 +72,11 @@ void LineGraphView::mouseDoubleClickEvent(QMouseEvent *e)
{
LineGraphScene *lineScene = qobject_cast<LineGraphScene *>(scene());
if(!lineScene || lineScene->getGraphType() == LineGraphType::NoGraph)
return; //Nothing to select
if (!lineScene || lineScene->getGraphType() == LineGraphType::NoGraph)
return; // Nothing to select
const QPointF scenePos = mapToScene(e->pos());
JobStopEntry job = lineScene->getJobAt(scenePos, Session->platformOffset / 2);
JobStopEntry job = lineScene->getJobAt(scenePos, Session->platformOffset / 2);
lineScene->setSelectedJob(job);
}

View File

@ -36,7 +36,7 @@ LineGraphWidget::LineGraphWidget(QWidget *parent) :
{
QVBoxLayout *lay = new QVBoxLayout(this);
toolBar = new LineGraphToolbar(this);
toolBar = new LineGraphToolbar(this);
lay->addWidget(toolBar);
@ -45,12 +45,13 @@ LineGraphWidget::LineGraphWidget(QWidget *parent) :
m_scene = new LineGraphScene(Session->m_Db, this);
//Subscribe to notifications and to session managment
// Subscribe to notifications and to session managment
Session->getViewManager()->getLineGraphMgr()->registerScene(m_scene);
view->setScene(m_scene);
toolBar->setScene(m_scene);
connect(view, &LineGraphView::syncToolbarToScene, toolBar, &LineGraphToolbar::resetToolbarToScene);
connect(view, &LineGraphView::syncToolbarToScene, toolBar,
&LineGraphToolbar::resetToolbarToScene);
connect(toolBar, &LineGraphToolbar::requestRedraw, m_scene, &LineGraphScene::reload);
connect(toolBar, &LineGraphToolbar::requestZoom, view, &LineGraphView::setZoomLevel);
@ -59,7 +60,7 @@ LineGraphWidget::LineGraphWidget(QWidget *parent) :
bool LineGraphWidget::tryLoadGraph(db_id graphObjId, LineGraphType type)
{
if(!m_scene)
if (!m_scene)
return false;
return m_scene->loadGraph(graphObjId, type);

View File

@ -46,9 +46,20 @@ class LineGraphWidget : public QWidget
public:
explicit LineGraphWidget(QWidget *parent = nullptr);
inline LineGraphScene *getScene() const { return m_scene; }
inline LineGraphView *getView() const { return view; }
inline LineGraphToolbar *getToolbar() const { return toolBar; }
inline LineGraphScene *getScene() const
{
return m_scene;
}
inline LineGraphView *getView() const
{
return view;
}
inline LineGraphToolbar *getToolbar() const
{
return toolBar;
}
bool tryLoadGraph(db_id graphObjId, LineGraphType type);

View File

@ -48,7 +48,6 @@
#include "app/scopedebug.h"
EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
QDialog(parent),
ui(new Ui::EditStopDialog),
@ -57,12 +56,13 @@ EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
{
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);
// 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();
@ -72,17 +72,17 @@ EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
ui->curStopLay->setWidget(2, QFormLayout::FieldRole, mStTrackEdit);
ui->curStopLay->setWidget(3, QFormLayout::FieldRole, mOutGateEdit);
//Coupling
couplingMgr = new RSCouplingInterface(Session->m_Db, this);
// Coupling
couplingMgr = new RSCouplingInterface(Session->m_Db, this);
coupledModel = new StopCouplingModel(Session->m_Db, this);
auto ps = new ModelPageSwitcher(true, 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 = new ModelPageSwitcher(true, this);
ps->setModel(uncoupledModel);
ui->uncoupledView->setModel(uncoupledModel);
ui->uncoupledLayout->insertWidget(1, ps);
@ -93,28 +93,32 @@ EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
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);
connect(ui->coupledView, &QAbstractItemView::customContextMenuRequested, this,
&EditStopDialog::couplingCustomContextMenuRequested);
connect(ui->uncoupledView, &QAbstractItemView::customContextMenuRequested, this,
&EditStopDialog::couplingCustomContextMenuRequested);
//Setup train asset models
// Setup train asset models
trainAssetModelBefore = new TrainAssetModel(Session->m_Db, this);
ps = new ModelPageSwitcher(true, 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 = 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);
connect(ui->assetBeforeView, &QAbstractItemView::customContextMenuRequested, this,
&EditStopDialog::couplingCustomContextMenuRequested);
connect(ui->assetAfterView, &QAbstractItemView::customContextMenuRequested, this,
&EditStopDialog::couplingCustomContextMenuRequested);
//Setup Crossings/Passings
// Setup Crossings/Passings
passingsModel = new JobPassingsModel(this);
ui->passingsView->setModel(passingsModel);
@ -123,13 +127,15 @@ EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
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
// 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."));
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);
@ -149,15 +155,15 @@ void EditStopDialog::clearUi()
{
helper->stopOutTrackTimer();
stopIdx = QModelIndex();
stopIdx = QModelIndex();
m_jobId = 0;
m_jobId = 0;
m_jobCat = JobCategory::FREIGHT;
trainAssetModelBefore->setStop(0, QTime(), TrainAssetModel::BeforeStop);
trainAssetModelAfter->setStop(0, QTime(), TrainAssetModel::AfterStop);
//TODO: clear UI properly
// TODO: clear UI properly
}
void EditStopDialog::showBeforeAsset(bool val)
@ -172,78 +178,79 @@ void EditStopDialog::showAfterAsset(bool val)
ui->assetAfterLabel->setVisible(val);
}
void EditStopDialog::setStop(const QModelIndex& idx)
void EditStopDialog::setStop(const QModelIndex &idx)
{
DEBUG_ENTRY;
if(!idx.isValid())
if (!idx.isValid())
{
clearUi();
return;
}
m_jobId = stopModel->getJobId();
m_jobCat = stopModel->getCategory();
m_jobId = stopModel->getJobId();
m_jobCat = stopModel->getCategory();
stopIdx = idx;
stopIdx = idx;
const StopItem& curStop = stopModel->getItemAt(idx.row());
const StopItem &curStop = stopModel->getItemAt(idx.row());
StopItem prevStop;
if(idx.row() == 0)
prevStop = StopItem(); //First stop has no previous stop
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
// 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
// Hide train asset before stop on First stop
showBeforeAsset(curStop.type != StopType::First);
//Hide train asset after stop on Last stop
// Hide train asset after stop on Last stop
showAfterAsset(curStop.type != StopType::Last);
//Coupling operations
// Coupling operations
coupledModel->setStop(curStop.stopId, RsOp::Coupled);
uncoupledModel->setStop(curStop.stopId, RsOp::Uncoupled);
//Update UI
// Update UI
updateInfo();
//Calc passings
// Calc passings
calcPassings();
//Update Title
// 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 StopItem &curStop = helper->getCurItem();
const StopItem &prevStop = helper->getPrevItem();
const QString inGateStr = helper->getGateString(curStop.fromGate.gateId,
prevStop.nextSegment.reversed);
const QString inGateStr =
helper->getGateString(curStop.fromGate.gateId, prevStop.nextSegment.reversed);
ui->inGateEdit->setText(inGateStr);
if(curStop.type == StopType::First)
if (curStop.type == StopType::First)
{
//Hide box of previous stop
// Hide box of previous stop
ui->prevStopBox->setVisible(false);
ui->curStopBox->setTitle(tr("First Stop"));
}
else
{
//Show box of previous stop
// Show box of previous stop
ui->prevStopBox->setVisible(true);
ui->curStopBox->setTitle(curStop.type == StopType::Last ? tr("Last Stop") : tr("Current Stop"));
ui->curStopBox->setTitle(curStop.type == StopType::Last ? tr("Last Stop")
: tr("Current Stop"));
QString prevStName;
if(prevStop.stationId)
if (prevStop.stationId)
{
query q(Session->m_Db, "SELECT name FROM stations WHERE id=?");
q.bind(1, prevStop.stationId);
@ -252,17 +259,17 @@ void EditStopDialog::updateInfo()
}
ui->prevStEdit->setText(prevStName);
const QString outGateStr = helper->getGateString(prevStop.toGate.gateId,
prevStop.nextSegment.reversed);
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)
if (curStop.type == StopType::Transit)
{
//On transit you cannot couple/uncouple rollingstock
// On transit you cannot couple/uncouple rollingstock
ui->editCoupledBut->setEnabled(false);
ui->editUncoupledBut->setEnabled(false);
}
@ -276,13 +283,12 @@ void EditStopDialog::saveDataToModel()
{
DEBUG_ENTRY;
const StopItem& curStop = helper->getCurItem();
const StopItem& prevStop = helper->getPrevItem();
const StopItem &curStop = helper->getCurItem();
const StopItem &prevStop = helper->getPrevItem();
if(ui->descriptionEdit->document()->isModified())
if (ui->descriptionEdit->document()->isModified())
{
stopModel->setDescription(stopIdx,
ui->descriptionEdit->toPlainText());
stopModel->setDescription(stopIdx, ui->descriptionEdit->toPlainText());
}
bool avoidTimeRecalc = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
@ -291,8 +297,8 @@ void EditStopDialog::saveDataToModel()
void EditStopDialog::importJobRS()
{
const StopItem& curStop = helper->getCurItem();
if(!curStop.stationId)
const StopItem &curStop = helper->getCurItem();
if (!curStop.stationId)
{
QMessageBox::warning(this, tr("Import Error"),
tr("In order to import rollingstock from other<br>"
@ -305,48 +311,49 @@ void EditStopDialog::importJobRS()
jobsMatch.setDefaultId(JobMatchModel::StopId);
jobsMatch.setFilter(m_jobId, curStop.stationId, curStop.departure);
QString stName = helper->getStationEdit()->text();
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->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
// Select model
int ret = dlg->exec();
if(ret != QDialog::Accepted || !dlg)
if (ret != QDialog::Accepted || !dlg)
return;
db_id otherJobStopId = dlg->getItemId();
if(!otherJobStopId)
if (!otherJobStopId)
return;
//Import rollingstock
// Import rollingstock
int count = couplingMgr->importRSFromJob(otherJobStopId);
//Refresh views
// 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));
// 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();
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->loadProxyModels(Session->m_Db, m_jobId, curStop.stopId, curStop.stationId,
curStop.arrival);
dlg->exec();
@ -356,14 +363,15 @@ void EditStopDialog::editCoupled()
void EditStopDialog::editUncoupled()
{
const StopItem& curStop = helper->getCurItem();
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->loadProxyModels(Session->m_Db, m_jobId, curStop.stopId, curStop.stationId,
curStop.arrival);
dlg->exec();
@ -381,20 +389,21 @@ void EditStopDialog::calcPassings()
{
DEBUG_ENTRY;
const StopItem& curStop = helper->getCurItem();
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 <> ?");
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);
@ -403,27 +412,27 @@ void EditStopDialog::calcPassings()
QVector<JobPassingsModel::Entry> passings, crossings;
for(auto r : q)
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.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
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
if (myDirection == otherDir)
passings.append(e); // Same direction -> Passing
else
crossings.append(e); //Opposite direction -> Crossing
crossings.append(e); // Opposite direction -> Crossing
}
q.reset();
@ -435,20 +444,21 @@ void EditStopDialog::calcPassings()
ui->crossingsView->resizeColumnsToContents();
}
void EditStopDialog::couplingCustomContextMenuRequested(const QPoint& pos)
void EditStopDialog::couplingCustomContextMenuRequested(const QPoint &pos)
{
OwningQPointer<QMenu> menu = new QMenu(this);
QAction *act = menu->addAction(tr("Refresh"));
QAction *act = menu->addAction(tr("Refresh"));
//HACK: could be ui->coupledView or ui->uncoupledView or ui->assetBeforeView or ui->assetAfterView
// 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 (!view)
return; // Error: not called by the view?
if(menu->exec(view->viewport()->mapToGlobal(pos)) != act)
return; //User didn't select 'Refresh' action
if (menu->exec(view->viewport()->mapToGlobal(pos)) != act)
return; // User didn't select 'Refresh' action
//Refresh data
// Refresh data
coupledModel->refreshData(true);
uncoupledModel->refreshData(true);
trainAssetModelBefore->refreshData(true);
@ -457,7 +467,7 @@ void EditStopDialog::couplingCustomContextMenuRequested(const QPoint& pos)
int EditStopDialog::getTrainSpeedKmH(bool afterStop)
{
const StopItem& curStop = helper->getCurItem();
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)"
@ -468,11 +478,11 @@ int EditStopDialog::getTrainSpeedKmH(bool afterStop)
" 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
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)
// 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);
@ -483,48 +493,48 @@ int EditStopDialog::getTrainSpeedKmH(bool afterStop)
void EditStopDialog::updateAdditionalNotes()
{
const StopItem& curStop = helper->getCurItem();
const StopItem &curStop = helper->getCurItem();
QString msg;
//Check direction
if(curStop.fromGate.gateConnId && curStop.toGate.gateConnId
&& curStop.type != StopType::First && curStop.type != StopType::Last)
// 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)
// 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
// 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)
// Check line traction
if (curStop.type != StopType::Last && curStop.nextSegment.segmentId)
{
//Last has no next segment so do not show traction type
// 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
bool prevSegmentElectrified = !nextSegmentElectrified; // Trigger change on First stop
if(curStop.type != StopType::First && stopIdx.row() >= 0)
if (curStop.type != StopType::First && stopIdx.row() >= 0)
{
//Get real previous railway type
// Get real previous railway type
prevSegmentElectrified = stopModel->isRailwayElectrifiedAfterRow(stopIdx.row() - 1);
}
if(!msg.isEmpty())
msg.append("\n\n"); //Separate from previous message
if (!msg.isEmpty())
msg.append("\n\n"); // Separate from previous message
if(nextSegmentElectrified)
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)
if (nextSegmentElectrified != prevSegmentElectrified && curStop.type != StopType::First)
{
//Railway type changed
// Railway type changed
msg.append('\n');
msg.append(tr("(Different traction then previous line!)"));
}
@ -536,7 +546,7 @@ void EditStopDialog::updateAdditionalNotes()
void EditStopDialog::setReadOnly(bool value)
{
readOnly = value;
readOnly = value;
CustomCompletionLineEdit *mStationEdit = helper->getStationEdit();
CustomCompletionLineEdit *mStTrackEdit = helper->getStTrackEdit();
@ -557,73 +567,78 @@ void EditStopDialog::setReadOnly(bool value)
void EditStopDialog::done(int val)
{
if(val == QDialog::Accepted)
if (val == QDialog::Accepted)
{
if(stopIdx.row() < stopModel->rowCount() - 2)
if (stopIdx.row() < stopModel->rowCount() - 2)
{
//We are not last stop
// 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
// 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)
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);
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)
if (ret == QMessageBox::Yes)
{
return; //Second chance to edit couplings
return; // Second chance to edit couplings
}
}
#ifdef ENABLE_AUTO_TIME_RECALC
if(originalSpeedAfterStop != newSpeedAfterStop)
if (originalSpeedAfterStop != newSpeedAfterStop)
{
int speedBefore = originalSpeedAfterStop;
int speedAfter = newSpeedAfterStop;
int speedBefore = originalSpeedAfterStop;
int speedAfter = newSpeedAfterStop;
LinesModel *linesModel = stopModel->getLinesModel();
db_id lineId = curLine ? curLine : stopIdx.data(NEXT_LINE_ROLE).toLongLong();
db_id lineId = curLine ? curLine : stopIdx.data(NEXT_LINE_ROLE).toLongLong();
int lineSpeed = linesModel->getLineSpeed(lineId);
if(speedBefore == 0)
if (speedBefore == 0)
{
//If speed is null (likely because there weren't RS coupled before)
//Fall back to line max speed
// If speed is null (likely because there weren't RS coupled before)
// Fall back to line max speed
speedBefore = lineSpeed;
}
if(speedAfter == 0)
if (speedAfter == 0)
{
//If speed is null (likely because there isn't RS coupled after this stop)
//Fall back to line max speed
// 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);
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)
if (ret == QMessageBox::Cancel)
{
return; //Second chance to edit couplings
return; // Second chance to edit couplings
}
if(ret == QMessageBox::Yes)
if (ret == QMessageBox::Yes)
{
stopModel->rebaseTimesToSpeed(stopIdx.row(), ui->arrivalTimeEdit->time(), ui->departureTimeEdit->time());
stopModel->rebaseTimesToSpeed(stopIdx.row(), ui->arrivalTimeEdit->time(),
ui->departureTimeEdit->time());
}
}
#endif

View File

@ -95,11 +95,11 @@ private:
StopCouplingModel *coupledModel;
StopCouplingModel *uncoupledModel;
TrainAssetModel *trainAssetModelBefore;
TrainAssetModel *trainAssetModelAfter;
TrainAssetModel *trainAssetModelBefore;
TrainAssetModel *trainAssetModelAfter;
JobPassingsModel *passingsModel;
JobPassingsModel *crossingsModel;
JobPassingsModel *passingsModel;
JobPassingsModel *crossingsModel;
bool readOnly;
};

View File

@ -46,7 +46,6 @@
#include "utils/delegates/sql/customcompletionlineedit.h"
#include "shifts/shiftcombomodel.h"
#include "utils/owningqpointer.h"
#include <QMenu>
#include <QMessageBox>
@ -55,7 +54,6 @@
#include <QCloseEvent>
JobPathEditor::JobPathEditor(QWidget *parent) :
QDialog(parent),
ui(new Ui::JobPathEditor),
@ -69,7 +67,7 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
QStringList catNames;
catNames.reserve(int(JobCategory::NCategories));
for(int cat = 0; cat < int(JobCategory::NCategories); cat++)
for (int cat = 0; cat < int(JobCategory::NCategories); cat++)
{
catNames.append(JobCategoryName::fullName(JobCategory(cat)));
}
@ -79,17 +77,18 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
ui->categoryCombo->setCurrentIndex(-1);
ShiftComboModel *shiftComboModel = new ShiftComboModel(Session->m_Db, this);
shiftCombo = new CustomCompletionLineEdit(shiftComboModel);
shiftCombo = new CustomCompletionLineEdit(shiftComboModel);
//Get Catecory combo position and insert shift below
// Get Catecory combo position and insert shift below
int categoryRow = 0;
QFormLayout::ItemRole unusedRole;
ui->formLayout->getWidgetPosition(ui->categoryCombo, &categoryRow, &unusedRole);
ui->formLayout->insertRow(categoryRow + 1, tr("Shift:"), shiftCombo);
//Stops
// Stops
stopModel = new StopModel(Session->m_Db, this);
connect(shiftCombo, &CustomCompletionLineEdit::dataIdChanged, stopModel, &StopModel::setNewShiftId);
connect(shiftCombo, &CustomCompletionLineEdit::dataIdChanged, stopModel,
&StopModel::setNewShiftId);
ui->stopsView->setModel(stopModel);
delegate = new StopDelegate(Session->m_Db, this);
@ -99,7 +98,7 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
ui->stopsView->setMovement(QListView::Static);
ui->stopsView->setSelectionMode(QListView::ContiguousSelection);
//Next/Prev Jobs
// Next/Prev Jobs
prevJobsModel = new NextPrevRSJobsModel(Session->m_Db, this);
prevJobsModel->setMode(NextPrevRSJobsModel::PrevJobs);
ui->prevJobsView->setModel(prevJobsModel);
@ -108,11 +107,12 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
nextJobsModel->setMode(NextPrevRSJobsModel::NextJobs);
ui->nextJobsView->setModel(nextJobsModel);
connect(ui->categoryCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), stopModel, &StopModel::setCategory);
connect(ui->categoryCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated),
stopModel, &StopModel::setCategory);
connect(stopModel, &StopModel::categoryChanged, this, &JobPathEditor::onCategoryChanged);
connect(ui->jobIdSpin, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &JobPathEditor::startJobNumberTimer);
connect(ui->jobIdSpin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
&JobPathEditor::startJobNumberTimer);
connect(stopModel, &StopModel::jobIdChanged, this, &JobPathEditor::onJobIdChanged);
connect(stopModel, &StopModel::edited, this, &JobPathEditor::setEdited);
@ -123,14 +123,17 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
connect(ui->sheetBut, &QPushButton::clicked, this, &JobPathEditor::onSaveSheet);
ui->stopsView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->stopsView, &QListView::customContextMenuRequested, this, &JobPathEditor::showStopsContextMenu);
connect(ui->stopsView, &QListView::customContextMenuRequested, this,
&JobPathEditor::showStopsContextMenu);
connect(ui->stopsView, &QListView::clicked, this, &JobPathEditor::onStopIndexClicked);
ui->prevJobsView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->prevJobsView, &QListView::customContextMenuRequested, this, &JobPathEditor::showJobContextMenu);
connect(ui->prevJobsView, &QListView::customContextMenuRequested, this,
&JobPathEditor::showJobContextMenu);
ui->nextJobsView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->nextJobsView, &QListView::customContextMenuRequested, this, &JobPathEditor::showJobContextMenu);
connect(ui->nextJobsView, &QListView::customContextMenuRequested, this,
&JobPathEditor::showJobContextMenu);
connect(Session, &MeetingSession::jobRemoved, this, &JobPathEditor::onJobRemoved);
connect(&AppSettings, &MRTPSettings::jobColorsChanged, this, &JobPathEditor::updateSpinColor);
@ -149,16 +152,16 @@ JobPathEditor::~JobPathEditor()
bool JobPathEditor::setJob(db_id jobId)
{
if(!canSetJob)
return false; //We are busy - (Avoid nested loop calls from inside 'saveChanges()')
if (!canSetJob)
return false; // We are busy - (Avoid nested loop calls from inside 'saveChanges()')
if(!isClear && stopModel->getJobId() == jobId)
return true; //Fake return, we already set this job
if (!isClear && stopModel->getJobId() == jobId)
return true; // Fake return, we already set this job
if(isEdited())
if (isEdited())
{
if(!maybeSave())
return false; //User still wants to edit the current job
if (!maybeSave())
return false; // User still wants to edit the current job
}
return setJob_internal(jobId);
@ -168,32 +171,32 @@ bool JobPathEditor::setJob_internal(db_id jobId)
{
DEBUG_IMPORTANT_ENTRY;
if(!canSetJob)
return false; //We are busy - (Avoid nested loop calls from inside 'saveChanges()')
if (!canSetJob)
return false; // We are busy - (Avoid nested loop calls from inside 'saveChanges()')
if(!isClear && stopModel->getJobId() == jobId)
return true; //Fake return, we already set this job
if (!isClear && stopModel->getJobId() == jobId)
return true; // Fake return, we already set this job
isClear = false;
stopJobNumberTimer();
//Load from database
if(!stopModel->loadJobStops(jobId))
// Load from database
if (!stopModel->loadJobStops(jobId))
{
//Error: job could not be loaded, maybe invalid jobId
// Error: job could not be loaded, maybe invalid jobId
clearJob();
setEnabled(false);
QMessageBox::warning(this, tr("Error Loading Job"),
tr("<b>Job %1</b> could not be loaded.<br>"
"Maybe it's number was changed or maybe it doesn't exist at all.")
.arg(jobId));
tr("<b>Job %1</b> could not be loaded.<br>"
"Maybe it's number was changed or maybe it doesn't exist at all.")
.arg(jobId));
return false;
}
//If read-only hide 'AddHere' row (last one)
// If read-only hide 'AddHere' row (last one)
ui->stopsView->setRowHidden(stopModel->rowCount() - 1, m_readOnly);
prevJobsModel->setJobId(jobId);
@ -204,16 +207,16 @@ bool JobPathEditor::setJob_internal(db_id jobId)
void JobPathEditor::startJobNumberTimer()
{
//Give user a small time to scroll values in ID QSpinBox
//This will skip eventual non available IDs (already existent)
//On timeout check ID and reset to old value if not available
// Give user a small time to scroll values in ID QSpinBox
// This will skip eventual non available IDs (already existent)
// On timeout check ID and reset to old value if not available
stopJobNumberTimer();
jobNumberTimerId = startTimer(700);
}
void JobPathEditor::stopJobNumberTimer()
{
if(jobNumberTimerId)
if (jobNumberTimerId)
{
killTimer(jobNumberTimerId);
jobNumberTimerId = 0;
@ -222,15 +225,16 @@ void JobPathEditor::stopJobNumberTimer()
void JobPathEditor::checkJobNumberValid()
{
//Kill timer
// Kill timer
stopJobNumberTimer();
db_id jobId = ui->jobIdSpin->value();
if(!stopModel->setNewJobId(jobId))
if (!stopModel->setNewJobId(jobId))
{
QMessageBox::warning(this, tr("Invalid"),
tr("Job number <b>%1</b> is already exists.<br>"
"Please choose a different number.").arg(jobId));
"Please choose a different number.")
.arg(jobId));
}
}
@ -249,43 +253,43 @@ bool JobPathEditor::createNewJob(db_id *out)
* or if user doesn't add at least 2 stops to the job
*/
if(out)
if (out)
*out = 0;
if(!clearJob())
return false; //Busy JobPathEditor
if (!clearJob())
return false; // Busy JobPathEditor
db_id jobId = 0;
if(!JobsHelper::createNewJob(Session->m_Db, jobId) || jobId == 0)
if (!JobsHelper::createNewJob(Session->m_Db, jobId) || jobId == 0)
{
return false; //An error occurred in database, abort
return false; // An error occurred in database, abort
}
if(!setJob_internal(jobId))
if (!setJob_internal(jobId))
{
//If we fail opening JobPathEditor remove the job
// If we fail opening JobPathEditor remove the job
JobsHelper::removeJob(Session->m_Db, jobId);
return false;
}
if(out)
if (out)
*out = jobId;
return true;
}
void JobPathEditor::showStopsContextMenu(const QPoint& pos)
void JobPathEditor::showStopsContextMenu(const QPoint &pos)
{
QModelIndex index = ui->stopsView->indexAt(pos);
if(!index.isValid() || index.row()>= stopModel->rowCount() || stopModel->isAddHere(index))
if (!index.isValid() || index.row() >= stopModel->rowCount() || stopModel->isAddHere(index))
return;
OwningQPointer<QMenu> menu = new QMenu(this);
QAction *toggleTransitAct = menu->addAction(tr("Toggle transit"));
QAction *setToTransitAct = menu->addAction(tr("Set transit"));
QAction *unsetTransit = menu->addAction(tr("Unset transit"));
QAction *toggleTransitAct = menu->addAction(tr("Toggle transit"));
QAction *setToTransitAct = menu->addAction(tr("Set transit"));
QAction *unsetTransit = menu->addAction(tr("Unset transit"));
menu->insertSeparator(unsetTransit);
QAction *editStopAct = menu->addAction(tr("Edit stop"));
QAction *editStopAct = menu->addAction(tr("Edit stop"));
QAction *showStationSVG = menu->addAction(tr("Station SVG Plan"));
menu->insertSeparator(editStopAct);
QAction *removeStopAct = menu->addAction(tr("Remove"));
@ -296,24 +300,24 @@ void JobPathEditor::showStopsContextMenu(const QPoint& pos)
removeStopAct->setEnabled(!m_readOnly);
const StopItem stop = stopModel->getItemAt(index.row());
showStationSVG->setEnabled(stop.stationId != 0); //Enable only if station is set
showStationSVG->setEnabled(stop.stationId != 0); // Enable only if station is set
QAction *act = menu->exec(ui->stopsView->viewport()->mapToGlobal(pos));
QAction *act = menu->exec(ui->stopsView->viewport()->mapToGlobal(pos));
QItemSelectionModel *sm = ui->stopsView->selectionModel();
QItemSelectionRange range;
QItemSelection s = ui->stopsView->selectionModel()->selection();
if(s.count() > 0)
if (s.count() > 0)
{
//Take the first range only
range = s.at(0); //Save range for later
// Take the first range only
range = s.at(0); // Save range for later
}
//Select only 1 index
// Select only 1 index
sm->select(index, QItemSelectionModel::ClearAndSelect);
if(act == editStopAct)
if (act == editStopAct)
{
OwningQPointer<EditStopDialog> dlg = new EditStopDialog(stopModel, this);
dlg->setReadOnly(m_readOnly);
@ -322,86 +326,87 @@ void JobPathEditor::showStopsContextMenu(const QPoint& pos)
return;
}
if(act == showStationSVG)
if (act == showStationSVG)
{
Session->getViewManager()->requestStSVGPlan(stop.stationId, true, stop.arrival);
}
if(m_readOnly)
if (m_readOnly)
return;
if(range.isValid())
if (range.isValid())
{
StopType type = StopType::ToggleType;
bool useRange = true;
if(act == toggleTransitAct)
if (act == toggleTransitAct)
type = StopType::ToggleType;
else if(act == setToTransitAct)
else if (act == setToTransitAct)
type = StopType::Transit;
else if(act == unsetTransit)
else if (act == unsetTransit)
type = StopType::Normal;
else
useRange = false;
if(useRange)
if (useRange)
{
stopModel->setStopTypeRange(range.top(), range.bottom(), type);
//Select only the range we changed (unselect possible other indexes)
sm->select(QItemSelection(range.topLeft(), range.bottomRight()), QItemSelectionModel::ClearAndSelect);
// Select only the range we changed (unselect possible other indexes)
sm->select(QItemSelection(range.topLeft(), range.bottomRight()),
QItemSelectionModel::ClearAndSelect);
return;
}
}
if(act == removeStopAct)
if (act == removeStopAct)
{
stopModel->removeStop(index);
}
}
void JobPathEditor::showJobContextMenu(const QPoint& pos)
void JobPathEditor::showJobContextMenu(const QPoint &pos)
{
QTableView *jobView = qobject_cast<QTableView *>(sender());
QTableView *jobView = qobject_cast<QTableView *>(sender());
NextPrevRSJobsModel *jobModel = nextJobsModel;
if(jobView == ui->prevJobsView)
if (jobView == ui->prevJobsView)
jobModel = prevJobsModel;
QModelIndex index = jobView->indexAt(pos);
QModelIndex index = jobView->indexAt(pos);
NextPrevRSJobsModel::Item item = jobModel->getItemAtRow(index.row());
OwningQPointer<QMenu> menu = new QMenu(this);
QAction *goToStop = menu->addAction(tr("Go to Stop"));
QAction *goToJob = menu->addAction(tr("Show Job"));
QAction *showRSPlan = menu->addAction(tr("Show RS Plan"));
OwningQPointer<QMenu> menu = new QMenu(this);
QAction *goToStop = menu->addAction(tr("Go to Stop"));
QAction *goToJob = menu->addAction(tr("Show Job"));
QAction *showRSPlan = menu->addAction(tr("Show RS Plan"));
menu->addSeparator();
QAction *refreshViews = menu->addAction(tr("Refresh"));
//Enable only if RS is not going to depot
// Enable only if RS is not going to depot
goToStop->setEnabled(index.isValid());
goToJob->setEnabled(index.isValid() && item.otherJobId != 0);
showRSPlan->setEnabled(index.isValid());
QAction *act = menu->exec(jobView->viewport()->mapToGlobal(pos));
if(act == goToStop)
if (act == goToStop)
{
selectStop(item.stopId);
}
else if(act == goToJob)
else if (act == goToJob)
{
if(isEdited()) //Prevent selecting other job before saving
if (isEdited()) // Prevent selecting other job before saving
{
if(!maybeSave())
if (!maybeSave())
return;
}
Session->getViewManager()->requestJobSelection(item.otherJobId, true, true);
}
else if(act == showRSPlan)
else if (act == showRSPlan)
{
Session->getViewManager()->requestRSInfo(item.rsId);
}
else if(act == refreshViews)
else if (act == refreshViews)
{
prevJobsModel->refreshData();
nextJobsModel->refreshData();
@ -412,18 +417,18 @@ bool JobPathEditor::clearJob()
{
DEBUG_ENTRY;
if(!canSetJob)
if (!canSetJob)
return false;
if(isEdited())
if (isEdited())
{
if(!maybeSave())
if (!maybeSave())
return false;
}
isClear = true;
//Reset color
// Reset color
ui->jobIdSpin->setPalette(QPalette());
stopModel->clearJob();
@ -438,23 +443,23 @@ bool JobPathEditor::clearJob()
void JobPathEditor::done(int res)
{
if(res == Accepted)
if (res == Accepted)
{
//Accepted: save changes
if(!saveChanges())
return; //Give user a second chance to edit job
// Accepted: save changes
if (!saveChanges())
return; // Give user a second chance to edit job
}
else
{
//Rejected: discard changes
// Rejected: discard changes
discardChanges();
}
//NOTE: if we call QDialog::done() the dialog is closed and QDockWidget remains open but empty
// NOTE: if we call QDialog::done() the dialog is closed and QDockWidget remains open but empty
setResult(res);
if(res == QDialog::Accepted)
if (res == QDialog::Accepted)
emit accepted();
else if(res == QDialog::Rejected)
else if (res == QDialog::Rejected)
emit rejected();
emit finished(res);
}
@ -463,7 +468,7 @@ bool JobPathEditor::saveChanges()
{
DEBUG_IMPORTANT_ENTRY;
if(!canSetJob)
if (!canSetJob)
return false;
canSetJob = false;
@ -474,15 +479,14 @@ bool JobPathEditor::saveChanges()
stopModel->removeLastIfEmpty();
stopModel->uncoupleStillCoupledAtLastStop();
if(stopModel->rowCount() < 3) //At least 2 stops + AddHere
if (stopModel->rowCount() < 3) // At least 2 stops + AddHere
{
int res = QMessageBox::warning(this,
tr("Error"),
int res = QMessageBox::warning(this, tr("Error"),
tr("You must register at least 2 stops.\n"
"Do you want to delete this job?"),
QMessageBox::Yes | QMessageBox::No);
if(res == QMessageBox::Yes)
if (res == QMessageBox::Yes)
{
qDebug() << "User wants to delete job:" << stopModel->getJobId();
stopModel->commitChanges();
@ -494,19 +498,18 @@ bool JobPathEditor::saveChanges()
}
canSetJob = true;
return false; //Give user a second chance
return false; // Give user a second chance
}
//Re-check shift because user may have added stops so this job could last longer!
if(stopModel->getNewShiftId())
// Re-check shift because user may have added stops so this job could last longer!
if (stopModel->getNewShiftId())
{
auto times = stopModel->getFirstLastTimes();
ShiftBusyModel model(Session->m_Db);
model.loadData(stopModel->getNewShiftId(),
stopModel->getJobId(),
times.first, times.second);
if(model.hasConcurrentJobs())
model.loadData(stopModel->getNewShiftId(), stopModel->getJobId(), times.first,
times.second);
if (model.hasConcurrentJobs())
{
OwningQPointer<ShiftBusyDlg> dlg = new ShiftBusyDlg(this);
dlg->setModel(&model);
@ -525,7 +528,7 @@ bool JobPathEditor::saveChanges()
prevJobsModel->setJobId(newJobId);
nextJobsModel->setJobId(newJobId);
//When updating the path selection gets cleared so we restore it
// When updating the path selection gets cleared so we restore it
Session->getViewManager()->requestJobSelection(newJobId, true, true);
canSetJob = true;
@ -536,28 +539,29 @@ void JobPathEditor::discardChanges()
{
DEBUG_ENTRY;
if(!canSetJob)
if (!canSetJob)
return;
canSetJob = false;
closeStopEditor(); //Close before rolling savepoint
closeStopEditor(); // Close before rolling savepoint
stopJobNumberTimer();
stopModel->revertChanges(); //Re-load old job from db
stopModel->revertChanges(); // Re-load old job from db
//After re-load but before possible 'clearJob()' (Below)
//Because this hides 'AddHere' so after 'loadJobStops()'
//Before 'clearJob()' because sets isEdited = false and
// After re-load but before possible 'clearJob()' (Below)
// Because this hides 'AddHere' so after 'loadJobStops()'
// Before 'clearJob()' because sets isEdited = false and
//'maybeSave()' doesn't be called in an infinite loop
canSetJob = true;
if(stopModel->rowCount() < 3) //At least 2 stops + AddHere
if (stopModel->rowCount() < 3) // At least 2 stops + AddHere
{
//User discarded an invalid job so we delete it
//This usually happens when you create a new job but then you change your mind and press 'Discard'
// User discarded an invalid job so we delete it
// This usually happens when you create a new job but then you change your mind and press
// 'Discard'
qDebug() << "User wants to delete job:" << stopModel->getJobId();
stopModel->commitChanges();
JobsHelper::removeJob(Session->m_Db, stopModel->getJobId());
@ -574,11 +578,9 @@ db_id JobPathEditor::currentJobId() const
bool JobPathEditor::maybeSave()
{
DEBUG_ENTRY;
QMessageBox::StandardButton ret = QMessageBox::question(this,
tr("Save?"),
tr("Do you want to save changes to job %1")
.arg(stopModel->getJobId()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
QMessageBox::StandardButton ret = QMessageBox::question(
this, tr("Save?"), tr("Do you want to save changes to job %1").arg(stopModel->getJobId()),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
switch (ret)
{
case QMessageBox::Yes:
@ -594,7 +596,7 @@ bool JobPathEditor::maybeSave()
void JobPathEditor::updateSpinColor()
{
if(!isClear)
if (!isClear)
{
QColor col = Session->colorForCat(stopModel->getCategory());
setSpinColor(col);
@ -603,7 +605,7 @@ void JobPathEditor::updateSpinColor()
void JobPathEditor::timerEvent(QTimerEvent *e)
{
if(e->timerId() == jobNumberTimerId)
if (e->timerId() == jobNumberTimerId)
{
checkJobNumberValid();
return;
@ -614,10 +616,10 @@ void JobPathEditor::timerEvent(QTimerEvent *e)
void JobPathEditor::onJobRemoved(db_id jobId)
{
//If the job shown is about to be removed clear JobPathEditor
if(stopModel->getJobId() == jobId)
// If the job shown is about to be removed clear JobPathEditor
if (stopModel->getJobId() == jobId)
{
if(clearJob())
if (clearJob())
setEnabled(false);
}
}
@ -627,7 +629,7 @@ void JobPathEditor::onJobIdChanged(db_id jobId)
ui->jobIdSpin->setValue(int(jobId));
}
void JobPathEditor::setSpinColor(const QColor& col)
void JobPathEditor::setSpinColor(const QColor &col)
{
QPalette pal = ui->jobIdSpin->palette();
pal.setColor(QPalette::Text, col);
@ -644,15 +646,13 @@ void JobPathEditor::onJobShiftChanged(db_id shiftId)
{
shiftCombo->setData(shiftId);
if(shiftId)
if (shiftId)
{
auto times = stopModel->getFirstLastTimes();
ShiftBusyModel model(Session->m_Db);
model.loadData(shiftId,
stopModel->getJobId(),
times.first, times.second);
if(model.hasConcurrentJobs())
model.loadData(shiftId, stopModel->getJobId(), times.first, times.second);
if (model.hasConcurrentJobs())
{
OwningQPointer<ShiftBusyDlg> dlg = new ShiftBusyDlg(this);
dlg->setModel(&model);
@ -667,8 +667,7 @@ void JobPathEditor::onJobShiftChanged(db_id shiftId)
void JobPathEditor::onShiftError()
{
QMessageBox::warning(this,
tr("Empty Job"),
QMessageBox::warning(this, tr("Empty Job"),
tr("Before setting a shift you should add stops to this job"),
QMessageBox::Ok);
}
@ -686,7 +685,7 @@ void JobPathEditor::setEdited(bool val)
void JobPathEditor::setReadOnly(bool readOnly)
{
if(m_readOnly == readOnly)
if (m_readOnly == readOnly)
return;
m_readOnly = readOnly;
@ -697,12 +696,12 @@ void JobPathEditor::setReadOnly(bool readOnly)
ui->buttonBox->setVisible(!m_readOnly);
//If read-only hide 'AddHere' row (last one)
// If read-only hide 'AddHere' row (last one)
int size = stopModel->rowCount();
if(size > 0)
if (size > 0)
ui->stopsView->setRowHidden(size - 1, m_readOnly);
if(m_readOnly)
if (m_readOnly)
{
ui->stopsView->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
@ -716,7 +715,7 @@ void JobPathEditor::onSaveSheet()
{
const QLatin1String job_sheet_key = QLatin1String("job_sheet_dir");
OwningQPointer<QFileDialog> dlg = new QFileDialog(this, tr("Save Job Sheet"));
OwningQPointer<QFileDialog> dlg = new QFileDialog(this, tr("Save Job Sheet"));
dlg->setFileMode(QFileDialog::AnyFile);
dlg->setAcceptMode(QFileDialog::AcceptSave);
dlg->setDirectory(RecentDirStore::getDir(job_sheet_key, RecentDirStore::Documents));
@ -726,12 +725,12 @@ void JobPathEditor::onSaveSheet()
filters << FileFormats::tr(FileFormats::odtFormat);
dlg->setNameFilters(filters);
if(dlg->exec() != QDialog::Accepted || !dlg)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
QString fileName = dlg->selectedUrls().value(0).toLocalFile();
if(fileName.isEmpty())
if (fileName.isEmpty())
return;
RecentDirStore::setPath(job_sheet_key, fileName);
@ -743,14 +742,14 @@ void JobPathEditor::onSaveSheet()
utils::OpenFileInFolderDlg::askUser(tr("Job Sheet Saved"), fileName, this);
}
void JobPathEditor::onStopIndexClicked(const QModelIndex& index)
void JobPathEditor::onStopIndexClicked(const QModelIndex &index)
{
DEBUG_ENTRY;
if(m_readOnly)
if (m_readOnly)
return;
if(stopModel->isAddHere(index))
if (stopModel->isAddHere(index))
{
qDebug() << index << "AddHere";
@ -758,22 +757,22 @@ void JobPathEditor::onStopIndexClicked(const QModelIndex& index)
int row = index.row();
if(row > 0)
if (row > 0)
{
//idx - 1 is former Last Stop (now it became a normal Stop)
//idx is new Last Stop (former AddHere)
//idx + 1 is the new AddHere
// idx - 1 is former Last Stop (now it became a normal Stop)
// idx is new Last Stop (former AddHere)
// idx + 1 is the new AddHere
//Edit former Last Stop
// Edit former Last Stop
QModelIndex prev = stopModel->index(row - 1, 0);
ui->stopsView->setCurrentIndex(prev);
ui->stopsView->scrollTo(prev);
ui->stopsView->edit(prev);
//Tell editor to popup lines combo
//QAbstractItemView::edit doesn't let you pass additional arguments
//So we work around by emitting a signal
//See 'StopDelegate::createEditor()'
// Tell editor to popup lines combo
// QAbstractItemView::edit doesn't let you pass additional arguments
// So we work around by emitting a signal
// See 'StopDelegate::createEditor()'
emit delegate->popupEditorSegmentCombo();
}
else
@ -794,8 +793,8 @@ bool JobPathEditor::getCanSetJob() const
void JobPathEditor::closeStopEditor()
{
QModelIndex idx = ui->stopsView->currentIndex();
QWidget *ed = ui->stopsView->indexWidget(idx);
if(ed == nullptr)
QWidget *ed = ui->stopsView->indexWidget(idx);
if (ed == nullptr)
return;
emit delegate->commitData(ed);
emit delegate->closeEditor(ed);
@ -803,10 +802,10 @@ void JobPathEditor::closeStopEditor()
void JobPathEditor::closeEvent(QCloseEvent *e)
{
//TODO: prevent QDockWidget closing even if we ignore this event
if(isEdited())
// TODO: prevent QDockWidget closing even if we ignore this event
if (isEdited())
{
if(maybeSave())
if (maybeSave())
e->accept();
else
e->ignore();
@ -820,7 +819,7 @@ void JobPathEditor::closeEvent(QCloseEvent *e)
void JobPathEditor::selectStop(db_id stopId)
{
int row = stopModel->getStopRow(stopId);
if(row >= 0)
if (row >= 0)
{
QModelIndex idx = stopModel->index(row, 0);
ui->stopsView->setCurrentIndex(idx);

View File

@ -48,7 +48,7 @@ class JobPathEditor : public QDialog
public:
explicit JobPathEditor(QWidget *parent = nullptr);
~JobPathEditor()override;
~JobPathEditor() override;
bool setJob(db_id jobId);
bool createNewJob(db_id *out = nullptr);
@ -122,10 +122,10 @@ private:
int jobNumberTimerId;
//TODO: there are too many bools
// TODO: there are too many bools
bool isClear;
bool canSetJob; //TODO: better name
bool canSetJob; // TODO: better name
bool m_readOnly;
};

View File

@ -30,7 +30,7 @@ JobPassingsModel::JobPassingsModel(QObject *parent) :
QVariant JobPassingsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section)
{
@ -71,7 +71,7 @@ QVariant JobPassingsModel::data(const QModelIndex &idx, int role) const
if (!idx.isValid() || idx.row() >= m_data.size() || idx.column() >= NCols)
return QVariant();
const Entry& e = m_data.at(idx.row());
const Entry &e = m_data.at(idx.row());
switch (role)
{
case Qt::DisplayRole:
@ -97,7 +97,7 @@ QVariant JobPassingsModel::data(const QModelIndex &idx, int role) const
{
QFont f;
f.setPointSize(10);
if(idx.column() == JobNameCol)
if (idx.column() == JobNameCol)
f.setBold(true);
return f;
}

View File

@ -33,7 +33,8 @@ class JobPassingsModel : public QAbstractTableModel
Q_OBJECT
public:
enum Columns {
enum Columns
{
JobNameCol = 0,
ArrivalCol,
DepartureCol,
@ -60,7 +61,7 @@ public:
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
void setJobs(const QVector<Entry>& vec);
void setJobs(const QVector<Entry> &vec);
private:
QVector<Entry> m_data;

View File

@ -34,7 +34,7 @@ NextPrevRSJobsModel::NextPrevRSJobsModel(sqlite3pp::database &db, QObject *paren
QVariant NextPrevRSJobsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section)
{
@ -67,7 +67,7 @@ QVariant NextPrevRSJobsModel::data(const QModelIndex &idx, int role) const
if (!idx.isValid() || idx.row() >= m_data.size() || idx.column() >= NCols)
return QVariant();
const Item& item = m_data.at(idx.row());
const Item &item = m_data.at(idx.row());
switch (role)
{
@ -77,8 +77,8 @@ QVariant NextPrevRSJobsModel::data(const QModelIndex &idx, int role) const
{
case JobIdCol:
{
if(item.otherJobCat == JobCategory::NCategories)
return tr("Depot"); //Rollingstock item taken from/released to depot
if (item.otherJobCat == JobCategory::NCategories)
return tr("Depot"); // Rollingstock item taken from/released to depot
return JobCategoryName::jobName(item.otherJobId, item.otherJobCat);
}
case RsNameCol:
@ -90,9 +90,9 @@ QVariant NextPrevRSJobsModel::data(const QModelIndex &idx, int role) const
}
case Qt::FontRole:
{
if(idx.column() == JobIdCol && item.otherJobCat == JobCategory::NCategories)
if (idx.column() == JobIdCol && item.otherJobCat == JobCategory::NCategories)
{
//Rollingstock item taken from/released to depot, distinguish font from job names
// Rollingstock item taken from/released to depot, distinguish font from job names
QFont f;
f.setItalic(true);
return f;
@ -108,34 +108,38 @@ QVariant NextPrevRSJobsModel::data(const QModelIndex &idx, int role) const
void NextPrevRSJobsModel::refreshData()
{
//GROUP by rollingstock
QByteArray sql = "SELECT c.id, c.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, stops.%time0,"
" %min(s1.%time0), stops.id, s1.job_id AS job_id, j1.category"
" FROM stops"
" JOIN coupling c ON c.stop_id=stops.id"
" JOIN rs_list ON rs_list.id=c.rs_id"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
" LEFT JOIN jobs j1 ON j1.id=s1.job_id"
" WHERE stops.job_id=?1 AND c.operation=%rem AND c1.operation=%add AND s1.%time0 %gt stops.%time1"
" GROUP BY c.rs_id"
" UNION ALL "
"SELECT c.id, c.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, stops.%time0,"
" %max(s1.%time0), stops.id, NULL AS job_id, NULL"
" FROM stops"
" JOIN coupling c ON c.stop_id=stops.id"
" JOIN rs_list ON rs_list.id=c.rs_id"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
" WHERE stops.job_id=?1 AND c.operation=%rem AND c1.operation=%add"
" GROUP BY c.rs_id"
" HAVING s1.%time1 %lt stops.%time0"
" ORDER BY stops.%time0, job_id, rs_models.type, rs_models.name, rs_list.number"
" LIMIT 100";
// GROUP by rollingstock
QByteArray sql =
"SELECT c.id, c.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, "
"stops.%time0,"
" %min(s1.%time0), stops.id, s1.job_id AS job_id, j1.category"
" FROM stops"
" JOIN coupling c ON c.stop_id=stops.id"
" JOIN rs_list ON rs_list.id=c.rs_id"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
" LEFT JOIN jobs j1 ON j1.id=s1.job_id"
" WHERE stops.job_id=?1 AND c.operation=%rem AND c1.operation=%add AND s1.%time0 %gt "
"stops.%time1"
" GROUP BY c.rs_id"
" UNION ALL "
"SELECT c.id, c.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, "
"stops.%time0,"
" %max(s1.%time0), stops.id, NULL AS job_id, NULL"
" FROM stops"
" JOIN coupling c ON c.stop_id=stops.id"
" JOIN rs_list ON rs_list.id=c.rs_id"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN coupling c1 ON c1.rs_id=c.rs_id"
" LEFT JOIN stops s1 ON s1.id=c1.stop_id"
" WHERE stops.job_id=?1 AND c.operation=%rem AND c1.operation=%add"
" GROUP BY c.rs_id"
" HAVING s1.%time1 %lt stops.%time0"
" ORDER BY stops.%time0, job_id, rs_models.type, rs_models.name, rs_list.number"
" LIMIT 100";
if(m_mode == NextJobs)
if (m_mode == NextJobs)
{
sql.replace("%time0", "arrival");
sql.replace("%time1", "departure");
@ -148,7 +152,7 @@ void NextPrevRSJobsModel::refreshData()
}
else
{
//Invert all
// Invert all
sql.replace("%time0", "departure");
sql.replace("%time1", "arrival");
sql.replace("%min", "MAX");
@ -194,33 +198,29 @@ void NextPrevRSJobsModel::refreshData()
beginResetModel();
m_data.clear();
for(auto row : q)
for (auto row : q)
{
Item item;
item.couplingId = row.get<db_id>(0);
item.rsId = row.get<db_id>(1);
item.couplingId = row.get<db_id>(0);
item.rsId = row.get<db_id>(1);
int number = row.get<int>(2);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 3));
int number = row.get<int>(2);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 3));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 4);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 4));
RsType rsType = RsType(row.get<int>(5));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 4);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 4));
RsType rsType = RsType(row.get<int>(5));
item.rsName = rs_utils::formatNameRef(modelName,
modelNameLen,
number,
modelSuffix,
modelSuffixLen,
rsType);
item.rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
modelSuffixLen, rsType);
item.opTime = row.get<QTime>(6);
//Ignore column 7, which is used just for MIN/MAX
item.stopId = row.get<db_id>(8);
item.otherJobId = row.get<db_id>(9);
// Ignore column 7, which is used just for MIN/MAX
item.stopId = row.get<db_id>(8);
item.otherJobId = row.get<db_id>(9);
item.otherJobCat = JobCategory(row.get<int>(10));
if(row.column_type(10) == SQLITE_NULL)
if (row.column_type(10) == SQLITE_NULL)
item.otherJobCat = JobCategory::NCategories;
m_data.append(item);
@ -245,7 +245,7 @@ db_id NextPrevRSJobsModel::jobId() const
void NextPrevRSJobsModel::setJobId(db_id newJobId)
{
m_jobId = newJobId;
if(m_jobId)
if (m_jobId)
refreshData();
else
clearData();
@ -259,7 +259,7 @@ NextPrevRSJobsModel::Mode NextPrevRSJobsModel::mode() const
void NextPrevRSJobsModel::setMode(Mode newMode)
{
m_mode = newMode;
if(m_jobId)
if (m_jobId)
refreshData();
}

View File

@ -61,8 +61,8 @@ public:
{
db_id otherJobId = 0;
db_id couplingId = 0;
db_id rsId = 0;
db_id stopId = 0;
db_id rsId = 0;
db_id stopId = 0;
QString rsName;
QTime opTime;
@ -73,7 +73,8 @@ public:
NextPrevRSJobsModel(sqlite3pp::database &db, QObject *parent = nullptr);
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
// Basic functionality:
int rowCount(const QModelIndex &p = QModelIndex()) const override;
@ -93,7 +94,6 @@ public:
Item getItemAtRow(int row) const;
private:
sqlite3pp::database &mDb;
db_id m_jobId = 0;

View File

@ -39,16 +39,15 @@ RSCouplingInterface::RSCouplingInterface(database &db, QObject *parent) :
" coupling(stop_id,rs_id,operation)"
" VALUES(?, ?, ?)")
{
}
void RSCouplingInterface::loadCouplings(StopModel *model, db_id stopId, db_id jobId, QTime arr)
{
stopsModel = model;
m_stopId = stopId;
m_jobId = jobId;
arrival = arr;
m_stopId = stopId;
m_jobId = jobId;
arrival = arr;
coupled.clear();
uncoupled.clear();
@ -56,12 +55,12 @@ void RSCouplingInterface::loadCouplings(StopModel *model, db_id stopId, db_id jo
query q(mDb, "SELECT rs_id, operation FROM coupling WHERE stop_id=?");
q.bind(1, m_stopId);
for(auto rs : q)
for (auto rs : q)
{
db_id rsId = rs.get<db_id>(0);
RsOp op = RsOp(rs.get<int>(1));
RsOp op = RsOp(rs.get<int>(1));
if(op == RsOp::Coupled)
if (op == RsOp::Coupled)
coupled.append(rsId);
else
uncoupled.append(rsId);
@ -70,20 +69,21 @@ void RSCouplingInterface::loadCouplings(StopModel *model, db_id stopId, db_id jo
bool RSCouplingInterface::contains(db_id rsId, RsOp op) const
{
if(op == RsOp::Coupled)
if (op == RsOp::Coupled)
return coupled.contains(rsId);
else
return uncoupled.contains(rsId);
}
bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, bool checkTractionType)
bool RSCouplingInterface::coupleRS(db_id rsId, const QString &rsName, bool on,
bool checkTractionType)
{
stopsModel->startStopsEditing();
stopsModel->markRsToUpdate(rsId);
if(on)
if (on)
{
if(coupled.contains(rsId))
if (coupled.contains(rsId))
{
qWarning() << "Error already checked:" << rsId;
return true;
@ -99,78 +99,79 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
" AND stops.arrival<?");
q_RS_lastOp.bind(1, rsId);
q_RS_lastOp.bind(2, arrival);
int ret = q_RS_lastOp.step();
int ret = q_RS_lastOp.step();
bool isOccupied = false; //No Op means RS is turned off in a depot so it isn't occupied
if(ret == SQLITE_ROW)
bool isOccupied = false; // No Op means RS is turned off in a depot so it isn't occupied
if (ret == SQLITE_ROW)
{
auto row = q_RS_lastOp.getRows();
RsOp operation = RsOp(row.get<int>(1)); //Get last operation
jobId = row.get<db_id>(2);
isOccupied = (operation == RsOp::Coupled);
auto row = q_RS_lastOp.getRows();
RsOp operation = RsOp(row.get<int>(1)); // Get last operation
jobId = row.get<db_id>(2);
isOccupied = (operation == RsOp::Coupled);
}
if(isOccupied)
if (isOccupied)
{
if(jobId == m_jobId)
if (jobId == m_jobId)
{
qWarning() << "Error while adding coupling op. Stop:" << m_stopId
<< "Rs:" << rsId << "Already coupled by this job:" << m_jobId;
qWarning() << "Error while adding coupling op. Stop:" << m_stopId << "Rs:" << rsId
<< "Already coupled by this job:" << m_jobId;
QMessageBox::warning(qApp->activeWindow(),
tr("Error"),
QMessageBox::warning(qApp->activeWindow(), tr("Error"),
tr("Error while adding coupling operation.\n"
"Rollingstock %1 is already coupled by this job (%2)")
.arg(rsName).arg(m_jobId),
.arg(rsName)
.arg(m_jobId),
QMessageBox::Ok);
return false;
}
else
{
qWarning() << "Error while adding coupling op. Stop:" << m_stopId
<< "Rs:" << rsId << "Occupied by this job:" << jobId;
qWarning() << "Error while adding coupling op. Stop:" << m_stopId << "Rs:" << rsId
<< "Occupied by this job:" << jobId;
int but = QMessageBox::warning(qApp->activeWindow(),
tr("Error"),
tr("Error while adding coupling operation.\n"
"Rollingstock %1 is already coupled to another job (%2)\n"
"Do you still want to couple it?")
.arg(rsName).arg(jobId),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
int but =
QMessageBox::warning(qApp->activeWindow(), tr("Error"),
tr("Error while adding coupling operation.\n"
"Rollingstock %1 is already coupled to another job (%2)\n"
"Do you still want to couple it?")
.arg(rsName)
.arg(jobId),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if(but == QMessageBox::No)
return false; //Abort
if (but == QMessageBox::No)
return false; // Abort
}
}
if(checkTractionType && !stopsModel->isRailwayElectrifiedAfterStop(m_stopId))
if (checkTractionType && !stopsModel->isRailwayElectrifiedAfterStop(m_stopId))
{
//Query RS type
// Query RS type
query q_getRSType(mDb, "SELECT rs_models.type,rs_models.sub_type"
" FROM rs_list"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" WHERE rs_list.id=?");
q_getRSType.bind(1, rsId);
if(q_getRSType.step() != SQLITE_ROW)
if (q_getRSType.step() != SQLITE_ROW)
{
qWarning() << "RS seems to not exist, ID:" << rsId;
}
auto rs = q_getRSType.getRows();
RsType type = RsType(rs.get<int>(0));
auto rs = q_getRSType.getRows();
RsType type = RsType(rs.get<int>(0));
RsEngineSubType subType = RsEngineSubType(rs.get<int>(1));
if(type == RsType::Engine && subType == RsEngineSubType::Electric)
if (type == RsType::Engine && subType == RsEngineSubType::Electric)
{
int but = QMessageBox::warning(qApp->activeWindow(),
tr("Warning"),
tr("Rollingstock %1 is an Electric engine but the line is not electrified\n"
"This engine will not be albe to move a train.\n"
"Do you still want to couple it?")
.arg(rsName),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if(but == QMessageBox::No)
return false; //Cancel coupling operation
int but = QMessageBox::warning(
qApp->activeWindow(), tr("Warning"),
tr("Rollingstock %1 is an Electric engine but the line is not electrified\n"
"This engine will not be albe to move a train.\n"
"Do you still want to couple it?")
.arg(rsName),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
if (but == QMessageBox::No)
return false; // Cancel coupling operation
}
}
@ -180,44 +181,45 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
ret = q_addCoupling.execute();
q_addCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while adding coupling op. Stop:" << m_stopId
<< "Rs:" << rsId << "Op: Coupled " << "Ret:" << ret
<< mDb.error_msg();
qWarning() << "Error while adding coupling op. Stop:" << m_stopId << "Rs:" << rsId
<< "Op: Coupled "
<< "Ret:" << ret << mDb.error_msg();
return false;
}
coupled.append(rsId);
//Check if there is a next coupling operation in the same job
// Check if there is a next coupling operation in the same job
query q(mDb, "SELECT s2.id, s2.arrival, s2.station_id, stations.name"
" FROM coupling"
" JOIN stops s2 ON s2.id=coupling.stop_id"
" JOIN stops s1 ON s1.id=?"
" JOIN stations ON stations.id=s2.station_id"
" WHERE coupling.rs_id=? AND coupling.operation=? AND s1.job_id=s2.job_id AND s1.arrival < s2.arrival");
" WHERE coupling.rs_id=? AND coupling.operation=? AND s1.job_id=s2.job_id AND "
"s1.arrival < s2.arrival");
q.bind(1, m_stopId);
q.bind(2, rsId);
q.bind(3, int(RsOp::Coupled));
if(q.step() == SQLITE_ROW)
if (q.step() == SQLITE_ROW)
{
auto r = q.getRows();
db_id stopId = r.get<db_id>(0);
QTime arr = r.get<QTime>(1);
db_id stId = r.get<db_id>(2);
auto r = q.getRows();
db_id stopId = r.get<db_id>(0);
QTime arr = r.get<QTime>(1);
db_id stId = r.get<db_id>(2);
QString stName = r.get<QString>(3);
qDebug() << "Found coupling, RS:" << rsId << "Stop:" << stopId << "St:" << stId << arr;
int but = QMessageBox::question(qApp->activeWindow(),
tr("Delete coupling?"),
tr("You couple %1 also in a next stop in %2 at %3.\n"
"Do you want to remove the other coupling operation?")
.arg(rsName, stName, arr.toString("HH:mm")),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if(but == QMessageBox::Yes)
int but =
QMessageBox::question(qApp->activeWindow(), tr("Delete coupling?"),
tr("You couple %1 also in a next stop in %2 at %3.\n"
"Do you want to remove the other coupling operation?")
.arg(rsName, stName, arr.toString("HH:mm")),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (but == QMessageBox::Yes)
{
qDebug() << "Deleting coupling";
@ -226,11 +228,11 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
ret = q_deleteCoupling.execute();
q_deleteCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while deleting next coupling op. Stop:" << stopId
<< "Rs:" << rsId << "Op: Uncoupled " << "Ret:" << ret
<< mDb.error_msg();
<< "Rs:" << rsId << "Op: Uncoupled "
<< "Ret:" << ret << mDb.error_msg();
}
}
else
@ -242,7 +244,7 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
else
{
int row = coupled.indexOf(rsId);
if(row == -1)
if (row == -1)
return false;
q_deleteCoupling.bind(1, m_stopId);
@ -250,44 +252,46 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
int ret = q_deleteCoupling.execute();
q_deleteCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while deleting coupling op. Stop:" << m_stopId
<< "Rs:" << rsId << "Op: Coupled " << "Ret:" << ret
<< mDb.error_msg();
qWarning() << "Error while deleting coupling op. Stop:" << m_stopId << "Rs:" << rsId
<< "Op: Coupled "
<< "Ret:" << ret << mDb.error_msg();
return false;
}
coupled.removeAt(row);
//Check if there is a next uncoupling operation
// Check if there is a next uncoupling operation
query q(mDb, "SELECT s2.id, MIN(s2.arrival), s2.station_id, stations.name"
" FROM coupling"
" JOIN stops s2 ON s2.id=coupling.stop_id"
" JOIN stops s1 ON s1.id=?"
" JOIN stations ON stations.id=s2.station_id"
" WHERE coupling.rs_id=? AND coupling.operation=? AND s2.arrival > s1.arrival AND s2.job_id=s1.job_id");
" WHERE coupling.rs_id=? AND coupling.operation=? AND s2.arrival > s1.arrival "
"AND s2.job_id=s1.job_id");
q.bind(1, m_stopId);
q.bind(2, rsId);
q.bind(3, int(RsOp::Uncoupled));
if(q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
if (q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
{
auto r = q.getRows();
db_id stopId = r.get<db_id>(0);
QTime arr = r.get<QTime>(1);
db_id stId = r.get<db_id>(2);
auto r = q.getRows();
db_id stopId = r.get<db_id>(0);
QTime arr = r.get<QTime>(1);
db_id stId = r.get<db_id>(2);
QString stName = r.get<QString>(3);
qDebug() << "Found uncoupling, RS:" << rsId << "Stop:" << stopId << "St:" << stId << arr;
qDebug() << "Found uncoupling, RS:" << rsId << "Stop:" << stopId << "St:" << stId
<< arr;
int but = QMessageBox::question(qApp->activeWindow(),
tr("Delete uncoupling?"),
tr("You don't couple %1 anymore.\n"
"Do you want to remove also the uncoupling operation in %2 at %3?")
.arg(rsName, stName, arr.toString("HH:mm")),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if(but == QMessageBox::Yes)
int but = QMessageBox::question(
qApp->activeWindow(), tr("Delete uncoupling?"),
tr("You don't couple %1 anymore.\n"
"Do you want to remove also the uncoupling operation in %2 at %3?")
.arg(rsName, stName, arr.toString("HH:mm")),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (but == QMessageBox::Yes)
{
qDebug() << "Deleting coupling";
@ -296,11 +300,11 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
ret = q_deleteCoupling.execute();
q_deleteCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while deleting next uncoupling op. Stop:" << stopId
<< "Rs:" << rsId << "Op: Uncoupled " << "Ret:" << ret
<< mDb.error_msg();
<< "Rs:" << rsId << "Op: Uncoupled "
<< "Ret:" << ret << mDb.error_msg();
}
}
else
@ -313,14 +317,14 @@ bool RSCouplingInterface::coupleRS(db_id rsId, const QString& rsName, bool on, b
return true;
}
bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString& rsName, bool on)
bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString &rsName, bool on)
{
stopsModel->startStopsEditing();
stopsModel->markRsToUpdate(rsId);
if(on)
if (on)
{
if(uncoupled.contains(rsId))
if (uncoupled.contains(rsId))
{
qWarning() << "Error already checked:" << rsId;
return true;
@ -332,44 +336,46 @@ bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString& rsName, bool on)
int ret = q_addCoupling.execute();
q_addCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while adding coupling op. Stop:" << m_stopId
<< "Rs:" << rsId << "Op: Uncoupled " << "Ret:" << ret
<< mDb.error_msg();
qWarning() << "Error while adding coupling op. Stop:" << m_stopId << "Rs:" << rsId
<< "Op: Uncoupled "
<< "Ret:" << ret << mDb.error_msg();
return false;
}
uncoupled.append(rsId);
//Check if there is a next uncoupling operation
// Check if there is a next uncoupling operation
query q(mDb, "SELECT s2.id, MIN(s2.arrival), s2.station_id, stations.name"
" FROM coupling"
" JOIN stops s2 ON s2.id=coupling.stop_id"
" JOIN stops s1 ON s1.id=?"
" JOIN stations ON stations.id=s2.station_id"
" WHERE coupling.rs_id=? AND coupling.operation=? AND s2.arrival > s1.arrival AND s2.job_id=s1.job_id");
" WHERE coupling.rs_id=? AND coupling.operation=? AND s2.arrival > s1.arrival "
"AND s2.job_id=s1.job_id");
q.bind(1, m_stopId);
q.bind(2, rsId);
q.bind(3, int(RsOp::Uncoupled));
if(q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
if (q.step() == SQLITE_ROW && q.getRows().column_type(0) != SQLITE_NULL)
{
auto r = q.getRows();
db_id stopId = r.get<db_id>(0);
QTime arr = r.get<QTime>(1);
db_id stId = r.get<db_id>(2);
auto r = q.getRows();
db_id stopId = r.get<db_id>(0);
QTime arr = r.get<QTime>(1);
db_id stId = r.get<db_id>(2);
QString stName = r.get<QString>(3);
qDebug() << "Found uncoupling, RS:" << rsId << "Stop:" << stopId << "St:" << stId << arr;
qDebug() << "Found uncoupling, RS:" << rsId << "Stop:" << stopId << "St:" << stId
<< arr;
int but = QMessageBox::question(qApp->activeWindow(),
tr("Delete uncoupling?"),
tr("You uncouple %1 also in %2 at %3.\n"
"Do you want to remove the other uncoupling operation?")
.arg(rsName, stName, arr.toString("HH:mm")),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if(but == QMessageBox::Yes)
int but =
QMessageBox::question(qApp->activeWindow(), tr("Delete uncoupling?"),
tr("You uncouple %1 also in %2 at %3.\n"
"Do you want to remove the other uncoupling operation?")
.arg(rsName, stName, arr.toString("HH:mm")),
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (but == QMessageBox::Yes)
{
qDebug() << "Deleting coupling";
@ -378,11 +384,11 @@ bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString& rsName, bool on)
ret = q_deleteCoupling.execute();
q_deleteCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while deleting next uncoupling op. Stop:" << stopId
<< "Rs:" << rsId << "Op: Uncoupled " << "Ret:" << ret
<< mDb.error_msg();
<< "Rs:" << rsId << "Op: Uncoupled "
<< "Ret:" << ret << mDb.error_msg();
}
}
else
@ -394,7 +400,7 @@ bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString& rsName, bool on)
else
{
int row = uncoupled.indexOf(rsId);
if(row == -1)
if (row == -1)
return false;
q_deleteCoupling.bind(1, m_stopId);
@ -402,11 +408,11 @@ bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString& rsName, bool on)
int ret = q_deleteCoupling.execute();
q_deleteCoupling.reset();
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
{
qWarning() << "Error while deleting coupling op. Stop:" << m_stopId
<< "Rs:" << rsId << "Op: Uncoupled " << "Ret:" << ret
<< mDb.error_msg();
qWarning() << "Error while deleting coupling op. Stop:" << m_stopId << "Rs:" << rsId
<< "Op: Uncoupled "
<< "Ret:" << ret << mDb.error_msg();
return false;
}
@ -426,48 +432,45 @@ int RSCouplingInterface::importRSFromJob(db_id otherStopId)
" WHERE coupling.stop_id=? AND coupling.operation=0");
q_getUncoupled.bind(1, otherStopId);
int count = 0;
int count = 0;
bool lineElectrified = stopsModel->isRailwayElectrifiedAfterStop(m_stopId);
QElapsedTimer timer;
timer.start();
for(auto rs : q_getUncoupled)
for (auto rs : q_getUncoupled)
{
db_id rsId = rs.get<db_id>(0);
db_id rsId = rs.get<db_id>(0);
int number = rs.get<int>(1);
int number = rs.get<int>(1);
int modelNameLen = sqlite3_column_bytes(q_getUncoupled.stmt(), 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(q_getUncoupled.stmt(), 2));
const char *modelName =
reinterpret_cast<char const *>(sqlite3_column_text(q_getUncoupled.stmt(), 2));
int modelSuffixLen = sqlite3_column_bytes(q_getUncoupled.stmt(), 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(q_getUncoupled.stmt(), 3));
RsType rsType = RsType(rs.get<int>(4));
const char *modelSuffix =
reinterpret_cast<char const *>(sqlite3_column_text(q_getUncoupled.stmt(), 3));
RsType rsType = RsType(rs.get<int>(4));
QString rsName = rs_utils::formatNameRef(modelName,
modelNameLen,
number,
modelSuffix,
modelSuffixLen,
rsType);
QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
modelSuffixLen, rsType);
//TODO: optimize work
if(coupleRS(rsId, rsName, true, !lineElectrified))
// TODO: optimize work
if (coupleRS(rsId, rsName, true, !lineElectrified))
count++;
if(timer.elapsed() > 10000)
if (timer.elapsed() > 10000)
{
//After 10 seconds, give opportunity to stop
// After 10 seconds, give opportunity to stop
int ret = QMessageBox::question(
qApp->activeWindow(),
tr("Continue Importation?"),
tr("Rollingstock importation is taking more time than expected.\n"
"Do you want to continue?"));
qApp->activeWindow(), tr("Continue Importation?"),
tr("Rollingstock importation is taking more time than expected.\n"
"Do you want to continue?"));
if(ret == QMessageBox::No)
return count; //Abort here
if (ret == QMessageBox::No)
return count; // Abort here
timer.restart(); //Count again
timer.restart(); // Count again
}
}
@ -487,13 +490,14 @@ bool RSCouplingInterface::hasEngineAfterStop(bool *isElectricOnNonElectrifiedLin
" LIMIT 1");
q_hasEngine.bind(1, m_jobId);
q_hasEngine.bind(2, arrival);
if(q_hasEngine.step() != SQLITE_ROW)
return false; //No engine
if (q_hasEngine.step() != SQLITE_ROW)
return false; // No engine
if(isElectricOnNonElectrifiedLine)
if (isElectricOnNonElectrifiedLine)
{
RsEngineSubType subType = RsEngineSubType(q_hasEngine.getRows().get<int>(1));
*isElectricOnNonElectrifiedLine = (subType == RsEngineSubType::Electric) && (!isRailwayElectrified());
*isElectricOnNonElectrifiedLine =
(subType == RsEngineSubType::Electric) && (!isRailwayElectrified());
}
return true;
}

View File

@ -35,18 +35,18 @@ QVariant RSListOnDemandModel::data(const QModelIndex &idx, int role) const
if (!idx.isValid() || row >= curItemCount || idx.column() >= NCols)
return QVariant();
//qDebug() << "Data:" << idx.row();
// qDebug() << "Data:" << idx.row();
if(row < cacheFirstRow || row >= cacheFirstRow + cache.size())
if (row < cacheFirstRow || row >= cacheFirstRow + cache.size())
{
//Fetch above or below current cache
// Fetch above or below current cache
const_cast<RSListOnDemandModel *>(this)->fetchRow(row);
//Temporarily return null
// Temporarily return null
return role == Qt::DisplayRole ? QVariant("...") : QVariant();
}
const RSItem& item = cache.at(row - cacheFirstRow);
const RSItem &item = cache.at(row - cacheFirstRow);
switch (role)
{
@ -63,9 +63,9 @@ QVariant RSListOnDemandModel::data(const QModelIndex &idx, int role) const
}
case Qt::FontRole:
{
if(item.type == RsType::Engine)
if (item.type == RsType::Engine)
{
//Engines in bold
// Engines in bold
QFont f;
f.setBold(true);
return f;

View File

@ -38,9 +38,13 @@ class RSListOnDemandModel : public IPagedItemModelImpl<RSListOnDemandModel, RSLi
Q_OBJECT
public:
enum { BatchSize = 50 };
enum
{
BatchSize = 50
};
enum Columns {
enum Columns
{
Name = 0,
NCols
};

View File

@ -25,17 +25,12 @@
#include <QBrush>
RSProxyModel::RSProxyModel(RSCouplingInterface *mgr,
RsOp o,
RsType type,
QObject *parent) :
QAbstractListModel (parent),
RSProxyModel::RSProxyModel(RSCouplingInterface *mgr, RsOp o, RsType type, QObject *parent) :
QAbstractListModel(parent),
couplingMgr(mgr),
op(o),
targetType(type)
{
}
int RSProxyModel::rowCount(const QModelIndex &parent) const
@ -45,10 +40,10 @@ int RSProxyModel::rowCount(const QModelIndex &parent) const
QVariant RSProxyModel::data(const QModelIndex &idx, int role) const
{
if(!idx.isValid() || idx.column() > 0 || idx.row() >= m_data.size())
if (!idx.isValid() || idx.column() > 0 || idx.row() >= m_data.size())
return QVariant();
const RsItem& item = m_data.at(idx.row());
const RsItem &item = m_data.at(idx.row());
switch (role)
{
@ -58,92 +53,104 @@ QVariant RSProxyModel::data(const QModelIndex &idx, int role) const
{
return couplingMgr->contains(item.rsId, op) ? Qt::Checked : Qt::Unchecked;
}
case Qt::BackgroundRole: //NOTE SYNC: RSCoupleDialog
case Qt::BackgroundRole: // NOTE SYNC: RSCoupleDialog
{
if(item.flag == ErrNotCoupledBefore || item.flag == ErrAlreadyCoupled)
if (item.flag == ErrNotCoupledBefore || item.flag == ErrAlreadyCoupled)
{
//Error: already coupled or already uncoupled or not coupled at all before this stop
return QBrush(qRgb(255, 86, 255)); //Solid light magenta #FF56FF
// Error: already coupled or already uncoupled or not coupled at all before this stop
return QBrush(qRgb(255, 86, 255)); // Solid light magenta #FF56FF
}
if(item.flag == WrongStation)
if (item.flag == WrongStation)
{
//Error: RS is not in this station
return QBrush(qRgb(255, 61, 67)); //Solid light red #FF3d43
// Error: RS is not in this station
return QBrush(qRgb(255, 61, 67)); // Solid light red #FF3d43
}
if(targetType == RsType::Engine && item.engineType == RsEngineSubType::Electric && !couplingMgr->isRailwayElectrified())
if (targetType == RsType::Engine && item.engineType == RsEngineSubType::Electric
&& !couplingMgr->isRailwayElectrified())
{
//Warn Electric traction not possible
return QBrush(qRgb(0, 0, 255), Qt::FDiagPattern); //Blue
// Warn Electric traction not possible
return QBrush(qRgb(0, 0, 255), Qt::FDiagPattern); // Blue
}
if(item.flag == FirstUseOfRS)
if (item.flag == FirstUseOfRS)
{
return QBrush(qRgb(0, 255, 255)); //Cyan
return QBrush(qRgb(0, 255, 255)); // Cyan
}
if(item.flag == UnusedRS)
if (item.flag == UnusedRS)
{
return QBrush(qRgb(0, 255, 0)); //Green
return QBrush(qRgb(0, 255, 0)); // Green
}
break;
}
case Qt::ToolTipRole:
{
if(item.flag == ErrNotCoupledBefore)
if (item.flag == ErrNotCoupledBefore)
{
//Error
return tr("Rollingstock <b>%1</b> cannot be uncoupled here because it wasn't coupled to this job before this stop "
// Error
return tr("Rollingstock <b>%1</b> cannot be uncoupled here because it wasn't coupled "
"to this job before this stop "
"or because it was already uncoupled before this stop.<br>"
"Please remove the tick").arg(item.rsName);
"Please remove the tick")
.arg(item.rsName);
}
if(item.flag == ErrAlreadyCoupled)
if (item.flag == ErrAlreadyCoupled)
{
//Error
if(item.jobId == couplingMgr->getJobId())
// Error
if (item.jobId == couplingMgr->getJobId())
{
return tr("Rollingstock <b>%1</b> cannot be coupled here because it was already coupled to this job before this stop<br>"
"Please remove the tick").arg(item.rsName);
}else{
return tr("Rollingstock <b>%1</b> cannot be coupled here because it was already coupled before this stop<br>"
return tr("Rollingstock <b>%1</b> cannot be coupled here because it was already "
"coupled to this job before this stop<br>"
"Please remove the tick")
.arg(item.rsName);
}
else
{
return tr("Rollingstock <b>%1</b> cannot be coupled here because it was already "
"coupled before this stop<br>"
"to job <b>%2<b/><br>"
"Please remove the tick")
.arg(item.rsName,
JobCategoryName::jobName(item.jobId, item.jobCat));
.arg(item.rsName, JobCategoryName::jobName(item.jobId, item.jobCat));
}
}
if(item.flag == WrongStation)
if (item.flag == WrongStation)
{
//Error
return tr("Rollingstock <b>%1</b> cannot be coupled here because it is not in this station.<br>"
"Please remove the tick").arg(item.rsName);
// Error
return tr("Rollingstock <b>%1</b> cannot be coupled here because it is not in this "
"station.<br>"
"Please remove the tick")
.arg(item.rsName);
}
if(targetType == RsType::Engine && item.engineType == RsEngineSubType::Electric && !couplingMgr->isRailwayElectrified())
if (targetType == RsType::Engine && item.engineType == RsEngineSubType::Electric
&& !couplingMgr->isRailwayElectrified())
{
//Warn Electric traction not possible
return tr("Engine <b>%1</b> is electric but the line is not electrified!").arg(item.rsName);
// Warn Electric traction not possible
return tr("Engine <b>%1</b> is electric but the line is not electrified!")
.arg(item.rsName);
}
if(item.flag == HasNextOperation)
if (item.flag == HasNextOperation)
{
return tr("Rollingstock <b>%1</b> is coupled in this station also by <b>%2</b> at <b>%3</b>.")
.arg(item.rsName,
JobCategoryName::jobName(item.jobId, item.jobCat),
item.time.toString("HH:mm"));
return tr("Rollingstock <b>%1</b> is coupled in this station also by <b>%2</b> at "
"<b>%3</b>.")
.arg(item.rsName, JobCategoryName::jobName(item.jobId, item.jobCat),
item.time.toString("HH:mm"));
}
if(item.flag == LastOperation)
if (item.flag == LastOperation)
{
return tr("Rollingstock <b>%1</b> was left in this station by <b>%2</b> at <b>%3</b>.")
.arg(item.rsName,
JobCategoryName::jobName(item.jobId, item.jobCat),
item.time.toString("HH:mm"));
.arg(item.rsName, JobCategoryName::jobName(item.jobId, item.jobCat),
item.time.toString("HH:mm"));
}
if(item.flag == FirstUseOfRS)
if (item.flag == FirstUseOfRS)
{
if(op == RsOp::Coupled && couplingMgr->contains(item.rsId, RsOp::Coupled))
if (op == RsOp::Coupled && couplingMgr->contains(item.rsId, RsOp::Coupled))
return tr("This is the first use of this rollingstock <b>%1</b>").arg(item.rsName);
return tr("This would be the first use of this rollingstock <b>%1</b>").arg(item.rsName);
return tr("This would be the first use of this rollingstock <b>%1</b>")
.arg(item.rsName);
}
if(item.flag == UnusedRS)
if (item.flag == UnusedRS)
{
return tr("Rollingstock <b>%1</b> is never used in this session. You can couple it for the first time from any one station")
.arg(item.rsName);
return tr("Rollingstock <b>%1</b> is never used in this session. You can couple it for "
"the first time from any one station")
.arg(item.rsName);
}
break;
}
@ -154,21 +161,23 @@ QVariant RSProxyModel::data(const QModelIndex &idx, int role) const
bool RSProxyModel::setData(const QModelIndex &idx, const QVariant &value, int role)
{
if(role != Qt::CheckStateRole || !idx.isValid() || idx.column() > 0 || idx.row() >= m_data.size())
if (role != Qt::CheckStateRole || !idx.isValid() || idx.column() > 0
|| idx.row() >= m_data.size())
return false;
Qt::CheckState state = value.value<Qt::CheckState>();
const RsItem& item = m_data.at(idx.row());
const RsItem &item = m_data.at(idx.row());
bool ret = false;
bool ret = false;
if(op == RsOp::Coupled) //Check traction type only if we are dealing with RsType::Engine
ret = couplingMgr->coupleRS(item.rsId, item.rsName, state == Qt::Checked, targetType == RsType::Engine);
if (op == RsOp::Coupled) // Check traction type only if we are dealing with RsType::Engine
ret = couplingMgr->coupleRS(item.rsId, item.rsName, state == Qt::Checked,
targetType == RsType::Engine);
else
ret = couplingMgr->uncoupleRS(item.rsId, item.rsName, state == Qt::Checked);
if(ret)
if (ret)
emit dataChanged(idx, idx);
return ret;
@ -176,11 +185,9 @@ bool RSProxyModel::setData(const QModelIndex &idx, const QVariant &value, int ro
Qt::ItemFlags RSProxyModel::flags(const QModelIndex &index) const
{
if(index.isValid())
return Qt::ItemIsEnabled
| Qt::ItemNeverHasChildren
| Qt::ItemIsSelectable
| Qt::ItemIsUserCheckable;
if (index.isValid())
return Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable
| Qt::ItemIsUserCheckable;
return Qt::NoItemFlags;
}

View File

@ -33,11 +33,7 @@ class RSProxyModel : public QAbstractListModel
Q_OBJECT
public:
RSProxyModel(RSCouplingInterface *mgr,
RsOp o,
RsType type,
QObject *parent = nullptr);
RSProxyModel(RSCouplingInterface *mgr, RsOp o, RsType type, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
@ -48,28 +44,28 @@ public:
enum RSItemFlg
{
NoFlag = 0,
WrongStation = 1,
LastOperation = 2,
HasNextOperation = 3,
FirstUseOfRS = 4,
UnusedRS = 5,
NoFlag = 0,
WrongStation = 1,
LastOperation = 2,
HasNextOperation = 3,
FirstUseOfRS = 4,
UnusedRS = 5,
ErrNotCoupledBefore = 6,
ErrAlreadyCoupled = 7
ErrAlreadyCoupled = 7
};
struct RsItem
{
db_id rsId;
db_id jobId; //Can be next job or previous job
db_id jobId; // Can be next job or previous job
QString rsName;
int flag;
QTime time; //Can be next or previous operation time
QTime time; // Can be next or previous operation time
JobCategory jobCat;
RsEngineSubType engineType;
};
void loadData(const QVector<RsItem>& items);
void loadData(const QVector<RsItem> &items);
private:
QVector<RsItem> m_data;

View File

@ -26,7 +26,6 @@ using namespace sqlite3pp;
#include "utils/rs_utils.h"
StopCouplingModel::StopCouplingModel(sqlite3pp::database &db, QObject *parent) :
RSListOnDemandModel(db, parent),
m_stopId(0),
@ -46,65 +45,65 @@ qint64 StopCouplingModel::recalcTotalItemCount()
void StopCouplingModel::setStop(db_id stopId, RsOp op)
{
m_stopId = stopId;
m_stopId = stopId;
m_operation = op;
refreshData(true);
}
void StopCouplingModel::internalFetch(int first, int /*sortCol*/, int /*valRow*/, const QVariant& /*val*/)
void StopCouplingModel::internalFetch(int first, int /*sortCol*/, int /*valRow*/,
const QVariant & /*val*/)
{
query q(mDb);
int offset = first + curPage * ItemsPerPage;
QByteArray sql = "SELECT coupling.rs_id,rs_list.number,rs_models.name,rs_models.suffix,rs_models.type"
" FROM coupling"
" JOIN rs_list ON rs_list.id=coupling.rs_id"
" LEFT JOIN rs_models ON rs_models.id=rs_list.model_id"
" WHERE coupling.stop_id=?2 AND coupling.operation=?3"
" ORDER BY rs_models.type,rs_models.name,rs_list.number,rs_models.suffix";
QByteArray sql =
"SELECT coupling.rs_id,rs_list.number,rs_models.name,rs_models.suffix,rs_models.type"
" FROM coupling"
" JOIN rs_list ON rs_list.id=coupling.rs_id"
" LEFT JOIN rs_models ON rs_models.id=rs_list.model_id"
" WHERE coupling.stop_id=?2 AND coupling.operation=?3"
" ORDER BY rs_models.type,rs_models.name,rs_list.number,rs_models.suffix";
sql += " LIMIT ?1";
if(offset)
if (offset)
sql += " OFFSET ?2";
q.prepare(sql);
q.bind(1, BatchSize);
q.bind(2, m_stopId);
q.bind(3, int(m_operation));
if(offset)
if (offset)
q.bind(2, offset);
QVector<RSItem> vec(BatchSize);
auto it = q.begin();
auto it = q.begin();
const auto end = q.end();
int i = 0;
int i = 0;
for(; it != end; ++it)
for (; it != end; ++it)
{
auto r = *it;
RSItem &item = vec[i];
item.rsId = r.get<db_id>(0);
auto r = *it;
RSItem &item = vec[i];
item.rsId = r.get<db_id>(0);
int number = r.get<int>(1);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 2));
int number = r.get<int>(1);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 2);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 2));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 3));
item.type = RsType(sqlite3_column_int(q.stmt(), 4));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 3));
item.type = RsType(sqlite3_column_int(q.stmt(), 4));
item.name = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
item.type);
item.name = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
modelSuffixLen, item.type);
i++;
}
if(i < BatchSize)
if (i < BatchSize)
vec.remove(i, BatchSize - i);
postResult(vec, first);

View File

@ -22,7 +22,6 @@
#include "rslistondemandmodel.h"
class StopCouplingModel : public RSListOnDemandModel
{
Q_OBJECT

File diff suppressed because it is too large Load Diff

View File

@ -36,9 +36,9 @@ struct StopItem
{
struct Gate
{
db_id gateConnId = 0;
db_id gateId = 0;
int gateTrackNum = -1;
db_id gateConnId = 0;
db_id gateId = 0;
int gateTrackNum = -1;
utils::Side stationTrackSide = utils::Side::NSides;
};
@ -48,7 +48,7 @@ struct StopItem
db_id segmentId = 0;
int inTrackNum = -1;
int outTrackNum = -1;
bool reversed = false;
bool reversed = false;
};
db_id stopId = 0;
@ -62,12 +62,11 @@ struct StopItem
QTime arrival;
QTime departure;
int addHere = 0;
int addHere = 0;
StopType type = StopType::Normal;
};
/*!
* \brief The StopModel class
*
@ -80,7 +79,7 @@ class StopModel : public QAbstractListModel
{
Q_OBJECT
public:
StopModel(sqlite3pp::database& db, QObject *parent = nullptr);
StopModel(sqlite3pp::database &db, QObject *parent = nullptr);
// QAbstractListModel
QVariant data(const QModelIndex &index, int role) const override;
@ -105,7 +104,7 @@ public:
// Editing
void addStop();
void removeStop(const QModelIndex& idx);
void removeStop(const QModelIndex &idx);
void removeLastIfEmpty();
void uncoupleStillCoupledAtLastStop();
@ -118,7 +117,8 @@ public:
db_id getNewShiftId() const;
// Setters
void setStopInfo(const QModelIndex& idx, StopItem newStop, StopItem::Segment prevSeg, bool avoidTimeRecalc = false);
void setStopInfo(const QModelIndex &idx, StopItem newStop, StopItem::Segment prevSeg,
bool avoidTimeRecalc = false);
bool setStopTypeRange(int firstRow, int lastRow, StopType type);
@ -129,7 +129,7 @@ public:
// Convinience
int getStopRow(db_id stopId) const;
bool isAddHere(const QModelIndex& idx);
bool isAddHere(const QModelIndex &idx);
std::pair<QTime, QTime> getFirstLastTimes() const;
@ -139,10 +139,18 @@ public:
bool isRailwayElectrifiedAfterStop(db_id stopId) const;
bool isRailwayElectrifiedAfterRow(int row) const;
inline StopItem getItemAt(int row) const { return stops.at(row); }
inline StopType getItemTypeAt(int row) const { return stops.at(row).type; }
inline db_id getItemStationAt(int row) const { return stops.at(row).stationId; }
inline StopItem getItemAt(int row) const
{
return stops.at(row);
}
inline StopType getItemTypeAt(int row) const
{
return stops.at(row).type;
}
inline db_id getItemStationAt(int row) const
{
return stops.at(row).stationId;
}
#ifdef ENABLE_AUTO_TIME_RECALC
void rebaseTimesToSpeed(int firstIdx, QTime firstArr, QTime firstDep);
@ -150,8 +158,7 @@ public:
bool trySelectTrackForStop(StopItem &item);
bool trySetTrackConnections(StopItem &item, db_id trackId,
QString *outErr);
bool trySetTrackConnections(StopItem &item, db_id trackId, QString *outErr);
bool trySelectNextSegment(StopItem &item, db_id segmentId, int suggestedOutGateTrk,
db_id nextStationId, db_id &out_gateId, db_id &out_suggestedTrackId);
@ -162,7 +169,7 @@ signals:
void categoryChanged(int newCat);
void jobIdChanged(db_id jobId);
void jobShiftChanged(db_id shiftId);
void errorSetShiftWithoutStops(); //TODO: find better way to show errors
void errorSetShiftWithoutStops(); // TODO: find better way to show errors
public slots:
void setCategory(int value);
@ -182,25 +189,29 @@ private:
db_id createStop(db_id jobId, const QTime &arr, const QTime &dep, StopType type);
void deleteStop(db_id stopId);
bool updateCurrentInGate(StopItem& curStop, const StopItem::Segment& prevSeg);
bool updateStopTime(StopItem& item, int row, bool propagate, const QTime &oldArr, const QTime &oldDep);
bool updateCurrentInGate(StopItem &curStop, const StopItem::Segment &prevSeg);
bool updateStopTime(StopItem &item, int row, bool propagate, const QTime &oldArr,
const QTime &oldDep);
int calcTravelTime(db_id segmentId);
int defaultStopTimeSec();
void shiftStopsBy24hoursFrom(const QTime& startTime);
void shiftStopsBy24hoursFrom(const QTime &startTime);
friend class RSCouplingInterface;
bool startInfoEditing();
bool startStopsEditing();
bool endStopsEditing();
inline void markRsToUpdate(db_id rsId) { rsToUpdate.insert(rsId); }
inline void markRsToUpdate(db_id rsId)
{
rsToUpdate.insert(rsId);
}
private:
//To simulate acceleration/braking we add 4 km to distance
// To simulate acceleration/braking we add 4 km to distance
static constexpr double accelerationDistMeters = 4000.0;
sqlite3pp::database& mDb;
sqlite3pp::database &mDb;
QVector<StopItem> stops;
@ -218,8 +229,8 @@ private:
enum EditState
{
NotEditing = 0,
InfoEditing = 1,
NotEditing = 0,
InfoEditing = 1,
StopsEditing = 2
};

View File

@ -28,7 +28,7 @@ using namespace sqlite3pp;
#include <QDebug>
TrainAssetModel::TrainAssetModel(database& db, QObject *parent) :
TrainAssetModel::TrainAssetModel(database &db, QObject *parent) :
RSListOnDemandModel(db, parent),
m_jobId(0),
m_mode(BeforeStop)
@ -45,30 +45,33 @@ qint64 TrainAssetModel::recalcTotalItemCount()
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=1)");
q.bind(1, m_jobId);
//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(m_mode == AfterStop)
// 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 (m_mode == AfterStop)
q.bind(2, m_arrival.addSecs(60));
else
q.bind(2, m_arrival);
int ret = q.step();
if(ret != SQLITE_ROW)
if (ret != SQLITE_ROW)
qWarning() << "TrainAssetModel: " << mDb.error_msg() << mDb.error_code();
const qint64 count = q.getRows().get<int>(0);
return count;
}
void TrainAssetModel::internalFetch(int first, int sortCol, int /*valRow*/, const QVariant &/*val*/)
void TrainAssetModel::internalFetch(int first, int sortCol, int /*valRow*/,
const QVariant & /*val*/)
{
query q(mDb);
int offset = first + curPage * ItemsPerPage;
//const char *whereCol;
// const char *whereCol;
QByteArray sql = "SELECT sub.rs_id,sub.number,sub.name,sub.suffix,sub.type FROM("
"SELECT coupling.rs_id,rs_list.number,rs_models.name,rs_models.suffix,rs_models.type,MAX(stops.arrival)"
"SELECT "
"coupling.rs_id,rs_list.number,rs_models.name,rs_models.suffix,rs_models.type,"
"MAX(stops.arrival)"
" FROM stops"
" JOIN coupling ON coupling.stop_id=stops.id"
" JOIN rs_list ON rs_list.id=rs_id"
@ -103,49 +106,47 @@ void TrainAssetModel::internalFetch(int first, int sortCol, int /*valRow*/, cons
// sql += " DESC";
sql += " LIMIT ?1";
if(offset)
if (offset)
sql += " OFFSET ?2";
q.prepare(sql);
q.bind(1, BatchSize);
q.bind(3, m_jobId);
//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(m_mode == AfterStop)
// 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 (m_mode == AfterStop)
q.bind(4, m_arrival.addSecs(60));
else
q.bind(4, m_arrival);
if(offset)
if (offset)
q.bind(2, offset);
QVector<RSItem> vec(BatchSize);
auto it = q.begin();
auto it = q.begin();
const auto end = q.end();
int i = 0;
for(; it != end; ++it)
int i = 0;
for (; it != end; ++it)
{
auto r = *it;
RSItem &item = vec[i];
item.rsId = r.get<db_id>(0);
auto r = *it;
RSItem &item = vec[i];
item.rsId = r.get<db_id>(0);
int number = r.get<int>(1);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 2));
int number = r.get<int>(1);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 2);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 2));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 3));
item.type = RsType(sqlite3_column_int(q.stmt(), 4));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 3));
item.type = RsType(sqlite3_column_int(q.stmt(), 4));
item.name = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
item.type);
item.name = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
modelSuffixLen, item.type);
i++;
}
if(i < BatchSize)
if (i < BatchSize)
vec.remove(i, BatchSize - i);
postResult(vec, first);
@ -153,9 +154,9 @@ void TrainAssetModel::internalFetch(int first, int sortCol, int /*valRow*/, cons
void TrainAssetModel::setStop(db_id jobId, QTime arrival, Mode mode)
{
m_jobId = jobId;
m_jobId = jobId;
m_arrival = arrival;
m_mode = mode;
m_mode = mode;
refreshData(true);
}

View File

@ -27,12 +27,13 @@ class TrainAssetModel : public RSListOnDemandModel
{
Q_OBJECT
public:
enum Mode {
enum Mode
{
BeforeStop,
AfterStop
};
TrainAssetModel(sqlite3pp::database& db, QObject *parent = nullptr);
TrainAssetModel(sqlite3pp::database &db, QObject *parent = nullptr);
// TrainAssetModel
void setStop(db_id jobId, QTime arrival, Mode mode);

View File

@ -36,15 +36,15 @@
#include "app/session.h"
RSCoupleDialog::RSCoupleDialog(RSCouplingInterface *mgr, RsOp o, QWidget *parent) :
QDialog (parent),
QDialog(parent),
couplingMgr(mgr),
legend(nullptr),
m_showLegend(false),
op(o)
{
engModel = new RSProxyModel(couplingMgr, op, RsType::Engine, this);
coachModel = new RSProxyModel(couplingMgr, op, RsType::Coach, this);
freightModel = new RSProxyModel(couplingMgr, op, RsType::FreightWagon, this);
engModel = new RSProxyModel(couplingMgr, op, RsType::Engine, this);
coachModel = new RSProxyModel(couplingMgr, op, RsType::Coach, this);
freightModel = new RSProxyModel(couplingMgr, op, RsType::FreightWagon, this);
QGridLayout *lay = new QGridLayout(this);
@ -86,7 +86,8 @@ RSCoupleDialog::RSCoupleDialog(RSCouplingInterface *mgr, RsOp o, QWidget *parent
showHideLegendBut = new QPushButton;
buttonLay->addWidget(showHideLegendBut);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok); //TODO: implement also cancel
QDialogButtonBox *box =
new QDialogButtonBox(QDialogButtonBox::Ok); // TODO: implement also cancel
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
buttonLay->addWidget(box);
@ -100,13 +101,14 @@ RSCoupleDialog::RSCoupleDialog(RSCouplingInterface *mgr, RsOp o, QWidget *parent
setLegendVisible(AppSettings.getShowCouplingLegend());
}
void RSCoupleDialog::loadProxyModels(sqlite3pp::database& db, db_id jobId, db_id stopId, db_id stationId, const QTime& arrival)
void RSCoupleDialog::loadProxyModels(sqlite3pp::database &db, db_id jobId, db_id stopId,
db_id stationId, const QTime &arrival)
{
QVector<RSProxyModel::RsItem> engines, freight, coaches;
sqlite3pp::query q(db);
if(op == RsOp::Coupled)
if (op == RsOp::Coupled)
{
/* Show Couple-able RS:
* - RS free in this station
@ -117,59 +119,68 @@ void RSCoupleDialog::loadProxyModels(sqlite3pp::database& db, db_id jobId, db_id
* - Possible wrong operations to let the user remove them
*/
q.prepare("SELECT MAX(sub.p), sub.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, rs_models.sub_type, sub.arr, sub.job_id, jobs.category FROM ("
q.prepare(
"SELECT MAX(sub.p), sub.rs_id, rs_list.number, rs_models.name, rs_models.suffix, "
"rs_models.type, rs_models.sub_type, sub.arr, sub.job_id, jobs.category FROM ("
//Select possible wrong operations to let user remove (un-check) them
" SELECT 1 AS p, coupling.rs_id AS rs_id, NULL AS arr, NULL AS job_id FROM coupling WHERE coupling.stop_id=?3 AND coupling.operation=1"
" UNION ALL"
// Select possible wrong operations to let user remove (un-check) them
" SELECT 1 AS p, coupling.rs_id AS rs_id, NULL AS arr, NULL AS job_id FROM coupling "
"WHERE coupling.stop_id=?3 AND coupling.operation=1"
" UNION ALL"
//Select RS uncoupled before our arrival (included RS uncoupled at exact same time) (except uncoupled by us)
" SELECT 2 AS p, coupling.rs_id AS rs_id, MAX(stops.arrival) AS arr, stops.job_id AS job_id"
" FROM stops"
" JOIN coupling ON coupling.stop_id=stops.id"
" WHERE stops.station_id=?1 AND stops.arrival <= ?2 AND stops.id<>?3"
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=0"
" UNION ALL"
// Select RS uncoupled before our arrival (included RS uncoupled at exact same time)
// (except uncoupled by us)
" SELECT 2 AS p, coupling.rs_id AS rs_id, MAX(stops.arrival) AS arr, stops.job_id AS "
"job_id"
" FROM stops"
" JOIN coupling ON coupling.stop_id=stops.id"
" WHERE stops.station_id=?1 AND stops.arrival <= ?2 AND stops.id<>?3"
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=0"
" UNION ALL"
//Select RS coupled after our arrival (excluded RS coupled at exact same time)
" SELECT 3 AS p, coupling.rs_id, MIN(stops.arrival) AS arr, stops.job_id AS job_id"
" FROM coupling"
" JOIN stops ON stops.id=coupling.stop_id"
" WHERE stops.station_id=?1 AND stops.arrival > ?2"
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=1"
" UNION ALL"
// Select RS coupled after our arrival (excluded RS coupled at exact same time)
" SELECT 3 AS p, coupling.rs_id, MIN(stops.arrival) AS arr, stops.job_id AS job_id"
" FROM coupling"
" JOIN stops ON stops.id=coupling.stop_id"
" WHERE stops.station_id=?1 AND stops.arrival > ?2"
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=1"
" UNION ALL"
//Select coupled RS for first time
" SELECT 4 AS p, rs_list.id AS rs_id, NULL AS arr, NULL AS job_id"
" FROM rs_list"
" WHERE NOT EXISTS ("
" SELECT coupling.rs_id FROM coupling"
" JOIN stops ON stops.id=coupling.stop_id WHERE coupling.rs_id=rs_list.id AND stops.arrival<?2)"
" UNION ALL"
// Select coupled RS for first time
" SELECT 4 AS p, rs_list.id AS rs_id, NULL AS arr, NULL AS job_id"
" FROM rs_list"
" WHERE NOT EXISTS ("
" SELECT coupling.rs_id FROM coupling"
" JOIN stops ON stops.id=coupling.stop_id WHERE coupling.rs_id=rs_list.id AND "
"stops.arrival<?2)"
" UNION ALL"
//Select unused RS (RS without any operation)
" SELECT 5 AS p, rs_list.id AS rs_id, NULL AS arr, NULL AS job_id"
" FROM rs_list"
" WHERE NOT EXISTS (SELECT coupling.rs_id FROM coupling WHERE coupling.rs_id=rs_list.id)"
" UNION ALL"
// Select unused RS (RS without any operation)
" SELECT 5 AS p, rs_list.id AS rs_id, NULL AS arr, NULL AS job_id"
" FROM rs_list"
" WHERE NOT EXISTS (SELECT coupling.rs_id FROM coupling WHERE coupling.rs_id=rs_list.id)"
" UNION ALL"
//Select RS coupled before our arrival (already coupled by this job or occupied by other job)
" SELECT 7 AS p, c1.rs_id, MAX(s1.arrival) AS arr, s1.job_id AS job_id"
" FROM coupling c1"
" JOIN coupling c2 ON c2.rs_id=c1.rs_id"
" JOIN stops s1 ON s1.id=c2.stop_id"
" WHERE c1.stop_id=?3 AND c1.operation=1 AND s1.arrival<?2"
" GROUP BY c1.rs_id"
" HAVING c2.operation=1"
" ) AS sub"
// Select RS coupled before our arrival (already coupled by this job or occupied by other
// job)
" SELECT 7 AS p, c1.rs_id, MAX(s1.arrival) AS arr, s1.job_id AS job_id"
" FROM coupling c1"
" JOIN coupling c2 ON c2.rs_id=c1.rs_id"
" JOIN stops s1 ON s1.id=c2.stop_id"
" WHERE c1.stop_id=?3 AND c1.operation=1 AND s1.arrival<?2"
" GROUP BY c1.rs_id"
" HAVING c2.operation=1"
" ) AS sub"
" JOIN rs_list ON rs_list.id=sub.rs_id" //FIXME: it seems it is better to join in the subquery directly, also avoids some LEFT in joins
" LEFT JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN jobs ON jobs.id=sub.job_id"
" GROUP BY sub.rs_id"
" ORDER BY rs_models.name, rs_list.number");
" JOIN rs_list ON rs_list.id=sub.rs_id" // FIXME: it seems it is better to join in the
// subquery directly, also avoids some LEFT in
// joins
" LEFT JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN jobs ON jobs.id=sub.job_id"
" GROUP BY sub.rs_id"
" ORDER BY rs_models.name, rs_list.number");
q.bind(1, stationId);
q.bind(2, arrival);
@ -184,65 +195,67 @@ void RSCoupleDialog::loadProxyModels(sqlite3pp::database& db, db_id jobId, db_id
* - Show possible wrong operations to let user remove them
*/
q.prepare("SELECT MAX(sub.p), sub.rs_id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type, rs_models.sub_type, sub.arr, NULL AS job_id, NULL AS category FROM("
"SELECT 8 AS p, coupling.rs_id AS rs_id, MAX(stops.arrival) AS arr"
" FROM stops"
" JOIN coupling ON coupling.stop_id=stops.id"
" WHERE stops.arrival<?2"
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=1 AND stops.job_id=?1"
" UNION ALL"
q.prepare(
"SELECT MAX(sub.p), sub.rs_id, rs_list.number, rs_models.name, rs_models.suffix, "
"rs_models.type, rs_models.sub_type, sub.arr, NULL AS job_id, NULL AS category FROM("
"SELECT 8 AS p, coupling.rs_id AS rs_id, MAX(stops.arrival) AS arr"
" FROM stops"
" JOIN coupling ON coupling.stop_id=stops.id"
" WHERE stops.arrival<?2"
" GROUP BY coupling.rs_id"
" HAVING coupling.operation=1 AND stops.job_id=?1"
" UNION ALL"
//Select possible wrong operations to let user remove them
" SELECT 6 AS p, coupling.rs_id AS rs_id, NULL AS arr FROM coupling WHERE coupling.stop_id=?3 AND coupling.operation=0"
") AS sub"
" JOIN rs_list ON rs_list.id=sub.rs_id" //FIXME: it seems it is better to join in the subquery directly, also avoids some LEFT in joins
" LEFT JOIN rs_models ON rs_models.id=rs_list.model_id"
" GROUP BY sub.rs_id"
" ORDER BY rs_models.name, rs_list.number");
// Select possible wrong operations to let user remove them
" SELECT 6 AS p, coupling.rs_id AS rs_id, NULL AS arr FROM coupling WHERE "
"coupling.stop_id=?3 AND coupling.operation=0"
") AS sub"
" JOIN rs_list ON rs_list.id=sub.rs_id" // FIXME: it seems it is better to join in the
// subquery directly, also avoids some LEFT in
// joins
" LEFT JOIN rs_models ON rs_models.id=rs_list.model_id"
" GROUP BY sub.rs_id"
" ORDER BY rs_models.name, rs_list.number");
q.bind(1, jobId);
q.bind(2, arrival);
q.bind(3, stopId);
}
for(auto rs : q)
for (auto rs : q)
{
/*Priority flag:
* 1: The RS is not free in this station, it's shown to let the user remove the operation
* 2: The RS is free and has no following operation in this station
* (It could have wrong operation in other station that should be fixed by user,
* as shown in RsErrorWidget)
* 3: The RS is free but a job will couple it in the future so you should bring it back here before that time
* 4: The RS is used for first time
* 5: The RS is unsed
* 6: RS was not coupled before this stop and you are trying to uncouple it
* 7: Normal RS uncouple-able, used to win against 6
*/
* 3: The RS is free but a job will couple it in the future so you should bring it back here
* before that time 4: The RS is used for first time 5: The RS is unsed 6: RS was not
* coupled before this stop and you are trying to uncouple it 7: Normal RS uncouple-able,
* used to win against 6
*/
RSProxyModel::RsItem item;
item.flag = rs.get<int>(0);
item.rsId = rs.get<db_id>(1);
item.flag = rs.get<int>(0);
item.rsId = rs.get<db_id>(1);
int number = rs.get<int>(2);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 3));
int number = rs.get<int>(2);
int modelNameLen = sqlite3_column_bytes(q.stmt(), 3);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 3));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 4);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(q.stmt(), 4));
RsType type = RsType(rs.get<int>(5));
int modelSuffixLen = sqlite3_column_bytes(q.stmt(), 4);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(q.stmt(), 4));
RsType type = RsType(rs.get<int>(5));
RsEngineSubType subType = RsEngineSubType(rs.get<int>(6));
item.rsName = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
type);
item.rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
modelSuffixLen, type);
item.engineType = RsEngineSubType::Invalid;
item.time = rs.get<QTime>(7);
item.jobId = rs.get<db_id>(8);
item.jobCat = JobCategory(rs.get<int>(9));
item.time = rs.get<QTime>(7);
item.jobId = rs.get<db_id>(8);
item.jobCat = JobCategory(rs.get<int>(9));
switch (type)
{
@ -274,7 +287,7 @@ void RSCoupleDialog::loadProxyModels(sqlite3pp::database& db, db_id jobId, db_id
void RSCoupleDialog::done(int ret)
{
//Save legend state
// Save legend state
AppSettings.setShowCouplingLegend(m_showLegend);
QDialog::done(ret);
@ -294,35 +307,43 @@ void RSCoupleDialog::setLegendVisible(bool val)
{
m_showLegend = val;
if(legend->isVisible() && !m_showLegend)
if (legend->isVisible() && !m_showLegend)
{
legend->hide();
}
else if(m_showLegend && !legend->isVisible())
else if (m_showLegend && !legend->isVisible())
{
legend->show();
if(!legend->layout())
if (!legend->layout())
{
double fontPt = font().pointSizeF() * 1.2;
double fontPt = font().pointSizeF() * 1.2;
QVBoxLayout *legendLay = new QVBoxLayout(legend);
QLabel *label = new QLabel(tr("<style>\n"
"table, td {"
"border: 1px solid black;"
"border-collapse:collapse;"
"padding:5px;"
" }"
"</style>"
"<table style=\"font-size:%1pt;padding:10pt\"><tr>"
"<td><span style=\"background-color:#FFFFFF\">___</span> This item is free in current station.</td>"
"<td><span style=\"background-color:#FF3d43\">___</span> The item isn't in this station.</td>"
"</tr><tr>"
"<td><span style=\"background-color:#00FFFF\">___</span> First use of this item.</td>"
"<td><span style=\"background-color:#FF56FF\">___</span> The item isn't coupled before or already coupled.</td>"
"</tr><tr>"
"<td><span style=\"background-color:#00FF00\">___</span> This item is never used in this session.</td>"
"<td><span style=\"color:#0000FF;background-color:#FFFFFF\">\\\\\\\\</span> Railway line doesn't allow electric traction.</td>"
"</tr></table>").arg(fontPt));
QLabel *label = new QLabel(
tr("<style>\n"
"table, td {"
"border: 1px solid black;"
"border-collapse:collapse;"
"padding:5px;"
" }"
"</style>"
"<table style=\"font-size:%1pt;padding:10pt\"><tr>"
"<td><span style=\"background-color:#FFFFFF\">___</span> This item is free in "
"current station.</td>"
"<td><span style=\"background-color:#FF3d43\">___</span> The item isn't in this "
"station.</td>"
"</tr><tr>"
"<td><span style=\"background-color:#00FFFF\">___</span> First use of this "
"item.</td>"
"<td><span style=\"background-color:#FF56FF\">___</span> The item isn't coupled "
"before or already coupled.</td>"
"</tr><tr>"
"<td><span style=\"background-color:#00FF00\">___</span> This item is never used "
"in this session.</td>"
"<td><span style=\"color:#0000FF;background-color:#FFFFFF\">\\\\\\\\</span> "
"Railway line doesn't allow electric traction.</td>"
"</tr></table>")
.arg(fontPt));
label->setTextFormat(Qt::RichText);
label->setWordWrap(true);
legendLay->addWidget(label);

View File

@ -32,14 +32,15 @@ namespace sqlite3pp {
class database;
}
//FIXME: on-demand load and filter
// FIXME: on-demand load and filter
class RSCoupleDialog : public QDialog
{
Q_OBJECT
public:
RSCoupleDialog(RSCouplingInterface *mgr, RsOp o, QWidget *parent = nullptr);
void loadProxyModels(sqlite3pp::database &db, db_id jobId, db_id stopId, db_id stationId, const QTime &arrival);
void loadProxyModels(sqlite3pp::database &db, db_id jobId, db_id stopId, db_id stationId,
const QTime &arrival);
protected:
void done(int ret) override;

View File

@ -34,7 +34,7 @@ ShiftBusyDlg::ShiftBusyDlg(QWidget *parent) :
{
QVBoxLayout *lay = new QVBoxLayout(this);
m_label = new QLabel;
m_label = new QLabel;
lay->addWidget(m_label);
view = new QTableView;
@ -57,8 +57,7 @@ void ShiftBusyDlg::setModel(ShiftBusyModel *m)
m_label->setText(tr("Cannot set shift <b>%1</b> to job <b>%2</b>.<br>"
"The selected shift is busy:<br>"
"From: %3 To: %4")
.arg(model->getShiftName(),
model->getJobName(),
model->getStart().toString("HH:mm"),
model->getEnd().toString("HH:mm")));
.arg(model->getShiftName(), model->getJobName(),
model->getStart().toString("HH:mm"),
model->getEnd().toString("HH:mm")));
}

View File

@ -36,6 +36,7 @@ public:
explicit ShiftBusyDlg(QWidget *parent = nullptr);
void setModel(ShiftBusyModel *m);
private:
QLabel *m_label;
QTableView *view;

View File

@ -35,9 +35,10 @@ ShiftBusyModel::ShiftBusyModel(sqlite3pp::database &db, QObject *parent) :
QVariant ShiftBusyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section) {
switch (section)
{
case JobCol:
return tr("Job");
case Start:
@ -66,7 +67,7 @@ QVariant ShiftBusyModel::data(const QModelIndex &idx, int role) const
if (!idx.isValid() || row >= m_data.size() || idx.column() >= NCols)
return QVariant();
const JobInfo& info = m_data.at(row);
const JobInfo &info = m_data.at(row);
switch (role)
{
@ -94,13 +95,13 @@ void ShiftBusyModel::loadData(db_id shiftId, db_id jobId, const QTime &start, co
m_data.clear();
m_shiftId = shiftId;
m_jobId = jobId;
m_start = start;
m_end = end;
m_jobId = jobId;
m_start = start;
m_end = end;
query q(mDb, "SELECT name FROM jobshifts WHERE id=?");
q.bind(1, m_shiftId);
if(q.step() != SQLITE_ROW)
if (q.step() != SQLITE_ROW)
{
endResetModel();
return;
@ -118,7 +119,7 @@ void ShiftBusyModel::loadData(db_id shiftId, db_id jobId, const QTime &start, co
q.bind(2, m_start);
q.bind(3, m_end);
q.step();
int count = q.getRows().get<int>(0) - 1; //Do not count ourself
int count = q.getRows().get<int>(0) - 1; // Do not count ourself
m_data.reserve(count);
q.prepare("SELECT jobs.id, jobs.category,"
@ -133,20 +134,20 @@ void ShiftBusyModel::loadData(db_id shiftId, db_id jobId, const QTime &start, co
q.bind(2, m_start);
q.bind(3, m_end);
for(auto j : q)
for (auto j : q)
{
JobInfo info;
info.jobId = j.get<db_id>(0);
info.jobId = j.get<db_id>(0);
info.jobCat = JobCategory(j.get<int>(1));
if(info.jobId == m_jobId)
if (info.jobId == m_jobId)
{
m_jobCat = info.jobCat;
continue;
}
info.start = j.get<QTime>(3);
info.end = j.get<QTime>(4);
info.end = j.get<QTime>(4);
m_data.append(info);
}

View File

@ -30,14 +30,14 @@ namespace sqlite3pp {
class database;
}
//TODO: move to shifts subdir
// TODO: move to shifts subdir
class ShiftBusyModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum Columns {
enum Columns
{
JobCol = 0,
Start,
End,
@ -55,7 +55,8 @@ public:
ShiftBusyModel(sqlite3pp::database &db, QObject *parent = nullptr);
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
// Basic functionality:
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
@ -63,14 +64,26 @@ public:
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
void loadData(db_id shiftId, db_id jobId, const QTime& start, const QTime& end);
void loadData(db_id shiftId, db_id jobId, const QTime &start, const QTime &end);
inline bool hasConcurrentJobs() const { return m_data.size(); }
inline bool hasConcurrentJobs() const
{
return m_data.size();
}
inline QTime getStart() const { return m_start; }
inline QTime getEnd() const { return m_end; }
inline QTime getStart() const
{
return m_start;
}
inline QTime getEnd() const
{
return m_end;
}
inline QString getShiftName() const { return m_shiftName; }
inline QString getShiftName() const
{
return m_shiftName;
}
QString getJobName() const;
private:

View File

@ -41,11 +41,11 @@ StopDelegate::StopDelegate(sqlite3pp::database &db, QObject *parent) :
void StopDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QRect rect = option.rect.adjusted(5, 5, -5, -5);
QRect rect = option.rect.adjusted(5, 5, -5, -5);
const StopModel *model = static_cast<const StopModel *>(index.model());
const StopItem item = model->getItemAt(index.row());
const bool isTransit = item.type == StopType::Transit;
const StopItem item = model->getItemAt(index.row());
const bool isTransit = item.type == StopType::Transit;
query q(mDb, "SELECT name FROM stations WHERE id=?");
q.bind(1, item.stationId);
@ -56,7 +56,7 @@ void StopDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
painter->save();
painter->setRenderHint(QPainter::Antialiasing, true);
//Draw bottom border
// Draw bottom border
painter->setPen(QPen(Qt::black, 1));
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
@ -73,120 +73,117 @@ void StopDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
painter->setBrush(option.palette.text());
const double top = rect.top();
const double bottom = rect.bottom();
const double left = rect.left();
const double width = rect.width();
const double height = rect.height();
const double top = rect.top();
const double bottom = rect.bottom();
const double left = rect.left();
const double width = rect.width();
const double height = rect.height();
const double stHeight = top + (isTransit ? 0.0 : height * 0.1);
const double timeHeight = top + height * 0.4;
const double lineHeight = top + height * 0.65;
const double stHeight = top + (isTransit ? 0.0 : height * 0.1);
const double timeHeight = top + height * 0.4;
const double lineHeight = top + height * 0.65;
const double arrX = left + width * (isTransit ? 0.4 : 0.2);
const double depX = left + width * 0.6;
const double arrX = left + width * (isTransit ? 0.4 : 0.2);
const double depX = left + width * 0.6;
const double transitLineX = left + width * 0.2;
if(item.addHere == 0)
if (item.addHere == 0)
{
//Draw item
//Station name
painter->drawText(QRectF(left, stHeight, width, bottom - stHeight),
station,
// Draw item
// Station name
painter->drawText(QRectF(left, stHeight, width, bottom - stHeight), station,
QTextOption(Qt::AlignHCenter));
if(item.type != StopType::First)
if (item.type != StopType::First)
{
//Arrival
// Arrival
painter->drawText(QRectF(arrX, timeHeight, width, bottom - timeHeight),
item.arrival.toString("HH:mm"));
}
if(item.type == StopType::First || item.type == StopType::Normal) //Last, Transit don't have a separate departure
if (item.type == StopType::First
|| item.type == StopType::Normal) // Last, Transit don't have a separate departure
{
//Departure
// Departure
painter->drawText(QRectF(depX, timeHeight, width, bottom - timeHeight),
item.departure.toString("HH:mm"));
}
//Check direction
if(item.fromGate.gateConnId && item.toGate.gateConnId
&& item.type != StopType::First && item.type != StopType::Last)
// Check direction
if (item.fromGate.gateConnId && item.toGate.gateConnId && item.type != StopType::First
&& item.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(item.fromGate.stationTrackSide == item.toGate.stationTrackSide)
// 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 (item.fromGate.stationTrackSide == item.toGate.stationTrackSide)
{
//Train leaves station track from same side of entrance, draw reverse icon
// Train leaves station track from same side of entrance, draw reverse icon
QPointF iconTopLeft(left, bottom - PixHeight);
painter->drawPixmap(iconTopLeft, m_reverseDirPix);
}
}
if(item.type != StopType::Last && item.nextSegment.segmentId)
if (item.type != StopType::Last && item.nextSegment.segmentId)
{
//Last has no next segment so do not draw lightning
// Last has no next segment so do not draw lightning
bool nextSegmentElectrified = model->isRailwayElectrifiedAfterRow(index.row());
bool prevSegmentElectrified = !nextSegmentElectrified; //Trigger change on First stop
bool prevSegmentElectrified = !nextSegmentElectrified; // Trigger change on First stop
if(item.type != StopType::First && index.row() >= 0)
if (item.type != StopType::First && index.row() >= 0)
{
//Get real previous railway type
// Get real previous railway type
prevSegmentElectrified = model->isRailwayElectrifiedAfterRow(index.row() - 1);
}
if(nextSegmentElectrified != prevSegmentElectrified)
if (nextSegmentElectrified != prevSegmentElectrified)
{
//Railway type changed, draw a lightning
// Railway type changed, draw a lightning
QPointF lightningTopLeft(left, top + qMin(5.0, height * 0.1));
painter->drawPixmap(lightningTopLeft, m_lightningPix);
if(!nextSegmentElectrified)
if (!nextSegmentElectrified)
{
//Next railway is not electrified, cross the lightning
//Then keep red pen to draw next segment name
// Next railway is not electrified, cross the lightning
// Then keep red pen to draw next segment name
painter->setPen(QPen(Qt::red, 4));
painter->drawLine(lightningTopLeft, lightningTopLeft + QPointF(PixWidth, PixHeight));
painter->drawLine(lightningTopLeft,
lightningTopLeft + QPointF(PixWidth, PixHeight));
}
}
//Draw next segment name
// Draw next segment name
q.prepare("SELECT name FROM railway_segments WHERE id=?");
q.bind(1, item.nextSegment.segmentId);
q.step();
auto r = q.getRows();
auto r = q.getRows();
const QString segName = r.get<QString>(0);
q.reset();
const double lineRightX = left + width * 0.8;
painter->drawText(QRectF(transitLineX, lineHeight, lineRightX - left, bottom - lineHeight),
tr("Seg: %1").arg(segName),
QTextOption(Qt::AlignHCenter));
painter->drawText(
QRectF(transitLineX, lineHeight, lineRightX - left, bottom - lineHeight),
tr("Seg: %1").arg(segName), QTextOption(Qt::AlignHCenter));
if(item.toGate.gateTrackNum != 0)
if (item.toGate.gateTrackNum != 0)
{
painter->setPen(QPen(Qt::red, 4));
painter->drawText(QRectF(lineRightX, lineHeight, left + width - lineRightX, bottom - lineHeight),
QString::number(item.toGate.gateTrackNum),
QTextOption(Qt::AlignHCenter));
painter->drawText(
QRectF(lineRightX, lineHeight, left + width - lineRightX, bottom - lineHeight),
QString::number(item.toGate.gateTrackNum), QTextOption(Qt::AlignHCenter));
}
}
if(isTransit)
if (isTransit)
{
//Draw a vertical -0- to tell this is a transit
// Draw a vertical -0- to tell this is a transit
painter->setPen(QPen(Qt::red, 4));
painter->setBrush(Qt::red);
painter->drawLine(QLineF(transitLineX, rect.top(),
transitLineX, rect.bottom()));
painter->drawLine(QLineF(transitLineX, rect.top(), transitLineX, rect.bottom()));
painter->drawEllipse(QRectF(transitLineX - 12 / 2,
rect.top() + rect.height() * 0.4,
12, 12));
painter->drawEllipse(
QRectF(transitLineX - 12 / 2, rect.top() + rect.height() * 0.4, 12, 12));
}
}
else if (item.addHere == 1)
{
@ -200,30 +197,29 @@ void StopDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
painter->restore();
}
QSize StopDelegate::sizeHint(const QStyleOptionViewItem &/*option*/,
QSize StopDelegate::sizeHint(const QStyleOptionViewItem & /*option*/,
const QModelIndex &index) const
{
int w = 200;
int h = NormalStopHeight;
int w = 200;
int h = NormalStopHeight;
const StopModel *model = static_cast<const StopModel *>(index.model());
if(index.row() < 0 || index.row() >= model->rowCount())
if (index.row() < 0 || index.row() >= model->rowCount())
return QSize(w, AddHereHeight);
const StopItem& item = model->getItemAt(index.row());
if(item.type == StopType::Transit)
const StopItem &item = model->getItemAt(index.row());
if (item.type == StopType::Transit)
h = TransitStopHeight;
if(item.addHere != 0)
if (item.addHere != 0)
h = AddHereHeight;
return QSize(w, h);
}
QWidget *StopDelegate::createEditor(QWidget *parent,
const QStyleOptionViewItem &/*option*/,
QWidget *StopDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem & /*option*/,
const QModelIndex &index) const
{
StopModel *model = const_cast<StopModel*>(static_cast<const StopModel *>(index.model()));
if(model->isAddHere(index))
StopModel *model = const_cast<StopModel *>(static_cast<const StopModel *>(index.model()));
if (model->isAddHere(index))
{
qDebug() << index << "is AddHere";
return nullptr;
@ -231,35 +227,34 @@ QWidget *StopDelegate::createEditor(QWidget *parent,
StopEditor *editor = new StopEditor(mDb, model, parent);
editor->setAutoFillBackground(true);
editor->setEnabled(false); //Mark it
editor->setEnabled(false); // Mark it
//Prevent JobPathEditor context menu in table view during stop editing
// Prevent JobPathEditor context menu in table view during stop editing
editor->setContextMenuPolicy(Qt::PreventContextMenu);
//See 'StopEditor::popupLinesCombo'
// See 'StopEditor::popupLinesCombo'
connect(this, &StopDelegate::popupEditorSegmentCombo, editor, &StopEditor::popupSegmentCombo);
connect(editor, &StopEditor::nextSegmentChosen, this, &StopDelegate::onEditorSegmentChosen);
return editor;
}
void StopDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
void StopDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
StopEditor *ed = static_cast<StopEditor*>(editor);
if(ed->isEnabled()) //We already set data
StopEditor *ed = static_cast<StopEditor *>(editor);
if (ed->isEnabled()) // We already set data
return;
ed->setEnabled(true); //Mark it
ed->setEnabled(true); // Mark it
const StopModel *model = static_cast<const StopModel *>(index.model());
const StopItem item = model->getItemAt(index.row());
const StopItem item = model->getItemAt(index.row());
StopItem prev;
int r = index.row();
if(r > 0)
if (r > 0)
{
//Current stop is not First, get also previous one
// Current stop is not First, get also previous one
prev = model->getItemAt(r - 1);
}
@ -272,7 +267,7 @@ void StopDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
DEBUG_IMPORTANT_ENTRY;
qDebug() << "End editing: stop" << index.row();
StopEditor *ed = static_cast<StopEditor*>(editor);
StopEditor *ed = static_cast<StopEditor *>(editor);
StopModel *stopModel = static_cast<StopModel *>(model);
bool avoidTimeRecalc = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
@ -280,14 +275,15 @@ void StopDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
stopModel->setStopInfo(index, ed->getCurItem(), ed->getPrevItem().nextSegment, avoidTimeRecalc);
}
void StopDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/*index*/) const
void StopDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex & /*index*/) const
{
editor->setGeometry(option.rect);
}
void StopDelegate::onEditorSegmentChosen(StopEditor *editor)
{
if(editor->closeOnSegmentChosen())
if (editor->closeOnSegmentChosen())
{
emit commitData(editor);
emit closeEditor(editor, StopDelegate::EditNextItem);
@ -296,10 +292,11 @@ void StopDelegate::onEditorSegmentChosen(StopEditor *editor)
void StopDelegate::refreshPixmaps()
{
const QString iconPath = QCoreApplication::instance()->applicationDirPath() + QStringLiteral("/icons");
const QString iconPath =
QCoreApplication::instance()->applicationDirPath() + QStringLiteral("/icons");
//Square pixmaps
m_lightningPix = QPixmap(PixWidth, PixHeight);
// Square pixmaps
m_lightningPix = QPixmap(PixWidth, PixHeight);
m_reverseDirPix = QPixmap(PixWidth, PixHeight);
m_lightningPix.fill(Qt::transparent);
@ -309,26 +306,26 @@ void StopDelegate::refreshPixmaps()
QPainter painter;
QRectF iconRect;
//Cache Lightning
// Cache Lightning
mSvg.load(iconPath + QStringLiteral("/lightning.svg"));
//Scale SVG to fit requested size
// Scale SVG to fit requested size
iconRect.setSize(mSvg.defaultSize().scaled(PixWidth, PixHeight, Qt::KeepAspectRatio));
//Center on pixmap
// Center on pixmap
iconRect.moveTop((PixHeight - iconRect.height()) / 2);
iconRect.moveLeft((PixWidth - iconRect.width()) / 2);
painter.begin(&m_lightningPix);
mSvg.render(&painter, iconRect);
painter.end();
//Cache Reverse Direction
// Cache Reverse Direction
mSvg.load(iconPath + QStringLiteral("/reverse_direction.svg"));
//Scale SVG to fit requested size
// Scale SVG to fit requested size
iconRect.setSize(mSvg.defaultSize().scaled(PixWidth, PixHeight, Qt::KeepAspectRatio));
//Center on pixmap
// Center on pixmap
iconRect.moveTop((PixHeight - iconRect.height()) / 2);
iconRect.moveLeft((PixWidth - iconRect.width()) / 2);
painter.begin(&m_reverseDirPix);

View File

@ -46,15 +46,15 @@ public:
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const override;
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
const QModelIndex &) const override;
signals:
/*!
@ -87,12 +87,12 @@ private:
QPixmap m_lightningPix;
QPixmap m_reverseDirPix;
static constexpr int NormalStopHeight = 100;
static constexpr int NormalStopHeight = 100;
static constexpr int TransitStopHeight = 80;
static constexpr int AddHereHeight = 30;
static constexpr int AddHereHeight = 30;
static constexpr int PixWidth = 35;
static constexpr int PixHeight = PixWidth;
static constexpr int PixWidth = 35;
static constexpr int PixHeight = PixWidth;
};
#endif // STOPDELEGATE_H

View File

@ -33,43 +33,47 @@
#include <QMessageBox>
StopEditingHelper::StopEditingHelper(database &db, StopModel *m,
QSpinBox *outTrackSpin, QTimeEdit *arr, QTimeEdit *dep,
QWidget *editor) :
StopEditingHelper::StopEditingHelper(database &db, StopModel *m, QSpinBox *outTrackSpin,
QTimeEdit *arr, QTimeEdit *dep, QWidget *editor) :
QObject(editor),
mEditor(editor),
model(m),
mTimerOutTrack(0)
{
stationsMatchModel = new StationsMatchModel(db, this);
stationTrackMatchModel = new StationTracksMatchModel(db, this);
stationsMatchModel = new StationsMatchModel(db, this);
stationTrackMatchModel = new StationTracksMatchModel(db, this);
stationOutGateMatchModel = new StationGatesMatchModel(db, this);
mStationEdit = new CustomCompletionLineEdit(stationsMatchModel, mEditor);
mStationEdit = new CustomCompletionLineEdit(stationsMatchModel, mEditor);
mStationEdit->setPlaceholderText(tr("Station name"));
mStationEdit->setToolTip(mStationEdit->placeholderText());
connect(mStationEdit, &CustomCompletionLineEdit::completionDone, this, &StopEditingHelper::onStationSelected);
connect(mStationEdit, &CustomCompletionLineEdit::completionDone, this,
&StopEditingHelper::onStationSelected);
mStTrackEdit = new CustomCompletionLineEdit(stationTrackMatchModel, mEditor);
mStTrackEdit->setPlaceholderText(tr("Track"));
mStTrackEdit->setToolTip(mStTrackEdit->placeholderText());
connect(mStTrackEdit, &CustomCompletionLineEdit::completionDone, this, &StopEditingHelper::onTrackSelected);
connect(mStTrackEdit, &CustomCompletionLineEdit::completionDone, this,
&StopEditingHelper::onTrackSelected);
mOutGateEdit = new CustomCompletionLineEdit(stationOutGateMatchModel, mEditor);
mOutGateEdit->setPlaceholderText(tr("Next segment"));
mOutGateEdit->setToolTip(mOutGateEdit->placeholderText());
connect(mOutGateEdit, &CustomCompletionLineEdit::indexSelected, this, &StopEditingHelper::onOutGateSelected);
connect(mOutGateEdit, &CustomCompletionLineEdit::indexSelected, this,
&StopEditingHelper::onOutGateSelected);
mOutGateTrackSpin = outTrackSpin;
mOutGateTrackSpin->setMaximum(0);
mOutGateTrackSpin->setToolTip(tr("Out Gate track"));
connect(mOutGateTrackSpin, qOverload<int>(&QSpinBox::valueChanged), this, &StopEditingHelper::startOutTrackTimer);
connect(mOutGateTrackSpin, &QSpinBox::editingFinished, this, &StopEditingHelper::checkOutGateTrack);
connect(mOutGateTrackSpin, qOverload<int>(&QSpinBox::valueChanged), this,
&StopEditingHelper::startOutTrackTimer);
connect(mOutGateTrackSpin, &QSpinBox::editingFinished, this,
&StopEditingHelper::checkOutGateTrack);
arrEdit = arr;
depEdit = dep;
//Do not set tooltip on arrEdit, it is done inside setStop() already
// Do not set tooltip on arrEdit, it is done inside setStop() already
depEdit->setToolTip(tr("Departure"));
connect(arrEdit, &QTimeEdit::timeChanged, this, &StopEditingHelper::arrivalChanged);
@ -83,69 +87,70 @@ StopEditingHelper::~StopEditingHelper()
void StopEditingHelper::setStop(const StopItem &item, const StopItem &prev)
{
curStop = item;
curStop = item;
prevStop = prev;
//Update match models
// Update match models
stationsMatchModel->setFilter(prevStop.stationId);
stationTrackMatchModel->setFilter(curStop.stationId);
stationOutGateMatchModel->setFilter(curStop.stationId, true, prevStop.nextSegment.segmentId, true);
stationOutGateMatchModel->setFilter(curStop.stationId, true, prevStop.nextSegment.segmentId,
true);
//Check Arrival and Departure
// Check Arrival and Departure
QTime minArr, minDep;
if(curStop.type != StopType::First)
if (curStop.type != StopType::First)
{
//First stop: arrival is hidden, you can change only departure so do not set a minimum
// First stop: arrival is hidden, you can change only departure so do not set a minimum
//Next stop must be at least one minute after previous (minimum travel duration)
//This is to prevent contemporary stops that will break ORDER BY arrival queries
// Next stop must be at least one minute after previous (minimum travel duration)
// This is to prevent contemporary stops that will break ORDER BY arrival queries
minArr = prevStop.departure.addSecs(60);
//Normal stop: at least 1 minute stop
//Transit, Last: departure = arrival
// Normal stop: at least 1 minute stop
// Transit, Last: departure = arrival
minDep = curStop.arrival;
if(curStop.type == StopType::Normal)
if (curStop.type == StopType::Normal)
minDep = minDep.addSecs(60);
if(curStop.arrival < minArr)
if (curStop.arrival < minArr)
curStop.arrival = minArr;
if(curStop.departure < minDep)
if (curStop.departure < minDep)
curStop.arrival = minDep;
}
//Show/Hide relevant fields for current stop
// Show/Hide relevant fields for current stop
//First stop has no Arrival, only Departure
// First stop has no Arrival, only Departure
arrEdit->setEnabled(curStop.type != StopType::First);
arrEdit->setVisible(curStop.type != StopType::First);
QString arrTootlip = tr("Arrival"); //No message by default
if(curStop.type == StopType::Normal)
QString arrTootlip = tr("Arrival"); // No message by default
if (curStop.type == StopType::Normal)
{
arrTootlip.append('\n'); //Separate from usage tooltip
arrTootlip.append('\n'); // Separate from usage tooltip
arrTootlip.append(tr("Press shift if you don't want to change also departure time."));
}
arrEdit->setToolTip(arrTootlip);
//Transit and Last stop only have Arrival
// Transit and Last stop only have Arrival
depEdit->setEnabled(curStop.type != StopType::Last && curStop.type != StopType::Transit);
depEdit->setVisible(curStop.type != StopType::Last && curStop.type != StopType::Transit);
//Last stop has no Out Gate because there's no stop after last
// Last stop has no Out Gate because there's no stop after last
mOutGateEdit->setVisible(curStop.type != StopType::Last);
mOutGateTrackSpin->setVisible(curStop.type != StopType::Last);
//Enable track edit only if station is selected
// Enable track edit only if station is selected
mStTrackEdit->setEnabled(curStop.stationId != 0);
//Update UI fields
// Update UI fields
mStationEdit->setData(curStop.stationId);
mStTrackEdit->setData(curStop.trackId);
mOutGateEdit->setData(curStop.toGate.gateId);
updateGateTrackSpin(curStop.toGate);
//Set Arrival and Departure
// Set Arrival and Departure
arrEdit->blockSignals(true);
arrEdit->setMinimumTime(minArr);
arrEdit->setTime(curStop.arrival);
@ -159,39 +164,41 @@ void StopEditingHelper::setStop(const StopItem &item, const StopItem &prev)
void StopEditingHelper::popupSegmentCombo()
{
//Look for all possible segments
// Look for all possible segments
stationOutGateMatchModel->autoSuggest(QString());
const int count = stationOutGateMatchModel->rowCount();
if(count > 1 && !stationOutGateMatchModel->isEmptyRow(0)
if (count > 1 && !stationOutGateMatchModel->isEmptyRow(0)
&& (stationOutGateMatchModel->isEmptyRow(1) || stationOutGateMatchModel->isEllipsesRow(1)))
{
//Only 1 segment available, use it
db_id newSegId = stationOutGateMatchModel->getSegmentIdAtRow(0);
// Only 1 segment available, use it
db_id newSegId = stationOutGateMatchModel->getSegmentIdAtRow(0);
db_id segOutGateId = 0;
db_id segOutGateId = 0;
db_id suggestedTrackId = 0;
if(model->trySelectNextSegment(curStop, newSegId, 0, 0, segOutGateId, suggestedTrackId))
if (model->trySelectNextSegment(curStop, newSegId, 0, 0, segOutGateId, suggestedTrackId))
{
//Success, close editor
// Success, close editor
emit nextSegmentChosen();
return;
}
}
//We have multiple segments, let the user choose
// We have multiple segments, let the user choose
mOutGateEdit->showPopup();
}
QString StopEditingHelper::getGateString(db_id gateId, bool reversed)
{
QString str = QLatin1String("<b>");
if(gateId)
if (gateId)
{
str += stationOutGateMatchModel->getName(gateId);
if(reversed)
if (reversed)
str += tr(" (reversed)");
}else{
}
else
{
str += tr("Not set!");
}
str.append(QLatin1String("</b>"));
@ -200,7 +207,7 @@ QString StopEditingHelper::getGateString(db_id gateId, bool reversed)
void StopEditingHelper::timerEvent(QTimerEvent *e)
{
if(e->timerId() == mTimerOutTrack)
if (e->timerId() == mTimerOutTrack)
{
checkOutGateTrack();
return;
@ -213,32 +220,33 @@ void StopEditingHelper::onStationSelected()
{
db_id newStId = 0;
QString tmp;
if(!mStationEdit->getData(newStId, tmp))
if (!mStationEdit->getData(newStId, tmp))
return;
if(newStId == curStop.stationId)
if (newStId == curStop.stationId)
return;
curStop.stationId = newStId;
//Update track
// Update track
stationTrackMatchModel->setFilter(curStop.stationId);
mStTrackEdit->setEnabled(curStop.stationId != 0); //Enable only if station is selected
mStTrackEdit->setEnabled(curStop.stationId != 0); // Enable only if station is selected
if(curStop.stationId)
if (curStop.stationId)
{
if(!model->trySelectTrackForStop(curStop))
curStop.trackId = 0; //Could not find a track
if (!model->trySelectTrackForStop(curStop))
curStop.trackId = 0; // Could not find a track
mStTrackEdit->setData(curStop.trackId);
}
//Update prev segment
prevStop.nextSegment = StopItem::Segment{}; //Reset, will be reloaded by model
// Update prev segment
prevStop.nextSegment = StopItem::Segment{}; // Reset, will be reloaded by model
//Update next segment
stationOutGateMatchModel->setFilter(curStop.stationId, true, prevStop.nextSegment.segmentId, true);
mOutGateEdit->setData(0); //Reset, user must choose again
// Update next segment
stationOutGateMatchModel->setFilter(curStop.stationId, true, prevStop.nextSegment.segmentId,
true);
mOutGateEdit->setData(0); // Reset, user must choose again
curStop.nextSegment = StopItem::Segment{};
@ -249,19 +257,19 @@ void StopEditingHelper::onTrackSelected()
{
db_id newTrackId = 0;
QString str;
if(!mStTrackEdit->getData(newTrackId, str))
if (!mStTrackEdit->getData(newTrackId, str))
return;
str.clear();
//Check if track is connected to gates
if(!model->trySetTrackConnections(curStop, newTrackId, &str))
// Check if track is connected to gates
if (!model->trySetTrackConnections(curStop, newTrackId, &str))
{
//Show error to the user
// Show error to the user
bool stillSucceded = (curStop.trackId == newTrackId);
QMessageBox::warning(mEditor, stillSucceded ? tr("Gate Warning") : tr("Track Error"), str);
if(!stillSucceded)
mStTrackEdit->setData(curStop.trackId); //Reset to previous track
if (!stillSucceded)
mStTrackEdit->setData(curStop.trackId); // Reset to previous track
}
emit stationTrackChosen();
@ -271,25 +279,26 @@ void StopEditingHelper::onOutGateSelected(const QModelIndex &idx)
{
db_id newGateId = 0;
QString gateSegmentName;
if(!mOutGateEdit->getData(newGateId, gateSegmentName))
if (!mOutGateEdit->getData(newGateId, gateSegmentName))
return;
const db_id newSegId = stationOutGateMatchModel->getSegmentIdAtRow(idx.row());
const db_id oldGateId = curStop.toGate.gateId;
db_id segOutGateId = 0;
const db_id newSegId = stationOutGateMatchModel->getSegmentIdAtRow(idx.row());
const db_id oldGateId = curStop.toGate.gateId;
db_id segOutGateId = 0;
db_id suggestedTrackId = 0;
if(model->trySelectNextSegment(curStop, newSegId, 0, 0, segOutGateId, suggestedTrackId))
if (model->trySelectNextSegment(curStop, newSegId, 0, 0, segOutGateId, suggestedTrackId))
{
//Update gate track
// Update gate track
updateGateTrackSpin(curStop.toGate);
//Success, close editor
// Success, close editor
emit nextSegmentChosen();
}
else
{
//Warn user and reset to previous chosen segment if any
QMessageBox::warning(mEditor, tr("Stop Error"), tr("Cannot set segment <b>%1</b>").arg(gateSegmentName));
// Warn user and reset to previous chosen segment if any
QMessageBox::warning(mEditor, tr("Stop Error"),
tr("Cannot set segment <b>%1</b>").arg(gateSegmentName));
mOutGateEdit->setData(oldGateId);
}
}
@ -298,38 +307,40 @@ void StopEditingHelper::checkOutGateTrack()
{
stopOutTrackTimer();
if(!curStop.nextSegment.segmentId)
return; //First we need to have a segment
if (!curStop.nextSegment.segmentId)
return; // First we need to have a segment
int trackNum = mOutGateTrackSpin->value();
curStop.toGate.gateTrackNum = trackNum; //Trigger checking of railway segment connections
int trackNum = mOutGateTrackSpin->value();
curStop.toGate.gateTrackNum = trackNum; // Trigger checking of railway segment connections
db_id segOutGateId = 0;
db_id suggestedTrackId = 0;
if(model->trySelectNextSegment(curStop, curStop.nextSegment.segmentId, trackNum, 0, segOutGateId, suggestedTrackId))
db_id segOutGateId = 0;
db_id suggestedTrackId = 0;
if (model->trySelectNextSegment(curStop, curStop.nextSegment.segmentId, trackNum, 0,
segOutGateId, suggestedTrackId))
{
//Update gate track
// Update gate track
updateGateTrackSpin(curStop.toGate);
if(curStop.toGate.gateTrackNum != trackNum)
if (curStop.toGate.gateTrackNum != trackNum)
{
//It wasn't possible to set requested track
QMessageBox::warning(mEditor, tr("Stop Error"),
tr("Requested gate out track <b>%1</b> is not connected to segment <b>%2</b>.<br>"
"Out track <b>%3</b> was chosen instead.<br>"
"Look segment track connection from Stations Manager for more information"
" on available tracks.")
.arg(trackNum)
.arg(mOutGateEdit->text())
.arg(curStop.toGate.gateTrackNum));
// It wasn't possible to set requested track
QMessageBox::warning(
mEditor, tr("Stop Error"),
tr("Requested gate out track <b>%1</b> is not connected to segment <b>%2</b>.<br>"
"Out track <b>%3</b> was chosen instead.<br>"
"Look segment track connection from Stations Manager for more information"
" on available tracks.")
.arg(trackNum)
.arg(mOutGateEdit->text())
.arg(curStop.toGate.gateTrackNum));
}
//Success, close editor
// Success, close editor
emit nextSegmentChosen();
}
else
{
//Warn user and reset to previous chosen segment if any
// Warn user and reset to previous chosen segment if any
QMessageBox::warning(mEditor, tr("Stop Error"), tr("Cannot set segment track!"));
}
}
@ -337,26 +348,26 @@ void StopEditingHelper::checkOutGateTrack()
void StopEditingHelper::arrivalChanged(const QTime &arrival)
{
bool shiftPressed = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
QTime dep = depEdit->time();
if(!shiftPressed)
QTime dep = depEdit->time();
if (!shiftPressed)
{
//Shift departure by the same amount if SHIFT NOT pressed
// Shift departure by the same amount if SHIFT NOT pressed
int diff = curStop.arrival.msecsTo(arrival);
dep = dep.addMSecs(diff);
dep = dep.addMSecs(diff);
}
QTime minDep = arrival;
if(curStop.type == StopType::Normal)
if (curStop.type == StopType::Normal)
{
minDep = arrival.addSecs(60); //At least stop for 1 minute in Normal stops
minDep = arrival.addSecs(60); // At least stop for 1 minute in Normal stops
}
depEdit->blockSignals(true);
depEdit->setMinimumTime(minDep); //Set minimum before setting value
depEdit->setMinimumTime(minDep); // Set minimum before setting value
depEdit->setTime(dep);
depEdit->blockSignals(false);
//Reset diff to 0 for next call
curStop.arrival = arrival;
// Reset diff to 0 for next call
curStop.arrival = arrival;
curStop.departure = dep;
}
@ -367,16 +378,16 @@ void StopEditingHelper::departureChanged(const QTime &dep)
void StopEditingHelper::startOutTrackTimer()
{
//Give user a small time to scroll values in out gate track QSpinBox
//This will skip eventual non available tracks (not connected)
//On timeout check track and reset to old value if not available
// Give user a small time to scroll values in out gate track QSpinBox
// This will skip eventual non available tracks (not connected)
// On timeout check track and reset to old value if not available
stopOutTrackTimer();
mTimerOutTrack = startTimer(700);
}
void StopEditingHelper::stopOutTrackTimer()
{
if(mTimerOutTrack)
if (mTimerOutTrack)
{
killTimer(mTimerOutTrack);
mTimerOutTrack = 0;
@ -388,12 +399,12 @@ void StopEditingHelper::updateGateTrackSpin(const StopItem::Gate &toGate)
stopOutTrackTimer();
int outTrackCount = 0;
if(toGate.gateId)
if (toGate.gateId)
outTrackCount = stationOutGateMatchModel->getGateTrackCount(toGate.gateId);
//Prevent trigger valueChanged() signal
// Prevent trigger valueChanged() signal
mOutGateTrackSpin->blockSignals(true);
mOutGateTrackSpin->setMaximum(qMax(0, outTrackCount - 1)); //At least one track numbered 0
mOutGateTrackSpin->setMaximum(qMax(0, outTrackCount - 1)); // At least one track numbered 0
mOutGateTrackSpin->setValue(toGate.gateTrackNum);
mOutGateTrackSpin->blockSignals(false);
}

View File

@ -43,23 +43,37 @@ class StopEditingHelper : public QObject
{
Q_OBJECT
public:
StopEditingHelper(sqlite3pp::database &db, StopModel *m,
QSpinBox *outTrackSpin, QTimeEdit *arr, QTimeEdit *dep,
QWidget *editor = nullptr);
StopEditingHelper(sqlite3pp::database &db, StopModel *m, QSpinBox *outTrackSpin, QTimeEdit *arr,
QTimeEdit *dep, QWidget *editor = nullptr);
~StopEditingHelper();
void setStop(const StopItem& item, const StopItem& prev);
void setStop(const StopItem &item, const StopItem &prev);
void popupSegmentCombo();
QString getGateString(db_id gateId, bool reversed);
inline CustomCompletionLineEdit *getStationEdit() const { return mStationEdit; }
inline CustomCompletionLineEdit *getStTrackEdit() const { return mStTrackEdit; }
inline CustomCompletionLineEdit *getOutGateEdit() const { return mOutGateEdit; }
inline CustomCompletionLineEdit *getStationEdit() const
{
return mStationEdit;
}
inline CustomCompletionLineEdit *getStTrackEdit() const
{
return mStTrackEdit;
}
inline CustomCompletionLineEdit *getOutGateEdit() const
{
return mOutGateEdit;
}
inline const StopItem& getCurItem() const { return curStop; }
inline const StopItem& getPrevItem() const { return prevStop; }
inline const StopItem &getCurItem() const
{
return curStop;
}
inline const StopItem &getPrevItem() const
{
return prevStop;
}
protected:
void timerEvent(QTimerEvent *e) override;
@ -82,7 +96,7 @@ public slots:
void stopOutTrackTimer();
private:
void updateGateTrackSpin(const StopItem::Gate& toGate);
void updateGateTrackSpin(const StopItem::Gate &toGate);
private:
QWidget *mEditor;

View File

@ -27,19 +27,17 @@
#include <QSpinBox>
#include <QGridLayout>
StopEditor::StopEditor(sqlite3pp::database &db, StopModel *m, QWidget *parent) :
QFrame(parent),
m_closeOnSegmentChosen(false)
{
mOutGateTrackSpin = new QSpinBox;
arrEdit = new QTimeEdit;
depEdit = new QTimeEdit;
arrEdit = new QTimeEdit;
depEdit = new QTimeEdit;
helper = new StopEditingHelper(db, m,
mOutGateTrackSpin, arrEdit, depEdit,
this);
connect(helper, &StopEditingHelper::nextSegmentChosen, this, &StopEditor::onHelperSegmentChoosen);
helper = new StopEditingHelper(db, m, mOutGateTrackSpin, arrEdit, depEdit, this);
connect(helper, &StopEditingHelper::nextSegmentChosen, this,
&StopEditor::onHelperSegmentChoosen);
#ifdef PRINT_DBG_MSG
setObjectName(QStringLiteral("StopEditor (%1)").arg(qintptr(this)));
@ -51,7 +49,7 @@ StopEditor::StopEditor(sqlite3pp::database &db, StopModel *m, QWidget *parent) :
CustomCompletionLineEdit *mStTrackEdit = helper->getStTrackEdit();
CustomCompletionLineEdit *mOutGateEdit = helper->getOutGateEdit();
lay = new QGridLayout(this);
lay = new QGridLayout(this);
lay->addWidget(mStationEdit, 0, 0);
lay->addWidget(arrEdit, 0, 1);
lay->addWidget(depEdit, 0, 2);
@ -63,7 +61,8 @@ StopEditor::StopEditor(sqlite3pp::database &db, StopModel *m, QWidget *parent) :
setTabOrder(arrEdit, depEdit);
setTabOrder(depEdit, mOutGateEdit);
setToolTip(tr("To avoid recalculation of travel times when saving changes, hold SHIFT modifier while closing editor"));
setToolTip(tr("To avoid recalculation of travel times when saving changes, hold SHIFT modifier "
"while closing editor"));
}
StopEditor::~StopEditor()
@ -76,7 +75,7 @@ void StopEditor::setStop(const StopItem &item, const StopItem &prev)
{
helper->setStop(item, prev);
if(item.stationId == 0)
if (item.stationId == 0)
setFocusProxy(helper->getStationEdit());
}
@ -97,21 +96,21 @@ void StopEditor::setCloseOnSegmentChosen(bool value)
void StopEditor::popupSegmentCombo()
{
//This code is used when adding a new stop.
//When user clicks on 'AddHere' a new stop is added
//but before editing it, user must choose the railway segment
//that the job will take from former Last Stop.
// This code is used when adding a new stop.
// When user clicks on 'AddHere' a new stop is added
// but before editing it, user must choose the railway segment
// that the job will take from former Last Stop.
//(It was Last Stop before we added this stop, so it didn't have a 'next segment')
//1 - We popup lines combo from former last stop
//2 - When user chooses a line we close the editor (emit lineChosen())
//3 - We edit edit new Last Stop (EditNextItem)
// 1 - We popup lines combo from former last stop
// 2 - When user chooses a line we close the editor (emit lineChosen())
// 3 - We edit edit new Last Stop (EditNextItem)
setCloseOnSegmentChosen(true);
helper->popupSegmentCombo();
}
void StopEditor::onHelperSegmentChoosen()
{
//Forward signal and pass self instance
// Forward signal and pass self instance
emit nextSegmentChosen(this);
}

View File

@ -49,16 +49,19 @@ public:
StopEditor(sqlite3pp::database &db, StopModel *m, QWidget *parent = nullptr);
~StopEditor();
void setStop(const StopItem& item, const StopItem& prev);
void setStop(const StopItem &item, const StopItem &prev);
const StopItem& getCurItem() const;
const StopItem& getPrevItem() const;
const StopItem &getCurItem() const;
const StopItem &getPrevItem() const;
/*!
* \brief closeOnSegmentChosen
* \return true if editor should be closed after user has chosen a valid next segment
*/
inline bool closeOnSegmentChosen() const { return m_closeOnSegmentChosen; };
inline bool closeOnSegmentChosen() const
{
return m_closeOnSegmentChosen;
};
void setCloseOnSegmentChosen(bool value);
signals:

View File

@ -21,78 +21,74 @@
JobCrossingErrorMap::JobCrossingErrorMap()
{
}
void JobCrossingErrorMap::removeJob(db_id jobId)
{
auto job = map.find(jobId);
if(job == map.end())
return; //Not contained in map
if (job == map.end())
return; // Not contained in map
//Remove all errors referencing to us
for(const JobCrossingErrorData& err : qAsConst(job->errors))
// Remove all errors referencing to us
for (const JobCrossingErrorData &err : qAsConst(job->errors))
{
auto otherJob = map.find(err.otherJob.jobId);
if(otherJob == map.end())
continue; //Maybe already remove, skip
if (otherJob == map.end())
continue; // Maybe already remove, skip
//Remove all errors regarding job in otherJob
std::remove_if(otherJob->errors.begin(),
otherJob->errors.end(),
[jobId](const JobCrossingErrorData& otherErr) -> bool
{
return otherErr.otherJob.jobId == jobId;
});
// Remove all errors regarding job in otherJob
std::remove_if(otherJob->errors.begin(), otherJob->errors.end(),
[jobId](const JobCrossingErrorData &otherErr) -> bool
{ return otherErr.otherJob.jobId == jobId; });
if(otherJob->errors.isEmpty())
if (otherJob->errors.isEmpty())
{
//otherJob has no errors, remove it
// otherJob has no errors, remove it
map.erase(otherJob);
}
}
//Remove job
// Remove job
map.erase(job);
}
void JobCrossingErrorMap::renameJob(db_id newJobId, db_id oldJobId)
{
auto job = map.find(oldJobId);
if(job == map.end())
return; //Not contained in map
if (job == map.end())
return; // Not contained in map
//Uodate all errors referencing to us
for(const JobCrossingErrorData& err : qAsConst(job->errors))
// Uodate all errors referencing to us
for (const JobCrossingErrorData &err : qAsConst(job->errors))
{
auto otherJob = map.find(err.otherJob.jobId);
if(otherJob == map.end())
continue; //Maybe already remove, skip
if (otherJob == map.end())
continue; // Maybe already remove, skip
for(JobCrossingErrorData& otherErr : otherJob->errors)
for (JobCrossingErrorData &otherErr : otherJob->errors)
{
if(otherErr.otherJob.jobId == oldJobId)
if (otherErr.otherJob.jobId == oldJobId)
{
otherErr.otherJob.jobId = newJobId;
}
}
}
//Update job
auto errList = map.take(oldJobId);
// Update job
auto errList = map.take(oldJobId);
errList.job.jobId = newJobId;
map.insert(newJobId, errList);
}
void JobCrossingErrorMap::merge(const ErrorMap &results)
{
for(const JobCrossingErrorList& list : results)
for (const JobCrossingErrorList &list : results)
{
//First clear Job
// First clear Job
removeJob(list.job.jobId);
//Then add new errors if not empty (already duplicated)
if(!list.errors.isEmpty())
// Then add new errors if not empty (already duplicated)
if (!list.errors.isEmpty())
map.insert(list.job.jobId, list);
}
}

View File

@ -22,16 +22,16 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include <QMap>
#include <QTime>
# include <QMap>
# include <QTime>
#include "utils/types.h"
# include "utils/types.h"
struct JobCrossingErrorData
{
db_id jobId = 0;
db_id jobId = 0;
db_id stopId = 0;
db_id stopId = 0;
db_id stationId = 0;
JobStopEntry otherJob;
@ -44,7 +44,8 @@ struct JobCrossingErrorData
enum Type
{
NoError = 0,
JobCrossing, //NOTE: arrival refers to next job stop so it comes after departure (same for otherArr/Dep)
JobCrossing, // NOTE: arrival refers to next job stop so it comes after departure (same for
// otherArr/Dep)
JobPassing
};
@ -56,8 +57,14 @@ struct JobCrossingErrorList
JobEntry job;
QVector<JobCrossingErrorData> errors;
inline int childCount() const { return errors.size(); }
inline const JobCrossingErrorData *ptrForRow(int row) const { return &errors.at(row); }
inline int childCount() const
{
return errors.size();
}
inline const JobCrossingErrorData *ptrForRow(int row) const
{
return &errors.at(row);
}
};
/*!
@ -74,11 +81,14 @@ public:
JobCrossingErrorMap();
inline int topLevelCount() const { return map.size(); }
inline int topLevelCount() const
{
return map.size();
}
inline const JobCrossingErrorList *getTopLevelAtRow(int row) const
{
if(row >= topLevelCount())
if (row >= topLevelCount())
return nullptr;
return &(map.constBegin() + row).value();
}
@ -86,7 +96,7 @@ public:
inline const JobCrossingErrorList *getParent(JobCrossingErrorData *child) const
{
auto it = map.constFind(child->jobId);
if(it == map.constEnd())
if (it == map.constEnd())
return nullptr;
return &it.value();
}
@ -94,7 +104,7 @@ public:
inline int getParentRow(JobCrossingErrorData *child) const
{
auto it = map.constFind(child->jobId);
if(it == map.constEnd())
if (it == map.constEnd())
return -1;
return std::distance(map.constBegin(), it);
}
@ -103,7 +113,7 @@ public:
void renameJob(db_id newJobId, db_id oldJobId);
void merge(const ErrorMap& results);
void merge(const ErrorMap &results);
public:
ErrorMap map;

View File

@ -19,22 +19,22 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include "jobcrossingchecker.h"
# include "jobcrossingchecker.h"
#include "jobcrossingtask.h"
#include "jobcrossingmodel.h"
# include "jobcrossingtask.h"
# include "jobcrossingmodel.h"
#include "app/session.h"
#include "viewmanager/viewmanager.h"
# include "app/session.h"
# include "viewmanager/viewmanager.h"
#include "utils/owningqpointer.h"
#include <QMenu>
# include "utils/owningqpointer.h"
# include <QMenu>
JobCrossingChecker::JobCrossingChecker(sqlite3pp::database &db, QObject *parent) :
IBackgroundChecker(db, parent),
mDb(db)
{
eventType = int(JobCrossingResultEvent::_Type);
eventType = int(JobCrossingResultEvent::_Type);
errorsModel = new JobCrossingModel(this);
connect(Session, &MeetingSession::jobChanged, this, &JobCrossingChecker::onJobChanged);
@ -51,36 +51,37 @@ void JobCrossingChecker::clearModel()
static_cast<JobCrossingModel *>(errorsModel)->clear();
}
void JobCrossingChecker::showContextMenu(QWidget *panel, const QPoint &pos, const QModelIndex &idx) const
void JobCrossingChecker::showContextMenu(QWidget *panel, const QPoint &pos,
const QModelIndex &idx) const
{
const JobCrossingModel *model = static_cast<const JobCrossingModel *>(errorsModel);
auto item = model->getItem(idx);
if(!item)
auto item = model->getItem(idx);
if (!item)
return;
OwningQPointer<QMenu> menu = new QMenu(panel);
QAction *showInJobEditor = new QAction(tr("Show in Job Editor"), menu);
QAction *showInGraph = new QAction(tr("Show in graph"), menu);
QAction *showInJobEditor = new QAction(tr("Show in Job Editor"), menu);
QAction *showInGraph = new QAction(tr("Show in graph"), menu);
menu->addAction(showInJobEditor);
menu->addAction(showInGraph);
QAction *act = menu->exec(pos);
if(act == showInJobEditor)
if (act == showInJobEditor)
{
Session->getViewManager()->requestJobEditor(item->jobId, item->stopId);
}
else if(act == showInGraph)
else if (act == showInGraph)
{
//TODO: pass stopId
// TODO: pass stopId
Session->getViewManager()->requestJobSelection(item->jobId, true, true);
}
}
void JobCrossingChecker::sessionLoadedHandler()
{
if(AppSettings.getCheckCrossingWhenOpeningDB())
if (AppSettings.getCheckCrossingWhenOpeningDB())
startWorker();
}
@ -92,8 +93,8 @@ IQuittableTask *JobCrossingChecker::createMainWorker()
void JobCrossingChecker::setErrors(QEvent *e, bool merge)
{
auto model = static_cast<JobCrossingModel *>(errorsModel);
auto ev = static_cast<JobCrossingResultEvent *>(e);
if(merge)
auto ev = static_cast<JobCrossingResultEvent *>(e);
if (merge)
model->mergeErrors(ev->results);
else
model->setErrors(ev->results);
@ -104,8 +105,8 @@ void JobCrossingChecker::onJobChanged(db_id newJobId, db_id oldJobId)
auto model = static_cast<JobCrossingModel *>(errorsModel);
model->renameJob(oldJobId, newJobId);
//After renaming check job
if(AppSettings.getCheckCrossingOnJobEdit())
// After renaming check job
if (AppSettings.getCheckCrossingOnJobEdit())
startWorker();
}

View File

@ -22,9 +22,9 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include "backgroundmanager/ibackgroundchecker.h"
# include "backgroundmanager/ibackgroundchecker.h"
#include "utils/types.h"
# include "utils/types.h"
class JobCrossingChecker : public IBackgroundChecker
{
@ -34,7 +34,7 @@ public:
QString getName() const override;
void clearModel() override;
void showContextMenu(QWidget *panel, const QPoint& pos, const QModelIndex& idx) const override;
void showContextMenu(QWidget *panel, const QPoint &pos, const QModelIndex &idx) const override;
void sessionLoadedHandler() override;

View File

@ -21,25 +21,23 @@
#include "utils/jobcategorystrings.h"
static const char* error_texts[] = {
nullptr,
QT_TRANSLATE_NOOP("JobErrors", "Job crosses another Job on same track."),
QT_TRANSLATE_NOOP("JobErrors", "Job passes another Job on same track.")
};
static const char *error_texts[] = {
nullptr, QT_TRANSLATE_NOOP("JobErrors", "Job crosses another Job on same track."),
QT_TRANSLATE_NOOP("JobErrors", "Job passes another Job on same track.")};
class JobErrors
{
Q_DECLARE_TR_FUNCTIONS(JobErrors)
};
JobCrossingModel::JobCrossingModel(QObject *parent) : JobCrossingModelBase(parent)
JobCrossingModel::JobCrossingModel(QObject *parent) :
JobCrossingModelBase(parent)
{
}
QVariant JobCrossingModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(orientation == Qt::Horizontal && role == Qt::DisplayRole)
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section)
{
@ -63,11 +61,11 @@ QVariant JobCrossingModel::headerData(int section, Qt::Orientation orientation,
QVariant JobCrossingModel::data(const QModelIndex &idx, int role) const
{
if(!idx.isValid() || role != Qt::DisplayRole)
if (!idx.isValid() || role != Qt::DisplayRole)
return QVariant();
const JobCrossingErrorData *item = getItem(idx);
if(item)
if (item)
{
switch (idx.column())
{
@ -87,8 +85,8 @@ QVariant JobCrossingModel::data(const QModelIndex &idx, int role) const
}
else
{
//Caption
if(idx.row() >= m_data.topLevelCount() || idx.column() != 0)
// Caption
if (idx.row() >= m_data.topLevelCount() || idx.column() != 0)
return QVariant();
auto topLevel = m_data.getTopLevelAtRow(idx.row());
@ -135,5 +133,4 @@ void JobCrossingModel::renameJob(db_id newJobId, db_id oldJobId)
void JobCrossingModel::renameStation(db_id stationId, const QString &name)
{
}

View File

@ -22,14 +22,15 @@
#ifdef ENABLE_BACKGROUND_MANAGER
#include "utils/singledepthtreemodelhelper.h"
# include "utils/singledepthtreemodelhelper.h"
#include "job_crossing_data.h"
# include "job_crossing_data.h"
class JobCrossingModel;
typedef SingleDepthTreeModelHelper<JobCrossingModel, JobCrossingErrorMap, JobCrossingErrorData> JobCrossingModelBase;
typedef SingleDepthTreeModelHelper<JobCrossingModel, JobCrossingErrorMap, JobCrossingErrorData>
JobCrossingModelBase;
//TODO: make on-demand
// TODO: make on-demand
class JobCrossingModel : public JobCrossingModelBase
{
Q_OBJECT
@ -48,7 +49,8 @@ public:
JobCrossingModel(QObject *parent = nullptr);
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
@ -62,7 +64,7 @@ public:
void renameJob(db_id newJobId, db_id oldJobId);
void renameStation(db_id stationId, const QString& name);
void renameStation(db_id stationId, const QString &name);
};
#endif // ENABLE_BACKGROUND_MANAGER

View File

@ -22,41 +22,42 @@
#include <sqlite3pp/sqlite3pp.h>
using namespace sqlite3pp;
inline bool fillCrossingErrorData(query::rows& job, JobCrossingErrorData& err, bool first, JobCategory &outCat)
inline bool fillCrossingErrorData(query::rows &job, JobCrossingErrorData &err, bool first,
JobCategory &outCat)
{
err.stopId = job.get<db_id>(0);
err.otherJob.stopId = job.get<db_id>(1);
err.jobId = job.get<db_id>(2);
outCat = JobCategory(job.get<int>(3));
err.otherJob.jobId = job.get<db_id>(4);
err.stopId = job.get<db_id>(0);
err.otherJob.stopId = job.get<db_id>(1);
err.jobId = job.get<db_id>(2);
outCat = JobCategory(job.get<int>(3));
err.otherJob.jobId = job.get<db_id>(4);
err.otherJob.category = JobCategory(job.get<int>(5));
err.departure = job.get<QTime>(6);
err.arrival = job.get<QTime>(7);
err.otherDep = job.get<QTime>(8);
err.otherArr = job.get<QTime>(9);
bool passing = job.get<int>(10) == 1;
err.stationId = job.get<db_id>(11);
err.stationName = job.get<QString>(12);
err.departure = job.get<QTime>(6);
err.arrival = job.get<QTime>(7);
err.otherDep = job.get<QTime>(8);
err.otherArr = job.get<QTime>(9);
bool passing = job.get<int>(10) == 1;
err.stationId = job.get<db_id>(11);
err.stationName = job.get<QString>(12);
if(passing)
if (passing)
{
//In passings:
//job A starts before B but gets passed and ends after B
//job B starts after A but ends before
//B travel period is contained in A travel period
// In passings:
// job A starts before B but gets passed and ends after B
// job B starts after A but ends before
// B travel period is contained in A travel period
//We need stricter checking of time, one travel must be contained in the other
if(err.departure < err.otherDep && err.arrival < err.otherArr)
return false; //A travels before B, no passing
if(err.departure > err.otherDep && err.arrival > err.otherArr)
return false; //A travels after B, no passing
// We need stricter checking of time, one travel must be contained in the other
if (err.departure < err.otherDep && err.arrival < err.otherArr)
return false; // A travels before B, no passing
if (err.departure > err.otherDep && err.arrival > err.otherArr)
return false; // A travels after B, no passing
}
err.type = passing ? JobCrossingErrorData::JobPassing : JobCrossingErrorData::JobCrossing;
if(!first)
if (!first)
{
//Swap points of view
// Swap points of view
qSwap(err.jobId, err.otherJob.jobId);
qSwap(outCat, err.otherJob.category);
qSwap(err.stopId, err.otherJob.stopId);
@ -67,43 +68,45 @@ inline bool fillCrossingErrorData(query::rows& job, JobCrossingErrorData& err, b
return true;
}
JobCrossingResultEvent::JobCrossingResultEvent(JobCrossingTask *worker, const JobCrossingErrorMap::ErrorMap &data, bool merge) :
JobCrossingResultEvent::JobCrossingResultEvent(JobCrossingTask *worker,
const JobCrossingErrorMap::ErrorMap &data,
bool merge) :
GenericTaskEvent(_Type, worker),
results(data),
mergeErrors(merge)
{
}
JobCrossingTask::JobCrossingTask(sqlite3pp::database &db, QObject *receiver, const QVector<db_id>& jobs) :
JobCrossingTask::JobCrossingTask(sqlite3pp::database &db, QObject *receiver,
const QVector<db_id> &jobs) :
IQuittableTask(receiver),
mDb(db),
jobsToCheck(jobs)
{
}
void JobCrossingTask::run()
{
//TODO: allow checking a single job
// TODO: allow checking a single job
//Look for passing or crossings on same segment
query q(mDb, "SELECT s1.id, s2.id, s1.job_id, j1.category, s2.job_id, j2.category,"
"s1.departure, MIN(s1_next.arrival),"
"s2.departure, MIN(s2_next.arrival),"
"g1.gate_id=g2.gate_id," //1 = passing, 0 = crossing (opposite direction)
"s1.station_id, stations.name"
" FROM stops s1"
" JOIN stops s1_next ON s1_next.job_id=s1.job_id AND s1_next.arrival>s1.arrival"
" JOIN stops s2 ON s2.next_segment_conn_id=s1.next_segment_conn_id AND s2.id<>s1.id"
" JOIN stops s2_next ON s2_next.job_id=s2.job_id AND s2_next.arrival>s2.arrival"
" JOIN jobs j1 ON j1.id=s1.job_id"
" JOIN jobs j2 ON j2.id=s2.job_id"
" JOIN station_gate_connections g1 ON g1.id=s1.out_gate_conn"
" JOIN station_gate_connections g2 ON g2.id=s2.out_gate_conn"
" JOIN stations ON stations.id=s1.station_id"
" GROUP BY s1.id,s2.id"
" HAVING s1.departure<=s2_next.arrival AND s1_next.arrival>=s2.departure");
// Look for passing or crossings on same segment
query q(mDb,
"SELECT s1.id, s2.id, s1.job_id, j1.category, s2.job_id, j2.category,"
"s1.departure, MIN(s1_next.arrival),"
"s2.departure, MIN(s2_next.arrival),"
"g1.gate_id=g2.gate_id," // 1 = passing, 0 = crossing (opposite direction)
"s1.station_id, stations.name"
" FROM stops s1"
" JOIN stops s1_next ON s1_next.job_id=s1.job_id AND s1_next.arrival>s1.arrival"
" JOIN stops s2 ON s2.next_segment_conn_id=s1.next_segment_conn_id AND s2.id<>s1.id"
" JOIN stops s2_next ON s2_next.job_id=s2.job_id AND s2_next.arrival>s2.arrival"
" JOIN jobs j1 ON j1.id=s1.job_id"
" JOIN jobs j2 ON j2.id=s2.job_id"
" JOIN station_gate_connections g1 ON g1.id=s1.out_gate_conn"
" JOIN station_gate_connections g2 ON g2.id=s2.out_gate_conn"
" JOIN stations ON stations.id=s1.station_id"
" GROUP BY s1.id,s2.id"
" HAVING s1.departure<=s2_next.arrival AND s1_next.arrival>=s2.departure");
QMap<db_id, JobCrossingErrorList> errorMap;
checkCrossAndPassSegments(errorMap, q);
@ -147,25 +150,26 @@ void JobCrossingTask::run()
// }
}
void JobCrossingTask::checkCrossAndPassSegments(JobCrossingErrorMap::ErrorMap &errMap, sqlite3pp::query &q)
void JobCrossingTask::checkCrossAndPassSegments(JobCrossingErrorMap::ErrorMap &errMap,
sqlite3pp::query &q)
{
for(auto job : q)
for (auto job : q)
{
JobCrossingErrorData err;
JobCategory category = JobCategory::NCategories;
if(!fillCrossingErrorData(job, err, true, category))
if (!fillCrossingErrorData(job, err, true, category))
continue;
auto it = errMap.find(err.jobId);
if(it == errMap.end())
if (it == errMap.end())
{
//Insert Job into map for first time
// Insert Job into map for first time
JobCrossingErrorList list;
list.job.jobId = err.jobId;
list.job.jobId = err.jobId;
list.job.category = category;
it = errMap.insert(list.job.jobId, list);
it = errMap.insert(list.job.jobId, list);
}
it.value().errors.append(err);

View File

@ -31,7 +31,7 @@
namespace sqlite3pp {
class database;
class query;
}
} // namespace sqlite3pp
class JobCrossingTask;
@ -40,7 +40,8 @@ class JobCrossingResultEvent : public GenericTaskEvent
public:
static const Type _Type = Type(CustomEvents::JobsCrossingCheckResult);
JobCrossingResultEvent(JobCrossingTask *worker, const JobCrossingErrorMap::ErrorMap &data, bool merge);
JobCrossingResultEvent(JobCrossingTask *worker, const JobCrossingErrorMap::ErrorMap &data,
bool merge);
QMap<db_id, JobCrossingErrorList> results;
bool mergeErrors;
@ -49,11 +50,11 @@ public:
class JobCrossingTask : public IQuittableTask
{
public:
JobCrossingTask(sqlite3pp::database &db, QObject *receiver, const QVector<db_id>& jobs);
JobCrossingTask(sqlite3pp::database &db, QObject *receiver, const QVector<db_id> &jobs);
void run() override;
void checkCrossAndPassSegments(JobCrossingErrorMap::ErrorMap& errMap, sqlite3pp::query &q);
void checkCrossAndPassSegments(JobCrossingErrorMap::ErrorMap &errMap, sqlite3pp::query &q);
private:
sqlite3pp::database &mDb;

View File

@ -45,15 +45,17 @@ JobsManager::JobsManager(QWidget *parent) :
QVBoxLayout *l = new QVBoxLayout(this);
setMinimumSize(750, 300);
QToolBar *toolBar = new QToolBar(this);
QToolBar *toolBar = new QToolBar(this);
QAction *actNewJob = toolBar->addAction(tr("New Job"), this, &JobsManager::onNewJob);
actRemoveJob = toolBar->addAction(tr("Remove"), this, &JobsManager::onRemove);
actNewJobSamePath = toolBar->addAction(tr("New Same Path"), this, &JobsManager::onNewJobSamePath);
actRemoveJob = toolBar->addAction(tr("Remove"), this, &JobsManager::onRemove);
actNewJobSamePath =
toolBar->addAction(tr("New Same Path"), this, &JobsManager::onNewJobSamePath);
toolBar->addSeparator();
actEditJob = toolBar->addAction(tr("Edit"), this, &JobsManager::onEditJob);
actEditJob = toolBar->addAction(tr("Edit"), this, &JobsManager::onEditJob);
actShowJobInGraph = toolBar->addAction(tr("Show Graph"), this, &JobsManager::onShowJobGraph);
toolBar->addSeparator();
QAction *actRemoveAll = toolBar->addAction(tr("Remove All"), this, &JobsManager::onRemoveAllJobs);
QAction *actRemoveAll =
toolBar->addAction(tr("Remove All"), this, &JobsManager::onRemoveAllJobs);
l->addWidget(toolBar);
view = new QTableView;
@ -61,7 +63,7 @@ JobsManager::JobsManager(QWidget *parent) :
connect(view, &QTableView::doubleClicked, this, &JobsManager::editJobAtRow);
l->addWidget(view);
//Custom sorting and filtering
// Custom sorting and filtering
FilterHeaderView *header = new FilterHeaderView(view);
header->installOnTable(view);
@ -72,12 +74,13 @@ JobsManager::JobsManager(QWidget *parent) :
l->addWidget(ps);
ps->setModel(jobsModel);
connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &JobsManager::onSelectionChanged);
connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&JobsManager::onSelectionChanged);
connect(jobsModel, &QAbstractItemModel::modelReset, this, &JobsManager::onSelectionChanged);
jobsModel->refreshData();
//Action Tooltips
// Action Tooltips
actNewJob->setToolTip(tr("Create new Job and open Job Editor"));
actRemoveJob->setToolTip(tr("Delete selected Job"));
actNewJobSamePath->setToolTip(tr("Create new Job with same path of selected one"));
@ -89,10 +92,10 @@ JobsManager::JobsManager(QWidget *parent) :
setWindowTitle("Jobs Manager");
}
void JobsManager::editJobAtRow(const QModelIndex& idx)
void JobsManager::editJobAtRow(const QModelIndex &idx)
{
db_id jobId = jobsModel->getIdAtRow(idx.row());
if(!jobId)
if (!jobId)
return;
Session->getViewManager()->requestJobEditor(jobId);
}
@ -105,64 +108,65 @@ void JobsManager::onNewJob()
void JobsManager::onRemove()
{
QModelIndex idx = view->currentIndex();
if(!idx.isValid())
if (!idx.isValid())
return;
db_id jobId = jobsModel->getIdAtRow(idx.row());
db_id jobId = jobsModel->getIdAtRow(idx.row());
JobCategory jobCat = jobsModel->getShiftAnCatAtRow(idx.row()).second;
QString jobName = JobCategoryName::jobName(jobId, jobCat);
QString jobName = JobCategoryName::jobName(jobId, jobCat);
int ret = QMessageBox::question(this,
tr("Job deletion"),
tr("Are you sure to delete job %1?").arg(jobName),
QMessageBox::Yes | QMessageBox::Cancel);
if(ret == QMessageBox::Yes)
int ret = QMessageBox::question(this, tr("Job deletion"),
tr("Are you sure to delete job %1?").arg(jobName),
QMessageBox::Yes | QMessageBox::Cancel);
if (ret == QMessageBox::Yes)
{
if(!JobsHelper::removeJob(Session->m_Db, jobId))
if (!JobsHelper::removeJob(Session->m_Db, jobId))
{
qWarning() << "Error while deleting job:" << jobId << "from JobManager" << Session->m_Db.error_msg();
//ERRORMSG: message box or statusbar
qWarning() << "Error while deleting job:" << jobId << "from JobManager"
<< Session->m_Db.error_msg();
// ERRORMSG: message box or statusbar
}
}
}
void JobsManager::onRemoveAllJobs()
{
int ret = QMessageBox::question(this, tr("Delete all jobs?"),
tr("Are you really sure you want to delete all jobs from this session?"));
if(ret == QMessageBox::Yes)
int ret = QMessageBox::question(
this, tr("Delete all jobs?"),
tr("Are you really sure you want to delete all jobs from this session?"));
if (ret == QMessageBox::Yes)
JobsHelper::removeAllJobs(Session->m_Db);
}
void JobsManager::onNewJobSamePath()
{
QModelIndex idx = view->currentIndex();
if(!idx.isValid())
if (!idx.isValid())
return;
db_id jobId = jobsModel->getIdAtRow(idx.row());
if(!jobId)
if (!jobId)
return;
JobCategory jobCat = jobsModel->getShiftAnCatAtRow(idx.row()).second;
auto times = jobsModel->getOrigAndDestTimeAtRow(idx.row());
JobCategory jobCat = jobsModel->getShiftAnCatAtRow(idx.row()).second;
auto times = jobsModel->getOrigAndDestTimeAtRow(idx.row());
OwningQPointer<NewJobSamePathDlg> dlg = new NewJobSamePathDlg(this);
dlg->setSourceJob(jobId, jobCat, times.first, times.second);
if(dlg->exec() != QDialog::Accepted || !dlg)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
const QTime newStart = dlg->getNewStartTime();
const int secsOffset = times.first.secsTo(newStart);
db_id newJobId = 0;
if(!JobsHelper::createNewJob(Session->m_Db, newJobId, jobCat))
db_id newJobId = 0;
if (!JobsHelper::createNewJob(Session->m_Db, newJobId, jobCat))
return;
JobsHelper::copyStops(Session->m_Db, jobId, newJobId, secsOffset,
dlg->shouldCopyRs(), dlg->shouldReversePath());
JobsHelper::copyStops(Session->m_Db, jobId, newJobId, secsOffset, dlg->shouldCopyRs(),
dlg->shouldReversePath());
//Let user edit newly created job
// Let user edit newly created job
Session->getViewManager()->requestJobEditor(newJobId);
}
@ -174,16 +178,16 @@ void JobsManager::onEditJob()
void JobsManager::onShowJobGraph()
{
QModelIndex idx = view->currentIndex();
if(!idx.isValid())
if (!idx.isValid())
return;
db_id jobId = jobsModel->getIdAtRow(idx.row());
if(!jobId)
if (!jobId)
return;
Session->getViewManager()->requestJobSelection(jobId, true, true);
//Minimize JobsManager to make graph visible
// Minimize JobsManager to make graph visible
showMinimized();
}

View File

@ -46,9 +46,9 @@ JobListModel::JobListModel(sqlite3pp::database &db, QObject *parent) :
QVariant JobListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(role == Qt::DisplayRole)
if (role == Qt::DisplayRole)
{
if(orientation == Qt::Horizontal)
if (orientation == Qt::Horizontal)
{
switch (section)
{
@ -84,16 +84,16 @@ QVariant JobListModel::data(const QModelIndex &idx, int role) const
if (!idx.isValid() || row >= curItemCount || idx.column() >= NCols)
return QVariant();
if(row < cacheFirstRow || row >= cacheFirstRow + cache.size())
if (row < cacheFirstRow || row >= cacheFirstRow + cache.size())
{
//Fetch above or below current cache
// Fetch above or below current cache
const_cast<JobListModel *>(this)->fetchRow(row);
//Temporarily return null
// Temporarily return null
return role == Qt::DisplayRole ? QVariant("...") : QVariant();
}
const JobItem& item = cache.at(row - cacheFirstRow);
const JobItem &item = cache.at(row - cacheFirstRow);
switch (role)
{
@ -122,7 +122,7 @@ QVariant JobListModel::data(const QModelIndex &idx, int role) const
}
case Qt::TextAlignmentRole:
{
if(idx.column() == IdCol)
if (idx.column() == IdCol)
{
return Qt::AlignVCenter + Qt::AlignRight;
}
@ -135,14 +135,14 @@ QVariant JobListModel::data(const QModelIndex &idx, int role) const
void JobListModel::setSortingColumn(int col)
{
if(sortColumn == col || col == OriginSt || col == DestinationSt || col >= NCols)
if (sortColumn == col || col == OriginSt || col == DestinationSt || col >= NCols)
return;
clearCache();
sortColumn = col;
sortColumn = col;
QModelIndex first = index(0, 0);
QModelIndex last = index(curItemCount - 1, NCols - 1);
QModelIndex last = index(curItemCount - 1, NCols - 1);
emit dataChanged(first, last);
}
@ -167,8 +167,8 @@ bool JobListModel::setFilterAtCol(int col, const QString &str)
{
case IdCol:
{
if(isNull)
return false; //Cannot have NULL Job ID
if (isNull)
return false; // Cannot have NULL Job ID
m_jobIdFilter = str;
break;
}
@ -187,7 +187,7 @@ bool JobListModel::setFilterAtCol(int col, const QString &str)
void JobListModel::onJobAddedOrRemoved()
{
refreshData(); //Recalc row count
refreshData(); // Recalc row count
}
qint64 JobListModel::recalcTotalItemCount()
@ -203,7 +203,7 @@ qint64 JobListModel::recalcTotalItemCount()
void JobListModel::buildQuery(sqlite3pp::query &q, int sortCol, int offset, bool fullData)
{
QByteArray sql;
if(fullData)
if (fullData)
{
sql = "SELECT jobs.id, jobs.category, jobs.shift_id, jobshifts.name,"
"MIN(s1.departure), s1.station_id, MAX(s2.arrival), s2.station_id"
@ -216,44 +216,44 @@ void JobListModel::buildQuery(sqlite3pp::query &q, int sortCol, int offset, bool
sql = "SELECT COUNT(1) FROM jobs";
}
//If counting but filtering by shift name (not null) we need to JOIN jobshifts
// If counting but filtering by shift name (not null) we need to JOIN jobshifts
bool shiftFilterIsNull = m_shiftFilter.startsWith(nullFilterStr, Qt::CaseInsensitive);
if(fullData || (!shiftFilterIsNull && !m_shiftFilter.isEmpty()))
if (fullData || (!shiftFilterIsNull && !m_shiftFilter.isEmpty()))
sql += " LEFT JOIN jobshifts ON jobshifts.id=jobs.shift_id";
bool whereClauseAdded = false;
if(!m_jobIdFilter.isEmpty())
if (!m_jobIdFilter.isEmpty())
{
sql.append(" WHERE jobs.id LIKE ?3");
whereClauseAdded = true;
}
if(!m_shiftFilter.isEmpty())
if (!m_shiftFilter.isEmpty())
{
if(whereClauseAdded)
if (whereClauseAdded)
sql.append(" AND ");
else
sql.append(" WHERE ");
if(shiftFilterIsNull)
if (shiftFilterIsNull)
sql.append("jobs.shift_id IS NULL");
else
sql.append("jobshifts.name LIKE ?4");
}
if(fullData)
if (fullData)
{
//Group by Job
// Group by Job
sql.append(" GROUP BY jobs.id");
//Apply sorting
// Apply sorting
const char *sortColExpr = nullptr;
switch (sortCol)
{
case IdCol:
{
sortColExpr = "jobs.id"; //Order by 1 column, no where clause
sortColExpr = "jobs.id"; // Order by 1 column, no where clause
break;
}
case Category:
@ -282,23 +282,23 @@ void JobListModel::buildQuery(sqlite3pp::query &q, int sortCol, int offset, bool
sql += sortColExpr;
sql += " LIMIT ?1";
if(offset)
if (offset)
sql += " OFFSET ?2";
}
q.prepare(sql);
if(fullData)
if (fullData)
{
//Apply offset and batch size
// Apply offset and batch size
q.bind(1, BatchSize);
if(offset)
if (offset)
q.bind(2, offset);
}
//Apply filters
// Apply filters
QByteArray jobFilter;
if(!m_jobIdFilter.isEmpty())
if (!m_jobIdFilter.isEmpty())
{
jobFilter.reserve(m_jobIdFilter.size() + 2);
jobFilter.append('%');
@ -308,7 +308,7 @@ void JobListModel::buildQuery(sqlite3pp::query &q, int sortCol, int offset, bool
}
QByteArray shiftFilter;
if(!m_shiftFilter.isEmpty() && !shiftFilterIsNull)
if (!m_shiftFilter.isEmpty() && !shiftFilterIsNull)
{
shiftFilter.reserve(m_shiftFilter.size() + 2);
shiftFilter.append('%');
@ -318,7 +318,7 @@ void JobListModel::buildQuery(sqlite3pp::query &q, int sortCol, int offset, bool
}
}
void JobListModel::internalFetch(int first, int sortCol, int /*valRow*/, const QVariant &/*val*/)
void JobListModel::internalFetch(int first, int sortCol, int /*valRow*/, const QVariant & /*val*/)
{
query q(mDb);
@ -331,29 +331,29 @@ void JobListModel::internalFetch(int first, int sortCol, int /*valRow*/, const Q
QVector<JobItem> vec(BatchSize);
//QString are implicitly shared, use QHash to temporary store them instead
//of creating new ones for each JobItem
// QString are implicitly shared, use QHash to temporary store them instead
// of creating new ones for each JobItem
QHash<db_id, QString> shiftHash;
QHash<db_id, QString> stationHash;
auto it = q.begin();
const auto end = q.end();
auto it = q.begin();
const auto end = q.end();
int i = 0;
int i = 0;
const int increment = 1;
for(; it != end; ++it)
for (; it != end; ++it)
{
auto r = *it;
auto r = *it;
JobItem &item = vec[i];
item.jobId = r.get<db_id>(0);
item.jobId = r.get<db_id>(0);
item.category = JobCategory(r.get<int>(1));
item.shiftId = r.get<db_id>(2);
item.shiftId = r.get<db_id>(2);
if(item.shiftId)
if (item.shiftId)
{
auto shift = shiftHash.constFind(item.shiftId);
if(shift == shiftHash.constEnd())
if (shift == shiftHash.constEnd())
{
shift = shiftHash.insert(item.shiftId, r.get<QString>(3));
}
@ -362,13 +362,13 @@ void JobListModel::internalFetch(int first, int sortCol, int /*valRow*/, const Q
item.originTime = r.get<QTime>(4);
item.originStId = r.get<db_id>(5);
item.destTime = r.get<QTime>(6);
item.destStId = r.get<db_id>(7);
item.destTime = r.get<QTime>(6);
item.destStId = r.get<db_id>(7);
if(item.originStId)
if (item.originStId)
{
auto st = stationHash.constFind(item.originStId);
if(st == stationHash.constEnd())
if (st == stationHash.constEnd())
{
q_stationName.bind(1, item.originStId);
q_stationName.step();
@ -378,10 +378,10 @@ void JobListModel::internalFetch(int first, int sortCol, int /*valRow*/, const Q
item.origStName = st.value();
}
if(item.destStId)
if (item.destStId)
{
auto st = stationHash.constFind(item.destStId);
if(st == stationHash.constEnd())
if (st == stationHash.constEnd())
{
q_stationName.bind(1, item.destStId);
q_stationName.step();
@ -394,7 +394,7 @@ void JobListModel::internalFetch(int first, int sortCol, int /*valRow*/, const Q
i += increment;
}
if(i < BatchSize)
if (i < BatchSize)
vec.remove(i, BatchSize - i);
postResult(vec, first);

View File

@ -48,9 +48,13 @@ class JobListModel : public IPagedItemModelImpl<JobListModel, JobListModelItem>
{
Q_OBJECT
public:
enum { BatchSize = 100 };
enum
{
BatchSize = 100
};
enum Columns {
enum Columns
{
IdCol = 0,
Category,
ShiftCol,
@ -69,7 +73,8 @@ public:
// QAbstractTableModel
// Header:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
@ -77,35 +82,35 @@ public:
virtual void setSortingColumn(int col) override;
//Filter
// Filter
std::pair<QString, FilterFlags> getFilterAtCol(int col) override;
bool setFilterAtCol(int col, const QString& str) override;
bool setFilterAtCol(int col, const QString &str) override;
// Convinience
inline db_id getIdAtRow(int row) const
{
if (row < cacheFirstRow || row >= cacheFirstRow + cache.size())
return 0; //Invalid
return 0; // Invalid
const JobItem& item = cache.at(row - cacheFirstRow);
const JobItem &item = cache.at(row - cacheFirstRow);
return item.jobId;
}
inline QPair<db_id, JobCategory> getShiftAnCatAtRow(int row) const
{
if (row < cacheFirstRow || row >= cacheFirstRow + cache.size())
return {0, JobCategory::NCategories}; //Invalid
return {0, JobCategory::NCategories}; // Invalid
const JobItem& item = cache.at(row - cacheFirstRow);
const JobItem &item = cache.at(row - cacheFirstRow);
return {item.shiftId, item.category};
}
inline QPair<QTime, QTime> getOrigAndDestTimeAtRow(int row) const
{
if (row < cacheFirstRow || row >= cacheFirstRow + cache.size())
return {}; //Invalid
return {}; // Invalid
const JobItem& item = cache.at(row - cacheFirstRow);
const JobItem &item = cache.at(row - cacheFirstRow);
return {item.originTime, item.destTime};
}

View File

@ -38,11 +38,11 @@ JobMatchModel::JobMatchModel(database &db, QObject *parent) :
int JobMatchModel::columnCount(const QModelIndex &p) const
{
if(p.isValid())
if (p.isValid())
return 0;
if(!m_stopStationId)
return NCols - 1; //Hide stop arrival if no station filter is set
if (!m_stopStationId)
return NCols - 1; // Hide stop arrival if no station filter is set
return NCols;
}
@ -59,7 +59,7 @@ QVariant JobMatchModel::data(const QModelIndex &idx, int role) const
{
case JobCategoryCol:
{
if(isEllipsesRow(idx.row()))
if (isEllipsesRow(idx.row()))
{
break;
}
@ -67,7 +67,7 @@ QVariant JobMatchModel::data(const QModelIndex &idx, int role) const
}
case JobNumber:
{
if(isEllipsesRow(idx.row()))
if (isEllipsesRow(idx.row()))
{
return ellipsesString;
}
@ -75,8 +75,8 @@ QVariant JobMatchModel::data(const QModelIndex &idx, int role) const
}
case StopArrivalCol:
{
if(!m_stopStationId)
break; //Do not show stop arrival if not filtering by station
if (!m_stopStationId)
break; // Do not show stop arrival if not filtering by station
return items[idx.row()].stopArrival;
}
@ -85,7 +85,7 @@ QVariant JobMatchModel::data(const QModelIndex &idx, int role) const
}
case Qt::ForegroundRole:
{
if(isEllipsesRow(idx.row()))
if (isEllipsesRow(idx.row()))
{
break;
}
@ -124,7 +124,7 @@ QVariant JobMatchModel::data(const QModelIndex &idx, int role) const
void JobMatchModel::autoSuggest(const QString &text)
{
mQuery.clear();
if(!text.isEmpty())
if (!text.isEmpty())
{
mQuery.reserve(text.size() + 2);
mQuery.append('%');
@ -137,36 +137,36 @@ void JobMatchModel::autoSuggest(const QString &text)
void JobMatchModel::refreshData()
{
if(!mDb.db())
if (!mDb.db())
return;
beginResetModel();
char emptyQuery = '%';
if(mQuery.isEmpty())
if (mQuery.isEmpty())
sqlite3_bind_text(q_getMatches.stmt(), 1, &emptyQuery, 1, SQLITE_STATIC);
else
sqlite3_bind_text(q_getMatches.stmt(), 1, mQuery, mQuery.size(), SQLITE_STATIC);
if(m_exceptJobId)
if (m_exceptJobId)
q_getMatches.bind(2, m_exceptJobId);
if(m_stopStationId)
if (m_stopStationId)
{
q_getMatches.bind(3, m_stopStationId);
if(!m_maxStopArrival.isNull())
if (!m_maxStopArrival.isNull())
q_getMatches.bind(4, m_maxStopArrival);
}
auto end = q_getMatches.end();
auto it = q_getMatches.begin();
int i = 0;
for(; i < MaxMatchItems && it != end; i++)
auto it = q_getMatches.begin();
int i = 0;
for (; i < MaxMatchItems && it != end; i++)
{
items[i].stop.jobId = (*it).get<db_id>(0);
items[i].stop.jobId = (*it).get<db_id>(0);
items[i].stop.category = JobCategory((*it).get<int>(1));
if(m_stopStationId)
if (m_stopStationId)
{
items[i].stop.stopId = (*it).get<db_id>(2);
items[i].stopArrival = (*it).get<QTime>(3);
@ -176,13 +176,13 @@ void JobMatchModel::refreshData()
}
size = i;
if(hasEmptyRow)
size++; //Items + Empty, add 1 row
if (hasEmptyRow)
size++; // Items + Empty, add 1 row
if(it != end)
if (it != end)
{
//There would be still rows, show Ellipses
size++; //Items + Empty + Ellispses
// There would be still rows, show Ellipses
size++; // Items + Empty + Ellispses
}
q_getMatches.reset();
@ -193,12 +193,12 @@ void JobMatchModel::refreshData()
QString JobMatchModel::getName(db_id jobId) const
{
if(!mDb.db())
if (!mDb.db())
return QString();
query q(mDb, "SELECT category FROM jobs WHERE id=?");
q.bind(1, jobId);
if(q.step() != SQLITE_ROW)
if (q.step() != SQLITE_ROW)
return QString();
JobCategory jobCat = JobCategory(q.getRows().get<int>(0));
@ -207,32 +207,31 @@ QString JobMatchModel::getName(db_id jobId) const
db_id JobMatchModel::getIdAtRow(int row) const
{
if(m_defaultId == StopId)
if (m_defaultId == StopId)
return items[row].stop.stopId;
return items[row].stop.jobId;
}
QString JobMatchModel::getNameAtRow(int row) const
{
return JobCategoryName::jobName(items[row].stop.jobId,
items[row].stop.category);
return JobCategoryName::jobName(items[row].stop.jobId, items[row].stop.category);
}
void JobMatchModel::setFilter(db_id exceptJobId, db_id stopsInStationId, const QTime &maxStopArr)
{
m_exceptJobId = exceptJobId;
m_stopStationId = stopsInStationId;
m_exceptJobId = exceptJobId;
m_stopStationId = stopsInStationId;
m_maxStopArrival = maxStopArr;
QByteArray sql;
if(m_stopStationId)
if (m_stopStationId)
{
//Filter by stopping station
// Filter by stopping station
sql = "SELECT stops.job_id, jobs.category, stops.id, stops.arrival"
" FROM stops JOIN jobs ON jobs.id=stops.job_id"
" WHERE stops.station_id=?3 AND";
if(!m_maxStopArrival.isNull())
if (!m_maxStopArrival.isNull())
sql += " stops.arrival < ?4 AND";
}
else
@ -240,18 +239,18 @@ void JobMatchModel::setFilter(db_id exceptJobId, db_id stopsInStationId, const Q
sql = "SELECT jobs.id, jobs.category FROM jobs WHERE";
}
if(exceptJobId)
if (exceptJobId)
{
//Filter out unwanted job ID
// Filter out unwanted job ID
sql += " jobs.id<>?2 AND";
}
//Filter by name (job ID number)
//FIXME: filter also by category with regexp
// Filter by name (job ID number)
// FIXME: filter also by category with regexp
sql += " jobs.id LIKE ?1";
sql += " ORDER BY ";
if(m_stopStationId)
if (m_stopStationId)
{
sql += "stops.arrival, ";
}

View File

@ -29,8 +29,8 @@
#include <sqlite3pp/sqlite3pp.h>
using namespace sqlite3pp;
//TODO: share common code with SearchResultModel
//TODO: allow filter byy job category
// TODO: share common code with SearchResultModel
// TODO: allow filter byy job category
/*!
* \brief Match model for Jobs
@ -67,7 +67,7 @@ public:
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
// ISqlFKMatchModel:
void autoSuggest(const QString& text) override;
void autoSuggest(const QString &text) override;
virtual void refreshData() override;
QString getName(db_id jobId) const override;
@ -75,7 +75,7 @@ public:
QString getNameAtRow(int row) const override;
// JobMatchModel:
void setFilter(db_id exceptJobId, db_id stopsInStationId, const QTime& maxStopArr);
void setFilter(db_id exceptJobId, db_id stopsInStationId, const QTime &maxStopArr);
void setDefaultId(DefaultId defaultId);

View File

@ -28,20 +28,20 @@
bool JobsHelper::createNewJob(sqlite3pp::database &db, db_id &outJobId, JobCategory cat)
{
sqlite3pp::command q_newJob(db, "INSERT INTO jobs(id,category,shift_id) VALUES(?,?,NULL)");
if(outJobId)
if (outJobId)
q_newJob.bind(1, outJobId);
else
q_newJob.bind(1); //Bind NULL
q_newJob.bind(1); // Bind NULL
q_newJob.bind(2, int(cat));
sqlite3_mutex *mutex = sqlite3_db_mutex(db.db());
sqlite3_mutex_enter(mutex);
int rc = q_newJob.execute();
int rc = q_newJob.execute();
db_id jobId = db.last_insert_rowid();
sqlite3_mutex_leave(mutex);
q_newJob.reset();
if(rc != SQLITE_OK)
if (rc != SQLITE_OK)
{
qWarning() << rc << db.error_msg();
outJobId = 0;
@ -59,44 +59,45 @@ bool JobsHelper::removeJob(sqlite3pp::database &db, db_id jobId)
{
sqlite3pp::query q(db, "SELECT shift_id FROM jobs WHERE id=?");
q.bind(1, jobId);
if(q.step() != SQLITE_ROW)
if (q.step() != SQLITE_ROW)
{
return false; //Job doesn't exist
return false; // Job doesn't exist
}
db_id shiftId = q.getRows().get<db_id>(0);
q.reset();
if(shiftId != 0)
if (shiftId != 0)
{
//Remove job from shift
// Remove job from shift
emit Session->shiftJobsChanged(shiftId, jobId);
}
//Get stations in which job stopped or transited
// Get stations in which job stopped or transited
QSet<db_id> stationsToUpdate;
q.prepare("SELECT station_id FROM stops WHERE job_id=?"
" UNION "
"SELECT station_id FROM old_stops WHERE job_id=?");
q.bind(1, jobId);
for(auto st : q)
for (auto st : q)
{
stationsToUpdate.insert(st.get<db_id>(0));
}
//Get Rollingstock used by job
// Get Rollingstock used by job
QSet<db_id> rsToUpdate;
q.prepare("SELECT coupling.rs_id FROM stops JOIN coupling ON coupling.stop_id=stops.id"
" WHERE stops.job_id=?"
" UNION "
"SELECT old_coupling.rs_id FROM old_stops JOIN old_coupling ON old_coupling.stop_id=old_stops.id"
"SELECT old_coupling.rs_id FROM old_stops JOIN old_coupling ON "
"old_coupling.stop_id=old_stops.id"
" WHERE old_stops.job_id=?");
q.bind(1, jobId);
for(auto rs : q)
for (auto rs : q)
{
rsToUpdate.insert(rs.get<db_id>(0));
}
//Remove job
// Remove job
db.execute("BEGIN TRANSACTION");
q.prepare("DELETE FROM stops WHERE job_id=?");
@ -105,16 +106,16 @@ bool JobsHelper::removeJob(sqlite3pp::database &db, db_id jobId)
int ret = q.step();
q.reset();
if(ret == SQLITE_OK || ret == SQLITE_DONE)
if (ret == SQLITE_OK || ret == SQLITE_DONE)
{
//Remove possible left over from editing
// Remove possible left over from editing
q.prepare("DELETE FROM old_stops WHERE job_id=?");
q.bind(1, jobId);
ret = q.step();
q.reset();
}
if(ret == SQLITE_OK || ret == SQLITE_DONE)
if (ret == SQLITE_OK || ret == SQLITE_DONE)
{
q.prepare("DELETE FROM jobs WHERE id=?");
q.bind(1, jobId);
@ -122,23 +123,24 @@ bool JobsHelper::removeJob(sqlite3pp::database &db, db_id jobId)
q.reset();
}
if(ret == SQLITE_OK || ret == SQLITE_DONE)
if (ret == SQLITE_OK || ret == SQLITE_DONE)
{
db.execute("COMMIT");
}
else
{
qDebug() << "Error while removing Job:" << jobId << ret << db.error_msg() << db.extended_error_code();
qDebug() << "Error while removing Job:" << jobId << ret << db.error_msg()
<< db.extended_error_code();
db.execute("ROLLBACK");
return false;
}
emit Session->jobRemoved(jobId);
//Refresh graphs and station views
// Refresh graphs and station views
emit Session->stationJobsPlanChanged(stationsToUpdate);
//Refresh Rollingstock views
// Refresh Rollingstock views
emit Session->rollingStockPlanChanged(rsToUpdate);
return true;
@ -146,21 +148,21 @@ bool JobsHelper::removeJob(sqlite3pp::database &db, db_id jobId)
bool JobsHelper::removeAllJobs(sqlite3pp::database &db)
{
//Old
// Old
sqlite3pp::command cmd(db, "DELETE FROM old_coupling");
cmd.execute();
cmd.prepare("DELETE FROM old_stops");
cmd.execute();
//Current
// Current
cmd.prepare("DELETE FROM coupling");
cmd.execute();
cmd.prepare("DELETE FROM stops");
cmd.execute();
//Delete jobs
// Delete jobs
cmd.prepare("DELETE FROM jobs");
cmd.execute();
@ -169,13 +171,14 @@ bool JobsHelper::removeAllJobs(sqlite3pp::database &db)
return true;
}
QTime calcReversedTime(const QTime& start, const QTime& end, const QTime& value)
QTime calcReversedTime(const QTime &start, const QTime &end, const QTime &value)
{
const int msecsFromStart = start.msecsTo(value);
return end.addMSecs(-msecsFromStart);
}
bool JobsHelper::copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJobId, int secsOffset, bool copyRsOps, bool reversePath)
bool JobsHelper::copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJobId, int secsOffset,
bool copyRsOps, bool reversePath)
{
query q_getStops(db, "SELECT id,station_id,arrival,departure,type,"
"description,in_gate_conn,out_gate_conn,next_segment_conn_id"
@ -191,9 +194,9 @@ bool JobsHelper::copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJob
QSet<db_id> rsToUpdate;
QTime start, end;
if(reversePath)
if (reversePath)
{
//Get first departure and last arrival to compute reversed time
// Get first departure and last arrival to compute reversed time
query q(db, "SELECT MIN(departure) FROM stops WHERE job_id=?");
q.bind(1, fromJobId);
q.step();
@ -205,54 +208,54 @@ bool JobsHelper::copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJob
end = q.getRows().get<QTime>(0);
}
//Store last next segment when reversing path
// Store last next segment when reversing path
db_id lastNextSegmentConn = 0;
q_getStops.bind(1, fromJobId);
for(auto stop : q_getStops)
for (auto stop : q_getStops)
{
db_id origStopId = stop.get<db_id>(0);
db_id stationId = stop.get<db_id>(1);
QTime arrival = stop.get<QTime>(2);
QTime departure = stop.get<QTime>(3);
int type = stop.get<int>(4);
db_id stationId = stop.get<db_id>(1);
QTime arrival = stop.get<QTime>(2);
QTime departure = stop.get<QTime>(3);
int type = stop.get<int>(4);
//Avoid memory copy
// Avoid memory copy
const unsigned char *description = sqlite3_column_text(q_getStops.stmt(), 5);
const int descrLen = sqlite3_column_bytes(q_getStops.stmt(), 5);
const int descrLen = sqlite3_column_bytes(q_getStops.stmt(), 5);
db_id in_gate_conn = stop.get<db_id>(6);
db_id out_gate_conn = stop.get<db_id>(7);
db_id next_seg_conn = stop.get<db_id>(8);
db_id in_gate_conn = stop.get<db_id>(6);
db_id out_gate_conn = stop.get<db_id>(7);
db_id next_seg_conn = stop.get<db_id>(8);
if(reversePath)
if (reversePath)
{
//Calculate reversed time
// Calculate reversed time
const QTime origArr = arrival;
const QTime origDep = departure;
//Arrival and departure get swapped
arrival = calcReversedTime(start, end, origDep);
// Arrival and departure get swapped
arrival = calcReversedTime(start, end, origDep);
departure = calcReversedTime(start, end, origArr);
//Swap current next segment with the one of previous stop
// Swap current next segment with the one of previous stop
qSwap(lastNextSegmentConn, next_seg_conn);
//Swap gate connections
// Swap gate connections
qSwap(in_gate_conn, out_gate_conn);
//First stop, set in_gate = out_gate so track matches
//TODO: this shouldn't be needed but seems to not cause harm
if(!in_gate_conn)
// First stop, set in_gate = out_gate so track matches
// TODO: this shouldn't be needed but seems to not cause harm
if (!in_gate_conn)
in_gate_conn = out_gate_conn;
//If we do not go past this station (Last stop) then we do not set out gate
if(!next_seg_conn)
// If we do not go past this station (Last stop) then we do not set out gate
if (!next_seg_conn)
out_gate_conn = 0;
}
//Apply time shift
arrival = arrival.addSecs(secsOffset);
// Apply time shift
arrival = arrival.addSecs(secsOffset);
departure = departure.addSecs(secsOffset);
q_setStop.bind(1, toJobId);
@ -260,34 +263,35 @@ bool JobsHelper::copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJob
q_setStop.bind(3, arrival);
q_setStop.bind(4, departure);
q_setStop.bind(5, type);
//Pass SQLITE_STATIC because description is valid until next loop cycle, so avoid copy
sqlite3_bind_text(q_setStop.stmt(), 6, reinterpret_cast<const char *>(description), descrLen, SQLITE_STATIC);
// Pass SQLITE_STATIC because description is valid until next loop cycle, so avoid copy
sqlite3_bind_text(q_setStop.stmt(), 6, reinterpret_cast<const char *>(description),
descrLen, SQLITE_STATIC);
q_setStop.bindOrNull(7, in_gate_conn);
q_setStop.bindOrNull(8, out_gate_conn);
q_setStop.bindOrNull(9, next_seg_conn);
if(q_setStop.execute() != SQLITE_OK)
if (q_setStop.execute() != SQLITE_OK)
{
qWarning() << "JobsHelper::copyStops() error setting stop" << origStopId << "To:" << toJobId << secsOffset
<< db.error_msg();
continue; //Skip stop
qWarning() << "JobsHelper::copyStops() error setting stop" << origStopId
<< "To:" << toJobId << secsOffset << db.error_msg();
continue; // Skip stop
}
db_id newStopId = db.last_insert_rowid();
q_setStop.reset();
if(copyRsOps)
if (copyRsOps)
{
q_getRsOp.bind(1, origStopId);
for(auto rs : q_getRsOp)
for (auto rs : q_getRsOp)
{
db_id rsId = rs.get<db_id>(0);
RsOp op = RsOp(rs.get<int>(1));
RsOp op = RsOp(rs.get<int>(1));
if(reversePath)
if (reversePath)
{
//Reverse operations (Couple -> Uncouple and viceversa)
if(op == RsOp::Coupled)
// Reverse operations (Couple -> Uncouple and viceversa)
if (op == RsOp::Coupled)
op = RsOp::Uncoupled;
else if(op == RsOp::Uncoupled)
else if (op == RsOp::Uncoupled)
op = RsOp::Coupled;
}
@ -297,20 +301,20 @@ bool JobsHelper::copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJob
q_setRsOp.execute();
q_setRsOp.reset();
//Store rollingstock ID to update it later
// Store rollingstock ID to update it later
rsToUpdate.insert(rsId);
}
q_getRsOp.reset();
}
//Store station to update it later
// Store station to update it later
stationsToUpdate.insert(stationId);
}
//Refresh graphs and station views
// Refresh graphs and station views
emit Session->stationJobsPlanChanged(stationsToUpdate);
//Refresh Rollingstock views
// Refresh Rollingstock views
emit Session->rollingStockPlanChanged(rsToUpdate);
return true;
@ -333,7 +337,7 @@ JobStopDirectionHelper::JobStopDirectionHelper(sqlite3pp::database &db) :
" LEFT JOIN station_gates g1 ON g1.id=c1.gate_id"
" LEFT JOIN station_gates g2 ON g2.id=c2.gate_id"
" WHERE stops.id=?");
if(ret != SQLITE_OK)
if (ret != SQLITE_OK)
qWarning() << "JobStopDirectionHelper cannot prepare query";
}
@ -346,27 +350,27 @@ JobStopDirectionHelper::~JobStopDirectionHelper()
utils::Side JobStopDirectionHelper::getStopOutSide(db_id stopId)
{
m_query->bind(1, stopId);
if(m_query->step() != SQLITE_ROW)
if (m_query->step() != SQLITE_ROW)
{
//Stop doesn't exist
// Stop doesn't exist
return utils::Side::NSides;
}
utils::Side in_side = utils::Side::NSides;
utils::Side in_side = utils::Side::NSides;
utils::Side out_side = utils::Side::NSides;
auto r = m_query->getRows();
if(r.column_type(0) != SQLITE_NULL)
auto r = m_query->getRows();
if (r.column_type(0) != SQLITE_NULL)
in_side = utils::Side(r.get<int>(0));
if(r.column_type(1) != SQLITE_NULL)
if (r.column_type(1) != SQLITE_NULL)
out_side = utils::Side(r.get<int>(1));
//Prefer out side
if(out_side != utils::Side::NSides)
// Prefer out side
if (out_side != utils::Side::NSides)
return out_side;
//We only have in side, invert it
if(in_side == utils::Side::NSides)
// We only have in side, invert it
if (in_side == utils::Side::NSides)
return in_side;
return in_side == utils::Side::East ? utils::Side::West : utils::Side::East;

View File

@ -26,7 +26,7 @@
namespace sqlite3pp {
class database;
class query;
}
} // namespace sqlite3pp
class JobsHelper
{
@ -42,12 +42,13 @@ public:
* If the Job ID is already in use, the function fails and no new Job is created.
* If 0 is passed, the function creates a new Job with arbitrary free ID from database
*/
static bool createNewJob(sqlite3pp::database &db, db_id &outJobId, JobCategory cat = JobCategory::FREIGHT);
static bool createNewJob(sqlite3pp::database &db, db_id &outJobId,
JobCategory cat = JobCategory::FREIGHT);
static bool removeJob(sqlite3pp::database &db, db_id jobId);
static bool removeAllJobs(sqlite3pp::database &db);
static bool copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJobId,
int secsOffset, bool copyRsOps, bool reversePath);
static bool copyStops(sqlite3pp::database &db, db_id fromJobId, db_id toJobId, int secsOffset,
bool copyRsOps, bool reversePath);
static bool checkShiftsExist(sqlite3pp::database &db);
};

View File

@ -33,7 +33,7 @@ NewJobSamePathDlg::NewJobSamePathDlg(QWidget *parent) :
QDialog(parent)
{
QVBoxLayout *lay = new QVBoxLayout(this);
label = new QLabel;
label = new QLabel;
label->setTextFormat(Qt::RichText);
lay->addWidget(label);
@ -41,11 +41,11 @@ NewJobSamePathDlg::NewJobSamePathDlg(QWidget *parent) :
lay->addWidget(startTimeEdit);
copyRsCheck = new QCheckBox(tr("Copy Rollingstock items"));
copyRsCheck->setChecked(true); //Enabled by default
copyRsCheck->setChecked(true); // Enabled by default
lay->addWidget(copyRsCheck);
reversePathCheck = new QCheckBox(tr("Reverse path"));
reversePathCheck->setChecked(false); //Disabled by default
reversePathCheck->setChecked(false); // Disabled by default
lay->addWidget(reversePathCheck);
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
@ -60,21 +60,21 @@ NewJobSamePathDlg::NewJobSamePathDlg(QWidget *parent) :
setWindowTitle(tr("New Job With Same Path"));
}
void NewJobSamePathDlg::setSourceJob(db_id jobId, JobCategory cat, const QTime &start, const QTime &end)
void NewJobSamePathDlg::setSourceJob(db_id jobId, JobCategory cat, const QTime &start,
const QTime &end)
{
sourceJobId = jobId;
sourceJobId = jobId;
sourceJobCat = cat;
sourceStart = start;
sourceEnd = end;
sourceStart = start;
sourceEnd = end;
label->setText(tr("Create a new job with same path of <b>%1</b>.<br>"
"Original job starts at <b>%2</b> and ends at <b>%3</b>.<br>"
"Please select below when the new job should start.")
.arg(JobCategoryName::jobName(sourceJobId, sourceJobCat),
sourceStart.toString("HH:mm"),
sourceEnd.toString("HH:mm")));
.arg(JobCategoryName::jobName(sourceJobId, sourceJobCat),
sourceStart.toString("HH:mm"), sourceEnd.toString("HH:mm")));
//Prevent calling checkTimeIsValid()
// Prevent calling checkTimeIsValid()
QSignalBlocker blk(startTimeEdit);
startTimeEdit->setTime(sourceStart);
}
@ -96,21 +96,21 @@ bool NewJobSamePathDlg::shouldReversePath() const
void NewJobSamePathDlg::checkTimeIsValid()
{
const QTime lastValidTime = QTime(23, 59);
const QTime lastValidTime = QTime(23, 59);
const int travelDurationMsecs = sourceStart.msecsTo(sourceEnd);
QTime newStart = startTimeEdit->time();
int msecsToMidnight = newStart.msecsTo(lastValidTime);
if(travelDurationMsecs > msecsToMidnight)
QTime newStart = startTimeEdit->time();
int msecsToMidnight = newStart.msecsTo(lastValidTime);
if (travelDurationMsecs > msecsToMidnight)
{
//New job would end after midnigth
// New job would end after midnigth
QMessageBox::warning(this, tr("Invalid Start Time"),
tr("New job would end past midnight."));
//Go back from midnight to get maximum start value
// Go back from midnight to get maximum start value
newStart = lastValidTime.addMSecs(-travelDurationMsecs);
//Prevent recursion
// Prevent recursion
QSignalBlocker blk(startTimeEdit);
startTimeEdit->setTime(newStart);
}

View File

@ -35,7 +35,7 @@ class NewJobSamePathDlg : public QDialog
public:
explicit NewJobSamePathDlg(QWidget *parent = nullptr);
void setSourceJob(db_id jobId, JobCategory cat, const QTime& start, const QTime& end);
void setSourceJob(db_id jobId, JobCategory cat, const QTime &start, const QTime &end);
QTime getNewStartTime() const;
bool shouldCopyRs() const;
@ -50,7 +50,7 @@ private:
QCheckBox *copyRsCheck;
QCheckBox *reversePathCheck;
db_id sourceJobId = 0;
db_id sourceJobId = 0;
JobCategory sourceJobCat = JobCategory::NCategories;
QTime sourceStart;
QTime sourceEnd;

View File

@ -29,66 +29,62 @@
#include <QDebug>
void writeJobSummary(QXmlStreamWriter& xml,
const QString& from, const QString& dep,
const QString& to, const QString& arr,
int axes)
void writeJobSummary(QXmlStreamWriter &xml, const QString &from, const QString &dep,
const QString &to, const QString &arr, int axes)
{
//Table 'job_summary'
// Table 'job_summary'
xml.writeStartElement("table:table");
xml.writeAttribute("table:name", "job_summary");
xml.writeAttribute("table:style-name", "job_5f_summary");
xml.writeEmptyElement("table:table-column"); //A
xml.writeEmptyElement("table:table-column"); // A
xml.writeAttribute("table:style-name", "job_5f_summary.A");
xml.writeEmptyElement("table:table-column"); //B
xml.writeEmptyElement("table:table-column"); // B
xml.writeAttribute("table:style-name", "job_5f_summary.B");
xml.writeEmptyElement("table:table-column"); //C
xml.writeEmptyElement("table:table-column"); // C
xml.writeAttribute("table:style-name", "job_5f_summary.C");
xml.writeEmptyElement("table:table-column"); //D
xml.writeEmptyElement("table:table-column"); // D
xml.writeAttribute("table:style-name", "job_5f_summary.D");
//Row
// Row
xml.writeStartElement("table:table-row");
//Cells
// Cells
writeCell(xml, "job_5f_summary_cell", "P2", Odt::text(Odt::jobSummaryFrom));
writeCell(xml, "job_5f_summary_cell", "P3", from);
writeCell(xml, "job_5f_summary_cell", "P2", Odt::text(Odt::jobSummaryDep));
writeCell(xml, "job_5f_summary_cell", "P3", dep);
xml.writeEndElement(); //table-row
xml.writeEndElement(); // table-row
//Row 2
// Row 2
xml.writeStartElement("table:table-row");
//Cells
// Cells
writeCell(xml, "job_5f_summary_cell", "P2", Odt::text(Odt::jobSummaryTo));
writeCell(xml, "job_5f_summary_cell", "P3", to);
writeCell(xml, "job_5f_summary_cell", "P2", Odt::text(Odt::jobSummaryArr));
writeCell(xml, "job_5f_summary_cell", "P3", arr);
xml.writeEndElement(); //table-row
xml.writeEndElement(); // table-row
//Row 3
// Row 3
xml.writeStartElement("table:table-row");
//Cells
// Cells
writeCell(xml, "job_5f_summary_cell", "P2", Odt::text(Odt::jobSummaryAxes));
writeCell(xml, "job_5f_summary_cell", "P3", QString::number(axes));
writeCell(xml, "job_5f_summary_cell", "P2", QString());
writeCell(xml, "job_5f_summary_cell", "P3", QString());
xml.writeEndElement(); //table-row
xml.writeEndElement(); // table-row
xml.writeEndElement(); //table:table END
xml.writeEndElement(); // table:table END
}
JobWriter::JobWriter(database &db) :
mDb(db),
q_getJobStops(mDb, "SELECT stops.id,"
@ -128,7 +124,8 @@ JobWriter::JobWriter(database &db) :
"stops.arrival,stops.departure"
" FROM stops"
" JOIN jobs ON jobs.id=stops.job_id"
" WHERE stops.station_id=? AND stops.departure>=? AND stops.arrival<=? AND stops.job_id<>?"),
" WHERE stops.station_id=? AND stops.departure>=? AND stops.arrival<=? "
"AND stops.job_id<>?"),
q_getStopCouplings(mDb, "SELECT coupling.rs_id,"
"rs_list.number,rs_models.name,rs_models.suffix,rs_models.type"
@ -141,21 +138,21 @@ JobWriter::JobWriter(database &db) :
void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
{
//job_summary columns
// job_summary columns
writeColumnStyle(xml, "job_5f_summary.A", "1.60cm");
writeColumnStyle(xml, "job_5f_summary.B", "8.30cm");
writeColumnStyle(xml, "job_5f_summary.C", "2.90cm");
writeColumnStyle(xml, "job_5f_summary.D", "4.20cm");
//job_stops columns
writeColumnStyle(xml, "job_5f_stops.A", "2.60cm"); //Station (IT: Stazione)
writeColumnStyle(xml, "job_5f_stops.B", "1.60cm"); //Arrival (IT: Arrivo)
writeColumnStyle(xml, "job_5f_stops.C", "2.10cm"); //Departure (IT: Partenza)
writeColumnStyle(xml, "job_5f_stops.D", "1.cm"); //Platorm 'Platf' (IT: Binario 'Bin')
writeColumnStyle(xml, "job_5f_stops.E", "3.00cm"); //Rollingstock (IT: Rotabili)
writeColumnStyle(xml, "job_5f_stops.F", "2.30cm"); //Crossings
writeColumnStyle(xml, "job_5f_stops.G", "2.30cm"); //Passings
writeColumnStyle(xml, "job_5f_stops.H", "3.20cm"); //Description (IT: Note)
// job_stops columns
writeColumnStyle(xml, "job_5f_stops.A", "2.60cm"); // Station (IT: Stazione)
writeColumnStyle(xml, "job_5f_stops.B", "1.60cm"); // Arrival (IT: Arrivo)
writeColumnStyle(xml, "job_5f_stops.C", "2.10cm"); // Departure (IT: Partenza)
writeColumnStyle(xml, "job_5f_stops.D", "1.cm"); // Platorm 'Platf' (IT: Binario 'Bin')
writeColumnStyle(xml, "job_5f_stops.E", "3.00cm"); // Rollingstock (IT: Rotabili)
writeColumnStyle(xml, "job_5f_stops.F", "2.30cm"); // Crossings
writeColumnStyle(xml, "job_5f_stops.G", "2.30cm"); // Passings
writeColumnStyle(xml, "job_5f_stops.H", "3.20cm"); // Description (IT: Note)
/* Style: job_5f_stops.A1
*
@ -181,8 +178,8 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-right", "none");
xml.writeAttribute("fo:border-top", "0.05pt solid #000000");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_stops.H1
*
@ -206,8 +203,8 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:padding-bottom", "0.15cm");
xml.writeAttribute("fo:border", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_stops.A2
*
@ -230,8 +227,8 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_stops.H2
*
@ -254,10 +251,10 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
//job_5f_asset columns
// job_5f_asset columns
writeColumnStyle(xml, "job_5f_asset.A", "3.0cm");
writeColumnStyle(xml, "job_5f_asset.B", "14.0cm");
@ -281,8 +278,8 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-right", "none");
xml.writeAttribute("fo:border-top", "0.05pt solid #000000");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_asset.B1
*
@ -302,8 +299,8 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:padding", "0.049cm");
xml.writeAttribute("fo:border", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_asset.A2
*
@ -326,8 +323,8 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_asset.B2
*
@ -350,11 +347,11 @@ void JobWriter::writeJobAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
}
void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
void JobWriter::writeJobStyles(QXmlStreamWriter &xml)
{
/* Style: job_5f_summary
*
@ -374,8 +371,8 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeAttribute("style:shadow", "none");
xml.writeAttribute("table:align", "left");
xml.writeAttribute("style:width", "8.0cm");
xml.writeEndElement(); //style:table-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-properties
xml.writeEndElement(); // style
/* Style: job_5f_summary_cell
*
@ -393,8 +390,8 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeStartElement("style:table-cell-properties");
xml.writeAttribute("fo:border", "none");
xml.writeAttribute("fo:padding", "0.097cm");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: job_5f_stops
*
@ -414,19 +411,19 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeAttribute("table:align", "left");
xml.writeAttribute("style:width", "16.0cm");
xml.writeEndElement(); //style:table-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-properties
xml.writeEndElement(); // style
/* Style: job_5f_asset
*
* Type: table
* Display name: job_asset
* Align: left
* Width: 16.0cm
*
* Usage:
* - job_stops table: displays job rollingstock asset summary
*/
*
* Type: table
* Display name: job_asset
* Align: left
* Width: 16.0cm
*
* Usage:
* - job_stops table: displays job rollingstock asset summary
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "table");
xml.writeAttribute("style:name", "job_5f_asset");
@ -435,8 +432,8 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeAttribute("table:align", "left");
xml.writeAttribute("style:width", "16.0cm");
xml.writeEndElement(); //style:table-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-properties
xml.writeEndElement(); // style
/* Style P2
* type: paragraph
@ -454,14 +451,14 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "start");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "16pt");
xml.writeAttribute("fo:font-weight", "bold");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
/* Style P3
* type: paragraph
@ -481,13 +478,13 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "start");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "16pt");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
/* Style P5
* type: paragraph
@ -498,7 +495,8 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
* Like P4 but not bold
*
* Usages:
* - job_stops: stop cell text for normal stops and transit Rollingstock/Crossings/Passings/Description
* - job_stops: stop cell text for normal stops and transit
* Rollingstock/Crossings/Passings/Description
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "paragraph");
@ -507,13 +505,13 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "12pt");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
/* Style P6
* type: paragraph
@ -526,7 +524,8 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
* (P4 + Italic, not bold)
*
* Usages:
* - job_stops: stop cell text for transit stops except for Rollingstock/Crossings/Passings/Description columns which have P5
* - job_stops: stop cell text for transit stops except for
* Rollingstock/Crossings/Passings/Description columns which have P5
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "paragraph");
@ -535,18 +534,17 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "12pt");
xml.writeAttribute("fo:font-style", "italic");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
//stile interruzione di pagina
//TODO: quando useremo 'Page master style' vedere se vanno in conflitto
// stile interruzione di pagina
// TODO: quando useremo 'Page master style' vedere se vanno in conflitto
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "paragraph");
xml.writeAttribute("style:name", "interruzione");
@ -554,16 +552,16 @@ void JobWriter::writeJobStyles(QXmlStreamWriter& xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "start");
xml.writeAttribute("fo:break-after", "page");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "1pt");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
}
void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
void JobWriter::writeJob(QXmlStreamWriter &xml, db_id jobId, JobCategory jobCat)
{
query q_getRSInfo(mDb, "SELECT rs_list.number,rs_models.name,rs_models.suffix,rs_models.type"
" FROM rs_list"
@ -572,33 +570,34 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
QList<QPair<QString, QList<db_id>>> stopsRS;
//Title
// Title
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeCharacters(JobCategoryName::jobNameSpaced(jobId, jobCat));
xml.writeEndElement();
//Vertical space
// Vertical space
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeEndElement();
db_id firstStopId = 0;
db_id lastStopId = 0;
db_id lastStopId = 0;
QTime start, end;
QString fromStation, toStation;
int axesCount = 0;
//Job summary
// Job summary
q_getFirstStop.bind(1, jobId);
if(q_getFirstStop.step() == SQLITE_ROW && q_getFirstStop.getRows().column_type(0) != SQLITE_NULL)
if (q_getFirstStop.step() == SQLITE_ROW
&& q_getFirstStop.getRows().column_type(0) != SQLITE_NULL)
{
auto r = q_getFirstStop.getRows();
auto r = q_getFirstStop.getRows();
firstStopId = r.get<db_id>(0);
fromStation = r.get<QString>(1);
start = r.get<QTime>(2);
start = r.get<QTime>(2);
q_initialJobAxes.bind(1, firstStopId);
q_initialJobAxes.step();
@ -608,21 +607,19 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
q_getFirstStop.reset();
q_getLastStop.bind(1, jobId);
if(q_getLastStop.step() == SQLITE_ROW && q_getLastStop.getRows().column_type(0) != SQLITE_NULL)
if (q_getLastStop.step() == SQLITE_ROW && q_getLastStop.getRows().column_type(0) != SQLITE_NULL)
{
auto r = q_getLastStop.getRows();
auto r = q_getLastStop.getRows();
lastStopId = r.get<db_id>(0);
toStation = r.get<QString>(1);
end = r.get<QTime>(2);
toStation = r.get<QString>(1);
end = r.get<QTime>(2);
}
q_getLastStop.reset();
if(firstStopId && lastStopId)
if (firstStopId && lastStopId)
{
writeJobSummary(xml,
fromStation, start.toString("HH:mm"),
toStation, end.toString("HH:mm"),
writeJobSummary(xml, fromStation, start.toString("HH:mm"), toStation, end.toString("HH:mm"),
axesCount);
}
else
@ -633,41 +630,41 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
writeJobSummary(xml, err, err, err, err, 0);
}
//Vertical space
// Vertical space
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeEndElement();
//Table 'job_stops'
// Table 'job_stops'
xml.writeStartElement("table:table");
xml.writeAttribute("table:name", "job_stops");
xml.writeAttribute("table:style-name", "job_5f_stops");
xml.writeEmptyElement("table:table-column"); //Station
xml.writeEmptyElement("table:table-column"); // Station
xml.writeAttribute("table:style-name", "job_5f_stops.A");
xml.writeEmptyElement("table:table-column"); //Arrival
xml.writeEmptyElement("table:table-column"); // Arrival
xml.writeAttribute("table:style-name", "job_5f_stops.B");
xml.writeEmptyElement("table:table-column"); //Departure
xml.writeEmptyElement("table:table-column"); // Departure
xml.writeAttribute("table:style-name", "job_5f_stops.C");
xml.writeEmptyElement("table:table-column"); //Platform (Platf)
xml.writeEmptyElement("table:table-column"); // Platform (Platf)
xml.writeAttribute("table:style-name", "job_5f_stops.D");
xml.writeEmptyElement("table:table-column"); //Rollingstock
xml.writeEmptyElement("table:table-column"); // Rollingstock
xml.writeAttribute("table:style-name", "job_5f_stops.E");
xml.writeEmptyElement("table:table-column"); //Crossings
xml.writeEmptyElement("table:table-column"); // Crossings
xml.writeAttribute("table:style-name", "job_5f_stops.F");
xml.writeEmptyElement("table:table-column"); //Passings
xml.writeEmptyElement("table:table-column"); // Passings
xml.writeAttribute("table:style-name", "job_5f_stops.G");
xml.writeEmptyElement("table:table-column"); //Description
xml.writeEmptyElement("table:table-column"); // Description
xml.writeAttribute("table:style-name", "job_5f_stops.H");
//Row 1 (Heading)
// Row 1 (Heading)
xml.writeStartElement("table:table-header-rows");
xml.writeStartElement("table:table-row");
@ -679,40 +676,41 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
writeCell(xml, "job_5f_stops.A1", P4_Style, Odt::text(Odt::rollingstock));
writeCell(xml, "job_5f_stops.A1", P4_Style, Odt::text(Odt::jobStopCross));
writeCell(xml, "job_5f_stops.A1", P4_Style, Odt::text(Odt::jobStopPassings));
writeCell(xml, "job_5f_stops.H1", P4_Style, Odt::text(Odt::notes)); //Description
writeCell(xml, "job_5f_stops.H1", P4_Style, Odt::text(Odt::notes)); // Description
xml.writeEndElement(); //end of row
xml.writeEndElement(); //header section
xml.writeEndElement(); // end of row
xml.writeEndElement(); // header section
QList<db_id> rsAsset;
const QString P5_style = "P5";
//Fill stops table
// Fill stops table
q_getJobStops.bind(1, jobId);
for(auto stop : q_getJobStops)
for (auto stop : q_getJobStops)
{
db_id stopId = stop.get<db_id>(0);
db_id stationId = stop.get<db_id>(1);
db_id stopId = stop.get<db_id>(0);
db_id stationId = stop.get<db_id>(1);
QString stationName = stop.get<QString>(2);
QTime arr = stop.get<QTime>(3);
QTime dep = stop.get<QTime>(4);
const int stopType = stop.get<int>(5);
QString descr = stop.get<QString>(6);
QTime arr = stop.get<QTime>(3);
QTime dep = stop.get<QTime>(4);
const int stopType = stop.get<int>(5);
QString descr = stop.get<QString>(6);
QString trackName = stop.get<QString>(7);
if(trackName.isEmpty())
trackName = stop.get<QString>(8); //Use out gate to get track name
QString trackName = stop.get<QString>(7);
if (trackName.isEmpty())
trackName = stop.get<QString>(8); // Use out gate to get track name
utils::Side entranceSide = utils::Side(stop.get<int>(9));
utils::Side exitSide = utils::Side(stop.get<int>(10));
utils::Side exitSide = utils::Side(stop.get<int>(10));
if(entranceSide == exitSide && stop.column_type(9) != SQLITE_NULL && stop.column_type(10) != SQLITE_NULL)
if (entranceSide == exitSide && stop.column_type(9) != SQLITE_NULL
&& stop.column_type(10) != SQLITE_NULL)
{
//Train enters and leaves from same track side, add reversal to description
// Train enters and leaves from same track side, add reversal to description
QString descr2 = Odt::text(Odt::jobReverseDirection);
if(!descr.isEmpty())
descr2.append('\n'); //Separate from manually set description
if (!descr.isEmpty())
descr2.append('\n'); // Separate from manually set description
descr2.append(descr);
descr = descr2;
}
@ -721,58 +719,59 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
qDebug() << "(Loop) Job:" << jobId << "Stop:" << stopId;
xml.writeStartElement("table:table-row"); //start new row
xml.writeStartElement("table:table-row"); // start new row
const QString styleName = isTransit ? "P6" : P5_style; //If it's transit use italic style
const QString styleName = isTransit ? "P6" : P5_style; // If it's transit use italic style
//Station
// Station
writeCell(xml, "job_5f_stops.A2", styleName, stationName);
//Arrival
writeCell(xml, "job_5f_stops.A2", styleName, stopId == firstStopId ? QString() : arr.toString("HH:mm"));
// Arrival
writeCell(xml, "job_5f_stops.A2", styleName,
stopId == firstStopId ? QString() : arr.toString("HH:mm"));
//Departure
//If it's transit then and arrival is equal to departure (should be always but if is different show both to warn user about the error)
//then show only arrival
writeCell(xml, "job_5f_stops.A2", styleName, (stopId == lastStopId || (isTransit && arr == dep)) ? QString() : dep.toString("HH:mm"));
// Departure
// If it's transit then and arrival is equal to departure (should be always but if is
// different show both to warn user about the error) then show only arrival
writeCell(xml, "job_5f_stops.A2", styleName,
(stopId == lastStopId || (isTransit && arr == dep)) ? QString()
: dep.toString("HH:mm"));
//Platform
// Platform
writeCell(xml, "job_5f_stops.A2", styleName, trackName);
//Rollingstock
// Rollingstock
sqlite3_stmt *stmt = q_getStopCouplings.stmt();
writeCellListStart(xml, "job_5f_stops.A2", P5_style);
//Coupled rollingstock
// Coupled rollingstock
bool firstCoupRow = true;
q_getStopCouplings.bind(1, stopId);
q_getStopCouplings.bind(2, int(RsOp::Coupled));
for(auto coup : q_getStopCouplings)
for (auto coup : q_getStopCouplings)
{
db_id rsId = coup.get<db_id>(0);
rsAsset.append(rsId);
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 2));
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 2));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
type);
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number,
modelSuffix, modelSuffixLen, type);
if(firstCoupRow)
if (firstCoupRow)
{
firstCoupRow = false;
//Use bold font
// Use bold font
xml.writeStartElement("text:span");
xml.writeAttribute("text:style-name", "T1");
xml.writeCharacters(Odt::text(Odt::CoupledAbbr));
xml.writeEndElement(); //test:span
xml.writeEndElement(); // test:span
}
xml.writeEmptyElement("text:line-break");
@ -780,38 +779,36 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
}
q_getStopCouplings.reset();
//Unoupled rollingstock
// Unoupled rollingstock
bool firstUncoupRow = true;
q_getStopCouplings.bind(1, stopId);
q_getStopCouplings.bind(2, int(RsOp::Uncoupled));
for(auto coup : q_getStopCouplings)
for (auto coup : q_getStopCouplings)
{
db_id rsId = coup.get<db_id>(0);
rsAsset.removeAll(rsId);
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 2));
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 2));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
type);
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number,
modelSuffix, modelSuffixLen, type);
if(firstUncoupRow)
if (firstUncoupRow)
{
if(!firstCoupRow) //Not first row, there were coupled rs
xml.writeEmptyElement("text:line-break"); //Separate from coupled
if (!firstCoupRow) // Not first row, there were coupled rs
xml.writeEmptyElement("text:line-break"); // Separate from coupled
firstUncoupRow = false;
//Use bold font
// Use bold font
xml.writeStartElement("text:span");
xml.writeAttribute("text:style-name", "T1");
xml.writeCharacters(Odt::text(Odt::UncoupledAbbr));
xml.writeEndElement(); //test:span
xml.writeEndElement(); // test:span
}
xml.writeEmptyElement("text:line-break");
@ -822,7 +819,7 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
stopsRS.append({stationName, rsAsset});
//Crossings / Passings
// Crossings / Passings
JobStopDirectionHelper dirHelper(mDb);
@ -835,25 +832,25 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
q_selectPassings.bind(3, dep);
q_selectPassings.bind(4, jobId);
//Incroci
// Incroci
firstCoupRow = true;
writeCellListStart(xml, "job_5f_stops.A2", P5_style);
for(auto pass : q_selectPassings)
for (auto pass : q_selectPassings)
{
db_id otherStopId = pass.get<db_id>(0);
db_id otherJobId = pass.get<db_id>(1);
db_id otherStopId = pass.get<db_id>(0);
db_id otherJobId = pass.get<db_id>(1);
JobCategory otherJobCat = JobCategory(pass.get<int>(2));
//QTime otherArr = pass.get<QTime>(3);
//QTime otherDep = pass.get<QTime>(4);
// QTime otherArr = pass.get<QTime>(3);
// QTime otherDep = pass.get<QTime>(4);
utils::Side otherDir = dirHelper.getStopOutSide(otherStopId);
if(myDir == otherDir)
if (myDir == otherDir)
passings.append({otherJobId, otherJobCat});
else
{
if(firstCoupRow)
if (firstCoupRow)
firstCoupRow = false;
else
xml.writeEmptyElement("text:line-break");
@ -863,12 +860,12 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
q_selectPassings.reset();
writeCellListEnd(xml);
//Passings
// Passings
firstCoupRow = true;
writeCellListStart(xml, "job_5f_stops.A2", P5_style);
for(auto entry : passings)
for (auto entry : passings)
{
if(firstCoupRow)
if (firstCoupRow)
firstCoupRow = false;
else
xml.writeEmptyElement("text:line-break");
@ -876,107 +873,105 @@ void JobWriter::writeJob(QXmlStreamWriter& xml, db_id jobId, JobCategory jobCat)
}
writeCellListEnd(xml);
//Description
// Description
writeCellListStart(xml, "job_5f_stops.H2", P5_style);
if(!descr.isEmpty())
if (!descr.isEmpty())
{
//Split in lines
// Split in lines
int lastIdx = 0;
while(true)
while (true)
{
int idx = descr.indexOf('\n', lastIdx);
int idx = descr.indexOf('\n', lastIdx);
QString line = descr.mid(lastIdx, idx == -1 ? idx : idx - lastIdx);
xml.writeCharacters(line.simplified());
if(idx < 0)
break; //Last line
if (idx < 0)
break; // Last line
lastIdx = idx + 1;
xml.writeEmptyElement("text:line-break");
}
}
writeCellListEnd(xml);
xml.writeEndElement(); //end of row
xml.writeEndElement(); // end of row
}
q_getJobStops.reset();
xml.writeEndElement(); //table:table END
xml.writeEndElement(); // table:table END
//text:p as separator
// text:p as separator
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeEndElement();
//Table 'job_asset'
// Table 'job_asset'
xml.writeStartElement("table:table");
xml.writeAttribute("table:name", "job_asset");
xml.writeAttribute("table:style-name", "job_5f_asset");
xml.writeEmptyElement("table:table-column"); //Stazione
xml.writeEmptyElement("table:table-column"); // Stazione
xml.writeAttribute("table:style-name", "job_5f_asset.A");
xml.writeEmptyElement("table:table-column"); //Assetto
xml.writeEmptyElement("table:table-column"); // Assetto
xml.writeAttribute("table:style-name", "job_5f_asset.B");
//Duplicate second-last asset to last stop because last stop would be always empty
if(stopsRS.size() >= 2)
// Duplicate second-last asset to last stop because last stop would be always empty
if (stopsRS.size() >= 2)
{
int i = stopsRS.size() - 2; //Get second-last (IT: penultima fermata)
int i = stopsRS.size() - 2; // Get second-last (IT: penultima fermata)
stopsRS[i + 1].second = stopsRS[i].second;
}
else
{
//Error!
// Error!
qWarning() << __FUNCTION__ << "At least 2 stops required!";
}
bool firstRow = true;
for(auto &s : qAsConst(stopsRS))
for (auto &s : qAsConst(stopsRS))
{
xml.writeStartElement("table:table-row"); //start new row
xml.writeStartElement("table:table-row"); // start new row
writeCell(xml, firstRow ? "job_5f_asset.A1" : "job_5f_asset.A2", P5_style, s.first);
writeCellListStart(xml, firstRow ? "job_5f_asset.B1" : "job_5f_asset.B2", P5_style);
for(int i = 0; i < s.second.size(); i++)
for (int i = 0; i < s.second.size(); i++)
{
q_getRSInfo.reset();
q_getRSInfo.bind(1, s.second.at(i));
int ret = q_getRSInfo.step();
if(ret != SQLITE_ROW)
if (ret != SQLITE_ROW)
{
//Error: RS does not exist!
// Error: RS does not exist!
continue;
}
sqlite3_stmt *stmt = q_getRSInfo.stmt();
int number = sqlite3_column_int(stmt, 0);
int modelNameLen = sqlite3_column_bytes(stmt, 1);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 1));
sqlite3_stmt *stmt = q_getRSInfo.stmt();
int number = sqlite3_column_int(stmt, 0);
int modelNameLen = sqlite3_column_bytes(stmt, 1);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 1));
int modelSuffixLen = sqlite3_column_bytes(stmt, 2);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 2));
RsType type = RsType(sqlite3_column_int(stmt, 3));
int modelSuffixLen = sqlite3_column_bytes(stmt, 2);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 2));
RsType type = RsType(sqlite3_column_int(stmt, 3));
const QString name = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
type);
const QString name = rs_utils::formatNameRef(modelName, modelNameLen, number,
modelSuffix, modelSuffixLen, type);
xml.writeCharacters(name);
if(i < s.second.size() - 1)
if (i < s.second.size() - 1)
xml.writeCharacters(" + ");
}
writeCellListEnd(xml);
xml.writeEndElement(); //end of row
xml.writeEndElement(); // end of row
if(firstRow)
if (firstRow)
firstRow = false;
}
xml.writeEndElement();
//Interruzione pagina TODO: see style 'interruzione'
// Interruzione pagina TODO: see style 'interruzione'
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "interruzione");
xml.writeEndElement();

View File

@ -30,9 +30,9 @@ class QXmlStreamWriter;
class JobWriter
{
public:
JobWriter(database& db);
JobWriter(database &db);
static void writeJobAutomaticStyles(QXmlStreamWriter& xml);
static void writeJobAutomaticStyles(QXmlStreamWriter &xml);
static void writeJobStyles(QXmlStreamWriter &xml);
void writeJob(QXmlStreamWriter &xml, db_id jobId, JobCategory jobCat);

View File

@ -25,54 +25,58 @@
#include <QDebug>
#include "info.h" //Fot App constants
#include "info.h" //Fot App constants
#include "app/session.h" //For settings
#include "db_metadata/metadatamanager.h"
#include "odtutils.h"
//content.xml
// content.xml
static constexpr char contentFileStr[] = "content.xml";
static constexpr QLatin1String contentFileName = QLatin1String(contentFileStr, sizeof (contentFileStr) - 1);
static constexpr QLatin1String contentFileName =
QLatin1String(contentFileStr, sizeof(contentFileStr) - 1);
//styles.xml
// styles.xml
static constexpr char stylesFileStr[] = "styles.xml";
static constexpr QLatin1String stylesFileName = QLatin1String(stylesFileStr, sizeof (stylesFileStr) - 1);
static constexpr QLatin1String stylesFileName =
QLatin1String(stylesFileStr, sizeof(stylesFileStr) - 1);
//meta.xml
static constexpr char metaFileStr[] = "meta.xml";
static constexpr QLatin1String metaFileName = QLatin1String(metaFileStr, sizeof (metaFileStr) - 1);
// meta.xml
static constexpr char metaFileStr[] = "meta.xml";
static constexpr QLatin1String metaFileName = QLatin1String(metaFileStr, sizeof(metaFileStr) - 1);
//META-INF/manifest.xml
// META-INF/manifest.xml
static constexpr char manifestFileNameStr[] = "manifest.xml";
static constexpr QLatin1String manifestFileName = QLatin1String(manifestFileNameStr, sizeof (manifestFileNameStr) - 1);
static constexpr QLatin1String manifestFileName =
QLatin1String(manifestFileNameStr, sizeof(manifestFileNameStr) - 1);
static constexpr char metaInfPathStr[] = "/META-INF";
static constexpr QLatin1String metaInfDirPath = QLatin1String(metaInfPathStr, sizeof (metaInfPathStr) - 1);
static constexpr QLatin1String metaInfDirPath =
QLatin1String(metaInfPathStr, sizeof(metaInfPathStr) - 1);
static constexpr char manifestFilePathStr[] = "META-INF/manifest.xml";
static constexpr QLatin1String manifestFilePath = QLatin1String(manifestFilePathStr, sizeof (manifestFilePathStr) - 1);
static constexpr QLatin1String manifestFilePath =
QLatin1String(manifestFilePathStr, sizeof(manifestFilePathStr) - 1);
OdtDocument::OdtDocument()
{
}
bool OdtDocument::initDocument()
{
if(!dir.isValid())
if (!dir.isValid())
return false;
content.setFileName(dir.filePath(contentFileName));
if(!content.open(QFile::WriteOnly | QFile::Truncate))
if (!content.open(QFile::WriteOnly | QFile::Truncate))
return false;
styles.setFileName(dir.filePath(stylesFileName));
if(!styles.open(QFile::WriteOnly | QFile::Truncate))
if (!styles.open(QFile::WriteOnly | QFile::Truncate))
return false;
contentXml.setDevice(&content);
stylesXml.setDevice(&styles);
//Init content.xml
// Init content.xml
writeStartDoc(contentXml);
contentXml.writeStartElement("office:document-content");
contentXml.writeNamespace("urn:oasis:names:tc:opendocument:xmlns:office:1.0", "office");
@ -85,7 +89,7 @@ bool OdtDocument::initDocument()
contentXml.writeNamespace("http://www.w3.org/1999/xlink", "xlink");
contentXml.writeAttribute("office:version", "1.2");
//Init styles.xml
// Init styles.xml
writeStartDoc(stylesXml);
stylesXml.writeStartElement("office:document-styles");
stylesXml.writeNamespace("urn:oasis:names:tc:opendocument:xmlns:office:1.0", "office");
@ -100,31 +104,32 @@ bool OdtDocument::initDocument()
return true;
}
void OdtDocument::startBody() //TODO: start body manually, remove this function
void OdtDocument::startBody() // TODO: start body manually, remove this function
{
contentXml.writeEndElement(); //office:automatic-styles
contentXml.writeEndElement(); // office:automatic-styles
contentXml.writeStartElement("office:body");
contentXml.writeStartElement("office:text");
}
bool OdtDocument::saveTo(const QString& fileName)
bool OdtDocument::saveTo(const QString &fileName)
{
int err = 0;
int err = 0;
zip_t *zipper = zip_open(fileName.toUtf8(), ZIP_CREATE | ZIP_TRUNCATE, &err);
if (zipper == nullptr)
{
zip_error_t ziperror;
zip_error_init_with_code(&ziperror, err);
qDebug() << "Failed to open output file" << fileName << "Err:" << zip_error_strerror(&ziperror);
qDebug() << "Failed to open output file" << fileName
<< "Err:" << zip_error_strerror(&ziperror);
zip_error_fini(&ziperror);
return false;
}
//Add mimetype file NOTE: must be the first file in archive
// Add mimetype file NOTE: must be the first file in archive
const char mimetype[] = "application/vnd.oasis.opendocument.text";
zip_source_t *source = zip_source_buffer(zipper, mimetype, sizeof (mimetype) - 1, 0);
zip_source_t *source = zip_source_buffer(zipper, mimetype, sizeof(mimetype) - 1, 0);
if (source == nullptr)
{
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
@ -136,10 +141,10 @@ bool OdtDocument::saveTo(const QString& fileName)
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
//Add META-INF/manifest.xml
// Add META-INF/manifest.xml
QString fileToCompress = dir.filePath(manifestFilePath);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
if (source == nullptr)
{
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
@ -151,10 +156,10 @@ bool OdtDocument::saveTo(const QString& fileName)
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
//Add styles.xml
// Add styles.xml
fileToCompress = dir.filePath(stylesFileName);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
if (source == nullptr)
{
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
@ -166,10 +171,10 @@ bool OdtDocument::saveTo(const QString& fileName)
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
//Add content.xml
// Add content.xml
fileToCompress = dir.filePath(contentFileName);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
if (source == nullptr)
{
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
@ -181,10 +186,10 @@ bool OdtDocument::saveTo(const QString& fileName)
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
//Add meta.xml
// Add meta.xml
fileToCompress = dir.filePath(metaFileName);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
source = zip_source_file(zipper, fileToCompress.toUtf8(), 0, 0);
if (source == nullptr)
{
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
@ -196,10 +201,10 @@ bool OdtDocument::saveTo(const QString& fileName)
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
//Add possible images
QString imgBasePath = dir.filePath("Pictures") + QLatin1String("/%1");
// Add possible images
QString imgBasePath = dir.filePath("Pictures") + QLatin1String("/%1");
QString imgNewBasePath = QLatin1String("Pictures/%1");
for(const auto& img : qAsConst(imageList))
for (const auto &img : qAsConst(imageList))
{
source = zip_source_file(zipper, imgBasePath.arg(img.first).toUtf8(), 0, 0);
if (source == nullptr)
@ -207,14 +212,15 @@ bool OdtDocument::saveTo(const QString& fileName)
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
if (zip_file_add(zipper, imgNewBasePath.arg(img.first).toUtf8(), source, ZIP_FL_ENC_UTF_8) < 0)
if (zip_file_add(zipper, imgNewBasePath.arg(img.first).toUtf8(), source, ZIP_FL_ENC_UTF_8)
< 0)
{
zip_source_free(source);
qDebug() << "Failed to add file to zip:" << zip_strerror(zipper);
}
}
if(zip_close(zipper) != 0)
if (zip_close(zipper) != 0)
{
qDebug() << "Failed to close zip:" << zip_strerror(zipper);
}
@ -236,11 +242,11 @@ void OdtDocument::endDocument()
QString OdtDocument::addImage(const QString &name, const QString &mediaType)
{
if(imageList.isEmpty())
if (imageList.isEmpty())
{
//First image added, create Pictures folder
// First image added, create Pictures folder
QDir pictures(dir.path());
if(!pictures.mkdir("Pictures"))
if (!pictures.mkdir("Pictures"))
qWarning() << "OdtDocument: cannot create Pictures folder";
}
imageList.append({name, mediaType});
@ -251,12 +257,12 @@ void OdtDocument::writeStartDoc(QXmlStreamWriter &xml)
{
xml.setAutoFormatting(true);
xml.setAutoFormattingIndent(-1);
//xml.writeStartDocument(QStringLiteral("1.0"), true);
// xml.writeStartDocument(QStringLiteral("1.0"), true);
xml.writeStartDocument(QStringLiteral("1.0"));
}
void OdtDocument::writeFileEntry(QXmlStreamWriter& xml,
const QString& fullPath, const QString& mediaType)
void OdtDocument::writeFileEntry(QXmlStreamWriter &xml, const QString &fullPath,
const QString &mediaType)
{
xml.writeStartElement("manifest:file-entry");
xml.writeAttribute("manifest:full-path", fullPath);
@ -264,12 +270,12 @@ void OdtDocument::writeFileEntry(QXmlStreamWriter& xml,
xml.writeEndElement();
}
void OdtDocument::saveManifest(const QString& path)
void OdtDocument::saveManifest(const QString &path)
{
const QString xmlMime = QLatin1String("text/xml");
QDir manifestDir(path + metaInfDirPath);
if(!manifestDir.exists())
if (!manifestDir.exists())
manifestDir.mkpath(".");
QFile manifest(manifestDir.filePath(manifestFileName));
@ -281,30 +287,30 @@ void OdtDocument::saveManifest(const QString& path)
xml.writeNamespace("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0", "manifest");
xml.writeAttribute("manifest:version", "1.2");
//Root
// Root
writeFileEntry(xml, "/", "application/vnd.oasis.opendocument.text");
//styles.xml
// styles.xml
writeFileEntry(xml, stylesFileName, xmlMime);
//content.xml
// content.xml
writeFileEntry(xml, contentFileName, xmlMime);
//meta.xml
// meta.xml
writeFileEntry(xml, metaFileName, xmlMime);
//Add possible images
for(const auto& img : qAsConst(imageList))
// Add possible images
for (const auto &img : qAsConst(imageList))
{
writeFileEntry(xml, "Pictures/" + img.first, img.second);
}
xml.writeEndElement(); //manifest:manifest
xml.writeEndElement(); // manifest:manifest
xml.writeEndDocument();
}
void OdtDocument::saveMeta(const QString& path)
void OdtDocument::saveMeta(const QString &path)
{
QDir metaDir(path);
@ -323,122 +329,122 @@ void OdtDocument::saveMeta(const QString& path)
xml.writeStartElement("office:meta");
MetaDataManager *meta = Session->getMetaDataManager();
MetaDataManager *meta = Session->getMetaDataManager();
const bool storeLocationAndDate = AppSettings.getSheetStoreLocationDateInMeta();
//Title
if(!documentTitle.isEmpty())
// Title
if (!documentTitle.isEmpty())
{
xml.writeStartElement("dc:title");
xml.writeCharacters(documentTitle);
xml.writeEndElement(); //dc:title
xml.writeEndElement(); // dc:title
}
//Subject
// Subject
xml.writeStartElement("dc:subject");
xml.writeCharacters(AppDisplayName);
xml.writeCharacters(" Session Meeting"); //Do not translate, so it's standard for everyone
xml.writeEndElement(); //dc:subject
xml.writeCharacters(" Session Meeting"); // Do not translate, so it's standard for everyone
xml.writeEndElement(); // dc:subject
//Description
// Description
QString meetingLocation;
if(storeLocationAndDate)
if (storeLocationAndDate)
{
meta->getString(meetingLocation, MetaDataKey::MeetingLocation);
QDate start, end;
qint64 tmp = 0;
if(meta->getInt64(tmp, MetaDataKey::MeetingStartDate) == MetaDataKey::ValueFound)
if (meta->getInt64(tmp, MetaDataKey::MeetingStartDate) == MetaDataKey::ValueFound)
start = QDate::fromJulianDay(tmp);
if(meta->getInt64(tmp, MetaDataKey::MeetingEndDate) == MetaDataKey::ValueFound)
if (meta->getInt64(tmp, MetaDataKey::MeetingEndDate) == MetaDataKey::ValueFound)
end = QDate::fromJulianDay(tmp);
if(!end.isValid() || end < start)
if (!end.isValid() || end < start)
end = start;
if(!meetingLocation.isEmpty() && start.isValid())
if (!meetingLocation.isEmpty() && start.isValid())
{
//Store description only if metadata is valid
//Example: Meeting in CORNUDA from 07/11/2020 to 09/11/2020
// Store description only if metadata is valid
// Example: Meeting in CORNUDA from 07/11/2020 to 09/11/2020
QString description;
if(start != end)
if (start != end)
{
description = Odt::text(Odt::meetingFromTo)
.arg(meetingLocation,
start.toString("dd/MM/yyyy"),
end.toString("dd/MM/yyyy"));
description =
Odt::text(Odt::meetingFromTo)
.arg(meetingLocation, start.toString("dd/MM/yyyy"), end.toString("dd/MM/yyyy"));
}
else
{
description = Odt::text(Odt::meetingOnDate)
.arg(meetingLocation,
start.toString("dd/MM/yyyy"));
description =
Odt::text(Odt::meetingOnDate).arg(meetingLocation, start.toString("dd/MM/yyyy"));
}
xml.writeStartElement("dc:description");
xml.writeCharacters(description);
xml.writeEndElement(); //dc:description
xml.writeEndElement(); // dc:description
}
}
//Language
// Language
xml.writeStartElement("dc:language");
xml.writeCharacters(Session->getSheetExportLocale().bcp47Name());
xml.writeEndElement(); //dc:language
xml.writeEndElement(); // dc:language
//Generator
// Generator
xml.writeStartElement("meta:generator");
xml.writeCharacters(AppProduct);
xml.writeCharacters("/");
xml.writeCharacters(AppVersion);
xml.writeCharacters("-");
xml.writeCharacters(AppBuildDate);
xml.writeEndElement(); //meta:generator
xml.writeEndElement(); // meta:generator
//Initial creator
// Initial creator
xml.writeStartElement("meta:initial-creator");
xml.writeCharacters(AppDisplayName);
xml.writeEndElement(); //meta:initial-creator
xml.writeEndElement(); // meta:initial-creator
//Creation date
// Creation date
xml.writeStartElement("meta:creation-date");
//NOTE: date must be in ISO 8601 format but without time zone offset (LibreOffice doesn't recognize it)
// so do not use Qt::ISODate otherwise there is the risk of adding time zone offset to string
// NOTE: date must be in ISO 8601 format but without time zone offset (LibreOffice doesn't
// recognize it)
// so do not use Qt::ISODate otherwise there is the risk of adding time zone offset to
// string
xml.writeCharacters(QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss"));
xml.writeEndElement(); //meta:creation-date
xml.writeEndElement(); // meta:creation-date
//Keywords
// Keywords
xml.writeStartElement("meta:keyword");
xml.writeCharacters(AppDisplayName);
xml.writeEndElement(); //meta:keyword
xml.writeEndElement(); // meta:keyword
xml.writeStartElement("meta:keyword");
xml.writeCharacters(AppProduct);
xml.writeEndElement(); //meta:keyword
xml.writeEndElement(); // meta:keyword
xml.writeStartElement("meta:keyword");
xml.writeCharacters(AppCompany);
xml.writeEndElement(); //meta:keyword
xml.writeEndElement(); // meta:keyword
xml.writeStartElement("meta:keyword");
xml.writeCharacters(Odt::text(Odt::meeting));
xml.writeEndElement(); //meta:keyword
xml.writeEndElement(); // meta:keyword
//Untranslated version
// Untranslated version
xml.writeStartElement("meta:keyword");
xml.writeCharacters(QString::fromUtf8(Odt::meeting.sourceText));
xml.writeEndElement(); //meta:keyword
xml.writeEndElement(); // meta:keyword
if(storeLocationAndDate && !meetingLocation.isEmpty())
if (storeLocationAndDate && !meetingLocation.isEmpty())
{
xml.writeStartElement("meta:keyword");
xml.writeCharacters(meetingLocation);
xml.writeEndElement(); //meta:keyword
xml.writeEndElement(); // meta:keyword
}
//End
xml.writeEndElement(); //office:meta
xml.writeEndElement(); //office:document-meta
// End
xml.writeEndElement(); // office:meta
xml.writeEndElement(); // office:document-meta
xml.writeEndDocument();
}

View File

@ -35,10 +35,13 @@ public:
void startBody();
void endDocument();
//Returns a 'path + file name' where you must save the image
QString addImage(const QString& name, const QString& mediaType);
// Returns a 'path + file name' where you must save the image
QString addImage(const QString &name, const QString &mediaType);
inline void setTitle(const QString& title) { documentTitle = title; }
inline void setTitle(const QString &title)
{
documentTitle = title;
}
public:
QTemporaryDir dir;
@ -57,7 +60,7 @@ private:
private:
QString documentTitle;
//pair: fileName, mediaType
// pair: fileName, mediaType
QList<QPair<QString, QString>> imageList;
};

View File

@ -34,29 +34,31 @@ void writeColumnStyle(QXmlStreamWriter &xml, const QString &name, const QString
xml.writeStartElement("style:table-column-properties");
xml.writeAttribute("style:column-width", width);
xml.writeEndElement(); //style:table-column-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-column-properties
xml.writeEndElement(); // style
}
/* writeCell
*
* Helper function to write table cell
* Sets up the cell style and paragraph style, writes single line text and closes xml opened elements
* Sets up the cell style and paragraph style, writes single line text and closes xml opened
* elements
*/
void writeCell(QXmlStreamWriter &xml, const QString &cellStyle, const QString &paragraphStyle, const QString &text)
void writeCell(QXmlStreamWriter &xml, const QString &cellStyle, const QString &paragraphStyle,
const QString &text)
{
//Cell
// Cell
xml.writeStartElement("table:table-cell");
xml.writeAttribute("office:value-type", "string");
xml.writeAttribute("table:style-name", cellStyle);
//text:p
// text:p
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", paragraphStyle);
xml.writeCharacters(text);
xml.writeEndElement(); //text:p
xml.writeEndElement(); // text:p
xml.writeEndElement(); //table-cell
xml.writeEndElement(); // table-cell
}
/* writeCellListStart
@ -66,14 +68,15 @@ void writeCell(QXmlStreamWriter &xml, const QString &cellStyle, const QString &p
* Then you have full control on cell contents
* Then call writeCellListEnd
*/
void writeCellListStart(QXmlStreamWriter &xml, const QString &cellStyle, const QString &paragraphStyle)
void writeCellListStart(QXmlStreamWriter &xml, const QString &cellStyle,
const QString &paragraphStyle)
{
//Cell
// Cell
xml.writeStartElement("table:table-cell");
xml.writeAttribute("office:value-type", "string");
xml.writeAttribute("table:style-name", cellStyle);
//text:p
// text:p
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", paragraphStyle);
}
@ -84,8 +87,8 @@ void writeCellListStart(QXmlStreamWriter &xml, const QString &cellStyle, const Q
*/
void writeCellListEnd(QXmlStreamWriter &xml)
{
xml.writeEndElement(); //text:p
xml.writeEndElement(); //table-cell
xml.writeEndElement(); // text:p
xml.writeEndElement(); // table-cell
}
void writeStandardStyle(QXmlStreamWriter &xml)
@ -96,7 +99,7 @@ void writeStandardStyle(QXmlStreamWriter &xml)
xml.writeAttribute("style:family", "paragraph");
xml.writeAttribute("style:class", "text");
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
}
void writeGraphicsStyle(QXmlStreamWriter &xml)
@ -113,9 +116,9 @@ void writeGraphicsStyle(QXmlStreamWriter &xml)
xml.writeAttribute("style:vertical-rel", "paragraph");
xml.writeAttribute("style:horizontal-pos", "center");
xml.writeAttribute("style:horizontal-rel", "paragraph");
xml.writeEndElement(); //style:graphic-properties
xml.writeEndElement(); // style:graphic-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
}
void writeCommonStyles(QXmlStreamWriter &xml)
@ -154,14 +157,14 @@ void writeCommonStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "18pt");
xml.writeAttribute("fo:font-weight", "bold");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
/* Style P4
* type: paragraph style
@ -199,14 +202,14 @@ void writeCommonStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "12pt");
xml.writeAttribute("fo:font-weight", "bold");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
/* Style T1
* type: text style
@ -225,14 +228,14 @@ void writeCommonStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-weight", "bold");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
}
void writeFooterStyle(QXmlStreamWriter &xml)
{
//Base style for MP1 style used in header/footer
// Base style for MP1 style used in header/footer
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "paragraph");
@ -250,33 +253,33 @@ void writeFooterStyle(QXmlStreamWriter &xml)
xml.writeStartElement("style:tab-stop");
xml.writeAttribute("style:position", "8.5cm");
xml.writeAttribute("style:type", "center");
xml.writeEndElement(); //style:tab-stop
xml.writeEndElement(); // style:tab-stop
xml.writeStartElement("style:tab-stop");
xml.writeAttribute("style:position", "17cm");
xml.writeAttribute("style:type", "right");
xml.writeEndElement(); //style:tab-stop
xml.writeEndElement(); // style:tab-stop
xml.writeEndElement(); //style:tab-stops
xml.writeEndElement(); // style:tab-stops
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
}
void writePageLayout(QXmlStreamWriter &xml)
{
//Footer style
// Footer style
xml.writeStartElement("style:style");
xml.writeAttribute("style:name", "MP1");
xml.writeAttribute("style:family", "paragraph");
xml.writeAttribute("style:parent-style-name", "Footer");
xml.writeStartElement("style:text-properties");
xml.writeAttribute("style:font-name", "Liberation Sans");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); // style:style
//Page Layout
// Page Layout
xml.writeStartElement("style:page-layout");
xml.writeAttribute("style:name", "Mpm1");
@ -286,25 +289,25 @@ void writePageLayout(QXmlStreamWriter &xml)
xml.writeAttribute("fo:margin-bottom", "2cm");
xml.writeAttribute("fo:margin-left", "2cm");
xml.writeAttribute("fo:margin-right", "2cm");
xml.writeEndElement(); //style:page-layout-properties
xml.writeEndElement(); // style:page-layout-properties
xml.writeStartElement("style:header-style");
xml.writeEndElement(); //style:header-style
xml.writeEndElement(); // style:header-style
xml.writeStartElement("style:footer-style");
xml.writeEndElement(); //style:footer-style
xml.writeEndElement(); // style:footer-style
xml.writeEndElement(); //style:page-layout
xml.writeEndElement(); // style:page-layout
}
void writeHeaderFooter(QXmlStreamWriter &xml, const QString& headerText, const QString& footerText)
void writeHeaderFooter(QXmlStreamWriter &xml, const QString &headerText, const QString &footerText)
{
xml.writeStartElement("style:master-page");
xml.writeAttribute("style:name", "Standard");
xml.writeAttribute("style:page-layout-name", "Mpm1"); //TODO
xml.writeAttribute("style:page-layout-name", "Mpm1"); // TODO
//Header
// Header
xml.writeStartElement("style:header");
xml.writeStartElement("text:p");
@ -313,10 +316,10 @@ void writeHeaderFooter(QXmlStreamWriter &xml, const QString& headerText, const Q
xml.writeCharacters(headerText);
xml.writeStartElement("text:tab");
xml.writeEndElement(); //text:tab
xml.writeEndElement(); // text:tab
xml.writeStartElement("text:tab");
xml.writeEndElement(); //text:tab
xml.writeEndElement(); // text:tab
const QString pageStr = Odt::text(Odt::headerPage);
@ -324,13 +327,13 @@ void writeHeaderFooter(QXmlStreamWriter &xml, const QString& headerText, const Q
xml.writeStartElement("text:page-number");
xml.writeAttribute("text:select-page", "current");
xml.writeEndElement(); //text:page-number
xml.writeEndElement(); // text:page-number
xml.writeEndElement(); //text:p
xml.writeEndElement(); // text:p
xml.writeEndElement(); //style:header
xml.writeEndElement(); // style:header
//Header for left pages (mirrored)
// Header for left pages (mirrored)
xml.writeStartElement("style:header-left");
xml.writeStartElement("text:p");
@ -340,21 +343,21 @@ void writeHeaderFooter(QXmlStreamWriter &xml, const QString& headerText, const Q
xml.writeStartElement("text:page-number");
xml.writeAttribute("text:select-page", "current");
xml.writeEndElement(); //text:page-number
xml.writeEndElement(); // text:page-number
xml.writeStartElement("text:tab");
xml.writeEndElement(); //text:tab
xml.writeEndElement(); // text:tab
xml.writeStartElement("text:tab");
xml.writeEndElement(); //text:tab
xml.writeEndElement(); // text:tab
xml.writeCharacters(headerText);
xml.writeEndElement(); //text:p
xml.writeEndElement(); // text:p
xml.writeEndElement(); //style:header-left
xml.writeEndElement(); // style:header-left
//Footer
// Footer
xml.writeStartElement("style:footer");
xml.writeStartElement("text:p");
@ -362,19 +365,19 @@ void writeHeaderFooter(QXmlStreamWriter &xml, const QString& headerText, const Q
xml.writeCharacters(footerText);
xml.writeEndElement(); //text:p
xml.writeEndElement(); // text:p
xml.writeEndElement(); //style:footer
xml.writeEndElement(); // style:footer
xml.writeEndElement(); //style:master-page
xml.writeEndElement(); // style:master-page
}
void writeLiberationFontFaces(QXmlStreamWriter &xml)
{
const QString variablePitch = QStringLiteral("variable");
const QString variablePitch = QStringLiteral("variable");
const QString liberationSerif = QStringLiteral("Liberation Serif");
const QString liberationSans = QStringLiteral("Liberation Sans");
const QString liberationMono = QStringLiteral("Liberation Mono");
const QString liberationSans = QStringLiteral("Liberation Sans");
const QString liberationMono = QStringLiteral("Liberation Mono");
writeFontFace(xml, liberationSerif, liberationSerif, "roman", variablePitch);
writeFontFace(xml, liberationSans, liberationSans, "swiss", variablePitch);
@ -386,18 +389,18 @@ QString Odt::text(const Text &t)
QTranslator *translator = Session->getSheetExportTranslator();
QString result;
if(translator)
if (translator)
{
//Prefer selected language
// Prefer selected language
result = translator->translate("Odt", t.sourceText, t.disambiguation);
}
else if(Session->getSheetExportLocale() == MeetingSession::embeddedLocale)
else if (Session->getSheetExportLocale() == MeetingSession::embeddedLocale)
{
//Bypass any translation and use hardcoded string literals
// Bypass any translation and use hardcoded string literals
return QString::fromUtf8(t.sourceText);
}
if(result.isNull()) //Fallback to application language
if (result.isNull()) // Fallback to application language
result = tr(t.sourceText, t.disambiguation);
return result;
}

View File

@ -24,16 +24,14 @@
#include <QCoreApplication>
//small util
void writeColumnStyle(QXmlStreamWriter& xml, const QString& name, const QString& width);
// small util
void writeColumnStyle(QXmlStreamWriter &xml, const QString &name, const QString &width);
void writeCell(QXmlStreamWriter &xml,
const QString& cellStyle,
const QString& paragraphStyle,
const QString& text);
void writeCell(QXmlStreamWriter &xml, const QString &cellStyle, const QString &paragraphStyle,
const QString &text);
void writeCellListStart(QXmlStreamWriter &xml, const QString &cellStyle, const QString &paragraphStyle);
void writeCellListStart(QXmlStreamWriter &xml, const QString &cellStyle,
const QString &paragraphStyle);
void writeCellListEnd(QXmlStreamWriter &xml);
void writeStandardStyle(QXmlStreamWriter &xml);
@ -46,37 +44,34 @@ void writeFooterStyle(QXmlStreamWriter &xml);
void writePageLayout(QXmlStreamWriter &xml);
void writeHeaderFooter(QXmlStreamWriter &xml,
const QString& headerText,
const QString& footerText);
void writeHeaderFooter(QXmlStreamWriter &xml, const QString &headerText, const QString &footerText);
inline void writeFontFace(QXmlStreamWriter &xml,
const QString& name, const QString& family,
const QString& genericFamily, const QString& pitch)
inline void writeFontFace(QXmlStreamWriter &xml, const QString &name, const QString &family,
const QString &genericFamily, const QString &pitch)
{
xml.writeStartElement("style:font-face");
xml.writeAttribute("style:name", name);
if(!family.isEmpty())
if (!family.isEmpty())
{
QString familyQuoted;
familyQuoted.reserve(family.size() + 2);
bool needsQuotes = family.contains(' '); //If family name contains blanks
if(needsQuotes) //Enclose in single quotes
bool needsQuotes = family.contains(' '); // If family name contains blanks
if (needsQuotes) // Enclose in single quotes
familyQuoted.append('\'');
familyQuoted.append(family);
if(needsQuotes)
if (needsQuotes)
familyQuoted.append('\'');
xml.writeAttribute("svg:font-family", familyQuoted);
}
if(!genericFamily.isEmpty())
if (!genericFamily.isEmpty())
{
xml.writeAttribute("svg:font-family-generic", genericFamily);
}
if(!pitch.isEmpty())
if (!pitch.isEmpty())
{
xml.writeAttribute("svg:font-pitch", pitch);
}
xml.writeEndElement(); //style:font-face
xml.writeEndElement(); // style:font-face
}
void writeLiberationFontFaces(QXmlStreamWriter &xml);
@ -91,72 +86,90 @@ public:
const char *disambiguation;
};
static QString text(const Text& t);
static QString text(const Text &t);
public:
//Header/Footer
static constexpr Text headerPage = QT_TRANSLATE_NOOP3("Odt", "Page ",
"Header page, leave space at end, page number will be added");
// Header/Footer
static constexpr Text headerPage = QT_TRANSLATE_NOOP3(
"Odt", "Page ", "Header page, leave space at end, page number will be added");
//Meeting
// Meeting
static constexpr Text meeting = QT_TRANSLATE_NOOP3("Odt", "Meeting", "Document keywords");
static constexpr Text meetingFromTo = QT_TRANSLATE_NOOP3("Odt", "Meeting in %1 from %2 to %3",
"Document description, where, from date, to date");
static constexpr Text meetingOnDate = QT_TRANSLATE_NOOP3("Odt", "Meeting in %1 on %2",
"Document description, where, when");
static constexpr Text meetingFromToShort = QT_TRANSLATE_NOOP3("Odt", "From %1 to %2",
"Shift cover, meeting from date, to date");
static constexpr Text meetingFromTo = QT_TRANSLATE_NOOP3(
"Odt", "Meeting in %1 from %2 to %3", "Document description, where, from date, to date");
static constexpr Text meetingOnDate =
QT_TRANSLATE_NOOP3("Odt", "Meeting in %1 on %2", "Document description, where, when");
static constexpr Text meetingFromToShort =
QT_TRANSLATE_NOOP3("Odt", "From %1 to %2", "Shift cover, meeting from date, to date");
//Rollingstock
static constexpr Text CoupledAbbr = QT_TRANSLATE_NOOP3("Odt", "Cp:", "Job stop coupled RS");
static constexpr Text UncoupledAbbr = QT_TRANSLATE_NOOP3("Odt", "Unc:", "Job stop uncoupled RS");
// Rollingstock
static constexpr Text CoupledAbbr = QT_TRANSLATE_NOOP3("Odt", "Cp:", "Job stop coupled RS");
static constexpr Text UncoupledAbbr =
QT_TRANSLATE_NOOP3("Odt", "Unc:", "Job stop uncoupled RS");
static constexpr Text genericRSOwner = QT_TRANSLATE_NOOP3("Odt", "Owner", "Rollingstock Owner");
static constexpr Text rsSessionTitle = QT_TRANSLATE_NOOP3("Odt", "Rollingstock by %1 at %2 of session",
"Rollingstock Session title, 1 is Owner/Station and 2 is start/end");
static constexpr Text rsSessionStart = QT_TRANSLATE_NOOP3("Odt", "start", "Rollingstock Session start");
static constexpr Text rsSessionEnd = QT_TRANSLATE_NOOP3("Odt", "end", "Rollingstock Session end");
static constexpr Text rsSessionTitle =
QT_TRANSLATE_NOOP3("Odt", "Rollingstock by %1 at %2 of session",
"Rollingstock Session title, 1 is Owner/Station and 2 is start/end");
static constexpr Text rsSessionStart =
QT_TRANSLATE_NOOP3("Odt", "start", "Rollingstock Session start");
static constexpr Text rsSessionEnd =
QT_TRANSLATE_NOOP3("Odt", "end", "Rollingstock Session end");
//Job Summary
static constexpr Text jobSummaryFrom = QT_TRANSLATE_NOOP3("Odt", "From:", "Job summary");
static constexpr Text jobSummaryTo = QT_TRANSLATE_NOOP3("Odt", "To:", "Job summary");
// Job Summary
static constexpr Text jobSummaryFrom = QT_TRANSLATE_NOOP3("Odt", "From:", "Job summary");
static constexpr Text jobSummaryTo = QT_TRANSLATE_NOOP3("Odt", "To:", "Job summary");
static constexpr Text jobSummaryDep = QT_TRANSLATE_NOOP3("Odt", "Departure:", "Job summary");
static constexpr Text jobSummaryArr = QT_TRANSLATE_NOOP3("Odt", "Arrival:", "Job summary");
static constexpr Text jobSummaryAxes = QT_TRANSLATE_NOOP3("Odt", "Axes:", "Job summary");
static constexpr Text jobSummaryArr = QT_TRANSLATE_NOOP3("Odt", "Arrival:", "Job summary");
static constexpr Text jobSummaryAxes = QT_TRANSLATE_NOOP3("Odt", "Axes:", "Job summary");
//Job Stops Header
static constexpr Text station = QT_TRANSLATE_NOOP3("Odt", "Station", "Job stop table");
static constexpr Text stationPageTitle = QT_TRANSLATE_NOOP3("Odt", "Station: %1", "Station title in station sheet");
static constexpr Text stationDocTitle = QT_TRANSLATE_NOOP3("Odt", "%1 station", "Station sheet title in document metadata");
static constexpr Text rollingstock = QT_TRANSLATE_NOOP3("Odt", "Rollingstock", "Job stop table");
static constexpr Text jobNr = QT_TRANSLATE_NOOP3("Odt", "Job Nr", "Job column");
// Job Stops Header
static constexpr Text station = QT_TRANSLATE_NOOP3("Odt", "Station", "Job stop table");
static constexpr Text stationPageTitle =
QT_TRANSLATE_NOOP3("Odt", "Station: %1", "Station title in station sheet");
static constexpr Text stationDocTitle =
QT_TRANSLATE_NOOP3("Odt", "%1 station", "Station sheet title in document metadata");
static constexpr Text rollingstock =
QT_TRANSLATE_NOOP3("Odt", "Rollingstock", "Job stop table");
static constexpr Text jobNr = QT_TRANSLATE_NOOP3("Odt", "Job Nr", "Job column");
static constexpr Text arrival = QT_TRANSLATE_NOOP3("Odt", "Arrival", "Job stop table");
static constexpr Text departure = QT_TRANSLATE_NOOP3("Odt", "Departure", "Job stop table");
static constexpr Text arrivalShort = QT_TRANSLATE_NOOP3("Odt", "Arr.", "Arrival abbreviated");
static constexpr Text departureShort = QT_TRANSLATE_NOOP3("Odt", "Dep.", "Departure abbreviated");
static constexpr Text arrival = QT_TRANSLATE_NOOP3("Odt", "Arrival", "Job stop table");
static constexpr Text departure = QT_TRANSLATE_NOOP3("Odt", "Departure", "Job stop table");
static constexpr Text arrivalShort = QT_TRANSLATE_NOOP3("Odt", "Arr.", "Arrival abbreviated");
static constexpr Text departureShort =
QT_TRANSLATE_NOOP3("Odt", "Dep.", "Departure abbreviated");
static constexpr Text stationFromCol = QT_TRANSLATE_NOOP3("Odt", "From", "Station stop table, From previous station column");
static constexpr Text stationToCol = QT_TRANSLATE_NOOP3("Odt", "To", "Station stop table, To next station column");
static constexpr Text stationFromCol =
QT_TRANSLATE_NOOP3("Odt", "From", "Station stop table, From previous station column");
static constexpr Text stationToCol =
QT_TRANSLATE_NOOP3("Odt", "To", "Station stop table, To next station column");
static constexpr Text jobStopPlatf = QT_TRANSLATE_NOOP3("Odt", "Platf", "Job stop table, platform abbreviated");
static constexpr Text jobStopIsTransit = QT_TRANSLATE_NOOP3("Odt", "Transit", "Job stop table, notes column");
static constexpr Text jobStopIsFirst = QT_TRANSLATE_NOOP3("Odt", "START",
"Station stop table, notes column for first job stop, "
"keep in English it's the same for every sheet");
static constexpr Text jobStopPlatf =
QT_TRANSLATE_NOOP3("Odt", "Platf", "Job stop table, platform abbreviated");
static constexpr Text jobStopIsTransit =
QT_TRANSLATE_NOOP3("Odt", "Transit", "Job stop table, notes column");
static constexpr Text jobStopIsFirst =
QT_TRANSLATE_NOOP3("Odt", "START",
"Station stop table, notes column for first job stop, "
"keep in English it's the same for every sheet");
static constexpr Text jobStopCross = QT_TRANSLATE_NOOP3("Odt", "Crossings", "Job stop table");
static constexpr Text jobStopPassings = QT_TRANSLATE_NOOP3("Odt", "Passings", "Job stop table");
static constexpr Text jobStopCrossShort = QT_TRANSLATE_NOOP3("Odt", "Cross", "Job stop crossings abbreviated column");
static constexpr Text jobStopPassingsShort = QT_TRANSLATE_NOOP3("Odt", "Passes", "Job stop passings abbreviated column");
static constexpr Text jobStopCross = QT_TRANSLATE_NOOP3("Odt", "Crossings", "Job stop table");
static constexpr Text jobStopPassings = QT_TRANSLATE_NOOP3("Odt", "Passings", "Job stop table");
static constexpr Text jobStopCrossShort =
QT_TRANSLATE_NOOP3("Odt", "Cross", "Job stop crossings abbreviated column");
static constexpr Text jobStopPassingsShort =
QT_TRANSLATE_NOOP3("Odt", "Passes", "Job stop passings abbreviated column");
static constexpr Text notes = QT_TRANSLATE_NOOP3("Odt", "Notes", "Job stop table");
//Job stops
static constexpr Text jobReverseDirection = QT_TRANSLATE_NOOP3("Odt", "Reverse direction", "Job stop table");
// Job stops
static constexpr Text jobReverseDirection =
QT_TRANSLATE_NOOP3("Odt", "Reverse direction", "Job stop table");
//Shift
static constexpr Text shiftCoverTitle = QT_TRANSLATE_NOOP3("Odt", "SHIFT %1", "Shift title in shift sheet cover");
static constexpr Text shiftDocTitle = QT_TRANSLATE_NOOP3("Odt", "Shift %1", "Shift sheet document title for metadata");
// Shift
static constexpr Text shiftCoverTitle =
QT_TRANSLATE_NOOP3("Odt", "SHIFT %1", "Shift title in shift sheet cover");
static constexpr Text shiftDocTitle =
QT_TRANSLATE_NOOP3("Odt", "Shift %1", "Shift sheet document title for metadata");
};
#endif // ODTUTILS_H

View File

@ -33,43 +33,39 @@ SessionRSWriter::SessionRSWriter(database &db, SessionRSMode mode, SessionRSOrde
m_mode(mode),
m_order(order)
{
//TODO: fetch departure instead of arrival for start session
const auto sql = QStringLiteral("SELECT %1,"
" %2, %3, %4,"
" rs_list.id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type,"
" t1.name,t2.name,"
" stops.job_id, jobs.category, coupling.operation"
" FROM rs_list"
" JOIN coupling ON coupling.rs_id=rs_list.id"
" JOIN stops ON stops.id=coupling.stop_id"
" JOIN jobs ON jobs.id=stops.job_id"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN station_gate_connections g1 ON g1.id=stops.in_gate_conn"
" LEFT JOIN station_gate_connections g2 ON g2.id=stops.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"
" JOIN %5"
" GROUP BY rs_list.id"
" ORDER BY %6, stops.arrival, stops.job_id, rs_list.model_id");
// TODO: fetch departure instead of arrival for start session
const auto sql = QStringLiteral(
"SELECT %1,"
" %2, %3, %4,"
" rs_list.id, rs_list.number, rs_models.name, rs_models.suffix, rs_models.type,"
" t1.name,t2.name,"
" stops.job_id, jobs.category, coupling.operation"
" FROM rs_list"
" JOIN coupling ON coupling.rs_id=rs_list.id"
" JOIN stops ON stops.id=coupling.stop_id"
" JOIN jobs ON jobs.id=stops.job_id"
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" LEFT JOIN station_gate_connections g1 ON g1.id=stops.in_gate_conn"
" LEFT JOIN station_gate_connections g2 ON g2.id=stops.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"
" JOIN %5"
" GROUP BY rs_list.id"
" ORDER BY %6, stops.arrival, stops.job_id, rs_list.model_id");
QString temp = sql.arg(m_mode == SessionRSMode::StartOfSession ? "MIN(stops.arrival)" : "MAX(stops.departure)");
if(m_order == SessionRSOrder::ByStation)
QString temp = sql.arg(m_mode == SessionRSMode::StartOfSession ? "MIN(stops.arrival)"
: "MAX(stops.departure)");
if (m_order == SessionRSOrder::ByStation)
{
temp = temp.arg("rs_list.owner_id",
"rs_owners.name",
"stops.station_id",
"rs_owners ON rs_owners.id=rs_list.owner_id",
"stops.station_id");
temp = temp.arg("rs_list.owner_id", "rs_owners.name", "stops.station_id",
"rs_owners ON rs_owners.id=rs_list.owner_id", "stops.station_id");
q_getParentName.prepare("SELECT name FROM stations WHERE id=?");
}
else
{
temp = temp.arg("stops.station_id",
"stations.name",
"rs_list.owner_id",
"stations ON stations.id=stops.station_id",
"rs_list.owner_id");
temp = temp.arg("stops.station_id", "stations.name", "rs_list.owner_id",
"stations ON stations.id=stops.station_id", "rs_list.owner_id");
q_getParentName.prepare("SELECT name FROM rs_owners WHERE id=?");
}
@ -91,7 +87,8 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
* Like P4 but not bold, and Sans Serif
*
* Usages:
* - job_stops: stop cell text for normal stops and transit Rollingstock/Crossings/Passings/Description
* - job_stops: stop cell text for normal stops and transit
* Rollingstock/Crossings/Passings/Description
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "paragraph");
@ -100,27 +97,26 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("style:font-name", "Liberation Sans");
xml.writeAttribute("fo:font-size", "12pt");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
//rs_table Table
// rs_table Table
/* Style: rs_5f_table
*
* Type: table
* Display name: rollingstock
* Align: left
* Width: 16.0cm
*
* Usage:
* - SessionRSWriter: main table for Rollingstock Owners/Stations
*/
*
* Type: table
* Display name: rollingstock
* Align: left
* Width: 16.0cm
*
* Usage:
* - SessionRSWriter: main table for Rollingstock Owners/Stations
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "table");
xml.writeAttribute("style:name", "rs_5f_table");
@ -129,15 +125,15 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
xml.writeAttribute("style:shadow", "none");
xml.writeAttribute("table:align", "left");
xml.writeAttribute("style:width", "16.0cm");
xml.writeEndElement(); //style:table-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-properties
xml.writeEndElement(); // style
//rs_table columns
writeColumnStyle(xml, "rs_5f_table.A", "3.00cm"); //RS Name
writeColumnStyle(xml, "rs_5f_table.B", "4.45cm"); //Job
writeColumnStyle(xml, "rs_5f_table.C", "2.21cm"); //Platf
writeColumnStyle(xml, "rs_5f_table.D", "3.17cm"); //Departure or Arrival
writeColumnStyle(xml, "rs_5f_table.E", "4.00cm"); //Station or Owner
// rs_table columns
writeColumnStyle(xml, "rs_5f_table.A", "3.00cm"); // RS Name
writeColumnStyle(xml, "rs_5f_table.B", "4.45cm"); // Job
writeColumnStyle(xml, "rs_5f_table.C", "2.21cm"); // Platf
writeColumnStyle(xml, "rs_5f_table.D", "3.17cm"); // Departure or Arrival
writeColumnStyle(xml, "rs_5f_table.E", "4.00cm"); // Station or Owner
/* Style: rs_5f_table.A1
*
@ -158,8 +154,8 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-right", "none");
xml.writeAttribute("fo:border-top", "0.05pt solid #000000");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: rs_5f_table.E1
*
@ -177,8 +173,8 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:table-cell-properties");
xml.writeAttribute("fo:padding", "0.049cm");
xml.writeAttribute("fo:border", "0.05pt solid #000000");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: rs_5f_table.A2
*
@ -199,8 +195,8 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-right", "none");
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: rs_5f_table.E2
*
@ -221,90 +217,93 @@ void SessionRSWriter::writeStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-right", "0.05pt solid #000000");
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
}
db_id SessionRSWriter::writeTable(QXmlStreamWriter &xml, const QString& parentName)
db_id SessionRSWriter::writeTable(QXmlStreamWriter &xml, const QString &parentName)
{
//Table '???_table' where ??? is the station/owner name without spaces
// Table '???_table' where ??? is the station/owner name without spaces
QString tableName = parentName;
tableName.replace(' ', '_'); //Replace spaces with underscores
tableName.replace(' ', '_'); // Replace spaces with underscores
tableName.append("_table");
xml.writeStartElement("table:table");
xml.writeAttribute("table:name", tableName);
xml.writeAttribute("table:style-name", "rs_5f_table");
//Columns
xml.writeEmptyElement("table:table-column"); //A - RS Name
// Columns
xml.writeEmptyElement("table:table-column"); // A - RS Name
xml.writeAttribute("table:style-name", "rs_5f_table.A");
xml.writeEmptyElement("table:table-column"); //B - Job
xml.writeEmptyElement("table:table-column"); // B - Job
xml.writeAttribute("table:style-name", "rs_5f_table.B");
xml.writeEmptyElement("table:table-column"); //C - Platf
xml.writeEmptyElement("table:table-column"); // C - Platf
xml.writeAttribute("table:style-name", "rs_5f_table.C");
xml.writeEmptyElement("table:table-column"); //D - Departure
xml.writeEmptyElement("table:table-column"); // D - Departure
xml.writeAttribute("table:style-name", "rs_5f_table.D");
xml.writeEmptyElement("table:table-column"); //E - Station or Owner
xml.writeEmptyElement("table:table-column"); // E - Station or Owner
xml.writeAttribute("table:style-name", "rs_5f_table.E");
//Row 1 (Heading)
// Row 1 (Heading)
xml.writeStartElement("table:table-header-rows");
xml.writeStartElement("table:table-row");
const QString P4_style = QStringLiteral("P4");
const QString P5_style = QStringLiteral("P5");
//Cells (column names, headings)
// Cells (column names, headings)
writeCell(xml, "rs_5f_table.A1", P4_style, Odt::text(Odt::rollingstock));
writeCell(xml, "rs_5f_table.A1", P4_style, Odt::text(Odt::jobNr));
writeCell(xml, "rs_5f_table.A1", P4_style, Odt::text(Odt::jobStopPlatf));
writeCell(xml, "rs_5f_table.A1", P4_style, Odt::text(m_mode == SessionRSMode::StartOfSession ? Odt::departure : Odt::arrival));
writeCell(xml, "rs_5f_table.E1", P4_style, Odt::text(m_order == SessionRSOrder::ByStation ? Odt::genericRSOwner : Odt::station));
writeCell(xml, "rs_5f_table.A1", P4_style,
Odt::text(m_mode == SessionRSMode::StartOfSession ? Odt::departure : Odt::arrival));
writeCell(xml, "rs_5f_table.E1", P4_style,
Odt::text(m_order == SessionRSOrder::ByStation ? Odt::genericRSOwner : Odt::station));
xml.writeEndElement(); //end of row
xml.writeEndElement(); //header section
xml.writeEndElement(); // end of row
xml.writeEndElement(); // header section
//Fill the table
for(; it != q_getSessionRS.end(); ++it)
// Fill the table
for (; it != q_getSessionRS.end(); ++it)
{
auto rs = *it;
QTime time = rs.get<QTime>(0); //Departure or arrival
auto rs = *it;
QTime time = rs.get<QTime>(0); // Departure or arrival
//db_id stationOrOwnerId = rs.get<db_id>(1);
QString name = rs.get<QString>(2); //Name of the station or owner
db_id parentId = rs.get<db_id>(3); //ownerOrStation (opposite of stationOrOwner)
// db_id stationOrOwnerId = rs.get<db_id>(1);
QString name = rs.get<QString>(2); // Name of the station or owner
db_id parentId = rs.get<db_id>(3); // ownerOrStation (opposite of stationOrOwner)
//db_id rsId = rs.get<db_id>(4);
int number = rs.get<int>(5);
sqlite3_stmt *stmt = q_getSessionRS.stmt();
int modelNameLen = sqlite3_column_bytes(stmt, 6);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 6));
// db_id rsId = rs.get<db_id>(4);
int number = rs.get<int>(5);
sqlite3_stmt *stmt = q_getSessionRS.stmt();
int modelNameLen = sqlite3_column_bytes(stmt, 6);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 6));
int modelSuffixLen = sqlite3_column_bytes(stmt, 7);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 7));
RsType type = RsType(rs.get<int>(8));
int modelSuffixLen = sqlite3_column_bytes(stmt, 7);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 7));
RsType type = RsType(rs.get<int>(8));
QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix, modelSuffixLen, type);
QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
modelSuffixLen, type);
QString platform = rs.get<QString>(9);
if(platform.isEmpty())
platform = rs.get<QString>(10); //Use out gate to get track name
if (platform.isEmpty())
platform = rs.get<QString>(10); // Use out gate to get track name
db_id jobId = rs.get<db_id>(11);
db_id jobId = rs.get<db_id>(11);
JobCategory jobCat = JobCategory(rs.get<int>(12));
if(parentId != lastParentId)
if (parentId != lastParentId)
{
xml.writeEndElement(); //table:table
xml.writeEndElement(); // table:table
return parentId;
}
xml.writeStartElement("table:table-row"); //start new row
xml.writeStartElement("table:table-row"); // start new row
writeCell(xml, "rs_5f_table.A2", P5_style, rsName);
writeCell(xml, "rs_5f_table.A2", P5_style, JobCategoryName::jobName(jobId, jobCat));
@ -312,18 +311,18 @@ db_id SessionRSWriter::writeTable(QXmlStreamWriter &xml, const QString& parentNa
writeCell(xml, "rs_5f_table.A2", P5_style, time.toString("HH:mm"));
writeCell(xml, "rs_5f_table.E2", P5_style, name);
xml.writeEndElement(); //end of row
xml.writeEndElement(); // end of row
}
xml.writeEndElement(); //table:table
xml.writeEndElement(); // table:table
return 0; //End of document, no more tables
return 0; // End of document, no more tables
}
void SessionRSWriter::writeContent(QXmlStreamWriter &xml)
{
it = q_getSessionRS.begin();
if(it == q_getSessionRS.end())
if (it == q_getSessionRS.end())
return;
lastParentId = (*it).get<db_id>(3);
@ -334,7 +333,7 @@ void SessionRSWriter::writeContent(QXmlStreamWriter &xml)
QString name = q_getParentName.getRows().get<QString>(0);
q_getParentName.reset();
//Write Station or Rollingstock Owner name
// Write Station or Rollingstock Owner name
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeCharacters(name);
@ -342,7 +341,7 @@ void SessionRSWriter::writeContent(QXmlStreamWriter &xml)
lastParentId = writeTable(xml, name);
//Add some space
// Add some space
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeEndElement();
@ -351,8 +350,10 @@ void SessionRSWriter::writeContent(QXmlStreamWriter &xml)
QString SessionRSWriter::generateTitle() const
{
QString title = Odt::text(Odt::rsSessionTitle).arg(
Odt::text(m_order == SessionRSOrder::ByStation ? Odt::genericRSOwner : Odt::station),
Odt::text(m_mode == SessionRSMode::StartOfSession ? Odt::rsSessionStart : Odt::rsSessionEnd));
QString title =
Odt::text(Odt::rsSessionTitle)
.arg(Odt::text(m_order == SessionRSOrder::ByStation ? Odt::genericRSOwner : Odt::station),
Odt::text(m_mode == SessionRSMode::StartOfSession ? Odt::rsSessionStart
: Odt::rsSessionEnd));
return title;
}

View File

@ -35,7 +35,7 @@ public:
static void writeStyles(QXmlStreamWriter &xml);
db_id writeTable(QXmlStreamWriter& xml, const QString& parentName);
db_id writeTable(QXmlStreamWriter &xml, const QString &parentName);
void writeContent(QXmlStreamWriter &xml);

View File

@ -30,46 +30,48 @@
#include <QDebug>
//first == true for the first time the stop is writter
//the second time it should be false to skip the repetition of Arrival, Crossings, Passings
void StationWriter::insertStop(QXmlStreamWriter &xml, const Stop& stop, bool first, bool transit)
// first == true for the first time the stop is writter
// the second time it should be false to skip the repetition of Arrival, Crossings, Passings
void StationWriter::insertStop(QXmlStreamWriter &xml, const Stop &stop, bool first, bool transit)
{
const QString P3_style = "P3";
//Row
// Row
xml.writeStartElement("table:table-row");
//Cells, hide Arrival the second time
if(first)
// Cells, hide Arrival the second time
if (first)
{
//Arrival in bold, if transit bold + italic
// Arrival in bold, if transit bold + italic
writeCell(xml, "stationtable.A2", transit ? "P5" : "P4", stop.arrival.toString("HH:mm"));
//Departure, if transit don't repeat it
writeCell(xml, "stationtable.A2", P3_style, transit ? "-/-" : stop.departure.toString("HH:mm"));
// Departure, if transit don't repeat it
writeCell(xml, "stationtable.A2", P3_style,
transit ? "-/-" : stop.departure.toString("HH:mm"));
}
else
{
writeCell(xml, "stationtable.A2", P3_style, QString()); //Don't repeat Arrival
writeCell(xml, "stationtable.A2", "P4", stop.departure.toString("HH:mm")); //Departure in bold
writeCell(xml, "stationtable.A2", P3_style, QString()); // Don't repeat Arrival
writeCell(xml, "stationtable.A2", "P4",
stop.departure.toString("HH:mm")); // Departure in bold
}
//Job N
// Job N
writeCell(xml, "stationtable.A2", P3_style, JobCategoryName::jobName(stop.jobId, stop.jobCat));
writeCell(xml, "stationtable.A2", P3_style, stop.platform); //Platform
writeCell(xml, "stationtable.A2", P3_style, stop.platform); // Platform
//From Previous Station, write only first time
// From Previous Station, write only first time
writeCell(xml, "stationtable.A2", P3_style, first ? stop.prevSt : QString());
writeCell(xml, "stationtable.A2", P3_style, stop.nextSt);
if(!first)
if (!first)
{
//Fill with empty cells, needed to keep the order
writeCell(xml, "stationtable.A2", P3_style, QString()); //Rollingstock
writeCell(xml, "stationtable.A2", P3_style, QString()); //Crossings
writeCell(xml, "stationtable.A2", P3_style, QString()); //Passings
// Fill with empty cells, needed to keep the order
writeCell(xml, "stationtable.A2", P3_style, QString()); // Rollingstock
writeCell(xml, "stationtable.A2", P3_style, QString()); // Crossings
writeCell(xml, "stationtable.A2", P3_style, QString()); // Passings
//Notes, description, repaeat to user the arrival to better link the 2 rows
// Notes, description, repaeat to user the arrival to better link the 2 rows
writeCell(xml, "stationtable.L2", P3_style, stop.arrival.toString("HH:mm"));
}
}
@ -97,7 +99,8 @@ StationWriter::StationWriter(database &db) :
q_selectPassings(mDb, "SELECT stops.id,stops.job_id,jobs.category"
" FROM stops"
" JOIN jobs ON jobs.id=stops.job_id"
" WHERE stops.station_id=? AND stops.departure>=? AND stops.arrival<=? AND stops.job_id<>?"),
" WHERE stops.station_id=? AND stops.departure>=? AND stops.arrival<=? "
"AND stops.job_id<>?"),
q_getStopCouplings(mDb, "SELECT coupling.rs_id,"
"rs_list.number,rs_models.name,rs_models.suffix,rs_models.type"
" FROM coupling"
@ -105,22 +108,21 @@ StationWriter::StationWriter(database &db) :
" JOIN rs_models ON rs_models.id=rs_list.model_id"
" WHERE coupling.stop_id=? AND coupling.operation=?")
{
}
//TODO: common styles with JobWriter should go in common
// TODO: common styles with JobWriter should go in common
void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
{
/* Style: stationtable
*
* Type: table
* Display name: Station Table
* Align: center
* Width: 20.0cm
*
* Usage:
* - StationWriter: station sheet main table showing jobs that stop in this station
*/
*
* Type: table
* Display name: Station Table
* Align: center
* Width: 20.0cm
*
* Usage:
* - StationWriter: station sheet main table showing jobs that stop in this station
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "table");
xml.writeAttribute("style:name", "stationtable");
@ -129,20 +131,20 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("style:shadow", "none");
xml.writeAttribute("table:align", "center");
xml.writeAttribute("style:width", "20.0cm");
xml.writeEndElement(); //style:table-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-properties
xml.writeEndElement(); // style
//stationtable columns
writeColumnStyle(xml, "stationtable.A", "1.74cm"); //1 Arrival
writeColumnStyle(xml, "stationtable.B", "1.80cm"); //2 Departure
writeColumnStyle(xml, "stationtable.C", "1.93cm"); //3 Job N
writeColumnStyle(xml, "stationtable.D", "1.00cm"); //4 Platform (Platf)
writeColumnStyle(xml, "stationtable.E", "3.09cm"); //5 From (Previous Station)
writeColumnStyle(xml, "stationtable.F", "3.11cm"); //6 To (Next Station)
writeColumnStyle(xml, "stationtable.G", "3.00cm"); //7 Rollingstock
writeColumnStyle(xml, "stationtable.H", "2.10cm"); //8 Crossings
writeColumnStyle(xml, "stationtable.I", "2.10cm"); //9 Passings
writeColumnStyle(xml, "stationtable.L", "2.21cm"); //10 Description
// stationtable columns
writeColumnStyle(xml, "stationtable.A", "1.74cm"); // 1 Arrival
writeColumnStyle(xml, "stationtable.B", "1.80cm"); // 2 Departure
writeColumnStyle(xml, "stationtable.C", "1.93cm"); // 3 Job N
writeColumnStyle(xml, "stationtable.D", "1.00cm"); // 4 Platform (Platf)
writeColumnStyle(xml, "stationtable.E", "3.09cm"); // 5 From (Previous Station)
writeColumnStyle(xml, "stationtable.F", "3.11cm"); // 6 To (Next Station)
writeColumnStyle(xml, "stationtable.G", "3.00cm"); // 7 Rollingstock
writeColumnStyle(xml, "stationtable.H", "2.10cm"); // 8 Crossings
writeColumnStyle(xml, "stationtable.I", "2.10cm"); // 9 Passings
writeColumnStyle(xml, "stationtable.L", "2.21cm"); // 10 Description
/* Style: stationtable.A1
*
@ -165,8 +167,8 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "0.05pt solid #000000");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: stationtable.L1
*
@ -186,8 +188,8 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border", "0.05pt solid #000000");
xml.writeAttribute("fo:padding", "0.097cm");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: stationtable.A2
*
@ -210,8 +212,8 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style: stationtable.L2
*
@ -234,8 +236,8 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeAttribute("fo:border-top", "none");
xml.writeAttribute("fo:border-bottom", "0.05pt solid #000000");
xml.writeAttribute("style:vertical-align", "middle");
xml.writeEndElement(); //style:table-cell-properties
xml.writeEndElement(); //style
xml.writeEndElement(); // style:table-cell-properties
xml.writeEndElement(); // style
/* Style P2
* type: paragraph
@ -253,14 +255,14 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "13pt");
xml.writeAttribute("fo:font-weight", "bold");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
/* Style P3
* type: paragraph
@ -277,15 +279,15 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "10pt");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
//P5 - Bold, Italics, for Transits
// P5 - Bold, Italics, for Transits
/* Style P5
* type: paragraph
* text-align: center
@ -294,7 +296,8 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
* font-style: italic
*
* Usages:
* - stationtable table: Arrival for transit jobs (Normal stops have bold Arrival, transits have Bold + Italic)
* - stationtable table: Arrival for transit jobs (Normal stops have bold Arrival, transits have
* Bold + Italic)
*/
xml.writeStartElement("style:style");
xml.writeAttribute("style:family", "paragraph");
@ -303,20 +306,20 @@ void StationWriter::writeStationAutomaticStyles(QXmlStreamWriter &xml)
xml.writeStartElement("style:paragraph-properties");
xml.writeAttribute("fo:text-align", "center");
xml.writeAttribute("style:justify-single-word", "false");
xml.writeEndElement(); //style:paragraph-properties
xml.writeEndElement(); // style:paragraph-properties
xml.writeStartElement("style:text-properties");
xml.writeAttribute("fo:font-size", "12pt");
xml.writeAttribute("fo:font-weight", "bold");
xml.writeAttribute("fo:font-style", "italic");
xml.writeEndElement(); //style:text-properties
xml.writeEndElement(); // style:text-properties
xml.writeEndElement(); //style:style
xml.writeEndElement(); // style:style
}
void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString *stNameOut)
{
QMap<QTime, Stop> stops; //Order by Departure ASC
QMap<QTime, Stop> stops; // Order by Departure ASC
query q_getStName(mDb, "SELECT name,short_name FROM stations WHERE id=?");
@ -328,71 +331,71 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
QString stationName;
QString shortName;
q_getStName.bind(1, stationId);
if(q_getStName.step() == SQLITE_ROW)
if (q_getStName.step() == SQLITE_ROW)
{
auto r = q_getStName.getRows();
auto r = q_getStName.getRows();
stationName = r.get<QString>(0);
shortName = r.get<QString>(1);
shortName = r.get<QString>(1);
}
q_getStName.reset();
if(stNameOut)
if (stNameOut)
*stNameOut = stationName;
QString stationNameStr = stationName;
if(!shortName.isEmpty())
if (!shortName.isEmpty())
{
stationNameStr.append(QLatin1String(" ("));
stationNameStr.append(shortName);
stationNameStr.append(')');
}
//Title
// Title
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeCharacters(Odt::text(Odt::stationPageTitle).arg(stationNameStr));
xml.writeEndElement();
//Vertical space
// Vertical space
xml.writeStartElement("text:p");
xml.writeAttribute("text:style-name", "P1");
xml.writeEndElement();
//stationtable
// stationtable
xml.writeStartElement("table:table");
//xml.writeAttribute("table:name", "stationtable");
// xml.writeAttribute("table:name", "stationtable");
xml.writeAttribute("table:style-name", "stationtable");
xml.writeEmptyElement("table:table-column"); //Arrival
xml.writeEmptyElement("table:table-column"); // Arrival
xml.writeAttribute("table:style-name", "stationtable.A");
xml.writeEmptyElement("table:table-column"); //Departure
xml.writeEmptyElement("table:table-column"); // Departure
xml.writeAttribute("table:style-name", "stationtable.B");
xml.writeEmptyElement("table:table-column"); //Job N
xml.writeEmptyElement("table:table-column"); // Job N
xml.writeAttribute("table:style-name", "stationtable.C");
xml.writeEmptyElement("table:table-column"); //Platform (Bin)
xml.writeEmptyElement("table:table-column"); // Platform (Bin)
xml.writeAttribute("table:style-name", "stationtable.D");
xml.writeEmptyElement("table:table-column"); //Comes From
xml.writeEmptyElement("table:table-column"); // Comes From
xml.writeAttribute("table:style-name", "stationtable.E");
xml.writeEmptyElement("table:table-column"); //Goes To
xml.writeEmptyElement("table:table-column"); // Goes To
xml.writeAttribute("table:style-name", "stationtable.F");
xml.writeEmptyElement("table:table-column"); //Rollingstock
xml.writeEmptyElement("table:table-column"); // Rollingstock
xml.writeAttribute("table:style-name", "stationtable.G");
xml.writeEmptyElement("table:table-column"); //Crossings
xml.writeEmptyElement("table:table-column"); // Crossings
xml.writeAttribute("table:style-name", "stationtable.H");
xml.writeEmptyElement("table:table-column"); //Passings
xml.writeEmptyElement("table:table-column"); // Passings
xml.writeAttribute("table:style-name", "stationtable.I");
xml.writeEmptyElement("table:table-column"); //Notes
xml.writeEmptyElement("table:table-column"); // Notes
xml.writeAttribute("table:style-name", "stationtable.L");
//Row 1 (Heading)
// Row 1 (Heading)
xml.writeStartElement("table:table-header-rows");
xml.writeStartElement("table:table-row");
@ -405,54 +408,55 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
writeCell(xml, "stationtable.A1", "P2", Odt::text(Odt::rollingstock));
writeCell(xml, "stationtable.A1", "P2", Odt::text(Odt::jobStopCrossShort));
writeCell(xml, "stationtable.A1", "P2", Odt::text(Odt::jobStopPassingsShort));
writeCell(xml, "stationtable.L1", "P2", Odt::text(Odt::notes)); //Description
writeCell(xml, "stationtable.L1", "P2", Odt::text(Odt::notes)); // Description
xml.writeEndElement(); //end of row
xml.writeEndElement(); //header section
xml.writeEndElement(); // end of row
xml.writeEndElement(); // header section
//Stops
// Stops
q_getJobsByStation.bind(1, stationId);
for(auto r : q_getJobsByStation)
for (auto r : q_getJobsByStation)
{
db_id stopId = r.get<db_id>(0);
Stop stop;
stop.jobId = r.get<db_id>(1);
stop.jobCat = JobCategory(r.get<int>(2));
stop.arrival = r.get<QTime>(3);
stop.departure = r.get<QTime>(4);
stop.jobId = r.get<db_id>(1);
stop.jobCat = JobCategory(r.get<int>(2));
stop.arrival = r.get<QTime>(3);
stop.departure = r.get<QTime>(4);
const int stopType = r.get<int>(5);
stop.description = r.get<QString>(6);
stop.description = r.get<QString>(6);
stop.platform = r.get<QString>(7);
if(stop.platform.isEmpty())
stop.platform = r.get<QString>(8); //Use out gate to get track name
stop.platform = r.get<QString>(7);
if (stop.platform.isEmpty())
stop.platform = r.get<QString>(8); // Use out gate to get track name
utils::Side entranceSide = utils::Side(r.get<int>(9));
utils::Side exitSide = utils::Side(r.get<int>(10));
utils::Side exitSide = utils::Side(r.get<int>(10));
if(entranceSide == exitSide && r.column_type(9) != SQLITE_NULL && r.column_type(10) != SQLITE_NULL)
if (entranceSide == exitSide && r.column_type(9) != SQLITE_NULL
&& r.column_type(10) != SQLITE_NULL)
{
//Train enters and leaves from same track side, add reversal to description
// Train enters and leaves from same track side, add reversal to description
QString descr2 = Odt::text(Odt::jobReverseDirection);
if(!stop.description.isEmpty())
descr2.append('\n'); //Separate from manually set description
if (!stop.description.isEmpty())
descr2.append('\n'); // Separate from manually set description
descr2.append(stop.description);
stop.description = descr2;
}
const bool isTransit = stopType == 1;
//BIG TODO: if this is First or Last stop of this job
//then it shouldn't be duplicated in 2 rows
// BIG TODO: if this is First or Last stop of this job
// then it shouldn't be duplicated in 2 rows
//Comes from station
// Comes from station
q_getPrevStop.bind(1, stop.jobId);
q_getPrevStop.bind(2, stop.arrival);
if(q_getPrevStop.step() == SQLITE_ROW)
if (q_getPrevStop.step() == SQLITE_ROW)
{
db_id prevStId = q_getPrevStop.getRows().get<db_id>(1);
q_getStName.bind(1, prevStId);
if(q_getStName.step() == SQLITE_ROW)
if (q_getStName.step() == SQLITE_ROW)
{
stop.prevSt = q_getStName.getRows().get<QString>(0);
}
@ -460,14 +464,14 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
}
q_getPrevStop.reset();
//Goes to station
// Goes to station
q_getNextStop.bind(1, stop.jobId);
q_getNextStop.bind(2, stop.arrival);
if(q_getNextStop.step() == SQLITE_ROW)
if (q_getNextStop.step() == SQLITE_ROW)
{
db_id nextStId = q_getNextStop.getRows().get<db_id>(1);
q_getStName.bind(1, nextStId);
if(q_getStName.step() == SQLITE_ROW)
if (q_getStName.step() == SQLITE_ROW)
{
stop.nextSt = q_getStName.getRows().get<QString>(0);
}
@ -475,62 +479,60 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
}
q_getNextStop.reset();
for(auto s = stops.begin(); s != stops.end(); /*nothing because of erase*/)
for (auto s = stops.begin(); s != stops.end(); /*nothing because of erase*/)
{
//If 's' departs after 'stop' arrives then skip 's' for now
if(s->departure >= stop.arrival)
// If 's' departs after 'stop' arrives then skip 's' for now
if (s->departure >= stop.arrival)
{
++s;
continue;
}
//'s' departs before 'stop' arrives so we
//insert it again to remind station master (IT: capostazione)
// insert it again to remind station master (IT: capostazione)
insertStop(xml, s.value(), false, false);
xml.writeEndElement(); //table-row
xml.writeEndElement(); // table-row
//Then remove from the list otherwise it gets inserted infinite times
// Then remove from the list otherwise it gets inserted infinite times
s = stops.erase(s);
}
//Fill with basic data
// Fill with basic data
insertStop(xml, stop, true, isTransit);
//First time this stop is written, fill with other data
// First time this stop is written, fill with other data
//Rollingstock
// Rollingstock
sqlite3_stmt *stmt = q_getStopCouplings.stmt();
writeCellListStart(xml, "stationtable.A2", "P3");
//Coupled rollingstock
// Coupled rollingstock
bool firstCoupRow = true;
q_getStopCouplings.bind(1, stopId);
q_getStopCouplings.bind(2, int(RsOp::Coupled));
for(auto coup : q_getStopCouplings)
for (auto coup : q_getStopCouplings)
{
//db_id rsId = coup.get<db_id>(0);
// db_id rsId = coup.get<db_id>(0);
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 2));
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 2));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
type);
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number,
modelSuffix, modelSuffixLen, type);
if(firstCoupRow)
if (firstCoupRow)
{
firstCoupRow = false;
//Use bold font
// Use bold font
xml.writeStartElement("text:span");
xml.writeAttribute("text:style-name", "T1");
xml.writeCharacters(Odt::text(Odt::CoupledAbbr));
xml.writeEndElement(); //test:span
xml.writeEndElement(); // test:span
}
xml.writeEmptyElement("text:line-break");
@ -538,38 +540,36 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
}
q_getStopCouplings.reset();
//Unoupled rollingstock
// Unoupled rollingstock
bool firstUncoupRow = true;
q_getStopCouplings.bind(1, stopId);
q_getStopCouplings.bind(2, int(RsOp::Uncoupled));
for(auto coup : q_getStopCouplings)
for (auto coup : q_getStopCouplings)
{
//db_id rsId = coup.get<db_id>(0);
// db_id rsId = coup.get<db_id>(0);
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 2));
int number = coup.get<int>(1);
int modelNameLen = sqlite3_column_bytes(stmt, 2);
const char *modelName = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 2));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const*>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
int modelSuffixLen = sqlite3_column_bytes(stmt, 3);
const char *modelSuffix = reinterpret_cast<char const *>(sqlite3_column_text(stmt, 3));
RsType type = RsType(sqlite3_column_int(stmt, 4));
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen,
number,
modelSuffix, modelSuffixLen,
type);
const QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number,
modelSuffix, modelSuffixLen, type);
if(firstUncoupRow)
if (firstUncoupRow)
{
if(!firstCoupRow) //Not first row, there were coupled rs
xml.writeEmptyElement("text:line-break"); //Separate from coupled
if (!firstCoupRow) // Not first row, there were coupled rs
xml.writeEmptyElement("text:line-break"); // Separate from coupled
firstUncoupRow = false;
//Use bold font
// Use bold font
xml.writeStartElement("text:span");
xml.writeAttribute("text:style-name", "T1");
xml.writeCharacters(Odt::text(Odt::UncoupledAbbr));
xml.writeEndElement(); //test:span
xml.writeEndElement(); // test:span
}
xml.writeEmptyElement("text:line-break");
@ -578,7 +578,7 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
q_getStopCouplings.reset();
writeCellListEnd(xml);
//Crossings, Passings
// Crossings, Passings
QVector<JobEntry> passings;
JobStopDirectionHelper dirHelper(mDb);
utils::Side myDirection = dirHelper.getStopOutSide(stopId);
@ -589,24 +589,24 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
q_selectPassings.bind(4, stop.jobId);
firstCoupRow = true;
//Incroci
// Incroci
writeCellListStart(xml, "stationtable.A2", "P3");
for(auto pass : q_selectPassings)
for (auto pass : q_selectPassings)
{
db_id otherStopId = pass.get<db_id>(0);
db_id otherJobId = pass.get<db_id>(1);
db_id otherStopId = pass.get<db_id>(0);
db_id otherJobId = pass.get<db_id>(1);
JobCategory otherJobCat = JobCategory(pass.get<int>(2));
//QTime otherArr = pass.get<QTime>(3);
//QTime otherDep = pass.get<QTime>(4);
// QTime otherArr = pass.get<QTime>(3);
// QTime otherDep = pass.get<QTime>(4);
utils::Side otherDir = dirHelper.getStopOutSide(otherStopId);
if(myDirection == otherDir)
if (myDirection == otherDir)
passings.append({otherJobId, otherJobCat});
else
{
if(firstCoupRow)
if (firstCoupRow)
firstCoupRow = false;
else
xml.writeEmptyElement("text:line-break");
@ -616,12 +616,12 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
q_selectPassings.reset();
writeCellListEnd(xml);
//Passings
// Passings
firstCoupRow = true;
writeCellListStart(xml, "stationtable.A2", "P3");
for(auto entry : passings)
for (auto entry : passings)
{
if(firstCoupRow)
if (firstCoupRow)
firstCoupRow = false;
else
xml.writeEmptyElement("text:line-break");
@ -629,66 +629,66 @@ void StationWriter::writeStation(QXmlStreamWriter &xml, db_id stationId, QString
}
writeCellListEnd(xml);
//Description, Notes
// Description, Notes
writeCellListStart(xml, "stationtable.L2", "P3");
bool needsLineBreak = false;
if(isTransit)
if (isTransit)
{
xml.writeCharacters(Odt::text(Odt::jobStopIsTransit));
needsLineBreak = true;
}
if(stop.prevSt.isEmpty())
if (stop.prevSt.isEmpty())
{
if(needsLineBreak)
if (needsLineBreak)
xml.writeEmptyElement("text:line-break");
//This is Origin (First stop), use Bold font
// This is Origin (First stop), use Bold font
xml.writeStartElement("text:span");
xml.writeAttribute("text:style-name", "T1");
xml.writeCharacters(Odt::text(Odt::jobStopIsFirst));
xml.writeEndElement(); //test:span
xml.writeEndElement(); // test:span
needsLineBreak = true;
}
if(!stop.description.isEmpty())
if (!stop.description.isEmpty())
{
if(needsLineBreak) //go to new line after 'Transit' word
if (needsLineBreak) // go to new line after 'Transit' word
xml.writeEmptyElement("text:line-break");
//Split in lines
// Split in lines
int lastIdx = 0;
while(true)
while (true)
{
int idx = stop.description.indexOf('\n', lastIdx);
int idx = stop.description.indexOf('\n', lastIdx);
QString line = stop.description.mid(lastIdx, idx == -1 ? idx : idx - lastIdx);
xml.writeCharacters(line.simplified());
if(idx < 0)
break; //Last line
if (idx < 0)
break; // Last line
lastIdx = idx + 1;
xml.writeEmptyElement("text:line-break");
}
}
writeCellListEnd(xml);
xml.writeEndElement(); //table-row
xml.writeEndElement(); // table-row
if(!isTransit && !stop.prevSt.isEmpty() && !stop.nextSt.isEmpty())
if (!isTransit && !stop.prevSt.isEmpty() && !stop.nextSt.isEmpty())
{
//If it is a normal stop (not a transit and not first stop or last stop)
//insert two rows: one for arrival and another
//that reminds the station master (capostazione)
//the departure of that train
//In order to achieve this we put the stop in a list
//and write it again before another arrival (see above)
// If it is a normal stop (not a transit and not first stop or last stop)
// insert two rows: one for arrival and another
// that reminds the station master (capostazione)
// the departure of that train
// In order to achieve this we put the stop in a list
// and write it again before another arrival (see above)
stops.insert(stop.departure, stop);
}
}
q_getJobsByStation.reset();
for(const Stop& s : stops)
for (const Stop &s : stops)
{
insertStop(xml, s, false, false);
xml.writeEndElement(); //table-row
xml.writeEndElement(); // table-row
}
xml.writeEndElement(); //stationtable end
xml.writeEndElement(); // stationtable end
}

View File

@ -31,9 +31,9 @@ class QXmlStreamWriter;
class StationWriter
{
public:
StationWriter(database& db);
StationWriter(database &db);
static void writeStationAutomaticStyles(QXmlStreamWriter& xml);
static void writeStationAutomaticStyles(QXmlStreamWriter &xml);
void writeStation(QXmlStreamWriter &xml, db_id stationId, QString *stNameOut = nullptr);
@ -54,7 +54,7 @@ private:
JobCategory jobCat;
};
void insertStop(QXmlStreamWriter &xml, const Stop& stop, bool first, bool transit);
void insertStop(QXmlStreamWriter &xml, const Stop &stop, bool first, bool transit);
private:
database &mDb;

View File

@ -30,7 +30,6 @@ JobSheetExport::JobSheetExport(db_id jobId, JobCategory cat) :
m_jobId(jobId),
m_jobCat(cat)
{
}
void JobSheetExport::write()
@ -38,27 +37,27 @@ void JobSheetExport::write()
qDebug() << "TEMP:" << odt.dir.path();
odt.initDocument();
//styles.xml font declarations
// styles.xml font declarations
odt.stylesXml.writeStartElement("office:font-face-decls");
writeLiberationFontFaces(odt.stylesXml);
odt.stylesXml.writeEndElement(); //office:font-face-decls
odt.stylesXml.writeEndElement(); // office:font-face-decls
//Content font declarations
// Content font declarations
odt.contentXml.writeStartElement("office:font-face-decls");
writeLiberationFontFaces(odt.contentXml);
odt.contentXml.writeEndElement(); //office:font-face-decls
odt.contentXml.writeEndElement(); // office:font-face-decls
//Content Automatic styles
// Content Automatic styles
odt.contentXml.writeStartElement("office:automatic-styles");
JobWriter::writeJobAutomaticStyles(odt.contentXml);
//Styles
// Styles
odt.stylesXml.writeStartElement("office:styles");
writeCommonStyles(odt.stylesXml);
JobWriter::writeJobStyles(odt.stylesXml);
odt.stylesXml.writeEndElement();
//Body
// Body
odt.startBody();
JobWriter w(Session->m_Db);

View File

@ -24,14 +24,13 @@
#include "utils/types.h"
class JobSheetExport
{
public:
JobSheetExport(db_id jobId, JobCategory cat);
void write();
void save(const QString& fileName);
void save(const QString &fileName);
private:
OdtDocument odt;

Some files were not shown because too many files have changed in this diff Show More