changeset 1704:452b48b29c2d single-point

Associate a label with each recent files entry, as well as the identifier (filename)
author Chris Cannam
date Mon, 13 May 2019 15:43:23 +0100
parents b17fb3a4560c
children 28f9ff7864c6
files base/RecentFiles.cpp base/RecentFiles.h
diffstat 2 files changed, 156 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- a/base/RecentFiles.cpp	Thu May 09 16:01:04 2019 +0100
+++ b/base/RecentFiles.cpp	Mon May 13 15:43:23 2019 +0100
@@ -20,6 +20,7 @@
 #include <QFileInfo>
 #include <QSettings>
 #include <QRegExp>
+#include <QMutexLocker>
 
 RecentFiles::RecentFiles(QString settingsGroup, int maxCount) :
     m_settingsGroup(settingsGroup),
@@ -36,16 +37,26 @@
 void
 RecentFiles::read()
 {
-    m_names.clear();
+    // Private method - called only from constructor - no mutex lock required
+    
+    m_entries.clear();
     QSettings settings;
     settings.beginGroup(m_settingsGroup);
 
     for (int i = 0; i < 100; ++i) {
-        QString key = QString("recent-%1").arg(i);
-        QString name = settings.value(key, "").toString();
-        if (name == "") break;
-        if (i < m_maxCount) m_names.push_back(name);
-        else settings.setValue(key, "");
+
+        QString idKey = QString("recent-%1").arg(i);
+        QString identifier = settings.value(idKey, "").toString();
+        if (identifier == "") break;
+
+        QString labelKey = QString("recent-%1-label").arg(i);
+        QString label = settings.value(labelKey, "").toString();
+        
+        if (i < m_maxCount) m_entries.push_back({ identifier, label });
+        else {
+            settings.setValue(idKey, "");
+            settings.setValue(labelKey, "");
+        }
     }
 
     settings.endGroup();
@@ -54,14 +65,22 @@
 void
 RecentFiles::write()
 {
+    // Private method - must be serialised at call site
+    
     QSettings settings;
     settings.beginGroup(m_settingsGroup);
 
     for (int i = 0; i < m_maxCount; ++i) {
-        QString key = QString("recent-%1").arg(i);
-        QString name = "";
-        if (i < (int)m_names.size()) name = m_names[i];
-        settings.setValue(key, name);
+        QString idKey = QString("recent-%1").arg(i);
+        QString labelKey = QString("recent-%1-label").arg(i);
+        QString identifier;
+        QString label;
+        if (in_range_for(m_entries, i)) {
+            identifier = m_entries[i].first;
+            label = m_entries[i].second;
+        }
+        settings.setValue(idKey, identifier);
+        settings.setValue(labelKey, label);
     }
 
     settings.endGroup();
@@ -70,67 +89,92 @@
 void
 RecentFiles::truncateAndWrite()
 {
-    while (int(m_names.size()) > m_maxCount) {
-        m_names.pop_back();
+    // Private method - must be serialised at call site
+    
+    while (int(m_entries.size()) > m_maxCount) {
+        m_entries.pop_back();
     }
     write();
 }
 
 std::vector<QString>
-RecentFiles::getRecent() const
+RecentFiles::getRecentIdentifiers() const
 {
-    std::vector<QString> names;
+    QMutexLocker locker(&m_mutex);
+
+    std::vector<QString> identifiers;
     for (int i = 0; i < m_maxCount; ++i) {
-        if (i < (int)m_names.size()) {
-            names.push_back(m_names[i]);
+        if (i < (int)m_entries.size()) {
+            identifiers.push_back(m_entries[i].first);
         }
     }
-    return names;
+    
+    return identifiers;
+}
+
+std::vector<std::pair<QString, QString>>
+RecentFiles::getRecentEntries() const
+{
+    QMutexLocker locker(&m_mutex);
+
+    std::vector<std::pair<QString, QString>> entries;
+    for (int i = 0; i < m_maxCount; ++i) {
+        if (i < (int)m_entries.size()) {
+            entries.push_back(m_entries[i]);
+        }
+    }
+    
+    return entries;
 }
 
 void
-RecentFiles::add(QString name)
+RecentFiles::add(QString identifier, QString label)
 {
-    bool have = false;
-    for (int i = 0; i < int(m_names.size()); ++i) {
-        if (m_names[i] == name) {
-            have = true;
-            break;
+    {
+        QMutexLocker locker(&m_mutex);
+
+        bool have = false;
+        for (int i = 0; i < int(m_entries.size()); ++i) {
+            if (m_entries[i].first == identifier) {
+                have = true;
+                break;
+            }
         }
+    
+        if (!have) {
+            m_entries.push_front({ identifier, label });
+        } else {
+            std::deque<std::pair<QString, QString>> newEntries;
+            newEntries.push_back({ identifier, label });
+            for (int i = 0; in_range_for(m_entries, i); ++i) {
+                if (m_entries[i].first == identifier) continue;
+                newEntries.push_back(m_entries[i]);
+            }
+            m_entries = newEntries;
+        }
+
+        truncateAndWrite();
     }
     
-    if (!have) {
-        m_names.push_front(name);
-    } else {
-        std::deque<QString> newnames;
-        newnames.push_back(name);
-        for (int i = 0; i < int(m_names.size()); ++i) {
-            if (m_names[i] == name) continue;
-            newnames.push_back(m_names[i]);
-        }
-        m_names = newnames;
-    }
-
-    truncateAndWrite();
     emit recentChanged();
 }
 
 void
-RecentFiles::addFile(QString name)
+RecentFiles::addFile(QString filepath, QString label)
 {
     static QRegExp schemeRE("^[a-zA-Z]{2,5}://");
     static QRegExp tempRE("[\\/][Tt]e?mp[\\/]");
-    if (schemeRE.indexIn(name) == 0) {
-        add(name);
+    if (schemeRE.indexIn(filepath) == 0) {
+        add(filepath, label);
     } else {
-        QString absPath = QFileInfo(name).absoluteFilePath();
+        QString absPath = QFileInfo(filepath).absoluteFilePath();
         if (tempRE.indexIn(absPath) != -1) {
             Preferences *prefs = Preferences::getInstance();
             if (prefs && !prefs->getOmitTempsFromRecentFiles()) {
-                add(absPath);
+                add(absPath, label);
             }
         } else {
-            add(absPath);
+            add(absPath, label);
         }
     }
 }
--- a/base/RecentFiles.h	Thu May 09 16:01:04 2019 +0100
+++ b/base/RecentFiles.h	Mon May 13 15:43:23 2019 +0100
@@ -18,15 +18,21 @@
 
 #include <QObject>
 #include <QString>
+#include <QMutex>
 #include <vector>
 #include <deque>
 
 /**
- * RecentFiles manages a list of the names of recently-used objects,
- * saving and restoring that list via QSettings.  The names do not
- * actually have to refer to files.
+ * RecentFiles manages a list of recently-used identifier strings,
+ * saving and restoring that list via QSettings.  The identifiers do
+ * not actually have to refer to files.
+ *
+ * Each entry must have a non-empty identifier, which is typically a
+ * filename, path, URI, or internal id, and may optionally also have a
+ * label, which is typically a user-visible convenience.
+ *
+ * RecentFiles is thread-safe - all access is serialised.
  */
-
 class RecentFiles : public QObject
 {
     Q_OBJECT
@@ -35,42 +41,81 @@
     /**
      * Construct a RecentFiles object that saves and restores in the
      * given QSettings group and truncates when the given count of
-     * strings is reached.
+     * identifiers is reached.
      */
-    RecentFiles(QString settingsGroup = "RecentFiles", int maxCount = 10);
+    RecentFiles(QString settingsGroup = "RecentFiles",
+                int maxCount = 10);
 
     virtual ~RecentFiles();
 
-    QString getSettingsGroup() const { return m_settingsGroup; }
-
-    int getMaxCount() const { return m_maxCount; }
-
-    std::vector<QString> getRecent() const;
+    /**
+     * Return the settingsGroup as passed to the constructor.
+     */
+    QString getSettingsGroup() const {
+        return m_settingsGroup;
+    }
 
     /**
-     * Add a name that should be treated as a literal string.
+     * Return the maxCount as passed to the constructor.
      */
-    void add(QString name);
+    int getMaxCount() const {
+        return m_maxCount;
+    }
+
+    /**
+     * Return the list of recent identifiers, without labels.
+     */
+    std::vector<QString> getRecentIdentifiers() const;
+
+    /**
+     * Return the list of recent identifiers, without labels. This is
+     * an alias for getRecentIdentifiers included for backward
+     * compatibility.
+     */
+    std::vector<QString> getRecent() const {
+        return getRecentIdentifiers();
+    }
+
+    /**
+     * Return the list of recent identifiers, with labels. Each
+     * returned entry is a pair of identifier and label in that order.
+     */
+    std::vector<std::pair<QString, QString>> getRecentEntries() const;
     
     /**
-     * Add a name that is known to be either a file path or a URL.  If
-     * it looks like a URL, add it literally; otherwise treat it as a
-     * file path and canonicalise it appropriately.  Also takes into
-     * account the user preference for whether to include temporary
-     * files in the recent files menu: the file will not be added if
-     * the preference is set and the file appears to be a temporary
-     * one.
+     * Add a literal identifier, optionally with a label.
+     *
+     * If the identifier already exists in the recent entries list, it
+     * is moved to the front of the list and its label is replaced
+     * with the given one.
      */
-    void addFile(QString name);
+    void add(QString identifier, QString label = "");
+    
+    /**
+     * Add a name that is known to be either a file path or a URL,
+     * optionally with a label.  If it looks like a URL, add it
+     * literally; otherwise treat it as a file path and canonicalise
+     * it appropriately.  Also take into account the user preference
+     * for whether to include temporary files in the recent files
+     * menu: the file will not be added if the preference is set and
+     * the file appears to be a temporary one.
+     *
+     * If the identifier derived from the file path already exists in
+     * the recent entries list, it is moved to the front of the list
+     * and its label is replaced with the given one.
+     */
+    void addFile(QString filepath, QString label = "");
 
 signals:
     void recentChanged();
 
-protected:
-    QString m_settingsGroup;
-    int m_maxCount;
+private:
+    mutable QMutex m_mutex;
 
-    std::deque<QString> m_names;
+    const QString m_settingsGroup;
+    const int m_maxCount;
+
+    std::deque<std::pair<QString, QString>> m_entries; // identifier, label
 
     void read();
     void write();