File indexing completed on 2024-05-12 04:37:32

0001 /*
0002     SPDX-FileCopyrightText: 2020 Friedrich W. H. Kossebau <kossebau@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "sessionfilestracker.h"
0008 
0009 // library
0010 #include <kdevelopsessionsobserver.h>
0011 // KF
0012 #include <KDirWatch>
0013 #include <KConfig>
0014 #include <KConfigGroup>
0015 // Qt
0016 #include <QFileInfo>
0017 #include <QDir>
0018 #include <QStandardPaths>
0019 #include <QUuid>
0020 #include <QMutexLocker>
0021 #include <QMetaObject>
0022 #include <QCoreApplication>
0023 // Std
0024 #include <algorithm>
0025 
0026 namespace {
0027 namespace Strings {
0028 QString sessionConfigFileName() { return QStringLiteral("sessionrc"); }
0029 }
0030 }
0031 
0032 Q_GLOBAL_STATIC(SessionFilesTracker, s_SessionFilesTrackerInstance)
0033 
0034 SessionFilesTracker *SessionFilesTracker::instance()
0035 {
0036     return s_SessionFilesTrackerInstance();
0037 }
0038 
0039 static void setSessionDataList(QObject* observer, const QVector<KDevelopSessionData>& sessionDataList)
0040 {
0041     QMetaObject::invokeMethod(observer, "setSessionDataList", Qt::AutoConnection,
0042                               Q_ARG(QVector<KDevelopSessionData>, sessionDataList));
0043 }
0044 
0045 static void cleanupSessionFilesTracker()
0046 {
0047     if (s_SessionFilesTrackerInstance.exists()) {
0048         s_SessionFilesTrackerInstance->cleanup();
0049     }
0050 }
0051 
0052 
0053 SessionFilesTracker::SessionFilesTracker()
0054     : QObject()
0055     , m_dirWatch(new KDirWatch(this))
0056 {
0057     // KDirWatch might have QFileSystemWatcher,
0058     // which wants to be deleted before qApp is gone - bug 261541
0059     qAddPostRoutine(cleanupSessionFilesTracker);
0060 
0061     m_sessionDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kdevelop/sessions");
0062     m_dirWatch->stopScan();
0063     m_dirWatch->addDir(m_sessionDir, KDirWatch::WatchSubDirs);
0064 
0065     connect(m_dirWatch, &KDirWatch::dirty, this, &SessionFilesTracker::sessionSourceChanged);
0066 
0067     updateSessions();
0068 }
0069 
0070 SessionFilesTracker::~SessionFilesTracker() = default;
0071 
0072 void SessionFilesTracker::cleanup()
0073 {
0074     delete m_dirWatch;
0075     m_dirWatch = nullptr;
0076 }
0077 
0078 
0079 void SessionFilesTracker::registerObserver(QObject* observer)
0080 {
0081     if (!qobject_cast<KDevelopSessionsObserver*>(observer)) {
0082         return;
0083     }
0084 
0085     QMutexLocker lock(&m_mutex);
0086     const bool isFirstObserver = m_observers.isEmpty();
0087 
0088     m_observers.append(observer);
0089     setSessionDataList(observer, m_sessionDataList);
0090 
0091     if (isFirstObserver) {
0092         m_dirWatch->startScan(true);
0093     }
0094 }
0095 
0096 void SessionFilesTracker::unregisterObserver(QObject* observer)
0097 {
0098     if (!qobject_cast<KDevelopSessionsObserver*>(observer)) {
0099         return;
0100     }
0101 
0102     QMutexLocker lock(&m_mutex);
0103     m_observers.removeOne(observer);
0104 
0105     if (m_observers.isEmpty()) {
0106         m_dirWatch->stopScan();
0107     }
0108 }
0109 
0110 QVector<KDevelopSessionData> SessionFilesTracker::readSessionDataList() const
0111 {
0112     QVector<KDevelopSessionData> sessions;
0113 
0114     QDir sessionBaseDir(m_sessionDir);
0115     const auto dirEntries = sessionBaseDir.entryList(QDir::Dirs);
0116     sessions.reserve(dirEntries.size());
0117     for (const QString& sessionDirName : dirEntries) {
0118         if (QUuid(sessionDirName).isNull()) {
0119             continue;
0120         }
0121 
0122         QDir sessionDir(sessionBaseDir.absoluteFilePath(sessionDirName));
0123         const QString sessionConfigFilePath = sessionDir.filePath(Strings::sessionConfigFileName());
0124         if (!QFile::exists(sessionConfigFilePath)) {
0125             continue;
0126         }
0127 
0128         KConfig sessionDataStorage(sessionConfigFilePath, KConfig::SimpleConfig);
0129         const KConfigGroup mainSessionData = sessionDataStorage.group("");
0130 
0131         const KDevelopSessionData sessionData {
0132             sessionDirName,
0133             mainSessionData.readEntry("SessionName"),
0134             mainSessionData.readEntry("SessionPrettyContents"),
0135         };
0136 
0137         sessions.append(sessionData);
0138     }
0139     sessions.squeeze();
0140     std::sort(sessions.begin(), sessions.end(), [](const KDevelopSessionData& a, const KDevelopSessionData& b) {
0141         return a.id < b.id;   
0142     });
0143 
0144     return sessions;
0145 }
0146 
0147 void SessionFilesTracker::sessionSourceChanged(const QString& path)
0148 {
0149     // session data is not changed too often, so we are fine to simply rescan all the data
0150     // in case any relevant files (or the main dir) have been touched
0151 
0152     // session dirs removed or added?
0153     if (m_sessionDir == path) {
0154         updateSessions();
0155     } else {
0156         // session config file?
0157         QFileInfo pathInfo(path);
0158         if (pathInfo.fileName() == Strings::sessionConfigFileName()) {
0159             updateSessions();
0160         }
0161     }
0162 }
0163 
0164 void SessionFilesTracker::updateSessions()
0165 {
0166     QMutexLocker lock(&m_mutex);
0167 
0168     const auto newSessionDataList = readSessionDataList();
0169 
0170     if (m_sessionDataList == newSessionDataList) {
0171         return;
0172     }
0173 
0174     m_sessionDataList = newSessionDataList;
0175 
0176     for (auto* observer : qAsConst(m_observers)) {
0177         setSessionDataList(observer, m_sessionDataList);
0178     }
0179 }
0180 
0181 #include "moc_sessionfilestracker.cpp"