- started to implement the eventdisplay as QAbstractItemModel
- started to adjust the serverconn getter to support that
This commit is contained in:
parent
bcfc802932
commit
4e1edbe11a
11 changed files with 286 additions and 79 deletions
|
@ -22,12 +22,14 @@ SOURCES += \
|
||||||
sources/serverconn.cpp \
|
sources/serverconn.cpp \
|
||||||
sources/main.cpp \
|
sources/main.cpp \
|
||||||
sources/appsettings.cpp \
|
sources/appsettings.cpp \
|
||||||
sources/foodplanmodel.cpp
|
sources/foodplanmodel.cpp \
|
||||||
|
sources/eventmodel.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
headers/serverconn.h \
|
headers/serverconn.h \
|
||||||
headers/appsettings.h \
|
headers/appsettings.h \
|
||||||
headers/foodplanmodel.h
|
headers/foodplanmodel.h \
|
||||||
|
headers/eventmodel.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
qml/qml.qrc \
|
qml/qml.qrc \
|
||||||
|
|
46
headers/eventmodel.h
Normal file
46
headers/eventmodel.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef EVENTMODEL_H
|
||||||
|
#define EVENTMODEL_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include "headers/serverconn.h"
|
||||||
|
|
||||||
|
class EventModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
EventModel(QObject *parent = nullptr);
|
||||||
|
~EventModel();
|
||||||
|
|
||||||
|
enum DayRole {
|
||||||
|
GradeRole = Qt::DisplayRole,
|
||||||
|
HourRole,
|
||||||
|
ReplaceRole,
|
||||||
|
SubjectRole,
|
||||||
|
RoomRole,
|
||||||
|
ToRole,
|
||||||
|
TextRole
|
||||||
|
};
|
||||||
|
Q_ENUM(DayRole)
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex & = QModelIndex()) const;
|
||||||
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||||
|
QHash<int, QByteArray> roleNames() const;
|
||||||
|
|
||||||
|
Q_INVOKABLE QVariantMap get(int row) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Day {
|
||||||
|
QString grade;
|
||||||
|
QString hour;
|
||||||
|
QString replace;
|
||||||
|
QString subject;
|
||||||
|
QString room;
|
||||||
|
QString to;
|
||||||
|
QString text;
|
||||||
|
};
|
||||||
|
|
||||||
|
QList<Day> m_events;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // EVENTMODEL_H
|
|
@ -11,6 +11,7 @@ class FoodPlanModel : public QAbstractListModel
|
||||||
public:
|
public:
|
||||||
explicit FoodPlanModel(QObject *parent = nullptr);
|
explicit FoodPlanModel(QObject *parent = nullptr);
|
||||||
~FoodPlanModel();
|
~FoodPlanModel();
|
||||||
|
|
||||||
enum DishRole {
|
enum DishRole {
|
||||||
CookteamRole = Qt::DisplayRole,
|
CookteamRole = Qt::DisplayRole,
|
||||||
DateRole,
|
DateRole,
|
||||||
|
@ -38,8 +39,6 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
QList<Dish> m_foodPlan;
|
QList<Dish> m_foodPlan;
|
||||||
|
|
||||||
public slots:
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FOODPLANMODEL_H
|
#endif // FOODPLANMODEL_H
|
||||||
|
|
|
@ -48,16 +48,13 @@ public:
|
||||||
ReturnData_t senddata(QUrl serviceUrl, QUrlQuery postData);
|
ReturnData_t senddata(QUrl serviceUrl, QUrlQuery postData);
|
||||||
|
|
||||||
QList<QList<QString>> m_weekplan;
|
QList<QList<QString>> m_weekplan;
|
||||||
|
QList<QStringList> m_events;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
Q_INVOKABLE void updateProgress(qint64 read, qint64 total);
|
Q_INVOKABLE void updateProgress(qint64 read, qint64 total);
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
QList<QStringList> m_eventlist;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
extern ServerConn * pGlobalServConn;
|
extern ServerConn * pGlobalServConn;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import QtQuick 2.9
|
import QtQuick 2.9
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
|
import Backend 1.0
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: control
|
id: control
|
||||||
|
/*
|
||||||
ScrollView {
|
ScrollView {
|
||||||
id:scroll
|
id:scroll
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -79,7 +80,7 @@ Item {
|
||||||
if(date.getDate() === today.getDate()){
|
if(date.getDate() === today.getDate()){
|
||||||
return("Heute")
|
return("Heute")
|
||||||
}
|
}
|
||||||
else if(date.getTime() < (today.getTime() + (24 * 60 * 60 * 1000) )/*date.getDate() === today.getDate() + 1 || (date.getDate() === 1 && date.getMonth() === today.getMonth() + 1)*/){
|
else if(date.getTime() < (today.getTime() + (24 * 60 * 60 * 1000) )){
|
||||||
return("Morgen")
|
return("Morgen")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -92,6 +93,64 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
ListView {
|
||||||
|
id: eventList
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 10
|
||||||
|
|
||||||
|
model: EventModel {
|
||||||
|
id: foodPlanModel
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Button {
|
||||||
|
width: listView.width
|
||||||
|
id: delegate
|
||||||
|
height: visible ? cookteam.height + date.height + text.height + cust_spacing*9 + 5:0
|
||||||
|
visible: listView.isDayVisible(index)
|
||||||
|
|
||||||
|
property int cust_spacing: 5
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: cookteam
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: 10
|
||||||
|
font.bold: true
|
||||||
|
font.pixelSize: date.font.pixelSize * 1.5
|
||||||
|
text: _cppServerConn.getEventData(index).grade
|
||||||
|
width: parent.width - 10
|
||||||
|
wrapMode: Label.Wrap
|
||||||
|
height: text!==""? undefined:0
|
||||||
|
}
|
||||||
|
Label {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.top: cookteam.bottom
|
||||||
|
id: date
|
||||||
|
text: _cppServerConn.getEventData(index).hour + " | "
|
||||||
|
+ _cppServerConn.getEventData(index).replace + " | "
|
||||||
|
+ _cppServerConn.getEventData(index).subject + " | "
|
||||||
|
+ _cppServerConn.getEventData(index).room + " | "
|
||||||
|
|
||||||
|
width: parent.width - 10
|
||||||
|
wrapMode: Label.Wrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: 10
|
||||||
|
anchors.top: date.bottom
|
||||||
|
anchors.topMargin: cust_spacing
|
||||||
|
font.pixelSize: date.font.pixelSize * 2
|
||||||
|
font.bold: true
|
||||||
|
id: text
|
||||||
|
text: _cppServerConn.getEventData(index).to + " " + _cppServerConn.getEventData(index).text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,6 +150,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ Page {
|
||||||
running: true
|
running: true
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
status = _cppServerConn.getEvents(day);
|
status = _cppServerConn.getEvents(day)
|
||||||
pageLoader.source = "../Components/EventDisplay.qml"
|
pageLoader.source = "../Components/EventDisplay.qml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
61
sources/eventmodel.cpp
Normal file
61
sources/eventmodel.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#include "headers/eventmodel.h"
|
||||||
|
|
||||||
|
EventModel::EventModel(QObject *parent) : QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
// foodplan constructor
|
||||||
|
// is called when the Foodplan Display is loaded
|
||||||
|
|
||||||
|
// list
|
||||||
|
m_events.clear();
|
||||||
|
|
||||||
|
// convert the stringlist from the serverconn to a Dish-list
|
||||||
|
foreach(QList<QString>day, pGlobalServConn->m_events){
|
||||||
|
m_events.append({day[0], day[1], day[2], day[3], day[4], day[5], day[6]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int EventModel::rowCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return m_events.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant EventModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (index.row() < rowCount())
|
||||||
|
switch (role) {
|
||||||
|
case GradeRole: return m_events.at(index.row()).grade;
|
||||||
|
case HourRole: return m_events.at(index.row()).hour;
|
||||||
|
case ReplaceRole: return m_events.at(index.row()).replace;
|
||||||
|
case SubjectRole: return m_events.at(index.row()).subject;
|
||||||
|
case RoomRole: return m_events.at(index.row()).room;
|
||||||
|
case ToRole: return m_events.at(index.row()).to;
|
||||||
|
case TextRole: return m_events.at(index.row()).text;
|
||||||
|
default: return QVariant();
|
||||||
|
}
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> EventModel::roleNames() const
|
||||||
|
{
|
||||||
|
static const QHash<int, QByteArray> roles {
|
||||||
|
{ GradeRole, "grade" },
|
||||||
|
{ HourRole, "hour" },
|
||||||
|
{ ReplaceRole, "replace" },
|
||||||
|
{ SubjectRole, "subject" },
|
||||||
|
{ RoomRole, "room" },
|
||||||
|
{ ToRole, "to" },
|
||||||
|
{ TextRole, "text" }
|
||||||
|
};
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap EventModel::get(int row) const
|
||||||
|
{
|
||||||
|
const Day foodPlan = m_events.value(row);
|
||||||
|
return { {"grade", foodPlan.grade}, {"hour", foodPlan.hour}, {"replace", foodPlan.replace}, {"subject", foodPlan.subject}, {"room", foodPlan.room}, {"to", foodPlan.to}, {"text", foodPlan.text} };
|
||||||
|
}
|
||||||
|
|
||||||
|
EventModel::~EventModel()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -2,16 +2,16 @@
|
||||||
|
|
||||||
FoodPlanModel::FoodPlanModel(QObject *parent) : QAbstractListModel(parent)
|
FoodPlanModel::FoodPlanModel(QObject *parent) : QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
//m_foodPlan.append({ "Angel Hogan", "Chapel St. 368 ", "Clearwater" , "0311 1823993", "uhj", "iuij" });
|
// foodplan constructor
|
||||||
qDebug() << "foodplan Konstructor";
|
// is called when the Foodplan Display is loaded
|
||||||
|
|
||||||
|
// list
|
||||||
m_foodPlan.clear();
|
m_foodPlan.clear();
|
||||||
|
|
||||||
|
// convert the stringlist from the serverconn to a Dish-list
|
||||||
foreach(QList<QString>day, pGlobalServConn->m_weekplan){
|
foreach(QList<QString>day, pGlobalServConn->m_weekplan){
|
||||||
m_foodPlan.append({day[0], day[1], day[2], day[3], day[4], day[5]});
|
m_foodPlan.append({day[0], day[1], day[2], day[3], day[4], day[5]});
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "end1";
|
|
||||||
//qDebug() << pGlobalServConn->m_weekplan;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FoodPlanModel::rowCount(const QModelIndex &) const
|
int FoodPlanModel::rowCount(const QModelIndex &) const
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "headers/serverconn.h"
|
#include "headers/serverconn.h"
|
||||||
#include "headers/appsettings.h"
|
#include "headers/appsettings.h"
|
||||||
#include "headers/foodplanmodel.h"
|
#include "headers/foodplanmodel.h"
|
||||||
|
#include "headers/eventmodel.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
@ -30,6 +31,7 @@ int main(int argc, char *argv[])
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
qmlRegisterType<FoodPlanModel>("Backend", 1, 0, "FoodPlanModel");
|
qmlRegisterType<FoodPlanModel>("Backend", 1, 0, "FoodPlanModel");
|
||||||
|
qmlRegisterType<EventModel>("Backend", 1, 0, "EventModel");
|
||||||
|
|
||||||
QQuickStyle::setStyle("Material");
|
QQuickStyle::setStyle("Material");
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
|
|
@ -184,7 +184,7 @@ float ServerConn::getProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServerConn::getEvents(QString day){
|
int ServerConn::getEvents(QString day){
|
||||||
|
/*
|
||||||
this->progress = 0;
|
this->progress = 0;
|
||||||
ReturnData_t ret; //this is a custom type to store the returned data
|
ReturnData_t ret; //this is a custom type to store the returned data
|
||||||
// Call the webservice
|
// Call the webservice
|
||||||
|
@ -214,25 +214,47 @@ int ServerConn::getEvents(QString day){
|
||||||
}
|
}
|
||||||
|
|
||||||
QString eventString = reply->readAll();
|
QString eventString = reply->readAll();
|
||||||
|
*/
|
||||||
|
|
||||||
qDebug() << "reading xml file";
|
qDebug() << "reading xml file";
|
||||||
// QFile * xmlFile = new QFile(":/samplehtml/Download File.xml");
|
QFile * xmlFile = new QFile(":/samplehtml/Download File.xml");
|
||||||
// if (!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!xmlFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
// qDebug() << "Load XML File Problem Couldn't open xmlfile.xml to load settings for download";
|
qDebug() << "Load XML File Problem Couldn't open xmlfile.xml to load settings for download";
|
||||||
// return 900;
|
return 900;
|
||||||
// }
|
}
|
||||||
|
|
||||||
QXmlStreamReader * xmlReader = new QXmlStreamReader(eventString);
|
//QXmlStreamReader * xmlReader = new QXmlStreamReader(eventString);
|
||||||
|
QXmlStreamReader * xmlReader = new QXmlStreamReader(xmlFile);
|
||||||
//qDebug() << xmlFile->readAll();
|
//qDebug() << xmlFile->readAll();
|
||||||
QList<QStringList> tmpEvents;
|
QList<QStringList> tmpEvents;
|
||||||
QStringList dayList;
|
QStringList dayList;
|
||||||
int currTop = 0;
|
int currTop = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
<text top="530" left="3" width="20" height="17" font="1">8a</text>
|
||||||
|
<text top="530" left="55" width="36" height="17" font="1">1 - 2</text>
|
||||||
|
<text top="530" left="123" width="16" height="17" font="4"><i>Ei</i></text>
|
||||||
|
<text top="530" left="178" width="23" height="17" font="4"><i>Ch</i></text>
|
||||||
|
<text top="530" left="233" width="18" height="17" font="1">---</text>
|
||||||
|
<text top="530" left="275" width="50" height="17" font="1">Entfall</text>
|
||||||
|
<text top="530" left="391" width="83" height="17" font="1">KEINE KA</text>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define eventXmlPosGrade 3
|
||||||
|
#define eventXmlPosHour 55
|
||||||
|
#define eventXmlPosReplace 123
|
||||||
|
#define eventXmlPosSubject 178
|
||||||
|
#define eventXmlPosRoom 233
|
||||||
|
#define eventXmlPosTo 275
|
||||||
|
#define eventXmlPosText 391
|
||||||
|
|
||||||
//Parse the XML until we reach end of it
|
//Parse the XML until we reach end of it
|
||||||
while(!xmlReader->atEnd()) {
|
while(!xmlReader->atEnd()) {
|
||||||
if (xmlReader->readNextStartElement()) {
|
if (xmlReader->readNextStartElement()) {
|
||||||
|
// read next element
|
||||||
|
|
||||||
if (xmlReader->name().toString() == "text"){
|
if (xmlReader->name().toString() == "text"){
|
||||||
|
// text element found
|
||||||
QXmlStreamAttributes attributes = xmlReader->attributes();
|
QXmlStreamAttributes attributes = xmlReader->attributes();
|
||||||
QString attribute_value;
|
QString attribute_value;
|
||||||
int top;
|
int top;
|
||||||
|
@ -242,38 +264,56 @@ int ServerConn::getEvents(QString day){
|
||||||
}
|
}
|
||||||
|
|
||||||
if(attributes.hasAttribute("top")){
|
if(attributes.hasAttribute("top")){
|
||||||
|
// get the y-Position of the text
|
||||||
|
|
||||||
top = attributes.value("top").toInt();
|
top = attributes.value("top").toInt();
|
||||||
|
|
||||||
if(abs(top - currTop) > 3){
|
if(abs(top - currTop) > 3){
|
||||||
//next line started
|
// new line started
|
||||||
|
|
||||||
if(currTop > 175){
|
if(currTop > 175){
|
||||||
// ignore the header
|
// ignore the header
|
||||||
|
qDebug() << dayList;
|
||||||
tmpEvents.append(dayList);
|
tmpEvents.append(dayList);
|
||||||
}
|
}
|
||||||
|
|
||||||
dayList.clear();
|
dayList.clear();
|
||||||
|
while (dayList.length() < 7) {
|
||||||
|
dayList.append("");
|
||||||
|
}
|
||||||
currTop = top;
|
currTop = top;
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
// no new line
|
||||||
|
|
||||||
|
if(attributes.hasAttribute("left")){
|
||||||
|
int left = attributes.value("left").toInt();
|
||||||
QString text = xmlReader->readElementText(QXmlStreamReader::IncludeChildElements);
|
QString text = xmlReader->readElementText(QXmlStreamReader::IncludeChildElements);
|
||||||
dayList.append(text);
|
if(abs(left - eventXmlPosGrade) < 3){
|
||||||
|
// position tells the text is the grade
|
||||||
|
dayList[0] = text;
|
||||||
|
}
|
||||||
qDebug() << qPrintable(xmlReader->name().toString()) << text << attribute_value << dayList;
|
else if (abs(left - eventXmlPosHour) < 3) {
|
||||||
|
// position tells the text is the grade
|
||||||
|
dayList[1] = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpEvents.takeFirst();
|
//qDebug() << qPrintable(xmlReader->name().toString()) << text << attribute_value << dayList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << tmpEvents;
|
qDebug() << tmpEvents;
|
||||||
|
|
||||||
this->m_eventlist = tmpEvents;
|
this->m_events = tmpEvents;
|
||||||
|
|
||||||
if(xmlReader->hasError()) {
|
if(xmlReader->hasError()) {
|
||||||
qDebug() << "xmlFile.xml Parse Error" << xmlReader->errorString();
|
qDebug() << "xmlFile.xml Parse Error" << xmlReader->errorString();
|
||||||
//return(500);
|
//return(900);
|
||||||
}
|
}
|
||||||
|
|
||||||
//close reader and flush file
|
//close reader and flush file
|
||||||
|
@ -507,18 +547,18 @@ QVariantMap ServerConn::getEventData(int index)
|
||||||
//qDebug() << index;
|
//qDebug() << index;
|
||||||
for(int i=0;i<=6;i++){
|
for(int i=0;i<=6;i++){
|
||||||
|
|
||||||
if(m_eventlist.size() > index){
|
if(m_events.size() > index){
|
||||||
//qDebug() << i << m_weekplan[index].size();
|
//qDebug() << i << m_weekplan[index].size();
|
||||||
if(m_eventlist[index].size() > i){
|
if(m_events[index].size() > i){
|
||||||
ret.append(m_eventlist[index][i]);
|
ret.append(m_events[index][i]);
|
||||||
//qDebug() << i << m_weekplan[index][i];
|
//qDebug() << i << m_weekplan[index][i];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret.append(NULL);
|
ret.append(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ret.append(NULL);
|
ret.append(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(ret.length() < 7){
|
if(ret.length() < 7){
|
||||||
|
@ -533,7 +573,7 @@ QVariantMap ServerConn::getEventData(int index)
|
||||||
}
|
}
|
||||||
|
|
||||||
int ServerConn::getEventCount(){
|
int ServerConn::getEventCount(){
|
||||||
return (m_eventlist.length());
|
return (m_events.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnData_t ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata)
|
ReturnData_t ServerConn::senddata(QUrl serviceUrl, QUrlQuery pdata)
|
||||||
|
|
Reference in a new issue