File indexing completed on 2024-04-21 03:50:59

0001 /*
0002     SPDX-FileCopyrightText: 2007 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 #include "vocabularymodel.h"
0006 
0007 #include "languagesettings.h"
0008 #include "parleydocument.h"
0009 #include "prefs.h"
0010 
0011 #include <KEduVocLesson>
0012 #include <KEduVocWordtype>
0013 
0014 #include "vocabularymimedata.h"
0015 
0016 #include <KLocalizedString>
0017 #include <KMessageBox>
0018 #include <QDebug>
0019 #include <QPixmap>
0020 #include <QTextStream>
0021 
0022 VocabularyModel::VocabularyModel(QObject *parent)
0023     : QAbstractTableModel(parent)
0024     , m_container(nullptr)
0025     , m_document(nullptr)
0026 {
0027     m_recursive = Prefs::showSublessonentries() ? KEduVocContainer::Recursive : KEduVocContainer::NotRecursive;
0028 
0029     qRegisterMetaType<KEduVocTranslation *>("KEduVocTranslationPointer");
0030 }
0031 
0032 VocabularyModel::~VocabularyModel()
0033 {
0034 }
0035 
0036 void VocabularyModel::setDocument(KEduVocDocument *doc)
0037 {
0038     beginResetModel();
0039 
0040     m_document = doc;
0041     m_container = nullptr;
0042     m_lesson = nullptr;
0043 
0044     if (m_document) {
0045         showContainer(m_document->lesson());
0046     } else {
0047         showContainer(nullptr);
0048     }
0049 
0050     endResetModel();
0051 }
0052 
0053 void VocabularyModel::showContainer(KEduVocContainer *container)
0054 {
0055     // use remove and insert rows. using reset resets all table headers too.
0056     if (rowCount(QModelIndex()) > 0) {
0057         beginRemoveRows(QModelIndex(), 0, rowCount(QModelIndex()) - 1);
0058         m_container = nullptr;
0059         endRemoveRows();
0060     }
0061     if (container) {
0062         if (container->entryCount(m_recursive) > 0) {
0063             beginInsertRows(QModelIndex(), 0, container->entryCount(m_recursive) - 1);
0064             m_container = container;
0065             endInsertRows();
0066         } else {
0067             m_container = container;
0068         }
0069     }
0070 }
0071 
0072 void VocabularyModel::setLesson(KEduVocLesson *lessonContainer)
0073 {
0074     m_lesson = lessonContainer;
0075 }
0076 
0077 KEduVocLesson *VocabularyModel::lesson()
0078 {
0079     return m_lesson;
0080 }
0081 
0082 int VocabularyModel::rowCount(const QModelIndex &index) const
0083 {
0084     // no lesson set - zarro rows
0085     if (!m_container) {
0086         return 0;
0087     }
0088     // only the root index has children because we have no hierarchical model.
0089     if (index == QModelIndex()) {
0090         return m_container->entryCount(m_recursive);
0091     }
0092     return 0;
0093 }
0094 
0095 int VocabularyModel::columnCount(const QModelIndex &) const
0096 {
0097     if (!m_document) {
0098         return 0;
0099     }
0100     return m_document->identifierCount() * EntryColumnsMAX;
0101 }
0102 
0103 QVariant VocabularyModel::data(const QModelIndex &index, int role) const
0104 {
0105     if (!m_document || !m_container) {
0106         return QVariant();
0107     }
0108 
0109     int translationId = translation(index.column());
0110     int entryColumn = columnType(index.column());
0111 
0112     switch (role) {
0113     case Qt::DisplayRole:
0114         switch (entryColumn) {
0115         case Translation:
0116             return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->text());
0117         case Pronunciation:
0118             return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->pronunciation());
0119         case WordClass:
0120             // if no word type is set, we get a null pointer
0121             if (m_container->entry(index.row(), m_recursive)->translation(translationId)->wordType()) {
0122                 return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->wordType()->name());
0123             }
0124             return QVariant(QString());
0125         case Synonym: {
0126             QStringList displayElements;
0127             QList<KEduVocTranslation *> synonyms = m_container->entry(index.row(), m_recursive)->translation(translationId)->synonyms();
0128             for (KEduVocTranslation *synonym : qAsConst(synonyms)) {
0129                 displayElements.append(synonym->text());
0130             }
0131             return QVariant(displayElements.join(QStringLiteral("; ")));
0132         }
0133         case Antonym: {
0134             QStringList displayElements;
0135             QList<KEduVocTranslation *> antonyms = m_container->entry(index.row(), m_recursive)->translation(translationId)->antonyms();
0136             for (KEduVocTranslation *antonym : qAsConst(antonyms)) {
0137                 displayElements.append(antonym->text());
0138             }
0139             return QVariant(displayElements.join(QStringLiteral("; ")));
0140         }
0141         case Example: {
0142             QString example = m_container->entry(index.row(), m_recursive)->translation(translationId)->example();
0143             /*QString word = m_container->entry(index.row(), m_recursive)->translation(translationId)->text();
0144             int pos = 0;
0145             QString start = "<font color=\"#FF0000\"><b>";
0146             QString end = "</b></font>";
0147             while ((pos = example.indexOf(word, pos)) >= 0) {
0148                 example.insert(pos, start);
0149                 example.insert(pos+word.length()+start.length(), end);
0150                 pos += word.length()+start.length()+end.length();
0151             }*/
0152             return QVariant(example);
0153         }
0154         case Comment:
0155             return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->comment());
0156         case Paraphrase:
0157             return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->paraphrase());
0158         default:
0159             return QVariant();
0160         }
0161         break;
0162     case Qt::FontRole:
0163         if (entryColumn == Translation) {
0164             QString locale = m_document->identifier(translationId).locale();
0165             LanguageSettings ls(locale);
0166             ls.load();
0167             return ls.editorFont();
0168         }
0169         return QVariant();
0170     case LocaleRole:
0171         return QVariant(m_document->identifier(translationId).locale());
0172     case AudioRole:
0173         return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->soundUrl());
0174     case ImageRole:
0175         return QVariant(m_container->entry(index.row(), m_recursive)->translation(translationId)->imageUrl());
0176     case EntryRole: {
0177         QVariant v;
0178         v.setValue(m_container->entry(index.row(), m_recursive));
0179         return v;
0180     }
0181     case Qt::ToolTipRole: {
0182         ///@todo more tooltips?
0183         switch (entryColumn) {
0184         case WordClass:
0185             return i18n("You can drag and drop words onto their word type.");
0186         case Synonym:
0187             return i18n("Enable the synonym view to edit synonyms.");
0188         case Antonym:
0189             return i18n("Enable the antonym view to edit antonyms.");
0190         }
0191     }
0192     }
0193     return QVariant();
0194 }
0195 
0196 bool VocabularyModel::setData(const QModelIndex &index, const QVariant &value, int role)
0197 {
0198     if ((!index.isValid()) || (role != Qt::EditRole)) {
0199         return false;
0200     }
0201 
0202     int translationId = translation(index.column());
0203     int column = columnType(index.column());
0204 
0205     switch (column) {
0206     case Translation:
0207         m_container->entry(index.row(), m_recursive)->translation(translationId)->setText(value.toString());
0208         break;
0209     case Pronunciation:
0210         m_container->entry(index.row(), m_recursive)->translation(translationId)->setPronunciation(value.toString());
0211         break;
0212     case WordClass:
0213         break;
0214     case Example:
0215         m_container->entry(index.row(), m_recursive)->translation(translationId)->setExample(value.toString());
0216         break;
0217     case Comment:
0218         m_container->entry(index.row(), m_recursive)->translation(translationId)->setComment(value.toString());
0219         break;
0220     case Paraphrase:
0221         m_container->entry(index.row(), m_recursive)->translation(translationId)->setParaphrase(value.toString());
0222         break;
0223     default:
0224         return false;
0225     }
0226 
0227     Q_EMIT dataChanged(index, index);
0228     ///@todo trust dirty bit
0229     m_document->setModified();
0230     return true;
0231 }
0232 
0233 Qt::ItemFlags VocabularyModel::flags(const QModelIndex &index) const
0234 {
0235     if (!index.isValid()) {
0236         return Qt::ItemIsDropEnabled;
0237     }
0238 
0239     switch (columnType(index.column())) {
0240     case Translation:
0241         return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
0242     case WordClass:
0243         return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
0244     case Synonym:
0245     case Antonym:
0246         return QAbstractItemModel::flags(index) | Qt::ItemIsDropEnabled;
0247     default:
0248         return QAbstractItemModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsDropEnabled;
0249     }
0250 }
0251 
0252 QVariant VocabularyModel::headerData(int section, Qt::Orientation orientation, int role) const
0253 {
0254     if (!m_document) {
0255         return QVariant();
0256     }
0257 
0258     if (orientation == Qt::Horizontal) {
0259         int translationId = section / EntryColumnsMAX;
0260         int entryColumn = section % EntryColumnsMAX;
0261 
0262         switch (role) {
0263         case Qt::DisplayRole:
0264             return VocabularyModel::columnTitle(m_document, translationId, entryColumn, /*addLocaleSuffix*/ true);
0265             break;
0266         } // switch role
0267     } // if horizontal
0268     return QVariant();
0269 }
0270 
0271 QString VocabularyModel::columnTitle(KEduVocDocument *document, int translation, int column, bool addLocaleSuffix)
0272 {
0273     const QString localeSuffix = document->identifier(translation).locale();
0274     switch (column) {
0275     case Translation:
0276         if (document->identifierCount() - 1 < translation) {
0277             return QString();
0278         }
0279         return document->identifier(translation).name(); // returns "English", "German", etc
0280     case Pronunciation:
0281         return addLocaleSuffix ? i18n("Pronunciation (%1)", localeSuffix) : i18n("Pronunciation");
0282     case WordClass:
0283         return addLocaleSuffix ? i18n("Word Type (%1)", localeSuffix) : i18n("Word Type");
0284     case Synonym:
0285         return addLocaleSuffix ? i18n("Synonym (%1)", localeSuffix) : i18n("Synonym");
0286     case Antonym:
0287         return addLocaleSuffix ? i18n("Antonym (%1)", localeSuffix) : i18n("Antonym");
0288     case Example:
0289         return addLocaleSuffix ? i18n("Example (%1)", localeSuffix) : i18n("Example");
0290     case Comment:
0291         return addLocaleSuffix ? i18n("Comment (%1)", localeSuffix) : i18n("Comment");
0292     case Paraphrase:
0293         return addLocaleSuffix ? i18n("Paraphrase (%1)", localeSuffix) : i18n("Paraphrase");
0294     }
0295 
0296     return QString();
0297 }
0298 
0299 int VocabularyModel::translation(int column)
0300 {
0301     return column / EntryColumnsMAX;
0302 }
0303 
0304 int VocabularyModel::columnType(int column)
0305 {
0306     return column % EntryColumnsMAX;
0307 }
0308 
0309 QModelIndex VocabularyModel::appendEntry(KEduVocExpression *expression)
0310 {
0311     if (m_document->identifierCount() == 0) {
0312         KMessageBox::information(nullptr, i18n("Please use Edit -> Languages to set up your document."), i18n("No Languages Defined"));
0313         return QModelIndex();
0314     }
0315 
0316     if (!m_lesson || !m_lesson->parent()) {
0317         KMessageBox::information(nullptr, i18n("Select a unit before adding vocabulary."), i18n("No Unit Selected"));
0318         delete expression;
0319         return QModelIndex();
0320     }
0321 
0322     beginInsertRows(QModelIndex(), rowCount(QModelIndex()), rowCount(QModelIndex()));
0323     if (!expression) {
0324         expression = new KEduVocExpression;
0325     }
0326     m_lesson->appendEntry(expression);
0327     endInsertRows();
0328 
0329     return index(rowCount(QModelIndex()) - 1, 0, QModelIndex());
0330 }
0331 
0332 bool VocabularyModel::removeRows(int row, int count, const QModelIndex &parent)
0333 {
0334     Q_UNUSED(parent);
0335     if (count < 1 || row < 0 || row + count > m_container->entryCount(m_recursive)) {
0336         return false;
0337     }
0338 
0339     int bottomRow = row + count - 1;
0340     beginRemoveRows(QModelIndex(), row, bottomRow);
0341 
0342     for (int i = bottomRow; i >= row; i--) {
0343         delete m_container->entry(i, m_recursive);
0344     }
0345 
0346     endRemoveRows();
0347     return true;
0348 }
0349 
0350 QStringList VocabularyModel::mimeTypes() const
0351 {
0352     return QStringList() << QStringLiteral("text/plain");
0353 }
0354 
0355 QMimeData *VocabularyModel::mimeData(const QModelIndexList &indexes) const
0356 {
0357     VocabularyMimeData *mimeData = new VocabularyMimeData();
0358     QModelIndexList sortedIndexes = indexes;
0359     std::sort(sortedIndexes.begin(), sortedIndexes.end());
0360 
0361     qDebug() << "mimeData for " << indexes.count() << "indexes";
0362 
0363     QList<KEduVocTranslation *> translations;
0364     for (const QModelIndex &index : qAsConst(sortedIndexes)) {
0365         // only add if it's a translation. other cells like word type are being ignored for now.
0366         if (columnType(index.column()) == Translation) {
0367             translations.append(m_container->entry(index.row(), m_recursive)->translation(translation(index.column())));
0368         }
0369     }
0370 
0371     mimeData->setTranslations(translations);
0372     return mimeData;
0373 }
0374 
0375 void VocabularyModel::showEntriesOfSubcontainers(bool show)
0376 {
0377     beginResetModel();
0378     Prefs::setShowSublessonentries(show);
0379     if (show) {
0380         m_recursive = KEduVocContainer::Recursive;
0381     } else {
0382         m_recursive = KEduVocContainer::NotRecursive;
0383     }
0384     endResetModel();
0385 }
0386 
0387 void VocabularyModel::resetLanguages()
0388 {
0389     // play it save - this happens seldom enough to warrant a reload
0390     setDocument(m_document);
0391 }
0392 
0393 void VocabularyModel::automaticTranslation(bool enabled)
0394 {
0395     Prefs::setAutomaticTranslation(enabled);
0396 }
0397 
0398 #include "moc_vocabularymodel.cpp"