1 /****************************************************************************************
2 * Copyright (c) 2008-2012 Soren Harward <stharward@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify it under *
5 * the terms of the GNU General Public License as published by the Free Software *
6 * Foundation; either version 2 of the License, or (at your option) any later *
7 * version. *
8 * *
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
11 * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
12 * *
13 * You should have received a copy of the GNU General Public License along with *
14 * this program. If not, see <http://www.gnu.org/licenses/>. *
15 ****************************************************************************************/
16
17 #define DEBUG_PREFIX "Constraint::TagMatch"
18
19 #include "TagMatch.h"
20
21 #include "playlistgenerator/Constraint.h"
22 #include "playlistgenerator/ConstraintFactory.h"
23
24 #include "core/collections/QueryMaker.h"
25 #include "core/meta/Meta.h"
26 #include "core/meta/Statistics.h"
27 #include "core/support/Debug.h"
28
29 #include <math.h>
30 #include <stdlib.h>
31
32 Constraint*
createFromXml(QDomElement & xmlelem,ConstraintNode * p)33 ConstraintTypes::TagMatch::createFromXml( QDomElement& xmlelem, ConstraintNode* p )
34 {
35 if ( p )
36 return new TagMatch( xmlelem, p );
37 else
38 return nullptr;
39 }
40
41 Constraint*
createNew(ConstraintNode * p)42 ConstraintTypes::TagMatch::createNew( ConstraintNode* p )
43 {
44 if ( p )
45 return new TagMatch( p );
46 else
47 return nullptr;
48 }
49
50 ConstraintFactoryEntry*
registerMe()51 ConstraintTypes::TagMatch::registerMe()
52 {
53 return new ConstraintFactoryEntry( QStringLiteral("TagMatch"),
54 i18n("Match Tags"),
55 i18n("Make all tracks in the playlist match the specified characteristic"),
56 &TagMatch::createFromXml, &TagMatch::createNew );
57 }
58
TagMatch(QDomElement & xmlelem,ConstraintNode * p)59 ConstraintTypes::TagMatch::TagMatch( QDomElement& xmlelem, ConstraintNode* p )
60 : MatchingConstraint( p )
61 , m_comparer( new Comparer() )
62 , m_fieldsModel( new TagMatchFieldsModel() )
63 {
64 QDomAttr a;
65
66 a = xmlelem.attributeNode( QStringLiteral("field") );
67 if ( !a.isNull() ) {
68 if ( m_fieldsModel->contains( a.value() ) )
69 m_field = a.value();
70 }
71
72 a = xmlelem.attributeNode( QStringLiteral("comparison") );
73 if ( !a.isNull() ) {
74 m_comparison = a.value().toInt();
75 }
76
77 a = xmlelem.attributeNode( QStringLiteral("value") );
78 if ( !a.isNull() ) {
79 if ( m_fieldsModel->type_of( m_field ) == FieldTypeInt ) {
80 m_value = a.value().toInt();
81 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) {
82 if ( m_comparison == CompareDateWithin ) {
83 QStringList parts = a.value().split(' ');
84 if ( parts.size() == 2 ) {
85 int u = parts.at( 0 ).toInt();
86 int v = 0;
87 if ( parts.at( 1 ) == QLatin1String("months") )
88 v = 1;
89 else if ( parts.at( 1 ) == QLatin1String("years") )
90 v = 2;
91 m_value = QVariant::fromValue( DateRange( u, v ) );
92 } else
93 m_value = QVariant::fromValue( DateRange( 0, 0 ) );
94 } else
95 m_value = QDate::fromString( a.value(), Qt::ISODate );
96 } else { // String type
97 m_value = a.value();
98 }
99 }
100
101 a = xmlelem.attributeNode( QStringLiteral("invert") );
102 if ( !a.isNull() && a.value() == QLatin1String("true") )
103 m_invert = true;
104 else
105 m_invert = false;
106
107 a = xmlelem.attributeNode( QStringLiteral("strictness") );
108 if ( !a.isNull() )
109 m_strictness = a.value().toDouble();
110 }
111
TagMatch(ConstraintNode * p)112 ConstraintTypes::TagMatch::TagMatch( ConstraintNode* p )
113 : MatchingConstraint( p )
114 , m_comparison( CompareStrEquals )
115 , m_field( QStringLiteral("title") )
116 , m_invert( false )
117 , m_strictness( 1.0 )
118 , m_value()
119 , m_comparer( new Comparer() )
120 , m_fieldsModel( new TagMatchFieldsModel() )
121 {
122 }
123
~TagMatch()124 ConstraintTypes::TagMatch::~TagMatch()
125 {
126 delete m_comparer;
127 delete m_fieldsModel;
128 }
129
130 QWidget*
editWidget() const131 ConstraintTypes::TagMatch::editWidget() const
132 {
133 TagMatchEditWidget* e = new TagMatchEditWidget(
134 m_comparison,
135 m_field,
136 m_invert,
137 static_cast<int>( m_strictness * 10 ),
138 m_value );
139 connect( e, &TagMatchEditWidget::comparisonChanged, this, &TagMatch::setComparison );
140 connect( e, &TagMatchEditWidget::fieldChanged, this, &TagMatch::setField );
141 connect( e, &TagMatchEditWidget::invertChanged, this, &TagMatch::setInvert );
142 connect( e, &TagMatchEditWidget::strictnessChanged, this, &TagMatch::setStrictness );
143 connect( e, &TagMatchEditWidget::valueChanged, this, &TagMatch::setValue );
144 return e;
145 }
146
147 void
toXml(QDomDocument & doc,QDomElement & elem) const148 ConstraintTypes::TagMatch::toXml( QDomDocument& doc, QDomElement& elem ) const
149 {
150 QDomElement c = doc.createElement( QStringLiteral("constraint") );
151
152 c.setAttribute( QStringLiteral("type"), QStringLiteral("TagMatch") );
153 c.setAttribute( QStringLiteral("field"), m_field );
154 c.setAttribute( QStringLiteral("comparison"), m_comparison );
155 c.setAttribute( QStringLiteral("value"), valueToString() );
156
157 if ( m_invert )
158 c.setAttribute( QStringLiteral("invert"), QStringLiteral("true") );
159 else
160 c.setAttribute( QStringLiteral("invert"), QStringLiteral("false") );
161
162 c.setAttribute( QStringLiteral("strictness"), QString::number( m_strictness ) );
163
164 elem.appendChild( c );
165 }
166
167 QString
getName() const168 ConstraintTypes::TagMatch::getName() const
169 {
170 QString v( i18nc( "%1 = empty string or \"not\"; "
171 "%2 = a metadata field, like \"title\" or \"artist name\"; "
172 "%3 = a predicate, can be equals, starts with, ends with or contains; "
173 "%4 = a string to match; "
174 "Example: Match tag: not title contains \"foo\"", "Match tag:%1 %2 %3 %4") );
175 v = v.arg( ( m_invert ? i18n(" not") : QLatin1String("") ), m_fieldsModel->pretty_name_of( m_field ), comparisonToString() );
176 if ( m_field == QLatin1String("rating") ) {
177 double r = m_value.toDouble() / 2.0;
178 return v.arg( i18ncp("number of stars in the rating of a track", "%1 star", "%1 stars", r) );
179 } else if ( m_field == QLatin1String("length") ) {
180 return v.arg( QTime(0, 0, 0).addMSecs( m_value.toInt() ).toString( QStringLiteral("H:mm:ss") ) );
181 } else {
182 if ( m_fieldsModel->type_of( m_field ) == FieldTypeString ) {
183 // put quotes around any strings (eg, track title or artist name) ...
184 QString s = i18nc("an arbitrary string surrounded by quotes", "\"%1\"", valueToString() );
185 return v.arg( s );
186 } else {
187 // ... but don't quote put quotes around anything else
188 return v.arg( valueToString() );
189 }
190 }
191 }
192
193 Collections::QueryMaker*
initQueryMaker(Collections::QueryMaker * qm) const194 ConstraintTypes::TagMatch::initQueryMaker( Collections::QueryMaker* qm ) const
195 {
196 if ( ( m_fieldsModel->type_of( m_field ) == FieldTypeInt ) ) {
197 int v = m_value.toInt();
198 int range = static_cast<int>( m_comparer->rangeNum( m_strictness, m_fieldsModel->meta_value_of( m_field ) ) );
199 if ( m_comparison == CompareNumEquals ) {
200 if ( !m_invert ) {
201 if ( m_strictness < 0.99 ) { // fuzzy approximation of "1.0"
202 qm->beginAnd();
203 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v - range, Collections::QueryMaker::GreaterThan );
204 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v + range, Collections::QueryMaker::LessThan );
205 qm->endAndOr();
206 } else {
207 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v, Collections::QueryMaker::Equals );
208 }
209 } else {
210 if ( m_strictness > 0.99 ) {
211 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), v, Collections::QueryMaker::Equals );
212 }
213 }
214 } else if ( m_comparison == CompareNumGreaterThan ) {
215 if ( m_invert )
216 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), v + range, Collections::QueryMaker::GreaterThan );
217 else
218 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v - range, Collections::QueryMaker::GreaterThan );
219 } else if ( m_comparison == CompareNumLessThan ) {
220 if ( m_invert )
221 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), v - range, Collections::QueryMaker::LessThan );
222 else
223 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), v + range, Collections::QueryMaker::LessThan );
224 }
225 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) {
226 uint referenceDate = 0;
227 int range = m_comparer->rangeDate( m_strictness );
228 if ( m_comparison == CompareDateBefore ) {
229 referenceDate = m_value.toDateTime().toSecsSinceEpoch();
230 if ( m_invert )
231 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::LessThan );
232 else
233 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::LessThan );
234 } else if ( m_comparison == CompareDateOn ) {
235 referenceDate = m_value.toDateTime().toSecsSinceEpoch();
236 if ( !m_invert ) {
237 qm->beginAnd();
238 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::GreaterThan );
239 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::LessThan );
240 qm->endAndOr();
241 }
242 } else if ( m_comparison == CompareDateAfter ) {
243 referenceDate = m_value.toDateTime().toSecsSinceEpoch();
244 if ( m_invert )
245 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::GreaterThan );
246 else
247 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::GreaterThan );
248 } else if ( m_comparison == CompareDateWithin ) {
249 QDateTime now = QDateTime::currentDateTime();
250 DateRange r = m_value.value<DateRange>();
251 switch ( r.second ) {
252 case 0:
253 referenceDate = now.addDays( -1 * r.first ).toSecsSinceEpoch();
254 break;
255 case 1:
256 referenceDate = now.addMonths( -1 * r.first ).toSecsSinceEpoch();
257 break;
258 case 2:
259 referenceDate = now.addYears( -1 * r.first ).toSecsSinceEpoch();
260 break;
261 default:
262 break;
263 }
264 if ( m_invert )
265 qm->excludeNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate + range, Collections::QueryMaker::GreaterThan );
266 else
267 qm->addNumberFilter( m_fieldsModel->meta_value_of( m_field ), referenceDate - range, Collections::QueryMaker::GreaterThan );
268 }
269 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeString ) {
270 if ( m_comparison == CompareStrEquals ) {
271 if ( m_invert )
272 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, true );
273 else
274 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, true );
275 } else if ( m_comparison == CompareStrStartsWith ) {
276 if ( m_invert )
277 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, false );
278 else
279 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), true, false );
280 } else if ( m_comparison == CompareStrEndsWith ) {
281 if ( m_invert )
282 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, true );
283 else
284 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, true );
285 } else if ( m_comparison == CompareStrContains ) {
286 if ( m_invert )
287 qm->excludeFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, false );
288 else
289 qm->addFilter( m_fieldsModel->meta_value_of( m_field ), m_value.toString(), false, false );
290 }
291 // TODO: regexp
292 } else {
293 error() << "TagMatch cannot initialize QM for unknown type";
294 }
295
296 return qm;
297 }
298
299 double
satisfaction(const Meta::TrackList & tl) const300 ConstraintTypes::TagMatch::satisfaction( const Meta::TrackList& tl ) const
301 {
302 double satisfaction = 0.0;
303 foreach( Meta::TrackPtr t, tl ) {
304 if ( matches( t ) ) {
305 satisfaction += 1.0;
306 }
307 }
308 satisfaction /= ( double )tl.size();
309 return satisfaction;
310 }
311
312 const QBitArray
whatTracksMatch(const Meta::TrackList & tl)313 ConstraintTypes::TagMatch::whatTracksMatch( const Meta::TrackList& tl )
314 {
315 QBitArray match = QBitArray( tl.size() );
316 for ( int i = 0; i < tl.size(); i++ ) {
317 if ( matches( tl.at( i ) ) )
318 match.setBit( i, true );
319 }
320 return match;
321 }
322
323 int
constraintMatchType() const324 ConstraintTypes::TagMatch::constraintMatchType() const
325 {
326 return ( 0 << 28 ) + m_fieldsModel->index_of( m_field );
327 }
328
329
330 QString
comparisonToString() const331 ConstraintTypes::TagMatch::comparisonToString() const
332 {
333 if ( m_fieldsModel->type_of( m_field ) == FieldTypeInt ) {
334 if ( m_comparison == CompareNumEquals ) {
335 return i18nc("a numerical tag (like year or track number) equals a value","equals");
336 } else if ( m_comparison == CompareNumGreaterThan ) {
337 return i18n("greater than");
338 } else if ( m_comparison == CompareNumLessThan ) {
339 return i18n("less than");
340 }
341 } else if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) {
342 if ( m_comparison == CompareDateBefore ) {
343 return i18n("before");
344 } else if ( m_comparison == CompareDateOn ) {
345 return i18n("on");
346 } else if ( m_comparison == CompareDateAfter ) {
347 return i18n("after");
348 } else if ( m_comparison == CompareDateWithin ) {
349 return i18n("within");
350 }
351 } else {
352 if ( m_comparison == CompareStrEquals ) {
353 return i18nc("an alphabetical tag (like title or artist name) equals some string","equals");
354 } else if ( m_comparison == CompareStrStartsWith ) {
355 return i18nc("an alphabetical tag (like title or artist name) starts with some string","starts with");
356 } else if ( m_comparison == CompareStrEndsWith ) {
357 return i18nc("an alphabetical tag (like title or artist name) ends with some string","ends with");
358 } else if ( m_comparison == CompareStrContains ) {
359 return i18nc("an alphabetical tag (like title or artist name) contains some string","contains");
360 } else if ( m_comparison == CompareStrRegExp ) {
361 return i18n("regexp");
362 }
363 }
364 return i18n("unknown comparison");
365 }
366
367 QString
valueToString() const368 ConstraintTypes::TagMatch::valueToString() const
369 {
370 if ( m_fieldsModel->type_of( m_field ) == FieldTypeDate ) {
371 if ( m_comparison != CompareDateWithin ) {
372 return m_value.toDate().toString( Qt::ISODate );
373 } else {
374 KLocalizedString unit;
375 switch ( m_value.value<DateRange>().second ) {
376 case 0:
377 unit = ki18np("%1 day", "%1 days");
378 break;
379 case 1:
380 unit = ki18np("%1 month", "%1 months");
381 break;
382 case 2:
383 unit = ki18np("%1 year", "%1 years");
384 break;
385 default:
386 break;
387 }
388 return unit.subs( m_value.value<DateRange>().first ).toString();
389 }
390 } else {
391 return m_value.toString();
392 }
393 }
394
395 bool
matches(Meta::TrackPtr track) const396 ConstraintTypes::TagMatch::matches( Meta::TrackPtr track ) const
397 {
398 if ( !m_matchCache.contains( track ) ) {
399 double v = 0.0;
400 qint64 fmv = m_fieldsModel->meta_value_of( m_field );
401 switch ( fmv ) {
402 case Meta::valUrl:
403 v = m_comparer->compareStr( track->prettyUrl(), m_comparison, m_value.toString() );
404 break;
405 case Meta::valTitle:
406 v = m_comparer->compareStr( track->prettyName(), m_comparison, m_value.toString() );
407 break;
408 case Meta::valArtist:
409 v = m_comparer->compareStr( track->artist()->prettyName(), m_comparison, m_value.toString() );
410 break;
411 case Meta::valAlbum:
412 v = m_comparer->compareStr( track->album()->prettyName(), m_comparison, m_value.toString() );
413 break;
414 case Meta::valGenre:
415 v = m_comparer->compareStr( track->genre()->prettyName(), m_comparison, m_value.toString() );
416 break;
417 case Meta::valComposer:
418 v = m_comparer->compareStr( track->composer()->prettyName(), m_comparison, m_value.toString() );
419 break;
420 case Meta::valYear:
421 v = m_comparer->compareNum( track->year()->prettyName().toInt(), m_comparison, m_value.toInt(), m_strictness, fmv );
422 break;
423 case Meta::valComment:
424 v = m_comparer->compareStr( track->comment(), m_comparison, m_value.toString() );
425 break;
426 case Meta::valTrackNr:
427 v = m_comparer->compareNum( track->trackNumber(), m_comparison, m_value.toInt(), m_strictness, fmv );
428 break;
429 case Meta::valDiscNr:
430 v = m_comparer->compareNum( track->discNumber(), m_comparison, m_value.toInt(), m_strictness, fmv );
431 break;
432 case Meta::valLength:
433 v = m_comparer->compareNum( track->length(), m_comparison, m_value.toInt(), m_strictness, fmv );
434 break;
435 case Meta::valBitrate:
436 v = m_comparer->compareNum( track->bitrate(), m_comparison, m_value.toInt(), m_strictness, fmv );
437 break;
438 case Meta::valFilesize:
439 v = m_comparer->compareNum( track->filesize(), m_comparison, m_value.toInt(), m_strictness, fmv );
440 break;
441 case Meta::valCreateDate:
442 v = m_comparer->compareDate( track->createDate().toSecsSinceEpoch(), m_comparison, m_value, m_strictness );
443 break;
444 case Meta::valScore:
445 v = m_comparer->compareNum( track->statistics()->score(), m_comparison, m_value.toDouble(), m_strictness, fmv );
446 break;
447 case Meta::valRating:
448 v = m_comparer->compareNum( track->statistics()->rating(), m_comparison, m_value.toInt(), m_strictness, fmv );
449 break;
450 case Meta::valFirstPlayed:
451 v = m_comparer->compareDate( track->statistics()->firstPlayed().toSecsSinceEpoch(), m_comparison, m_value, m_strictness );
452 break;
453 case Meta::valLastPlayed:
454 v = m_comparer->compareDate( track->statistics()->lastPlayed().toSecsSinceEpoch(), m_comparison, m_value, m_strictness );
455 break;
456 case Meta::valPlaycount:
457 v = m_comparer->compareNum( track->statistics()->playCount(), m_comparison, m_value.toInt(), m_strictness, fmv );
458 break;
459 case Meta::valLabel:
460 v = m_comparer->compareLabels( track, m_comparison, m_value.toString() );
461 break;
462 default:
463 v = 0.0;
464 break;
465 }
466 if ( m_invert )
467 v = 1.0 - v;
468
469 m_matchCache.insert( track, ( v > ( (double)qrand() / (double)RAND_MAX ) ) );
470 }
471 return m_matchCache.value( track );
472 }
473
474 void
setComparison(int c)475 ConstraintTypes::TagMatch::setComparison( int c )
476 {
477 m_comparison = c;
478 m_matchCache.clear();
479 Q_EMIT dataChanged();
480 }
481
482 void
setField(const QString & s)483 ConstraintTypes::TagMatch::setField( const QString& s )
484 {
485 m_field = s;
486 m_matchCache.clear();
487 Q_EMIT dataChanged();
488 }
489
490 void
setInvert(bool v)491 ConstraintTypes::TagMatch::setInvert( bool v )
492 {
493 if ( m_invert != v ) {
494 foreach( const Meta::TrackPtr t, m_matchCache.keys() ) {
495 m_matchCache.insert( t, !m_matchCache.value( t ) );
496 }
497 }
498 m_invert = v;
499 Q_EMIT dataChanged();
500 }
501
502 void
setStrictness(int v)503 ConstraintTypes::TagMatch::setStrictness( int v )
504 {
505 m_strictness = static_cast<double>( v ) / 10.0;
506 m_matchCache.clear();
507 }
508
509 void
setValue(const QVariant & v)510 ConstraintTypes::TagMatch::setValue( const QVariant& v )
511 {
512 m_value = v;
513 m_matchCache.clear();
514 Q_EMIT dataChanged();
515 }
516
517 /******************************
518 * Edit Widget *
519 ******************************/
520
TagMatchEditWidget(const int comparison,const QString & field,const bool invert,const int strictness,const QVariant & value)521 ConstraintTypes::TagMatchEditWidget::TagMatchEditWidget(
522 const int comparison,
523 const QString& field,
524 const bool invert,
525 const int strictness,
526 const QVariant& value )
527 : QWidget( nullptr )
528 , m_fieldsModel( new TagMatchFieldsModel() )
529 {
530 ui.setupUi( this );
531
532 // plural support in combobox labels
533 connect( ui.spinBox_ValueDateValue, QOverload<int>::of(&QSpinBox::valueChanged),
534 this, &TagMatchEditWidget::slotUpdateComboBoxLabels );
535 ui.comboBox_ValueDateUnit->insertItem(0, i18ncp("within the last %1 days", "day", "days", 0));
536 ui.comboBox_ValueDateUnit->insertItem(1, i18ncp("within the last %1 months", "month", "months", 0));
537 ui.comboBox_ValueDateUnit->insertItem(2, i18ncp("within the last %1 years", "year", "years", 0));
538
539 // fill in appropriate defaults for some attributes
540 ui.qcalendarwidget_DateSpecific->setSelectedDate( QDate::currentDate() );
541
542 // 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
543 ui.comboBox_Field->setModel( m_fieldsModel );
544 ui.checkBox_Invert->setChecked( invert );
545
546 if ( field == QLatin1String("rating") ) {
547 ui.comboBox_ComparisonRating->setCurrentIndex( comparison );
548 ui.slider_StrictnessRating->setValue( strictness );
549 ui.rating_RatingValue->setRating( value.toInt() );
550 } else if ( field == QLatin1String("length") ) {
551 ui.comboBox_ComparisonTime->setCurrentIndex( comparison );
552 ui.slider_StrictnessTime->setValue( strictness );
553 ui.timeEdit_TimeValue->setTime( QTime(0, 0, 0).addMSecs( value.toInt() ) );
554 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeInt ) {
555 ui.comboBox_ComparisonInt->setCurrentIndex( comparison );
556 ui.slider_StrictnessInt->setValue( strictness );
557 ui.spinBox_ValueInt->setValue( value.toInt() );
558 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeDate ) {
559 ui.comboBox_ComparisonDate->setCurrentIndex( comparison );
560 ui.slider_StrictnessDate->setValue( strictness );
561 if ( comparison == TagMatch::CompareDateWithin ) {
562 ui.stackedWidget_Date->setCurrentIndex( 1 );
563 ui.spinBox_ValueDateValue->setValue( value.value<DateRange>().first );
564 ui.comboBox_ValueDateUnit->setCurrentIndex( value.value<DateRange>().second );
565 } else {
566 ui.stackedWidget_Date->setCurrentIndex( 0 );
567 ui.qcalendarwidget_DateSpecific->setSelectedDate( value.toDate() );
568 }
569 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeString ) {
570 ui.comboBox_ComparisonString->setCurrentIndex( comparison );
571 ui.lineEdit_StringValue->setText( value.toString() );
572 }
573
574 // set this after the slot has been connected so that it also sets the field page correctly
575 ui.comboBox_Field->setCurrentIndex( m_fieldsModel->index_of( field ) );
576 }
577
~TagMatchEditWidget()578 ConstraintTypes::TagMatchEditWidget::~TagMatchEditWidget()
579 {
580 delete m_fieldsModel;
581 }
582
583 // ComboBox slots for comparisons
584 void
on_comboBox_ComparisonDate_currentIndexChanged(int c)585 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonDate_currentIndexChanged( int c )
586 {
587 if ( c == TagMatch::CompareDateWithin )
588 ui.stackedWidget_Date->setCurrentIndex( 1 );
589 else
590 ui.stackedWidget_Date->setCurrentIndex( 0 );
591 Q_EMIT comparisonChanged( c );
592 }
593
594 void
on_comboBox_ComparisonInt_currentIndexChanged(int c)595 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonInt_currentIndexChanged( int c )
596 {
597 Q_EMIT comparisonChanged( c );
598 }
599
600 void
on_comboBox_ComparisonRating_currentIndexChanged(int c)601 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonRating_currentIndexChanged( int c )
602 {
603 Q_EMIT comparisonChanged( c );
604 }
605
606 void
on_comboBox_ComparisonString_currentIndexChanged(int c)607 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonString_currentIndexChanged( int c )
608 {
609 Q_EMIT comparisonChanged( c );
610 }
611
612 void
on_comboBox_ComparisonTime_currentIndexChanged(int c)613 ConstraintTypes::TagMatchEditWidget::on_comboBox_ComparisonTime_currentIndexChanged( int c )
614 {
615 Q_EMIT comparisonChanged( c );
616 }
617
618 // ComboBox slots for field
619 void
on_comboBox_Field_currentIndexChanged(int idx)620 ConstraintTypes::TagMatchEditWidget::on_comboBox_Field_currentIndexChanged( int idx )
621 {
622 QString field = m_fieldsModel->field_at( idx );
623 int c = 0;
624 int s = 0;
625 QVariant v;
626 if ( field == QLatin1String("length") ) {
627 ui.stackedWidget_Field->setCurrentIndex( 3 );
628 c = ui.comboBox_ComparisonTime->currentIndex();
629 s = ui.slider_StrictnessTime->value();
630 v = QTime(0, 0, 0).msecsTo( ui.timeEdit_TimeValue->time() );
631 } else if ( field == QLatin1String("rating") ) {
632 ui.stackedWidget_Field->setCurrentIndex( 4 );
633 c = ui.comboBox_ComparisonRating->currentIndex();
634 s = ui.slider_StrictnessRating->value();
635 v = ui.rating_RatingValue->rating();
636 } else {
637 if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeInt ) {
638 ui.stackedWidget_Field->setCurrentIndex( 0 );
639 c = ui.comboBox_ComparisonInt->currentIndex();
640 s = ui.slider_StrictnessInt->value();
641 v = ui.spinBox_ValueInt->value();
642 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeDate ) {
643 ui.stackedWidget_Field->setCurrentIndex( 1 );
644 c = ui.comboBox_ComparisonDate->currentIndex();
645 s = ui.slider_StrictnessDate->value();
646 if ( c == TagMatch::CompareDateWithin ) {
647 ui.stackedWidget_Date->setCurrentIndex( 1 );
648 int a = ui.spinBox_ValueDateValue->value();
649 int b = ui.comboBox_ValueDateUnit->currentIndex();
650 v = QVariant::fromValue( DateRange( a, b ) );
651 } else {
652 ui.stackedWidget_Date->setCurrentIndex( 0 );
653 v = ui.qcalendarwidget_DateSpecific->selectedDate();
654 }
655 } else if ( m_fieldsModel->type_of( field ) == TagMatch::FieldTypeString ) {
656 ui.stackedWidget_Field->setCurrentIndex( 2 );
657 c = ui.comboBox_ComparisonString->currentIndex();
658 s = 1.0;
659 v = ui.lineEdit_StringValue->text();
660 }
661 }
662
663 // TODO: set range limitations and default values depending on field
664
665 Q_EMIT fieldChanged( field );
666 Q_EMIT valueChanged( v );
667 Q_EMIT comparisonChanged( c );
668 Q_EMIT strictnessChanged( s );
669 }
670
671 // Invert checkbox slot
672 void
on_checkBox_Invert_clicked(bool v)673 ConstraintTypes::TagMatchEditWidget::on_checkBox_Invert_clicked( bool v )
674 {
675 Q_EMIT invertChanged( v );
676 }
677
678 // Strictness Slider slots
679 void
on_slider_StrictnessDate_valueChanged(int v)680 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessDate_valueChanged( int v )
681 {
682 Q_EMIT strictnessChanged( v );
683 }
684
685 void
on_slider_StrictnessInt_valueChanged(int v)686 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessInt_valueChanged( int v )
687 {
688 Q_EMIT strictnessChanged( v );
689 }
690
691 void
on_slider_StrictnessRating_valueChanged(int v)692 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessRating_valueChanged( int v )
693 {
694 Q_EMIT strictnessChanged( v );
695 }
696
697 void
on_slider_StrictnessTime_valueChanged(int v)698 ConstraintTypes::TagMatchEditWidget::on_slider_StrictnessTime_valueChanged( int v )
699 {
700 Q_EMIT strictnessChanged( v );
701 }
702
703 // various value slots
704 void
on_kdatewidget_DateSpecific_changed(const QDate & v)705 ConstraintTypes::TagMatchEditWidget::on_kdatewidget_DateSpecific_changed( const QDate& v )
706 {
707 Q_EMIT valueChanged( QVariant( v ) );
708 }
709
710 void
on_comboBox_ValueDateUnit_currentIndexChanged(int u)711 ConstraintTypes::TagMatchEditWidget::on_comboBox_ValueDateUnit_currentIndexChanged( int u )
712 {
713 int v = ui.spinBox_ValueDateValue->value();
714 Q_EMIT valueChanged( QVariant::fromValue( DateRange( v, u ) ) );
715 }
716
717 void
on_spinBox_ValueDateValue_valueChanged(int v)718 ConstraintTypes::TagMatchEditWidget::on_spinBox_ValueDateValue_valueChanged( int v )
719 {
720 int u = ui.comboBox_ValueDateUnit->currentIndex();
721 Q_EMIT valueChanged( QVariant::fromValue( DateRange( v, u ) ) );
722 }
723
724 void
on_spinBox_ValueInt_valueChanged(int v)725 ConstraintTypes::TagMatchEditWidget::on_spinBox_ValueInt_valueChanged( int v )
726 {
727 Q_EMIT valueChanged( QVariant( v ) );
728 }
729
730 void
on_lineEdit_StringValue_textChanged(const QString & v)731 ConstraintTypes::TagMatchEditWidget::on_lineEdit_StringValue_textChanged( const QString& v )
732 {
733 Q_EMIT valueChanged( QVariant( v ) );
734 }
735
736 void
on_rating_RatingValue_ratingChanged(int v)737 ConstraintTypes::TagMatchEditWidget::on_rating_RatingValue_ratingChanged( int v )
738 {
739 Q_EMIT valueChanged( QVariant( v ) );
740 }
741
742 void
on_timeEdit_TimeValue_timeChanged(const QTime & t)743 ConstraintTypes::TagMatchEditWidget::on_timeEdit_TimeValue_timeChanged( const QTime& t )
744 {
745 int v = QTime(0, 0, 0).msecsTo( t );
746 Q_EMIT valueChanged( QVariant( v ) );
747 }
748
749 void
slotUpdateComboBoxLabels(int value)750 ConstraintTypes::TagMatchEditWidget::slotUpdateComboBoxLabels( int value )
751 {
752 ui.comboBox_ValueDateUnit->setItemText(0, i18ncp("within the last %1 days", "day", "days", value));
753 ui.comboBox_ValueDateUnit->setItemText(1, i18ncp("within the last %1 months", "month", "months", value));
754 ui.comboBox_ValueDateUnit->setItemText(2, i18ncp("within the last %1 years", "year", "years", value));
755 }
756