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 #ifndef APG_TAGMATCH_CONSTRAINT
18 #define APG_TAGMATCH_CONSTRAINT
19 
20 #include "ui_TagMatchEditWidget.h"
21 
22 #include "Matching.h"
23 
24 #include "core/meta/forward_declarations.h"
25 
26 #include <QAbstractListModel>
27 #include <QBitArray>
28 #include <QHash>
29 #include <QString>
30 #include <QVariant>
31 
32 class Constraint;
33 class ConstraintFactoryEntry;
34 
35 namespace Collections {
36     class QueryMaker;
37 }
38 
39 namespace ConstraintTypes {
40 
41     class TagMatchFieldsModel;
42 
43     /* Puts tracks with the specified tag into the playlist.  "Tag" is used a
44      * bit loosely here; a more accurate term would probably be "metadata",
45      * since the matchable properties include parameters like "rating" and
46      * "last played" that aren't in the actual file that corresponds to the
47      * track being played. -- sth */
48 
49     class TagMatch : public MatchingConstraint {
50         Q_OBJECT
51 
52         /* support classes declared below */
53         class Comparer;
54 
55         public:
56             enum FieldTypes { FieldTypeInt, FieldTypeDate, FieldTypeString };
57             enum NumComparison { CompareNumLessThan, CompareNumEquals, CompareNumGreaterThan };
58             enum StrComparison { CompareStrEquals, CompareStrStartsWith, CompareStrEndsWith, CompareStrContains, CompareStrRegExp };
59             enum DateComparison { CompareDateBefore, CompareDateOn, CompareDateAfter, CompareDateWithin };
60 
61             static Constraint* createFromXml(QDomElement&, ConstraintNode*);
62             static Constraint* createNew(ConstraintNode*);
63             static ConstraintFactoryEntry* registerMe();
64 
65             QWidget* editWidget() const override;
66             void toXml(QDomDocument&, QDomElement&) const override;
67 
68             QString getName() const override;
69 
70             Collections::QueryMaker* initQueryMaker(Collections::QueryMaker*) const override;
71             double satisfaction(const Meta::TrackList&) const override;
72 
73             // Implementation of MatchingConstraint virtuals
74             const QBitArray whatTracksMatch( const Meta::TrackList& ) override;
75             int constraintMatchType() const override;
76 
77         private Q_SLOTS:
78             void setComparison( int );
79             void setField( const QString& );
80             void setInvert( bool );
81             void setStrictness( int );
82             void setValue( const QVariant& );
83 
84         private:
85             TagMatch( QDomElement&, ConstraintNode* );
86             explicit TagMatch( ConstraintNode* );
87             ~TagMatch() override;
88 
89             // constraint parameters
90             int m_comparison;
91             QString m_field;
92             bool m_invert;
93             double m_strictness;
94             QVariant m_value;
95 
96             // convenience classes
97             const Comparer* const m_comparer;
98             const TagMatchFieldsModel* const m_fieldsModel;
99 
100             // convenience functions
101             QString comparisonToString() const;
102             QString valueToString() const;
103 
104             bool matches( const Meta::TrackPtr ) const; // match values are fuzzily calculated
105             mutable QHash<Meta::TrackPtr, bool> m_matchCache; // internal cache for per-track true/false data
106 
107 
108         /* support class that does fuzzy comparisons */
109         class Comparer {
110             public:
111                 Comparer();
112                 ~Comparer();
113 
114                 double compareNum( const double, const int, const double, const double, const qint64 ) const;
115                 double compareStr( const QString&, const int, const QString& ) const;
116                 double compareDate( const uint, const int, const QVariant&, const double ) const;
117                 double compareLabels( const Meta::TrackPtr&, const int, const QString& ) const;
118 
119                 // rough inverses of the comparison
120                 uint rangeDate( const double ) const;
121                 int rangeNum( const double, const qint64 ) const;
122 
123             private:
124                 QHash<qint64, double> m_numFieldWeight;
125                 const double m_dateWeight;
126 
127                 double fuzzyProb( const double, const double, const double, const double ) const;
128         };
129 
130     };
131 
132     /* support class that manages data relationships for the various fields */
133     class TagMatchFieldsModel : public QAbstractListModel {
134         Q_OBJECT
135 
136         public:
137             TagMatchFieldsModel();
138             ~TagMatchFieldsModel() override;
139 
140             // required by QAbstractListModel
141             QVariant data( const QModelIndex&, int role = Qt::DisplayRole ) const override;
142             int rowCount( const QModelIndex& parent = QModelIndex() ) const override;
143 
144             bool contains( const QString& ) const;
145             int index_of( const QString& ) const;
146             QString field_at( int ) const;
147             qint64 meta_value_of( const QString& ) const;
148             QString pretty_name_of( const QString& ) const;
149             TagMatch::FieldTypes type_of( const QString& ) const;
150 
151         private:
152             QList<QString> m_fieldNames;
153             QHash<QString, TagMatch::FieldTypes> m_fieldTypes;
154             QHash<QString, qint64> m_fieldMetaValues;
155             QHash<QString, QString> m_fieldPrettyNames;
156     };
157 
158     class TagMatchEditWidget : public QWidget {
159         Q_OBJECT
160 
161         public:
162             TagMatchEditWidget( const int, const QString&, const bool, const int, const QVariant& );
163             ~TagMatchEditWidget();
164 
165         Q_SIGNALS:
166             void comparisonChanged( int );
167             void fieldChanged( const QString& );
168             void invertChanged( bool );
169             void strictnessChanged( int );
170             void valueChanged( const QVariant& );
171             void updated();
172 
173         private Q_SLOTS:
174             // comparison
175             void on_comboBox_ComparisonDate_currentIndexChanged( int );
176             void on_comboBox_ComparisonInt_currentIndexChanged( int );
177             void on_comboBox_ComparisonRating_currentIndexChanged( int );
178             void on_comboBox_ComparisonString_currentIndexChanged( int );
179             void on_comboBox_ComparisonTime_currentIndexChanged( int );
180 
181             // field
182             void on_comboBox_Field_currentIndexChanged( int );
183 
184             // invert
185             void on_checkBox_Invert_clicked( bool );
186 
187             // strictness
188             void on_slider_StrictnessDate_valueChanged( int );
189             void on_slider_StrictnessInt_valueChanged( int );
190             void on_slider_StrictnessRating_valueChanged( int );
191             void on_slider_StrictnessTime_valueChanged( int );
192 
193             // value
194             void on_kdatewidget_DateSpecific_changed( const QDate& );
195             void on_spinBox_ValueDateValue_valueChanged( int );
196             void on_comboBox_ValueDateUnit_currentIndexChanged( int );
197             void on_spinBox_ValueInt_valueChanged( int );
198             void on_lineEdit_StringValue_textChanged( const QString& );
199             void on_rating_RatingValue_ratingChanged( int );
200             void on_timeEdit_TimeValue_timeChanged( const QTime& );
201             void slotUpdateComboBoxLabels( int );
202 
203         private:
204             Ui::TagMatchEditWidget ui;
205             TagMatchFieldsModel* const m_fieldsModel;
206     };
207 
208 } // namespace ConstraintTypes
209 
210 typedef QPair<int,int> DateRange;
211 Q_DECLARE_METATYPE( DateRange )
212 
213 #endif
214