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
43}
44
46
48{
49 d->mFilter = filter;
51}
52
53bool ContactsFilterProxyModel::filterAcceptsRow(int row, const QModelIndex &parent) const
54{
55 const QModelIndex index = sourceModel()->index(row, 0, parent);
56 if (d->mExcludeVirtualCollections) {
58 if (collection.isValid() && collection.isVirtual()) {
59 return false;
60 }
61 }
62
63 if ((d->mFilter.isEmpty()) && (!(d->flags & ContactsFilterProxyModel::HasEmail))) {
64 return true;
65 }
66 const QString filterStr = TextUtils::ConvertText::normalize(d->mFilter);
68
69 if (item.hasPayload<KContacts::Addressee>()) {
70 const auto contact = item.payload<KContacts::Addressee>();
71 if (d->flags & ContactsFilterProxyModel::HasEmail) {
72 if (contact.emails().isEmpty()) {
73 return false;
74 }
75 }
76 if (!d->mFilter.isEmpty()) {
77 return contactMatchesFilter(contact, filterStr, d->matchFilterFlag);
78 }
79 } else {
80 if (!d->mFilter.isEmpty()) {
81 if (item.hasPayload<KContacts::ContactGroup>()) {
82 const auto group = item.payload<KContacts::ContactGroup>();
83 return contactGroupMatchesFilter(group, filterStr);
84 }
85 }
86 }
87
88 return true;
89}
90
91bool ContactsFilterProxyModel::lessThan(const QModelIndex &leftIndex, const QModelIndex &rightIndex) const
92{
93 const QDate leftDate = leftIndex.data(ContactsTreeModel::DateRole).toDate();
94 const QDate rightDate = rightIndex.data(ContactsTreeModel::DateRole).toDate();
95 if (leftDate.isValid() && rightDate.isValid()) {
96 if (leftDate.month() < rightDate.month()) {
97 return true;
98 } else if (leftDate.month() == rightDate.month()) {
99 if (leftDate.day() < rightDate.day()) {
100 return true;
101 }
102 } else {
103 return false;
104 }
105 }
106
107 return QSortFilterProxyModel::lessThan(leftIndex, rightIndex);
108}
109
110void ContactsFilterProxyModel::setMatchFilterContactFlag(ContactsFilterProxyModel::MatchFilterContactFlag flag)
111{
112 d->matchFilterFlag = flag;
113}
114
119
121{
122 if (exclude != d->mExcludeVirtualCollections) {
123 d->mExcludeVirtualCollections = exclude;
125 }
126}
127
128Qt::ItemFlags ContactsFilterProxyModel::flags(const QModelIndex &index) const
129{
130 if (!index.isValid()) {
131 // Don't crash
132 return Qt::NoItemFlags;
133 }
135 if (collection.isValid()) {
137 }
139}
140
141static bool addressMatchesFilter(const KContacts::Address &address, const QString &filterString)
142{
143 if (address.street().contains(filterString, Qt::CaseInsensitive)) {
144 return true;
145 }
146
147 if (address.locality().contains(filterString, Qt::CaseInsensitive)) {
148 return true;
149 }
150
151 if (address.region().contains(filterString, Qt::CaseInsensitive)) {
152 return true;
153 }
154
155 if (address.postalCode().contains(filterString, Qt::CaseInsensitive)) {
156 return true;
157 }
158
159 if (address.country().contains(filterString, Qt::CaseInsensitive)) {
160 return true;
161 }
162
163 if (address.label().contains(filterString, Qt::CaseInsensitive)) {
164 return true;
165 }
166
167 if (address.postOfficeBox().contains(filterString, Qt::CaseInsensitive)) {
168 return true;
169 }
170
171 return false;
172}
173
174static bool contactMatchesFilter(const KContacts::Addressee &contact, const QString &filterString, ContactsFilterProxyModel::MatchFilterContactFlag flag)
175{
176 if (TextUtils::ConvertText::normalize(contact.assembledName()).contains(filterString, Qt::CaseInsensitive)) {
177 return true;
178 }
179
180 if (TextUtils::ConvertText::normalize(contact.formattedName()).contains(filterString, Qt::CaseInsensitive)) {
181 return true;
182 }
183
184 if (TextUtils::ConvertText::normalize(contact.nickName()).contains(filterString, Qt::CaseInsensitive)) {
185 return true;
186 }
187
188 int count = 0;
189 if (flag == ContactsFilterProxyModel::MatchFilterContactFlag::All) {
190 if (contact.birthday().toString().contains(filterString, Qt::CaseInsensitive)) {
191 return true;
192 }
193 const KContacts::Address::List addresses = contact.addresses();
194 count = addresses.count();
195 for (int i = 0; i < count; ++i) {
196 if (addressMatchesFilter(addresses.at(i), filterString)) {
197 return true;
198 }
199 }
200
201 const KContacts::PhoneNumber::List phoneNumbers = contact.phoneNumbers();
202 count = phoneNumbers.count();
203 for (int i = 0; i < count; ++i) {
204 if (phoneNumbers.at(i).number().contains(filterString, Qt::CaseInsensitive)) {
205 return true;
206 }
207 }
208 }
209
210 const QStringList emails = contact.emails();
211 count = emails.count();
212 for (int i = 0; i < count; ++i) {
213 if (TextUtils::ConvertText::normalize(emails.at(i)).contains(filterString, Qt::CaseInsensitive)) {
214 return true;
215 }
216 }
217
218 if (flag == ContactsFilterProxyModel::MatchFilterContactFlag::All) {
219 const QStringList categories = contact.categories();
220 count = categories.count();
221 for (int i = 0; i < count; ++i) {
222 if (categories.at(i).contains(filterString, Qt::CaseInsensitive)) {
223 return true;
224 }
225 }
226 if (contact.mailer().contains(filterString, Qt::CaseInsensitive)) {
227 return true;
228 }
229
230 if (TextUtils::ConvertText::normalize(contact.title()).contains(filterString, Qt::CaseInsensitive)) {
231 return true;
232 }
233
234 if (contact.role().contains(filterString, Qt::CaseInsensitive)) {
235 return true;
236 }
237
238 if (TextUtils::ConvertText::normalize(contact.organization()).contains(filterString, Qt::CaseInsensitive)) {
239 return true;
240 }
241
242 if (TextUtils::ConvertText::normalize(contact.department()).contains(filterString, Qt::CaseInsensitive)) {
243 return true;
244 }
245
246 if (TextUtils::ConvertText::normalize(contact.note()).contains(filterString, Qt::CaseInsensitive)) {
247 return true;
248 }
249
250 if (contact.url().url().url().contains(filterString, Qt::CaseInsensitive)) {
251 return true;
252 }
253
254 const QStringList customs = contact.customs();
255 count = customs.count();
256 for (int i = 0; i < count; ++i) {
257 if (customs.at(i).contains(filterString, Qt::CaseInsensitive)) {
258 return true;
259 }
260 }
261 }
262
263 return false;
264}
265
266bool contactGroupMatchesFilter(const KContacts::ContactGroup &group, const QString &filterString)
267{
268 if (TextUtils::ConvertText::normalize(group.name()).contains(filterString, Qt::CaseInsensitive)) {
269 return true;
270 }
271
272 const int count = group.dataCount();
273 for (int i = 0; i < count; ++i) {
274 if (TextUtils::ConvertText::normalize(group.data(i).name()).contains(filterString, Qt::CaseInsensitive)) {
275 return true;
276 }
277 if (TextUtils::ConvertText::normalize(group.data(i).email()).contains(filterString, Qt::CaseInsensitive)) {
278 return true;
279 }
280 }
281
282 return false;
283}
284
285#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.
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
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 Sat Dec 21 2024 17:05:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.