2018-02-27 18:06:05 +01:00
/*
* Strawberry Music Player
* This file 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 >
2018-02-27 18:06:05 +01: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/>.
2018-08-09 18:39:44 +02:00
*
2018-02-27 18:06:05 +01:00
*/
# include "config.h"
2021-06-21 15:40:25 +02:00
# include <memory>
2018-05-01 00:41:33 +02:00
# include <QtGlobal>
# include <QWidget>
2020-02-08 03:40:30 +01:00
# include <QAbstractItemView>
# include <QTreeView>
2018-05-01 00:41:33 +02:00
# include <QItemSelectionModel>
# include <QSortFilterProxyModel>
2020-02-08 03:40:30 +01:00
# include <QMimeData>
# include <QSet>
# include <QList>
# include <QMap>
2018-05-01 00:41:33 +02:00
# include <QVariant>
# include <QString>
# include <QUrl>
2020-02-08 03:40:30 +01:00
# include <QPixmap>
2018-05-01 00:41:33 +02:00
# include <QPainter>
# include <QRect>
# include <QFont>
# include <QFontMetrics>
2020-02-08 03:40:30 +01:00
# include <QMenu>
# include <QAction>
# include <QMessageBox>
2018-05-01 00:41:33 +02:00
# include <QSettings>
# include <QtEvents>
2018-02-27 18:06:05 +01:00
# include "core/application.h"
2018-05-01 00:41:33 +02:00
# include "core/iconloader.h"
2018-02-27 18:06:05 +01:00
# include "core/mimedata.h"
2020-08-19 22:35:35 +02:00
# include "core/musicstorage.h"
2020-08-19 22:02:35 +02:00
# include "core/deletefiles.h"
2022-12-28 03:12:00 +01:00
# include "utilities/filemanagerutils.h"
2019-06-30 21:06:07 +02:00
# include "collection.h"
2018-05-01 00:41:33 +02:00
# include "collectionbackend.h"
# include "collectiondirectorymodel.h"
# include "collectionfilterwidget.h"
# include "collectionitem.h"
2019-05-27 21:10:37 +02:00
# include "collectionitemdelegate.h"
2018-05-01 00:41:33 +02:00
# include "collectionmodel.h"
# include "collectionview.h"
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
# include "device / devicemanager.h"
# include "device / devicestatefiltermodel.h"
# endif
2018-05-01 00:41:33 +02:00
# include "dialogs/edittagdialog.h"
2020-08-19 22:02:35 +02:00
# include "dialogs/deleteconfirmationdialog.h"
2020-08-04 21:18:14 +02:00
# include "organize/organizedialog.h"
2020-08-19 22:02:35 +02:00
# include "organize/organizeerrordialog.h"
2018-02-27 18:06:05 +01:00
# include "settings/collectionsettingspage.h"
2023-07-21 05:55:24 +02:00
using std : : make_unique ;
2018-02-27 18:06:05 +01:00
CollectionView : : CollectionView ( QWidget * parent )
: AutoExpandingTreeView ( parent ) ,
app_ ( nullptr ) ,
filter_ ( nullptr ) ,
total_song_count_ ( - 1 ) ,
total_artist_count_ ( - 1 ) ,
total_album_count_ ( - 1 ) ,
nomusic_ ( " :/pictures/nomusic.png " ) ,
context_menu_ ( nullptr ) ,
2020-08-19 22:02:35 +02:00
action_load_ ( nullptr ) ,
action_add_to_playlist_ ( nullptr ) ,
action_add_to_playlist_enqueue_ ( nullptr ) ,
action_add_to_playlist_enqueue_next_ ( nullptr ) ,
action_open_in_new_playlist_ ( nullptr ) ,
action_organize_ ( nullptr ) ,
# ifndef Q_OS_WIN
action_copy_to_device_ ( nullptr ) ,
# endif
action_edit_track_ ( nullptr ) ,
action_edit_tracks_ ( nullptr ) ,
action_rescan_songs_ ( nullptr ) ,
action_show_in_browser_ ( nullptr ) ,
action_show_in_various_ ( nullptr ) ,
action_no_show_in_various_ ( nullptr ) ,
2021-06-20 23:53:28 +02:00
action_delete_files_ ( nullptr ) ,
2020-08-19 22:02:35 +02:00
is_in_keyboard_search_ ( false ) ,
2021-07-11 07:40:57 +02:00
delete_files_ ( false ) {
2018-02-27 18:06:05 +01:00
setItemDelegate ( new CollectionItemDelegate ( this ) ) ;
setAttribute ( Qt : : WA_MacShowFocusRect , false ) ;
setHeaderHidden ( true ) ;
setAllColumnsShowFocus ( true ) ;
setDragEnabled ( true ) ;
setDragDropMode ( QAbstractItemView : : DragOnly ) ;
setSelectionMode ( QAbstractItemView : : ExtendedSelection ) ;
setStyleSheet ( " QTreeView::item{padding-top:1px;} " ) ;
}
2020-06-15 21:55:05 +02:00
CollectionView : : ~ CollectionView ( ) = default ;
2018-02-27 18:06:05 +01:00
void CollectionView : : 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 ) ) {
2018-02-27 18:06:05 +01:00
return ;
}
last_selected_path_ . clear ( ) ;
last_selected_song_ = Song ( ) ;
last_selected_container_ = QString ( ) ;
switch ( type . toInt ( ) ) {
case CollectionItem : : Type_Song : {
QModelIndex index = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapToSource ( current ) ;
SongList songs = app_ - > collection_model ( ) - > GetChildSongs ( index ) ;
if ( ! songs . isEmpty ( ) ) {
last_selected_song_ = songs . last ( ) ;
}
break ;
}
case CollectionItem : : Type_Container :
case CollectionItem : : Type_Divider : {
QString text = model ( ) - > data ( current , CollectionModel : : Role_SortText ) . toString ( ) ;
last_selected_container_ = text ;
break ;
}
default :
return ;
}
SaveContainerPath ( current ) ;
}
void CollectionView : : 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 ) ) {
2018-02-27 18:06:05 +01:00
return ;
}
QString text = model ( ) - > data ( current , CollectionModel : : Role_SortText ) . toString ( ) ;
last_selected_path_ < < text ;
SaveContainerPath ( current ) ;
}
void CollectionView : : RestoreFocus ( ) {
if ( last_selected_container_ . isEmpty ( ) & & last_selected_song_ . url ( ) . isEmpty ( ) ) {
return ;
}
RestoreLevelFocus ( ) ;
}
bool CollectionView : : 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 ( ) ) {
QModelIndex index = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapToSource ( current ) ;
2021-06-21 19:52:37 +02:00
const SongList songs = app_ - > collection_model ( ) - > GetChildSongs ( index ) ;
if ( std : : any_of ( songs . begin ( ) , songs . end ( ) , [ this ] ( const Song & song ) { return song = = last_selected_song_ ; } ) ) {
setCurrentIndex ( current ) ;
return true ;
2018-02-27 18:06:05 +01:00
}
}
break ;
case CollectionItem : : Type_Container :
case CollectionItem : : Type_Divider : {
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 ) ;
2018-02-27 18:06:05 +01:00
setCurrentIndex ( current ) ;
return true ;
}
else if ( last_selected_path_ . contains ( text ) ) {
2021-03-21 04:47:11 +01:00
expand ( current ) ;
2018-05-01 00:41:33 +02:00
// If a selected container or song were not found, we've got into a wrong subtree (happens with "unknown" all the time)
2018-02-27 18:06:05 +01:00
if ( ! RestoreLevelFocus ( current ) ) {
2021-03-21 04:47:11 +01:00
collapse ( current ) ;
2018-02-27 18:06:05 +01:00
}
else {
return true ;
}
}
break ;
}
}
}
return false ;
}
void CollectionView : : ReloadSettings ( ) {
QSettings settings ;
settings . beginGroup ( CollectionSettingsPage : : kSettingsGroup ) ;
2018-08-29 21:42:24 +02:00
SetAutoOpen ( settings . value ( " auto_open " , false ) . toBool ( ) ) ;
2018-02-27 18:06:05 +01:00
2018-08-29 21:42:24 +02:00
if ( app_ ) {
2018-02-27 18:06:05 +01:00
app_ - > collection_model ( ) - > set_pretty_covers ( settings . value ( " pretty_covers " , true ) . toBool ( ) ) ;
app_ - > collection_model ( ) - > set_show_dividers ( settings . value ( " show_dividers " , true ) . toBool ( ) ) ;
}
2018-10-02 00:38:52 +02:00
2020-08-19 22:02:35 +02:00
delete_files_ = settings . value ( " delete_files " , false ) . toBool ( ) ;
2018-02-27 18:06:05 +01:00
settings . endGroup ( ) ;
}
void CollectionView : : SetApplication ( Application * app ) {
app_ = app ;
ReloadSettings ( ) ;
}
void CollectionView : : SetFilter ( CollectionFilterWidget * filter ) { filter_ = filter ; }
2020-09-10 22:05:12 +02:00
void CollectionView : : TotalSongCountUpdated ( const int count ) {
2018-02-27 18:06:05 +01:00
2019-04-08 18:46:11 +02:00
int old = total_song_count_ ;
2018-02-27 18:06:05 +01:00
total_song_count_ = count ;
if ( old ! = total_song_count_ ) update ( ) ;
2021-08-23 21:21:08 +02:00
if ( total_song_count_ = = 0 ) {
2018-02-27 18:06:05 +01:00
setCursor ( Qt : : PointingHandCursor ) ;
2021-08-23 21:21:08 +02:00
}
else {
2018-02-27 18:06:05 +01:00
unsetCursor ( ) ;
2021-08-23 21:21:08 +02:00
}
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
emit TotalSongCountUpdated_ ( ) ;
}
2020-09-10 22:05:12 +02:00
void CollectionView : : TotalArtistCountUpdated ( const int count ) {
2018-02-27 18:06:05 +01:00
2019-04-08 18:46:11 +02:00
int old = total_artist_count_ ;
2018-02-27 18:06:05 +01:00
total_artist_count_ = count ;
if ( old ! = total_artist_count_ ) update ( ) ;
2021-08-23 21:21:08 +02:00
if ( total_artist_count_ = = 0 ) {
2018-02-27 18:06:05 +01:00
setCursor ( Qt : : PointingHandCursor ) ;
2021-08-23 21:21:08 +02:00
}
else {
2018-02-27 18:06:05 +01:00
unsetCursor ( ) ;
2021-08-23 21:21:08 +02:00
}
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
emit TotalArtistCountUpdated_ ( ) ;
}
2020-09-10 22:05:12 +02:00
void CollectionView : : TotalAlbumCountUpdated ( const int count ) {
2018-02-27 18:06:05 +01:00
2019-04-08 18:46:11 +02:00
int old = total_album_count_ ;
2018-02-27 18:06:05 +01:00
total_album_count_ = count ;
if ( old ! = total_album_count_ ) update ( ) ;
2021-08-23 21:21:08 +02:00
if ( total_album_count_ = = 0 ) {
2018-02-27 18:06:05 +01:00
setCursor ( Qt : : PointingHandCursor ) ;
2021-08-23 21:21:08 +02:00
}
else {
2018-02-27 18:06:05 +01:00
unsetCursor ( ) ;
2021-08-23 21:21:08 +02:00
}
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
emit TotalAlbumCountUpdated_ ( ) ;
}
void CollectionView : : 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 ( " Your 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 add some music " ) ) ;
}
else {
QTreeView : : paintEvent ( event ) ;
}
}
void CollectionView : : mouseReleaseEvent ( QMouseEvent * e ) {
2018-08-29 21:42:24 +02:00
2018-02-27 18:06:05 +01:00
QTreeView : : mouseReleaseEvent ( e ) ;
if ( total_song_count_ = = 0 ) {
emit ShowConfigDialog ( ) ;
}
}
void CollectionView : : contextMenuEvent ( QContextMenuEvent * e ) {
if ( ! context_menu_ ) {
context_menu_ = new QMenu ( this ) ;
2021-01-26 16:48:04 +01:00
action_add_to_playlist_ = context_menu_ - > addAction ( IconLoader : : Load ( " media-playback-start " ) , tr ( " Append to current playlist " ) , this , & CollectionView : : AddToPlaylist ) ;
action_load_ = context_menu_ - > addAction ( IconLoader : : Load ( " media-playback-start " ) , tr ( " Replace current playlist " ) , this , & CollectionView : : Load ) ;
action_open_in_new_playlist_ = context_menu_ - > addAction ( IconLoader : : Load ( " document-new " ) , tr ( " Open in new playlist " ) , this , & CollectionView : : OpenInNewPlaylist ) ;
2018-02-27 18:06:05 +01:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
action_add_to_playlist_enqueue_ = context_menu_ - > addAction ( IconLoader : : Load ( " go-next " ) , tr ( " Queue track " ) , this , & CollectionView : : AddToPlaylistEnqueue ) ;
action_add_to_playlist_enqueue_next_ = context_menu_ - > addAction ( IconLoader : : Load ( " go-next " ) , tr ( " Queue to play next " ) , this , & CollectionView : : AddToPlaylistEnqueueNext ) ;
2018-02-27 18:06:05 +01:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
action_organize_ = context_menu_ - > addAction ( IconLoader : : Load ( " edit-copy " ) , tr ( " Organize files... " ) , this , & CollectionView : : Organize ) ;
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2021-01-26 16:48:04 +01:00
action_copy_to_device_ = context_menu_ - > addAction ( IconLoader : : Load ( " device " ) , tr ( " Copy to device... " ) , this , & CollectionView : : CopyToDevice ) ;
2018-09-20 17:36:23 +02:00
# endif
2021-01-26 16:48:04 +01:00
action_delete_files_ = context_menu_ - > addAction ( IconLoader : : Load ( " edit-delete " ) , tr ( " Delete from disk... " ) , this , & CollectionView : : Delete ) ;
2018-02-27 18:06:05 +01:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
action_edit_track_ = context_menu_ - > addAction ( IconLoader : : Load ( " edit-rename " ) , tr ( " Edit track information... " ) , this , & CollectionView : : EditTracks ) ;
action_edit_tracks_ = context_menu_ - > addAction ( IconLoader : : Load ( " edit-rename " ) , tr ( " Edit tracks information... " ) , this , & CollectionView : : EditTracks ) ;
action_show_in_browser_ = context_menu_ - > addAction ( IconLoader : : Load ( " document-open-folder " ) , tr ( " Show in file browser... " ) , this , & CollectionView : : ShowInBrowser ) ;
2018-02-27 18:06:05 +01:00
2019-06-30 21:06:07 +02:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
action_rescan_songs_ = context_menu_ - > addAction ( tr ( " Rescan song(s) " ) , this , & CollectionView : : RescanSongs ) ;
2019-06-30 21:06:07 +02:00
2018-02-27 18:06:05 +01:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
action_show_in_various_ = context_menu_ - > addAction ( tr ( " Show in various artists " ) , this , & CollectionView : : ShowInVarious ) ;
action_no_show_in_various_ = context_menu_ - > addAction ( tr ( " Don't show in various artists " ) , this , & CollectionView : : NoShowInVarious ) ;
2018-02-27 18:06:05 +01:00
context_menu_ - > addSeparator ( ) ;
context_menu_ - > addMenu ( filter_ - > menu ( ) ) ;
2019-01-06 16:48:23 +01:00
# ifndef Q_OS_WIN
2020-08-19 22:02:35 +02:00
action_copy_to_device_ - > setDisabled ( app_ - > device_manager ( ) - > connected_devices_model ( ) - > rowCount ( ) = = 0 ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( app_ - > device_manager ( ) - > connected_devices_model ( ) , & DeviceStateFilterModel : : IsEmptyChanged , action_copy_to_device_ , & QAction : : setDisabled ) ;
2018-02-27 18:06:05 +01:00
# endif
}
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 ( ) ;
int regular_elements = 0 ;
int regular_editable = 0 ;
2021-01-26 16:48:04 +01:00
for ( const QModelIndex & idx : selected_indexes ) {
2020-08-12 21:34:42 +02:00
+ + regular_elements ;
2021-01-26 16:48:04 +01:00
if ( app_ - > collection_model ( ) - > data ( idx , CollectionModel : : Role_Editable ) . toBool ( ) ) {
2020-08-12 21:34:42 +02:00
+ + regular_editable ;
2018-02-27 18:06:05 +01:00
}
}
const int songs_selected = regular_elements ;
const bool regular_elements_only = songs_selected = = regular_elements & & regular_elements > 0 ;
// in all modes
2020-08-19 22:02:35 +02:00
action_load_ - > setEnabled ( songs_selected > 0 ) ;
action_add_to_playlist_ - > setEnabled ( songs_selected > 0 ) ;
action_open_in_new_playlist_ - > setEnabled ( songs_selected > 0 ) ;
action_add_to_playlist_enqueue_ - > setEnabled ( songs_selected > 0 ) ;
2018-02-27 18:06:05 +01:00
// if neither edit_track not edit_tracks are available, we show disabled edit_track element
2020-08-19 22:02:35 +02:00
action_edit_track_ - > setVisible ( regular_editable = = 1 ) ;
action_edit_track_ - > setEnabled ( regular_editable = = 1 ) ;
action_edit_tracks_ - > setVisible ( regular_editable > 1 ) ;
action_edit_tracks_ - > setEnabled ( regular_editable > 1 ) ;
2018-02-27 18:06:05 +01:00
2020-08-19 22:02:35 +02:00
action_rescan_songs_ - > setVisible ( regular_editable > 0 ) ;
action_rescan_songs_ - > setEnabled ( regular_editable > 0 ) ;
2019-06-30 21:06:07 +02:00
2020-08-19 22:02:35 +02:00
action_organize_ - > setVisible ( regular_elements = = regular_editable ) ;
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2020-08-19 22:02:35 +02:00
action_copy_to_device_ - > setVisible ( regular_elements = = regular_editable ) ;
# endif
# if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2022-02-05 15:56:01 +01:00
action_delete_files_ - > setVisible ( delete_files_ ) ;
2020-08-19 22:02:35 +02:00
# else
action_delete_files_ - > setVisible ( false ) ;
2018-09-20 17:36:23 +02:00
# endif
2020-08-19 22:02:35 +02:00
action_show_in_various_ - > setVisible ( regular_elements_only ) ;
action_no_show_in_various_ - > setVisible ( regular_elements_only ) ;
2018-02-27 18:06:05 +01:00
// only when all selected items are editable
2020-08-19 22:02:35 +02:00
action_organize_ - > setEnabled ( regular_elements = = regular_editable ) ;
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2020-08-19 22:02:35 +02:00
action_copy_to_device_ - > setEnabled ( regular_elements = = regular_editable ) ;
# endif
# if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2022-02-05 15:56:01 +01:00
action_delete_files_ - > setEnabled ( delete_files_ ) ;
2020-08-19 22:02:35 +02:00
# else
action_delete_files_ - > setEnabled ( false ) ;
2018-09-20 17:36:23 +02:00
# endif
2018-02-27 18:06:05 +01:00
context_menu_ - > popup ( e - > globalPos ( ) ) ;
}
2021-01-26 16:48:04 +01:00
void CollectionView : : ShowInVarious ( ) { SetShowInVarious ( true ) ; }
2018-02-27 18:06:05 +01:00
2021-01-26 16:48:04 +01:00
void CollectionView : : NoShowInVarious ( ) { SetShowInVarious ( false ) ; }
2018-02-27 18:06:05 +01:00
2021-01-26 16:48:04 +01:00
void CollectionView : : SetShowInVarious ( const bool on ) {
2018-02-27 18:06:05 +01:00
if ( ! context_menu_index_ . isValid ( ) ) return ;
2018-05-01 00:41:33 +02:00
// Map is from album name -> all artists sharing that album name, built from each selected song.
// We put through "Various Artists" changes one album at a time,
// to make sure the old album node gets removed (due to all children removed), before the new one gets added
2018-02-27 18:06:05 +01:00
QMultiMap < QString , QString > albums ;
2020-09-10 22:05:12 +02:00
for ( const Song & song : GetSelectedSongs ( ) ) {
2018-02-27 18:06:05 +01:00
if ( albums . find ( song . album ( ) , song . artist ( ) ) = = albums . end ( ) )
albums . insert ( song . album ( ) , song . artist ( ) ) ;
}
// If we have only one album and we are putting it into Various Artists, check to see
// if there are other Artists in this album and prompt the user if they'd like them moved, too
if ( on & & albums . keys ( ) . count ( ) = = 1 ) {
2021-03-21 04:47:11 +01:00
const QStringList albums_list = albums . keys ( ) ;
const QString album = albums_list . first ( ) ;
2021-09-19 15:41:36 +02:00
SongList all_of_album = app_ - > collection_backend ( ) - > GetSongsByAlbum ( album ) ;
2018-02-27 18:06:05 +01:00
QSet < QString > other_artists ;
for ( const Song & s : all_of_album ) {
2018-08-29 21:42:24 +02:00
if ( ! albums . contains ( album , s . artist ( ) ) & & ! other_artists . contains ( s . artist ( ) ) ) {
2018-02-27 18:06:05 +01:00
other_artists . insert ( s . artist ( ) ) ;
}
}
if ( other_artists . count ( ) > 0 ) {
2021-02-26 21:03:51 +01:00
if ( QMessageBox : : question ( this , tr ( " There are other songs in this album " ) , tr ( " Would you like to move the other songs on this album to Various Artists as well? " ) , QMessageBox : : Yes | QMessageBox : : No , QMessageBox : : Yes ) = = QMessageBox : : Yes ) {
2018-02-27 18:06:05 +01:00
for ( const QString & s : other_artists ) {
albums . insert ( album , s ) ;
}
}
}
}
2019-12-21 18:22:18 +01:00
# if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
2021-03-21 04:47:11 +01:00
QSet < QString > albums_set = QSet < QString > ( albums . keyBegin ( ) , albums . keyEnd ( ) ) ;
2019-12-21 18:22:18 +01:00
# else
2021-03-21 04:47:11 +01:00
QSet < QString > albums_set = QSet < QString > : : fromList ( albums . keys ( ) ) ;
2019-12-21 18:22:18 +01:00
# endif
2021-03-21 04:47:11 +01:00
for ( const QString & album : albums_set ) {
2018-02-27 18:06:05 +01:00
app_ - > collection_backend ( ) - > ForceCompilation ( album , albums . values ( album ) , on ) ;
}
}
void CollectionView : : 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 ;
2018-02-27 18:06:05 +01:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-02-27 18:06:05 +01:00
}
void CollectionView : : AddToPlaylist ( ) {
emit AddToPlaylistSignal ( model ( ) - > mimeData ( selectedIndexes ( ) ) ) ;
}
void CollectionView : : AddToPlaylistEnqueue ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
2021-06-12 20:53:23 +02:00
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
2020-04-23 21:08:28 +02:00
mimedata - > enqueue_now_ = true ;
2018-02-27 18:06:05 +01:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-02-27 18:06:05 +01:00
}
2018-11-18 23:21:12 +01:00
void CollectionView : : 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 ;
2018-11-18 23:21:12 +01:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-11-18 23:21:12 +01:00
}
2018-02-27 18:06:05 +01:00
void CollectionView : : OpenInNewPlaylist ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
2021-06-12 20:53:23 +02:00
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
2020-04-23 21:08:28 +02:00
mimedata - > open_in_new_playlist_ = true ;
2018-02-27 18:06:05 +01:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-02-27 18:06:05 +01:00
}
void CollectionView : : 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 CollectionView : : scrollTo ( const QModelIndex & idx , ScrollHint hint ) {
2018-02-27 18:06:05 +01: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
}
2018-02-27 18:06:05 +01:00
}
SongList CollectionView : : GetSelectedSongs ( ) const {
QModelIndexList selected_indexes = qobject_cast < QSortFilterProxyModel * > ( model ( ) ) - > mapSelectionToSource ( selectionModel ( ) - > selection ( ) ) . indexes ( ) ;
return app_ - > collection_model ( ) - > GetChildSongs ( selected_indexes ) ;
}
2020-08-04 21:18:14 +02:00
void CollectionView : : Organize ( ) {
2018-02-27 18:06:05 +01:00
2021-06-20 19:04:08 +02:00
if ( ! organize_dialog_ ) {
2023-07-21 05:55:24 +02:00
organize_dialog_ = make_unique < OrganizeDialog > ( app_ - > task_manager ( ) , app_ - > collection_backend ( ) , this ) ;
2021-06-20 19:04:08 +02:00
}
2018-02-27 18:06:05 +01:00
2020-08-04 21:18:14 +02:00
organize_dialog_ - > SetDestinationModel ( app_ - > collection_model ( ) - > directory_model ( ) ) ;
organize_dialog_ - > SetCopy ( false ) ;
2021-06-20 19:04:08 +02:00
const SongList songs = GetSelectedSongs ( ) ;
if ( organize_dialog_ - > SetSongs ( songs ) ) {
2020-08-04 21:18:14 +02:00
organize_dialog_ - > show ( ) ;
2021-06-20 19:04:08 +02:00
}
2018-02-27 18:06:05 +01:00
else {
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " None of the selected songs were suitable for copying to a device " ) ) ;
}
2019-02-20 21:27:53 +01:00
2018-02-27 18:06:05 +01:00
}
void CollectionView : : EditTracks ( ) {
if ( ! edit_tag_dialog_ ) {
2023-07-21 05:55:24 +02:00
edit_tag_dialog_ = make_unique < EditTagDialog > ( app_ , this ) ;
QObject : : connect ( & * edit_tag_dialog_ , & EditTagDialog : : Error , this , & CollectionView : : EditTagError ) ;
2018-02-27 18:06:05 +01:00
}
2021-06-20 19:04:08 +02:00
const SongList songs = GetSelectedSongs ( ) ;
edit_tag_dialog_ - > SetSongs ( songs ) ;
2018-02-27 18:06:05 +01:00
edit_tag_dialog_ - > show ( ) ;
}
2019-02-20 21:27:53 +01:00
void CollectionView : : EditTagError ( const QString & message ) {
emit Error ( message ) ;
}
2019-06-30 21:06:07 +02:00
void CollectionView : : RescanSongs ( ) {
app_ - > collection ( ) - > Rescan ( GetSelectedSongs ( ) ) ;
}
2018-02-27 18:06:05 +01:00
void CollectionView : : CopyToDevice ( ) {
2020-04-07 01:26:17 +02:00
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2021-06-21 15:40:25 +02:00
if ( ! organize_dialog_ ) {
2023-07-21 05:55:24 +02:00
organize_dialog_ = make_unique < OrganizeDialog > ( app_ - > task_manager ( ) , nullptr , this ) ;
2021-06-21 15:40:25 +02:00
}
2018-02-27 18:06:05 +01:00
2020-08-04 21:18:14 +02:00
organize_dialog_ - > SetDestinationModel ( app_ - > device_manager ( ) - > connected_devices_model ( ) , true ) ;
organize_dialog_ - > SetCopy ( true ) ;
organize_dialog_ - > SetSongs ( GetSelectedSongs ( ) ) ;
organize_dialog_ - > show ( ) ;
2018-09-20 17:36:23 +02:00
# endif
2020-04-07 01:26:17 +02:00
2018-02-27 18:06:05 +01:00
}
void CollectionView : : 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
void CollectionView : : ShowInBrowser ( ) const {
2019-06-20 17:00:10 +02:00
SongList songs = GetSelectedSongs ( ) ;
2018-02-27 18:06:05 +01:00
QList < QUrl > urls ;
2021-06-20 19:04:08 +02:00
urls . reserve ( songs . count ( ) ) ;
2019-06-20 17:00:10 +02:00
for ( const Song & song : songs ) {
2018-02-27 18:06:05 +01:00
urls < < song . url ( ) ;
}
Utilities : : OpenInFileBrowser ( urls ) ;
2019-06-20 17:00:10 +02:00
2018-02-27 18:06:05 +01:00
}
2021-06-22 13:45:29 +02:00
int CollectionView : : TotalSongs ( ) const {
2018-02-27 18:06:05 +01:00
return total_song_count_ ;
}
2021-06-22 13:45:29 +02:00
int CollectionView : : TotalArtists ( ) const {
2018-02-27 18:06:05 +01:00
return total_artist_count_ ;
}
2021-06-22 13:45:29 +02:00
int CollectionView : : TotalAlbums ( ) const {
2018-02-27 18:06:05 +01:00
return total_album_count_ ;
}
2020-08-19 22:02:35 +02:00
void CollectionView : : Delete ( ) {
if ( ! delete_files_ ) return ;
SongList selected_songs = GetSelectedSongs ( ) ;
2022-02-05 15:56:01 +01:00
SongList songs ;
2020-08-19 22:02:35 +02:00
QStringList files ;
2022-02-05 15:56:01 +01:00
songs . reserve ( selected_songs . count ( ) ) ;
2021-06-20 19:04:08 +02:00
files . reserve ( selected_songs . count ( ) ) ;
2020-08-19 22:02:35 +02:00
for ( const Song & song : selected_songs ) {
2022-02-05 15:56:01 +01:00
QString filename = song . url ( ) . toLocalFile ( ) ;
if ( ! files . contains ( filename ) ) {
songs < < song ;
files < < filename ;
}
2020-08-19 22:02:35 +02:00
}
if ( DeleteConfirmationDialog : : warning ( files ) ! = QDialogButtonBox : : Yes ) return ;
// We can cheat and always take the storage of the first directory, since they'll all be FilesystemMusicStorage in a collection and deleting doesn't check the actual directory.
2023-07-21 05:55:24 +02:00
SharedPtr < MusicStorage > storage = app_ - > collection_model ( ) - > directory_model ( ) - > index ( 0 , 0 ) . data ( MusicStorage : : Role_Storage ) . value < SharedPtr < MusicStorage > > ( ) ;
2020-08-19 22:02:35 +02:00
DeleteFiles * delete_files = new DeleteFiles ( app_ - > task_manager ( ) , storage , true ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( delete_files , & DeleteFiles : : Finished , this , & CollectionView : : DeleteFilesFinished ) ;
2022-02-05 15:56:01 +01:00
delete_files - > Start ( songs ) ;
2020-08-19 22:02:35 +02:00
}
void CollectionView : : DeleteFilesFinished ( const SongList & songs_with_errors ) {
if ( songs_with_errors . isEmpty ( ) ) return ;
OrganizeErrorDialog * dialog = new OrganizeErrorDialog ( this ) ;
2023-02-18 14:09:27 +01:00
dialog - > Show ( OrganizeErrorDialog : : OperationType : : Delete , songs_with_errors ) ;
2020-08-19 22:02:35 +02:00
// It deletes itself when the user closes it
}