Warning, file /sdk/lokalize/src/lokalizemainwindow.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002   This file is part of Lokalize
0003 
0004   SPDX-FileCopyrightText: 2008-2015 Nick Shaforostoff <shafff@ukr.net>
0005   SPDX-FileCopyrightText: 2018-2019 Simon Depiets <sdepiets@gmail.com>
0006 
0007   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0008 */
0009 
0010 #include "lokalizemainwindow.h"
0011 
0012 #include "lokalize_debug.h"
0013 
0014 #include "actionproxy.h"
0015 #include "editortab.h"
0016 #include "projecttab.h"
0017 #include "tmtab.h"
0018 #include "jobs.h"
0019 #include "filesearchtab.h"
0020 #include "prefs_lokalize.h"
0021 
0022 // #define WEBQUERY_ENABLE
0023 
0024 #include "project.h"
0025 #include "projectmodel.h"
0026 #include "projectlocal.h"
0027 #include "prefs.h"
0028 
0029 #include "tools/widgettextcaptureconfig.h"
0030 
0031 #include "multieditoradaptor.h"
0032 
0033 #include <KLocalizedString>
0034 #include <KMessageBox>
0035 #include <KNotification>
0036 #include <KActionCollection>
0037 #include <KActionCategory>
0038 #include <KStandardAction>
0039 #include <KStandardShortcut>
0040 #include <KRecentFilesAction>
0041 #include <KXMLGUIFactory>
0042 
0043 
0044 #include <QMenu>
0045 #include <QTabBar>
0046 #include <QActionGroup>
0047 #include <QMdiSubWindow>
0048 #include <QMdiArea>
0049 #include <QMenuBar>
0050 #include <QStatusBar>
0051 #include <QLabel>
0052 #include <QIcon>
0053 #include <QApplication>
0054 #include <QElapsedTimer>
0055 
0056 
0057 LokalizeMainWindow::LokalizeMainWindow()
0058     : KXmlGuiWindow()
0059     , m_mdiArea(new LokalizeMdiArea)
0060     , m_prevSubWindow(nullptr)
0061     , m_projectSubWindow(nullptr)
0062     , m_translationMemorySubWindow(nullptr)
0063     , m_editorActions(new QActionGroup(this))
0064     , m_managerActions(new QActionGroup(this))
0065     , m_spareEditor(new EditorTab(this, false))
0066     , m_multiEditorAdaptor(new MultiEditorAdaptor(m_spareEditor))
0067 {
0068     m_spareEditor->hide();
0069     m_mdiArea->setViewMode(QMdiArea::TabbedView);
0070     m_mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
0071     m_mdiArea->setDocumentMode(true);
0072     m_mdiArea->setTabsMovable(true);
0073     m_mdiArea->setTabsClosable(true);
0074 
0075     setCentralWidget(m_mdiArea);
0076     connect(m_mdiArea, &QMdiArea::subWindowActivated, this, &LokalizeMainWindow::slotSubWindowActivated);
0077     setupActions();
0078 
0079     //prevent relayout of dockwidgets
0080     m_mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation, true);
0081 
0082     connect(Project::instance(), QOverload<const QString &, const bool>::of(&Project::fileOpenRequested), this, QOverload<QString, const bool>::of(&LokalizeMainWindow::fileOpen_), Qt::QueuedConnection);
0083     connect(Project::instance(), &Project::configChanged, this, &LokalizeMainWindow::projectSettingsChanged);
0084     connect(Project::instance(), &Project::closed, this, &LokalizeMainWindow::closeProject);
0085     showProjectOverview();
0086     showTranslationMemory(); //fix for #342558
0087 
0088     for (int i = ID_STATUS_CURRENT; i <= ID_STATUS_ISFUZZY; i++) {
0089         m_statusBarLabels.append(new QLabel());
0090         statusBar()->insertWidget(i, m_statusBarLabels.last(), 2);
0091     }
0092 
0093     setAttribute(Qt::WA_DeleteOnClose, true);
0094 
0095 
0096     if (!qApp->isSessionRestored()) {
0097         KConfig config;
0098         KConfigGroup stateGroup(&config, "State");
0099         readProperties(stateGroup);
0100     }
0101 
0102     registerDBusAdaptor();
0103 
0104     QTimer::singleShot(0, this, &LokalizeMainWindow::initLater);
0105 }
0106 
0107 void LokalizeMainWindow::initLater()
0108 {
0109     if (!m_prevSubWindow && m_projectSubWindow)
0110         slotSubWindowActivated(m_projectSubWindow);
0111 
0112     if (!Project::instance()->isTmSupported()) {
0113         KNotification* notification = new KNotification("NoSqlModulesAvailable");
0114         notification->setWidget(this);
0115         notification->setText(i18nc("@info", "No Qt Sql modules were found. Translation memory will not work."));
0116         notification->sendEvent();
0117     }
0118 }
0119 
0120 LokalizeMainWindow::~LokalizeMainWindow()
0121 {
0122     TM::cancelAllJobs();
0123 
0124     KConfig config;
0125     KConfigGroup stateGroup(&config, "State");
0126     if (!m_lastEditorState.isEmpty()) {
0127         stateGroup.writeEntry("DefaultDockWidgets", m_lastEditorState);
0128     }
0129     saveProjectState(stateGroup);
0130     m_multiEditorAdaptor->deleteLater();
0131 
0132     //Disconnect the signals pointing to this MainWindow object
0133     QMdiSubWindow* sw;
0134     for (int i = 0; i < m_fileToEditor.values().count(); i++) {
0135         sw = m_fileToEditor.values().at(i);
0136         disconnect(sw, &QMdiSubWindow::destroyed, this, &LokalizeMainWindow::editorClosed);
0137         EditorTab* w = static_cast<EditorTab*>(sw->widget());
0138         disconnect(w, &EditorTab::aboutToBeClosed, this, &LokalizeMainWindow::resetMultiEditorAdaptor);
0139         disconnect(w, QOverload<const QString &, const QString &, const QString &, const bool>::of(&EditorTab::fileOpenRequested), this, QOverload<const QString &, const QString &, const QString &, const bool>::of(&LokalizeMainWindow::fileOpen));
0140         disconnect(w, QOverload<const QString &, const QString &>::of(&EditorTab::tmLookupRequested), this, QOverload<const QString &, const QString &>::of(&LokalizeMainWindow::lookupInTranslationMemory));
0141     }
0142 
0143     qCWarning(LOKALIZE_LOG) << "MainWindow destroyed";
0144 }
0145 
0146 void LokalizeMainWindow::slotSubWindowActivated(QMdiSubWindow* w)
0147 {
0148     //QTime aaa;aaa.start();
0149     if (!w || m_prevSubWindow == w)
0150         return;
0151 
0152     w->setUpdatesEnabled(true);  //QTBUG-23289
0153 
0154     if (m_prevSubWindow) {
0155         m_prevSubWindow->setUpdatesEnabled(false);
0156         LokalizeSubwindowBase* prevEditor = static_cast<LokalizeSubwindowBase2*>(m_prevSubWindow->widget());
0157         prevEditor->hideDocks();
0158         guiFactory()->removeClient(prevEditor->guiClient());
0159         prevEditor->statusBarItems.unregisterStatusBar();
0160 
0161         if (qobject_cast<EditorTab*>(prevEditor)) {
0162             EditorTab* w = static_cast<EditorTab*>(prevEditor);
0163             EditorState state = w->state();
0164             m_lastEditorState = state.dockWidgets.toBase64();
0165         }
0166     }
0167     LokalizeSubwindowBase* editor = static_cast<LokalizeSubwindowBase2*>(w->widget());
0168 
0169     editor->reloadUpdatedXML();
0170     if (qobject_cast<EditorTab*>(editor)) {
0171         EditorTab* w = static_cast<EditorTab*>(editor);
0172         w->setProperFocus();
0173 
0174         EditorState state = w->state();
0175         m_lastEditorState = state.dockWidgets.toBase64();
0176 
0177         m_multiEditorAdaptor->setEditorTab(w);
0178 
0179         QTabBar* tw = m_mdiArea->findChild<QTabBar*>();
0180         if (tw) tw->setTabToolTip(tw->currentIndex(), w->currentFilePath());
0181 
0182         Q_EMIT editorActivated();
0183     } else if (w == m_projectSubWindow && m_projectSubWindow) {
0184         QTabBar* tw = m_mdiArea->findChild<QTabBar*>();
0185         if (tw) tw->setTabToolTip(tw->currentIndex(), Project::instance()->path());
0186     }
0187 
0188     editor->showDocks();
0189     editor->statusBarItems.registerStatusBar(statusBar(), m_statusBarLabels);
0190     guiFactory()->addClient(editor->guiClient());
0191 
0192     m_prevSubWindow = w;
0193 
0194     //qCWarning(LOKALIZE_LOG)<<"finished"<<aaa.elapsed();
0195 }
0196 
0197 
0198 bool LokalizeMainWindow::queryClose()
0199 {
0200     QList<QMdiSubWindow*> editors = m_mdiArea->subWindowList();
0201     int i = editors.size();
0202     while (--i >= 0) {
0203         //if (editors.at(i)==m_projectSubWindow)
0204         if (!qobject_cast<EditorTab*>(editors.at(i)->widget()))
0205             continue;
0206         if (!static_cast<EditorTab*>(editors.at(i)->widget())->queryClose())
0207             return false;
0208     }
0209 
0210     bool ok = Project::instance()->queryCloseForAuxiliaryWindows();
0211 
0212     if (ok) {
0213         QThreadPool::globalInstance()->clear();
0214         Project::instance()->model()->threadPool()->clear();
0215     }
0216     return ok;
0217 }
0218 EditorTab* LokalizeMainWindow::fileOpen_(QString filePath, const bool setAsActive)
0219 {
0220     return fileOpen(filePath, 0, setAsActive);
0221 }
0222 EditorTab* LokalizeMainWindow::fileOpen(QString filePath, int entry, bool setAsActive, const QString& mergeFile, bool silent)
0223 {
0224     if (filePath.length()) {
0225         FileToEditor::const_iterator it = m_fileToEditor.constFind(filePath);
0226         if (it != m_fileToEditor.constEnd()) {
0227             qCWarning(LOKALIZE_LOG) << "already opened:" << filePath;
0228             if (QMdiSubWindow* sw = it.value()) {
0229                 m_mdiArea->setActiveSubWindow(sw);
0230                 return static_cast<EditorTab*>(sw->widget());
0231             }
0232         }
0233     }
0234 
0235     QByteArray state = m_lastEditorState;
0236     EditorTab* w = new EditorTab(this);
0237 
0238     QMdiSubWindow* sw = nullptr;
0239     //create QMdiSubWindow BEFORE fileOpen() because it causes some strange QMdiArea behaviour otherwise
0240     if (filePath.length())
0241         sw = m_mdiArea->addSubWindow(w);
0242 
0243     QString suggestedDirPath;
0244     QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow();
0245     if (activeSW && qobject_cast<LokalizeSubwindowBase*>(activeSW->widget())) {
0246         QString fp = static_cast<LokalizeSubwindowBase*>(activeSW->widget())->currentFilePath();
0247         if (fp.length()) suggestedDirPath = QFileInfo(fp).absolutePath();
0248     }
0249 
0250     if (!w->fileOpen(filePath, suggestedDirPath, m_fileToEditor, silent)) {
0251         if (sw) {
0252             m_mdiArea->removeSubWindow(sw);
0253             sw->deleteLater();
0254         }
0255         w->deleteLater();
0256         return nullptr;
0257     }
0258     filePath = w->currentFilePath();
0259     m_openRecentFileAction->addUrl(QUrl::fromLocalFile(filePath));//(w->currentUrl());
0260 
0261     if (!sw)
0262         sw = m_mdiArea->addSubWindow(w);
0263     w->showMaximized();
0264     sw->showMaximized();
0265 
0266     if (!state.isEmpty()) {
0267         w->restoreState(QByteArray::fromBase64(state));
0268         m_lastEditorState = state;
0269     } else {
0270         //Dummy restore to "initialize" widgets
0271         w->restoreState(w->saveState());
0272     }
0273 
0274     if (entry/* || offset*/)
0275         w->gotoEntry(DocPosition(entry/*, DocPosition::Target, 0, offset*/));
0276     if (setAsActive) {
0277         m_toBeActiveSubWindow = sw;
0278         QTimer::singleShot(0, this, &LokalizeMainWindow::applyToBeActiveSubWindow);
0279     } else {
0280         m_mdiArea->setActiveSubWindow(activeSW);
0281         sw->setUpdatesEnabled(false); //QTBUG-23289
0282     }
0283 
0284     if (!mergeFile.isEmpty())
0285         w->mergeOpen(mergeFile);
0286 
0287     connect(sw, &QMdiSubWindow::destroyed, this, &LokalizeMainWindow::editorClosed);
0288     connect(w, &EditorTab::aboutToBeClosed, this, &LokalizeMainWindow::resetMultiEditorAdaptor);
0289     connect(w, QOverload<const QString &, const QString &, const QString &, const bool>::of(&EditorTab::fileOpenRequested), this, QOverload<const QString &, const QString &, const QString &, const bool>::of(&LokalizeMainWindow::fileOpen));
0290     connect(w, QOverload<const QString &, const QString &>::of(&EditorTab::tmLookupRequested), this, QOverload<const QString &, const QString &>::of(&LokalizeMainWindow::lookupInTranslationMemory));
0291 
0292     QStringRef fnSlashed = filePath.midRef(filePath.lastIndexOf('/'));
0293     FileToEditor::const_iterator i = m_fileToEditor.constBegin();
0294     while (i != m_fileToEditor.constEnd()) {
0295         if (i.key().endsWith(fnSlashed)) {
0296             static_cast<EditorTab*>(i.value()->widget())->setFullPathShown(true);
0297             w->setFullPathShown(true);
0298         }
0299         ++i;
0300     }
0301     m_fileToEditor.insert(filePath, sw);
0302 
0303     sw->setAttribute(Qt::WA_DeleteOnClose, true);
0304     Q_EMIT editorAdded();
0305     return w;
0306 }
0307 
0308 void LokalizeMainWindow::resetMultiEditorAdaptor()
0309 {
0310     m_multiEditorAdaptor->setEditorTab(m_spareEditor); //it will be reparented shortly if there are other editors
0311 }
0312 
0313 void LokalizeMainWindow::editorClosed(QObject* obj)
0314 {
0315     m_fileToEditor.remove(m_fileToEditor.key(static_cast<QMdiSubWindow*>(obj)));
0316 }
0317 
0318 EditorTab* LokalizeMainWindow::fileOpen(const QString& filePath, const QString& source, const QString& ctxt, const bool setAsActive)
0319 {
0320     EditorTab* w = fileOpen(filePath, 0, setAsActive);
0321     if (!w)
0322         return nullptr;//TODO message
0323     w->findEntryBySourceContext(source, ctxt);
0324     return w;
0325 }
0326 EditorTab* LokalizeMainWindow::fileOpen(const QString& filePath, DocPosition docPos, int selection, const bool setAsActive)
0327 {
0328     EditorTab* w = fileOpen(filePath, 0, setAsActive);
0329     if (!w)
0330         return nullptr;//TODO message
0331     w->gotoEntry(docPos, selection);
0332     return w;
0333 }
0334 
0335 QObject* LokalizeMainWindow::projectOverview()
0336 {
0337     if (!m_projectSubWindow) {
0338         ProjectTab* w = new ProjectTab(this);
0339         m_projectSubWindow = m_mdiArea->addSubWindow(w);
0340         w->showMaximized();
0341         m_projectSubWindow->showMaximized();
0342         connect(w, QOverload<const QString &, const bool>::of(&ProjectTab::fileOpenRequested), this, QOverload<QString, const bool>::of(&LokalizeMainWindow::fileOpen_));
0343         connect(w, QOverload<QString>::of(&ProjectTab::projectOpenRequested), this, QOverload<QString>::of(&LokalizeMainWindow::openProject));
0344         connect(w, QOverload<>::of(&ProjectTab::projectOpenRequested), this, QOverload<>::of(&LokalizeMainWindow::openProject));
0345         connect(w, QOverload<const QStringList &>::of(&ProjectTab::searchRequested), this, QOverload<const QStringList &>::of(&LokalizeMainWindow::addFilesToSearch));
0346     }
0347     if (m_mdiArea->currentSubWindow() == m_projectSubWindow)
0348         return m_projectSubWindow->widget();
0349     return nullptr;
0350 }
0351 
0352 void LokalizeMainWindow::showProjectOverview()
0353 {
0354     projectOverview();
0355     m_mdiArea->setActiveSubWindow(m_projectSubWindow);
0356 }
0357 
0358 TM::TMTab* LokalizeMainWindow::showTM()
0359 {
0360     if (!Project::instance()->isTmSupported()) {
0361         KMessageBox::information(nullptr, i18n("TM facility requires SQLite Qt module."), i18n("No SQLite module available"));
0362         return nullptr;
0363     }
0364 
0365     if (!m_translationMemorySubWindow) {
0366         TM::TMTab* w = new TM::TMTab(this);
0367         m_translationMemorySubWindow = m_mdiArea->addSubWindow(w);
0368         w->showMaximized();
0369         m_translationMemorySubWindow->showMaximized();
0370         connect(w, QOverload<const QString &, const QString &, const QString &, const bool>::of(&TM::TMTab::fileOpenRequested), this, QOverload<const QString &, const QString &, const QString &, const bool>::of(&LokalizeMainWindow::fileOpen));
0371     }
0372 
0373     m_mdiArea->setActiveSubWindow(m_translationMemorySubWindow);
0374     return static_cast<TM::TMTab*>(m_translationMemorySubWindow->widget());
0375 }
0376 
0377 FileSearchTab* LokalizeMainWindow::showFileSearch(bool activate)
0378 {
0379     EditorTab* precedingEditor = qobject_cast<EditorTab*>(activeEditor());
0380 
0381     if (!m_fileSearchSubWindow) {
0382         FileSearchTab* w = new FileSearchTab(this);
0383         m_fileSearchSubWindow = m_mdiArea->addSubWindow(w);
0384         w->showMaximized();
0385         m_fileSearchSubWindow->showMaximized();
0386         connect(w, QOverload<const QString &, DocPosition, int, const bool>::of(&FileSearchTab::fileOpenRequested), this, QOverload<const QString &, DocPosition, int, const bool>::of(&LokalizeMainWindow::fileOpen));
0387         connect(w, QOverload<const QString &, const bool>::of(&FileSearchTab::fileOpenRequested), this, QOverload<QString, const bool>::of(&LokalizeMainWindow::fileOpen_));
0388     }
0389 
0390     if (activate) {
0391         m_mdiArea->setActiveSubWindow(m_fileSearchSubWindow);
0392         if (precedingEditor) {
0393             if (precedingEditor->selectionInSource().length())
0394                 static_cast<FileSearchTab*>(m_fileSearchSubWindow->widget())->setSourceQuery(precedingEditor->selectionInSource());
0395             if (precedingEditor->selectionInTarget().length())
0396                 static_cast<FileSearchTab*>(m_fileSearchSubWindow->widget())->setTargetQuery(precedingEditor->selectionInTarget());
0397         }
0398     }
0399     return static_cast<FileSearchTab*>(m_fileSearchSubWindow->widget());
0400 }
0401 
0402 void LokalizeMainWindow::fileSearchNext()
0403 {
0404     FileSearchTab* w = showFileSearch(/*activate=*/false);
0405     //TODO fill search params based on current selection
0406     w->fileSearchNext();
0407 }
0408 
0409 void LokalizeMainWindow::addFilesToSearch(const QStringList& files)
0410 {
0411     FileSearchTab* w = showFileSearch();
0412     w->addFilesToSearch(files);
0413 }
0414 
0415 
0416 void LokalizeMainWindow::applyToBeActiveSubWindow()
0417 {
0418     m_mdiArea->setActiveSubWindow(m_toBeActiveSubWindow);
0419 }
0420 
0421 
0422 void LokalizeMainWindow::setupActions()
0423 {
0424     //all operations that can be done after initial setup
0425     //(via QTimer::singleShot) go to initLater()
0426 
0427 //     QElapsedTimer aaa;
0428 //     aaa.start();
0429 
0430     setStandardToolBarMenuEnabled(true);
0431 
0432     QAction *action;
0433     KActionCollection* ac = actionCollection();
0434     KActionCategory* actionCategory;
0435     KActionCategory* file = new KActionCategory(i18nc("@title actions category", "File"), ac);
0436     //KActionCategory* config=new KActionCategory(i18nc("@title actions category","Settings"), ac);
0437     KActionCategory* glossary = new KActionCategory(i18nc("@title actions category", "Glossary"), ac);
0438     KActionCategory* tm = new KActionCategory(i18nc("@title actions category", "Translation Memory"), ac);
0439     KActionCategory* proj = new KActionCategory(i18nc("@title actions category", "Project"), ac);
0440 
0441     actionCategory = file;
0442 
0443 // File
0444     //KStandardAction::open(this, SLOT(fileOpen()), ac);
0445     file->addAction(KStandardAction::Open, this, SLOT(fileOpen()));
0446     m_openRecentFileAction = KStandardAction::openRecent(this, SLOT(fileOpen(QUrl)), ac);
0447 
0448     file->addAction(KStandardAction::Quit, qApp, SLOT(closeAllWindows()));
0449 
0450 
0451 //Settings
0452     SettingsController* sc = SettingsController::instance();
0453     KStandardAction::preferences(sc, &SettingsController::showSettingsDialog, ac);
0454 
0455 #define ADD_ACTION_SHORTCUT(_name,_text,_shortcut)\
0456     action = actionCategory->addAction(QStringLiteral(_name));\
0457     ac->setDefaultShortcut(action, QKeySequence( _shortcut ));\
0458     action->setText(_text);
0459 
0460 
0461 //Window
0462     //documentBack
0463     //KStandardAction::close(m_mdiArea, SLOT(closeActiveSubWindow()), ac);
0464 
0465     actionCategory = file;
0466     ADD_ACTION_SHORTCUT("next-tab", i18n("Next tab"), Qt::CTRL + Qt::Key_Tab)
0467     connect(action, &QAction::triggered, m_mdiArea, &LokalizeMdiArea::activateNextSubWindow);
0468 
0469     ADD_ACTION_SHORTCUT("prev-tab", i18n("Previous tab"), Qt::CTRL + Qt::SHIFT + Qt::Key_Tab)
0470     connect(action, &QAction::triggered, m_mdiArea, &LokalizeMdiArea::activatePreviousSubWindow);
0471 
0472     ADD_ACTION_SHORTCUT("prev-active-tab", i18n("Previously active tab"), Qt::CTRL + Qt::Key_BracketLeft)
0473     connect(action, &QAction::triggered, m_mdiArea, &QMdiArea::activatePreviousSubWindow);
0474 
0475 //Tools
0476     actionCategory = glossary;
0477     Project* project = Project::instance();
0478     ADD_ACTION_SHORTCUT("tools_glossary", i18nc("@action:inmenu", "Glossary"), Qt::CTRL + Qt::ALT + Qt::Key_G)
0479     connect(action, &QAction::triggered, project, &Project::showGlossary);
0480 
0481     actionCategory = tm;
0482     ADD_ACTION_SHORTCUT("tools_tm", i18nc("@action:inmenu", "Translation memory"), Qt::Key_F7)
0483     connect(action, &QAction::triggered, this, &LokalizeMainWindow::showTM);
0484 
0485     action = tm->addAction("tools_tm_manage", project, SLOT(showTMManager()));
0486     action->setText(i18nc("@action:inmenu", "Manage translation memories"));
0487 
0488 //Project
0489     actionCategory = proj;
0490     ADD_ACTION_SHORTCUT("project_overview", i18nc("@action:inmenu", "Project overview"), Qt::Key_F4)
0491     connect(action, &QAction::triggered, this, &LokalizeMainWindow::showProjectOverview);
0492 
0493     action = proj->addAction(QStringLiteral("project_configure"), sc, SLOT(projectConfigure()));
0494     action->setText(i18nc("@action:inmenu", "Configure project..."));
0495 
0496     action = proj->addAction(QStringLiteral("project_create"), sc, SLOT(projectCreate()));
0497     action->setText(i18nc("@action:inmenu", "Create software translation project..."));
0498 
0499     action = proj->addAction(QStringLiteral("project_create_odf"), Project::instance(), SLOT(projectOdfCreate()));
0500     action->setText(i18nc("@action:inmenu", "Create OpenDocument translation project..."));
0501 
0502     action = proj->addAction(QStringLiteral("project_open"), this, SLOT(openProject()));
0503     action->setText(i18nc("@action:inmenu", "Open project..."));
0504     action->setIcon(QIcon::fromTheme("project-open"));
0505 
0506     action = proj->addAction(QStringLiteral("project_close"), this, SLOT(closeProject()));
0507     action->setText(i18nc("@action:inmenu", "Close project"));
0508     action->setIcon(QIcon::fromTheme("project-close"));
0509 
0510     m_openRecentProjectAction = new KRecentFilesAction(i18nc("@action:inmenu", "Open recent project"), this);
0511     action = proj->addAction(QStringLiteral("project_open_recent"), m_openRecentProjectAction);
0512     connect(m_openRecentProjectAction, QOverload<const QUrl &>::of(&KRecentFilesAction::urlSelected), this, QOverload<const QUrl &>::of(&LokalizeMainWindow::openProject));
0513 
0514     //Qt::QueuedConnection: defer until event loop is running to eliminate QWidgetPrivate::showChildren(bool) startup crash
0515     connect(Project::instance(), &Project::loaded, this, &LokalizeMainWindow::projectLoaded, Qt::QueuedConnection);
0516 
0517 
0518     ADD_ACTION_SHORTCUT("tools_filesearch", i18nc("@action:inmenu", "Search and replace in files"), Qt::Key_F6)
0519     connect(action, &QAction::triggered, this, &LokalizeMainWindow::showFileSearch);
0520 
0521     ADD_ACTION_SHORTCUT("tools_filesearch_next", i18nc("@action:inmenu", "Find next in files"), Qt::META + Qt::Key_F3)
0522     connect(action, &QAction::triggered, this, &LokalizeMainWindow::fileSearchNext);
0523 
0524     action = ac->addAction(QStringLiteral("tools_widgettextcapture"), this, SLOT(widgetTextCapture()));
0525     action->setText(i18nc("@action:inmenu", "Widget text capture"));
0526 
0527     setupGUI(Default, QStringLiteral("lokalizemainwindowui.rc"));
0528 
0529     //qCDebug(LOKALIZE_LOG)<<"action setup finished"<<aaa.elapsed();
0530 }
0531 
0532 bool LokalizeMainWindow::closeProject()
0533 {
0534     if (!queryClose())
0535         return false;
0536 
0537     KConfigGroup emptyGroup; //don't save which project to reopen
0538     saveProjectState(emptyGroup);
0539     //close files from previous project
0540     const auto subwindows = m_mdiArea->subWindowList();
0541     for (QMdiSubWindow* subwindow : subwindows) {
0542         if (subwindow == m_translationMemorySubWindow && m_translationMemorySubWindow)
0543             subwindow->deleteLater();
0544         else if (qobject_cast<EditorTab*>(subwindow->widget())) {
0545             m_fileToEditor.remove(static_cast<EditorTab*>(subwindow->widget())->currentFilePath());//safety
0546             m_mdiArea->removeSubWindow(subwindow);
0547             subwindow->deleteLater();
0548         } else if (subwindow == m_projectSubWindow && m_projectSubWindow)
0549             static_cast<ProjectTab*>(m_projectSubWindow->widget())->showWelcomeScreen();
0550     }
0551     Project::instance()->load(QString());
0552     //TODO scripts
0553     return true;
0554 }
0555 
0556 void LokalizeMainWindow::openProject(QString path)
0557 {
0558     path = SettingsController::instance()->projectOpen(path, false); //dry run
0559 
0560     if (path.isEmpty())
0561         return;
0562 
0563     if (closeProject())
0564         SettingsController::instance()->projectOpen(path, true);//really open
0565 }
0566 
0567 void LokalizeMainWindow::saveProperties(KConfigGroup& stateGroup)
0568 {
0569     saveProjectState(stateGroup);
0570 }
0571 
0572 void LokalizeMainWindow::saveProjectState(KConfigGroup& stateGroup)
0573 {
0574     QList<QMdiSubWindow*> editors = m_mdiArea->subWindowList();
0575 
0576     QStringList files;
0577     QStringList mergeFiles;
0578     QList<QByteArray> dockWidgets;
0579     //QList<int> offsets;
0580     QList<int> entries;
0581     QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow();
0582     int activeSWIndex = -1;
0583     int i = editors.size();
0584 
0585     while (--i >= 0) {
0586         //if (editors.at(i)==m_projectSubWindow)
0587         if (!editors.at(i) || !qobject_cast<EditorTab*>(editors.at(i)->widget()))
0588             continue;
0589 
0590         EditorState state = static_cast<EditorTab*>(editors.at(i)->widget())->state();
0591         if (editors.at(i) == activeSW) {
0592             activeSWIndex = files.size();
0593             m_lastEditorState = state.dockWidgets.toBase64();
0594         }
0595         files.append(state.filePath);
0596         mergeFiles.append(state.mergeFilePath);
0597         dockWidgets.append(state.dockWidgets.toBase64());
0598         entries.append(state.entry);
0599         //offsets.append(state.offset);
0600         //qCWarning(LOKALIZE_LOG)<<static_cast<EditorWindow*>(editors.at(i)->widget() )->state().url;
0601     }
0602     //if (activeSWIndex==-1 && activeSW==m_projectSubWindow)
0603 
0604     if (files.size() == 0 && !m_lastEditorState.isEmpty()) {
0605         dockWidgets.append(m_lastEditorState); // save last state if no editor open
0606     }
0607     if (stateGroup.isValid())
0608         stateGroup.writeEntry("Project", Project::instance()->path());
0609 
0610 
0611     KConfig config;
0612     KConfigGroup projectStateGroup(&config, "State-" + Project::instance()->path());
0613     projectStateGroup.writeEntry("Active", activeSWIndex);
0614     projectStateGroup.writeEntry("Files", files);
0615     projectStateGroup.writeEntry("MergeFiles", mergeFiles);
0616     projectStateGroup.writeEntry("DockWidgets", dockWidgets);
0617     //stateGroup.writeEntry("Offsets",offsets);
0618     projectStateGroup.writeEntry("Entries", entries);
0619     if (m_projectSubWindow) {
0620         ProjectTab *w = static_cast<ProjectTab*>(m_projectSubWindow->widget());
0621         if (w->unitsCount() > 0)
0622             projectStateGroup.writeEntry("UnitsCount", w->unitsCount());
0623     }
0624 
0625 
0626     QString nameSpecifier = Project::instance()->path();
0627     if (!nameSpecifier.isEmpty()) nameSpecifier.prepend('-');
0628     KConfig* c = stateGroup.isValid() ? stateGroup.config() : &config;
0629     m_openRecentFileAction->saveEntries(KConfigGroup(c, "RecentFiles" + nameSpecifier));
0630     m_openRecentProjectAction->saveEntries(KConfigGroup(&config, "RecentProjects"));
0631 }
0632 
0633 void LokalizeMainWindow::readProperties(const KConfigGroup& stateGroup)
0634 {
0635     KConfig config;
0636     m_openRecentProjectAction->loadEntries(KConfigGroup(&config, "RecentProjects"));
0637     QString path = stateGroup.readEntry("Project", QString());
0638     if (Project::instance()->isLoaded() || path.isEmpty()) {
0639         //1. we weren't existing yet when the signal was emitted
0640         //2. defer until event loop is running to eliminate QWidgetPrivate::showChildren(bool) startup crash
0641         QTimer::singleShot(0, this, &LokalizeMainWindow::projectLoaded);
0642     } else
0643         Project::instance()->load(path);
0644 }
0645 
0646 void LokalizeMainWindow::projectLoaded()
0647 {
0648     QString projectPath = Project::instance()->path();
0649     qCDebug(LOKALIZE_LOG) << "Loaded project : " << projectPath;
0650     if (!projectPath.isEmpty())
0651         m_openRecentProjectAction->addUrl(QUrl::fromLocalFile(projectPath));
0652 
0653     KConfig config;
0654 
0655     QString nameSpecifier = projectPath;
0656     if (!nameSpecifier.isEmpty()) nameSpecifier.prepend('-');
0657     m_openRecentFileAction->loadEntries(KConfigGroup(&config, "RecentFiles" + nameSpecifier));
0658 
0659 
0660     //if project isn't loaded, still restore opened files from "State-"
0661     KConfigGroup stateGroup(&config, "State");
0662     KConfigGroup projectStateGroup(&config, "State-" + projectPath);
0663 
0664     QStringList files;
0665     QStringList mergeFiles;
0666     QList<QByteArray> dockWidgets;
0667     //QList<int> offsets;
0668     QList<int> entries;
0669 
0670     projectOverview();
0671     if (m_projectSubWindow) {
0672         ProjectTab *w = static_cast<ProjectTab*>(m_projectSubWindow->widget());
0673         w->setLegacyUnitsCount(projectStateGroup.readEntry("UnitsCount", 0));
0674 
0675         QTabBar* tw = m_mdiArea->findChild<QTabBar*>();
0676         if (tw) for (int i = 0; i < tw->count(); i++)
0677                 if (tw->tabText(i) == w->windowTitle())
0678                     tw->setTabToolTip(i, Project::instance()->path());
0679     }
0680     entries = projectStateGroup.readEntry("Entries", entries);
0681 
0682     if (Settings::self()->restoreRecentFilesOnStartup())
0683         files = projectStateGroup.readEntry("Files", files);
0684     mergeFiles = projectStateGroup.readEntry("MergeFiles", mergeFiles);
0685     dockWidgets = projectStateGroup.readEntry("DockWidgets", dockWidgets);
0686     int i = files.size();
0687     int activeSWIndex = projectStateGroup.readEntry("Active", -1);
0688     QStringList failedFiles;
0689     while (--i >= 0) {
0690         if (i < dockWidgets.size()) {
0691             m_lastEditorState = dockWidgets.at(i);
0692         }
0693         if (!fileOpen(files.at(i), entries.at(i)/*, offsets.at(i)*//*,&activeSW11*/, activeSWIndex == i, mergeFiles.at(i),/*silent*/true))
0694             failedFiles.append(files.at(i));
0695     }
0696     if (!failedFiles.isEmpty()) {
0697         qCDebug(LOKALIZE_LOG) << "failedFiles" << failedFiles;
0698 //         KMessageBox::error(this, i18nc("@info","Error opening the following files:")+
0699 //                                 "<br><il><li><filename>"+failedFiles.join("</filename></li><li><filename>")+"</filename></li></il>" );
0700         KNotification* notification = new KNotification("FilesOpenError");
0701         notification->setWidget(this);
0702         notification->setText(i18nc("@info", "Error opening the following files:\n\n") + "<filename>" + failedFiles.join("</filename><br><filename>") + "</filename>");
0703         notification->sendEvent();
0704     }
0705 
0706     if (files.isEmpty() && dockWidgets.size() > 0) {
0707         m_lastEditorState = dockWidgets.last(); // restore last state if no editor open
0708     } else {
0709         m_lastEditorState = stateGroup.readEntry("DefaultDockWidgets", m_lastEditorState); // restore default state if no last editor for this project
0710     }
0711 
0712     if (activeSWIndex == -1) {
0713         m_toBeActiveSubWindow = m_projectSubWindow;
0714         QTimer::singleShot(0, this, &LokalizeMainWindow::applyToBeActiveSubWindow);
0715     }
0716 
0717     projectSettingsChanged();
0718 }
0719 
0720 void LokalizeMainWindow::projectSettingsChanged()
0721 {
0722     //TODO show langs
0723     setCaption(Project::instance()->projectID());
0724 }
0725 
0726 void LokalizeMainWindow::widgetTextCapture()
0727 {
0728     WidgetTextCaptureConfig* w = new WidgetTextCaptureConfig(this);
0729     w->show();
0730 }
0731 
0732 
0733 //BEGIN DBus interface
0734 
0735 //#include "plugin.h"
0736 #include "mainwindowadaptor.h"
0737 
0738 /*
0739 void LokalizeMainWindow::checkForProjectAlreadyOpened()
0740 {
0741 
0742     QStringList services=QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
0743     int i=services.size();
0744     while(--i>=0)
0745     {
0746         if (services.at(i).startsWith("org.kde.lokalize"))
0747             //QDBusReply<uint> QDBusConnectionInterface::servicePid ( const QString & serviceName ) const;
0748             QDBusConnection::callWithCallback(QDBusMessage::createMethodCall(services.at(i),"/ThisIsWhatYouWant","org.kde.Lokalize.MainWindow","currentProject"),
0749                                               this, SLOT(), const char * errorMethod);
0750     }
0751 
0752 }
0753 */
0754 
0755 void LokalizeMainWindow::registerDBusAdaptor()
0756 {
0757     new MainWindowAdaptor(this);
0758     QDBusConnection::sessionBus().registerObject(QLatin1String("/ThisIsWhatYouWant"), this);
0759 
0760     //qCWarning(LOKALIZE_LOG)<<QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
0761 
0762     //QMenu* projectActions=static_cast<QMenu*>(factory()->container("project_actions",this));
0763 
0764     /*
0765         KActionCollection* actionCollection = mWindow->actionCollection();
0766         actionCollection->action("file_save")->setEnabled(canSave);
0767         actionCollection->action("file_save_as")->setEnabled(canSave);
0768     */
0769 }
0770 
0771 int LokalizeMainWindow::lookupInTranslationMemory(DocPosition::Part part, const QString& text)
0772 {
0773     TM::TMTab* w = showTM();
0774     if (!text.isEmpty())
0775         w->lookup(part == DocPosition::Source ? text : QString(), part == DocPosition::Target ? text : QString());
0776     return w->dbusId();
0777 }
0778 
0779 int LokalizeMainWindow::lookupInTranslationMemory(const QString& source, const QString& target)
0780 {
0781     TM::TMTab* w = showTM();
0782     w->lookup(source, target);
0783     return w->dbusId();
0784 }
0785 
0786 
0787 int LokalizeMainWindow::showTranslationMemory()
0788 {
0789     /*activateWindow();
0790     raise();
0791     show();*/
0792     return lookupInTranslationMemory(DocPosition::UndefPart, QString());
0793 }
0794 
0795 int LokalizeMainWindow::openFileInEditorAt(const QString& path, const QString& source, const QString& ctxt)
0796 {
0797     EditorTab* w = fileOpen(path, source, ctxt, true);
0798     if (!w) return -1;
0799     return w->dbusId();
0800 }
0801 
0802 int LokalizeMainWindow::openFileInEditor(const QString& path)
0803 {
0804     return openFileInEditorAt(path, QString(), QString());
0805 }
0806 
0807 QObject* LokalizeMainWindow::activeEditor()
0808 {
0809     //QList<QMdiSubWindow*> editors=m_mdiArea->subWindowList();
0810     QMdiSubWindow* activeSW = m_mdiArea->currentSubWindow();
0811     if (!activeSW || !qobject_cast<EditorTab*>(activeSW->widget()))
0812         return nullptr;
0813     return activeSW->widget();
0814 }
0815 
0816 QObject* LokalizeMainWindow::editorForFile(const QString& path)
0817 {
0818     FileToEditor::const_iterator it = m_fileToEditor.constFind(QFileInfo(path).canonicalFilePath());
0819     if (it == m_fileToEditor.constEnd()) return nullptr;
0820     QMdiSubWindow* w = it.value();
0821     if (!w) return nullptr;
0822     return static_cast<EditorTab*>(w->widget());
0823 }
0824 
0825 int LokalizeMainWindow::editorIndexForFile(const QString& path)
0826 {
0827     EditorTab* editor = static_cast<EditorTab*>(editorForFile(path));
0828     if (!editor) return -1;
0829     return editor->dbusId();
0830 }
0831 
0832 
0833 QString LokalizeMainWindow::currentProject()
0834 {
0835     return Project::instance()->path();
0836 }
0837 
0838 #ifdef Q_OS_WIN
0839 #include <windows.h>
0840 int LokalizeMainWindow::pid()
0841 {
0842     return GetCurrentProcessId();
0843 }
0844 #else
0845 #include <unistd.h>
0846 int LokalizeMainWindow::pid()
0847 {
0848     return getpid();
0849 }
0850 #endif
0851 
0852 QString LokalizeMainWindow::dbusName()
0853 {
0854     return QStringLiteral("org.kde.lokalize-%1").arg(pid());
0855 }
0856 void LokalizeMainWindow::busyCursor(bool busy)
0857 {
0858     busy ? QApplication::setOverrideCursor(Qt::WaitCursor) : QApplication::restoreOverrideCursor();
0859 }
0860 
0861 
0862 void LokalizeMdiArea::activateNextSubWindow()
0863 {
0864     this->setActivationOrder((QMdiArea::WindowOrder)Settings::tabSwitch());
0865     this->QMdiArea::activateNextSubWindow();
0866     this->setActivationOrder(QMdiArea::ActivationHistoryOrder);
0867 }
0868 
0869 void LokalizeMdiArea::activatePreviousSubWindow()
0870 {
0871     this->setActivationOrder((QMdiArea::WindowOrder)Settings::tabSwitch());
0872     this->QMdiArea::activatePreviousSubWindow();
0873     this->setActivationOrder(QMdiArea::ActivationHistoryOrder);
0874 }
0875 
0876 MultiEditorAdaptor::MultiEditorAdaptor(EditorTab *parent)
0877     : EditorAdaptor(parent)
0878 {
0879     setObjectName(QStringLiteral("MultiEditorAdaptor"));
0880     connect(parent, QOverload<QObject*>::of(&EditorTab::destroyed), this, QOverload<QObject*>::of(&MultiEditorAdaptor::handleParentDestroy));
0881 }
0882 
0883 void MultiEditorAdaptor::setEditorTab(EditorTab* e)
0884 {
0885     if (parent())
0886         disconnect(parent(), QOverload<QObject*>::of(&EditorTab::destroyed), this, QOverload<QObject*>::of(&MultiEditorAdaptor::handleParentDestroy));
0887     if (e)
0888         connect(e, QOverload<QObject*>::of(&EditorTab::destroyed), this, QOverload<QObject*>::of(&MultiEditorAdaptor::handleParentDestroy));
0889     setParent(e);
0890     setAutoRelaySignals(false);
0891     setAutoRelaySignals(true);
0892 }
0893 
0894 void MultiEditorAdaptor::handleParentDestroy(QObject* p)
0895 {
0896     Q_UNUSED(p);
0897     setParent(nullptr);
0898 }
0899 
0900 //END DBus interface
0901 
0902 
0903 
0904 DelayedFileOpener::DelayedFileOpener(const QVector<QString>& urls, LokalizeMainWindow* lmw)
0905     : QObject()
0906     , m_urls(urls)
0907     , m_lmw(lmw)
0908 {
0909     //do the work just after project loading is finished
0910     //(i.e. all the files from previous project session are loaded)
0911     QTimer::singleShot(1, this, &DelayedFileOpener::doOpen);
0912 }
0913 
0914 void DelayedFileOpener::doOpen()
0915 {
0916     int lastIndex = m_urls.count() - 1;
0917     for (int i = 0; i <= lastIndex; i++)
0918         m_lmw->fileOpen(m_urls.at(i), 0, /*set as active*/i == lastIndex);
0919     deleteLater();
0920 }
0921 
0922 
0923 #include "moc_lokalizesubwindowbase.cpp"
0924 #include "moc_multieditoradaptor.cpp"
0925