Akonadi Contacts

contactsfilterproxymodel.cpp
1/*
2 This file is part of Akonadi Contact.
3
4 SPDX-FileCopyrightText: 2009 Tobias Koenig <tokoe@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "contactsfilterproxymodel.h"
10
11#include "contactstreemodel.h"
12
13#include <Akonadi/EntityTreeModel>
14#include <KContacts/Addressee>
15#include <KContacts/ContactGroup>
16#include <TextUtils/ConvertText>
17
18using namespace Akonadi;
19
20static bool contactMatchesFilter(const KContacts::Addressee &contact, const QString &filterString, ContactsFilterProxyModel::MatchFilterContactFlag flag);
21static bool contactGroupMatchesFilter(const KContacts::ContactGroup &group, const QString &filterString);
22
23class Akonadi::ContactsFilterProxyModelPrivate
24{
25public:
26 ContactsFilterProxyModelPrivate()
27 : flags({})
28 {
29 }
30
31 QString mFilter;
33 ContactsFilterProxyModel::MatchFilterContactFlag matchFilterFlag = ContactsFilterProxyModel::MatchFilterContactFlag::All;
34 bool mExcludeVirtualCollections = false;
35};
36
38 : QSortFilterProxyModel(parent)
39 , d(new ContactsFilterProxyModelPrivate)
40{
41 // contact names should be sorted correctly
44}
45
47
49{
50 d->mFilter = filter;
52}
53
54bool ContactsFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
55{
56 const QModelIndex index = sourceModel()->index(row, 0, parent);
57 if (d->mExcludeVirtualCollections) {
59 if (collection.isValid() && collection.isVirtual()) {
60 return false;
61 }
62 }
63
64 if ((d->mFilter.isEmpty()) && (!(d->flags & ContactsFilterProxyModel::HasEmail))) {
65 return true;
66 }
67 const QString filterStr = TextUtils::ConvertText::normalize(d->mFilter);
69
70 if (item.hasPayload<KContacts::Addressee>()) {
71 const auto contact = item.payload<KContacts::Addressee>();
72 if (d->flags & ContactsFilterProxyModel::HasEmail) {
73 if (contact.emails().isEmpty()) {
74 return false;
75 }
76 }
77 if (!d->mFilter.isEmpty()) {
78 return contactMatchesFilter(contact, filterStr, d->matchFilterFlag);
79 }
80 } else {
81 if (!d->mFilter.isEmpty()) {
82 if (item.hasPayload<KContacts::ContactGroup>()) {
83 const auto group = item.payload<KContacts::ContactGroup>();
84 return contactGroupMatchesFilter(group, filterStr);
85 }
86 }
87 }
88
89 return true;
90}
91
92bool ContactsFilterProxyModel::lessThan(const QModelIndex &leftIndex, const QModelIndex &rightIndex) const
93{
94 const QDate leftDate = leftIndex.data(ContactsTreeModel::DateRole).toDate();
95 const QDate rightDate = rightIndex.data(ContactsTreeModel::DateRole).toDate();
96 if (leftDate.isValid() && rightDate.isValid()) {
97 if (leftDate.month() < rightDate.month()) {
98 return true;
99 } else if (leftDate.month() == rightDate.month()) {
100 if (leftDate.day() < rightDate.day()) {
101 return true;
102 }
103 } else {
104 return false;
105 }
106 }
107
108 return QSortFilterProxyModel::lessThan(leftIndex, rightIndex);
109}
110
111void ContactsFilterProxyModel::setMatchFilterContactFlag(ContactsFilterProxyModel::MatchFilterContactFlag flag)
112{
113 d->matchFilterFlag = flag;
114}
115
120
122{
123 if (exclude != d->mExcludeVirtualCollections) {
124 d->mExcludeVirtualCollections = exclude;
126 }
127}
128
129Qt::ItemFlags ContactsFilterProxyModel::flags(const QModelIndex &index) const
130{
131 if (!index.isValid()) {
132 // Don't crash
133 return Qt::NoItemFlags;
134 }
136 if (collection.isValid()) {
138 }
140}
141
142static bool addressMatchesFilter(const KContacts::Address &address, const QString &filterString)
143{
144 if (address.street().contains(filterString, Qt::CaseInsensitive)) {
145 return true;
146 }
147
148 if (address.locality().contains(filterString, Qt::CaseInsensitive)) {
149 return true;
150 }
151
152 if (address.region().contains(filterString, Qt::CaseInsensitive)) {
153 return true;
154 }
155
156 if (address.postalCode().contains(filterString, Qt::CaseInsensitive)) {
157 return true;
158 }
159
160 if (address.country().contains(filterString, Qt::CaseInsensitive)) {
161 return true;
162 }
163
164 if (address.label().contains(filterString, Qt::CaseInsensitive)) {
165 return true;
166 }
167
168 if (address.postOfficeBox().contains(filterString, Qt::CaseInsensitive)) {
169 return true;
170 }
171
172 return false;
173}
174
175static bool contactMatchesFilter(const KContacts::Addressee &contact, const QString &filterString, ContactsFilterProxyModel::MatchFilterContactFlag flag)
176{
177 if (TextUtils::ConvertText::normalize(contact.assembledName()).contains(filterString, Qt::CaseInsensitive)) {
178 return true;
179 }
180
181 if (TextUtils::ConvertText::normalize(contact.formattedName()).contains(filterString, Qt::CaseInsensitive)) {
182 return true;
183 }
184
185 if (TextUtils::ConvertText::normalize(contact.nickName()).contains(filterString, Qt::CaseInsensitive)) {
186 return true;
187 }
188
189 int count = 0;
190 if (flag == ContactsFilterProxyModel::MatchFilterContactFlag::All) {
191 if (contact.birthday().toString().contains(filterString, Qt::CaseInsensitive)) {
192 return true;
193 }
194 const KContacts::Address::List addresses = contact.addresses();
195 count = addresses.count();
196 for (int i = 0; i < count; ++i) {
197 if (addressMatchesFilter(addresses.at(i), filterString)) {
198 return true;
199 }
200 }
201
202 const KContacts::PhoneNumber::List phoneNumbers = contact.phoneNumbers();
203 count = phoneNumbers.count();
204 for (int i = 0; i < count; ++i) {
205 if (phoneNumbers.at(i).number().contains(filterString, Qt::CaseInsensitive)) {
206 return true;
207 }
208 }
209 }
210
211 const QStringList emails = contact.emails();
212 count = emails.count();
213 for (int i = 0; i < count; ++i) {
214 if (TextUtils::ConvertText::normalize(emails.at(i)).contains(filterString, Qt::CaseInsensitive)) {
215 return true;
216 }
217 }
218
219 if (flag == ContactsFilterProxyModel::MatchFilterContactFlag::All) {
220 const QStringList categories = contact.categories();
221 count = categories.count();
222 for (int i = 0; i < count; ++i) {
223 if (categories.at(i).contains(filterString, Qt::CaseInsensitive)) {
224 return true;
225 }
226 }
227 if (contact.mailer().contains(filterString, Qt::CaseInsensitive)) {
228 return true;
229 }
230
231 if (TextUtils::ConvertText::normalize(contact.title()).contains(filterString, Qt::CaseInsensitive)) {
232 return true;
233 }
234
235 if (contact.role().contains(filterString, Qt::CaseInsensitive)) {
236 return true;
237 }
238
239 if (TextUtils::ConvertText::normalize(contact.organization()).contains(filterString, Qt::CaseInsensitive)) {
240 return true;
241 }
242
243 if (TextUtils::ConvertText::normalize(contact.department()).contains(filterString, Qt::CaseInsensitive)) {
244 return true;
245 }
246
247 if (TextUtils::ConvertText::normalize(contact.note()).contains(filterString, Qt::CaseInsensitive)) {
248 return true;
249 }
250
251 if (contact.url().url().url().contains(filterString, Qt::CaseInsensitive)) {
252 return true;
253 }
254
255 const QStringList customs = contact.customs();
256 count = customs.count();
257 for (int i = 0; i < count; ++i) {
258 if (customs.at(i).contains(filterString, Qt::CaseInsensitive)) {
259 return true;
260 }
261 }
262 }
263
264 return false;
265}
266
267bool contactGroupMatchesFilter(const KContacts::ContactGroup &group, const QString &filterString)
268{
269 if (TextUtils::ConvertText::normalize(group.name()).contains(filterString, Qt::CaseInsensitive)) {
270 return true;
271 }
272
273 const int count = group.dataCount();
274 for (int i = 0; i < count; ++i) {
275 if (TextUtils::ConvertText::normalize(group.data(i).name()).contains(filterString, Qt::CaseInsensitive)) {
276 return true;
277 }
278 if (TextUtils::ConvertText::normalize(group.data(i).email()).contains(filterString, Qt::CaseInsensitive)) {
279 return true;
280 }
281 }
282
283 return false;
284}
285
286#include "moc_contactsfilterproxymodel.cpp"
ContactsFilterProxyModel(QObject *parent=nullptr)
Creates a new contacts filter proxy model.
void setExcludeVirtualCollections(bool exclude)
Sets whether we want virtual collections to be filtered or not.
void setFilterFlags(ContactsFilterProxyModel::FilterFlags flags)
Sets the filter flags.
~ContactsFilterProxyModel() override
Destroys the contacts filter proxy model.
void setFilterString(const QString &filter)
Sets the filter that is used to filter for matching contacts and contact groups.
void setMatchFilterContactFlag(ContactsFilterProxyModel::MatchFilterContactFlag flag)
setMatchFilterContactFlag
@ DateRole
The QDate object for the current index.
T payload() const
QString mailer() const
QStringList emails() const
QString nickName() const
QString organization() const
QStringList customs() const
QString note() const
QDateTime birthday() const
QString role() const
QString title() const
QString assembledName() const
QString formattedName() const
ResourceLocatorUrl url() const
Address::List addresses() const
QStringList categories() const
QString department() const
PhoneNumber::List phoneNumbers() const
Data & data(int index)
QString name() const
A widget for editing the display name of a contact.
PostalAddress address(const QVariant &location)
int day() const const
bool isValid(int year, int month, int day)
int month() const const
QString toString(QStringView format, QCalendar cal) const const
const_reference at(qsizetype i) const const
qsizetype count() const const
QVariant data(int role) const const
bool isValid() const const
QObject * parent() const const
void setDynamicSortFilter(bool enable)
virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const const
virtual Qt::ItemFlags flags(const QModelIndex &index) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
void setSortLocaleAware(bool on)
virtual bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
CaseInsensitive
typedef ItemFlags
QDate toDate() const const
T value() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:13:20 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.