diff --git a/fannyapp.pro b/fannyapp.pro index dfb9636..da34ee8 100644 --- a/fannyapp.pro +++ b/fannyapp.pro @@ -23,13 +23,15 @@ SOURCES += \ sources/main.cpp \ sources/appsettings.cpp \ sources/foodplanmodel.cpp \ - sources/eventmodel.cpp + sources/eventmodel.cpp \ + sources/filtermodel.cpp HEADERS += \ headers/serverconn.h \ headers/appsettings.h \ headers/foodplanmodel.h \ - headers/eventmodel.h + headers/eventmodel.h \ + headers/filtermodel.h RESOURCES += \ qml/qml.qrc \ diff --git a/headers/appsettings.h b/headers/appsettings.h index b5459ec..d597a30 100644 --- a/headers/appsettings.h +++ b/headers/appsettings.h @@ -1,8 +1,12 @@ #ifndef APPSETTINGS_H #define APPSETTINGS_H +#include #include +#include #include +#include +#include #include class AppSettings : public QObject @@ -15,7 +19,14 @@ public: Q_INVOKABLE QString loadSetting(const QString &key); Q_INVOKABLE void writeSetting(const QString &key, const QVariant &variant); + QList readFilters(); + void writeFilters(QList list); + + Q_INVOKABLE QStringList readFiltersQml(); + Q_INVOKABLE void writeFiltersQml(QStringList); + QSettings *settingsManager; + QFile * filtersFile; signals: diff --git a/headers/filtermodel.h b/headers/filtermodel.h new file mode 100644 index 0000000..3f21f32 --- /dev/null +++ b/headers/filtermodel.h @@ -0,0 +1,37 @@ +#ifndef FILTERMODEL_H +#define FILTERMODEL_H + +#include +#include +#include "serverconn.h" + +class FilterModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit FilterModel(QObject *parent = nullptr); + + enum FilterRole { + GradeRole = Qt::DisplayRole, + ClassLetterRole + }; + Q_ENUM(FilterRole) + + int rowCount(const QModelIndex & = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QHash roleNames() const; + + Q_INVOKABLE QVariantMap get(int row) const; + Q_INVOKABLE void append(const QString &grade, const QString &classLetter); + Q_INVOKABLE void remove(int row); + +private: + struct Filter { + QString grade; + QString classLetter; + }; + + QList m_filters; +}; + +#endif // FILTERMODEL_H diff --git a/qml/Components/SettingsDelegate.qml b/qml/Components/SettingsDelegate.qml new file mode 100644 index 0000000..f907b81 --- /dev/null +++ b/qml/Components/SettingsDelegate.qml @@ -0,0 +1,65 @@ +import QtQuick 2.0 +import QtQuick.Controls 2.4 + +ItemDelegate { + id: control + + property string title: "" + property string description: "" + property bool showForwardIcon: true + + height: 10 + shortDescription.height + 2 + longDescription.height + 10 + + Label { + id: shortDescription + + anchors { + top: parent.top + left: parent.left + margins: 10 + } + + font.pixelSize: longDescription.font.pixelSize * 1.4 + + text: control.title + + } + + Label { + id: longDescription + + anchors { + top: shortDescription.bottom + topMargin: 2 + left: parent.left + leftMargin: 10 + } + + width: parent.width - 10 - forwardIcon.width - 10 + + wrapMode: Label.Wrap + + text: control.description + } + + Image { + id: forwardIcon + + visible: control.showForwardIcon + + anchors { + verticalCenter: parent.verticalCenter + right: parent.right + rightMargin: 10 + } + + height: parent.height * 0.4 + + rotation: 180 + + fillMode: Image.PreserveAspectFit + + source: "/graphics/icons/backDark.png" + } + +} diff --git a/qml/Forms/FilterForm.qml b/qml/Forms/FilterForm.qml new file mode 100644 index 0000000..f95eb78 --- /dev/null +++ b/qml/Forms/FilterForm.qml @@ -0,0 +1,222 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.4 +import QtQuick.Layouts 1.3 +import Backend 1.0 + +import "../Components" + +Page { + id: root + + title: "Vertretungsplan Filter" + + signal opened() + + onOpened: { + console.log("Filter Form opened") + + var filters = _cppAppSettings.readFiltersQml() + } + + Dialog { + id: filterDialog + + signal finished(string grade, string classletter) + + onFinished: { + if(parseInt(grade) > 10 || classletter === "alle"){ + classletter = "" + } + + contactView.model.append(grade, classletter) + } + + function createContact() { + form.grade.value = 5 + + filterDialog.title = qsTr("Klasse hinzufügen"); + filterDialog.open(); + } + + x: ( parent.width - width ) / 2 + y: ( parent.height - height ) / 2 + + focus: true + modal: true + title: qsTr("Add Contact") + standardButtons: Dialog.Ok | Dialog.Cancel + + contentItem: GridLayout { + id: form + property alias grade: gradeSb + property alias classLetter: classLetterCb + property int minimumInputSize: 120 + + rows: 4 + columns: 2 + + Label { + text: qsTr("Stufe") + Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline + } + + SpinBox { + id: gradeSb + focus: true + Layout.fillWidth: true + Layout.minimumWidth: form.minimumInputSize + Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline + from: 5 + to: 12 + stepSize: 1 + value: 5 + } + + Label { + text: qsTr("Klasse") + Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline + } + + ComboBox { + id: classLetterCb + Layout.fillWidth: true + Layout.minimumWidth: form.minimumInputSize + Layout.alignment: Qt.AlignLeft | Qt.AlignBaseline + model: ["a", "b", "c", "d", "e", "alle"] + enabled: gradeSb.value < 11 + } + } + + onAccepted: finished(form.grade.value.toString(), form.classLetter.currentText) + } + + ListView { + id: contactView + + anchors.fill: parent + + width: 320 + height: 480 + + focus: true + + delegate: ItemDelegate { + id: delegate + + width: contactView.width + + text: grade + classLetter + + Rectangle { + + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + + height: 1 + width: parent.width + + color: "lightgrey" + } + + Button { + id: deleteButton + + anchors { + right: parent.right + rightMargin: 10 + verticalCenter: parent.verticalCenter + } + + height: parent.height * 0.6 + width: height + + scale: pressed ? 0.8:1 + + onClicked: { + contactView.model.remove(index) + } + + background: Image { + source: "/graphics/icons/delete.png" + fillMode: Image.PreserveAspectFit + mipmap: true + } + + Behavior on scale { + PropertyAnimation { + duration: 100 + } + } + } + } + + model: FilterModel { + + } + + ScrollBar.vertical: ScrollBar { } + } + + FancyButton { + + highlighted: true + + anchors { + margins: 10 + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + + imageScale: 0 + + height: 50 + width: height + + onClicked: { + filterDialog.createContact() + } + + Label { + anchors.centerIn: parent + font.pixelSize: parent.height * 0.6 + text: "+" + } + } + + /* + ListView { + function getModel() { + // convert the filter list into an object + var keys = Object.keys(_cppAppSettings.readFiltersQml()); + // get he lenght + var len = keys.length + // return the lenght + return(len) + } + + function getDetails(index) { + var ret = _cppAppSettings.readFiltersQml()[index] + var details = ret.split("|") + return(details) + } + + function refresh() { + filterList.model = filterList.getModel() + } + + id: filterList + anchors.fill: parent + property string title: qsTr("connections") + property int delegateHeight: height*0.18 + model: getModel() + delegate: ItemDelegate { + width: parent.width + height: filterList.delegateHeight + text: filterList.getDetails(index)[0] + filterList.getDetails(index)[1] + } + } + */ +} diff --git a/qml/Forms/SettingsForm.qml b/qml/Forms/SettingsForm.qml index 34974c7..4618a69 100644 --- a/qml/Forms/SettingsForm.qml +++ b/qml/Forms/SettingsForm.qml @@ -1,6 +1,8 @@ import QtQuick 2.0 import QtQuick.Controls 2.4 +import "../Components" + Page { id: root @@ -9,7 +11,9 @@ Page { signal opened() onOpened: { - console.log("Home Form opened") + console.log("Settings Form opened") + + var filters = _cppAppSettings.readFiltersQml() } Column { @@ -17,59 +21,15 @@ Page { anchors.fill: parent - ItemDelegate { + SettingsDelegate { width: parent.width - height: 10 + shortDescription.height + 2 + longDescription.height + 10 - - Label { - id: shortDescription - - anchors { - top: parent.top - left: parent.left - margins: 10 - } - - font.pixelSize: longDescription.font.pixelSize * 1.4 - - text: "Klassenstufe" + onClicked: { + formStack.push(filterForm) } - Label { - id: longDescription - - anchors { - top: shortDescription.bottom - topMargin: 2 - left: parent.left - leftMargin: 10 - } - - width: parent.width - 10 - gradeSelect.width - - wrapMode: Label.Wrap - - text: "Wähle deine Klassenstufe aus, um den Vertretungsplan zu sortieren" - } - - SpinBox { - id: gradeSelect - - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - } - - from: 5 - to: 12 - stepSize: 1 - value: _cppAppSettings.loadSetting("grade") - - onValueChanged: { - _cppAppSettings.writeSetting("grade", value) - } - } + title: "Klassen" + description: "Wähle die Klassen(stufen) aus, für die du den Vertretungsplan ansehen möchtest" } } } diff --git a/qml/Pages/MainPage.qml b/qml/Pages/MainPage.qml index 7c521c7..13d8fd3 100644 --- a/qml/Pages/MainPage.qml +++ b/qml/Pages/MainPage.qml @@ -61,6 +61,11 @@ Page { SettingsForm {} } + Component { + id: filterForm + FilterForm {} + } + popEnter: Transition { XAnimator { from: (formStack.mirrored ? -1 : 1) * -formStack.width @@ -145,7 +150,7 @@ Page { } Label { - text: formStack.currentItem.title + text: getText() anchors { verticalCenter: parent.verticalCenter left: toolButton.right @@ -153,6 +158,18 @@ Page { } font.bold: true color: "black" + + function getText(){ + var titleString = ""; + for(var i=1; i 1){ + titleString += " > " + } + + titleString += formStack.get(i).title + } + return(titleString) + } } Behavior on anchors.topMargin { diff --git a/qml/qml.qrc b/qml/qml.qrc index 3f68976..17ba6fb 100644 --- a/qml/qml.qrc +++ b/qml/qml.qrc @@ -14,5 +14,7 @@ Components/EventView.qml Components/InfoArea.qml Forms/SettingsForm.qml + Forms/FilterForm.qml + Components/SettingsDelegate.qml diff --git a/shared/graphics/icons/delete.png b/shared/graphics/icons/delete.png new file mode 100644 index 0000000..62e09ea Binary files /dev/null and b/shared/graphics/icons/delete.png differ diff --git a/shared/samplehtml/.Download File.txt.swp b/shared/samplehtml/.Download File.txt.swp deleted file mode 100644 index 95f3e15..0000000 Binary files a/shared/samplehtml/.Download File.txt.swp and /dev/null differ diff --git a/shared/samplehtml/convert.sh b/shared/samplehtml/convert.sh new file mode 100755 index 0000000..16fb8f0 --- /dev/null +++ b/shared/samplehtml/convert.sh @@ -0,0 +1 @@ +pdftotext Download\ File.pdf -layout -y 130 -H 1000 -W 1000 -nopgbrk diff --git a/shared/shared.qrc b/shared/shared.qrc index bbe63ed..e2c08c6 100644 --- a/shared/shared.qrc +++ b/shared/shared.qrc @@ -24,5 +24,6 @@ graphics/icons/settingsBlack.png graphics/icons/backDark.png samplehtml/Download File.txt + graphics/icons/delete.png diff --git a/sources/appsettings.cpp b/sources/appsettings.cpp index 5a5c7a8..ac72863 100644 --- a/sources/appsettings.cpp +++ b/sources/appsettings.cpp @@ -5,17 +5,26 @@ AppSettings * pGlobalAppSettings = nullptr; AppSettings::AppSettings(QObject* parent) :QObject(parent) { + qDebug("+----- AppSettings konstruktor -----"); + QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); - qDebug(path.toLatin1()); + qDebug() << "+----- Settings Path:" << path; + this->settingsManager = new QSettings(path+"/fannyapp/settings.ini", QSettings::IniFormat); - qDebug("AppSettings konstruktor"); if(loadSetting("init") == "false"){ this->writeSetting("init", 0); } if(loadSetting("grade") == "false"){ this->writeSetting("grade", 5); } + + this->filtersFile = new QFile(path + "/fannyapp/filters.json"); + + //QList filters = {{"5", "d"}, {"6", "c"}, {"11", ""}}; + + //writeFilters(filters); + qDebug() << readFilters(); } QString AppSettings::loadSetting(const QString &key) @@ -33,6 +42,76 @@ void AppSettings::writeSetting(const QString &key, const QVariant &variant) this->settingsManager->endGroup(); } +QList AppSettings::readFilters() { + + // list to be returned + QList filtersList; + + this->filtersFile->open(QFile::ReadOnly | QFile::Text); + + QString jsonString = this->filtersFile->readAll(); + + this->filtersFile->close(); + + qDebug() << jsonString; + QJsonDocument jsonFilters = QJsonDocument::fromJson(jsonString.toUtf8()); + // array with all filters in it + QJsonArray filtersArray = jsonFilters.array(); + foreach(const QJsonValue & value, filtersArray){ + // array of a single filter + QJsonArray filterArray = value.toArray(); + + // extract values from array + QString gradeString = filterArray[0].toString(); + QString letterString = filterArray[1].toString(); + + filtersList.append({gradeString, letterString}); + } + + return(filtersList); +} + +void AppSettings::writeFilters(QList list) { + + // string to write to file + QString jsonString; + QJsonArray filtersArray; + + foreach(QStringList filter, list){ + QJsonArray filterArray; + filterArray.append(filter[0]); + filterArray.append(filter[1]); + + filtersArray.append(filterArray); + } + + QJsonDocument filtersDoc(filtersArray); + qDebug() << filtersDoc.toJson(); + + this->filtersFile->open(QIODevice::ReadWrite); + + this->filtersFile->resize(0); + + this->filtersFile->write(filtersDoc.toJson()); + + this->filtersFile->close(); +} + +QStringList AppSettings::readFiltersQml() { + + QStringList filtersList; + + foreach(QStringList filterList, this->readFilters()){ + filtersList.append(filterList[0]+"|"+filterList[1]); + } + + return(filtersList); +} + +void AppSettings::writeFiltersQml(QStringList) { + +} + AppSettings::~AppSettings() { delete settingsManager; diff --git a/sources/filtermodel.cpp b/sources/filtermodel.cpp new file mode 100644 index 0000000..701e604 --- /dev/null +++ b/sources/filtermodel.cpp @@ -0,0 +1,95 @@ +#include "headers/filtermodel.h" + +FilterModel::FilterModel(QObject *parent ) : QAbstractListModel(parent) +{ + m_filters.clear(); + + QList filtersList = pGlobalAppSettings->readFilters(); + + foreach(QStringList filterList, filtersList){ + m_filters.append({filterList[0], filterList[1]}); + } +} + +int FilterModel::rowCount(const QModelIndex &) const +{ + return m_filters.count(); +} + +QVariant FilterModel::data(const QModelIndex &index, int role) const +{ + if (index.row() < rowCount()) + switch (role) { + case GradeRole: return m_filters.at(index.row()).grade; + case ClassLetterRole: return m_filters.at(index.row()).classLetter; + default: return QVariant(); + } + return QVariant(); +} + +QHash FilterModel::roleNames() const +{ + static const QHash roles { + { GradeRole, "grade" }, + { ClassLetterRole, "classLetter" }, + }; + return roles; +} + +QVariantMap FilterModel::get(int row) const +{ + const Filter filter = m_filters.value(row); + return { {"grade", filter.grade}, {"classLetter", filter.classLetter} }; +} + +void FilterModel::append(const QString &grade, const QString &classLetter) +{ + + foreach(Filter filter, this->m_filters){ + if(filter.grade == grade && filter.classLetter == classLetter){ + // dublicates aren't allowed + return; + } + } + + int row = 0; + while (row < m_filters.count() && grade.toInt() > m_filters.at(row).grade.toInt()){ + row++; + } + + while (row < m_filters.count() && classLetter > m_filters.at(row).classLetter && grade.toInt() == m_filters.at(row).grade.toInt()) { + row++; + } + beginInsertRows(QModelIndex(), row, row); + m_filters.insert(row, {grade, classLetter}); + endInsertRows(); + + QList filtersList; + filtersList.clear(); + + foreach(Filter filter, this->m_filters){ + filtersList.append({filter.grade, filter.classLetter}); + } + + pGlobalAppSettings->writeFilters(filtersList); + +} + +void FilterModel::remove(int row) +{ + if (row < 0 || row >= m_filters.count()) + return; + + beginRemoveRows(QModelIndex(), row, row); + m_filters.removeAt(row); + endRemoveRows(); + + QList filtersList; + filtersList.clear(); + + foreach(Filter filter, this->m_filters){ + filtersList.append({filter.grade, filter.classLetter}); + } + + pGlobalAppSettings->writeFilters(filtersList); +} diff --git a/sources/main.cpp b/sources/main.cpp index 85f7da5..77b22ca 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -17,6 +17,7 @@ #include "headers/appsettings.h" #include "headers/foodplanmodel.h" #include "headers/eventmodel.h" +#include "headers/filtermodel.h" int main(int argc, char *argv[]) { @@ -32,6 +33,7 @@ int main(int argc, char *argv[]) qmlRegisterType("Backend", 1, 0, "FoodPlanModel"); qmlRegisterType("Backend", 1, 0, "EventModel"); + qmlRegisterType("Backend", 1, 0, "FilterModel"); QQuickStyle::setStyle("Material"); QQmlApplicationEngine engine; @@ -40,7 +42,6 @@ int main(int argc, char *argv[]) context->setContextProperty("_cppServerConn", pServerConn); context->setContextProperty("_cppAppSettings", pAppSettings); - if (engine.rootObjects().isEmpty()) return -1; diff --git a/sources/serverconn.cpp b/sources/serverconn.cpp index e5f0882..7a399a3 100644 --- a/sources/serverconn.cpp +++ b/sources/serverconn.cpp @@ -41,9 +41,7 @@ int ServerConn::login(QString username, QString password, bool permanent) // and finally write it into the request header request.setRawHeader( "Authorization", headerData.toLocal8Bit() ); - //QUrlQuery pdata; - // Send request and connect all possible signals - //QNetworkReply*reply = this->networkManager->post(request, pdata.toString(QUrl::FullyEncoded).toUtf8()); + // Send GET request to fanny server QNetworkReply*reply = networkManager->get( request ); // loop to wait until the request has finished before processing the data @@ -316,9 +314,15 @@ int ServerConn::getEvents(QString day){ } // if the event matches the filter - if(dayList[0].contains(pGlobalAppSettings->loadSetting("grade"))){ - // append the dayList to the temporary event list - tmpEvents.append(dayList); + foreach(QStringList filter, pGlobalAppSettings->readFilters()){ + // go through all filters and check if one of them matches the event + + if(dayList[0].contains(filter[0]) && dayList[0].contains(filter[1])){ + // append the dayList to the temporary event list + tmpEvents.append(dayList); + // terminate the loop + break; + } } }