diff --git a/src/coreservices.cpp b/src/coreservices.cpp index 26b6b9714cb..62360aec814 100644 --- a/src/coreservices.cpp +++ b/src/coreservices.cpp @@ -449,6 +449,12 @@ void CoreServices::initialize(QApplication* pApp) { m_pTrackCollectionManager->startLibraryScan(); } + // Check if we upgrade from before Mixxx 2.1 + // If yes, remove hidden tracks from ALL playlists. + if (m_pSettingsManager->upgradedFrom21OrEarlier()) { + m_pTrackCollectionManager->removeHiddenTracksFromPlaylists(); + } + // This has to be done before m_pSoundManager->setupDevices() // https://github.com/mixxxdj/mixxx/issues/9188 m_pPlayerManager->loadSamplers(); diff --git a/src/library/dao/playlistdao.cpp b/src/library/dao/playlistdao.cpp index de1f3568be6..6ff7b087647 100644 --- a/src/library/dao/playlistdao.cpp +++ b/src/library/dao/playlistdao.cpp @@ -478,9 +478,9 @@ QList> PlaylistDAO::getPlaylists(const HiddenType hidden) co QSqlQuery query(m_database); query.prepare( mixxx::DbConnection::collateLexicographically( - QString("SELECT id, name FROM Playlists " - "WHERE hidden = %1 " - "ORDER BY name") + QStringLiteral("SELECT id, name FROM Playlists " + "WHERE hidden = %1 " + "ORDER BY name") .arg(hidden))); QList> playlists; @@ -498,6 +498,31 @@ QList> PlaylistDAO::getPlaylists(const HiddenType hidden) co return playlists; } +QList PlaylistDAO::getPlaylistIdsByType(const HiddenType hidden) const { + // qDebug() << "PlaylistDAO::getPlaylistIds(hidden =" << hidden + // << QThread::currentThread() << m_database.connectionName(); + + QSqlQuery query(m_database); + query.prepare( + mixxx::DbConnection::collateLexicographically( + QStringLiteral("SELECT id FROM Playlists " + "WHERE hidden = %1") + .arg(hidden))); + + QList playlistIds; + + if (!query.exec()) { + LOG_FAILED_QUERY(query); + return playlistIds; + } + + while (query.next()) { + const int id = query.value(0).toInt(); + playlistIds.append(id); + } + return playlistIds; +} + int PlaylistDAO::getPlaylistId(const int index) const { //qDebug() << "PlaylistDAO::getPlaylistId" // << QThread::currentThread() << m_database.connectionName(); @@ -556,6 +581,15 @@ bool PlaylistDAO::isHidden(const int playlistId) const { return true; } +void PlaylistDAO::removeHiddenTracksFromPlaylists() { + // Remove tracks from Playlists, keep in AutoDJ and History + const QList ids = getPlaylistIdsByType(HiddenType::PLHT_NOT_HIDDEN); + + for (int id : std::as_const(ids)) { + removeHiddenTracks(id); + } +} + void PlaylistDAO::removeHiddenTracks(const int playlistId) { ScopedTransaction transaction(m_database); // This query deletes all tracks marked as hidden and all diff --git a/src/library/dao/playlistdao.h b/src/library/dao/playlistdao.h index fc66e23dcf9..0cb3ae0ca7a 100644 --- a/src/library/dao/playlistdao.h +++ b/src/library/dao/playlistdao.h @@ -63,6 +63,7 @@ class PlaylistDAO : public QObject, public virtual DAO { unsigned int playlistCount() const; // Get all playlist ids and names of a specific type QList> getPlaylists(const HiddenType hidden) const; + QList getPlaylistIdsByType(const HiddenType hidden) const; // Find out the name of the playlist at the given Id QString getPlaylistName(const int playlistId) const; // Get the playlist id by its name @@ -80,6 +81,8 @@ class PlaylistDAO : public QObject, public virtual DAO { int getMaxPosition(const int playlistId) const; // Remove a track from all playlists void removeTracksFromPlaylists(const QList& trackIds, bool purged = false); + // removes all hidden Tracks from ALL playlists + void removeHiddenTracksFromPlaylists(); // removes all hidden and purged Tracks from the playlist void removeHiddenTracks(const int playlistId); // Remove a track from a playlist diff --git a/src/library/playlisttablemodel.cpp b/src/library/playlisttablemodel.cpp index 90ae39fbd98..bf9b3e6a4dc 100644 --- a/src/library/playlisttablemodel.cpp +++ b/src/library/playlisttablemodel.cpp @@ -15,11 +15,9 @@ const QString kModelName = "playlist:"; PlaylistTableModel::PlaylistTableModel(QObject* parent, TrackCollectionManager* pTrackCollectionManager, - const char* settingsNamespace, - bool keepHiddenTracks) + const char* settingsNamespace) : TrackSetTableModel(parent, pTrackCollectionManager, settingsNamespace), - m_iPlaylistId(kInvalidPlaylistId), - m_keepHiddenTracks(keepHiddenTracks) { + m_iPlaylistId(kInvalidPlaylistId) { connect(&m_pTrackCollectionManager->internalCollection()->getPlaylistDAO(), &PlaylistDAO::tracksAdded, this, @@ -146,15 +144,6 @@ void PlaylistTableModel::selectPlaylist(int playlistId) { } m_iPlaylistId = playlistId; - - if (!m_keepHiddenTracks) { - // From Mixxx 2.1 we drop tracks that have been explicitly deleted - // in the library (mixxx_deleted = 0) from playlists. - // These invisible tracks, consuming a playlist position number were - // a source user of confusion in the past. - m_pTrackCollectionManager->internalCollection()->getPlaylistDAO().removeHiddenTracks(m_iPlaylistId); - } - QString playlistTableName = "playlist_" + QString::number(m_iPlaylistId); QSqlQuery query(m_database); FieldEscaper escaper(m_database); diff --git a/src/library/playlisttablemodel.h b/src/library/playlisttablemodel.h index cbdc930f645..b0caea243a7 100644 --- a/src/library/playlisttablemodel.h +++ b/src/library/playlisttablemodel.h @@ -9,8 +9,7 @@ class PlaylistTableModel final : public TrackSetTableModel { public: PlaylistTableModel(QObject* parent, TrackCollectionManager* pTrackCollectionManager, - const char* settingsNamespace, - bool keepHiddenTracks = false); + const char* settingsNamespace); ~PlaylistTableModel() final = default; void selectPlaylist(int playlistId = -1 /* kInvalidPlaylistId */); @@ -51,6 +50,5 @@ class PlaylistTableModel final : public TrackSetTableModel { void initSortColumnMapping() override; int m_iPlaylistId; - bool m_keepHiddenTracks; QHash m_searchTexts; }; diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp index 011ef73e4ed..518c4eefcc1 100644 --- a/src/library/trackcollection.cpp +++ b/src/library/trackcollection.cpp @@ -274,7 +274,8 @@ QList TrackCollection::resolveTrackIdsFromLocations( bool TrackCollection::hideTracks(const QList& trackIds) { DEBUG_ASSERT_QOBJECT_THREAD_AFFINITY(this); - // Warn if tracks have a playlist membership + // Warn if tracks are in playlists. + // Always keep them in History playlists. QSet allPlaylistIds; for (const auto& trackId: trackIds) { QSet playlistIds; @@ -286,6 +287,9 @@ bool TrackCollection::hideTracks(const QList& trackIds) { } } + // TODO show list with checkboxes for playlists to leave unchanged + // use QListWidget, see WTrackMenu::slotRemoveFromDisk() + bool removeFromplaylists = false; if (!allPlaylistIds.isEmpty()) { QStringList playlistNames; playlistNames.reserve(allPlaylistIds.count()); @@ -293,21 +297,20 @@ bool TrackCollection::hideTracks(const QList& trackIds) { playlistNames.append(m_playlistDao.getPlaylistName(playlistId)); } - QString playlistNamesSection = - "\n\n\"" % + const QString playlistNamesSection = + "\n\n\"" % // prepend linebreak playlistNames.join("\"\n\"") % - "\"\n\n"; + "\"\n\n"; // append linebreak - if (QMessageBox::question( - nullptr, + QMessageBox::StandardButton btn = QMessageBox::question(nullptr, tr("Hiding tracks"), tr("The selected tracks are in the following playlists:" - "%1" - "Hiding them will remove them from these playlists. Continue?") + "%1" + "Do you want to remove them from these playlists. Continue?") .arg(playlistNamesSection), - QMessageBox::Ok | QMessageBox::Cancel) != QMessageBox::Ok) { - return false; - } + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + removeFromplaylists = btn == QMessageBox::Yes; } // Transactional @@ -322,7 +325,9 @@ bool TrackCollection::hideTracks(const QList& trackIds) { return false; } - m_playlistDao.removeTracksFromPlaylists(trackIds); + if (removeFromplaylists) { + m_playlistDao.removeTracksFromPlaylists(trackIds); + } // Post-processing // TODO(XXX): Move signals from TrackDAO to TrackCollection diff --git a/src/library/trackcollectionmanager.cpp b/src/library/trackcollectionmanager.cpp index 397b87bac74..37dcf2477c8 100644 --- a/src/library/trackcollectionmanager.cpp +++ b/src/library/trackcollectionmanager.cpp @@ -602,6 +602,10 @@ bool TrackCollectionManager::updateTrackGenre( genre); } +void TrackCollectionManager::removeHiddenTracksFromPlaylists() { + m_pInternalCollection->getPlaylistDAO().removeHiddenTracksFromPlaylists(); +} + #if defined(__EXTRA_METADATA__) bool TrackCollectionManager::updateTrackMood( Track* pTrack, diff --git a/src/library/trackcollectionmanager.h b/src/library/trackcollectionmanager.h index edf48753e05..e8f43695fa3 100644 --- a/src/library/trackcollectionmanager.h +++ b/src/library/trackcollectionmanager.h @@ -66,6 +66,8 @@ class TrackCollectionManager: public QObject, const QString& mood) const; #endif // __EXTRA_METADATA__ + void removeHiddenTracksFromPlaylists(); + bool hideTracks(const QList& trackIds) const; bool unhideTracks(const QList& trackIds) const; void hideAllTracks(const QDir& rootDir) const; diff --git a/src/library/trackset/baseplaylistfeature.cpp b/src/library/trackset/baseplaylistfeature.cpp index 71362d34885..9f2a088c9d0 100644 --- a/src/library/trackset/baseplaylistfeature.cpp +++ b/src/library/trackset/baseplaylistfeature.cpp @@ -42,15 +42,13 @@ BasePlaylistFeature::BasePlaylistFeature( PlaylistTableModel* pModel, const QString& rootViewName, const QString& iconName, - const QString& countsDurationTableName, - bool keepHiddenTracks) + const QString& countsDurationTableName) : BaseTrackSetFeature(pLibrary, pConfig, rootViewName, iconName), m_playlistDao(pLibrary->trackCollectionManager() - ->internalCollection() - ->getPlaylistDAO()), + ->internalCollection() + ->getPlaylistDAO()), m_pPlaylistTableModel(pModel), - m_countsDurationTableName(countsDurationTableName), - m_keepHiddenTracks(keepHiddenTracks) { + m_countsDurationTableName(countsDurationTableName) { pModel->setParent(this); initActions(); @@ -574,8 +572,7 @@ void BasePlaylistFeature::slotExportPlaylist() { std::unique_ptr pPlaylistTableModel = std::make_unique(this, m_pLibrary->trackCollectionManager(), - "mixxx.db.model.playlist_export", - m_keepHiddenTracks); + "mixxx.db.model.playlist_export"); emit saveModelState(); pPlaylistTableModel->selectPlaylist(playlistId); diff --git a/src/library/trackset/baseplaylistfeature.h b/src/library/trackset/baseplaylistfeature.h index 4ad572b829e..4efe63c3325 100644 --- a/src/library/trackset/baseplaylistfeature.h +++ b/src/library/trackset/baseplaylistfeature.h @@ -26,8 +26,7 @@ class BasePlaylistFeature : public BaseTrackSetFeature { PlaylistTableModel* pModel, const QString& rootViewName, const QString& iconName, - const QString& countsDurationTableName, - bool keepHiddenTracks = false); + const QString& countsDurationTableName); ~BasePlaylistFeature() override = default; TreeItemModel* sidebarModel() const override; @@ -130,7 +129,4 @@ class BasePlaylistFeature : public BaseTrackSetFeature { virtual QString getRootViewHtml() const = 0; void markTreeItem(TreeItem* pTreeItem); QString fetchPlaylistLabel(int playlistId); - - - const bool m_keepHiddenTracks; }; diff --git a/src/library/trackset/setlogfeature.cpp b/src/library/trackset/setlogfeature.cpp index 68d4bd80a78..7fc7c9f3e36 100644 --- a/src/library/trackset/setlogfeature.cpp +++ b/src/library/trackset/setlogfeature.cpp @@ -34,12 +34,10 @@ SetlogFeature::SetlogFeature( new PlaylistTableModel( nullptr, pLibrary->trackCollectionManager(), - "mixxx.db.model.setlog", - /*keep hidden tracks*/ true), + "mixxx.db.model.setlog"), QStringLiteral("SETLOGHOME"), QStringLiteral("history"), - QStringLiteral("SetlogCountsDurations"), - /*keep hidden tracks*/ true), + QStringLiteral("SetlogCountsDurations")), m_currentPlaylistId(kInvalidPlaylistId), m_yearNodeId(kInvalidPlaylistId), m_pLibrary(pLibrary), diff --git a/src/preferences/settingsmanager.cpp b/src/preferences/settingsmanager.cpp index 48c377c192a..e57559cc7da 100644 --- a/src/preferences/settingsmanager.cpp +++ b/src/preferences/settingsmanager.cpp @@ -7,7 +7,8 @@ #include "util/assert.h" SettingsManager::SettingsManager(const QString& settingsPath) - : m_bShouldRescanLibrary(false) { + : m_bShouldRescanLibrary(false), + m_upgradedFrom21OrEarlier(false) { // First make sure the settings path exists. If we don't then other parts of // Mixxx (such as the library) will produce confusing errors. if (!QDir(settingsPath).exists()) { @@ -23,6 +24,8 @@ SettingsManager::SettingsManager(const QString& settingsPath) } m_bShouldRescanLibrary = upgrader.rescanLibrary(); + m_upgradedFrom21OrEarlier = upgrader.upgradedFrom21OrEarlier(); + ControlDoublePrivate::setUserConfig(m_pSettings); #ifdef __BROADCAST__ diff --git a/src/preferences/settingsmanager.h b/src/preferences/settingsmanager.h index a97e0095b17..e9ebd57577b 100644 --- a/src/preferences/settingsmanager.h +++ b/src/preferences/settingsmanager.h @@ -28,9 +28,14 @@ class SettingsManager { return m_bShouldRescanLibrary; } + bool upgradedFrom21OrEarlier() { + return m_upgradedFrom21OrEarlier; + }; + private: UserSettingsPointer m_pSettings; bool m_bShouldRescanLibrary; + bool m_upgradedFrom21OrEarlier; #ifdef __BROADCAST__ BroadcastSettingsPointer m_pBroadcastSettings; #endif diff --git a/src/preferences/upgrade.cpp b/src/preferences/upgrade.cpp index 1d7704a6da4..a6bb7d5f876 100644 --- a/src/preferences/upgrade.cpp +++ b/src/preferences/upgrade.cpp @@ -27,7 +27,8 @@ Upgrade::Upgrade() : m_bFirstRun(false), - m_bRescanLibrary(false) { + m_bRescanLibrary(false), + m_upgradedFrom21OrEarlier(false) { } Upgrade::~Upgrade() { @@ -137,6 +138,9 @@ upgradeToAllShaders(int unsafeWaveformType, return {waveformType, waveformBackend, waveformOption}; } +const ConfigKey kVersionCfgKey = ConfigKey(QStringLiteral("[Config]"), + QStringLiteral("Version")); + VSyncThread::VSyncMode upgradeDeprecatedVSyncModes(int configVSyncMode) { using VT = VSyncThread; if (configVSyncMode >= 0 || configVSyncMode <= static_cast(VT::ST_COUNT)) { @@ -316,10 +320,14 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { UserSettingsPointer config(new ConfigObject( QDir(settingsPath).filePath(MIXXX_SETTINGS_FILE))); - QString configVersion = config->getValueString(ConfigKey("[Config]","Version")); + QString configVersion = config->getValueString(kVersionCfgKey); - if (configVersion.isEmpty()) { + const auto oldConfigFileVersion = QVersionNumber::fromString(configVersion); + if (oldConfigFileVersion < QVersionNumber(2, 1, 0)) { + m_upgradedFrom21OrEarlier = true; + } + if (configVersion.isEmpty()) { #ifdef __APPLE__ qDebug() << "Config version is empty, trying to read pre-1.9.0 config"; // Try to read the config from the pre-1.9.0 final directory on OS X (we moved it in 1.9.0 final) @@ -334,7 +342,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { // TODO(XXX) Trailing slash not needed anymore as we switches from String::append // to QDir::filePath elsewhere in the code. This is candidate for removal. CmdlineArgs::Instance().setSettingsPath(QDir::homePath().append("/.mixxx/")); - configVersion = config->getValueString(ConfigKey("[Config]","Version")); + configVersion = config->getValueString(kVersionCfgKey); } else { #elif defined(__WINDOWS__) @@ -352,14 +360,14 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { // TODO(XXX) Trailing slash not needed anymore as we switches from String::append // to QDir::filePath elsewhere in the code. This is candidate for removal. CmdlineArgs::Instance().setSettingsPath(QDir::homePath().append("/Local Settings/Application Data/Mixxx/")); - configVersion = config->getValueString(ConfigKey("[Config]","Version")); + configVersion = config->getValueString(kVersionCfgKey); } else { #endif // This must have been the first run... right? :) qDebug() << "No version number in configuration file. Setting to" << VersionStore::version(); - config->set(ConfigKey("[Config]", "Version"), ConfigValue(VersionStore::version())); + config->set(kVersionCfgKey, ConfigValue(VersionStore::version())); m_bFirstRun = true; return config; #ifdef __APPLE__ @@ -387,13 +395,13 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { qDebug() << "Upgrading from v1.6.0 to 1.6.1..."; // Upgrade tasks go here configVersion = "1.6.1"; - config->set(ConfigKey("[Config]","Version"), ConfigValue("1.6.1")); + config->set(kVersionCfgKey, ConfigValue("1.6.1")); } if (configVersion.startsWith("1.6.1")) { qDebug() << "Upgrading from v1.6.1 to 1.7.0..."; // Upgrade tasks go here configVersion = "1.7.0"; - config->set(ConfigKey("[Config]","Version"), ConfigValue("1.7.0")); + config->set(kVersionCfgKey, ConfigValue("1.7.0")); } */ @@ -407,7 +415,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { // Upgrade tasks go here // Nothing to change, really configVersion = "1.8.0"; - config->set(ConfigKey("[Config]","Version"), ConfigValue("1.8.0")); + config->set(kVersionCfgKey, ConfigValue("1.8.0")); } if (configVersion.startsWith("1.8.0~beta1") || @@ -415,7 +423,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { qDebug() << "Upgrading from v1.8.0~beta..."; // Upgrade tasks go here configVersion = "1.8.0"; - config->set(ConfigKey("[Config]","Version"), ConfigValue("1.8.0")); + config->set(kVersionCfgKey, ConfigValue("1.8.0")); } if (configVersion.startsWith("1.8") || configVersion.startsWith("1.9.0beta1")) { qDebug() << "Upgrading from" << configVersion << "..."; @@ -466,7 +474,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { QDir(settingsPath).filePath(MIXXX_SETTINGS_FILE))); #endif configVersion = "1.9.0"; - config->set(ConfigKey("[Config]","Version"), ConfigValue("1.9.0")); + config->set(kVersionCfgKey, ConfigValue("1.9.0")); } if (configVersion.startsWith("1.9") || configVersion.startsWith("1.10")) { qDebug() << "Upgrading from v1.9.x/1.10.x..."; @@ -507,8 +515,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { if (successful) { qDebug() << "Upgrade Successful"; configVersion = "1.11.0"; - config->set(ConfigKey("[Config]","Version"), - ConfigValue(configVersion)); + config->set(kVersionCfgKey, ConfigValue(configVersion)); } else { qDebug() << "Upgrade Failed"; } @@ -568,8 +575,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { // updated if (successful) { configVersion = "1.12.0"; - config->set(ConfigKey("[Config]", "Version"), - ConfigValue(configVersion)); + config->set(kVersionCfgKey, ConfigValue(configVersion)); } else { qDebug() << "Upgrade failed!\n"; @@ -609,8 +615,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { correctedWaveformOption); // mark the configuration as updated configVersion = "2.6.0"; - config->set(ConfigKey("[Config]", "Version"), - ConfigValue(configVersion)); + config->set(kVersionCfgKey, ConfigValue(configVersion)); } // This variable indicates the first known version that requires no changes. @@ -620,7 +625,7 @@ UserSettingsPointer Upgrade::versionUpgrade(const QString& settingsPath) { if (QVersionNumber::fromString(configVersion) >= cleanVersion) { // No special upgrade required, just update the value. configVersion = VersionStore::version(); - config->set(ConfigKey("[Config]", "Version"), ConfigValue(VersionStore::version())); + config->set(kVersionCfgKey, ConfigValue(VersionStore::version())); } if (configVersion == VersionStore::version()) { diff --git a/src/preferences/upgrade.h b/src/preferences/upgrade.h index d382d9a8ef8..adf2f726908 100644 --- a/src/preferences/upgrade.h +++ b/src/preferences/upgrade.h @@ -9,11 +9,17 @@ class Upgrade { UserSettingsPointer versionUpgrade(const QString& settingsPath); bool isFirstRun() { return m_bFirstRun; }; - bool rescanLibrary() {return m_bRescanLibrary; }; + bool rescanLibrary() { + return m_bRescanLibrary; + }; + bool upgradedFrom21OrEarlier() { + return m_upgradedFrom21OrEarlier; + }; private: bool askReanalyzeBeats(); bool askReScanLibrary(); bool m_bFirstRun; bool m_bRescanLibrary; + bool m_upgradedFrom21OrEarlier; };