FlightGear next
AircraftProxyModel.cxx
Go to the documentation of this file.
2
3#include <QSettings>
4#include <QDebug>
5
8
9#include <simgear/package/Package.hxx>
10
11AircraftProxyModel::AircraftProxyModel(QObject *pr, QAbstractItemModel * source) :
12 QSortFilterProxyModel(pr)
13{
14 m_ratings = {4, 4, 4, 4};
15 setSourceModel(source);
16 setSortCaseSensitivity(Qt::CaseInsensitive);
17 setFilterCaseSensitivity(Qt::CaseInsensitive);
18
19 // important we sort on the primary name role and not Qt::DisplayRole
20 // otherwise the aircraft jump when switching variant
22
23 setDynamicSortFilter(true);
24
25 // kick off initial sort
26 sort(0);
27
28 connect(this, &QAbstractItemModel::rowsInserted, this, &AircraftProxyModel::countChanged);
29 connect(this, &QAbstractItemModel::rowsRemoved, this, &AircraftProxyModel::countChanged);
30 connect(this, &QAbstractItemModel::modelReset, this, &AircraftProxyModel::countChanged);
31}
32
34{
35 if (ratings == m_ratings)
36 return;
37 m_ratings = ratings;
38 invalidate();
39 emit ratingsChanged();
40 emit summaryTextChanged();
41}
42
44{
45 m_filterString = s;
46
47 m_filterProps = new SGPropertyNode;
48 int index = 0;
49#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
50 Q_FOREACH (QString term, s.split(QRegularExpression("\\W+"), Qt::SkipEmptyParts)) {
51#else
52 Q_FOREACH (QString term, s.split(QRegularExpression("\\W+"), QString::SkipEmptyParts)) {
53#endif
54 m_filterProps->getNode("all-of/text", index++, true)->setStringValue(term.toStdString());
55 }
56
57 invalidate();
58 emit countChanged();
59}
60
62{
63 auto sourceIndex = qobject_cast<AircraftItemModel*>(sourceModel())->indexOfAircraftURI(uri);
64 auto ourIndex = mapFromSource(sourceIndex);
65 if (!sourceIndex.isValid() || !ourIndex.isValid()) {
66 return -1;
67 }
68
69 return ourIndex.row();
70}
71
73{
74 qobject_cast<AircraftItemModel*>(sourceModel())->selectVariantForAircraftURI(uri);
75}
76
78{
79 if (e == m_ratingsFilter) {
80 return;
81 }
82
83 m_ratingsFilter = e;
84 invalidate();
86 emit summaryTextChanged();
87 emit countChanged();
88}
89
91{
92 const int unfilteredCount = sourceModel()->rowCount();
93 if (m_ratingsFilter) {
94 return tr("(%1 of %2 aircraft)").arg(rowCount()).arg(unfilteredCount);
95 }
96
97 return tr("(%1 aircraft)").arg(unfilteredCount);
98}
99
101{
102 return rowCount();
103}
104
106{
107 if (e == m_onlyShowInstalled) {
108 return;
109 }
110
111 m_onlyShowInstalled = e;
112 invalidate();
113}
114
116{
117 if (e == m_onlyShowWithUpdate)
118 return;
119
120 m_onlyShowWithUpdate = e;
121 invalidate();
122}
123
125{
126 if (e == m_onlyShowFavourites)
127 return;
128
129 m_onlyShowFavourites = e;
130 if (e) {
131 setDynamicSortFilter(false);
133 [this]() {
134 this->invalidate();
135 });
136 }
137 invalidate();
138}
139
140bool AircraftProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
141{
142 QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
143 QVariant v = index.data(AircraftPackageStatusRole);
144
145 if (!filterAircraft(index)) {
146 return false;
147 }
148
149 if (m_onlyShowInstalled) {
150 QVariant v = index.data(AircraftPackageStatusRole);
151 const auto status = static_cast<LocalAircraftCache::PackageStatus>(v.toInt());
153 return false;
154 }
155 }
156
157 if (m_onlyShowWithUpdate) {
158 QVariant v = index.data(AircraftPackageStatusRole);
159 const auto status = static_cast<LocalAircraftCache::PackageStatus>(v.toInt());
160 switch (status) {
164 return false; // no updated need / possible
165
166 // otherwise, show in the update list
167 default:
168 break;
169 }
170 }
171
172 if (m_onlyShowFavourites) {
173 if (!index.data(AircraftIsFavouriteRole).toBool())
174 return false;
175 }
176
177 // if there is no search active, i.e we are browsing, we might apply the
178 // ratings filter.
179 if (m_filterString.isEmpty() && !m_onlyShowInstalled && m_ratingsFilter) {
180 for (int i=0; i<m_ratings.size(); ++i) {
181 if (m_ratings.at(i) > index.data(AircraftRatingRole + i).toInt()) {
182 return false;
183 }
184 }
185 }
186
187 return true;
188}
189
190bool AircraftProxyModel::filterAircraft(const QModelIndex &sourceIndex) const
191{
192 if (m_filterString.isEmpty()) {
193 return true;
194 }
195
196 simgear::pkg::PackageRef pkg = sourceIndex.data(AircraftPackageRefRole).value<simgear::pkg::PackageRef>();
197 if (pkg) {
198 return pkg->matches(m_filterProps.ptr());
199 }
200
201 QString baseName = sourceIndex.data(Qt::DisplayRole).toString();
202 if (baseName.contains(m_filterString, Qt::CaseInsensitive)) {
203 return true;
204 }
205
206 QString longDesc = sourceIndex.data(AircraftLongDescriptionRole).toString();
207 if (longDesc.contains(m_filterString, Qt::CaseInsensitive)) {
208 return true;
209 }
210
211 const int variantCount = sourceIndex.data(AircraftVariantCountRole).toInt();
212 for (int variant = 0; variant < variantCount; ++variant) {
213 QString desc = sourceIndex.data(AircraftVariantDescriptionRole + variant).toString();
214 if (desc.contains(m_filterString, Qt::CaseInsensitive)) {
215 return true;
216 }
217 }
218
219 return false;
220}
221
223{
224 QSettings settings;
225 m_ratingsFilter = settings.value("enable-ratings-filter", true).toBool();
226 QVariantList vRatings = settings.value("ratings-filter").toList();
227 if (vRatings.size() == 4) {
228 for (int i=0; i < 4; ++i) {
229 m_ratings[i] = vRatings.at(i).toInt();
230 }
231 }
232
233 invalidate();
234}
235
237{
238 QSettings settings;
239 settings.setValue("enable-ratings-filter", m_ratingsFilter);
240 QVariantList vRatings;
241 for (int i=0; i < 4; ++i) {
242 vRatings.append(m_ratings.at(i));
243 }
244 settings.setValue("ratings-filter", vRatings);
245}
246
253bool AircraftProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
254{
255 const QString variantLeft = left.data(AircraftVariantDescriptionRole).toString();
256 const QString variantRight = right.data(AircraftVariantDescriptionRole).toString();
257
258 // we're comparing by default by variantDescriptionRole but when the variantDescriptionRole
259 // is equal (e.g. two the same aircrafts installed from different sources - fgaddon + git)
260 // we sort them by the AircraftURIRole. This ensures that the order of the same
261 // items in the view is constant
262 const int c = QString::compare(variantLeft, variantRight, Qt::CaseInsensitive);
263 if (c == 0) {
264 const QString uriLeft = left.data(AircraftURIRole).toString();
265 const QString uriRight = right.data(AircraftURIRole).toString();
266 return QString::localeAwareCompare(uriLeft, uriRight) < 0;
267 } else {
268 return c < 0;
269 }
270}
const int AircraftVariantCountRole
const int AircraftRatingRole
const int AircraftVariantDescriptionRole
const int AircraftLongDescriptionRole
const int AircraftPackageRefRole
const int AircraftURIRole
const int AircraftIsFavouriteRole
const int AircraftPackageStatusRole
#define i(x)
void setRatingFilterEnabled(bool e)
Q_INVOKABLE void saveRatingsSettings()
void ratingsFilterEnabledChanged()
void setRatings(QList< int > ratings)
void setHaveUpdateFilterEnabled(bool e)
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
void setInstalledFilterEnabled(bool e)
Q_INVOKABLE void loadRatingsSettings()
AircraftProxyModel(QObject *pr, QAbstractItemModel *source)
Q_INVOKABLE void setAircraftFilterString(QString s)
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
Custom sorting based on aircraft variants and URI.
Q_INVOKABLE int indexForURI(QUrl uri) const
Compute the row (index in QML / ListView speak) based on an aircraft URI.
Q_INVOKABLE void selectVariantForAircraftURI(QUrl uri)
void changed(QUrl u)
static FavouriteAircraftData * instance()
static int status