Warning, file /multimedia/amarok/src/widgets/MetaQueryWidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /**************************************************************************************** 0002 * Copyright (c) 2008 Daniel Caleb Jones <danielcjones@gmail.com> * 0003 * Copyright (c) 2009 Mark Kretschmann <kretschmann@kde.org> * 0004 * Copyright (c) 2010 Ralf Engels <ralf-engels@gmx.de> * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify it under * 0007 * the terms of the GNU General Public License as published by the Free Software * 0008 * Foundation; either version 2 of the License, or (at your option) version 3 or * 0009 * any later version accepted by the membership of KDE e.V. (or its successor approved * 0010 * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * 0011 * version 3 of the license. * 0012 * * 0013 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0014 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0015 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0016 * * 0017 * You should have received a copy of the GNU General Public License along with * 0018 * this program. If not, see <http://www.gnu.org/licenses/>. * 0019 ****************************************************************************************/ 0020 0021 #include "core-impl/collections/support/CollectionManager.h" 0022 #include "core/collections/MetaQueryMaker.h" 0023 #include "core/collections/QueryMaker.h" 0024 #include "widgets/MetaQueryWidget.h" 0025 #include "widgets/kdatecombo.h" 0026 #include "FileType.h" 0027 0028 #include <typeinfo> 0029 0030 #include <QWidget> 0031 #include <QLineEdit> 0032 #include <QGridLayout> 0033 #include <QHBoxLayout> 0034 #include <QVBoxLayout> 0035 #include <QLabel> 0036 #include <QListView> 0037 #include <QTimeEdit> 0038 0039 #include <QIcon> 0040 #include <KLocalizedString> 0041 #include <KRatingWidget> 0042 0043 using namespace Amarok; 0044 0045 static const int maxHours = 24; 0046 0047 TimeDistanceWidget::TimeDistanceWidget( QWidget *parent ) 0048 : QWidget( parent ) 0049 { 0050 m_timeEdit = new QSpinBox(this); 0051 m_timeEdit->setMinimum( 0 ); 0052 m_timeEdit->setMaximum( 600 ); 0053 0054 m_unitSelection = new QComboBox(this); 0055 connect( m_timeEdit, QOverload<int>::of(&QSpinBox::valueChanged), 0056 this, &TimeDistanceWidget::slotUpdateComboBoxLabels ); 0057 for (int i = 0; i < 7; ++i) { 0058 m_unitSelection->addItem( QString() ); 0059 } 0060 slotUpdateComboBoxLabels( 0 ); 0061 0062 QHBoxLayout *hLayout = new QHBoxLayout(this); 0063 hLayout->setContentsMargins(0, 0, 0, 0); 0064 hLayout->addWidget( m_timeEdit ); 0065 hLayout->addWidget( m_unitSelection ); 0066 } 0067 0068 qint64 TimeDistanceWidget::timeDistance() const 0069 { 0070 qint64 time = m_timeEdit->value(); 0071 switch( m_unitSelection->currentIndex() ) 0072 { 0073 case 6: 0074 time *= 365*24*60*60; // years 0075 break; 0076 case 5: 0077 time *= 30*24*60*60; // months 0078 break; 0079 case 4: 0080 time *= 7*24*60*60; // weeks 0081 break; 0082 case 3: 0083 time *= 24*60*60; // days 0084 break; 0085 case 2: 0086 time *= 60*60; // hours 0087 break; 0088 case 1: 0089 time *= 60; // minutes 0090 break; 0091 } 0092 0093 return time; 0094 } 0095 0096 void TimeDistanceWidget::setTimeDistance( qint64 value ) 0097 { 0098 // as we don't store the time unit we try to reconstruct it 0099 int unit = 0; 0100 if( value > 600 || !(value % 60) ) { 0101 unit = 1; 0102 value /= 60; 0103 0104 if( value > 600 || !(value % 60) ) { 0105 unit = 2; 0106 value /= 60; 0107 0108 if( value > 72 || !(value % 24) ) { 0109 unit = 3; 0110 value /= 24; 0111 0112 if( !(value % 365) ) { 0113 unit = 6; 0114 value /= 365; 0115 } else if( !(value % 30) ) { 0116 unit = 5; 0117 value /= 30; 0118 } else if( !(value % 7) ) { 0119 unit = 4; 0120 value /= 7; 0121 } 0122 } 0123 } 0124 } 0125 0126 m_unitSelection->setCurrentIndex( unit ); 0127 m_timeEdit->setValue( value ); 0128 } 0129 0130 void TimeDistanceWidget::slotUpdateComboBoxLabels( int value ) 0131 { 0132 m_unitSelection->setItemText(0, i18np("second", "seconds", value)); 0133 m_unitSelection->setItemText(1, i18np("minute", "minutes", value)); 0134 m_unitSelection->setItemText(2, i18np("hour", "hours", value)); 0135 m_unitSelection->setItemText(3, i18np("day", "days", value)); 0136 m_unitSelection->setItemText(4, i18np("week", "weeks", value)); 0137 m_unitSelection->setItemText(5, i18np("month", "months", value)); 0138 m_unitSelection->setItemText(6, i18np("year", "years", value)); 0139 } 0140 0141 void 0142 MetaQueryWidget::Filter::setField( qint64 newField ) 0143 { 0144 if( m_field == newField ) 0145 return; 0146 0147 // -- reset the value and the condition if the new filter has another type 0148 if( MetaQueryWidget::isNumeric( m_field ) != MetaQueryWidget::isNumeric( newField ) ) 0149 { 0150 value.clear(); 0151 if( MetaQueryWidget::isNumeric( newField ) ) 0152 condition = Equals; 0153 else 0154 condition = Contains; 0155 } 0156 if( !MetaQueryWidget::isDate( m_field ) && MetaQueryWidget::isDate( newField ) ) 0157 { 0158 numValue = QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); 0159 numValue2 = QDateTime::currentDateTimeUtc().toSecsSinceEpoch(); 0160 } 0161 else 0162 { 0163 numValue = 0; 0164 numValue2 = 0; 0165 } 0166 0167 if (numValue < minimumValue( newField ) || numValue > maximumValue( newField ) ) 0168 numValue = defaultValue( newField ); 0169 0170 if (numValue2 < minimumValue( newField ) || numValue2 > maximumValue( newField ) ) 0171 numValue2 = defaultValue( newField ); 0172 0173 m_field = newField; 0174 } 0175 0176 qint64 0177 MetaQueryWidget::Filter::minimumValue( quint64 field ) 0178 { 0179 switch( field ) 0180 { 0181 case Meta::valYear: return 1900; 0182 case Meta::valTrackNr: return 0; 0183 case Meta::valDiscNr: return 0; 0184 case Meta::valBpm: return 60; 0185 case Meta::valBitrate: return 60; 0186 case Meta::valSamplerate: return 8000; 0187 case Meta::valFilesize: return 0; 0188 case Meta::valScore: return 0; 0189 case Meta::valPlaycount: return 0; 0190 case Meta::valRating: return 0; 0191 case Meta::valLength: return 0; 0192 default: return 0; 0193 } 0194 } 0195 0196 qint64 0197 MetaQueryWidget::Filter::maximumValue( quint64 field ) 0198 { 0199 switch( field ) 0200 { 0201 case Meta::valYear: return 2300; 0202 case Meta::valTrackNr: return 100; 0203 case Meta::valDiscNr: return 10; 0204 case Meta::valBpm: return 200; 0205 case Meta::valBitrate: return 2000; 0206 case Meta::valSamplerate: return 48000; 0207 case Meta::valFilesize: return 1000; 0208 case Meta::valScore: return 100; 0209 case Meta::valPlaycount: return 1000; 0210 case Meta::valRating: return 10; 0211 case Meta::valLength: return maxHours * 60 * 60 - 1; 0212 default: return 0; 0213 } 0214 } 0215 0216 qint64 0217 MetaQueryWidget::Filter::defaultValue( quint64 field ) 0218 { 0219 switch( field ) 0220 { 0221 case Meta::valYear: return 1976; 0222 case Meta::valTrackNr: return 0; 0223 case Meta::valDiscNr: return 0; 0224 case Meta::valBpm: return 80; 0225 case Meta::valBitrate: return 160; 0226 case Meta::valSamplerate: return 44100; 0227 case Meta::valFilesize: return 10; 0228 case Meta::valScore: return 0; 0229 case Meta::valPlaycount: return 00; 0230 case Meta::valRating: return 0; 0231 case Meta::valLength: return 3 * 60 + 59; 0232 default: return 0; 0233 } 0234 } 0235 0236 MetaQueryWidget::MetaQueryWidget( QWidget* parent, bool onlyNumeric, bool noCondition ) 0237 : QWidget( parent ) 0238 , m_onlyNumeric( onlyNumeric ) 0239 , m_noCondition( noCondition ) 0240 , m_settingFilter( false ) 0241 , m_andLabel(nullptr) 0242 , m_compareSelection(nullptr) 0243 , m_valueSelection1(nullptr) 0244 , m_valueSelection2(nullptr) 0245 { 0246 // note: we are using the strange layout structure because the KRatingWidget size depends on the height. 0247 m_layoutMain = new QVBoxLayout( this ); 0248 m_layoutMain->setContentsMargins(0, 0, 0, 0); 0249 0250 makeFieldSelection(); 0251 m_layoutMain->addWidget( m_fieldSelection ); 0252 0253 m_layoutValue = new QHBoxLayout(); 0254 m_layoutMain->addLayout(m_layoutValue); 0255 0256 m_layoutValueLabels = new QVBoxLayout(); 0257 m_layoutValue->addLayout(m_layoutValueLabels, 0); 0258 m_layoutValueValues = new QVBoxLayout(); 0259 m_layoutValue->addLayout(m_layoutValueValues, 1); 0260 0261 if( m_onlyNumeric ) 0262 m_filter.setField( Meta::valYear ); 0263 else 0264 m_filter.setField( 0 ); 0265 0266 setFilter(m_filter); 0267 } 0268 0269 MetaQueryWidget::~MetaQueryWidget() 0270 { 0271 } 0272 0273 MetaQueryWidget::Filter 0274 MetaQueryWidget::filter() const 0275 { 0276 // special handling for between 0277 if( m_filter.condition == Contains ) 0278 { 0279 Filter f = m_filter; 0280 f.numValue = qMin(m_filter.numValue, m_filter.numValue2) - 1; 0281 f.numValue2 = qMax(m_filter.numValue, m_filter.numValue2) + 1; 0282 } 0283 return m_filter; 0284 } 0285 0286 void 0287 MetaQueryWidget::setFilter( const MetaQueryWidget::Filter &value ) 0288 { 0289 m_settingFilter = true; 0290 m_filter = value; 0291 0292 int index = m_fieldSelection->findData( int(m_filter.field()) ); 0293 m_fieldSelection->setCurrentIndex( index == -1 ? 0 : index ); 0294 0295 if( !m_noCondition ) 0296 makeCompareSelection(); 0297 makeValueSelection(); 0298 setValueSelection(); 0299 0300 m_settingFilter = false; 0301 Q_EMIT changed(m_filter); 0302 } 0303 0304 static void addIconItem( QComboBox *box, qint64 field ) 0305 { 0306 QString icon = Meta::iconForField( field ); 0307 QString text = Meta::i18nForField( field ); 0308 if( icon.isEmpty() ) 0309 box->addItem( text, field ); 0310 else 0311 box->addItem( QIcon::fromTheme( icon ), text, field ); 0312 } 0313 0314 void 0315 MetaQueryWidget::makeFieldSelection() 0316 { 0317 m_fieldSelection = new QComboBox( this ); 0318 if (!m_onlyNumeric) 0319 { 0320 m_fieldSelection->addItem( i18n( "Simple Search" ), 0 ); 0321 addIconItem( m_fieldSelection, Meta::valUrl ); 0322 // note: what about directory? 0323 addIconItem( m_fieldSelection, Meta::valTitle ); 0324 addIconItem( m_fieldSelection, Meta::valArtist ); 0325 addIconItem( m_fieldSelection, Meta::valAlbumArtist ); 0326 addIconItem( m_fieldSelection, Meta::valAlbum ); 0327 addIconItem( m_fieldSelection, Meta::valGenre ); 0328 addIconItem( m_fieldSelection, Meta::valComposer ); 0329 } 0330 addIconItem( m_fieldSelection, Meta::valYear ); 0331 if (!m_onlyNumeric) 0332 addIconItem( m_fieldSelection, Meta::valComment ); 0333 addIconItem( m_fieldSelection, Meta::valTrackNr ); 0334 addIconItem( m_fieldSelection, Meta::valDiscNr ); 0335 addIconItem( m_fieldSelection, Meta::valBpm ); 0336 addIconItem( m_fieldSelection, Meta::valLength ); 0337 addIconItem( m_fieldSelection, Meta::valBitrate ); 0338 addIconItem( m_fieldSelection, Meta::valSamplerate ); 0339 addIconItem( m_fieldSelection, Meta::valFilesize ); 0340 if (!m_onlyNumeric) 0341 addIconItem( m_fieldSelection, Meta::valFormat ); 0342 addIconItem( m_fieldSelection, Meta::valCreateDate ); 0343 addIconItem( m_fieldSelection, Meta::valScore ); 0344 addIconItem( m_fieldSelection, Meta::valRating ); 0345 addIconItem( m_fieldSelection, Meta::valFirstPlayed ); 0346 addIconItem( m_fieldSelection, Meta::valLastPlayed ); 0347 addIconItem( m_fieldSelection, Meta::valPlaycount ); 0348 if (!m_onlyNumeric) 0349 addIconItem( m_fieldSelection, Meta::valLabel ); 0350 addIconItem( m_fieldSelection, Meta::valModified ); 0351 connect( m_fieldSelection, QOverload<int>::of(&QComboBox::currentIndexChanged), 0352 this, &MetaQueryWidget::fieldChanged ); 0353 } 0354 0355 void 0356 MetaQueryWidget::fieldChanged( int i ) 0357 { 0358 if( m_settingFilter ) 0359 return; 0360 0361 qint64 field = 0; 0362 if( i<0 || i>=m_fieldSelection->count() ) 0363 field = m_fieldSelection->itemData( 0 ).toInt(); 0364 else 0365 field = m_fieldSelection->itemData( i ).toInt(); 0366 0367 m_filter.setField( field ); 0368 0369 // in the fieldChanged slot we assume that the field was really changed, 0370 // so we don't have a problem with throwing away all the old widgets 0371 0372 if( !m_noCondition ) 0373 makeCompareSelection(); 0374 makeValueSelection(); 0375 0376 setValueSelection(); 0377 0378 Q_EMIT changed(m_filter); 0379 } 0380 0381 void 0382 MetaQueryWidget::compareChanged( int index ) 0383 { 0384 FilterCondition condition = FilterCondition( m_compareSelection->itemData( index ).toInt() ); 0385 0386 if( m_filter.condition == condition ) 0387 return; // nothing to do 0388 0389 if( m_filter.isDate() ) 0390 { 0391 if( ( condition == OlderThan || condition == NewerThan ) 0392 && m_filter.condition != OlderThan && m_filter.condition != NewerThan 0393 ) 0394 { 0395 // fix some inaccuracies caused by the conversion absolute/relative time specifications 0396 // this is actually just for visual consistency 0397 int unit = 0; 0398 qint64 value = QDateTime::currentDateTimeUtc().toSecsSinceEpoch() - m_filter.numValue; 0399 if( value > 600 || !(value % 60) ) { 0400 unit = 1; 0401 value /= 60; 0402 0403 if( value > 600 || !(value % 60) ) { 0404 unit = 2; 0405 value /= 60; 0406 0407 if( value > 72 || !(value % 24) ) { 0408 unit = 3; 0409 value /= 24; 0410 0411 if( !(value % 365) ) { 0412 unit = 6; 0413 value /= 365; 0414 } else if( !(value % 30) ) { 0415 unit = 5; 0416 value /= 30; 0417 } else if( !(value % 7) ) { 0418 unit = 4; 0419 value /= 7; 0420 } 0421 } 0422 } 0423 } 0424 switch( unit ) 0425 { 0426 case 6: 0427 value *= 365*24*60*60; // years 0428 break; 0429 case 5: 0430 value *= 30*24*60*60; // months 0431 break; 0432 case 4: 0433 value *= 7*24*60*60; // weeks 0434 break; 0435 case 3: 0436 value *= 24*60*60; // days 0437 break; 0438 case 2: 0439 value *= 60*60; // hours 0440 break; 0441 case 1: 0442 value *= 60; // minutes 0443 break; 0444 } 0445 m_filter.numValue = value; 0446 } 0447 else if( condition != OlderThan && condition != NewerThan 0448 && ( m_filter.condition == OlderThan || m_filter.condition == NewerThan ) 0449 ) 0450 { 0451 m_filter.numValue = QDateTime::currentDateTimeUtc().toSecsSinceEpoch() - m_filter.numValue; 0452 } 0453 } 0454 0455 m_filter.condition = condition; 0456 0457 // need to re-generate the value selection fields 0458 makeValueSelection(); 0459 0460 setValueSelection(); 0461 0462 Q_EMIT changed(m_filter); 0463 } 0464 0465 void 0466 MetaQueryWidget::valueChanged( const QString& value ) 0467 { 0468 m_filter.value = value; 0469 0470 Q_EMIT changed(m_filter); 0471 } 0472 0473 void 0474 MetaQueryWidget::numValueChanged( int value ) 0475 { 0476 m_filter.numValue = value; 0477 0478 Q_EMIT changed(m_filter); 0479 } 0480 0481 void 0482 MetaQueryWidget::numValue2Changed( int value ) 0483 { 0484 m_filter.numValue2 = value; 0485 0486 Q_EMIT changed(m_filter); 0487 } 0488 0489 void 0490 MetaQueryWidget::numValueChanged( qint64 value ) 0491 { 0492 m_filter.numValue = value; 0493 0494 Q_EMIT changed(m_filter); 0495 } 0496 0497 void 0498 MetaQueryWidget::numValue2Changed( qint64 value ) 0499 { 0500 m_filter.numValue2 = value; 0501 0502 Q_EMIT changed(m_filter); 0503 } 0504 0505 void 0506 MetaQueryWidget::numValueChanged( const QTime& value ) 0507 { 0508 m_filter.numValue = qAbs( value.secsTo( QTime(0,0,0) ) ); 0509 0510 Q_EMIT changed(m_filter); 0511 } 0512 0513 void 0514 MetaQueryWidget::numValue2Changed( const QTime& value ) 0515 { 0516 m_filter.numValue2 = qAbs( value.secsTo( QTime(0,0,0) ) ); 0517 0518 Q_EMIT changed(m_filter); 0519 } 0520 0521 void 0522 MetaQueryWidget::numValueDateChanged() 0523 { 0524 KDateCombo* dateSelection = qobject_cast<KDateCombo*>( sender() ); 0525 if( dateSelection ) 0526 { 0527 QDate date; 0528 dateSelection->getDate( &date ); 0529 m_filter.numValue = date.startOfDay().toSecsSinceEpoch(); 0530 0531 Q_EMIT changed(m_filter); 0532 } 0533 } 0534 0535 void 0536 MetaQueryWidget::numValue2DateChanged() 0537 { 0538 KDateCombo* dateSelection = qobject_cast<KDateCombo*>( sender() ); 0539 if( dateSelection ) 0540 { 0541 QDate date; 0542 dateSelection->getDate( &date ); 0543 m_filter.numValue2 = date.startOfDay().toSecsSinceEpoch(); 0544 0545 Q_EMIT changed(m_filter); 0546 } 0547 } 0548 0549 void 0550 MetaQueryWidget::numValueTimeDistanceChanged() 0551 { 0552 if( !sender() ) 0553 return; 0554 0555 // static_cast. Remember: the TimeDistanceWidget does not have a Q_OBJECT macro 0556 TimeDistanceWidget* distanceSelection = static_cast<TimeDistanceWidget*>( sender()->parent() ); 0557 if( distanceSelection ) 0558 { 0559 m_filter.numValue = distanceSelection->timeDistance(); 0560 0561 Q_EMIT changed(m_filter); 0562 } 0563 } 0564 0565 void 0566 MetaQueryWidget::numValueFormatChanged(int index) 0567 { 0568 QComboBox* combo = static_cast<QComboBox*>(sender()); 0569 if( combo ) { 0570 m_filter.numValue = combo->itemData( index ).toInt(); 0571 0572 Q_EMIT changed(m_filter); 0573 } 0574 } 0575 0576 void 0577 MetaQueryWidget::setValueSelection() 0578 { 0579 if( m_compareSelection ) 0580 m_layoutValueLabels->addWidget( m_compareSelection ); 0581 0582 if( m_filter.condition == Between ) 0583 { 0584 delete m_andLabel; // delete the old label 0585 m_andLabel = new QLabel( i18n( "and" ), this ); 0586 m_layoutValueLabels->addWidget( m_andLabel ); 0587 } 0588 else 0589 { 0590 delete m_andLabel; 0591 m_andLabel = nullptr; 0592 } 0593 0594 if( m_valueSelection1 ) 0595 m_layoutValueValues->addWidget( m_valueSelection1 ); 0596 0597 if( m_valueSelection2 ) 0598 m_layoutValueValues->addWidget( m_valueSelection2 ); 0599 } 0600 0601 0602 void 0603 MetaQueryWidget::makeCompareSelection() 0604 { 0605 delete m_compareSelection; 0606 m_compareSelection = nullptr; 0607 0608 qint64 field = m_filter.field(); 0609 0610 if( field == Meta::valFormat ) 0611 return; // the field is fixed 0612 0613 else if( isDate(field) ) 0614 { 0615 m_compareSelection = new QComboBox(); 0616 m_compareSelection->addItem( conditionToString( Equals, field ), (int)Equals ); 0617 m_compareSelection->addItem( conditionToString( LessThan, field ), (int)LessThan ); 0618 m_compareSelection->addItem( conditionToString( GreaterThan, field ), (int)GreaterThan ); 0619 m_compareSelection->addItem( conditionToString( Between, field ), (int)Between ); 0620 m_compareSelection->addItem( conditionToString( OlderThan, field ), (int)OlderThan ); 0621 m_compareSelection->addItem( conditionToString( NewerThan, field ), (int)NewerThan ); 0622 } 0623 else if( isNumeric(field) ) 0624 { 0625 m_compareSelection = new QComboBox(); 0626 m_compareSelection->addItem( conditionToString( Equals, field ), (int)Equals ); 0627 m_compareSelection->addItem( conditionToString( LessThan, field ), (int)LessThan ); 0628 m_compareSelection->addItem( conditionToString( GreaterThan, field ), (int)GreaterThan ); 0629 m_compareSelection->addItem( conditionToString( Between, field ), (int)Between ); 0630 } 0631 else 0632 { 0633 m_compareSelection = new QComboBox(); 0634 m_compareSelection->addItem( conditionToString( Contains, field ), (int)Contains ); 0635 m_compareSelection->addItem( conditionToString( Equals, field ), (int)Equals ); 0636 } 0637 0638 // -- select the correct entry (even if the condition is not one of the selection) 0639 int index = m_compareSelection->findData( int(m_filter.condition) ); 0640 if( index == -1 ) 0641 { 0642 index = 0; 0643 m_filter.condition = FilterCondition(m_compareSelection->itemData( index ).toInt()); 0644 compareChanged(index); 0645 } 0646 m_compareSelection->setCurrentIndex( index == -1 ? 0 : index ); 0647 0648 connect( m_compareSelection, QOverload<int>::of(&QComboBox::currentIndexChanged), 0649 this, &MetaQueryWidget::compareChanged ); 0650 } 0651 0652 void 0653 MetaQueryWidget::makeValueSelection() 0654 { 0655 delete m_valueSelection1; 0656 m_valueSelection1 = nullptr; 0657 delete m_valueSelection2; 0658 m_valueSelection2 = nullptr; 0659 0660 qint64 field = m_filter.field(); 0661 if( field == Meta::valUrl ) 0662 makeFilenameSelection(); 0663 else if( field == Meta::valTitle ) 0664 // We,re not going to populate this. There tends to be too many titles. 0665 makeGenericComboSelection( true, nullptr ); 0666 else if( field == Meta::valArtist || 0667 field == Meta::valAlbumArtist || 0668 field == Meta::valAlbum || 0669 field == Meta::valGenre || 0670 field == Meta::valComposer ) 0671 makeMetaComboSelection( field ); 0672 else if( field == Meta::valYear ) 0673 makeGenericNumberSelection( field ); 0674 else if( field == Meta::valComment ) 0675 makeGenericComboSelection( true, nullptr ); 0676 else if( field == Meta::valTrackNr ) 0677 makeGenericNumberSelection( field ); 0678 else if( field == Meta::valDiscNr ) 0679 makeGenericNumberSelection( field ); 0680 else if( field == Meta::valBpm ) 0681 makeGenericNumberSelection( field ); 0682 else if( field == Meta::valLength ) 0683 makeLengthSelection(); 0684 else if( field == Meta::valBitrate ) 0685 makeGenericNumberSelection( field, i18nc("Unit for data rate kilo bit per seconds", "kbps") ); 0686 else if( field == Meta::valSamplerate ) 0687 makeGenericNumberSelection( field, i18nc("Unit for sample rate", "Hz") ); 0688 else if( field == Meta::valFilesize ) 0689 makeGenericNumberSelection( field, i18nc("Unit for file size in mega byte", "MiB") ); 0690 else if( field == Meta::valFormat ) 0691 makeFormatComboSelection(); 0692 else if( field == Meta::valCreateDate ) 0693 makeDateTimeSelection(); 0694 else if( field == Meta::valScore ) 0695 makeGenericNumberSelection( field ); 0696 else if( field == Meta::valRating ) 0697 makeRatingSelection(); 0698 else if( field == Meta::valFirstPlayed ) 0699 makeDateTimeSelection(); 0700 else if( field == Meta::valLastPlayed ) 0701 makeDateTimeSelection(); 0702 else if( field == Meta::valPlaycount ) 0703 makeGenericNumberSelection( field ); 0704 else if( field == Meta::valLabel ) 0705 makeGenericComboSelection( true, nullptr ); 0706 else if( field == Meta::valModified ) 0707 makeDateTimeSelection(); 0708 else // e.g. the simple search 0709 makeGenericComboSelection( true, nullptr ); 0710 } 0711 0712 void 0713 MetaQueryWidget::makeGenericComboSelection( bool editable, Collections::QueryMaker* populateQuery ) 0714 { 0715 KComboBox* combo = new KComboBox( this ); 0716 combo->setEditable( editable ); 0717 0718 if( populateQuery != nullptr ) 0719 { 0720 m_runningQueries.insert(populateQuery, QPointer<KComboBox>(combo)); 0721 connect( populateQuery, &Collections::QueryMaker::newResultReady, 0722 this, &MetaQueryWidget::populateComboBox ); 0723 connect( populateQuery, &Collections::QueryMaker::queryDone, 0724 this, &MetaQueryWidget::comboBoxPopulated ); 0725 0726 populateQuery->run(); 0727 } 0728 combo->setEditText( m_filter.value ); 0729 0730 connect( combo, &KComboBox::editTextChanged, 0731 this, &MetaQueryWidget::valueChanged ); 0732 0733 combo->completionObject()->setIgnoreCase( true ); 0734 combo->setCompletionMode( KCompletion::CompletionPopup ); 0735 combo->setInsertPolicy( KComboBox::InsertAtTop ); 0736 m_valueSelection1 = combo; 0737 } 0738 0739 void 0740 MetaQueryWidget::makeMetaComboSelection( qint64 field ) 0741 { 0742 Collections::QueryMaker* qm = CollectionManager::instance()->queryMaker(); 0743 qm->setQueryType( Collections::QueryMaker::Custom ); 0744 qm->addReturnValue( field ); 0745 qm->setAutoDelete( true ); 0746 makeGenericComboSelection( true, qm ); 0747 } 0748 0749 void 0750 MetaQueryWidget::populateComboBox( const QStringList &results ) 0751 { 0752 QObject* query = sender(); 0753 if( !query ) 0754 return; 0755 0756 QPointer<KComboBox> combo = m_runningQueries.value(query); 0757 if( combo.isNull() ) 0758 return; 0759 0760 // note: adding items seems to reset the edit text, so we have 0761 // to take care of that. 0762 disconnect( combo.data(), nullptr, this, nullptr ); 0763 0764 // want the results unique and sorted 0765 const QSet<QString> dataSet(results.begin(), results.end()); 0766 QStringList dataList = dataSet.values(); 0767 dataList.sort(); 0768 combo->addItems( dataList ); 0769 0770 KCompletion* comp = combo->completionObject(); 0771 comp->setItems( dataList ); 0772 0773 // reset the text and re-enable the signal 0774 combo.data()->setEditText( m_filter.value ); 0775 connect( combo.data(), &QComboBox::editTextChanged, 0776 this, &MetaQueryWidget::valueChanged ); 0777 } 0778 0779 void 0780 MetaQueryWidget::makeFormatComboSelection() 0781 { 0782 QComboBox* combo = new QComboBox( this ); 0783 combo->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred ); 0784 QStringList filetypes = Amarok::FileTypeSupport::possibleFileTypes(); 0785 for (int listpos=0;listpos<filetypes.size();listpos++) 0786 { 0787 combo->addItem(filetypes.at(listpos),listpos); 0788 } 0789 0790 int index = m_fieldSelection->findData( (int)m_filter.numValue ); 0791 combo->setCurrentIndex( index == -1 ? 0 : index ); 0792 0793 connect( combo, QOverload<int>::of(&QComboBox::currentIndexChanged), 0794 this, &MetaQueryWidget::numValueFormatChanged ); 0795 0796 m_valueSelection1 = combo; 0797 } 0798 0799 void 0800 MetaQueryWidget::comboBoxPopulated() 0801 { 0802 QObject* query = sender(); 0803 if( !query ) 0804 return; 0805 0806 m_runningQueries.remove( query ); 0807 } 0808 0809 void 0810 MetaQueryWidget::makeFilenameSelection() 0811 { 0812 // Don't populate the combobox. Too many urls. 0813 makeGenericComboSelection( true, nullptr ); 0814 } 0815 0816 0817 void 0818 MetaQueryWidget::makeRatingSelection() 0819 { 0820 KRatingWidget* ratingWidget = new KRatingWidget(); 0821 ratingWidget->setRating( (int)m_filter.numValue ); 0822 connect( ratingWidget, QOverload<int>::of(&KRatingWidget::ratingChanged), 0823 this, QOverload<int>::of(&MetaQueryWidget::numValueChanged) ); 0824 0825 m_valueSelection1 = ratingWidget; 0826 0827 if( m_filter.condition != Between ) 0828 return; 0829 0830 // second KRatingWidget for the between selection 0831 KRatingWidget* ratingWidget2 = new KRatingWidget(); 0832 ratingWidget2->setRating( (int)m_filter.numValue2 ); 0833 connect( ratingWidget2, QOverload<int>::of(&KRatingWidget::ratingChanged), 0834 this, QOverload<int>::of(&MetaQueryWidget::numValue2Changed) ); 0835 0836 m_valueSelection2 = ratingWidget2; 0837 } 0838 0839 0840 void 0841 MetaQueryWidget::makeLengthSelection() 0842 { 0843 QString displayFormat = i18nc( "time format for specifying track length - hours, minutes, seconds", "h:m:ss" ); 0844 QTimeEdit* timeSpin = new QTimeEdit(); 0845 timeSpin->setDisplayFormat( displayFormat ); 0846 timeSpin->setMinimumTime( QTime( 0, 0, 0 ) ); 0847 timeSpin->setMaximumTime( QTime( maxHours - 1, 59, 59 ) ); 0848 timeSpin->setTime( QTime(0, 0, 0).addSecs( m_filter.numValue ) ); 0849 0850 connect( timeSpin, &QTimeEdit::timeChanged, 0851 this, QOverload<const QTime&>::of(&MetaQueryWidget::numValueChanged) ); 0852 0853 m_valueSelection1 = timeSpin; 0854 0855 if( m_filter.condition != Between ) 0856 return; 0857 0858 QTimeEdit* timeSpin2 = new QTimeEdit(); 0859 timeSpin2->setDisplayFormat( displayFormat ); 0860 timeSpin2->setMinimumTime( QTime( 0, 0, 0 ) ); 0861 timeSpin2->setMaximumTime( QTime( maxHours - 1, 59, 59 ) ); 0862 timeSpin2->setTime( QTime(0, 0, 0).addSecs( m_filter.numValue2 ) ); 0863 0864 connect( timeSpin2, &QTimeEdit::timeChanged, 0865 this, QOverload<const QTime&>::of(&MetaQueryWidget::numValue2Changed) ); 0866 0867 m_valueSelection2 = timeSpin2; 0868 } 0869 0870 void 0871 MetaQueryWidget::makeGenericNumberSelection( qint64 field, const QString& unit ) 0872 { 0873 QSpinBox* spin = new QSpinBox(); 0874 spin->setMinimum( Filter::minimumValue( field ) ); 0875 spin->setMaximum( Filter::maximumValue( field ) ); 0876 if( !unit.isEmpty() ) 0877 spin->setSuffix( ' ' + unit ); 0878 spin->setValue( m_filter.numValue ); 0879 0880 connect( spin, QOverload<int>::of(&QSpinBox::valueChanged), 0881 this, QOverload<int>::of(&MetaQueryWidget::numValueChanged) ); 0882 0883 m_valueSelection1 = spin; 0884 0885 if( m_filter.condition != Between ) 0886 return; 0887 0888 // second spin box for the between selection 0889 QSpinBox* spin2 = new QSpinBox(); 0890 spin2->setMinimum( Filter::minimumValue( field ) ); 0891 spin2->setMaximum( Filter::maximumValue( field ) ); 0892 if( !unit.isEmpty() ) 0893 spin2->setSuffix( ' ' + unit ); 0894 spin2->setValue( m_filter.numValue2 ); 0895 0896 connect( spin2, QOverload<int>::of(&QSpinBox::valueChanged), 0897 this, QOverload<int>::of(&MetaQueryWidget::numValue2Changed) ); 0898 0899 m_valueSelection2 = spin2; 0900 } 0901 0902 0903 void 0904 MetaQueryWidget::makeDateTimeSelection() 0905 { 0906 if( m_filter.condition == OlderThan || m_filter.condition == NewerThan ) 0907 { 0908 TimeDistanceWidget* distanceSelection = new TimeDistanceWidget(); 0909 distanceSelection->setTimeDistance( m_filter.numValue ); 0910 0911 distanceSelection->connectChanged( this, &MetaQueryWidget::numValueTimeDistanceChanged); 0912 0913 m_valueSelection1 = distanceSelection; 0914 } 0915 else 0916 { 0917 KDateCombo* dateSelection = new KDateCombo(); 0918 QDateTime dt; 0919 // if( m_filter.condition == Contains || m_filter.condition == Equals ) 0920 // dt = QDateTime::currentDateTime(); 0921 // else 0922 // dt.setSecsSinceEpoch( m_filter.numValue ); 0923 dt.setSecsSinceEpoch( m_filter.numValue ); 0924 dateSelection->setDate( dt.date() ); 0925 0926 connect( dateSelection, QOverload<int>::of(&KDateCombo::currentIndexChanged), 0927 this, &MetaQueryWidget::numValueDateChanged ); 0928 0929 m_valueSelection1 = dateSelection; 0930 0931 if( m_filter.condition != Between ) 0932 return; 0933 0934 // second KDateCombo for the between selection 0935 KDateCombo* dateSelection2 = new KDateCombo(); 0936 dt.setSecsSinceEpoch( m_filter.numValue2 ); 0937 dateSelection2->setDate( dt.date() ); 0938 0939 connect( dateSelection2, QOverload<int>::of(&KDateCombo::currentIndexChanged), 0940 this, &MetaQueryWidget::numValue2DateChanged ); 0941 0942 m_valueSelection2 = dateSelection2; 0943 } 0944 } 0945 0946 0947 bool 0948 MetaQueryWidget::isNumeric( qint64 field ) 0949 { 0950 switch( field ) 0951 { 0952 case Meta::valYear: 0953 case Meta::valTrackNr: 0954 case Meta::valDiscNr: 0955 case Meta::valBpm: 0956 case Meta::valLength: 0957 case Meta::valBitrate: 0958 case Meta::valSamplerate: 0959 case Meta::valFilesize: 0960 case Meta::valFormat: 0961 case Meta::valCreateDate: 0962 case Meta::valScore: 0963 case Meta::valRating: 0964 case Meta::valFirstPlayed: 0965 case Meta::valLastPlayed: 0966 case Meta::valPlaycount: 0967 case Meta::valModified: 0968 return true; 0969 default: 0970 return false; 0971 } 0972 } 0973 0974 bool 0975 MetaQueryWidget::isDate( qint64 field ) 0976 { 0977 switch( field ) 0978 { 0979 case Meta::valCreateDate: 0980 case Meta::valFirstPlayed: 0981 case Meta::valLastPlayed: 0982 case Meta::valModified: 0983 return true; 0984 default: 0985 return false; 0986 } 0987 } 0988 0989 QString 0990 MetaQueryWidget::conditionToString( FilterCondition condition, qint64 field ) 0991 { 0992 if( isDate(field) ) 0993 { 0994 switch( condition ) 0995 { 0996 case LessThan: 0997 return i18nc( "The date lies before the given fixed date", "before" ); 0998 case Equals: 0999 return i18nc( "The date is the same as the given fixed date", "on" ); 1000 case GreaterThan: 1001 return i18nc( "The date is after the given fixed date", "after" ); 1002 case Between: 1003 return i18nc( "The date is between the given fixed dates", "between" ); 1004 case OlderThan: 1005 return i18nc( "The date lies before the given time interval", "older than" ); 1006 case NewerThan: 1007 return i18nc( "The date lies after the given time interval", "newer than" ); 1008 default: 1009 ; // fall through 1010 } 1011 } 1012 else if( isNumeric(field) ) 1013 { 1014 switch( condition ) 1015 { 1016 case LessThan: 1017 return i18n("less than"); 1018 case Equals: 1019 return i18nc("a numerical tag (like year or track number) equals a value","equals"); 1020 case GreaterThan: 1021 return i18n("greater than"); 1022 case Between: 1023 return i18nc( "a numerical tag (like year or track number) is between two values", "between" ); 1024 default: 1025 ; // fall through 1026 } 1027 } 1028 else 1029 { 1030 switch( condition ) 1031 { 1032 case Equals: 1033 return i18nc("an alphabetical tag (like title or artist name) equals some string","equals"); 1034 case Contains: 1035 return i18nc("an alphabetical tag (like title or artist name) contains some string", "contains"); 1036 default: 1037 ; // fall through 1038 } 1039 } 1040 return i18n("unknown comparison"); 1041 } 1042 1043 QString 1044 MetaQueryWidget::Filter::fieldToString() const 1045 { 1046 return Meta::shortI18nForField( m_field ); 1047 } 1048 1049 QString MetaQueryWidget::Filter::toString( bool invert ) const 1050 { 1051 // this member is called when there is a keyword that needs numeric attributes 1052 QString strValue1 = value; 1053 QString strValue2 = value; 1054 1055 if( m_field == Meta::valFormat ) 1056 { 1057 strValue1 = Amarok::FileTypeSupport::toString( Amarok::FileType( numValue )); 1058 } 1059 else if( m_field == Meta::valRating ) 1060 { 1061 strValue1 = QString::number( (float)numValue / 2 ); 1062 strValue2 = QString::number( (float)numValue2 / 2 ); 1063 } 1064 else if( isDate() ) 1065 { 1066 if( condition == OlderThan || condition == NewerThan ) 1067 { 1068 strValue1 = QString::number( numValue ); 1069 strValue2 = QString::number( numValue2 ); 1070 } 1071 else 1072 { 1073 strValue1 = QLocale().toString( QDateTime::fromSecsSinceEpoch(numValue).date(), QLocale::ShortFormat ); 1074 strValue2 = QLocale().toString( QDateTime::fromSecsSinceEpoch(numValue2).date(), QLocale::ShortFormat ); 1075 } 1076 } 1077 else if( isNumeric() ) 1078 { 1079 if ( condition != Between ) 1080 { 1081 strValue1 = QString::number( numValue ); 1082 } 1083 else if (numValue < numValue2) // two values are only used for "between". We want to order them by size 1084 { 1085 strValue1 = QString::number( numValue ); 1086 strValue2 = QString::number( numValue2 ); 1087 } 1088 else 1089 { 1090 strValue1 = QString::number( numValue2 ); 1091 strValue2 = QString::number( numValue ); 1092 } 1093 } 1094 1095 QString result; 1096 if( m_field ) 1097 result = fieldToString() + ':'; 1098 1099 switch( condition ) 1100 { 1101 case Equals: 1102 { 1103 if( isNumeric() ) 1104 result += strValue1; 1105 else 1106 result += '=' + QString( "\"%1\"" ).arg( value ); 1107 if( invert ) 1108 result.prepend( QChar('-') ); 1109 break; 1110 } 1111 1112 case GreaterThan: 1113 { 1114 result += '>' + strValue1; 1115 if( invert ) 1116 result.prepend( QChar('-') ); 1117 break; 1118 } 1119 1120 case LessThan: 1121 { 1122 result +='<' + strValue1; 1123 if( invert ) 1124 result.prepend( QChar('-') ); 1125 break; 1126 } 1127 1128 case Between: 1129 { 1130 if( invert ) 1131 result = QString( "%1<%2 OR %1>%3" ).arg( result, strValue1, strValue2 ); 1132 else 1133 result = QString( "%1>%2 AND %1<%3" ).arg( result, strValue1, strValue2 ); 1134 break; 1135 } 1136 1137 case OlderThan: 1138 case NewerThan: 1139 { 1140 // a human readable time.. 1141 QChar strUnit = 's'; 1142 qint64 value = numValue; 1143 if( !(value % 60) ) { 1144 strUnit = 'M'; 1145 value /= 60; 1146 1147 if( !(value % 60) ) { 1148 strUnit = 'h'; 1149 value /= 60; 1150 1151 if( !(value % 24) ) { 1152 strUnit = 'd'; 1153 value /= 24; 1154 1155 if( !(value % 365) ) { 1156 strUnit = 'y'; 1157 value /= 365; 1158 } else if( !(value % 30) ) { 1159 strUnit = 'm'; 1160 value /= 30; 1161 } else if( !(value % 7) ) { 1162 strUnit = 'w'; 1163 value /= 7; 1164 } 1165 } 1166 } 1167 } 1168 1169 if( condition == OlderThan ) 1170 result += '>' + QString::number(value) + strUnit; 1171 else 1172 result += '<' + QString::number(value) + strUnit; 1173 if( invert ) 1174 result.prepend( QChar('-') ); 1175 break; 1176 } 1177 1178 case Contains: 1179 { 1180 result += QString( "\"%1\"" ).arg( value ); 1181 if( invert ) 1182 result.prepend( QChar('-') ); 1183 break; 1184 } 1185 } 1186 1187 return result; 1188 } 1189 1190 bool 1191 MetaQueryWidget::isFieldSelectorHidden() const 1192 { 1193 return m_fieldSelection->isHidden(); 1194 } 1195 1196 void 1197 MetaQueryWidget::setFieldSelectorHidden( const bool hidden ) 1198 { 1199 m_fieldSelection->setVisible( !hidden ); 1200 } 1201 1202 void 1203 MetaQueryWidget::setField( const qint64 field ) 1204 { 1205 int index = m_fieldSelection->findData( field ); 1206 m_fieldSelection->setCurrentIndex( index == -1 ? 0 : index ); 1207 }