diff --git a/CMakeLists.txt b/CMakeLists.txt index b058758192903c605c4f40cdeb2b940a42e750af..1b6d89bc91a66db08fe58bad978a80358ff387c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ INCLUDE(GNUInstallDirs) INCLUDE(CMakePackageConfigHelpers) INCLUDE(GenerateExportHeader) -ADD_DEFINITIONS("-std=c++14") +ADD_DEFINITIONS("-std=c++1y") ADD_DEFINITIONS( ${QT_DEFINITIONS} @@ -175,6 +175,7 @@ SET( libringclient_LIB_SRCS #Models src/accountmodel.cpp + src/availableaccountmodel.cpp src/callmodel.cpp src/historymodel.cpp src/bookmarkmodel.cpp @@ -249,6 +250,7 @@ SET( libringclient_LIB_SRCS SET( libringclient_LIB_HDRS src/account.h src/accountmodel.h + src/availableaccountmodel.h src/call.h src/callmodel.h src/historymodel.h diff --git a/src/account.cpp b/src/account.cpp index 8a2741f020a4d73ebc709520a36fea52f554b8d4..de939910b581a0c479e9618b03106e854eaa5ad8 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -70,7 +70,7 @@ m_pVideoCodecs(nullptr),m_LastErrorCode(-1),m_VoiceMailCount(0),m_pRingToneModel m_CurrentState(Account::EditState::READY), m_pAccountNumber(nullptr),m_pKeyExchangeModel(nullptr),m_pSecurityValidationModel(nullptr),m_pTlsMethodModel(nullptr), m_pCaCert(nullptr),m_pTlsCert(nullptr),m_pPrivateKey(nullptr),m_isLoaded(true),m_pCipherModel(nullptr), -m_pStatusModel(nullptr),m_LastTransportCode(0) +m_pStatusModel(nullptr),m_LastTransportCode(0),m_RegistrationState(Account::RegistrationState::UNREGISTERED) { Q_Q(Account); } @@ -219,29 +219,29 @@ const QString Account::toHumanStateName() const static const QString invalid = tr("Invalid" ); static const QString requestTimeout = tr("Request Timeout" ); - if(s == Account::State::REGISTERED ) + if(s == DRing::Account::States::REGISTERED ) return registered ; - if(s == Account::State::UNREGISTERED ) + if(s == DRing::Account::States::UNREGISTERED ) return notRegistered ; - if(s == Account::State::TRYING ) + if(s == DRing::Account::States::TRYING ) return trying ; - if(s == Account::State::ERROR ) + if(s == DRing::Account::States::ERROR ) return d_ptr->m_LastErrorMessage.isEmpty()?error:d_ptr->m_LastErrorMessage; - if(s == Account::State::ERROR_AUTH ) + if(s == DRing::Account::States::ERROR_AUTH ) return authenticationFailed ; - if(s == Account::State::ERROR_NETWORK ) + if(s == DRing::Account::States::ERROR_NETWORK ) return networkUnreachable ; - if(s == Account::State::ERROR_HOST ) + if(s == DRing::Account::States::ERROR_HOST ) return hostUnreachable ; - if(s == Account::State::ERROR_CONF_STUN ) + if(s == DRing::Account::States::ERROR_CONF_STUN ) return stunConfigurationError ; - if(s == Account::State::ERROR_EXIST_STUN ) + if(s == DRing::Account::States::ERROR_EXIST_STUN ) return stunServerInvalid ; - if(s == Account::State::ERROR_SERVICE_UNAVAILABLE ) + if(s == DRing::Account::States::ERROR_SERVICE_UNAVAILABLE ) return serviceUnavailable ; - if(s == Account::State::ERROR_NOT_ACCEPTABLE ) + if(s == DRing::Account::States::ERROR_NOT_ACCEPTABLE ) return notAcceptable ; - if(s == Account::State::REQUEST_TIMEOUT ) + if(s == DRing::Account::States::REQUEST_TIMEOUT ) return requestTimeout ; return invalid ; } @@ -259,8 +259,9 @@ const QString AccountPrivate::accountDetail(const QString& param) const else if (m_hAccountDetails.count() > 0) { if (param == DRing::Account::ConfProperties::ENABLED) //If an account is invalid, at least does not try to register it return AccountPrivate::RegistrationEnabled::NO; - if (param == DRing::Account::ConfProperties::Registration::STATUS) //If an account is new, then it is unregistered - return Account::State::UNREGISTERED; + if (param == DRing::Account::ConfProperties::Registration::STATUS) { //If an account is new, then it is unregistered + return DRing::Account::States::UNREGISTERED; + } if (q_ptr->protocol() != Account::Protocol::IAX) //IAX accounts lack some fields, be quiet qDebug() << "Account parameter \"" << param << "\" not found"; return QString(); @@ -277,15 +278,10 @@ const QString Account::alias() const return d_ptr->accountDetail(DRing::Account::ConfProperties::ALIAS); } -///Is this account registered -bool Account::isRegistered() const -{ - return (d_ptr->accountDetail(DRing::Account::ConfProperties::Registration::STATUS) == Account::State::REGISTERED); -} - ///Return the model index of this item QModelIndex Account::index() const { + //There is usually < 5 accounts, the loop may be faster than a hash for most users for (int i=0;i < AccountModel::instance()->size();i++) { if (this == (*AccountModel::instance())[i]) { return AccountModel::instance()->index(i,0); @@ -297,11 +293,17 @@ QModelIndex Account::index() const ///Return status color name QString Account::stateColorName() const { - if(registrationStatus() == Account::State::UNREGISTERED) - return "black"; - if(registrationStatus() == Account::State::REGISTERED || registrationStatus() == Account::State::READY) - return "darkGreen"; - return "red"; + switch(registrationState()) { + case RegistrationState::READY: + return "darkGreen"; + case RegistrationState::UNREGISTERED: + return "black"; + case RegistrationState::TRYING: + return "orange"; + case RegistrationState::ERROR: + return "red"; + }; + return QString(); } ///I @@ -664,9 +666,9 @@ QString Account::localInterface() const } ///Return the account registration status -QString Account::registrationStatus() const +Account::RegistrationState Account::registrationState() const { - return d_ptr->accountDetail(DRing::Account::ConfProperties::Registration::STATUS); + return d_ptr->m_RegistrationState; } ///Return the account type @@ -743,6 +745,7 @@ QString Account::userAgent() const return d_ptr->accountDetail(DRing::Account::ConfProperties::USER_AGENT); } +#define CAST(item) static_cast<int>(item) QVariant Account::roleData(int role) const { switch(role) { @@ -758,95 +761,98 @@ QVariant Account::roleData(int role) const return AccountListColorDelegate::instance()->getIcon(this); //Specialized - case Account::Role::Alias: + case CAST(Account::Role::Alias): return alias(); - case Account::Role::Proto: + case CAST(Account::Role::Proto): return static_cast<int>(protocol()); - case Account::Role::Hostname: + case CAST(Account::Role::Hostname): return hostname(); - case Account::Role::Username: + case CAST(Account::Role::Username): return username(); - case Account::Role::Mailbox: + case CAST(Account::Role::Mailbox): return mailbox(); - case Account::Role::Proxy: + case CAST(Account::Role::Proxy): return proxy(); // case Password: // return accountPassword(); - case Account::Role::TlsPassword: + case CAST(Account::Role::TlsPassword): return tlsPassword(); - case Account::Role::TlsCaListCertificate: + case CAST(Account::Role::TlsCaListCertificate): return tlsCaListCertificate()?tlsCaListCertificate()->path().toLocalFile():QVariant(); - case Account::Role::TlsCertificate: + case CAST(Account::Role::TlsCertificate): return tlsCertificate()?tlsCertificate()->path().toLocalFile():QVariant(); - case Account::Role::TlsPrivateKeyCertificate: + case CAST(Account::Role::TlsPrivateKeyCertificate): return tlsPrivateKeyCertificate()?tlsPrivateKeyCertificate()->path().toLocalFile():QVariant(); - case Account::Role::TlsServerName: + case CAST(Account::Role::TlsServerName): return tlsServerName(); - case Account::Role::SipStunServer: + case CAST(Account::Role::SipStunServer): return sipStunServer(); - case Account::Role::PublishedAddress: + case CAST(Account::Role::PublishedAddress): return publishedAddress(); - case Account::Role::LocalInterface: + case CAST(Account::Role::LocalInterface): return localInterface(); - case Account::Role::RingtonePath: + case CAST(Account::Role::RingtonePath): return ringtonePath(); - case Account::Role::RegistrationExpire: + case CAST(Account::Role::RegistrationExpire): return registrationExpire(); - case Account::Role::TlsNegotiationTimeoutSec: + case CAST(Account::Role::TlsNegotiationTimeoutSec): return tlsNegotiationTimeoutSec(); - case Account::Role::LocalPort: + case CAST(Account::Role::LocalPort): return localPort(); - case Account::Role::TlsListenerPort: + case CAST(Account::Role::TlsListenerPort): return tlsListenerPort(); - case Account::Role::PublishedPort: + case CAST(Account::Role::PublishedPort): return publishedPort(); - case Account::Role::Enabled: + case CAST(Account::Role::Enabled): return isEnabled(); - case Account::Role::AutoAnswer: + case CAST(Account::Role::AutoAnswer): return isAutoAnswer(); - case Account::Role::TlsVerifyServer: + case CAST(Account::Role::TlsVerifyServer): return isTlsVerifyServer(); - case Account::Role::TlsVerifyClient: + case CAST(Account::Role::TlsVerifyClient): return isTlsVerifyClient(); - case Account::Role::TlsRequireClientCertificate: + case CAST(Account::Role::TlsRequireClientCertificate): return isTlsRequireClientCertificate(); - case Account::Role::TlsEnabled: + case CAST(Account::Role::TlsEnabled): return isTlsEnabled(); - case Account::Role::DisplaySasOnce: + case CAST(Account::Role::DisplaySasOnce): return isDisplaySasOnce(); - case Account::Role::SrtpRtpFallback: + case CAST(Account::Role::SrtpRtpFallback): return isSrtpRtpFallback(); - case Account::Role::ZrtpDisplaySas: + case CAST(Account::Role::ZrtpDisplaySas): return isZrtpDisplaySas(); - case Account::Role::ZrtpNotSuppWarning: + case CAST(Account::Role::ZrtpNotSuppWarning): return isZrtpNotSuppWarning(); - case Account::Role::ZrtpHelloHash: + case CAST(Account::Role::ZrtpHelloHash): return isZrtpHelloHash(); - case Account::Role::SipStunEnabled: + case CAST(Account::Role::SipStunEnabled): return isSipStunEnabled(); - case Account::Role::PublishedSameAsLocal: + case CAST(Account::Role::PublishedSameAsLocal): return isPublishedSameAsLocal(); - case Account::Role::RingtoneEnabled: + case CAST(Account::Role::RingtoneEnabled): return isRingtoneEnabled(); - case Account::Role::dTMFType: + case CAST(Account::Role::dTMFType): return DTMFType(); - case Account::Role::Id: + case CAST(Account::Role::Id): return id(); - case Account::Role::Object: { + case CAST(Account::Role::Object): { QVariant var; var.setValue(const_cast<Account*>(this)); return var; } - case Account::Role::TypeName: + case CAST(Account::Role::TypeName): return static_cast<int>(protocol()); - case Account::Role::PresenceStatus: + case CAST(Account::Role::PresenceStatus): return PresenceStatusModel::instance()->currentStatus(); - case Account::Role::PresenceMessage: + case CAST(Account::Role::PresenceMessage): return PresenceStatusModel::instance()->currentMessage(); + case CAST(Account::Role::RegistrationState): + return QVariant::fromValue(registrationState()); default: return QVariant(); } } +#undef CAST /***************************************************************************** @@ -868,6 +874,8 @@ bool AccountPrivate::setAccountProperty(const QString& param, const QString& val { const bool accChanged = m_hAccountDetails[param] != val; const QString buf = m_hAccountDetails[param]; + //Status can be changed regardless of the EditState + //TODO make this more generic for volatile properties if (param == DRing::Account::ConfProperties::Registration::STATUS) { m_hAccountDetails[param] = val; if (accChanged) { @@ -1220,139 +1228,141 @@ void Account::setDTMFType(DtmfType type) d_ptr->setAccountProperty(DRing::Account::ConfProperties::DTMF_TYPE,(type==OverRtp)?"overrtp":"oversip"); } +#define CAST(item) static_cast<int>(item) ///Proxy for AccountModel::setData void Account::setRoleData(int role, const QVariant& value) { switch(role) { - case Account::Role::Alias: + case CAST(Account::Role::Alias): setAlias(value.toString()); break; - case Account::Role::Proto: { + case CAST(Account::Role::Proto): { const int proto = value.toInt(); setProtocol((proto>=0&&proto<=1)?static_cast<Account::Protocol>(proto):Account::Protocol::SIP); break; } - case Account::Role::Hostname: + case CAST(Account::Role::Hostname): setHostname(value.toString()); break; - case Account::Role::Username: + case CAST(Account::Role::Username): setUsername(value.toString()); break; - case Account::Role::Mailbox: + case CAST(Account::Role::Mailbox): setMailbox(value.toString()); break; - case Account::Role::Proxy: + case CAST(Account::Role::Proxy): setProxy(value.toString()); break; // case Password: // accountPassword(); - case Account::Role::TlsPassword: + case CAST(Account::Role::TlsPassword): setTlsPassword(value.toString()); break; - case Account::Role::TlsCaListCertificate: { + case CAST(Account::Role::TlsCaListCertificate): { const QString path = value.toString(); if ((tlsCaListCertificate() && tlsCaListCertificate()->path() != QUrl(path)) || !tlsCaListCertificate()) { tlsCaListCertificate()->setPath(path); } break; } - case Account::Role::TlsCertificate: { + case CAST(Account::Role::TlsCertificate): { const QString path = value.toString(); if ((tlsCertificate() && tlsCertificate()->path() != QUrl(path)) || !tlsCertificate()) tlsCertificate()->setPath(path); } break; - case Account::Role::TlsPrivateKeyCertificate: { + case CAST(Account::Role::TlsPrivateKeyCertificate): { const QString path = value.toString(); if ((tlsPrivateKeyCertificate() && tlsPrivateKeyCertificate()->path() != QUrl(path)) || !tlsPrivateKeyCertificate()) tlsPrivateKeyCertificate()->setPath(path); } break; - case Account::Role::TlsServerName: + case CAST(Account::Role::TlsServerName): setTlsServerName(value.toString()); break; - case Account::Role::SipStunServer: + case CAST(Account::Role::SipStunServer): setSipStunServer(value.toString()); break; - case Account::Role::PublishedAddress: + case CAST(Account::Role::PublishedAddress): setPublishedAddress(value.toString()); break; - case Account::Role::LocalInterface: + case CAST(Account::Role::LocalInterface): setLocalInterface(value.toString()); break; - case Account::Role::RingtonePath: + case CAST(Account::Role::RingtonePath): setRingtonePath(value.toString()); break; - case Account::Role::KeyExchange: { + case CAST(Account::Role::KeyExchange): { const int method = value.toInt(); setKeyExchange(method<=keyExchangeModel()->rowCount()?static_cast<KeyExchangeModel::Type>(method):KeyExchangeModel::Type::NONE); break; } - case Account::Role::RegistrationExpire: + case CAST(Account::Role::RegistrationExpire): setRegistrationExpire(value.toInt()); break; - case Account::Role::TlsNegotiationTimeoutSec: + case CAST(Account::Role::TlsNegotiationTimeoutSec): setTlsNegotiationTimeoutSec(value.toInt()); break; - case Account::Role::LocalPort: + case CAST(Account::Role::LocalPort): setLocalPort(value.toInt()); break; - case Account::Role::TlsListenerPort: + case CAST(Account::Role::TlsListenerPort): setTlsListenerPort(value.toInt()); break; - case Account::Role::PublishedPort: + case CAST(Account::Role::PublishedPort): setPublishedPort(value.toInt()); break; - case Account::Role::Enabled: + case CAST(Account::Role::Enabled): setEnabled(value.toBool()); break; - case Account::Role::AutoAnswer: + case CAST(Account::Role::AutoAnswer): setAutoAnswer(value.toBool()); break; - case Account::Role::TlsVerifyServer: + case CAST(Account::Role::TlsVerifyServer): setTlsVerifyServer(value.toBool()); break; - case Account::Role::TlsVerifyClient: + case CAST(Account::Role::TlsVerifyClient): setTlsVerifyClient(value.toBool()); break; - case Account::Role::TlsRequireClientCertificate: + case CAST(Account::Role::TlsRequireClientCertificate): setTlsRequireClientCertificate(value.toBool()); break; - case Account::Role::TlsEnabled: + case CAST(Account::Role::TlsEnabled): setTlsEnabled(value.toBool()); break; - case Account::Role::DisplaySasOnce: + case CAST(Account::Role::DisplaySasOnce): setDisplaySasOnce(value.toBool()); break; - case Account::Role::SrtpRtpFallback: + case CAST(Account::Role::SrtpRtpFallback): setSrtpRtpFallback(value.toBool()); break; - case Account::Role::ZrtpDisplaySas: + case CAST(Account::Role::ZrtpDisplaySas): setZrtpDisplaySas(value.toBool()); break; - case Account::Role::ZrtpNotSuppWarning: + case CAST(Account::Role::ZrtpNotSuppWarning): setZrtpNotSuppWarning(value.toBool()); break; - case Account::Role::ZrtpHelloHash: + case CAST(Account::Role::ZrtpHelloHash): setZrtpHelloHash(value.toBool()); break; - case Account::Role::SipStunEnabled: + case CAST(Account::Role::SipStunEnabled): setSipStunEnabled(value.toBool()); break; - case Account::Role::PublishedSameAsLocal: + case CAST(Account::Role::PublishedSameAsLocal): setPublishedSameAsLocal(value.toBool()); break; - case Account::Role::RingtoneEnabled: + case CAST(Account::Role::RingtoneEnabled): setRingtoneEnabled(value.toBool()); break; - case Account::Role::dTMFType: + case CAST(Account::Role::dTMFType): setDTMFType((DtmfType)value.toInt()); break; - case Account::Role::Id: + case CAST(Account::Role::Id): setId(value.toByteArray()); break; } } +#undef CAST /***************************************************************************** @@ -1375,7 +1385,7 @@ bool Account::performAction(const Account::EditAction action) } ///Get the current account edition state -Account::EditState Account::state() const +Account::EditState Account::editState() const { return d_ptr->m_CurrentState; } @@ -1387,11 +1397,18 @@ bool AccountPrivate::updateState() { if(! q_ptr->isNew()) { ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance(); - const MapStringString details = configurationManager.getAccountDetails(q_ptr->id()); - const QString status = details[DRing::Account::ConfProperties::Registration::STATUS]; - const QString currentStatus = q_ptr->registrationStatus(); + const MapStringString details = configurationManager.getVolatileAccountDetails(q_ptr->id()); + const QString status = details[DRing::Account::VolatileProperties::Registration::STATUS]; + const Account::RegistrationState cst = q_ptr->registrationState(); + const Account::RegistrationState st = AccountModelPrivate::fromDaemonName(status); + setAccountProperty(DRing::Account::ConfProperties::Registration::STATUS, status); //Update -internal- object state - return status == currentStatus; + m_RegistrationState = st; + + if (st != cst) + emit q_ptr->stateChanged(q_ptr->registrationState()); + + return st == cst; } return true; } @@ -1481,7 +1498,9 @@ void AccountPrivate::reload() } m_CurrentState = Account::EditState::READY; + //TODO port this to the URI class helpers, this doesn't cover all corner cases const QString currentUri = QString("%1@%2").arg(q_ptr->username()).arg(m_HostName); + if (!m_pAccountNumber || (m_pAccountNumber && m_pAccountNumber->uri() != currentUri)) { if (m_pAccountNumber) { disconnect(m_pAccountNumber,SIGNAL(presenceMessageChanged(QString)),this,SLOT(slotPresenceMessageChanged(QString))); @@ -1498,6 +1517,9 @@ void AccountPrivate::reload() q_ptr->reloadCredentials(); emit q_ptr->changed(q_ptr); + //The registration state is cached, update that cache + updateState(); + AccountModel::instance()->d_ptr->slotVolatileAccountDetailsChange(q_ptr->id(),configurationManager.getVolatileAccountDetails(q_ptr->id())); } } diff --git a/src/account.h b/src/account.h index 0df58fcd995e198d80607af51c4676dbf8d62c69..64d2eb9550bbe7584b43e64c06e8aff4a8d13a1d 100644 --- a/src/account.h +++ b/src/account.h @@ -127,7 +127,7 @@ class LIB_EXPORT Account : public QObject { Q_PROPERTY(int audioPortMax READ audioPortMax WRITE setAudioPortMax ) Q_PROPERTY(int audioPortMin READ audioPortMin WRITE setAudioPortMin ) Q_PROPERTY(QString userAgent READ userAgent WRITE setUserAgent ) - + Q_PROPERTY(RegistrationState registrationState READ registrationState ) public: ///@enum EditState: Manage how and when an account can be reloaded or change state @@ -151,24 +151,16 @@ class LIB_EXPORT Account : public QObject { CANCEL = 6 }; - class State { - public: - constexpr static const char* REGISTERED = "REGISTERED" ; - constexpr static const char* READY = "READY" ; - constexpr static const char* UNREGISTERED = "UNREGISTERED" ; - constexpr static const char* TRYING = "TRYING" ; - constexpr static const char* ERROR = "ERROR" ; - constexpr static const char* ERROR_AUTH = "ERRORAUTH" ; - constexpr static const char* ERROR_NETWORK = "ERRORNETWORK" ; - constexpr static const char* ERROR_HOST = "ERRORHOST" ; - constexpr static const char* ERROR_CONF_STUN = "ERROR_CONF_STUN" ; - constexpr static const char* ERROR_EXIST_STUN = "ERROREXISTSTUN" ; - constexpr static const char* ERROR_SERVICE_UNAVAILABLE = "ERRORSERVICEUNAVAILABLE"; - constexpr static const char* ERROR_NOT_ACCEPTABLE = "ERRORNOTACCEPTABLE" ; - constexpr static const char* REQUEST_TIMEOUT = "Request Timeout" ; + ///@enum RegistrationState The account state from a client point of view + enum class RegistrationState { + READY = 0, + UNREGISTERED = 1, + TRYING = 2, + ERROR = 3, }; + Q_ENUMS(RegistrationState) - enum Role { + enum class Role { Alias = 100, Proto = 101, Hostname = 102, @@ -211,6 +203,7 @@ class LIB_EXPORT Account : public QObject { TypeName = 141, PresenceStatus = 142, PresenceMessage = 143, + RegistrationState = 144, }; class ProtocolName { @@ -231,14 +224,13 @@ class LIB_EXPORT Account : public QObject { * @return If the state changed */ bool performAction(Account::EditAction action); - Account::EditState state() const; + Account::EditState editState() const; //Getters bool isNew () const; const QByteArray id () const; const QString toHumanStateName() const; const QString alias () const; - bool isRegistered () const; QModelIndex index () const; QString stateColorName () const; QVariant stateColor () const; @@ -292,7 +284,6 @@ class LIB_EXPORT Account : public QObject { int localPort () const; int voiceMailCount () const; QString localInterface () const; - QString registrationStatus () const; DtmfType DTMFType () const; bool presenceStatus () const; QString presenceMessage () const; @@ -307,6 +298,7 @@ class LIB_EXPORT Account : public QObject { int lastTransportErrorCode () const; QString lastTransportErrorMessage () const; QString userAgent () const; + RegistrationState registrationState () const; Account::Protocol protocol () const; KeyExchangeModel::Type keyExchange () const; QVariant roleData (int role) const; @@ -386,7 +378,7 @@ class LIB_EXPORT Account : public QObject { Q_SIGNALS: ///The account state (Invalid,Trying,Registered) changed - void stateChanged(QString state); + void stateChanged(Account::RegistrationState state); ///One of the account property changed //TODO Qt5 drop the account parameter void propertyChanged(Account* a, const QString& name, const QString& newVal, const QString& oldVal); @@ -399,6 +391,7 @@ class LIB_EXPORT Account : public QObject { }; // Q_DISABLE_COPY(Account) Q_DECLARE_METATYPE(Account*) +Q_DECLARE_METATYPE(Account::RegistrationState) /** * Some accounts can be loaded at later time. This object will be upgraded diff --git a/src/accountmodel.cpp b/src/accountmodel.cpp index 0a524cb358bed5c95a59c1709fff7d6213cd8919..e4306e095c1d293cf473837a00dec734c0e58706 100644 --- a/src/accountmodel.cpp +++ b/src/accountmodel.cpp @@ -36,35 +36,8 @@ #include "dbus/callmanager.h" #include "dbus/instancemanager.h" -AccountModel* AccountModel::m_spAccountList = nullptr; -Account* AccountModel::m_spPriorAccount = nullptr; QHash<QByteArray,AccountPlaceHolder*> AccountModelPrivate::m_hsPlaceHolder; - -QVariant AccountListNoCheckProxyModel::data(const QModelIndex& idx,int role ) const -{ - if (role == Qt::CheckStateRole) { - return QVariant(); - } - return AccountModel::instance()->data(idx,role); -} - -bool AccountListNoCheckProxyModel::setData( const QModelIndex& idx, const QVariant &value, int role) -{ - return AccountModel::instance()->setData(idx,value,role); -} - -Qt::ItemFlags AccountListNoCheckProxyModel::flags (const QModelIndex& idx) const -{ - const QModelIndex& src = AccountModel::instance()->index(idx.row(),idx.column()); - if (!idx.row() || AccountModel::instance()->data(src,Qt::CheckStateRole) == Qt::Unchecked) - return Qt::NoItemFlags; - return AccountModel::instance()->flags(idx); -} - -int AccountListNoCheckProxyModel::rowCount(const QModelIndex& parentIdx ) const -{ - return AccountModel::instance()->rowCount(parentIdx); -} +AccountModel* AccountModelPrivate::m_spAccountList; AccountModelPrivate::AccountModelPrivate(AccountModel* parent) : QObject(parent),q_ptr(parent), m_pIP2IP(nullptr) @@ -106,59 +79,64 @@ AccountModel::~AccountModel() d_ptr->m_lAccounts.remove(0); delete a; } + for(Account* a : d_ptr->m_pRemovedAccounts) { + delete a; + } delete d_ptr; } +#define CAST(item) static_cast<int>(item) QHash<int,QByteArray> AccountModel::roleNames() const { static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); static bool initRoles = false; if (!initRoles) { initRoles = true; - roles.insert(Account::Role::Alias ,QByteArray("alias" )); - roles.insert(Account::Role::Proto ,QByteArray("protocol" )); - roles.insert(Account::Role::Hostname ,QByteArray("hostname" )); - roles.insert(Account::Role::Username ,QByteArray("username" )); - roles.insert(Account::Role::Mailbox ,QByteArray("mailbox" )); - roles.insert(Account::Role::Proxy ,QByteArray("proxy" )); - roles.insert(Account::Role::TlsPassword ,QByteArray("tlsPassword" )); - roles.insert(Account::Role::TlsCaListCertificate ,QByteArray("tlsCaListCertificate" )); - roles.insert(Account::Role::TlsCertificate ,QByteArray("tlsCertificate" )); - roles.insert(Account::Role::TlsPrivateKeyCertificate ,QByteArray("tlsPrivateKeyCertificate" )); - roles.insert(Account::Role::TlsServerName ,QByteArray("tlsServerName" )); - roles.insert(Account::Role::SipStunServer ,QByteArray("sipStunServer" )); - roles.insert(Account::Role::PublishedAddress ,QByteArray("publishedAddress" )); - roles.insert(Account::Role::LocalInterface ,QByteArray("localInterface" )); - roles.insert(Account::Role::RingtonePath ,QByteArray("ringtonePath" )); - roles.insert(Account::Role::RegistrationExpire ,QByteArray("registrationExpire" )); - roles.insert(Account::Role::TlsNegotiationTimeoutSec ,QByteArray("tlsNegotiationTimeoutSec" )); - roles.insert(Account::Role::TlsNegotiationTimeoutMsec,QByteArray("tlsNegotiationTimeoutMsec" )); - roles.insert(Account::Role::LocalPort ,QByteArray("localPort" )); - roles.insert(Account::Role::TlsListenerPort ,QByteArray("tlsListenerPort" )); - roles.insert(Account::Role::PublishedPort ,QByteArray("publishedPort" )); - roles.insert(Account::Role::Enabled ,QByteArray("enabled" )); - roles.insert(Account::Role::AutoAnswer ,QByteArray("autoAnswer" )); - roles.insert(Account::Role::TlsVerifyServer ,QByteArray("tlsVerifyServer" )); - roles.insert(Account::Role::TlsVerifyClient ,QByteArray("tlsVerifyClient" )); - roles.insert(Account::Role::TlsRequireClientCertificate,QByteArray("tlsRequireClientCertificate" )); - roles.insert(Account::Role::TlsEnabled ,QByteArray("tlsEnabled" )); - roles.insert(Account::Role::DisplaySasOnce ,QByteArray("displaySasOnce" )); - roles.insert(Account::Role::SrtpRtpFallback ,QByteArray("srtpRtpFallback" )); - roles.insert(Account::Role::ZrtpDisplaySas ,QByteArray("zrtpDisplaySas" )); - roles.insert(Account::Role::ZrtpNotSuppWarning ,QByteArray("zrtpNotSuppWarning" )); - roles.insert(Account::Role::ZrtpHelloHash ,QByteArray("zrtpHelloHash" )); - roles.insert(Account::Role::SipStunEnabled ,QByteArray("sipStunEnabled" )); - roles.insert(Account::Role::PublishedSameAsLocal ,QByteArray("publishedSameAsLocal" )); - roles.insert(Account::Role::RingtoneEnabled ,QByteArray("ringtoneEnabled" )); - roles.insert(Account::Role::dTMFType ,QByteArray("dTMFType" )); - roles.insert(Account::Role::Id ,QByteArray("id" )); - roles.insert(Account::Role::Object ,QByteArray("object" )); - roles.insert(Account::Role::TypeName ,QByteArray("typeName" )); - roles.insert(Account::Role::PresenceStatus ,QByteArray("presenceStatus" )); - roles.insert(Account::Role::PresenceMessage ,QByteArray("presenceMessage" )); + roles.insert(CAST(Account::Role::Alias ) ,QByteArray("alias" )); + roles.insert(CAST(Account::Role::Proto ) ,QByteArray("protocol" )); + roles.insert(CAST(Account::Role::Hostname ) ,QByteArray("hostname" )); + roles.insert(CAST(Account::Role::Username ) ,QByteArray("username" )); + roles.insert(CAST(Account::Role::Mailbox ) ,QByteArray("mailbox" )); + roles.insert(CAST(Account::Role::Proxy ) ,QByteArray("proxy" )); + roles.insert(CAST(Account::Role::TlsPassword ) ,QByteArray("tlsPassword" )); + roles.insert(CAST(Account::Role::TlsCaListCertificate ) ,QByteArray("tlsCaListCertificate" )); + roles.insert(CAST(Account::Role::TlsCertificate ) ,QByteArray("tlsCertificate" )); + roles.insert(CAST(Account::Role::TlsPrivateKeyCertificate ) ,QByteArray("tlsPrivateKeyCertificate" )); + roles.insert(CAST(Account::Role::TlsServerName ) ,QByteArray("tlsServerName" )); + roles.insert(CAST(Account::Role::SipStunServer ) ,QByteArray("sipStunServer" )); + roles.insert(CAST(Account::Role::PublishedAddress ) ,QByteArray("publishedAddress" )); + roles.insert(CAST(Account::Role::LocalInterface ) ,QByteArray("localInterface" )); + roles.insert(CAST(Account::Role::RingtonePath ) ,QByteArray("ringtonePath" )); + roles.insert(CAST(Account::Role::RegistrationExpire ) ,QByteArray("registrationExpire" )); + roles.insert(CAST(Account::Role::TlsNegotiationTimeoutSec ) ,QByteArray("tlsNegotiationTimeoutSec" )); + roles.insert(CAST(Account::Role::TlsNegotiationTimeoutMsec ) ,QByteArray("tlsNegotiationTimeoutMsec" )); + roles.insert(CAST(Account::Role::LocalPort ) ,QByteArray("localPort" )); + roles.insert(CAST(Account::Role::TlsListenerPort ) ,QByteArray("tlsListenerPort" )); + roles.insert(CAST(Account::Role::PublishedPort ) ,QByteArray("publishedPort" )); + roles.insert(CAST(Account::Role::Enabled ) ,QByteArray("enabled" )); + roles.insert(CAST(Account::Role::AutoAnswer ) ,QByteArray("autoAnswer" )); + roles.insert(CAST(Account::Role::TlsVerifyServer ) ,QByteArray("tlsVerifyServer" )); + roles.insert(CAST(Account::Role::TlsVerifyClient ) ,QByteArray("tlsVerifyClient" )); + roles.insert(CAST(Account::Role::TlsRequireClientCertificate ) ,QByteArray("tlsRequireClientCertificate" )); + roles.insert(CAST(Account::Role::TlsEnabled ) ,QByteArray("tlsEnabled" )); + roles.insert(CAST(Account::Role::DisplaySasOnce ) ,QByteArray("displaySasOnce" )); + roles.insert(CAST(Account::Role::SrtpRtpFallback ) ,QByteArray("srtpRtpFallback" )); + roles.insert(CAST(Account::Role::ZrtpDisplaySas ) ,QByteArray("zrtpDisplaySas" )); + roles.insert(CAST(Account::Role::ZrtpNotSuppWarning ) ,QByteArray("zrtpNotSuppWarning" )); + roles.insert(CAST(Account::Role::ZrtpHelloHash ) ,QByteArray("zrtpHelloHash" )); + roles.insert(CAST(Account::Role::SipStunEnabled ) ,QByteArray("sipStunEnabled" )); + roles.insert(CAST(Account::Role::PublishedSameAsLocal ) ,QByteArray("publishedSameAsLocal" )); + roles.insert(CAST(Account::Role::RingtoneEnabled ) ,QByteArray("ringtoneEnabled" )); + roles.insert(CAST(Account::Role::dTMFType ) ,QByteArray("dTMFType" )); + roles.insert(CAST(Account::Role::Id ) ,QByteArray("id" )); + roles.insert(CAST(Account::Role::Object ) ,QByteArray("object" )); + roles.insert(CAST(Account::Role::TypeName ) ,QByteArray("typeName" )); + roles.insert(CAST(Account::Role::PresenceStatus ) ,QByteArray("presenceStatus" )); + roles.insert(CAST(Account::Role::PresenceMessage ) ,QByteArray("presenceMessage" )); } return roles; } +#undef CAST ///Get the IP2IP account Account* AccountModel::ip2ip() const @@ -175,19 +153,45 @@ Account* AccountModel::ip2ip() const ///Singleton AccountModel* AccountModel::instance() { - if (! m_spAccountList) { - m_spAccountList = new AccountModel(); - m_spAccountList->d_ptr->init(); + if (! AccountModelPrivate::m_spAccountList) { + AccountModelPrivate::m_spAccountList = new AccountModel(); + AccountModelPrivate::m_spAccountList->d_ptr->init(); } - return m_spAccountList; + return AccountModelPrivate::m_spAccountList; } -///Static destructor -void AccountModel::destroy() +/** + * The client have a different point of view when it come to the account + * state. All the different errors are also handled elsewhere + */ +Account::RegistrationState AccountModelPrivate::fromDaemonName(const QString& st) { - if (m_spAccountList) - delete m_spAccountList; - m_spAccountList = nullptr; + if ( st == DRing::Account::States::REGISTERED + || st == DRing::Account::States::READY ) + return Account::RegistrationState::READY; + + else if( st == DRing::Account::States::UNREGISTERED ) + return Account::RegistrationState::UNREGISTERED; + + else if( st == DRing::Account::States::TRYING ) + return Account::RegistrationState::TRYING; + + else if( st == DRing::Account::States::ERROR + || st == DRing::Account::States::ERROR_AUTH + || st == DRing::Account::States::ERROR_NETWORK + || st == DRing::Account::States::ERROR_HOST + || st == DRing::Account::States::ERROR_CONF_STUN + || st == DRing::Account::States::ERROR_EXIST_STUN + || st == DRing::Account::States::ERROR_SERVICE_UNAVAILABLE + || st == DRing::Account::States::ERROR_NOT_ACCEPTABLE + || st == DRing::Account::States::REQUEST_TIMEOUT ) + return Account::RegistrationState::ERROR; + + else { + qWarning() << "Unkown registration state" << st; + return Account::RegistrationState::ERROR; + } + } ///Account status changed @@ -195,10 +199,13 @@ void AccountModelPrivate::slotAccountChanged(const QString& account,const QStrin { Account* a = q_ptr->getById(account.toLatin1()); - if (!a || (a && a->registrationStatus() != status )) { + //TODO move this to AccountStatusModel + if (!a || (a && a->d_ptr->m_LastSipRegistrationStatus != status )) { if (status != "OK") //Do not pollute the log qDebug() << "Account" << account << "status changed to" << status; } + a->d_ptr->m_LastSipRegistrationStatus = status; + ConfigurationManagerInterface& configurationManager = DBus::ConfigurationManager::instance(); //The account may have been deleted by the user, but 'apply' have not been pressed @@ -216,7 +223,7 @@ void AccountModelPrivate::slotAccountChanged(const QString& account,const QStrin } foreach (Account* acc, m_lAccounts) { const int idx =accountIds.indexOf(acc->id()); - if (idx == -1 && (acc->state() == Account::EditState::READY || acc->state() == Account::EditState::REMOVED)) { + if (idx == -1 && (acc->editState() == Account::EditState::READY || acc->editState() == Account::EditState::REMOVED)) { m_lAccounts.remove(idx); emit q_ptr->dataChanged(q_ptr->index(idx - 1, 0), q_ptr->index(m_lAccounts.size()-1, 0)); emit q_ptr->layoutChanged(); @@ -224,19 +231,18 @@ void AccountModelPrivate::slotAccountChanged(const QString& account,const QStrin } } else { - const bool isRegistered = a->isRegistered(); + const bool isRegistered = a->registrationState() == Account::RegistrationState::READY; a->d_ptr->updateState(); - emit a->stateChanged(a->toHumanStateName()); const QModelIndex idx = a->index(); emit q_ptr->dataChanged(idx, idx); - const bool regStateChanged = isRegistered != a->isRegistered(); + const bool regStateChanged = isRegistered != (a->registrationState() == Account::RegistrationState::READY); //Handle some important events directly if (regStateChanged && (code == 502 || code == 503)) { emit q_ptr->badGateway(); } else if (regStateChanged) - emit q_ptr->registrationChanged(a,a->isRegistered()); + emit q_ptr->registrationChanged(a,a->registrationState() == Account::RegistrationState::READY); //Send the messages to AccountStatusModel for processing a->statusModel()->addSipRegistrationEvent(status,code); @@ -246,9 +252,10 @@ void AccountModelPrivate::slotAccountChanged(const QString& account,const QStrin a->setLastErrorCode(code); //Make sure volatile details get reloaded + //TODO eventually remove this call and trust the signal slotVolatileAccountDetailsChange(account,configurationManager.getVolatileAccountDetails(account)); - emit q_ptr->accountStateChanged(a,a->toHumanStateName()); + emit q_ptr->accountStateChanged(a,a->registrationState()); } } @@ -294,11 +301,15 @@ void AccountModelPrivate::slotVolatileAccountDetailsChange(const QString& accoun if (a) { const int transportCode = details[DRing::Account::VolatileProperties::Transport::STATE_CODE].toInt(); const QString transportDesc = details[DRing::Account::VolatileProperties::Transport::STATE_DESC]; + const QString status = details[DRing::Account::VolatileProperties::Registration::STATUS]; a->statusModel()->addTransportEvent(transportDesc,transportCode); a->d_ptr->m_LastTransportCode = transportCode; a->d_ptr->m_LastTransportMessage = transportDesc; + + const Account::RegistrationState state = fromDaemonName(a->d_ptr->accountDetail(DRing::Account::ConfProperties::Registration::STATUS)); + a->d_ptr->m_RegistrationState = state; } } @@ -312,9 +323,9 @@ void AccountModel::update() for (int i = 0; i < tmp.size(); i++) { Account* current = tmp[i]; - if (!current->isNew() && (current->state() != Account::EditState::NEW - && current->state() != Account::EditState::MODIFIED - && current->state() != Account::EditState::OUTDATED)) + if (!current->isNew() && (current->editState() != Account::EditState::NEW + && current->editState() != Account::EditState::MODIFIED + && current->editState() != Account::EditState::OUTDATED)) remove(current); } //ask for the list of accounts ids to the configurationManager @@ -421,7 +432,7 @@ void AccountModel::registerAllAccounts() ///Cancel all modifications void AccountModel::cancel() { foreach (Account* a, d_ptr->m_lAccounts) { - if (a->state() == Account::EditState::MODIFIED || a->state() == Account::EditState::OUTDATED) + if (a->editState() == Account::EditState::MODIFIED || a->editState() == Account::EditState::OUTDATED) a->performAction(Account::EditAction::CANCEL); } d_ptr->m_lDeletedAccounts.clear(); @@ -466,46 +477,12 @@ Account* AccountModel::getById(const QByteArray& id, bool usePlaceHolder) const return nullptr; } -///Get the first registerred account (default account) -Account* AccountModelPrivate::firstRegisteredAccount() const -{ - for (int i = 0; i < m_lAccounts.count(); ++i) { - Account* current = m_lAccounts[i]; - if(current && current->registrationStatus() == Account::State::REGISTERED && current->isEnabled()) - return current; - else if (current && (current->registrationStatus() == Account::State::READY) && m_lAccounts.count() == 1) - return current; -// else if (current && !(current->accountRegistrationStatus()() == ACCOUNT_STATE_READY)) { -// qDebug() << "Account " << ((current)?current->accountId():"") << " is not registered (" -// << ((current)?current->accountRegistrationStatus()():"") << ") State:" -// << ((current)?current->accountRegistrationStatus()():""); -// } - } - return nullptr; -} - ///Get the account size int AccountModel::size() const { return d_ptr->m_lAccounts.size(); } -///Return the current account -Account* AccountModel::currentAccount() -{ - Account* priorAccount = m_spPriorAccount; - if(priorAccount && priorAccount->registrationStatus() == Account::State::REGISTERED && priorAccount->isEnabled() ) { - return priorAccount; - } - else { - Account* a = instance()->d_ptr->firstRegisteredAccount(); - if (!a) - a = instance()->getById(Account::ProtocolName::IP2IP); - instance()->setPriorAccount(a); - return a; - } -} //getCurrentAccount - ///Get data from the model QVariant AccountModel::data ( const QModelIndex& idx, int role) const { @@ -615,11 +592,12 @@ void AccountModel::remove(Account* account) const int aindex = d_ptr->m_lAccounts.indexOf(account); d_ptr->m_lAccounts.remove(aindex); d_ptr->m_lDeletedAccounts << account->id(); - if (currentAccount() == account) - setPriorAccount(getById(Account::ProtocolName::IP2IP)); + + emit accountRemoved(account); emit dataChanged(index(aindex,0), index(d_ptr->m_lAccounts.size()-1,0)); emit layoutChanged(); //delete account; + d_ptr->m_pRemovedAccounts << account; } void AccountModel::remove(const QModelIndex& idx ) @@ -627,14 +605,6 @@ void AccountModel::remove(const QModelIndex& idx ) remove(getAccountByModelIndex(idx)); } -///Set the previous account used -void AccountModel::setPriorAccount(const Account* account) { - const bool changed = (account && m_spPriorAccount != account) || (!account && m_spPriorAccount); - m_spPriorAccount = const_cast<Account*>(account); - if (changed) - emit priorAccountChanged(currentAccount()); -} - ///Set model data bool AccountModel::setData(const QModelIndex& idx, const QVariant& value, int role) { diff --git a/src/accountmodel.h b/src/accountmodel.h index adb1a82cb78cca9a01a087429d36fc9d6a838994..8f95e2b74bb379c0865c2a3667e94d4999fa31ff 100644 --- a/src/accountmodel.h +++ b/src/accountmodel.h @@ -37,28 +37,28 @@ class LIB_EXPORT AccountModel : public QAbstractListModel { #pragma GCC diagnostic pop public: - Q_PROPERTY(Account* ip2ip READ ip2ip) - Q_PROPERTY(bool presenceEnabled READ isPresenceEnabled ) - Q_PROPERTY(bool presencePublishSupported READ isPresencePublishSupported ) - Q_PROPERTY(bool presenceSubscribeSupported READ isPresenceSubscribeSupported ) + Q_PROPERTY(Account* ip2ip READ ip2ip ) + Q_PROPERTY(bool presenceEnabled READ isPresenceEnabled ) + Q_PROPERTY(bool presencePublishSupported READ isPresencePublishSupported ) + Q_PROPERTY(bool presenceSubscribeSupported READ isPresenceSubscribeSupported ) friend class Account; friend class AccountPrivate; + friend class AvailableAccountModel; + friend class AvailableAccountModelPrivate; //Static getter and destructor static AccountModel* instance(); - static void destroy(); //Getters - Q_INVOKABLE Account* getById ( const QByteArray& id, bool ph = false) const; - int size ( ) const; - static Account* currentAccount ( ) ; - Account* getAccountByModelIndex ( const QModelIndex& item ) const; - static QString getSimilarAliasIndex ( const QString& alias ) ; - Account* ip2ip ( ) const; - bool isPresenceEnabled ( ) const; - bool isPresencePublishSupported ( ) const; - bool isPresenceSubscribeSupported( ) const; + Q_INVOKABLE Account* getById ( const QByteArray& id, bool ph = false) const; + int size ( ) const; + Account* getAccountByModelIndex ( const QModelIndex& item ) const; + static QString getSimilarAliasIndex ( const QString& alias ) ; + Account* ip2ip ( ) const; + bool isPresenceEnabled ( ) const; + bool isPresencePublishSupported ( ) const; + bool isPresenceSubscribeSupported( ) const; //Abstract model accessors virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const override; @@ -67,17 +67,14 @@ public: virtual bool setData ( const QModelIndex& index, const QVariant &value, int role) override; virtual QHash<int,QByteArray> roleNames( ) const override; - //Setters - void setPriorAccount ( const Account* ); - //Mutators - Q_INVOKABLE Account* add ( const QString& alias ); - Q_INVOKABLE void remove ( Account* account ); - void remove ( const QModelIndex& index ); - void save ( ); - Q_INVOKABLE bool moveUp ( const QModelIndex& idx ); - Q_INVOKABLE bool moveDown ( const QModelIndex& idx ); - Q_INVOKABLE void cancel ( ); + Q_INVOKABLE Account* add ( const QString& alias ); + Q_INVOKABLE void remove ( Account* account ); + void remove ( const QModelIndex& index ); + void save ( ); + Q_INVOKABLE bool moveUp ( const QModelIndex& idx ); + Q_INVOKABLE bool moveDown ( const QModelIndex& idx ); + Q_INVOKABLE void cancel ( ); //Operators Account* operator[] (int i) ; @@ -86,16 +83,12 @@ public: private: //Constructors & Destructors - explicit AccountModel(); - ~AccountModel(); + explicit AccountModel (); + virtual ~AccountModel(); //Helpers void add(Account* acc); - //Attributes - static AccountModel* m_spAccountList ; - static Account* m_spPriorAccount ; - AccountModelPrivate* d_ptr; Q_DECLARE_PRIVATE(AccountModel) @@ -108,14 +101,10 @@ public Q_SLOTS: Q_SIGNALS: ///The account list changed void accountListUpdated( ); - ///Emitted when an account state change - void accountStateChanged ( Account* account, const QString& state); ///Emitted when an account enable attribute change void accountEnabledChanged( Account* source ); ///Emitted when the default account change void defaultAccountChanged( Account* a ); - ///Emitted when the default account change - void priorAccountChanged ( Account* a ); ///Emitted when one account registration state change void registrationChanged(Account* a, bool registration ); ///Emitted when the network is down @@ -124,17 +113,11 @@ Q_SIGNALS: void voiceMailNotify(Account* account, int count ); ///Propagate Account::presenceEnabledChanged void presenceEnabledChanged(bool isPresent ); + ///An account has been removed + void accountRemoved(Account* account ); + ///Emitted when an account state change + void accountStateChanged ( Account* account, const Account::RegistrationState state); }; Q_DECLARE_METATYPE(AccountModel*) -//TODO Qt5 use QAbstractItemProxyModel -class LIB_EXPORT AccountListNoCheckProxyModel : public QAbstractListModel -{ -public: - virtual QVariant data (const QModelIndex& index,int role = Qt::DisplayRole ) const override; - virtual bool setData (const QModelIndex& index, const QVariant &value, int role ) override; - virtual Qt::ItemFlags flags (const QModelIndex& index ) const override; - virtual int rowCount(const QModelIndex& parent = QModelIndex() ) const override; -}; - #endif diff --git a/src/availableaccountmodel.cpp b/src/availableaccountmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f528d3e4e24ea0750a6ca922764d0d55adb8fb70 --- /dev/null +++ b/src/availableaccountmodel.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** + * 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 "availableaccountmodel.h" + +//Qt +#include <QtCore/QItemSelectionModel> +#include <QtCore/QCoreApplication> + +//Ring +#include "private/accountmodel_p.h" + +class AvailableAccountModelPrivate : public QObject +{ + Q_OBJECT +public: + AvailableAccountModelPrivate(AvailableAccountModel* parent); + + QItemSelectionModel* m_pSelectionModel; + static Account* m_spPriorAccount ; + static AvailableAccountModel* m_spInstance ; + + static void setPriorAccount ( const Account* account ); + static Account* firstRegisteredAccount( ); + + AvailableAccountModel* q_ptr; + +public Q_SLOTS: + void checkRemovedAccount(Account* a); + void checkStateChanges(Account* account, const Account::RegistrationState state); + void selectionChanged(const QModelIndex& idx, const QModelIndex& previous); +}; + +Account* AvailableAccountModelPrivate::m_spPriorAccount = nullptr; +AvailableAccountModel* AvailableAccountModelPrivate::m_spInstance = nullptr; + +AvailableAccountModelPrivate::AvailableAccountModelPrivate(AvailableAccountModel* parent) :m_pSelectionModel(nullptr),q_ptr(parent) +{ + connect(AccountModel::instance(), &AccountModel::accountRemoved , this, &AvailableAccountModelPrivate::checkRemovedAccount ); + connect(AccountModel::instance(), &AccountModel::accountStateChanged, this, &AvailableAccountModelPrivate::checkStateChanges ); +} + +AvailableAccountModel::AvailableAccountModel(QObject* parent) : QSortFilterProxyModel(parent), +d_ptr(new AvailableAccountModelPrivate(this)) +{ + setSourceModel(AccountModel::instance()); +} + +AvailableAccountModel* AvailableAccountModel::instance() +{ + if (!AvailableAccountModelPrivate::m_spInstance) + AvailableAccountModelPrivate::m_spInstance = new AvailableAccountModel(QCoreApplication::instance()); + + return AvailableAccountModelPrivate::m_spInstance; +} + +//Do not show the checkbox +QVariant AvailableAccountModel::data(const QModelIndex& idx,int role ) const +{ + return (role == Qt::CheckStateRole) ? QVariant() : mapToSource(idx).data(role); +} + +///Disable the unavailable accounts +Qt::ItemFlags AvailableAccountModel::flags (const QModelIndex& idx) const +{ + const QModelIndex& src = mapToSource(idx); + if (qvariant_cast<Account::RegistrationState>(src.data(static_cast<int>(Account::Role::RegistrationState))) != Account::RegistrationState::READY) + return Qt::NoItemFlags; + return sourceModel()->flags(idx); +} + +//Do not display disabled account +bool AvailableAccountModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const +{ + return sourceModel()->index(source_row,0,source_parent).data(Qt::CheckStateRole) == Qt::Checked; +} + + +///Return the current account +Account* AvailableAccountModel::currentDefaultAccount() +{ + Account* priorAccount = AvailableAccountModelPrivate::m_spPriorAccount; + if(priorAccount && priorAccount->registrationState() == Account::RegistrationState::READY && priorAccount->isEnabled() ) { + return priorAccount; + } + else { + Account* a = AvailableAccountModelPrivate::firstRegisteredAccount(); + if (!a) + a = AccountModel::instance()->getById(Account::ProtocolName::IP2IP); + + AvailableAccountModelPrivate::setPriorAccount(a); + return a; + } +} //getCurrentAccount + +///Set the previous account used +void AvailableAccountModelPrivate::setPriorAccount(const Account* account) { + const bool changed = (account && m_spPriorAccount != account) || (!account && m_spPriorAccount); + m_spPriorAccount = const_cast<Account*>(account); + if (changed) { + AvailableAccountModel* self = AvailableAccountModel::instance(); + Account* a = self->currentDefaultAccount(); + + emit self->currentDefaultAccountChanged(a); + + if (self->d_ptr->m_pSelectionModel) { + self->d_ptr->m_pSelectionModel->setCurrentIndex(self->mapFromSource(a->index()), QItemSelectionModel::ClearAndSelect); + } + } +} + +///Get the first registerred account (default account) +Account* AvailableAccountModelPrivate::firstRegisteredAccount() +{ + for (Account* current : AccountModel::instance()->d_ptr->m_lAccounts) { + if(current && current->registrationState() == Account::RegistrationState::READY && current->isEnabled()) + return current; + } + return nullptr; +} + +QItemSelectionModel* AvailableAccountModel::selectionModel() const +{ + if (!d_ptr->m_pSelectionModel) { + d_ptr->m_pSelectionModel = new QItemSelectionModel(const_cast<AvailableAccountModel*>(this)); + connect(d_ptr->m_pSelectionModel, &QItemSelectionModel::currentChanged,d_ptr,&AvailableAccountModelPrivate::selectionChanged); + Account* a = d_ptr->firstRegisteredAccount(); + if (a) + d_ptr->m_pSelectionModel->setCurrentIndex(mapFromSource(a->index()), QItemSelectionModel::ClearAndSelect); + } + return d_ptr->m_pSelectionModel; +} + +void AvailableAccountModelPrivate::selectionChanged(const QModelIndex& idx, const QModelIndex& previous) +{ + Q_UNUSED(previous) + Account* a = qvariant_cast<Account*>(idx.data(static_cast<int>(Account::Role::Object))); + + setPriorAccount(a); +} + +void AvailableAccountModelPrivate::checkRemovedAccount(Account* a) +{ + if (a == m_spPriorAccount) { + Account* a = firstRegisteredAccount(); + qDebug() << "The current default account has been removed, now defaulting to" << a; + setPriorAccount(a); + } +} + +void AvailableAccountModelPrivate::checkStateChanges(Account* account, const Account::RegistrationState state) +{ + Q_UNUSED(account) + Q_UNUSED(state) + Account* a = firstRegisteredAccount(); + if ( m_spPriorAccount != a ) { + qDebug() << "The current default account changed to" << a; + setPriorAccount(a); + } +} + +#include <availableaccountmodel.moc> diff --git a/src/availableaccountmodel.h b/src/availableaccountmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..2f4b20fc4e25a323b68e53d4e7c4fa0ec5981443 --- /dev/null +++ b/src/availableaccountmodel.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * 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/>. * + ***************************************************************************/ +#ifndef AVAILABLE_ACCOUNT_MODEL_H +#define AVAILABLE_ACCOUNT_MODEL_H + +#include <QtCore/QSortFilterProxyModel> + +#include <accountmodel.h> + +class AvailableAccountModelPrivate; + +//TODO Qt5 use QAbstractItemProxyModel +/** + * This model filter the account list to disable all unavailable accounts. It + * also remove the enabled checkbox as it is no longer relevant. + * + * This model also handle all the logic behind the "current account" used by + * default when passing a call. It use the first "READY" account in the + * AccountModel list unless manually specified. To change the default + * permanently, use the setDefaultAccount method. + * + * @todo Once the history is not saved by the daemon, implement setDefaultAccount + */ +class LIB_EXPORT AvailableAccountModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + AvailableAccountModel(QObject* parent = nullptr); + + virtual QVariant data (const QModelIndex& index,int role = Qt::DisplayRole ) const override; + virtual Qt::ItemFlags flags (const QModelIndex& index ) const override; + virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent ) const override; + + QItemSelectionModel* selectionModel() const; + + //Getter + static Account* currentDefaultAccount(); + + //Singleton + static AvailableAccountModel* instance(); + +Q_SIGNALS: + void currentDefaultAccountChanged(Account*); + +private: + AvailableAccountModelPrivate* d_ptr; + Q_DECLARE_PRIVATE(AvailableAccountModel) +}; +Q_DECLARE_METATYPE(AvailableAccountModel*) + +#endif \ No newline at end of file diff --git a/src/call.cpp b/src/call.cpp index c927f8f4be6d3737db11959756bef8c3503cc69d..b35e523c7ee3ad1d9a79953b9d53f72a4590a6a1 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -36,6 +36,7 @@ #include "uri.h" #include "account.h" #include "accountmodel.h" +#include "availableaccountmodel.h" #include "video/manager.h" #include "historymodel.h" #include "instantmessagingmodel.h" @@ -1228,7 +1229,7 @@ void CallPrivate::call() qDebug() << "account = " << m_Account; if(!m_Account) { qDebug() << "Account is not set, taking the first registered."; - this->m_Account = AccountModel::currentAccount(); + m_Account = AvailableAccountModel::currentDefaultAccount(); } //Calls to empty URI should not be allowed, dring will go crazy if ((!m_pDialNumber) || m_pDialNumber->uri().isEmpty()) { diff --git a/src/callmodel.cpp b/src/callmodel.cpp index 1b6a08a0aecc03250ad13f747742d0737ef218c9..7e93cd3d0bc8ca84130385617dd5a1802c301d2e 100644 --- a/src/callmodel.cpp +++ b/src/callmodel.cpp @@ -29,6 +29,7 @@ #include "phonedirectorymodel.h" #include "contactmethod.h" #include "accountmodel.h" +#include "availableaccountmodel.h" #include "dbus/metatypes.h" #include "dbus/callmanager.h" #include "dbus/configurationmanager.h" @@ -123,7 +124,7 @@ CallModel* CallModel::instance() { CallModelPrivate::CallModelPrivate(CallModel* parent) : QObject(parent),q_ptr(parent) { - + } ///Retrieve current and older calls from the daemon, fill history, model and enable drag n' drop @@ -397,7 +398,7 @@ Call* CallModel::dialingCall(const QString& peerName, Account* account) } //No dialing call found, creating one - Account* acc = (account)?account:AccountModel::currentAccount(); + Account* acc = (account)?account:AvailableAccountModel::currentDefaultAccount(); if (!acc) { qWarning() << "No account is available, cannot call" << QStringList(DBus::ConfigurationManager::instance().getAccountList()); @@ -1049,7 +1050,7 @@ void CallModelPrivate::slotChangingConference(const QString &confID, const QStri q_ptr->beginInsertRows(QModelIndex(),m_lInternalModel.size(),m_lInternalModel.size()); m_lInternalModel << child; q_ptr->endInsertRows(); - const QModelIndex idx = q_ptr->getIndex(child->call_real); +// const QModelIndex idx = q_ptr->getIndex(child->call_real); } } confInt->m_lChildren.clear(); diff --git a/src/ciphermodel.cpp b/src/ciphermodel.cpp index dff4060984a74bf12e79caa807e2548d3058b448..5f531414adc80611149bab847ccce078dba63f51 100644 --- a/src/ciphermodel.cpp +++ b/src/ciphermodel.cpp @@ -86,8 +86,8 @@ CipherModel::~CipherModel() QHash<int,QByteArray> CipherModel::roleNames() const { static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); - static bool initRoles = false; - /*if (!initRoles) { + /*static bool initRoles = false; + if (!initRoles) { initRoles = true; }*/ diff --git a/src/collectioneditor.hpp b/src/collectioneditor.hpp index f440d2690f9bd58815c0704f759800889a64a49a..2c6eb17e47e12bb3bfd5ec80b8d61f348336b2c1 100644 --- a/src/collectioneditor.hpp +++ b/src/collectioneditor.hpp @@ -16,8 +16,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * ***************************************************************************/ +//Qt #include <QtCore/QMetaObject> +//Ring +#include <collectionmediator.h> template<typename T> CollectionEditor<T>::CollectionEditor(CollectionMediator<T>* m) : CollectionEditorBase(m->model()), m_pMediator(m) diff --git a/src/numbercompletionmodel.cpp b/src/numbercompletionmodel.cpp index affe6d9d3d185ac3b80f5031917b69452529ba0c..37f348b63beced7a1507444641909510c9502c33 100644 --- a/src/numbercompletionmodel.cpp +++ b/src/numbercompletionmodel.cpp @@ -30,6 +30,7 @@ #include "uri.h" #include "numbercategory.h" #include "accountmodel.h" +#include "availableaccountmodel.h" #include "numbercategorymodel.h" #include "delegates/pixmapmanipulationdelegate.h" @@ -106,7 +107,7 @@ QVariant NumberCompletionModel::data(const QModelIndex& index, int role ) const const ContactMethod* n = i.value(); const int weight = i.key (); - bool needAcc = (role>=100 || role == Qt::UserRole) && n->account() && n->account() != AccountModel::instance()->currentAccount() + bool needAcc = (role>=100 || role == Qt::UserRole) && n->account() && n->account() != AvailableAccountModel::currentDefaultAccount() && n->account()->alias() != Account::ProtocolName::IP2IP; switch (static_cast<NumberCompletionModelPrivate::Columns>(index.column())) { @@ -144,7 +145,7 @@ QVariant NumberCompletionModel::data(const QModelIndex& index, int role ) const case NumberCompletionModelPrivate::Columns::ACCOUNT: switch (role) { case Qt::DisplayRole: - return n->account()?n->account()->id():AccountModel::instance()->currentAccount()->id(); + return n->account()?n->account()->id():AvailableAccountModel::currentDefaultAccount()->id(); }; break; case NumberCompletionModelPrivate::Columns::WEIGHT: @@ -244,7 +245,8 @@ void NumberCompletionModelPrivate::updateModel() locateNumberRange( m_Prefix, numbers ); foreach(ContactMethod* n,numbers) { - if (m_UseUnregisteredAccount || ((n->account() && n->account()->isRegistered()) || !n->account())) + if (m_UseUnregisteredAccount || ((n->account() && n->account()->registrationState() == Account::RegistrationState::READY) + || !n->account())) m_hNumbers.insert(getWeight(n),n); } } diff --git a/src/private/account_p.h b/src/private/account_p.h index 947a6c6d0f939c8fe241b238647b23043d1cc1f0..a20bc1bad25eff825380acd6a49d04bb1f6d76b2 100644 --- a/src/private/account_p.h +++ b/src/private/account_p.h @@ -56,13 +56,15 @@ public: AccountPrivate(Account* acc); //Attributes - QByteArray m_AccountId ; - QHash<QString,QString> m_hAccountDetails ; - ContactMethod* m_pAccountNumber ; - Account* q_ptr ; - bool m_isLoaded ; - int m_LastTransportCode ; - QString m_LastTransportMessage; + QByteArray m_AccountId ; + QHash<QString,QString> m_hAccountDetails ; + ContactMethod* m_pAccountNumber ; + Account* q_ptr ; + bool m_isLoaded ; + int m_LastTransportCode ; + QString m_LastTransportMessage ; + Account::RegistrationState m_RegistrationState ; + QString m_LastSipRegistrationStatus; //Setters void setAccountProperties(const QHash<QString,QString>& m ); diff --git a/src/private/accountmodel_p.h b/src/private/accountmodel_p.h index 02f880bb9454433ac3496869de66cbf0cc02ef68..8d156803d7de75b7c0881517d51244728462bdbb 100644 --- a/src/private/accountmodel_p.h +++ b/src/private/accountmodel_p.h @@ -30,20 +30,23 @@ class AccountModelPrivate : public QObject Q_DECLARE_PUBLIC(AccountModel) friend class AccountPrivate; + friend class AvailableAccountModel; public: //Constructor AccountModelPrivate(AccountModel* parent); void init(); //Helpers - Account* firstRegisteredAccount() const; + static Account::RegistrationState fromDaemonName(const QString& st); //Attributes AccountModel* q_ptr ; QVector<Account*> m_lAccounts ; - AccountListColorDelegate* m_pColorDelegate ; + AccountListColorDelegate* m_pColorDelegate ; QStringList m_lDeletedAccounts; Account* m_pIP2IP ; + QList<Account*> m_pRemovedAccounts; + static AccountModel* m_spAccountList ; //Future account cache static QHash<QByteArray,AccountPlaceHolder*> m_hsPlaceHolder;