Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/library/basesqltablemodel.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines