Compare commits

...

1193 Commits

Author SHA1 Message Date
gfgit 130b4312c4 Fix CMake src/graph/CMakeLists.txt
Was broken in recent clang-format commit
2023-08-30 11:32:10 +00:00
Filippo Gentile 1aa23f281a Add .gitignore-blame
This file will contain all big style refactor commits
2023-06-25 13:36:10 +02:00
Filippo Gentile 9f14ee1c64 Add .clang-format file
Run clang-format 16
2023-06-25 13:28:38 +02:00
Filippo Gentile 42c486f5cf
Merge pull request #61 from luca-pellegrini/add_license_to_source_files
Add license and copyright information to source files
2023-05-16 21:28:02 +02:00
Luca Pellegrini 21a54888f2 Remove unwanted non-printable characters
Remove non-printable characters that were causing the compilation to fail
2023-05-07 18:45:36 +02:00
Luca Pellegrini 05c45381d4 Add license and copyright to source files 2023-05-06 22:28:06 +02:00
Filippo Gentile 87ec3e53ce CPack: add better short/extended descriptions
This fixes bad DEB paackage descriptions
2023-03-25 17:01:54 +01:00
Filippo Gentile 7ac337a181 Clear some unused stuff and old comments 2023-03-25 15:51:32 +01:00
Filippo Gentile 4e6f49dd44 JobPathEditor: clear Next/Prev tables when clearing job 2023-02-24 17:00:21 +01:00
Filippo Gentile efd60f9ce6 CMake: bump version to 6.2.1 2023-02-24 16:59:11 +01:00
Filippo Gentile 48faf57a93 ViewManager: fix crash on view close
View widgets connect 'destroyed()' signal to a lambda which removes them
from the list.
This causes iterator removal while on close we loop all the lists and
delete views.
To fix that, empty the list and loop on copies
2023-02-19 11:16:20 +01:00
Filippo Gentile 90ca41cb55 CMake: Bump project version to 6.2.0 2023-02-18 20:17:21 +01:00
Filippo Gentile 9abb3b014c Fix some compiler warnings about unused parameters 2023-02-18 20:08:11 +01:00
Filippo Gentile 6323a87ff0 translations: update Italian and German translations 2023-02-18 19:46:39 +01:00
Filippo Gentile 9bf0027fe3 Fix translation typos and missing Q_OBJECT macro 2023-02-18 19:46:21 +01:00
Filippo Gentile 6923642a53
Merge pull request #59 from gfgit/feature/next_prev_jobs_rs
Feature: Next/Prev jobs
2023-02-18 19:15:27 +01:00
Filippo Gentile 5d643196e0 JobPathEditor: add next/prev jobs views
- Use a splitter to lay 3 views vertically
- New context menu for Jobs views
2023-02-18 19:13:10 +01:00
Filippo Gentile 62ee86b8b5 NextPrevRSJobsModel: new model to list next/prev jobs 2023-02-18 19:13:10 +01:00
Filippo Gentile 585476240f EditStopDialog: allow RS import from other Jobs
- Removed unused included header
- Added new button to UI
- New importJobRS() slot connected to button

Import all uncoupled RS from another Job
2023-02-18 19:13:09 +01:00
Filippo Gentile a4393aec43 RSCouplingInterface: add importJobRS()
Imports uncoupled RS from another Job stop and couples them to current
stop.
2023-02-18 19:13:09 +01:00
Filippo Gentile 02f4938396 JobMatchModel: new match model for jobs
Allows filtering by station
2023-02-18 19:13:09 +01:00
Filippo Gentile 49c7f60c94 RSCouplingInterface: minor optimization
Do not check for rollingstock traction type if we already know next
railway segment is electrified (so it accepts all types of engine).
2023-02-18 19:13:09 +01:00
Filippo Gentile 7fb7271745
Merge pull request #58 from gfgit/feature/jobs_checker2
Feature: Job Checker
2023-02-18 19:04:21 +01:00
Filippo Gentile 1cbc5e38a2 JobCrossingModel: add description to errors 2023-02-18 18:58:27 +01:00
Filippo Gentile f60f9ed11d JobCrossingChecker: react to session loaded and job changed
- Add context menu
2023-02-18 18:58:27 +01:00
Filippo Gentile 1b65e413de RsCheckerManager: react to session loaded 2023-02-18 18:58:27 +01:00
Filippo Gentile 3847853913 SettingsDialog: add job crossing checker options 2023-02-18 18:58:26 +01:00
Filippo Gentile 8c61554a5d JobCrossingTask: prepare for single job task 2023-02-18 18:58:26 +01:00
Filippo Gentile 4e622d0841 JobCrossingErrorData: fix initialization
JobCrossingErrorMap: allow to renameJob()
2023-02-18 18:58:26 +01:00
Filippo Gentile 0149217499 IBackgroundChecker: allow to react on session loaded
MeetingSession: fix call to BackgroundManager
2023-02-18 18:58:26 +01:00
Filippo Gentile e5fc856c10 BackgroundManager: allow to start all tasks
CMake: remove ENABLE_RS_CHECKER dependant option
2023-02-18 18:58:26 +01:00
Filippo Gentile ecf934bf86 MainWindow: use new BackgroundResultPanel 2023-02-18 18:58:26 +01:00
Filippo Gentile 5f165502f8 RsCheckerManager: port to IBackgroundChecker
Remove old RsErrorsWidget, replaced by BackgroundResultWidget
2023-02-18 18:58:26 +01:00
Filippo Gentile 232f4e1dcc RsErrWorker: port to TaskProgressEvent
Use ENABLE_BACKGROUND_MANAGER guard macro
2023-02-18 18:58:26 +01:00
Filippo Gentile 576a79e065 RsErrorTreeModel: port to SingleDepthTreeModelHelper
Use ENABLE_BACKGROUND_MANAGER guard macro
2023-02-18 18:58:26 +01:00
Filippo Gentile c61fac42b7 Rename error_data.h to rs_error_data.h
Filename was too generic
2023-02-18 18:58:26 +01:00
Filippo Gentile 1618cc47ad RsCheckerManager: fix error_data.h header
Add new helper functions to use in SingleDepthTreeModelHelper
2023-02-18 18:58:26 +01:00
Filippo Gentile 6dacc44880 JobCrossingChecker: new checker for crossings and passings
Initial implementation
2023-02-18 18:58:26 +01:00
Filippo Gentile 3b969aafce BackgroundManager: use new checker interface
BackgroundResultPanel: tab widget for BackgroundResultWidget

MeetingSession: do not use RSCheckerManager directly
2023-02-18 18:58:26 +01:00
Filippo Gentile 77dcb5d0de IBackgroundChecker: new helper class for checkers
BackgroundResultWidget: result widget implementation
2023-02-18 18:58:26 +01:00
Filippo Gentile 9383f4a922 SingleDepthTreeModelHelper: new tree model helper class 2023-02-18 18:58:26 +01:00
Filippo Gentile a352eafdbb TaskProgressEvent: new generic progress event 2023-02-18 18:58:26 +01:00
Filippo Gentile b2a8e04faf translations: fix Italian translation typo 2023-02-18 18:56:11 +01:00
Filippo Gentile 66442b13cb StopModel: fix warnings printing and segment errors
Fix not updating out gate if only segment changed but not the gate
itself. This scenario happens when database is corrupted (maybe because
segment changed track number while jobs where already set on it)
2023-02-18 18:55:34 +01:00
Filippo Gentile 706d9dadb4 Fix unused headers in editstopdialog.cpp and stopdelegate.cpp 2023-02-18 18:48:09 +01:00
Filippo Gentile de54030785 StopModel: allow skipping travel time recalculation
- Now EditStopDialog and StopDelegate check for SHIFT pressed on save.
- Added tooltips to StopEditor and EditStopDialog Ok button
2023-02-18 18:46:37 +01:00
Filippo Gentile 0c04121867 StationEditDialog: refresh row count on connection import
When importing track connections from SVG, recalculate model row count,
so the view gets update automatically.
2023-01-10 16:51:58 +01:00
Filippo Gentile 017005e1a9 RSProxyModel: fix Italian translation 2023-01-06 15:24:17 +01:00
Filippo Gentile 8667ea96fe StationSVGHelper: raise track connection limit
Big stations easily exceed 100 track connections.
This meant that some of them were not displayed.
1000 seems more reasonable limit.
2023-01-06 14:47:18 +01:00
Filippo Gentile 3672395be5 LineSegmentsModel: fix comment typo 2023-01-06 14:46:11 +01:00
Filippo Gentile a53c8948fe StationSVGHelper: do not show arrival if stop really is a departure
If stop's in_gate connection is NULL, do not try to match it,
which would lead to incorrect path being highlighted
2023-01-06 14:45:54 +01:00
Filippo Gentile 0273501500 StationSVGPlanDlg: force zoom change on reload 2023-01-06 14:45:47 +01:00
gfgit b951cedbc9
Merge pull request #57 from gfgit/feature/import_from_svg
Feature: Import SVG station connections
2023-01-06 14:40:18 +01:00
Filippo Gentile f352555266 StationSVGHelper: raise track connection limit
Big stations easily exceed 100 track connections.
This meant that some of them were not displayed.
500 seems more reasonable limit.
2022-12-10 10:58:24 +01:00
Filippo Gentile f0ca606bf2 StationEditDialog: add button to import track connections
This tells StationSVGHelper to import track connection from SVG plan
2022-12-06 23:03:15 +01:00
Filippo Gentile af337e3065 StationSVGHelper: import track connections from SVG
New Feature:
Import station track connections to gates from a station plan SVG.

NOTE: SVG must contain special data regardin tracks
2022-12-06 23:02:21 +01:00
Filippo Gentile 214f08dc3e RsErrorTreeModel: fix sibling() and add some comments 2022-11-01 16:43:31 +01:00
Filippo Gentile fca21eeae6 RsErrWorker: pass by reference 2022-11-01 16:43:04 +01:00
Filippo Gentile cb20814a69 StopModel: suggest track when fail to set next segment 2022-11-01 16:10:51 +01:00
Filippo Gentile 43658c227a ViewManager: fix JobsManager not closed on session close
This could cause crashed do to SQL query run after database was closed.
And also would let cached data persist when switching session.
2022-10-27 23:52:53 +02:00
Filippo Gentile 3df21ef200 ViewManager: react to delete all jobs
When deleting all session's jobs we do not emit signals for every single
job, so we need to manually reload things.

Close jobs views of rollingstock, shift and stations because they would
be empty.
2022-10-27 23:48:26 +02:00
Filippo Gentile 5e94d9b603 ShiftGraphScene: react to delete all jobs
When deleting all session's jobs we do not emit shift jobs changed for
every single job, so we need to manually reload things.
2022-10-27 23:46:35 +02:00
Filippo Gentile a3db250463 StationSVGPlanDlg: new clearJobs_internal() function
clearJobs() needs to update() SSPViewer view in order to show changes.

Since we call it also from reloadJobs(), instead of having 2 calls to
update view, we split in public and internal function.
2022-10-27 23:37:29 +02:00
Filippo Gentile 98cd6f2702 ShiftGraphEditor: remove unused includes 2022-10-27 23:18:38 +02:00
Filippo Gentile 58523ed724 LineGraphManager: react to delete all jobs
When deleting all session's jobs we do not emit station plan changed for
every single stop, so we need to manually reload things.
2022-10-27 22:54:36 +02:00
Filippo Gentile 04a148b9ba JobsHelper: use suggested jobId passed
Add Doxygen comment
2022-10-27 22:31:30 +02:00
gfgit cfc705a009
Merge pull request #53 from gfgit/delay_linegraph_reload
Delay LineGraphScene reload

When we will have multiple line views, updating should be done in event loop to avoid freezing the UI.
- Do batch updates, cumulate all changes to every scene and then update only once (saves time)
- Fix scene not correctly updated in some cases (e.g. changing Job ID without other modifications didn't trigger update)
- LineGraphScene: light update when only station names change but not the rest.
2022-09-11 17:02:49 +02:00
Filippo Gentile b2bbf975f8 LineGraphManager: set a maximum update time
When maximum is reached, update is stopped and a second one is scheduled
to finish operation later.
2022-09-11 17:01:21 +02:00
Filippo Gentile 79e609b720 StationSVGHelper: store next segment conn and stop type
- Show Transit instead of Departing for transits
- Don't be fooled by departure time, a job is departing only if next
segment connection is set.
2022-09-11 16:49:50 +02:00
Filippo Gentile 78bf2166f9 StationSVGPlanDlg: round time to minutes 2022-09-11 16:16:50 +02:00
Filippo Gentile 8583684780 StopModel: fix uncoupleStillCoupledAtStop()
Do not select also RS which are correctly uncoupled at last stop, so
they are not needlessly updated.
Select rollingstock item which are coupled at last stop, which need to
be uncoupled (or removed by user)
So REPLACE instead of IGNORE for such cases.
2022-09-11 15:35:01 +02:00
Filippo Gentile e83d8f601e StopModel: update all stations and rollingstock on job info changes
When a Job changes ID or category, we need to inform all stations and
rollingstock items involved on this Job

- JobPathEditor: move emit to StopModel
2022-09-11 15:27:02 +02:00
Filippo Gentile 8e2f7c545b LineGraphManager: add comment and rework onJobChanged() 2022-09-11 15:18:56 +02:00
Filippo Gentile 4360e4b5b1 MeetingSession: distinguish track plan change from job plan change
Renamed stationJobsPlanChanged()
Added   stationTrackPlanChanged()

When only Jobs change there is no need to fully reload graphs.
LineGraphManager: use common implementation to react to this new signal
2022-09-11 14:32:05 +02:00
Filippo Gentile 140d076651 LineGraphManager: use batch update mode
- Renamed stationPlanChanged() to onStationJobPlanChanged()
- Schedule update instad of updating immediatly so we can do cumulative
updates on a single scene
- Added some comments
2022-09-11 14:22:41 +02:00
Filippo Gentile 7728133a95 LineGraphScene: allow to update station names
Update only station names on horizontal header and also graph name for
single station graphs
2022-09-11 13:56:13 +02:00
Filippo Gentile 1438c4b118 LineGraphScene: add PendingUpdate flags
Reset it when loading of graph is complete
2022-09-11 13:36:38 +02:00
Filippo Gentile 774d4497c5 LineGraphManager: add async updates basic infrastructure
This will be needed for batch updates of multiple scenes
2022-09-11 13:07:00 +02:00
Filippo Gentile eb7b773fc4 StopModel: move clearing of stations and rollingstock to common place
stationsToUpdate and rsToUpdate are now only cleared on loadJobStops()
and endStopsEditing()
2022-09-10 13:07:05 +02:00
Filippo Gentile 42a468a1b5 StopModel: do not skip stops when setting type in a range.
If we skip a stop, arrival of next stops might get shifted wrongly.
Insetead just do not set Transit type on stops which (un)couple
rollingstock items but still shift them as needed.
2022-09-10 13:02:22 +02:00
Filippo Gentile 1bc031cae6 LineGraphManager: unnecessary double check 2022-09-10 13:00:28 +02:00
Filippo Gentile 0a5d02b862 LineGraphScene: remove old TODO comment now resolved
It was done in commit f165928a57
2022-09-10 12:59:59 +02:00
Filippo Gentile d161b87859 ViewManager: always disable JobPathEditor when it gets cleared
This prevent inconsistend behaviour of empty JobPathEditor (no job
loaded) but enabled widgets which allow user to edit fields.
2022-09-10 12:44:15 +02:00
Filippo Gentile fede5a826d JobPathEditor: show error when trying to open invalid jobId
Modify StopModel::loadJobStops() to return false for invalid jobs.
Clear JobPathEditor, disable it and show error to user.
This avoids confusing empty job which does not exist, but for which
JobPathEditor would ask user to save changes or delete because it has no
stops.
But the job does not exist so it cannot be removed nor it's changes
saved which would produce further error messages and really confuse the
user about what is happening.
2022-09-10 12:42:53 +02:00
Filippo Gentile f6148cf0ba EditRailwayConnectionDlg: fix Gate max track limit 2022-07-18 19:08:52 +02:00
Filippo Gentile f626d9ff8d EditRailwaySegmentDlg: offer default segment name on save
When user forgets to add segment name, on save try to build a default
one and let user decide if it's good otherwise go back and let user
choose a different name
2022-07-18 18:59:43 +02:00
Filippo Gentile 90f6669614 EditRailwaySegmentDlg: explicitly updateTrackConnectionModel()
Update model explicitly when segment is set, so it gets called only once
at end of dialog setup.
Then call it when user changes data rather than also when program
changes data.
2022-07-18 18:42:15 +02:00
Filippo Gentile 76c774554e RailwaySegmentConnectionsModel: print errors on applyChanges()
Turn in to bool, but for now we just return true
2022-07-18 18:34:24 +02:00
Filippo Gentile 0b34512f2f EditRailwayConnectionDlg: prevent getting stuck on error
When database gets corrupted, or there is some bug in program logic,
user would be unable to add a connection and thus unable to close dialog
because railway segment cannot have zero connections.
This is quite frustrating behaviour, expecially for user with low
understanding of program internals
2022-07-18 18:33:32 +02:00
Filippo Gentile 9727d68493 SplitRailwaySegmentDlg: make taller and better align buttons 2022-07-18 18:23:24 +02:00
Filippo Gentile 6bb011d96b Doxygen: fix ref not parsed well 2022-07-17 19:54:02 +02:00
Filippo Gentile b7e1f7284a SplitRailwaySegmentDlg: allow to edit first segment, fix connections
Allow to edit both halves of original segment.
Fix segment onnections not remembered.

Pass 2 RailwaySegmentConnectionModel to RailwaySegmentSplitHelper
2022-07-17 19:53:22 +02:00
Filippo Gentile dfc856aabe EditRailwaySegmentDlg: allow to save custom connections
Allow to pass a RailwaySegmentConnection model so if manually apply is
true, caller can store segment connections and applying them later
togheter with other changes made by user

Also document constructor and setManuallyApply()
2022-07-17 19:41:17 +02:00
Filippo Gentile 042644c00a RailwaySegmentConnectionsModel: fix default connections
The same logic was repeated 3 times but only first one was correct.
Move the duplicated connection check to insertOrReplace()
2022-07-17 19:13:15 +02:00
Filippo Gentile 0497d6442f Translations: update translations 2022-07-17 11:59:46 +02:00
Filippo Gentile 0c1d9db3bf Merge branch 'feature/german_translation' 2022-07-17 11:46:31 +02:00
Filippo Gentile cbebaf5204 ViewManager: small optimization for StationSVGPlanDlg 2022-07-17 11:08:05 +02:00
Filippo Gentile 6a3281b4f1 EditRailwaySegmentDlg: reorder functions 2022-07-17 10:24:32 +02:00
Filippo Gentile 5608f0bc28 Merge branch 'feature/layout_split_segment' into master
Merge SplitRailwaySegmentDlg with previous implementation
2022-07-17 10:24:12 +02:00
Filippo Gentile 36bfe821f1 Add initial SplitRailwaySegmentDlg
Created new dialog to split existing railway segments
With help of RailwaySegmentSplitHelper
2022-07-16 18:58:47 +02:00
Filippo Gentile b1f202c1cd Utils: move GateInfo and SegmentInfo out from RailwaySegmentHelper
Move it to utils:: namespace so it can be forward declared

- EditRailwaySegmentDlg: allow to manually apply changes after closing
dialog:
This is useful for reusing dialog in another dialog such as splitting
segments
2022-07-16 18:43:15 +02:00
Filippo Gentile 38e1248f60 SQLite: fix if clause in models
Affected: RollingstockSQLModel, LinesModel, StationsModel
2022-05-30 22:02:35 +02:00
Filippo Gentile b8483827ae ViewManager: ask to create Shifts before creating Jobs
JobsHelper: check if there are no shifts
2022-03-08 23:38:38 +01:00
Filippo Gentile 2a3496f0bc Translations: update Italian translation 2022-03-08 15:42:09 +01:00
Filippo Gentile 63335f7613 StationTrackConnectionsModel: better error message 2022-03-08 15:41:55 +01:00
Filippo Gentile 7a3a34a1ac StationTrackConnectionsModel: check also for constraint trigger error 2022-03-08 15:36:54 +01:00
Filippo Gentile 816a0830d9 RSCoupleDialog: align legend in a table with 2 columns
Add "normal" state in the ledend, now they are 6

MRTPSettings: remember if legend was hidden or shown
2022-03-08 15:33:59 +01:00
Filippo Gentile 1c7d288632 StationsModel: fix typo 2022-03-08 15:32:50 +01:00
Filippo Gentile 3ef0a142ef StationsManager, StationEditDialog: show error directly 2022-03-08 15:32:34 +01:00
Filippo Gentile 783683b232 RailwaySegmentHelper: add custom error text on segment removal 2022-03-08 15:31:57 +01:00
Filippo Gentile 573dfac662 RsPlanModel: mark cells with errors 2022-03-08 13:23:12 +01:00
Filippo Gentile 82c154fca2 StationsManager: ask confirmation before deleting items 2022-03-08 12:17:31 +01:00
Filippo Gentile d185c0016e Models: check also for SQLITE_CONSTRAINT_FOREIGNKEY error 2022-03-08 12:17:00 +01:00
Filippo Gentile 48d7180cd5 ShiftsModel: better shift creation, better error reporting 2022-03-08 12:10:04 +01:00
Filippo Gentile d3d6e099b6 MeetingSession: fix database table creation
Add note on SQLite extended error codes
Explicitly set Foreign Key ON DELETE RESTRICT for jobs.shift_id
2022-03-08 12:08:00 +01:00
Filippo Gentile 4c25de9133 RSOwnersSQLModel: better error message for failing removal 2022-03-08 11:42:46 +01:00
Filippo Gentile f1fc42dd9b ShiftManager: ask name on shift creation, show errors to user 2022-03-08 11:42:02 +01:00
Filippo Gentile 1816b6292c RollingStockManager: show model errors to user 2022-03-08 11:40:55 +01:00
Filippo Gentile c04d77d004 SQLite: use sqlite3_prepare_v2 to get extended_error_code()
See SQLite documentation for more info.
The old deprecated sqlite3_prepare() made sqlite3_step() always
return generic SQLITE_ERROR on fail, now we get better error codes.
2022-03-08 11:32:59 +01:00
Filippo Gentile e536d1897f RSImportWizard: fix progress page complete
When importation is done we need to tell progress page to enable
'Finish' button.
But we were using load page instead of import page so 'Finish' remained
disabled.
2022-02-13 23:41:47 +01:00
Filippo Gentile ba97509c2b Translations: update Italian translation 2022-02-13 23:32:46 +01:00
Filippo Gentile 1aa479afcd MainWindow: fix infinite loop when closing is busy
When Database is busy on closing it migth be because some background
task is still running,
so we wait for a bit and then try to close again.
But if second closeSession() is succesful we need to stopCloseTimer()
otherwise we trigger an infinite loop.
2022-02-13 23:29:42 +01:00
Filippo Gentile 0f0aa2c94e StopEditingHelper: add tooltips for fields
Some tooltips are taken from placeholder texts
2022-02-13 23:27:45 +01:00
Filippo Gentile 80630fbb2a StopEditingHelper: fix showing out gate track on last stop
Last stop has not 'out' gate so hide gate track spinbox
2022-02-13 23:14:22 +01:00
gfgit b48372faa7
Merge pull request #48 from gfgit/feature/detect_direction_change
Feature detect direction change

Detect train direction change on stops by comparing station track sides.
When a direction change is detected:

- draw a little U-shaped arrow near station in JobPathEditor
- In EditStopDialog tell user about it in "Additional Notes" section
- In Job (and Shift) and Station sheets prepend "Reverses direction" on stop description

In addition, show line traction type in EditStopDialog
Cache rendered SVG in pixmaps for StopDelegate
2022-02-13 23:09:59 +01:00
Filippo Gentile e0e22c8830 Translations: update Italian translation 2022-02-13 22:53:58 +01:00
Filippo Gentile 11637625f8 JobWriter: add direction change in notes
StationWriter: do the same
Odt: add translation text for direction reverse
2022-02-13 22:53:58 +01:00
Filippo Gentile a005f0f49e StationPlanModel: show direction change in description field 2022-02-13 22:53:47 +01:00
Filippo Gentile 1c4d27351d Icons: fix reverse_direction.svg icon join betwen arrow and path 2022-02-13 22:50:51 +01:00
Filippo Gentile 7fe4e130ea StopDelegate: cache SVG in pixmaps, draw direction change indicator 2022-02-13 22:50:51 +01:00
Filippo Gentile 50a2f94233 CMake: add reverse_direction.svg icon and install it in icon folder 2022-02-13 22:50:51 +01:00
Filippo Gentile b533c5dfe6 EditStopDialog: remove unused variable and unused logic if 2022-02-13 22:50:51 +01:00
Filippo Gentile 3ad2ab563f StopDelegate: always put a space on left of segment name
Previously was done only for transits

- Use constant for sizeHint()
2022-02-13 22:50:51 +01:00
Filippo Gentile 681220732c EditStopDialog: add Additional Notes section
These are automatically generated (as opposed to stop description
which is manually typed by the user).

Current notes: traction type and direction changes
2022-02-13 22:50:50 +01:00
Filippo Gentile 5ccf083911 StopEditingHelper: notify when station track is changed 2022-02-13 22:50:50 +01:00
Filippo Gentile 95c1633103 StopItem: rename gate track member to avoid confusion with station track 2022-02-13 22:50:50 +01:00
Filippo Gentile 4dd467212c StopModel: store station in and out connections side
- Generally improve comments
2022-02-13 22:50:50 +01:00
Filippo Gentile 97cd886109 Style: modernize C++, remove typedef from struct and enum declarations 2022-02-12 16:46:43 +01:00
Filippo Gentile d25897424e Clang: mismatched forward declarations struct/class 2022-02-12 16:35:24 +01:00
Filippo Gentile 3427248703 EditRailwaySegmentDlg: fix crash
When user clicked on Gate field before setting corresponding Station
field, it crashed because Gate models were not yet initialized.
To fix, initialize them immediately on contructing dialog
2022-02-10 23:32:14 +01:00
Filippo Gentile 7e12edba26 Translations: add German translations for Station, Job, From, To 2022-02-09 10:11:29 +01:00
Filippo Gentile e749e82eb4 FilterHeaderLineEdit: fix context menu separator
FilterHeaderView: QLineEdit include is not needed
2022-02-07 15:32:02 +01:00
Filippo Gentile a622a49bdc CMake: move windeployqt and DLL path finding to separate files
Remove unused code for crash debugging
2022-02-03 16:17:13 +01:00
Filippo Gentile 5313a0f287 Spell check on BUILDING.md and CONTRIBUTING.md 2022-02-03 12:07:47 +01:00
Filippo Gentile d28c2b868b README: spell check on README.md and README_it.md 2022-02-03 12:02:45 +01:00
Filippo Gentile 83e60d26a1 Screenshots: fix path in README.md and README_it.md 2022-02-01 18:58:23 +01:00
Filippo Gentile 8d5c280663 Screenshots: move to dedicated folder 2022-02-01 15:28:18 +01:00
Filippo Gentile 4e5463173e Screenshot: add SVG Station Plan screenshots
English and Italian versions of Adria station SVG Plan
2022-02-01 15:24:16 +01:00
Filippo Gentile 6bde7076ca JobPathEditor: add labels for number, category and shift in UI
- Use QFormLayout instead of QVBoxLayout
- Remove not needed includes in header
- Renamed shiftCustomCombo to just shiftCombo
2022-01-29 13:02:18 +01:00
Filippo Gentile ec7897806f Screenshot: add smaller sized screenshot, useful for small previews 2022-01-29 12:38:27 +01:00
Filippo Gentile cc0770501d CMake: Bump project version to 6.1.0 2022-01-27 19:07:02 +01:00
gfgit d407eb98f0
Merge pull request #46 from gfgit/feature/paged_printing
Feature paged printing

- Port shift graph and line graph to common IGraphScene interface
- Create a proxy scene to draw page margins on top of source scene
- Refactor print system, so it can print generic IGraphScene
2022-01-27 18:57:41 +01:00
Filippo Gentile bdd6be0886 Translations: update Italian translation 2022-01-27 18:56:35 +01:00
Filippo Gentile 2d3336a14f PrintWorker: lock task when accessing members
This is not fully correct because QPainter also keeps a reference to
QPrinter object so it might be better to copy QPrinter or transfer
ownership to task
2022-01-27 18:31:32 +01:00
Filippo Gentile 147b532395 IQuittableTask: add lockTask() and unlockTask() methods
Add Doxygen documentation to class
2022-01-27 18:19:59 +01:00
Filippo Gentile f5279788ce PrintWorker: move to helper/model subdirectory
Move also PrintWorkerHandler and printdefs.h
2022-01-27 17:44:40 +01:00
Filippo Gentile 539f07472f ShiftGraphEditor: use new print dialog 2022-01-27 17:32:52 +01:00
Filippo Gentile ea67bc93a8 ShiftGraphPrintDlg: new dialog to print shift graph
ShiftGraphSceneCollection: new collection to send shif graph in printing
2022-01-27 17:32:52 +01:00
Filippo Gentile 04c0b5cdfd PrintWorker: abort when print paged returns false 2022-01-27 17:32:52 +01:00
Filippo Gentile 53421d4507 PrintWizard: rename QPrinter member to m_printer 2022-01-26 16:20:08 +01:00
Filippo Gentile a2a487fad3 ScenePrintPreviewDlg, PrintPreviewSceneProxy: adapt to namespace changes 2022-01-26 16:14:07 +01:00
Filippo Gentile ed06754520 PrintWizard: use PrintWorkerHandler
- Move user interaction code to printdefs.h
2022-01-26 16:12:37 +01:00
Filippo Gentile 34b277e6f4 PrintWorker: adapt to namespace change, delete scenes
PrinterOptionsWidget: adapt to namespace change
2022-01-26 16:11:19 +01:00
Filippo Gentile fe23e91071 IGraphSceneCollection: remove takeOwnershipOfLastScene()
Document code, ownership is always passed to the caller now.
SceneSelectionModel: adapt code
2022-01-26 16:10:02 +01:00
Filippo Gentile aceaf41475 PrintWorkerHandler: new class to handle PrintWorker
- PrintHelper: move classes in Print namespace so they can be forward
declared
2022-01-26 16:03:16 +01:00
Filippo Gentile 69318af831 PrintHelper: remove unused include 2022-01-26 12:28:21 +01:00
Filippo Gentile c02c523433 PrintWizard: adapt to IGraphSceneCollection
- PrintWorker: do not depend on LineGraphScene
- PrintSelectionPage: adapt to IGraphSceneCollection
- printdefs.h: do not depend on LineGraphScene type
2022-01-26 12:28:05 +01:00
Filippo Gentile ce1d6329d3 SceneSelectionModel: port to IGraphSceneCollection 2022-01-26 12:24:27 +01:00
Filippo Gentile b36e260ab4 IGraphSceneCollection: new class for printing multiple scenes
This allows to remove dependency of PrintWorker on LineGraphScene
2022-01-26 12:23:56 +01:00
Filippo Gentile 163dd176bc PrintWorke: fix print layout for PDF
It was checking wrong flag
2022-01-26 00:39:15 +01:00
Filippo Gentile 1c26d4f771 PrintWizard: tell user when done printing, disable Cancel button
- Also do not prompt user when closing dialog if printing already
finished
2022-01-25 23:52:45 +01:00
Filippo Gentile 98eec5abeb PrintPreviewSceneProxy: allocate memory in advance
This is a small optimization
2022-01-25 23:40:29 +01:00
Filippo Gentile 85da6c49f2 PrintWorker: better progress reporting, better PDF handling
- Split each scene in 10 steps so user sees progress on almost each page
- Allow to print multiple paged scenes on signle PDF
- Reset painter after each scene so transformations do not accumulate.
- Fix paged printing was adding pages on wrong device
- PDF: set creator and document title
2022-01-25 23:40:00 +01:00
Filippo Gentile d85cfe3d62 PrintHelper: better progress reporting, removed isFirstPage flag 2022-01-25 23:36:36 +01:00
Filippo Gentile c4e24b954c PrintWizard: apply layout on construction 2022-01-25 22:34:19 +01:00
Filippo Gentile d095abacd5 PrinterOptionsWidget: save last inserted directory
- ScenePrintPreviewDlg: change margin width units

- ScenePrintPreviewDlg and PrinterOptionsWidget: apply layout
2022-01-25 22:34:00 +01:00
Filippo Gentile a3eb525e84 PrintPreviewSceneProxy: fix painting 2022-01-25 22:32:36 +01:00
Filippo Gentile d1df52524a PrintHelper: rename members and fix printing 2022-01-25 22:32:05 +01:00
Filippo Gentile 967a7aefd4 PrintBasicOptions: change defaults 2022-01-25 22:31:16 +01:00
Filippo Gentile 2688c075c7 PrintOptionsPage: set and update scene page layout 2022-01-24 15:40:18 +01:00
Filippo Gentile bb4983f9eb ScenePrintPreviewDlg, PrintWizard: reset page margins 2022-01-24 15:39:49 +01:00
Filippo Gentile 1f89e8e0e6 ScenePrintPreviewDlg: fix page setup not applied correctly 2022-01-24 14:52:45 +01:00
Filippo Gentile 5d7975a71e PrintHelper: set default DPI to Qt default 72 2022-01-24 14:52:17 +01:00
Filippo Gentile a6af65e93e PrintOptionsPage: fix crash m_scene member not initialized
- Setup page outside of initializePage() function
- Use first selected scene from wizard
2022-01-24 13:58:41 +01:00
Filippo Gentile add51736d4 PrintWizard: custom page validation to work around QWizard
- New getFirstScene() function to get first selected scene
2022-01-24 13:54:26 +01:00
Filippo Gentile 0d3e07050b PrintPreviewSceneProxy: adapt to printer resolution
- ScenePrintPreviewDlg: fix printer resolution
- PrintWizard: do not force resolution on printer
2022-01-24 13:27:54 +01:00
Filippo Gentile 04f7123cf2 PrintHelper: to accomodate printer resolution use new scale factor
We need a premultiplied scale factor to compensate effects of printer
resolutions.
2022-01-24 13:27:54 +01:00
Filippo Gentile adeca33348 MainWindow: fix Print::OutputType enum class 2022-01-24 13:27:54 +01:00
Filippo Gentile 0ebc219cf7 PrintOptionsPage: use new PrinterOptionsWidget 2022-01-24 13:27:54 +01:00
Filippo Gentile 682bb816d9 PrintWorker: use new PrintBasicOptions structure 2022-01-24 13:27:54 +01:00
Filippo Gentile 2a8922c34b PrintWizard: use new PrintBasicOptions structure 2022-01-24 13:27:54 +01:00
Filippo Gentile 2358f4296a PrinterOptionsWidget: new common widget to set options
printdefs.h add PrintBasicOptions structure
2022-01-24 13:27:54 +01:00
Filippo Gentile 1e1478ebd0 PrintWorker: adapt to PrintHelper changes 2022-01-23 22:50:04 +01:00
Filippo Gentile 13ae3f5450 PrintHelper: lower default printer resolution 2022-01-23 22:49:13 +01:00
Filippo Gentile 5869fa3f3f ScenePrintPreviewDlg: show page count, adapt to changes 2022-01-23 22:48:58 +01:00
Filippo Gentile 613088b68d PrintPreviewSceneProxy: adapt to PrintHelper changes 2022-01-23 22:45:00 +01:00
Filippo Gentile 6b15cd6402 PrintHelper: split render parameters, remove useless class
- Split PageLayoutOpt and PageLayoutScaled
- Remove IRenderScene, use IGraphScene directly
- Move calculatePageCount() to PrintHelper
- Move fixPageSize() to PrintHelper
- Render scene headers
- Define default PrinterResolution
2022-01-23 22:43:37 +01:00
Filippo Gentile 3a9451a55c PrintPreviewSceneProxy: remove comments, rename members
- New calculatePageCount() function to do central calculation of page
count.
- Emit pageCountChanged() when updating layout.
2022-01-23 21:52:16 +01:00
Filippo Gentile 2f58a64f2a PrintPreviewSceneProxy: change color groups to 3
3 color groups are enough to avoid 2 adjacent pages of same color.
Make color wash out a bit less transparent so page borders get more
contrast.

Fix extra page rects appearing outside of scene.
2022-01-23 21:40:03 +01:00
Filippo Gentile 851520ef86 PrintPreviewSceneProxy: fix glitch, borders disappearing on scrolling 2022-01-23 21:09:36 +01:00
Filippo Gentile bd1f4efbdb PrintPreviewSceneProxy: use effective page size for font size
Calculate font size based on shorter edge of effective page size
This way when margin is bigger, font gets smaller
2022-01-23 21:06:37 +01:00
Filippo Gentile ce30ac0edc ScenePrintPreviewDlg: fix page just after setup dialog 2022-01-23 20:17:02 +01:00
Filippo Gentile 1c0646eb1a PrintOptionsPage: fix possible wrong page size 2022-01-23 20:13:31 +01:00
Filippo Gentile db70886108 PrintWizard: store a PrintLayoutOpt structure
PrintWorker: store copy of page layout
2022-01-23 19:59:26 +01:00
Filippo Gentile 3d25dc8171 ScenePrintPreviewDlg: fix page size sometimes wrong 2022-01-23 19:58:28 +01:00
Filippo Gentile d9bfe81cfd PrintOptionsPage: remove standard preview dialog 2022-01-23 16:22:37 +01:00
Filippo Gentile cf40959137 PrintPreviewSceneProxy: removed getters, use PageLayoutOpt directly
ScenePrintPreviewDlg: adapt to scene
2022-01-23 15:11:13 +01:00
Filippo Gentile 09ae6d932f PrintPreviewSceneProxy: allow to set page margins
- Draw page margins on top istead of below real page borders
2022-01-23 15:00:39 +01:00
Filippo Gentile eb6c94e59b PrintOptionsPage: update page layout after preview 2022-01-23 14:47:18 +01:00
Filippo Gentile ce689d5250 ScenePrintPreviewDlg: fix compilation, missing semicolon
CustomPageSetupDlg: fix not updating page size
2022-01-23 14:42:28 +01:00
Filippo Gentile 06e72ff1e7 PrintOptionsPage: allow to setup pages also for PDF 2022-01-23 14:35:01 +01:00
Filippo Gentile f4878cafb7 ScenePrintPreviewDlg: use QPageLayout directly 2022-01-23 14:34:36 +01:00
Filippo Gentile 3b532959a0 CustomPageSetupDlg: use QPageLayout::Orientation
It has better names than Qt::Orientation enum
2022-01-23 14:34:11 +01:00
Filippo Gentile d4c22f3d94 ScenePrintPreviewDlg: allow to setup page layout 2022-01-23 14:09:14 +01:00
Filippo Gentile 55ede00bd5 CustomPageSetupDlg: new dialog to set page on PDF printers
PageSizeModel: new model for page combobox
2022-01-23 13:58:55 +01:00
Filippo Gentile 1cc32a1913 PrintPreviewSceneProxy: draw page numbers on top of page rects 2022-01-23 13:12:29 +01:00
Filippo Gentile 63e94bfdab PrintPreviewSceneProxy: draw real page borders along margins 2022-01-23 12:58:36 +01:00
Filippo Gentile eaf8773004 PrintPreviewSceneProxy: draw row/column numbers on headers 2022-01-22 00:04:36 +01:00
Filippo Gentile 35b62322b2 ScenePrintPreviewDlg: painter coordinates are in points (dots) 2022-01-21 23:50:30 +01:00
Filippo Gentile 9b88e8cc1c PrintPreviewSceneProxy: fix vertical header
- Fix page count calculation (it was adding extra pages)
- Wash out colors of source scene in green so red page borders are more
visible
2022-01-21 23:22:48 +01:00
Filippo Gentile 472618ca80 PrintPreviewSceneProxy: shift by top left margin
- Move page drawing to drawPageBorders()
2022-01-21 19:02:44 +01:00
Filippo Gentile 5881f9135e ScenePrintPreviewDlg: pass zoom to proxy scene
Do not store zoom value, use BasicGraphView one
2022-01-21 18:22:00 +01:00
Filippo Gentile 91fd9abf1f LineGraphScene, ShiftGraphScene: fix function signature 2022-01-21 18:18:19 +01:00
Filippo Gentile a6cae27f8b IGraphScene: pass view scrolling in renderHeader()
This mainly is needed for proxy scenes.
2022-01-21 18:17:30 +01:00
Filippo Gentile 36df87b012 ScenePrintPreviewDlg: keep header size fixed
Keep header size independent from view zoom.
Use Qt::FlatCap for borders QPen to avoid exceeding line limits on big
zoom levels
2022-01-21 16:03:53 +01:00
Filippo Gentile e83d1d73f0 PrintPreviewSceneProxy: fix page borders limits
No need to updatePageLay() when source size changes
page caluclation is done already by updateSourceSizeAndRedraw()
2022-01-21 15:49:55 +01:00
Filippo Gentile 5979af91df PrintPreviewSceneProxy: cut page border lines, consider scale 2022-01-21 15:44:12 +01:00
Filippo Gentile 5eaabb9174 ShiftGraphScene: use floating point lines for better precision 2022-01-21 15:37:10 +01:00
Filippo Gentile de2f8dd414 ScenePrintPreviewDlg: set an initial default size 2022-01-21 15:36:44 +01:00
Filippo Gentile 8d05b55732 PrintPreviewSceneProxy: updatePageLay() on scene size change
Draw effective page borders inside margins on top
2022-01-21 15:36:21 +01:00
Filippo Gentile 01fe4d938a ScenePrintPreviewDlg: allow to scale source scene 2022-01-21 01:17:09 +01:00
Filippo Gentile b142d3f537 PrintPreviewSceneProxy: allow to set a page layout 2022-01-21 01:16:48 +01:00
Filippo Gentile 0226e08b0f PrintHelper: allow fractional QPen widths 2022-01-21 01:16:21 +01:00
Filippo Gentile 3d9d1e9b4b PrintPreviewSceneProxy: allow to scale source scene 2022-01-21 00:46:09 +01:00
Filippo Gentile 159d046773 ScenePrintPreviewDlg: allow to reset zoom on double click 2022-01-21 00:10:28 +01:00
Filippo Gentile 9c595279b2 LineGraphScene: allow fractional contents size 2022-01-20 23:57:54 +01:00
Filippo Gentile 1cd0b19536 PrintOptionsPage: temporary show new preview dialog 2022-01-20 23:40:13 +01:00
Filippo Gentile 9e541c7986 PrintPreviewSceneProxy: new IGraphScene proxy
Used in conjunction with ScenePrintPreviewDlg to draw a print preview on
top of a source IGraphucScene
2022-01-20 23:39:53 +01:00
Filippo Gentile 892bc9a6cf LineGraphToolbar: fix doxygen comment
LineGraphScene: add space
2022-01-20 23:38:44 +01:00
Filippo Gentile 73e5e6e179 ShiftGraphScene: move tooltip handling to ShiftGraphView 2022-01-20 22:24:58 +01:00
Filippo Gentile 5e56dc4b39 Doxygen: fix documentation
- BackgroundHelper: remove reference to deprecated classes
- IPagedItemModel: escape # character to suppress warning
2022-01-20 22:23:44 +01:00
Filippo Gentile 71f1e32c92 ShiftGraphEditor: adapt to scene changes 2022-01-20 21:58:22 +01:00
Filippo Gentile 40aff7d14a ShiftGraphHourPanel, ShiftGraphNameHeader: remove deprecated
They are replaced by BasicGraphHeader
2022-01-20 21:57:36 +01:00
Filippo Gentile 4d88c236e0 ShiftGraphScene: subclass IGraphScene
ShiftGraphView: subclass BasicGraphView
2022-01-20 21:56:16 +01:00
Filippo Gentile f46fe790d3 PrintWorker: adapt to LineGraphScene changes 2022-01-20 20:21:52 +01:00
Filippo Gentile 9e56c4f55e LineGraphManager: adapt to LineGraphScene changes 2022-01-20 20:21:41 +01:00
Filippo Gentile 06e929d1eb StationLabelsHeader, HourPanel: remove old classes
They are now replaced by BasicGraphHeader
2022-01-20 20:21:16 +01:00
Filippo Gentile 9562a667eb LineGraphView: subclass BasicGraphView 2022-01-20 20:18:06 +01:00
Filippo Gentile f0ed0bcaf6 BasicGraphView: react to scene header size changes 2022-01-20 20:03:20 +01:00
Filippo Gentile ff475f3eb8 LineGraphScene: subclass IGraphScene
- BackgroundHelper: adapt header drawing
2022-01-20 20:02:56 +01:00
Filippo Gentile 3c27da07bb Doxygen: fix documentation 2022-01-20 19:38:50 +01:00
Filippo Gentile 67ee46b9fb IGraphScene: new graphic scene interface to reduce code duplication
- IGraphScene: abstract scene interface
- BasicGraphView: basic view to display scene
- BasicGraphHeader: header internally used by BasicGraphView
2022-01-20 18:39:46 +01:00
Filippo Gentile 0f76aea14e Printing: fix includes after restructoring directory 2022-01-20 16:50:53 +01:00
Filippo Gentile 4c2da6c816 Printing: refactor structure
Move PrintHelper to separate file
2022-01-20 16:45:52 +01:00
Filippo Gentile ec52f3f833 PrintHelper: default initialize all members 2022-01-20 01:31:53 +01:00
Filippo Gentile 9b566b2e8a PrintHelper: fix painter initialization 2022-01-20 01:25:56 +01:00
Filippo Gentile f5e2ce1ff4 PrintHelper: fix extra page 2022-01-20 01:15:26 +01:00
Filippo Gentile 10734e0a4c PrintWorker: use PrintHelper, implement interfaces 2022-01-20 01:13:32 +01:00
Filippo Gentile db73ff693d PrintOptionsPage: initial print preview work 2022-01-20 00:49:53 +01:00
Filippo Gentile 824cb2100b PrintWorker: initial adapt to PrintHelper 2022-01-20 00:49:25 +01:00
Filippo Gentile e45b6d0eab PrintWorker: add generic PrintHelper class and move out logic 2022-01-20 00:21:11 +01:00
Filippo Gentile a361918694 PrintWorker: move variables into structures 2022-01-19 23:46:23 +01:00
Filippo Gentile 7ae34b3160 PrintWorker: add possibility to draw page numbers 2022-01-19 23:16:15 +01:00
Filippo Gentile 2b263968c5 Utils: make setFontPointSizeDPI() common, put in separate header 2022-01-19 23:15:22 +01:00
Filippo Gentile 63e5a33240 PrintWorker: allow to split a scene on multiple pages
- Allow to set a custom scene scaling
- Allow to set a custom overlap margin
- Allow to draw margin frame
2022-01-19 22:37:24 +01:00
Filippo Gentile 51a6325797 BackgroundHelper: fix job labels disappearings or truncated
In some rare cases Job labels where disappearing because we couldn't
determine if they had to be drawn when they where extending after last
station platform.
In other cases when Job name was too long it got truncated.

Both problems are fixed by setting a maximum right edge for labels.
2022-01-19 21:27:19 +01:00
Filippo Gentile 1648900cde BackgroundHelper: simplify drawBackgroundHourLines()
- Do not draw lines below '24:00'
2022-01-19 20:28:50 +01:00
Filippo Gentile 9e7f7f2a91 Clazy: fix uninitialized variables and dead stores 2022-01-19 16:22:51 +01:00
Filippo Gentile 42f780a017 Clazy: fix missing emit keyword 2022-01-19 16:21:58 +01:00
Filippo Gentile 1aee704e6c PrintWizard: avoid non-POD global static (detected by Clazy) 2022-01-19 15:54:50 +01:00
Filippo Gentile e901df125b Clazy: use multi-arg QString::arg() 2022-01-19 15:48:09 +01:00
Filippo Gentile 4a2dcac484 Fix some tooltips:
When using HTML we need '<br>' instead of '\n' to break line
Use '<table>' to align tooltips with many rows
2022-01-19 15:12:15 +01:00
gfgit 4dded56bf1
Merge pull request #45 from gfgit/feature/new_shift_graph
Feature new shift graph

- Port away from QGraphicsView
- Add context menu and tool tips
- Better text layout
2022-01-19 14:45:15 +01:00
Filippo Gentile 08e15fed81 ShiftGraphScene: fix drawing and better tooltip alignment
Try to avoid overlapping text by using 2 rows for each label type
2022-01-19 14:02:27 +01:00
Filippo Gentile c7d222fcf7 MRTPSettings: better defaults for ShiftGraphScene 2022-01-19 13:15:51 +01:00
Filippo Gentile 6d9d3ebe80 BackgroundHelper: small fix, avoid contructing options again 2022-01-19 12:04:35 +01:00
Filippo Gentile 2f7ccfc97c ShiftGraphScene: store station full name for better tooltips
- Use single QString::arg()
2022-01-19 12:02:15 +01:00
Filippo Gentile 02dcf9edf9 ShiftGraphView: handle context menu
- ShiftGraphEditor: remove context menu slot
2022-01-19 11:33:51 +01:00
Filippo Gentile 55cbb8aaa1 ShiftGraphScene: fix tooltips 2022-01-19 00:24:24 +01:00
Filippo Gentile 5a400cf736 ShiftGraphView: show tooltips 2022-01-19 00:20:33 +01:00
Filippo Gentile 00245da29b ShiftGraphScene: get tooltips at pos 2022-01-19 00:20:13 +01:00
Filippo Gentile 7308c49f2d JobEntry: fix initialize to null 2022-01-19 00:19:52 +01:00
Filippo Gentile 18c58ab3f1 ShiftGraphEditor: fix PDF page size
Needs more investigation...
2022-01-18 23:38:56 +01:00
Filippo Gentile e3bce19a14 SettingsDialog: fix page 2022-01-18 23:38:24 +01:00
Filippo Gentile 6c6b5e97b4 ShiftGraphEditor: fix not creating view
- Make window bigger
- Renamed window, remove "Editor"
2022-01-18 23:16:22 +01:00
Filippo Gentile d5c39a1492 ShiftGraphEditor: delete old files 2022-01-18 23:09:21 +01:00
Filippo Gentile 59db245b7c ShiftGraphEditor: remove old code, common renderGraph() function 2022-01-18 23:05:24 +01:00
Filippo Gentile d3754d0122 ShiftGraphScene: use new settings
- Renamed ShiftRow to ShiftGraph
2022-01-18 22:47:07 +01:00
Filippo Gentile 5163dc7d66 SettingsDialog: better shift settings names 2022-01-18 22:46:42 +01:00
Filippo Gentile 8e2a5bc979 ShiftGraphView: fix settings reload, draw hour lines 2022-01-18 22:32:45 +01:00
Filippo Gentile 52c289000d ShiftGraphScene: load settings on creation
- Draw background hour lines
- Draw background separation lines below each row
- Exceed a bit labels from Job line to get more space
2022-01-18 22:32:17 +01:00
Filippo Gentile 74088c8602 ShiftGraphScene: fix rename shift, fix variable redefinition
- Fix compilation, put const variable definition inside class.
- Fix sorting on name change
- Fix station label alignment
2022-01-18 21:57:24 +01:00
Filippo Gentile 432053e59b ShiftGraphView: refactor directory structor in model and view 2022-01-18 20:12:18 +01:00
Filippo Gentile 788e2e05dc ShiftGraphView: new view to embed ShiftGraphScene
- ShiftGraphHourPanel: horizontal hour panel
- ShiftGraphNameHeader: vertical label panel
2022-01-18 20:07:05 +01:00
Filippo Gentile d9ea922cf5 ShiftGraphScene: implement drawing 2022-01-18 19:12:57 +01:00
Filippo Gentile aef7cfd1ee ShiftGraphScene: new class to hold Job shift data 2022-01-18 16:49:36 +01:00
Filippo Gentile 09aaadaff0 Translations: initial German translation
Made with Google Translate
2022-01-18 13:53:09 +01:00
gfgit eaa0f0d372
Merge pull request #44 from gfgit/feature/paged_model_filtering
Feature paged model filtering

Allow to filter items in paged models

- FilterHeaderView: new QHeaderView subclass to allow filtering
- New API for setting filters
- Implemented filtering in most models
- Move query building to common function buildQuery() in most models
2022-01-13 12:51:49 +01:00
Filippo Gentile 8a25c3f390 IPagedItemModel, IPagedItemModelImpl, FilterHeaderLineEdit: add doc
Document briefly the code
2022-01-13 12:50:29 +01:00
Filippo Gentile c7a472e309 FilterHeaderView: fix signal, document briefly 2022-01-13 12:50:06 +01:00
Filippo Gentile d177ab3f6a ShiftsModel: document briefly 2022-01-13 12:49:49 +01:00
Filippo Gentile d8da91af97 Doxygen: fix syntax on some comments 2022-01-13 12:29:54 +01:00
Filippo Gentile 28e8c89216 ShiftsModel, RSOwnersSQLModel, ImportStationModel: fix switch
Fix switch statment in setFilterAtCol()
2022-01-13 12:18:05 +01:00
Filippo Gentile c2cc58f439 RSModelsSQLModel: add filtering and common query building 2022-01-13 11:41:01 +01:00
Filippo Gentile 36bd2d6a68 StationsModel: fix preparing query twice 2022-01-13 11:40:17 +01:00
Filippo Gentile e99ede2470 RSListOnDemandModel: port to IPagedItemModelImpl common code
- Removed now unused RSListOnDemandResultEvent class
- Adapt TrainAssetModel and StopCouplingModel
2022-01-13 11:39:55 +01:00
Filippo Gentile f3b8ea166b RollingstockSQLModel: fix filter types 2022-01-11 23:54:17 +01:00
Filippo Gentile 20c931276f FilterHeaderView: add tooltips to FilterHeaderLineEdit
FilterHeaderLineEdit: add "Filter #NULL" action to context menu
2022-01-11 23:54:05 +01:00
Filippo Gentile c2f2671277 StationsModel: add header tooltip 2022-01-11 22:29:35 +01:00
Filippo Gentile c20bab48ef StationModel: common query building 2022-01-11 22:25:14 +01:00
Filippo Gentile fbb3c241cb JobListModel: common query building 2022-01-11 19:22:17 +01:00
Filippo Gentile 964a8df501 RollingstockSQLModel: add new filters, common query building 2022-01-11 19:21:36 +01:00
Filippo Gentile d8b1e1ab3d IPagedItemModel: remove old comments and better filtering code 2022-01-11 16:02:57 +01:00
Filippo Gentile 3fe400d126 RollingstockSQLModel: add filtering support 2022-01-11 16:02:25 +01:00
Filippo Gentile 9f5cd87a98 StationsModel: add filtering support 2022-01-11 14:23:48 +01:00
Filippo Gentile 15c5da8802 RSOwnersSQLModel: add filtering
- Clear filter when adding new RS Owner

- JobListModel, ImportStationModel: emit filterChanged()
2022-01-11 14:07:11 +01:00
Filippo Gentile a884a4340a ShiftsModel: port to IPagedModelHelper implementation
- Add filtering
2022-01-11 14:05:51 +01:00
Filippo Gentile f22d3e656f FilterHeaderLineEdit: keep old value for comparison
Emit only if really changed and do not emit when changed by model
filterChanged() signal
2022-01-11 14:05:12 +01:00
Filippo Gentile cc23a26f96 IPagedItemModel: add filterChanged() signal
- FilterHeaderView: sync filters when they change
2022-01-11 13:51:40 +01:00
Filippo Gentile 1fd61d179c ImportStationModel: move to new filtering API
- SelectStationPage: adapt to model changes
2022-01-11 11:59:24 +01:00
Filippo Gentile b4fbc5c661 FilterHeaderView: use it on main views
- JobsManager, StationsManager, RollingStockManager, ShiftManager

- StationFreeRSViewer: make a note to fix when ready
2022-01-11 11:57:43 +01:00
Filippo Gentile a186023ad0 FilterHeaderView: new QHeaderView subclass to allow filtering
FilterHeaderLineEdit: convinience class to get delayed signal
2022-01-10 15:32:38 +01:00
Filippo Gentile 657407cda1 JobListModel: initial filtering support 2022-01-10 13:11:45 +01:00
Filippo Gentile 12587f733e IPagedItemModel: support filtering 2022-01-10 13:11:45 +01:00
Filippo Gentile 098f1838a8 StationEditDialog: allow to save XML plan 2022-01-04 12:56:11 +01:00
Filippo Gentile 063d4f02bc StationSVGHelper: allow to save station plan to XML
When saving to XML load every item.
When loading for SVG, skip items not in SVG
2022-01-04 12:55:56 +01:00
Filippo Gentile 79eb82f758 FileFormats: add XML format 2022-01-04 12:50:20 +01:00
Filippo Gentile 7352e0ca05 StationSVGHelper, StationSVGPlanDlg: adapt to library change 2022-01-04 12:20:53 +01:00
Filippo Gentile 2cd423aff1 StationSVGPlanDlg: make window a bit wider 2022-01-03 23:06:18 +01:00
Filippo Gentile 7e18451f2f StopModel: reset in gate for First stop 2022-01-03 20:37:20 +01:00
Filippo Gentile a30b5642b5 StopModel: clear in gate on first stop when we have out gate 2022-01-03 20:11:12 +01:00
Filippo Gentile d43cafbc3a StationSVGPlanDlg: reloadJobs() inside reloadDBData() 2022-01-03 20:10:53 +01:00
Filippo Gentile d0e1e65553 StopModel: fix updating stop type on toggle transit 2022-01-03 19:35:49 +01:00
gfgit 9fb9ff6e1a
Merge pull request #43 from gfgit/feature/station_svg_view_jobs
Feature station svg view jobs

This feature is extremely useful to see which station platforms are free at a given time and to check no train collides with another one.
2022-01-03 19:21:30 +01:00
Filippo Gentile 0a5cd5cd96 Translations: update translations 2022-01-03 19:05:53 +01:00
Filippo Gentile 5fae5bc678 StationSVGHelper: make arrival and departure bold 2022-01-03 18:57:08 +01:00
Filippo Gentile 96f9a959d0 StationSVGPlanDlg: always show time in message box
Need to have a valid QTime
2022-01-03 18:56:51 +01:00
Filippo Gentile a72c161cca StationSVGPlanDlg: allow to go to next or prev stops 2022-01-03 18:34:22 +01:00
Filippo Gentile 6488fcdcd8 StationSVGHelper: getPrevNextStop() to get prev or next stops 2022-01-03 18:34:07 +01:00
Filippo Gentile 635042b360 ViewManager: fix QTime not included 2022-01-03 18:33:45 +01:00
Filippo Gentile cce4b327be JobPathEditor, StationJobView: open Station SVG with time
StationPlanModel: new finction to get item at row
2022-01-03 18:02:34 +01:00
Filippo Gentile 97519d311a ViewManager: allow to request Staation SVG to set Job time 2022-01-03 18:01:51 +01:00
Filippo Gentile 4b30b2af89 StationSVGPlanDlg: show messagebox on track double click 2022-01-03 17:45:04 +01:00
Filippo Gentile 7242fbe0ee StationSVGHelper: stop gate connection ID 2022-01-03 17:43:12 +01:00
Filippo Gentile ef9ce4c950 StationSVGHelper: set better tooltip for station tracks 2022-01-03 16:47:09 +01:00
Filippo Gentile 8ff1d35ac5 StationSVGPlanDlg: show jobs at requested time 2022-01-03 14:26:10 +01:00
Filippo Gentile afdcbc4ad4 StationSVGHelper: fix loading connection and track match
applyStationJobsToPlan(): get by reference in loops
2022-01-03 14:24:11 +01:00
Filippo Gentile bd248ca2b9 StationSVGHelper: fix job stop loading 2022-01-03 13:31:51 +01:00
Filippo Gentile 27d74dce1e StationSVGHelper: load job stops
StationSVGPlanDlg: clearJobs() clears only jobs
2022-01-02 20:03:36 +01:00
Filippo Gentile 0d9103c67b Translations: fix Qt translations on Linux
Tested on Ubuntu 21.04, Qt 5.15
On Linux we do not copy Qt internal translations to installation
directory, they are installed system-wide.
Get translation folder path with QLibraryInfo
2022-01-01 21:45:17 +01:00
Filippo Gentile 734e8244e2 Translations: update translations 2022-01-01 16:43:11 +01:00
Filippo Gentile ea764842ad SettingsDialog: fix button text 2022-01-01 16:42:59 +01:00
Filippo Gentile f4dd194853 RecentDirStore: use it on most of QFileDialog 2021-12-31 13:37:19 +01:00
Filippo Gentile 915d4376c3 RecentDirStore: new class to remember last visited directory 2021-12-31 13:23:11 +01:00
Filippo Gentile 06d345c4c7 Utils: fix includes after refactor 2021-12-30 21:23:08 +01:00
Filippo Gentile a149d82a53 Utils: big refactor of directory structure 2021-12-30 21:22:38 +01:00
gfgit 3fce3f145d
Merge pull request #41 from gfgit/fix_odt_export2
This PR has a bunch of fixes for Sheet Exportation:

- Fix queries to database
- Vertical align to middle for cells in exported Station and Job sheets
- New dialog which allows to open resulting file upon exportation success
- Settings: dynamically load available translations (more robust logic)
- Allow Exporting Sheet in a different language than Application Language (closes  #17)
2021-12-30 20:00:16 +01:00
Filippo Gentile 0e2e253222 JobPathEditor: avoid using internal translation table 2021-12-30 19:47:57 +01:00
Filippo Gentile f85c462fff JobWriter: put a space between category and Job number
JobCategoryName: new function jobNameSpaced()
2021-12-30 19:47:32 +01:00
Filippo Gentile 8584ae1c82 JobWriter: vertical align to middle for stop and asset table cells 2021-12-30 19:37:38 +01:00
Filippo Gentile 8a1233a90b OdtDocument: fix untranslated metadata keyword 2021-12-30 19:11:15 +01:00
Filippo Gentile 6dd5ac5331 Sheet Export: use new Text struct odtutils.h for translations
It's cleaner than an array of char strings.
2021-12-30 19:09:15 +01:00
Filippo Gentile f2fd243deb Translations: update italian translation 2021-12-30 18:57:59 +01:00
Filippo Gentile a97ad5753b Sheet Export: use new translation system
OdtDocument: store correct language in document
2021-12-30 18:57:41 +01:00
Filippo Gentile d93ee3e1b9 Sheet Export: store translatable strings and allow custom language
- Allow to export sheets in a different language than Application
Language
2021-12-30 18:56:58 +01:00
Filippo Gentile 823be54714 SettingsDialog: use embedded QLocale and fix logic
Translations: update languageutils.cpp too
2021-12-30 18:53:21 +01:00
Filippo Gentile cfa5e66c94 MeetingSession: store embedded QLocale language
This avoids having to construct a QLocale object every time we need
comparison with default language.
2021-12-30 18:52:20 +01:00
Filippo Gentile 5060518140 SettingsDialog: fix not getting chosen language 2021-12-30 18:08:00 +01:00
Filippo Gentile eb97bcab68 Translations: use original app language on loading 2021-12-30 17:49:20 +01:00
Filippo Gentile 4abfff007c SettingsDialog: use LanguageModel, set Sheet Export language 2021-12-30 17:48:42 +01:00
Filippo Gentile 061602469f MeetingSession: allow setting custom Sheet Export language 2021-12-30 17:47:51 +01:00
Filippo Gentile 13aec23cc6 LanguageModel: new model to list available translations 2021-12-30 17:28:12 +01:00
Filippo Gentile c4063b3a8b SettingsDialog: list all installed language translations
- Show Language native name in QComboBox and english name in tooltip
2021-12-30 15:58:44 +01:00
Filippo Gentile 94029a19ee Translations: allow getting available translation list
- Skip loading if language is English with default country
2021-12-30 15:13:21 +01:00
Filippo Gentile fb8fbf838e Translations: move translations loading to utils 2021-12-30 14:27:59 +01:00
Filippo Gentile e63fda2235 Translations: removed old translations 2021-12-28 18:33:05 +01:00
Filippo Gentile c7cdc60c0c OpenFileInFolderDlg: display little icon for requested file type 2021-12-28 18:29:40 +01:00
Filippo Gentile 01e290850c Translations: Update italian translation 2021-12-27 12:57:48 +01:00
Filippo Gentile e519a69b4f ShiftGraphEditor: use OpenFileInFolderDlg 2021-12-27 12:51:25 +01:00
Filippo Gentile babe30d30d OpenFileInFolderDlg: use it for sheet exoprts
- JobPathEditor, ShiftManager, StationJobView, SessionStartEndRSViewer
2021-12-27 12:44:31 +01:00
Filippo Gentile 861c7affde StationSheetExport: remove old comment 2021-12-27 12:32:45 +01:00
Filippo Gentile f6f558f325 StationWriter: use shorter header column names 2021-12-27 12:32:30 +01:00
Filippo Gentile e868e4e20c OpenFileInFolderDlg: new dialog to open containing folder
Do not use QMessageBox to avoid closing when click buttons
2021-12-27 12:31:03 +01:00
Filippo Gentile 198608fee6 openfileinfolder.h new common message box to open result file 2021-12-25 23:29:16 +01:00
Filippo Gentile efa6213ace StationWriter: do not repeat first or last job stop
- Write 'START' on Notes for First job stop
2021-12-25 22:25:47 +01:00
Filippo Gentile cd016c9d47 StationWriter: vertical align to middle for station table cells 2021-12-25 19:59:35 +01:00
Filippo Gentile 31733992d3 StationWriter: fix query not reset, fix font and Notes
- Transit Italic P5: take to 12pt from 10pt like normal stops
- Notes field: repeat arrival to better link two rows
2021-12-25 19:45:19 +01:00
Filippo Gentile 5ca7d0fbfe ShiftSheetExport: fix shift query 2021-12-25 17:37:56 +01:00
Filippo Gentile 1a0ee27a76 RailwaySegmentSplitHelper: fix moving segment pos 2021-12-25 16:56:16 +01:00
Filippo Gentile 9879623f03 RailwaySegmentSplitHelper: set also original segment info
- Update also station views
2021-12-25 16:26:53 +01:00
Filippo Gentile 368e0d3f7f SplitRailwaySegmentDlg: store also original segment info 2021-12-25 16:15:58 +01:00
Filippo Gentile 1760d23ccc SplitRailwaySegmentDlg: manually apply segment changes 2021-12-25 15:53:52 +01:00
Filippo Gentile a202702f31 RailwaySegmentSplitHelper: fix query and emit segment changed 2021-12-25 15:53:31 +01:00
Filippo Gentile ba2c55ab70 SplitRailwaySegmentDlg: update gates and fix new segment name 2021-12-25 15:22:46 +01:00
Filippo Gentile a6e0e46c81 SplitRailwaySegmentDlg: add button box, apply changes on done 2021-12-25 15:11:47 +01:00
Filippo Gentile b5bcbafec8 SplitRailwaySegmentDlg: enable edit segment only after station
- Edit new segment only if middle station is set
- Set a better window size
2021-12-25 13:28:24 +01:00
Filippo Gentile 4b91b76e50 EditRailwaySegmentDlg: set minimum distance 2021-12-25 13:20:09 +01:00
Filippo Gentile 1fb67335a7 RailwaySegmentInfo: fix default distance value to 10 km 2021-12-25 13:19:57 +01:00
Filippo Gentile fd428e0361 EditRailwaySegmentDlg: fix not applying values
- Fill all members of RailwaySegmentInfo struct
2021-12-25 13:00:51 +01:00
Filippo Gentile 39b773a118 SplitRailwaySegmentDlg: allow editing segment info 2021-12-25 12:49:38 +01:00
Filippo Gentile 191766ae62 EditRailwaySegmentDlg: allow setting directly segment info 2021-12-25 12:47:53 +01:00
Filippo Gentile 2c123543f9 RailwaySegmentHelper: move RailwaySegmentInfo to station_utils.h
- Put struct in utils namespace
2021-12-25 12:47:25 +01:00
Filippo Gentile c8a580d14b SplitRailwaySegmentDlg: find available segment name 2021-12-25 12:19:00 +01:00
Filippo Gentile f7a38e8bce SplitRailwaySegmentDlg: fix model and title
- Add window title and set minimum size
- Set filter to segment model before choosing
- Fix In Gate edit
- Set segment name
2021-12-25 12:13:00 +01:00
Filippo Gentile 5208e05c59 RailwaySegmentHelper: remove typedef 2021-12-22 20:22:54 +01:00
Filippo Gentile 8d84dd6046 StationsManager: add Split segment Dialog 2021-12-22 20:22:02 +01:00
Filippo Gentile 7cb17b7601 SplitRailwaySegmentDlg: new dialog to split segments in 2 parts 2021-12-22 20:20:14 +01:00
Filippo Gentile 2b191d152c RailwaySegmentSplitHelper: new helper to split segments 2021-12-22 17:21:19 +01:00
Filippo Gentile 984f706c0f RailwaySegmentHelper: take out RailwaySegmentInfo struct 2021-12-22 17:20:35 +01:00
Filippo Gentile b84c2c80ae EditRailwaySegmentDlg: allow manually applying changes
- Get new values in a RailwaySegmentInfo struct
2021-12-22 17:20:17 +01:00
gfgit 2ec77863de Create pull_request_template.md
Add simple pull request template
2021-12-22 15:39:25 +01:00
Filippo Gentile 5d05c48de8 Translations: updted italian translation 2021-12-22 15:15:23 +01:00
gfgit a95a866c95
Merge pull request #39 from gfgit/feature/new_station_import
Features new station import

- Add StationImportWizard
- Fix RSImportWizard: better separate backend code, fix translations
2021-12-22 15:02:56 +01:00
Filippo Gentile b9befd4105 Translations: update Italian translations 2021-12-22 13:58:54 +01:00
Filippo Gentile 1a9acfd01d SelectStationPage: fix dialog title 2021-12-22 13:53:53 +01:00
Filippo Gentile 70e6d07e27 ChooseFilePage: moved to utils
This QWizardPage is common to Station and Rollingstock import
2021-12-22 13:50:48 +01:00
gfgit c85a213370
Merge pull request #40 from gfgit/feature/zoom_line_graph
Feature Zoom Line Graph

- Remove HourLineOffset setting
- Remove associated HourLineStart UI in SettingsDialog
2021-12-22 13:38:48 +01:00
Filippo Gentile 0921eff096 LineGraphToolbar: double click to reset zoom level 2021-12-22 13:24:50 +01:00
Filippo Gentile 1cea37d58a LineGraphView: keep same scroll postition when changing zoom 2021-12-22 13:23:29 +01:00
Filippo Gentile 1af024ac42 MRTPSettings: HourLineOffset removed, use HorizOffset
- Update SettingsDialog, BackgroundHelper

This setting was used to start backgroud hour line a bit before the
first station platform, so a bit before horizontal offset.
But now we apply half station offset before first station and after last
station so it's not needed and causes confusion
2021-12-22 13:01:10 +01:00
Filippo Gentile 25ba2db404 LineGraphWidget: connect zoom signals and slots 2021-12-22 12:55:27 +01:00
Filippo Gentile 0f5c3c5f60 LineGraphView: redraw graph when changing zoom level 2021-12-22 12:55:03 +01:00
Filippo Gentile b5c7e26ef4 LineGraphToolbar: add zoom feature 2021-12-22 12:54:34 +01:00
Filippo Gentile d2e510e487 HourPanel, StationLabelsHeader: fix division rounded to integer 2021-12-22 12:30:36 +01:00
Filippo Gentile 853a7ed2eb LineGraphView: setZoomLevel() to zoom view 2021-12-22 12:18:42 +01:00
Filippo Gentile 34fc6e876a StationLabelsHeader: allow zooming 2021-12-22 12:14:03 +01:00
Filippo Gentile 92262ca5a9 HourPanel: allow zooming 2021-12-22 12:11:26 +01:00
Filippo Gentile 0391922b36 BackgroundHelper: use double for scroll, round cap for jobs
- Use double for scroll values to get precise scrolling
- Set Qt::RoundCap for QPen so we do not get artifacrts on line joins
2021-12-22 12:10:55 +01:00
Filippo Gentile 1525ffcab5 SelectStationPage: show table with all importable stations 2021-12-21 00:09:38 +01:00
Filippo Gentile 51003a74dc ImportStationModel: new model to list importable stations
- CMake: add stations/importer/model subdirectory
- ImportStationModel is similar to StationsModel
2021-12-20 23:55:06 +01:00
Filippo Gentile c6289bd8a4 StationEditDialog: remember if editing is disabled
It was always enabling SVG editing after station was loaded
2021-12-20 23:51:55 +01:00
Filippo Gentile 188385bf46 StationEditDialog: fix renamed Gate Connections Tab
- Fix which Tab is disabled when disabling gate connections
2021-12-20 22:59:48 +01:00
Filippo Gentile b8b95b7d19 StationImportWizard: checkNames() in separate function
- Split station name checking from importation to get better error
reporting if name already exists.
2021-12-20 22:48:24 +01:00
Filippo Gentile ad5a3efe02 StationSVGPlanDlg: do not reloadDBData() when reloadSVG()
- Manually reloading SVG should not also reload Database data
2021-12-20 22:30:43 +01:00
Filippo Gentile 9c9dd1ef47 StationEditDialog: disable buttons when in readonly
- setGateConnectionsVisible(): allow to hide Gate connections Tab
2021-12-20 22:25:04 +01:00
Filippo Gentile 7307961be5 StationImportWizard: copySVGData() to copy SVG station plan 2021-12-20 16:59:39 +01:00
Filippo Gentile 8f0bf71e63 SelectStationPage: import station, allow changing name
- StationImportWizard: add friendship
2021-12-20 16:01:16 +01:00
Filippo Gentile 4ab4393c1f StationImportWizard: addStation() imports station 2021-12-20 13:21:50 +01:00
Filippo Gentile d799e313f2 SelectStationPage: forgot connection 2021-12-18 13:30:33 +01:00
Filippo Gentile 96327de241 StationsManager: add StationImportWizard and connect it 2021-12-18 13:24:38 +01:00
Filippo Gentile 6713df39f9 StationImportWizard: new QWizard to import stations 2021-12-18 13:19:30 +01:00
Filippo Gentile c2c77c3d43 RSImportBackend: add virtual destructor
Needed to delete instance of derived class from base class pointer
2021-12-18 12:28:40 +01:00
Filippo Gentile 585cd69141 RSImportWizard: remove ImportSource enum 2021-12-18 12:25:55 +01:00
Filippo Gentile c8c3e7bcd9 Translations: update Italian translation 2021-12-17 14:11:54 +01:00
Filippo Gentile 94129131a6 RSImportBackendsModel: fix include 2021-12-17 14:11:16 +01:00
Filippo Gentile ee4a37e72f RSImportWizard: use backends model
- Adapt OptionsPage to backend model
2021-12-17 14:10:04 +01:00
Filippo Gentile 82743ec76f CMake: rsbackendsmodel.cpp renamed from optionsmodel.cpp 2021-12-17 14:09:13 +01:00
Filippo Gentile 8adf8a1337 SQLiteOptionsWidget: fix string 2021-12-17 14:06:45 +01:00
Filippo Gentile 7c6d183e4d RSImportBackendsModel: renamed from OptionsModel
- Own backend list
2021-12-17 14:06:25 +01:00
Filippo Gentile 17e536565f RSImportBackend: new class to handle backend
- RSImportODSBackend: backend for ODS Import
- RSImportSQLiteBackend: backend for SQLite Import
2021-12-17 14:05:39 +01:00
Filippo Gentile 096c463f8c LoadingPage: make independent from RSImportWizard 2021-12-17 12:44:42 +01:00
Filippo Gentile 56c2d87194 RSImportWizard: set ChooseFilePage options
IOptionsWidget: add file dialog options getter
2021-12-17 12:39:33 +01:00
Filippo Gentile d5cbc47219 ChooseFilePage: make independent from RSImportWizard 2021-12-17 12:38:35 +01:00
Filippo Gentile 9397c8ad3b ChooseFilePage: remove old comments 2021-12-17 12:18:33 +01:00
Filippo Gentile 9321624201 OptionsPage: fix set default RS type and speed 2021-12-17 12:04:38 +01:00
Filippo Gentile 1127f1c004 CMake: prepare for Station Import 2021-12-17 12:04:15 +01:00
Filippo Gentile 52cedb8f69 Translations: update italian translation 2021-12-17 11:47:21 +01:00
Filippo Gentile 5e785e2a80 MainWindow: try again if closing fails for background task still running 2021-12-16 16:04:37 +01:00
Filippo Gentile 62ed7db377 MeetingSession: remove some TODO comments 2021-12-16 16:04:05 +01:00
Filippo Gentile 1ee66fb438 StopModel: update RS on time change and reset Transit on remove 2021-12-16 15:41:17 +01:00
Filippo Gentile 88123d3da5 StopModel: make markRsToUpdate() private
RSCouplingInterface is marked as friend and it's the only use of this
function
2021-12-16 15:19:15 +01:00
Filippo Gentile f95be9d9d9 Merge branch 'master' of https://github.com/gfgit/ModelRailroadTimetablePlanner 2021-12-16 14:00:41 +01:00
Filippo Gentile 21d473ffbb Update Screenshot.png and Screenshot_it.png
- Make them of same size
2021-12-16 13:59:49 +01:00
Filippo Gentile c18f1b809c Translations: update Italian translation 2021-12-16 13:56:12 +01:00
Filippo Gentile 7804d31fb3 MainWindow: translate Job Editor title 2021-12-16 13:55:08 +01:00
gfgit e9e7cd90f4
Update README_it.md
Added Screenshot chapter also in Italian README
2021-12-16 13:53:08 +01:00
Filippo Gentile 0ba9cc02ac Merge branch 'master' of https://github.com/gfgit/ModelRailroadTimetablePlanner 2021-12-16 13:50:35 +01:00
Filippo Gentile afcb93aaf9 Added Screenshot_it.png 2021-12-16 13:50:21 +01:00
gfgit 0bf16b2481
Update README.md
Added Screenshots chapter
2021-12-16 13:46:22 +01:00
Filippo Gentile a3f1b4086c Added Screenshot.png 2021-12-16 13:42:45 +01:00
Filippo Gentile a1b9d83110 StopDelegate: move transit line to left so it doesn't overlap
Previously it was ovelapping with segment name
2021-12-16 13:41:47 +01:00
Filippo Gentile 95ad2d4a0d Translations: update italian translation 2021-12-16 13:30:06 +01:00
Filippo Gentile a4691a904d ShiftManager: fix string 2021-12-16 13:19:56 +01:00
Filippo Gentile 1f1649138a Translations: "selected" is better word than "current"
Update JobsManager, StationsManager, ShiftManager
2021-12-16 13:12:40 +01:00
Filippo Gentile c2d11f64a5 ShiftManager: enable actions only if row selected
- Added tooltips to QToolbar actions
- Ask user confirmation before removing a Job Shift
2021-12-16 13:06:14 +01:00
Filippo Gentile a78ebbc826 RollingStockManager: enable actions only if row selected 2021-12-16 12:50:04 +01:00
Filippo Gentile c92a415a59 StationsManager: enable actions only if row selected
- Added action tooltips
2021-12-16 12:35:12 +01:00
Filippo Gentile ff00ae9dd0 MainWindow: fix action enable/disable
- Fix remove Job Tooltip
- MainWindow: make window a bit bigger
2021-12-16 12:15:10 +01:00
Filippo Gentile 4a1f36feec JobsManager: enable Job actions only if row selected
- Disable Job actions if no row is selected.
- Added tooltips to all QToolbar actions.
- Added new Edit and Show in Graph actions.
2021-12-16 11:47:25 +01:00
Filippo Gentile a207a97053 ViewManager: ask confirmation before removing a Job 2021-12-16 11:41:58 +01:00
Filippo Gentile 1718479bab MainWindow: enable remove Job only if one selected
If no job is selected, disable toolbar action and set tooltip explaining
you have to select a job first.
2021-12-16 11:41:35 +01:00
Filippo Gentile 4631802ed8 Translations: fix typo 2021-12-13 14:06:10 +01:00
Filippo Gentile 4192e71466 Translations: update Italian translation 2021-12-13 14:00:31 +01:00
Filippo Gentile c4779d7a77 CMake: Bump project version to 6.0.0 2021-12-13 13:19:32 +01:00
gfgit cb7a778cab
Merge pull request #38 from gfgit/feature/new_job_system
Feature New Job System
- Job Path Editing and Scene drawing with new railway layout schema
2021-12-12 18:45:17 +01:00
Filippo Gentile 83356a0a67 LineGraphScene: prefer full station name as graph name 2021-12-12 18:42:22 +01:00
Filippo Gentile 18be558673 CustomCompletionLineEdit: add stopSuggestionsTimer()
- Kill timer in a dedicated function instead of calling killTimer()
every time
2021-12-12 18:37:06 +01:00
Filippo Gentile 9cf172dcf4 StopEditingHelper: when gate has 1 track set max to 0 2021-12-12 18:27:40 +01:00
Filippo Gentile 4e1aa236aa BackgroundHelper: draw job stop labels when needed 2021-12-12 18:27:19 +01:00
Filippo Gentile 7fc79c63ea LineGraphScene: check if job stop labels need to be drawn 2021-12-12 18:26:56 +01:00
Filippo Gentile 4610fa9edf StopModel: fix deleted functions 2021-12-12 18:19:48 +01:00
Filippo Gentile 2fd7705b82 StopModel: reorganize functions 2021-12-12 17:41:02 +01:00
Filippo Gentile e0cd632da8 StopModel: listen for segment name changes insteas of lines 2021-12-12 17:22:20 +01:00
Filippo Gentile fca049f179 Utils: remove unused roles in model_roles.h 2021-12-12 17:17:21 +01:00
Filippo Gentile ba2a67c043 MeetingSession: remove prepareQuerues() and finalizeStatements()
Theese functions where needed when we had queries as class memeber so
they would outlive database lifetime.
Now we prepare queries only when needed and finalize them shortly after.
2021-12-12 17:11:11 +01:00
Filippo Gentile 18c7557fa0 StopModel: removed queries and old methods 2021-12-12 17:09:43 +01:00
Filippo Gentile 58e5501620 JobPathEditor: fix EditStopDlg construction 2021-12-12 16:04:48 +01:00
Filippo Gentile 7e2048b331 OwningQPointer: use it for QMenu::exec() 2021-12-12 16:03:49 +01:00
Filippo Gentile c5bb89e862 EditStopDialog: use StopEditingHelper internally 2021-12-12 15:57:10 +01:00
Filippo Gentile cb78c456d8 StopDelegate: StopEditor already updates stop times 2021-12-12 15:56:23 +01:00
Filippo Gentile 816e3082b5 StopEditingHelper: refactor setStop()
- getGateString() helper function
- Allow to start and stop gate track timer from outside
2021-12-12 15:56:01 +01:00
Filippo Gentile af5d3c7351 StopEditor: use StopEditingHelper internally 2021-12-12 14:52:09 +01:00
Filippo Gentile 989557033e StopEditingHelper: new class with common Stop code
Reuse common code in StopEditor and EditStopDialog
2021-12-12 14:51:20 +01:00
Filippo Gentile 0dd0b9caab EditStopDialog: allow setting out gate track 2021-12-12 13:20:04 +01:00
Filippo Gentile 60d9205c57 StopEditor: fix departure update on arrival change 2021-12-12 13:19:34 +01:00
Filippo Gentile dbb370a526 JobPathEditor: do not call parent event handler on our timer 2021-12-12 12:48:15 +01:00
Filippo Gentile 8c97372fb6 StopEditor: allow to set out gate track 2021-12-12 12:47:49 +01:00
Filippo Gentile 62a6b218fe StopModel: keep station track when changing in gate 2021-12-12 12:46:51 +01:00
Filippo Gentile 89b1d13203 StopDelegate: draw out gate track if != 0 2021-12-12 12:45:14 +01:00
Filippo Gentile f112c4a350 StationGatesMatchModel: get gate out track count 2021-12-12 12:39:45 +01:00
Filippo Gentile 4c7265a2d8 EditLineDlg, ChooseSegmentDlg: use StationGatesMatchModel
This will make the process simpler
2021-12-10 00:47:48 +01:00
Filippo Gentile 06d8afd9a0 StationGatesMatchModel: return full name and check reversed segment 2021-12-10 00:46:10 +01:00
Filippo Gentile b665770522 JobPathEditor: give user time to scroll ID spin, skip middle IDs check 2021-12-10 00:23:09 +01:00
Filippo Gentile 6d5a6a4af7 StopEditor, EditStopDialog: do not mark previous segment 2021-12-09 23:59:59 +01:00
Filippo Gentile 999b21b204 StationGatesMatchModel: align to left when showing segments 2021-12-09 23:59:12 +01:00
Filippo Gentile 7aec8834b9 StopDelegate: fix segment height to avoid cutting bottom of letters 2021-12-09 23:54:51 +01:00
Filippo Gentile d18f271dbe StopEditor: replace segment edit with out gate edit 2021-12-09 23:45:07 +01:00
Filippo Gentile 3d03d53d79 EditStopDialog: fix resetting out gate ID 2021-12-09 23:43:35 +01:00
Filippo Gentile 792f5aa76b EditStopDialog: remove previous stop segment field
- Hide previous stop box on First stop
- Fill values of previous stop and In Gate
2021-12-09 23:32:50 +01:00
Filippo Gentile 1819327660 StationGatesMatchModel: fix query bind parameters 2021-12-09 23:21:47 +01:00
Filippo Gentile 360fc4d960 StationGatesMatchModel: fix skipping gates when not connected 2021-12-09 23:19:12 +01:00
Filippo Gentile 84c922962a StationGatesMatchModel: fix setFilter() and getName()
- getName() follow data(Qt::DisplayRole) name format respecting filter
2021-12-09 23:12:08 +01:00
Filippo Gentile 7fa2f0bb3e EditStopDialog: rename memebers similar to StopEditor
- Avoid using model roles, directly use StopModel instead
- Removed onStEditingFinished() unused function
2021-12-09 22:45:13 +01:00
Filippo Gentile 47acae35c3 JobPathEditor: temporarily disable insert before stop action 2021-12-09 22:43:27 +01:00
Filippo Gentile 2142bf7b59 StopEditor, StopModel: clarify variable name 2021-12-09 22:38:36 +01:00
Filippo Gentile d2e8168e6a StationGatesMatchModel: allow displaying segment name and get ID 2021-12-09 22:29:24 +01:00
Filippo Gentile ac33fff6df StopEditor: rename track edit to mStTrackEdit 2021-12-09 22:27:05 +01:00
Filippo Gentile 3e366d5d78 JobPathEditor: avoid using model roles 2021-12-09 22:07:14 +01:00
Filippo Gentile 31339c81c8 StopModel, StopDelegate: include model_roles.h in source file 2021-12-09 22:06:56 +01:00
Filippo Gentile 2dab6b8fa4 StopEditor: rename members to curStop and prevStop 2021-12-09 21:58:05 +01:00
Filippo Gentile 6a00ecb9b2 LineGraphScene: do not swap arrival and departure
- LineGraphScene: always keep departure before arrival in segments
- BackgroundHelper: prevent flipping of text, use x coordinate as now
arrival_Y is always greater than departure_Y
2021-12-09 20:29:37 +01:00
Filippo Gentile 53196f6a28 StopModel: reset query before re-using it 2021-12-09 15:37:08 +01:00
Filippo Gentile d08a8cdbed RsErrWorker: save also previous job category using JobEntry 2021-12-09 14:55:29 +01:00
Filippo Gentile ee9d566f02 RailwaySegmentHelper: fix removing segments with connections 2021-12-09 13:23:27 +01:00
Filippo Gentile ffdc3aeeef BackgroundHelper: draw job name on top of job segments 2021-12-09 13:17:49 +01:00
Filippo Gentile f43ddc52a8 RSJobViewer: fix allow accented letters in owner name
When there are accented letters inside name the string is in UTF-8 so
it's length is shorter than number of bytes (some letter occupy more
than 1 byte) and this was not taken into account.
Converting to QString automatically computes correct string legth.
2021-12-06 19:17:31 +01:00
Filippo Gentile 18e672c8c5 JobsManager: allow to reverse job path when copying it 2021-12-06 18:47:41 +01:00
Filippo Gentile d33324d704 JobsHelper: allow to reverse job path when copyint it 2021-12-06 18:47:18 +01:00
Filippo Gentile e96a2df910 RollingStockManager: fix RS plan search dialog 2021-12-06 18:40:48 +01:00
Filippo Gentile 3b90bee8f7 RsOp: make enum class 2021-12-06 18:25:17 +01:00
Filippo Gentile e60e2eacc8 JobsHelper: copyStops() fix reset RS query at each stop
Previously it was copying first stop RS operations to all new job stops.
2021-12-06 13:21:33 +01:00
Filippo Gentile 60e1c77d1f JobsManager: when copying job path, copy also category 2021-12-06 13:14:20 +01:00
Filippo Gentile 66b15c33e1 JobsHelper: allow setting job category on creation 2021-12-06 13:13:36 +01:00
Filippo Gentile 47278767f5 JobsHelper: fix copy stop RS query and bind NULL instead of 0 2021-12-06 13:12:31 +01:00
Filippo Gentile b32cb4a5f6 JobsManager: edit job after is created from same path 2021-12-06 13:06:52 +01:00
Filippo Gentile 18c1a596c8 JobsManager: allow create new job with same path 2021-12-06 13:04:06 +01:00
Filippo Gentile 5bd3f4d664 JobsHelper: add copyStops() to create a new job with same path 2021-12-06 13:03:50 +01:00
Filippo Gentile 9297f4d205 StopModel: fix time calculation 2021-12-05 19:47:15 +01:00
Filippo Gentile 6f84774d7f JobPathEditor: do not invoke RsCheckerManager directly
Connect RsCheckerManager to MeetingSession::rollingStockPlanChanged()
signal.
This way it is automatically invoked every time a rollingstock item
changes plan.
2021-12-05 19:36:30 +01:00
Filippo Gentile c79eaeb66d JobsHelper: update stations and rollingstock when removing a job 2021-12-05 19:21:15 +01:00
Filippo Gentile 48d91c87e2 JobPathEditor: choose previous stop segment on stop add
Reworked feature similar to previous 'choose nextline' but with railway
segments.
2021-12-05 19:00:25 +01:00
Filippo Gentile 195c4c6186 RSImportWizard: fix title 2021-12-04 11:48:05 +01:00
Filippo Gentile 063f0840ff Remove FIXMEs 2021-12-04 10:36:52 +01:00
Filippo Gentile 251194323d StationEditDialog: fix editing modes 2021-12-04 10:36:27 +01:00
Filippo Gentile 3a50406c60 JobChangeShiftDlg: allow unset shift 2021-12-04 10:04:58 +01:00
Filippo Gentile 184433331b JobPathEditor: fix update stations on discardChanges() 2021-12-04 00:10:19 +01:00
Filippo Gentile 4c5150dc1e JobChangeShiftDlg: adapt query to new schema 2021-12-04 00:07:53 +01:00
Filippo Gentile 7b15248a89 CMake: fix install icon extension Linux CPack 2021-12-03 23:11:00 +01:00
Filippo Gentile 2119ae07b4 Translations: update Italian translations 2021-12-03 22:27:31 +01:00
Filippo Gentile 5edc489c47 FileFormats: update file format name 2021-12-03 22:23:18 +01:00
Filippo Gentile a00b5dec9d JobPathEditor: commit changes before updating views
This way views get latest job id and shift id
2021-12-03 22:12:11 +01:00
Filippo Gentile 480029309a StopModel: prevent resetting shift on commitChanges()
- If Job Shift didn't change do not emit twice
2021-12-03 22:11:18 +01:00
Filippo Gentile 3e81dbf4a9 ShiftBusyModel: adapt query to new schema 2021-12-03 22:09:03 +01:00
Filippo Gentile f818046ea1 ShiftGraphHolder: adapt query to new schema 2021-12-03 22:08:43 +01:00
Filippo Gentile d615542367 ShiftJobsModel: simplify query 2021-12-03 22:05:35 +01:00
Filippo Gentile 1e8ac30101 ShiftJobsModel: adapt query to new schema 2021-12-03 21:37:50 +01:00
Filippo Gentile e16b639657 ShiftManager: fix include 2021-12-03 21:33:24 +01:00
Filippo Gentile ff74c05165 ShiftsModel: renamed from ShiftSQLModel 2021-12-03 21:31:09 +01:00
Filippo Gentile 3011a0a831 MainWindow: load first line or segment when opening session
- When all segments are removed show welcome label
2021-12-03 20:49:06 +01:00
Filippo Gentile d6d9d1db0c Utils: remove deprecated enum 2021-12-03 20:44:58 +01:00
gfgit b22cbd9128
Merge pull request #35 from gfgit/feature/cpack_deb
Feature CPack DEB
- Allow to create DEB packages to easily install the application on Debian based Linux distros
- Fix NSIS installer for windows
2021-12-03 20:10:09 +01:00
Filippo Gentile 0a30c2d3c4 MeetingSession: fix AppData location finding 2021-12-03 19:59:03 +01:00
Filippo Gentile e74cf4b73a NSIS: use pretty name for Start Menu shortcut 2021-12-03 19:55:57 +01:00
Filippo Gentile 3ecd70c1b1 MeetingSession: use short product name for AppData location 2021-12-03 19:55:27 +01:00
Filippo Gentile 3f83a581f1 CMake: Fix install and deploy Qt of Windows installation
- Move NSIS target to packaging subdirectory
2021-12-03 19:43:19 +01:00
Filippo Gentile 7556b1bbbc CMake: rename linux mimetype file 2021-12-02 15:34:04 +01:00
Filippo Gentile d9396b90bd CMake: linux MIME type xml fix identation 2021-12-02 13:57:28 +01:00
Filippo Gentile 274c03ae10 CMake: rename MIME type to vendor dot name format 2021-12-02 13:55:45 +01:00
Filippo Gentile a395fe8c0e CMake: fix Linux .desktop mimetype name 2021-12-02 13:31:10 +01:00
Filippo Gentile 790cdec50e CMake: CPack add custom DEB generator options file 2021-12-02 13:29:54 +01:00
Filippo Gentile 69bcedbb0d CMake: add mimetypes associations for MRTPlanner MIME type 2021-12-02 13:29:28 +01:00
Filippo Gentile 47abd018d1 CMake: add icon files and install them 2021-12-02 13:27:48 +01:00
Filippo Gentile a2eab58e78 CMake: add .desktop file for Linux install 2021-12-02 13:25:35 +01:00
Filippo Gentile b4dd98864b CMake: CPack change DEB package name
- Rename CMake variable to APP_COMPANY_NAME_LOWER
2021-12-02 13:17:57 +01:00
Filippo Gentile 1a26cd8b28 CMake: use GNU standard dirs and install Linux specific stuff
- Desktop File
- Mimetypes association and icon
- Application icon
2021-12-01 12:12:47 +01:00
Filippo Gentile 49ba6d7437 CMake: add Packing.cmake for CPack instructions 2021-12-01 12:11:30 +01:00
Filippo Gentile 1225dd38ce gitignore: exclude CPack DEB artifacts 2021-12-01 12:11:03 +01:00
Filippo Gentile 83bd0e314b StationTracksMatchModel: add tooltips 2021-11-30 09:14:42 +01:00
Filippo Gentile 5d2f3e06c0 StopDelegate: clarify QPen usage in comment 2021-11-30 09:12:33 +01:00
Filippo Gentile bcccdf57b4 StopType: make enum class
Update EditStopDialog, JobPathEditor, StopEditor, StationPlanModel
2021-11-30 09:10:05 +01:00
Filippo Gentile 217c1c6952 StopDelegate: use segments instead of line for electrified calc.
- Draw segment name instead of line
- Use StopItem directly
2021-11-30 09:08:57 +01:00
Filippo Gentile 64984b1c2b StopModel: remove TransitLineChange enum
- Make StopType enum class
- isRailwayElectrifiedAfterRow() pass row instead of stop ID
- createStop() use directly the stop type
- Remove StopItem old members
2021-11-30 09:06:49 +01:00
Filippo Gentile 42b6aa629d Remove platform_utils.h include 2021-11-29 23:51:06 +01:00
Filippo Gentile 6580fe5c31 CMake: remove platform_utils.h deprecated by new schema 2021-11-29 23:44:30 +01:00
Filippo Gentile 0aedffc089 SessionStartEndModel: adapt query to new schema 2021-11-29 23:42:43 +01:00
Filippo Gentile 4fc2fa150b SessionRSWriter: adapt query to new schema 2021-11-29 23:42:13 +01:00
Filippo Gentile ffd831feaa RSCoupleDialog: adapt query to new schema 2021-11-29 23:41:58 +01:00
Filippo Gentile 73fbec395e RSCouplingInterface: fix query and new isRailwayElectrified()
Do not use old LineType enum
2021-11-29 23:33:22 +01:00
Filippo Gentile bbd0966487 StopModel: fix line type detection, use segments 2021-11-29 23:30:43 +01:00
Filippo Gentile 13d4d9f845 StationFreeRSModel: fix query and use insertSorted() 2021-11-29 23:30:05 +01:00
Filippo Gentile 389e66d51e RsPlanModel: adapt query to new schema 2021-11-29 23:29:31 +01:00
Filippo Gentile eea39e9204 LineGraphManager: update selection when job changes ID or category 2021-11-29 22:35:39 +01:00
Filippo Gentile 3955129f7a LineGraphScene: make updateJobSelection() static 2021-11-29 22:26:22 +01:00
Filippo Gentile 215dc2881c StopModel: calc travel times and set station on new stops 2021-11-29 22:12:17 +01:00
Filippo Gentile b43db5bbae StopModel: fix setStopTypeRange() 2021-11-29 20:57:15 +01:00
Filippo Gentile 0ffa6e9fe4 StopModel: fix setStopType() 2021-11-29 20:54:44 +01:00
Filippo Gentile 6ccb9c32ef RSCouplingInterface: adapt query to new schema 2021-11-29 20:51:00 +01:00
Filippo Gentile 7cb0c944cf StopCouplingModel: adapt query to new schema 2021-11-29 20:50:40 +01:00
Filippo Gentile 00dd49a54d TrainAssetModel: adapt query to new schema 2021-11-29 20:50:20 +01:00
Filippo Gentile aa127229c9 JobPassingsModel: platform name is string now 2021-11-29 20:49:56 +01:00
Filippo Gentile d63648ba0b EditStopDialog: fix calcPassings() 2021-11-29 20:49:28 +01:00
Filippo Gentile 1a4c9b40dd StationWriter: adapt query to new schema 2021-11-29 20:43:45 +01:00
Filippo Gentile 40267968c7 StationPlanModel: adapt query to new schema 2021-11-29 20:43:23 +01:00
Filippo Gentile 1ee033b165 JobsHelper: fix job removal 2021-11-29 20:01:50 +01:00
Filippo Gentile 814dfb4aab RsErrorsWidget: fix missing include QEvent 2021-11-29 12:27:05 +01:00
Filippo Gentile 2b93b5597f LineGraphToolbar: fix missing include QEvent 2021-11-29 12:21:16 +01:00
Filippo Gentile ec78cdfde7 MeetingSession: remove deprecated stop methods and queries 2021-11-29 12:19:04 +01:00
Filippo Gentile 9c6c01b1db EditStopDialog, JobWriter, StationWriter: use JobStopDirectionHelper 2021-11-29 12:18:47 +01:00
Filippo Gentile e45ac59bd3 JobStopDirectionHelper: helper for selecting stop direction
Replaces deprecated method MeetingSession::getStopDirection()
2021-11-29 12:17:38 +01:00
Filippo Gentile 54f19c494b OdtDocument: fix manifest.xml file path
This fixes bug of archive not created on document save
2021-11-25 23:54:04 +01:00
Filippo Gentile 6b045e9dee OdtDocument: use langua bcp47 name 2021-11-25 23:44:39 +01:00
Filippo Gentile a3b3625b1c OdtDocument: avoid Qt container detaching 2021-11-25 22:42:48 +01:00
Filippo Gentile 845fa5eb8e JobWriter: fix query and column type check 2021-11-25 22:29:52 +01:00
Filippo Gentile 989b423b2d JobWriter: adapt queries to new schema
- Remove platform_utils.h usage
2021-11-25 22:16:57 +01:00
Filippo Gentile 9fbfc59d15 ShiftSheetExport: pass database, do not use query as member
- Fix member variable name
2021-11-25 21:50:40 +01:00
Filippo Gentile c88fbd4135 LineGraphSelectionHelper: fix comment 2021-11-25 21:40:32 +01:00
Filippo Gentile 7b26f40fd6 MeetingSession: stationPlanChanged() emit once with multiple stations 2021-11-25 20:11:01 +01:00
Filippo Gentile c8057c3b4e StopModel: remove old line concept 2021-11-25 19:54:51 +01:00
Filippo Gentile 4ae39ea8da StopModel: startStopsEditing() when setting info from StopEditor 2021-11-25 19:25:46 +01:00
Filippo Gentile d5ea9ebc3d StopModel: remove ol platform code 2021-11-25 19:10:35 +01:00
Filippo Gentile 488945abd9 StopModel: fix loop and update view 2021-11-25 18:14:03 +01:00
Filippo Gentile b43fbe07f0 BackgroundHelper: make selected stops more visible if arrival=departure 2021-11-25 17:58:17 +01:00
Filippo Gentile 222089931a LineGraphScene: make job stop easily selectable 2021-11-25 17:57:58 +01:00
Filippo Gentile 40c9816b60 StopModel: update stop time in separate function 2021-11-25 17:27:13 +01:00
Filippo Gentile ec71cf956d StopEditor: sync UI time value on saving 2021-11-25 17:26:18 +01:00
Filippo Gentile 779a5914b6 StopModel: change default field value, reset struct with braces
- Do not propagate segment change if it didn't really change
2021-11-25 16:35:11 +01:00
Filippo Gentile 635ca16499 StopModel: fix query result check updateCurrentInGate() 2021-11-25 13:20:22 +01:00
Filippo Gentile a45e0712d2 StopModel: fix trySelectTrackForStop() query result type 2021-11-25 13:14:34 +01:00
Filippo Gentile 3025d29467 StopModel: revertChanges() revert previous commit.
StopEditing mode doesn't need to reset job info manually
2021-11-25 13:08:27 +01:00
Filippo Gentile b5d4a9acfd StopEditor: set track when loading stop 2021-11-25 13:06:01 +01:00
Filippo Gentile 0cca7a0838 StopModel: store new in gate and track ID 2021-11-25 13:01:20 +01:00
Filippo Gentile 2c215ab362 StopModel: fix query bind parameter 2021-11-25 12:39:28 +01:00
Filippo Gentile b0ca3bec24 StopEditor: select next segment 2021-11-25 12:34:38 +01:00
Filippo Gentile d73cec4a63 StopModel: fix Last stop type and trySelectNextSegment() 2021-11-25 12:34:25 +01:00
Filippo Gentile 8b11ea1aab StopModel: fix query and revertChanges() 2021-11-23 19:44:40 +01:00
Filippo Gentile 748fc8e0a5 StopEditor: add track edit to layout 2021-11-23 14:19:59 +01:00
Filippo Gentile b6beb19f13 StopEditor: use StopModel
- StopModel: fix query and assist StopEditor in choosing track
2021-11-23 13:35:06 +01:00
Filippo Gentile 7a67d5b446 JobListModel: fix typedef not needed 2021-11-23 13:31:42 +01:00
Filippo Gentile fd9ffe4d09 Merge branch 'refs/heads/master' into feature/new_job_system 2021-11-23 12:03:12 +01:00
gfgit 06ee85eaca
Merge pull request #34 from gfgit/feature/dialogs_qpointer_autodelete
Feature/dialogs qpointer autodelete
QDialog should be allocated on heap because if parent widget gets deleted while dialog is still open, being a children it will be deleted too.
Use OwningQPointer to track and delete dialog pointers
2021-11-23 11:57:06 +01:00
Filippo Gentile 020819dd46 QFileDialog: use OwningQPointer, allocate on heap like other dialogs 2021-11-21 23:50:55 +01:00
Filippo Gentile ab2773adb4 OwningQPointer: use it for QDialog and subclasses 2021-11-21 23:25:13 +01:00
Filippo Gentile deadbe8a7c OwningQPointer: a QPointer which deletes object upon destruction
Very useful for QDialog because they sould be allocated on heap because
the event loop could delete them.
2021-11-21 22:52:30 +01:00
Filippo Gentile d0039d1760 JobPathEditor: show station SVG plan from context menu 2021-11-21 12:35:21 +01:00
Filippo Gentile e46f35a9a1 StopModel: fix createStop() arguments 2021-11-21 12:08:06 +01:00
Filippo Gentile fd90fbc188 ViewManager: fix member initialization order 2021-11-21 12:07:49 +01:00
Filippo Gentile 6e3917824c JobListModel: use new IPagedItemModelImpl base class 2021-11-21 12:07:27 +01:00
Filippo Gentile 336c3c9f68 Merge branch 'master' into feature/new_job_system 2021-11-21 11:39:55 +01:00
gfgit acccf2df00
Merge pull request #33 from gfgit/feature/paged_model_base_class
Reduce code duplication on IPagedItemModel sublasses:

-  Move more code to common IPagedItemModel base class
-  Prevent infinite loops when loading returns less rows than expected and the model continues trying reloading

Resolves #32
2021-11-21 11:22:46 +01:00
Filippo Gentile 5a2d10c885 RollingstockSQLModel, RSModelsSQLModel, RSOwnersSQLModel:
IPagedItemModelImpl
2021-11-21 11:21:06 +01:00
Filippo Gentile ac4b11e084 IPagedItemModel: remove setSortingColumn() from subclasses if not used 2021-11-21 10:52:23 +01:00
Filippo Gentile dff063ac93 StopModel: adapt createStop() 2021-11-21 10:30:28 +01:00
Filippo Gentile 59d14b1759 Model roles: remove unused ones 2021-11-20 21:25:34 +01:00
Filippo Gentile 3c0f4ec113 TrainGraphics: removed deprecated class 2021-11-20 21:22:16 +01:00
Filippo Gentile daff553e1b StopModel: consider also reversed segments 2021-11-20 21:02:27 +01:00
Filippo Gentile c94496c030 StationsMatchModel: move back to signle filter 2021-11-20 20:55:13 +01:00
Filippo Gentile 2a64622489 LineGraphSelectionHelper: fix requestCurrentJobNextSegmentVisible() 2021-11-20 20:28:02 +01:00
Filippo Gentile 17246ba99f MainWindow: allow absolute move next/prev job segment with shift
modifier
2021-11-20 20:27:47 +01:00
Filippo Gentile 0b0e1de6a4 Revert "StationsMatchModel: filter also by previous station."
This reverts commit d098c70c0f.
2021-11-20 20:14:04 +01:00
Filippo Gentile 00d3cbca4c Merge remote-tracking branch 'refs/remotes/origin/feature/
graph_next_prev_segment' into feature/new_job_system
2021-11-20 19:27:34 +01:00
Filippo Gentile 079ef099ae LineGraphSelectionHelper: implement next/prev job selection
LineGraphSelectionHelper::SegmentInfo: renamed members
2021-11-20 17:43:48 +01:00
Filippo Gentile 4e27294446 MeetingSession: remove old GraphManager include 2021-11-20 17:42:46 +01:00
Filippo Gentile e017f8e9d2 Remove deprecated class GraphManager 2021-11-20 16:53:36 +01:00
Filippo Gentile 192b6432a7 ViewManager: remove deprecated GraphManager, use LineGraphManager 2021-11-20 16:51:14 +01:00
Filippo Gentile 13573d4a56 MainWindow: remove deprecated GraphManager, use LineGraphManager 2021-11-20 16:48:57 +01:00
Filippo Gentile 911724e6c8 LineGraphManager: handle active scene job selection 2021-11-20 16:48:01 +01:00
Filippo Gentile 3caa54f84f ViewManager: use LineGraphSelectionHelper for next/prev segments 2021-11-20 16:25:11 +01:00
Filippo Gentile 9f63c1c570 LineGraphSelectionHelper: fix query returning NULL
- Initial next/prev segment implementation
2021-11-20 16:24:04 +01:00
Filippo Gentile d004ae2527 LineGraphScene: add a margin to requested show zone 2021-11-20 15:52:46 +01:00
Filippo Gentile 0a665385d8 LineGraphSelectionHelper: fix query again 2021-11-20 15:47:58 +01:00
Filippo Gentile 7ca5765873 LineGraphManager: implement follow job selection 2021-11-20 15:27:02 +01:00
Filippo Gentile 3db6cb896e LineGraphSelectionHelper: fix queries and avoid following selection 2021-11-20 15:26:48 +01:00
Filippo Gentile c7cba33a5f ViewManager: remove old code for job selection 2021-11-20 15:26:16 +01:00
Filippo Gentile a1b473399e LineGraphScene: allow to not emit signals on selection change
graphChanged() added pointer to self as last argument
2021-11-20 15:25:52 +01:00
Filippo Gentile 35ac28759a MainWindow: use completionDone() for search edit
This signal is emitted even if user selects again the same job, this way
every time user selects a job, it is shown on the graph.
2021-11-20 14:23:10 +01:00
Filippo Gentile aa223182ba LineGraphScene: fix requestShowZone() left edge was 0 2021-11-20 14:22:02 +01:00
Filippo Gentile e01ce21ab4 LineGraphView: fix ensureVisible() avoid headers zone 2021-11-20 14:05:35 +01:00
Filippo Gentile 246533feff LineGraphManager: implement sync job selection on all scenes 2021-11-20 12:55:39 +01:00
Filippo Gentile 2c5f84b922 LineGraphView: fix function call and comment ensureVisible() 2021-11-20 12:53:23 +01:00
Filippo Gentile 5a4a6bcc0b ViewManager: use LineGraphSelectionHelper 2021-11-20 12:52:44 +01:00
Filippo Gentile 7cea95eb58 LineGraphSelectionHelper: new helper class for job selection 2021-11-20 12:52:17 +01:00
Filippo Gentile 7deb58c377 LineGraphScene: emit also selected job category and stopId
- renamed setSelectedJobId() to setSelectedJob()
- signal jobSelected() added arguments.
2021-11-20 12:39:43 +01:00
Filippo Gentile 734be8b34c MRTPSettings, SettingsDialog: add follow and sync job selection options 2021-11-20 11:40:35 +01:00
Filippo Gentile 759fe09e34 JobPathEditor: restore job show request on save 2021-11-19 22:29:24 +01:00
Filippo Gentile 75145c5e90 ViewManager: implement request to show job in graph 2021-11-19 22:27:51 +01:00
Filippo Gentile 80f1c1ba97 LineGraphView: react to scene zone requests 2021-11-19 22:19:51 +01:00
Filippo Gentile 4a2ec7ecfc LineGraphScene: new function to request showing a zone
Request to ensure an item is visible between a period of time.
2021-11-19 22:16:59 +01:00
Filippo Gentile 170e234e62 ViewManager: initial job selection implementation 2021-11-19 17:13:01 +01:00
Filippo Gentile 06e04a142c MeetingSession: fix a comment on database jobsegment table 2021-11-19 17:12:39 +01:00
Filippo Gentile d38898ee8e LineGraphManager: ensure active scene is registered.
Only registered scenes can become active.
First registered scenes is set as active.
When resetting active scene, try to activate our first registered scene.
2021-11-19 16:47:55 +01:00
Filippo Gentile 25abd16e23 LineGraphWidget: getters for scene, view and toolbar 2021-11-19 16:30:08 +01:00
Filippo Gentile 9395434918 LineGraphManager: new activate scene concept
When multiple views/scenes are opened, record the last focused one to
have a target for actions like 'Show Job in graph' or 'Show Next/Prev
Job Segment'.
We implement this by catching focusInEvent() on LineGraphView,
LineGraphToolbar and its children.
2021-11-19 16:24:08 +01:00
Filippo Gentile 162f3acabc StationTrackConnectionsModel: use new IPagedItemModelImpl base class 2021-11-19 14:44:24 +01:00
Filippo Gentile aee947aa79 StationGatesModel: use new IPagedItemModelImpl base class 2021-11-19 14:29:15 +01:00
Filippo Gentile ab40eda802 RailwaySegmentsModel: update model when station changes name 2021-11-19 13:23:30 +01:00
Filippo Gentile 1a070c628f JobsSQLModel: use clearCache_slot() from base class 2021-11-19 13:23:00 +01:00
Filippo Gentile 18aea4a02c IPagedItemModel: add slot version of clearCache() 2021-11-19 13:21:52 +01:00
Filippo Gentile a757cc4e00 RailwaySegmentsModel: use new IPagedItemModelImpl base class 2021-11-19 12:27:40 +01:00
Filippo Gentile e575f4bc6e StationTracksModel: use new IPagedItemModelImpl base class 2021-11-18 17:28:41 +01:00
Filippo Gentile b8a1a94194 StationsModel: use new IPagedItemModelImpl base class 2021-11-18 16:39:03 +01:00
Filippo Gentile baed3f75d1 LinesModel: use new IPagedItemModelImpl base class 2021-11-18 15:59:20 +01:00
Filippo Gentile 8c42d1222b IPagedItemModelImpl: move constructor to implementation file 2021-11-18 15:24:32 +01:00
Filippo Gentile 41361a35d7 IPagedItemModel: initialize sortColumn member to 0 2021-11-18 15:13:05 +01:00
Filippo Gentile e7e1778d2e IPagedItemModelImpl: avoid infinite loops on fetch error
Fix infinite loops when fetching returns less rows than expected.
Add fake remaining rows to avoid triggering again fetching.
2021-11-18 14:56:47 +01:00
Filippo Gentile b1561dd692 IPagedItemModelImpl: postResult() new function
Posting results is common to many subclasses so make a common function
to avoid duplicating code and hide internal event system.
2021-11-18 14:19:37 +01:00
Filippo Gentile e14fec00c5 IPagedItemModel: add default setSortingColumn() implementation
Default implementation does nothing.
2021-11-18 14:18:10 +01:00
Filippo Gentile 5c54358ddf IPagedItemModelImpl: fix compile error
ModelItemType must be passed as template parameter.
It cannot be a typedef from SuperType because SuperType is still
an incomplete type when class is declared.

ResultEvent: make member public
2021-11-18 14:06:58 +01:00
Filippo Gentile 3b3ac6b140 IPagedItemModelImpl: move implementation to separate header 2021-11-18 11:10:29 +01:00
Filippo Gentile 87d270d299 CMake: rename pageditemmodelimpl.h to pageditemmodelhelper.h
And rename pageditemmodelimpl.cpp to pageditemmodelhelper_impl.h
2021-11-18 11:07:38 +01:00
Filippo Gentile 3940fa0d81 IPagedItemModelImpl: implement standard fetchRow()
Prevent infinite loop if fetching returns less rows than expected.
2021-11-18 10:59:15 +01:00
Filippo Gentile e8ff9800a1 IPagedItemModelImpl: use only 1 template parameter 2021-11-18 09:42:59 +01:00
Filippo Gentile 9edf582aa7 IPagedItemModelImpl: implement basic functions 2021-11-18 01:01:07 +01:00
Filippo Gentile ad78dae306 IPagedItemModelImpl: fix constructor missing initializers 2021-11-18 00:56:18 +01:00
Filippo Gentile ffc323c991 IPagedItemModelImpl: implement event handling 2021-11-18 00:54:53 +01:00
Filippo Gentile b2b7568453 IPagedItemModelImpl: add ResultEvent class 2021-11-18 00:44:36 +01:00
Filippo Gentile e5d4712816 IPagedItemModelImpl: new helper class for IPagedItemModel 2021-11-18 00:32:06 +01:00
Filippo Gentile e6968fd366 JobListModel: fix load query causing infinite loop 2021-11-18 00:19:48 +01:00
Filippo Gentile 638282f5ff StopModel: fix load consistency check 2021-11-18 00:08:29 +01:00
Filippo Gentile 3c71578df4 JobListModel: fix load query causing infinite loop 2021-11-18 00:03:09 +01:00
Filippo Gentile 51f7e63528 StopModel: fix load stop query column numbers 2021-11-17 23:43:53 +01:00
Filippo Gentile 9787644278 LineGraphScene: fix job segment selection
Store previous segment distance
2021-11-17 23:40:03 +01:00
Filippo Gentile c7e9627bd4 StopModel: fix load stop query column numbers 2021-11-17 23:35:30 +01:00
Filippo Gentile 64b7390fb7 LineGraphScene: allow to select jobs by clicking on segments 2021-11-17 22:28:27 +01:00
Filippo Gentile 99bddaeb11 LineGraphScene: updateJobSelection on loading 2021-11-17 18:48:45 +01:00
Filippo Gentile b713517a7b JobStopEntry, JobStop: use C++ style, correct initializers 2021-11-17 18:47:51 +01:00
Filippo Gentile 7104c596f3 StopModel: do not check stop segment on removal, it's deprecated 2021-11-17 18:12:34 +01:00
Filippo Gentile 7c6fe5c999 StopModel: do not check stop segment on removal, it's deprecated 2021-11-17 18:11:10 +01:00
Filippo Gentile e0a9428816 LineGraphScene: redraw graph on job selection change 2021-11-17 18:02:25 +01:00
Filippo Gentile f165928a57 BackgroundHelper: draw aura around selected job
PrintWorker: never draw selection
LineGraphView: always draw selectio if any

Selected job segments and stops get drawn with a semi-transparent border
around them.
2021-11-17 18:01:21 +01:00
Filippo Gentile 60725206f7 Merge branch 'new_station_schema2' 2021-11-17 16:11:46 +01:00
Filippo Gentile 9f5777cbbd Merge branch 'feature/svg_station_plan' into new_station_schema2 2021-11-17 16:01:40 +01:00
Filippo Gentile 82c77f7dd1 Merge branch 'new_station_schema2' into feature/svg_station_plan 2021-11-17 16:01:01 +01:00
Filippo Gentile be74a2d53f Merge branch 'refs/heads/develop' 2021-11-12 22:43:56 +01:00
Filippo Gentile 38c4f2ade4 StopModel: fix backup/restore, removed jobsegments
Fix queries in general
2021-11-02 15:07:58 +01:00
Filippo Gentile 1981a876d0 StopModel: fix loading stop. LEFT JOIN to include NULL fields 2021-11-02 14:51:58 +01:00
Filippo Gentile fa4ef443e2 JobPathEditor: temporary disable job selection until reworked 2021-11-02 14:51:04 +01:00
Filippo Gentile c61a8c5fbe JobsHelper: jobsegments table does not exist anymore 2021-11-02 14:37:46 +01:00
Filippo Gentile bc111d78f2 LineGraphScene: fix comment 2021-11-02 14:36:58 +01:00
Filippo Gentile 328df9ddfa EditRailwaySegmentDlg: fix station filter 2021-11-02 14:34:18 +01:00
Filippo Gentile 4db438fcd1 ChooseSegmentDlg: fix station filter 2021-11-02 14:33:21 +01:00
Filippo Gentile d701528fac EditStopDialog: fill layout with new widgets 2021-11-02 14:28:22 +01:00
Filippo Gentile 535aa3d802 EditStopDialog: prev station widgets are now labels 2021-11-02 13:45:23 +01:00
Filippo Gentile 4466278a3a JobPathEditor: remove popupEditorLinesCombo() system
Not needed anymore. Remove also from StopDelegate
2021-11-02 13:44:45 +01:00
Filippo Gentile 42261fd663 MRTPSettings: remove 'ChooseLineOnAddStop' setting
SettingsDialog: remove associated field
2021-11-02 13:43:53 +01:00
Filippo Gentile 925b860f8c LineGraphSelectionWidget: fix station filter 2021-11-02 13:34:19 +01:00
Filippo Gentile 9fd0b36ae8 StopModel: remove deprecated line helper 2021-11-02 13:32:41 +01:00
Filippo Gentile 2b16295eba EditStopDialog: removed infoLabel and other old fields 2021-11-02 13:32:27 +01:00
Filippo Gentile 5c187ad389 CMake: remove 'line' subdirectory
It was not used anymore because it's classes were deprecated in favour
of 'stations' subdirectory
2021-11-02 13:13:26 +01:00
Filippo Gentile 46b68df652 EditStopDialog: renamed labels and layouts 2021-11-02 13:07:30 +01:00
Filippo Gentile b4176b1ba4 EditStopDialog: removed Time Tab, new Stop Tab 2021-11-02 12:44:23 +01:00
Filippo Gentile 082f3e3715 StationsMatchModel: fix switch statment missing break 2021-11-02 12:43:05 +01:00
Filippo Gentile 5a12eb7229 EditStopDialog: new Rollingstock Tab
Moved Coupled/Uncoupled and Train Asset Before/After stop to this tab
2021-11-02 12:15:34 +01:00
Filippo Gentile 8b88832140 StopModel: updateCurrentInGate() new function 2021-11-02 12:03:27 +01:00
Filippo Gentile bdee972a47 StopModel: new setStopInfo() function 2021-11-02 11:42:02 +01:00
Filippo Gentile 6901e906a2 StopDelegate: use new StopModel function 2021-11-02 11:41:41 +01:00
Filippo Gentile e8e0fa2adc StopEditor: fix time calculation and remove old functions 2021-11-02 11:41:15 +01:00
Filippo Gentile f898dbc3f4 StopEditor: use new match models 2021-11-02 10:18:14 +01:00
Filippo Gentile 656edd5a74 CustomCompletionLineEdit: add signal to get indexSelected 2021-11-01 20:06:57 +01:00
Filippo Gentile d098c70c0f StationsMatchModel: filter also by previous station.
Show segment when filter is enabled.
2021-11-01 20:01:13 +01:00
Filippo Gentile abfa3b7983 StationLinesListModel: remove deprecated model
Use RailwaySegmentMatchModel
2021-11-01 20:00:38 +01:00
Filippo Gentile a43baa3126 StopModel: new item structure, add consistency checks 2021-11-01 17:55:03 +01:00
Filippo Gentile eb1d798212 StopEditor: fix typo in comment 2021-11-01 17:54:17 +01:00
Filippo Gentile 7d140c0e31 StopModel: remove unused field possibleLine 2021-10-31 12:39:01 +01:00
Filippo Gentile 15adfd545a StopModel: fix stop loading query 2021-10-31 12:36:59 +01:00
Filippo Gentile 6b8d815af5 JobsHelper, JobListModel: fix queries 2021-10-31 12:01:03 +01:00
Filippo Gentile f939acb134 CMake: fix file joblistmodel.h 2021-10-31 11:57:42 +01:00
Filippo Gentile 32c00ee77b JobListModel: rename files
Rename jobssqlmodel.h to joblistmodel.h
2021-10-31 11:53:40 +01:00
Filippo Gentile 3c24fc7d0e JobListModel: renamed from JobsSQLModel 2021-10-31 11:50:33 +01:00
Filippo Gentile 549ccf43e3 JobStorage: remove deprecated class 2021-10-31 11:48:29 +01:00
Filippo Gentile 621da16076 MeetingSession: remove dependency on JobStorage 2021-10-31 11:46:36 +01:00
Filippo Gentile 4173155df5 JobsManager: remove dependency on JobStorage 2021-10-31 11:44:01 +01:00
Filippo Gentile 5a579fd403 ViewManager: remove dependency on JobStorage 2021-10-31 11:43:54 +01:00
Filippo Gentile d4f5eff5cf StopDelegate: fix missing emit keyword 2021-10-31 11:41:39 +01:00
Filippo Gentile 0e4401da92 JobPathEditor: remove dependency on JobsManager 2021-10-31 11:41:23 +01:00
Filippo Gentile 0b24216db7 JobsHelper: new helper to add/remove jobs 2021-10-31 11:27:19 +01:00
Filippo Gentile 9ae91f5633 Move JobsManager and JobsSQLModel to own subdirectory 2021-10-31 11:16:21 +01:00
Filippo Gentile 8111753315 JobsSQLModel: adapt query and signals 2021-10-30 20:36:15 +02:00
Filippo Gentile 7ef5498f9d MeetingSession: remove old method getStationGraphPos() 2021-10-30 20:18:32 +02:00
gfgit 54cd17276b
Merge pull request #28 from gfgit/feature/new_print_wizard
Feature New Print Wizard

Rework Printing system

- Adapt worker to new rendering classes
- Reorganize wizard
2021-10-27 15:04:54 +02:00
Filippo Gentile a57ebcb36a PrintOptionsPage: rename function and use QStandardPaths 2021-10-27 15:03:29 +02:00
Filippo Gentile 058d0fc2ab PrintOptionsPage: start task when page is validated
PrintWizard: remove unused signal
2021-10-27 14:47:44 +02:00
Filippo Gentile 590cb2e335 RSImportWizard: fix task quit and remove unused functions
stop() must be called before cleanup()
2021-10-27 14:37:46 +02:00
Filippo Gentile bb98e2a4f9 RSImportWizard: fix task quit and remove unused functions
stop() must be called before cleanup()
2021-10-27 13:45:05 +02:00
Filippo Gentile 4ffd245f94 PrintOptionsPage: set commit page 2021-10-27 13:44:07 +02:00
Filippo Gentile ba37234b63 PrintWizard: handle PrintWorker 2021-10-27 13:43:48 +02:00
Filippo Gentile 2f611ed1c7 PrintProgressPage: remove worker handling
Task will be handled directly by PrintWizard
2021-10-27 13:40:51 +02:00
Filippo Gentile 2d3db05020 PrintWorker: subclass IQuittableTask for using QThreadPool 2021-10-27 13:39:38 +02:00
Filippo Gentile 39678fbdc9 PrintOptionsPage: set button text to 'Print' 2021-10-27 11:15:40 +02:00
Filippo Gentile 9bcfd591a7 PrintOptionsPage: check if page is complete when changing type 2021-10-27 11:12:35 +02:00
Filippo Gentile 112c9dd028 PrintOptionsPage: add outputTypeCombo to layout
Rename onOutputTypeChanged() to updateOutputType()
Manually update output type on page initialization.
2021-10-27 11:10:16 +02:00
Filippo Gentile 219ec6db11 PrintProgressPage: update options manually 2021-10-27 11:04:53 +02:00
Filippo Gentile 707e4b4649 PrintWizard: use new PrintOptionsPage
Remove PrintSelectionPage::nextId() function, now the wizard is not
dynamic and follows linear order so no need to set custon nexId.
2021-10-27 10:57:15 +02:00
Filippo Gentile 10a01a9cc9 PrintOptionsPage: generic print option wizard page
Delete old PrinterOptionsPage which was limited to native printig
Rename PrintFileOptionsPage to PrintOptionsPage
2021-10-27 10:55:38 +02:00
Filippo Gentile 8069a51400 PrintFileOptionsPage: make generic
Allow setting options also for native printer, no need to have a
separate wizard page
2021-10-27 10:48:39 +02:00
Filippo Gentile 36ec641ded PrintWizard: add string for OutputType 2021-10-27 10:37:14 +02:00
Filippo Gentile fe3a84b8ed BackgroundHelper: center hour labels 2021-10-26 19:52:19 +02:00
Filippo Gentile 8c3b5a2c9e BackgroundHelper: fix hour label font was too big 2021-10-26 19:30:15 +02:00
Filippo Gentile ac46bb0768 PrintSelectionPage, SceneSelectionModel: do not allow invalid values 2021-10-26 19:29:01 +02:00
Filippo Gentile 70a0c53b31 PrintSelectionPage: updateSelectionCount on init 2021-10-26 19:12:15 +02:00
Filippo Gentile 8b8bf0685f PrintSelectionPage: fix mode combobox was ignored 2021-10-26 19:08:19 +02:00
Filippo Gentile eeaadcd748 PrintWorker: fix hour panel rect 2021-10-26 19:08:01 +02:00
Filippo Gentile 9d39b6f3c6 BackgroundHelper: set font size DPI independent 2021-10-26 18:47:57 +02:00
Filippo Gentile f4ab5d9439 PrintWorker: draw hour panel 2021-10-26 18:04:05 +02:00
Filippo Gentile 7074e66216 PrintWorker: remove test code and set custom page size 2021-10-26 18:01:17 +02:00
Filippo Gentile 98ecc5561e PrintProgressPage: initialize worker to nullptr
Fixes crash if canceling process
2021-10-26 16:57:58 +02:00
Filippo Gentile 77ea3b6661 PrintWizard: allow setting file pattern
File pattern to use when printing to multiple files
2021-10-26 16:51:14 +02:00
Filippo Gentile e67061013c PrintProgressPage: is already finish, does not have to be commitPage 2021-10-26 15:52:07 +02:00
Filippo Gentile b54512045e PrintWorker: add errorOccured signal
PrintProgressPage: show errors in QMessageBox and offer option to try
again printing
2021-10-25 22:39:55 +02:00
Filippo Gentile 5e1d19904e PrintWorker: reset QSvgGenerator at every page
QPainter::end() does not clear QSvgGenerator, so the second page gets
mixed with first page contents and gets wrong XML tags which results in
broken parsing. Use a new instance of QSvgGenerator for every page.
2021-10-24 12:26:30 +02:00
Filippo Gentile 3ae46826d1 PrintWorker: fix station labels at wrong coords 2021-10-22 18:28:23 +02:00
Filippo Gentile d7634f7851 PrintWorker: fix do not initialize directly QPainter
QPainter is initialized in lambdas passed as BeginPaintFunc
2021-10-22 18:20:35 +02:00
Filippo Gentile 9db2b7fcde PrintWorker: add debug warnings 2021-10-22 18:18:16 +02:00
Filippo Gentile dd2e3d0852 PrintProgressPage: better show progress has finished 2021-10-22 18:18:04 +02:00
Filippo Gentile c322ad5358 PrintSelectionPage: use QTableView instead of QListView 2021-10-22 18:02:34 +02:00
Filippo Gentile be3e18da2a SceneSelectionModel: fix adding entries 2021-10-22 18:01:43 +02:00
Filippo Gentile 0c58f82ea4 PrintSelectionPage: use LineGraphSelectionWidget to add entries 2021-10-22 18:01:28 +02:00
Filippo Gentile a3e3c4ed5d LineGraphSelectionWidget: allow getting name
* Prevent setting object when NoGraph type is selected
2021-10-22 17:49:17 +02:00
Filippo Gentile e51a2f0c4c SceneSelectionModel: fix missing comma in constructor 2021-10-22 17:22:04 +02:00
Filippo Gentile e012561efb LineGraphSelectionWidget: new widget to select graph
LineGraphToolbar: use LineGraphSelectionWidget internally
2021-10-22 17:21:41 +02:00
Filippo Gentile d2bbf4a320 LineGraphManager: force clear graph on close 2021-10-22 17:12:45 +02:00
Filippo Gentile 3f781e1be3 MainWindow: pass database to PrintWizard 2021-10-22 15:56:58 +02:00
Filippo Gentile 9cd01a2262 PrintProgressPage: adapt to PrintWorker
Rename class from ProgressPage to PrintProgressPage.
2021-10-22 15:54:38 +02:00
Filippo Gentile ed7a03f003 PrintWizard: add database getter 2021-10-22 15:53:46 +02:00
Filippo Gentile 355f76a7e7 PrintWorker: port to LineGraphScene 2021-10-22 15:53:21 +02:00
Filippo Gentile 898a6e3ae3 PrintSelectionPage: rename class 2021-10-22 14:38:46 +02:00
Filippo Gentile 19a6b9f6b9 PrintProgressPage: use QPointer for QDialog on heap 2021-10-22 14:38:21 +02:00
Filippo Gentile 50afaa744a PrintFileOptionsPage: rename class and use PrintWizard getters 2021-10-22 14:37:58 +02:00
Filippo Gentile 71d1dceaa8 PrintWizard: add QPrinter getter
Rename pages
2021-10-22 14:37:28 +02:00
Filippo Gentile 4f07094379 Remove CheckProxyModel: deprecate class 2021-10-22 14:23:58 +02:00
Filippo Gentile 29131938b1 SelectionPage: use SceneSelectionModel 2021-10-22 14:23:17 +02:00
Filippo Gentile 0e99960d86 PrintWizard: use SceneSelectionModel 2021-10-22 14:21:59 +02:00
Filippo Gentile fa95d8fb5e SceneSelectionModel: cache selection count, notify changes 2021-10-22 13:43:39 +02:00
Filippo Gentile 79acafde1b SceneSelectionModel: fix return and add NModes enum value 2021-10-22 13:32:42 +02:00
Filippo Gentile 4445d45126 SceneSelectionModel: add mode name getter 2021-10-22 13:09:32 +02:00
Filippo Gentile cf660faa37 SceneSelectionModel: add getters and notify signal 2021-10-22 13:07:03 +02:00
Filippo Gentile 49f427d206 SceneSelectionModel: check on addEntry() and iterate
* addEntry(): check if entry is of required type and not yet added

* keepOnlyType() remove previous entries of wrong type

* startIteration(): start iterating selection
2021-10-22 13:04:37 +02:00
Filippo Gentile 7e678393d0 SceneSelectionModel: add/remove/move functionality 2021-10-22 01:32:00 +02:00
Filippo Gentile 5a049166e3 SceneSelectionModel: new model to select what to print 2021-10-22 01:17:23 +02:00
Filippo Gentile cfacea0caa LineGraphToolbar: use new LineGraphType translations 2021-10-22 01:17:03 +02:00
Filippo Gentile 0279fe0856 LineGraphType: add source file for name translation 2021-10-22 01:16:34 +02:00
Filippo Gentile 35792d0ee5 JobStorage: remove unused functions 2021-10-22 00:27:49 +02:00
Filippo Gentile 5896ca8cfe JobPathEditor: depend less on JobStorage 2021-10-22 00:26:52 +02:00
Filippo Gentile 832f2105ec MeetingSession: remove LineStorage usages 2021-10-22 00:26:15 +02:00
Filippo Gentile fa501cbd76 Remove LineStorage class: deprecated 2021-10-22 00:25:55 +02:00
Filippo Gentile 2c262728ad TrainGraphics, ProgressPage: remove LineStorage dependency 2021-10-22 00:25:32 +02:00
Filippo Gentile 3db4379f84 MeetingSession: need to clarify signal usage 2021-10-22 00:06:06 +02:00
Filippo Gentile 6c5380153f JobPathEditor, StopModel: remove LineStorage dependency 2021-10-22 00:05:42 +02:00
Filippo Gentile b21b9298ea JobsSQLModel: remove LineStorage dependency 2021-10-22 00:05:05 +02:00
Filippo Gentile 5fbe29469a ShiftGraphHolder: remove LineStorage dependency 2021-10-22 00:04:44 +02:00
Filippo Gentile 4e9f81e2c4 ViewManager: remove LineStorage dependency 2021-10-22 00:04:18 +02:00
Filippo Gentile fb527db6af GraphManager: old deprecated class, remove some code 2021-10-21 23:43:51 +02:00
Filippo Gentile db2323faba LineGraphScene: store stopId in selection
* LineGraphView: check if scene is null
* JobStopEntry: new struct for storing stopId
2021-10-21 23:11:44 +02:00
Filippo Gentile 8d2ca01dc0 LineGraphManager: update settings on MeetingSession
* Request job editing on selection changes
2021-10-21 22:39:11 +02:00
Filippo Gentile 0bde072585 CMake: fix header not listed 2021-10-21 22:17:17 +02:00
Filippo Gentile fbd469e7e6 LineGraphManager: react to item removed, clear graphs 2021-10-21 22:16:52 +02:00
Filippo Gentile b7be5a3446 LineGraphScene: move LineGraphType enum to separate header 2021-10-21 22:14:58 +02:00
Filippo Gentile 37f95fae3e MeetingSession: new signals for removed items
Emitted when removing stations, lines and segments
2021-10-21 21:59:29 +02:00
Filippo Gentile 0c7d65f198 LineGraphView: select jobs on double click, document code 2021-10-21 20:47:27 +02:00
Filippo Gentile f414477cb2 LineGraphScene: store job selection, document code 2021-10-21 20:40:03 +02:00
Filippo Gentile 4c050b4d7a Delete example classes 2021-10-21 19:56:23 +02:00
Filippo Gentile 10588aecd5 StationSVGPlanDlg: fix message box text 2021-10-21 19:30:13 +02:00
Filippo Gentile 46312eec48 StationSVGPlanDlg: allow to go to next station plan
When clicking on a label offer possibility to go to the station written
in the label.
2021-10-21 17:51:50 +02:00
Filippo Gentile 8bc3fdadd1 EditRailwaySegmentDlg: use SegmentInfo struct 2021-10-21 17:37:33 +02:00
Filippo Gentile bf17a3d7ab RailwaySegmentHelper: reorganize info in struct SegmentInfo 2021-10-21 17:34:19 +02:00
Filippo Gentile c2404ae81f ViewManager: do not create dialog if no SVG saved
If station has not an SVG plan, do not show the StationSVGPlanDlg and
instead show a QMessageBox to inform the user.
2021-10-21 16:42:39 +02:00
Filippo Gentile aba10c10ef ViewManager: fix not loading SVG 2021-10-21 16:25:15 +02:00
Filippo Gentile 005e9cce6b StationEditDialog: remove show SVG button
It's now possible to show SVG from StationsManager
2021-10-21 16:21:27 +02:00
Filippo Gentile e74b5eec52 StationsManager: show SVG station plan 2021-10-21 16:19:44 +02:00
Filippo Gentile c2835a7ae6 StationEditDialog: use ViewManager for StationSVGPlanDlg 2021-10-21 16:10:32 +02:00
Filippo Gentile f5fc4fdd09 StationSVGPlanDlg: make a non-modal QWidget 2021-10-21 16:07:12 +02:00
Filippo Gentile 95b8f2417e ViewManager: manage StationSVGPlanDlg 2021-10-21 16:06:07 +02:00
Filippo Gentile cf2f579c25 StationSVGHelper: fetch station name 2021-10-21 15:49:34 +02:00
Filippo Gentile 577430cfcd StationSVGPlanDlg: add reload button to toolbar 2021-10-21 15:40:54 +02:00
Filippo Gentile 9c37bc4baf StationEditDialog, StationSVGPlanDlg: better error reporting 2021-10-21 15:32:34 +02:00
Filippo Gentile 1ca17b3e77 StationSVGHelper: better error reporting 2021-10-21 15:26:57 +02:00
Filippo Gentile dc72d6712a ImageBlobDevice: check if already open 2021-10-21 15:15:26 +02:00
Filippo Gentile b487b8b2b4 ImageBlobDevice: better error reporting 2021-10-21 15:09:10 +02:00
Filippo Gentile 36cb11f08b StationSVGPlanDlg: move loading entirely here
* reloadDBData(): clear previous data and reload database data
* reloadSVG(): reload SVG from device
* reloadPlan(): reload everything
2021-10-21 15:02:57 +02:00
Filippo Gentile 0bdd72aff3 StationSVGPlanDlg: react to label click 2021-10-21 13:01:55 +02:00
Filippo Gentile 341aac3c1c StationSVGPlanDlg: resize and zoom to fit when loading 2021-10-21 11:56:11 +02:00
Filippo Gentile 1135393616 StationSVGHelper: fix warnings unused variables 2021-10-21 11:31:51 +02:00
Filippo Gentile a0080c7846 StationSVGHelper: fix wrong station name 2021-10-21 11:30:37 +02:00
Filippo Gentile d59f40de77 StationSVGHelper: store track ID in StationPlan 2021-10-21 10:39:49 +02:00
Filippo Gentile 9ca9d7f79d StationSVGPlanDlg: sort items and load from database 2021-10-21 10:33:28 +02:00
Filippo Gentile b9b168bb10 StationSVGHelper: load StationPlan data from database 2021-10-21 10:32:58 +02:00
Filippo Gentile 113d8071e9 POSSIBLE CRASH: use heap for QDialog and subclasses.
QPointer does NOT delete the object so it must be done manually.
2021-10-18 15:05:36 +02:00
Filippo Gentile f2dbc66b2b StationEditDialog: update SVG buttons when editing SVG.
- Fix QDialog not deleted
- Fix QIODevice not opened
2021-10-18 15:04:28 +02:00
Filippo Gentile a921e8f9cf StationSVGHelper: device is opened after resize 2021-10-18 15:03:33 +02:00
Filippo Gentile e3c7b222a4 Merge branch 'refs/heads/new_station_schema2' into feature/svg_station_plan 2021-10-18 13:47:18 +02:00
Filippo Gentile 717a96f0fa RsErrWorker: catch by reference 2021-10-18 13:46:22 +02:00
Filippo Gentile d7c461143e StationSVGPlanDlg: allow zooming 2021-10-18 13:45:36 +02:00
Filippo Gentile b9d65dc724 StationEditDialog: fix file dialog called twice 2021-10-18 12:04:37 +02:00
Filippo Gentile 74502cdcda Merge branch 'new_station_schema2' into feature/svg_station_plan 2021-10-18 11:26:46 +02:00
Filippo Gentile 0bab5fcca1 RsErrWorker: fix SQL crash
QtConcurrent doesn't support C++ exceptions so try/catch before control
returns to Qt

Fix wrong query columns which caused the exception.
2021-10-18 11:20:11 +02:00
Filippo Gentile d2bc4a3296 StationEditDialog: show SVG plan 2021-10-18 11:11:28 +02:00
Filippo Gentile 5f63abf811 StationSVGPlanDlg: new dialog to show SVG station plan 2021-10-18 10:52:59 +02:00
Filippo Gentile 3c1e4c2a3d StationSVGHelper: small optimization for file save 2021-10-18 10:28:47 +02:00
Filippo Gentile bff1990e39 StationEditDialog: add/remove/save SVG Station Plan 2021-10-18 10:26:15 +02:00
Filippo Gentile a5afa855be StationSVGHelper: new helper for handling SVG images 2021-10-18 10:09:00 +02:00
Filippo Gentile c5ef978757 ImageBlobDevice: allow to reserve space for new BLOB 2021-10-18 09:54:36 +02:00
Filippo Gentile 8262507040 ImageBlobDevice: make it more general
Remove hardcoded table name and column name.
2021-10-18 09:14:08 +02:00
Filippo Gentile 57abe1f810 StationEditDialog: react to SVG image change
StationGatesModel: add check for SVG image existance.
2021-10-18 09:05:42 +02:00
Filippo Gentile 9dc679f68f StationEditDialog: rename SVG Buttons 2021-10-18 09:04:55 +02:00
Filippo Gentile ddf0ea5642 SVG Station Plan: link to ssplib 2021-10-16 11:43:29 +02:00
Filippo Gentile 363a76d9c7 Merge branch 'master' of https://github.com/gfgit/ModelRailroadTimetablePlanner 2021-08-30 15:05:49 +02:00
Filippo Gentile cc08fd7822 CMake dalay variable expansion
Delay CMAKE_INSTALL_PREFIX expansion so it has correct value instead of
value at first CMake run time.
2021-08-30 14:48:21 +02:00
gfgit fbadf1cc7c
BUILDING fix typo 2021-08-28 10:28:25 +02:00
gfgit 4699fc85c7
Merge pull request #26 from gfgit/develop
Merge develop into new_station_schema2

* BUILDING MinGW dlltool
* LibZip: prefer config package over our Find module
* Add project website to MainWindow
2021-08-27 18:00:41 +02:00
gfgit 76c7fc01cd
Merge branch 'new_station_schema2' into develop 2021-08-27 17:59:46 +02:00
gfgit ec0d9499eb
Merge pull request #25 from gfgit/master
Merge recent changes into develop

* BUILDING MinGW dlltool
* LibZip: prefer config package over our Find module
* Add project website to MainWindow
2021-08-27 17:53:00 +02:00
gfgit b4e5de5b1e
BUILDING fix Qt path 2021-08-27 17:51:40 +02:00
gfgit 56db0b40af
BUILDING: explain libzip setup
- Move links to libraries in first section
- Explain 'libzip' setup
- Doxygen output folder
- Fix formatting
2021-08-27 17:50:30 +02:00
Filippo Gentile 8325da8959 Add project website to MainWindow
* Add AppProjectWebSite to 'info.h.in' file
* Print website in main() at application startup
* MainWindow: better about dialog
2021-08-27 17:23:28 +02:00
Filippo Gentile 49afab64d5 Merge branch 'master' of https://github.com/gfgit/ModelRailroadTimetablePlanner 2021-08-27 16:37:56 +02:00
Filippo Gentile 2850ad58f9 CMake bump version to 3.17
* LibZip: prefer config package over our Find module
* get_dll_library_from_import_library() function to get DLL library path
from imported library path. This is needed for installation
* Link LibZip according to config or find module
2021-08-27 16:37:18 +02:00
gfgit 64fc188e65
BUILDING MinGW dlltool
Explain how to manually create MinGW import libraries when they are not provided.
2021-08-27 12:03:12 +02:00
Filippo Gentile b44831f5d9 CMake fix URL
* Set github repository URL as project URL
* Set APP_HELP_URL and APP_UPDATE_URL to same project URL for now
* Explain how we include custom cmake FindXXX modules
2021-08-27 10:30:59 +02:00
Filippo Gentile 8e5bb6ddf5 CMake fix URL
* Set github repository URL as project URL
* Set APP_HELP_URL and APP_UPDATE_URL to same project URL for now
* Explain how we include custom cmake FindXXX modules
2021-08-27 10:28:56 +02:00
Filippo Gentile 5b55670f3b Rename Project Part 5 Bis
Rename the rest (files not included in master)
2021-08-27 00:49:42 +02:00
Filippo Gentile d2c77a1230 Merge remote-tracking branch 'origin/develop' into new_station_schema2 2021-08-27 00:19:41 +02:00
gfgit d2680abf97
Merge pull request #24 from gfgit/master
Rename Project add compile instructions
2021-08-27 00:01:02 +02:00
gfgit e76b632715
BUILDING.md run instructions
Add run instructions
2021-08-26 23:55:50 +02:00
gfgit eb21b0ded1
Update BUILDING.md
Add compile instructions
2021-08-26 23:53:23 +02:00
gfgit a7929f5a55
Update CONTRIBUTING.md
Link to BUILDING.md
2021-08-26 22:45:36 +02:00
Filippo Gentile 121d48092f gitignore CMake user preferences
Do not track CMakeUserPresets.json beause it is personal
2021-08-26 19:04:58 +02:00
Filippo Gentile 4d95550493 CMake Fix LibZip
Fix package finding logic and remove old hacks
2021-08-26 19:03:57 +02:00
Filippo Gentile d0faf43432 Merge branch 'master' of https://github.com/gfgit/ModelRailroadTimetablePlanner 2021-08-26 18:51:25 +02:00
Filippo Gentile be1a770087 CMake: LibZip module
Try to make it easier to find libZip headers and library
2021-08-26 16:55:18 +02:00
gfgit 75adbf8c31
Create BUILDING.md
New file to give compile instructions
2021-08-26 15:33:47 +02:00
Filippo Gentile 62185c5018 Rename Project Part 5
Rename NSIS installer variables
2021-08-26 15:14:18 +02:00
Filippo Gentile 4ce6e9372c Rename Project Part 4
Replace hard coded application name in strings. Get it from
QCoreApplication.
2021-08-26 15:13:25 +02:00
Filippo Gentile 94e8250745 Rename Project Part 3
Rename settings class
2021-08-26 15:05:34 +02:00
Filippo Gentile 456cc1a544 Rename Project Part 2
Rename translation files
Rename log and settings files
2021-08-26 15:04:54 +02:00
Filippo Gentile 7f1a0bbdbc Rename Project Part 1
Rename CMake Project
Rename CMake variable prefixes
2021-08-26 14:59:14 +02:00
gfgit 28194fc0c9
Merge pull request #23 from gfgit/develop
Merge develop into new_station_schema
2021-08-26 13:39:03 +02:00
gfgit a6e7da2395
Merge pull request #22 from gfgit/master
Merge recent cosmetic changes
2021-08-26 13:37:30 +02:00
gfgit e15a29283e
Merge pull request #8 from gfgit/new_job_graph
New job graph is complete
2021-08-26 13:33:49 +02:00
Filippo Gentile 6918fb0b10 LineGraphScene: fix tooltip logic 2021-08-26 12:06:48 +02:00
Filippo Gentile 3b8d4f1cc6 Load job segments and draw them
JobLineGraph: moved from StationGraphObject to LineGraphScene
JobLineGraph: renamed to JobSegmentGraph

LineGraphScene: do not warn on last stop if tracks do not match
LineGraphScene: fix job segment query
2021-08-26 11:45:24 +02:00
Filippo Gentile 6e954c4585 Add Doxygen comments to graph folder classes
Document classes in the 'src/graph' directory
2021-08-25 17:50:02 +02:00
Filippo Gentile 433216d666 JobStopGraph: rename occurrencies 2021-08-25 17:05:06 +02:00
Filippo Gentile 04159bbca2 Add struct JobLineGraph to draw moving job graph
* Rename job stop graph to JobStopGraph

* Document code
2021-08-25 17:04:46 +02:00
gfgit d3c7d5057c
Update README_it.md
Better headings for italian README to match English one.
Theese are just cosmetic commits because I cannot code right now but will be soon.
2021-08-12 21:43:08 +02:00
gfgit 4903486b46
Update README.md
Better headings
2021-08-12 21:31:35 +02:00
gfgit 294db0a153
Update CONTRIBUTING.md
Better heading
2021-08-12 21:27:28 +02:00
gfgit d034e015c3
Update CONTRIBUTING.md
Add instructions for translating UI in new languages
2021-08-12 17:27:23 +02:00
gfgit c6de565034
README Italian
Update italian translation to match English README
2021-08-12 11:39:05 +02:00
gfgit 23ec490220
README Mention cross-platform
- Mention cross-platform
- Use more optimistic language
- State available UI languages
2021-08-12 11:15:11 +02:00
gfgit 21d8822772
Link italian README
Link italian translation of README from English version
2021-08-10 09:52:12 +02:00
gfgit 1477520955
Link English README
Add a link i side italian README which points back to English version
2021-08-10 09:50:25 +02:00
gfgit 179bce8779
Update README_it.md
Complete italian translation of README
2021-08-10 09:44:25 +02:00
gfgit f6740539c9
Create README_it.md
Create initial italian translations of README file
2021-08-10 09:32:34 +02:00
gfgit f4f2698176
README add feature list
Add list of main features and explain briefly project history. Fix CONTRIBUTING link.
2021-08-09 15:11:26 +02:00
gfgit 09e77c3fdd
Update and rename CONTRIBUTING to CONTRIBUTING.md
Use *.md extension and split contento i ti sections.
2021-08-09 14:58:19 +02:00
gfgit 5b80b89e4c
Rename README to README.md
Github needs *.md extension to render markdown.
2021-08-09 14:46:45 +02:00
gfgit cf49d6101e
Use markdown in README
Use markdown formatting for better readability.
2021-08-09 14:45:26 +02:00
gfgit bfc6138725 Update issue templates 2
Re ove smartphone part be cause it is not useful. Use labels.
2021-08-07 12:44:25 +02:00
gfgit 9a160df919 Update issue templates
Follow github suggestion for issue templates for now.
2021-08-07 12:30:39 +02:00
gfgit 1fbdb7c64c
Update README
Fix typo
2021-08-05 23:27:42 +02:00
gfgit 944955751c
Update README
Reflect New project name and clarify goals.
2021-08-04 19:21:06 +02:00
Filippo Gentile ac67f4f40f Initial job segment graph 2021-07-20 15:10:18 +02:00
Filippo Gentile e8c49130cf LineGraphScene: support tooltips on jobs 2021-07-20 13:36:35 +02:00
Filippo Gentile 404796d02e LineGraphScene: fix job stop query
stops.in_gate_conn might be NULL for first job stop
stops.out_gate_conn might be NULL for last job stop
Use LEFT JOIN to prevent excluding the stop.
2021-07-20 13:02:19 +02:00
Filippo Gentile ad00c6da7f LineGraphScene: load job stops
LineGraphScene: remove friendship to LineGraphView, not needed anymore
BackgroundHelper: draw visible job stops.
2021-07-20 12:41:43 +02:00
Filippo Gentile 5ba6d1f1df BackgroundHelper: draw also stations
Draw only visible stations, skip others.
2021-07-20 11:52:07 +02:00
Filippo Gentile b38e98ff0a StationLabelsHeader: paint only exposed regions 2021-07-20 11:10:14 +02:00
Filippo Gentile b6d208f939 BackgroundHelper: common drawing class
Move drawing code to common helper class so it can be reused when
printing to PDF or QPrinter.
2021-07-20 11:04:50 +02:00
Filippo Gentile 39081f8a4f Remove some old code part 3
Fix code still referencing removed parts.
Many of theese fixes are just made to be able to compile, as many of
this code will soon be removed too.
2021-07-20 10:43:29 +02:00
Filippo Gentile 864e9a158c Remove some old code part 2 2021-07-20 10:42:11 +02:00
Filippo Gentile 3efaeec029 Remove some old code 2021-07-20 10:16:10 +02:00
gfgit 9e279119c6
Merge pull request #7 from gfgit/new_station_graph
Initial commit Station Graph
2021-07-20 00:08:34 +02:00
Filippo Gentile c4000267a3 Schema Diagram PDF
Added diagram with old_* tables excluded for clarity.
I.e. old_stops just mirrors stops so do not put it in diagram.
2021-07-20 00:03:50 +02:00
Filippo Gentile fe5fa9994e Schema Diagram PDF
New database schema diagram generated by SchemaCrawler.
2021-07-19 23:48:34 +02:00
Filippo Gentile 26568cb6a8 LineGraphScene: reorder function for better code readability 2021-07-19 23:01:17 +02:00
Filippo Gentile 3bf45b00d8 LineGraphView: missing nullptr initialization 2021-07-19 22:38:50 +02:00
Filippo Gentile 887be8275a Merge branch 'new_station_graph' of ../../git_repo_origin_bare/TrainTimetable_GIT_master_ into new_station_graph 2021-07-19 22:23:36 +02:00
Filippo Gentile 4ff710f305 Revert "LineGraphView: attempt to drawBackgroundHourLines"
This reverts commit 77d1410b4e.
2021-07-19 12:15:00 +02:00
Filippo Gentile 36c5c3c6c2 LineGraphScene: rework positioning
Now horizOffset is already included in station xPos
And also half station offset on left to searate first station from
HourPanel.
2021-07-19 11:55:48 +02:00
Filippo Gentile c6e0027edf TrainTimetableSettings: tweak default platform offset value 2021-07-19 11:53:52 +02:00
Filippo Gentile cdfb447800 LineGraphView: drawBackgroundHourLines
Draw visible hour lines in the background.
2021-07-19 11:38:29 +02:00
Filippo Gentile 77d1410b4e LineGraphView: attempt to drawBackgroundHourLines
Not working. Needs translating rect argument.
2021-07-18 23:27:25 +02:00
Filippo Gentile 95a5aa0391 LineGraphManager: react to settings changes
Listen setting for changes and reload all scenes
2021-07-18 23:19:08 +02:00
Filippo Gentile 979c181c2a HourPanel: get color from settings
Listen settings for changes and update consequently.
2021-07-18 23:15:45 +02:00
Filippo Gentile 35b86a6696 LineGraphView, StationLabelsHeader: center labels
Center station labels on station platforms and leave half stationOffset
on left and right borders of graph to give extra space for last station
label and to separate a bit first station from HourPanel.
2021-07-18 23:05:45 +02:00
Filippo Gentile 76e78877df LineGraphScene: handle size calculation
Do not calculate size in LineGraphView, do it in scene.
2021-07-18 22:52:17 +02:00
Filippo Gentile 451bd9c488 LineGraphView: preserve box frame
LineGraphView is a subclass of QAbstractScrollArea which is a subclass
of QFrame.
QFrame draws a box around widget contents but we cover top and left
borders with our custom headers.
Move the headers away.
2021-07-18 22:33:47 +02:00
Filippo Gentile 3f4e8c28b5 LineGraphView: fix background updates
Update viewport instead of the scroll view (self) to prevent glitches on
clearing graph.
No need to manually paint background for now.
Set background to white for a better contrast with the graph and to be
closer to printed output.
2021-07-18 22:19:58 +02:00
Filippo Gentile 0ddafd2af8 LineGraphWidget: rename viewport to view 2021-07-18 21:58:04 +02:00
Filippo Gentile 78a725c5b2 LineGraphToolbar: make toolbar vertically smaller 2021-07-18 21:57:49 +02:00
Filippo Gentile 26c3ea47c7 LineGraphView: new class to paint scene
Subclass of QAbstractScrollArea which is a bit more efficient than
having a custom QWidget (formet LineGraphViewport) inside a QScrollArea.
QScrollArea was already subclassed in LineGraphScrollArea to add headers
(HourPanel and StationLabelsHeader).

LineGraphView replaces both LineGraphViewport and LineGraphScrollArea in
a single class and manages headers.
2021-07-18 21:48:45 +02:00
Filippo Gentile 2dee27b9c5 StationsModel: emit also on short-name changes 2021-07-17 16:32:49 +02:00
Filippo Gentile ae3bb8b7ac LineGraphManager: new class to manage graphs
Single instance member of ViewManager.
Central place which listens to notifications about railway layout
changes from MeetingSession and decides which graphs needs to be
refreshed.
It is also a central place to clear all graph before closing database.
All scenes must be registered, they get automatially unregistered if
destroyed.
2021-07-17 16:31:02 +02:00
Filippo Gentile 32f86532d7 Centralize signals for railway layout changes
Initial implementation.
All models or part of code which modify railway layout report to
MeetingSession.
2021-07-17 16:17:51 +02:00
Filippo Gentile c8b1a7cd06 LineGraphToolbar: new button to reload
LineGraphScene: do not reload when requested same object unless forced.
2021-07-17 15:36:07 +02:00
Filippo Gentile 41e373b43e Minor function modification for code clarity
Behavior is unaffected.
2021-07-17 15:17:45 +02:00
Filippo Gentile d0b7766e8d LineGraphWidget: connect sync request 2021-07-17 15:10:49 +02:00
Filippo Gentile 6d0e50b5d5 StationLabelsHeader: smaller station font
* Paint electric platform in blue and embolden throgh tracks
2021-07-17 15:10:08 +02:00
Filippo Gentile 1bd9085de9 LineGraphToolbar: sync on scroll area request
* Re-use names already fetched by scene when possible
2021-07-17 15:09:14 +02:00
Filippo Gentile 9ea6856a25 LineGraphScrollArea: request sync toolbar on click 2021-07-17 15:08:17 +02:00
Filippo Gentile efc92a8dd0 LineGraphScene: added getters 2021-07-17 15:07:20 +02:00
Filippo Gentile f8c3b5e43a LineGraphToolbar: force clearing graph
LineGraphScene: allow setting NoGraph
2021-07-17 13:15:34 +02:00
Filippo Gentile 8ba629b328 LineGraphViewport: inverted x and y on painting, wrong slots 2021-07-17 13:11:26 +02:00
Filippo Gentile 31fa2cef29 LineGraphToolbar: add new combo item
Always clear previous model when changing type and also clear line edit
when selecting NoGraph
2021-07-17 13:10:55 +02:00
Filippo Gentile d135b11a68 LineGraphScene: forgot to bind id to SQL query 2021-07-17 13:09:45 +02:00
Filippo Gentile bf5b5c7005 LineGraphScene: LineGraphType enum start from zero.
Starting from -1 allowed to have empty combobox on NoGraph but this way
when setting another value you cannot go back to NoGraph which is needed
to clear the graph query object for now.
Without finalizing SQL statements (in future will be done automatically
on timer basis) the database canno close correctly.
2021-07-17 13:09:06 +02:00
Filippo Gentile 3453383823 LineGraphToolbar: store previous graph type for comparison
Initialize scene to nullptr
2021-07-17 12:37:44 +02:00
Filippo Gentile 271c9a4321 LineGraphViewport: fix height 2021-07-17 12:25:55 +02:00
Filippo Gentile 15a2907a00 LineGraphToolbar: wrong signal connections 2021-07-17 12:14:47 +02:00
Filippo Gentile 64cc4d57e1 Fix LineGraphScene header
Leftovers of conflict caused by messy commits when I started this branch
2021-07-17 12:14:31 +02:00
Filippo Gentile 51a8f10ae5 Test new graph widget 2021-07-17 12:11:40 +02:00
Filippo Gentile 5bbde8823c Missing connections on LineGraphToolbar 2021-07-17 12:05:32 +02:00
Filippo Gentile 083beccf23 fixup! CustomCompletionLineEdit: allow setting model 2021-07-17 11:55:14 +02:00
Filippo Gentile 28950dc11e CustomCompletionLineEdit: allow setting model
Allow setting model after creating the line edit
2021-07-17 11:54:53 +02:00
Filippo Gentile 0fd080d2a6 LineGraphToolbar: separate class
Manages the graph selection and in future maybe zooming
2021-07-17 11:43:45 +02:00
Filippo Gentile a664790141 fixup! CustomCompletionLineEdit: allow setting model 2021-07-17 11:42:43 +02:00
Filippo Gentile 1e1ab7f92b LineGraphScene: new signal, changed enum values
Now NoGraph is -1 so in a combobox it gets empty name.
2021-07-17 11:41:24 +02:00
Filippo Gentile c4c130d62f CustomCompletionLineEdit: allow setting model
Allow setting model after creating the line edit
2021-07-17 11:16:03 +02:00
Filippo Gentile 47e91567c1 Move scroll area to separate class
It's more clear and smaller classes are better
2021-07-17 10:42:33 +02:00
Filippo Gentile 537f53da7f New line graph widget class
HourPanel: shows hour labels on the left
LineGraphWidget: manages the graph logic all in one class.
2021-07-17 10:32:38 +02:00
Filippo Gentile b2c0be9149 Added station graph view classes
LineGraphViewport: draws station platforms and jobs
StationLabelsHeader: draws station labels on top of graph
2021-07-17 09:36:20 +02:00
Filippo Gentile a5be3f61b4 Added note for future development 2021-07-17 00:46:30 +02:00
Filippo Gentile 9dfcaa4ec6 Allow loading lines
Implement loading full railway line.
2021-07-17 00:44:12 +02:00
Filippo Gentile 2a452ae8cf Fix commit mess and add support for segments 2021-07-17 00:17:04 +02:00
Filippo Gentile f53e4b3340 Merge branch 'new_station_graph' of ../../git_repo_origin_bare/TrainTimetable_GIT_master_ into new_station_graph 2021-07-17 00:10:48 +02:00
Filippo Gentile bd497c89ff Initial commit Station Graph
LineGraphScene: class to hold a graph and its logics
StationGraphObject: represent a single station inside the railway line
2021-07-16 23:58:39 +02:00
Filippo Gentile 9981b07f0d Initial commit Station Graph
LineGraphScene: class to hold a graph and its logics
StationGraphObject: represent a single station inside the railway line
2021-07-16 23:33:14 +02:00
Filippo Gentile b4fcd30bd8 StationEditDialog: support new track connections modes 2021-04-08 18:15:12 +02:00
Filippo Gentile af9cc5d880 NewTrackConnDlg: react correctly to new modes
*Example: when connecting a gate to all tracks, disable track selection
and don't warn if track is null (Null in this case means All tracks of
the current station)
2021-04-08 18:14:27 +02:00
Filippo Gentile a10e35f9c7 StationTrackConnectionsModel: fixed query 2021-04-08 18:12:54 +02:00
Filippo Gentile 32b4001f24 StationGatesMatchModel: getter function for gate side 2021-04-08 17:56:40 +02:00
Filippo Gentile 5514ebcaca StationTrackConnectionsModel: add multiple connections at once
*Add a track to all gates on chosen side
*Add all tracks to a gate
2021-04-08 17:56:13 +02:00
Filippo Gentile 40204e8b61 NewTrackConnDlg: use gate out track count from model to set QSpinBox
maximum
2021-04-08 15:18:47 +02:00
Filippo Gentile 3ade15071f StationGatesMatchModel: store out track count 2021-04-08 15:17:55 +02:00
Filippo Gentile 38078f4f3c Updated translations 2021-04-08 15:17:27 +02:00
Filippo Gentile 5bb553fa27 StationUtils: fixed wrong translation domain 2021-04-08 15:17:11 +02:00
Filippo Gentile ff2942698b Updated translations 2021-04-08 13:57:46 +02:00
Filippo Gentile 160b79fb8d Fixed some UI strings 2021-04-08 13:57:25 +02:00
Filippo Gentile f9d72cc8d0 EditLineDlg, EditRailwaySegmentDlg: set 'Km ' prefix on KmSpinBox 2021-04-08 12:20:25 +02:00
Filippo Gentile a66c036650 KmSpinBox: fixed handling of suffix and prefix 2021-04-08 12:18:34 +02:00
Filippo Gentile f748d3b0c8 StationLinesListModel: deprecated, use instead RailwaySegmentMatchModel 2021-04-08 11:45:13 +02:00
Filippo Gentile c072ca8fd5 Fix includes after moving match models 2021-04-08 11:44:30 +02:00
Filippo Gentile 2266dbf6ec Moded station/segment/line match models to a common 'stations/
match_models' folder
2021-04-08 11:40:48 +02:00
Filippo Gentile 06d1c0802a Added FIXME comments to deprecated enums and functions
Removed unused DirectionNames class
2021-04-08 11:23:48 +02:00
Filippo Gentile 4220cc2713 LinesSQLModel: removed deprecated model
Superseeded by LinesModel and RailwaySegmentsModel
2021-04-08 11:20:25 +02:00
Filippo Gentile fd3640afc6 Model Roles: removed unused custom roles 2021-04-08 11:09:01 +02:00
Filippo Gentile 75d1f23297 CustomEvents enum: renamed Node to Segments 2021-04-08 11:02:23 +02:00
Filippo Gentile 41ee0253f1 RailwayNodeEditor, RailwayNodeModel, StationOrLineMatchFactory:
removed deprecated classes.

Superseeded by EditLineDlg and EditRailwaySegmentDlg
2021-04-08 10:59:14 +02:00
Filippo Gentile 21badd0ab6 StationsSQLModel: removed deprecated model
Superseeded by StationsModel
2021-04-08 10:50:43 +02:00
Filippo Gentile 1a5e94a6ca DefaultPlatfDelegate, PlatformSpinBox: removed because deprecated.
Platform numbers have been replaced by track_id so it can be used with
standard match model item delegate
2021-04-08 10:47:30 +02:00
Filippo Gentile 13a94f5412 StationEditDialog, StationsManager: prefer refreshData() instead of
clearCache() because it emits signal and view update immediately
2021-04-08 10:39:05 +02:00
Filippo Gentile 809d2d61ed JobsSQLModel: fix wrong column
StationTracksModel: emit dataChanged when moving tracks up/down
2021-04-08 10:37:58 +02:00
Filippo Gentile eb7ef4ba1c IPagedItemModel: refreshData() can be implemented in base class
Remove duplicated code
2021-04-08 10:23:58 +02:00
Filippo Gentile c4058b010e IPagedItemModel: pass bool to refreshData() which forces model reset and
clears cache even if row count has not changed.

This is better than just using clearCache() because it immediately
signal the view to redraw the contents, while with clearCache() we must
be lucky and wait the view for redrawind itself sopntaneusly.
2021-04-08 09:42:39 +02:00
Filippo Gentile a4483a0f9c EditLineDlg: split line details and line path, support adding/removing
segments and editing line details.
2021-04-08 09:09:00 +02:00
Filippo Gentile efbd76133d ChooseSegmentDlg: dialog to choose railway segment and filter by origin
and destination stations
2021-04-08 09:08:10 +02:00
Filippo Gentile 7a47f846ab RailwaySegmentMatchModel: new model to match segments, filter by origin
station
2021-04-08 09:07:24 +02:00
Filippo Gentile 35b439e637 LineSegmentsModel: show always only 1 page
*Add/Remove line segments
2021-04-08 09:06:41 +02:00
Filippo Gentile 5e625c15de StationGatesMatchModel: fix section comment 2021-04-08 09:04:46 +02:00
Filippo Gentile 543f5b6443 KmSpinBox: attempt to support prefix 2021-04-08 09:04:24 +02:00
Filippo Gentile b404344c8d LineSegmentsModel: show segment type and direction in tooltip
*Fixed comment for class
*Use decoration to tell a segment is elecrified
*Light cyan background for stations
2021-04-07 19:47:17 +02:00
Filippo Gentile f9c6663dbc RailwayNodeEditor: deprecate class, will remove in future
StationsManager: switch to EditLineDlg
2021-04-07 19:22:43 +02:00
Filippo Gentile 5ba51e5322 EditLineDlg: new dialog to manage railway line segments 2021-04-07 19:21:51 +02:00
Filippo Gentile 0fe7cec356 LineSegmentsModel: new model to manage railway line segments 2021-04-07 19:21:20 +02:00
Filippo Gentile 38ae8a82be LinesModel: add/remove lines 2021-04-07 19:20:05 +02:00
Filippo Gentile a7a76f781d MainWindow: temporarily prevent loading line at document opening 2021-04-07 19:19:23 +02:00
Filippo Gentile cb0d1d58db line_segments: allow up to 100 segments for each line.
Longer lines can be implemented by splitting in 2 sub lines.
2021-04-07 16:42:09 +02:00
Filippo Gentile 348683948f LinesModel: new model to view lines 2021-04-07 13:56:59 +02:00
Filippo Gentile a1d007928a EditRailwaySegmentDlg: now it handles railway track connections 2021-04-07 13:34:10 +02:00
Filippo Gentile bfe1b78e18 Fix includes after moving files to segment subfolder 2021-04-07 13:33:09 +02:00
Filippo Gentile 5e8cd824ba Stations: moved segments model/dialogs to stations/manager/segments 2021-04-07 13:28:03 +02:00
Filippo Gentile 089344ce9a station_gate_connections: fixed UNIQUE constraint 2021-04-06 23:44:59 +02:00
Filippo Gentile d38a0841f1 EditRailwaySegmentDlg: rejecting is actually possible
Thanks to the delayed changes made by RailwaySegmentConnectionsModel
changes are applied only if dialog is accepted.
2021-04-06 23:15:21 +02:00
Filippo Gentile 6cc00a79ab EditRailwaySegmentDlg, RailwaySegmentsModel: better km formatting 2021-04-06 23:14:13 +02:00
Filippo Gentile d90b935bf8 RailwayNodeEditor: fix include 2021-04-06 23:11:30 +02:00
Filippo Gentile c1b6f922a1 Moved KmDelegate and KmSpinBox to utils/spinbox
KmDelegate: allow set prefix and minimum
2021-04-06 22:58:28 +02:00
Filippo Gentile 42065ea7d2 EditRailwaySegmentDlg: manage railway track connections 2021-04-06 22:44:33 +02:00
Filippo Gentile 14a713900f EditRailwayConnectionDlg: dialog to manage railway track connections 2021-04-06 22:43:39 +02:00
Filippo Gentile e9e81f2985 RailwaySegmentConnectionsModel: new model to add/edit/remove segment
track connections

New feature: changes are stored in a list and applied only if user
accepts the dialog
2021-04-06 22:42:51 +02:00
Filippo Gentile 6f12a5ff6e railway_connections: fix UNIQUE constraint
Removed triggers on railway_connections because they are wrong
2021-04-06 22:41:09 +02:00
Filippo Gentile 1f0f41dc50 EditRailwaySegmentDlg: fixed group box title 2021-04-06 16:49:58 +02:00
Filippo Gentile 6e1fd56cd1 StationsManager: added railway segments tab 2021-04-06 16:47:06 +02:00
Filippo Gentile a4e780b8a3 StationGatesMatchModel: fix wrong quotes on string literal. 2021-04-06 16:05:17 +02:00
Filippo Gentile 8131efce0c EditRailwaySegmentDlg: pass segment id to StationGatesMatchModel 2021-04-06 16:01:34 +02:00
Filippo Gentile 8ca690c439 RailwaySegmentsModel: fixed sorting exclude columns.
Changed ToGateCol name to 'To Gate' in order to distinguish it from
FromGateCol which is just 'Gate'.

Align gate letter to center and align speed and distance to right.
2021-04-06 16:00:55 +02:00
Filippo Gentile cce2e9b9e1 StationGatesMatchModel: mark connected gates instead of hiding them
Show segment name in tooltip
2021-04-06 15:58:44 +02:00
Filippo Gentile 53ac31185d EditRailwaySegmentDlg: change group box titles according to locking and
reversing
2021-04-06 15:21:49 +02:00
Filippo Gentile 5133f9687f StationEditDialog: hide FromStationCol in gate connections
Clear cache after gate connection has been edited
2021-04-06 15:21:15 +02:00
Filippo Gentile a55465f3cf RailwaySegmentsModel: show light red background and tooltip if segment
is shown reversed
2021-04-06 15:06:02 +02:00
Filippo Gentile 5367813083 StationGatesMatchModel: tooltip tells on which side is a gate 2021-04-06 15:02:33 +02:00
Filippo Gentile f0c9c76532 RailwaySegmentHelper: added error message output
EditRailwaySegmentDlg: show error message from RailwaySegmentHelper
2021-04-06 14:58:39 +02:00
Filippo Gentile b85b83b6bd Side enum: make West come before East in ordering 2021-04-06 14:57:24 +02:00
Filippo Gentile 755b9ebf97 multiple_gate_segments_update_out: fixed column names 2021-04-06 14:47:53 +02:00
Filippo Gentile 273320fb31 StationEditDialog: forgot to connect button signal 2021-04-06 13:51:17 +02:00
Filippo Gentile e2907b82e1 EditRailwaySegmentDlg: restore reversed segment before setting data 2021-04-06 13:50:52 +02:00
Filippo Gentile 52c67dc4f5 EditRailwaySegmentDlg: added dialog button box 2021-04-06 13:44:26 +02:00
Filippo Gentile 91704e2c5d StationEditDialog: save database reference and add/edit/remove segments 2021-04-06 13:38:47 +02:00
Filippo Gentile 422cecb3e2 EditRailwaySegmentDlg: dialog to add/edit railway segment 2021-04-06 13:37:51 +02:00
Filippo Gentile 71261bc350 RailwaySegmentHelper: helper class to get/set railway segment info 2021-04-06 13:37:21 +02:00
Filippo Gentile 7aeee2ef9a railway_segments: added check constaraint 2021-04-06 13:36:42 +02:00
Filippo Gentile caac213bd1 CustomCompletionLineEdit: prevent showing popup if read only 2021-04-06 13:36:04 +02:00
Filippo Gentile fbb663bb18 StationGatesMatchModel: can now filter out already connected gates (this
gate are already used as start/end point of a railway segment so they
cannot be chosen for a new segment)
2021-03-27 15:20:29 +01:00
Filippo Gentile 01f3e6430f StationEditDialog: added Gate Connections Page 2021-03-27 15:13:12 +01:00
Filippo Gentile e7276a766f RailwaySegmentsModel: fix sorting and fetching 2021-03-27 15:12:37 +01:00
Filippo Gentile fd81054dd8 StationsModel: TODO, emit station added/removed/changed signals.
Wire to a central object
2021-03-27 15:12:02 +01:00
Filippo Gentile 4645940ba6 RailwaySegmentsModel: initial implementation of model to view
connections between stations
2021-03-26 10:38:40 +01:00
Filippo Gentile 3e47088350 StationMatchFactory: new factory for StationsMatchModel
In future remove old StationOrLineMatchFactory.
2021-03-25 09:52:16 +01:00
Filippo Gentile c701473618 QPointer: it doesn't delete on destructor, so manually delete pointer. 2021-03-24 16:12:27 +01:00
Filippo Gentile 9ecc277d7f StationTrackConnectionsModel: allow sorting by gate_track 2021-03-24 16:10:27 +01:00
Filippo Gentile f16d1c3d63 SQL FIXME 2021-03-23 17:39:47 +01:00
Filippo Gentile 06a3f913a1 NewTrackConnDlg: add button box 2021-03-23 17:39:33 +01:00
Filippo Gentile 09de8b0229 StationEditDialog: properly refresh track connections data when track or
gate is removed
2021-03-23 17:39:11 +01:00
Filippo Gentile 622a3fc27a StationTrackConnectionsModel: bind NULL instead of zero
StationGatesModel: bind NULL instead of zero
2021-03-23 17:37:18 +01:00
Filippo Gentile dacf9101ac StationTrackConnectionsModel: fix wrong query 2021-03-23 17:02:37 +01:00
Filippo Gentile 234a9f0448 stations_gates: fix trigger preventing also NULL dfault platform which
is valid.
2021-03-23 17:00:25 +01:00
Filippo Gentile 56975778ba NewTrackConnDlg: dialog to instert initial values
StationEditDialog: added track connections tab
2021-03-23 16:26:02 +01:00
Filippo Gentile 8522cf721d Fix compilation: StationsMatchModel now doesn't support filtering by
line anymore
2021-03-23 16:25:11 +01:00
Filippo Gentile 40a0fa15db QDialog: initial migration to use QPointer guard 2021-03-23 16:24:02 +01:00
Filippo Gentile ace27499ee ComboDelegate: take string list by reference 2021-03-23 15:40:47 +01:00
Filippo Gentile 830d79e3ef CustomCompletionLineEdit: fix expicit constructor 2021-03-23 15:40:18 +01:00
Filippo Gentile 4abcb751f4 TODO: implement filtering on StationTracksMatchModel 2021-03-23 15:04:17 +01:00
Filippo Gentile 7b251d3f45 SQL: new triggers.
Prevent out of bound gate track,
connecting gate to tracks of different stations and setting gate's
default track to one that is not connected
2021-03-23 15:03:21 +01:00
Filippo Gentile 3bf0d06d54 StationTrackConnectionsModel: new model to add/remove station track
connections
2021-03-23 14:00:50 +01:00
Filippo Gentile 668769f558 StationGatesMatchModel: non need for QFlags<> on utils::Side 2021-03-23 12:26:03 +01:00
Filippo Gentile 4e676eb9ea GateSide enum renamed to Side.
More generic name, can be used also for track side.
2021-03-23 12:18:29 +01:00
Filippo Gentile c9c938f86a StationGatesMatchModel: added match model to select gate.
StationGatesMatchFactory: factory for SqlFKFieldDelegate
2021-03-23 12:08:50 +01:00
Filippo Gentile 486979a663 StationsMatchModel: remove line filter
It was based on old line concept.
To reintroduce it we must first implement the rest of models.
2021-03-23 11:48:24 +01:00
Filippo Gentile e4543d10a6 StationTracksModel: reworked moveTrack logic
Previously every move increased pos number, now they stay compact
2021-03-23 11:42:45 +01:00
Filippo Gentile 63ae9c959a Merge branch 'develop' into new_station_schema2 2021-03-23 10:11:06 +01:00
Filippo Gentile 168b7e848d Revert "ModelPageSwitcher: clearCache() before refreshData()"
This reverts commit a9176b9c3d.
2021-03-23 09:45:20 +01:00
Filippo Gentile a9176b9c3d ModelPageSwitcher: clearCache() before refreshData()
Because refreshData() internally calls beginResetModel() which triggers
view update but if the row count is not changed, cache is not cleared
and the view updates on old data. When clearing the cache after it's too
late, the view may already have been updated.
2021-03-23 09:38:24 +01:00
Filippo Gentile c424c7dadf ModelPageSwitcher: clearCache() before refreshData()
Because refreshData() internally calls beginResetModel() which triggers
view update but if the row count is not changed, cache is not cleared
and the view updates on old data. When clearing the cache after it's too
late, the view may already have been updated.
2021-03-23 09:36:01 +01:00
Filippo Gentile 22e6cdf4dc StationTracksModel: fixed cannot edit passenger and freight length 2021-03-23 09:32:13 +01:00
Filippo Gentile 3be3da6b69 station_gates: additional triggers to ensure def_in_platf belongs to
same station of gate.

stations: name must not be empty (which is different than NULL)
2021-03-23 09:31:29 +01:00
Filippo Gentile 283e174abd StationEditDialog: added Track Tab logic.
Added ColorDelegate and SqlFKFieldDelegate to StationTracksModel.
Added ComboDelegate and SpinBox delegate to StationGatesModel.
2021-03-22 23:54:25 +01:00
Filippo Gentile ef33066620 StationGatesModel: implement IFKField and allow setting Out track count
IFKField inteface is needed to work with delegate for default im
platform column.
2021-03-22 23:51:46 +01:00
Filippo Gentile bf2bc9e07a StationTracksModel: fix SQL syntax and moveTrack logic
Adjusted cell text alignment.
2021-03-22 23:49:15 +01:00
Filippo Gentile d5ff235f5d StationTrackType: fix flag value 2021-03-22 23:48:03 +01:00
Filippo Gentile 6a67b21a42 StationTracksMatchModel: added match model to select track.
StationTracksMatchFactory: factory for SqlFKFieldDelegate
2021-03-22 23:47:29 +01:00
Filippo Gentile cb926b09b0 station_gates: CHECK out_track_count > 0 2021-03-22 15:56:29 +01:00
Filippo Gentile 3a24847e6e ColorDelegate: moved to utils subdirectory 2021-03-22 15:36:07 +01:00
Filippo Gentile 46875b26c1 StationGatesModel: fix editable field not initialized
When parsing QVariant toInt() bool ok was not used
Fixed query name
2021-03-22 15:28:52 +01:00
Filippo Gentile b46104ac42 GateSides enum: add NSides count value 2021-03-22 15:27:31 +01:00
Filippo Gentile 0e879d2fe1 StationTracksModel: new model to add/remove/edit tracks 2021-03-22 15:26:57 +01:00
Filippo Gentile 8ac9583cb0 StationGatesModel: add gateRemoved() signal
StationEditDialog: reaact to this signal
2021-03-22 12:35:28 +01:00
Filippo Gentile df651b849c Station Track doesn't need extra type flag for Passenger/Freight traffc
Just set corresponding legth to non-zero values
2021-03-22 12:13:37 +01:00
Filippo Gentile ceea03ac65 station_gates: check name is a single upper case latin letter
station_tracks: check non-zero track length and axes>1

Also check freight_length_cm and platf_length_cm to be less than track
length or zero.
2021-03-22 12:12:38 +01:00
Filippo Gentile 59967a139d StationGatesModel: nicer error message if setting invalid type
(neither Entrance neither Exit case which is invalid)
2021-03-22 11:53:43 +01:00
Filippo Gentile 26b67781ba StationEditDialog: separate editing modes
Internal: name, type, platforms, gates and platform connections.
External: phone, gate connections with other station
2021-03-22 11:52:50 +01:00
Filippo Gentile 5a50120db7 StationGatesModel: fix SQL syntax error and bind station ID.
Fix Qt::CheckStateRole value
2021-03-22 11:38:02 +01:00
Filippo Gentile 229c99ddb6 StationEditDialog: add/remove gates functionality 2021-03-22 11:26:59 +01:00
Filippo Gentile e03f41b660 StationGatesModel: fix SQL spelling error
Better name handling (strict latin letters)
2021-03-22 11:26:35 +01:00
Filippo Gentile 667097a809 StationsManager: better name handling 2021-03-22 11:25:32 +01:00
Filippo Gentile fe8304d06b StationEditDialog: initialize gatesModel before using getters 2021-03-22 10:55:11 +01:00
Filippo Gentile 77d0771063 station_gates: fix SQL syntax 2021-03-22 10:55:11 +01:00
Filippo Gentile 9a1eb628e7 Fix compilation errors 2021-03-22 10:06:13 +01:00
Filippo Gentile 89c0b8513d StationEditDialog: use StationGatesModel
StationsManager: use StationEditDialog

and refresh model after editing
2021-03-22 10:05:43 +01:00
Filippo Gentile 05d521046b StationGatesModel: added getter/setters for station details
This is to avoid direct queries inside StationEditDialog.
2021-03-22 10:02:15 +01:00
Filippo Gentile 15d2ccb18f StationsModel: fix use qin64 for phone number 2021-03-22 09:59:43 +01:00
Filippo Gentile 5c23c61f16 station_gates: add check constraint to prevent invalid type value.
A gate must be Entrance, Exit or both (= Bidirectional) so avoid zero
value (neither Entrance neither Exit)
2021-03-22 09:59:43 +01:00
Filippo Gentile c23bc1b85e StationGatesModel: new model to create/edit/remove station gates 2021-03-21 19:31:04 +01:00
Filippo Gentile d2092b3cf0 station_gates: added a 'side' field (East/West)
station_tracks: name is mandatory now (NOT NULL clause)
2021-03-21 19:30:29 +01:00
Filippo Gentile 5c0faf5f1c Fix class member 2021-03-21 18:23:43 +01:00
Filippo Gentile c770850473 StationEditDialog: mock-up made with designer tool just to see how it
looks.
2021-03-21 16:33:52 +01:00
Filippo Gentile c429ba3e64 StationEditDialog: initial implementation of new dialog to edit or view
station details.

Future working modes:
1) Station Editing Mode: (for central database)
StationsManager can add/remove stations and change their types and names
but not phone number.
StationEditDialog can change Gates (add/remove/set name), Tracks and
Track Connections and add/remove SVG image.

2) Layout Editing Mode: (for normal sessions)
StationsManger can import or remove stations but not adding new.
StationsManager can only change phone number.
StationEditDialog can only change phone number and Gate Connections, or
edit connections with SVG Viewer.
2021-03-21 16:13:27 +01:00
Filippo Gentile 4fe9d6a023 StationsModel: show empty phone number instead of zero when NULL.
Set NULL when user sets empty number or cannot be converted to integer.
Fixed error checking short_name againsta (full) name.
2021-03-21 15:31:49 +01:00
Filippo Gentile 10f9693e92 StationsManager: use new StationsModel and set station type delegate
with ComboDelegate.
2021-03-21 15:11:45 +01:00
Filippo Gentile 7ea413e54b StationsModel: fix prevent empty name on new station and refresh model
automatically when removing station.
2021-03-21 15:10:43 +01:00
Filippo Gentile 7857f2c5cb Fix table creation order and a spelling error in foreign key clause. 2021-03-21 15:09:49 +01:00
Filippo Gentile 8272becd70 DefaultPlatfDelegate and PlatformSpinBox need to be reworket to support
foreign keys and maybe a match model.
2021-03-21 14:57:24 +01:00
Filippo Gentile d38236dd16 Removed Job checking at session open.
firstStop and lastStop columns were removed from 'jobs' table so not
needed anymore.
2021-03-21 14:56:22 +01:00
Filippo Gentile fd5187e744 Added new StationsModel (deprecates StationsSQLModel).
Temporary commented out MeetingSession queries, in future will be
removed completely.
2021-03-21 13:33:08 +01:00
Filippo Gentile 6ef6378dca Fixed some spelling errors in table creation.
Added triggers to prevent multiple segments on same staion gate and
multiple railway track connections on same segment.
2021-03-21 12:19:28 +01:00
Filippo Gentile 1e2a299907 Initial implementation of new database schema.
This allows more precise description of the layout and is more flexible
to describe complex stations.
2021-03-21 11:56:31 +01:00
Filippo Gentile 70e9f167be Fix CONTRIBUTE and CHANGELOG.
Now they are the same on all branches so they don't come up in changed files when merging.
2021-03-17 17:27:32 +01:00
Filippo Gentile d8f000cf9d Doxygen: increase dot max graph nodes to allow generate 'included by'
graph for 'session.h'
2021-03-17 17:13:22 +01:00
Filippo Gentile 72cc8c189d Better CHANGELOG message 2021-03-17 13:19:16 +01:00
Filippo Gentile d0c76d94f7 Setup develop branch 2021-03-17 13:14:31 +01:00
Filippo Gentile 196f9c2b3a Master branch is now only for stable code 2021-03-17 12:22:14 +01:00
Filippo Gentile 6629f13693 Merge branch 'postgresql'
Resolved conflicts on CHANGELOG
2021-03-17 11:57:00 +01:00
Filippo Gentile 7c49484568 Edit CHANGELOG, clarify branch role 2021-03-17 10:22:33 +01:00
Filippo Gentile a007b7764d This is an initial commit for PostgreSQL port 2021-03-17 10:17:53 +01:00
Filippo Gentile 382f56ed94 Second commit
Added .gitignore file and added a README file
2021-03-16 18:18:04 +01:00
Filippo Gentile 9b1c9b878f Initial commit 2021-03-16 10:46:22 +01:00
661 changed files with 66320 additions and 34322 deletions

55
.clang-format Normal file
View File

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

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. Windows 10]
- Version [e.g. 5]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@ -0,0 +1,7 @@
Fixes #
## Proposed Changes
-
-
-

4
.gitignore vendored
View File

@ -2,6 +2,8 @@
# CMake artifacts
/build*/
/_packages/
CMakeUserPresets.json
# Doxygen
/docs/
@ -91,4 +93,4 @@ compile_commands.json
# QtCreator local machine specific files for imported projects
*creator.user*
*_qmlcache.qrc
*_qmlcache.qrc

5
.gitignore-blame Normal file
View File

@ -0,0 +1,5 @@
# Use this file when blaming
# git-blame --ignore-revs-file .gitignore-blame
# Big clang-format refactor
9f14ee1c64b3b9d399078918fc20ccce7bc4a7d9

120
BUILDING.md Normal file
View File

@ -0,0 +1,120 @@
# Building using CMake
ModelRailroadTimetablePlanner uses CMake build system.
CMake Minimum Version: **3.5**
## Dependencies
1. **Qt**: [qt.io](https://www.qt.io)
Components:
- Core
- Gui
- Widgets
- Svg
- PrintSupport
- LinguistTools
2. **SQLite 3**: [sqlite.org](https://sqlite.org/index.html)
3. **libzip**: [libzip.org](https://libzip.org)
4. **zlib**: [zlib.net](https://www.zlib.net)
## Ubuntu
- Install GCC and CMake
>`sudo apt install build-essential cmake`
- Install Qt 5:
>`sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev qttools5-dev-tools`
- Install SQLite 3
>`sudo apt install libsqlite3-dev`
- Install libzip
>`sudo apt install libzip-dev`
- Install zlib
> NOTE: automatically installed if installing libzip
>`sudo apt install zlib1g-dev`
## Windows
### Setup Qt
> Set `QT5_DIR` CMake variable:
> Set to the folder which contains `Qt5Config.cmake` file
> Example: `C:\Qt\5.15.2\mingw81_64\lib\cmake\Qt5`
### Setup libzip
If compiled from source `libzip` should generate CMake config package files
This is the preferred method to include it:
> Set `LibZip_DIR` CMake variable:
> Set to the folder which contains `libzip-config.cmake` file
> Example: `%LIBZIP_INSTALL_DIRECTORY%\lib\cmake\libzip`
> (replace `%LIBZIP_INSTALL_DIRECTORY%` with the directory in which you installed `libzip`)
Alternative method:
Include headers directory and libraries directory in CMake variables
> Append `include` directory to `CMAKE_INCLUDE_PATH` or set `LibZip_INCLUDE_DIRS`
> Append `lib` and `bin` directory to `CMAKE_LIBRARY_PATH`
## Troubleshooting
### Windows CMake Doesn't Find DLL
MinGW can link directly to `*.dll` dynamic libraries but CMake is set to look for
import libraries `*.dll.a`.
**SQLite 3** does not provide an import library so CMake will NOT find it.
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 information see [DLL Import Library Tool](https://www.willus.com/mingw/colinp/win32/tools/dlltool.html)
## Compile
- Clone the repository
> `git clone https://github.com/gfgit/ModelRailroadTimetablePlanner`
- Go to project directory
> `cd ModelRailroadTimetablePlanner`
- Create directory for building the program
> `mkdir build`
> `cd build`
- Run CMake
> `cmake ..`
> NOTE: you can use `cmake-gui` to configure CMake variables
- Build
> `cmake --build .`
> NOTE: if you have problems locating `libzip` on Ubuntu
> Set `LibZip_LIBRARIES=/usr/lib/x86_64-linux-gnu/libzip.so`
- Install
> `cmake --build . -t install`
- Run
> `mrtplanner`
> NOTE: the location depends on where you installed the program
> Look at `CMAKE_INSTALL_PREFIX` variable
## Doxygen
Enable `BUILD_DOXYGEN` in CMake configuration to generate doxygen documentation.
The output will go in `build/docs` directory.
## NSIS Installer
> TODO: document `makensis.exe`

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.17)
include(CMakeDependentOption)
project(TrainTimetable VERSION 5.25.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)
@ -13,81 +13,74 @@ endif()
## Defines ##
set(DB_FORMAT_VERSION 7)
set(DB_FORMAT_VERSION 8)
set(APP_PRODUCT_NAME "TrainTimeTable")
set(APP_DISPLAY_NAME "Train Timetable")
set(APP_PRODUCT_NAME "ModelRailroadTimetablePlanner")
set(APP_DISPLAY_NAME "Model Railroad Timetable Planner")
set(APP_COMPANY_NAME "Train Software")
set(APP_COMPANY_NAME_LOWER "trainsoftware")
set(PROJECT_HOMEPAGE_URL "www.pollofrittomachebuono.altervista.org")
set(APP_HELP_URL ${APP_ABOUT_URL})
set(APP_UPDATE_URL ${APP_ABOUT_URL})
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)
# Create main application target
set(TRAINTIMETABLE_TARGET "train-timetable")
set(MR_TIMETABLE_PLANNER_TARGET "mrtplanner")
## defines end ##
# NSIS Installer
if(WIN32)
configure_file(packaging/windows/NSIS/constants.nsh.in ${CMAKE_BINARY_DIR}/NSIS/constants.nsh @ONLY)
configure_file(packaging/windows/NSIS/installer.nsi ${CMAKE_BINARY_DIR}/NSIS/installer.nsi COPYONLY)
endif()
add_custom_target(NSIS
DEPENDS ${TRAINTIMETABLE_TARGET}
#COMMAND TODO add NSIS compiler
SOURCES
packaging/windows/NSIS/constants.nsh.in
packaging/windows/NSIS/installer.nsi
packaging/windows/resources.rc.in
VERBATIM)
## CUSTOM CONFIGURATION ##
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)
if(CONFIG_GLOBAL_TRY_CATCH)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DGLOBAL_TRY_CATCH)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DGLOBAL_TRY_CATCH)
endif()
if(CONFIG_NO_DEBUG_CALL_TRACE)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DNO_DEBUG_CALL_TRACE)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DNO_DEBUG_CALL_TRACE)
endif()
if(CONFIG_PRINT_DBG_MSG)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DPRINT_DBG_MSG)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DPRINT_DBG_MSG)
endif()
if(CONFIG_ENABLE_BACKGROUND_MANAGER)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DENABLE_BACKGROUND_MANAGER)
endif()
if(CONFIG_ENABLE_RS_CHECKER)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DENABLE_RS_CHECKER)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DENABLE_BACKGROUND_MANAGER)
endif()
if(CONFIG_SEARCHBOX_MODE_ASYNC)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DSEARCHBOX_MODE_ASYNC)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DSEARCHBOX_MODE_ASYNC)
endif()
if(CONFIG_ENABLE_AUTO_TIME_RECALC)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DENABLE_AUTO_TIME_RECALC)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DENABLE_AUTO_TIME_RECALC)
endif()
if(CONFIG_ENABLE_USER_QUERY)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DENABLE_USER_QUERY)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DENABLE_USER_QUERY)
endif()
## Config end ##
@ -100,8 +93,9 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(TRAINTIMETABLE_DEFINITIONS ${TRAINTIMETABLE_DEFINITIONS} -DAPPVERSION="${PROJECT_VERSION}" -DQT_DEPRECATED_WARNINGS)
set(MR_TIMETABLE_PLANNER_DEFINITIONS ${MR_TIMETABLE_PLANNER_DEFINITIONS} -DAPPVERSION="${PROJECT_VERSION}" -DQT_DEPRECATED_WARNINGS)
# Include our custom FindXXX moudules in '/cmake' subdirectory
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(Qt5 REQUIRED
@ -115,44 +109,24 @@ find_package(Qt5 REQUIRED
find_package(SQLite3)
find_package(ZLIB)
find_package(ssplib)
# Locate libzip
if(NOT ZIP_INCLUDE_DIR OR NOT ZIP_LIBRARY)
set(ZIP_INCLUDE_DIR NOTFOUND CACHE PATH "Path to libzip include directory, contains 'zip.h' file.")
set(ZIP_LIBRARY NOTFOUND CACHE FILEPATH "Path to libzip library (shared DLL usually libzip.dll).")
message(FATAL_ERROR "libzip NOT FOUND (set ZIP_INCLUDE_DIR and ZIP_LIBRARY)")
endif()
# Prefer config files shipped with 'libzip'
# If not found, it will fallback to out custom FindLibZip.cmake module
# Located in '/cmake' subdirectory
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG true)
find_package(LibZip)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG false)
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)
add_subdirectory(src)
add_subdirectory(packaging)
include(Packing)

View File

@ -1,7 +0,0 @@
Usage of branches:
master: stable code ready to be released
develop: unstable code or code that needs further testing
To develop new features please create new branches and finally merge to develop

47
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,47 @@
# HOW TO CONTRIBUTE
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`.
## Usage of branches
- `master`: stable code ready to be released
- `develop`: unstable code or code that needs further testing
To develop new features please create new branches and finally merge to develop
## Building
See [BUILDING.md](BUILDING.md)
## Translations
UI is localized with Qt Linguist.
Translation files live in [`src/translations`](src/translations) folder.
For more information see [Qt Documentation](https://doc.qt.io/qt-5/linguist-overview.html).
### Adding a new language
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
[`src/translations/CMakeLists.txt`](src/translations/CMakeLists.txt).
Add the file name with path in `TRAINTIMETABLE_TS_FILES` variabile.
Then follow next paragraph.
### Update translations to match new UI elements
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.
4. Use Qt Linguist or other software to translate text.
## Do you have suggestions?
If you think this workflow model is not efficient please let me know!

3
README
View File

@ -1,3 +0,0 @@
Train Timetable
A C++ software with Qt GUI for model railway timetable scheduling
By Filippo Gentile

48
README.md Normal file
View File

@ -0,0 +1,48 @@
# ModelRailroadTimetablePlanner
[Versione in italiano](README_it.md)
Formerly **TrainTimetable**
A cross-platform C++ application with Qt GUI for model railway timetable scheduling.
*Currently tested on Windows and Ubuntu.*
By Filippo Gentile
## Screenshots
![Screenshot of ModelRailroadTimetablePlanner](screenshots/Screenshot.png "ModelRailroadTimetablePlanner Screenshot")
## Goals
The goal of the project is to implement
automatic mechanism which prevent invalid
operations and suggest user with useful action
in order to speed up the creation of timetables.
The programs aims to provide all documents useful
for driving and dispatching trains on big layouts like in FREMO Meetings.
## Main features
- 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
- 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 instabilities and limitations.
Since then it has grown unexpectedly.
So I'd like it to become a community project!
## Project motto
Less time spent on the computer,
more time to have fun on your railway layout!!!
## Contributing to the project
Please see [CONTRIBUTING.md](CONTRIBUTING.md) file.

48
README_it.md Normal file
View File

@ -0,0 +1,48 @@
# ModelRailroadTimetablePlanner
[English version](README.md)
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.*
By Filippo Gentile
## Screenshots
![Screenshot di ModelRailroadTimetablePlanner](screenshots/Screenshot_it.png "ModelRailroadTimetablePlanner Screenshot")
## Obiettivi
L'obiettivo del progetto è implementare meccanismi
automatici che impediscano operazioni non valide e
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 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
- Importazione del materiale rotabile da altre sessioni o fogli di calcolo ODS Spreadsheet
## Storia del progetto
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.
Quindi mi piacerebbe che diventasse un progetto condiviso con la comunità!
## Motto del progetto
Meno tempo passato al computer, più tempo
per divertirsi sul proprio tracciato ferroviario!!!
## Partecipare al progetto
Per favore vedere il file [CONTRIBUTING.md](CONTRIBUTING.md).

45
cmake/DLL_Utils.cmake Normal file
View File

@ -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()

68
cmake/FindLibZip.cmake Normal file
View File

@ -0,0 +1,68 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindLibZip
-----------
.. versionadded:: 3.14
Find the Zip libraries, v3
IMPORTED targets
^^^^^^^^^^^^^^^^
This module defines the following :prop_tgt:`IMPORTED` target:
``LibZip::LibZip``
Result variables
^^^^^^^^^^^^^^^^
This module will set the following variables if found:
``LibZip_INCLUDE_DIRS``
where to find zip.h, etc.
``LibZip_LIBRARIES``
the libraries to link against to use SQLite3.
``LibZip_VERSION``
version of the LibZip library found
``LibZip_FOUND``
TRUE if found
#]=======================================================================]
# Look for the necessary header
find_path(LibZip_INCLUDE_DIR NAMES zip.h)
mark_as_advanced(LibZip_INCLUDE_DIR)
# Look for the necessary library
find_library(LibZip_LIBRARY NAMES libzip)
mark_as_advanced(LibZip_LIBRARY)
# Extract version information from the header file
if(LibZip_INCLUDE_DIR)
file(STRINGS ${LibZip_INCLUDE_DIR}/zipconf.h _ver_line
REGEX "^#define LIBZIP_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
LibZip_VERSION "${_ver_line}")
unset(_ver_line)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibZip
REQUIRED_VARS LibZip_INCLUDE_DIR LibZip_LIBRARY
VERSION_VAR LibZip_VERSION)
# Create the imported target
if(LibZip_FOUND)
set(LibZip_INCLUDE_DIRS ${LibZip_INCLUDE_DIR})
set(LibZip_LIBRARIES ${LibZip_LIBRARY})
if(NOT TARGET LibZip::LibZip)
add_library(LibZip::LibZip UNKNOWN IMPORTED)
set_target_properties(LibZip::LibZip PROPERTIES
IMPORTED_LOCATION "${LibZip_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${LibZip_INCLUDE_DIR}")
endif()
endif()

View File

@ -1,66 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindSQLite3
-----------
Find the SQLite libraries, v3
IMPORTED targets
^^^^^^^^^^^^^^^^
This module defines the following :prop_tgt:`IMPORTED` target:
``SQLite::SQLite3``
Result variables
^^^^^^^^^^^^^^^^
This module will set the following variables if found:
``SQLite3_INCLUDE_DIRS``
where to find sqlite3.h, etc.
``SQLite3_LIBRARIES``
the libraries to link against to use SQLite3.
``SQLite3_VERSION``
version of the SQLite3 library found
``SQLite3_FOUND``
TRUE if found
#]=======================================================================]
# Look for the necessary header
find_path(SQLite3_INCLUDE_DIR NAMES sqlite3.h)
mark_as_advanced(SQLite3_INCLUDE_DIR)
# Look for the necessary library
find_library(SQLite3_LIBRARY NAMES sqlite3 sqlite)
mark_as_advanced(SQLite3_LIBRARY)
# Extract version information from the header file
if(SQLite3_INCLUDE_DIR)
file(STRINGS ${SQLite3_INCLUDE_DIR}/sqlite3.h _ver_line
REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
SQLite3_VERSION "${_ver_line}")
unset(_ver_line)
endif()
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
find_package_handle_standard_args(SQLite3
REQUIRED_VARS SQLite3_INCLUDE_DIR SQLite3_LIBRARY
VERSION_VAR SQLite3_VERSION)
# Create the imported target
if(SQLite3_FOUND)
set(SQLite3_INCLUDE_DIRS ${SQLite3_INCLUDE_DIR})
set(SQLite3_LIBRARIES ${SQLite3_LIBRARY})
if(NOT TARGET SQLite::SQLite3)
add_library(SQLite::SQLite3 UNKNOWN IMPORTED)
set_target_properties(SQLite::SQLite3 PROPERTIES
IMPORTED_LOCATION "${SQLite3_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${SQLite3_INCLUDE_DIR}")
endif()
endif()

View File

@ -1,63 +0,0 @@
# Searches for an installation of the zip library. On success, it sets the following variables:
#
# Zip_FOUND Set to true to indicate the zip library was found
# Zip_INCLUDE_DIRS The directory containing the header file zip/zip.h
# Zip_LIBRARIES The libraries needed to use the zip library
#
# To specify an additional directory to search, set Zip_ROOT.
#
# Author: Siddhartha Chaudhuri, 2009
#
# Look for the header, first in the user-specified location and then in the system locations
SET(Zip_INCLUDE_DOC "The directory containing the header file zip/zip.h")
FIND_PATH(Zip_INCLUDE_DIRS NAMES zip/zip.h PATHS ${Zip_ROOT} ${Zip_ROOT}/include DOC ${Zip_INCLUDE_DOC} NO_DEFAULT_PATH)
IF(NOT Zip_INCLUDE_DIRS) # now look in system locations
FIND_PATH(Zip_INCLUDE_DIRS NAMES zip/zip.h DOC ${Zip_INCLUDE_DOC})
ENDIF(NOT Zip_INCLUDE_DIRS)
SET(Zip_FOUND FALSE)
IF(Zip_INCLUDE_DIRS)
SET(Zip_LIBRARY_DIRS ${Zip_INCLUDE_DIRS})
IF("${Zip_LIBRARY_DIRS}" MATCHES "/include$")
# Strip off the trailing "/include" in the path.
GET_FILENAME_COMPONENT(Zip_LIBRARY_DIRS ${Zip_LIBRARY_DIRS} PATH)
ENDIF("${Zip_LIBRARY_DIRS}" MATCHES "/include$")
IF(EXISTS "${Zip_LIBRARY_DIRS}/lib")
SET(Zip_LIBRARY_DIRS ${Zip_LIBRARY_DIRS}/lib)
ENDIF(EXISTS "${Zip_LIBRARY_DIRS}/lib")
# Find Zip libraries
FIND_LIBRARY(Zip_DEBUG_LIBRARY NAMES zipd zip_d libzipd libzip_d
PATH_SUFFIXES Debug ${CMAKE_LIBRARY_ARCHITECTURE} ${CMAKE_LIBRARY_ARCHITECTURE}/Debug
PATHS ${Zip_LIBRARY_DIRS} NO_DEFAULT_PATH)
FIND_LIBRARY(Zip_RELEASE_LIBRARY NAMES zip libzip
PATH_SUFFIXES Release ${CMAKE_LIBRARY_ARCHITECTURE} ${CMAKE_LIBRARY_ARCHITECTURE}/Release
PATHS ${Zip_LIBRARY_DIRS} NO_DEFAULT_PATH)
SET(Zip_LIBRARIES )
IF(Zip_DEBUG_LIBRARY AND Zip_RELEASE_LIBRARY)
SET(Zip_LIBRARIES debug ${Zip_DEBUG_LIBRARY} optimized ${Zip_RELEASE_LIBRARY})
ELSEIF(Zip_DEBUG_LIBRARY)
SET(Zip_LIBRARIES ${Zip_DEBUG_LIBRARY})
ELSEIF(Zip_RELEASE_LIBRARY)
SET(Zip_LIBRARIES ${Zip_RELEASE_LIBRARY})
ENDIF(Zip_DEBUG_LIBRARY AND Zip_RELEASE_LIBRARY)
IF(Zip_LIBRARIES)
SET(Zip_FOUND TRUE)
ENDIF(Zip_LIBRARIES)
ENDIF(Zip_INCLUDE_DIRS)
IF(Zip_FOUND)
IF(NOT Zip_FIND_QUIETLY)
MESSAGE(STATUS "Found Zip: headers at ${Zip_INCLUDE_DIRS}, libraries at ${Zip_LIBRARY_DIRS}")
ENDIF(NOT Zip_FIND_QUIETLY)
ELSE(Zip_FOUND)
IF(Zip_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Zip library not found")
ENDIF(Zip_FIND_REQUIRED)
ENDIF(Zip_FOUND)

View File

@ -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()

92
cmake/Packing.cmake Normal file
View File

@ -0,0 +1,92 @@
set(MY_PACKAGE_MAINTAINER_NAME "Filippo Gentile")
set(MY_PACKAGE_MAINTAINER_EMAIL "gentilefilippo01@gmail.com")
# these are cache variables, so they could be overwritten with -D,
# ${namespace}-... could be your main project name, or company, or whatever
set(CPACK_PACKAGE_NAME "${APP_COMPANY_NAME_LOWER}-${MR_TIMETABLE_PLANNER_TARGET}"
CACHE STRING "The resulting package name"
)
# which is useful in case of packing only selected components instead of the whole thing
set(
CPACK_PACKAGE_DESCRIPTION_SUMMARY "${CMAKE_PROJECT_DESCRIPTION}"
CACHE STRING "Package description for the package metadata"
)
set(CPACK_PACKAGE_VENDOR "${APP_COMPANY_NAME}")
set(CPACK_VERBATIM_VARIABLES YES)
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
SET(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_SOURCE_DIR}/_packages")
set(CPACK_STRIP_FILES YES)
set(
CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
)
# https://unix.stackexchange.com/a/11552/254512
set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/${MR_TIMETABLE_PLANNER_TARGET}")#/${CMAKE_PROJECT_VERSION}")
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_CONTACT ${MY_PACKAGE_MAINTAINER_EMAIL})
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${MY_PACKAGE_MAINTAINER_NAME} <${CPACK_PACKAGE_CONTACT}>")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION})
set(CPACK_PACKAGE_HOMEPAGE_URL ${PROJECT_HOMEPAGE_URL})
# package name for deb
# if set, then instead of some-application-0.9.2-Linux.deb
# you'll get some-application_0.9.2_amd64.deb (note the underscores too)
set(CPACK_DEBIAN_FILE_NAME DEB-DEFAULT)
# if you want every group to have its own package,
# although the same happens if this is not sent (so it defaults to ONE_PER_GROUP)
# and CPACK_DEB_COMPONENT_INSTALL is set to YES
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)#ONE_PER_GROUP)
# without this you won't be able to pack only specified component
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)
# optionally, you can add various meta information to the components defined in INSTALLs
# cpack_add_component(some-application
# DISPLAY_NAME "Some application"
# DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}"
# #GROUP group1
# )
# cpack_add_component(SomeLibrary
# DISPLAY_NAME "Some library"
# DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}"
# #GROUP group1
# )
# cpack_add_component(AnotherLibrary
# DISPLAY_NAME "Another library"
# DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY}"
# #GROUP group2
# )
# you can also put them into groups
#cpack_add_component_group(group1)
#cpack_add_component_group(group2)
# can be also set as -DCPACK_COMPONENTS_ALL="AnotherLibrary"
#set(CPACK_COMPONENTS_ALL "AnotherLibrary")
message(STATUS "Components to pack: ${CPACK_COMPONENTS_ALL}")

BIN
files/diagram/Proposal.odt Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -0,0 +1,3 @@
# TODO: if/else for different package types
## CPack DEB Generator ##

View File

@ -0,0 +1,19 @@
# NSIS Installer
set(MAKENSIS_EXE "makensis")
if(WIN32)
configure_file(windows/NSIS/constants.nsh.in ${CMAKE_BINARY_DIR}/NSIS/constants.nsh @ONLY)
configure_file(windows/NSIS/installer.nsi ${CMAKE_BINARY_DIR}/NSIS/installer.nsi COPYONLY)
endif()
add_custom_target(NSIS
DEPENDS ${MR_TIMETABLE_PLANNER_TARGET} # First build the application
COMMAND ${MAKENSIS_EXE} installer.nsi
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/NSIS
COMMENT "Building NSIS Win32 installer..."
SOURCES #Make this files visible in IDE editor
windows/NSIS/constants.nsh.in
windows/NSIS/installer.nsi
windows/resources.rc.in
VERBATIM)

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="application/vnd.trainsoftware-mrtplanner-session-sqlite3">
<comment>MRTPlanner Session database file</comment>
<comment xml:lang="it">Sessione Database MRTPlanner</comment>
<sub-class-of type="application/x-sqlite3"/>
<glob pattern="*.ttt"/>
<alias type="application/x-mrtplanner-session-sqlite3"/>
</mime-type>
</mime-info>

View File

@ -0,0 +1,14 @@
[Desktop Entry]
Name=Model Railroad Timetable Planner
Comment=Database application to create and manage model railway sessions
Comment[it]=Applicazione database per creare e gestire sessioni di plastici ferroviari
Keywords=Train;Trains;Railway;Database;Model;Timetable
Keywords[it]=Treno;Treni;Ferrovia;Plastico;Database;Orario
Exec=/opt/mrtplanner/bin/mrtplanner %f
Icon=mrtplanner
Terminal=false
X-MultipleArgs=false
Type=Application
Categories=Development;Utility;Database;
MimeType=application/x-mrtplanner-session-sqlite3
StartupWMClass=mrtplanner

View File

@ -1,7 +1,7 @@
;Definitions
!define APP_NAME "@APP_DISPLAY_NAME@"
!define APP_PRODUCT "@TRAINTIMETABLE_TARGET@"
!define APP_PRODUCT "@MR_TIMETABLE_PLANNER_TARGET@"
!define COMPANY_NAME "@APP_COMPANY_NAME@"
!define DESCRIPTION "@PROJECT_DESCRIPTION@"
@ -19,10 +19,10 @@
# This is the size (in kB) of all the files copied into "Program Files"
!define INSTALLSIZE 8000
!define TRAIN_TIMETABLE_PATH "@CMAKE_BINARY_DIR@\debug\"
!define TRAIN_TIMETABLE_EXTRA "@CMAKE_SOURCE_DIR@\files"
!define TRAIN_TIMETABLE_EXE "@TRAINTIMETABLE_TARGET@.exe"
!define TRAIN_TIMETABLE_SETTINGS "traintimetable_settings.ini"
!define MR_TIMETABLE_PLANNER_PATH "@CMAKE_INSTALL_PREFIX@\@CMAKE_INSTALL_BINDIR@"
!define MR_TIMETABLE_PLANNER_EXTRA "@CMAKE_SOURCE_DIR@\files"
!define MR_TIMETABLE_PLANNER_EXE "@MR_TIMETABLE_PLANNER_TARGET@.exe"
!define MR_TIMETABLE_PLANNER_SETTINGS "mrtp_settings.ini"
!define TRAIN_TIMETABLE_LICENSE "${NSISDIR}\Docs\Modern UI\License.txt" ;TODO
!define TRAIN_TIMETABLE_README "${NSISDIR}\Docs\Modern UI\License.txt" ;TODO
!define MR_TIMETABLE_PLANNER_LICENSE "${NSISDIR}\Docs\Modern UI\License.txt" ;TODO
!define MR_TIMETABLE_PLANNER_README "${NSISDIR}\Docs\Modern UI\License.txt" ;TODO

View File

@ -1,4 +1,4 @@
; Setup TrainTimetable and register variables for .ttt file associations
; Setup ModelRailroadTimetablePlanner and register variables for .ttt file associations
;--------------------------------
;Include Modern UI and FileFunc and LogicLib
@ -27,10 +27,10 @@ Unicode True
SetCompressor /SOLID /FINAL lzma
;Default installation folder
InstallDir "$PROGRAMFILES64\${COMPANY_NAME}\${APP_NAME}" ; x86_64 64-bit
InstallDir "$PROGRAMFILES64\${COMPANY_NAME}\${APP_PRODUCT}" ; x86_64 64-bit
;Get installation folder from registry if available
InstallDirRegKey HKCU "Software\${COMPANY_NAME} ${APP_NAME}" ""
InstallDirRegKey HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}" ""
;Request application privileges for Windows Vista
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
@ -53,7 +53,7 @@ ManifestDPIAware True
#!define MUI_WELCOMEPAGE_TEXT "$(welcome_text)"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE ${TRAIN_TIMETABLE_LICENSE}
!insertmacro MUI_PAGE_LICENSE "${MR_TIMETABLE_PLANNER_LICENSE}"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
@ -61,10 +61,10 @@ ManifestDPIAware True
!define MUI_FINISHPAGE_LINK "$(visit_site)"
!define MUI_FINISHPAGE_LINK_LOCATION ${ABOUTURL}
!define MUI_FINISHPAGE_RUN "$INSTDIR\${TRAIN_TIMETABLE_EXE}"
!define MUI_FINISHPAGE_RUN "$INSTDIR\${MR_TIMETABLE_PLANNER_EXE}"
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
!define MUI_FINISHPAGE_SHOWREADME ${TRAIN_TIMETABLE_README}
!define MUI_FINISHPAGE_SHOWREADME "${MR_TIMETABLE_PLANNER_README}"
!define MUI_FINISHPAGE_SHOWREADME_TEXT "$(show_readme_label)"
!insertmacro MUI_PAGE_FINISH
@ -97,7 +97,7 @@ LangString show_readme_label ${LANG_ITALIAN} "Mostra note di rilascio"
LangString DESC_MainProgram ${LANG_ENGLISH} "Main application and settings files"
LangString DESC_MainProgram ${LANG_ITALIAN} "Applicazione principale e file di configurazione"
LangString DESC_SM_Shortcut ${LANG_ENGLISH} "Create shortcuts to Start Menu. This makes easier to start Train Timetable"
LangString DESC_SM_Shortcut ${LANG_ENGLISH} "Create shortcuts to Start Menu. This makes easier to start Model Railroad Timetable Planner"
LangString DESC_SM_Shortcut ${LANG_ITALIAN} "Crea collegamenti al Menu Start. Questo rende pi${U+00FA} facile l'avvio dell'applicazione" #${U+00FA} = ù (U accentata minuscola)
LangString DESC_FileAss ${LANG_ENGLISH} "Setup file associations to display an icon for Train Timetable Session files and be able to open the application by just double clicking on the file"
@ -151,32 +151,32 @@ Section "Application" main_program
SetOutPath $INSTDIR
File ${TRAIN_TIMETABLE_PATH}\${TRAIN_TIMETABLE_EXE}
File ${TRAIN_TIMETABLE_EXTRA}\icons\icon.ico
File ${MR_TIMETABLE_PLANNER_PATH}\${MR_TIMETABLE_PLANNER_EXE}
File ${MR_TIMETABLE_PLANNER_EXTRA}\icons\icon.ico
File ${TRAIN_TIMETABLE_PATH}\*.dll
File ${MR_TIMETABLE_PLANNER_PATH}\*.dll
SetOutPath $INSTDIR\platforms
File ${TRAIN_TIMETABLE_PATH}\platforms\*.dll
File ${MR_TIMETABLE_PLANNER_PATH}\platforms\*.dll
SetOutPath $INSTDIR\printsupport
File ${TRAIN_TIMETABLE_PATH}\printsupport\*.dll
File ${MR_TIMETABLE_PLANNER_PATH}\printsupport\*.dll
SetOutPath $INSTDIR\styles
File ${TRAIN_TIMETABLE_PATH}\styles\*.dll
File ${MR_TIMETABLE_PLANNER_PATH}\styles\*.dll
SetOutPath $INSTDIR\imageformats
File ${TRAIN_TIMETABLE_PATH}\imageformats\*.dll
File ${MR_TIMETABLE_PLANNER_PATH}\imageformats\*.dll
SetOutPath $INSTDIR\icons
File ${TRAIN_TIMETABLE_EXTRA}\icons\lightning\lightning.svg
File ${MR_TIMETABLE_PLANNER_EXTRA}\icons\lightning\lightning.svg
SetOutPath $INSTDIR\translations
File ${TRAIN_TIMETABLE_PATH}\translations\*.qm
File ${MR_TIMETABLE_PLANNER_PATH}\translations\*.qm
SetOutPath "$LOCALAPPDATA\${COMPANY_NAME}\${APP_NAME}"
SetOutPath "$LOCALAPPDATA\${COMPANY_NAME}\${APP_PRODUCT}"
; Create empty settings file
FileOpen $0 $OUTDIR\${TRAIN_TIMETABLE_SETTINGS} w
FileOpen $0 $OUTDIR\${MR_TIMETABLE_PLANNER_SETTINGS} w
FileClose $0
; Set application language to the language chosen int the installer (default English)
@ -189,35 +189,35 @@ Section "Application" main_program
${Default}
${Break}
${EndSwitch}
WriteINIStr $OUTDIR\${TRAIN_TIMETABLE_SETTINGS} "General" "language" $0
WriteINIStr $OUTDIR\${MR_TIMETABLE_PLANNER_SETTINGS} "General" "language" $0
# Uninstaller - See function un.onInit and section "uninstall" for configuration
WriteUninstaller "$INSTDIR\uninstall.exe"
# Registry information for add/remove programs
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "DisplayName" "${COMPANY_NAME} - ${APP_NAME} - ${DESCRIPTION}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "DisplayIcon" "$\"$INSTDIR\icon.ico$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "Publisher" "$\"${COMPANY_NAME}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "HelpLink" "$\"${HELPURL}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "URLInfoAbout" "$\"${ABOUTURL}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "VersionMajor" ${VERSIONMAJOR}
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "VersionMinor" ${VERSIONMINOR}
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "DisplayName" "${COMPANY_NAME} - ${APP_PRODUCT} - ${DESCRIPTION}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "InstallLocation" "$\"$INSTDIR$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "DisplayIcon" "$\"$INSTDIR\icon.ico$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "Publisher" "$\"${COMPANY_NAME}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "HelpLink" "$\"${HELPURL}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "URLUpdateInfo" "$\"${UPDATEURL}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "URLInfoAbout" "$\"${ABOUTURL}$\""
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\""
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "VersionMajor" ${VERSIONMAJOR}
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "VersionMinor" ${VERSIONMINOR}
# There is no option for modifying or repairing the install
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "NoModify" 1
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "NoRepair" 1
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "NoModify" 1
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "NoRepair" 1
# Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "EstimatedSize" ${INSTALLSIZE}
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "EstimatedSize" ${INSTALLSIZE}
WriteRegStr HKCU "Software\${COMPANY_NAME} ${APP_NAME}" "" $INSTDIR
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_NAME}" "VersionMajor" "${VERSIONMAJOR}"
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_NAME}" "VersionMinor" "${VERSIONMINOR}"
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_NAME}" "VersionRevision" "77"
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_NAME}" "VersionBuild" "${VERSIONBUILD}"
WriteRegStr HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}" "" $INSTDIR
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}" "VersionMajor" "${VERSIONMAJOR}"
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}" "VersionMinor" "${VERSIONMINOR}"
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}" "VersionRevision" "77"
WriteRegDWORD HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}" "VersionBuild" "${VERSIONBUILD}"
SectionEnd
@ -226,7 +226,7 @@ Section "Start Menu Shortcuts" sm_shorcuts
# Start Menu
SetShellVarContext current
CreateDirectory "$SMPROGRAMS\${COMPANY_NAME}"
CreateShortCut "$SMPROGRAMS\${COMPANY_NAME}\${APP_NAME}.lnk" "$INSTDIR\${TRAIN_TIMETABLE_EXE}" "" "$INSTDIR\icon.ico"
CreateShortCut "$SMPROGRAMS\${COMPANY_NAME}\${APP_NAME}.lnk" "$INSTDIR\${MR_TIMETABLE_PLANNER_EXE}" "" "$INSTDIR\icon.ico"
SectionEnd
; Open a section to register file type
@ -234,11 +234,11 @@ Section "File associations" file_ass
SetShellVarContext current
SetOutPath $INSTDIR
WriteRegStr HKCU "Software\Classes\.ttt" "" "Train_Timetable.session"
WriteRegStr HKCU "Software\Classes\.ttt" "" "MR_TIMETABLE_PLANNER.session"
WriteRegStr HKCU "Software\Classes\.ttt" "PerceivedType" "document"
WriteRegStr HKCU "Software\Classes\Train_Timetable.session" "" "Train Timetable Session File"
WriteRegStr HKCU "Software\Classes\Train_Timetable.session\DefaultIcon" "" "$INSTDIR\${TRAIN_TIMETABLE_EXE},0"
WriteRegStr HKCU "Software\Classes\Train_Timetable.session\shell\open\command" "" '$INSTDIR\${TRAIN_TIMETABLE_EXE} "%1"'
WriteRegStr HKCU "Software\Classes\MR_TIMETABLE_PLANNER.session" "" "MRTPlanner Timetable Session File"
WriteRegStr HKCU "Software\Classes\MR_TIMETABLE_PLANNER.session\DefaultIcon" "" "$INSTDIR\${MR_TIMETABLE_PLANNER_EXE},0"
WriteRegStr HKCU "Software\Classes\MR_TIMETABLE_PLANNER.session\shell\open\command" "" '$INSTDIR\${MR_TIMETABLE_PLANNER_EXE} "%1"'
DetailPrint $INSTDIR
@ -294,7 +294,7 @@ Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
ReadRegStr $0 HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}" "UninstallString"
ReadRegStr $0 HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}" "UninstallString"
${If} $0 != ""
${AndIf} ${Cmd} `MessageBox MB_YESNO|MB_ICONQUESTION "$(unist_previous_msg)" /SD IDYES IDYES`
!insertmacro UninstallExisting $0 $0
@ -326,7 +326,7 @@ Section un.main_program
RMDir "$SMPROGRAMS\${COMPANY_NAME}"
# Remove files
Delete $INSTDIR\${TRAIN_TIMETABLE_EXE}
Delete $INSTDIR\${MR_TIMETABLE_PLANNER_EXE}
Delete $INSTDIR\icon.ico
Delete $INSTDIR\icons\lightning.svg
@ -337,11 +337,11 @@ Section un.main_program
# Ask user if they want to delete or keep log files. If they choose to keep them AppData folder is not removed
MessageBox MB_YESNO "$(keep_logs_message)" IDYES delete_settings
RMDir /r "$LOCALAPPDATA\${COMPANY_NAME}\${APP_NAME}\logs"
RMDir /r "$LOCALAPPDATA\${COMPANY_NAME}\${APP_PRODUCT}\logs"
delete_settings:
Delete "$LOCALAPPDATA\${COMPANY_NAME}\${APP_NAME}\${TRAIN_TIMETABLE_SETTINGS}"
RMDir "$LOCALAPPDATA\${COMPANY_NAME}\${APP_NAME}"
Delete "$LOCALAPPDATA\${COMPANY_NAME}\${APP_PRODUCT}\${MR_TIMETABLE_PLANNER_SETTINGS}"
RMDir "$LOCALAPPDATA\${COMPANY_NAME}\${APP_PRODUCT}"
RMDir "$LOCALAPPDATA\${COMPANY_NAME}"
Delete $INSTDIR\*.dll
@ -361,8 +361,8 @@ delete_settings:
RMDir /r $INSTDIR\imageformats
# Remove uninstaller information from the registry
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_NAME}"
DeleteRegKey HKCU "Software\${COMPANY_NAME} ${APP_NAME}"
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANY_NAME} ${APP_PRODUCT}"
DeleteRegKey HKCU "Software\${COMPANY_NAME} ${APP_PRODUCT}"
; Unregister file associations in uninstall.exe
!macro AssocDeleteFileExtAndProgId _hkey _dotext _pid
@ -373,7 +373,7 @@ delete_settings:
DeleteRegKey ${_hkey} "Software\Classes\${_pid}"
!macroend
!insertmacro AssocDeleteFileExtAndProgId HKCU ".ttt" "Train_Timetable.session"
!insertmacro AssocDeleteFileExtAndProgId HKCU ".ttt" "MR_TIMETABLE_PLANNER.session"
DetailPrint $INSTDIR
DetailPrint $OUTDIR

View File

@ -23,7 +23,7 @@ VS_VERSION_INFO VERSIONINFO
VALUE "FileDescription", "${APP_DISPLAY_NAME}\0"
VALUE "FileVersion", "${PROJECT_VERSION}\0"
VALUE "LegalCopyright", "${APP_COMPANY_NAME}\0"
VALUE "OriginalFilename", "${TRAINTIMETABLE_TARGET}.exe\0"
VALUE "OriginalFilename", "${MR_TIMETABLE_PLANNER_TARGET}.exe\0"
VALUE "ProductName", "${APP_PRODUCT_NAME}\0"
VALUE "ProductVersion", "${PROJECT_VERSION}\0"
END

BIN
screenshots/Screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@ -1,9 +1,13 @@
include(GNUInstallDirs)
include(DLL_Utils)
#Set Win32 resources
if (WIN32)
configure_file(../packaging/windows/resources.rc.in ${CMAKE_BINARY_DIR}/resources/resources.rc)
set(TRAINTIMETABLE_RESOURCES
${TRAINTIMETABLE_RESOURCES}
set(MR_TIMETABLE_PLANNER_RESOURCES
${MR_TIMETABLE_PLANNER_RESOURCES}
${CMAKE_BINARY_DIR}/resources/resources.rc
)
endif()
@ -13,7 +17,6 @@ add_subdirectory(backgroundmanager)
add_subdirectory(db_metadata)
add_subdirectory(graph)
add_subdirectory(jobs)
add_subdirectory(lines)
add_subdirectory(odt_export)
add_subdirectory(printing)
add_subdirectory(rollingstock)
@ -27,24 +30,24 @@ add_subdirectory(translations)
add_subdirectory(utils)
add_subdirectory(viewmanager)
# Set TrainTimetable info template file
set(TRAINTIMETABLE_SOURCES
${TRAINTIMETABLE_SOURCES}
# Set ModelRailroadTimetablePlanner info template file
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
app/info.h.in
)
configure_file(app/info.h.in ${CMAKE_BINARY_DIR}/include/info.h)
# Add executable
add_executable(${TRAINTIMETABLE_TARGET} WIN32
${TRAINTIMETABLE_SOURCES}
${TRAINTIMETABLE_UI_FILES}
${TRAINTIMETABLE_RESOURCES}
add_executable(${MR_TIMETABLE_PLANNER_TARGET} WIN32
${MR_TIMETABLE_PLANNER_SOURCES}
${MR_TIMETABLE_PLANNER_UI_FILES}
${MR_TIMETABLE_PLANNER_RESOURCES}
)
# Set compiler options
if(MSVC)
target_compile_options(
${TRAINTIMETABLE_TARGET}
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
/WX
/wd4267
@ -57,7 +60,7 @@ if(MSVC)
)
else()
target_compile_options(
${TRAINTIMETABLE_TARGET}
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
"$<$<CONFIG:RELEASE>:-O2>"
#-Werror
@ -72,7 +75,7 @@ endif()
if(UNIX AND NOT APPLE)
target_link_options(
${TRAINTIMETABLE_TARGET}
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
-rdynamic
)
@ -80,16 +83,15 @@ endif()
# Set include directories
target_include_directories(
${TRAINTIMETABLE_TARGET}
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
${SQLite3_INCLUDE_DIRS}
${ZIP_INCLUDE_DIR}
${CMAKE_BINARY_DIR}/include #For template files
)
# Set link libraries
target_link_libraries(
${TRAINTIMETABLE_TARGET}
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
Qt5::Core
Qt5::Gui
@ -98,95 +100,48 @@ target_link_libraries(
Qt5::PrintSupport
${SQLite3_LIBRARIES}
${ZLIB_LIBRARIES}
${ZIP_LIBRARY}
ssplib::ssplib
)
# Link LibZip
if(TARGET libzip::zip)
# LibZip was found with Config Package
# Include directory setup is automatic
target_link_libraries(
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
libzip::zip
)
get_target_property(LibZip_LIBRARY_TO_INSTALL libzip::zip LOCATION)
else()
# LibZip was found with our Find Module
# Set include directories manually
target_include_directories(
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
${LibZip_INCLUDE_DIRS}
)
target_link_libraries(
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
${LibZip_LIBRARIES}
)
get_dll_library_from_import_library(${LibZip_LIBRARIES} LibZip_LIBRARY_TO_INSTALL)
endif()
if (WIN32)
target_link_libraries(
${TRAINTIMETABLE_TARGET}
${MR_TIMETABLE_PLANNER_TARGET}
PRIVATE
DbgHelp
)
endif()
if(UNIX AND NOT APPLE)
install(TARGETS ${TRAINTIMETABLE_TARGET} RUNTIME DESTINATION bin)
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(${TRAINTIMETABLE_TARGET} PRIVATE ${TRAINTIMETABLE_DEFINITIONS})
target_compile_definitions(${MR_TIMETABLE_PLANNER_TARGET} PRIVATE ${MR_TIMETABLE_PLANNER_DEFINITIONS})
## Doxygen documentation ##
if(DOXYGEN_FOUND)
@ -196,13 +151,13 @@ if(DOXYGEN_FOUND)
set(DOXYGEN_EXTRACT_ALL "YES")
set(DOXYGEN_EXTRACT_PRIVATE "YES")
set(DOXYGEN_DOT_GRAPH_MAX_NODES 100)
doxygen_add_docs(docs ALL ${TRAINTIMETABLE_SOURCES})
doxygen_add_docs(docs ALL ${MR_TIMETABLE_PLANNER_SOURCES})
endif()
## Doxygen end ##
## Update/Release translations ##
#(Run this target before installing installing and every time you update translations)
#(Run this target before installing and every time you update translations)
add_custom_target(RELEASE_TRANSLATIONS ALL
COMMENT "Running translations it_IT...")
@ -211,14 +166,14 @@ if(UPDATE_TS)
if(UPDATE_TS_KEEP_OBSOLETE)
add_custom_command(TARGET RELEASE_TRANSLATIONS
POST_BUILD
COMMAND ${Qt5_LUPDATE_EXECUTABLE} ARGS ${CMAKE_SOURCE_DIR}/src -ts ${TRAINTIMETABLE_TS_FILES}
COMMAND ${Qt5_LUPDATE_EXECUTABLE} ARGS ${CMAKE_SOURCE_DIR}/src -ts ${MR_TIMETABLE_PLANNER_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src
COMMENT "Updating translations"
VERBATIM)
else()
add_custom_command(TARGET RELEASE_TRANSLATIONS
POST_BUILD
COMMAND ${Qt5_LUPDATE_EXECUTABLE} ARGS ${CMAKE_SOURCE_DIR}/src -ts ${TRAINTIMETABLE_TS_FILES} -no-obsolete
COMMAND ${Qt5_LUPDATE_EXECUTABLE} ARGS ${CMAKE_SOURCE_DIR}/src -ts ${MR_TIMETABLE_PLANNER_TS_FILES} -no-obsolete
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src
COMMENT "Updating translations"
VERBATIM)
@ -227,7 +182,7 @@ if(UPDATE_TS)
endif()
# For each .ts file release a .qm file
foreach(TS_FILE ${TRAINTIMETABLE_TS_FILES})
foreach(TS_FILE ${MR_TIMETABLE_PLANNER_TS_FILES})
get_filename_component(QM_FILE_NAME ${TS_FILE} NAME_WLE)
set(QM_FILE "${CMAKE_BINARY_DIR}/src/translations/${QM_FILE_NAME}.qm")
message("Generating translation: ${QM_FILE}")
@ -243,49 +198,87 @@ endforeach()
## Install and Deploy ##
# Copy executable
install(TARGETS ${TRAINTIMETABLE_TARGET}
RUNTIME
DESTINATION ${CMAKE_INSTALL_PREFIX})
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_PREFIX}/icons)
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 ${TRAINTIMETABLE_TS_FILES})
foreach(TS_FILE ${MR_TIMETABLE_PLANNER_TS_FILES})
get_filename_component(QM_FILE_NAME ${TS_FILE} NAME_WLE)
install(FILES
"${CMAKE_BINARY_DIR}/src/translations/${QM_FILE_NAME}.qm"
DESTINATION ${CMAKE_INSTALL_PREFIX}/translations OPTIONAL)
DESTINATION ${CMAKE_INSTALL_BINDIR}/translations OPTIONAL)
endforeach()
if(WIN32)
# Copy SQlite3 DLL
install(PROGRAMS ${SQLite3_LIBRARIES} DESTINATION ${CMAKE_INSTALL_PREFIX})
get_dll_library_from_import_library(${SQLite3_LIBRARIES} SQLite3_LIBRARY_TO_INSTALL)
install(PROGRAMS ${SQLite3_LIBRARY_TO_INSTALL} DESTINATION ${CMAKE_INSTALL_BINDIR})
# Copy ZLib DLL
install(PROGRAMS ${ZLIB_LIBRARIES} DESTINATION ${CMAKE_INSTALL_PREFIX})
get_dll_library_from_import_library(${ZLIB_LIBRARIES} ZLIB_LIBRARY_TO_INSTALL)
install(PROGRAMS ${ZLIB_LIBRARY_TO_INSTALL} DESTINATION ${CMAKE_INSTALL_BINDIR})
# Copy libzip DLL
install(PROGRAMS ${ZIP_LIBRARY} DESTINATION ${CMAKE_INSTALL_PREFIX})
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}\")
execute_process(COMMAND ${WINDEPLOYQT_EXE} ${CMAKE_INSTALL_PREFIX}
WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}
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_EXE_RESULT}\")
message(STATUS \"windeployqt Done.\")
message(STATUS \"${windeployqt_exe} Done.\")
")
endif()
endif()
if(UNIX AND NOT APPLE)
set(LINUX_ICON_DIR_BASE "/usr/share/icons/hicolor")
# Install PNG icons
set(MY_ICON_SIZES "16;24;32;64;128;256;512")
foreach(ICON_SIZE ${MY_ICON_SIZES})
install(FILES ${CMAKE_SOURCE_DIR}/packaging/linux/icons/train_${ICON_SIZE}.png
DESTINATION ${LINUX_ICON_DIR_BASE}/${ICON_SIZE}x${ICON_SIZE}/apps/
RENAME ${MR_TIMETABLE_PLANNER_TARGET}.png)
install(FILES ${CMAKE_SOURCE_DIR}/packaging/linux/icons/train_${ICON_SIZE}.png
DESTINATION ${LINUX_ICON_DIR_BASE}/${ICON_SIZE}x${ICON_SIZE}/mimetypes/
RENAME application-x-${MR_TIMETABLE_PLANNER_TARGET}-session-sqlite3.png)
endforeach()
# Install SVG icon
install(FILES ${CMAKE_SOURCE_DIR}/packaging/linux/icons/train.svg
DESTINATION ${LINUX_ICON_DIR_BASE}/scalable/apps/
RENAME ${MR_TIMETABLE_PLANNER_TARGET}.svg)
install(FILES ${CMAKE_SOURCE_DIR}/packaging/linux/icons/train.svg
DESTINATION ${LINUX_ICON_DIR_BASE}/scalable/mimetypes/
RENAME application-vnd.${APP_COMPANY_NAME_LOWER}-${MR_TIMETABLE_PLANNER_TARGET}-session-sqlite3.svg)
# Install desktop file
install(FILES ${CMAKE_SOURCE_DIR}/packaging/linux/${MR_TIMETABLE_PLANNER_TARGET}.desktop
DESTINATION /usr/share/applications/)
# Install mime type association
#vnd.trainsoftware-mrtplanner.xml
install(FILES ${CMAKE_SOURCE_DIR}/packaging/linux/mime/vnd.${APP_COMPANY_NAME_LOWER}-${MR_TIMETABLE_PLANNER_TARGET}.xml
DESTINATION /usr/share/mime/packages)
endif(UNIX AND NOT APPLE)
## Install end ##

View File

@ -1,5 +1,5 @@
set(TRAINTIMETABLE_SOURCES
${TRAINTIMETABLE_SOURCES}
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
app/main.cpp
app/mainwindow.h
app/mainwindow.cpp
@ -12,8 +12,8 @@ set(TRAINTIMETABLE_SOURCES
PARENT_SCOPE
)
set(TRAINTIMETABLE_UI_FILES
${TRAINTIMETABLE_UI_FILES}
set(MR_TIMETABLE_PLANNER_UI_FILES
${MR_TIMETABLE_PLANNER_UI_FILES}
app/mainwindow.ui
PARENT_SCOPE
)

View File

@ -6,7 +6,9 @@
static const QString AppVersion = QStringLiteral("${PROJECT_VERSION}");
static const QString AppCompany = QStringLiteral("${APP_COMPANY_NAME}");
static const QString AppProduct = QStringLiteral("${APP_PRODUCT_NAME}");
static const QString AppProductShort = QStringLiteral("${MR_TIMETABLE_PLANNER_TARGET}");
static const QString AppDisplayName = QStringLiteral("${APP_DISPLAY_NAME}");
static const QString AppProjectWebSite = QStringLiteral("${PROJECT_HOMEPAGE_URL}");
static const QString FormatVersionStr = QStringLiteral("${DB_FORMAT_VERSION}");

View File

@ -1,19 +1,32 @@
/*
* 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"
#include <QTranslator>
#include "utils/localization/languageutils.h"
#include <QTextStream>
#include <QFile>
#include <QDir>
#include <QDebug>
#include <QSettings>
#include <QFile>
#include <QTextStream>
#include <QDateTime>
#include "info.h"
@ -22,7 +35,7 @@
#include <QMutex>
#include <QStandardPaths>
#include <QDebug>
Q_GLOBAL_STATIC(QFile, gLogFile)
static QtMessageHandler defaultHandler;
@ -33,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")).arg(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")).arg(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")).arg(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")).arg(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")).arg(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());
@ -58,143 +77,126 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS
defaultHandler(type, context, msg);
}
void loadTranslations()
{
QLocale locale = AppSettings.getLanguage();
qDebug() << "Locale:" << locale << locale.uiLanguages();
QString path = qApp->applicationDirPath() + QStringLiteral("/translations");
qDebug() << path;
QTranslator *translatorQt = new QTranslator(qApp);
if(translatorQt->load(locale,
QStringLiteral("qt"), QStringLiteral("_"),
path))
{
qDebug() << "Loading Qt translations";
qApp->installTranslator(translatorQt);
}
QTranslator *translator = new QTranslator(qApp);
if(translator->load(locale,
QStringLiteral("traintimetable"), QStringLiteral("_"),
path))
{
qDebug() << "Loading UI translations";
qApp->installTranslator(translator);
}
}
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/traintimetable_log.log"));
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;
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;
loadTranslations();
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;

File diff suppressed because it is too large Load Diff

View File

@ -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
@ -15,18 +34,13 @@ class database;
using namespace sqlite3pp;
class QGraphicsView;
class QGraphicsScene;
class LineGraphWidget;
class JobPathEditor;
class QDockWidget;
class QLabel;
class QActionGroup;
class CustomCompletionLineEdit;
#ifdef ENABLE_RS_CHECKER
class RsErrorsWidget;
#endif
class MainWindow : public QMainWindow
{
Q_OBJECT
@ -43,7 +57,7 @@ public:
void loadFile(const QString &fileName);
bool closeSession();
private slots:
void onStationManager();
@ -80,6 +94,8 @@ private slots:
void checkLineNumber();
protected:
void timerEvent(QTimerEvent *e) override;
void closeEvent(QCloseEvent *e) override;
bool eventFilter(QObject *watched, QEvent *event) override;
@ -89,12 +105,13 @@ private slots:
void onSessionRSViewer();
void onJobSearchItemSelected(db_id jobId);
void onJobSearchItemSelected();
void onJobSearchResultsReady();
private:
void setup_actions();
void showCloseWarning();
void stopCloseTimer();
enum class CentralWidgetMode
{
@ -110,25 +127,28 @@ private:
JobPathEditor *jobEditor;
#ifdef ENABLE_RS_CHECKER
RsErrorsWidget *rsErrorsWidget;
QDockWidget *rsErrDock;
#endif
#ifdef ENABLE_BACKGROUND_MANAGER
QDockWidget *resPanelDock;
#endif // ENABLE_BACKGROUND_MANAGER
QGraphicsView *view;
LineGraphWidget *view;
QDockWidget *jobDock;
CustomCompletionLineEdit *searchEdit;
CustomCompletionLineEdit *lineComboSearch;
QLabel *welcomeLabel;
QActionGroup *databaseActionGroup;
enum { MaxRecentFiles = 5 };
QAction* recentFileActs[MaxRecentFiles];
enum
{
MaxRecentFiles = 5
};
QAction *recentFileActs[MaxRecentFiles];
CentralWidgetMode m_mode;
int closeTimerId;
};
#endif // MAINWINDOW_H

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
<width>500</width>
<height>400</height>
</rect>
</property>
<property name="windowTitle">
@ -21,8 +21,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>26</height>
<width>500</width>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -122,7 +122,7 @@
<string>Remove Job</string>
</property>
<property name="toolTip">
<string>Remove selected train job</string>
<string>Remove Job</string>
</property>
</action>
<action name="actionStations">
@ -205,7 +205,7 @@
</action>
<action name="actionNext_Job_Segment">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>Next Job Segment</string>
@ -213,7 +213,7 @@
</action>
<action name="actionPrev_Job_Segment">
<property name="enabled">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="text">
<string>Prev Job Segment</string>

View File

@ -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);
}

View File

@ -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

View File

@ -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();
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,26 @@
/*
* 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
#include "utils/types.h"
#include "utils/directiontype.h"
#include <sqlite3pp/sqlite3pp.h>
using namespace sqlite3pp;
@ -15,9 +33,6 @@ using namespace sqlite3pp;
#include <settings/appsettings.h>
class LineStorage;
class JobStorage;
class ViewManager;
class MetaDataManager;
@ -25,6 +40,8 @@ class MetaDataManager;
class BackgroundManager;
#endif
class QTranslator;
enum class DB_Error
{
NoError = 0,
@ -36,51 +53,75 @@ enum class DB_Error
FormatTooNew
};
//TODO: reorder functions
/*!
* \brief The MeetingSession class
*
* This class is a singleton.
* It stores all informations about current loaded session and settings
*/
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 SYNC: wire them from models
// Rollingstock
void rollingstockRemoved(db_id rsId);
void rollingStockPlanChanged(db_id rsId);
void rollingStockPlanChanged(QSet<db_id> rsIds);
void rollingStockModified(db_id rsId);
//Jobs
void jobChanged(db_id jobId, db_id oldJobId); //Updated id/category/stops
// Jobs
void jobAdded(db_id jobId);
void jobChanged(db_id jobId, db_id oldJobId); // Updated id/category/stops
void jobRemoved(db_id jobId);
//TODO: old methods, remove them
public:
qreal getStationGraphPos(db_id lineId, db_id stId, int platf = 0);
// Stations
void stationNameChanged(db_id stationId);
void stationJobsPlanChanged(const QSet<db_id> &stationIds);
void stationTrackPlanChanged(const QSet<db_id> &stationIds);
void stationRemoved(db_id stationId);
bool getPrevStop(db_id stopId, db_id &prevSt, db_id &lineId);
bool getNextStop(db_id stopId, db_id &nextSt, db_id &lineId);
Direction getStopDirection(db_id stopId, db_id stId);
// Segments
void segmentAdded(db_id segmentId);
void segmentNameChanged(db_id segmentId);
void segmentStationsChanged(db_id segmentId);
void segmentRemoved(db_id segmentId);
// Lines
void lineAdded(db_id lineId);
void lineNameChanged(db_id lineId);
void lineSegmentsChanged(db_id lineId);
void lineRemoved(db_id lineId);
private:
std::unique_ptr<ViewManager> viewManager;
@ -91,71 +132,130 @@ private:
std::unique_ptr<BackgroundManager> backgroundManager;
#endif
public:
LineStorage *mLineStorage;
JobStorage *mJobStorage;
//Settings TODO: remove
// Settings TODO: remove
public:
void loadSettings(const QString &settings_file);
TrainTimetableSettings settings;
MRTPSettings settings;
int hourOffset;
int stationOffset;
qreal platformOffset;
int hourOffset;
int stationOffset;
qreal platformOffset;
int horizOffset;
int vertOffset;
int horizOffset;
int vertOffset;
int jobLineWidth;
//Queries TODO: remove
// Database
public:
database m_Db;
query q_getPrevStop;
query q_getNextStop;
query q_getKmDirection;
//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();
bool clearImportRSTables();
void prepareQueryes();
void finalizeStatements();
QString fileName;
QString fileName; //TODO: re organize variables
//AppData
// AppData
public:
static void locateAppdata();
static QString appDataPath;
private:
/*!
* \brief sheetExportTranslator
*
* Custom translator for Sheet Export
* When it's valid it should be used before application translations
* When it's \c nullptr, application translations should be used
* Special case: when it's \c nullptr and \a sheetExportLocale is \c QLocale(QLocale::English)
* which is default language, then use hardcoded string literals and bypass any translation.
*
* \sa sheetExportLocale
* \sa setSheetExportTranslator()
*/
QTranslator *sheetExportTranslator;
/*!
* \brief sheetExportLocale
*
* Locale for Sheet Export
* \sa sheetExportTranslator
*/
QLocale sheetExportLocale;
/*!
* \brief originalAppLocale
* Store original language in which application was loaded
* When user changes Application Language in settings a restart
* is required to apply it but settings reports already new language.
* So use this variable to get original settings value before restart.
*/
QLocale originalAppLocale;
/*!
* \brief setSheetExportTranslator
* \param translator
* \param loc
*
* Set translator for Sheet Export
* \sa sheetExportTranslator
*/
public:
/*!
* \brief Embedded Locale
*
* This represents the QLocale of embedded strings (American English).
* This is the default Application Language if no translations are loaded
* If user choose this language no translations need to be loaded.
*/
static const QLocale embeddedLocale;
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;
}
};
#define Session MeetingSession::Get()
#define Session MeetingSession::Get()
#define AppSettings Session->settings

View File

@ -1,6 +1,18 @@
set(TRAINTIMETABLE_SOURCES
${TRAINTIMETABLE_SOURCES}
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
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
set(TRAINTIMETABLE_SOURCES
${TRAINTIMETABLE_SOURCES}
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
db_metadata/imagemetadata.h
db_metadata/imagemetadata.cpp
db_metadata/meetinginformationdialog.h
@ -9,8 +9,8 @@ set(TRAINTIMETABLE_SOURCES
PARENT_SCOPE
)
set(TRAINTIMETABLE_UI_FILES
${TRAINTIMETABLE_UI_FILES}
set(MR_TIMETABLE_PLANNER_UI_FILES
${MR_TIMETABLE_PLANNER_UI_FILES}
db_metadata/meetinginformationdialog.ui
PARENT_SCOPE
)

View File

@ -1,32 +1,115 @@
/*
* 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>
namespace ImageMetaData
{
#include <QDebug>
namespace ImageMetaData {
constexpr char sql_get_key_id[] = "SELECT rowid FROM metadata WHERE name=? AND val NOT NULL";
ImageBlobDevice::ImageBlobDevice(sqlite3 *db, qint64 rowId, QObject *parent) :
ImageBlobDevice::ImageBlobDevice(sqlite3 *db, QObject *parent) :
QIODevice(parent),
mRowId(rowId),
mRowId(0),
mSize(0),
mDb(db),
mBlob(nullptr)
{
}
ImageBlobDevice::~ImageBlobDevice()
{
close();
ImageBlobDevice::close();
}
void ImageBlobDevice::setBlobInfo(const QByteArray &table, const QByteArray &column, qint64 rowId)
{
mRowId = rowId;
mTable = table;
mColumn = column;
}
bool ImageBlobDevice::reserveSizeAndReset(qint64 len)
{
// NOTE: this will discard any previous content
// Close previous BLOB handle
if (mBlob)
close();
// 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)
{
qWarning() << "ImageBlobDevice::reserveSizeAndReset cannot prepare:" << sqlite3_errmsg(mDb);
setErrorString(tr("Cannot query database"));
return false;
}
// Reserve BLOB memory
rc = sqlite3_bind_zeroblob64(stmt, 1, len);
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return false;
}
rc = sqlite3_bind_int64(stmt, 2, mRowId);
if (rc != SQLITE_OK)
{
sqlite3_finalize(stmt);
return false;
}
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
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
return open(QIODevice::ReadWrite);
}
bool ImageBlobDevice::open(QIODevice::OpenMode mode)
{
mode |= QIODevice::ReadOnly;
int rc = sqlite3_blob_open(mDb, "main", "metadata", "val", mRowId, (mode & QIODevice::WriteOnly) != 0, &mBlob);
if(rc != SQLITE_OK || !mBlob)
if (isOpen())
{
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)
{
mBlob = nullptr;
setErrorString(sqlite3_errmsg(mDb));
@ -42,7 +125,7 @@ bool ImageBlobDevice::open(QIODevice::OpenMode mode)
void ImageBlobDevice::close()
{
if(mBlob)
if (mBlob)
{
sqlite3_blob_close(mBlob);
mBlob = nullptr;
@ -59,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));
@ -82,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;
@ -129,17 +213,19 @@ ImageBlobDevice* getImage(sqlite3pp::database& db, const MetaDataManager::Key &k
rowId = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
if(!rowId)
if (!rowId)
return nullptr;
return new ImageBlobDevice(db.db(), rowId);
ImageBlobDevice *dev = new ImageBlobDevice(db.db());
dev->setBlobInfo("metadata", "val", rowId);
return dev;
}
void setImage(sqlite3pp::database& db, const MetaDataManager::Key &key, const void *data, int size)
void setImage(sqlite3pp::database &db, const MetaDataManager::Key &key, const void *data, int size)
{
sqlite3pp::command cmd(db, "REPLACE INTO metadata(name, val) VALUES(?, ?)");
sqlite3_bind_text(cmd.stmt(), 1, key.str, key.len, SQLITE_STATIC);
if(data)
if (data)
sqlite3_bind_blob(cmd.stmt(), 2, data, size, SQLITE_STATIC);
else
sqlite3_bind_null(cmd.stmt(), 2);

View File

@ -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,16 +27,20 @@
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_blob sqlite3_blob;
namespace ImageMetaData
{
namespace ImageMetaData {
// TODO: move to utils
class ImageBlobDevice : public QIODevice
{
Q_OBJECT
public:
ImageBlobDevice(sqlite3 *db, qint64 rowId, QObject *parent = nullptr);
ImageBlobDevice(sqlite3 *db, QObject *parent = nullptr);
~ImageBlobDevice() override;
void setBlobInfo(const QByteArray &table, const QByteArray &column, qint64 rowId);
bool reserveSizeAndReset(qint64 len);
virtual bool open(OpenMode mode) override;
virtual void close() override;
@ -32,10 +55,13 @@ private:
qint64 mSize;
sqlite3 *mDb;
sqlite3_blob *mBlob;
QByteArray mTable;
QByteArray mColumn;
};
ImageBlobDevice *getImage(sqlite3pp::database& db, const MetaDataManager::Key& key);
void setImage(sqlite3pp::database& db, const MetaDataManager::Key &key, const void *data, int size);
ImageBlobDevice *getImage(sqlite3pp::database &db, const MetaDataManager::Key &key);
void setImage(sqlite3pp::database &db, const MetaDataManager::Key &key, const void *data, int size);
} // namespace ImageMetaData

View File

@ -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"
@ -11,9 +30,10 @@
#include <QBuffer>
#include <QMessageBox>
#include <QFileDialog>
#include <QStandardPaths>
#include "utils/files/recentdirstore.h"
#include "utils/imageviewer.h"
#include "utils/delegates/imageviewer/imageviewer.h"
#include "utils/owningqpointer.h"
#include <QDebug>
@ -27,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);
}
}
@ -62,7 +88,7 @@ bool MeetingInformationDialog::loadData()
{
MetaDataManager *meta = Session->getMetaDataManager();
qint64 tmp = 0;
qint64 tmp = 0;
QDate date;
switch (meta->getInt64(tmp, MetaDataKey::MeetingStartDate))
@ -73,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();
}
@ -106,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;
@ -125,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());
@ -150,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);
}
@ -169,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();
}
}
@ -181,69 +214,76 @@ void MeetingInformationDialog::saveData()
void MeetingInformationDialog::showImage()
{
ImageViewer dlg(this);
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();
}
}
dlg.setImage(img);
dlg->setImage(img);
dlg.exec();
dlg->exec();
if(!needsToSaveImg)
img = QImage(); //Cleanup to free memory
if (!needsToSaveImg)
img = QImage(); // Cleanup to free memory
}
void MeetingInformationDialog::importImage()
{
QFileDialog dlg(this, tr("Import image"));
dlg.setFileMode(QFileDialog::ExistingFile);
dlg.setAcceptMode(QFileDialog::AcceptOpen);
dlg.setDirectory(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
const QLatin1String meeting_image_key = QLatin1String("meeting_image_key");
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));
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);
dlg->setMimeTypeFilters(filters);
if(dlg.exec() != QDialog::Accepted)
if (dlg->exec() != QDialog::Accepted || !dlg)
return;
QString fileName = dlg.selectedUrls().value(0).toLocalFile();
if(fileName.isEmpty())
QString fileName = dlg->selectedUrls().value(0).toLocalFile();
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."));
@ -251,7 +291,7 @@ void MeetingInformationDialog::importImage()
return;
}
img = image;
img = image;
needsToSaveImg = true;
}
}
@ -260,10 +300,10 @@ void MeetingInformationDialog::removeImage()
{
int ret = QMessageBox::question(this, tr("Remove image?"),
tr("Are you sure to remove the image logo?"));
if(ret != QMessageBox::Yes)
if (ret != QMessageBox::Yes)
return;
img = QImage(); //Cleanup to free memory
img = QImage(); // Cleanup to free memory
needsToSaveImg = true;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -1,16 +1,8 @@
set(TRAINTIMETABLE_SOURCES
${TRAINTIMETABLE_SOURCES}
graph/backgroundhelper.h
graph/backgroundhelper.cpp
graph/graphicsscene.h
graph/graphicsscene.cpp
graph/graphicsview.h
graph/graphicsview.cpp
graph/graphmanager.h
graph/graphmanager.cpp
graph/hourpane.h
graph/hourpane.cpp
graph/stationlayer.h
graph/stationlayer.cpp
PARENT_SCOPE
add_subdirectory(model)
add_subdirectory(view)
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
graph/linegraphtypes.h
graph/linegraphtypes.cpp PARENT_SCOPE
)

View File

@ -1,175 +0,0 @@
#include "backgroundhelper.h"
#include "app/scopedebug.h"
#include <QGraphicsScene>
#include <QGraphicsLineItem>
#include <QGraphicsSimpleTextItem>
#include <QPainter>
#include <QtMath>
#include "app/session.h"
BackgroundHelper::BackgroundHelper(QObject *parent) :
QObject(parent)
{
}
BackgroundHelper::~BackgroundHelper()
{
}
void BackgroundHelper::setHourLinePen(const QPen& pen)
{
if(hourLinePen == pen)
return;
hourLinePen = pen;
emit updateGraph();
}
void BackgroundHelper::setHourTextPen(const QPen &pen)
{
if(hourTextPen == pen)
return;
hourTextPen = pen;
emit updateGraph();
}
void BackgroundHelper::setHourTextFont(const QFont& font)
{
if(hourTextFont == font)
return;
hourTextFont = font;
emit updateGraph();
}
void BackgroundHelper::setHourOffset(qreal value)
{
hourOffset = value;
emit updateGraph();
}
void BackgroundHelper::setVertOffset(qreal value)
{
vertOffset = value;
emit vertOffsetChanged();
emit updateGraph();
}
qreal BackgroundHelper::getVertOffset() const
{
return vertOffset;
}
void BackgroundHelper::setHourHorizOffset(qreal value)
{
hourHorizOffset = value;
emit horizHorizOffsetChanged();
emit updateGraph();
}
qreal BackgroundHelper::getHourHorizOffset() const
{
return hourHorizOffset;
}
void BackgroundHelper::drawBackgroundLines(QPainter *painter, const QRectF &rect)
{
const qreal x1 = qMax(qreal(hourHorizOffset), rect.left());
const qreal x2 = rect.right();
const qreal t = qMax(rect.top(), vertOffset);
const qreal b = rect.bottom();
if(x1 > x2 || b < vertOffset || t > b)
return;
qreal f = std::remainder(t - vertOffset, hourOffset);
if(f < 0)
f += hourOffset;
qreal f1 = qFuzzyIsNull(f) ? vertOffset : qMax(t - f + hourOffset, vertOffset);
const qreal l = std::remainder(b - vertOffset, hourOffset);
const qreal l1 = b - l;
std::size_t n = std::size_t((l1 - f1)/hourOffset) + 1;
QLineF *arr = new QLineF[n];
for(std::size_t i = 0; i < n; i++)
{
arr[i] = QLineF(x1, f1, x2, f1);
f1 += hourOffset;
}
painter->setPen(hourLinePen);
painter->drawLines(arr, int(n));
delete [] arr;
}
void BackgroundHelper::drawForegroundHours(QPainter *painter, const QRectF &rect, int scroll)
{
painter->setFont(hourTextFont);
painter->setPen(hourTextPen);
//qDebug() << "Drawing hours..." << rect << scroll;
const QString fmt(QStringLiteral("%1:00"));
const qreal top = scroll;
const qreal bottom = rect.bottom();
int h = qFloor(top / hourOffset);
qreal y = h * hourOffset - scroll + vertOffset;
for(; h <= 24 && y <= bottom; h++)
{
//qDebug() << "Y:" << y << fmt.arg(h);
painter->drawText(QPointF(5, y + 8), fmt.arg(h)); //y + 8 to center text vertically
y += hourOffset;
}
}
void BackgroundHelper::drawForegroundStationLabels(QPainter *painter, const QRectF &rect, int hScroll, db_id lineId)
{
query q(Session->m_Db, "SELECT s.name,s.short_name,s.platforms,s.depot_platf FROM railways"
" JOIN stations s ON s.id=railways.stationId"
" WHERE railways.lineId=? ORDER BY railways.pos_meters ASC");
q.bind(1, lineId);
QFont f;
f.setBold(true);
f.setPointSize(15);
painter->setFont(f);
painter->setPen(AppSettings.getStationTextColor());
const qreal platformOffset = Session->platformOffset;
const int stationOffset = Session->stationOffset;
qreal x = Session->horizOffset;
QRectF r = rect;
for(auto station : q)
{
QString stName;
if(station.column_bytes(1) == 0)
stName = station.get<QString>(0); //Fallback to full name
else
stName = station.get<QString>(1);
int platf = station.get<int>(2);
platf += station.get<int>(3);
r.setLeft(x - hScroll); //Eat width
painter->drawText(r, Qt::AlignVCenter, stName);
x += stationOffset + platf * platformOffset;
}
}

View File

@ -1,50 +0,0 @@
#ifndef BACKGROUNDHELPER_H
#define BACKGROUNDHELPER_H
#include <QObject>
#include <QPen>
#include <QFont>
#include "utils/types.h"
class BackgroundHelper : public QObject
{
Q_OBJECT
public:
BackgroundHelper(QObject *parent = nullptr);
~BackgroundHelper();
void setHourLinePen(const QPen &pen);
void setHourTextPen(const QPen &pen);
void setHourTextFont(const QFont &font);
void setHourOffset(qreal value);
void setVertOffset(qreal value);
qreal getVertOffset() const;
void setHourHorizOffset(qreal value);
qreal getHourHorizOffset() const;
void drawBackgroundLines(QPainter *painter, const QRectF& rect);
void drawForegroundHours(QPainter *painter, const QRectF& rect, int scroll);
void drawForegroundStationLabels(QPainter *painter, const QRectF& rect, int hScroll, db_id lineId);
signals:
void updateGraph();
void vertOffsetChanged();
void horizHorizOffsetChanged();
private:
qreal hourOffset;
qreal vertOffset;
qreal hourHorizOffset;
QFont hourTextFont;
QPen hourTextPen;
QPen hourLinePen;
};
#endif // BACKGROUNDHELPER_H

View File

@ -1,22 +0,0 @@
#include "graphicsscene.h"
GraphicsScene::GraphicsScene(QObject *parent) : QGraphicsScene(parent)
{
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
/* This is needed to clear selection in some nasty cases:
* 1 - select a job in Line1 (JobPathEditor opens...)
* 2 - change to Line2 (but the selected job is not in Line2)
* 3 - JobPathEditor is still open and you want to lose it
* 4 - clicking in empty area won't emit 'selectionChanged()' because
* the selection was already emty so we need to subclass
* and manually emit 'selectionCleared()'
*/
QGraphicsScene::mousePressEvent(e);
if(selectedItems().isEmpty())
emit selectionCleared();
}

View File

@ -1,19 +0,0 @@
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = nullptr);
signals:
void selectionCleared();
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e);
};
#endif // GRAPHICSSCENE_H

View File

@ -1,77 +0,0 @@
#include "graphicsview.h"
#include "graphmanager.h"
#include "backgroundhelper.h"
#include "hourpane.h"
#include "stationlayer.h"
#include <QScrollBar>
#include <QDebug>
#include <QResizeEvent>
#include <QMoveEvent>
GraphicsView::GraphicsView(GraphManager *mgr, QWidget *parent) :
QGraphicsView(parent),
helper(mgr->getBackGround())
{
//setRenderHint(QPainter::Antialiasing); It blurs background lines with big pen sizes
setAlignment(Qt::AlignLeft | Qt::AlignTop);
hourPane = new HourPane(helper, this);
stationLayer = new StationLayer(mgr, this);
updateHourHorizOffset();
updateStationVertOffset();
connect(verticalScrollBar(), &QScrollBar::valueChanged, hourPane, &HourPane::setScroll);
connect(horizontalScrollBar(), &QScrollBar::valueChanged, stationLayer, &StationLayer::setScroll);
connect(helper, &BackgroundHelper::horizHorizOffsetChanged, this, &GraphicsView::updateHourHorizOffset);
connect(helper, &BackgroundHelper::vertOffsetChanged, this, &GraphicsView::updateStationVertOffset);
connect(helper, &BackgroundHelper::updateGraph, this, static_cast<void(QGraphicsView::*)()>(&GraphicsView::update));
}
void GraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
{
helper->drawBackgroundLines(painter, rect);
}
void GraphicsView::updateHourHorizOffset()
{
hourPane->resize(int(helper->getHourHorizOffset()) - 5, viewport()->height());
}
void GraphicsView::updateStationVertOffset()
{
stationLayer->resize(viewport()->width(), int(helper->getVertOffset()) - 5);
}
void GraphicsView::redrawStationNames()
{
stationLayer->update();
}
bool GraphicsView::viewportEvent(QEvent *e)
{
switch (e->type())
{
case QEvent::Resize:
{
//qDebug() << e << "Resizing HourPane";
//qDebug() << "View:" << rect() << "Viewport:" << viewport()->rect();
hourPane->resize(hourPane->width(), viewport()->height());
hourPane->setScroll(verticalScrollBar()->value());
stationLayer->resize(viewport()->width(), stationLayer->height());
stationLayer->setScroll(horizontalScrollBar()->value());
break;
}
default:
break;
}
return QGraphicsView::viewportEvent(e);
}

View File

@ -1,32 +0,0 @@
#ifndef GRAPHICSVIEW_H
#define GRAPHICSVIEW_H
#include <QGraphicsView>
class HourPane;
class StationLayer;
class BackgroundHelper;
class GraphManager;
class GraphicsView : public QGraphicsView
{
public:
GraphicsView(GraphManager *mgr, QWidget *parent = nullptr);
void redrawStationNames();
public slots:
void updateStationVertOffset();
void updateHourHorizOffset();
protected:
void drawBackground(QPainter *painter, const QRectF &rect) override;
bool viewportEvent(QEvent *e) override;
private:
BackgroundHelper *helper;
HourPane *hourPane;
StationLayer *stationLayer;
};
#endif // GRAPHICSVIEW_H

View File

@ -1,244 +0,0 @@
#include "graphmanager.h"
#include "app/session.h"
#include "viewmanager/viewmanager.h"
#include "backgroundhelper.h"
#include "graphicsview.h"
#include "graphicsscene.h"
#include "utils/model_roles.h"
#include "lines/linestorage.h"
#include "app/scopedebug.h"
#include <QGraphicsSimpleTextItem>
GraphManager::GraphManager(QObject *parent) :
QObject(parent),
backGround(nullptr),
curLineId(0),
curJobId(0)
{
backGround = new BackgroundHelper(this);
lineStorage = Session->mLineStorage;
connect(&AppSettings, &TrainTimetableSettings::jobGraphOptionsChanged, this, &GraphManager::updateGraphOptions);
updateGraphOptions();
view = new GraphicsView(this);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
connect(lineStorage, &LineStorage::lineNameChanged, this, &GraphManager::onLineNameChanged);
connect(lineStorage, &LineStorage::lineStationsModified, this, &GraphManager::onLineModified);
connect(lineStorage, &LineStorage::lineRemoved, this, &GraphManager::onLineRemoved);
}
GraphManager::~GraphManager()
{
}
bool GraphManager::setCurrentLine(db_id lineId)
{
if(curLineId == lineId)
return true;
if(lineId < 0)
lineId = 0;
LineStorage *lines = Session->mLineStorage;
bool error = false;
GraphicsScene *scene = static_cast<GraphicsScene *>(view->scene());
if(scene)
{
disconnect(scene, &QGraphicsScene::selectionChanged, this, &GraphManager::onSelectionChanged);
disconnect(scene, &GraphicsScene::selectionCleared, this, &GraphManager::onSelectionCleared);
scene->clearSelection();
}
view->setScene(nullptr);
scene = nullptr;
if(curLineId)
lines->releaseLine(curLineId);
if(lineId > 0)
{
if(lines->increfLine(lineId) && (scene = static_cast<GraphicsScene *>(lineStorage->sceneForLine(lineId))))
{
connect(scene, &QGraphicsScene::selectionChanged, this, &GraphManager::onSelectionChanged);
connect(scene, &GraphicsScene::selectionCleared, this, &GraphManager::onSelectionCleared);
view->setScene(scene);
view->centerOn(0.0, 0.0);
}
else
{
error = true;
lineId = 0;
}
}
view->redrawStationNames();
curLineId = lineId;
emit currentLineChanged(curLineId);
return !error;
}
void GraphManager::onSelectionChanged()
{
//TODO: single selection. Ctrl+click allow to select multiple items but only the first one is considerated
QGraphicsScene *scene = view->scene();
if(scene && !scene->selectedItems().isEmpty())
{
auto sel = scene->selectedItems();
QGraphicsItem *item = sel.first();
db_id jobId = item->data(JOB_ID_ROLE).toLongLong();
if(curJobId == jobId)
return;
curJobId = jobId;
Session->getViewManager()->requestJobEditor(jobId);
emit jobSelected(jobId);
}
else
{
onSelectionCleared();
}
}
void GraphManager::onSelectionCleared()
{
curJobId = 0;
Session->getViewManager()->requestClearJob();
emit jobSelected(0);
}
BackgroundHelper *GraphManager::getBackGround() const
{
return backGround;
}
JobSelection GraphManager::getSelectedJob()
{
if(!view)
return {0, 0}; //NULL
QGraphicsScene *scene = view->scene();
if(!scene)
return {0, 0};
auto selection = scene->selectedItems();
if(selection.isEmpty())
return {0, 0};
QGraphicsItem *item = selection.first();
db_id jobId = item->data(JOB_ID_ROLE).toLongLong();
db_id segmentId = item->data(SEGMENT_ROLE).toLongLong();
return {jobId, segmentId};
}
void GraphManager::clearSelection()
{
QGraphicsScene *scene = view->scene();
if(scene)
{
scene->clearSelection();
}
}
/* db_id GraphManager::showFirstLine()
* Tries to select first line in Alphabetical order
* If it succeds returs its lineId
* Otherwise returns 0
*/
db_id GraphManager::getFirstLineId()
{
db_id firstLineId = 0;
if(Session->m_Db.db())
{
query q(Session->m_Db, "SELECT id,MIN(name) FROM lines");
q.step();
firstLineId = q.getRows().get<db_id>(0);
}
return firstLineId;
}
db_id GraphManager::getCurLineId() const
{
return curLineId;
}
GraphicsView *GraphManager::getView() const
{
return view;
}
void GraphManager::updateGraphOptions()
{
//TODO: maybe get rid of theese variables in MeetingSession and always use AppSettings?
int hourOffset = AppSettings.getHourOffset();
backGround->setHourOffset(hourOffset);
Session->hourOffset = hourOffset;
int horizOffset = AppSettings.getHorizontalOffset();
backGround->setHourHorizOffset(AppSettings.getHourLineOffset());
Session->horizOffset = horizOffset;
int vertOffset = AppSettings.getVerticalOffset();
backGround->setVertOffset(vertOffset);
Session->vertOffset = vertOffset;
Session->stationOffset = AppSettings.getStationOffset();
Session->platformOffset = AppSettings.getPlatformOffset();
Session->jobLineWidth = AppSettings.getJobLineWidth();
QPen hourLinePen;
hourLinePen.setColor(AppSettings.getHourLineColor());
hourLinePen.setWidth(AppSettings.getHourLineWidth());
backGround->setHourLinePen(hourLinePen);
backGround->setHourTextPen(AppSettings.getHourTextColor());
QFont f;
f.setPointSize(11);
backGround->setHourTextFont(f);
}
void GraphManager::onLineNameChanged(db_id lineId)
{
if(lineId == curLineId)
{
//Emit the signal again
//So MainWindow->lineComboSearch (CustomCompletionLineEdit)
//updates the text
emit currentLineChanged(curLineId);
}
}
void GraphManager::onLineModified(db_id lineId)
{
if(lineId == curLineId)
{
view->updateStationVertOffset();
view->redrawStationNames();
}
}
void GraphManager::onLineRemoved(db_id lineId)
{
if(curLineId == lineId)
{
//Current line is removed, show another line instead
lineId = getFirstLineId();
if(lineId)
setCurrentLine(lineId);
}
}

View File

@ -1,65 +0,0 @@
#ifndef GRAPHMANAGER_H
#define GRAPHMANAGER_H
#include <QObject>
#include "utils/types.h"
class BackgroundHelper;
class GraphicsView;
class LineStorage;
typedef struct JobSelection
{
db_id jobId;
db_id segmentId;
} JobSelection;
class GraphManager : public QObject
{
Q_OBJECT
public:
explicit GraphManager(QObject *parent = nullptr);
~GraphManager();
GraphicsView *getView() const;
db_id getCurLineId() const;
BackgroundHelper *getBackGround() const;
JobSelection getSelectedJob();
void clearSelection();
db_id getFirstLineId();
signals:
void currentLineChanged(db_id lineId);
void jobSelected(db_id jobId);
public slots:
bool setCurrentLine(db_id lineId);
void onSelectionChanged();
void onSelectionCleared();
private slots:
void onLineNameChanged(db_id lineId);
void onLineModified(db_id lineId);
void onLineRemoved(db_id lineId);
void updateGraphOptions();
public:
LineStorage *lineStorage;
private:
BackgroundHelper *backGround;
GraphicsView *view;
db_id curLineId;
db_id curJobId;
};
#endif // GRAPHMANAGER_H

View File

@ -1,29 +0,0 @@
#include "hourpane.h"
#include "backgroundhelper.h"
#include <QPainter>
#include <QDebug>
HourPane::HourPane(BackgroundHelper *h, QWidget *parent) :
QWidget (parent),
helper(h),
verticalScroll(0)
{
}
void HourPane::paintEvent(QPaintEvent *)
{
QPainter p(this);
QColor c(255, 255, 255, 220);
p.fillRect(rect(), c);
helper->drawForegroundHours(&p, rect(), verticalScroll);
}
void HourPane::setScroll(int value)
{
verticalScroll = value;
update();
}

View File

@ -1,25 +0,0 @@
#ifndef HOURPANE_H
#define HOURPANE_H
#include <QWidget>
class BackgroundHelper;
class HourPane : public QWidget
{
Q_OBJECT
public:
HourPane(BackgroundHelper *h, QWidget *parent);
public slots:
void setScroll(int value);
protected:
void paintEvent(QPaintEvent *);
private:
BackgroundHelper *helper;
int verticalScroll;
};
#endif // HOURPANE_H

View File

@ -0,0 +1,41 @@
/*
* 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>
class LineGraphTypeNames
{
Q_DECLARE_TR_FUNCTIONS(LineGraphTypeNames)
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")};
QString utils::getLineGraphTypeName(LineGraphType type)
{
if (type >= LineGraphType::NTypes)
return QString();
return LineGraphTypeNames::tr(LineGraphTypeNames::texts[int(type)]);
}

View File

@ -0,0 +1,43 @@
/*
* 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
#include <QString>
/*!
* \brief Enum to describe view content type
*/
enum class LineGraphType
{
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)
NTypes
};
namespace utils {
QString getLineGraphTypeName(LineGraphType type);
} // namespace utils
#endif // LINEGRAPHTYPES_H

View File

@ -0,0 +1,13 @@
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
graph/model/linegraphmanager.h
graph/model/linegraphscene.h
graph/model/linegraphselectionhelper.h
graph/model/stationgraphobject.h
graph/model/linegraphmanager.cpp
graph/model/linegraphscene.cpp
graph/model/linegraphselectionhelper.cpp
graph/model/stationgraphobject.cpp
PARENT_SCOPE
)

View File

@ -0,0 +1,608 @@
/*
* 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"
#include "linegraphselectionhelper.h"
#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_hasScheduledUpdate(false)
{
auto session = Session;
// Stations
connect(session, &MeetingSession::stationNameChanged, this,
&LineGraphManager::onStationNameChanged);
connect(session, &MeetingSession::stationJobsPlanChanged, this,
&LineGraphManager::onStationJobPlanChanged);
connect(session, &MeetingSession::stationTrackPlanChanged, this,
&LineGraphManager::onStationTrackPlanChanged);
connect(session, &MeetingSession::stationRemoved, this, &LineGraphManager::onStationRemoved);
// Segments
connect(session, &MeetingSession::segmentNameChanged, this,
&LineGraphManager::onSegmentNameChanged);
connect(session, &MeetingSession::segmentStationsChanged, this,
&LineGraphManager::onSegmentStationsChanged);
connect(session, &MeetingSession::segmentRemoved, this, &LineGraphManager::onSegmentRemoved);
// Lines
connect(session, &MeetingSession::lineNameChanged, this, &LineGraphManager::onLineNameChanged);
connect(session, &MeetingSession::lineSegmentsChanged, this,
&LineGraphManager::onLineSegmentsChanged);
connect(session, &MeetingSession::lineRemoved, this, &LineGraphManager::onLineRemoved);
// Jobs
connect(session, &MeetingSession::jobChanged, this, &LineGraphManager::onJobChanged);
connect(session, &MeetingSession::jobRemoved, this, &LineGraphManager::onJobRemoved);
// 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));
scenes.append(scene);
connect(scene, &LineGraphScene::destroyed, this, &LineGraphManager::onSceneDestroyed);
connect(scene, &LineGraphScene::sceneActivated, this, &LineGraphManager::setActiveScene);
connect(scene, &LineGraphScene::jobSelected, this, &LineGraphManager::onJobSelected);
if (m_followJobOnGraphChange)
connect(scene, &LineGraphScene::graphChanged, this, &LineGraphManager::onGraphChanged);
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
setActiveScene(scene);
}
}
void LineGraphManager::unregisterScene(LineGraphScene *scene)
{
Q_ASSERT(scenes.contains(scene));
scenes.removeOne(scene);
disconnect(scene, &LineGraphScene::destroyed, this, &LineGraphManager::onSceneDestroyed);
disconnect(scene, &LineGraphScene::sceneActivated, this, &LineGraphManager::setActiveScene);
disconnect(scene, &LineGraphScene::jobSelected, this, &LineGraphManager::onJobSelected);
if (m_followJobOnGraphChange)
disconnect(scene, &LineGraphScene::graphChanged, this, &LineGraphManager::onGraphChanged);
// Reset active scene if it is unregistered
if (activeScene == scene)
setActiveScene(nullptr);
}
void LineGraphManager::clearAllGraphs()
{
for (LineGraphScene *scene : qAsConst(scenes))
{
scene->loadGraph(0, LineGraphType::NoGraph, true);
}
}
void LineGraphManager::clearGraphsOfObject(db_id objectId, LineGraphType type)
{
for (LineGraphScene *scene : qAsConst(scenes))
{
if (scene->getGraphObjectId() == objectId && scene->getGraphType() == type)
scene->loadGraph(0, LineGraphType::NoGraph);
}
}
JobStopEntry LineGraphManager::getCurrentSelectedJob() const
{
JobStopEntry selectedJob;
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 (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))
return;
}
else if (!scenes.isEmpty())
{
// Activate first registered scene because previous one was unregistered
lineScene = scenes.first();
}
activeScene = lineScene;
emit activeSceneChanged(activeScene);
// Triegger selection update or clear it
JobStopEntry selectedJob;
if (activeScene)
{
selectedJob = activeScene->getSelectedJob();
}
onJobSelected(selectedJob.jobId, int(selectedJob.category), selectedJob.stopId);
}
void LineGraphManager::onSceneDestroyed(QObject *obj)
{
LineGraphScene *scene = static_cast<LineGraphScene *>(obj);
unregisterScene(scene);
}
void LineGraphManager::onGraphChanged(int /*graphType_*/, db_id graphObjId, LineGraphScene *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
// 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
LineGraphSelectionHelper helper(Session->m_Db);
LineGraphSelectionHelper::SegmentInfo info;
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);
}
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
lastSelectedJob.jobId = jobId;
lastSelectedJob.category = cat;
lastSelectedJob.stopId = stopId;
if (jobId)
Session->getViewManager()->requestJobEditor(jobId);
else
Session->getViewManager()->requestClearJob();
if (AppSettings.getSyncSelectionOnAllGraphs())
{
// Sync selection among all registered scenes
for (LineGraphScene *scene : qAsConst(scenes))
{
scene->setSelectedJob(lastSelectedJob);
}
}
if (activeScene)
{
const JobStopEntry selectedJob = activeScene->getSelectedJob();
if (selectedJob.jobId == lastSelectedJob.jobId)
{
emit jobSelected(lastSelectedJob.jobId, int(lastSelectedJob.category),
lastSelectedJob.stopId);
}
}
}
void LineGraphManager::onStationNameChanged(db_id stationId)
{
bool found = false;
for (LineGraphScene *scene : qAsConst(scenes))
{
if (scene->pendingUpdate.testFlag(PendingUpdate::FullReload))
continue; // Already flagged
if (scene->stations.contains(stationId))
{
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)
{
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)
{
bool found = false;
for (LineGraphScene *scene : qAsConst(scenes))
{
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)
{
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)
{
bool found = false;
for (LineGraphScene *scene : qAsConst(scenes))
{
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 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 (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)
{
activeScene->setSelectedJob(selectedJob);
}
}
else
{
// Manually update all scenes
for (LineGraphScene *scene : qAsConst(scenes))
{
JobStopEntry oldSelectedJob = scene->getSelectedJob();
if (oldSelectedJob.jobId == oldJobId)
{
scene->setSelectedJob(selectedJob);
}
}
}
}
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;
int horizOffset = AppSettings.getHorizontalOffset();
Session->horizOffset = horizOffset;
int vertOffset = AppSettings.getVerticalOffset();
Session->vertOffset = vertOffset;
Session->stationOffset = AppSettings.getStationOffset();
Session->platformOffset = AppSettings.getPlatformOffset();
Session->jobLineWidth = AppSettings.getJobLineWidth();
// Reload all graphs
for (LineGraphScene *scene : qAsConst(scenes))
{
scene->reload();
}
const bool oldVal = m_followJobOnGraphChange;
m_followJobOnGraphChange = AppSettings.getFollowSelectionOnGraphChange();
if (m_followJobOnGraphChange != oldVal)
{
// Update connections
for (LineGraphScene *scene : qAsConst(scenes))
{
if (m_followJobOnGraphChange)
connect(scene, &LineGraphScene::graphChanged, this,
&LineGraphManager::onGraphChanged);
else
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();
}

View File

@ -0,0 +1,196 @@
/*
* 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
#include <QObject>
#include <QVector>
#include "utils/types.h"
#include "graph/linegraphtypes.h"
class IGraphScene;
class LineGraphScene;
/*!
* \brief Class for managing LineGraphScene instances
*
* The manager listens for changes on railway plan and
* decides which of the registered LineGraphScene needs
* to be updated.
*
* \sa LineGraphScene
*/
class LineGraphManager : public QObject
{
Q_OBJECT
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
*
* The scene gets registered on this manager and will be refreshed
* we railway layout changes.
* The first scene registered is set as active
*
* \sa unregisterScene()
* \sa setActiveScene()
*/
void registerScene(LineGraphScene *scene);
/*!
* \brief unsubscribe scene from notifications
*
* The scene will not be refreshed by this manager anymore
* If it was the active scene, active scene will be reset
*
* \sa registerScene()
* \sa setActiveScene()
*/
void unregisterScene(LineGraphScene *scene);
void clearAllGraphs();
void clearGraphsOfObject(db_id objectId, LineGraphType type);
/*!
* \brief get active scene
* \return Scene instance or nullptr if no scene is active
*/
inline LineGraphScene *getActiveScene() const
{
return activeScene;
}
/*!
* \brief get current selected job
*
* If there is an active scene, return it's selection otherwise null JobStopEntry::jobId
* \sa getActiveScene()
* \sa LineGraphScene::getSelectedJob()
*/
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
*/
void activeSceneChanged(LineGraphScene *scene);
/*!
* \brief jobSelected
* \param jobId
* \param category
* \param stopId
*
* Emitted when \a active scene job selection changes or if active scene changes.
* Not emitted for other scenes.
*
* \sa getActiveScene()
* \sa getCurrentSelectedJob()
*/
void jobSelected(db_id jobId, int category, db_id stopId);
public slots:
/*!
* \brief sets active scene
*
* This scene instance will be the active one and will therefore receive
* user requests to show items.
* Scene must be registered on this manager first.
* If scene parameter is nullptr (i.e. when resetting active scene) we try
* to activate our first registered scene.
*
* \sa getActiveScene()
* \sa activeSceneChanged()
* \sa LineGraphScene::activateScene()
*/
void setActiveScene(IGraphScene *scene);
private slots:
// 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
void onStationNameChanged(db_id stationId);
void onStationJobPlanChanged(const QSet<db_id> &stationIds);
void onStationTrackPlanChanged(const QSet<db_id> &stationIds);
void onStationRemoved(db_id stationId);
// Segments
void onSegmentNameChanged(db_id segmentId);
void onSegmentStationsChanged(db_id segmentId);
void onSegmentRemoved(db_id segmentId);
// Lines
void onLineNameChanged(db_id lineId);
void onLineSegmentsChanged(db_id lineId);
void onLineRemoved(db_id lineId);
// Jobs
void onJobChanged(db_id jobId, db_id oldJobId);
void onJobRemoved(db_id jobId);
// 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

View File

@ -0,0 +1,965 @@
/*
* 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 "linegraphscene.h"
#include "graph/view/backgroundhelper.h"
#include "app/session.h"
#include <sqlite3pp/sqlite3pp.h>
#include <QDebug>
// TODO: maybe move to utils?
constexpr qreal MSEC_PER_HOUR = 1000 * 60 * 60;
static inline qreal timeToHourFraction(const QTime &t)
{
qreal ret = t.msecsSinceStartOfDay() / MSEC_PER_HOUR;
return ret;
}
static inline double stationPlatformPosition(const StationGraphObject &st, const db_id platfId,
const double platfOffset)
{
double x = st.xPos;
for (const StationGraphObject::PlatformGraph &platf : st.platforms)
{
if (platf.platformId == platfId)
return x;
x += platfOffset;
}
// Error: requested platform belongs to different station
qWarning() << "Station:" << st.stationName << st.stationId << "No platf:" << platfId;
return -1;
}
LineGraphScene::LineGraphScene(sqlite3pp::database &db, QObject *parent) :
IGraphScene(parent),
mDb(db),
graphObjectId(0),
graphType(LineGraphType::NoGraph),
m_drawSelection(true)
{
}
void LineGraphScene::renderContents(QPainter *painter, const QRectF &sceneRect)
{
BackgroundHelper::drawBackgroundHourLines(painter, sceneRect);
if (getGraphType() == LineGraphType::NoGraph)
return; // Nothing to draw
BackgroundHelper::drawStations(painter, this, sceneRect);
BackgroundHelper::drawJobStops(painter, this, sceneRect, m_drawSelection);
BackgroundHelper::drawJobSegments(painter, this, sceneRect, m_drawSelection);
}
void LineGraphScene::renderHeader(QPainter *painter, const QRectF &sceneRect,
Qt::Orientation orient, double /*scroll*/)
{
if (orient == Qt::Horizontal)
BackgroundHelper::drawStationHeader(painter, this, sceneRect);
else
BackgroundHelper::drawHourPanel(painter, sceneRect);
}
void LineGraphScene::recalcContentSize()
{
m_cachedContentsSize = QSize();
if (graphType == LineGraphType::NoGraph)
return; // Nothing to draw
if (stationPositions.isEmpty())
return;
const auto entry = stationPositions.last();
const int platfCount = stations.value(entry.stationId).platforms.count();
// Add an additional half station offset after last station
// This gives extra space to center station label
const double maxWidth =
entry.xPos + platfCount * Session->platformOffset + Session->stationOffset / 2;
const double lastY = Session->vertOffset + Session->hourOffset * 24 + 10;
m_cachedContentsSize = QSize(maxWidth, lastY);
}
void LineGraphScene::reload()
{
loadGraph(graphObjectId, graphType, true);
}
bool LineGraphScene::loadGraph(db_id objectId, LineGraphType type, bool force)
{
if (!force && objectId == graphObjectId && type == graphType)
return true; // Already loaded
// Initial state is invalid
graphType = LineGraphType::NoGraph;
graphObjectId = 0;
graphObjectName.clear();
stations.clear();
stationPositions.clear();
m_cachedContentsSize = QSize();
if (type == LineGraphType::NoGraph)
{
// Nothing to load
emit graphChanged(int(graphType), graphObjectId, this);
emit redrawGraph();
return true;
}
if (!mDb.db())
{
qWarning() << "Database not open on graph loading!";
return false;
}
if (objectId <= 0)
{
qWarning() << "Invalid object ID on graph loading!";
return false;
}
// Leave on left horizOffset plus half station offset to separate first station from HourPanel
// and to give more space to station label.
const double curPos = Session->horizOffset + Session->stationOffset / 2;
if (type == LineGraphType::SingleStation)
{
StationGraphObject st;
st.stationId = objectId;
if (!loadStation(st, graphObjectName))
return false;
// Register a single station at start position
st.xPos = curPos;
stations.insert(st.stationId, st);
stationPositions = {{st.stationId, 0, st.xPos, {}}};
}
else if (type == LineGraphType::RailwaySegment)
{
// TODO: maybe show also station gates
StationGraphObject stA, stB;
sqlite3pp::query q(mDb, "SELECT s.in_gate_id,s.out_gate_id,s.name,s.max_speed_kmh,"
"s.type,s.distance_meters,"
"g1.station_id,g2.station_id"
" FROM railway_segments s"
" JOIN station_gates g1 ON g1.id=s.in_gate_id"
" JOIN station_gates g2 ON g2.id=s.out_gate_id"
" WHERE s.id=?");
q.bind(1, objectId);
if (q.step() != SQLITE_ROW)
{
qWarning() << "Graph: invalid segment ID" << objectId;
return false;
}
auto r = q.getRows();
// TODO useful?
// outFromGateId = r.get<db_id>(0);
// outToGateId = r.get<db_id>(1);
graphObjectName = r.get<QString>(2);
// outSpeed = r.get<int>(3);
// outType = utils::RailwaySegmentType(r.get<db_id>(4));
// outDistance = r.get<int>(5);
stA.stationId = r.get<db_id>(6);
stB.stationId = r.get<db_id>(7);
QString unusedStFullName;
if (!loadStation(stA, unusedStFullName) || !loadStation(stB, unusedStFullName))
return false;
stA.xPos = curPos;
stB.xPos =
stA.xPos + stA.platforms.count() * Session->platformOffset + Session->stationOffset;
stations.insert(stA.stationId, stA);
stations.insert(stB.stationId, stB);
stationPositions = {{stA.stationId, objectId, stA.xPos, {}},
{stB.stationId, 0, stB.xPos, {}}};
}
else if (type == LineGraphType::RailwayLine)
{
if (!loadFullLine(objectId))
{
stations.clear();
stationPositions.clear();
return false;
}
}
graphObjectId = objectId;
graphType = type;
recalcContentSize();
updateHeaderSize();
reloadJobs();
// Reset pending update
pendingUpdate = PendingUpdate::NothingToDo;
emit graphChanged(int(graphType), graphObjectId, this);
emit redrawGraph();
return true;
}
bool LineGraphScene::reloadJobs()
{
if (graphType == LineGraphType::NoGraph)
return false;
// TODO: maybe only load visible
for (StationGraphObject &st : stations)
{
if (!loadStationJobStops(st))
return false;
}
// Save last station from previous iteration
auto lastSt = stations.constEnd();
for (int i = 0; i < stationPositions.size(); i++)
{
StationPosEntry &stPos = stationPositions[i];
if (!stPos.segmentId)
continue; // No segment, skip
db_id fromStId = stPos.stationId;
db_id toStId = 0;
if (i <= stationPositions.size() - 1)
toStId = stationPositions.at(i + 1).stationId;
if (!toStId)
break; // No next station
auto fromSt = lastSt;
if (fromSt == stations.constEnd() || fromSt->stationId != fromStId)
{
fromSt = stations.constFind(fromStId);
if (fromSt == stations.constEnd())
{
continue;
}
}
auto toSt = stations.constFind(toStId);
if (toSt == stations.constEnd())
continue;
if (!loadSegmentJobs(stPos, fromSt.value(), toSt.value()))
return false;
// Store last station
lastSt = toSt;
}
JobStopEntry newSelection = selectedJob;
updateJobSelection(mDb, newSelection);
setSelectedJob(newSelection);
return true;
}
void LineGraphScene::updateHeaderSize()
{
QSizeF headerSize(Session->horizOffset, Session->vertOffset);
if (headerSize != m_cachedHeaderSize)
{
m_cachedHeaderSize = headerSize;
emit headersSizeChanged();
}
}
JobStopEntry LineGraphScene::getJobStopAt(const StationGraphObject *prevSt,
const StationGraphObject *nextSt, const QPointF &pos,
const double tolerance)
{
const double platformOffset = Session->platformOffset;
JobStopEntry job;
// Find nearest station
const StationGraphObject *nearestSt = nullptr;
double nextStDistance = 0;
if (nextSt)
{
nextStDistance = qAbs(nextSt->xPos - pos.x());
if (nextStDistance <= tolerance)
{
// Next station is a good candidate
nearestSt = nextSt;
}
}
if (prevSt)
{
const double prevStRight = prevSt->xPos + prevSt->platforms.count() * platformOffset;
if (pos.x() >= prevSt->xPos && pos.x() <= prevStRight)
{
// Requested pos is inside this station
nearestSt = prevSt;
}
else if (pos.x() >= prevStRight)
{
// Requested position is between prevSt and nextSt, find nearest
const double prevStDistance = pos.x() - prevStRight;
if (prevStDistance <= tolerance && (!nearestSt || prevStDistance < nextStDistance))
{
nearestSt = prevSt;
}
}
}
if (!nearestSt)
return job; // Both stations exceed tolerance, null selection
const StationGraphObject::PlatformGraph *prevPlatf = nullptr;
const StationGraphObject::PlatformGraph *nextPlatf = nullptr;
double prevPos = 0;
double nextPos = 0;
double xPos = nearestSt->xPos;
for (const StationGraphObject::PlatformGraph &platf : nearestSt->platforms)
{
if (xPos >= pos.x())
{
// We went past the requested position
nextPlatf = &platf;
nextPos = xPos;
break;
}
prevPlatf = &platf;
prevPos = xPos;
xPos += platformOffset;
}
// Find nearest platform
const StationGraphObject::PlatformGraph *resultPlatf = nullptr;
const double prevDistance = qAbs(prevPos - pos.x());
if (prevPlatf && prevDistance <= tolerance)
{
// Previous platform is a good candidate
resultPlatf = prevPlatf;
}
const double nextDistance = qAbs(nextPos - pos.x());
if (nextPlatf && nextDistance <= tolerance)
{
// Next platform is a good candidate
if (!resultPlatf || nextDistance < prevDistance)
{
// We are the nearest
resultPlatf = nextPlatf;
}
}
if (!resultPlatf)
return job; // No match
for (const StationGraphObject::JobStopGraph &jobStop : resultPlatf->jobStops)
{
// NOTE: in stops arrival comes BEFORE departure
if (jobStop.arrivalY <= pos.y() + tolerance && jobStop.departureY >= pos.y() - tolerance)
{
// Found match
job = jobStop.stop;
break;
}
}
return job;
}
JobStopEntry LineGraphScene::getJobAt(const QPointF &pos, const double tolerance)
{
JobStopEntry job;
if (stationPositions.isEmpty())
return job;
db_id prevStId = 0;
db_id nextStId = 0;
const StationPosEntry *entry = nullptr;
for (const StationPosEntry &stPos : qAsConst(stationPositions))
{
if (stPos.xPos <= pos.x())
{
prevStId = stPos.stationId;
entry = &stPos;
}
if (stPos.xPos >= pos.x())
{
// We went past the requested position
nextStId = stPos.stationId;
break;
}
}
auto prevSt = stations.constFind(prevStId);
auto nextSt = stations.constFind(nextStId);
const StationGraphObject *prevStPtr = prevSt == stations.constEnd() ? nullptr : &prevSt.value();
const StationGraphObject *nextStPtr = nextSt == stations.constEnd() ? nullptr : &nextSt.value();
if (!prevStPtr && !nextStPtr)
return job; // Error
job = getJobStopAt(prevStPtr, nextStPtr, pos, tolerance);
if (job.jobId)
return job; // Found match
// Check job segments
if (!entry)
return job; // Error, no match
double prevSegDistance = -1;
for (const JobSegmentGraph &segment : qAsConst(entry->nextSegmentJobGraphs))
{
// NOTE: in segments arrival comes AFTER departure
const QRectF r = QRectF(segment.fromDeparture, segment.toArrival).normalized();
if (r.contains(pos))
{
// Requested position is inside bounds, might be a match
const double resultingY = r.top() + (pos.x() - r.left()) * r.height() / r.width();
const double segDistance = qAbs(resultingY - pos.y());
if (prevSegDistance < 0 || segDistance < prevSegDistance)
{
// We are a better match than previous, replace it
// Use departure station ('from') because arrival station might be last one
// So there might be no segments after arrival
job.stopId = segment.fromStopId;
job.jobId = segment.jobId;
job.category = segment.category;
// Store new minimum distance
prevSegDistance = segDistance;
}
}
}
return job;
}
bool LineGraphScene::loadStation(StationGraphObject &st, QString &outFullName)
{
sqlite3pp::query q(mDb);
q.prepare("SELECT name,short_name,type FROM stations WHERE id=?");
q.bind(1, st.stationId);
if (q.step() != SQLITE_ROW)
{
qWarning() << "Graph: invalid station ID" << st.stationId;
return false;
}
// Load station
auto row = q.getRows();
outFullName = row.get<QString>(0);
st.stationName = row.get<QString>(1);
if (st.stationName.isEmpty())
{
// Empty short name, fallback to full name
st.stationName = outFullName;
}
st.stationType = utils::StationType(row.get<int>(2));
// Load platforms
const QRgb white = qRgb(255, 255, 255);
q.prepare(
"SELECT id, type, color_rgb, name FROM station_tracks WHERE station_id=? ORDER BY pos");
q.bind(1, st.stationId);
for (auto r : q)
{
StationGraphObject::PlatformGraph platf;
platf.platformId = r.get<db_id>(0);
platf.platformType = utils::StationTrackType(r.get<int>(1));
if (r.column_type(2) == SQLITE_NULL) // NULL is white (#FFFFFF) -> default value
platf.color = white;
else
platf.color = QRgb(r.get<int>(2));
platf.platformName = r.get<QString>(3);
st.platforms.append(platf);
}
return true;
}
bool LineGraphScene::updateStationNames()
{
sqlite3pp::query q(mDb);
q.prepare("SELECT name,short_name FROM stations WHERE id=?");
for (StationGraphObject &st : stations)
{
q.bind(1, st.stationId);
if (q.step() != SQLITE_ROW)
{
qWarning() << "Graph: invalid station ID" << st.stationId;
continue;
}
st.stationName = q.getRows().get<QString>(1);
QString fullName = q.getRows().get<QString>(0);
if (st.stationName.isEmpty())
{
// Empty short name, fallback to full name
st.stationName = fullName;
}
if (graphObjectId == st.stationId && graphType == LineGraphType::SingleStation)
{
// If we are a station graph also update grah name
graphObjectName = fullName;
// Notify views TODO: specify graph didn't really change, just name
emit graphChanged(int(graphType), graphObjectId, this);
}
q.reset();
}
return true;
}
bool LineGraphScene::loadFullLine(db_id lineId)
{
// TODO: maybe show also station gates
// TODO: load only visible stations, other will be loaded when scrolling graph
sqlite3pp::query q(mDb, "SELECT name FROM lines WHERE id=?");
q.bind(1, lineId);
if (q.step() != SQLITE_ROW)
{
qWarning() << "Graph: invalid line ID" << lineId;
return false;
}
// Store line name
graphObjectName = q.getRows().get<QString>(0);
// Get segments
q.prepare("SELECT ls.id, ls.seg_id, ls.direction,"
"seg.name, seg.max_speed_kmh, seg.type, seg.distance_meters,"
"g1.station_id, g2.station_id"
" FROM line_segments ls"
" JOIN railway_segments seg ON seg.id=ls.seg_id"
" JOIN station_gates g1 ON g1.id=seg.in_gate_id"
" JOIN station_gates g2 ON g2.id=seg.out_gate_id"
" WHERE ls.line_id=?"
" ORDER BY ls.pos");
q.bind(1, lineId);
db_id lastStationId = 0;
double curPos = Session->horizOffset + Session->stationOffset / 2;
QString unusedStFullName;
for (auto seg : q)
{
db_id lineSegmentId = seg.get<db_id>(0);
db_id railwaySegmentId = seg.get<db_id>(1);
bool reversed = seg.get<int>(2) != 0;
// item.segmentName = seg.get<QString>(3);
// item.maxSpeedKmH = seg.get<int>(4);
// item.segmentType = utils::RailwaySegmentType(seg.get<int>(5));
// item.distanceMeters = seg.get<int>(6);
// Store first segment end
db_id fromStationId = seg.get<db_id>(7);
// Store also the other end of segment for last item
db_id otherStationId = seg.get<db_id>(8);
if (reversed)
{
// Swap segments ends
qSwap(fromStationId, otherStationId);
}
if (!lastStationId)
{
// First line station
StationGraphObject st;
st.stationId = fromStationId;
if (!loadStation(st, unusedStFullName))
return false;
st.xPos = curPos;
stations.insert(st.stationId, st);
stationPositions.append({st.stationId, railwaySegmentId, st.xPos, {}});
curPos += st.platforms.count() * Session->platformOffset + Session->stationOffset;
}
else if (fromStationId != lastStationId)
{
qWarning() << "Line segments are not adjacent, ID:" << lineSegmentId
<< "LINE:" << lineId;
return false;
}
StationGraphObject stB;
stB.stationId = otherStationId;
if (!loadStation(stB, unusedStFullName))
return false;
stB.xPos = curPos;
stations.insert(stB.stationId, stB);
stationPositions.last().segmentId = railwaySegmentId;
stationPositions.append({stB.stationId, 0, stB.xPos, {}});
curPos += stB.platforms.count() * Session->platformOffset + Session->stationOffset;
lastStationId = stB.stationId;
}
return true;
}
bool LineGraphScene::loadStationJobStops(StationGraphObject &st)
{
// Reset previous job graphs
for (StationGraphObject::PlatformGraph &platf : st.platforms)
{
platf.jobStops.clear();
}
sqlite3pp::query q_prevSegment(
mDb, "SELECT c.seg_id, MAX(stops.departure)"
" FROM stops"
" LEFT JOIN railway_connections c ON c.id=stops.next_segment_conn_id"
" WHERE stops.job_id=? AND stops.departure<?");
sqlite3pp::query q(mDb,
"SELECT stops.id, stops.job_id, jobs.category,"
"stops.arrival, stops.departure,"
"g_in.track_id, g_out.track_id,"
"c.seg_id"
" FROM stops"
" JOIN jobs ON stops.job_id=jobs.id"
" LEFT JOIN station_gate_connections g_in ON g_in.id=stops.in_gate_conn"
" LEFT JOIN station_gate_connections g_out ON g_out.id=stops.out_gate_conn"
" LEFT JOIN railway_connections c ON c.id=stops.next_segment_conn_id"
" WHERE stops.station_id=?"
" ORDER BY stops.arrival");
q.bind(1, st.stationId);
const double vertOffset = Session->vertOffset;
const double hourOffset = Session->hourOffset;
for (auto stop : q)
{
StationGraphObject::JobStopGraph jobStop;
jobStop.stop.stopId = stop.get<db_id>(0);
jobStop.stop.jobId = stop.get<db_id>(1);
jobStop.stop.category = JobCategory(stop.get<int>(2));
QTime arrival = stop.get<QTime>(3);
QTime departure = stop.get<QTime>(4);
db_id trackId = stop.get<db_id>(5);
db_id outTrackId = stop.get<db_id>(6);
db_id nextSegId = stop.get<db_id>(7);
if (trackId && outTrackId && trackId != outTrackId)
{
// Not last stop, neither first stop. Tracks must correspond
qWarning() << "Stop:" << jobStop.stop.stopId << "Track not corresponding, using in";
}
else if (!trackId)
{
if (outTrackId)
trackId = outTrackId; // First stop, use out gate connection
else
{
qWarning() << "Stop:" << jobStop.stop.stopId << "Both in/out track NULL, skipping";
continue; // Skip this stop
}
}
StationGraphObject::PlatformGraph *platf = nullptr;
// Find platform
for (StationGraphObject::PlatformGraph &p : st.platforms)
{
if (p.platformId == trackId)
{
platf = &p;
break;
}
}
if (!platf)
{
// Requested platform is not in this station
qWarning() << "Stop:" << jobStop.stop.stopId << "Track is not in this station";
continue; // Skip this stop
}
// Check if we need job label
bool isSegmentVisible = false;
if (graphType == LineGraphType::SingleStation)
isSegmentVisible = true; // Skip checking, always draw label
if (!isSegmentVisible && nextSegId)
{
for (const StationPosEntry &stPos : qAsConst(stationPositions))
{
if (stPos.segmentId == nextSegId)
{
isSegmentVisible = true;
break;
}
}
}
if (!isSegmentVisible)
{
// Check if previous segment is visible
q_prevSegment.bind(1, jobStop.stop.jobId);
q_prevSegment.bind(2, arrival);
q_prevSegment.step();
auto seg = q_prevSegment.getRows();
if (seg.column_type(0) != SQLITE_NULL)
{
db_id prevSegId = seg.get<db_id>(0);
for (const StationPosEntry &stPos : qAsConst(stationPositions))
{
if (stPos.segmentId == prevSegId)
{
isSegmentVisible = true;
break;
}
}
}
q_prevSegment.reset();
}
// Draw only if neither segment is visible or when graph is SignleStation
jobStop.drawLabel = !isSegmentVisible || graphType == LineGraphType::SingleStation;
// Calculate coordinates
jobStop.arrivalY = vertOffset + timeToHourFraction(arrival) * hourOffset;
jobStop.departureY = vertOffset + timeToHourFraction(departure) * hourOffset;
platf->jobStops.append(jobStop);
}
return true;
}
bool LineGraphScene::loadSegmentJobs(LineGraphScene::StationPosEntry &stPos,
const StationGraphObject &fromSt,
const StationGraphObject &toSt)
{
// Reset previous job segment graph
stPos.nextSegmentJobGraphs.clear();
const double vertOffset = Session->vertOffset;
const double hourOffset = Session->hourOffset;
const double platfOffset = Session->platformOffset;
sqlite3pp::query q(
mDb, "SELECT sub.*, jobs.category, g_out.track_id, g_in.track_id FROM ("
" SELECT stops.id AS cur_stop_id, lead(stops.id, 1) OVER win AS next_stop_id,"
" stops.station_id,"
" stops.job_id,"
" stops.departure, lead(stops.arrival, 1) OVER win AS next_stop_arrival,"
" stops.out_gate_conn,"
" lead(stops.in_gate_conn, 1) OVER win AS next_stop_g_in,"
" seg_conn.seg_id"
" FROM stops"
" LEFT JOIN railway_connections seg_conn ON seg_conn.id=stops.next_segment_conn_id"
" WINDOW win AS (PARTITION BY stops.job_id ORDER BY stops.arrival)"
") AS sub"
" JOIN station_gate_connections g_out ON g_out.id=sub.out_gate_conn"
" JOIN station_gate_connections g_in ON g_in.id=sub.next_stop_g_in"
" JOIN jobs ON jobs.id=sub.job_id"
" WHERE sub.seg_id=?");
q.bind(1, stPos.segmentId);
for (auto stop : q)
{
JobSegmentGraph job;
job.fromStopId = stop.get<db_id>(0);
job.toStopId = stop.get<db_id>(1);
db_id stId = stop.get<db_id>(2);
job.jobId = stop.get<db_id>(3);
QTime departure = stop.get<QTime>(4);
QTime arrival = stop.get<QTime>(5);
// 6 - out gate connection
// 7 - in gate connection
// 8 - segment_id
job.category = JobCategory(stop.get<int>(9));
job.fromPlatfId = stop.get<db_id>(10);
job.toPlatfId = stop.get<db_id>(11);
// NOTE: fromPlatfId and toPlatfId do not need to be reversed because represent correct
// platforms Only stations might be reversed
bool reverse = toSt.stationId == stId; // If job goes in opposite direction
// Calculate coordinates
job.fromDeparture.rx() =
stationPlatformPosition(reverse ? toSt : fromSt, job.fromPlatfId, platfOffset);
job.fromDeparture.ry() = vertOffset + timeToHourFraction(departure) * hourOffset;
job.toArrival.rx() =
stationPlatformPosition(reverse ? fromSt : toSt, job.toPlatfId, platfOffset);
job.toArrival.ry() = vertOffset + timeToHourFraction(arrival) * hourOffset;
if (job.fromDeparture.x() < 0 || job.toArrival.x() < 0)
continue; // Skip, couldn't find platform
stPos.nextSegmentJobGraphs.append(job);
}
return true;
}
void LineGraphScene::updateJobSelection(sqlite3pp::database &db, JobStopEntry &job)
{
if (!job.jobId)
return;
query q(db);
if (job.stopId)
{
// Check if stop is valid
q.prepare("SELECT job_id FROM stops WHERE id=?");
q.bind(1, job.stopId);
if (q.step() == SQLITE_ROW)
{
db_id jobId = q.getRows().get<db_id>(0);
if (jobId != job.jobId)
job.stopId = 0; // Stop doesn't belong to this job
}
else
{
// This stop doesn't exist anymore
job.stopId = 0;
}
}
q.prepare("SELECT category FROM jobs WHERE id=?");
q.bind(1, job.jobId);
if (q.step() != SQLITE_ROW)
{
// Job doesn't exist anymore, clear selection
job = JobStopEntry{};
return;
}
JobCategory newCategory = JobCategory(q.getRows().get<int>(0));
if (newCategory != job.category)
{
job.category = newCategory;
}
}
JobStopEntry LineGraphScene::getSelectedJob() const
{
return selectedJob;
}
void LineGraphScene::setSelectedJob(JobStopEntry stop, bool sendChange)
{
const JobStopEntry oldJob = selectedJob;
selectedJob = stop;
if (!selectedJob.jobId)
{
// Clear other members too
selectedJob.stopId = 0;
selectedJob.category = JobCategory::NCategories;
}
if (sendChange
&& (selectedJob.jobId != oldJob.jobId || selectedJob.category != oldJob.category))
{
emit redrawGraph();
emit jobSelected(selectedJob.jobId, int(selectedJob.category), selectedJob.stopId);
}
}
bool LineGraphScene::requestShowZone(db_id stationId, db_id segmentId, QTime from, QTime to)
{
// TODO: when we will load incrementally, ensure relevant items are loaded
const double vertOffset = Session->vertOffset;
const double hourOffset = Session->hourOffset;
const double platfOffset = Session->platformOffset;
QRectF result;
result.setTop(vertOffset + timeToHourFraction(from) * hourOffset);
result.setBottom(vertOffset + timeToHourFraction(to) * hourOffset);
// NOTE: Initially left() is 0 which will always be less than any station position
// So the first station must set it's position regardless of left() value
bool leftEdgeSet = false;
for (const StationPosEntry &entry : qAsConst(stationPositions))
{
// Match the requested station or both station in the segment
if (entry.stationId == stationId || entry.segmentId == segmentId)
{
auto st = stations.constFind(entry.stationId);
if (st == stations.constEnd())
continue;
if (result.left() > entry.xPos || !leftEdgeSet)
{
result.setLeft(entry.xPos);
leftEdgeSet = true;
}
const int platfCount = st->platforms.count();
const double rightPos = entry.xPos + platfCount * platfOffset;
if (result.right() < rightPos)
result.setRight(rightPos);
}
}
// Set a margin around the selection so it douesn't end up at view edges
const double margin = hourOffset / 4;
result.adjust(-margin, -margin, margin, margin);
emit requestShowRect(result);
return true;
}

View File

@ -0,0 +1,371 @@
/*
* 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
#include "utils/scene/igraphscene.h"
#include <QVector>
#include <QHash>
#include <QPointF>
#include "utils/types.h"
#include "graph/linegraphtypes.h"
#include "stationgraphobject.h"
namespace sqlite3pp {
class database;
}
/*!
* \brief Class to store line information
*
* Reimplement IGraphScene for railway line graph
* Stores information to draw railway contents in a LineGraphView
*
* \sa LineGraphManager
* \sa LineGraphView
*/
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;
/*!
* \brief Load graph contents
*
* Loads stations and jobs
*
* \param objectId Graph object ID
* \param type Graph type
* \param force Force reloading if objectId and type are the same as current
*
* \sa loadStation()
* \sa loadFullLine()
* \sa reloadJobs()
*/
bool loadGraph(db_id objectId, LineGraphType type, bool force = false);
/*!
* \brief Load graph jobs
*
* Reloads only jobs but not stations
* It also updates current job selection
* \sa loadGraph()
* \sa updateJobSelection()
*/
bool reloadJobs();
/*!
* \brief update header size
*
* Updates header size from settings valuse.
* \sa MRTPSettings
*/
void updateHeaderSize();
/*!
* \brief Get job at graph position
*
* \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);
inline LineGraphType getGraphType() const
{
return graphType;
}
inline db_id getGraphObjectId() const
{
return graphObjectId;
}
inline QString getGraphObjectName() const
{
return graphObjectName;
}
/*!
* \brief get selected job
*
* Get selected job info
* \sa setSelectedJob()
*/
JobStopEntry getSelectedJob() const;
/*!
* \brief setSelectedJob
* \param stop a specific stop or just a job ID and its category
* \param sendChange emit redrawGraph() and jobSelected() signals if true
*
* Set job selection and schedule graph redraw
* \sa jobSelected()
* \sa getSelectedJob()
*/
void setSelectedJob(JobStopEntry stop, bool sendChange = true);
/*!
* \brief getDrawSelection
* \return true if selection is drawn
*
* When true and a Job is selected, it gets a semi transparent aurea
* around it's line to make it more visible
*
* \sa setDrawSelection()
*/
inline bool getDrawSelection()
{
return m_drawSelection;
}
/*!
* \brief setDrawSelection
* \param val true if needs to draw selection
*
* Sets scene option. You must manually redraw graph
* by calling \ref renderContents() or emitting \ref redrawGraph()
*
* \sa getDrawSelection()
*/
inline void setDrawSelection(bool val)
{
m_drawSelection = val;
}
/*!
* \brief requestShowZone
* \param stationId null if you want to select segment
* \param segmentId null if you want to select station
* \param from top of the zone
* \param to bottom of the zone
*
* Calculates a zone in the scene which show the selected station or entire segment
* (It depends on which is non-null)
* The y values are calculated from the QTime arguments
* Then it requests the view to show the area.
*
* \sa requestShowRect()
*/
bool requestShowZone(db_id stationId, db_id segmentId, QTime from, QTime to);
signals:
void graphChanged(int type, db_id objectId, LineGraphScene *self);
/*!
* \brief job selected
* \param jobId ID of the job on 0 if selection cleared.
* \param category job's category casted to int
* \param stopId possible stop ID hint to select a specific section
*
* Selection changed: either user clicked on a job
* or on an empty zone to clear selection
* \sa setSelectedJob()
*/
void jobSelected(db_id jobId, int category, db_id stopId);
public slots:
/*!
* \brief Reload everything
*
* Reload entire contents
* \sa loadSegmentJobs()
*/
void reload();
private:
/*!
* \brief Graph of the job while is moving
*
* Contains informations to draw job line between two adjacent stations
*/
struct JobSegmentGraph
{
db_id jobId;
JobCategory category;
db_id fromStopId;
db_id fromPlatfId;
QPointF fromDeparture;
db_id toStopId;
db_id toPlatfId;
QPointF toArrival;
};
/*!
* \brief Station entry on scene
*
* Represents a station item placeholder in an ordered list of scene
*/
struct StationPosEntry
{
db_id stationId;
db_id segmentId;
double xPos;
QVector<JobSegmentGraph> nextSegmentJobGraphs;
/*!<
* Stores job graph of the next segment
* Which means jobs departing from this staation and going to next one
*/
};
private:
/*!
* \brief Get job stop at graph position
*
* \param prevSt Station at position's left
* \param nextSt Station at position's right
* \param pos Point in scene coordinates
* \param tolerance A tolerance if mouse doesn't exactly click on job item
*
* Check if a job stop in this station matches requested position
* Otherwise return null selection
*/
JobStopEntry getJobStopAt(const StationGraphObject *prevSt, const StationGraphObject *nextSt,
const QPointF &pos, const double tolerance);
/*!
* \brief Recalculate and store content size
*
* Stores cached calculated size of the graph.
* It depends on settings like station offset.
* \sa MRTPSettings
*/
void recalcContentSize();
/*!
* \brief Load station details
*
* Loads station name and tracks (color, attributes, names)
* It does NOT load jobs, only tracks
*/
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
*
* Loads stations of all railway line segments
* \sa loadStation()
*/
bool loadFullLine(db_id lineId);
/*!
* \brief Load job stops in station
*
* Load jobs stops, only the part on station's track
* \sa loadSegmentJobs()
*/
bool loadStationJobStops(StationGraphObject &st);
/*!
* \brief Load job segments between 2 stations
*
* Load job segments and stores them in 'from' station
* \sa loadStationJobStops()
*/
bool loadSegmentJobs(StationPosEntry &stPos, const StationGraphObject &fromSt,
const StationGraphObject &toSt);
/*!
* \brief Update job selection category
*
* Updates current selection.
* If selected job got removed or changed ID (Number) selection is cleared
* If selected stop got removed or doesn't belong to selected job it's cleared
* Category of selected job gets updated if changed in the meantime
*/
static void updateJobSelection(sqlite3pp::database &db, JobStopEntry &job);
private:
friend class BackgroundHelper;
friend class LineGraphManager;
sqlite3pp::database &mDb;
/*!
* \brief Graph Object ID
*
* Can be station, segment, line ID
* Depending on graph type
*/
db_id graphObjectId;
/*!
* \brief Type of Graph displayed by Scene
*/
LineGraphType graphType;
/*!
* \brief Graph Object Name
*
* Can be station, segment, line name
* Depending on graph type
*/
QString graphObjectName;
QVector<StationPosEntry> stationPositions;
QHash<db_id, StationGraphObject> stations;
/*!
* \brief Job selection
*
* Caches selected job item ID
*/
JobStopEntry selectedJob;
bool m_drawSelection;
PendingUpdateFlags pendingUpdate;
};
#endif // LINEGRAPHSCENE_H

View File

@ -0,0 +1,402 @@
/*
* 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"
#include <sqlite3pp/sqlite3pp.h>
using namespace sqlite3pp;
LineGraphSelectionHelper::LineGraphSelectionHelper(sqlite3pp::database &db) :
mDb(db)
{
}
bool LineGraphSelectionHelper::tryFindJobStopInGraph(LineGraphScene *scene, db_id jobId,
SegmentInfo &info)
{
query q(mDb);
switch (scene->getGraphType())
{
case LineGraphType::SingleStation:
{
q.prepare("SELECT stops.id, c.seg_id, MIN(stops.arrival), stops.departure"
" FROM stops"
" LEFT JOIN railway_connections c ON c.id=stops.next_segment_conn_id"
" WHERE stops.job_id=? AND stops.station_id=?");
break;
}
case LineGraphType::RailwaySegment:
{
q.prepare("SELECT stops.id, c.seg_id, MIN(stops.arrival), stops.departure, stops.station_id"
" FROM railway_segments seg"
" JOIN station_gates g1 ON g1.id=seg.in_gate_id"
" JOIN station_gates g2 ON g2.id=seg.out_gate_id"
" JOIN stops ON stops.job_id=? AND"
" (stops.station_id=g1.station_id OR stops.station_id=g2.station_id)"
" LEFT JOIN railway_connections c ON c.id=stops.next_segment_conn_id"
" WHERE seg.id=?");
break;
}
case LineGraphType::RailwayLine:
{
q.prepare("SELECT stops.id, c.seg_id, MIN(stops.arrival), stops.departure, stops.station_id"
" FROM line_segments"
" JOIN railway_segments seg ON seg.id=line_segments.seg_id"
" JOIN station_gates g1 ON g1.id=seg.in_gate_id"
" JOIN station_gates g2 ON g2.id=seg.out_gate_id"
" JOIN stops ON stops.job_id=? AND"
" (stops.station_id=g1.station_id OR stops.station_id=g2.station_id)"
" LEFT JOIN railway_connections c ON c.id=stops.next_segment_conn_id"
" WHERE line_segments.line_id=?");
break;
}
case LineGraphType::NoGraph:
case LineGraphType::NTypes:
{
// We need to load a new graph, give up
return false;
}
}
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
// 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);
// 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);
return true;
}
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
q.bind(1, jobId);
q.bind(2, info.arrivalAndStart);
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);
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);
q.reset();
// 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)
{
// We found only 1 stop, return that
info.secondStationId = 0;
return true;
}
// 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
info.secondStationId = stop.get<db_id>(3);
return true;
}
bool LineGraphSelectionHelper::tryFindNewGraphForJob(db_id jobId, SegmentInfo &info,
db_id &outGraphObjId,
LineGraphType &outGraphType)
{
if (!tryFindJobStopsAfter(jobId, info))
return false; // No stops found
if (!info.secondStationId || !info.segmentId)
{
// We found only 1 stop, select first station
outGraphObjId = info.firstStationId;
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?)
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)
{
// Found a line
outGraphObjId = q.getRows().get<db_id>(0);
outGraphType = LineGraphType::RailwayLine;
return true;
}
// No lines found, use the railway segment
outGraphObjId = info.segmentId;
outGraphType = LineGraphType::RailwaySegment;
return true;
}
bool LineGraphSelectionHelper::requestJobSelection(LineGraphScene *scene, db_id jobId, bool select,
bool ensureVisible)
{
// 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
JobStopEntry selectedJob;
selectedJob.jobId = jobId;
selectedJob.category = JobCategory(q.getRows().get<int>(0));
SegmentInfo info;
// Try to select earliest stop of this job in current graph, if any
const bool found = tryFindJobStopInGraph(scene, selectedJob.jobId, info);
if (!found)
{
// Find a NEW line graph or segment or station with this job
// Find first 2 stops of the job
db_id graphObjId = 0;
LineGraphType graphType = LineGraphType::NoGraph;
if (!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
{
// 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
const JobStopEntry oldSelection = scene->getSelectedJob();
scene->setSelectedJob(JobStopEntry{}, false); // Clear selection
// Select the graph
scene->loadGraph(graphObjId, graphType);
// Restore previous selection
scene->setSelectedJob(oldSelection, false);
}
// Extract the info
selectedJob.stopId = info.firstStopId;
if (!selectedJob.stopId)
return false; // No stop found, abort
// Select job
if (select)
scene->setSelectedJob(selectedJob);
if (ensureVisible)
{
return scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}
return true;
}
bool LineGraphSelectionHelper::requestCurrentJobPrevSegmentVisible(LineGraphScene *scene,
bool goToStart)
{
JobStopEntry selectedJob = scene->getSelectedJob();
if (!selectedJob.jobId)
return false; // No job selected, nothing to do
query q(mDb);
SegmentInfo info;
if (selectedJob.stopId && !goToStart)
{
// 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"
" JOIN stops s1 ON s1.job_id=s2.job_id"
" LEFT JOIN railway_connections c ON c.id=s1.next_segment_conn_id"
" 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)
{
auto stop = q.getRows();
db_id jobId = stop.get<db_id>(0);
if (jobId == selectedJob.jobId)
{
// 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.secondStationId = stop.get<db_id>(6);
}
}
}
if (!info.firstStopId)
{
// goToStart or failed to get previous stop so go to start anyway
if (!tryFindJobStopsAfter(selectedJob.jobId, info))
return false; // No stops found, give up
}
db_id graphObjId = 0;
LineGraphType graphType = LineGraphType::NoGraph;
if (!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
{
// 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
// Select the graph
scene->loadGraph(graphObjId, graphType);
// Restore selection
selectedJob.stopId = info.firstStopId;
scene->setSelectedJob(selectedJob); // This time emit
return scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}
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.
JobStopEntry selectedJob = scene->getSelectedJob();
if (!selectedJob.jobId)
return false; // No job selected, nothing to do
query q(mDb);
SegmentInfo info;
if (selectedJob.stopId && !goToEnd)
{
// 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"
" LEFT JOIN railway_connections c ON c.id=s1.next_segment_conn_id"
" 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)
{
auto stop = q.getRows();
db_id jobId = stop.get<db_id>(0);
if (jobId == selectedJob.jobId)
{
// 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);
}
}
}
if (!info.firstStopId)
{
// goToEnd or failed to get next stop so go to end anyway
// 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)
{
// 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);
}
}
db_id graphObjId = 0;
LineGraphType graphType = LineGraphType::NoGraph;
if (!tryFindNewGraphForJob(selectedJob.jobId, info, graphObjId, graphType))
{
// 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
// Select the graph
scene->loadGraph(graphObjId, graphType);
// Restore selection
selectedJob.stopId = info.firstStopId;
scene->setSelectedJob(selectedJob); // This time emit
return scene->requestShowZone(info.firstStationId, info.segmentId, info.arrivalAndStart,
info.departure);
}

View File

@ -0,0 +1,123 @@
/*
* 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
#include "utils/types.h"
#include "graph/linegraphtypes.h"
#include <QTime>
class LineGraphScene;
namespace sqlite3pp {
class database;
}
class LineGraphSelectionHelper
{
public:
/*!
* \brief The SegmentInfo struct
*/
struct SegmentInfo
{
db_id segmentId = 0;
db_id firstStationId = 0;
db_id secondStationId = 0;
db_id firstStopId = 0;
/*!
* \brief arrival and start
*
* Input: hour time limit after which stops are searched.
* Output: first stop arrival
*/
QTime arrivalAndStart;
QTime departure; //!< departure of first or second stop (if found)
};
LineGraphSelectionHelper(sqlite3pp::database &db);
// Low level API
/*!
* \brief find job in current graph
* \param scene the graph scene
* \param jobId job ID
* \param info structure to get output result
* \return true if found
*
* Try to find a job stop in current graph.
* 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);
/*!
* \brief find 2 job stops after requested hour
* \param jobId job ID
* \param info structure to get output result, and pass SegmentInfo::arrivalAndStart
* \return true if found 1 or 2 stops
*
* Try to find 2 job stops after requested hour (SegmentInfo::arrivalAndStart).
* If only 1 stop is found, SegmentInfo::secondStId is 0.
*/
bool tryFindJobStopsAfter(db_id jobId, SegmentInfo &info);
/*!
* \brief try find a new graph for job
* \param jobId job ID
* \param info structure to get output result, and pass SegmentInfo::arrivalAndStart
* \param outGraphObjId output object to load in scene
* \param outGraphType graph scene type
* \return false if not found
*
* 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);
public:
// High level API
/*!
* \brief request job selection
* \param scene the graph scene
* \param jobId jobId
* \param select do you want to select the job?
* \param ensureVisible do you want to make it visible on LineGraphView
* \return true on success
*
* Try to select or make visible (or both) the requested job on current scene graph.
* If the job is not found on current graph, it tries to find a new grah and loads it.
*
* \sa ViewManager::requestJobSelection()
*/
bool requestJobSelection(LineGraphScene *scene, db_id jobId, bool select, bool ensureVisible);
bool requestCurrentJobPrevSegmentVisible(LineGraphScene *scene, bool goToStart);
bool requestCurrentJobNextSegmentVisible(LineGraphScene *scene, bool goToEnd);
private:
sqlite3pp::database &mDb;
};
#endif // LINEGRAPHSELECTIONHELPER_H

View File

@ -0,0 +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()
{
}

View File

@ -0,0 +1,79 @@
/*
* 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
#include <QVector>
#include "utils/types.h"
#include "stations/station_utils.h"
#include <QRgb>
/*!
* \brief Graph of a railway station
*
* Contains informations to draw station name, platforms and jobs
*
* \sa PlatformGraph
*/
class StationGraphObject
{
public:
StationGraphObject();
db_id stationId;
QString stationName;
utils::StationType stationType;
/*!
* \brief Graph of the job while is stopping
*
* Contains informations to draw job line on top of the PlatformGraph
*/
struct JobStopGraph
{
JobStopEntry stop;
double arrivalY;
double departureY;
bool drawLabel;
};
/*!
* \brief Graph of a station track (platform)
*
* Contains informations to draw platform line and header name
* \sa JobStopGraph
*/
struct PlatformGraph
{
db_id platformId;
QString platformName;
QRgb color;
QFlags<utils::StationTrackType> platformType;
QVector<JobStopGraph> jobStops;
};
QVector<PlatformGraph> platforms;
double xPos;
};
#endif // STATIONGRAPHOBJECT_H

View File

@ -1,39 +0,0 @@
#include "stationlayer.h"
#include <QPainter>
#include "graphmanager.h"
#include "backgroundhelper.h"
#include "app/session.h"
#include "lines/linestorage.h"
StationLayer::StationLayer(GraphManager *mgr, QWidget *parent) :
QWidget(parent),
graphMgr(mgr),
horizontalScroll(0)
{
connect(Session->mLineStorage, &LineStorage::stationNameChanged,
this, static_cast<void(QWidget::*)()>(&StationLayer::update));
}
void StationLayer::setScroll(int value)
{
horizontalScroll = value;
update();
}
void StationLayer::paintEvent(QPaintEvent *)
{
QPainter p(this);
QColor c(255, 255, 255, 220);
p.fillRect(rect(), c);
db_id lineId = graphMgr->getCurLineId();
if(lineId == 0)
return; //No line selected
graphMgr->getBackGround()->drawForegroundStationLabels(&p, rect(),
horizontalScroll,
lineId);
}

View File

@ -1,25 +0,0 @@
#ifndef STATIONLAYER_H
#define STATIONLAYER_H
#include <QWidget>
class GraphManager;
class StationLayer : public QWidget
{
Q_OBJECT
public:
StationLayer(GraphManager *mgr, QWidget *parent = nullptr);
public slots:
void setScroll(int value);
protected:
void paintEvent(QPaintEvent *) override;
private:
GraphManager *graphMgr;
int horizontalScroll;
};
#endif // STATIONLAYER_H

View File

@ -0,0 +1,20 @@
set(MR_TIMETABLE_PLANNER_SOURCES
${MR_TIMETABLE_PLANNER_SOURCES}
graph/view/backgroundhelper.h
graph/view/backgroundhelper.cpp
graph/view/linegraphtoolbar.h
graph/view/linegraphtoolbar.cpp
graph/view/linegraphview.h
graph/view/linegraphview.cpp
graph/view/linegraphwidget.h
graph/view/linegraphwidget.cpp
graph/view/linegraphselectionwidget.h
graph/view/linegraphselectionwidget.cpp
PARENT_SCOPE
)

View File

@ -0,0 +1,461 @@
/*
* 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 "utils/jobcategorystrings.h"
#include <QPainter>
#include "utils/font_utils.h"
#include <QtMath>
#include <QDebug>
void BackgroundHelper::drawHourPanel(QPainter *painter, const QRectF &rect)
{
// TODO: settings
QFont hourTextFont;
setFontPointSizeDPI(hourTextFont, 15, painter);
QPen hourTextPen(AppSettings.getHourTextColor());
const int vertOffset = Session->vertOffset;
const int hourOffset = Session->hourOffset;
painter->setFont(hourTextFont);
painter->setPen(hourTextPen);
// qDebug() << "Drawing hours..." << rect << scroll;
const QString fmt(QStringLiteral("%1:00"));
const qreal top = rect.top() - vertOffset;
const qreal bottom = rect.bottom();
int h = qFloor(top / hourOffset);
if (h < 0)
h = 0;
QRectF labelRect = rect;
labelRect.setWidth(labelRect.width() * 0.9);
labelRect.setHeight(hourOffset);
labelRect.moveTop(h * hourOffset + vertOffset - hourOffset / 2);
for (; h <= 24 && labelRect.top() <= bottom; h++)
{
// qDebug() << "Y:" << y << fmt.arg(h);
painter->drawText(labelRect, fmt.arg(h), QTextOption(Qt::AlignVCenter | Qt::AlignRight));
labelRect.moveTop(labelRect.top() + hourOffset);
}
}
void BackgroundHelper::drawBackgroundHourLines(QPainter *painter, const QRectF &rect)
{
const double horizOffset = Session->horizOffset;
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();
if (x1 > x2 || b < vertOffset || t > b)
return;
int firstH = qCeil((t - vertOffset) / hourOffset);
int lastH = qFloor((b - vertOffset) / hourOffset);
if (firstH > 24 || lastH < 0)
return;
if (firstH < 0)
firstH = 0;
if (lastH > 24)
lastH = 24;
const int n = lastH - firstH + 1;
if (n <= 0)
return;
qreal y = vertOffset + firstH * hourOffset;
QLineF *arr = new QLineF[n];
for (int i = 0; i < n; i++)
{
arr[i] = QLineF(x1, y, x2, y);
y += hourOffset;
}
painter->setPen(hourLinePen);
painter->drawLines(arr, n);
delete[] arr;
}
void BackgroundHelper::drawStationHeader(QPainter *painter, LineGraphScene *scene,
const QRectF &rect)
{
QFont stationFont;
stationFont.setBold(true);
setFontPointSizeDPI(stationFont, 25, painter);
QPen stationPen(AppSettings.getStationTextColor());
QFont platfBoldFont = stationFont;
setFontPointSizeDPI(platfBoldFont, 16, painter);
QFont platfNormalFont = platfBoldFont;
platfNormalFont.setBold(false);
QPen electricPlatfPen(Qt::blue);
QPen nonElectricPlatfPen(Qt::black);
const qreal platformOffset = Session->platformOffset;
const int stationOffset = Session->stationOffset;
// On left go back by half station offset to center station label
// and center platform label by going a back of half platformOffset
const int leftOffset = -stationOffset / 2 - platformOffset / 2;
const double margin = stationOffset * 0.1;
QRectF r = rect;
for (auto st : qAsConst(scene->stations))
{
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
QRectF labelRect = r;
labelRect.setLeft(left + margin);
labelRect.setRight(right - margin);
labelRect.setBottom(r.bottom() - r.height() / 3);
painter->setPen(stationPen);
painter->setFont(stationFont);
painter->drawText(labelRect, Qt::AlignVCenter | Qt::AlignCenter, st.stationName);
labelRect = r;
labelRect.setTop(r.top() + r.height() * 2 / 3);
// Go to start of station (first platform)
// We need to compensate the half stationOffset used to center station label
double xPos = left + stationOffset / 2;
labelRect.setWidth(platformOffset);
for (const StationGraphObject::PlatformGraph &platf : qAsConst(st.platforms))
{
if (platf.platformType.testFlag(utils::StationTrackType::Electrified))
painter->setPen(electricPlatfPen);
else
painter->setPen(nonElectricPlatfPen);
if (platf.platformType.testFlag(utils::StationTrackType::Through))
painter->setFont(platfBoldFont);
else
painter->setFont(platfNormalFont);
labelRect.moveLeft(xPos);
painter->drawText(labelRect, Qt::AlignCenter, platf.platformName);
xPos += platformOffset;
}
}
}
void BackgroundHelper::drawStations(QPainter *painter, LineGraphScene *scene, const QRectF &rect)
{
const QRgb white = qRgb(255, 255, 255);
// const int horizOffset = Session->horizOffset;
const int vertOffset = Session->vertOffset;
// const int stationOffset = Session->stationOffset;
const double platfOffset = Session->platformOffset;
const int lastY = vertOffset + Session->hourOffset * 24 + 10;
const int width = AppSettings.getPlatformLineWidth();
const QColor mainPlatfColor = AppSettings.getMainPlatfColor();
QPen platfPen(mainPlatfColor, width);
QPointF top(0, vertOffset);
QPointF bottom(0, lastY);
for (const StationGraphObject &st : qAsConst(scene->stations))
{
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
top.rx() = bottom.rx() = st.xPos;
for (const StationGraphObject::PlatformGraph &platf : st.platforms)
{
if (platf.color == white)
platfPen.setColor(mainPlatfColor);
else
platfPen.setColor(platf.color);
painter->setPen(platfPen);
painter->drawLine(top, bottom);
top.rx() += platfOffset;
bottom.rx() += platfOffset;
}
}
}
void BackgroundHelper::drawJobStops(QPainter *painter, LineGraphScene *scene, const QRectF &rect,
bool drawSelection)
{
const double platfOffset = Session->platformOffset;
const double stationOffset = Session->stationOffset;
QFont jobNameFont;
setFontPointSizeDPI(jobNameFont, 20, painter);
painter->setFont(jobNameFont);
QPen jobPen;
jobPen.setWidth(AppSettings.getJobLineWidth());
jobPen.setCapStyle(Qt::RoundCap);
jobPen.setJoinStyle(Qt::RoundJoin);
QPen selectedJobPen;
const JobStopEntry selectedJob = scene->getSelectedJob();
if (drawSelection && selectedJob.jobId)
{
selectedJobPen.setWidthF(jobPen.widthF() * SelectedJobWidthFactor);
selectedJobPen.setCapStyle(Qt::RoundCap);
selectedJobPen.setJoinStyle(Qt::RoundJoin);
QColor color = Session->colorForCat(selectedJob.category);
color.setAlpha(SelectedJobAlphaFactor);
selectedJobPen.setColor(color);
}
QPointF top;
QPointF bottom;
JobCategory lastJobCategory = JobCategory::NCategories;
QTextOption textOption(Qt::AlignTop | Qt::AlignLeft);
for (const StationGraphObject &st : qAsConst(scene->stations))
{
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
const double maxJobLabelX = right + stationOffset;
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::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
top.setY(jobStop.arrivalY);
bottom.setY(jobStop.departureY);
const bool nullStopDuration = qFuzzyCompare(top.y(), bottom.y());
if (drawSelection && selectedJob.jobId == jobStop.stop.jobId)
{
// Draw selection around segment
painter->setPen(selectedJobPen);
if (nullStopDuration)
painter->drawPoint(top);
else
painter->drawLine(top, bottom);
// Reset pen
painter->setPen(jobPen);
}
if (lastJobCategory != jobStop.stop.category)
{
QColor color = Session->colorForCat(jobStop.stop.category);
jobPen.setColor(color);
painter->setPen(jobPen);
lastJobCategory = jobStop.stop.category;
}
if (nullStopDuration)
painter->drawPoint(top);
else
painter->drawLine(top, bottom);
if (jobStop.drawLabel)
{
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
const qreal topWithMargin = top.x() + platfOffset / 2;
QRectF r(topWithMargin, top.y(), maxJobLabelX - topWithMargin, 25);
painter->drawText(r, jobName, textOption);
}
}
top.rx() += platfOffset;
bottom.rx() += platfOffset;
}
}
}
void BackgroundHelper::drawJobSegments(QPainter *painter, LineGraphScene *scene, const QRectF &rect,
bool drawSelection)
{
const double stationOffset = Session->stationOffset;
QFont jobNameFont;
setFontPointSizeDPI(jobNameFont, 20, painter);
painter->setFont(jobNameFont);
QColor textBackground(Qt::white);
textBackground.setAlpha(100);
QPen jobPen;
jobPen.setWidth(AppSettings.getJobLineWidth());
jobPen.setCapStyle(Qt::RoundCap);
jobPen.setJoinStyle(Qt::RoundJoin);
QPen selectedJobPen;
const JobStopEntry selectedJob = scene->getSelectedJob();
if (drawSelection && selectedJob.jobId)
{
selectedJobPen.setWidthF(jobPen.widthF() * SelectedJobWidthFactor);
selectedJobPen.setCapStyle(Qt::RoundCap);
selectedJobPen.setJoinStyle(Qt::RoundJoin);
QColor color = Session->colorForCat(selectedJob.category);
color.setAlpha(SelectedJobAlphaFactor);
selectedJobPen.setColor(color);
}
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++)
{
const LineGraphScene::StationPosEntry &stPos = scene->stationPositions.at(i);
const double left = stPos.xPos;
double right = 0;
if (i < scene->stationPositions.size() - 2)
{
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
}
if (left > rect.right() || right < rect.left())
continue; // Skip station, it's not visible
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
const QLineF line(job.fromDeparture, job.toArrival);
if (drawSelection && selectedJob.jobId == job.jobId)
{
// Draw selection around segment
painter->setPen(selectedJobPen);
painter->drawLine(line);
// Reset pen
painter->setPen(jobPen);
}
if (lastJobCategory != job.category)
{
QColor color = Session->colorForCat(job.category);
jobPen.setColor(color);
painter->setPen(jobPen);
lastJobCategory = job.category;
}
painter->drawLine(line);
const QString jobName = JobCategoryName::jobName(job.jobId, job.category);
// Save old transformation to reset it after drawing text
const QTransform oldTransf = painter->transform();
// Move to line center, it will be rotation pivot
painter->translate(line.center());
// Rotate by line angle
qreal angle = line.angle();
if (job.fromDeparture.x() > job.toArrival.x())
angle += 180.0; // Prevent flipping text
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())
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
painter->fillRect(textRect, textBackground);
painter->drawText(textRect, jobName, textOption);
// Reset to old transformation
painter->setTransform(oldTransf);
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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
#include <QRectF>
class QPainter;
class LineGraphScene;
/*!
* \brief Helper class to render LineGraphScene contents
*
* Contains static helper functions to draw each part of the view
*
* \sa LineGraphScene
*/
class BackgroundHelper
{
public:
static void drawHourPanel(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 drawStations(QPainter *painter, LineGraphScene *scene, const QRectF &rect);
static void drawJobStops(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;
};
#endif // BACKGROUNDHELPER_H

View File

@ -0,0 +1,182 @@
/*
* 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>
#include <QPushButton>
#include "utils/delegates/sql/customcompletionlineedit.h"
#include "stations/match_models/stationsmatchmodel.h"
#include "stations/match_models/railwaysegmentmatchmodel.h"
#include "stations/match_models/linesmatchmodel.h"
#include "app/session.h"
#include <QHBoxLayout>
LineGraphSelectionWidget::LineGraphSelectionWidget(QWidget *parent) :
QWidget(parent),
matchModel(nullptr),
m_objectId(0),
m_graphType(LineGraphType::NoGraph)
{
QHBoxLayout *lay = new QHBoxLayout(this);
lay->setContentsMargins(0, 0, 0, 0);
graphTypeCombo = new QComboBox;
lay->addWidget(graphTypeCombo);
objectCombo = new CustomCompletionLineEdit(nullptr, this);
lay->addWidget(objectCombo);
QStringList items;
items.reserve(int(LineGraphType::NTypes));
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);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
}
LineGraphSelectionWidget::~LineGraphSelectionWidget()
{
if (matchModel)
{
objectCombo->setModel(nullptr);
delete matchModel;
matchModel = nullptr;
}
}
LineGraphType LineGraphSelectionWidget::getGraphType() const
{
return LineGraphType(graphTypeCombo->currentIndex());
}
void LineGraphSelectionWidget::setGraphType(LineGraphType type)
{
if (getGraphType() == type)
return;
graphTypeCombo->setCurrentIndex(int(type));
setupModel(type);
emit graphChanged(int(m_graphType), m_objectId);
}
db_id LineGraphSelectionWidget::getObjectId() const
{
return m_objectId;
}
const QString &LineGraphSelectionWidget::getObjectName() const
{
return m_name;
}
void LineGraphSelectionWidget::setObjectId(db_id objectId, const QString &name)
{
if (m_graphType == LineGraphType::NoGraph)
return; // Object ID must be null
m_name = name;
if (!objectId)
m_name.clear();
objectCombo->setData(objectId, name);
if (m_objectId != objectId)
emit graphChanged(int(m_graphType), m_objectId);
}
void LineGraphSelectionWidget::onTypeComboActivated(int index)
{
setupModel(LineGraphType(index));
emit graphChanged(int(m_graphType), m_objectId);
}
void LineGraphSelectionWidget::onCompletionDone()
{
if (!objectCombo->getData(m_objectId, m_name))
return;
emit graphChanged(int(m_graphType), m_objectId);
}
void LineGraphSelectionWidget::setupModel(LineGraphType type)
{
if (type != m_graphType)
{
// Clear old model
if (matchModel)
{
objectCombo->setModel(nullptr);
delete matchModel;
matchModel = nullptr;
}
// Manually clear line edit
m_objectId = 0;
m_name.clear();
objectCombo->setData(m_objectId, m_name);
switch (LineGraphType(type))
{
case LineGraphType::NoGraph:
default:
{
// Prevent recursion on loadGraph() calling back to us
type = LineGraphType::NoGraph;
break;
}
case LineGraphType::SingleStation:
{
StationsMatchModel *m = new StationsMatchModel(Session->m_Db, this);
m->setFilter(0);
matchModel = m;
break;
}
case LineGraphType::RailwaySegment:
{
RailwaySegmentMatchModel *m = new RailwaySegmentMatchModel(Session->m_Db, this);
m->setFilter(0, 0, 0);
matchModel = m;
break;
}
case LineGraphType::RailwayLine:
{
LinesMatchModel *m = new LinesMatchModel(Session->m_Db, true, this);
matchModel = m;
break;
}
}
if (matchModel)
objectCombo->setModel(matchModel);
}
m_graphType = type;
}

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