summaryrefslogtreecommitdiffstats
path: root/src/library/basetracktablemodel.cpp
diff options
context:
space:
mode:
authorUwe Klotz <uklotz@mixxx.org>2020-09-27 09:32:33 +0200
committerUwe Klotz <uklotz@mixxx.org>2020-11-15 22:19:49 +0100
commitfba5e10d1a372062684a23228a646d556f0b6d07 (patch)
tree32db38b64e4c5767007323a12e72e36a94a59202 /src/library/basetracktablemodel.cpp
parentebe12558bf65c4309ceb5df4decc270a546f3bf4 (diff)
BaseTrackTableModel: Validate and transform raw data from derived classes
Diffstat (limited to 'src/library/basetracktablemodel.cpp')
-rw-r--r--src/library/basetracktablemodel.cpp295
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;