From 8a10a798db08b29af234aa53e7c02272f363c7c4 Mon Sep 17 00:00:00 2001
From: Emmanuel Lepage <emmanuel.lepage@savoirfairelinux.com>
Date: Wed, 23 May 2012 17:35:00 -0400
Subject: [PATCH] [ #11337 ] Add basic text to speech status for calls

---
 kde/po/fr/sflphone-client-kde.po          |  31 +++++++
 kde/src/CMakeLists.txt                    |   3 +
 kde/src/CallView.cpp                      |   8 +-
 kde/src/KSpeechInterfaceSingleton.cpp     |  33 +++++++
 kde/src/KSpeechInterfaceSingleton.h       |  32 +++++++
 kde/src/SFLPhone.cpp                      |   7 ++
 kde/src/SFLPhoneAccessibility.cpp         | 100 ++++++++++++++++++++++
 kde/src/SFLPhoneAccessibility.h           |  41 +++++++++
 kde/src/klib/dataengine/sflphonEngine.cpp |  33 ++-----
 kde/src/lib/Call.cpp                      |  59 ++++++++++++-
 kde/src/lib/Call.h                        |   6 +-
 kde/src/lib/CallModel.cpp                 |  16 ++--
 kde/src/lib/CallModel.h                   |  19 ++--
 kde/src/lib/CallModel.hpp                 |   4 +-
 kde/src/lib/Contact.h                     |  22 ++---
 kde/src/main.cpp                          |   1 -
 kde/src/widgets/ContactItemWidget.cpp     |   3 -
 kde/src/widgets/HistoryTreeItem.cpp       |   3 +-
 18 files changed, 358 insertions(+), 63 deletions(-)
 create mode 100644 kde/src/KSpeechInterfaceSingleton.cpp
 create mode 100644 kde/src/KSpeechInterfaceSingleton.h
 create mode 100644 kde/src/SFLPhoneAccessibility.cpp
 create mode 100644 kde/src/SFLPhoneAccessibility.h

diff --git a/kde/po/fr/sflphone-client-kde.po b/kde/po/fr/sflphone-client-kde.po
index 051391efec..1fc88c3a4c 100755
--- a/kde/po/fr/sflphone-client-kde.po
+++ b/kde/po/fr/sflphone-client-kde.po
@@ -952,6 +952,12 @@ msgstr "Terminée"
 msgid "Error"
 msgstr "Erreur"
 
+msgid "Ringing (in)"
+msgstr "Sonnerie (entrant)"
+
+msgid "Ringing (out)"
+msgstr "Sonnerie (sortante)"
+
 #: src/klib/dataengine/sflphonEngine.cpp:302
 msgid "Current_account"
 msgstr ""
@@ -1528,6 +1534,31 @@ msgstr "Définir si il faut ajouter un préfix pour les appels sortants"
 msgid "Defines the prefix to add."
 msgstr "Définir le préfix à ajouter"
 
+#: src/SFLPhoneAccessibility.cpp:30
+msgid "You currently have %1 calls"
+msgstr "Vous avez %1 appel en cours"
+
+#: src/SFLPhoneAccessibility.cpp:30
+msgid "You currently have no call"
+msgstr "Vous n'avez pas d'appels en cours"
+
+#: src/SFLPhoneAccessibility.cpp:30
+msgid "Call from %1, number %2"
+msgstr "Appel de %1, au numero %2"
+
+msgid "The current call is %1"
+msgstr "L'appel actuel est %1"
+
+msgid ",Your peer is %1"
+msgstr ", votre interlocuteur est %1"
+
+msgid ", the peer phone number is %1 "
+msgstr ", le numero de votre interlocuteur est %1"
+
+msgid " and you have been talking since %1 seconds"
+msgstr " et vous parlez depuis %1 seconde"
+
+
 #: rc.cpp:467
 msgctxt "NAME OF TRANSLATORS"
 msgid "Your names"
diff --git a/kde/src/CMakeLists.txt b/kde/src/CMakeLists.txt
index 2e1af6b3ff..c2cd1e36ed 100755
--- a/kde/src/CMakeLists.txt
+++ b/kde/src/CMakeLists.txt
@@ -28,6 +28,8 @@ SET(
    SFLPhoneView.cpp
    SFLPhone.cpp
    SFLPhoneapplication.cpp
+   KSpeechInterfaceSingleton.cpp
+   SFLPhoneAccessibility.cpp
    widgets/SFLPhoneTray.cpp
    main.cpp
    AccountWizard.cpp
@@ -76,6 +78,7 @@ SET(
 )
 
 KDE4_ADD_UI_FILES(sflphone_client_kde_SRCS ui/SFLPhoneView_base.ui  ${config_ui_files}  )
+QT4_ADD_DBUS_INTERFACES(sflphone_client_kde_SRCS ${KDE4_DBUS_INTERFACES_DIR}/org.kde.KSpeech.xml)
 
 
 KDE4_ADD_EXECUTABLE(sflphone-client-kde ${sflphone_client_kde_SRCS} ${QtApp_RCC_SRCS})
diff --git a/kde/src/CallView.cpp b/kde/src/CallView.cpp
index 3c8ff903a6..7c2045451e 100644
--- a/kde/src/CallView.cpp
+++ b/kde/src/CallView.cpp
@@ -78,6 +78,10 @@ CallView::CallView(QWidget* parent) : QTreeWidget(parent),m_pActiveOverlay(0),m_
    gl->addWidget(m_pTransferB                                                    , 1 , 4 , 1 , 2 );
    gl->addItem  (new QSpacerItem(0,0,QSizePolicy::Expanding,QSizePolicy::Minimum), 2 , 0 , 1 , 3 );
 
+   foreach(Call* active, SFLPhone::model()->getCallList()) {
+      addCall(active);
+   }
+
    //User Interface even
    //              SENDER                                   SIGNAL                              RECEIVER                     SLOT                        /
    /**/connect(this              , SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)              ) , this, SLOT( itemDoubleClicked(QTreeWidgetItem*,int)) );
@@ -583,7 +587,9 @@ void CallView::itemDoubleClicked(QTreeWidgetItem* item, int column) {
 
 void CallView::itemClicked(QTreeWidgetItem* item, int column) {
    Q_UNUSED(column)
-   emit itemChanged(SFLPhone::model()->getCall(item));
+   Call* call = SFLPhone::model()->getCall(item);
+   call->setSelected(true);
+   emit itemChanged(call);
    kDebug() << "Item clicked";
 }
 
diff --git a/kde/src/KSpeechInterfaceSingleton.cpp b/kde/src/KSpeechInterfaceSingleton.cpp
new file mode 100644
index 0000000000..5e748d54ca
--- /dev/null
+++ b/kde/src/KSpeechInterfaceSingleton.cpp
@@ -0,0 +1,33 @@
+/***************************************************************************
+ *   Copyright (C) 2009-2012 by Savoir-Faire Linux                         *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>*
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 3 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ **************************************************************************/
+#include "KSpeechInterfaceSingleton.h"
+
+#include <QtDBus/QDBusConnection>
+#include <KSpeech>
+
+OrgKdeKSpeechInterface* KSpeechInterfaceSingleton::m_pInstance = 0;
+
+OrgKdeKSpeechInterface* KSpeechInterfaceSingleton::getInstance() {
+   if (!m_pInstance) {
+      m_pInstance = new OrgKdeKSpeechInterface( "org.kde.kttsd", "/KSpeech", QDBusConnection::sessionBus());
+      m_pInstance->setApplicationName("SFLPhone KDE");
+   }
+   return m_pInstance;
+}
\ No newline at end of file
diff --git a/kde/src/KSpeechInterfaceSingleton.h b/kde/src/KSpeechInterfaceSingleton.h
new file mode 100644
index 0000000000..b09ef1f421
--- /dev/null
+++ b/kde/src/KSpeechInterfaceSingleton.h
@@ -0,0 +1,32 @@
+/***************************************************************************
+ *   Copyright (C) 2009-2012 by Savoir-Faire Linux                         *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>*
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 3 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ **************************************************************************/
+#ifndef KSPEECH_INTERFACE_SINGLETON_H
+#define KSPEECH_INTERFACE_SINGLETON_H
+
+#include "src/kspeechinterface.h"
+
+class KSpeechInterfaceSingleton {
+public:
+   static OrgKdeKSpeechInterface* getInstance();
+private:
+   static OrgKdeKSpeechInterface* m_pInstance;
+};
+
+#endif
\ No newline at end of file
diff --git a/kde/src/SFLPhone.cpp b/kde/src/SFLPhone.cpp
index 8cadd64f45..814971aa8e 100755
--- a/kde/src/SFLPhone.cpp
+++ b/kde/src/SFLPhone.cpp
@@ -55,6 +55,7 @@
 #include "widgets/HistoryDock.h"
 #include "widgets/BookmarkDock.h"
 #include "klib/ConfigurationSkeleton.h"
+#include "SFLPhoneAccessibility.h"
 
 SFLPhone* SFLPhone::m_sApp              = NULL;
 TreeWidgetCallModel* SFLPhone::m_pModel = NULL;
@@ -230,6 +231,12 @@ void SFLPhone::setupActions()
    actionCollection()->addAction("action_accountCreationWizard" , action_accountCreationWizard );
    actionCollection()->addAction("action_configureShortcut"     , action_configureShortcut     );
 
+   QList<KAction*> acList = *SFLPhoneAccessibility::getInstance();
+   
+   foreach(KAction* ac,acList) {
+      actionCollection()->addAction(ac->objectName() , ac);
+   }
+
    setAutoSaveSettings();
    createGUI();
 }
diff --git a/kde/src/SFLPhoneAccessibility.cpp b/kde/src/SFLPhoneAccessibility.cpp
new file mode 100644
index 0000000000..9e53dbd016
--- /dev/null
+++ b/kde/src/SFLPhoneAccessibility.cpp
@@ -0,0 +1,100 @@
+/***************************************************************************
+ *   Copyright (C) 2009-2012 by Savoir-Faire Linux                         *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>*
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 3 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ **************************************************************************/
+#include "SFLPhoneAccessibility.h"
+#include "KSpeechInterfaceSingleton.h"
+#include <KSpeech>
+#include <KDebug>
+#include <KLocale>
+#include "SFLPhone.h"
+
+SFLPhoneAccessibility* SFLPhoneAccessibility::m_pInstance=0;
+
+SFLPhoneAccessibility::SFLPhoneAccessibility() : QObject(0),QList<KAction*>()
+{
+   KAction* action = new KAction(0);
+   action->setObjectName("listCall");
+   action->setShortcut( Qt::CTRL + Qt::Key_L );
+   action->setText("List all current calls");
+   action->setIcon(KIcon("text-speak"));
+   *this << action;
+   connect(action,SIGNAL(triggered(bool)),this,SLOT(listCall()));
+
+   action = new KAction(0);
+   action->setObjectName("currentCallDetails");
+   action->setShortcut( Qt::CTRL + Qt::Key_I );
+   action->setText("Get current call details");
+   action->setIcon(KIcon("text-speak"));
+   *this << action;
+   connect(action,SIGNAL(triggered(bool)),this,SLOT(currentCallDetails()));
+}
+
+SFLPhoneAccessibility* SFLPhoneAccessibility::getInstance()
+{
+   if (not m_pInstance) {
+      m_pInstance = new SFLPhoneAccessibility();
+   }
+   return m_pInstance;
+}
+
+void SFLPhoneAccessibility::listCall()
+{
+   if (SFLPhone::model()->getCallList().size()>0) {
+      KSpeechInterfaceSingleton::getInstance()->say(i18n("You currently have %1 calls").arg(QString::number(SFLPhone::model()->getCallList().size())), KSpeech::soPlainText);
+      foreach (Call* call,SFLPhone::model()->getCallList()) {
+         KSpeechInterfaceSingleton::getInstance()->say(i18n("Call from %1, number %2").arg(call->getPeerName()).arg(numberToDigit((!call->getPeerPhoneNumber().isEmpty())?call->getPeerPhoneNumber():call->getCallNumber())), KSpeech::soPlainText);
+      }
+   }
+   else {
+      KSpeechInterfaceSingleton::getInstance()->say("You currently have no call", KSpeech::soPlainText);
+   }
+}
+
+QString SFLPhoneAccessibility::numberToDigit(QString number)
+{
+   QString toReturn;
+   for(int i=0;i<number.count();i++) {
+      if (i+1 < number.count() && (number[i] >= 0x30 && number[i] <= 0x39) && (number[i+1] >= 0x30 && number[i+1] <= 0x39))
+         toReturn += number[i]+" ";
+      else
+         toReturn += number[i];
+   }
+   return toReturn;
+}
+
+void SFLPhoneAccessibility::currentCallDetails()
+{
+   foreach (Call* call,SFLPhone::model()->getCallList()) {
+      if (call->isSelected()) {
+         QString toSay = i18n("The current call is %1").arg( i18n(call->toHumanStateName().toAscii() ));
+         if (!call->getPeerName().trimmed().isEmpty())
+            toSay += i18n(",Your peer is %1").arg( numberToDigit(call->getPeerName())           );
+         if (!call->getPeerPhoneNumber().isEmpty())
+            toSay += i18n(", the peer phone number is %1 ").arg( numberToDigit(call->getPeerPhoneNumber())    );
+         else if (!call->getCallNumber().isEmpty())
+            toSay += i18n(", the phone number is %1 ").arg( numberToDigit(call->getCallNumber())    );
+         
+         int nSec = QDateTime::fromTime_t(call->getStartTimeStamp().toInt()).time().secsTo( QTime::currentTime() );
+         if (nSec>0)
+            toSay += i18n(" and you have been talking since %1 seconds").arg( nSec );
+
+         KSpeechInterfaceSingleton::getInstance()->say(toSay, KSpeech::soPlainText);
+      }
+   }
+}
\ No newline at end of file
diff --git a/kde/src/SFLPhoneAccessibility.h b/kde/src/SFLPhoneAccessibility.h
new file mode 100644
index 0000000000..09a7443a42
--- /dev/null
+++ b/kde/src/SFLPhoneAccessibility.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ *   Copyright (C) 2009-2012 by Savoir-Faire Linux                         *
+ *   Author : Emmanuel Lepage Vallee <emmanuel.lepage@savoirfairelinux.com>*
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 3 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ **************************************************************************/
+#ifndef SFLPHONE_ACCESSIBILITY_H
+#define SFLPHONE_ACCESSIBILITY_H
+
+#include <QtCore/QList>
+#include <KAction>
+
+class SFLPhoneAccessibility : public QObject, public QList<KAction*>
+{
+   Q_OBJECT
+public:
+   static SFLPhoneAccessibility* getInstance();
+private:
+   SFLPhoneAccessibility();
+   QString numberToDigit(QString number);
+   static SFLPhoneAccessibility* m_pInstance;
+
+private slots:
+   void listCall();
+   void currentCallDetails();
+};
+
+#endif
\ No newline at end of file
diff --git a/kde/src/klib/dataengine/sflphonEngine.cpp b/kde/src/klib/dataengine/sflphonEngine.cpp
index 68ef8dd3df..e2456bf4b9 100644
--- a/kde/src/klib/dataengine/sflphonEngine.cpp
+++ b/kde/src/klib/dataengine/sflphonEngine.cpp
@@ -127,25 +127,6 @@ Plasma::Service* SFLPhoneEngine::serviceForSource(const QString &source)
  *                                                                           *
  ****************************************************************************/
 
-///Transform a backend state into a translated string
-QString SFLPhoneEngine::getCallStateName(call_state state)
-{
-   /*                     STATE                                  I18N NAME               */
-   /**/if      ( state == CALL_STATE_INCOMING    ) { return I18N_NOOP( "Ringing (in)"  ); }
-   /**/else if ( state == CALL_STATE_RINGING     ) { return I18N_NOOP( "Ringing (out)" ); }
-   /**/else if ( state == CALL_STATE_CURRENT     ) { return I18N_NOOP( "Talking"       ); }
-   /**/else if ( state == CALL_STATE_DIALING     ) { return I18N_NOOP( "Dialing"       ); }
-   /**/else if ( state == CALL_STATE_HOLD        ) { return I18N_NOOP( "Hold"          ); }
-   /**/else if ( state == CALL_STATE_FAILURE     ) { return I18N_NOOP( "Failed"        ); }
-   /**/else if ( state == CALL_STATE_BUSY        ) { return I18N_NOOP( "Busy"          ); }
-   /**/else if ( state == CALL_STATE_TRANSFER    ) { return I18N_NOOP( "Transfer"      ); }
-   /**/else if ( state == CALL_STATE_TRANSF_HOLD ) { return I18N_NOOP( "Transfer hold" ); }
-   /**/else if ( state == CALL_STATE_OVER        ) { return I18N_NOOP( "Over"          ); }
-   /**/else if ( state == CALL_STATE_ERROR       ) { return I18N_NOOP( "Error"         ); }
-   /*                                                                                    */
-   return "";
-}
-
 ///Return the model
 CallModel<>* SFLPhoneEngine::getModel()
 {
@@ -194,13 +175,13 @@ void SFLPhoneEngine::updateCallList()
    foreach (Call* call, m_pModel->getCalls()) {
       if ((!m_pModel->isConference(call)) && (call->getState() != CALL_STATE_OVER)) {
          HashStringString current;
-         /*               KEY                     VALUE                               */
-         /**/current[ "peerName"      ] = call->getPeerName        (                  );
-         /**/current[ "peerNumber"    ] = call->getPeerPhoneNumber (                  );
-         /**/current[ "stateName"     ] = getCallStateName         ( call->getState() );
-         /**/current[ "state"         ] = call->getState           (                  );
-         /**/current[ "id"            ] = call->getCallId          (                  );
-         /*                                                                           */
+         /*               KEY                     VALUE              */
+         /**/current[ "peerName"      ] = call->getPeerName        ( );
+         /**/current[ "peerNumber"    ] = call->getPeerPhoneNumber ( );
+         /**/current[ "stateName"     ] = call->toHumanStateName   ( );
+         /**/current[ "state"         ] = call->getState           ( );
+         /**/current[ "id"            ] = call->getCallId          ( );
+         /*                                                          */
          setData("calls", call->getCallId(), current);
       }
    }
diff --git a/kde/src/lib/Call.cpp b/kde/src/lib/Call.cpp
index b9982a7c90..ede7b5399c 100644
--- a/kde/src/lib/Call.cpp
+++ b/kde/src/lib/Call.cpp
@@ -97,7 +97,8 @@ const function Call::stateChangedFunctionMap[11][6] =
 
 const char * Call::historyIcons[3] = {ICON_HISTORY_INCOMING, ICON_HISTORY_OUTGOING, ICON_HISTORY_MISSED};
 
-ContactBackend* Call::m_pContactBackend = 0;
+ContactBackend* Call::m_pContactBackend = nullptr;
+Call*           Call::m_sSelectedCall   = nullptr;
 
 void Call::setContactBackend(ContactBackend* be)
 {
@@ -308,6 +309,48 @@ daemon_call_state Call::toDaemonCallState(const QString & stateName)
    return DAEMON_CALL_STATE_FAILURE    ;
 } //toDaemonCallState
 
+///Transform a backend state into a translated string
+const QString Call::toHumanStateName() const
+{
+   switch (m_CurrentState) {
+      case CALL_STATE_INCOMING:
+         return ( "Ringing (in)"  );
+         break;
+      case CALL_STATE_RINGING:
+         return ( "Ringing (out)" );
+         break;
+      case CALL_STATE_CURRENT:
+         return ( "Talking"       );
+         break;
+      case CALL_STATE_DIALING:
+         return ( "Dialing"       );
+         break;
+      case CALL_STATE_HOLD:
+         return ( "Hold"          );
+         break;
+      case CALL_STATE_FAILURE:
+         return ( "Failed"        );
+         break;
+      case CALL_STATE_BUSY:
+         return ( "Busy"          );
+         break;
+      case CALL_STATE_TRANSFER:
+         return ( "Transfer"      );
+         break;
+      case CALL_STATE_TRANSF_HOLD:
+         return ( "Transfer hold" );
+         break;
+      case CALL_STATE_OVER:
+         return ( "Over"          );
+         break;
+      case CALL_STATE_ERROR:
+         return ( "Error"         );
+         break;
+      default:
+         return "";
+   }
+}
+
 ///Get the time (second from 1 jan 1970) when the call ended
 QString Call::getStopTimeStamp()     const
 {
@@ -415,6 +458,12 @@ bool Call::isHistory()                      const
    return (getState() == CALL_STATE_OVER);
 }
 
+///Is this call selected (useful for GUIs)
+bool Call::isSelected() const
+{
+   return m_sSelectedCall == this;
+}
+
 ///This function could also be called mayBeSecure or haveChancesToBeEncryptedButWeCantTell.
 bool Call::isSecure() const {
 
@@ -476,6 +525,14 @@ void Call::setPeerName(const QString& name)
    m_PeerName = name;
 }
 
+///Set selected
+void Call::setSelected(const bool value)
+{
+   if (value) {
+      m_sSelectedCall = this;
+   }
+}
+
 /*****************************************************************************
  *                                                                           *
  *                                  Mutator                                  *
diff --git a/kde/src/lib/Call.h b/kde/src/lib/Call.h
index af85777562..8299710841 100644
--- a/kde/src/lib/Call.h
+++ b/kde/src/lib/Call.h
@@ -147,10 +147,12 @@ public:
    QString              getCurrentCodecName () const;
    bool                 isSecure            () const;
    bool                 isConference        () const;
+   bool                 isSelected          () const;
    const QString&       getConfId           () const;
    const QString&       getTransferNumber   () const;
    const QString&       getCallNumber       () const;
    const QString&       getRecordingPath    () const;
+   const QString        toHumanStateName    () const;
 
    //Automated function
    call_state stateChanged(const QString & newState);
@@ -163,6 +165,7 @@ public:
    void setCallNumber     ( const QString& number );
    void setRecordingPath  ( const QString& path   );
    void setPeerName       ( const QString& name   );
+   void setSelected       ( const bool     value  );
    
    //Mutators
    void appendText(const QString& str);
@@ -188,8 +191,9 @@ private:
    bool                   m_isConference   ;
    call_state             m_CurrentState   ;
    bool                   m_Recording      ;
+   static Call*           m_sSelectedCall  ;
    
-   //Automate attributes
+   //State machine
    /**
     *  actionPerformedStateMap[orig_state][action]
     *  Map of the states to go to when the action action is 
diff --git a/kde/src/lib/CallModel.cpp b/kde/src/lib/CallModel.cpp
index 19a9551fc4..7800218c7d 100644
--- a/kde/src/lib/CallModel.cpp
+++ b/kde/src/lib/CallModel.cpp
@@ -22,6 +22,7 @@
 #include <CallModel.h>
 
 bool CallModelBase::dbusInit = false;
+CallMap CallModelBase::m_sActiveCalls;
 
 CallModelBase::CallModelBase(QObject* parent) : QObject(parent)
 {
@@ -122,18 +123,23 @@ void CallModelBase::on1_voiceMailNotify(const QString &accountID, int count)
 
 void CallModelBase::on1_volumeChanged(const QString & device, double value)
 {
-//    qDebug() << "Signal : Volume Changed !";
-//    if(! (toolButton_recVol->isChecked() && value == 0.0))
-//       updateRecordBar();
-//    if(! (toolButton_sndVol->isChecked() && value == 0.0))
-//       updateVolumeBar();
    emit volumeChanged(device,value);
 }
 
 Call* CallModelBase::addCall(Call* call, Call* parent)
 {
    emit callAdded(call,parent);
+
+   connect(call, SIGNAL(isOver(Call*)), this, SLOT(removeActiveCall(Call*)));
    return call;
 }
 
+///Remove it from active calls
+void CallModelBase::removeActiveCall(Call* call)
+{
+   Q_UNUSED(call);
+   //There is a race condition
+   //m_sActiveCalls[call->getCallId()] = nullptr;
+}
+
 //More code in CallModel.hpp
\ No newline at end of file
diff --git a/kde/src/lib/CallModel.h b/kde/src/lib/CallModel.h
index a4aabc7c27..6bea44a228 100644
--- a/kde/src/lib/CallModel.h
+++ b/kde/src/lib/CallModel.h
@@ -67,6 +67,12 @@ public slots:
    void on1_conferenceRemoved  ( const QString& confId                             );
    void on1_voiceMailNotify    ( const QString& accountID , int count              );
    void on1_volumeChanged      ( const QString& device    , double value           );
+
+protected:
+   static CallMap m_sActiveCalls;
+
+private slots:
+  void removeActiveCall(Call*);
 private:
    static bool dbusInit;
 signals:
@@ -83,9 +89,8 @@ signals:
 };
 
 /**
- * Note from the author: It was previously done by a QAbstractModel + QTreeView, but the sip-call use case is incompatible  
- *  with the MVC model. The MVC never got to a point were it was bug-free and the code was getting dirty. The Mirror model  
- *  solution may be less "clean" than MVC, but is 3 time smaller and easier to improve (in fact, possible to improve).      
+ * Using QAbstractModel resulted in a failure. Managing all corner case bloated the code to the point of no
+ * return. This frontend may not be cleaner from a design point of view, but it is from a code point of view
  */
 ///@class CallModel Central model/frontend to deal with sflphoned
 template  <typename CallWidget = QWidget*, typename Index = QModelIndex*>
@@ -193,7 +198,6 @@ class LIB_EXPORT CallModel : public CallModelBase {
       typedef QHash< Index      , InternalStruct* > InternalIndex ;
 
       //Static attributes
-      static CallMap m_sActiveCalls ;
       static CallMap m_sHistoryCalls;
       
       static InternalCall   m_sPrivateCallList_call  ;
@@ -213,13 +217,6 @@ class LIB_EXPORT CallModel : public CallModelBase {
       Call* addCallCommon(Call* call);
       bool  updateCommon (Call* call);
 };
-
-/*class CallModelConvenience : public CallModel<QWidget*,QModelIndex*>
-{
-   public:
-      CallModelConvenience(ModelType type) : CallModel<QWidget*,QModelIndex*>(type) {}
-};*/
-
 #include "CallModel.hpp"
 
 #endif
diff --git a/kde/src/lib/CallModel.hpp b/kde/src/lib/CallModel.hpp
index 40ac122263..12555704a3 100644
--- a/kde/src/lib/CallModel.hpp
+++ b/kde/src/lib/CallModel.hpp
@@ -48,7 +48,6 @@ CALLMODEL_TEMPLATE bool CALLMODEL_T::m_sInstanceInit        = false ;
 CALLMODEL_TEMPLATE bool CALLMODEL_T::m_sCallInit            = false ;
 CALLMODEL_TEMPLATE bool CALLMODEL_T::m_sHistoryInit         = false ;
 
-CALLMODEL_TEMPLATE CallMap CALLMODEL_T::m_sActiveCalls  ;
 CALLMODEL_TEMPLATE CallMap CALLMODEL_T::m_sHistoryCalls ;
 
 CALLMODEL_TEMPLATE typename CALLMODEL_T::InternalCall   CALLMODEL_T::m_sPrivateCallList_call   ;
@@ -188,7 +187,8 @@ CALLMODEL_TEMPLATE QList<Call*> CALLMODEL_T::getCallList()
 {
    QList<Call*> callList;
    foreach(Call* call, m_sActiveCalls) {
-      callList.push_back(call);
+      if (call->getState() != CALL_STATE_OVER) //Prevent a race
+         callList.push_back(call);
    }
    return callList;
 }
diff --git a/kde/src/lib/Contact.h b/kde/src/lib/Contact.h
index 4b8da5e058..bad7467bd0 100644
--- a/kde/src/lib/Contact.h
+++ b/kde/src/lib/Contact.h
@@ -96,17 +96,17 @@ public:
    virtual const QString& getDepartment()      const;
 
    //Setters
-   virtual void setPhoneNumbers   ( PhoneNumbers          );
-   virtual void setFormattedName  ( const QString& name   );
-   virtual void setNickName       ( const QString& name   );
-   virtual void setFirstName      ( const QString& name   );
-   virtual void setFamilyName     ( const QString& name   );
-   virtual void setOrganization   ( const QString& name   );
-   virtual void setPreferredEmail ( const QString& name   );
-   virtual void setGroup          ( const QString& name   );
-   virtual void setDepartment     ( const QString& name   );
-   virtual void setUid            ( const QString& id     );
-   virtual void setPhoto          ( QPixmap* photo        );
+   virtual void setPhoneNumbers   ( PhoneNumbers        );
+   virtual void setFormattedName  ( const QString& name );
+   virtual void setNickName       ( const QString& name );
+   virtual void setFirstName      ( const QString& name );
+   virtual void setFamilyName     ( const QString& name );
+   virtual void setOrganization   ( const QString& name );
+   virtual void setPreferredEmail ( const QString& name );
+   virtual void setGroup          ( const QString& name );
+   virtual void setDepartment     ( const QString& name );
+   virtual void setUid            ( const QString& id   );
+   virtual void setPhoto          ( QPixmap* photo      );
 
    //Mutator
    QHash<QString,QVariant> toHash();
diff --git a/kde/src/main.cpp b/kde/src/main.cpp
index 340be2b2ce..b979cc3368 100755
--- a/kde/src/main.cpp
+++ b/kde/src/main.cpp
@@ -56,7 +56,6 @@ static const char version[] = "1.1.0";
 
 int main(int argc, char **argv)
 {
-
    try
    {
       KLocale::setMainCatalog("sflphone-client-kde");
diff --git a/kde/src/widgets/ContactItemWidget.cpp b/kde/src/widgets/ContactItemWidget.cpp
index eabffd2263..6d76f99303 100644
--- a/kde/src/widgets/ContactItemWidget.cpp
+++ b/kde/src/widgets/ContactItemWidget.cpp
@@ -172,9 +172,6 @@ void ContactItemWidget::updated()
    }
 
    PhoneNumbers numbers = m_pContactKA->getPhoneNumbers();
-   foreach (Contact::PhoneNumber* number, numbers) {
-      kDebug() << "Phone:" << number->getNumber() << number->getType();
-   }
 
    if (getCallNumbers().count() == 1)
       m_pCallNumberL->setText(getCallNumbers()[0]->getNumber());
diff --git a/kde/src/widgets/HistoryTreeItem.cpp b/kde/src/widgets/HistoryTreeItem.cpp
index 3c41300e54..e92dcb80de 100644
--- a/kde/src/widgets/HistoryTreeItem.cpp
+++ b/kde/src/widgets/HistoryTreeItem.cpp
@@ -438,7 +438,7 @@ void HistoryTreeItem::metaStateChanged(Phonon::State newState, Phonon::State old
       return;
 
    if (m_pMetaInformationResolver->currentSource().type() == Phonon::MediaSource::Invalid)
-            return;
+      return;
 
    QMap<QString, QString> metaData = m_pMetaInformationResolver->metaData();
 
@@ -463,6 +463,7 @@ void HistoryTreeItem::resizeEvent(QResizeEvent* event)
 
 void HistoryTreeItem::mouseDoubleClickEvent(QMouseEvent* event)
 {
+   Q_UNUSED(event);
    callAgain();
 }
 
-- 
GitLab