2018-08-29 21:42:24 +02:00
/*
* Strawberry Music Player
* This code was part of Clementine .
* Copyright 2010 , David Sansome < me @ davidsansome . com >
2020-04-06 02:47:57 +02:00
* Copyright 2013 - 2020 , Jonas Kvinge < jonas @ jkvinge . net >
2018-08-29 21:42:24 +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"
# include <qcoreevent.h>
# include <QtGlobal>
2020-02-08 03:40:30 +01:00
# include <QTreeView>
2018-08-29 21:42:24 +02:00
# include <QItemSelectionModel>
# include <QSortFilterProxyModel>
# include <QAbstractItemView>
# include <QStyleOptionViewItem>
2020-02-08 03:40:30 +01:00
# include <QAbstractScrollArea>
# include <QMimeData>
# include <QList>
2018-08-29 21:42:24 +02:00
# include <QVariant>
# include <QString>
# include <QUrl>
# include <QLocale>
# include <QMessageBox>
# include <QMenu>
2020-02-08 03:40:30 +01:00
# include <QAction>
2018-08-29 21:42:24 +02:00
# 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"
2018-09-20 17:36:23 +02:00
# include "collection/collectionmodel.h"
2018-08-29 21:42:24 +02:00
# include "collection/collectionitem.h"
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
# include "device / devicemanager.h"
# include "device / devicestatefiltermodel.h"
# endif
2018-08-29 21:42:24 +02:00
# include "dialogs/edittagdialog.h"
2020-08-04 21:18:14 +02:00
# include "organize/organizedialog.h"
2018-08-29 21:42:24 +02:00
# include "contextalbumsmodel.h"
# include "contextalbumsview.h"
ContextItemDelegate : : ContextItemDelegate ( QObject * parent ) : QStyledItemDelegate ( parent ) { }
2021-01-26 16:48:04 +01:00
bool ContextItemDelegate : : helpEvent ( QHelpEvent * event , QAbstractItemView * view , const QStyleOptionViewItem & option , const QModelIndex & idx ) {
2018-10-02 00:38:52 +02:00
2018-08-29 21:42:24 +02:00
return true ;
Q_UNUSED ( option ) ;
if ( ! event | | ! view ) return false ;
QHelpEvent * he = static_cast < QHelpEvent * > ( event ) ;
2021-01-26 16:48:04 +01:00
QString text = displayText ( idx . data ( ) , QLocale : : system ( ) ) ;
2018-08-29 21:42:24 +02:00
if ( text . isEmpty ( ) | | ! he ) return false ;
switch ( event - > type ( ) ) {
case QEvent : : ToolTip : {
2021-01-26 16:48:04 +01:00
QSize real_text = sizeHint ( option , idx ) ;
QRect displayed_text = view - > visualRect ( idx ) ;
2019-04-08 18:46:11 +02:00
bool is_elided = displayed_text . width ( ) < real_text . width ( ) ;
2018-08-29 21:42:24 +02:00
if ( is_elided ) {
QToolTip : : showText ( he - > globalPos ( ) , text , view ) ;
}
2021-01-26 16:48:04 +01:00
else if ( idx . data ( Qt : : ToolTipRole ) . isValid ( ) ) {
2018-08-29 21:42:24 +02:00
// If the item has a tooltip text, display it
2021-01-26 16:48:04 +01:00
QString tooltip_text = idx . data ( Qt : : ToolTipRole ) . toString ( ) ;
2018-08-29 21:42:24 +02:00
QToolTip : : showText ( he - > 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 ( he - > globalPos ( ) , text , view ) ;
return true ;
default :
break ;
}
return false ;
}
ContextAlbumsView : : ContextAlbumsView ( QWidget * parent )
: AutoExpandingTreeView ( parent ) ,
app_ ( nullptr ) ,
context_menu_ ( 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 ) ;
}
2020-06-15 21:55:05 +02:00
ContextAlbumsView : : ~ ContextAlbumsView ( ) = default ;
2018-08-29 21:42:24 +02:00
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 ) ;
SongList songs = model_ - > GetChildSongs ( index ) ;
for ( const Song & song : songs ) {
if ( song = = last_selected_song_ ) {
setCurrentIndex ( current ) ;
return true ;
}
}
}
break ;
}
}
return false ;
}
2019-11-26 19:41:32 +01:00
void ContextAlbumsView : : Init ( Application * app ) {
2018-08-29 21:42:24 +02:00
app_ = app ;
2018-10-02 00:38:52 +02:00
2018-08-29 21:42:24 +02:00
model_ = new ContextAlbumsModel ( app_ - > collection_backend ( ) , app_ , this ) ;
model_ - > Reset ( ) ;
setModel ( model_ ) ;
2021-01-26 16:48:04 +01:00
QObject : : connect ( model_ , & ContextAlbumsModel : : modelAboutToBeReset , this , & ContextAlbumsView : : SaveFocus ) ;
QObject : : connect ( model_ , & ContextAlbumsModel : : modelReset , this , & ContextAlbumsView : : RestoreFocus ) ;
2018-10-02 00:38:52 +02:00
2018-08-29 21:42:24 +02:00
}
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 ) ;
2021-01-26 16:48:04 +01:00
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 ) ;
2018-08-29 21:42:24 +02:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
add_to_playlist_enqueue_ = context_menu_ - > addAction ( IconLoader : : Load ( " go-next " ) , tr ( " Queue track " ) , this , & ContextAlbumsView : : AddToPlaylistEnqueue ) ;
2018-08-29 21:42:24 +02:00
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
organize_ = context_menu_ - > addAction ( IconLoader : : Load ( " edit-copy " ) , tr ( " Organize files... " ) , this , & ContextAlbumsView : : Organize ) ;
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2021-01-26 16:48:04 +01:00
copy_to_device_ = context_menu_ - > addAction ( IconLoader : : Load ( " device " ) , tr ( " Copy to device... " ) , this , & ContextAlbumsView : : CopyToDevice ) ;
2018-08-29 21:42:24 +02:00
# endif
context_menu_ - > addSeparator ( ) ;
2021-01-26 16:48:04 +01:00
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 ) ;
2018-08-29 21:42:24 +02:00
context_menu_ - > addSeparator ( ) ;
2019-01-06 16:48:23 +01:00
# ifndef Q_OS_WIN
2018-08-29 21:42:24 +02:00
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 , copy_to_device_ , & QAction : : setDisabled ) ;
2018-08-29 21:42:24 +02:00
# 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 ;
2021-01-26 16:48:04 +01:00
for ( const QModelIndex & idx : selected_indexes ) {
2018-08-29 21:42:24 +02:00
regular_elements + + ;
2021-01-26 16:48:04 +01:00
if ( model_ - > data ( idx , ContextAlbumsModel : : Role_Editable ) . toBool ( ) ) {
2018-08-29 21:42:24 +02:00
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 ) ;
add_to_playlist_ - > setEnabled ( songs_selected ) ;
open_in_new_playlist_ - > setEnabled ( songs_selected ) ;
add_to_playlist_enqueue_ - > setEnabled ( songs_selected ) ;
// 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 ) ;
2020-08-04 21:18:14 +02:00
organize_ - > setVisible ( regular_elements_only ) ;
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2018-08-29 21:42:24 +02:00
copy_to_device_ - > setVisible ( regular_elements_only ) ;
# endif
// only when all selected items are editable
2020-08-04 21:18:14 +02:00
organize_ - > setEnabled ( regular_elements = = regular_editable ) ;
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2018-08-29 21:42:24 +02:00
copy_to_device_ - > setEnabled ( regular_elements = = regular_editable ) ;
# endif
context_menu_ - > popup ( e - > globalPos ( ) ) ;
}
void ContextAlbumsView : : 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-08-29 21:42:24 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-08-29 21:42:24 +02:00
}
void ContextAlbumsView : : AddToPlaylist ( ) {
emit AddToPlaylistSignal ( model ( ) - > mimeData ( selectedIndexes ( ) ) ) ;
}
void ContextAlbumsView : : 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 ;
2018-08-29 21:42:24 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-08-29 21:42:24 +02:00
}
void ContextAlbumsView : : OpenInNewPlaylist ( ) {
2020-04-23 21:08:28 +02:00
QMimeData * q_mimedata = model ( ) - > mimeData ( selectedIndexes ( ) ) ;
if ( MimeData * mimedata = qobject_cast < MimeData * > ( q_mimedata ) ) {
mimedata - > open_in_new_playlist_ = true ;
2018-08-29 21:42:24 +02:00
}
2020-04-23 21:08:28 +02:00
emit AddToPlaylistSignal ( q_mimedata ) ;
2018-08-29 21:42:24 +02:00
}
2021-01-26 16:48:04 +01:00
void ContextAlbumsView : : scrollTo ( const QModelIndex & idx , ScrollHint hint ) {
2018-08-29 21:42:24 +02:00
if ( is_in_keyboard_search_ )
2021-01-26 16:48:04 +01:00
QTreeView : : scrollTo ( idx , QAbstractItemView : : PositionAtTop ) ;
2018-08-29 21:42:24 +02:00
else
2021-01-26 16:48:04 +01:00
QTreeView : : scrollTo ( idx , hint ) ;
2018-08-29 21:42:24 +02:00
}
SongList ContextAlbumsView : : GetSelectedSongs ( ) const {
QModelIndexList selected_indexes = selectionModel ( ) - > selectedRows ( ) ;
return model_ - > GetChildSongs ( selected_indexes ) ;
}
2020-08-04 21:18:14 +02:00
void ContextAlbumsView : : Organize ( ) {
2018-08-29 21:42:24 +02:00
2020-08-04 21:18:14 +02:00
if ( ! organize_dialog_ )
organize_dialog_ . reset ( new OrganizeDialog ( app_ - > task_manager ( ) , app_ - > collection_backend ( ) , this ) ) ;
2018-08-29 21:42:24 +02:00
2020-08-04 21:18:14 +02:00
organize_dialog_ - > SetDestinationModel ( app_ - > collection_model ( ) - > directory_model ( ) ) ;
organize_dialog_ - > SetCopy ( false ) ;
if ( organize_dialog_ - > SetSongs ( GetSelectedSongs ( ) ) )
organize_dialog_ - > show ( ) ;
2018-08-29 21:42:24 +02:00
else {
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " None of the selected songs were suitable for copying to a device " ) ) ;
}
2020-04-07 01:26:17 +02:00
2018-08-29 21:42:24 +02:00
}
void ContextAlbumsView : : EditTracks ( ) {
if ( ! edit_tag_dialog_ ) {
edit_tag_dialog_ . reset ( new EditTagDialog ( app_ , this ) ) ;
}
edit_tag_dialog_ - > SetSongs ( GetSelectedSongs ( ) ) ;
edit_tag_dialog_ - > show ( ) ;
}
void ContextAlbumsView : : CopyToDevice ( ) {
2018-09-20 17:36:23 +02:00
# ifndef Q_OS_WIN
2020-08-04 21:18:14 +02:00
if ( ! organize_dialog_ )
2020-10-27 17:50:16 +01:00
organize_dialog_ . reset ( new OrganizeDialog ( app_ - > task_manager ( ) , nullptr , this ) ) ;
2018-08-29 21:42:24 +02: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
2018-08-29 21:42:24 +02:00
}
void ContextAlbumsView : : ShowInBrowser ( ) {
QList < QUrl > urls ;
for ( const Song & song : GetSelectedSongs ( ) ) {
urls < < song . url ( ) ;
}
Utilities : : OpenInFileBrowser ( urls ) ;
2021-01-26 16:48:04 +01:00
2018-08-29 21:42:24 +02:00
}