summaryrefslogtreecommitdiffstats
path: root/src/library/basetrackcache.h
blob: d4e957f2b6fa225d3d4ef14b8e0f7ef7f57f461e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#pragma once

#include <QHash>
#include <QList>
#include <QObject>
#include <QSet>
#include <QSqlDatabase>
#include <QString>
#include <QStringList>
#include <QVector>
#include <memory>

#include "library/columncache.h"
#include "track/track_decl.h"
#include "track/trackid.h"
#include "util/class.h"
#include "util/string.h"

class SearchQueryParser;
class TrackCollection;

class SortColumn {
  public:
    SortColumn(int column, Qt::SortOrder order)
        : m_column(column),
          m_order(order) {
    }
    int m_column;
    Qt::SortOrder m_order;
};

// BaseTrackCache is a cache of all of the values in certain table. It supports
// searching and sorting of tracks by values within the table. The reasoning for
// this is that previously there was a per-table-model cache which was largely a
// waste of memory because all the table-models were caching the same data
// (track properties). Furthermore, the base SQL tables of these table-models
// involve complicated joins, which are very slow.
class BaseTrackCache : public QObject {
    Q_OBJECT
  public:
    /// Construct a BaseTrackCache object.
    ///
    /// The order of the `columns` list parameter defines the initial/default
    /// order of columns in the library view.
    BaseTrackCache(TrackCollection* pTrackCollection,
                   const QString& tableName,
                   const QString& idColumn,
                   const QStringList& columns,
                   bool isCaching);
    ~BaseTrackCache() override;

    // Rebuild the BaseTrackCache index from the SQL table. This can be
    // expensive on large tables.
    virtual void buildIndex();

    ////////////////////////////////////////////////////////////////////////////
    // Data access methods
    ////////////////////////////////////////////////////////////////////////////

    virtual QVariant data(TrackId trackId, int column) const;
    virtual int columnCount() const;
    virtual int fieldIndex(const QString& column) const;
    QString columnNameForFieldIndex(int index) const;
    QString columnSortForFieldIndex(int index) const;
    int fieldIndex(ColumnCache::Column column) const;
    virtual void filterAndSort(const QSet<TrackId>& trackIds,
                               const QString& query,
                               const QString& extraFilter,
                               const QString& orderByClause,
                               const QList<SortColumn>& sortColumns,
                               const int columnOffset,
                               QHash<TrackId, int>* trackToIndex);
    virtual bool isCached(TrackId trackId) const;
    virtual void ensureCached(TrackId trackId);
    virtual void ensureCached(const QSet<TrackId>& trackIds);
    virtual void setSearchColumns(const QStringList& columns);

  signals:
    void tracksChanged(const QSet<TrackId>& trackIds);

  public slots:
    void slotScanTrackAdded(TrackPointer pTrack);

    void slotTracksAddedOrChanged(const QSet<TrackId>& trackId);
    void slotTracksRemoved(const QSet<TrackId>& trackId);
    void slotTrackDirty(TrackId trackId);
    void slotTrackClean(TrackId trackId);

  private:
    const TrackPointer& getRecentTrack(TrackId trackId) const;
    void replaceRecentTrack(TrackPointer pTrack) const;
    void replaceRecentTrack(TrackId trackId, TrackPointer pTrack) const;
    void resetRecentTrack() const;

    bool updateIndexWithQuery(const QString& query);
    void updateTrackInIndex(TrackId trackId);
    bool updateTrackInIndex(const TrackPointer& pTrack);
    void updateTracksInIndex(const QSet<TrackId>& trackIds);
    void getTrackValueForColumn(TrackPointer pTrack, int column,
                                QVariant& trackValue) const;

    int findSortInsertionPoint(TrackPointer pTrack,
                               const QList<SortColumn>& sortColumns,
                               const int columnOffset,
                               const QVector<TrackId>& trackIds) const;
    int compareColumnValues(int sortColumn,
            Qt::SortOrder sortOrder,
            const QVariant& val1,
            const QVariant& val2) const;
    bool trackMatches(const TrackPointer& pTrack,
                      const QRegExp& matcher) const;
    bool trackMatchesNumeric(const TrackPointer& pTrack,
                             const QStringList& numberMatchers) const;
    bool trackMatchesNamedString(const TrackPointer& pTrack,
                             const QStringList& numberMatchers) const;
    bool evaluateNumeric(const int value, const QString& expression) const;

    const QString m_tableName;
    const QString m_idColumn;
    const int m_columnCount;
    const QString m_columnsJoined;

    const ColumnCache m_columnCache;

    const std::unique_ptr<SearchQueryParser> m_pQueryParser;

    const StringCollator m_collator;

    QStringList m_searchColumns;
    QVector<int> m_searchColumnIndices;

    // Temporary storage for filterAndSort()

    QVector<TrackId> m_trackOrder;

    // Remember key and value of the most recent cache lookup to avoid querying
    // the global track cache again and again while populating the columns
    // of a single row. These members serve as a single-valued private cache.
    mutable TrackId m_recentTrackId;
    mutable TrackPointer m_recentTrackPtr;

    // This set is updated by signals from the Track object. It might contain
    // false positives, i.e. track ids of tracks that are neither cached nor
    // dirty. Each invocation of getRecentTrack() will take care of updating
    // this set by inserting and removing entries as required.
    mutable QSet<TrackId> m_dirtyTracks;

    bool m_bIndexBuilt;
    bool m_bIsCaching;
    QHash<TrackId, QVector<QVariant> > m_trackInfo;
    QSqlDatabase m_database;

    DISALLOW_COPY_AND_ASSIGN(BaseTrackCache);
};