From 5041bea59bef0405f28c51859ae35f411fb50521 Mon Sep 17 00:00:00 2001
From: Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>
Date: Thu, 4 Jun 2015 17:40:05 -0400
Subject: [PATCH] bookmark: Fix the bookmarks duplicated/misplaced logic

 * Create a most popular model in PhoneDirectoryModel
 * Un-duplicate ContactMethod::roleData
 * Make the changes to CategorizedBookmarkModel to act as a proxy

I came to the conclusion the old code path was unfixable and its logic
could not be fixed. The fact that it was duplicating data that could
change without much notice made it too prone to desynchronization.

Refs #73492

Change-Id: I83ad7ca569f1855dd48cca6be39966e5fc3b041b
---
 src/categorizedbookmarkmodel.cpp    | 345 +++++++++++-----------------
 src/categorizedbookmarkmodel.h      |   3 -
 src/contactmethod.cpp               |  54 ++++-
 src/phonedirectorymodel.cpp         |  64 +++++-
 src/phonedirectorymodel.h           |   4 +
 src/private/phonedirectorymodel_p.h |  17 ++
 6 files changed, 264 insertions(+), 223 deletions(-)

diff --git a/src/categorizedbookmarkmodel.cpp b/src/categorizedbookmarkmodel.cpp
index d3378d89..a7be6d84 100644
--- a/src/categorizedbookmarkmodel.cpp
+++ b/src/categorizedbookmarkmodel.cpp
@@ -20,6 +20,7 @@
 //Qt
 #include <QtCore/QMimeData>
 #include <QtCore/QCoreApplication>
+#include <QtCore/QAbstractItemModel>
 
 //Ring
 #include "categorizedhistorymodel.h"
@@ -33,19 +34,9 @@
 #include "mime.h"
 #include "collectioneditor.h"
 #include "collectioninterface.h"
+#include "private/phonedirectorymodel_p.h"
 
-///Top level bookmark item
-class BookmarkTopLevelItem final : public CategorizedCompositeNode {
-   friend class CategorizedBookmarkModel;
-   public:
-      virtual QObject* getSelf() const override;
-      int m_Row;
-   private:
-      explicit BookmarkTopLevelItem(QString name);
-      QList<NumberTreeBackend*> m_lChildren;
-      QString m_Name;
-      bool m_MostPopular;
-};
+class NumberTreeBackend;
 
 class CategorizedBookmarkModelPrivate final : public QObject
 {
@@ -53,24 +44,19 @@ class CategorizedBookmarkModelPrivate final : public QObject
 public:
    CategorizedBookmarkModelPrivate(CategorizedBookmarkModel* parent);
 
-//    QVector<CollectionInterface*> m_lBackends;
-
    //Attributes
-   QList<BookmarkTopLevelItem*>         m_lCategoryCounter ;
-   QHash<QString,BookmarkTopLevelItem*> m_hCategories      ;
-   QStringList                          m_lMimes           ;
-   QHash<ContactMethod*,QMetaObject::Connection> m_Tracked ;
+   QList<NumberTreeBackend*>                     m_lCategoryCounter ;
+   QHash<QString,NumberTreeBackend*>             m_hCategories      ;
+   QStringList                                   m_lMimes           ;
+   QHash<ContactMethod*,QMetaObject::Connection> m_Tracked          ;
 
    //Helpers
-   QVariant commonCallInfo(NumberTreeBackend* call, int role = Qt::DisplayRole) const;
-   QString category(NumberTreeBackend* number) const;
-   bool                  displayFrequentlyUsed() const;
-   QVector<ContactMethod*>   bookmarkList         () const;
-   static QVector<ContactMethod*> serialisedToList(const QStringList& list);
+   QString                 category             ( NumberTreeBackend* number ) const;
+   bool                    displayFrequentlyUsed(                           ) const;
+   QVector<ContactMethod*> bookmarkList         (                           ) const;
 
 public Q_SLOTS:
-   void slotRequest(const QString& uri);
-   void slotIndexChanged(const QModelIndex& idx);
+   void slotIndexChanged( const QModelIndex& idx );
 
 private:
    CategorizedBookmarkModel* q_ptr;
@@ -80,47 +66,52 @@ CategorizedBookmarkModel* CategorizedBookmarkModel::m_spInstance = nullptr;
 
 static bool test = false;
 //Model item/index
-class NumberTreeBackend final : public CategorizedCompositeNode
+class NumberTreeBackend final
 {
    friend class CategorizedBookmarkModel;
-   public:
-      enum class Type {
-         BOOKMARK,
-         POPULAR,
-      };
-
-      NumberTreeBackend(ContactMethod* number);
-      virtual ~NumberTreeBackend();
-      virtual QObject* getSelf() const override { return nullptr; }
-
-      ContactMethod* m_pNumber;
-      BookmarkTopLevelItem* m_pParent;
-      int m_Index;
-      Type m_Type;
-      int m_PopularIdx;
-      QMetaObject::Connection m_Conn;
-};
+public:
+   enum class Type {
+      BOOKMARK,
+      CATEGORY,
+   };
 
-CategorizedBookmarkModelPrivate::CategorizedBookmarkModelPrivate(CategorizedBookmarkModel* parent) : QObject(parent), q_ptr(parent)
-{
+   //Constructor
+   explicit NumberTreeBackend(const QString& name);
+   NumberTreeBackend(ContactMethod* number);
+   virtual ~NumberTreeBackend();
 
-}
+   //Attributes
+   ContactMethod*            m_pNumber    ;
+   NumberTreeBackend*        m_pParent    ;
+   int                       m_Index      ;
+   Type                      m_Type       ;
+   QString                   m_Name       ;
+   bool                      m_MostPopular;
+   QList<NumberTreeBackend*> m_lChildren  ;
+   QMetaObject::Connection   m_Conn       ;
+};
+
+CategorizedBookmarkModelPrivate::CategorizedBookmarkModelPrivate(CategorizedBookmarkModel* parent) :
+QObject(parent), q_ptr(parent)
+{}
 
-NumberTreeBackend::NumberTreeBackend(ContactMethod* number): CategorizedCompositeNode(CategorizedCompositeNode::Type::BOOKMARK),
-   m_pNumber(number),m_pParent(nullptr),m_Index(-1), m_Type(NumberTreeBackend::Type::BOOKMARK), m_PopularIdx(-1){
+NumberTreeBackend::NumberTreeBackend(ContactMethod* number):
+m_pNumber(number),m_pParent(nullptr),m_Index(-1), m_Type(NumberTreeBackend::Type::BOOKMARK),
+m_MostPopular(false)
+{
    Q_ASSERT(number != nullptr);
 }
 
+NumberTreeBackend::NumberTreeBackend(const QString& name)
+   : m_Type(NumberTreeBackend::Type::CATEGORY),m_Name(name),
+     m_MostPopular(false),m_Index(-1), m_pNumber(nullptr),m_pParent(nullptr)
+{}
+
 NumberTreeBackend::~NumberTreeBackend()
 {
    QObject::disconnect(m_Conn);
 }
 
-QObject* BookmarkTopLevelItem::getSelf() const
-{
-   return nullptr;
-}
-
 CategorizedBookmarkModel::CategorizedBookmarkModel(QObject* parent) : QAbstractItemModel(parent), CollectionManagerInterface<ContactMethod>(this),
 d_ptr(new CategorizedBookmarkModelPrivate(this))
 {
@@ -128,11 +119,9 @@ d_ptr(new CategorizedBookmarkModelPrivate(this))
    reloadCategories();
    d_ptr->m_lMimes << RingMimes::PLAIN_TEXT << RingMimes::PHONENUMBER;
 
-   //Connect
-   connect(&DBus::PresenceManager::instance(),SIGNAL(newServerSubscriptionRequest(QString)),d_ptr,SLOT(slotRequest(QString)));
-//    if (Call::contactBackend()) {
-//       connect(Call::contactBackend(),SIGNAL(collectionChanged()),this,SLOT(reloadCategories()));
-//    } //TODO implement reordering
+   if (d_ptr->displayFrequentlyUsed()) {
+      connect(PhoneDirectoryModel::instance()->mostPopularNumberModel(),&QAbstractItemModel::rowsInserted,this,&CategorizedBookmarkModel::reloadCategories);
+   }
 }
 
 CategorizedBookmarkModel* CategorizedBookmarkModel::instance()
@@ -161,7 +150,7 @@ void CategorizedBookmarkModel::reloadCategories()
       d_ptr->m_hCategories.clear();
 
       //TODO this is not efficient, nor necessary
-      foreach(BookmarkTopLevelItem* item, d_ptr->m_lCategoryCounter) {
+      foreach(NumberTreeBackend* item, d_ptr->m_lCategoryCounter) {
          foreach (NumberTreeBackend* child, item->m_lChildren) {
             auto l = d_ptr->m_Tracked[child->m_pNumber];
             if (l) {
@@ -176,55 +165,39 @@ void CategorizedBookmarkModel::reloadCategories()
 
       //Load most used contacts
       if (d_ptr->displayFrequentlyUsed()) {
-         BookmarkTopLevelItem* item = new BookmarkTopLevelItem(tr("Most popular"));
+         NumberTreeBackend* item = new NumberTreeBackend(tr("Most popular"));
          d_ptr->m_hCategories["mp"] = item;
-         item->m_Row = d_ptr->m_lCategoryCounter.size();
+         item->m_Index = d_ptr->m_lCategoryCounter.size();
          item->m_MostPopular = true;
          d_ptr->m_lCategoryCounter << item;
          const QVector<ContactMethod*> cl = PhoneDirectoryModel::instance()->getNumbersByPopularity();
-
-         for (int i=0;i<((cl.size()>=10)?10:cl.size());i++) {
-            ContactMethod* n = cl[i];
-            NumberTreeBackend* bm = new NumberTreeBackend(n);
-            bm->m_pParent    = item;
-            bm->m_Type       = NumberTreeBackend::Type::POPULAR;
-            bm->m_PopularIdx = i;
-            bm->m_Index      = item->m_lChildren.size();
-
-            bm->m_Conn = connect(n, &ContactMethod::changed, [this,bm]() {
-               d_ptr->slotIndexChanged(index(bm->m_Index,0,index(bm->m_pParent->m_Row,0)));
-            });
-
-            item->m_lChildren << bm;
-         }
-
       }
 
       foreach(ContactMethod* bookmark, d_ptr->bookmarkList()) {
          NumberTreeBackend* bm = new NumberTreeBackend(bookmark);
          const QString val = d_ptr->category(bm);
          if (!d_ptr->m_hCategories[val]) {
-            BookmarkTopLevelItem* item = new BookmarkTopLevelItem(val);
+            NumberTreeBackend* item = new NumberTreeBackend(val);
             d_ptr->m_hCategories[val] = item;
-            item->m_Row = d_ptr->m_lCategoryCounter.size();
+            item->m_Index = d_ptr->m_lCategoryCounter.size();
             d_ptr->m_lCategoryCounter << item;
          }
-         BookmarkTopLevelItem* item = d_ptr->m_hCategories[val];
+         NumberTreeBackend* item = d_ptr->m_hCategories[val];
          if (item) {
             bookmark->setBookmarked(true);
             bm->m_pParent = item;
             bm->m_Index = item->m_lChildren.size();
             bm->m_Conn = connect(bookmark, &ContactMethod::changed, [this,bm]() {
-               d_ptr->slotIndexChanged(index(bm->m_Index,0,index(bm->m_pParent->m_Row,0)));
+               d_ptr->slotIndexChanged(index(bm->m_Index,0,index(bm->m_pParent->m_Index,0)));
             });
             item->m_lChildren << bm;
 
             if (!d_ptr->m_Tracked[bookmark]) {
-               const QString displayName = d_ptr->commonCallInfo(bm).toString();
+               const QString displayName = bm->m_pNumber->roleData(Qt::DisplayRole).toString();
 
                QMetaObject::Connection conn = connect(bookmark, &ContactMethod::primaryNameChanged, [this,displayName,bm]() {
                   //If a contact arrive later, reload
-                  if (displayName != d_ptr->commonCallInfo(bm)) {
+                  if (displayName != bm->m_pNumber->roleData(Qt::DisplayRole)) {
                      reloadCategories();
                   }
                });
@@ -248,7 +221,7 @@ bool CategorizedBookmarkModel::setData( const QModelIndex& index, const QVariant
 {
    Q_UNUSED(index)
    Q_UNUSED(value)
-   Q_UNUSED(role)
+   Q_UNUSED(role )
    return false;
 }
 
@@ -258,31 +231,41 @@ QVariant CategorizedBookmarkModel::data( const QModelIndex& index, int role) con
    if (!index.isValid() || test)
       return QVariant();
 
-   CategorizedCompositeNode* modelItem = static_cast<CategorizedCompositeNode*>(index.internalPointer());
+   if (index.parent().isValid()) {
+      NumberTreeBackend* parentItem = static_cast<NumberTreeBackend*>(index.parent().internalPointer());
+      if (parentItem->m_MostPopular) {
+         return PhoneDirectoryModel::instance()->mostPopularNumberModel()->data(
+            PhoneDirectoryModel::instance()->mostPopularNumberModel()->index(index.row(),0),
+            role
+         );
+      }
+   }
+
+   NumberTreeBackend* modelItem = static_cast<NumberTreeBackend*>(index.internalPointer());
+
    if (!modelItem)
       return QVariant();
-   switch (modelItem->type()) {
-      case CategorizedCompositeNode::Type::TOP_LEVEL:
+
+   switch (modelItem->m_Type) {
+      case NumberTreeBackend::Type::CATEGORY:
          switch (role) {
             case Qt::DisplayRole:
-               return static_cast<BookmarkTopLevelItem*>(modelItem)->m_Name;
+               return modelItem->m_Name;
             case static_cast<int>(Call::Role::Name):
-               if (static_cast<BookmarkTopLevelItem*>(modelItem)->m_MostPopular) {
+               //Make sure it is at the top of the bookmarks when sorted
+               if (modelItem->m_MostPopular) {
                   return "000000";
                }
                else {
-                  return static_cast<BookmarkTopLevelItem*>(modelItem)->m_Name;
+                  return modelItem->m_Name;
                }
          }
          break;
-      case CategorizedCompositeNode::Type::BOOKMARK:
-         return d_ptr->commonCallInfo(static_cast<NumberTreeBackend*>(modelItem),role);
-         break;
-      case CategorizedCompositeNode::Type::CALL:
-      case CategorizedCompositeNode::Type::NUMBER:
-      case CategorizedCompositeNode::Type::CONTACT:
+      case NumberTreeBackend::Type::BOOKMARK:
+         return modelItem->m_pNumber->roleData(role == Qt::DisplayRole ? (int)Call::Role::Name : role);
          break;
    };
+
    return QVariant();
 } //Data
 
@@ -295,16 +278,28 @@ QVariant CategorizedBookmarkModel::headerData(int section, Qt::Orientation orien
    return QVariant();
 }
 
-
 ///Get the number of child of "parent"
 int CategorizedBookmarkModel::rowCount( const QModelIndex& parent ) const
 {
-   if (test) return 0; //HACK
    if (!parent.isValid())
       return d_ptr->m_lCategoryCounter.size();
-   else if (!parent.parent().isValid() && parent.row() < d_ptr->m_lCategoryCounter.size()) {
-      BookmarkTopLevelItem* item = static_cast<BookmarkTopLevelItem*>(parent.internalPointer());
-      return item->m_lChildren.size();
+
+   NumberTreeBackend* modelItem = static_cast<NumberTreeBackend*>(parent.internalPointer());
+
+   //Is from MostPopularModel
+   if (!modelItem)
+      return 0;
+
+   switch (modelItem->m_Type) {
+      case NumberTreeBackend::Type::CATEGORY:
+         if (modelItem->m_MostPopular) {
+            static PhoneDirectoryModel* m = PhoneDirectoryModel::instance();
+            return m->d_ptr->m_lPopularityIndex.size();
+         }
+         else
+            return modelItem->m_lChildren.size();
+      case NumberTreeBackend::Type::BOOKMARK:
+         return 0;
    }
    return 0;
 }
@@ -313,7 +308,11 @@ Qt::ItemFlags CategorizedBookmarkModel::flags( const QModelIndex& index ) const
 {
    if (!index.isValid())
       return 0;
-   return Qt::ItemIsEnabled | Qt::ItemIsSelectable | (index.parent().isValid()?Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled:Qt::ItemIsEnabled);
+   return index.isValid() ? (
+      Qt::ItemIsEnabled    |
+      Qt::ItemIsSelectable |
+      (index.parent().isValid()?Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled:Qt::ItemIsEnabled)
+      ) : Qt::NoItemFlags;
 }
 
 ///There is only 1 column
@@ -329,12 +328,16 @@ QModelIndex CategorizedBookmarkModel::parent( const QModelIndex& idx) const
    if (!idx.isValid()) {
       return QModelIndex();
    }
-   const CategorizedCompositeNode* modelItem = static_cast<CategorizedCompositeNode*>(idx.internalPointer());
-   if (modelItem->type() == CategorizedCompositeNode::Type::BOOKMARK) {
-      BookmarkTopLevelItem* item = static_cast<const NumberTreeBackend*>(modelItem)->m_pParent;
-      if (item) {
-         return index(item->m_Row,0);
-      }
+   const NumberTreeBackend* modelItem = static_cast<NumberTreeBackend*>(idx.internalPointer());
+
+   if (!modelItem)
+      return index(d_ptr->m_hCategories["mp"]->m_Index,0);
+
+   switch(modelItem->m_Type) {
+      case NumberTreeBackend::Type::BOOKMARK:
+         return index(modelItem->m_pParent->m_Index,0);
+      case NumberTreeBackend::Type::CATEGORY:
+         return QModelIndex();
    }
    return QModelIndex();
 } //parent
@@ -342,10 +345,20 @@ QModelIndex CategorizedBookmarkModel::parent( const QModelIndex& idx) const
 ///Get the index
 QModelIndex CategorizedBookmarkModel::index(int row, int column, const QModelIndex& parent) const
 {
-   if (parent.isValid() && (!column) && d_ptr->m_lCategoryCounter.size() > parent.row() && d_ptr->m_lCategoryCounter[parent.row()]->m_lChildren.size() > row)
-      return createIndex(row,column,(void*) static_cast<CategorizedCompositeNode*>(d_ptr->m_lCategoryCounter[parent.row()]->m_lChildren[row]));
-   else if (row >= 0 && row < d_ptr->m_lCategoryCounter.size() && !column) {
-      return createIndex(row,column,(void*) static_cast<CategorizedCompositeNode*>(d_ptr->m_lCategoryCounter[row]));
+   if (column)
+      return QModelIndex();
+
+   if (parent.isValid() && d_ptr->m_lCategoryCounter.size() > parent.row()) {
+      const NumberTreeBackend* modelItem = static_cast<NumberTreeBackend*>(parent.internalPointer());
+
+      if (modelItem->m_MostPopular)
+         return createIndex(row, column, nullptr);
+
+      if (modelItem->m_lChildren.size() > row)
+         return createIndex(row,column,(void*) static_cast<NumberTreeBackend*>(modelItem->m_lChildren[row]));
+   }
+   else if (row >= 0 && row < d_ptr->m_lCategoryCounter.size()) {
+      return createIndex(row,column,(void*) static_cast<NumberTreeBackend*>(d_ptr->m_lCategoryCounter[row]));
    }
    return QModelIndex();
 }
@@ -377,88 +390,19 @@ int CategorizedBookmarkModel::acceptedPayloadTypes()
    return CallModel::DropPayloadType::CALL;
 }
 
-///Get call info TODO use Call:: one
-QVariant CategorizedBookmarkModelPrivate::commonCallInfo(NumberTreeBackend* number, int role) const
-{
-   if (!number)
-      return QVariant();
-   QVariant cat;
-   switch (role) {
-      case Qt::DisplayRole:
-      case static_cast<int>(Call::Role::Name):
-         cat = number->m_pNumber->contact()?number->m_pNumber->contact()->formattedName():number->m_pNumber->primaryName();
-         break;
-      case Qt::ToolTipRole:
-         cat = number->m_pNumber->presenceMessage();
-         break;
-      case static_cast<int>(Call::Role::Number):
-         cat = number->m_pNumber->uri();//call->getPeerContactMethod();
-         break;
-      case static_cast<int>(Call::Role::Direction):
-         cat = 4;//call->getHistoryState();
-         break;
-      case static_cast<int>(Call::Role::Date):
-         cat = tr("N/A");//call->getStartTimeStamp();
-         break;
-      case static_cast<int>(Call::Role::Length):
-         cat = tr("N/A");//call->getLength();
-         break;
-      case static_cast<int>(Call::Role::FormattedDate):
-         cat = tr("N/A");//QDateTime::fromTime_t(call->getStartTimeStamp().toUInt()).toString();
-         break;
-      case static_cast<int>(Call::Role::HasAVRecording):
-         cat = false;//call->hasRecording();
-         break;
-      case static_cast<int>(Call::Role::FuzzyDate):
-         cat = "N/A";//timeToHistoryCategory(QDateTime::fromTime_t(call->getStartTimeStamp().toUInt()).date());
-         break;
-      case static_cast<int>(Call::Role::ContactMethod):
-         return QVariant::fromValue(number->m_pNumber);
-      case static_cast<int>(Call::Role::IsBookmark):
-         return true;
-      case static_cast<int>(Call::Role::Filter):
-         return number->m_pNumber->uri()+number->m_pNumber->primaryName();
-      case static_cast<int>(Call::Role::IsPresent):
-         return number->m_pNumber->isPresent();
-      case static_cast<int>(Call::Role::Photo):
-         if (number->m_pNumber->contact())
-            return number->m_pNumber->contact()->photo();
-         cat = true;
-         break;
-   }
-   return cat;
-} //commonCallInfo
-
 ///Get category
 QString CategorizedBookmarkModelPrivate::category(NumberTreeBackend* number) const
 {
-   QString cat = commonCallInfo(number).toString();
+   if (number->m_Name.size())
+      return number->m_Name;
+
+   QString cat = number->m_pNumber->roleData(Qt::DisplayRole).toString();
+
    if (cat.size())
       cat = cat[0].toUpper();
    return cat;
 }
 
-void CategorizedBookmarkModelPrivate::slotRequest(const QString& uri)
-{
-   Q_UNUSED(uri)
-   qDebug() << "Presence Request" << uri << "denied";
-   //DBus::PresenceManager::instance().answerServerRequest(uri,true); //FIXME turn on after 1.3.0
-}
-
-QVector<ContactMethod*> CategorizedBookmarkModelPrivate::serialisedToList(const QStringList& list)
-{
-   QVector<ContactMethod*> numbers;
-   foreach(const QString& item,list) {
-      ContactMethod* nb = PhoneDirectoryModel::instance()->fromHash(item);
-      if (nb) {
-         nb->setTracked(true);
-         nb->setUid(item);
-         numbers << nb;
-      }
-   }
-   return numbers;
-}
-
 bool CategorizedBookmarkModelPrivate::displayFrequentlyUsed() const
 {
    return true;
@@ -469,12 +413,6 @@ QVector<ContactMethod*> CategorizedBookmarkModelPrivate::bookmarkList() const
    return (q_ptr->collections().size() > 0) ? q_ptr->collections()[0]->items<ContactMethod>() : QVector<ContactMethod*>();
 }
 
-BookmarkTopLevelItem::BookmarkTopLevelItem(QString name)
-   : CategorizedCompositeNode(CategorizedCompositeNode::Type::TOP_LEVEL),m_Name(name),
-      m_MostPopular(false),m_Row(-1)
-{
-}
-
 bool CategorizedBookmarkModel::removeRows( int row, int count, const QModelIndex & parent)
 {
    if (parent.isValid()) {
@@ -488,7 +426,7 @@ bool CategorizedBookmarkModel::removeRows( int row, int count, const QModelIndex
          d_ptr->m_hCategories.remove(d_ptr->m_hCategories.key(d_ptr->m_lCategoryCounter[parentRow]));
          d_ptr->m_lCategoryCounter.removeAt(parentRow);
          for (int i=0;i<d_ptr->m_lCategoryCounter.size();i++) {
-            d_ptr->m_lCategoryCounter[i]->m_Row =i;
+            d_ptr->m_lCategoryCounter[i]->m_Index =i;
          }
          endRemoveRows();
       }
@@ -513,14 +451,6 @@ void CategorizedBookmarkModel::removeBookmark(ContactMethod* number)
 
 void CategorizedBookmarkModel::remove(const QModelIndex& idx)
 {
-   Q_UNUSED(idx)
-//    ContactMethod* nb = getNumber(idx);
-//    if (nb) {
-//       removeRows(idx.row(),1,idx.parent());
-//       removeBookmark(nb);
-//       emit layoutAboutToBeChanged();
-//       emit layoutChanged();
-//    }
    collections()[0]->editor<ContactMethod>()->remove(getNumber(idx));
 }
 
@@ -528,7 +458,8 @@ ContactMethod* CategorizedBookmarkModel::getNumber(const QModelIndex& idx)
 {
    if (idx.isValid()) {
       if (idx.parent().isValid() && idx.parent().row() < d_ptr->m_lCategoryCounter.size()) {
-         return d_ptr->m_lCategoryCounter[idx.parent().row()]->m_lChildren[idx.row()]->m_pNumber;
+         NumberTreeBackend* bm = d_ptr->m_lCategoryCounter[idx.parent().row()]->m_lChildren[idx.row()];
+         return bm->m_pNumber;
       }
    }
    return nullptr;
@@ -553,16 +484,6 @@ bool CategorizedBookmarkModel::removeItemCallback(const ContactMethod* item)
    return false;
 }
 
-bool CategorizedBookmarkModel::clearAllCollections() const
-{
-   foreach (CollectionInterface* backend, collections()) {
-      if (backend->supportedFeatures() & CollectionInterface::SupportedFeatures::ADD) {
-         backend->clear();
-      }
-   }
-   return true;
-}
-
 void CategorizedBookmarkModel::collectionAddedCallback(CollectionInterface* backend)
 {
    Q_UNUSED(backend)
diff --git a/src/categorizedbookmarkmodel.h b/src/categorizedbookmarkmodel.h
index 0ed7c0dc..bcb8d398 100644
--- a/src/categorizedbookmarkmodel.h
+++ b/src/categorizedbookmarkmodel.h
@@ -52,9 +52,6 @@ public:
    void setRole(int role);
    void setShowAll(bool showAll);
 
-   //Backend model implementation
-   virtual bool clearAllCollections() const override;
-
    //Model implementation
    virtual bool          setData     ( const QModelIndex& index, const QVariant &value, int role   )       override;
    virtual bool          removeRows  ( int row, int count, const QModelIndex& parent=QModelIndex() )       override;
diff --git a/src/contactmethod.cpp b/src/contactmethod.cpp
index cd4531b3..6f65c33b 100644
--- a/src/contactmethod.cpp
+++ b/src/contactmethod.cpp
@@ -474,17 +474,61 @@ QHash<QString,int> ContactMethod::alternativeNames() const
 
 QVariant ContactMethod::roleData(int role) const
 {
-   switch(role) {
+   QVariant cat;
+   switch (role) {
+      case static_cast<int>(Call::Role::Name):
+         cat = contact()?contact()->formattedName():primaryName();
+         break;
+      case Qt::ToolTipRole:
+         cat = presenceMessage();
+         break;
       case Qt::DisplayRole:
       case Qt::EditRole:
       case static_cast<int>(Role::Uri):
-         return uri();
+      case static_cast<int>(Call::Role::Number):
+         cat = uri();//call->getPeerContactMethod();
+         break;
+      case static_cast<int>(Call::Role::Direction):
+         cat = tr("N/A");//call->getHistoryState();
+         break;
+      case static_cast<int>(Call::Role::Date):
+         cat = tr("N/A");//call->getStartTimeStamp();
+         break;
+      case static_cast<int>(Call::Role::Length):
+         cat = tr("N/A");//call->getLength();
+         break;
+      case static_cast<int>(Call::Role::FormattedDate):
+         cat = tr("N/A");//QDateTime::fromTime_t(call->getStartTimeStamp().toUInt()).toString();
+         break;
+      case static_cast<int>(Call::Role::HasAVRecording):
+         cat = false;//call->hasRecording();
+         break;
+      case static_cast<int>(Call::Role::FuzzyDate):
+         cat = "N/A";//timeToHistoryCategory(QDateTime::fromTime_t(call->getStartTimeStamp().toUInt()).date());
+         break;
+      case static_cast<int>(Call::Role::ContactMethod):
       case static_cast<int>(Role::Object):
-         return QVariant::fromValue(const_cast<ContactMethod*>(this));
+         cat = QVariant::fromValue(const_cast<ContactMethod*>(this));
+         break;
+      case static_cast<int>(Call::Role::IsBookmark):
+         cat = false;
+         break;
+      case static_cast<int>(Call::Role::Filter):
+         cat = uri()+primaryName();
+         break;
+      case static_cast<int>(Call::Role::IsPresent):
+         cat = isPresent();
+         break;
+      case static_cast<int>(Call::Role::Photo):
+         if (contact())
+            cat = contact()->photo();
+         break;
       case static_cast<int>(Role::CategoryIcon):
-         return category()? d_ptr->m_pCategory->icon(isTracked(), isPresent()) : QVariant();
+         if (category())
+            cat = d_ptr->m_pCategory->icon(isTracked(), isPresent());
+         break;
    }
-   return QVariant();
+   return cat;
 }
 
 ///Add a call to the call list, notify listener
diff --git a/src/phonedirectorymodel.cpp b/src/phonedirectorymodel.cpp
index 1bb89f85..9541c1e0 100644
--- a/src/phonedirectorymodel.cpp
+++ b/src/phonedirectorymodel.cpp
@@ -42,10 +42,8 @@
 
 PhoneDirectoryModel* PhoneDirectoryModel::m_spInstance = nullptr;
 
-
-
 PhoneDirectoryModelPrivate::PhoneDirectoryModelPrivate(PhoneDirectoryModel* parent) : QObject(parent), q_ptr(parent),
-m_CallWithAccount(false)
+m_CallWithAccount(false),m_pPopularModel(nullptr)
 {
 }
 
@@ -610,10 +608,14 @@ void PhoneDirectoryModelPrivate::slotCallAdded(Call* call)
          } while (currentIndex && m_lPopularityIndex[currentIndex-1]->callCount() < number->callCount());
          number->setPopularityIndex(currentIndex);
          emit q_ptr->layoutChanged();
+         if (m_pPopularModel)
+            m_pPopularModel->reload();
       }
       //The top 10 is not complete, a call count of "1" is enough to make it
       else if (m_lPopularityIndex.size() < 10 && currentIndex == -1) {
          m_lPopularityIndex << number;
+         if (m_pPopularModel)
+            m_pPopularModel->addRow();
          number->setPopularityIndex(m_lPopularityIndex.size()-1);
          emit q_ptr->layoutChanged();
       }
@@ -625,6 +627,8 @@ void PhoneDirectoryModelPrivate::slotCallAdded(Call* call)
          number->setPopularityIndex(9);
          emit tmp->changed();
          emit number->changed();
+         if (m_pPopularModel)
+            m_pPopularModel->reload();
       }
 
       //Now check for new peer names
@@ -699,4 +703,58 @@ void PhoneDirectoryModel::setCallWithAccount(bool value) {
    d_ptr->m_CallWithAccount = value;
 }
 
+///Popular number model related code
+
+MostPopularNumberModel::MostPopularNumberModel() : QAbstractListModel(PhoneDirectoryModel::instance()) {
+   setObjectName("MostPopularNumberModel");
+}
+
+QVariant MostPopularNumberModel::data( const QModelIndex& index, int role ) const
+{
+   if (!index.isValid())
+      return QVariant();
+
+   return PhoneDirectoryModel::instance()->d_ptr->m_lPopularityIndex[index.row()]->roleData(
+      role == Qt::DisplayRole ? (int)Call::Role::Name : role
+   );
+}
+
+int MostPopularNumberModel::rowCount( const QModelIndex& parent ) const
+{
+   return parent.isValid() ? 0 : PhoneDirectoryModel::instance()->d_ptr->m_lPopularityIndex.size();
+}
+
+Qt::ItemFlags MostPopularNumberModel::flags( const QModelIndex& index ) const
+{
+   return index.isValid() ? Qt::ItemIsEnabled | Qt::ItemIsSelectable : Qt::NoItemFlags;
+}
+
+bool MostPopularNumberModel::setData( const QModelIndex& index, const QVariant &value, int role)
+{
+   Q_UNUSED(index)
+   Q_UNUSED(value)
+   Q_UNUSED(role)
+   return false;
+}
+
+void MostPopularNumberModel::addRow()
+{
+   const int oldSize = PhoneDirectoryModel::instance()->d_ptr->m_lPopularityIndex.size()-1;
+   beginInsertRows(QModelIndex(),oldSize,oldSize);
+   endInsertRows();
+}
+
+void MostPopularNumberModel::reload()
+{
+   emit dataChanged(index(0,0),index(rowCount(),0));
+}
+
+QAbstractListModel* PhoneDirectoryModel::mostPopularNumberModel() const
+{
+   if (!d_ptr->m_pPopularModel)
+      d_ptr->m_pPopularModel = new MostPopularNumberModel();
+
+   return d_ptr->m_pPopularModel;
+}
+
 #include <phonedirectorymodel.moc>
diff --git a/src/phonedirectorymodel.h b/src/phonedirectorymodel.h
index 1a409aa9..3a889d8e 100644
--- a/src/phonedirectorymodel.h
+++ b/src/phonedirectorymodel.h
@@ -30,6 +30,7 @@ class Person              ;
 class Account             ;
 class Call                ;
 class TemporaryContactMethod;
+class NumberTreeBackend;
 
 //Private
 class PhoneDirectoryModelPrivate;
@@ -40,6 +41,8 @@ class LIB_EXPORT PhoneDirectoryModel : public QAbstractTableModel {
    //NumberCompletionModel need direct access to the indexes
    friend class NumberCompletionModel;
    friend class NumberCompletionModelPrivate;
+   friend class MostPopularNumberModel;
+   friend class CategorizedBookmarkModel;
 
    //Friend unit test class
    friend class AutoCompletionTest;
@@ -78,6 +81,7 @@ public:
    //Getter
    int count() const;
    bool callWithAccount() const;
+   QAbstractListModel* mostPopularNumberModel() const;
 
    //Setters
    void setCallWithAccount(bool value);
diff --git a/src/private/phonedirectorymodel_p.h b/src/private/phonedirectorymodel_p.h
index 62d67641..9c59f336 100644
--- a/src/private/phonedirectorymodel_p.h
+++ b/src/private/phonedirectorymodel_p.h
@@ -29,6 +29,22 @@ struct NumberWrapper {
    QVector<ContactMethod*> numbers;
 };
 
+class MostPopularNumberModel final : public QAbstractListModel
+{
+   Q_OBJECT
+public:
+   MostPopularNumberModel();
+
+   //Model functions
+   virtual QVariant      data     ( const QModelIndex& index, int role = Qt::DisplayRole     ) const override;
+   virtual int           rowCount ( const QModelIndex& parent = QModelIndex()                ) const override;
+   virtual Qt::ItemFlags flags    ( const QModelIndex& index                                 ) const override;
+   virtual bool          setData  ( const QModelIndex& index, const QVariant &value, int role)       override;
+
+   void addRow();
+   void reload();
+};
+
 class PhoneDirectoryModelPrivate final : public QObject
 {
    Q_OBJECT
@@ -72,6 +88,7 @@ public:
    QMap<QString,NumberWrapper*>  m_hSortedNumbers   ;
    QHash<QString,NumberWrapper*> m_hNumbersByNames  ;
    bool                          m_CallWithAccount  ;
+   MostPopularNumberModel*       m_pPopularModel    ;
 
 private:
    PhoneDirectoryModel* q_ptr;
-- 
GitLab