2019-05-27 21:10:37 +02:00
/*
* Strawberry Music Player
* This code was part of Clementine
* Copyright 2010 , David Sansome < me @ davidsansome . com >
2021-03-20 21:14:47 +01:00
* Copyright 2018 - 2021 , Jonas Kvinge < jonas @ jkvinge . net >
2019-05-27 21:10:37 +02:00
*
* 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"
2024-04-23 17:15:42 +02:00
# include <utility>
2020-02-09 02:29:35 +01:00
# include <QWidget>
2019-05-27 21:10:37 +02:00
# include <QTreeView>
# include <QSortFilterProxyModel>
# include <QAbstractItemView>
2020-02-09 02:29:35 +01:00
# include <QItemSelectionModel>
2019-05-27 21:10:37 +02:00
# include <QVariant>
# include <QString>
# include <QPainter>
# include <QRect>
# include <QFont>
# include <QFontMetrics>
# include <QMimeData>
# include <QMenu>
# include <QtEvents>
2023-07-21 05:55:24 +02:00
# include "core/shared_ptr.h"
2019-05-27 21:10:37 +02:00
# include "core/application.h"
# include "core/iconloader.h"
# include "core/mimedata.h"
# include "collection/collectionbackend.h"
# include "collection/collectionmodel.h"
# include "collection/collectionfilterwidget.h"
# include "collection/collectionitem.h"
# include "collection/collectionitemdelegate.h"
# include "internetcollectionview.h"
InternetCollectionView : : InternetCollectionView ( QWidget * parent )
: AutoExpandingTreeView ( parent ) ,
app_ ( nullptr ) ,
collection_backend_ ( nullptr ) ,
collection_model_ ( nullptr ) ,
filter_ ( nullptr ) ,
2019-06-19 23:26:15 +02:00
favorite_ ( false ) ,
2019-05-27 21:10:37 +02:00
total_song_count_ ( 0 ) ,
total_artist_count_ ( 0 ) ,
total_album_count_ ( 0 ) ,
2024-04-09 23:20:26 +02:00
nomusic_ ( QStringLiteral ( " :/pictures/nomusic.png " ) ) ,
2019-05-27 21:10:37 +02:00
context_menu_ ( nullptr ) ,
2019-06-19 23:26:15 +02:00
load_ ( nullptr ) ,
add_to_playlist_ ( nullptr ) ,
add_to_playlist_enqueue_ ( nullptr ) ,
add_to_playlist_enqueue_next_ ( nullptr ) ,
open_in_new_playlist_ ( nullptr ) ,
remove_songs_ ( nullptr ) ,
2021-07-11 07:40:57 +02:00
is_in_keyboard_search_ ( false ) {
2019-05-27 21:10:37 +02:00
setItemDelegate ( new CollectionItemDelegate ( this ) ) ;
setAttribute ( Qt : : WA_MacShowFocusRect , false ) ;
setHeaderHidden ( true ) ;
setAllColumnsShowFocus ( true ) ;
setDragEnabled ( true ) ;
setDragDropMode ( QAbstractItemView : : DragOnly ) ;
setSelectionMode ( QAbstractItemView : : ExtendedSelection ) ;
SetAutoOpen ( false ) ;
2024-04-09 23:20:26 +02:00
setStyleSheet ( QStringLiteral ( " QTreeView::item{padding-top:1px;} " ) ) ;
2019-05-27 21:10:37 +02:00
}
2023-07-21 05:55:24 +02:00
void InternetCollectionView : : Init ( Application * app , SharedPtr < CollectionBackend > collection_backend , CollectionModel * collection_model , const bool favorite ) {
2019-05-27 21:10:37 +02:00
app_ = app ;
2023-07-21 05:55:24 +02:00
collection_backend_ = collection_backend ;
collection_model_ = collection_model ;
2019-06-19 23:26:15 +02:00
favorite_ = favorite ;
2019-05-27 21:10:37 +02:00
collection_model_ - > set_pretty_covers ( true ) ;
collection_model_ - > set_show_dividers ( true ) ;
2024-03-11 11:09:33 +01:00
collection_model_ - > set_sort_skips_articles ( true ) ;
2019-05-27 21:10:37 +02:00
ReloadSettings ( ) ;
}
2020-04-20 18:01:45 +02:00
void InternetCollectionView : : SetFilter ( CollectionFilterWidget * filter ) {
filter_ = filter ;
}
2019-05-27 21:10:37 +02:00
2020-06-28 18:36:48 +02:00
void InternetCollectionView : : ReloadSettings ( ) {
if ( filter_ ) filter_ - > ReloadSettings ( ) ;
}
2019-05-27 21:10:37 +02:00
void InternetCollectionView : : SaveFocus ( ) {
QModelIndex current = currentIndex ( ) ;
QVariant type = model ( ) - > data ( current , CollectionModel : : Role_Type ) ;
2023-06-06 23:19:45 +02:00
if ( ! type . isValid ( ) | | ( type . toInt ( ) ! = CollectionItem : : Type_Song & & type . toInt ( ) ! = CollectionItem : : Type_Container & & type . toInt ( ) ! = CollectionItem : : Type_Divider ) ) {
2019-05-27 21:10:37 +02:00
return ;
}
last_selected_path_ . clear ( ) ;
last_selected_song_ = Song ( ) ;
last_selected_container_ = QString ( ) ;
switch ( type . toInt ( ) ) {
2024-04-11 02:56:01 +02:00
case CollectionItem : : Type_Song : {
2021-01-26 16:48:04 +01:00
QModelIndex idx = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapToSource ( current ) ;
SongList songs = collection_model_ - > GetChildSongs ( idx ) ;
2019-05-27 21:10:37 +02:00
if ( ! songs . isEmpty ( ) ) {
last_selected_song_ = songs . last ( ) ;
}
break ;
}
case CollectionItem : : Type_Container :
2024-04-11 02:56:01 +02:00
case CollectionItem : : Type_Divider : {
2019-05-27 21:10:37 +02:00
QString text = model ( ) - > data ( current , CollectionModel : : Role_SortText ) . toString ( ) ;
last_selected_container_ = text ;
break ;
}
default :
return ;
}
SaveContainerPath ( current ) ;
}
void InternetCollectionView : : SaveContainerPath ( const QModelIndex & child ) {
QModelIndex current = model ( ) - > parent ( child ) ;
QVariant type = model ( ) - > data ( current , CollectionModel : : Role_Type ) ;
2023-06-06 23:19:45 +02:00
if ( ! type . isValid ( ) | | ( type . toInt ( ) ! = CollectionItem : : Type_Container & & type . toInt ( ) ! = CollectionItem : : Type_Divider ) ) {
2019-05-27 21:10:37 +02:00
return ;
}
QString text = model ( ) - > data ( current , CollectionModel : : Role_SortText ) . toString ( ) ;
last_selected_path_ < < text ;
SaveContainerPath ( current ) ;
}
void InternetCollectionView : : RestoreFocus ( ) {
if ( last_selected_container_ . isEmpty ( ) & & last_selected_song_ . url ( ) . isEmpty ( ) ) {
return ;
}
RestoreLevelFocus ( ) ;
}
bool InternetCollectionView : : 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 , CollectionModel : : Role_Type ) ;
switch ( type . toInt ( ) ) {
case CollectionItem : : Type_Song :
if ( ! last_selected_song_ . url ( ) . isEmpty ( ) ) {
2021-01-26 16:48:04 +01:00
QModelIndex idx = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapToSource ( current ) ;
2024-04-23 17:15:42 +02:00
const SongList songs = collection_model_ - > GetChildSongs ( idx ) ;
2019-05-27 21:10:37 +02:00
for ( const Song & song : songs ) {
if ( song = = last_selected_song_ ) {
setCurrentIndex ( current ) ;
return true ;
}
}
}
break ;
case CollectionItem : : Type_Container :
2024-04-11 02:56:01 +02:00
case CollectionItem : : Type_Divider : {
2019-05-27 21:10:37 +02:00
QString text = model ( ) - > data ( current , CollectionModel : : Role_SortText ) . toString ( ) ;
if ( ! last_selected_container_ . isEmpty ( ) & & last_selected_container_ = = text ) {
2021-03-21 04:47:11 +01:00
expand ( current ) ;
2019-05-27 21:10:37 +02:00
setCurrentIndex ( current ) ;
return true ;
}
else if ( last_selected_path_ . contains ( text ) ) {
2021-03-21 04:47:11 +01:00
expand ( current ) ;
2019-05-27 21:10:37 +02:00
// If a selected container or song were not found, we've got into a wrong subtree (happens with "unknown" all the time)
if ( ! RestoreLevelFocus ( current ) ) {
2021-03-21 04:47:11 +01:00
collapse ( current ) ;
2019-05-27 21:10:37 +02:00
}
else {
return true ;
}
}
break ;
}
}
}
return false ;
}
void InternetCollectionView : : TotalSongCountUpdated ( int count ) {
int old = total_song_count_ ;
total_song_count_ = count ;
if ( old ! = total_song_count_ ) update ( ) ;
2021-08-23 21:21:08 +02:00
if ( total_song_count_ = = 0 ) {
2019-05-27 21:10:37 +02:00
setCursor ( Qt : : PointingHandCursor ) ;
2021-08-23 21:21:08 +02:00
}
else {
2019-05-27 21:10:37 +02:00
unsetCursor ( ) ;
2021-08-23 21:21:08 +02:00
}
2019-05-27 21:10:37 +02:00
emit TotalSongCountUpdated_ ( ) ;
}
void InternetCollectionView : : TotalArtistCountUpdated ( int count ) {
int old = total_artist_count_ ;
total_artist_count_ = count ;
if ( old ! = total_artist_count_ ) update ( ) ;
2021-08-23 21:21:08 +02:00
if ( total_artist_count_ = = 0 ) {
2019-05-27 21:10:37 +02:00
setCursor ( Qt : : PointingHandCursor ) ;
2021-08-23 21:21:08 +02:00
}
else {
2019-05-27 21:10:37 +02:00
unsetCursor ( ) ;
2021-08-23 21:21:08 +02:00
}
2019-05-27 21:10:37 +02:00
emit TotalArtistCountUpdated_ ( ) ;
}
void InternetCollectionView : : TotalAlbumCountUpdated ( int count ) {
int old = total_album_count_ ;
total_album_count_ = count ;
if ( old ! = total_album_count_ ) update ( ) ;
2021-08-23 21:21:08 +02:00
if ( total_album_count_ = = 0 ) {
2019-05-27 21:10:37 +02:00
setCursor ( Qt : : PointingHandCursor ) ;
2021-08-23 21:21:08 +02:00
}
else {
2019-05-27 21:10:37 +02:00
unsetCursor ( ) ;
2021-08-23 21:21:08 +02:00
}
2019-05-27 21:10:37 +02:00
emit TotalAlbumCountUpdated_ ( ) ;
}
void InternetCollectionView : : paintEvent ( QPaintEvent * event ) {
if ( total_song_count_ = = 0 ) {
QPainter p ( viewport ( ) ) ;
QRect rect ( viewport ( ) - > rect ( ) ) ;
// Draw the confused strawberry
QRect image_rect ( ( rect . width ( ) - nomusic_ . width ( ) ) / 2 , 50 , nomusic_ . width ( ) , nomusic_ . height ( ) ) ;
p . drawPixmap ( image_rect , nomusic_ ) ;
// Draw the title text
QFont bold_font ;
bold_font . setBold ( true ) ;
p . setFont ( bold_font ) ;
QFontMetrics metrics ( bold_font ) ;
QRect title_rect ( 0 , image_rect . bottom ( ) + 20 , rect . width ( ) , metrics . height ( ) ) ;
p . drawText ( title_rect , Qt : : AlignHCenter , tr ( " The internet collection is empty! " ) ) ;
// Draw the other text
p . setFont ( QFont ( ) ) ;
QRect text_rect ( 0 , title_rect . bottom ( ) + 5 , rect . width ( ) , metrics . height ( ) ) ;
p . drawText ( text_rect , Qt : : AlignHCenter , tr ( " Click here to retrieve music " ) ) ;
}
else {
QTreeView : : paintEvent ( event ) ;
}
}
void InternetCollectionView : : mouseReleaseEvent ( QMouseEvent * e ) {
QTreeView : : mouseReleaseEvent ( e ) ;
if ( total_song_count_ = = 0 ) {
emit GetSongs ( ) ;
}
}
void InternetCollectionView : : contextMenuEvent ( QContextMenuEvent * e ) {
if ( ! context_menu_ ) {
context_menu_ = new QMenu ( this ) ;
2024-04-09 23:20:26 +02:00
add_to_playlist_ = context_menu_ - > addAction ( IconLoader : : Load ( QStringLiteral ( " media-playback-start " ) ) , tr ( " Append to current playlist " ) , this , & InternetCollectionView : : AddToPlaylist ) ;
load_ = context_menu_ - > addAction ( IconLoader : : Load ( QStringLiteral ( " media-playback-start " ) ) , tr ( " Replace current playlist " ) , this , & InternetCollectionView : : Load ) ;
open_in_new_playlist_ = context_menu_ - > addAction ( IconLoader : : Load ( QStringLiteral ( " document-new " ) ) , tr ( " Open in new playlist " ) , this , & InternetCollectionView : : OpenInNewPlaylist ) ;
2019-05-27 21:10:37 +02:00
context_menu_ - > addSeparator ( ) ;
2024-04-09 23:20:26 +02:00
add_to_playlist_enqueue_ = context_menu_ - > addAction ( IconLoader : : Load ( QStringLiteral ( " go-next " ) ) , tr ( " Queue track " ) , this , & InternetCollectionView : : AddToPlaylistEnqueue ) ;
add_to_playlist_enqueue_next_ = context_menu_ - > addAction ( IconLoader : : Load ( QStringLiteral ( " go-next " ) ) , tr ( " Queue to play next " ) , this , & InternetCollectionView : : AddToPlaylistEnqueueNext ) ;
2019-05-27 21:10:37 +02:00
context_menu_ - > addSeparator ( ) ;
2019-06-19 23:26:15 +02:00
if ( favorite_ ) {
2024-04-09 23:20:26 +02:00
remove_songs_ = context_menu_ - > addAction ( IconLoader : : Load ( QStringLiteral ( " edit-delete " ) ) , tr ( " Remove from favorites " ) , this , & InternetCollectionView : : RemoveSelectedSongs ) ;
2019-06-19 23:26:15 +02:00
context_menu_ - > addSeparator ( ) ;
}
2019-05-27 21:10:37 +02:00
if ( filter_ ) context_menu_ - > addMenu ( filter_ - > menu ( ) ) ;
}
context_menu_index_ = indexAt ( e - > pos ( ) ) ;
if ( ! context_menu_index_ . isValid ( ) ) return ;
context_menu_index_ = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapToSource ( context_menu_index_ ) ;
QModelIndexList selected_indexes = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapSelectionToSource ( selectionModel ( ) - > selection ( ) ) . indexes ( ) ;
2021-10-30 02:21:29 +02:00
qint64 songs_selected = selected_indexes . count ( ) ;
2019-05-27 21:10:37 +02:00
// In all modes
2021-06-22 13:54:58 +02:00
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 ( remove_songs_ ) remove_songs_ - > setEnabled ( songs_selected > 0 ) ;
2019-05-27 21:10:37 +02:00
context_menu_ - > popup ( e - > globalPos ( ) ) ;
}
void InternetCollectionView : : Load ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
mimedata - > clear_first_ = true ;
2019-05-27 21:10:37 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2019-05-27 21:10:37 +02:00
}
void InternetCollectionView : : AddToPlaylist ( ) {
emit AddToPlaylistSignal ( model ( ) - > mimeData ( selectedIndexes ( ) ) ) ;
}
void InternetCollectionView : : AddToPlaylistEnqueue ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
mimedata - > enqueue_now_ = true ;
2019-05-27 21:10:37 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2019-05-27 21:10:37 +02:00
}
void InternetCollectionView : : AddToPlaylistEnqueueNext ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
mimedata - > enqueue_next_now_ = true ;
2019-05-27 21:10:37 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2019-05-27 21:10:37 +02:00
}
void InternetCollectionView : : OpenInNewPlaylist ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
2022-03-22 21:09:05 +01:00
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
2020-04-23 21:08:28 +02:00
mimedata - > open_in_new_playlist_ = true ;
2019-05-27 21:10:37 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2019-05-27 21:10:37 +02:00
}
2021-01-26 16:48:04 +01:00
void InternetCollectionView : : RemoveSelectedSongs ( ) {
2019-05-27 21:10:37 +02:00
emit RemoveSongs ( GetSelectedSongs ( ) ) ;
}
void InternetCollectionView : : keyboardSearch ( const QString & search ) {
is_in_keyboard_search_ = true ;
QTreeView : : keyboardSearch ( search ) ;
is_in_keyboard_search_ = false ;
}
2021-01-26 16:48:04 +01:00
void InternetCollectionView : : scrollTo ( const QModelIndex & idx , ScrollHint hint ) {
2019-05-27 21:10:37 +02:00
2021-08-23 21:21:08 +02:00
if ( is_in_keyboard_search_ ) {
2021-01-26 16:48:04 +01:00
QTreeView : : scrollTo ( idx , QAbstractItemView : : PositionAtTop ) ;
2021-08-23 21:21:08 +02:00
}
else {
2021-01-26 16:48:04 +01:00
QTreeView : : scrollTo ( idx , hint ) ;
2021-08-23 21:21:08 +02:00
}
2019-05-27 21:10:37 +02:00
}
SongList InternetCollectionView : : GetSelectedSongs ( ) const {
QModelIndexList selected_indexes = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapSelectionToSource ( selectionModel ( ) - > selection ( ) ) . indexes ( ) ;
return collection_model_ - > GetChildSongs ( selected_indexes ) ;
}
void InternetCollectionView : : FilterReturnPressed ( ) {
if ( ! currentIndex ( ) . isValid ( ) ) {
// Pick the first thing that isn't a divider
for ( int row = 0 ; row < model ( ) - > rowCount ( ) ; + + row ) {
QModelIndex idx ( model ( ) - > index ( row , 0 ) ) ;
if ( idx . data ( CollectionModel : : Role_Type ) ! = CollectionItem : : Type_Divider ) {
setCurrentIndex ( idx ) ;
break ;
}
}
}
if ( ! currentIndex ( ) . isValid ( ) ) return ;
emit doubleClicked ( currentIndex ( ) ) ;
}
2021-06-22 13:45:29 +02:00
int InternetCollectionView : : TotalSongs ( ) const {
2019-05-27 21:10:37 +02:00
return total_song_count_ ;
}
2021-06-22 13:45:29 +02:00
int InternetCollectionView : : TotalArtists ( ) const {
2019-05-27 21:10:37 +02:00
return total_artist_count_ ;
}
2021-06-22 13:45:29 +02:00
int InternetCollectionView : : TotalAlbums ( ) const {
2019-05-27 21:10:37 +02:00
return total_album_count_ ;
}