diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index be6a0ce7b..cf1176b44 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,10 +67,7 @@ set(SOURCES src/components/settings/AccountSettingsModel.cpp src/components/settings/SettingsModel.cpp src/components/sip-addresses/SipAddressesModel.cpp - src/components/sip-addresses/UnregisteredSipAddressesModel.cpp - src/components/sip-addresses/UnregisteredSipAddressesProxyModel.cpp src/components/smart-search-bar/SmartSearchBarModel.cpp - src/components/smart-search-bar/SmartSearchBarProxyModel.cpp src/components/timeline/TimelineModel.cpp src/main.cpp ) @@ -94,10 +91,7 @@ set(HEADERS src/components/settings/AccountSettingsModel.hpp src/components/settings/SettingsModel.hpp src/components/sip-addresses/SipAddressesModel.hpp - src/components/sip-addresses/UnregisteredSipAddressesModel.hpp - src/components/sip-addresses/UnregisteredSipAddressesProxyModel.hpp src/components/smart-search-bar/SmartSearchBarModel.hpp - src/components/smart-search-bar/SmartSearchBarProxyModel.hpp src/components/timeline/TimelineModel.hpp src/utils.hpp ) diff --git a/tests/src/app/App.cpp b/tests/src/app/App.cpp index 6bac00b52..ddeac905b 100644 --- a/tests/src/app/App.cpp +++ b/tests/src/app/App.cpp @@ -10,9 +10,8 @@ #include "../components/core/CoreManager.hpp" #include "../components/notifier/Notifier.hpp" #include "../components/settings/AccountSettingsModel.hpp" -#include "../components/sip-addresses/UnregisteredSipAddressesProxyModel.hpp" #include "../components/timeline/TimelineModel.hpp" -#include "../components/smart-search-bar/SmartSearchBarProxyModel.hpp" +#include "../components/smart-search-bar/SmartSearchBarModel.hpp" #include "App.hpp" @@ -143,8 +142,7 @@ void App::registerTypes () { qmlRegisterType("Linphone", 1, 0, "ContactsListProxyModel"); qmlRegisterType("Linphone", 1, 0, "ChatModel"); qmlRegisterType("Linphone", 1, 0, "ChatProxyModel"); - qmlRegisterType("Linphone", 1, 0, "UnregisteredSipAddressesProxyModel"); - qmlRegisterType("Linphone", 1, 0, "SmartSearchBarProxyModel"); + qmlRegisterType("Linphone", 1, 0, "SmartSearchBarModel"); qRegisterMetaType("ChatModel::EntryType"); } diff --git a/tests/src/components/contact/ContactModel.hpp b/tests/src/components/contact/ContactModel.hpp index 52e93ade0..d80299837 100644 --- a/tests/src/components/contact/ContactModel.hpp +++ b/tests/src/components/contact/ContactModel.hpp @@ -15,6 +15,7 @@ class ContactModel : public QObject { friend class ContactsListModel; friend class ContactsListProxyModel; + friend class SmartSearchBarModel; public: ContactModel (std::shared_ptr linphone_friend); diff --git a/tests/src/components/contacts/ContactsListProxyModel.cpp b/tests/src/components/contacts/ContactsListProxyModel.cpp index fdf107f98..187a9207b 100644 --- a/tests/src/components/contacts/ContactsListProxyModel.cpp +++ b/tests/src/components/contacts/ContactsListProxyModel.cpp @@ -35,7 +35,6 @@ ContactsListProxyModel::ContactsListProxyModel (QObject *parent) : QSortFilterPr m_list = CoreManager::getInstance()->getContactsListModel(); setSourceModel(m_list); - setDynamicSortFilter(false); for (const ContactModel *contact : m_list->m_list) m_weights[contact] = 0; @@ -43,11 +42,20 @@ ContactsListProxyModel::ContactsListProxyModel (QObject *parent) : QSortFilterPr sort(0); } +// ----------------------------------------------------------------------------- + +void ContactsListProxyModel::setFilter (const QString &pattern) { + m_filter = pattern; + invalidate(); +} + +// ----------------------------------------------------------------------------- + bool ContactsListProxyModel::filterAcceptsRow ( int source_row, const QModelIndex &source_parent ) const { - QModelIndex index = sourceModel()->index(source_row, 0, source_parent); + const QModelIndex &index = sourceModel()->index(source_row, 0, source_parent); const ContactModel *contact = index.data().value(); m_weights[contact] = static_cast(computeContactWeight(*contact)); @@ -79,7 +87,7 @@ float ContactsListProxyModel::computeStringWeight (const QString &string, float int offset = -1; // Search pattern. - while ((index = filterRegExp().indexIn(string, index + 1)) != -1) { + while ((index = string.indexOf(m_filter, index + 1, Qt::CaseInsensitive)) != -1) { // Search n chars between one separator and index. int tmp_offset = index - string.lastIndexOf(m_search_separators, index) - 1; diff --git a/tests/src/components/contacts/ContactsListProxyModel.hpp b/tests/src/components/contacts/ContactsListProxyModel.hpp index 2129cd152..b2e4e2706 100644 --- a/tests/src/components/contacts/ContactsListProxyModel.hpp +++ b/tests/src/components/contacts/ContactsListProxyModel.hpp @@ -23,10 +23,7 @@ public: ~ContactsListProxyModel () = default; public slots: - void setFilter (const QString &pattern) { - setFilterFixedString(pattern); - invalidate(); - } + void setFilter (const QString &pattern); protected: bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override; @@ -43,6 +40,7 @@ private: void setConnectedFilter (bool use_connected_filter); ContactsListModel *m_list; + QString m_filter; bool m_use_connected_filter = false; // It's just a cache to save values computed by `filterAcceptsRow` diff --git a/tests/src/components/core/CoreManager.cpp b/tests/src/components/core/CoreManager.cpp index fedb91bff..55e6f1492 100644 --- a/tests/src/components/core/CoreManager.cpp +++ b/tests/src/components/core/CoreManager.cpp @@ -19,7 +19,6 @@ void CoreManager::init () { m_instance->m_contacts_list_model = new ContactsListModel(m_instance); m_instance->m_sip_addresses_model = new SipAddressesModel(m_instance); - m_instance->m_unregistered_sip_addresses_model = new UnregisteredSipAddressesModel(m_instance); } } diff --git a/tests/src/components/core/CoreManager.hpp b/tests/src/components/core/CoreManager.hpp index 44c2e85db..5739c9386 100644 --- a/tests/src/components/core/CoreManager.hpp +++ b/tests/src/components/core/CoreManager.hpp @@ -4,7 +4,6 @@ #include "../contact/VcardModel.hpp" #include "../contacts/ContactsListModel.hpp" #include "../sip-addresses/SipAddressesModel.hpp" -#include "../sip-addresses/UnregisteredSipAddressesModel.hpp" // ============================================================================= @@ -30,10 +29,6 @@ public: return m_sip_addresses_model; } - UnregisteredSipAddressesModel *getUnregisteredSipAddressesModel () { - return m_unregistered_sip_addresses_model; - } - static void init (); static CoreManager *getInstance () { @@ -53,7 +48,6 @@ private: std::shared_ptr m_core; ContactsListModel *m_contacts_list_model; SipAddressesModel *m_sip_addresses_model; - UnregisteredSipAddressesModel *m_unregistered_sip_addresses_model; static CoreManager *m_instance; }; diff --git a/tests/src/components/sip-addresses/UnregisteredSipAddressesModel.cpp b/tests/src/components/sip-addresses/UnregisteredSipAddressesModel.cpp deleted file mode 100644 index 8776b0e17..000000000 --- a/tests/src/components/sip-addresses/UnregisteredSipAddressesModel.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "../core/CoreManager.hpp" - -#include "UnregisteredSipAddressesModel.hpp" - -// ============================================================================= - -UnregisteredSipAddressesModel::UnregisteredSipAddressesModel (QObject *parent) : QSortFilterProxyModel(parent) { - setSourceModel(CoreManager::getInstance()->getSipAddressesModel()); -} - -bool UnregisteredSipAddressesModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const { - QModelIndex index = sourceModel()->index(source_row, 0, source_parent); - return !index.data().toMap().contains("contact"); -} diff --git a/tests/src/components/sip-addresses/UnregisteredSipAddressesModel.hpp b/tests/src/components/sip-addresses/UnregisteredSipAddressesModel.hpp deleted file mode 100644 index ffc8f6aa5..000000000 --- a/tests/src/components/sip-addresses/UnregisteredSipAddressesModel.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef UNREGISTERED_SIP_ADDRESSES_MODEL_H_ -#define UNREGISTERED_SIP_ADDRESSES_MODEL_H_ - -#include - -// ============================================================================= - -class UnregisteredSipAddressesModel : public QSortFilterProxyModel { - Q_OBJECT; - -public: - UnregisteredSipAddressesModel (QObject *parent = Q_NULLPTR); - ~UnregisteredSipAddressesModel () = default; - -protected: - bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override; -}; - - #endif // UNREGISTERED_SIP_ADDRESSES_MODEL_H_ diff --git a/tests/src/components/sip-addresses/UnregisteredSipAddressesProxyModel.cpp b/tests/src/components/sip-addresses/UnregisteredSipAddressesProxyModel.cpp deleted file mode 100644 index 526eedcb8..000000000 --- a/tests/src/components/sip-addresses/UnregisteredSipAddressesProxyModel.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "../core/CoreManager.hpp" - -#include "UnregisteredSipAddressesProxyModel.hpp" - -#define WEIGHT_POS_0 5 -#define WEIGHT_POS_1 4 -#define WEIGHT_POS_2 3 -#define WEIGHT_POS_3 2 -#define WEIGHT_POS_OTHER 1 - -// ============================================================================= - -const QRegExp UnregisteredSipAddressesProxyModel::m_search_separators("^[^_.-;@ ][_.-;@ ]"); - -// ----------------------------------------------------------------------------- - -UnregisteredSipAddressesProxyModel::UnregisteredSipAddressesProxyModel (QObject *parent) : - QSortFilterProxyModel(parent) { - setSourceModel(CoreManager::getInstance()->getUnregisteredSipAddressesModel()); - setDynamicSortFilter(false); - sort(0); -} - -bool UnregisteredSipAddressesProxyModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const { - QModelIndex index = sourceModel()->index(source_row, 0, source_parent); - return computeStringWeight(index.data().toMap()["sipAddress"].toString()) > 0; -} - -bool UnregisteredSipAddressesProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { - QString sip_address_a = sourceModel()->data(left).toMap()["sipAddress"].toString(); - QString sip_address_b = sourceModel()->data(right).toMap()["sipAddress"].toString(); - - int weight_a = computeStringWeight(sip_address_a); - int weight_b = computeStringWeight(sip_address_b); - - return weight_a > weight_b || ( - weight_a == weight_b && sip_address_a >= sip_address_b - ); -} - -int UnregisteredSipAddressesProxyModel::computeStringWeight (const QString &string) const { - int index = -1; - int offset = -1; - - while ((index = filterRegExp().indexIn(string, index + 1)) != -1) { - 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; - } - - switch (offset) { - case -1: return 0; - case 0: return WEIGHT_POS_0; - case 1: return WEIGHT_POS_1; - case 2: return WEIGHT_POS_2; - case 3: return WEIGHT_POS_3; - default: break; - } - - return WEIGHT_POS_OTHER; -} diff --git a/tests/src/components/sip-addresses/UnregisteredSipAddressesProxyModel.hpp b/tests/src/components/sip-addresses/UnregisteredSipAddressesProxyModel.hpp deleted file mode 100644 index 1173ecc33..000000000 --- a/tests/src/components/sip-addresses/UnregisteredSipAddressesProxyModel.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef UNREGISTERED_SIP_ADDRESSES_PROXY_MODEL_H_ -#define UNREGISTERED_SIP_ADDRESSES_PROXY_MODEL_H_ - -#include "UnregisteredSipAddressesModel.hpp" - -// ============================================================================= - -class UnregisteredSipAddressesProxyModel : public QSortFilterProxyModel { - Q_OBJECT; - -public: - UnregisteredSipAddressesProxyModel (QObject *parent = Q_NULLPTR); - ~UnregisteredSipAddressesProxyModel () = default; - -public slots: - void setFilter (const QString &pattern) { - setFilterFixedString(pattern); - invalidate(); - } - -protected: - bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override; - bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; - -private: - int computeStringWeight (const QString &string) const; - - static const QRegExp m_search_separators; -}; - -#endif // UNREGISTERED_SIP_ADDRESSES_PROXY_MODEL_H_ diff --git a/tests/src/components/smart-search-bar/SmartSearchBarModel.cpp b/tests/src/components/smart-search-bar/SmartSearchBarModel.cpp index b117868f6..c1b1792a9 100644 --- a/tests/src/components/smart-search-bar/SmartSearchBarModel.cpp +++ b/tests/src/components/smart-search-bar/SmartSearchBarModel.cpp @@ -1,9 +1,22 @@ +#include "../core/CoreManager.hpp" + #include "SmartSearchBarModel.hpp" +#define WEIGHT_POS_0 5 +#define WEIGHT_POS_1 4 +#define WEIGHT_POS_2 3 +#define WEIGHT_POS_3 2 +#define WEIGHT_POS_OTHER 1 + // ============================================================================= -int SmartSearchBarModel::rowCount (const QModelIndex &) const { - return m_contacts.rowCount() + m_sip_addresses.rowCount(); +const QRegExp SmartSearchBarModel::m_search_separators("^[^_.-;@ ][_.-;@ ]"); + +// ----------------------------------------------------------------------------- + +SmartSearchBarModel::SmartSearchBarModel (QObject *parent) : QSortFilterProxyModel(parent) { + setSourceModel(CoreManager::getInstance()->getSipAddressesModel()); + sort(0); } QHash SmartSearchBarModel::roleNames () const { @@ -12,20 +25,78 @@ QHash SmartSearchBarModel::roleNames () const { return roles; } -QVariant SmartSearchBarModel::data (const QModelIndex &index, int role) const { - int row = index.row(); - int n_contacts = m_contacts.rowCount(); - int n_sip_addresses = m_sip_addresses.rowCount(); +// ----------------------------------------------------------------------------- - if (row < 0 || row >= n_contacts + n_sip_addresses) - return QVariant(); +void SmartSearchBarModel::setFilter (const QString &pattern) { + m_filter = pattern; + invalidate(); +} - if (role == Qt::DisplayRole) { - if (row < n_contacts) - return QVariant::fromValue(m_contacts.data(m_contacts.index(row, 0), role)); +// ----------------------------------------------------------------------------- - return QVariant::fromValue(m_sip_addresses.data(m_sip_addresses.index(row - n_contacts, 0), role)); +bool SmartSearchBarModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const { + const QModelIndex &index = sourceModel()->index(source_row, 0, source_parent); + const QVariantMap &map = index.data().toMap(); + + return computeStringWeight(map["sipAddress"].toString()) > 0; +} + +bool SmartSearchBarModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { + const QVariantMap &map_a = sourceModel()->data(left).toMap(); + const QVariantMap &map_b = sourceModel()->data(right).toMap(); + + const QString &sip_address_a = map_a["sipAddress"].toString(); + const QString &sip_address_b = map_b["sipAddress"].toString(); + + int weight_a = computeStringWeight(sip_address_a); + int weight_b = computeStringWeight(sip_address_b); + + // 1. Not the same weight. + if (weight_a != weight_b) + return weight_a > weight_b; + + const ContactModel *contact_a = map_a.value("contact").value(); + const ContactModel *contact_b = map_b.value("contact").value(); + + // 2. No contacts. + if (!contact_a && !contact_b) + return sip_address_a <= sip_address_b; + + // 3. No contact for a or b. + if (!contact_a || !contact_b) + return !!contact_a; + + // 4. Same contact (address). + if (contact_a == contact_b) + return sip_address_a <= sip_address_b; + + // 5. Not the same contact name. + int diff = contact_a->m_linphone_friend->getName().compare(contact_b->m_linphone_friend->getName()); + if (diff) + return diff <= 0; + + // 6. Same contact name, so compare sip addresses. + return sip_address_a <= sip_address_b; +} + +int SmartSearchBarModel::computeStringWeight (const QString &string) const { + int index = -1; + int offset = -1; + + while ((index = string.indexOf(m_filter, index + 1, Qt::CaseInsensitive)) != -1) { + 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; } - return QVariant(); + switch (offset) { + case -1: return 0; + case 0: return WEIGHT_POS_0; + case 1: return WEIGHT_POS_1; + case 2: return WEIGHT_POS_2; + case 3: return WEIGHT_POS_3; + default: break; + } + + return WEIGHT_POS_OTHER; } diff --git a/tests/src/components/smart-search-bar/SmartSearchBarModel.hpp b/tests/src/components/smart-search-bar/SmartSearchBarModel.hpp index 4771a769b..4ecfbcb8d 100644 --- a/tests/src/components/smart-search-bar/SmartSearchBarModel.hpp +++ b/tests/src/components/smart-search-bar/SmartSearchBarModel.hpp @@ -1,29 +1,33 @@ #ifndef SMART_SEARCH_BAR_MODEL_H_ #define SMART_SEARCH_BAR_MODEL_H_ -#include +#include -#include "../contacts/ContactsListProxyModel.hpp" -#include "../sip-addresses/UnregisteredSipAddressesProxyModel.hpp" +#include "../sip-addresses/SipAddressesModel.hpp" // ============================================================================= -class SmartSearchBarModel : public QAbstractListModel { +class SmartSearchBarModel : public QSortFilterProxyModel { Q_OBJECT; public: - SmartSearchBarModel (QObject *parent = Q_NULLPTR) : QAbstractListModel(parent) {} - - virtual ~SmartSearchBarModel () = default; - - int rowCount (const QModelIndex &index = QModelIndex()) const override; + SmartSearchBarModel (QObject *parent = Q_NULLPTR); + ~SmartSearchBarModel () = default; QHash roleNames () const override; - QVariant data (const QModelIndex &index, int role) const override; + +public slots: + void setFilter (const QString &pattern); protected: - ContactsListProxyModel m_contacts; - UnregisteredSipAddressesProxyModel m_sip_addresses; + bool filterAcceptsRow (int source_row, const QModelIndex &source_parent) const override; + bool lessThan (const QModelIndex &left, const QModelIndex &right) const override; + +private: + int computeStringWeight (const QString &string) const; + + QString m_filter; + static const QRegExp m_search_separators; }; #endif // SMART_SEARCH_BAR_MODEL_H_ diff --git a/tests/src/components/smart-search-bar/SmartSearchBarProxyModel.cpp b/tests/src/components/smart-search-bar/SmartSearchBarProxyModel.cpp deleted file mode 100644 index 11d8881d0..000000000 --- a/tests/src/components/smart-search-bar/SmartSearchBarProxyModel.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "SmartSearchBarProxyModel.hpp" - -// ============================================================================= - -void SmartSearchBarProxyModel::setFilter (const QString &pattern) { - m_contacts.setFilter(pattern); - m_sip_addresses.setFilter(pattern); -} diff --git a/tests/src/components/smart-search-bar/SmartSearchBarProxyModel.hpp b/tests/src/components/smart-search-bar/SmartSearchBarProxyModel.hpp deleted file mode 100644 index d52757861..000000000 --- a/tests/src/components/smart-search-bar/SmartSearchBarProxyModel.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef SMART_SEARCH_BAR_PROXY_MODEL_H_ -#define SMART_SEARCH_BAR_PROXY_MODEL_H_ - -#include "SmartSearchBarModel.hpp" - -// ============================================================================= - -class SmartSearchBarProxyModel : public SmartSearchBarModel { - Q_OBJECT; - -public: - SmartSearchBarProxyModel (QObject *parent = Q_NULLPTR) : SmartSearchBarModel(parent) {} - - ~SmartSearchBarProxyModel () = default; - -public slots: - void setFilter (const QString &pattern); -}; - -#endif // SMART_SEARCH_BAR_PROXY_MODEL_H_ diff --git a/tests/src/components/timeline/TimelineModel.cpp b/tests/src/components/timeline/TimelineModel.cpp index 0d3421c9f..4d9d5d01e 100644 --- a/tests/src/components/timeline/TimelineModel.cpp +++ b/tests/src/components/timeline/TimelineModel.cpp @@ -18,8 +18,10 @@ QHash TimelineModel::roleNames () const { // ----------------------------------------------------------------------------- bool TimelineModel::filterAcceptsRow (int source_row, const QModelIndex &source_parent) const { - QModelIndex index = sourceModel()->index(source_row, 0, source_parent); - return index.data().toMap().contains("timestamp"); + const QModelIndex &index = sourceModel()->index(source_row, 0, source_parent); + const QVariantMap &map = index.data().toMap(); + + return map.contains("timestamp"); } bool TimelineModel::lessThan (const QModelIndex &left, const QModelIndex &right) const { diff --git a/tests/ui/modules/Linphone/SmartSearchBar.qml b/tests/ui/modules/Linphone/SmartSearchBar.qml index b5e3175e0..6c48b5ee9 100644 --- a/tests/ui/modules/Linphone/SmartSearchBar.qml +++ b/tests/ui/modules/Linphone/SmartSearchBar.qml @@ -14,7 +14,7 @@ SearchBox { id: searchBoxEntry height: searchBox.entryHeight - width: parent.width + width: parent ? parent.width : 0 Rectangle { id: indicator @@ -47,9 +47,9 @@ SearchBox { id: avatar Layout.preferredHeight: 30 Layout.preferredWidth: 30 - image: $entry.vcard && $entry.vcard.avatar - presenceLevel: $entry.presenceLevel != null ? $entry.presenceLevel : -1 - username: LinphoneUtils.getContactUsername($entry.sipAddress || $entry) + image: $entry.contact && $entry.contact.vcard.avatar + presenceLevel: $entry.contact ? $entry.contact.presenceLevel : -1 + username: LinphoneUtils.getContactUsername($entry.contact || $entry.sipAddress) } Text { @@ -62,7 +62,7 @@ SearchBox { pointSize: 9 } - text: $entry.vcard ? $entry.vcard.username : $entry.sipAddress + text: $entry.contact ? $entry.contact.vcard.username : $entry.sipAddress } // --------------------------------------------------------------------- diff --git a/tests/ui/views/App/MainWindow/MainWindow.qml b/tests/ui/views/App/MainWindow/MainWindow.qml index 775cbf7e7..43cc342af 100644 --- a/tests/ui/views/App/MainWindow/MainWindow.qml +++ b/tests/ui/views/App/MainWindow/MainWindow.qml @@ -145,7 +145,7 @@ ApplicationWindow { maxMenuHeight: MainWindowStyle.searchBox.maxHeight placeholderText: qsTr('mainSearchBarPlaceholder') - model: SmartSearchBarProxyModel {} + model: SmartSearchBarModel {} } } }