diff --git a/src/account.cpp b/src/account.cpp index de939910b581a0c479e9618b03106e854eaa5ad8..03bf1b4bafcb5bced2ffbbb9e7259c9066f7d3cf 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -302,6 +302,8 @@ QString Account::stateColorName() const return "orange"; case RegistrationState::ERROR: return "red"; + case RegistrationState::COUNT__: + break; }; return QString(); } @@ -440,8 +442,15 @@ QString Account::password() const case Account::Protocol::SIP: if (credentialsModel()->rowCount()) return credentialsModel()->data(credentialsModel()->index(0,0),CredentialModel::Role::PASSWORD).toString(); + break; case Account::Protocol::IAX: return d_ptr->accountDetail(DRing::Account::ConfProperties::PASSWORD); + break; + case Account::Protocol::DHT: + return tlsPassword(); + break; + case Account::Protocol::COUNT__: + break; }; return ""; } @@ -906,6 +915,7 @@ void Account::setId(const QByteArray& id) ///Set the account type, SIP or IAX void Account::setProtocol(Account::Protocol proto) { + //TODO prevent this if the protocol has been saved switch (proto) { case Account::Protocol::SIP: d_ptr->setAccountProperty(DRing::Account::ConfProperties::TYPE ,Account::ProtocolName::SIP); @@ -913,6 +923,11 @@ void Account::setProtocol(Account::Protocol proto) case Account::Protocol::IAX: d_ptr->setAccountProperty(DRing::Account::ConfProperties::TYPE ,Account::ProtocolName::IAX); break; + case Account::Protocol::DHT: + d_ptr->setAccountProperty(DRing::Account::ConfProperties::TYPE ,Account::ProtocolName::DHT); + break; + case Account::Protocol::COUNT__: + break; }; } @@ -958,6 +973,10 @@ void Account::setPassword(const QString& detail) case Account::Protocol::IAX: d_ptr->setAccountProperty(DRing::Account::ConfProperties::PASSWORD, detail); break; + case Account::Protocol::DHT: + setTlsPassword(detail); + case Account::Protocol::COUNT__: + break; }; } diff --git a/src/account.h b/src/account.h index 64d2eb9550bbe7584b43e64c06e8aff4a8d13a1d..f7e97dcdbb3c14da3b03234a509e4a152629bd64 100644 --- a/src/account.h +++ b/src/account.h @@ -157,6 +157,7 @@ class LIB_EXPORT Account : public QObject { UNREGISTERED = 1, TRYING = 2, ERROR = 3, + COUNT__, }; Q_ENUMS(RegistrationState) @@ -211,11 +212,14 @@ class LIB_EXPORT Account : public QObject { constexpr static const char* SIP = "SIP" ; constexpr static const char* IAX = "IAX" ; constexpr static const char* IP2IP = "IP2IP"; + constexpr static const char* DHT = "DHT" ; }; enum class Protocol { SIP = 0, IAX = 1, + DHT = 2, + COUNT__, }; Q_ENUMS(Protocol) diff --git a/src/call.cpp b/src/call.cpp index b35e523c7ee3ad1d9a79953b9d53f72a4590a6a1..97c19a48750e74afa60b9a4f22357bca57e84ace 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -222,7 +222,7 @@ m_pImModel(nullptr),m_pTimer(nullptr),m_Recording(false),m_Account(nullptr), m_PeerName(),m_pPeerContactMethod(nullptr),m_HistoryConst(HistoryTimeCategoryModel::HistoryConst::Never), m_CallId(),m_pStartTimeStamp(0),m_pDialNumber(nullptr),m_pTransferNumber(nullptr), m_History(false),m_Missed(false),m_Direction(Call::Direction::OUTGOING),m_Type(Call::Type::CALL), -m_pUserActionModel(new UserActionModel(parent)) +m_pUserActionModel(nullptr) { } @@ -1580,6 +1580,8 @@ void CallPrivate::updatePlayback(const QString& path, int position,int size) UserActionModel* Call::userActionModel() const { + if (!d_ptr->m_pUserActionModel) + d_ptr->m_pUserActionModel = new UserActionModel(const_cast<Call*>(this)); return d_ptr->m_pUserActionModel; } diff --git a/src/callmodel.cpp b/src/callmodel.cpp index 7e93cd3d0bc8ca84130385617dd5a1802c301d2e..3acae76a4e0fa885e6d079d5a193f7c8237ace39 100644 --- a/src/callmodel.cpp +++ b/src/callmodel.cpp @@ -20,8 +20,8 @@ //Qt #include <QtCore/QDebug> #include <QtCore/QCoreApplication> -#include <QtGui/QDragEnterEvent> #include <QtCore/QMimeData> +#include <QtCore/QItemSelectionModel> //Ring library #include "call.h" @@ -41,6 +41,7 @@ #include "historymodel.h" #include "delegates/phonenumberselectordelegate.h" #include "personmodel.h" +#include "useractionmodel.h" //Other #include <unistd.h> @@ -79,6 +80,8 @@ public: QList<InternalStruct*> m_lInternalModel; QHash< Call* , InternalStruct* > m_sPrivateCallList_call ; QHash< QString , InternalStruct* > m_sPrivateCallList_callId ; + QItemSelectionModel* m_pSelectionModel; + UserActionModel* m_pUserActionModel; //Helpers @@ -122,7 +125,8 @@ CallModel* CallModel::instance() { return m_spInstance; } -CallModelPrivate::CallModelPrivate(CallModel* parent) : QObject(parent),q_ptr(parent) +CallModelPrivate::CallModelPrivate(CallModel* parent) : QObject(parent),q_ptr(parent),m_pSelectionModel(nullptr), +m_pUserActionModel(nullptr) { } @@ -244,6 +248,14 @@ QHash<int,QByteArray> CallModel::roleNames() const } +QItemSelectionModel* CallModel::selectionModel() const +{ + if (!d_ptr->m_pSelectionModel) { + d_ptr->m_pSelectionModel = new QItemSelectionModel(const_cast<CallModel*>(this)); + } + return d_ptr->m_pSelectionModel; +} + /***************************************************************************** * * * Access related functions * @@ -305,7 +317,7 @@ bool CallModel::isConnected() const { #ifdef ENABLE_LIBWRAP return DBus::InstanceManager::instance().isConnected(); -#else +#else return DBus::InstanceManager::instance().connection().isConnected(); #endif //ENABLE_LIBWRAP } @@ -316,6 +328,14 @@ bool CallModel::isValid() } +UserActionModel* CallModel::userActionModel() const +{ + if (!d_ptr->m_pUserActionModel) + d_ptr->m_pUserActionModel = new UserActionModel(const_cast<CallModel*>(this)); + return d_ptr->m_pUserActionModel; +} + + /***************************************************************************** * * * Call related code * diff --git a/src/callmodel.h b/src/callmodel.h index 5fe3fc660497aca9615e73c272943e500eb38c23..9c48a7b578cde60c19e38a454d82d048d6d25d3b 100644 --- a/src/callmodel.h +++ b/src/callmodel.h @@ -23,6 +23,9 @@ #include <QMap> #include "typedefs.h" +//Qt +class QItemSelectionModel; + //Ring #include "call.h" class Account; @@ -37,92 +40,98 @@ typedef QList<Call*> CallList; ///CallModel: Central model/frontend to deal with dring class LIB_EXPORT CallModel : public QAbstractItemModel { - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" - Q_OBJECT - #pragma GCC diagnostic pop - public: - ///Accepted (mime) payload types - enum DropPayloadType { - NONE = 0 , /*!< No drop payload is supported */ - CALL = 1<<0, /*!< The item support the dropping of a current or history call */ - HISTORY = 1<<1, /*!< The item support the dropping of a history call */ - CONTACT = 1<<2, /*!< The item support the dropping of a contact id */ - NUMBER = 1<<3, /*!< The item support the dropping of an URI */ - TEXT = 1<<4, /*!< The item support the dropping of random text (to be interpreted as URI) */ - ACCOUNT = 1<<5, /*!< The item support the dropping of an account id */ - }; - - //Properties - Q_PROPERTY(int size READ size ) - Q_PROPERTY(bool hasConference READ hasConference) - Q_PROPERTY(int callCount READ rowCount ) - - //Constructors, initializer and destructors - virtual ~CallModel( ); - - //Call related - Q_INVOKABLE Call* dialingCall ( const QString& peerName=QString(), Account* account=nullptr ); - Q_INVOKABLE void attendedTransfer ( Call* toTransfer , Call* target ); - Q_INVOKABLE void transfer ( Call* toTransfer , const ContactMethod* target ); - QModelIndex getIndex ( Call* call ); - - //Conference related - Q_INVOKABLE bool createConferenceFromCall ( Call* call1, Call* call2 ); - Q_INVOKABLE bool mergeConferences ( Call* conf1, Call* conf2 ); - Q_INVOKABLE bool addParticipant ( Call* call2, Call* conference ); - Q_INVOKABLE bool detachParticipant ( Call* call ); - - //Getters - Q_INVOKABLE bool isValid (); - Q_INVOKABLE int size (); - Q_INVOKABLE CallList getActiveCalls (); - Q_INVOKABLE CallList getActiveConferences(); - Q_INVOKABLE int acceptedPayloadTypes(); - Q_INVOKABLE bool hasConference () const; - Q_INVOKABLE bool isConnected () const; - - Q_INVOKABLE Call* getCall ( const QString& callId ) const; - Q_INVOKABLE Call* getCall ( const QModelIndex& idx ) const; - - //Model implementation - virtual bool setData ( const QModelIndex& index, const QVariant &value, int role ) override; - virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const override; - virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const override; - virtual Qt::ItemFlags flags ( const QModelIndex& index ) const override; - virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const override; - virtual QModelIndex parent ( const QModelIndex& index ) const override; - virtual QModelIndex index ( int row, int column, const QModelIndex& parent=QModelIndex()) const override; - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; - virtual QStringList mimeTypes ( ) const override; - virtual QMimeData* mimeData ( const QModelIndexList &indexes ) const override; - virtual bool dropMimeData ( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) override; - virtual QHash<int,QByteArray> roleNames() const override; - - //Singleton - static CallModel* instance(); - - private: - explicit CallModel(); - QScopedPointer<CallModelPrivate> d_ptr; - Q_DECLARE_PRIVATE(CallModel) - - //Singleton - static CallModel* m_spInstance; - - Q_SIGNALS: - ///Emitted when a call state change - void callStateChanged ( Call* call, Call::State previousState ); - ///Emitted when a new call is incoming - void incomingCall ( Call* call ); - ///Emitted when a conference is created - void conferenceCreated ( Call* conf ); - ///Emitted when a conference change state or participant - void conferenceChanged ( Call* conf ); - ///Emitted when a conference is removed - void conferenceRemoved ( Call* conf ); - ///Emitted when a call is added - void callAdded ( Call* call , Call* parent ); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +Q_OBJECT +#pragma GCC diagnostic pop +public: + ///Accepted (mime) payload types + enum DropPayloadType { + NONE = 0 , /*!< No drop payload is supported */ + CALL = 1<<0, /*!< The item support the dropping of a current or history call */ + HISTORY = 1<<1, /*!< The item support the dropping of a history call */ + CONTACT = 1<<2, /*!< The item support the dropping of a contact id */ + NUMBER = 1<<3, /*!< The item support the dropping of an URI */ + TEXT = 1<<4, /*!< The item support the dropping of random text (to be interpreted as URI) */ + ACCOUNT = 1<<5, /*!< The item support the dropping of an account id */ + }; + + //Properties + Q_PROPERTY(int size READ size ) + Q_PROPERTY(bool hasConference READ hasConference ) + Q_PROPERTY(int callCount READ rowCount ) + Q_PROPERTY(bool isValid READ isValid ) + Q_PROPERTY(bool hasConference READ hasConference ) + Q_PROPERTY(bool isConnected READ isConnected ) + Q_PROPERTY(UserActionModel* userActionModel READ userActionModel ) + + //Call related + Q_INVOKABLE Call* dialingCall ( const QString& peerName=QString(), Account* account=nullptr ); + Q_INVOKABLE void attendedTransfer ( Call* toTransfer , Call* target ); + Q_INVOKABLE void transfer ( Call* toTransfer , const ContactMethod* target ); + QModelIndex getIndex ( Call* call ); + + //Conference related + Q_INVOKABLE bool createConferenceFromCall ( Call* call1, Call* call2 ); + Q_INVOKABLE bool mergeConferences ( Call* conf1, Call* conf2 ); + Q_INVOKABLE bool addParticipant ( Call* call2, Call* conference ); + Q_INVOKABLE bool detachParticipant ( Call* call ); + + //Getters + Q_INVOKABLE CallList getActiveCalls (); + Q_INVOKABLE CallList getActiveConferences(); + Q_INVOKABLE int acceptedPayloadTypes(); + bool isValid (); + int size (); + bool hasConference () const; + bool isConnected () const; + UserActionModel* userActionModel () const; + + Q_INVOKABLE Call* getCall ( const QString& callId ) const; + Q_INVOKABLE Call* getCall ( const QModelIndex& idx ) const; + + //Model implementation + virtual bool setData ( const QModelIndex& index, const QVariant &value, int role ) override; + virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const override; + virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const override; + virtual Qt::ItemFlags flags ( const QModelIndex& index ) const override; + virtual int columnCount ( const QModelIndex& parent = QModelIndex() ) const override; + virtual QModelIndex parent ( const QModelIndex& index ) const override; + virtual QModelIndex index ( int row, int column, const QModelIndex& parent=QModelIndex()) const override; + virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; + virtual QStringList mimeTypes ( ) const override; + virtual QMimeData* mimeData ( const QModelIndexList &indexes ) const override; + virtual bool dropMimeData ( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) override; + virtual QHash<int,QByteArray> roleNames() const override; + + QItemSelectionModel* selectionModel() const; + + //Singleton + static CallModel* instance(); + virtual ~CallModel( ); + +private: + //Constructors, initializer and destructors + explicit CallModel(); + QScopedPointer<CallModelPrivate> d_ptr; + Q_DECLARE_PRIVATE(CallModel) + + //Singleton + static CallModel* m_spInstance; + +Q_SIGNALS: + ///Emitted when a call state change + void callStateChanged ( Call* call, Call::State previousState ); + ///Emitted when a new call is incoming + void incomingCall ( Call* call ); + ///Emitted when a conference is created + void conferenceCreated ( Call* conf ); + ///Emitted when a conference change state or participant + void conferenceChanged ( Call* conf ); + ///Emitted when a conference is removed + void conferenceRemoved ( Call* conf ); + ///Emitted when a call is added + void callAdded ( Call* call , Call* parent ); }; Q_DECLARE_METATYPE(CallModel*) diff --git a/src/useractionmodel.cpp b/src/useractionmodel.cpp index 2cbe5f4df6b7eab63712050f868fb5a0c6db1a9d..95bb83b8e11a34924c464fa2a41b9043dfdd36be 100644 --- a/src/useractionmodel.cpp +++ b/src/useractionmodel.cpp @@ -17,168 +17,327 @@ ***************************************************************************/ #include "useractionmodel.h" +//Qt +#include <QtCore/QItemSelection> + +//Ring #include "call.h" +#include "callmodel.h" +#include "account.h" +#include "accountmodel.h" class UserActionModelPrivate : public QObject { Q_OBJECT public: + enum class SelectionState { + NONE , /*!< No Item is selected */ + UNIQUE, /*!< A single item is selected */ + MULTI , /*!< Multiple items are selected */ + COUNT__, + }; + + enum class UserActionModelMode { + CALL , /*!< Model the react on a single call element */ + CALLMODEL, /*!< Model that react on the callmodel selection */ + }; + + //Availability matrices UserActionModelPrivate(UserActionModel* parent); - static const TypedStateMachine< TypedStateMachine< bool , Call::State > , UserActionModel::Action > availableActionMap; + static const TypedStateMachine< TypedStateMachine< bool , Call::State > , UserActionModel::Action > availableActionMap; + static const TypedStateMachine< TypedStateMachine< bool , Account::RegistrationState > , UserActionModel::Action > availableAccountActionMap; + static const TypedStateMachine< TypedStateMachine< bool , SelectionState > , UserActionModel::Action > multi_call_options; + static const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserActionModel::Action > availableProtocolActions; + static const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLevel , SelectionState > , UserActionModel::Action > actionStatefulness; + + //Helpers + void updateActions(); + bool updateAction(UserActionModel::Action action); + bool updateByCall(UserActionModel::Action action, const Call* c); //Attribues - Call* m_pCall; + Call* m_pCall; + UserActionModelMode m_Mode ; + SelectionState m_SelectionState; + TypedStateMachine< bool, UserActionModel::Action> m_CurrentActions; + static const TypedStateMachine< QString, UserActionModel::Action> m_ActionNames; private: UserActionModel* q_ptr; -private Q_SLOTS: +public Q_SLOTS: + //Single call mode void slotStateChanged(); + + //CallModel mode + void slotCurrentChanged (const QModelIndex& current , const QModelIndex& previous ); + void slotSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected); + void slotCallStateChanged ( Call* call, Call::State previousState ); + + //Common + void slotAccountStateChanged ( Account* account, const Account::RegistrationState state ); }; + +/* + * This algorithm implement a matrix multiplication of the 4 sources of relevant states. + * The result of the multiplication indicate is the option is enabled in a given scenario. + */ + //Enabled actions const TypedStateMachine< TypedStateMachine< bool , Call::State > , UserActionModel::Action > UserActionModelPrivate::availableActionMap = {{ - /* INCOMING RINGING CURRENT DIALING HOLD FAILURE BUSY TRANSFERRED TRANSF_HOLD OVER ERROR CONFERENCE CONFERENCE_HOLD:*/ - /*PICKUP */ {{ true , true , false, false, false, false, false, false, false, false, false, false, false }}, - /*HOLD */ {{ false , false, true , false, false, false, false, true , false, false, false, true , false }}, - /*UNHOLD */ {{ false , false, false, false, true , false, false, false, false, false, false, false, false }}, - /*MUTE */ {{ false , true , true , false, false, false, false, false, false, false, false, false, false }}, - /*TRANSFER */ {{ false , false, true , false, true , false, false, false, false, false, false, false, false }}, - /*RECORD */ {{ false , true , true , false, true , false, false, true , true , false, false, true , true }}, - /*REFUSE */ {{ true , false, false, false, false, false, false, false, false, false, false, false, false }}, - /*ACCEPT */ {{ false , false, false, true , false, false, false, false, false, false, false, false, false }}, - /*HANGUP */ {{ false , true , true , true , true , true , true , true , true , false, true , true , true }}, + /* INCOMING RINGING CURRENT DIALING HOLD FAILURE BUSY TRANSFERRED TRANSF_HOLD OVER ERROR CONFERENCE CONFERENCE_HOLD:*/ + /*ACCEPT */ {{ true , true , false, true , false, false, false, false, false, false, false, false, false }}, + /*HOLD */ {{ false , false, true , false, true , false, false, false, false, false, false, true , false }}, + /*MUTE_AUDIO */ {{ false , true , true , false, false, false, false, false, false, false, false, false, false }}, + /*MUTE_VIDEO */ {{ false , true , true , false, false, false, false, false, false, false, false, false, false }}, + /*SERVER_TRANSFER */ {{ false , false, true , false, true , false, false, false, false, false, false, false, false }}, + /*RECORD */ {{ false , true , true , false, true , false, false, true , true , false, false, true , true }}, + /*HANGUP */ {{ true , true , true , true , true , true , true , true , true , false, true , true , true }}, + + /*JOIN */ {{ false , true , true , false, true , false, false, true , true , false, false, true , true }}, +}}; + +/** + * Assuming a call is in progress, the communication can still be valid if the account is down, however, + * this will impact the available actions + */ +const TypedStateMachine< TypedStateMachine< bool , Account::RegistrationState > , UserActionModel::Action > UserActionModelPrivate::availableAccountActionMap = {{ + /* READY UNREGISTERED TRYING ERROR */ + /* ACCEPT */ {{ true , false, false, false }}, + /* HOLD */ {{ true , false, false, false }}, + /* MUTE_AUDIO */ {{ true , true , true , true }}, + /* MUTE_VIDEO */ {{ true , true , true , true }}, + /* SERVER_TRANSFER */ {{ true , false, false, false }}, + /* RECORD */ {{ true , true , true , true }}, + /* HANGUP */ {{ true , true , true , true }}, + + /* JOIN */ {{ true , true , true , true }}, +}}; + +const TypedStateMachine< TypedStateMachine< bool , UserActionModelPrivate::SelectionState > , UserActionModel::Action > UserActionModelPrivate::multi_call_options = {{ + /* NONE UNIQUE MULTI */ + /* ACCEPT */ {{ false, true , true }}, + /* HOLD */ {{ false, true , true }}, + /* MUTE_AUDIO */ {{ false, true , true }}, + /* MUTE_VIDEO */ {{ false, true , true }}, + /* SERVER_TRANSFER */ {{ false, true , true }}, + /* RECORD */ {{ false, true , true }}, + /* HANGUP */ {{ false, true , true }}, + + /* JOIN */ {{ false, false, true }}, +}}; + +const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserActionModel::Action > UserActionModelPrivate::availableProtocolActions = {{ + /* SIP IAX DHT */ + /* ACCEPT */ {{ true , true , true }}, + /* HOLD */ {{ true , true , true }}, + /* MUTE_AUDIO */ {{ true , true , true }}, + /* MUTE_VIDEO */ {{ true , true , true }}, + /* SERVER_TRANSFER */ {{ true , false, false }}, + /* RECORD */ {{ true , true , true }}, + /* HANGUP */ {{ true , true , true }}, + + /* JOIN */ {{ true , true , true }}, +}}; + +#define ST UserActionModel::ActionStatfulnessLevel:: +const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLevel , UserActionModelPrivate::SelectionState > , UserActionModel::Action > UserActionModelPrivate::actionStatefulness = {{ + /* NONE UNIQUE MULTI */ + /* ACCEPT */ {{ ST UNISTATE, ST UNISTATE , ST UNISTATE }}, + /* HOLD */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, + /* MUTE_AUDIO */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, + /* MUTE_VIDEO */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, + /* SERVER_TRANSFER */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, + /* RECORD */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, + /* HANGUP */ {{ ST UNISTATE, ST UNISTATE , ST TRISTATE }}, + /* JOIN */ {{ ST UNISTATE, ST UNISTATE , ST UNISTATE }}, +}}; +#undef ST + +const TypedStateMachine< QString, UserActionModel::Action> UserActionModelPrivate::m_ActionNames = {{ + /* ACCEPT */ QObject::tr("ACCEPT" ), //TODO use better (and stateful) names + /* HOLD */ QObject::tr("HOLD" ), + /* MUTE_AUDIO */ QObject::tr("MUTE_AUDIO" ), + /* MUTE_VIDEO */ QObject::tr("MUTE_VIDEO" ), + /* SERVER_TRANSFER */ QObject::tr("SERVER_TRANSFER" ), + /* RECORD */ QObject::tr("RECORD" ), + /* HANGUP */ QObject::tr("HANGUP" ), + /* JOIN */ QObject::tr("JOIN" ), }}; UserActionModelPrivate::UserActionModelPrivate(UserActionModel* parent) : QObject(parent),q_ptr(parent),m_pCall(nullptr) { } -UserActionModel::UserActionModel(Call* parent) : QObject(parent),d_ptr(new UserActionModelPrivate(this)) +/** + * Create an UserActionModel around a single call. This wont take advantage + * of the multiselection feature. + */ +UserActionModel::UserActionModel(Call* parent) : QAbstractListModel(parent),d_ptr(new UserActionModelPrivate(this)) { Q_ASSERT(parent != nullptr); + Q_ASSERT(parent->state() != Call::State::OVER); + d_ptr->m_SelectionState = UserActionModelPrivate::SelectionState::UNIQUE; + d_ptr->m_Mode = UserActionModelPrivate::UserActionModelMode::CALL; d_ptr->m_pCall = parent; + connect(AccountModel::instance(), &AccountModel::accountStateChanged , d_ptr.data(), &UserActionModelPrivate::slotAccountStateChanged); } -UserActionModel::~UserActionModel() +/** + * Create an UserActionModel around the CallModel selected call(s) + */ +UserActionModel::UserActionModel(CallModel* parent) : QAbstractListModel(parent),d_ptr(new UserActionModelPrivate(this)) { - -} - -// QVariant UserActionModel::data(const QModelIndex& index, int role ) const -// { -// Q_UNUSED( index ) -// Q_UNUSED( role ) -// return QVariant(); -// } -// -// int UserActionModel::rowCount(const QModelIndex& parent ) const -// { -// Q_UNUSED( parent ) -// return 1; -// } -// -// int UserActionModel::columnCount(const QModelIndex& parent ) const -// { -// Q_UNUSED( parent ) -// return 1; -// } -// -// ///For now, this model probably wont be used that way -// Qt::ItemFlags UserActionModel::flags(const QModelIndex& index ) const -// { -// Q_UNUSED(index) -// return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -// } -// -// ///This model is read only -// bool UserActionModel::setData(const QModelIndex& index, const QVariant &value, int role) -// { -// Q_UNUSED( index ) -// Q_UNUSED( value ) -// Q_UNUSED( role ) -// return false; -// } + Q_ASSERT(parent != nullptr); + d_ptr->m_Mode = UserActionModelPrivate::UserActionModelMode::CALLMODEL; + d_ptr->m_SelectionState = UserActionModelPrivate::SelectionState::UNIQUE; + connect(parent->selectionModel(), &QItemSelectionModel::currentRowChanged, d_ptr.data(), &UserActionModelPrivate::slotCurrentChanged ); + connect(parent->selectionModel(), &QItemSelectionModel::selectionChanged , d_ptr.data(), &UserActionModelPrivate::slotSelectionChanged ); + connect(parent, &CallModel::callStateChanged , d_ptr.data(), &UserActionModelPrivate::slotCallStateChanged ); -bool UserActionModel::isActionEnabled( UserActionModel::Action action ) const + connect(AccountModel::instance(), &AccountModel::accountStateChanged , d_ptr.data(), &UserActionModelPrivate::slotAccountStateChanged); +} + +UserActionModel::~UserActionModel() { - return d_ptr->availableActionMap[action][d_ptr->m_pCall->state()]; + } -void UserActionModelPrivate::slotStateChanged() +QHash<int,QByteArray> UserActionModel::roleNames() const { - emit q_ptr->actionStateChanged(); + static QHash<int, QByteArray> roles = QAbstractItemModel::roleNames(); + /*static bool initRoles = false; + if (!initRoles) { + initRoles = true; + + }*/ + return roles; } +QVariant UserActionModel::data(const QModelIndex& idx, int role ) const +{ + if (!idx.isValid() && (idx.row()>=0 && idx.row() < enum_class_size<UserActionModel::Action>())) + return QVariant(); -uint UserActionModel::relativeIndex( UserActionModel::Action action ) const + UserActionModel::Action action = static_cast<UserActionModel::Action>(idx.row()); + + switch(role) { + case Qt::DisplayRole: + return UserActionModelPrivate::m_ActionNames[action]; + case Qt::CheckStateRole: + return Qt::Unchecked; + }; + + return QVariant(); +} + +int UserActionModel::rowCount(const QModelIndex& parent ) const { - int i(0),ret(0); - while (i != static_cast<int>(action) && i < enum_class_size<UserActionModel::Action>()) { - ret += isActionEnabled(static_cast<UserActionModel::Action>(i))?1:0; - i++; - } - return ret; + return parent.isValid() ? 0 : static_cast<int>(Action::COUNT__); } -uint UserActionModel::enabledCount( ) const +///For now, this model probably wont be used that way +Qt::ItemFlags UserActionModel::flags(const QModelIndex& idx ) const { - uint ret =0; - for (int i=0;i< enum_class_size<UserActionModel::Action>(); i++) - ret += isActionEnabled(static_cast<UserActionModel::Action>(i))?1:0; - return ret; + if (!idx.isValid() && (idx.row()>=0 && idx.row() < enum_class_size<UserActionModel::Action>())) + return Qt::NoItemFlags; + + UserActionModel::Action action = static_cast<UserActionModel::Action>(idx.row()); + + return (d_ptr->m_CurrentActions[action] ? (Qt::ItemIsEnabled | Qt::ItemIsSelectable) : Qt::NoItemFlags) + | (d_ptr->actionStatefulness[action][d_ptr->m_SelectionState] == UserActionModel::ActionStatfulnessLevel::CHECKABLE + ? Qt::ItemIsUserCheckable : Qt::NoItemFlags); } +// ///This model is read only +bool UserActionModel::setData(const QModelIndex& index, const QVariant &value, int role) +{ + Q_UNUSED( index ) + Q_UNUSED( value ) + Q_UNUSED( role ) + return false; +} -/**************************************************************** - * * - * ACTIONS * - * * - ***************************************************************/ +bool UserActionModel::isActionEnabled( UserActionModel::Action action ) const +{ + return d_ptr->availableActionMap[action][d_ptr->m_pCall->state()]; +} -bool UserActionModel::isPickupEnabled() const +void UserActionModelPrivate::slotStateChanged() { - return isActionEnabled(UserActionModel::Action::PICKUP); + emit q_ptr->actionStateChanged(); } -bool UserActionModel::isHoldEnabled() const +bool UserActionModelPrivate::updateByCall(UserActionModel::Action action, const Call* c) { - return isActionEnabled(UserActionModel::Action::HOLD); + return (!c) ? false : ( + availableActionMap [action] [c->state() ] && + availableAccountActionMap [action] [c->account()->registrationState()] && + multi_call_options [action] [m_SelectionState ] && + availableProtocolActions [action] [c->account()->protocol() ] // + ); } -bool UserActionModel::isUnholdEnabled() const +bool UserActionModelPrivate::updateAction(UserActionModel::Action action) { - return isActionEnabled(UserActionModel::Action::UNHOLD); + switch(m_Mode) { + case UserActionModelMode::CALL: + return updateByCall(action, m_pCall); + case UserActionModelMode::CALLMODEL: { + bool ret = true; + m_SelectionState = CallModel::instance()->selectionModel()->selectedRows().size() > 1 ? SelectionState::MULTI : SelectionState::UNIQUE; + for (const QModelIndex& idx : CallModel::instance()->selectionModel()->selectedRows()) + ret &= updateByCall(action, qvariant_cast<Call*>(idx.data(Call::Role::Object))); + return ret; + } + }; + return false; } -bool UserActionModel::isHangupEnabled() const +void UserActionModelPrivate::updateActions() { - return isActionEnabled(UserActionModel::Action::HANGUP); + for (UserActionModel::Action action : EnumIterator<UserActionModel::Action>()) + m_CurrentActions[action] = updateAction(action); + emit q_ptr->dataChanged(q_ptr->index(0,0),q_ptr->index(enum_class_size<UserActionModel::Action>()-1,0)); } -bool UserActionModel::isMuteEnabled() const +void UserActionModelPrivate::slotCurrentChanged(const QModelIndex& current, const QModelIndex& previous) { - return isActionEnabled(UserActionModel::Action::MUTE); + Q_UNUSED(current) + Q_UNUSED(previous) + updateActions(); } -bool UserActionModel::isTransferEnabled() const +void UserActionModelPrivate::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { - return isActionEnabled(UserActionModel::Action::TRANSFER); + Q_UNUSED(selected) + Q_UNUSED(deselected) + updateActions(); } -bool UserActionModel::isRecordEnabled() const +void UserActionModelPrivate::slotCallStateChanged(Call* call, Call::State previousState) { - return isActionEnabled(UserActionModel::Action::RECORD); + Q_UNUSED(call) + Q_UNUSED(previousState) + updateActions(); } -bool UserActionModel::isRefuseEnabled() const +void UserActionModelPrivate::slotAccountStateChanged(Account* account, const Account::RegistrationState state) { - return isActionEnabled(UserActionModel::Action::REFUSE); + Q_UNUSED(account) + Q_UNUSED(state) + updateActions(); } -bool UserActionModel::isAcceptEnabled() const +uint UserActionModel::relativeIndex( UserActionModel::Action action ) const { - return isActionEnabled(UserActionModel::Action::ACCEPT); + int i(0),ret(0); + while (i != static_cast<int>(action) && i < enum_class_size<UserActionModel::Action>()) { + ret += isActionEnabled(static_cast<UserActionModel::Action>(i))?1:0; + i++; + } + return ret; } #include <useractionmodel.moc> diff --git a/src/useractionmodel.h b/src/useractionmodel.h index 52828a6eb6e0a95ef4eb7b11fe849b9eb758e576..8f32bd760557c304a948af10a0b5c7a799655c7b 100644 --- a/src/useractionmodel.h +++ b/src/useractionmodel.h @@ -25,14 +25,14 @@ #include "call.h" class Call; +class CallModel; class UserActionModelPrivate; /** * @class UserActionModel Hold available actions for a given call state * - * @todo This is not a model yet, however, it would be nice if it was **/ -class LIB_EXPORT UserActionModel : public QObject/*QAbstractItemModel*/ { +class LIB_EXPORT UserActionModel : public QAbstractListModel { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" Q_OBJECT @@ -45,59 +45,46 @@ public: RELATIVEINDEX = 101, }; + ///If options are checkable or not + enum class ActionStatfulnessLevel { + UNISTATE = 0, /*!< The action has no state beside being available or not */ + CHECKABLE = 1, /*!< The action can be (un)available and "checked" */ + TRISTATE = 2, /*!< The action can be (un)available and unchecked, partially checked and fully checked */ + COUNT__, + }; + ///(End)user action, all possibility, not only state aware ones like "Action" enum class Action { - PICKUP = 0, - HOLD = 1, - UNHOLD = 2, - MUTE = 3, - TRANSFER = 4, - RECORD = 5, - REFUSE = 6, - ACCEPT = 7, - HANGUP = 8, + //Uni selection + ACCEPT , /*!< Pickup incoming call(s) or send */ + HOLD , /*!< [Stateful] Hold (check) or Unhold (uncheck) call(s) */ + MUTE_AUDIO , /*!< [Stateful] Stop sending audio to call(s) */ + MUTE_VIDEO , /*!< [Stateful] Stop sending video to call(s) */ + SERVER_TRANSFER , /*!< [Stateful] Perform an unattended transfer */ + RECORD , /*!< [Stateful] Record the call(s) to .wav file(s) */ + HANGUP , /*!< Resuse an incomming call or hang up an in progress one */ + + //Multi selection + JOIN , /*!< [Stateful] Join all seclect calls into a conference */ COUNT__, }; Q_ENUMS(Action) - //Properties - Q_PROPERTY( bool isPickupEnabled READ isPickupEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isHoldEnabled READ isHoldEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isUnholdEnabled READ isUnholdEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isHangupEnabled READ isHangupEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isMuteEnabled READ isMuteEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isTransferEnabled READ isTransferEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isRecordEnabled READ isRecordEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isRefuseEnabled READ isRefuseEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( bool isAcceptEnabled READ isAcceptEnabled NOTIFY actionStateChanged ) - Q_PROPERTY( uint enabledCount READ enabledCount NOTIFY actionStateChanged ) - //Constructor explicit UserActionModel(Call* parent); + UserActionModel(CallModel* parent); virtual ~UserActionModel(); //Abstract model members -// virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole ) const; -// virtual int rowCount (const QModelIndex& parent = QModelIndex() ) const; -// virtual int columnCount(const QModelIndex& parent = QModelIndex() ) const; -// virtual Qt::ItemFlags flags (const QModelIndex& index ) const; -// virtual bool setData (const QModelIndex& index, const QVariant &value, int role); -// virtual QHash<int,QByteArray> roleNames() const override; + virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole ) const override; + virtual int rowCount (const QModelIndex& parent = QModelIndex() ) const override; + virtual Qt::ItemFlags flags (const QModelIndex& index ) const override; + virtual bool setData (const QModelIndex& index, const QVariant &value, int role) override; + virtual QHash<int,QByteArray> roleNames() const override; //Getters Q_INVOKABLE bool isActionEnabled ( UserActionModel::Action action ) const; Q_INVOKABLE uint relativeIndex ( UserActionModel::Action action ) const; - Q_INVOKABLE uint enabledCount ( ) const; - - bool isPickupEnabled () const; - bool isHoldEnabled () const; - bool isUnholdEnabled () const; - bool isHangupEnabled () const; - bool isMuteEnabled () const; - bool isTransferEnabled() const; - bool isRecordEnabled () const; - bool isRefuseEnabled () const; - bool isAcceptEnabled () const; private: const QScopedPointer<UserActionModelPrivate> d_ptr; @@ -107,6 +94,6 @@ Q_SIGNALS: ///The list of currently available actions has changed void actionStateChanged(); }; -// Q_DECLARE_METATYPE(UserActionModel*) +Q_DECLARE_METATYPE(UserActionModel*) #endif