Several bias bugfixes
[amarok:rengelss-amarok.git] / src / dynamic / BiasFactory.cpp
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 #include "BiasFactory.h"
18 #include "Bias.h"
19 #include "DynamicBiasWidgets.h"
20
21 #include "biases/AlbumPlayBias.h"
22 #include "biases/PartBias.h"
23 #include "biases/TagMatchBias.h"
24 #include "biases/QuizPlayBias.h"
25 #include "biases/EchoNestBias.h"
26
27 #define DEBUG_PREFIX "BiasFactory"
28
29 #include "App.h"
30 #include "core/support/Debug.h"
31
32 #include <QList>
33 #include <QXmlStreamReader>
34
35 #include <QLabel>
36 #include <QFormLayout>
37
38 class RandomBiasFactory : public Dynamic::AbstractBiasFactory
39 {
40     QString i18nName() const
41     { return i18nc("Name of the random bias", "Random"); }
42
43     QString name() const
44     { return Dynamic::RandomBias::sName(); }
45
46     QString i18nDescription() const
47     { return i18nc("Description of the random bias",
48                    "The random bias adds random tracks from the whole collection without any bias."); }
49
50     Dynamic::BiasPtr createBias()
51     { return Dynamic::BiasPtr( new Dynamic::RandomBias() ); }
52
53     Dynamic::BiasPtr createBias( QXmlStreamReader *reader )
54     { return Dynamic::BiasPtr( new Dynamic::RandomBias( reader ) ); }
55 };
56
57
58 class UniqueBiasFactory : public Dynamic::AbstractBiasFactory
59 {
60     QString i18nName() const
61     { return i18nc("Name of the unique bias", "Unique"); }
62
63     QString name() const
64     { return Dynamic::UniqueBias::sName(); }
65
66     QString i18nDescription() const
67     { return i18nc("Description of the unique bias",
68                    "The unique bias adds matches tracks that are not already in the playlist effectively preventing duplicates."); }
69
70     Dynamic::BiasPtr createBias()
71     { return Dynamic::BiasPtr( new Dynamic::UniqueBias() ); }
72
73     Dynamic::BiasPtr createBias( QXmlStreamReader *reader )
74     { return Dynamic::BiasPtr( new Dynamic::UniqueBias( reader ) ); }
75 };
76
77
78 class NotBiasFactory : public Dynamic::AbstractBiasFactory
79 {
80     QString i18nName() const
81     { return i18nc("Name of the \"Not\" bias", "Not"); }
82
83     QString name() const
84     { return Dynamic::NotBias::sName(); }
85
86     QString i18nDescription() const
87     { return i18nc("Description of the \"Not\" bias",
88                    "The \"Not\" bias adds tracks that do not match any sub bias."); }
89
90     Dynamic::BiasPtr createBias()
91     { return Dynamic::BiasPtr( new Dynamic::NotBias() ); }
92
93     Dynamic::BiasPtr createBias( QXmlStreamReader *reader )
94     { return Dynamic::BiasPtr( new Dynamic::NotBias( reader ) ); }
95 };
96
97
98 class AndBiasFactory : public Dynamic::AbstractBiasFactory
99 {
100     QString i18nName() const
101     { return i18nc("Name of the \"And\" bias", "And"); }
102
103     QString name() const
104     { return Dynamic::AndBias::sName(); }
105
106     QString i18nDescription() const
107     { return i18nc("Description of the \"And\" bias",
108                    "The \"And\" bias adds tracks that match all of the sub biases at the same time."); }
109
110     Dynamic::BiasPtr createBias()
111     { return Dynamic::BiasPtr( new Dynamic::AndBias() ); }
112
113     Dynamic::BiasPtr createBias( QXmlStreamReader *reader )
114     { return Dynamic::BiasPtr( new Dynamic::AndBias( reader ) ); }
115 };
116
117
118 class OrBiasFactory : public Dynamic::AbstractBiasFactory
119 {
120     QString i18nName() const
121     { return i18nc("Name of the \"Or\" bias", "Or"); }
122
123     QString name() const
124     { return Dynamic::OrBias::sName(); }
125
126     QString i18nDescription() const
127     { return i18nc("Description of the \"Or\" bias",
128                    "The \"Or\" bias adds tracks that match at least one of the sub biases at the same time."); }
129
130     Dynamic::BiasPtr createBias()
131     { return Dynamic::BiasPtr( new Dynamic::OrBias() ); }
132
133     Dynamic::BiasPtr createBias( QXmlStreamReader *reader )
134     { return Dynamic::BiasPtr( new Dynamic::OrBias( reader ) ); }
135 };
136
137 Dynamic::BiasFactory* Dynamic::BiasFactory::s_instance = 0;
138
139 QList<Dynamic::AbstractBiasFactory*> Dynamic::BiasFactory::s_biasFactories = QList<Dynamic::AbstractBiasFactory*>();
140
141 Dynamic::BiasFactory*
142 Dynamic::BiasFactory::instance()
143 {
144     if( !s_instance )
145     {
146         // --- build in biases
147         s_biasFactories.append( new Dynamic::TagMatchBiasFactory() );
148         s_biasFactories.append( new Dynamic::PartBiasFactory() );
149         s_biasFactories.append( new RandomBiasFactory() );
150         s_biasFactories.append( new UniqueBiasFactory() );
151         s_biasFactories.append( new NotBiasFactory() );
152         s_biasFactories.append( new AndBiasFactory() );
153         s_biasFactories.append( new OrBiasFactory() );
154         s_biasFactories.append( new Dynamic::AlbumPlayBiasFactory() );
155         s_biasFactories.append( new Dynamic::QuizPlayBiasFactory() );
156         s_biasFactories.append( new Dynamic::EchoNestBiasFactory() );
157
158         s_instance = new BiasFactory( App::instance() );
159     }
160     return s_instance;
161 }
162
163
164
165 // --------------- ReplacementBias -------------
166
167
168 Dynamic::ReplacementBias::ReplacementBias( const QString &n )
169     : m_name( n )
170 {
171     connect( BiasFactory::instance(), SIGNAL( changed() ), this, SLOT( factoryChanged() ) );
172 }
173
174 Dynamic::ReplacementBias::ReplacementBias( const QString &n, QXmlStreamReader *reader )
175     : m_name( n )
176 {
177     // -- read the original bias data as one block
178     quint64 start = reader->characterOffset();
179     reader->skipCurrentElement();
180     quint64 end = reader->characterOffset();
181
182     QIODevice *device = reader->device();
183     if( device->isSequential() )
184     {
185         warning() << "Cannot read xml for bias"<<n<<"from sequential device.";
186         return;
187     }
188     device->seek( start );
189     m_html = device->read( end - start );
190
191 debug() << "replacement bias for"<<n<<"is"<<m_html;
192
193     connect( BiasFactory::instance(), SIGNAL( changed() ), this, SLOT( factoryChanged() ) );
194 }
195
196 void
197 Dynamic::ReplacementBias::toXml( QXmlStreamWriter *writer ) const
198 {
199     Q_UNUSED( writer );
200     writer->device()->write( m_html.left( m_html.size() - m_name.length() - 3 ) );
201 }
202
203 QString
204 Dynamic::ReplacementBias::sName()
205 {
206     return QLatin1String( "replacementBias" );
207 }
208
209 QString
210 Dynamic::ReplacementBias::name() const
211 {
212     return m_name;
213 }
214
215 QWidget*
216 Dynamic::ReplacementBias::widget( QWidget* parent )
217 {
218     PlaylistBrowserNS::BiasWidget *bw = new PlaylistBrowserNS::BiasWidget( BiasPtr(this), parent );
219
220     QLabel *label = new QLabel( m_name );
221     bw->formLayout()->addRow( i18n( "Replacement for:" ), label );
222
223     return bw;
224 }
225
226 void
227 Dynamic::ReplacementBias::factoryChanged()
228 {
229     DEBUG_BLOCK;
230
231     // -- search if there is a new factory with my name
232     foreach( AbstractBiasFactory* factory, BiasFactory::instance()->factories() )
233     {
234         if( factory->name() == m_name )
235         {
236             debug() << "Found new factory for" << m_name;
237
238             // -- replace myself with the new bias
239             QXmlStreamReader reader( m_html );
240
241             Dynamic::BiasPtr newBias( factory->createBias( &reader ) );
242             replace( newBias );
243             return;
244         }
245     }
246 }
247
248
249 // ------------- BiasFactory --------------
250
251 Dynamic::BiasFactory::BiasFactory( QObject *parent )
252     : QObject( parent )
253 { }
254
255 Dynamic::BiasPtr
256 Dynamic::BiasFactory::fromXml( QXmlStreamReader *reader )
257 {
258     QStringRef name = reader->name();
259
260     instance(); // ensure that we have an instance with the default factories
261     foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
262     {
263         if( name == fac->name() )
264             return fac->createBias( reader );
265     }
266     return Dynamic::BiasPtr( new ReplacementBias( name.toString(), reader ) );
267 }
268
269 Dynamic::BiasPtr
270 Dynamic::BiasFactory::fromName( const QString &name )
271 {
272     instance(); // ensure that we have an instance with the default factories
273     foreach( Dynamic::AbstractBiasFactory* fac, s_biasFactories )
274     {
275         if( name == fac->name() )
276             return fac->createBias();
277     }
278     return Dynamic::BiasPtr( new ReplacementBias( name ) );
279 }
280
281 void
282 Dynamic::BiasFactory::registerNewBiasFactory( Dynamic::AbstractBiasFactory* factory )
283 {
284     instance(); // ensure that we have an instance with the default factories
285     debug() << "new factory of type:" << factory->name();
286     if( !s_biasFactories.contains( factory ) )
287         s_biasFactories.append( factory );
288
289     /*
290     foreach( const QString &name, s_failedMap.keys() )
291     {
292         if( name == entry->pluginName() ) // lazy loading!
293         {
294             debug() << "found entry loaded without proper custombiasentry. fixing now, with  old weight of" << s_failedMap[ name ]->weight() ;
295             //  need to manually set the weight, as we set it on the old widget which is now being thrown away
296             Dynamic::CustomBiasEntry* cbe = factory->newCustomBiasEntry( s_failedMapXml[ name ] );
297             s_failedMap[ name ]->setCurrentEntry( cbe );
298             s_failedMap.remove( name );
299             s_failedMapXml.remove( name );
300         }
301     }
302     */
303
304     instance()->emitChanged();
305 }
306
307 void
308 Dynamic::BiasFactory::removeBiasFactory( Dynamic::AbstractBiasFactory* factory )
309 {
310     if( s_biasFactories.contains( factory ) )
311         s_biasFactories.removeAll( factory );
312
313     instance()->emitChanged();
314 }
315
316 QList<Dynamic::AbstractBiasFactory*>
317 Dynamic::BiasFactory::factories()
318 {
319     instance(); // ensure that we have an instance with the default factories
320     return s_biasFactories;
321 }
322
323 void
324 Dynamic::BiasFactory::emitChanged()
325 {
326     emit changed();
327 }
328
329 #include "BiasFactory.moc"