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