Compare commits
173 Commits
Author | SHA1 | Date |
---|---|---|
gfgit | 130b4312c4 | |
Filippo Gentile | 1aa23f281a | |
Filippo Gentile | 9f14ee1c64 | |
Filippo Gentile | 42c486f5cf | |
Luca Pellegrini | 21a54888f2 | |
Luca Pellegrini | 05c45381d4 | |
Filippo Gentile | 87ec3e53ce | |
Filippo Gentile | 7ac337a181 | |
Filippo Gentile | 4e6f49dd44 | |
Filippo Gentile | efd60f9ce6 | |
Filippo Gentile | 48faf57a93 | |
Filippo Gentile | 90ca41cb55 | |
Filippo Gentile | 9abb3b014c | |
Filippo Gentile | 6323a87ff0 | |
Filippo Gentile | 9bf0027fe3 | |
Filippo Gentile | 6923642a53 | |
Filippo Gentile | 5d643196e0 | |
Filippo Gentile | 62ee86b8b5 | |
Filippo Gentile | 585476240f | |
Filippo Gentile | a4393aec43 | |
Filippo Gentile | 02f4938396 | |
Filippo Gentile | 49c7f60c94 | |
Filippo Gentile | 7fb7271745 | |
Filippo Gentile | 1cbc5e38a2 | |
Filippo Gentile | f60f9ed11d | |
Filippo Gentile | 1b65e413de | |
Filippo Gentile | 3847853913 | |
Filippo Gentile | 8c61554a5d | |
Filippo Gentile | 4e622d0841 | |
Filippo Gentile | 0149217499 | |
Filippo Gentile | e5fc856c10 | |
Filippo Gentile | ecf934bf86 | |
Filippo Gentile | 5f165502f8 | |
Filippo Gentile | 232f4e1dcc | |
Filippo Gentile | 576a79e065 | |
Filippo Gentile | c61fac42b7 | |
Filippo Gentile | 1618cc47ad | |
Filippo Gentile | 6dacc44880 | |
Filippo Gentile | 3b969aafce | |
Filippo Gentile | 77dcb5d0de | |
Filippo Gentile | 9383f4a922 | |
Filippo Gentile | a352eafdbb | |
Filippo Gentile | b2a8e04faf | |
Filippo Gentile | 66442b13cb | |
Filippo Gentile | 706d9dadb4 | |
Filippo Gentile | de54030785 | |
Filippo Gentile | 0c04121867 | |
Filippo Gentile | 017005e1a9 | |
Filippo Gentile | 8667ea96fe | |
Filippo Gentile | 3672395be5 | |
Filippo Gentile | a53c8948fe | |
Filippo Gentile | 0273501500 | |
gfgit | b951cedbc9 | |
Filippo Gentile | f352555266 | |
Filippo Gentile | f0ca606bf2 | |
Filippo Gentile | af337e3065 | |
Filippo Gentile | 214f08dc3e | |
Filippo Gentile | fca21eeae6 | |
Filippo Gentile | cb20814a69 | |
Filippo Gentile | 43658c227a | |
Filippo Gentile | 3df21ef200 | |
Filippo Gentile | 5e94d9b603 | |
Filippo Gentile | a3db250463 | |
Filippo Gentile | 98cd6f2702 | |
Filippo Gentile | 58523ed724 | |
Filippo Gentile | 04a148b9ba | |
gfgit | cfc705a009 | |
Filippo Gentile | b2bbf975f8 | |
Filippo Gentile | 79e609b720 | |
Filippo Gentile | 78bf2166f9 | |
Filippo Gentile | 8583684780 | |
Filippo Gentile | e83d8f601e | |
Filippo Gentile | 8e2f7c545b | |
Filippo Gentile | 4360e4b5b1 | |
Filippo Gentile | 140d076651 | |
Filippo Gentile | 7728133a95 | |
Filippo Gentile | 1438c4b118 | |
Filippo Gentile | 774d4497c5 | |
Filippo Gentile | eb7b773fc4 | |
Filippo Gentile | 42a468a1b5 | |
Filippo Gentile | 1bc031cae6 | |
Filippo Gentile | 0a5d02b862 | |
Filippo Gentile | d161b87859 | |
Filippo Gentile | fede5a826d | |
Filippo Gentile | f6148cf0ba | |
Filippo Gentile | f626d9ff8d | |
Filippo Gentile | 90f6669614 | |
Filippo Gentile | 76c774554e | |
Filippo Gentile | 0b34512f2f | |
Filippo Gentile | 9727d68493 | |
Filippo Gentile | 6bb011d96b | |
Filippo Gentile | b7e1f7284a | |
Filippo Gentile | dfc856aabe | |
Filippo Gentile | 042644c00a | |
Filippo Gentile | 0497d6442f | |
Filippo Gentile | 0c1d9db3bf | |
Filippo Gentile | cbebaf5204 | |
Filippo Gentile | 6a3281b4f1 | |
Filippo Gentile | 5608f0bc28 | |
Filippo Gentile | 36bfe821f1 | |
Filippo Gentile | b1f202c1cd | |
Filippo Gentile | 38e1248f60 | |
Filippo Gentile | b8483827ae | |
Filippo Gentile | 2a3496f0bc | |
Filippo Gentile | 63335f7613 | |
Filippo Gentile | 7a3a34a1ac | |
Filippo Gentile | 816a0830d9 | |
Filippo Gentile | 1c7d288632 | |
Filippo Gentile | 3ef0a142ef | |
Filippo Gentile | 783683b232 | |
Filippo Gentile | 573dfac662 | |
Filippo Gentile | 82c154fca2 | |
Filippo Gentile | d185c0016e | |
Filippo Gentile | 48d7180cd5 | |
Filippo Gentile | d3d6e099b6 | |
Filippo Gentile | 4c25de9133 | |
Filippo Gentile | f1fc42dd9b | |
Filippo Gentile | 1816b6292c | |
Filippo Gentile | c04d77d004 | |
Filippo Gentile | e536d1897f | |
Filippo Gentile | ba97509c2b | |
Filippo Gentile | 1aa479afcd | |
Filippo Gentile | 0f0aa2c94e | |
Filippo Gentile | 80630fbb2a | |
gfgit | b48372faa7 | |
Filippo Gentile | e0e22c8830 | |
Filippo Gentile | 11637625f8 | |
Filippo Gentile | a005f0f49e | |
Filippo Gentile | 1c4d27351d | |
Filippo Gentile | 7fe4e130ea | |
Filippo Gentile | 50a2f94233 | |
Filippo Gentile | b533c5dfe6 | |
Filippo Gentile | 3ad2ab563f | |
Filippo Gentile | 681220732c | |
Filippo Gentile | 5ccf083911 | |
Filippo Gentile | 95c1633103 | |
Filippo Gentile | 4dd467212c | |
Filippo Gentile | 97cd886109 | |
Filippo Gentile | d25897424e | |
Filippo Gentile | 3427248703 | |
Filippo Gentile | 7e12edba26 | |
Filippo Gentile | e749e82eb4 | |
Filippo Gentile | a622a49bdc | |
Filippo Gentile | 5313a0f287 | |
Filippo Gentile | d28c2b868b | |
Filippo Gentile | 83e60d26a1 | |
Filippo Gentile | 8d5c280663 | |
Filippo Gentile | 4e5463173e | |
Filippo Gentile | 6bde7076ca | |
Filippo Gentile | ec7897806f | |
Filippo Gentile | 09aaadaff0 | |
Filippo Gentile | 1a0ee27a76 | |
Filippo Gentile | 9879623f03 | |
Filippo Gentile | 368e0d3f7f | |
Filippo Gentile | 1760d23ccc | |
Filippo Gentile | a202702f31 | |
Filippo Gentile | ba2c55ab70 | |
Filippo Gentile | a6e0e46c81 | |
Filippo Gentile | b5bcbafec8 | |
Filippo Gentile | 4b91b76e50 | |
Filippo Gentile | 1fb67335a7 | |
Filippo Gentile | fd428e0361 | |
Filippo Gentile | 39b773a118 | |
Filippo Gentile | 191766ae62 | |
Filippo Gentile | 2c123543f9 | |
Filippo Gentile | c8a580d14b | |
Filippo Gentile | f7a38e8bce | |
Filippo Gentile | 5208e05c59 | |
Filippo Gentile | 8d84dd6046 | |
Filippo Gentile | 7cb17b7601 | |
Filippo Gentile | 2b191d152c | |
Filippo Gentile | 984f706c0f | |
Filippo Gentile | b84c2c80ae |
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
# Use this file when blaming
|
||||
# git-blame --ignore-revs-file .gitignore-blame
|
||||
|
||||
# Big clang-format refactor
|
||||
9f14ee1c64b3b9d399078918fc20ccce7bc4a7d9
|
10
BUILDING.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
ModelRailroadTimetablePlanner uses CMake build system.
|
||||
|
||||
CMake Minimun Version: **3.5**
|
||||
CMake Minimum Version: **3.5**
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
@ -31,7 +31,7 @@ CMake Minimun Version: **3.5**
|
|||
|
||||
- Install Qt 5:
|
||||
>`sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev qttools5-dev-tools`
|
||||
|
||||
|
||||
- Install SQLite 3
|
||||
>`sudo apt install libsqlite3-dev`
|
||||
|
||||
|
@ -40,7 +40,7 @@ CMake Minimun Version: **3.5**
|
|||
|
||||
- Install zlib
|
||||
> NOTE: automatically installed if installing libzip
|
||||
|
||||
|
||||
>`sudo apt install zlib1g-dev`
|
||||
|
||||
|
||||
|
@ -75,7 +75,7 @@ import libraries `*.dll.a`.
|
|||
To manually create an import library from a `*.dll` and associated `*.def` file, go to library directory and run:
|
||||
> `dlltool --dllname sqlite3.dll --def sqlite3.def --output-lib sqlite3.dll.a`
|
||||
|
||||
For more informations see [DLL Import Library Tool](https://www.willus.com/mingw/colinp/win32/tools/dlltool.html)
|
||||
For more information see [DLL Import Library Tool](https://www.willus.com/mingw/colinp/win32/tools/dlltool.html)
|
||||
|
||||
## Compile
|
||||
|
||||
|
@ -105,7 +105,7 @@ For more informations see [DLL Import Library Tool](https://www.willus.com/mingw
|
|||
|
||||
- Run
|
||||
> `mrtplanner`
|
||||
|
||||
|
||||
> NOTE: the location depends on where you installed the program
|
||||
> Look at `CMAKE_INSTALL_PREFIX` variable
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.17)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
project(ModelRailroadTimetablePlanner VERSION 6.1.0 LANGUAGES CXX)
|
||||
project(ModelRailroadTimetablePlanner VERSION 6.2.1 LANGUAGES CXX)
|
||||
|
||||
option(UPDATE_TS "Update translations" OFF)
|
||||
option(UPDATE_TS_KEEP_OBSOLETE "Keep obsolete entries when updating translations" ON)
|
||||
|
@ -24,7 +24,19 @@ set(PROJECT_HOMEPAGE_URL "https://github.com/gfgit/ModelRailroadTimetablePlanner
|
|||
set(APP_HELP_URL ${PROJECT_HOMEPAGE_URL})
|
||||
set(APP_UPDATE_URL ${PROJECT_HOMEPAGE_URL})
|
||||
|
||||
set(PROJECT_DESCRIPTION "${APP_DISPLAY_NAME} lets you create and manage model railway sessions")
|
||||
string(CONCAT PROJECT_DESCRIPTION "${APP_DISPLAY_NAME} is a cross-platform C++\n"
|
||||
"application with Qt GUI for model railway timetable scheduling.\n\n"
|
||||
"It lets you create and manage model railway sessions\n"
|
||||
"and provides all documents useful for driving and dispatching\n"
|
||||
"trains on big layouts like in FREMO Meetings.\n\n"
|
||||
"Main features:\n"
|
||||
" * Railway timetable graph per each line in SVG, PDF or printed\n"
|
||||
" * Group jobs (trains) in work shift\n"
|
||||
" * Export booklets in ODT (LibreOffice Writer) for shifts and stations\n"
|
||||
" * Importation of rollingstock pieces from\n"
|
||||
" other sessions or ODS Spreadsheet")
|
||||
|
||||
set(PROJECT_DESCRIPTION_SHORT "Timetables for model railroads")
|
||||
|
||||
set(APP_ICON ${CMAKE_SOURCE_DIR}/files/icons/icon.ico)
|
||||
|
||||
|
@ -39,7 +51,6 @@ option(CONFIG_GLOBAL_TRY_CATCH "Global try/catch at main()" OFF)
|
|||
option(CONFIG_NO_DEBUG_CALL_TRACE "Disable scope call trace messages" OFF)
|
||||
option(CONFIG_PRINT_DBG_MSG "Debug messages (some)" ON)
|
||||
option(CONFIG_ENABLE_BACKGROUND_MANAGER "Enable background task manager" ON)
|
||||
cmake_dependent_option(CONFIG_ENABLE_RS_CHECKER "Enable rollingstock checker" ON "CONFIG_ENABLE_BACKGROUND_MANAGER" OFF)
|
||||
cmake_dependent_option(CONFIG_SEARCHBOX_MODE_ASYNC "Use thread to search for jobs" ON "CONFIG_ENABLE_BACKGROUND_MANAGER" OFF)
|
||||
option(CONFIG_ENABLE_AUTO_TIME_RECALC "Automatic recalculation of travel times based on rollingstock speed, experimental" OFF)
|
||||
option(CONFIG_ENABLE_USER_QUERY "Enable SQL console" OFF)
|
||||
|
@ -60,10 +71,6 @@ if(CONFIG_ENABLE_BACKGROUND_MANAGER)
|
|||
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DENABLE_BACKGROUND_MANAGER)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ENABLE_RS_CHECKER)
|
||||
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DENABLE_RS_CHECKER)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SEARCHBOX_MODE_ASYNC)
|
||||
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DSEARCHBOX_MODE_ASYNC)
|
||||
endif()
|
||||
|
@ -115,29 +122,7 @@ if(BUILD_DOXYGEN)
|
|||
find_package(Doxygen)
|
||||
endif()
|
||||
|
||||
|
||||
#Locate windeployqt
|
||||
if(WIN32 AND RUN_WINDEPLOYQT AND NOT WINDEPLOYQT_EXE)
|
||||
set(WINDEPLOYQT_EXE_TMP NOTFOUND)
|
||||
message("Searching windeployqt executable")
|
||||
if(QT_QMAKE_EXECUTABLE)
|
||||
get_filename_component(WINDEPLOYQT_DIR ${QT_QMAKE_EXECUTABLE} DIRECTORY)
|
||||
set(WINDEPLOYQT_EXE_TMP "${WINDEPLOYQT_DIR}/windeployqt.exe")
|
||||
endif()
|
||||
if(NOT EXISTS ${WINDEPLOYQT_EXE_TMP} AND Qt5_DIR)
|
||||
get_filename_component(WINDEPLOYQT_EXE_TMP "${Qt5_DIR}/../../../bin/windeployqt.exe" REALPATH)
|
||||
endif()
|
||||
|
||||
if(EXISTS ${WINDEPLOYQT_EXE_TMP})
|
||||
message("Found ${WINDEPLOYQT_EXE_TMP}")
|
||||
else()
|
||||
message("windeployqt NOT FOUND")
|
||||
set(WINDEPLOYQT_EXE_TMP NOTFOUND)
|
||||
endif()
|
||||
set(WINDEPLOYQT_EXE ${WINDEPLOYQT_EXE_TMP} CACHE FILEPATH "windeployqt executable file path.")
|
||||
unset(WINDEPLOYQT_EXE_TMP)
|
||||
unset(WINDEPLOYQT_DIR)
|
||||
endif()
|
||||
include(LocateWinDeployQt)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# HOW TO CONTRIBUTE
|
||||
|
||||
I'm New to Git and Github so I don't know yet how to manage repositories and contributions.
|
||||
I'm New to Git and GitHub so I don't know yet how to manage repositories and contributions.
|
||||
|
||||
The model should be creating pull requests with topic branches and merging on master or `master` or `develop`.
|
||||
|
||||
|
@ -28,7 +28,7 @@ For more information see [Qt Documentation](https://doc.qt.io/qt-5/linguist-over
|
|||
1. Create a file named `traintimetable_*.ts` in translation folder.
|
||||
Replace placeholder with language code (i.e. `it`, `de`, `fr`, etc).
|
||||
|
||||
2. Make file known to Cmake by adding it to
|
||||
2. Make file known to CMake by adding it to
|
||||
[`src/translations/CMakeLists.txt`](src/translations/CMakeLists.txt).
|
||||
Add the file name with path in `TRAINTIMETABLE_TS_FILES` variabile.
|
||||
|
||||
|
@ -36,7 +36,7 @@ Then follow next paragraph.
|
|||
|
||||
### Update translations to match new UI elements
|
||||
|
||||
3. Run `lupdate` to fill with text to be traslated.
|
||||
3. Run `lupdate` to fill with text to be translated.
|
||||
This is done by enabling `UPDATE_TS` option in CMake and
|
||||
running `RELEASE_TRANSLATIONS` target.
|
||||
|
||||
|
|
12
README.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
[Versione in italiano](README_it.md)
|
||||
|
||||
Formely **TrainTimetable**
|
||||
Formerly **TrainTimetable**
|
||||
|
||||
A cross-platform C++ application with Qt GUI for model railway timetable scheduling.
|
||||
*Currently tested on Windows and Ubuntu.*
|
||||
|
@ -11,7 +11,7 @@ By Filippo Gentile
|
|||
|
||||
## Screenshots
|
||||
|
||||
![Screenshot of ModelRailroadTimetablePlanner](Screenshot.png "ModelRailroadTimetablePlanner Screenshot")
|
||||
![Screenshot of ModelRailroadTimetablePlanner](screenshots/Screenshot.png "ModelRailroadTimetablePlanner Screenshot")
|
||||
|
||||
## Goals
|
||||
|
||||
|
@ -27,15 +27,15 @@ for driving and dispatching trains on big layouts like in FREMO Meetings.
|
|||
- Available UI languages: English, Italian
|
||||
- Railway timetable graph per each line in SVG, PDF or printed
|
||||
- Group jobs (trains) in work shift
|
||||
- Export booklets in ODT (Libreoffice Writer) for shifts and stations
|
||||
- Export booklets in ODT (LibreOffice Writer) for shifts and stations
|
||||
- Importation of rollingstock pieces from other sessions or ODS Spreadsheet
|
||||
|
||||
## Project history
|
||||
The development started as a small hobby project back in 2016,
|
||||
in collaboration with italian FREMO organizations.
|
||||
It was rewritten from scratch due to core instsbilities and limitations.
|
||||
in collaboration with Italian FREMO organizations.
|
||||
It was rewritten from scratch due to core instabilities and limitations.
|
||||
Since then it has grown unexpectedly.
|
||||
So I'd like it to become a comunity project!
|
||||
So I'd like it to become a community project!
|
||||
|
||||
|
||||
## Project motto
|
||||
|
|
12
README_it.md
|
@ -2,7 +2,7 @@
|
|||
|
||||
[English version](README.md)
|
||||
|
||||
Precedenteme noto come **TrainTimetable**
|
||||
Precedentemente noto come **TrainTimetable**
|
||||
|
||||
Un programma multipiattaforma C++ con GUI Qt (interfaccia grafica) per creare orari di treni per plastici ferroviari.
|
||||
*Attualmente testato su Windows e Ubuntu.*
|
||||
|
@ -11,7 +11,7 @@ By Filippo Gentile
|
|||
|
||||
## Screenshots
|
||||
|
||||
![Screenshot di ModelRailroadTimetablePlanner](Screenshot_it.png "ModelRailroadTimetablePlanner Screenshot")
|
||||
![Screenshot di ModelRailroadTimetablePlanner](screenshots/Screenshot_it.png "ModelRailroadTimetablePlanner Screenshot")
|
||||
|
||||
## Obiettivi
|
||||
|
||||
|
@ -21,17 +21,17 @@ suggeriscano all'utente azioni utili al fine di
|
|||
velocizzare la creazione di tabelle orarie.
|
||||
|
||||
Il programma si prefigge di produrre tutta la documentazione
|
||||
per condurre e gestire i treni su grandi tracciati come nei FREMO Meetings.
|
||||
per condurre e gestire i treni su grandi tracciati come nei FREMO Meeting.
|
||||
|
||||
## Principali funzioni
|
||||
- Interfaccia disponibile in: Inglese, Italiano
|
||||
- Grafico orario ferroviario per ogni linea in SVG, PDF o stampato
|
||||
- Raggruppa servizi (treni) in turni lavorativi
|
||||
- Esporta libretti in ODT (Libreoffice Writer) per turni e stazioni
|
||||
- Raggruppa servizi (treni) in turni lavorativi
|
||||
- Esporta libretti in ODT (LibreOffice Writer) per turni e stazioni
|
||||
- Importazione del materiale rotabile da altre sessioni o fogli di calcolo ODS Spreadsheet
|
||||
|
||||
## Storia del progetto
|
||||
Lo sviluppo è partito come piccolo progetto hobbystico nel 2016,
|
||||
Lo sviluppo è partito come piccolo progetto hobbistico nel 2016,
|
||||
in collaborazione con organizzazioni FREMO italiane.
|
||||
È stato completamente riscritto a causa di instabilità e limitazioni nella struttura interna.
|
||||
Da allora è cresciuto inaspettatamente.
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Small helper for DLL file
|
||||
|
||||
function(get_dll_library_from_import_library LIB_VAR OUT_VAR)
|
||||
# Locate DLL file
|
||||
# Sometimes we link to import libraries '*.dll.a' or '*.lib'
|
||||
# When installing we need the real '*.dll' file
|
||||
# Try to locate it in same directory or in library path
|
||||
|
||||
get_filename_component(TEMP_EXT ${LIB_VAR} EXT)
|
||||
|
||||
if(${TEMP_EXT} MATCHES ".dll.a" OR ${TEMP_EXT} MATCHES ".lib")
|
||||
# Get filename without extension and then add '.dll'
|
||||
get_filename_component(TEMP_NAME ${LIB_VAR} NAME_WE)
|
||||
set(TEMP_NAME "${TEMP_NAME}.dll")
|
||||
|
||||
# Get file path
|
||||
get_filename_component(TEMP_PATH ${LIB_VAR} DIRECTORY)
|
||||
|
||||
# Try new file in same path
|
||||
set(TEMP_PATH "${TEMP_PATH}/${TEMP_NAME}")
|
||||
|
||||
if(NOT EXISTS ${TEMP_PATH})
|
||||
# Doesn't exist, try to find it in other directory, same name
|
||||
# Searche also in CMAKE_LIBRARY_PATH which is not used by default in find_file(...)
|
||||
find_file(TEMP_PATH_2 NAMES ${TEMP_NAME} PATHS ${CMAKE_LIBRARY_PATH})
|
||||
set(TEMP_PATH ${TEMP_PATH_2})
|
||||
|
||||
# find_file caches the variable but this causes problems
|
||||
# with subsequent calls reading old value instead of finding a new file
|
||||
unset(TEMP_PATH_2 CACHE)
|
||||
unset(TEMP_PATH_2)
|
||||
endif()
|
||||
|
||||
unset(TEMP_NAME)
|
||||
|
||||
elseif(${TEMP_EXT} MATCHES ".dll")
|
||||
# Library is already a *.dll, use it directly
|
||||
set(TEMP_PATH ${LIB_VAR})
|
||||
endif()
|
||||
|
||||
set("${OUT_VAR}" ${TEMP_PATH} PARENT_SCOPE)
|
||||
|
||||
unset(TEMP_PATH)
|
||||
unset(TEMP_EXT)
|
||||
endfunction()
|
|
@ -0,0 +1,37 @@
|
|||
# Locate windeployqt
|
||||
|
||||
if (NOT TARGET windeployqt_exe)
|
||||
add_executable(windeployqt_exe IMPORTED)
|
||||
|
||||
# Default exe name
|
||||
set(WINDEPLOYQT_EXE_NAME "windeployqt.exe")
|
||||
|
||||
if(WINDEPLOYQT_EXE_DIR)
|
||||
# If we have explicitly set directory use it
|
||||
set(WINDEPLOYQT_EXE_TMP "${WINDEPLOYQT_EXE_DIR}/${WINDEPLOYQT_EXE_NAME}")
|
||||
endif()
|
||||
if((NOT EXISTS ${WINDEPLOYQT_EXE_TMP}) AND QT_QMAKE_EXECUTABLE)
|
||||
# If we have QMake, it should be in same folder
|
||||
get_filename_component(WINDEPLOYQT_EXE_DIR ${QT_QMAKE_EXECUTABLE} DIRECTORY)
|
||||
set(WINDEPLOYQT_EXE_TMP "${WINDEPLOYQT_EXE_DIR}/${WINDEPLOYQT_EXE_NAME}")
|
||||
endif()
|
||||
if((NOT EXISTS ${WINDEPLOYQT_EXE_TMP}) AND Qt5_DIR)
|
||||
# If we have Qt5_DIR, go up and select 'bin' folder
|
||||
get_filename_component(WINDEPLOYQT_EXE_DIR "${Qt5_DIR}/../../../bin" REALPATH)
|
||||
set(WINDEPLOYQT_EXE_TMP "${WINDEPLOYQT_EXE_DIR}/${WINDEPLOYQT_EXE_NAME}")
|
||||
endif()
|
||||
|
||||
if(EXISTS ${WINDEPLOYQT_EXE_TMP})
|
||||
message("Found ${WINDEPLOYQT_EXE_TMP}")
|
||||
else()
|
||||
message("windeployqt NOT FOUND")
|
||||
set(WINDEPLOYQT_EXE_TMP NOTFOUND)
|
||||
endif()
|
||||
|
||||
set_target_properties(windeployqt_exe PROPERTIES
|
||||
IMPORTED_LOCATION ${WINDEPLOYQT_EXE_TMP}
|
||||
)
|
||||
|
||||
unset(WINDEPLOYQT_EXE_TMP)
|
||||
unset(WINDEPLOYQT_EXE_DIR)
|
||||
endif()
|
|
@ -59,6 +59,10 @@ set(CPACK_DEB_COMPONENT_INSTALL OFF)
|
|||
# list dependencies
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS YES)
|
||||
|
||||
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION_SHORT})
|
||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION ${PROJECT_DESCRIPTION})
|
||||
|
||||
set(CPACK_PROJECT_CONFIG_FILE ${CMAKE_SOURCE_DIR}/packaging/CMakeCPackOptions.cmake)
|
||||
|
||||
include(CPack)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="100mm"
|
||||
height="100mm"
|
||||
viewBox="0 0 100 100"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:none;stroke:#00aa00;stroke-width:18.4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 25,85 V 35 C 25.100727,6.7404564 75.224095,7.2276899 75,35 v 38"
|
||||
id="path876" />
|
||||
<path
|
||||
style="fill:#00aa00;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 53,70 H 97 L 75,96 Z"
|
||||
id="path2659" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 829 B |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 79 KiB |
|
@ -1,50 +1,7 @@
|
|||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
function(get_dll_library_from_import_library LIB_VAR OUT_VAR)
|
||||
# Locate DLL file
|
||||
# Sometimes we link to import libraries '*.dll.a' or '*.lib'
|
||||
# When installing we need the real '*.dll' file
|
||||
# Try to locate it in same directory or in library path
|
||||
|
||||
get_filename_component(TEMP_EXT ${LIB_VAR} EXT)
|
||||
|
||||
if(${TEMP_EXT} MATCHES ".dll.a" OR ${TEMP_EXT} MATCHES ".lib")
|
||||
# Get filename without extension and then add '.dll'
|
||||
get_filename_component(TEMP_NAME ${LIB_VAR} NAME_WE)
|
||||
set(TEMP_NAME "${TEMP_NAME}.dll")
|
||||
|
||||
# Get file path
|
||||
get_filename_component(TEMP_PATH ${LIB_VAR} DIRECTORY)
|
||||
|
||||
# Try new file in same path
|
||||
set(TEMP_PATH "${TEMP_PATH}/${TEMP_NAME}")
|
||||
|
||||
if(NOT EXISTS ${TEMP_PATH})
|
||||
# Doesn't exist, try to find it in other directory, same name
|
||||
# Searche also in CMAKE_LIBRARY_PATH which is not used by default in find_file(...)
|
||||
find_file(TEMP_PATH_2 NAMES ${TEMP_NAME} PATHS ${CMAKE_LIBRARY_PATH})
|
||||
set(TEMP_PATH ${TEMP_PATH_2})
|
||||
|
||||
# find_file caches the variable but this causes problems
|
||||
# with subsequent calls reading old value instead of finding a new file
|
||||
unset(TEMP_PATH_2 CACHE)
|
||||
unset(TEMP_PATH_2)
|
||||
endif()
|
||||
|
||||
unset(TEMP_NAME)
|
||||
|
||||
elseif(${TEMP_EXT} MATCHES ".dll")
|
||||
# Library is already a *.dll, use it directly
|
||||
set(TEMP_PATH ${LIB_VAR})
|
||||
endif()
|
||||
|
||||
set("${OUT_VAR}" ${TEMP_PATH} PARENT_SCOPE)
|
||||
|
||||
unset(TEMP_PATH)
|
||||
unset(TEMP_EXT)
|
||||
endfunction()
|
||||
|
||||
include(DLL_Utils)
|
||||
|
||||
#Set Win32 resources
|
||||
if (WIN32)
|
||||
|
@ -183,78 +140,6 @@ if (WIN32)
|
|||
)
|
||||
endif()
|
||||
|
||||
## Enable Crashpad if found
|
||||
#if (GoogleCrashpad_FOUND)
|
||||
# set(OLIVE_DEFINITIONS ${OLIVE_DEFINITIONS} USE_CRASHPAD)
|
||||
|
||||
# target_include_directories(
|
||||
# ${OLIVE_TARGET}
|
||||
# PRIVATE
|
||||
# ${CRASHPAD_INCLUDE_DIRS}
|
||||
# )
|
||||
|
||||
# target_link_libraries(
|
||||
# ${OLIVE_TARGET}
|
||||
# PRIVATE
|
||||
# ${CRASHPAD_LIBRARIES}
|
||||
# )
|
||||
|
||||
# set(OLIVE_CRASH_TARGET "olive-crashhandler")
|
||||
|
||||
# set(OLIVE_CRASH_SOURCES
|
||||
# dialog/crashhandler/crashhandler.h
|
||||
# dialog/crashhandler/crashhandler.cpp
|
||||
# dialog/crashhandler/crashhandlermain.cpp
|
||||
# )
|
||||
|
||||
# if (WIN32)
|
||||
# add_executable(
|
||||
# ${OLIVE_CRASH_TARGET}
|
||||
# WIN32
|
||||
# ${OLIVE_CRASH_SOURCES}
|
||||
# )
|
||||
# else()
|
||||
# add_executable(
|
||||
# ${OLIVE_CRASH_TARGET}
|
||||
# ${OLIVE_CRASH_SOURCES}
|
||||
# )
|
||||
# endif()
|
||||
|
||||
# target_include_directories(
|
||||
# ${OLIVE_CRASH_TARGET}
|
||||
# PRIVATE
|
||||
# ${CRASHPAD_INCLUDE_DIRS}
|
||||
# )
|
||||
|
||||
# target_link_libraries(
|
||||
# ${OLIVE_CRASH_TARGET}
|
||||
# PRIVATE
|
||||
# Qt5::Core
|
||||
# Qt5::Gui
|
||||
# Qt5::Widgets
|
||||
# Qt5::Network
|
||||
# ${CRASHPAD_LIBRARIES}
|
||||
# )
|
||||
|
||||
# set(CRASHPAD_HANDLER "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
# set(MINIDUMP_STACKWALK "minidump_stackwalk${CMAKE_EXECUTABLE_SUFFIX}")
|
||||
|
||||
# if(UNIX AND NOT APPLE)
|
||||
# install(TARGETS ${OLIVE_CRASH_TARGET} RUNTIME DESTINATION bin)
|
||||
# install(PROGRAMS ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} DESTINATION bin)
|
||||
# install(PROGRAMS ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} DESTINATION bin)
|
||||
# endif()
|
||||
|
||||
# if(APPLE)
|
||||
# # Move crash handler executables inside Mac app bundle
|
||||
# add_custom_command(TARGET ${OLIVE_CRASH_TARGET} POST_BUILD
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_if_different ${OLIVE_CRASH_TARGET} $<TARGET_FILE_DIR:${OLIVE_TARGET}>
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} $<TARGET_FILE_DIR:${OLIVE_TARGET}>
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} $<TARGET_FILE_DIR:${OLIVE_TARGET}>
|
||||
# )
|
||||
# endif()
|
||||
#endif()
|
||||
|
||||
# Set compiler definitions
|
||||
target_compile_definitions(${MR_TIMETABLE_PLANNER_TARGET} PRIVATE ${MR_TIMETABLE_PLANNER_DEFINITIONS})
|
||||
|
||||
|
@ -315,10 +200,13 @@ endforeach()
|
|||
# Copy executable
|
||||
install(TARGETS ${MR_TIMETABLE_PLANNER_TARGET})
|
||||
|
||||
# Copy SVG icon
|
||||
# Copy SVG icons
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/files/icons/lightning/lightning.svg
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}/icons)
|
||||
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/files/icons/reverse_direction/reverse_direction.svg
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}/icons)
|
||||
|
||||
# For each .ts file install corrensponding .qm file
|
||||
foreach(TS_FILE ${MR_TIMETABLE_PLANNER_TS_FILES})
|
||||
get_filename_component(QM_FILE_NAME ${TS_FILE} NAME_WLE)
|
||||
|
@ -340,21 +228,21 @@ if(WIN32)
|
|||
install(PROGRAMS ${LibZip_LIBRARY_TO_INSTALL} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(RUN_WINDEPLOYQT)
|
||||
if(NOT WINDEPLOYQT_EXE)
|
||||
message(FATAL_ERROR "In order to run windeployqt you must first set th exe path in WINDEPLOYQT_EXE")
|
||||
if(NOT TARGET windeployqt_exe)
|
||||
message(FATAL_ERROR "In order to run windeployqt you must first set the exe path in WINDEPLOYQT_EXE_DIR")
|
||||
endif()
|
||||
|
||||
# Use [[...]] to delay variable expansion
|
||||
install(CODE "
|
||||
message(STATUS \"Running windeployqt ${WINDEPLOYQT_EXE} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\")
|
||||
execute_process(COMMAND ${WINDEPLOYQT_EXE} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}
|
||||
message(STATUS \"Running windeployqt ${windeployqt_exe} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\")
|
||||
execute_process(COMMAND ${windeployqt_exe} ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}
|
||||
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}
|
||||
OUTPUT_VARIABLE WINDEPLOYQT_EXE_RESULT
|
||||
ERROR_VARIABLE WINDEPLOYQT_EXE_RESULT)
|
||||
|
||||
message(STATUS \"${WINDEPLOYQT_EXE_RESULT}\")
|
||||
|
||||
message(STATUS \"windeployqt Done.\")
|
||||
message(STATUS \"${windeployqt_exe} Done.\")
|
||||
")
|
||||
endif()
|
||||
endif()
|
||||
|
|
171
src/app/main.cpp
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include "app/session.h"
|
||||
|
@ -27,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());
|
||||
|
@ -54,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;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
|
@ -5,7 +24,6 @@
|
|||
|
||||
#include "viewmanager/viewmanager.h"
|
||||
|
||||
|
||||
#include "jobs/jobeditor/jobpatheditor.h"
|
||||
#include <QDockWidget>
|
||||
|
||||
|
@ -33,7 +51,7 @@
|
|||
#include "printing/wizard/printwizard.h"
|
||||
|
||||
#ifdef ENABLE_USER_QUERY
|
||||
#include "sqlconsole/sqlconsole.h"
|
||||
# include "sqlconsole/sqlconsole.h"
|
||||
#endif
|
||||
|
||||
#include <QActionGroup>
|
||||
|
@ -42,12 +60,11 @@
|
|||
#include "searchbox/searchresultmodel.h"
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
#include "backgroundmanager/backgroundmanager.h"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
#include "rollingstock/rs_checker/rserrorswidget.h"
|
||||
#endif
|
||||
# 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"
|
||||
#include "info.h"
|
||||
|
@ -68,10 +85,9 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow),
|
||||
jobEditor(nullptr),
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsErrorsWidget(nullptr),
|
||||
rsErrDock(nullptr),
|
||||
#endif
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
resPanelDock(nullptr),
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
view(nullptr),
|
||||
jobDock(nullptr),
|
||||
searchEdit(nullptr),
|
||||
|
@ -83,63 +99,72 @@ 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_RS_CHECKER
|
||||
//RS Errors dock
|
||||
rsErrorsWidget = new RsErrorsWidget(this);
|
||||
rsErrDock = new QDockWidget(rsErrorsWidget->windowTitle(), this);
|
||||
rsErrDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
|
||||
rsErrDock->setWidget(rsErrorsWidget);
|
||||
rsErrDock->installEventFilter(this); //NOTE: see eventFilter() below
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
// Background Errors dock
|
||||
BackgroundResultPanel *resPanel = new BackgroundResultPanel(this);
|
||||
resPanelDock = new QDockWidget(tr("Errors"), this);
|
||||
resPanelDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
|
||||
resPanelDock->setWidget(resPanel);
|
||||
resPanelDock->installEventFilter(this); // NOTE: see eventFilter() below
|
||||
|
||||
addDockWidget(Qt::BottomDockWidgetArea, rsErrDock);
|
||||
ui->menuView->addAction(rsErrDock->toggleViewAction());
|
||||
ui->mainToolBar->addAction(rsErrDock->toggleViewAction());
|
||||
#endif
|
||||
addDockWidget(Qt::BottomDockWidgetArea, resPanelDock);
|
||||
ui->menuView->addAction(resPanelDock->toggleViewAction());
|
||||
ui->mainToolBar->addAction(resPanelDock->toggleViewAction());
|
||||
|
||||
//Allow JobPathEditor to use all vertical space when RsErrorWidget dock is at bottom
|
||||
// Add checkers FIXME: move to session?
|
||||
JobCrossingChecker *jobCrossingChecker = new JobCrossingChecker(Session->m_Db, this);
|
||||
Session->getBackgroundManager()->addChecker(jobCrossingChecker);
|
||||
|
||||
RsCheckerManager *rsChecker = new RsCheckerManager(Session->m_Db, this);
|
||||
Session->getBackgroundManager()->addChecker(rsChecker);
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
// 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);
|
||||
|
@ -148,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]);
|
||||
}
|
||||
|
@ -162,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);
|
||||
|
@ -216,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);
|
||||
|
@ -239,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);
|
||||
});
|
||||
}
|
||||
|
@ -260,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);
|
||||
|
@ -285,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;
|
||||
|
@ -311,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();
|
||||
|
@ -338,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;
|
||||
|
@ -352,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();
|
||||
|
@ -448,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--;
|
||||
|
@ -487,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());
|
||||
|
@ -502,21 +537,20 @@ void MainWindow::onNew()
|
|||
emit Session->getBackgroundManager()->abortTrivialTasks();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
if(Session->getBackgroundManager()->isRunning())
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
OwningQPointer<QFileDialog> dlg = new QFileDialog(this, tr("Create new Session"));
|
||||
dlg->setFileMode(QFileDialog::AnyFile);
|
||||
|
@ -529,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();
|
||||
|
@ -552,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);
|
||||
|
@ -571,7 +604,7 @@ void MainWindow::onNew()
|
|||
|
||||
void MainWindow::onSave()
|
||||
{
|
||||
if(!Session->getViewManager()->closeEditors())
|
||||
if (!Session->getViewManager()->closeEditors())
|
||||
return;
|
||||
|
||||
Session->releaseAllSavepoints();
|
||||
|
@ -581,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"));
|
||||
|
@ -595,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;
|
||||
|
@ -627,7 +661,7 @@ void MainWindow::onSaveCopyAs()
|
|||
|
||||
void MainWindow::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
if(closeSession())
|
||||
if (closeSession())
|
||||
e->accept();
|
||||
else
|
||||
e->ignore();
|
||||
|
@ -635,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;
|
||||
|
@ -657,9 +690,11 @@ void MainWindow::setCentralWidgetMode(MainWindow::CentralWidgetMode mode)
|
|||
case CentralWidgetMode::StartPageMode:
|
||||
{
|
||||
jobDock->hide();
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsErrDock->hide();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
resPanelDock->hide();
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
welcomeLabel->setText(tr("<p>Open a file: <b>File</b> > <b>Open</b></p>"
|
||||
"<p>Create new project: <b>File</b> > <b>New</b></p>"));
|
||||
statusBar()->showMessage(tr("Open file or create a new one"));
|
||||
|
@ -669,48 +704,52 @@ void MainWindow::setCentralWidgetMode(MainWindow::CentralWidgetMode mode)
|
|||
case CentralWidgetMode::NoLinesWarningMode:
|
||||
{
|
||||
jobDock->show();
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsErrDock->hide();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
resPanelDock->hide();
|
||||
#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:
|
||||
{
|
||||
jobDock->show();
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsErrDock->show();
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
resPanelDock->show();
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
welcomeLabel->setText(QString());
|
||||
break;
|
||||
}
|
||||
|
@ -718,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);
|
||||
}
|
||||
|
||||
|
@ -769,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();
|
||||
}
|
||||
|
||||
|
@ -778,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();
|
||||
|
||||
|
@ -790,17 +831,19 @@ bool MainWindow::closeSession()
|
|||
return false;
|
||||
}
|
||||
|
||||
//Start a timer to try again
|
||||
// Start a timer to try again
|
||||
closeTimerId = startTimer(1500);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(err != DB_Error::NoError && err != DB_Error::DbNotOpen)
|
||||
stopCloseTimer();
|
||||
|
||||
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;
|
||||
|
@ -810,11 +853,11 @@ void MainWindow::enableDBActions(bool enable)
|
|||
{
|
||||
databaseActionGroup->setEnabled(enable);
|
||||
searchEdit->setEnabled(enable);
|
||||
if(!enable)
|
||||
if (!enable)
|
||||
jobEditor->setEnabled(false);
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsErrorsWidget->setEnabled(enable);
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
resPanelDock->widget()->setEnabled(enable);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -892,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;
|
||||
|
@ -932,41 +975,35 @@ 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_RS_CHECKER
|
||||
else if(watched == rsErrDock && event->type() == QEvent::Close)
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
else if (watched == resPanelDock && event->type() == QEvent::Close)
|
||||
{
|
||||
if(rsErrDock->isFloating())
|
||||
if (resPanelDock->isFloating())
|
||||
{
|
||||
QTimer::singleShot(0, rsErrDock, [this]()
|
||||
{
|
||||
rsErrDock->setFloating(false);
|
||||
});
|
||||
QTimer::singleShot(0, resPanelDock, [this]() { resPanelDock->setFloating(false); });
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
return QMainWindow::eventFilter(watched, event);
|
||||
}
|
||||
|
@ -980,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
|
@ -22,10 +41,6 @@ class QLabel;
|
|||
class QActionGroup;
|
||||
class CustomCompletionLineEdit;
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
class RsErrorsWidget;
|
||||
#endif
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -42,7 +57,7 @@ public:
|
|||
void loadFile(const QString &fileName);
|
||||
|
||||
bool closeSession();
|
||||
|
||||
|
||||
private slots:
|
||||
void onStationManager();
|
||||
|
||||
|
@ -112,10 +127,9 @@ private:
|
|||
|
||||
JobPathEditor *jobEditor;
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
RsErrorsWidget *rsErrorsWidget;
|
||||
QDockWidget *rsErrDock;
|
||||
#endif
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
QDockWidget *resPanelDock;
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
LineGraphWidget *view;
|
||||
QDockWidget *jobDock;
|
||||
|
@ -126,8 +140,11 @@ private:
|
|||
|
||||
QActionGroup *databaseActionGroup;
|
||||
|
||||
enum { MaxRecentFiles = 5 };
|
||||
QAction* recentFileActs[MaxRecentFiles];
|
||||
enum
|
||||
{
|
||||
MaxRecentFiles = 5
|
||||
};
|
||||
QAction *recentFileActs[MaxRecentFiles];
|
||||
|
||||
CentralWidgetMode m_mode;
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "propertiesdialog.h"
|
||||
|
||||
#include "app/session.h"
|
||||
|
@ -28,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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PROPERTIESDIALOG_H
|
||||
#define PROPERTIESDIALOG_H
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "app/scopedebug.h"
|
||||
|
||||
#ifndef NO_DEBUG_CALL_TRACE
|
||||
|
@ -9,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();
|
||||
}
|
||||
|
|
|
@ -1,57 +1,71 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "app/session.h"
|
||||
#include "info.h"
|
||||
|
||||
|
@ -10,22 +29,15 @@
|
|||
#include "db_metadata/metadatamanager.h"
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
#include "backgroundmanager/backgroundmanager.h"
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
#include "rollingstock/rs_checker/rscheckermanager.h"
|
||||
#endif // ENABLE_RS_CHECKER
|
||||
|
||||
# include "backgroundmanager/backgroundmanager.h"
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
#include "utils/jobcategorystrings.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include <QTranslator>
|
||||
|
||||
MeetingSession* MeetingSession::session;
|
||||
MeetingSession *MeetingSession::session;
|
||||
QString MeetingSession::appDataPath;
|
||||
const QLocale MeetingSession::embeddedLocale = QLocale(QLocale::English, QLocale::UnitedStates);
|
||||
|
||||
|
@ -42,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);
|
||||
|
@ -64,7 +77,7 @@ MeetingSession::MeetingSession() :
|
|||
|
||||
MeetingSession::~MeetingSession()
|
||||
{
|
||||
//Delete sheet export translator
|
||||
// Delete sheet export translator
|
||||
setSheetExportTranslator(nullptr, sheetExportLocale);
|
||||
}
|
||||
|
||||
|
@ -78,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;
|
||||
}
|
||||
|
@ -111,45 +124,43 @@ 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_RS_CHECKER
|
||||
if(settings.getCheckRSWhenOpeningDB())
|
||||
backgroundManager->getRsChecker()->startWorker();
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
backgroundManager->handleSessionLoaded();
|
||||
#endif
|
||||
|
||||
|
||||
return DB_Error::NoError;
|
||||
}
|
||||
|
||||
|
@ -157,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
|
||||
|
@ -168,31 +179,31 @@ 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_RS_CHECKER
|
||||
backgroundManager->getRsChecker()->clearModel();
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
backgroundManager->clearResults();
|
||||
#endif
|
||||
|
||||
fileName.clear();
|
||||
|
@ -200,28 +211,36 @@ 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);
|
||||
|
||||
fileName = file;
|
||||
|
||||
//Tables
|
||||
/* NOTE: SQLite Extended Error codes as of version 3.35.0
|
||||
* If a foreign key is explicitly created with "ON DELETE RESTRICT",
|
||||
* it will trigger SQLITE_CONSTRAINT_TRIGGER error,
|
||||
* if not explicitly set, it will trigger SQLITE_CONSTRAINT_FOREIGNKEY error.
|
||||
*/
|
||||
|
||||
// Tables
|
||||
result = m_Db.execute("CREATE TABLE rs_models ("
|
||||
"id INTEGER,"
|
||||
"name TEXT,"
|
||||
|
@ -262,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 ("
|
||||
|
@ -345,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 ("
|
||||
|
@ -363,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) )");
|
||||
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 ("
|
||||
|
@ -404,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 ("
|
||||
|
@ -449,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 ("
|
||||
|
@ -464,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 ("
|
||||
|
@ -475,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 ("
|
||||
|
@ -484,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"
|
||||
|
@ -547,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);
|
||||
|
@ -568,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
|
||||
|
||||
|
@ -589,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;
|
||||
}
|
||||
|
@ -615,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;
|
||||
|
@ -631,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;
|
||||
|
@ -651,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;
|
||||
|
@ -673,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;
|
||||
|
@ -695,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;
|
||||
|
@ -713,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;
|
||||
|
@ -723,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();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEETINGSESSION_H
|
||||
#define MEETINGSESSION_H
|
||||
|
||||
|
@ -14,7 +33,6 @@ using namespace sqlite3pp;
|
|||
|
||||
#include <settings/appsettings.h>
|
||||
|
||||
|
||||
class ViewManager;
|
||||
class MetaDataManager;
|
||||
|
||||
|
@ -46,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);
|
||||
//TODO: separate job stop changes (time plan) from track changes (track plan)
|
||||
void stationPlanChanged(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);
|
||||
|
@ -107,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();
|
||||
|
@ -153,7 +181,7 @@ public:
|
|||
|
||||
QString fileName;
|
||||
|
||||
//AppData
|
||||
// AppData
|
||||
public:
|
||||
static void locateAppdata();
|
||||
static QString appDataPath;
|
||||
|
@ -209,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
|
||||
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
set(MR_TIMETABLE_PLANNER_SOURCES
|
||||
${MR_TIMETABLE_PLANNER_SOURCES}
|
||||
|
||||
backgroundmanager/backgroundmanager.h
|
||||
backgroundmanager/backgroundmanager.cpp
|
||||
|
||||
backgroundmanager/backgroundresultpanel.cpp
|
||||
backgroundmanager/backgroundresultpanel.h
|
||||
|
||||
backgroundmanager/backgroundresultwidget.cpp
|
||||
backgroundmanager/backgroundresultwidget.h
|
||||
|
||||
|
||||
backgroundmanager/ibackgroundchecker.cpp
|
||||
backgroundmanager/ibackgroundchecker.h
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -1,50 +1,91 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backgroundmanager.h"
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
# include "backgroundmanager/ibackgroundchecker.h"
|
||||
|
||||
#include "app/session.h"
|
||||
|
||||
#include <QThreadPool>
|
||||
#include "rollingstock/rs_checker/rscheckermanager.h"
|
||||
|
||||
#include <QSet>
|
||||
# include <QThreadPool>
|
||||
# include <QSet>
|
||||
|
||||
BackgroundManager::BackgroundManager(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsChecker = new RsCheckerManager(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
BackgroundManager::~BackgroundManager()
|
||||
{
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
delete rsChecker;
|
||||
rsChecker = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BackgroundManager::handleSessionLoaded()
|
||||
{
|
||||
for (IBackgroundChecker *mgr : qAsConst(checkers))
|
||||
mgr->startWorker();
|
||||
}
|
||||
|
||||
void BackgroundManager::abortAllTasks()
|
||||
{
|
||||
emit abortTrivialTasks();
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
rsChecker->abortTasks();
|
||||
#endif
|
||||
for (IBackgroundChecker *mgr : qAsConst(checkers))
|
||||
mgr->abortTasks();
|
||||
}
|
||||
|
||||
bool BackgroundManager::isRunning()
|
||||
{
|
||||
bool running = QThreadPool::globalInstance()->activeThreadCount() > 0;
|
||||
if(running)
|
||||
if (running)
|
||||
return true;
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
running |= rsChecker->isRunning();
|
||||
#endif
|
||||
for (IBackgroundChecker *mgr : qAsConst(checkers))
|
||||
{
|
||||
if (mgr->isRunning())
|
||||
return true;
|
||||
}
|
||||
|
||||
return running;
|
||||
return false;
|
||||
}
|
||||
|
||||
void BackgroundManager::addChecker(IBackgroundChecker *mgr)
|
||||
{
|
||||
checkers.append(mgr);
|
||||
|
||||
connect(mgr, &IBackgroundChecker::destroyed, this,
|
||||
[this](QObject *self) { removeChecker(static_cast<IBackgroundChecker *>(self)); });
|
||||
|
||||
emit checkerAdded(mgr);
|
||||
}
|
||||
|
||||
void BackgroundManager::removeChecker(IBackgroundChecker *mgr)
|
||||
{
|
||||
emit checkerRemoved(mgr);
|
||||
disconnect(mgr, nullptr, this, nullptr);
|
||||
checkers.removeOne(mgr);
|
||||
}
|
||||
|
||||
void BackgroundManager::clearResults()
|
||||
{
|
||||
for (IBackgroundChecker *mgr : qAsConst(checkers))
|
||||
{
|
||||
mgr->clearModel();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
|
|
@ -1,15 +1,33 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKGROUNDMANAGER_H
|
||||
#define BACKGROUNDMANAGER_H
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
#include <QObject>
|
||||
# include <QObject>
|
||||
# include <QVector>
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
class RsCheckerManager;
|
||||
#endif
|
||||
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
|
||||
|
@ -17,12 +35,14 @@ public:
|
|||
explicit BackgroundManager(QObject *parent = nullptr);
|
||||
~BackgroundManager() override;
|
||||
|
||||
void handleSessionLoaded();
|
||||
void abortAllTasks();
|
||||
bool isRunning();
|
||||
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
inline RsCheckerManager *getRsChecker() const { return rsChecker; };
|
||||
#endif // ENABLE_RS_CHECKER
|
||||
void addChecker(IBackgroundChecker *mgr);
|
||||
void removeChecker(IBackgroundChecker *mgr);
|
||||
|
||||
void clearResults();
|
||||
|
||||
signals:
|
||||
/* abortTrivialTasks() signal
|
||||
|
@ -39,10 +59,12 @@ signals:
|
|||
*/
|
||||
void abortTrivialTasks();
|
||||
|
||||
void checkerAdded(IBackgroundChecker *mgr);
|
||||
void checkerRemoved(IBackgroundChecker *mgr);
|
||||
|
||||
private:
|
||||
#ifdef ENABLE_RS_CHECKER
|
||||
RsCheckerManager *rsChecker;
|
||||
#endif
|
||||
friend class BackgroundResultPanel;
|
||||
QVector<IBackgroundChecker *> checkers;
|
||||
};
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
# include "backgroundresultpanel.h"
|
||||
|
||||
# include "backgroundmanager/ibackgroundchecker.h"
|
||||
# include "backgroundresultwidget.h"
|
||||
|
||||
# include "app/session.h"
|
||||
# include "backgroundmanager.h"
|
||||
|
||||
BackgroundResultPanel::BackgroundResultPanel(QWidget *parent) :
|
||||
QTabWidget(parent)
|
||||
{
|
||||
auto bkMgr = Session->getBackgroundManager();
|
||||
connect(bkMgr, &BackgroundManager::checkerAdded, this, &BackgroundResultPanel::addChecker);
|
||||
connect(bkMgr, &BackgroundManager::checkerRemoved, this, &BackgroundResultPanel::removeChecker);
|
||||
|
||||
for (auto mgr : bkMgr->checkers)
|
||||
addChecker(mgr);
|
||||
}
|
||||
|
||||
void BackgroundResultPanel::addChecker(IBackgroundChecker *mgr)
|
||||
{
|
||||
BackgroundResultWidget *w = new BackgroundResultWidget(mgr, this);
|
||||
addTab(w, mgr->getName());
|
||||
}
|
||||
|
||||
void BackgroundResultPanel::removeChecker(IBackgroundChecker *mgr)
|
||||
{
|
||||
for (int i = 0; i < count(); i++)
|
||||
{
|
||||
BackgroundResultWidget *w = static_cast<BackgroundResultWidget *>(widget(i));
|
||||
if (w->mgr == mgr)
|
||||
{
|
||||
removeTab(i);
|
||||
delete w;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKGROUNDRESULTPANEL_H
|
||||
#define BACKGROUNDRESULTPANEL_H
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
# include <QTabWidget>
|
||||
|
||||
class IBackgroundChecker;
|
||||
|
||||
class BackgroundResultPanel : public QTabWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BackgroundResultPanel(QWidget *parent = nullptr);
|
||||
|
||||
private slots:
|
||||
void addChecker(IBackgroundChecker *mgr);
|
||||
void removeChecker(IBackgroundChecker *mgr);
|
||||
};
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
#endif // BACKGROUNDRESULTPANEL_H
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
# include "ibackgroundchecker.h"
|
||||
# include "backgroundresultwidget.h"
|
||||
|
||||
# include <QTreeView>
|
||||
# include <QHeaderView>
|
||||
# include <QProgressBar>
|
||||
# include <QPushButton>
|
||||
|
||||
# include <QGridLayout>
|
||||
|
||||
# include <QMenu>
|
||||
# include <QAction>
|
||||
|
||||
# include <QTimerEvent>
|
||||
|
||||
BackgroundResultWidget::BackgroundResultWidget(IBackgroundChecker *mgr_, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
mgr(mgr_),
|
||||
timerId(0)
|
||||
{
|
||||
view = new QTreeView;
|
||||
view->setUniformRowHeights(true);
|
||||
view->setSelectionBehavior(QTreeView::SelectRows);
|
||||
|
||||
progressBar = new QProgressBar;
|
||||
startBut = new QPushButton(tr("Start"));
|
||||
stopBut = new QPushButton(tr("Stop"));
|
||||
|
||||
startBut->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
stopBut->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
|
||||
|
||||
view->setModel(mgr_->getModel());
|
||||
view->header()->setStretchLastSection(true);
|
||||
view->setSelectionBehavior(QTreeView::SelectRows);
|
||||
|
||||
QGridLayout *grid = new QGridLayout(this);
|
||||
grid->addWidget(view, 0, 0, 1, 3);
|
||||
grid->addWidget(startBut, 1, 0, 1, 1);
|
||||
grid->addWidget(stopBut, 1, 1, 1, 1);
|
||||
grid->addWidget(progressBar, 1, 2, 1, 1);
|
||||
|
||||
connect(mgr, &IBackgroundChecker::progress, this, &BackgroundResultWidget::onTaskProgress);
|
||||
connect(mgr, &IBackgroundChecker::taskFinished, this, &BackgroundResultWidget::taskFinished);
|
||||
|
||||
connect(startBut, &QPushButton::clicked, this, &BackgroundResultWidget::startTask);
|
||||
connect(stopBut, &QPushButton::clicked, this, &BackgroundResultWidget::stopTask);
|
||||
|
||||
view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(view, &QTreeView::customContextMenuRequested, this,
|
||||
&BackgroundResultWidget::showContextMenu);
|
||||
|
||||
setWindowTitle(tr("Error Results"));
|
||||
|
||||
progressBar->hide();
|
||||
}
|
||||
|
||||
void BackgroundResultWidget::startTask()
|
||||
{
|
||||
progressBar->setValue(0);
|
||||
|
||||
if (mgr->startWorker())
|
||||
{
|
||||
if (timerId)
|
||||
{
|
||||
killTimer(timerId); // Stop progressBar from hiding in 1 second
|
||||
timerId = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundResultWidget::stopTask()
|
||||
{
|
||||
mgr->abortTasks();
|
||||
}
|
||||
|
||||
void BackgroundResultWidget::onTaskProgress(int val, int max)
|
||||
{
|
||||
progressBar->setMaximum(max);
|
||||
progressBar->setValue(val);
|
||||
progressBar->show();
|
||||
}
|
||||
|
||||
void BackgroundResultWidget::taskFinished()
|
||||
{
|
||||
progressBar->setValue(progressBar->maximum());
|
||||
|
||||
if (timerId)
|
||||
killTimer(timerId);
|
||||
timerId = startTimer(1000); // Hide progressBar after 1 second
|
||||
}
|
||||
|
||||
void BackgroundResultWidget::timerEvent(QTimerEvent *e)
|
||||
{
|
||||
if (e->timerId() == timerId)
|
||||
{
|
||||
killTimer(timerId);
|
||||
timerId = 0;
|
||||
progressBar->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundResultWidget::showContextMenu(const QPoint &pos)
|
||||
{
|
||||
QModelIndex idx = view->indexAt(pos);
|
||||
if (!idx.isValid())
|
||||
return;
|
||||
|
||||
mgr->showContextMenu(this, view->viewport()->mapToGlobal(pos), idx);
|
||||
}
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKGROUNDRESULTWIDGET_H
|
||||
#define BACKGROUNDRESULTWIDGET_H
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
# include <QWidget>
|
||||
|
||||
class QTreeView;
|
||||
class QProgressBar;
|
||||
class QPushButton;
|
||||
|
||||
class IBackgroundChecker;
|
||||
|
||||
class BackgroundResultWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit BackgroundResultWidget(IBackgroundChecker *mgr_, QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void startTask();
|
||||
void stopTask();
|
||||
void onTaskProgress(int val, int max);
|
||||
void taskFinished();
|
||||
void showContextMenu(const QPoint &pos);
|
||||
|
||||
private:
|
||||
friend class BackgroundResultPanel;
|
||||
|
||||
IBackgroundChecker *mgr;
|
||||
QTreeView *view;
|
||||
QProgressBar *progressBar;
|
||||
QPushButton *startBut;
|
||||
QPushButton *stopBut;
|
||||
int timerId;
|
||||
};
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
#endif // BACKGROUNDRESULTWIDGET_H
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
# include "ibackgroundchecker.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)
|
||||
{
|
||||
e->setAccepted(true);
|
||||
|
||||
TaskProgressEvent *ev = static_cast<TaskProgressEvent *>(e);
|
||||
emit progress(ev->progress, ev->progressMax);
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (e->type() == eventType)
|
||||
{
|
||||
e->setAccepted(true);
|
||||
|
||||
GenericTaskEvent *ev = static_cast<GenericTaskEvent *>(e);
|
||||
if (m_mainWorker && ev->task == m_mainWorker)
|
||||
{
|
||||
if (!m_mainWorker->wasStopped())
|
||||
{
|
||||
setErrors(ev, false);
|
||||
}
|
||||
|
||||
delete m_mainWorker;
|
||||
m_mainWorker = nullptr;
|
||||
|
||||
emit taskFinished();
|
||||
}
|
||||
else
|
||||
{
|
||||
int idx = m_workers.indexOf(ev->task);
|
||||
if (idx != -1)
|
||||
{
|
||||
m_workers.removeAt(idx);
|
||||
if (!ev->task->wasStopped())
|
||||
setErrors(ev, true);
|
||||
|
||||
delete ev->task;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
bool IBackgroundChecker::startWorker()
|
||||
{
|
||||
if (m_mainWorker)
|
||||
return false;
|
||||
|
||||
if (!mDb.db())
|
||||
return false;
|
||||
|
||||
m_mainWorker = createMainWorker();
|
||||
|
||||
QThreadPool::globalInstance()->start(m_mainWorker);
|
||||
|
||||
for (auto task = m_workers.begin(); task != m_workers.end(); task++)
|
||||
{
|
||||
if (QThreadPool::globalInstance()->tryTake(*task))
|
||||
{
|
||||
IQuittableTask *ptr = *task;
|
||||
m_workers.erase(task);
|
||||
delete ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
(*task)->stop();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IBackgroundChecker::abortTasks()
|
||||
{
|
||||
if (m_mainWorker)
|
||||
{
|
||||
m_mainWorker->stop();
|
||||
}
|
||||
|
||||
for (IQuittableTask *task : qAsConst(m_workers))
|
||||
{
|
||||
task->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void IBackgroundChecker::sessionLoadedHandler()
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
void IBackgroundChecker::addSubTask(IQuittableTask *task)
|
||||
{
|
||||
m_workers.append(task);
|
||||
QThreadPool::globalInstance()->start(task);
|
||||
}
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IBACKGROUNDCHECKER_H
|
||||
#define IBACKGROUNDCHECKER_H
|
||||
|
||||
#ifdef ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
# include <QObject>
|
||||
# include <QVector>
|
||||
|
||||
class QAbstractItemModel;
|
||||
class QModelIndex;
|
||||
class QWidget;
|
||||
class QPoint;
|
||||
|
||||
class IQuittableTask;
|
||||
|
||||
namespace sqlite3pp {
|
||||
class database;
|
||||
}
|
||||
|
||||
class IBackgroundChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IBackgroundChecker(sqlite3pp::database &db, QObject *parent = nullptr);
|
||||
|
||||
bool event(QEvent *e) override;
|
||||
|
||||
bool startWorker();
|
||||
void abortTasks();
|
||||
|
||||
inline bool isRunning() const
|
||||
{
|
||||
return m_mainWorker || m_workers.size() > 0;
|
||||
}
|
||||
|
||||
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 void sessionLoadedHandler();
|
||||
|
||||
signals:
|
||||
void progress(int val, int max);
|
||||
void taskFinished();
|
||||
|
||||
protected:
|
||||
void addSubTask(IQuittableTask *task);
|
||||
|
||||
virtual IQuittableTask *createMainWorker() = 0;
|
||||
virtual void setErrors(QEvent *e, bool merge) = 0;
|
||||
|
||||
protected:
|
||||
sqlite3pp::database &mDb;
|
||||
QAbstractItemModel *errorsModel = nullptr;
|
||||
int eventType = 0;
|
||||
|
||||
private:
|
||||
IQuittableTask *m_mainWorker = nullptr;
|
||||
QVector<IQuittableTask *> m_workers;
|
||||
};
|
||||
|
||||
#endif // ENABLE_BACKGROUND_MANAGER
|
||||
|
||||
#endif // IBACKGROUNDCHECKER_H
|
|
@ -1,11 +1,29 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "imagemetadata.h"
|
||||
|
||||
#include <sqlite3pp/sqlite3pp.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace ImageMetaData
|
||||
{
|
||||
namespace ImageMetaData {
|
||||
|
||||
constexpr char sql_get_key_id[] = "SELECT rowid FROM metadata WHERE name=? AND val NOT NULL";
|
||||
|
||||
|
@ -16,7 +34,6 @@ ImageBlobDevice::ImageBlobDevice(sqlite3 *db, QObject *parent) :
|
|||
mDb(db),
|
||||
mBlob(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ImageBlobDevice::~ImageBlobDevice()
|
||||
|
@ -26,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;
|
||||
|
@ -69,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));
|
||||
|
@ -108,7 +125,7 @@ bool ImageBlobDevice::open(QIODevice::OpenMode mode)
|
|||
|
||||
void ImageBlobDevice::close()
|
||||
{
|
||||
if(mBlob)
|
||||
if (mBlob)
|
||||
{
|
||||
sqlite3_blob_close(mBlob);
|
||||
mBlob = nullptr;
|
||||
|
@ -125,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));
|
||||
|
@ -148,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;
|
||||
|
@ -195,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());
|
||||
|
@ -203,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);
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef IMAGEBLOBDEVICE_H
|
||||
#define IMAGEBLOBDEVICE_H
|
||||
|
||||
|
@ -8,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
|
||||
|
@ -19,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);
|
||||
|
||||
|
@ -42,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
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "meetinginformationdialog.h"
|
||||
#include "ui_meetinginformationdialog.h"
|
||||
|
||||
|
@ -28,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);
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +88,7 @@ bool MeetingInformationDialog::loadData()
|
|||
{
|
||||
MetaDataManager *meta = Session->getMetaDataManager();
|
||||
|
||||
qint64 tmp = 0;
|
||||
qint64 tmp = 0;
|
||||
QDate date;
|
||||
|
||||
switch (meta->getInt64(tmp, MetaDataKey::MeetingStartDate))
|
||||
|
@ -74,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();
|
||||
}
|
||||
|
@ -107,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;
|
||||
|
@ -126,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());
|
||||
|
@ -151,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);
|
||||
}
|
||||
|
@ -170,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();
|
||||
}
|
||||
}
|
||||
|
@ -184,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();
|
||||
}
|
||||
}
|
||||
|
@ -211,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));
|
||||
|
@ -227,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."));
|
||||
|
@ -256,7 +291,7 @@ void MeetingInformationDialog::importImage()
|
|||
return;
|
||||
}
|
||||
|
||||
img = image;
|
||||
img = image;
|
||||
needsToSaveImg = true;
|
||||
}
|
||||
}
|
||||
|
@ -265,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MEETINGINFORMATIONDIALOG_H
|
||||
#define MEETINGINFORMATIONDIALOG_H
|
||||
|
||||
|
|
|
@ -1,30 +1,49 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "metadatamanager.h"
|
||||
|
||||
#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;
|
||||
|
@ -32,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;
|
||||
}
|
||||
|
@ -49,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;
|
||||
|
@ -69,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;
|
||||
}
|
||||
|
@ -94,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;
|
||||
|
@ -124,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;
|
||||
}
|
||||
|
@ -137,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;
|
||||
|
@ -157,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;
|
||||
}
|
||||
|
@ -184,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;
|
||||
|
@ -204,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;
|
||||
|
@ -218,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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef METADATAMANAGER_H
|
||||
#define METADATAMANAGER_H
|
||||
|
||||
|
@ -8,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
|
||||
{
|
||||
|
@ -52,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);
|
||||
|
|
|
@ -4,6 +4,5 @@ add_subdirectory(view)
|
|||
set(MR_TIMETABLE_PLANNER_SOURCES
|
||||
${MR_TIMETABLE_PLANNER_SOURCES}
|
||||
graph/linegraphtypes.h
|
||||
graph/linegraphtypes.cpp
|
||||
PARENT_SCOPE
|
||||
graph/linegraphtypes.cpp PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphtypes.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
@ -9,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)]);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHTYPES_H
|
||||
#define LINEGRAPHTYPES_H
|
||||
|
||||
|
@ -8,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
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphmanager.h"
|
||||
|
||||
#include "linegraphscene.h"
|
||||
|
@ -6,37 +25,66 @@
|
|||
#include "app/session.h"
|
||||
#include "viewmanager/viewmanager.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include "utils/worker_event_types.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
typedef LineGraphScene::PendingUpdate PendingUpdate;
|
||||
|
||||
LineGraphManager::LineGraphManager(QObject *parent) :
|
||||
QObject(parent),
|
||||
activeScene(nullptr),
|
||||
m_followJobOnGraphChange(false)
|
||||
m_followJobOnGraphChange(false),
|
||||
m_hasScheduledUpdate(false)
|
||||
{
|
||||
auto session = Session;
|
||||
//Stations
|
||||
connect(session, &MeetingSession::stationNameChanged, this, &LineGraphManager::onStationNameChanged);
|
||||
connect(session, &MeetingSession::stationPlanChanged, this, &LineGraphManager::onStationPlanChanged);
|
||||
// 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))
|
||||
{
|
||||
ev->accept();
|
||||
processPendingUpdates();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QObject::event(ev);
|
||||
}
|
||||
|
||||
void LineGraphManager::registerScene(LineGraphScene *scene)
|
||||
{
|
||||
Q_ASSERT(!scenes.contains(scene));
|
||||
|
@ -47,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);
|
||||
}
|
||||
}
|
||||
|
@ -68,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);
|
||||
}
|
||||
|
@ -86,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);
|
||||
}
|
||||
}
|
||||
|
@ -96,37 +144,92 @@ 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
|
||||
|
||||
// Mark as scheduled and post event to ourself
|
||||
m_hasScheduledUpdate = true;
|
||||
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
|
||||
m_hasScheduledUpdate = false;
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
if (timer.elapsed() > MAX_UPDATE_TIME_MS)
|
||||
{
|
||||
// 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::FullReload))
|
||||
{
|
||||
scene->reload();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::ReloadJobs))
|
||||
{
|
||||
scene->reloadJobs();
|
||||
}
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::ReloadStationNames))
|
||||
{
|
||||
scene->updateStationNames();
|
||||
}
|
||||
|
||||
// Manually cleare pending update and trigger redraw
|
||||
scene->pendingUpdate = PendingUpdate::NothingToDo;
|
||||
emit scene->redrawGraph();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -140,60 +243,62 @@ void LineGraphManager::onSceneDestroyed(QObject *obj)
|
|||
unregisterScene(scene);
|
||||
}
|
||||
|
||||
void LineGraphManager::onGraphChanged(int graphType_, db_id graphObjId, LineGraphScene *scene)
|
||||
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);
|
||||
|
@ -203,143 +308,301 @@ void LineGraphManager::onJobSelected(db_id jobId, int category, db_id stopId)
|
|||
|
||||
void LineGraphManager::onStationNameChanged(db_id stationId)
|
||||
{
|
||||
onStationPlanChanged({stationId}); //FIXME: update only labels
|
||||
}
|
||||
bool found = false;
|
||||
|
||||
void LineGraphManager::onStationPlanChanged(const QSet<db_id>& stationIds)
|
||||
{
|
||||
//FIXME: speed up with threads???
|
||||
for(LineGraphScene *scene : qAsConst(scenes))
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
for(db_id stationId : stationIds)
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
|
||||
continue; // Already flagged
|
||||
|
||||
if (scene->stations.contains(stationId))
|
||||
{
|
||||
if(scene->stations.contains(stationId))
|
||||
{
|
||||
scene->reload();
|
||||
break;
|
||||
}
|
||||
scene->pendingUpdate.setFlag(PendingUpdate::ReloadStationNames);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
void LineGraphManager::onStationJobPlanChanged(const QSet<db_id> &stationIds)
|
||||
{
|
||||
onStationPlanChanged_internal(stationIds, int(PendingUpdate::ReloadJobs));
|
||||
}
|
||||
|
||||
void LineGraphManager::onStationTrackPlanChanged(const QSet<db_id> &stationIds)
|
||||
{
|
||||
onStationPlanChanged_internal(stationIds, int(PendingUpdate::FullReload));
|
||||
}
|
||||
|
||||
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
|
||||
clearGraphsOfObject(stationId, LineGraphType::SingleStation);
|
||||
}
|
||||
|
||||
void LineGraphManager::onSegmentNameChanged(db_id segmentId)
|
||||
{
|
||||
onSegmentStationsChanged(segmentId); //FIXME: update only labels
|
||||
QString segName;
|
||||
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
|
||||
continue; // Already flagged
|
||||
|
||||
if (scene->graphType == LineGraphType::RailwaySegment && scene->graphObjectId == segmentId)
|
||||
{
|
||||
if (segName.isEmpty())
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
qWarning() << "Graph: invalid segment ID" << segmentId;
|
||||
return;
|
||||
}
|
||||
|
||||
// Store segment name
|
||||
segName = q.getRows().get<QString>(0);
|
||||
}
|
||||
|
||||
scene->graphObjectName = segName;
|
||||
emit scene->graphChanged(int(scene->graphType), scene->graphObjectId, scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LineGraphManager::onSegmentStationsChanged(db_id segmentId)
|
||||
{
|
||||
//FIXME: speed up with threads???
|
||||
for(LineGraphScene *scene : qAsConst(scenes))
|
||||
bool found = false;
|
||||
|
||||
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
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
|
||||
continue; // Already flagged
|
||||
|
||||
if (scene->graphType == LineGraphType::RailwayLine)
|
||||
{
|
||||
for (const auto &stPos : qAsConst(scene->stationPositions))
|
||||
{
|
||||
if (stPos.segmentId == segmentId)
|
||||
{
|
||||
scene->pendingUpdate.setFlag(PendingUpdate::FullReload);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (scene->graphType == LineGraphType::RailwaySegment
|
||||
&& scene->graphObjectId == segmentId)
|
||||
{
|
||||
scene->pendingUpdate.setFlag(PendingUpdate::FullReload);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
clearGraphsOfObject(segmentId, LineGraphType::RailwaySegment);
|
||||
}
|
||||
|
||||
void LineGraphManager::onLineNameChanged(db_id lineId)
|
||||
{
|
||||
onLineSegmentsChanged(lineId); //FIXME: update only labels
|
||||
QString lineName;
|
||||
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
|
||||
continue; // Already flagged
|
||||
|
||||
if (scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
|
||||
{
|
||||
if (lineName.isEmpty())
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
qWarning() << "Graph: invalid line ID" << lineId;
|
||||
return;
|
||||
}
|
||||
|
||||
// Store line name
|
||||
lineName = q.getRows().get<QString>(0);
|
||||
}
|
||||
|
||||
scene->graphObjectName = lineName;
|
||||
emit scene->graphChanged(int(scene->graphType), scene->graphObjectId, scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LineGraphManager::onLineSegmentsChanged(db_id lineId)
|
||||
{
|
||||
//FIXME: speed up with threads???
|
||||
for(LineGraphScene *scene : qAsConst(scenes))
|
||||
bool found = false;
|
||||
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
if(scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
|
||||
scene->reload();
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
|
||||
continue; // Already flagged
|
||||
|
||||
if (scene->graphType == LineGraphType::RailwayLine && scene->graphObjectId == lineId)
|
||||
{
|
||||
scene->pendingUpdate.setFlag(PendingUpdate::FullReload);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
void LineGraphManager::onLineRemoved(db_id lineId)
|
||||
{
|
||||
// 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 all scenes which had it selected
|
||||
// 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)
|
||||
if (activeScene && AppSettings.getSyncSelectionOnAllGraphs())
|
||||
{
|
||||
// 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);
|
||||
activeScene->reloadJobs();
|
||||
}
|
||||
}
|
||||
|
||||
for(LineGraphScene *scene : qAsConst(scenes))
|
||||
else
|
||||
{
|
||||
JobStopEntry oldSelectedJob = scene->getSelectedJob();
|
||||
if(oldSelectedJob.jobId == oldJobId)
|
||||
// Manually update all scenes
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
scene->setSelectedJob(selectedJob);
|
||||
JobStopEntry oldSelectedJob = scene->getSelectedJob();
|
||||
if (oldSelectedJob.jobId == oldJobId)
|
||||
{
|
||||
scene->setSelectedJob(selectedJob);
|
||||
}
|
||||
}
|
||||
|
||||
if(scene->getSelectedJob().jobId == selectedJob.jobId)
|
||||
scene->reloadJobs();
|
||||
}
|
||||
}
|
||||
|
||||
void LineGraphManager::onJobRemoved(db_id 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
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload)
|
||||
|| scene->pendingUpdate.testFlag(PendingUpdate(PendingUpdate::ReloadJobs)))
|
||||
continue; // Already flagged
|
||||
|
||||
scene->pendingUpdate.setFlag(PendingUpdate(PendingUpdate::ReloadJobs));
|
||||
found = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LineGraphManager::onStationPlanChanged_internal(const QSet<db_id> &stationIds, int flag)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
for (LineGraphScene *scene : qAsConst(scenes))
|
||||
{
|
||||
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload)
|
||||
|| scene->pendingUpdate.testFlag(PendingUpdate(flag)))
|
||||
continue; // Already flagged
|
||||
|
||||
for (db_id stationId : stationIds)
|
||||
{
|
||||
if (scene->stations.contains(stationId))
|
||||
{
|
||||
scene->pendingUpdate.setFlag(PendingUpdate(flag));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHMANAGER_H
|
||||
#define LINEGRAPHMANAGER_H
|
||||
|
||||
|
@ -26,6 +45,13 @@ class LineGraphManager : public QObject
|
|||
public:
|
||||
explicit LineGraphManager(QObject *parent = nullptr);
|
||||
|
||||
/*!
|
||||
* \brief react to line graph update events
|
||||
*
|
||||
* \sa processPendingUpdates()
|
||||
*/
|
||||
bool event(QEvent *ev) override;
|
||||
|
||||
/*!
|
||||
* \brief subscribe scene to notifications
|
||||
*
|
||||
|
@ -56,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
|
||||
|
@ -67,6 +96,27 @@ public:
|
|||
*/
|
||||
JobStopEntry getCurrentSelectedJob() const;
|
||||
|
||||
/*!
|
||||
* \brief schedule async update
|
||||
*
|
||||
* If an update is already schedule, does nothing.
|
||||
* Otherwise posts an event to self so it will update
|
||||
* when Qt event loop is not busy
|
||||
*
|
||||
* \sa processPendingUpdates()
|
||||
*/
|
||||
void scheduleUpdate();
|
||||
|
||||
/*!
|
||||
* \brief process pending updates
|
||||
*
|
||||
* Updates all scenes marked for update
|
||||
*
|
||||
* \sa scheduleUpdate()
|
||||
* \sa event()
|
||||
*/
|
||||
void processPendingUpdates();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Active scene is changed
|
||||
|
@ -104,37 +154,43 @@ 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 onStationPlanChanged(const QSet<db_id> &stationIds);
|
||||
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:
|
||||
void onStationPlanChanged_internal(const QSet<db_id> &stationIds, int flag);
|
||||
|
||||
private:
|
||||
QVector<LineGraphScene *> scenes;
|
||||
LineGraphScene *activeScene;
|
||||
JobStopEntry lastSelectedJob;
|
||||
bool m_followJobOnGraphChange;
|
||||
bool m_hasScheduledUpdate;
|
||||
};
|
||||
|
||||
#endif // LINEGRAPHMANAGER_H
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHSCENE_H
|
||||
#define LINEGRAPHSCENE_H
|
||||
|
||||
|
@ -31,11 +50,25 @@ class LineGraphScene : public IGraphScene
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/*!
|
||||
* \brief Enum to describe pending update needed
|
||||
*
|
||||
* FIXME: allow to specify segments to update
|
||||
*/
|
||||
enum class PendingUpdate
|
||||
{
|
||||
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
|
||||
};
|
||||
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
|
||||
|
@ -76,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
|
||||
{
|
||||
|
@ -121,7 +154,10 @@ public:
|
|||
*
|
||||
* \sa setDrawSelection()
|
||||
*/
|
||||
inline bool getDrawSelection() { return m_drawSelection; }
|
||||
inline bool getDrawSelection()
|
||||
{
|
||||
return m_drawSelection;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief setDrawSelection
|
||||
|
@ -132,7 +168,10 @@ public:
|
|||
*
|
||||
* \sa getDrawSelection()
|
||||
*/
|
||||
inline void setDrawSelection(bool val) { m_drawSelection = val; }
|
||||
inline void setDrawSelection(bool val)
|
||||
{
|
||||
m_drawSelection = val;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief requestShowZone
|
||||
|
@ -175,13 +214,12 @@ public slots:
|
|||
void reload();
|
||||
|
||||
private:
|
||||
|
||||
/*!
|
||||
* \brief Graph of the job while is moving
|
||||
*
|
||||
* Contains informations to draw job line between two adjacent stations
|
||||
*/
|
||||
typedef struct
|
||||
struct JobSegmentGraph
|
||||
{
|
||||
db_id jobId;
|
||||
JobCategory category;
|
||||
|
@ -193,14 +231,14 @@ private:
|
|||
db_id toStopId;
|
||||
db_id toPlatfId;
|
||||
QPointF toArrival;
|
||||
} JobSegmentGraph;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Station entry on scene
|
||||
*
|
||||
* Represents a station item placeholder in an ordered list of scene
|
||||
*/
|
||||
typedef struct
|
||||
struct StationPosEntry
|
||||
{
|
||||
db_id stationId;
|
||||
db_id segmentId;
|
||||
|
@ -211,7 +249,7 @@ private:
|
|||
* Stores job graph of the next segment
|
||||
* Which means jobs departing from this staation and going to next one
|
||||
*/
|
||||
} StationPosEntry;
|
||||
};
|
||||
|
||||
private:
|
||||
/*!
|
||||
|
@ -245,6 +283,14 @@ private:
|
|||
*/
|
||||
bool loadStation(StationGraphObject &st, QString &outFullName);
|
||||
|
||||
/*!
|
||||
* \brief updateStationNames
|
||||
*
|
||||
* Update names of already loaded stations
|
||||
* If graph type is SingleStation, graph name will be update too
|
||||
*/
|
||||
bool updateStationNames();
|
||||
|
||||
/*!
|
||||
* \brief Load all stations in a railway line
|
||||
*
|
||||
|
@ -267,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
|
||||
|
@ -283,7 +330,7 @@ private:
|
|||
friend class BackgroundHelper;
|
||||
friend class LineGraphManager;
|
||||
|
||||
sqlite3pp::database& mDb;
|
||||
sqlite3pp::database &mDb;
|
||||
|
||||
/*!
|
||||
* \brief Graph Object ID
|
||||
|
@ -317,6 +364,8 @@ private:
|
|||
JobStopEntry selectedJob;
|
||||
|
||||
bool m_drawSelection;
|
||||
|
||||
PendingUpdateFlags pendingUpdate;
|
||||
};
|
||||
|
||||
#endif // LINEGRAPHSCENE_H
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphselectionhelper.h"
|
||||
|
||||
#include "linegraphscene.h"
|
||||
|
@ -8,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);
|
||||
|
||||
|
@ -53,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;
|
||||
}
|
||||
}
|
||||
|
@ -61,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);
|
||||
|
@ -80,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;
|
||||
|
@ -177,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"
|
||||
|
@ -244,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"
|
||||
|
@ -316,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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHSELECTIONHELPER_H
|
||||
#define LINEGRAPHSELECTIONHELPER_H
|
||||
|
||||
|
@ -14,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
|
||||
|
@ -37,7 +55,7 @@ public:
|
|||
|
||||
LineGraphSelectionHelper(sqlite3pp::database &db);
|
||||
|
||||
//Low level API
|
||||
// Low level API
|
||||
|
||||
/*!
|
||||
* \brief find job in current graph
|
||||
|
@ -50,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
|
||||
|
@ -74,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
|
||||
|
|
|
@ -1,6 +1,24 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stationgraphobject.h"
|
||||
|
||||
StationGraphObject::StationGraphObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STATIONGRAPHOBJECT_H
|
||||
#define STATIONGRAPHOBJECT_H
|
||||
|
||||
|
|
|
@ -1,8 +1,27 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backgroundhelper.h"
|
||||
|
||||
#include "app/session.h"
|
||||
|
||||
#include "graph/model/linegraphscene.h"
|
||||
#include "graph/model/linegraphscene.h"
|
||||
#include "utils/jobcategorystrings.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
@ -12,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);
|
||||
|
||||
|
@ -26,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;
|
||||
|
@ -41,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);
|
||||
}
|
||||
|
@ -52,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;
|
||||
|
@ -92,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);
|
||||
|
@ -113,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);
|
||||
|
@ -141,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);
|
||||
|
@ -172,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);
|
||||
|
@ -213,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;
|
||||
|
@ -230,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);
|
||||
|
@ -247,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);
|
||||
|
@ -296,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);
|
||||
|
@ -319,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;
|
||||
|
||||
|
@ -338,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);
|
||||
|
@ -352,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);
|
||||
|
@ -404,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKGROUNDHELPER_H
|
||||
#define BACKGROUNDHELPER_H
|
||||
|
||||
|
@ -17,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
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphselectionwidget.h"
|
||||
|
||||
#include <QComboBox>
|
||||
|
@ -30,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;
|
||||
|
@ -58,7 +79,7 @@ LineGraphType LineGraphSelectionWidget::getGraphType() const
|
|||
|
||||
void LineGraphSelectionWidget::setGraphType(LineGraphType type)
|
||||
{
|
||||
if(getGraphType() == type)
|
||||
if (getGraphType() == type)
|
||||
return;
|
||||
|
||||
graphTypeCombo->setCurrentIndex(int(type));
|
||||
|
@ -79,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);
|
||||
}
|
||||
|
||||
|
@ -100,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);
|
||||
|
@ -108,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);
|
||||
|
@ -128,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;
|
||||
}
|
||||
|
@ -149,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHSELECTIONWIDGET_H
|
||||
#define LINEGRAPHSELECTIONWIDGET_H
|
||||
|
||||
|
@ -32,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);
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphtoolbar.h"
|
||||
|
||||
#include "graph/model/linegraphscene.h"
|
||||
|
@ -30,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"));
|
||||
|
@ -51,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);
|
||||
}
|
||||
|
||||
|
@ -111,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);
|
||||
|
@ -127,7 +149,7 @@ void LineGraphToolbar::resetToolbarToScene()
|
|||
|
||||
void LineGraphToolbar::updateZoomLevel(int zoom)
|
||||
{
|
||||
if(mZoom == zoom)
|
||||
if (mZoom == zoom)
|
||||
return;
|
||||
|
||||
mZoom = zoom;
|
||||
|
@ -141,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);
|
||||
}
|
||||
|
||||
|
@ -156,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);
|
||||
}
|
||||
|
@ -164,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);
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHTOOLBAR_H
|
||||
#define LINEGRAPHTOOLBAR_H
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphview.h"
|
||||
|
||||
#include "graph/model/linegraphscene.h"
|
||||
|
@ -12,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();
|
||||
}
|
||||
|
||||
|
@ -52,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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHVIEW_H
|
||||
#define LINEGRAPHVIEW_H
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "linegraphwidget.h"
|
||||
|
||||
#include "graph/model/linegraphscene.h"
|
||||
|
@ -17,7 +36,7 @@ LineGraphWidget::LineGraphWidget(QWidget *parent) :
|
|||
{
|
||||
QVBoxLayout *lay = new QVBoxLayout(this);
|
||||
|
||||
toolBar = new LineGraphToolbar(this);
|
||||
toolBar = new LineGraphToolbar(this);
|
||||
|
||||
lay->addWidget(toolBar);
|
||||
|
||||
|
@ -26,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);
|
||||
|
@ -40,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);
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LINEGRAPHWIDGET_H
|
||||
#define LINEGRAPHWIDGET_H
|
||||
|
||||
|
@ -27,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);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
add_subdirectory(jobeditor)
|
||||
add_subdirectory(jobs_checker)
|
||||
add_subdirectory(jobsmanager)
|
||||
|
||||
set(MR_TIMETABLE_PLANNER_SOURCES
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "editstopdialog.h"
|
||||
#include "ui_editstopdialog.h"
|
||||
|
||||
|
@ -11,14 +30,15 @@
|
|||
#include "rscoupledialog.h"
|
||||
#include "model/rscouplinginterface.h"
|
||||
|
||||
#include "model/rsproxymodel.h"
|
||||
#include "model/stopcouplingmodel.h"
|
||||
#include "model/trainassetmodel.h"
|
||||
#include "model/jobpassingsmodel.h"
|
||||
#include "jobs/jobsmanager/model/jobshelper.h"
|
||||
#include "jobs/jobsmanager/model/jobmatchmodel.h"
|
||||
|
||||
#include "utils/delegates/sql/modelpageswitcher.h"
|
||||
#include "utils/delegates/sql/customcompletionlineedit.h"
|
||||
#include "utils/delegates/sql/chooseitemdlg.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
|
@ -28,7 +48,6 @@
|
|||
|
||||
#include "app/scopedebug.h"
|
||||
|
||||
|
||||
EditStopDialog::EditStopDialog(StopModel *m, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::EditStopDialog),
|
||||
|
@ -37,10 +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);
|
||||
// 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();
|
||||
|
@ -50,48 +72,53 @@ 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);
|
||||
|
||||
connect(ui->importJobRsBut, &QPushButton::clicked, this, &EditStopDialog::importJobRS);
|
||||
connect(ui->editCoupledBut, &QPushButton::clicked, this, &EditStopDialog::editCoupled);
|
||||
connect(ui->editUncoupledBut, &QPushButton::clicked, this, &EditStopDialog::editUncoupled);
|
||||
|
||||
ui->coupledView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
ui->uncoupledView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->coupledView, &QAbstractItemView::customContextMenuRequested, this, &EditStopDialog::couplingCustomContextMenuRequested);
|
||||
connect(ui->uncoupledView, &QAbstractItemView::customContextMenuRequested, this, &EditStopDialog::couplingCustomContextMenuRequested);
|
||||
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);
|
||||
|
||||
|
@ -100,11 +127,16 @@ 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."));
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
|
@ -123,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)
|
||||
|
@ -146,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);
|
||||
|
@ -226,50 +259,101 @@ 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);
|
||||
}
|
||||
|
||||
couplingMgr->loadCouplings(stopModel, curStop.stopId, m_jobId, curStop.arrival);
|
||||
|
||||
updateAdditionalNotes();
|
||||
}
|
||||
|
||||
void EditStopDialog::saveDataToModel()
|
||||
{
|
||||
DEBUG_ENTRY;
|
||||
|
||||
const StopItem& curStop = helper->getCurItem();
|
||||
const StopItem& prevStop = helper->getPrevItem();
|
||||
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());
|
||||
}
|
||||
|
||||
stopModel->setStopInfo(stopIdx, curStop, prevStop.nextSegment);
|
||||
bool avoidTimeRecalc = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
||||
stopModel->setStopInfo(stopIdx, curStop, prevStop.nextSegment, avoidTimeRecalc);
|
||||
}
|
||||
|
||||
void EditStopDialog::importJobRS()
|
||||
{
|
||||
const StopItem &curStop = helper->getCurItem();
|
||||
if (!curStop.stationId)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Import Error"),
|
||||
tr("In order to import rollingstock from other<br>"
|
||||
"Jobs stopping in the <b>same station</b> as current one, "
|
||||
"you must first set a valid station for this stop."));
|
||||
return;
|
||||
}
|
||||
|
||||
JobMatchModel jobsMatch(Session->m_Db);
|
||||
jobsMatch.setDefaultId(JobMatchModel::StopId);
|
||||
jobsMatch.setFilter(m_jobId, curStop.stationId, curStop.departure);
|
||||
|
||||
QString stName = helper->getStationEdit()->text();
|
||||
|
||||
OwningQPointer<ChooseItemDlg> dlg = new ChooseItemDlg(&jobsMatch, this);
|
||||
dlg->setDescription(
|
||||
tr("Please choose a Job among the ones stopping at <b>%1</b> before <b>%2</b>.<br>"
|
||||
"All rollingstock uncoupled by selected Job at current station<br>"
|
||||
"will be coupled to current Job.")
|
||||
.arg(stName, curStop.departure.toString("HH:mm")));
|
||||
dlg->setPlaceholder(tr("Job number without category"));
|
||||
|
||||
// Select model
|
||||
int ret = dlg->exec();
|
||||
if (ret != QDialog::Accepted || !dlg)
|
||||
return;
|
||||
|
||||
db_id otherJobStopId = dlg->getItemId();
|
||||
if (!otherJobStopId)
|
||||
return;
|
||||
|
||||
// Import rollingstock
|
||||
int count = couplingMgr->importRSFromJob(otherJobStopId);
|
||||
|
||||
// Refresh views
|
||||
coupledModel->refreshData(true);
|
||||
trainAssetModelAfter->refreshData(true);
|
||||
|
||||
// Tell user it's completed
|
||||
QMessageBox::information(
|
||||
this, tr("Importation Finished"),
|
||||
tr("<b>%1</b> rollingstock items were successfully imported").arg(count));
|
||||
}
|
||||
|
||||
void EditStopDialog::editCoupled()
|
||||
{
|
||||
const StopItem& curStop = helper->getCurItem();
|
||||
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();
|
||||
|
||||
|
@ -277,17 +361,17 @@ void EditStopDialog::editCoupled()
|
|||
trainAssetModelAfter->refreshData(true);
|
||||
}
|
||||
|
||||
|
||||
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();
|
||||
|
||||
|
@ -305,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);
|
||||
|
@ -327,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();
|
||||
|
@ -359,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);
|
||||
|
@ -381,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)"
|
||||
|
@ -392,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);
|
||||
|
@ -405,9 +491,62 @@ int EditStopDialog::getTrainSpeedKmH(bool afterStop)
|
|||
return q.getRows().get<int>(0);
|
||||
}
|
||||
|
||||
void EditStopDialog::updateAdditionalNotes()
|
||||
{
|
||||
const StopItem &curStop = helper->getCurItem();
|
||||
|
||||
QString msg;
|
||||
|
||||
// Check direction
|
||||
if (curStop.fromGate.gateConnId && curStop.toGate.gateConnId && curStop.type != StopType::First
|
||||
&& curStop.type != StopType::Last)
|
||||
{
|
||||
// Ignore First and Last stop (sometimes they have fake in/out gates set which might trigger
|
||||
// this message) Both entry and exit path are set, check direction
|
||||
if (curStop.fromGate.stationTrackSide == curStop.toGate.stationTrackSide)
|
||||
{
|
||||
// Train leaves station track from same side of entrance
|
||||
msg = tr("Train reverses direction.");
|
||||
}
|
||||
}
|
||||
|
||||
// Check line traction
|
||||
if (curStop.type != StopType::Last && curStop.nextSegment.segmentId)
|
||||
{
|
||||
// Last has no next segment so do not show traction type
|
||||
|
||||
bool nextSegmentElectrified = stopModel->isRailwayElectrifiedAfterRow(stopIdx.row());
|
||||
bool prevSegmentElectrified = !nextSegmentElectrified; // Trigger change on First stop
|
||||
|
||||
if (curStop.type != StopType::First && stopIdx.row() >= 0)
|
||||
{
|
||||
// Get real previous railway type
|
||||
prevSegmentElectrified = stopModel->isRailwayElectrifiedAfterRow(stopIdx.row() - 1);
|
||||
}
|
||||
|
||||
if (!msg.isEmpty())
|
||||
msg.append("\n\n"); // Separate from previous message
|
||||
|
||||
if (nextSegmentElectrified)
|
||||
msg.append(tr("Electric traction is ALLOWED."));
|
||||
else
|
||||
msg.append(tr("Electric traction is NOT ALLOWED."));
|
||||
|
||||
if (nextSegmentElectrified != prevSegmentElectrified && curStop.type != StopType::First)
|
||||
{
|
||||
// Railway type changed
|
||||
msg.append('\n');
|
||||
msg.append(tr("(Different traction then previous line!)"));
|
||||
}
|
||||
}
|
||||
|
||||
ui->additionalNotesLabel->setText(msg);
|
||||
ui->additionalNotesBox->setVisible(!msg.isEmpty());
|
||||
}
|
||||
|
||||
void EditStopDialog::setReadOnly(bool value)
|
||||
{
|
||||
readOnly = value;
|
||||
readOnly = value;
|
||||
|
||||
CustomCompletionLineEdit *mStationEdit = helper->getStationEdit();
|
||||
CustomCompletionLineEdit *mStTrackEdit = helper->getStTrackEdit();
|
||||
|
@ -428,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
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef EDITSTOPDIALOG2_H
|
||||
#define EDITSTOPDIALOG2_H
|
||||
|
||||
|
@ -9,7 +28,7 @@
|
|||
class StopEditingHelper;
|
||||
|
||||
class StopModel;
|
||||
class StopItem;
|
||||
struct StopItem;
|
||||
|
||||
class TrainAssetModel;
|
||||
class RSCouplingInterface;
|
||||
|
@ -42,6 +61,7 @@ public slots:
|
|||
void done(int val) override;
|
||||
|
||||
private slots:
|
||||
void importJobRS();
|
||||
void editCoupled();
|
||||
void editUncoupled();
|
||||
|
||||
|
@ -49,6 +69,8 @@ private slots:
|
|||
|
||||
void couplingCustomContextMenuRequested(const QPoint &pos);
|
||||
|
||||
void updateAdditionalNotes();
|
||||
|
||||
private:
|
||||
void saveDataToModel();
|
||||
|
||||
|
@ -73,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;
|
||||
};
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
<item row="0" column="0">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="stopTab">
|
||||
<attribute name="title">
|
||||
|
@ -220,6 +220,25 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="additionalNotesBox">
|
||||
<property name="title">
|
||||
<string>Additional Notes</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="additionalNotesLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="rsTab">
|
||||
|
@ -227,40 +246,6 @@
|
|||
<string>Rollingstock</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="rsTabLay">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="coupledBox">
|
||||
<property name="title">
|
||||
<string>Coupled</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="coupledLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="coupledView"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editCoupledBut">
|
||||
<property name="text">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QGroupBox" name="uncoupledBox">
|
||||
<property name="title">
|
||||
|
@ -295,7 +280,7 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="rollingstockBox">
|
||||
<property name="title">
|
||||
<string>Job Asset</string>
|
||||
|
@ -306,7 +291,6 @@
|
|||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
@ -341,7 +325,6 @@
|
|||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
@ -352,7 +335,6 @@
|
|||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
@ -364,6 +346,47 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="coupledBox">
|
||||
<property name="title">
|
||||
<string>Coupled</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="coupledLayout">
|
||||
<property name="spacing">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListView" name="coupledView"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editCoupledBut">
|
||||
<property name="text">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QPushButton" name="importJobRsBut">
|
||||
<property name="text">
|
||||
<string>Import From Job</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="infoTab">
|
||||
|
@ -406,7 +429,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>837</width>
|
||||
<height>589</height>
|
||||
<height>581</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="infoScrollLay">
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "jobpatheditor.h"
|
||||
#include "ui_jobpatheditor.h"
|
||||
|
||||
|
@ -6,18 +25,12 @@
|
|||
|
||||
#include "viewmanager/viewmanager.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include "utils/files/recentdirstore.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include "utils/owningqpointer.h"
|
||||
|
||||
#include <QCloseEvent>
|
||||
|
||||
#include "model/stopmodel.h"
|
||||
#include "stopdelegate.h"
|
||||
#include "jobs/jobsmanager/model/jobshelper.h"
|
||||
|
||||
#include "model/nextprevrsjobsmodel.h"
|
||||
|
||||
#include "jobs/jobeditor/editstopdialog.h"
|
||||
|
||||
#include "utils/jobcategorystrings.h"
|
||||
|
@ -33,6 +46,14 @@
|
|||
#include "utils/delegates/sql/customcompletionlineedit.h"
|
||||
#include "shifts/shiftcombomodel.h"
|
||||
|
||||
#include "utils/owningqpointer.h"
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include "utils/files/recentdirstore.h"
|
||||
|
||||
#include <QCloseEvent>
|
||||
|
||||
JobPathEditor::JobPathEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::JobPathEditor),
|
||||
|
@ -46,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)));
|
||||
}
|
||||
|
@ -56,24 +77,42 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
|
|||
ui->categoryCombo->setCurrentIndex(-1);
|
||||
|
||||
ShiftComboModel *shiftComboModel = new ShiftComboModel(Session->m_Db, this);
|
||||
shiftCustomCombo = new CustomCompletionLineEdit(shiftComboModel);
|
||||
ui->verticalLayout->insertWidget(ui->verticalLayout->indexOf(ui->categoryCombo) + 1, shiftCustomCombo);
|
||||
shiftCombo = new CustomCompletionLineEdit(shiftComboModel);
|
||||
|
||||
// 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
|
||||
stopModel = new StopModel(Session->m_Db, this);
|
||||
connect(shiftCustomCombo, &CustomCompletionLineEdit::dataIdChanged, stopModel, &StopModel::setNewShiftId);
|
||||
ui->view->setModel(stopModel);
|
||||
connect(shiftCombo, &CustomCompletionLineEdit::dataIdChanged, stopModel,
|
||||
&StopModel::setNewShiftId);
|
||||
ui->stopsView->setModel(stopModel);
|
||||
|
||||
delegate = new StopDelegate(Session->m_Db, this);
|
||||
ui->view->setItemDelegateForColumn(0, delegate);
|
||||
ui->stopsView->setItemDelegateForColumn(0, delegate);
|
||||
|
||||
ui->view->setResizeMode(QListView::Adjust);
|
||||
ui->view->setMovement(QListView::Static);
|
||||
ui->view->setSelectionMode(QListView::ContiguousSelection);
|
||||
ui->stopsView->setResizeMode(QListView::Adjust);
|
||||
ui->stopsView->setMovement(QListView::Static);
|
||||
ui->stopsView->setSelectionMode(QListView::ContiguousSelection);
|
||||
|
||||
connect(ui->categoryCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), stopModel, &StopModel::setCategory);
|
||||
// Next/Prev Jobs
|
||||
prevJobsModel = new NextPrevRSJobsModel(Session->m_Db, this);
|
||||
prevJobsModel->setMode(NextPrevRSJobsModel::PrevJobs);
|
||||
ui->prevJobsView->setModel(prevJobsModel);
|
||||
|
||||
nextJobsModel = new NextPrevRSJobsModel(Session->m_Db, this);
|
||||
nextJobsModel->setMode(NextPrevRSJobsModel::NextJobs);
|
||||
ui->nextJobsView->setModel(nextJobsModel);
|
||||
|
||||
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);
|
||||
|
@ -83,9 +122,18 @@ JobPathEditor::JobPathEditor(QWidget *parent) :
|
|||
|
||||
connect(ui->sheetBut, &QPushButton::clicked, this, &JobPathEditor::onSaveSheet);
|
||||
|
||||
ui->view->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->view, &QListView::customContextMenuRequested, this, &JobPathEditor::showContextMenu);
|
||||
connect(ui->view, &QListView::clicked, this, &JobPathEditor::onIndexClicked);
|
||||
ui->stopsView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
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);
|
||||
|
||||
ui->nextJobsView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(ui->nextJobsView, &QListView::customContextMenuRequested, this,
|
||||
&JobPathEditor::showJobContextMenu);
|
||||
|
||||
connect(Session, &MeetingSession::jobRemoved, this, &JobPathEditor::onJobRemoved);
|
||||
connect(&AppSettings, &MRTPSettings::jobColorsChanged, this, &JobPathEditor::updateSpinColor);
|
||||
|
@ -104,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);
|
||||
|
@ -123,36 +171,52 @@ 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();
|
||||
|
||||
stopModel->loadJobStops(jobId); //Load from database
|
||||
// Load from database
|
||||
if (!stopModel->loadJobStops(jobId))
|
||||
{
|
||||
// Error: job could not be loaded, maybe invalid jobId
|
||||
clearJob();
|
||||
setEnabled(false);
|
||||
|
||||
//If read-only hide 'AddHere' row (last one)
|
||||
ui->view->setRowHidden(stopModel->rowCount() - 1, m_readOnly);
|
||||
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));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If read-only hide 'AddHere' row (last one)
|
||||
ui->stopsView->setRowHidden(stopModel->rowCount() - 1, m_readOnly);
|
||||
|
||||
prevJobsModel->setJobId(jobId);
|
||||
nextJobsModel->setJobId(jobId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -161,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,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::showContextMenu(const QPoint& pos)
|
||||
void JobPathEditor::showStopsContextMenu(const QPoint &pos)
|
||||
{
|
||||
QModelIndex index = ui->view->indexAt(pos);
|
||||
if(!index.isValid() || index.row()>= stopModel->rowCount() || stopModel->isAddHere(index))
|
||||
QModelIndex index = ui->stopsView->indexAt(pos);
|
||||
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"));
|
||||
|
@ -235,24 +300,24 @@ void JobPathEditor::showContextMenu(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->view->viewport()->mapToGlobal(pos));
|
||||
QAction *act = menu->exec(ui->stopsView->viewport()->mapToGlobal(pos));
|
||||
|
||||
QItemSelectionModel *sm = ui->view->selectionModel();
|
||||
QItemSelectionModel *sm = ui->stopsView->selectionModel();
|
||||
|
||||
QItemSelectionRange range;
|
||||
QItemSelection s = ui->view->selectionModel()->selection();
|
||||
if(s.count() > 0)
|
||||
QItemSelection s = ui->stopsView->selectionModel()->selection();
|
||||
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);
|
||||
|
@ -261,86 +326,140 @@ void JobPathEditor::showContextMenu(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)
|
||||
{
|
||||
QTableView *jobView = qobject_cast<QTableView *>(sender());
|
||||
NextPrevRSJobsModel *jobModel = nextJobsModel;
|
||||
|
||||
if (jobView == ui->prevJobsView)
|
||||
jobModel = prevJobsModel;
|
||||
|
||||
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"));
|
||||
menu->addSeparator();
|
||||
QAction *refreshViews = menu->addAction(tr("Refresh"));
|
||||
|
||||
// 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)
|
||||
{
|
||||
selectStop(item.stopId);
|
||||
}
|
||||
else if (act == goToJob)
|
||||
{
|
||||
if (isEdited()) // Prevent selecting other job before saving
|
||||
{
|
||||
if (!maybeSave())
|
||||
return;
|
||||
}
|
||||
Session->getViewManager()->requestJobSelection(item.otherJobId, true, true);
|
||||
}
|
||||
else if (act == showRSPlan)
|
||||
{
|
||||
Session->getViewManager()->requestRSInfo(item.rsId);
|
||||
}
|
||||
else if (act == refreshViews)
|
||||
{
|
||||
prevJobsModel->refreshData();
|
||||
nextJobsModel->refreshData();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
stopJobNumberTimer();
|
||||
|
||||
prevJobsModel->clearData();
|
||||
nextJobsModel->clearData();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -349,7 +468,7 @@ bool JobPathEditor::saveChanges()
|
|||
{
|
||||
DEBUG_IMPORTANT_ENTRY;
|
||||
|
||||
if(!canSetJob)
|
||||
if (!canSetJob)
|
||||
return false;
|
||||
canSetJob = false;
|
||||
|
||||
|
@ -360,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();
|
||||
|
@ -380,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);
|
||||
|
@ -405,20 +522,14 @@ bool JobPathEditor::saveChanges()
|
|||
}
|
||||
}
|
||||
|
||||
//Store before they are cleared on commitChanges()
|
||||
const auto stationsToUpdate = stopModel->getStationsToUpdate();
|
||||
const auto rsToUpdate = stopModel->getRsToUpdate();
|
||||
|
||||
stopModel->commitChanges();
|
||||
|
||||
//Update views
|
||||
emit Session->rollingStockPlanChanged(rsToUpdate);
|
||||
db_id newJobId = stopModel->getJobId();
|
||||
prevJobsModel->setJobId(newJobId);
|
||||
nextJobsModel->setJobId(newJobId);
|
||||
|
||||
//Update station views
|
||||
emit Session->stationPlanChanged(stationsToUpdate);
|
||||
|
||||
//When updating the path selection gets cleared so we restore it
|
||||
Session->getViewManager()->requestJobSelection(stopModel->getJobId(), true, true);
|
||||
// When updating the path selection gets cleared so we restore it
|
||||
Session->getViewManager()->requestJobSelection(newJobId, true, true);
|
||||
|
||||
canSetJob = true;
|
||||
return true;
|
||||
|
@ -428,46 +539,35 @@ void JobPathEditor::discardChanges()
|
|||
{
|
||||
DEBUG_ENTRY;
|
||||
|
||||
if(!canSetJob)
|
||||
if (!canSetJob)
|
||||
return;
|
||||
|
||||
canSetJob = false;
|
||||
|
||||
closeStopEditor(); //Close before rolling savepoint
|
||||
closeStopEditor(); // Close before rolling savepoint
|
||||
|
||||
stopJobNumberTimer();
|
||||
|
||||
//Save them before reverting changes
|
||||
QSet<db_id> rsToUpdate = stopModel->getRsToUpdate();
|
||||
QSet<db_id> stationsToUpdate = stopModel->getStationsToUpdate();
|
||||
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());
|
||||
clearJob();
|
||||
setEnabled(false);
|
||||
}
|
||||
|
||||
//After possible job deletion update views
|
||||
|
||||
//Update RS views
|
||||
emit Session->rollingStockPlanChanged(rsToUpdate);
|
||||
|
||||
//Update station views
|
||||
emit Session->stationPlanChanged(stationsToUpdate);
|
||||
}
|
||||
|
||||
db_id JobPathEditor::currentJobId() const
|
||||
|
@ -478,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:
|
||||
|
@ -498,7 +596,7 @@ bool JobPathEditor::maybeSave()
|
|||
|
||||
void JobPathEditor::updateSpinColor()
|
||||
{
|
||||
if(!isClear)
|
||||
if (!isClear)
|
||||
{
|
||||
QColor col = Session->colorForCat(stopModel->getCategory());
|
||||
setSpinColor(col);
|
||||
|
@ -507,7 +605,7 @@ void JobPathEditor::updateSpinColor()
|
|||
|
||||
void JobPathEditor::timerEvent(QTimerEvent *e)
|
||||
{
|
||||
if(e->timerId() == jobNumberTimerId)
|
||||
if (e->timerId() == jobNumberTimerId)
|
||||
{
|
||||
checkJobNumberValid();
|
||||
return;
|
||||
|
@ -518,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);
|
||||
}
|
||||
}
|
||||
|
@ -531,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);
|
||||
|
@ -546,17 +644,15 @@ void JobPathEditor::onCategoryChanged(int newCat)
|
|||
|
||||
void JobPathEditor::onJobShiftChanged(db_id shiftId)
|
||||
{
|
||||
shiftCustomCombo->setData(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);
|
||||
|
@ -571,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);
|
||||
}
|
||||
|
@ -590,29 +685,29 @@ void JobPathEditor::setEdited(bool val)
|
|||
|
||||
void JobPathEditor::setReadOnly(bool readOnly)
|
||||
{
|
||||
if(m_readOnly == readOnly)
|
||||
if (m_readOnly == readOnly)
|
||||
return;
|
||||
|
||||
m_readOnly = readOnly;
|
||||
|
||||
ui->jobIdSpin->setReadOnly(m_readOnly);
|
||||
ui->categoryCombo->setDisabled(m_readOnly);
|
||||
shiftCustomCombo->setDisabled(m_readOnly);
|
||||
shiftCombo->setDisabled(m_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)
|
||||
ui->view->setRowHidden(size - 1, m_readOnly);
|
||||
if (size > 0)
|
||||
ui->stopsView->setRowHidden(size - 1, m_readOnly);
|
||||
|
||||
if(m_readOnly)
|
||||
if (m_readOnly)
|
||||
{
|
||||
ui->view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
ui->stopsView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->view->setEditTriggers(QAbstractItemView::DoubleClicked);
|
||||
ui->stopsView->setEditTriggers(QAbstractItemView::DoubleClicked);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -620,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));
|
||||
|
@ -630,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);
|
||||
|
@ -647,14 +742,14 @@ void JobPathEditor::onSaveSheet()
|
|||
utils::OpenFileInFolderDlg::askUser(tr("Job Sheet Saved"), fileName, this);
|
||||
}
|
||||
|
||||
void JobPathEditor::onIndexClicked(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";
|
||||
|
||||
|
@ -662,30 +757,30 @@ void JobPathEditor::onIndexClicked(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->view->setCurrentIndex(prev);
|
||||
ui->view->scrollTo(prev);
|
||||
ui->view->edit(prev);
|
||||
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
|
||||
{
|
||||
QModelIndex lastStop = stopModel->index(row, 0);
|
||||
ui->view->setCurrentIndex(lastStop);
|
||||
ui->view->scrollTo(lastStop);
|
||||
ui->view->edit(lastStop);
|
||||
ui->stopsView->setCurrentIndex(lastStop);
|
||||
ui->stopsView->scrollTo(lastStop);
|
||||
ui->stopsView->edit(lastStop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -697,9 +792,9 @@ bool JobPathEditor::getCanSetJob() const
|
|||
|
||||
void JobPathEditor::closeStopEditor()
|
||||
{
|
||||
QModelIndex idx = ui->view->currentIndex();
|
||||
QWidget *ed = ui->view->indexWidget(idx);
|
||||
if(ed == nullptr)
|
||||
QModelIndex idx = ui->stopsView->currentIndex();
|
||||
QWidget *ed = ui->stopsView->indexWidget(idx);
|
||||
if (ed == nullptr)
|
||||
return;
|
||||
emit delegate->commitData(ed);
|
||||
emit delegate->closeEditor(ed);
|
||||
|
@ -707,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();
|
||||
|
@ -724,10 +819,10 @@ 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->view->setCurrentIndex(idx);
|
||||
ui->view->scrollTo(idx, QListView::EnsureVisible);
|
||||
ui->stopsView->setCurrentIndex(idx);
|
||||
ui->stopsView->scrollTo(idx, QListView::EnsureVisible);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,34 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef JOBPATHEDITOR_H
|
||||
#define JOBPATHEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <QSet>
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
#include "utils/types.h"
|
||||
|
||||
class StopDelegate;
|
||||
class CustomCompletionLineEdit;
|
||||
|
||||
class StopModel;
|
||||
class NextPrevRSJobsModel;
|
||||
|
||||
namespace Ui {
|
||||
class JobPathEditor;
|
||||
|
@ -32,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);
|
||||
|
@ -71,8 +87,11 @@ protected:
|
|||
private slots:
|
||||
void setEdited(bool val);
|
||||
|
||||
void showContextMenu(const QPoint &pos);
|
||||
void onIndexClicked(const QModelIndex &index);
|
||||
void showJobContextMenu(const QPoint &pos);
|
||||
void showStopsContextMenu(const QPoint &pos);
|
||||
|
||||
void onStopIndexClicked(const QModelIndex &index);
|
||||
|
||||
void onJobRemoved(db_id jobId);
|
||||
|
||||
void startJobNumberTimer();
|
||||
|
@ -93,17 +112,20 @@ private:
|
|||
private:
|
||||
Ui::JobPathEditor *ui;
|
||||
|
||||
CustomCompletionLineEdit *shiftCustomCombo;
|
||||
CustomCompletionLineEdit *shiftCombo;
|
||||
|
||||
StopModel *stopModel;
|
||||
StopDelegate *delegate;
|
||||
|
||||
NextPrevRSJobsModel *prevJobsModel;
|
||||
NextPrevRSJobsModel *nextJobsModel;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -6,35 +6,15 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>603</height>
|
||||
<width>350</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="jobIdSpin">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="categoryCombo">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
|
@ -49,17 +29,93 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="view"/>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="categoryLabel">
|
||||
<property name="text">
|
||||
<string>Category:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="numberLabel">
|
||||
<property name="text">
|
||||
<string>Number:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="jobIdSpin">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>99999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="splitterLay1">
|
||||
<item>
|
||||
<widget class="QLabel" name="stopsLabel">
|
||||
<property name="text">
|
||||
<string>Stops:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="stopsView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="prevJobsLabel">
|
||||
<property name="text">
|
||||
<string>Prev Jobs:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="prevJobsView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget">
|
||||
<layout class="QVBoxLayout" name="splitterLay3">
|
||||
<item>
|
||||
<widget class="QLabel" name="nextJobsLabel">
|
||||
<property name="text">
|
||||
<string>Next Jobs:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableView" name="nextJobsView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="sheetBut">
|
||||
<property name="text">
|
||||
<string>Save Sheet</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
set(MR_TIMETABLE_PLANNER_SOURCES
|
||||
${MR_TIMETABLE_PLANNER_SOURCES}
|
||||
|
||||
jobs/jobeditor/model/nextprevrsjobsmodel.h
|
||||
jobs/jobeditor/model/jobpassingsmodel.h
|
||||
jobs/jobeditor/model/rscouplinginterface.h
|
||||
jobs/jobeditor/model/rslistondemandmodel.h
|
||||
|
@ -9,6 +10,7 @@ set(MR_TIMETABLE_PLANNER_SOURCES
|
|||
jobs/jobeditor/model/trainassetmodel.h
|
||||
jobs/jobeditor/model/stopmodel.h
|
||||
|
||||
jobs/jobeditor/model/nextprevrsjobsmodel.cpp
|
||||
jobs/jobeditor/model/jobpassingsmodel.cpp
|
||||
jobs/jobeditor/model/rscouplinginterface.cpp
|
||||
jobs/jobeditor/model/rslistondemandmodel.cpp
|
||||
|
@ -16,5 +18,6 @@ set(MR_TIMETABLE_PLANNER_SOURCES
|
|||
jobs/jobeditor/model/stopcouplingmodel.cpp
|
||||
jobs/jobeditor/model/trainassetmodel.cpp
|
||||
jobs/jobeditor/model/stopmodel.cpp
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "jobpassingsmodel.h"
|
||||
|
||||
#include <QFont>
|
||||
|
@ -11,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)
|
||||
{
|
||||
|
@ -52,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:
|
||||
|
@ -78,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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef JOBPASSINGSMODEL_H
|
||||
#define JOBPASSINGSMODEL_H
|
||||
|
||||
|
@ -14,22 +33,23 @@ class JobPassingsModel : public QAbstractTableModel
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
typedef enum {
|
||||
enum Columns
|
||||
{
|
||||
JobNameCol = 0,
|
||||
ArrivalCol,
|
||||
DepartureCol,
|
||||
PlatformCol,
|
||||
NCols
|
||||
} Columns;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
struct Entry
|
||||
{
|
||||
db_id jobId;
|
||||
QTime arrival;
|
||||
QTime departure;
|
||||
QString platform;
|
||||
JobCategory category;
|
||||
} Entry;
|
||||
};
|
||||
|
||||
explicit JobPassingsModel(QObject *parent = nullptr);
|
||||
|
||||
|
@ -41,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;
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "nextprevrsjobsmodel.h"
|
||||
|
||||
#include "utils/jobcategorystrings.h"
|
||||
#include "utils/rs_utils.h"
|
||||
|
||||
#include <QFont>
|
||||
|
||||
#include <sqlite3pp/sqlite3pp.h>
|
||||
|
||||
NextPrevRSJobsModel::NextPrevRSJobsModel(sqlite3pp::database &db, QObject *parent) :
|
||||
QAbstractTableModel(parent),
|
||||
mDb(db)
|
||||
{
|
||||
}
|
||||
|
||||
QVariant NextPrevRSJobsModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
||||
{
|
||||
switch (section)
|
||||
{
|
||||
case JobIdCol:
|
||||
return tr("Job");
|
||||
case RsNameCol:
|
||||
return tr("RS item");
|
||||
case TimeCol:
|
||||
return tr("Time");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return QAbstractTableModel::headerData(section, orientation, role);
|
||||
}
|
||||
|
||||
int NextPrevRSJobsModel::rowCount(const QModelIndex &p) const
|
||||
{
|
||||
return p.isValid() ? 0 : m_data.size();
|
||||
}
|
||||
|
||||
int NextPrevRSJobsModel::columnCount(const QModelIndex &p) const
|
||||
{
|
||||
return p.isValid() ? 0 : NCols;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
switch (role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
switch (idx.column())
|
||||
{
|
||||
case JobIdCol:
|
||||
{
|
||||
if (item.otherJobCat == JobCategory::NCategories)
|
||||
return tr("Depot"); // Rollingstock item taken from/released to depot
|
||||
return JobCategoryName::jobName(item.otherJobId, item.otherJobCat);
|
||||
}
|
||||
case RsNameCol:
|
||||
return item.rsName;
|
||||
case TimeCol:
|
||||
return item.opTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt::FontRole:
|
||||
{
|
||||
if (idx.column() == JobIdCol && item.otherJobCat == JobCategory::NCategories)
|
||||
{
|
||||
// Rollingstock item taken from/released to depot, distinguish font from job names
|
||||
QFont f;
|
||||
f.setItalic(true);
|
||||
return f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
if (m_mode == NextJobs)
|
||||
{
|
||||
sql.replace("%time0", "arrival");
|
||||
sql.replace("%time1", "departure");
|
||||
sql.replace("%min", "MIN");
|
||||
sql.replace("%max", "MAX");
|
||||
sql.replace("%rem", "0");
|
||||
sql.replace("%add", "1");
|
||||
sql.replace("%gt", ">=");
|
||||
sql.replace("%lt", "<=");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invert all
|
||||
sql.replace("%time0", "departure");
|
||||
sql.replace("%time1", "arrival");
|
||||
sql.replace("%min", "MAX");
|
||||
sql.replace("%max", "MIN");
|
||||
sql.replace("%rem", "1");
|
||||
sql.replace("%add", "0");
|
||||
sql.replace("%gt", "<=");
|
||||
sql.replace("%lt", ">=");
|
||||
}
|
||||
|
||||
/*
|
||||
//GROUP by next job_id (NOTE: added COUNT(1) column)
|
||||
QByteArray sql1 = "SELECT c.id, c.rs_id, c.operation, rs_models.name, rs_list.number,"
|
||||
" stops.arrival, MIN(s1.arrival), s1.job_id, c1.operation, COUNT(1)"
|
||||
" 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=16816 AND c.operation=0 AND c1.operation=1"
|
||||
" AND s1.arrival >= stops.departure"
|
||||
" GROUP BY s1.job_id"
|
||||
" UNION ALL "
|
||||
"SELECT *, COUNT(1) FROM (" //TODO: better way than sub query?
|
||||
" SELECT c.id, c.rs_id, c.operation, rs_models.name, rs_list.number,"
|
||||
" stops.arrival, MAX(s1.arrival), NULL, c1.operation, COUNT(1)"
|
||||
" 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=16816 AND c.operation=0 AND c1.operation=1"
|
||||
" GROUP BY c.rs_id"
|
||||
" HAVING s1.departure <= stops.arrival"
|
||||
")";
|
||||
*/
|
||||
|
||||
sqlite3pp::query q(mDb, sql);
|
||||
q.bind(1, m_jobId);
|
||||
|
||||
beginResetModel();
|
||||
m_data.clear();
|
||||
|
||||
for (auto row : q)
|
||||
{
|
||||
Item item;
|
||||
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 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.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);
|
||||
item.otherJobCat = JobCategory(row.get<int>(10));
|
||||
if (row.column_type(10) == SQLITE_NULL)
|
||||
item.otherJobCat = JobCategory::NCategories;
|
||||
|
||||
m_data.append(item);
|
||||
}
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void NextPrevRSJobsModel::clearData()
|
||||
{
|
||||
beginResetModel();
|
||||
m_data.clear();
|
||||
m_data.squeeze();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
db_id NextPrevRSJobsModel::jobId() const
|
||||
{
|
||||
return m_jobId;
|
||||
}
|
||||
|
||||
void NextPrevRSJobsModel::setJobId(db_id newJobId)
|
||||
{
|
||||
m_jobId = newJobId;
|
||||
if (m_jobId)
|
||||
refreshData();
|
||||
else
|
||||
clearData();
|
||||
}
|
||||
|
||||
NextPrevRSJobsModel::Mode NextPrevRSJobsModel::mode() const
|
||||
{
|
||||
return m_mode;
|
||||
}
|
||||
|
||||
void NextPrevRSJobsModel::setMode(Mode newMode)
|
||||
{
|
||||
m_mode = newMode;
|
||||
if (m_jobId)
|
||||
refreshData();
|
||||
}
|
||||
|
||||
NextPrevRSJobsModel::Item NextPrevRSJobsModel::getItemAtRow(int row) const
|
||||
{
|
||||
return m_data.value(row, Item());
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NEXTPREVRSJOBSMODEL_H
|
||||
#define NEXTPREVRSJOBSMODEL_H
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
#include <QVector>
|
||||
#include <QTime>
|
||||
|
||||
#include "utils/types.h"
|
||||
|
||||
namespace sqlite3pp {
|
||||
class database;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief The NextPrevRSJobsModel class
|
||||
*
|
||||
* This model shows Next/Prev Jobs of currently set Job
|
||||
*
|
||||
* Next/Prev jobs are calculated based on coupled RS plans.
|
||||
* RS which do not have previous/next job are showed with "Depot" string
|
||||
*/
|
||||
class NextPrevRSJobsModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Columns
|
||||
{
|
||||
JobIdCol = 0,
|
||||
RsNameCol,
|
||||
TimeCol,
|
||||
NCols
|
||||
};
|
||||
|
||||
enum Mode
|
||||
{
|
||||
PrevJobs = 0,
|
||||
NextJobs
|
||||
};
|
||||
|
||||
struct Item
|
||||
{
|
||||
db_id otherJobId = 0;
|
||||
db_id couplingId = 0;
|
||||
db_id rsId = 0;
|
||||
db_id stopId = 0;
|
||||
|
||||
QString rsName;
|
||||
QTime opTime;
|
||||
|
||||
JobCategory otherJobCat = JobCategory::NCategories;
|
||||
};
|
||||
|
||||
NextPrevRSJobsModel(sqlite3pp::database &db, QObject *parent = nullptr);
|
||||
|
||||
// Header:
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const override;
|
||||
|
||||
// Basic functionality:
|
||||
int rowCount(const QModelIndex &p = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex &p = QModelIndex()) const override;
|
||||
|
||||
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
|
||||
|
||||
void refreshData();
|
||||
void clearData();
|
||||
|
||||
db_id jobId() const;
|
||||
void setJobId(db_id newJobId);
|
||||
|
||||
Mode mode() const;
|
||||
void setMode(Mode newMode);
|
||||
|
||||
Item getItemAtRow(int row) const;
|
||||
|
||||
private:
|
||||
sqlite3pp::database &mDb;
|
||||
|
||||
db_id m_jobId = 0;
|
||||
QVector<Item> m_data;
|
||||
|
||||
Mode m_mode = PrevJobs;
|
||||
};
|
||||
|
||||
#endif // NEXTPREVRSJOBSMODEL_H
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rscouplinginterface.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
@ -7,6 +26,9 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include "stopmodel.h"
|
||||
#include "utils/rs_utils.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
||||
RSCouplingInterface::RSCouplingInterface(database &db, QObject *parent) :
|
||||
QObject(parent),
|
||||
|
@ -17,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();
|
||||
|
@ -34,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);
|
||||
|
@ -48,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;
|
||||
|
@ -77,82 +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)
|
||||
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)
|
||||
{
|
||||
bool electrified = stopsModel->isRailwayElectrifiedAfterStop(m_stopId);
|
||||
if(!electrified)
|
||||
{
|
||||
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; //Abort
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,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";
|
||||
|
||||
|
@ -208,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
|
||||
|
@ -224,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);
|
||||
|
@ -232,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";
|
||||
|
||||
|
@ -278,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
|
||||
|
@ -295,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;
|
||||
|
@ -314,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";
|
||||
|
||||
|
@ -360,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
|
||||
|
@ -376,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);
|
||||
|
@ -384,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;
|
||||
}
|
||||
|
||||
|
@ -398,6 +422,61 @@ bool RSCouplingInterface::uncoupleRS(db_id rsId, const QString& rsName, bool on)
|
|||
return true;
|
||||
}
|
||||
|
||||
int RSCouplingInterface::importRSFromJob(db_id otherStopId)
|
||||
{
|
||||
query q_getUncoupled(mDb, "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"
|
||||
" JOIN rs_models ON rs_models.id=rs_list.model_id"
|
||||
" WHERE coupling.stop_id=? AND coupling.operation=0");
|
||||
q_getUncoupled.bind(1, otherStopId);
|
||||
|
||||
int count = 0;
|
||||
bool lineElectrified = stopsModel->isRailwayElectrifiedAfterStop(m_stopId);
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
for (auto rs : q_getUncoupled)
|
||||
{
|
||||
db_id rsId = rs.get<db_id>(0);
|
||||
|
||||
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));
|
||||
|
||||
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));
|
||||
|
||||
QString rsName = rs_utils::formatNameRef(modelName, modelNameLen, number, modelSuffix,
|
||||
modelSuffixLen, rsType);
|
||||
|
||||
// TODO: optimize work
|
||||
if (coupleRS(rsId, rsName, true, !lineElectrified))
|
||||
count++;
|
||||
|
||||
if (timer.elapsed() > 10000)
|
||||
{
|
||||
// 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?"));
|
||||
|
||||
if (ret == QMessageBox::No)
|
||||
return count; // Abort here
|
||||
|
||||
timer.restart(); // Count again
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool RSCouplingInterface::hasEngineAfterStop(bool *isElectricOnNonElectrifiedLine)
|
||||
{
|
||||
query q_hasEngine(mDb, "SELECT coupling.rs_id,MAX(rs_models.sub_type),MAX(stops.arrival)"
|
||||
|
@ -411,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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RSCOUPLINGINTERFACE_H
|
||||
#define RSCOUPLINGINTERFACE_H
|
||||
|
||||
|
@ -25,6 +44,8 @@ public:
|
|||
bool coupleRS(db_id rsId, const QString &rsName, bool on, bool checkTractionType);
|
||||
bool uncoupleRS(db_id rsId, const QString &rsName, bool on);
|
||||
|
||||
int importRSFromJob(db_id otherStopId);
|
||||
|
||||
bool hasEngineAfterStop(bool *isElectricOnNonElectrifiedLine = nullptr);
|
||||
|
||||
bool isRailwayElectrified() const;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rslistondemandmodel.h"
|
||||
|
||||
#include "utils/delegates/sql/pageditemmodelhelper_impl.h"
|
||||
|
@ -16,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)
|
||||
{
|
||||
|
@ -44,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;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RSLISTONDEMANDMODEL_H
|
||||
#define RSLISTONDEMANDMODEL_H
|
||||
|
||||
|
@ -19,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
|
||||
};
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rsproxymodel.h"
|
||||
|
||||
#include "rscouplinginterface.h"
|
||||
|
@ -6,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
|
||||
|
@ -26,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)
|
||||
{
|
||||
|
@ -39,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;
|
||||
}
|
||||
|
@ -135,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;
|
||||
|
@ -157,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RSPROXYMODEL_H
|
||||
#define RSPROXYMODEL_H
|
||||
|
||||
|
@ -14,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;
|
||||
|
||||
|
@ -29,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;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stopcouplingmodel.h"
|
||||
|
||||
#include "utils/delegates/sql/pageditemmodelhelper_impl.h"
|
||||
|
@ -7,7 +26,6 @@ using namespace sqlite3pp;
|
|||
|
||||
#include "utils/rs_utils.h"
|
||||
|
||||
|
||||
StopCouplingModel::StopCouplingModel(sqlite3pp::database &db, QObject *parent) :
|
||||
RSListOnDemandModel(db, parent),
|
||||
m_stopId(0),
|
||||
|
@ -27,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);
|
||||
|
|
|
@ -1,9 +1,27 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STOPCOUPLINGMODEL_H
|
||||
#define STOPCOUPLINGMODEL_H
|
||||
|
||||
#include "rslistondemandmodel.h"
|
||||
|
||||
|
||||
class StopCouplingModel : public RSListOnDemandModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STOPMODEL_H
|
||||
#define STOPMODEL_H
|
||||
|
||||
|
@ -7,7 +26,7 @@
|
|||
#include <QVector>
|
||||
#include <QSet>
|
||||
|
||||
#include "utils/types.h"
|
||||
#include "stations/station_utils.h"
|
||||
|
||||
namespace sqlite3pp {
|
||||
class database;
|
||||
|
@ -17,9 +36,10 @@ struct StopItem
|
|||
{
|
||||
struct Gate
|
||||
{
|
||||
db_id gateConnId = 0;
|
||||
db_id gateId = 0;
|
||||
int trackNum = -1;
|
||||
db_id gateConnId = 0;
|
||||
db_id gateId = 0;
|
||||
int gateTrackNum = -1;
|
||||
utils::Side stationTrackSide = utils::Side::NSides;
|
||||
};
|
||||
|
||||
struct Segment
|
||||
|
@ -28,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;
|
||||
|
@ -42,12 +62,11 @@ struct StopItem
|
|||
QTime arrival;
|
||||
QTime departure;
|
||||
|
||||
int addHere = 0;
|
||||
int addHere = 0;
|
||||
|
||||
StopType type = StopType::Normal;
|
||||
};
|
||||
|
||||
|
||||
/*!
|
||||
* \brief The StopModel class
|
||||
*
|
||||
|
@ -60,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;
|
||||
|
@ -75,7 +94,7 @@ public:
|
|||
void setAutoUncoupleAtLast(bool value);
|
||||
|
||||
// Loading
|
||||
void loadJobStops(db_id jobId);
|
||||
bool loadJobStops(db_id jobId);
|
||||
void clearJob();
|
||||
|
||||
// Saving
|
||||
|
@ -85,7 +104,7 @@ public:
|
|||
|
||||
// Editing
|
||||
void addStop();
|
||||
void removeStop(const QModelIndex& idx);
|
||||
void removeStop(const QModelIndex &idx);
|
||||
void removeLastIfEmpty();
|
||||
|
||||
void uncoupleStillCoupledAtLastStop();
|
||||
|
@ -98,7 +117,8 @@ public:
|
|||
db_id getNewShiftId() const;
|
||||
|
||||
// Setters
|
||||
void setStopInfo(const QModelIndex& idx, StopItem newStop, StopItem::Segment prevSeg);
|
||||
void setStopInfo(const QModelIndex &idx, StopItem newStop, StopItem::Segment prevSeg,
|
||||
bool avoidTimeRecalc = false);
|
||||
|
||||
bool setStopTypeRange(int firstRow, int lastRow, StopType type);
|
||||
|
||||
|
@ -109,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;
|
||||
|
||||
|
@ -119,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);
|
||||
|
@ -130,11 +158,10 @@ 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 nextStationId, db_id &out_gateId, db_id &out_suggestedTrackId);
|
||||
|
||||
signals:
|
||||
void edited(bool val);
|
||||
|
@ -142,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);
|
||||
|
@ -162,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;
|
||||
|
||||
|
@ -198,8 +229,8 @@ private:
|
|||
|
||||
enum EditState
|
||||
{
|
||||
NotEditing = 0,
|
||||
InfoEditing = 1,
|
||||
NotEditing = 0,
|
||||
InfoEditing = 1,
|
||||
StopsEditing = 2
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "trainassetmodel.h"
|
||||
|
||||
#include "utils/delegates/sql/pageditemmodelhelper_impl.h"
|
||||
|
@ -9,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)
|
||||
|
@ -26,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"
|
||||
|
@ -84,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);
|
||||
|
@ -134,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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TRAINASSETMODEL_H
|
||||
#define TRAINASSETMODEL_H
|
||||
|
||||
|
@ -8,12 +27,13 @@ class TrainAssetModel : public RSListOnDemandModel
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef enum {
|
||||
enum Mode
|
||||
{
|
||||
BeforeStop,
|
||||
AfterStop
|
||||
} Mode;
|
||||
};
|
||||
|
||||
TrainAssetModel(sqlite3pp::database& db, QObject *parent = nullptr);
|
||||
TrainAssetModel(sqlite3pp::database &db, QObject *parent = nullptr);
|
||||
|
||||
// TrainAssetModel
|
||||
void setStop(db_id jobId, QTime arrival, Mode mode);
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rscoupledialog.h"
|
||||
|
||||
#include <QLabel>
|
||||
|
@ -14,15 +33,18 @@
|
|||
|
||||
#include <sqlite3pp/sqlite3pp.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
@ -54,32 +76,39 @@ RSCoupleDialog::RSCoupleDialog(RSCouplingInterface *mgr, RsOp o, QWidget *parent
|
|||
lay->addWidget(freightLabel, 0, 2);
|
||||
lay->addWidget(freightView, 1, 2);
|
||||
|
||||
showHideLegendBut = new QPushButton;
|
||||
lay->addWidget(showHideLegendBut, 2, 0, 1, 1);
|
||||
|
||||
legend = new QFrame;
|
||||
lay->addWidget(legend, 3, 0, 1, 3);
|
||||
lay->addWidget(legend, 2, 0, 1, 3);
|
||||
legend->hide();
|
||||
|
||||
QDialogButtonBox *box = new QDialogButtonBox(QDialogButtonBox::Ok); //TODO: implement also cancel
|
||||
QHBoxLayout *buttonLay = new QHBoxLayout;
|
||||
lay->addLayout(buttonLay, 3, 0, 1, 3);
|
||||
|
||||
showHideLegendBut = new QPushButton;
|
||||
buttonLay->addWidget(showHideLegendBut);
|
||||
|
||||
QDialogButtonBox *box =
|
||||
new QDialogButtonBox(QDialogButtonBox::Ok); // TODO: implement also cancel
|
||||
connect(box, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
lay->addWidget(box, 4, 0, 1, 3);
|
||||
buttonLay->addWidget(box);
|
||||
|
||||
connect(showHideLegendBut, &QPushButton::clicked, this, &RSCoupleDialog::toggleLegend);
|
||||
updateButText();
|
||||
|
||||
setMinimumSize(400, 300);
|
||||
setWindowFlag(Qt::WindowMaximizeButtonHint);
|
||||
|
||||
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
|
||||
|
@ -90,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);
|
||||
|
@ -157,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)
|
||||
{
|
||||
|
@ -245,32 +285,72 @@ void RSCoupleDialog::loadProxyModels(sqlite3pp::database& db, db_id jobId, db_id
|
|||
coachModel->loadData(coaches);
|
||||
}
|
||||
|
||||
void RSCoupleDialog::done(int ret)
|
||||
{
|
||||
// Save legend state
|
||||
AppSettings.setShowCouplingLegend(m_showLegend);
|
||||
|
||||
QDialog::done(ret);
|
||||
}
|
||||
|
||||
void RSCoupleDialog::toggleLegend()
|
||||
{
|
||||
if(legend->isVisible())
|
||||
setLegendVisible(!m_showLegend);
|
||||
}
|
||||
|
||||
void RSCoupleDialog::updateButText()
|
||||
{
|
||||
showHideLegendBut->setText(m_showLegend ? tr("Hide legend") : tr("Show legend"));
|
||||
}
|
||||
|
||||
void RSCoupleDialog::setLegendVisible(bool val)
|
||||
{
|
||||
m_showLegend = val;
|
||||
|
||||
if (legend->isVisible() && !m_showLegend)
|
||||
{
|
||||
legend->hide();
|
||||
}else{
|
||||
}
|
||||
else if (m_showLegend && !legend->isVisible())
|
||||
{
|
||||
legend->show();
|
||||
if(!legend->layout())
|
||||
if (!legend->layout())
|
||||
{
|
||||
double fontPt = font().pointSizeF() * 1.2;
|
||||
|
||||
QVBoxLayout *legendLay = new QVBoxLayout(legend);
|
||||
QLabel *label = new QLabel(tr("<p style=\"font-size:13pt\">"
|
||||
"<span style=\"background-color:#FF56FF\">___</span> The item isn't coupled before or already coupled.<br>"
|
||||
"<span style=\"background-color:#FF3d43\">___</span> The item isn't in this station.<br>"
|
||||
"<span style=\"color:#0000FF;background-color:#FFFFFF\">\\\\\\\\</span> Railway line doesn't allow electric traction.<br>"
|
||||
"<span style=\"background-color:#00FFFF\">___</span> First use of this item.<br>"
|
||||
"<span style=\"background-color:#00FF00\">___</span> This item is never used in this session.</p>"));
|
||||
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);
|
||||
legendLay->setContentsMargins(QMargins());
|
||||
legend->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
||||
}
|
||||
}
|
||||
updateButText();
|
||||
adjustSize();
|
||||
}
|
||||
|
||||
void RSCoupleDialog::updateButText()
|
||||
{
|
||||
showHideLegendBut->setText(legend->isVisible() ? tr("Hide legend") : tr("Show legend"));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef RSCOUPLEDIALOG_H
|
||||
#define RSCOUPLEDIALOG_H
|
||||
|
||||
|
@ -13,20 +32,25 @@ 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;
|
||||
|
||||
private slots:
|
||||
void toggleLegend();
|
||||
|
||||
private:
|
||||
void updateButText();
|
||||
void setLegendVisible(bool val);
|
||||
|
||||
private:
|
||||
RSCouplingInterface *couplingMgr;
|
||||
|
@ -37,6 +61,7 @@ private:
|
|||
|
||||
QPushButton *showHideLegendBut;
|
||||
QWidget *legend;
|
||||
bool m_showLegend;
|
||||
|
||||
RsOp op;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "shiftbusydialog.h"
|
||||
#include "shiftbusymodel.h"
|
||||
|
||||
|
@ -15,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;
|
||||
|
@ -38,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")));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHIFTBUSYBOX_H
|
||||
#define SHIFTBUSYBOX_H
|
||||
|
||||
|
@ -17,6 +36,7 @@ public:
|
|||
explicit ShiftBusyDlg(QWidget *parent = nullptr);
|
||||
|
||||
void setModel(ShiftBusyModel *m);
|
||||
|
||||
private:
|
||||
QLabel *m_label;
|
||||
QTableView *view;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "shiftbusymodel.h"
|
||||
|
||||
#include "utils/jobcategorystrings.h"
|
||||
|
@ -16,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:
|
||||
|
@ -47,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)
|
||||
{
|
||||
|
@ -75,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;
|
||||
|
@ -99,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,"
|
||||
|
@ -114,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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SHIFTBUSYMODEL_H
|
||||
#define SHIFTBUSYMODEL_H
|
||||
|
||||
|
@ -11,32 +30,33 @@ namespace sqlite3pp {
|
|||
class database;
|
||||
}
|
||||
|
||||
//TODO: move to shifts subdir
|
||||
// TODO: move to shifts subdir
|
||||
class ShiftBusyModel : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
typedef enum {
|
||||
enum Columns
|
||||
{
|
||||
JobCol = 0,
|
||||
Start,
|
||||
End,
|
||||
NCols
|
||||
} Columns;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
struct JobInfo
|
||||
{
|
||||
db_id jobId;
|
||||
QTime start;
|
||||
QTime end;
|
||||
JobCategory jobCat;
|
||||
} JobInfo;
|
||||
};
|
||||
|
||||
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;
|
||||
|
@ -44,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:
|
||||
|
|
|
@ -1,34 +1,51 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stopdelegate.h"
|
||||
|
||||
#include "stopeditor.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "app/session.h"
|
||||
#include "model/stopmodel.h"
|
||||
|
||||
#include "app/scopedebug.h"
|
||||
#include <QPainter>
|
||||
#include <QSvgRenderer>
|
||||
#include <QGuiApplication>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <sqlite3pp/sqlite3pp.h>
|
||||
using namespace sqlite3pp;
|
||||
|
||||
#include "app/scopedebug.h"
|
||||
#include <QDebug>
|
||||
|
||||
StopDelegate::StopDelegate(sqlite3pp::database &db, QObject *parent) :
|
||||
QStyledItemDelegate(parent),
|
||||
mDb(db)
|
||||
{
|
||||
renderer = new QSvgRenderer(this);
|
||||
loadIcon(QCoreApplication::instance()->applicationDirPath() + QStringLiteral("/icons/lightning.svg"));
|
||||
refreshPixmaps();
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -39,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());
|
||||
|
||||
|
@ -56,107 +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 lineX = left + (isTransit ? width * 0.1 : 0.0);
|
||||
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"));
|
||||
}
|
||||
|
||||
if(item.type != StopType::Last && item.nextSegment.segmentId)
|
||||
// Check direction
|
||||
if (item.fromGate.gateConnId && item.toGate.gateConnId && item.type != StopType::First
|
||||
&& item.type != StopType::Last)
|
||||
{
|
||||
//Last has no next segment so do not draw lightning
|
||||
// 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
|
||||
QPointF iconTopLeft(left, bottom - PixHeight);
|
||||
painter->drawPixmap(iconTopLeft, m_reverseDirPix);
|
||||
}
|
||||
}
|
||||
|
||||
if (item.type != StopType::Last && item.nextSegment.segmentId)
|
||||
{
|
||||
// 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
|
||||
QSizeF s = QSizeF(renderer->defaultSize()).scaled(width, height / 2, Qt::KeepAspectRatio);
|
||||
QRectF lightningRect(left, top + height / 4, s.width(), s.height());
|
||||
renderer->render(painter, lightningRect);
|
||||
// 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(lightningRect.topLeft(), lightningRect.bottomRight());
|
||||
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(lineX, 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.trackNum != 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.trackNum),
|
||||
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)
|
||||
{
|
||||
const double transitLinePos = rect.left() + rect.width() * 0.1;
|
||||
painter->setPen(QPen(Qt::red, 5));
|
||||
// Draw a vertical -0- to tell this is a transit
|
||||
painter->setPen(QPen(Qt::red, 4));
|
||||
painter->setBrush(Qt::red);
|
||||
painter->drawLine(QLineF(transitLinePos, rect.top(),
|
||||
transitLinePos, rect.bottom()));
|
||||
painter->drawLine(QLineF(transitLineX, rect.top(), transitLineX, rect.bottom()));
|
||||
|
||||
painter->drawEllipse(QRectF(transitLinePos - 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)
|
||||
{
|
||||
|
@ -170,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 = 100;
|
||||
int w = 200;
|
||||
int h = NormalStopHeight;
|
||||
const StopModel *model = static_cast<const StopModel *>(index.model());
|
||||
if(index.row() < 0 || index.row() >= model->rowCount())
|
||||
return QSize(w, 30);
|
||||
if (index.row() < 0 || index.row() >= model->rowCount())
|
||||
return QSize(w, AddHereHeight);
|
||||
|
||||
const StopItem& item = model->getItemAt(index.row());
|
||||
if(item.type == StopType::Transit)
|
||||
h = 80;
|
||||
if(item.addHere != 0)
|
||||
h = 30;
|
||||
const StopItem &item = model->getItemAt(index.row());
|
||||
if (item.type == StopType::Transit)
|
||||
h = TransitStopHeight;
|
||||
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;
|
||||
|
@ -201,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);
|
||||
}
|
||||
|
||||
|
@ -242,27 +267,68 @@ 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);
|
||||
|
||||
stopModel->setStopInfo(index, ed->getCurItem(), ed->getPrevItem().nextSegment);
|
||||
bool avoidTimeRecalc = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
||||
|
||||
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::loadIcon(const QString &fileName)
|
||||
{
|
||||
renderer->load(fileName);
|
||||
}
|
||||
|
||||
void StopDelegate::onEditorSegmentChosen(StopEditor *editor)
|
||||
{
|
||||
if(editor->closeOnSegmentChosen())
|
||||
if (editor->closeOnSegmentChosen())
|
||||
{
|
||||
emit commitData(editor);
|
||||
emit closeEditor(editor, StopDelegate::EditNextItem);
|
||||
}
|
||||
}
|
||||
|
||||
void StopDelegate::refreshPixmaps()
|
||||
{
|
||||
const QString iconPath =
|
||||
QCoreApplication::instance()->applicationDirPath() + QStringLiteral("/icons");
|
||||
|
||||
// Square pixmaps
|
||||
m_lightningPix = QPixmap(PixWidth, PixHeight);
|
||||
m_reverseDirPix = QPixmap(PixWidth, PixHeight);
|
||||
|
||||
m_lightningPix.fill(Qt::transparent);
|
||||
m_reverseDirPix.fill(Qt::transparent);
|
||||
|
||||
QSvgRenderer mSvg;
|
||||
QPainter painter;
|
||||
QRectF iconRect;
|
||||
|
||||
// Cache Lightning
|
||||
mSvg.load(iconPath + QStringLiteral("/lightning.svg"));
|
||||
|
||||
// Scale SVG to fit requested size
|
||||
iconRect.setSize(mSvg.defaultSize().scaled(PixWidth, PixHeight, Qt::KeepAspectRatio));
|
||||
|
||||
// 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
|
||||
mSvg.load(iconPath + QStringLiteral("/reverse_direction.svg"));
|
||||
|
||||
// Scale SVG to fit requested size
|
||||
iconRect.setSize(mSvg.defaultSize().scaled(PixWidth, PixHeight, Qt::KeepAspectRatio));
|
||||
|
||||
// Center on pixmap
|
||||
iconRect.moveTop((PixHeight - iconRect.height()) / 2);
|
||||
iconRect.moveLeft((PixWidth - iconRect.width()) / 2);
|
||||
painter.begin(&m_reverseDirPix);
|
||||
mSvg.render(&painter, iconRect);
|
||||
painter.end();
|
||||
}
|
||||
|
|
|
@ -1,12 +1,27 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STOPDELEGATE_H
|
||||
#define STOPDELEGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
#include "utils/types.h"
|
||||
|
||||
#include <QSvgRenderer>
|
||||
|
||||
namespace sqlite3pp {
|
||||
class database;
|
||||
}
|
||||
|
@ -31,17 +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 loadIcon(const QString& fileName);
|
||||
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &) const override;
|
||||
|
||||
signals:
|
||||
/*!
|
||||
|
@ -66,8 +79,20 @@ private slots:
|
|||
void onEditorSegmentChosen(StopEditor *editor);
|
||||
|
||||
private:
|
||||
QSvgRenderer *renderer;
|
||||
void refreshPixmaps();
|
||||
|
||||
private:
|
||||
sqlite3pp::database &mDb;
|
||||
|
||||
QPixmap m_lightningPix;
|
||||
QPixmap m_reverseDirPix;
|
||||
|
||||
static constexpr int NormalStopHeight = 100;
|
||||
static constexpr int TransitStopHeight = 80;
|
||||
static constexpr int AddHereHeight = 30;
|
||||
|
||||
static constexpr int PixWidth = 35;
|
||||
static constexpr int PixHeight = PixWidth;
|
||||
};
|
||||
|
||||
#endif // STOPDELEGATE_H
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stopeditinghelper.h"
|
||||
|
||||
#include "utils/delegates/sql/customcompletionlineedit.h"
|
||||
|
@ -14,37 +33,49 @@
|
|||
|
||||
#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"));
|
||||
connect(mStationEdit, &CustomCompletionLineEdit::completionDone, this, &StopEditingHelper::onStationSelected);
|
||||
mStationEdit->setToolTip(mStationEdit->placeholderText());
|
||||
connect(mStationEdit, &CustomCompletionLineEdit::completionDone, this,
|
||||
&StopEditingHelper::onStationSelected);
|
||||
|
||||
mStTrackEdit = new CustomCompletionLineEdit(stationTrackMatchModel, mEditor);
|
||||
mStTrackEdit->setPlaceholderText(tr("Track"));
|
||||
connect(mStTrackEdit, &CustomCompletionLineEdit::completionDone, this, &StopEditingHelper::onTrackSelected);
|
||||
mStTrackEdit->setToolTip(mStTrackEdit->placeholderText());
|
||||
connect(mStTrackEdit, &CustomCompletionLineEdit::completionDone, this,
|
||||
&StopEditingHelper::onTrackSelected);
|
||||
|
||||
mOutGateEdit = new CustomCompletionLineEdit(stationOutGateMatchModel, mEditor);
|
||||
mOutGateEdit->setPlaceholderText(tr("Next segment"));
|
||||
connect(mOutGateEdit, &CustomCompletionLineEdit::indexSelected, this, &StopEditingHelper::onOutGateSelected);
|
||||
mOutGateEdit->setToolTip(mOutGateEdit->placeholderText());
|
||||
connect(mOutGateEdit, &CustomCompletionLineEdit::indexSelected, this,
|
||||
&StopEditingHelper::onOutGateSelected);
|
||||
|
||||
mOutGateTrackSpin = outTrackSpin;
|
||||
mOutGateTrackSpin->setMaximum(0);
|
||||
connect(mOutGateTrackSpin, qOverload<int>(&QSpinBox::valueChanged), this, &StopEditingHelper::startOutTrackTimer);
|
||||
connect(mOutGateTrackSpin, &QSpinBox::editingFinished, this, &StopEditingHelper::checkOutGateTrack);
|
||||
mOutGateTrackSpin->setToolTip(tr("Out Gate track"));
|
||||
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
|
||||
depEdit->setToolTip(tr("Departure"));
|
||||
|
||||
connect(arrEdit, &QTimeEdit::timeChanged, this, &StopEditingHelper::arrivalChanged);
|
||||
connect(depEdit, &QTimeEdit::timeChanged, this, &StopEditingHelper::departureChanged);
|
||||
}
|
||||
|
@ -56,65 +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; //No tooltip by default
|
||||
if(curStop.type == StopType::Normal)
|
||||
arrTootlip = tr("Press shift if you don't want to change also departure time.");
|
||||
QString arrTootlip = tr("Arrival"); // No message by default
|
||||
if (curStop.type == StopType::Normal)
|
||||
{
|
||||
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);
|
||||
|
@ -128,38 +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;
|
||||
if(model->trySelectNextSegment(curStop, newSegId, 0, 0, segOutGateId))
|
||||
db_id segOutGateId = 0;
|
||||
db_id suggestedTrackId = 0;
|
||||
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>"));
|
||||
|
@ -168,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;
|
||||
|
@ -181,78 +220,85 @@ 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{};
|
||||
|
||||
emit stationTrackChosen();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
if(model->trySelectNextSegment(curStop, newSegId, 0, 0, segOutGateId))
|
||||
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))
|
||||
{
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
@ -261,37 +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.trackNum = 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;
|
||||
if(model->trySelectNextSegment(curStop, curStop.nextSegment.segmentId, trackNum, 0, segOutGateId))
|
||||
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.trackNum != 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.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));
|
||||
}
|
||||
|
||||
//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!"));
|
||||
}
|
||||
}
|
||||
|
@ -299,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;
|
||||
}
|
||||
|
||||
|
@ -329,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;
|
||||
|
@ -350,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->setValue(toGate.trackNum);
|
||||
mOutGateTrackSpin->setMaximum(qMax(0, outTrackCount - 1)); // At least one track numbered 0
|
||||
mOutGateTrackSpin->setValue(toGate.gateTrackNum);
|
||||
mOutGateTrackSpin->blockSignals(false);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STOPEDITINGHELPER_H
|
||||
#define STOPEDITINGHELPER_H
|
||||
|
||||
|
@ -24,29 +43,44 @@ 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;
|
||||
|
||||
signals:
|
||||
void nextSegmentChosen();
|
||||
void stationTrackChosen();
|
||||
|
||||
private slots:
|
||||
void onStationSelected();
|
||||
|
@ -62,7 +96,7 @@ public slots:
|
|||
void stopOutTrackTimer();
|
||||
|
||||
private:
|
||||
void updateGateTrackSpin(const StopItem::Gate& toGate);
|
||||
void updateGateTrackSpin(const StopItem::Gate &toGate);
|
||||
|
||||
private:
|
||||
QWidget *mEditor;
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "stopeditor.h"
|
||||
|
||||
#include "stopeditinghelper.h"
|
||||
|
@ -8,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)));
|
||||
|
@ -32,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);
|
||||
|
@ -43,6 +60,9 @@ StopEditor::StopEditor(sqlite3pp::database &db, StopModel *m, QWidget *parent) :
|
|||
setTabOrder(mStationEdit, arrEdit);
|
||||
setTabOrder(arrEdit, depEdit);
|
||||
setTabOrder(depEdit, mOutGateEdit);
|
||||
|
||||
setToolTip(tr("To avoid recalculation of travel times when saving changes, hold SHIFT modifier "
|
||||
"while closing editor"));
|
||||
}
|
||||
|
||||
StopEditor::~StopEditor()
|
||||
|
@ -55,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());
|
||||
}
|
||||
|
||||
|
@ -76,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);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,28 @@
|
|||
/*
|
||||
* ModelRailroadTimetablePlanner
|
||||
* Copyright 2016-2023, Filippo Gentile
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef STOPEDITOR_H
|
||||
#define STOPEDITOR_H
|
||||
|
||||
#include <QFrame>
|
||||
class StopModel;
|
||||
class StopItem;
|
||||
struct StopItem;
|
||||
|
||||
class QTimeEdit;
|
||||
class QSpinBox;
|
||||
|
@ -30,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:
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
add_subdirectory(crossing)
|
||||
|
||||
set(MR_TIMETABLE_PLANNER_SOURCES
|
||||
${MR_TIMETABLE_PLANNER_SOURCES}
|
||||
PARENT_SCOPE
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
set(MR_TIMETABLE_PLANNER_SOURCES
|
||||
${MR_TIMETABLE_PLANNER_SOURCES}
|
||||
|
||||
jobs/jobs_checker/crossing/job_crossing_data.cpp
|
||||
jobs/jobs_checker/crossing/job_crossing_data.h
|
||||
|
||||
jobs/jobs_checker/crossing/jobcrossingchecker.cpp
|
||||
jobs/jobs_checker/crossing/jobcrossingchecker.h
|
||||
|
||||
jobs/jobs_checker/crossing/jobcrossingmodel.cpp
|
||||
jobs/jobs_checker/crossing/jobcrossingmodel.h
|
||||
|
||||
jobs/jobs_checker/crossing/jobcrossingtask.cpp
|
||||
jobs/jobs_checker/crossing/jobcrossingtask.h
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|