Commit 1470102c authored by Volker Krause's avatar Volker Krause

Initial commit of the itinerary management application

parents
*.kdev4
*~
*.rej
*.orig
*.out
CMakeLists.txt.user
callgrind.*
heaptrack.*
*.qmlc
// kate: space-indent on; indent-width 4; remove-trailing-spaces modified;
cmake_minimum_required(VERSION 3.0)
project(itinerary VERSION 0.0.1)
find_package(ECM 5.38 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_SOURCE_DIR}/cmake)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMAddTests)
include(ECMGenerateHeaders)
include(ECMQtDeclareLoggingCategory)
include(ECMSetupVersion)
include(FeatureSummary)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(GenerateExportHeader)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
ecm_setup_version(PROJECT VARIABLE_PREFIX ITINERARY VERSION_HEADER itinerary_version.h)
find_package(Qt5 REQUIRED COMPONENTS Test Quick)
find_package(KF5 REQUIRED COMPONENTS Archive)
find_package(QmlLint)
set_package_properties(QmlLint PROPERTIES URL "https://qt.io" PURPOSE "Validate QML code.")
find_package(SharedMimeInfo 1.0 REQUIRED)
add_subdirectory(src)
add_subdirectory(autotests)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
#!/bin/bash
find "$@" -name '*.h' -o -name '*.cpp' -o -name '*.qml' | grep -v /3rdparty/ | while read FILE; do
if grep -qiE "Copyright \(C\) [0-9, -]{4,} " "$FILE" ; then continue; fi
thisfile=`basename $FILE`
authorName=`git config user.name`
authorEmail=`git config user.email`
thisYear=`date +%Y`
cat <<EOF > "$FILE".tmp
/*
Copyright (C) $thisYear $authorName <$authorEmail>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
EOF
cat "$FILE" >> "$FILE".tmp
mv "$FILE".tmp "$FILE"
done
add_definitions(-DSOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
ecm_add_test(pkpasstest.cpp LINK_LIBRARIES Qt5::Test KPkPass)
ecm_add_test(pkpassmanagertest.cpp LINK_LIBRARIES Qt5::Test itinerary)
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <pkpassmanager.h>
#include <QUrl>
#include <QtTest/qtest.h>
#include <QSignalSpy>
#include <QStandardPaths>
class PkPassManagerTest : public QObject
{
Q_OBJECT
private:
void clearPasses(PkPassManager *mgr)
{
for (const auto id : mgr->passes())
mgr->removePass(id);
}
private slots:
void initTestCase()
{
QStandardPaths::setTestModeEnabled(true);
}
void testImport()
{
PkPassManager mgr;
clearPasses(&mgr);
QSignalSpy addSpy(&mgr, &PkPassManager::passAdded);
QVERIFY(addSpy.isValid());
QVERIFY(mgr.passes().isEmpty());
mgr.importPass(QUrl::fromLocalFile(QLatin1String(SOURCE_DIR "/data/boardingpass.pkpass")));
QCOMPARE(addSpy.size(), 1);
QCOMPARE(addSpy.at(0).at(0).toString(), QLatin1String("pass.booking.kde.org/1234"));
auto passes = mgr.passes();
QCOMPARE(passes.size(), 1);
QCOMPARE(passes.at(0), QLatin1String("pass.booking.kde.org/1234"));
}
};
QTEST_GUILESS_MAIN(PkPassManagerTest)
#include "pkpassmanagertest.moc"
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <file.h>
#include <boardingpass.h>
#include <QLocale>
#include <QtTest/qtest.h>
class PkPassTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase()
{
QLocale::setDefault(QLocale("de_DE"));
}
void testBoardingPass()
{
std::unique_ptr<KPkPass::File> file(KPkPass::File::fromFile(QLatin1String(SOURCE_DIR "/data/boardingpass.pkpass")));
QVERIFY(file);
QCOMPARE(file->logoText(), QLatin1String("Boarding Pass"));
auto headers = file->headerFields();
QCOMPARE(headers.size(), 2);
auto field = headers.at(0);
QCOMPARE(field.label(), QLatin1String("Sitzplatz"));
QCOMPARE(field.value(), QLatin1String("10E"));
auto boardingPass = dynamic_cast<KPkPass::BoardingPass*>(file.get());
QVERIFY(boardingPass);
QCOMPARE(boardingPass->transitType(), KPkPass::BoardingPass::Air);
auto barcodes = file->barcodes();
QCOMPARE(barcodes.size(), 1);
auto bc = barcodes.at(0);
QCOMPARE(bc.format(), KPkPass::Barcode::QR);
QVERIFY(!bc.message().isEmpty());
}
};
QTEST_GUILESS_MAIN(PkPassTest)
#include "pkpasstest.moc"
# Copyright (C) 2016-2018 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
# All rights reserved.
#
# Author: Volker Krause <volker.krause@kdab.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
find_program(QMLLINT_EXECUTABLE qmllint)
if (QMLLINT_EXECUTABLE AND NOT Qt5Core_VERSION VERSION_LESS 5.4)
if(NOT QMLLINT_IS_WORKING)
# Try to fix common problems on Debian-based distros -- they provide /usr/bin/qmllint, which is a symlink to
# /usr/lib/x86_64-linux-gnu/qt4/bin/qmllint (or the Qt5 version of it). The actual executable is part of different
# package, so might not even be installed => double-check whether qmllint is working by executing it
execute_process(COMMAND ${QMLLINT_EXECUTABLE} --version RESULT_VARIABLE _qmllint_result OUTPUT_QUIET ERROR_QUIET)
if (_qmllint_result EQUAL 0)
set(QMLLINT_IS_WORKING TRUE CACHE BOOL "Whether the found qmllint executable is actually usable" FORCE)
endif()
endif()
if(QMLLINT_IS_WORKING)
set(QmlLint_FOUND TRUE)
endif()
endif()
# validate a list of qml files
function(qml_lint)
if (NOT QMLLINT_EXECUTABLE OR NOT QmlLint_FOUND)
return()
endif()
foreach(_file ${ARGN})
get_filename_component(_file_abs ${_file} ABSOLUTE)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_file}.qmllint
COMMAND ${QMLLINT_EXECUTABLE} ${_file_abs}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${_file}.qmllint
MAIN_DEPENDENCY ${_file_abs}
)
add_custom_target(${_file}_qmllint ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${_file}.qmllint)
endforeach()
endfunction()
add_subdirectory(pkpass)
add_subdirectory(app)
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.5
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.1 as QQC2
import org.kde.pkpass 1.0 as KPkPass
import org.kde.prison 1.0 as Prison
Item {
property var pass: null
property string passId
implicitHeight: frontLayout.implicitHeight
implicitWidth: Math.max(frontLayout.implicitWidth, 332)
ColumnLayout {
id: frontLayout
spacing: 0
anchors.fill: parent
Rectangle {
id: headerBackground
radius: 10
color: pass.backgroundColor
Layout.fillWidth: true
implicitWidth: headerLayout.implicitWidth + 2 * headerLayout.anchors.margins
implicitHeight: headerLayout.implicitHeight + 2 * headerLayout.anchors.margins
GridLayout {
id: headerLayout
rows: 2
columns: pass.headerFields.length + 2
anchors.fill: parent
anchors.margins: 10
Image {
Layout.rowSpan: 2
source: "image://org.kde.pkpass/" + passId + "/logo"
sourceSize.height: 1 // ??? seems necessary to trigger high dpi scaling...
}
QQC2.Label {
Layout.rowSpan: 2
Layout.fillWidth: true
text: pass.logoText
color: pass.foregroundColor
}
Repeater {
model: pass.headerFields
delegate: QQC2.Label {
text: modelData.label
color: pass.labelColor
}
}
Repeater {
model: pass.headerFields
delegate: QQC2.Label {
text: modelData.value
color: pass.foregroundColor
}
}
}
}
Rectangle {
id: bodyBackground
radius: 10
color: pass.backgroundColor
Layout.fillWidth: true
implicitHeight: bodyLayout.implicitHeight + 2 * bodyLayout.anchors.margins
implicitWidth: bodyLayout.implicitWidth + 2 * bodyLayout.anchors.margins
ColumnLayout {
id: bodyLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
// primary fields
GridLayout {
id: primaryFieldsLayout
rows: 2
columns: 3
flow: GridLayout.TopToBottom
Layout.fillWidth: true
QQC2.Label {
id: primaryLabel
Layout.fillWidth: true
Layout.preferredWidth: 1
text: pass.primaryFields[0].label
color: pass.labelColor
}
QQC2.Label {
id: primaryValue
text: pass.primaryFields[0].value
color: pass.foregroundColor
font.pointSize: 1.5 * primaryLabel.font.pointSize
}
QQC2.Label {
Layout.rowSpan: 2
Layout.alignment: Qt.AlignBottom
text: "" // TODO: check transit type
color: pass.labelColor
font.pointSize: primaryValue.font.pointSize
}
QQC2.Label {
Layout.fillWidth: true
Layout.preferredWidth: 1
horizontalAlignment: Qt.AlignRight
text: pass.primaryFields[1].label
color: pass.labelColor
}
QQC2.Label {
Layout.alignment: Qt.AlignRight
text: pass.primaryFields[1].value
color: pass.foregroundColor
font.pointSize: primaryValue.font.pointSize
}
}
// auxiliary fields
GridLayout {
id: auxFieldsLayout
rows: 2
columns: pass.auxiliaryFields.length
Layout.fillWidth: true
Repeater {
model: pass.auxiliaryFields
delegate: QQC2.Label {
color: pass.labelColor
text: modelData.label
}
}
Repeater {
model: pass.auxiliaryFields
delegate: QQC2.Label {
Layout.fillWidth: true
color: pass.foregroundColor
text: modelData.value
}
}
}
// secondary fields
GridLayout {
id: secFieldsLayout
rows: 2
columns: pass.secondaryFields.length
Layout.fillWidth: true
Repeater {
model: pass.secondaryFields
delegate: QQC2.Label {
Layout.fillWidth: true
color: pass.labelColor
text: modelData.label
}
}
Repeater {
model: pass.secondaryFields
delegate: QQC2.Label {
color: pass.foregroundColor
text: modelData.value
}
}
}
// barcode
Rectangle {
implicitHeight: barcodeLayout.implicitHeight
implicitWidth: barcodeLayout.implicitWidth
color: "white"
Layout.alignment: Qt.AlignCenter
ColumnLayout {
id: barcodeLayout
anchors.fill: parent
Prison.Barcode {
Layout.alignment: Qt.AlignCenter
Layout.margins: 4
barcodeType: pass.barcodes[0].format == KPkPass.Barcode.QR ? Prison.Barcode.QRCode : Prison.Barcode.Aztec
content: pass.barcodes[0].message
}
QQC2.Label {
Layout.alignment: Qt.AlignCenter
text: pass.barcodes[0].alternativeText
color: "black"
visible: text.length > 0
}
}
}
// TODO back fields
}
}
}
}
add_library(itinerary STATIC
pkpassmanager.cpp
timelinemodel.cpp
)
target_link_libraries(itinerary PUBLIC
KPkPass
)
add_executable(itinerary-app
main.cpp
pkpassimageprovider.cpp
qml.qrc
)
target_include_directories(itinerary-app PRIVATE ${CMAKE_BINARY_DIR})
target_link_libraries(itinerary-app PRIVATE
itinerary
Qt5::Quick
)
set_target_properties(itinerary-app PROPERTIES OUTPUT_NAME "itinerary")
qml_lint(
main.qml
BoardingPass.qml
TimelinePage.qml
)
install(TARGETS itinerary-app ${INSTALL_TARGETS_DEFAULT_ARGS})
install(PROGRAMS org.kde.itinerary.desktop DESTINATION ${KDE_INSTALL_APPDIR})
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.5
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.1 as QQC2
import org.kde.kirigami 2.0 as Kirigami
import "." as App
Kirigami.ScrollablePage {
title: qsTr("Boarding Passes")
// context drawer content
actions {
contextualActions: [
Kirigami.Action {
text: qsTr("Delete Pass")
iconName: "edit-delete"
onTriggered: print("TODO")
}
]
}
// page content
ListView {
model: _timelineModel
spacing: 5
delegate: Item {
height: passElement.implicitHeight
width: passElement.implicitWidth
App.BoardingPass {
x: (parent.ListView.view.width - implicitWidth) / 2
id: passElement
pass: model.pass
passId: model.passId
}
}
}
}
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "itinerary_version.h"
#include "pkpassmanager.h"
#include "timelinemodel.h"
#include "pkpassimageprovider.h"
#include <KPkPass/Field>
#include <KPkPass/Barcode>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QCommandLineParser>
#include <QGuiApplication>
#include <QIcon>
int main(int argc, char **argv)
{
QCoreApplication::setApplicationName(QStringLiteral("kde-itinerary"));
QCoreApplication::setOrganizationName(QStringLiteral("KDE"));
QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
QCoreApplication::setApplicationVersion(QStringLiteral(ITINERARY_VERSION_STRING));
QGuiApplication::setApplicationDisplayName(QStringLiteral("KDE Itinerary")); // TODO i18n
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QGuiApplication app(argc, argv);
QGuiApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("map-globe")));
QCommandLineParser parser;
parser.addHelpOption();
parser.addVersionOption();
parser.addPositionalArgument("pass", QStringLiteral("PkPass file to import."));
parser.process(app);
PkPassManager passMgr;
TimelineModel timelineModel;
timelineModel.setPkPassManager(&passMgr);
qmlRegisterUncreatableType<KPkPass::Barcode>("org.kde.pkpass", 1, 0, "Barcode", {});
qmlRegisterUncreatableType<KPkPass::Field>("org.kde.pkpass", 1, 0, "Field", {});
QQmlApplicationEngine engine;
engine.addImageProvider(QStringLiteral("org.kde.pkpass"), new PkPassImageProvider(&passMgr));
engine.rootContext()->setContextProperty(QStringLiteral("_pkpassManager"), &passMgr);
engine.rootContext()->setContextProperty(QStringLiteral("_timelineModel"), &timelineModel);
engine.load(QStringLiteral(":/main.qml"));
for (const auto &file : parser.positionalArguments())
passMgr.importPass(QUrl::fromLocalFile(file));
return app.exec();
}
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.5
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.0
import QtQuick.Controls 2.1 as QQC2
import org.kde.kirigami 2.0 as Kirigami
import "." as App
Kirigami.ApplicationWindow {
title: qsTr("KDE Itinerary")
header: Kirigami.ApplicationHeader {}
width: 480
height: 720
FileDialog {
id: fileDialog
title: qsTr("Please choose a file")
folder: shortcuts.home
onAccepted: {
console.log(fileDialog.fileUrls);
_pkpassManager.importPass(fileDialog.fileUrl);
}
}
globalDrawer: Kirigami.GlobalDrawer {
title: qsTr("KDE Itinerary")
titleIcon: "map-symbolic"
actions: [
Kirigami.Action {
text: qsTr("Import Pass...")
iconName: "document-open"
onTriggered: {
fileDialog.visible = true;
}
}
]
}
contextDrawer: Kirigami.ContextDrawer {
id: contextDrawer
}
pageStack.initialPage: mainPageComponent
Component {
id: mainPageComponent
App.TimelinePage {}
}
}
[Desktop Entry]
Name=KDE Itinerary
GenericName=Itinerary management
Exec=itinerary %f
Icon=map-globe
Type=Application
Comment=Travel assistance application.
Terminal=false
MimeType=application/vnd.apple.pkpass;
Categories=Qt;KDE;
/*
Copyright (C) 2018 Volker Krause <vkrause@kde.org>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pkpassimageprovider.h"
#include "pkpassmanager.h"
#include <KPkPass/File>