diff --git a/src/contactmethod.cpp b/src/contactmethod.cpp index 84a3878cb876906918cc06606a3592d72c3a855c..27cd53cedac24b304efb882aa3f015ebcd33b12e 100644 --- a/src/contactmethod.cpp +++ b/src/contactmethod.cpp @@ -632,9 +632,9 @@ void ContactMethod::contactRebased(Person* other) { d_ptr->m_PrimaryName_cache = other->formattedName(); d_ptr->primaryNameChanged(d_ptr->m_PrimaryName_cache); - d_ptr->changed(); + setPerson(other); - //It is a "partial" rebase, so the ContactMethod data stay the same + d_ptr->changed(); d_ptr->rebased(this); } diff --git a/src/recentmodel.cpp b/src/recentmodel.cpp index 464a96c2c05c784d0447dd47a51f460a9cb6b9b4..40395a6e3dc3b48758f69905a5f31337e856ec77 100644 --- a/src/recentmodel.cpp +++ b/src/recentmodel.cpp @@ -1,6 +1,7 @@ /************************************************************************************ - * Copyright (C) 2015 by Savoir-Faire Linux * + * Copyright (C) 2015 by Savoir-faire Linux * * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * Alexandre Lision <alexandre.lision@savoirfairelinux.com> * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -27,6 +28,10 @@ #include <personmodel.h> #include <contactmethod.h> #include <phonedirectorymodel.h> +#include <callmodel.h> +#include <categorizedhistorymodel.h> +#include <media/recordingmodel.h> +#include <media/textrecording.h> struct CallGroup { @@ -58,7 +63,7 @@ struct RecentViewNode RecentViewNode* m_pParent ; QList<RecentViewNode*> m_lChildren; union { - Person* m_pPerson ; + const Person* m_pPerson ; ContactMethod* m_pContactMethod; Call* m_pCall ; CallGroup* m_pCallGroup ; @@ -76,14 +81,15 @@ public: RecentModelPrivate(RecentModel* p); /* - * m_lTopLevelReverted hold the elements in the reverse order of - * QAbstractItemModel::index. This cause most of the energy to be - * in the bottom half of the vector, preventing std::move every time - * someone is contacted - */ + * m_lTopLevelReverted hold the elements in the reverse order of + * QAbstractItemModel::index. This cause most of the energy to be + * in the bottom half of the vector, preventing std::move every time + * someone is contacted + */ QList<RecentViewNode*> m_lTopLevelReverted; - QHash<Person*,RecentViewNode*> m_hPersonsToNodes ; + QHash<const Person*,RecentViewNode*> m_hPersonsToNodes ; QHash<ContactMethod*,RecentViewNode*> m_hCMsToNodes ; + QList<Call*> m_lCallBucket ; //Helper void insertNode(RecentViewNode* n, time_t t, bool isNew); @@ -93,9 +99,13 @@ private: RecentModel* q_ptr; public Q_SLOTS: - void slotLastUsedTimeChanged(Person* p , time_t t ); + void slotLastUsedTimeChanged(const Person* p , time_t t ); + void slotPersonAdded (const Person* p ); void slotLastUsedChanged (ContactMethod* cm, time_t t ); void slotContactChanged (ContactMethod* cm, Person* np, Person* op); + void slotCallAdded (Call* call , Call* parent ); + void slotCallStateChanged (Call* call , Call::State previousState); + void slotUpdate ( ); }; RecentModelPrivate::RecentModelPrivate(RecentModel* p) : q_ptr(p) @@ -105,23 +115,26 @@ RecentModelPrivate::RecentModelPrivate(RecentModel* p) : q_ptr(p) RecentModel::RecentModel(QObject* parent) : QAbstractItemModel(parent), d_ptr(new RecentModelPrivate(this)) { connect(PersonModel::instance() , &PersonModel::lastUsedTimeChanged , d_ptr, &RecentModelPrivate::slotLastUsedTimeChanged); + connect(PersonModel::instance() , &PersonModel::newPersonAdded , d_ptr, &RecentModelPrivate::slotPersonAdded ); connect(PhoneDirectoryModel::instance(), &PhoneDirectoryModel::lastUsedChanged, d_ptr, &RecentModelPrivate::slotLastUsedChanged ); connect(PhoneDirectoryModel::instance(), &PhoneDirectoryModel::contactChanged , d_ptr, &RecentModelPrivate::slotContactChanged ); + connect(CallModel::instance() , &CallModel::callAdded , d_ptr, &RecentModelPrivate::slotCallAdded ); + connect(CallModel::instance() , &CallModel::callStateChanged , d_ptr, &RecentModelPrivate::slotCallStateChanged ); //Fill the contacts for (int i=0; i < PersonModel::instance()->rowCount(); i++) { - Person* p = qvariant_cast<Person*>(PersonModel::instance()->data( + auto person = qvariant_cast<Person*>(PersonModel::instance()->data( PersonModel::instance()->index(i,0), static_cast<int>(Person::Role::Object) )); - if (p && p->lastUsedTime()) - d_ptr->slotLastUsedTimeChanged(p, p->lastUsedTime()); + if (person && person->lastUsedTime()) + d_ptr->slotLastUsedTimeChanged(person, person->lastUsedTime()); } //Fill the "orphan" contact methods - for (int i=0; i < PhoneDirectoryModel::instance()->rowCount(); i++) { - ContactMethod* cm = qvariant_cast<ContactMethod*>(PhoneDirectoryModel::instance()->data( + for (int i = 0; i < PhoneDirectoryModel::instance()->rowCount(); i++) { + auto cm = qvariant_cast<ContactMethod*>(PhoneDirectoryModel::instance()->data( PhoneDirectoryModel::instance()->index(i,0), static_cast<int>(PhoneDirectoryModel::Role::Object) )); @@ -129,6 +142,12 @@ RecentModel::RecentModel(QObject* parent) : QAbstractItemModel(parent), d_ptr(ne if (cm && cm->lastUsed() && !cm->contact()) d_ptr->slotLastUsedChanged(cm, cm->lastUsed()); } + + //Fill node with history data + //const CallMap callMap = CategorizedHistoryModel::instance()->getHistoryCalls(); + //Q_FOREACH(auto const &call , callMap) { + // d_ptr->slotCallAdded(call, nullptr); + //} } RecentModel::~RecentModel() @@ -172,8 +191,47 @@ time_t RecentViewNode::lastUsed() const RecentModel* RecentModel::instance() { - RecentModel* m_spInstance = new RecentModel(QCoreApplication::instance()); - return m_spInstance; + static RecentModel* instance = new RecentModel(QCoreApplication::instance()); + return instance; +} + +/** + * Check if given index has an ongoing call + * returns true if one of its child is also in the CallModel + */ +bool RecentModel::hasActiveCall(const QModelIndex &idx) +{ + if (not idx.isValid()) + return false; + + auto node = static_cast<RecentViewNode*>(idx.internalPointer()); + + auto reverseEnd = std::make_reverse_iterator(node->m_lChildren.begin()); + auto it = std::find_if (std::make_reverse_iterator(node->m_lChildren.end()), + reverseEnd, [] (RecentViewNode* child) { + return child->m_Type == RecentViewNode::Type::CALL; + }); + + return it != reverseEnd; +} + +/** + * Return the first found ongoing call of the given parent index + */ +Call* RecentModel::getActiveCall(const QModelIndex &idx) +{ + if (not idx.isValid()) + return nullptr; + + RecentViewNode* node = static_cast<RecentViewNode*>(idx.internalPointer()); + + auto reverseEnd = std::make_reverse_iterator(node->m_lChildren.begin()); + auto it = std::find_if (std::make_reverse_iterator(node->m_lChildren.end()), + reverseEnd, [] (RecentViewNode* child) { + return child->m_Type == RecentViewNode::Type::CALL; + }); + + return it != reverseEnd ? (*it)->m_uContent.m_pCall : nullptr; } QHash<int,QByteArray> RecentModel::roleNames() const @@ -226,7 +284,6 @@ int RecentModel::rowCount( const QModelIndex& parent ) const return d_ptr->m_lTopLevelReverted.size(); RecentViewNode* node = static_cast<RecentViewNode*>(parent.internalPointer()); - return node->m_lChildren.size(); } @@ -306,15 +363,18 @@ void RecentModelPrivate::insertNode(RecentViewNode* n, time_t t, bool isNew) return a->lastUsed() < t2->m_Index; }); - newPos = (*lower)->m_Index+1; + // the value pointed by the iterator returned by this function may also + // be equivalent to val, and not only greater + if (!isNew && n->m_Index == (*lower)->m_Index) { + newPos = (*lower)->m_Index; + } else + newPos = (*lower)->m_Index+1; } //Begin the transaction if (!isNew) { - if (newPos == n->m_Index) return; //Nothing to do - q_ptr->beginMoveRows(QModelIndex(), n->m_Index, n->m_Index, QModelIndex(), newPos ? newPos+1 : newPos ); m_lTopLevelReverted.removeAt(m_lTopLevelReverted.size()-1-n->m_Index); } @@ -322,22 +382,9 @@ void RecentModelPrivate::insertNode(RecentViewNode* n, time_t t, bool isNew) q_ptr->beginInsertRows(QModelIndex(),newPos,newPos); //Apply the transaction - const int updateBound = n->m_Index; - if (m_lTopLevelReverted.last()->lastUsed() <= t) { - - //TODO this happen often and is O(N), inverting m_Index would "fix" this - for (int i = m_lTopLevelReverted.size()-1; i >= updateBound; i--) - m_lTopLevelReverted[m_lTopLevelReverted.size()-1-i]->m_Index = i+1; - - m_lTopLevelReverted << n; - } - else { - m_lTopLevelReverted.insert(m_lTopLevelReverted.size()-newPos,n); - n->m_Index = newPos; - - for (int i = m_lTopLevelReverted.size()-1; i >= updateBound; i--) - m_lTopLevelReverted[m_lTopLevelReverted.size()-1-i]->m_Index = i; - + m_lTopLevelReverted.insert(m_lTopLevelReverted.size() - newPos,n); + for (int i = 0 ; i < m_lTopLevelReverted.size(); ++i) { + m_lTopLevelReverted[i]->m_Index = m_lTopLevelReverted.size() - 1 - i; } //Notify that the transaction is complete @@ -346,32 +393,41 @@ void RecentModelPrivate::insertNode(RecentViewNode* n, time_t t, bool isNew) else q_ptr->endInsertRows(); - //Uncomment if there is issues - //qDebug() << "\n\nList:" << m_lTopLevelReverted.size() << isNew; - //for (int i = 0; i<m_lTopLevelReverted.size();i++) - // qDebug() << "|||" << m_lTopLevelReverted[i]->lastUsed() << m_lTopLevelReverted[i]->m_Index << q_ptr->data(q_ptr->index(m_lTopLevelReverted.size()-1-i,0),Qt::DisplayRole); - +#if 0 + //Uncomment if there is issues + qDebug() << "\n\nList:" << m_lTopLevelReverted.size() << isNew; + for (int i = 0; i<m_lTopLevelReverted.size();i++) { + qDebug() << "|||" << m_lTopLevelReverted[i]->lastUsed() << m_lTopLevelReverted[i]->m_Index << q_ptr->data(q_ptr->index(m_lTopLevelReverted.size()-1-i,0),Qt::DisplayRole); + for (auto child : m_lTopLevelReverted[i]->m_lChildren) { + qDebug() << "|||" << "|||" << child << child->m_uContent.m_pCall->formattedName(); + } + } +#endif } void RecentModelPrivate::removeNode(RecentViewNode* n) { const int idx = n->m_Index; - const int size = m_lTopLevelReverted.size(); q_ptr->beginRemoveRows(QModelIndex(), idx, idx); - //If this assert, the data is corrupted anyway, it will crash later on - Q_ASSERT(m_lTopLevelReverted[size-idx-1] == n); - - m_lTopLevelReverted.removeAt(size-idx-1); - - for (int i = 0; i <= idx; i++) - m_lTopLevelReverted[i]->m_Index--; + m_lTopLevelReverted.removeOne(n); + if (idx < m_lTopLevelReverted.size()) { + for (int i = 0; i <= idx; i++) { + m_lTopLevelReverted[i]->m_Index--; + } + } q_ptr->endRemoveRows(); } -void RecentModelPrivate::slotLastUsedTimeChanged(Person* p, time_t t) +void RecentModelPrivate::slotPersonAdded(const Person* p) +{ + if (p) + slotLastUsedTimeChanged(p, p->lastUsedTime()); +} + +void RecentModelPrivate::slotLastUsedTimeChanged(const Person* p, time_t t) { RecentViewNode* n = m_hPersonsToNodes[p]; const bool isNew = !n; @@ -383,9 +439,15 @@ void RecentModelPrivate::slotLastUsedTimeChanged(Person* p, time_t t) n->m_pParent = nullptr ; n->m_Index = 0 ; m_hPersonsToNodes[p] = n ; + Q_FOREACH(auto cm, p->phoneNumbers()) { + if (auto cmNode = m_hCMsToNodes[cm]) + n->m_lChildren.append(cmNode->m_lChildren); + } + } insertNode(n, t, isNew); + slotUpdate(); } void RecentModelPrivate::slotLastUsedChanged(ContactMethod* cm, time_t t) @@ -403,19 +465,91 @@ void RecentModelPrivate::slotLastUsedChanged(ContactMethod* cm, time_t t) n->m_Index = 0 ; m_hCMsToNodes[cm] = n ; } - insertNode(n, t, isNew); + slotUpdate(); } } ///Remove the contact method once they are associated with a contact void RecentModelPrivate::slotContactChanged(ContactMethod* cm, Person* np, Person* op) { - if (op) - return; - + Q_UNUSED(np) RecentViewNode* n = m_hCMsToNodes[cm]; if (n) removeNode(n); } + +void RecentModelPrivate::slotCallStateChanged(Call* call, Call::State previousState) +{ + //qDebug() << "STATE CHANGED:" << call->peerContactMethod(); + RecentViewNode* n; + if (auto p = call->peerContactMethod()->contact()) { + n = m_hPersonsToNodes[p]; + } else { + n = m_hCMsToNodes[call->peerContactMethod()]; + } + if (!n) + return; + + if (call->state() == Call::State::OVER) { + // Find the active call in children of this node and remove it + auto itEnd = n->m_lChildren.end(); + auto it = std::find_if (n->m_lChildren.begin(), + itEnd, [call] (RecentViewNode* child) { + return child->m_uContent.m_pCall == call; + }); + + if (it == itEnd) { + // Call can be in the bucket (after callAdded) but not inserted + // because it failed before lastUsedChanged to be emitted + m_lCallBucket.removeOne(call); + return; + } + + q_ptr->beginRemoveRows(q_ptr->index(n->m_Index,0), (*it)->m_Index, (*it)->m_Index); + n->m_lChildren.removeAt((*it)->m_Index); + q_ptr->endRemoveRows(); + } else + emit q_ptr->dataChanged(q_ptr->index(n->m_Index,0),q_ptr->index(n->m_Index,0)); +} + +void RecentModelPrivate::slotCallAdded(Call* call, Call* parent) +{ + Q_UNUSED(parent) + + RecentViewNode* n = nullptr; + if (auto p = call->peerContactMethod()->contact()) { + n = m_hPersonsToNodes[p]; + } else { + n = m_hCMsToNodes[call->peerContactMethod()]; + } + if (!n && !m_lCallBucket.contains(call)) { + m_lCallBucket.append(call); + connect(call, &Call::lifeCycleStateChanged, this, &RecentModelPrivate::slotUpdate); + return; + } else if (n && m_lCallBucket.contains(call)) { + m_lCallBucket.removeOne(call); + //TODO: remove the connection store callbucket as a map key = call value = metaconnection + } else if (!n && m_lCallBucket.contains(call)) { + return; + } + + auto callNode = new RecentViewNode(); + callNode->m_Type = RecentViewNode::Type::CALL; + callNode->m_uContent.m_pCall = call; + callNode->m_pParent = n; + callNode->m_Index = n->m_lChildren.size(); + + q_ptr->beginInsertRows(q_ptr->index(n->m_Index,0), n->m_lChildren.size(), n->m_lChildren.size()); + n->m_lChildren.append(callNode); + q_ptr->endInsertRows(); +} + +void RecentModelPrivate::slotUpdate() +{ + Q_FOREACH(auto call, m_lCallBucket) { + qDebug() << "trying to empty bucket call:" << call; + slotCallAdded(call, nullptr); + } +} diff --git a/src/recentmodel.h b/src/recentmodel.h index db21e50dec895bfb9c4f6842f3267abceeddeb9c..30a66bd213174758466f111b02854c027c9d453d 100644 --- a/src/recentmodel.h +++ b/src/recentmodel.h @@ -1,6 +1,7 @@ /************************************************************************************ - * Copyright (C) 2015 by Savoir-Faire Linux * + * Copyright (C) 2015 by Savoir-faire Linux * * Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com> * + * Alexandre Lision <alexandre.lision@savoirfairelinux.com> * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * @@ -23,6 +24,7 @@ #include <typedefs.h> class RecentModelPrivate; +class Call; class LIB_EXPORT RecentModel : public QAbstractItemModel { @@ -41,6 +43,9 @@ public: virtual QHash<int,QByteArray> roleNames() const override; static RecentModel* instance(); + + bool hasActiveCall(const QModelIndex& parent); + Call* getActiveCall(const QModelIndex& parent); private: explicit RecentModel(QObject* parent = nullptr); virtual ~RecentModel();