diff --git a/files/diagram/new_station_layout.pdf b/files/diagram/new_station_layout.pdf new file mode 100644 index 00000000..aeda7af1 Binary files /dev/null and b/files/diagram/new_station_layout.pdf differ diff --git a/files/diagram/new_station_layout_no_old_tables.pdf b/files/diagram/new_station_layout_no_old_tables.pdf new file mode 100644 index 00000000..54522801 Binary files /dev/null and b/files/diagram/new_station_layout_no_old_tables.pdf differ diff --git a/src/app/mainwindow.cpp b/src/app/mainwindow.cpp index 53033002..f99e363b 100644 --- a/src/app/mainwindow.cpp +++ b/src/app/mainwindow.cpp @@ -22,9 +22,11 @@ #include "settings/settingsdialog.h" #include "graph/graphmanager.h" -#include "graph/graphicsview.h" +#include "graph/graphicsview.h" //FIXME: remove #include +#include "graph/view/linegraphwidget.h" + #include "db_metadata/meetinginformationdialog.h" #include "lines/linestorage.h" @@ -83,9 +85,9 @@ MainWindow::MainWindow(QWidget *parent) : auto graphMgr = viewMgr->getGraphMgr(); connect(graphMgr, &GraphManager::jobSelected, this, &MainWindow::onJobSelected); - view = graphMgr->getView(); + //view = graphMgr->getView(); + view = new LineGraphWidget(this); view->setObjectName("GraphicsView"); - view->setParent(this); auto linesMatchModel = new LinesMatchModel(Session->m_Db, true, this); linesMatchModel->setHasEmptyRow(false); //Do not allow empty view (no line selected) @@ -847,17 +849,17 @@ void MainWindow::checkLineNumber() { auto graphMgr = Session->getViewManager()->getGraphMgr(); //db_id firstLineId = graphMgr->getFirstLineId(); + + //FIXME: lines are now optional, you can work using only segments + //Segments are now more important than lines, check segments db_id firstLineId = 0; //FIXME - if(firstLineId && m_mode != CentralWidgetMode::ViewSessionMode) + if(/*firstLineId &&*/ m_mode != CentralWidgetMode::ViewSessionMode) { //First line was added or newly opened file -> Session has at least one line ui->actionAddJob->setEnabled(true); ui->actionAddJob->setToolTip(tr("Add train job")); ui->actionRemoveJob->setEnabled(true); setCentralWidgetMode(CentralWidgetMode::ViewSessionMode); - - //Show first line (Alphabetically) - graphMgr->setCurrentLine(firstLineId); } else if(firstLineId == 0 && m_mode != CentralWidgetMode::NoLinesWarningMode) { diff --git a/src/app/mainwindow.h b/src/app/mainwindow.h index 2d3de757..6fbd5b99 100644 --- a/src/app/mainwindow.h +++ b/src/app/mainwindow.h @@ -15,7 +15,8 @@ class database; using namespace sqlite3pp; -class QGraphicsView; +class LineGraphWidget; +class QGraphicsView; //FIXME: remove class QGraphicsScene; class JobPathEditor; class QDockWidget; @@ -115,7 +116,7 @@ private slots: QDockWidget *rsErrDock; #endif - QGraphicsView *view; + LineGraphWidget *view; QDockWidget *jobDock; CustomCompletionLineEdit *searchEdit; diff --git a/src/app/session.cpp b/src/app/session.cpp index 5e31dee0..61799734 100644 --- a/src/app/session.cpp +++ b/src/app/session.cpp @@ -184,6 +184,8 @@ DB_Error MeetingSession::closeDB() if(!viewManager->closeEditors()) return DB_Error::EditorsStillOpened; //User wants to continue editing + viewManager->clearAllLineGraphs(); + releaseAllSavepoints(); finalizeStatements(); diff --git a/src/app/session.h b/src/app/session.h index fbb7db88..02677d3d 100644 --- a/src/app/session.h +++ b/src/app/session.h @@ -74,6 +74,14 @@ class MeetingSession : public QObject //Jobs void jobChanged(db_id jobId, db_id oldJobId); //Updated id/category/stops + //Stations + void stationNameChanged(db_id stationId); + void stationPlanChanged(db_id stationId); + void segmentNameChanged(db_id segmentId); + void segmentStationsChanged(db_id segmentId); + void lineNameChanged(db_id lineId); + void lineSegmentsChanged(db_id lineId); + //TODO: old methods, remove them public: qreal getStationGraphPos(db_id lineId, db_id stId, int platf = 0); diff --git a/src/graph/CMakeLists.txt b/src/graph/CMakeLists.txt index ca1d2792..b4e875a5 100644 --- a/src/graph/CMakeLists.txt +++ b/src/graph/CMakeLists.txt @@ -1,3 +1,6 @@ +add_subdirectory(model) +add_subdirectory(view) + set(TRAINTIMETABLE_SOURCES ${TRAINTIMETABLE_SOURCES} graph/backgroundhelper.h @@ -13,4 +16,4 @@ set(TRAINTIMETABLE_SOURCES graph/stationlayer.h graph/stationlayer.cpp PARENT_SCOPE -) +) \ No newline at end of file diff --git a/src/graph/model/CMakeLists.txt b/src/graph/model/CMakeLists.txt new file mode 100644 index 00000000..ec13fc1c --- /dev/null +++ b/src/graph/model/CMakeLists.txt @@ -0,0 +1,11 @@ +set(TRAINTIMETABLE_SOURCES + ${TRAINTIMETABLE_SOURCES} + graph/model/linegraphmanager.h + graph/model/linegraphscene.h + graph/model/stationgraphobject.h + + graph/model/linegraphmanager.cpp + graph/model/linegraphscene.cpp + graph/model/stationgraphobject.cpp + PARENT_SCOPE +) diff --git a/src/graph/model/linegraphmanager.cpp b/src/graph/model/linegraphmanager.cpp new file mode 100644 index 00000000..f194f5b2 --- /dev/null +++ b/src/graph/model/linegraphmanager.cpp @@ -0,0 +1,107 @@ +#include "linegraphmanager.h" + +#include "linegraphscene.h" + +#include "app/session.h" + +LineGraphManager::LineGraphManager(QObject *parent) : + QObject(parent) +{ + auto session = Session; + connect(session, &MeetingSession::stationNameChanged, this, &LineGraphManager::onStationNameChanged); + connect(session, &MeetingSession::stationPlanChanged, this, &LineGraphManager::onStationPlanChanged); + connect(session, &MeetingSession::segmentNameChanged, this, &LineGraphManager::onSegmentNameChanged); + connect(session, &MeetingSession::segmentStationsChanged, this, &LineGraphManager::onSegmentStationsChanged); + connect(session, &MeetingSession::lineNameChanged, this, &LineGraphManager::onLineNameChanged); + connect(session, &MeetingSession::lineSegmentsChanged, this, &LineGraphManager::onLineSegmentsChanged); + + connect(&AppSettings, &TrainTimetableSettings::jobGraphOptionsChanged, this, &LineGraphManager::updateGraphOptions); +} + +void LineGraphManager::registerScene(LineGraphScene *scene) +{ + Q_ASSERT(!scenes.contains(scene)); + + scenes.append(scene); + + connect(scene, &LineGraphScene::destroyed, this, &LineGraphManager::onSceneDestroyed); +} + +void LineGraphManager::unregisterScene(LineGraphScene *scene) +{ + Q_ASSERT(scenes.contains(scene)); + + scenes.removeOne(scene); + + disconnect(scene, &LineGraphScene::destroyed, this, &LineGraphManager::onSceneDestroyed); +} + +void LineGraphManager::clearAllGraphs() +{ + for(LineGraphScene *scene : qAsConst(scenes)) + { + scene->loadGraph(0, LineGraphType::NoGraph); + } +} + +void LineGraphManager::onSceneDestroyed(QObject *obj) +{ + LineGraphScene *scene = static_cast(obj); + unregisterScene(scene); +} + +void LineGraphManager::onStationNameChanged(db_id stationId) +{ + onStationPlanChanged(stationId); //FIXME: update only labels +} + +void LineGraphManager::onStationPlanChanged(db_id stationId) +{ + //FIXME: speed up with threads??? + for(LineGraphScene *scene : qAsConst(scenes)) + { + if(scene->stations.contains(stationId)) + scene->reload(); + } +} + +void LineGraphManager::onSegmentNameChanged(db_id segmentId) +{ + onSegmentStationsChanged(segmentId); //FIXME: update only labels +} + +void LineGraphManager::onSegmentStationsChanged(db_id segmentId) +{ + //FIXME: speed up with threads??? + for(LineGraphScene *scene : qAsConst(scenes)) + { + if(scene->graphType == LineGraphType::RailwaySegment && scene->graphObjectId == segmentId) + scene->reload(); + else if(scene->graphType == LineGraphType::RailwayLine) + scene->reload(); //FIXME: only if inside this line + //Single stations are not affected + } +} + +void LineGraphManager::onLineNameChanged(db_id lineId) +{ + onLineSegmentsChanged(lineId); //FIXME: update only labels +} + +void LineGraphManager::onLineSegmentsChanged(db_id lineId) +{ + //FIXME: speed up with threads??? + for(LineGraphScene *scene : qAsConst(scenes)) + { + if(scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId) + scene->reload(); + } +} + +void LineGraphManager::updateGraphOptions() +{ + for(LineGraphScene *scene : qAsConst(scenes)) + { + scene->reload(); + } +} diff --git a/src/graph/model/linegraphmanager.h b/src/graph/model/linegraphmanager.h new file mode 100644 index 00000000..26b55041 --- /dev/null +++ b/src/graph/model/linegraphmanager.h @@ -0,0 +1,39 @@ +#ifndef LINEGRAPHMANAGER_H +#define LINEGRAPHMANAGER_H + +#include + +#include + +#include "utils/types.h" + +class LineGraphScene; + +class LineGraphManager : public QObject +{ + Q_OBJECT +public: + explicit LineGraphManager(QObject *parent = nullptr); + + void registerScene(LineGraphScene *scene); + void unregisterScene(LineGraphScene *scene); + + void clearAllGraphs(); + +private slots: + void onSceneDestroyed(QObject *obj); + + void onStationNameChanged(db_id stationId); + void onStationPlanChanged(db_id stationId); + void onSegmentNameChanged(db_id segmentId); + void onSegmentStationsChanged(db_id segmentId); + void onLineNameChanged(db_id lineId); + void onLineSegmentsChanged(db_id lineId); + + void updateGraphOptions(); + +private: + QVector scenes; +}; + +#endif // LINEGRAPHMANAGER_H diff --git a/src/graph/model/linegraphscene.cpp b/src/graph/model/linegraphscene.cpp new file mode 100644 index 00000000..1215968d --- /dev/null +++ b/src/graph/model/linegraphscene.cpp @@ -0,0 +1,289 @@ +#include "linegraphscene.h" + +#include "app/session.h" + +#include + +#include + +LineGraphScene::LineGraphScene(sqlite3pp::database &db, QObject *parent) : + QObject(parent), + mDb(db), + graphObjectId(0), + graphType(LineGraphType::NoGraph) +{ + +} + +void LineGraphScene::recalcContentSize() +{ + contentSize = QSize(); + + if(graphType == LineGraphType::NoGraph) + return; //Nothing to draw + + if(stationPositions.isEmpty()) + return; + + const auto entry = stationPositions.last(); + const int platfCount = stations.value(entry.stationId).platforms.count(); + + //Add an additional half station offset after last station + //This gives extra space to center station label + const int maxWidth = entry.xPos + platfCount * Session->platformOffset + Session->stationOffset / 2; + const int lastY = Session->vertOffset + Session->hourOffset * 24 + 10; + + contentSize = QSize(maxWidth, lastY); +} + +void LineGraphScene::reload() +{ + loadGraph(graphObjectId, graphType, true); +} + +bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force) +{ + if(!force && objectId == graphObjectId && type == graphType) + return true; //Already loaded + + //Initial state is invalid + graphType = LineGraphType::NoGraph; + graphObjectId = 0; + graphObjectName.clear(); + stations.clear(); + stationPositions.clear(); + contentSize = QSize(); + + if(type == LineGraphType::NoGraph) + { + //Nothing to load + emit graphChanged(int(graphType), graphObjectId); + emit redrawGraph(); + return true; + } + + if(!mDb.db()) + { + qWarning() << "Database not open on graph loading!"; + return false; + } + + if(objectId <= 0) + { + qWarning() << "Invalid object ID on graph loading!"; + return false; + } + + //Leave on left horizOffset plus half station offset to separate first station from HourPanel + //and to give more space to station label. + const double curPos = Session->horizOffset + Session->stationOffset / 2; + + if(type == LineGraphType::SingleStation) + { + StationGraphObject st; + st.stationId = objectId; + if(!loadStation(st)) + return false; + + //Register a single station at start position + st.xPos = curPos; + stations.insert(st.stationId, st); + stationPositions = {{st.stationId, st.xPos}}; + graphObjectName = st.stationName; + } + else if(type == LineGraphType::RailwaySegment) + { + //TODO: maybe show also station gates + StationGraphObject stA, stB; + + sqlite3pp::query q(mDb, + "SELECT s.in_gate_id,s.out_gate_id,s.name,s.max_speed_kmh," + "s.type,s.distance_meters," + "g1.station_id,g2.station_id" + " FROM railway_segments s" + " JOIN station_gates g1 ON g1.id=s.in_gate_id" + " JOIN station_gates g2 ON g2.id=s.out_gate_id" + " WHERE s.id=?"); + q.bind(1, objectId); + if(q.step() != SQLITE_ROW) + { + qWarning() << "Graph: invalid segment ID" << objectId; + return false; + } + + auto r = q.getRows(); +// TODO useful? +// outFromGateId = r.get(0); +// outToGateId = r.get(1); + graphObjectName = r.get(2); +// outSpeed = r.get(3); +// outType = utils::RailwaySegmentType(r.get(4)); +// outDistance = r.get(5); + + stA.stationId = r.get(6); + stB.stationId = r.get(7); + + if(!loadStation(stA) || !loadStation(stB)) + return false; + + stA.xPos = curPos; + stB.xPos = stA.xPos + stA.platforms.count() * Session->platformOffset + Session->stationOffset; + + stations.insert(stA.stationId, stA); + stations.insert(stB.stationId, stB); + + stationPositions = {{stA.stationId, stA.xPos}, + {stB.stationId, stB.xPos}}; + } + else if(type == LineGraphType::RailwayLine) + { + if(!loadFullLine(objectId)) + { + stations.clear(); + stationPositions.clear(); + return false; + } + } + + graphObjectId = objectId; + graphType = type; + + recalcContentSize(); + + emit graphChanged(int(graphType), graphObjectId); + emit redrawGraph(); + + return true; +} + +bool LineGraphScene::loadStation(StationGraphObject& st) +{ + sqlite3pp::query q(mDb); + + q.prepare("SELECT name,short_name,type FROM stations WHERE id=?"); + q.bind(1, st.stationId); + if(q.step() != SQLITE_ROW) + { + qWarning() << "Graph: invalid station ID" << st.stationId; + return false; + } + + //Load station + auto row = q.getRows(); + + st.stationName = row.get(1); + if(st.stationName.isEmpty()) + { + //Empty short name, fallback to full name + st.stationName = row.get(0); + } + st.stationType = utils::StationType(row.get(2)); + + //Load platforms + const QRgb white = qRgb(255, 255, 255); + q.prepare("SELECT id, type, color_rgb, name FROM station_tracks WHERE station_id=? ORDER BY pos"); + q.bind(1, st.stationId); + for(auto r : q) + { + StationGraphObject::PlatformGraph platf; + platf.platformId = r.get(0); + platf.platformType = utils::StationTrackType(r.get(1)); + if(r.column_type(2) == SQLITE_NULL) //NULL is white (#FFFFFF) -> default value + platf.color = white; + else + platf.color = QRgb(r.get(2)); + platf.platformName = r.get(3); + st.platforms.append(platf); + } + + return true; +} + +bool LineGraphScene::loadFullLine(db_id lineId) +{ + //TODO: maybe show also station gates + //TODO: load only visible stations, other will be loaded when scrolling graph + sqlite3pp::query q(mDb, "SELECT name FROM lines WHERE id=?"); + q.bind(1, lineId); + if(q.step() != SQLITE_ROW) + { + qWarning() << "Graph: invalid line ID" << lineId; + return false; + } + + //Store line name + graphObjectName = q.getRows().get(0); + + //Get segments + q.prepare("SELECT ls.id, ls.seg_id, ls.direction," + "seg.name, seg.max_speed_kmh, seg.type, seg.distance_meters," + "g1.station_id, g2.station_id" + " FROM line_segments ls" + " JOIN railway_segments seg ON seg.id=ls.seg_id" + " JOIN station_gates g1 ON g1.id=seg.in_gate_id" + " JOIN station_gates g2 ON g2.id=seg.out_gate_id" + " WHERE ls.line_id=?" + " ORDER BY ls.pos"); + q.bind(1, lineId); + + db_id lastStationId = 0; + double curPos = Session->horizOffset + Session->stationOffset / 2; + + for(auto seg : q) + { + db_id lineSegmentId = seg.get(0); + //item.railwaySegmentId = seg.get(1); + bool reversed = seg.get(2) != 0; + + //item.segmentName = seg.get(3); + //item.maxSpeedKmH = seg.get(4); + //item.segmentType = utils::RailwaySegmentType(seg.get(5)); + //item.distanceMeters = seg.get(6); + + //Store first segment end + db_id fromStationId = seg.get(7); + + //Store also the other end of segment for last item + db_id otherStationId = seg.get(8); + + if(reversed) + { + //Swap segments ends + qSwap(fromStationId, otherStationId); + } + + if(!lastStationId) + { + //First line station + StationGraphObject st; + st.stationId = fromStationId; + if(!loadStation(st)) + return false; + + st.xPos = curPos; + stations.insert(st.stationId, st); + stationPositions.append({st.stationId, st.xPos}); + curPos += st.platforms.count() * Session->platformOffset + Session->stationOffset; + } + else if(fromStationId != lastStationId) + { + qWarning() << "Line segments are not adjacent, ID:" << lineSegmentId + << "LINE:" << lineId; + return false; + } + + StationGraphObject stB; + stB.stationId = otherStationId; + if(!loadStation(stB)) + return false; + + stB.xPos = curPos; + stations.insert(stB.stationId, stB); + stationPositions.append({stB.stationId, stB.xPos}); + + curPos += stB.platforms.count() * Session->platformOffset + Session->stationOffset; + lastStationId = stB.stationId; + } + + return true; +} diff --git a/src/graph/model/linegraphscene.h b/src/graph/model/linegraphscene.h new file mode 100644 index 00000000..8bc2b1ee --- /dev/null +++ b/src/graph/model/linegraphscene.h @@ -0,0 +1,90 @@ +#ifndef LINEGRAPHSCENE_H +#define LINEGRAPHSCENE_H + +#include +#include +#include +#include + +#include "utils/types.h" + +#include "stationgraphobject.h" + +namespace sqlite3pp { +class database; +} + +enum class LineGraphType +{ + NoGraph = 0, + SingleStation, + RailwaySegment, + RailwayLine +}; + +class LineGraphScene : public QObject +{ + Q_OBJECT +public: + LineGraphScene(sqlite3pp::database &db, QObject *parent = nullptr); + + bool loadGraph(db_id objectId, LineGraphType type, bool force = false); + + inline LineGraphType getGraphType() const + { + return graphType; + } + + inline db_id getGraphObjectId() const + { + return graphObjectId; + } + + inline QString getGraphObjectName() const + { + return graphObjectName; + } + + inline QSize getContentSize() const + { + return contentSize; + } + +signals: + void graphChanged(int type, db_id objectId); + void redrawGraph(); + +public slots: + void reload(); + +private: + void recalcContentSize(); + bool loadStation(StationGraphObject &st); + bool loadFullLine(db_id lineId); + +private: + friend class LineGraphView; + friend class StationLabelsHeader; + friend class LineGraphManager; + + sqlite3pp::database& mDb; + + /* Can be station/segment/line ID depending on graph type */ + db_id graphObjectId; + LineGraphType graphType; + + QString graphObjectName; + + typedef struct + { + db_id stationId; + double xPos; + } StationPosEntry; + + QVector stationPositions; + QHash stations; + + QSize contentSize; +}; + +#endif // LINEGRAPHSCENE_H diff --git a/src/graph/model/stationgraphobject.cpp b/src/graph/model/stationgraphobject.cpp new file mode 100644 index 00000000..4c1a8825 --- /dev/null +++ b/src/graph/model/stationgraphobject.cpp @@ -0,0 +1,6 @@ +#include "stationgraphobject.h" + +StationGraphObject::StationGraphObject() +{ + +} diff --git a/src/graph/model/stationgraphobject.h b/src/graph/model/stationgraphobject.h new file mode 100644 index 00000000..70d178b4 --- /dev/null +++ b/src/graph/model/stationgraphobject.h @@ -0,0 +1,33 @@ +#ifndef STATIONGRAPHOBJECT_H +#define STATIONGRAPHOBJECT_H + +#include + +#include "utils/types.h" + +#include "stations/station_utils.h" +#include + +class StationGraphObject +{ +public: + StationGraphObject(); + + db_id stationId; + QString stationName; + utils::StationType stationType; + + typedef struct + { + db_id platformId; + QString platformName; + QRgb color; + QFlags platformType; + } PlatformGraph; + + QVector platforms; + + double xPos; +}; + +#endif // STATIONGRAPHOBJECT_H diff --git a/src/graph/view/CMakeLists.txt b/src/graph/view/CMakeLists.txt new file mode 100644 index 00000000..5cd002db --- /dev/null +++ b/src/graph/view/CMakeLists.txt @@ -0,0 +1,17 @@ +set(TRAINTIMETABLE_SOURCES + ${TRAINTIMETABLE_SOURCES} + + graph/view/hourpanel.h + graph/view/linegraphtoolbar.h + graph/view/linegraphview.h + graph/view/linegraphwidget.h + graph/view/stationlabelsheader.h + + graph/view/hourpanel.cpp + graph/view/linegraphtoolbar.cpp + graph/view/linegraphview.cpp + graph/view/linegraphwidget.cpp + graph/view/stationlabelsheader.cpp + + PARENT_SCOPE +) diff --git a/src/graph/view/hourpanel.cpp b/src/graph/view/hourpanel.cpp new file mode 100644 index 00000000..0e44a1a8 --- /dev/null +++ b/src/graph/view/hourpanel.cpp @@ -0,0 +1,52 @@ +#include "hourpanel.h" + +#include "app/session.h" + +#include +#include + +HourPanel::HourPanel(QWidget *parent) : + QWidget(parent), + verticalScroll(0) +{ + connect(&AppSettings, &TrainTimetableSettings::jobGraphOptionsChanged, this, qOverload<>(&HourPanel::update)); +} + +void HourPanel::setScroll(int value) +{ + verticalScroll = value; + update(); +} + +void HourPanel::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QColor c(255, 255, 255, 220); + painter.fillRect(rect(), c); + + //TODO: settings + QFont hourTextFont; + QPen hourTextPen(AppSettings.getHourTextColor()); + + const int vertOffset = Session->vertOffset; + const int hourOffset = Session->hourOffset; + + painter.setFont(hourTextFont); + painter.setPen(hourTextPen); + + //qDebug() << "Drawing hours..." << rect << scroll; + const QString fmt(QStringLiteral("%1:00")); + + const qreal top = verticalScroll; + const qreal bottom = rect().bottom(); + + int h = qFloor(top / hourOffset); + qreal y = h * hourOffset - verticalScroll + vertOffset; + + for(; h <= 24 && y <= bottom; h++) + { + //qDebug() << "Y:" << y << fmt.arg(h); + painter.drawText(QPointF(5, y + 8), fmt.arg(h)); //y + 8 to center text vertically + y += hourOffset; + } +} diff --git a/src/graph/view/hourpanel.h b/src/graph/view/hourpanel.h new file mode 100644 index 00000000..1534abd0 --- /dev/null +++ b/src/graph/view/hourpanel.h @@ -0,0 +1,22 @@ +#ifndef HOURPANEL_H +#define HOURPANEL_H + +#include + +class HourPanel : public QWidget +{ + Q_OBJECT +public: + explicit HourPanel(QWidget *parent = nullptr); + +public slots: + void setScroll(int value); + +protected: + void paintEvent(QPaintEvent *); + +private: + int verticalScroll; +}; + +#endif // HOURPANEL_H diff --git a/src/graph/view/linegraphtoolbar.cpp b/src/graph/view/linegraphtoolbar.cpp new file mode 100644 index 00000000..b3bb5747 --- /dev/null +++ b/src/graph/view/linegraphtoolbar.cpp @@ -0,0 +1,187 @@ +#include "linegraphtoolbar.h" + +#include "graph/model/linegraphscene.h" + +#include +#include + +#include "utils/sqldelegate/customcompletionlineedit.h" + +#include "stations/match_models/stationsmatchmodel.h" +#include "stations/match_models/railwaysegmentmatchmodel.h" +#include "stations/match_models/linesmatchmodel.h" + +#include "app/session.h" + +#include + +LineGraphToolbar::LineGraphToolbar(QWidget *parent) : + QWidget(parent), + m_scene(nullptr), + matchModel(nullptr), + oldGraphType(0) +{ + QHBoxLayout *lay = new QHBoxLayout(this); + lay->setContentsMargins(0, 0, 0, 0); + + graphTypeCombo = new QComboBox; + lay->addWidget(graphTypeCombo); + + objectCombo = new CustomCompletionLineEdit(nullptr, this); + lay->addWidget(objectCombo); + + redrawBut = new QPushButton(tr("Redraw")); + connect(redrawBut, &QPushButton::clicked, this, &LineGraphToolbar::requestRedraw); + lay->addWidget(redrawBut); + + QStringList items; + items.reserve(4); + items << tr("No Graph") << tr("Station") << tr("Segment") << tr("Line"); + graphTypeCombo->addItems(items); + graphTypeCombo->setCurrentIndex(0); + + connect(graphTypeCombo, qOverload(&QComboBox::activated), this, &LineGraphToolbar::onTypeComboActivated); + connect(objectCombo, &CustomCompletionLineEdit::completionDone, this, &LineGraphToolbar::onCompletionDone); + + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); +} + +LineGraphToolbar::~LineGraphToolbar() +{ + if(matchModel) + { + objectCombo->setModel(nullptr); + delete matchModel; + matchModel = nullptr; + } +} + +void LineGraphToolbar::setScene(LineGraphScene *scene) +{ + if(m_scene) + { + disconnect(m_scene, &LineGraphScene::graphChanged, this, &LineGraphToolbar::onGraphChanged); + disconnect(m_scene, &QObject::destroyed, this, &LineGraphToolbar::onSceneDestroyed); + } + m_scene = scene; + if(m_scene) + { + connect(m_scene, &LineGraphScene::graphChanged, this, &LineGraphToolbar::onGraphChanged); + connect(m_scene, &QObject::destroyed, this, &LineGraphToolbar::onSceneDestroyed); + } +} + +void LineGraphToolbar::resetToolbarToScene() +{ + LineGraphType type = LineGraphType::NoGraph; + db_id objectId = 0; + QString name; + + if(m_scene) + { + type = m_scene->getGraphType(); + objectId = m_scene->getGraphObjectId(); + name = m_scene->getGraphObjectName(); + } + + graphTypeCombo->setCurrentIndex(int(type)); + objectCombo->setData(objectId, name); + oldGraphType = int(type); +} + +void LineGraphToolbar::onGraphChanged(int type, db_id objectId) +{ + setupModel(type); + graphTypeCombo->setCurrentIndex(type); + + QString name; + if(m_scene && m_scene->getGraphObjectId() == objectId) + name = m_scene->getGraphObjectName(); + objectCombo->setData(objectId, name); +} + +void LineGraphToolbar::onTypeComboActivated(int index) +{ + setupModel(index); +} + +void LineGraphToolbar::onCompletionDone() +{ + db_id objectId; + QString name; + if(!objectCombo->getData(objectId, name)) + return; + + if(m_scene) + m_scene->loadGraph(objectId, LineGraphType(graphTypeCombo->currentIndex())); +} + +void LineGraphToolbar::onSceneDestroyed() +{ + m_scene = nullptr; + resetToolbarToScene(); //Clear UI +} + +void LineGraphToolbar::setupModel(int type) +{ + if(type != oldGraphType) + { + //Clear old model + if(matchModel) + { + objectCombo->setModel(nullptr); + delete matchModel; + matchModel = nullptr; + } + + db_id objectId = 0; //Manually clear line edit + QString name; + if(m_scene && type == int(m_scene->getGraphType())) + { + //Suggest current graph object + objectId = m_scene->getGraphObjectId(); + name = m_scene->getGraphObjectName(); + } + + objectCombo->setData(objectId, name); + + switch (LineGraphType(type)) + { + case LineGraphType::NoGraph: + default: + { + //Prevent recursion on loadGraph() calling back to us + oldGraphType = type; + + //Clear graph + if(m_scene) + m_scene->loadGraph(0, LineGraphType::NoGraph); + break; + } + case LineGraphType::SingleStation: + { + StationsMatchModel *m = new StationsMatchModel(Session->m_Db, this); + m->setFilter(0); + matchModel = m; + break; + } + case LineGraphType::RailwaySegment: + { + RailwaySegmentMatchModel *m = new RailwaySegmentMatchModel(Session->m_Db, this); + m->setFilter(0, 0, 0); + matchModel = m; + break; + } + case LineGraphType::RailwayLine: + { + LinesMatchModel *m = new LinesMatchModel(Session->m_Db, true, this); + matchModel = m; + break; + } + } + + if(matchModel) + objectCombo->setModel(matchModel); + } + oldGraphType = type; +} diff --git a/src/graph/view/linegraphtoolbar.h b/src/graph/view/linegraphtoolbar.h new file mode 100644 index 00000000..d640c3d9 --- /dev/null +++ b/src/graph/view/linegraphtoolbar.h @@ -0,0 +1,51 @@ +#ifndef LINEGRAPHTOOLBAR_H +#define LINEGRAPHTOOLBAR_H + +#include + +#include "utils/types.h" + +class QComboBox; +class QPushButton; +class CustomCompletionLineEdit; + +class LineGraphScene; +class ISqlFKMatchModel; + +class LineGraphToolbar : public QWidget +{ + Q_OBJECT +public: + explicit LineGraphToolbar(QWidget *parent = nullptr); + ~LineGraphToolbar(); + + void setScene(LineGraphScene *scene); + +signals: + void requestRedraw(); + +public slots: + void resetToolbarToScene(); + +private slots: + void onGraphChanged(int type, db_id objectId); + void onTypeComboActivated(int index); + void onCompletionDone(); + void onSceneDestroyed(); + +private: + void setupModel(int type); + +private: + LineGraphScene *m_scene; + + QComboBox *graphTypeCombo; + CustomCompletionLineEdit *objectCombo; + QPushButton *redrawBut; + + ISqlFKMatchModel *matchModel; + + int oldGraphType; +}; + +#endif // LINEGRAPHTOOLBAR_H diff --git a/src/graph/view/linegraphview.cpp b/src/graph/view/linegraphview.cpp new file mode 100644 index 00000000..798c8c3f --- /dev/null +++ b/src/graph/view/linegraphview.cpp @@ -0,0 +1,256 @@ +#include "linegraphview.h" + +#include "graph/model/linegraphscene.h" + +#include "stationlabelsheader.h" +#include "hourpanel.h" + +#include "app/session.h" + +#include +#include + +#include + +#include + +LineGraphView::LineGraphView(QWidget *parent) : + QAbstractScrollArea(parent), + m_scene(nullptr) +{ + QPalette pal = palette(); + pal.setColor(backgroundRole(), Qt::white); + setPalette(pal); + + horizontalScrollBar()->setSingleStep(20); + verticalScrollBar()->setSingleStep(20); + + hourPanel = new HourPanel(this); + stationHeader = new StationLabelsHeader(this); + + connect(verticalScrollBar(), &QScrollBar::valueChanged, hourPanel, &HourPanel::setScroll); + connect(horizontalScrollBar(), &QScrollBar::valueChanged, stationHeader, &StationLabelsHeader::setScroll); + connect(&AppSettings, &TrainTimetableSettings::jobGraphOptionsChanged, this, &LineGraphView::resizeHeaders); + + resizeHeaders(); +} + +LineGraphScene *LineGraphView::scene() const +{ + return m_scene; +} + +void LineGraphView::setScene(LineGraphScene *newScene) +{ + stationHeader->setScene(newScene); + + if(m_scene) + { + disconnect(m_scene, &LineGraphScene::redrawGraph, this, &LineGraphView::redrawGraph); + disconnect(m_scene, &QObject::destroyed, this, &LineGraphView::onSceneDestroyed); + } + m_scene = newScene; + if(m_scene) + { + connect(m_scene, &LineGraphScene::redrawGraph, this, &LineGraphView::redrawGraph); + connect(m_scene, &QObject::destroyed, this, &LineGraphView::onSceneDestroyed); + } + redrawGraph(); +} + +void LineGraphView::redrawGraph() +{ + updateScrollBars(); + + viewport()->update(); +} + +bool LineGraphView::event(QEvent *e) +{ + if (e->type() == QEvent::StyleChange || e->type() == QEvent::LayoutRequest) + { + updateScrollBars(); + resizeHeaders(); + } + + return QAbstractScrollArea::event(e); +} + +void LineGraphView::paintEvent(QPaintEvent *e) +{ + //TODO: repaint only new regions, not all + + //FIXME: paint hour lines + + QPainter painter(viewport()); + + //Scroll contents + const QPoint origin(-horizontalScrollBar()->value(), -verticalScrollBar()->value()); + + painter.translate(origin); + + QRect visibleRect(-origin, viewport()->size()); + drawBackgroundHourLines(&painter, visibleRect); + + if(!m_scene || m_scene->getGraphType() == LineGraphType::NoGraph) + return; //Nothing to draw + + paintStations(&painter); +} + +void LineGraphView::resizeEvent(QResizeEvent *) +{ + updateScrollBars(); + resizeHeaders(); +} + +void LineGraphView::mousePressEvent(QMouseEvent *e) +{ + emit syncToolbarToScene(); + QAbstractScrollArea::mousePressEvent(e); +} + +void LineGraphView::onSceneDestroyed() +{ + m_scene = nullptr; + redrawGraph(); +} + +void LineGraphView::resizeHeaders() +{ + const int horizOffset = Session->horizOffset; + const int vertOffset = Session->vertOffset; + + const QRect vg = viewport()->geometry(); + + hourPanel->move(vg.topLeft()); + hourPanel->resize(horizOffset - 5, vg.height()); + hourPanel->setScroll(verticalScrollBar()->value()); + + stationHeader->move(vg.topLeft()); + stationHeader->resize(vg.width(), vertOffset - 5); + stationHeader->setScroll(horizontalScrollBar()->value()); +} + +void LineGraphView::updateScrollBars() +{ + if(!m_scene) + return; + + const QSize contentSize = m_scene->getContentSize(); + if(contentSize.isEmpty()) + return; + + QSize p = viewport()->size(); + QSize m = maximumViewportSize(); + + if(m.expandedTo(contentSize) == m) + p = m; //No scrollbars needed + + auto hbar = horizontalScrollBar(); + hbar->setRange(0, contentSize.width() - p.width()); + hbar->setPageStep(p.width()); + + auto vbar = verticalScrollBar(); + vbar->setRange(0, contentSize.height() - p.height()); + vbar->setPageStep(p.height()); +} + +void LineGraphView::ensureVisible(int x, int y, int xmargin, int ymargin) +{ + auto hbar = horizontalScrollBar(); + auto vbar = verticalScrollBar(); + auto vp = viewport(); + + int logicalX = x; + if (logicalX - xmargin < hbar->value()) { + hbar->setValue(qMax(0, logicalX - xmargin)); + } else if (logicalX > hbar->value() + vp->width() - xmargin) { + hbar->setValue(qMin(logicalX - vp->width() + xmargin, hbar->maximum())); + } + if (y - ymargin < vbar->value()) { + vbar->setValue(qMax(0, y - ymargin)); + } else if (y > vbar->value() + vp->height() - ymargin) { + vbar->setValue(qMin(y - vp->height() + ymargin, vbar->maximum())); + } +} + +void LineGraphView::drawBackgroundHourLines(QPainter *painter, const QRectF &rect) +{ + const double vertOffset = Session->vertOffset; + const double hourOffset = Session->hourOffset; + const double hourHorizOffset = AppSettings.getHourLineOffset(); + + QPen hourLinePen(AppSettings.getHourLineColor(), AppSettings.getHourLineWidth()); + + const qreal x1 = qMax(qreal(hourHorizOffset), rect.left()); + const qreal x2 = rect.right(); + const qreal t = qMax(rect.top(), vertOffset); + const qreal b = rect.bottom(); + + + if(x1 > x2 || b < vertOffset || t > b) + return; + + qreal f = std::remainder(t - vertOffset, hourOffset); + + if(f < 0) + f += hourOffset; + qreal f1 = qFuzzyIsNull(f) ? vertOffset : qMax(t - f + hourOffset, vertOffset); + + + const qreal l = std::remainder(b - vertOffset, hourOffset); + const qreal l1 = b - l; + + std::size_t n = std::size_t((l1 - f1)/hourOffset) + 1; + + QLineF *arr = new QLineF[n]; + for(std::size_t i = 0; i < n; i++) + { + arr[i] = QLineF(x1, f1, x2, f1); + f1 += hourOffset; + } + + painter->setPen(hourLinePen); + painter->drawLines(arr, int(n)); + delete [] arr; +} + +void LineGraphView::paintStations(QPainter *painter) +{ + const QRgb white = qRgb(255, 255, 255); + + //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 width = AppSettings.getPlatformLineWidth(); + const QColor mainPlatfColor = AppSettings.getMainPlatfColor(); + + QPen platfPen (mainPlatfColor, width); + + QPointF top(0, vertOffset); + QPointF bottom(0, lastY); + + for(const StationGraphObject &st : qAsConst(m_scene->stations)) + { + top.rx() = bottom.rx() = st.xPos; + + for(const StationGraphObject::PlatformGraph& platf : st.platforms) + { + if(platf.color == white) + platfPen.setColor(mainPlatfColor); + else + platfPen.setColor(platf.color); + + painter->setPen(platfPen); + + painter->drawLine(top, bottom); + + top.rx() += platfOffset; + bottom.rx() += platfOffset; + } + } +} diff --git a/src/graph/view/linegraphview.h b/src/graph/view/linegraphview.h new file mode 100644 index 00000000..4796e294 --- /dev/null +++ b/src/graph/view/linegraphview.h @@ -0,0 +1,51 @@ +#ifndef LINEGRAPHVIEW_H +#define LINEGRAPHVIEW_H + +#include + +class LineGraphScene; + +class StationLabelsHeader; +class HourPanel; + +class LineGraphView : public QAbstractScrollArea +{ + Q_OBJECT +public: + explicit LineGraphView(QWidget *parent = nullptr); + + LineGraphScene *scene() const; + void setScene(LineGraphScene *newScene); + + void ensureVisible(int x, int y, int xmargin, int ymargin); + +signals: + void syncToolbarToScene(); + +public slots: + void redrawGraph(); + +protected: + bool event(QEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void resizeEvent(QResizeEvent *) override; + void mousePressEvent(QMouseEvent *e) override; + +private slots: + void onSceneDestroyed(); + void resizeHeaders(); + +private: + void updateScrollBars(); + + void drawBackgroundHourLines(QPainter *painter, const QRectF &rect); + void paintStations(QPainter *painter); + +private: + StationLabelsHeader *stationHeader; + HourPanel *hourPanel; + + LineGraphScene *m_scene; +}; + +#endif // LINEGRAPHVIEW_H diff --git a/src/graph/view/linegraphwidget.cpp b/src/graph/view/linegraphwidget.cpp new file mode 100644 index 00000000..f2cb3ea9 --- /dev/null +++ b/src/graph/view/linegraphwidget.cpp @@ -0,0 +1,36 @@ +#include "linegraphwidget.h" + +#include "graph/model/linegraphscene.h" + +#include "linegraphview.h" +#include "linegraphtoolbar.h" + +#include "app/session.h" +#include "viewmanager/viewmanager.h" +#include "graph/model/linegraphmanager.h" + +#include + +LineGraphWidget::LineGraphWidget(QWidget *parent) : + QWidget(parent), + m_scene(nullptr) +{ + QVBoxLayout *lay = new QVBoxLayout(this); + + toolBar = new LineGraphToolbar(this); + + lay->addWidget(toolBar); + + view = new LineGraphView(this); + lay->addWidget(view); + + m_scene = new LineGraphScene(Session->m_Db, this); + + //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(toolBar, &LineGraphToolbar::requestRedraw, m_scene, &LineGraphScene::reload); +} diff --git a/src/graph/view/linegraphwidget.h b/src/graph/view/linegraphwidget.h new file mode 100644 index 00000000..99f68c23 --- /dev/null +++ b/src/graph/view/linegraphwidget.h @@ -0,0 +1,25 @@ +#ifndef LINEGRAPHWIDGET_H +#define LINEGRAPHWIDGET_H + +#include + +#include "utils/types.h" + +class LineGraphScene; +class LineGraphView; +class LineGraphToolbar; + +class LineGraphWidget : public QWidget +{ + Q_OBJECT +public: + explicit LineGraphWidget(QWidget *parent = nullptr); + +private: + LineGraphScene *m_scene; + + LineGraphView *view; + LineGraphToolbar *toolBar; +}; + +#endif // LINEGRAPHWIDGET_H diff --git a/src/graph/view/stationlabelsheader.cpp b/src/graph/view/stationlabelsheader.cpp new file mode 100644 index 00000000..d31a82c6 --- /dev/null +++ b/src/graph/view/stationlabelsheader.cpp @@ -0,0 +1,130 @@ +#include "stationlabelsheader.h" + +#include "graph/model/linegraphscene.h" + +#include "app/session.h" + +#include + +StationLabelsHeader::StationLabelsHeader(QWidget *parent) : + QWidget(parent), + m_scene(nullptr), + horizontalScroll(0) +{ + +} + +void StationLabelsHeader::setScroll(int value) +{ + horizontalScroll = value; + update(); +} + +void StationLabelsHeader::onSceneDestroyed() +{ + m_scene = nullptr; + update(); +} + +void StationLabelsHeader::paintEvent(QPaintEvent *) +{ + //TODO: repaint only new regions, not all + + if(!m_scene || m_scene->getGraphType() == LineGraphType::NoGraph) + return; //Nothing to draw + + QPainter painter(this); + QColor c(255, 255, 255, 220); + painter.fillRect(rect(), c); + + QFont stationFont; + stationFont.setBold(true); + stationFont.setPointSize(12); + + QPen stationPen(AppSettings.getStationTextColor()); + + QFont platfBoldFont = stationFont; + platfBoldFont.setPointSize(10); + + QFont platfNormalFont = platfBoldFont; + platfNormalFont.setBold(false); + + QPen electricPlatfPen(Qt::blue); + QPen nonElectricPlatfPen(Qt::black); + + const qreal platformOffset = Session->platformOffset; + 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; + + const double margin = stationOffset * 0.1; + + QRectF r = rect(); + + for(auto st : qAsConst(m_scene->stations)) + { + const double left = st.xPos + leftOffset - horizontalScroll; + const double right = left + st.platforms.count() * platformOffset + stationOffset; + + if(right <= 0 || left >= r.width()) + continue; //Skip station, it's not visible + + QRectF labelRect = r; + labelRect.setLeft(left + margin); + labelRect.setRight(right - margin); + labelRect.setBottom(r.bottom() - r.height() / 3); + + painter.setPen(stationPen); + painter.setFont(stationFont); + painter.drawText(labelRect, Qt::AlignVCenter | Qt::AlignCenter, st.stationName); + + labelRect = r; + 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; + labelRect.setWidth(platformOffset); + for(const StationGraphObject::PlatformGraph& platf : qAsConst(st.platforms)) + { + if(platf.platformType.testFlag(utils::StationTrackType::Electrified)) + painter.setPen(electricPlatfPen); + else + painter.setPen(nonElectricPlatfPen); + + if(platf.platformType.testFlag(utils::StationTrackType::Through)) + painter.setFont(platfBoldFont); + else + painter.setFont(platfNormalFont); + + labelRect.moveLeft(xPos); + + painter.drawText(labelRect, Qt::AlignCenter, platf.platformName); + + xPos += platformOffset; + } + } +} + +LineGraphScene *StationLabelsHeader::scene() const +{ + return m_scene; +} + +void StationLabelsHeader::setScene(LineGraphScene *newScene) +{ + if(m_scene) + { + disconnect(m_scene, &LineGraphScene::redrawGraph, this, qOverload<>(&StationLabelsHeader::update)); + disconnect(m_scene, &QObject::destroyed, this, &StationLabelsHeader::onSceneDestroyed); + } + m_scene = newScene; + if(m_scene) + { + connect(m_scene, &LineGraphScene::redrawGraph, this, qOverload<>(&StationLabelsHeader::update)); + connect(m_scene, &QObject::destroyed, this, &StationLabelsHeader::onSceneDestroyed); + } + update(); +} diff --git a/src/graph/view/stationlabelsheader.h b/src/graph/view/stationlabelsheader.h new file mode 100644 index 00000000..48bf25cb --- /dev/null +++ b/src/graph/view/stationlabelsheader.h @@ -0,0 +1,31 @@ +#ifndef STATIONLABELSHEADER_H +#define STATIONLABELSHEADER_H + +#include + +class LineGraphScene; + +class StationLabelsHeader : public QWidget +{ + Q_OBJECT +public: + explicit StationLabelsHeader(QWidget *parent = nullptr); + + LineGraphScene *scene() const; + void setScene(LineGraphScene *newScene); + +public slots: + void setScroll(int value); + +private slots: + void onSceneDestroyed(); + +protected: + void paintEvent(QPaintEvent *) override; + +private: + LineGraphScene *m_scene; + int horizontalScroll; +}; + +#endif // STATIONLABELSHEADER_H diff --git a/src/settings/appsettings.h b/src/settings/appsettings.h index f54b5d1f..837e990e 100644 --- a/src/settings/appsettings.h +++ b/src/settings/appsettings.h @@ -64,7 +64,7 @@ class TrainTimetableSettings : public QObject FIELD(VerticalOffset, "job_graph/vertical_offset", int, 50) FIELD(HourOffset, "job_graph/hour_offset", int, 100) FIELD(StationOffset, "job_graph/station_offset", int, 150) - FIELD(PlatformOffset, "job_graph/platform_offset", int, 12) + FIELD(PlatformOffset, "job_graph/platform_offset", int, 20) FIELD(PlatformLineWidth, "job_graph/platf_line_width", int, 2) FIELD(HourLineWidth, "job_graph/hour_line_width", int, 2) diff --git a/src/stations/manager/stations/model/stationsmodel.cpp b/src/stations/manager/stations/model/stationsmodel.cpp index b051550a..76060382 100644 --- a/src/stations/manager/stations/model/stationsmodel.cpp +++ b/src/stations/manager/stations/model/stationsmodel.cpp @@ -10,6 +10,8 @@ using namespace sqlite3pp; #include "stations/station_name_utils.h" +#include "app/session.h" + #include class StationsSQLModelResultEvent : public QEvent @@ -589,6 +591,8 @@ bool StationsModel::setName(StationsModel::StationItem &item, const QString &val item.name = name; + emit Session->stationNameChanged(item.stationId); + //This row has now changed position so we need to invalidate cache //HACK: we emit dataChanged for this index (that doesn't exist anymore) //but the view will trigger fetching at same scroll position so it is enough @@ -644,6 +648,7 @@ bool StationsModel::setShortName(StationsModel::StationItem &item, const QString } item.shortName = shortName; + emit Session->stationNameChanged(item.stationId); return true; } diff --git a/src/stations/manager/stationsmanager.cpp b/src/stations/manager/stationsmanager.cpp index 9567efac..aa769de5 100644 --- a/src/stations/manager/stationsmanager.cpp +++ b/src/stations/manager/stationsmanager.cpp @@ -362,6 +362,10 @@ void StationsManager::onEditStation() //Refresh stations model stationsModel->refreshData(true); + //FIXME: check if actually changed + emit Session->stationNameChanged(stId); + emit Session->stationPlanChanged(stId); + //Refresh segments int &segmentsTimer = clearModelTimers[RailwaySegmentsTab]; if(segmentsTimer != ModelCleared) @@ -454,6 +458,10 @@ void StationsManager::onEditSegment() if(ret != QDialog::Accepted || !dlg) return; + //FIXME: check if actually changed + emit Session->segmentNameChanged(segmentId); + emit Session->segmentStationsChanged(segmentId); + //Refresh fields segmentsModel->refreshData(true); } @@ -527,6 +535,10 @@ void StationsManager::onEditLine() if(ret != QDialog::Accepted || !dlg) return; + //FIXME: check if actually changed + emit Session->lineNameChanged(lineId); + emit Session->lineSegmentsChanged(lineId); + //Refresh fields linesModel->refreshData(true); } diff --git a/src/viewmanager/viewmanager.cpp b/src/viewmanager/viewmanager.cpp index 6427252f..a5c6ce01 100644 --- a/src/viewmanager/viewmanager.cpp +++ b/src/viewmanager/viewmanager.cpp @@ -22,6 +22,7 @@ #include "jobs/jobsmanager.h" #include "graph/graphmanager.h" +#include "graph/model/linegraphmanager.h" #include "sessionstartendrsviewer.h" @@ -38,6 +39,7 @@ ViewManager::ViewManager(QObject *parent) : sessionRSViewer(nullptr) { mGraphMgr = new GraphManager(this); + lineGraphManager = new LineGraphManager(this); //RollingStock connect(Session, &MeetingSession::rollingstockRemoved, this, &ViewManager::onRSRemoved); @@ -500,6 +502,11 @@ bool ViewManager::closeEditors() return true; } +void ViewManager::clearAllLineGraphs() +{ + lineGraphManager->clearAllGraphs(); +} + bool ViewManager::requestJobSelection(db_id jobId, bool select, bool ensureVisible) const { db_id curLineId = mGraphMgr->getCurLineId(); diff --git a/src/viewmanager/viewmanager.h b/src/viewmanager/viewmanager.h index 33209719..2c1ae192 100644 --- a/src/viewmanager/viewmanager.h +++ b/src/viewmanager/viewmanager.h @@ -16,7 +16,8 @@ class ShiftManager; class ShiftViewer; class JobPathEditor; class ShiftGraphEditor; -class GraphManager; +class GraphManager; //FIXME: remove +class LineGraphManager; class JobsManager; class SessionStartEndRSViewer; @@ -32,9 +33,12 @@ class ViewManager : public QObject void finalizeQueries(); bool closeEditors(); + void clearAllLineGraphs(); GraphManager *getGraphMgr() const; + inline LineGraphManager *getLineGraphMgr() const { return lineGraphManager; } + bool requestJobSelection(db_id jobId, bool select, bool ensureVisible) const; bool requestJobEditor(db_id jobId, db_id stopId = 0); bool requestJobCreation(); @@ -83,6 +87,7 @@ private slots: private: GraphManager *mGraphMgr; + LineGraphManager *lineGraphManager; QWidget *m_mainWidget;