1 /****************************************************************************************
2 * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org> *
3 * *
4 * This program is free software; you can redistribute it and/or modify it under *
5 * the terms of the GNU General Public License as published by the Free Software *
6 * Foundation; either version 2 of the License, or (at your option) any later *
7 * version. *
8 * *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
11 * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
12 * *
13 * You should have received a copy of the GNU General Public License along with *
14 * this program. If not, see <http://www.gnu.org/licenses/>. *
15 ****************************************************************************************/
16
17 #include "ServiceBase.h"
18
19 #include "browsers/CollectionTreeItem.h"
20 #include "browsers/CollectionTreeItemModelBase.h"
21 #include "browsers/InfoProxy.h"
22 #include "core/collections/Collection.h"
23 #include "core/support/Amarok.h"
24 #include "core/support/Debug.h"
25 #include "core-impl/collections/support/CollectionManager.h"
26 #include "widgets/SearchWidget.h"
27 #include "widgets/BoxWidget.h"
28
29 #include <QLabel>
30 #include <QLayout>
31 #include <QMenuBar>
32
33
ServiceFactory()34 ServiceFactory::ServiceFactory()
35 : Plugins::PluginFactory()
36 {
37 CollectionManager::instance()->addTrackProvider( this );
38 connect( this, &ServiceFactory::newService, this, &ServiceFactory::slotNewService );
39 connect( this, &ServiceFactory::removeService, this, &ServiceFactory::slotRemoveService );
40 }
41
~ServiceFactory()42 ServiceFactory::~ServiceFactory()
43 {
44 CollectionManager::instance()->removeTrackProvider( this );
45 }
46
47
48 Meta::TrackPtr
trackForUrl(const QUrl & url)49 ServiceFactory::trackForUrl( const QUrl &url )
50 {
51 if ( m_activeServices.isEmpty() ) {
52 debug() << "our service (" << name() << ") is needed for a url, so init it!";
53 init();
54 }
55
56 foreach( ServiceBase *service, m_activeServices )
57 {
58 if( !service->serviceReady() )
59 {
60 debug() << "our service is not ready! queuing track and returning proxy";
61 MetaProxy::Track *ptrack = new MetaProxy::Track( url, MetaProxy::Track::ManualLookup );
62 MetaProxy::TrackPtr trackptr( ptrack );
63 m_tracksToLocate.enqueue( trackptr );
64 return Meta::TrackPtr::staticCast( trackptr );
65 }
66 else if( service->collection() )
67 {
68 debug() << "Service Ready. Collection is: " << service->collection();
69 return service->collection()->trackForUrl( url );
70 }
71 else
72 warning() << __PRETTY_FUNCTION__ << "service is ready, but service->collection() is null!";
73 }
74 return Meta::TrackPtr();
75 }
76
clearActiveServices()77 void ServiceFactory::clearActiveServices()
78 {
79 m_activeServices.clear();
80 }
81
slotServiceReady()82 void ServiceFactory::slotServiceReady()
83 {
84 while( !m_tracksToLocate.isEmpty() )
85 {
86 MetaProxy::TrackPtr track = m_tracksToLocate.dequeue();
87 if( !track )
88 continue;
89
90 track->lookupTrack( this );
91 }
92 }
93
94 void
slotNewService(ServiceBase * newService)95 ServiceFactory::slotNewService( ServiceBase *newService )
96 {
97 Q_ASSERT( newService );
98 connect( newService, &ServiceBase::ready, this, &ServiceFactory::slotServiceReady );
99 m_activeServices << newService;
100 }
101
102 void
slotRemoveService(ServiceBase * service)103 ServiceFactory::slotRemoveService( ServiceBase *service )
104 {
105 Q_ASSERT( service );
106 m_activeServices.remove( service );
107 service->deleteLater();
108 }
109
110 ServiceBase *ServiceBase::s_instance = nullptr;
111
ServiceBase(const QString & name,ServiceFactory * parent,bool useCollectionTreeView,const QString & prettyName)112 ServiceBase::ServiceBase( const QString &name, ServiceFactory *parent, bool useCollectionTreeView, const QString &prettyName )
113 : BrowserCategory( name, nullptr )
114 , m_contentView ( nullptr )
115 , m_parentFactory( parent )
116 , m_polished( false )
117 , m_useCollectionTreeView( useCollectionTreeView )
118 , m_infoParser( nullptr )
119 , m_serviceready( false )
120 , m_model( nullptr )
121 , m_filterModel( nullptr )
122 {
123 DEBUG_BLOCK
124
125 if ( !prettyName.isEmpty() )
126 {
127 setPrettyName( prettyName );
128 }
129 else
130 setPrettyName( name );
131
132 layout()->setSpacing( 1 );
133
134 m_topPanel = new BoxWidget( true, this );
135
136 if( useCollectionTreeView )
137 {
138 m_contentView = new ServiceCollectionTreeView( this );
139 m_contentView->setFrameShape( QFrame::NoFrame );
140 m_contentView->setSortingEnabled( true );
141 m_contentView->sortByColumn ( 0, Qt::AscendingOrder );
142 m_contentView->setDragEnabled ( true );
143 m_contentView->setDragDropMode ( QAbstractItemView::DragOnly );
144 connect( static_cast<ServiceCollectionTreeView*>( m_contentView ), &ServiceCollectionTreeView::itemSelected,
145 this, &ServiceBase::itemSelected );
146 }
147
148 m_bottomPanel = new BoxWidget( true, this );
149
150 m_bottomPanel->setFrameStyle( QFrame::NoFrame );
151 m_bottomPanel->setLineWidth(2);
152 m_bottomPanel->layout()->setSpacing( 2 );
153 m_bottomPanel->layout()->setMargin( 2 );
154
155 m_filterModel = new QSortFilterProxyModel( this );
156 m_filterModel->setSortCaseSensitivity( Qt::CaseInsensitive );
157 m_filterModel->setFilterCaseSensitivity( Qt::CaseInsensitive );
158
159 m_menubar = new QMenuBar( m_topPanel );
160 // Make sure we do not expose this menubar outside to ensure it does not
161 // replace the main menubar when Amarok is used with Plasma Menubar
162 m_menubar->setNativeMenuBar( false );
163 m_filterMenu = m_menubar->addMenu( i18n( "Group By" ) );
164
165 m_menubar->hide();
166
167 m_searchWidget = new SearchWidget( m_topPanel );
168 if( m_contentView )
169 connect( m_searchWidget, &SearchWidget::filterChanged,
170 static_cast<ServiceCollectionTreeView*>( m_contentView ), &ServiceCollectionTreeView::slotSetFilter );
171 }
172
~ServiceBase()173 ServiceBase::~ServiceBase()
174 {
175 delete m_infoParser;
176 }
177
178 ServiceFactory*
parent() const179 ServiceBase::parent() const
180 {
181 return m_parentFactory;
182 }
183
184 void
itemActivated(const QModelIndex & index)185 ServiceBase::itemActivated ( const QModelIndex & index )
186 {
187 Q_UNUSED( index );
188 }
189
190
191 void
setModel(QAbstractItemModel * model)192 ServiceBase::setModel( QAbstractItemModel * model )
193 {
194 if( m_contentView )
195 m_contentView->setModel( model );
196 m_model = model;
197 }
198
199 QAbstractItemModel *
model()200 ServiceBase::model()
201 {
202 return m_model;
203 }
204
205 QTreeView *
view()206 ServiceBase::view()
207 {
208 return m_contentView;
209 }
210
211 void
setView(QTreeView * view)212 ServiceBase::setView( QTreeView * view )
213 {
214 if( !view)
215 return;
216 m_contentView = view;
217 if( m_model )
218 m_contentView->setModel( m_model );
219 }
220
221 bool
serviceReady() const222 ServiceBase::serviceReady() const
223 {
224 return m_serviceready;
225 }
226
227 void
setServiceReady(bool newReady)228 ServiceBase::setServiceReady( bool newReady )
229 {
230 if( newReady == m_serviceready )
231 return; // nothing to do
232
233 m_serviceready = newReady;
234 if( m_serviceready )
235 Q_EMIT ready();
236 }
237
238 void
infoChanged(const QString & infoHtml)239 ServiceBase::infoChanged( const QString &infoHtml )
240 {
241 QVariantMap map;
242 map[QStringLiteral("service_name")] = prettyName();
243 map[QStringLiteral("main_info")] = infoHtml;
244 The::infoProxy()->setInfo( map );
245 }
246
247 void
itemSelected(CollectionTreeItem * item)248 ServiceBase::itemSelected( CollectionTreeItem * item )
249 {
250
251 Meta::DataPtr ptr = item->data();
252 if ( ( ptr.data() == 0 ) || ( m_infoParser == 0 )) return;
253
254 debug() << "selected item: " << ptr->name();
255
256 ServiceDisplayInfoProvider * infoProvider = dynamic_cast<ServiceDisplayInfoProvider *>( ptr.data() );
257 if (infoProvider == 0 ) return;
258
259 infoProvider->processInfoOf( m_infoParser );
260 }
261
262 void
generateWidgetInfo(const QString & html) const263 ServiceBase::generateWidgetInfo( const QString &html ) const
264 {
265 QVariantMap map;
266 map[QStringLiteral("service_name")] = prettyName();
267 map[QStringLiteral("main_info")] = html;
268 The::infoProxy()->setInfo( map );
269 }
270
271 void
setPlayableTracks(bool playable)272 ServiceBase::setPlayableTracks(bool playable)
273 {
274 if( m_useCollectionTreeView ) {
275 if( ServiceCollectionTreeView* view = dynamic_cast<ServiceCollectionTreeView*>(m_contentView) )
276 view->setPlayableTracks( playable );
277 }
278 }
279
280 void
sortByArtist()281 ServiceBase::sortByArtist()
282 {
283 setLevels( QList<CategoryId::CatMenuId>() << CategoryId::Artist );
284 }
285
286 void
sortByArtistAlbum()287 ServiceBase::sortByArtistAlbum()
288 {
289 setLevels( QList<CategoryId::CatMenuId>() << CategoryId::Artist << CategoryId::Album );
290 }
291
292 void
sortByAlbum()293 ServiceBase::sortByAlbum()
294 {
295 setLevels( QList<CategoryId::CatMenuId>() << CategoryId::Album );
296 }
297
298 void
sortByGenreArtist()299 ServiceBase::sortByGenreArtist()
300 {
301 setLevels( QList<CategoryId::CatMenuId>() << CategoryId::Genre << CategoryId::Artist );
302 }
303
304 void
sortByGenreArtistAlbum()305 ServiceBase::sortByGenreArtistAlbum()
306 {
307 if( m_useCollectionTreeView ) {
308 if( ServiceCollectionTreeView* view = dynamic_cast<ServiceCollectionTreeView*>(m_contentView) )
309 view->setLevels( QList<CategoryId::CatMenuId>() << CategoryId::Genre << CategoryId::Artist << CategoryId::Album );
310 }
311 }
312
313 void
setFilter(const QString & filter)314 ServiceBase::setFilter(const QString & filter)
315 {
316 polish();
317 m_searchWidget->setSearchString( filter );
318 }
319
320 void
setInfoParser(InfoParserBase * infoParser)321 ServiceBase::setInfoParser(InfoParserBase * infoParser)
322 {
323 m_infoParser = infoParser;
324
325 connect ( m_infoParser, &InfoParserBase::info, this, &ServiceBase::infoChanged );
326 }
327
328 InfoParserBase *
infoParser()329 ServiceBase::infoParser()
330 {
331 return m_infoParser;
332 }
333
334 QString
messages()335 ServiceBase::messages()
336 {
337 return i18n( "This service does not accept any messages" );
338 }
339
340 QString
sendMessage(const QString & message)341 ServiceBase::sendMessage( const QString & message )
342 {
343 Q_UNUSED( message );
344 return i18n( "ERROR: unknown message" );
345 }
346
347 QString
filter() const348 ServiceBase::filter() const
349 {
350 return m_searchWidget->currentText();
351 }
352
353 QList<CategoryId::CatMenuId>
levels() const354 ServiceBase::levels() const
355 {
356 CollectionTreeView *contentView = qobject_cast<CollectionTreeView*>(m_contentView);
357 if( contentView )
358 return contentView->levels();
359 return QList<CategoryId::CatMenuId>();
360 }
361
setLevels(const QList<CategoryId::CatMenuId> & levels)362 void ServiceBase::setLevels( const QList<CategoryId::CatMenuId> &levels )
363 {
364 if( m_useCollectionTreeView ) {
365 if( ServiceCollectionTreeView* view = dynamic_cast<ServiceCollectionTreeView*>(m_contentView) )
366 view->setLevels( levels );
367 }
368 }
369
370
371
372