File indexing completed on 2024-10-06 03:31:31

0001 // SPDX-FileCopyrightText: 2006-2010 Peter Hedlund <peter.hedlund@kdemail.net>
0002 // SPDX-FileCopyrightText: 2023 Carl Schwan <carl@carlschwan.eu>
0003 // SPDX-License-Identifier: GPL-2.0-or-later
0004 
0005 #include "kwqcardmodel.h"
0006 #include "keduvocexpression.h"
0007 #include "kwordquiz_version.h"
0008 #include <keduvocdocument.h>
0009 #include <keduvoclesson.h>
0010 
0011 #include <KLocalizedString>
0012 #include <QDebug>
0013 #include <QDir>
0014 #include <QFileDialog>
0015 #include <QPointer>
0016 #include <QRegularExpression>
0017 #include <QStandardPaths>
0018 
0019 using namespace Qt::Literals::StringLiterals;
0020 
0021 KWQCardModel::KWQCardModel(QObject *parent)
0022     : QAbstractListModel(parent)
0023 {
0024 }
0025 
0026 void KWQCardModel::createNew()
0027 {
0028     auto doc = new KEduVocDocument(this);
0029     doc->setGenerator(QStringLiteral("kwordquiz %1").arg(QStringLiteral(KWORDQUIZ_VERSION_STRING)));
0030     doc->appendIdentifier();
0031     doc->identifier(0).setName(i18n("Question"));
0032     doc->appendIdentifier();
0033     doc->identifier(1).setName(i18n("Answer"));
0034 
0035     setDocument(doc);
0036 }
0037 
0038 QString KWQCardModel::identifierLeft() const
0039 {
0040     return m_document ? m_document->identifier(0).name() : QString();
0041 }
0042 
0043 void KWQCardModel::setIdentifierLeft(const QString &identifierLeft)
0044 {
0045     if (m_document->identifier(0).name() == identifierLeft) {
0046         return;
0047     }
0048     m_document->identifier(0).setName(identifierLeft);
0049     identifierLeftChanged();
0050 }
0051 
0052 QString KWQCardModel::identifierRight() const
0053 {
0054     return m_document ? m_document->identifier(1).name() : QString();
0055 }
0056 
0057 void KWQCardModel::setIdentifierRight(const QString &identifierRight)
0058 {
0059     if (m_document->identifier(1).name() == identifierRight) {
0060         return;
0061     }
0062     m_document->identifier(1).setName(identifierRight);
0063     identifierRightChanged();
0064 }
0065 
0066 void KWQCardModel::add(const QString &question, const QString &answer)
0067 {
0068     Q_ASSERT(m_document);
0069 
0070     beginInsertRows({}, rowCount(), rowCount());
0071     m_document->lesson()->appendEntry(new KEduVocExpression({ question, answer }));
0072     endInsertRows();
0073 
0074     Q_EMIT multipleChoiceAvailableChanged();
0075 
0076     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(rowCount() - 1);
0077     expression->setTranslation(0, question);
0078     expression->setTranslation(1, answer);
0079 
0080     Q_EMIT dataChanged(index(rowCount() - 1, 0), index(rowCount() - 1, 0));
0081 }
0082 
0083 void KWQCardModel::edit(const int row, const QString &question, const QString &answer)
0084 {
0085     Q_ASSERT(m_document);
0086 
0087     if (question.length() == 0 && answer.length() == 0 && row != 0 && row != rowCount() - 1) {
0088         beginRemoveRows({}, row, row);
0089         const auto entry = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0090         entry->lesson()->removeEntry(entry);
0091         endRemoveRows();
0092 
0093         Q_EMIT multipleChoiceAvailableChanged();
0094         delete entry;
0095 
0096         return;
0097     }
0098 
0099     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0100     expression->setTranslation(0, question);
0101     expression->setTranslation(1, answer);
0102 
0103     Q_EMIT dataChanged(index(row, 0), index(row, 0));
0104 }
0105 
0106 bool KWQCardModel::save()
0107 {
0108     Q_ASSERT(m_document);
0109 
0110     QUrl url;
0111 
0112     if (m_document->url().isEmpty()) {
0113         const auto dir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
0114         url = QUrl::fromLocalFile(QDir::toNativeSeparators(dir + QLatin1Char('/') + m_document->title() + QStringLiteral(".kvtml")));
0115 
0116         // Create parent in case it doesn't exist
0117         QDir newDir(dir);
0118         if (!newDir.mkpath(dir)) {
0119             qCritical() << "Unable to create directory" << dir;
0120             return false;
0121         }
0122     } else {
0123         url = m_document->url();
0124     }
0125 
0126 
0127     auto result = m_document->saveAs(url, KEduVocDocument::Automatic);
0128     return result == KEduVocDocument::NoError;
0129 }
0130 
0131 KEduVocDocument *KWQCardModel::document() const
0132 {
0133     return m_document;
0134 }
0135 
0136 void KWQCardModel::setDocument(KEduVocDocument *document)
0137 {
0138     if (document == m_document) {
0139         return;
0140     }
0141     beginResetModel();
0142     m_document = document;
0143     endResetModel();
0144 
0145     Q_EMIT documentChanged();
0146     Q_EMIT enabledChanged();
0147     Q_EMIT titleChanged();
0148     Q_EMIT authorContact();
0149     Q_EMIT authorContactChanged();
0150     Q_EMIT licenseChanged();
0151     Q_EMIT identifierLeftChanged();
0152     Q_EMIT identifierRightChanged();
0153 }
0154 
0155 QString KWQCardModel::title() const
0156 {
0157     return m_document ? m_document->title() : QString{};
0158 }
0159 
0160 void KWQCardModel::setTitle(const QString &title)
0161 {
0162     Q_ASSERT(m_document);
0163 
0164     if (title == m_document->title()) {
0165         return;
0166     }
0167     m_document->setTitle(title);
0168 
0169     Q_EMIT titleChanged();
0170 }
0171 
0172 QString KWQCardModel::author() const
0173 {
0174     return m_document ? m_document->author() : QString{};
0175 }
0176 
0177 void KWQCardModel::setAuthor(const QString &author)
0178 {
0179     Q_ASSERT(m_document);
0180 
0181     if (author == m_document->author()) {
0182         return;
0183     }
0184     m_document->setAuthor(author);
0185     Q_EMIT authorChanged();
0186 }
0187 
0188 QString KWQCardModel::authorContact() const
0189 {
0190     return m_document ? m_document->authorContact() : QString{};
0191 }
0192 
0193 void KWQCardModel::setAuthorContact(const QString &authorContact)
0194 {
0195     Q_ASSERT(m_document);
0196 
0197     if (authorContact == m_document->authorContact()) {
0198         return;
0199     }
0200     m_document->setAuthorContact(authorContact);
0201     Q_EMIT authorContactChanged();
0202 }
0203 
0204 QString KWQCardModel::license() const
0205 {
0206     return m_document ? m_document->license() : QString{};
0207 }
0208 
0209 void KWQCardModel::setLicense(const QString &license)
0210 {
0211     Q_ASSERT(m_document);
0212 
0213     if (license == m_document->license()) {
0214         return;
0215     }
0216     m_document->setLicense(license);
0217     Q_EMIT licenseChanged();
0218 }
0219 
0220 QString KWQCardModel::langQuestion() const
0221 {
0222     return m_document ? m_document->identifier(0).locale() : QString{};
0223 }
0224 
0225 void KWQCardModel::setLangQuestion(const QString &langQuestion)
0226 {
0227     Q_ASSERT(m_document);
0228 
0229     if (langQuestion == m_document->identifier(0).locale()) {
0230         return;
0231     }
0232     auto identifier = m_document->identifier(0);
0233     identifier.setLocale(langQuestion);
0234     m_document->setIdentifier(0, identifier);
0235     Q_EMIT langQuestionChanged();
0236 }
0237 
0238 QString KWQCardModel::langAnswer() const
0239 {
0240     return m_document ? m_document->identifier(1).locale() : QString{};
0241 }
0242 
0243 void KWQCardModel::setLangAnswer(const QString &langAnswer)
0244 {
0245     Q_ASSERT(m_document);
0246 
0247     if (langAnswer == m_document->identifier(1).locale()) {
0248         return;
0249     }
0250     auto identifier = m_document->identifier(1);
0251     identifier.setLocale(langAnswer);
0252     m_document->setIdentifier(1, identifier);
0253     Q_EMIT langAnswerChanged();
0254 }
0255 
0256 bool KWQCardModel::multipleChoiceAvailable() const
0257 {
0258     return rowCount() > 4;
0259 }
0260 
0261 bool KWQCardModel::enabled() const
0262 {
0263     return m_document;
0264 }
0265 
0266 void KWQCardModel::addQuestionImage(const int row, const QUrl &url)
0267 {
0268     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0269     expression->translation(0)->setImageUrl(url);
0270 
0271     Q_EMIT dataChanged(index(row, 0), index(row, 0), {QuestionImageRole});
0272 }
0273 
0274 void KWQCardModel::addAnswerImage(const int row, const QUrl &url)
0275 {
0276     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0277     expression->translation(1)->setImageUrl(url);
0278 
0279     Q_EMIT dataChanged(index(row, 0), index(row, 0), {AnswerImageRole});
0280 }
0281 
0282 void KWQCardModel::removeQuestionImage(const int row)
0283 {
0284     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0285     expression->translation(0)->setImageUrl({});
0286 
0287     Q_EMIT dataChanged(index(row, 0), index(row, 0), {QuestionImageRole});
0288 }
0289 
0290 void KWQCardModel::removeAnswerImage(const int row)
0291 {
0292     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0293     expression->translation(1)->setImageUrl({});
0294 
0295     Q_EMIT dataChanged(index(row, 0), index(row, 0), {AnswerImageRole});
0296 }
0297 
0298 void KWQCardModel::addQuestionSound(const int row, const QUrl &url)
0299 {
0300     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0301     expression->translation(0)->setSoundUrl(url);
0302 
0303     Q_EMIT dataChanged(index(row, 0), index(row, 0), {QuestionSoundRole});
0304 }
0305 
0306 void KWQCardModel::addAnswerSound(const int row, const QUrl &url)
0307 {
0308     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0309     expression->translation(1)->setSoundUrl(url);
0310 
0311     Q_EMIT dataChanged(index(row, 0), index(row, 0), {AnswerSoundRole});
0312 }
0313 
0314 void KWQCardModel::removeQuestionSound(const int row)
0315 {
0316     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0317     expression->translation(0)->setSoundUrl({});
0318 
0319     Q_EMIT dataChanged(index(row, 0), index(row, 0), {QuestionSoundRole});
0320 }
0321 
0322 void KWQCardModel::removeAnswerSound(const int row)
0323 {
0324     auto expression = m_document->lesson()->entries(KEduVocLesson::Recursive).value(row);
0325     expression->translation(1)->setSoundUrl({});
0326 
0327     Q_EMIT dataChanged(index(row, 0), index(row, 0), {AnswerSoundRole});
0328 }
0329 
0330 int KWQCardModel::rowCount(const QModelIndex &parent) const
0331 {
0332     if (parent.isValid() || !m_document) {
0333         return 0;
0334     }
0335 
0336     return m_document->lesson()->entryCount(KEduVocLesson::Recursive);
0337 }
0338 
0339 QVariant KWQCardModel::data(const QModelIndex &index, int role) const
0340 {
0341     Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
0342 
0343     const auto entry = m_document->lesson()->entries(KEduVocLesson::Recursive).value(index.row());
0344 
0345     switch (role) {
0346     case Qt::DisplayRole:
0347         return QString(entry->translation(0)->text() + u" ->"_s + entry->translation(1)->text());
0348 
0349     case QuestionRole:
0350         return entry->translation(0)->text();
0351     case QuestionImageRole:
0352         return entry->translation(0)->imageUrl().toLocalFile();
0353     case QuestionSoundRole:
0354         return entry->translation(0)->soundUrl().toLocalFile();
0355 
0356     case AnswerRole:
0357         return entry->translation(1)->text();
0358     case AnswerImageRole:
0359         return entry->translation(1)->imageUrl().toLocalFile();
0360     case AnswerSoundRole:
0361         return entry->translation(1)->soundUrl().toLocalFile();
0362 
0363     default:
0364         return {};
0365     }
0366 }
0367 
0368 QHash<int, QByteArray> KWQCardModel::roleNames() const
0369 {
0370     return {
0371         { QuestionRole, "question" },
0372         { QuestionImageRole, "questionImage" },
0373         { QuestionSoundRole, "questionSound" },
0374 
0375         { AnswerRole, "answer" },
0376         { AnswerImageRole, "answerImage" },
0377         { AnswerSoundRole, "answerSound" },
0378     };
0379 }
0380 
0381 #include "moc_kwqcardmodel.cpp"