1 /****************************************************************************************
2 * Copyright (c) 2010 Ralf Engels <ralf-engels@gmx.de> *
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 #define DEBUG_PREFIX "BiasFactory"
18
19 #include "BiasFactory.h"
20
21 #include "App.h"
22 #include "biases/AlbumPlayBias.h"
23 #include "biases/IfElseBias.h"
24 #include "biases/PartBias.h"
25 #include "biases/TagMatchBias.h"
26 #include "biases/SearchQueryBias.h"
27 #include "biases/QuizPlayBias.h"
28 #include "biases/EchoNestBias.h"
29 #include "core/support/Debug.h"
30 #include "core/collections/QueryMaker.h"
31 #include "dynamic/Bias.h"
32 #include "scripting/scriptengine/exporters/ScriptableBiasExporter.h"
33
34 #include <QFormLayout>
35 #include <QLabel>
36 #include <QList>
37 #include <QXmlStreamReader>
38
39 Dynamic::BiasPtr
createFromXml(QXmlStreamReader * reader)40 Dynamic::AbstractBiasFactory::createFromXml( QXmlStreamReader *reader )
41 {
42 Dynamic::BiasPtr bias( createBias() );
43 bias->fromXml( reader );
44 return bias;
45 }
46
47 class RandomBiasFactory : public Dynamic::AbstractBiasFactory
48 {
i18nName() const49 QString i18nName() const override
50 { return i18nc("Name of the random bias", "Random"); }
51
name() const52 QString name() const override
53 { return Dynamic::RandomBias::sName(); }
54
i18nDescription() const55 QString i18nDescription() const override
56 { return i18nc("Description of the random bias",
57 "The random bias adds random tracks from the\n"
58 "whole collection without any bias."); }
59
createBias()60 Dynamic::BiasPtr createBias() override
61 { return Dynamic::BiasPtr( new Dynamic::RandomBias() ); }
62 };
63
64
65 class AndBiasFactory : public Dynamic::AbstractBiasFactory
66 {
i18nName() const67 QString i18nName() const override
68 { return i18nc("Name of the \"And\" bias", "And"); }
69
name() const70 QString name() const override
71 { return Dynamic::AndBias::sName(); }
72
i18nDescription() const73 QString i18nDescription() const override
74 { return i18nc("Description of the \"And\" bias",
75 "The \"And\" bias adds tracks that match all\n"
76 "of the sub biases."); }
77
createBias()78 Dynamic::BiasPtr createBias() override
79 { return Dynamic::BiasPtr( new Dynamic::AndBias() ); }
80 };
81
82
83 class OrBiasFactory : public Dynamic::AbstractBiasFactory
84 {
i18nName() const85 QString i18nName() const override
86 { return i18nc("Name of the \"Or\" bias", "Or"); }
87
name() const88 QString name() const override
89 { return Dynamic::OrBias::sName(); }
90
i18nDescription() const91 QString i18nDescription() const override
92 { return i18nc("Description of the \"Or\" bias",
93 "The \"Or\" bias adds tracks that match at\n"
94 "least one of the sub biases."); }
95
createBias()96 Dynamic::BiasPtr createBias() override
97 { return Dynamic::BiasPtr( new Dynamic::OrBias() ); }
98 };
99
100 Dynamic::BiasFactory* Dynamic::BiasFactory::s_instance = nullptr;
101
102 QList<Dynamic::AbstractBiasFactory*> Dynamic::BiasFactory::s_biasFactories = QList<Dynamic::AbstractBiasFactory*>();
103
104 Dynamic::BiasFactory*
instance()105 Dynamic::BiasFactory::instance()
106 {
107 if( !s_instance )
108 {
109 // --- build in biases
110 s_biasFactories.append( new Dynamic::SearchQueryBiasFactory() );
111 s_biasFactories.append( new RandomBiasFactory() );
112 s_biasFactories.append( new AndBiasFactory() );
113 s_biasFactories.append( new OrBiasFactory() );
114 s_biasFactories.append( new Dynamic::PartBiasFactory() );
115 s_biasFactories.append( new Dynamic::IfElseBiasFactory() );
116 s_biasFactories.append( new Dynamic::TagMatchBiasFactory() );
117 s_biasFactories.append( new Dynamic::AlbumPlayBiasFactory() );
118 s_biasFactories.append( new Dynamic::QuizPlayBiasFactory() );
119 s_biasFactories.append( new Dynamic::EchoNestBiasFactory() );
120
121 s_instance = new BiasFactory( pApp );
122 }
123 return s_instance;
124 }
125
126
127
128 // --------------- ReplacementBias -------------
129
130
ReplacementBias(const QString & n)131 Dynamic::ReplacementBias::ReplacementBias( const QString &n )
132 : m_name( n )
133 {
134 connect( BiasFactory::instance(), &Dynamic::BiasFactory::changed, this, &ReplacementBias::factoryChanged );
135 }
136
ReplacementBias(const QString & n,QXmlStreamReader * reader)137 Dynamic::ReplacementBias::ReplacementBias( const QString &n, QXmlStreamReader *reader )
138 : m_name( n )
139 {
140 // -- read the original bias data as one block
141 quint64 start = reader->characterOffset();
142 reader->skipCurrentElement();
143 quint64 end = reader->characterOffset();
144
145 QIODevice *device = reader->device();
146 if( device->isSequential() )
147 {
148 warning() << "Cannot read xml for bias"<<n<<"from sequential device.";
149 return;
150 }
151 device->seek( start );
152 m_html = device->read( end - start );
153
154 debug() << "replacement bias for"<<n<<"is"<<m_html;
155
156 connect( BiasFactory::instance(), &Dynamic::BiasFactory::changed, this, &ReplacementBias::factoryChanged );
157 }
158
159 void
toXml(QXmlStreamWriter * writer) const160 Dynamic::ReplacementBias::toXml( QXmlStreamWriter *writer ) const
161 {
162 Q_UNUSED( writer );
163 writer->writeComment(QStringLiteral("Replacement")); // we need to force the closing of the bias start tag
164 writer->device()->write( m_html.left( m_html.size() - m_name.length() - 3 ) );
165 }
166
167 QString
sName()168 Dynamic::ReplacementBias::sName()
169 {
170 return QStringLiteral( "replacementBias" );
171 }
172
173 QString
name() const174 Dynamic::ReplacementBias::name() const
175 {
176 return m_name;
177 }
178
179 QString
toString() const180 Dynamic::ReplacementBias::toString() const
181 {
182 return i18n( "Replacement for bias %1", m_name );
183 }
184
185 QWidget*
widget(QWidget * parent)186 Dynamic::ReplacementBias::widget( QWidget* parent )
187 {
188 QLabel *label = new QLabel( i18n( "Replacement for bias %1", m_name ), parent );
189
190 return label;
191 }
192
193 void
factoryChanged()194 Dynamic::ReplacementBias::factoryChanged()
195 {
196 DEBUG_BLOCK;
197
198 // -- search if there is a new factory with my name
199 foreach( AbstractBiasFactory* factory, BiasFactory::instance()->factories() )
200 {
201 if( factory->name() == m_name )
202 {
203 debug() << "Found new factory for" << m_name;
204
205 // -- replace myself with the new bias
206 QXmlStreamReader reader( m_html );
207
208 Dynamic::BiasPtr newBias( factory->createFromXml( &reader ) );
209 replace( newBias );
210 return;
211 }
212 }
213 }
214
215
216 // ------------- BiasFactory --------------
217
BiasFactory(QObject * parent)218 Dynamic::BiasFactory::BiasFactory( QObject *parent )
219 : QObject( parent )
220 { }
221
~BiasFactory()222 Dynamic::BiasFactory::~BiasFactory()
223 {
224 qDeleteAll(s_biasFactories);
225 }
226
227 Dynamic::BiasPtr
fromXml(QXmlStreamReader * reader)228 Dynamic::BiasFactory::fromXml( QXmlStreamReader *reader )
229 {
230 QStringRef name = reader->name();
231
232 instance(); // ensure that we have an instance with the default factories
233 foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
234 {
235 if( name == fac->name() )
236 return fac->createFromXml( reader );
237 }
238 return Dynamic::BiasPtr( new ReplacementBias( name.toString(), reader ) );
239 }
240
241 Dynamic::BiasPtr
fromName(const QString & name)242 Dynamic::BiasFactory::fromName( const QString &name )
243 {
244 instance(); // ensure that we have an instance with the default factories
245 foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
246 {
247 if( name == fac->name() )
248 return fac->createBias();
249 }
250 return Dynamic::BiasPtr( new ReplacementBias( name ) );
251 }
252
253 void
registerNewBiasFactory(Dynamic::AbstractBiasFactory * factory)254 Dynamic::BiasFactory::registerNewBiasFactory( Dynamic::AbstractBiasFactory* factory )
255 {
256 instance(); // ensure that we have an instance with the default factories
257 debug() << "new factory of type:" << factory->name();
258 if( !s_biasFactories.contains( factory ) )
259 s_biasFactories.append( factory );
260
261 /*
262 foreach( const QString &name, s_failedMap.keys() )
263 {
264 if( name == entry->pluginName() ) // lazy loading!
265 {
266 debug() << "found entry loaded without proper custombiasentry. fixing now, with old weight of" << s_failedMap[ name ]->weight() ;
267 // need to manually set the weight, as we set it on the old widget which is now being thrown away
268 Dynamic::CustomBiasEntry* cbe = factory->newCustomBiasEntry( s_failedMapXml[ name ] );
269 s_failedMap[ name ]->setCurrentEntry( cbe );
270 s_failedMap.remove( name );
271 s_failedMapXml.remove( name );
272 }
273 }
274 */
275
276 instance()->emitChanged();
277 }
278
279 void
removeBiasFactory(Dynamic::AbstractBiasFactory * factory)280 Dynamic::BiasFactory::removeBiasFactory( Dynamic::AbstractBiasFactory* factory )
281 {
282 if( s_biasFactories.contains( factory ) )
283 s_biasFactories.removeAll( factory );
284
285 instance()->emitChanged();
286 }
287
288 QList<Dynamic::AbstractBiasFactory*>
factories()289 Dynamic::BiasFactory::factories()
290 {
291 instance(); // ensure that we have an instance with the default factories
292 return s_biasFactories;
293 }
294
295 void
emitChanged()296 Dynamic::BiasFactory::emitChanged()
297 {
298 Q_EMIT changed();
299 }
300
301