diff --git a/res/images/library/ic_beatjump_right_active_bl.svg b/res/images/library/ic_beatjump_right_active_bl.svg new file mode 100644 index 000000000000..ec9429fbd978 --- /dev/null +++ b/res/images/library/ic_beatjump_right_active_bl.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/images/library/ic_beatjump_right_active_w.svg b/res/images/library/ic_beatjump_right_active_w.svg new file mode 100644 index 000000000000..69b0c0c2659a --- /dev/null +++ b/res/images/library/ic_beatjump_right_active_w.svg @@ -0,0 +1,49 @@ + + + + + + + diff --git a/res/images/library/ic_loop_active_bl.svg b/res/images/library/ic_loop_active_bl.svg new file mode 100644 index 000000000000..69740f76efcc --- /dev/null +++ b/res/images/library/ic_loop_active_bl.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/images/library/ic_loop_active_w.svg b/res/images/library/ic_loop_active_w.svg new file mode 100644 index 000000000000..e21ebb1bfec3 --- /dev/null +++ b/res/images/library/ic_loop_active_w.svg @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/res/mixxx.qrc b/res/mixxx.qrc index 828b89ffdf2f..207e9e9193bb 100644 --- a/res/mixxx.qrc +++ b/res/mixxx.qrc @@ -29,6 +29,11 @@ images/library/ic_library_traktor.svg images/library/ic_library_rekordbox.svg images/library/ic_library_serato.svg + + images/library/ic_loop_active_w.svg + images/library/ic_loop_active_bl.svg + images/library/ic_beatjump_right_active_w.svg + images/library/ic_beatjump_right_active_bl.svg images/mixxx_logo.svg images/icons/scalable/apps/mixxx.svg diff --git a/src/library/basetracktablemodel.cpp b/src/library/basetracktablemodel.cpp index c1cd4254dcb1..138c49e53a06 100644 --- a/src/library/basetracktablemodel.cpp +++ b/src/library/basetracktablemodel.cpp @@ -5,8 +5,10 @@ #include #include #include +#include #include "library/coverartcache.h" +#include "library/dao/cuedao.h" #include "library/dao/trackschema.h" #include "library/starrating.h" #include "library/tabledelegates/bpmdelegate.h" @@ -22,13 +24,16 @@ #include "library/tabledelegates/stardelegate.h" #include "library/trackcollection.h" #include "library/trackcollectionmanager.h" +#include "library/trackmodel.h" #include "mixer/playerinfo.h" #include "mixer/playermanager.h" #include "moc_basetracktablemodel.cpp" +#include "track/cue.h" #include "track/keyutils.h" #include "track/track.h" #include "util/assert.h" #include "util/clipboard.h" +#include "util/color/color.h" #include "util/color/colorpalette.h" #include "util/color/predefinedcolorpalettes.h" #include "util/datetime.h" @@ -73,6 +78,19 @@ QSqlDatabase cloneDatabase( pTrackCollectionManager->internalCollection()->database()); } +// For hotcue tooltip +inline QString posOrLengthToSeconds(double posOrLength, double sampleRate) { + if (sampleRate <= 0) { + // if no sampleRate -> 44.1kHz + + sampleRate = 44100.0; + } + + double seconds = posOrLength / sampleRate; + + return mixxx::Duration::formatTime(seconds, mixxx::Duration::Precision::CENTISECONDS); +} + } // anonymous namespace // static @@ -598,6 +616,19 @@ QVariant BaseTrackTableModel::roleValue( case ColumnCache::COLUMN_LIBRARYTABLE_RATING: case ColumnCache::COLUMN_LIBRARYTABLE_TIMESPLAYED: return rawValue; + // Eve: show the hotcue overview for in tooltips for following fields: + case ColumnCache::COLUMN_LIBRARYTABLE_TITLE: + case ColumnCache::COLUMN_LIBRARYTABLE_ARTIST: + case ColumnCache::COLUMN_LIBRARYTABLE_ALBUM: + case ColumnCache::COLUMN_LIBRARYTABLE_ALBUMARTIST: + case ColumnCache::COLUMN_LIBRARYTABLE_GENRE: + case ColumnCache::COLUMN_LIBRARYTABLE_COMPOSER: + case ColumnCache::COLUMN_LIBRARYTABLE_GROUPING: + case ColumnCache::COLUMN_LIBRARYTABLE_COMMENT: + if (role == Qt::ToolTipRole) { + return composeHotCueTooltip(index, rawValue.toString()); + } + break; default: // Same value as for Qt::DisplayRole (see below) break; @@ -1181,3 +1212,138 @@ QString BaseTrackTableModel::getFieldString( const QModelIndex& index, ColumnCache::Column column) const { return getFieldVariant(index, column).toString(); } + +QString BaseTrackTableModel::composeHotCueTooltip( + const QModelIndex& index, const QString& columnValue) const { + TrackPointer pTrack = getTrack(index); + if (!pTrack || !pTrack->getId().isValid()) { + return columnValue; + } + + // Get all cue points for this track + const QList cues = pTrack->getCuePoints(); + // Collect only hotcues + QList hotcues; + for (const auto& pCue : std::as_const(cues)) { + if (pCue && pCue->getHotCue() != Cue::kNoHotCue) { + hotcues.append(pCue); + } + } + + // No hotcues -> column value + if (hotcues.isEmpty()) { + return columnValue; + } + + // Sort hotcues by number + std::sort(hotcues.begin(), hotcues.end(), [](const CuePointer& a, const CuePointer& b) { + return a->getHotCue() < b->getHotCue(); + }); + + double sampleRate = pTrack->getSampleRate(); + + // Start HTML + QString tooltip = QStringLiteral(""); + + // Always show the column value (title, artist, composer, etc.) + if (!columnValue.isEmpty()) { + tooltip += QStringLiteral("%1").arg(columnValue.toHtmlEscaped()); + } + + if (!tooltip.isEmpty()) { + tooltip += QStringLiteral("

"); + } + tooltip += QStringLiteral("Hot Cues:"); + // In order to keep all properties aligned vertically we construct a html table. + // That means we need to wrap each field in tags. + tooltip += QStringLiteral(""); + + for (const auto& pHotcue : std::as_const(hotcues)) { + tooltip += QStringLiteral(""); // start table row + + // Make the cue appear like a colored hotcue button. + QColor cueColor = mixxx::RgbColor::toQColor(pHotcue->getColor()); + // Pick a contrasting color for the label, like in skins + // FIXME Use the skin's custoom dark/bright threshold? + const QColor textColor = Color::chooseColorByBrightness( + cueColor, + Qt::white, + Qt::black, + 127); + tooltip += QStringLiteral( + "") + .arg(cueColor.name(), + textColor.name(), + QString::number(pHotcue->getHotCue() + 1)); + // Cue position + tooltip += QStringLiteral("") + .arg(posOrLengthToSeconds(pHotcue->getPosition().value(), sampleRate)); + + // Add label if present + const QString label = pHotcue->getLabel(); + if (!label.isEmpty()) { + tooltip += QStringLiteral("").arg(label.toHtmlEscaped()); + } + + // Add type icon + // Icons are copied from LateNight Palemoon to images/library + // white icons were created from the original black icons + // still need to detect the background + QString iconHtml; + switch (pHotcue->getType()) { + case mixxx::CueType::Loop: + // white + iconHtml = + ""; + // black + // iconHtml = ""; + break; + case mixxx::CueType::Jump: + // white + iconHtml = + ""; + // black + // iconHtml = ""; + break; + default: + iconHtml = ""; + break; + } + + if (!iconHtml.isEmpty()) { + tooltip += QStringLiteral("").arg(iconHtml); + } else { + // Add empty cell for alignment when no icon + tooltip += QStringLiteral(""); + } + + // Add duration for saved loops/jumps + const auto length = pHotcue->getLengthFrames(); + if (length > 0) { + tooltip += QStringLiteral("") + .arg(posOrLengthToSeconds(length, sampleRate)); + } + tooltip += QStringLiteral(""); // end table row + } + + // Close table and HTML + tooltip += QStringLiteral("
" + "" + // Qt RichText/Html only supports a subset of Html, so we + // need to fake left/right padding with whitespaces. + " %3 " + "%1%1%1(%1)
"); + + return tooltip; +} diff --git a/src/library/basetracktablemodel.h b/src/library/basetracktablemodel.h index eb95d4bff0b0..71ea3cec0a48 100644 --- a/src/library/basetracktablemodel.h +++ b/src/library/basetracktablemodel.h @@ -7,10 +7,12 @@ #include "library/columncache.h" #include "library/trackmodel.h" +#include "track/cue.h" #include "track/track_decl.h" #include "util/color/colorpalette.h" class TrackCollectionManager; +class CuePointer; /// Base class for tabular track list views. /// @@ -307,4 +309,5 @@ class BaseTrackTableModel : public QAbstractTableModel, public TrackModel { static std::optional s_keyColorPalette; static bool s_bApplyPlayedTrackColor; + QString composeHotCueTooltip(const QModelIndex& index, const QString& columnValue) const; }; diff --git a/src/library/trackcollection.h b/src/library/trackcollection.h index 94cebb99cd63..84e8b596e2f6 100644 --- a/src/library/trackcollection.h +++ b/src/library/trackcollection.h @@ -68,6 +68,10 @@ class TrackCollection : public QObject, DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); return m_analysisDao; } + CueDAO& getCueDAO() { + DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); + return m_cueDao; + } void connectTrackSource(QSharedPointer pTrackSource); QWeakPointer disconnectTrackSource();