File indexing completed on 2022-09-20 12:32:08

0001 /*
0002     main part of parley
0003     SPDX-FileCopyrightText: 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
0004     SPDX-FileCopyrightText: 2004-2007 Peter Hedlund <peter.hedlund@kdemail.net>
0005     SPDX-FileCopyrightText: 2007-2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
0006     SPDX-FileCopyrightText: 2008 Daniel Laidig <d.laidig@gmx.de>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "parleymainwindow.h"
0011 #include "dashboard/dashboard.h"
0012 #include "editor/editor.h"
0013 #include "parleyactions.h"
0014 #include "practice/configure/configurepracticedialog.h"
0015 #include "practice/guifrontend.h"
0016 #include "practice/practicesummarycomponent.h"
0017 #include "prefs.h"
0018 #include "settings/parleyprefs.h"
0019 #include "statistics/statisticsmainwindow.h"
0020 #include <KActionCollection>
0021 #include <KMessageBox>
0022 #include <KNSWidgets/Action>
0023 #include <KRecentFilesAction>
0024 #include <KToolBar>
0025 #include <KXMLGUIFactory>
0026 #include <QMenuBar>
0027 #include <QTimer>
0028 #include <config-parley.h>
0029 
0030 using namespace Editor;
0031 
0032 ParleyMainWindow *ParleyMainWindow::s_instance = nullptr;
0033 ParleyMainWindow *ParleyMainWindow::instance()
0034 {
0035     return s_instance;
0036 }
0037 
0038 ParleyMainWindow::ParleyMainWindow(const QUrl &filename)
0039     : KXmlGuiWindow(nullptr)
0040     , m_currentComponent(NoComponent)
0041     , m_sessionManager(this)
0042 {
0043     s_instance = this;
0044     m_document = new ParleyDocument(this);
0045 
0046     connect(m_document, &ParleyDocument::documentChanged, this, &ParleyMainWindow::documentUpdated);
0047 
0048     setCentralWidget(new QWidget());
0049     centralWidget()->setLayout(new QHBoxLayout());
0050     centralWidget()->layout()->setContentsMargins(0, 0, 0, 0);
0051 
0052     initActions();
0053 
0054     bool startWithDashboard = false;
0055 
0056     setupGUI(ToolBar | Keys | Create); ///@todo frameworks KXMLGui warnings
0057 
0058     if (!filename.isEmpty()) {
0059         m_document->open(filename);
0060     } else {
0061         bool openLastFile = Prefs::autoOpenLast();
0062         if (openLastFile && m_recentFilesAction->actions().count() > 0
0063             && m_recentFilesAction->action(m_recentFilesAction->actions().count() - 1)->isEnabled()) {
0064             m_recentFilesAction->action(m_recentFilesAction->actions().count() - 1)->trigger();
0065         } else {
0066             startWithDashboard = true;
0067         }
0068     }
0069 
0070     // save position of dock windows etc
0071     setAutoSaveSettings();
0072 
0073     if (startWithDashboard) {
0074         showDashboard();
0075     } else {
0076         showEditor();
0077     }
0078 
0079     connect(this, &ParleyMainWindow::preferencesChanged, this, &ParleyMainWindow::slotApplyPreferences);
0080     menuBar()->show();
0081 }
0082 
0083 ParleyMainWindow::~ParleyMainWindow()
0084 {
0085     guiFactory()->removeClient(m_currentComponentWindow);
0086     centralWidget()->layout()->removeWidget(m_currentComponentWindow);
0087     delete m_currentComponentWindow;
0088 
0089     // Prevent calling this slot with already deleted m_document as it is no longer needed anyway
0090     if (m_document->document()) {
0091         disconnect(m_document->document().get(), &KEduVocDocument::destroyed, this, &ParleyMainWindow::slotUpdateWindowCaption);
0092     }
0093     delete m_document;
0094 }
0095 
0096 void ParleyMainWindow::addRecentFile(const QUrl &url, const QString &name)
0097 {
0098     m_recentFilesAction->addUrl(url, name);
0099     m_recentFilesAction->saveEntries(KSharedConfig::openConfig()->group("Recent Files"));
0100 }
0101 
0102 void ParleyMainWindow::removeRecentFile(const QUrl &url)
0103 {
0104     m_recentFilesAction->removeUrl(url);
0105     m_recentFilesAction->saveEntries(KSharedConfig::openConfig()->group("Recent Files"));
0106 }
0107 
0108 void ParleyMainWindow::documentUpdated(const std::shared_ptr<KEduVocDocument> &doc)
0109 {
0110     if (doc) {
0111         connect(doc.get(), &KEduVocDocument::docModified, this, &ParleyMainWindow::slotUpdateWindowCaption);
0112         connect(doc.get(), &KEduVocDocument::destroyed, this, &ParleyMainWindow::slotUpdateWindowCaption);
0113         slotUpdateWindowCaption();
0114     }
0115 }
0116 
0117 void ParleyMainWindow::updateRecentFilesModel()
0118 {
0119     Q_EMIT recentFilesChanged();
0120 }
0121 
0122 void ParleyMainWindow::saveOptions()
0123 {
0124     Prefs::self()->save();
0125 }
0126 
0127 void ParleyMainWindow::slotUpdateWindowCaption()
0128 {
0129     QString title;
0130     bool modified = false;
0131     if (m_document->document()) {
0132         title = i18nc("Title and a modified status indicator.  [*] is exact and will be shown only when document is modified",
0133                       "%1 [*]",
0134                       m_document->document()->title());
0135         modified = m_document->document()->isModified();
0136         if (title == i18n("Untitled")) {
0137             title = QStringLiteral("[*]");
0138         }
0139     }
0140     setCaption(title, modified);
0141 }
0142 
0143 void ParleyMainWindow::slotGeneralOptions()
0144 {
0145     ParleyPrefs *dialog = new ParleyPrefs(m_document->document().get(), this, QStringLiteral("settings"), Prefs::self());
0146     connect(dialog, &ParleyPrefs::settingsChanged, this, &ParleyMainWindow::preferencesChanged);
0147     dialog->show();
0148 }
0149 
0150 void ParleyMainWindow::slotApplyPreferences()
0151 {
0152     m_document->enableAutoBackup((m_currentComponent != DashboardComponent) && Prefs::autoBackup());
0153 }
0154 
0155 void ParleyMainWindow::slotCloseDocument()
0156 {
0157     if (!queryClose()) {
0158         return;
0159     }
0160     showDashboard();
0161     m_document->close();
0162 }
0163 
0164 void ParleyMainWindow::configurePractice()
0165 {
0166     ConfigurePracticeDialog configurePracticeDialog(m_document->document().get(), this, QStringLiteral("practice settings"), Prefs::self());
0167     configurePracticeDialog.exec();
0168 }
0169 
0170 void ParleyMainWindow::startPractice()
0171 {
0172     if (Prefs::learningLanguage() == Prefs::knownLanguage()) {
0173         KMessageBox::error(this, i18n("You cannot start to practice when the known language is the same as the language to learn."), i18n("Select languages"));
0174         return;
0175     }
0176     qDebug() << "Starting Switch Practice";
0177     switchComponent(PracticeComponent);
0178     qDebug() << "Finished Switch Practice";
0179 }
0180 
0181 void ParleyMainWindow::practiceFinished()
0182 {
0183     switchComponent(m_componentBeforePractice);
0184 }
0185 
0186 bool ParleyMainWindow::queryClose()
0187 {
0188     if (m_document->queryClose()) {
0189         Prefs::self()->save();
0190         return true;
0191     }
0192     return false;
0193 }
0194 
0195 QSize ParleyMainWindow::sizeHint() const
0196 {
0197     return QSize(800, 600).expandedTo(KXmlGuiWindow::minimumSizeHint());
0198 }
0199 
0200 void ParleyMainWindow::slotFileNew()
0201 {
0202     m_document->slotFileNew();
0203     slotUpdateWindowCaption();
0204 }
0205 
0206 void ParleyMainWindow::initActions()
0207 {
0208     ParleyActions::create(ParleyActions::FileNew, this, SLOT(slotFileNew()), actionCollection());
0209     ParleyActions::create(ParleyActions::FileOpen, m_document, SLOT(slotFileOpen()), actionCollection());
0210 
0211     KNSWidgets::Action *pAction = new KNSWidgets::Action(i18n("Download New Vocabularies..."), QStringLiteral("parley.knsrc"), actionCollection());
0212     actionCollection()->addAction(QStringLiteral("file_ghns"), pAction);
0213     QObject::connect(pAction, &KNSWidgets::Action::dialogFinished, m_document, &ParleyDocument::slotGHNS);
0214 
0215     pAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_G));
0216     actionCollection()->setDefaultShortcut(pAction, QKeySequence(Qt::CTRL | Qt::Key_G));
0217     pAction->setToolTip(i18n("Downloads new vocabulary collections"));
0218 
0219     ParleyActions::create(ParleyActions::FileOpenDownloaded, m_document, SLOT(openGHNS()), actionCollection());
0220 
0221     m_recentFilesAction = ParleyActions::createRecentFilesAction(m_document, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
0222     m_recentFilesAction->loadEntries(KSharedConfig::openConfig()->group("Recent Files"));
0223 
0224     ParleyActions::create(ParleyActions::FileSave, m_document, SLOT(save()), actionCollection());
0225     ParleyActions::create(ParleyActions::FileSaveAs, m_document, SLOT(saveAs()), actionCollection());
0226 #ifdef HAVE_LIBXSLT
0227     ParleyActions::create(ParleyActions::FileExport, m_document, SLOT(exportDialog()), actionCollection());
0228 #endif
0229 
0230     ParleyActions::create(ParleyActions::FileProperties, m_document, SLOT(documentProperties()), actionCollection());
0231 
0232     ParleyActions::create(ParleyActions::FileClose, this, SLOT(slotCloseDocument()), actionCollection());
0233     ParleyActions::create(ParleyActions::FileQuit, this, SLOT(close()), actionCollection());
0234     ParleyActions::create(ParleyActions::Preferences, this, SLOT(slotGeneralOptions()), actionCollection());
0235 }
0236 
0237 void ParleyMainWindow::showDashboard()
0238 {
0239     switchComponent(DashboardComponent);
0240 }
0241 
0242 void ParleyMainWindow::showEditor()
0243 {
0244     switchComponent(EditorComponent);
0245 }
0246 
0247 void ParleyMainWindow::showPracticeConfiguration()
0248 {
0249     switchComponent(ConfigurePracticeComponent);
0250 }
0251 
0252 void ParleyMainWindow::showPractice()
0253 {
0254     switchComponent(PracticeComponent);
0255 }
0256 
0257 void ParleyMainWindow::showPracticeSummary()
0258 {
0259     switchComponent(PracticeSummary);
0260 }
0261 
0262 void ParleyMainWindow::switchComponent(Component component)
0263 {
0264     if (component == PracticeComponent) {
0265         StatisticsMainWindow *statisticsWidget = qobject_cast<StatisticsMainWindow *>(m_currentComponentWindow);
0266         if (statisticsWidget) {
0267             statisticsWidget->syncConfig();
0268         }
0269 
0270         // Don't start a practice when there are no words to practice.
0271         // This has to be checked before deleting the old component.
0272         m_sessionManager.setDocument(m_document->document().get());
0273         if (!m_sessionManager.allEntryCount()) {
0274             return;
0275         }
0276     }
0277 
0278     // Remove and delete the old component window if there is one active.
0279     if (m_currentComponentWindow) {
0280         guiFactory()->removeClient(m_currentComponentWindow);
0281         centralWidget()->layout()->removeWidget(m_currentComponentWindow);
0282         m_currentComponentWindow->deleteLater();
0283     }
0284 
0285     switch (component) {
0286     case DashboardComponent: {
0287         Dashboard *dashboard = new Dashboard(this);
0288         m_currentComponentWindow = dashboard;
0289         showDocumentActions(true, false);
0290         // dashboard->updateRecentFilesModel();
0291         break;
0292     }
0293     case ConfigurePracticeComponent: {
0294         StatisticsMainWindow *statisticsWidget = new StatisticsMainWindow(m_document->document(), this);
0295         m_currentComponentWindow = statisticsWidget;
0296         showDocumentActions(true, true);
0297         break;
0298     }
0299     case EditorComponent: {
0300         EditorWindow *editor = new EditorWindow(this);
0301         m_currentComponentWindow = editor;
0302         showDocumentActions(true, true);
0303         editor->updateDocument(m_document->document());
0304         break;
0305     }
0306     case PracticeComponent: {
0307         ///@todo trust the dirty bit
0308         m_document->document()->setModified(true);
0309         Practice::PracticeMainWindow *practiceWindow = new Practice::PracticeMainWindow(&m_sessionManager, this);
0310         connect(practiceWindow, &Practice::PracticeMainWindow::stopPractice, this, &ParleyMainWindow::showPracticeSummary);
0311         m_currentComponentWindow = practiceWindow;
0312         qDebug() << " Practice Slotted up";
0313         showDocumentActions(false, false);
0314         practiceWindow->startPractice();
0315         qDebug() << " Practice Slotted up2";
0316         break;
0317     }
0318     case PracticeSummary: {
0319         Practice::PracticeSummaryComponent *summary = new Practice::PracticeSummaryComponent(&m_sessionManager, this);
0320         m_currentComponentWindow = summary;
0321         showDocumentActions(true, true);
0322         break;
0323     }
0324     default:
0325         break;
0326     }
0327     // qDebug() << "new component" << m_currentComponentWindow;
0328 
0329     guiFactory()->addClient(m_currentComponentWindow);
0330     centralWidget()->layout()->addWidget(m_currentComponentWindow);
0331     m_currentComponentWindow->show();
0332     switch (component) {
0333     case DashboardComponent: {
0334         setVisibleToolbar(QString());
0335         break;
0336     }
0337     case ConfigurePracticeComponent: {
0338         setVisibleToolbar(QStringLiteral("statisticsToolBar"));
0339         break;
0340     }
0341     case EditorComponent: {
0342         setVisibleToolbar(QStringLiteral("editorToolBar"));
0343         break;
0344     }
0345     case PracticeComponent: {
0346         setVisibleToolbar(QStringLiteral("practiceToolBar"));
0347         break;
0348     }
0349     case PracticeSummary: {
0350         setVisibleToolbar(QStringLiteral("practiceSummaryToolBar"));
0351         break;
0352     }
0353     default:
0354         break;
0355     }
0356     m_currentComponent = component;
0357     setupToolbarMenuActions();
0358 }
0359 
0360 void ParleyMainWindow::showDocumentActions(bool open, bool edit)
0361 {
0362     actionCollection()->action(QStringLiteral("file_new"))->setVisible(open);
0363     actionCollection()->action(QStringLiteral("file_open"))->setVisible(open);
0364     actionCollection()->action(QStringLiteral("file_open_recent"))->setVisible(open);
0365     actionCollection()->action(QStringLiteral("file_open_downloaded"))->setVisible(open);
0366 
0367     actionCollection()->action(QStringLiteral("file_save"))->setVisible(edit);
0368     actionCollection()->action(QStringLiteral("file_save_as"))->setVisible(edit);
0369     actionCollection()->action(QStringLiteral("file_close"))->setVisible(edit);
0370 #ifdef HAVE_LIBXSLT
0371     actionCollection()->action("file_export")->setVisible(edit);
0372 #endif
0373     actionCollection()->action(QStringLiteral("file_properties"))->setVisible(edit);
0374     actionCollection()->action(QStringLiteral("file_close"))->setVisible(edit);
0375 }
0376 
0377 void ParleyMainWindow::setVisibleToolbar(const QString &name)
0378 {
0379     const QList<KToolBar *> toolbars = toolBars();
0380     for (KToolBar *toolbar : toolbars) {
0381         if (toolbar && toolbar->objectName() == name) {
0382             toolbar->show();
0383         } else if (toolbar) {
0384             toolbar->hide();
0385         }
0386     }
0387 }
0388 
0389 ParleyDocument *ParleyMainWindow::parleyDocument()
0390 {
0391     return m_document;
0392 }
0393 
0394 ParleyMainWindow::Component ParleyMainWindow::currentComponent()
0395 {
0396     return m_currentComponent;
0397 }