File indexing completed on 2023-05-30 10:45:25

0001 /*
0002     SPDX-FileCopyrightText: 2002-2010 Peter Hedlund <peter.hedlund@kdemail.net>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "kwordquiz.h"
0007 
0008 #include <QBitmap>
0009 #include <QCheckBox>
0010 #include <QDBusInterface>
0011 #include <QDBusReply>
0012 #include <QDebug>
0013 #include <QFileDialog>
0014 #include <QFontDialog>
0015 #include <QIcon>
0016 #include <QMenu>
0017 #include <QMimeDatabase>
0018 #include <QMimeType>
0019 #include <QPainter>
0020 #include <QPointer>
0021 #include <QStandardPaths>
0022 #include <QStatusBar>
0023 #include <QTemporaryFile>
0024 
0025 #include <KActionCollection>
0026 #include <KActionMenu>
0027 #include <KConfigGroup>
0028 #include <KLocalizedString>
0029 #include <KMessageBox>
0030 #include <KNotifyConfigWidget>
0031 #include <KPageWidget>
0032 #include <KProcess>
0033 #include <KToggleAction>
0034 #include <KToolBar>
0035 #include <KUndoActions>
0036 #include <KXMLGUIFactory>
0037 
0038 #include "keduvoclesson.h"
0039 #include "keduvocexpression.h"
0040 #include "kwqtablemodel.h"
0041 #include "kwqsortfiltermodel.h"
0042 #include "kwqtableview.h"
0043 #include "kwqcommands.h"
0044 #include "dlglanguage.h"
0045 #include "kwordquizprefs.h"
0046 #include "qaview.h"
0047 #include "flashview.h"
0048 #include "multipleview.h"
0049 #include "wqprintdialogpage.h"
0050 #include "prefs.h"
0051 #include "kwordquiz_version.h"
0052 #include <kwidgetsaddons_version.h>
0053 
0054 KWordQuizApp::KWordQuizApp(QWidget*):KXmlGuiWindow(0)
0055 {
0056   m_quiz = 0;
0057   m_prefDialog = 0;
0058 
0059   initStatusBar();
0060   initActions();
0061   initDocument();
0062   initModel();
0063 
0064   readOptions();
0065 
0066   initView();
0067 
0068   m_dirWatch = KDirWatch::self();
0069 
0070   slotModeActionGroupTriggered(m_modeActionGroup->actions().at(Prefs::mode() - 1));
0071 
0072   editMarkBlank->setEnabled(Prefs::enableBlanks());
0073   editUnmarkBlank->setEnabled(Prefs::enableBlanks());
0074 
0075   if (Prefs::firstRun())
0076   {
0077     fileOpenRecent->addUrl(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwordquiz/examples/example.kvtml"))));
0078     fileOpenRecent->addUrl(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwordquiz/examples/french_verbs.kvtml"))));
0079     fileOpenRecent->addUrl(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwordquiz/examples/fill_in_the_blank.kvtml"))));
0080     fileOpenRecent->addUrl(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwordquiz/examples/us_states_and_capitals.kvtml"))));
0081     Prefs::setFirstRun(false);
0082   }
0083 }
0084 
0085 KWordQuizApp::~KWordQuizApp()
0086 {
0087   disconnect(m_undoStack, &QUndoStack::undoTextChanged, this, &KWordQuizApp::slotUndoTextChanged);
0088   disconnect(m_undoStack, &QUndoStack::redoTextChanged, this, &KWordQuizApp::slotRedoTextChanged);
0089 }
0090 
0091 void KWordQuizApp::initActions()
0092 {
0093   QAction* configToolbar;
0094   QAction* configNotifications;
0095   QAction* configApp;
0096 
0097   QAction * a;
0098 
0099   fileNew = KStandardAction::openNew(this, SLOT(slotFileNew()), actionCollection());
0100   fileNew->setWhatsThis(i18n("Creates a new blank vocabulary document"));
0101   fileNew->setToolTip(fileNew->whatsThis());
0102   fileNew->setStatusTip(fileNew->whatsThis());
0103 
0104   fileOpen = KStandardAction::open(this, SLOT(slotFileOpen()), actionCollection());
0105   fileOpen->setWhatsThis(i18n("Opens an existing vocabulary document"));
0106   fileOpen->setToolTip(fileOpen->whatsThis());
0107   fileOpen->setStatusTip(fileOpen->whatsThis());
0108 
0109   fileOpenRecent = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
0110 
0111   KNSWidgets::Action *fileGHNS = new KNSWidgets::Action(i18n("Download New Vocabularies..."), QStringLiteral("kwordquiz.knsrc"), this);
0112   connect(fileGHNS, &KNSWidgets::Action::dialogFinished, this, &KWordQuizApp::slotFileGHNS);
0113   actionCollection()->addAction(QStringLiteral("file_ghns"), fileGHNS);
0114   actionCollection()->setDefaultShortcut(fileGHNS, QKeySequence(Qt::CTRL | Qt::Key_G));
0115   fileGHNS->setWhatsThis(i18n("Downloads new vocabularies"));
0116   fileGHNS->setToolTip(fileGHNS->whatsThis());
0117   fileGHNS->setStatusTip(fileGHNS->whatsThis());
0118 
0119   fileSave = KStandardAction::save(this, SLOT(slotFileSave()), actionCollection());
0120   fileSave->setWhatsThis(i18n("Saves the active vocabulary document"));
0121   fileSave->setToolTip(fileSave->whatsThis());
0122   fileSave->setStatusTip(fileSave->whatsThis());
0123 
0124   fileSaveAs = KStandardAction::saveAs(this, SLOT(slotFileSaveAs()), actionCollection());
0125   fileSaveAs->setWhatsThis(i18n("Saves the active vocabulary document with a different name"));
0126   fileSaveAs->setToolTip(fileSaveAs->whatsThis());
0127   fileSaveAs->setStatusTip(fileSaveAs->whatsThis());
0128 
0129   fileClose = KStandardAction::close(this, SLOT(slotFileClose()), actionCollection());
0130   fileClose->setWhatsThis(i18n("Closes the active vocabulary document"));
0131   fileClose->setStatusTip(fileClose->whatsThis());
0132 
0133   filePrint = KStandardAction::print(this, SLOT(slotFilePrint()), actionCollection());
0134   filePrint->setWhatsThis(i18n("Prints the active vocabulary document"));
0135   filePrint->setToolTip(filePrint->whatsThis());
0136   filePrint->setStatusTip(filePrint->whatsThis());
0137 
0138   filePrintPreview = KStandardAction::printPreview(this, SLOT(slotFilePrintPreview()), actionCollection());
0139   filePrintPreview->setWhatsThis(i18n("Shows a preview of the active vocabulary document"));
0140   filePrintPreview->setToolTip(filePrintPreview->whatsThis());
0141   filePrintPreview->setStatusTip(filePrintPreview->whatsThis());
0142 
0143   fileQuit = KStandardAction::quit(this, SLOT(slotFileQuit()), actionCollection());
0144   fileQuit->setWhatsThis(i18n("Quits KWordQuiz"));
0145   fileQuit->setToolTip(fileQuit->whatsThis());
0146   fileQuit->setStatusTip(fileQuit->whatsThis());
0147 
0148   m_undoStack = new QUndoStack(this);
0149   editUndo = KUndoActions::createUndoAction(m_undoStack, actionCollection());
0150   editRedo = KUndoActions::createRedoAction(m_undoStack, actionCollection());
0151   connect(m_undoStack, &QUndoStack::cleanChanged, this, &KWordQuizApp::slotCleanChanged);
0152   connect(m_undoStack, &QUndoStack::undoTextChanged, this, &KWordQuizApp::slotUndoTextChanged);
0153   connect(m_undoStack, &QUndoStack::redoTextChanged, this, &KWordQuizApp::slotRedoTextChanged);
0154 
0155   editCut = KStandardAction::cut(this, SLOT(slotEditCut()), actionCollection());
0156   editCut->setWhatsThis(i18n("Cuts the text from the selected cells and places it on the clipboard"));
0157   editCut->setToolTip(editCut->whatsThis());
0158   editCut->setStatusTip(editCut->whatsThis());
0159 
0160   editCopy = KStandardAction::copy(this, SLOT(slotEditCopy()), actionCollection());
0161   editCopy->setWhatsThis(i18n("Copies the text from the selected cells and places it on the clipboard"));
0162   editCopy->setToolTip(editCopy->whatsThis());
0163   editCopy->setStatusTip(editCopy->whatsThis());
0164 
0165   editPaste = KStandardAction::paste(this, SLOT(slotEditPaste()), actionCollection());
0166   editPaste->setWhatsThis(i18n("Pastes previously cut or copied text from the clipboard into the selected cells"));
0167   editPaste->setToolTip(editPaste->whatsThis());
0168   editPaste->setStatusTip(editPaste->whatsThis());
0169 
0170   editClear = KStandardAction::clear(this, SLOT(slotEditClear()), actionCollection());
0171   actionCollection()->setDefaultShortcut(editClear, Qt::Key_Delete);
0172   editClear->setWhatsThis(i18n("Clears the content of the selected cells"));
0173   editClear->setToolTip(editClear->whatsThis());
0174   editClear->setStatusTip(editClear->whatsThis());
0175 
0176   editInsert = actionCollection()->addAction(QStringLiteral("edit_insert"));
0177   editInsert->setIcon(QIcon::fromTheme(QStringLiteral("insert-table-row")));
0178   editInsert->setText(i18n("&Insert Row"));
0179   actionCollection()->setDefaultShortcut(editInsert, QKeySequence(Qt::CTRL | Qt::Key_I));
0180   editInsert->setWhatsThis(i18n("Inserts a new row above the current row"));
0181   editInsert->setToolTip(editInsert->whatsThis());
0182   editInsert->setStatusTip(editInsert->whatsThis());
0183   connect(editInsert, &QAction::triggered, this, &KWordQuizApp::slotEditInsert);
0184 
0185   editDelete = actionCollection()->addAction(QStringLiteral("edit_delete"));
0186   editDelete->setIcon(QIcon::fromTheme(QStringLiteral("delete-table-row")));
0187   editDelete->setText(i18n("&Delete Row"));
0188   actionCollection()->setDefaultShortcut(editDelete, QKeySequence(Qt::CTRL | Qt::Key_K));
0189   editDelete->setWhatsThis(i18n("Deletes the selected row(s)"));
0190   editDelete->setToolTip(editDelete->whatsThis());
0191   editDelete->setStatusTip(editDelete->whatsThis());
0192   connect(editDelete, &QAction::triggered, this, &KWordQuizApp::slotEditDelete);
0193 
0194   editMarkBlank = actionCollection()->addAction(QStringLiteral("edit_mark_blank"));
0195   editMarkBlank->setIcon(QIcon::fromTheme(QStringLiteral("markasblank")));
0196   editMarkBlank->setText(i18n("&Mark as Blank"));
0197   actionCollection()->setDefaultShortcut(editMarkBlank, QKeySequence(Qt::CTRL | Qt::Key_M));
0198   editMarkBlank->setWhatsThis(i18n("Marks the current or selected word as a blank for Fill-in-the-blank"));
0199   editMarkBlank->setToolTip(editMarkBlank->whatsThis());
0200   editMarkBlank->setStatusTip(editMarkBlank->whatsThis());
0201   connect(editMarkBlank, &QAction::triggered, this, &KWordQuizApp::slotEditMarkBlank);
0202 
0203   editUnmarkBlank = actionCollection()->addAction(QStringLiteral("edit_unmark_blank"));
0204   editUnmarkBlank->setIcon(QIcon::fromTheme(QStringLiteral("unmarkasblank")));
0205   editUnmarkBlank->setText(i18n("&Unmark Blanks"));
0206   editUnmarkBlank->setWhatsThis(i18n("Removes blanks from the current or selected word"));
0207   editUnmarkBlank->setToolTip(editUnmarkBlank->whatsThis());
0208   editUnmarkBlank->setStatusTip(editUnmarkBlank->whatsThis());
0209   connect(editUnmarkBlank, &QAction::triggered, this, &KWordQuizApp::slotEditUnmarkBlank);
0210 
0211   vocabLanguages = actionCollection()->addAction(QStringLiteral("vocab_languages"));
0212   vocabLanguages->setIcon(QIcon::fromTheme(QStringLiteral("languages")));
0213   vocabLanguages->setText(i18n("&Column Settings..."));
0214   actionCollection()->setDefaultShortcut(vocabLanguages, QKeySequence(Qt::CTRL | Qt::Key_L));
0215   vocabLanguages->setWhatsThis(i18n("Defines the column settings for the active vocabulary"));
0216   vocabLanguages->setToolTip(vocabLanguages->whatsThis());
0217   vocabLanguages->setStatusTip(vocabLanguages->whatsThis());
0218   connect(vocabLanguages, &QAction::triggered, this, &KWordQuizApp::slotVocabLanguages);
0219 
0220   vocabFont = actionCollection()->addAction(QStringLiteral("vocab_font"));
0221   vocabFont->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-font")));
0222   vocabFont->setText(i18n("&Font..."));
0223   vocabFont->setWhatsThis(i18n("Defines the font used by the editor"));
0224   vocabFont->setToolTip(vocabFont->whatsThis());
0225   vocabFont->setStatusTip(vocabFont->whatsThis());
0226   connect(vocabFont, &QAction::triggered, this, &KWordQuizApp::slotVocabFont);
0227 
0228   a = actionCollection()->addAction(QStringLiteral("vocab_image"));
0229   a->setIcon(QIcon::fromTheme(QStringLiteral("image-x-generic")));
0230   a->setText(i18n("Link &Image..."));
0231   a->setWhatsThis(i18n("Links an image with the current entry"));
0232   a->setToolTip(a->whatsThis());
0233   a->setStatusTip(a->whatsThis());
0234   connect(a, &QAction::triggered, this, &KWordQuizApp::slotVocabImage);
0235 
0236   a = actionCollection()->addAction(QStringLiteral("vocab_sound"));
0237   a->setIcon(QIcon::fromTheme(QStringLiteral("audio-x-generic")));
0238   a->setText(i18n("Link &Sound..."));
0239   a->setWhatsThis(i18n("Links a sound with the current entry"));
0240   a->setToolTip(a->whatsThis());
0241   a->setStatusTip(a->whatsThis());
0242   connect(a, &QAction::triggered, this, &KWordQuizApp::slotVocabSound);
0243 
0244   vocabAdjustRows = actionCollection()->addAction(QStringLiteral("vocab_adjust_rows"));
0245   //vocabAdjustRows->setIcon(QIcon::fromTheme());
0246   vocabAdjustRows->setText(i18n("&Adjust Row Heights"));
0247   vocabAdjustRows->setWhatsThis(i18n("Automatically adjusts the height of selected rows"));
0248   vocabAdjustRows->setToolTip(vocabAdjustRows->whatsThis());
0249   vocabAdjustRows->setStatusTip(vocabAdjustRows->whatsThis());
0250   connect(vocabAdjustRows, &QAction::triggered, this, &KWordQuizApp::slotVocabAdjustRows);
0251 
0252   vocabShuffle = actionCollection()->addAction(QStringLiteral("vocab_shuffle"));
0253   vocabShuffle->setIcon(QIcon::fromTheme(QStringLiteral("shuffle")));
0254   vocabShuffle->setText(i18n("Sh&uffle"));
0255   vocabShuffle->setWhatsThis(i18n("Shuffles the entries of the active vocabulary"));
0256   vocabShuffle->setToolTip(vocabShuffle->whatsThis());
0257   vocabShuffle->setStatusTip(vocabShuffle->whatsThis());
0258   connect(vocabShuffle, &QAction::triggered, this, &KWordQuizApp::slotVocabShuffle);
0259 
0260   vocabLayouts = actionCollection()->addAction(QStringLiteral("vocab_layouts"));
0261   vocabLayouts->setText(i18n("&Keyboard Layout"));
0262   vocabLayouts->setWhatsThis(i18n("Shows available keyboard layouts"));
0263   vocabLayouts->setToolTip(vocabLayouts->whatsThis());
0264   vocabLayouts->setStatusTip(vocabLayouts->whatsThis());
0265   QMenu *m = new QMenu(this);
0266   vocabLayouts->setMenu(m);
0267 
0268   m_layoutActionGroup = new QActionGroup(this);
0269   connect(m_layoutActionGroup, &QActionGroup::triggered, this, &KWordQuizApp::slotLayoutActionGroupTriggered);
0270 
0271   m_modeActionMenu = actionCollection()->add<KActionMenu>(QStringLiteral("mode_0"));
0272   m_modeActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mode1")));
0273   m_modeActionMenu->setText(i18n("Change Mode"));
0274   m_modeActionMenu->setIconText(i18n("Mode"));
0275   m_modeActionMenu->setWhatsThis(i18n("Changes the mode used in quiz sessions"));
0276   m_modeActionMenu->setToolTip(m_modeActionMenu->whatsThis());
0277   m_modeActionMenu->setStatusTip(m_modeActionMenu->whatsThis());
0278   connect(m_modeActionMenu, &QAction::triggered, this, &KWordQuizApp::slotModeChange);
0279 
0280   m_modeActionGroup = new QActionGroup(this);
0281   connect(m_modeActionGroup, &QActionGroup::triggered, this, &KWordQuizApp::slotModeActionGroupTriggered);
0282 
0283   for (int i = 1; i <=5; ++i) {
0284     a = actionCollection()->addAction(QStringLiteral("mode_%1").arg(QString::number(i)));
0285     a->setData(i);
0286     a->setCheckable(true);
0287     a->setIcon(QIcon::fromTheme(QStringLiteral("mode%1").arg(QString::number(i))));
0288     a->setWhatsThis(i18n("Selects this mode"));
0289     a->setToolTip(a->whatsThis());
0290     a->setStatusTip(a->whatsThis());
0291     m_modeActionMenu->addAction(a);
0292     m_modeActionGroup->addAction(a);
0293   }
0294 
0295   quizEditor = actionCollection()->addAction(QStringLiteral("quiz_editor"));
0296   quizEditor->setIcon(QIcon::fromTheme(QStringLiteral("editor")));
0297   quizEditor->setText(i18nc("@item:inlistbox activate vocabulary editor", "&Editor"));
0298   actionCollection()->setDefaultShortcut(quizEditor, QKeySequence(Qt::Key_F6));
0299   quizEditor->setWhatsThis(i18n("Activates the vocabulary editor"));
0300   quizEditor->setToolTip(quizEditor->whatsThis());
0301   quizEditor->setStatusTip(quizEditor->whatsThis());
0302   connect(quizEditor, &QAction::triggered, this, &KWordQuizApp::slotQuizEditor);
0303 
0304   quizFlash = actionCollection()->addAction(QStringLiteral("quiz_flash"));
0305   quizFlash->setIcon(QIcon::fromTheme(QStringLiteral("flash")));
0306   quizFlash->setText(i18n("&Flashcard"));
0307   actionCollection()->setDefaultShortcut(quizFlash, QKeySequence(Qt::Key_F7));
0308   quizFlash->setWhatsThis(i18n("Starts a flashcard session using the active vocabulary"));
0309   quizFlash->setToolTip(quizFlash->whatsThis());
0310   quizFlash->setStatusTip(quizFlash->whatsThis());
0311   connect(quizFlash, &QAction::triggered, this, &KWordQuizApp::slotQuizFlash);
0312 
0313   quizMultiple = actionCollection()->addAction(QStringLiteral("quiz_multiple"));
0314   quizMultiple->setIcon(QIcon::fromTheme(QStringLiteral("multiple")));
0315   quizMultiple->setText(i18n("&Multiple Choice"));
0316   actionCollection()->setDefaultShortcut(quizMultiple, QKeySequence(Qt::Key_F8));
0317   quizMultiple->setWhatsThis(i18n("Starts a multiple choice session using the active vocabulary"));
0318   quizMultiple->setToolTip(quizMultiple->whatsThis());
0319   quizMultiple->setStatusTip(quizMultiple->whatsThis());
0320   connect(quizMultiple, &QAction::triggered, this, &KWordQuizApp::slotQuizMultiple);
0321 
0322   quizQA = actionCollection()->addAction(QStringLiteral("quiz_qa"));
0323   quizQA->setIcon(QIcon::fromTheme(QStringLiteral("qa")));
0324   quizQA->setText(i18n("&Question and Answer"));
0325   quizQA->setIconText(i18n("Q&&A"));
0326   actionCollection()->setDefaultShortcut(quizQA, QKeySequence(Qt::Key_F9));
0327   quizQA->setWhatsThis(i18n("Starts a question and answer session using the active vocabulary"));
0328   quizQA->setToolTip(quizQA->whatsThis());
0329   quizQA->setStatusTip(quizQA->whatsThis());
0330   connect(quizQA, &QAction::triggered, this, &KWordQuizApp::slotQuizQA);
0331 
0332   quizCheck = actionCollection()->addAction(QStringLiteral("quiz_check"));
0333   quizCheck->setIcon(QIcon::fromTheme(QStringLiteral("answer-correct")));
0334   quizCheck->setText(i18n("&Check"));
0335   actionCollection()->setDefaultShortcut(quizCheck, QKeySequence(Qt::Key_Return));
0336   quizCheck->setWhatsThis(i18n("Checks your answer to this question"));
0337   quizCheck->setToolTip(quizCheck->whatsThis());
0338   quizCheck->setStatusTip(quizCheck->whatsThis());
0339 
0340   quizOpt1 = actionCollection()->addAction(QStringLiteral("quiz_Opt1"));
0341   quizOpt1->setText(i18n("Choose Option &1"));
0342   actionCollection()->setDefaultShortcut(quizOpt1, QKeySequence(Qt::Key_1));
0343 
0344   quizOpt2 = actionCollection()->addAction(QStringLiteral("quiz_Opt2"));
0345   quizOpt2->setText(i18n("Choose Option &2"));
0346   actionCollection()->setDefaultShortcut(quizOpt2, QKeySequence(Qt::Key_2));
0347 
0348   quizOpt3 = actionCollection()->addAction(QStringLiteral("quiz_Opt3"));
0349   quizOpt3->setText(i18n("Choose Option &3"));
0350   actionCollection()->setDefaultShortcut(quizOpt3, QKeySequence(Qt::Key_3));
0351 
0352   flashKnow = actionCollection()->addAction(QStringLiteral("flash_know"));
0353   flashKnow->setIcon(QIcon::fromTheme(QStringLiteral("know")));
0354   flashKnow->setText(i18n("I &Know"));
0355   actionCollection()->setDefaultShortcut(flashKnow, QKeySequence(Qt::Key_K));
0356   flashKnow->setWhatsThis(i18n("Counts this card as correct and shows the next card"));
0357   flashKnow->setToolTip(flashKnow->whatsThis());
0358   flashKnow->setStatusTip(flashKnow->whatsThis());
0359 
0360   flashDontKnow = actionCollection()->addAction(QStringLiteral("flash_dont_know"));
0361   flashDontKnow->setIcon(QIcon::fromTheme(QStringLiteral("dontknow")));
0362   flashDontKnow->setText(i18n("I &Do Not Know"));
0363   actionCollection()->setDefaultShortcut(flashDontKnow, QKeySequence(Qt::Key_D));
0364   flashDontKnow->setWhatsThis(i18n("Counts this card as incorrect and shows the next card"));
0365   flashDontKnow->setToolTip(flashDontKnow->whatsThis());
0366   flashDontKnow->setStatusTip(flashDontKnow->whatsThis());
0367 
0368   qaHint = actionCollection()->addAction(QStringLiteral("qa_hint"));
0369   qaHint->setIcon(QIcon::fromTheme(QStringLiteral("hint")));
0370   qaHint->setText(i18n("&Hint"));
0371   actionCollection()->setDefaultShortcut(qaHint, QKeySequence(Qt::CTRL | Qt::Key_H));
0372   qaHint->setWhatsThis(i18n("Gets the next correct letter of the answer"));
0373   qaHint->setToolTip(qaHint->whatsThis());
0374   qaHint->setStatusTip(qaHint->whatsThis());
0375 
0376   qaMarkLastCorrect = actionCollection()->addAction(QStringLiteral("qa_mark_last_correct"));
0377   qaMarkLastCorrect->setIcon(QIcon::fromTheme(QStringLiteral("answer-correct")));
0378   qaMarkLastCorrect->setText(i18n("Mark Last Correct"));
0379   actionCollection()->setDefaultShortcut(qaMarkLastCorrect, QKeySequence(Qt::SHIFT | Qt::Key_Return));
0380   qaMarkLastCorrect->setWhatsThis(i18n("Marks last answer as correct"));
0381   qaMarkLastCorrect->setToolTip(qaMarkLastCorrect->whatsThis());
0382   qaMarkLastCorrect->setStatusTip(qaMarkLastCorrect->whatsThis());
0383 
0384   quizRestart = actionCollection()->addAction(QStringLiteral("quiz_restart"));
0385   quizRestart->setIcon(QIcon::fromTheme(QStringLiteral("start-over")));
0386   quizRestart->setText(i18n("&Restart"));
0387   actionCollection()->setDefaultShortcut(quizRestart, QKeySequence(Qt::CTRL | Qt::Key_R));
0388   quizRestart->setWhatsThis(i18n("Restarts the quiz session from the beginning"));
0389   quizRestart->setToolTip(quizRestart->whatsThis());
0390   quizRestart->setStatusTip(quizRestart->whatsThis());
0391 
0392   a = actionCollection()->addAction(QStringLiteral("quiz_audio_play"));
0393   a->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0394   a->setText(i18n("&Play Audio"));
0395   actionCollection()->setDefaultShortcut(a, QKeySequence(Qt::CTRL | Qt::Key_B));
0396   a->setWhatsThis(i18n("Play associated audio"));
0397   a->setToolTip(a->whatsThis());
0398   a->setStatusTip(a->whatsThis());
0399 
0400   quizRepeatErrors = actionCollection()->addAction(QStringLiteral("quiz_repeat_errors"));
0401   quizRepeatErrors->setIcon(QIcon::fromTheme(QStringLiteral("repeat")));
0402   quizRepeatErrors->setText(i18n("Repeat &Errors"));
0403   actionCollection()->setDefaultShortcut(quizRepeatErrors, QKeySequence(Qt::CTRL | Qt::Key_E));
0404   quizRepeatErrors->setWhatsThis(i18n("Repeats all incorrectly answered questions"));
0405   quizRepeatErrors->setToolTip(quizRepeatErrors->whatsThis());
0406   quizRepeatErrors->setStatusTip(quizRepeatErrors->whatsThis());
0407 
0408   quizExportErrors = actionCollection()->addAction(QStringLiteral("quiz_export_errors"));
0409   quizExportErrors->setIcon(KStandardGuiItem::saveAs().icon());
0410   quizExportErrors->setText(i18n("Export Errors &As..."));
0411   quizExportErrors->setWhatsThis(i18n("Exports all errors as a new vocabulary document"));
0412   quizExportErrors->setToolTip(quizExportErrors->whatsThis());
0413   quizExportErrors->setStatusTip(quizExportErrors->whatsThis());
0414   connect(quizExportErrors, &QAction::triggered, this, &KWordQuizApp::slotCreateErrorListDocument);
0415 
0416   configShowSearchBar = actionCollection()->add<KToggleAction>(QStringLiteral("config_show_search"));
0417   configShowSearchBar->setText(i18n("Show Se&arch"));
0418   connect(configShowSearchBar, &QAction::triggered, this, &KWordQuizApp::slotConfigShowSearch);
0419   configShowSearchBar->setWhatsThis(i18n("Toggle display of the search bar"));
0420   configShowSearchBar->setToolTip(configShowSearchBar->whatsThis());
0421   configShowSearchBar->setStatusTip(configShowSearchBar->whatsThis());
0422 
0423   configNotifications = KStandardAction::configureNotifications(this, SLOT(slotConfigureNotifications()), actionCollection());
0424   configNotifications->setWhatsThis(i18n("Configures sound and other notifications for certain events"));
0425   configNotifications->setToolTip(configNotifications->whatsThis());
0426   configNotifications->setStatusTip(configNotifications->whatsThis());
0427 
0428   configApp = KStandardAction::preferences(this, SLOT(slotConfigure()), actionCollection());
0429   configApp->setWhatsThis(i18n("Specifies preferences for the vocabulary editor and quiz sessions"));
0430   configApp->setToolTip(configApp->whatsThis());
0431   configApp->setStatusTip(configApp->whatsThis());
0432 
0433   for (int i = 1; i <=9; ++i) {
0434     a = actionCollection()->addAction(QStringLiteral("char_%1").arg(QString::number(i)));
0435     a->setText(i18n("Special Character <numid>%1</numid>", i));
0436     actionCollection()->setDefaultShortcut(a, QKeySequence(QStringLiteral("Ctrl+%1").arg(QString::number(i))));
0437     connect(a, &QAction::triggered, this, [this, i] () { slotInsertChar(i); });
0438   }
0439 
0440   updateSpecialCharIcons();
0441 
0442   resize( QSize(650, 450).expandedTo(minimumSizeHint()));
0443   setupGUI(ToolBar | Keys | StatusBar | Create);
0444   setAutoSaveSettings();
0445 
0446   configToolbar = actionCollection()->action(QStringLiteral("options_configure_toolbars"));
0447   configToolbar->setWhatsThis(i18n("Toggles display of the toolbars"));
0448   configToolbar->setToolTip(configToolbar->whatsThis());
0449   configToolbar->setStatusTip(configToolbar->whatsThis());
0450 }
0451 
0452 void KWordQuizApp::initStatusBar()
0453 {
0454   m_statusLabel = new QLabel(statusBar());
0455   statusBar()->insertPermanentWidget(0, m_statusLabel);
0456   //statusBar()->setItemFixed(1, 250);
0457 }
0458 
0459 void KWordQuizApp::initDocument()
0460 {
0461   m_doc = new KEduVocDocument(this);
0462   m_doc->setGenerator(QStringLiteral("kwordquiz %1").arg(KWORDQUIZ_VERSION_STRING));
0463   ///@todo New kvtml documents have a locale setting which should contain the language they are written in.
0464   /* it would be nice to support that somehow. get hot new stuff for KHangman and KAnagram already uses this. */
0465   m_doc->appendIdentifier();
0466   m_doc->identifier(0).setName(i18n("Column 1"));
0467   // m_doc->identifier(0).setLocale("en");
0468   m_doc->appendIdentifier();
0469   m_doc->identifier(1).setName(i18n("Column 2"));
0470   // m_doc->identifier(1).setLocale("en");
0471 
0472   for (int i=0; i<20; i++)
0473   {
0474     m_doc->lesson()->appendEntry(new KEduVocExpression());
0475   }
0476 }
0477 
0478 void KWordQuizApp::initModel()
0479 {
0480   m_tableModel = new KWQTableModel(this);
0481   m_tableModel->setDocument(m_doc);
0482   m_tableModel->setHeaderData(0, Qt::Horizontal, QSize(250, 25), Qt::SizeHintRole);
0483   m_tableModel->setHeaderData(1, Qt::Horizontal, QSize(250, 25), Qt::SizeHintRole);
0484   m_sortFilterModel = new KWQSortFilterModel(this);
0485   m_sortFilterModel->setTableModel(m_tableModel);
0486 }
0487 
0488 void KWordQuizApp::initView()
0489 {
0490   m_pageWidget = new KPageWidget(this);
0491   m_pageWidget->setFaceType( KPageView::List );
0492   setCentralWidget(m_pageWidget);
0493   connect(m_pageWidget, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)), this, SLOT(slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*)));
0494 
0495   QVBoxLayout *editorLayout = new QVBoxLayout();
0496   editorLayout->setContentsMargins(0, 0, 0, 0);
0497 
0498   m_searchLine = new FilterProxySearchLine(this);
0499   m_searchLine->setFocusPolicy(Qt::ClickFocus);
0500   m_searchLine->setPlaceholderText(i18n("Enter search terms here"));
0501   m_searchLine->setFilterProxyModel(m_sortFilterModel);
0502 
0503   m_tableView = new KWQTableView(m_undoStack, this);
0504   editorLayout->addWidget(m_searchLine);
0505   editorLayout->addWidget(m_tableView);
0506   m_tableView->setFilterModel(m_sortFilterModel);
0507   m_tableView->setColumnWidth(0, qvariant_cast<QSize>(m_tableModel->headerData(0, Qt::Horizontal, Qt::SizeHintRole)).width());
0508   m_tableView->setColumnWidth(1, qvariant_cast<QSize>(m_tableModel->headerData(1, Qt::Horizontal, Qt::SizeHintRole)).width());
0509   setWindowTitle(m_doc->url().fileName() + QStringLiteral("[*]"));
0510   setWindowModified(false);
0511   m_tableView->setContextMenuPolicy(Qt::CustomContextMenu);
0512   connect(m_tableView, &QWidget::customContextMenuRequested, this, &KWordQuizApp::slotTableContextMenuRequested);
0513   connect(m_tableModel, &QAbstractItemModel::modelReset, m_tableView, &KWQTableView::slotModelReset);
0514 
0515   m_searchLine->setVisible(Prefs::showSearch());
0516   configShowSearchBar->setChecked(Prefs::showSearch());
0517 
0518   m_editorView = new QWidget(this);
0519   m_flashView = new FlashView(this, actionCollection());
0520   m_multipleView = new MultipleView(this, actionCollection());
0521   m_qaView = new QAView(this, actionCollection());
0522 
0523   m_editorPage = m_pageWidget->addPage(m_editorView, i18nc("@item:inlistbox vocabulary editor", "Editor"));
0524   m_editorPage->setIcon(QIcon::fromTheme(QStringLiteral("editor")));
0525   m_editorPage->setHeader(QLatin1String(""));
0526   m_editorPage->widget()->setLayout(editorLayout);
0527 
0528   m_flashPage = m_pageWidget->addPage(m_flashView, i18n("Flashcard"));
0529   m_flashPage->setIcon(QIcon::fromTheme(QStringLiteral("flash")));
0530   m_flashPage->setHeader(QLatin1String(""));
0531 
0532   m_multiplePage = m_pageWidget->addPage(m_multipleView, i18n("Multiple Choice"));
0533   m_multiplePage->setIcon(QIcon::fromTheme(QStringLiteral("multiple")));
0534   m_multiplePage->setHeader(QLatin1String(""));
0535 
0536   m_qaPage = m_pageWidget->addPage(m_qaView, i18n("Question & Answer"));
0537   m_qaPage->setIcon(QIcon::fromTheme(QStringLiteral("qa")));
0538   m_qaPage->setHeader(QLatin1String(""));
0539 
0540   m_pageWidget->addAction(quizEditor);
0541   m_pageWidget->addAction(quizFlash);
0542   m_pageWidget->addAction(quizMultiple);
0543   m_pageWidget->addAction(quizQA);
0544 
0545   const QList<QObject*> listOfChildren = m_pageWidget->children();
0546 
0547   for (QObject *object : listOfChildren) {
0548     if(!object->isWidgetType())
0549       continue;
0550 
0551     QWidget *childWidget = static_cast<QWidget *>(object);
0552 
0553     if (childWidget->inherits("QListView"))
0554       childWidget->setFocusPolicy(Qt::NoFocus);
0555   }
0556 
0557   m_pageWidget->setCurrentPage(m_editorPage);
0558   m_tableView->setFocus();
0559   updateActions();
0560 }
0561 
0562 void KWordQuizApp::openUrl(const QUrl &url)
0563 {
0564   if(!url.isEmpty()) {
0565     if (m_dirWatch->contains(url.path()))
0566     {
0567       KMainWindow* w;
0568       if(!memberList().isEmpty())
0569       {
0570         for (int i = 0; i < memberList().size(); ++i)
0571         {
0572           w = memberList().at(i);
0573           KWordQuizApp *a =(KWordQuizApp *) w;
0574           if(a->document()->url().path() == url.path())
0575           {
0576             if (w->isMinimized()) {
0577               w->setWindowState(w->windowState() & ~Qt::WindowMinimized);
0578             }
0579             w->activateWindow();
0580             w->raise();
0581             break;
0582           }
0583         }
0584       }
0585     }
0586     else
0587     {
0588       if (m_tableModel->isEmpty()){
0589         openDocumentFile(url);
0590       }
0591       else
0592       {
0593         KWordQuizApp *new_window= new KWordQuizApp();
0594         new_window->show();
0595         new_window->openDocumentFile(url);
0596       }
0597     }
0598   }
0599 }
0600 
0601 void KWordQuizApp::openDocumentFile(const QUrl &url, KEduVocDocument::FileHandlingFlags flags)
0602 {
0603   slotStatusMsg(i18n("Opening file..."));
0604   if (!url.isEmpty()) {
0605     m_tableModel->beginResetModel();
0606     int result = m_doc->open(url, flags);
0607     if (result == KEduVocDocument::NoError) {
0608       while (m_doc->identifierCount() < 2) { //if we opened a TAB-less CSV, there
0609         m_doc->appendIdentifier(); //may be 0 or 1 identifiers, we need at least 2
0610       }
0611       m_tableModel->endResetModel();
0612 
0613       m_dirWatch->addFile(url.path());
0614       setWindowTitle(m_doc->url().fileName() + QStringLiteral("[*]"));
0615       setWindowModified(false);
0616       fileOpenRecent->addUrl(url);
0617       slotModeActionGroupTriggered(m_modeActionGroup->checkedAction());
0618       m_undoStack->clear();
0619       switch (Prefs::startSession()) {
0620         case Prefs::EnumStartSession::Flashcard:
0621           slotQuizFlash();
0622           break;
0623         case Prefs::EnumStartSession::MultipleChoice:
0624           slotQuizMultiple();
0625           break;
0626         case Prefs::EnumStartSession::QA:
0627           slotQuizQA();
0628           break;
0629         default:
0630           slotQuizEditor();
0631           break;
0632       }
0633     }
0634     else {
0635       if (result == KEduVocDocument::FileLocked) {
0636 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0637         const KMessageBox::ButtonCode userAnswer = KMessageBox::questionTwoActions(this, i18nc("%1 is an error message", "%1\nIf you are sure no other program is using the file you can choose to ignore the lock and open the file anyway.\nDo you want to open the file?", KEduVocDocument::errorDescription(result)), i18n("Open File"), KStandardGuiItem::open(), KStandardGuiItem::cancel());
0638 #else
0639         const KMessageBox::ButtonCode userAnswer = KMessageBox::questionYesNo(this, i18nc("%1 is an error message", "%1\nIf you are sure no other program is using the file you can choose to ignore the lock and open the file anyway.\nDo you want to open the file?", KEduVocDocument::errorDescription(result)));
0640 #endif
0641 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0642         if (userAnswer == KMessageBox::ButtonCode::PrimaryAction) {
0643 #else
0644         if (userAnswer == KMessageBox::Yes) {
0645 #endif
0646           return openDocumentFile(url, KEduVocDocument::FileIgnoreLock);
0647         }
0648       } else {
0649         KMessageBox::error(this, KEduVocDocument::errorDescription(result));
0650       }
0651       while (m_doc->identifierCount() < 2) { //if we opened a TAB-less CSV, there
0652         m_doc->appendIdentifier(); //may be 0 or 1 identifiers, we need at least 2
0653       }
0654       m_tableModel->endResetModel();
0655     }
0656   }
0657   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0658 }
0659 
0660 KEduVocDocument *KWordQuizApp::document() const
0661 {
0662   return m_doc;
0663 }
0664 
0665 void KWordQuizApp::saveOptions()
0666 {
0667   fileOpenRecent->saveEntries(KSharedConfig::openConfig()->group( "Recent Files") );
0668   Prefs::self()->save();
0669 }
0670 
0671 void KWordQuizApp::readOptions()
0672 {
0673   fileOpenRecent->loadEntries(KSharedConfig::openConfig()->group( "Recent Files") );
0674 }
0675 
0676 void KWordQuizApp::saveProperties(KConfigGroup &_cfg)
0677 {
0678   if(!m_doc->url().fileName().isEmpty() && !m_doc->isModified())
0679   {
0680     // saving to tempfile not necessary
0681   }
0682   else
0683   {
0684     QUrl url = m_doc->url();
0685     _cfg.writeEntry("filename", url.url());
0686     _cfg.writeEntry("modified", m_doc->isModified());
0687     QString tempname = QTemporaryFile().fileName();
0688     QString tempurl = QUrl::toPercentEncoding(tempname);
0689     QUrl _url(tempurl);
0690     m_doc->saveAs(_url, KEduVocDocument::Automatic);
0691   }
0692 }
0693 
0694 void KWordQuizApp::readProperties(const KConfigGroup &_cfg)
0695 {
0696   QString filename = _cfg.readEntry("filename", "");
0697   QUrl url(filename);
0698   bool modified = _cfg.readEntry("modified", false);
0699   if(modified)
0700   {
0701     QTemporaryFile tmpfile(filename);
0702     if (tmpfile.open()) {
0703         QUrl _url(QUrl::fromLocalFile(tmpfile.fileName()));
0704         m_doc->open(_url);
0705         m_doc->setModified();
0706         setWindowTitle(_url.fileName() + QStringLiteral("[*]"));
0707         setWindowModified(true);
0708     }
0709   }
0710   else
0711   {
0712     if(!filename.isEmpty())
0713     {
0714       m_doc->open(url);
0715       setWindowTitle(url.fileName() + QStringLiteral("[*]"));
0716       setWindowModified(false);
0717     }
0718   }
0719 }
0720 
0721 bool KWordQuizApp::queryClose()
0722 {
0723   bool completed=true;
0724   if(m_doc->isModified())
0725   {
0726 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0727     int want_save = KMessageBox::warningTwoActionsCancel(this, i18n("The current document has been modified.\nDo you want to save it?"), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard());
0728 #else
0729     int want_save = KMessageBox::warningYesNoCancel(this, i18n("The current document has been modified.\nDo you want to save it?"), QString(), KStandardGuiItem::save(), KStandardGuiItem::discard());
0730 #endif
0731     switch(want_save)
0732     {
0733       case KMessageBox::Cancel:
0734         completed = false;
0735         break;
0736 
0737 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0738       case KMessageBox::ButtonCode::PrimaryAction:
0739 #else
0740       case KMessageBox::Yes:
0741 #endif
0742         if (m_doc->url().fileName().isEmpty())
0743         {
0744           completed = saveDocAsFileName(m_doc);
0745         }
0746         else
0747         {
0748           completed = (m_doc->saveAs(m_doc->url(), KEduVocDocument::Automatic) == KEduVocDocument::NoError);
0749         }
0750 
0751         break;
0752 
0753 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 100, 0)
0754       case KMessageBox::ButtonCode::SecondaryAction:
0755 #else
0756       case KMessageBox::No:
0757 #endif
0758         completed = true;
0759         break;
0760 
0761       default:
0762         completed = false;
0763         break;
0764     }
0765   }
0766 
0767   if (completed)
0768     if (m_dirWatch->contains(m_doc->url().toLocalFile()))
0769       m_dirWatch->removeFile(m_doc->url().toLocalFile());
0770   saveOptions();
0771   // Close the current document to get rid of its lock file
0772   m_doc->close();
0773   return completed;
0774 }
0775 
0776 
0777 void KWordQuizApp::slotFileNew()
0778 {
0779   slotStatusMsg(i18n("Opening a new document window..."));
0780   if (!m_tableModel->isEmpty()){
0781     KWordQuizApp *new_window= new KWordQuizApp();
0782     new_window->show();
0783   }
0784   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0785 }
0786 
0787 void KWordQuizApp::slotFileOpen()
0788 {
0789   slotStatusMsg(i18n("Opening file..."));
0790 
0791   QCheckBox * cb = new QCheckBox(i18n("&Merge selected files with the current document"), 0);
0792   cb -> setChecked(false);
0793   cb -> setEnabled(false);
0794 
0795   QString filter = KEduVocDocument::pattern(KEduVocDocument::Reading);
0796   QPointer<QFileDialog> fd = new QFileDialog(this, i18n("Open Vocabulary Document"), QString(), filter);
0797   fd->setFileMode(QFileDialog::ExistingFiles);
0798 
0799   if (fd->exec() == QDialog::Accepted)
0800   {
0801     QList<QUrl> l = fd->selectedUrls();
0802     bool append = ((cb->isChecked()) && (l.count() >= 1));
0803 
0804     if (append)
0805     {
0806       QList<QUrl>::iterator it;
0807       for(it = l.begin(); it != l.end(); ++it)
0808       {
0809         KEduVocDocument *new_doc = new KEduVocDocument(this);
0810         new_doc->open(*it);
0811 
0812         m_doc->merge(new_doc, false);
0813         delete (new_doc);
0814       }
0815       //m_tableModel->reset();
0816     }
0817     else
0818     {
0819       QList<QUrl>::iterator it;
0820       for(it = l.begin(); it != l.end(); ++it)
0821       {
0822         openUrl(*it);
0823       }
0824     }
0825   }
0826 
0827   delete (fd); //deletes cb also
0828 
0829   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0830 }
0831 
0832 void KWordQuizApp::slotFileOpenRecent(const QUrl &url)
0833 {
0834   slotStatusMsg(i18n("Opening file..."));
0835   fileOpenRecent->setCurrentItem(-1);
0836   openUrl(url);
0837   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0838 }
0839 
0840 
0841 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0842 void KWordQuizApp::slotFileGHNS(const QList<KNS3::Entry> &entries)
0843 #else
0844 void KWordQuizApp::slotFileGHNS(const QList<KNSCore::Entry> &entries)
0845 #endif
0846 {
0847   QMimeDatabase db;
0848 
0849   // list of changed entries
0850   for(const auto &entry : entries) {
0851     // check mime type and if kvtml, open it
0852     for (const QString &file : entry.installedFiles()) {
0853       const QMimeType mimeType = db.mimeTypeForFile(file);
0854       if (mimeType.name() == QLatin1String("application/x-kvtml")) {
0855         KProcess::startDetached(QStringLiteral("kwordquiz"), QStringList() << file);
0856       }
0857     }
0858   }
0859 }
0860 
0861 
0862 void KWordQuizApp::slotFileSave()
0863 {
0864   bool success = false;
0865   slotStatusMsg(i18n("Saving file..."));
0866   if (m_doc->url().fileName().isEmpty()) {
0867     success = saveDocAsFileName(m_doc);
0868   }
0869   else {
0870     int saveStatus = m_doc->saveAs(m_doc->url(), KEduVocDocument::Automatic);
0871     if (saveStatus == KEduVocDocument::NoError)
0872       success = true;
0873     else
0874       success = saveDocAsFileName(m_doc);
0875   }
0876   if (success)
0877     m_undoStack->setClean(); //emits cleanChanged()
0878   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0879 }
0880 
0881 
0882 void KWordQuizApp::slotFileSaveAs()
0883 {
0884   slotStatusMsg(i18n("Saving file with a new filename..."));
0885   bool success = saveDocAsFileName(m_doc);
0886   if (success) {
0887     m_undoStack->setClean(); //emits cleanChanged()
0888     setWindowTitle(m_doc->url().fileName() + QStringLiteral("[*]")); //if clean to begin with
0889     setWindowModified(m_doc->isModified());
0890   }
0891   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0892 }
0893 
0894 static QString extractSuffixFromQtPattern(const QString &qtPattern)
0895 {
0896   static const QRegularExpression regexp(".*\\((.*)\\)");
0897   const QRegularExpressionMatch match = regexp.match(qtPattern);
0898   if (!match.hasMatch()) {
0899     qWarning() << "extractSuffixesFromQtPattern regexp match failed" << qtPattern;
0900     return { QStringLiteral(".report_bug_please") };
0901   }
0902   return match.captured(1);
0903 }
0904 
0905 bool KWordQuizApp::saveDocAsFileName(KEduVocDocument *document)
0906 {
0907   bool success = false;
0908   int result = KEduVocDocument::Unknown;
0909 
0910   QString filter = KEduVocDocument::pattern(KEduVocDocument::Writing);
0911   filter.append(QStringLiteral(";;"));
0912   filter.append(i18n("HTML Document") + QStringLiteral(" (*.html)"));
0913 
0914   QPointer<QFileDialog> fd = new QFileDialog(this, i18n("Save Vocabulary Document As"), QString(), filter);
0915   fd->setAcceptMode(QFileDialog::AcceptSave);
0916 
0917   if (fd->exec() == QDialog::Accepted) {
0918     QUrl url = fd->selectedUrls().at(0);
0919     if (!url.isEmpty()) {
0920       const QString chosenQtFilter = fd->selectedNameFilter();
0921       const QString chosenFilter = extractSuffixFromQtPattern(chosenQtFilter);
0922       if (!url.fileName().contains(QLatin1Char('.'))) {
0923         qDebug() << "Selected name filter is " << chosenQtFilter << chosenFilter;
0924         if (chosenFilter == QLatin1String("*.csv"))
0925           url.setPath(url.path().append(QStringLiteral(".csv")));
0926         else if (chosenFilter == QLatin1String("*.kvtml"))
0927           url.setPath(url.path().append(QStringLiteral(".kvtml")));
0928         else if (chosenFilter == QLatin1String("*.html"))
0929           url.setPath(url.path().append(QStringLiteral(".html")));
0930       }
0931 
0932       if (chosenFilter == QLatin1String("*.html")) {
0933         if (m_tableView->doHtmlExport(url))
0934           result = KEduVocDocument::NoError;
0935         else
0936           result = KEduVocDocument::FileCannotWrite;
0937         success = false; //export only, do not consider really saved
0938       }
0939       else {
0940         if (m_dirWatch->contains(document->url().toLocalFile()))
0941           m_dirWatch->removeFile(document->url().toLocalFile());
0942         result = document->saveAs(url, KEduVocDocument::Automatic);
0943         if (result == KEduVocDocument::NoError) {
0944           m_dirWatch->addFile(url.path());
0945           fileOpenRecent->addUrl(url);
0946           success = true;
0947         }
0948       }
0949 
0950       if (result != KEduVocDocument::NoError) {
0951         KMessageBox::error(this, KEduVocDocument::errorDescription(result));
0952         success = false;
0953       }
0954     }
0955   }
0956   delete(fd);
0957   return success;
0958 }
0959 
0960 void KWordQuizApp::slotFileClose()
0961 {
0962   slotStatusMsg(i18n("Closing file..."));
0963 
0964   if (memberList().count() > 1)
0965     close();
0966   else
0967     if (queryClose())
0968     {
0969       delete m_doc;
0970       initDocument();
0971       setWindowTitle(m_doc->url().fileName() + "[*]");
0972       setWindowModified(m_doc->isModified());
0973       m_tableModel->setDocument(m_doc);
0974       slotQuizEditor();
0975       slotModeActionGroupTriggered(m_modeActionGroup->actions().at(Prefs::mode() - 1));
0976       m_tableView ->selectionModel()->setCurrentIndex(m_sortFilterModel->index(0, 0), QItemSelectionModel::SelectCurrent);
0977       m_undoStack->clear();
0978     }
0979 
0980   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0981 }
0982 
0983 void KWordQuizApp::slotFilePrint()
0984 {
0985   slotStatusMsg(i18n("Printing..."));
0986   m_tableView->doPrint();
0987   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0988 }
0989 
0990 void KWordQuizApp::slotFilePrintPreview() {
0991   slotStatusMsg(i18n("Showing Preview..."));
0992   m_tableView->doPrintPreview();
0993   slotStatusMsg(i18nc("@info:status ready", "Ready"));
0994 }
0995 
0996 
0997 void KWordQuizApp::slotFileQuit()
0998 {
0999   slotStatusMsg(i18nc("@info:status quitting", "Quitting..."));
1000   saveOptions();
1001   // close the first window, the list makes the next one the first again.
1002   // This ensures that queryClose() is called on each window to ask for closing
1003   KMainWindow* w;
1004   if(!memberList().isEmpty())
1005   {
1006     for (int i = 0; i < memberList().size(); ++i)
1007     {
1008     w = memberList().at(i);
1009       // only close the window if the closeEvent is accepted. If the user presses Cancel on the saveModified() dialog,
1010       // the window and the application stay open.
1011       if(!w->close())
1012         break;
1013       // Close the corresponding document to remove its lock file
1014       static_cast<KWordQuizApp *>(w)->document()->close();
1015     }
1016   }
1017 }
1018 
1019 void KWordQuizApp::slotEditCut()
1020 {
1021   slotStatusMsg(i18n("Cutting selection..."));
1022   m_tableView->doEditCut();
1023   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1024 }
1025 
1026 void KWordQuizApp::slotEditCopy()
1027 {
1028   slotStatusMsg(i18n("Copying selection to clipboard..."));
1029   m_tableView->doEditCopy();
1030   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1031 }
1032 
1033 void KWordQuizApp::slotEditPaste()
1034 {
1035   slotStatusMsg(i18n("Inserting clipboard contents..."));
1036   m_tableView->doEditPaste();
1037   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1038 }
1039 
1040 void KWordQuizApp::slotEditClear()
1041 {
1042   slotStatusMsg(i18n("Clearing the selected cells..."));
1043   m_tableView->doEditClear();
1044   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1045 }
1046 
1047 void KWordQuizApp::slotEditInsert()
1048 {
1049   slotStatusMsg(i18n("Inserting rows..."));
1050   m_tableView->doEditInsert();
1051   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1052 }
1053 
1054 void KWordQuizApp::slotEditDelete()
1055 {
1056   slotStatusMsg(i18n("Deleting selected rows..."));
1057   m_tableView->doEditDelete();
1058   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1059 }
1060 
1061 void KWordQuizApp::slotEditMarkBlank()
1062 {
1063   slotStatusMsg(i18n("Marking selected text as a blank..."));
1064   m_tableView->doEditMarkBlank();
1065   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1066 }
1067 
1068 void KWordQuizApp::slotEditUnmarkBlank()
1069 {
1070   slotStatusMsg(i18n("Removing blank markings..."));
1071   m_tableView->doEditUnmarkBlank();
1072   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1073 }
1074 
1075 void KWordQuizApp::slotVocabLanguages()
1076 {
1077   slotStatusMsg(i18n("Setting the column titles of the vocabulary..."));
1078   DlgLanguage* dlg;
1079   dlg = new DlgLanguage(m_tableModel, this);
1080 
1081   if (dlg->exec() == QDialog::Accepted) {
1082     KWQCommandIdentifiers *kwqc = new KWQCommandIdentifiers(m_tableView, dlg->columnData());
1083     m_undoStack->push(kwqc);
1084   }
1085   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1086 }
1087 
1088 void KWordQuizApp::slotVocabFont()
1089 {
1090   slotStatusMsg(i18n("Setting the font of the vocabulary..."));
1091   QPointer<QFontDialog> dlg = new QFontDialog(this);
1092   dlg->setObjectName(QStringLiteral("dlg_font"));
1093   dlg->setFont(Prefs::editorFont());
1094   if (dlg->exec() == QDialog::Accepted)
1095   {
1096     KWQCommandFont *kwqc = new KWQCommandFont(m_tableView, Prefs::editorFont(), dlg->font());
1097     m_undoStack->push(kwqc);
1098   }
1099   delete dlg;
1100   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1101 }
1102 
1103 
1104 void KWordQuizApp::slotVocabImage()
1105 {
1106   slotStatusMsg(i18n("Linking an image with the current entry..."));
1107   m_tableView->doVocabImage();
1108   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1109 }
1110 
1111 
1112 void KWordQuizApp::slotVocabSound()
1113 {
1114   slotStatusMsg(i18n("Linking a sound with the current entry..."));
1115   m_tableView->doVocabSound();
1116   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1117 }
1118 
1119 
1120 void KWordQuizApp::slotVocabAdjustRows()
1121 {
1122   slotStatusMsg(i18n("Adjusting row heights..."));
1123   foreach(const QModelIndex &index, m_tableView->selectionModel()->selectedIndexes())
1124     m_tableView->adjustRow(index.row());
1125   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1126 }
1127 
1128 
1129 void KWordQuizApp::slotVocabShuffle()
1130 {
1131   slotStatusMsg(i18n("Randomizing the vocabulary..."));
1132   m_tableView->doVocabShuffle();
1133   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1134 }
1135 
1136 void KWordQuizApp::slotModeChange()
1137 {
1138   QString newMode;
1139   slotStatusMsg(i18n("Updating mode..."));
1140   if (Prefs::mode() < 5)
1141     newMode = QStringLiteral("mode_%1").arg(QString::number(Prefs::mode() + 1));
1142   else
1143     newMode = QStringLiteral("mode_1");
1144 
1145   QAction *a = actionCollection()->action(newMode);
1146   a->activate(QAction::Trigger);
1147   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1148 }
1149 
1150 
1151 void KWordQuizApp::slotQuizEditor()
1152 {
1153   slotStatusMsg(i18n("Starting editor session..."));
1154   m_pageWidget->setCurrentPage(m_editorPage);
1155   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1156 }
1157 
1158 void KWordQuizApp::slotQuizFlash()
1159 {
1160   slotStatusMsg(i18n("Starting flashcard session..."));
1161   m_pageWidget->setCurrentPage(m_flashPage);
1162   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1163 }
1164 
1165 void KWordQuizApp::slotQuizMultiple()
1166 {
1167   slotStatusMsg(i18n("Starting multiple choice session..."));
1168   m_pageWidget->setCurrentPage(m_multiplePage);
1169   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1170 }
1171 
1172 void KWordQuizApp::slotQuizQA()
1173 {
1174   slotStatusMsg(i18n("Starting question & answer session..."));
1175   m_pageWidget->setCurrentPage(m_qaPage);
1176   slotStatusMsg(i18nc("@info:status ready", "Ready"));
1177 }
1178 
1179 void KWordQuizApp::slotCurrentPageChanged(KPageWidgetItem *current, KPageWidgetItem *before)
1180 {
1181   Q_UNUSED(before);
1182   delete(m_quiz);
1183   m_quiz = 0;
1184   disconnect(quizCheck, 0, 0, 0);
1185   disconnect(flashKnow, 0, 0, 0);
1186   disconnect(flashDontKnow, 0, 0, 0);
1187   disconnect(quizRestart, 0, 0, 0);
1188   disconnect(actionCollection()->action(QStringLiteral("quiz_audio_play")), 0, 0, 0);
1189   disconnect(quizRepeatErrors, 0, 0, 0);
1190   disconnect(qaHint, 0, 0, 0);
1191   disconnect(qaMarkLastCorrect, 0, 0, 0);
1192 
1193   if (current == m_editorPage) {
1194     m_tableView->setFocus();
1195     m_searchLine->setVisible(Prefs::showSearch());
1196   }
1197 
1198   else  if (current == m_flashPage) {
1199     if (Prefs::enableBlanks()) {
1200       if (!m_tableModel->checkSyntax())
1201         m_pageWidget->setCurrentPage(m_editorPage);
1202     }
1203     m_quiz = new KWQQuizModel(this);
1204     m_quiz->setFilterModel(m_sortFilterModel);
1205     connect(m_quiz, &KWQQuizModel::checkingAnswer, m_tableView, &KWQTableView::slotCheckedAnswer);
1206     m_quiz->setQuizType(Prefs::EnumStartSession::Flashcard);
1207     m_quiz->setQuizMode(Prefs::mode());
1208     if (m_quiz->init())
1209     {
1210       connect(quizCheck, SIGNAL(triggered(bool)), m_flashView, SLOT(slotCheck()));
1211       connect(flashKnow, &QAction::triggered, m_flashView, &FlashView::slotKnow);
1212       connect(flashDontKnow, &QAction::triggered, m_flashView, &FlashView::slotDontKnow);
1213       connect(actionCollection()->action(QStringLiteral("quiz_audio_play")), &QAction::triggered, m_flashView, &KWQQuizView::slotAudioPlay);
1214       connect(quizRestart, &QAction::triggered, m_flashView, &KWQQuizView::slotRestart);
1215       connect(quizRepeatErrors, &QAction::triggered, m_flashView, &KWQQuizView::slotRepeat);
1216       connect(this, SIGNAL(settingsChanged()), m_flashView, SLOT(slotApplySettings()));
1217 
1218       m_flashView->setQuiz(m_quiz);
1219       m_flashView->init();
1220       m_flashView->stackedWidget->setCurrentIndex(0);
1221     }
1222     else
1223     {
1224       delete(m_quiz);
1225       m_quiz = 0;
1226       m_flashView->stackedWidget->setCurrentIndex(1);
1227     }
1228   }
1229 
1230   else if (current == m_multiplePage) {
1231     if (Prefs::enableBlanks()) {
1232       if (!m_tableModel->checkSyntax())
1233         m_pageWidget->setCurrentPage(m_editorPage);
1234     }
1235     m_quiz = new KWQQuizModel(this);
1236     m_quiz->setFilterModel(m_sortFilterModel);
1237     connect(m_quiz, &KWQQuizModel::checkingAnswer, m_tableView, &KWQTableView::slotCheckedAnswer);
1238     m_quiz->setQuizType(Prefs::EnumStartSession::MultipleChoice);
1239     m_quiz->setQuizMode(Prefs::mode());
1240     if (m_quiz->init())
1241     {
1242       connect(quizCheck, SIGNAL(triggered(bool)), m_multipleView, SLOT(slotCheck()));
1243       connect(quizRestart, &QAction::triggered, m_multipleView, &KWQQuizView::slotRestart);
1244       connect(quizRepeatErrors, &QAction::triggered, m_multipleView, &KWQQuizView::slotRepeat);
1245       connect(this, SIGNAL(settingsChanged()), m_multipleView, SLOT(slotApplySettings()));
1246 
1247       m_multipleView->setQuiz(m_quiz);
1248       m_multipleView->init();
1249       m_multipleView->stackedWidget->setCurrentIndex(0);
1250     }
1251     else
1252     {
1253       delete(m_quiz);
1254       m_quiz = 0;
1255       m_multipleView->stackedWidget->setCurrentIndex(1);
1256     }
1257   }
1258 
1259   else if (current == m_qaPage) {
1260     if (Prefs::enableBlanks()) {
1261       if (!m_tableModel->checkSyntax())
1262         m_pageWidget->setCurrentPage(m_editorPage);
1263     }
1264     m_quiz = new KWQQuizModel(this);
1265     m_quiz->setFilterModel(m_sortFilterModel);
1266     connect(m_quiz, &KWQQuizModel::checkingAnswer, m_tableView, &KWQTableView::slotCheckedAnswer);
1267     m_quiz->setQuizType(Prefs::EnumStartSession::QA);
1268     m_quiz->setQuizMode(Prefs::mode());
1269     if (m_quiz->init())
1270     {
1271       connect(quizCheck, SIGNAL(triggered(bool)), m_qaView, SLOT(slotCheck()));
1272       connect(qaHint, &QAction::triggered, m_qaView, &QAView::slotHint);
1273       connect(qaMarkLastCorrect, &QAction::triggered, m_qaView, &QAView::slotMarkLastCorrect);
1274       connect(quizRestart, &QAction::triggered, m_qaView, &KWQQuizView::slotRestart);
1275       connect(actionCollection()->action(QStringLiteral("quiz_audio_play")), &QAction::triggered, m_qaView, &KWQQuizView::slotAudioPlay);
1276       connect(quizRepeatErrors, &QAction::triggered, m_qaView, &KWQQuizView::slotRepeat);
1277       connect(this, SIGNAL(settingsChanged()), m_qaView, SLOT(slotApplySettings()));
1278 
1279       m_qaView->setQuiz(m_quiz);
1280       m_qaView->init();
1281       m_qaView->stackedWidget->setCurrentIndex(0);
1282     }
1283     else
1284     {
1285       delete(m_quiz);
1286       m_quiz = 0;
1287       m_qaView->stackedWidget->setCurrentIndex(1);
1288     }
1289   }
1290 
1291   updateActions();
1292 }
1293 
1294 /** Configure notifications */
1295 void KWordQuizApp::slotConfigureNotifications( )
1296 {
1297   KNotifyConfigWidget::configure(this);
1298 }
1299 
1300 /** Configure kwordquiz */
1301 void KWordQuizApp::slotConfigure()
1302 {
1303   if (KWordQuizPrefs::showDialog(QStringLiteral("settings")))
1304     return;
1305 
1306   //KConfigDialog didn't find an instance of this dialog, so lets create it :
1307   KWordQuizPrefs* dialog = new KWordQuizPrefs(this, QStringLiteral("settings"),  Prefs::self(), actionCollection());
1308   connect(dialog, &KConfigDialog::settingsChanged, this, &KWordQuizApp::slotApplyPreferences);
1309   dialog->show();
1310 }
1311 
1312 void KWordQuizApp::slotApplyPreferences()
1313 {
1314   editMarkBlank->setEnabled(Prefs::enableBlanks());
1315   editUnmarkBlank->setEnabled(Prefs::enableBlanks());
1316   m_tableView->reset();
1317   updateSpecialCharIcons();
1318   Q_EMIT settingsChanged();
1319 }
1320 
1321 void KWordQuizApp::updateSpecialCharIcons()
1322 {
1323   for (int i = 0; i < 9; i++){
1324     QAction * a = actionCollection()->action(QStringLiteral("char_%1").arg(QString::number(i + 1)));
1325     a->setIcon(charIcon(Prefs::specialCharacters()[i]));
1326     a->setIconText(i18n("Insert %1", Prefs::specialCharacters()[i]));
1327     a->setWhatsThis(i18n("Inserts the character %1", Prefs::specialCharacters()[i]));
1328     a->setToolTip(a->whatsThis());
1329     a->setStatusTip(a->whatsThis());
1330   }
1331 }
1332 
1333 QIcon KWordQuizApp::charIcon(const QChar & c)
1334 {
1335   QRect r(4, 4, 120, 120);
1336 
1337   ///A font to draw the character with
1338   QFont font(QStringLiteral("sans"));
1339   font.setPixelSize(100);
1340   font.setWeight(QFont::Bold);
1341 
1342   ///Create the pixmap
1343   QPixmap pm(128, 128);
1344   pm.fill(Qt::white);
1345   QPainter p;
1346   p.begin(&pm);
1347   p.setFont(font);
1348   p.setPen(Qt::blue);
1349   p.drawText(r, Qt::AlignCenter, (QString) c);
1350   p.end();
1351 
1352   ///Create transparency mask
1353   QBitmap bm(128, 128);
1354   bm.fill(Qt::color0);
1355   QPainter b;
1356   b.begin(&bm);
1357   b.setFont(font);
1358   b.setPen(Qt::color1);
1359   b.drawText(r, Qt::AlignCenter, (QString) c);
1360   b.end();
1361 
1362   ///Mask the pixmap
1363   pm.setMask(bm);
1364 
1365   return QIcon(pm);
1366 }
1367 
1368 void KWordQuizApp::slotStatusMsg(const QString &text)
1369 {
1370   //statusBar()->clearMessage();
1371   statusBar()->showMessage(text);
1372 }
1373 
1374 
1375 void KWordQuizApp::slotModeActionGroupTriggered(QAction *act)
1376 {
1377   if (m_quiz != 0)
1378     if (KMessageBox::warningContinueCancel(this, i18n("This will restart your quiz. Do you wish to continue?"), QString(), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("askModeQuiz"))
1379       != KMessageBox::Continue)
1380       {
1381         m_modeActionGroup->actions()[0]->setChecked(Prefs::mode() == 1);
1382         m_modeActionGroup->actions()[1]->setChecked(Prefs::mode() == 2);
1383         m_modeActionGroup->actions()[2]->setChecked(Prefs::mode() == 3);
1384         m_modeActionGroup->actions()[3]->setChecked(Prefs::mode() == 4);
1385         m_modeActionGroup->actions()[4]->setChecked(Prefs::mode() == 5);
1386         return;
1387       }
1388 
1389   act->setChecked(true);
1390 
1391   QString s1 = m_tableModel->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString();
1392   QString s2 = m_tableModel->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString();
1393 
1394   m_modeActionGroup->actions()[0]->setText(i18n("&1 %1 -> %2 In Order", s1, s2));
1395   m_modeActionGroup->actions()[1]->setText(i18n("&2 %1 -> %2 In Order", s2, s1));
1396   m_modeActionGroup->actions()[2]->setText(i18n("&3 %1 -> %2 Randomly", s1, s2));
1397   m_modeActionGroup->actions()[3]->setText(i18n("&4 %1 -> %2 Randomly", s2, s1));
1398   m_modeActionGroup->actions()[4]->setText(i18n("&5 %1 <-> %2 Randomly", s1, s2));
1399 
1400   Prefs::setMode(act->data().toInt());
1401   m_modeActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("mode") + QString::number(Prefs::mode())));
1402 
1403   switch (Prefs::mode()){
1404   case 1:
1405     m_statusLabel->setText(i18n("%1 -> %2 In Order", s1, s2));
1406     break;
1407   case 2:
1408     m_statusLabel->setText(i18n("%1 -> %2 In Order", s2, s1));
1409     break;
1410   case 3:
1411     m_statusLabel->setText(i18n("%1 -> %2 Randomly", s1, s2));
1412     break;
1413   case 4:
1414     m_statusLabel->setText(i18n("%1 -> %2 Randomly", s2, s1));
1415     break;
1416   case 5:
1417     m_statusLabel->setText(i18n("%1 &lt;-&gt; %2 Randomly", s1, s2));
1418     break;
1419   }
1420 
1421   if (m_quiz !=0)
1422     slotCurrentPageChanged(m_pageWidget->currentPage(), m_pageWidget->currentPage());
1423 }
1424 
1425 
1426 void KWordQuizApp::slotInsertChar( int i )
1427 {
1428   if (m_pageWidget->currentPage() == m_qaPage)
1429     m_qaView->slotSpecChar(Prefs::specialCharacters()[i - 1]);
1430   else
1431     if (m_pageWidget->currentPage() == m_editorPage) {
1432       m_tableView->slotSpecChar(Prefs::specialCharacters()[i - 1]);
1433     }
1434 }
1435 
1436 
1437 void KWordQuizApp::updateActions()
1438 {
1439   bool fEdit = (m_pageWidget->currentPage() == m_editorPage);
1440   bool fQuiz = !fEdit && (m_quiz != 0);
1441 
1442   fileSave->setEnabled(fEdit);
1443   fileSaveAs->setEnabled(fEdit);
1444   filePrint->setEnabled(fEdit);
1445   filePrintPreview->setEnabled(fEdit);
1446 
1447   editCopy->setEnabled(fEdit);
1448   editCut->setEnabled(fEdit);
1449   editPaste->setEnabled(fEdit);
1450   editClear->setEnabled(fEdit);
1451   editInsert->setEnabled(fEdit);
1452   editDelete->setEnabled(fEdit);
1453   editMarkBlank->setEnabled(fEdit && Prefs::enableBlanks());
1454   editUnmarkBlank->setEnabled(fEdit && Prefs::enableBlanks());
1455 
1456   vocabLanguages->setEnabled(fEdit);
1457   vocabFont->setEnabled(fEdit);
1458   actionCollection()->action(QStringLiteral("vocab_image"))->setEnabled(fEdit);
1459   actionCollection()->action(QStringLiteral("vocab_sound"))->setEnabled(fEdit);
1460   vocabAdjustRows->setEnabled(fEdit);
1461   vocabShuffle->setEnabled(fEdit);
1462 
1463   quizCheck->setEnabled(fQuiz);
1464   quizRestart->setEnabled(fQuiz);
1465   quizRepeatErrors->setEnabled(false);
1466   quizExportErrors->setEnabled(false);
1467 
1468   flashKnow->setEnabled((m_pageWidget->currentPage() == m_flashPage) && fQuiz);
1469   flashDontKnow->setEnabled((m_pageWidget->currentPage() == m_flashPage) && fQuiz);
1470 
1471   qaHint->setEnabled((m_pageWidget->currentPage() == m_qaPage) && fQuiz);
1472   qaMarkLastCorrect->setVisible((m_pageWidget->currentPage() == m_qaPage) && fQuiz);
1473   actionCollection()->action(QStringLiteral("quiz_audio_play"))->setEnabled(false);
1474 
1475   quizOpt1->setEnabled((m_pageWidget->currentPage() == m_multiplePage) && fQuiz);
1476   quizOpt2->setEnabled((m_pageWidget->currentPage() == m_multiplePage) && fQuiz);
1477   quizOpt3->setEnabled((m_pageWidget->currentPage() == m_multiplePage) && fQuiz);
1478 
1479   configShowSearchBar->setEnabled(fEdit);
1480 
1481   addToolBar(Qt::RightToolBarArea, toolBar(QStringLiteral("quizToolBar")));
1482   toolBar(QStringLiteral("quizToolBar"))->setHidden(fEdit);
1483 }
1484 
1485 
1486 void KWordQuizApp::slotConfigShowSearch()
1487 {
1488   if (m_searchLine) {
1489     m_searchLine->setVisible(m_searchLine->isHidden());
1490     Prefs::setShowSearch(m_searchLine->isVisible());
1491   }
1492 }
1493 
1494 
1495 void KWordQuizApp::slotCleanChanged(bool clean)
1496 {
1497   m_doc->setModified(!clean);
1498   setWindowTitle(m_doc->url().fileName() + "[*]");
1499   setWindowModified(m_doc->isModified());
1500 }
1501 
1502 
1503 void KWordQuizApp::slotUndoTextChanged(const QString & undoText)
1504 {
1505   Q_UNUSED(undoText);
1506   editUndo->setWhatsThis(editUndo->text());
1507   editUndo->setStatusTip(editUndo->text());
1508 
1509   slotModeActionGroupTriggered(m_modeActionGroup->actions().at(Prefs::mode() - 1));
1510 }
1511 
1512 
1513 void KWordQuizApp::slotRedoTextChanged(const QString &redoText)
1514 {
1515   Q_UNUSED(redoText);
1516   editRedo->setWhatsThis(editRedo->text());
1517   editRedo->setStatusTip(editRedo->text());
1518 
1519   slotModeActionGroupTriggered(m_modeActionGroup->actions().at(Prefs::mode() - 1));
1520 }
1521 
1522 void KWordQuizApp::slotCreateErrorListDocument()
1523 {
1524   if (m_quiz) {
1525     KEduVocDocument *errorDoc = new KEduVocDocument(this);
1526     errorDoc->appendIdentifier();
1527     errorDoc->identifier(0).setName(m_tableModel->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString());
1528     errorDoc->appendIdentifier();
1529     errorDoc->identifier(1).setName(m_tableModel->headerData(1, Qt::Horizontal, Qt::DisplayRole).toString());
1530 
1531     const auto errors{m_quiz->errorList()};
1532     for (int item : errors) {
1533       KEduVocExpression *errorExpr = new KEduVocExpression();
1534       errorDoc->lesson()->appendEntry(errorExpr);
1535       errorExpr->setTranslation(0, m_quiz->data(m_quiz->index(item, 0), Qt::DisplayRole).toString());
1536       errorExpr->setTranslation(1, m_quiz->data(m_quiz->index(item, 1), Qt::DisplayRole).toString());
1537     }
1538     saveDocAsFileName(errorDoc);
1539     if (m_dirWatch->contains(errorDoc->url().toLocalFile()))
1540       m_dirWatch->removeFile(errorDoc->url().toLocalFile());
1541   }
1542 }
1543 
1544 void KWordQuizApp::slotTableContextMenuRequested(const QPoint & pos)
1545 {
1546     QMenu *popup = static_cast<QMenu*>(guiFactory()->container(QStringLiteral("editor_popup"),this));
1547     QAction *a;
1548     int column = m_tableView->currentIndex().column();
1549     QString currentLayout = QLatin1String("");
1550     currentLayout = m_tableModel->headerData(column, Qt::Horizontal, KWQTableModel::KeyboardLayoutRole).toString();
1551 
1552     // keyboard layout
1553     // try to talk to kxbk - get a list of keyboard layouts
1554     QDBusInterface kxbk(QStringLiteral("org.kde.keyboard"), QStringLiteral("/Layouts"), QStringLiteral("org.kde.KeyboardLayouts"));
1555     QDBusReply<QStringList> reply = kxbk.call(QStringLiteral("getLayoutsList"));
1556     if (reply.isValid()) {
1557         const QStringList layouts = reply;
1558         //layouts.prepend(QString());
1559         QMenu *m = vocabLayouts->menu();
1560         m->clear();
1561         for (const QString &s : layouts) {
1562             a = m->addAction(s);
1563             a->setData(s);
1564             a->setCheckable(true);
1565             a->setActionGroup(m_layoutActionGroup);
1566             if (s == currentLayout)
1567               a->setChecked(true);
1568         }
1569     } else {
1570         qDebug() << "kxkb dbus error";
1571     }
1572 
1573     popup->exec(m_tableView->viewport()->mapToGlobal(pos));
1574 }
1575 
1576 void KWordQuizApp::slotLayoutActionGroupTriggered(QAction *act)
1577 {
1578     int column = m_tableView->currentIndex().column();
1579     m_tableModel->setHeaderData(column, Qt::Horizontal, act->data().toString(), KWQTableModel::KeyboardLayoutRole);
1580 }