Warning, /pim/itinerary/src/app/main.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2018 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 import QtCore as QtCore 0008 import QtQuick 0009 import QtQuick.Layouts 0010 import QtQuick.Controls as QQC2 0011 import QtQuick.Dialogs 0012 import QtLocation as QtLocation 0013 import org.kde.kirigami as Kirigami 0014 import org.kde.solidextras as Solid 0015 import org.kde.kpublictransport.onboard 0016 import internal.org.kde.kcalendarcore as KCalendarCore 0017 import org.kde.itinerary 0018 0019 Kirigami.ApplicationWindow { 0020 id: root 0021 title: i18n("KDE Itinerary") 0022 0023 width: Kirigami.Settings.isMobile ? 480 : 800 0024 height: Kirigami.Settings.isMobile ? 720 : 650 0025 minimumWidth: 300 0026 minimumHeight: 400 0027 Kirigami.PagePool { 0028 id: pagepool 0029 } 0030 pageStack { 0031 columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn 0032 0033 globalToolBar { 0034 style: Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.Breadcrumb : Kirigami.ApplicationHeaderStyle.Auto 0035 showNavigationButtons: Kirigami.ApplicationHeaderStyle.ShowBackButton 0036 } 0037 } 0038 0039 // pop pages when not in use 0040 Connections { 0041 target: applicationWindow().pageStack 0042 function onCurrentIndexChanged() { 0043 // wait for animation to finish before popping pages 0044 timer.restart(); 0045 } 0046 } 0047 0048 Timer { 0049 id: timer 0050 interval: 300 0051 onTriggered: { 0052 let currentIndex = applicationWindow().pageStack.currentIndex; 0053 while (applicationWindow().pageStack.depth > (currentIndex + 1) && currentIndex >= 0) { 0054 applicationWindow().pageStack.pop(); 0055 } 0056 } 0057 } 0058 0059 property list<Kirigami.Action> importActions: [ 0060 Kirigami.Action { 0061 text: i18n("Open File...") 0062 icon.name: "document-open" 0063 shortcut: StandardKey.Open 0064 onTriggered: { 0065 importFileDialog.open(); 0066 } 0067 }, 0068 Kirigami.Action { 0069 text: i18n("Paste") 0070 icon.name: "edit-paste" 0071 enabled: ApplicationController.hasClipboardContent 0072 shortcut: StandardKey.Paste 0073 onTriggered: { 0074 ApplicationController.importFromClipboard(); 0075 } 0076 }, 0077 Kirigami.Action { 0078 text: i18n("Scan Barcode...") 0079 icon.name: "view-barcode-qr" 0080 onTriggered: { 0081 pageStack.layers.push(scanBarcodeComponent); 0082 } 0083 }, 0084 Kirigami.Action { 0085 icon.name: "view-calendar-day" 0086 text: i18n("Add from Calendar...") 0087 onTriggered: { 0088 PermissionManager.requestPermission(Permission.ReadCalendar, function() { 0089 if (!calendarSelector.model) { 0090 calendarSelector.model = calendarModel.createObject(root); 0091 } 0092 calendarSelector.open(); 0093 }) 0094 } 0095 visible: KCalendarCore.CalendarPluginLoader.hasPlugin 0096 }, 0097 // TODO this should not be hardcoded here, but dynamically filled based on what online ticket 0098 // sources we support 0099 Kirigami.Action { 0100 text: i18n("Deutsche Bahn Online Ticket...") 0101 icon.name: "download" 0102 onTriggered: { 0103 pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "OnlineImportPage"), { 0104 source: "db", 0105 }); 0106 } 0107 }, 0108 Kirigami.Action { 0109 text: i18n("SNCF Online Ticket...") 0110 icon.name: "download" 0111 onTriggered: { 0112 pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "OnlineImportPage"), { 0113 source: "sncf" 0114 }); 0115 } 0116 } 0117 ] 0118 0119 FileDialog { 0120 id: importFileDialog 0121 fileMode: FileDialog.OpenFile 0122 title: i18n("Import Reservation") 0123 currentFolder: QtCore.StandardPaths.writableLocation(QtCore.StandardPaths.DocumentsLocation) 0124 // Android has no file type selector, we get the superset of all filters there since Qt6 (apart from "all"), 0125 // so don't set any filters on Android in order to be able to open everything we can read 0126 nameFilters: Qt.platform.os === "android" ? 0127 [i18n("All Files (*.*)")] : 0128 [i18n("All Files (*.*)"), i18n("PkPass files (*.pkpass)"), i18n("PDF files (*.pdf)"), i18n("iCal events (*.ics)"), i18n("KDE Itinerary files (*.itinerary)")] 0129 onAccepted: ApplicationController.importFromUrl(selectedFile) 0130 } 0131 0132 FileDialog { 0133 id: exportDialog 0134 fileMode: FileDialog.SaveFile 0135 title: i18n("Export Itinerary Data") 0136 currentFolder: QtCore.StandardPaths.writableLocation(QtCore.StandardPaths.DocumentsLocation) 0137 nameFilters: [i18n("KDE Itinerary files (*.itinerary)")] 0138 onAccepted: ApplicationController.exportToFile(selectedFile) 0139 } 0140 0141 Component { 0142 id: calendarModel 0143 // needs to be created on demand, after we have calendar access permissions 0144 KCalendarCore.CalendarListModel {} 0145 } 0146 Component { 0147 id: calendarImportPage 0148 CalendarImportPage {} 0149 } 0150 footer: NavigationBar { 0151 id: navigationbar 0152 } 0153 CalendarSelectionSheet { 0154 id: calendarSelector 0155 // parent: root.Overlay.overlay 0156 onCalendarSelected: pageStack.push(calendarImportPage, { calendar: calendar }); 0157 } 0158 0159 OnboardStatus { 0160 id: onboardStatus 0161 } 0162 0163 globalDrawer: Kirigami.GlobalDrawer { 0164 title: i18n("KDE Itinerary") 0165 titleIcon: "map-symbolic" 0166 isMenu: true 0167 actions: [ 0168 Kirigami.Action { 0169 text: i18n("Import...") 0170 icon.name: "document-import" 0171 children: importActions 0172 }, 0173 Kirigami.Action { 0174 text: i18n("Check for Updates") 0175 icon.name: "view-refresh" 0176 enabled: Solid.NetworkStatus.connectivity != Solid.NetworkStatus.No 0177 shortcut: StandardKey.Refresh 0178 onTriggered: { 0179 LiveDataManager.checkForUpdates(); 0180 } 0181 }, 0182 Kirigami.Action { 0183 text: i18n("Download Maps") 0184 icon.name: "download" 0185 enabled: Solid.NetworkStatus.connectivity != Solid.NetworkStatus.No 0186 icon.color: Solid.NetworkStatus.metered != Solid.NetworkStatus.No ? Kirigami.Theme.neutralTextColor : Kirigami.Theme.textColor 0187 onTriggered: MapDownloadManager.download(); 0188 }, 0189 Kirigami.Action { 0190 id: statsAction 0191 text: i18n("Statistics") 0192 icon.name: "view-statistics" 0193 enabled: pageStack.layers.depth < 2 0194 onTriggered: pageStack.layers.push(statisticsComponent) 0195 }, 0196 Kirigami.Action { 0197 id: healthCertAction 0198 text: i18n("Health Certificates") 0199 icon.name: "cross-shape" 0200 onTriggered: { 0201 healtCertificateComponent.source = Qt.resolvedUrl("HealthCertificatePage.qml") 0202 pageStack.layers.push(healtCertificateComponent.item) 0203 } 0204 enabled: pageStack.layers.depth < 2 0205 visible: ApplicationController.hasHealthCertificateSupport && ApplicationController.healthCertificateManager.rowCount() > 0 0206 }, 0207 Kirigami.Action { 0208 id: liveAction 0209 text: i18n("Live Status") 0210 icon.name: "media-playback-playing" 0211 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "LiveStatusPage")) 0212 enabled: pageStack.layers.depth < 2 0213 visible: onboardStatus.status == OnboardStatus.Onboard 0214 }, 0215 Kirigami.Action { 0216 id: settingsAction 0217 text: i18n("Settings...") 0218 icon.name: "settings-configure" 0219 enabled: pageStack.layers.depth < 2 0220 shortcut: StandardKey.Preferences 0221 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "SettingsPage")) 0222 }, 0223 Kirigami.Action { 0224 text: i18n("Export...") 0225 icon.name: "export-symbolic" 0226 onTriggered: exportDialog.open() 0227 }, 0228 Kirigami.Action { 0229 text: i18n("Help") 0230 icon.name: "help-contents" 0231 enabled: pageStack.layers.depth < 2 0232 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "WelcomePage")) 0233 }, 0234 Kirigami.Action { 0235 id: aboutAction 0236 text: i18n("About") 0237 icon.name: "help-about-symbolic" 0238 enabled: pageStack.layers.depth < 2 0239 onTriggered: pageStack.pushDialogLayer(Qt.resolvedUrl("AboutPage.qml")) 0240 }, 0241 Kirigami.Action { 0242 id: devModeAction 0243 text: "Development" 0244 icon.name: "tools-report-bug" 0245 enabled: pageStack.layers.depth < 2 0246 onTriggered: pageStack.layers.push(Qt.createComponent("org.kde.itinerary", "DevelopmentModePage")); 0247 visible: Settings.developmentMode 0248 } 0249 ] 0250 } 0251 contextDrawer: Kirigami.ContextDrawer { 0252 id: contextDrawer 0253 actions: { 0254 if (pageStack.layers.depth > 1) 0255 return (pageStack.layers.currentItem as Kirigami.Page)?.actions ?? []; 0256 else (pageStack.currentItem as Kirigami.Page)?.actions ?? []; 0257 } 0258 } 0259 pageStack.initialPage: pagepool.loadPage(Qt.resolvedUrl("TimelinePage.qml")) 0260 0261 DropArea { 0262 id: topDropArea 0263 onEntered: { 0264 if (!drag.hasUrls) { 0265 drag.accepted = false; 0266 } 0267 } 0268 onDropped: { 0269 for (var i in drop.urls) { 0270 ApplicationController.importFromUrl(drop.urls[i]); 0271 } 0272 } 0273 anchors.fill: parent 0274 } 0275 0276 Connections { 0277 target: ApplicationController 0278 function onInfoMessage(msg) { showPassiveNotification(msg, "short"); } 0279 function onOpenPageRequested(page) { 0280 while (pageStack.layers.depth > 1) { 0281 pageStack.layers.pop(); 0282 } 0283 switch (page) { 0284 case "currentTicket": 0285 pagepool.loadPage("TimelinePage.qml").showDetailsPageForReservation(TimelineModel.currentBatchId); 0286 break; 0287 case "stats": 0288 statsAction.trigger(); 0289 break; 0290 case "healthCert": 0291 healthCertAction.trigger(); 0292 break; 0293 case "live": 0294 liveAction.trigger(); 0295 break; 0296 default: 0297 console.warn("Requested to open unknown page", page); 0298 } 0299 } 0300 } 0301 0302 Connections { 0303 target: MapDownloadManager 0304 function onFinished() { showPassiveNotification(i18n("Map download finished."), "short"); } 0305 } 0306 0307 Component.onCompleted: { 0308 if (ReservationManager.isEmpty()) { 0309 pageStack.push(Qt.createComponent("org.kde.itinerary", "WelcomePage")); 0310 } 0311 } 0312 0313 Component { 0314 id: mainPageComponent 0315 TimelinePage {} 0316 } 0317 Component { 0318 id: scanBarcodeComponent 0319 BarcodeScannerPage {} 0320 } 0321 Component { 0322 id: statisticsComponent 0323 StatisticsPage { 0324 reservationManager: ReservationManager 0325 tripGroupManager: TripGroupManager 0326 } 0327 } 0328 Component { 0329 id: passComponent 0330 PassPage {} 0331 } 0332 // replace loader with component once we depend on KHealthCertificate unconditionally 0333 Loader { 0334 id: healtCertificateComponent 0335 } 0336 Component { 0337 id: journeySectionPage 0338 JourneySectionPage {} 0339 } 0340 Component { 0341 id: journeyPathPage 0342 JourneyPathPage {} 0343 } 0344 Component { 0345 id: indoorMapPage 0346 IndoorMapPage {} 0347 } 0348 0349 // "singleton" OSM QtLocation plugin 0350 // we only want one of these, and created only when absolutely necessary 0351 // as this triggers network operations on creation already 0352 function osmPlugin() { 0353 if (!__qtLocationOSMPlugin) { 0354 __qtLocationOSMPlugin = __qtLocationOSMPluginComponent.createObject(); 0355 } 0356 return __qtLocationOSMPlugin; 0357 } 0358 property var __qtLocationOSMPlugin: null 0359 Component { 0360 id: __qtLocationOSMPluginComponent 0361 QtLocation.Plugin { 0362 name: "osm" 0363 QtLocation.PluginParameter { name: "osm.useragent"; value: ApplicationController.userAgent } 0364 QtLocation.PluginParameter { name: "osm.mapping.providersrepository.address"; value: "https://autoconfig.kde.org/qtlocation/" } 0365 } 0366 } 0367 0368 // workaround for Back key handling on Android causing the application to close 0369 // on secondary layers if those have no focus (either explicitly or via interaction) 0370 // ### if there isn't a proepr fix for this, should this happen in Kirigami instead? 0371 Connections { 0372 target: pageStack.layers 0373 function onCurrentItemChanged() { 0374 pageStack.layers.currentItem.forceActiveFocus(); 0375 } 0376 } 0377 }