![]() |
Mixxx
|
00001 00002 #include <QtCore> 00003 #include <QtSql> 00004 #include <QStringList> 00005 #include <QtConcurrentRun> 00006 #include <QMetaType> 00007 #include <QMessageBox> 00008 00009 #include "library/browse/browsetablemodel.h" 00010 #include "library/browse/browsethread.h" 00011 #include "soundsourceproxy.h" 00012 #include "mixxxutils.cpp" 00013 #include "playerinfo.h" 00014 #include "controlobject.h" 00015 #include "library/dao/trackdao.h" 00016 #include "audiotagger.h" 00017 00018 00019 BrowseTableModel::BrowseTableModel(QObject* parent, TrackCollection* pTrackCollection, 00020 RecordingManager* pRecordingManager) 00021 : TrackModel(pTrackCollection->getDatabase(), // TrackCollections m_db (defaultConnection) 00022 "mixxx.db.model.browse"), 00023 QStandardItemModel(parent), 00024 m_pTrackCollection(pTrackCollection), 00025 m_pRecordingManager(pRecordingManager) { 00026 QStringList header_data; 00027 header_data.insert(COLUMN_FILENAME, tr("Filename")); 00028 header_data.insert(COLUMN_ARTIST, tr("Artist")); 00029 header_data.insert(COLUMN_TITLE, tr("Title")); 00030 header_data.insert(COLUMN_ALBUM, tr("Album")); 00031 header_data.insert(COLUMN_TRACK_NUMBER, tr("Track #")); 00032 header_data.insert(COLUMN_YEAR, tr("Year")); 00033 header_data.insert(COLUMN_GENRE, tr("Genre")); 00034 header_data.insert(COLUMN_COMMENT, tr("Comment")); 00035 header_data.insert(COLUMN_DURATION, tr("Duration")); 00036 header_data.insert(COLUMN_BPM, tr("BPM")); 00037 header_data.insert(COLUMN_KEY, tr("Key")); 00038 header_data.insert(COLUMN_TYPE, tr("Type")); 00039 header_data.insert(COLUMN_BITRATE, tr("Bitrate")); 00040 header_data.insert(COLUMN_LOCATION, tr("Location")); 00041 00042 addSearchColumn(COLUMN_FILENAME); 00043 addSearchColumn(COLUMN_ARTIST); 00044 addSearchColumn(COLUMN_ALBUM); 00045 addSearchColumn(COLUMN_TITLE); 00046 addSearchColumn(COLUMN_GENRE); 00047 addSearchColumn(COLUMN_KEY); 00048 addSearchColumn(COLUMN_COMMENT); 00049 00050 setHorizontalHeaderLabels(header_data); 00051 //register the QList<T> as a metatype since we use QueuedConnection below 00052 qRegisterMetaType< QList< QList<QStandardItem*> > >("QList< QList<QStandardItem*> >"); 00053 qRegisterMetaType<BrowseTableModel*>("BrowseTableModel*"); 00054 00055 connect(BrowseThread::getInstance(), SIGNAL(clearModel(BrowseTableModel*)), 00056 this, SLOT(slotClear(BrowseTableModel*)), 00057 Qt::QueuedConnection); 00058 00059 connect(BrowseThread::getInstance(), SIGNAL(rowsAppended(const QList< QList<QStandardItem*> >&, BrowseTableModel*)), 00060 this, SLOT(slotInsert(const QList< QList<QStandardItem*> >&, BrowseTableModel*)), 00061 Qt::QueuedConnection); 00062 00063 } 00064 00065 BrowseTableModel::~BrowseTableModel() 00066 { 00067 00068 } 00069 00070 const QList<int>& BrowseTableModel::searchColumns() const { 00071 return m_searchColumns; 00072 } 00073 00074 void BrowseTableModel::addSearchColumn(int index) { 00075 m_searchColumns.push_back(index); 00076 } 00077 00078 void BrowseTableModel::setPath(QString absPath) 00079 { 00080 m_current_path = absPath; 00081 BrowseThread::getInstance()->executePopulation(m_current_path, this); 00082 00083 } 00084 00085 TrackPointer BrowseTableModel::getTrack(const QModelIndex& index) const 00086 { 00087 QString track_location = getTrackLocation(index); 00088 if(m_pRecordingManager->getRecordingLocation() == track_location) { 00089 QMessageBox::critical(0, tr("Mixxx Library"),tr("Could not load the following file because" 00090 " it is in use by Mixxx or another application.") 00091 + "\n" +track_location); 00092 return TrackPointer(); 00093 } 00094 00095 TrackDAO& track_dao = m_pTrackCollection->getTrackDAO(); 00096 int track_id = track_dao.getTrackId(track_location); 00097 if (track_id < 0) { 00098 // Add Track to library 00099 track_id = track_dao.addTrack(track_location, true); 00100 } 00101 00102 return track_dao.getTrack(track_id); 00103 } 00104 00105 QString BrowseTableModel::getTrackLocation(const QModelIndex& index) const 00106 { 00107 int row = index.row(); 00108 00109 QModelIndex index2 = this->index(row, COLUMN_LOCATION); 00110 return data(index2).toString(); 00111 00112 } 00113 00114 int BrowseTableModel::getTrackId(const QModelIndex& index) const { 00115 Q_UNUSED(index); 00116 // We can't implement this as it stands. 00117 return -1; 00118 } 00119 00120 const QLinkedList<int> BrowseTableModel::getTrackRows(int trackId) const { 00121 Q_UNUSED(trackId); 00122 // We can't implement this as it stands. 00123 return QLinkedList<int>(); 00124 } 00125 00126 void BrowseTableModel::search(const QString& searchText) { 00127 Q_UNUSED(searchText); 00128 } 00129 00130 const QString BrowseTableModel::currentSearch() const { 00131 return QString(); 00132 } 00133 00134 bool BrowseTableModel::isColumnInternal(int) { 00135 return false; 00136 } 00137 00138 bool BrowseTableModel::isColumnHiddenByDefault(int) { 00139 return false; 00140 } 00141 00142 void BrowseTableModel::moveTrack(const QModelIndex&, const QModelIndex&) { 00143 00144 } 00145 00146 QItemDelegate* BrowseTableModel::delegateForColumn(const int) { 00147 return NULL; 00148 } 00149 00150 void BrowseTableModel::removeTrack(const QModelIndex& index) 00151 { 00152 if(!index.isValid()) { 00153 return; 00154 } 00155 QStringList trackLocations; 00156 trackLocations.append(getTrackLocation(index)); 00157 removeTracks(trackLocations); 00158 } 00159 00160 void BrowseTableModel::removeTracks(const QModelIndexList& indices) 00161 { 00162 QStringList trackLocations; 00163 foreach (QModelIndex index, indices) { 00164 if (!index.isValid()) { 00165 continue; 00166 } 00167 trackLocations.append(getTrackLocation(index)); 00168 } 00169 removeTracks(trackLocations); 00170 } 00171 00172 void BrowseTableModel::removeTracks(QStringList trackLocations) { 00173 if (trackLocations.size() == 0) 00174 return; 00175 00176 // Ask user if s/he is sure 00177 if (QMessageBox::question(NULL, tr("Mixxx Library"), 00178 tr("Warning: This will permanently delete the following files:") 00179 + "\n" + trackLocations.join("\n") + "\n" + 00180 tr("Are you sure you want to delete these files from your computer?"), 00181 QMessageBox::Yes, QMessageBox::Abort) == QMessageBox::Abort) { 00182 return; 00183 } 00184 00185 00186 bool any_deleted = false; 00187 TrackDAO& track_dao = m_pTrackCollection->getTrackDAO(); 00188 00189 foreach (QString track_location, trackLocations) { 00190 // If track is in use or deletion fails, show an error message. 00191 if (isTrackInUse(track_location) || !QFile::remove(track_location)) { 00192 QMessageBox::critical(0, tr("Mixxx Library"),tr("Could not delete the following file because" 00193 " it is in use by Mixxx or another application:") + "\n" +track_location); 00194 continue; 00195 } 00196 00197 qDebug() << "BrowseFeature: User deleted track " << track_location; 00198 any_deleted = true; 00199 00200 // If the track was contained in the Mixxx library, delete it 00201 if (track_dao.trackExistsInDatabase(track_location)) { 00202 int id = track_dao.getTrackId(track_location); 00203 qDebug() << "BrowseFeature: Deletion affected database"; 00204 track_dao.removeTrack(id); 00205 } 00206 } 00207 00208 // Repopulate model if any tracks were actually deleted 00209 if (any_deleted) { 00210 BrowseThread::getInstance()->executePopulation(m_current_path, this); 00211 } 00212 } 00213 00214 bool BrowseTableModel::addTrack(const QModelIndex& index, QString location) 00215 { 00216 Q_UNUSED(index); 00217 Q_UNUSED(location); 00218 return false; 00219 } 00220 00221 QMimeData* BrowseTableModel::mimeData(const QModelIndexList &indexes) const { 00222 QMimeData *mimeData = new QMimeData(); 00223 QList<QUrl> urls; 00224 00225 //Ok, so the list of indexes we're given contains separates indexes for 00226 //each column, so even if only one row is selected, we'll have like 7 indexes. 00227 //We need to only count each row once: 00228 QList<int> rows; 00229 00230 foreach (QModelIndex index, indexes) { 00231 if (index.isValid()) { 00232 if (!rows.contains(index.row())) { 00233 rows.push_back(index.row()); 00234 QUrl url = QUrl::fromLocalFile(getTrackLocation(index)); 00235 if (!url.isValid()) 00236 qDebug() << "ERROR invalid url\n"; 00237 else { 00238 urls.append(url); 00239 qDebug() << "Appending URL:" << url; 00240 } 00241 } 00242 } 00243 } 00244 mimeData->setUrls(urls); 00245 return mimeData; 00246 } 00247 00248 void BrowseTableModel::slotClear(BrowseTableModel* caller_object) 00249 { 00250 if(caller_object == this) 00251 removeRows(0, rowCount()); 00252 } 00253 00254 void BrowseTableModel::slotInsert(const QList< QList<QStandardItem*> >& rows, BrowseTableModel* caller_object) { 00255 //There exists more than one BrowseTableModel in Mixxx 00256 //We only want to receive items here, this object has 'ordered' by the BrowserThread (singleton) 00257 if(caller_object == this) { 00258 //qDebug() << "BrowseTableModel::slotInsert"; 00259 for(int i=0; i < rows.size(); ++i) { 00260 appendRow(rows.at(i)); 00261 } 00262 } 00263 00264 } 00265 00266 TrackModel::CapabilitiesFlags BrowseTableModel::getCapabilities() const { 00267 // See src/library/trackmodel.h for the list of TRACKMODELCAPS 00268 return TRACKMODELCAPS_NONE 00269 | TRACKMODELCAPS_ADDTOPLAYLIST 00270 | TRACKMODELCAPS_ADDTOCRATE 00271 | TRACKMODELCAPS_ADDTOAUTODJ 00272 | TRACKMODELCAPS_LOADTODECK 00273 | TRACKMODELCAPS_LOADTOSAMPLER; 00274 } 00275 00276 Qt::ItemFlags BrowseTableModel::flags(const QModelIndex &index) const{ 00277 00278 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); 00279 00280 //Enable dragging songs from this data model to elsewhere (like the waveform 00281 //widget to load a track into a Player). 00282 defaultFlags |= Qt::ItemIsDragEnabled; 00283 00284 QString track_location = getTrackLocation(index); 00285 int column = index.column(); 00286 00287 if(isTrackInUse(track_location) || 00288 column == COLUMN_FILENAME || 00289 column == COLUMN_BITRATE || 00290 column == COLUMN_DURATION || 00291 column == COLUMN_TYPE) { 00292 return defaultFlags; 00293 } 00294 else{ 00295 return defaultFlags | Qt::ItemIsEditable; 00296 } 00297 } 00298 00299 bool BrowseTableModel::isTrackInUse(QString &track_location) const 00300 { 00301 int decks = ControlObject::getControl(ConfigKey("[Master]","num_decks"))->get(); 00302 //check if file is loaded to a deck 00303 for(int i=1; i <= decks; ++i) { 00304 TrackPointer loaded_track = PlayerInfo::Instance().getTrackInfo(QString("[Channel%1]").arg(i)); 00305 if(loaded_track && (loaded_track->getLocation() == track_location)) { 00306 return true; 00307 } 00308 } 00309 00310 if(m_pRecordingManager->getRecordingLocation() == track_location) { 00311 return true; 00312 } 00313 00314 return false; 00315 } 00316 00317 bool BrowseTableModel::setData(const QModelIndex &index, const QVariant &value, int role) 00318 { 00319 Q_UNUSED(role); 00320 00321 if(!index.isValid()) 00322 return false; 00323 qDebug() << "BrowseTableModel::setData(" << index.data() << ")"; 00324 int row = index.row(); 00325 int col = index.column(); 00326 QString track_location = getTrackLocation(index); 00327 AudioTagger tagger(track_location); 00328 00329 //set tagger information 00330 tagger.setArtist(this->index(row,COLUMN_ARTIST).data().toString()); 00331 tagger.setTitle(this->index(row,COLUMN_TITLE).data().toString()); 00332 tagger.setAlbum(this->index(row,COLUMN_ALBUM).data().toString()); 00333 tagger.setKey(this->index(row,COLUMN_KEY).data().toString()); 00334 tagger.setBpm(this->index(row,COLUMN_BPM).data().toString()); 00335 tagger.setComment(this->index(row,COLUMN_COMMENT).data().toString()); 00336 tagger.setTracknumber(this->index(row,COLUMN_TRACK_NUMBER).data().toString()); 00337 tagger.setYear(this->index(row,COLUMN_YEAR).data().toString()); 00338 tagger.setGenre(this->index(row,COLUMN_GENRE).data().toString()); 00339 00340 //check if one the item were edited 00341 if(col == COLUMN_ARTIST) { 00342 tagger.setArtist(value.toString()); 00343 } else if(col == COLUMN_TITLE) { 00344 tagger.setTitle(value.toString()); 00345 } else if(col == COLUMN_ALBUM) { 00346 tagger.setAlbum(value.toString()); 00347 } else if(col == COLUMN_BPM) { 00348 tagger.setBpm(value.toString()); 00349 } else if(col == COLUMN_KEY) { 00350 tagger.setKey(value.toString()); 00351 } else if(col == COLUMN_TRACK_NUMBER) { 00352 tagger.setTracknumber(value.toString()); 00353 } else if(col == COLUMN_COMMENT) { 00354 tagger.setComment(value.toString()); 00355 } else if(col == COLUMN_GENRE) { 00356 tagger.setGenre(value.toString()); 00357 } else if(col == COLUMN_YEAR) { 00358 tagger.setYear(value.toString()); 00359 } 00360 00361 00362 QStandardItem* item = itemFromIndex(index); 00363 if(tagger.save()) { 00364 //Modify underlying interalPointer object 00365 item->setText(value.toString()); 00366 return true; 00367 } 00368 else { 00369 //reset to old value in error 00370 item->setText(index.data().toString()); 00371 QMessageBox::critical(0, tr("Mixxx Library"),tr("Could not update file metadata.") 00372 + "\n" +track_location); 00373 return false; 00374 } 00375 }