Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the track order during drag and drop Lp1829601 #2237

Merged
merged 13 commits into from Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/library/analysisfeature.cpp
Expand Up @@ -196,7 +196,7 @@ bool AnalysisFeature::dropAccept(QList<QUrl> urls, QObject* pSource) {
Q_UNUSED(pSource);
QList<QFileInfo> files = DragAndDropHelper::supportedTracksFromUrls(urls, false, true);
// Adds track, does not insert duplicates, handles unremoving logic.
QList<TrackId> trackIds = m_pTrackCollection->getTrackDAO().addMultipleTracks(files, true);
QList<TrackId> trackIds = m_pTrackCollection->getAndEnsureTrackIds(files, true);
analyzeTracks(trackIds);
return trackIds.size() > 0;
}
Expand Down
28 changes: 13 additions & 15 deletions src/library/autodj/autodjfeature.cpp
Expand Up @@ -138,26 +138,24 @@ void AutoDJFeature::activate() {
}

bool AutoDJFeature::dropAccept(QList<QUrl> urls, QObject* pSource) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dropAccept()/dropAcceptChild() contain a lot of duplicated code (AutoDJ/Crates/Playlist). Is It possible to extract this code into a function?

// If a track is dropped onto a playlist's name, but the track isn't in the
// library, then add the track to the library before adding it to the
// playlist.
QList<QFileInfo> files = DragAndDropHelper::supportedTracksFromUrls(urls, false, true);
QList<TrackId> trackIds;
if (pSource) {
trackIds = m_pTrackCollection->getTrackDAO().getTrackIds(files);
m_pTrackCollection->unhideTracks(trackIds);
} else {
trackIds = m_pTrackCollection->getTrackDAO().addMultipleTracks(files, true);
if (!files.size()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isEmpty()

return false;
}

// remove tracks that could not be added
for (int trackIdIndex = 0; trackIdIndex < trackIds.size(); trackIdIndex++) {
if (!trackIds.at(trackIdIndex).isValid()) {
trackIds.removeAt(trackIdIndex--);
}
// If a track is dropped onto the Auto DJ tree node, but the track isn't in the
// library, then add the track to the library before adding it to the
// Auto DJ playlist. getAndEnsureTrackIds(), does not insert duplicates and handles
// unremove logic.
// pSource != nullptr it is a drop from inside Mixxx and indicates all
// tracks already in the DB
bool addMissingTracks = (pSource == nullptr);
QList<TrackId> trackIds = m_pTrackCollection->getAndEnsureTrackIds(files, addMissingTracks);
if (!trackIds.size()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isEmpty()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

return false;
}

// Return whether the tracks were appended.
// Return whether appendTracksToPlaylist succeeded.
return m_playlistDao.appendTracksToPlaylist(trackIds, m_iAutoDJPlaylistId);
}

Expand Down
32 changes: 16 additions & 16 deletions src/library/crate/cratefeature.cpp
Expand Up @@ -200,26 +200,26 @@ void updateTreeItemForTrackSelection(
bool CrateFeature::dropAcceptChild(const QModelIndex& index, QList<QUrl> urls,
QObject* pSource) {
CrateId crateId(crateIdFromIndex(index));
if (!crateId.isValid()) {
VERIFY_OR_DEBUG_ASSERT(crateId.isValid()) {
return false;
}
QList<QFileInfo> files = DragAndDropHelper::supportedTracksFromUrls(urls, false, true);
QList<TrackId> trackIds;
if (pSource) {
trackIds = m_pTrackCollection->getTrackDAO().getTrackIds(files);
m_pTrackCollection->unhideTracks(trackIds);
} else {
// Adds track, does not insert duplicates, handles unremoving logic.
trackIds = m_pTrackCollection->getTrackDAO().addMultipleTracks(files, true);
}
qDebug() << "CrateFeature::dropAcceptChild adding tracks"
<< trackIds.size() << " to crate "<< crateId;
// remove tracks that could not be added
for (int trackIdIndex = 0; trackIdIndex < trackIds.size(); ++trackIdIndex) {
if (!trackIds.at(trackIdIndex).isValid()) {
trackIds.removeAt(trackIdIndex--);
}
if (!files.size()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isEmpty()

return false;
}

// If a track is dropped onto a crate's name, but the track isn't in the
// library, then add the track to the library before adding it to the
// playlist. getAndEnsureTrackIds(), does not insert duplicates and handles
// unremove logic.
// pSource != nullptr it is a drop from inside Mixxx and indicates all
// tracks already in the DB
bool addMissingTracks = (pSource == nullptr);
QList<TrackId> trackIds = m_pTrackCollection->getAndEnsureTrackIds(files, addMissingTracks);
if (!trackIds.size()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isEmpty()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ping

return false;
}

m_pTrackCollection->addCrateTracks(crateId, trackIds);
return true;
}
Expand Down
6 changes: 2 additions & 4 deletions src/library/crate/cratetablemodel.cpp
Expand Up @@ -149,12 +149,10 @@ int CrateTableModel::addTracks(const QModelIndex& index,
QList<QFileInfo> fileInfoList;
foreach(QString fileLocation, locations) {
QFileInfo fileInfo(fileLocation);
if (fileInfo.exists()) {
fileInfoList.append(fileInfo);
}
fileInfoList.append(fileInfo);
}

QList<TrackId> trackIds(m_pTrackCollection->getTrackDAO().addMultipleTracks(fileInfoList, true));
QList<TrackId> trackIds = m_pTrackCollection->getAndEnsureTrackIds(fileInfoList, true);
if (m_pTrackCollection->addCrateTracks(m_selectedCrate, trackIds)) {
select();
return trackIds.size();
Expand Down
184 changes: 71 additions & 113 deletions src/library/dao/trackdao.cpp
Expand Up @@ -135,22 +135,73 @@ TrackId TrackDAO::getTrackId(const QString& absoluteFilePath) {
return trackId;
}

QList<TrackId> TrackDAO::getTrackIds(const QList<QFileInfo>& files) {
QList<TrackId> TrackDAO::getTrackIds(const QList<QFileInfo>& files,
bool addMissingTracks) {
QList<TrackId> trackIds;
trackIds.reserve(files.size());

// Create a temporary database of the paths of all the imported tracks.
QSqlQuery query(m_database);
{
QStringList pathList;
pathList.reserve(files.size());
for (const auto& file: files) {
pathList << file.absoluteFilePath();
query.prepare(
"CREATE TEMP TABLE playlist_import "
"(location varchar (512))");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
return trackIds;
}

QStringList pathList;
pathList.reserve(files.size());
for (const auto& file: files) {
pathList << "(" + SqlStringFormatter::format(m_database, file.absoluteFilePath()) + ")";
}

// Add all the track paths temporary to this database.
query.prepare(
"INSERT INTO playlist_import (location) "
"VALUES " + pathList.join(','));
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}

if (addMissingTracks) {
// Prepare to add tracks to the database.
// This also begins an SQL transaction.
addTracksPrepare();

// Any tracks not already in the database need to be added.
query.prepare("SELECT location FROM playlist_import "
"WHERE NOT EXISTS (SELECT location FROM track_locations "
"WHERE playlist_import.location = track_locations.location)");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}
query.prepare(QString("SELECT library.id FROM library INNER JOIN "
"track_locations ON library.location = track_locations.id "
"WHERE track_locations.location in (%1)").arg(
SqlStringFormatter::formatList(m_database, pathList)));
const int locationColumn = query.record().indexOf("location");
while (query.next()) {
QString location = query.value(locationColumn).toString();
const QFileInfo fileInfo(location);
addTracksAddFile(fileInfo, true);
}

// Finish adding tracks to the database.
addTracksFinish();
}

query.prepare(
"SELECT library.id FROM playlist_import "
"INNER JOIN track_locations ON playlist_import.location = track_locations.location "
"INNER JOIN library ON library.location = track_locations.id "
// the order by clause enforces the native sorting which is used anyway
// hopefully optimized away. TODO() verify.
"ORDER BY playlist_import.ROWID");

// Old syntax for a shorter but less readable query. TODO() check performance gain
// query.prepare(
// "SELECT library.id FROM playlist_import, "
// "track_locations, library WHERE library.location = track_locations.id "
// "AND playlist_import.location = track_locations.location");
// "ORDER BY playlist_import.ROWID");

if (query.exec()) {
const int idColumn = query.record().indexOf("id");
while (query.next()) {
Expand All @@ -164,6 +215,12 @@ QList<TrackId> TrackDAO::getTrackIds(const QList<QFileInfo>& files) {
LOG_FAILED_QUERY(query);
}

// Drop the temporary playlist-import table.
query.prepare("DROP TABLE IF EXISTS playlist_import");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}

return trackIds;
}

Expand Down Expand Up @@ -687,106 +744,6 @@ TrackPointer TrackDAO::addSingleTrack(const QFileInfo& fileInfo, bool unremove)
return pTrack;
}

QList<TrackId> TrackDAO::addMultipleTracks(
const QList<QFileInfo>& fileInfoList,
bool unremove) {
// Prepare to add tracks to the database.
// This also begins an SQL transaction.
addTracksPrepare();

// Create a temporary database of the paths of all the imported tracks.
QSqlQuery query(m_database);
query.prepare("CREATE TEMP TABLE playlist_import "
"(add_index INTEGER PRIMARY KEY, location varchar (512))");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
addTracksFinish(true);
return QList<TrackId>();
}

// Add all the track paths to this database.
query.prepare("INSERT INTO playlist_import (add_index, location) "
"VALUES (:add_index, :location)");
int index = 0;
for (const auto& fileInfo: fileInfoList) {
query.bindValue(":add_index", index);
query.bindValue(":location", fileInfo.absoluteFilePath());
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}
index++;
}

// If imported-playlist tracks are to be unremoved, do that for all playlist
// tracks that were already in the database.
if (unremove) {
query.prepare("SELECT library.id FROM playlist_import, "
"track_locations, library WHERE library.location = track_locations.id "
"AND playlist_import.location = track_locations.location");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}

int idColumn = query.record().indexOf("id");
QStringList idStringList;
while (query.next()) {
TrackId trackId(query.value(idColumn));
idStringList.append(trackId.toString());
}

query.prepare(QString("UPDATE library SET mixxx_deleted=0 "
"WHERE id in (%1) AND mixxx_deleted=1")
.arg(idStringList.join(",")));
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}
}

// Any tracks not already in the database need to be added.
query.prepare("SELECT add_index, location FROM playlist_import "
"WHERE NOT EXISTS (SELECT location FROM track_locations "
"WHERE playlist_import.location = track_locations.location)");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}
const int addIndexColumn = query.record().indexOf("add_index");
while (query.next()) {
int addIndex = query.value(addIndexColumn).toInt();
const QFileInfo fileInfo(fileInfoList.at(addIndex));
addTracksAddFile(fileInfo, unremove);
}

// Now that we have imported any tracks that were not already in the
// library, re-select ordering by playlist_import.add_index to return
// the list of track ids in the order that they were requested to be
// added.
QList<TrackId> trackIds;
query.prepare("SELECT library.id FROM playlist_import, "
"track_locations, library WHERE library.location = track_locations.id "
"AND playlist_import.location = track_locations.location "
"ORDER BY playlist_import.add_index");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}
int idColumn = query.record().indexOf("id");
while (query.next()) {
TrackId trackId(query.value(idColumn));
trackIds.append(trackId);
}

// Drop the temporary playlist-import table.
query.prepare("DROP TABLE IF EXISTS playlist_import");
if (!query.exec()) {
LOG_FAILED_QUERY(query);
}

// Finish adding tracks to the database.
addTracksFinish();

// Return the list of track IDs added to the database.
return trackIds;
}

bool TrackDAO::onHidingTracks(
const QList<TrackId>& trackIds) {
QStringList idList;
Expand All @@ -811,15 +768,16 @@ void TrackDAO::afterHidingTracks(
// up in the library views again.
// This function should get called if you drag-and-drop a file that's been
// "hidden" from Mixxx back into the library view.
bool TrackDAO::onUnhidingTracks(
bool TrackDAO::unhideTracks(
uklotzde marked this conversation as resolved.
Show resolved Hide resolved
const QList<TrackId>& trackIds) {
QStringList idList;
for (const auto& trackId: trackIds) {
idList.append(trackId.toString());
}
FwdSqlQuery query(m_database, QString(
FwdSqlQuery query(m_database,
"UPDATE library SET mixxx_deleted=0 "
"WHERE id in (%1)").arg(idList.join(",")));
"WHERE mixxx_deleted!=0 "
"AND id in (" + idList.join(",") + ")");
return !query.hasError() && query.execPrepared();
}

Expand Down
6 changes: 2 additions & 4 deletions src/library/dao/trackdao.h
Expand Up @@ -40,7 +40,7 @@ class TrackDAO : public QObject, public virtual DAO, public virtual GlobalTrackC
void finish();

TrackId getTrackId(const QString& absoluteFilePath);
QList<TrackId> getTrackIds(const QList<QFileInfo>& files);
QList<TrackId> getTrackIds(const QList<QFileInfo>& files, bool addMissingTracks);
QList<TrackId> getTrackIds(const QDir& dir);

// WARNING: Only call this from the main thread instance of TrackDAO.
Expand All @@ -51,8 +51,6 @@ class TrackDAO : public QObject, public virtual DAO, public virtual GlobalTrackC
QString getTrackLocation(TrackId trackId);

TrackPointer addSingleTrack(const QFileInfo& fileInfo, bool unremove);
QList<TrackId> addMultipleTracks(const QList<QFileInfo>& fileInfoList, bool unremove);

void addTracksPrepare();
TrackPointer addTracksAddFile(const QFileInfo& fileInfo, bool unremove);
TrackId addTracksAddTrack(const TrackPointer& pTrack, bool unremove);
Expand All @@ -63,7 +61,7 @@ class TrackDAO : public QObject, public virtual DAO, public virtual GlobalTrackC
void afterHidingTracks(
const QList<TrackId>& trackIds);

bool onUnhidingTracks(
bool unhideTracks(
const QList<TrackId>& trackIds);
void afterUnhidingTracks(
const QList<TrackId>& trackIds);
Expand Down
2 changes: 1 addition & 1 deletion src/library/librarytablemodel.cpp
Expand Up @@ -65,7 +65,7 @@ int LibraryTableModel::addTracks(const QModelIndex& index,
foreach (QString fileLocation, locations) {
fileInfoList.append(QFileInfo(fileLocation));
}
QList<TrackId> trackIds = m_pTrackCollection->getTrackDAO().addMultipleTracks(fileInfoList, true);
QList<TrackId> trackIds = m_pTrackCollection->getAndEnsureTrackIds(fileInfoList, true);
select();
return trackIds.size();
}
Expand Down
2 changes: 1 addition & 1 deletion src/library/mixxxlibraryfeature.cpp
Expand Up @@ -184,7 +184,7 @@ bool MixxxLibraryFeature::dropAccept(QList<QUrl> urls, QObject* pSource) {
QList<QFileInfo> files = DragAndDropHelper::supportedTracksFromUrls(urls, false, true);

// Adds track, does not insert duplicates, handles unremoving logic.
QList<TrackId> trackIds = m_trackDao.addMultipleTracks(files, true);
QList<TrackId> trackIds = m_pTrackCollection->getAndEnsureTrackIds(files, true);
return trackIds.size() > 0;
}
}
Expand Down