diff --git a/ChangeLog b/ChangeLog index 1e0e54a61..0ee29768d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -109,6 +109,7 @@ (Qt-only builds) 67. If connection to MPD faiils, attempt to ascertain if its a proxy error. 68. Add button under playqueue to re-center playqueue on current song. +69. Add option to only act on songs that pass current string or genre filter. 1.3.4 ----- diff --git a/devices/devicespage.cpp b/devices/devicespage.cpp index 0f31f8bb8..c43bd4128 100644 --- a/devices/devicespage.cpp +++ b/devices/devicespage.cpp @@ -209,13 +209,8 @@ QList DevicesPage::selectedSongs(bool allowPlaylists) const } // Ensure all songs are from UMS/Remote devices... - QString udi; - QModelIndexList mapped; foreach (const QModelIndex &idx, selected) { - QModelIndex index = proxy.mapToSource(idx); - mapped.append(index); - MusicLibraryItem *item=static_cast(index.internalPointer()); - + MusicLibraryItem *item=static_cast(proxy.mapToSource(idx).internalPointer()); if (item && MusicLibraryItem::Type_Root!=item->itemType()) { while(item->parentItem()) { item=item->parentItem(); @@ -230,7 +225,7 @@ QList DevicesPage::selectedSongs(bool allowPlaylists) const } } - return DevicesModel::self()->songs(mapped); + return DevicesModel::self()->songs(proxy.mapToSource(selected, Settings::self()->filteredOnly())); } void DevicesPage::addSelectionToPlaylist(const QString &name, bool replace, quint8 priorty, bool randomAlbums) diff --git a/gui/albumspage.cpp b/gui/albumspage.cpp index 75b13a2c0..ff739b57c 100644 --- a/gui/albumspage.cpp +++ b/gui/albumspage.cpp @@ -124,29 +124,45 @@ QStringList AlbumsPage::selectedFiles(bool allowPlaylists, bool randomAlbums) co return QStringList(); } - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - mapped.append(proxy.mapToSource(idx)); - } - + QModelIndexList mapped=proxy.mapToSource(selected, Settings::self()->filteredOnly()); if (randomAlbums) { - QModelIndexList albumIndexes; - foreach (const QModelIndex &idx, mapped) { - if (static_cast(idx.internalPointer())->isAlbum()) { - albumIndexes.append(idx); + if (Settings::self()->filteredOnly()) { + QMap albums; + foreach (const QModelIndex &idx, mapped) { + if (idx.parent().isValid()) { + albums[idx.parent().row()].append(idx); + } + } + QList keys=albums.keys(); + if (keys.isEmpty()) { + return QStringList(); + } else if (1==keys.count()) { + mapped=albums.begin().value(); + } else { + mapped.clear(); + while (!keys.isEmpty()) { + mapped.append(albums[keys.takeAt(Utils::random(keys.count()))]); + } } - } - - if (albumIndexes.isEmpty()) { - return QStringList(); - } - - if (1==albumIndexes.count()) { - mapped=albumIndexes; } else { - mapped.clear(); - while (!albumIndexes.isEmpty()) { - mapped.append(albumIndexes.takeAt(Utils::random(albumIndexes.count()))); + QModelIndexList albumIndexes; + foreach (const QModelIndex &idx, mapped) { + if (static_cast(idx.internalPointer())->isAlbum()) { + albumIndexes.append(idx); + } + } + + if (albumIndexes.isEmpty()) { + return QStringList(); + } + + if (1==albumIndexes.count()) { + mapped=albumIndexes; + } else { + mapped.clear(); + while (!albumIndexes.isEmpty()) { + mapped.append(albumIndexes.takeAt(Utils::random(albumIndexes.count()))); + } } } } diff --git a/gui/folderpage.cpp b/gui/folderpage.cpp index 510ac09da..e8b44d981 100644 --- a/gui/folderpage.cpp +++ b/gui/folderpage.cpp @@ -228,13 +228,7 @@ QStringList FolderPage::selectedFiles(bool allowPlaylists) const if (selected.isEmpty()) { return QStringList(); } - - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - mapped.append(proxy.mapToSource(idx)); - } - - return DirViewModel::self()->filenames(mapped, allowPlaylists); + return DirViewModel::self()->filenames(proxy.mapToSource(selected, Settings::self()->filteredOnly()), allowPlaylists); } void FolderPage::addSelectionToPlaylist(const QString &name, bool replace, quint8 priorty, bool randomAlbums) diff --git a/gui/interfacesettings.cpp b/gui/interfacesettings.cpp index 9fb12fad9..75375797d 100644 --- a/gui/interfacesettings.cpp +++ b/gui/interfacesettings.cpp @@ -244,6 +244,7 @@ void InterfaceSettings::load() #endif groupSingle->setChecked(Settings::self()->groupSingle()); useComposer->setChecked(Settings::self()->useComposer()); + filteredOnly->setChecked(Settings::self()->filteredOnly()); #ifdef ENABLE_DEVICES_SUPPORT showDeleteAction->setChecked(Settings::self()->showDeleteAction()); selectEntry(devicesView, Settings::self()->devicesView()); @@ -325,6 +326,7 @@ void InterfaceSettings::save() #endif Settings::self()->saveGroupSingle(groupSingle->isChecked()); Settings::self()->saveUseComposer(useComposer->isChecked()); + Settings::self()->saveFilteredOnly(filteredOnly->isChecked()); #ifdef ENABLE_DEVICES_SUPPORT Settings::self()->saveShowDeleteAction(showDeleteAction->isChecked()); Settings::self()->saveDevicesView(getValue(devicesView)); diff --git a/gui/interfacesettings.ui b/gui/interfacesettings.ui index dd964933c..904825566 100644 --- a/gui/interfacesettings.ui +++ b/gui/interfacesettings.ui @@ -751,34 +751,44 @@ within the folder of the current track, or within its parent folder. If no image + + + <p>When addig artists, or albums to the playqueue, or editing tags, etc, then only use songs that pass the current Genre or string filter.</p><p>e.g. If you have an album with 10 songs, but only 5 have the genre set to 'Metal', then only these 5 would be added if the view's Genre filter was set to 'Metal'. + + + Only act on songs that pass filtering + + + + Fetch missing covers from Last.fm - + Cache scaled covers - + Show delete action in context menus - + Enforce single-click activation of items - + <p>This will change Cantata's interface as detailed: @@ -789,7 +799,7 @@ within the folder of the current track, or within its parent folder. If no image - + Language: @@ -799,7 +809,7 @@ within the folder of the current track, or within its parent folder. If no image - + diff --git a/gui/librarypage.cpp b/gui/librarypage.cpp index bc6f38ae6..da8a3bcf5 100644 --- a/gui/librarypage.cpp +++ b/gui/librarypage.cpp @@ -140,44 +140,60 @@ QStringList LibraryPage::selectedFiles(bool allowPlaylists, bool randomAlbums) c return QStringList(); } - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - mapped.append(proxy.mapToSource(idx)); - } - + QModelIndexList mapped=proxy.mapToSource(selected, Settings::self()->filteredOnly()); if (randomAlbums) { - QModelIndexList albumIndexes; - QSet albumNames; - foreach (const QModelIndex &idx, mapped) { - MusicLibraryItem *item=static_cast(idx.internalPointer()); - if (MusicLibraryItem::Type_Album==item->itemType()) { - QString name=nameKey(item->parentItem()->data(), item->data()); - if (!albumNames.contains(name)) { - albumNames.insert(name); - albumIndexes.append(idx); + if (Settings::self()->filteredOnly()) { + QMap albums; + foreach (const QModelIndex &idx, mapped) { + if (idx.parent().isValid() && idx.parent().parent().isValid()) { + albums[(idx.parent().parent().row()<<16)+idx.parent().row()].append(idx); } - } else if (MusicLibraryItem::Type_Artist==item->itemType()) { - for (int row=0; row(item)->childCount(); ++row) { - MusicLibraryItem *album=static_cast(item)->childItem(row); - QString name=nameKey(item->data(), album->data()); + } + QList keys=albums.keys(); + if (keys.isEmpty()) { + return QStringList(); + } else if (1==keys.count()) { + mapped=albums.begin().value(); + } else { + mapped.clear(); + while (!keys.isEmpty()) { + mapped.append(albums[keys.takeAt(Utils::random(keys.count()))]); + } + } + } else { + QModelIndexList albumIndexes; + QSet albumNames; + foreach (const QModelIndex &idx, mapped) { + MusicLibraryItem *item=static_cast(idx.internalPointer()); + if (MusicLibraryItem::Type_Album==item->itemType()) { + QString name=nameKey(item->parentItem()->data(), item->data()); if (!albumNames.contains(name)) { albumNames.insert(name); - albumIndexes.append(MusicLibraryModel::self()->index(row, 0, idx)); + albumIndexes.append(idx); + } + } else if (MusicLibraryItem::Type_Artist==item->itemType()) { + for (int row=0; row(item)->childCount(); ++row) { + MusicLibraryItem *album=static_cast(item)->childItem(row); + QString name=nameKey(item->data(), album->data()); + if (!albumNames.contains(name)) { + albumNames.insert(name); + albumIndexes.append(MusicLibraryModel::self()->index(row, 0, idx)); + } } } } - } - if (albumIndexes.isEmpty()) { - return QStringList(); - } + if (albumIndexes.isEmpty()) { + return QStringList(); + } - if (1==albumIndexes.count()) { - mapped=albumIndexes; - } else { - mapped.clear(); - while (!albumIndexes.isEmpty()) { - mapped.append(albumIndexes.takeAt(Utils::random(albumIndexes.count()))); + if (1==albumIndexes.count()) { + mapped=albumIndexes; + } else { + mapped.clear(); + while (!albumIndexes.isEmpty()) { + mapped.append(albumIndexes.takeAt(Utils::random(albumIndexes.count()))); + } } } } @@ -191,13 +207,7 @@ QList LibraryPage::selectedSongs(bool allowPlaylists) const if (selected.isEmpty()) { return QList(); } - - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - mapped.append(proxy.mapToSource(idx)); - } - - return MusicLibraryModel::self()->songs(mapped, allowPlaylists); + return MusicLibraryModel::self()->songs(proxy.mapToSource(selected, Settings::self()->filteredOnly()), allowPlaylists); } Song LibraryPage::coverRequest() const diff --git a/gui/playlistspage.cpp b/gui/playlistspage.cpp index f60bd9ec5..a02549db3 100644 --- a/gui/playlistspage.cpp +++ b/gui/playlistspage.cpp @@ -32,6 +32,7 @@ #include "actioncollection.h" #include "mpdconnection.h" #include "tableview.h" +#include "settings.h" #include #ifdef ENABLE_KDE_SUPPORT #include @@ -339,7 +340,7 @@ void PlaylistsPage::addItemsToPlayList(const QModelIndexList &indexes, const QSt // If we only have 1 item selected, see if it is a playlist. If so, we might be able to // just ask MPD to load it... - if (name.isEmpty() && 1==indexes.count() && 0==priorty) { + if (name.isEmpty() && 1==indexes.count() && 0==priorty && (!proxy.enabled() || !Settings::self()->filteredOnly())) { QModelIndex idx=proxy.mapToSource(*(indexes.begin())); PlaylistsModel::Item *item=static_cast(idx.internalPointer()); @@ -349,11 +350,9 @@ void PlaylistsPage::addItemsToPlayList(const QModelIndexList &indexes, const QSt } } - QModelIndexList mapped; - bool checkNames=!name.isEmpty(); - foreach (const QModelIndex &idx, indexes) { - QModelIndex m=proxy.mapToSource(idx); - if (checkNames) { + if (!name.isEmpty()) { + foreach (const QModelIndex &idx, indexes) { + QModelIndex m=proxy.mapToSource(idx); PlaylistsModel::Item *item=static_cast(m.internalPointer()); if ( (item->isPlaylist() && static_cast(item)->name==name) || (!item->isPlaylist() && static_cast(item)->parent->name==name) ) { @@ -361,10 +360,9 @@ void PlaylistsPage::addItemsToPlayList(const QModelIndexList &indexes, const QSt return; } } - mapped.append(m); } - QStringList files=PlaylistsModel::self()->filenames(mapped); + QStringList files=PlaylistsModel::self()->filenames(proxy.mapToSource(indexes, Settings::self()->filteredOnly())); if (!files.isEmpty()) { if (name.isEmpty()) { emit add(files, replace, priorty); @@ -385,12 +383,7 @@ QList PlaylistsPage::selectedSongs(bool allowPlaylists) const return QList(); } - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - mapped.append(proxy.mapToSource(idx)); - } - - return PlaylistsModel::self()->songs(mapped); + return PlaylistsModel::self()->songs(proxy.mapToSource(selected, Settings::self()->filteredOnly())); } void PlaylistsPage::addSelectionToDevice(const QString &udi) diff --git a/gui/settings.cpp b/gui/settings.cpp index 78b0232e8..23083149f 100644 --- a/gui/settings.cpp +++ b/gui/settings.cpp @@ -936,6 +936,11 @@ bool Settings::touchFriendly() return cfg.get("touchFriendly", false); } +bool Settings::filteredOnly() +{ + return cfg.get("filteredOnly", false); +} + void Settings::removeConnectionDetails(const QString &v) { if (v==currentConnection()) { @@ -1452,6 +1457,11 @@ void Settings::saveTouchFriendly(bool v) cfg.set("touchFriendly", v); } +void Settings::saveFilteredOnly(bool v) +{ + cfg.set("filteredOnly", v); +} + void Settings::save(bool force) { if (force) { diff --git a/gui/settings.h b/gui/settings.h index 2fcca2aeb..ba3f2634f 100644 --- a/gui/settings.h +++ b/gui/settings.h @@ -188,6 +188,7 @@ public: bool showMenubar(); int menu(); bool touchFriendly(); + bool filteredOnly(); void removeConnectionDetails(const QString &v); void saveConnectionDetails(const MPDConnectionDetails &v); @@ -299,6 +300,7 @@ public: #endif void saveShowMenubar(bool v); void saveTouchFriendly(bool v); + void saveFilteredOnly(bool v); void save(bool force=false); #if defined ENABLE_KDE_SUPPORT && defined ENABLE_KWALLET bool openWallet(); diff --git a/models/proxymodel.cpp b/models/proxymodel.cpp index e6787c8e4..422b350cc 100644 --- a/models/proxymodel.cpp +++ b/models/proxymodel.cpp @@ -22,10 +22,14 @@ */ #include "proxymodel.h" +#ifndef ENABLE_UBUNTU +#include "settings.h" +#endif #include #include #include #include +#include bool ProxyModel::matchesFilter(const Song &s) const { @@ -161,3 +165,55 @@ QList ProxyModel::mapToSourceRows(const QModelIndexList &list) const } return rows; } + +QModelIndexList ProxyModel::mapToSource(const QModelIndexList &list, bool leavesOnly) const +{ + QModelIndexList mapped; + if (leavesOnly) { + QModelIndexList l=leaves(list); + foreach (const QModelIndex &idx, l) { + mapped.append(mapToSource(idx)); + } + } else { + foreach (const QModelIndex &idx, list) { + mapped.append(mapToSource(idx)); + } + } + return mapped; +} + +#ifndef ENABLE_UBUNTU +QMimeData * ProxyModel::mimeData(const QModelIndexList &indexes) const +{ + QModelIndexList nodes=Settings::self()->filteredOnly() ? leaves(indexes) : indexes; + QModelIndexList sourceIndexes; + foreach (const QModelIndex &idx, nodes) { + sourceIndexes << mapToSource(idx); + } + return sourceModel()->mimeData(sourceIndexes); +} +#endif + +QModelIndexList ProxyModel::leaves(const QModelIndexList &list) const +{ + QModelIndexList l; + + foreach (const QModelIndex &idx, list) { + l+=leaves(idx); + } + return l; +} + +QModelIndexList ProxyModel::leaves(const QModelIndex &idx) const +{ + QModelIndexList list; + int rc=rowCount(idx); + if (rc>0) { + for (int i=0; i #include #include "song.h" +#include "config.h" + +class QMimeData; class ProxyModel : public QSortFilterProxyModel { @@ -45,11 +48,20 @@ public: void sort() { isSorted=false; sort(0); } void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); QList mapToSourceRows(const QModelIndexList &list) const; + QModelIndex mapToSource(const QModelIndex &idx) const { return QSortFilterProxyModel::mapToSource(idx); } + QModelIndexList mapToSource(const QModelIndexList &list, bool leavesOnly) const; + #ifndef ENABLE_UBUNTU + QMimeData * mimeData(const QModelIndexList &indexes) const; + #endif + QModelIndexList leaves(const QModelIndexList &list) const; protected: bool matchesFilter(const Song &s) const; bool matchesFilter(const QStringList &strings) const; +private: + QModelIndexList leaves(const QModelIndex &idx) const; + protected: bool isSorted; bool filterEnabled; diff --git a/online/onlineservicespage.cpp b/online/onlineservicespage.cpp index e1fb8713f..c7dbf4352 100644 --- a/online/onlineservicespage.cpp +++ b/online/onlineservicespage.cpp @@ -178,13 +178,7 @@ QStringList OnlineServicesPage::selectedFiles() const if (selected.isEmpty()) { return QStringList(); } - - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - mapped.append(proxy.mapToSource(idx)); - } - - return OnlineServicesModel::self()->filenames(mapped); + return OnlineServicesModel::self()->filenames(proxy.mapToSource(selected, Settings::self()->filteredOnly())); } QList OnlineServicesPage::selectedSongs(bool allowPlaylists) const @@ -194,21 +188,7 @@ QList OnlineServicesPage::selectedSongs(bool allowPlaylists) const if (selected.isEmpty()) { return QList(); } - - QModelIndexList mapped; - foreach (const QModelIndex &idx, selected) { - QModelIndex index = proxy.mapToSource(idx); - mapped.append(index); - MusicLibraryItem *item=static_cast(index.internalPointer()); - - if (item && MusicLibraryItem::Type_Root!=item->itemType()) { - while(item->parentItem()) { - item=item->parentItem(); - } - } - } - - return OnlineServicesModel::self()->songs(mapped); + return OnlineServicesModel::self()->songs(proxy.mapToSource(selected, Settings::self()->filteredOnly())); } void OnlineServicesPage::addSelectionToPlaylist(const QString &name, bool replace, quint8 priorty, bool randomAlbums)