slight improvement to the context view layout imo.
[amarok.git] / src / SmartPlaylistEditor.cpp
blob73cb419192f13ec3ef0801c232052e515f85bca7
1 /// (c) Pierpaolo Di Panfilo 2004
2 // (c) Alexandre Pereira de Oliveira 2005
3 // (c) 2005 Isaiah Damron <xepo@trifault.net>
4 // (c) 2006 Peter C. Ndikuwera <pndiku@gmail.com>
5 // (c) 2007 Seb Ruiz <ruiz@kde.org>
6 // See COPYING file for licensing information
8 #define DEBUG_PREFIX "SmartPlaylistEditor"
10 #include "SmartPlaylistEditor.h"
12 #include "debug.h"
13 #include "querybuilder.h"
14 #include "metabundle.h"
15 #include "mountpointmanager.h"
17 #include <KComboBox>
18 #include <KLineEdit>
19 #include <KLocale>
20 #include <kmountpoint.h>
21 #include <KNumInput>
22 #include <KVBox>
24 #include <QCheckBox>
25 #include <QDateTime>
26 #include <QFrame>
27 #include <QLabel>
28 #include <QLayout>
29 #include <QObject>
30 #include <QStringList>
31 #include <QToolButton>
32 #include <q3vgroupbox.h>
35 enum Fields
37 FArtist = 0,
38 FComposer,
39 FAlbum,
40 FGenre,
41 FTitle,
42 FLength,
43 FTrack,
44 FYear,
45 FComment,
46 FPlayCounter,
47 FScore,
48 FRating,
49 FFirstPlay,
50 FLastPlay,
51 FModfiedDate,
52 FFilePath,
53 FBPM,
54 FMountPoint,
55 FBitRate,
56 FLabel
60 QStringList m_fields;
61 QStringList m_dbFields;
62 QStringList m_expandableFields;
63 QStringList m_expandableDbFields;
67 SmartPlaylistEditor::SmartPlaylistEditor( QString defaultName, QWidget *parent, const char *name )
68 : KDialog( parent )
70 setObjectName( name );
71 setCaption( i18n("Create Smart Playlist") );
72 setModal( true );
73 setButtons( Ok | Cancel );
74 setDefaultButton( Ok );
75 showButtonSeparator( true );
77 init(defaultName);
78 addCriteriaAny();
79 addCriteriaAll();
83 SmartPlaylistEditor::SmartPlaylistEditor( QWidget *parent, QDomElement xml, const char *name)
84 : KDialog( parent )
86 setObjectName( name );
87 setCaption( i18n("Edit Smart Playlist") );
88 setModal( true );
89 setButtons( Ok | Cancel );
90 setDefaultButton( Ok );
91 showButtonSeparator( true );
94 init( xml.attribute( "name" ) );
95 // matches
96 QDomNodeList matchesList = xml.elementsByTagName( "matches" );
97 bool matchedANY = false, matchedALL = false;
99 m_matchAllCheck->setChecked( true );
100 m_matchAnyCheck->setChecked( true );
102 for (int i = 0, m = matchesList.count(); i<m; i++) {
103 QDomElement matches = matchesList.item(i).toElement();
104 QDomNodeList criteriaList = matches.elementsByTagName( "criteria" );
106 if ( criteriaList.count() ) {
107 for (int j = 0, c=criteriaList.count() ; j<c; ++j ) {
108 QDomElement criteria = criteriaList.item(j).toElement();
110 if (matches.attribute( "glue" ) == "OR") {
111 matchedANY = true;
112 addCriteriaAny( criteria );
114 else {
115 matchedALL = true;
116 addCriteriaAll( criteria );
122 if ( !matchedALL ) {
123 addCriteriaAll();
124 m_matchAllCheck->setChecked( false );
127 if ( !matchedANY ) {
128 m_matchAnyCheck->setChecked( false );
129 addCriteriaAny( );
132 // orderby
133 QDomNodeList orderbyList = xml.elementsByTagName( "orderby" );
134 if ( orderbyList.count() ) {
135 m_orderCheck->setChecked( true );
136 QDomElement orderby = orderbyList.item(0).toElement(); // we only allow one orderby node
138 //random is always the last one.
139 int dbfield = orderby.attribute( "field" ) == "random" ? m_dbFields.count() : m_dbFields.indexOf( orderby.attribute( "field" ) );
141 m_orderCombo->setCurrentIndex( dbfield );
142 updateOrderTypes( dbfield );
143 if ( orderby.attribute( "order" ) == "DESC" || orderby.attribute( "order" ) == "weighted" )
144 m_orderTypeCombo->setCurrentIndex( 1 );
145 else if ( orderby.attribute( "order" ) == "ratingweighted" )
146 m_orderTypeCombo->setCurrentIndex( 2 );
147 else
148 m_orderTypeCombo->setCurrentIndex( 0 );
150 // limit
151 if ( xml.hasAttribute( "maxresults" ) ) {
152 m_limitCheck->setChecked( true );
153 m_limitSpin->setValue( xml.attribute( "maxresults" ).toInt() );
156 // expand by
157 QDomNodeList expandbyList = xml.elementsByTagName( "expandby" );
158 if ( expandbyList.count() ) {
159 m_expandCheck->setChecked( true );
160 QDomElement expandby = expandbyList.item(0).toElement(); // we only allow one orderby node
162 int dbfield = m_expandableFields.indexOf( expandby.attribute( "field" ) );
163 m_expandCombo->setCurrentIndex( dbfield );
168 void SmartPlaylistEditor::init(QString defaultName)
170 KVBox *vbox = new KVBox( this );
171 setMainWidget( vbox );
174 m_fields.clear();
175 m_fields << i18n("Artist") << i18n("Composer") << i18n("Album") << i18n("Genre") << i18n("Title") << i18n("Length")
176 << i18n("Track #") << i18n("Year") << i18n("Comment") << i18n("Play Counter")
177 << i18n("Score") << i18n( "Rating" ) << i18n("First Play")
178 << i18n("Last Play") << i18n("Modified Date") << i18n("File Path")
179 << i18n("BPM") << i18n("Mount Point") << i18n( "Bitrate" ) << i18n( "Label" );
181 m_dbFields.clear();
182 m_dbFields << "artist.name" << "composer.name" << "album.name" << "genre.name" << "tags.title" << "tags.length"
183 << "tags.track" << "year.name" << "tags.comment" << "statistics.playcounter"
184 << "statistics.percentage" << "statistics.rating" << "statistics.createdate"
185 << "statistics.accessdate" << "tags.createdate" << "tags.url"
186 << "tags.bpm" << "devices.lastmountpoint" << "tags.bitrate" << "labels.name";
188 m_expandableFields.clear();
189 m_expandableFields << i18n("Artist") << i18n("Composer") << i18n("Album") << i18n("Genre") << i18n("Year") << i18n( "Label" );
191 m_expandableDbFields.clear();
192 m_expandableDbFields << "artist.name" << "composer.name" << "album.name" << "genre.name" << "year.name" << "labels.name";
194 KHBox *hbox = new KHBox( mainWidget() );
195 hbox->setSpacing( 5 );
196 new QLabel( i18n("Playlist name:"), hbox );
197 m_nameLineEdit = new KLineEdit( defaultName, hbox );
199 QFrame *sep = new QFrame( mainWidget() );
200 sep->setFrameStyle( QFrame::HLine | QFrame::Sunken );
202 //match box (any)
203 KHBox *matchAnyBox = new KHBox( mainWidget() );
204 m_matchAnyCheck = new QCheckBox( i18n("Match Any of the following conditions" ), matchAnyBox );
205 matchAnyBox->setStretchFactor( new QWidget( matchAnyBox ), 1 );
207 //criteria box
208 m_criteriaAnyGroupBox = new QGroupBox( QString(), mainWidget() );
209 QVBoxLayout *anyBoxLayout = new QVBoxLayout();
210 m_criteriaAnyGroupBox->setLayout( anyBoxLayout );
212 //match box (all)
213 KHBox *matchAllBox = new KHBox( mainWidget() );
214 m_matchAllCheck = new QCheckBox( i18n("Match All of the following conditions" ), matchAllBox );
215 matchAllBox->setStretchFactor( new QWidget( matchAllBox ), 1 );
217 //criteria box
218 m_criteriaAllGroupBox = new QGroupBox( QString(), mainWidget() );
219 QVBoxLayout *allBoxLayout = new QVBoxLayout();
220 m_criteriaAllGroupBox->setLayout( allBoxLayout );
222 //order box
223 KHBox *hbox2 = new KHBox( mainWidget() );
224 m_orderCheck = new QCheckBox( i18n("Order by"), hbox2 );
225 KHBox *orderBox = new KHBox( hbox2 );
226 orderBox->setSpacing( 5 );
227 //fields combo
228 m_orderCombo = new KComboBox( orderBox );
229 m_orderCombo->insertItems( 0, m_fields );
230 m_orderCombo->addItem( i18n("Random") );
231 //order type
232 m_orderTypeCombo = new KComboBox( orderBox );
233 updateOrderTypes(0); // populate the new m_orderTypeCombo
234 hbox2->setStretchFactor( new QWidget( hbox2 ), 1 );
236 //limit box
237 KHBox *hbox1 = new KHBox( mainWidget() );
238 m_limitCheck = new QCheckBox( i18n("Limit to"), hbox1 );
239 KHBox *limitBox = new KHBox( hbox1 );
240 limitBox->setSpacing( 5 );
241 m_limitSpin = new QSpinBox( limitBox );
242 m_limitSpin->setMinimum( 1 );
243 m_limitSpin->setMaximum( 100000 );
244 m_limitSpin->setValue( 15 );
245 new QLabel( i18n("tracks"), limitBox );
246 hbox1->setStretchFactor( new QWidget( hbox1 ), 1 );
248 //Expand By
249 KHBox *hbox3 = new KHBox( mainWidget() );
250 m_expandCheck = new QCheckBox( i18n("Expand by"), hbox3 );
251 KHBox *expandBox = new KHBox( hbox3 );
252 expandBox->setSpacing( 5 );
253 m_expandCombo = new KComboBox( expandBox );
254 m_expandCombo->insertItems( 0, m_expandableFields );
255 hbox3->setStretchFactor( new QWidget( hbox3 ), 1 );
257 //add stretch
258 static_cast<KHBox *>(mainWidget())->setStretchFactor(new QWidget(mainWidget()), 1);
260 connect( m_matchAnyCheck, SIGNAL( toggled(bool) ), m_criteriaAnyGroupBox, SLOT( setEnabled(bool) ) );
261 connect( m_matchAllCheck, SIGNAL( toggled(bool) ), m_criteriaAllGroupBox, SLOT( setEnabled(bool) ) );
262 connect( m_orderCheck, SIGNAL( toggled(bool) ), orderBox, SLOT( setEnabled(bool) ) );
263 connect( m_limitCheck, SIGNAL( toggled(bool) ), limitBox, SLOT( setEnabled(bool) ) );
264 connect( m_expandCheck, SIGNAL( toggled(bool) ), expandBox, SLOT( setEnabled(bool) ) );
265 connect( m_orderCombo, SIGNAL( activated(int) ), this, SLOT( updateOrderTypes(int) ) );
267 m_criteriaAnyGroupBox->setEnabled( false );
268 m_criteriaAllGroupBox->setEnabled( false );
270 orderBox->setEnabled( false );
271 limitBox->setEnabled( false );
272 expandBox->setEnabled( false );
274 m_nameLineEdit->setFocus();
276 resize( 550, 200 );
280 void SmartPlaylistEditor::addCriteriaAny()
282 CriteriaEditor *criteria= new CriteriaEditor( this, m_criteriaAnyGroupBox, criteriaAny );
283 m_criteriaAnyGroupBox->layout()->addWidget( criteria );
284 m_criteriaEditorAnyList.append( criteria );
285 m_criteriaEditorAnyList.first()->enableRemove( m_criteriaEditorAnyList.count() > 1 );
288 void SmartPlaylistEditor::addCriteriaAll()
290 CriteriaEditor *criteria= new CriteriaEditor( this, m_criteriaAllGroupBox, criteriaAll );
291 m_criteriaAllGroupBox->layout()->addWidget( criteria );
292 m_criteriaEditorAllList.append( criteria );
293 m_criteriaEditorAllList.first()->enableRemove( m_criteriaEditorAllList.count() > 1 );
296 void SmartPlaylistEditor::addCriteriaAny( QDomElement &xml )
298 CriteriaEditor *criteria = new CriteriaEditor( this, m_criteriaAnyGroupBox, criteriaAny, xml );
299 m_criteriaAnyGroupBox->layout()->addWidget( criteria );
300 m_criteriaEditorAnyList.append( criteria );
301 m_criteriaEditorAnyList.first()->enableRemove( m_criteriaEditorAnyList.count() > 1 );
304 void SmartPlaylistEditor::addCriteriaAll( QDomElement &xml )
306 CriteriaEditor *criteria = new CriteriaEditor( this, m_criteriaAllGroupBox, criteriaAll, xml );
307 m_criteriaAllGroupBox->layout()->addWidget( criteria );
308 m_criteriaEditorAllList.append( criteria );
309 m_criteriaEditorAllList.first()->enableRemove( m_criteriaEditorAllList.count() > 1 );
312 void SmartPlaylistEditor::removeCriteriaAny( CriteriaEditor *criteria )
314 m_criteriaEditorAnyList.removeAll( criteria );
315 criteria->deleteLater();
316 resize( size().width(), sizeHint().height() );
318 if( m_criteriaEditorAnyList.count() == 1 )
319 m_criteriaEditorAnyList.first()->enableRemove( false );
322 void SmartPlaylistEditor::removeCriteriaAll( CriteriaEditor *criteria )
324 m_criteriaEditorAllList.removeAll( criteria );
325 criteria->deleteLater();
326 resize( size().width(), sizeHint().height() );
328 if( m_criteriaEditorAllList.count() == 1 )
329 m_criteriaEditorAllList.first()->enableRemove( false );
332 void SmartPlaylistEditor::updateOrderTypes( int index )
334 int currentOrderType = m_orderTypeCombo->currentIndex();
335 if( index == m_orderCombo->count()-1 ) { // random order selected
336 m_orderTypeCombo->clear();
337 m_orderTypeCombo->addItem( i18n("Completely Random") );
338 m_orderTypeCombo->addItem( i18n("Score Weighted") );
339 m_orderTypeCombo->addItem( i18n("Rating Weighted") );
341 else { // ordinary order column selected
342 m_orderTypeCombo->clear();
343 m_orderTypeCombo->addItem( i18n("Ascending") );
344 m_orderTypeCombo->addItem( i18n("Descending") );
346 if( currentOrderType < m_orderTypeCombo->count() )
347 m_orderTypeCombo->setCurrentIndex( currentOrderType );
348 m_orderTypeCombo->setFont(m_orderTypeCombo->font()); // invalidate size hint
349 m_orderTypeCombo->updateGeometry();
352 QDomElement SmartPlaylistEditor::result()
354 QDomDocument doc;
355 QDomNode node = doc.namedItem( "smartplaylists" );
356 QDomElement nodeE;
357 nodeE = node.toElement();
359 QDomElement smartplaylist = doc.createElement( "smartplaylist" );
361 smartplaylist.setAttribute( "name", name() );
363 // Limit
364 if ( m_limitCheck->isChecked() )
365 smartplaylist.setAttribute( "maxresults", m_limitSpin->value() );
367 nodeE.appendChild( smartplaylist );
368 // Matches
369 if( m_matchAnyCheck->isChecked() )
371 QDomElement matches = doc.createElement("matches");
372 smartplaylist.appendChild( matches );
373 // Iterate through all criteria list
374 foreach( CriteriaEditor *criteriaeditor, m_criteriaEditorAnyList )
375 matches.appendChild( doc.importNode( criteriaeditor->getDomSearchCriteria( doc ), true ) );
377 matches.setAttribute( "glue", "OR" );
378 smartplaylist.appendChild( matches );
381 if( m_matchAllCheck->isChecked() )
383 QDomElement matches = doc.createElement("matches");
384 smartplaylist.appendChild( matches );
385 // Iterate through all criteria list
386 foreach( CriteriaEditor *criteriaeditor, m_criteriaEditorAllList )
387 matches.appendChild( doc.importNode( criteriaeditor->getDomSearchCriteria( doc ), true ) );
389 matches.setAttribute( "glue", "AND" );
390 smartplaylist.appendChild( matches );
393 // Order By
394 if( m_orderCheck->isChecked() ) {
395 QDomElement orderby = doc.createElement("orderby");
396 if (m_orderCombo->currentIndex() != m_orderCombo->count()-1) {
397 orderby.setAttribute( "field", m_dbFields[ m_orderCombo->currentIndex() ] );
398 orderby.setAttribute( "order", m_orderTypeCombo->currentIndex() == 1 ? "DESC" : "ASC" );
399 } else {
400 orderby.setAttribute( "field", "random" );
401 QString order;
402 if ( m_orderTypeCombo->currentIndex() == 0 )
403 order = "random";
404 else if ( m_orderTypeCombo->currentIndex() == 1 )
405 order = "weighted";
406 else
407 order = "ratingweighted";
408 orderby.setAttribute( "order", order );
411 smartplaylist.appendChild( orderby );
414 if( m_expandCheck->isChecked() ) {
415 QDomElement expandBy = doc.createElement("expandby");
416 expandBy.setAttribute( "field", m_expandableFields[ m_expandCombo->currentIndex() ] );
417 smartplaylist.appendChild( expandBy );
420 return (smartplaylist);
423 /////////////////////////////////////////////////////////////////////////////
424 // CLASS CriteriaEditor
425 ////////////////////////////////////////////////////////////////////////////
427 CriteriaEditor::CriteriaEditor( SmartPlaylistEditor *editor, QWidget *parent, int criteriaType, QDomElement criteria )
428 : KHBox( parent )
429 , m_playlistEditor( editor )
430 , m_currentValueType( -1 )
432 setSpacing( 5 );
434 m_fieldCombo = new KComboBox( this );
435 m_fieldCombo->insertItems( 0, m_fields );
437 m_criteriaCombo = new KComboBox( this );
439 m_editBox = new KHBox( this );
440 m_editBox->setSpacing( 5 );
441 setStretchFactor( m_editBox, 1 );
443 m_addButton = new QToolButton( this );
444 m_addButton->setToolButtonStyle( Qt::ToolButtonTextOnly );
445 m_addButton->setText( "+" );
446 m_removeButton = new QToolButton( this );
447 m_removeButton->setToolButtonStyle( Qt::ToolButtonTextOnly );
448 m_removeButton->setText( "-" );
450 connect( m_fieldCombo, SIGNAL( activated(int) ), SLOT( slotFieldSelected(int) ) );
451 connect( m_criteriaCombo, SIGNAL( activated(int) ), SLOT( loadEditWidgets() ) );
452 if (criteriaType == SmartPlaylistEditor::criteriaAny) {
453 connect( m_addButton, SIGNAL( clicked() ), editor, SLOT( addCriteriaAny() ) );
454 connect( m_removeButton, SIGNAL( clicked() ), SLOT( slotRemoveCriteriaAny() ) );
456 else {
457 connect( m_addButton, SIGNAL( clicked() ), editor, SLOT( addCriteriaAll() ) );
458 connect( m_removeButton, SIGNAL( clicked() ), SLOT( slotRemoveCriteriaAll() ) );
461 if ( !criteria.isNull() ) {
462 int field = m_dbFields.indexOf( criteria.attribute( "field" ) );
463 QString condition = criteria.attribute("condition");
466 QStringList values; //List of the values (only one item, unless condition is "is between")
467 QDomNodeList domvalueList = criteria.elementsByTagName( "value" );
468 for (int j = 0, c=domvalueList.count() ; j<c; ++j ) {
469 values << domvalueList.item(j).toElement().text();
472 //Set the selected field
474 m_fieldCombo->setCurrentIndex( field );
475 slotFieldSelected( field );
476 int valueType = getValueType( field );
477 //Load the right set of criterias for this type, in the dialog
478 loadCriteriaList( valueType, condition );
480 loadEditWidgets();
482 switch( valueType ) {
483 case String: //fall through
484 case AutoCompletionString:
486 m_lineEdit->setText( values.first() );
487 break;
489 case Year: //fall through
490 case Number:
492 m_intSpinBox1->setValue( values.first().toInt() );
493 if( condition == i18n("is between") )
494 m_intSpinBox2->setValue( values.last().toInt() );
495 break;
497 case Rating:
499 m_comboBox->setCurrentIndex( ratingToIndex( values.first().toInt() ) );
500 if( condition == i18n("is between") )
501 m_comboBox2->setCurrentIndex( ratingToIndex( values.last().toInt() ) );
502 break;
504 case Date:
506 if( condition == i18n("is in the last") || condition == i18n("is not in the last") ) {
507 m_intSpinBox1->setValue( values.first().toInt() );
508 QString period = criteria.attribute("period");
509 if (period=="days" || period.isEmpty() )
510 m_dateCombo->setCurrentIndex(0);
511 else if (period=="months")
512 m_dateCombo->setCurrentIndex(1);
513 else
514 m_dateCombo->setCurrentIndex(2);
516 else {
517 QDateTime dt;
518 dt.setTime_t( values.first().toUInt() );
519 m_dateEdit1->setDate( dt.date() );
520 if( condition == i18n("is between") ) {
521 dt.setTime_t( values.last().toUInt() );
522 m_dateEdit2->setDate( dt.date() );
525 break;
527 case Length:
529 m_intSpinBox1->setValue( values.first().toInt() );
530 if( condition == i18n("is between") )
531 m_intSpinBox2->setValue( values.last().toInt() );
532 QString period = criteria.attribute( "period" );
533 if ( period == "seconds" || period.isEmpty() ) //for compatibility
534 m_lengthCombo->setCurrentIndex( 0 );
535 else if ( period == "minutes" )
536 m_lengthCombo->setCurrentIndex( 1 );
537 else
538 m_lengthCombo->setCurrentIndex( 2 );
539 break;
541 default: ;
544 else
545 slotFieldSelected( 0 );
546 show();
550 CriteriaEditor::~CriteriaEditor()
554 QDomElement CriteriaEditor::getDomSearchCriteria( QDomDocument &doc )
556 QDomElement criteria = doc.createElement( "criteria" );
557 QString field = m_dbFields[ m_fieldCombo->currentIndex() ];
558 QString condition = m_criteriaCombo->currentText();
560 criteria.setAttribute( "condition", condition );
561 criteria.setAttribute( "field", field );
563 QStringList values;
564 // Get the proper value(s)
565 switch( getValueType( m_fieldCombo->currentIndex() ) ) {
566 case String: // fall through
567 case AutoCompletionString:
568 values << m_lineEdit->text();
569 break;
570 case Year: // fall through
571 case Number:
573 values << QString::number( m_intSpinBox1->value() );
574 if( condition == i18n("is between") )
575 values << QString::number( m_intSpinBox2->value() );
576 break;
578 case Rating:
580 values << QString::number( indexToRating( m_comboBox->currentIndex() ) );
581 if( condition == i18n("is between") )
582 values << QString::number( indexToRating( m_comboBox2->currentIndex() ) );
583 break;
585 case Date:
587 if( condition == i18n("is in the last") || condition == i18n("is not in the last") ) {
588 values << QString::number( m_intSpinBox1->value() );
589 // 0 = days; 1=months; 2=years
590 criteria.setAttribute( "period", !m_dateCombo->currentIndex() ? "days" : (m_dateCombo->currentIndex() == 1 ? "months" : "years") );
592 else {
593 values << QString::number( QDateTime( m_dateEdit1->date() ).toTime_t() );
594 if( condition == i18n("is between") ) {
595 values << QString::number( QDateTime( m_dateEdit2->date() ).toTime_t() );
598 break;
600 case Length:
602 values << QString::number( m_intSpinBox1->value() );
603 // 0 = seconds, 1=minutes, 2=hours
604 criteria.setAttribute( "period", !m_lengthCombo->currentIndex() ? "seconds" : (m_lengthCombo->currentIndex() == 1 ? "minutes" : "hours") );
605 if( condition == i18n( "is between" ) ) {
606 values << QString::number( m_intSpinBox2->value() );
608 break;
610 default: ;
612 oldForeach( values ) {
613 QDomElement value = doc.createElement( "value" );
614 QDomText t = doc.createTextNode( *it );
615 value.appendChild( t );
616 criteria.appendChild( value );
618 return (criteria);
622 QString CriteriaEditor::getSearchCriteria()
624 QString searchCriteria;
625 QString field = m_dbFields[ m_fieldCombo->currentIndex() ];
626 QString criteria = m_criteriaCombo->currentText();
628 if( field.isEmpty() )
629 return QString();
631 if ( ( field=="statistics.playcounter" || field=="statistics.rating" || field=="statistics.percentage" || field=="statistics.accessdate" || field=="statistics.createdate") )
632 searchCriteria += "COALESCE(" + field + ",0)";
633 else
634 searchCriteria += field;
636 QString value;
637 switch( getValueType( m_fieldCombo->currentIndex() ) ) {
638 case String:
639 case AutoCompletionString:
640 value = m_lineEdit->text();
641 break;
642 case Year: //fall through
643 case Number:
644 value = QString::number( m_intSpinBox1->value() );
645 if( criteria == i18n("is between") )
646 value += " AND " + QString::number( m_intSpinBox2->value() );
647 break;
648 case Rating:
650 value = QString::number( indexToRating( m_comboBox->currentIndex() ) );
651 if( criteria == i18n("is between") )
652 value += " AND " + QString::number( indexToRating( m_comboBox2->currentIndex() ) );
653 break;
655 case Date:
657 if( criteria == i18n("is in the last") || criteria == i18n("is not in the last") ) {
658 int n = m_intSpinBox1->value();
659 int time;
660 if( m_dateCombo->currentIndex() == 0 ) //days
661 time=86400*n;
662 else if( m_dateCombo->currentIndex() == 1 ) //months
663 time=86400*30*n;
664 else time=86400*365*n; //years
665 value += "(*CurrentTimeT*)" + QString(" - %1 AND ").arg(time) + "(*CurrentTimeT*)";
667 else {
668 QDateTime datetime1( m_dateEdit1->date() );
669 value += QString::number( datetime1.toTime_t() );
670 if( criteria == i18n("is between") ) {
671 QDateTime datetime2( m_dateEdit2->date() );
672 value += " AND " + QString::number( datetime2.toTime_t() );
674 else
675 value += " AND " + QString::number( datetime1.addDays( 1 ).toTime_t() );
677 break;
679 case Length:
681 int n = m_intSpinBox1->value();
682 int time;
683 if( m_lengthCombo->currentIndex() == 0 ) //seconds
684 time = n;
685 else if( m_lengthCombo->currentIndex() == 1 ) //minutes
686 time = 60*n;
687 else
688 time = 3600*n; //hours
689 value = QString::number( time );
690 if( criteria == i18n("is between") ) {
691 int n2 = m_intSpinBox2->value();
692 int time2;
693 if( m_lengthCombo->currentIndex() == 0 ) //seconds
694 time2 = n2;
695 else if( m_lengthCombo->currentIndex() == 1 ) //minutes
696 time2 = 60*n2;
697 else
698 time2 = 3600*n2; //hours
699 value += " AND " + QString::number( time2 );
701 break;
703 default: ;
707 if( criteria == i18n("contains") )
708 searchCriteria += CollectionDB::likeCondition( value, true, true );
709 else if( criteria == i18n("does not contain") )
710 searchCriteria += " NOT " + CollectionDB::likeCondition( value, true, true );
711 else if( criteria == i18n("is") ) {
712 if( m_currentValueType == Date )
713 searchCriteria += " BETWEEN ";
714 else
715 searchCriteria += " = ";
716 if( m_currentValueType == String || m_currentValueType == AutoCompletionString )
717 value.prepend("'").append("'");
718 searchCriteria += value;
720 else if( criteria == i18n("is not") ) {
721 if( m_currentValueType == Date )
722 searchCriteria += " NOT BETWEEN ";
723 else
724 searchCriteria += " <> ";
725 if( m_currentValueType == String || m_currentValueType == AutoCompletionString )
726 value.prepend("'").append("'");
727 searchCriteria += value;
729 else if( criteria == i18n("starts with") )
731 if( field == "tags.url" )
733 if( value.startsWith( '/' ) )
734 value = '.' + value;
735 if( !value.startsWith( "./" ) )
736 value = "./" + value;
738 searchCriteria += CollectionDB::likeCondition( value, false, true );
740 else if( criteria == i18n("does not start with") )
742 if( field == "tags.url" )
744 if( value.startsWith( '/' ) )
745 value = '.' + value;
746 if( !value.startsWith( "./" ) )
747 value = "./" + value;
749 searchCriteria += " NOT " + CollectionDB::likeCondition( value, false, true );
751 else if( criteria == i18n("ends with") )
752 searchCriteria += CollectionDB::likeCondition( value, true, false );
753 else if( criteria == i18n("does not end with") )
754 searchCriteria += " NOT " + CollectionDB::likeCondition( value, true, false );
755 else if( criteria == i18n("is greater than") || criteria == i18n("is after") )
756 searchCriteria += " > " + value;
757 else if( criteria == i18n("is smaller than") || criteria == i18n("is before" ) )
758 searchCriteria += " < " + value;
759 else if( criteria == i18n("is between") || criteria == i18n("is in the last") )
760 searchCriteria += " BETWEEN " + value;
761 else if( criteria == i18n("is not in the last") )
762 searchCriteria += " NOT BETWEEN " + value;
764 return searchCriteria;
768 void CriteriaEditor::setSearchCriteria( const QString & )
770 //TODO
774 void CriteriaEditor::enableRemove( bool enable )
776 m_removeButton->setEnabled( enable );
780 void CriteriaEditor::slotRemoveCriteriaAny()
782 m_playlistEditor->removeCriteriaAny( this );
785 void CriteriaEditor::slotRemoveCriteriaAll()
787 m_playlistEditor->removeCriteriaAll( this );
790 void CriteriaEditor::slotAddCriteriaAny()
792 m_playlistEditor->addCriteriaAny();
795 void CriteriaEditor::slotAddCriteriaAll()
797 m_playlistEditor->addCriteriaAll();
800 void CriteriaEditor::slotFieldSelected( int field )
802 int valueType = getValueType( field );
803 loadCriteriaList( valueType );
804 loadEditWidgets();
805 m_currentValueType = valueType;
807 //enable auto-completion for artist, album, composer, label, mountpoint and genre
808 if( valueType == AutoCompletionString ) {
809 QStringList items;
810 m_comboBox->clear();
811 m_comboBox->completionObject()->clear();
813 int currentField = m_fieldCombo->currentIndex();
814 if( currentField == FArtist ) //artist
815 items = CollectionDB::instance()->artistList();
816 else if( currentField == FComposer ) //composer
817 items = CollectionDB::instance()->composerList();
818 else if( currentField == FAlbum ) //album
819 items = CollectionDB::instance()->albumList();
820 else if (currentField == FLabel ) //label
821 items = CollectionDB::instance()->labelList();
822 else if (currentField == FMountPoint ) //mount point
824 KMountPoint::List mountpoints = KMountPoint::currentMountPoints( KMountPoint::NeedRealDeviceName );
825 oldForeachType( KMountPoint::List, mountpoints )
827 /* This code is adapted from KDE mediamanager's fstabbackend.cpp
828 * Copyright Kévin Ottens, Bernhard Rosenkraenzer, and from looking
829 * at the commit messages a few other guys who didn't add their name to the header.
831 QString device = (*it)->realDeviceName();
832 QString fs = (*it)->mountType();
833 QString mountpoint = (*it)->mountPoint();
834 if ( fs != "swap"
835 && fs != "tmpfs"
836 && fs != "sysfs"
837 && fs != "fdescfs"
838 && fs != "kernfs"
839 && fs != "usbfs"
840 && !fs.contains( "proc" )
841 && fs != "unknown"
842 && fs != "none"
843 && fs != "sunrpc"
844 && fs != "none"
845 && device != "tmpfs"
846 && device.indexOf("shm") == -1
847 && mountpoint != "/dev/swap"
848 && mountpoint != "/dev/pts"
849 && mountpoint.indexOf("/proc") != 0
850 && mountpoint.indexOf("/sys") != 0
851 || fs.indexOf( "smb" ) != -1
852 || fs.indexOf( "cifs" ) != -1
853 || fs.indexOf( "nfs" ) != -1
855 items << mountpoint;
858 else //genre
859 items = CollectionDB::instance()->genreList();
861 m_comboBox->insertItems( 0, items );
862 m_comboBox->completionObject()->insertItems( items );
863 m_comboBox->completionObject()->setIgnoreCase( true );
864 m_comboBox->setItemText( m_comboBox->currentIndex(), "" );
865 m_comboBox->setFocus();
870 void CriteriaEditor::loadEditWidgets()
872 int valueType = getValueType( m_fieldCombo->currentIndex() );
874 if( m_currentValueType == valueType && !(
875 m_criteriaCombo->currentText() == i18n( "is between" ) ||
876 m_criteriaCombo->currentText() == i18n( "is in the last" ) ||
877 m_criteriaCombo->currentText() == i18n( "is not in the last" ) ||
878 m_lastCriteria == i18n( "is between" ) ||
879 m_lastCriteria == i18n( "is in the last" ) ||
880 m_lastCriteria == i18n( "is not in the last" ) ) )
881 return;
883 /* Store lastCriteria. This information is used above to decide whether it's necessary to change the Widgets */
884 m_lastCriteria = m_criteriaCombo->currentText();
886 QList<QWidget *> list = qFindChildren<QWidget *>( m_editBox );
887 foreach( QWidget *w, list )
888 w->deleteLater();
890 switch( valueType ) {
892 case String:
894 m_lineEdit = new KLineEdit( m_editBox );
895 m_lineEdit->setFocus();
896 m_lineEdit->show();
897 break;
900 case AutoCompletionString: //artist, composer, album, genre, label
902 m_comboBox = new KComboBox( true, m_editBox );
903 m_lineEdit = static_cast<KLineEdit*>( m_comboBox->lineEdit() );
904 m_lineEdit->setFocus();
905 m_comboBox->setMinimumSize( QSize( 240, 20 ) );
906 m_comboBox->show();
907 break;
910 case Year: //fall through
911 case Number:
913 bool yearField = m_fieldCombo->currentText() == i18n("Year");
915 m_intSpinBox1 = new QSpinBox( m_editBox );
916 int maxValue = 1000;
917 if( yearField ) {
918 maxValue = QDate::currentDate().year();
919 m_intSpinBox1->setValue( maxValue );
921 m_intSpinBox1->setMaximum( maxValue );
922 m_intSpinBox1->setFocus();
923 m_intSpinBox1->show();
925 if( m_criteriaCombo->currentText() == i18n("is between") ) {
926 m_rangeLabel = new QLabel( i18n("and"), m_editBox );
927 m_rangeLabel->setAlignment( Qt::AlignHCenter );
928 m_rangeLabel->show();
929 m_intSpinBox2 = new QSpinBox( m_editBox );
930 if( yearField ) {
931 maxValue = QDate::currentDate().year();
932 m_intSpinBox2->setValue( maxValue );
934 m_intSpinBox2->setMaximum( maxValue );
935 m_intSpinBox2->show();
937 break;
940 case Rating:
942 const QStringList list = MetaBundle::ratingList();
943 m_comboBox = new KComboBox( false, m_editBox );
944 m_comboBox->insertItems( 0, list );
945 m_comboBox->show();
947 if( m_criteriaCombo->currentText() == i18n("is between") ) {
948 m_rangeLabel = new QLabel( i18n("and"), m_editBox );
949 m_rangeLabel->setAlignment( Qt::AlignHCenter );
950 m_rangeLabel->show();
951 m_comboBox2 = new KComboBox( false, m_editBox );
952 m_comboBox2->insertItems( 0, list );
953 m_comboBox2->show();
955 break;
958 case Date:
960 if( m_criteriaCombo->currentText() == i18n("is in the last") ||
961 m_criteriaCombo->currentText() == i18n("is not in the last") ) {
962 m_intSpinBox1 = new QSpinBox( m_editBox );
963 m_intSpinBox1->setMinimum( 1 );
964 m_intSpinBox1->show();
965 m_dateCombo = new KComboBox( m_editBox );
966 m_dateCombo->addItem( i18n("Days") );
967 m_dateCombo->addItem( i18n("Months") );
968 m_dateCombo->addItem( i18n("Years") );
969 m_dateCombo->show();
971 else {
972 m_dateEdit1 = new QDateEdit( QDate::currentDate(), m_editBox);
973 m_dateEdit1->setFocus();
974 m_dateEdit1->show();
975 if( m_criteriaCombo->currentText() == i18n("is between") ) {
976 m_rangeLabel = new QLabel( i18n("and"), m_editBox );
977 m_rangeLabel->setAlignment( Qt::AlignHCenter );
978 m_rangeLabel->show();
979 m_dateEdit2 = new QDateEdit( QDate::currentDate(), m_editBox);
980 m_dateEdit2->show();
984 break;
987 case Length:
989 m_intSpinBox1 = new QSpinBox( m_editBox );
990 int maxValue = 1000;
991 m_intSpinBox1->setMaximum( maxValue );
992 m_intSpinBox1->setFocus();
993 m_intSpinBox1->show();
994 if( m_criteriaCombo->currentText() == i18n("is between") ) {
995 m_rangeLabel = new QLabel( i18n("and"), m_editBox );
996 m_rangeLabel->setAlignment( Qt::AlignHCenter );
997 m_rangeLabel->show();
998 m_intSpinBox2 = new QSpinBox( m_editBox );
999 m_intSpinBox2->setMaximum( maxValue );
1000 m_intSpinBox2->show();
1002 m_lengthCombo = new KComboBox( m_editBox );
1003 m_lengthCombo->addItem( i18n( "Seconds" ) );
1004 m_lengthCombo->addItem( i18n( "Minutes" ) );
1005 m_lengthCombo->addItem( i18n( "Hours" ) );
1006 m_lengthCombo->show();
1009 default: ;
1015 void CriteriaEditor::loadCriteriaList( int valueType, QString condition )
1017 if( m_currentValueType == valueType && condition.isNull() )
1018 return;
1020 QStringList items;
1022 switch( valueType ) {
1023 case String:
1024 case AutoCompletionString:
1025 items << i18n( "contains" ) << i18n( "does not contain" ) << i18n( "is" ) << i18n( "is not" )
1026 << i18n( "starts with" ) << i18n( "does not start with" )
1027 << i18n( "ends with" ) << i18n( "does not end with" );
1028 break;
1030 case Rating:
1031 case Length:
1032 case Number:
1033 items << i18n( "is" ) << i18n( "is not" ) << i18n( "is greater than" ) << i18n( "is smaller than" )
1034 << i18n( "is between" );
1035 break;
1037 case Year: //fall through
1038 case Date:
1039 items << i18n( "is" ) << i18n( "is not" ) << i18n( "is before" ) << i18n( "is after" )
1040 << i18n( "is in the last" ) << i18n( "is not in the last" ) << i18n( "is between" );
1041 break;
1042 default: ;
1045 m_criteriaCombo->clear();
1046 m_criteriaCombo->insertItems( 0, items );
1048 if( !condition.isEmpty() )
1050 int index = items.indexOf( condition );
1051 if( index != -1 )
1052 m_criteriaCombo->setCurrentIndex( index );
1057 int CriteriaEditor::getValueType( int index )
1059 int valueType;
1061 switch( index ) {
1062 case FArtist:
1063 case FComposer:
1064 case FAlbum:
1065 case FGenre:
1066 case FLabel:
1067 case FMountPoint:
1068 valueType = AutoCompletionString;
1069 break;
1070 case FTitle:
1071 case FComment:
1072 case FFilePath:
1073 valueType = String;
1074 break;
1075 case FLength:
1076 valueType = Length;
1077 break;
1078 case FTrack:
1079 case FScore:
1080 case FPlayCounter:
1081 case FBPM:
1082 case FBitRate:
1083 valueType = Number;
1084 break;
1085 case FRating:
1086 valueType = Rating;
1087 break;
1088 case FYear:
1089 valueType = Year;
1090 break;
1091 default: valueType = Date;
1094 return valueType;
1098 #include "SmartPlaylistEditor.moc"