Kaynağa Gözat

Merge pull request #104 from fiffty-50/develop-unknown-airports

Fixes and improvements
Felix Turowsky 1 yıl önce
ebeveyn
işleme
8850c34f84
100 değiştirilmiş dosya ile 4664 ekleme ve 3808 silme
  1. 42 22
      CMakeLists.txt
  2. 126 119
      assets/database/database_schema.sql
  3. 0 1
      assets/database/templates.qrc
  4. 0 32
      assets/database/templates/currencies.json
  5. 0 0
      deprecated/aircraftwidget.ui
  6. 0 0
      deprecated/airportwidget.cpp
  7. 0 0
      deprecated/airportwidget.h
  8. 0 0
      deprecated/airportwidget.ui
  9. 19 53
      deprecated/pilotswidget.cpp
  10. 25 5
      deprecated/pilotswidget.h
  11. 0 0
      deprecated/pilotswidget.ui
  12. 2 3
      deprecated/tailswidget.cpp
  13. 0 0
      deprecated/tailswidget.h
  14. 41 32
      main.cpp
  15. 56 77
      mainwindow.cpp
  16. 14 20
      mainwindow.h
  17. 49 0
      src/classes/date.cpp
  18. 45 0
      src/classes/date.h
  19. 16 0
      src/classes/easaftl.cpp
  20. 14 0
      src/classes/easaftl.h
  21. 36 101
      src/classes/settings.cpp
  22. 207 63
      src/classes/settings.h
  23. 7 6
      src/classes/style.cpp
  24. 13 0
      src/classes/styleddatedelegate.cpp
  25. 23 0
      src/classes/styleddatedelegate.h
  26. 13 0
      src/classes/styledpilotdelegate.cpp
  27. 20 0
      src/classes/styledpilotdelegate.h
  28. 13 0
      src/classes/styledregistrationdelegate.cpp
  29. 20 0
      src/classes/styledregistrationdelegate.h
  30. 12 0
      src/classes/styledtimedelegate.cpp
  31. 22 0
      src/classes/styledtimedelegate.h
  32. 14 0
      src/classes/styledtypedelegate.cpp
  33. 16 0
      src/classes/styledtypedelegate.h
  34. 64 17
      src/classes/time.cpp
  35. 53 16
      src/classes/time.h
  36. 23 0
      src/database/airportentry.cpp
  37. 12 0
      src/database/airportentry.h
  38. 24 8
      src/database/currencyentry.cpp
  39. 24 7
      src/database/currencyentry.h
  40. 3 2
      src/database/database.cpp
  41. 3 3
      src/database/database.h
  42. 12 1
      src/database/databasecache.cpp
  43. 9 1
      src/database/databasecache.h
  44. 24 0
      src/database/flightentry.cpp
  45. 5 0
      src/database/flightentry.h
  46. 16 1
      src/database/tailentry.cpp
  47. 7 2
      src/database/tailentry.h
  48. 186 0
      src/database/views/logbookviewinfo.h
  49. 1 1
      src/functions/calc.cpp
  50. 6 6
      src/functions/calc.h
  51. 10 3
      src/functions/datetime.h
  52. 9 8
      src/functions/statistics.cpp
  53. 13 0
      src/gui/dialogues/entryeditdialog.cpp
  54. 35 0
      src/gui/dialogues/entryeditdialog.h
  55. 1 1
      src/gui/dialogues/exporttocsvdialog.cpp
  56. 42 62
      src/gui/dialogues/firstrundialog.cpp
  57. 9 2
      src/gui/dialogues/firstrundialog.h
  58. 1 1
      src/gui/dialogues/firstrundialog.ui
  59. 18 6
      src/gui/dialogues/newairportdialog.cpp
  60. 11 3
      src/gui/dialogues/newairportdialog.h
  61. 289 148
      src/gui/dialogues/newflightdialog.cpp
  62. 68 11
      src/gui/dialogues/newflightdialog.h
  63. 231 230
      src/gui/dialogues/newflightdialog.ui
  64. 23 5
      src/gui/dialogues/newpilotdialog.cpp
  65. 12 2
      src/gui/dialogues/newpilotdialog.h
  66. 37 25
      src/gui/dialogues/newsimdialog.cpp
  67. 17 2
      src/gui/dialogues/newsimdialog.h
  68. 34 11
      src/gui/dialogues/newtaildialog.cpp
  69. 10 2
      src/gui/dialogues/newtaildialog.h
  70. 34 4
      src/gui/verification/timeinput.cpp
  71. 7 1
      src/gui/verification/timeinput.h
  72. 79 0
      src/gui/widgets/airporttableeditwidget.cpp
  73. 52 0
      src/gui/widgets/airporttableeditwidget.h
  74. 10 2
      src/gui/widgets/backupwidget.cpp
  75. 2 0
      src/gui/widgets/backupwidget.h
  76. 264 0
      src/gui/widgets/currencywidget.cpp
  77. 84 0
      src/gui/widgets/currencywidget.h
  78. 1 1
      src/gui/widgets/debugwidget.cpp
  79. 13 171
      src/gui/widgets/homewidget.cpp
  80. 3 8
      src/gui/widgets/homewidget.h
  81. 8 394
      src/gui/widgets/homewidget.ui
  82. 188 0
      src/gui/widgets/logbooktableeditwidget.cpp
  83. 67 0
      src/gui/widgets/logbooktableeditwidget.h
  84. 0 322
      src/gui/widgets/logbookwidget.cpp
  85. 0 116
      src/gui/widgets/logbookwidget.h
  86. 0 169
      src/gui/widgets/logbookwidget.ui
  87. 112 0
      src/gui/widgets/pilottableeditwidget.cpp
  88. 52 0
      src/gui/widgets/pilottableeditwidget.h
  89. 47 261
      src/gui/widgets/settingswidget.cpp
  90. 2 17
      src/gui/widgets/settingswidget.h
  91. 254 663
      src/gui/widgets/settingswidget.ui
  92. 231 0
      src/gui/widgets/tableeditwidget.cpp
  93. 154 0
      src/gui/widgets/tableeditwidget.h
  94. 111 0
      src/gui/widgets/tailtableeditwidget.cpp
  95. 50 0
      src/gui/widgets/tailtableeditwidget.h
  96. 6 3
      src/gui/widgets/totalswidget.cpp
  97. 14 0
      src/gui/widgets/totalswidget.h
  98. 525 529
      src/gui/widgets/totalswidget.ui
  99. 85 24
      src/opl.h
  100. 2 2
      src/testing/importCrewlounge/processflights.cpp

+ 42 - 22
CMakeLists.txt

@@ -8,7 +8,7 @@ set(CMAKE_AUTOUIC ON)
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
 
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 set(QT_MIN_VERSION "5.5.1")
@@ -53,34 +53,38 @@ set(PROJECT_SOURCES
     src/gui/dialogues/exporttocsvdialog.h
     src/gui/dialogues/exporttocsvdialog.cpp
     src/gui/dialogues/exporttocsvdialog.ui
+    src/gui/dialogues/entryeditdialog.h
+    src/gui/dialogues/entryeditdialog.cpp
+
     # Widgets
-    src/gui/widgets/tailswidget.h
-    src/gui/widgets/tailswidget.cpp
-    src/gui/widgets/aircraftwidget.ui
-    src/gui/widgets/airportwidget.h
-    src/gui/widgets/airportwidget.cpp
-    src/gui/widgets/airportwidget.ui
+    src/gui/widgets/homewidget.h
+    src/gui/widgets/homewidget.cpp
+    src/gui/widgets/homewidget.ui
+    src/gui/widgets/totalswidget.h
+    src/gui/widgets/totalswidget.cpp
+    src/gui/widgets/totalswidget.ui
     src/gui/widgets/backupwidget.h
     src/gui/widgets/backupwidget.cpp
     src/gui/widgets/backupwidget.ui
     src/gui/widgets/debugwidget.h
     src/gui/widgets/debugwidget.cpp
     src/gui/widgets/debugwidget.ui
-    src/gui/widgets/homewidget.h
-    src/gui/widgets/homewidget.cpp
-    src/gui/widgets/homewidget.ui
-    src/gui/widgets/logbookwidget.h
-    src/gui/widgets/logbookwidget.cpp
-    src/gui/widgets/logbookwidget.ui
-    src/gui/widgets/pilotswidget.h
-    src/gui/widgets/pilotswidget.cpp
-    src/gui/widgets/pilotswidget.ui
+
     src/gui/widgets/settingswidget.h
     src/gui/widgets/settingswidget.cpp
     src/gui/widgets/settingswidget.ui
-    src/gui/widgets/totalswidget.h
-    src/gui/widgets/totalswidget.cpp
-    src/gui/widgets/totalswidget.ui
+
+    src/gui/widgets/tableeditwidget.h
+    src/gui/widgets/tableeditwidget.cpp
+    src/gui/widgets/pilottableeditwidget.h
+    src/gui/widgets/pilottableeditwidget.cpp
+    src/gui/widgets/tailtableeditwidget.h
+    src/gui/widgets/tailtableeditwidget.cpp
+    src/gui/widgets/airporttableeditwidget.h
+    src/gui/widgets/airporttableeditwidget.cpp
+    src/gui/widgets/logbooktableeditwidget.h
+    src/gui/widgets/logbooktableeditwidget.cpp
+
     # Verification
     src/gui/verification/validationstate.h
     src/gui/verification/userinput.h
@@ -95,7 +99,8 @@ set(PROJECT_SOURCES
     src/gui/verification/completerprovider.cpp
     src/gui/verification/tailinput.h
     src/gui/verification/tailinput.cpp
-
+    src/gui/widgets/currencywidget.h
+    src/gui/widgets/currencywidget.cpp
 
     # Classes
     src/classes/style.h
@@ -116,6 +121,20 @@ set(PROJECT_SOURCES
     src/classes/md5sum.cpp
     src/classes/time.h
     src/classes/time.cpp
+    src/classes/easaftl.h
+    src/classes/easaftl.cpp
+    src/classes/date.h
+    src/classes/date.cpp
+    src/classes/styleddatedelegate.h
+    src/classes/styleddatedelegate.cpp
+    src/classes/styledtimedelegate.h
+    src/classes/styledtimedelegate.cpp
+    src/classes/styledpilotdelegate.h
+    src/classes/styledpilotdelegate.cpp
+    src/classes/styledregistrationdelegate.h
+    src/classes/styledregistrationdelegate.cpp
+    src/classes/styledtypedelegate.h
+    src/classes/styledtypedelegate.cpp
 
     # Database Entries
     src/database/flightentry.h
@@ -132,7 +151,6 @@ set(PROJECT_SOURCES
     src/database/simulatorentry.cpp
     src/database/currencyentry.h
     src/database/currencyentry.cpp
-
     src/database/previousexperienceentry.h
     src/database/previousexperienceentry.cpp
 
@@ -158,6 +176,8 @@ set(PROJECT_SOURCES
     src/database/databasecache.h
     src/database/databasecache.cpp
 
+    src/database/views/logbookviewinfo.h
+
     # Ressources
     assets/icons.qrc
     assets/database/templates.qrc
@@ -224,7 +244,7 @@ if(WIN32)
         )
     endif()
 else()
-    if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
+if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
         qt_add_executable(openPilotLog
             ${PROJECT_SOURCES}
             ${QM_FILES}

+ 126 - 119
assets/database/database_schema.sql

@@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS 'tails' (
 	'multiengine'	INTEGER,
 	'engineType'	INTEGER,
 	'weightClass'	INTEGER,
+        'typeString'	TEXT,
 	PRIMARY KEY('tail_id' AUTOINCREMENT)
 );
 DROP TABLE IF EXISTS 'flights';
@@ -89,11 +90,11 @@ CREATE TABLE IF NOT EXISTS 'airports' (
 	PRIMARY KEY('airport_id' AUTOINCREMENT)
 );
 DROP TABLE IF EXISTS 'currencies';
-CREATE TABLE IF NOT EXISTS 'currencies' (
-	'currency_id'	INTEGER NOT NULL,
-	'currencyName'	TEXT,
-	'expiryDate'	NUMERIC,
-	PRIMARY KEY('currency_id' AUTOINCREMENT)
+CREATE TABLE IF NOT EXISTS "currencies" (
+        "currency_id"	INTEGER NOT NULL,
+        "currencyName"	TEXT,
+        "expiryDate"	NUMERIC,
+        PRIMARY KEY('currency_id' AUTOINCREMENT)
 );
 DROP TABLE IF EXISTS 'changelog';
 CREATE TABLE IF NOT EXISTS 'changelog' (
@@ -134,133 +135,139 @@ CREATE TABLE 'previousExperience' (
         'autoland'	INTEGER
 );
 DROP VIEW IF EXISTS 'viewDefault';
-CREATE VIEW viewDefault AS  
+CREATE VIEW viewDefault AS
 SELECT 	flight_id,
-        doft as 'Date',
-        dept AS 'Dept',
-        printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',
-        dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',
-        printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',
-        CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',
-        CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',
-        registration AS 'Registration',
-        FlightNumber AS 'Flight #',
-        remarks AS 'Remarks'
-FROM flights  
+        doft,
+        dept,
+        tofb,
+        dest,
+        tonb,
+        tblk,
+        pilots.pilot_id,
+        tails.tail_id,
+        tails.registration,
+        flightNumber,
+        remarks
+FROM flights
 INNER JOIN pilots on flights.pic = pilots.pilot_id
 INNER JOIN tails on flights.acft = tails.tail_id
-ORDER BY date DESC;
+ORDER BY doft DESC
 
 DROP VIEW IF EXISTS 'viewDefaultSim';
-CREATE VIEW viewDefaultSim AS 
-SELECT 	flight_id AS 'rowid',
-        doft as 'Date',
-        dept AS 'Dept',
-        printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',
-        dest AS 'Dest', printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',
-        printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',
-        CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',
-        CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',
-        registration AS 'Registration',
-        null AS 'Sim Type',
-        null AS 'Time of Session',
-        remarks AS 'Remarks'
-FROM flights   
-INNER JOIN pilots on flights.pic = pilots.pilot_id  
-INNER JOIN tails on flights.acft = tails.tail_id   
-UNION 
-        SELECT (session_id * -1),
-        date,
-        null, null, null, null,
-        'SIM',
-        null,
-        aircraftType,
-        registration,
-        deviceType,
-        printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)),
+CREATE VIEW viewDefaultSim AS
+SELECT 	flights.flight_id,
+        flights.doft,
+        flights.dept,
+        flights.tofb,
+        flights.dest,
+        flights.tonb,
+        flights.tblk,
+        pilots.pilot_id ,
+        tails.tail_id,
+        tails.registration,
+        null AS 'deviceType',
+        null AS 'SimTime',
+        flights.remarks
+FROM flights
+INNER JOIN pilots on flights.pic = pilots.pilot_id
+INNER JOIN tails on flights.acft = tails.tail_id
+UNION
+        SELECT (simulators.session_id * -1),
+        simulators.date,
+        null, null, null, null, null, null,
+        simulators.aircraftType,
+        simulators.registration,
+        simulators.deviceType,
+        simulators.totalTime,
         remarks
-FROM simulators 
-ORDER BY date DESC;
+FROM simulators
+ORDER BY date DESC
 
 DROP VIEW IF EXISTS 'viewEasa';
-CREATE VIEW viewEasa AS  SELECT  flight_id, 
-        doft as 'Date',
-        dept AS 'Dept',
-        printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',
-        dest AS 'Dest',
-        printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',
-        CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',
-        registration AS 'Registration',
-        (SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE',
-        (SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME',
-        (SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP',
-        printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',
-        CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',
-        ldgDay AS 'L/D',
-        ldgNight AS 'L/N',
-        (SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL)  AS 'Night',
-        (SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL)  AS 'IFR',
-        (SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL)  AS 'PIC',
-        (SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL)  AS 'SIC',
-        (SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL)  AS 'Dual',
-        (SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL)  AS 'FI',
-        remarks AS 'Remarks'
-FROM flights  
-INNER JOIN pilots on flights.pic = pilots.pilot_id  
-INNER JOIN tails on flights.acft = tails.tail_id  ORDER BY date DESC;
+CREATE VIEW viewEasa AS
+SELECT
+        flight_id,
+        doft,
+        dept,
+        tofb,
+        dest,
+        tonb,
+        tail_id,
+        registration,
+        tSPSE,
+        tSPME,
+        tMP,
+        tblk,
+        pilot_id,
+        ldgDay,
+        ldgNight,
+        tNight,
+        tIFR,
+        tPIC,
+        tSIC,
+        tDUAL,
+        tFI,
+        remarks
+FROM flights
+INNER JOIN pilots on flights.pic = pilots.pilot_id
+INNER JOIN tails on flights.acft = tails.tail_id  ORDER BY doft DESC
 
 DROP VIEW IF EXISTS 'viewEasaSim';
-CREATE VIEW viewEasaSim AS  SELECT  flight_id, 
-        doft as 'Date',
-        dept AS 'Dept',
-        printf('%02d',(tofb/60))||':'||printf('%02d',(tofb%60)) AS 'Time',
-        dest AS 'Dest',
-        printf('%02d',(tonb/60))||':'||printf('%02d',(tonb%60)) AS 'Time ',
-        CASE  WHEN variant IS NOT NULL THEN make||' '||model||'-'||variant  ELSE make||' '||model  END  AS 'Type',
-        registration AS 'Registration',
-        (SELECT printf('%02d',(tSPSE/60))||':'||printf('%02d',(tSPSE%60)) WHERE tSPSE IS NOT NULL) AS 'SP SE',
-        (SELECT printf('%02d',(tSPME/60))||':'||printf('%02d',(tSPME%60)) WHERE tSPME IS NOT NULL) AS 'SP ME',
-        (SELECT printf('%02d',(tMP/60))||':'||printf('%02d',(tMP%60)) WHERE tMP IS NOT NULL) AS 'MP',
-        printf('%02d',(tblk/60))||':'||printf('%02d',(tblk%60)) AS 'Total',
-        CASE  WHEN pilot_id = 1 THEN alias  ELSE lastname||', '||substr(firstname, 1, 1)||'.'  END  AS 'Name PIC',
-        ldgDay AS 'L/D',
-        ldgNight AS 'L/N',
-        (SELECT printf('%02d',(tNight/60))||':'||printf('%02d',(tNight%60)) WHERE tNight IS NOT NULL)  AS 'Night',
-        (SELECT printf('%02d',(tIFR/60))||':'||printf('%02d',(tIFR%60)) WHERE tIFR IS NOT NULL)  AS 'IFR',
-        (SELECT printf('%02d',(tPIC/60))||':'||printf('%02d',(tPIC%60)) WHERE tPIC IS NOT NULL)  AS 'PIC',
-        (SELECT printf('%02d',(tSIC/60))||':'||printf('%02d',(tSIC%60)) WHERE tSIC IS NOT NULL)  AS 'SIC',
-        (SELECT printf('%02d',(tDual/60))||':'||printf('%02d',(tDual%60)) WHERE tDual IS NOT NULL)  AS 'Dual',
-        (SELECT printf('%02d',(tFI/60))||':'||printf('%02d',(tFI%60)) WHERE tFI IS NOT NULL)  AS 'FI',
-        null AS 'Sim Type',
-        null AS 'Time of Session',
-        remarks AS 'Remarks'
-FROM flights    
-INNER JOIN pilots on flights.pic = pilots.pilot_id    
-INNER JOIN tails on flights.acft = tails.tail_id    
-UNION  
-SELECT (session_id * -1),  
+CREATE VIEW viewEasaSim AS
+SELECT  flight_id,
+        flights.doft as 'Date',
+        flights.dept,
+        flights.tofb,
+        flights.dest,
+        flights.tonb,
+        tails.tail_id AS 'Type',
+        tails.registration AS 'Registration',
+        flights.tSPSE,
+        flights.tSPME,
+        flights.tMP,
+        flights.tblk,
+        pilots.pilot_id AS 'PIC',
+        flights.ldgDay,
+        flights.ldgNight,
+        flights.tNight,
+        flights.tIFR,
+        flights.tPIC,
+        flights.tSIC,
+        flights.tDual,
+        flights.tFI,
+        null AS 'deviceType',
+        null AS 'simTime',
+        flights.remarks
+FROM flights
+INNER JOIN pilots on flights.pic = pilots.pilot_id
+INNER JOIN tails on flights.acft = tails.tail_id
+UNION
+SELECT (session_id * -1),
+        simulators.date,
+        null, null, null, null,
+        simulators.aircraftType,
+        simulators.registration,
+        null, null, null, null,
+        null, null, null, null,
+        null, null, null, null,
+        null,
+        simulators.deviceType,
+        simulators.totalTime,
+        simulators.remarks
+FROM simulators
+ORDER BY date DESC
+
+DROP VIEW IF EXISTS 'viewSimulators';
+CREATE VIEW viewSimulators AS
+SELECT (session_id * -1),
         date,
-        null,  null,  null,  null,
-        aircraftType,
         registration,
-        null,  null,  null,
-        'SIM',
-        null,  null,  null,  null,  null,  null,  null,  null,  null,
-        deviceType,  printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)),
+        aircraftType,
+        deviceType,
+        totalTime,
         remarks
-FROM simulators  
-ORDER BY date DESC;
-
-DROP VIEW IF EXISTS 'viewSimulators';
-CREATE VIEW viewSimulators AS SELECT (session_id * -1),  
-        date as 'Date',
-        registration AS 'Registration',
-        aircraftType AS 'Aircraft Type',
-        deviceType 'Sim Type',
-        printf('%02d',(totalTime/60))||':'||printf('%02d',(totalTime%60)) AS 'Time of Session',
-        remarks AS 'Remarks'
-FROM simulators  
-ORDER BY date DESC;
+FROM simulators
+ORDER BY date DESC
 
 DROP VIEW IF EXISTS 'viewTails';
 CREATE VIEW viewTails AS  

+ 0 - 1
assets/database/templates.qrc

@@ -3,7 +3,6 @@
         <file>templates/aircraft.json</file>
         <file>templates/airports.json</file>
         <file>templates/changelog.json</file>
-        <file>templates/currencies.json</file>
         <file>database_schema.sql</file>
     </qresource>
 </RCC>

+ 0 - 32
assets/database/templates/currencies.json

@@ -1,32 +0,0 @@
-[
-    {
-        "currency_id": 1,
-        "currencyName": "Licence",
-        "expiryDate": null
-    },
-    {
-        "currency_id": 2,
-        "currencyName": "Type Rating",
-        "expiryDate": null
-    },
-    {
-        "currency_id": 3,
-        "currencyName": "Line Check",
-        "expiryDate": null
-    },
-    {
-        "currency_id": 4,
-        "currencyName": "Medical",
-        "expiryDate": null
-    },
-    {
-        "currency_id": 5,
-        "currencyName": "Custom1",
-        "expiryDate": null
-    },
-    {
-        "currency_id": 6,
-        "currencyName": "Custom2",
-        "expiryDate": null
-    }
-]

+ 0 - 0
src/gui/widgets/aircraftwidget.ui → deprecated/aircraftwidget.ui


+ 0 - 0
src/gui/widgets/airportwidget.cpp → deprecated/airportwidget.cpp


+ 0 - 0
src/gui/widgets/airportwidget.h → deprecated/airportwidget.h


+ 0 - 0
src/gui/widgets/airportwidget.ui → deprecated/airportwidget.ui


+ 19 - 53
src/gui/widgets/pilotswidget.cpp → deprecated/pilotswidget.cpp

@@ -59,7 +59,7 @@ void PilotsWidget::setupModelAndView()
     view->verticalHeader()->hide();
     view->setAlternatingRowColors(true);
     view->setSortingEnabled(true);
-    sortColumn = Settings::read(Settings::UserData::PilotSortColumn).toInt();
+    sortColumn = Settings::getPilotSortColumn();
     view->sortByColumn(sortColumn, Qt::AscendingOrder);
 
     view->show();
@@ -70,10 +70,12 @@ void PilotsWidget::setupModelAndView()
 
 void PilotsWidget::connectSignalsAndSlots()
 {
-    QObject::connect(ui->tableView->selectionModel(),   &QItemSelectionModel::selectionChanged,
-                     this,                              &PilotsWidget::tableView_selectionChanged);
-    QObject::connect(ui->tableView->horizontalHeader(), &QHeaderView::sectionClicked,
-                     this,                              &PilotsWidget::tableView_headerClicked);
+    QObject::connect(ui->pilotSearchLineEdit,  &QLineEdit::textChanged,
+                     this,                     &PilotsWidget::filterLineEdit_textChanged);
+    QObject::connect(view->horizontalHeader(), &QHeaderView::sectionClicked,
+                     this,                     &PilotsWidget::newSortColumnSelected);
+    QObject::connect(view,					   &QTableView::clicked,
+                     this, 			    	   &PilotsWidget::editRequested);
 }
 
 void PilotsWidget::setUiEnabled(bool enabled)
@@ -103,43 +105,28 @@ void PilotsWidget::onPilotsWidget_databaseUpdated()
     refreshView();
 }
 
-void PilotsWidget::on_pilotSearchLineEdit_textChanged(const QString &arg1)
+void PilotsWidget::filterLineEdit_textChanged(const QString &arg1)
 {
     model->setFilter(QLatin1Char('\"') + ui->pilotsSearchComboBox->currentText()
                      + QLatin1String("\" LIKE '%") + arg1
                      + QLatin1String("%' AND ID > 1"));
 }
 
-void PilotsWidget::tableView_selectionChanged()
+void PilotsWidget::editRequested(const QModelIndex &index)
 {
-    if (this->findChild<NewPilotDialog*>() != nullptr) {
-        delete this->findChild<NewPilotDialog*>();
-    }
-
-    auto selection = ui->tableView->selectionModel();
-    selectedPilots.clear();
-
-    for (const auto& row : selection->selectedRows()) {
-        selectedPilots.append(row.data().toInt());
-        DEB << "Selected Tails(s) with ID: " << selectedPilots;
-    }
-    if(selectedPilots.length() == 1) {
-        NewPilotDialog np = NewPilotDialog(selectedPilots.first(), this);
-        np.setWindowFlag(Qt::Widget);
-        ui->stackedWidget->addWidget(&np);
-        ui->stackedWidget->setCurrentWidget(&np);
+    int pilotID = model->index(index.row(), 0).data().toInt();
 
-        setUiEnabled(false);
-        np.exec();
-        refreshView();
-        setUiEnabled(true);
-    }
+    NewPilotDialog np = NewPilotDialog(pilotID, this);
+    np.setWindowFlag(Qt::Widget);
+    ui->stackedWidget->addWidget(&np);
+    ui->stackedWidget->setCurrentWidget(&np);
+    np.exec();
+    refreshView();
 }
 
-void PilotsWidget::tableView_headerClicked(int column)
+void PilotsWidget::newSortColumnSelected(int newSortColumn)
 {
-    sortColumn = column;
-    Settings::write(Settings::UserData::PilotSortColumn, column);
+    Settings::setPilotSortColumn(newSortColumn);
 }
 
 void PilotsWidget::on_newPilotButton_clicked()
@@ -209,7 +196,7 @@ void PilotsWidget::onDeleteUnsuccessful()
     } else {
         QString constrained_flights_string;
         for (int i=0; i<constrained_flights.length(); i++) {
-            constrained_flights_string.append(getFlightSummary(constrained_flights[i]) + QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
+            constrained_flights_string.append(OPL::FlightEntry(constrained_flights[i]).getFlightSummary() + QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
             if (i>10) {
                 constrained_flights_string.append("<br>[...]<br>");
                 break;
@@ -245,24 +232,3 @@ const QString PilotsWidget::getPilotName(const OPL::PilotEntry &pilot) const
 
     return pilot.getLastName() + QLatin1String(", ") + pilot.getFirstName();
 }
-
-const QString PilotsWidget::getFlightSummary(const OPL::FlightEntry &flight) const
-{
-
-    if(!flight.isValid())
-        return QString();
-
-    auto tableData = flight.getData();
-    QString flight_summary;
-    auto space = QLatin1Char(' ');
-    flight_summary.append(tableData.value(OPL::FlightEntry::DOFT).toString() + space);
-    flight_summary.append(tableData.value(OPL::FlightEntry::DEPT).toString() + space);
-    flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TOFB).toInt()).toString()
-                          + space);
-    flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TONB).toInt()).toString()
-                          + space);
-    flight_summary.append(tableData.value(OPL::FlightEntry::DEST).toString());
-
-    return flight_summary;
-
-}

+ 25 - 5
src/gui/widgets/pilotswidget.h → deprecated/pilotswidget.h

@@ -59,12 +59,34 @@ public:
     ~PilotsWidget();
 
 private slots:
-    void tableView_selectionChanged();
-    void tableView_headerClicked(int);
+    /*!
+     * \brief Creates a dialog to add a new Pilot to the database
+     */
     void on_newPilotButton_clicked();
+
+    /*!
+     * \brief Deletes the selected pilot from the database if the selection is valid.
+     */
     void on_deletePilotButton_clicked();
+
+    /*!
+     * \brief Informs the user about a error that ocurred when trying to delete an entry.
+     */
     void onDeleteUnsuccessful();
-    void on_pilotSearchLineEdit_textChanged(const QString &arg1);
+
+    /*!
+     * \brief Sets a filter on the model
+     */
+    void filterLineEdit_textChanged(const QString &arg1);
+
+    /*!
+     * \brief Creates a new PilotWidget to allow editing of the selected item
+     */
+    void editRequested(const QModelIndex &index);
+    /*!
+     * \brief Sorts the table on the selected column
+     */
+    void newSortColumnSelected(int newSortColumn);
 
 public slots:
     /*!
@@ -96,8 +118,6 @@ private:
 
     const QString getPilotName(const OPL::PilotEntry &pilot) const;
 
-    const QString getFlightSummary(const OPL::FlightEntry &flight) const;
-
     void setupModelAndView();
 
     void connectSignalsAndSlots();

+ 0 - 0
src/gui/widgets/pilotswidget.ui → deprecated/pilotswidget.ui


+ 2 - 3
src/gui/widgets/tailswidget.cpp → deprecated/tailswidget.cpp

@@ -59,7 +59,7 @@ void TailsWidget::setupModelAndView()
     view->verticalHeader()->hide();
     view->setAlternatingRowColors(true);
 
-    sortColumn = Settings::read(Settings::UserData::TailSortColumn).toInt();
+    sortColumn = Settings::getTailSortColumn();
     view->setSortingEnabled(true);
     view->sortByColumn(sortColumn, Qt::DescendingOrder);
 
@@ -153,8 +153,7 @@ void TailsWidget::on_aircraftSearchLineEdit_textChanged(const QString &arg1)
 
 void TailsWidget::tableView_headerClicked(int column)
 {
-    sortColumn = column;
-    Settings::write(Settings::UserData::TailSortColumn, column);
+    Settings::setTailSortColumn(column);
 }
 
 void TailsWidget::on_deleteAircraftButton_clicked()

+ 0 - 0
src/gui/widgets/tailswidget.h → deprecated/tailswidget.h


+ 41 - 32
main.cpp

@@ -33,50 +33,70 @@
 #include <QDebug>
 #include <QTranslator>
 
+
+
+/*!
+ * \brief firstRun - is run if the application is run for the first time and launches
+ * the FirstRunDialog which guides the user through the initial set-up process.
+ */
+bool firstRun()
+{
+    if(FirstRunDialog().exec() == QDialog::Rejected){
+        LOG << "Initial setup incomplete or unsuccessfull.";
+        return false;
+    }
+
+    Settings::setSetupCompleted(true);
+    LOG << "Initial Setup Completed successfully";
+    QMessageBox mb;
+    mb.setText("Initial set-up has been completed successfully.<br><br>Please re-start the application.");
+    mb.exec();
+    return true;
+}
+
+
 /*!
  * \brief init - Sets up the logging facilities, loads the user settings and sets
  * up the application style before the MainWindow is instantiated
  */
-void init()
+bool init()
 {
+    // Check if another instance of the application is already running, we don't want
+    // different processes writing to the same database
+    RunGuard guard(QStringLiteral("opl_single_key"));
+    if ( !guard.tryToRun() ){
+        LOG << "Another Instance of openPilotLog is already running. Exiting.";
+        return false;
+    }
+
     LOG << "Setting up / verifying Application Directories...";
     if(OPL::Paths::setup()) {
         LOG << "Application Directories... verified";
     } else {
+        return false;
         LOG << "Unable to create directories.";
     }
+
     LOG << "Setting up logging facilities...";
     if(OPL::Log::init(true)) {
         LOG << "Logging enabled.";
     } else {
         LOG << "Unable to initalise logging.";
     }
+
     LOG << "Reading Settings...";
-    Settings::setup();
+    Settings::init();
     LOG << "Setting up application style...";
     OPL::Style::setup();
     // Translations to be done at a later stage
     //LOG << "Installing translator...";
     //ATranslator::installTranslator(OPL::Translations::English);
-}
 
-/*!
- * \brief firstRun - is run if the application is run for the first time and launches
- * the FirstRunDialog which guides the user through the initial set-up process.
- */
-int firstRun()
-{
-    if(FirstRunDialog().exec() == QDialog::Rejected){
-        LOG << "Initial setup incomplete or unsuccessfull.";
-        return 1;
-    }
+    // Check for First Run and launch Setup Wizard
+    if(!Settings::getSetupCompleted())
+        return firstRun();
 
-    Settings::write(Settings::Main::SetupComplete, true);
-    LOG << "Initial Setup Completed successfully";
-    QMessageBox mb;
-    mb.setText("Initial set-up has been completed successfully.<br><br>Please re-start the application.");
-    mb.exec();
-    return 0;
+    return true;
 }
 
 int main(int argc, char *argv[])
@@ -86,20 +106,9 @@ int main(int argc, char *argv[])
     QCoreApplication::setOrganizationDomain(ORGDOMAIN);
     QCoreApplication::setApplicationName(APPNAME);
 
-    // Check of another instance of the application is already running, we don't want
-    // different processes writing to the same database
-    RunGuard guard(QStringLiteral("opl_single_key"));
-    if ( !guard.tryToRun() ){
-        LOG << "Another Instance of openPilotLog is already running. Exiting.";
-        return 0;
-    }
-
     // Set Up the Application
-    init();
-
-    // Check for First Run and launch Setup Wizard
-    if (!Settings::read(Settings::Main::SetupComplete).toBool())
-        return firstRun();
+    if(!init())
+        return 1;
 
     // Create Main Window and set Window Icon acc. to Platform
     MainWindow w;

+ 56 - 77
mainwindow.cpp

@@ -17,30 +17,23 @@
  */
 #include <QToolBar>
 #include "mainwindow.h"
+#include "src/gui/widgets/airporttableeditwidget.h"
+#include "src/gui/widgets/logbooktableeditwidget.h"
+#include "src/gui/widgets/pilottableeditwidget.h"
+#include "src/gui/widgets/tailtableeditwidget.h"
 #include "ui_mainwindow.h"
 #include "src/database/database.h"
 #include "src/classes/style.h"
 #include "src/gui/dialogues/firstrundialog.h"
-#include "src/gui/dialogues/newflightdialog.h"
-#include "src/gui/dialogues/newsimdialog.h"
-#include "src/gui/dialogues/newflightdialog.h"
 #include "src/database/databasecache.h"
 #include "src/classes/settings.h"
 // Quick and dirty Debug area
 void MainWindow::doDebugStuff()
 {
-    OPL::RowData_T xp = DB->getTotals(false);
-    LOG << "Totals without previous:";
-    LOG << xp;
-
-    xp = DB->getTotals(true);
-    LOG << "Totals with previous:";
-    LOG << xp;
-
-    OPL::FlightEntry fe = OPL::FlightEntry();
-    LOG << "FLIGHT table: " << fe.getTableName();
-    OPL::Row row = OPL::Row();
-    LOG << "ROW table: " << row.getTableName();
+//    LogbookTableEditWidget *widget = new LogbookTableEditWidget(this);
+//    widget->init();
+//    widget->setWindowFlags(Qt::Dialog);
+//    widget->show();
 }
 
 MainWindow::MainWindow(QWidget *parent)
@@ -49,9 +42,6 @@ MainWindow::MainWindow(QWidget *parent)
 {
     ui->setupUi(this);
     init();
-
-    // set Startup Screen (Home Screen)
-    ui->stackedWidget->setCurrentWidget(homeWidget);
 }
 
 MainWindow::~MainWindow()
@@ -66,6 +56,7 @@ void MainWindow::init()
     setupToolbar();
     connectWidgets();
     setActionIcons(OPL::Style::getStyleType());
+    ui->stackedWidget->setCurrentWidget(homeWidget);
 }
 
 void MainWindow::setupToolbar()
@@ -93,16 +84,20 @@ void MainWindow::initialiseWidgets()
     homeWidget = new HomeWidget(this);
     ui->stackedWidget->addWidget(homeWidget);
 
-    logbookWidget = new LogbookWidget(this);
+    logbookWidget = new LogbookTableEditWidget(this);
+    logbookWidget->init();
     ui->stackedWidget->addWidget(logbookWidget);
 
-    aircraftWidget = new TailsWidget(this);
-    ui->stackedWidget->addWidget(aircraftWidget);
+    tailsWidget = new TailTableEditWidget(this);
+    tailsWidget->init();
+    ui->stackedWidget->addWidget(tailsWidget);
 
-    pilotsWidget = new PilotsWidget(this);
+    pilotsWidget = new PilotTableEditWidget(this);
+    pilotsWidget->init();
     ui->stackedWidget->addWidget(pilotsWidget);
 
-    airportWidget = new AirportWidget(this);
+    airportWidget = new AirportTableEditWidget(this);
+    airportWidget->init();
     ui->stackedWidget->addWidget(airportWidget);
 
     settingsWidget = new SettingsWidget(this);
@@ -122,8 +117,9 @@ void MainWindow::connectDatabase()
         WARN(tr("Error establishing database connection. The following error has ocurred:<br><br>%1")
              .arg(DB->lastError.text()));
     }
-    DBCache->init();
+
     // Load Cache
+    DBCache->init();
 }
 
 void MainWindow::setActionIcons(OPL::Style::StyleType style)
@@ -131,27 +127,27 @@ void MainWindow::setActionIcons(OPL::Style::StyleType style)
     switch (style){
     case OPL::Style::StyleType::Light:
         LOG << "Setting Light Icon theme";
-        ui->actionHome->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_HOME));
-        ui->actionNewFlight->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT));
-        ui->actionNewSim->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT)); // pending seperate icon
-        ui->actionLogbook->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK));
-        ui->actionAircraft->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT));
-        ui->actionPilots->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_PILOT));
-        ui->actionAirports->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP));
-        ui->actionSettings->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS));
-        ui->actionQuit->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_QUIT));
+        ui->actionHome->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_HOME));
+        ui->actionNewFlight->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT));
+        ui->actionNewSim->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT)); // TODO seperate icon
+        ui->actionLogbook->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK));
+        ui->actionAircraft->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT));
+        ui->actionPilots->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_PILOT));
+        ui->actionAirports->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP));
+        ui->actionSettings->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS));
+        ui->actionQuit->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_QUIT));
         break;
     case OPL::Style::StyleType::Dark:
         LOG << "Setting Dark Icon theme";
-        ui->actionHome->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_HOME_DARK));
-        ui->actionNewFlight->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK));
-        ui->actionNewSim->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK)); // pending separate icon
-        ui->actionLogbook->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK_DARK));
-        ui->actionAircraft->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT_DARK));
-        ui->actionPilots->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_PILOT_DARK));
-        ui->actionAirports->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP_DARK));
-        ui->actionSettings->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS_DARK));
-        ui->actionQuit->setIcon(QIcon(OPL::Assets::ICON_TOOLBAR_QUIT_DARK));
+        ui->actionHome->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_HOME_DARK));
+        ui->actionNewFlight->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK));
+        ui->actionNewSim->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_NEW_FLIGHT_DARK)); // pending separate icon
+        ui->actionLogbook->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_LOGBOOK_DARK));
+        ui->actionAircraft->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_AIRCRAFT_DARK));
+        ui->actionPilots->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_PILOT_DARK));
+        ui->actionAirports->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_BACKUP_DARK));
+        ui->actionSettings->setIcon(	QIcon(OPL::Assets::ICON_TOOLBAR_SETTINGS_DARK));
+        ui->actionQuit->setIcon(		QIcon(OPL::Assets::ICON_TOOLBAR_QUIT_DARK));
         break;
     }
 }
@@ -170,40 +166,19 @@ void MainWindow::nope()
  */
 void MainWindow::connectWidgets()
 {
-    QObject::connect(DB,             &OPL::Database::dataBaseUpdated,
-                     homeWidget,     &HomeWidget::refresh);
-    QObject::connect(settingsWidget, &SettingsWidget::settingChanged,
-                     homeWidget,     &HomeWidget::refresh);
-
-    QObject::connect(DB,             &OPL::Database::dataBaseUpdated,
-                     logbookWidget,  &LogbookWidget::refresh);
-    QObject::connect(settingsWidget, &SettingsWidget::settingChanged,
-                     logbookWidget,  &LogbookWidget::onLogbookWidget_viewSelectionChanged);
-
-    QObject::connect(DB,             &OPL::Database::dataBaseUpdated,
-                     aircraftWidget, &TailsWidget::onAircraftWidget_dataBaseUpdated);
-    QObject::connect(settingsWidget, &SettingsWidget::settingChanged,
-                     aircraftWidget, &TailsWidget::onAircraftWidget_settingChanged);
-
-    QObject::connect(DB,             &OPL::Database::dataBaseUpdated,
-                     pilotsWidget,   &PilotsWidget::onPilotsWidget_databaseUpdated);
     QObject::connect(settingsWidget, &SettingsWidget::settingChanged,
-                     pilotsWidget,   &PilotsWidget::onPilotsWidget_settingChanged);
+                     logbookWidget,  &LogbookTableEditWidget::viewSelectionChanged);
+    QObject::connect(this,			 &MainWindow::addFlightEntryRequested,
+                     logbookWidget,  &LogbookTableEditWidget::addEntryRequested);
+    QObject::connect(this,			 &MainWindow::addSimulatorEntryRequested,
+                     logbookWidget,  &LogbookTableEditWidget::addSimulatorEntryRequested);
     QObject::connect(settingsWidget, &SettingsWidget::settingChanged,
                      this,           &MainWindow::onStyleChanged);
-
-    QObject::connect(DB,              &OPL::Database::connectionReset,
-                     logbookWidget,   &LogbookWidget::repopulateModel);
-    QObject::connect(DB,              &OPL::Database::connectionReset,
-                     pilotsWidget,    &PilotsWidget::repopulateModel);
-    QObject::connect(DB,              &OPL::Database::connectionReset,
-                     aircraftWidget,  &TailsWidget::repopulateModel);
 }
 
 void MainWindow::onDatabaseInvalid()
 {
     QMessageBox db_error(this);
-    //db_error.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
     db_error.addButton(tr("Restore Backup"), QMessageBox::ButtonRole::AcceptRole);
     db_error.addButton(tr("Create New Database"), QMessageBox::ButtonRole::RejectRole);
     db_error.addButton(tr("Abort"), QMessageBox::ButtonRole::DestructiveRole);
@@ -235,7 +210,7 @@ void MainWindow::onDatabaseInvalid()
             LOG << "Initial setup incomplete or unsuccessfull.";
             on_actionQuit_triggered();
         }
-        Settings::write(Settings::Main::SetupComplete, true);
+        Settings::setSetupCompleted(true);
         LOG << "Initial Setup Completed successfully";
     }
 }
@@ -251,8 +226,16 @@ void MainWindow::on_actionHome_triggered()
 
 void MainWindow::on_actionNewFlight_triggered()
 {
-    auto* nf = new NewFlightDialog(this);
-    nf->exec();
+    ui->stackedWidget->setCurrentWidget(logbookWidget);
+    emit addFlightEntryRequested();
+}
+
+void MainWindow::on_actionNewSim_triggered()
+{
+    // auto nsd = NewSimDialog(this);
+    // nsd.exec();
+    ui->stackedWidget->setCurrentWidget(logbookWidget);
+    emit addSimulatorEntryRequested();
 }
 
 void MainWindow::on_actionLogbook_triggered()
@@ -262,7 +245,7 @@ void MainWindow::on_actionLogbook_triggered()
 
 void MainWindow::on_actionAircraft_triggered()
 {
-    ui->stackedWidget->setCurrentWidget(aircraftWidget);
+    ui->stackedWidget->setCurrentWidget(tailsWidget);
 }
 
 void MainWindow::on_actionPilots_triggered()
@@ -291,8 +274,4 @@ void MainWindow::on_actionDebug_triggered()
     ui->stackedWidget->setCurrentWidget(debugWidget);
 }
 
-void MainWindow::on_actionNewSim_triggered()
-{
-    auto nsd = NewSimDialog(this);
-    nsd.exec();
-}
+

+ 14 - 20
mainwindow.h

@@ -29,17 +29,14 @@
 #include <QKeyEvent>
 #include <QToolBar>
 
+#include <src/gui/widgets/logbooktableeditwidget.h>
+
 #include "src/gui/widgets/homewidget.h"
 #include "src/gui/widgets/settingswidget.h"
-#include "src/gui/widgets/logbookwidget.h"
-#include "src/gui/widgets/tailswidget.h"
-#include "src/gui/widgets/airportwidget.h"
-#include "src/gui/widgets/airportwidget.h"
-#include "src/gui/widgets/pilotswidget.h"
+#include "src/gui/widgets/tableeditwidget.h"
 #include "src/gui/widgets/debugwidget.h"
 #include "src/classes/style.h"
 
-enum Style {Light, Dark};
 QT_BEGIN_NAMESPACE
 namespace Ui {
 class MainWindow;
@@ -121,13 +118,13 @@ private:
 
     HomeWidget* homeWidget;
 
-    LogbookWidget* logbookWidget;
-
-    TailsWidget* aircraftWidget;
+    LogbookTableEditWidget* logbookWidget; // This widget has a slot not present in TableEditWidget
+    
+    TableEditWidget* tailsWidget;
 
-    PilotsWidget* pilotsWidget;
+    TableEditWidget* pilotsWidget;
 
-    AirportWidget* airportWidget;
+    TableEditWidget* airportWidget;
 
     SettingsWidget* settingsWidget;
 
@@ -172,18 +169,15 @@ protected:
      */
     void resizeEvent(QResizeEvent *event) override
     {
-        //DEB << "SIZE:" << this->size();
-        int icon_size;
-        if (this->height() < 760)
-            icon_size = (this->height() / 16);
-        else
-            icon_size = (this->height() / 14);
-
-        auto tool_bar = this->findChild<QToolBar*>();
-        tool_bar->setIconSize(QSize(icon_size, icon_size));
+        const auto icon_size = this->height() / 14;
+        const auto toolBar = this->findChild<QToolBar*>();
+        toolBar->setIconSize(QSize(icon_size, icon_size));
         event->accept();
     }
 
+signals:
+    void addFlightEntryRequested();
+    void addSimulatorEntryRequested();
     //void closeEvent(QCloseEvent *event) override; //TODO check and prompt for creation of backup?
 };
 #endif // MAINWINDOW_H

+ 49 - 0
src/classes/date.cpp

@@ -0,0 +1,49 @@
+#include "date.h"
+#include <QLocale>
+
+namespace OPL {
+
+Date::Date(int julianDay, const DateTimeFormat &format)
+    : m_format(format)
+{
+    m_date = QDate::fromJulianDay(julianDay);
+}
+
+Date::Date(const QString &textDate, const DateTimeFormat &format)
+    : m_format(format)
+{
+    switch(format.dateFormat()) {
+    case DateTimeFormat::DateFormat::Default:
+        m_date = QDate::fromString(textDate, Qt::ISODate);
+        break;
+    case DateTimeFormat::DateFormat::Custom:
+        m_date = QDate::fromString(textDate, format.dateFormatString());
+        break;
+    case DateTimeFormat::DateFormat::SystemLocale:
+        m_date = QDate::fromString(QLocale::system().dateFormat(QLocale::ShortFormat));
+        break;
+    default:
+        break;
+    }
+}
+
+Date::Date(const QDate &date, const DateTimeFormat &format)
+    : m_format(format), m_date(date)
+{}
+
+const QString Date::toString() const
+{
+    switch (m_format.dateFormat()) {
+    case DateTimeFormat::DateFormat::Default:
+        return m_date.toString(Qt::ISODate);
+    case DateTimeFormat::DateFormat::SystemLocale:
+        return m_date.toString(QLocale::system().dateFormat(QLocale::ShortFormat));
+    case DateTimeFormat::DateFormat::Custom:
+        return m_date.toString(m_format.dateFormatString());
+    default:
+        return QString();
+    }
+}
+
+
+} // namespace OPL

+ 45 - 0
src/classes/date.h

@@ -0,0 +1,45 @@
+#ifndef DATE_H
+#define DATE_H
+#include <QDate>
+#include "src/opl.h"
+namespace OPL {
+
+/*!
+ * \brief The Date class wraps the QDate class.
+ * \details The QDate class stores dates internally as a Julian Day number,
+ * an integer count of every day in a contiguous range, with 24 November 4714 BCE
+ * in the Gregorian calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar).
+ *
+ * Storing a given date as an integer value allows for easy conversion to localised strings
+ * as well as calculations like date ranges.
+ *
+ * Julian day is also used to store a date in the database.
+ */
+class Date
+{
+public:
+
+    Date() = delete;
+    Date(int julianDay, const DateTimeFormat &format);
+    Date(const QString &textDate, const DateTimeFormat &format);
+    Date(const QDate &date, const DateTimeFormat &format);
+
+    const QString toString() const;
+    const bool isValid() const { return m_date.isValid(); }
+
+    const inline int toJulianDay() const { return m_date.toJulianDay(); }
+    const static inline Date today(const DateTimeFormat &format) { return Date(QDate::currentDate().toJulianDay(), format); }
+
+//    void setDateFormat(const DateFormat_ &format) {m_format = format}
+    // todo copy constructor
+
+private:
+    QDate m_date;
+    DateTimeFormat m_format;
+};
+
+
+
+} // namespace OPL
+
+#endif // DATE_H

+ 16 - 0
src/classes/easaftl.cpp

@@ -0,0 +1,16 @@
+#include "easaftl.h"
+
+int EasaFTL::getLimit(OPL::Statistics::TimeFrame timeFrame)
+{
+    switch (timeFrame) {
+    case OPL::Statistics::TimeFrame::Rolling28Days:
+        return 100*60; // 100h
+    case OPL::Statistics::TimeFrame::Rolling12Months:
+        return 1000 * 60; // 1000h
+    case OPL::Statistics::TimeFrame::CalendarYear:
+        return 900 * 60; // 900h
+    default:
+        return 0;
+        break;
+    }
+}

+ 14 - 0
src/classes/easaftl.h

@@ -0,0 +1,14 @@
+#ifndef EASAFTL_H
+#define EASAFTL_H
+#include "src/functions/statistics.h"
+
+
+class EasaFTL
+{
+public:
+    EasaFTL() = delete;
+
+    static int getLimit(OPL::Statistics::TimeFrame timeFrame);
+};
+
+#endif // EASAFTL_H

+ 36 - 101
src/classes/settings.cpp

@@ -19,50 +19,12 @@
 #include <QSettings>
 #include "src/classes/paths.h"
 
-
-QMap<Settings::Main, QString> Settings::mainMap = {
-    {Main::SetupComplete,               QStringLiteral("setupComplete")},
-    {Main::Style,                       QStringLiteral("style")},
-    {Main::Font,                        QStringLiteral("font")},
-    {Main::FontSize,                    QStringLiteral("fontSize")},
-    {Main::UseSystemFont,               QStringLiteral("useSystemFont")},
-    {Main::LogbookView,                 QStringLiteral("logbookView")},
-    {Main::DateFormat,                  QStringLiteral("dateFormat")},
-};
-
-QMap<Settings::UserData, QString> Settings::userDataMap = {
-    {UserData::DisplaySelfAs,           QStringLiteral("displayselfas")},
-    {UserData::TailSortColumn,          QStringLiteral("tailSortColumn")},
-    {UserData::PilotSortColumn,         QStringLiteral("pilotSortColumn")},
-    {UserData::FtlWarningThreshold,     QStringLiteral("ftlWarningThreshold")},
-    {UserData::CurrWarningThreshold,    QStringLiteral("currWarningThreshold")},
-    {UserData::ShowToLgdCurrency,       QStringLiteral("showToLdgCurrency")},
-    {UserData::ShowLicCurrency,         QStringLiteral("showLicCurrency")},
-    {UserData::ShowTrCurrency,          QStringLiteral("showTrCurrency")},
-    {UserData::ShowLckCurrency,         QStringLiteral("showLckCurrency")},
-    {UserData::ShowMedCurrency,         QStringLiteral("showMedCurrency")},
-    {UserData::ShowCustom1Currency,     QStringLiteral("showCustom1Currency")},
-    {UserData::ShowCustom2Currency,     QStringLiteral("showCustom2Currency")},
-    {UserData::Custom1CurrencyName,     QStringLiteral("custom1CurrencyName")},
-    {UserData::Custom2CurrencyName,     QStringLiteral("custom2CurrencyName")},
-};
-
-QMap<Settings::FlightLogging, QString> Settings::flightLoggingMap = {
-    {FlightLogging::Function,           QStringLiteral("function")},
-    {FlightLogging::Approach,           QStringLiteral("approach")},
-    {FlightLogging::NightLoggingEnabled,QStringLiteral("nightLoggingEnabled")},
-    {FlightLogging::LogIFR,             QStringLiteral("logIfr")},
-    {FlightLogging::FlightNumberPrefix, QStringLiteral("flightnumberPrefix")},
-    {FlightLogging::PilotFlying,        QStringLiteral("pilotFlying")},
-    {FlightLogging::NightAngle,         QStringLiteral("nightangle")},
-    //{FlightLogging::FlightTimeFormat,   QStringLiteral("flightTimeFormat")},
-};
-
-void Settings::setup()
+void Settings::init()
 {
+    LOG << "Initialising application settings...";
     QSettings::setDefaultFormat(QSettings::IniFormat);
     QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, OPL::Paths::path(OPL::Paths::Settings));
-    QSettings();
+    Settings::settingsInstance = new QSettings();
 }
 
 /*!
@@ -70,70 +32,43 @@ void Settings::setup()
  */
 void Settings::resetToDefaults()
 {
-    write(Main::Style, QStringLiteral("Fusion"));
-    write(Main::UseSystemFont, true);
-    write(Main::LogbookView, 0);
-    write(Main::DateFormat, 0);
-
-    write(UserData::DisplaySelfAs, 0);
-    write(UserData::FtlWarningThreshold, 0.8); // To Do: UI Option
-    write(UserData::CurrWarningThreshold, 30);
-    write(UserData::ShowToLgdCurrency, true);
-    write(UserData::ShowLicCurrency, false);
-    write(UserData::ShowTrCurrency, false);
-    write(UserData::ShowLckCurrency, false);
-    write(UserData::ShowMedCurrency, false);
-    write(UserData::ShowCustom1Currency, false);
-    write(UserData::ShowCustom2Currency, false);
-    write(UserData::PilotSortColumn, 0);
-    write(UserData::TailSortColumn, 0);
-
-    write(FlightLogging::PilotFlying, true);
-    write(FlightLogging::NightAngle, -6);
+    setApplicationStyle(QStringLiteral("Fusion"));
+    setUseSystemFont(true);
+    setLogbookView(OPL::LogbookView::Default);
+    setLogAsPilotFlying(true);
+    setNightAngle(-6);
+    setShowSelfAs(0);
+    setFtlWarningThreshold(0.8);
+    setCurrencyWarningThreshold(90);
+    setPilotSortColumn(0);
+    setTailSortColumn(0);
+    setDisplayFormat(OPL::DateTimeFormat());
+
+    sync();
 }
 
-//
-// Read/Write
-//
-
-QVariant Settings::read(const FlightLogging key)
-{ return QSettings().value(groupOfKey(key)); }
-
-void Settings::write(const FlightLogging key, const QVariant &val)
-{ QSettings().setValue(groupOfKey(key), val); }
-
-QVariant Settings::read(const Main key)
-{ return QSettings().value(groupOfKey(key)); }
-
-void Settings::write(const Main key, const QVariant &val)
-{ QSettings().setValue(groupOfKey(key), val); }
-
-QVariant Settings::read(const UserData key)
-{ return QSettings().value(groupOfKey(key)); }
-
-void Settings::write(const UserData key, const QVariant &val)
-{ QSettings().setValue(groupOfKey(key), val); }
-
-//
-// QString conversion PATH
-//
-QString Settings::groupOfKey (const Settings::FlightLogging key)
-{ return QStringLiteral("flightlogging/") + flightLoggingMap[key]; }
+OPL::DateTimeFormat Settings::getDisplayFormat()
+{
+    using namespace OPL;
 
-QString Settings::groupOfKey (const Settings::Main key)
-{ return QStringLiteral("main/") + mainMap[key]; }
+    // date format
+    const DateTimeFormat::DateFormat dateFormat = static_cast<DateTimeFormat::DateFormat>(
+        settingsInstance->value(FORMAT_DATE_FORMAT, 0).toInt());
+    const QString dateFormatString = settingsInstance->value(FORMAT_DATE_STRING, QStringLiteral("yyyy-MM-dd")).toString();
+    // time format
+    const DateTimeFormat::TimeFormat timeFormat = static_cast<DateTimeFormat::TimeFormat>(
+        settingsInstance->value(FORMAT_TIME_FORMAT, 0).toInt());
+    const QString timeFormatString = settingsInstance->value(FORMAT_TIME_STRING, QStringLiteral("hh:mm")).toString();
 
-QString Settings::groupOfKey (const Settings::UserData key)
-{ return QStringLiteral("userdata/") + userDataMap[key]; }
+    return DateTimeFormat(dateFormat, dateFormatString, timeFormat, timeFormatString);
 
-//
-// QString conversion ONLY KEY
-//
-QString Settings::stringOfKey (const Settings::FlightLogging key)
-{ return  flightLoggingMap[key]; }
+}
 
-QString Settings::stringOfKey (const Settings::Main key)
-{ return  mainMap[key]; }
+void Settings::setDisplayFormat(const OPL::DateTimeFormat &format)
+{
+    settingsInstance->setValue(FORMAT_DATE_FORMAT, static_cast<int>(format.dateFormat()));
+    settingsInstance->setValue(FORMAT_DATE_STRING, format.dateFormatString());
+    settingsInstance->setValue(FORMAT_TIME_FORMAT, static_cast<int>(format.timeFormat()));
+    settingsInstance->setValue(FORMAT_TIME_STRING, format.timeFormatString());
+}
 
-QString Settings::stringOfKey (const Settings::UserData key)
-{ return  userDataMap[key]; }

+ 207 - 63
src/classes/settings.h

@@ -17,89 +17,233 @@
  */
 #ifndef SETTINGS_H
 #define SETTINGS_H
+#include "src/opl.h"
 #include <QtCore>
 #include <QSettings>
 
 /*!
- * \brief Thin wrapper for the QSettings class,
- * simplifying reading and writing of settings.
+ * \brief A wrapper for the QSettings class, simplifying reading and writing of settings.
  */
 class Settings {
 public:
-    enum class Main {
-        SetupComplete,
-        Style,
-        Font,
-        FontSize,
-        UseSystemFont,
-        LogbookView,
-        DateFormat,
-    };
-
-    enum class UserData {
-        DisplaySelfAs,
-        TailSortColumn,
-        PilotSortColumn,
-        FtlWarningThreshold,
-        CurrWarningThreshold,
-        ShowToLgdCurrency,
-        ShowLicCurrency,
-        ShowTrCurrency,
-        ShowLckCurrency,
-        ShowMedCurrency,
-        ShowCustom1Currency,
-        ShowCustom2Currency,
-        Custom1CurrencyName,
-        Custom2CurrencyName,
-    };
-
-    enum class FlightLogging {
-        Function,
-        Approach,
-        NightLoggingEnabled,
-        LogIFR,
-        FlightNumberPrefix,
-        PilotFlying,
-        NightAngle,
-        //FlightTimeFormat,
-    };
-
-    /*!
-     * \brief Should be called after QCoreApplication::set...Name have been called.
-     */
-    static void setup();
+    /*!
+     * \brief Initialise the default setting object. Call after QCoreApplication has been set up in main
+     */
+    static void init();
+
+    /*!
+     * \brief Reset the application settings to its default values
+     */
     static void resetToDefaults();
 
-    static QVariant read(const Main key);
-    static void write(const Main key, const QVariant &val);
+    /*!
+     * \brief Writes any unsaved changes to permanent storage
+     * \note This function is called automatically from QSettings's destructor and by the event loop at regular intervals,
+     * so you normally don't need to call it yourself.
+     */
+    static void sync() { settingsInstance->sync(); }
+
+    /*!
+     * \brief Initial set-up of the application and database has been completed
+     */
+    static bool getSetupCompleted() { return settingsInstance->value(MAIN_SETUP_COMPLETE, false).toBool(); }
+
+    /*!
+     * \brief Initial set-up of the application and database has been completed
+     */
+    static void setSetupCompleted(bool completed) { settingsInstance->setValue(MAIN_SETUP_COMPLETE, completed); }
+
+    /*!
+     * \brief returns the name of the preferred application style (default: fusion)
+     */
+    static const QString getApplicationStyle() { return settingsInstance->value(MAIN_STYLE, "Fusion").toString(); }
+
+    /*!
+     * \brief set the name of the preferred application style
+     */
+    static const void setApplicationStyle(const QString &style) { settingsInstance->setValue(MAIN_STYLE, style); }
+
+    /*!
+     * \brief returns the name of the preferred application font (default: system font)
+     * \return
+     */
+    static const QString getApplicationFontName() { return settingsInstance->value(MAIN_FONT_NAME, QString()).toString(); }
+
+    /*!
+     * \brief set the name of the preferred application font
+     * \return
+     */
+    static const void setApplicationFontName(const QString &fontName) { settingsInstance->setValue(MAIN_FONT_NAME, fontName); }
+
+    /*!
+     * \brief returns the preferred font size (default: 10)
+     * \return
+     */
+    static int getApplicationFontSize() { return settingsInstance->value(MAIN_FONT_SIZE, 10).toInt(); }
+
+    /*!
+     * \brief sets the preferred font size
+     */
+    static void setApplicationFontSize(int size) { settingsInstance->setValue(MAIN_FONT_SIZE, size); }
+
+    /*!
+     * \brief returns if the default system font should be used (default: true)
+     */
+    static bool getUseSystemFont() { return settingsInstance->value(MAIN_USE_SYSTEM_FONT, true).toBool(); }
+
+    /*!
+     * \brief sets if the default system font should be used
+     */
+    static void setUseSystemFont(bool value) { settingsInstance->setValue(MAIN_USE_SYSTEM_FONT, value); }
+
+    /*!
+     * \brief returns the view to be used in the logbook widget
+     */
+    static OPL::LogbookView getLogbookView() { return OPL::LogbookView(settingsInstance->value(MAIN_LOGBOOK_VIEW).toInt()); }
+
+    /*!
+     * \brief sets the view to be used in the logbook widget
+     */
+    static void setLogbookView(OPL::LogbookView view) { (settingsInstance->setValue(MAIN_LOGBOOK_VIEW, static_cast<int>(view))); }
+
+    /*!
+     * \brief returns the Display Format used in the application
+     */
+    static OPL::DateTimeFormat getDisplayFormat();
+
+    /*!
+     * \brief sets the Display Format used in the application
+     */
+    static void setDisplayFormat(const OPL::DateTimeFormat &format);
+
+    /*!
+     * \brief returns the default pilot function for new flights
+     */
+    static OPL::PilotFunction getPilotFunction() { return OPL::PilotFunction(settingsInstance->value(LOG_FUNCTION).toInt()); }
 
-    static QVariant read(const FlightLogging key);
-    static void write(const UserData key, const QVariant &val);
+    /*!
+     * \brief sets the default pilot function for new flights
+     */
+    static void setPilotFunction(OPL::PilotFunction function) { settingsInstance->setValue(LOG_FUNCTION, static_cast<int>(function)); }
 
-    static QVariant read(const UserData key);
-    static void write(const FlightLogging key, const QVariant &val);
+    /*!
+     * \brief returns the default approach type for new flights
+     */
+    static const QString getApproachType() { return settingsInstance->value(LOG_APPROACH).toString(); }
+    /*!
+     * \brief sets the default approach type for new flights
+     */
+    static void setApproachType(const QString &value) { settingsInstance->setValue(LOG_APPROACH, value); }
 
     /*!
-     * \brief Return string representation of group of key: "ini_header/key"
+     * \brief returns if automatic night time calculation is enabled for new flights
      */
-    static QString groupOfKey(const Main key);
-    static QString groupOfKey(const FlightLogging key);
-    static QString groupOfKey(const UserData key);
+    static bool getNightLoggingEnabled() { return settingsInstance->value(LOG_NIGHT).toBool(); }
 
     /*!
-     * \brief Return string representation of key
+     * \brief sets if automatic night time calculation is enabled for new flights
      */
-    static QString stringOfKey(const Main key);
-    static QString stringOfKey(const FlightLogging key);
-    static QString stringOfKey(const UserData key);
+    static void setNightLoggingEnabled(bool value) { settingsInstance->setValue(LOG_NIGHT, value); }
+
+    /*!
+     * \brief returns the angle of elevation for night time calculation (default: -6 degrees)
+     */
+    static int getNightAngle() { return settingsInstance->value(LOG_NIGHT_ANGLE, -6).toInt(); }
+
+    /*!
+     * \brief sets the angle of elevation for night time calculation
+     */
+    static void setNightAngle(int value) { settingsInstance->setValue(LOG_NIGHT_ANGLE, value); }
+
+    /*!
+     * \brief returns if flight time should be logged as IFR for new flights
+     */
+    static bool getLogIfr() { return settingsInstance->value(LOG_IFR).toBool(); }
+
+    /*!
+     * \brief sets if flight time should be logged as IFR for new flights
+     */
+    static void setLogIfr(bool value) { settingsInstance->setValue(LOG_IFR, value); }
+
+    /*!
+     * \brief returns if new flights should be logged as Pilot Flying
+     */
+    static bool getLogAsPilotFlying() { return settingsInstance->value(LOG_AS_PF).toBool(); }
+
+    /*!
+     * \brief sets if new flights should be logged as Pilot Flying
+     */
+    static void setLogAsPilotFlying(bool value) { settingsInstance->setValue(LOG_AS_PF, value); }
+
+    /*!
+     * \brief returns the default Flight Number Prefix for new flights
+     */
+    static const QString getFlightNumberPrefix() { return settingsInstance->value(LOG_PREFIX).toString(); }
+
+    /*!
+     * \brief sets the default Flight Number Prefix for new flights
+     */
+    static void setFlightNumberPrefix(const QString &value) { settingsInstance->setValue(LOG_PREFIX, value); }
+
+    /*!
+     * \brief sets how the logbook owner is shown in the view
+     * \details
+     * <ul>
+     * <li> 0 - self
+     *
+     */
+    static int getShowSelfAs() { return settingsInstance->value(SHOW_SELF_AS).toInt(); }
+
+    static void setShowSelfAs(int value) { settingsInstance->setValue(SHOW_SELF_AS, value); }
+
+    static int getTailSortColumn() { return settingsInstance->value(TAIL_SORT_COLUMN).toInt(); }
+    static void setTailSortColumn(int value) { settingsInstance->setValue(TAIL_SORT_COLUMN, value); }
+
+    static int getPilotSortColumn() { return settingsInstance->value(PILOT_SORT_COLUMN).toInt(); }
+    static void setPilotSortColumn(int value) { settingsInstance->setValue(PILOT_SORT_COLUMN, value); }
+
+    static double getFtlWarningThreshold() { return settingsInstance->value(FTL_WARNING_THR, 0.8).toDouble(); }
+    static void setFtlWarningThreshold(double value) { settingsInstance->setValue(FTL_WARNING_THR, value); }
+
+    static int getCurrencyWarningThreshold() { return settingsInstance->value(CURR_WARNING_THR, 90).toInt(); }
+    static void setCurrencyWarningThreshold(int days) { settingsInstance->setValue(CURR_WARNING_THR, days); }
+
 
-    static QSettings settings();
-    static void sync() { QSettings().sync(); }
 
 private:
-    static QMap<Main, QString> mainMap;
-    static QMap<UserData, QString> userDataMap;
-    static QMap<FlightLogging, QString> flightLoggingMap;
+
+    // keep an instance to avoid having to create a new QSettings object every time
+    static inline QSettings *settingsInstance;
+
+    // Setting keys
+    const static inline QString CURRENCY_STUB   	= QStringLiteral("userdata/%1Currency");
+    const static inline QString SHOW_SELF_AS    	= QStringLiteral("userdata/displaySelfAs");
+    const static inline QString TAIL_SORT_COLUMN    = QStringLiteral("userdata/tailSortColumn");
+    const static inline QString PILOT_SORT_COLUMN	= QStringLiteral("userdata/pilotSortColumn");
+    const static inline QString FTL_WARNING_THR		= QStringLiteral("ftlWarningThreshold");
+    const static inline QString CURR_WARNING_THR	= QStringLiteral("currWarningThreshold");
+
+    const static inline QString LOG_FUNCTION	= QStringLiteral("flightlogging/function");
+    const static inline QString LOG_APPROACH	= QStringLiteral("flightlogging/approach");
+    const static inline QString LOG_NIGHT	 	= QStringLiteral("flightlogging/nightLoggingEnabled");
+    const static inline QString LOG_NIGHT_ANGLE	= QStringLiteral("flightlogging/nightangle");
+    const static inline QString LOG_IFR	 	 	= QStringLiteral("flightlogging/logIfr");
+    const static inline QString LOG_AS_PF	 	= QStringLiteral("flightlogging/pilotFlying");
+    const static inline QString LOG_PREFIX	 	= QStringLiteral("flightlogging/flightnumberPrefix");
+
+    const static inline QString MAIN_SETUP_COMPLETE  	= QStringLiteral("main/setupComplete");
+    const static inline QString MAIN_STYLE			 	= QStringLiteral("main/style");
+    const static inline QString MAIN_FONT_NAME		 	= QStringLiteral("main/font");
+    const static inline QString MAIN_FONT_SIZE 		 	= QStringLiteral("main/fontSize");
+    const static inline QString MAIN_USE_SYSTEM_FONT 	= QStringLiteral("main/useSystemFont");
+    const static inline QString MAIN_LOGBOOK_VIEW 	 	= QStringLiteral("main/logbookView");
+
+    const static inline QString FORMAT_DATE_FORMAT 	= QStringLiteral("format/dateFormat");
+    const static inline QString FORMAT_DATE_STRING 	= QStringLiteral("format/dateFormatString");
+    const static inline QString FORMAT_TIME_FORMAT	= QStringLiteral("format/timeFormat");
+    const static inline QString FORMAT_TIME_STRING 	= QStringLiteral("format/timeFormatString");
+
+
 };
 
 #endif // SETTINGS_H

+ 7 - 6
src/classes/style.cpp

@@ -50,21 +50,22 @@ QLatin1String Style::DARK_PALETTE = QLatin1String("Dark-Palette");
  */
 void Style::setup()
 {
-    if (!Settings::read(Settings::Main::SetupComplete).toBool()) // Use system default for first run
+    if (!Settings::getSetupCompleted()) // Use system default for first run
         return;
+//    if (!Settings::read(Settings::Main::SetupComplete).toBool()) // Use system default for first run
+//        return;
     // Set Font
-    if (!Settings::read(Settings::Main::UseSystemFont).toBool()) {
-        QFont font(Settings::read(Settings::Main::Font).toString());
-        font.setPointSize(Settings::read(Settings::Main::FontSize).toUInt());
+    if (!Settings::getUseSystemFont()) {
+        const QFont font(Settings::getApplicationFontName(), Settings::getApplicationFontSize());
         qApp->setFont(font);
         LOG << "Application Font set: " << font.toString().split(',').first();
     }
     // Set style, stylesheet or palette
-    QString style_setting = Settings::read(Settings::Main::Style).toString();
+    const QString style_setting = Settings::getApplicationStyle();
 
     if (style_setting == DARK_PALETTE) {
         Style::setStyle(Style::darkPalette());
-        Settings::write(Settings::Main::Style, style_setting);
+        Settings::setApplicationStyle(style_setting);
         return;
     }
     for (const auto &style_name : styles) {

+ 13 - 0
src/classes/styleddatedelegate.cpp

@@ -0,0 +1,13 @@
+#include "styleddatedelegate.h"
+#include "src/classes/date.h"
+
+StyledDateDelegate::StyledDateDelegate(const OPL::DateTimeFormat &dateFormat, QObject *parent)
+    :
+    QStyledItemDelegate(parent),
+    m_format(dateFormat)
+{}
+
+QString StyledDateDelegate::displayText(const QVariant &value, const QLocale &locale) const
+{
+    return OPL::Date(value.toInt(), m_format).toString();
+}

+ 23 - 0
src/classes/styleddatedelegate.h

@@ -0,0 +1,23 @@
+#ifndef STYLEDDATEDELEGATE_H
+#define STYLEDDATEDELEGATE_H
+
+#include <QStyledItemDelegate>
+#include "src/opl.h"
+
+/*!
+ * \brief The StyledDateDelegate class is used to display a database date value human-readable.
+ * \details The database stores dates as an integer representing the days elapsed since the
+ * beginning of the julian calendar. This integer has to be converted to a human-readable date
+ * according to the users selected date format.
+ */
+class StyledDateDelegate : public QStyledItemDelegate
+{
+public:
+    StyledDateDelegate(const OPL::DateTimeFormat &dateFormat, QObject * parent = nullptr);
+
+    QString displayText(const QVariant &value, const QLocale &locale) const override;
+private:
+    OPL::DateTimeFormat m_format;
+};
+
+#endif // STYLEDDATEDELEGATE_H

+ 13 - 0
src/classes/styledpilotdelegate.cpp

@@ -0,0 +1,13 @@
+#include "styledpilotdelegate.h"
+#include "src/database/databasecache.h"
+
+StyledPilotDelegate::StyledPilotDelegate(QObject *parent)
+    : QStyledItemDelegate{parent}
+{
+
+}
+
+QString StyledPilotDelegate::displayText(const QVariant &value, const QLocale &locale) const
+{
+    return DBCache->getPilotNamesMap().value(value.toInt());
+}

+ 20 - 0
src/classes/styledpilotdelegate.h

@@ -0,0 +1,20 @@
+#ifndef STYLEDPILOTDELEGATE_H
+#define STYLEDPILOTDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+/*!
+ * \brief The StyledPilotDelegate class is used to display a database date value human-readable.
+ * \details The database stores pilots as an integer representing a foreign key into a database
+ * of pilots. This delegate uses the Database cache to map this ID to a name and displays the name
+ * in the view.
+ */
+class StyledPilotDelegate : public QStyledItemDelegate
+{
+public:
+    explicit StyledPilotDelegate(QObject *parent = nullptr);
+
+    QString displayText(const QVariant &value, const QLocale &locale) const override;
+};
+
+#endif // STYLEDPILOTDELEGATE_H

+ 13 - 0
src/classes/styledregistrationdelegate.cpp

@@ -0,0 +1,13 @@
+#include "styledregistrationdelegate.h"
+#include "src/database/databasecache.h"
+
+StyledRegistrationDelegate::StyledRegistrationDelegate(QObject *parent)
+    : QStyledItemDelegate{parent}
+{
+
+}
+
+QString StyledRegistrationDelegate::displayText(const QVariant &value, const QLocale &locale) const
+{
+    return DBCache->getTailsMap().value(value.toInt());
+}

+ 20 - 0
src/classes/styledregistrationdelegate.h

@@ -0,0 +1,20 @@
+#ifndef STYLEDREGISTRATIONDELEGATE_H
+#define STYLEDREGISTRATIONDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+/*!
+ * \brief The StyledRegistrationDelegate class is used to display a database date value human-readable.
+ * \details The database stores tails as an integer representing a foreign key into a database
+ * of tails. This delegate uses the Database cache to map this ID to a registration to display
+ * in the view.
+ */
+class StyledRegistrationDelegate : public QStyledItemDelegate
+{
+public:
+    explicit StyledRegistrationDelegate(QObject *parent = nullptr);
+
+    QString displayText(const QVariant &value, const QLocale &locale) const override;
+};
+
+#endif // STYLEDREGISTRATIONDELEGATE_H

+ 12 - 0
src/classes/styledtimedelegate.cpp

@@ -0,0 +1,12 @@
+#include "styledtimedelegate.h"
+#include "src/classes/time.h"
+
+StyledTimeDelegate::StyledTimeDelegate(const OPL::DateTimeFormat &format, QObject *parent)
+    : QStyledItemDelegate{parent}, m_format(format)
+{}
+
+QString StyledTimeDelegate::displayText(const QVariant &value, const QLocale &locale) const
+{
+    const OPL::Time time(value.toInt(), m_format);
+    return time.toString();
+}

+ 22 - 0
src/classes/styledtimedelegate.h

@@ -0,0 +1,22 @@
+#ifndef STYLEDTIMEDELEGATE_H
+#define STYLEDTIMEDELEGATE_H
+
+#include "src/opl.h"
+#include <QStyledItemDelegate>
+
+/*!
+ * \brief The StyledTimeDelegate class is used to convert the database time format to a human-readable format.
+ * \details The database stores time values as an integer representing minutes elapsed since midnight. This
+ * delegate can be used in a QTableView to format the database value as "hh:mm"
+ */
+class StyledTimeDelegate : public QStyledItemDelegate
+{
+public:
+    explicit StyledTimeDelegate(const OPL::DateTimeFormat &format, QObject *parent = nullptr);
+
+    QString displayText(const QVariant &value, const QLocale &locale) const override;
+private:
+    OPL::DateTimeFormat m_format;
+};
+
+#endif // STYLEDTIMEDELEGATE_H

+ 14 - 0
src/classes/styledtypedelegate.cpp

@@ -0,0 +1,14 @@
+#include "styledtypedelegate.h"
+#include "src/database/databasecache.h"
+
+StyledTypeDelegate::StyledTypeDelegate(QObject *parent)
+    : QStyledItemDelegate{parent}
+{
+
+}
+
+QString StyledTypeDelegate::displayText(const QVariant &value, const QLocale &locale) const
+{
+    Q_UNUSED(locale);
+    return DBCache->getTypesMap().value(value.toInt());
+}

+ 16 - 0
src/classes/styledtypedelegate.h

@@ -0,0 +1,16 @@
+#ifndef STYLEDTYPEDELEGATE_H
+#define STYLEDTYPEDELEGATE_H
+
+#include <QStyledItemDelegate>
+#include <QObject>
+
+class StyledTypeDelegate : public QStyledItemDelegate
+{
+    Q_OBJECT
+public:
+    explicit StyledTypeDelegate(QObject *parent = nullptr);
+
+    QString displayText(const QVariant &value, const QLocale &locale) const override;
+};
+
+#endif // STYLEDTYPEDELEGATE_H

+ 64 - 17
src/classes/time.cpp

@@ -1,20 +1,47 @@
 #include "time.h"
+#include "math.h"
 
 namespace OPL {
 
-Time::Time()
+Time::Time(const DateTimeFormat &format)
+    : m_format(format),
+    m_minutes(-1)
+{}
+
+Time::Time(const QTime &qTime, const DateTimeFormat &format)
 {
+    m_minutes = qTime.isValid() ? qTime.minute() + qTime.hour() * 60 : -1;
 }
 
+Time::Time(int32_t minutes, const DateTimeFormat &format)
+    : m_minutes(minutes), m_format(format)
+{}
+
 bool Time::isValidTimeOfDay() const
 {
-    return m_minutes <= MINUTES_PER_DAY;
+    return isValid() && m_minutes <= MINUTES_PER_DAY;
+}
+
+bool Time::isValid() const
+{
+    return m_minutes >= 0;
 }
 
 const QString Time::toString() const
 {
-    // convert to hh:mm
-    return QString::number(m_minutes / 60).rightJustified(2, '0') + QLatin1Char(':') + QString::number(m_minutes % 60).rightJustified(2, '0');
+    switch(m_format.timeFormat()) {
+    case DateTimeFormat::TimeFormat::Default:
+        return QString::number(m_minutes / 60).rightJustified(2, '0')
+               + ':'
+               + QString::number(m_minutes % 60).rightJustified(2, '0');
+    case DateTimeFormat::TimeFormat::Decimal:
+        return QString::number(m_minutes / 60.0, 'f', 2);
+        break;
+    case DateTimeFormat::TimeFormat::Custom:
+        return QTime(0, m_minutes, 0).toString(m_format.timeFormatString());
+    default:
+        return QString();
+    }
 }
 
 int32_t Time::toMinutes() const
@@ -22,20 +49,38 @@ int32_t Time::toMinutes() const
     return m_minutes;
 }
 
-Time Time::fromString(const QString &timeString)
+Time Time::fromString(const QString &timeString, const DateTimeFormat &format)
 {
-    const QStringList parts = timeString.split(QChar(':'));
-    if(parts.size() < 2)
-        return {};
-
+    switch(format.timeFormat()) {
+    case DateTimeFormat::TimeFormat::Default:
+    {
+        const auto qTime = QTime::fromString(timeString, QStringLiteral("hh:mm"));
+        return Time(qTime, format);
+        break;
+    }
+    case DateTimeFormat::TimeFormat::Decimal:
+    {
+        // try to convert string to double
+        bool ok = false;
+        const double timeValue = timeString.toDouble(&ok);
 
-    int32_t hours = parts[0].toInt();
-    int32_t minutes = parts[1].toInt();
+        if(!ok) {
+            return {-1, format};
+        }
 
-    if(hours < 0 || minutes < 0)
-        return{};
+        // extract integer and fractional part
+        double hours, minutes;
+        hours = modf(timeValue, &minutes);
 
-    return Time(hours * 60 + minutes);
+        // create the Time Object
+        return(Time(hours * 60 + minutes, format));
+        break;
+    }
+    case DateTimeFormat::TimeFormat::Custom:
+        const auto qTime = QTime::fromString(timeString, format.timeFormatString());
+        return Time(qTime, format);
+        break;
+    }
 }
 
 Time Time::blockTime(const Time &offBlocks, const Time &onBlocks)
@@ -43,16 +88,18 @@ Time Time::blockTime(const Time &offBlocks, const Time &onBlocks)
     // make sure both times are in 24h range
     bool bothTimesAreValid = offBlocks.isValidTimeOfDay() && onBlocks.isValidTimeOfDay();
     if(!bothTimesAreValid)
-        return {};
+        return {-1, offBlocks.m_format};
 
     // calculate the block time
     if(onBlocks.m_minutes > offBlocks.m_minutes) {
         // take-off and landing on the same day
-        return Time(onBlocks.m_minutes - offBlocks.m_minutes);
+        return Time(onBlocks.m_minutes - offBlocks.m_minutes, offBlocks.m_format);
     } else {
+        if(offBlocks.m_minutes == onBlocks.m_minutes)
+            return Time(0, offBlocks.m_format);
         // landing the day after take off
         int minutesToMidnight = MINUTES_PER_DAY - offBlocks.m_minutes;
-        return Time(minutesToMidnight + onBlocks.m_minutes);
+        return Time(minutesToMidnight + onBlocks.m_minutes, offBlocks.m_format);
     }
 }
 

+ 53 - 16
src/classes/time.h

@@ -1,6 +1,7 @@
 #ifndef TIME_H
 #define TIME_H
 
+#include "src/opl.h"
 #include <QtCore>
 namespace OPL {
 
@@ -8,18 +9,17 @@ namespace OPL {
  * \brief The Time class handles conversions between user input / user-facing time data display and
  * database format.
  * \details Time data in the database is stored as an integer value of minutes, whereas the user-facing
- * time data is normally displayed in the Qt::ISODATE format. A database value of 72 would for example be
- * displayed as 01:12 (1 hour and 12 minutes).
+ * time data is normally displayed in accordance with the selected DateTimeFormat, by default "hh:mm".
  */
 class Time
 {
-private:
-    const static inline int MINUTES_PER_DAY = 24 * 60;
-    int32_t m_minutes = 0;
-
 public:
-    Time();
-    Time(int32_t minutes) : m_minutes(minutes) {};
+    Time() = delete;
+    Time(const DateTimeFormat &format);
+    Time(const QTime &qTime, const DateTimeFormat &format);
+    Time(int32_t minutes, const DateTimeFormat &format);;
+
+    enum TimeFrame {Day, Week, Year};
 
     /**
      * @brief isValidTimeOfDay - determines whether the instance can be converted to a time hh:mm
@@ -27,8 +27,13 @@ public:
      */
     bool isValidTimeOfDay() const;
 
+    /*!
+     * \brief a time is considered valid if it has a time value of >= 0
+     */
+    bool isValid() const;
+
     /**
-     * @brief toString returns the time as hh:mm
+     * @brief toString returns the time in the specified format
      */
     const QString toString() const;
 
@@ -42,17 +47,49 @@ public:
      * @param timeString the input string
      * @return the Time Object corresponding to the string, equivalent to 0 minutes if conversion fails.
      */
-    static Time fromString(const QString& timeString);
+    static Time fromString(const QString& timeString, const DateTimeFormat &format);
 
-    /**
-     * @brief timeDifference returns the time difference between this time and another time object.
-     * @param other - The other time object
-     * @return the number of minutes of time difference, or 0 if one of the two objects is greater than 24h
+    /*!
+     * \brief Calculate the elapsed time between two events
+     * \param offBlocks - The start tmie
+     * \param onBlocks - the end time
+     * \return The elapsed time
      */
-    int32_t timeElapsed(const Time &other);
-
     static Time blockTime(const Time &offBlocks, const Time& onBlocks);
+
+    /*!
+     * \brief Calculate elapsed time between two events
+     * \param offBlocks - The start time
+     * \param onBlocks - The end time
+     * \return the elapsed time in minutes
+     */
     static int32_t blockMinutes(const Time &offBlocks, const Time& onBlocks);
+
+    /*!
+     * \brief toMinutes returns the number of minutes in the given time frame
+     * \param count - The number of time frames (e.g. '7' days)
+     * \return
+     */
+    static constexpr int timeFrameToMinutes(TimeFrame timeFrame, int count) {
+        switch (timeFrame) {
+        case Day:
+            return count * MINUTES_PER_DAY;
+        case Week:
+            return count * 7 * MINUTES_PER_DAY;
+        case Year:
+            return count * 7 * 52 * MINUTES_PER_DAY;
+        default:
+            return 0;
+        }
+    }
+
+
+private:
+    static constexpr int MINUTES_PER_DAY = 24 * 60;
+
+    const DateTimeFormat m_format;
+    int32_t m_minutes;
+
 };
 
 }// namespace OPL

+ 23 - 0
src/database/airportentry.cpp

@@ -46,4 +46,27 @@ const QString AirportEntry::getIcaoCode() const
     return getData().value(ICAO).toString();
 }
 
+const QString AirportEntry::getAirportName() const
+{
+    return getData().value(NAME).toString();
+}
+
+const QString AirportEntry::getAirportDescriptor() const
+{
+    if(getIataCode().isEmpty()) {
+        if(getAirportName().isEmpty()) {
+            return getIcaoCode();
+        }
+        return getIcaoCode()
+               + QStringLiteral(" - ")
+               + getAirportName();
+    }
+    return getIcaoCode()
+           + QStringLiteral(" - ")
+           + getAirportName()
+           + QStringLiteral(" (")
+           + getIataCode()
+           + QLatin1Char(')');
+}
+
 } // namespace OPL

+ 12 - 0
src/database/airportentry.h

@@ -43,6 +43,18 @@ public:
      */
     const QString getIcaoCode() const;
 
+    /*!
+     * \brief Returns the airport common given name
+     */
+    const QString getAirportName() const;
+
+    /*!
+     * \brief return a string describing the airport
+     * \details The string consists of the Airport ICAO Code, and if available
+     * IATA Code and Airport Name
+     */
+    const QString getAirportDescriptor() const;
+
     /*!
      * \brief The ICAO code is a 4-letter alphanumeric identifier for airports
      */

+ 24 - 8
src/database/currencyentry.cpp

@@ -19,14 +19,6 @@
 
 namespace OPL {
 
-CurrencyEntry::CurrencyEntry()
-    : Row(DbTable::Currencies, 0)
-{}
-
-CurrencyEntry::CurrencyEntry(const RowData_T &row_data)
-    : Row(DbTable::Currencies, 0, row_data)
-{}
-
 CurrencyEntry::CurrencyEntry(int row_id, const RowData_T &row_data)
     : Row(DbTable::Currencies, row_id, row_data)
 {}
@@ -36,4 +28,28 @@ const QString CurrencyEntry::getTableName() const
     return TABLE_NAME;
 }
 
+void CurrencyEntry::setName(const QString &displayName)
+{
+    auto data = getData();
+    data.insert(NAME, displayName);
+    setData(data);
+}
+
+const QString CurrencyEntry::getName() const
+{
+    return getData().value(NAME).toString();
+}
+
+void CurrencyEntry::setExpiryDate(const Date &date)
+{
+    auto data = getData();
+    data.insert(EXPIRYDATE, date.toJulianDay());
+    setData(data);
+}
+
+const Date CurrencyEntry::getExpiryDate(const OPL::DateTimeFormat &format) const
+{
+    return OPL::Date(getData().value(EXPIRYDATE).toInt(), format);
+}
+
 } // namespace OPL

+ 24 - 7
src/database/currencyentry.h

@@ -18,6 +18,7 @@
 #ifndef CURRENCYENTRY_H
 #define CURRENCYENTRY_H
 #include "src/database/row.h"
+#include "src/classes/date.h"
 
 namespace OPL {
 
@@ -29,23 +30,39 @@ namespace OPL {
  */
 class CurrencyEntry : public Row
 {
-    const static inline QString TABLE_NAME = QStringLiteral("currencies");
 public:
-    CurrencyEntry();
-    CurrencyEntry(const RowData_T &row_data);
+
+    enum Currency {Licence = 1, TypeRating = 2, LineCheck = 3, Medical = 4, Custom1 = 5, Custom2 = 6, TakeOffLanding = 7};
+
+    CurrencyEntry() = delete;
+    CurrencyEntry(const RowData_T &row_data) = delete;
     CurrencyEntry(int row_id, const RowData_T &row_data);
 
     const QString getTableName() const override;
 
+    void setName(const QString& displayName);
+    const QString getName() const;
+
+    void setExpiryDate(const OPL::Date &date);
+    const OPL::Date getExpiryDate(const OPL::DateTimeFormat &format) const;
+
+private:
+
+    /*!
+     * \brief The sql column name for the row id
+     */
+    const static inline QString ROW_ID = QStringLiteral("currency_id");
 
+    /*!
+     * \brief The sql column name for the display name
+     */
+    const static inline QString NAME = QStringLiteral("currencyName");
     /*!
     * \brief The sql column name for the expiry date
     */
     const static inline QString EXPIRYDATE  = QStringLiteral("expiryDate");
-    /*!
-     * \brief The sql column name for the currency name
-     */
-    const static inline QString CURRENCYNAME = QStringLiteral("currencyName");
+
+    const static inline QString TABLE_NAME = QStringLiteral("currencies");
 };
 
 } // namespace OPL

+ 3 - 2
src/database/database.cpp

@@ -517,6 +517,7 @@ const RowData_T Database::getTotals(bool includePreviousExperience)
         return entry_data;
     }
 
+    // name the return types for easy mapping to QLineEdit names
     statement = "SELECT"
                 " SUM(tblk) AS tblk,"
                 " SUM(tSPSE) AS tSPSE,"
@@ -563,10 +564,10 @@ const RowData_T Database::getTotals(bool includePreviousExperience)
         int entryValue = entry_data.value(it.key()).toInt();
 
         const QVariant sum = prevXpValue + entryValue;
-        it.value() = sum;
+        entry_data.insert(it.key(), sum);
     }
 
-    return prev_exp_data;
+    return entry_data;
 }
 
 QList<int> Database::getForeignKeyConstraints(int foreign_row_id, OPL::DbTable table)

+ 3 - 3
src/database/database.h

@@ -277,10 +277,10 @@ public:
     /*!
      * \brief Retreives a currency entry from the database. See row class for details.
      */
-    inline OPL::CurrencyEntry getCurrencyEntry(int row_id)
+    inline OPL::CurrencyEntry getCurrencyEntry(OPL::CurrencyEntry::Currency currency)
     {
-        const auto data = getRowData(OPL::DbTable::Currencies, row_id);
-        return OPL::CurrencyEntry(row_id, data);
+        const auto data = getRowData(OPL::DbTable::Currencies, currency);
+        return OPL::CurrencyEntry(currency, data);
     }
 
     /*!

+ 12 - 1
src/database/databasecache.cpp

@@ -58,6 +58,11 @@ const IdMap DatabaseCache::fetchMap(CompleterTarget target)
     case Tails:
         statement.append(QStringLiteral("SELECT ROWID, registration FROM tails"));
         break;
+    case Types:
+        statement.append(QStringLiteral("SELECT ROWID, make||' '||model FROM tails WHERE model IS NOT NULL AND variant IS NULL "
+                                        " UNION "
+                                        " SELECT ROWID, make||' '||model||'-'||variant FROM tails WHERE variant IS NOT NULL"));
+        break;
     case AircraftTypes:
         statement.append(QStringLiteral("SELECT ROWID, make||' '||model FROM aircraft WHERE model IS NOT NULL AND variant IS NULL "
                          "UNION "
@@ -132,6 +137,7 @@ void DatabaseCache::updateTails()
             reg = copy + " (" + reg + QLatin1Char(')');
         }
     }
+    typesMap = fetchMap(Types);
 }
 
 void DatabaseCache::updateAirports()
@@ -144,7 +150,7 @@ void DatabaseCache::updateAirports()
 
 void DatabaseCache::updateSimulators()
 {
-    TODO << "not yet implemented";
+    TODO << "Simulators map not yet cached";
 }
 
 void DatabaseCache::updatePilots()
@@ -239,6 +245,11 @@ const IdMap &DatabaseCache::getTailsMap() const
     return tailsMap;
 }
 
+const IdMap &DatabaseCache::getTypesMap() const
+{
+    return typesMap;
+}
+
 
 
 } // namespace OPL

+ 9 - 1
src/database/databasecache.h

@@ -44,7 +44,7 @@ public:
     DatabaseCache(DatabaseCache const&) = delete;
     void operator=(DatabaseCache const&) = delete;
 
-    enum CompleterTarget {PilotNames, Tails, AircraftTypes, AirportsAny, AirportsICAO, AirportNames, AirportsIATA, Companies};
+    enum CompleterTarget {PilotNames, Tails, AircraftTypes, AirportsAny, AirportsICAO, AirportNames, AirportsIATA, Companies, Types};
 
     void init();
 
@@ -52,6 +52,7 @@ public:
     const IdMap &getAirportsMapIATA() const;
     const IdMap &getPilotNamesMap() const;
     const IdMap &getTailsMap() const;
+    const IdMap &getTypesMap() const;
 
     const QStringList &getPilotNamesList() const;
     const QStringList &getTailsList() const;
@@ -74,7 +75,14 @@ private:
     IdMap airportsMapIATA;
     IdMap airportsMapNames;
     IdMap pilotNamesMap;
+    /*!
+     * \brief key: tail_id / value: registration
+     */
     IdMap tailsMap;
+    /*!
+     * \brief key: tail_id value: type string ("Boeing 737-800")
+     */
+    IdMap typesMap;
     IdMap aircraftMap;
     // Lists
     QStringList pilotNamesList;

+ 24 - 0
src/database/flightentry.cpp

@@ -16,6 +16,8 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "flightentry.h"
+#include "src/classes/date.h"
+#include "src/classes/time.h"
 
 namespace OPL {
 
@@ -36,4 +38,26 @@ const QString FlightEntry::getTableName() const
     return TABLE_NAME;
 }
 
+const QString FlightEntry::getFlightSummary() const
+{
+    using namespace OPL;
+    if(!isValid())
+        return QString();
+
+    auto tableData = getData();
+    QString flight_summary;
+    const auto space = QLatin1Char(' ');
+    flight_summary.append(Date(tableData.value(FlightEntry::DOFT).toInt(), DateTimeFormat()).toString() + space);
+    flight_summary.append(tableData.value(FlightEntry::DEPT).toString() + space);
+    flight_summary.append(Time(tableData.value(FlightEntry::TOFB).toInt(), DateTimeFormat()).toString()
+                          + space);
+    flight_summary.append(Time(tableData.value(FlightEntry::TONB).toInt(), DateTimeFormat()).toString()
+                          + space);
+    flight_summary.append(tableData.value(FlightEntry::DEST).toString());
+
+    return flight_summary;
+}
+
+
+
 } // namespace OPL

+ 5 - 0
src/database/flightentry.h

@@ -34,6 +34,11 @@ public:
 
     const QString getTableName() const override;
 
+    /*!
+     * \brief returns a String representation of the key data of this flight
+     */
+    const QString getFlightSummary() const;
+
     const static inline QString ROWID          = QStringLiteral("flight_id");
     const static inline QString DOFT           = QStringLiteral("doft");
     const static inline QString DEPT           = QStringLiteral("dept");

+ 16 - 1
src/database/tailentry.cpp

@@ -42,7 +42,22 @@ const QString TailEntry::registration() const
 }
 
 const QString TailEntry::type() const {
-    return getData().value(MAKE).toString();
+    const auto &data = getData();
+
+    if(data.value(VARIANT).toString().isEmpty()) {
+        if(data.value(MODEL).toString().isEmpty()) {
+            return data.value(MAKE).toString();
+        }
+        return data.value(MAKE).toString()
+               + QLatin1Char(' ')
+               + data.value(MODEL).toString();
+    }
+
+    return data.value(MAKE).toString()
+           + QLatin1Char(' ')
+           + data.value(MODEL).toString()
+           + QLatin1Char('-')
+           + data.value(VARIANT).toString();
 }
 
 } // namespace OPL

+ 7 - 2
src/database/tailentry.h

@@ -44,9 +44,9 @@ public:
      */
     const QString registration() const;
     /*!
-     * \brief Return the aircraft type
+     * \brief Return the aircraft type (Make and  - if available - Model and Variant)
      */
-    const QString type()         const; //TODO - Create String for make-model-variant
+    const QString type() const;
 
     /*!
      * \brief The entries row id in the database
@@ -102,6 +102,11 @@ public:
      * <\ul>
      */
     static const inline QString WEIGHT_CLASS = QStringLiteral("weightClass");
+
+    /*!
+     * \brief The aircraft type string ("Make Model-Variant")
+     */
+    static const inline QString TYPE_STRING = QStringLiteral("typeString");
 };
 
 } // namespace OPL

+ 186 - 0
src/database/views/logbookviewinfo.h

@@ -0,0 +1,186 @@
+#ifndef LOGBOOKVIEWINFO_H
+#define LOGBOOKVIEWINFO_H
+
+#include "src/opl.h"
+#include <QtCore>
+
+namespace OPL {
+
+
+/*!
+ * \brief The LogbookViewInfo class is a base class for classes that encapsulate information
+ * about a LogbookView
+ * \details In the logbook display, SQL views are used instead of raw table data, since in
+ * some cases data is aggregated from different tables. Using views avoid
+ */
+class LogbookViewInfo : public QObject {
+    Q_OBJECT // enable tr()
+public:
+    
+    /*!
+     * \brief Return the column in the view which contains the date of flight
+     */
+    static constexpr int getDateColumn(LogbookView view)
+    {
+        return DATE_COLUMNS.at(static_cast<int>(view));
+    }
+
+    /*!
+     * \brief Return the column in the view which contains the aircrafts type
+     */
+    static constexpr int getTypeColumn(LogbookView view)
+    {
+        return TYPE_COLUMNS.at(static_cast<int>(view));
+    }
+    
+    /*!
+     * \brief Return the column(s) in the view which contain pilot names
+     */
+    static constexpr int getPicColumn(LogbookView view)
+    {
+        return PIC_COLUMNS.at(static_cast<int>(view));
+    }
+    
+    /*!
+     * \brief Return the column(s) in the view which contain Time entries
+     */
+    static constexpr std::vector<int> getTimeColumns(LogbookView view)
+    {
+        switch (view) {
+        case LogbookView::Default:
+            return { 3, 5, 6 };
+        case LogbookView::DefaultWithSim:
+            return { 3, 5, 6, 11 };
+        case LogbookView::Easa:
+            return { 3, 5, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20 };
+        case LogbookView::EasaWithSim:
+            return { 3, 5, 8, 9, 10, 11, 15, 16, 17, 18, 19, 20, 22 };
+        default:
+            assert(((void)"View is not implemented", false));
+            return { 0 };
+        }
+    }
+
+    // translations need to be done at runtime
+    static const QStringList getTableHeaders(LogbookView view)
+    {
+        switch (view) {
+        case LogbookView::Default:
+            return {
+                QStringLiteral("flight_id"), // flight id column - hidden
+                tr("Date of Flight"),
+                tr("Dept"),
+                tr("Time"),
+                tr("Dest"),
+                tr("Time"),
+                tr("Total"),
+                tr("Name PIC"),
+                tr("Type"),
+                tr("Registration"),
+                tr("Flight Number"),
+                tr("Remarks"),
+            };
+        case LogbookView::DefaultWithSim:
+            return {
+                QStringLiteral("flight_id"), // flight id column - hidden
+                tr("Date of Flight"),
+                tr("Dept"),
+                tr("Time"),
+                tr("Dest"),
+                tr("Time"),
+                tr("Total"),
+                tr("Name PIC"),
+                tr("Type"),
+                tr("Registration"),
+                tr("Sim Type"),
+                tr("Time of Session"),
+                tr("Remarks"),
+            };
+        case LogbookView::Easa:
+            return {
+                QStringLiteral("flight_id"), // flight id column - hidden
+                tr("Date of Flight"),
+                tr("Dept"),
+                tr("Time"),
+                tr("Dest"),
+                tr("Time"),
+                tr("Type"),
+                tr("Registration"),
+                tr("SP SE"),
+                tr("SP ME"),
+                tr("MP"),
+                tr("Total"),
+                tr("Name PIC"),
+                tr("L/D"),
+                tr("L/N"),
+                tr("Night"),
+                tr("IFR"),
+                tr("PIC"),
+                tr("SIC"),
+                tr("Dual"),
+                tr("FI"),
+                tr("Remarks"),
+            };
+        case LogbookView::EasaWithSim:
+            return {
+                QStringLiteral("flight_id"), // flight id column - hidden
+                tr("Date of Flight"),
+                tr("Dept"),
+                tr("Time"),
+                tr("Dest"),
+                tr("Time"),
+                tr("Type"),
+                tr("Registration"),
+                tr("SP SE"),
+                tr("SP ME"),
+                tr("MP"),
+                tr("Total"),
+                tr("Name PIC"),
+                tr("L/D"),
+                tr("L/N"),
+                tr("Night"),
+                tr("IFR"),
+                tr("PIC"),
+                tr("SIC"),
+                tr("Dual"),
+                tr("FI"),
+                tr("Sim Type"),
+                tr("Time of Session"),
+                tr("Remarks"),
+            };
+        default:
+            assert(((void)"View is not implemented", false));
+            return {};
+        }
+    }
+
+private:
+
+    static constexpr std::array DATE_COLUMNS {
+        1, // Default
+        1, // Default With Sim
+        1, // Easa
+        1, // Easa With Sim
+        1, // Simulator Only
+    };
+
+    static constexpr std::array TYPE_COLUMNS {
+        8, // Default
+        8, // Default With Sim
+        6, // Easa
+        6, // Easa With Sim
+        1, // Simulator Only
+    };
+
+    static constexpr std::array PIC_COLUMNS {
+        7, // Default
+        7, // Default With Sim
+        12, // Easa
+        12, // Easa With Sim
+        -1, // Simulator Only
+    };
+
+};
+
+} // namespace OPL
+#endif // LOGBOOKVIEWINFO_H

+ 1 - 1
src/functions/calc.cpp

@@ -343,7 +343,7 @@ void OPL::Calc::updateAutoTimes(int acft_id)
  */
 void OPL::Calc::updateNightTimes()
 {
-    const int night_angle = Settings::read(Settings::FlightLogging::NightAngle).toInt();
+    int night_angle = Settings::getNightAngle();
 
     //find all flights for aircraft
     auto statement = QStringLiteral("SELECT ROWID FROM flights");

+ 6 - 6
src/functions/calc.h

@@ -144,8 +144,8 @@ struct NightTimeValues{
     {
         nightMinutes = calculateNightTime(dept, dest, departure_time, block_minutes, night_angle);
 
-        nightTime = OPL::Time(nightMinutes);
-        totalTime = OPL::Time(block_minutes);
+        OPL::Time nightTime = OPL::Time(nightMinutes, DateTimeFormat());
+        OPL::Time totalTime = OPL::Time(block_minutes, DateTimeFormat());
 
         if (nightMinutes == 0) { // all day
             takeOffNight = false;
@@ -167,13 +167,13 @@ struct NightTimeValues{
 
     };
 
-    NightTimeValues(bool to_night, bool ldg_night, int night_minutes, OPL::Time night_time, OPL::Time total_time)
-        : takeOffNight(to_night), landingNight(ldg_night), nightMinutes(night_minutes), nightTime(night_time), totalTime(total_time){};
+//    NightTimeValues(bool to_night, bool ldg_night, int night_minutes, OPL::Time night_time, OPL::Time total_time)
+//        : takeOffNight(to_night), landingNight(ldg_night), nightMinutes(night_minutes), nightTime(night_time), totalTime(total_time){};
     bool takeOffNight;
     bool landingNight;
     int nightMinutes;
-    OPL::Time nightTime;
-    OPL::Time totalTime;
+//    OPL::Time nightTime;
+//    OPL::Time totalTime;
 
     inline bool isAllDay()      {return (!takeOffNight  && !landingNight);}
     inline bool isAllNight()    {return ( takeOffNight  &&  landingNight);}

+ 10 - 3
src/functions/datetime.h

@@ -1,10 +1,17 @@
 #ifndef DATETIME_H
 #define DATETIME_H
 #include "src/opl.h"
+#include "src/classes/date.h"
+#include "src/classes/time.h"
 
 namespace OPL {
 
 class DateTime {
+
+public:
+//    DateTime(const OPL::Date date, const OPL::Time &time);
+
+
 public:
     const inline static QString ISO_FORMAT_STRING = QStringLiteral("yyyy-MM-dd");
     const inline static QString DE_FORMAT_STRING = QStringLiteral("dd.MM.yyyy");
@@ -69,11 +76,11 @@ public:
      * \brief dateTimeToString formats a QDateTime object into a string in a uniform way.
      * \return
      */
-    static inline const QString dateTimeToString (const QDateTime& date_time, OPL::DateTimeFormat format) {
+    static inline const QString dateTimeToString (const QDateTime& date_time, OPL::DateTimeFormat_deprecated format) {
         switch (format) {
-        case OPL::DateTimeFormat::Default:
+        case OPL::DateTimeFormat_deprecated::Default:
             return date_time.toString(Qt::ISODate);
-        case OPL::DateTimeFormat::Backup:
+        case OPL::DateTimeFormat_deprecated::Backup:
             return date_time.toString(QStringLiteral("yyyy_MM_dd_T_hh_mm"));
         default:
             return QString();

+ 9 - 8
src/functions/statistics.cpp

@@ -35,21 +35,21 @@ int OPL::Statistics::totalTime(TimeFrame time_frame)
         break;
     case TimeFrame::CalendarYear:
         start.setDate(QDate::currentDate().year(), 1, 1);
-        start_date = start.toString(Qt::ISODate);
+        start_date = QString::number(start.toJulianDay());
         start_date.append(QLatin1Char('\''));
         start_date.prepend(QLatin1Char('\''));
         statement = QLatin1String("SELECT SUM(tblk) FROM flights WHERE doft >= ") + start_date;
         break;
     case TimeFrame::Rolling12Months:
         start = QDate::fromJulianDay(QDate::currentDate().toJulianDay() - 365);
-        start_date = start.toString(Qt::ISODate);
+        start_date = QString::number(start.toJulianDay());
         start_date.append(QLatin1Char('\''));
         start_date.prepend(QLatin1Char('\''));
         statement = QLatin1String("SELECT SUM(tblk) FROM flights WHERE doft >= ") + start_date;
         break;
     case TimeFrame::Rolling28Days:
         start = QDate::fromJulianDay(QDate::currentDate().toJulianDay() - 28);
-        start_date = start.toString(Qt::ISODate);
+        start_date = QString::number(start.toJulianDay());
         start_date.append(QLatin1Char('\''));
         start_date.prepend(QLatin1Char('\''));
         statement = QLatin1String("SELECT SUM(tblk) FROM flights WHERE doft >= ") + start_date;
@@ -72,16 +72,17 @@ int OPL::Statistics::totalTime(TimeFrame time_frame)
  */
 QVector<QVariant> OPL::Statistics::countTakeOffLanding(int days)
 {
-    QDate start = QDate::fromJulianDay(QDate::currentDate().toJulianDay() - days);
-    QString startdate = start.toString(Qt::ISODate);
-    startdate.append(QLatin1Char('\''));
-    startdate.prepend(QLatin1Char('\''));
+    QString startDate = QString::number(QDate::fromJulianDay(QDate::currentDate().toJulianDay() - days).toJulianDay());
+
+    // QString startdate = start.toString(Qt::ISODate);
+    startDate.append(QLatin1Char('\''));
+    startDate.prepend(QLatin1Char('\''));
 
     QString statement = QLatin1String("SELECT "
                                       " SUM(IFNULL(flights.toDay,0) + IFNULL(flights.toNight,0)) AS 'TO', "
                                       " SUM(IFNULL(flights.ldgDay,0) + IFNULL(flights.ldgNight,0)) AS 'LDG' "
                                       " FROM flights "
-                                      " WHERE doft >=") + startdate;
+                                      " WHERE doft >=") + startDate;
 
     QVector<QVariant> result = DB->customQuery(statement, 2);
     // make sure a value is returned instead of NULL

+ 13 - 0
src/gui/dialogues/entryeditdialog.cpp

@@ -0,0 +1,13 @@
+#include "entryeditdialog.h"
+
+EntryEditDialog::EntryEditDialog(QWidget *parent)
+    : QDialog{parent}
+{
+    rowID = 0;
+}
+
+EntryEditDialog::EntryEditDialog(int rowID, QWidget *parent)
+    : QDialog{parent}, rowID(rowID)
+{
+
+}

+ 35 - 0
src/gui/dialogues/entryeditdialog.h

@@ -0,0 +1,35 @@
+#ifndef ENTRYEDITDIALOG_H
+#define ENTRYEDITDIALOG_H
+
+#include <QDialog>
+#include <QObject>
+
+/*!
+ * \brief The EntryEditDialog class is a base class for Dialogs that enable editing of individual database entries
+ */
+class EntryEditDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    EntryEditDialog() = delete;
+    EntryEditDialog(QWidget *parent = nullptr);
+    EntryEditDialog(int rowID, QWidget *parent = nullptr);
+
+    /*!
+     * \brief load an entry from the database for editing
+     * \param rowID - The row ID of the entry
+     */
+    virtual void loadEntry(int rowID) = 0;
+
+    /*!
+     * \brief delete an entry from the database
+     * \param rowID - the row ID to be deleted
+     * \return true on success
+     */
+    virtual bool deleteEntry(int rowID) = 0;
+
+protected:
+    int rowID;
+};
+
+#endif // ENTRYEDITDIALOG_H

+ 1 - 1
src/gui/dialogues/exporttocsvdialog.cpp

@@ -56,7 +56,7 @@ void ExportToCsvDialog::selectRows()
     // create a QSqlTableModel based on the selected views
     const auto model = new QSqlTableModel(this);
     if(ui->viewComboBox->currentIndex() < 4)
-        model->setTable(OPL::GLOBALS->getViewIdentifier(OPL::DbViewName(ui->viewComboBox->currentIndex())));
+        model->setTable(OPL::GLOBALS->getViewIdentifier(OPL::LogbookView(ui->viewComboBox->currentIndex())));
     else
         model->setTable(exportView);
     model->select();

+ 42 - 62
src/gui/dialogues/firstrundialog.cpp

@@ -150,7 +150,7 @@ bool FirstRunDialog::finishSetup()
     } // if database file exists
 
     if (!DB->connect()) {
-        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"),
+        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (connect)"),
                                 tr("Errors have ocurred creating the database."
                                    "Without a working database The application will not be usable.<br>"
                                    "The following error has ocurred:<br>"
@@ -160,7 +160,7 @@ bool FirstRunDialog::finishSetup()
     }
 
     if (!setupDatabase()) {
-        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"),
+        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (create schema)"),
                                 tr("Errors have ocurred creating the database."
                                    "Without a working database The application will not be usable.<br>"
                                    "The following error has ocurred:<br>%1"
@@ -170,7 +170,7 @@ bool FirstRunDialog::finishSetup()
     }
 
     if (!createUserEntry()) {
-        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"),
+        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (user entry)"),
                                 tr("Unable to execute database query<br>"
                                    "The following error has occured:<br>%1"
                                    ).arg(DB->lastError.text()));
@@ -179,7 +179,7 @@ bool FirstRunDialog::finishSetup()
     }
 
     if (!setupPreviousExperienceEntry()) {
-        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"),
+        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed (previous Experience)"),
                                 tr("Unable to execute database query<br>"
                                    "The following error has occured:<br>%1"
                                    ).arg(DB->lastError.text()));
@@ -187,14 +187,10 @@ bool FirstRunDialog::finishSetup()
         return false;
     }
 
-    if (!writeCurrencies()) {
-        QMessageBox message_box(QMessageBox::Critical, tr("Database setup failed"),
-                                tr("Unable to execute database query<br>"
-                                   "The following error has occured:<br>%1"
-                                   ).arg(DB->lastError.text()));
-        message_box.exec();
-        return false;
-    }
+    // non-critical error
+    if(!writeCurrencies())
+        LOG << "Error writing currencies during initial setup.";
+
     DB->disconnect(); // Connection will be re-established by MainWindow
     return true;
 }
@@ -274,23 +270,24 @@ bool FirstRunDialog::verifyTemplates()
 void FirstRunDialog::writeSettings()
 {
     Settings::resetToDefaults();
+    Settings::setPilotFunction(OPL::PilotFunction(ui->functionComboBox->currentIndex()));
+    Settings::setApproachType(ui->approachComboBox->currentText());
+    Settings::setNightLoggingEnabled(ui->nightComboBox->currentIndex());
+    Settings::setLogIfr(ui->rulesComboBox->currentIndex());
+    Settings::setFlightNumberPrefix(ui->prefixLineEdit->text());
+    Settings::setLogbookView(OPL::LogbookView(ui->logbookViewComboBox->currentIndex()));
+    Settings::setApplicationStyle(ui->styleComboBox->currentText());
 
-    Settings::write(Settings::FlightLogging::Function, ui->functionComboBox->currentIndex());
-    Settings::write(Settings::FlightLogging::Approach, ui->approachComboBox->currentIndex());
-    Settings::write(Settings::FlightLogging::NightLoggingEnabled, ui->nightComboBox->currentIndex());
     switch (ui->nightRulesComboBox->currentIndex()) {
     case 0:
-        Settings::write(Settings::FlightLogging::NightAngle, -6);
+        Settings::setNightAngle(-6);
         break;
     case 1:
-        Settings::write(Settings::FlightLogging::NightAngle, 0);
+        Settings::setNightAngle(0);
         break;
     }
-    Settings::write(Settings::FlightLogging::LogIFR, ui->rulesComboBox->currentIndex());
-    Settings::write(Settings::FlightLogging::FlightNumberPrefix, ui->prefixLineEdit->text());
-    Settings::write(Settings::UserData::DisplaySelfAs, ui->aliasComboBox->currentIndex());
-    Settings::write(Settings::Main::LogbookView, ui->logbookViewComboBox->currentIndex());
-    Settings::write(Settings::Main::Style, ui->styleComboBox->currentText());
+
+    Settings::setShowSelfAs(ui->aliasComboBox->currentIndex());
     Settings::sync();
 }
 
@@ -352,39 +349,32 @@ bool FirstRunDialog::setupPreviousExperienceEntry()
 
 bool FirstRunDialog::writeCurrencies()
 {
-    const QMap<OPL::CurrencyName, QDateEdit*> currencies_list = {
-        {OPL::CurrencyName::Licence,    ui->currLicDateEdit},
-        {OPL::CurrencyName::TypeRating, ui->currTrDateEdit},
-        {OPL::CurrencyName::LineCheck,  ui->currLckDateEdit},
-        {OPL::CurrencyName::Medical,    ui->currMedDateEdit},
-        {OPL::CurrencyName::Custom1,    ui->currCustom1DateEdit},
-        {OPL::CurrencyName::Custom2,    ui->currCustom2DateEdit},
-    };
-    const QMap<OPL::CurrencyName, Settings::UserData> settings_list = {
-        {OPL::CurrencyName::Licence,    Settings::UserData::ShowLicCurrency },
-        {OPL::CurrencyName::TypeRating, Settings::UserData::ShowTrCurrency },
-        {OPL::CurrencyName::LineCheck,  Settings::UserData::ShowLckCurrency },
-        {OPL::CurrencyName::Medical,    Settings::UserData::ShowMedCurrency },
-        {OPL::CurrencyName::Custom1,    Settings::UserData::ShowCustom1Currency },
-        {OPL::CurrencyName::Custom2,    Settings::UserData::ShowCustom2Currency },
+    const QList<QPair<QString, QDateEdit*>> currencies = {
+        { ui->currLicLabel->text(), 		ui->currLicDateEdit },
+        { ui->currTrLabel->text(), 			ui->currTrDateEdit },
+        { ui->currLckLabel->text(), 		ui->currLckDateEdit },
+        { ui->currMedLabel->text(), 		ui->currMedDateEdit },
+        { ui->currCustom1LineEdit->text(), 	ui->currCustom1DateEdit },
+        { ui->currCustom2LineEdit->text(), 	ui->currCustom2DateEdit },
     };
 
-    QDate today = QDate::currentDate();
-    for (const auto &date_edit : currencies_list) {
-        const auto enum_value = currencies_list.key(date_edit);
-        // only write dates that have been edited
-        if (date_edit->date() != today) {
-            OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date_edit->date().toString(Qt::ISODate)}};
-            if (enum_value == OPL::CurrencyName::Custom1)
-                row_data.insert(OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom1LineEdit->text());
-            else if(enum_value == OPL::CurrencyName::Custom2)
-                row_data.insert(OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom2LineEdit->text());
-
-            Settings::write(settings_list.value(enum_value), true); // Show selected currency on Home Screen
-            OPL::CurrencyEntry entry(static_cast<int>(enum_value), row_data);
-            if (!DB->commit(entry))
-                return false;
+    const QDate today = QDate::currentDate();
+
+    for(const auto &pair : currencies) {
+        // list 0-indexed, db row indexes start at 1
+        OPL::CurrencyEntry currencyEntry = OPL::CurrencyEntry(currencies.indexOf(pair) + 1, OPL::RowData_T());
+
+        currencyEntry.setName(pair.first);
+
+        // only set expiry date if user has modified it
+        const QDate date = pair.second->date();
+        if(date != today) {
+            int julianDay = date.toJulianDay();
+            currencyEntry.setExpiryDate(OPL::Date(julianDay, m_format));
         }
+
+        if(!DB->commit(currencyEntry))
+            return false;
     }
     return true;
 }
@@ -434,16 +424,6 @@ void FirstRunDialog::on_styleComboBox_currentTextChanged(const QString &new_styl
     }
 }
 
-void FirstRunDialog::on_currCustom1LineEdit_editingFinished()
-{
-    Settings::write(Settings::UserData::Custom1CurrencyName, ui->currCustom1LineEdit->text());
-}
-
-void FirstRunDialog::on_currCustom2LineEdit_editingFinished()
-{
-    Settings::write(Settings::UserData::Custom2CurrencyName, ui->currCustom2LineEdit->text());
-}
-
 void FirstRunDialog::on_importPushButton_clicked()
 {
     QString filename = QDir::toNativeSeparators(QFileDialog::getOpenFileName(

+ 9 - 2
src/gui/dialogues/firstrundialog.h

@@ -18,6 +18,7 @@
 #ifndef FIRSTRUNDIALOG_H
 #define FIRSTRUNDIALOG_H
 
+#include "src/database/currencyentry.h"
 #include <QDialog>
 #include <QButtonGroup>
 #include <QMessageBox>
@@ -85,8 +86,6 @@ private slots:
     void on_previousPushButton_clicked();
     void on_nextPushButton_clicked();
     void on_styleComboBox_currentTextChanged(const QString &new_style_setting);
-    void on_currCustom1LineEdit_editingFinished();
-    void on_currCustom2LineEdit_editingFinished();
 
     /*!
      * \brief Import an existing database instead of creating a new one
@@ -97,6 +96,14 @@ private:
     Ui::FirstRunDialog *ui;
     bool useRessourceData;
 
+    //TODO load from settings
+    OPL::DateTimeFormat m_format = OPL::DateTimeFormat(
+        OPL::DateTimeFormat::DateFormat::Default,
+        QStringLiteral("yyyy-MM-dd"),
+        OPL::DateTimeFormat::TimeFormat::Default,
+        QStringLiteral("hh:mm")
+        );
+
     /*!
      * \brief finishSetup - once all the necessary data is entered by the user, this functions executes the steps necessary
      * to collect the data, process it and create the database

+ 1 - 1
src/gui/dialogues/firstrundialog.ui

@@ -175,7 +175,7 @@
        <item row="0" column="0" colspan="2">
         <widget class="QLabel" name="label_6">
          <property name="text">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:20pt;&quot;&gt;Welcome to openPilotLog!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt;&quot;&gt;The Free and Open Source Logbook application&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If you would like to keep track of your license and/or medical expiration dates, you can enter them here. By default, only Take-Off and Landing currency is shown on the home page, but any other currency can also be shown as well. This can be enabled in the settings menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:20pt;&quot;&gt;Welcome to openPilotLog!&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:14pt;&quot;&gt;The Free and Open Source Logbook application&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;If you would like to keep track of your license and/or medical expiration dates, you can enter them here. Your currencies are tracked and you will be warned about impending expiries. These warnings can be disabled in the Settings Menu.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
          </property>
          <property name="textFormat">
           <enum>Qt::RichText</enum>

+ 18 - 6
src/gui/dialogues/newairportdialog.cpp

@@ -8,19 +8,18 @@
 #include "src/database/row.h"
 
 NewAirportDialog::NewAirportDialog(QWidget *parent) :
-    QDialog(parent), ui(new Ui::NewAirportDialog)
+    EntryEditDialog(parent), ui(new Ui::NewAirportDialog)
 {
-    rowId = 0; // new entry
+    m_rowId = 0; // new entry
     ui->setupUi(this);
     setValidators();
     loadTimeZones();
 }
 
 NewAirportDialog::NewAirportDialog(int row_id, QWidget *parent)
-    : QDialog(parent), ui(new Ui::NewAirportDialog), rowId(row_id)
+    : EntryEditDialog(parent), ui(new Ui::NewAirportDialog), m_rowId(row_id)
 {
     ui->setupUi(this);
-    this->setWindowTitle(tr("Edit Airport"));
     setValidators();
     loadTimeZones();
     loadAirportData(row_id);
@@ -47,8 +46,8 @@ void NewAirportDialog::loadTimeZones()
 
 void NewAirportDialog::loadAirportData(int row_id)
 {
+    this->setWindowTitle(tr("Edit Airport"));
     const auto airport_data = DB->getAirportEntry(row_id).getData();
-    //const auto airport_data = airport.getData();
     DEB << "Filling Airport Data: " << airport_data;
 
     ui->nameLineEdit->setText(airport_data.value(OPL::AirportEntry::NAME).toString());
@@ -118,7 +117,7 @@ void NewAirportDialog::on_buttonBox_accepted()
         {OPL::AirportEntry::COUNTRY,  ui->countryLineEdit->text()},
     };
 
-    OPL::AirportEntry entry(rowId, airport_data);
+    OPL::AirportEntry entry(m_rowId, airport_data);
     if(DB->commit(entry))
         QDialog::accept();
     else {
@@ -142,3 +141,16 @@ void NewAirportDialog::on_buttonBox_rejected()
     QDialog::reject();
 }
 
+// EntryEditDialog interface
+void NewAirportDialog::loadEntry(int rowId)
+{
+    m_rowId = rowId;
+    loadAirportData(rowId);
+}
+
+bool NewAirportDialog::deleteEntry(int rowId)
+{
+    auto entry = DB->getAirportEntry(rowId);
+    return DB->remove(entry);
+}
+

+ 11 - 3
src/gui/dialogues/newairportdialog.h

@@ -1,13 +1,13 @@
 #ifndef NEWAIRPORTDIALOG_H
 #define NEWAIRPORTDIALOG_H
 
-#include <QDialog>
+#include "src/gui/dialogues/entryeditdialog.h"
 
 namespace Ui {
 class NewAirportDialog;
 }
 
-class NewAirportDialog : public QDialog
+class NewAirportDialog : public EntryEditDialog
 {
     Q_OBJECT
 
@@ -17,6 +17,7 @@ public:
     ~NewAirportDialog();
 
 
+
 private slots:
     void on_buttonBox_accepted();
 
@@ -28,12 +29,19 @@ private slots:
 
 private:
     Ui::NewAirportDialog *ui;
+    int m_rowId;
+
     void setValidators();
     void loadTimeZones();
     bool confirmTimezone();
     void loadAirportData(int row_id);
     bool verifyInput();
-    int rowId;
+
+
+    // EntryEditDialog interface
+public:
+    virtual void loadEntry(int rowId) override;
+    virtual bool deleteEntry(int rowId) override;
 };
 
 #endif // NEWAIRPORTDIALOG_H

+ 289 - 148
src/gui/dialogues/newflightdialog.cpp

@@ -16,9 +16,11 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "newflightdialog.h"
+#include "QtWidgets/qcalendarwidget.h"
 #include "src/classes/time.h"
 #include "src/database/database.h"
 #include "src/database/databasecache.h"
+#include "src/gui/dialogues/newairportdialog.h"
 #include "src/gui/verification/airportinput.h"
 #include "src/gui/verification/completerprovider.h"
 #include "src/gui/verification/pilotinput.h"
@@ -28,6 +30,7 @@
 #include "src/opl.h"
 #include "src/functions/datetime.h"
 #include "src/classes/settings.h"
+#include "src/classes/date.h"
 #include "src/functions/calc.h"
 #include "src/gui/dialogues/newtaildialog.h"
 #include "src/gui/dialogues/newpilotdialog.h"
@@ -37,28 +40,18 @@
 
 
 NewFlightDialog::NewFlightDialog(QWidget *parent)
-    : QDialog(parent),
+    : EntryEditDialog(parent),
       ui(new Ui::NewFlightDialog)
 {
     init();
-    // Set up UI (New Flight)
-    LOG << Settings::read(Settings::FlightLogging::Function);
-    if(Settings::read(Settings::FlightLogging::Function).toInt() == static_cast<int>(OPL::PilotFunction::PIC)){
-        ui->picNameLineEdit->setText(self);
-        ui->functionComboBox->setCurrentIndex(0);
-        emit ui->picNameLineEdit->editingFinished();
-    }
-    if (Settings::read(Settings::FlightLogging::Function).toInt() == static_cast<int>(OPL::PilotFunction::SIC)) {
-        ui->sicNameLineEdit->setText(self);
-        ui->functionComboBox->setCurrentIndex(2);
-        emit ui->sicNameLineEdit->editingFinished();
-    }
-    ui->doftLineEdit->setText(OPL::DateTime::currentDate());
-    emit ui->doftLineEdit->editingFinished();
+    setPilotFunction();
+
+    ui->doftLineEdit->setText(OPL::Date::today(m_format).toString());
+//    emit ui->doftLineEdit->editingFinished();
 }
 
 NewFlightDialog::NewFlightDialog(int row_id, QWidget *parent)
-    : QDialog(parent),
+    : EntryEditDialog(parent),
       ui(new Ui::NewFlightDialog)
 {
     init();
@@ -71,6 +64,20 @@ NewFlightDialog::~NewFlightDialog()
     delete ui;
 }
 
+void NewFlightDialog::setPilotFunction()
+{
+    const QString &self = DBCache->getPilotNamesMap().value(1);
+    if(Settings::getPilotFunction() == OPL::PilotFunction::PIC){
+        ui->picNameLineEdit->setText(self);
+        ui->functionComboBox->setCurrentIndex(0);
+    }
+    if (Settings::getPilotFunction() == OPL::PilotFunction::SIC) {
+        ui->sicNameLineEdit->setText(self);
+        ui->functionComboBox->setCurrentIndex(2);
+    }
+    ui->pilotFlyingCheckBox->setCheckState(Qt::Checked);
+}
+
 void NewFlightDialog::init()
 {
     // Setup UI
@@ -94,6 +101,11 @@ void NewFlightDialog::init()
     OPL::GLOBALS->loadApproachTypes(ui->approachComboBox);
     OPL::GLOBALS->loadPilotFunctios(ui->functionComboBox);
 
+    // allocate a widget for date selection
+    calendar = new QCalendarWidget(this);
+    calendar->setVisible(false);
+    calendar->setWindowFlags(Qt::Dialog); // pop-up calendar
+
     setupRawInputValidation();
     setupSignalsAndSlots();
     readSettings();
@@ -140,6 +152,9 @@ void NewFlightDialog::setupSignalsAndSlots()
     for (const auto& line_edit : *pilotNameLineEdits)
         QObject::connect(line_edit, &QLineEdit::editingFinished,
                          this, &NewFlightDialog::onPilotNameLineEdit_editingFinshed);
+
+    QObject::connect(calendar, &QCalendarWidget::selectionChanged,
+                     this, &NewFlightDialog::calendarDateSelected);
 }
 
 /*!
@@ -167,10 +182,12 @@ bool NewFlightDialog::eventFilter(QObject *object, QEvent *event)
  */
 void NewFlightDialog::readSettings()
 {
-    ui->functionComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Function).toInt());
-    ui->approachComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Approach).toInt());
-    ui->flightRulesComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::LogIFR).toInt());
-    ui->flightNumberLineEdit->setText(Settings::read(Settings::FlightLogging::FlightNumberPrefix).toString());
+    ui->functionComboBox->setCurrentIndex(static_cast<int>(Settings::getPilotFunction()));
+    ui->approachComboBox->setCurrentText(Settings::getApproachType());
+    ui->flightRulesComboBox->setCurrentIndex(Settings::getLogIfr());
+    ui->flightNumberLineEdit->setText(Settings::getFlightNumberPrefix());
+
+    m_format = Settings::getDisplayFormat();
 }
 
 /*!
@@ -181,55 +198,54 @@ void NewFlightDialog::fillWithEntryData()
 {
     DEB << "Restoring Flight: ";
     DEB << flightEntry;
+    using namespace OPL;
 
     const auto &flight_data = flightEntry.getData();
 
     // Date of Flight
-    ui->doftLineEdit->setText(flight_data.value(OPL::FlightEntry::DOFT).toString());
+    const QDate date = QDate::fromJulianDay(flight_data.value(FlightEntry::DOFT).toInt());
+    calendar->setSelectedDate(date);
+    ui->doftLineEdit->setText(Date(date, m_format).toString());
+
     // Location
     ui->deptLocationLineEdit->setText(flight_data.value(OPL::FlightEntry::DEPT).toString());
     ui->destLocationLineEdit->setText(flight_data.value(OPL::FlightEntry::DEST).toString());
     // Times
-    ui->tofbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TOFB).toInt()).toString());
-    ui->tonbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TONB).toInt()).toString());
+    ui->tofbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TOFB).toInt(), m_format).toString());
+    ui->tonbTimeLineEdit->setText(OPL::Time(flight_data.value(OPL::FlightEntry::TONB).toInt(), m_format).toString());
     ui->acftLineEdit->setText(DBCache->getTailsMap().value(flight_data.value(OPL::FlightEntry::ACFT).toInt()));
     ui->picNameLineEdit->setText(DBCache->getPilotNamesMap().value(flight_data.value(OPL::FlightEntry::PIC).toInt()));
     ui->sicNameLineEdit->setText(DBCache->getPilotNamesMap().value(flight_data.value(OPL::FlightEntry::SECONDPILOT).toInt()));
     ui->thirdPilotNameLineEdit->setText(DBCache->getPilotNamesMap().value(flight_data.value(OPL::FlightEntry::THIRDPILOT).toInt()));
 
     //Function
-    const QHash<int, QString> functions = {
-        {0, OPL::FlightEntry::TPIC},
-        {1, OPL::FlightEntry::TPICUS},
-        {2, OPL::FlightEntry::TSIC},
-        {3, OPL::FlightEntry::TDUAL},
-        {4, OPL::FlightEntry::TFI},
-    };
     for (int i = 0; i < 5; i++) { // QHash::iterator not guarenteed to be in ordetr
-        if(flight_data.value(functions.value(i)).toInt() != 0)
+        if(flight_data.value(pilotFuncionsMap.value(i)).toInt() != 0)
             ui->functionComboBox->setCurrentIndex(i);
     }
     // Approach ComboBox
-    const QString& app = flight_data.value(OPL::FlightEntry::APPROACHTYPE).toString();
-    if(app != QString()){
+    const QString app = flight_data.value(OPL::FlightEntry::APPROACHTYPE).toString();
+    if(app != QString()) {
         ui->approachComboBox->setCurrentText(app);
     }
-    // Flight Rules
+    // Flight Rules, check if tIFR > 0
     bool time_ifr = flight_data.value(OPL::FlightEntry::TIFR).toBool();
-    int rulesIndex = time_ifr ? 1 : 0;
-    ui->flightRulesComboBox->setCurrentIndex(rulesIndex);
+    ui->flightRulesComboBox->setCurrentIndex(time_ifr);
+
     // Take-Off and Landing
-    int takeOffCount =  flight_data.value(OPL::FlightEntry::TODAY).toInt()
+    int takeOffCount = flight_data.value(OPL::FlightEntry::TODAY).toInt()
             + flight_data.value(OPL::FlightEntry::TONIGHT).toInt();
     int landingCount = flight_data.value(OPL::FlightEntry::LDGDAY).toInt()
             + flight_data.value(OPL::FlightEntry::LDGNIGHT).toInt();
     ui->takeOffSpinBox->setValue(takeOffCount);
     ui->landingSpinBox->setValue(landingCount);
-    // Remarks
+    ui->pilotFlyingCheckBox->setChecked(flight_data.value(OPL::FlightEntry::PILOTFLYING).toBool());
+
+    // Remarks and Flight Number
     ui->remarksLineEdit->setText(flight_data.value(OPL::FlightEntry::REMARKS).toString());
-    // Flight Number
     ui->flightNumberLineEdit->setText(flight_data.value(OPL::FlightEntry::FLIGHTNUMBER).toString());
 
+    // re-trigger input verification
     for(const auto &line_edit : *mandatoryLineEdits)
         emit line_edit->editingFinished();
 }
@@ -262,7 +278,10 @@ void NewFlightDialog::onGoodInputReceived(QLineEdit *line_edit)
         validationState.validate(mandatoryLineEdits->indexOf(line_edit));
 
     if (validationState.timesValid()) {
-        updateBlockTimeLabel();
+        const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text(), m_format);
+        const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text(), m_format);
+        const OPL::Time tblk = OPL::Time::blockTime(tofb, tonb);
+        ui->tblkDisplayLabel->setText(tblk.toString());
     }
 }
 
@@ -278,81 +297,90 @@ void NewFlightDialog::onBadInputReceived(QLineEdit *line_edit)
     validationState.printValidationStatus();
 }
 
-void NewFlightDialog::updateBlockTimeLabel()
+bool NewFlightDialog::addNewDatabaseElement(QLineEdit *parent, OPL::DbTable table)
 {
-    const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text());
-    const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text());
-    const OPL::Time tblk = OPL::Time::blockTime(tofb, tonb);
-
-    ui->tblkDisplayLabel->setText(tblk.toString());
-}
+    QDialog *dialog = nullptr;
+    if(userWantsToAddNewEntry(table)) {
+        switch (table) {
+        case OPL::DbTable::Pilots:
+            dialog = new NewPilotDialog(parent->text(), this);
+            break;
+        case OPL::DbTable::Tails:
+            dialog = new NewTailDialog(ui->acftLineEdit->text(), this);
+            break;
+        case OPL::DbTable::Airports:
+            dialog = new NewAirportDialog(this);
+            break;
+        default:
+            return false;
+            break;
+        }
+    } else
+        return false;
 
-/*!
- * \brief NewFlightDialog::addNewTail If the user input is not in the aircraftList, the user
- * is prompted if he wants to add a new entry to the database
- */
-bool NewFlightDialog::addNewTail(QLineEdit& parent_line_edit)
-{
-    QMessageBox::StandardButton reply;
-    reply = QMessageBox::question(this, tr("No Aircraft found"),
-                                  tr("No aircraft with this registration found.<br>"
-                                     "If this is the first time you log a flight with this aircraft, "
-                                     "you have to add the registration to the database first."
-                                     "<br><br>Would you like to add a new aircraft to the database?"),
-                                  QMessageBox::Yes|QMessageBox::No);
-    if (reply == QMessageBox::Yes) {
-        // create and open new aircraft dialog
-        NewTailDialog nt(ui->acftLineEdit->text(), this);
-        int ret = nt.exec();
-        // update map and list, set line edit
-        if (ret == QDialog::Accepted) {
-            DEB << "New Tail Entry added:";
-            DEB << DB->getTailEntry(DB->getLastEntry(OPL::DbTable::Tails));
-
-            // update Line Edit with newly added tail
-            parent_line_edit.setText(DBCache->getTailsMap().value(DB->getLastEntry(OPL::DbTable::Tails)));
-            emit parent_line_edit.editingFinished();
-            return true;
-        } else {
+    // execute the dialog and check for success. Set the line edit to the newly created entry.
+    if(dialog->exec() == QDialog::Accepted) {
+        delete dialog;
+        int latestEntry = DB->getLastEntry(table);
+        switch (table) {
+        case OPL::DbTable::Pilots:
+            parent->setText(DBCache->getPilotNamesMap().value(latestEntry));
+            break;
+        case OPL::DbTable::Tails:
+            parent->setText(DBCache->getTailsMap().value(latestEntry));
+            break;
+        case OPL::DbTable::Airports:
+            parent->setText(DBCache->getAirportsMapICAO().value(latestEntry));
+            break;
+        default:
             return false;
+            break;
         }
     } else {
+        delete dialog;
         return false;
     }
+
+    // re-emit editing finished to trigger input validation
+    emit parent->editingFinished();
+    return true;
 }
 
-/*!
- * \brief NewFlightDialog::addNewPilot If the user input is not in the pilotNameList, the user
- * is prompted if he wants to add a new entry to the database
- */
-bool NewFlightDialog::addNewPilot(QLineEdit& parent_line_edit)
+bool NewFlightDialog::userWantsToAddNewEntry(OPL::DbTable table)
 {
     QMessageBox::StandardButton reply;
-    reply = QMessageBox::question(this, tr("No Pilot found"),
-                                  tr("No pilot found.<br>Please enter the Name as"
-                                     "<br><br><center><b>Lastname, Firstname</b></center><br><br>"
-                                     "If this is the first time you log a flight with this pilot, "
-                                     "you have to add the pilot to the database first."
-                                     "<br><br>Would you like to add a new pilot to the database?"),
-                                  QMessageBox::Yes|QMessageBox::No);
-    if (reply == QMessageBox::Yes) {
-        // create and open new pilot dialog
-        NewPilotDialog np(this);
-        int ret = np.exec();
-        // update map and list, set line edit
-        if (ret == QDialog::Accepted) {
-            DEB << "New Pilot Entry added:";
-            DEB << DB->getPilotEntry(DB->getLastEntry(OPL::DbTable::Pilots));
-
-            // update Line Edit with newly added pilot
-            parent_line_edit.setText(DBCache->getPilotNamesMap().value(DB->getLastEntry(OPL::DbTable::Pilots)));
-            emit parent_line_edit.editingFinished();
-            return true;
-        } else {
-            return false;
-        }
-    } else
-        return false;
+    switch (table) {
+    case OPL::DbTable::Pilots:
+        reply = QMessageBox::question(this, tr("No Pilot found"),
+                                     tr("No pilot found.<br>Please enter the Name as"
+                                        "<br><br><center><b>Lastname, Firstname</b></center><br><br>"
+                                        "If this is the first time you log a flight with this pilot, "
+                                        "you have to add the pilot to the database first."
+                                        "<br><br>Would you like to add a new pilot to the database?"),
+                                      QMessageBox::Yes|QMessageBox::No, QMessageBox::StandardButton::Yes);
+        break;
+    case OPL::DbTable::Tails:
+        reply = QMessageBox::question(this, tr("No Aircraft found"),
+                                      tr("No aircraft with this registration found.<br>"
+                                         "If this is the first time you log a flight with this aircraft, "
+                                         "you have to add the registration to the database first."
+                                         "<br><br>Would you like to add a new aircraft to the database?"),
+                                      QMessageBox::Yes|QMessageBox::No, QMessageBox::StandardButton::Yes);
+        break;
+    case OPL::DbTable::Airports:
+        reply = QMessageBox::question(this, tr("No Airport found"),
+                                      tr("No Airport with this identifier found.<br>"
+                                         "If this is the first time you log a flight to this airport, "
+                                         "you have to add the airport to the database first."
+                                         "<br><br>Would you like to add a new airport to the database?"),
+                                      QMessageBox::Yes|QMessageBox::No, QMessageBox::StandardButton::Yes);
+        break;
+    default:
+        reply = QMessageBox::No;
+        break;
+    }
+
+    return reply == QMessageBox::Yes;
 }
 
 /*!
@@ -369,15 +397,15 @@ OPL::RowData_T NewFlightDialog::prepareFlightEntryData()
     OPL::RowData_T new_data;
 
     // Calculate Block and Night Time
-    const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text());
-    const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text());
+    const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text(), m_format);
+    const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text(), m_format);
     const int block_minutes = OPL::Time::blockMinutes(tofb, tonb);
 
-    QDateTime departure_date_time = OPL::DateTime::fromString(ui->doftLineEdit->text() + ui->tofbTimeLineEdit->text());
+    const QDateTime departure_date_time = OPL::DateTime::fromString(ui->doftLineEdit->text() + ui->tofbTimeLineEdit->text());
     const auto night_time_data = OPL::Calc::NightTimeValues(ui->deptLocationLineEdit->text(), ui->destLocationLineEdit->text(),
-                           departure_date_time, block_minutes, Settings::read(Settings::FlightLogging::NightAngle).toInt());
+                           departure_date_time, block_minutes, Settings::getNightAngle());
     // Mandatory data
-    new_data.insert(OPL::FlightEntry::DOFT, ui->doftLineEdit->text());
+    new_data.insert(OPL::FlightEntry::DOFT, QDate::fromString(ui->doftLineEdit->text(), Qt::ISODate).toJulianDay());
     new_data.insert(OPL::FlightEntry::DEPT, ui->deptLocationLineEdit->text());
     new_data.insert(OPL::FlightEntry::TOFB, tofb.toMinutes());
     new_data.insert(OPL::FlightEntry::DEST, ui->destLocationLineEdit->text());
@@ -461,7 +489,7 @@ OPL::RowData_T NewFlightDialog::prepareFlightEntryData()
         new_data.insert(OPL::FlightEntry::AUTOLAND, ui->landingSpinBox->value());
 
     // Pilot flying / Pilot monitoring
-    bool isPilotFlying = toDay + toNight + ldgDay + ldgNight > 0;
+    bool isPilotFlying = toDay + toNight + ldgDay + ldgNight;
     new_data.insert(OPL::FlightEntry::PILOTFLYING, isPilotFlying);
 
     // Additional Data
@@ -485,15 +513,28 @@ void NewFlightDialog::toUpper(const QString &text)
 
 void NewFlightDialog::onTimeLineEdit_editingFinished()
 {
-    auto line_edit = this->findChild<QLineEdit*>(sender()->objectName());
-    verifyUserInput(line_edit, TimeInput(line_edit->text()));
+    const auto line_edit = this->findChild<QLineEdit*>(sender()->objectName());
+
+    if(!verifyUserInput(line_edit, TimeInput(line_edit->text(), m_format))) {
+        onBadInputReceived(line_edit);
+    }
 }
 
 void NewFlightDialog::onPilotNameLineEdit_editingFinshed()
 {
     auto line_edit = this->findChild<QLineEdit*>(sender()->objectName());
+    if(line_edit->text() == QString())
+        return;
+
+    // create a copy to refill line edit and pass through to creation dialog if verification fails
+    QString userInput = line_edit->text();
+
     if(!verifyUserInput(line_edit, PilotInput(line_edit->text()))) {
-        if(!addNewPilot(*line_edit))
+        {
+            QSignalBlocker blocker(line_edit);
+            line_edit->setText(userInput);
+        }
+        if(!addNewDatabaseElement(line_edit, OPL::DbTable::Pilots))
             onBadInputReceived(line_edit);
     }
 }
@@ -514,17 +555,31 @@ void NewFlightDialog::onLocationLineEdit_editingFinished()
         name_label->setText(DBCache->getAirportsMapNames().value(
                                 DBCache->getAirportsMapICAO().key(
                                     line_edit->text())));
-    } else
+    } else {
         name_label->setText("Unknown Airport");
+        addNewDatabaseElement(line_edit, OPL::DbTable::Airports);
+    }
 }
 
 void NewFlightDialog::on_acftLineEdit_editingFinished()
 {
     const auto line_edit = ui->acftLineEdit;
+    if(line_edit->text().isEmpty()){
+        return;
+    }
+
+    const QString user_input = line_edit->text(); // keep around for adding new tail if needed
 
-    if(!verifyUserInput(line_edit, TailInput(line_edit->text())))
-        if(!addNewTail(*line_edit))
+    if(!verifyUserInput(line_edit, TailInput(line_edit->text()))) {
+        // re-populate user input to hand through to NewTailDialog
+        {
+            QSignalBlocker blocker(line_edit);
+            line_edit->setText(user_input);
+        }
+        if(!addNewDatabaseElement(line_edit, OPL::DbTable::Tails)) {
             onBadInputReceived(line_edit);
+        }
+    }
 
     const auto space = QLatin1Char(' ');
     if(line_edit->text().contains(space))
@@ -534,21 +589,19 @@ void NewFlightDialog::on_acftLineEdit_editingFinished()
 void NewFlightDialog::on_doftLineEdit_editingFinished()
 {
     const auto line_edit = ui->doftLineEdit;
-    auto text = ui->doftLineEdit->text();
-    auto label = ui->doftDisplayLabel;
-
-    TODO << "Non-default Date formats not implemented yet.";
-    OPL::DateFormat date_format = OPL::DateFormat::ISODate;
-    auto date = OPL::DateTime::parseInput(text, date_format);
-    if (date.isValid()) {
-        label->setText(date.toString(Qt::TextDate));
-        line_edit->setText(OPL::DateTime::dateToString(date, date_format));
-        onGoodInputReceived(line_edit);
+    OPL::Date date(ui->doftLineEdit->text(), m_format);
+
+
+    LOG << "Date: " << date.toString();
+    LOG << "is valid? " << date.isValid();
+
+    line_edit->setText(date.toString());
+    if(ui->doftLineEdit->text().isEmpty()) {
+        onBadInputReceived(line_edit);
         return;
     }
 
-    label->setText(tr("Invalid Date."));
-    onBadInputReceived(line_edit);
+    onGoodInputReceived(line_edit);
 }
 
 void NewFlightDialog::on_pilotFlyingCheckBox_stateChanged(int arg1)
@@ -577,18 +630,45 @@ void NewFlightDialog::on_approachComboBox_currentTextChanged(const QString &arg1
  */
 void NewFlightDialog::on_functionComboBox_currentIndexChanged(int index)
 {
-    if (index == static_cast<int>(OPL::PilotFunction::PIC)) {
+    int picPilotId = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text());
+    int sicPilotId = DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text());
+    int thirdPilotId = DBCache->getPilotNamesMap().key(ui->thirdPilotNameLineEdit->text());
+    const QString &self = DBCache->getPilotNamesMap().value(1);
+
+    switch (index) {
+    case static_cast<int>(OPL::PilotFunction::PIC):
+        DEB << "PIC selected...";
         ui->picNameLineEdit->setText(self);
         emit ui->picNameLineEdit->editingFinished();
-        if (DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text())
-         == DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text()))
-                ui->sicNameLineEdit->setText(QString());
-    } else if (index == static_cast<int>(OPL::PilotFunction::SIC)) {
+        picPilotId = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text());
+
+        if (picPilotId == sicPilotId) {
+            ui->sicNameLineEdit->setText(QString());
+            onBadInputReceived(ui->sicNameLineEdit);
+        }
+        if (picPilotId == thirdPilotId) {
+            ui->thirdPilotNameLineEdit->setText(QString());
+            onBadInputReceived(ui->thirdPilotNameLineEdit);
+        }
+        break;
+    case static_cast<int>(OPL::PilotFunction::SIC):
+        DEB << "SIC selected...";
         ui->sicNameLineEdit->setText(self);
         emit ui->sicNameLineEdit->editingFinished();
-        if (DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text())
-         == DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text()))
+        sicPilotId = DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text());
+
+        if (sicPilotId == picPilotId) {
             ui->picNameLineEdit->setText(QString());
+            onBadInputReceived(ui->picNameLineEdit);
+        }
+        if (sicPilotId == thirdPilotId) {
+            ui->thirdPilotNameLineEdit->setText(QString());
+            onBadInputReceived(ui->thirdPilotNameLineEdit);
+        }
+        break;
+    default:
+        break;
+
     }
 }
 
@@ -601,7 +681,7 @@ void NewFlightDialog::on_functionComboBox_currentIndexChanged(int index)
  * \param error_msg - the error string displayed to the user
  * \return
  */
-bool NewFlightDialog::checkPilotFunctionsValid()
+bool NewFlightDialog::pilotFunctionsInvalid()
 {
     int pic_id = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text());
     int function_index = ui->functionComboBox->currentIndex();
@@ -610,17 +690,52 @@ bool NewFlightDialog::checkPilotFunctionsValid()
         if (!(function_index == static_cast<int>(OPL::PilotFunction::PIC) || function_index == static_cast<int>(OPL::PilotFunction::FI))) {
             INFO(tr("The PIC is set to %1 but the Pilot Function is set to %2")
                     .arg(ui->picNameLineEdit->text(), ui->functionComboBox->currentText()));
-            return false;
+            return true;
         }
     } else {
         if (function_index == static_cast<int>(OPL::PilotFunction::PIC) || function_index == static_cast<int>(OPL::PilotFunction::FI)) {
             INFO(tr("The Pilot Function is set to %1, but the PIC is set to %2")
                     .arg(ui->functionComboBox->currentText(), ui->picNameLineEdit->text()));
-            return false;
+            return true;
         }
     }
 
-    return true;
+    return false;
+}
+
+bool NewFlightDialog::duplicateNamesPresent()
+{
+    const int picId = DBCache->getPilotNamesMap().key(ui->picNameLineEdit->text());
+    const int sicId = DBCache->getPilotNamesMap().key(ui->sicNameLineEdit->text());
+    const int thirdPilotId = DBCache->getPilotNamesMap().key(ui->thirdPilotNameLineEdit->text());
+
+    // this is a bit explicit but better point out to the user what the case is
+    if (picId == sicId) {
+        INFO(tr("PIC and SIC names are the same."));
+        return true;
+    }
+    if (picId == thirdPilotId && picId > 0) {
+        INFO(tr("PIC and third Pilot names are the same."));
+        return true;
+    }
+    if (sicId == thirdPilotId && sicId > 0) {
+        INFO(tr("SIC and third Pilot names are the same."));
+        return true;
+    }
+
+    return false;
+}
+
+bool NewFlightDialog::flightTimeIsZero()
+{
+    const OPL::Time tofb = OPL::Time::fromString(ui->tofbTimeLineEdit->text(), m_format);
+    const OPL::Time tonb = OPL::Time::fromString(ui->tonbTimeLineEdit->text(), m_format);
+    const int block_minutes = OPL::Time::blockMinutes(tofb, tonb);
+    if(block_minutes == 0) {
+        INFO(tr("Total time of flight is zero."));
+        return true;
+    }
+    return false;
 }
 
 /*!
@@ -638,17 +753,12 @@ void NewFlightDialog::on_buttonBox_accepted()
         emit le->editingFinished();
     // If input verification is passed, continue, otherwise prompt user to correct
     if (!validationState.allValid()) {
-        const auto display_names = QHash<ValidationState::ValidationItem, QString> {
-            {ValidationState::ValidationItem::doft, QObject::tr("Date of Flight")},      {ValidationState::ValidationItem::dept, QObject::tr("Departure Airport")},
-            {ValidationState::ValidationItem::dest, QObject::tr("Destination Airport")}, {ValidationState::ValidationItem::tofb, QObject::tr("Time Off Blocks")},
-            {ValidationState::ValidationItem::tonb, QObject::tr("Time on Blocks")},      {ValidationState::ValidationItem::pic, QObject::tr("PIC Name")},
-            {ValidationState::ValidationItem::acft, QObject::tr("Aircraft Registration")}
-        };
+
         QString missing_items;
         for (int i=0; i < mandatoryLineEdits->size(); i++) {
             if (!validationState.validAt(i)){
-                missing_items.append(display_names.value(static_cast<ValidationState::ValidationItem>(i)) + "<br>");
-                mandatoryLineEdits->at(i)->setStyleSheet(QStringLiteral("border: 1px solid red"));
+                missing_items.append(validationItemsDisplayNames.value(static_cast<ValidationState::ValidationItem>(i)) + "<br>");
+                mandatoryLineEdits->at(i)->setStyleSheet(OPL::CssStyles::RED_BORDER);
             }
         }
 
@@ -660,7 +770,11 @@ void NewFlightDialog::on_buttonBox_accepted()
         return;
     }
 
-    if(!checkPilotFunctionsValid())
+    if(pilotFunctionsInvalid())
+        return;
+    if(duplicateNamesPresent())
+        return;
+    if(flightTimeIsZero())
         return;
 
     // If input verification passed, collect input and submit to database
@@ -682,3 +796,30 @@ void NewFlightDialog::on_buttonBox_accepted()
     }
 }
 
+
+void NewFlightDialog::on_calendarPushButton_clicked()
+{
+    calendar->setVisible(true);
+}
+
+void NewFlightDialog::calendarDateSelected()
+{
+    calendar->setVisible(false);
+    ui->doftLineEdit->setText(OPL::Date(calendar->selectedDate(), m_format).toString());
+    emit ui->doftLineEdit->editingFinished();
+}
+
+// EntryEditDialog interface
+
+void NewFlightDialog::loadEntry(int rowID)
+{
+    flightEntry = DB->getFlightEntry(rowID);
+    fillWithEntryData();
+}
+
+bool NewFlightDialog::deleteEntry(int rowID)
+{
+    const auto entry = DB->getFlightEntry(rowID);
+    return DB->remove(entry);
+}
+

+ 68 - 11
src/gui/dialogues/newflightdialog.h

@@ -24,10 +24,14 @@
 #include <QList>
 #include <QBitArray>
 
+#include "QtWidgets/qcalendarwidget.h"
+#include "src/database/database.h"
 #include "src/database/flightentry.h"
+#include "src/gui/dialogues/entryeditdialog.h"
 #include "src/gui/verification/userinput.h"
 #include "src/opl.h"
 #include "src/gui/verification/validationstate.h"
+#include "src/classes/date.h"
 
 namespace Ui {
 class NewFlightDialog;
@@ -57,7 +61,7 @@ class NewFlightDialog;
  * Once the user is satisfied with his entries, a final set of input verification is triggered and the entry is submitted to the database,
  * see on_buttonBox_accepted() and Database::commit()
  */
-class NewFlightDialog : public QDialog
+class NewFlightDialog : public EntryEditDialog
 {
     Q_OBJECT
 
@@ -74,9 +78,13 @@ public:
     explicit NewFlightDialog(int row_id, QWidget* parent = nullptr);
     ~NewFlightDialog();
 
+
+
 private:
     Ui::NewFlightDialog *ui;
     ValidationState validationState;
+    QCalendarWidget *calendar;
+    OPL::DateTimeFormat m_format;
 
     /*!
      * \brief a AFlightEntry object that is used to store either position data
@@ -88,23 +96,47 @@ private:
     /*!
      * \brief timeLineEdits - Line Edits for time Off Blocks and Time On Blocks
      */
-    static const inline QList<QLineEdit*>* timeLineEdits;
+    static const inline QList<QLineEdit*> *timeLineEdits;
     /*!
      * \brief locationLineEdits - Line Edits for Departure and Destination Airports
      */
-    static const inline QList<QLineEdit*>* locationLineEdits;
+    static const inline QList<QLineEdit*> *locationLineEdits;
     /*!
      * \brief pilotNameLineEdits - Line Edits for Pilot in Command, Second in Command (Co-Pilot) and Third Pilot
      */
-    static const inline QList<QLineEdit*>* pilotNameLineEdits;
+    static const inline QList<QLineEdit*> *pilotNameLineEdits;
     /*!
      * \brief mandatoryLineEdits - Contains the Line Edits that are needed for logging a complete flight from A to B.
      * The list is ordered like the ValidationItem enum so that indexed access is possible using the enum.
      */
-    static const inline QList<QLineEdit*>* mandatoryLineEdits;
-    static const inline QLatin1String self = QLatin1String("self");
+    static const inline QList<QLineEdit*> *mandatoryLineEdits;
+    // static const inline QLatin1String self = QLatin1String("self");
+    static const inline QHash<int, QString> pilotFuncionsMap = {
+                                           {0, OPL::FlightEntry::TPIC},
+                                           {1, OPL::FlightEntry::TPICUS},
+                                           {2, OPL::FlightEntry::TSIC},
+                                           {3, OPL::FlightEntry::TDUAL},
+                                           {4, OPL::FlightEntry::TFI},
+                                           };
+    static const inline QHash<ValidationState::ValidationItem, QString> validationItemsDisplayNames = {
+        {ValidationState::ValidationItem::doft, QObject::tr("Date of Flight")},
+        {ValidationState::ValidationItem::dept, QObject::tr("Departure Airport")},
+        {ValidationState::ValidationItem::dest, QObject::tr("Destination Airport")},
+        {ValidationState::ValidationItem::tofb, QObject::tr("Time Off Blocks")},
+        {ValidationState::ValidationItem::tonb, QObject::tr("Time on Blocks")},
+        {ValidationState::ValidationItem::pic, QObject::tr("PIC Name")},
+        {ValidationState::ValidationItem::acft, QObject::tr("Aircraft Registration")},
+    };
 
+    /*!
+     * \brief init - set up the UI
+     */
     void init();
+    /*!
+     * \brief setPilotFunction - Reads the application setting and pre-fills the logbook owners
+     * desired function
+     */
+    void setPilotFunction();
     void setupRawInputValidation();
     void setupSignalsAndSlots();
     void readSettings();
@@ -124,14 +156,30 @@ private:
      */
     void onBadInputReceived(QLineEdit *line_edit);
 
-    void updateBlockTimeLabel();
-    void setNightCheckboxes();
+    /*!
+     * \brief addNewDatabaseElement Adds a new element to the database
+     * \param parent - The line edit that triggered the event
+     * \param table - The table to which the new element is added
+     * \return true on success
+     * \details The flights database has a couple of NOT NULL constraints which
+     * must be met before a new flight can be submitted. If the user enters a
+     * constrained field which does not exist in a related table (pilots, tails
+     * or airports), the user is prompted to add a new entry to one of those
+     * tables before proceeding to log a flight with the missing element.
+     */
+    bool addNewDatabaseElement(QLineEdit* parent, OPL::DbTable table);
 
-    bool addNewTail(QLineEdit& parent_line_edit);
-    bool addNewPilot(QLineEdit& parent_line_edit);
+    /*!
+     * \brief userWantsToAddNewEntry - Asks the user if he wants to add a new entry to the database
+     * \param table - The table to which the entry will be committed.
+     * \return true if the reply is QMessageBox::Yes
+     */
+    bool userWantsToAddNewEntry(OPL::DbTable table);
 
 
-    bool checkPilotFunctionsValid();
+    bool pilotFunctionsInvalid();
+    bool duplicateNamesPresent();
+    bool flightTimeIsZero();
     OPL::RowData_T prepareFlightEntryData();
 
     const static inline auto CAT_3 = QLatin1String(OPL::GLOBALS->getApproachTypes()[3].toLatin1());
@@ -148,8 +196,17 @@ private slots:
     void on_approachComboBox_currentTextChanged(const QString &arg1);
     void on_functionComboBox_currentIndexChanged(int index);
 
+    void on_calendarPushButton_clicked();
+
+    void calendarDateSelected();
+
 protected:
     bool eventFilter(QObject* object, QEvent* event) override;
+
+    // EntryEditDialog interface
+public:
+    virtual void loadEntry(int rowID) override;
+    virtual bool deleteEntry(int rowID) override;
 };
 
 

+ 231 - 230
src/gui/dialogues/newflightdialog.ui

@@ -6,48 +6,67 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>767</width>
-    <height>362</height>
+    <width>799</width>
+    <height>391</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Add New Flight</string>
   </property>
   <layout class="QGridLayout" name="gridLayout">
-   <item row="5" column="5">
-    <widget class="QLineEdit" name="flightNumberLineEdit">
+   <item row="4" column="2">
+    <widget class="QLabel" name="destNameLabel">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
      <property name="minimumSize">
       <size>
-       <width>160</width>
+       <width>200</width>
        <height>0</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
-       <width>120</width>
+       <width>200</width>
        <height>16777215</height>
       </size>
      </property>
+     <property name="font">
+      <font>
+       <italic>true</italic>
+      </font>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
     </widget>
    </item>
-   <item row="2" column="2">
-    <widget class="QLineEdit" name="deptLocationLineEdit">
-     <property name="minimumSize">
-      <size>
-       <width>160</width>
-       <height>0</height>
-      </size>
+   <item row="9" column="0">
+    <widget class="QLabel" name="functionLabel">
+     <property name="text">
+      <string>Function</string>
      </property>
-     <property name="maximumSize">
-      <size>
-       <width>120</width>
-       <height>16777215</height>
-      </size>
+    </widget>
+   </item>
+   <item row="5" column="0">
+    <widget class="QLabel" name="tofbLabel">
+     <property name="text">
+      <string>Off Blocks</string>
      </property>
     </widget>
    </item>
-   <item row="3" column="5">
-    <widget class="QLineEdit" name="sicNameLineEdit">
+   <item row="2" column="3">
+    <widget class="QLabel" name="picLabel">
+     <property name="text">
+      <string>PIC</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QLineEdit" name="destLocationLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -62,42 +81,35 @@
      </property>
     </widget>
    </item>
-   <item row="7" column="4">
-    <widget class="QLabel" name="takeOffLabel">
+   <item row="0" column="3">
+    <widget class="QLabel" name="acftLabel">
      <property name="text">
-      <string>Take Off</string>
+      <string>Aircraft</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
      </property>
     </widget>
    </item>
-   <item row="2" column="3">
-    <widget class="QLabel" name="deptNameLabel">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
-     <property name="minimumSize">
-      <size>
-       <width>200</width>
-       <height>0</height>
-      </size>
-     </property>
-     <property name="maximumSize">
-      <size>
-       <width>200</width>
-       <height>16777215</height>
-      </size>
-     </property>
-     <property name="font">
-      <font>
-       <italic>true</italic>
-      </font>
+   <item row="10" column="0">
+    <widget class="QLabel" name="approachLabel">
+     <property name="text">
+      <string>Approach</string>
      </property>
+    </widget>
+   </item>
+   <item row="5" column="3">
+    <widget class="QLabel" name="thirdPilotLabel">
      <property name="text">
-      <string/>
+      <string>Third Pilot</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
      </property>
     </widget>
    </item>
-   <item row="1" column="5">
-    <widget class="QLineEdit" name="acftLineEdit">
+   <item row="2" column="4">
+    <widget class="QLineEdit" name="picNameLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -112,8 +124,12 @@
      </property>
     </widget>
    </item>
-   <item row="7" column="5">
-    <widget class="QSpinBox" name="takeOffSpinBox"/>
+   <item row="0" column="0">
+    <widget class="QPushButton" name="calendarPushButton">
+     <property name="text">
+      <string>Date Of flight</string>
+     </property>
+    </widget>
    </item>
    <item row="2" column="0">
     <widget class="QLabel" name="deptLabel">
@@ -122,8 +138,8 @@
      </property>
     </widget>
    </item>
-   <item row="4" column="5">
-    <widget class="QLineEdit" name="thirdPilotNameLineEdit">
+   <item row="0" column="1">
+    <widget class="QLineEdit" name="doftLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -136,17 +152,27 @@
        <height>16777215</height>
       </size>
      </property>
+     <property name="placeholderText">
+      <string>YYYY-MM-DD</string>
+     </property>
     </widget>
    </item>
-   <item row="5" column="0">
+   <item row="11" column="0">
+    <widget class="QLabel" name="flightRulesLabel">
+     <property name="text">
+      <string>Flight Rules</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="0">
     <widget class="QLabel" name="tonbLabel">
      <property name="text">
       <string>On Blocks</string>
      </property>
     </widget>
    </item>
-   <item row="3" column="2">
-    <widget class="QLineEdit" name="destLocationLineEdit">
+   <item row="5" column="1">
+    <widget class="QLineEdit" name="tofbTimeLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -161,81 +187,68 @@
      </property>
     </widget>
    </item>
-   <item row="11" column="0">
-    <widget class="QLabel" name="tblkLabel">
+   <item row="4" column="3">
+    <widget class="QLabel" name="sicLabel">
      <property name="text">
-      <string>Total Time</string>
+      <string>SIC</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
      </property>
     </widget>
    </item>
-   <item row="11" column="2">
-    <widget class="QLabel" name="tblkDisplayLabel">
-     <property name="minimumSize">
-      <size>
-       <width>160</width>
-       <height>0</height>
-      </size>
-     </property>
+   <item row="11" column="3">
+    <widget class="QLabel" name="landingLabel">
      <property name="text">
-      <string>00:00</string>
+      <string>Landing</string>
      </property>
     </widget>
    </item>
-   <item row="11" column="4" colspan="2">
-    <widget class="QDialogButtonBox" name="buttonBox">
+   <item row="6" column="2">
+    <widget class="QLabel" name="tonbSpacerLabel">
      <property name="enabled">
-      <bool>true</bool>
-     </property>
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+      <bool>false</bool>
      </property>
-    </widget>
-   </item>
-   <item row="5" column="2">
-    <widget class="QLineEdit" name="tonbTimeLineEdit">
      <property name="minimumSize">
       <size>
-       <width>160</width>
+       <width>200</width>
        <height>0</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
-       <width>120</width>
+       <width>200</width>
        <height>16777215</height>
       </size>
      </property>
-    </widget>
-   </item>
-   <item row="3" column="0">
-    <widget class="QLabel" name="destLabel">
      <property name="text">
-      <string>Destination</string>
+      <string/>
      </property>
     </widget>
    </item>
-   <item row="1" column="4">
-    <widget class="QLabel" name="acftLabel">
-     <property name="text">
-      <string>Aircraft</string>
+   <item row="9" column="1">
+    <widget class="QComboBox" name="functionComboBox"/>
+   </item>
+   <item row="7" column="1">
+    <widget class="QLabel" name="tblkDisplayLabel">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
      </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     <property name="font">
+      <font>
+       <bold>true</bold>
+      </font>
      </property>
-    </widget>
-   </item>
-   <item row="4" column="0">
-    <widget class="QLabel" name="tofbLabel">
      <property name="text">
-      <string>Off Blocks</string>
+      <string>00:00</string>
      </property>
     </widget>
    </item>
-   <item row="1" column="2">
-    <widget class="QLineEdit" name="doftLineEdit">
+   <item row="2" column="1">
+    <widget class="QLineEdit" name="deptLocationLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -248,35 +261,40 @@
        <height>16777215</height>
       </size>
      </property>
-     <property name="placeholderText">
-      <string>YYYY-MM-DD</string>
-     </property>
     </widget>
    </item>
-   <item row="4" column="3">
-    <widget class="QLabel" name="tofbSpacerLabel">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
+   <item row="7" column="4">
+    <widget class="QLineEdit" name="remarksLineEdit">
      <property name="minimumSize">
       <size>
-       <width>200</width>
+       <width>160</width>
        <height>0</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
-       <width>200</width>
+       <width>120</width>
        <height>16777215</height>
       </size>
      </property>
-     <property name="text">
-      <string/>
-     </property>
     </widget>
    </item>
-   <item row="2" column="5">
-    <widget class="QLineEdit" name="picNameLineEdit">
+   <item row="11" column="1">
+    <widget class="QComboBox" name="flightRulesComboBox">
+     <item>
+      <property name="text">
+       <string>VFR</string>
+      </property>
+     </item>
+     <item>
+      <property name="text">
+       <string>IFR</string>
+      </property>
+     </item>
+    </widget>
+   </item>
+   <item row="4" column="4">
+    <widget class="QLineEdit" name="sicNameLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -291,11 +309,8 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="3">
-    <widget class="QLabel" name="destNameLabel">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
+   <item row="15" column="2">
+    <widget class="QLabel" name="submissionReadyLabel">
      <property name="minimumSize">
       <size>
        <width>200</width>
@@ -308,40 +323,29 @@
        <height>16777215</height>
       </size>
      </property>
-     <property name="font">
-      <font>
-       <italic>true</italic>
-      </font>
-     </property>
      <property name="text">
       <string/>
      </property>
     </widget>
    </item>
-   <item row="5" column="3">
-    <widget class="QLabel" name="tonbSpacerLabel">
-     <property name="enabled">
-      <bool>false</bool>
-     </property>
+   <item row="6" column="1">
+    <widget class="QLineEdit" name="tonbTimeLineEdit">
      <property name="minimumSize">
       <size>
-       <width>200</width>
+       <width>160</width>
        <height>0</height>
       </size>
      </property>
      <property name="maximumSize">
       <size>
-       <width>200</width>
+       <width>120</width>
        <height>16777215</height>
       </size>
      </property>
-     <property name="text">
-      <string/>
-     </property>
     </widget>
    </item>
-   <item row="6" column="5">
-    <widget class="QLineEdit" name="remarksLineEdit">
+   <item row="0" column="4">
+    <widget class="QLineEdit" name="acftLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -356,17 +360,23 @@
      </property>
     </widget>
    </item>
-   <item row="6" column="4">
-    <widget class="QLabel" name="remarksLabel">
-     <property name="text">
-      <string>Remarks</string>
+   <item row="5" column="4">
+    <widget class="QLineEdit" name="thirdPilotNameLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>160</width>
+       <height>0</height>
+      </size>
      </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
      </property>
     </widget>
    </item>
-   <item row="5" column="4">
+   <item row="6" column="3">
     <widget class="QLabel" name="flightNumberLabel">
      <property name="text">
       <string>Flight Number</string>
@@ -376,59 +386,18 @@
      </property>
     </widget>
    </item>
-   <item row="2" column="4">
-    <widget class="QLabel" name="picLabel">
-     <property name="text">
-      <string>PIC</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="3" column="4">
-    <widget class="QLabel" name="sicLabel">
-     <property name="text">
-      <string>SIC</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="1" column="0">
-    <widget class="QLabel" name="doftLabel">
-     <property name="text">
-      <string>Date of flight</string>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="4">
-    <widget class="QLabel" name="thirdPilotLabel">
-     <property name="text">
-      <string>Third Pilot</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="6" column="0">
-    <widget class="QLabel" name="functionLabel">
-     <property name="text">
-      <string>Function</string>
-     </property>
-    </widget>
-   </item>
    <item row="7" column="0">
-    <widget class="QLabel" name="approachLabel">
+    <widget class="QLabel" name="tblkLabel">
      <property name="text">
-      <string>Approach</string>
+      <string>Total Time</string>
      </property>
     </widget>
    </item>
-   <item row="4" column="2">
-    <widget class="QLineEdit" name="tofbTimeLineEdit">
+   <item row="10" column="1">
+    <widget class="QComboBox" name="approachComboBox"/>
+   </item>
+   <item row="6" column="4">
+    <widget class="QLineEdit" name="flightNumberLineEdit">
      <property name="minimumSize">
       <size>
        <width>160</width>
@@ -443,45 +412,24 @@
      </property>
     </widget>
    </item>
-   <item row="8" column="0">
-    <widget class="QLabel" name="flightRulesLabel">
-     <property name="text">
-      <string>Flight Rules</string>
+   <item row="15" column="3" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="enabled">
+      <bool>true</bool>
      </property>
-    </widget>
-   </item>
-   <item row="6" column="2">
-    <widget class="QComboBox" name="functionComboBox"/>
-   </item>
-   <item row="7" column="2">
-    <widget class="QComboBox" name="approachComboBox"/>
-   </item>
-   <item row="8" column="2">
-    <widget class="QComboBox" name="flightRulesComboBox">
-     <item>
-      <property name="text">
-       <string>VFR</string>
-      </property>
-     </item>
-     <item>
-      <property name="text">
-       <string>IFR</string>
-      </property>
-     </item>
-    </widget>
-   </item>
-   <item row="8" column="4">
-    <widget class="QLabel" name="landingLabel">
-     <property name="text">
-      <string>Landing</string>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
      </property>
     </widget>
    </item>
-   <item row="8" column="5">
-    <widget class="QSpinBox" name="landingSpinBox"/>
-   </item>
-   <item row="1" column="3">
-    <widget class="QLabel" name="doftDisplayLabel">
+   <item row="5" column="2">
+    <widget class="QLabel" name="tofbSpacerLabel">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
      <property name="minimumSize">
       <size>
        <width>200</width>
@@ -499,8 +447,21 @@
      </property>
     </widget>
    </item>
-   <item row="11" column="3">
-    <widget class="QLabel" name="submissionReadyLabel">
+   <item row="7" column="3">
+    <widget class="QLabel" name="remarksLabel">
+     <property name="text">
+      <string>Remarks</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2">
+    <widget class="QLabel" name="deptNameLabel">
+     <property name="enabled">
+      <bool>false</bool>
+     </property>
      <property name="minimumSize">
       <size>
        <width>200</width>
@@ -513,6 +474,45 @@
        <height>16777215</height>
       </size>
      </property>
+     <property name="font">
+      <font>
+       <italic>true</italic>
+      </font>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="destLabel">
+     <property name="text">
+      <string>Destination</string>
+     </property>
+    </widget>
+   </item>
+   <item row="11" column="4">
+    <widget class="QSpinBox" name="landingSpinBox"/>
+   </item>
+   <item row="10" column="3">
+    <widget class="QLabel" name="takeOffLabel">
+     <property name="text">
+      <string>Take Off</string>
+     </property>
+    </widget>
+   </item>
+   <item row="10" column="4">
+    <widget class="QSpinBox" name="takeOffSpinBox"/>
+   </item>
+   <item row="9" column="3">
+    <widget class="QLabel" name="pilotFlyingLabel">
+     <property name="text">
+      <string>Pilot Flying</string>
+     </property>
+    </widget>
+   </item>
+   <item row="9" column="4">
+    <widget class="QCheckBox" name="pilotFlyingCheckBox">
      <property name="text">
       <string/>
      </property>
@@ -521,17 +521,18 @@
   </layout>
  </widget>
  <tabstops>
+  <tabstop>doftLineEdit</tabstop>
   <tabstop>deptLocationLineEdit</tabstop>
   <tabstop>destLocationLineEdit</tabstop>
   <tabstop>tofbTimeLineEdit</tabstop>
   <tabstop>tonbTimeLineEdit</tabstop>
+  <tabstop>acftLineEdit</tabstop>
   <tabstop>picNameLineEdit</tabstop>
   <tabstop>sicNameLineEdit</tabstop>
   <tabstop>thirdPilotNameLineEdit</tabstop>
   <tabstop>flightNumberLineEdit</tabstop>
   <tabstop>remarksLineEdit</tabstop>
-  <tabstop>takeOffSpinBox</tabstop>
-  <tabstop>approachComboBox</tabstop>
+  <tabstop>calendarPushButton</tabstop>
  </tabstops>
  <resources/>
  <connections>

+ 23 - 5
src/gui/dialogues/newpilotdialog.cpp

@@ -21,16 +21,18 @@
 #include "src/opl.h"
 
 #include "src/database/database.h"
-#include "src/database/row.h"
 
 /*!
  * \brief NewPilotDialog::NewPilotDialog - creates a new pilot dialog which can be used to add a new entry to the database
  */
-NewPilotDialog::NewPilotDialog(QWidget *parent) :
-    QDialog(parent),
+NewPilotDialog::NewPilotDialog(QString userInput, QWidget* parent)
+    : EntryEditDialog{parent},
     ui(new Ui::NewPilot)
 {
     setup();
+    if(userInput != QString()) {
+        ui->lastnameLineEdit->setText(userInput.replace(0, 1, userInput.first(1).toUpper()));
+    }
     ui->lastnameLineEdit->setFocus();
 }
 
@@ -39,7 +41,7 @@ NewPilotDialog::NewPilotDialog(QWidget *parent) :
  * \param rowId - the rowid of the entry to be edited in the database
  */
 NewPilotDialog::NewPilotDialog(int rowId, QWidget *parent) :
-    QDialog(parent),
+    EntryEditDialog{rowId, parent},
     ui(new Ui::NewPilot)
 {
     setup();
@@ -58,7 +60,9 @@ NewPilotDialog::~NewPilotDialog()
 void NewPilotDialog::setup()
 {
     ui->setupUi(this);
-    ui->companyLineEdit->setCompleter(QCompleterProvider.getCompleter(CompleterProvider::Companies));
+    auto completer = QCompleterProvider.getCompleter(CompleterProvider::Companies);
+    completer->setCompletionMode(QCompleter::InlineCompletion);
+    ui->companyLineEdit->setCompleter(completer);
 }
 
 void NewPilotDialog::on_buttonBox_accepted()
@@ -107,3 +111,17 @@ void NewPilotDialog::submitForm()
         QDialog::accept();
     }
 }
+
+bool NewPilotDialog::deleteEntry(int rowId)
+{
+    auto entry = DB->getPilotEntry(rowId);
+    return DB->remove(entry);
+
+}
+
+void NewPilotDialog::loadEntry(int rowId)
+{
+    pilotEntry = DB->getPilotEntry(rowId);
+    formFiller();
+    ui->lastnameLineEdit->setFocus();
+}

+ 12 - 2
src/gui/dialogues/newpilotdialog.h

@@ -24,6 +24,7 @@
 #include <QRegularExpressionValidator>
 #include <QCompleter>
 #include "src/database/pilotentry.h"
+#include "src/gui/dialogues/entryeditdialog.h"
 
 namespace Ui {
 class NewPilot;
@@ -41,14 +42,17 @@ class NewPilot;
  * come in all different forms and shapes around the world. In order to maintain a maximum
  * amount of flexibility, any unicode input is allowed.
  */
-class NewPilotDialog : public QDialog
+class NewPilotDialog : public EntryEditDialog
 {
     Q_OBJECT
 public:
-    explicit NewPilotDialog(QWidget *parent = nullptr);
+    explicit NewPilotDialog(QString userInput = QString(), QWidget *parent = nullptr);
     explicit NewPilotDialog(int rowId, QWidget *parent = nullptr);
     ~NewPilotDialog();
 
+
+
+
 private slots:
     void on_buttonBox_accepted();
 private:
@@ -67,6 +71,12 @@ private:
      * \brief submitForm - retreives the input from the line edits and commits to the database.
      */
     void submitForm();
+
+    // EntryEditDialog interface
+public:
+    virtual bool deleteEntry(int rowId) override;
+    virtual void loadEntry(int rowId) override;
+
 };
 
 

+ 37 - 25
src/gui/dialogues/newsimdialog.cpp

@@ -4,33 +4,33 @@
 #include "ui_newsimdialog.h"
 #include "src/opl.h"
 #include "src/classes/time.h"
-#include "src/functions/datetime.h"
 #include "src/database/database.h"
+#include "src/classes/settings.h"
 #include <QCompleter>
 /*!
  * \brief create a NewSimDialog to add a new Simulator Entry to the database
  */
-NewSimDialog::NewSimDialog(QWidget *parent) :
-    QDialog(parent),
+NewSimDialog::NewSimDialog(QWidget *parent)
+    : EntryEditDialog(parent),
     ui(new Ui::NewSimDialog)
 {
-    //entry = ASimulatorEntry();
     ui->setupUi(this);
-    ui->dateLineEdit->setText(OPL::DateTime::currentDate());
     init();
+
+    ui->dateLineEdit->setText(OPL::Date::today(m_format).toString());
 }
 /*!
  * \brief create a NewSimDialog to edit an existing Simulator Entry
  * \param row_id of the entry to be edited
  */
-NewSimDialog::NewSimDialog(int row_id, QWidget *parent) :
-    QDialog(parent),
+NewSimDialog::NewSimDialog(int row_id, QWidget *parent)
+    : EntryEditDialog(parent),
     ui(new Ui::NewSimDialog)
 {
-
     ui->setupUi(this);
-    entry = DB->getSimEntry(row_id);
     init();
+
+    entry = DB->getSimEntry(row_id);
     fillEntryData();
 }
 
@@ -47,6 +47,8 @@ void NewSimDialog::init()
     completer->setCompletionMode(QCompleter::PopupCompletion);
     completer->setFilterMode(Qt::MatchContains);
     ui->aircraftTypeLineEdit->setCompleter(completer);
+
+    m_format = Settings::getDisplayFormat();
 }
 
 /*!
@@ -55,8 +57,8 @@ void NewSimDialog::init()
 void NewSimDialog::fillEntryData()
 {
     const auto& data = entry.getData();
-    ui->dateLineEdit->setText(data.value(OPL::SimulatorEntry::DATE).toString());
-    ui->totalTimeLineEdit->setText(OPL::Time(data.value(OPL::SimulatorEntry::TIME).toInt()).toString());
+    ui->dateLineEdit->setText(OPL::Date(data.value(OPL::SimulatorEntry::DATE).toInt(), m_format).toString());
+    ui->totalTimeLineEdit->setText(OPL::Time(data.value(OPL::SimulatorEntry::TIME).toInt(), m_format).toString());
     ui->deviceTypeComboBox->setCurrentIndex(data.value(OPL::SimulatorEntry::TYPE).toInt());
     ui->aircraftTypeLineEdit->setText(data.value(OPL::SimulatorEntry::ACFT).toString());
     ui->registrationLineEdit->setText(data.value(OPL::SimulatorEntry::REG).toString());
@@ -70,12 +72,9 @@ NewSimDialog::~NewSimDialog()
 
 void NewSimDialog::on_dateLineEdit_editingFinished()
 {
-    auto text = ui->dateLineEdit->text();
-
-    OPL::DateFormat date_format = OPL::DateFormat::ISODate;
-    auto date = OPL::DateTime::parseInput(text, date_format);
-    if (date.isValid()) {
-        ui->dateLineEdit->setText(OPL::DateTime::dateToString(date, date_format));
+    const auto date = OPL::Date(ui->dateLineEdit->text(), m_format);
+    if(date.isValid()) {
+        ui->dateLineEdit->setText(date.toString());
         ui->dateLineEdit->setStyleSheet(QString());
         return;
     } else {
@@ -87,7 +86,7 @@ void NewSimDialog::on_dateLineEdit_editingFinished()
 
 void NewSimDialog::on_totalTimeLineEdit_editingFinished()
 {
-    const auto input = TimeInput(ui->totalTimeLineEdit->text());
+    const auto input = TimeInput(ui->totalTimeLineEdit->text(), m_format);
     if(input.isValid())
         return;
     else {
@@ -122,9 +121,7 @@ void NewSimDialog::on_helpPushButton_clicked()
 bool NewSimDialog::verifyInput(QString& error_msg)
 {
     // Date
-    auto text = ui->dateLineEdit->text();
-    OPL::DateFormat date_format = OPL::DateFormat::ISODate;
-    const auto date = OPL::DateTime::parseInput(text, date_format);
+    const auto date = OPL::Date(ui->dateLineEdit->text(), m_format);
 
     if (!date.isValid()) {
         ui->dateLineEdit->setStyleSheet(OPL::CssStyles::RED_BORDER);
@@ -133,10 +130,10 @@ bool NewSimDialog::verifyInput(QString& error_msg)
         return false;
     }
     // Time
-    if(!TimeInput(ui->totalTimeLineEdit->text()).isValid())
+    if(!TimeInput(ui->totalTimeLineEdit->text(), m_format).isValid())
         return false;
 
-    const OPL::Time time = OPL::Time::fromString(ui->totalTimeLineEdit->text());
+    const OPL::Time time = OPL::Time::fromString(ui->totalTimeLineEdit->text(), m_format);
 
     if (!time.isValidTimeOfDay()) {
         ui->totalTimeLineEdit->setStyleSheet(OPL::CssStyles::RED_BORDER);
@@ -159,9 +156,10 @@ OPL::RowData_T NewSimDialog::collectInput()
 {
     OPL::RowData_T new_entry;
     // Date
-    new_entry.insert(OPL::SimulatorEntry::DATE, ui->dateLineEdit->text());
+    const auto date = OPL::Date(ui->dateLineEdit->text(), m_format);
+    new_entry.insert(OPL::SimulatorEntry::DATE, date.toJulianDay());
     // Time
-    new_entry.insert(OPL::SimulatorEntry::TIME, OPL::Time::fromString(ui->totalTimeLineEdit->text()).toMinutes());
+    new_entry.insert(OPL::SimulatorEntry::TIME, OPL::Time::fromString(ui->totalTimeLineEdit->text(), m_format).toMinutes());
     // Device Type
     new_entry.insert(OPL::SimulatorEntry::TYPE, ui->deviceTypeComboBox->currentText());
     // Aircraft Type
@@ -193,3 +191,17 @@ void NewSimDialog::on_buttonBox_accepted()
     else
         WARN(tr("Unable to commit entry to database. The following error has ocurred <br><br>%1").arg(DB->lastError.text()));
 }
+
+// EntryEdit interface
+void NewSimDialog::loadEntry(int rowID)
+{
+    entry = DB->getSimEntry(rowID);
+    init();
+    fillEntryData();
+}
+
+bool NewSimDialog::deleteEntry(int rowID)
+{
+    const auto entry = DB->getSimEntry(rowID);
+    return DB->remove(entry);
+}

+ 17 - 2
src/gui/dialogues/newsimdialog.h

@@ -2,7 +2,10 @@
 #define NEWSIMDIALOG_H
 
 #include <QDialog>
+#include "src/database/database.h"
 #include "src/database/simulatorentry.h"
+#include "src/gui/dialogues/entryeditdialog.h"
+#include "src/classes/date.h"
 
 namespace Ui {
 class NewSimDialog;
@@ -17,7 +20,7 @@ class NewSimDialog;
  *
  * A QCompleter provides in-line completion for the aircraft type field.
  */
-class NewSimDialog : public QDialog
+class NewSimDialog : public EntryEditDialog
 {
     Q_OBJECT
 
@@ -39,12 +42,24 @@ private slots:
 
 private:
     Ui::NewSimDialog *ui;
+    //TODO load from settings
+    OPL::DateTimeFormat m_format = OPL::DateTimeFormat(
+        OPL::DateTimeFormat::DateFormat::Default,
+        QStringLiteral("yyyy-MM-dd"),
+        OPL::DateTimeFormat::TimeFormat::Default,
+        QStringLiteral("hh:mm")
+        );
+
     void init();
     void fillEntryData();
     bool verifyInput(QString &error_msg);
     OPL::RowData_T collectInput();
-
     OPL::SimulatorEntry entry;
+
+    // EntryEditDialog interface
+public:
+    virtual void loadEntry(int rowID) override;
+    virtual bool deleteEntry(int rowID) override;
 };
 
 #endif // NEWSIMDIALOG_H

+ 34 - 11
src/gui/dialogues/newtaildialog.cpp

@@ -22,7 +22,7 @@
 #include "src/opl.h"
 
 NewTailDialog::NewTailDialog(const QString &new_registration, QWidget *parent) :
-    QDialog(parent),
+    EntryEditDialog(parent),
     ui(new Ui::NewTail)
 {
     ui->setupUi(this);
@@ -38,7 +38,7 @@ NewTailDialog::NewTailDialog(const QString &new_registration, QWidget *parent) :
 }
 
 NewTailDialog::NewTailDialog(int row_id, QWidget *parent) :
-    QDialog(parent),
+    EntryEditDialog(parent),
     ui(new Ui::NewTail)
 {
     ui->setupUi(this);
@@ -202,6 +202,12 @@ void NewTailDialog::submitForm()
     //create db object
 
     entry.setData(new_data);
+
+    // add type string
+    auto data = entry.getData();
+    data.insert(OPL::TailEntry::TYPE_STRING, entry.type());
+    entry.setData(data);
+
     LOG << "Commiting: " << entry;
     if (!DB->commit(entry)) {
         QMessageBox message_box(this);
@@ -271,15 +277,32 @@ void NewTailDialog::onSearchCompleterActivated()
     const auto &text = ui->searchLineEdit->text();
     if (aircraftList.contains(text)) {
 
-            DEB << "Template Selected. aircraft_id is: " << idMap.key(text);
-            //call autofiller for dialog
-            fillForm(DB->getAircraftEntry(idMap.key(text)), true);
-            ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid green"));
-            ui->searchLabel->setText(text);
-        } else {
-            //for example, editing finished without selecting a result from Qcompleter
-            ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid orange"));
-        }
+        DEB << "Template Selected. aircraft_id is: " << idMap.key(text);
+        //call autofiller for dialog
+        fillForm(DB->getAircraftEntry(idMap.key(text)), true);
+        ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid green"));
+        ui->searchLabel->setText(text);
+    } else {
+        //for example, editing finished without selecting a result from Qcompleter
+        ui->searchLineEdit->setStyleSheet(QStringLiteral("border: 1px solid orange"));
+    }
+}
+
+bool NewTailDialog::deleteEntry(int rowID)
+{
+    auto entry = DB->getTailEntry(rowID);
+    return DB->remove(entry);
+}
+
+void NewTailDialog::loadEntry(int rowId)
+{
+    ui->searchLabel->hide();
+    ui->searchLineEdit->hide();
+    ui->line->hide();
+
+    setupValidators();
+    entry = DB->getTailEntry(rowId);
+    fillForm(entry, false);
 }
 
 void NewTailDialog::on_registrationLineEdit_textChanged(const QString &arg1)

+ 10 - 2
src/gui/dialogues/newtaildialog.h

@@ -26,6 +26,7 @@
 
 #include "src/database/row.h"
 #include "src/database/tailentry.h"
+#include "src/gui/dialogues/entryeditdialog.h"
 
 namespace Ui {
 class NewTail;
@@ -55,7 +56,7 @@ class NewTail;
  *
  *
  */
-class NewTailDialog : public QDialog
+class NewTailDialog : public EntryEditDialog
 {
     Q_OBJECT
 
@@ -64,7 +65,7 @@ public:
      * \brief NewTailDialog - create a new ATailEntry and submit it to the database
      * \param new_registration - when called from the NewFlightDialog, pre-fills the registration already entered.
      */
-    explicit NewTailDialog(const QString& new_registration, QWidget *parent = nullptr);
+    explicit NewTailDialog(const QString &new_registration, QWidget *parent = nullptr);
     /*!
      * \brief NewTailDialog - edit an existing Tail Entry
      * \param row_id - the ROW_ID of the entry to be edited in the database
@@ -73,6 +74,8 @@ public:
 
     ~NewTailDialog();
 
+
+
 signals:
     void tailDataChanged();
 private:
@@ -102,6 +105,11 @@ private slots:
     void on_buttonBox_accepted();
     void onSearchCompleterActivated();
 
+
+    // EntryEditDialog interface
+public:
+    virtual bool deleteEntry(int rowID) override;
+    virtual void loadEntry(int rowId) override;
 };
 
 #endif // NEWTAIL_H

+ 34 - 4
src/gui/verification/timeinput.cpp

@@ -1,10 +1,11 @@
 #include "timeinput.h"
+#include "src/classes/time.h"
 #include "src/opl.h"
 #include <QTime>
 
 bool TimeInput::isValid() const
 {
-     return QTime::fromString(input, OPL::Format::TIME_FORMAT).isValid();
+    return OPL::Time::fromString(input, m_format).isValid();
 }
 
 /*!
@@ -14,8 +15,23 @@ bool TimeInput::isValid() const
  */
 QString TimeInput::fixup() const
 {
-    QString fixed = input;
+    switch(m_format.timeFormat()) {
+    case OPL::DateTimeFormat::TimeFormat::Default:
+        return fixDefaultFormat();
+    case OPL::DateTimeFormat::TimeFormat::Decimal:
+        return fixDecimalFormat();
+    case OPL::DateTimeFormat::TimeFormat::Custom:
+        return input; // custom formats cannot be fixed
+        break;
+    }
+}
 
+const QString TimeInput::fixDefaultFormat() const
+{
+    if(input == QString())
+        return QStringLiteral("00:00");
+    // try inserting a ':' for hhmm inputs
+    QString fixed = input;
     if (input.contains(':')) { // contains seperator
         if(input.length() == 4)
             fixed.prepend(QLatin1Char('0'));
@@ -29,6 +45,20 @@ QString TimeInput::fixup() const
         }
     }
 
-    QTime time = QTime::fromString(fixed, OPL::Format::TIME_FORMAT);
-    return time.toString(OPL::Format::TIME_FORMAT);
+    OPL::Time fixedTime = OPL::Time::fromString(fixed, m_format);
+    if(fixedTime.isValid()) {
+        return OPL::Time::fromString(fixed, m_format).toString();
+    } else {
+        return QString();
+    }
+
+
+}
+
+const QString TimeInput::fixDecimalFormat() const
+{
+    // try to replace an erroneus decimal seperator
+    QString fixed = input;
+    return OPL::Time::fromString(fixed.replace(QLatin1Char(','), OPL::DECIMAL_SEPERATOR), m_format).toString();
 }
+

+ 7 - 1
src/gui/verification/timeinput.h

@@ -1,13 +1,15 @@
 #ifndef TIMEINPUT_H
 #define TIMEINPUT_H
 
+#include "src/opl.h"
 #include "userinput.h"
 
 class TimeInput : public UserInput
 {
 public:
     TimeInput() = delete;
-    TimeInput(const QString &input) : UserInput(input) {}
+    TimeInput(const QString &input, const OPL::DateTimeFormat &format)
+        : UserInput(input), m_format(format) {}
 
     /*!
      * \brief Checks if a user entered String is a valid time input
@@ -25,6 +27,10 @@ public:
      */
     QString fixup() const override;
 private:
+    const OPL::DateTimeFormat &m_format;
+
+    const QString fixDefaultFormat() const;
+    const QString fixDecimalFormat() const;
 };
 
 #endif // TIMEINPUT_H

+ 79 - 0
src/gui/widgets/airporttableeditwidget.cpp

@@ -0,0 +1,79 @@
+#include "airporttableeditwidget.h"
+#include "src/database/database.h"
+#include "src/gui/dialogues/newairportdialog.h"
+
+AirportTableEditWidget::AirportTableEditWidget(QWidget *parent)
+    : TableEditWidget(Vertical, parent)
+{}
+
+void AirportTableEditWidget::setupModelAndView()
+{
+    m_model = new QSqlTableModel(this, DB->database());
+    m_model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Airports));
+    m_model->select();
+
+    for(int i = 0; i < HEADER_NAMES.size(); i++) {
+        m_model->setHeaderData(i + 1, Qt::Horizontal, HEADER_NAMES.at(i));
+    }
+
+    m_view->setModel(m_model);
+    m_view->setSelectionMode(QAbstractItemView::SingleSelection);
+    m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
+    m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    m_view->resizeColumnsToContents();
+    m_view->verticalHeader()->hide();
+    m_view->setAlternatingRowColors(true);
+    m_view->hideColumn(COL_ROWID);
+
+}
+
+void AirportTableEditWidget::setupUI()
+{
+    // the base class does most of the setup
+    TableEditWidget::setupUI();
+
+    // only need to set the table specific labels and combo box items
+    m_addNewEntryPushButton->setText(tr("Add New Airport"));
+    m_deleteEntryPushButton->setText(tr("Delete Selected Airport"));
+    for(const int i : FILTER_COLUMNS) {
+        m_filterSelectionComboBox->addItem(HEADER_NAMES.at(i));
+    }
+}
+
+QString AirportTableEditWidget::deleteErrorString(int rowId)
+{
+    return tr("<br>Unable to delete.<br><br>The following error has ocurred: %1"
+              ).arg(DB->lastError.text());
+}
+
+QString AirportTableEditWidget::confirmDeleteString(int rowId)
+{
+    const auto entry = DB->getAirportEntry(rowId);
+    return tr("The following airport will be deleted:<br><br><b><tt>"
+              "%1<br></b></tt>"
+              "Deleting airports is irreversible.<br>Do you want to proceed?"
+              ).arg(entry.getAirportDescriptor());
+}
+
+EntryEditDialog *AirportTableEditWidget::getEntryEditDialog(QWidget *parent)
+{
+    return new NewAirportDialog(parent);
+}
+
+void AirportTableEditWidget::filterTextChanged(const QString &filterString)
+{
+    if(filterString.isEmpty()) {
+        m_model->setFilter(QString());
+        return;
+    }
+
+    int i = m_filterSelectionComboBox->currentIndex();
+    const QString filter =
+        QLatin1Char('\"')
+        + HEADER_NAMES.at(FILTER_COLUMNS[i])
+        + QLatin1String("\" LIKE '%")
+        + filterString
+        + QLatin1String("%'");
+    m_model->setFilter(filter);
+}

+ 52 - 0
src/gui/widgets/airporttableeditwidget.h

@@ -0,0 +1,52 @@
+#ifndef AIRPORTTABLEEDITWIDGET_H
+#define AIRPORTTABLEEDITWIDGET_H
+
+#include "tableeditwidget.h"
+#include <QObject>
+#include "src/database/airportentry.h"
+
+class AirportTableEditWidget : public TableEditWidget
+{
+    Q_OBJECT
+public:
+    AirportTableEditWidget() = delete;
+    AirportTableEditWidget(QWidget *parent = nullptr);
+
+    // TableEditWidget interface
+    virtual void setupModelAndView() override;
+    virtual void setupUI() override;
+    virtual QString deleteErrorString(int rowId) override;
+    virtual QString confirmDeleteString(int rowId) override;
+    virtual EntryEditDialog *getEntryEditDialog(QWidget *parent) override;
+
+    // table columns and header names
+
+    const int COL_ROWID = 0;
+
+    // used to display the Header Views and Fill the FilterComboBox
+    const QStringList HEADER_NAMES = {
+                                      tr("ICAO"),
+                                      tr("IATA"),
+                                      tr("Name"),
+                                      tr("Latitude"),
+                                      tr("Longitude"),
+                                      tr("Country"),
+                                      tr("Time Zone"),
+    };
+
+    // These are indexes into HEADER_NAMES
+    const int FILTER_COLUMNS[4] = {0, 1, 2, 5};
+
+    // The sql column names corresponding to the entries of the FilterComboBox index
+    const static inline QStringList FILTER_COLUMN_NAMES = {
+        OPL::AirportEntry::ICAO,
+        OPL::AirportEntry::IATA,
+        OPL::AirportEntry::NAME,
+        OPL::AirportEntry::TZ_OLSON,
+    };
+
+private slots:
+    virtual void filterTextChanged(const QString &filterString) override;
+};
+
+#endif // AIRPORTTABLEEDITWIDGET_H

+ 10 - 2
src/gui/widgets/backupwidget.cpp

@@ -22,6 +22,8 @@
 #include "src/functions/datetime.h"
 #include "src/database/dbsummary.h"
 #include "src/gui/dialogues/firstrundialog.h"
+#include "src/classes/settings.h"
+#include "src/classes/styleddatedelegate.h"
 
 #include <QListView>
 #include <QStandardItemModel>
@@ -39,6 +41,12 @@ BackupWidget::BackupWidget(QWidget *parent) :
     model->setHorizontalHeaderLabels(QStringList{tr("Total Time"),tr("Flights"), tr("Aircraft"),
                                                  tr("Pilots"), tr("Last Flight"), tr("Backup File")});
     view = ui->tableView;
+
+
+    // julian day to Date Format
+    const auto dateDelegate = new StyledDateDelegate(Settings::getDisplayFormat(), model);
+    view->setItemDelegateForColumn(DATE_COLUMN, dateDelegate);
+
     refresh();
 }
 
@@ -86,8 +94,8 @@ const QString BackupWidget::absoluteBackupPath()
 const QString BackupWidget::backupName()
 {
     auto owner = DB->getPilotEntry(1);
-    return  QString("logbook_backup_%1_%2.db").arg(
-                OPL::DateTime::dateTimeToString(QDateTime::currentDateTime(), OPL::DateTimeFormat::Backup),
+    return  QStringLiteral("logbook_backup_%1_%2.db").arg(
+        OPL::DateTime::dateTimeToString(QDateTime::currentDateTime(), OPL::DateTimeFormat_deprecated::Backup),
                                                   owner.getLastName()
                 );
 }

+ 2 - 0
src/gui/widgets/backupwidget.h

@@ -96,6 +96,8 @@ private:
     QList<int> selectedRows;
     void refresh();
 
+    static constexpr int DATE_COLUMN = 4;
+
 protected:
     /*!
      * \brief Handles change events, like updating the UI to new localisation

+ 264 - 0
src/gui/widgets/currencywidget.cpp

@@ -0,0 +1,264 @@
+#include "currencywidget.h"
+#include "QtSql/qsqltablemodel.h"
+#include "QtWidgets/qheaderview.h"
+#include "qgridlayout.h"
+#include "src/classes/easaftl.h"
+#include "src/classes/styleddatedelegate.h"
+#include "src/classes/time.h"
+#include "src/database/database.h"
+#include "src/functions/statistics.h"
+#include "src/classes/settings.h"
+#include <QCalendarWidget>
+#include <QInputDialog>
+#include <QLabel>
+
+CurrencyWidget::CurrencyWidget(QWidget *parent)
+    : QWidget{parent}
+{
+    m_format = Settings::getDisplayFormat();
+    setupModelAndView();
+    setupUI();
+
+    fillTakeOffAndLandingCurrencies();
+    fillFlightTimeLimitations();
+
+    warnAboutExpiries();
+}
+
+void CurrencyWidget::setupModelAndView()
+{
+    model = new QSqlTableModel(this, DB->database());
+    model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Currencies));
+    model->select();
+    model->setHeaderData(EXPIRY_DATE_COLUMN, Qt::Horizontal, tr("Expiry Date"));
+    model->setHeaderData(CURRENCY_NAME_COLUMN, Qt::Horizontal, tr("Name"));
+
+    view = new QTableView(this);
+    view->setModel(model);
+    view->setSelectionMode(QAbstractItemView::SingleSelection);
+    view->setSelectionBehavior(QAbstractItemView::SelectItems);
+    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    view->resizeColumnsToContents();
+    view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    view->verticalHeader()->hide();
+    view->setAlternatingRowColors(true);
+    view->hideColumn(ROWID_COLUMN);
+
+    const auto dateDelegate = new StyledDateDelegate(Settings::getDisplayFormat(), this);
+    view->setItemDelegateForColumn(EXPIRY_DATE_COLUMN, dateDelegate);
+}
+
+void CurrencyWidget::setupUI()
+{
+    // create a 3-column grid layout
+    int colL = 0; // left column
+    int colM = 1; // middle column
+    int colR = 2; // right column
+    int allColSpan = 3; // span all columns
+    int singleRowSpan = 1; // span only a single row
+    int row = 0;
+    auto gridLayout = new QGridLayout(this);
+
+    // Take-off and Landing Currency
+    gridLayout->addWidget(takeOffLandingHeaderLabel, row, colM, Qt::AlignCenter);
+    row++;
+    gridLayout->addWidget(getHorizontalLine(), row, colL, singleRowSpan, allColSpan);
+    row++;
+
+    gridLayout->addWidget(takeOffCountLabel, row, colL, Qt::AlignLeft);
+    gridLayout->addWidget(takeOffCountDisplayLabel, row, colR, Qt::AlignRight);
+    row++;
+
+    gridLayout->addWidget(landingCountLabel, row, colL, Qt::AlignLeft);
+    gridLayout->addWidget(landingCountDisplayLabel, row, colR, Qt::AlignRight);
+    row++;
+
+    gridLayout->addWidget(takeOffLandingExpiryLabel, row, colL, Qt::AlignLeft);
+    gridLayout->addWidget(takeOffLandingExpiryDisplayLabel, row, colR, Qt::AlignRight);
+    row++;
+
+    // Flight Time Limitations
+    gridLayout->addWidget(flightTimeHeaderLabel, row, colM, Qt::AlignCenter);
+    row++;
+    gridLayout->addWidget(getHorizontalLine(), row, colL, singleRowSpan, allColSpan);
+    row++;
+
+    gridLayout->addWidget(flightTime28DaysLabel, row, colL, Qt::AlignLeft);
+    gridLayout->addWidget(flightTime28DaysDisplayLabel, row, colR, Qt::AlignRight);
+    row++;
+
+    gridLayout->addWidget(flightTime365DaysLabel, row, colL, Qt::AlignLeft);
+    gridLayout->addWidget(flightTime365DaysDisplayLabel, row, colR, Qt::AlignRight);
+    row++;
+
+    gridLayout->addWidget(flightTimeCalendarYearLabel, row, colL, Qt::AlignLeft);
+    gridLayout->addWidget(flightTimeCalendarYearDisplayLabel, row, colR, Qt::AlignRight);
+    row++;
+
+    // Expiries (currencies table)
+    gridLayout->addWidget(expiriesHeaderLabel, row, colM, Qt::AlignCenter);
+    row++;
+    gridLayout->addWidget(getHorizontalLine(), row, colL, singleRowSpan, allColSpan);
+    row++;
+    gridLayout->addWidget(view, row, colL, singleRowSpan, allColSpan);
+
+    // set the layout active
+    layout = gridLayout;
+
+    // allocate a widget for date selection
+    calendar = new QCalendarWidget(this);
+    calendar->setVisible(false);
+    calendar->setWindowFlags(Qt::Dialog); // pop-up calendar
+
+
+    // connect signals
+    QObject::connect(view,	&QTableView::activated,
+                     this, &CurrencyWidget::editRequested);
+    QObject::connect(calendar, &QCalendarWidget::selectionChanged,
+                     this, &CurrencyWidget::newExpiryDateSelected);
+}
+
+void CurrencyWidget::fillTakeOffAndLandingCurrencies()
+{
+    const auto takeoff_landings = OPL::Statistics::countTakeOffLanding();
+    LOG << "Currencies: " << takeoff_landings;
+    if(takeoff_landings.isEmpty() || takeoff_landings.size() != 2)
+        return;
+
+    QList<QLabel*> displayLabels = {
+        takeOffCountDisplayLabel,
+        landingCountDisplayLabel
+    };
+
+    for(int i = 0; i < 2; i++) {
+        int count = takeoff_landings[i].toInt();
+        if(count < 3)
+            setLabelColour(displayLabels[i], Colour::Red);
+        displayLabels[i]->setText(displayLabels[i]->text().arg(count));
+    }
+    QDate expiration_date = OPL::Statistics::currencyTakeOffLandingExpiry();
+    if (expiration_date <= QDate::currentDate())
+        setLabelColour(takeOffLandingExpiryDisplayLabel, Colour::Red);
+    takeOffLandingExpiryDisplayLabel->setText(expiration_date.toString(Qt::TextDate));
+}
+
+void CurrencyWidget::fillFlightTimeLimitations()
+{
+    const QList<QPair<QLabel *, OPL::Statistics::TimeFrame>> limits =
+        {
+        { flightTime28DaysDisplayLabel, 		 OPL::Statistics::TimeFrame::Rolling28Days },
+        { flightTime365DaysDisplayLabel, 	 	 OPL::Statistics::TimeFrame::Rolling12Months },
+        { flightTimeCalendarYearDisplayLabel,    OPL::Statistics::TimeFrame::CalendarYear },
+        };
+
+    double ftlWarningThreshold = Settings::getFtlWarningThreshold();
+    for (const auto &pair : limits) {
+        int accruedMinutes = OPL::Statistics::totalTime(pair.second);
+        int limitMinutes = EasaFTL::getLimit(pair.second);
+        pair.first->setText(OPL::Time(accruedMinutes, m_format).toString());
+
+
+        if (accruedMinutes >= limitMinutes)
+            setLabelColour(pair.first, Colour::Red);
+        else if (accruedMinutes >= limitMinutes * ftlWarningThreshold)
+            setLabelColour(pair.first, Colour::Orange);
+    }
+}
+
+void CurrencyWidget::editRequested(const QModelIndex &index)
+{
+    if(index.column() == EXPIRY_DATE_COLUMN) {
+        expiryDateEditRequested(index);
+    }
+
+    if(index.column() == CURRENCY_NAME_COLUMN) {
+        displayNameEditRequested(index);
+    }
+}
+
+
+
+void CurrencyWidget::refresh()
+{
+    model->select();
+    fillTakeOffAndLandingCurrencies();
+    fillFlightTimeLimitations();
+
+    view->resizeColumnsToContents();
+    view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+}
+
+void CurrencyWidget::displayNameEditRequested(const QModelIndex &index)
+{
+    QString oldData = index.data().toString();
+    bool textEdited;
+    const QString text = QInputDialog::getText(
+        this,
+        tr("Edit Currency Name"),
+        tr("Please enter a name for this currency"),
+        QLineEdit::Normal,
+        oldData,
+        &textEdited);
+
+    if(textEdited) {
+        model->setData(index, text);
+        model->submitAll();
+    }
+}
+
+void CurrencyWidget::expiryDateEditRequested(const QModelIndex &index)
+{
+    const int selectedDate = index.data().toInt();
+
+    if(selectedDate > 0) {
+        const QSignalBlocker blocker(calendar);
+        calendar->setSelectedDate(QDate::fromJulianDay(selectedDate));
+        calendar->show();
+    } else {
+        calendar->show();
+    }
+    // calendars date selected signal is connected to newExpiryDateSelected()
+}
+
+void CurrencyWidget::newExpiryDateSelected()
+{
+    calendar->hide();
+    model->setData(view->selectionModel()->currentIndex(), calendar->selectedDate().toJulianDay());
+    model->submitAll();
+    model->select();
+}
+
+void CurrencyWidget::warnAboutExpiries()
+{
+    int warningThreshold = Settings::getCurrencyWarningThreshold();
+    if(warningThreshold < 1) {
+        return;
+    }
+
+    const QDate today = QDate::currentDate();
+
+    for(int i = 0; i < model->rowCount(); i++) {
+        const QModelIndex dateIndex = model->index(i, EXPIRY_DATE_COLUMN);
+        if(dateIndex.data().toInt() == 0) {
+            continue;
+        }
+        const auto expiryDate = QDate::fromJulianDay(dateIndex.data().toInt());
+        const auto warningDate = QDate::fromJulianDay(expiryDate.toJulianDay() - warningThreshold);
+
+        if(today >= warningDate) {
+            const QString dateString = expiryDate.toString(Qt::ISODate);
+            const QString nameString = model->index(i, CURRENCY_NAME_COLUMN).data().toString();
+            const QString daysLeft = QString::number(expiryDate.toJulianDay() - today.toJulianDay());
+
+            QString msg = tr("Your %1 expires in %2 days: <br><br><tt>%3</tt>").arg(nameString, daysLeft, dateString);
+            WARN(msg);
+        }
+    }
+}
+
+QFrame *CurrencyWidget::getHorizontalLine()
+{
+    QFrame* newFrame = new QFrame(this);
+    newFrame->setFrameShape(QFrame::HLine);
+    return newFrame;
+}

+ 84 - 0
src/gui/widgets/currencywidget.h

@@ -0,0 +1,84 @@
+#ifndef CURRENCYWIDGET_H
+#define CURRENCYWIDGET_H
+
+#include "src/opl.h"
+#include <QWidget>
+#include <QCalendarWidget>
+#include <QTableView>
+#include <QSqlTableModel>
+#include <QLabel>
+class CurrencyWidget : public QWidget
+{
+    Q_OBJECT
+    QLayout* layout;
+    QTableView *view;
+    QSqlTableModel *model;
+    QCalendarWidget *calendar;
+    QModelIndex lastSelection;
+    OPL::DateTimeFormat m_format;
+
+    int ROWID_COLUMN = 0;
+    int CURRENCY_NAME_COLUMN = 1;
+    int EXPIRY_DATE_COLUMN = 2;
+
+    void setupModelAndView();
+    void setupUI();
+    void fillTakeOffAndLandingCurrencies();
+    void fillFlightTimeLimitations();
+    void warnAboutExpiries();
+
+    void displayNameEditRequested(const QModelIndex &index);
+    void expiryDateEditRequested(const QModelIndex &index);
+
+    QFrame *getHorizontalLine();
+    QLabel *takeOffLandingHeaderLabel   		= new QLabel(tr("<b>Take-off and Landing Currency<\b>"), this);
+    QLabel *takeOffCountLabel					= new QLabel(tr("Take offs (last 90 days)"), this);
+    QLabel *takeOffCountDisplayLabel 			= new QLabel(QStringLiteral("<b>%1</b>"), this);
+    QLabel *landingCountLabel 					= new QLabel(tr("Landings (last 90 days)"), this);
+    QLabel *landingCountDisplayLabel			= new QLabel(QStringLiteral("<b>%1</b>"), this);
+    QLabel *takeOffLandingExpiryLabel   		= new QLabel(tr("3 Take-offs and Landings expiry date"), this);
+    QLabel *takeOffLandingExpiryDisplayLabel    = new QLabel(QStringLiteral("<b>%1</b>"), this);
+    QLabel *flightTimeHeaderLabel		   		= new QLabel(tr("<b>Flight Time Limitations</b>"), this);
+    QLabel *flightTime28DaysLabel 	    		= new QLabel(tr("Flight time (last 28 days)"), this);
+    QLabel *flightTime28DaysDisplayLabel 	    = new QLabel(QStringLiteral("<b>%1</b>"), this);
+    QLabel *flightTime365DaysLabel 	    		= new QLabel(tr("FLight time (last 365 days)"), this);
+    QLabel *flightTime365DaysDisplayLabel 	    = new QLabel(QStringLiteral("<b>%1</b>"), this);
+    QLabel *flightTimeCalendarYearLabel 		= new QLabel(tr("Flight time (this calendar year"),this);
+    QLabel *flightTimeCalendarYearDisplayLabel  = new QLabel(QStringLiteral("<b>%1</b>"), this);
+    QLabel *expiriesHeaderLabel			   		= new QLabel(tr("<b>Expiries<\b>"), this);
+
+    enum class Colour {Red, Orange, None};
+    inline void setLabelColour(QLabel* label, Colour colour)
+    {
+        switch (colour) {
+        case Colour::None:
+            label->setStyleSheet(QString());
+            break;
+        case Colour::Red:
+            label->setStyleSheet(QStringLiteral("color: red"));
+            break;
+        case Colour::Orange:
+            label->setStyleSheet(QStringLiteral("color: orange"));
+            break;
+        default:
+            label->setStyleSheet(QString());
+            break;
+        }
+    }
+
+
+private slots:
+    void editRequested(const QModelIndex &index);
+    void newExpiryDateSelected();
+
+public slots:
+    void refresh();
+
+public:
+    explicit CurrencyWidget(QWidget *parent = nullptr);
+
+signals:
+
+};
+
+#endif // CURRENCYWIDGET_H

+ 1 - 1
src/gui/widgets/debugwidget.cpp

@@ -300,6 +300,6 @@ void DebugWidget::on_debug2LineEdit_editingFinished()
 void DebugWidget::on_pushButton_clicked()
 {
     Settings::resetToDefaults();
-    Settings::write(Settings::Main::SetupComplete, false);
+    Settings::setSetupCompleted(false);
 }
 

+ 13 - 171
src/gui/widgets/homewidget.cpp

@@ -16,46 +16,23 @@
  *along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 #include "homewidget.h"
-#include "src/functions/statistics.h"
+#include "src/gui/widgets/currencywidget.h"
 #include "src/gui/widgets/totalswidget.h"
 #include "ui_homewidget.h"
 #include "src/database/database.h"
-#include "src/classes/time.h"
-#include "src/classes/settings.h"
-#include "src/database/row.h"
-
-// EASA FTL Limitations in minutes
-// 100 hours per 28 days
-static const int ROLLING_28_DAYS = 6000;
-// 900 hours per calendar year
-static const int CALENDAR_YEAR = 54000;
-// 1000 hours per rolling 12 months
-static const int ROLLING_12_MONTHS = 60000;
-// Todo: Encapsulate and plan to also use non-EASA (FAA,...) options
 
 HomeWidget::HomeWidget(QWidget *parent) :
     QWidget(parent),
     ui(new Ui::HomeWidget)
 {
     ui->setupUi(this);
-    today = QDate::currentDate();
-    ftlWarningThreshold = Settings::read(Settings::UserData::FtlWarningThreshold).toDouble();
-    currWarningThreshold = Settings::read(Settings::UserData::CurrWarningThreshold).toInt();
-    auto logo = QPixmap(OPL::Assets::LOGO);
+
+    const auto logo = QPixmap(OPL::Assets::LOGO);
     ui->logoLabel->setPixmap(logo);
     ui->welcomeLabel->setText(tr("Welcome to openPilotLog, %1!").arg(getLogbookOwnerName()));
 
-
-    limitationDisplayLabels = {
-        ui->TakeOffDisplayLabel,       ui->LandingsDisplayLabel,
-        ui->FlightTime28dDisplayLabel, ui->FlightTimeCalYearDisplayLabel,
-        ui->FlightTime12mDisplayLabel
-    };
-
-    LOG << "Filling Home Widget...";
     fillTotals();
-    fillSelectedCurrencies();
-    fillLimitations();
+    fillCurrencies();
 
     QObject::connect(DB,    &OPL::Database::dataBaseUpdated,
                      this,  &HomeWidget::onPilotsDatabaseChanged);
@@ -66,23 +43,20 @@ HomeWidget::~HomeWidget()
     delete ui;
 }
 
-void HomeWidget::refresh()
+void HomeWidget::fillTotals()
 {
-    LOG << "Updating HomeWidget...";
-    const auto label_list = this->findChildren<QLabel *>();
-    for (const auto label : label_list)
-        label->setVisible(true);
-    for (const auto &label : qAsConst(limitationDisplayLabels))
-        label->setStyleSheet(QString());
+    auto tw = new TotalsWidget(TotalsWidget::TotalTimeWidget, this);
+    ui->tabWidget->insertTab(0, tw, tr("Totals"));
+}
 
-    fillTotals();
-    fillSelectedCurrencies();
-    fillLimitations();
+void HomeWidget::fillCurrencies()
+{
+    auto cw = new CurrencyWidget(this);
+    ui->tabWidget->insertTab(1, cw, tr("Currencies"));
 }
 
 void HomeWidget::onPilotsDatabaseChanged(const OPL::DbTable table)
 {
-    // maybe logbook owner name has changed, redraw
     if (table == OPL::DbTable::Pilots)
         ui->welcomeLabel->setText(tr("Welcome to openPilotLog, %1!").arg(getLogbookOwnerName()));
 }
@@ -94,144 +68,12 @@ void HomeWidget::changeEvent(QEvent *event)
             ui->retranslateUi(this);
 }
 
-/*!
- * \brief HomeWidget::fillTotals Retreives a Database Summary of Total Flight Time via the OPL::Statistics::totals
- * function and parses the return to fill out the QLineEdits.
- */
-void HomeWidget::fillTotals()
-{
-    auto tw = new TotalsWidget(TotalsWidget::TotalTimeWidget, this);
-    ui->totalsStackedWidget->addWidget(tw);
-    ui->totalsStackedWidget->setCurrentWidget(tw);
-}
-
-void HomeWidget::fillCurrency(OPL::CurrencyName currency_name, QLabel* display_label)
-{
-    const auto currency_entry = DB->getCurrencyEntry(static_cast<int>(currency_name));
-
-    if (currency_name == OPL::CurrencyName::Custom1) {
-        ui->currCustom1Label->setText(currency_entry.getData().value(OPL::CurrencyEntry::CURRENCYNAME).toString());
-    } else if (currency_name == OPL::CurrencyName::Custom2) {
-        ui->currCustom2Label->setText(currency_entry.getData().value(OPL::CurrencyEntry::CURRENCYNAME).toString());
-    }
-
-    if (currency_entry.isValid()) {
-        const auto currency_date = QDate::fromString(currency_entry.getData().value(
-                                               OPL::CurrencyEntry::EXPIRYDATE).toString(),
-                                               Qt::ISODate);
-        display_label->setText(currency_date.toString(Qt::TextDate));
-        setLabelColour(display_label, Colour::None);
-
-        if (today >= currency_date) { // is expired
-            setLabelColour(display_label, Colour::Red);
-            return;
-        } else if (today.addDays(currWarningThreshold) >=currency_date) { // expires less than <currWarningThreshold> days from current Date
-
-            setLabelColour(display_label, Colour::Orange);
-        }
-    } else {
-        display_label->setText(tr("Invalid Date"));
-    }
-}
-
-/*!
- * \brief HomeWidget::fillSelectedCurrencies Checks whether a currency is selected and
- * retreives and displays relevant data.
- */
-void HomeWidget::fillSelectedCurrencies()
-{
-    fillCurrencyTakeOffLanding();
-
-    Settings::read(Settings::UserData::ShowLicCurrency).toBool() ?
-                fillCurrency(OPL::CurrencyName::Licence, ui->currLicDisplayLabel)
-              : hideLabels(ui->currLicLabel, ui->currLicDisplayLabel);
-    Settings::read(Settings::UserData::ShowTrCurrency).toBool() ?
-                fillCurrency(OPL::CurrencyName::TypeRating, ui->currTrDisplayLabel)
-              : hideLabels(ui->currTrLabel, ui->currTrDisplayLabel);
-    Settings::read(Settings::UserData::ShowLckCurrency).toBool() ?
-                fillCurrency(OPL::CurrencyName::LineCheck, ui->currLckDisplayLabel)
-              : hideLabels(ui->currLckLabel, ui->currLckDisplayLabel);
-    Settings::read(Settings::UserData::ShowMedCurrency).toBool() ?
-                fillCurrency(OPL::CurrencyName::Medical, ui->currMedDisplayLabel)
-              : hideLabels(ui->currMedLabel, ui->currMedDisplayLabel);
-    Settings::read(Settings::UserData::ShowCustom1Currency).toBool() ?
-                fillCurrency(OPL::CurrencyName::Custom1, ui->currCustom1DisplayLabel)
-              : hideLabels(ui->currCustom1Label, ui->currCustom1DisplayLabel);
-    Settings::read(Settings::UserData::ShowCustom1Currency).toBool() ?
-                fillCurrency(OPL::CurrencyName::Custom1, ui->currCustom1DisplayLabel)
-              : hideLabels(ui->currCustom1Label, ui->currCustom1DisplayLabel);
-    Settings::read(Settings::UserData::ShowCustom2Currency).toBool() ?
-                fillCurrency(OPL::CurrencyName::Custom2, ui->currCustom2DisplayLabel)
-              : hideLabels(ui->currCustom2Label, ui->currCustom2DisplayLabel);
-}
-
-/*!
- * \brief HomeWidget::fillCurrencyTakeOffLanding Uses OPL::Statistics::countTakeOffLandings to determine
- * the amount of Take-Offs and Landings in the last 90 days and displays data and notifications
- * as required.
- */
-void HomeWidget::fillCurrencyTakeOffLanding()
-{
-    const auto takeoff_landings = OPL::Statistics::countTakeOffLanding();
-    if(takeoff_landings.isEmpty())
-        return;
-
-    ui->TakeOffDisplayLabel->setText(takeoff_landings[0].toString());
-    if (takeoff_landings[0].toUInt() < 3)
-        setLabelColour(ui->TakeOffDisplayLabel, Colour::Red);
-    ui->LandingsDisplayLabel->setText(takeoff_landings[1].toString());
-    if (takeoff_landings[1].toUInt() < 3)
-        setLabelColour(ui->LandingsDisplayLabel, Colour::Red);
-
-    if (Settings::read(Settings::UserData::ShowToLgdCurrency).toBool()) {
-        QDate expiration_date = OPL::Statistics::currencyTakeOffLandingExpiry();
-        if (expiration_date <= QDate::currentDate())
-            setLabelColour(ui->currToLdgDisplayLabel, Colour::Red);
-        ui->currToLdgDisplayLabel->setText(expiration_date.toString(Qt::TextDate));
-    } else {
-        ui->currToLdgLabel->hide();
-        ui->currToLdgDisplayLabel->hide();
-    }
-}
-
-/*!
- * \brief HomeWidget::fillLimitations Queries OPL::Statistics to obtain information regarding cumulative
- * Flight Times and Calculates and Notifies about approaching Flight Time Limitations
- */
-void HomeWidget::fillLimitations()
-{
-    int minutes = OPL::Statistics::totalTime(OPL::Statistics::TimeFrame::Rolling28Days);
-    ui->FlightTime28dDisplayLabel->setText(OPL::Time(minutes).toString());
-    if (minutes >= ROLLING_28_DAYS) {
-        setLabelColour(ui->FlightTime28dDisplayLabel, Colour::Red);
-    } else if (minutes >= ROLLING_28_DAYS * ftlWarningThreshold) {
-        setLabelColour(ui->FlightTime28dDisplayLabel, Colour::Orange);
-    }
-
-    minutes = OPL::Statistics::totalTime(OPL::Statistics::TimeFrame::Rolling12Months);
-    ui->FlightTime12mDisplayLabel->setText(OPL::Time(minutes).toString());
-    if (minutes >= ROLLING_12_MONTHS) {
-        setLabelColour(ui->FlightTime12mDisplayLabel, Colour::Red);
-    } else if (minutes >= ROLLING_12_MONTHS * ftlWarningThreshold) {
-        setLabelColour(ui->FlightTime12mDisplayLabel, Colour::Orange);
-    }
-
-    minutes = OPL::Statistics::totalTime(OPL::Statistics::TimeFrame::CalendarYear);
-    ui->FlightTimeCalYearDisplayLabel->setText(OPL::Time(minutes).toString());
-    if (minutes >= CALENDAR_YEAR) {
-        setLabelColour(ui->FlightTimeCalYearDisplayLabel, Colour::Red);
-    } else if (minutes >= CALENDAR_YEAR * ftlWarningThreshold) {
-        setLabelColour(ui->FlightTimeCalYearDisplayLabel, Colour::Orange);
-    }
-}
-
-const QString HomeWidget::getLogbookOwnerName()
+const QString HomeWidget::getLogbookOwnerName() const
 {
     OPL::PilotEntry owner = DB->getLogbookOwner();
     QString name = owner.getFirstName();
     if(name.isEmpty()) {
         name = owner.getLastName();
     }
-    DEB << "owner name: " << name;
     return name;
 }

+ 3 - 8
src/gui/widgets/homewidget.h

@@ -18,12 +18,12 @@
 #ifndef HOMEWIDGET_H
 #define HOMEWIDGET_H
 
+#include "src/opl.h"
 #include <QWidget>
 #include <QStackedLayout>
 #include <QLabel>
 #include <QLineEdit>
 #include <QSettings>
-#include "src/database/database.h"
 
 namespace Ui {
 class HomeWidget;
@@ -62,10 +62,7 @@ private:
     double ftlWarningThreshold;
 
     void fillTotals();
-    void fillSelectedCurrencies();
-    void fillCurrencyTakeOffLanding();
-    void fillCurrency(OPL::CurrencyName currency_name, QLabel *display_label);
-    void fillLimitations();
+    void fillCurrencies();
 
     enum class Colour {Red, Orange, None};
     inline void setLabelColour(QLabel* label, Colour colour)
@@ -94,11 +91,9 @@ private:
     /*!
      * \brief Retreives the users first name from the database.
      */
-    const QString getLogbookOwnerName();
+    const QString getLogbookOwnerName() const;
 
 public slots:
-    void refresh();
-
     void onPilotsDatabaseChanged(const OPL::DbTable table);
 
 protected:

+ 8 - 394
src/gui/widgets/homewidget.ui

@@ -6,396 +6,24 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>1087</width>
-    <height>777</height>
+    <width>670</width>
+    <height>442</height>
    </rect>
   </property>
   <property name="windowTitle">
    <string>Form</string>
   </property>
-  <layout class="QGridLayout" name="gridLayout_4">
-   <item row="13" column="0">
-    <layout class="QGridLayout" name="gridLayout_2">
-     <item row="0" column="0">
-      <widget class="QLabel" name="FlightTime28dLabel">
-       <property name="text">
-        <string>Flight Time (last 28 days)</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="1">
-      <widget class="QLabel" name="FlightTime28dDisplayLabel">
-       <property name="layoutDirection">
-        <enum>Qt::LeftToRight</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="0">
-      <widget class="QLabel" name="FlightTimeCalYearLabel">
-       <property name="text">
-        <string>Flight Time (this calendar year)</string>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QLabel" name="FlightTimeCalYearDisplayLabel">
-       <property name="layoutDirection">
-        <enum>Qt::LeftToRight</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="0">
-      <widget class="QLabel" name="FlightTime12mLabel">
-       <property name="text">
-        <string>Flight Time (last 12 calendar months)</string>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="1">
-      <widget class="QLabel" name="FlightTime12mDisplayLabel">
-       <property name="layoutDirection">
-        <enum>Qt::LeftToRight</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-    </layout>
-   </item>
-   <item row="10" column="0">
-    <layout class="QGridLayout" name="gridLayout_3">
-     <item row="0" column="0">
-      <widget class="QLabel" name="TakeOffLabel">
-       <property name="text">
-        <string>Take offs (last 90 days)</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="1">
-      <widget class="QLabel" name="TakeOffDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="layoutDirection">
-        <enum>Qt::LeftToRight</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="0">
-      <widget class="QLabel" name="LandingsLabel">
-       <property name="text">
-        <string>Landings (last 90 days)</string>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QLabel" name="LandingsDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="layoutDirection">
-        <enum>Qt::LeftToRight</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="0">
-      <widget class="QLabel" name="currToLdgLabel">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Take-Off / Landing</string>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="1">
-      <widget class="QLabel" name="currToLdgDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="0">
-      <widget class="QLabel" name="currLicLabel">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Licence</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="1">
-      <widget class="QLabel" name="currLicDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="0">
-      <widget class="QLabel" name="currTrLabel">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Type Rating</string>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="1">
-      <widget class="QLabel" name="currTrDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="5" column="0">
-      <widget class="QLabel" name="currLckLabel">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Line Check</string>
-       </property>
-      </widget>
-     </item>
-     <item row="5" column="1">
-      <widget class="QLabel" name="currLckDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="6" column="0">
-      <widget class="QLabel" name="currMedLabel">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Medical</string>
-       </property>
-      </widget>
-     </item>
-     <item row="6" column="1">
-      <widget class="QLabel" name="currMedDisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="7" column="0">
-      <widget class="QLabel" name="currCustom1Label">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Custom1</string>
-       </property>
-      </widget>
-     </item>
-     <item row="7" column="1">
-      <widget class="QLabel" name="currCustom1DisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="8" column="0">
-      <widget class="QLabel" name="currCustom2Label">
-       <property name="font">
-        <font>
-         <italic>false</italic>
-        </font>
-       </property>
-       <property name="text">
-        <string>Custom2</string>
-       </property>
-      </widget>
-     </item>
-     <item row="8" column="1">
-      <widget class="QLabel" name="currCustom2DisplayLabel">
-       <property name="font">
-        <font>
-         <bold>true</bold>
-        </font>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-    </layout>
-   </item>
-   <item row="9" column="0">
-    <widget class="Line" name="line_4">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-    </widget>
-   </item>
-   <item row="8" column="0">
-    <widget class="QLabel" name="currencyLabel">
-     <property name="font">
-      <font>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Currency</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignBottom|Qt::AlignHCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="12" column="0">
-    <widget class="Line" name="line_3">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-    </widget>
-   </item>
-   <item row="5" column="0">
-    <widget class="QLabel" name="totalsLabel">
-     <property name="font">
-      <font>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Your Totals</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignBottom|Qt::AlignHCenter</set>
-     </property>
-    </widget>
-   </item>
-   <item row="4" column="0">
-    <widget class="Line" name="line_7">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-    </widget>
-   </item>
+  <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
     <widget class="QLabel" name="logoLabel">
      <property name="text">
-      <string/>
+      <string>[openPilotLog Logo]</string>
      </property>
      <property name="alignment">
       <set>Qt::AlignCenter</set>
      </property>
     </widget>
    </item>
-   <item row="6" column="0">
-    <widget class="Line" name="line">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="0">
-    <widget class="Line" name="line_2">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-    </widget>
-   </item>
    <item row="1" column="0">
     <widget class="QLabel" name="welcomeLabel">
      <property name="text">
@@ -406,27 +34,13 @@
      </property>
     </widget>
    </item>
-   <item row="11" column="0">
-    <widget class="QLabel" name="limitationsLabel">
-     <property name="font">
-      <font>
-       <bold>true</bold>
-      </font>
-     </property>
-     <property name="text">
-      <string>Limitations</string>
-     </property>
-     <property name="alignment">
-      <set>Qt::AlignBottom|Qt::AlignHCenter</set>
+   <item row="2" column="0">
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>-1</number>
      </property>
     </widget>
    </item>
-   <item row="7" column="0">
-    <widget class="QStackedWidget" name="totalsStackedWidget">
-     <widget class="QWidget" name="page"/>
-     <widget class="QWidget" name="page_2"/>
-    </widget>
-   </item>
   </layout>
  </widget>
  <resources/>

+ 188 - 0
src/gui/widgets/logbooktableeditwidget.cpp

@@ -0,0 +1,188 @@
+#include "logbooktableeditwidget.h"
+#include "src/classes/settings.h"
+#include "src/classes/styleddatedelegate.h"
+#include "src/classes/styledpilotdelegate.h"
+#include "src/classes/styledtimedelegate.h"
+#include "src/classes/styledtypedelegate.h"
+#include "src/database/database.h"
+#include "src/database/views/logbookviewinfo.h"
+#include "src/gui/dialogues/newflightdialog.h"
+#include "src/gui/dialogues/newsimdialog.h"
+
+LogbookTableEditWidget::LogbookTableEditWidget(QWidget *parent)
+    : TableEditWidget(Vertical, parent)
+{}
+
+
+// TableEditWidget implementation
+
+void LogbookTableEditWidget::setupModelAndView()
+{
+    m_logbookView = Settings::getLogbookView();
+    m_model = new QSqlTableModel(this, DB->database());
+    m_model->setTable(OPL::GLOBALS->getViewIdentifier(m_logbookView));
+    m_model->select();
+
+    const auto headers = OPL::LogbookViewInfo::getTableHeaders(m_logbookView);
+    for(int i = 0; i < headers.size(); i++) {
+        m_model->setHeaderData(i, Qt::Horizontal, headers[i]);
+    }
+
+    m_view->setModel(m_model);
+    m_view->setSelectionMode(QAbstractItemView::SingleSelection);
+    m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
+    m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    m_view->verticalHeader()->hide();
+    m_view->setAlternatingRowColors(true);
+    m_view->hideColumn(COL_ROWID);
+
+    setupDelegates();
+    m_view->resizeColumnsToContents();
+}
+
+void LogbookTableEditWidget::setupUI()
+{
+    TableEditWidget::setupUI();
+    m_addNewEntryPushButton->setText(tr("Add new Flight"));
+    m_deleteEntryPushButton->setText(tr("Delete selected Entry"));
+    m_filterWidget->hide();
+    m_stackedWidget->hide();
+
+    m_format = Settings::getDisplayFormat();
+}
+
+QString LogbookTableEditWidget::deleteErrorString(int rowId)
+{
+    return tr("<br>Unable to delete.<br><br>The following error has ocurred: %1"
+              ).arg(DB->lastError.text());
+}
+
+QString LogbookTableEditWidget::confirmDeleteString(int rowId)
+{
+    if(rowId > 0) {
+    const auto selectedEntry = DB->getFlightEntry(rowId);
+    return tr("The following flight will be deleted:<br><br><b><tt>"
+               "%1<br></b></tt><br><br>"
+               "Deleting flights is irreversible.<br>Do you want to proceed?"
+              ).arg(selectedEntry.getFlightSummary());
+
+    }
+
+    return tr("Deleting entries is irreversible.<br>Do you want to proceed?");
+}
+
+EntryEditDialog *LogbookTableEditWidget::getEntryEditDialog(QWidget *parent)
+{
+    return new NewFlightDialog(parent);
+}
+
+void LogbookTableEditWidget::filterTextChanged(const QString &filterString)
+{}
+
+void LogbookTableEditWidget::editEntryRequested(const QModelIndex &selectedIndex)
+{
+    showEditWidget();
+    const auto idx = m_view->selectionModel()->currentIndex();
+    const auto rowId = m_model->index(idx.row(), 0).data().toInt();
+    if(rowId > 0) {
+        auto nfd = NewFlightDialog(rowId, this);
+        m_stackedWidget->addWidget(&nfd);
+        m_stackedWidget->setCurrentWidget(&nfd);
+        nfd.exec();
+    } else {
+        auto nsd = NewSimDialog(rowId * -1, this);
+        m_stackedWidget->addWidget(&nsd);
+        m_stackedWidget->setCurrentWidget(&nsd);
+        nsd.exec();
+    }
+    hideEditWidget();
+}
+
+void LogbookTableEditWidget::deleteEntryRequested()
+{
+    const QModelIndex selectedIndex = m_view->selectionModel()->currentIndex();
+    if(!selectedIndex.isValid()) {
+        WARN(tr("No entry selected."));
+        return;
+    }
+    m_stackedWidget->hide();
+
+    int rowId = m_model->index(selectedIndex.row(), 0).data().toInt();
+    m_view->selectionModel()->reset();
+
+    // get user confirmation
+    QMessageBox confirm(this);
+    confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+    confirm.setDefaultButton(QMessageBox::No);
+    confirm.setIcon(QMessageBox::Question);
+    confirm.setWindowTitle(tr("Confirm Deletion"));
+
+    confirm.setText(confirmDeleteString(rowId));
+    if (confirm.exec() == QMessageBox::Yes) {
+        if(rowId > 0) {
+            const auto selectedEntry = DB->getFlightEntry(rowId);
+            if(!DB->remove(selectedEntry))
+                WARN(deleteErrorString(rowId));
+        } else {
+            const auto selectedEntry = DB->getSimEntry(rowId * - 1);
+            if(!DB->remove(selectedEntry))
+                WARN(deleteErrorString(rowId));
+        }
+    }
+}
+
+// private implementations
+
+void LogbookTableEditWidget::addSimulatorEntryRequested()
+{
+        showEditWidget();
+
+        auto nsd = NewSimDialog(this);
+        m_stackedWidget->addWidget(&nsd);
+        m_stackedWidget->setCurrentWidget(&nsd);
+        nsd.exec();
+
+        hideEditWidget();
+}
+
+void LogbookTableEditWidget::viewSelectionChanged(SettingsWidget::SettingSignal widget)
+{
+    for(auto i = m_defaultDelegates.constBegin(); i != m_defaultDelegates.constEnd(); i++) {
+        m_view->setItemDelegateForColumn(i.key(), i.value());
+        // should probably delete the old custom delegate, Qt docs say the
+        // view does not take ownership of the delegates so they might be leaking
+        // https://doc.qt.io/qt-6/qabstractitemdelegate.html
+    }
+
+    if(widget == SettingsWidget::SettingSignal::LogbookWidget)
+        setupModelAndView();
+}
+
+void LogbookTableEditWidget::setupDelegates()
+{
+    // minutes to hh:mm
+    const auto timeDelegate = new StyledTimeDelegate(m_format, m_model);
+    for(const auto col : OPL::LogbookViewInfo::getTimeColumns(m_logbookView)) {
+        m_defaultDelegates.insert(col, m_view->itemDelegateForColumn(col));
+        m_view->setItemDelegateForColumn(col, timeDelegate);
+    }
+
+    // julian day to Date Format
+    const int dateCol = OPL::LogbookViewInfo::getDateColumn(m_logbookView);
+    const auto dateDelegate = new StyledDateDelegate(Settings::getDisplayFormat(), m_model);
+    m_defaultDelegates.insert(dateCol, m_view->itemDelegateForColumn(dateCol));
+    m_view->setItemDelegateForColumn(dateCol, dateDelegate);
+
+    // pilot_id to names
+    const int pilCol = OPL::LogbookViewInfo::getPicColumn(m_logbookView);
+    const auto pilotDelegate = new StyledPilotDelegate(m_model);
+    m_defaultDelegates.insert(pilCol, m_view->itemDelegateForColumn(pilCol));
+    m_view->setItemDelegateForColumn(pilCol, pilotDelegate);
+
+    // tail_id to aircraft type and registration
+    const int typeCol = OPL::LogbookViewInfo::getTypeColumn(m_logbookView);
+    const auto typeDelegate = new StyledTypeDelegate(m_model);
+    m_defaultDelegates.insert(typeCol, m_view->itemDelegateForColumn(typeCol));
+    m_view->setItemDelegateForColumn(typeCol, typeDelegate);
+}

+ 67 - 0
src/gui/widgets/logbooktableeditwidget.h

@@ -0,0 +1,67 @@
+#ifndef LOGBOOKTABLEEDITWIDGET_H
+#define LOGBOOKTABLEEDITWIDGET_H
+
+#include "settingswidget.h"
+#include "tableeditwidget.h"
+#include "src/opl.h"
+#include <QObject>
+
+/*!
+ * \brief The LogbookTableEditWidget allows editing of logbook entries.
+ *
+ * \details This widget differs from the other TableEditWidget implementations in that
+ * instead of a table, it uses a [view](https://sqlite.org/lang_createview.html) as the source of the display model.
+ *
+ * The user can select a view from a list of available views in the SettingsWidget.
+ *
+ * Some of the selectable views aggregate data from the flights and simulators table. In those views, flight
+ * entries have positive row id's whereas the simulators have inverse (negative) row id's. This necessitates
+ * checking the row id's value before deciding on the apropriate EntryEditDialog.
+ */
+class LogbookTableEditWidget : public TableEditWidget
+{
+    Q_OBJECT
+public:
+    LogbookTableEditWidget() = delete;
+    explicit LogbookTableEditWidget(QWidget *parent = nullptr);
+
+private:
+    OPL::LogbookView m_logbookView;
+    OPL::DateTimeFormat m_format;
+
+    /*!
+     * \brief Set up the QStyledItemDelegate instances that transform the database values to user-readable values.
+     */
+    void setupDelegates();
+
+    static constexpr int COL_ROWID = 0;
+
+    // keep track of default and custom delegates set on certain columns
+    QHash<int, QAbstractItemDelegate*> m_defaultDelegates;
+
+    // TableEditWidget interface
+public:
+    virtual void setupModelAndView() override;
+    virtual void setupUI() override;
+    virtual QString deleteErrorString(int rowId) override;
+    virtual QString confirmDeleteString(int rowId) override;
+    virtual EntryEditDialog *getEntryEditDialog(QWidget *parent) override;
+
+public slots:
+    void viewSelectionChanged(SettingsWidget::SettingSignal widget);
+
+public slots:
+    virtual void filterTextChanged(const QString &filterString) override;
+
+    virtual void editEntryRequested(const QModelIndex &selectedIndex) override;
+    virtual void deleteEntryRequested() override;
+
+    /*!
+     * \brief add a new Simulator Entry to the datbase
+     * \details The Primary Entry handled by the LogbookTableEditWidget are Flights, which are stored in the flights table.
+     * However, this widget also handles adding simulator tables which are stored in the simulators table.
+     */
+    void addSimulatorEntryRequested();
+};
+
+#endif // LOGBOOKTABLEEDITWIDGET_H

+ 0 - 322
src/gui/widgets/logbookwidget.cpp

@@ -1,322 +0,0 @@
-/*
- *openPilotLog - A FOSS Pilot Logbook Application
- *Copyright (C) 2020-2023 Felix Turowsky
- *
- *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 <https://www.gnu.org/licenses/>.
- */
-#include "src/classes/time.h"
-#include "src/database/database.h"
-#include "logbookwidget.h"
-#include "ui_logbookwidget.h"
-#include "src/database/row.h"
-#include "src/database/database.h"
-#include "src/classes/settings.h"
-#include "src/gui/dialogues/newflightdialog.h"
-#include "src/gui/dialogues/newsimdialog.h"
-
-LogbookWidget::LogbookWidget(QWidget *parent) :
-    QWidget(parent),
-    ui(new Ui::LogbookWidget)
-{
-    ui->setupUi(this);
-
-    OPL::GLOBALS->fillViewNamesComboBox(ui->viewsComboBox);
-
-    //customContextMenu for tablewidget
-    menu  = new QMenu(this);
-    menu->addAction(ui->actionEdit_Flight);
-    menu->addAction(ui->actionDelete_Flight);
-
-    // Initalise the display Model and view
-    displayModel = new QSqlTableModel(this);
-    view = ui->tableView;
-
-    setupModelAndView(Settings::read(Settings::Main::LogbookView).toInt());
-    connectSignalsAndSlots();
-
-}
-
-LogbookWidget::~LogbookWidget()
-{
-    delete ui;
-}
-
-/*
- * Functions
- */
-
-/*!
- * \brief LogbookWidget::setupModelAndView configures the QTableView and populates the model with data
- * according to the current view.
- * \param view_id - retreived from Settings::Main::LogbookView
- */
-void LogbookWidget::setupModelAndView(int view_id)
-{
-    displayModel->setTable(OPL::GLOBALS->getViewIdentifier(OPL::DbViewName(view_id)));
-    displayModel->select();
-
-    view->setModel(displayModel);
-    view->setSelectionBehavior(QAbstractItemView::SelectRows);
-    view->setSelectionMode(QAbstractItemView::ExtendedSelection);
-    view->setEditTriggers(QAbstractItemView::NoEditTriggers);
-    view->setContextMenuPolicy(Qt::CustomContextMenu);
-    view->resizeColumnsToContents();
-    view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
-    view->verticalHeader()->hide();
-    view->setAlternatingRowColors(true);
-    view->hideColumn(0);
-    view->show();
-}
-
-void LogbookWidget::connectSignalsAndSlots()
-{
-    selectionModel = view->selectionModel();
-    QObject::connect(view->selectionModel(), &QItemSelectionModel::selectionChanged,
-                     this, &LogbookWidget::flightsTableView_selectionChanged);
-}
-
-const QString LogbookWidget::getFlightSummary(const OPL::FlightEntry &flight) const
-{
-    if(!flight.isValid())
-        return QString();
-
-    auto tableData = flight.getData();
-    QString flight_summary;
-    auto space = QLatin1Char(' ');
-    flight_summary.append(tableData.value(OPL::FlightEntry::DOFT).toString() + space);
-    flight_summary.append(tableData.value(OPL::FlightEntry::DEPT).toString() + space);
-    flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TOFB).toInt()).toString()
-                          + space);
-    flight_summary.append(OPL::Time(tableData.value(OPL::FlightEntry::TONB).toInt()).toString()
-                          + space);
-    flight_summary.append(tableData.value(OPL::FlightEntry::DEST).toString());
-
-    return flight_summary;
-}
-
-void LogbookWidget::changeEvent(QEvent *event)
-{
-    if (event != nullptr)
-        if(event->type() == QEvent::LanguageChange)
-            ui->retranslateUi(this);
-}
-
-/*!
- * \brief LogbookWidget::flightsTableView_selectionChanged saves the selected row(s)
- * to the selectedFlights member variable.
- */
-void LogbookWidget::flightsTableView_selectionChanged()
-{
-    selectedEntries.clear();
-    for (const auto& row : selectionModel->selectedRows()) {
-        selectedEntries.append(row.data().toInt());
-        DEB << "Selected Flight(s) with ID: " << selectedEntries;
-    }
-    if (selectedEntries.length() == 1) {
-        if (isFlight(selectedEntries.first()))
-            on_actionEdit_Flight_triggered();
-        else
-            on_actionEdit_Sim_triggered();
-    }
-}
-
-/*!
- * \brief If a row is selected, query information
- * about the affected row(s) and ask the user to confirm deletion.
- */
-void LogbookWidget::on_actionDelete_Flight_triggered()
-{
-    DEB << "Flights selected: " << selectedEntries.length();
-    if (selectedEntries.length() == 0) {
-        WARN(tr("<br>No flight selected.<br>"));
-        return;
-    } else if (selectedEntries.length() > 0 && selectedEntries.length() <= 10) {
-        QVector<OPL::FlightEntry> flights_list;
-
-        for (const auto &flight_id : qAsConst(selectedEntries)) {
-            flights_list.append(DB->getFlightEntry(flight_id));
-        }
-
-        QString flights_list_string;
-
-        for (auto &flight : flights_list) {
-            flights_list_string.append(getFlightSummary(flight));
-            flights_list_string.append(QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
-        }
-
-        QMessageBox confirm(this);
-        confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
-        confirm.setDefaultButton(QMessageBox::No);
-        confirm.setIcon(QMessageBox::Question);
-        confirm.setWindowTitle("Delete Flight");
-        confirm.setText(tr("The following flight(s) will be deleted:<br><br><b><tt>"
-                           "%1<br></b></tt>"
-                           "Deleting flights is irreversible.<br>Do you want to proceed?"
-                           ).arg(flights_list_string));
-        if (confirm.exec() == QMessageBox::Yes) {
-            for (auto& flight : flights_list) {
-                DEB << "Deleting flight: " << flight;
-                if(!DB->remove(flight)) {
-                    WARN(tr("<br>Unable to delete.<br><br>The following error has ocurred: %1"
-                                       ).arg(DB->lastError.text()));
-                    return;
-                }
-            }
-            INFO(tr("%1 flights have been deleted successfully."
-                                   ).arg(QString::number(selectedEntries.length())));
-            displayModel->select();
-        }
-    } else if (selectedEntries.length() > 10) {
-        QMessageBox confirm;
-        confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
-        confirm.setDefaultButton(QMessageBox::No);
-        confirm.setIcon(QMessageBox::Warning);
-        confirm.setWindowTitle("Delete Flight");
-        confirm.setText(tr("You have selected %1 flights.<br><br>"
-                           "Deleting flights is irreversible.<br><br>"
-                           "Are you sure you want to proceed?"
-                           ).arg(QString::number(selectedEntries.length())));
-        if(confirm.exec() == QMessageBox::Yes) {
-            if (!DB->removeMany(OPL::DbTable::Flights, selectedEntries)) {
-                WARN(tr("Unable to delete. No changes have been made to the database. The following error has ocurred:<br><br>%1").arg(DB->lastError.text()));
-                return;
-            }
-            INFO(tr("%1 flights have been deleted successfully."
-                                   ).arg(QString::number(selectedEntries.length())));
-            displayModel->select();
-        }
-        displayModel->select();
-    }
-}
-
-
-void LogbookWidget::on_tableView_customContextMenuRequested(const QPoint &pos)
-{
-    menu->popup(ui->tableView->viewport()->mapToGlobal(pos));
-}
-
-void LogbookWidget::on_tableView_doubleClicked()
-{
-    on_actionEdit_Flight_triggered();
-}
-
-void LogbookWidget::on_flightSearchComboBox_currentIndexChanged(int)
-{
-    //emit ui->showAllButton->clicked();
-}
-
-/*!
- * \brief LogbookWidget::refresh Refreshes the view to reflect changes in the database.
- */
-void LogbookWidget::refresh()
-{
-    displayModel->select();
-    view->resizeColumnsToContents();
-}
-
-void LogbookWidget::onLogbookWidget_viewSelectionChanged(SettingsWidget::SettingSignal signal)
-{
-    if (signal == SettingsWidget::SettingSignal::LogbookWidget)
-        setupModelAndView(Settings::read(Settings::Main::LogbookView).toInt());
-}
-
-//void LogbookWidget::on_showAllButton_clicked()
-//{
-//    ui->flightSearchLlineEdit->setText(QString());
-//    displayModel->setFilter(QString());
-//    displayModel->select();
-//}
-
-/*!
- * \brief LogbookWidget::on_flightSearchLlineEdit_textChanged applies a filter to the
- * display model allowing the user to search for flights by specified elements (date, aircraft,
- * Pilot Name)
- */
-void LogbookWidget::on_flightSearchLlineEdit_textChanged(const QString &arg1)
-{
-    if(arg1.length() == 0) {
-        displayModel->setFilter("");
-        displayModel->select();
-        return;
-    }
-
-    if (ui->flightSearchComboBox->currentIndex() < 3) {
-        displayModel->setFilter(FILTER_MAP.value(ui->flightSearchComboBox->currentIndex())
-                                + arg1 + QLatin1String("%\""));
-        return;
-    } else if (ui->flightSearchComboBox->currentIndex() == 3) { // registration
-        displayModel->setFilter(FILTER_MAP.value(ui->flightSearchComboBox->currentIndex())
-                                + arg1 + QLatin1String("%\""));
-        return;
-    } else if (ui->flightSearchComboBox->currentIndex() == 4) { // Name Pic
-        displayModel->setFilter(FILTER_MAP.value(ui->flightSearchComboBox->currentIndex())
-                                + arg1 + QLatin1String("%\""));
-        return;
-    }
-}
-
-/*!
- * \brief LogbookWidget::repopulateModel (public slot) - cleanly re-populates the model to cater for a change
- * to the database connection (for example, when a backup is created or restored)
- */
-void LogbookWidget::repopulateModel()
-{
-    // unset the current model and delete it to avoid leak
-    view->setModel(nullptr);
-    delete displayModel;
-    // create a new model and populate it
-    displayModel = new QSqlTableModel(this);
-    setupModelAndView(Settings::read(Settings::Main::LogbookView).toInt());
-    connectSignalsAndSlots();
-}
-
-void LogbookWidget::on_viewsComboBox_currentIndexChanged(int index)
-{
-    setupModelAndView(index);
-}
-
-void LogbookWidget::on_actionEdit_Flight_triggered()
-{
-    if(selectedEntries.length() == 1){
-        NewFlightDialog nff(selectedEntries.first(), this);
-        ui->stackedWidget->addWidget(&nff);
-        ui->stackedWidget->setCurrentWidget(&nff);
-        nff.setWindowFlag(Qt::Widget);
-        nff.exec();
-        displayModel->select();
-    } else if (selectedEntries.isEmpty()) {
-        WARN(tr("<br>No flight selected.<br>"));
-    } else {
-        WARN(tr("<br>More than one flight selected."
-                               "<br><br>Editing multiple entries is not yet supported."));
-    }
-}
-
-void LogbookWidget::on_actionEdit_Sim_triggered()
-{
-    if (selectedEntries.length() == 1) {
-        NewSimDialog nsd((selectedEntries.first() * -1), this); // simulator entries have inverse row ID's in the model
-        ui->stackedWidget->addWidget(&nsd);
-        ui->stackedWidget->setCurrentWidget(&nsd);
-        nsd.setWindowFlag(Qt::Widget);
-        nsd.exec();
-        displayModel->select();
-    } else if (selectedEntries.isEmpty()) {
-        WARN(tr("<br>No flight selected.<br>"));
-    } else {
-        WARN(tr("<br>More than one flight selected."
-                               "<br><br>Editing multiple entries is not yet supported."));
-    }
-}
-

+ 0 - 116
src/gui/widgets/logbookwidget.h

@@ -1,116 +0,0 @@
-/*
- *openPilotLog - A FOSS Pilot Logbook Application
- *Copyright (C) 2020-2023 Felix Turowsky
- *
- *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 <https://www.gnu.org/licenses/>.
- */
-#ifndef LOGBOOKWIDGET_H
-#define LOGBOOKWIDGET_H
-
-#include <QWidget>
-#include <QItemSelection>
-#include <QSqlTableModel>
-#include <QMessageBox>
-#include <QDebug>
-#include <QMenu>
-#include <QTableView>
-#include "src/database/flightentry.h"
-#include "src/gui/widgets/settingswidget.h"
-
-namespace Ui {
-class LogbookWidget;
-}
-
-/*!
- * \brief The LogbookWidget displays data from the database in a QSqlTableView fed by a QSqlQuery Model
- *
- * \details The LogbookWidget is the primary display interface for flights logged in the database. It fetches and stores
- * flight data from the database via a QSqlQueryModel and displays it in a QTableView. With the way the flight data is
- * written in the database, it would not be human-readable, so some processing is done on the database side to present
- * a nicely formatted, human-readable display. This is achieved by means of a [SQL View](https://sqlite.org/lang_createview.html).
- *
- * The user can select a view from a list of available views in the SettingsWidget.
- *
- */
-class LogbookWidget : public QWidget
-{
-    Q_OBJECT
-
-public:
-    explicit LogbookWidget(QWidget *parent = nullptr);
-    ~LogbookWidget();
-
-private slots:
-    void flightsTableView_selectionChanged();
-    void on_tableView_customContextMenuRequested(const QPoint &pos);
-    void on_actionDelete_Flight_triggered();
-
-    void on_tableView_doubleClicked();
-    void on_flightSearchLlineEdit_textChanged(const QString &arg1);
-    void on_flightSearchComboBox_currentIndexChanged(int);
-
-    void on_viewsComboBox_currentIndexChanged(int index);
-
-    void on_actionEdit_Flight_triggered();
-    void on_actionEdit_Sim_triggered();
-
-public slots:
-    void refresh();
-    void onLogbookWidget_viewSelectionChanged(SettingsWidget::SettingSignal signal);
-    void repopulateModel();
-
-private:
-    Ui::LogbookWidget *ui;
-
-    QTableView* view;
-
-    QSqlTableModel* displayModel;
-
-    QItemSelectionModel* selectionModel;
-
-    QMenu* menu;
-
-    QList<int> selectedEntries;
-
-    void setupModelAndView(int view_id);
-    void connectSignalsAndSlots();
-
-    const QString getFlightSummary(const OPL::FlightEntry &flight) const;
-
-    /*!
-     * \brief isFlight Determines whether an entry shown in a view is a Flight or a Simulator.
-     * \param model_row_id the row id in the QSqlTableModel used for displaying
-     * \details In the composite views (SQL UNION) with Simulators included, the row_id of the
-     * simulator entries is inverted to a negative value. A positive row id is thus a row id from
-     * the flights table, whereas a negative rowid is a row id from the simulators table.
-     */
-    inline bool isFlight(int model_row_id) { return model_row_id > 0; }
-
-    const static inline QHash<int, QString> FILTER_MAP = {
-        {0, QStringLiteral("Date LIKE \"%")},
-        {1, QStringLiteral("Dept LIKE \"%")},
-        {2, QStringLiteral("Dest LIKE \"%")},
-        {3, QStringLiteral("Registration LIKE \"%")},
-        {4, QStringLiteral("\"Name PIC\" LIKE \"%")}
-    };
-    const static inline QRegularExpression NON_WORD_CHAR = QRegularExpression("\\W");
-
-protected:
-    /*!
-     * \brief Handles change events, like updating the UI to new localisation
-     */
-    void changeEvent(QEvent* event) override;
-};
-
-#endif // LOGBOOKWIDGET_H

+ 0 - 169
src/gui/widgets/logbookwidget.ui

@@ -1,169 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>LogbookWidget</class>
- <widget class="QWidget" name="LogbookWidget">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>1280</width>
-    <height>720</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Form</string>
-  </property>
-  <layout class="QGridLayout" name="gridLayout">
-   <item row="1" column="0">
-    <widget class="QTableView" name="tableView">
-     <property name="font">
-      <font/>
-     </property>
-     <property name="styleSheet">
-      <string notr="true"/>
-     </property>
-     <property name="selectionMode">
-      <enum>QAbstractItemView::SingleSelection</enum>
-     </property>
-     <property name="selectionBehavior">
-      <enum>QAbstractItemView::SelectRows</enum>
-     </property>
-    </widget>
-   </item>
-   <item row="2" column="0">
-    <widget class="QStackedWidget" name="stackedWidget">
-     <widget class="QWidget" name="defaultPage">
-      <layout class="QGridLayout" name="gridLayout_2">
-       <item row="0" column="0">
-        <widget class="QLabel" name="flightSearchLabel">
-         <property name="minimumSize">
-          <size>
-           <width>200</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Search Flight</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-         </property>
-        </widget>
-       </item>
-       <item row="0" column="1">
-        <widget class="QLineEdit" name="flightSearchLlineEdit">
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the searchterm you want to filter your flights by.&lt;/p&gt;&lt;p&gt;For dates, make sure to use the format YYYY-MM-DD&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="whatsThis">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="flightSearchInLabel">
-         <property name="minimumSize">
-          <size>
-           <width>200</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Search by</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="1">
-        <widget class="QComboBox" name="flightSearchComboBox">
-         <item>
-          <property name="text">
-           <string>Date of Flight</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Departure Airport</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Destination Airport</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Aircraft Registration</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Pilot Name</string>
-          </property>
-         </item>
-        </widget>
-       </item>
-       <item row="2" column="0" colspan="2">
-        <widget class="QLabel" name="label">
-         <property name="text">
-          <string>Select a Flight from the list to show or edit details.</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignCenter</set>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-    </widget>
-   </item>
-   <item row="0" column="0">
-    <widget class="QComboBox" name="viewsComboBox"/>
-   </item>
-  </layout>
-  <widget class="QLabel" name="spacerLabel">
-   <property name="geometry">
-    <rect>
-     <x>284</x>
-     <y>410</y>
-     <width>200</width>
-     <height>16</height>
-    </rect>
-   </property>
-   <property name="minimumSize">
-    <size>
-     <width>200</width>
-     <height>0</height>
-    </size>
-   </property>
-   <property name="text">
-    <string/>
-   </property>
-   <property name="alignment">
-    <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-   </property>
-  </widget>
-  <action name="actionEdit_Flight">
-   <property name="text">
-    <string>Edit Flight</string>
-   </property>
-  </action>
-  <action name="actionDelete_Flight">
-   <property name="text">
-    <string>Delete Flight</string>
-   </property>
-  </action>
-  <action name="actionEdit_Sim">
-   <property name="text">
-    <string>Edit_Sim</string>
-   </property>
-   <property name="toolTip">
-    <string>Edit an existing Simulator Entry</string>
-   </property>
-  </action>
- </widget>
- <resources/>
- <connections/>
-</ui>

+ 112 - 0
src/gui/widgets/pilottableeditwidget.cpp

@@ -0,0 +1,112 @@
+#include "pilottableeditwidget.h"
+#include "src/database/database.h"
+#include "src/gui/dialogues/entryeditdialog.h"
+#include "src/gui/dialogues/newpilotdialog.h"
+#include "src/opl.h"
+#include <QGridLayout>
+
+PilotTableEditWidget::PilotTableEditWidget(QWidget *parent)
+    : TableEditWidget(Horizontal, parent)
+{}
+
+void PilotTableEditWidget::setupModelAndView()
+{
+    m_model = new QSqlTableModel(this, DB->database());
+    m_model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Pilots));
+    m_model->select();
+    m_model->setHeaderData(COL_LASTNAME, Qt::Horizontal, tr("Last Name"));
+    m_model->setHeaderData(COL_FIRSTNAME, Qt::Horizontal, tr("First Name"));
+    m_model->setHeaderData(COL_COMPANY, Qt::Horizontal, tr("Company"));
+    m_model->setFilter(QStringLiteral("%1 > 1").arg(OPL::PilotEntry::ROWID)); // hide self
+
+    m_view->setModel(m_model);
+    m_view->setSelectionMode(QAbstractItemView::SingleSelection);
+    m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
+    m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    m_view->resizeColumnsToContents();
+    m_view->verticalHeader()->hide();
+    m_view->setAlternatingRowColors(true);
+    for(const int i : COLS_TO_HIDE)
+        m_view->hideColumn(i);
+
+}
+
+void PilotTableEditWidget::setupUI()
+{
+    // the base class does most of the setup
+    TableEditWidget::setupUI();
+
+    // only need to set the table specific labels and combo box items
+    m_addNewEntryPushButton->setText(tr("Add New Pilot"));
+    m_deleteEntryPushButton->setText(tr("Delete Selected Pilot"));
+    m_filterSelectionComboBox->addItems(FILTER_COLUMNS);
+}
+
+EntryEditDialog *PilotTableEditWidget::getEntryEditDialog(QWidget *parent)
+{
+    return new NewPilotDialog(QString(), parent);
+}
+
+QString PilotTableEditWidget::deleteErrorString(int pilotId)
+{
+    const QList<int> foreign_key_constraints = DB->getForeignKeyConstraints(pilotId,
+                                                                            OPL::DbTable::Pilots);
+    QList<OPL::FlightEntry> constrained_flights;
+    for (const auto &row_id : foreign_key_constraints) {
+        constrained_flights.append(DB->getFlightEntry(row_id));
+    }
+
+    // the error is a database error
+    if (constrained_flights.isEmpty()) {
+        return(tr("<br>Unable to delete.<br><br>The following error has ocurred:<br>%1"
+                ).arg(DB->lastError.text()));
+    } else {
+        // the error is a foreign key constraint
+        QString constrained_flights_string;
+        for (int i=0; i<constrained_flights.length(); i++) {
+            constrained_flights_string.append(constrained_flights[i].getFlightSummary() + QStringLiteral("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
+            if (i>10) {
+                constrained_flights_string.append("<br>[...]<br>");
+                break;
+            }
+        }
+        return(tr("Unable to delete.<br><br>"
+                "This is most likely the case because a flight exists with the Pilot "
+                "you are trying to delete as PIC.<br><br>"
+                "%1 flight(s) with this pilot have been found:<br><br><br><b><tt>"
+                "%2"
+                "</b></tt><br><br>You have to change or remove the conflicting flight(s) "
+                "before removing this pilot from the database.<br><br>"
+                ).arg(QString::number(constrained_flights.length()),
+                      constrained_flights_string));
+    }
+}
+
+QString PilotTableEditWidget::confirmDeleteString(int rowId)
+{
+    const auto entry = DB->getPilotEntry(rowId);
+    return tr("You are deleting the following pilot:<br><br><b><tt>"
+              "%1, %2</b></tt><br><br>Are you sure?").arg(entry.getLastName(), entry.getFirstName());
+}
+
+void PilotTableEditWidget::filterTextChanged(const QString &filterText)
+{
+    if(filterText.isEmpty()) {
+        m_model->setFilter(QStringLiteral("%1 > 1").arg(OPL::PilotEntry::ROWID)); // hide self
+        return;
+    }
+
+    int i = m_filterSelectionComboBox->currentIndex();
+    const QString filter =
+        QLatin1Char('\"')
+        + FILTER_COLUMN_NAMES.at(i)
+        + QLatin1String("\" LIKE '%")
+        + filterText
+        + QLatin1String("%' AND ")
+        + OPL::PilotEntry::ROWID
+        + QLatin1String(" > 1");
+    m_model->setFilter(filter);
+}
+
+

+ 52 - 0
src/gui/widgets/pilottableeditwidget.h

@@ -0,0 +1,52 @@
+#ifndef PILOTTABLEEDITWIDGET_H
+#define PILOTTABLEEDITWIDGET_H
+
+#include "tableeditwidget.h"
+#include "src/database/pilotentry.h"
+
+class PilotTableEditWidget : public TableEditWidget
+{
+    Q_OBJECT
+public:
+    PilotTableEditWidget(QWidget *parent = nullptr);
+
+    virtual void setupModelAndView() override;
+    virtual void setupUI() override;
+    virtual EntryEditDialog *getEntryEditDialog(QWidget *parent = nullptr) override;
+
+
+private:
+    static constexpr int COL_ROWID = 0;
+    static constexpr int COL_LASTNAME = 1;
+    static constexpr int COL_FIRSTNAME = 2;
+    static constexpr int COL_COMPANY = 4;
+    static constexpr int COLS_TO_HIDE[5] = {0, 3, 5, 6, 7};
+
+    const QString COLUMN_1_NAME = tr("First Name");
+    const QString COLUMN_2_NAME = tr("Last Name");
+    const QString COLUMN_3_NAME = tr("Company");
+
+    const QStringList FILTER_COLUMNS = { COLUMN_1_NAME, COLUMN_2_NAME, COLUMN_3_NAME };
+    const static inline QStringList FILTER_COLUMN_NAMES = {
+                                                            OPL::PilotEntry::FIRSTNAME,
+                                                            OPL::PilotEntry::LASTNAME,
+                                                            OPL::PilotEntry::COMPANY };
+
+    /*!
+    * \brief Informs the user that deleting a Pilot has been unsuccessful
+    *
+    * \details Normally, when one of these entries can not be deleted, it is because of
+    * a [foreign key constraint](https://sqlite.org/foreignkeys.html), meaning that a flight
+    * is associated with the Pilot that was supposed to be deleted as Pilot-in-command.
+    *
+    * This function is used to inform the user and give hints on how to solve the problem.
+    */
+    virtual QString deleteErrorString(int pilotId) override;
+
+    virtual QString confirmDeleteString(int rowId) override;
+
+private slots:
+    virtual void filterTextChanged(const QString &filterText) override;
+};
+
+#endif // PILOTTABLEEDITWIDGET_H

+ 47 - 261
src/gui/widgets/settingswidget.cpp

@@ -23,7 +23,6 @@
 #include "src/classes/settings.h"
 #include "src/database/database.h"
 #include "src/opl.h"
-#include "src/functions/datetime.h"
 #include "src/gui/widgets/backupwidget.h"
 
 SettingsWidget::SettingsWidget(QWidget *parent) :
@@ -36,7 +35,6 @@ SettingsWidget::SettingsWidget(QWidget *parent) :
     loadBackupWidget();
     loadPreviousExperienceWidget();
     setupComboBoxes();
-    setupDateEdits();
     setupValidators();
     readSettings();
 }
@@ -62,41 +60,9 @@ void SettingsWidget::setupComboBoxes(){
         OPL::GLOBALS->loadPilotFunctios(ui->functionComboBox);
         OPL::GLOBALS->fillViewNamesComboBox(ui->logbookViewComboBox);
         OPL::GLOBALS->fillLanguageComboBox(ui->languageComboBox);
-    }
-}
 
-
-void SettingsWidget::setupDateEdits()
-{
-    // Read Display Format Setting
-    int date_format_index = Settings::read(Settings::Main::DateFormat).toInt();
-    const QString date_format_string = OPL::DateTime::getFormatString(
-                static_cast<OPL::DateFormat>(date_format_index));
-    const auto date_edits = this->findChildren<QDateEdit*>();
-    for (const auto &date_edit : date_edits) {
-        date_edit->setDisplayFormat(date_format_string);
-    }
-    // Fill currencies
-    const QList<QPair<OPL::CurrencyName, QDateEdit*>> currencies_list = {
-        {OPL::CurrencyName::Licence,    ui->currLicDateEdit},
-        {OPL::CurrencyName::TypeRating, ui->currTrDateEdit},
-        {OPL::CurrencyName::LineCheck,  ui->currLckDateEdit},
-        {OPL::CurrencyName::Medical,    ui->currMedDateEdit},
-        {OPL::CurrencyName::Custom1,    ui->currCustom1DateEdit},
-        {OPL::CurrencyName::Custom2,    ui->currCustom2DateEdit},
-    };
-    for (const auto &pair : currencies_list) {
-        const QSignalBlocker signal_blocker(pair.second);
-        const auto entry = DB->getCurrencyEntry(static_cast<int>(pair.first));
-        if (entry.isValid()) { // set date
-            const auto date = QDate::fromString(
-                entry.getData().value(OPL::CurrencyEntry::EXPIRYDATE).toString(),
-                        Qt::ISODate);
-            if(date.isValid())
-                pair.second->setDate(date);
-        } else { // set current date
-            pair.second->setDate(QDate::currentDate());
-        }
+        // Set up the currency warning threshold spin box
+        ui->currencyWarningDaysSpinBox->setValue(Settings::getCurrencyWarningThreshold());
     }
 }
 
@@ -135,29 +101,18 @@ void SettingsWidget::readSettings()
     ui->emailLineEdit->setText(user_data.value(OPL::PilotEntry::EMAIL).toString());
 
     // FLight Logging Tab
-    ui->functionComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Function).toInt());
-    ui->rulesComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::LogIFR).toInt());
-    ui->approachComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::Approach).toInt());
-    ui->nightComboBox->setCurrentIndex(Settings::read(Settings::FlightLogging::NightLoggingEnabled).toInt());
-    ui->prefixLineEdit->setText(Settings::read(Settings::FlightLogging::FlightNumberPrefix).toString());
-
-    ui->logbookViewComboBox->setCurrentIndex(Settings::read(Settings::Main::LogbookView).toInt());
-    ui->aliasComboBox->setCurrentIndex(Settings::read(Settings::UserData::DisplaySelfAs).toInt());
-
-    // Currencies Tab
-    ui->currToLdgCheckBox->setChecked(Settings::read(Settings::UserData::ShowToLgdCurrency).toBool());
-    ui->currLicCheckBox->setChecked(Settings::read(Settings::UserData::ShowLicCurrency).toBool());
-    ui->currTrCheckBox->setChecked(Settings::read(Settings::UserData::ShowTrCurrency).toBool());
-    ui->currLckCheckBox->setChecked(Settings::read(Settings::UserData::ShowLckCurrency).toBool());
-    ui->currMedCheckBox->setChecked(Settings::read(Settings::UserData::ShowMedCurrency).toBool());
-    ui->currCustom1CheckBox->setChecked(Settings::read(Settings::UserData::ShowCustom1Currency).toBool());
-    ui->currCustom2CheckBox->setChecked(Settings::read(Settings::UserData::ShowCustom2Currency).toBool());
-    ui->currCustom1LineEdit->setText(Settings::read(Settings::UserData::Custom1CurrencyName).toString());
-    ui->currCustom2LineEdit->setText(Settings::read(Settings::UserData::Custom2CurrencyName).toString());
+    ui->functionComboBox->setCurrentIndex(static_cast<int>(Settings::getPilotFunction()));
+    ui->rulesComboBox->setCurrentIndex(Settings::getLogIfr());
+    ui->approachComboBox->setCurrentText(Settings::getApproachType());
+    ui->nightComboBox->setCurrentIndex(Settings::getNightLoggingEnabled());
+    ui->prefixLineEdit->setText(Settings::getFlightNumberPrefix());
+
+    ui->logbookViewComboBox->setCurrentIndex(static_cast<int>(Settings::getLogbookView()));
+    ui->aliasComboBox->setCurrentIndex(Settings::getShowSelfAs());
 
     // Misc Tab
-    ui->acftSortComboBox->setCurrentIndex(Settings::read(Settings::UserData::TailSortColumn).toInt());
-    ui->pilotSortComboBox->setCurrentIndex(Settings::read(Settings::UserData::PilotSortColumn).toInt());
+    ui->acftSortComboBox->setCurrentIndex(Settings::getTailSortColumn());
+    ui->pilotSortComboBox->setCurrentIndex(Settings::getPilotSortColumn());
 
     // Don't emit signals for OPL::Style changes during setup
     const QSignalBlocker style_blocker(ui->styleComboBox);
@@ -165,10 +120,10 @@ void SettingsWidget::readSettings()
     const QSignalBlocker font_blocker_2(ui->fontComboBox);
     const QSignalBlocker font_blocker_3(ui->fontCheckBox);
 
-    ui->styleComboBox->setCurrentText(Settings::read(Settings::Main::Style).toString());
-    ui->fontSpinBox->setValue(Settings::read(Settings::Main::FontSize).toUInt());
-    ui->fontComboBox->setCurrentFont(QFont(Settings::read(Settings::Main::Font).toString()));
-    bool use_system_font = Settings::read(Settings::Main::UseSystemFont).toBool();
+    ui->styleComboBox->setCurrentText(Settings::getApplicationStyle());
+    ui->fontSpinBox->setValue(Settings::getApplicationFontSize());
+    ui->fontComboBox->setCurrentFont(QFont(Settings::getApplicationFontName()));
+    bool use_system_font = Settings::getUseSystemFont();
     ui->fontCheckBox->setChecked(use_system_font);
     if (!use_system_font) {
         ui->fontComboBox->setEnabled(true);
@@ -179,7 +134,6 @@ void SettingsWidget::readSettings()
 void SettingsWidget::setupValidators()
 {
     ui->phoneLineEdit->setValidator(new QRegularExpressionValidator(OPL::RegEx::RX_PHONE_NUMBER, ui->phoneLineEdit));
-    ui->emailLineEdit->setValidator(new QRegularExpressionValidator(OPL::RegEx::RX_EMAIL_ADDRESS, ui->emailLineEdit));
 }
 
 /*!
@@ -262,44 +216,43 @@ void SettingsWidget::on_phoneLineEdit_editingFinished()
 
 void SettingsWidget::on_aliasComboBox_currentIndexChanged(int index)
 {
-    Settings::write(Settings::UserData::DisplaySelfAs, index);
+    Settings::setShowSelfAs(index);
     updatePersonalDetails();
 }
 
 void SettingsWidget::on_functionComboBox_currentIndexChanged(int arg1)
 {
-    Settings::write(Settings::FlightLogging::Function, arg1);
+    Settings::setPilotFunction(OPL::PilotFunction(arg1));
 }
 
 void SettingsWidget::on_rulesComboBox_currentIndexChanged(int arg1)
 {
-    Settings::write(Settings::FlightLogging::LogIFR, arg1);
+    Settings::setLogIfr(arg1);
 }
 
 void SettingsWidget::on_approachComboBox_currentIndexChanged(int arg1)
 {
-    Settings::write(Settings::FlightLogging::Approach, arg1);
+    Settings::setApproachType(ui->approachComboBox->currentText());
 }
 
 void SettingsWidget::on_nightComboBox_currentIndexChanged(int index)
 {
-    Settings::write(Settings::FlightLogging::NightLoggingEnabled, index);
+    Settings::setNightLoggingEnabled(index);
     switch (index) {
     case 1:
-        Settings::write(Settings::FlightLogging::NightAngle, -6);
+        Settings::setNightAngle(-6);
         break;
     case 2:
-        Settings::write(Settings::FlightLogging::NightAngle, 0);
+        Settings::setNightAngle(0);
         break;
     default:
-        Settings::write(Settings::FlightLogging::NightAngle, -6);
+        Settings::setNightAngle(-6);
     }
 }
 
 void SettingsWidget::on_prefixLineEdit_textChanged(const QString &arg1)
 {
-    Settings::write(Settings::FlightLogging::FlightNumberPrefix, arg1);
-
+    Settings::setFlightNumberPrefix(arg1);
 }
 
 /*
@@ -308,18 +261,19 @@ void SettingsWidget::on_prefixLineEdit_textChanged(const QString &arg1)
 
 void SettingsWidget::on_logbookViewComboBox_currentIndexChanged(int index)
 {
-    Settings::write(Settings::Main::LogbookView, index);
+//    Settings::write(Settings::Main::LogbookView, index);
+    Settings::setLogbookView(OPL::LogbookView(index));
     emit settingChanged(SettingSignal::LogbookWidget);
 }
 void SettingsWidget::on_pilotSortComboBox_currentIndexChanged(int index)
 {
-    Settings::write(Settings::UserData::PilotSortColumn, index);
+    Settings::setPilotSortColumn(index);
     emit settingChanged(PilotsWidget);
 }
 
 void SettingsWidget::on_acftSortComboBox_currentIndexChanged(int index)
 {
-    Settings::write(Settings::UserData::TailSortColumn, index);
+    Settings::setTailSortColumn(index);
     emit settingChanged(AircraftWidget);
 }
 
@@ -404,14 +358,16 @@ void SettingsWidget::on_styleComboBox_currentTextChanged(const QString& new_styl
 {
     if (new_style_setting == QLatin1String("Dark-Palette")) {
         OPL::Style::setStyle(OPL::Style::darkPalette());
-        Settings::write(Settings::Main::Style, new_style_setting);
+//        Settings::write(Settings::Main::Style, new_style_setting);
+        Settings::setApplicationStyle(new_style_setting);
         emit settingChanged(MainWindow);
         return;
     }
     for (const auto &style_name : OPL::Style::styles) {
         if (new_style_setting == style_name) {
             OPL::Style::setStyle(style_name);
-            Settings::write(Settings::Main::Style, new_style_setting);
+//            Settings::write(Settings::Main::Style, new_style_setting);
+            Settings::setApplicationStyle(style_name);
             emit settingChanged(MainWindow);
             return;
         }
@@ -420,7 +376,8 @@ void SettingsWidget::on_styleComboBox_currentTextChanged(const QString& new_styl
     for (const auto &style_sheet : OPL::Style::styleSheets) {
         if (new_style_setting == style_sheet.styleSheetName) {
             OPL::Style::setStyle(style_sheet);
-            Settings::write(Settings::Main::Style, new_style_setting);
+//            Settings::write(Settings::Main::Style, new_style_setting);
+            Settings::setApplicationStyle(new_style_setting);
             emit settingChanged(MainWindow);
             return;
         }
@@ -430,7 +387,8 @@ void SettingsWidget::on_styleComboBox_currentTextChanged(const QString& new_styl
 void SettingsWidget::on_fontComboBox_currentFontChanged(const QFont &f)
 {
     qApp->setFont(f);
-    Settings::write(Settings::Main::Font, f.toString());
+//    Settings::write(Settings::Main::Font, f.toString());
+    Settings::setApplicationFontName(f.toString());
     DEB << "Setting Font:" << f.toString();
 }
 
@@ -439,7 +397,8 @@ void SettingsWidget::on_fontSpinBox_valueChanged(int arg1)
     QFont f = qApp->font();
     f.setPointSize(arg1);
     qApp->setFont(f);
-    Settings::write(Settings::Main::FontSize, arg1);
+//    Settings::write(Settings::Main::FontSize, arg1);
+    Settings::setApplicationFontSize(arg1);
     DEB << "Setting Font:" << f.toString();
 }
 
@@ -455,7 +414,7 @@ void SettingsWidget::on_fontCheckBox_stateChanged(int arg1)
     {
         ui->fontComboBox->setEnabled(true);
         ui->fontSpinBox->setEnabled(true);
-        Settings::write(Settings::Main::UseSystemFont, false);
+        Settings::setUseSystemFont(false);
         QFont font(ui->fontComboBox->currentFont());
         font.setPointSize(ui->fontSpinBox->value());
         qApp->setFont(font);
@@ -466,7 +425,7 @@ void SettingsWidget::on_fontCheckBox_stateChanged(int arg1)
     {
         ui->fontComboBox->setEnabled(false);
         ui->fontSpinBox->setEnabled(false);
-        Settings::write(Settings::Main::UseSystemFont, true);
+        Settings::setUseSystemFont(true);
         INFO(tr("The application will be restarted for this change to take effect."));
         qApp->quit();
         QProcess::startDetached(qApp->arguments()[0], qApp->arguments());
@@ -496,184 +455,6 @@ void SettingsWidget::on_resetStylePushButton_clicked()
     ui->fontCheckBox->setChecked(true);
 }
 
-void SettingsWidget::on_currLicDateEdit_userDateChanged(const QDate &date)
-{
-    const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}};
-    const OPL::CurrencyEntry entry(static_cast<int>(OPL::CurrencyName::Licence), row_data);
-    if (!DB->commit(entry))
-        WARN(tr("Unable to update currency. The following error has ocurred:<br>%1").arg(DB->lastError.text()));
-
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currTrDateEdit_userDateChanged(const QDate &date)
-{
-    const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}};
-    const OPL::CurrencyEntry entry(static_cast<int>(OPL::CurrencyName::TypeRating), row_data);
-    if (!DB->commit(entry))
-        WARN(tr("Unable to update currency. The following error has ocurred:<br>%1").arg(DB->lastError.text()));
-
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currLckDateEdit_userDateChanged(const QDate &date)
-{
-    const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}};
-    const OPL::CurrencyEntry entry(static_cast<int>(OPL::CurrencyName::LineCheck), row_data);
-    if (!DB->commit(entry))
-        WARN(tr("Unable to update currency. The following error has ocurred:<br>%1").arg(DB->lastError.text()));
-
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currMedDateEdit_userDateChanged(const QDate &date)
-{
-    const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)}};
-    const OPL::CurrencyEntry entry(static_cast<int>(OPL::CurrencyName::Medical), row_data);
-    if (!DB->commit(entry))
-        WARN(tr("Unable to update currency. The following error has ocurred:<br>%1").arg(DB->lastError.text()));
-
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currCustom1DateEdit_userDateChanged(const QDate &date)
-{
-    const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)},
-                                {OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom1LineEdit->text()}};
-    const OPL::CurrencyEntry entry(static_cast<int>(OPL::CurrencyName::Custom1), row_data);
-    DEB << entry;
-    if (!DB->commit(entry))
-        WARN(tr("Unable to update currency. The following error has ocurred:<br><br>%1").arg(DB->lastError.text()));
-
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currCustom2DateEdit_userDateChanged(const QDate &date)
-{
-    const OPL::RowData_T row_data = {{OPL::CurrencyEntry::EXPIRYDATE, date.toString(Qt::ISODate)},
-                                {OPL::CurrencyEntry::CURRENCYNAME, ui->currCustom2LineEdit->text()}};
-    const OPL::CurrencyEntry entry(static_cast<int>(OPL::CurrencyName::Custom2), row_data);
-    if (!DB->commit(entry))
-        WARN(tr("Unable to update currency. The following error has ocurred:<br><br>%1").arg(DB->lastError.text()));
-
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currToLdgCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowToLgdCurrency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowToLgdCurrency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currLicCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowLicCurrency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowLicCurrency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currTrCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowTrCurrency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowTrCurrency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currLckCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowLckCurrency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowLckCurrency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currMedCheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowMedCurrency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowMedCurrency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currCustom1CheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowCustom1Currency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowCustom1Currency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currCustom2CheckBox_stateChanged(int arg1)
-{
-    switch (arg1) {
-    case Qt::CheckState::Checked:
-        Settings::write(Settings::UserData::ShowCustom2Currency, true);
-        break;
-    case Qt::CheckState::Unchecked:
-        Settings::write(Settings::UserData::ShowCustom2Currency, false);
-        break;
-    default:
-        break;
-    }
-    emit settingChanged(HomeWidget);
-}
-
-void SettingsWidget::on_currCustom1LineEdit_editingFinished()
-{
-    Settings::write(Settings::UserData::Custom1CurrencyName, ui->currCustom1LineEdit->text());
-}
-
-void SettingsWidget::on_currCustom2LineEdit_editingFinished()
-{
-    Settings::write(Settings::UserData::Custom2CurrencyName, ui->currCustom2LineEdit->text());
-}
-
 void SettingsWidget::on_languageComboBox_activated(int arg1)
 {
     if (arg1 != 0) {
@@ -684,10 +465,15 @@ void SettingsWidget::on_languageComboBox_activated(int arg1)
     }
 }
 
-
 void SettingsWidget::on_exportPushButton_clicked()
 {
     auto exp = new ExportToCsvDialog(this);
     exp->exec();
 }
 
+
+void SettingsWidget::on_currencyWarningDaysSpinBox_valueChanged(int arg1)
+{
+    Settings::setCurrencyWarningThreshold(arg1);
+}
+

+ 2 - 17
src/gui/widgets/settingswidget.h

@@ -74,24 +74,11 @@ private slots:
     void on_fontSpinBox_valueChanged(int arg1);
     void on_fontCheckBox_stateChanged(int arg1);
     void on_resetStylePushButton_clicked();
-    void on_currLicDateEdit_userDateChanged(const QDate &date);
-    void on_currTrDateEdit_userDateChanged(const QDate &date);
-    void on_currLckDateEdit_userDateChanged(const QDate &date);
-    void on_currMedDateEdit_userDateChanged(const QDate &date);
-    void on_currCustom1DateEdit_userDateChanged(const QDate &date);
-    void on_currCustom2DateEdit_userDateChanged(const QDate &date);
-    void on_currToLdgCheckBox_stateChanged(int arg1);
-    void on_currLicCheckBox_stateChanged(int arg1);
-    void on_currTrCheckBox_stateChanged(int arg1);
-    void on_currLckCheckBox_stateChanged(int arg1);
-    void on_currMedCheckBox_stateChanged(int arg1);
-    void on_currCustom1CheckBox_stateChanged(int arg1);
-    void on_currCustom2CheckBox_stateChanged(int arg1);
-    void on_currCustom1LineEdit_editingFinished();
-    void on_currCustom2LineEdit_editingFinished();
     void on_languageComboBox_activated(int arg1);
     void on_exportPushButton_clicked();
 
+    void on_currencyWarningDaysSpinBox_valueChanged(int arg1);
+
 private:
     Ui::SettingsWidget *ui;
 
@@ -101,8 +88,6 @@ private:
 
     void setupComboBoxes();
 
-    void setupDateEdits();
-
     void loadBackupWidget();
 
     void loadPreviousExperienceWidget();

+ 254 - 663
src/gui/widgets/settingswidget.ui

@@ -17,8 +17,261 @@
    <item row="0" column="0">
     <widget class="QTabWidget" name="tabWidget">
      <property name="currentIndex">
-      <number>2</number>
+      <number>0</number>
      </property>
+     <widget class="QWidget" name="generalTab">
+      <attribute name="title">
+       <string>General</string>
+      </attribute>
+      <layout class="QGridLayout" name="gridLayout_3">
+       <item row="0" column="0">
+        <widget class="QLabel" name="currencyLeftLabel">
+         <property name="toolTip">
+          <string>Setting this value to 0 disables warnings.</string>
+         </property>
+         <property name="text">
+          <string>Warn about expiring currencies</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="styleLabel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>Style</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QPushButton" name="resetStylePushButton">
+         <property name="text">
+          <string>Reset to Default</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <widget class="QComboBox" name="styleComboBox"/>
+       </item>
+       <item row="2" column="0">
+        <widget class="QLabel" name="fontLabel">
+         <property name="text">
+          <string>Font</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QCheckBox" name="fontCheckBox">
+         <property name="text">
+          <string>Use System Font</string>
+         </property>
+         <property name="checked">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="2">
+        <widget class="QFontComboBox" name="fontComboBox">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="3">
+        <widget class="QSpinBox" name="fontSpinBox">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>80</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="minimum">
+          <number>8</number>
+         </property>
+         <property name="maximum">
+          <number>18</number>
+         </property>
+         <property name="value">
+          <number>10</number>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <widget class="QLabel" name="pilotSortLabel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Pilots in the Pilots Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="whatsThis">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Pilots in the Pilots Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="text">
+          <string>Sort Pilots by</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="2">
+        <widget class="QComboBox" name="pilotSortComboBox">
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Pilots in the Pilots Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <item>
+          <property name="text">
+           <string>Last Name</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>First Name</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Company</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="4" column="0">
+        <widget class="QLabel" name="acftSortLabel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Aircaft in the Aircraft Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="whatsThis">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Aircaft in the Aircraft Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="text">
+          <string>Sort Aircraft by</string>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="2">
+        <widget class="QComboBox" name="acftSortComboBox">
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Aircaft in the Aircraft Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <item>
+          <property name="text">
+           <string>Registration</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Type</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Company</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="5" column="0">
+        <widget class="QLabel" name="logbookViewLabel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="whatsThis">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="text">
+          <string>Logbook Display</string>
+         </property>
+        </widget>
+       </item>
+       <item row="5" column="2">
+        <widget class="QComboBox" name="logbookViewComboBox">
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+        </widget>
+       </item>
+       <item row="6" column="0">
+        <widget class="QLabel" name="aliasLabel">
+         <property name="toolTip">
+          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;How your own name is displayed in your logbook&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+         </property>
+         <property name="text">
+          <string>Show own name as</string>
+         </property>
+        </widget>
+       </item>
+       <item row="6" column="2">
+        <widget class="QComboBox" name="aliasComboBox">
+         <item>
+          <property name="text">
+           <string>self</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>SELF</string>
+          </property>
+         </item>
+         <item>
+          <property name="text">
+           <string>Lastname, Firstname</string>
+          </property>
+         </item>
+        </widget>
+       </item>
+       <item row="7" column="0">
+        <widget class="QLabel" name="languageLabel">
+         <property name="text">
+          <string>Language</string>
+         </property>
+        </widget>
+       </item>
+       <item row="7" column="2">
+        <widget class="QComboBox" name="languageComboBox"/>
+       </item>
+       <item row="0" column="1">
+        <widget class="QSpinBox" name="currencyWarningDaysSpinBox">
+         <property name="toolTip">
+          <string>Setting this value to 0 disables warnings.</string>
+         </property>
+         <property name="value">
+          <number>30</number>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLabel" name="currencyRightLabel">
+         <property name="toolTip">
+          <string>Setting this value to 0 disables warnings.</string>
+         </property>
+         <property name="text">
+          <string>days before expiry</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
      <widget class="QWidget" name="personalTab">
       <attribute name="title">
        <string>Personal</string>
@@ -320,668 +573,6 @@
        </item>
       </layout>
      </widget>
-     <widget class="QWidget" name="currenciesTab">
-      <attribute name="title">
-       <string>Currencies</string>
-      </attribute>
-      <layout class="QGridLayout" name="gridLayout_6">
-       <item row="3" column="3">
-        <widget class="QCheckBox" name="currToLdgCheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="8" column="0">
-        <widget class="QLineEdit" name="currCustom1LineEdit">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-         <property name="placeholderText">
-          <string>custom currency</string>
-         </property>
-        </widget>
-       </item>
-       <item row="5" column="3">
-        <widget class="QCheckBox" name="currTrCheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="3" column="0">
-        <widget class="QLabel" name="currToLdgLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Take-off / Landing (days)</string>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="1">
-        <spacer name="verticalSpacer">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>168</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item row="4" column="2">
-        <widget class="QDateEdit" name="currLicDateEdit">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
-         </property>
-        </widget>
-       </item>
-       <item row="5" column="2">
-        <widget class="QDateEdit" name="currTrDateEdit">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
-         </property>
-        </widget>
-       </item>
-       <item row="9" column="3">
-        <widget class="QCheckBox" name="currCustom2CheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="7" column="2">
-        <widget class="QDateEdit" name="currMedDateEdit">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
-         </property>
-        </widget>
-       </item>
-       <item row="3" column="2">
-        <widget class="QSpinBox" name="currToLdgSpinBox">
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Number of days for TO/LDG currency. Default 90&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="value">
-          <number>90</number>
-         </property>
-        </widget>
-       </item>
-       <item row="7" column="0">
-        <widget class="QLabel" name="currMedLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Medical</string>
-         </property>
-        </widget>
-       </item>
-       <item row="8" column="3">
-        <widget class="QCheckBox" name="currCustom1CheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="9" column="0">
-        <widget class="QLineEdit" name="currCustom2LineEdit">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="placeholderText">
-          <string>custom currency</string>
-         </property>
-        </widget>
-       </item>
-       <item row="6" column="2">
-        <widget class="QDateEdit" name="currLckDateEdit">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
-         </property>
-        </widget>
-       </item>
-       <item row="6" column="0">
-        <widget class="QLabel" name="currLckLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Line Check</string>
-         </property>
-        </widget>
-       </item>
-       <item row="5" column="0">
-        <widget class="QLabel" name="currTrLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Type Rating</string>
-         </property>
-        </widget>
-       </item>
-       <item row="4" column="0">
-        <widget class="QLabel" name="currLicLabel">
-         <property name="minimumSize">
-          <size>
-           <width>280</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Licence</string>
-         </property>
-        </widget>
-       </item>
-       <item row="6" column="3">
-        <widget class="QCheckBox" name="currLckCheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="10" column="1">
-        <spacer name="verticalSpacer_2">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>167</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item row="9" column="2">
-        <widget class="QDateEdit" name="currCustom2DateEdit">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
-         </property>
-        </widget>
-       </item>
-       <item row="4" column="3">
-        <widget class="QCheckBox" name="currLicCheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="8" column="2">
-        <widget class="QDateEdit" name="currCustom1DateEdit">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="currentSection">
-          <enum>QDateTimeEdit::MonthSection</enum>
-         </property>
-         <property name="displayFormat">
-          <string>MM/dd/yyyy</string>
-         </property>
-         <property name="calendarPopup">
-          <bool>true</bool>
-         </property>
-         <property name="timeSpec">
-          <enum>Qt::UTC</enum>
-         </property>
-         <property name="date">
-          <date>
-           <year>2020</year>
-           <month>1</month>
-           <day>1</day>
-          </date>
-         </property>
-        </widget>
-       </item>
-       <item row="7" column="3">
-        <widget class="QCheckBox" name="currMedCheckBox">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="layoutDirection">
-          <enum>Qt::LeftToRight</enum>
-         </property>
-         <property name="text">
-          <string/>
-         </property>
-        </widget>
-       </item>
-       <item row="2" column="3">
-        <widget class="QLabel" name="headerShowLabel">
-         <property name="minimumSize">
-          <size>
-           <width>140</width>
-           <height>0</height>
-          </size>
-         </property>
-         <property name="text">
-          <string>Show on Home Page</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-     <widget class="QWidget" name="appearanceTab">
-      <attribute name="title">
-       <string>Appearance</string>
-      </attribute>
-      <layout class="QGridLayout" name="gridLayout_4">
-       <item row="0" column="0">
-        <widget class="QLabel" name="styleLabel">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="text">
-          <string>Style</string>
-         </property>
-        </widget>
-       </item>
-       <item row="0" column="1">
-        <widget class="QPushButton" name="resetStylePushButton">
-         <property name="text">
-          <string>Reset to Default</string>
-         </property>
-        </widget>
-       </item>
-       <item row="0" column="2">
-        <widget class="QComboBox" name="styleComboBox"/>
-       </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="fontLabel">
-         <property name="text">
-          <string>Font</string>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="1">
-        <widget class="QCheckBox" name="fontCheckBox">
-         <property name="text">
-          <string>Use System Font</string>
-         </property>
-         <property name="checked">
-          <bool>true</bool>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="2">
-        <widget class="QFontComboBox" name="fontComboBox">
-         <property name="enabled">
-          <bool>false</bool>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="3">
-        <widget class="QSpinBox" name="fontSpinBox">
-         <property name="enabled">
-          <bool>false</bool>
-         </property>
-         <property name="maximumSize">
-          <size>
-           <width>80</width>
-           <height>16777215</height>
-          </size>
-         </property>
-         <property name="minimum">
-          <number>8</number>
-         </property>
-         <property name="maximum">
-          <number>18</number>
-         </property>
-         <property name="value">
-          <number>10</number>
-         </property>
-        </widget>
-       </item>
-       <item row="2" column="0">
-        <widget class="QLabel" name="pilotSortLabel">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Pilots in the Pilots Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="whatsThis">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Pilots in the Pilots Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="text">
-          <string>Sort Pilots by</string>
-         </property>
-        </widget>
-       </item>
-       <item row="2" column="2">
-        <widget class="QComboBox" name="pilotSortComboBox">
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Pilots in the Pilots Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <item>
-          <property name="text">
-           <string>Last Name</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>First Name</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Company</string>
-          </property>
-         </item>
-        </widget>
-       </item>
-       <item row="3" column="0">
-        <widget class="QLabel" name="acftSortLabel">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Aircaft in the Aircraft Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="whatsThis">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Aircaft in the Aircraft Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="text">
-          <string>Sort Aircraft by</string>
-         </property>
-        </widget>
-       </item>
-       <item row="3" column="2">
-        <widget class="QComboBox" name="acftSortComboBox">
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines by which column to sort the display of Aircaft in the Aircraft Tab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <item>
-          <property name="text">
-           <string>Registration</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Type</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Company</string>
-          </property>
-         </item>
-        </widget>
-       </item>
-       <item row="4" column="0">
-        <widget class="QLabel" name="logbookViewLabel">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="whatsThis">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="text">
-          <string>Logbook Dispay</string>
-         </property>
-        </widget>
-       </item>
-       <item row="4" column="2">
-        <widget class="QComboBox" name="logbookViewComboBox">
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Determines how your logbook is displayed in the logbook tab. This has no influence on what details are logged, just on what is displayed by default.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-        </widget>
-       </item>
-       <item row="5" column="0">
-        <widget class="QLabel" name="aliasLabel">
-         <property name="toolTip">
-          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;How your own name is displayed in your logbook&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
-         </property>
-         <property name="text">
-          <string>Show own name as</string>
-         </property>
-        </widget>
-       </item>
-       <item row="5" column="2">
-        <widget class="QComboBox" name="aliasComboBox">
-         <item>
-          <property name="text">
-           <string>self</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>SELF</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Lastname, Firstname</string>
-          </property>
-         </item>
-        </widget>
-       </item>
-       <item row="6" column="0">
-        <widget class="QLabel" name="languageLabel">
-         <property name="text">
-          <string>Language</string>
-         </property>
-        </widget>
-       </item>
-       <item row="6" column="2">
-        <widget class="QComboBox" name="languageComboBox"/>
-       </item>
-      </layout>
-     </widget>
      <widget class="QWidget" name="backupTab">
       <attribute name="title">
        <string>Backups</string>

+ 231 - 0
src/gui/widgets/tableeditwidget.cpp

@@ -0,0 +1,231 @@
+#include "tableeditwidget.h"
+#include "src/database/database.h"
+#include "src/opl.h"
+#include <QGridLayout>
+#include <QLabel>
+
+TableEditWidget::TableEditWidget(Orientation orientation, QWidget *parent)
+    : QWidget{parent}, m_orientation(orientation)
+{}
+
+void TableEditWidget::init()
+{
+    setupUI();
+    setupSignalsAndSlots();
+}
+
+void TableEditWidget::setupUI()
+{
+    // Setting up the model and view is done in the derived class
+    setupModelAndView();
+    m_entryEditDialog = getEntryEditDialog(this);
+    m_stackedWidget->addWidget(m_entryEditDialog);
+
+    // set up the UI
+    switch (m_orientation) {
+    case Horizontal:
+        setupHorizontalUI();
+        break;
+    case Vertical:
+        setupVerticalUI();
+    default:
+        break;
+    }
+
+}
+
+void TableEditWidget::setupHorizontalUI()
+{
+    // In the horizontal view, the editing widget is hidden on the right hand side
+    m_stackedWidget->hide();
+
+    // create a 2-column grid layout and fill the cells
+    int colL = 0; // left column
+    int colR = 1; // right column
+    int row = 0;
+    int allRowSpan = 4; // adjust as needed for stackedWidget to span all rows
+
+    auto gridLayout = new QGridLayout(this);
+
+    gridLayout->addWidget(m_view, row, colL);
+    gridLayout->addWidget(m_stackedWidget, row, colR, allRowSpan, 1);
+    row++;
+
+    setupButtonWidget();
+    gridLayout->addWidget(m_buttonWidget);
+    row++;
+
+    setupFilterWidget();
+    gridLayout->addWidget(m_filterWidget, row, colL);
+}
+
+void TableEditWidget::setupVerticalUI()
+{
+    // create a single column grid layout and fill the cells
+    int col = 0;
+    int row = 0;
+    auto gridLayout = new QGridLayout(this);
+
+    gridLayout->addWidget(m_view, row, col);
+    row++;
+
+    gridLayout->addWidget(m_stackedWidget, row, col);
+    row++;
+
+    setupButtonWidget();
+    gridLayout->addWidget(m_buttonWidget);
+    row++;
+
+    setupFilterWidget();
+    m_stackedWidget->addWidget(m_filterWidget);
+    m_stackedWidget->setCurrentWidget(m_filterWidget);
+    gridLayout->addWidget(m_stackedWidget);
+}
+
+void TableEditWidget::setupFilterWidget()
+{
+    // place the filter items in a grid layout so they occupy one cell in parent layout
+    QWidget *widget = new QWidget(this);
+    QGridLayout *layout = new QGridLayout(widget);
+
+    // one row, three columns
+    layout->addWidget(new QLabel(tr("Filter"), this), 0, 0);
+    layout->addWidget(m_filterLineEdit,               0, 1);
+    layout->addWidget(m_filterSelectionComboBox,      0, 2);
+
+    m_filterWidget = widget;
+}
+
+void TableEditWidget::setupButtonWidget()
+{
+    auto buttonWidget = new QWidget(this);
+    auto buttonGridLayout = new QGridLayout(buttonWidget);
+
+    switch (m_orientation) {
+    case Horizontal:
+        buttonGridLayout->addWidget(m_addNewEntryPushButton, 0, 0);
+        buttonGridLayout->addWidget(m_deleteEntryPushButton, 1, 0);
+        break;
+    case Vertical:
+        buttonGridLayout->addWidget(m_addNewEntryPushButton, 0, 0);
+        buttonGridLayout->addWidget(m_deleteEntryPushButton, 0, 1);
+    default:
+        break;
+    }
+
+    m_buttonWidget = buttonWidget;
+}
+
+void TableEditWidget::setupSignalsAndSlots()
+{
+    // refresh the view when the database is updated
+    QObject::connect(DB,             		   	&OPL::Database::dataBaseUpdated,
+                     this,     		 		   	&TableEditWidget::databaseContentChanged);
+    // filter the view
+    QObject::connect(m_filterLineEdit,  		&QLineEdit::textChanged,
+                     this,                     	&TableEditWidget::filterTextChanged);
+    // sort the view by column
+    QObject::connect(m_view->horizontalHeader(),&QHeaderView::sectionClicked,
+                     this,                     	&TableEditWidget::sortColumnChanged);
+    // Edit an entry
+    QObject::connect(m_view,					&QTableView::clicked,
+                     this, 			    	   	&TableEditWidget::editEntryRequested);
+    // Add a new entry
+    QObject::connect(m_addNewEntryPushButton,   &QPushButton::clicked,
+                     this, 					   	&TableEditWidget::addEntryRequested);
+    // Delete a selected entry
+    QObject::connect(m_deleteEntryPushButton,   &QPushButton::clicked,
+                     this, 					   	&TableEditWidget::deleteEntryRequested);
+}
+
+void TableEditWidget::addEntryRequested()
+{
+    cleanUpOldEditDialog();
+
+    m_entryEditDialog = getEntryEditDialog(this);
+    m_stackedWidget->addWidget(m_entryEditDialog);
+    m_stackedWidget->setCurrentWidget(m_entryEditDialog);
+
+    showEditWidget();
+    m_entryEditDialog->exec();
+    hideEditWidget();
+}
+
+void TableEditWidget::editEntryRequested(const QModelIndex &selectedIndex)
+{
+    int rowIdToEdit = m_model->index(selectedIndex.row(), 0).data().toInt();
+
+    cleanUpOldEditDialog();
+
+    m_entryEditDialog = getEntryEditDialog(this);
+    m_entryEditDialog->loadEntry(rowIdToEdit);
+    m_stackedWidget->addWidget(m_entryEditDialog);
+    m_stackedWidget->setCurrentWidget(m_entryEditDialog);
+
+    showEditWidget();
+    m_entryEditDialog->exec();
+    hideEditWidget();
+}
+
+void TableEditWidget::deleteEntryRequested()
+{
+    const QModelIndex selectedIndex = m_view->selectionModel()->currentIndex();
+    if(!selectedIndex.isValid()) {
+        WARN(tr("No entry selected."));
+        return;
+    }
+    m_stackedWidget->hide();
+
+    int rowId = m_model->index(selectedIndex.row(), 0).data().toInt();
+    m_view->selectionModel()->reset();
+
+    // get user confirmation
+    QMessageBox confirm(this);
+    confirm.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+    confirm.setDefaultButton(QMessageBox::No);
+    confirm.setIcon(QMessageBox::Question);
+    confirm.setWindowTitle(tr("Confirm Deletion"));
+
+    confirm.setText(confirmDeleteString(rowId));
+    if (confirm.exec() == QMessageBox::Yes) {
+        auto editDialog = getEntryEditDialog(this);
+        if(!editDialog->deleteEntry(rowId))
+            WARN(deleteErrorString(rowId));
+    }
+
+    // re-set stackedWidget for Vertical Layout
+    if(m_orientation == Vertical) {
+        m_stackedWidget->setCurrentWidget(m_filterWidget);
+        m_stackedWidget->show();
+    }
+}
+
+void TableEditWidget::sortColumnChanged(int newSortColumn)
+{
+    m_view->sortByColumn(newSortColumn, Qt::AscendingOrder);
+}
+
+void TableEditWidget::databaseContentChanged()
+{
+    m_model->select();
+    m_view->resizeColumnsToContents();
+}
+
+void TableEditWidget::showEditWidget()
+{
+    m_buttonWidget->hide();
+    m_stackedWidget->show();
+}
+
+void TableEditWidget::hideEditWidget()
+{
+    m_stackedWidget->hide();
+    m_buttonWidget->show();
+}
+
+void TableEditWidget::cleanUpOldEditDialog()
+{
+    if(m_stackedWidget->indexOf(m_entryEditDialog) != -1) {
+        delete m_entryEditDialog;
+    }
+}

+ 154 - 0
src/gui/widgets/tableeditwidget.h

@@ -0,0 +1,154 @@
+#ifndef TABLEEDITWIDGET_H
+#define TABLEEDITWIDGET_H
+
+#include "src/gui/dialogues/entryeditdialog.h"
+#include <QWidget>
+#include <QSqlTableModel>
+#include <QHeaderView>
+#include <QTableView>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QStackedWidget>
+#include <QComboBox>
+#include <QLabel>
+
+/*!
+ * \brief The TableEditWidget class is a base class for widgets which enable
+ * editing certain tables in the datbase.
+ * \details The TableEditWidget consists of a QTableView which displays the data
+ * from a given table. The data is held in a QSqlTableModel. The views edit triggers
+ * are disabled. Whenever a row is selected in the view, the selected entry is displayed
+ * for editing in a suitable EntryEditDialog which is responsible for the verification of
+ * the user input as well as reading and writing to and from the database.
+ *
+ * The TableEditWidget has two Orientation options: Horizontal and Vertical
+ *
+ * In the Horizontal layout, the table view is split horizontally to make space
+ * for the TableEditWidget on the right hand side, whereas on the Vertical layout
+ * it is split vertically with the TableEditWidget occupying the lower half of the screen.
+ *
+ * When implementing the TableEditWidget it is important to set up the model and call the
+ * Base Class implementation of setupUI before performing any specialisations. Before the
+ * TableEditWidget is shown, the init method must be run.
+ */
+class TableEditWidget : public QWidget
+{
+    Q_OBJECT
+public:
+    /*!
+     * \brief Determines how the layout is created
+     * \details <ul>
+     * <li> Horizontal: The edit widget is shown besides the table </li>
+     * <li> Vertical: The edit widget is shown below the table </li>
+     * </ul>
+     */
+    enum Orientation {Horizontal, Vertical};
+
+    /*!
+     * \brief Create a new TableEditWidget
+     */
+    explicit TableEditWidget(Orientation orientation = Horizontal, QWidget *parent = nullptr);
+
+    /*!
+     * \brief Initialises the dialog by calling its virtual setup functions.
+     * \attention Call this functian before showing the dialog.
+     */
+    void init();
+
+    /*!
+     * \brief Set up the model and view of the widget
+     * \details Implement this function to initialise the protected members of this class.
+     * This includes setting the QSqlTableModel and QTableView      */
+    virtual void setupModelAndView() = 0;
+
+    /*!
+     * \brief Set up the UI of the widget
+     * \details Implement this function to set appropriate labels to the protected members of this
+     * class. This includes setting appropriate labels on the Push Buttons as well as
+     * appropriate filter options in the filter Combo Box. Make sure to call the base class
+     * implementation first when overriding this method.
+     */
+    virtual void setupUI();
+
+    /*!
+     * \brief create an error String when deleting a database entry has been unsuccessful
+     * \param rowId - the row id of the entry to be deleted
+     * \details When deleting an entry from a database fails, this can have different reasons
+     * depending on the table. This function returns an implementation-specific error string
+     * to inform the user about the failure and give hints on how to fix it
+     */
+    virtual QString deleteErrorString(int rowId) = 0;
+
+    /*!
+     * \brief return a String asking the user to confirm deletion of a given entry
+     * \param rowId - the row id of the entry to be deleted
+     * \brief The message string is displayed in a QMessageBox
+     */
+    virtual QString confirmDeleteString(int rowId) = 0;
+
+    /*!
+     * \brief get an apropriate Edit Dialog for the implementation
+     * \details The Edit Dialogs for different tables differ in the data they display
+     * and how they verify the user inputs. This method returns an apropriate
+     * EntryEditDialog for the selected table.
+     */
+    virtual EntryEditDialog *getEntryEditDialog(QWidget *parent = nullptr) = 0;
+
+protected:
+    Orientation m_orientation;
+    QSqlTableModel *m_model = nullptr;
+    QTableView *m_view = new QTableView(this);
+    QWidget *m_filterWidget = nullptr;
+    QWidget *m_buttonWidget = nullptr;
+    EntryEditDialog* m_entryEditDialog = nullptr;
+
+    QPushButton *m_addNewEntryPushButton = new QPushButton(this);
+    QPushButton *m_deleteEntryPushButton = new QPushButton(this);
+
+    QStackedWidget *m_stackedWidget = new QStackedWidget(this);
+    QLineEdit *m_filterLineEdit = new QLineEdit(this);
+    QComboBox *m_filterSelectionComboBox = new QComboBox(this);
+
+    virtual void showEditWidget();
+    virtual void hideEditWidget();
+    /*!
+     * \brief makes sure heap allocated widgets are destroyed when a user requests another add or edit before completing
+     * a proviously opened one.
+     */
+    void cleanUpOldEditDialog();
+
+private:
+    void setupHorizontalUI();
+    void setupVerticalUI();
+    void setupSignalsAndSlots();
+
+    /*!
+     * \brief Place the filter items in a widget to facilitate easier placement in parent layout
+     */
+    void setupFilterWidget();
+
+    /*!
+     * \brief Place the new and edit buttons in a widget to facilitate easier placement in parent layout
+     */
+    void setupButtonWidget();
+
+public slots:
+    virtual void addEntryRequested();
+    virtual void editEntryRequested(const QModelIndex &selectedIndex);
+    virtual void deleteEntryRequested();
+    virtual void sortColumnChanged(int newSortColumn);
+
+    /*!
+     * \brief Set a filter on the model
+     */
+    virtual void filterTextChanged(const QString &filterString) = 0;
+
+public slots:
+    /*!
+     * \brief refresh the view after a Database change
+     */
+    void databaseContentChanged();
+
+};
+
+#endif // TABLEEDITWIDGET_H

+ 111 - 0
src/gui/widgets/tailtableeditwidget.cpp

@@ -0,0 +1,111 @@
+#include "tailtableeditwidget.h"
+#include "src/database/database.h"
+#include "src/gui/dialogues/newtaildialog.h"
+
+TailTableEditWidget::TailTableEditWidget(QWidget *parent)
+    : TableEditWidget(Horizontal, parent)
+{}
+
+void TailTableEditWidget::setupModelAndView()
+{
+    m_model = new QSqlTableModel(this, DB->database());
+    m_model->setTable(OPL::GLOBALS->getDbTableName(OPL::DbTable::Tails));
+    m_model->select();
+    m_model->setHeaderData(COL_REGISTRATION, Qt::Horizontal, COLUMN_NAME_REGISTRATION);
+    m_model->setHeaderData(COL_TYPE, Qt::Horizontal, COLUMN_NAME_TYPE);
+    m_model->setHeaderData(COL_COMPANY, Qt::Horizontal, COLUMN_NAME_COMPANY);
+
+    m_view->setModel(m_model);
+    m_view->setSelectionMode(QAbstractItemView::SingleSelection);
+    m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
+    m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    m_view->horizontalHeader()->setStretchLastSection(QHeaderView::Stretch);
+    m_view->resizeColumnsToContents();
+    m_view->verticalHeader()->hide();
+    m_view->setAlternatingRowColors(true);
+    for(const int i : COLS_TO_HIDE)
+        m_view->hideColumn(i);
+}
+
+void TailTableEditWidget::setupUI()
+{
+    // the base class does most of the setup
+    TableEditWidget::setupUI();
+
+    // only need to set the table specific labels and combo box items
+    m_addNewEntryPushButton->setText(tr("Add New Tail"));
+    m_deleteEntryPushButton->setText(tr("Delete Selected Tail"));
+    m_filterSelectionComboBox->addItems(FILTER_COLUMNS);
+}
+
+QString TailTableEditWidget::deleteErrorString(int rowId)
+{
+    QList<int> foreign_key_constraints = DB->getForeignKeyConstraints(rowId,
+                                                                      OPL::DbTable::Tails);
+    QList<OPL::FlightEntry> constrained_flights;
+    for (const auto &row_id : qAsConst(foreign_key_constraints)) {
+        constrained_flights.append(DB->getFlightEntry(row_id));
+    }
+
+    QMessageBox message_box(this);
+    if (constrained_flights.isEmpty()) {
+        // error is a database error
+        return tr("<br>Unable to delete.<br><br>The following error has ocurred: %1"
+                  ).arg(DB->lastError.text());
+    } else {
+        QString constrained_flights_string;
+        for (int i=0; i<constrained_flights.length(); i++) {
+            constrained_flights_string.append(constrained_flights[i].getFlightSummary()
+                                              + QLatin1String("&nbsp;&nbsp;&nbsp;&nbsp;<br>"));
+            if (i>10) {
+                constrained_flights_string.append(QLatin1String("<br>[...]<br>"));
+                break;
+            }
+        }
+        return (tr("Unable to delete.<br><br>"
+                   "This is most likely the case because a flight exists with the aircraft "
+                   "you are trying to delete.<br><br>"
+                   "%1 flight(s) with this aircraft have been found:<br><br><br><b><tt>"
+                   "%2"
+                   "</b></tt><br><br>You have to change or remove the conflicting flight(s) "
+                   "before removing this aircraft from the database.<br><br>"
+                   ).arg(
+                        QString::number(constrained_flights.length()),
+                        constrained_flights_string)
+                );
+    }
+}
+
+QString TailTableEditWidget::confirmDeleteString(int rowId)
+{
+    const auto entry = DB->getTailEntry(rowId);
+    return tr("You are deleting the following aircraft:<br><br><b><tt>"
+              "%1 (%2)</b></tt><br><br>Are you sure?"
+              ).arg(
+              entry.getData().value(OPL::TailEntry::REGISTRATION).toString(),
+              entry.type()
+              );
+}
+
+EntryEditDialog *TailTableEditWidget::getEntryEditDialog(QWidget *parent)
+{
+    QString empty;
+    return new NewTailDialog(empty, parent);
+}
+
+void TailTableEditWidget::filterTextChanged(const QString &filterString)
+{
+    if(filterString.isEmpty()) {
+        m_model->setFilter(QString());
+        return;
+    }
+
+    int i = m_filterSelectionComboBox->currentIndex();
+    const QString filter =
+        QLatin1Char('\"')
+        + FILTER_COLUMN_NAMES.at(i)
+        + QLatin1String("\" LIKE '%")
+        + filterString
+        + QLatin1String("%'");
+    m_model->setFilter(filter);
+}

+ 50 - 0
src/gui/widgets/tailtableeditwidget.h

@@ -0,0 +1,50 @@
+#ifndef TAILTABLEEDITWIDGET_H
+#define TAILTABLEEDITWIDGET_H
+
+#include "tableeditwidget.h"
+#include "src/database/tailentry.h"
+
+class TailTableEditWidget : public TableEditWidget
+{
+    Q_OBJECT
+public:
+    TailTableEditWidget() = delete;
+    explicit TailTableEditWidget(QWidget *parent = nullptr);
+
+
+    virtual void setupModelAndView() override;
+    virtual void setupUI() override;
+    virtual QString deleteErrorString(int rowId) override;
+    virtual QString confirmDeleteString(int rowId) override;
+    virtual EntryEditDialog *getEntryEditDialog(QWidget *parent) override;
+
+private:
+    const int COL_ROWID = 0;
+    const int COL_REGISTRATION = 1;
+    const int COL_TYPE = 10;
+    const int COL_COMPANY = 2;
+
+    const int COLS_TO_HIDE[8] = {0, 3, 4, 5, 6, 7, 8, 9};
+
+    const QString COLUMN_NAME_REGISTRATION = tr("Registration");
+    const QString COLUMN_NAME_TYPE = tr("Type");
+    const QString COLUMN_NAME_COMPANY = tr("Company");
+
+    const QStringList FILTER_COLUMNS = {
+        COLUMN_NAME_REGISTRATION,
+        COLUMN_NAME_TYPE,
+        COLUMN_NAME_COMPANY,
+    };
+
+    const static inline QStringList FILTER_COLUMN_NAMES = {
+        OPL::TailEntry::REGISTRATION,
+        OPL::TailEntry::TYPE_STRING,
+        OPL::TailEntry::COMPANY
+    };
+
+private slots:
+
+    virtual void filterTextChanged(const QString &filterString) override;
+};
+
+#endif // TAILTABLEEDITWIDGET_H

+ 6 - 3
src/gui/widgets/totalswidget.cpp

@@ -22,6 +22,7 @@
 #include "src/opl.h"
 #include "src/classes/time.h"
 #include "ui_totalswidget.h"
+#include "src/classes/settings.h"
 
 TotalsWidget::TotalsWidget(WidgetType widgetType, QWidget *parent) :
     QWidget(parent),
@@ -43,6 +44,7 @@ TotalsWidget::~TotalsWidget()
  */
 void TotalsWidget::setup(const WidgetType widgetType)
 {
+    m_format = Settings::getDisplayFormat();
     const QList<QLineEdit *> lineEdits = this->findChildren<QLineEdit *>();
 
     switch (widgetType) {
@@ -90,6 +92,7 @@ void TotalsWidget::fillTotals(const WidgetType widgetType)
         break;
     case PreviousExperienceWidget:
         time_data = DB->getRowData(OPL::DbTable::PreviousExperience, ROW_ID);
+        break;
     }
 
     // fill the line edits with the data obtained
@@ -106,7 +109,7 @@ void TotalsWidget::fillTotals(const WidgetType widgetType)
                 line_edit->setText(field.toString());
             } else {
                 // line edits for total time
-                OPL::Time time = OPL::Time(field.toInt());// = Time(field.toInt());
+                OPL::Time time = OPL::Time(field.toInt(), m_format);
                 line_edit->setText(time.toString());
             }
         }
@@ -171,7 +174,7 @@ void TotalsWidget::timeLineEditEditingFinished()
 
     // write the updated value to the database
     const QString db_field = line_edit->objectName().remove(QLatin1String("LineEdit"));
-    const QVariant value = OPL::Time::fromString(line_edit->text()).toMinutes();
+    const QVariant value = OPL::Time::fromString(line_edit->text(), m_format).toMinutes();
 
     m_rowData.insert(db_field, value);
     LOG << "Added row data: " + db_field + ": " + value.toString();
@@ -181,7 +184,7 @@ void TotalsWidget::timeLineEditEditingFinished()
 
     // Read back the value and set the line edit to confirm input is correct and provide user feedback
     m_rowData = DB->getRowData(OPL::DbTable::PreviousExperience, ROW_ID);
-    OPL::Time new_time = OPL::Time(m_rowData.value(db_field).toInt());
+    OPL::Time new_time = OPL::Time(m_rowData.value(db_field).toInt(), m_format);
     line_edit->setText(new_time.toString());
 }
 

+ 14 - 0
src/gui/widgets/totalswidget.h

@@ -28,6 +28,18 @@ namespace Ui {
 class TotalsWidget;
 }
 
+/*!
+ * \brief The TotalsWidget is used to display or edit total time values.
+ * \details This widget has two different purposes.
+ * <ul>
+ * <li> It displays the cumulative total time in the logbook </li>
+ * <li> It is used to enter and edit the total time accumulated in previous logbooks </li>
+ * </ul>
+ *
+ * In its total time form, the Widget functions purely as a display widget, whereas when used
+ * for previous experience, its fields are editable.
+ *
+ */
 class TotalsWidget : public QWidget
 {
     Q_OBJECT
@@ -48,6 +60,8 @@ private:
     /*!
      * \brief ROW_ID the row ID for previous experience entries (1)
      */
+
+    OPL::DateTimeFormat m_format;
     const static int ROW_ID = 1;
     void fillTotals(const WidgetType widgetType);
     void setup(const WidgetType widgetType);

+ 525 - 529
src/gui/widgets/totalswidget.ui

@@ -15,535 +15,531 @@
   </property>
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="0">
-    <layout class="QGridLayout" name="gridLayout_3">
-     <item row="2" column="2">
-      <widget class="QLabel" name="nightLabel_2">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>Night</string>
-       </property>
-      </widget>
-     </item>
-     <item row="7" column="2">
-      <widget class="QLabel" name="ldgnightLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>LDG Night</string>
-       </property>
-      </widget>
-     </item>
-     <item row="5" column="3">
-      <widget class="QLineEdit" name="toNightLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="0">
-      <widget class="QLabel" name="spmeLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>SP ME</string>
-       </property>
-      </widget>
-     </item>
-     <item row="6" column="3">
-      <widget class="QLineEdit" name="ldgDayLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="1">
-      <widget class="QLineEdit" name="tMPLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="7" column="1">
-      <widget class="QLineEdit" name="tFILineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="6" column="0">
-      <widget class="QLabel" name="dualLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>DUAL</string>
-       </property>
-      </widget>
-     </item>
-     <item row="5" column="2">
-      <widget class="QLabel" name="tonightLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>TO Night</string>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QLineEdit" name="tSPSELineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="5" column="1">
-      <widget class="QLineEdit" name="tSICLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="6" column="1">
-      <widget class="QLineEdit" name="tDUALLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="7" column="3">
-      <widget class="QLineEdit" name="ldgNightLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="0">
-      <widget class="QLabel" name="piclabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>PIC</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="2">
-      <widget class="QLabel" name="picusLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>PICus</string>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="2">
-      <widget class="QLabel" name="ifrLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>IFR</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="3">
-      <widget class="QLineEdit" name="tPICUSLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="0">
-      <widget class="QLabel" name="multipilotLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>Multi Pilot</string>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="3">
-      <widget class="QLineEdit" name="tNIGHTLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="3">
-      <widget class="QLineEdit" name="toDayLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-       <property name="text">
-        <string>0</string>
-       </property>
-      </widget>
-     </item>
-     <item row="6" column="2">
-      <widget class="QLabel" name="ldgdayLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>LDG Day</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="2">
-      <widget class="QLabel" name="simLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>Simulator</string>
-       </property>
-      </widget>
-     </item>
-     <item row="3" column="3">
-      <widget class="QLineEdit" name="tSIMLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="0">
-      <widget class="QLabel" name="totalLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>Total</string>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="0">
-      <widget class="QLabel" name="spseLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>SP SE</string>
-       </property>
-      </widget>
-     </item>
-     <item row="5" column="0">
-      <widget class="QLabel" name="sicLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>SIC</string>
-       </property>
-      </widget>
-     </item>
-     <item row="7" column="0">
-      <widget class="QLabel" name="fiLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>FI</string>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="1">
-      <widget class="QLineEdit" name="tblkLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-       <property name="text">
-        <string/>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="3">
-      <widget class="QLineEdit" name="tIFRLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="2" column="1">
-      <widget class="QLineEdit" name="tSPMELineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="1">
-      <widget class="QLineEdit" name="tPICLineEdit">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="focusPolicy">
-        <enum>Qt::TabFocus</enum>
-       </property>
-      </widget>
-     </item>
-     <item row="4" column="2">
-      <widget class="QLabel" name="todayLabel">
-       <property name="maximumSize">
-        <size>
-         <width>120</width>
-         <height>16777215</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>TO Day</string>
-       </property>
-      </widget>
-     </item>
-    </layout>
+    <widget class="QLabel" name="totalLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>Total</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QLineEdit" name="tblkLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="2">
+    <widget class="QLabel" name="picusLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>PICus</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="3">
+    <widget class="QLineEdit" name="tPICUSLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="spseLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>SP SE</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLineEdit" name="tSPSELineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2">
+    <widget class="QLabel" name="ifrLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>IFR</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="3">
+    <widget class="QLineEdit" name="tIFRLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="spmeLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>SP ME</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QLineEdit" name="tSPMELineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="2">
+    <widget class="QLabel" name="nightLabel_2">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>Night</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="3">
+    <widget class="QLineEdit" name="tNIGHTLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="multipilotLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>Multi Pilot</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="1">
+    <widget class="QLineEdit" name="tMPLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="2">
+    <widget class="QLabel" name="simLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>Simulator</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="3">
+    <widget class="QLineEdit" name="tSIMLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QLabel" name="piclabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>PIC</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QLineEdit" name="tPICLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="2">
+    <widget class="QLabel" name="todayLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>TO Day</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="3">
+    <widget class="QLineEdit" name="toDayLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="0">
+    <widget class="QLabel" name="sicLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>SIC</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="1">
+    <widget class="QLineEdit" name="tSICLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="2">
+    <widget class="QLabel" name="tonightLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>TO Night</string>
+     </property>
+    </widget>
+   </item>
+   <item row="5" column="3">
+    <widget class="QLineEdit" name="toNightLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="0">
+    <widget class="QLabel" name="dualLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>DUAL</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="1">
+    <widget class="QLineEdit" name="tDUALLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="2">
+    <widget class="QLabel" name="ldgdayLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>LDG Day</string>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="3">
+    <widget class="QLineEdit" name="ldgDayLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="0">
+    <widget class="QLabel" name="fiLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>FI</string>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="1">
+    <widget class="QLineEdit" name="tFILineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="2">
+    <widget class="QLabel" name="ldgnightLabel">
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="text">
+      <string>LDG Night</string>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="3">
+    <widget class="QLineEdit" name="ldgNightLineEdit">
+     <property name="minimumSize">
+      <size>
+       <width>100</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="maximumSize">
+      <size>
+       <width>120</width>
+       <height>16777215</height>
+      </size>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::TabFocus</enum>
+     </property>
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
    </item>
   </layout>
  </widget>

+ 85 - 24
src/opl.h

@@ -70,16 +70,22 @@ namespace OPL {
 /**
  * @brief Defines the row ID for non-user entries in the database;
  */
-const static int STUB_ROW_ID = -1;
+constexpr static int STUB_ROW_ID = -1;
 
 /**
  * @brief Defines a four-letter code for a non-extistent (dummy) airport: "XXXX"
  */
-const static char* STUB_AIRPORT_CODE = "XXXX";
+constexpr static auto STUB_AIRPORT_CODE = QLatin1String("XXXX");
 /**
  * @brief Defines a registration for a non-existent (dummy) aircraft: "XX-XXX"
  */
-const static char* STUB_AIRCRAFT_REG = "XX-XXX";
+constexpr static auto STUB_AIRCRAFT_REG = QLatin1String("XX-XXX");
+
+/*!
+ * \brief The decimal seperator used internally
+ */
+constexpr static char DECIMAL_SEPERATOR = '.';
+
 
 /*!
  * \brief The ANotificationHandler class handles displaying of user-directed messages. It displays
@@ -118,6 +124,63 @@ struct ToLdgCount_T {
         : toDay(toDay), toNight(toNight), ldgDay(ldgDay), ldgNight(ldgNight) {}
 };
 
+/*!
+ * \brief The DateFormat struct encapsulates how date and time values are displayed.
+ * \details Stores how the user wishes to display and enter Date and Time Entries.
+ * These are stored numerically in the database and thus need to be converted to
+ * human-readably form.
+ */
+struct DateTimeFormat {
+    /*!
+     * \brief Enumerates how dates can be formatted to a localised format.
+     * \value Default - The Application standard, Equivalent to Qt::ISODate
+     * \value SystemLocale - The current system locale date format
+     */
+    enum class DateFormat { Default, SystemLocale, Custom };
+
+    /*!
+     * \brief Enumerates how time values can be formatted
+     * \value Default - The application default is 'hh:mm'
+     * \value Decimal - Time as Decmial hours (01:30 == 1.5)
+     * \value Custom - A user-provided custom format string
+     */
+    enum class TimeFormat { Default, Decimal, Custom };
+
+    /*!
+     * \brief Initialise a DateTimeFormat instance with default values
+     */
+    DateTimeFormat()
+        : m_dateFormat(DateFormat::Default),
+        m_dateFormatString(QStringLiteral("yyyy-MM-dd")),
+        m_timeFormat(TimeFormat::Default),
+        m_timeFormatString(QStringLiteral("hh:mm"))
+    {}
+
+    DateTimeFormat(DateFormat dateFormat_,
+                   const QString &dateFormatString_,
+                   TimeFormat timeFormat_,
+                   const QString &timeFormatString_)
+        :
+        m_dateFormat(dateFormat_),
+        m_dateFormatString(dateFormatString_),
+        m_timeFormat(timeFormat_),
+        m_timeFormatString(timeFormatString_)
+    {}
+
+
+public:
+    DateFormat dateFormat() const { return m_dateFormat; }
+    TimeFormat timeFormat() const { return m_timeFormat; }
+    const QString &dateFormatString() const { return m_dateFormatString; }
+    const QString &timeFormatString() const { return m_timeFormatString; }
+
+private:
+    DateFormat m_dateFormat;
+    TimeFormat m_timeFormat;
+    QString m_dateFormatString;
+    QString m_timeFormatString;
+};
+
 /*!
  * \brief ADateFormats enumerates the accepted date formats for QDateEdits
  * \todo At the moment, only ISODate is accepet as a valid date format.
@@ -126,7 +189,7 @@ enum class DateFormat {ISODate, DE, EN };
 
 enum class FlightTimeFormat {Default, Decimal};
 
-enum class DateTimeFormat {Default, Backup};
+enum class DateTimeFormat_deprecated {Default, Backup};
 
 /*!
  * \brief PilotFunction
@@ -142,7 +205,7 @@ enum class Translation {English, German, Spanish};
 /*!
  * \brief Enumerates the available SQL views in the database
  */
-enum class DbViewName {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly};
+enum class LogbookView {Default, DefaultWithSim, Easa, EasaWithSim, SimulatorOnly};
 
 /*!
  * \brief Enumerates the Simulator Types: Flight and Navigation Procedures Trainer 1/2, Flight Simulation Training Device
@@ -176,7 +239,7 @@ public:
 
     inline const QStringList &getApproachTypes() const {return APPROACH_TYPES;}
     inline const QString getLanguageFilePath(Translation language) const {return L10N_FilePaths.value(language);}
-    inline const QString getViewIdentifier(DbViewName view_name) const {return DATABASE_VIEWS.value(view_name);}
+    inline const QString getViewIdentifier(LogbookView view_name) const {return DATABASE_VIEWS.value(view_name);}
     inline const QString getDbTableName(DbTable table_name) const {return DB_TABLES.value(table_name);}
 
 private:
@@ -191,19 +254,19 @@ private:
         {Translation::German,  QStringLiteral("Deutsch")},
         {Translation::Spanish, QStringLiteral("Español")},
     };
-    const static inline QMap<DbViewName, QString> DATABASE_VIEWS = {
-        {DbViewName::Default,        QStringLiteral("viewDefault")},
-        {DbViewName::DefaultWithSim, QStringLiteral("viewDefaultSim")},
-        {DbViewName::Easa,           QStringLiteral("viewEasa")},
-        {DbViewName::EasaWithSim,    QStringLiteral("viewEasaSim")},
-        {DbViewName::SimulatorOnly,  QStringLiteral("viewSimulators")},
+    const static inline QMap<LogbookView, QString> DATABASE_VIEWS = {
+        {LogbookView::Default,        QStringLiteral("viewDefault")},
+        {LogbookView::DefaultWithSim, QStringLiteral("viewDefaultSim")},
+        {LogbookView::Easa,           QStringLiteral("viewEasa")},
+        {LogbookView::EasaWithSim,    QStringLiteral("viewEasaSim")},
+        {LogbookView::SimulatorOnly,  QStringLiteral("viewSimulators")},
     };
-    const QMap<DbViewName, QString> DATABASE_VIEW_DISPLAY_NAMES = {
-        {DbViewName::Default,        tr("Default")},
-        {DbViewName::DefaultWithSim, tr("Default with Simulator")},
-        {DbViewName::Easa,           tr("EASA-FCL")},
-        {DbViewName::EasaWithSim,    tr("EASA-FCL with Simulator")},
-        {DbViewName::SimulatorOnly,  tr("Simulator Sessions Only")},
+    const QMap<LogbookView, QString> DATABASE_VIEW_DISPLAY_NAMES = {
+        {LogbookView::Default,        tr("Default")},
+        {LogbookView::DefaultWithSim, tr("Default with Simulator")},
+        {LogbookView::Easa,           tr("EASA-FCL")},
+        {LogbookView::EasaWithSim,    tr("EASA-FCL with Simulator")},
+        {LogbookView::SimulatorOnly,  tr("Simulator Sessions Only")},
     };
     const static inline QMap<PilotFunction, QString> PILOT_FUNCTIONS = {
         {PilotFunction::PIC,   QStringLiteral("PIC")},
@@ -265,7 +328,6 @@ const inline auto  DATABASE_SCHEMA               = QStringLiteral(":/database/da
 const inline auto  DATABASE_TEMPLATE_AIRCRAFT    = QStringLiteral(":/database/templates/aircraft.json");
 const inline auto  DATABASE_TEMPLATE_AIRPORT     = QStringLiteral(":/database/templates/airports.json");
 const inline auto  DATABASE_TEMPLATE_CHANGELOG   = QStringLiteral(":/database/templates/changelog.json");
-const inline auto  DATABASE_TEMPLATE_CURRENCIES  = QStringLiteral(":/database/templates/currencies.json");
 
 const inline auto  LOGO                          = QStringLiteral(":/icons/opl-icons/logos/logo_text.png");
 const inline auto  ICON_MAIN                     = QStringLiteral(":/icons/opl-icons/app/icon_main.png");
@@ -300,17 +362,16 @@ namespace CssStyles {
 const inline auto  RED_BORDER = QStringLiteral("border: 1px solid red");
 } // namespace Styles
 
-namespace Format {
+//namespace Format {
 
-const inline auto TIME_FORMAT = QStringLiteral("hh:mm");
+//const inline auto TIME_FORMAT = QStringLiteral("hh:mm");
 
-} // namespace Format
+//} // namespace Format
 
 namespace RegEx {
 
 const inline auto RX_PHONE_NUMBER  = QRegularExpression(QStringLiteral("^[+]{0,1}[0-9\\-\\s]+"));
-const inline auto RX_EMAIL_ADDRESS = QRegularExpression(QStringLiteral("\\A[a-z0-9!#$%&'*+/=?^_‘{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_‘{|}~-]+)*@"));
-const inline auto RX_TIME_ENTRY    = QRegularExpression(QStringLiteral("([01]?[0-9]|2[0-3]):?[0-5][0-9]?"));
+const inline auto RX_TIME_ENTRY    = QRegularExpression(QStringLiteral("^(?:(?:([01]?\\d|2[0-3])(?::?)([0-5]\\d))|(?:([01]?\\d|2[0-3])([0-5]\\d))|(?:([1-9]|[1-9]\\d)\\:([0-5]\\d)?)|(?:([01]?\\d|2[0-3])\\.([0-5]?\\d)))$"));
 const inline auto RX_AIRPORT_CODE  = QRegularExpression(QStringLiteral("[a-zA-Z0-9]{1,4}"));
 
 } // namespace RegEx

+ 2 - 2
src/testing/importCrewlounge/processflights.cpp

@@ -54,14 +54,14 @@ void ProcessFlights::processParsedData()
         auto time_off = QTime::fromString(row[4], QStringLiteral("hh:mm"));
         if (!time_off.isValid())
             time_off = QTime::fromString(row[4], QStringLiteral("h:mm"));
-        int tofb = OPL::Time::fromString(time_off.toString()).toMinutes();
+        int tofb = OPL::Time::fromString(time_off.toString(), OPL::DateTimeFormat()).toMinutes();
         new_flight_data.insert(OPL::FlightEntry::TOFB, tofb);
 
         auto time_on = QTime::fromString(row[5], QStringLiteral("hh:mm"));
         if (!time_on.isValid())
             time_on = QTime::fromString(row[5], QStringLiteral("h:mm"));
 
-        int tonb = OPL::Time::fromString(time_on.toString()).toMinutes();
+        int tonb = OPL::Time::fromString(time_on.toString(), OPL::DateTimeFormat()).toMinutes();
         new_flight_data.insert(OPL::FlightEntry::TONB, tonb);
 
         // map pilots

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor