File indexing completed on 2024-04-21 07:37:49

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "parleydocument.h"
0007 
0008 #include "editor/editor.h"
0009 #include "parleymainwindow.h"
0010 #include "prefs.h"
0011 
0012 #include "dashboard/dashboard.h"
0013 #include "settings/documentproperties.h"
0014 #include "vocabularyview.h"
0015 
0016 #include <KEduVocExpression>
0017 #include <KEduVocLeitnerBox>
0018 #include <KEduVocLesson>
0019 #include <KEduVocWordtype>
0020 
0021 #include <KEMailSettings>
0022 #include <KMessageBox>
0023 #include <KProcess>
0024 #include <KRecentFilesAction>
0025 #include <QFileDialog>
0026 #include <QMimeDatabase>
0027 #include <QStandardPaths>
0028 #include <QTemporaryDir>
0029 #include <knewstuff_version.h>
0030 
0031 #include <QDialog>
0032 #include <QDialogButtonBox>
0033 #include <QTimer>
0034 
0035 #ifdef HAVE_LIBXSLT
0036 #include "exportdialog.h"
0037 #include <libxml/parser.h>
0038 #include <libxml/tree.h>
0039 #include <libxslt/transform.h>
0040 #include <libxslt/xslt.h>
0041 #include <libxslt/xsltInternals.h>
0042 #include <libxslt/xsltutils.h>
0043 #endif
0044 
0045 #include "settings/languageproperties.h"
0046 
0047 namespace DocumentHelper
0048 {
0049 void fetchGrammar(KEduVocDocument *doc, int languageIndex)
0050 {
0051     QString locale = doc->identifier(languageIndex).locale();
0052 
0053     QUrl location(QUrl::fromUserInput(QStringLiteral("https://edu.kde.org/parley/locale/") + locale.split('_').at(0).toLower() + QStringLiteral(".kvtml")));
0054 
0055     KEduVocDocument grammarDoc;
0056     if (grammarDoc.open(location) == KEduVocDocument::NoError) {
0057         doc->identifier(languageIndex).setArticle(grammarDoc.identifier(0).article());
0058         doc->identifier(languageIndex).setPersonalPronouns(grammarDoc.identifier(0).personalPronouns());
0059         // @todo        m_doc->identifier(index).setDeclension(grammarDoc.identifier(0).declension());
0060         doc->identifier(languageIndex).setTenseList(grammarDoc.identifier(0).tenseList());
0061     } else {
0062         qDebug() << "Download of " << location << " failed.";
0063     }
0064 }
0065 } // namespace DocumentHelper
0066 
0067 ParleyDocument::ParleyDocument(ParleyMainWindow *parleyMainWindow)
0068     : QObject(parleyMainWindow)
0069     , m_parleyApp(parleyMainWindow)
0070     , m_doc(new KEduVocDocument)
0071 {
0072 }
0073 
0074 ParleyDocument::~ParleyDocument()
0075 {
0076     close();
0077 }
0078 
0079 std::shared_ptr<KEduVocDocument> ParleyDocument::document()
0080 {
0081     // If there is no present vocabulary document, create an empty one.
0082     if (!m_doc) {
0083         m_doc.reset(new KEduVocDocument);
0084     }
0085 
0086     return m_doc;
0087 }
0088 
0089 void ParleyDocument::setTitle(const QString &title)
0090 {
0091     m_doc->setTitle(title);
0092 }
0093 
0094 void ParleyDocument::slotFileNew()
0095 {
0096     if (queryClose()) {
0097         newDocument(true);
0098     }
0099 }
0100 
0101 void ParleyDocument::newDocument(bool wizard)
0102 {
0103     std::shared_ptr<KEduVocDocument> newDoc(new KEduVocDocument);
0104 
0105     initializeDefaultGrammar(newDoc.get());
0106     setDefaultDocumentProperties(newDoc.get());
0107     bool showGrammarDialog = false;
0108     bool fetchGrammarOnline = false;
0109     if (wizard) {
0110         DocumentProperties *titleAuthorWidget = new DocumentProperties(newDoc.get(), true, m_parleyApp);
0111 
0112         QDialogButtonBox *button_dialog = new QDialogButtonBox;
0113         button_dialog->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0114 
0115         QVBoxLayout *layout = new QVBoxLayout;
0116         layout->addWidget(titleAuthorWidget);
0117         layout->addWidget(button_dialog);
0118 
0119         QDialog *titleAuthorDialog = new QDialog;
0120         titleAuthorDialog->setLayout(layout);
0121         titleAuthorDialog->setWindowTitle(i18nc("@title:window document properties", "Properties for %1", newDoc->url().url()));
0122 
0123         connect(titleAuthorDialog, &QDialog::accepted, titleAuthorWidget, &DocumentProperties::accept);
0124         connect(button_dialog, &QDialogButtonBox::accepted, titleAuthorDialog, &QDialog::accept);
0125         connect(button_dialog, &QDialogButtonBox::rejected, titleAuthorDialog, &QDialog::reject);
0126 
0127         if (titleAuthorDialog->exec()) {
0128             showGrammarDialog = titleAuthorWidget->grammarCheckBox->isChecked();
0129             fetchGrammarOnline = titleAuthorWidget->downloadGrammarCheckBox->isChecked();
0130             delete titleAuthorDialog;
0131         } else {
0132             delete titleAuthorDialog;
0133             return;
0134         }
0135     }
0136 
0137     close();
0138     m_doc = std::move(newDoc);
0139     Q_EMIT documentChanged(m_doc);
0140     enableAutoBackup(Prefs::autoBackup());
0141 
0142     if (fetchGrammarOnline) {
0143         DocumentHelper::fetchGrammar(m_doc.get(), 0);
0144         DocumentHelper::fetchGrammar(m_doc.get(), 1);
0145     }
0146     if (showGrammarDialog) {
0147         languageProperties();
0148     }
0149 
0150     m_parleyApp->showEditor();
0151 }
0152 
0153 void ParleyDocument::slotFileOpen()
0154 {
0155     if (queryClose()) {
0156         ///@todo frameworks check filters are fixed in kvocdoc
0157         ///@todo frameworks check how to add child checkbox to this dialog
0158         QFileDialog dialog(m_parleyApp, i18nc("@title:window", "Open Vocabulary Collection"), QString(), KEduVocDocument::pattern(KEduVocDocument::Reading));
0159         QCheckBox *practiceCheckBox = new QCheckBox(i18n("Open in practice &mode"), &dialog);
0160         practiceCheckBox->setChecked(m_parleyApp->currentComponent() != ParleyMainWindow::EditorComponent);
0161         dialog.setFileMode(QFileDialog::ExistingFile);
0162         if (dialog.exec() && !dialog.selectedFiles().isEmpty() && open(QUrl::fromLocalFile(dialog.selectedFiles().constFirst()))) {
0163             if (practiceCheckBox->isChecked()) {
0164                 m_parleyApp->showPracticeConfiguration();
0165             } else {
0166                 m_parleyApp->showEditor();
0167             }
0168         }
0169     }
0170 }
0171 
0172 void ParleyDocument::slotFileOpenRecent(const QUrl &url)
0173 {
0174     if (queryClose() && open(url)) {
0175         m_parleyApp->showEditor(); ///@todo: start practice directly depending on current component
0176     }
0177 }
0178 
0179 bool ParleyDocument::open(const QUrl &url)
0180 {
0181     if (url.path().isEmpty()) {
0182         return false;
0183     }
0184 
0185     close();
0186 
0187     m_doc.reset(new KEduVocDocument);
0188     Q_EMIT documentChanged(m_doc);
0189     m_doc->setCsvDelimiter(Prefs::separator());
0190 
0191     bool isSuccess = false, isError = false;
0192 
0193     KEduVocDocument::ErrorCode ret = m_doc->open(url, KEduVocDocument::FileDefaultHandling);
0194     switch (ret) {
0195     case KEduVocDocument::NoError:
0196         isSuccess = true;
0197         break;
0198     case KEduVocDocument::FileLocked: {
0199         int exit =
0200             KMessageBox::warningTwoActions(m_parleyApp,
0201                                            i18n("The vocabulary collection is locked by another process.  You can open the file if you take over the lock, "
0202                                                 "but you will lose any changes from the other process.\n\nDo you want to take over the lock?\n"),
0203                                            i18n("Take Over Lock"),
0204                                            KStandardGuiItem::open(),
0205                                            KStandardGuiItem::cancel());
0206         if (exit == KMessageBox::PrimaryAction) { // attempt to steal lock
0207 
0208             ret = m_doc->open(url, KEduVocDocument::FileIgnoreLock);
0209             if (ret == KEduVocDocument::NoError) {
0210                 qDebug() << "Lock stolen";
0211                 isSuccess = true;
0212             } else {
0213                 isError = true;
0214             }
0215         } else { // Don't Steal continue work without saving !!!
0216         }
0217 
0218         break;
0219     }
0220     default:
0221         isError = true;
0222     }
0223 
0224     if (isSuccess) {
0225         qDebug() << "Open success.";
0226         // m_parleyApp->editor()->updateDocument();
0227         m_parleyApp->addRecentFile(url, m_doc->title());
0228 
0229         enableAutoBackup(Prefs::autoBackup());
0230 
0231     } else {
0232         if (isError) {
0233             KMessageBox::error(m_parleyApp,
0234                                i18n("Opening collection \"%1\" resulted in an error: %2", m_doc->url().url(), m_doc->errorDescription(ret)),
0235                                i18nc("@title:window", "Open Collection"));
0236         }
0237         m_doc.reset();
0238         Q_EMIT documentChanged(m_doc);
0239     }
0240 
0241     return isSuccess;
0242 }
0243 
0244 void ParleyDocument::close()
0245 {
0246     enableAutoBackup(false);
0247     if (m_doc) {
0248         m_doc.reset();
0249         Q_EMIT documentChanged(m_doc);
0250     }
0251 }
0252 
0253 bool ParleyDocument::queryClose()
0254 {
0255     if (!m_doc || !m_doc->isModified()) {
0256         return true;
0257     }
0258 
0259     bool canSave = Prefs::autoSave(); // save without asking
0260 
0261     if (!canSave) {
0262         int exit = KMessageBox::warningTwoActionsCancel(m_parleyApp,
0263                                                         i18n("Vocabulary is modified.\n\nSave file before exit?\n"),
0264                                                         QString(),
0265                                                         KStandardGuiItem::save(),
0266                                                         KStandardGuiItem::discard());
0267         switch (exit) {
0268         case KMessageBox::PrimaryAction:
0269             canSave = true; // save and exit
0270             break;
0271         case KMessageBox::SecondaryAction:
0272             canSave = false; // don't save but exit
0273             break;
0274         case KMessageBox::Continue:
0275         default:
0276             return false; // continue work without saving !!!
0277         }
0278     }
0279 
0280     if (canSave) {
0281         save(); // save and exit
0282     }
0283 
0284     close();
0285     return true;
0286 }
0287 
0288 void ParleyDocument::openGHNS()
0289 {
0290     if (m_parleyApp->queryClose()) {
0291         QDir downloadDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + '/' + "kvtml/");
0292 
0293         downloadDir.mkpath(downloadDir.absolutePath());
0294 
0295         QUrl url = QUrl::fromUserInput(QFileDialog::getOpenFileName(m_parleyApp,
0296                                                                     i18nc("@title:window", "Open Downloaded Vocabulary Collection"),
0297                                                                     downloadDir.path(),
0298                                                                     KEduVocDocument::pattern(KEduVocDocument::Reading)));
0299 
0300         if (open(url)) {
0301             m_parleyApp->showPracticeConfiguration();
0302         }
0303     }
0304 }
0305 
0306 void ParleyDocument::save()
0307 {
0308     if (m_doc->url().fileName().isEmpty()) {
0309         saveAs();
0310         return;
0311     }
0312 
0313     // remove previous backup
0314     QFile::remove(m_doc->url().toLocalFile() + '~');
0315     QFile::copy(QFile::encodeName(m_doc->url().toLocalFile()), QFile::encodeName(m_doc->url().toLocalFile() + '~'));
0316 
0317     m_doc->setCsvDelimiter(Prefs::separator());
0318 
0319     Q_EMIT statesNeedSaving();
0320 
0321     QString newgenerator = QLatin1String("Parley ") + PARLEY_VERSION_STRING;
0322     m_doc->setGenerator(newgenerator);
0323 
0324     bool isSuccess = false, isError = false;
0325 
0326     KEduVocDocument::ErrorCode ret = m_doc->saveAs(m_doc->url(), KEduVocDocument::Automatic, KEduVocDocument::FileIgnoreLock);
0327 
0328     switch (ret) {
0329     case KEduVocDocument::NoError:
0330         isSuccess = true;
0331         break;
0332     case KEduVocDocument::FileLocked: {
0333         int exit =
0334             KMessageBox::warningTwoActions(m_parleyApp,
0335                                            i18n("File \"%1\" is locked by another process.  You can save to the file if you take over the lock, but you will "
0336                                                 "lose any changes from the other process.\n\nDo you want to take over the lock?\n",
0337                                                 m_doc->url().url()),
0338                                            QString(),
0339                                            KStandardGuiItem::save(),
0340                                            KStandardGuiItem::cancel());
0341         if (exit == KMessageBox::PrimaryAction) {
0342             m_doc->setGenerator(newgenerator);
0343             ret = m_doc->saveAs(m_doc->url(), KEduVocDocument::Automatic, KEduVocDocument::FileIgnoreLock);
0344 
0345             if (ret == KEduVocDocument::NoError) {
0346                 isSuccess = true;
0347                 qDebug() << "Lock stolen";
0348             } else {
0349                 isError = true;
0350             }
0351         } else {
0352             // Intentionally empty else. Try to saveAs another filename
0353         }
0354         break;
0355     }
0356     default:
0357         isError = true;
0358     }
0359 
0360     if (isSuccess) {
0361         m_parleyApp->addRecentFile(m_doc->url(), m_doc->title());
0362         enableAutoBackup(Prefs::autoBackup());
0363     } else {
0364         if (isError) {
0365             KMessageBox::error(m_parleyApp,
0366                                i18n("Writing file \"%1\" resulted in an error: %2", m_doc->url().url(), m_doc->errorDescription(ret)),
0367                                i18nc("@title:window", "Save File"));
0368         }
0369         qDebug() << "Save failed trying save as for " << m_doc->url().url();
0370         saveAs();
0371     }
0372 }
0373 
0374 void ParleyDocument::saveAs(QUrl url)
0375 {
0376     if (!m_doc) {
0377         return;
0378     }
0379 
0380     if (url.isEmpty()) {
0381         url = QUrl::fromLocalFile(QFileDialog::getSaveFileName(m_parleyApp->parentWidget(),
0382                                                                i18nc("@title:window", "Save Vocabulary As"),
0383                                                                QString(),
0384                                                                KEduVocDocument::pattern(KEduVocDocument::Writing)));
0385 
0386         if (url.isEmpty()) {
0387             return;
0388         }
0389     }
0390 
0391     QFileInfo fileinfo(url.toLocalFile());
0392     if (fileinfo.exists()) {
0393         if (KMessageBox::warningContinueCancel(nullptr,
0394                                                i18n("<qt>The file<p><b>%1</b></p>already exists. Do you want to overwrite it?</qt>", url.toLocalFile()),
0395                                                QString(),
0396                                                KStandardGuiItem::overwrite())
0397             == KMessageBox::Cancel) {
0398             return;
0399         }
0400     }
0401 
0402     QString msg = i18nc("@info:status saving a file", "Saving %1", url.toLocalFile());
0403 
0404     QFile::remove(url.toLocalFile() + '~'); // remove previous backup
0405     QFile::copy(QFile::encodeName(url.toLocalFile()), QFile::encodeName(QString(url.toLocalFile() + '~')));
0406 
0407     m_doc->setCsvDelimiter(Prefs::separator());
0408 
0409     if (!url.toLocalFile().contains('.')) {
0410         url = QUrl::fromLocalFile(url.toLocalFile() + QLatin1String(".kvtml"));
0411     }
0412 
0413     bool isSuccess = false, isError = false;
0414     m_doc->setGenerator(QStringLiteral("Parley"));
0415     int ret = m_doc->saveAs(url, KEduVocDocument::Automatic);
0416     switch (ret) {
0417     case KEduVocDocument::NoError:
0418         isSuccess = true;
0419         break;
0420     case KEduVocDocument::FileLocked: {
0421         int exit =
0422             KMessageBox::warningTwoActions(m_parleyApp,
0423                                            i18n("File \"%1\" is locked by another process.  You can save to the file if you take over the lock, but you will "
0424                                                 "lose any changes from the other process.\n\nDo you want to take over the lock?\n",
0425                                                 m_doc->url().url()),
0426                                            QString(),
0427                                            KStandardGuiItem::save(),
0428                                            KStandardGuiItem::cancel());
0429         if (exit == KMessageBox::PrimaryAction) { // attempt lock steal
0430             m_doc->setGenerator(QLatin1String("Parley ") + PARLEY_VERSION_STRING);
0431             ret = m_doc->saveAs(m_doc->url(), KEduVocDocument::Automatic, KEduVocDocument::FileIgnoreLock);
0432 
0433             if (ret == KEduVocDocument::NoError) {
0434                 isSuccess = true;
0435                 qDebug() << "Lock stolen";
0436             } else {
0437                 isError = true;
0438             }
0439             break;
0440         } else { // don't steal the lock
0441         }
0442 
0443         break;
0444     }
0445     default:
0446         isError = true;
0447         break;
0448     }
0449 
0450     if (isSuccess) {
0451         qDebug() << "SaveAs success.";
0452         m_parleyApp->addRecentFile(m_doc->url(), m_doc->title());
0453         Q_EMIT statesNeedSaving();
0454 
0455     } else {
0456         qDebug() << "SaveAs failed for " << m_doc->url().url() << " \nwhy " << m_doc->errorDescription(ret);
0457         if (isError) {
0458             KMessageBox::error(m_parleyApp,
0459                                i18n("Writing file \"%1\" resulted in an error: %2", m_doc->url().url(), m_doc->errorDescription(ret)),
0460                                i18nc("@title:window", "Save File"));
0461         }
0462     }
0463 }
0464 
0465 void ParleyDocument::initializeDefaultGrammar(KEduVocDocument *doc)
0466 {
0467     KEduVocWordType *root = doc->wordTypeContainer();
0468     KEduVocWordType *noun = new KEduVocWordType(i18n("Noun"), root);
0469     noun->setWordType(KEduVocWordFlag::Noun);
0470     root->appendChildContainer(noun);
0471 
0472     KEduVocWordType *nounChild = new KEduVocWordType(i18n("Masculine"), noun);
0473     nounChild->setWordType(KEduVocWordFlag::Noun | KEduVocWordFlag::Masculine);
0474     noun->appendChildContainer(nounChild);
0475     nounChild = new KEduVocWordType(i18n("Feminine"), noun);
0476     nounChild->setWordType(KEduVocWordFlag::Noun | KEduVocWordFlag::Feminine);
0477     noun->appendChildContainer(nounChild);
0478     nounChild = new KEduVocWordType(i18n("Neuter"), noun);
0479     nounChild->setWordType(KEduVocWordFlag::Noun | KEduVocWordFlag::Neuter);
0480     noun->appendChildContainer(nounChild);
0481 
0482     KEduVocWordType *verb = new KEduVocWordType(i18n("Verb"), root);
0483     verb->setWordType(KEduVocWordFlag::Verb);
0484     root->appendChildContainer(verb);
0485 
0486     KEduVocWordType *adjective = new KEduVocWordType(i18n("Adjective"), root);
0487     adjective->setWordType(KEduVocWordFlag::Adjective);
0488     root->appendChildContainer(adjective);
0489 
0490     KEduVocWordType *adverb = new KEduVocWordType(i18n("Adverb"), root);
0491     adverb->setWordType(KEduVocWordFlag::Adverb);
0492     root->appendChildContainer(adverb);
0493 
0494     KEduVocWordType *conjunction = new KEduVocWordType(i18n("Conjunction"), root);
0495     conjunction->setWordType(KEduVocWordFlag::Conjunction);
0496     root->appendChildContainer(conjunction);
0497 }
0498 
0499 void ParleyDocument::setDefaultDocumentProperties(KEduVocDocument *doc)
0500 {
0501     KEMailSettings emailSettings;
0502     emailSettings.setProfile(emailSettings.defaultProfileName());
0503     doc->setAuthor(emailSettings.getSetting(KEMailSettings::RealName));
0504     doc->setAuthorContact(emailSettings.getSetting(KEMailSettings::EmailAddress));
0505 
0506     doc->setLicense(i18n("Public Domain"));
0507     doc->setCategory(i18n("Languages"));
0508 
0509     QString locale = QLocale().name();
0510 
0511     doc->appendIdentifier();
0512     doc->appendIdentifier();
0513     doc->identifier(0).setName(QLocale(locale).nativeLanguageName());
0514     doc->identifier(0).setLocale(locale);
0515     doc->identifier(1).setName(i18n("A Second Language"));
0516     doc->identifier(1).setLocale(locale);
0517 
0518     KEduVocLesson *lesson = new KEduVocLesson(i18n("Lesson 1"), doc->lesson());
0519     doc->lesson()->appendChildContainer(lesson);
0520 
0521     // add some entries
0522     for (int i = 0; i < 15; i++) {
0523         lesson->appendEntry(new KEduVocExpression());
0524     }
0525 
0526     doc->setModified(false);
0527 }
0528 
0529 void ParleyDocument::slotGHNS(const QList<KNSCore::Entry> &entries)
0530 {
0531     QMimeDatabase db;
0532     QString fileName;
0533     int numberInstalled = 0;
0534     for (const auto &entry : entries) {
0535         if (entry.status() != KNSCore::Entry::Installed) {
0536             continue;
0537         }
0538         ++numberInstalled;
0539         // check mime type and if kvtml, open it
0540         const QStringList installedFiles = entry.installedFiles();
0541         for (const QString &file : installedFiles) {
0542             QMimeType mimeType = db.mimeTypeForFile(file);
0543             qDebug() << "KNS2 file of mime type:" << db.mimeTypeForFile(file).name();
0544             if (mimeType.inherits(QStringLiteral("application/x-kvtml"))) {
0545                 ParleyMainWindow::instance()->addRecentFile(QUrl::fromLocalFile(file), QString()); ///@todo: title!
0546                 fileName = file;
0547             }
0548         }
0549     }
0550 
0551     // to enable the display in the welcome screen
0552     Prefs::self()->save();
0553     m_parleyApp->updateRecentFilesModel();
0554     if (numberInstalled > 1) {
0555         openGHNS();
0556     } else if (numberInstalled == 1) {
0557         if (open(QUrl::fromLocalFile(fileName)))
0558             m_parleyApp->showPracticeConfiguration();
0559         else
0560             KMessageBox::error(m_parleyApp, i18n("Could not open vocabulary collection \"%1\"", entries.first().name()));
0561     }
0562 }
0563 
0564 void ParleyDocument::documentProperties()
0565 {
0566     DocumentProperties *titleAuthorWidget = new DocumentProperties(m_doc.get(), false, m_parleyApp);
0567 
0568     QDialogButtonBox *button_dialog = new QDialogButtonBox;
0569     button_dialog->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0570 
0571     QVBoxLayout *layout = new QVBoxLayout;
0572     layout->addWidget(titleAuthorWidget);
0573     layout->addWidget(button_dialog);
0574 
0575     QDialog *titleAuthorDialog = new QDialog;
0576     titleAuthorDialog->setLayout(layout);
0577     titleAuthorDialog->setWindowTitle(i18nc("@title:window document properties", "Properties for %1", m_doc->url().url()));
0578 
0579     // the language options are only shown, when this is used to create a new document.
0580     titleAuthorWidget->languageGroupBox->setVisible(false);
0581 
0582     connect(titleAuthorDialog, &QDialog::accepted, titleAuthorWidget, &DocumentProperties::accept);
0583     connect(button_dialog, &QDialogButtonBox::accepted, titleAuthorDialog, &QDialog::accept);
0584     connect(button_dialog, &QDialogButtonBox::rejected, titleAuthorDialog, &QDialog::reject);
0585 
0586     titleAuthorDialog->exec();
0587     delete titleAuthorDialog;
0588     Q_EMIT documentChanged(m_doc);
0589 }
0590 
0591 void ParleyDocument::languageProperties()
0592 {
0593     LanguageProperties properties(m_doc.get(), m_parleyApp);
0594     if (properties.exec() == QDialog::Accepted) {
0595         Q_EMIT languagesChanged();
0596     }
0597 }
0598 
0599 void ParleyDocument::exportDialog()
0600 {
0601 #ifdef HAVE_LIBXSLT
0602     ExportDialog dialog(this, m_parleyApp);
0603     dialog.exec();
0604 #endif
0605 }
0606 
0607 void ParleyDocument::slotFileMerge()
0608 {
0609     ///@todo as soon as some brave soul descends into the lib and implements merging this should be enabled
0610     //     KUrl url = KFileDialog::getOpenUrl(QString(), KEduVocDocument::pattern(KEduVocDocument::Reading), parentWidget(), i18n("Merge Vocabulary File"));
0611     //
0612     //     if (!url.isEmpty()) {
0613     //         QString msg = i18n("Loading %1", url.path());
0614     //         slotStatusMsg(msg);
0615     //
0616     //         KEduVocDocument *new_doc = new KEduVocDocument(this);
0617     //         new_doc->setCsvDelimiter(Prefs::separator());
0618     //         new_doc->open(url);
0619     //
0620     //         m_doc->merge(new_doc, true);
0621     //
0622     //         KEduVocWordFlag::setTenseNames(m_doc->tenseDescriptions());
0623     //         KVTUsage::setUsageNames(m_doc->usageDescriptions());
0624     //
0625     //         delete(new_doc);
0626     //         m_recentFilesAction->addUrl(url);
0627     //         m_tableModel->reset();
0628     //         m_lessonModel->setDocument(m_doc);
0629     //         m_tableView->adjustContent();
0630     //     }
0631 }
0632 
0633 void ParleyDocument::enableAutoBackup(bool enable)
0634 {
0635     if (!enable) {
0636         if (m_backupTimer) {
0637             m_backupTimer->stop();
0638         }
0639     } else {
0640         if (!m_backupTimer) {
0641             m_backupTimer = new QTimer(this);
0642             connect(m_backupTimer, &QTimer::timeout, this, &ParleyDocument::save);
0643         }
0644         m_backupTimer->start(Prefs::backupTime() * 60 * 1000);
0645     }
0646 }
0647 
0648 #include "moc_parleydocument.cpp"