Warning, /libraries/kpublictransport/tests/journeyquery.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
0008 import QtQuick
0009 import QtQuick.Layouts
0010 import QtQuick.Controls as QQC2
0011 import QtQuick.Dialogs as Dialogs
0012 import org.kde.kirigami as Kirigami
0013 import org.kde.kpublictransport
0014 import org.kde.example
0015 
0016 Kirigami.ApplicationWindow {
0017     title: "Journey Query"
0018     width: 640
0019     height: 800
0020 
0021     pageStack.initialPage: journyQueryPage
0022 
0023     Manager {
0024         id: ptMgr;
0025     }
0026     Settings {
0027         id: settings
0028         property alias allowInsecureBackends: ptMgr.allowInsecureBackends
0029         property alias enabledBackends: ptMgr.enabledBackends
0030         property alias disabledBackends: ptMgr.disabledBackends
0031     }
0032 
0033     JourneyQueryModel {
0034         id: journeyModel
0035         manager: ptMgr
0036     }
0037 
0038     JourneyTitleModel {
0039         id: titleModel
0040         sourceModel: journeyModel
0041     }
0042 
0043     globalDrawer: Kirigami.GlobalDrawer {
0044         actions: [
0045             Kirigami.Action {
0046                 text: "Save..."
0047                 icon.name: "document-save"
0048                 onTriggered: fileDialog.open();
0049             },
0050             Kirigami.Action {
0051                 icon.name: "help-about-symbolic"
0052                 text: "Current Data Sources"
0053                 enabled: journeyModel.attributions.length > 0
0054                 onTriggered: {
0055                     aboutSheet.attributions = Qt.binding(function() { return journeyModel.attributions; });
0056                     aboutSheet.open();
0057                 }
0058             },
0059             Kirigami.Action {
0060                 icon.name: "help-about-symbolic"
0061                 text: "All Data Sources"
0062                 onTriggered: {
0063                     aboutSheet.attributions = Qt.binding(function() { return ptMgr.attributions; });
0064                     aboutSheet.open();
0065                 }
0066             },
0067             Kirigami.Action {
0068                 icon.name: "settings-configure"
0069                 text: "Backends"
0070                 onTriggered: pageStack.push(backendPage)
0071             }
0072         ]
0073     }
0074 
0075     Dialogs.FileDialog {
0076         id: fileDialog
0077         title: "Save Journey Data"
0078         fileMode: Dialogs.FileDialog.SaveFile
0079         nameFilters: ["JSON files (*.json)"]
0080         onAccepted: ExampleUtil.saveTo(journeyModel, fileDialog.selectedFile);
0081     }
0082 
0083     TestLocationsModel { id: exampleModel }
0084     AttributionSheet { id: aboutSheet }
0085     LocationDetailsSheet { id:locationDetailsSheet }
0086 
0087     function displayDuration(dur)
0088     {
0089         if (dur < 60)
0090             return "<1min";
0091         if (dur < 3600)
0092             return Math.floor(dur/60) + "min";
0093         return Math.floor(dur/3600) + ":" + Math.floor((dur % 3600)/60)
0094     }
0095 
0096     function displayDistance(dist)
0097     {
0098         if (dist == 0)
0099             return "";
0100         if (dist < 1000)
0101             return dist + "m";
0102         return Math.floor(dist/1000) + "km";
0103     }
0104 
0105     function locationName(loc)
0106     {
0107         switch(loc.type) {
0108             case Location.Stop: return "🚏 " + loc.name;
0109             case Location.RentedVehicleStation: return "🚏🚲 " + loc.name;
0110             case Location.RentedVehicle: return "🚲 " + loc.name;
0111             case Location.Place: return loc.name;
0112         }
0113     }
0114 
0115     Component {
0116         id: vehicleLayoutPage
0117         VehicleLayoutPage {
0118             publicTransportManager: ptMgr
0119         }
0120     }
0121 
0122     Component {
0123         id: indoorMapPage
0124         IndoorMapPage {}
0125     }
0126 
0127     Component {
0128         id: journeyDelegate
0129         QQC2.ItemDelegate {
0130             enabled: modelData.disruptionEffect != Disruption.NoService
0131             highlighted: false
0132             width: ListView.view.width
0133             contentItem: RowLayout {
0134                 id: topLayout
0135 
0136                 Kirigami.Icon {
0137                     id: icon
0138                     source: modelData.route.line.hasLogo ? modelData.route.line.logo : modelData.route.line.modeLogo
0139                     width: height
0140                     height: Kirigami.Units.iconSizes.large
0141                     visible: source != ""
0142                 }
0143 
0144                 Rectangle {
0145                     id: colorBar
0146                     width: Kirigami.Units.largeSpacing
0147                     color: modelData.route.line.hasColor ? modelData.route.line.color : "transparent"
0148                     Layout.fillHeight: true
0149                     visible: icon.source == ""
0150                 }
0151 
0152                 QQC2.Label {
0153                     text: {
0154                         switch (modelData.mode) {
0155                             case JourneySection.PublicTransport:
0156                             {
0157                                 switch (modelData.route.line.mode) {
0158                                     case Line.Air: return "✈️";
0159                                     case Line.Boat: return "🛥️";
0160                                     case Line.Bus: return "🚍";
0161                                     case Line.BusRapidTransit: return "🚌";
0162                                     case Line.Coach: return "🚌";
0163                                     case Line.Ferry: return "⛴️";
0164                                     case Line.Funicular: return "🚞";
0165                                     case Line.LocalTrain: return "🚆";
0166                                     case Line.LongDistanceTrain: return "🚄";
0167                                     case Line.Metro: return "🚇";
0168                                     case Line.RailShuttle: return "🚅";
0169                                     case Line.RapidTransit: return "🚊";
0170                                     case Line.Shuttle: return "🚐";
0171                                     case Line.Taxi: return "🚕";
0172                                     case Line.Train: return "🚆";
0173                                     case Line.Tramway: return "🚈";
0174                                     case Line.RideShare: return "🚗";
0175                                     default: return "?";
0176                                 }
0177                                 break;
0178                             }
0179                             case JourneySection.Walking: return "🚶";
0180                             case JourneySection.Waiting: return "⌛";
0181                             case JourneySection.Transfer: return "⇄";
0182                             case JourneySection.RentedVehicle:
0183                             {
0184                                 switch (modelData.rentalVehicle.type) {
0185                                     case RentalVehicle.Bicycle: return "🔑🚲";
0186                                     case RentalVehicle.Pedelec: return "🔑🔌🚲";
0187                                     case RentalVehicle.ElectricKickScooter: return "🔑🛴";
0188                                     case RentalVehicle.ElectricMoped: return "🔑🛵";
0189                                     case RentalVehicle.Car: return "🔑🚗";
0190                                     default: return "?";
0191                                 }
0192                             }
0193                             case JourneySection.IndividualTransport:
0194                             {
0195                                 switch (modelData.individualTransport.mode) {
0196                                     case IndividualTransport.Walk: return "🚶";
0197                                     case IndividualTransport.Bike: return "🚲";
0198                                     case IndividualTransport.Car: return "🚗";
0199                                 }
0200                             }
0201                             default: return "?";
0202                         }
0203                     }
0204                     font.pointSize: Kirigami.Theme.defaultFont.pointSize * 2
0205                     visible: icon.source == ""
0206                 }
0207 
0208                 ColumnLayout {
0209                     Layout.fillWidth: true
0210                     RowLayout {
0211                         QQC2.Label {
0212                             text: "From: <a href=\"#from\">" + locationName(modelData.from) + "</a> Platform: " + modelData.scheduledDeparturePlatform
0213                             onLinkActivated: {
0214                                 locationDetailsSheet.location = modelData.from;
0215                                 locationDetailsSheet.open();
0216                             }
0217                         }
0218                         QQC2.Label {
0219                             text: modelData.expectedDeparturePlatform
0220                             color: modelData.departurePlatformChanged ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor
0221                             visible: modelData.hasExpectedDeparturePlatform
0222                         }
0223                     }
0224                     RowLayout {
0225                         QQC2.Label {
0226                             text: "Departure: " + modelData.scheduledDepartureTime.toTimeString()
0227                         }
0228                         QQC2.Label {
0229                             text: (modelData.departureDelay >= 0 ? "+" : "") + modelData.departureDelay
0230                             color: modelData.departureDelay > 1 ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor
0231                             visible: modelData.hasExpectedDepartureTime
0232                         }
0233                         QQC2.Label {
0234                             text: "<a href=\"#layout\">vehicle</a>"
0235                             visible: modelData.route.line.mode == Line.LongDistanceTrain || modelData.route.line.mode == Line.Train || modelData.route.name !== ""
0236                             onLinkActivated: applicationWindow().pageStack.push(vehicleLayoutPage, {"departure": modelData.departure });
0237                             Layout.fillWidth: true
0238                             horizontalAlignment: Text.Right
0239                         }
0240                     }
0241                     QQC2.Label {
0242                         Layout.fillWidth: true
0243                         text: {
0244                             switch (modelData.mode) {
0245                             case JourneySection.PublicTransport:
0246                             {
0247                                 if (modelData.route.name !== "") {
0248                                     return modelData.route.line.modeString + " " + modelData.route.line.name + " (" + modelData.route.name
0249                                         + ") " + displayDuration(modelData.duration) + " / " + displayDistance(modelData.distance)
0250                                 }
0251                                 return modelData.route.line.modeString + " " + modelData.route.line.name + " " + displayDuration(modelData.duration) + " / " + displayDistance(modelData.distance)
0252                             }
0253                             case JourneySection.Walking:
0254                                 return "Walk " + displayDuration(modelData.duration) + " / " + displayDistance(modelData.distance)
0255                             case JourneySection.Transfer:
0256                                 return "Transfer " + displayDuration(modelData.duration)  + " / " + displayDistance(modelData.distance)
0257                             case JourneySection.Waiting:
0258                                 return "Wait " + displayDuration(modelData.duration)
0259                             case JourneySection.RentedVehicle:
0260                                 return "Drive (" + modelData.rentalVehicle.network.name + ") " + displayDuration(modelData.duration)  + " / " + displayDistance(modelData.distance);
0261                             case JourneySection.IndividualTransport:
0262                                 return "Drive " + displayDuration(modelData.duration) + " / " + displayDistance(modelData.distance)
0263                             return "???";
0264                         }}
0265                     }
0266                     RowLayout {
0267                         QQC2.Label {
0268                             text: "To: <a href=\"#to\">" + locationName(modelData.to) + "</a> Platform: " + modelData.scheduledArrivalPlatform
0269                             onLinkActivated: {
0270                                 locationDetailsSheet.location = modelData.to;
0271                                 locationDetailsSheet.open();
0272                             }
0273                         }
0274                         QQC2.Label {
0275                             text: modelData.expectedArrivalPlatform
0276                             color: modelData.arrivalPlatformChanged ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor
0277                             visible: modelData.hasExpectedArrivalPlatform
0278                         }
0279                     }
0280                     RowLayout {
0281                         QQC2.Label {
0282                             text: "Arrival: " + modelData.scheduledArrivalTime.toTimeString()
0283                         }
0284                         QQC2.Label {
0285                             text: (modelData.arrivalDelay >= 0 ? "+" : "") + modelData.arrivalDelay
0286                             color: modelData.arrivalDelay > 1 ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.positiveTextColor
0287                             visible: modelData.hasExpectedArrivalTime
0288                         }
0289                         QQC2.Label {
0290                             text: "<a href=\"#layout\">vehicle</a>"
0291                             visible: modelData.route.line.mode == Line.LongDistanceTrain
0292                             onLinkActivated: applicationWindow().pageStack.push(vehicleLayoutPage, {"departure": modelData.arrival });
0293                             Layout.fillWidth: true
0294                             horizontalAlignment: Text.Right
0295                         }
0296                     }
0297                     RowLayout {
0298                         visible: modelData.loadInformation.length > 0
0299                         QQC2.Label {
0300                             text: "Load: ";
0301                         }
0302                         Repeater {
0303                             model: modelData.loadInformation
0304                             RowLayout {
0305                                 QQC2.Label {
0306                                     text: {
0307                                         switch (modelData.load) {
0308                                             case Load.Low: return "Low";
0309                                             case Load.Medium: return "Medium";
0310                                             case Load.High: return "High";
0311                                             case Load.Full: return "Full";
0312                                             default: return "?"
0313                                         }
0314                                     }
0315                                     color: {
0316                                         switch (modelData.load) {
0317                                             case Load.Low: return Kirigami.Theme.positiveTextColor;
0318                                             case Load.Medium: return Kirigami.Theme.neutralTextColor;
0319                                             case Load.High:
0320                                             case Load.Full:
0321                                                 return Kirigami.Theme.negativeTextColor;
0322                                             default:
0323                                                 return Kirigami.Theme.textColor;
0324                                         }
0325                                     }
0326                                 }
0327                                 QQC2.Label {
0328                                     text: "(class " + modelData.seatingClass + ")"
0329                                     visible: modelData.seatingClass != ""
0330                                 }
0331                             }
0332                         }
0333                     }
0334                     QQC2.Label {
0335                         text: modelData.notes.join("<br/>")
0336                         textFormat: Text.RichText
0337                         visible: modelData.notes.length > 0
0338                         font.italic: true
0339                     }
0340                 }
0341             }
0342             onClicked: {
0343                 if (modelData.mode == JourneySection.PublicTransport) {
0344                     applicationWindow().pageStack.push(journeySectionPage, {"journeySection": modelData});
0345                 } else if (modelData.path.sectionCount > 1) {
0346                     applicationWindow().pageStack.push(pathPage, {"path": modelData.path});
0347                 }
0348             }
0349         }
0350     }
0351 
0352     Component {
0353         id: backendPage
0354         BackendPage {
0355             publicTransportManager: ptMgr
0356         }
0357     }
0358 
0359     Component {
0360         id: journeySectionPage
0361         JourneySectionPage {}
0362     }
0363 
0364     Component {
0365         id: pathPage
0366         PathPage {}
0367     }
0368 
0369     readonly property var individualTransportModes: [
0370         [{ mode: IndividualTransport.Walk }],
0371         [{ mode: IndividualTransport.Bike }],
0372         [{ mode: IndividualTransport.Bike, qualifier: IndividualTransport.Park }],
0373         [{ mode: IndividualTransport.Bike, qualifier: IndividualTransport.Rent }],
0374         [{ mode: IndividualTransport.Car, qualifier: IndividualTransport.Park }],
0375         [{ mode: IndividualTransport.Car, qualifier: IndividualTransport.Rent }],
0376         [{ mode: IndividualTransport.Car, qualifier: IndividualTransport.Pickup }],
0377         [{ mode: IndividualTransport.Car, qualifier: IndividualTransport.Dropoff }],
0378     ]
0379     ListModel {
0380         id: individualTransportModesModel
0381         ListElement { name: "Walk" }
0382         ListElement { name: "Take Bike" }
0383         ListElement { name: "Park Bike" }
0384         ListElement { name: "Rent Bike" }
0385         ListElement { name: "Park Car" }
0386         ListElement { name: "Rent Car" }
0387         ListElement { name: "Pickup by car" }
0388         ListElement { name: "Drop-off by car" }
0389     }
0390 
0391     Component {
0392         id: journyQueryPage
0393         Kirigami.Page {
0394             Settings {
0395                 id: settings
0396                 property alias singleBackend: backendBox.checked
0397                 property alias backend: backendSelector.currentIndex
0398                 property alias maxResults: maxResults.text
0399                 property alias includeIntermediateStops: intermediateStops.checked
0400                 property alias includePaths: includePaths.checked
0401                 property alias accessMode: accessMode.currentIndex
0402                 property alias egressMode: egressMode.currentIndex
0403             }
0404 
0405             ColumnLayout {
0406                 anchors.fill: parent
0407                 QQC2.CheckBox {
0408                     id: searchDirection
0409                     text: checked ? "Time is arrival" : "Time is departure"
0410                 }
0411 
0412                 QQC2.CheckBox {
0413                     text: "Allow insecure backends"
0414                     checked: ptMgr.allowInsecureBackends
0415                     onToggled: ptMgr.allowInsecureBackends = checked
0416                 }
0417 
0418                 RowLayout {
0419                     QQC2.CheckBox {
0420                         id: backendBox
0421                         text: "Select Backend:"
0422                     }
0423                     QQC2.ComboBox {
0424                         id: backendSelector
0425                         Layout.fillWidth: true
0426                         textRole: "identifier"
0427                         model: BackendModel {
0428                             manager: ptMgr
0429                         }
0430                         enabled: backendBox.checked
0431                     }
0432                 }
0433 
0434                 RowLayout {
0435                     QQC2.CheckBox {
0436                         id: ptMode
0437                         checked: true
0438                         text: "Public Transport"
0439                     }
0440                     QQC2.CheckBox {
0441                         id: rentalMode
0442                         checked: true
0443                         text: "Rental Vehicles"
0444                     }
0445                 }
0446                 RowLayout {
0447                     QQC2.Label { text: "Results:" }
0448                     QQC2.TextField {
0449                         id: maxResults
0450                         text: "10"
0451                     }
0452                     QQC2.CheckBox {
0453                         id: intermediateStops
0454                         checked: true
0455                         text: "Intermediate stops"
0456                     }
0457                     QQC2.CheckBox {
0458                         id: includePaths
0459                         text: "Paths"
0460                     }
0461                 }
0462 
0463                 QQC2.ComboBox {
0464                     id: accessMode
0465                     Layout.fillWidth: true
0466                     model: individualTransportModesModel
0467                     textRole: "name"
0468                 }
0469                 QQC2.ComboBox {
0470                     id: fromSelector
0471                     Layout.fillWidth: true
0472                     model: exampleModel
0473                     textRole: "label"
0474                     onCurrentIndexChanged: {
0475                         var obj = exampleModel.get(currentIndex);
0476                         fromName.text = obj.name == "" ? obj.label : obj.name;
0477                         fromLon.text = obj.lon;
0478                         fromLat.text = obj.lat;
0479                         if (toSelector.currentIndex == currentIndex) {
0480                             toSelector.currentIndex = (currentIndex + 1) % count;
0481                         }
0482                     }
0483                 }
0484                 RowLayout {
0485                     QQC2.TextField {
0486                         id: fromName
0487                     }
0488                     QQC2.TextField {
0489                         id: fromLon
0490                     }
0491                     QQC2.TextField {
0492                         id: fromLat
0493                     }
0494                 }
0495 
0496                 QQC2.ComboBox {
0497                     id: toSelector
0498                     Layout.fillWidth: true
0499                     model: exampleModel
0500                     textRole: "label"
0501                     onCurrentIndexChanged: {
0502                         var obj = exampleModel.get(currentIndex);
0503                         toName.text = obj.name == "" ? obj.label : obj.name;
0504                         toLon.text = obj.lon;
0505                         toLat.text = obj.lat;
0506                         if (fromSelector.currentIndex == currentIndex) {
0507                             fromSelector.currentIndex = (currentIndex - 1 + count) % count;
0508                         }
0509                     }
0510                 }
0511                 RowLayout {
0512                     QQC2.TextField {
0513                         id: toName
0514                     }
0515                     QQC2.TextField {
0516                         id: toLon
0517                     }
0518                     QQC2.TextField {
0519                         id: toLat
0520                     }
0521                 }
0522                 QQC2.ComboBox {
0523                     id: egressMode
0524                     Layout.fillWidth: true
0525                     model: individualTransportModesModel
0526                     textRole: "name"
0527                 }
0528                 QQC2.ComboBox {
0529                     id: lineModeSelector
0530                     Layout.fillWidth: true
0531                     model: [ "All", "Only long distance", "Local public transport", "Local trains only", "Rapit transit/metro/tram", "Bus" ]
0532                     property var currentMode: {
0533                         switch (currentIndex) {
0534                             case 1: return [Line.LongDistanceTrain, Line.Train];
0535                             case 2: return [Line.LocalTrain, Line.RapidTransit, Line.Metro, Line.Tramway, Line.Funicular, Line.Bus];
0536                             case 3: return [Line.LocalTrain];
0537                             case 4: return [Line.RapidTransit, Line.Metro, Line.Tramway, Line.Funicular];
0538                             case 5: return [Line.Bus];
0539                         }
0540                         return [];
0541                     }
0542                 }
0543 
0544                 RowLayout {
0545                     function setupRequestCommon()
0546                     {
0547                         journeyModel.request.dateTimeMode = searchDirection.checked ? JourneyRequest.Arrival : JourneyRequest.Departure;
0548                         journeyModel.request.dateTime = new Date(new Date().getTime() + (searchDirection.checked ? 7200000 : 0));
0549                         journeyModel.request.backends = backendBox.checked ? [ backendSelector.currentText ] : [];
0550                         journeyModel.request.downloadAssets = true
0551                         journeyModel.request.modes = (ptMode.checked ?  JourneySection.PublicTransport : JourneySection.Invalid)
0552                             | (rentalMode.checked ? JourneySection.RentedVehicle : JourneySection.Invalid);
0553                         journeyModel.request.maximumResults = maxResults.text;
0554                         journeyModel.request.includeIntermediateStops = intermediateStops.checked;
0555                         journeyModel.request.includePaths = includePaths.checked;
0556                         journeyModel.request.accessModes = individualTransportModes[accessMode.currentIndex];
0557                         journeyModel.request.egressModes = individualTransportModes[egressMode.currentIndex];
0558                         journeyModel.request.lineModes = lineModeSelector.currentMode;
0559                     }
0560 
0561                     QQC2.Button {
0562                         text: "Query"
0563                         onClicked: {
0564                             var from = journeyModel.request.from;
0565                             from.name = fromName.text;
0566                             from.latitude = fromLat.text;
0567                             from.longitude = fromLon.text;
0568                             journeyModel.request.from = from;
0569                             var to = journeyModel.request.to;
0570                             to.name = toName.text;
0571                             to.latitude = toLat.text;
0572                             to.longitude = toLon.text;
0573                             journeyModel.request.to = to;
0574                             parent.setupRequestCommon();
0575                         }
0576                     }
0577                     QQC2.Button {
0578                         text: "Query Name"
0579                         onClicked: {
0580                             var from = journeyModel.request.from;
0581                             from.name = fromName.text;
0582                             from.latitude = NaN;
0583                             from.longitude = NaN;
0584                             journeyModel.request.from = from;
0585                             var to = journeyModel.request.to;
0586                             to.name = toName.text;
0587                             to.latitude = NaN;
0588                             to.longitude = NaN;
0589                             journeyModel.request.to = to;
0590                             parent.setupRequestCommon();
0591                         }
0592                     }
0593                     QQC2.Button {
0594                         text: "Query Coord"
0595                         onClicked: {
0596                             var from = journeyModel.request.from;
0597                             from.name = "";
0598                             from.latitude = fromLat.text;
0599                             from.longitude = fromLon.text;
0600                             journeyModel.request.from = from;
0601                             var to = journeyModel.request.to;
0602                             to.name = "";
0603                             to.latitude = toLat.text;
0604                             to.longitude = toLon.text;
0605                             journeyModel.request.to = to;
0606                             parent.setupRequestCommon();
0607                         }
0608                     }
0609                     QQC2.Button {
0610                         text: "Clear"
0611                         onClicked: {
0612                             fromName.text = "";
0613                             fromLon.text = "";
0614                             fromLat.text = "";
0615                             toName.text = "";
0616                             toLon.text = "";
0617                             toLat.text = "";
0618                         }
0619                     }
0620                 }
0621 
0622                 RowLayout {
0623                     QQC2.ToolButton {
0624                         id: prevQueryButton
0625                         icon.name: "go-previous"
0626                         enabled: journeyModel.canQueryPrevious
0627                         onClicked: journeyModel.queryPrevious()
0628                     }
0629                     QQC2.ComboBox {
0630                         id: journeySelector
0631                         Layout.fillWidth: true
0632                         model: titleModel
0633                         textRole: "display"
0634                     }
0635                     QQC2.ToolButton {
0636                         id: nextQueryButton
0637                         icon.name: "go-next"
0638                         enabled: journeyModel.canQueryNext
0639                         onClicked: journeyModel.queryNext()
0640                     }
0641                 }
0642 
0643                 ListView {
0644                     Layout.fillHeight: true
0645                     Layout.fillWidth: true
0646                     model: journeyModel.data(journeyModel.index(journeySelector.currentIndex, 0), 256).sections
0647                     clip: true
0648                     delegate: journeyDelegate
0649 
0650                     QQC2.BusyIndicator {
0651                         anchors.centerIn: parent
0652                         running: journeyModel.loading
0653                     }
0654 
0655                     QQC2.Label {
0656                         anchors.centerIn: parent
0657                         width: parent.width
0658                         text: journeyModel.errorMessage
0659                         color: Kirigami.Theme.negativeTextColor
0660                         wrapMode: Text.Wrap
0661                     }
0662                 }
0663 
0664             }
0665         }
0666     }
0667 }