summaryrefslogtreecommitdiffstats
path: root/src/library/basesqltablemodel.h
blob: 017c6df0958192aac4c6322b37e6e86c4d8dca8f (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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#pragma once

#include <QHash>
#include <QtSql>

#include "library/basetrackcache.h"
#include "library/dao/trackdao.h"
#include "library/trackmodel.h"
#include "library/columncache.h"
#include "util/class.h"

class TrackCollectionManager;

// BaseSqlTableModel is a custom-written SQL-backed table which aggressively
// caches the contents of the table and supports lightweight updates.
class BaseSqlTableModel : public QAbstractTableModel, public TrackModel {
    Q_OBJECT
  public:
    BaseSqlTableModel(QObject* pParent,
                      TrackCollectionManager* pTrackCollectionManager,
                      const char* settingsNamespace);
    ~BaseSqlTableModel() override;

    // Returns true if the BaseSqlTableModel has been initialized. Calling data
    // access methods on a BaseSqlTableModel which is not initialized is likely
    // to cause instability / crashes.
    bool initialized() const {
        return m_bInitialized;
    }

    void setSearch(const QString& searchText, const QString& extraFilter = QString());
    void setSort(int column, Qt::SortOrder order);

    int fieldIndex(ColumnCache::Column column) const;

    ///////////////////////////////////////////////////////////////////////////
    // Inherited from TrackModel
    ///////////////////////////////////////////////////////////////////////////
    int fieldIndex(const QString& fieldName) const final;

    ///////////////////////////////////////////////////////////////////////////
    // Inherited from QAbstractItemModel
    ///////////////////////////////////////////////////////////////////////////
    void sort(int column, Qt::SortOrder order) final;
    int rowCount(const QModelIndex& parent=QModelIndex()) const final;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const final;
    int columnCount(const QModelIndex& parent = QModelIndex()) const final;
    bool setHeaderData(int section, Qt::Orientation orientation,
                       const QVariant &value, int role = Qt::DisplayRole) final;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role=Qt::DisplayRole) const final;
    QMimeData* mimeData(const QModelIndexList &indexes) const final;

    ///////////////////////////////////////////////////////////////////////////
    //  Functions that might be reimplemented/overridden in derived classes
    ///////////////////////////////////////////////////////////////////////////
    //  This class also has protected variables that should be used in children
    //  m_database, m_pTrackCollection

    // calls readWriteFlags() by default, reimplement this if the child calls
    // should be readOnly
    Qt::ItemFlags flags(const QModelIndex &index) const override;

    ///////////////////////////////////////////////////////////////////////////
    // Inherited from TrackModel
    ///////////////////////////////////////////////////////////////////////////
    bool isColumnHiddenByDefault(int column) override;
    TrackPointer getTrack(const QModelIndex& index) const override;
    TrackId getTrackId(const QModelIndex& index) const override;
    const QLinkedList<int> getTrackRows(TrackId trackId) const override {
        return m_trackIdToRows.value(trackId);
    }
    QString getTrackLocation(const QModelIndex& index) const override;
    void hideTracks(const QModelIndexList& indices) override;
    void search(const QString& searchText, const QString& extraFilter = QString()) override;
    const QString currentSearch() const override;
    QAbstractItemDelegate* delegateForColumn(const int i, QObject* pParent) override;
    TrackModel::SortColumnId sortColumnIdFromColumnIndex(int column) override;
    int columnIndexFromSortColumnId(TrackModel::SortColumnId sortColumn) override;

    ///////////////////////////////////////////////////////////////////////////
    // Inherited from QAbstractItemModel
    ///////////////////////////////////////////////////////////////////////////
    bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;

  public slots:
    void select() override;

  protected:
    void setTable(const QString& tableName, const QString& trackIdColumn,
                  const QStringList& tableColumns,
                  QSharedPointer<BaseTrackCache> trackSource);
    void initHeaderData();
    virtual void initSortColumnMapping();

    // Use this if you want a model that is read-only.
    virtual Qt::ItemFlags readOnlyFlags(const QModelIndex &index) const;
    // Use this if you want a model that can be changed
    virtual Qt::ItemFlags readWriteFlags(const QModelIndex &index) const;

    TrackCollectionManager* const m_pTrackCollectionManager;

  protected:
    QList<TrackRef> getTrackRefs(const QModelIndexList& indices) const;

    QSqlDatabase m_database;

    QString m_previewDeckGroup;
    TrackId m_previewDeckTrackId;
    QString m_tableOrderBy;
    int m_columnIndexBySortColumnId[NUM_SORTCOLUMNIDS];
    QMap<int, TrackModel::SortColumnId> m_sortColumnIdByColumnIndex;

  private slots:
    virtual void tracksChanged(QSet<TrackId> trackIds);
    virtual void trackLoaded(QString group, TrackPointer pTrack);
    void refreshCell(int row, int column);

  private:
    // A simple helper function for initializing header title and width.  Note
    // that the ideal width of a column is based on the width of its data,
    // not the title string itself.
    void setHeaderProperties(ColumnCache::Column column, QString title, int defaultWidth);
    inline void setTrackValueForColumn(TrackPointer pTrack, int column, QVariant value);
    QVariant getBaseValue(const QModelIndex& index, int role = Qt::DisplayRole) const;
    // Set the columns used for searching. Names must correspond to the column
    // names in the table provided to setTable. Must be called after setTable is
    // called.
    QString orderByClause() const;

    struct RowInfo {
        TrackId trackId;
        int order;
        QVector<QVariant> metadata;

        bool operator<(const RowInfo& other) const {
            // -1 is greater than anything
            if (order == -1) {
                return false;
            } else if (other.order == -1) {
                return true;
            }
            return order < other.order;
        }
    };

    typedef QHash<TrackId, QLinkedList<int>> TrackId2Rows;

    void clearRows();
    void replaceRows(
            QVector<RowInfo>&& rows,
            TrackId2Rows&& trackIdToRows);

    QVector<RowInfo> m_rowInfo;

    QString m_tableName;
    QString m_idColumn;
    QSharedPointer<BaseTrackCache> m_trackSource;
    QStringList m_tableColumns;
    ColumnCache m_tableColumnCache;
    QList<SortColumn> m_sortColumns;
    bool m_bInitialized;
    QHash<TrackId, int> m_trackSortOrder;
    TrackId2Rows m_trackIdToRows;
    QString m_currentSearch;
    QString m_currentSearchFilter;
    QVector<QHash<int, QVariant> > m_headerInfo;
    QString m_trackSourceOrderBy;

    DISALLOW_COPY_AND_ASSIGN(BaseSqlTableModel);
};