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