Akonadi Contacts

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

KDE's Doxygen guidelines are available online.