File indexing completed on 2024-03-24 03:51:05
0001 /* 0002 * Vocabulary Document for KDE Edu 0003 * SPDX-FileCopyrightText: 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de> 0004 * SPDX-FileCopyrightText: 2005-2007 Peter Hedlund <peter.hedlund@kdemail.net> 0005 * SPDX-FileCopyrightText: 2007 Frederik Gladhorn <frederik.gladhorn@kdemail.net> 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 #include "keduvocdocument.h" 0009 0010 #include <QCoreApplication> 0011 #include <QFileInfo> 0012 #include <QIODevice> 0013 #include <QTemporaryFile> 0014 #include <QTextStream> 0015 #include <QtAlgorithms> 0016 0017 #include <KAutoSaveFile> 0018 #include <KCompressionDevice> 0019 #include <KLazyLocalizedString> 0020 #include <KLocalizedString> 0021 #include <QDebug> 0022 #include <kio/filecopyjob.h> 0023 0024 #include "keduvoccsvwriter.h" 0025 #include "keduvocexpression.h" 0026 #include "keduvockvtml2writer.h" 0027 #include "keduvocleitnerbox.h" 0028 #include "keduvoclesson.h" 0029 #include "keduvocwordtype.h" 0030 #include "readerwriters/readerbase.h" 0031 #include "readerwriters/readermanager.h" 0032 0033 #define WQL_IDENT "WordQuiz" 0034 0035 #define KVTML_EXT "kvtml" 0036 #define CSV_EXT "csv" 0037 #define TXT_EXT "txt" 0038 #define WQL_EXT "wql" 0039 0040 /** @details Private Data class for KEduVocDocument */ 0041 class KEduVocDocument::KEduVocDocumentPrivate 0042 { 0043 public: 0044 /** constructor */ 0045 KEduVocDocumentPrivate(KEduVocDocument *qq) 0046 : q(qq) 0047 { 0048 m_lessonContainer = nullptr; 0049 m_wordTypeContainer = nullptr; 0050 m_leitnerContainer = nullptr; 0051 m_autosave = new KAutoSaveFile; 0052 init(); 0053 } 0054 0055 /** destructor */ 0056 ~KEduVocDocumentPrivate(); 0057 0058 /** initializer */ 0059 void init(); 0060 0061 KEduVocDocument *q; ///< The Front End 0062 0063 /** autosave file used to provide locking access to the underlying file 0064 * Note: It is a pointer to allow locking a new file, saving results and 0065 * then transferring the lock to m_autosave without risking loss of lock. 0066 * See saveAs for clarification*/ 0067 KAutoSaveFile *m_autosave; 0068 0069 bool m_dirty; ///< dirty bit 0070 bool m_isReadOnly; ///< FileOpenReadOnly was used for opening 0071 0072 // save these to document 0073 QList<KEduVocIdentifier> m_identifiers; ///< list of identifiers 0074 0075 QList<int> m_extraSizeHints; ///< unused 0076 QList<int> m_sizeHints; ///< unused 0077 0078 QString m_generator; ///< name of generating application 0079 QString m_queryorg; ///< unused 0080 QString m_querytrans; ///< unused 0081 0082 QStringList m_tenseDescriptions; ///< unused. Was used in merge 0083 QSet<QString> m_usages; ///< unused 0084 0085 QString m_title; ///< Document title 0086 QString m_author; ///< Document author 0087 QString m_authorContact; ///< Author contact information 0088 QString m_license; ///< Document license 0089 QString m_comment; ///< Document comment 0090 QString m_version; ///< Document version 0091 QString m_csvDelimiter; ///< CSV delimiter 0092 0093 /** Categories that can later be used to sork kvtml files: 0094 * language, music, children, anatomy 0095 */ 0096 QString m_category; ///< Document category 0097 0098 KEduVocLesson *m_lessonContainer; ///< Root lesson container 0099 KEduVocWordType *m_wordTypeContainer; ///< Root word types container 0100 /** Root Leitner container 0101 * (probably unused) This is not used in Parley 0102 * */ 0103 KEduVocLeitnerBox *m_leitnerContainer; 0104 0105 /** Creates an autosave file for fpath in order to lock the file 0106 * @param autosave a reference to an allocated KAutosave 0107 * @param fpath File path to the file to be locked 0108 * @param flags Describes how to deal with locked file etc. 0109 * @return ErrorCode where NoError is success 0110 * */ 0111 KEduVocDocument::ErrorCode initializeKAutoSave(KAutoSaveFile &autosave, QString const &fpath, FileHandlingFlags flags) const; 0112 }; 0113 0114 KEduVocDocument::KEduVocDocumentPrivate::~KEduVocDocumentPrivate() 0115 { 0116 delete m_lessonContainer; 0117 delete m_wordTypeContainer; 0118 delete m_leitnerContainer; 0119 delete m_autosave; 0120 } 0121 0122 void KEduVocDocument::KEduVocDocumentPrivate::init() 0123 { 0124 delete m_lessonContainer; 0125 m_lessonContainer = new KEduVocLesson(i18nc("The top level lesson which contains all other lessons of the document.", "Document Lesson"), q); 0126 m_lessonContainer->setContainerType(KEduVocLesson::Lesson); 0127 delete m_wordTypeContainer; 0128 m_wordTypeContainer = new KEduVocWordType(i18n("Word types")); 0129 0130 delete m_leitnerContainer; 0131 m_leitnerContainer = new KEduVocLeitnerBox(i18n("Leitner Box")); 0132 0133 m_tenseDescriptions.clear(); 0134 m_identifiers.clear(); 0135 m_extraSizeHints.clear(); 0136 m_sizeHints.clear(); 0137 m_dirty = false; 0138 m_isReadOnly = false; 0139 m_queryorg = QLatin1String(""); 0140 m_querytrans = QLatin1String(""); 0141 m_autosave->setManagedFile(QUrl()); 0142 m_author = QLatin1String(""); 0143 m_title = QLatin1String(""); 0144 m_comment = QLatin1String(""); 0145 m_version = QLatin1String(""); 0146 m_generator = QLatin1String(""); 0147 m_csvDelimiter = QString('\t'); 0148 m_usages.clear(); 0149 m_license.clear(); 0150 m_category.clear(); 0151 } 0152 0153 KEduVocDocument::ErrorCode 0154 KEduVocDocument::KEduVocDocumentPrivate::initializeKAutoSave(KAutoSaveFile &autosave, QString const &fpath, FileHandlingFlags flags) const 0155 { 0156 QList<KAutoSaveFile *> staleFiles = KAutoSaveFile::staleFiles(QUrl::fromLocalFile(fpath), QCoreApplication::instance()->applicationName()); 0157 if (!staleFiles.isEmpty()) { 0158 if (flags & FileIgnoreLock) { 0159 foreach (KAutoSaveFile *f, staleFiles) { 0160 f->open(QIODevice::ReadWrite); 0161 f->remove(); 0162 delete f; 0163 } 0164 } else { 0165 qWarning() << i18n("Cannot lock file %1", fpath); 0166 return FileLocked; 0167 } 0168 } 0169 0170 autosave.setManagedFile(QUrl::fromLocalFile(fpath)); 0171 if (!autosave.open(QIODevice::ReadWrite)) { 0172 qWarning() << i18n("Cannot lock file %1", autosave.fileName()); 0173 return FileCannotLock; 0174 } 0175 0176 return NoError; 0177 } 0178 0179 KEduVocDocument::KEduVocDocument(QObject *parent) 0180 : QObject(parent) 0181 , d(new KEduVocDocumentPrivate(this)) 0182 { 0183 } 0184 0185 KEduVocDocument::~KEduVocDocument() 0186 { 0187 delete d; 0188 } 0189 0190 void KEduVocDocument::setModified(bool dirty) 0191 { 0192 d->m_dirty = dirty; 0193 Q_EMIT docModified(d->m_dirty); 0194 } 0195 0196 KEduVocDocument::FileType KEduVocDocument::detectFileType(const QString &fileName) 0197 { 0198 KCompressionDevice f(fileName); 0199 f.open(QIODevice::ReadOnly); 0200 0201 ReaderManager::ReaderPtr reader(ReaderManager::reader(f)); 0202 0203 f.close(); 0204 0205 return reader->fileTypeHandled(); 0206 } 0207 0208 KEduVocDocument::ErrorCode KEduVocDocument::open(const QUrl &url, FileHandlingFlags flags) 0209 { 0210 // save csv delimiter to preserve it in case this is a csv document 0211 QString csv = d->m_csvDelimiter; 0212 // clear all other properties 0213 d->init(); 0214 if (!url.isEmpty()) { 0215 setUrl(url); 0216 } 0217 d->m_csvDelimiter = csv; 0218 0219 KEduVocDocument::ErrorCode errStatus = Unknown; 0220 QString errorMessage = i18n("<qt>Cannot open file<br /><b>%1</b></qt>", url.toDisplayString()); 0221 0222 QString temporaryFile; 0223 QTemporaryFile tempFile; 0224 if (url.isLocalFile()) { 0225 temporaryFile = url.toLocalFile(); 0226 } else { 0227 if (!tempFile.open()) { 0228 qWarning() << i18n("Cannot open tempfile %1", tempFile.fileName()); 0229 return Unknown; 0230 } 0231 KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tempFile.fileName()), -1, KIO::Overwrite); 0232 if (!job->exec()) { 0233 qWarning() << i18n("Cannot download %1: %2", url.toDisplayString(), job->errorString()); 0234 return FileDoesNotExist; 0235 } 0236 temporaryFile = tempFile.fileName(); 0237 } 0238 0239 if (flags & FileOpenReadOnly) { 0240 d->m_isReadOnly = true; 0241 } 0242 0243 ErrorCode autosaveError = NoError; 0244 0245 if (!d->m_isReadOnly) { 0246 autosaveError = d->initializeKAutoSave(*d->m_autosave, temporaryFile, flags); 0247 if (autosaveError != NoError) { 0248 return autosaveError; 0249 } 0250 } 0251 0252 KCompressionDevice f(temporaryFile); 0253 if (f.open(QIODevice::ReadOnly)) { 0254 ReaderManager::ReaderPtr reader(ReaderManager::reader(f)); 0255 errStatus = reader->read(*this); 0256 0257 if (errStatus != KEduVocDocument::NoError) { 0258 errorMessage = i18n("Could not open or properly read \"%1\"\n(Error reported: %2)", url.toDisplayString(), reader->errorMessage()); 0259 } 0260 } else { 0261 errStatus = FileCannotRead; 0262 } 0263 0264 f.close(); 0265 0266 if (errStatus == KEduVocDocument::NoError) { 0267 setModified(false); 0268 } else { 0269 qWarning() << errorMessage; 0270 } 0271 0272 return errStatus; 0273 } 0274 0275 void KEduVocDocument::close() 0276 { 0277 if (!d->m_isReadOnly) { 0278 d->m_autosave->releaseLock(); 0279 } 0280 } 0281 0282 KEduVocDocument::ErrorCode KEduVocDocument::saveAs(const QUrl &url, FileType ft, FileHandlingFlags flags) 0283 { 0284 if (d->m_isReadOnly) { 0285 return FileIsReadOnly; 0286 } 0287 0288 QUrl tmp(url); 0289 0290 if (ft == Automatic) { 0291 if (tmp.path().right(strlen("." KVTML_EXT)) == "." KVTML_EXT) 0292 ft = Kvtml; 0293 else if (tmp.path().right(strlen("." CSV_EXT)) == "." CSV_EXT) 0294 ft = Csv; 0295 else { 0296 return FileTypeUnknown; 0297 } 0298 } 0299 0300 QString errorMessage = i18n("Cannot write to file %1", tmp.toDisplayString()); 0301 0302 KAutoSaveFile *autosave; 0303 0304 // If we don't care about the lock always create a new one 0305 // If we are changing files create a new lock 0306 if ((flags & FileIgnoreLock) || (d->m_autosave->managedFile() != tmp)) { 0307 autosave = new KAutoSaveFile; 0308 ErrorCode autosaveError = d->initializeKAutoSave(*autosave, tmp.toLocalFile(), flags); 0309 if (autosaveError != NoError) { 0310 delete autosave; 0311 return autosaveError; 0312 } 0313 } 0314 0315 // We care about the lock and we think we still have it. 0316 else { 0317 autosave = d->m_autosave; 0318 // Is the lock still good? 0319 if (!autosave->exists()) { 0320 return FileCannotLock; 0321 } 0322 } 0323 0324 QFile f(tmp.toLocalFile()); 0325 if (!f.open(QIODevice::WriteOnly)) { 0326 qCritical() << i18n("Cannot write to file %1", f.fileName()); 0327 return FileCannotWrite; 0328 } 0329 0330 bool saved = false; 0331 0332 switch (ft) { 0333 case Kvtml: { 0334 // write version 2 file 0335 KEduVocKvtml2Writer kvtmlWriter(&f); 0336 saved = kvtmlWriter.writeDoc(this, d->m_generator); 0337 } break; 0338 ///@todo port me 0339 // case Kvtml1: { 0340 // // write old version 1 file 0341 // KEduVocKvtmlWriter kvtmlWriter( &f ); 0342 // saved = kvtmlWriter.writeDoc( this, d->m_generator ); 0343 // } 0344 // break; 0345 case Csv: { 0346 KEduVocCsvWriter csvWriter(&f); 0347 saved = csvWriter.writeDoc(this, d->m_generator); 0348 } break; 0349 default: { 0350 qCritical() << "kvcotrainDoc::saveAs(): unknown filetype"; 0351 } break; 0352 } // switch 0353 0354 f.close(); 0355 0356 if (!saved) { 0357 qCritical() << "Error Saving File" << tmp.toDisplayString(); 0358 0359 if (autosave != d->m_autosave) { 0360 delete autosave; 0361 } 0362 return FileWriterFailed; 0363 } 0364 0365 if (autosave != d->m_autosave) { 0366 // The order is important: release old lock, delete old locker and then claim new locker. 0367 d->m_autosave->releaseLock(); 0368 delete d->m_autosave; 0369 d->m_autosave = autosave; 0370 } 0371 0372 setModified(false); 0373 return NoError; 0374 } 0375 0376 QByteArray KEduVocDocument::toByteArray(const QString &generator) 0377 { 0378 // no file needed 0379 KEduVocKvtml2Writer kvtmlWriter(nullptr); 0380 return kvtmlWriter.toByteArray(this, generator); 0381 } 0382 0383 void KEduVocDocument::merge(KEduVocDocument *docToMerge, bool matchIdentifiers) 0384 { 0385 Q_UNUSED(docToMerge) 0386 Q_UNUSED(matchIdentifiers) 0387 qDebug() << "Merging of docs is not implemented"; /// @todo IMPLEMENT ME 0388 // This code was really horribly broken. 0389 // Now with the new classes we could attempt to reactivate it. 0390 // A rewrite might be easier. 0391 0392 /* 0393 if (docToMerge) { 0394 0395 QStringList new_names = docToMerge->lessonDescriptions(); 0396 0397 QStringList new_types = docToMerge->typeDescriptions(); 0398 0399 QStringList new_tenses = docToMerge->tenseDescriptions(); 0400 0401 QList<int> old_in_query = lessonsInPractice(); 0402 QList<int> new_in_query = docToMerge->lessonsInPractice(); 0403 0404 QStringList new_usages = docToMerge->usageDescriptions(); 0405 0406 int lesson_offset = d->m_lessonDescriptions.count(); 0407 for (int i = 0; i < new_names.count(); i++) { 0408 d->m_lessonDescriptions.append(new_names[i]); 0409 } 0410 0411 for (int i = 0; i < new_in_query.count(); i++) 0412 old_in_query.append(new_in_query[i] + lesson_offset); 0413 setLessonsInPractice(old_in_query); 0414 0415 int types_offset = d->m_typeDescriptions.count(); 0416 for (int i = 0; i < new_types.count(); i++) { 0417 d->m_typeDescriptions.append(new_types[i]); 0418 } 0419 0420 int tenses_offset = d->m_tenseDescriptions.count(); 0421 for (int i = 0; i < new_tenses.count(); i++) { 0422 d->m_tenseDescriptions.append(new_tenses[i]); 0423 } 0424 0425 int usages_offset = d->m_usageDescriptions.count(); 0426 for (int i = 0; i < new_usages.count(); i++) { 0427 d->m_usageDescriptions.append(new_usages[i]); 0428 } 0429 0430 bool equal = true; 0431 if (originalIdentifier() != docToMerge->originalIdentifier()) 0432 equal = false; 0433 for (int i = 1; i < identifierCount(); i++) 0434 if (identifier(i) != docToMerge->identifier(i)) 0435 equal = false; 0436 0437 if (!matchIdentifiers) 0438 equal = true; ///@todo massive cheating, problem if docToMerge has more identifiers than this 0439 0440 if (equal) { // easy way: same language codes, just append 0441 0442 for (int i = 0; i < docToMerge->entryCount(); i++) { 0443 KEduVocExpression *expr = docToMerge->entry(i); 0444 0445 expr->setLesson(expr->lesson() + lesson_offset); 0446 0447 for (int lang = 0; lang <= expr->translationCount(); lang++) { 0448 QString t = expr->translation(lang).type(); 0449 // adjust type offset 0450 if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) { 0451 QString t2; 0452 t.remove(0, 1); 0453 t2.setNum(t.toInt() + types_offset); 0454 t2.prepend(QM_USER_TYPE); 0455 expr->translation(lang).setType (t2); 0456 } 0457 0458 t = expr->translation(lang).usageLabel(); 0459 // adjust usage offset 0460 QString tg; 0461 if (!t.isEmpty()) { 0462 QString t2; 0463 while (t.left(strlen(":")) == UL_USER_USAGE) { 0464 QString n; 0465 t.remove(0, 1); 0466 int next; 0467 if ((next = t.indexOf(":")) >= 0) { 0468 n = t.left(next); 0469 t.remove(0, next + 1); 0470 } 0471 else { 0472 n = t; 0473 t = ""; 0474 } 0475 0476 t2.setNum(n.toInt() + usages_offset); 0477 t2.prepend(UL_USER_USAGE); 0478 if (tg.length() == 0) 0479 tg = t2; 0480 else 0481 tg += ':' + t2; 0482 } 0483 0484 if (tg.length() == 0) 0485 tg = t; 0486 else if (t.length() != 0) 0487 tg += ':' + t; 0488 0489 expr->translation(lang).setUsageLabel (tg); 0490 } 0491 0492 KEduVocConjugation conj = expr->translation(lang).conjugation(); 0493 bool condirty = false; 0494 for (int ci = 0; ci < conj.entryCount(); ci++) { 0495 t = conj.getType(ci); 0496 if (!t.isEmpty() && t.left(1) == UL_USER_TENSE) { 0497 t.remove(0, strlen(UL_USER_TENSE)); 0498 QString t2; 0499 t2.setNum(t.toInt() + tenses_offset); 0500 t2.prepend(UL_USER_TENSE); 0501 conj.setType(ci, t2); 0502 condirty = true; 0503 } 0504 if (condirty) 0505 expr->translation(lang).setConjugation(conj); 0506 } 0507 } 0508 0509 appendEntry(expr); 0510 } 0511 setModified(); 0512 } 0513 else { // hard way: move entries around, most attributes get lost 0514 QList<int> move_matrix; 0515 QList<bool> cs_equal; 0516 QString s; 0517 0518 for (int i = 0; i < qMax (identifierCount(), docToMerge->identifierCount()); i++) 0519 cs_equal.append(false); 0520 0521 move_matrix.append(docToMerge->indexOfIdentifier(originalIdentifier())); 0522 for (int i = 1; i < identifierCount(); i++) 0523 move_matrix.append(docToMerge->indexOfIdentifier(identifier(i))); 0524 0525 for (int j = 0; j < docToMerge->entryCount(); j++) { 0526 KEduVocExpression new_expr; 0527 KEduVocExpression *expr = docToMerge->entry(j); 0528 new_expr.setLesson(expr->lesson()+lesson_offset); 0529 0530 for (int i = 0; i < move_matrix.count(); i++) { 0531 int lpos = move_matrix[i]; 0532 if (lpos >= 0) { 0533 0534 if (lpos == 0) 0535 s = expr->original(); 0536 else 0537 s = expr->translation(lpos); 0538 0539 if (!cs_equal[lpos]) { 0540 cs_equal[lpos] = true; 0541 QString id = lpos == 0 ? originalIdentifier() : identifier(lpos); 0542 } 0543 0544 if (i == 0) 0545 new_expr.setOriginal(s); 0546 else 0547 new_expr.setTranslation(i, s); 0548 QString r = expr->remark(lpos); 0549 new_expr.setRemark (i, r); 0550 0551 QString t = expr->type(lpos); 0552 if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) { 0553 QString t2; 0554 t.remove(0, 1); 0555 t2.setNum(t.toInt() + types_offset); 0556 t2.prepend(QM_USER_TYPE); 0557 new_expr.setType(i, t2); 0558 } 0559 0560 t = expr->usageLabel(lpos); 0561 if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) { 0562 QString t2; 0563 t.remove(0, 1); 0564 t2.setNum(t.toInt() + usages_offset); 0565 t2.prepend(QM_USER_TYPE); 0566 new_expr.setUsageLabel(i, t2); 0567 } 0568 0569 KEduVocConjugation conj = expr->conjugation(lpos); 0570 for (int ci = 0; ci < conj.entryCount(); ci++) { 0571 t = conj.getType(ci); 0572 if (!t.isEmpty() && t.left(1) == QM_USER_TYPE) { 0573 t.remove (0, strlen(QM_USER_TYPE)); 0574 QString t2; 0575 t2.setNum(t.toInt() + tenses_offset); 0576 t2.prepend(QM_USER_TYPE); 0577 conj.setType(ci, t2); 0578 } 0579 } 0580 0581 } 0582 } 0583 // only append if entries are used 0584 bool used = !new_expr.original().isEmpty(); 0585 for (int i = 1; i < identifierCount(); i++) 0586 if (!new_expr.translation(i).isEmpty()) 0587 used = true; 0588 0589 if (used) { 0590 appendEntry(&new_expr); 0591 setModified(); 0592 } 0593 } 0594 } 0595 } 0596 */ 0597 } 0598 0599 const KEduVocIdentifier &KEduVocDocument::identifier(int index) const 0600 { 0601 if (index < 0 || index >= d->m_identifiers.size()) { 0602 qCritical() << " Error: Invalid identifier index: " << index; 0603 } 0604 return d->m_identifiers[index]; 0605 } 0606 0607 KEduVocIdentifier &KEduVocDocument::identifier(int index) 0608 { 0609 if (index < 0 || index >= d->m_identifiers.size()) { 0610 qCritical() << " Error: Invalid identifier index: " << index; 0611 } 0612 return d->m_identifiers[index]; 0613 } 0614 0615 void KEduVocDocument::setIdentifier(int idx, const KEduVocIdentifier &id) 0616 { 0617 if (idx >= 0 && idx < d->m_identifiers.size()) { 0618 d->m_identifiers[idx] = id; 0619 } 0620 setModified(true); 0621 } 0622 0623 // works if const is removed 0624 int KEduVocDocument::indexOfIdentifier(const QString &name) const 0625 { 0626 for (int i = 0; i < identifierCount(); i++) 0627 if (identifier(i).locale() == name) 0628 return i; 0629 return -1; 0630 } 0631 0632 void KEduVocDocument::removeIdentifier(int index) 0633 { 0634 if (index < d->m_identifiers.size() && index >= 0) { 0635 d->m_identifiers.removeAt(index); 0636 d->m_lessonContainer->removeTranslation(index); 0637 } 0638 } 0639 0640 bool KEduVocDocument::isModified() const 0641 { 0642 return d->m_dirty; 0643 } 0644 0645 int KEduVocDocument::identifierCount() const 0646 { 0647 return d->m_identifiers.count(); // number of translations 0648 } 0649 0650 int KEduVocDocument::appendIdentifier(const KEduVocIdentifier &id) 0651 { 0652 int i = d->m_identifiers.size(); 0653 // qDebug() << "appendIdentifier: " << i << id.name() << id.locale(); 0654 d->m_identifiers.append(id); 0655 if (id.name().isEmpty()) { 0656 if (i == 0) { 0657 identifier(i).setName(i18nc("The name of the first language/column of vocabulary, if we have to guess it.", "Original")); 0658 } else { 0659 identifier(i).setName(i18nc("The name of the second, third ... language/column of vocabulary, if we have to guess it.", "Translation %1", i)); 0660 } 0661 } 0662 0663 return i; 0664 } 0665 0666 KEduVocLesson *KEduVocDocument::lesson() 0667 { 0668 return d->m_lessonContainer; 0669 } 0670 0671 KEduVocWordType *KEduVocDocument::wordTypeContainer() 0672 { 0673 return d->m_wordTypeContainer; 0674 } 0675 0676 KEduVocLeitnerBox *KEduVocDocument::leitnerContainer() 0677 { 0678 return d->m_leitnerContainer; 0679 } 0680 0681 QUrl KEduVocDocument::url() const 0682 { 0683 return d->m_autosave->managedFile(); 0684 } 0685 0686 void KEduVocDocument::setUrl(const QUrl &url) 0687 { 0688 d->m_autosave->setManagedFile(url); 0689 } 0690 0691 QString KEduVocDocument::title() const 0692 { 0693 if (d->m_title.isEmpty()) 0694 return d->m_autosave->managedFile().fileName(); 0695 else 0696 return d->m_title; 0697 } 0698 0699 void KEduVocDocument::setTitle(const QString &title) 0700 { 0701 d->m_title = title; 0702 ///@todo decouple document title and root lesson title 0703 d->m_lessonContainer->setName(title); 0704 setModified(true); 0705 } 0706 0707 QString KEduVocDocument::author() const 0708 { 0709 return d->m_author; 0710 } 0711 0712 void KEduVocDocument::setAuthor(const QString &s) 0713 { 0714 d->m_author = s.simplified(); 0715 setModified(true); 0716 } 0717 0718 QString KEduVocDocument::authorContact() const 0719 { 0720 return d->m_authorContact; 0721 } 0722 0723 void KEduVocDocument::setAuthorContact(const QString &s) 0724 { 0725 d->m_authorContact = s.simplified(); 0726 setModified(true); 0727 } 0728 0729 QString KEduVocDocument::license() const 0730 { 0731 return d->m_license; 0732 } 0733 0734 QString KEduVocDocument::documentComment() const 0735 { 0736 return d->m_comment; 0737 } 0738 0739 void KEduVocDocument::setCategory(const QString &category) 0740 { 0741 d->m_category = category; 0742 setModified(true); 0743 } 0744 0745 QString KEduVocDocument::category() const 0746 { 0747 return d->m_category; 0748 ///@todo make writer/reader use this 0749 } 0750 0751 void KEduVocDocument::queryIdentifier(QString &org, QString &trans) const 0752 { 0753 org = d->m_queryorg; 0754 trans = d->m_querytrans; 0755 } 0756 0757 void KEduVocDocument::setQueryIdentifier(const QString &org, const QString &trans) 0758 { 0759 d->m_queryorg = org; 0760 d->m_querytrans = trans; 0761 setModified(true); 0762 } 0763 0764 void KEduVocDocument::setLicense(const QString &s) 0765 { 0766 d->m_license = s.simplified(); 0767 setModified(true); 0768 } 0769 0770 void KEduVocDocument::setDocumentComment(const QString &s) 0771 { 0772 d->m_comment = s.trimmed(); 0773 setModified(true); 0774 } 0775 0776 void KEduVocDocument::setGenerator(const QString &generator) 0777 { 0778 d->m_generator = generator; 0779 setModified(true); 0780 } 0781 0782 QString KEduVocDocument::generator() const 0783 { 0784 return d->m_generator; 0785 } 0786 0787 QString KEduVocDocument::version() const 0788 { 0789 return d->m_version; 0790 } 0791 0792 void KEduVocDocument::setVersion(const QString &vers) 0793 { 0794 d->m_version = vers; 0795 setModified(true); 0796 } 0797 0798 QString KEduVocDocument::csvDelimiter() const 0799 { 0800 return d->m_csvDelimiter; 0801 } 0802 0803 void KEduVocDocument::setCsvDelimiter(const QString &delimiter) 0804 { 0805 d->m_csvDelimiter = delimiter; 0806 setModified(true); 0807 } 0808 0809 QString KEduVocDocument::pattern(FileDialogMode mode) 0810 { 0811 static const struct SupportedFilter { 0812 bool reading; 0813 bool writing; 0814 const char *extensions; 0815 const KLazyLocalizedString description; 0816 } filters[] = {{true, true, "*.kvtml", kli18n("KDE Vocabulary Document")}, 0817 {true, false, "*.wql", kli18n("KWordQuiz Document")}, 0818 {true, false, "*.xml.qz *.pau.gz", kli18n("Pauker Lesson")}, 0819 {true, false, "*.voc", kli18n("Vokabeltrainer")}, 0820 {true, false, "*.xdxf", kli18n("XML Dictionary Exchange Format")}, 0821 {true, true, "*.csv", kli18n("Comma Separated Values (CSV)")}, 0822 // last is marker for the end, do not remove it 0823 {false, false, nullptr, KLazyLocalizedString()}}; 0824 QStringList newfilters; 0825 QStringList allext; 0826 for (int i = 0; filters[i].extensions; ++i) { 0827 if ((mode == Reading && filters[i].reading) || (mode == Writing && filters[i].writing)) { 0828 newfilters.append(KLocalizedString(filters[i].description).toString() + " (" + QLatin1String(filters[i].extensions) + ')'); 0829 allext.append(QLatin1String(filters[i].extensions)); 0830 } 0831 } 0832 if (mode == Reading) { 0833 newfilters.prepend(allext.join(QLatin1Char(' ')) + '|' + i18n("All supported documents")); 0834 } 0835 return newfilters.join(QLatin1String(";;")); 0836 } 0837 0838 QString KEduVocDocument::errorDescription(int errorCode) 0839 { 0840 switch (errorCode) { 0841 case NoError: 0842 return i18n("No error found."); 0843 0844 case InvalidXml: 0845 return i18n("Invalid XML in document."); 0846 case FileTypeUnknown: 0847 return i18n("Unknown file type."); 0848 case FileCannotWrite: 0849 return i18n("File is not writeable."); 0850 case FileWriterFailed: 0851 return i18n("File writer failed."); 0852 case FileCannotRead: 0853 return i18n("File is not readable."); 0854 case FileReaderFailed: 0855 return i18n("The file reader failed."); 0856 case FileDoesNotExist: 0857 return i18n("The file does not exist."); 0858 case FileLocked: 0859 return i18n("The file is locked by another process."); 0860 case FileCannotLock: 0861 return i18n("The lock file can't be created."); 0862 case Unknown: 0863 default: 0864 return i18n("Unknown error."); 0865 } 0866 } 0867 0868 #include "moc_keduvocdocument.cpp"