![]() |
Mixxx
|
00001 // basesqltablemodel.h 00002 // Created by RJ Ryan (rryan@mit.edu) 1/29/2010 00003 00004 #include <QtAlgorithms> 00005 #include <QtDebug> 00006 #include <QTime> 00007 00008 #include "library/basesqltablemodel.h" 00009 00010 #include "library/starrating.h" 00011 #include "mixxxutils.cpp" 00012 00013 const bool sDebug = false; 00014 00015 BaseSqlTableModel::BaseSqlTableModel(QObject* pParent, 00016 TrackCollection* pTrackCollection, 00017 QSqlDatabase db, 00018 QString settingsNamespace) 00019 : QAbstractTableModel(pParent), 00020 TrackModel(db, settingsNamespace), 00021 m_pTrackCollection(pTrackCollection), 00022 m_trackDAO(m_pTrackCollection->getTrackDAO()), 00023 m_database(db) { 00024 m_bInitialized = false; 00025 m_bDirty = true; 00026 m_iSortColumn = 0; 00027 m_eSortOrder = Qt::AscendingOrder; 00028 } 00029 00030 BaseSqlTableModel::~BaseSqlTableModel() { 00031 } 00032 00033 void BaseSqlTableModel::initHeaderData() { 00034 // Set the column heading labels, rename them for translations and have 00035 // proper capitalization 00036 setHeaderData(fieldIndex(LIBRARYTABLE_TIMESPLAYED), 00037 Qt::Horizontal, tr("Played")); 00038 setHeaderData(fieldIndex(LIBRARYTABLE_ARTIST), 00039 Qt::Horizontal, tr("Artist")); 00040 setHeaderData(fieldIndex(LIBRARYTABLE_TITLE), 00041 Qt::Horizontal, tr("Title")); 00042 setHeaderData(fieldIndex(LIBRARYTABLE_ALBUM), 00043 Qt::Horizontal, tr("Album")); 00044 setHeaderData(fieldIndex(LIBRARYTABLE_GENRE), 00045 Qt::Horizontal, tr("Genre")); 00046 setHeaderData(fieldIndex(LIBRARYTABLE_YEAR), 00047 Qt::Horizontal, tr("Year")); 00048 setHeaderData(fieldIndex(LIBRARYTABLE_FILETYPE), 00049 Qt::Horizontal, tr("Type")); 00050 setHeaderData(fieldIndex(LIBRARYTABLE_LOCATION), 00051 Qt::Horizontal, tr("Location")); 00052 setHeaderData(fieldIndex(LIBRARYTABLE_COMMENT), 00053 Qt::Horizontal, tr("Comment")); 00054 setHeaderData(fieldIndex(LIBRARYTABLE_DURATION), 00055 Qt::Horizontal, tr("Duration")); 00056 setHeaderData(fieldIndex(LIBRARYTABLE_RATING), 00057 Qt::Horizontal, tr("Rating")); 00058 setHeaderData(fieldIndex(LIBRARYTABLE_BITRATE), 00059 Qt::Horizontal, tr("Bitrate")); 00060 setHeaderData(fieldIndex(LIBRARYTABLE_BPM), 00061 Qt::Horizontal, tr("BPM")); 00062 setHeaderData(fieldIndex(LIBRARYTABLE_TRACKNUMBER), 00063 Qt::Horizontal, tr("Track #")); 00064 setHeaderData(fieldIndex(LIBRARYTABLE_DATETIMEADDED), 00065 Qt::Horizontal, tr("Date Added")); 00066 setHeaderData(fieldIndex(PLAYLISTTRACKSTABLE_POSITION), 00067 Qt::Horizontal, tr("#")); 00068 setHeaderData(fieldIndex(LIBRARYTABLE_KEY), 00069 Qt::Horizontal, tr("Key")); 00070 } 00071 00072 QSqlDatabase BaseSqlTableModel::database() const { 00073 return m_database; 00074 } 00075 00076 bool BaseSqlTableModel::setHeaderData(int section, Qt::Orientation orientation, 00077 const QVariant &value, int role) { 00078 int numColumns = columnCount(); 00079 if (section < 0 || section >= numColumns) { 00080 return false; 00081 } 00082 00083 if (orientation != Qt::Horizontal) { 00084 // We only care about horizontal headers. 00085 return false; 00086 } 00087 00088 if (m_headerInfo.size() != numColumns) { 00089 m_headerInfo.resize(numColumns); 00090 } 00091 00092 m_headerInfo[section][role] = value; 00093 emit(headerDataChanged(orientation, section, section)); 00094 return true; 00095 } 00096 00097 QVariant BaseSqlTableModel::headerData(int section, Qt::Orientation orientation, 00098 int role) const { 00099 if (role != Qt::DisplayRole) 00100 return QAbstractTableModel::headerData(section, orientation, role); 00101 00102 if (orientation == Qt::Horizontal) { 00103 QVariant headerValue = m_headerInfo.value(section).value(role); 00104 if (!headerValue.isValid() && role == Qt::DisplayRole) { 00105 // Try EditRole if DisplayRole wasn't present 00106 headerValue = m_headerInfo.value(section).value(Qt::EditRole); 00107 } 00108 if (!headerValue.isValid()) { 00109 headerValue = QVariant(section).toString(); 00110 } 00111 return headerValue; 00112 } 00113 return QAbstractTableModel::headerData(section, orientation, role); 00114 } 00115 00116 QString BaseSqlTableModel::orderByClause() const { 00117 bool tableColumnSort = m_iSortColumn < m_tableColumns.size(); 00118 00119 if (m_iSortColumn < 0 || !tableColumnSort) { 00120 return ""; 00121 } 00122 00123 QString field = m_idColumn; 00124 if (m_iSortColumn != 0) { 00125 field = m_tableColumns[m_iSortColumn]; 00126 } 00127 00128 QString s; 00129 s.append(QLatin1String("ORDER BY ")); 00130 QString sort_field = QString("%1.%2").arg(m_tableName, field); 00131 s.append(sort_field); 00132 00133 s.append((m_eSortOrder == Qt::AscendingOrder) ? QLatin1String(" ASC") : 00134 QLatin1String(" DESC")); 00135 return s; 00136 } 00137 00138 void BaseSqlTableModel::select() { 00139 if (!m_bInitialized) { 00140 return; 00141 } 00142 00143 // We should be able to detect when a select() would be a no-op. The DAO's 00144 // do not currently broadcast signals for when common things happen. In the 00145 // future, we can turn this check on and avoid a lot of needless 00146 // select()'s. rryan 9/2011 00147 // if (!m_bDirty) { 00148 // if (sDebug) { 00149 // qDebug() << this << "Skipping non-dirty select()"; 00150 // } 00151 // return; 00152 // } 00153 00154 if (sDebug) { 00155 qDebug() << this << "select()"; 00156 } 00157 00158 QTime time; 00159 time.start(); 00160 00161 // Remove all the rows from the table. 00162 // TODO(rryan) we could edit the table in place instead of clearing it? 00163 if (m_rowInfo.size() > 0) { 00164 beginRemoveRows(QModelIndex(), 0, m_rowInfo.size()-1); 00165 m_rowInfo.clear(); 00166 m_trackIdToRows.clear(); 00167 endRemoveRows(); 00168 } 00169 00170 QString columns = m_tableColumnsJoined; 00171 QString orderBy = orderByClause(); 00172 QString queryString = QString("SELECT %1 FROM %2 %3") 00173 .arg(columns, m_tableName, orderBy); 00174 00175 if (sDebug) { 00176 qDebug() << this << "select() executing:" << queryString; 00177 } 00178 00179 QSqlQuery query(m_database); 00180 // This causes a memory savings since QSqlCachedResult (what QtSQLite uses) 00181 // won't allocate a giant in-memory table that we won't use at all. 00182 query.setForwardOnly(true); 00183 query.prepare(queryString); 00184 00185 if (!query.exec()) { 00186 qDebug() << this << "select() error:" << __FILE__ << __LINE__ 00187 << query.executedQuery() << query.lastError(); 00188 } 00189 00190 QSqlRecord record = query.record(); 00191 int idColumn = record.indexOf(m_idColumn); 00192 00193 QLinkedList<int> tableColumnIndices; 00194 foreach (QString column, m_tableColumns) { 00195 Q_ASSERT(record.indexOf(column) == m_tableColumnIndex[column]); 00196 tableColumnIndices.push_back(record.indexOf(column)); 00197 } 00198 int rows = query.size(); 00199 if (sDebug) { 00200 qDebug() << "Rows returned" << rows << m_rowInfo.size(); 00201 } 00202 00203 QVector<RowInfo> rowInfo; 00204 QSet<int> trackIds; 00205 while (query.next()) { 00206 int id = query.value(idColumn).toInt(); 00207 trackIds.insert(id); 00208 00209 RowInfo thisRowInfo; 00210 thisRowInfo.trackId = id; 00211 thisRowInfo.order = rowInfo.size(); 00212 // Get all the table columns and store them in the hash for this 00213 // row-info section. 00214 00215 foreach (int tableColumnIndex, tableColumnIndices) { 00216 thisRowInfo.metadata[tableColumnIndex] = 00217 query.value(tableColumnIndex); 00218 } 00219 rowInfo.push_back(thisRowInfo); 00220 } 00221 00222 if (sDebug) { 00223 qDebug() << "Rows actually received:" << rowInfo.size(); 00224 } 00225 00226 // Adjust sort column to remove table columns and add 1 to add an id column. 00227 int sortColumn = m_iSortColumn - m_tableColumns.size() + 1; 00228 00229 if (sortColumn < 0) { 00230 sortColumn = 0; 00231 } 00232 00233 // If we were sorting a table column, then secondary sort by id. TODO(rryan) 00234 // we should look into being able to drop the secondary sort to save time 00235 // but going for correctness first. 00236 m_trackSource->filterAndSort(trackIds, m_currentSearch, 00237 m_currentSearchFilter, 00238 sortColumn, m_eSortOrder, 00239 &m_trackSortOrder); 00240 00241 // Re-sort the track IDs since filterAndSort can change their order or mark 00242 // them for removal (by setting their row to -1). 00243 for (QVector<RowInfo>::iterator it = rowInfo.begin(); 00244 it != rowInfo.end(); ++it) { 00245 // If the sort column is not a track column then we will sort only to 00246 // separate removed tracks (order == -1) from present tracks (order == 00247 // 0). Otherwise we sort by the order that filterAndSort returned to us. 00248 if (sortColumn == 0) { 00249 it->order = m_trackSortOrder.contains(it->trackId) ? 0 : -1; 00250 } else { 00251 it->order = m_trackSortOrder.value(it->trackId, -1); 00252 } 00253 } 00254 00255 // RowInfo::operator< sorts by the order field, except -1 is placed at the 00256 // end so we can easily slice off rows that are no longer present. Stable 00257 // sort is necessary because the tracks may be in pre-sorted order so we 00258 // should not disturb that if we are only removing tracks. 00259 qStableSort(rowInfo.begin(), rowInfo.end()); 00260 00261 m_trackIdToRows.clear(); 00262 for (int i = 0; i < rowInfo.size(); ++i) { 00263 const RowInfo& row = rowInfo[i]; 00264 00265 if (row.order == -1) { 00266 // We've reached the end of valid rows. Resize rowInfo to cut off 00267 // this and all further elements. 00268 rowInfo.resize(i); 00269 break; 00270 } 00271 QLinkedList<int>& rows = m_trackIdToRows[row.trackId]; 00272 rows.push_back(i); 00273 } 00274 00275 // We're done! Issue the update signals and replace the master maps. 00276 beginInsertRows(QModelIndex(), 0, rowInfo.size()-1); 00277 m_rowInfo = rowInfo; 00278 m_bDirty = false; 00279 endInsertRows(); 00280 00281 int elapsed = time.elapsed(); 00282 qDebug() << this << "select() took" << elapsed << "ms"; 00283 } 00284 00285 void BaseSqlTableModel::setTable(const QString& tableName, 00286 const QString& idColumn, 00287 const QStringList& tableColumns, 00288 QSharedPointer<BaseTrackCache> trackSource) { 00289 Q_ASSERT(trackSource); 00290 if (sDebug) { 00291 qDebug() << this << "setTable" << tableName << tableColumns << idColumn; 00292 } 00293 m_tableName = tableName; 00294 m_idColumn = idColumn; 00295 m_tableColumns = tableColumns; 00296 m_tableColumnsJoined = tableColumns.join(","); 00297 00298 if (m_trackSource) { 00299 disconnect(m_trackSource.data(), SIGNAL(tracksChanged(QSet<int>)), 00300 this, SLOT(tracksChanged(QSet<int>))); 00301 } 00302 m_trackSource = trackSource; 00303 connect(m_trackSource.data(), SIGNAL(tracksChanged(QSet<int>)), 00304 this, SLOT(tracksChanged(QSet<int>))); 00305 00306 // Build a map from the column names to their indices, used by fieldIndex() 00307 m_tableColumnIndex.clear(); 00308 for (int i = 0; i < tableColumns.size(); ++i) { 00309 m_tableColumnIndex[m_tableColumns[i]] = i; 00310 } 00311 00312 m_bInitialized = true; 00313 m_bDirty = true; 00314 } 00315 00316 const QString BaseSqlTableModel::currentSearch() const { 00317 return m_currentSearch; 00318 } 00319 00320 void BaseSqlTableModel::setSearch(const QString& searchText, const QString extraFilter) { 00321 if (sDebug) { 00322 qDebug() << this << "setSearch" << searchText; 00323 } 00324 00325 bool searchIsDifferent = m_currentSearch.isNull() || m_currentSearch != searchText; 00326 bool filterDisabled = (m_currentSearchFilter.isNull() && extraFilter.isNull()); 00327 bool searchFilterIsDifferent = m_currentSearchFilter != extraFilter; 00328 00329 if (!searchIsDifferent && (filterDisabled || !searchFilterIsDifferent)) { 00330 // Do nothing if the filters are no different. 00331 return; 00332 } 00333 00334 m_currentSearch = searchText; 00335 m_currentSearchFilter = extraFilter; 00336 m_bDirty = true; 00337 } 00338 00339 void BaseSqlTableModel::search(const QString& searchText, const QString extraFilter) { 00340 if (sDebug) { 00341 qDebug() << this << "search" << searchText; 00342 } 00343 setSearch(searchText, extraFilter); 00344 select(); 00345 } 00346 00347 void BaseSqlTableModel::setSort(int column, Qt::SortOrder order) { 00348 if (sDebug) { 00349 qDebug() << this << "setSort()" << column << order; 00350 } 00351 00352 bool sortColumnChanged = m_iSortColumn != column; 00353 bool sortOrderChanged = m_eSortOrder != order; 00354 00355 if (!sortColumnChanged && !sortOrderChanged) { 00356 // Do nothing if the sort is not different. 00357 return; 00358 } 00359 00360 // TODO(rryan) optimization: if the sort column has not changed but the 00361 // order has, just reverse our ordering of the rows. 00362 00363 m_iSortColumn = column; 00364 m_eSortOrder = order; 00365 m_bDirty = true; 00366 } 00367 00368 void BaseSqlTableModel::sort(int column, Qt::SortOrder order) { 00369 if (sDebug) { 00370 qDebug() << this << "sort()" << column << order; 00371 } 00372 setSort(column, order); 00373 select(); 00374 } 00375 00376 int BaseSqlTableModel::rowCount(const QModelIndex& parent) const { 00377 int count = parent.isValid() ? 0 : m_rowInfo.size(); 00378 //qDebug() << "rowCount()" << parent << count; 00379 return count; 00380 } 00381 00382 int BaseSqlTableModel::columnCount(const QModelIndex& parent) const { 00383 if (parent.isValid()) { 00384 return 0; 00385 } 00386 00387 // Subtract one from trackSource::columnCount to ignore the id column 00388 int count = m_tableColumns.size() + 00389 (m_trackSource ? m_trackSource->columnCount() - 1: 0); 00390 return count; 00391 } 00392 00393 int BaseSqlTableModel::fieldIndex(const QString& fieldName) const { 00394 int tableIndex = m_tableColumnIndex.value(fieldName, -1); 00395 if (tableIndex > -1) { 00396 return tableIndex; 00397 } 00398 // Subtract one from the fieldIndex() result to account for the id column 00399 return m_trackSource ? (m_tableColumns.size() + 00400 m_trackSource->fieldIndex(fieldName) - 1) : -1; 00401 } 00402 00403 QVariant BaseSqlTableModel::data(const QModelIndex& index, int role) const { 00404 //qDebug() << this << "data()"; 00405 if (!index.isValid() || (role != Qt::DisplayRole && 00406 role != Qt::EditRole && 00407 role != Qt::CheckStateRole && 00408 role != Qt::ToolTipRole)) { 00409 return QVariant(); 00410 } 00411 00412 int row = index.row(); 00413 int column = index.column(); 00414 00415 // This value is the value in its most raw form. It was looked up either 00416 // from the SQL table or from the cached track layer. 00417 QVariant value = getBaseValue(index, role); 00418 00419 // Format the value based on whether we are in a tooltip, display, or edit 00420 // role 00421 switch (role) { 00422 case Qt::ToolTipRole: 00423 case Qt::DisplayRole: 00424 if (column == fieldIndex(LIBRARYTABLE_DURATION)) { 00425 if (qVariantCanConvert<int>(value)) { 00426 value = MixxxUtils::secondsToMinutes( 00427 qVariantValue<int>(value)); 00428 } 00429 } else if (column == fieldIndex(LIBRARYTABLE_RATING)) { 00430 if (qVariantCanConvert<int>(value)) 00431 value = qVariantFromValue(StarRating(value.toInt())); 00432 } else if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) { 00433 if (qVariantCanConvert<int>(value)) 00434 value = QString("(%1)").arg(value.toInt()); 00435 } else if (column == fieldIndex(LIBRARYTABLE_PLAYED)) { 00436 // Convert to a bool. Not really that useful since it gets 00437 // converted right back to a QVariant 00438 value = (value == "true") ? true : false; 00439 } else if (column == fieldIndex(LIBRARYTABLE_DATETIMEADDED)) { 00440 value = value.toDateTime(); 00441 } 00442 break; 00443 case Qt::EditRole: 00444 if (column == fieldIndex(LIBRARYTABLE_BPM)) { 00445 return value.toDouble(); 00446 } else if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) { 00447 return index.sibling( 00448 row, fieldIndex(LIBRARYTABLE_PLAYED)).data().toBool(); 00449 } else if (column == fieldIndex(LIBRARYTABLE_RATING)) { 00450 if (qVariantCanConvert<int>(value)) 00451 value = qVariantFromValue(StarRating(value.toInt())); 00452 } 00453 break; 00454 case Qt::CheckStateRole: 00455 if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) { 00456 bool played = index.sibling( 00457 row, fieldIndex(LIBRARYTABLE_PLAYED)).data().toBool(); 00458 value = played ? Qt::Checked : Qt::Unchecked; 00459 } 00460 break; 00461 default: 00462 break; 00463 } 00464 return value; 00465 } 00466 00467 bool BaseSqlTableModel::setData( 00468 const QModelIndex& index, const QVariant& value, int role) { 00469 if (!index.isValid()) 00470 return false; 00471 00472 int row = index.row(); 00473 int column = index.column(); 00474 00475 if (sDebug) { 00476 qDebug() << this << "setData() column:" << column << "value:" << value << "role:" << role; 00477 } 00478 00479 // Over-ride sets to TIMESPLAYED and re-direct them to PLAYED 00480 if (role == Qt::CheckStateRole) { 00481 if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) { 00482 QString val = value.toInt() > 0 ? QString("true") : QString("false"); 00483 QModelIndex playedIndex = index.sibling(index.row(), fieldIndex(LIBRARYTABLE_PLAYED)); 00484 return setData(playedIndex, val, Qt::EditRole); 00485 } 00486 return false; 00487 } 00488 00489 if (row < 0 || row >= m_rowInfo.size()) { 00490 return false; 00491 } 00492 00493 const RowInfo& rowInfo = m_rowInfo[row]; 00494 int trackId = rowInfo.trackId; 00495 00496 // You can't set something in the table columns because we have no way of 00497 // persisting it. 00498 const QHash<int, QVariant>& columns = rowInfo.metadata; 00499 if (columns.contains(column)) { 00500 return false; 00501 } 00502 00503 // TODO(rryan) ugly and only works because the mixxx library tables are the 00504 // only ones that aren't read-only. This should be moved into BTC. 00505 TrackPointer pTrack = m_trackDAO.getTrack(trackId); 00506 setTrackValueForColumn(pTrack, column, value); 00507 00508 // Do not save the track here. Changing the track dirties it and the caching 00509 // system will automatically save the track once it is unloaded from 00510 // memory. rryan 10/2010 00511 //m_trackDAO.saveTrack(pTrack); 00512 00513 return true; 00514 } 00515 00516 Qt::ItemFlags BaseSqlTableModel::flags(const QModelIndex &index) const { 00517 return readWriteFlags(index); 00518 } 00519 00520 Qt::ItemFlags BaseSqlTableModel::readWriteFlags( 00521 const QModelIndex &index) const { 00522 if (!index.isValid()) 00523 return Qt::ItemIsEnabled; 00524 00525 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); 00526 00527 // Enable dragging songs from this data model to elsewhere (like the 00528 // waveform widget to load a track into a Player). 00529 defaultFlags |= Qt::ItemIsDragEnabled; 00530 00531 int row = index.row(); 00532 int column = index.column(); 00533 00534 if ( column == fieldIndex(LIBRARYTABLE_FILETYPE) 00535 || column == fieldIndex(LIBRARYTABLE_LOCATION) 00536 || column == fieldIndex(LIBRARYTABLE_DURATION) 00537 || column == fieldIndex(LIBRARYTABLE_BITRATE) 00538 || column == fieldIndex(LIBRARYTABLE_DATETIMEADDED)) { 00539 return defaultFlags; 00540 } else if (column == fieldIndex(LIBRARYTABLE_TIMESPLAYED)) { 00541 return defaultFlags | Qt::ItemIsUserCheckable; 00542 } else { 00543 return defaultFlags | Qt::ItemIsEditable; 00544 } 00545 } 00546 00547 Qt::ItemFlags BaseSqlTableModel::readOnlyFlags(const QModelIndex &index) const { 00548 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); 00549 if (!index.isValid()) 00550 return Qt::ItemIsEnabled; 00551 00552 // Enable dragging songs from this data model to elsewhere (like the 00553 // waveform widget to load a track into a Player). 00554 defaultFlags |= Qt::ItemIsDragEnabled; 00555 00556 return defaultFlags; 00557 } 00558 00559 const QLinkedList<int> BaseSqlTableModel::getTrackRows(int trackId) const { 00560 if (m_trackIdToRows.contains(trackId)) { 00561 return m_trackIdToRows[trackId]; 00562 } 00563 return QLinkedList<int>(); 00564 } 00565 00566 int BaseSqlTableModel::getTrackId(const QModelIndex& index) const { 00567 if (!index.isValid()) { 00568 return -1; 00569 } 00570 return index.sibling(index.row(), fieldIndex(m_idColumn)).data().toInt(); 00571 } 00572 00573 QString BaseSqlTableModel::getTrackLocation(const QModelIndex& index) const { 00574 if (!index.isValid()) { 00575 return ""; 00576 } 00577 QString location = index.sibling( 00578 index.row(), fieldIndex("location")).data().toString(); 00579 return location; 00580 } 00581 00582 void BaseSqlTableModel::tracksChanged(QSet<int> trackIds) { 00583 if (sDebug) { 00584 qDebug() << this << "trackChanged" << trackIds.size(); 00585 } 00586 00587 const int numColumns = columnCount(); 00588 foreach (int trackId, trackIds) { 00589 QLinkedList<int> rows = getTrackRows(trackId); 00590 foreach (int row, rows) { 00591 //qDebug() << "Row in this result set was updated. Signalling update. track:" << trackId << "row:" << row; 00592 QModelIndex left = index(row, 0); 00593 QModelIndex right = index(row, numColumns); 00594 emit(dataChanged(left, right)); 00595 } 00596 } 00597 } 00598 00599 void BaseSqlTableModel::setTrackValueForColumn(TrackPointer pTrack, int column, 00600 QVariant value) { 00601 // TODO(XXX) Qt properties could really help here. 00602 if (fieldIndex(LIBRARYTABLE_ARTIST) == column) { 00603 pTrack->setArtist(value.toString()); 00604 } else if (fieldIndex(LIBRARYTABLE_TITLE) == column) { 00605 pTrack->setTitle(value.toString()); 00606 } else if (fieldIndex(LIBRARYTABLE_ALBUM) == column) { 00607 pTrack->setAlbum(value.toString()); 00608 } else if (fieldIndex(LIBRARYTABLE_YEAR) == column) { 00609 pTrack->setYear(value.toString()); 00610 } else if (fieldIndex(LIBRARYTABLE_GENRE) == column) { 00611 pTrack->setGenre(value.toString()); 00612 } else if (fieldIndex(LIBRARYTABLE_FILETYPE) == column) { 00613 pTrack->setType(value.toString()); 00614 } else if (fieldIndex(LIBRARYTABLE_TRACKNUMBER) == column) { 00615 pTrack->setTrackNumber(value.toString()); 00616 } else if (fieldIndex(LIBRARYTABLE_LOCATION) == column) { 00617 pTrack->setLocation(value.toString()); 00618 } else if (fieldIndex(LIBRARYTABLE_COMMENT) == column) { 00619 pTrack->setComment(value.toString()); 00620 } else if (fieldIndex(LIBRARYTABLE_DURATION) == column) { 00621 pTrack->setDuration(value.toInt()); 00622 } else if (fieldIndex(LIBRARYTABLE_BITRATE) == column) { 00623 pTrack->setBitrate(value.toInt()); 00624 } else if (fieldIndex(LIBRARYTABLE_BPM) == column) { 00625 // QVariant::toFloat needs >= QT 4.6.x 00626 pTrack->setBpm(static_cast<float>(value.toDouble())); 00627 } else if (fieldIndex(LIBRARYTABLE_PLAYED) == column) { 00628 pTrack->setPlayed(value.toBool()); 00629 } else if (fieldIndex(LIBRARYTABLE_TIMESPLAYED) == column) { 00630 pTrack->setTimesPlayed(value.toInt()); 00631 } else if (fieldIndex(LIBRARYTABLE_RATING) == column) { 00632 StarRating starRating = qVariantValue<StarRating>(value); 00633 pTrack->setRating(starRating.starCount()); 00634 } else if (fieldIndex(LIBRARYTABLE_KEY) == column) { 00635 pTrack->setKey(value.toString()); 00636 } 00637 } 00638 00639 QVariant BaseSqlTableModel::getBaseValue( 00640 const QModelIndex& index, int role) const { 00641 if (role != Qt::DisplayRole && 00642 role != Qt::ToolTipRole && 00643 role != Qt::EditRole) { 00644 return QVariant(); 00645 } 00646 00647 int row = index.row(); 00648 int column = index.column(); 00649 00650 if (row < 0 || row >= m_rowInfo.size()) { 00651 return QVariant(); 00652 } 00653 00654 // TODO(rryan) check range on column 00655 00656 const RowInfo& rowInfo = m_rowInfo[row]; 00657 int trackId = rowInfo.trackId; 00658 00659 // If the row info has the row-specific column, return that. 00660 const QHash<int, QVariant>& columns = rowInfo.metadata; 00661 if (columns.contains(column)) { 00662 if (sDebug) { 00663 qDebug() << "Returning table-column value" << columns[column] 00664 << "for column" << column << "role" << role; 00665 } 00666 return columns[column]; 00667 } 00668 00669 // Otherwise, return the information from the track record cache for the 00670 // given track ID 00671 if (m_trackSource) { 00672 // Subtract table columns from index to get the track source column 00673 // number and add 1 to skip over the id column. 00674 int trackSourceColumn = column - m_tableColumns.size() + 1; 00675 if (!m_trackSource->isCached(trackId)) { 00676 // Ideally Mixxx would have notified us of this via a signal, but in 00677 // the case that a track is not in the cache, we attempt to load it 00678 // on the fly. This will be a steep penalty to pay if there are tons 00679 // of these tracks in the table that are not cached. 00680 qDebug() << __FILE__ << __LINE__ 00681 << "Track" << trackId 00682 << "was not present in cache and had to be manually fetched."; 00683 m_trackSource->ensureCached(trackId); 00684 } 00685 return m_trackSource->data(trackId, trackSourceColumn); 00686 } 00687 return QVariant(); 00688 } 00689 00690 QMimeData* BaseSqlTableModel::mimeData(const QModelIndexList &indexes) const { 00691 QMimeData *mimeData = new QMimeData(); 00692 QList<QUrl> urls; 00693 00694 // The list of indexes we're given contains separates indexes for each 00695 // column, so even if only one row is selected, we'll have columnCount() 00696 // indices. We need to only count each row once: 00697 QSet<int> rows; 00698 00699 foreach (QModelIndex index, indexes) { 00700 if (!index.isValid() || rows.contains(index.row())) { 00701 continue; 00702 } 00703 rows.insert(index.row()); 00704 QUrl url = QUrl::fromLocalFile(getTrackLocation(index)); 00705 if (!url.isValid()) { 00706 qDebug() << this << "ERROR: invalid url" << url; 00707 continue; 00708 } 00709 urls.append(url); 00710 } 00711 mimeData->setUrls(urls); 00712 return mimeData; 00713 }