File indexing completed on 2023-11-26 08:16:44

0001 /*
0002  * Provide some filters on the account model
0003  *
0004  * Copyright (C) 2011 David Edmundson <kde@davidedmundson.co.uk>
0005  * Copyright (C) 2011 Martin Klapetek <martin dot klapetek at gmail dot com>
0006  * Copyright (C) 2012 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
0007  * Copyright (C) 2012 Dominik Cermak <d.cermak@arcor.de>
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Lesser General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2.1 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Lesser General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Lesser General Public
0020  * License along with this library; if not, write to the Free Software
0021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0022  */
0023 
0024 #include "contacts-filter-model.h"
0025 
0026 #include "types.h"
0027 
0028 #include <presence.h>
0029 
0030 #include "debug.h"
0031 
0032 
0033 class KTp::ContactsFilterModel::Private
0034 {
0035 public:
0036     Private(ContactsFilterModel *parent)
0037         : q(parent),
0038           presenceTypeFilterFlags(DoNotFilterByPresence),
0039           capabilityFilterFlags(DoNotFilterByCapability),
0040           subscriptionStateFilterFlags(DoNotFilterBySubscription),
0041           globalFilterMatchFlags(Qt::MatchContains),
0042           displayNameFilterMatchFlags(Qt::MatchContains),
0043           nicknameFilterMatchFlags(Qt::MatchContains),
0044           aliasFilterMatchFlags(Qt::MatchContains),
0045           groupsFilterMatchFlags(Qt::MatchContains),
0046           idFilterMatchFlags(Qt::MatchContains)
0047     {
0048     }
0049 
0050     ContactsFilterModel *q;
0051 
0052     PresenceTypeFilterFlags presenceTypeFilterFlags;
0053     CapabilityFilterFlags capabilityFilterFlags;
0054     SubscriptionStateFilterFlags subscriptionStateFilterFlags;
0055 
0056     QString globalFilterString;
0057     Qt::MatchFlags globalFilterMatchFlags;
0058 
0059     QString displayNameFilterString;
0060     QString nicknameFilterString;
0061     QString aliasFilterString;
0062     QString groupsFilterString;
0063     QString idFilterString;
0064     QStringList tubesFilterStrings;
0065     Qt::MatchFlags displayNameFilterMatchFlags;
0066     Qt::MatchFlags nicknameFilterMatchFlags;
0067     Qt::MatchFlags aliasFilterMatchFlags;
0068     Qt::MatchFlags groupsFilterMatchFlags;
0069     Qt::MatchFlags idFilterMatchFlags;
0070     Tp::AccountPtr accountFilter;
0071 
0072     bool filterAcceptsAccount(const QModelIndex &index) const;
0073     bool filterAcceptsContact(const QModelIndex &index) const;
0074     bool filterAcceptsGroup(const QModelIndex &index);
0075 
0076     void sourceModelParentIndexChanged(const QModelIndex &sourceIndex);
0077     void sourceModelIndexChanged(const QModelIndex &sourceIndex);
0078 };
0079 
0080 using namespace KTp;
0081 
0082 bool ContactsFilterModel::Private::filterAcceptsAccount(const QModelIndex &index) const
0083 {
0084     // Check capability
0085     if (capabilityFilterFlags != DoNotFilterByCapability) {
0086         if ((capabilityFilterFlags & FilterByTextChatCapability)
0087                 && !index.data(KTp::ContactCanTextChatRole).toBool()) {
0088             return false;
0089         }
0090         if ((capabilityFilterFlags & FilterByAudioCallCapability)
0091                 && !index.data(KTp::ContactCanAudioCallRole).toBool()) {
0092             return false;
0093         }
0094         if ((capabilityFilterFlags & FilterByVideoCallCapability)
0095                 && !index.data(KTp::ContactCanVideoCallRole).toBool()) {
0096             return false;
0097         }
0098         if ((capabilityFilterFlags & FilterByFileTransferCapability)
0099                 && !index.data(KTp::ContactCanFileTransferRole).toBool()) {
0100             return false;
0101         }
0102         if (capabilityFilterFlags & FilterByTubes) {
0103             Q_FOREACH(const QString &tube, index.data(KTp::ContactTubesRole).toStringList()) {
0104                 if (tubesFilterStrings.contains(tube)) {
0105                     return true;
0106                 }
0107             }
0108             return false;
0109         }
0110     }
0111 
0112     return true;
0113 }
0114 
0115 bool ContactsFilterModel::Private::filterAcceptsContact(const QModelIndex &index) const
0116 {
0117     // Presence type, capability and subscription state are always checked
0118     // Then if global filter is set we can return true if a result is found for
0119     // any of the strings, otherwise we check all of them
0120 
0121     Q_ASSERT(index.isValid());
0122     if (!index.isValid()) {
0123         return false;
0124     }
0125 
0126     //always return all subcontacts of a metacontact
0127     if (index.parent().isValid() && index.parent().data(KTp::RowTypeRole).toUInt() == KTp::PersonRowType) {
0128         return true;
0129     }
0130 
0131     // Check presence type
0132     if (presenceTypeFilterFlags != DoNotFilterByPresence) {
0133         switch (static_cast<Tp::ConnectionPresenceType>(index.data(KTp::ContactPresenceTypeRole).toUInt())) {
0134         case Tp::ConnectionPresenceTypeUnset:
0135             if (presenceTypeFilterFlags & HidePresenceTypeUnset) {
0136                 return false;
0137             }
0138             break;
0139         case Tp::ConnectionPresenceTypeOffline:
0140             if (presenceTypeFilterFlags & HidePresenceTypeOffline) {
0141                 return false;
0142             }
0143             break;
0144         case Tp::ConnectionPresenceTypeAvailable:
0145             if (presenceTypeFilterFlags & HidePresenceTypeAvailable) {
0146                 return false;
0147             }
0148             break;
0149         case Tp::ConnectionPresenceTypeAway:
0150             if (presenceTypeFilterFlags & HidePresenceTypeAway) {
0151                 return false;
0152             }
0153             break;
0154         case Tp::ConnectionPresenceTypeExtendedAway:
0155             if (presenceTypeFilterFlags & HidePresenceTypeExtendedAway) {
0156                 return false;
0157             }
0158             break;
0159         case Tp::ConnectionPresenceTypeHidden:
0160             if (presenceTypeFilterFlags & HidePresenceTypeHidden) {
0161                 return false;
0162             }
0163             break;
0164         case Tp::ConnectionPresenceTypeBusy:
0165             if (presenceTypeFilterFlags & HidePresenceTypeBusy) {
0166                 return false;
0167             }
0168             break;
0169         case Tp::ConnectionPresenceTypeUnknown:
0170             if (presenceTypeFilterFlags & HidePresenceTypeUnknown) {
0171                 return false;
0172             }
0173             break;
0174         case Tp::ConnectionPresenceTypeError:
0175             if (presenceTypeFilterFlags & HidePresenceTypeError) {
0176                 return false;
0177             }
0178             break;
0179         default:
0180             //This should never happen
0181             Q_ASSERT(false);
0182             return false;
0183         }
0184     }
0185 
0186 
0187     // Check capability
0188     if (capabilityFilterFlags != DoNotFilterByCapability) {
0189         if ((capabilityFilterFlags & FilterByTextChatCapability)
0190                 && !index.data(KTp::ContactCanTextChatRole).toBool()) {
0191             return false;
0192         }
0193         if ((capabilityFilterFlags & FilterByAudioCallCapability)
0194                 && !index.data(KTp::ContactCanAudioCallRole).toBool()) {
0195             return false;
0196         }
0197         if ((capabilityFilterFlags & FilterByVideoCallCapability)
0198                 && !index.data(KTp::ContactCanVideoCallRole).toBool()) {
0199             return false;
0200         }
0201         if ((capabilityFilterFlags & FilterByFileTransferCapability)
0202                 && !index.data(KTp::ContactCanFileTransferRole).toBool()) {
0203             return false;
0204         }
0205         if (capabilityFilterFlags & FilterByTubes) {
0206             if (!tubesFilterStrings.isEmpty()) {
0207                 bool tubeFound = false;
0208                 Q_FOREACH(const QString &tube, index.data(KTp::ContactTubesRole).toStringList()) {
0209                     if (tubesFilterStrings.contains(tube)) {
0210                         tubeFound = true;
0211                     }
0212                 }
0213                 if (!tubeFound) {
0214                     return false;
0215                 }
0216             }
0217         }
0218     }
0219 
0220 
0221     // Check subscription state
0222     if (subscriptionStateFilterFlags != DoNotFilterBySubscription) {
0223         switch (index.data(KTp::ContactSubscriptionStateRole).toUInt()) {
0224         case Tp::Contact::PresenceStateNo:
0225             if (subscriptionStateFilterFlags & HideSubscriptionStateNo) {
0226                 return false;
0227             }
0228             break;
0229         case Tp::Contact::PresenceStateAsk:
0230             if (subscriptionStateFilterFlags & HideSubscriptionStateAsk) {
0231                 return false;
0232             }
0233             break;
0234         case Tp::Contact::PresenceStateYes:
0235             if (subscriptionStateFilterFlags & HideSubscriptionStateYes) {
0236                 return false;
0237             }
0238             break;
0239         default:
0240             //This should never happen
0241             Q_ASSERT(false);
0242             return false;
0243         }
0244 
0245         switch (index.data(KTp::ContactPublishStateRole).toUInt()) {
0246         case Tp::Contact::PresenceStateNo:
0247             if (subscriptionStateFilterFlags & HidePublishStateNo) {
0248                 return false;
0249             }
0250             break;
0251         case Tp::Contact::PresenceStateAsk:
0252             if (subscriptionStateFilterFlags & HidePublishStateAsk) {
0253                 return false;
0254             }
0255             break;
0256         case Tp::Contact::PresenceStateYes:
0257             if (subscriptionStateFilterFlags & HidePublishStateYes) {
0258                 return false;
0259             }
0260             break;
0261         default:
0262             //This should never happen
0263             Q_ASSERT(false);
0264             return false;
0265         }
0266 
0267         if (index.data(KTp::ContactIsBlockedRole).toBool()) {
0268             if (subscriptionStateFilterFlags & HideBlocked) {
0269                 return false;
0270             }
0271         } else {
0272             if (subscriptionStateFilterFlags & HideNonBlocked) {
0273                 return false;
0274             }
0275         }
0276     }
0277 
0278     if (!globalFilterString.isEmpty()) {
0279         // Check global filter (search on all the roles)
0280 
0281         // Check display name
0282         if (!q->match(index, Qt::DisplayRole, globalFilterString, 1, globalFilterMatchFlags).isEmpty()) {
0283             return true;
0284         }
0285 
0286         // check groups
0287         // TODO Check if exact match on a single group works
0288         if (!q->match(index, KTp::ContactGroupsRole, globalFilterString, 1, globalFilterMatchFlags).isEmpty()) {
0289             return true;
0290         }
0291 
0292         // Check id
0293         if (!q->match(index, KTp::IdRole, globalFilterString, 1, globalFilterMatchFlags).isEmpty()) {
0294             return true;
0295         }
0296 
0297         return false;
0298     } else {
0299         // Check on single filters
0300         // Check display name
0301         if (!displayNameFilterString.isEmpty()) {
0302             if (q->match(index, Qt::DisplayRole, displayNameFilterString, 1, displayNameFilterMatchFlags).isEmpty()) {
0303                 return false;
0304             }
0305         }
0306         // check groups
0307         // TODO Check if exact match on a single group works
0308         if (!groupsFilterString.isEmpty()) {
0309             if (q->match(index, KTp::ContactGroupsRole, groupsFilterString, 1, groupsFilterMatchFlags).isEmpty()) {
0310                 return false;
0311             }
0312         }
0313 
0314         // Check id
0315         if (!idFilterString.isEmpty()) {
0316             if (q->match(index, KTp::IdRole, idFilterString, 1, idFilterMatchFlags).isEmpty()) {
0317                 return false;
0318             }
0319         }
0320     }
0321 
0322     //check account
0323     if (accountFilter) {
0324         if(index.data(KTp::AccountRole).value<Tp::AccountPtr>()->uniqueIdentifier() != accountFilter->uniqueIdentifier()) {
0325             return false;
0326         }
0327     }
0328 
0329     return true;
0330 }
0331 
0332 bool ContactsFilterModel::Private::filterAcceptsGroup(const QModelIndex &/*index*/)
0333 {
0334     return true;
0335 }
0336 
0337 void ContactsFilterModel::Private::sourceModelParentIndexChanged(const QModelIndex &sourceIndex)
0338 {
0339     //if parent is a group heading
0340     if (sourceIndex.isValid() &&
0341         (sourceIndex.data(KTp::RowTypeRole).toInt() == KTp::GroupRowType ||
0342         sourceIndex.data(KTp::RowTypeRole).toInt() == KTp::AccountRowType)) {
0343 
0344 
0345         q->dataChanged(q->mapFromSource(sourceIndex), q->mapFromSource(sourceIndex));
0346     }
0347 }
0348 
0349 void ContactsFilterModel::Private::sourceModelIndexChanged(const QModelIndex &sourceIndex)
0350 {
0351     sourceModelParentIndexChanged(sourceIndex.parent());
0352 }
0353 
0354 
0355 ContactsFilterModel::ContactsFilterModel(QObject *parent)
0356     : QSortFilterProxyModel(parent),
0357       d(new Private(this))
0358 {
0359     sort(0); //sort always
0360     setDynamicSortFilter(true);
0361 }
0362 
0363 ContactsFilterModel::~ContactsFilterModel()
0364 {
0365     delete d;
0366 }
0367 
0368 QVariant ContactsFilterModel::data(const QModelIndex &index, int role) const
0369 {
0370     if (!index.isValid()) {
0371         return QVariant();
0372     }
0373 
0374     QModelIndex sourceIndex = mapToSource(index);
0375     if (!sourceIndex.isValid()) {
0376         return QVariant();
0377     }
0378 
0379     if (role == KTp::HeaderOnlineUsersRole) {
0380         return rowCount(index);
0381     } else if (role == KTp::HeaderTotalUsersRole) {
0382         return sourceModel()->rowCount(sourceIndex);
0383     }
0384 
0385     // In all other cases just delegate it to the source model
0386     return sourceModel()->data(sourceIndex, role);
0387 }
0388 
0389 void ContactsFilterModel::setSourceModel(QAbstractItemModel *sourceModel)
0390 {
0391     // Disconnect the previous source model
0392     if (this->sourceModel()) {
0393         disconnect(this->sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0394                 this, SLOT(sourceModelIndexChanged(QModelIndex)));
0395         disconnect(this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
0396                 this, SLOT(sourceModelParentIndexChanged(QModelIndex)));
0397         disconnect(this->sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
0398                 this, SLOT(sourceModelParentIndexChanged(QModelIndex)));
0399         disconnect(this->sourceModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
0400                 this, SLOT(sourceModelParentIndexChanged(QModelIndex)));
0401     }
0402 
0403     if (sourceModel) {
0404         QSortFilterProxyModel::setSourceModel(sourceModel);
0405 
0406         // Connect the new source model
0407         connect(this->sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0408                 this, SLOT(sourceModelIndexChanged(QModelIndex)));
0409         connect(this->sourceModel(), SIGNAL(rowsInserted(QModelIndex,int,int)),
0410                 this, SLOT(sourceModelParentIndexChanged(QModelIndex)));
0411         connect(this->sourceModel(), SIGNAL(rowsRemoved(QModelIndex,int,int)),
0412                 this, SLOT(sourceModelParentIndexChanged(QModelIndex)));
0413         connect(this->sourceModel(), SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
0414                 this, SLOT(sourceModelParentIndexChanged(QModelIndex)));
0415     }
0416 }
0417 
0418 void ContactsFilterModel::invalidateFilter()
0419 {
0420     QSortFilterProxyModel::invalidateFilter();
0421 }
0422 
0423 ContactsFilterModel::PresenceTypeFilterFlags ContactsFilterModel::presenceTypeFilterFlags() const
0424 {
0425     return d->presenceTypeFilterFlags;
0426 }
0427 
0428 void ContactsFilterModel::clearPresenceTypeFilterFlags()
0429 {
0430     setPresenceTypeFilterFlags(DoNotFilterByPresence);
0431 }
0432 
0433 void ContactsFilterModel::setPresenceTypeFilterFlags(ContactsFilterModel::PresenceTypeFilterFlags presenceTypeFilterFlags)
0434 {
0435     if (d->presenceTypeFilterFlags != presenceTypeFilterFlags) {
0436         d->presenceTypeFilterFlags = presenceTypeFilterFlags;
0437         invalidateFilter();
0438         Q_EMIT presenceTypeFilterFlagsChanged(presenceTypeFilterFlags);
0439     }
0440 }
0441 
0442 ContactsFilterModel::CapabilityFilterFlags ContactsFilterModel::capabilityFilterFlags() const
0443 {
0444     return d->capabilityFilterFlags;
0445 }
0446 
0447 void ContactsFilterModel::clearCapabilityFilterFlags()
0448 {
0449     setCapabilityFilterFlags(DoNotFilterByCapability);
0450 }
0451 
0452 void ContactsFilterModel::setCapabilityFilterFlags(ContactsFilterModel::CapabilityFilterFlags capabilityFilterFlags)
0453 {
0454     if (d->capabilityFilterFlags != capabilityFilterFlags) {
0455         d->capabilityFilterFlags = capabilityFilterFlags;
0456         invalidateFilter();
0457         Q_EMIT capabilityFilterFlagsChanged(capabilityFilterFlags);
0458     }
0459 }
0460 
0461 ContactsFilterModel::SubscriptionStateFilterFlags ContactsFilterModel::subscriptionStateFilterFlags() const
0462 {
0463     return d->subscriptionStateFilterFlags;
0464 }
0465 
0466 void ContactsFilterModel::clearSubscriptionStateFilterFlags()
0467 {
0468     setSubscriptionStateFilterFlags(DoNotFilterBySubscription);
0469 }
0470 
0471 void ContactsFilterModel::setSubscriptionStateFilterFlags(ContactsFilterModel::SubscriptionStateFilterFlags subscriptionStateFilterFlags)
0472 {
0473     if (d->subscriptionStateFilterFlags != subscriptionStateFilterFlags) {
0474         d->subscriptionStateFilterFlags = subscriptionStateFilterFlags;
0475         invalidateFilter();
0476         Q_EMIT subscriptionStateFilterFlagsChanged(subscriptionStateFilterFlags);
0477     }
0478 }
0479 
0480 QString ContactsFilterModel::globalFilterString() const
0481 {
0482     return d->globalFilterString;
0483 }
0484 
0485 void ContactsFilterModel::clearGlobalFilterString()
0486 {
0487     setGlobalFilterString(QString());
0488 }
0489 
0490 void ContactsFilterModel::setGlobalFilterString(const QString &globalFilterString)
0491 {
0492     if (d->globalFilterString != globalFilterString) {
0493         d->globalFilterString = globalFilterString;
0494         invalidateFilter();
0495         Q_EMIT globalFilterStringChanged(globalFilterString);
0496     }
0497 }
0498 
0499 Qt::MatchFlags ContactsFilterModel::globalFilterMatchFlags() const
0500 {
0501     return d->globalFilterMatchFlags;
0502 }
0503 
0504 void ContactsFilterModel::resetGlobalFilterMatchFlags()
0505 {
0506     setGlobalFilterMatchFlags(Qt::MatchStartsWith | Qt::MatchWrap);
0507 }
0508 
0509 void ContactsFilterModel::setGlobalFilterMatchFlags(Qt::MatchFlags globalFilterMatchFlags)
0510 {
0511     if (d->globalFilterMatchFlags != globalFilterMatchFlags) {
0512         d->globalFilterMatchFlags = globalFilterMatchFlags;
0513         invalidateFilter();
0514         Q_EMIT globalFilterMatchFlagsChanged(globalFilterMatchFlags);
0515     }
0516 }
0517 
0518 QString ContactsFilterModel::displayNameFilterString() const
0519 {
0520     return d->displayNameFilterString;
0521 }
0522 
0523 void ContactsFilterModel::clearDisplayNameFilterString()
0524 {
0525     setDisplayNameFilterString(QString());
0526 }
0527 
0528 void ContactsFilterModel::setDisplayNameFilterString(const QString &displayNameFilterString)
0529 {
0530     if (d->displayNameFilterString != displayNameFilterString) {
0531         d->displayNameFilterString = displayNameFilterString;
0532         invalidateFilter();
0533         Q_EMIT displayNameFilterStringChanged(displayNameFilterString);
0534     }
0535 }
0536 
0537 Qt::MatchFlags ContactsFilterModel::displayNameFilterMatchFlags() const
0538 {
0539     return d->displayNameFilterMatchFlags;
0540 }
0541 
0542 void ContactsFilterModel::resetDisplayNameFilterMatchFlags()
0543 {
0544     setDisplayNameFilterMatchFlags(Qt::MatchStartsWith | Qt::MatchWrap);
0545 }
0546 
0547 void ContactsFilterModel::setDisplayNameFilterMatchFlags(Qt::MatchFlags displayNameFilterMatchFlags)
0548 {
0549     if (d->displayNameFilterMatchFlags != displayNameFilterMatchFlags) {
0550         d->displayNameFilterMatchFlags = displayNameFilterMatchFlags;
0551         invalidateFilter();
0552         Q_EMIT displayNameFilterMatchFlagsChanged(displayNameFilterMatchFlags);
0553     }
0554 }
0555 
0556 QString ContactsFilterModel::nicknameFilterString() const
0557 {
0558     return d->nicknameFilterString;
0559 }
0560 
0561 void ContactsFilterModel::clearNicknameFilterString()
0562 {
0563     setNicknameFilterString(QString());
0564 }
0565 
0566 void ContactsFilterModel::setNicknameFilterString(const QString &nicknameFilterString)
0567 {
0568     if (d->nicknameFilterString != nicknameFilterString) {
0569         d->nicknameFilterString = nicknameFilterString;
0570         invalidateFilter();
0571         Q_EMIT nicknameFilterStringChanged(nicknameFilterString);
0572     }
0573 }
0574 
0575 Qt::MatchFlags ContactsFilterModel::nicknameFilterMatchFlags() const
0576 {
0577     return d->nicknameFilterMatchFlags;
0578 }
0579 
0580 void ContactsFilterModel::resetNicknameFilterMatchFlags()
0581 {
0582     setNicknameFilterMatchFlags(Qt::MatchStartsWith | Qt::MatchWrap);
0583 }
0584 
0585 void ContactsFilterModel::setNicknameFilterMatchFlags(Qt::MatchFlags nicknameFilterMatchFlags)
0586 {
0587     if (d->nicknameFilterMatchFlags != nicknameFilterMatchFlags) {
0588         d->nicknameFilterMatchFlags = nicknameFilterMatchFlags;
0589         invalidateFilter();
0590         Q_EMIT nicknameFilterMatchFlagsChanged(nicknameFilterMatchFlags);
0591     }
0592 }
0593 
0594 QString ContactsFilterModel::aliasFilterString() const
0595 {
0596     return d->aliasFilterString;
0597 }
0598 
0599 void ContactsFilterModel::clearAliasFilterString()
0600 {
0601     setAliasFilterString(QString());
0602 }
0603 
0604 void ContactsFilterModel::setAliasFilterString(const QString &aliasFilterString)
0605 {
0606     if (d->aliasFilterString != aliasFilterString) {
0607         d->aliasFilterString = aliasFilterString;
0608         invalidateFilter();
0609         Q_EMIT aliasFilterStringChanged(aliasFilterString);
0610     }
0611 }
0612 
0613 Qt::MatchFlags ContactsFilterModel::aliasFilterMatchFlags() const
0614 {
0615     return d->aliasFilterMatchFlags;
0616 }
0617 
0618 void ContactsFilterModel::resetAliasFilterMatchFlags()
0619 {
0620     setAliasFilterMatchFlags(Qt::MatchStartsWith | Qt::MatchWrap);
0621 }
0622 
0623 void ContactsFilterModel::setAliasFilterMatchFlags(Qt::MatchFlags aliasFilterMatchFlags)
0624 {
0625     if (d->aliasFilterMatchFlags != aliasFilterMatchFlags) {
0626         d->aliasFilterMatchFlags = aliasFilterMatchFlags;
0627         invalidateFilter();
0628         Q_EMIT aliasFilterMatchFlagsChanged(aliasFilterMatchFlags);
0629     }
0630 }
0631 
0632 QString ContactsFilterModel::groupsFilterString() const
0633 {
0634     return d->groupsFilterString;
0635 }
0636 
0637 void ContactsFilterModel::clearGroupsFilterString()
0638 {
0639     setGroupsFilterString(QString());
0640 }
0641 
0642 void ContactsFilterModel::setGroupsFilterString(const QString &groupsFilterString)
0643 {
0644     if (d->groupsFilterString != groupsFilterString) {
0645         d->groupsFilterString = groupsFilterString;
0646         invalidateFilter();
0647         Q_EMIT groupsFilterStringChanged(groupsFilterString);
0648     }
0649 }
0650 
0651 Qt::MatchFlags ContactsFilterModel::groupsFilterMatchFlags() const
0652 {
0653     return d->groupsFilterMatchFlags;
0654 }
0655 
0656 void ContactsFilterModel::resetGroupsFilterMatchFlags()
0657 {
0658     setGroupsFilterMatchFlags(Qt::MatchStartsWith | Qt::MatchWrap);
0659 }
0660 
0661 void ContactsFilterModel::setGroupsFilterMatchFlags(Qt::MatchFlags groupsFilterMatchFlags)
0662 {
0663     if (d->groupsFilterMatchFlags != groupsFilterMatchFlags) {
0664         d->groupsFilterMatchFlags = groupsFilterMatchFlags;
0665         invalidateFilter();
0666         Q_EMIT groupsFilterMatchFlagsChanged(groupsFilterMatchFlags);
0667     }
0668 }
0669 
0670 QString ContactsFilterModel::idFilterString() const
0671 {
0672     return d->idFilterString;
0673 }
0674 
0675 void ContactsFilterModel::clearIdFilterString()
0676 {
0677     setIdFilterString(QString());
0678 }
0679 
0680 void ContactsFilterModel::setIdFilterString(const QString &idFilterString)
0681 {
0682     if (d->idFilterString != idFilterString) {
0683         d->idFilterString = idFilterString;
0684         invalidateFilter();
0685         Q_EMIT idFilterStringChanged(idFilterString);
0686     }
0687 }
0688 
0689 Qt::MatchFlags ContactsFilterModel::idFilterMatchFlags() const
0690 {
0691     return d->idFilterMatchFlags;
0692 }
0693 
0694 
0695 Tp::AccountPtr ContactsFilterModel::accountFilter() const
0696 {
0697     return d->accountFilter;
0698 }
0699 
0700 void ContactsFilterModel::setAccountFilter(const Tp::AccountPtr &accountFilter)
0701 {
0702     if (d->accountFilter != accountFilter) {
0703         d->accountFilter = accountFilter;
0704         invalidateFilter();
0705         Q_EMIT accountFilterChanged(accountFilter);
0706     }
0707 }
0708 
0709 void ContactsFilterModel::clearAccountFilter()
0710 {
0711     setAccountFilter(Tp::AccountPtr());
0712 }
0713 
0714 void ContactsFilterModel::resetIdFilterMatchFlags()
0715 {
0716     setIdFilterMatchFlags(Qt::MatchStartsWith | Qt::MatchWrap);
0717 }
0718 
0719 void ContactsFilterModel::setIdFilterMatchFlags(Qt::MatchFlags idFilterMatchFlags)
0720 {
0721     if (d->idFilterMatchFlags != idFilterMatchFlags) {
0722         d->idFilterMatchFlags = idFilterMatchFlags;
0723         invalidateFilter();
0724         Q_EMIT idFilterMatchFlagsChanged(idFilterMatchFlags);
0725     }
0726 }
0727 
0728 QStringList ContactsFilterModel::tubesFilterStrings() const
0729 {
0730     return d->tubesFilterStrings;
0731 }
0732 
0733 void ContactsFilterModel::clearTubesFilterStrings()
0734 {
0735     setTubesFilterStrings(QStringList());
0736 }
0737 
0738 void ContactsFilterModel::setTubesFilterStrings(const QStringList &tubesFilterStrings)
0739 {
0740     if (d->tubesFilterStrings != tubesFilterStrings) {
0741         d->tubesFilterStrings = tubesFilterStrings;
0742         invalidateFilter();
0743         Q_EMIT tubesFilterStringsChanged(tubesFilterStrings);
0744     }
0745 }
0746 
0747 bool ContactsFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
0748 {
0749     QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
0750 
0751     bool rc;
0752     int type = index.data(KTp::RowTypeRole).toInt();
0753     if (type == KTp::ContactRowType || type == KTp::PersonRowType) {
0754         rc = d->filterAcceptsContact(index);
0755     }
0756     else if (type == KTp::AccountRowType) {
0757         rc = d->filterAcceptsAccount(index);
0758     }
0759     else if (type == KTp::GroupRowType) {
0760         rc = d->filterAcceptsGroup(index);
0761     }
0762     else {
0763         qCDebug(KTP_MODELS) << "Unknown type found in Account Filter";
0764         rc = true;
0765     }
0766 
0767     return rc;
0768 }
0769 
0770 bool ContactsFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
0771 {
0772 
0773     QString leftDisplayedName = sourceModel()->data(left).toString();
0774     QString rightDisplayedName = sourceModel()->data(right).toString();
0775 
0776     switch (sortRole()) {
0777         case KTp::ContactPresenceTypeRole:
0778     {
0779         Tp::ConnectionPresenceType leftPresence = (Tp::ConnectionPresenceType)left.data(KTp::ContactPresenceTypeRole).toUInt();
0780         Tp::ConnectionPresenceType rightPresence = (Tp::ConnectionPresenceType)right.data(KTp::ContactPresenceTypeRole).toUInt();
0781 
0782         if (leftPresence == rightPresence) {
0783             //presences are the same, compare client types
0784 
0785             bool leftPhone = left.data(KTp::ContactClientTypesRole).toStringList().contains(QLatin1String("phone"));
0786             bool rightPhone = right.data(KTp::ContactClientTypesRole).toStringList().contains(QLatin1String("phone"));
0787 
0788             if (leftPhone && ! rightPhone) {
0789                 return false;
0790             }
0791             else if (rightPhone && !leftPhone) {
0792                 return true;
0793             }
0794 
0795             return QString::localeAwareCompare(leftDisplayedName, rightDisplayedName) < 0;
0796         } else {
0797             if (leftPresence == Tp::ConnectionPresenceTypeAvailable) {
0798                 return true;
0799             }
0800             if (leftPresence == Tp::ConnectionPresenceTypeUnset ||
0801                     leftPresence == Tp::ConnectionPresenceTypeOffline ||
0802                     leftPresence == Tp::ConnectionPresenceTypeUnknown ||
0803                     leftPresence == Tp::ConnectionPresenceTypeError) {
0804                 return false;
0805             }
0806 
0807             return KTp::Presence::sortPriority(leftPresence) < KTp::Presence::sortPriority(rightPresence);
0808         }
0809     }
0810     case Qt::DisplayRole:
0811     default:
0812         return QString::localeAwareCompare(leftDisplayedName, rightDisplayedName) < 0;
0813     }
0814 }
0815 
0816 
0817 QModelIndexList ContactsFilterModel::match(const QModelIndex &start, int role,
0818                                           const QVariant &value, int hits,
0819                                           Qt::MatchFlags flags) const
0820 {
0821     if (!start.isValid()) {
0822         return QModelIndexList();
0823     }
0824 
0825     QModelIndexList result;
0826     uint matchType = flags & 0x0F;
0827     Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
0828     bool recurse = flags & Qt::MatchRecursive;
0829     bool allHits = (hits == -1);
0830     QString text; // only convert to a string if it is needed
0831     QVariant v = start.data(role);
0832     // QVariant based matching
0833     if (matchType == Qt::MatchExactly) {
0834         if (value == v)
0835             result.append(start);
0836     } else { // QString based matching
0837         if (text.isEmpty()) // lazy conversion
0838             text = value.toString();
0839         QString t;
0840 
0841         // If we're being case-insensitve then we should also be "foreign character insensitive"
0842         if (cs == Qt::CaseInsensitive) {
0843             const QString normalized = v.toString().normalized(QString::NormalizationForm_D);
0844             Q_FOREACH (const QChar &c, normalized) {
0845                 if (c.category() != QChar::Mark_NonSpacing
0846                     && c.category() != QChar::Mark_SpacingCombining
0847                     && c.category() != QChar::Mark_Enclosing) {
0848                     t.append(c);
0849                 }
0850             }
0851         } else {
0852             t = v.toString();
0853         }
0854 
0855         switch (matchType) {
0856         case Qt::MatchRegExp:
0857             if (QRegExp(text, cs).exactMatch(t))
0858                 result.append(start);
0859             break;
0860         case Qt::MatchWildcard:
0861             if (QRegExp(text, cs, QRegExp::Wildcard).exactMatch(t))
0862                 result.append(start);
0863             break;
0864         case Qt::MatchStartsWith:
0865             if (t.startsWith(text, cs))
0866                 result.append(start);
0867             break;
0868         case Qt::MatchEndsWith:
0869             if (t.endsWith(text, cs))
0870                 result.append(start);
0871             break;
0872         case Qt::MatchFixedString:
0873             if (t.compare(text, cs) == 0)
0874                 result.append(start);
0875             break;
0876         case Qt::MatchContains:
0877         default:
0878             if (t.contains(text, cs))
0879                 result.append(start);
0880         }
0881     }
0882     if (recurse && hasChildren(start)) { // search the hierarchy
0883         result += match(index(0, start.column(), start), role,
0884                         (text.isEmpty() ? value : text),
0885                         (allHits ? -1 : hits - result.count()), flags);
0886     }
0887     return result;
0888 }
0889 
0890 void ContactsFilterModel::setSortRoleString(const QString &role)
0891 {
0892     setSortRole(roleNames().key(role.toUtf8()));
0893 }
0894 
0895 QString ContactsFilterModel::sortRoleString() const
0896 {
0897 //    Q_ASSERT(roleNames().contains(sortRole()));
0898 //    return QString::fromUtf8(roleNames().value(sortRole()));
0899     return QString();
0900 }
0901 
0902 #include "moc_contacts-filter-model.cpp"