diff options
author | Uwe Klotz <uklotz@mixxx.org> | 2020-09-27 09:32:33 +0200 |
---|---|---|
committer | Uwe Klotz <uklotz@mixxx.org> | 2020-11-15 22:19:49 +0100 |
commit | fba5e10d1a372062684a23228a646d556f0b6d07 (patch) | |
tree | 32db38b64e4c5767007323a12e72e36a94a59202 /src/library/basetracktablemodel.cpp | |
parent | ebe12558bf65c4309ceb5df4decc270a546f3bf4 (diff) |
BaseTrackTableModel: Validate and transform raw data from derived classes
Diffstat (limited to 'src/library/basetracktablemodel.cpp')
-rw-r--r-- | src/library/basetracktablemodel.cpp | 295 |
1 files changed, 205 insertions, 90 deletions
diff --git a/src/library/basetracktablemodel.cpp b/src/library/basetracktablemodel.cpp index f74acb5774..d3c22f7736 100644 --- a/src/library/basetracktablemodel.cpp +++ b/src/library/basetracktablemodel.cpp @@ -402,19 +402,14 @@ QVariant BaseTrackTableModel::data( } if (role == Qt::BackgroundRole) { - QModelIndex colorIndex = index.sibling( - index.row(), - fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COLOR)); - if (!colorIndex.isValid()) { + const auto rgbColorValue = rawSiblingValue( + index, + ColumnCache::COLUMN_LIBRARYTABLE_COLOR); + const auto rgbColor = mixxx::RgbColor::fromQVariant(rgbColorValue); + if (!rgbColor) { return QVariant(); } - const auto trackColor = - mixxx::RgbColor::fromQVariant( - rawValue(colorIndex)); - if (!trackColor) { - return QVariant(); - } - auto bgColor = mixxx::RgbColor::toQColor(trackColor); + auto bgColor = mixxx::RgbColor::toQColor(rgbColor); DEBUG_ASSERT(bgColor.isValid()); DEBUG_ASSERT(m_backgroundColorOpacity >= 0.0); DEBUG_ASSERT(m_backgroundColorOpacity <= 1.0); @@ -433,6 +428,37 @@ QVariant BaseTrackTableModel::data( return roleValue(index, rawValue(index), role); } +QVariant BaseTrackTableModel::rawValue( + const QModelIndex& index) const { + VERIFY_OR_DEBUG_ASSERT(index.isValid()) { + return QVariant(); + } + const auto field = mapColumn(index.column()); + if (field == ColumnCache::COLUMN_LIBRARYTABLE_INVALID) { + return QVariant(); + } + return rawSiblingValue(index, field); +} + +QVariant BaseTrackTableModel::rawSiblingValue( + const QModelIndex& index, + ColumnCache::Column siblingField) const { + VERIFY_OR_DEBUG_ASSERT(index.isValid()) { + return QVariant(); + } + VERIFY_OR_DEBUG_ASSERT(siblingField != ColumnCache::COLUMN_LIBRARYTABLE_INVALID) { + return QVariant(); + } + const auto siblingColumn = fieldIndex(siblingField); + DEBUG_ASSERT(siblingColumn >= 0); + VERIFY_OR_DEBUG_ASSERT(siblingColumn != index.column()) { + // Prevent infinite recursion + return QVariant(); + } + const auto siblingIndex = index.sibling(index.row(), siblingColumn); + return rawValue(siblingIndex); +} + bool BaseTrackTableModel::setData( const QModelIndex& index, const QVariant& value, @@ -443,11 +469,15 @@ bool BaseTrackTableModel::setData( if (role == Qt::CheckStateRole) { const auto val = value.toInt() > 0; if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED)) { - QModelIndex playedIndex = index.sibling(index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)); + QModelIndex playedIndex = index.sibling( + index.row(), + fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)); return setData(playedIndex, val, Qt::EditRole); } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM)) { - QModelIndex bpmLockindex = index.sibling(index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK)); - return setData(bpmLockindex, val, Qt::EditRole); + QModelIndex bpmLockedIndex = index.sibling( + index.row(), + fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK)); + return setData(bpmLockedIndex, val, Qt::EditRole); } return false; } @@ -499,133 +529,218 @@ QVariant BaseTrackTableModel::roleValue( const QModelIndex& index, QVariant&& rawValue, int role) const { - const int column = index.column(); - // Format the value based on whether we are in a tooltip, - // display, or edit role + const auto field = mapColumn(index.column()); + if (field == ColumnCache::COLUMN_LIBRARYTABLE_INVALID) { + return std::move(rawValue); + } switch (role) { case Qt::ToolTipRole: - if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COLOR)) { + if (field == ColumnCache::COLUMN_LIBRARYTABLE_COLOR) { return mixxx::RgbColor::toQString(mixxx::RgbColor::fromQVariant(rawValue)); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_COVERART)) { + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_COVERART) { return composeCoverArtToolTipHtml(index); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PREVIEW)) { + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_PREVIEW) { return QVariant(); } M_FALLTHROUGH_INTENDED; case Qt::DisplayRole: - if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_DURATION)) { + if (field == ColumnCache::COLUMN_LIBRARYTABLE_DURATION) { + if (rawValue.isNull()) { + return QVariant(); + } + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<double>()) { + return QVariant(); + } bool ok; const auto duration = rawValue.toDouble(&ok); - if (ok && duration >= 0) { - return mixxx::Duration::formatTime( - duration, - mixxx::Duration::Precision::SECONDS); - } else { + VERIFY_OR_DEBUG_ASSERT(ok && duration >= 0) { return QVariant(); } - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_RATING)) { - VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert(QMetaType::Int)) { + return mixxx::Duration::formatTime( + duration, + mixxx::Duration::Precision::SECONDS); + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_RATING) { + if (rawValue.isNull()) { return QVariant(); } - return QVariant::fromValue(StarRating(rawValue.toInt())); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)) { - return rawValue.toBool(); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED)) { - VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert(QMetaType::Int)) { + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<int>()) { return QVariant(); } - return QString("(%1)").arg(rawValue.toInt()); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_DATETIMEADDED) || - column == fieldIndex(ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_DATETIMEADDED)) { - return mixxx::localDateTimeFromUtc(mixxx::convertVariantToDateTime(rawValue)); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM)) { bool ok; - const auto bpmValue = rawValue.toDouble(&ok); - if (ok && bpmValue > 0.0) { - return QString("%1").arg(bpmValue, 0, 'f', 1); + const auto starCount = rawValue.toInt(&ok); + VERIFY_OR_DEBUG_ASSERT(ok && starCount >= StarRating::kMinStarCount) { + return QVariant(); + } + return QVariant::fromValue(StarRating(starCount)); + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED) { + if (rawValue.isNull()) { + return QVariant(); + } + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<int>()) { + return QVariant(); + } + bool ok; + const auto timesPlayed = rawValue.toInt(&ok); + VERIFY_OR_DEBUG_ASSERT(ok && timesPlayed >= 0) { + return QVariant(); + } + return QString("(%1)").arg(timesPlayed); + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_DATETIMEADDED || + field == ColumnCache::COLUMN_PLAYLISTTRACKSTABLE_DATETIMEADDED) { + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<QDateTime>()) { + return QVariant(); + } + return mixxx::localDateTimeFromUtc(rawValue.toDateTime()); + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_BPM) { + mixxx::Bpm bpm; + if (!rawValue.isNull()) { + if (rawValue.canConvert<mixxx::Bpm>()) { + bpm = rawValue.value<mixxx::Bpm>(); + } else { + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<double>()) { + return QVariant(); + } + bool ok; + const auto bpmValue = rawValue.toDouble(&ok); + VERIFY_OR_DEBUG_ASSERT(ok) { + return QVariant(); + } + bpm = mixxx::Bpm(bpmValue); + } + } + if (bpm.hasValue()) { + return QString("%1").arg(bpm.getValue(), 0, 'f', 1); } else { return QChar('-'); } - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK)) { - return rawValue.toBool(); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_YEAR)) { - return mixxx::TrackMetadata::formatCalendarYear(rawValue.toString()); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_TRACKNUMBER)) { - const auto trackNumber = rawValue.toInt(0); - if (trackNumber > 0) { - return std::move(rawValue); - } else { - // clear invalid values + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_YEAR) { + if (rawValue.isNull()) { return QVariant(); } - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BITRATE)) { - int bitrateValue = rawValue.toInt(0); - if (bitrateValue > 0) { + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<QString>()) { + return QVariant(); + } + bool ok; + const auto year = mixxx::TrackMetadata::formatCalendarYear(rawValue.toString(), &ok); + if (!ok) { + return QVariant(); + } + return year; + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_BITRATE) { + if (rawValue.isNull()) { + return QVariant(); + } + if (rawValue.canConvert<mixxx::audio::Bitrate>()) { + // return value as is return std::move(rawValue); } else { - // clear invalid values - return QVariant(); + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<int>()) { + return QVariant(); + } + bool ok; + const auto bitrateValue = rawValue.toInt(&ok); + VERIFY_OR_DEBUG_ASSERT(ok) { + return QVariant(); + } + if (mixxx::audio::Bitrate(bitrateValue).isValid()) { + // return value as is + return std::move(rawValue); + } else { + // clear invalid values + return QVariant(); + } } - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_KEY)) { + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_KEY) { // If we know the semantic key via the LIBRARYTABLE_KEY_ID // column (as opposed to the string representation of the key // currently stored in the DB) then lookup the key and render it // using the user's selected notation. - int keyIdColumn = fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_KEY_ID); - if (keyIdColumn == -1) { - // Otherwise, just use the column value + const QVariant keyCodeValue = rawSiblingValue( + index, + ColumnCache::COLUMN_LIBRARYTABLE_KEY_ID); + if (keyCodeValue.isNull()) { + // Otherwise, just use the column value as is return std::move(rawValue); } - mixxx::track::io::key::ChromaticKey key = - KeyUtils::keyFromNumericValue( - index.sibling(index.row(), keyIdColumn).data().toInt()); + // Convert or clear invalid values + VERIFY_OR_DEBUG_ASSERT(keyCodeValue.canConvert<int>()) { + return QVariant(); + } + bool ok; + const auto keyCode = keyCodeValue.toInt(&ok); + VERIFY_OR_DEBUG_ASSERT(ok) { + return QVariant(); + } + const auto key = KeyUtils::keyFromNumericValue(keyCode); if (key == mixxx::track::io::key::INVALID) { - // clear invalid values return QVariant(); } - // Render this key with the user-provided notation. + // Render the key with the user-provided notation return KeyUtils::keyToString(key); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_REPLAYGAIN)) { + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_REPLAYGAIN) { + if (rawValue.isNull()) { + return QVariant(); + } + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<double>()) { + return QVariant(); + } bool ok; const auto gainValue = rawValue.toDouble(&ok); - return ok ? mixxx::ReplayGain::ratioToString(gainValue) : QString(); + VERIFY_OR_DEBUG_ASSERT(ok) { + return QVariant(); + } + return mixxx::ReplayGain::ratioToString(gainValue); } // Otherwise, just use the column value break; case Qt::EditRole: - if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM)) { + if (field == ColumnCache::COLUMN_LIBRARYTABLE_BPM) { bool ok; const auto bpmValue = rawValue.toDouble(&ok); - return ok ? bpmValue : 0.0; - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED)) { + return ok ? bpmValue : mixxx::Bpm().getValue(); + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED) { return index.sibling( - index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)) - .data() - .toBool(); - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_RATING)) { - VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert(QMetaType::Int)) { + index.row(), + fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)) + .data() + .toBool(); + } else if (field == ColumnCache::COLUMN_LIBRARYTABLE_RATING) { + VERIFY_OR_DEBUG_ASSERT(rawValue.canConvert<int>()) { return QVariant(); } return QVariant::fromValue(StarRating(rawValue.toInt())); } // Otherwise, just use the column value break; - case Qt::CheckStateRole: - if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED)) { - bool played = index.sibling( - index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_PLAYED)) - .data() - .toBool(); - return played ? Qt::Checked : Qt::Unchecked; - } else if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM)) { - bool locked = index.sibling( - index.row(), fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK)) - .data() - .toBool(); - return locked ? Qt::Checked : Qt::Unchecked; + case Qt::CheckStateRole: { + QVariant boolValue; + switch (field) { + case ColumnCache::COLUMN_LIBRARYTABLE_PREVIEW: + boolValue = rawValue; + break; + case ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED: + boolValue = rawSiblingValue( + index, + ColumnCache::COLUMN_LIBRARYTABLE_PLAYED); + break; + case ColumnCache::COLUMN_LIBRARYTABLE_BPM: + boolValue = rawSiblingValue( + index, + ColumnCache::COLUMN_LIBRARYTABLE_BPM_LOCK); + break; + default: + // No check state supported + return QVariant(); } - // No check state supported - return QVariant(); + // Flags in the database are stored as integers that are + // convertible to bool. + if (!boolValue.isNull() && boolValue.canConvert<bool>()) { + return boolValue.toBool() ? Qt::Checked : Qt::Unchecked; + } else { + // Undecidable + return Qt::PartiallyChecked; + } + } default: DEBUG_ASSERT(!"unexpected role"); break; |