Version: 2.2.2 beta1 (using KDE 4.3.4) OS: Linux Installed from: Debian testing/unstable Packages One of these "I miss this feature from version 1.4" request, so feel free to discard if it makes no sense to you. With a large number of podcasts, it would be great to be able to add groups (folders) to the podcast view in Amarok to increase usability in these cases.
*** Bug 231282 has been marked as a duplicate of this bug. ***
commit 8e234c3c7767544e2ac5b5bacf867d034c015fbc Author: Bart Cerneels <bart.cerneels@kde.org> Date: Tue Jul 20 23:27:03 2010 +0200 Make Podcast browser category use common classes. Now podcasts also get provider filtering and folders. BUG:219519 diff --git a/ChangeLog b/ChangeLog index 98bcd10..4af06e8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,7 @@ Amarok ChangeLog VERSION 2.3.2-Beta 1 FEATURES: + * Podcasts can now be filtered on provider and grouped in folders. (BR 219519) * Amarok 1.4 Database Importer: added support for importing labels. Thanks to MatÄj Laitl for the patch. (BR 218996) * Shortcuts: Added "Replay current track" shortcut. (BR 217081) diff --git a/src/browsers/playlistbrowser/PlaylistBrowserCategory.cpp b/src/browsers/playlistbrowser/PlaylistBrowserCategory.cpp index 905e71e..d5dc348 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserCategory.cpp +++ b/src/browsers/playlistbrowser/PlaylistBrowserCategory.cpp @@ -116,6 +116,11 @@ PlaylistBrowserCategory::PlaylistBrowserCategory( int playlistCategory, createProviderButton( provider ); } + connect( The::playlistManager(), SIGNAL(providerAdded( Playlists::PlaylistProvider*, int )), + SLOT(slotProviderAdded( Playlists::PlaylistProvider*, int )) ); + connect( The::playlistManager(), SIGNAL(providerRemoved( Playlists::PlaylistProvider*, int )), + SLOT(slotProviderRemoved( Playlists::PlaylistProvider*, int )) ); + connect( The::paletteHandler(), SIGNAL( newPalette( const QPalette & ) ), SLOT( newPalette( const QPalette & ) ) ); } diff --git a/src/browsers/playlistbrowser/PlaylistBrowserView.h b/src/browsers/playlistbrowser/PlaylistBrowserView.h index 160a8f3..da3ad90 100644 --- a/src/browsers/playlistbrowser/PlaylistBrowserView.h +++ b/src/browsers/playlistbrowser/PlaylistBrowserView.h @@ -32,6 +32,7 @@ namespace PlaylistBrowserNS { class PlaylistBrowserView : public Amarok::PrettyTreeView { +Q_OBJECT public: explicit PlaylistBrowserView( QAbstractItemModel *model, QWidget *parent = 0 ); ~PlaylistBrowserView(); diff --git a/src/browsers/playlistbrowser/PodcastCategory.cpp b/src/browsers/playlistbrowser/PodcastCategory.cpp index 1a740c8..f886311 100644 --- a/src/browsers/playlistbrowser/PodcastCategory.cpp +++ b/src/browsers/playlistbrowser/PodcastCategory.cpp @@ -70,14 +70,13 @@ namespace The using namespace PlaylistBrowserNS; QString PodcastCategory::s_configGroup( "Podcast View" ); -QString PodcastCategory::s_mergedViewKey( "Merged View" ); PodcastCategory* PodcastCategory::s_instance = 0; PodcastCategory* PodcastCategory::instance() { - return s_instance ? s_instance : new PodcastCategory( The::podcastModel() ); + return s_instance ? s_instance : new PodcastCategory( 0 ); } void @@ -90,9 +89,12 @@ PodcastCategory::destroy() } } -PodcastCategory::PodcastCategory( PodcastModel *podcastModel ) - : BrowserCategory( "podcasts", 0 ) - , m_podcastModel( podcastModel ) +PodcastCategory::PodcastCategory( QWidget *parent ) + : PlaylistBrowserCategory( Playlists::PodcastChannelPlaylist, + "podcasts", + s_configGroup, + The::podcastModel(), + parent ) { setPrettyName( i18n( "Podcasts" ) ); setShortDescription( i18n( "List of podcast subscriptions and episodes" ) ); @@ -104,107 +106,51 @@ PodcastCategory::PodcastCategory( PodcastModel *podcastModel ) setImagePath( KStandardDirs::locate( "data", "amarok/images/hover_info_podcasts.png" ) ); - resize(339, 574); - QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth( this->sizePolicy().hasHeightForWidth()); - setSizePolicy( sizePolicy ); - - setContentsMargins(0,0,0,0); - - KToolBar *toolBar = new KToolBar( this, false, false ); - toolBar->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); - - QAction* addPodcastAction = new QAction( KIcon( "list-add-amarok" ), i18n("&Add Podcast"), toolBar ); - toolBar->addAction( addPodcastAction ); - connect( addPodcastAction, SIGNAL(triggered( bool )), m_podcastModel, SLOT(addPodcast()) ); - - QAction* updateAllAction = new QAction( KIcon("view-refresh-amarok"), - i18n("&Update All"), toolBar ); - toolBar->addAction( updateAllAction ); - connect( updateAllAction, SIGNAL(triggered( bool )), - m_podcastModel, SLOT(refreshPodcasts()) ); - - //a QWidget with minimumExpanding makes the next button right aligned. - QWidget *spacerWidget = new QWidget( this ); - spacerWidget->setSizePolicy( QSizePolicy::MinimumExpanding, - QSizePolicy::MinimumExpanding ); - toolBar->addWidget( spacerWidget ); - - m_podcastTreeView = new PodcastView( podcastModel, this ); - m_defaultItemDelegate = m_podcastTreeView->itemDelegate(); - - m_byProviderProxy = new PlaylistsByProviderProxy( podcastModel, - PlaylistBrowserModel::ProviderColumn ); - m_byProviderDelegate = new PlaylistTreeItemDelegate( m_podcastTreeView ); - - m_podcastTreeView->setFrameShape( QFrame::NoFrame ); - m_podcastTreeView->setContentsMargins( 0, 0, 0, 0 ); - - KAction *toggleAction = new KAction( KIcon( "view-list-tree" ), QString(), toolBar ); - toggleAction->setToolTip( i18n( "Merged View" ) ); - toggleAction->setCheckable( true ); - toggleAction->setChecked( Amarok::config( s_configGroup ).readEntry( s_mergedViewKey, true ) ); - toolBar->addAction( toggleAction ); - connect( toggleAction, SIGNAL( triggered( bool ) ), SLOT( toggleView( bool ) ) ); - toggleView( toggleAction->isChecked() ); - - m_podcastTreeView->header()->hide(); - m_podcastTreeView->setIconSize( QSize( 32, 32 ) ); - - m_podcastTreeView->setAlternatingRowColors( true ); - m_podcastTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection ); - m_podcastTreeView->setSelectionBehavior( QAbstractItemView::SelectItems ); - m_podcastTreeView->setDragEnabled( true ); - m_podcastTreeView->setAcceptDrops( true ); - m_podcastTreeView->setDropIndicatorShown( true ); - - for( int column = 1; column < podcastModel->columnCount(); ++ column ) - { - m_podcastTreeView->hideColumn( column ); - } - - //transparency - QPalette p = m_podcastTreeView->palette(); - QColor c = p.color( QPalette::Base ); - c.setAlpha( 0 ); - p.setColor( QPalette::Base, c ); +// QAction* addPodcastAction = new QAction( KIcon( "list-add-amarok" ), i18n("&Add Podcast"), toolBar ); +// toolBar->addAction( addPodcastAction ); +// connect( addPodcastAction, SIGNAL(triggered( bool )), m_podcastModel, SLOT(addPodcast()) ); - c = p.color( QPalette::AlternateBase ); - c.setAlpha( 77 ); - p.setColor( QPalette::AlternateBase, c ); +// QAction* updateAllAction = new QAction( KIcon("view-refresh-amarok"), +// i18n("&Update All"), toolBar ); +// toolBar->addAction( updateAllAction ); +// connect( updateAllAction, SIGNAL(triggered( bool )), +// m_podcastModel, SLOT(refreshPodcasts()) ); - m_podcastTreeView->setPalette( p ); - QSizePolicy sizePolicy1(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); - sizePolicy1.setHorizontalStretch(0); - sizePolicy1.setVerticalStretch(0); - sizePolicy1.setHeightForWidth(m_podcastTreeView->sizePolicy().hasHeightForWidth()); - m_podcastTreeView->setSizePolicy(sizePolicy1); - - m_viewKicker = new ViewKicker( m_podcastTreeView ); - - connect( m_podcastTreeView, SIGNAL( clicked( const QModelIndex & ) ), this, - SLOT( showInfo( const QModelIndex & ) ) ); - - QAction *importOpmlAction = new QAction( KIcon("document-import") - , i18n( "Import OPML File" ) - , toolBar - ); - importOpmlAction->setToolTip( i18n( "Import OPML File" ) ); - toolBar->addAction( importOpmlAction ); - connect( importOpmlAction, SIGNAL( triggered() ), SLOT( slotImportOpml() ) ); + //transparency +// QPalette p = m_podcastTreeView->palette(); +// QColor c = p.color( QPalette::Base ); +// c.setAlpha( 0 ); +// p.setColor( QPalette::Base, c ); +// +// c = p.color( QPalette::AlternateBase ); +// c.setAlpha( 77 ); +// p.setColor( QPalette::AlternateBase, c ); +// +// m_podcastTreeView->setPalette( p ); +// +// QSizePolicy sizePolicy1(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); +// sizePolicy1.setHorizontalStretch(0); +// sizePolicy1.setVerticalStretch(0); +// sizePolicy1.setHeightForWidth(m_podcastTreeView->sizePolicy().hasHeightForWidth()); +// m_podcastTreeView->setSizePolicy(sizePolicy1); +// +// QAction *importOpmlAction = new QAction( KIcon("document-import") +// , i18n( "Import OPML File" ) +// , toolBar +// ); +// importOpmlAction->setToolTip( i18n( "Import OPML File" ) ); +// toolBar->addAction( importOpmlAction ); +// connect( importOpmlAction, SIGNAL( triggered() ), SLOT( slotImportOpml() ) ); } PodcastCategory::~PodcastCategory() { - delete m_viewKicker; } void -PodcastCategory::showInfo( const QModelIndex & index ) +PodcastCategory::showInfo( const QModelIndex &index ) { QVariantMap map; const int row = index.row(); @@ -343,7 +289,7 @@ PodcastCategory::slotImportOpml() if( !url.isEmpty() ) { // user entered something and pressed OK - m_podcastModel->importOpml( url ); + The::podcastModel()->importOpml( url ); } else { @@ -352,39 +298,7 @@ PodcastCategory::slotImportOpml() } } -void -PodcastCategory::toggleView( bool merged ) //SLOT -{ - if( merged ) - { - m_podcastTreeView->setModel( m_podcastModel ); - m_podcastTreeView->setItemDelegate( m_defaultItemDelegate ); - m_podcastTreeView->setRootIsDecorated( true ); - } - else - { - m_podcastTreeView->setModel( m_byProviderProxy ); - m_podcastTreeView->setItemDelegate( m_byProviderDelegate ); - m_podcastTreeView->setRootIsDecorated( false ); - } - - Amarok::config( s_configGroup ).writeEntry( s_mergedViewKey, merged ); -} - -ViewKicker::ViewKicker( QTreeView * treeView ) -{ - DEBUG_BLOCK - m_treeView = treeView; -} - -void -ViewKicker::kickView() -{ - DEBUG_BLOCK - m_treeView->setRootIndex( QModelIndex() ); -} - -PodcastCategoryDelegate::PodcastCategoryDelegate( QTreeView * view ) +PodcastCategoryDelegate::PodcastCategoryDelegate( QTreeView *view ) : QItemDelegate() , m_view( view ) { @@ -517,237 +431,4 @@ PodcastCategoryDelegate::sizeHint(const QStyleOptionViewItem & option, const QMo return QSize ( width, height ); } -PodcastView::PodcastView( PodcastModel *model, QWidget * parent ) - : Amarok::PrettyTreeView( parent ) - , m_podcastModel( model ) - , m_pd( 0 ) - , m_ongoingDrag( false ) - , m_dragMutex() - , m_expandToggledWhenPressed( false ) -{ -} - -PodcastView::~PodcastView() -{} - -void -PodcastView::mousePressEvent( QMouseEvent *event ) -{ - const QModelIndex index = indexAt( event->pos() ); - if( !index.isValid() ) - { - event->accept(); - return; - } - - const int actionCount = - index.data( PlaylistBrowserNS::PlaylistBrowserModel::ActionCountRole ).toInt(); - if( actionCount > 0 ) - { - const QRect rect = PlaylistTreeItemDelegate::actionsRect( index ); - if( rect.contains( event->pos() ) ) - return; - } - - bool prevExpandState = isExpanded( index ); - - // This will toggle the expansion of the current item when clicking - // on the fold marker but not on the item itself. Required here to - // enable dragging. - Amarok::PrettyTreeView::mousePressEvent( event ); - - m_expandToggledWhenPressed = ( prevExpandState != isExpanded(index) ); -} - -void -PodcastView::mouseReleaseEvent( QMouseEvent * event ) -{ - const QModelIndex index = indexAt( event->pos() ); - if( !index.parent().isValid() ) // not a root element, don't bother checking actions - { - const int actionCount = - index.data( PlaylistBrowserNS::PlaylistBrowserModel::ActionCountRole ).toInt(); - if( actionCount > 0 ) - { - const QRect rect = PlaylistTreeItemDelegate::actionsRect( index ); - if( rect.contains( event->pos() ) ) - { - QVariantList variantList = - index.data( PlaylistBrowserNS::PlaylistBrowserModel::ActionRole ).toList(); - if( variantList.isEmpty() ) - return; - - QList<QAction*> actions = variantList.first().value<QList<QAction*> >(); - //hack: rect height == the width of one action's area. - int indexOfActionToTrigger - = ( event->pos().x() - rect.left() ) / rect.height(); - debug() << "triggering action " << indexOfActionToTrigger; - if( indexOfActionToTrigger >= actions.count() ) - { - debug() << "no such action"; - return; - } - QAction *action = actions.value( indexOfActionToTrigger ); - if( action ) - action->trigger(); - return; - } - } - } - - if( m_pd ) - { - connect( m_pd, SIGNAL( fadeHideFinished() ), m_pd, SLOT( deleteLater() ) ); - m_pd->hide(); - m_pd = 0; - } - - if( !m_expandToggledWhenPressed && - event->button() != Amarok::contextMouseButton() && - event->modifiers() == Qt::NoModifier && - KGlobalSettings::singleClick() && - model()->hasChildren( index ) ) - { - m_expandToggledWhenPressed = !m_expandToggledWhenPressed; - setCurrentIndex( index ); - setExpanded( index, !isExpanded( index ) ); - event->accept(); - return; - } - Amarok::PrettyTreeView::mouseReleaseEvent( event ); -} - -void -PodcastView::mouseMoveEvent( QMouseEvent *event ) -{ - if( event->buttons() || event->modifiers() ) - { - Amarok::PrettyTreeView::mouseMoveEvent( event ); - return; - } - event->accept(); -} - -void -PodcastView::mouseDoubleClickEvent( QMouseEvent * event ) -{ - QModelIndex index = indexAt( event->pos() ); - if( !index.isValid() ) - { - event->accept(); - return; - } - - if( model()->hasChildren( index ) ) - { - if( event->button() != Amarok::contextMouseButton() && - event->modifiers() == Qt::NoModifier ) - { - setExpanded( index, !isExpanded( index ) ); - } - } - else - { - QList<QAction *> actions = - index.data( PlaylistBrowserNS::PlaylistBrowserModel::ActionRole ).value<QList<QAction *> >(); - if( actions.count() > 0 ) - { - //HACK execute the first action assuming it's load - actions.first()->trigger(); - actions.first()->setData( QVariant() ); - } - } - event->accept(); -} - -void -PodcastView::startDrag( Qt::DropActions supportedActions ) -{ - DEBUG_BLOCK - - // When a parent item is dragged, startDrag() is called a bunch of times. Here we prevent that: - m_dragMutex.lock(); - if( m_ongoingDrag ) - { - m_dragMutex.unlock(); - return; - } - m_ongoingDrag = true; - m_dragMutex.unlock(); - - if( !m_pd ) - m_pd = The::popupDropperFactory()->createPopupDropper( Context::ContextView::self() ); - - QList<QAction*> actions; - - if( m_pd && m_pd->isHidden() ) - { - actions = actionsFor( selectedIndexes() ); - - foreach( QAction *action, actions ) - m_pd->addItem( The::popupDropperFactory()->createItem( action ) ); - - m_pd->show(); - } - - QTreeView::startDrag( supportedActions ); - debug() << "After the drag!"; - - //We keep the items that the actions need to be applied to in the actions private data. - //Clear the data from all actions now that the context menu has executed. - foreach( QAction *action, actions ) - action->setData( QVariant() ); - - if( m_pd ) - { - debug() << "clearing PUD"; - connect( m_pd, SIGNAL( fadeHideFinished() ), m_pd, SLOT( clear() ) ); - m_pd->hide(); - } - m_dragMutex.lock(); - m_ongoingDrag = false; - m_dragMutex.unlock(); -} - -QList<QAction *> -PodcastView::actionsFor( QModelIndexList indexes ) -{ - QList<QAction *> actions; - foreach( QModelIndex idx, indexes ) - { - QList<QAction *> idxActions = - idx.data( PlaylistBrowserNS::PlaylistBrowserModel::ActionRole ).value<QList<QAction *> >(); - //only add unique actions model is responsible for making them unique - foreach( QAction *action, idxActions ) - { - if( !actions.contains( action ) ) - actions << action; - } - } - return actions; -} - -void -PodcastView::contextMenuEvent( QContextMenuEvent *event ) -{ - QList<QAction *> actions = actionsFor( selectedIndexes() ); - - if( actions.isEmpty() ) - return; - - KMenu menu; - foreach( QAction *action, actions ) - { - if( action ) - menu.addAction( action ); - } - - menu.exec( mapToGlobal( event->pos() ) ); - //We keep the items that the actions need to be applied to in the actions private data. - //Clear the data from all actions now that the PUD has executed. - foreach( QAction *action, actions ) - action->setData( QVariant() ); -} - #include "PodcastCategory.moc" - diff --git a/src/browsers/playlistbrowser/PodcastCategory.h b/src/browsers/playlistbrowser/PodcastCategory.h index d71dfb2..058b7ba 100644 --- a/src/browsers/playlistbrowser/PodcastCategory.h +++ b/src/browsers/playlistbrowser/PodcastCategory.h @@ -17,38 +17,22 @@ #ifndef PODCASTCATEGORY_H #define PODCASTCATEGORY_H -#include "browsers/BrowserCategory.h" -#include "playlist/PlaylistModel.h" -#include "PodcastModel.h" -#include "widgets/PrettyTreeView.h" +#include "PlaylistBrowserCategory.h" -#include <KGlobalSettings> +#include <KDialog> -#include <QContextMenuEvent> +#include <QModelIndex> +#include <QPoint> +#include <QSortFilterProxyModel> #include <QItemDelegate> -#include <QMutex> -#include <QListView> -#include <QTimer> -#include <QToolButton> #include <QWebPage> -class PopupDropper; -class QAction; - -class PlaylistsByProviderProxy; -class PlaylistTreeItemDelegate; - namespace PlaylistBrowserNS { -class PodcastModel; -class PodcastView; -class PodcastCategoryDelegate; -class ViewKicker; - /** @author Bart Cerneels <bart.cerneels@kde.org> */ -class PodcastCategory : public BrowserCategory +class PodcastCategory : public PlaylistBrowserCategory { Q_OBJECT public: @@ -60,63 +44,12 @@ class PodcastCategory : public BrowserCategory static QString s_configGroup; static QString s_mergedViewKey; - PodcastCategory( PlaylistBrowserNS::PodcastModel *podcastModel ); + PodcastCategory( QWidget *parent ); ~PodcastCategory(); - PodcastModel *m_podcastModel; - PlaylistsByProviderProxy *m_byProviderProxy; - PodcastView *m_podcastTreeView; - ViewKicker * m_viewKicker; - - PlaylistTreeItemDelegate *m_byProviderDelegate; - QAbstractItemDelegate *m_defaultItemDelegate; - private slots: void showInfo( const QModelIndex & index ); void slotImportOpml(); - void toggleView( bool ); -}; - -class ViewKicker : public QObject -{ -Q_OBJECT - public: - ViewKicker( QTreeView * treeView ); - - private: - QTreeView * m_treeView; - - public slots: - void kickView(); - -}; - -class PodcastView : public Amarok::PrettyTreeView -{ - Q_OBJECT - public: - explicit PodcastView( PodcastModel *model, QWidget *parent = 0 ); - ~PodcastView(); - - protected: - virtual void mousePressEvent( QMouseEvent *event ); - virtual void mouseReleaseEvent( QMouseEvent *event ); - virtual void mouseDoubleClickEvent( QMouseEvent *event ); - virtual void mouseMoveEvent( QMouseEvent *event ); - virtual void startDrag( Qt::DropActions supportedActions ); - - virtual void contextMenuEvent( QContextMenuEvent* event ); - - private: - QList<QAction *> actionsFor( QModelIndexList indexes ); - - PodcastModel *m_podcastModel; - - PopupDropper* m_pd; - - bool m_ongoingDrag; - QMutex m_dragMutex; - bool m_expandToggledWhenPressed; }; /** @@ -130,13 +63,13 @@ class PodcastCategoryDelegate : public QItemDelegate PodcastCategoryDelegate( QTreeView *view ); ~PodcastCategoryDelegate(); - void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; - QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; + void paint( QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index ) const; + QSize sizeHint( const QStyleOptionViewItem &option, const QModelIndex &index ) const; private: QTreeView *m_view; mutable int m_lastHeight; - QWebPage * m_webPage; + QWebPage *m_webPage; }; }