File indexing completed on 2024-05-19 04:49:57
0001 /**************************************************************************************** 0002 * Copyright (c) 2008-2012 Soren Harward <stharward@gmail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "Constraint::TagMatch" 0018 0019 #include "TagMatch.h" 0020 0021 #include "playlistgenerator/Constraint.h" 0022 #include "playlistgenerator/ConstraintFactory.h" 0023 0024 #include "core/collections/QueryMaker.h" 0025 #include "core/meta/Meta.h" 0026 #include "core/meta/Statistics.h" 0027 #include "core/support/Debug.h" 0028 0029 #include <QRandomGenerator> 0030 0031 #include <cmath> 0032 #include <cstdlib> 0033 0034 Constraint* 0035 ConstraintTypes::TagMatch::createFromXml( QDomElement& xmlelem, ConstraintNode* p ) 0036 { 0037 if ( p ) 0038 return new TagMatch( xmlelem, p ); 0039 else 0040 return nullptr; 0041 } 0042 0043 Constraint* 0044 ConstraintTypes::TagMatch::createNew( ConstraintNode* p ) 0045 { 0046 if ( p ) 0047 return new TagMatch( p ); 0048 else 0049 return nullptr; 0050 } 0051 0052 ConstraintFactoryEntry* 0053 ConstraintTypes::TagMatch::registerMe() 0054 { 0055 return new ConstraintFactoryEntry( QStringLiteral("TagMatch"), 0056 i18n("Match Tags"), 0057 i18n("Make all tracks in the playlist match the specified characteristic"), 0058 &TagMatch::createFromXml, &TagMatch::createNew ); 0059 } 0060 0061 ConstraintTypes::TagMatch::TagMatch( QDomElement& xmlelem, ConstraintNode* p ) 0062 : MatchingConstraint( p ) 0063 , m_comparer( new Comparer() ) 0064 , m_fieldsModel( new TagMatchFieldsModel() ) 0065 { 0066 QDomAttr a; 0067 0068 a = xmlelem.attributeNode( QStringLiteral("field") ); 0069 if ( !a.isNull() ) { 0070 if ( m_fieldsModel->contains( a.value() ) ) 0071 m_field = a.value(); 0072 } 0073 0074 a = xmlelem.attributeNode( QStringLiteral("comparison") ); 0075 if ( !a.isNull() ) { 0076 m_comparison = a.value().toInt(); 0077 } 0078 0079 a = xmlelem.attributeNode( QStringLiteral("value") ); 0080 if ( !a.isNull() ) { 0081 if ( m_fieldsModel->type_of( m_field ) == FieldTypeInt ) { 0082 m_value = a.value().toInt(); 0083 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) { 0084 if ( m_comparison == CompareDateWithin ) { 0085 QStringList parts = a.value().split(' '); 0086 if ( parts.size() == 2 ) { 0087 int u = parts.at( 0 ).toInt(); 0088 int v = 0; 0089 if ( parts.at( 1 ) == QLatin1String("months") ) 0090 v = 1; 0091 else if ( parts.at( 1 ) == QLatin1String("years") ) 0092 v = 2; 0093 m_value = QVariant::fromValue( DateRange( u, v ) ); 0094 } else 0095 m_value = QVariant::fromValue( DateRange( 0, 0 ) ); 0096 } else 0097 m_value = QDate::fromString( a.value(), Qt::ISODate ); 0098 } else { // String type 0099 m_value = a.value(); 0100 } 0101 } 0102 0103 a = xmlelem.attributeNode( QStringLiteral("invert") ); 0104 if ( !a.isNull() && a.value() == QLatin1String("true") ) 0105 m_invert = true; 0106 else 0107 m_invert = false; 0108 0109 a = xmlelem.attributeNode( QStringLiteral("strictness") ); 0110 if ( !a.isNull() ) 0111 m_strictness = a.value().toDouble(); 0112 } 0113 0114 ConstraintTypes::TagMatch::TagMatch( ConstraintNode* p ) 0115 : MatchingConstraint( p ) 0116 , m_comparison( CompareStrEquals ) 0117 , m_field( QStringLiteral("title") ) 0118 , m_invert( false ) 0119 , m_strictness( 1.0 ) 0120 , m_value() 0121 , m_comparer( new Comparer() ) 0122 , m_fieldsModel( new TagMatchFieldsModel() ) 0123 { 0124 } 0125 0126 ConstraintTypes::TagMatch::~TagMatch() 0127 { 0128 delete m_comparer; 0129 delete m_fieldsModel; 0130 } 0131 0132 QWidget* 0133 ConstraintTypes::TagMatch::editWidget() const 0134 { 0135 TagMatchEditWidget* e = new TagMatchEditWidget( 0136 m_comparison, 0137 m_field, 0138 m_invert, 0139 static_cast<int>( m_strictness * 10 ), 0140 m_value ); 0141 connect( e, &TagMatchEditWidget::comparisonChanged, this, &TagMatch::setComparison ); 0142 connect( e, &TagMatchEditWidget::fieldChanged, this, &TagMatch::setField ); 0143 connect( e, &TagMatchEditWidget::invertChanged, this, &TagMatch::setInvert ); 0144 connect( e, &TagMatchEditWidget::strictnessChanged, this, &TagMatch::setStrictness ); 0145 connect( e, &TagMatchEditWidget::valueChanged, this, &TagMatch::setValue ); 0146 return e; 0147 } 0148 0149 void 0150 ConstraintTypes::TagMatch::toXml( QDomDocument& doc, QDomElement& elem ) const 0151 { 0152 QDomElement c = doc.createElement( QStringLiteral("constraint") ); 0153 0154 c.setAttribute( QStringLiteral("type"), QStringLiteral("TagMatch") ); 0155 c.setAttribute( QStringLiteral("field"), m_field ); 0156 c.setAttribute( QStringLiteral("comparison"), m_comparison ); 0157 c.setAttribute( QStringLiteral("value"), valueToString() ); 0158 0159 if ( m_invert ) 0160 c.setAttribute( QStringLiteral("invert"), QStringLiteral("true") ); 0161 else 0162 c.setAttribute( QStringLiteral("invert"), QStringLiteral("false") ); 0163 0164 c.setAttribute( QStringLiteral("strictness"), QString::number( m_strictness ) ); 0165 0166 elem.appendChild( c ); 0167 } 0168 0169 QString 0170 ConstraintTypes::TagMatch::getName() const 0171 { 0172 KLocalizedString v( ki18nc( "%1 = empty string or \"not\"; " 0173 "%2 = a metadata field, like \"title\" or \"artist name\"; " 0174 "%3 = a predicate, can be equals, starts with, ends with or contains; " 0175 "%4 = a string to match; " 0176 "Example: Match tag: not title contains \"foo\"", "Match tag:%1 %2 %3 %4") ); 0177 v = v.subs( m_invert ? i18n(" not") : QLatin1String("") ); 0178 v = v.subs( m_fieldsModel->pretty_name_of( m_field ) ); 0179 v = v.subs( comparisonToString() ); 0180 if ( m_field == QLatin1String("rating") ) { 0181 double r = m_value.toDouble() / 2.0; 0182 v = v.subs( i18ncp("number of stars in the rating of a track", "%1 star", "%1 stars", r) ); 0183 } else if ( m_field == QLatin1String("length") ) { 0184 v = v.subs( QTime(0, 0, 0).addMSecs( m_value.toInt() ).toString( QStringLiteral("H:mm:ss") ) ); 0185 } else { 0186 if ( m_fieldsModel->type_of( m_field ) == FieldTypeString ) { 0187 // put quotes around any strings (eg, track title or artist name) ... 0188 QString s = i18nc("an arbitrary string surrounded by quotes", "\"%1\"", valueToString() ); 0189 v = v.subs( s ); 0190 } else { 0191 // ... but don't quote put quotes around anything else 0192 v = v.subs( valueToString() ); 0193 } 0194 } 0195 return v.toString(); 0196 } 0197 0198 Collections::QueryMaker* 0199 ConstraintTypes::TagMatch::initQueryMaker( Collections::QueryMaker* qm ) const 0200 { 0201 if ( ( m_fieldsModel->type_of( m_field ) == FieldTypeInt ) ) { 0202 int v = m_value.toInt(); 0203 int range = static_cast<int>( m_comparer->rangeNum( m_strictness, m_fieldsModel->meta_value_of( m_field ) ) ); 0204 if ( m_comparison == CompareNumEquals ) { 0205 if ( !m_invert ) { 0206 if ( m_strictness < 0.99 ) { // fuzzy approximation of "1.0" 0207 qm->beginAnd(); 0208 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v - range, Collections::QueryMaker::GreaterThan ); 0209 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v + range, Collections::QueryMaker::LessThan ); 0210 qm->endAndOr(); 0211 } else { 0212 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v, Collections::QueryMaker::Equals ); 0213 } 0214 } else { 0215 if ( m_strictness > 0.99 ) { 0216 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), v, Collections::QueryMaker::Equals ); 0217 } 0218 } 0219 } else if ( m_comparison == CompareNumGreaterThan ) { 0220 if ( m_invert ) 0221 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), v + range, Collections::QueryMaker::GreaterThan ); 0222 else 0223 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v - range, Collections::QueryMaker::GreaterThan ); 0224 } else if ( m_comparison == CompareNumLessThan ) { 0225 if ( m_invert ) 0226 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), v - range, Collections::QueryMaker::LessThan ); 0227 else 0228 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v + range, Collections::QueryMaker::LessThan ); 0229 } 0230 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) { 0231 uint referenceDate = 0; 0232 int range = m_comparer->rangeDate( m_strictness ); 0233 if ( m_comparison == CompareDateBefore ) { 0234 referenceDate = m_value.toDateTime().toSecsSinceEpoch(); 0235 if ( m_invert ) 0236 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::LessThan ); 0237 else 0238 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::LessThan ); 0239 } else if ( m_comparison == CompareDateOn ) { 0240 referenceDate = m_value.toDateTime().toSecsSinceEpoch(); 0241 if ( !m_invert ) { 0242 qm->beginAnd(); 0243 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::GreaterThan ); 0244 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::LessThan ); 0245 qm->endAndOr(); 0246 } 0247 } else if ( m_comparison == CompareDateAfter ) { 0248 referenceDate = m_value.toDateTime().toSecsSinceEpoch(); 0249 if ( m_invert ) 0250 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::GreaterThan ); 0251 else 0252 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::GreaterThan ); 0253 } else if ( m_comparison == CompareDateWithin ) { 0254 QDateTime now = QDateTime::currentDateTime(); 0255 DateRange r = m_value.value<DateRange>(); 0256 switch ( r.second ) { 0257 case 0: 0258 referenceDate = now.addDays( -1 * r.first ).toSecsSinceEpoch(); 0259 break; 0260 case 1: 0261 referenceDate = now.addMonths( -1 * r.first ).toSecsSinceEpoch(); 0262 break; 0263 case 2: 0264 referenceDate = now.addYears( -1 * r.first ).toSecsSinceEpoch(); 0265 break; 0266 default: 0267 break; 0268 } 0269 if ( m_invert ) 0270 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::GreaterThan ); 0271 else 0272 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::GreaterThan ); 0273 } 0274 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeString ) { 0275 if ( m_comparison == CompareStrEquals ) { 0276 if ( m_invert ) 0277 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, true ); 0278 else 0279 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, true ); 0280 } else if ( m_comparison == CompareStrStartsWith ) { 0281 if ( m_invert ) 0282 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, false ); 0283 else 0284 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, false ); 0285 } else if ( m_comparison == CompareStrEndsWith ) { 0286 if ( m_invert ) 0287 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, true ); 0288 else 0289 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, true ); 0290 } else if ( m_comparison == CompareStrContains ) { 0291 if ( m_invert ) 0292 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, false ); 0293 else 0294 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, false ); 0295 } 0296 // TODO: regexp 0297 } else { 0298 error() << "TagMatch cannot initialize QM for unknown type"; 0299 } 0300 0301 return qm; 0302 } 0303 0304 double 0305 ConstraintTypes::TagMatch::satisfaction( const Meta::TrackList& tl ) const 0306 { 0307 double satisfaction = 0.0; 0308 foreach( Meta::TrackPtr t, tl ) { 0309 if ( matches( t ) ) { 0310 satisfaction += 1.0; 0311 } 0312 } 0313 satisfaction /= ( double )tl.size(); 0314 return satisfaction; 0315 } 0316 0317 const QBitArray 0318 ConstraintTypes::TagMatch::whatTracksMatch( const Meta::TrackList& tl ) 0319 { 0320 QBitArray match = QBitArray( tl.size() ); 0321 for ( int i = 0; i < tl.size(); i++ ) { 0322 if ( matches( tl.at( i ) ) ) 0323 match.setBit( i, true ); 0324 } 0325 return match; 0326 } 0327 0328 int 0329 ConstraintTypes::TagMatch::constraintMatchType() const 0330 { 0331 return ( 0 << 28 ) + m_fieldsModel->index_of( m_field ); 0332 } 0333 0334 0335 QString 0336 ConstraintTypes::TagMatch::comparisonToString() const 0337 { 0338 if ( m_fieldsModel->type_of( m_field ) == FieldTypeInt ) { 0339 if ( m_comparison == CompareNumEquals ) { 0340 return i18nc("a numerical tag (like year or track number) equals a value","equals"); 0341 } else if ( m_comparison == CompareNumGreaterThan ) { 0342 return i18n("greater than"); 0343 } else if ( m_comparison == CompareNumLessThan ) { 0344 return i18n("less than"); 0345 } 0346 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) { 0347 if ( m_comparison == CompareDateBefore ) { 0348 return i18n("before"); 0349 } else if ( m_comparison == CompareDateOn ) { 0350 return i18n("on"); 0351 } else if ( m_comparison == CompareDateAfter ) { 0352 return i18n("after"); 0353 } else if ( m_comparison == CompareDateWithin ) { 0354 return i18n("within"); 0355 } 0356 } else { 0357 if ( m_comparison == CompareStrEquals ) { 0358 return i18nc("an alphabetical tag (like title or artist name) equals some string","equals"); 0359 } else if ( m_comparison == CompareStrStartsWith ) { 0360 return i18nc("an alphabetical tag (like title or artist name) starts with some string","starts with"); 0361 } else if ( m_comparison == CompareStrEndsWith ) { 0362 return i18nc("an alphabetical tag (like title or artist name) ends with some string","ends with"); 0363 } else if ( m_comparison == CompareStrContains ) { 0364 return i18nc("an alphabetical tag (like title or artist name) contains some string","contains"); 0365 } else if ( m_comparison == CompareStrRegExp ) { 0366 return i18n("regexp"); 0367 } 0368 } 0369 return i18n("unknown comparison"); 0370 } 0371 0372 QString 0373 ConstraintTypes::TagMatch::valueToString() const 0374 { 0375 if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) { 0376 if ( m_comparison != CompareDateWithin ) { 0377 return m_value.toDate().toString( Qt::ISODate ); 0378 } else { 0379 KLocalizedString unit; 0380 switch ( m_value.value<DateRange>().second ) { 0381 case 0: 0382 unit = ki18np("%1 day", "%1 days"); 0383 break; 0384 case 1: 0385 unit = ki18np("%1 month", "%1 months"); 0386 break; 0387 case 2: 0388 unit = ki18np("%1 year", "%1 years"); 0389 break; 0390 default: 0391 break; 0392 } 0393 return unit.subs( m_value.value<DateRange>().first ).toString(); 0394 } 0395 } else { 0396 return m_value.toString(); 0397 } 0398 } 0399 0400 bool 0401 ConstraintTypes::TagMatch::matches( Meta::TrackPtr track ) const 0402 { 0403 if ( !m_matchCache.contains( track ) ) { 0404 double v = 0.0; 0405 qint64 fmv = m_fieldsModel->meta_value_of( m_field ); 0406 switch ( fmv ) { 0407 case Meta::valUrl: 0408 v = m_comparer->compareStr( track->prettyUrl(), m_comparison, m_value.toString() ); 0409 break; 0410 case Meta::valTitle: 0411 v = m_comparer->compareStr( track->prettyName(), m_comparison, m_value.toString() ); 0412 break; 0413 case Meta::valArtist: 0414 v = m_comparer->compareStr( track->artist()->prettyName(), m_comparison, m_value.toString() ); 0415 break; 0416 case Meta::valAlbum: 0417 v = m_comparer->compareStr( track->album()->prettyName(), m_comparison, m_value.toString() ); 0418 break; 0419 case Meta::valGenre: 0420 v = m_comparer->compareStr( track->genre()->prettyName(), m_comparison, m_value.toString() ); 0421 break; 0422 case Meta::valComposer: 0423 v = m_comparer->compareStr( track->composer()->prettyName(), m_comparison, m_value.toString() ); 0424 break; 0425 case Meta::valYear: 0426 v = m_comparer->compareNum( track->year()->prettyName().toInt(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0427 break; 0428 case Meta::valComment: 0429 v = m_comparer->compareStr( track->comment(), m_comparison, m_value.toString() ); 0430 break; 0431 case Meta::valTrackNr: 0432 v = m_comparer->compareNum( track->trackNumber(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0433 break; 0434 case Meta::valDiscNr: 0435 v = m_comparer->compareNum( track->discNumber(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0436 break; 0437 case Meta::valLength: 0438 v = m_comparer->compareNum( track->length(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0439 break; 0440 case Meta::valBitrate: 0441 v = m_comparer->compareNum( track->bitrate(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0442 break; 0443 case Meta::valFilesize: 0444 v = m_comparer->compareNum( track->filesize(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0445 break; 0446 case Meta::valCreateDate: 0447 v = m_comparer->compareDate( track->createDate().toSecsSinceEpoch(), m_comparison, m_value, m_strictness ); 0448 break; 0449 case Meta::valScore: 0450 v = m_comparer->compareNum( track->statistics()->score(), m_comparison, m_value.toDouble(), m_strictness, fmv ); 0451 break; 0452 case Meta::valRating: 0453 v = m_comparer->compareNum( track->statistics()->rating(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0454 break; 0455 case Meta::valFirstPlayed: 0456 v = m_comparer->compareDate( track->statistics()->firstPlayed().toSecsSinceEpoch(), m_comparison, m_value, m_strictness ); 0457 break; 0458 case Meta::valLastPlayed: 0459 v = m_comparer->compareDate( track->statistics()->lastPlayed().toSecsSinceEpoch(), m_comparison, m_value, m_strictness ); 0460 break; 0461 case Meta::valPlaycount: 0462 v = m_comparer->compareNum( track->statistics()->playCount(), m_comparison, m_value.toInt(), m_strictness, fmv ); 0463 break; 0464 case Meta::valLabel: 0465 v = m_comparer->compareLabels( track, m_comparison, m_value.toString() ); 0466 break; 0467 default: 0468 v = 0.0; 0469 break; 0470 } 0471 if ( m_invert ) 0472 v = 1.0 - v; 0473 0474 m_matchCache.insert( track, ( v > ( (double)QRandomGenerator::global()->generate() / (double)RAND_MAX ) ) ); 0475 } 0476 return m_matchCache.value( track ); 0477 } 0478 0479 void 0480 ConstraintTypes::TagMatch::setComparison( int c ) 0481 { 0482 m_comparison = c; 0483 m_matchCache.clear(); 0484 Q_EMIT dataChanged(); 0485 } 0486 0487 void 0488 ConstraintTypes::TagMatch::setField( const QString& s ) 0489 { 0490 m_field = s; 0491 m_matchCache.clear(); 0492 Q_EMIT dataChanged(); 0493 } 0494 0495 void 0496 ConstraintTypes::TagMatch::setInvert( bool v ) 0497 { 0498 if ( m_invert != v ) { 0499 foreach( const Meta::TrackPtr t, m_matchCache.keys() ) { 0500 m_matchCache.insert( t, !m_matchCache.value( t ) ); 0501 } 0502 } 0503 m_invert = v; 0504 Q_EMIT dataChanged(); 0505 } 0506 0507 void 0508 ConstraintTypes::TagMatch::setStrictness( int v ) 0509 { 0510 m_strictness = static_cast<double>( v ) / 10.0; 0511 m_matchCache.clear(); 0512 } 0513 0514 void 0515 ConstraintTypes::TagMatch::setValue( const QVariant& v ) 0516 { 0517 m_value = v; 0518 m_matchCache.clear(); 0519 Q_EMIT dataChanged(); 0520 } 0521 0522 /****************************** 0523 * Edit Widget * 0524 ******************************/ 0525 0526 ConstraintTypes::TagMatchEditWidget::TagMatchEditWidget( 0527 const int comparison, 0528 const QString& field, 0529 const bool invert, 0530 const int strictness, 0531 const QVariant& value ) 0532 : QWidget( nullptr ) 0533 , m_fieldsModel( new TagMatchFieldsModel() ) 0534 { 0535 ui.setupUi( this ); 0536 0537 // plural support in combobox labels 0538 connect( ui.spinBox_ValueDateValue, QOverload<int>::of(&QSpinBox::valueChanged), 0539 this, &TagMatchEditWidget::slotUpdateComboBoxLabels ); 0540 ui.comboBox_ValueDateUnit->insertItem(0, i18ncp("within the last %1 days", "day", "days", 0)); 0541 ui.comboBox_ValueDateUnit->insertItem(1, i18ncp("within the last %1 months", "month", "months", 0)); 0542 ui.comboBox_ValueDateUnit->insertItem(2, i18ncp("within the last %1 years", "year", "years", 0)); 0543 0544 // fill in appropriate defaults for some attributes 0545 ui.qcalendarwidget_DateSpecific->setSelectedDate( QDate::currentDate() ); 0546 0547 // fill in user-specified values before the slots have been connected to we don't have to call back to the constraint a dozen times 0548 ui.comboBox_Field->setModel( m_fieldsModel ); 0549 ui.checkBox_Invert->setChecked( invert ); 0550 0551 if ( field == QLatin1String("rating") ) { 0552 ui.comboBox_ComparisonRating->setCurrentIndex( comparison ); 0553 ui.slider_StrictnessRating->setValue( strictness ); 0554 ui.rating_RatingValue->setRating( value.toInt() ); 0555 } else if ( field == QLatin1String("length") ) { 0556 ui.comboBox_ComparisonTime->setCurrentIndex( comparison ); 0557 ui.slider_StrictnessTime->setValue( strictness ); 0558 ui.timeEdit_TimeValue->setTime( QTime(0, 0, 0).addMSecs( value.toInt() ) ); 0559 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeInt ) { 0560 ui.comboBox_ComparisonInt->setCurrentIndex( comparison ); 0561 ui.slider_StrictnessInt->setValue( strictness ); 0562 ui.spinBox_ValueInt->setValue( value.toInt() ); 0563 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeDate ) { 0564 ui.comboBox_ComparisonDate->setCurrentIndex( comparison ); 0565 ui.slider_StrictnessDate->setValue( strictness ); 0566 if ( comparison == TagMatch::CompareDateWithin ) { 0567 ui.stackedWidget_Date->setCurrentIndex( 1 ); 0568 ui.spinBox_ValueDateValue->setValue( value.value<DateRange>().first ); 0569 ui.comboBox_ValueDateUnit->setCurrentIndex( value.value<DateRange>().second ); 0570 } else { 0571 ui.stackedWidget_Date->setCurrentIndex( 0 ); 0572 ui.qcalendarwidget_DateSpecific->setSelectedDate( value.toDate() ); 0573 } 0574 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeString ) { 0575 ui.comboBox_ComparisonString->setCurrentIndex( comparison ); 0576 ui.lineEdit_StringValue->setText( value.toString() ); 0577 } 0578 0579 // set this after the slot has been connected so that it also sets the field page correctly 0580 ui.comboBox_Field->setCurrentIndex( m_fieldsModel->index_of( field ) ); 0581 } 0582 0583 ConstraintTypes::TagMatchEditWidget::~TagMatchEditWidget() 0584 { 0585 delete m_fieldsModel; 0586 } 0587 0588 // ComboBox slots for comparisons 0589 void 0590 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonDate_currentIndexChanged( int c ) 0591 { 0592 if ( c == TagMatch::CompareDateWithin ) 0593 ui.stackedWidget_Date->setCurrentIndex( 1 ); 0594 else 0595 ui.stackedWidget_Date->setCurrentIndex( 0 ); 0596 Q_EMIT comparisonChanged( c ); 0597 } 0598 0599 void 0600 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonInt_currentIndexChanged( int c ) 0601 { 0602 Q_EMIT comparisonChanged( c ); 0603 } 0604 0605 void 0606 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonRating_currentIndexChanged( int c ) 0607 { 0608 Q_EMIT comparisonChanged( c ); 0609 } 0610 0611 void 0612 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonString_currentIndexChanged( int c ) 0613 { 0614 Q_EMIT comparisonChanged( c ); 0615 } 0616 0617 void 0618 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonTime_currentIndexChanged( int c ) 0619 { 0620 Q_EMIT comparisonChanged( c ); 0621 } 0622 0623 // ComboBox slots for field 0624 void 0625 ConstraintTypes::TagMatchEditWidget::on_comboBox_Field_currentIndexChanged( int idx ) 0626 { 0627 QString field = m_fieldsModel->field_at( idx ); 0628 int c = 0; 0629 int s = 0; 0630 QVariant v; 0631 if ( field == QLatin1String("length") ) { 0632 ui.stackedWidget_Field->setCurrentIndex( 3 ); 0633 c = ui.comboBox_ComparisonTime->currentIndex(); 0634 s = ui.slider_StrictnessTime->value(); 0635 v = QTime(0, 0, 0).msecsTo( ui.timeEdit_TimeValue->time() ); 0636 } else if ( field == QLatin1String("rating") ) { 0637 ui.stackedWidget_Field->setCurrentIndex( 4 ); 0638 c = ui.comboBox_ComparisonRating->currentIndex(); 0639 s = ui.slider_StrictnessRating->value(); 0640 v = ui.rating_RatingValue->rating(); 0641 } else { 0642 if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeInt ) { 0643 ui.stackedWidget_Field->setCurrentIndex( 0 ); 0644 c = ui.comboBox_ComparisonInt->currentIndex(); 0645 s = ui.slider_StrictnessInt->value(); 0646 v = ui.spinBox_ValueInt->value(); 0647 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeDate ) { 0648 ui.stackedWidget_Field->setCurrentIndex( 1 ); 0649 c = ui.comboBox_ComparisonDate->currentIndex(); 0650 s = ui.slider_StrictnessDate->value(); 0651 if ( c == TagMatch::CompareDateWithin ) { 0652 ui.stackedWidget_Date->setCurrentIndex( 1 ); 0653 int a = ui.spinBox_ValueDateValue->value(); 0654 int b = ui.comboBox_ValueDateUnit->currentIndex(); 0655 v = QVariant::fromValue( DateRange( a, b ) ); 0656 } else { 0657 ui.stackedWidget_Date->setCurrentIndex( 0 ); 0658 v = ui.qcalendarwidget_DateSpecific->selectedDate(); 0659 } 0660 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeString ) { 0661 ui.stackedWidget_Field->setCurrentIndex( 2 ); 0662 c = ui.comboBox_ComparisonString->currentIndex(); 0663 s = 1.0; 0664 v = ui.lineEdit_StringValue->text(); 0665 } 0666 } 0667 0668 // TODO: set range limitations and default values depending on field 0669 0670 Q_EMIT fieldChanged( field ); 0671 Q_EMIT valueChanged( v ); 0672 Q_EMIT comparisonChanged( c ); 0673 Q_EMIT strictnessChanged( s ); 0674 } 0675 0676 // Invert checkbox slot 0677 void 0678 ConstraintTypes::TagMatchEditWidget::on_checkBox_Invert_clicked( bool v ) 0679 { 0680 Q_EMIT invertChanged( v ); 0681 } 0682 0683 // Strictness Slider slots 0684 void 0685 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessDate_valueChanged( int v ) 0686 { 0687 Q_EMIT strictnessChanged( v ); 0688 } 0689 0690 void 0691 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessInt_valueChanged( int v ) 0692 { 0693 Q_EMIT strictnessChanged( v ); 0694 } 0695 0696 void 0697 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessRating_valueChanged( int v ) 0698 { 0699 Q_EMIT strictnessChanged( v ); 0700 } 0701 0702 void 0703 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessTime_valueChanged( int v ) 0704 { 0705 Q_EMIT strictnessChanged( v ); 0706 } 0707 0708 // various value slots 0709 void 0710 ConstraintTypes::TagMatchEditWidget::on_kdatewidget_DateSpecific_changed( const QDate& v ) 0711 { 0712 Q_EMIT valueChanged( QVariant( v ) ); 0713 } 0714 0715 void 0716 ConstraintTypes::TagMatchEditWidget::on_comboBox_ValueDateUnit_currentIndexChanged( int u ) 0717 { 0718 int v = ui.spinBox_ValueDateValue->value(); 0719 Q_EMIT valueChanged( QVariant::fromValue( DateRange( v, u ) ) ); 0720 } 0721 0722 void 0723 ConstraintTypes::TagMatchEditWidget::on_spinBox_ValueDateValue_valueChanged( int v ) 0724 { 0725 int u = ui.comboBox_ValueDateUnit->currentIndex(); 0726 Q_EMIT valueChanged( QVariant::fromValue( DateRange( v, u ) ) ); 0727 } 0728 0729 void 0730 ConstraintTypes::TagMatchEditWidget::on_spinBox_ValueInt_valueChanged( int v ) 0731 { 0732 Q_EMIT valueChanged( QVariant( v ) ); 0733 } 0734 0735 void 0736 ConstraintTypes::TagMatchEditWidget::on_lineEdit_StringValue_textChanged( const QString& v ) 0737 { 0738 Q_EMIT valueChanged( QVariant( v ) ); 0739 } 0740 0741 void 0742 ConstraintTypes::TagMatchEditWidget::on_rating_RatingValue_ratingChanged( int v ) 0743 { 0744 Q_EMIT valueChanged( QVariant( v ) ); 0745 } 0746 0747 void 0748 ConstraintTypes::TagMatchEditWidget::on_timeEdit_TimeValue_timeChanged( const QTime& t ) 0749 { 0750 int v = QTime(0, 0, 0).msecsTo( t ); 0751 Q_EMIT valueChanged( QVariant( v ) ); 0752 } 0753 0754 void 0755 ConstraintTypes::TagMatchEditWidget::slotUpdateComboBoxLabels( int value ) 0756 { 0757 ui.comboBox_ValueDateUnit->setItemText(0, i18ncp("within the last %1 days", "day", "days", value)); 0758 ui.comboBox_ValueDateUnit->setItemText(1, i18ncp("within the last %1 months", "month", "months", value)); 0759 ui.comboBox_ValueDateUnit->setItemText(2, i18ncp("within the last %1 years", "year", "years", value)); 0760 }