linphone-desktop/tests/src/components/contacts/ContactsListProxyModel.cpp
2016-12-15 14:56:51 +01:00

139 lines
4.2 KiB
C++

#include <QDebug>
#include "../../utils.hpp"
#include "ContactsListProxyModel.hpp"
#define USERNAME_WEIGHT 50.f
#define SIP_ADDRESSES_WEIGHT 50.f
#define FACTOR_POS_0 1.0f
#define FACTOR_POS_1 0.9f
#define FACTOR_POS_2 0.8f
#define FACTOR_POS_3 0.7f
#define FACTOR_POS_OTHER 0.6f
using namespace std;
// =============================================================================
ContactsListModel *ContactsListProxyModel::m_list = nullptr;
// Notes:
//
// - First `^` is necessary to search two words with one separator
// between them like `Claire Manning`.
//
// - [^_.-;@ ] is used to search patterns which starts with
// a separator like ` word`.
//
// - [_.-;@ ] is the main pattern (a separator).
const QRegExp ContactsListProxyModel::m_search_separators("^[^_.-;@ ][_.-;@ ]");
// -----------------------------------------------------------------------------
ContactsListProxyModel::ContactsListProxyModel (QObject *parent) : QSortFilterProxyModel(parent) {
if (m_list == nullptr)
qFatal("Contacts list model is undefined.");
setSourceModel(m_list);
setFilterCaseSensitivity(Qt::CaseInsensitive);
for (const ContactModel *contact : m_list->m_list)
m_weights[contact] = 0;
setDynamicSortFilter(false);
sort(0);
}
void ContactsListProxyModel::initContactsListModel (ContactsListModel *list) {
if (!m_list)
m_list = list;
else
qWarning() << "Contacts list model is already defined.";
}
bool ContactsListProxyModel::filterAcceptsRow (
int source_row,
const QModelIndex &source_parent
) const {
QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
const ContactModel *contact = qvariant_cast<ContactModel *>(index.data());
m_weights[contact] = static_cast<unsigned int>(computeContactWeight(*contact));
return m_weights[contact] > 0 && (
!m_use_connected_filter ||
contact->getPresenceLevel() != Presence::PresenceLevel::White
);
}
bool ContactsListProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
const ContactModel *contact_a = qvariant_cast<ContactModel *>(sourceModel()->data(left));
const ContactModel *contact_b = qvariant_cast<ContactModel *>(sourceModel()->data(right));
unsigned int weight_a = m_weights[contact_a];
unsigned int weight_b = m_weights[contact_b];
// Sort by weight and name.
return weight_a > weight_b || (
weight_a == weight_b &&
contact_a->m_linphone_friend->getName() <= contact_b->m_linphone_friend->getName()
);
}
// -----------------------------------------------------------------------------
float ContactsListProxyModel::computeStringWeight (const QString &string, float percentage) const {
int index = -1;
int offset = -1;
// Search pattern.
while ((index = filterRegExp().indexIn(string, index + 1)) != -1) {
// Search n chars between one separator and index.
int tmp_offset = index - string.lastIndexOf(m_search_separators, index) - 1;
if ((tmp_offset != -1 && tmp_offset < offset) || offset == -1)
if ((offset = tmp_offset) == 0) break;
}
// No weight.
if (offset == -1)
return 0;
// Weight & offset.
switch (offset) {
case 0: return percentage * FACTOR_POS_0;
case 1: return percentage * FACTOR_POS_1;
case 2: return percentage * FACTOR_POS_2;
case 3: return percentage * FACTOR_POS_3;
default: break;
}
return percentage * FACTOR_POS_OTHER;
}
float ContactsListProxyModel::computeContactWeight (const ContactModel &contact) const {
float weight = computeStringWeight(contact.getVcardModel()->getUsername(), USERNAME_WEIGHT);
// Get all contact's addresses.
const list<shared_ptr<linphone::Address> > addresses = contact.m_linphone_friend->getAddresses();
float size = static_cast<float>(addresses.size());
for (auto it = addresses.cbegin(); it != addresses.cend(); ++it)
weight += computeStringWeight(
::Utils::linphoneStringToQString((*it)->asString()),
SIP_ADDRESSES_WEIGHT / size
);
return weight;
}
// -----------------------------------------------------------------------------
void ContactsListProxyModel::setConnectedFilter (bool use_connected_filter) {
if (use_connected_filter != m_use_connected_filter) {
m_use_connected_filter = use_connected_filter;
invalidate();
}
}