1 #include "library/crate/cratetablemodel.h"
2 
3 #include <QtDebug>
4 
5 #include "library/dao/trackschema.h"
6 #include "library/trackcollection.h"
7 #include "library/trackcollectionmanager.h"
8 #include "mixer/playermanager.h"
9 #include "moc_cratetablemodel.cpp"
10 #include "track/track.h"
11 #include "util/db/fwdsqlquery.h"
12 
CrateTableModel(QObject * pParent,TrackCollectionManager * pTrackCollectionManager)13 CrateTableModel::CrateTableModel(QObject* pParent,
14                                  TrackCollectionManager* pTrackCollectionManager)
15         : BaseSqlTableModel(pParent, pTrackCollectionManager,
16                             "mixxx.db.model.crate") {
17 }
18 
~CrateTableModel()19 CrateTableModel::~CrateTableModel() {
20 }
21 
selectCrate(CrateId crateId)22 void CrateTableModel::selectCrate(CrateId crateId) {
23     //qDebug() << "CrateTableModel::setCrate()" << crateId;
24     if (crateId == m_selectedCrate) {
25         qDebug() << "Already focused on crate " << crateId;
26         return;
27     }
28     m_selectedCrate = crateId;
29 
30     QString tableName = QString("crate_%1").arg(m_selectedCrate.toString());
31     QStringList columns;
32     columns << LIBRARYTABLE_ID
33             << "'' AS " + LIBRARYTABLE_PREVIEW
34             // For sorting the cover art column we give LIBRARYTABLE_COVERART
35             // the same value as the cover hash.
36             << LIBRARYTABLE_COVERART_HASH + " AS " + LIBRARYTABLE_COVERART;
37     // We hide files that have been explicitly deleted in the library
38     // (mixxx_deleted = 0) from the view.
39     // They are kept in the database, because we treat crate membership as a
40     // track property, which persist over a hide / unhide cycle.
41     QString queryString = QString("CREATE TEMPORARY VIEW IF NOT EXISTS %1 AS "
42                                   "SELECT %2 FROM %3 "
43                                   "WHERE %4 IN (%5) "
44                                   "AND %6=0")
45                           .arg(tableName,
46                                columns.join(","),
47                                LIBRARY_TABLE,
48                                LIBRARYTABLE_ID,
49                                CrateStorage::formatSubselectQueryForCrateTrackIds(crateId),
50                                LIBRARYTABLE_MIXXXDELETED);
51     FwdSqlQuery(m_database, queryString).execPrepared();
52 
53     columns[0] = LIBRARYTABLE_ID;
54     columns[1] = LIBRARYTABLE_PREVIEW;
55     columns[2] = LIBRARYTABLE_COVERART;
56     setTable(tableName, LIBRARYTABLE_ID, columns,
57              m_pTrackCollectionManager->internalCollection()->getTrackSource());
58     setSearch("");
59     setDefaultSort(fieldIndex("artist"), Qt::AscendingOrder);
60 }
61 
addTrack(const QModelIndex & index,const QString & location)62 bool CrateTableModel::addTrack(const QModelIndex& index, const QString& location) {
63     Q_UNUSED(index);
64 
65     // This will only succeed if the file actually exist.
66     TrackFile fileInfo(location);
67     if (!fileInfo.checkFileExists()) {
68         qDebug() << "CrateTableModel::addTrack:"
69                 << "File"
70                 << location
71                 << "not found";
72         return false;
73     }
74 
75     // If a track is dropped but it isn't in the library, then add it because
76     // the user probably dropped a file from outside Mixxx into this crate.
77     // If the track is already contained in the library it will not insert
78     // a duplicate. It also handles unremoving logic if the track has been
79     // removed from the library recently and re-adds it.
80     const TrackPointer pTrack = m_pTrackCollectionManager->getOrAddTrack(
81             TrackRef::fromFileInfo(fileInfo));
82     if (!pTrack) {
83         qDebug() << "CrateTableModel::addTrack:"
84                 << "Failed to add track"
85                 << location
86                 << "to library";
87         return false;
88     }
89 
90     QList<TrackId> trackIds;
91     trackIds.append(pTrack->getId());
92     if (!m_pTrackCollectionManager->internalCollection()->addCrateTracks(m_selectedCrate, trackIds)) {
93         qDebug() << "CrateTableModel::addTrack:"
94                 << "Failed to add track"
95                 << location
96                 << "to crate"
97                 << m_selectedCrate;
98         return false;
99     }
100 
101     // TODO(rryan) just add the track don't select
102     select();
103     return true;
104 }
105 
getCapabilities() const106 TrackModel::CapabilitiesFlags CrateTableModel::getCapabilities() const {
107     CapabilitiesFlags caps =  TRACKMODELCAPS_NONE
108             | TRACKMODELCAPS_RECEIVEDROPS
109             | TRACKMODELCAPS_ADDTOPLAYLIST
110             | TRACKMODELCAPS_ADDTOCRATE
111             | TRACKMODELCAPS_ADDTOAUTODJ
112             | TRACKMODELCAPS_EDITMETADATA
113             | TRACKMODELCAPS_LOADTODECK
114             | TRACKMODELCAPS_LOADTOSAMPLER
115             | TRACKMODELCAPS_LOADTOPREVIEWDECK
116             | TRACKMODELCAPS_REMOVE_CRATE
117             | TRACKMODELCAPS_RESETPLAYED;
118     if (m_selectedCrate.isValid()) {
119         Crate crate;
120         if (m_pTrackCollectionManager->internalCollection()->crates().readCrateById(m_selectedCrate, &crate)) {
121             if (crate.isLocked()) {
122                 caps |= TRACKMODELCAPS_LOCKED;
123             }
124         } else {
125             qWarning() << "Failed to read create" << m_selectedCrate;
126         }
127     }
128     return caps;
129 }
130 
isColumnInternal(int column)131 bool CrateTableModel::isColumnInternal(int column) {
132     return column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_ID) ||
133             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED) ||
134             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_MIXXXDELETED) ||
135             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK) ||
136             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_KEY_ID)||
137             column == fieldIndex(ColumnCache::COLUMN_TRACKLOCATIONSTABLE_FSDELETED) ||
138             (PlayerManager::numPreviewDecks() == 0 &&
139              column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PREVIEW)) ||
140             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART_SOURCE) ||
141             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART_TYPE) ||
142             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART_LOCATION) ||
143             column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART_HASH);;
144 }
145 
addTracks(const QModelIndex & index,const QList<QString> & locations)146 int CrateTableModel::addTracks(const QModelIndex& index,
147                                const QList<QString>& locations) {
148     Q_UNUSED(index);
149     // If a track is dropped but it isn't in the library, then add it because
150     // the user probably dropped a file from outside Mixxx into this crate.
151     QList<TrackId> trackIds = m_pTrackCollectionManager->internalCollection()->resolveTrackIdsFromLocations(
152             locations);
153     if (!m_pTrackCollectionManager->internalCollection()->addCrateTracks(m_selectedCrate, trackIds)) {
154         qWarning() << "CrateTableModel::addTracks could not add"
155                  << locations.size()
156                  << "tracks to crate" << m_selectedCrate;
157         return 0;
158     }
159 
160     select();
161     return trackIds.size();
162 }
163 
removeTracks(const QModelIndexList & indices)164 void CrateTableModel::removeTracks(const QModelIndexList& indices) {
165     VERIFY_OR_DEBUG_ASSERT(m_selectedCrate.isValid()) {
166         return;
167     }
168     if (indices.empty()) {
169         return;
170     }
171 
172     Crate crate;
173     if (!m_pTrackCollectionManager->internalCollection()->crates().readCrateById(m_selectedCrate, &crate)) {
174         qWarning() << "Failed to read create" << m_selectedCrate;
175         return;
176     }
177 
178     VERIFY_OR_DEBUG_ASSERT(!crate.isLocked()) {
179         return;
180     }
181 
182     QList<TrackId> trackIds;
183     trackIds.reserve(indices.size());
184     for (const QModelIndex& index: indices) {
185         trackIds.append(getTrackId(index));
186     }
187     if (!m_pTrackCollectionManager->internalCollection()->removeCrateTracks(crate.getId(), trackIds)) {
188         qWarning() << "Failed to remove tracks from crate" << crate;
189         return;
190     }
191 
192     select();
193 }
194