Skip to content
Snippets Groups Projects
Commit b6af344c authored by Emmanuel Lepage Vallee's avatar Emmanuel Lepage Vallee
Browse files

Allow to call actions displayed in the UserActionModel

Refs #66613
parent dfd7b01f
No related branches found
No related tags found
No related merge requests found
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
***************************************************************************/ ***************************************************************************/
#include "pixmapmanipulationdelegate.h" #include "pixmapmanipulationdelegate.h"
#include <useractionmodel.h>
#include <QtCore/QSize> #include <QtCore/QSize>
PixmapManipulationDelegate* PixmapManipulationDelegate::m_spInstance = new PixmapManipulationDelegate(); PixmapManipulationDelegate* PixmapManipulationDelegate::m_spInstance = new PixmapManipulationDelegate();
...@@ -80,3 +82,9 @@ QVariant PixmapManipulationDelegate::profilePhoto(const QByteArray& data) ...@@ -80,3 +82,9 @@ QVariant PixmapManipulationDelegate::profilePhoto(const QByteArray& data)
Q_UNUSED(data) Q_UNUSED(data)
return QVariant(); return QVariant();
} }
QVariant PixmapManipulationDelegate::userActionIcon(const UserActionElement& state) const
{
Q_UNUSED(state)
return QVariant();
}
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
class Person ; class Person ;
class ContactMethod ; class ContactMethod ;
class Call ; class Call ;
struct UserActionElement;
/** /**
* Different clients can have multiple way of displaying images. Some may * Different clients can have multiple way of displaying images. Some may
...@@ -50,6 +51,10 @@ public: ...@@ -50,6 +51,10 @@ public:
virtual QByteArray toByteArray(const QVariant& pxm); virtual QByteArray toByteArray(const QVariant& pxm);
virtual QVariant profilePhoto(const QByteArray& data); virtual QVariant profilePhoto(const QByteArray& data);
/**
* Return the icons associated with the action and its state
*/
virtual QVariant userActionIcon(const UserActionElement& state) const;
//Singleton //Singleton
static PixmapManipulationDelegate* instance(); static PixmapManipulationDelegate* instance();
......
/****************************************************************************
* 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 USERACTIONS_H
#define USERACTIONS_H
/**
* This code used to be in the KDE client. It doesn't really fit well in well
* in what libringclient is supposed to do, but as it has to be replicated for
* each clients, then UserActionModel will provide an abstract way to call those
* functions.
*/
namespace UserActions {
bool accept(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if(!call) {
qDebug() << "Calling when no item is selected. Opening an item.";
CallModel::instance()->dialingCall();
CallModel::instance()->selectionModel()->setCurrentIndex(CallModel::instance()->getIndex(call), QItemSelectionModel::ClearAndSelect);
}
else {
const Call::State state = call->state();
//TODO port to lifeCycle code
if (state == Call::State::RINGING || state == Call::State::CURRENT || state == Call::State::HOLD
|| state == Call::State::BUSY || state == Call::State::FAILURE || state == Call::State::ERROR) {
qDebug() << "Calling when item currently ringing, current, hold or busy. Opening an item.";
Call* c2 = CallModel::instance()->dialingCall();
CallModel::instance()->selectionModel()->setCurrentIndex(CallModel::instance()->getIndex(c2), QItemSelectionModel::ClearAndSelect);
}
else {
try {
call->performAction(Call::Action::ACCEPT);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
ret = false;
}
}
}
}
return ret;
} //accept
///Call
bool hangup(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if (call) {
try {
call->performAction(Call::Action::REFUSE);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
}
}
}
return ret;
} //hangup
///Refuse call
bool refuse(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if(!call) {
qDebug() << "Error : Hanging up when no item selected. Should not happen.";
}
else {
try {
call->performAction(Call::Action::REFUSE);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
}
}
}
return ret;
}
///Put call on hold
bool hold(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if(!call) {
qDebug() << "Error : Holding when no item selected. Should not happen.";
}
else {
try {
call->performAction(Call::Action::HOLD);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
}
}
}
return ret;
}
///Remove call from hold
bool unhold(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if(!call) {
qDebug() << "Error : Un-Holding when no item selected. Should not happen.";
}
else {
try {
call->performAction(Call::Action::HOLD);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
}
}
}
return ret;
}
///Transfer a call
bool transfer(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if(!call) {
qDebug() << "Error : Transferring when no item selected. Should not happen.";
}
else {
try {
call->performAction(Call::Action::TRANSFER);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
}
}
}
return ret;
}
///Record a call
bool record(const QList<Call*> calls)
{
bool ret = true;
for (Call* call : calls) {
if(!call) {
qDebug() << "Error : Recording when no item selected. Should not happen.";
}
else {
try {
call->performAction(Call::Action::RECORD);
}
catch(const char * msg) {
// KMessageBox::error(Ring::app(),i18n(msg));
}
}
}
return ret;
}
}; //namespace UserActions
#endif
\ No newline at end of file
...@@ -19,12 +19,25 @@ ...@@ -19,12 +19,25 @@
//Qt //Qt
#include <QtCore/QItemSelection> #include <QtCore/QItemSelection>
#include <QtCore/QSortFilterProxyModel>
//Ring //Ring
#include "call.h" #include "call.h"
#include "callmodel.h" #include "callmodel.h"
#include "account.h" #include "account.h"
#include "accountmodel.h" #include "accountmodel.h"
#include "delegates/pixmapmanipulationdelegate.h"
#include "private/useractions.h"
class ActiveUserActionModel : public QSortFilterProxyModel
{
public:
ActiveUserActionModel(QAbstractItemModel* parent) : QSortFilterProxyModel(parent)
{
setSourceModel(parent);
}
virtual bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override;
};
class UserActionModelPrivate : public QObject class UserActionModelPrivate : public QObject
{ {
...@@ -47,35 +60,39 @@ public: ...@@ -47,35 +60,39 @@ public:
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 , Account::RegistrationState > , UserActionModel::Action > availableAccountActionMap ;
static const TypedStateMachine< TypedStateMachine< bool , SelectionState > , UserActionModel::Action > multi_call_options ; static const TypedStateMachine< TypedStateMachine< bool , SelectionState > , UserActionModel::Action > multi_call_options ;
static const TypedStateMachine< bool , UserActionModel::Action > heterogenous_call_options ;
static const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserActionModel::Action > availableProtocolActions ; static const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserActionModel::Action > availableProtocolActions ;
static const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLevel , SelectionState > , UserActionModel::Action > actionStatefulness ; static const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLevel , SelectionState > , UserActionModel::Action > actionStatefulness ;
//Helpers //Helpers
void updateActions();
bool updateAction (UserActionModel::Action action ); bool updateAction (UserActionModel::Action action );
bool updateByCall (UserActionModel::Action action, const Call* c ); bool updateByCall (UserActionModel::Action action, const Call* c );
void updateCheckMask(int& ret, UserActionModel::Action action, const Call* c );
//Attribues //Attribues
Call* m_pCall ; Call* m_pCall ;
UserActionModelMode m_Mode ; UserActionModelMode m_Mode ;
SelectionState m_SelectionState ; SelectionState m_SelectionState ;
TypedStateMachine< bool, UserActionModel::Action> m_CurrentActions ; TypedStateMachine< bool, UserActionModel::Action> m_CurrentActions ;
TypedStateMachine< Qt::CheckState, UserActionModel::Action> m_CurrentActionsState;
static const TypedStateMachine< QString, UserActionModel::Action> m_ActionNames ; static const TypedStateMachine< QString, UserActionModel::Action> m_ActionNames ;
ActiveUserActionModel* m_pActiveModel ;
//The mute per call, per media is not merged upstream yet, faking it for now
//BEGIN HACK
bool m_MuteAudio_TO_REMOVE;
bool m_MuteVideo_TO_REMOVE;
//END HACK
private: private:
UserActionModel* q_ptr; UserActionModel* q_ptr;
public Q_SLOTS: public Q_SLOTS:
//Single call mode //Single call mode
void slotStateChanged(); void slotStateChanged(); //TODO
//CallModel mode //CallModel mode
void slotCurrentChanged (const QModelIndex& current , const QModelIndex& previous ); void updateActions();
void slotSelectionChanged (const QItemSelection& selected, const QItemSelection& deselected);
void slotCallStateChanged ( Call* call, Call::State previousState );
//Common
void slotAccountStateChanged ( Account* account, const Account::RegistrationState state );
}; };
...@@ -115,10 +132,13 @@ const TypedStateMachine< TypedStateMachine< bool , Account::RegistrationState > ...@@ -115,10 +132,13 @@ const TypedStateMachine< TypedStateMachine< bool , Account::RegistrationState >
/* JOIN */ {{ true , true , true , true }}, /* JOIN */ {{ true , true , true , true }},
}}; }};
/**
* This matrix define if an option is available depending on the number of selection elements
*/
const TypedStateMachine< TypedStateMachine< bool , UserActionModelPrivate::SelectionState > , UserActionModel::Action > UserActionModelPrivate::multi_call_options = {{ const TypedStateMachine< TypedStateMachine< bool , UserActionModelPrivate::SelectionState > , UserActionModel::Action > UserActionModelPrivate::multi_call_options = {{
/* NONE UNIQUE MULTI */ /* NONE UNIQUE MULTI */
/* ACCEPT */ {{ false, true , true }}, /* ACCEPT */ {{ false, true , true }},
/* HOLD */ {{ false, true , true }}, /* HOLD */ {{ false, true , false }},
/* MUTE_AUDIO */ {{ false, true , true }}, /* MUTE_AUDIO */ {{ false, true , true }},
/* MUTE_VIDEO */ {{ false, true , true }}, /* MUTE_VIDEO */ {{ false, true , true }},
/* SERVER_TRANSFER */ {{ false, true , true }}, /* SERVER_TRANSFER */ {{ false, true , true }},
...@@ -128,6 +148,24 @@ const TypedStateMachine< TypedStateMachine< bool , UserActionModelPrivate::Selec ...@@ -128,6 +148,24 @@ const TypedStateMachine< TypedStateMachine< bool , UserActionModelPrivate::Selec
/* JOIN */ {{ false, false, true }}, /* JOIN */ {{ false, false, true }},
}}; }};
/**
* This matrix define if an option is available when multiple elements with mismatching CheckState are selected
*/
const TypedStateMachine< bool, UserActionModel::Action > UserActionModelPrivate::heterogenous_call_options = {{
/* ACCEPT */ true , /* N/A */
/* HOLD */ false , /* Do not allow to set a state */
/* MUTE_AUDIO */ true , /* Force mute on all calls */
/* MUTE_VIDEO */ true , /* Mute all calls */
/* SERVER_TRANSFER */ false , /* It make no sense to let the user set this */
/* RECORD */ true , /* Force "record" on all calls */
/* HANGUP */ true , /* N/A */
/* JOIN */ true , /* N/A */
}};
/**
* This matrix allow to enable/disable actions depending on the call protocol
*/
const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserActionModel::Action > UserActionModelPrivate::availableProtocolActions = {{ const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserActionModel::Action > UserActionModelPrivate::availableProtocolActions = {{
/* SIP IAX DHT */ /* SIP IAX DHT */
/* ACCEPT */ {{ true , true , true }}, /* ACCEPT */ {{ true , true , true }},
...@@ -141,6 +179,12 @@ const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserAct ...@@ -141,6 +179,12 @@ const TypedStateMachine< TypedStateMachine< bool , Account::Protocol > , UserAct
/* JOIN */ {{ true , true , true }}, /* JOIN */ {{ true , true , true }},
}}; }};
/**
* This matrix define if an action is stateless or stateful. The only state
* supported is "checked", but when multiple items are selected, this can
* cause a heterogenous bunch of checked and unchecked elements, this is
* called "TRISTATE".
*/
#define ST UserActionModel::ActionStatfulnessLevel:: #define ST UserActionModel::ActionStatfulnessLevel::
const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLevel , UserActionModelPrivate::SelectionState > , UserActionModel::Action > UserActionModelPrivate::actionStatefulness = {{ const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLevel , UserActionModelPrivate::SelectionState > , UserActionModel::Action > UserActionModelPrivate::actionStatefulness = {{
/* NONE UNIQUE MULTI */ /* NONE UNIQUE MULTI */
...@@ -151,6 +195,7 @@ const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLe ...@@ -151,6 +195,7 @@ const TypedStateMachine< TypedStateMachine< UserActionModel::ActionStatfulnessLe
/* SERVER_TRANSFER */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, /* SERVER_TRANSFER */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }},
/* RECORD */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }}, /* RECORD */ {{ ST UNISTATE, ST CHECKABLE , ST TRISTATE }},
/* HANGUP */ {{ ST UNISTATE, ST UNISTATE , ST TRISTATE }}, /* HANGUP */ {{ ST UNISTATE, ST UNISTATE , ST TRISTATE }},
/* JOIN */ {{ ST UNISTATE, ST UNISTATE , ST UNISTATE }}, /* JOIN */ {{ ST UNISTATE, ST UNISTATE , ST UNISTATE }},
}}; }};
#undef ST #undef ST
...@@ -163,10 +208,12 @@ const TypedStateMachine< QString, UserActionModel::Action> UserActionModelPrivat ...@@ -163,10 +208,12 @@ const TypedStateMachine< QString, UserActionModel::Action> UserActionModelPrivat
/* SERVER_TRANSFER */ QObject::tr("SERVER_TRANSFER" ), /* SERVER_TRANSFER */ QObject::tr("SERVER_TRANSFER" ),
/* RECORD */ QObject::tr("RECORD" ), /* RECORD */ QObject::tr("RECORD" ),
/* HANGUP */ QObject::tr("HANGUP" ), /* HANGUP */ QObject::tr("HANGUP" ),
/* JOIN */ QObject::tr("JOIN" ), /* JOIN */ QObject::tr("JOIN" ),
}}; }};
UserActionModelPrivate::UserActionModelPrivate(UserActionModel* parent) : QObject(parent),q_ptr(parent),m_pCall(nullptr) UserActionModelPrivate::UserActionModelPrivate(UserActionModel* parent) : QObject(parent),q_ptr(parent),
m_pCall(nullptr), m_pActiveModel(nullptr)
{ {
} }
...@@ -181,7 +228,8 @@ UserActionModel::UserActionModel(Call* parent) : QAbstractListModel(parent),d_pt ...@@ -181,7 +228,8 @@ UserActionModel::UserActionModel(Call* parent) : QAbstractListModel(parent),d_pt
d_ptr->m_SelectionState = UserActionModelPrivate::SelectionState::UNIQUE; d_ptr->m_SelectionState = UserActionModelPrivate::SelectionState::UNIQUE;
d_ptr->m_Mode = UserActionModelPrivate::UserActionModelMode::CALL; d_ptr->m_Mode = UserActionModelPrivate::UserActionModelMode::CALL;
d_ptr->m_pCall = parent; d_ptr->m_pCall = parent;
connect(AccountModel::instance(), &AccountModel::accountStateChanged , d_ptr.data(), &UserActionModelPrivate::slotAccountStateChanged);
connect(AccountModel::instance(), SIGNAL(accountStateChanged(Account*,Account::RegistrationState)), d_ptr.data(), SLOT(slotStateChanged()));
} }
/** /**
...@@ -192,11 +240,11 @@ UserActionModel::UserActionModel(CallModel* parent) : QAbstractListModel(parent) ...@@ -192,11 +240,11 @@ UserActionModel::UserActionModel(CallModel* parent) : QAbstractListModel(parent)
Q_ASSERT(parent != nullptr); Q_ASSERT(parent != nullptr);
d_ptr->m_Mode = UserActionModelPrivate::UserActionModelMode::CALLMODEL; d_ptr->m_Mode = UserActionModelPrivate::UserActionModelMode::CALLMODEL;
d_ptr->m_SelectionState = UserActionModelPrivate::SelectionState::UNIQUE; 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 );
connect(AccountModel::instance(), &AccountModel::accountStateChanged , d_ptr.data(), &UserActionModelPrivate::slotAccountStateChanged); connect(parent->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)) , d_ptr.data(), SLOT(updateActions()));
connect(parent->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)) , d_ptr.data(), SLOT(updateActions()));
connect(parent, SIGNAL(callStateChanged(Call*,Call::State)) , d_ptr.data(), SLOT(updateActions()));
connect(AccountModel::instance(), SIGNAL(accountStateChanged(Account*,Account::RegistrationState)), d_ptr.data(), SLOT(updateActions()));
} }
UserActionModel::~UserActionModel() UserActionModel::~UserActionModel()
...@@ -226,7 +274,16 @@ QVariant UserActionModel::data(const QModelIndex& idx, int role ) const ...@@ -226,7 +274,16 @@ QVariant UserActionModel::data(const QModelIndex& idx, int role ) const
case Qt::DisplayRole: case Qt::DisplayRole:
return UserActionModelPrivate::m_ActionNames[action]; return UserActionModelPrivate::m_ActionNames[action];
case Qt::CheckStateRole: case Qt::CheckStateRole:
return Qt::Unchecked; if (d_ptr->actionStatefulness[action][d_ptr->m_SelectionState] != UserActionModel::ActionStatfulnessLevel::UNISTATE)
return d_ptr->m_CurrentActionsState[action];
break;
case Qt::DecorationRole: {
UserActionElement state;
state.action = action ;
state.calls = {} ;
state.checkState = Qt::Unchecked;
return PixmapManipulationDelegate::instance()->userActionIcon(state);
}
}; };
return QVariant(); return QVariant();
...@@ -246,7 +303,7 @@ Qt::ItemFlags UserActionModel::flags(const QModelIndex& idx ) const ...@@ -246,7 +303,7 @@ Qt::ItemFlags UserActionModel::flags(const QModelIndex& idx ) const
UserActionModel::Action action = static_cast<UserActionModel::Action>(idx.row()); UserActionModel::Action action = static_cast<UserActionModel::Action>(idx.row());
return (d_ptr->m_CurrentActions[action] ? (Qt::ItemIsEnabled | Qt::ItemIsSelectable) : Qt::NoItemFlags) return (d_ptr->m_CurrentActions[action] ? (Qt::ItemIsEnabled | Qt::ItemIsSelectable) : Qt::NoItemFlags)
| (d_ptr->actionStatefulness[action][d_ptr->m_SelectionState] == UserActionModel::ActionStatfulnessLevel::CHECKABLE | (d_ptr->actionStatefulness[action][d_ptr->m_SelectionState] != UserActionModel::ActionStatfulnessLevel::UNISTATE
? Qt::ItemIsUserCheckable : Qt::NoItemFlags); ? Qt::ItemIsUserCheckable : Qt::NoItemFlags);
} }
...@@ -269,6 +326,38 @@ void UserActionModelPrivate::slotStateChanged() ...@@ -269,6 +326,38 @@ void UserActionModelPrivate::slotStateChanged()
emit q_ptr->actionStateChanged(); emit q_ptr->actionStateChanged();
} }
void UserActionModelPrivate::updateCheckMask(int& ret, UserActionModel::Action action, const Call* c)
{
switch(action) {
case UserActionModel::Action::ACCEPT :
ret += 0;
break;
case UserActionModel::Action::HOLD :
ret += c->state() == Call::State::HOLD? 100 : 1;
break;
case UserActionModel::Action::MUTE_AUDIO :
ret += m_MuteAudio_TO_REMOVE ? 100 : 1;
break;
case UserActionModel::Action::MUTE_VIDEO :
ret += m_MuteVideo_TO_REMOVE ? 100 : 1;
break;
case UserActionModel::Action::SERVER_TRANSFER :
ret += c->state() == Call::State::TRANSFERRED? 100 : 1;
break;
case UserActionModel::Action::RECORD :
ret += c->isRecording() ? 100 : 1;
break;
case UserActionModel::Action::HANGUP :
ret += 0;
break;
case UserActionModel::Action::JOIN :
ret += 0;
break;
case UserActionModel::Action::COUNT__:
break;
};
}
bool UserActionModelPrivate::updateByCall(UserActionModel::Action action, const Call* c) bool UserActionModelPrivate::updateByCall(UserActionModel::Action action, const Call* c)
{ {
return (!c) ? false : ( return (!c) ? false : (
...@@ -281,15 +370,28 @@ bool UserActionModelPrivate::updateByCall(UserActionModel::Action action, const ...@@ -281,15 +370,28 @@ bool UserActionModelPrivate::updateByCall(UserActionModel::Action action, const
bool UserActionModelPrivate::updateAction(UserActionModel::Action action) bool UserActionModelPrivate::updateAction(UserActionModel::Action action)
{ {
int state = 0;
switch(m_Mode) { switch(m_Mode) {
case UserActionModelMode::CALL: case UserActionModelMode::CALL:
updateCheckMask(state,action,m_pCall);
m_CurrentActionsState[action] = state / 100 ? Qt::Checked : Qt::Unchecked;
return updateByCall(action, m_pCall); return updateByCall(action, m_pCall);
case UserActionModelMode::CALLMODEL: { case UserActionModelMode::CALLMODEL: {
bool ret = true; bool ret = true;
m_SelectionState = CallModel::instance()->selectionModel()->selectedRows().size() > 1 ? SelectionState::MULTI : SelectionState::UNIQUE; 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))); //Aggregate and reduce the action state for each selected calls
return ret; for (const QModelIndex& idx : CallModel::instance()->selectionModel()->selectedRows()) {
const Call* c = qvariant_cast<Call*>(idx.data(Call::Role::Object));
updateCheckMask ( state ,action, c );
ret &= updateByCall( action , c );
}
//Detect if the multiple selection has mismatching item states, disable it if necessary
m_CurrentActionsState[action] = (state % 100 && state / 100) ? Qt::PartiallyChecked : (state / 100 ? Qt::Checked : Qt::Unchecked);
return ret && (m_CurrentActionsState[action] != Qt::PartiallyChecked || heterogenous_call_options[action]);
} }
}; };
return false; return false;
...@@ -302,42 +404,129 @@ void UserActionModelPrivate::updateActions() ...@@ -302,42 +404,129 @@ void UserActionModelPrivate::updateActions()
emit q_ptr->dataChanged(q_ptr->index(0,0),q_ptr->index(enum_class_size<UserActionModel::Action>()-1,0)); emit q_ptr->dataChanged(q_ptr->index(0,0),q_ptr->index(enum_class_size<UserActionModel::Action>()-1,0));
} }
void UserActionModelPrivate::slotCurrentChanged(const QModelIndex& current, const QModelIndex& previous) uint UserActionModel::relativeIndex( UserActionModel::Action action ) const
{ {
Q_UNUSED(current) int i(0),ret(0);
Q_UNUSED(previous) while (i != static_cast<int>(action) && i < enum_class_size<UserActionModel::Action>()) {
updateActions(); ret += isActionEnabled(static_cast<UserActionModel::Action>(i))?1:0;
i++;
}
return ret;
} }
void UserActionModelPrivate::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) bool UserActionModel::execute(const UserActionModel::Action action) const
{ {
Q_UNUSED(selected) /*
Q_UNUSED(deselected) * The rational behind not expanding Call::Actions to handle this are:
updateActions(); *
* 1) There is some actions that apply _only_ to multiple calls
* 2) There is still a need to abstract the multi call use case
*/
//1) Build the list of all selected calls
QList<Call*> selected;
switch(d_ptr->m_Mode) {
case UserActionModelPrivate::UserActionModelMode::CALL:
selected << d_ptr->m_pCall;
break;
case UserActionModelPrivate::UserActionModelMode::CALLMODEL: {
for (const QModelIndex& idx : CallModel::instance()->selectionModel()->selectedRows()) {
Call* c = qvariant_cast<Call*>(idx.data(Call::Role::Object));
if (c)
selected << c;
} }
break;
}
};
void UserActionModelPrivate::slotCallStateChanged(Call* call, Call::State previousState) //2) Perform the actions
{ switch(action) {
Q_UNUSED(call) case UserActionModel::Action::ACCEPT :
Q_UNUSED(previousState) if (UserActions::accept(selected))
updateActions(); d_ptr->updateActions();
break;
case UserActionModel::Action::HOLD :
switch(d_ptr->m_CurrentActionsState[UserActionModel::Action::HOLD]) {
case Qt::Checked:
if (UserActions::unhold(selected))
d_ptr->updateActions();
break;
case Qt::Unchecked:
if (UserActions::hold(selected))
d_ptr->updateActions();
break;
case Qt::PartiallyChecked:
//Invalid
break;
};
break;
case UserActionModel::Action::MUTE_AUDIO :
d_ptr->m_MuteAudio_TO_REMOVE != d_ptr->m_MuteAudio_TO_REMOVE; //FIXME evil fake property
d_ptr->updateActions();
break;
case UserActionModel::Action::MUTE_VIDEO :
d_ptr->m_MuteVideo_TO_REMOVE != d_ptr->m_MuteVideo_TO_REMOVE; //FIXME evil fake property
d_ptr->updateActions();
UserActions::accept(selected);
break;
case UserActionModel::Action::SERVER_TRANSFER :
UserActions::transfer(selected);
break;
case UserActionModel::Action::RECORD :
if (UserActions::record(selected))
d_ptr->updateActions();
break;
case UserActionModel::Action::HANGUP :
if (UserActions::accept(selected))
d_ptr->updateActions();
break;
case UserActionModel::Action::JOIN :
//TODO unimplemented
break;
case UserActionModel::Action::COUNT__:
break;
};
return true; //TODO handle errors
} }
void UserActionModelPrivate::slotAccountStateChanged(Account* account, const Account::RegistrationState state) /**
* Execute an action
* @param idx A model index. It can be from proxies.
*/
bool UserActionModel::execute(const QModelIndex& idx) const
{ {
Q_UNUSED(account) QModelIndex idx2 = idx;
Q_UNUSED(state)
updateActions(); //Make this API a little more user friendly and unwind the proxies
QAbstractProxyModel* m = nullptr;
while (idx2.model() != this && idx2.isValid()) {
m = qobject_cast<QAbstractProxyModel*>(const_cast<QAbstractItemModel*>(idx2.model()));
if (m)
idx2 = m->mapToSource(idx2);
else
break;
} }
uint UserActionModel::relativeIndex( UserActionModel::Action action ) const if (!idx2.isValid())
return false;
UserActionModel::Action action = static_cast<UserActionModel::Action>(idx2.row());
return execute(action);
}
///Get a model filter with only the available actions
QSortFilterProxyModel* UserActionModel::activeActionModel() const
{ {
int i(0),ret(0); if (!d_ptr->m_pActiveModel)
while (i != static_cast<int>(action) && i < enum_class_size<UserActionModel::Action>()) { d_ptr->m_pActiveModel = new ActiveUserActionModel(const_cast<UserActionModel*>(this));
ret += isActionEnabled(static_cast<UserActionModel::Action>(i))?1:0; return d_ptr->m_pActiveModel;
i++;
} }
return ret;
//Do not display disabled account
bool ActiveUserActionModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
{
return sourceModel()->index(source_row,0,source_parent).flags() & Qt::ItemIsEnabled;
} }
#include <useractionmodel.moc> #include <useractionmodel.moc>
...@@ -22,8 +22,11 @@ ...@@ -22,8 +22,11 @@
#include <QtCore/QAbstractItemModel> #include <QtCore/QAbstractItemModel>
#include "typedefs.h" #include "typedefs.h"
#include "call.h" //Qt
class QSortFilterProxyModel;
//Ring
#include "call.h"
class Call; class Call;
class CallModel; class CallModel;
class UserActionModelPrivate; class UserActionModelPrivate;
...@@ -70,6 +73,8 @@ public: ...@@ -70,6 +73,8 @@ public:
}; };
Q_ENUMS(Action) Q_ENUMS(Action)
Q_PROPERTY(QSortFilterProxyModel* activeActionModel READ activeActionModel);
//Constructor //Constructor
explicit UserActionModel(Call* parent); explicit UserActionModel(Call* parent);
UserActionModel(CallModel* parent); UserActionModel(CallModel* parent);
...@@ -85,6 +90,11 @@ public: ...@@ -85,6 +90,11 @@ public:
//Getters //Getters
Q_INVOKABLE bool isActionEnabled ( UserActionModel::Action action ) const; Q_INVOKABLE bool isActionEnabled ( UserActionModel::Action action ) const;
Q_INVOKABLE uint relativeIndex ( UserActionModel::Action action ) const; Q_INVOKABLE uint relativeIndex ( UserActionModel::Action action ) const;
QSortFilterProxyModel* activeActionModel() const;
//Mutators
bool execute( const Action action ) const;
bool execute( const QModelIndex& idx ) const;
private: private:
const QScopedPointer<UserActionModelPrivate> d_ptr; const QScopedPointer<UserActionModelPrivate> d_ptr;
...@@ -96,4 +106,14 @@ Q_SIGNALS: ...@@ -96,4 +106,14 @@ Q_SIGNALS:
}; };
Q_DECLARE_METATYPE(UserActionModel*) Q_DECLARE_METATYPE(UserActionModel*)
/**
* "Java bean" used to avoid having a 5+ parameter pixmap delegate
*/
struct UserActionElement {
UserActionModel::Action action ;
QList<Call*> calls ;
Qt::CheckState checkState ;
};
#endif #endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment