diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bdf69c8049b9f7993504342f8140dcc1aa6adc60..14b027fd41ebecf42ea5a3caeae3c8eedc43cb5b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,6 +75,8 @@ set( qtsflphone_LIB_SRCS audiosettingsmodel.cpp ringtonemodel.cpp lastusednumbermodel.cpp + contactmodel.cpp + transitionalcontactbackend.cpp #Communication dbus/configurationmanager.cpp @@ -126,6 +128,8 @@ set( qtsflphone_LIB_HDRS audiosettingsmodel.h ringtonemodel.h lastusednumbermodel.h + contactmodel.h + transitionalcontactbackend.h ) set( qtsflphone_extra_LIB_HDRS diff --git a/src/abstractbookmarkmodel.cpp b/src/abstractbookmarkmodel.cpp index c59d4c1598c1f5de60df3800faa1e96fd6b0886f..b0ddfbf1b695ac71cbc3346318d2eb8728da0c06 100644 --- a/src/abstractbookmarkmodel.cpp +++ b/src/abstractbookmarkmodel.cpp @@ -73,9 +73,9 @@ AbstractBookmarkModel::AbstractBookmarkModel(QObject* parent) : QAbstractItemMod //Connect connect(&DBus::PresenceManager::instance(),SIGNAL(newServerSubscriptionRequest(QString)),this,SLOT(slotRequest(QString))); - if (Call::contactBackend()) { - connect(Call::contactBackend(),SIGNAL(collectionChanged()),this,SLOT(reloadCategories())); - } +// if (Call::contactBackend()) { +// connect(Call::contactBackend(),SIGNAL(collectionChanged()),this,SLOT(reloadCategories())); +// } //TODO implement reordering } diff --git a/src/abstractcontactbackend.cpp b/src/abstractcontactbackend.cpp index 93d6a53d2283c09e55609ab4f948b25e82240522..f9d00f681cd26663779a1f98c4fdb284c15bb4a3 100644 --- a/src/abstractcontactbackend.cpp +++ b/src/abstractcontactbackend.cpp @@ -32,63 +32,27 @@ #include <QtCore/QCoreApplication> ///Constructor -AbstractContactBackend::AbstractContactBackend(QObject* par) : QAbstractItemModel(par?par:QCoreApplication::instance()) +AbstractContactBackend::AbstractContactBackend(QObject* par) : QObject(par?par:QCoreApplication::instance()) { - connect(this,SIGNAL(collectionChanged()),this,SLOT(slotReloadModel())); } ///Destructor AbstractContactBackend::~AbstractContactBackend() { - if (Call::contactBackend() == this) - Call::setContactBackend(nullptr); - foreach (Contact* c,m_ContactByUid) { - delete c; - } +// if (Call::contactBackend() == this) +// Call::setContactBackend(nullptr); +// foreach (Contact* c,m_ContactByUid) { +// delete c; +// } //TODO uncomment } ///Update slot -ContactList AbstractContactBackend::update() -{ - return update_slot(); -} +// ContactList AbstractContactBackend::update() +// { +// return update_slot(); + //TODO do something +// } -///Called when the new contacts are added -void AbstractContactBackend::slotReloadModel() -{ - reset(); - emit layoutChanged(); - emit dataChanged(index(0,0),index(rowCount(),0)); -} - -/***************************************************************************** - * * - * Helpers * - * * - ****************************************************************************/ - -///Return the extension/user of an URI (<sip:12345@exemple.com>) -QString AbstractContactBackend::getUserFromPhone(const QString &phoneNumber) -{ - //Too slow -// if (phoneNumber.indexOf('@') != -1) { -// QString user = phoneNumber.split('@')[0]; -// return (user.indexOf(':') != -1)?user.split(':')[1]:user; -// } - int start(0),stop(0); - for (int i=0;i<phoneNumber.size();i++) { - const char c = phoneNumber[i].cell(); //Because it is fast - if (c == ':') - start = i; - else if (c == '@') { - stop = i; - break; - } - } - if (stop) - return phoneNumber.mid(start,stop); - return phoneNumber; -} //getUserFromPhone /***************************************************************************** * * @@ -97,88 +61,98 @@ QString AbstractContactBackend::getUserFromPhone(const QString &phoneNumber) ****************************************************************************/ -bool AbstractContactBackend::setData( const QModelIndex& idx, const QVariant &value, int role) -{ - Q_UNUSED(idx) - Q_UNUSED(value) - Q_UNUSED(role) - return false; -} - -QVariant AbstractContactBackend::data( const QModelIndex& idx, int role) const -{ - if (!idx.isValid()) - return QVariant(); - if (!idx.parent().isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { - const Contact* c = getContactList()[idx.row()]; - if (c) - return QVariant(c->formattedName()); - } - else if (idx.parent().isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { - const Contact* c = getContactList()[idx.parent().row()]; - if (c) - return QVariant(c->phoneNumbers()[idx.row()]->uri()); - } - return QVariant(); -} - -QVariant AbstractContactBackend::headerData(int section, Qt::Orientation orientation, int role) const -{ - Q_UNUSED(section) - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - return QVariant(tr("Contacts")); - return QVariant(); -} - -int AbstractContactBackend::rowCount( const QModelIndex& par ) const -{ - if (!par.isValid()) { - return getContactList().size(); - } - else if (!par.parent().isValid() && par.row() < getContactList().size()) { - const Contact* c = getContactList()[par.row()]; - if (c) { - const int size = c->phoneNumbers().size(); - return size==1?0:size; - } - } - return 0; -} - -Qt::ItemFlags AbstractContactBackend::flags( const QModelIndex& idx ) const -{ - if (!idx.isValid()) - return Qt::NoItemFlags; - return Qt::ItemIsEnabled | ((idx.parent().isValid())?Qt::ItemIsSelectable:Qt::ItemIsEnabled); -} +// bool AbstractContactBackend::setData( const QModelIndex& idx, const QVariant &value, int role) +// { +// Q_UNUSED(idx) +// Q_UNUSED(value) +// Q_UNUSED(role) +// return false; +// } + +// QVariant AbstractContactBackend::data( const QModelIndex& idx, int role) const +// { +// if (!idx.isValid()) +// return QVariant(); +// if (!idx.parent().isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { +// const Contact* c = getContactList()[idx.row()]; +// if (c) +// return QVariant(c->formattedName()); +// } +// else if (idx.parent().isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { +// const Contact* c = getContactList()[idx.parent().row()]; +// if (c) +// return QVariant(c->phoneNumbers()[idx.row()]->uri()); +// } +// return QVariant(); +// } + +// QVariant AbstractContactBackend::headerData(int section, Qt::Orientation orientation, int role) const +// { +// Q_UNUSED(section) +// if (orientation == Qt::Horizontal && role == Qt::DisplayRole) +// return QVariant(tr("Contacts")); +// return QVariant(); +// } + +// int AbstractContactBackend::rowCount( const QModelIndex& par ) const +// { +// if (!par.isValid()) { +// return getContactList().size(); +// } +// else if (!par.parent().isValid() && par.row() < getContactList().size()) { +// const Contact* c = getContactList()[par.row()]; +// if (c) { +// const int size = c->phoneNumbers().size(); +// return size==1?0:size; +// } +// } +// return 0; +// } + +// Qt::ItemFlags AbstractContactBackend::flags( const QModelIndex& idx ) const +// { +// if (!idx.isValid()) +// return Qt::NoItemFlags; +// return Qt::ItemIsEnabled | ((idx.parent().isValid())?Qt::ItemIsSelectable:Qt::ItemIsEnabled); +// } + +// int AbstractContactBackend::columnCount ( const QModelIndex& par) const +// { +// Q_UNUSED(par) +// return 1; +// } + +// QModelIndex AbstractContactBackend::parent( const QModelIndex& idx) const +// { +// if (!idx.isValid()) +// return QModelIndex(); +// CategorizedCompositeNode* modelItem = (CategorizedCompositeNode*)idx.internalPointer(); +// if (modelItem && modelItem->type() == CategorizedCompositeNode::Type::NUMBER) { +// int idx2 = getContactList().indexOf(((Contact::PhoneNumbers*)modelItem)->contact()); +// if (idx2 != -1) { +// return AbstractContactBackend::index(idx2,0,QModelIndex()); +// } +// } +// return QModelIndex(); +// } -int AbstractContactBackend::columnCount ( const QModelIndex& par) const -{ - Q_UNUSED(par) - return 1; -} +// QModelIndex AbstractContactBackend::index( int row, int column, const QModelIndex& par) const +// { +// if (!par.isValid() && getContactList().size() > row) { +// return createIndex(row,column,getContactList()[row]); +// } +// else if (par.isValid() && getContactList()[par.row()]->phoneNumbers().size() > row) { +// return createIndex(row,column,(CategorizedCompositeNode*)(&(getContactList()[par.row()]->phoneNumbers()))); +// } +// return QModelIndex(); +// } -QModelIndex AbstractContactBackend::parent( const QModelIndex& idx) const -{ - if (!idx.isValid()) - return QModelIndex(); - CategorizedCompositeNode* modelItem = (CategorizedCompositeNode*)idx.internalPointer(); - if (modelItem && modelItem->type() == CategorizedCompositeNode::Type::NUMBER) { - int idx2 = getContactList().indexOf(((Contact::PhoneNumbers*)modelItem)->contact()); - if (idx2 != -1) { - return AbstractContactBackend::index(idx2,0,QModelIndex()); - } - } - return QModelIndex(); -} -QModelIndex AbstractContactBackend::index( int row, int column, const QModelIndex& par) const +bool AbstractContactBackend::saveContacts(const QList<Contact*> contacts) { - if (!par.isValid() && getContactList().size() > row) { - return createIndex(row,column,getContactList()[row]); - } - else if (par.isValid() && getContactList()[par.row()]->phoneNumbers().size() > row) { - return createIndex(row,column,(CategorizedCompositeNode*)(&(getContactList()[par.row()]->phoneNumbers()))); + bool ret = true; + foreach(const Contact* c, contacts) { + ret &= saveContact(c); } - return QModelIndex(); + return ret; } diff --git a/src/abstractcontactbackend.h b/src/abstractcontactbackend.h index 1ae573f3f7be93297cb15b6128134bcf78e6c3b7..c41d4062b9297769fc50988985b607fac46d65c2 100644 --- a/src/abstractcontactbackend.h +++ b/src/abstractcontactbackend.h @@ -32,74 +32,38 @@ class Contact; class Account; -//Typedef -typedef QList<Contact*> ContactList; - ///AbstractContactBackend: Allow different way to handle contact without poluting the library -class LIB_EXPORT AbstractContactBackend : public QAbstractItemModel { +class LIB_EXPORT AbstractContactBackend : public QObject { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" Q_OBJECT #pragma GCC diagnostic pop public: - enum Role { - Organization = 100, - Group = 101, - Department = 102, - PreferredEmail = 103, - FormattedLastUsed = 104, - IndexedLastUsed = 105, - DatedLastUsed = 106, - Active = 107, - Filter = 200, //All roles, all at once - DropState = 300, //State for drag and drop + enum SupportedFeatures { + NONE = 0x0 , + LOAD = 0x1 << 0, + SAVE = 0x1 << 1, + EDIT = 0x1 << 2, + PROBE = 0x1 << 3, }; explicit AbstractContactBackend(QObject* parent = nullptr); virtual ~AbstractContactBackend(); - ///Get a contact using a phone number - ///@param resolveDNS interpret the number as is (false) or parse it to extract the domain and number (true) -// virtual Contact* getContactByPhone ( const QString& phoneNumber , bool resolveDNS = false, Account* a = nullptr) = 0; + virtual bool load() = 0; + virtual bool reload() = 0; + virtual bool saveContact(const Contact* contact) =0; + virtual bool saveContacts(const QList<Contact*> contacts); + virtual SupportedFeatures supportedFeatures() const =0; - ///Return a contact (or nullptr) according to the contact unique identifier - virtual Contact* getContactByUid ( const QString& uid ) = 0; ///Edit 'contact', the implementation may be a GUI or somehting else virtual void editContact ( Contact* contact ) = 0; ///Add a new contact to the backend virtual void addNewContact ( Contact* contact ) = 0; - - virtual const ContactList& getContactList() const = 0; ///Add a new phone number to an existing contact - virtual void addPhoneNumber( Contact* contact , QString number, QString type )=0; - - //Model implementation - virtual bool setData ( const QModelIndex& index, const QVariant &value, int role ) __attribute__ ((const)); - virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const; - virtual Qt::ItemFlags flags ( const QModelIndex& index ) const; - virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const; - virtual QModelIndex parent ( const QModelIndex& index ) const; - virtual QModelIndex index ( int row, int column, const QModelIndex& parent=QModelIndex()) const; - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; - - int getUpdateCount(); - -protected: - virtual ContactList update_slot() = 0; - - //Helper - QString getUserFromPhone (const QString &phoneNumber); - - //Attributes - QHash<QString,Contact*> m_ContactByUid ; - int m_UpdatesCounter; -public Q_SLOTS: - ContactList update(); + virtual void addPhoneNumber( Contact* contact , PhoneNumber* number )=0; -private Q_SLOTS: - void slotReloadModel(); Q_SIGNALS: void collectionChanged(); diff --git a/src/call.cpp b/src/call.cpp index c219a6c4c59c44216539b623c38f724b5fdd699f..065f4d3b1c294a501161cbbfabb48cdcee0771ca 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -46,6 +46,7 @@ #include "videorenderer.h" #include "tlsmethodmodel.h" #include "audiosettingsmodel.h" +#include "contactmodel.h" const TypedStateMachine< TypedStateMachine< Call::State , Call::Action> , Call::State> Call::actionPerformedStateMap = {{ @@ -139,19 +140,6 @@ QDebug LIB_EXPORT operator<<(QDebug dbg, const Call::Action& c) return dbg.space(); } -AbstractContactBackend* Call::m_pContactBackend = nullptr; -Call* Call::m_sSelectedCall = nullptr; - -void Call::setContactBackend(AbstractContactBackend* be) -{ - m_pContactBackend = be; -} - -AbstractContactBackend* Call::contactBackend () -{ - return m_pContactBackend; -} - ///Constructor Call::Call(Call::State startState, const QString& callId, const QString& peerName, PhoneNumber* number, Account* account) : QObject(CallModel::instance()), m_isConference(false),m_pStopTimeStamp(0), @@ -1117,7 +1105,7 @@ void Call::call() qDebug() << "Calling " << peerPhoneNumber()->uri() << " with account " << m_Account << ". callId : " << m_CallId << "ConfId:" << m_ConfId; callManager.placeCall(m_Account->id(), m_CallId, m_pDialNumber->uri()); this->m_pPeerPhoneNumber = PhoneDirectoryModel::instance()->getNumber(m_pDialNumber->uri(),account()); - if (m_pContactBackend) { + if (ContactModel::instance()->hasBackends()) { if (peerPhoneNumber()->contact()) m_PeerName = peerPhoneNumber()->contact()->formattedName(); } diff --git a/src/call.h b/src/call.h index 089187d714117017f2fd23f54d9a1da610e51326..f6f9a7cc4e9e11e4a8e45d42f14916d75cca09ab 100644 --- a/src/call.h +++ b/src/call.h @@ -31,7 +31,6 @@ class QTimer; #include "sflphone_const.h" #include "typedefs.h" #include "historytimecategorymodel.h" -class AbstractContactBackend; class Account; class VideoRenderer; class InstantMessagingModel; @@ -297,8 +296,6 @@ public: static Call* buildRingingCall (const QString& callId ); static Call* buildHistoryCall (const QMap<QString,QString>& hc ); static Call* buildExistingCall (QString callId ); - static void setContactBackend (AbstractContactBackend* be ); - static AbstractContactBackend* contactBackend (); //Static getters static Call::LegacyHistoryState historyStateFromType ( const QString& type ); @@ -371,7 +368,6 @@ private: bool m_isConference ; Call::State m_CurrentState ; bool m_Recording ; - static Call* m_sSelectedCall ; InstantMessagingModel* m_pImModel ; QTimer* m_pTimer ; UserActionModel* m_pUserActionModel; @@ -382,9 +378,6 @@ private: //Cache HistoryTimeCategoryModel::HistoryConst m_HistoryConst; - //Static attribute - static AbstractContactBackend* m_pContactBackend; - //State machine /** * actionPerformedStateMap[orig_state][action] diff --git a/src/callmodel.cpp b/src/callmodel.cpp index 5f5935aba5554b1e5c8e0278323852022c2922ee..56777542fc79f35ab96eb217f4f6821c25721d6e 100644 --- a/src/callmodel.cpp +++ b/src/callmodel.cpp @@ -36,6 +36,7 @@ #include "dbus/videomanager.h" #include "historymodel.h" #include "visitors/phonenumberselector.h" +#include "contactmodel.h" //Define ///InternalStruct: internal representation of a call @@ -819,7 +820,7 @@ bool CallModel::dropMimeData(const QMimeData* mimedata, Qt::DropAction action, i qDebug() << "Contact" << encodedContact << "on call" << target; if (PhoneNumberSelector::defaultVisitor()) { const PhoneNumber* number = PhoneNumberSelector::defaultVisitor()->getNumber( - Call::contactBackend()->getContactByUid(encodedContact)); + ContactModel::instance()->getContactByUid(encodedContact)); if (!number->uri().isEmpty()) { Call* newCall = dialingCall(); newCall->setDialNumber(number); diff --git a/src/contact.cpp b/src/contact.cpp index c8a3fcbf3a0a083c947cf666cbd297c238cc7cd8..7b1e418582d4f0d5deb37c3c6522c5a987eff24e 100644 --- a/src/contact.cpp +++ b/src/contact.cpp @@ -26,6 +26,8 @@ //SFLPhone library #include "sflphone_const.h" #include "phonenumber.h" +#include "abstractcontactbackend.h" +#include "transitionalcontactbackend.h" @@ -45,8 +47,8 @@ Contact* Contact::PhoneNumbers::contact() const } ///Constructor -Contact::Contact(QObject* parent):QObject(parent),m_pPhoto(nullptr), - m_Numbers(this),m_DisplayPhoto(nullptr),m_Active(true) +Contact::Contact(AbstractContactBackend* parent):QObject(parent?parent:TransitionalContactBackend::instance()),m_pPhoto(nullptr), + m_Numbers(this),m_DisplayPhoto(nullptr),m_Active(true),m_pBackend(parent?parent:TransitionalContactBackend::instance()) { } @@ -273,3 +275,9 @@ void Contact::slotPresenceChanged() { emit changed(); } + +///Save the contact +bool Contact::save() const +{ + return m_pBackend->saveContact(this); +} diff --git a/src/contact.h b/src/contact.h index e7e0fc20aac183acd35f0b53bb816ff4e7d2a6d4..ccb6d7faef72c06a9757e59ec9f194fc51aaed34 100644 --- a/src/contact.h +++ b/src/contact.h @@ -36,6 +36,7 @@ namespace KABC { //SFLPhone class PhoneNumber; +class AbstractContactBackend; #include "typedefs.h" #include "categorizedcompositenode.h" @@ -74,6 +75,9 @@ public: Q_PROPERTY( QString department READ department WRITE setDepartment ) Q_PROPERTY( bool active READ isActive WRITE setActive NOTIFY statusChanged ) + //Mutator + Q_INVOKABLE bool save() const; + private: QString m_FirstName ; QString m_SecondName ; @@ -88,10 +92,11 @@ private: bool m_DisplayPhoto ; PhoneNumbers m_Numbers ; bool m_Active ; + AbstractContactBackend* m_pBackend; public: //Constructors & Destructors - explicit Contact(QObject* parent = nullptr); + explicit Contact(AbstractContactBackend* parent = nullptr); virtual ~Contact(); //Getters diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff6935818036e62eb31baa1cf2aaea0ad0a7c500 --- /dev/null +++ b/src/contactmodel.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** + * Copyright (C) 2014 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/>. * + ***************************************************************************/ + +//Parent +#include "contactmodel.h" + +//SFLPhone library +#include "contact.h" +#include "call.h" +#include "phonenumber.h" + +//Qt +#include <QtCore/QHash> +#include <QtCore/QDebug> +#include <QtCore/QCoreApplication> + +ContactModel* ContactModel::m_spInstance = nullptr; + +///Constructor +ContactModel::ContactModel(QObject* par) : QAbstractItemModel(par?par:QCoreApplication::instance()) +{ + connect(this,SIGNAL(collectionChanged()),this,SLOT(slotReloadModel())); +} + +///Destructor +ContactModel::~ContactModel() +{ + m_hContactsByUid.clear(); + while (m_lContacts.size()) { + Contact* c = m_lContacts[0]; + m_lContacts.remove(0); + delete c; + } +} + +ContactModel* ContactModel::instance() { + if (!m_spInstance) + m_spInstance = new ContactModel(QCoreApplication::instance()); + return m_spInstance; +} + +/***************************************************************************** + * * + * Model * + * * + ****************************************************************************/ + + +bool ContactModel::setData( const QModelIndex& idx, const QVariant &value, int role) +{ + Q_UNUSED(idx) + Q_UNUSED(value) + Q_UNUSED(role) + return false; +} + +QVariant ContactModel::data( const QModelIndex& idx, int role) const +{ + if (!idx.isValid()) + return QVariant(); + if (!idx.parent().isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { + const Contact* c = m_lContacts[idx.row()]; + if (c) + return QVariant(c->formattedName()); + } + else if (idx.parent().isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { + const Contact* c = m_lContacts[idx.parent().row()]; + if (c) + return QVariant(c->phoneNumbers()[idx.row()]->uri()); + } + return QVariant(); +} + +QVariant ContactModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + Q_UNUSED(section) + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + return QVariant(tr("Contacts")); + return QVariant(); +} + +int ContactModel::rowCount( const QModelIndex& par ) const +{ + if (!par.isValid()) { + return m_lContacts.size(); + } + else if (!par.parent().isValid() && par.row() < m_lContacts.size()) { + const Contact* c = m_lContacts[par.row()]; + if (c) { + const int size = c->phoneNumbers().size(); + return size==1?0:size; + } + } + return 0; +} + +Qt::ItemFlags ContactModel::flags( const QModelIndex& idx ) const +{ + if (!idx.isValid()) + return Qt::NoItemFlags; + return Qt::ItemIsEnabled | ((idx.parent().isValid())?Qt::ItemIsSelectable:Qt::ItemIsEnabled); +} + +int ContactModel::columnCount ( const QModelIndex& par) const +{ + Q_UNUSED(par) + return 1; +} + +QModelIndex ContactModel::parent( const QModelIndex& idx) const +{ + if (!idx.isValid()) + return QModelIndex(); + CategorizedCompositeNode* modelItem = (CategorizedCompositeNode*)idx.internalPointer(); + if (modelItem && modelItem->type() == CategorizedCompositeNode::Type::NUMBER) { + int idx2 = m_lContacts.indexOf(((Contact::PhoneNumbers*)modelItem)->contact()); + if (idx2 != -1) { + return ContactModel::index(idx2,0,QModelIndex()); + } + } + return QModelIndex(); +} + +QModelIndex ContactModel::index( int row, int column, const QModelIndex& par) const +{ + if (!par.isValid() && m_lContacts.size() > row) { + return createIndex(row,column,m_lContacts[row]); + } + else if (par.isValid() && m_lContacts[par.row()]->phoneNumbers().size() > row) { + return createIndex(row,column,(CategorizedCompositeNode*)(&(m_lContacts[par.row()]->phoneNumbers()))); + } + return QModelIndex(); +} + + +///Find contact by UID +Contact* ContactModel::getContactByUid(const QString& uid) +{ + return m_ContactByUid[uid]; +} + +///Return if there is backends +bool ContactModel::hasBackends() const +{ + return m_lBackends.size(); +} + +bool ContactModel::addContact(Contact* c) +{ + m_lContacts << c; + m_ContactByUid[c->uid()] = c; + return true; +} + + +void ContactModel::disableContact(Contact* c) +{ + if (c) + c->setActive(false); +} + +const ContactList ContactModel::contacts() const +{ + return m_lContacts; +} diff --git a/src/contactmodel.h b/src/contactmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..9d9cdc6e35f9c09c49bd892e3e4a859bd8a39f19 --- /dev/null +++ b/src/contactmodel.h @@ -0,0 +1,116 @@ +/**************************************************************************** + * Copyright (C) 2014 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/>. * + ***************************************************************************/ +#ifndef CONTACTMODEL_H +#define CONTACTMODEL_H + +#include <QObject> +#include <QHash> +#include <QStringList> +#include <QVariant> +#include <QtCore/QAbstractItemModel> + +#include "typedefs.h" +#include "contact.h" + +//SFLPhone +class Contact; +class Account; +class AbstractContactBackend; + +//Typedef +typedef QVector<Contact*> ContactList; + +///ContactModel: Allow different way to handle contact without poluting the library +class LIB_EXPORT ContactModel : public QAbstractItemModel { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + Q_OBJECT + #pragma GCC diagnostic pop +public: + enum Role { + Organization = 100, + Group = 101, + Department = 102, + PreferredEmail = 103, + FormattedLastUsed = 104, + IndexedLastUsed = 105, + DatedLastUsed = 106, + Active = 107, + Filter = 200, //All roles, all at once + DropState = 300, //State for drag and drop + }; + + //Properties + Q_PROPERTY(bool hasBackends READ hasBackends ) + + explicit ContactModel(QObject* parent = nullptr); + virtual ~ContactModel(); + + //Mutator + bool addContact(Contact* c); + void disableContact(Contact* c); + + //Getters + Contact* getContactByUid ( const QString& uid ); + bool hasBackends () const; + const ContactList contacts() const; + + //Model implementation + virtual bool setData ( const QModelIndex& index, const QVariant &value, int role ) __attribute__ ((const)); + virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; + virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const; + virtual Qt::ItemFlags flags ( const QModelIndex& index ) const; + virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const; + virtual QModelIndex parent ( const QModelIndex& index ) const; + virtual QModelIndex index ( int row, int column, const QModelIndex& parent=QModelIndex()) const; + virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + + //Singleton + static ContactModel* instance(); + +protected: +// virtual ContactList update_slot() = 0; + + //Helper +// QString getUserFromPhone (const QString &phoneNumber); + + //Attributes + QHash<QString,Contact*> m_ContactByUid ; + int m_UpdatesCounter; + +private: + //Attributes + static ContactModel* m_spInstance; + QList<AbstractContactBackend*> m_lBackends; + + //Indexes + QHash<QByteArray,Contact*> m_hContactsByUid; + QVector<Contact*> m_lContacts; + +// public Q_SLOTS: +// ContactList update(); + +// private Q_SLOTS: +// void slotReloadModel(); + +Q_SIGNALS: + void collectionChanged(); + void newContactAdded(Contact* c); +}; + +#endif diff --git a/src/contactproxymodel.cpp b/src/contactproxymodel.cpp index 50c3240c2a395b287d00016de869ac2fd1db2a9c..c9e7068e098d76f51e0ea739e74cdf15a259ed53 100644 --- a/src/contactproxymodel.cpp +++ b/src/contactproxymodel.cpp @@ -31,6 +31,7 @@ #include "phonedirectorymodel.h" #include "historytimecategorymodel.h" #include "contact.h" +#include "contactmodel.h" class ContactTreeNode; @@ -144,15 +145,15 @@ m_pModel(parent),m_Role(role),m_ShowAll(showAll),m_lCategoryCounter() connect(m_pModel,SIGNAL(collectionChanged()),this,SLOT(reloadCategories())); connect(m_pModel,SIGNAL(newContactAdded(Contact*)),this,SLOT(slotContactAdded(Contact*))); QHash<int, QByteArray> roles = roleNames(); - roles.insert(AbstractContactBackend::Role::Organization ,QByteArray("organization") ); - roles.insert(AbstractContactBackend::Role::Group ,QByteArray("group") ); - roles.insert(AbstractContactBackend::Role::Department ,QByteArray("department") ); - roles.insert(AbstractContactBackend::Role::PreferredEmail ,QByteArray("preferredEmail") ); - roles.insert(AbstractContactBackend::Role::FormattedLastUsed ,QByteArray("formattedLastUsed")); - roles.insert(AbstractContactBackend::Role::IndexedLastUsed ,QByteArray("indexedLastUsed") ); - roles.insert(AbstractContactBackend::Role::DatedLastUsed ,QByteArray("datedLastUsed") ); - roles.insert(AbstractContactBackend::Role::Filter ,QByteArray("filter") ); - roles.insert(AbstractContactBackend::Role::DropState ,QByteArray("dropState") ); + roles.insert(ContactModel::Role::Organization ,QByteArray("organization") ); + roles.insert(ContactModel::Role::Group ,QByteArray("group") ); + roles.insert(ContactModel::Role::Department ,QByteArray("department") ); + roles.insert(ContactModel::Role::PreferredEmail ,QByteArray("preferredEmail") ); + roles.insert(ContactModel::Role::FormattedLastUsed ,QByteArray("formattedLastUsed")); + roles.insert(ContactModel::Role::IndexedLastUsed ,QByteArray("indexedLastUsed") ); + roles.insert(ContactModel::Role::DatedLastUsed ,QByteArray("datedLastUsed") ); + roles.insert(ContactModel::Role::Filter ,QByteArray("filter") ); + roles.insert(ContactModel::Role::DropState ,QByteArray("dropState") ); setRoleNames(roles); } @@ -190,11 +191,11 @@ void ContactProxyModel::reloadCategories() } endRemoveRows(); m_lCategoryCounter.clear(); - foreach(Contact* cont, m_pModel->getContactList()) { + foreach(const Contact* cont, ContactModel::instance()->contacts()) { if (cont) { const QString val = category(cont); TopLevelItem* item = getTopLevelItem(val); - ContactTreeNode* contactNode = new ContactTreeNode(cont,this); + ContactTreeNode* contactNode = new ContactTreeNode(const_cast<Contact*>(cont),this); contactNode->m_pParent3 = item; contactNode->m_Index = item->m_lChildren.size(); item->m_lChildren << contactNode; @@ -223,7 +224,7 @@ bool ContactProxyModel::setData( const QModelIndex& index, const QVariant &value { if (index.isValid() && index.parent().isValid()) { CategorizedCompositeNode* modelItem = (CategorizedCompositeNode*)index.internalPointer(); - if (role == AbstractContactBackend::Role::DropState) { + if (role == ContactModel::Role::DropState) { modelItem->setDropState(value.toInt()); emit dataChanged(index, index); return true; @@ -243,9 +244,9 @@ QVariant ContactProxyModel::data( const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: return static_cast<const TopLevelItem*>(modelItem)->m_Name; - case AbstractContactBackend::Role::IndexedLastUsed: - return index.child(0,0).data(AbstractContactBackend::Role::IndexedLastUsed); - case AbstractContactBackend::Role::Active: + case ContactModel::Role::IndexedLastUsed: + return index.child(0,0).data(ContactModel::Role::IndexedLastUsed); + case ContactModel::Role::Active: return true; default: break; @@ -256,25 +257,25 @@ QVariant ContactProxyModel::data( const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: return QVariant(c->formattedName()); - case AbstractContactBackend::Role::Organization: + case ContactModel::Role::Organization: return QVariant(c->organization()); - case AbstractContactBackend::Role::Group: + case ContactModel::Role::Group: return QVariant(c->group()); - case AbstractContactBackend::Role::Department: + case ContactModel::Role::Department: return QVariant(c->department()); - case AbstractContactBackend::Role::PreferredEmail: + case ContactModel::Role::PreferredEmail: return QVariant(c->preferredEmail()); - case AbstractContactBackend::Role::DropState: + case ContactModel::Role::DropState: return QVariant(modelItem->dropState()); - case AbstractContactBackend::Role::FormattedLastUsed: + case ContactModel::Role::FormattedLastUsed: return QVariant(HistoryTimeCategoryModel::timeToHistoryCategory(c->phoneNumbers().lastUsedTimeStamp())); - case AbstractContactBackend::Role::IndexedLastUsed: + case ContactModel::Role::IndexedLastUsed: return QVariant((int)HistoryTimeCategoryModel::timeToHistoryConst(c->phoneNumbers().lastUsedTimeStamp())); - case AbstractContactBackend::Role::Active: + case ContactModel::Role::Active: return c->isActive(); - case AbstractContactBackend::Role::DatedLastUsed: + case ContactModel::Role::DatedLastUsed: return QVariant(QDateTime::fromTime_t( c->phoneNumbers().lastUsedTimeStamp())); - case AbstractContactBackend::Role::Filter: { + case ContactModel::Role::Filter: { //Strip non essential characters like accents from the filter string QString normStripppedC; foreach(QChar char2,QString(c->formattedName()+'\n'+c->organization()+'\n'+c->group()+'\n'+ @@ -294,7 +295,7 @@ QVariant ContactProxyModel::data( const QModelIndex& index, int role) const case CategorizedCompositeNode::Type::BOOKMARK: default: switch (role) { - case AbstractContactBackend::Role::Active: + case ContactModel::Role::Active: return true; } break; @@ -505,30 +506,30 @@ int ContactProxyModel::acceptedPayloadTypes() ****************************************************************************/ -QString ContactProxyModel::category(Contact* ct) const { +QString ContactProxyModel::category(const Contact* ct) const { if (!ct) return QString(); QString cat; switch (m_Role) { - case AbstractContactBackend::Role::Organization: + case ContactModel::Role::Organization: cat = ct->organization(); break; - case AbstractContactBackend::Role::Group: + case ContactModel::Role::Group: cat = ct->group(); break; - case AbstractContactBackend::Role::Department: + case ContactModel::Role::Department: cat = ct->department(); break; - case AbstractContactBackend::Role::PreferredEmail: + case ContactModel::Role::PreferredEmail: cat = ct->preferredEmail(); break; - case AbstractContactBackend::Role::FormattedLastUsed: + case ContactModel::Role::FormattedLastUsed: cat = HistoryTimeCategoryModel::timeToHistoryCategory(ct->phoneNumbers().lastUsedTimeStamp()); break; - case AbstractContactBackend::Role::IndexedLastUsed: + case ContactModel::Role::IndexedLastUsed: cat = QString::number((int)HistoryTimeCategoryModel::timeToHistoryConst(ct->phoneNumbers().lastUsedTimeStamp())); break; - case AbstractContactBackend::Role::DatedLastUsed: + case ContactModel::Role::DatedLastUsed: cat = QDateTime::fromTime_t(ct->phoneNumbers().lastUsedTimeStamp()).toString(); break; default: diff --git a/src/contactproxymodel.h b/src/contactproxymodel.h index 89734dc53c655491c65b1c1f1a8df5db95965707..6b315d3769d368c51e1c7cf11e73f77be1884ddd 100644 --- a/src/contactproxymodel.h +++ b/src/contactproxymodel.h @@ -66,7 +66,7 @@ public: private: //Helpers - QString category(Contact* ct) const; + QString category(const Contact* ct) const; //Attributes QHash<Contact*, time_t> m_hContactByDate ; diff --git a/src/historymodel.cpp b/src/historymodel.cpp index 28dce0fde18c3294b9656f20c87dd00af04cfe5a..0599d953f70ffc34a6e922980390c713d305c8a6 100644 --- a/src/historymodel.cpp +++ b/src/historymodel.cpp @@ -96,7 +96,7 @@ Call* HistoryModel::HistoryItem::call() const ****************************************************************************/ ///Constructor -HistoryModel::HistoryModel():QAbstractItemModel(QCoreApplication::instance()),m_HistoryInit(false),m_Role(Call::Role::FuzzyDate),m_HaveContactModel(false) +HistoryModel::HistoryModel():QAbstractItemModel(QCoreApplication::instance()),m_HistoryInit(false),m_Role(Call::Role::FuzzyDate) { ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance(); const QVector< QMap<QString, QString> > history = configurationManager.getHistory(); @@ -211,10 +211,10 @@ void HistoryModel::add(Call* call) return; } - if (!m_HaveContactModel && call->contactBackend()) { - connect(((QObject*)call->contactBackend()),SIGNAL(collectionChanged()),this,SLOT(reloadCategories())); - m_HaveContactModel = true; - } +// if (!m_HaveContactModel && call->contactBackend()) { +// connect(((QObject*)call->contactBackend()),SIGNAL(collectionChanged()),this,SLOT(reloadCategories())); +// m_HaveContactModel = true; +// }//TODO implement reordering emit newHistoryCall(call); emit layoutAboutToBeChanged(); diff --git a/src/historymodel.h b/src/historymodel.h index 221028d64400feb3bf5dd47126f851c80bcdd233..d1cb089f9f69349903bea6c6d3c6bc89f365822f 100644 --- a/src/historymodel.h +++ b/src/historymodel.h @@ -127,7 +127,6 @@ private: bool m_isContactDateInit; int m_Role ; bool m_ShowAll ; - bool m_HaveContactModel ; QStringList m_lMimes ; private Q_SLOTS: diff --git a/src/phonedirectorymodel.cpp b/src/phonedirectorymodel.cpp index 1c393debe7c5a40fdca658eb5ef36aa61b8168eb..20e3cd76eec60a49c5dcdd5243dcbe7e677402fc 100644 --- a/src/phonedirectorymodel.cpp +++ b/src/phonedirectorymodel.cpp @@ -31,6 +31,7 @@ #include "abstractcontactbackend.h" #include "dbus/presencemanager.h" #include "visitors/pixmapmanipulationvisitor.h" +#include "contactmodel.h" PhoneDirectoryModel* PhoneDirectoryModel::m_spInstance = nullptr; @@ -405,7 +406,7 @@ PhoneNumber* PhoneDirectoryModel::fromHash(const QString& hash) if (fields.size() == 3) { const QString uri = fields[0]; Account* account = AccountListModel::instance()->getAccountById(fields[1]); - Contact* contact = Call::contactBackend()?Call::contactBackend()->getContactByUid(fields[2]):nullptr; + Contact* contact = ContactModel::instance()->getContactByUid(fields[2]); return getNumber(uri,contact,account); } else if (fields.size() == 1) { diff --git a/src/phonenumber.cpp b/src/phonenumber.cpp index cd19d17b13b6892717e67bc36a4200a57425dc95..09f04bf87a70854220dcea95855cf5656d2e6e78 100644 --- a/src/phonenumber.cpp +++ b/src/phonenumber.cpp @@ -113,8 +113,11 @@ void PhoneNumber::setAccount(Account* account) void PhoneNumber::setContact(Contact* contact) { m_pContact = contact; - if (contact && m_Type != PhoneNumber::Type::TEMPORARY) + if (contact && m_Type != PhoneNumber::Type::TEMPORARY) { PhoneDirectoryModel::instance()->indexNumber(this,m_hNames.keys()+QStringList(contact->formattedName())); + m_PrimaryName_cache = contact->formattedName(); + emit primaryNameChanged(m_PrimaryName_cache); + } emit changed(); } @@ -217,23 +220,32 @@ bool PhoneNumber::haveCalled() const ///Best bet for this person real name QString PhoneNumber::primaryName() const { - if (m_pContact) - return m_pContact->formattedName(); - else if (m_hNames.size() == 1) - return m_hNames.constBegin().key(); - else { - QString toReturn = tr("Unknown"); - QHash<QString,int>::const_iterator i = m_hNames.constBegin(); - int max = 0; - while (i != m_hNames.end()) { - if (i.value() > max) { - max = i.value(); - toReturn = i.key (); + //Compute the primary name + if (m_PrimaryName_cache.isEmpty()) { + QString ret; + if (m_hNames.size() == 1) + ret = m_hNames.constBegin().key(); + else { + QString toReturn = tr("Unknown"); + int max = 0; + for (QHash<QString,int>::const_iterator i = m_hNames.begin(); i != m_hNames.end(); ++i) { + if (i.value() > max) { + max = i.value(); + toReturn = i.key (); + } } - i++; + ret = toReturn; } - return toReturn; + const_cast<PhoneNumber*>(this)->m_PrimaryName_cache = ret; + emit const_cast<PhoneNumber*>(this)->primaryNameChanged(m_PrimaryName_cache); + } + //Fallback: Use the URI + if (m_PrimaryName_cache.isEmpty()) { + return uri(); } + + //Return the cached primaryname + return m_PrimaryName_cache; } ///Is this number bookmarked @@ -321,7 +333,7 @@ QString PhoneNumber::toHash() const } -///Return the domaine of an URI (<sip:12345@exemple.com>) +///Return the domaine of an URI (<sip:12345@example.com>) QString PhoneNumber::hostname() const { if (m_Uri.indexOf('@') != -1) { @@ -352,8 +364,12 @@ void PhoneNumber::incrementAlternativeName(const QString& name) { const bool needReIndexing = !m_hNames[name]; m_hNames[name]++; - if (needReIndexing && m_Type != PhoneNumber::Type::TEMPORARY) + if (needReIndexing && m_Type != PhoneNumber::Type::TEMPORARY) { PhoneDirectoryModel::instance()->indexNumber(this,m_hNames.keys()+(m_pContact?(QStringList(m_pContact->formattedName())):QStringList())); + //Invalid m_PrimaryName_cache + if (!m_pContact) + m_PrimaryName_cache.clear(); + } } void PhoneNumber::accountDestroyed(QObject* o) diff --git a/src/phonenumber.h b/src/phonenumber.h index 6714108ec97148806847127e408150cc65d1a027..b226920ee7d4a0668f8dc8237e8f779617bff3f1 100644 --- a/src/phonenumber.h +++ b/src/phonenumber.h @@ -160,6 +160,7 @@ private: bool m_IsBookmark ; int m_TotalSeconds ; QString m_Uid ; + QString m_PrimaryName_cache; //Static attributes static QHash<int,Call*> m_shMostUsed ; @@ -172,8 +173,9 @@ Q_SIGNALS: void callAdded(Call* call); void changed ( ); void presentChanged(bool); - void presenceMessageChanged(QString); + void presenceMessageChanged(const QString&); void trackedChanged(bool); + void primaryNameChanged(const QString& name); }; Q_DECLARE_METATYPE(PhoneNumber*) diff --git a/src/transitionalcontactbackend.cpp b/src/transitionalcontactbackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f9ac5bd786ef86e89534d6b092d2646befe35d67 --- /dev/null +++ b/src/transitionalcontactbackend.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** + * Copyright (C) 2014 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 "transitionalcontactbackend.h" + +AbstractContactBackend* TransitionalContactBackend::m_spInstance = nullptr; + +AbstractContactBackend* TransitionalContactBackend::instance() +{ + if (!m_spInstance) { + m_spInstance = new TransitionalContactBackend(); + } + return m_spInstance; +} + +TransitionalContactBackend::~TransitionalContactBackend() +{ +} + +TransitionalContactBackend::TransitionalContactBackend(QObject* parent) : AbstractContactBackend(parent) +{ +} + +bool TransitionalContactBackend::load() +{ + return false; +} + +bool TransitionalContactBackend::reload() +{ + return false; +} + +bool TransitionalContactBackend::saveContact(const Contact* contact) +{ + Q_UNUSED(contact) + return false; +} + +void TransitionalContactBackend::editContact( Contact* contact) +{ + Q_UNUSED(contact) +} + +void TransitionalContactBackend::addNewContact( Contact* contact) +{ + Q_UNUSED(contact) +} + +void TransitionalContactBackend::addPhoneNumber( Contact* contact , PhoneNumber* number ) +{ + Q_UNUSED(contact) + Q_UNUSED(number) +} + + +AbstractContactBackend::SupportedFeatures TransitionalContactBackend::supportedFeatures() const +{ + return AbstractContactBackend::SupportedFeatures::NONE; +} diff --git a/src/transitionalcontactbackend.h b/src/transitionalcontactbackend.h new file mode 100644 index 0000000000000000000000000000000000000000..4393b86237286d761a4490f1fbbcb7448fed92c0 --- /dev/null +++ b/src/transitionalcontactbackend.h @@ -0,0 +1,59 @@ +/**************************************************************************** + * Copyright (C) 2014 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/>. * + ***************************************************************************/ +#ifndef TRANSITIONAL_CONTACT_BACKEND +#define TRANSITIONAL_CONTACT_BACKEND + +#include "abstractcontactbackend.h" + +#include "typedefs.h" + +///Contact backend for new unsaved contacts +class LIB_EXPORT TransitionalContactBackend : public AbstractContactBackend { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + Q_OBJECT + #pragma GCC diagnostic pop +public: + + virtual ~TransitionalContactBackend(); + + virtual bool load(); + virtual bool reload(); + virtual bool saveContact(const Contact* contact); + + ///Edit 'contact', the implementation may be a GUI or somehting else + virtual void editContact ( Contact* contact ); + ///Add a new contact to the backend + virtual void addNewContact ( Contact* contact ); + + ///Add a new phone number to an existing contact + virtual void addPhoneNumber( Contact* contact , PhoneNumber* number ); + + SupportedFeatures supportedFeatures() const; + + //Singleton + static AbstractContactBackend* instance(); + +private: + explicit TransitionalContactBackend(QObject* parent = nullptr); + static AbstractContactBackend* m_spInstance; + +}; + + +#endif \ No newline at end of file