diff --git a/kate/katedocmanager.cpp b/kate/katedocmanager.cpp index e56935fdf..390163ba4 100644 --- a/kate/katedocmanager.cpp +++ b/kate/katedocmanager.cpp @@ -1,612 +1,593 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2002 Joseph Wenninger Copyright (C) 2007 Flavio Castelli This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katedocmanager.h" #include "kateapp.h" #include "katemainwindow.h" #include "kateviewmanager.h" #include "katesavemodifieddialog.h" #include "katedebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KateDocManager::KateDocManager(QObject *parent) : QObject(parent) , m_metaInfos(QStringLiteral("katemetainfos"), KConfig::NoGlobals) , m_saveMetaInfos(true) , m_daysMetaInfos(0) - , m_documentStillToRestore(0) { // set our application wrapper KTextEditor::Editor::instance()->setApplication(KateApp::self()->wrapper()); // create one doc, we always have at least one around! createDoc(); } KateDocManager::~KateDocManager() { // write metainfos? if (m_saveMetaInfos) { // saving meta-infos when file is saved is not enough, we need to do it once more at the end saveMetaInfos(m_docList); // purge saved filesessions if (m_daysMetaInfos > 0) { const QStringList groups = m_metaInfos.groupList(); QDateTime def(QDate(1970, 1, 1)); for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) { QDateTime last = m_metaInfos.group(*it).readEntry("Time", def); if (last.daysTo(QDateTime::currentDateTimeUtc()) > m_daysMetaInfos) { m_metaInfos.deleteGroup(*it); } } } } qDeleteAll(m_docInfos); } KTextEditor::Document *KateDocManager::createDoc(const KateDocumentInfo &docInfo) { KTextEditor::Document *doc = KTextEditor::Editor::instance()->createDocument(this); // turn of the editorpart's own modification dialog, we have our own one, too! const KConfigGroup generalGroup(KSharedConfig::openConfig(), "General"); bool ownModNotification = generalGroup.readEntry("Modified Notification", false); if (qobject_cast(doc)) { qobject_cast(doc)->setModifiedOnDiskWarning(!ownModNotification); } m_docList.append(doc); m_docInfos.insert(doc, new KateDocumentInfo(docInfo)); // connect internal signals... connect(doc, &KTextEditor::Document::modifiedChanged, this, &KateDocManager::slotModChanged1); connect(doc, SIGNAL(modifiedOnDisk(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason)), this, SLOT(slotModifiedOnDisc(KTextEditor::Document*,bool,KTextEditor::ModificationInterface::ModifiedOnDiskReason))); // we have a new document, show it the world emit documentCreated(doc); emit documentCreatedViewManager(doc); // return our new document return doc; } KateDocumentInfo *KateDocManager::documentInfo(KTextEditor::Document *doc) { return m_docInfos.contains(doc) ? m_docInfos[doc] : nullptr; } KTextEditor::Document *KateDocManager::findDocument(const QUrl &url) const { QUrl u(url.adjusted(QUrl::NormalizePathSegments)); // Resolve symbolic links for local files (done anyway in KTextEditor) if (u.isLocalFile()) { QString normalizedUrl = QFileInfo(u.toLocalFile()).canonicalFilePath(); if (!normalizedUrl.isEmpty()) { u = QUrl::fromLocalFile(normalizedUrl); } } foreach(KTextEditor::Document * it, m_docList) { if (it->url() == u) { return it; } } return nullptr; } QList KateDocManager::openUrls(const QList &urls, const QString &encoding, bool isTempFile, const KateDocumentInfo &docInfo) { QList docs; emit aboutToCreateDocuments(); foreach(const QUrl & url, urls) { docs << openUrl(url, encoding, isTempFile, docInfo); } emit documentsCreated(docs); return docs; } KTextEditor::Document *KateDocManager::openUrl(const QUrl &url, const QString &encoding, bool isTempFile, const KateDocumentInfo &docInfo) { // special handling: if only one unmodified empty buffer in the list, // keep this buffer in mind to close it after opening the new url KTextEditor::Document *untitledDoc = nullptr; if ((documentList().count() == 1) && (!documentList().at(0)->isModified() && documentList().at(0)->url().isEmpty())) { untitledDoc = documentList().first(); } // // create new document // QUrl u(url.adjusted(QUrl::NormalizePathSegments)); KTextEditor::Document *doc = nullptr; // always new document if url is empty... if (!u.isEmpty()) { doc = findDocument(u); } if (!doc) { if (untitledDoc) { // reuse the untitled document which is not needed auto & info = m_docInfos.find(untitledDoc).value(); delete info; info = new KateDocumentInfo(docInfo); doc = untitledDoc; } else { doc = createDoc(docInfo); } if (!encoding.isEmpty()) { doc->setEncoding(encoding); } if (!u.isEmpty()) { doc->openUrl(u); loadMetaInfos(doc, u); } } // // if needed, register as temporary file // if (isTempFile && u.isLocalFile()) { QFileInfo fi(u.toLocalFile()); if (fi.exists()) { m_tempFiles[doc] = qMakePair(u, fi.lastModified()); qCDebug(LOG_KATE) << "temporary file will be deleted after use unless modified: " << u; } } return doc; } bool KateDocManager::closeDocuments(const QList &documents, bool closeUrl) { if (documents.isEmpty()) { return false; } saveMetaInfos(documents); emit aboutToDeleteDocuments(documents); int last = 0; bool success = true; foreach(KTextEditor::Document * doc, documents) { if (closeUrl && !doc->closeUrl()) { success = false; // get out on first error break; } if (closeUrl && m_tempFiles.contains(doc)) { QFileInfo fi(m_tempFiles[ doc ].first.toLocalFile()); if (fi.lastModified() <= m_tempFiles[ doc ].second || KMessageBox::questionYesNo(KateApp::self()->activeKateMainWindow(), i18n("The supposedly temporary file %1 has been modified. " "Do you want to delete it anyway?", m_tempFiles[ doc ].first.url(QUrl::PreferLocalFile)), i18n("Delete File?")) == KMessageBox::Yes) { KIO::del(m_tempFiles[ doc ].first, KIO::HideProgressInfo); qCDebug(LOG_KATE) << "Deleted temporary file " << m_tempFiles[ doc ].first; m_tempFiles.remove(doc); } else { m_tempFiles.remove(doc); qCDebug(LOG_KATE) << "The supposedly temporary file " << m_tempFiles[ doc ].first.url() << " have been modified since loaded, and has not been deleted."; } } KateApp::self()->emitDocumentClosed(QString::number((qptrdiff)doc)); // document will be deleted, soon emit documentWillBeDeleted(doc); // really delete the document and its infos delete m_docInfos.take(doc); delete m_docList.takeAt(m_docList.indexOf(doc)); // document is gone, emit our signals emit documentDeleted(doc); last++; } /** * never ever empty the whole document list * do this before documentsDeleted is emitted, to have no flicker */ if (m_docList.isEmpty()) { createDoc(); } emit documentsDeleted(documents.mid(last)); return success; } bool KateDocManager::closeDocument(KTextEditor::Document *doc, bool closeUrl) { if (!doc) { return false; } QList documents; documents.append(doc); return closeDocuments(documents, closeUrl); } bool KateDocManager::closeDocumentList(QList documents) { QList modifiedDocuments; foreach(KTextEditor::Document * document, documents) { if (document->isModified()) { modifiedDocuments.append(document); } } if (modifiedDocuments.size() > 0 && !KateSaveModifiedDialog::queryClose(nullptr, modifiedDocuments)) { return false; } return closeDocuments(documents, false); // Do not show save/discard dialog } bool KateDocManager::closeAllDocuments(bool closeUrl) { /** * just close all documents */ return closeDocuments(m_docList, closeUrl); } bool KateDocManager::closeOtherDocuments(KTextEditor::Document *doc) { /** * close all documents beside the passed one */ QList documents = m_docList; documents.removeOne(doc); return closeDocuments(documents); } /** * Find all modified documents. * @return Return the list of all modified documents. */ QList KateDocManager::modifiedDocumentList() { QList modified; foreach(KTextEditor::Document * doc, m_docList) { if (doc->isModified()) { modified.append(doc); } } return modified; } bool KateDocManager::queryCloseDocuments(KateMainWindow *w) { int docCount = m_docList.count(); foreach(KTextEditor::Document * doc, m_docList) { if (doc->url().isEmpty() && doc->isModified()) { int msgres = KMessageBox::warningYesNoCancel(w, i18n("

The document '%1' has been modified, but not saved.

" "

Do you want to save your changes or discard them?

", doc->documentName()), i18n("Close Document"), KStandardGuiItem::save(), KStandardGuiItem::discard()); if (msgres == KMessageBox::Cancel) { return false; } if (msgres == KMessageBox::Yes) { const QUrl url = QFileDialog::getSaveFileUrl(w, i18n("Save As")); if (!url.isEmpty()) { if (!doc->saveAs(url)) { return false; } } else { return false; } } } else { if (!doc->queryClose()) { return false; } } } // document count changed while queryClose, abort and notify user if (m_docList.count() > docCount) { KMessageBox::information(w, i18n("New file opened while trying to close Kate, closing aborted."), i18n("Closing Aborted")); return false; } return true; } void KateDocManager::saveAll() { foreach(KTextEditor::Document * doc, m_docList) if (doc->isModified()) { doc->documentSave(); } } void KateDocManager::saveSelected(const QList &docList) { foreach(KTextEditor::Document * doc, docList) { if (doc->isModified()) { doc->documentSave(); } } } void KateDocManager::reloadAll() { // reload all docs that are NOT modified on disk foreach(KTextEditor::Document * doc, m_docList) { if (! documentInfo(doc)->modifiedOnDisc) { doc->documentReload(); } } // take care of all documents that ARE modified on disk KateApp::self()->activeKateMainWindow()->showModOnDiskPrompt(); } void KateDocManager::closeOrphaned() { QList documents; foreach(KTextEditor::Document * doc, m_docList) { KateDocumentInfo *info = documentInfo(doc); if (info && !info->openSuccess) { documents.append(doc); } } closeDocuments(documents); } void KateDocManager::saveDocumentList(KConfig *config) { KConfigGroup openDocGroup(config, "Open Documents"); openDocGroup.writeEntry("Count", m_docList.count()); int i = 0; foreach(KTextEditor::Document * doc, m_docList) { KConfigGroup cg(config, QStringLiteral("Document %1").arg(i)); doc->writeSessionConfig(cg); i++; } } void KateDocManager::restoreDocumentList(KConfig *config) { KConfigGroup openDocGroup(config, "Open Documents"); unsigned int count = openDocGroup.readEntry("Count", 0); if (count == 0) { return; } QProgressDialog progress; progress.setWindowTitle(i18n("Starting Up")); progress.setLabelText(i18n("Reopening files from the last session...")); progress.setModal(true); progress.setCancelButton(nullptr); progress.setRange(0, count); - m_documentStillToRestore = count; - m_openingErrors.clear(); for (unsigned int i = 0; i < count; i++) { KConfigGroup cg(config, QStringLiteral("Document %1").arg(i)); KTextEditor::Document *doc = nullptr; if (i == 0) { doc = m_docList.first(); } else { doc = createDoc(); } connect(doc, SIGNAL(completed()), this, SLOT(documentOpened())); connect(doc, &KParts::ReadOnlyPart::canceled, this, &KateDocManager::documentOpened); doc->readSessionConfig(cg); progress.setValue(i); } } void KateDocManager::slotModifiedOnDisc(KTextEditor::Document *doc, bool b, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) { if (m_docInfos.contains(doc)) { m_docInfos[doc]->modifiedOnDisc = b; m_docInfos[doc]->modifiedOnDiscReason = reason; slotModChanged1(doc); } } /** * Load file's meta-information if the checksum didn't change since last time. */ bool KateDocManager::loadMetaInfos(KTextEditor::Document *doc, const QUrl &url) { if (!m_saveMetaInfos) { return false; } if (!m_metaInfos.hasGroup(url.toDisplayString())) { return false; } const QByteArray checksum = doc->checksum().toHex(); bool ok = true; if (!checksum.isEmpty()) { KConfigGroup urlGroup(&m_metaInfos, url.toDisplayString()); const QString old_checksum = urlGroup.readEntry("Checksum"); if (QString::fromLatin1(checksum) == old_checksum) { QSet flags; if (documentInfo(doc)->openedByUser) { flags << QStringLiteral ("SkipEncoding"); } flags << QStringLiteral ("SkipUrl"); doc->readSessionConfig(urlGroup, flags); } else { urlGroup.deleteGroup(); ok = false; } m_metaInfos.sync(); } return ok && doc->url() == url; } /** * Save file's meta-information if doc is in 'unmodified' state */ void KateDocManager::saveMetaInfos(const QList &documents) { /** * skip work if no meta infos wanted */ if (!m_saveMetaInfos) { return; } /** * store meta info for all non-modified documents which have some checksum */ const QDateTime now = QDateTime::currentDateTimeUtc(); foreach(KTextEditor::Document * doc, documents) { /** * skip modified docs */ if (doc->isModified()) { continue; } const QByteArray checksum = doc->checksum().toHex(); if (!checksum.isEmpty()) { /** * write the group with checksum and time */ KConfigGroup urlGroup(&m_metaInfos, doc->url().toString()); urlGroup.writeEntry("Checksum", QString::fromLatin1(checksum)); urlGroup.writeEntry("Time", now); /** * write document session config */ doc->writeSessionConfig(urlGroup); } } /** * sync to not loose data */ m_metaInfos.sync(); } void KateDocManager::slotModChanged(KTextEditor::Document *doc) { QList documents; documents.append(doc); saveMetaInfos(documents); } void KateDocManager::slotModChanged1(KTextEditor::Document *doc) { QMetaObject::invokeMethod(KateApp::self()->activeKateMainWindow(), "queueModifiedOnDisc", Qt::QueuedConnection, Q_ARG(KTextEditor::Document *, doc)); } void KateDocManager::documentOpened() { KColorScheme colors(QPalette::Active); KTextEditor::Document *doc = qobject_cast(sender()); if (!doc) { return; // should never happen, but who knows } disconnect(doc, SIGNAL(completed()), this, SLOT(documentOpened())); disconnect(doc, &KParts::ReadOnlyPart::canceled, this, &KateDocManager::documentOpened); - if (doc->openingError()) { - m_openingErrors += QLatin1Char('\n') + doc->openingErrorMessage() + QStringLiteral("\n\n"); + + // Only set "no success" when doc is empty to avoid close of files + // with other trouble when do closeOrphaned() + if (doc->openingError() && doc->isEmpty()) { KateDocumentInfo *info = documentInfo(doc); if (info) { info->openSuccess = false; } } - --m_documentStillToRestore; - - if (m_documentStillToRestore == 0) { - QTimer::singleShot(0, this, &KateDocManager::showRestoreErrors); - } } - -void KateDocManager::showRestoreErrors() -{ - if (!m_openingErrors.isEmpty()) { - KMessageBox::information(nullptr, - m_openingErrors, - i18n("Errors/Warnings while opening documents")); - - // clear errors - m_openingErrors.clear(); - } -} - diff --git a/kate/katedocmanager.h b/kate/katedocmanager.h index 4f9e70a78..41517b50a 100644 --- a/kate/katedocmanager.h +++ b/kate/katedocmanager.h @@ -1,222 +1,219 @@ /* This file is part of the KDE project Copyright (C) 2001 Christoph Cullmann Copyright (C) 2002 Joseph Wenninger This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_DOCMANAGER_H__ #define __KATE_DOCMANAGER_H__ #include #include #include #include #include #include #include #include #include #include #include class KateMainWindow; class KateDocumentInfo { public: enum CustomRoles {RestoreOpeningFailedRole }; public: KateDocumentInfo() : modifiedOnDisc(false) , modifiedOnDiscReason(KTextEditor::ModificationInterface::OnDiskUnmodified) , openedByUser(false) , openSuccess(true) {} bool modifiedOnDisc; KTextEditor::ModificationInterface::ModifiedOnDiskReason modifiedOnDiscReason; bool openedByUser; bool openSuccess; }; class KateDocManager : public QObject { Q_OBJECT public: KateDocManager(QObject *parent); ~KateDocManager() override; KTextEditor::Document *createDoc(const KateDocumentInfo &docInfo = KateDocumentInfo()); KateDocumentInfo *documentInfo(KTextEditor::Document *doc); /** Returns the documentNumber of the doc with url URL or -1 if no such doc is found */ KTextEditor::Document *findDocument(const QUrl &url) const; const QList &documentList() const { return m_docList; } KTextEditor::Document *openUrl(const QUrl &, const QString &encoding = QString(), bool isTempFile = false, const KateDocumentInfo &docInfo = KateDocumentInfo()); QList openUrls(const QList &, const QString &encoding = QString(), bool isTempFile = false, const KateDocumentInfo &docInfo = KateDocumentInfo()); bool closeDocument(KTextEditor::Document *, bool closeUrl = true); bool closeDocuments(const QList &documents, bool closeUrl = true); bool closeDocumentList(QList documents); bool closeAllDocuments(bool closeUrl = true); bool closeOtherDocuments(KTextEditor::Document *); QList modifiedDocumentList(); bool queryCloseDocuments(KateMainWindow *w); void saveDocumentList(KConfig *config); void restoreDocumentList(KConfig *config); inline bool getSaveMetaInfos() { return m_saveMetaInfos; } inline void setSaveMetaInfos(bool b) { m_saveMetaInfos = b; } inline int getDaysMetaInfos() { return m_daysMetaInfos; } inline void setDaysMetaInfos(int i) { m_daysMetaInfos = i; } public Q_SLOTS: /** * saves all documents that has at least one view. * documents with no views are ignored :P */ void saveAll(); /** * reloads all documents that has at least one view. * documents with no views are ignored :P */ void reloadAll(); /** * close all documents, which could not be reopened */ void closeOrphaned(); /** * save selected documents from the File List */ void saveSelected(const QList &); Q_SIGNALS: /** * This signal is emitted when the \p document was created. */ void documentCreated(KTextEditor::Document *document); /** * This signal is emitted when the \p document was created. * This is emitted after the initial documentCreated for internal use in view manager */ void documentCreatedViewManager(KTextEditor::Document *document); /** * This signal is emitted before a \p document which should be closed is deleted * The document is still accessible and usable, but it will be deleted * after this signal was send. * * @param document document that will be deleted */ void documentWillBeDeleted(KTextEditor::Document *document); /** * This signal is emitted when the \p document has been deleted. * * Warning !!! DO NOT ACCESS THE DATA REFERENCED BY THE POINTER, IT IS ALREADY INVALID * Use the pointer only to remove mappings in hash or maps */ void documentDeleted(KTextEditor::Document *document); /** * This signal is emitted before the batch of documents is being created. * * You can use it to pause some updates. */ void aboutToCreateDocuments(); /** * This signal is emitted after the batch of documents is created. * * @param documents list of documents that have been created */ void documentsCreated(const QList &documents); /** * This signal is emitted before the documents batch is going to be deleted * * note that the batch can be interrupted in the middle and only some * of the documents may be actually deleted. See documentsDeleted() signal. */ void aboutToDeleteDocuments(const QList &); /** * This signal is emitted after the documents batch was deleted * * This is the batch closing signal for aboutToDeleteDocuments * @param documents the documents that weren't deleted after all */ void documentsDeleted(const QList &documents); private Q_SLOTS: void slotModifiedOnDisc(KTextEditor::Document *doc, bool b, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason); void slotModChanged(KTextEditor::Document *doc); void slotModChanged1(KTextEditor::Document *doc); - void showRestoreErrors(); private: bool loadMetaInfos(KTextEditor::Document *doc, const QUrl &url); void saveMetaInfos(const QList &docs); QList m_docList; QHash m_docInfos; KConfig m_metaInfos; bool m_saveMetaInfos; int m_daysMetaInfos; typedef QPair TPair; QMap m_tempFiles; - QString m_openingErrors; - int m_documentStillToRestore; private Q_SLOTS: void documentOpened(); }; #endif