Changeset View
Changeset View
Standalone View
Standalone View
src/browsers/CollectionTreeItemModelBase.cpp
Show All 29 Lines | |||||
30 | #include "core/meta/TrackEditor.h" | 30 | #include "core/meta/TrackEditor.h" | ||
31 | #include "core/meta/support/MetaConstants.h" | 31 | #include "core/meta/support/MetaConstants.h" | ||
32 | #include "core/support/Amarok.h" | 32 | #include "core/support/Amarok.h" | ||
33 | #include "core/support/Debug.h" | 33 | #include "core/support/Debug.h" | ||
34 | #include "core-impl/collections/support/TextualQueryFilter.h" | 34 | #include "core-impl/collections/support/TextualQueryFilter.h" | ||
35 | #include "widgets/PrettyTreeRoles.h" | 35 | #include "widgets/PrettyTreeRoles.h" | ||
36 | 36 | | |||
37 | #include <KLocalizedString> | 37 | #include <KLocalizedString> | ||
38 | #include <ThreadWeaver/Lambda> | ||||
39 | #include <ThreadWeaver/Queue> | ||||
38 | 40 | | |||
39 | #include <QApplication> | 41 | #include <QApplication> | ||
40 | #include <QIcon> | 42 | #include <QIcon> | ||
43 | #include <QMutex> | ||||
41 | #include <QPixmap> | 44 | #include <QPixmap> | ||
42 | #include <QStandardPaths> | 45 | #include <QStandardPaths> | ||
43 | #include <QStyle> | 46 | #include <QStyle> | ||
44 | #include <QTimeLine> | 47 | #include <QTimeLine> | ||
45 | #include <QTimer> | 48 | #include <QTimer> | ||
46 | 49 | | |||
47 | using namespace Meta; | 50 | using namespace Meta; | ||
48 | 51 | | |||
49 | 52 | | |||
53 | class TrackLoaderJob : public ThreadWeaver::Job | ||||
54 | { | ||||
55 | public: | ||||
56 | TrackLoaderJob( const QModelIndex &index, const Meta::AlbumPtr &album, CollectionTreeItemModelBase *model ) | ||||
57 | : m_index( index ) | ||||
58 | , m_album( album ) | ||||
59 | , m_model( model ) | ||||
60 | { | ||||
61 | } | ||||
62 | | ||||
63 | protected: | ||||
64 | void run( ThreadWeaver::JobPointer self, ThreadWeaver::Thread *thread ) override | ||||
65 | { | ||||
66 | Q_UNUSED( self ) | ||||
67 | Q_UNUSED( thread ) | ||||
68 | | ||||
69 | m_model->tracksLoaded( m_album, m_index, m_album->tracks() ); | ||||
70 | } | ||||
71 | | ||||
72 | private: | ||||
73 | QModelIndex m_index; | ||||
74 | Meta::AlbumPtr m_album; | ||||
75 | CollectionTreeItemModelBase *m_model; | ||||
76 | }; | ||||
77 | | ||||
50 | inline uint qHash( const Meta::DataPtr &data ) | 78 | inline uint qHash( const Meta::DataPtr &data ) | ||
51 | { | 79 | { | ||
52 | return qHash( data.data() ); | 80 | return qHash( data.data() ); | ||
53 | } | 81 | } | ||
54 | 82 | | |||
55 | /** | 83 | /** | ||
56 | * This set determines which collection browser levels should have shown Various Artists | 84 | * This set determines which collection browser levels should have shown Various Artists | ||
57 | * item under them. AlbumArtist is certain, (Track)Artist is questionable. | 85 | * item under them. AlbumArtist is certain, (Track)Artist is questionable. | ||
58 | */ | 86 | */ | ||
59 | static const QSet<CategoryId::CatMenuId> variousArtistCategories = | 87 | static const QSet<CategoryId::CatMenuId> variousArtistCategories = | ||
60 | QSet<CategoryId::CatMenuId>() << CategoryId::AlbumArtist; | 88 | QSet<CategoryId::CatMenuId>() << CategoryId::AlbumArtist; | ||
61 | 89 | | |||
62 | CollectionTreeItemModelBase::CollectionTreeItemModelBase( ) | 90 | CollectionTreeItemModelBase::CollectionTreeItemModelBase( ) | ||
63 | : QAbstractItemModel() | 91 | : QAbstractItemModel() | ||
92 | , m_loadingAlbumsMutex( new QMutex ) | ||||
64 | , m_rootItem( 0 ) | 93 | , m_rootItem( 0 ) | ||
65 | , m_animFrame( 0 ) | 94 | , m_animFrame( 0 ) | ||
66 | , m_loading1( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/loading1.png" ) ) ) | 95 | , m_loading1( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/loading1.png" ) ) ) | ||
67 | , m_loading2( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/loading2.png" ) ) ) | 96 | , m_loading2( QPixmap( QStandardPaths::locate( QStandardPaths::GenericDataLocation, "amarok/images/loading2.png" ) ) ) | ||
68 | , m_currentAnimPixmap( m_loading1 ) | 97 | , m_currentAnimPixmap( m_loading1 ) | ||
69 | , m_autoExpand( false ) | 98 | , m_autoExpand( false ) | ||
70 | { | 99 | { | ||
71 | m_timeLine = new QTimeLine( 10000, this ); | 100 | m_timeLine = new QTimeLine( 10000, this ); | ||
72 | m_timeLine->setFrameRange( 0, 20 ); | 101 | m_timeLine->setFrameRange( 0, 20 ); | ||
73 | m_timeLine->setLoopCount ( 0 ); | 102 | m_timeLine->setLoopCount ( 0 ); | ||
74 | connect( m_timeLine, &QTimeLine::frameChanged, this, &CollectionTreeItemModelBase::loadingAnimationTick ); | 103 | connect( m_timeLine, &QTimeLine::frameChanged, this, &CollectionTreeItemModelBase::loadingAnimationTick ); | ||
75 | } | 104 | } | ||
76 | 105 | | |||
77 | CollectionTreeItemModelBase::~CollectionTreeItemModelBase() | 106 | CollectionTreeItemModelBase::~CollectionTreeItemModelBase() | ||
78 | { | 107 | { | ||
79 | KConfigGroup config = Amarok::config( "Collection Browser" ); | 108 | KConfigGroup config = Amarok::config( "Collection Browser" ); | ||
80 | QList<int> levelNumbers; | 109 | QList<int> levelNumbers; | ||
81 | foreach( CategoryId::CatMenuId category, levels() ) | 110 | foreach( CategoryId::CatMenuId category, levels() ) | ||
82 | levelNumbers.append( category ); | 111 | levelNumbers.append( category ); | ||
83 | config.writeEntry( "TreeCategory", levelNumbers ); | 112 | config.writeEntry( "TreeCategory", levelNumbers ); | ||
84 | 113 | | |||
85 | if( m_rootItem ) | 114 | if( m_rootItem ) | ||
86 | m_rootItem->deleteLater(); | 115 | m_rootItem->deleteLater(); | ||
116 | | ||||
117 | delete m_loadingAlbumsMutex; | ||||
87 | } | 118 | } | ||
88 | 119 | | |||
89 | Qt::ItemFlags CollectionTreeItemModelBase::flags(const QModelIndex & index) const | 120 | Qt::ItemFlags CollectionTreeItemModelBase::flags(const QModelIndex & index) const | ||
90 | { | 121 | { | ||
91 | Qt::ItemFlags flags = 0; | 122 | Qt::ItemFlags flags = 0; | ||
92 | if( index.isValid() ) | 123 | if( index.isValid() ) | ||
93 | { | 124 | { | ||
94 | flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; | 125 | flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEditable; | ||
▲ Show 20 Lines • Show All 147 Lines • ▼ Show 20 Line(s) | 272 | { | |||
242 | Meta::AlbumPtr album = Meta::AlbumPtr::dynamicCast( item->data() ); | 273 | Meta::AlbumPtr album = Meta::AlbumPtr::dynamicCast( item->data() ); | ||
243 | switch( role ) | 274 | switch( role ) | ||
244 | { | 275 | { | ||
245 | case Qt::DisplayRole: | 276 | case Qt::DisplayRole: | ||
246 | case Qt::ToolTipRole: | 277 | case Qt::ToolTipRole: | ||
247 | { | 278 | { | ||
248 | QString name = album->prettyName(); | 279 | QString name = album->prettyName(); | ||
249 | // add years for named albums (if enabled) | 280 | // add years for named albums (if enabled) | ||
250 | if( AmarokConfig::showYears() && !album->name().isEmpty() ) | 281 | if( AmarokConfig::showYears() ) | ||
251 | { | 282 | { | ||
252 | Meta::TrackList tracks = album->tracks(); | 283 | if( m_years.contains( album.data() ) ) | ||
253 | if( !tracks.isEmpty() ) | | |||
254 | { | 284 | { | ||
255 | Meta::YearPtr year = tracks.first()->year(); | 285 | int year = m_years.value( album.data() ); | ||
256 | if( year && (year->year() != 0) ) | 286 | | ||
257 | name.prepend( QString("%1 - ").arg( year->name() ) ); | 287 | if( year > 0 ) | ||
288 | name.prepend( QString("%1 - ").arg( year ) ); | ||||
289 | } | ||||
290 | else if( !album->name().isEmpty() ) | ||||
291 | { | ||||
292 | QMutexLocker locker( m_loadingAlbumsMutex ); | ||||
293 | if( !m_loadingAlbums.contains( album ) ) | ||||
294 | { | ||||
295 | m_loadingAlbums.insert( album ); | ||||
296 | | ||||
297 | auto nonConstThis = const_cast<CollectionTreeItemModelBase*>( this ); | ||||
298 | auto job = QSharedPointer<TrackLoaderJob>::create( itemIndex( item ), album, nonConstThis ); | ||||
299 | ThreadWeaver::Queue::instance()->enqueue( job ); | ||||
300 | } | ||||
258 | } | 301 | } | ||
259 | } | 302 | } | ||
260 | return name; | 303 | return name; | ||
261 | } | 304 | } | ||
262 | 305 | | |||
263 | case Qt::DecorationRole: | 306 | case Qt::DecorationRole: | ||
264 | if( AmarokConfig::showAlbumArt() ) | 307 | if( AmarokConfig::showAlbumArt() ) | ||
265 | { | 308 | { | ||
266 | QStyle *style = QApplication::style(); | 309 | QStyle *style = QApplication::style(); | ||
267 | const int largeIconSize = style->pixelMetric( QStyle::PM_LargeIconSize ); | 310 | const int largeIconSize = style->pixelMetric( QStyle::PM_LargeIconSize ); | ||
268 | 311 | | |||
269 | return The::svgHandler()->imageWithBorder( album, largeIconSize, 2 ); | 312 | return The::svgHandler()->imageWithBorder( album, largeIconSize, 2 ); | ||
270 | } | 313 | } | ||
271 | else | 314 | else | ||
272 | return iconForLevel( level ); | 315 | return iconForLevel( level ); | ||
273 | 316 | | |||
274 | case PrettyTreeRoles::SortRole: | 317 | case PrettyTreeRoles::SortRole: | ||
275 | return album->sortableName(); | 318 | return album->sortableName(); | ||
276 | 319 | | |||
277 | case PrettyTreeRoles::HasCoverRole: | 320 | case PrettyTreeRoles::HasCoverRole: | ||
278 | return AmarokConfig::showAlbumArt(); | 321 | return AmarokConfig::showAlbumArt(); | ||
322 | | ||||
323 | case PrettyTreeRoles::YearRole: | ||||
324 | { | ||||
325 | if( m_years.contains( album.data() ) ) | ||||
326 | return m_years.value( album.data() ); | ||||
327 | | ||||
328 | else if( !album->name().isEmpty() ) | ||||
329 | { | ||||
330 | QMutexLocker locker( m_loadingAlbumsMutex ); | ||||
331 | if( !m_loadingAlbums.contains( album ) ) | ||||
332 | { | ||||
333 | m_loadingAlbums.insert( album ); | ||||
334 | | ||||
335 | auto nonConstThis = const_cast<CollectionTreeItemModelBase*>( this ); | ||||
336 | auto job = QSharedPointer<TrackLoaderJob>::create( itemIndex( item ), album, nonConstThis ); | ||||
337 | ThreadWeaver::Queue::instance()->enqueue( job ); | ||||
338 | } | ||||
339 | } | ||||
340 | return -1; | ||||
341 | } | ||||
279 | } | 342 | } | ||
280 | } | 343 | } | ||
281 | else if( item->isDataItem() ) | 344 | else if( item->isDataItem() ) | ||
282 | { | 345 | { | ||
283 | switch( role ) | 346 | switch( role ) | ||
284 | { | 347 | { | ||
285 | case Qt::DisplayRole: | 348 | case Qt::DisplayRole: | ||
286 | case Qt::ToolTipRole: | 349 | case Qt::ToolTipRole: | ||
▲ Show 20 Lines • Show All 211 Lines • ▼ Show 20 Line(s) | |||||
498 | CollectionTreeItemModelBase::itemIndex( CollectionTreeItem *item ) const | 561 | CollectionTreeItemModelBase::itemIndex( CollectionTreeItem *item ) const | ||
499 | { | 562 | { | ||
500 | if( !item || item == m_rootItem ) | 563 | if( !item || item == m_rootItem ) | ||
501 | return QModelIndex(); | 564 | return QModelIndex(); | ||
502 | 565 | | |||
503 | return createIndex( item->row(), 0, item ); | 566 | return createIndex( item->row(), 0, item ); | ||
504 | } | 567 | } | ||
505 | 568 | | |||
506 | void CollectionTreeItemModelBase::listForLevel(int level, Collections::QueryMaker * qm, CollectionTreeItem * parent) | 569 | void | ||
570 | CollectionTreeItemModelBase::listForLevel(int level, Collections::QueryMaker * qm, CollectionTreeItem * parent) | ||||
507 | { | 571 | { | ||
508 | if( qm && parent ) | 572 | if( qm && parent ) | ||
509 | { | 573 | { | ||
510 | // this check should not hurt anyone... needs to check if single... needs it | 574 | // this check should not hurt anyone... needs to check if single... needs it | ||
511 | if( m_runningQueries.contains( parent ) ) | 575 | if( m_runningQueries.contains( parent ) ) | ||
512 | return; | 576 | return; | ||
513 | 577 | | |||
514 | // following special cases are handled extra - right when the parent is added | 578 | // following special cases are handled extra - right when the parent is added | ||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Line(s) | 674 | default: | |||
611 | type = Collections::QueryMaker::None; | 675 | type = Collections::QueryMaker::None; | ||
612 | break; | 676 | break; | ||
613 | } | 677 | } | ||
614 | 678 | | |||
615 | return type; | 679 | return type; | ||
616 | } | 680 | } | ||
617 | 681 | | |||
618 | void | 682 | void | ||
683 | CollectionTreeItemModelBase::tracksLoaded( Meta::AlbumPtr album, const QModelIndex &index, const Meta::TrackList& tracks ) | ||||
684 | { | ||||
685 | DEBUG_BLOCK | ||||
686 | | ||||
687 | if( !album ) | ||||
688 | return; | ||||
689 | | ||||
690 | QMutexLocker locker( m_loadingAlbumsMutex ); | ||||
691 | m_loadingAlbums.remove( album ); | ||||
692 | | ||||
693 | if( !index.isValid() ) | ||||
694 | return; | ||||
695 | | ||||
696 | int year = 0; | ||||
697 | | ||||
698 | if( !tracks.isEmpty() ) | ||||
699 | { | ||||
700 | Meta::YearPtr yearPtr = tracks.first()->year(); | ||||
701 | if( yearPtr ) | ||||
702 | year = yearPtr->year(); | ||||
703 | | ||||
704 | debug() << "Valid album year found:" << year; | ||||
705 | } | ||||
706 | | ||||
707 | // Set the year in the thread associated with this | ||||
708 | auto lambda = [=] () { | ||||
709 | if( !m_years.contains( album.data() ) || m_years.value( album.data() ) != year ) | ||||
710 | { | ||||
711 | m_years[ album.data() ] = year; | ||||
712 | emit dataChanged( index, index ); | ||||
713 | } | ||||
714 | }; | ||||
715 | QTimer::singleShot( 0, this, lambda ); | ||||
716 | } | ||||
717 | | ||||
718 | void | ||||
619 | CollectionTreeItemModelBase::addQueryMaker( CollectionTreeItem* item, | 719 | CollectionTreeItemModelBase::addQueryMaker( CollectionTreeItem* item, | ||
620 | Collections::QueryMaker *qm ) const | 720 | Collections::QueryMaker *qm ) const | ||
621 | { | 721 | { | ||
622 | connect( qm, &Collections::QueryMaker::newTracksReady, this, &CollectionTreeItemModelBase::newTracksReady, Qt::QueuedConnection ); | 722 | connect( qm, &Collections::QueryMaker::newTracksReady, this, &CollectionTreeItemModelBase::newTracksReady, Qt::QueuedConnection ); | ||
623 | connect( qm, &Collections::QueryMaker::newArtistsReady, this, &CollectionTreeItemModelBase::newArtistsReady, Qt::QueuedConnection ); | 723 | connect( qm, &Collections::QueryMaker::newArtistsReady, this, &CollectionTreeItemModelBase::newArtistsReady, Qt::QueuedConnection ); | ||
624 | connect( qm, &Collections::QueryMaker::newAlbumsReady, this, &CollectionTreeItemModelBase::newAlbumsReady, Qt::QueuedConnection ); | 724 | connect( qm, &Collections::QueryMaker::newAlbumsReady, this, &CollectionTreeItemModelBase::newAlbumsReady, Qt::QueuedConnection ); | ||
625 | connect( qm, &Collections::QueryMaker::newGenresReady, this, &CollectionTreeItemModelBase::newGenresReady, Qt::QueuedConnection ); | 725 | connect( qm, &Collections::QueryMaker::newGenresReady, this, &CollectionTreeItemModelBase::newGenresReady, Qt::QueuedConnection ); | ||
626 | connect( qm, &Collections::QueryMaker::newComposersReady, this, &CollectionTreeItemModelBase::newComposersReady, Qt::QueuedConnection ); | 726 | connect( qm, &Collections::QueryMaker::newComposersReady, this, &CollectionTreeItemModelBase::newComposersReady, Qt::QueuedConnection ); | ||
Show All 36 Lines | 760 | { | |||
663 | m_timeLine->stop(); | 763 | m_timeLine->stop(); | ||
664 | } | 764 | } | ||
665 | qm->deleteLater(); | 765 | qm->deleteLater(); | ||
666 | } | 766 | } | ||
667 | 767 | | |||
668 | // TODO | 768 | // TODO | ||
669 | 769 | | |||
670 | /** Small helper function to convert a list of e.g. tracks to a list of DataPtr */ | 770 | /** Small helper function to convert a list of e.g. tracks to a list of DataPtr */ | ||
671 | template<class PointerType, class ListType> | 771 | template<class PointerType> | ||
672 | static Meta::DataList | 772 | static Meta::DataList | ||
673 | convertToDataList( const ListType& list ) | 773 | convertToDataList( const QList<PointerType>& list ) | ||
674 | { | 774 | { | ||
675 | Meta::DataList data; | 775 | Meta::DataList data; | ||
676 | foreach( PointerType p, list ) | 776 | for( const auto &p : list ) | ||
677 | data << Meta::DataPtr::staticCast( p ); | 777 | data << Meta::DataPtr::staticCast( p ); | ||
678 | 778 | | |||
679 | return data; | 779 | return data; | ||
680 | } | 780 | } | ||
681 | 781 | | |||
682 | void | 782 | void | ||
683 | CollectionTreeItemModelBase::newTracksReady( Meta::TrackList res ) | 783 | CollectionTreeItemModelBase::newTracksReady( Meta::TrackList res ) | ||
684 | { | 784 | { | ||
685 | newDataReady( convertToDataList<Meta::TrackPtr, Meta::TrackList>( res ) ); | 785 | newDataReady( convertToDataList( res ) ); | ||
686 | } | 786 | } | ||
687 | 787 | | |||
688 | void | 788 | void | ||
689 | CollectionTreeItemModelBase::newArtistsReady( Meta::ArtistList res ) | 789 | CollectionTreeItemModelBase::newArtistsReady( Meta::ArtistList res ) | ||
690 | { | 790 | { | ||
691 | newDataReady( convertToDataList<Meta::ArtistPtr, Meta::ArtistList>( res ) ); | 791 | newDataReady( convertToDataList( res ) ); | ||
692 | } | 792 | } | ||
693 | 793 | | |||
694 | void | 794 | void | ||
695 | CollectionTreeItemModelBase::newAlbumsReady( Meta::AlbumList res ) | 795 | CollectionTreeItemModelBase::newAlbumsReady( Meta::AlbumList res ) | ||
696 | { | 796 | { | ||
697 | newDataReady( convertToDataList<Meta::AlbumPtr, Meta::AlbumList>( res ) ); | 797 | newDataReady( convertToDataList( res ) ); | ||
698 | } | 798 | } | ||
699 | 799 | | |||
700 | void | 800 | void | ||
701 | CollectionTreeItemModelBase::newGenresReady( Meta::GenreList res ) | 801 | CollectionTreeItemModelBase::newGenresReady( Meta::GenreList res ) | ||
702 | { | 802 | { | ||
703 | newDataReady( convertToDataList<Meta::GenrePtr, Meta::GenreList>( res ) ); | 803 | newDataReady( convertToDataList( res ) ); | ||
704 | } | 804 | } | ||
705 | 805 | | |||
706 | void | 806 | void | ||
707 | CollectionTreeItemModelBase::newComposersReady( Meta::ComposerList res ) | 807 | CollectionTreeItemModelBase::newComposersReady( Meta::ComposerList res ) | ||
708 | { | 808 | { | ||
709 | newDataReady( convertToDataList<Meta::ComposerPtr, Meta::ComposerList>( res ) ); | 809 | newDataReady( convertToDataList( res ) ); | ||
710 | } | 810 | } | ||
711 | 811 | | |||
712 | void | 812 | void | ||
713 | CollectionTreeItemModelBase::newYearsReady( Meta::YearList res ) | 813 | CollectionTreeItemModelBase::newYearsReady( Meta::YearList res ) | ||
714 | { | 814 | { | ||
715 | newDataReady( convertToDataList<Meta::YearPtr, Meta::YearList>( res ) ); | 815 | newDataReady( convertToDataList( res ) ); | ||
716 | } | 816 | } | ||
717 | 817 | | |||
718 | void | 818 | void | ||
719 | CollectionTreeItemModelBase::newLabelsReady( Meta::LabelList res ) | 819 | CollectionTreeItemModelBase::newLabelsReady( Meta::LabelList res ) | ||
720 | { | 820 | { | ||
721 | newDataReady( convertToDataList<Meta::LabelPtr, Meta::LabelList>( res ) ); | 821 | newDataReady( convertToDataList( res ) ); | ||
722 | } | 822 | } | ||
723 | 823 | | |||
724 | void | 824 | void | ||
725 | CollectionTreeItemModelBase::newDataReady( Meta::DataList data ) | 825 | CollectionTreeItemModelBase::newDataReady( Meta::DataList data ) | ||
726 | { | 826 | { | ||
727 | //if we are expanding an item, we'll find the sender in childQueries | 827 | //if we are expanding an item, we'll find the sender in childQueries | ||
728 | //otherwise we are filtering all collections | 828 | //otherwise we are filtering all collections | ||
729 | Collections::QueryMaker *qm = qobject_cast<Collections::QueryMaker*>( sender() ); | 829 | Collections::QueryMaker *qm = qobject_cast<Collections::QueryMaker*>( sender() ); | ||
▲ Show 20 Lines • Show All 485 Lines • Show Last 20 Lines |