diff --git a/src/library/trackset/baseplaylistfeature.cpp b/src/library/trackset/baseplaylistfeature.cpp index 970a2f98722..efa722620f8 100644 --- a/src/library/trackset/baseplaylistfeature.cpp +++ b/src/library/trackset/baseplaylistfeature.cpp @@ -325,11 +325,8 @@ void BasePlaylistFeature::slotDuplicatePlaylist() { int newPlaylistId = m_playlistDao.createPlaylist(name); - if (newPlaylistId != kInvalidPlaylistId && - m_playlistDao.copyPlaylistTracks(oldPlaylistId, newPlaylistId)) { - // Note: this assumes the sidebar model was already updated by slotPlaylisttableChanged - // and the sidebar scrolled to the new playlist - activatePlaylist(oldPlaylistId); + if (newPlaylistId != kInvalidPlaylistId) { + m_playlistDao.copyPlaylistTracks(oldPlaylistId, newPlaylistId); } } @@ -420,21 +417,12 @@ void BasePlaylistFeature::slotDeletePlaylist() { return; } - // we will switch to the sibling if the deleted playlist is currently active - bool wasActive = m_pPlaylistTableModel->getPlaylist() == playlistId; - - VERIFY_OR_DEBUG_ASSERT(playlistId >= 0) { - return; - } - bool locked = m_playlistDao.isPlaylistLocked(playlistId); if (locked) { qDebug() << "Skipping playlist deletion because playlist" << playlistId << "is locked."; return; } - int siblingId = getSiblingPlaylistIdOf(m_lastRightClickedIndex); - QMessageBox::StandardButton btn = QMessageBox::question(nullptr, tr("Confirm Deletion"), tr("Do you really want to delete playlist %1?") @@ -446,15 +434,6 @@ void BasePlaylistFeature::slotDeletePlaylist() { } m_playlistDao.deletePlaylist(playlistId); - - if (siblingId == kInvalidPlaylistId) { - return; - } - if (wasActive) { - activatePlaylist(siblingId); - } else if (m_pSidebarWidget) { - m_pSidebarWidget->selectChildIndex(indexFromPlaylistId(siblingId), false); - } } void BasePlaylistFeature::slotImportPlaylist() { @@ -755,6 +734,8 @@ void BasePlaylistFeature::updateChildModel(int playlistId) { /// Clears the child model dynamically, but the invisible root item remains void BasePlaylistFeature::clearChildModel() { + m_lastClickedIndex = QModelIndex(); + m_lastRightClickedIndex = QModelIndex(); m_pSidebarModel->removeRows(0, m_pSidebarModel->rowCount()); } diff --git a/src/library/trackset/crate/cratefeature.cpp b/src/library/trackset/crate/cratefeature.cpp index 7db9e7700cc..9845e13bb68 100644 --- a/src/library/trackset/crate/cratefeature.cpp +++ b/src/library/trackset/crate/cratefeature.cpp @@ -301,13 +301,14 @@ void CrateFeature::activate() { } void CrateFeature::activateChild(const QModelIndex& index) { - //qDebug() << "CrateFeature::activateChild()" << index; + qDebug() << " CrateFeature::activateChild()" << index; CrateId crateId(crateIdFromIndex(index)); VERIFY_OR_DEBUG_ASSERT(crateId.isValid()) { return; } m_lastClickedIndex = index; m_lastRightClickedIndex = QModelIndex(); + m_prevSiblingCrate = CrateId(); emit saveModelState(); m_crateTableModel.selectCrate(crateId); emit showTrackModel(&m_crateTableModel); @@ -323,9 +324,10 @@ bool CrateFeature::activateCrate(CrateId crateId) { VERIFY_OR_DEBUG_ASSERT(index.isValid()) { return false; } - emit saveModelState(); m_lastClickedIndex = index; m_lastRightClickedIndex = QModelIndex(); + m_prevSiblingCrate = CrateId(); + emit saveModelState(); m_crateTableModel.selectCrate(crateId); emit showTrackModel(&m_crateTableModel); emit enableCoverArtDisplay(true); @@ -434,7 +436,11 @@ void CrateFeature::slotDeleteCrate() { CrateId crateId = crate.getId(); // Store sibling id to restore selection after crate was deleted // to avoid the scroll position being reset to Crate root item. - storePrevSiblingCrateId(crateId); + m_prevSiblingCrate = CrateId(); + if (isChildIndexSelectedInSidebar(m_lastRightClickedIndex)) { + storePrevSiblingCrateId(crateId); + } + QMessageBox::StandardButton btn = QMessageBox::question(nullptr, tr("Confirm Deletion"), tr("Do you really want to delete crate %1?") @@ -509,13 +515,10 @@ void CrateFeature::slotDuplicateCrate() { .duplicateCrate(crate); if (newCrateId.isValid()) { qDebug() << "Duplicate crate" << crate << ", new crate:" << newCrateId; - // expand Crates and scroll to new crate - m_pSidebarWidget->selectChildIndex(indexFromCrateId(newCrateId), false); - activateCrate(crate.getId()); + return; } - } else { - qDebug() << "Failed to duplicate selected crate"; } + qDebug() << "Failed to duplicate selected crate"; } void CrateFeature::slotToggleCrateLock() { @@ -856,15 +859,14 @@ void CrateFeature::storePrevSiblingCrateId(CrateId crateId) { } void CrateFeature::slotCrateTableChanged(CrateId crateId) { - if (m_lastRightClickedIndex.isValid() && - (crateIdFromIndex(m_lastRightClickedIndex) == crateId)) { - // Try to restore previous selection - m_lastRightClickedIndex = rebuildChildModel(crateId); - if (m_lastRightClickedIndex.isValid()) { - // Select last active crate - activateCrate(crateId); - } else if (m_prevSiblingCrate.isValid()) { - // Select neighbour of deleted crate + Q_UNUSED(crateId); + if (isChildIndexSelectedInSidebar(m_lastClickedIndex)) { + // If the previously selected crate was loaded to the tracks table and + // selected in the sidebar try to activate that or a sibling + rebuildChildModel(); + if (!activateCrate(m_crateTableModel.selectedCrate())) { + // probably last clicked crate was deleted, try to + // select the stored sibling activateCrate(m_prevSiblingCrate); } } else { diff --git a/src/library/trackset/playlistfeature.cpp b/src/library/trackset/playlistfeature.cpp index 92015e90316..bf6a1c56a32 100644 --- a/src/library/trackset/playlistfeature.cpp +++ b/src/library/trackset/playlistfeature.cpp @@ -278,8 +278,29 @@ void PlaylistFeature::slotPlaylistTableChanged(int playlistId) { enum PlaylistDAO::HiddenType type = m_playlistDao.getHiddenType(playlistId); if (type == PlaylistDAO::PLHT_NOT_HIDDEN || type == PlaylistDAO::PLHT_UNKNOWN) { // In case of a deleted Playlist + // Store current selection + int selectedPlaylistId = kInvalidPlaylistId; + if (isChildIndexSelectedInSidebar(m_lastClickedIndex)) { + if (playlistId == playlistIdFromIndex(m_lastClickedIndex) && + type == PlaylistDAO::PLHT_UNKNOWN) { + // if the selected playlist was deleted, find a sibling to select + selectedPlaylistId = getSiblingPlaylistIdOf(m_lastClickedIndex); + } else { + // just restore the current selection + selectedPlaylistId = playlistIdFromIndex(m_lastClickedIndex); + } + } + clearChildModel(); - m_lastRightClickedIndex = constructChildModel(playlistId); + QModelIndex newIndex = constructChildModel(selectedPlaylistId); + if (newIndex.isValid()) { + // If a child index was selected and we got a new valid index select that. + // Else (root item was selected or for some reason no index could be created) + // there's nothing to do: either no child was selected earlier, or the root + // was selected and will remain selected after the child model was rebuilt. + activateChild(newIndex); + emit featureSelect(this, newIndex); + } } } diff --git a/src/library/trackset/setlogfeature.cpp b/src/library/trackset/setlogfeature.cpp index f3d57b512a0..355b6639090 100644 --- a/src/library/trackset/setlogfeature.cpp +++ b/src/library/trackset/setlogfeature.cpp @@ -208,8 +208,9 @@ QModelIndex SetlogFeature::constructChildModel(int selectedId) { int idColumn = record.indexOf("id"); int createdColumn = record.indexOf("date_created"); + // Nice to have: restore previous expanded/collapsed state of YEAR items + clearChildModel(); QMap groups; - QList itemList; // Generous estimate (number of years the db is used ;)) itemList.reserve(kNumToplevelHistoryEntries + 15); @@ -229,16 +230,19 @@ QModelIndex SetlogFeature::constructChildModel(int selectedId) { .toDateTime(); // Create the TreeItem whose parent is the invisible root item - // Show only [kNumToplevelHistoryEntries -1] recent playlists at the top - // level before grouping them by year. + // Show only [kNumToplevelHistoryEntries] recent playlists at the top level + // before grouping them by year. if (row >= kNumToplevelHistoryEntries) { + // group by year int yearCreated = dateCreated.date().year(); auto i = groups.find(yearCreated); TreeItem* groupItem; if (i != groups.end() && i.key() == yearCreated) { + // get YEAR item the playlist will sorted into groupItem = i.value(); } else { + // create YEAR item the playlist will sorted into // store id of empty placeholder playlist groupItem = new TreeItem(QString::number(yearCreated), m_placeholderId); groups.insert(yearCreated, groupItem); @@ -252,6 +256,7 @@ QModelIndex SetlogFeature::constructChildModel(int selectedId) { groupItem->appendChild(std::move(item)); } else { + // add most recent top-level playlist TreeItem* item = new TreeItem(name, id); item->setBold(m_playlistIdsOfSelectedTrack.contains(id)); @@ -264,10 +269,7 @@ QModelIndex SetlogFeature::constructChildModel(int selectedId) { // Append all the newly created TreeItems in a dynamic way to the childmodel m_pSidebarModel->insertTreeItemRows(itemList, 0); - if (selectedId) { - return indexFromPlaylistId(selectedId); - } - return QModelIndex(); + return indexFromPlaylistId(selectedId); } QString SetlogFeature::fetchPlaylistLabel(int playlistId) { @@ -384,11 +386,7 @@ void SetlogFeature::slotJoinWithPrevious() { << " previous:" << previousPlaylistId; if (m_playlistDao.copyPlaylistTracks( currentPlaylistId, previousPlaylistId)) { - m_lastRightClickedIndex = constructChildModel(previousPlaylistId); m_playlistDao.deletePlaylist(currentPlaylistId); - reloadChildModel(previousPlaylistId); // For moving selection - emit showTrackModel(m_pPlaylistTableModel); - activatePlaylist(previousPlaylistId); } } } @@ -523,16 +521,61 @@ void SetlogFeature::slotPlayingTrackChanged(TrackPointer currentPlayingTrack) { } void SetlogFeature::slotPlaylistTableChanged(int playlistId) { - reloadChildModel(playlistId); -} - -void SetlogFeature::reloadChildModel(int playlistId) { //qDebug() << "updateChildModel() playlistId:" << playlistId; PlaylistDAO::HiddenType type = m_playlistDao.getHiddenType(playlistId); - if (type == PlaylistDAO::PLHT_SET_LOG || - type == PlaylistDAO::PLHT_UNKNOWN) { // In case of a deleted Playlist - clearChildModel(); - m_lastRightClickedIndex = constructChildModel(playlistId); + if (type != PlaylistDAO::PLHT_SET_LOG && + type != PlaylistDAO::PLHT_UNKNOWN) { // deleted Playlist + return; + } + + // save currently selected History sidebar item (if any) + int selectedYearIndexRow = -1; + int selectedPlaylistId = kInvalidPlaylistId; + bool rootWasSelected = false; + if (isChildIndexSelectedInSidebar(m_lastClickedIndex)) { + // a child index was selected (actual playlist or YEAR item) + int lastClickedPlaylistId = m_pPlaylistTableModel->getPlaylist(); + if (lastClickedPlaylistId == m_placeholderId) { + // a YEAR item was selected + selectedYearIndexRow = m_lastClickedIndex.row(); + } else if (playlistId == lastClickedPlaylistId && + type == PlaylistDAO::PLHT_UNKNOWN) { + // selected playlist was deleted, find a sibling. + // prev/next works here because history playlists are always + // sorted by date of creation. + selectedPlaylistId = m_playlistDao.getPreviousPlaylist( + lastClickedPlaylistId, + PlaylistDAO::PLHT_SET_LOG); + if (selectedPlaylistId == kInvalidPlaylistId) { + // no previous playlist, try to get the next playlist + selectedPlaylistId = m_playlistDao.getNextPlaylist( + lastClickedPlaylistId, + PlaylistDAO::PLHT_SET_LOG); + } + } else { + selectedPlaylistId = lastClickedPlaylistId; + } + } else { + rootWasSelected = m_pSidebarWidget && + m_pSidebarWidget->isFeatureRootIndexSelected(this); + } + + QModelIndex newIndex = constructChildModel(selectedPlaylistId); + + // restore selection + if (selectedYearIndexRow != -1) { + // if row is valid this means newIndex is invalid anyway + newIndex = m_pSidebarModel->index(selectedYearIndexRow, 0); + if (!newIndex.isValid()) { + // seems like we deleted the oldest (bottom) YEAR node while it was + // selected. Try to pick the row above + newIndex = m_pSidebarModel->index(selectedYearIndexRow - 1, 0); + } + } + if (newIndex.isValid() || rootWasSelected) { + // calling featureSelect with invalid index will select the root item + emit featureSelect(this, newIndex); + activateChild(newIndex); } } @@ -561,7 +604,7 @@ void SetlogFeature::slotPlaylistTableRenamed(int playlistId, const QString& newN void SetlogFeature::activate() { // The root item was clicked, so actuvate the current playlist. - m_lastClickedIndex = QModelIndex(); + m_lastClickedIndex = m_pSidebarModel->getRootIndex(); activatePlaylist(m_playlistId); } @@ -571,18 +614,25 @@ void SetlogFeature::activatePlaylist(int playlistId) { return; } QModelIndex index = indexFromPlaylistId(playlistId); - if (index.isValid()) { - emit saveModelState(); - m_pPlaylistTableModel->setTableModel(playlistId); - emit showTrackModel(m_pPlaylistTableModel); - emit enableCoverArtDisplay(true); - // Update sidebar selection only if this is a child, incl. current playlist. - // indexFromPlaylistId() can't be used because, in case the root item was - // selected, that would switch to the 'current' child. - if (m_lastClickedIndex.isValid()) { - emit featureSelect(this, index); - activateChild(index); - } + VERIFY_OR_DEBUG_ASSERT(index.isValid()) { + return; + } + emit saveModelState(); + m_pPlaylistTableModel->setTableModel(playlistId); + emit showTrackModel(m_pPlaylistTableModel); + emit enableCoverArtDisplay(true); + // Update sidebar selection only if this is a child, incl. current playlist. + // indexFromPlaylistId() can't be used because, in case the root item was + // selected, that would switch to the 'current' child. + if (m_lastClickedIndex != m_pSidebarModel->getRootIndex()) { + m_lastClickedIndex = index; + emit featureSelect(this, index); + // redundant + // activateChild(index); + + // TODO(ronso0) Disable search for YEAR items + // emit disableSearch(); + // emit enableCoverArtDisplay(false); } } diff --git a/src/library/trackset/setlogfeature.h b/src/library/trackset/setlogfeature.h index fc6e4dfeb52..84f9f159d0a 100644 --- a/src/library/trackset/setlogfeature.h +++ b/src/library/trackset/setlogfeature.h @@ -45,7 +45,6 @@ class SetlogFeature : public BasePlaylistFeature { private: void deleteAllUnlockedPlaylistsWithFewerTracks(); - void reloadChildModel(int playlistId); QString getRootViewHtml() const override; std::list m_recentTracks;