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"