Context: Remove albums

This commit is contained in:
Jonas Kvinge 2022-06-05 11:58:53 +02:00
parent 6c0b395f4a
commit 1ea70b085f
11 changed files with 3 additions and 1165 deletions

View File

@ -60,8 +60,6 @@ set(SOURCES
context/contextview.cpp
context/contextalbum.cpp
context/contextalbumsmodel.cpp
context/contextalbumsview.cpp
collection/collection.cpp
collection/collectionmodel.cpp
@ -299,8 +297,6 @@ set(HEADERS
context/contextview.h
context/contextalbum.h
context/contextalbumsmodel.h
context/contextalbumsview.h
collection/collection.h
collection/collectionmodel.h

View File

@ -1,390 +0,0 @@
/*
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <functional>
#include <algorithm>
#include <QObject>
#include <QtGlobal>
#include <QMutex>
#include <QMimeData>
#include <QMetaType>
#include <QVariant>
#include <QList>
#include <QSet>
#include <QRegularExpression>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QImage>
#include <QPixmapCache>
#include "core/application.h"
#include "core/database.h"
#include "core/iconloader.h"
#include "collection/collectionquery.h"
#include "collection/collectionbackend.h"
#include "collection/collectionmodel.h"
#include "collection/collectionitem.h"
#include "playlist/playlistmanager.h"
#include "playlist/songmimedata.h"
#include "covermanager/albumcoverloader.h"
#include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h"
#include "contextalbumsmodel.h"
const int ContextAlbumsModel::kPrettyCoverSize = 32;
ContextAlbumsModel::ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent)
: SimpleTreeModel<CollectionItem>(new CollectionItem(this), parent),
backend_(backend),
app_(app),
album_icon_(IconLoader::Load("cdcase")) {
root_->lazy_loaded = true;
cover_loader_options_.get_image_data_ = false;
cover_loader_options_.scale_output_image_ = true;
cover_loader_options_.pad_output_image_ = true;
cover_loader_options_.desired_height_ = kPrettyCoverSize;
QObject::connect(app_->album_cover_loader(), &AlbumCoverLoader::AlbumCoverLoaded, this, &ContextAlbumsModel::AlbumCoverLoaded);
QIcon nocover = IconLoader::Load("cdcase");
QList<QSize> nocover_sizes = nocover.availableSizes();
no_cover_icon_ = nocover.pixmap(nocover_sizes.last()).scaled(kPrettyCoverSize, kPrettyCoverSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
ContextAlbumsModel::~ContextAlbumsModel() { delete root_; }
void ContextAlbumsModel::AddSongs(const SongList &songs) {
for (const Song &song : songs) {
if (song_nodes_.contains(song.id())) continue;
QString key = CollectionModel::ContainerKey(CollectionModel::GroupBy_Album, song);
CollectionItem *container = nullptr;
if (container_nodes_.contains(key)) {
container = container_nodes_[key];
}
else {
container = ItemFromSong(CollectionItem::Type_Container, true, root_, song, 0);
container_nodes_.insert(key, container);
}
song_nodes_[song.id()] = ItemFromSong(CollectionItem::Type_Song, true, container, song, -1);
}
}
QString ContextAlbumsModel::AlbumIconPixmapCacheKey(const QModelIndex &idx) {
QStringList path;
QModelIndex index_copy(idx);
while (index_copy.isValid()) {
path.prepend(index_copy.data().toString());
index_copy = index_copy.parent();
}
return "contextalbumsart:" + path.join("/");
}
QVariant ContextAlbumsModel::AlbumIcon(const QModelIndex &idx) {
CollectionItem *item = IndexToItem(idx);
if (!item) return no_cover_icon_;
// Check the cache for a pixmap we already loaded.
const QString cache_key = AlbumIconPixmapCacheKey(idx);
QPixmap cached_pixmap;
if (QPixmapCache::find(cache_key, &cached_pixmap)) {
return cached_pixmap;
}
// Maybe we're loading a pixmap already?
if (pending_cache_keys_.contains(cache_key)) {
return no_cover_icon_;
}
// No art is cached and we're not loading it already. Load art for the first song in the album.
SongList songs = GetChildSongs(idx);
if (!songs.isEmpty()) {
const quint64 id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, songs.first());
pending_art_.insert(id, ItemAndCacheKey(item, cache_key));
pending_cache_keys_.insert(cache_key);
}
return no_cover_icon_;
}
void ContextAlbumsModel::AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result) {
if (!pending_art_.contains(id)) return;
ItemAndCacheKey item_and_cache_key = pending_art_.take(id);
CollectionItem *item = item_and_cache_key.first;
if (!item) return;
const QString &cache_key = item_and_cache_key.second;
if (pending_cache_keys_.contains(cache_key)) {
pending_cache_keys_.remove(cache_key);
}
// Insert this image in the cache.
if (!result.success || result.image_scaled.isNull() || result.type == AlbumCoverLoaderResult::Type_ManuallyUnset) {
// Set the no_cover image so we don't continually try to load art.
QPixmapCache::insert(cache_key, no_cover_icon_);
}
else {
QPixmap image_pixmap;
image_pixmap = QPixmap::fromImage(result.image_scaled);
QPixmapCache::insert(cache_key, image_pixmap);
}
const QModelIndex idx = ItemToIndex(item);
emit dataChanged(idx, idx);
}
QVariant ContextAlbumsModel::data(const QModelIndex &idx, int role) const {
const CollectionItem *item = IndexToItem(idx);
if (role == Qt::DecorationRole && item->type == CollectionItem::Type_Container && item->container_level == 0) {
return const_cast<ContextAlbumsModel*>(this)->AlbumIcon(idx);
}
return data(item, role);
}
QVariant ContextAlbumsModel::data(const CollectionItem *item, int role) const {
switch (role) {
case Qt::DisplayRole:
case Qt::ToolTipRole:
return item->DisplayText();
case Qt::DecorationRole:
switch (item->type) {
case CollectionItem::Type_Container:
if (item->type == CollectionItem::Type_Container && item->container_level == 0) { return album_icon_; }
break;
default:
break;
}
break;
case Role_Type:
return item->type;
case Role_ContainerType:
return item->type;
case Role_Key:
return item->key;
case Role_Artist:
return item->metadata.artist();
case Role_Editable:
if (item->type == CollectionItem::Type_Container) {
// if we have even one non editable item as a child, we ourselves are not available for edit
if (!item->children.isEmpty()) {
for (CollectionItem *child : item->children) {
if (!data(child, role).toBool()) {
return false;
}
}
return true;
}
else {
return false;
}
}
else if (item->type == CollectionItem::Type_Song) {
return item->metadata.IsEditable();
}
else {
return false;
}
case Role_SortText:
return item->SortText();
default:
return QVariant();
}
return QVariant();
}
void ContextAlbumsModel::Reset() {
for (QMap<QString, CollectionItem*>::const_iterator it = container_nodes_.constBegin(); it != container_nodes_.constEnd(); ++it) {
const QString cache_key = AlbumIconPixmapCacheKey(ItemToIndex(it.value()));
QPixmapCache::remove(cache_key);
}
beginResetModel();
delete root_;
song_nodes_.clear();
container_nodes_.clear();
pending_art_.clear();
pending_cache_keys_.clear();
root_ = new CollectionItem(this);
root_->lazy_loaded = true;
endResetModel();
}
CollectionItem *ContextAlbumsModel::ItemFromSong(CollectionItem::Type item_type, const bool signal, CollectionItem *parent, const Song &s, const int container_level) {
if (signal) beginInsertRows(ItemToIndex(parent), static_cast<int>(parent->children.count()), static_cast<int>(parent->children.count()));
CollectionItem *item = new CollectionItem(item_type, parent);
item->container_level = container_level;
item->lazy_loaded = true;
if (item_type == CollectionItem::Type_Container) {
item->key = CollectionModel::ContainerKey(CollectionModel::GroupBy_Album, s);
item->display_text = CollectionModel::TextOrUnknown(s.album());
item->sort_text = CollectionModel::SortTextForArtist(s.album());
}
else {
item->key = s.album() + " " + s.title();
item->display_text = CollectionModel::TextOrUnknown(s.title());
item->sort_text = CollectionModel::SortTextForSong(s);
item->metadata = s;
}
if (signal) endInsertRows();
return item;
}
Qt::ItemFlags ContextAlbumsModel::flags(const QModelIndex &idx) const {
switch (IndexToItem(idx)->type) {
case CollectionItem::Type_Song:
case CollectionItem::Type_Container:
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
case CollectionItem::Type_Root:
case CollectionItem::Type_LoadingIndicator:
default:
return Qt::ItemIsEnabled;
}
}
QStringList ContextAlbumsModel::mimeTypes() const {
return QStringList() << "text/uri-list";
}
QMimeData *ContextAlbumsModel::mimeData(const QModelIndexList &indexes) const {
if (indexes.isEmpty()) return nullptr;
SongMimeData *data = new SongMimeData;
QList<QUrl> urls;
QSet<int> song_ids;
data->backend = backend_;
for (const QModelIndex &idx : indexes) {
GetChildSongs(IndexToItem(idx), &urls, &data->songs, &song_ids);
}
data->setUrls(urls);
data->name_for_new_playlist_ = PlaylistManager::GetNameForNewPlaylist(data->songs);
return data;
}
bool ContextAlbumsModel::CompareItems(const CollectionItem *a, const CollectionItem *b) const {
QVariant left(data(a, ContextAlbumsModel::Role_SortText));
QVariant right(data(b, ContextAlbumsModel::Role_SortText));
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
if (left.metaType().id() == QMetaType::Int)
#else
if (left.type() == QVariant::Int)
#endif
return left.toInt() < right.toInt();
else return left.toString() < right.toString();
}
void ContextAlbumsModel::GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const {
switch (item->type) {
case CollectionItem::Type_Container:{
QList<CollectionItem*> children = item->children;
std::sort(children.begin(), children.end(), std::bind(&ContextAlbumsModel::CompareItems, this, std::placeholders::_1, std::placeholders::_2));
for (CollectionItem *child : children) {
GetChildSongs(child, urls, songs, song_ids);
}
break;
}
case CollectionItem::Type_Song:
urls->append(item->metadata.url());
if (!song_ids->contains(item->metadata.id())) {
songs->append(item->metadata);
song_ids->insert(item->metadata.id());
}
break;
default:
break;
}
}
SongList ContextAlbumsModel::GetChildSongs(const QModelIndexList &indexes) const {
QList<QUrl> dontcare;
SongList ret;
QSet<int> song_ids;
for (const QModelIndex &idx : indexes) {
GetChildSongs(IndexToItem(idx), &dontcare, &ret, &song_ids);
}
return ret;
}
SongList ContextAlbumsModel::GetChildSongs(const QModelIndex &idx) const {
return GetChildSongs(QModelIndexList() << idx);
}

View File

@ -1,111 +0,0 @@
/*
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CONTEXTALBUMSMODEL_H
#define CONTEXTALBUMSMODEL_H
#include "config.h"
#include <QtGlobal>
#include <QObject>
#include <QAbstractItemModel>
#include <QPair>
#include <QSet>
#include <QList>
#include <QMap>
#include <QVariant>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QImage>
#include <QPixmap>
#include <QIcon>
#include "core/simpletreemodel.h"
#include "core/song.h"
#include "collection/collectionquery.h"
#include "collection/collectionitem.h"
#include "covermanager/albumcoverloaderoptions.h"
#include "covermanager/albumcoverloaderresult.h"
class QMimeData;
class Application;
class CollectionBackend;
class CollectionItem;
class ContextAlbumsModel : public SimpleTreeModel<CollectionItem> {
Q_OBJECT
public:
explicit ContextAlbumsModel(CollectionBackend *backend, Application *app, QObject *parent = nullptr);
~ContextAlbumsModel() override;
static const int kPrettyCoverSize;
enum Role {
Role_Type = Qt::UserRole + 1,
Role_ContainerType,
Role_SortText,
Role_Key,
Role_Artist,
Role_Editable,
LastRole
};
void GetChildSongs(CollectionItem *item, QList<QUrl> *urls, SongList *songs, QSet<int> *song_ids) const;
SongList GetChildSongs(const QModelIndex &idx) const;
SongList GetChildSongs(const QModelIndexList &indexes) const;
QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &idx) const override;
QStringList mimeTypes() const override;
QMimeData *mimeData(const QModelIndexList &indexes) const override;
void Reset();
void AddSongs(const SongList &songs);
private slots:
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &result);
private:
CollectionItem *ItemFromSong(CollectionItem::Type item_type, const bool signal, CollectionItem *parent, const Song &s, const int container_level);
static QString AlbumIconPixmapCacheKey(const QModelIndex &idx);
QVariant AlbumIcon(const QModelIndex &idx);
QVariant data(const CollectionItem *item, int role) const;
bool CompareItems(const CollectionItem *a, const CollectionItem *b) const;
private:
CollectionBackend *backend_;
Application *app_;
QueryOptions query_options_;
QMap<QString, CollectionItem*> container_nodes_;
QMap<int, CollectionItem*> song_nodes_;
QIcon album_icon_;
QPixmap no_cover_icon_;
AlbumCoverLoaderOptions cover_loader_options_;
typedef QPair<CollectionItem*, QString> ItemAndCacheKey;
QMap<quint64, ItemAndCacheKey> pending_art_;
QSet<QString> pending_cache_keys_;
};
#endif // CONTEXTALBUMSMODEL_H

View File

@ -1,434 +0,0 @@
/*
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <memory>
#include <qcoreevent.h>
#include <QtGlobal>
#include <QTreeView>
#include <QItemSelectionModel>
#include <QSortFilterProxyModel>
#include <QAbstractItemView>
#include <QStyleOptionViewItem>
#include <QAbstractScrollArea>
#include <QMimeData>
#include <QList>
#include <QVariant>
#include <QString>
#include <QUrl>
#include <QLocale>
#include <QMessageBox>
#include <QMenu>
#include <QAction>
#include <QRect>
#include <QSize>
#include <QToolTip>
#include <QWhatsThis>
#include "core/application.h"
#include "core/iconloader.h"
#include "core/mimedata.h"
#include "core/utilities.h"
#include "collection/collectiondirectorymodel.h"
#include "collection/collectionmodel.h"
#include "collection/collectionitem.h"
#ifndef Q_OS_WIN
# include "device/devicemanager.h"
# include "device/devicestatefiltermodel.h"
#endif
#include "dialogs/edittagdialog.h"
#include "organize/organizedialog.h"
#include "contextalbumsmodel.h"
#include "contextalbumsview.h"
ContextItemDelegate::ContextItemDelegate(QObject *parent) : QStyledItemDelegate(parent) {}
bool ContextItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &idx) {
return true;
Q_UNUSED(option);
if (!event || !view) return false;
QString text = displayText(idx.data(), QLocale::system());
if (text.isEmpty() || !event) return false;
switch (event->type()) {
case QEvent::ToolTip: {
QSize real_text = sizeHint(option, idx);
QRect displayed_text = view->visualRect(idx);
bool is_elided = displayed_text.width() < real_text.width();
if (is_elided) {
QToolTip::showText(event->globalPos(), text, view);
}
else if (idx.data(Qt::ToolTipRole).isValid()) {
// If the item has a tooltip text, display it
QString tooltip_text = idx.data(Qt::ToolTipRole).toString();
QToolTip::showText(event->globalPos(), tooltip_text, view);
}
else {
// in case that another text was previously displayed
QToolTip::hideText();
}
return true;
}
case QEvent::QueryWhatsThis:
return true;
case QEvent::WhatsThis:
QWhatsThis::showText(event->globalPos(), text, view);
return true;
default:
break;
}
return false;
}
ContextAlbumsView::ContextAlbumsView(QWidget *parent)
: AutoExpandingTreeView(parent),
app_(nullptr),
context_menu_(nullptr),
load_(nullptr),
add_to_playlist_(nullptr),
add_to_playlist_enqueue_(nullptr),
open_in_new_playlist_(nullptr),
organize_(nullptr),
#ifndef Q_OS_WIN
copy_to_device_(nullptr),
#endif
edit_track_(nullptr),
edit_tracks_(nullptr),
show_in_browser_(nullptr),
is_in_keyboard_search_(false),
model_(nullptr) {
setStyleSheet("border: none;");
setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
setItemDelegate(new ContextItemDelegate(this));
setAttribute(Qt::WA_MacShowFocusRect, false);
setHeaderHidden(true);
setAllColumnsShowFocus(true);
setDragEnabled(true);
setDragDropMode(QAbstractItemView::DragOnly);
setSelectionMode(QAbstractItemView::ExtendedSelection);
SetAddOnDoubleClick(false);
}
ContextAlbumsView::~ContextAlbumsView() = default;
void ContextAlbumsView::SaveFocus() {
QModelIndex current = currentIndex();
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
if (!type.isValid() || !(type.toInt() == CollectionItem::Type_Song || type.toInt() == CollectionItem::Type_Container || type.toInt() == CollectionItem::Type_Divider)) {
return;
}
last_selected_path_.clear();
last_selected_song_ = Song();
last_selected_container_ = QString();
switch (type.toInt()) {
case CollectionItem::Type_Song: {
QModelIndex index = current;
SongList songs = model_->GetChildSongs(index);
if (!songs.isEmpty()) {
last_selected_song_ = songs.last();
}
break;
}
case CollectionItem::Type_Container:
case CollectionItem::Type_Divider: {
break;
}
default:
return;
}
SaveContainerPath(current);
}
void ContextAlbumsView::SaveContainerPath(const QModelIndex &child) {
QModelIndex current = model()->parent(child);
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
if (!type.isValid() || !(type.toInt() == CollectionItem::Type_Container || type.toInt() == CollectionItem::Type_Divider)) {
return;
}
QString text = model()->data(current, ContextAlbumsModel::Role_SortText).toString();
last_selected_path_ << text;
SaveContainerPath(current);
}
void ContextAlbumsView::RestoreFocus() {
if (last_selected_container_.isEmpty() && last_selected_song_.url().isEmpty()) {
return;
}
RestoreLevelFocus();
}
bool ContextAlbumsView::RestoreLevelFocus(const QModelIndex &parent) {
if (model()->canFetchMore(parent)) {
model()->fetchMore(parent);
}
int rows = model()->rowCount(parent);
for (int i = 0; i < rows; i++) {
QModelIndex current = model()->index(i, 0, parent);
QVariant type = model()->data(current, ContextAlbumsModel::Role_Type);
switch (type.toInt()) {
case CollectionItem::Type_Song:
if (!last_selected_song_.url().isEmpty()) {
QModelIndex index = qobject_cast<QSortFilterProxyModel*>(model())->mapToSource(current);
const SongList songs = model_->GetChildSongs(index);
if (std::any_of(songs.begin(), songs.end(), [this](const Song &song) { return song == last_selected_song_; })) {
setCurrentIndex(current);
return true;
}
}
break;
}
}
return false;
}
void ContextAlbumsView::Init(Application *app) {
app_ = app;
model_ = new ContextAlbumsModel(app_->collection_backend(), app_, this);
model_->Reset();
setModel(model_);
QObject::connect(model_, &ContextAlbumsModel::modelAboutToBeReset, this, &ContextAlbumsView::SaveFocus);
QObject::connect(model_, &ContextAlbumsModel::modelReset, this, &ContextAlbumsView::RestoreFocus);
}
void ContextAlbumsView::paintEvent(QPaintEvent *event) {
QTreeView::paintEvent(event);
}
void ContextAlbumsView::mouseReleaseEvent(QMouseEvent *e) {
QTreeView::mouseReleaseEvent(e);
}
void ContextAlbumsView::contextMenuEvent(QContextMenuEvent *e) {
if (!context_menu_) {
context_menu_ = new QMenu(this);
add_to_playlist_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Append to current playlist"), this, &ContextAlbumsView::AddToPlaylist);
load_ = context_menu_->addAction(IconLoader::Load("media-playback-start"), tr("Replace current playlist"), this, &ContextAlbumsView::Load);
open_in_new_playlist_ = context_menu_->addAction(IconLoader::Load("document-new"), tr("Open in new playlist"), this, &ContextAlbumsView::OpenInNewPlaylist);
context_menu_->addSeparator();
add_to_playlist_enqueue_ = context_menu_->addAction(IconLoader::Load("go-next"), tr("Queue track"), this, &ContextAlbumsView::AddToPlaylistEnqueue);
context_menu_->addSeparator();
organize_ = context_menu_->addAction(IconLoader::Load("edit-copy"), tr("Organize files..."), this, &ContextAlbumsView::Organize);
#ifndef Q_OS_WIN
copy_to_device_ = context_menu_->addAction(IconLoader::Load("device"), tr("Copy to device..."), this, &ContextAlbumsView::CopyToDevice);
#endif
context_menu_->addSeparator();
edit_track_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit track information..."), this, &ContextAlbumsView::EditTracks);
edit_tracks_ = context_menu_->addAction(IconLoader::Load("edit-rename"), tr("Edit tracks information..."), this, &ContextAlbumsView::EditTracks);
show_in_browser_ = context_menu_->addAction(IconLoader::Load("document-open-folder"), tr("Show in file browser..."), this, &ContextAlbumsView::ShowInBrowser);
context_menu_->addSeparator();
#ifndef Q_OS_WIN
copy_to_device_->setDisabled(app_->device_manager()->connected_devices_model()->rowCount() == 0);
QObject::connect(app_->device_manager()->connected_devices_model(), &DeviceStateFilterModel::IsEmptyChanged, copy_to_device_, &QAction::setDisabled);
#endif
}
context_menu_index_ = indexAt(e->pos());
if (!context_menu_index_.isValid()) return;
QModelIndexList selected_indexes = selectionModel()->selectedRows();
int regular_elements = 0;
int regular_editable = 0;
for (const QModelIndex &idx : selected_indexes) {
regular_elements++;
if (model_->data(idx, ContextAlbumsModel::Role_Editable).toBool()) {
regular_editable++;
}
}
// TODO: check if custom plugin actions should be enabled / visible
const int songs_selected = regular_elements;
const bool regular_elements_only = songs_selected == regular_elements && regular_elements > 0;
// in all modes
load_->setEnabled(songs_selected > 0);
add_to_playlist_->setEnabled(songs_selected > 0);
open_in_new_playlist_->setEnabled(songs_selected > 0);
add_to_playlist_enqueue_->setEnabled(songs_selected > 0);
// if neither edit_track not edit_tracks are available, we show disabled edit_track element
edit_track_->setVisible(regular_editable <= 1);
edit_track_->setEnabled(regular_editable == 1);
organize_->setVisible(regular_elements_only);
#ifndef Q_OS_WIN
copy_to_device_->setVisible(regular_elements_only);
#endif
// only when all selected items are editable
organize_->setEnabled(regular_elements == regular_editable);
#ifndef Q_OS_WIN
copy_to_device_->setEnabled(regular_elements == regular_editable);
#endif
context_menu_->popup(e->globalPos());
}
void ContextAlbumsView::Load() {
QMimeData *q_mimedata = model()->mimeData(selectedIndexes());
if (MimeData *mimedata = qobject_cast<MimeData*>(q_mimedata)) {
mimedata->clear_first_ = true;
}
emit AddToPlaylistSignal(q_mimedata);
}
void ContextAlbumsView::AddToPlaylist() {
emit AddToPlaylistSignal(model()->mimeData(selectedIndexes()));
}
void ContextAlbumsView::AddToPlaylistEnqueue() {
QMimeData *q_mimedata = model()->mimeData(selectedIndexes());
if (MimeData *mimedata = qobject_cast<MimeData*>(q_mimedata)) {
mimedata->enqueue_now_ = true;
}
emit AddToPlaylistSignal(q_mimedata);
}
void ContextAlbumsView::OpenInNewPlaylist() {
QMimeData *q_mimedata = model()->mimeData(selectedIndexes());
if (MimeData *mimedata = qobject_cast<MimeData*>(q_mimedata)) {
mimedata->open_in_new_playlist_ = true;
}
emit AddToPlaylistSignal(q_mimedata);
}
void ContextAlbumsView::scrollTo(const QModelIndex &idx, ScrollHint hint) {
if (is_in_keyboard_search_) {
QTreeView::scrollTo(idx, QAbstractItemView::PositionAtTop);
}
else {
QTreeView::scrollTo(idx, hint);
}
}
SongList ContextAlbumsView::GetSelectedSongs() const {
QModelIndexList selected_indexes = selectionModel()->selectedRows();
return model_->GetChildSongs(selected_indexes);
}
void ContextAlbumsView::Organize() {
if (!organize_dialog_) {
organize_dialog_ = std::make_unique<OrganizeDialog>(app_->task_manager(), app_->collection_backend(), this);
}
organize_dialog_->SetDestinationModel(app_->collection_model()->directory_model());
organize_dialog_->SetCopy(false);
if (organize_dialog_->SetSongs(GetSelectedSongs())) {
organize_dialog_->show();
}
else {
QMessageBox::warning(this, tr("Error"), tr("None of the selected songs were suitable for copying to a device"));
}
}
void ContextAlbumsView::EditTracks() {
if (!edit_tag_dialog_) {
edit_tag_dialog_ = std::make_unique<EditTagDialog>(app_, this);
}
edit_tag_dialog_->SetSongs(GetSelectedSongs());
edit_tag_dialog_->show();
}
void ContextAlbumsView::CopyToDevice() {
#ifndef Q_OS_WIN
if (!organize_dialog_) {
organize_dialog_ = std::make_unique<OrganizeDialog>(app_->task_manager(), nullptr, this);
}
organize_dialog_->SetDestinationModel(app_->device_manager()->connected_devices_model(), true);
organize_dialog_->SetCopy(true);
organize_dialog_->SetSongs(GetSelectedSongs());
organize_dialog_->show();
#endif
}
void ContextAlbumsView::ShowInBrowser() const {
const SongList songs = GetSelectedSongs();
QList<QUrl> urls;
urls.reserve(songs.count());
for (const Song &song : songs) {
urls << song.url();
}
Utilities::OpenInFileBrowser(urls);
}

View File

@ -1,137 +0,0 @@
/*
* Strawberry Music Player
* This code was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2013-2021, Jonas Kvinge <jonas@jkvinge.net>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CONTEXTALBUMSVIEW_H
#define CONTEXTALBUMSVIEW_H
#include "config.h"
#include <memory>
#include <QObject>
#include <QAbstractItemModel>
#include <QAbstractItemView>
#include <QStyledItemDelegate>
#include <QStyleOption>
#include <QSet>
#include <QString>
#include "core/song.h"
#include "widgets/autoexpandingtreeview.h"
class QWidget;
class QMenu;
class QAction;
class QContextMenuEvent;
class QHelpEvent;
class QMouseEvent;
class QPaintEvent;
class Application;
class ContextAlbumsModel;
class EditTagDialog;
class OrganizeDialog;
class ContextItemDelegate : public QStyledItemDelegate {
Q_OBJECT
public:
explicit ContextItemDelegate(QObject *parent);
public slots:
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &idx) override;
};
class ContextAlbumsView : public AutoExpandingTreeView {
Q_OBJECT
public:
explicit ContextAlbumsView(QWidget *parent = nullptr);
~ContextAlbumsView() override;
// Returns Songs currently selected in the collection view.
// Please note that the selection is recursive meaning that if for example an album is selected this will return all of it's songs.
SongList GetSelectedSongs() const;
void Init(Application *app);
// QTreeView
void scrollTo(const QModelIndex &idx, ScrollHint hint = EnsureVisible) override;
ContextAlbumsModel *albums_model() { return model_; }
public slots:
void SaveFocus();
void RestoreFocus();
protected:
// QWidget
void paintEvent(QPaintEvent *event) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void contextMenuEvent(QContextMenuEvent *e) override;
private slots:
void Load();
void AddToPlaylist();
void AddToPlaylistEnqueue();
void OpenInNewPlaylist();
void Organize();
void CopyToDevice();
void EditTracks();
void ShowInBrowser() const;
private:
void RecheckIsEmpty();
bool RestoreLevelFocus(const QModelIndex &parent = QModelIndex());
void SaveContainerPath(const QModelIndex &child);
private:
Application *app_;
QMenu *context_menu_;
QModelIndex context_menu_index_;
QAction *load_;
QAction *add_to_playlist_;
QAction *add_to_playlist_enqueue_;
QAction *open_in_new_playlist_;
QAction *organize_;
#ifndef Q_OS_WIN
QAction *copy_to_device_;
#endif
QAction *edit_track_;
QAction *edit_tracks_;
QAction *show_in_browser_;
std::unique_ptr<OrganizeDialog> organize_dialog_;
std::unique_ptr<EditTagDialog> edit_tag_dialog_;
bool is_in_keyboard_search_;
// Save focus
Song last_selected_song_;
QString last_selected_container_;
QSet<QString> last_selected_path_;
ContextAlbumsModel *model_;
};
#endif // CONTEXTALBUMSVIEW_H

View File

@ -68,8 +68,6 @@
#include "contextview.h"
#include "contextalbum.h"
#include "contextalbumsmodel.h"
#include "contextalbumsview.h"
ContextView::ContextView(QWidget *parent)
: QWidget(parent),
@ -81,7 +79,6 @@ ContextView::ContextView(QWidget *parent)
action_show_album_(nullptr),
action_show_data_(nullptr),
action_show_output_(nullptr),
action_show_albums_(nullptr),
action_show_lyrics_(nullptr),
action_search_lyrics_(nullptr),
layout_container_(new QVBoxLayout()),
@ -101,12 +98,9 @@ ContextView::ContextView(QWidget *parent)
widget_play_output_(new QWidget(this)),
layout_play_data_(new QGridLayout()),
layout_play_output_(new QGridLayout()),
label_play_albums_(new QLabel(this)),
textedit_play_lyrics_(new ResizableTextEdit(this)),
widget_albums_(new ContextAlbumsView(this)),
spacer_play_output_(new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Fixed)),
spacer_play_data_(new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Fixed)),
spacer_play_albums_(new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Fixed)),
spacer_play_bottom_(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Minimum)),
label_filetype_title_(new QLabel(this)),
label_length_title_(new QLabel(this)),
@ -246,18 +240,11 @@ ContextView::ContextView(QWidget *parent)
textedit_play_lyrics_->setFrameShape(QFrame::NoFrame);
textedit_play_lyrics_->hide();
widget_albums_->hide();
label_play_albums_->setWordWrap(true);
label_play_albums_->hide();
layout_play_->setContentsMargins(0, 0, 0, 0);
layout_play_->addWidget(widget_play_output_);
layout_play_->addSpacerItem(spacer_play_output_);
layout_play_->addWidget(widget_play_data_);
layout_play_->addSpacerItem(spacer_play_data_);
layout_play_->addWidget(label_play_albums_);
layout_play_->addWidget(widget_albums_);
layout_play_->addSpacerItem(spacer_play_albums_);
layout_play_->addWidget(textedit_play_lyrics_);
layout_play_->addSpacerItem(spacer_play_bottom_);
@ -267,8 +254,7 @@ ContextView::ContextView(QWidget *parent)
<< label_length_title_
<< label_samplerate_title_
<< label_bitdepth_title_
<< label_bitrate_title_
<< label_play_albums_;
<< label_bitrate_title_;
labels_play_data_ << label_engine_icon_
<< label_engine_
@ -278,8 +264,7 @@ ContextView::ContextView(QWidget *parent)
<< label_length_
<< label_samplerate_
<< label_bitdepth_
<< label_bitrate_
<< label_play_albums_;
<< label_bitrate_;
labels_play_all_ = labels_play_ << labels_play_data_;
@ -296,7 +281,6 @@ void ContextView::Init(Application *app, CollectionView *collectionview, AlbumCo
album_cover_choice_controller_ = album_cover_choice_controller;
widget_album_->Init(this, album_cover_choice_controller_);
widget_albums_->Init(app_);
lyrics_fetcher_ = new LyricsFetcher(app_->lyrics_providers(), this);
QObject::connect(collectionview_, &CollectionView::TotalSongCountUpdated_, this, &ContextView::UpdateNoSong);
@ -322,10 +306,6 @@ void ContextView::AddActions() {
action_show_output_->setCheckable(true);
action_show_output_->setChecked(true);
action_show_albums_ = new QAction(tr("Show albums by artist"), this);
action_show_albums_->setCheckable(true);
action_show_albums_->setChecked(false);
action_show_lyrics_ = new QAction(tr("Show song lyrics"), this);
action_show_lyrics_->setCheckable(true);
action_show_lyrics_->setChecked(true);
@ -337,17 +317,15 @@ void ContextView::AddActions() {
menu_options_->addAction(action_show_album_);
menu_options_->addAction(action_show_data_);
menu_options_->addAction(action_show_output_);
menu_options_->addAction(action_show_albums_);
menu_options_->addAction(action_show_lyrics_);
menu_options_->addAction(action_search_lyrics_);
menu_options_->addSeparator();
ReloadSettings();
QObject::connect(action_show_album_, &QAction::triggered, this, &ContextView::ActionShowAlbums);
QObject::connect(action_show_album_, &QAction::triggered, this, &ContextView::ActionShowAlbum);
QObject::connect(action_show_data_, &QAction::triggered, this, &ContextView::ActionShowData);
QObject::connect(action_show_output_, &QAction::triggered, this, &ContextView::ActionShowOutput);
QObject::connect(action_show_albums_, &QAction::triggered, this, &ContextView::ActionShowAlbums);
QObject::connect(action_show_lyrics_, &QAction::triggered, this, &ContextView::ActionShowLyrics);
QObject::connect(action_search_lyrics_, &QAction::triggered, this, &ContextView::ActionSearchLyrics);
@ -362,7 +340,6 @@ void ContextView::ReloadSettings() {
action_show_album_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ALBUM], true).toBool());
action_show_data_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::TECHNICAL_DATA], false).toBool());
action_show_output_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ENGINE_AND_DEVICE], false).toBool());
action_show_albums_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ALBUMS_BY_ARTIST], false).toBool());
action_show_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::SONG_LYRICS], true).toBool());
action_search_lyrics_->setChecked(s.value(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::SEARCH_LYRICS], true).toBool());
font_headline_ = s.value("font_headline", font().family()).toString();
@ -476,7 +453,6 @@ void ContextView::UpdateFonts() {
for (QTextEdit *e : textedit_play_) {
e->setStyleSheet(font_style);
}
label_play_albums_->setStyleSheet(QString("background-color: #3DADE8; color: rgb(255, 255, 255); font: %1pt \"%2\"; font-weight: regular;").arg(font_size_normal_).arg(font_normal_));
}
@ -596,35 +572,6 @@ void ContextView::SetSong() {
spacer_play_output_->changeSize(0, 0, QSizePolicy::Fixed);
}
if (action_show_albums_->isChecked() && song_prev_.artist() != song_playing_.artist()) {
CollectionBackend::AlbumList albumlist;
widget_albums_->albums_model()->Reset();
albumlist = app_->collection_backend()->GetAlbumsByArtist(song_playing_.effective_albumartist());
if (albumlist.count() > 1) {
label_play_albums_->show();
widget_albums_->show();
label_play_albums_->setText("<b>" + tr("Albums by %1").arg(song_playing_.effective_albumartist().toHtmlEscaped()) + "</b>");
for (const CollectionBackend::Album &album : albumlist) {
SongList songs = app_->collection_backend()->GetAlbumSongs(song_playing_.effective_albumartist(), album.album);
widget_albums_->albums_model()->AddSongs(songs);
}
spacer_play_albums_->changeSize(20, 10, QSizePolicy::Fixed);
}
else {
label_play_albums_->hide();
widget_albums_->hide();
label_play_albums_->clear();
spacer_play_albums_->changeSize(0, 0, QSizePolicy::Fixed);
}
}
else if (!action_show_albums_->isChecked()) {
label_play_albums_->hide();
widget_albums_->hide();
label_play_albums_->clear();
widget_albums_->albums_model()->Reset();
spacer_play_albums_->changeSize(0, 0, QSizePolicy::Fixed);
}
if (action_show_lyrics_->isChecked() && !lyrics_.isEmpty()) {
textedit_play_lyrics_->setText(lyrics_);
textedit_play_lyrics_->show();
@ -794,17 +741,6 @@ void ContextView::ActionShowOutput() {
}
void ContextView::ActionShowAlbums() {
QSettings s;
s.beginGroup(ContextSettingsPage::kSettingsGroup);
s.setValue(ContextSettingsPage::kSettingsGroupEnable[ContextSettingsPage::ContextSettingsOrder::ALBUMS_BY_ARTIST], action_show_albums_->isChecked());
s.endGroup();
song_prev_ = Song();
if (song_playing_.is_valid()) SetSong();
}
void ContextView::ActionShowLyrics() {
QSettings s;

View File

@ -49,7 +49,6 @@ class ResizableTextEdit;
class Application;
class CollectionView;
class AlbumCoverChoiceController;
class ContextAlbumsView;
class LyricsFetcher;
class ContextView : public QWidget {
@ -61,7 +60,6 @@ class ContextView : public QWidget {
void Init(Application *app, CollectionView *collectionview, AlbumCoverChoiceController *album_cover_choice_controller);
ContextAlbum *album_widget() const { return widget_album_; }
ContextAlbumsView *albums_widget() const { return widget_albums_; }
bool album_enabled() const { return action_show_album_->isChecked(); }
Song song_playing() const { return song_playing_; }
@ -88,7 +86,6 @@ class ContextView : public QWidget {
void ActionShowAlbum();
void ActionShowData();
void ActionShowOutput();
void ActionShowAlbums();
void ActionShowLyrics();
void ActionSearchLyrics();
void UpdateNoSong();
@ -113,7 +110,6 @@ class ContextView : public QWidget {
QAction *action_show_album_;
QAction *action_show_data_;
QAction *action_show_output_;
QAction *action_show_albums_;
QAction *action_show_lyrics_;
QAction *action_search_lyrics_;
@ -134,13 +130,10 @@ class ContextView : public QWidget {
QWidget *widget_play_output_;
QGridLayout *layout_play_data_;
QGridLayout *layout_play_output_;
QLabel *label_play_albums_;
ResizableTextEdit *textedit_play_lyrics_;
ContextAlbumsView *widget_albums_;
QSpacerItem *spacer_play_output_;
QSpacerItem *spacer_play_data_;
QSpacerItem *spacer_play_albums_;
QSpacerItem *spacer_play_bottom_;
QLabel *label_filetype_title_;

View File

@ -120,7 +120,6 @@
#include "widgets/trackslider.h"
#include "osd/osdbase.h"
#include "context/contextview.h"
#include "context/contextalbumsview.h"
#include "collection/collection.h"
#include "collection/collectionbackend.h"
#include "collection/collectiondirectorymodel.h"
@ -833,7 +832,6 @@ MainWindow::MainWindow(Application *app, std::shared_ptr<SystemTrayIcon> tray_ic
QObject::connect(this, &MainWindow::AlbumCoverReady, context_view_, &ContextView::AlbumCoverLoaded);
QObject::connect(this, &MainWindow::SearchCoverInProgress, context_view_->album_widget(), &ContextAlbum::SearchCoverInProgress);
QObject::connect(context_view_, &ContextView::AlbumEnabledChanged, this, &MainWindow::TabSwitched);
QObject::connect(context_view_->albums_widget(), &ContextAlbumsView::AddToPlaylistSignal, this, &MainWindow::AddToPlaylist);
// Analyzer
QObject::connect(ui_->analyzer, &AnalyzerContainer::WheelEvent, this, &MainWindow::VolumeWheelEvent);

View File

@ -52,7 +52,6 @@ const char *ContextSettingsPage::kSettingsGroupEnable[ContextSettingsOrder::NELE
"AlbumEnable",
"EngineAndDeviceEnable",
"TechnicalDataEnable",
"AlbumsByArtistEnable",
"SongLyricsEnable",
"SearchCoverEnable",
"SearchLyricsEnable",
@ -70,7 +69,6 @@ ContextSettingsPage::ContextSettingsPage(SettingsDialog *dialog, QWidget *parent
checkboxes_[ContextSettingsOrder::ALBUM] = ui_->checkbox_album;
checkboxes_[ContextSettingsOrder::ENGINE_AND_DEVICE] = ui_->checkbox_engine_device;
checkboxes_[ContextSettingsOrder::TECHNICAL_DATA] = ui_->checkbox_technical_data;
checkboxes_[ContextSettingsOrder::ALBUMS_BY_ARTIST] = ui_->checkbox_albums;
checkboxes_[ContextSettingsOrder::SONG_LYRICS] = ui_->checkbox_song_lyrics;
checkboxes_[ContextSettingsOrder::SEARCH_COVER] = ui_->checkbox_search_cover;
checkboxes_[ContextSettingsOrder::SEARCH_LYRICS] = ui_->checkbox_search_lyrics;

View File

@ -46,7 +46,6 @@ class ContextSettingsPage : public SettingsPage {
ALBUM,
ENGINE_AND_DEVICE,
TECHNICAL_DATA,
ALBUMS_BY_ARTIST,
SONG_LYRICS,
SEARCH_COVER,
SEARCH_LYRICS,

View File

@ -170,16 +170,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkbox_albums">
<property name="text">
<string>Albums by Artist</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkbox_song_lyrics">
<property name="text">