File indexing completed on 2024-05-19 04:48:39
0001 /**************************************************************************************** 0002 * Copyright (c) 2009-2010 Bart Cerneels <bart.cerneels@kde.org> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "PlaylistBrowserCategory" 0018 0019 #include "PlaylistBrowserCategory.h" 0020 0021 #include "amarokconfig.h" 0022 #include "core/support/Debug.h" 0023 #include "PaletteHandler.h" 0024 #include "PlaylistBrowserModel.h" 0025 #include "playlist/PlaylistModel.h" 0026 #include "playlistmanager/PlaylistManager.h" 0027 #include "PlaylistsInFoldersProxy.h" 0028 #include "PlaylistsByProviderProxy.h" 0029 #include "PlaylistBrowserFilterProxy.h" 0030 #include "SvgHandler.h" 0031 #include "PlaylistBrowserView.h" 0032 #include "widgets/PrettyTreeDelegate.h" 0033 0034 #include <KActionMenu> 0035 #include <KConfigGroup> 0036 #include <QIcon> 0037 #include <QStandardPaths> 0038 #include <KToolBar> 0039 0040 #include <QHeaderView> 0041 #include <QToolBar> 0042 0043 #include <typeinfo> 0044 0045 using namespace PlaylistBrowserNS; 0046 0047 QString PlaylistBrowserCategory::s_mergeViewKey( QStringLiteral("Merged View") ); 0048 0049 PlaylistBrowserCategory::PlaylistBrowserCategory( int playlistCategory, 0050 const QString &categoryName, 0051 const QString &configGroup, 0052 PlaylistBrowserModel *model, 0053 QWidget *parent ) : 0054 BrowserCategory( categoryName, parent ), 0055 m_configGroup( configGroup ), 0056 m_playlistCategory( playlistCategory ) 0057 { 0058 setContentsMargins( 0, 0, 0, 0 ); 0059 setImagePath( QStandardPaths::locate( QStandardPaths::GenericDataLocation, QStringLiteral("amarok/images/hover_info_podcasts.png") ) ); 0060 0061 // set background 0062 if( AmarokConfig::showBrowserBackgroundImage() ) 0063 setBackgroundImage( imagePath() ); 0064 0065 m_toolBar = new KToolBar( this, false, false ); 0066 m_toolBar->setToolButtonStyle( Qt::ToolButtonTextBesideIcon ); 0067 0068 //a QWidget with minimumExpanding makes the next button right aligned. 0069 QWidget *spacerWidget = new QWidget( this ); 0070 spacerWidget->setSizePolicy( QSizePolicy::MinimumExpanding, 0071 QSizePolicy::MinimumExpanding ); 0072 // add a separator so subclasses can add their actions before it 0073 m_separator = m_toolBar->addWidget( spacerWidget ); 0074 0075 m_toolBar->addSeparator(); 0076 0077 m_addFolderAction = new QAction( QIcon::fromTheme( QStringLiteral("folder-new") ), i18n( "Add Folder" ), this ); 0078 m_addFolderAction->setPriority( QAction::LowPriority ); 0079 m_toolBar->addAction( m_addFolderAction ); 0080 connect( m_addFolderAction, &QAction::triggered, this, &PlaylistBrowserCategory::createNewFolder ); 0081 0082 m_providerMenu = new KActionMenu( QIcon::fromTheme( QStringLiteral("checkbox") ), i18n( "Visible Sources"), this ); 0083 m_providerMenu->setDelayed( false ); 0084 m_providerMenu->setPriority( QAction::HighPriority ); 0085 m_toolBar->addAction( m_providerMenu ); 0086 0087 QAction *toggleAction = new QAction( QIcon::fromTheme( QStringLiteral("view-list-tree") ), i18n( "Merged View" ), 0088 m_toolBar ); 0089 toggleAction->setCheckable( true ); 0090 toggleAction->setChecked( Amarok::config( m_configGroup ).readEntry( s_mergeViewKey, false ) ); 0091 toggleAction->setPriority( QAction::LowPriority ); 0092 m_toolBar->addAction( toggleAction ); 0093 connect( toggleAction, &QAction::triggered, this, &PlaylistBrowserCategory::toggleView ); 0094 0095 m_toolBar->addSeparator(); 0096 0097 m_byProviderProxy = new PlaylistsByProviderProxy( m_playlistCategory, this ); 0098 m_byProviderProxy->setSourceModel( model ); 0099 m_byProviderProxy->setGroupedColumn( PlaylistBrowserModel::ProviderColumn ); 0100 m_byFolderProxy = new PlaylistsInFoldersProxy( model ); 0101 0102 m_filterProxy = new PlaylistBrowserFilterProxy( this ); 0103 //no need to setModel on filterProxy since it will be done in toggleView anyway. 0104 m_filterProxy->setDynamicSortFilter( true ); 0105 m_filterProxy->setFilterKeyColumn( PlaylistBrowserModel::ProviderColumn ); 0106 0107 m_playlistView = new PlaylistBrowserView( m_filterProxy, this ); 0108 m_defaultItemDelegate = m_playlistView->itemDelegate(); 0109 m_byProviderDelegate = new PrettyTreeDelegate( m_playlistView ); 0110 0111 toggleView( toggleAction->isChecked() ); 0112 0113 m_playlistView->setFrameShape( QFrame::NoFrame ); 0114 m_playlistView->setContentsMargins( 0, 0, 0, 0 ); 0115 m_playlistView->setAlternatingRowColors( true ); 0116 m_playlistView->header()->hide(); 0117 //hide all columns except the first. 0118 for( int i = 1; i < m_playlistView->model()->columnCount(); i++ ) 0119 m_playlistView->hideColumn( i ); 0120 0121 m_playlistView->setDragEnabled( true ); 0122 m_playlistView->setAcceptDrops( true ); 0123 m_playlistView->setDropIndicatorShown( true ); 0124 0125 foreach( const Playlists::PlaylistProvider *provider, 0126 The::playlistManager()->providersForCategory( m_playlistCategory ) ) 0127 { 0128 createProviderButton( provider ); 0129 } 0130 0131 connect( The::playlistManager(), &PlaylistManager::providerAdded, 0132 this, &PlaylistBrowserCategory::slotProviderAdded ); 0133 connect( The::playlistManager(), &PlaylistManager::providerRemoved, 0134 this, &PlaylistBrowserCategory::slotProviderRemoved ); 0135 0136 connect( The::paletteHandler(), &PaletteHandler::newPalette, 0137 this, &PlaylistBrowserCategory::newPalette ); 0138 } 0139 0140 PlaylistBrowserCategory::~PlaylistBrowserCategory() 0141 { 0142 } 0143 0144 QString 0145 PlaylistBrowserCategory::filter() const 0146 { 0147 return QUrl::toPercentEncoding( m_filterProxy->filterRegExp().pattern() ); 0148 } 0149 0150 void 0151 PlaylistBrowserCategory::setFilter( const QString &filter ) 0152 { 0153 debug() << "Setting filter " << filter; 0154 m_filterProxy->setFilterRegExp( QRegExp( QUrl::fromPercentEncoding( filter.toUtf8() ) ) ); 0155 //disable all other provider-buttons 0156 foreach( QAction * const providerAction, m_providerActions ) 0157 { 0158 const Playlists::PlaylistProvider *provider = 0159 providerAction->data().value<const Playlists::PlaylistProvider *>(); 0160 if( provider ) 0161 providerAction->setChecked( 0162 m_filterProxy->filterRegExp().exactMatch( provider->prettyName() ) ); 0163 } 0164 } 0165 0166 QTreeView * 0167 PlaylistBrowserCategory::playlistView() 0168 { 0169 return m_playlistView; 0170 } 0171 0172 void 0173 PlaylistBrowserCategory::toggleView( bool merged ) 0174 { 0175 if( merged ) 0176 { 0177 m_filterProxy->setSourceModel( m_byFolderProxy ); 0178 m_playlistView->setItemDelegate( m_defaultItemDelegate ); 0179 m_playlistView->setRootIsDecorated( true ); 0180 setHelpText( m_addFolderAction->text(), m_addFolderAction ); 0181 } 0182 else 0183 { 0184 m_filterProxy->setSourceModel( m_byProviderProxy ); 0185 m_playlistView->setItemDelegate( m_byProviderDelegate ); 0186 m_playlistView->setRootIsDecorated( false ); 0187 setHelpText( i18n( "Folders are only shown in <b>merged view</b>." ), m_addFolderAction ); 0188 } 0189 0190 //folders don't make sense in per-provider view 0191 m_addFolderAction->setEnabled( merged ); 0192 0193 Amarok::config( m_configGroup ).writeEntry( s_mergeViewKey, merged ); 0194 } 0195 0196 void 0197 PlaylistBrowserCategory::setHelpText(const QString &text, QAction *qa) 0198 { 0199 qa->setStatusTip(text); 0200 qa->setToolTip(text); 0201 if ((qa->whatsThis()).isEmpty()) { 0202 qa->setWhatsThis(text); 0203 } 0204 } 0205 0206 void 0207 PlaylistBrowserCategory::slotProviderAdded( Playlists::PlaylistProvider *provider, int category ) 0208 { 0209 if( category != m_playlistCategory ) 0210 return; //ignore 0211 0212 if( !m_providerActions.keys().contains( provider ) ) 0213 createProviderButton( provider ); 0214 0215 if( provider->playlistCount() != 0 ) 0216 toggleView( false ); // set view to non-merged if new provider has some tracks 0217 0218 slotToggleProviderButton(); 0219 } 0220 0221 void 0222 PlaylistBrowserCategory::slotProviderRemoved( Playlists::PlaylistProvider *provider, int category ) 0223 { 0224 Q_UNUSED( category ) 0225 0226 if( m_providerActions.keys().contains( provider ) ) 0227 { 0228 QAction *providerToggle = m_providerActions.take( provider ); 0229 m_providerMenu->removeAction( providerToggle ); 0230 } 0231 } 0232 0233 void 0234 PlaylistBrowserCategory::createProviderButton( const Playlists::PlaylistProvider *provider ) 0235 { 0236 QAction *providerToggle = new QAction( provider->icon(), provider->prettyName(), this ); 0237 providerToggle->setCheckable( true ); 0238 providerToggle->setChecked( true ); 0239 providerToggle->setData( QVariant::fromValue( provider ) ); 0240 connect( providerToggle, &QAction::toggled, this, &PlaylistBrowserCategory::slotToggleProviderButton ); 0241 m_providerMenu->addAction( providerToggle ); 0242 0243 //if there is only one provider the button needs to be disabled. 0244 //When a second is added we can enable the first. 0245 if( m_providerActions.isEmpty() ) 0246 providerToggle->setEnabled( false ); 0247 else if( m_providerActions.count() == 1 ) 0248 m_providerActions.values().first()->setEnabled( true ); 0249 0250 m_providerActions.insert( provider, providerToggle ); 0251 } 0252 0253 void 0254 PlaylistBrowserCategory::slotToggleProviderButton() 0255 { 0256 QString filter; 0257 QActionList checkedActions; 0258 foreach( const Playlists::PlaylistProvider *p, m_providerActions.keys() ) 0259 { 0260 QAction *action = m_providerActions.value( p ); 0261 if( action->isChecked() ) 0262 { 0263 QString escapedName = QRegExp::escape( p->prettyName() ).replace( ' ', QLatin1String("\\ ") ); 0264 filter += QString( filter.isEmpty() ? "%1" : "|%1" ).arg( escapedName ); 0265 checkedActions << action; 0266 action->setEnabled( true ); 0267 } 0268 } 0269 //if all are enabled the filter can be completely disabled. 0270 if( checkedActions.count() == m_providerActions.count() ) 0271 filter.clear(); 0272 0273 m_filterProxy->setFilterRegExp( filter ); 0274 0275 //don't allow the last visible provider to be hidden 0276 if( checkedActions.count() == 1 ) 0277 checkedActions.first()->setEnabled( false ); 0278 } 0279 0280 void 0281 PlaylistBrowserCategory::createNewFolder() 0282 { 0283 QString name = i18nc( "default name for new folder", "New Folder" ); 0284 const QModelIndex &rootIndex = m_byFolderProxy->index(0,0); 0285 QModelIndexList folderIndices = m_byFolderProxy->match( rootIndex, Qt::DisplayRole, name, -1 ); 0286 QString groupName = name; 0287 if( !folderIndices.isEmpty() ) 0288 { 0289 int folderCount( 0 ); 0290 foreach( const QModelIndex &folder, folderIndices ) 0291 { 0292 QRegExp regex( name + " \\((\\d+)\\)" ); 0293 int matchIndex = regex.indexIn( folder.data( Qt::DisplayRole ).toString() ); 0294 if (matchIndex != -1) 0295 { 0296 int newNumber = regex.cap( 1 ).toInt(); 0297 if (newNumber > folderCount) 0298 folderCount = newNumber; 0299 } 0300 } 0301 groupName += QStringLiteral( " (%1)" ).arg( folderCount + 1 ); 0302 } 0303 QModelIndex idx = m_filterProxy->mapFromSource( m_byFolderProxy->createNewFolder( groupName ) ); 0304 m_playlistView->setCurrentIndex( idx ); 0305 m_playlistView->edit( idx ); 0306 } 0307 0308 void 0309 PlaylistBrowserCategory::newPalette( const QPalette &palette ) 0310 { 0311 Q_UNUSED( palette ) 0312 0313 The::paletteHandler()->updateItemView( m_playlistView ); 0314 } 0315