Skip to content
Snippets Groups Projects
Select Git revision
  • 30660f0b0825b64e7cf2f251526b7206b9312260
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/releaseTest
  • release/releaseWindowsTest
  • release/windowsReleaseTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 1.0.0
  • 0.3.0
  • 0.2.1
  • 0.2.0
  • 0.1.0
26 results

categorizedhistorymodel.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    categorizedhistorymodel.cpp 24.23 KiB
    /****************************************************************************
     *   Copyright (C) 2012-2015 by Savoir-Faire Linux                          *
     *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> *
     *                                                                          *
     *   This library is free software; you can redistribute it and/or          *
     *   modify it under the terms of the GNU Lesser General Public             *
     *   License as published by the Free Software Foundation; either           *
     *   version 2.1 of the License, or (at your option) any later version.     *
     *                                                                          *
     *   This library is distributed in the hope that it will be useful,        *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU      *
     *   Lesser General Public License for more details.                        *
     *                                                                          *
     *   You should have received a copy of the GNU General Public License      *
     *   along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
     ***************************************************************************/
    #include "categorizedhistorymodel.h"
    
    //C include
    #include <time.h>
    
    //Qt include
    #include <QMimeData>
    #include <QCoreApplication>
    
    //Ring lib
    #include "mime.h"
    #include "dbus/callmanager.h"
    #include "dbus/configurationmanager.h"
    #include "call.h"
    #include "person.h"
    #include "contactmethod.h"
    #include "callmodel.h"
    #include "collectioneditor.h"
    #include "historytimecategorymodel.h"
    #include "lastusednumbermodel.h"
    #include "collectioninterface.h"
    #include "delegates/itemmodelstateserializationdelegate.h"
    
    /*****************************************************************************
     *                                                                           *
     *                             Private classes                               *
     *                                                                           *
     ****************************************************************************/
    
    class HistoryTopLevelItem;
    
    class CategorizedHistoryModelPrivate : public QObject
    {
       Q_OBJECT
    public:
       CategorizedHistoryModelPrivate(CategorizedHistoryModel* parent);
    
       //Model
       class HistoryItem : public CategorizedCompositeNode {
       public:
          explicit HistoryItem(Call* call);
          virtual ~HistoryItem();
          virtual QObject* getSelf() const;
          Call* call() const;
          int m_Index;
          HistoryTopLevelItem* m_pParent;
          HistoryItemNode* m_pNode;
       private:
          Call* m_pCall;
       };
    
       //Helpers
       HistoryTopLevelItem* getCategory(const Call* call);
    
       //Attributes
       static CallMap m_sHistoryCalls;
    
       //Model categories
       QVector<HistoryTopLevelItem*>       m_lCategoryCounter ;
       QHash<int,HistoryTopLevelItem*>     m_hCategories      ;
       QHash<QString,HistoryTopLevelItem*> m_hCategoryByName  ;
       int                          m_Role             ;
       QStringList                  m_lMimes           ;
    
    private:
       CategorizedHistoryModel* q_ptr;
    
    public Q_SLOTS:
       void add(Call* call);
       void reloadCategories();
       void slotChanged(const QModelIndex& idx);
    };
    
    class HistoryTopLevelItem : public CategorizedCompositeNode,public QObject {
       friend class CategorizedHistoryModel;
       friend class CategorizedHistoryModelPrivate;
    public:
       virtual QObject* getSelf() const;
       virtual ~HistoryTopLevelItem();
       int m_Index;
       int m_AbsoluteIndex;
       QVector<CategorizedHistoryModelPrivate::HistoryItem*> m_lChildren;
    private:
       explicit HistoryTopLevelItem(const QString& name, int index);
       QString m_NameStr;
       int modelRow;
    };
    
    class HistoryItemNode : public QObject //TODO remove this once Qt4 support is dropped
    {
       Q_OBJECT
    public:
       HistoryItemNode(CategorizedHistoryModel* m, Call* c, CategorizedHistoryModelPrivate::HistoryItem* backend);
       Call* m_pCall;
    private:
       CategorizedHistoryModelPrivate::HistoryItem* m_pBackend;
       CategorizedHistoryModel* m_pModel;
    private Q_SLOTS:
       void slotNumberChanged();
    Q_SIGNALS:
       void changed(const QModelIndex& idx);
    };
    
    HistoryItemNode::HistoryItemNode(CategorizedHistoryModel* m, Call* c, CategorizedHistoryModelPrivate::HistoryItem* backend) :
    m_pCall(c),m_pBackend(backend),m_pModel(m){
       connect(c,SIGNAL(changed()),this,SLOT(slotNumberChanged()));
    }
    
    void HistoryItemNode::slotNumberChanged()
    {
       emit changed(m_pModel->index(m_pBackend->m_Index,0,m_pModel->index(m_pBackend->m_pParent->m_AbsoluteIndex,0)));
    }
    
    CategorizedHistoryModel* CategorizedHistoryModel::m_spInstance    = nullptr;
    CallMap       CategorizedHistoryModelPrivate::m_sHistoryCalls          ;
    
    HistoryTopLevelItem::HistoryTopLevelItem(const QString& name, int index) : 
       CategorizedCompositeNode(CategorizedCompositeNode::Type::TOP_LEVEL),QObject(nullptr),m_Index(index),m_NameStr(name),
       m_AbsoluteIndex(-1),modelRow(-1)
    {}
    
    HistoryTopLevelItem::~HistoryTopLevelItem() {
       const int idx = CategorizedHistoryModel::m_spInstance->d_ptr->m_lCategoryCounter.indexOf(this);
       if (idx != -1)
          CategorizedHistoryModel::m_spInstance->d_ptr->m_lCategoryCounter.remove(idx);
       while(m_lChildren.size()) {
          CategorizedHistoryModelPrivate::HistoryItem* item = m_lChildren[0];
          m_lChildren.remove(0);
          delete item;
       }
    }
    
    QObject* HistoryTopLevelItem::getSelf() const
    {
       return const_cast<HistoryTopLevelItem*>(this);
    }
    
    CategorizedHistoryModelPrivate::HistoryItem::HistoryItem(Call* call) : CategorizedCompositeNode(CategorizedCompositeNode::Type::CALL),m_pCall(call),
    m_Index(0),m_pParent(nullptr),m_pNode(nullptr)
    {
       
    }
    
    CategorizedHistoryModelPrivate::HistoryItem::~HistoryItem()
    {
       delete m_pNode;
    }
    
    
    QObject* CategorizedHistoryModelPrivate::HistoryItem::getSelf() const
    {
       return const_cast<Call*>(m_pCall);
    }
    
    Call* CategorizedHistoryModelPrivate::HistoryItem::call() const
    {
       return m_pCall;
    }
    
    
    /*****************************************************************************
     *                                                                           *
     *                                 Constructor                               *
     *                                                                           *
     ****************************************************************************/
    
    CategorizedHistoryModelPrivate::CategorizedHistoryModelPrivate(CategorizedHistoryModel* parent) : QObject(parent), q_ptr(parent),
    m_Role(static_cast<int>(Call::Role::FuzzyDate))
    {
    }
    
    ///Constructor
    CategorizedHistoryModel::CategorizedHistoryModel():QAbstractItemModel(QCoreApplication::instance()),CollectionManagerInterface<Call>(this),
    d_ptr(new CategorizedHistoryModelPrivate(this))
    {
       m_spInstance  = this;
       d_ptr->m_lMimes << RingMimes::PLAIN_TEXT << RingMimes::PHONENUMBER << RingMimes::HISTORYID;
    } //initHistory
    
    ///Destructor
    CategorizedHistoryModel::~CategorizedHistoryModel()
    {
       for (int i=0; i<d_ptr->m_lCategoryCounter.size();i++) {
          delete d_ptr->m_lCategoryCounter[i];
       }
       while(d_ptr->m_lCategoryCounter.size()) {
          HistoryTopLevelItem* item = d_ptr->m_lCategoryCounter[0];
          d_ptr->m_lCategoryCounter.remove(0);
    
          delete item;
       }
       m_spInstance = nullptr;
    }
    
    QHash<int,QByteArray> CategorizedHistoryModel::roleNames() const
    {
       static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames();
       static bool initRoles = false;
       if (!initRoles) {
          initRoles = true;
          roles.insert(static_cast<int>(Call::Role::Name          ) ,QByteArray("name"          ));
          roles.insert(static_cast<int>(Call::Role::Number        ) ,QByteArray("number"        ));
          roles.insert(static_cast<int>(Call::Role::Direction     ) ,QByteArray("direction"     ));
          roles.insert(static_cast<int>(Call::Role::Date          ) ,QByteArray("date"          ));
          roles.insert(static_cast<int>(Call::Role::Length        ) ,QByteArray("length"        ));
          roles.insert(static_cast<int>(Call::Role::FormattedDate ) ,QByteArray("formattedDate" ));
          roles.insert(static_cast<int>(Call::Role::HasRecording  ) ,QByteArray("hasRecording"  ));
          roles.insert(static_cast<int>(Call::Role::Historystate  ) ,QByteArray("historyState"  ));
          roles.insert(static_cast<int>(Call::Role::Filter        ) ,QByteArray("filter"        ));
          roles.insert(static_cast<int>(Call::Role::FuzzyDate     ) ,QByteArray("fuzzyDate"     ));
          roles.insert(static_cast<int>(Call::Role::IsBookmark    ) ,QByteArray("isBookmark"    ));
          roles.insert(static_cast<int>(Call::Role::Security      ) ,QByteArray("security"      ));
          roles.insert(static_cast<int>(Call::Role::Department    ) ,QByteArray("department"    ));
          roles.insert(static_cast<int>(Call::Role::Email         ) ,QByteArray("email"         ));
          roles.insert(static_cast<int>(Call::Role::Organisation  ) ,QByteArray("organisation"  ));
          roles.insert(static_cast<int>(Call::Role::Object        ) ,QByteArray("object"        ));
          roles.insert(static_cast<int>(Call::Role::Photo         ) ,QByteArray("photo"         ));
          roles.insert(static_cast<int>(Call::Role::State         ) ,QByteArray("state"         ));
          roles.insert(static_cast<int>(Call::Role::StartTime     ) ,QByteArray("startTime"     ));
          roles.insert(static_cast<int>(Call::Role::StopTime      ) ,QByteArray("stopTime"      ));
          roles.insert(static_cast<int>(Call::Role::DropState     ) ,QByteArray("dropState"     ));
          roles.insert(static_cast<int>(Call::Role::DTMFAnimState ) ,QByteArray("dTMFAnimState" ));
          roles.insert(static_cast<int>(Call::Role::LastDTMFidx   ) ,QByteArray("lastDTMFidx"   ));
          roles.insert(static_cast<int>(Call::Role::IsRecording   ) ,QByteArray("isRecording"   ));
       }
       return roles;
    }
    
    ///Singleton
    CategorizedHistoryModel* CategorizedHistoryModel::instance()
    {
       if (!m_spInstance)
          m_spInstance = new CategorizedHistoryModel();
       return m_spInstance;
    }
    
    
    /*****************************************************************************
     *                                                                           *
     *                           History related code                            *
     *                                                                           *
     ****************************************************************************/
    ///Get the top level item based on a call
    HistoryTopLevelItem* CategorizedHistoryModelPrivate::getCategory(const Call* call)
    {
       HistoryTopLevelItem* category = nullptr;
       static QString name;
       int index = -1;
       if (m_Role == static_cast<int>(Call::Role::FuzzyDate)) {
          index = call->roleData(Call::Role::FuzzyDate).toInt();
          name = HistoryTimeCategoryModel::indexToName(index);
          category = m_hCategories[index];
       }
       else {
          name = call->roleData(m_Role).toString();
          category = m_hCategoryByName[name];
       }
       if (!category) {
          category = new HistoryTopLevelItem(name,index);
          category->modelRow = m_lCategoryCounter.size();
          //emit layoutAboutToBeChanged(); //Not necessary
          CategorizedHistoryModel::instance()->beginInsertRows(QModelIndex(),m_lCategoryCounter.size(),m_lCategoryCounter.size());
          category->m_AbsoluteIndex = m_lCategoryCounter.size();
          m_lCategoryCounter << category;
          m_hCategories    [index] = category;
          m_hCategoryByName[name ] = category;
          CategorizedHistoryModel::instance()->endInsertRows();
          //emit layoutChanged();
       }
       return category;
    }
    
    
    const CallMap CategorizedHistoryModel::getHistoryCalls() const
    {
       return d_ptr->m_sHistoryCalls;
    }
    
    ///Add to history
    void CategorizedHistoryModelPrivate::add(Call* call)
    {
       if (!call || call->lifeCycleState() != Call::LifeCycleState::FINISHED || !call->startTimeStamp()) {
          return;
       }
    
    //    if (!m_HavePersonModel && call->contactBackend()) {
    //       connect(((QObject*)call->contactBackend()),SIGNAL(collectionChanged()),this,SLOT(reloadCategories()));
    //       m_HavePersonModel = true;
    //    }//TODO implement reordering
    
       emit q_ptr->newHistoryCall(call);
       HistoryTopLevelItem* tl = getCategory(call);
       const QModelIndex& parentIdx = q_ptr->index(tl->modelRow,0);
       q_ptr->beginInsertRows(parentIdx,tl->m_lChildren.size(),tl->m_lChildren.size());
       CategorizedHistoryModelPrivate::HistoryItem* item = new CategorizedHistoryModelPrivate::HistoryItem(call);
       item->m_pParent = tl;
       item->m_pNode = new HistoryItemNode(q_ptr,call,item);
       connect(item->m_pNode,SIGNAL(changed(QModelIndex)),this,SLOT(slotChanged(QModelIndex)));
       item->m_Index = tl->m_lChildren.size();
       tl->m_lChildren << item;
    
       //Try to prevent startTimeStamp() collisions, it technically doesn't work as time_t are signed
       //we don't care
       m_sHistoryCalls[(call->startTimeStamp() << 10)+qrand()%1024] = call;
       q_ptr->endInsertRows();
       LastUsedNumberModel::instance()->addCall(call);
       emit q_ptr->historyChanged();
    
       /*
       // Loop until it find a compatible backend
       //HACK only support a single active history backend
       if (!call->collection()) {
          foreach (CollectionInterface* backend, q_ptr->collections(CollectionInterface::ADD)) {
             if (backend->editor<Call>()->addNew(call)) {
                call->setCollection(backend);
                break;
             }
          }
       }*/
    }
    
    ///Set if the history has a limit
    void CategorizedHistoryModel::setHistoryLimited(bool isLimited)
    {
       if (!isLimited)
          DBus::ConfigurationManager::instance().setHistoryLimit(0);
    }
    
    ///Set the number of days before history items are discarded
    void CategorizedHistoryModel::setHistoryLimit(int numberOfDays)
    {
       DBus::ConfigurationManager::instance().setHistoryLimit(numberOfDays);
    }
    
    ///Is history items are being deleted after "historyLimit()" days
    bool CategorizedHistoryModel::isHistoryLimited() const
    {
       return DBus::ConfigurationManager::instance().getHistoryLimit() != 0;
    }
    
    ///Number of days before items are discarded (0 = never)
    int CategorizedHistoryModel::historyLimit() const
    {
       return DBus::ConfigurationManager::instance().getHistoryLimit();
    }
    
    
    /*****************************************************************************
     *                                                                           *
     *                              Model related                                *
     *                                                                           *
     ****************************************************************************/
    
    void CategorizedHistoryModelPrivate::reloadCategories()
    {
       q_ptr->beginResetModel();
       m_hCategories.clear();
       m_hCategoryByName.clear();
       foreach(HistoryTopLevelItem* item, m_lCategoryCounter) {
          delete item;
       }
       m_lCategoryCounter.clear();
       foreach(Call* call, m_sHistoryCalls) {
          HistoryTopLevelItem* category = getCategory(call);
          if (category) {
             HistoryItem* item = new HistoryItem(call);
             item->m_Index = category->m_lChildren.size();
             item->m_pNode = new HistoryItemNode(q_ptr,call,item);
             connect(item->m_pNode,SIGNAL(changed(QModelIndex)),this,SLOT(slotChanged(QModelIndex)));
             item->m_pParent = category;
             category->m_lChildren << item;
          }
          else
             qDebug() << "ERROR count";
       }
       q_ptr->endResetModel();
       emit q_ptr->layoutAboutToBeChanged();
       emit q_ptr->layoutChanged();
       emit q_ptr->dataChanged(q_ptr->index(0,0),q_ptr->index(q_ptr->rowCount()-1,0));
    }
    
    void CategorizedHistoryModelPrivate::slotChanged(const QModelIndex& idx)
    {
       emit q_ptr->dataChanged(idx,idx);
    }
    
    bool CategorizedHistoryModel::setData( const QModelIndex& idx, const QVariant &value, int role)
    {
       if (idx.isValid() && idx.parent().isValid()) {
          CategorizedCompositeNode* modelItem = (CategorizedCompositeNode*)idx.internalPointer();
          if (role == static_cast<int>(Call::Role::DropState)) {
             modelItem->setDropState(value.toInt());
             emit dataChanged(idx, idx);
          }
       }
       return false;
    }
    
    QVariant CategorizedHistoryModel::data( const QModelIndex& idx, int role) const
    {
       if (!idx.isValid())
          return QVariant();
    
       CategorizedCompositeNode* modelItem = static_cast<CategorizedCompositeNode*>(idx.internalPointer());
       switch (modelItem->type()) {
          case CategorizedCompositeNode::Type::TOP_LEVEL:
          switch (role) {
             case Qt::DisplayRole:
                return static_cast<HistoryTopLevelItem*>(modelItem)->m_NameStr;
             case static_cast<int>(Call::Role::FuzzyDate):
             case static_cast<int>(Call::Role::Date):
                return d_ptr->m_lCategoryCounter.size() - static_cast<HistoryTopLevelItem*>(modelItem)->m_Index;
             default:
                break;
          }
          break;
       case CategorizedCompositeNode::Type::CALL:
          if (role == static_cast<int>(Call::Role::DropState))
             return QVariant(modelItem->dropState());
          else {
             const int parRow = idx.parent().row();
             const HistoryTopLevelItem* parTli = d_ptr->m_lCategoryCounter[parRow];
    
             //HACK force a reload
             #if QT_VERSION >= 0x050400
             if (parTli->isActive() && !parTli->m_lChildren[idx.row()]->call()->isActive()) {
                QTimer::singleShot(0,[this,idx]() {
                   emit dataChanged(idx,idx);
                });
             }
             #endif
    
             if (d_ptr->m_lCategoryCounter.size() > parRow && parRow >= 0 && parTli && parTli->m_lChildren.size() > idx.row())
                return parTli->m_lChildren[idx.row()]->call()->roleData((Call::Role)role);
          }
          break;
       case CategorizedCompositeNode::Type::NUMBER:
       case CategorizedCompositeNode::Type::BOOKMARK:
       case CategorizedCompositeNode::Type::CONTACT:
       default:
          break;
       };
       return QVariant();
    }
    
    QVariant CategorizedHistoryModel::headerData(int section, Qt::Orientation orientation, int role) const
    {
       Q_UNUSED(section)
       if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
          return QVariant(tr("History"));
       if (role == Qt::InitialSortOrderRole)
          return QVariant(Qt::DescendingOrder);
       return QVariant();
    }
    
    int CategorizedHistoryModel::rowCount( const QModelIndex& parentIdx ) const
    {
       if ((!parentIdx.isValid()) || (!parentIdx.internalPointer())) {
          return d_ptr->m_lCategoryCounter.size();
       }
       else {
          CategorizedCompositeNode* node = static_cast<CategorizedCompositeNode*>(parentIdx.internalPointer());
          switch(node->type()) {
             case CategorizedCompositeNode::Type::TOP_LEVEL:
                return ((HistoryTopLevelItem*)node)->m_lChildren.size();
             case CategorizedCompositeNode::Type::CALL:
             case CategorizedCompositeNode::Type::NUMBER:
             case CategorizedCompositeNode::Type::BOOKMARK:
             case CategorizedCompositeNode::Type::CONTACT:
             default:
                return 0;
          };
       }
    }
    
    Qt::ItemFlags CategorizedHistoryModel::flags( const QModelIndex& idx ) const
    {
       if (!idx.isValid())
          return Qt::NoItemFlags;
    
       CategorizedCompositeNode* node = static_cast<CategorizedCompositeNode*>(idx.internalPointer());
       const bool hasParent = node->type() != CategorizedCompositeNode::Type::TOP_LEVEL;
       const bool isEnabled = node->type() == CategorizedCompositeNode::Type::CALL && ((Call*)((CategorizedCompositeNode*)(idx.internalPointer()))->getSelf())->isActive();
    
       return (isEnabled?Qt::ItemIsEnabled:Qt::NoItemFlags) | Qt::ItemIsSelectable | (hasParent?Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled:Qt::ItemIsEnabled);
    }
    
    int CategorizedHistoryModel::columnCount ( const QModelIndex& parentIdx) const
    {
       Q_UNUSED(parentIdx)
       return 1;
    }
    
    QModelIndex CategorizedHistoryModel::parent( const QModelIndex& idx) const
    {
       if (!idx.isValid() || !idx.internalPointer()) {
          return QModelIndex();
       }
       CategorizedCompositeNode* modelItem = static_cast<CategorizedCompositeNode*>(idx.internalPointer());
       if (modelItem && modelItem->type() == CategorizedCompositeNode::Type::CALL) {
          const Call* call = (Call*)((CategorizedCompositeNode*)(idx.internalPointer()))->getSelf();
          //TODO this is called way to often to use getCategory, make sure getSelf return the node and cache this
          HistoryTopLevelItem* tli = d_ptr->getCategory(call);
          if (tli)
             return CategorizedHistoryModel::index(tli->modelRow,0);
       }
       return QModelIndex();
    }
    
    QModelIndex CategorizedHistoryModel::index( int row, int column, const QModelIndex& parentIdx) const
    {
       if (!parentIdx.isValid()) {
          if (row >= 0 && d_ptr->m_lCategoryCounter.size() > row) {
             return createIndex(row,column,(void*)d_ptr->m_lCategoryCounter[row]);
          }
       }
       else {
          CategorizedCompositeNode* node = static_cast<CategorizedCompositeNode*>(parentIdx.internalPointer());
          switch(node->type()) {
             case CategorizedCompositeNode::Type::TOP_LEVEL:
                if (((HistoryTopLevelItem*)node)->m_lChildren.size() > row)
                   return createIndex(row,column,(void*)static_cast<CategorizedCompositeNode*>(((HistoryTopLevelItem*)node)->m_lChildren[row]));
                break;
             case CategorizedCompositeNode::Type::CALL:
             case CategorizedCompositeNode::Type::NUMBER:
             case CategorizedCompositeNode::Type::BOOKMARK:
             case CategorizedCompositeNode::Type::CONTACT:
                break;
          };
       }
       return QModelIndex();
    }
    
    ///Called when dynamically adding calls, otherwise the proxy filter will segfault
    bool CategorizedHistoryModel::insertRows( int row, int count, const QModelIndex & parent)
    {
       if (parent.isValid()) {
          beginInsertRows(parent,row,row+count-1);
          endInsertRows();
          return true;
       }
       return false;
    }
    
    QStringList CategorizedHistoryModel::mimeTypes() const
    {
       return d_ptr->m_lMimes;
    }
    
    QMimeData* CategorizedHistoryModel::mimeData(const QModelIndexList &indexes) const
    {
       QMimeData *mimeData2 = new QMimeData();
       foreach (const QModelIndex &idx, indexes) {
          if (idx.isValid()) {
             const QString text = data(idx, static_cast<int>(Call::Role::Number)).toString();
             mimeData2->setData(RingMimes::PLAIN_TEXT , text.toUtf8());
             const Call* call = (Call*)((CategorizedCompositeNode*)(idx.internalPointer()))->getSelf();
             mimeData2->setData(RingMimes::PHONENUMBER, call->peerContactMethod()->toHash().toUtf8());
             CategorizedCompositeNode* node = static_cast<CategorizedCompositeNode*>(idx.internalPointer());
             if (node->type() == CategorizedCompositeNode::Type::CALL)
                mimeData2->setData(RingMimes::HISTORYID  , static_cast<Call*>(node->getSelf())->dringId().toUtf8());
             return mimeData2;
          }
       }
       return mimeData2;
    }
    
    bool CategorizedHistoryModel::dropMimeData(const QMimeData *mime, Qt::DropAction action, int row, int column, const QModelIndex &parentIdx)
    {
       Q_UNUSED(row)
       Q_UNUSED(column)
       Q_UNUSED(action)
       setData(parentIdx,-1,static_cast<int>(Call::Role::DropState));
       QByteArray encodedContactMethod = mime->data( RingMimes::PHONENUMBER );
       QByteArray encodedPerson     = mime->data( RingMimes::CONTACT     );
    
       if (parentIdx.isValid() && mime->hasFormat( RingMimes::CALLID)) {
          QByteArray encodedCallId      = mime->data( RingMimes::CALLID      );
          Call* call = CallModel::instance()->fromMime(encodedCallId);
          if (call) {
             const QModelIndex& idx = index(row,column,parentIdx);
             if (idx.isValid()) {
                const Call* target = (Call*)((CategorizedCompositeNode*)(idx.internalPointer()))->getSelf();
                if (target) {
                   CallModel::instance()->transfer(call,target->peerContactMethod());
                   return true;
                }
             }
          }
       }
       return false;
    }
    
    void CategorizedHistoryModel::collectionAddedCallback(CollectionInterface* backend)
    {
       Q_UNUSED(backend)
    }
    
    ///Call all collections that support clearing
    bool CategorizedHistoryModel::clearAllCollections() const
    {
       foreach (CollectionInterface* backend, collections()) { //TODO use the filter API
          if (backend->supportedFeatures() & CollectionInterface::CLEAR) {
             backend->clear();
          }
       }
       return true;
    }
    
    bool CategorizedHistoryModel::addItemCallback(const Call* item)
    {
       d_ptr->add(const_cast<Call*>(item));
       return true;
    }
    
    bool CategorizedHistoryModel::removeItemCallback(const Call* item)
    {
       Q_UNUSED(item)
       emit const_cast<Call*>(item)->changed();
       return false;
    }
    
    ///Return valid payload types
    int CategorizedHistoryModel::acceptedPayloadTypes() const
    {
       return CallModel::DropPayloadType::CALL;
    }
    
    void CategorizedHistoryModel::setCategoryRole(int role)
    {
       if (d_ptr->m_Role != role) {
          d_ptr->m_Role = role;
          d_ptr->reloadCategories();
       }
    }
    
    #include <categorizedhistorymodel.moc>